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.
- shepherd_core/data_models/__init__.py +2 -0
- shepherd_core/data_models/content/_external_fixtures.yaml +32 -32
- shepherd_core/data_models/content/energy_environment.py +2 -2
- shepherd_core/data_models/content/virtual_harvester.py +4 -4
- shepherd_core/data_models/content/virtual_harvester_fixture.yaml +2 -2
- shepherd_core/data_models/content/virtual_source.py +4 -2
- shepherd_core/data_models/content/virtual_source_fixture.yaml +3 -3
- shepherd_core/data_models/experiment/experiment.py +2 -2
- shepherd_core/data_models/experiment/observer_features.py +102 -8
- shepherd_core/data_models/experiment/target_config.py +5 -0
- shepherd_core/data_models/task/__init__.py +6 -3
- shepherd_core/data_models/task/emulation.py +19 -3
- shepherd_core/data_models/task/programming.py +2 -1
- shepherd_core/data_models/testbed/mcu_fixture.yaml +4 -4
- shepherd_core/data_models/virtual_source_doc.txt +3 -3
- shepherd_core/fw_tools/converter.py +6 -3
- shepherd_core/fw_tools/validation.py +8 -4
- shepherd_core/reader.py +75 -46
- shepherd_core/testbed_client/client_abc_fix.py +2 -3
- shepherd_core/testbed_client/fixtures.py +2 -6
- shepherd_core/version.py +1 -1
- shepherd_core/vsource/virtual_harvester_simulation.py +1 -1
- shepherd_core/vsource/virtual_source_simulation.py +1 -1
- shepherd_core/writer.py +8 -8
- {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/METADATA +1 -1
- {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/RECORD +29 -29
- {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/WHEEL +1 -1
- {shepherd_core-2025.4.2.dist-info → shepherd_core-2025.5.2.dist-info}/top_level.txt +0 -0
- {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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
15
|
+
description: 16MHz Ultra-Low-Pwr MCU with 256 KB FRAM
|
|
16
16
|
platform: MSP430
|
|
17
|
-
core:
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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[
|
|
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 (
|
|
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 /
|
|
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((
|
|
183
|
-
self.runtime_s = round(self.
|
|
184
|
-
self.buffers_n = int(self.
|
|
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
|
|
201
|
+
def read(
|
|
192
202
|
self,
|
|
193
203
|
start_n: int = 0,
|
|
194
204
|
end_n: Optional[int] = None,
|
|
195
|
-
|
|
205
|
+
n_samples_per_chunk: Optional[int] = None,
|
|
196
206
|
*,
|
|
197
207
|
is_raw: bool = False,
|
|
198
|
-
|
|
208
|
+
omit_timestamps: bool = False,
|
|
199
209
|
) -> Generator[tuple, None, None]:
|
|
200
|
-
"""Read the specified range of
|
|
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
|
|
207
|
-
:param end_n: (int) Index of last
|
|
208
|
-
:param
|
|
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
|
|
211
|
-
Yields:
|
|
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
|
|
215
|
-
|
|
216
|
-
end_max = int(self.
|
|
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
|
|
228
|
+
self._logger.debug("Reading chunk %d to %d from source-file", start_n, end_n)
|
|
219
229
|
_raw = is_raw
|
|
220
|
-
_wts = not
|
|
230
|
+
_wts = not omit_timestamps
|
|
221
231
|
|
|
222
232
|
for i in range(start_n, end_n):
|
|
223
|
-
idx_start = i *
|
|
224
|
-
idx_end = idx_start +
|
|
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
|
|
417
|
+
if ds_size != self.samples_n:
|
|
391
418
|
self._logger.warning(
|
|
392
419
|
"[FileValidation] dataset '%s' has different size (=%d), "
|
|
393
|
-
"compared to
|
|
420
|
+
"compared to smallest set (=%d), in '%s'",
|
|
394
421
|
dset,
|
|
395
422
|
ds_size,
|
|
396
|
-
|
|
423
|
+
self.samples_n,
|
|
397
424
|
self.file_path.name,
|
|
398
425
|
)
|
|
399
|
-
# dataset-length should be multiple of
|
|
400
|
-
remaining_size =
|
|
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
|
|
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.
|
|
485
|
+
iterations = math.ceil(self.samples_n / self.max_elements)
|
|
459
486
|
job_iter = trange(
|
|
460
487
|
0,
|
|
461
|
-
self.
|
|
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.
|
|
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
|
|
550
|
+
"""Calculate list of unique time-deltas [s] between chunks.
|
|
524
551
|
|
|
525
|
-
Optimized version that only looks at the start of each
|
|
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
|
|
556
|
+
:return: list of (unique) time-deltas between chunks [s]
|
|
528
557
|
"""
|
|
529
|
-
iterations = math.ceil(self.
|
|
558
|
+
iterations = math.ceil(self.samples_n / self.max_elements)
|
|
530
559
|
job_iter = trange(
|
|
531
560
|
0,
|
|
532
|
-
self.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
76
|
-
|
|
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
|
@@ -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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
346
|
+
"""Align datasets with chunk-size of shepherd."""
|
|
347
347
|
self._refresh_file_stats()
|
|
348
|
-
|
|
349
|
-
size_new = int(math.floor(
|
|
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
|
|
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.
|
|
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=
|
|
6
|
-
shepherd_core/version.py,sha256=
|
|
7
|
-
shepherd_core/writer.py,sha256=
|
|
8
|
-
shepherd_core/data_models/__init__.py,sha256=
|
|
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=
|
|
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=
|
|
20
|
-
shepherd_core/data_models/content/energy_environment.py,sha256=
|
|
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=
|
|
25
|
-
shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=
|
|
26
|
-
shepherd_core/data_models/content/virtual_source.py,sha256=
|
|
27
|
-
shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=
|
|
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=
|
|
30
|
-
shepherd_core/data_models/experiment/observer_features.py,sha256=
|
|
31
|
-
shepherd_core/data_models/experiment/target_config.py,sha256=
|
|
32
|
-
shepherd_core/data_models/task/__init__.py,sha256=
|
|
33
|
-
shepherd_core/data_models/task/emulation.py,sha256
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
77
|
-
shepherd_core-2025.
|
|
78
|
-
shepherd_core-2025.
|
|
79
|
-
shepherd_core-2025.
|
|
80
|
-
shepherd_core-2025.
|
|
81
|
-
shepherd_core-2025.
|
|
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,,
|
|
File without changes
|
|
File without changes
|