shepherd-core 2024.7.4__py3-none-any.whl → 2024.8.1__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.
@@ -51,7 +51,7 @@ class CalibrationPair(ShpModel):
51
51
 
52
52
  gain: PositiveFloat
53
53
  offset: float = 0
54
- # TODO: add unit
54
+ unit: Optional[str] = None # TODO: add units when used
55
55
 
56
56
  def raw_to_si(self, values_raw: Calc_t, *, allow_negative: bool = True) -> Calc_t:
57
57
  """Convert between physical units and raw unsigned integers."""
@@ -78,14 +78,11 @@ class CalibrationPair(ShpModel):
78
78
  return values_raw
79
79
 
80
80
  @classmethod
81
- def from_fn(cls, fn: Callable) -> Self:
81
+ def from_fn(cls, fn: Callable, unit: Optional[str] = None) -> Self:
82
82
  """Probe linear function to determine scaling values."""
83
83
  offset = fn(0, limited=False)
84
84
  gain_inv = fn(1.0, limited=False) - offset
85
- return cls(
86
- gain=1.0 / float(gain_inv),
87
- offset=-float(offset) / gain_inv,
88
- )
85
+ return cls(gain=1.0 / float(gain_inv), offset=-float(offset) / gain_inv, unit=unit)
89
86
 
90
87
 
91
88
  cal_hrv_legacy = { # legacy translator
@@ -99,10 +96,10 @@ cal_hrv_legacy = { # legacy translator
99
96
  class CalibrationHarvester(ShpModel):
100
97
  """Container for all calibration-pairs for that device."""
101
98
 
102
- dac_V_Hrv: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
103
- dac_V_Sim: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
104
- adc_V_Sense: CalibrationPair = CalibrationPair.from_fn(adc_voltage_to_raw)
105
- adc_C_Hrv: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw)
99
+ dac_V_Hrv: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
100
+ dac_V_Sim: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
101
+ adc_V_Sense: CalibrationPair = CalibrationPair.from_fn(adc_voltage_to_raw, unit="V")
102
+ adc_C_Hrv: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw, unit="A")
106
103
 
107
104
  def export_for_sysfs(self) -> dict:
108
105
  """Convert and write the essential data.
@@ -143,10 +140,10 @@ class CalibrationEmulator(ShpModel):
143
140
  Differentiates between both target-ports A/B.
144
141
  """
145
142
 
146
- dac_V_A: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
147
- dac_V_B: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
148
- adc_C_A: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw)
149
- adc_C_B: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw)
143
+ dac_V_A: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
144
+ dac_V_B: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
145
+ adc_C_A: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw, unit="A")
146
+ adc_C_B: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw, unit="A")
150
147
 
151
148
  def export_for_sysfs(self) -> dict:
152
149
  """Convert and write the essential data.
@@ -233,10 +230,11 @@ class CalibrationCape(ShpModel):
233
230
 
234
231
  """
235
232
  dv = cls().model_dump(include={"harvester", "emulator"})
236
- lw = list(dict_generator(dv))
237
- values = struct.unpack(">" + len(lw) * "d", data)
233
+ lw1 = list(dict_generator(dv))
234
+ lw2 = [elem for elem in lw1 if isinstance(elem[-1], float)]
235
+ values = struct.unpack(">" + len(lw2) * "d", data)
238
236
  # ⤷ X => double float, big endian
239
- for _i, walk in enumerate(lw):
237
+ for _i, walk in enumerate(lw2):
240
238
  # hardcoded fixed depth ... bad but easy
241
239
  dv[walk[0]][walk[1]][walk[2]] = float(values[_i])
242
240
  dv["cape"] = cape
@@ -252,18 +250,18 @@ class CalibrationCape(ShpModel):
252
250
 
