shepherd-core 2025.8.1__tar.gz → 2025.10.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.
Files changed (164) hide show
  1. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/PKG-INFO +2 -1
  2. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/experiment_generic_var1.py +2 -1
  3. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/experiment_generic_var2.py +2 -1
  4. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/experiment_models.py +2 -1
  5. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/firmware_model.py +3 -2
  6. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/firmware_modification.py +2 -1
  7. shepherd_core-2025.8.1/examples/simulate_vharvester.py → shepherd_core-2025.10.1/examples/simulations/vharvester.py +13 -8
  8. shepherd_core-2025.8.1/examples/simulate_vsource.py → shepherd_core-2025.10.1/examples/simulations/vsource.py +19 -10
  9. shepherd_core-2025.10.1/examples/simulations/vstorage.py +239 -0
  10. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/pyproject.toml +1 -0
  11. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/__init__.py +4 -2
  12. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/base/content.py +2 -0
  13. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/__init__.py +4 -2
  14. shepherd_core-2025.8.1/shepherd_core/data_models/content/virtual_harvester.py → shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_harvester_config.py +3 -3
  15. shepherd_core-2025.8.1/shepherd_core/data_models/content/virtual_source.py → shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_source_config.py +82 -58
  16. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/virtual_source_fixture.yaml +24 -24
  17. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_config.py +426 -0
  18. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_creator.py +267 -0
  19. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_ideal.yaml +637 -0
  20. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_lead.yaml +49 -0
  21. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_lipo.yaml +735 -0
  22. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_mlcc.yaml +200 -0
  23. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_param_experiments.py +151 -0
  24. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_super.yaml +150 -0
  25. shepherd_core-2025.10.1/shepherd_core/data_models/content/virtual_storage_fixture_tantal.yaml +550 -0
  26. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/experiment/target_config.py +1 -1
  27. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/emulation.py +1 -1
  28. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/harvest.py +1 -1
  29. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/decoder_waveform/uart.py +1 -1
  30. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/inventory/system.py +1 -1
  31. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/reader.py +4 -3
  32. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/version.py +1 -1
  33. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/vsource/__init__.py +4 -0
  34. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/vsource/virtual_converter_model.py +27 -26
  35. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/vsource/virtual_harvester_model.py +27 -19
  36. shepherd_core-2025.10.1/shepherd_core/vsource/virtual_harvester_simulation.py +71 -0
  37. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/vsource/virtual_source_model.py +17 -13
  38. shepherd_core-2025.10.1/shepherd_core/vsource/virtual_source_simulation.py +143 -0
  39. shepherd_core-2025.10.1/shepherd_core/vsource/virtual_storage_model.py +164 -0
  40. shepherd_core-2025.10.1/shepherd_core/vsource/virtual_storage_model_fixed_point_math.py +58 -0
  41. shepherd_core-2025.10.1/shepherd_core/vsource/virtual_storage_models_kibam.py +449 -0
  42. shepherd_core-2025.10.1/shepherd_core/vsource/virtual_storage_simulator.py +104 -0
  43. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core.egg-info/PKG-INFO +2 -1
  44. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core.egg-info/SOURCES.txt +18 -5
  45. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_config_emulator.yaml +2 -1
  46. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_config_virtsource.yaml +9 -8
  47. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_base_models.py +0 -1
  48. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_content_fixtures.py +4 -4
  49. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_content_models.py +9 -8
  50. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_task_generation.py +2 -1
  51. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_task_models.py +0 -1
  52. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_testbed_models.py +0 -1
  53. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/decoder_waveform/test_decoder.py +0 -1
  54. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/inventory/test_inventory.py +0 -1
  55. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/test_cal_hw.py +0 -1
  56. shepherd_core-2025.10.1/tests/test_examples.py +53 -0
  57. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/vsource/test_converter.py +11 -14
  58. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/vsource/test_harvester.py +3 -3
  59. shepherd_core-2025.8.1/shepherd_core/data_models/virtual_source_doc.txt +0 -207
  60. shepherd_core-2025.8.1/shepherd_core/vsource/virtual_harvester_simulation.py +0 -72
  61. shepherd_core-2025.8.1/shepherd_core/vsource/virtual_source_simulation.py +0 -145
  62. shepherd_core-2025.8.1/tests/test_examples.py +0 -42
  63. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/README.md +0 -0
  64. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/eenv_generator.py +1 -1
  65. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/experiment_from_yaml.yaml +0 -0
  66. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/inventory.py +0 -0
  67. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/uart_decode_waveform.py +0 -0
  68. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/uart_raw2.csv +0 -0
  69. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/examples/vsource_debug_sim.py +2 -2
  70. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/setup.cfg +0 -0
  71. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/__init__.py +0 -0
  72. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/calibration_hw_def.py +0 -0
  73. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/commons.py +0 -0
  74. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/config.py +0 -0
  75. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/base/__init__.py +0 -0
  76. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/base/cal_measurement.py +0 -0
  77. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/base/calibration.py +0 -0
  78. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/base/shepherd.py +0 -0
  79. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/base/timezone.py +0 -0
  80. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/base/wrapper.py +0 -0
  81. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/_external_fixtures.yaml +0 -0
  82. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/energy_environment.py +0 -0
  83. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/energy_environment_fixture.yaml +0 -0
  84. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/firmware.py +0 -0
  85. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/firmware_datatype.py +0 -0
  86. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/content/virtual_harvester_fixture.yaml +0 -0
  87. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/experiment/__init__.py +0 -0
  88. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/experiment/experiment.py +0 -0
  89. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/experiment/observer_features.py +0 -0
  90. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/readme.md +0 -0
  91. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/__init__.py +0 -0
  92. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/firmware_mod.py +0 -0
  93. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/helper_paths.py +0 -0
  94. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/observer_tasks.py +0 -0
  95. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/programming.py +0 -0
  96. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/task/testbed_tasks.py +0 -0
  97. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/__init__.py +0 -0
  98. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/cape.py +0 -0
  99. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/cape_fixture.yaml +0 -0
  100. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/gpio.py +0 -0
  101. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/gpio_fixture.yaml +0 -0
  102. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/mcu.py +0 -0
  103. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/mcu_fixture.yaml +0 -0
  104. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/observer.py +0 -0
  105. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/observer_fixture.yaml +0 -0
  106. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/target.py +0 -0
  107. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/target_fixture.old1 +0 -0
  108. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/target_fixture.yaml +0 -0
  109. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/testbed.py +0 -0
  110. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/data_models/testbed/testbed_fixture.yaml +0 -0
  111. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/decoder_waveform/__init__.py +0 -0
  112. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/fw_tools/__init__.py +0 -0
  113. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/fw_tools/converter.py +0 -0
  114. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/fw_tools/converter_elf.py +0 -0
  115. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/fw_tools/patcher.py +0 -0
  116. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/fw_tools/validation.py +0 -0
  117. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/inventory/__init__.py +0 -0
  118. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/inventory/python.py +0 -0
  119. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/inventory/target.py +0 -0
  120. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/logger.py +0 -0
  121. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/testbed_client/__init__.py +0 -0
  122. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/testbed_client/cache_path.py +0 -0
  123. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/testbed_client/client_abc_fix.py +0 -0
  124. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/testbed_client/client_web.py +0 -0
  125. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/testbed_client/fixtures.py +0 -0
  126. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/testbed_client/user_model.py +0 -0
  127. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/vsource/target_model.py +0 -0
  128. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core/writer.py +0 -0
  129. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core.egg-info/dependency_links.txt +0 -0
  130. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core.egg-info/requires.txt +0 -0
  131. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core.egg-info/top_level.txt +0 -0
  132. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/shepherd_core.egg-info/zip-safe +0 -0
  133. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/__init__.py +0 -0
  134. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/conftest.py +0 -0
  135. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/__init__.py +0 -0
  136. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/conftest.py +0 -0
  137. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_cal_data.yaml +0 -0
  138. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_cal_data_faulty.yaml +0 -0
  139. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_cal_meas.yaml +0 -0
  140. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_cal_meas_faulty1.yaml +0 -0
  141. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_cal_meas_faulty2.yaml +0 -0
  142. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_config_experiment.yaml +0 -0
  143. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_config_experiment_alternative.yaml +0 -0
  144. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_config_harvester.yaml +0 -0
  145. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/example_config_testbed.yaml +0 -0
  146. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_examples.py +0 -0
  147. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_experiment_models.py +2 -2
  148. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/data_models/test_testbed_fixtures.py +0 -0
  149. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/decoder_waveform/__init__.py +0 -0
  150. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/fw_tools/__init__.py +0 -0
  151. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/fw_tools/build_msp.elf +0 -0
  152. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/fw_tools/build_nrf.elf +0 -0
  153. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/fw_tools/conftest.py +0 -0
  154. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/fw_tools/test_converter.py +0 -0
  155. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/fw_tools/test_patcher.py +0 -0
  156. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/fw_tools/test_validation.py +1 -1
  157. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/inventory/__init__.py +0 -0
  158. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/test_logger.py +0 -0
  159. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/test_reader.py +0 -0
  160. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/test_writer.py +4 -4
  161. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/testbed_client/__init__.py +0 -0
  162. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/vsource/__init__.py +0 -0
  163. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/vsource/conftest.py +0 -0
  164. {shepherd_core-2025.8.1 → shepherd_core-2025.10.1}/tests/vsource/test_z.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shepherd_core
