shepherd-data 2023.12.1__py3-none-any.whl → 2024.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shepherd_data/__init__.py +8 -5
- shepherd_data/cli.py +29 -29
- shepherd_data/ivonne.py +11 -11
- shepherd_data/mppt.py +22 -15
- shepherd_data/reader.py +31 -23
- shepherd_data-2024.4.2.dist-info/METADATA +88 -0
- shepherd_data-2024.4.2.dist-info/RECORD +11 -0
- {shepherd_data-2023.12.1.dist-info → shepherd_data-2024.4.2.dist-info}/WHEEL +1 -1
- {shepherd_data-2023.12.1.dist-info → shepherd_data-2024.4.2.dist-info}/top_level.txt +0 -1
- shepherd_data/debug_resampler.py +0 -30
- shepherd_data-2023.12.1.dist-info/METADATA +0 -274
- shepherd_data-2023.12.1.dist-info/RECORD +0 -22
- tests/__init__.py +0 -0
- tests/conftest.py +0 -33
- tests/test_cli.py +0 -8
- tests/test_cli_downsample.py +0 -47
- tests/test_cli_extract.py +0 -80
- tests/test_cli_plot.py +0 -120
- tests/test_cli_validate.py +0 -15
- tests/test_examples.py +0 -26
- tests/test_ivonne.py +0 -42
- tests/test_reader.py +0 -3
- {shepherd_data-2023.12.1.dist-info → shepherd_data-2024.4.2.dist-info}/entry_points.txt +0 -0
- {shepherd_data-2023.12.1.dist-info → shepherd_data-2024.4.2.dist-info}/zip-safe +0 -0
shepherd_data/__init__.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
"""
|
|
2
|
-
~~~~~
|
|
3
|
-
Provides classes for storing and retrieving sampled IV data to/from
|
|
4
|
-
HDF5 files.
|
|
1
|
+
"""Provides higher functionality compared to core, with additional lib-requirements.
|
|
5
2
|
|
|
3
|
+
Provides classes for storing and retrieving sampled IV data to/from
|
|
4
|
+
HDF5 files, with
|
|
5
|
+
- resampling
|
|
6
|
+
- plotting
|
|
7
|
+
- extracting metadata
|
|
6
8
|
"""
|
|
9
|
+
|
|
7
10
|
from shepherd_core import Writer
|
|
8
11
|
|
|
9
12
|
from .reader import Reader
|
|
10
13
|
|
|
11
|
-
__version__ = "
|
|
14
|
+
__version__ = "2024.4.2"
|
|
12
15
|
|
|
13
16
|
__all__ = [
|
|
14
17
|
"Reader",
|
shepherd_data/cli.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"""Command definitions for CLI
|
|
2
|
-
|
|
1
|
+
"""Command definitions for CLI."""
|
|
2
|
+
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
@@ -16,15 +16,17 @@ from shepherd_core import increase_verbose_level
|
|
|
16
16
|
from shepherd_core import local_tz
|
|
17
17
|
from shepherd_core.commons import samplerate_sps_default
|
|
18
18
|
|
|
19
|
-
from . import Reader
|
|
20
19
|
from . import Writer
|
|
21
20
|
from . import __version__
|
|
21
|
+
from .reader import Reader
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger("SHPData.cli")
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def path_to_flist(data_path: Path) -> List[Path]:
|
|
27
|
-
"""Every path gets transformed to a list of paths
|
|
27
|
+
"""Every path gets transformed to a list of paths.
|
|
28
|
+
|
|
29
|
+
Transformations:
|
|
28
30
|
- if directory: list of files inside
|
|
29
31
|
- if existing file: list with 1 element
|
|
30
32
|
- or else: empty list
|
|
@@ -57,7 +59,7 @@ def path_to_flist(data_path: Path) -> List[Path]:
|
|
|
57
59
|
)
|
|
58
60
|
@click.pass_context # TODO: is the ctx-type correct?
|
|
59
61
|
def cli(ctx: click.Context, verbose: bool, version: bool) -> None: # noqa: FBT001
|
|
60
|
-
"""Shepherd: Synchronized Energy Harvesting Emulator and Recorder"""
|
|
62
|
+
"""Shepherd: Synchronized Energy Harvesting Emulator and Recorder."""
|
|
61
63
|
if verbose:
|
|
62
64
|
increase_verbose_level(3)
|
|
63
65
|
if version:
|
|
@@ -71,7 +73,7 @@ def cli(ctx: click.Context, verbose: bool, version: bool) -> None: # noqa: FBT0
|
|
|
71
73
|
@cli.command(short_help="Validates a file or directory containing shepherd-recordings")
|
|
72
74
|
@click.argument("in_data", type=click.Path(exists=True, resolve_path=True))
|
|
73
75
|
def validate(in_data: Path) -> None:
|
|
74
|
-
"""
|
|
76
|
+
"""Validate a file or directory containing shepherd-recordings."""
|
|
75
77
|
files = path_to_flist(in_data)
|
|
76
78
|
verbose_level = get_verbose_level() # TODO: should be stored and passed in ctx
|
|
77
79
|
valid_dir = True
|
|
@@ -85,8 +87,8 @@ def validate(in_data: Path) -> None:
|
|
|
85
87
|
valid_dir &= valid_file
|
|
86
88
|
if not valid_file:
|
|
87
89
|
logger.error(" -> File '%s' was NOT valid", file.name)
|
|
88
|
-
except TypeError
|
|
89
|
-
logger.
|
|
90
|
+
except TypeError:
|
|
91
|
+
logger.exception("ERROR: Will skip file. It caused an exception.")
|
|
90
92
|
sys.exit(int(not valid_dir))
|
|
91
93
|
|
|
92
94
|
|
|
@@ -107,7 +109,7 @@ def validate(in_data: Path) -> None:
|
|
|
107
109
|
help="Set an individual csv-separator",
|
|
108
110
|
)
|
|
109
111
|
def extract(in_data: Path, ds_factor: float, separator: str) -> None:
|
|
110
|
-
"""
|
|
112
|
+
"""Extract recorded IVSamples and store them to csv."""
|
|
111
113
|
files = path_to_flist(in_data)
|
|
112
114
|
verbose_level = get_verbose_level()
|
|
113
115
|
if not isinstance(ds_factor, (float, int)) or ds_factor < 1:
|
|
@@ -143,8 +145,8 @@ def extract(in_data: Path, ds_factor: float, separator: str) -> None:
|
|
|
143
145
|
|
|
144
146
|
with Reader(ds_file, verbose=verbose_level > 2) as shpd:
|
|
145
147
|
shpd.save_csv(shpd["data"], separator)
|
|
146
|
-
except TypeError
|
|
147
|
-
logger.
|
|
148
|
+
except TypeError:
|
|
149
|
+
logger.exception("ERROR: Will skip file. It caused an exception.")
|
|
148
150
|
|
|
149
151
|
|
|
150
152
|
@cli.command(
|
|
@@ -159,14 +161,14 @@ def extract(in_data: Path, ds_factor: float, separator: str) -> None:
|
|
|
159
161
|
help="Set an individual csv-separator",
|
|
160
162
|
)
|
|
161
163
|
def extract_meta(in_data: Path, separator: str) -> None:
|
|
162
|
-
"""
|
|
164
|
+
"""Extract metadata and logs from file or directory containing shepherd-recordings."""
|
|
163
165
|
files = path_to_flist(in_data)
|
|
164
166
|
verbose_level = get_verbose_level()
|
|
165
167
|
for file in files:
|
|
166
168
|
logger.info("Extracting metadata & logs from '%s' ...", file.name)
|
|
167
169
|
# TODO: add default exports (user-centric) and allow specifying --all or specific ones
|
|
168
170
|
# TODO: could also be combined with other extractors (just have one)
|
|
169
|
-
# TODO remove deprecated
|
|
171
|
+
# TODO: remove deprecated timesync; "shepherd-log", "dmesg", "exceptions"
|
|
170
172
|
try:
|
|
171
173
|
with Reader(file, verbose=verbose_level > 2) as shpr:
|
|
172
174
|
shpr.save_metadata()
|
|
@@ -181,8 +183,8 @@ def extract_meta(in_data: Path, separator: str) -> None:
|
|
|
181
183
|
# TODO: allow omitting timestamp,
|
|
182
184
|
# also test if segmented uart is correctly written
|
|
183
185
|
shpr.warn_logs(element, show=True)
|
|
184
|
-
except TypeError
|
|
185
|
-
logger.
|
|
186
|
+
except TypeError:
|
|
187
|
+
logger.exception("ERROR: Will skip file. It caused an exception.")
|
|
186
188
|
|
|
187
189
|
|
|
188
190
|
@cli.command(
|
|
@@ -190,7 +192,7 @@ def extract_meta(in_data: Path, separator: str) -> None:
|
|
|
190
192
|
)
|
|
191
193
|
@click.argument("in_data", type=click.Path(exists=True, resolve_path=True))
|
|
192
194
|
def extract_uart(in_data: Path) -> None:
|
|
193
|
-
"""
|
|
195
|
+
"""Extract UART from GPIO-trace in file or directory containing shepherd-recordings."""
|
|
194
196
|
files = path_to_flist(in_data)
|
|
195
197
|
verbose_level = get_verbose_level()
|
|
196
198
|
for file in files:
|
|
@@ -213,8 +215,8 @@ def extract_uart(in_data: Path) -> None:
|
|
|
213
215
|
# TODO: allow to skip Timestamp and export raw text
|
|
214
216
|
log_file.write(f"\t{str.encode(line[1])}")
|
|
215
217
|
log_file.write("\n")
|
|
216
|
-
except TypeError
|
|
217
|
-
logger.
|
|
218
|
+
except TypeError:
|
|
219
|
+
logger.exception("ERROR: Will skip file. It caused an exception.")
|
|
218
220
|
|
|
219
221
|
|
|
220
222
|
@cli.command(short_help="Extracts gpio-trace from file or directory containing shepherd-recordings")
|
|
@@ -227,7 +229,7 @@ def extract_uart(in_data: Path) -> None:
|
|
|
227
229
|
help="Set an individual csv-separator",
|
|
228
230
|
)
|
|
229
231
|
def extract_gpio(in_data: Path, separator: str) -> None:
|
|
230
|
-
"""
|
|
232
|
+
"""Extract UART from gpio-trace in file or directory containing shepherd-recordings."""
|
|
231
233
|
files = path_to_flist(in_data)
|
|
232
234
|
verbose_level = get_verbose_level()
|
|
233
235
|
for file in files:
|
|
@@ -237,8 +239,8 @@ def extract_gpio(in_data: Path, separator: str) -> None:
|
|
|
237
239
|
wfs = shpr.gpio_to_waveforms()
|
|
238
240
|
for name, wf in wfs.items():
|
|
239
241
|
shpr.waveform_to_csv(name, wf, separator)
|
|
240
|
-
except TypeError
|
|
241
|
-
logger.
|
|
242
|
+
except TypeError:
|
|
243
|
+
logger.exception("ERROR: Will skip file. It caused an exception.")
|
|
242
244
|
|
|
243
245
|
|
|
244
246
|
@cli.command(
|
|
@@ -261,9 +263,7 @@ def extract_gpio(in_data: Path, separator: str) -> None:
|
|
|
261
263
|
help="Alternative Input to determine a downsample-factor (Choose One)",
|
|
262
264
|
)
|
|
263
265
|
def downsample(in_data: Path, ds_factor: Optional[float], sample_rate: Optional[int]) -> None:
|
|
264
|
-
"""
|
|
265
|
-
or directory containing shepherd-recordings
|
|
266
|
-
"""
|
|
266
|
+
"""Create an array of down-sampled files from file or dir containing shepherd-recordings."""
|
|
267
267
|
if ds_factor is None and sample_rate is not None and sample_rate >= 1:
|
|
268
268
|
ds_factor = int(samplerate_sps_default / sample_rate)
|
|
269
269
|
# TODO: shouldn't current sps be based on file rather than default?
|
|
@@ -303,8 +303,8 @@ def downsample(in_data: Path, ds_factor: Optional[float], sample_rate: Optional[
|
|
|
303
303
|
shpr.downsample(shpr.ds_time, shpw.ds_time, ds_factor=_factor, is_time=True)
|
|
304
304
|
shpr.downsample(shpr.ds_voltage, shpw.ds_voltage, ds_factor=_factor)
|
|
305
305
|
shpr.downsample(shpr.ds_current, shpw.ds_current, ds_factor=_factor)
|
|
306
|
-
except TypeError
|
|
307
|
-
logger.
|
|
306
|
+
except TypeError:
|
|
307
|
+
logger.exception("ERROR: Will skip file. It caused an exception.")
|
|
308
308
|
|
|
309
309
|
|
|
310
310
|
@cli.command(short_help="Plots IV-trace from file or directory containing shepherd-recordings")
|
|
@@ -351,7 +351,7 @@ def plot(
|
|
|
351
351
|
height: int,
|
|
352
352
|
multiplot: bool, # noqa: FBT001
|
|
353
353
|
) -> None:
|
|
354
|
-
"""
|
|
354
|
+
"""Plot IV-trace from file or directory containing shepherd-recordings."""
|
|
355
355
|
files = path_to_flist(in_data)
|
|
356
356
|
verbose_level = get_verbose_level()
|
|
357
357
|
multiplot = multiplot and len(files) > 1
|
|
@@ -364,8 +364,8 @@ def plot(
|
|
|
364
364
|
data.append(shpr.generate_plot_data(start, end, relative_timestamp=True))
|
|
365
365
|
else:
|
|
366
366
|
shpr.plot_to_file(start, end, width, height)
|
|
367
|
-
except TypeError
|
|
368
|
-
logger.
|
|
367
|
+
except TypeError:
|
|
368
|
+
logger.exception("ERROR: Will skip file. It caused an exception.")
|
|
369
369
|
if multiplot:
|
|
370
370
|
logger.info("Got %d datasets to plot", len(data))
|
|
371
371
|
mpl_path = Reader.multiplot_to_file(data, in_data, width, height)
|
shepherd_data/ivonne.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
"""
|
|
2
|
-
to generate valid shepherd-data for emulation
|
|
1
|
+
"""file-reader with various converters to generate valid shepherd-data for emulation."""
|
|
3
2
|
|
|
4
|
-
"""
|
|
5
3
|
import errno
|
|
6
4
|
import logging
|
|
7
5
|
import math
|
|
@@ -34,7 +32,7 @@ def get_isc(coeffs: pd.DataFrame): # noqa: ANN201
|
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
class Reader:
|
|
37
|
-
"""
|
|
35
|
+
"""Container for converters that bridge the gap to shepherds data-files."""
|
|
38
36
|
|
|
39
37
|
_logger: logging.Logger = logging.getLogger("SHPData.IVonne.Reader")
|
|
40
38
|
|
|
@@ -102,8 +100,9 @@ class Reader:
|
|
|
102
100
|
pts_per_curve: int = 1000,
|
|
103
101
|
duration_s: Optional[float] = None,
|
|
104
102
|
) -> None:
|
|
105
|
-
"""
|
|
106
|
-
|
|
103
|
+
"""Transform recorded parameters to shepherd hdf database with IV curves.
|
|
104
|
+
|
|
105
|
+
Shepherd works with IV 'surfaces', which is a stream of IV curves.
|
|
107
106
|
|
|
108
107
|
:param shp_output: Path where the resulting hdf file shall be stored
|
|
109
108
|
:param v_max: Maximum voltage supported by shepherd
|
|
@@ -134,7 +133,7 @@ class Reader:
|
|
|
134
133
|
for idx in job_iter:
|
|
135
134
|
idx_top = min(idx + max_elements, df_elements_n)
|
|
136
135
|
df_slice = self._df.iloc[idx : idx_top + 1].copy()
|
|
137
|
-
df_slice["timestamp"] = pd.
|
|
136
|
+
df_slice["timestamp"] = pd.to_timedelta(df_slice["time"], unit="s")
|
|
138
137
|
df_slice = df_slice.set_index("timestamp")
|
|
139
138
|
# warning: .interpolate does crash in debug-mode with typeError
|
|
140
139
|
df_slice = (
|
|
@@ -163,7 +162,7 @@ class Reader:
|
|
|
163
162
|
duration_s: Optional[float] = None,
|
|
164
163
|
tracker: Optional[MPPTracker] = None,
|
|
165
164
|
) -> None:
|
|
166
|
-
"""
|
|
165
|
+
"""Transform shepherd IV curves to shepherd IV samples / traces.
|
|
167
166
|
|
|
168
167
|
For the 'buck' and 'buck-boost' modes, shepherd takes voltage and current traces.
|
|
169
168
|
These can be recorded with shepherd or generated from existing IV curves by, for
|
|
@@ -180,6 +179,7 @@ class Reader:
|
|
|
180
179
|
:param v_max: Maximum voltage supported by shepherd
|
|
181
180
|
:param duration_s: time to stop in seconds, counted from beginning
|
|
182
181
|
:param tracker: VOC or OPT
|
|
182
|
+
|
|
183
183
|
"""
|
|
184
184
|
if self._df is None:
|
|
185
185
|
raise RuntimeError("IVonne Context was not entered - file not open!")
|
|
@@ -213,7 +213,7 @@ class Reader:
|
|
|
213
213
|
df_slice.loc[:, "voc"] = get_voc(df_slice)
|
|
214
214
|
df_slice.loc[df_slice["voc"] >= v_max, "voc"] = v_max
|
|
215
215
|
df_slice = tracker.process(df_slice)
|
|
216
|
-
df_slice["timestamp"] = pd.
|
|
216
|
+
df_slice["timestamp"] = pd.to_timedelta(df_slice["time"], unit="s")
|
|
217
217
|
df_slice = df_slice[["time", "v", "i", "timestamp"]].set_index("timestamp")
|
|
218
218
|
# warning: .interpolate does crash in debug-mode with typeError
|
|
219
219
|
df_slice = (
|
|
@@ -231,7 +231,7 @@ class Reader:
|
|
|
231
231
|
v_max: float = 5.0,
|
|
232
232
|
duration_s: Optional[float] = None,
|
|
233
233
|
) -> None:
|
|
234
|
-
"""
|
|
234
|
+
"""Transform ivonne-parameters to up-sampled versions for shepherd.
|
|
235
235
|
|
|
236
236
|
:param shp_output: Path where the resulting hdf file shall be stored
|
|
237
237
|
:param v_max: Maximum voltage supported by shepherd
|
|
@@ -264,7 +264,7 @@ class Reader:
|
|
|
264
264
|
df_slice.loc[:, "voc"] = get_voc(df_slice)
|
|
265
265
|
df_slice.loc[df_slice["voc"] >= v_max, "voc"] = v_max
|
|
266
266
|
df_slice.loc[:, "isc"] = get_isc(df_slice)
|
|
267
|
-
df_slice["timestamp"] = pd.
|
|
267
|
+
df_slice["timestamp"] = pd.to_timedelta(df_slice["time"], unit="s")
|
|
268
268
|
df_slice = df_slice[["time", "voc", "isc", "timestamp"]].set_index("timestamp")
|
|
269
269
|
# warning: .interpolate does crash in debug-mode with typeError
|
|
270
270
|
df_slice = (
|
shepherd_data/mppt.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Harvesters, simple and fast approach.
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
Might be exchanged by shepherds py-model of pru-harvesters.
|
|
3
4
|
"""
|
|
5
|
+
|
|
4
6
|
import numpy as np
|
|
5
7
|
import pandas as pd
|
|
6
8
|
|
|
@@ -8,16 +10,11 @@ from shepherd_core import Calc_t
|
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def iv_model(voltages: Calc_t, coeffs: pd.Series) -> Calc_t:
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
----
|
|
15
|
-
:param voltages: Load voltage of the solar panel
|
|
16
|
-
:param coeffs: three generic coefficients
|
|
13
|
+
"""Calculate simple diode based model (equivalent circuit diagram) of a solar panel IV curve.
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
:param voltages: Load voltage of the solar panel
|
|
16
|
+
:param coeffs: three generic coefficients
|
|
17
|
+
:return: Solar current at given load voltage
|
|
21
18
|
"""
|
|
22
19
|
currents = float(coeffs["a"]) - float(coeffs["b"]) * (
|
|
23
20
|
np.exp(float(coeffs["c"]) * voltages) - 1.0
|
|
@@ -40,7 +37,7 @@ def find_oc(v_arr: np.ndarray, i_arr: np.ndarray, ratio: float = 0.05) -> np.nda
|
|
|
40
37
|
|
|
41
38
|
|
|
42
39
|
class MPPTracker:
|
|
43
|
-
"""Prototype
|
|
40
|
+
"""Prototype for a MPPT-class.
|
|
44
41
|
|
|
45
42
|
:param v_max: Maximum voltage supported by shepherd
|
|
46
43
|
:param pts_per_curve: resolution of internal ivcurve
|
|
@@ -52,15 +49,15 @@ class MPPTracker:
|
|
|
52
49
|
self.v_proto: np.ndarray = np.linspace(0, v_max, pts_per_curve)
|
|
53
50
|
|
|
54
51
|
def process(self, coeffs: pd.DataFrame) -> pd.DataFrame:
|
|
55
|
-
"""Apply harvesting model to input data
|
|
52
|
+
"""Apply harvesting model to input data.
|
|
56
53
|
|
|
57
54
|
:param coeffs: ivonne coefficients
|
|
58
|
-
:return:
|
|
55
|
+
:return: ivsample-data
|
|
59
56
|
"""
|
|
60
57
|
|
|
61
58
|
|
|
62
59
|
class OpenCircuitTracker(MPPTracker):
|
|
63
|
-
"""Open-circuit based MPPT
|
|
60
|
+
"""Open-circuit (-voltage) based MPPT.
|
|
64
61
|
|
|
65
62
|
:param v_max: Maximum voltage supported by shepherd
|
|
66
63
|
:param pts_per_curve: resolution of internal ivcurve
|
|
@@ -72,6 +69,11 @@ class OpenCircuitTracker(MPPTracker):
|
|
|
72
69
|
self.ratio = ratio
|
|
73
70
|
|
|
74
71
|
def process(self, coeffs: pd.DataFrame) -> pd.DataFrame:
|
|
72
|
+
"""Apply harvesting model to input data.
|
|
73
|
+
|
|
74
|
+
:param coeffs: ivonne coefficients
|
|
75
|
+
:return: ivsample-data
|
|
76
|
+
"""
|
|
75
77
|
coeffs["icurve"] = coeffs.apply(lambda x: iv_model(self.v_proto, x), axis=1)
|
|
76
78
|
if "voc" not in coeffs.columns:
|
|
77
79
|
coeffs["voc"] = coeffs.apply(lambda x: find_oc(self.v_proto, x["ivcurve"]), axis=1)
|
|
@@ -85,7 +87,7 @@ class OpenCircuitTracker(MPPTracker):
|
|
|
85
87
|
|
|
86
88
|
|
|
87
89
|
class OptimalTracker(MPPTracker):
|
|
88
|
-
"""Optimal MPPT
|
|
90
|
+
"""Optimal MPPT by looking at the whole curve.
|
|
89
91
|
|
|
90
92
|
Calculates optimal harvesting voltage for every time and corresponding IV curve.
|
|
91
93
|
|
|
@@ -97,6 +99,11 @@ class OptimalTracker(MPPTracker):
|
|
|
97
99
|
super().__init__(v_max, pts_per_curve)
|
|
98
100
|
|
|
99
101
|
def process(self, coeffs: pd.DataFrame) -> pd.DataFrame:
|
|
102
|
+
"""Apply harvesting model to input data.
|
|
103
|
+
|
|
104
|
+
:param coeffs: ivonne coefficients
|
|
105
|
+
:return: ivsample-data
|
|
106
|
+
"""
|
|
100
107
|
coeffs["icurve"] = coeffs.apply(lambda x: iv_model(self.v_proto, x), axis=1)
|
|
101
108
|
coeffs["pcurve"] = coeffs.apply(lambda x: self.v_proto * x["icurve"], axis=1)
|
|
102
109
|
coeffs["max_pos"] = coeffs.apply(lambda x: np.argmax(x["pcurve"]), axis=1)
|
shepherd_data/reader.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"""Reader-Baseclass
|
|
2
|
-
|
|
1
|
+
"""Reader-Baseclass for opening shepherds hdf5-files."""
|
|
2
|
+
|
|
3
3
|
import math
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from pathlib import Path
|
|
@@ -26,6 +26,7 @@ class Reader(CoreReader):
|
|
|
26
26
|
----
|
|
27
27
|
file_path: Path of hdf5 file containing shepherd data with iv-samples, iv-curves or isc&voc
|
|
28
28
|
verbose: more info during usage, 'None' skips the setter
|
|
29
|
+
|
|
29
30
|
"""
|
|
30
31
|
|
|
31
32
|
def __init__(
|
|
@@ -37,7 +38,7 @@ class Reader(CoreReader):
|
|
|
37
38
|
super().__init__(file_path, verbose=verbose)
|
|
38
39
|
|
|
39
40
|
def save_csv(self, h5_group: h5py.Group, separator: str = ";") -> int:
|
|
40
|
-
"""Extract numerical data
|
|
41
|
+
"""Extract numerical data from group and store it into csv.
|
|
41
42
|
|
|
42
43
|
:param h5_group: can be external and should probably be downsampled
|
|
43
44
|
:param separator: used between columns
|
|
@@ -73,7 +74,7 @@ class Reader(CoreReader):
|
|
|
73
74
|
return h5_group["time"][:].shape[0]
|
|
74
75
|
|
|
75
76
|
def save_log(self, h5_group: h5py.Group, *, add_timestamp: bool = True) -> int:
|
|
76
|
-
"""Save dataset
|
|
77
|
+
"""Save dataset from group as log, optimal for logged 'dmesg' and console-output.
|
|
77
78
|
|
|
78
79
|
:param h5_group: can be external
|
|
79
80
|
:param add_timestamp: can be external
|
|
@@ -114,6 +115,7 @@ class Reader(CoreReader):
|
|
|
114
115
|
*,
|
|
115
116
|
show: bool = True,
|
|
116
117
|
) -> int:
|
|
118
|
+
"""Print warning messages from log in data-group."""
|
|
117
119
|
_count = self.count_errors_in_log(group_name, min_level)
|
|
118
120
|
if _count < 1:
|
|
119
121
|
return 0
|
|
@@ -145,7 +147,7 @@ class Reader(CoreReader):
|
|
|
145
147
|
|
|
146
148
|
def downsample(
|
|
147
149
|
self,
|
|
148
|
-
data_src: h5py.Dataset,
|
|
150
|
+
data_src: Union[h5py.Dataset, np.ndarray],
|
|
149
151
|
data_dst: Union[None, h5py.Dataset, np.ndarray],
|
|
150
152
|
start_n: int = 0,
|
|
151
153
|
end_n: Optional[int] = None,
|
|
@@ -153,15 +155,17 @@ class Reader(CoreReader):
|
|
|
153
155
|
*,
|
|
154
156
|
is_time: bool = False,
|
|
155
157
|
) -> Union[h5py.Dataset, np.ndarray]:
|
|
156
|
-
"""
|
|
158
|
+
"""Sample down iv-data.
|
|
159
|
+
|
|
160
|
+
Warning: only valid for IV-Stream, not IV-Curves
|
|
157
161
|
|
|
158
162
|
:param data_src: a h5-dataset to digest, can be external
|
|
159
163
|
:param data_dst: can be a dataset, numpy-array or None (will be created internally then)
|
|
160
164
|
:param start_n: start-sample
|
|
161
165
|
:param end_n: ending-sample (not included)
|
|
162
|
-
:param ds_factor:
|
|
163
|
-
:param is_time: time is not really
|
|
164
|
-
:return:
|
|
166
|
+
:param ds_factor: sampling-factor
|
|
167
|
+
:param is_time: time is not really down-sampled, but decimated
|
|
168
|
+
:return: resampled h5-dataset or numpy-array
|
|
165
169
|
"""
|
|
166
170
|
from scipy import signal # here due to massive delay
|
|
167
171
|
|
|
@@ -222,7 +226,7 @@ class Reader(CoreReader):
|
|
|
222
226
|
|
|
223
227
|
def resample(
|
|
224
228
|
self,
|
|
225
|
-
data_src: h5py.Dataset,
|
|
229
|
+
data_src: Union[h5py.Dataset, np.ndarray],
|
|
226
230
|
data_dst: Union[None, h5py.Dataset, np.ndarray],
|
|
227
231
|
start_n: int = 0,
|
|
228
232
|
end_n: Optional[int] = None,
|
|
@@ -230,13 +234,15 @@ class Reader(CoreReader):
|
|
|
230
234
|
*,
|
|
231
235
|
is_time: bool = False,
|
|
232
236
|
) -> Union[h5py.Dataset, np.ndarray]:
|
|
233
|
-
"""
|
|
234
|
-
|
|
235
|
-
:param
|
|
236
|
-
:param
|
|
237
|
-
:param
|
|
238
|
-
:param
|
|
239
|
-
:
|
|
237
|
+
"""Up- or down-sample the original trace-data.
|
|
238
|
+
|
|
239
|
+
:param data_src: original iv-data
|
|
240
|
+
:param data_dst: resampled iv-traces
|
|
241
|
+
:param start_n: start index of the source
|
|
242
|
+
:param end_n: end index of the source
|
|
243
|
+
:param samplerate_dst: desired sampling rate
|
|
244
|
+
:param is_time: time-array is handled differently than IV-Samples
|
|
245
|
+
:return: resampled iv-data
|
|
240
246
|
"""
|
|
241
247
|
self._logger.error("Resampling is still under construction - do not use for now!")
|
|
242
248
|
if self.get_datatype() == "ivcurve":
|
|
@@ -327,7 +333,7 @@ class Reader(CoreReader):
|
|
|
327
333
|
*,
|
|
328
334
|
relative_timestamp: bool = True,
|
|
329
335
|
) -> Dict:
|
|
330
|
-
"""
|
|
336
|
+
"""Provide down-sampled iv-data that can be fed into plot_to_file().
|
|
331
337
|
|
|
332
338
|
:param start_s: time in seconds, relative to start of recording
|
|
333
339
|
:param end_s: time in seconds, relative to start of recording
|
|
@@ -371,14 +377,15 @@ class Reader(CoreReader):
|
|
|
371
377
|
|
|
372
378
|
@staticmethod
|
|
373
379
|
def assemble_plot(data: Union[dict, list], width: int = 20, height: int = 10) -> plt.Figure:
|
|
374
|
-
"""
|
|
380
|
+
"""Create the actual figure.
|
|
375
381
|
|
|
376
382
|
:param data: plottable / down-sampled iv-data with some meta-data
|
|
377
383
|
-> created with generate_plot_data()
|
|
378
384
|
:param width: plot-width
|
|
379
385
|
:param height: plot-height
|
|
380
|
-
:return:
|
|
386
|
+
:return: figure
|
|
381
387
|
"""
|
|
388
|
+
# TODO: add power (if wanted)
|
|
382
389
|
if isinstance(data, dict):
|
|
383
390
|
data = [data]
|
|
384
391
|
fig, axes = plt.subplots(2, 1, sharex="all")
|
|
@@ -403,8 +410,9 @@ class Reader(CoreReader):
|
|
|
403
410
|
width: int = 20,
|
|
404
411
|
height: int = 10,
|
|
405
412
|
) -> None:
|
|
406
|
-
"""
|
|
407
|
-
|
|
413
|
+
"""Create (down-sampled) IV-Plots.
|
|
414
|
+
|
|
415
|
+
Omitting start- and end-time will use the whole trace (full duration).
|
|
408
416
|
|
|
409
417
|
:param start_s: time in seconds, relative to start of recording, optional
|
|
410
418
|
:param end_s: time in seconds, relative to start of recording, optional
|
|
@@ -432,7 +440,7 @@ class Reader(CoreReader):
|
|
|
432
440
|
def multiplot_to_file(
|
|
433
441
|
data: list, plot_path: Path, width: int = 20, height: int = 10
|
|
434
442
|
) -> Optional[Path]:
|
|
435
|
-
"""
|
|
443
|
+
"""Create (down-sampled) IV-Multi-Plots (of more than one trace).
|
|
436
444
|
|
|
437
445
|
:param data: plottable / down-sampled iv-data with some meta-data
|
|
438
446
|
-> created with generate_plot_data()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: shepherd_data
|
|
3
|
+
Version: 2024.4.2
|
|
4
|
+
Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
|
|
5
|
+
Author-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
|
|
6
|
+
Maintainer-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
|
|
7
|
+
Project-URL: Documentation, https://github.com/orgua/shepherd-datalib/blob/main/README.md
|
|
8
|
+
Project-URL: Issues, https://pypi.org/project/shepherd-data/issues
|
|
9
|
+
Project-URL: Source, https://pypi.org/project/shepherd-data/
|
|
10
|
+
Keywords: testbed,beaglebone,pru,batteryless,energyharvesting,solar
|
|
11
|
+
Platform: unix
|
|
12
|
+
Platform: linux
|
|
13
|
+
Platform: osx
|
|
14
|
+
Platform: cygwin
|
|
15
|
+
Platform: win32
|
|
16
|
+
Platform: win64
|
|
17
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: Intended Audience :: Information Technology
|
|
20
|
+
Classifier: Intended Audience :: Science/Research
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
27
|
+
Classifier: Operating System :: OS Independent
|
|
28
|
+
Classifier: Natural Language :: English
|
|
29
|
+
Requires-Python: >=3.8
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
Requires-Dist: click
|
|
32
|
+
Requires-Dist: h5py
|
|
33
|
+
Requires-Dist: matplotlib
|
|
34
|
+
Requires-Dist: numpy
|
|
35
|
+
Requires-Dist: pandas >=2.0.0
|
|
36
|
+
Requires-Dist: pyYAML
|
|
37
|
+
Requires-Dist: scipy
|
|
38
|
+
Requires-Dist: shepherd-core[inventory] >=2024.4.2
|
|
39
|
+
Requires-Dist: tqdm
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: shepherd-core[dev] ; extra == 'dev'
|
|
42
|
+
Requires-Dist: pandas-stubs ; extra == 'dev'
|
|
43
|
+
Provides-Extra: elf
|
|
44
|
+
Requires-Dist: shepherd-core[elf] ; extra == 'elf'
|
|
45
|
+
Provides-Extra: test
|
|
46
|
+
Requires-Dist: shepherd-core[test] ; extra == 'test'
|
|
47
|
+
Requires-Dist: pytest ; extra == 'test'
|
|
48
|
+
Requires-Dist: pytest-click ; extra == 'test'
|
|
49
|
+
Requires-Dist: coverage ; extra == 'test'
|
|
50
|
+
|
|
51
|
+
# Shepherd-Data-Tool
|
|
52
|
+
|
|
53
|
+
[](https://pypi.org/project/shepherd_data)
|
|
54
|
+
[](https://pypi.python.org/pypi/shepherd-data)
|
|
55
|
+
[](https://github.com/orgua/shepherd-datalib/actions/workflows/py_unittest.yml)
|
|
56
|
+
[](https://github.com/astral-sh/ruff)
|
|
57
|
+
|
|
58
|
+
**Main Documentation**: <https://orgua.github.io/shepherd>
|
|
59
|
+
|
|
60
|
+
**Source Code**: <https://github.com/orgua/shepherd-datalib>
|
|
61
|
+
|
|
62
|
+
**Main Project**: <https://github.com/orgua/shepherd>
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
`shepherd-data` eases the handling of hdf5-recordings used by the [shepherd](https://github.com/orgua/shepherd)-testbed. Users can read, validate and create files and also extract, down-sample and plot information.
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
### PIP - Online
|
|
71
|
+
|
|
72
|
+
```shell
|
|
73
|
+
pip3 install shepherd-data -U
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For bleeding-edge-features or dev-work it is possible to install directly from GitHub-Sources (here `dev`-branch):
|
|
77
|
+
|
|
78
|
+
```Shell
|
|
79
|
+
pip install git+https://github.com/orgua/shepherd-datalib.git@dev#subdirectory=shepherd_data -U
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## More
|
|
83
|
+
|
|
84
|
+
Please consult the [official documentation](https://orgua.github.io/shepherd) for more, it covers:
|
|
85
|
+
|
|
86
|
+
- general context
|
|
87
|
+
- command-line interface
|
|
88
|
+
- programming interface
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
shepherd_data/__init__.py,sha256=EVDwIGGT7VHG4hZ8WScG5aUFIokNAxmd8fR0ZR22hmA,352
|
|
2
|
+
shepherd_data/cli.py,sha256=GD9yxRToqi5fDhfwPZrXQUGlRz5G65A9K1j3Kr6x-zI,14981
|
|
3
|
+
shepherd_data/ivonne.py,sha256=_tys7LNnUStj-zV56DZxxx3Ku-ILdrNJqLXst7PXcxU,11584
|
|
4
|
+
shepherd_data/mppt.py,sha256=588KSrLuJfNRKKnnL6ewePLi3zrwaO_PAZypikACrks,3925
|
|
5
|
+
shepherd_data/reader.py,sha256=JKTVemjH_6678MCNf2cDzRIsCyfD2B6CTUzBiG110QY,18531
|
|
6
|
+
shepherd_data-2024.4.2.dist-info/METADATA,sha256=R9-bdFb5pm8FR8Rg6CTwInx-LOIifJHyBRIP0uxoj3s,3387
|
|
7
|
+
shepherd_data-2024.4.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
8
|
+
shepherd_data-2024.4.2.dist-info/entry_points.txt,sha256=6PBfY36A1xNOdzLiz-Qoukya_UzFZAwOapwmRNnPeZ8,56
|
|
9
|
+
shepherd_data-2024.4.2.dist-info/top_level.txt,sha256=7-SCTY-TG1mLY72OVKCaqte1hy-X8woxknIUAD3OIxs,14
|
|
10
|
+
shepherd_data-2024.4.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
11
|
+
shepherd_data-2024.4.2.dist-info/RECORD,,
|