shepherd-core 2025.4.1__py3-none-any.whl → 2025.4.2__py3-none-any.whl

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.
Files changed (55) hide show
  1. shepherd_core/calibration_hw_def.py +11 -11
  2. shepherd_core/commons.py +4 -4
  3. shepherd_core/data_models/base/cal_measurement.py +10 -11
  4. shepherd_core/data_models/base/calibration.py +7 -6
  5. shepherd_core/data_models/base/content.py +1 -1
  6. shepherd_core/data_models/base/shepherd.py +6 -7
  7. shepherd_core/data_models/base/wrapper.py +2 -2
  8. shepherd_core/data_models/content/energy_environment.py +4 -3
  9. shepherd_core/data_models/content/firmware.py +9 -7
  10. shepherd_core/data_models/content/virtual_harvester.py +30 -22
  11. shepherd_core/data_models/content/virtual_source.py +16 -15
  12. shepherd_core/data_models/experiment/experiment.py +13 -13
  13. shepherd_core/data_models/experiment/observer_features.py +7 -8
  14. shepherd_core/data_models/experiment/target_config.py +12 -12
  15. shepherd_core/data_models/task/__init__.py +5 -5
  16. shepherd_core/data_models/task/emulation.py +13 -14
  17. shepherd_core/data_models/task/firmware_mod.py +11 -11
  18. shepherd_core/data_models/task/harvest.py +7 -6
  19. shepherd_core/data_models/task/observer_tasks.py +7 -7
  20. shepherd_core/data_models/task/programming.py +11 -11
  21. shepherd_core/data_models/task/testbed_tasks.py +8 -8
  22. shepherd_core/data_models/testbed/cape.py +7 -6
  23. shepherd_core/data_models/testbed/gpio.py +8 -7
  24. shepherd_core/data_models/testbed/mcu.py +8 -7
  25. shepherd_core/data_models/testbed/observer.py +9 -7
  26. shepherd_core/data_models/testbed/target.py +9 -7
  27. shepherd_core/data_models/testbed/testbed.py +11 -10
  28. shepherd_core/decoder_waveform/uart.py +5 -5
  29. shepherd_core/fw_tools/converter.py +4 -3
  30. shepherd_core/fw_tools/patcher.py +14 -15
  31. shepherd_core/fw_tools/validation.py +3 -2
  32. shepherd_core/inventory/__init__.py +6 -6
  33. shepherd_core/inventory/python.py +1 -1
  34. shepherd_core/inventory/system.py +11 -8
  35. shepherd_core/inventory/target.py +3 -3
  36. shepherd_core/logger.py +2 -2
  37. shepherd_core/reader.py +37 -39
  38. shepherd_core/testbed_client/client_abc_fix.py +20 -13
  39. shepherd_core/testbed_client/client_web.py +18 -11
  40. shepherd_core/testbed_client/fixtures.py +19 -16
  41. shepherd_core/testbed_client/user_model.py +6 -5
  42. shepherd_core/version.py +1 -1
  43. shepherd_core/vsource/target_model.py +3 -3
  44. shepherd_core/vsource/virtual_converter_model.py +3 -3
  45. shepherd_core/vsource/virtual_harvester_model.py +7 -9
  46. shepherd_core/vsource/virtual_harvester_simulation.py +6 -5
  47. shepherd_core/vsource/virtual_source_model.py +6 -5
  48. shepherd_core/vsource/virtual_source_simulation.py +7 -6
  49. shepherd_core/writer.py +32 -34
  50. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.4.2.dist-info}/METADATA +2 -3
  51. shepherd_core-2025.4.2.dist-info/RECORD +81 -0
  52. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.4.2.dist-info}/WHEEL +1 -1
  53. shepherd_core-2025.4.1.dist-info/RECORD +0 -81
  54. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.4.2.dist-info}/top_level.txt +0 -0
  55. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.4.2.dist-info}/zip-safe +0 -0