3
- Version: 2025.8.1
3
+ Version: 2025.10.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>
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
25
26
  Classifier: License :: OSI Approved :: MIT License
26
27
  Classifier: Operating System :: OS Independent
27
28
  Classifier: Natural Language :: English
@@ -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)
@@ -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)
@@ -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,31 @@
6
6
 
7
7
  Output:
8
8
  E_out = 0.000 mWs -> cv20
9
- E_out = 17.165 mWs -> cv10
10
- E_out = 17.427 mWs -> mppt_voc
11
- E_out = 17.242 mWs -> mppt_bq_solar
12
- E_out = 13.998 mWs -> mppt_bq_thermoelectric
13
- E_out = 15.202 mWs -> mppt_po
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
 
17
18
  from pathlib import Path
18
19
 
19
- from shepherd_core import Reader
20
20
  from shepherd_core.data_models import VirtualHarvesterConfig
21
21
  from shepherd_core.vsource import simulate_harvester
22
+
23
+ from shepherd_core import Reader
22
24
  from shepherd_data import ivonne
23
25
 
24
26
  # config simulation
25
27
  sim_duration = 32
26
- file_ivonne = Path(__file__).parent.parent.parent / "shepherd_data/examples/jogging_10m.iv"
28
+ file_ivonne = Path(__file__).parents[3] / "shepherd_data/examples/jogging_10m.iv"
27
29
  file_ivcurve = Path(__file__).parent / "jogging_ivcurve.h5"