253
251
  """
254
252
  lw = list(dict_generator(self.model_dump(include={"harvester", "emulator"})))
255
- values = [walk[-1] for walk in lw]
256
- return struct.pack(">" + len(lw) * "d", *values)
253
+ values = [walk[-1] for walk in lw if isinstance(walk[-1], float)]
254
+ return struct.pack(">" + len(values) * "d", *values)
257
255
 
258
256
 
259
257
  class CalibrationSeries(ShpModel):
260
258
  """Cal-Data for a typical recording of a testbed experiment."""
261
259
 
262
- voltage: CalibrationPair = CalibrationPair(gain=3 * 1e-9)
260
+ voltage: CalibrationPair = CalibrationPair(gain=3 * 1e-9, unit="V")
263
261
  # ⤷ default allows 0 - 12 V in 3 nV-Steps
264
- current: CalibrationPair = CalibrationPair(gain=250 * 1e-12)
262
+ current: CalibrationPair = CalibrationPair(gain=250 * 1e-12, unit="A")
265
263
  # ⤷ default allows 0 - 1 A in 250 pA - Steps
266
- time: CalibrationPair = CalibrationPair(gain=1e-9)
264
+ time: CalibrationPair = CalibrationPair(gain=1e-9, unit="s")
267
265
  # ⤷ default = nanoseconds
268
266
 
269
267
  @classmethod
@@ -149,20 +149,27 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
149
149
 
150
150
  def calc_window_size(
151
151
  self,
152
- dtype_in: Optional[EnergyDType] = EnergyDType.ivsample,
152
+ dtype_in: Optional[EnergyDType] = None,
153
153
  *,
154
154
  for_emu: bool,
155
155
  ) -> int:
156
- if for_emu:
157
- if dtype_in == EnergyDType.ivcurve:
158
- return self.samples_n * (1 + self.wait_cycles)
159
- if dtype_in == EnergyDType.ivsample:
160
- return 0
161
- # isc_voc: 2 * (1 + wait_cycles), noqa
162
- raise NotImplementedError
156
+ if not for_emu:
157
+ # TODO: should be named 'for_ivcurve_recording'
158
+ # TODO: add extra variable to distinguish step_count
159
+ # and window_size (currently mixed together)
160
+ # only used by ivcurve algo (in ADC-Mode)
161
+ return self.samples_n
163
162
 
164
- # only used by ivcurve algo (in ADC-Mode)
165
- return self.samples_n
163
+ if dtype_in is None:
164
+ dtype_in = self.get_datatype()
165
+
166
+ if dtype_in == EnergyDType.ivcurve:
167
+ return self.samples_n * (1 + self.wait_cycles)
168
+ if dtype_in == EnergyDType.ivsample:
169
+ return 0
170
+ if dtype_in == EnergyDType.isc_voc:
171
+ return 2 * (1 + self.wait_cycles)
172
+ raise NotImplementedError
166
173
 
167
174
 
168
175
  u32 = Annotated[int, Field(ge=0, lt=2**32)]
@@ -234,7 +241,7 @@ class HarvesterPRUConfig(ShpModel):
234
241
  dtype_in = EnergyDType[dtype_in]
235
242
  if for_emu and dtype_in not in {EnergyDType.ivsample, EnergyDType.ivcurve}:
236
243
  raise NotImplementedError
237
- # TODO: use dtype properly in shepherd
244
+
238
245
  interval_ms, duration_ms = data.calc_timings_ms(for_emu=for_emu)
239
246
  return cls(
240
247
  algorithm=data.calc_algorithm_num(for_emu=for_emu),
@@ -19,13 +19,13 @@
19
19
  id: 1010
20
20
  name: ivcurve
21
21
  description: Postpone harvesting by sampling ivcurves (voltage stepped as sawtooth-wave)
22
- comment: ~200 Hz
22
+ comment: ~110 Hz, Between 50 & 60 Hz line-frequency to avoid standing waves
23
23
  inherit_from: neutral
24
24
  algorithm: ivcurve
25
- samples_n: 250
25
+ samples_n: 909
26
26
  voltage_min_mV: 0
27
27
  voltage_max_mV: 5000
28
- wait_cycles: 1 # results in 200 Hz (= 100kHz /(2*250))
28
+ wait_cycles: 0
29
29
  rising: false # downward sawtooth seems to have advantages for solar cells
30
30
  # todo: also add switch for sawtooth- vs triangle-wave?
31
31
  # todo: could also include a version with dynamic upper-boundary, varied if voc is reached very early
@@ -38,20 +38,17 @@
38
38
 
39
39
  - datatype: VirtualHarvesterConfig
40
40
  parameters:
41
- id: 1012
42
- name: iv1000
43
- comment: Name relates to curves per second
41
+ id: 1013
42
+ name: iv110 # synonym
44
43
  inherit_from: ivcurve
45
- samples_n: 100
46
- wait_cycles: 0
47
44
 
48
45
  - datatype: VirtualHarvesterConfig
49
46
  parameters:
50
- id: 1013
51
- name: iv110
52
- comment: Between 50 & 60 Hz line-frequency to avoid standing waves
47
+ id: 1012
48
+ name: iv1000
49
+ comment: Name relates to curves per second
53
50
  inherit_from: ivcurve
54
- samples_n: 909
51
+ samples_n: 100
55
52
  wait_cycles: 0
56
53
 
57
54
  - datatype: VirtualHarvesterConfig
@@ -61,7 +58,7 @@
61
58
  description: Postpone harvesting by sampling short circuit current & open circuit voltage
62
59
  inherit_from: neutral
63
60
  algorithm: isc_voc
64
- wait_cycles: 1 # results in 25 kHz (isc, wait, voc, wait)
61
+ wait_cycles: 4 # results in 10 kHz (isc, wait, voc, wait)
65
62
 
66
63
  - datatype: VirtualHarvesterConfig
67
64
  parameters:
@@ -76,6 +76,8 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
76
76
  # final (always last) stage to compensate undetectable current spikes
77
77
  # when enabling power for target
78
78
  C_output_uF: Annotated[float, Field(ge=0, le=4.29e6)] = 1.0
79
+ # TODO: C_output is handled internally as delta-V, but should be a I_transient
80
+ # that makes it visible in simulation as additional i_out_drain
79
81
 
80
82
  # Extra
81
83
  V_output_log_gpio_threshold_mV: Annotated[float, Field(ge=0, le=4.29e6)] = 1_400
@@ -93,7 +95,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
93
95
  # influence of cap-voltage is not implemented
94
96
  LUT_input_V_min_log2_uV: Annotated[int, Field(ge=0, le=20)] = 0
95
97
  # ⤷ 2^7 = 128 uV -> LUT[0][:] is for inputs < 128 uV
96
- LUT_input_I_min_log2_nA: Annotated[int, Field(ge=0, le=20)] = 0
98
+ LUT_input_I_min_log2_nA: Annotated[int, Field(ge=1, le=20)] = 1
97
99
  # ⤷ 2^8 = 256 nA -> LUT[:][0] is for inputs < 256 nA
98
100
 
99
101
  # Buck Converter
@@ -103,7 +105,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
103
105
 
104
106
  LUT_output_efficiency: LUT1D = 12 * [1.00]
105
107
  # ⤷ array[12] depending on output_current
106
- LUT_output_I_min_log2_nA: Annotated[int, Field(ge=0, le=20)] = 0
108
+ LUT_output_I_min_log2_nA: Annotated[int, Field(ge=1, le=20)] = 1
107
109
  # ⤷ 2^8 = 256 nA -> LUT[0] is for inputs < 256 nA, see notes on LUT_input for explanation
108
110
 
109
111
  @model_validator(mode="before")
@@ -318,8 +320,8 @@ class ConverterPRUConfig(ShpModel):
318
320
  V_buck_drop_uV=round(data.V_buck_drop_mV * 1e3),
319
321
  # LUTs
320
322
  LUT_input_V_min_log2_uV=data.LUT_input_V_min_log2_uV,
321
- LUT_input_I_min_log2_nA=data.LUT_input_I_min_log2_nA,
322
- LUT_output_I_min_log2_nA=data.LUT_output_I_min_log2_nA,
323
+ LUT_input_I_min_log2_nA=data.LUT_input_I_min_log2_nA - 1, # sub-1 due to later log2-op
324
+ LUT_output_I_min_log2_nA=data.LUT_output_I_min_log2_nA - 1, # sub-1 due to later log2
323
325
  LUT_inp_efficiency_n8=[
324
326
  [min(255, round(256 * ival)) for ival in il] for il in data.LUT_input_efficiency
325
327
  ],
@@ -56,16 +56,18 @@
56
56
  [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ],
57
57
  [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ],
58
58
  [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ],
59
- ] # input-array[12][12] depending on array[inp_voltage][log(inp_current)], influence of cap-voltage is not implemented
59
+ ]
60
+ # input-array[12][12] depending on array[inp_voltage][log(inp_current)],
61
+ # influence of cap-voltage is not implemented
60
62
  LUT_input_V_min_log2_uV: 0 # 2^7 = 128 uV -> array[0] is for inputs < 128 uV
61
- LUT_input_I_min_log2_nA: 0 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA
63
+ LUT_input_I_min_log2_nA: 1 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA
62
64
 
63
65
  # Buck-converter
64
66
  V_output_mV: 2400
65
67
  V_buck_drop_mV: 0.0 # simulate LDO min voltage differential or output-diode
66
68
 
67
69
  LUT_output_efficiency: [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ] # array[12] depending on output_current
68
- LUT_output_I_min_log2_nA: 0 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA, see notes on LUT_input for explanation
70
+ LUT_output_I_min_log2_nA: 1 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA, see notes on LUT_input for explanation
69
71
 
70
72
  owner: Ingmar
71
73
  group: NES Lab
@@ -114,7 +116,7 @@
114
116
  parameters:
115
117
  id: 1020
116
118
  name: BQ25504
117
- description: TI BQ25504 with integrated boost-converter
119
+ description: TI BQ25504 with integrated boost-converter. Values are taken from the DK-Board.
118
120
  inherit_from: neutral # to complete undefined vars
119
121
  enable_boost: true # if false -> v_intermediate = v_input, output-switch-hysteresis is still usable
120
122
 
@@ -124,16 +126,16 @@
124
126
  V_input_max_mV: 3000
125
127
  I_input_max_mA: 100
126
128
 
127
- C_intermediate_uF: 22.0 # primary storage-Cap
129
+ C_intermediate_uF: 100.0 # primary storage-Cap
128
130
  V_intermediate_init_mV: 3000 # allow a proper / fast startup
129
131
  I_intermediate_leak_nA: 330
130
132
 
131
- V_intermediate_enable_threshold_mV: 2600 # -> target gets connected (hysteresis-combo with next value)
132
- V_intermediate_disable_threshold_mV: 2300 # -> target gets disconnected
133
+ V_intermediate_enable_threshold_mV: 1000 # -> target gets connected (hysteresis-combo with next value)
134
+ V_intermediate_disable_threshold_mV: 0 # -> target gets disconnected
133
135
  interval_check_thresholds_ms: 64.0 # some BQs check every 64 ms if output should be disconnected
134
136
 
135
137
  V_pwr_good_enable_threshold_mV: 2800 # target is informed by pwr-good on output-pin (hysteresis) -> for intermediate voltage
136
- V_pwr_good_disable_threshold_mV: 2400
138
+ V_pwr_good_disable_threshold_mV: 2340
137
139
  immediate_pwr_good_signal: false # 1: activate instant schmitt-trigger, 0: stay in interval for checking thresholds
138
140
 
139
141
  # Boost Converter
shepherd_core/reader.py CHANGED
@@ -84,6 +84,7 @@ class Reader:
84
84
 
85
85
  # init stats
86
86
  self.runtime_s: float = 0
87
+ self.buffers_n: int = 0
87
88
  self.file_size: int = 0
88
89
  self.data_rate: float = 0
89
90
 
@@ -170,12 +171,16 @@ class Reader:
170
171
  def _refresh_file_stats(self) -> None:
171
172
  """Update internal states, helpful after resampling or other changes in data-group."""
172
173
  self.h5file.flush()
173
- if (self.ds_time.shape[0] > 1) and (self.ds_time[1] != self.ds_time[0]):
174
- # this assumes isochronal sampling
175
- self.sample_interval_s = self._cal.time.raw_to_si(self.ds_time[1] - self.ds_time[0])
174
+ sample_count = self.ds_time.shape[0]
175
+ duration_raw = self.ds_time[sample_count - 1] - self.ds_time[0] if sample_count > 0 else 0
176
+ if (sample_count > 0) and (duration_raw > 0):
177
+ # this assumes iso-chronous sampling
178
+ duration_s = self._cal.time.raw_to_si(duration_raw)
179
+ self.sample_interval_s = duration_s / sample_count
176
180
  self.sample_interval_ns = int(10**9 * self.sample_interval_s)
177
- self.samplerate_sps = max(int(10**9 // self.sample_interval_ns), 1)
181
+ self.samplerate_sps = max(int((sample_count - 1) / duration_s), 1)
178
182
  self.runtime_s = round(self.ds_voltage.shape[0] / self.samplerate_sps, 1)
183
+ self.buffers_n = int(self.ds_voltage.shape[0] // self.samples_per_buffer)
179
184
  if isinstance(self.file_path, Path):
180
185
  self.file_size = self.file_path.stat().st_size
181
186
  else:
shepherd_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2024.7.4"
3
+ version: str = "2024.8.1"
@@ -25,12 +25,28 @@ from ..data_models.content.virtual_source import ConverterPRUConfig
25
25
  class PruCalibration:
26
26
  """part of calibration.h."""
27
27
 
28
+ # negative residue compensation - compensate for noise around 0
29
+ # -> current uint-design cuts away negative part and leads to biased mean()
30
+ NOISE_ESTIMATE_nA: int = 2000
31
+ RESIDUE_SIZE_FACTOR: int = 30
32
+ RESIDUE_MAX_nA: int = NOISE_ESTIMATE_nA * RESIDUE_SIZE_FACTOR
33
+ negative_residue_nA = 0
34
+
28
35
  def __init__(self, cal_emu: Optional[CalibrationEmulator] = None) -> None:
29
36
  self.cal = cal_emu if cal_emu else CalibrationEmulator()
30
37
 
31
38
  def conv_adc_raw_to_nA(self, current_raw: int) -> float:
32
- return self.cal.adc_C_A.raw_to_si(current_raw) * (10**9)
33
- # TODO: add feature "negative residue compensation" to here
39
+ I_nA = self.cal.adc_C_A.raw_to_si(current_raw) * (10**9)
40
+ if self.cal.adc_C_A.offset < 0:
41
+ if I_nA > self.negative_residue_nA:
42
+ I_nA -= self.negative_residue_nA
43
+ self.negative_residue_nA = 0
44
+ else:
45
+ self.negative_residue_nA = self.negative_residue_nA - I_nA
46
+ if self.negative_residue_nA > self.RESIDUE_MAX_nA:
47
+ self.negative_residue_nA = self.RESIDUE_MAX_nA
48
+ I_nA = 0
49
+ return I_nA
34
50
 
35
51
  @staticmethod
36
52
  def conv_adc_raw_to_uV(voltage_raw: int) -> float:
@@ -88,10 +104,11 @@ class VirtualConverterModel:
88
104
  self.vsource_skip_gpio_logging: bool = False
89
105
 
90
106
  def calc_inp_power(self, input_voltage_uV: float, input_current_nA: float) -> int:
91
- # Next 2 lines are Python-specific
107
+ # Next 2 lines are Python-specific (model unsigned int)
92
108
  input_voltage_uV = max(0.0, input_voltage_uV)
93
109
  input_current_nA = max(0.0, input_current_nA)
94
110
 
111
+ # Input diode
95
112
  if input_voltage_uV > self._cfg.V_input_drop_uV:
96
113
  input_voltage_uV -= self._cfg.V_input_drop_uV
97
114
  else:
@@ -108,8 +125,6 @@ class VirtualConverterModel:
108
125
  if self.enable_boost:
109
126
  if input_voltage_uV < self._cfg.V_input_boost_threshold_uV:
110
127
  input_voltage_uV = 0.0
111
- if input_voltage_uV > self.V_mid_uV:
112
- input_voltage_uV = self.V_mid_uV
113
128
  elif not self.enable_storage:
114
129
  # direct connection
115
130
  self.V_mid_uV = input_voltage_uV
@@ -134,7 +149,7 @@ class VirtualConverterModel:
134
149
  return round(self.P_inp_fW) # Python-specific, added for easier testing
135
150
 
136
151
  def calc_out_power(self, current_adc_raw: int) -> int:
137
- # Next 2 lines are Python-specific
152
+ # Next 2 lines are Python-specific (model unsigned int)
138
153
  current_adc_raw = max(0, current_adc_raw)
139
154
  current_adc_raw = min((2**18) - 1, current_adc_raw)
140
155
 
@@ -35,7 +35,11 @@ class VirtualHarvesterModel:
35
35
 
36
36
  # INIT global vars: shared states
37
37
  self.voltage_set_uV: int = self._cfg.voltage_uV + 1
38
- self.interval_step: int = 2**30
38
+ if self._cfg.interval_n > 2 * self._cfg.window_size:
39
+ self.interval_step = self._cfg.interval_n - (2 * self._cfg.window_size)
40
+ else:
41
+ self.interval_step = 2**30
42
+ # ⤷ intake two ivcurves before overflow / reset
39
43
  self.is_rising: bool = (self._cfg.hrv_mode & (2**1)) != 0
40
44
 
41
45
  # PO-Relevant, iv & adc
@@ -51,6 +55,7 @@ class VirtualHarvesterModel:
51
55
  self.voltage_hold: int = 0
52
56
  self.current_hold: int = 0
53
57
  self.voltage_step_x4_uV: int = self._cfg.voltage_step_uV * 4
58
+ self.age_max: int = 2 * self._cfg.window_size
54
59
 
55
60
  # INIT static vars: CV
56
61
  self.voltage_last: int = 0
@@ -59,9 +64,10 @@ class VirtualHarvesterModel:
59
64
 
60
65
  # INIT static vars: VOC
61
66
  self.age_now: int = 0
62
- self.voc_now: int = 0
67
+ self.voc_now: int = self._cfg.voltage_max_uV
63
68
  self.age_nxt: int = 0
64
- self.voc_nxt: int = 0
69
+ self.voc_nxt: int = self._cfg.voltage_max_uV
70
+ self.voc_min: int = max(1000, self._cfg.voltage_min_uV)
65
71
 
66
72
  # INIT static vars: PO
67
73
  # already done: interval step
@@ -110,20 +116,22 @@ class VirtualHarvesterModel:
110
116
  return self.voltage_hold, self.current_hold
111
117
 
112
118
  def ivcurve_2_mppt_voc(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
113
- self.interval_step = (self.interval_step + 1) % self._cfg.interval_n
119
+ self.interval_step = self.interval_step + 1
120
+ if self.interval_step >= self._cfg.interval_n:
121
+ self.interval_step = 0
114
122
  self.age_nxt += 1
115
123
  self.age_now += 1
116
124
 
117
125
  if (
118
126
  (_current_nA < self._cfg.current_limit_nA)
119
127
  and (_voltage_uV < self.voc_nxt)
120
- and (_voltage_uV >= self._cfg.voltage_min_uV)
128
+ and (_voltage_uV >= self.voc_min)
121
129
  and (_voltage_uV <= self._cfg.voltage_max_uV)
122
130
  ):
123
131
  self.voc_nxt = _voltage_uV
124
132
  self.age_nxt = 0
125
133
 
126
- if (self.age_now > self._cfg.window_size) or (self.voc_nxt <= self.voc_now):
134
+ if (self.age_now > self.age_max) or (self.voc_nxt <= self.voc_now):
127
135
  self.age_now = self.age_nxt
128
136
  self.voc_now = self.voc_nxt
129
137
  self.age_nxt = 0
@@ -137,7 +145,9 @@ class VirtualHarvesterModel:
137
145
  return _voltage_uV, _current_nA
138
146
 
139
147
  def ivcurve_2_mppt_po(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
140
- self.interval_step = (self.interval_step + 1) % self._cfg.interval_n
148
+ self.interval_step = self.interval_step + 1
149
+ if self.interval_step >= self._cfg.interval_n:
150
+ self.interval_step = 0
141
151
 
142
152
  _voltage_uV, _current_nA = self.ivcurve_2_cv(_voltage_uV, _current_nA)
143
153
 
@@ -193,7 +203,7 @@ class VirtualHarvesterModel:
193
203
  self.voltage_nxt = _voltage_uV
194
204
  self.current_nxt = _current_nA
195
205
 
196
- if (self.age_now > self._cfg.window_size) or (self.power_nxt >= self.power_now):
206
+ if (self.age_now > self.age_max) or (self.power_nxt >= self.power_now):
197
207
  self.age_now = self.age_nxt
198
208
  self.power_now = self.power_nxt
199
209
  self.voltage_now = self.voltage_nxt
shepherd_core/writer.py CHANGED
@@ -149,9 +149,18 @@ class Writer(Reader):
149
149
  raise ValueError(msg)
150
150
 
151
151
  if self._modify:
152
- self._mode = mode
153
- self._datatype = datatype
154
- self._window_samples = window_samples
152
+ if mode:
153
+ self._mode = mode
154
+ if not hasattr(self, "_mode"):
155
+ self._mode = self.mode_default
156
+ if datatype:
157
+ self._datatype = datatype
158
+ if not hasattr(self, "_datatype"):
159
+ self._datatype = self.datatype_default
160
+ if window_samples:
161
+ self._window_samples = window_samples
162
+ if not hasattr(self, "_window_samples"):
163
+ self._window_samples = 0
155
164
  else:
156
165
  self._mode = mode if isinstance(mode, str) else self.mode_default
157
166
  self._datatype = (
@@ -335,6 +344,7 @@ class Writer(Reader):
335
344
  current: ndarray in physical-unit A
336
345
 
337
346
  """