@@ -10,26 +10,26 @@ currently configured for cape v2.4c
10
10
  """
11
11
 
12
12
  # both current channels have a 0.1 % shunt resistance of
13
- R_SHT = 2.0 # [ohm]
13
+ R_SHT: float = 2.0 # [ohm]
14
14
  # the instrumentation amplifiers are configured for gain of
15
- G_INST_AMP = 48 # [n]
15
+ G_INST_AMP: int = 48 # [n]
16
16
  # we use the ADC's internal reference with
17
- V_REF_ADC = 4.096 # [V]
17
+ V_REF_ADC: float = 4.096 # [V]
18
18
  # range of current channels is
19
- G_ADC_I = 1.25 # [gain / V_REF]
19
+ G_ADC_I: float = 1.25 # [gain / V_REF]
20
20
  # range of voltage channels is
21
- G_ADC_V = 1.25 # [gain / V_REF]
21
+ G_ADC_V: float = 1.25 # [gain / V_REF]
22
22
  # bit resolution of ADC
23
- M_ADC = 18 # [bit]
23
+ M_ADC: int = 18 # [bit]
24
24
  # DACs use internal reference with
25
- V_REF_DAC = 2.5 # [V]
25
+ V_REF_DAC: float = 2.5 # [V]
26
26
  # gain of DAC is set to
27
- G_DAC = 2 # [n]
27
+ G_DAC: int = 2 # [n]
28
28
  # bit resolution of DAC
29
- M_DAC = 16 # [bit]
29
+ M_DAC: int = 16 # [bit]
30
30
  # DERIVED VARIABLES
31
- RAW_MAX_ADC = 2**M_ADC - 1
32
- RAW_MAX_DAC = 2**M_DAC - 1
31
+ RAW_MAX_ADC: int = 2**M_ADC - 1
32
+ RAW_MAX_DAC: int = 2**M_DAC - 1
33
33
 
34
34
 
35
35
  def adc_current_to_raw(current: float, *, limited: bool = True) -> int:
shepherd_core/commons.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """Container for commonly shared constants."""
2
2
 
3
- samplerate_sps_default: int = 100_000
3
+ SAMPLERATE_SPS_DEFAULT: int = 100_000
4
4
 
5
- uid_str_default: str = "SHEPHERD_NODE_ID"
6
- uid_len_default: int = 2
5
+ UID_NAME: str = "SHEPHERD_NODE_ID"
6
+ UID_SIZE: int = 2
7
7
 
8
- testbed_server_default: str = "http://127.0.0.1:8000/shepherd"
8
+ TESTBED_SERVER_URI: str = "http://127.0.0.1:8000/shepherd"
@@ -1,13 +1,12 @@
1
1
  """Models for the process of calibration a device by measurements."""
2
2
 
3
- from typing import List
3
+ from typing import Annotated
4
4
  from typing import Optional
5
5
 
6
6
  import numpy as np
7
7
  from pydantic import Field
8
8
  from pydantic import PositiveFloat
9
9
  from pydantic import validate_call
10
- from typing_extensions import Annotated
11
10
 
12
11
  from .calibration import CalibrationCape
13
12
  from .calibration import CalibrationEmulator
@@ -24,7 +23,7 @@ class CalMeasurementPair(ShpModel):
24
23
  reference_si: float = 0
25
24
 
26
25
 
27
- CalMeasPairs = Annotated[List[CalMeasurementPair], Field(min_length=2)]
26
+ CalMeasPairs = Annotated[list[CalMeasurementPair], Field(min_length=2)]
28
27
 
29
28
 
30
29
  @validate_call
@@ -37,16 +36,16 @@ def meas_to_cal(data: CalMeasPairs, component: str) -> CalibrationPair:
37
36
  y[i] = pair.reference_si
38
37
 
39
38
  model = np.polyfit(x, y, 1)
40
- offset = model[1]
41
- gain = model[0]
39
+ offset: float = float(model[1])
40
+ gain: float = float(model[0])
42
41
 
43
42
  # r-squared, Pearson correlation coefficient
44
- p = np.poly1d(model)
45
- yhat = p(x)
46
- ybar = np.sum(y) / len(y)
47
- ssreg = np.sum((yhat - ybar) ** 2)
48
- sstot = np.sum((y - ybar) ** 2)
49
- rval = ssreg / sstot
43
+ _p = np.poly1d(model)
44
+ yhat = _p(x)
45
+ ybar: float = np.sum(y) / len(y)
46
+ ssreg: float = np.sum((yhat - ybar) ** 2)
47
+ sstot: float = np.sum((y - ybar) ** 2)
48
+ rval: float = ssreg / sstot
50
49
 
51
50
  if rval < 0.999:
52
51
  msg = (
@@ -1,11 +1,11 @@
1
1
  """Models for the calibration-data to convert between raw & SI-Values."""
2
2
 
3
3
  import struct
4
+ from collections.abc import Generator
5
+ from collections.abc import Mapping
6
+ from collections.abc import Sequence
4
7
  from typing import Callable
5
- from typing import Generator
6
- from typing import Mapping
7
8
  from typing import Optional
8
- from typing import Sequence
9
9
  from typing import TypeVar
10
10
  from typing import Union
11
11
 
@@ -18,9 +18,10 @@ from pydantic import constr
18
18
  from pydantic import validate_call
19
19
  from typing_extensions import Self
20
20
 
21
- from ...calibration_hw_def import adc_current_to_raw
22
- from ...calibration_hw_def import adc_voltage_to_raw
23
- from ...calibration_hw_def import dac_voltage_to_raw
21
+ from shepherd_core.calibration_hw_def import adc_current_to_raw
22
+ from shepherd_core.calibration_hw_def import adc_voltage_to_raw
23
+ from shepherd_core.calibration_hw_def import dac_voltage_to_raw
24
+
24
25
  from .shepherd import ShpModel
25
26
  from .timezone import local_iso_date
26
27
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import hashlib
4
4
  from datetime import datetime
5
+ from typing import Annotated
5
6
  from typing import Optional
6
7
  from typing import Union
7
8
  from uuid import uuid4
@@ -10,7 +11,6 @@ from pydantic import UUID4
10
11
  from pydantic import Field
11
12
  from pydantic import StringConstraints
12
13
  from pydantic import model_validator
13
- from typing_extensions import Annotated
14
14
  from typing_extensions import Self
15
15
  from typing_extensions import deprecated
16
16
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  import hashlib
4
4
  import pathlib
5
+ from collections.abc import Generator
5
6
  from datetime import timedelta
6
7
  from ipaddress import IPv4Address
7
8
  from pathlib import Path
8
9
  from typing import Any
9
- from typing import Generator
10
10
  from typing import Optional
11
11
  from typing import Union
12
12
  from uuid import UUID
@@ -15,27 +15,26 @@ import yaml
15
15
  from pydantic import BaseModel
16
16
  from pydantic import ConfigDict
17
17
  from typing_extensions import Self
18
- from yaml import Dumper
18
+ from yaml import Node
19
19
  from yaml import SafeDumper
20
- from yaml import ScalarNode
21
20
 
22
21
  from .timezone import local_now
23
22
  from .wrapper import Wrapper
24
23
 
25
24
 
26
25
  def path2str(
27
- dumper: Dumper, data: Union[pathlib.Path, pathlib.WindowsPath, pathlib.PosixPath]
28
- ) -> ScalarNode:
26
+ dumper: SafeDumper, data: Union[pathlib.Path, pathlib.WindowsPath, pathlib.PosixPath]
27
+ ) -> Node:
29
28
  """Add a yaml-representation for a specific datatype."""
30
29
  return dumper.represent_scalar("tag:yaml.org,2002:str", str(data.as_posix()))
31
30
 
32
31
 
33
- def time2int(dumper: Dumper, data: timedelta) -> ScalarNode:
32
+ def time2int(dumper: SafeDumper, data: timedelta) -> Node:
34
33
  """Add a yaml-representation for a specific datatype."""
35
34
  return dumper.represent_scalar("tag:yaml.org,2002:int", str(int(data.total_seconds())))
36
35
 
37
36
 
38
- def generic2str(dumper: Dumper, data: Any) -> ScalarNode:
37
+ def generic2str(dumper: SafeDumper, data: Any) -> Node:
39
38
  """Add a yaml-representation for a specific datatype."""
40
39
  return dumper.represent_scalar("tag:yaml.org,2002:str", str(data))
41
40
 
@@ -1,13 +1,13 @@
1
1
  """Wrapper-related ecosystem for transferring models."""
2
2
 
3
3
  from datetime import datetime
4
+ from typing import Annotated
4
5
  from typing import Optional
5
6
 
6
7
  from pydantic import BaseModel
7
8
  from pydantic import StringConstraints
8
- from typing_extensions import Annotated
9
9
 
10
- from ...version import version
10
+ from shepherd_core.version import version
11
11
 
12
12
  SafeStrClone = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
13
13
  # ⤷ copy avoids circular import
@@ -2,13 +2,14 @@
2
2
 
3
3
  from enum import Enum
4
4
  from pathlib import Path
5
+ from typing import Any
5
6
  from typing import Optional
6
7
 
7
8
  from pydantic import PositiveFloat
8
9
  from pydantic import model_validator
9
10
 
10
- from ...testbed_client import tb_client
11
- from ..base.content import ContentModel
11
+ from shepherd_core.data_models.base.content import ContentModel
12
+ from shepherd_core.testbed_client import tb_client
12
13
 
13
14
 
14
15
  class EnergyDType(str, Enum):
@@ -46,7 +47,7 @@ class EnergyEnvironment(ContentModel):
46
47
 
47
48
  @model_validator(mode="before")
48
49
  @classmethod
49
- def query_database(cls, values: dict) -> dict:
50
+ def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
50
51
  values, _ = tb_client.try_completing_model(cls.__name__, values)
51
52
  # TODO: figure out a way to crosscheck type with actual data
52
53
  return tb_client.fill_in_user_data(values)
@@ -4,6 +4,8 @@ TODO: should be more generalized - currently only supports msp & nRF
4
4
  """
