shepherd-core 2025.8.1__tar.gz → 2026.2.1__tar.gz
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-2026.2.1/LICENSE +21 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/PKG-INFO +6 -3
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/eenv_generator.py +3 -3
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/experiment_generic_var1.py +8 -7
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/experiment_generic_var2.py +6 -5
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/experiment_models.py +2 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/firmware_model.py +3 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/firmware_modification.py +2 -1
- shepherd_core-2025.8.1/examples/simulate_vharvester.py → shepherd_core-2026.2.1/examples/simulations/vharvester.py +20 -9
- shepherd_core-2025.8.1/examples/simulate_vsource.py → shepherd_core-2026.2.1/examples/simulations/vsource.py +19 -10
- shepherd_core-2026.2.1/examples/simulations/vstorage.py +241 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/vsource_debug_sim.py +7 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/pyproject.toml +4 -12
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/config.py +1 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/__init__.py +8 -4
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/cal_measurement.py +7 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/calibration.py +23 -12
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/content.py +12 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/shepherd.py +13 -4
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/wrapper.py +2 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/__init__.py +24 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/_external_fixtures.yaml +104 -96
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_bonito.yaml +436 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_multivariate_random_walk.yaml +164 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_on_off_markov.yaml +3280 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_on_off_windows.yaml +3260 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_static.yaml +450 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/energy_environment.py +370 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/energy_environment_fixture.yaml +21 -18
- shepherd_core-2026.2.1/shepherd_core/data_models/content/enum_datatypes.py +109 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/firmware.py +44 -16
- shepherd_core-2025.8.1/shepherd_core/data_models/content/virtual_harvester.py → shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_harvester_config.py +13 -96
- shepherd_core-2025.8.1/shepherd_core/data_models/content/virtual_source.py → shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_source_config.py +103 -60
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_source_fixture.yaml +24 -24
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_config.py +429 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_creator.py +267 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_ideal.yaml +637 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_lead.yaml +49 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_lipo.yaml +735 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_mlcc.yaml +200 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_param_experiments.py +151 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_super.yaml +150 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/virtual_storage_fixture_tantal.yaml +550 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/experiment.py +38 -13
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/observer_features.py +17 -4
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/target_config.py +56 -8
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/__init__.py +13 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/emulation.py +10 -6
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/firmware_mod.py +3 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/harvest.py +3 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/helper_paths.py +2 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/observer_tasks.py +8 -6
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/programming.py +4 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/testbed_tasks.py +8 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/cape.py +2 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/gpio.py +2 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/mcu.py +2 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/observer.py +2 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/target.py +7 -5
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/target_fixture.old1 +1 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/target_fixture.yaml +1 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/testbed.py +17 -15
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/decoder_waveform/uart.py +1 -1
- shepherd_core-2026.2.1/shepherd_core/exit_handler.py +22 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/converter.py +2 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/validation.py +1 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/__init__.py +23 -21
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/system.py +3 -3
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/logger.py +0 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/reader.py +32 -27
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/cache_path.py +3 -3
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/client_abc_fix.py +14 -3
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/client_web.py +7 -5
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/fixtures.py +7 -7
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/version.py +1 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/__init__.py +4 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_converter_model.py +29 -28
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_harvester_model.py +29 -21
- shepherd_core-2026.2.1/shepherd_core/vsource/virtual_harvester_simulation.py +71 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_source_model.py +18 -14
- shepherd_core-2026.2.1/shepherd_core/vsource/virtual_source_simulation.py +143 -0
- shepherd_core-2026.2.1/shepherd_core/vsource/virtual_storage_model.py +164 -0
- shepherd_core-2026.2.1/shepherd_core/vsource/virtual_storage_model_fixed_point_math.py +58 -0
- shepherd_core-2026.2.1/shepherd_core/vsource/virtual_storage_models_kibam.py +449 -0
- shepherd_core-2026.2.1/shepherd_core/vsource/virtual_storage_simulator.py +104 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/writer.py +16 -9
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/PKG-INFO +6 -3
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/SOURCES.txt +26 -6
- shepherd_core-2026.2.1/tests/data_models/conftest.py +40 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_emulator.yaml +2 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_virtsource.yaml +9 -8
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_base_models.py +3 -2
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_content_fixtures.py +6 -6
- shepherd_core-2026.2.1/tests/data_models/test_content_models.py +649 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_experiment_models.py +88 -8
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_task_generation.py +6 -5
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_task_models.py +2 -3
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_testbed_models.py +0 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/decoder_waveform/test_decoder.py +0 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/inventory/test_inventory.py +0 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/test_cal_hw.py +0 -1
- shepherd_core-2026.2.1/tests/test_examples.py +53 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/vsource/test_converter.py +19 -22
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/vsource/test_harvester.py +7 -7
- shepherd_core-2025.8.1/shepherd_core/data_models/content/__init__.py +0 -20
- shepherd_core-2025.8.1/shepherd_core/data_models/content/energy_environment.py +0 -52
- shepherd_core-2025.8.1/shepherd_core/data_models/content/firmware_datatype.py +0 -15
- shepherd_core-2025.8.1/shepherd_core/data_models/virtual_source_doc.txt +0 -207
- shepherd_core-2025.8.1/shepherd_core/vsource/virtual_harvester_simulation.py +0 -72
- shepherd_core-2025.8.1/shepherd_core/vsource/virtual_source_simulation.py +0 -145
- shepherd_core-2025.8.1/tests/data_models/conftest.py +0 -14
- shepherd_core-2025.8.1/tests/data_models/test_content_models.py +0 -282
- shepherd_core-2025.8.1/tests/test_examples.py +0 -42
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/README.md +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/experiment_from_yaml.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/inventory.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/uart_decode_waveform.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/examples/uart_raw2.csv +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/setup.cfg +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/calibration_hw_def.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/commons.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/timezone.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_harvester_fixture.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/readme.md +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/cape_fixture.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/gpio_fixture.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/mcu_fixture.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/observer_fixture.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/testbed_fixture.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/decoder_waveform/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/converter_elf.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/patcher.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/python.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/target.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/user_model.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/target_model.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/dependency_links.txt +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/requires.txt +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/top_level.txt +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/zip-safe +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/conftest.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_data.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_data_faulty.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_meas.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_meas_faulty1.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_meas_faulty2.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_experiment.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_experiment_alternative.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_harvester.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_testbed.yaml +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_examples.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/data_models/test_testbed_fixtures.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/decoder_waveform/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/fw_tools/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/fw_tools/build_msp.elf +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/fw_tools/build_nrf.elf +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/fw_tools/conftest.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/fw_tools/test_converter.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/fw_tools/test_patcher.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/fw_tools/test_validation.py +1 -1
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/inventory/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/test_logger.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/test_reader.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/test_writer.py +4 -4
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/testbed_client/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/vsource/__init__.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/vsource/conftest.py +0 -0
- {shepherd_core-2025.8.1 → shepherd_core-2026.2.1}/tests/vsource/test_z.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2025, Networked Embedded Systems Lab, TU Dresden, Ingmar Splitt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
|
-
Name:
|
|
3
|
-
Version:
|
|
2
|
+
Name: shepherd-core
|
|
3
|
+
Version: 2026.2.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>
|
|
7
|
+
License-Expression: MIT
|
|
7
8
|
Project-URL: Documentation, https://github.com/nes-lab/shepherd-tools/blob/main/README.md
|
|
8
9
|
Project-URL: Issues, https://github.com/nes-lab/shepherd-tools/issues
|
|
9
10
|
Project-URL: Source, https://pypi.org/project/shepherd-core/
|
|
@@ -22,11 +23,12 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
22
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
25
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
-
Classifier:
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
27
|
Classifier: Operating System :: OS Independent
|
|
27
28
|
Classifier: Natural Language :: English
|
|
28
29
|
Requires-Python: >=3.10
|
|
29
30
|
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
30
32
|
Requires-Dist: h5py
|
|
31
33
|
Requires-Dist: numpy
|
|
32
34
|
Requires-Dist: pyYAML
|
|
@@ -49,6 +51,7 @@ Requires-Dist: pytest; extra == "test"
|
|
|
49
51
|
Requires-Dist: coverage; extra == "test"
|
|
50
52
|
Provides-Extra: all
|
|
51
53
|
Requires-Dist: shepherd-core[dev,elf,inventory,test]; extra == "all"
|
|
54
|
+
Dynamic: license-file
|
|
52
55
|
|
|
53
56
|
# Core Library
|
|
54
57
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
"""Create a set of static
|
|
1
|
+
"""Create a set of static synthetic energy environments."""
|
|
2
2
|
|
|
3
3
|
from itertools import product
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
|
+
from shepherd_core.logger import log
|
|
7
8
|
from tqdm import trange
|
|
8
9
|
|
|
9
10
|
from shepherd_core import Reader as ShpReader
|
|
10
11
|
from shepherd_core import Writer as ShpWriter
|
|
11
|
-
from shepherd_core.logger import log
|
|
12
12
|
|
|
13
13
|
# Config
|
|
14
14
|
voltages_V = [4.0, 2.0]
|
|
@@ -29,7 +29,7 @@ for _v, _c in product(voltages_V, currents_A):
|
|
|
29
29
|
log.info("File exists, will skip: %s", file_path.name)
|
|
30
30
|
else:
|
|
31
31
|
with ShpWriter(file_path) as file:
|
|
32
|
-
file.store_hostname("
|
|
32
|
+
file.store_hostname("synthetic")
|
|
33
33
|
# values in SI units
|
|
34
34
|
timestamp_vector = np.arange(0.0, duration_s, file.sample_interval_ns / 1e9)
|
|
35
35
|
voltage_vector = np.linspace(_v, _v, int(file.samplerate_sps * duration_s))
|
|
@@ -16,10 +16,11 @@ What the code does:
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
18
|
import shepherd_core.data_models as sm
|
|
19
|
-
from shepherd_core import WebClient
|
|
20
19
|
from shepherd_core.data_models.task import TestbedTasks
|
|
21
20
|
from shepherd_core.data_models.testbed import MCU
|
|
22
21
|
|
|
22
|
+
from shepherd_core import WebClient
|
|
23
|
+
|
|
23
24
|
# For online-queries the lib can be connected to the testbed-server.
|
|
24
25
|
# NOTE: there are 3 states:
|
|
25
26
|
# - unconnected -> demo-fixtures are queried (locally)
|
|
@@ -29,20 +30,20 @@ do_connect = False
|
|
|
29
30
|
if do_connect:
|
|
30
31
|
WebClient()
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
exp = sm.Experiment(
|
|
33
34
|
name="meaningful_TestName",
|
|
34
35
|
# time_start could be "2033-03-13 14:15:16" or "datetime.now() + timedelta(minutes=30)"
|
|
35
|
-
duration=30,
|
|
36
|
+
duration=30, # ty: ignore[invalid-argument-type]
|
|
36
37
|
target_configs=[
|
|
37
38
|
sm.TargetConfig(
|
|
38
39
|
target_IDs=range(7, 12),
|
|
39
40
|
custom_IDs=range(1, 100), # note: longer list is OK
|
|
40
|
-
energy_env=sm.EnergyEnvironment(name="
|
|
41
|
+
energy_env=sm.EnergyEnvironment(name="synthetic_static_3000mV_50mA"),
|
|
41
42
|
firmware1=sm.Firmware(
|
|
42
43
|
name="FW_TestXYZ",
|
|
43
44
|
data=Path("/var/shepherd/content/fw/nes_lab/nrf52_demo_rf/build.elf"),
|
|
44
45
|
data_type=sm.FirmwareDType.path_elf,
|
|
45
|
-
|
|
46
|
+
data_2_copy=False,
|
|
46
47
|
mcu=MCU(name="nRF52"),
|
|
47
48
|
),
|
|
48
49
|
power_tracing=None,
|
|
@@ -51,10 +52,10 @@ xp = sm.Experiment(
|
|
|
51
52
|
),
|
|
52
53
|
],
|
|
53
54
|
)
|
|
54
|
-
|
|
55
|
+
exp.to_file("experiment_generic_var1.yaml")
|
|
55
56
|
|
|
56
57
|
# Create a tasks-list for the testbed
|
|
57
|
-
tb_tasks = TestbedTasks.from_xp(
|
|
58
|
+
tb_tasks = TestbedTasks.from_xp(exp)
|
|
58
59
|
tb_tasks.to_file("experiment_generic_var1_tbt.yaml")
|
|
59
60
|
|
|
60
61
|
# next steps:
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
17
|
import shepherd_core.data_models as sm
|
|
18
|
-
from shepherd_core import WebClient
|
|
19
18
|
from shepherd_core.data_models.task import TestbedTasks
|
|
20
19
|
|
|
20
|
+
from shepherd_core import WebClient
|
|
21
|
+
|
|
21
22
|
# For online-queries the lib can be connected to the testbed-server.
|
|
22
23
|
# NOTE: there are 3 states:
|
|
23
24
|
# - unconnected -> demo-fixtures are queried (locally)
|
|
@@ -27,7 +28,7 @@ do_connect = False
|
|
|
27
28
|
if do_connect:
|
|
28
29
|
WebClient()
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
exp = sm.Experiment(
|
|
31
32
|
name="meaningful_TestName",
|
|
32
33
|
# time_start could be "2033-03-13 14:15:16" or "datetime.now() + timedelta(minutes=30)"
|
|
33
34
|
duration=30,
|
|
@@ -35,7 +36,7 @@ xp = sm.Experiment(
|
|
|
35
36
|
sm.TargetConfig(
|
|
36
37
|
target_IDs=range(7, 12),
|
|
37
38
|
custom_IDs=range(1, 100), # note: longer list is OK
|
|
38
|
-
energy_env=sm.EnergyEnvironment(name="
|
|
39
|
+
energy_env=sm.EnergyEnvironment(name="synthetic_static_3000mV_50mA"),
|
|
39
40
|
firmware1=sm.Firmware.from_firmware(
|
|
40
41
|
file=Path("./firmware_nrf.elf").absolute(),
|
|
41
42
|
),
|
|
@@ -44,10 +45,10 @@ xp = sm.Experiment(
|
|
|
44
45
|
),
|
|
45
46
|
],
|
|
46
47
|
)
|
|
47
|
-
|
|
48
|
+
exp.to_file("experiment_generic_var2.yaml")
|
|
48
49
|
|
|
49
50
|
# Create a tasks-list for the testbed
|
|
50
|
-
tb_tasks = TestbedTasks.from_xp(
|
|
51
|
+
tb_tasks = TestbedTasks.from_xp(exp)
|
|
51
52
|
tb_tasks.to_file("experiment_generic_var2_tbt.yaml")
|
|
52
53
|
|
|
53
54
|
# next steps:
|
|
@@ -19,9 +19,10 @@ How to define an experiment:
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import shepherd_core.data_models as sm
|
|
22
|
-
from shepherd_core import WebClient
|
|
23
22
|
from shepherd_core.data_models.task import TestbedTasks
|
|
24
23
|
|
|
24
|
+
from shepherd_core import WebClient
|
|
25
|
+
|
|
25
26
|
# generate description for all parameters -> base for web-forms
|
|
26
27
|
sm.Experiment.schema_to_file("experiment_schema.yaml")
|
|
27
28
|
|
|
@@ -6,11 +6,12 @@ or shepherd-core[elf].
|
|
|
6
6
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
-
from shepherd_core import WebClient
|
|
10
|
-
from shepherd_core import fw_tools
|
|
11
9
|
from shepherd_core.data_models import Firmware
|
|
12
10
|
from shepherd_core.data_models import FirmwareDType
|
|
13
11
|
|
|
12
|
+
from shepherd_core import WebClient
|
|
13
|
+
from shepherd_core import fw_tools
|
|
14
|
+
|
|
14
15
|
path_elf = Path(__file__).parent.parent / "tests/fw_tools/build_msp.elf"
|
|
15
16
|
|
|
16
17
|
# Option 1 - fully manual
|
|
@@ -8,10 +8,11 @@ Note: make sure to have installed
|
|
|
8
8
|
import shutil
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
|
-
from shepherd_core import fw_tools
|
|
12
11
|
from shepherd_core.data_models import Firmware
|
|
13
12
|
from shepherd_core.data_models import FirmwareDType
|
|
14
13
|
|
|
14
|
+
from shepherd_core import fw_tools
|
|
15
|
+
|
|
15
16
|
path_src = Path(__file__).parent.parent / "tests/fw_tools/build_msp.elf"
|
|
16
17
|
path_elf = Path(__file__).with_name("firmware_msp.elf")
|
|
17
18
|
|
|
@@ -6,26 +6,37 @@
|
|
|
6
6
|
|
|
7
7
|
Output:
|
|
8
8
|
E_out = 0.000 mWs -> cv20
|
|
9
|
-
E_out = 17.
|
|
10
|
-
E_out = 17.
|
|
11
|
-
E_out = 17.
|
|
12
|
-
E_out = 13.
|
|
13
|
-
E_out = 15.
|
|
9
|
+
E_out = 17.143 mWs -> cv10
|
|
10
|
+
E_out = 17.384 mWs -> mppt_voc
|
|
11
|
+
E_out = 17.249 mWs -> mppt_bq_solar
|
|
12
|
+
E_out = 13.954 mWs -> mppt_bq_thermoelectric
|
|
13
|
+
E_out = 15.001 mWs -> mppt_po
|
|
14
14
|
E_out = 17.811 mWs -> mppt_opt
|
|
15
|
+
|
|
15
16
|
"""
|
|
16
17
|
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
17
20
|
from pathlib import Path
|
|
18
21
|
|
|
19
|
-
from shepherd_core import Reader
|
|
20
22
|
from shepherd_core.data_models import VirtualHarvesterConfig
|
|
21
23
|
from shepherd_core.vsource import simulate_harvester
|
|
24
|
+
|
|
25
|
+
from shepherd_core import Reader
|
|
22
26
|
from shepherd_data import ivonne
|
|
23
27
|
|
|
28
|
+
DURATION_MAX = 1 if "PYTEST_CURRENT_TEST" in os.environ else sys.float_info.max
|
|
29
|
+
# ⤷ limits runtime for pytest
|
|
30
|
+
|
|
24
31
|
# config simulation
|
|
25
|
-
sim_duration = 32
|
|
26
|
-
|
|
32
|
+
sim_duration = min(32, DURATION_MAX)
|
|
33
|
+
# ⤷ limits runtime for pytest
|
|
34
|
+
file_ivonne = Path(__file__).parents[3] / "shepherd_data/examples/jogging_10m.iv"
|
|
27
35
|
file_ivcurve = Path(__file__).parent / "jogging_ivcurve.h5"
|
|
28
36
|
|
|
37
|
+
if not file_ivonne.exists():
|
|
38
|
+
raise FileNotFoundError("Input-File not found - check path")
|
|
39
|
+
|
|
29
40
|
hrv_list = [
|
|
30
41
|
"cv20",
|
|
31
42
|
# ⤷ fails due to lower solar voltage
|
|
@@ -39,7 +50,7 @@ hrv_list = [
|
|
|
39
50
|
"mppt_opt",
|
|
40
51
|
]
|
|
41
52
|
|
|
42
|
-
save_files: bool =
|
|
53
|
+
save_files: bool = True
|
|
43
54
|
|
|
44
55
|
# convert IVonne to IVCurve
|
|
45
56
|
if not file_ivcurve.exists():
|
|
@@ -10,20 +10,22 @@ The output file can be analyzed and plotted with shepherds tool suite.
|
|
|
10
10
|
|
|
11
11
|
Output:
|
|
12
12
|
E_out = 220.001 mWs -> direct (no current-limit)
|
|
13
|
-
E_out =
|
|
14
|
-
E_out =
|
|
15
|
-
E_out =
|
|
16
|
-
E_out =
|
|
17
|
-
E_out =
|
|
18
|
-
E_out =
|
|
13
|
+
E_out = 14.670 mWs -> diode+capacitor
|
|
14
|
+
E_out = 14.563 mWs -> diode+resistor+capacitor
|
|
15
|
+
E_out = 16.718 mWs -> BQ25504
|
|
16
|
+
E_out = 16.805 mWs -> BQ25504s
|
|
17
|
+
E_out = 16.369 mWs -> BQ25570
|
|
18
|
+
E_out = 16.387 mWs -> BQ25570s
|
|
19
19
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
|
|
24
24
|
from shepherd_core.data_models import VirtualSourceConfig
|
|
25
|
+
from shepherd_core.data_models import VirtualStorageConfig
|
|
25
26
|
from shepherd_core.vsource import ResistiveTarget
|
|
26
27
|
from shepherd_core.vsource import simulate_source
|
|
28
|
+
|
|
27
29
|
from shepherd_data import Reader
|
|
28
30
|
|
|
29
31
|
# config simulation
|
|
@@ -41,6 +43,11 @@ src_list = [
|
|
|
41
43
|
tgt = ResistiveTarget(R_Ohm=1_000, controlled=True)
|
|
42
44
|
save_files = True
|
|
43
45
|
|
|
46
|
+
if not file_input.exists():
|
|
47
|
+
raise FileNotFoundError(
|
|
48
|
+
"Input-File not found - please run harvester-simulation first to create it."
|
|
49
|
+
)
|
|
50
|
+
|
|
44
51
|
for src_name in src_list:
|
|
45
52
|
file_output = file_input.with_stem(file_input.stem + "_emu_" + src_name) if save_files else None
|
|
46
53
|
|
|
@@ -48,10 +55,12 @@ for src_name in src_list:
|
|
|
48
55
|
config=VirtualSourceConfig(
|
|
49
56
|
name=src_name,
|
|
50
57
|
C_output_uF=0,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# jogging-dataset has
|
|
54
|
-
|
|
58
|
+
V_intermediate_enable_output_threshold_mV=1,
|
|
59
|
+
V_intermediate_disable_output_threshold_mV=0,
|
|
60
|
+
# jogging-dataset has maximum VOC of ~1.6 V -> lower set-point for non-boost
|
|
61
|
+
storage=VirtualStorageConfig.capacitor(C_uF=100, V_rated=10.0)
|
|
62
|
+
if "direct" not in src_name
|
|
63
|
+
else None,
|
|
55
64
|
V_pwr_good_enable_threshold_mV=1300 if "dio" in src_name else 2800,
|
|
56
65
|
V_pwr_good_disable_threshold_mV=1000 if "dio" in src_name else 2400,
|
|
57
66
|
V_input_drop_mV=150 if "dio" in src_name else 0,
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""A set of experiments to validate and qualify the virtual storage algorithms.
|
|
2
|
+
|
|
3
|
+
Some general Notes:
|
|
4
|
+
|
|
5
|
+
- ShpCap is also displayed when emulating Lipo and lead-acid, but it can't and won't behave similar
|
|
6
|
+
- during charging the model KiBaM-Plus will deviate from normal KiBaM and KiBaM-Simple,
|
|
7
|
+
as it supports the rate capacity effect and transients (during charging)
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import multiprocessing
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
from datetime import timedelta
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from pydantic import PositiveFloat
|
|
18
|
+
from pydantic import validate_call
|
|
19
|
+
from shepherd_core.data_models.content.virtual_storage_config import VirtualStorageConfig
|
|
20
|
+
from shepherd_core.data_models.content.virtual_storage_config import soc_t
|
|
21
|
+
from shepherd_core.vsource.virtual_storage_model import ModelStorage
|
|
22
|
+
from shepherd_core.vsource.virtual_storage_model import VirtualStorageModel
|
|
23
|
+
from shepherd_core.vsource.virtual_storage_models_kibam import ModelKiBaM
|
|
24
|
+
from shepherd_core.vsource.virtual_storage_models_kibam import ModelKiBaMPlus
|
|
25
|
+
from shepherd_core.vsource.virtual_storage_models_kibam import ModelKiBaMSimple
|
|
26
|
+
from shepherd_core.vsource.virtual_storage_models_kibam import ModelShpCap
|
|
27
|
+
from shepherd_core.vsource.virtual_storage_simulator import StorageSimulator
|
|
28
|
+
|
|
29
|
+
from shepherd_core import log
|
|
30
|
+
|
|
31
|
+
path_here = Path(__file__).parent
|
|
32
|
+
|
|
33
|
+
DURATION_MAX = 1 if "PYTEST_CURRENT_TEST" in os.environ else sys.float_info.max
|
|
34
|
+
# ⤷ limits runtime for pytest
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@validate_call
|
|
38
|
+
def get_models(
|
|
39
|
+
SoC_init: soc_t, config: VirtualStorageConfig, dt_s: PositiveFloat
|
|
40
|
+
) -> list[ModelStorage]:
|
|
41
|
+
"""Models to include in experiments."""
|
|
42
|
+
return [
|
|
43
|
+
ModelKiBaM(SoC_init=SoC_init, cfg=config, dt_s=dt_s),
|
|
44
|
+
ModelKiBaMPlus(SoC_init=SoC_init, cfg=config, dt_s=dt_s),
|
|
45
|
+
ModelKiBaMSimple(SoC_init=SoC_init, cfg=config, dt_s=dt_s, optimize_clamp=True),
|
|
46
|
+
ModelKiBaMSimple(SoC_init=SoC_init, cfg=config, dt_s=dt_s, interpolate=True),
|
|
47
|
+
VirtualStorageModel(SoC_init=SoC_init, cfg=config, dt_s=dt_s),
|
|
48
|
+
ModelShpCap(SoC_init=SoC_init, cfg=config, dt_s=dt_s),
|
|
49
|
+
][1:5]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CurrentPulsed:
|
|
53
|
+
"""A simple constant current source that is pulsed until a target SoC is reached."""
|
|
54
|
+
|
|
55
|
+
@validate_call
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
I_pulse: float,
|
|
59
|
+
period_pulse: PositiveFloat,
|
|
60
|
+
duration_pulse: PositiveFloat,
|
|
61
|
+
SoC_target: soc_t,
|
|
62
|
+
) -> None:
|
|
63
|
+
self.I_pulse = I_pulse
|
|
64
|
+
self.period_pulse = period_pulse
|
|
65
|
+
self.duration_pulse = duration_pulse
|
|
66
|
+
self.SoC_target = SoC_target
|
|
67
|
+
|
|
68
|
+
def step(self, t_s: float, SoC: float, _v: float) -> float:
|
|
69
|
+
if (self.I_pulse < 0 and SoC <= self.SoC_target) or (
|
|
70
|
+
self.I_pulse > 0 and SoC >= self.SoC_target
|
|
71
|
+
):
|
|
72
|
+
return 0
|
|
73
|
+
return self.I_pulse if t_s % self.period_pulse < self.duration_pulse else 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ResistiveChargePulsed:
|
|
77
|
+
"""A pulsed charger that is 'current limited' by a resistor."""
|
|
78
|
+
|
|
79
|
+
@validate_call
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
V_target: PositiveFloat,
|
|
83
|
+
R_Ohm: PositiveFloat,
|
|
84
|
+
period_pulse: PositiveFloat,
|
|
85
|
+
duration_pulse: PositiveFloat,
|
|
86
|
+
) -> None:
|
|
87
|
+
self.R_Ohm = R_Ohm
|
|
88
|
+
self.V_target = V_target
|
|
89
|
+
self.period_pulse = period_pulse
|
|
90
|
+
self.duration_pulse = duration_pulse
|
|
91
|
+
|
|
92
|
+
def step(self, t_s: float, _s: float, V: float) -> float:
|
|
93
|
+
I_A = (self.V_target - V) / self.R_Ohm
|
|
94
|
+
return I_A if t_s % self.period_pulse < self.duration_pulse else 0
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def experiment_current_ramp_pos(config: VirtualStorageConfig) -> None:
|
|
98
|
+
"""Charge virtual storage with a positive current ramp (increasing power)."""
|
|
99
|
+
dt_s = 0.1
|
|
100
|
+
SoC_start = 0.5
|
|
101
|
+
duration_s = min(200, DURATION_MAX)
|
|
102
|
+
sim = StorageSimulator(
|
|
103
|
+
models=get_models(SoC_start, config, dt_s),
|
|
104
|
+
dt_s=dt_s,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def current_trace(t_s: float, _s: float, _v: float) -> float:
|
|
108
|
+
return 0.1 + 0.15 * t_s / duration_s # pru-model can handle +- 268 mA
|
|
109
|
+
|
|
110
|
+
sim.run(fn=current_trace, duration_s=duration_s)
|
|
111
|
+
sim.plot(path_here, f"Experiment {config.name}, current charge ramp (positive)")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def experiment_current_ramp_neg(config: VirtualStorageConfig) -> None:
|
|
115
|
+
"""Discharge virtual storage with a negative current ramp (increasing power)."""
|
|
116
|
+
dt_s = 0.1
|
|
117
|
+
SoC_start = 0.5
|
|
118
|
+
duration_s = min(200, DURATION_MAX)
|
|
119
|
+
sim = StorageSimulator(
|
|
120
|
+
models=get_models(SoC_start, config, dt_s),
|
|
121
|
+
dt_s=dt_s,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def current_trace(t_s: float, _s: float, _v: float) -> float:
|
|
125
|
+
return -(0.1 + 0.14 * t_s / duration_s) # pru-model can handle +- 268 mA
|
|
126
|
+
|
|
127
|
+
sim.run(fn=current_trace, duration_s=duration_s)
|
|
128
|
+
sim.plot(path_here, f"Experiment {config.name}, current discharge ramp (negative)")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def experiment_pulsed_discharge(config: VirtualStorageConfig) -> None:
|
|
132
|
+
"""Discharge virtual storage with a pulsed constant current."""
|
|
133
|
+
dt_s = 0.2
|
|
134
|
+
SoC_start = 1.0
|
|
135
|
+
SoC_target = 0.0
|
|
136
|
+
i_pulse = CurrentPulsed(
|
|
137
|
+
I_pulse=-0.1, period_pulse=200, duration_pulse=100, SoC_target=SoC_target
|
|
138
|
+
) # pru-model can handle +- 268 mA
|
|
139
|
+
sim = StorageSimulator(
|
|
140
|
+
models=get_models(SoC_start, config, dt_s),
|
|
141
|
+
dt_s=dt_s,
|
|
142
|
+
)
|
|
143
|
+
sim.run(fn=i_pulse.step, duration_s=min(1_000, DURATION_MAX))
|
|
144
|
+
sim.plot(path_here, f"Experiment {config.name}, pulsed discharge .1A, 1000 s (figure_9a)")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def experiment_pulsed_charge(config: VirtualStorageConfig) -> None:
|
|
148
|
+
"""Charge virtual storage with a pulsed constant current."""
|
|
149
|
+
dt_s = 0.2
|
|
150
|
+
SoC_start = 0.0
|
|
151
|
+
SoC_target = 1.0
|
|
152
|
+
i_pulse = CurrentPulsed(
|
|
153
|
+
I_pulse=0.1, period_pulse=200, duration_pulse=100, SoC_target=SoC_target
|
|
154
|
+
) # pru-model can handle +- 268 mA
|
|
155
|
+
sim = StorageSimulator(
|
|
156
|
+
models=get_models(SoC_start, config, dt_s),
|
|
157
|
+
dt_s=dt_s,
|
|
158
|
+
)
|
|
159
|
+
sim.run(fn=i_pulse.step, duration_s=min(1_000, DURATION_MAX))
|
|
160
|
+
sim.plot(path_here, f"Experiment {config.name}, pulsed charge .1A, 1000 s (figure_9b)")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def experiment_pulsed_resistive_charge(config: VirtualStorageConfig) -> None:
|
|
164
|
+
"""Charge virtual storage with a resistive constant voltage."""
|
|
165
|
+
dt_s = 0.5
|
|
166
|
+
SoC_start = 0.0
|
|
167
|
+
i_pulse = ResistiveChargePulsed(R_Ohm=20, V_target=4.2, period_pulse=200, duration_pulse=100)
|
|
168
|
+
sim = StorageSimulator(
|
|
169
|
+
models=get_models(SoC_start, config, dt_s),
|
|
170
|
+
dt_s=dt_s,
|
|
171
|
+
)
|
|
172
|
+
sim.run(fn=i_pulse.step, duration_s=min(3_000, DURATION_MAX))
|
|
173
|
+
sim.plot(
|
|
174
|
+
path_here, f"Experiment {config.name}, pulsed resistive charge 20 Ohm to 4.2 V, 3000 s"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def experiment_resistive_load(config: VirtualStorageConfig) -> None:
|
|
179
|
+
"""Charge virtual storage with a resistive constant voltage."""
|
|
180
|
+
dt_s = 0.5
|
|
181
|
+
SoC_start = 1.0
|
|
182
|
+
|
|
183
|
+
def i_charge(_t_s: float, _s: float, V: float) -> float:
|
|
184
|
+
return -V / 20
|
|
185
|
+
|
|
186
|
+
sim = StorageSimulator(
|
|
187
|
+
models=get_models(SoC_start, config, dt_s),
|
|
188
|
+
dt_s=dt_s,
|
|
189
|
+
)
|
|
190
|
+
sim.run(fn=i_charge, duration_s=min(1_000, DURATION_MAX))
|
|
191
|
+
sim.plot(path_here, f"Experiment {config.name}, resistive load 20 Ohm from 4.2 V, 1000 s")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def experiment_self_discharge() -> None:
|
|
195
|
+
"""Observe self-discharge behavior of virtual storage models."""
|
|
196
|
+
dt_s = 0.2
|
|
197
|
+
SoC_start = 1.0
|
|
198
|
+
SoC_target = 0.9
|
|
199
|
+
duration = timedelta(minutes=25)
|
|
200
|
+
store = VirtualStorageConfig.capacitor(C_uF=100, V_rated=6.3)
|
|
201
|
+
R_leak = store.calc_R_leak_capacitor(duration=duration, SoC_final=SoC_target, SoC_0=SoC_start)
|
|
202
|
+
log.info("R_leak = %.2f Ohm", R_leak)
|
|
203
|
+
config = VirtualStorageConfig.capacitor(C_uF=100, V_rated=6.3, R_leak_Ohm=R_leak)
|
|
204
|
+
sim = StorageSimulator(
|
|
205
|
+
models=get_models(SoC_start, config, dt_s),
|
|
206
|
+
dt_s=dt_s,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def step(_t: float, _s: float, _v: float) -> float:
|
|
210
|
+
return 0
|
|
211
|
+
|
|
212
|
+
sim.run(fn=step, duration_s=min(duration.total_seconds(), DURATION_MAX))
|
|
213
|
+
sim.plot(
|
|
214
|
+
path_here,
|
|
215
|
+
f"Experiment {config.name}, self-discharge, "
|
|
216
|
+
f"SoC {SoC_start} to {SoC_target} in {duration.total_seconds()} s",
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == "__main__":
|
|
221
|
+
with multiprocessing.Pool() as pool:
|
|
222
|
+
pool.apply_async(experiment_self_discharge)
|
|
223
|
+
|
|
224
|
+
configs = [
|
|
225
|
+
VirtualStorageConfig.capacitor(C_uF=10e6, V_rated=4.2), # match charge with batteries
|
|
226
|
+
VirtualStorageConfig.lipo(q_mAh=10),
|
|
227
|
+
VirtualStorageConfig.lead_acid(q_mAh=10),
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
for cfg in configs:
|
|
231
|
+
pool.apply_async(experiment_pulsed_charge, args=(cfg,))
|
|
232
|
+
pool.apply_async(experiment_pulsed_discharge, args=(cfg,))
|
|
233
|
+
pool.apply_async(experiment_current_ramp_pos, args=(cfg,))
|
|
234
|
+
pool.apply_async(experiment_current_ramp_neg, args=(cfg,))
|
|
235
|
+
|
|
236
|
+
for cfg in configs[0:2]:
|
|
237
|
+
pool.apply_async(experiment_pulsed_resistive_charge, args=(cfg,))
|
|
238
|
+
pool.apply_async(experiment_resistive_load, args=(cfg,))
|
|
239
|
+
|
|
240
|
+
pool.close()
|
|
241
|
+
pool.join()
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
import os
|
|
13
14
|
from itertools import product
|
|
14
15
|
|
|
15
16
|
import matplotlib.pyplot as plt
|
|
16
17
|
import numpy as np
|
|
18
|
+
from shepherd_core.data_models import VirtualSourceConfig
|
|
19
|
+
from shepherd_core.vsource import VirtualSourceModel
|
|
17
20
|
|
|
18
21
|
from shepherd_core import CalibrationEmulator
|
|
19
22
|
from shepherd_core import WebClient
|
|
20
|
-
from shepherd_core.data_models import VirtualSourceConfig
|
|
21
|
-
from shepherd_core.vsource import VirtualSourceModel
|
|
22
23
|
|
|
23
24
|
# config simulation
|
|
24
25
|
sample_dur_list = [50_000, 500_000]
|
|
@@ -38,6 +39,10 @@ src_list = [
|
|
|
38
39
|
I_mcu_sleep_A = 200e-9
|
|
39
40
|
I_mcu_active_A = 1e-3
|
|
40
41
|
|
|
42
|
+
# limit runtime for pytest
|
|
43
|
+
if "PYTEST_CURRENT_TEST" in os.environ:
|
|
44
|
+
sample_dur_list = [10_000]
|
|
45
|
+
|
|
41
46
|
# For online-queries the lib can be connected to the testbed-server.
|
|
42
47
|
# NOTE: there are 3 states:
|
|
43
48
|
# - unconnected -> demo-fixtures are queried (locally)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
|
-
name = "
|
|
2
|
+
name = "shepherd-core"
|
|
3
3
|
description = "Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed"
|
|
4
4
|
keywords = ["testbed", "beaglebone", "pru", "batteryless", "energyharvesting", "solar"]
|
|
5
5
|
|
|
6
6
|
authors = [{name = "Ingmar Splitt", email = "ingmar.splitt@tu-dresden.de"},]
|
|
7
7
|
maintainers = [{name = "Ingmar Splitt", email = "ingmar.splitt@tu-dresden.de"},]
|
|
8
8
|
|
|
9
|
-
license =
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICENSE"] # can't be in parent dir
|
|
10
11
|
dynamic = ["version"]
|
|
11
12
|
|
|
12
13
|
classifiers = [
|
|
@@ -18,7 +19,7 @@ classifiers = [
|
|
|
18
19
|
"Programming Language :: Python :: 3.11",
|
|
19
20
|
"Programming Language :: Python :: 3.12",
|
|
20
21
|
"Programming Language :: Python :: 3.13",
|
|
21
|
-
"
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
22
23
|
"Operating System :: OS Independent",
|
|
23
24
|
"Natural Language :: English",
|
|
24
25
|
]
|
|
@@ -103,12 +104,3 @@ addopts = "-vvv --stepwise" # opts: verbose result for each tests
|
|
|
103
104
|
[tool.coverage.run]
|
|
104
105
|
source = ["shepherd_core"]
|
|
105
106
|
omit = ["*/shepherd_data/*"]
|
|
106
|
-
|
|
107
|
-
[tool.mypy]
|
|
108
|
-
python_version = 3.10
|
|
109
|
-
ignore_missing_imports = true
|
|
110
|
-
disable_error_code = ["call-arg", ]
|
|
111
|
-
exclude = [
|
|
112
|
-
"build/",
|
|
113
|
-
".egg-info/",
|
|
114
|
-
]
|
|
@@ -27,7 +27,7 @@ class ConfigDefault(BaseModel):
|
|
|
27
27
|
UID_SIZE: int = 2
|
|
28
28
|
"""Variable size in Byte"""
|
|
29
29
|
|
|
30
|
-
TESTBED_SERVER: HttpUrl = "https://shepherd.cfaed.tu-dresden.de:8000/"
|
|
30
|
+
TESTBED_SERVER: HttpUrl = HttpUrl("https://shepherd.cfaed.tu-dresden.de:8000/")
|
|
31
31
|
"""Server that holds up to date testbed fixtures"""
|
|
32
32
|
|
|
33
33
|
|