347
+ # TODO: make timestamp optional to add it raw, OR unify append with granular raw-switch
338
348
  timestamp = self._cal.time.si_to_raw(timestamp)
339
349
  voltage = self._cal.voltage.si_to_raw(voltage)
340
350
  current = self._cal.current.si_to_raw(current)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shepherd_core
3
- Version: 2024.7.4
3
+ Version: 2024.8.1
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,15 +2,15 @@ shepherd_core/__init__.py,sha256=QyqENyf508XfZQ4vDU5o6UL9rmIqkf8kzwgTF9XU1-Y,127
2
2
  shepherd_core/calibration_hw_def.py,sha256=_nMzgNzSnYyqcLnVCGd4tfA2e0avUXbccjmNpFhiDgo,2830
3
3
  shepherd_core/commons.py,sha256=vymKXWcy_1bz7ChzzEATUkJ4p3czCzjIdsSehVjJOY8,218
4
4
  shepherd_core/logger.py,sha256=4Q4hTI-nccOZ1_A68fo4UEctfu3pJx3IeHfa9VuDDEo,1804
5
- shepherd_core/reader.py,sha256=9BuArqou5pmPKUrJH9oiPYlU1DkMxUScL4nftDJuFIs,26790
6
- shepherd_core/version.py,sha256=nuk8f6h1RBadV7-koFp4yLk1jExaUHnFqMTtxlKZyCo,75
7
- shepherd_core/writer.py,sha256=xcLCw-YokKaN8TrkwD0IjRmn8xZU0Q8wwWp_1K8JFVY,14475
5
+ shepherd_core/reader.py,sha256=xpzwt1JjubNat0PNwdzmBJltLPOmvCyQGwcC-bq83ZI,27052
6
+ shepherd_core/version.py,sha256=xmbickJLTzW9cd7jhYX7fdLN72fW-vKC8QWUEFq3lR4,75
7
+ shepherd_core/writer.py,sha256=t53fXkYUWHhYnOFnHCoMWwUADkgYydy9Dg66PiqPa68,14946
8
8
  shepherd_core/data_models/__init__.py,sha256=IVjKbT2Ilz5bev325EvAuuhd9LfQgQ1u7qKo6dhVA2k,1866