28
30
 
31
+ if not file_ivonne.exists():
32
+ raise FileNotFoundError("Input-File not found - check path")
33
+
29
34
  hrv_list = [
30
35
  "cv20",
31
36
  # ⤷ fails due to lower solar voltage
@@ -39,7 +44,7 @@ hrv_list = [
39
44
  "mppt_opt",
40
45
  ]
41
46
 
42
- save_files: bool = False
47
+ save_files: bool = True
43
48
 
44
49
  # convert IVonne to IVCurve
45
50
  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 = 13.142 mWs -> diode+capacitor
14
- E_out = 13.066 mWs -> diode+resistor+capacitor
15
- E_out = 15.045 mWs -> BQ25504
16
- E_out = 14.962 mWs -> BQ25504s
17
- E_out = 14.397 mWs -> BQ25570
18
- E_out = 14.232 mWs -> BQ25570s
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
- V_intermediate_enable_threshold_mV=1,
52
- V_intermediate_disable_threshold_mV=0,
53
- # jogging-dataset has max VOC of ~1.6 V -> lower set-point for non-boost
54
- C_intermediate_uF=100 if "direct" not in src_name else 0,
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,239 @@
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 = 20 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"XP {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"XP {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"XP {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"XP {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(path_here, f"XP {config.name}, pulsed resistive charge 20 Ohm to 4.2 V, 3000 s")
174
+
175
+
176
+ def experiment_resistive_load(config: VirtualStorageConfig) -> None:
177
+ """Charge virtual storage with a resistive constant voltage."""
178
+ dt_s = 0.5
179
+ SoC_start = 1.0
180
+
181
+ def i_charge(_t_s: float, _s: float, V: float) -> float:
182
+ return -V / 20
183
+
184
+ sim = StorageSimulator(
185
+ models=get_models(SoC_start, config, dt_s),
186
+ dt_s=dt_s,
187
+ )
188
+ sim.run(fn=i_charge, duration_s=min(1_000, duration_max))
189
+ sim.plot(path_here, f"XP {config.name}, resistive load 20 Ohm from 4.2 V, 1000 s")
190
+
191
+
192
+ def experiment_self_discharge() -> None:
193
+ """Observe self-discharge behavior of virtual storage models."""
194
+ dt_s = 0.2
195
+ SoC_start = 1.0
196
+ SoC_target = 0.9
197
+ duration = timedelta(minutes=25)
198
+ store = VirtualStorageConfig.capacitor(C_uF=100, V_rated=6.3)
199
+ R_leak = store.calc_R_leak_capacitor(duration=duration, SoC_final=SoC_target, SoC_0=SoC_start)
200
+ log.info("R_leak = %.2f Ohm", R_leak)
201
+ config = VirtualStorageConfig.capacitor(C_uF=100, V_rated=6.3, R_leak_Ohm=R_leak)
202
+ sim = StorageSimulator(
203
+ models=get_models(SoC_start, config, dt_s),
204
+ dt_s=dt_s,
205
+ )
206
+
207
+ def step(_t: float, _s: float, _v: float) -> float:
208
+ return 0
209
+
210
+ sim.run(fn=step, duration_s=min(duration.total_seconds(), duration_max))
211
+ sim.plot(
212
+ path_here,
213
+ f"XP {config.name}, self-discharge, "
214
+ f"SoC {SoC_start} to {SoC_target} in {duration.total_seconds()} s",
215
+ )
216
+
217
+
218
+ if __name__ == "__main__":
219
+ with multiprocessing.Pool() as pool:
220
+ pool.apply_async(experiment_self_discharge)
221
+
222
+ configs = [
223
+ VirtualStorageConfig.capacitor(C_uF=10e6, V_rated=4.2), # match charge with batteries
224
+ VirtualStorageConfig.lipo(q_mAh=10),
225
+ VirtualStorageConfig.lead_acid(q_mAh=10),
226
+ ]
227
+
228
+ for cfg in configs:
229
+ pool.apply_async(experiment_pulsed_charge, args=(cfg,))
230
+ pool.apply_async(experiment_pulsed_discharge, args=(cfg,))
231
+ pool.apply_async(experiment_current_ramp_pos, args=(cfg,))
232
+ pool.apply_async(experiment_current_ramp_neg, args=(cfg,))
233
+
234
+ for cfg in configs[0:2]:
235
+ pool.apply_async(experiment_pulsed_resistive_charge, args=(cfg,))
236
+ pool.apply_async(experiment_resistive_load, args=(cfg,))
237
+
238
+ pool.close()
239
+ pool.join()
@@ -18,6 +18,7 @@ classifiers = [
18
18
  "Programming Language :: Python :: 3.11",
19
19
  "Programming Language :: Python :: 3.12",
20
20
  "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
21
22
  "License :: OSI Approved :: MIT License",
22
23
  "Operating System :: OS Independent",
23
24
  "Natural Language :: English",
@@ -22,8 +22,9 @@ from .content.energy_environment import EnergyDType
22
22
  from .content.energy_environment import EnergyEnvironment
23
23
  from .content.firmware import Firmware
24
24
  from .content.firmware import FirmwareDType
25
- from .content.virtual_harvester import VirtualHarvesterConfig
26
- from .content.virtual_source import VirtualSourceConfig
25
+ from .content.virtual_harvester_config import VirtualHarvesterConfig
26
+ from .content.virtual_source_config import VirtualSourceConfig
27
+ from .content.virtual_storage_config import VirtualStorageConfig
27
28
  from .experiment.experiment import Experiment
28
29
  from .experiment.observer_features import GpioActuation
29
30
  from .experiment.observer_features import GpioEvent
@@ -58,5 +59,6 @@ __all__ = [
58
59
  "UartLogging",
59
60
  "VirtualHarvesterConfig",
60
61
  "VirtualSourceConfig",
62
+ "VirtualStorageConfig",
61
63
  "Wrapper",
62
64
  ]
@@ -36,10 +36,12 @@ class ContentModel(ShpModel):
36
36
  default_factory=id_default,
37
37
  )
38
38
  name: NameStr
39
+ """ ⤷ name of virtual content, models can be queried by this slug."""
39
40
  description: Annotated[SafeStr | None, Field(description="Required when public")] = None
40
41
  comment: SafeStr | None = None
41
42
  created: datetime = Field(default_factory=datetime.now)
42
43
  updated_last: datetime = Field(default_factory=datetime.now)
44
+ # TODO: add dedicated 'inherit_from' field?
43
45
 
44
46
  # Ownership & Access
45
47
  # TODO: remove owner & group, only needed for DB
@@ -7,8 +7,9 @@ from .energy_environment import EnergyDType
7
7
  from .energy_environment import EnergyEnvironment
8
8
  from .firmware import Firmware
9
9
  from .firmware_datatype import FirmwareDType
10
- from .virtual_harvester import VirtualHarvesterConfig
11
- from .virtual_source import VirtualSourceConfig
10
+ from .virtual_harvester_config import VirtualHarvesterConfig
11
+ from .virtual_source_config import VirtualSourceConfig
12
+ from .virtual_storage_config import VirtualStorageConfig
12
13
 
13
14
  __all__ = [
14
15
  "EnergyDType",
@@ -17,4 +18,5 @@ __all__ = [
17
18
  "FirmwareDType",
18
19
  "VirtualHarvesterConfig",
19
20
  "VirtualSourceConfig",
21
+ "VirtualStorageConfig",
20
22
  ]
@@ -332,11 +332,11 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
332
332
  @model_validator(mode="after")
333
333
  def post_validation(self) -> Self:
334
334
  if self.voltage_min_mV > self.voltage_max_mV:
335
- raise ValueError("Voltage min > max")
335
+ raise ValueError("Voltage minimum > max")
336
336
  if self.voltage_mV < self.voltage_min_mV:
337
- raise ValueError("Voltage below min")
337
+ raise ValueError("Voltage below minimum")
338
338
  if self.voltage_mV > self.voltage_max_mV:
339
- raise ValueError("Voltage above max")
339
+ raise ValueError("Voltage above maximum")
340
340
 
341
341
  return self
342
342