5
5
 
6
6
  from pathlib import Path
7
+ from typing import Annotated
8
+ from typing import Any
7
9
  from typing import Optional
8
10
  from typing import TypedDict
9
11
  from typing import Union
@@ -11,15 +13,15 @@ from typing import Union
11
13
  from pydantic import StringConstraints
12
14
  from pydantic import model_validator
13
15
  from pydantic import validate_call
14
- from typing_extensions import Annotated
15
16
  from typing_extensions import Self
16
17
  from typing_extensions import Unpack
17
18
 
18
- from ... import fw_tools
19
- from ...logger import logger
20
- from ...testbed_client import tb_client
21
- from ..base.content import ContentModel
22
- from ..testbed.mcu import MCU
19
+ from shepherd_core import fw_tools
20
+ from shepherd_core.data_models.base.content import ContentModel
21
+ from shepherd_core.data_models.testbed.mcu import MCU
22
+ from shepherd_core.logger import logger
23
+ from shepherd_core.testbed_client import tb_client
24
+
23
25
  from .firmware_datatype import FirmwareDType
24
26
 
25
27
  suffix_to_DType: dict = {
@@ -64,7 +66,7 @@ class Firmware(ContentModel, title="Firmware of Target"):
64
66
 
65
67
  @model_validator(mode="before")
66
68
  @classmethod
67
- def query_database(cls, values: dict) -> dict:
69
+ def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
68
70
  values, _ = tb_client.try_completing_model(cls.__name__, values)
69
71
  # crosscheck type with actual data
70
72
  _type = values.get("data_type")
@@ -1,20 +1,22 @@
1
1
  """Generalized energy harvester data models."""
2
2
 
3
+ from collections.abc import Mapping
3
4
  from enum import Enum
5
+ from typing import Annotated
6
+ from typing import Any
4
7
  from typing import Optional
5
- from typing import Tuple
6
8
 
7
9
  from pydantic import Field
8
10
  from pydantic import model_validator
9
- from typing_extensions import Annotated
10
11
  from typing_extensions import Self
11
12
 
12
- from ...commons import samplerate_sps_default
13
- from ...logger import logger
14
- from ...testbed_client import tb_client
15
- from ..base.calibration import CalibrationHarvester
16
- from ..base.content import ContentModel
17
- from ..base.shepherd import ShpModel
13
+ from shepherd_core.commons import SAMPLERATE_SPS_DEFAULT
14
+ from shepherd_core.data_models.base.calibration import CalibrationHarvester
15
+ from shepherd_core.data_models.base.content import ContentModel
16
+ from shepherd_core.data_models.base.shepherd import ShpModel
17
+ from shepherd_core.logger import logger
18
+ from shepherd_core.testbed_client import tb_client
19
+
18
20
  from .energy_environment import EnergyDType
19
21
 
20
22
 
@@ -73,7 +75,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
73
75
 
74
76
  @model_validator(mode="before")
75
77
  @classmethod
76
- def query_database(cls, values: dict) -> dict:
78
+ def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
77
79
  values, chain = tb_client.try_completing_model(cls.__name__, values)
78
80
  values = tb_client.fill_in_user_data(values)
79
81
  if values["name"] == "neutral":
@@ -114,14 +116,14 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
114
116
  return 1 * int(for_emu) + 2 * self.rising + 4 * self.enable_linear_extrapolation
115
117
 
116
118
  def calc_algorithm_num(self, *, for_emu: bool) -> int:
117
- num = algo_to_num.get(self.algorithm)
119
+ num: int = ALGO_TO_NUM.get(self.algorithm, ALGO_TO_NUM["neutral"])
118
120
  if for_emu and self.get_datatype() != EnergyDType.ivsample:
119
121
  msg = (
120
122
  f"[{self.name}] Select valid harvest-algorithm for emulator, "
121
123
  f"current usage = {self.algorithm}"
122
124
  )
123
125
  raise ValueError(msg)
124
- if not for_emu and num < algo_to_num["isc_voc"]:
126
+ if not for_emu and num < ALGO_TO_NUM["isc_voc"]:
125
127
  msg = (
126
128
  f"[{self.name}] Select valid harvest-algorithm for harvester, "
127
129
  f"current usage = {self.algorithm}"
@@ -129,12 +131,12 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
129
131
  raise ValueError(msg)
130
132
  return num
131
133
 
132
- def calc_timings_ms(self, *, for_emu: bool) -> Tuple[float, float]:
134
+ def calc_timings_ms(self, *, for_emu: bool) -> tuple[float, float]:
133
135
  """factor-in model-internal timing-constraints."""
134
136
  window_length = self.samples_n * (1 + self.wait_cycles)
135
- time_min_ms = (1 + self.wait_cycles) * 1_000 / samplerate_sps_default
137
+ time_min_ms = (1 + self.wait_cycles) * 1_000 / SAMPLERATE_SPS_DEFAULT
136
138
  if for_emu:
137
- window_ms = window_length * 1_000 / samplerate_sps_default
139
+ window_ms = window_length * 1_000 / SAMPLERATE_SPS_DEFAULT
138
140
  time_min_ms = max(time_min_ms, window_ms)
139
141
 
140
142
  interval_ms = min(max(self.interval_ms, time_min_ms), 1_000_000)
@@ -149,7 +151,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
149
151
  return interval_ms, duration_ms
150
152
 
151
153
  def get_datatype(self) -> EnergyDType:
152
- return algo_to_dtype[self.algorithm]
154
+ return ALGO_TO_DTYPE[self.algorithm]
153
155
 
154
156
  def calc_window_size(
155
157
  self,
@@ -184,7 +186,7 @@ u32 = Annotated[int, Field(ge=0, lt=2**32)]
184
186
  # - harvesting on "neutral" is not possible - direct pass-through
185
187
  # - emulation with "ivcurve" or lower is also resulting in Error
186
188
  # - "_opt" has its own algo for emulation, but is only a fast mppt_po for harvesting
187
- algo_to_num = {
189
+ ALGO_TO_NUM: Mapping[str, int] = {
188
190
  "neutral": 2**0,
189
191
  "isc_voc": 2**3,
190
192
  "ivcurve": 2**4,
@@ -195,7 +197,7 @@ algo_to_num = {
195
197
  "mppt_opt": 2**14,
196
198
  }
197
199
 
198
- algo_to_dtype = {
200
+ ALGO_TO_DTYPE: Mapping[str, EnergyDType] = {
199
201
  "neutral": EnergyDType.ivsample,
200
202
  "isc_voc": EnergyDType.isc_voc,
201
203
  "ivcurve": EnergyDType.ivcurve,
@@ -266,9 +268,15 @@ class HarvesterPRUConfig(ShpModel):
266
268
  if window_size is not None
267
269
  else data.calc_window_size(dtype_in, for_emu=for_emu)
268
270
  )
269
- voltage_step_mV = (
270
- 1e3 * voltage_step_V if voltage_step_V is not None else data.voltage_step_mV
271
- )
271
+ if voltage_step_V is not None:
272
+ voltage_step_mV = 1e3 * voltage_step_V
273
+ elif data.voltage_step_mV is not None:
274
+ voltage_step_mV = data.voltage_step_mV
275
+ else:
276
+ raise ValueError(
277
+ "For correct emulation specify voltage_step used by harvester "
278
+ "e.g. via file_src.get_voltage_step()"
279
+ )
272
280
 
273
281
  return cls(
274
282
  algorithm=data.calc_algorithm_num(for_emu=for_emu),
@@ -280,7 +288,7 @@ class HarvesterPRUConfig(ShpModel):
280
288
  voltage_step_uV=round(voltage_step_mV * 10**3),
281
289
  current_limit_nA=round(data.current_limit_uA * 10**3),
282
290
  setpoint_n8=round(min(255, data.setpoint_n * 2**8)),
283
- interval_n=round(interval_ms * samplerate_sps_default * 1e-3),
284
- duration_n=round(duration_ms * samplerate_sps_default * 1e-3),
291
+ interval_n=round(interval_ms * SAMPLERATE_SPS_DEFAULT * 1e-3),
292
+ duration_n=round(duration_ms * SAMPLERATE_SPS_DEFAULT * 1e-3),
285
293
  wait_cycles_n=data.wait_cycles,
286
294
  )
@@ -1,17 +1,18 @@
1
1
  """Generalized virtual source data models."""
2
2
 
3
- from typing import List
3
+ from typing import Annotated
4
+ from typing import Any
4
5
 
5
6
  from pydantic import Field
6
7
  from pydantic import model_validator
7
- from typing_extensions import Annotated
8
8
  from typing_extensions import Self
9
9
 
10
- from ...commons import samplerate_sps_default
11
- from ...logger import logger
12
- from ...testbed_client import tb_client
13
- from ..base.content import ContentModel
14
- from ..base.shepherd import ShpModel
10
+ from shepherd_core.commons import SAMPLERATE_SPS_DEFAULT
11
+ from shepherd_core.data_models.base.content import ContentModel
12
+ from shepherd_core.data_models.base.shepherd import ShpModel
13
+ from shepherd_core.logger import logger
14
+ from shepherd_core.testbed_client import tb_client
15
+
15
16
  from .energy_environment import EnergyDType
16
17
  from .virtual_harvester import HarvesterPRUConfig
17
18
  from .virtual_harvester import VirtualHarvesterConfig
@@ -19,8 +20,8 @@ from .virtual_harvester import VirtualHarvesterConfig
19
20
  # Custom Types
20
21
  LUT_SIZE: int = 12
21
22
  NormedNum = Annotated[float, Field(ge=0.0, le=1.0)]
22
- LUT1D = Annotated[List[NormedNum], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]
23
- LUT2D = Annotated[List[LUT1D], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]
23
+ LUT1D = Annotated[list[NormedNum], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]
24
+ LUT2D = Annotated[list[LUT1D], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]
24
25
 
25
26
 
26
27
  class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
@@ -113,7 +114,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
113
114
 
114
115
  @model_validator(mode="before")
115
116
  @classmethod
116
- def query_database(cls, values: dict) -> dict:
117
+ def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
117
118
  values, chain = tb_client.try_completing_model(cls.__name__, values)
118
119
  values = tb_client.fill_in_user_data(values)
119
120
  logger.debug("VSrc-Inheritances: %s", chain)
@@ -238,19 +239,19 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
238
239
  dV[uV] = constant[us/nF] * current[nA] = constant[us*V/nAs] * current[nA]
239
240
  """
240
241
  C_cap_uF = max(self.C_intermediate_uF, 0.001)
241
- return int((10**3 * (2**28)) // (C_cap_uF * samplerate_sps_default))
242
+ return int((10**3 * (2**28)) // (C_cap_uF * SAMPLERATE_SPS_DEFAULT))
242
243
 
243
244
 
244
245
  u32 = Annotated[int, Field(ge=0, lt=2**32)]
245
246
  u8 = Annotated[int, Field(ge=0, lt=2**8)]
246
247
  lut_i = Annotated[
247
- List[Annotated[List[u8], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]],
248
+ list[Annotated[list[u8], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]],
248
249
  Field(
249
250
  min_length=LUT_SIZE,
250
251
  max_length=LUT_SIZE,
251
252
  ),
252
253
  ]
253
- lut_o = Annotated[List[u32], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]
254
+ lut_o = Annotated[list[u32], Field(min_length=LUT_SIZE, max_length=LUT_SIZE)]
254
255
 
255
256
 
256
257
  class ConverterPRUConfig(ShpModel):
@@ -313,7 +314,7 @@ class ConverterPRUConfig(ShpModel):
313
314
  dtype_in, log_intermediate_node=log_intermediate_node
314
315
  ),
315
316
  interval_startup_delay_drain_n=round(
316
- data.interval_startup_delay_drain_ms * samplerate_sps_default * 1e-3
317
+ data.interval_startup_delay_drain_ms * SAMPLERATE_SPS_DEFAULT * 1e-3
317
318
  ),
318
319
  V_input_max_uV=round(data.V_input_max_mV * 1e3),
319
320
  I_input_max_nA=round(data.I_input_max_mA * 1e6),
@@ -326,7 +327,7 @@ class ConverterPRUConfig(ShpModel):
326
327
  V_disable_output_threshold_uV=round(states["V_disable_output_threshold_mV"] * 1e3),
327
328
  dV_enable_output_uV=round(states["dV_enable_output_mV"] * 1e3),
328
329
  interval_check_thresholds_n=round(
329
- data.interval_check_thresholds_ms * samplerate_sps_default * 1e-3
330
+ data.interval_check_thresholds_ms * SAMPLERATE_SPS_DEFAULT * 1e-3
330
331
  ),
331
332
  V_pwr_good_enable_threshold_uV=round(data.V_pwr_good_enable_threshold_mV * 1e3),
332
333
  V_pwr_good_disable_threshold_uV=round(data.V_pwr_good_disable_threshold_mV * 1e3),
@@ -1,9 +1,9 @@
1
1
  """Config for testbed experiments."""
2
2
 
3
+ from collections.abc import Iterable
3
4
  from datetime import datetime
4
5
  from datetime import timedelta
5
- from typing import Iterable
6
- from typing import List
6
+ from typing import Annotated
7
7
  from typing import Optional
8
8
  from typing import Union
9
9
  from uuid import uuid4
@@ -11,16 +11,16 @@ from uuid import uuid4
11
11
  from pydantic import UUID4
12
12
  from pydantic import Field
13
13
  from pydantic import model_validator
14
- from typing_extensions import Annotated
15
14
  from typing_extensions import Self
16
15
 
17
- from ...version import version
18
- from ..base.content import IdInt
19
- from ..base.content import NameStr
20
- from ..base.content import SafeStr
21
- from ..base.shepherd import ShpModel
22
- from ..testbed.target import Target
23
- from ..testbed.testbed import Testbed
16
+ from shepherd_core.data_models.base.content import IdInt
17
+ from shepherd_core.data_models.base.content import NameStr
18
+ from shepherd_core.data_models.base.content import SafeStr
19
+ from shepherd_core.data_models.base.shepherd import ShpModel
20
+ from shepherd_core.data_models.testbed.target import Target
21
+ from shepherd_core.data_models.testbed.testbed import Testbed
22
+ from shepherd_core.version import version
23
+
24
24
  from .observer_features import SystemLogging
25
25
  from .target_config import TargetConfig
26
26
 
@@ -54,7 +54,7 @@ class Experiment(ShpModel, title="Config of an Experiment"):
54
54
  abort_on_error: bool = False
55
55
 
56
56
  # targets
57
- target_configs: Annotated[List[TargetConfig], Field(min_length=1, max_length=128)]
57
+ target_configs: Annotated[list[TargetConfig], Field(min_length=1, max_length=128)]
58
58
 
59
59
  # for debug-purposes and later comp-checks
60
60
  lib_ver: Optional[str] = version
@@ -70,8 +70,8 @@ class Experiment(ShpModel, title="Config of an Experiment"):
70
70
 
71
71
  @staticmethod
72
72
  def _validate_targets(configs: Iterable[TargetConfig]) -> None:
73
- target_ids = []
74
- custom_ids = []
73
+ target_ids: list[int] = []
74
+ custom_ids: list[int] = []
75
75
  for _config in configs:
76
76
  for _id in _config.target_IDs:
77
77
  target_ids.append(_id)
@@ -2,18 +2,17 @@
2
2
 
3
3
  from datetime import timedelta
4
4
  from enum import Enum
5
- from typing import List
5
+ from typing import Annotated
6
6
  from typing import Optional
7
7
 
8
8
  import numpy as np
9
9
  from pydantic import Field
10
10
  from pydantic import PositiveFloat
11
11
  from pydantic import model_validator
12
- from typing_extensions import Annotated
13
12
  from typing_extensions import Self
14
13
 
15
- from ..base.shepherd import ShpModel
16
- from ..testbed.gpio import GPIO
14
+ from shepherd_core.data_models.base.shepherd import ShpModel
15
+ from shepherd_core.data_models.testbed.gpio import GPIO
17
16
 
18
17
 
19
18
  class PowerTracing(ShpModel, title="Config for Power-Tracing"):
@@ -27,7 +26,7 @@ class PowerTracing(ShpModel, title="Config for Power-Tracing"):
27
26
  # this also includes current!
28
27
 
29
28
  # time
30
- delay: timedelta = 0 # seconds
29
+ delay: timedelta = timedelta(seconds=0)
31
30
  duration: Optional[timedelta] = None # till EOF
32
31
 
33
32
  # post-processing
@@ -61,11 +60,11 @@ class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
61
60
  # initial recording
62
61
  mask: Annotated[int, Field(ge=0, lt=2**10)] = 0b11_1111_1111 # all
63
62
  # ⤷ TODO: custom mask not implemented in PRU, ATM
64
- gpios: Optional[Annotated[List[GPIO], Field(min_length=1, max_length=10)]] = None # = all
63
+ gpios: Optional[Annotated[list[GPIO], Field(min_length=1, max_length=10)]] = None # = all
65
64
  # ⤷ TODO: list of GPIO to build mask, one of both should be internal / computed field
66
65
 
67
66
  # time
68
- delay: timedelta = 0 # seconds
67
+ delay: timedelta = timedelta(seconds=0)
69
68
  duration: Optional[timedelta] = None # till EOF
70
69
 
71
70
  # post-processing,
@@ -125,7 +124,7 @@ class GpioActuation(ShpModel, title="Config for GPIO-Actuation"):
125
124
  # TODO: not implemented ATM - decide if pru control sys-gpio or
126
125
  # TODO: not implemented ATM - reverses pru-gpio (preferred if possible)
127
126
 
128
- events: Annotated[List[GpioEvent], Field(min_length=1, max_length=1024)]
127
+ events: Annotated[list[GpioEvent], Field(min_length=1, max_length=1024)]
129
128
 
130
129
  def get_gpios(self) -> set:
131
130
  return {_ev.gpio for _ev in self.events}
@@ -1,20 +1,20 @@
1
1
  """Configuration related to Target Nodes (DuT)."""
2
2
 
3
- from typing import List
3
+ from typing import Annotated
4
4
  from typing import Optional
5
5
 
6
6
  from pydantic import Field
7
7
  from pydantic import model_validator
8
- from typing_extensions import Annotated
9
8
  from typing_extensions import Self
10
9
 
11
- from ..base.content import IdInt
12
- from ..base.shepherd import ShpModel
13
- from ..content.energy_environment import EnergyEnvironment
14
- from ..content.firmware import Firmware
15
- from ..content.virtual_source import VirtualSourceConfig
16
- from ..testbed.target import IdInt16
17
- from ..testbed.target import Target
10
+ from shepherd_core.data_models.base.content import IdInt
11
+ from shepherd_core.data_models.base.shepherd import ShpModel
12
+ from shepherd_core.data_models.content.energy_environment import EnergyEnvironment
13
+ from shepherd_core.data_models.content.firmware import Firmware
14
+ from shepherd_core.data_models.content.virtual_source import VirtualSourceConfig
15
+ from shepherd_core.data_models.testbed.target import IdInt16
16
+ from shepherd_core.data_models.testbed.target import Target
17
+
18
18
  from .observer_features import GpioActuation
19
19
  from .observer_features import GpioTracing
20
20
  from .observer_features import PowerTracing
@@ -23,15 +23,15 @@ from .observer_features import PowerTracing
23
23
  class TargetConfig(ShpModel, title="Target Config"):
24
24
  """Configuration related to Target Nodes (DuT)."""
25
25
 
26
- target_IDs: Annotated[List[IdInt], Field(min_length=1, max_length=128)]
27
- custom_IDs: Optional[Annotated[List[IdInt16], Field(min_length=1, max_length=128)]] = None
26
+ target_IDs: Annotated[list[IdInt], Field(min_length=1, max_length=128)]
27
+ custom_IDs: Optional[Annotated[list[IdInt16], Field(min_length=1, max_length=128)]] = None
28
28
  # ⤷ will replace 'const uint16_t SHEPHERD_NODE_ID' in firmware
29
29
  # if no custom ID is provided, the original ID of target is used
30
30
 
31
31
  energy_env: EnergyEnvironment # alias: input
32
32
  virtual_source: VirtualSourceConfig = VirtualSourceConfig(name="neutral")
33
33
  target_delays: Optional[
34
- Annotated[List[Annotated[int, Field(ge=0)]], Field(min_length=1, max_length=128)]
34
+ Annotated[list[Annotated[int, Field(ge=0)]], Field(min_length=1, max_length=128)]
35
35
  ] = None
36
36
  # ⤷ individual starting times -> allows to use the same environment
37
37
  # TODO: delays not used ATM