9
9
  shepherd_core/data_models/readme.md,sha256=1bdfEypY_0NMhXLxOPRnLAsFca0HuHdq7_01yEWxvUs,2470
10
10
  shepherd_core/data_models/virtual_source_doc.txt,sha256=KizMcfGKj7BnHIbaJHT7KeTF01SV__UXv01qV_DGHSs,6057
11
11
  shepherd_core/data_models/base/__init__.py,sha256=PSJ6acWViqBm0Eiom8DIgKfFVrp5lzYr8OsDvP79vwI,94
12
12
  shepherd_core/data_models/base/cal_measurement.py,sha256=YScPG7QLynbUHdjcznYqU8O5KRh0XiROGxGSk9BETMk,3357
13
- shepherd_core/data_models/base/calibration.py,sha256=iy04ajChReqrRNLoNDEH01CM-iCvb5XepBIlyFSWeII,10466
13
+ shepherd_core/data_models/base/calibration.py,sha256=k5VwAK_cwr0a6QY82Lw-9uHth5KUReFOakuOTzghoso,10725
14
14
  shepherd_core/data_models/base/content.py,sha256=13j7GSgT73xn27jgDP508thUEJR4U-nCb5n7CJ50c9Y,2463
15
15
  shepherd_core/data_models/base/shepherd.py,sha256=DNrx59o1VBuy_liJuUzZRzmTTYB73D_pUWiNyMQyjYY,6112
