shepherd-core 2023.8.6__tar.gz → 2023.8.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/PKG-INFO +8 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/README.md +7 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/setup.cfg +1 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/__init__.py +1 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/__init__.py +3 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/cal_measurement.py +17 -14
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/calibration.py +41 -8
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/content.py +17 -13
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/shepherd.py +29 -22
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/wrapper.py +5 -4
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/energy_environment.py +3 -2
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/firmware.py +10 -6
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_harvester.py +42 -39
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_source.py +83 -72
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/doc_virtual_source.py +7 -14
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/experiment.py +20 -15
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/observer_features.py +33 -31
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/target_config.py +24 -18
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/__init__.py +13 -5
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/emulation.py +35 -23
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/firmware_mod.py +14 -13
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/harvest.py +28 -13
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/observer_tasks.py +17 -7
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/programming.py +13 -13
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/testbed_tasks.py +16 -6
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/cape.py +3 -2
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/gpio.py +18 -15
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/mcu.py +7 -6
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/observer.py +23 -19
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/target.py +15 -14
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/testbed.py +14 -11
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/converter.py +7 -7
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/converter_elf.py +2 -2
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/patcher.py +7 -6
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/validation.py +3 -3
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/__init__.py +16 -8
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/python.py +4 -3
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/system.py +5 -5
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/target.py +4 -4
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/reader.py +3 -3
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/client.py +6 -4
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/user_model.py +14 -10
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/writer.py +2 -2
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/PKG-INFO +8 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/requires.txt +1 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_data.yaml +2 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_meas.yaml +2 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_base_models.py +19 -2
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/inventory/test_inventory.py +1 -1
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/pyproject.toml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/calibration_hw_def.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/commons.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/energy_environment_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/firmware_datatype.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/firmware_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_harvester_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_source_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/cape_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/gpio_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/mcu_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/observer_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/target_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/testbed_fixture.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/decoder_waveform/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/decoder_waveform/uart.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/logger.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/fixtures.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/virtual_converter_model.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/virtual_harvester_model.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/virtual_source_model.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/SOURCES.txt +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/dependency_links.txt +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/top_level.txt +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/zip-safe +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/conftest.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/conftest.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_data_faulty.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_meas_faulty1.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_meas_faulty2.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_emulator.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_experiment.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_experiment_alternative.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_harvester.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_testbed.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_virtsource.yaml +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_content_fixtures.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_content_models.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_examples.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_experiment_models.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_task_generation.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_task_models.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_testbed_fixtures.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_testbed_models.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/decoder_waveform/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/decoder_waveform/test_decoder.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/conftest.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/test_converter.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/test_patcher.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/test_validation.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/inventory/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_cal_hw.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_examples.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_logger.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_reader.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_writer.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/testbed_client/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/__init__.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/conftest.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/test_converter.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/test_harvester.py +0 -0
- {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/test_z.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shepherd_core
|
|
3
|
-
Version: 2023.8.
|
|
3
|
+
Version: 2023.8.8
|
|
4
4
|
Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
|
|
5
5
|
Home-page: https://pypi.org/project/shepherd-core/
|
|
6
6
|
Author: Ingmar Splitt, Kai Geissdoerfer
|
|
@@ -77,6 +77,13 @@ The Library is available via PyPI and can be installed with
|
|
|
77
77
|
pip install shepherd-data
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
for bleeding-edge-features or dev-work it is possible to install directly from GitHub-Sources (here `dev`-branch):
|
|
81
|
+
|
|
82
|
+
```Shell
|
|
83
|
+
pip install git+https://github.com/orgua/shepherd-datalib.git@dev#subdirectory=shepherd_core -U
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
|
|
80
87
|
If you are working with ``.elf``-files (embedding into experiments) you make "objcopy" accessible to python. In Ubuntu, you can either install ``build-essential`` or ``binutils-$ARCH`` with arch being ``msp430`` or ``arm-none-eabi`` for the nRF52.
|
|
81
88
|
|
|
82
89
|
```shell
|
|
@@ -40,6 +40,13 @@ The Library is available via PyPI and can be installed with
|
|
|
40
40
|
pip install shepherd-data
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
for bleeding-edge-features or dev-work it is possible to install directly from GitHub-Sources (here `dev`-branch):
|
|
44
|
+
|
|
45
|
+
```Shell
|
|
46
|
+
pip install git+https://github.com/orgua/shepherd-datalib.git@dev#subdirectory=shepherd_core -U
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
|
|
43
50
|
If you are working with ``.elf``-files (embedding into experiments) you make "objcopy" accessible to python. In Ubuntu, you can either install ``build-essential`` or ``binutils-$ARCH`` with arch being ``msp430`` or ``arm-none-eabi`` for the nRF52.
|
|
44
51
|
|
|
45
52
|
```shell
|
|
@@ -3,6 +3,7 @@ from .base.calibration import CalibrationEmulator
|
|
|
3
3
|
from .base.calibration import CalibrationHarvester
|
|
4
4
|
from .base.calibration import CalibrationPair
|
|
5
5
|
from .base.calibration import CalibrationSeries
|
|
6
|
+
from .base.calibration import CapeData
|
|
6
7
|
from .base.content import ContentModel
|
|
7
8
|
from .base.shepherd import ShpModel
|
|
8
9
|
from .base.wrapper import Wrapper
|
|
@@ -23,6 +24,7 @@ from .experiment.target_config import TargetConfig
|
|
|
23
24
|
|
|
24
25
|
__all__ = [
|
|
25
26
|
# Core
|
|
27
|
+
"CapeData",
|
|
26
28
|
"CalibrationCape",
|
|
27
29
|
"CalibrationEmulator",
|
|
28
30
|
"CalibrationHarvester",
|
|
@@ -46,4 +48,4 @@ __all__ = [
|
|
|
46
48
|
"EnergyDType",
|
|
47
49
|
"VirtualSourceConfig",
|
|
48
50
|
"VirtualHarvesterConfig",
|
|
49
|
-
]
|
|
51
|
+
]
|
{shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/cal_measurement.py
RENAMED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
from typing import List
|
|
1
2
|
from typing import Optional
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
5
|
+
from pydantic import Field
|
|
4
6
|
from pydantic import PositiveFloat
|
|
5
|
-
from pydantic import
|
|
6
|
-
from
|
|
7
|
+
from pydantic import validate_call
|
|
8
|
+
from typing_extensions import Annotated
|
|
7
9
|
|
|
8
10
|
from .. import CalibrationCape
|
|
9
11
|
from .. import CalibrationEmulator
|
|
10
12
|
from .. import CalibrationHarvester
|
|
11
13
|
from .. import CalibrationPair
|
|
14
|
+
from .calibration import CapeData
|
|
12
15
|
from .shepherd import ShpModel
|
|
13
16
|
|
|
14
17
|
# TODO: move to shepherd_data to remove scipy-dependency from _core
|
|
@@ -19,10 +22,10 @@ class CalMeasurementPair(ShpModel):
|
|
|
19
22
|
reference_si: float = 0
|
|
20
23
|
|
|
21
24
|
|
|
22
|
-
CalMeasPairs =
|
|
25
|
+
CalMeasPairs = Annotated[List[CalMeasurementPair], Field(min_length=2)]
|
|
23
26
|
|
|
24
27
|
|
|
25
|
-
@
|
|
28
|
+
@validate_call
|
|
26
29
|
def meas_to_cal(data: CalMeasPairs, component: str) -> CalibrationPair:
|
|
27
30
|
from scipy import stats # here due to massive delay
|
|
28
31
|
|
|
@@ -51,8 +54,8 @@ class CalMeasurementHarvester(ShpModel):
|
|
|
51
54
|
adc_C_Hrv: CalMeasPairs
|
|
52
55
|
|
|
53
56
|
def to_cal(self) -> CalibrationHarvester:
|
|
54
|
-
dv = self.
|
|
55
|
-
dcal = CalibrationHarvester().
|
|
57
|
+
dv = self.model_dump()
|
|
58
|
+
dcal = CalibrationHarvester().model_dump()
|
|
56
59
|
for key in dv.keys():
|
|
57
60
|
dcal[key] = meas_to_cal(self[key], f"hrv_{key}")
|
|
58
61
|
return CalibrationHarvester(**dcal)
|
|
@@ -65,23 +68,23 @@ class CalMeasurementEmulator(ShpModel):
|
|
|
65
68
|
adc_C_B: CalMeasPairs
|
|
66
69
|
|
|
67
70
|
def to_cal(self) -> CalibrationEmulator:
|
|
68
|
-
dv = self.
|
|
69
|
-
dcal = CalibrationEmulator().
|
|
71
|
+
dv = self.model_dump()
|
|
72
|
+
dcal = CalibrationEmulator().model_dump()
|
|
70
73
|
for key in dv.keys():
|
|
71
74
|
dcal[key] = meas_to_cal(self[key], f"emu_{key}")
|
|
72
75
|
return CalibrationEmulator(**dcal)
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
class CalMeasurementCape(ShpModel):
|
|
76
|
-
|
|
77
|
-
emulator: Optional[CalMeasurementEmulator]
|
|
78
|
-
|
|
79
|
-
cape: Optional[str] = None
|
|
79
|
+
cape: Optional[CapeData] = None
|
|
80
80
|
host: Optional[str] = None
|
|
81
81
|
|
|
82
|
+
harvester: Optional[CalMeasurementHarvester] = None
|
|
83
|
+
emulator: Optional[CalMeasurementEmulator] = None
|
|
84
|
+
|
|
82
85
|
def to_cal(self) -> CalibrationCape:
|
|
83
|
-
dv = self.
|
|
84
|
-
dcal = CalibrationCape().
|
|
86
|
+
dv = self.model_dump()
|
|
87
|
+
dcal = CalibrationCape().model_dump()
|
|
85
88
|
# TODO: is it helpful to default wrong / missing values?
|
|
86
89
|
for key, value in dv.items():
|
|
87
90
|
if key in ["harvester", "emulator"]:
|
{shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/calibration.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import struct
|
|
2
|
+
from datetime import date
|
|
2
3
|
from typing import Callable
|
|
3
4
|
from typing import Generator
|
|
4
5
|
from typing import Optional
|
|
@@ -7,8 +8,11 @@ from typing import Union
|
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
from numpy.typing import NDArray
|
|
11
|
+
from pydantic import Field
|
|
10
12
|
from pydantic import PositiveFloat
|
|
11
|
-
from pydantic import
|
|
13
|
+
from pydantic import conbytes
|
|
14
|
+
from pydantic import constr
|
|
15
|
+
from pydantic import validate_call
|
|
12
16
|
|
|
13
17
|
from ...calibration_hw_def import adc_current_to_raw
|
|
14
18
|
from ...calibration_hw_def import adc_voltage_to_raw
|
|
@@ -146,6 +150,33 @@ class CalibrationEmulator(ShpModel):
|
|
|
146
150
|
return cal_set
|
|
147
151
|
|
|
148
152
|
|
|
153
|
+
class CapeData(ShpModel):
|
|
154
|
+
"""Representation of Beaglebone Cape information
|
|
155
|
+
-> just provide serial-number on creation
|
|
156
|
+
|
|
157
|
+
According to BeagleBone specifications, each cape should host an EEPROM
|
|
158
|
+
that contains some standardized information about the type of cape,
|
|
159
|
+
manufacturer, version etc.
|
|
160
|
+
|
|
161
|
+
`See<https://github.com/beagleboard/beaglebone-black/wiki/System-Reference-Manual#824_EEPROM_Data_Format>`_
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
header: conbytes(max_length=4) = b"\xAA\x55\x33\xEE"
|
|
165
|
+
eeprom_revision: constr(max_length=2) = "A2"
|
|
166
|
+
board_name: constr(max_length=32) = "BeagleBone SHEPHERD2 Cape"
|
|
167
|
+
version: constr(max_length=4) = "24B0"
|
|
168
|
+
manufacturer: constr(max_length=16) = "NES TU DRESDEN"
|
|
169
|
+
part_number: constr(max_length=16) = "BB-SHPRD"
|
|
170
|
+
|
|
171
|
+
serial_number: constr(max_length=12)
|
|
172
|
+
|
|
173
|
+
cal_date: constr(max_length=12) = Field(default_factory=date.today().isoformat)
|
|
174
|
+
|
|
175
|
+
def __repr__(self) -> str:
|
|
176
|
+
"""string-representation allows print(model)"""
|
|
177
|
+
return str(self.model_dump())
|
|
178
|
+
|
|
179
|
+
|
|
149
180
|
class CalibrationCape(ShpModel):
|
|
150
181
|
"""Represents calibration data of shepherd cape.
|
|
151
182
|
Defines the format of calibration data and provides convenient functions
|
|
@@ -154,29 +185,31 @@ class CalibrationCape(ShpModel):
|
|
|
154
185
|
YAML: .to_file() and .from_file() already in ShpModel
|
|
155
186
|
"""
|
|
156
187
|
|
|
188
|
+
cape: Optional[CapeData] = None
|
|
189
|
+
host: Optional[str] = None
|
|
190
|
+
|
|
157
191
|
harvester: CalibrationHarvester = CalibrationHarvester()
|
|
158
192
|
emulator: CalibrationEmulator = CalibrationEmulator()
|
|
159
193
|
|
|
160
|
-
cape: Optional[str] = None
|
|
161
|
-
host: Optional[str] = None
|
|
162
|
-
|
|
163
194
|
@classmethod
|
|
164
|
-
def from_bytestr(cls, data: bytes):
|
|
195
|
+
def from_bytestr(cls, data: bytes, cape: Optional[CapeData] = None):
|
|
165
196
|
"""Instantiates calibration data based on byte string.
|
|
166
197
|
This is mainly used to deserialize data read from an EEPROM memory.
|
|
167
198
|
|
|
168
199
|
Args:
|
|
169
200
|
data: Byte string containing calibration data.
|
|
201
|
+
cape: data can be supplied
|
|
170
202
|
Returns:
|
|
171
203
|
CalibrationCape object with extracted calibration data.
|
|
172
204
|
"""
|
|
173
|
-
dv = cls().
|
|
205
|
+
dv = cls().model_dump(include={"harvester", "emulator"})
|
|
174
206
|
lw = list(dict_generator(dv))
|
|
175
207
|
values = struct.unpack(">" + len(lw) * "d", data)
|
|
176
208
|
# ⤷ X => double float, big endian
|
|
177
209
|
for _i, walk in enumerate(lw):
|
|
178
210
|
# hardcoded fixed depth ... bad but easy
|
|
179
211
|
dv[walk[0]][walk[1]][walk[2]] = float(values[_i])
|
|
212
|
+
dv["cape"] = cape
|
|
180
213
|
return cls(**dv)
|
|
181
214
|
|
|
182
215
|
def to_bytestr(self) -> bytes:
|
|
@@ -186,7 +219,7 @@ class CalibrationCape(ShpModel):
|
|
|
186
219
|
Returns:
|
|
187
220
|
Byte string representation of calibration values.
|
|
188
221
|
"""
|
|
189
|
-
lw = list(dict_generator(self.
|
|
222
|
+
lw = list(dict_generator(self.model_dump(include={"harvester", "emulator"})))
|
|
190
223
|
values = [walk[-1] for walk in lw]
|
|
191
224
|
return struct.pack(">" + len(lw) * "d", *values)
|
|
192
225
|
|
|
@@ -200,7 +233,7 @@ class CalibrationSeries(ShpModel):
|
|
|
200
233
|
# ⤷ default = nanoseconds
|
|
201
234
|
|
|
202
235
|
@classmethod
|
|
203
|
-
@
|
|
236
|
+
@validate_call
|
|
204
237
|
def from_cal(
|
|
205
238
|
cls,
|
|
206
239
|
cal: Union[CalibrationHarvester, CalibrationEmulator],
|
|
@@ -3,18 +3,20 @@ from datetime import datetime
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from pydantic import Field
|
|
6
|
-
from pydantic import
|
|
7
|
-
from pydantic import
|
|
8
|
-
from
|
|
6
|
+
from pydantic import StringConstraints
|
|
7
|
+
from pydantic import model_validator
|
|
8
|
+
from typing_extensions import Annotated
|
|
9
9
|
|
|
10
10
|
from .shepherd import ShpModel
|
|
11
11
|
|
|
12
12
|
# constr -> to_lower=True, max_length=16, regex=r"^[\w]+$"
|
|
13
13
|
# ⤷ Regex = AlphaNum
|
|
14
|
-
IdInt =
|
|
15
|
-
NameStr =
|
|
14
|
+
IdInt = Annotated[int, Field(ge=0, lt=2**128)]
|
|
15
|
+
NameStr = Annotated[
|
|
16
|
+
str, StringConstraints(max_length=32, pattern=r'^[^<>:;,?"*|\/\\]+$')
|
|
17
|
+
]
|
|
16
18
|
# ⤷ Regex = FileSystem-Compatible ASCII
|
|
17
|
-
SafeStr =
|
|
19
|
+
SafeStr = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
|
|
18
20
|
# ⤷ Regex = All Printable ASCII-Characters with Space
|
|
19
21
|
|
|
20
22
|
|
|
@@ -31,25 +33,27 @@ class ContentModel(ShpModel):
|
|
|
31
33
|
default_factory=id_default,
|
|
32
34
|
)
|
|
33
35
|
name: NameStr
|
|
34
|
-
description:
|
|
36
|
+
description: Annotated[
|
|
37
|
+
Optional[SafeStr], Field(description="Required when public")
|
|
38
|
+
] = None
|
|
35
39
|
comment: Optional[SafeStr] = None
|
|
36
40
|
created: datetime = Field(default_factory=datetime.now)
|
|
37
41
|
|
|
38
42
|
# Ownership & Access
|
|
39
43
|
owner: NameStr
|
|
40
|
-
group: NameStr
|
|
44
|
+
group: Annotated[NameStr, Field(description="University or Subgroup")]
|
|
41
45
|
visible2group: bool = False
|
|
42
46
|
visible2all: bool = False
|
|
43
47
|
|
|
44
48
|
def __str__(self):
|
|
45
49
|
return self.name
|
|
46
50
|
|
|
47
|
-
@
|
|
48
|
-
def content_validation(
|
|
49
|
-
is_visible =
|
|
50
|
-
if is_visible and
|
|
51
|
+
@model_validator(mode="after")
|
|
52
|
+
def content_validation(self):
|
|
53
|
+
is_visible = self.visible2group or self.visible2all
|
|
54
|
+
if is_visible and self.description is None:
|
|
51
55
|
raise ValueError(
|
|
52
56
|
"Public instances require a description "
|
|
53
57
|
"(check visible2*- and description-field)"
|
|
54
58
|
)
|
|
55
|
-
return
|
|
59
|
+
return self
|
{shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/shepherd.py
RENAMED
|
@@ -8,7 +8,7 @@ from typing import Union
|
|
|
8
8
|
|
|
9
9
|
import yaml
|
|
10
10
|
from pydantic import BaseModel
|
|
11
|
-
from pydantic import
|
|
11
|
+
from pydantic import ConfigDict
|
|
12
12
|
from yaml import SafeDumper
|
|
13
13
|
|
|
14
14
|
from .wrapper import Wrapper
|
|
@@ -43,36 +43,43 @@ class ShpModel(BaseModel):
|
|
|
43
43
|
- schema cls.schema() can also be stored to yaml with .schema_to_file()
|
|
44
44
|
"""
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
model_config = ConfigDict(
|
|
47
|
+
frozen=True, # -> const after creation, hashable! but currently manually with .get_hash()
|
|
48
|
+
extra="forbid", # no unnamed attributes allowed
|
|
49
|
+
validate_default=True,
|
|
50
|
+
validate_assignment=True, # not relevant for the frozen model
|
|
51
|
+
str_min_length=1, # force more meaningful descriptors,
|
|
52
|
+
# ⤷ TODO: was 4 but localizing constraints works different with pydantic2
|
|
53
|
+
# - might be solvable with "use_enum_values=True"
|
|
54
|
+
str_max_length=512,
|
|
54
55
|
# ⤷ local str-length constraints overrule global ones!
|
|
55
|
-
|
|
56
|
-
use_enum_values
|
|
57
|
-
allow_inf_nan
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
# - https://docs.pydantic.dev/usage/schema/#field-customization
|
|
61
|
-
# - https://docs.pydantic.dev/usage/model_config/
|
|
62
|
-
# "fields["name"].description = ... should be usable to modify model
|
|
56
|
+
str_strip_whitespace=True, # strip leading & trailing whitespaces
|
|
57
|
+
use_enum_values=True, # cleaner export of enum-parameters
|
|
58
|
+
allow_inf_nan=False, # float without +-inf or NaN
|
|
59
|
+
# defer_build, possible speedup -> but it triggers a bug
|
|
60
|
+
)
|
|
63
61
|
|
|
64
62
|
def __repr__(self) -> str:
|
|
65
63
|
"""string-representation allows print(model)"""
|
|
66
|
-
return str(self.
|
|
64
|
+
return str(self.model_dump(exclude_unset=True, exclude_defaults=True))
|
|
67
65
|
|
|
68
66
|
def __getitem__(self, key):
|
|
69
67
|
"""allows dict access -> model["key"], in addition to model.key"""
|
|
70
68
|
return self.__getattribute__(key)
|
|
71
69
|
|
|
70
|
+
def keys(self):
|
|
71
|
+
"""Fn of dict"""
|
|
72
|
+
return self.model_dump().keys()
|
|
73
|
+
|
|
74
|
+
def items(self):
|
|
75
|
+
"""Fn of dict"""
|
|
76
|
+
for key in self.keys():
|
|
77
|
+
yield key, self[key]
|
|
78
|
+
|
|
72
79
|
@classmethod
|
|
73
80
|
def schema_to_file(cls, path: Union[str, Path]) -> None:
|
|
74
81
|
"""store schema to yaml (for frontend-generators)"""
|
|
75
|
-
model_dict = cls.
|
|
82
|
+
model_dict = cls.model_json_schema()
|
|
76
83
|
model_yaml = yaml.safe_dump(
|
|
77
84
|
model_dict, default_flow_style=False, sort_keys=False
|
|
78
85
|
)
|
|
@@ -89,7 +96,7 @@ class ShpModel(BaseModel):
|
|
|
89
96
|
minimal: stores minimal set (filters out unset & default parameters)
|
|
90
97
|
comment: documentation
|
|
91
98
|
"""
|
|
92
|
-
model_dict = self.
|
|
99
|
+
model_dict = self.model_dump(exclude_unset=minimal, exclude_defaults=minimal)
|
|
93
100
|
model_wrap = Wrapper(
|
|
94
101
|
datatype=type(self).__name__,
|
|
95
102
|
comment=comment,
|
|
@@ -97,7 +104,7 @@ class ShpModel(BaseModel):
|
|
|
97
104
|
parameters=model_dict,
|
|
98
105
|
)
|
|
99
106
|
model_yaml = yaml.safe_dump(
|
|
100
|
-
model_wrap.
|
|
107
|
+
model_wrap.model_dump(exclude_unset=minimal, exclude_defaults=minimal),
|
|
101
108
|
default_flow_style=False,
|
|
102
109
|
sort_keys=False,
|
|
103
110
|
)
|
|
@@ -118,4 +125,4 @@ class ShpModel(BaseModel):
|
|
|
118
125
|
return cls(**shp_wrap.parameters)
|
|
119
126
|
|
|
120
127
|
def get_hash(self):
|
|
121
|
-
return hashlib.sha3_224(str(self.
|
|
128
|
+
return hashlib.sha3_224(str(self.model_dump()).encode("utf-8")).hexdigest()
|
|
@@ -2,9 +2,10 @@ from datetime import datetime
|
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
|
-
from pydantic import
|
|
5
|
+
from pydantic import StringConstraints
|
|
6
|
+
from typing_extensions import Annotated
|
|
6
7
|
|
|
7
|
-
SafeStrClone =
|
|
8
|
+
SafeStrClone = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
|
|
8
9
|
# ⤷ copy avoids circular import
|
|
9
10
|
|
|
10
11
|
|
|
@@ -15,8 +16,8 @@ class Wrapper(BaseModel):
|
|
|
15
16
|
|
|
16
17
|
datatype: str
|
|
17
18
|
# ⤷ model-name
|
|
18
|
-
comment: Optional[SafeStrClone]
|
|
19
|
-
created: Optional[datetime]
|
|
19
|
+
comment: Optional[SafeStrClone] = None
|
|
20
|
+
created: Optional[datetime] = None
|
|
20
21
|
# ⤷ Optional metadata
|
|
21
22
|
parameters: dict
|
|
22
23
|
# ⤷ ShpModel
|
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from pydantic import PositiveFloat
|
|
6
|
-
from pydantic import
|
|
6
|
+
from pydantic import model_validator
|
|
7
7
|
|
|
8
8
|
from ...testbed_client import tb_client
|
|
9
9
|
from ..base.content import ContentModel
|
|
@@ -37,7 +37,8 @@ class EnergyEnvironment(ContentModel):
|
|
|
37
37
|
indoor: Optional[bool] = None
|
|
38
38
|
location: Optional[str] = None
|
|
39
39
|
|
|
40
|
-
@
|
|
40
|
+
@model_validator(mode="before")
|
|
41
|
+
@classmethod
|
|
41
42
|
def query_database(cls, values: dict) -> dict:
|
|
42
43
|
values, _ = tb_client.try_completing_model(cls.__name__, values)
|
|
43
44
|
return tb_client.fill_in_user_data(values)
|
{shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/firmware.py
RENAMED
|
@@ -2,9 +2,10 @@ from pathlib import Path
|
|
|
2
2
|
from typing import Optional
|
|
3
3
|
from typing import Union
|
|
4
4
|
|
|
5
|
-
from pydantic import
|
|
6
|
-
from pydantic import
|
|
7
|
-
from pydantic import
|
|
5
|
+
from pydantic import StringConstraints
|
|
6
|
+
from pydantic import model_validator
|
|
7
|
+
from pydantic import validate_call
|
|
8
|
+
from typing_extensions import Annotated
|
|
8
9
|
|
|
9
10
|
from ... import fw_tools
|
|
10
11
|
from ... import logger
|
|
@@ -32,6 +33,8 @@ arch_to_mcu: dict = {
|
|
|
32
33
|
"nrf52": {"name": "nrf52"},
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
FirmwareStr = Annotated[str, StringConstraints(min_length=3, max_length=8_000_000)]
|
|
37
|
+
|
|
35
38
|
|
|
36
39
|
class Firmware(ContentModel, title="Firmware of Target"):
|
|
37
40
|
"""meta-data representation of a data-component"""
|
|
@@ -40,13 +43,14 @@ class Firmware(ContentModel, title="Firmware of Target"):
|
|
|
40
43
|
|
|
41
44
|
mcu: MCU
|
|
42
45
|
|
|
43
|
-
data: Union[
|
|
46
|
+
data: Union[FirmwareStr, Path]
|
|
44
47
|
data_type: FirmwareDType
|
|
45
48
|
data_hash: Optional[str] = None
|
|
46
49
|
|
|
47
50
|
# TODO: a data-hash would be awesome
|
|
48
51
|
|
|
49
|
-
@
|
|
52
|
+
@model_validator(mode="before")
|
|
53
|
+
@classmethod
|
|
50
54
|
def query_database(cls, values: dict) -> dict:
|
|
51
55
|
values, _ = tb_client.try_completing_model(cls.__name__, values)
|
|
52
56
|
return tb_client.fill_in_user_data(values)
|
|
@@ -101,7 +105,7 @@ class Firmware(ContentModel, title="Firmware of Target"):
|
|
|
101
105
|
logger.warning("FW-Hash does not match with stored value!")
|
|
102
106
|
return match
|
|
103
107
|
|
|
104
|
-
@
|
|
108
|
+
@validate_call
|
|
105
109
|
def extract_firmware(self, file: Path) -> Path:
|
|
106
110
|
"""stores embedded data in file
|
|
107
111
|
- file-suffix is derived from data-type and adapted
|
|
@@ -2,9 +2,9 @@ from enum import Enum
|
|
|
2
2
|
from typing import Optional
|
|
3
3
|
from typing import Tuple
|
|
4
4
|
|
|
5
|
-
from pydantic import
|
|
6
|
-
from pydantic import
|
|
7
|
-
from
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from pydantic import model_validator
|
|
7
|
+
from typing_extensions import Annotated
|
|
8
8
|
|
|
9
9
|
from ...commons import samplerate_sps_default
|
|
10
10
|
from ...logger import logger
|
|
@@ -39,32 +39,33 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
|
|
|
39
39
|
algorithm: AlgorithmDType
|
|
40
40
|
# ⤷ used to harvest energy
|
|
41
41
|
|
|
42
|
-
samples_n:
|
|
42
|
+
samples_n: Annotated[int, Field(ge=8, le=2_000)] = 8
|
|
43
43
|
# ⤷ for & of ivcurve (and more?`)
|
|
44
44
|
|
|
45
|
-
voltage_mV:
|
|
45
|
+
voltage_mV: Annotated[float, Field(ge=0, le=5_000)] = 2_500
|
|
46
46
|
# ⤷ starting-point for some algorithms (mppt_po)
|
|
47
|
-
voltage_min_mV:
|
|
48
|
-
voltage_max_mV:
|
|
49
|
-
current_limit_uA:
|
|
47
|
+
voltage_min_mV: Annotated[float, Field(ge=0, le=5_000)] = 0
|
|
48
|
+
voltage_max_mV: Annotated[float, Field(ge=0, le=5_000)] = 5_000
|
|
49
|
+
current_limit_uA: Annotated[float, Field(ge=1, le=50_000)] = 50_000
|
|
50
50
|
# ⤷ allows to keep trajectory in special region (or constant current tracking)
|
|
51
51
|
# ⤷ boundary for detecting open circuit in emulated version (working on IV-Curves)
|
|
52
|
-
voltage_step_mV: Optional[
|
|
52
|
+
voltage_step_mV: Optional[Annotated[float, Field(ge=1, le=1_000_000)]] = None
|
|
53
53
|
|
|
54
|
-
setpoint_n:
|
|
54
|
+
setpoint_n: Annotated[float, Field(ge=0, le=1.0)] = 0.70
|
|
55
55
|
# ⤷ ie. for mppt_voc
|
|
56
|
-
interval_ms:
|
|
56
|
+
interval_ms: Annotated[float, Field(ge=0.01, le=1_000_000)] = 100
|
|
57
57
|
# ⤷ between start of measurements (ie. V_OC)
|
|
58
|
-
duration_ms:
|
|
58
|
+
duration_ms: Annotated[float, Field(ge=0.01, le=1_000_000)] = 0.1
|
|
59
59
|
# ⤷ of (open voltage) measurement
|
|
60
60
|
rising: bool = True
|
|
61
61
|
# ⤷ direction of sawtooth
|
|
62
62
|
|
|
63
63
|
# Underlying recorder
|
|
64
|
-
wait_cycles:
|
|
64
|
+
wait_cycles: Annotated[int, Field(ge=0, le=100)] = 1
|
|
65
65
|
# ⤷ first cycle: ADC-Sampling & DAC-Writing, further steps: waiting
|
|
66
66
|
|
|
67
|
-
@
|
|
67
|
+
@model_validator(mode="before")
|
|
68
|
+
@classmethod
|
|
68
69
|
def query_database(cls, values: dict) -> dict:
|
|
69
70
|
values, chain = tb_client.try_completing_model(cls.__name__, values)
|
|
70
71
|
values = tb_client.fill_in_user_data(values)
|
|
@@ -72,27 +73,18 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
|
|
|
72
73
|
# TODO: same test is later done in calc_algorithm_num() again
|
|
73
74
|
raise ValueError("Resulting Harvester can't be neutral")
|
|
74
75
|
logger.debug("VHrv-Inheritances: %s", chain)
|
|
75
|
-
return values
|
|
76
|
-
|
|
77
|
-
@root_validator(pre=False)
|
|
78
|
-
def post_validation(cls, values: dict) -> dict:
|
|
79
|
-
if values.get("voltage_min_mV") > values.get("voltage_max_mV"):
|
|
80
|
-
raise ValueError("Voltage min > max")
|
|
81
|
-
if values.get("voltage_mV") < values.get("voltage_min_mV"):
|
|
82
|
-
raise ValueError("Voltage below min")
|
|
83
|
-
if values.get("voltage_mV") > values.get("voltage_max_mV"):
|
|
84
|
-
raise ValueError("Voltage above max")
|
|
85
76
|
|
|
77
|
+
# post corrections -> should be in separate validator
|
|
86
78
|
cal = CalibrationHarvester() # todo: as argument?
|
|
87
|
-
values
|
|
88
|
-
|
|
89
|
-
)
|
|
79
|
+
c_limit = values.get("current_limit_uA", 50_000) # cls.current_limit_uA)
|
|
80
|
+
values["current_limit_uA"] = max(10**6 * cal.adc_C_Hrv.raw_to_si(4), c_limit)
|
|
90
81
|
|
|
91
82
|
if values.get("voltage_step_mV") is None:
|
|
92
83
|
# algo includes min & max!
|
|
93
|
-
values
|
|
94
|
-
|
|
95
|
-
|
|
84
|
+
v_max = values.get("voltage_max_mV", 5_000) # cls.voltage_max_mV)
|
|
85
|
+
v_min = values.get("voltage_min_mV", 0) # cls.voltage_min_mV)
|
|
86
|
+
samples_n = values.get("samples_n", 8) # cls.samples_n) TODO
|
|
87
|
+
values["voltage_step_mV"] = abs(v_max - v_min) / (samples_n - 1)
|
|
96
88
|
|
|
97
89
|
values["voltage_step_mV"] = max(
|
|
98
90
|
10**3 * cal.dac_V_Hrv.raw_to_si(4), values["voltage_step_mV"]
|
|
@@ -100,6 +92,17 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
|
|
|
100
92
|
|
|
101
93
|
return values
|
|
102
94
|
|
|
95
|
+
@model_validator(mode="after")
|
|
96
|
+
def post_validation(self):
|
|
97
|
+
if self.voltage_min_mV > self.voltage_max_mV:
|
|
98
|
+
raise ValueError("Voltage min > max")
|
|
99
|
+
if self.voltage_mV < self.voltage_min_mV:
|
|
100
|
+
raise ValueError("Voltage below min")
|
|
101
|
+
if self.voltage_mV > self.voltage_max_mV:
|
|
102
|
+
raise ValueError("Voltage above max")
|
|
103
|
+
|
|
104
|
+
return self
|
|
105
|
+
|
|
103
106
|
def calc_hrv_mode(self, for_emu: bool) -> int:
|
|
104
107
|
return 1 * int(for_emu) + 2 * self.rising
|
|
105
108
|
|
|
@@ -154,7 +157,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
|
|
|
154
157
|
return self.samples_n
|
|
155
158
|
|
|
156
159
|
|
|
157
|
-
u32 =
|
|
160
|
+
u32 = Annotated[int, Field(ge=0, lt=2**32)]
|
|
158
161
|
|
|
159
162
|
|
|
160
163
|
# Currently implemented harvesters
|
|
@@ -230,13 +233,13 @@ class HarvesterPRUConfig(ShpModel):
|
|
|
230
233
|
window_size=window_size
|
|
231
234
|
if window_size is not None
|
|
232
235
|
else data.calc_window_size(for_emu, dtype_in),
|
|
233
|
-
voltage_uV=data.voltage_mV * 10**3,
|
|
234
|
-
voltage_min_uV=data.voltage_min_mV * 10**3,
|
|
235
|
-
voltage_max_uV=data.voltage_max_mV * 10**3,
|
|
236
|
-
voltage_step_uV=data.voltage_step_mV * 10**3,
|
|
237
|
-
current_limit_nA=data.current_limit_uA * 10**3,
|
|
238
|
-
setpoint_n8=min(255, data.setpoint_n * 2**8),
|
|
239
|
-
interval_n=interval_ms * samplerate_sps_default * 1e-3,
|
|
240
|
-
duration_n=duration_ms * samplerate_sps_default * 1e-3,
|
|
236
|
+
voltage_uV=round(data.voltage_mV * 10**3),
|
|
237
|
+
voltage_min_uV=round(data.voltage_min_mV * 10**3),
|
|
238
|
+
voltage_max_uV=round(data.voltage_max_mV * 10**3),
|
|
239
|
+
voltage_step_uV=round(data.voltage_step_mV * 10**3),
|
|
240
|
+
current_limit_nA=round(data.current_limit_uA * 10**3),
|
|
241
|
+
setpoint_n8=round(min(255, data.setpoint_n * 2**8)),
|
|
242
|
+
interval_n=round(interval_ms * samplerate_sps_default * 1e-3),
|
|
243
|
+
duration_n=round(duration_ms * samplerate_sps_default * 1e-3),
|
|
241
244
|
wait_cycles_n=data.wait_cycles,
|
|
242
245
|
)
|