shepherd-core 2025.4.2__py3-none-any.whl → 2025.5.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 (29) hide show
  1. shepherd_core/data_models/__init__.py +2 -0
  2. shepherd_core/data_models/content/_external_fixtures.yaml +32 -32
  3. shepherd_core/data_models/content/energy_environment.py +2 -2
  4. shepherd_core/data_models/content/virtual_harvester.py +4 -4
  5. shepherd_core/data_models/content/virtual_harvester_fixture.yaml +2 -2
  6. shepherd_core/data_models/content/virtual_source.py +4 -2
  7. shepherd_core/data_models/content/virtual_source_fixture.yaml +3 -3
  8. shepherd_core/data_models/experiment/experiment.py +2 -2
  9. shepherd_core/data_models/experiment/observer_features.py +102 -8
  10. shepherd_core/data_models/experiment/target_config.py +5 -0
  11. shepherd_core/data_models/task/__init__.py +6 -3
  12. shepherd_core/data_models/task/emulation.py +19 -3
  13. shepherd_core/data_models/task/programming.py +2 -1
  14. shepherd_core/data_models/testbed/mcu_fixture.yaml +4 -4
  15. shepherd_core/data_models/virtual_source_doc.txt +3 -3
  16. shepherd_core/fw_tools/converter.py +6 -3
  17. shepherd_core/fw_tools/validation.py +8 -4
  18. shepherd_core/reader.py +75 -46
  19. shepherd_core/testbed_client/client_abc_fix.py +2 -3
  20. shepherd_core/testbed_client/fixtures.py +2 -6
  21. shepherd_core/version.py +1 -1
  22. shepherd_core/vsource/virtual_harvester_simulation.py +1 -1
  23. shepherd_core/vsource/virtual_source_simulation.py +1 -1
  24. shepherd_core/writer.py +8 -8
  25. {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/METADATA +1 -1
  26. {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/RECORD +29 -29
  27. {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/WHEEL +1 -1
  28. {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/top_level.txt +0 -0
  29. {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/zip-safe +0 -0
@@ -43,7 +43,8 @@ class ProgrammingTask(ShpModel):
43
43
  def post_validation(self) -> Self:
44
44
  d_type = suffix_to_DType.get(self.firmware_file.suffix.lower())
45
45
  if d_type != FirmwareDType.base64_hex:
46
- raise ValueError("Firmware is not intel-.hex ('%s')", self.firmware_file.as_posix())
46
+ msg = (f"Firmware is not intel-hex ({self.firmware_file.as_posix()})",)
47
+ raise ValueError(msg)
47
48
  return self
48
49
 
49
50
  @classmethod
@@ -3,17 +3,17 @@
3
3
  parameters:
4
4
  id: 1001
5
5
  name: nRF52
6
- description: Panasonic PAN1780, ENW-89854A1KF, Bluetooth 5 Low Energy Module
6
+ description: MCU with RF, 802.15.4, Bluetooth v5.0, 2.4GHz
7
7
  platform: nRF52
8
8
  core: nRF52840
9
9
  prog_protocol: SWD
10
- fw_name_default: nrf52_demo_rf
10
+ fw_name_default: nrf52_deep_sleep
11
11
  - datatype: mcu
12
12
  parameters:
13
13
  id: 1002
14
14
  name: MSP430FR
15
- description: 16MHz Ultra-Low-Pwr MCU with 128 KB FRAM
15
+ description: 16MHz Ultra-Low-Pwr MCU with 256 KB FRAM
16
16
  platform: MSP430
17
- core: MSP430FR5962
17
+ core: MSP430FR5994
18
18
  prog_protocol: SBW
19
19
  fw_name_default: msp430_deep_sleep
@@ -35,14 +35,14 @@ class VirtualSourceDoc(ShpModel, title="Virtual Source (Documented, Testversion)
35
35
  )
36
36
 
37
37
  interval_startup_delay_drain_ms: float = Field(
38
- description="Model begins running but Target is not draining the buffer",
38
+ description="Model begins running but Target is not draining the storage capacitor",
39
39
  default=0,
40
40
  ge=0,
41
41
  le=10e3,
42
42
  )
43
43
 
44
44
  harvester: VirtualHarvesterConfig = Field(
45
- description="Only active / needed if input is 'ivcurves'",
45
+ description="Only active / needed if input is ivsurface / curves,
46
46
  default=VirtualHarvesterConfig(name="mppt_opt"),
47
47
  )
48
48
 
@@ -85,7 +85,7 @@ class VirtualSourceDoc(ShpModel, title="Virtual Source (Documented, Testversion)
85
85
  le=10_000,
86
86
  )
87
87
  I_intermediate_leak_nA: float = Field(
88
- description="Current leakage of intermediate buffer capacitor",
88
+ description="Current leakage of intermediate storage capacitor",
89
89
  default=0,
90
90
  ge=0,
91
91
  le=4.29e9,
@@ -28,7 +28,8 @@ def firmware_to_hex(file_path: Path) -> Path:
28
28
  return elf_to_hex(file_path)
29
29
  if is_hex(file_path):
30
30
  return file_path
31
- raise FileNotFoundError("FW2Hex: unknown file '%s', it should be ELF or HEX", file_path.name)
31
+ msg = (f"FW2Hex: unknown file '{file_path.name}', it should be ELF or HEX",)
32
+ raise FileNotFoundError(msg)
32
33
 
33
34
 
34
35
  @validate_call
@@ -98,10 +99,12 @@ def extract_firmware(data: Union[str, Path], data_type: FirmwareDType, file_path
98
99
  elif data_type == FirmwareDType.path_hex:
99
100
  file = file_path.with_suffix(".hex")
100
101
  else:
101
- raise ValueError("FW-Extraction failed due to unknown datatype '%s'", data_type)
102
+ msg = "FW-Extraction failed due to unknown datatype '{data_type}'"
103
+ raise ValueError(msg)
102
104
  if not file.parent.exists():
103
105
  file.parent.mkdir(parents=True)
104
106
  shutil.copy(data, file)
105
107
  else:
106
- raise ValueError("FW-Extraction failed due to unknown data-type '%s'", type(data))
108
+ msg = f"FW-Extraction failed due to unknown data-type '{type(data)}'"
109
+ raise ValueError(msg)
107
110
  return file
@@ -137,7 +137,8 @@ def determine_type(file: Path) -> FirmwareDType:
137
137
  return FirmwareDType.path_hex
138
138
  if is_elf(file):
139
139
  return FirmwareDType.path_elf
140
- raise ValueError("Type of file '%s' could not be determined", file.name)
140
+ msg = f"Type of file '{file.name}' could not be determined"
141
+ raise ValueError(msg)
141
142
 
142
143
 
143
144
  def determine_arch(file: Path) -> str:
@@ -148,11 +149,14 @@ def determine_arch(file: Path) -> str:
148
149
  return "msp430"
149
150
  if is_elf_nrf52(file):
150
151
  return "nrf52"
151
- raise ValueError("Arch of ELF '%s' could not be determined", file.name)
152
+ msg = f"Arch of ELF '{file.name}' could not be determined"
153
+ raise ValueError(msg)
152
154
  if file_t == FirmwareDType.path_hex:
153
155
  if is_hex_msp430(file):
154
156
  return "msp430"
155
157
  if is_hex_nrf52(file):
156
158
  return "nrf52"
157
- raise ValueError("Arch of HEX '%s' could not be determined", file.name)
158
- raise ValueError("Arch of file '%s' could not be determined", file.name)
159
+ msg = f"Arch of HEX '{file.name}' could not be determined"
160
+ raise ValueError(msg)
161
+ msg = f"Arch of file '{file.name}' could not be determined"
162
+ raise ValueError(msg)
shepherd_core/reader.py CHANGED
@@ -11,6 +11,7 @@ from itertools import product
11
11
  from pathlib import Path
12
12
  from types import MappingProxyType
13
13
  from typing import TYPE_CHECKING
14
+ from typing import Annotated
14
15
  from typing import Any
15
16
  from typing import Optional
16
17
  from typing import Union
@@ -21,6 +22,7 @@ import yaml
21
22
  from pydantic import validate_call
22
23
  from tqdm import trange
23
24
  from typing_extensions import Self
25
+ from typing_extensions import deprecated
24
26
 
25
27
  from .commons import SAMPLERATE_SPS_DEFAULT
26
28
  from .data_models.base.calibration import CalibrationPair
@@ -45,7 +47,7 @@ class Reader:
45
47
 
46
48
  """
47
49
 
48
- BUFFER_SAMPLES_N: int = 10_000
50
+ CHUNK_SAMPLES_N: int = 10_000
49
51
 
50
52
  MODE_TO_DTYPE: Mapping[str, Sequence[EnergyDType]] = MappingProxyType(
51
53
  {
@@ -82,10 +84,13 @@ class Reader:
82
84
 
83
85
  # init stats
84
86
  self.runtime_s: float = 0
85
- self.buffers_n: int = 0
87
+ self.samples_n: int = 0
88
+ self.chunks_n: int = 0
86
89
  self.file_size: int = 0
87
90
  self.data_rate: float = 0
88
91
 
92
+ self.buffers_n: Annotated[int, deprecated("use .chunk_n instead")] = 0
93
+
89
94
  # open file (if not already done by writer)
90
95
  self._reader_opened: bool = False
91
96
  if not hasattr(self, "h5file"):
@@ -113,7 +118,8 @@ class Reader:
113
118
  )
114
119
 
115
120
  if not isinstance(self.h5file, h5py.File):
116
- raise TypeError("Type of opened file is not h5py.File, for %s", self.file_path.name)
121
+ msg = (f"Type of opened file is not h5py.File, for {self.file_path.name}",)
122
+ raise TypeError(msg)
117
123
 
118
124
  self.ds_time: h5py.Dataset = self.h5file["data"]["time"]
119
125
  self.ds_voltage: h5py.Dataset = self.h5file["data"]["voltage"]
@@ -169,59 +175,63 @@ class Reader:
169
175
  def _refresh_file_stats(self) -> None:
170
176
  """Update internal states, helpful after resampling or other changes in data-group."""
171
177
  self.h5file.flush()
172
- sample_count = self.ds_time.shape[0]
178
+ self.samples_n = min(
179
+ self.ds_time.shape[0], self.ds_current.shape[0], self.ds_voltage.shape[0]
180
+ )
173
181
  duration_raw = (
174
- (int(self.ds_time[sample_count - 1]) - int(self.ds_time[0])) if sample_count > 0 else 0
182
+ (int(self.ds_time[self.samples_n - 1]) - int(self.ds_time[0]))
183
+ if self.samples_n > 0
184
+ else 0
175
185
  )
176
186
  # above's typecasting prevents overflow in u64-format
177
- if (sample_count > 0) and (duration_raw > 0):
178
- # this assumes iso-chronous sampling
187
+ if (self.samples_n > 0) and (duration_raw > 0):
188
+ # this assumes iso-chronous sampling, TODO: not the best choice?
179
189
  duration_s = self._cal.time.raw_to_si(duration_raw)
180
- self.sample_interval_s = duration_s / sample_count
190
+ self.sample_interval_s = duration_s / self.samples_n
181
191
  self.sample_interval_ns = round(10**9 * self.sample_interval_s)
182
- self.samplerate_sps = max(round((sample_count - 1) / duration_s), 1)
183
- self.runtime_s = round(self.ds_voltage.shape[0] / self.samplerate_sps, 1)
184
- self.buffers_n = int(self.ds_voltage.shape[0] // self.BUFFER_SAMPLES_N)
192
+ self.samplerate_sps = max(round((self.samples_n - 1) / duration_s), 1)
193
+ self.runtime_s = round(self.samples_n / self.samplerate_sps, 1)
194
+ self.chunks_n = self.buffers_n = int(self.samples_n // self.CHUNK_SAMPLES_N)
185
195
  if isinstance(self.file_path, Path):
186
196
  self.file_size = self.file_path.stat().st_size
187
197
  else:
188
198
  self.file_size = 0
189
199
  self.data_rate = self.file_size / self.runtime_s if self.runtime_s > 0 else 0
190
200
 
191
- def read_buffers(
201
+ def read(
192
202
  self,
193
203
  start_n: int = 0,
194
204
  end_n: Optional[int] = None,
195
- n_samples_per_buffer: Optional[int] = None,
205
+ n_samples_per_chunk: Optional[int] = None,
196
206
  *,
197
207
  is_raw: bool = False,
198
- omit_ts: bool = False,
208
+ omit_timestamps: bool = False,
199
209
  ) -> Generator[tuple, None, None]:
200
- """Read the specified range of buffers from the hdf5 file.
210
+ """Read the specified range of chunks from the hdf5 file.
201
211
 
202
212
  Generator - can be configured on first call
203
213
 
204
214
  Args:
205
215
  ----
206
- :param start_n: (int) Index of first buffer to be read
207
- :param end_n: (int) Index of last buffer to be read
208
- :param n_samples_per_buffer: (int) allows changing
216
+ :param start_n: (int) Index of first chunk to be read
217
+ :param end_n: (int) Index of last chunk to be read
218
+ :param n_samples_per_chunk: (int) allows changing
209
219
  :param is_raw: (bool) output original data, not transformed to SI-Units
210
- :param omit_ts: (bool) optimize reading if timestamp is never used
211
- Yields: Buffers between start and end (tuple with time, voltage, current)
220
+ :param omit_timestamps: (bool) optimize reading if timestamp is never used
221
+ Yields: chunks between start and end (tuple with time, voltage, current)
212
222
 
213
223
  """
214
- if n_samples_per_buffer is None:
215
- n_samples_per_buffer = self.BUFFER_SAMPLES_N
216
- end_max = int(self.ds_voltage.shape[0] // n_samples_per_buffer)
224
+ if n_samples_per_chunk is None:
225
+ n_samples_per_chunk = self.CHUNK_SAMPLES_N
226
+ end_max = int(self.samples_n // n_samples_per_chunk)
217
227
  end_n = end_max if end_n is None else min(end_n, end_max)
218
- self._logger.debug("Reading blocks %d to %d from source-file", start_n, end_n)
228
+ self._logger.debug("Reading chunk %d to %d from source-file", start_n, end_n)
219
229
  _raw = is_raw
220
- _wts = not omit_ts
230
+ _wts = not omit_timestamps
221
231
 
222
232
  for i in range(start_n, end_n):
223
- idx_start = i * n_samples_per_buffer
224
- idx_end = idx_start + n_samples_per_buffer
233
+ idx_start = i * n_samples_per_chunk
234
+ idx_end = idx_start + n_samples_per_chunk
225
235
  if _raw:
226
236
  yield (
227
237
  self.ds_time[idx_start:idx_end] if _wts else None,
@@ -235,6 +245,24 @@ class Reader:
235
245
  self._cal.current.raw_to_si(self.ds_current[idx_start:idx_end]),
236
246
  )
237
247
 
248
+ @deprecated("use .read() instead")
249
+ def read_buffers(
250
+ self,
251
+ start_n: int = 0,
252
+ end_n: Optional[int] = None,
253
+ n_samples_per_buffer: Optional[int] = None,
254
+ *,
255
+ is_raw: bool = False,
256
+ omit_ts: bool = False,
257
+ ) -> Generator[tuple, None, None]:
258
+ return self.read(
259
+ start_n=start_n,
260
+ end_n=end_n,
261
+ n_samples_per_chunk=n_samples_per_buffer,
262
+ is_raw=is_raw,
263
+ omit_timestamps=omit_ts,
264
+ )
265
+
238
266
  def get_calibration_data(self) -> CalibrationSeries:
239
267
  """Read calibration-data from hdf5 file.
240
268
 
@@ -384,23 +412,22 @@ class Reader:
384
412
  self.file_path.name,
385
413
  )
386
414
  # same length of datasets:
387
- ds_volt_size = self.h5file["data"]["voltage"].shape[0]
388
415
  for dset in ["current", "time"]:
389
416
  ds_size = self.h5file["data"][dset].shape[0]
390
- if ds_volt_size != ds_size:
417
+ if ds_size != self.samples_n:
391
418
  self._logger.warning(
392
419
  "[FileValidation] dataset '%s' has different size (=%d), "
393
- "compared to time-ds (=%d), in '%s'",
420
+ "compared to smallest set (=%d), in '%s'",
394
421
  dset,
395
422
  ds_size,
396
- ds_volt_size,
423
+ self.samples_n,
397
424
  self.file_path.name,
398
425
  )
399
- # dataset-length should be multiple of buffersize
400
- remaining_size = ds_volt_size % self.BUFFER_SAMPLES_N
426
+ # dataset-length should be multiple of chunk-size
427
+ remaining_size = self.samples_n % self.CHUNK_SAMPLES_N
401
428
  if remaining_size != 0:
402
429
  self._logger.warning(
403
- "[FileValidation] datasets are not aligned with buffer-size in '%s'",
430
+ "[FileValidation] datasets are not aligned with chunk-size in '%s'",
404
431
  self.file_path.name,
405
432
  )
406
433
  # check compression
@@ -455,10 +482,10 @@ class Reader:
455
482
 
456
483
  :return: sampled energy in Ws (watt-seconds)
457
484
  """
458
- iterations = math.ceil(self.ds_voltage.shape[0] / self.max_elements)
485
+ iterations = math.ceil(self.samples_n / self.max_elements)
459
486
  job_iter = trange(
460
487
  0,
461
- self.ds_voltage.shape[0],
488
+ self.samples_n,
462
489
  self.max_elements,
463
490
  desc="energy",
464
491
  leave=False,
@@ -466,7 +493,7 @@ class Reader:
466
493
  )
467
494
 
468
495
  def _calc_energy(idx_start: int) -> float:
469
- idx_stop = min(idx_start + self.max_elements, self.ds_voltage.shape[0])
496
+ idx_stop = min(idx_start + self.max_elements, self.samples_n)
470
497
  vol_v = self._cal.voltage.raw_to_si(self.ds_voltage[idx_start:idx_stop])
471
498
  cur_a = self._cal.current.raw_to_si(self.ds_current[idx_start:idx_stop])
472
499
  return (vol_v[:] * cur_a[:]).sum() * self.sample_interval_s
@@ -520,16 +547,18 @@ class Reader:
520
547
  return stats
521
548
 
522
549
  def _data_timediffs(self) -> list[float]:
523
- """Calculate list of unique time-deltas [s] between buffers.
550
+ """Calculate list of unique time-deltas [s] between chunks.
524
551
 
525
- Optimized version that only looks at the start of each buffer.
552
+ Optimized version that only looks at the start of each chunk.
553
+ Timestamps get converted to signed (it still fits > 100 years)
554
+ to allow calculating negative diffs.
526
555
 
527
- :return: list of (unique) time-deltas between buffers [s]
556
+ :return: list of (unique) time-deltas between chunks [s]
528
557
  """
529
- iterations = math.ceil(self.ds_time.shape[0] / self.max_elements)
558
+ iterations = math.ceil(self.samples_n / self.max_elements)
530
559
  job_iter = trange(
531
560
  0,
532
- self.h5file["data"]["time"].shape[0],
561
+ self.samples_n,
533
562
  self.max_elements,
534
563
  desc="timediff",
535
564
  leave=False,
@@ -538,14 +567,14 @@ class Reader:
538
567
 
539
568
  def calc_timediffs(idx_start: int) -> list:
540
569
  ds_time = self.ds_time[
541
- idx_start : (idx_start + self.max_elements) : self.BUFFER_SAMPLES_N
542
- ]
570
+ idx_start : (idx_start + self.max_elements) : self.CHUNK_SAMPLES_N
571
+ ].astype(np.int64)
543
572
  diffs_np = np.unique(ds_time[1:] - ds_time[0:-1], return_counts=False)
544
573
  return list(np.array(diffs_np))
545
574
 
546
575
  diffs_ll = [calc_timediffs(i) for i in job_iter]
547
576
  diffs = {
548
- round(self._cal.time.raw_to_si(j) / self.BUFFER_SAMPLES_N, 6)
577
+ round(self._cal.time.raw_to_si(j) / self.CHUNK_SAMPLES_N, 6)
549
578
  for i in diffs_ll
550
579
  for j in i
551
580
  }
@@ -563,7 +592,7 @@ class Reader:
563
592
  self._logger.warning(
564
593
  "Time-jumps detected -> expected equal steps, but got: %s s", diffs
565
594
  )
566
- return (len(diffs) <= 1) and diffs[0] == round(0.1 / self.BUFFER_SAMPLES_N, 6)
595
+ return (len(diffs) <= 1) and diffs[0] == round(0.1 / self.CHUNK_SAMPLES_N, 6)
567
596
 
568
597
  def count_errors_in_log(self, group_name: str = "sheep", min_level: int = 40) -> int:
569
598
  if group_name not in self.h5file:
@@ -72,9 +72,8 @@ class AbcClient(ABC):
72
72
  try:
73
73
  values = self.query_item(model_type, name=values.get("name"), uid=values.get("id"))
74
74
  except ValueError as err:
75
- raise ValueError(
76
- "Query %s by name / ID failed - %s is unknown!", model_type, values
77
- ) from err
75
+ msg = f"Query {model_type} by name / ID failed - {values} is unknown!"
76
+ raise ValueError(msg) from err
78
77
  return self.try_inheritance(model_type, values)
79
78
 
80
79
  @abstractmethod
@@ -101,14 +101,10 @@ class Fixture:
101
101
  base_name = values.get("name")
102
102
  if base_name in chain:
103
103
  msg = f"Inheritance-Circle detected ({base_name} already in {chain})"
104
- raise ValueError(
105
- msg,
106
- )
104
+ raise ValueError(msg)
107
105
  if base_name == fixture_name:
108
106
  msg = f"Inheritance-Circle detected ({base_name} == {fixture_name})"
109
- raise ValueError(
110
- msg,
111
- )
107
+ raise ValueError(msg)
112
108
  chain.append(base_name)
113
109
  fixture_base = copy.copy(self[fixture_name])
114
110
  logger.debug("'%s' will inherit from '%s'", self.model_type, fixture_name)
shepherd_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2025.04.2"
3
+ version: str = "2025.05.2"
@@ -54,7 +54,7 @@ def simulate_harvester(
54
54
  e_out_Ws = 0.0
55
55
 
56
56
  for _t, v_inp, i_inp in tqdm(
57
- file_inp.read_buffers(is_raw=True), total=file_inp.buffers_n, desc="Buffers", leave=False
57
+ file_inp.read(is_raw=True), total=file_inp.chunks_n, desc="Chunk", leave=False
58
58
  ):
59
59
  v_uV = cal_inp.voltage.raw_to_si(v_inp) * 1e6
60
60
  i_nA = cal_inp.current.raw_to_si(i_inp) * 1e9
@@ -74,7 +74,7 @@ def simulate_source(
74
74
  stats_internal = None
75
75
 
76
76
  for _t, v_inp, i_inp in tqdm(
77
- file_inp.read_buffers(is_raw=True), total=file_inp.buffers_n, desc="Buffers", leave=False
77
+ file_inp.read(is_raw=True), total=file_inp.chunks_n, desc="Chunk", leave=False
78
78
  ):
79
79
  v_uV = 1e6 * cal_inp.voltage.raw_to_si(v_inp)
80
80
  i_nA = 1e9 * cal_inp.current.raw_to_si(i_inp)
shepherd_core/writer.py CHANGED
@@ -94,7 +94,7 @@ class Writer(Reader):
94
94
  MODE_DEFAULT: str = "harvester"
95
95
  DATATYPE_DEFAULT: EnergyDType = EnergyDType.ivsample
96
96
 
97
- _CHUNK_SHAPE: tuple = (Reader.BUFFER_SAMPLES_N,)
97
+ _CHUNK_SHAPE: tuple = (Reader.CHUNK_SAMPLES_N,)
98
98
 
99
99
  @validate_call
100
100
  def __init__(
@@ -240,7 +240,7 @@ class Writer(Reader):
240
240
  # Store voltage and current samples in the data group,
241
241
  # both are stored as 4 Byte unsigned int
242
242
  grp_data = self.h5file.create_group("data")
243
- # the size of window_samples-attribute in harvest-data indicates ivcurves as input
243
+ # the size of window_samples-attribute in harvest-data indicates ivsurface / curves as input
244
244
  # -> emulator uses virtual-harvester, field will be adjusted by .embed_config()
245
245
  grp_data.attrs["window_samples"] = 0
246
246
 
@@ -287,7 +287,7 @@ class Writer(Reader):
287
287
 
288
288
  Args:
289
289
  ----
290
- timestamp: just start of buffer or whole ndarray
290
+ timestamp: just start of chunk (1 timestamp) or whole ndarray
291
291
  voltage: ndarray as raw unsigned integers
292
292
  current: ndarray as raw unsigned integers
293
293
 
@@ -331,7 +331,7 @@ class Writer(Reader):
331
331
  Args:
332
332
  ----
333
333
  timestamp: python timestamp (time.time()) in seconds (si-unit)
334
- -> provide start of buffer or whole ndarray
334
+ -> provide start of chunk (1 timestamp) or whole ndarray
335
335
  voltage: ndarray in physical-unit V
336
336
  current: ndarray in physical-unit A
337
337
 
@@ -343,16 +343,16 @@ class Writer(Reader):
343
343
  self.append_iv_data_raw(timestamp, voltage, current)
344
344
 
345
345
  def _align(self) -> None:
346
- """Align datasets with buffer-size of shepherd."""
346
+ """Align datasets with chunk-size of shepherd."""
347
347
  self._refresh_file_stats()
348
- n_buff = self.ds_voltage.size / self.BUFFER_SAMPLES_N
349
- size_new = int(math.floor(n_buff) * self.BUFFER_SAMPLES_N)
348
+ chunks_n = self.ds_voltage.size / self.CHUNK_SAMPLES_N
349
+ size_new = int(math.floor(chunks_n) * self.CHUNK_SAMPLES_N)
350
350
  if size_new < self.ds_voltage.size:
351
351
  if self.samplerate_sps != SAMPLERATE_SPS_DEFAULT:
352
352
  self._logger.debug("skipped alignment due to altered samplerate")
353
353
  return
354
354
  self._logger.info(
355
- "aligning with buffer-size, discarding last %d entries",
355
+ "aligning with chunk-size, discarding last %d entries",
356
356
  self.ds_voltage.size - size_new,
357
357
  )
358
358
  self.ds_time.resize((size_new,))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shepherd_core
3
- Version: 2025.4.2
3
+ Version: 2025.5.2
4
4
  Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
5
5
  Author-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
6
6
  Maintainer-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
@@ -2,12 +2,12 @@ shepherd_core/__init__.py,sha256=fCld2mcl0y0h6kRyPal3DP-sWXnKl_0aYWYBdg4QuUk,127
2
2
  shepherd_core/calibration_hw_def.py,sha256=aL94bA1Sf14L5A3PLdVvQVYtGi28S4NUWA65wbim8bw,2895
3
3
  shepherd_core/commons.py,sha256=rTxtndtiJ4cOHYRPRbdZdqp6T90CKFFN-I-YAFzhm4Q,200
4
4
  shepherd_core/logger.py,sha256=i8j3alm8icAx_h1_IZ6SgVgC9W5J7S-bDc31mPDWl-w,1812
5
- shepherd_core/reader.py,sha256=Xu_MSADsR4Xzh8YH-N50mKIHZlhPyFigv8umQSsYsJ0,28320
6
- shepherd_core/version.py,sha256=dd_uzGz0uQwh1pttFJSjXzIKovnBzMAXJGgpRa5QR34,76
7
- shepherd_core/writer.py,sha256=HRiCnJRM4hIBT6UBZJZ5ZOZ79OyBpBN4a2kX-XmOYRE,14574
8
- shepherd_core/data_models/__init__.py,sha256=bnHSP_HBOYm4WuoiHs_vyiRsWlvkyDjsarMNfKedN-Q,1836
5
+ shepherd_core/reader.py,sha256=EYgpL09DMrACiDHoG3LbgSJrci4pCPY3r13X0XVNVLI,29179
6
+ shepherd_core/version.py,sha256=p9ZnvPFg9wu9KOtST8r_-f6-9CC_FMIG7IhKqfjDez4,76
7
+ shepherd_core/writer.py,sha256=_ji3X2bb4JKZZm4uz8DO9uG_WoHL5Zpg6_Na36WAF5o,14609
8
+ shepherd_core/data_models/__init__.py,sha256=Fl9Lhdo32mrdF232ZjOnkawQYeFsUbTS4FOUrvmSzlM,1909
9
9
  shepherd_core/data_models/readme.md,sha256=DHPVmkWqDksWomRHRTVWVHy9wXF9oMJrITgKs4Pnz2g,2494
10
- shepherd_core/data_models/virtual_source_doc.txt,sha256=KizMcfGKj7BnHIbaJHT7KeTF01SV__UXv01qV_DGHSs,6057
10
+ shepherd_core/data_models/virtual_source_doc.txt,sha256=OK_7zYbLvr6cEj3KaUcWwZJ9naoFB2KwAaXudbhzouQ,6076
11
11
  shepherd_core/data_models/base/__init__.py,sha256=PSJ6acWViqBm0Eiom8DIgKfFVrp5lzYr8OsDvP79vwI,94
12
12
  shepherd_core/data_models/base/cal_measurement.py,sha256=c-vjACNxsQY8LU2Msw0COrsTY-8pToJ5ZWkOztJ9tVY,3380
13
13
  shepherd_core/data_models/base/calibration.py,sha256=oUTfY6iUWUbBbOw5aMCRkdEfHzIV8aUrhwqek0QzPJM,10849
@@ -16,25 +16,25 @@ shepherd_core/data_models/base/shepherd.py,sha256=P1xW10_vIbGNyQ3Ary67vyovw9kmDt
16
16
  shepherd_core/data_models/base/timezone.py,sha256=2T6E46hJ1DAvmqKfu6uIgCK3RSoAKjGXRyzYNaqKyjY,665
17
17
  shepherd_core/data_models/base/wrapper.py,sha256=7QwvI30GuORH7WmyGLnRMsZ3xkRRkXIAvZ-pYRAL-WI,755
18
18
  shepherd_core/data_models/content/__init__.py,sha256=69aiNG0h5t1OF7HsLg_ke5eaQKsKyMK8o6Kfaby5vlY,525
19
- shepherd_core/data_models/content/_external_fixtures.yaml,sha256=0CH7YSWT_hzL-jcg4JjgN9ryQOzbS8S66_pd6GbMnHw,12259
20
- shepherd_core/data_models/content/energy_environment.py,sha256=emlVFbcPA0-_jgpYI0Xwebm81_gWLYD8Tex0XVtJr4c,1618
19
+ shepherd_core/data_models/content/_external_fixtures.yaml,sha256=Iv0T5cN8BFVELuLtmxml2-xwcMDMZWF7LOu_TM3RIJ8,12184
20
+ shepherd_core/data_models/content/energy_environment.py,sha256=DSTB3CE3XEBXaL16z4FVaHSyLnp0bfxUgw3SnQrUpf0,1628
21
21
  shepherd_core/data_models/content/energy_environment_fixture.yaml,sha256=UBXTdGT7MK98zx5w_RBCu-f9uNCKxRgiFBQFbmDUxPc,1301
22
22
  shepherd_core/data_models/content/firmware.py,sha256=KLByo1GuUMJ8ZbM6WccB8IJArorNetBxqOpuUb2_QIE,5926
23
23
  shepherd_core/data_models/content/firmware_datatype.py,sha256=XPU9LOoT3h5qFOlE8WU0vAkw-vymNxzor9kVFyEqsWg,255
24
- shepherd_core/data_models/content/virtual_harvester.py,sha256=PQ3CykoMgMoklmb_vypRdU512BuVhV-KjPTmBPKTzCM,11227
25
- shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=LZe5ue1xYhXZwB3a32sva-L4uKhkQA5AtG9JzW4B2hQ,4564
26
- shepherd_core/data_models/content/virtual_source.py,sha256=ftCcWCYKHw854lGwuxLf_ZIOY6O9vLixHHC77BqcSQA,15441
27
- shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=1o-31mGgn7eyCNidKoOUp9vZh3K4Al0kJgmz54Q2DAE,11191
24
+ shepherd_core/data_models/content/virtual_harvester.py,sha256=QLyOLH0Q1E8NKWgWyMd4e65iEA13CVHmdf7kLf4SqFk,11287
25
+ shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=4eNQyFI9LozpButOTlBQ07T0MFCaPEYIxwtedMjUf3U,4575
26
+ shepherd_core/data_models/content/virtual_source.py,sha256=4TAk7jgXCQh6MMnT0Ijw_AdE6YPgrbSwFfb8F1V6RVE,15565
27
+ shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=tsSJ1mM4OSPSeNkqVoZlDQ8Az3N8KeTXd0oA2JKd-hk,11201
28
28
  shepherd_core/data_models/experiment/__init__.py,sha256=lorsx0M-JWPIrt_UZfexsLwaITv5slFb3krBOt0idm8,618
29
- shepherd_core/data_models/experiment/experiment.py,sha256=h24HaZltat0mKbOT1h9O58B5noTIUcGQbBIzmPi4oo8,4232
30
- shepherd_core/data_models/experiment/observer_features.py,sha256=u6nJthpluZrxw9ym_rvkTrlOQTKlc1TY5b1HPqbO4Jg,5185
31
- shepherd_core/data_models/experiment/target_config.py,sha256=ZODI4_MwBLxKTQ1DwFrLZAuKprxDiLyg5-JYOQxXp_w,3808
32
- shepherd_core/data_models/task/__init__.py,sha256=3wTIJ1L5wcRDLR6JRaT0bNh7vABckSoOd5erPwhhCGI,3331
33
- shepherd_core/data_models/task/emulation.py,sha256=PfSWci9V47EWpJRTKyHBxQZodHs3yQXMce0Mslm_-Ns,6665
29
+ shepherd_core/data_models/experiment/experiment.py,sha256=oFOpyxHmwCZmbJAejyoqAAjarA2ZgnNYEx6Qf7TPjqU,4178
30
+ shepherd_core/data_models/experiment/observer_features.py,sha256=xCaN2wymG-ygEy19jTIvY9YyitdjPWepDH5UjXdq8Y8,8315
31
+ shepherd_core/data_models/experiment/target_config.py,sha256=nlOZvLC5C6Pjn-ZEXLMr5WZlC1edixL_l3rWi93UPZE,4082
32
+ shepherd_core/data_models/task/__init__.py,sha256=m7_OK05djkoDt-XwKQ6gPT7qIcCUoyBxZgSXvD3HmzQ,3386
33
+ shepherd_core/data_models/task/emulation.py,sha256=-JzRm-xRp421Xl4lBkT52AEhTkb8ZqH9C1Z8CeHPHF8,7380
34
34
  shepherd_core/data_models/task/firmware_mod.py,sha256=WLmevU9-Q5QnyANbkrSCdFbnVuSi8N8fXdQ2l_Um9e8,3034
35
35
  shepherd_core/data_models/task/harvest.py,sha256=rQ5Aq40m4WHpEFfiwn309GjLIPu4aKzF4Fvbo4j7PLs,3470
36
36
  shepherd_core/data_models/task/observer_tasks.py,sha256=icUPtoIqic8IpE0U9Fc7btMj4CpYx1PoAu_wAqqZkU8,3608
37
- shepherd_core/data_models/task/programming.py,sha256=6UHb8rT1M3bglt_3fHql6MsHE8sTvi9ii7Uhug5O4PM,2552
37
+ shepherd_core/data_models/task/programming.py,sha256=jX_5biD6BGkBdIS652CajXWm0KOgY3w1Z3AOJIFgTws,2573
38
38
  shepherd_core/data_models/task/testbed_tasks.py,sha256=J3aOSVVG1KvEI_83j0TK0gc5rN_v_T-URFRhrEs8czY,2176
39
39
  shepherd_core/data_models/testbed/__init__.py,sha256=t9nwml5pbu7ZWghimOyZ8ujMIgnRgFkl23pNb5d_KdU,581
40
40
  shepherd_core/data_models/testbed/cape.py,sha256=vfS05D0rC1-_wMiHeLw69VE9PxXC6PHl9ndtrv219_k,1396
@@ -42,7 +42,7 @@ shepherd_core/data_models/testbed/cape_fixture.yaml,sha256=uwZxe6hsqvofn5tzg4sff
42
42
  shepherd_core/data_models/testbed/gpio.py,sha256=pOY_7Zs32sRGuqJZrZDrjvr69QkFXC8iXgav7kk2heY,2334
43
43
  shepherd_core/data_models/testbed/gpio_fixture.yaml,sha256=yXvoXAau2hancKi2yg1xIkErPWQa6gIxNUG3y8JuF9Y,3076
44
44
  shepherd_core/data_models/testbed/mcu.py,sha256=fuq2AWbVFbbzPRPCgIeMNFhJhVNCIsmpjFagnOXkjbY,1514
45
- shepherd_core/data_models/testbed/mcu_fixture.yaml,sha256=lRZMLs27cTeERSFGkbMt5xgxbn11Gh9G1mQqOZK136I,522
45
+ shepherd_core/data_models/testbed/mcu_fixture.yaml,sha256=bOYXdQY-6JYesxOkZAT8WvuGsdUc_MW4dkAmopLL8RM,507
46
46
  shepherd_core/data_models/testbed/observer.py,sha256=RO7i9TmHcxx69P-EHN59M9MAJetoHZg5qJUD5SEtrcg,3386
47
47
  shepherd_core/data_models/testbed/observer_fixture.yaml,sha256=jqAatTebWrShXBlhqkCUQIrtVqEjl7RVDR9mosS2LJQ,4807
48
48
  shepherd_core/data_models/testbed/target.py,sha256=W7U9nCz_xWMRCCgaGuqOV4dGi4ZeQ1SAp7W2FqCkanU,1978
@@ -53,29 +53,29 @@ shepherd_core/data_models/testbed/testbed_fixture.yaml,sha256=LaaU8mXLQboYWYNPpA
53
53
  shepherd_core/decoder_waveform/__init__.py,sha256=-ohGz0fA2tKxUJk4FAQXKtI93d6YGdy0CrkdhOod1QU,120
54
54
  shepherd_core/decoder_waveform/uart.py,sha256=vuw9fKwZb_1mMtQ5fdiZN8Cr1YWSgYKar9FIMK8Bogo,11084
55
55
  shepherd_core/fw_tools/__init__.py,sha256=D9GGj9TzLWZfPjG_iV2BsF-Q1TGTYTgEzWTUI5ReVAA,2090
56
- shepherd_core/fw_tools/converter.py,sha256=F06iJlYYG2w-OCKayrgeO870lOX87xWVTYTAi1X3UPU,3604
56
+ shepherd_core/fw_tools/converter.py,sha256=V74V-82VVkvkZ2i4HI7K_291_FGZNjxVdX6eO7Y3Zks,3657
57
57
  shepherd_core/fw_tools/converter_elf.py,sha256=GQDVqIqMW4twNMvZIV3sowFMezhs2TN-IYREjRP7Xt4,1089
58
58
  shepherd_core/fw_tools/patcher.py,sha256=ISbnA2ZLTIV2JZh_b3GorNDGNrLaBN0fFnFnyh-smNU,3946
59
- shepherd_core/fw_tools/validation.py,sha256=GWm_T3xmRde-jQ8cf27kPosBTFTK3E2M2LSb24pT2g8,4732
59
+ shepherd_core/fw_tools/validation.py,sha256=KxOBtAgqUemZC_Lj1_xtV6AMpGLU04_epxihMF3RX7U,4792
60
60
  shepherd_core/inventory/__init__.py,sha256=yQxP55yV61xXWfZSSzekQQYopPZCspFpHSyG7VTqtpg,3819
61
61
  shepherd_core/inventory/python.py,sha256=pvugNgLZaDllIXX_KiuvpcWUWlJtD2IUKYDRjcTGQss,1262
62
62
  shepherd_core/inventory/system.py,sha256=qFTRVXClQZoCeV2KM8RAbst8XL7IaJwwV0pGHPZSJs4,3325
63
63
  shepherd_core/inventory/target.py,sha256=zLUNQs2FE7jMDsiRtbeAwqRVcit4e2F1UUOF04xw-XY,520
64
64
  shepherd_core/testbed_client/__init__.py,sha256=QtbsBUzHwOoM6rk0qa21ywuz63YV7af1fwUtWW8Vg_4,234
65
65
  shepherd_core/testbed_client/cache_path.py,sha256=tS0er9on5fw8wddMCt1jkc2uyYOdSTvX_UmfmYJf6tY,445
66
- shepherd_core/testbed_client/client_abc_fix.py,sha256=3oDg2RSEP88mygtX820Y-685kYKvqQK_hwp2iFylLUU,4265
66
+ shepherd_core/testbed_client/client_abc_fix.py,sha256=xMch4y6vMMLRiqFEry2zSnYuLM6Ay-fY0dunXMz9Hws,4250
67
67
  shepherd_core/testbed_client/client_web.py,sha256=zqY4MGMWfTl2_0T1qrQl5Vz9SPjl-wMj8O5yMMQyo9I,6044
68
- shepherd_core/testbed_client/fixtures.py,sha256=OyL50wEuJ8336RPtinxOFbUsIVtLrgyO2_ROOyiBTEE,9317
68
+ shepherd_core/testbed_client/fixtures.py,sha256=ftPJwiOn-USroIee2Al3evR3WxsA1SxERusoZ-jbkVk,9223
69
69
  shepherd_core/testbed_client/user_model.py,sha256=f4WZ8IvSCt3s1RG_Bhi43ojiJQsZYyolJ3Ft8HNRYas,2175
70
70
  shepherd_core/vsource/__init__.py,sha256=vTvFWuJn4eurPNzEiMd15c1Rd6o3DTWzCfbhOomflZU,771
71
71
  shepherd_core/vsource/target_model.py,sha256=BjOlwX_gIOJ91e4OOLB4_OsCpuhq9vm57ERjM-iBhAM,5129
72
72
  shepherd_core/vsource/virtual_converter_model.py,sha256=jUnJwP-FFDMtXm1NCLUJfZTvImYH4_A9rc_lXVAZ33I,11628
73
73
  shepherd_core/vsource/virtual_harvester_model.py,sha256=8uPTMDth4KI5XarkX9EJtMRZ_8eWUeWCZxgmx4be3Fs,9763
74
- shepherd_core/vsource/virtual_harvester_simulation.py,sha256=H4m9lB4Hk_sskGi3LoFYARBpq1uY3WsJj-ktF3nk2Ck,2558
74
+ shepherd_core/vsource/virtual_harvester_simulation.py,sha256=eK9uCn-j_8xSTa7BQhG879ep0oZDeatlnF31LeWEDiM,2547
75
75
  shepherd_core/vsource/virtual_source_model.py,sha256=-8RwBrkIdO0g4zpo7XHnqv8F_qNh_qf5hxEUJoIuAmg,3164
76
- shepherd_core/vsource/virtual_source_simulation.py,sha256=6FKbaO-zaODp0nf-ksnl-nRAVpFKGQrpxpIBrGIhD0I,5284
77
- shepherd_core-2025.4.2.dist-info/METADATA,sha256=SD_1RcpaF_Pb-IbG2uAh7xkWHJv8TOaqy8c0zQI7Q1o,7756
78
- shepherd_core-2025.4.2.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
79
- shepherd_core-2025.4.2.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
- shepherd_core-2025.4.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
- shepherd_core-2025.4.2.dist-info/RECORD,,
76
+ shepherd_core/vsource/virtual_source_simulation.py,sha256=6voU1TAB2P0xuVu1bnyKa-xW9rwYXUC6f06gYtUDHTs,5273
77
+ shepherd_core-2025.5.2.dist-info/METADATA,sha256=nACqRWB12RTUhFeUPiKcnw7UvP2YEV4ZNg-kKRGkWSw,7756
78
+ shepherd_core-2025.5.2.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
79
+ shepherd_core-2025.5.2.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
+ shepherd_core-2025.5.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
+ shepherd_core-2025.5.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5