16
16
  shepherd_core/data_models/base/timezone.py,sha256=2T6E46hJ1DAvmqKfu6uIgCK3RSoAKjGXRyzYNaqKyjY,665
@@ -21,10 +21,10 @@ shepherd_core/data_models/content/energy_environment.py,sha256=bXInmHzlRjBAt7mit
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=MyEiaP6bkOm7i_oihDXTxHC7ajc5aqiIDLn7mhap6YY,5722
23
23
  shepherd_core/data_models/content/firmware_datatype.py,sha256=XPU9LOoT3h5qFOlE8WU0vAkw-vymNxzor9kVFyEqsWg,255
24
- shepherd_core/data_models/content/virtual_harvester.py,sha256=5eEHAZrgHPHZlTxDGaJrckDQgupFNC3Zax67EcCSqR8,9448
25
- shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=-IRyoQU0HXCEtIIcFmkFdz4snLB7bjFFqNcFVGSMiSA,4332
26
- shepherd_core/data_models/content/virtual_source.py,sha256=aoD8oam1POid0JG2ppttPA_Jl3y4ko5FNqzoaNKyBD8,14142
27
- shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=kx_lpBx0bLKqEHxS09GTnk8kuSbhuGhLgKHeaM6UviE,10481
24
+ shepherd_core/data_models/content/virtual_harvester.py,sha256=52HNac5k_GBCAhT2jBgu8oIqnvwMN3WMVmsAJrrxVRo,9678
25
+ shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=1u-V9RvFhbU74wEUv-BMQugh7OdzBE1SvhVe1v0DN-8,4251
26
+ shepherd_core/data_models/content/virtual_source.py,sha256=2ebNI1CvZgcfY5nz9P9p-Vr26E9l9bli9YeEd6_7yBY,14364
27
+ shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=qmtZp4Tcf5K8l65AjqOZhH-kDAaXs7YUIZNjJwaQhm8,10526
28
28
  shepherd_core/data_models/experiment/__init__.py,sha256=9TE9_aSnCNRhagsIWLTE8XkyjyMGB7kEGdswl-296v0,645
29
29
  shepherd_core/data_models/experiment/experiment.py,sha256=wnn6T3czuh4rz6OSYtMltCTbRpPX55TLVAtQcKO7Uhg,4044
30
30
  shepherd_core/data_models/experiment/observer_features.py,sha256=qxnb7anuQz9ZW5IUlPdUXYPIl5U7O9uXkJqZtMnAb0Y,5156
@@ -67,11 +67,11 @@ shepherd_core/testbed_client/client_web.py,sha256=iMh5T91152uugbFsqr2vvxLser0KIo
67
67
  shepherd_core/testbed_client/fixtures.py,sha256=4Uk583R4r6I5IB78HxOn-9UNH3sbFha7OPEdcSXvMCU,9939
68
68
  shepherd_core/testbed_client/user_model.py,sha256=5M3vWkAGBwdGDUYAanAjrZwpzMBlh3XLOVvNYWiLmms,2107
69
69
  shepherd_core/vsource/__init__.py,sha256=dS33KYLq5GQ9_D8HfdP8iWSocWTghCi2ZZG2AJWNfaM,391
70
- shepherd_core/vsource/virtual_converter_model.py,sha256=ZSoWVLfRmFEjeCNoQCg3BctzhdfayINUBDU_AJK1CR0,10404
71
- shepherd_core/vsource/virtual_harvester_model.py,sha256=wCbFfsqDRC5Rfu8qANkmkP9XGJOPHJY9-iSnI850JI4,7817
70
+ shepherd_core/vsource/virtual_converter_model.py,sha256=sQkJSj-7CVabHvXqk6C3cbLmztSSsdrSU3WgYr4h30E,11067
71
+ shepherd_core/vsource/virtual_harvester_model.py,sha256=ROR8vtKeM2WTnogV68TKBOu0zRVwOwQj_q67hW_qtpQ,8297
72
72
  shepherd_core/vsource/virtual_source_model.py,sha256=fjN8myTY3I_LpikF_aGAcxes3RGu1GP23P7XKC_UIyA,2737
73
- shepherd_core-2024.7.4.dist-info/METADATA,sha256=zFRCTLQEf-XN8Szx8fxb2JwCA6H0F26oUU5UMgWxVaA,7771
74
- shepherd_core-2024.7.4.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
75
- shepherd_core-2024.7.4.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
76
- shepherd_core-2024.7.4.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
77
- shepherd_core-2024.7.4.dist-info/RECORD,,
73
+ shepherd_core-2024.8.1.dist-info/METADATA,sha256=-DJfS_CoIctcSz8u4V-85AUpTYrcLVDZ0Pc8UQScNJI,7771
74
+ shepherd_core-2024.8.1.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
75
+ shepherd_core-2024.8.1.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
76
+ shepherd_core-2024.8.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
77
+ shepherd_core-2024.8.1.dist-info/RECORD,,