shepherd-core 2024.7.4__tar.gz → 2024.8.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 (141) hide show
  1. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/PKG-INFO +1 -1
  2. shepherd_core-2024.8.1/examples/eenv_generator.py +43 -0
  3. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/vharvester_simulation.py +26 -3
  4. shepherd_core-2024.8.1/examples/vsource_emulation.py +86 -0
  5. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/base/calibration.py +20 -22
  6. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/virtual_harvester.py +18 -11
  7. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/virtual_harvester_fixture.yaml +10 -13
  8. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/virtual_source.py +6 -4
  9. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/virtual_source_fixture.yaml +10 -8
  10. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/reader.py +9 -4
  11. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/version.py +1 -1
  12. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/vsource/virtual_converter_model.py +21 -6
  13. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/vsource/virtual_harvester_model.py +18 -8
  14. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/writer.py +13 -3
  15. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core.egg-info/PKG-INFO +1 -1
  16. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core.egg-info/SOURCES.txt +2 -0
  17. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/vsource/test_converter.py +10 -4
  18. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/README.md +0 -0
  19. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/experiment_from_yaml.yaml +0 -0
  20. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/experiment_generic_var1.py +0 -0
  21. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/experiment_generic_var2.py +0 -0
  22. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/experiment_models.py +0 -0
  23. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/firmware_model.py +0 -0
  24. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/firmware_modification.py +0 -0
  25. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/inventory.py +0 -0
  26. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/uart_decode_waveform.py +0 -0
  27. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/uart_raw2.csv +0 -0
  28. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/examples/vsource_simulation.py +0 -0
  29. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/pyproject.toml +0 -0
  30. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/setup.cfg +0 -0
  31. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/__init__.py +0 -0
  32. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/calibration_hw_def.py +0 -0
  33. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/commons.py +0 -0
  34. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/__init__.py +0 -0
  35. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/base/__init__.py +0 -0
  36. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/base/cal_measurement.py +0 -0
  37. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/base/content.py +0 -0
  38. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/base/shepherd.py +0 -0
  39. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/base/timezone.py +0 -0
  40. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/base/wrapper.py +0 -0
  41. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/__init__.py +0 -0
  42. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/_external_fixtures.yaml +0 -0
  43. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/energy_environment.py +0 -0
  44. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/energy_environment_fixture.yaml +0 -0
  45. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/firmware.py +0 -0
  46. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/content/firmware_datatype.py +0 -0
  47. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/experiment/__init__.py +0 -0
  48. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/experiment/experiment.py +0 -0
  49. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/experiment/observer_features.py +0 -0
  50. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/experiment/target_config.py +0 -0
  51. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/readme.md +0 -0
  52. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/task/__init__.py +0 -0
  53. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/task/emulation.py +0 -0
  54. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/task/firmware_mod.py +0 -0
  55. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/task/harvest.py +0 -0
  56. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/task/observer_tasks.py +0 -0
  57. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/task/programming.py +0 -0
  58. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/task/testbed_tasks.py +0 -0
  59. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/__init__.py +0 -0
  60. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/cape.py +0 -0
  61. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/cape_fixture.yaml +0 -0
  62. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/gpio.py +0 -0
  63. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/gpio_fixture.yaml +0 -0
  64. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/mcu.py +0 -0
  65. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/mcu_fixture.yaml +0 -0
  66. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/observer.py +0 -0
  67. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/observer_fixture.yaml +0 -0
  68. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/target.py +0 -0
  69. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/target_fixture.yaml +0 -0
  70. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/testbed.py +0 -0
  71. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/testbed/testbed_fixture.yaml +0 -0
  72. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/data_models/virtual_source_doc.txt +0 -0
  73. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/decoder_waveform/__init__.py +0 -0
  74. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/decoder_waveform/uart.py +0 -0
  75. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/fw_tools/__init__.py +0 -0
  76. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/fw_tools/converter.py +0 -0
  77. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/fw_tools/converter_elf.py +0 -0
  78. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/fw_tools/patcher.py +0 -0
  79. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/fw_tools/validation.py +0 -0
  80. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/inventory/__init__.py +0 -0
  81. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/inventory/python.py +0 -0
  82. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/inventory/system.py +0 -0
  83. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/inventory/target.py +0 -0
  84. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/logger.py +0 -0
  85. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/testbed_client/__init__.py +0 -0
  86. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/testbed_client/cache_path.py +0 -0
  87. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/testbed_client/client_abc_fix.py +0 -0
  88. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/testbed_client/client_web.py +0 -0
  89. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/testbed_client/fixtures.py +0 -0
  90. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/testbed_client/user_model.py +0 -0
  91. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/vsource/__init__.py +0 -0
  92. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core/vsource/virtual_source_model.py +0 -0
  93. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core.egg-info/dependency_links.txt +0 -0
  94. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core.egg-info/requires.txt +0 -0
  95. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core.egg-info/top_level.txt +0 -0
  96. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/shepherd_core.egg-info/zip-safe +0 -0
  97. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/__init__.py +0 -0
  98. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/conftest.py +0 -0
  99. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/__init__.py +0 -0
  100. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/conftest.py +0 -0
  101. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_cal_data.yaml +0 -0
  102. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_cal_data_faulty.yaml +0 -0
  103. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_cal_meas.yaml +0 -0
  104. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_cal_meas_faulty1.yaml +0 -0
  105. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_cal_meas_faulty2.yaml +0 -0
  106. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_config_emulator.yaml +0 -0
  107. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_config_experiment.yaml +0 -0
  108. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_config_experiment_alternative.yaml +0 -0
  109. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_config_harvester.yaml +0 -0
  110. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_config_testbed.yaml +0 -0
  111. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/example_config_virtsource.yaml +0 -0
  112. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_base_models.py +0 -0
  113. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_content_fixtures.py +0 -0
  114. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_content_models.py +0 -0
  115. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_examples.py +0 -0
  116. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_experiment_models.py +0 -0
  117. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_task_generation.py +0 -0
  118. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_task_models.py +0 -0
  119. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_testbed_fixtures.py +0 -0
  120. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/data_models/test_testbed_models.py +0 -0
  121. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/decoder_waveform/__init__.py +0 -0
  122. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/decoder_waveform/test_decoder.py +0 -0
  123. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/fw_tools/__init__.py +0 -0
  124. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/fw_tools/build_msp.elf +0 -0
  125. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/fw_tools/build_nrf.elf +0 -0
  126. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/fw_tools/conftest.py +0 -0
  127. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/fw_tools/test_converter.py +0 -0
  128. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/fw_tools/test_patcher.py +0 -0
  129. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/fw_tools/test_validation.py +0 -0
  130. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/inventory/__init__.py +0 -0
  131. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/inventory/test_inventory.py +0 -0
  132. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/test_cal_hw.py +0 -0
  133. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/test_examples.py +0 -0
  134. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/test_logger.py +0 -0
  135. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/test_reader.py +0 -0
  136. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/test_writer.py +0 -0
  137. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/testbed_client/__init__.py +0 -0
  138. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/vsource/__init__.py +0 -0
  139. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/vsource/conftest.py +0 -0
  140. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/vsource/test_harvester.py +0 -0
  141. {shepherd_core-2024.7.4 → shepherd_core-2024.8.1}/tests/vsource/test_z.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shepherd_core
3
- Version: 2024.7.4
3
+ Version: 2024.8.1
4
4
  Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
5
5
  Author-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
6
6
  Maintainer-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
@@ -0,0 +1,43 @@
1
+ """Create a set of static artificial energy environments."""
2
+
3
+ from itertools import product
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+ from tqdm import trange
8
+
9
+ from shepherd_core import Reader as ShpReader
10
+ from shepherd_core import Writer as ShpWriter
11
+ from shepherd_core import logger
12
+
13
+ # Config
14
+ voltages_V = [4.0, 2.0]
15
+ currents_A = [2e-3, 1e-3]
16
+ duration_s = 10
17
+ repetitions = 1
18
+
19
+ path_here = Path(__file__).parent.absolute()
20
+
21
+ for _v, _c in product(voltages_V, currents_A):
22
+ v_str = f"{round(_v * 1000)}mV"
23
+ c_str = f"{round(_c * 1000)}mA"
24
+ t_str = f"{round(duration_s * repetitions)}s"
25
+ name = f"eenv_static_{v_str}_{c_str}_{t_str}"
26
+ file_path = path_here / f"{name}.h5"
27
+
28
+ if file_path.exists():
29
+ logger.info("File exists, will skip: %s", file_path.name)
30
+ else:
31
+ with ShpWriter(file_path) as file:
32
+ file.store_hostname("artificial")
33
+ # values in SI units
34
+ timestamp_vector = np.arange(0.0, duration_s, file.sample_interval_ns / 1e9)
35
+ voltage_vector = np.linspace(_v, _v, int(file.samplerate_sps * duration_s))
36
+ current_vector = np.linspace(_c, _c, int(file.samplerate_sps * duration_s))
37
+
38
+ for idx in trange(repetitions, desc="generate"):
39
+ timestamps = idx * duration_s + timestamp_vector
40
+ file.append_iv_data_si(timestamps, voltage_vector, current_vector)
41
+
42
+ with ShpReader(file_path) as file:
43
+ file.save_metadata()
@@ -6,9 +6,12 @@
6
6
 
7
7
  """
8
8
 
9
+ from contextlib import ExitStack
9
10
  from pathlib import Path
10
11
 
12
+ from shepherd_core import CalibrationHarvester
11
13
  from shepherd_core import Reader
14
+ from shepherd_core import Writer
12
15
  from shepherd_core.data_models import VirtualHarvesterConfig
13
16
  from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
14
17
  from shepherd_core.vsource import VirtualHarvesterModel
@@ -32,6 +35,8 @@ hrv_list = [
32
35
  "mppt_opt",
33
36
  ]
34
37
 
38
+ save_files: bool = True
39
+
35
40
  # convert IVonne to IVCurve
36
41
  if not file_ivcurve.exists():
37
42
  with ivonne.Reader(file_ivonne) as db:
@@ -45,7 +50,7 @@ with Reader(file_ivcurve, verbose=False) as file:
45
50
  I_in_max = max(I_in_max, _i.max())
46
51
  print(
47
52
  f"Input-file: \n"
48
- f"\tE_in = {file.energy() * 1e3:.3f} mWs\n"
53
+ f"\tE_in = {file.energy() * 1e3:.3f} mWs (not representative)\n"
49
54
  f"\tI_in_max = {I_in_max * 1e3:.3f} mA\n"
50
55
  f"\twindow_size = {window_size} n\n",
51
56
  )
@@ -53,21 +58,39 @@ with Reader(file_ivcurve, verbose=False) as file:
53
58
  # Simulation
54
59
  for hrv_name in hrv_list:
55
60
  E_out_Ws = 0.0
61
+ _cal = CalibrationHarvester()
62
+ if save_files:
63
+ stack = ExitStack()
64
+ file_output = file_ivcurve.with_name(
65
+ file_ivcurve.stem + "_" + hrv_name + file_ivcurve.suffix
66
+ )
67
+ fh_output = Writer(
68
+ file_output, cal_data=_cal, mode="harvester", verbose=False, force_overwrite=True
69
+ )
70
+ stack.enter_context(fh_output)
71
+ fh_output.store_hostname("hrv_sim_" + hrv_name)
72
+
56
73
  with Reader(file_ivcurve, verbose=False) as file:
57
74
  hrv_config = VirtualHarvesterConfig(name=hrv_name)
58
75
  hrv_pru = HarvesterPRUConfig.from_vhrv(
59
76
  hrv_config,
60
77
  for_emu=True,
61
78
  dtype_in=file.get_datatype(),
62
- window_size=file.get_window_samples(),
79
+ window_size=4 * file.get_window_samples(),
63
80
  )
64
81
  hrv = VirtualHarvesterModel(hrv_pru)
65
82
  for _t, _v, _i in file.read_buffers():
83
+ # TODO: _t should be handed to new file without conversions
66
84
  length = max(_v.size, _i.size)
67
85
  for _n in range(length):
68
86
  _v[_n], _i[_n] = hrv.ivcurve_sample(
69
87
  _voltage_uV=_v[_n] * 10**6, _current_nA=_i[_n] * 10**9
70
88
  )
71
89
  E_out_Ws += (_v * _i).sum() * 1e-15 * file.sample_interval_s
72
-
90
+ if save_files:
91
+ fh_output.append_iv_data_si(_t, _v / 1e6, _i / 1e9)
92
+ if save_files:
93
+ stack.close()
94
+ with Reader(file_output) as fh_output:
95
+ fh_output.save_metadata()
73
96
  print(f"E_out = {E_out_Ws * 1e3:.3f} mWs -> {hrv_name}")
@@ -0,0 +1,86 @@
1
+ """Demonstrate behavior of Virtual Source Algorithms.
2
+
3
+ The emulation recreates an observer-cape, the virtual Source and a virtual target
4
+ - input = hdf5-file with a harvest-recording
5
+ - output = hdf5-file
6
+ - config is currently hardcoded, but it could be an emulation-task
7
+ - target is currently a simple resistor
8
+
9
+ The output file can be analyzed and plotted with shepherds tool suite.
10
+ """
11
+ # TODO: `shepherd-data emulate config.yaml`
12
+
13
+ from pathlib import Path
14
+
15
+ import numpy as np
16
+ from tqdm import tqdm
17
+
18
+ from shepherd_core import CalibrationEmulator
19
+ from shepherd_core import Reader
20
+ from shepherd_core import Writer
21
+ from shepherd_core.data_models import VirtualHarvesterConfig
22
+ from shepherd_core.data_models import VirtualSourceConfig
23
+ from shepherd_core.vsource import VirtualSourceModel
24
+
25
+ # config simulation
26
+ file_input = Path(__file__).parent / "jogging_ivcurve.h5"
27
+
28
+ src_list = ["BQ25504"]
29
+
30
+ I_mcu_sleep_A = 3e-3
31
+ I_mcu_active_A = 3e-3
32
+ R_Ohm = 1000
33
+
34
+ for vs_name in src_list:
35
+ file_output = file_input.with_name(file_input.stem + "_emu_" + vs_name + file_input.suffix)
36
+
37
+ cal_emu = CalibrationEmulator()
38
+ src_config = VirtualSourceConfig(
39
+ inherit_from=vs_name,
40
+ V_intermediate_init_mV=3000,
41
+ harvester=VirtualHarvesterConfig(name="mppt_bq_solar"),
42
+ C_intermediate_uF=50,
43
+ )
44
+
45
+ with Reader(file_input, verbose=False) as f_inp, Writer(
46
+ file_output, cal_data=cal_emu, mode="emulator", verbose=False
47
+ ) as f_out:
48
+ window_size = f_inp.get_window_samples()
49
+ f_out.store_hostname("emu_sim_" + vs_name)
50
+ f_out.store_config(src_config.model_dump())
51
+ src = VirtualSourceModel(
52
+ src_config, cal_emu, log_intermediate=False, window_size=window_size
53
+ )
54
+
55
+ I_out_nA = 0
56
+
57
+ for _t, _V_inp, _I_inp in tqdm(f_inp.read_buffers(), total=f_inp.buffers_n):
58
+ V_out = np.empty(_V_inp.shape)
59
+ I_out = np.empty(_I_inp.shape)
60
+
61
+ for _iter in range(len(_t)):
62
+ V_out_uV = src.iterate_sampling(
63
+ V_inp_uV=_V_inp[_iter] * 10**6,
64
+ I_inp_nA=_I_inp[_iter] * 10**9,
65
+ I_out_nA=I_out_nA,
66
+ )
67
+ I_out_nA = 1e3 * V_out_uV / R_Ohm
68
+
69
+ V_out[_iter] = V_out_uV / 1e6
70
+ I_out[_iter] = I_out_nA / 1e9
71
+
72
+ # TODO: src.cnv.get_I_mod_out_nA() has more internal drains
73
+
74
+ f_out.append_iv_data_si(_t, V_out, I_out)
75
+
76
+ # listen to power-good signal
77
+ """
78
+ if src.cnv.get_power_good():
79
+ I_out_nA = int(I_mcu_active_A * 10 ** 9)
80
+ N_good += 1
81
+ else:
82
+ I_out_nA = int(I_mcu_sleep_A * 10 ** 9)
83
+ """
84
+
85
+ with Reader(file_output, verbose=False) as f_out:
86
+ f_out.save_metadata()
@@ -51,7 +51,7 @@ class CalibrationPair(ShpModel):
51
51
 
52
52
  gain: PositiveFloat
53
53
  offset: float = 0
54
- # TODO: add unit
54
+ unit: Optional[str] = None # TODO: add units when used
55
55
 
56
56
  def raw_to_si(self, values_raw: Calc_t, *, allow_negative: bool = True) -> Calc_t:
57
57
  """Convert between physical units and raw unsigned integers."""
@@ -78,14 +78,11 @@ class CalibrationPair(ShpModel):
78
78
  return values_raw
79
79
 
80
80
  @classmethod
81
- def from_fn(cls, fn: Callable) -> Self:
81
+ def from_fn(cls, fn: Callable, unit: Optional[str] = None) -> Self:
82
82
  """Probe linear function to determine scaling values."""
83
83
  offset = fn(0, limited=False)
84
84
  gain_inv = fn(1.0, limited=False) - offset
85
- return cls(
86
- gain=1.0 / float(gain_inv),
87
- offset=-float(offset) / gain_inv,
88
- )
85
+ return cls(gain=1.0 / float(gain_inv), offset=-float(offset) / gain_inv, unit=unit)
89
86
 
90
87
 
91
88
  cal_hrv_legacy = { # legacy translator
@@ -99,10 +96,10 @@ cal_hrv_legacy = { # legacy translator
99
96
  class CalibrationHarvester(ShpModel):
100
97
  """Container for all calibration-pairs for that device."""
101
98
 
102
- dac_V_Hrv: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
103
- dac_V_Sim: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
104
- adc_V_Sense: CalibrationPair = CalibrationPair.from_fn(adc_voltage_to_raw)
105
- adc_C_Hrv: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw)
99
+ dac_V_Hrv: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
100
+ dac_V_Sim: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
101
+ adc_V_Sense: CalibrationPair = CalibrationPair.from_fn(adc_voltage_to_raw, unit="V")
102
+ adc_C_Hrv: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw, unit="A")
106
103
 
107
104
  def export_for_sysfs(self) -> dict:
108
105
  """Convert and write the essential data.
@@ -143,10 +140,10 @@ class CalibrationEmulator(ShpModel):
143
140
  Differentiates between both target-ports A/B.
144
141
  """
145
142
 
146
- dac_V_A: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
147
- dac_V_B: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw)
148
- adc_C_A: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw)
149
- adc_C_B: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw)
143
+ dac_V_A: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
144
+ dac_V_B: CalibrationPair = CalibrationPair.from_fn(dac_voltage_to_raw, unit="V")
145
+ adc_C_A: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw, unit="A")
146
+ adc_C_B: CalibrationPair = CalibrationPair.from_fn(adc_current_to_raw, unit="A")
150
147
 
151
148
  def export_for_sysfs(self) -> dict:
152
149
  """Convert and write the essential data.
@@ -233,10 +230,11 @@ class CalibrationCape(ShpModel):
233
230
 
234
231
  """
235
232
  dv = cls().model_dump(include={"harvester", "emulator"})
236
- lw = list(dict_generator(dv))
237
- values = struct.unpack(">" + len(lw) * "d", data)
233
+ lw1 = list(dict_generator(dv))
234
+ lw2 = [elem for elem in lw1 if isinstance(elem[-1], float)]
235
+ values = struct.unpack(">" + len(lw2) * "d", data)
238
236
  # ⤷ X => double float, big endian
239
- for _i, walk in enumerate(lw):
237
+ for _i, walk in enumerate(lw2):
240
238
  # hardcoded fixed depth ... bad but easy
241
239
  dv[walk[0]][walk[1]][walk[2]] = float(values[_i])
242
240
  dv["cape"] = cape
@@ -252,18 +250,18 @@ class CalibrationCape(ShpModel):
252
250
 
253
251
  """
254
252
  lw = list(dict_generator(self.model_dump(include={"harvester", "emulator"})))
255
- values = [walk[-1] for walk in lw]
256
- return struct.pack(">" + len(lw) * "d", *values)
253
+ values = [walk[-1] for walk in lw if isinstance(walk[-1], float)]
254
+ return struct.pack(">" + len(values) * "d", *values)
257
255
 
258
256
 
259
257
  class CalibrationSeries(ShpModel):
260
258
  """Cal-Data for a typical recording of a testbed experiment."""
261
259
 
262
- voltage: CalibrationPair = CalibrationPair(gain=3 * 1e-9)
260
+ voltage: CalibrationPair = CalibrationPair(gain=3 * 1e-9, unit="V")
263
261
  # ⤷ default allows 0 - 12 V in 3 nV-Steps
264
- current: CalibrationPair = CalibrationPair(gain=250 * 1e-12)
262
+ current: CalibrationPair = CalibrationPair(gain=250 * 1e-12, unit="A")
265
263
  # ⤷ default allows 0 - 1 A in 250 pA - Steps
266
- time: CalibrationPair = CalibrationPair(gain=1e-9)
264
+ time: CalibrationPair = CalibrationPair(gain=1e-9, unit="s")
267
265
  # ⤷ default = nanoseconds
268
266
 
269
267
  @classmethod
@@ -149,20 +149,27 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
149
149
 
150
150
  def calc_window_size(
151
151
  self,
152
- dtype_in: Optional[EnergyDType] = EnergyDType.ivsample,
152
+ dtype_in: Optional[EnergyDType] = None,
153
153
  *,
154
154
  for_emu: bool,
155
155
  ) -> int:
156
- if for_emu:
157
- if dtype_in == EnergyDType.ivcurve:
158
- return self.samples_n * (1 + self.wait_cycles)
159
- if dtype_in == EnergyDType.ivsample:
160
- return 0
161
- # isc_voc: 2 * (1 + wait_cycles), noqa
162
- raise NotImplementedError
156
+ if not for_emu:
157
+ # TODO: should be named 'for_ivcurve_recording'
158
+ # TODO: add extra variable to distinguish step_count
159
+ # and window_size (currently mixed together)
160
+ # only used by ivcurve algo (in ADC-Mode)
161
+ return self.samples_n
163
162
 
164
- # only used by ivcurve algo (in ADC-Mode)
165
- return self.samples_n
163
+ if dtype_in is None:
164
+ dtype_in = self.get_datatype()
165
+
166
+ if dtype_in == EnergyDType.ivcurve:
167
+ return self.samples_n * (1 + self.wait_cycles)
168
+ if dtype_in == EnergyDType.ivsample:
169
+ return 0
170
+ if dtype_in == EnergyDType.isc_voc:
171
+ return 2 * (1 + self.wait_cycles)
172
+ raise NotImplementedError
166
173
 
167
174
 
168
175
  u32 = Annotated[int, Field(ge=0, lt=2**32)]
@@ -234,7 +241,7 @@ class HarvesterPRUConfig(ShpModel):
234
241
  dtype_in = EnergyDType[dtype_in]
235
242
  if for_emu and dtype_in not in {EnergyDType.ivsample, EnergyDType.ivcurve}:
236
243
  raise NotImplementedError
237
- # TODO: use dtype properly in shepherd
244
+
238
245
  interval_ms, duration_ms = data.calc_timings_ms(for_emu=for_emu)
239
246
  return cls(
240
247
  algorithm=data.calc_algorithm_num(for_emu=for_emu),
@@ -19,13 +19,13 @@
19
19
  id: 1010
20
20
  name: ivcurve
21
21
  description: Postpone harvesting by sampling ivcurves (voltage stepped as sawtooth-wave)
22
- comment: ~200 Hz
22
+ comment: ~110 Hz, Between 50 & 60 Hz line-frequency to avoid standing waves
23
23
  inherit_from: neutral
24
24
  algorithm: ivcurve
25
- samples_n: 250
25
+ samples_n: 909
26
26
  voltage_min_mV: 0
27
27
  voltage_max_mV: 5000
28
- wait_cycles: 1 # results in 200 Hz (= 100kHz /(2*250))
28
+ wait_cycles: 0
29
29
  rising: false # downward sawtooth seems to have advantages for solar cells
30
30
  # todo: also add switch for sawtooth- vs triangle-wave?
31
31
  # todo: could also include a version with dynamic upper-boundary, varied if voc is reached very early
@@ -38,20 +38,17 @@
38
38
 
39
39
  - datatype: VirtualHarvesterConfig
40
40
  parameters:
41
- id: 1012
42
- name: iv1000
43
- comment: Name relates to curves per second
41
+ id: 1013
42
+ name: iv110 # synonym
44
43
  inherit_from: ivcurve
45
- samples_n: 100
46
- wait_cycles: 0
47
44
 
48
45
  - datatype: VirtualHarvesterConfig
49
46
  parameters:
50
- id: 1013
51
- name: iv110
52
- comment: Between 50 & 60 Hz line-frequency to avoid standing waves
47
+ id: 1012
48
+ name: iv1000
49
+ comment: Name relates to curves per second
53
50
  inherit_from: ivcurve
54
- samples_n: 909
51
+ samples_n: 100
55
52
  wait_cycles: 0
56
53
 
57
54
  - datatype: VirtualHarvesterConfig
@@ -61,7 +58,7 @@
61
58
  description: Postpone harvesting by sampling short circuit current & open circuit voltage
62
59
  inherit_from: neutral
63
60
  algorithm: isc_voc
64
- wait_cycles: 1 # results in 25 kHz (isc, wait, voc, wait)
61
+ wait_cycles: 4 # results in 10 kHz (isc, wait, voc, wait)
65
62
 
66
63
  - datatype: VirtualHarvesterConfig
67
64
  parameters:
@@ -76,6 +76,8 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
76
76
  # final (always last) stage to compensate undetectable current spikes
77
77
  # when enabling power for target
78
78
  C_output_uF: Annotated[float, Field(ge=0, le=4.29e6)] = 1.0
79
+ # TODO: C_output is handled internally as delta-V, but should be a I_transient
80
+ # that makes it visible in simulation as additional i_out_drain
79
81
 
80
82
  # Extra
81
83
  V_output_log_gpio_threshold_mV: Annotated[float, Field(ge=0, le=4.29e6)] = 1_400
@@ -93,7 +95,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
93
95
  # influence of cap-voltage is not implemented
94
96
  LUT_input_V_min_log2_uV: Annotated[int, Field(ge=0, le=20)] = 0
95
97
  # ⤷ 2^7 = 128 uV -> LUT[0][:] is for inputs < 128 uV
96
- LUT_input_I_min_log2_nA: Annotated[int, Field(ge=0, le=20)] = 0
98
+ LUT_input_I_min_log2_nA: Annotated[int, Field(ge=1, le=20)] = 1
97
99
  # ⤷ 2^8 = 256 nA -> LUT[:][0] is for inputs < 256 nA
98
100
 
99
101
  # Buck Converter
@@ -103,7 +105,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
103
105
 
104
106
  LUT_output_efficiency: LUT1D = 12 * [1.00]
105
107
  # ⤷ array[12] depending on output_current
106
- LUT_output_I_min_log2_nA: Annotated[int, Field(ge=0, le=20)] = 0
108
+ LUT_output_I_min_log2_nA: Annotated[int, Field(ge=1, le=20)] = 1
107
109
  # ⤷ 2^8 = 256 nA -> LUT[0] is for inputs < 256 nA, see notes on LUT_input for explanation
108
110
 
109
111
  @model_validator(mode="before")
@@ -318,8 +320,8 @@ class ConverterPRUConfig(ShpModel):
318
320
  V_buck_drop_uV=round(data.V_buck_drop_mV * 1e3),
319
321
  # LUTs
320
322
  LUT_input_V_min_log2_uV=data.LUT_input_V_min_log2_uV,
321
- LUT_input_I_min_log2_nA=data.LUT_input_I_min_log2_nA,
322
- LUT_output_I_min_log2_nA=data.LUT_output_I_min_log2_nA,
323
+ LUT_input_I_min_log2_nA=data.LUT_input_I_min_log2_nA - 1, # sub-1 due to later log2-op
324
+ LUT_output_I_min_log2_nA=data.LUT_output_I_min_log2_nA - 1, # sub-1 due to later log2
323
325
  LUT_inp_efficiency_n8=[
324
326
  [min(255, round(256 * ival)) for ival in il] for il in data.LUT_input_efficiency
325
327
  ],
@@ -56,16 +56,18 @@
56
56
  [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ],
57
57
  [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ],
58
58
  [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ],
59
- ] # input-array[12][12] depending on array[inp_voltage][log(inp_current)], influence of cap-voltage is not implemented
59
+ ]
60
+ # input-array[12][12] depending on array[inp_voltage][log(inp_current)],
61
+ # influence of cap-voltage is not implemented
60
62
  LUT_input_V_min_log2_uV: 0 # 2^7 = 128 uV -> array[0] is for inputs < 128 uV
61
- LUT_input_I_min_log2_nA: 0 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA
63
+ LUT_input_I_min_log2_nA: 1 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA
62
64
 
63
65
  # Buck-converter
64
66
  V_output_mV: 2400
65
67
  V_buck_drop_mV: 0.0 # simulate LDO min voltage differential or output-diode
66
68
 
67
69
  LUT_output_efficiency: [ 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 ] # array[12] depending on output_current
68
- LUT_output_I_min_log2_nA: 0 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA, see notes on LUT_input for explanation
70
+ LUT_output_I_min_log2_nA: 1 # 2^8 = 256 nA -> array[0] is for inputs < 256 nA, see notes on LUT_input for explanation
69
71
 
70
72
  owner: Ingmar
71
73
  group: NES Lab
@@ -114,7 +116,7 @@
114
116
  parameters:
115
117
  id: 1020
116
118
  name: BQ25504
117
- description: TI BQ25504 with integrated boost-converter
119
+ description: TI BQ25504 with integrated boost-converter. Values are taken from the DK-Board.
118
120
  inherit_from: neutral # to complete undefined vars
119
121
  enable_boost: true # if false -> v_intermediate = v_input, output-switch-hysteresis is still usable
120
122
 
@@ -124,16 +126,16 @@
124
126
  V_input_max_mV: 3000
125
127
  I_input_max_mA: 100
126
128
 
127
- C_intermediate_uF: 22.0 # primary storage-Cap
129
+ C_intermediate_uF: 100.0 # primary storage-Cap
128
130
  V_intermediate_init_mV: 3000 # allow a proper / fast startup
129
131
  I_intermediate_leak_nA: 330
130
132
 
131
- V_intermediate_enable_threshold_mV: 2600 # -> target gets connected (hysteresis-combo with next value)
132
- V_intermediate_disable_threshold_mV: 2300 # -> target gets disconnected
133
+ V_intermediate_enable_threshold_mV: 1000 # -> target gets connected (hysteresis-combo with next value)
134
+ V_intermediate_disable_threshold_mV: 0 # -> target gets disconnected
133
135
  interval_check_thresholds_ms: 64.0 # some BQs check every 64 ms if output should be disconnected
134
136
 
135
137
  V_pwr_good_enable_threshold_mV: 2800 # target is informed by pwr-good on output-pin (hysteresis) -> for intermediate voltage
136
- V_pwr_good_disable_threshold_mV: 2400
138
+ V_pwr_good_disable_threshold_mV: 2340
137
139
  immediate_pwr_good_signal: false # 1: activate instant schmitt-trigger, 0: stay in interval for checking thresholds
138
140
 
139
141
  # Boost Converter
@@ -84,6 +84,7 @@ class Reader:
84
84
 
85
85
  # init stats
86
86
  self.runtime_s: float = 0
87
+ self.buffers_n: int = 0
87
88
  self.file_size: int = 0
88
89
  self.data_rate: float = 0
89
90
 
@@ -170,12 +171,16 @@ class Reader:
170
171
  def _refresh_file_stats(self) -> None:
171
172
  """Update internal states, helpful after resampling or other changes in data-group."""
172
173
  self.h5file.flush()
173
- if (self.ds_time.shape[0] > 1) and (self.ds_time[1] != self.ds_time[0]):
174
- # this assumes isochronal sampling
175
- self.sample_interval_s = self._cal.time.raw_to_si(self.ds_time[1] - self.ds_time[0])
174
+ sample_count = self.ds_time.shape[0]
175
+ duration_raw = self.ds_time[sample_count - 1] - self.ds_time[0] if sample_count > 0 else 0
176
+ if (sample_count > 0) and (duration_raw > 0):
177
+ # this assumes iso-chronous sampling
178
+ duration_s = self._cal.time.raw_to_si(duration_raw)
179
+ self.sample_interval_s = duration_s / sample_count
176
180
  self.sample_interval_ns = int(10**9 * self.sample_interval_s)
177
- self.samplerate_sps = max(int(10**9 // self.sample_interval_ns), 1)
181
+ self.samplerate_sps = max(int((sample_count - 1) / duration_s), 1)
178
182
  self.runtime_s = round(self.ds_voltage.shape[0] / self.samplerate_sps, 1)
183
+ self.buffers_n = int(self.ds_voltage.shape[0] // self.samples_per_buffer)
179
184
  if isinstance(self.file_path, Path):
180
185
  self.file_size = self.file_path.stat().st_size
181
186
  else:
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2024.7.4"
3
+ version: str = "2024.8.1"
@@ -25,12 +25,28 @@ from ..data_models.content.virtual_source import ConverterPRUConfig
25
25
  class PruCalibration:
26
26
  """part of calibration.h."""
27
27
 
28
+ # negative residue compensation - compensate for noise around 0
29
+ # -> current uint-design cuts away negative part and leads to biased mean()
30
+ NOISE_ESTIMATE_nA: int = 2000
31
+ RESIDUE_SIZE_FACTOR: int = 30
32
+ RESIDUE_MAX_nA: int = NOISE_ESTIMATE_nA * RESIDUE_SIZE_FACTOR
33
+ negative_residue_nA = 0
34
+
28
35
  def __init__(self, cal_emu: Optional[CalibrationEmulator] = None) -> None:
29
36
  self.cal = cal_emu if cal_emu else CalibrationEmulator()
30
37
 
31
38
  def conv_adc_raw_to_nA(self, current_raw: int) -> float:
32
- return self.cal.adc_C_A.raw_to_si(current_raw) * (10**9)
33
- # TODO: add feature "negative residue compensation" to here
39
+ I_nA = self.cal.adc_C_A.raw_to_si(current_raw) * (10**9)
40
+ if self.cal.adc_C_A.offset < 0:
41
+ if I_nA > self.negative_residue_nA:
42
+ I_nA -= self.negative_residue_nA
43
+ self.negative_residue_nA = 0
44
+ else:
45
+ self.negative_residue_nA = self.negative_residue_nA - I_nA
46
+ if self.negative_residue_nA > self.RESIDUE_MAX_nA:
47
+ self.negative_residue_nA = self.RESIDUE_MAX_nA
48
+ I_nA = 0
49
+ return I_nA
34
50
 
35
51
  @staticmethod
36
52
  def conv_adc_raw_to_uV(voltage_raw: int) -> float:
@@ -88,10 +104,11 @@ class VirtualConverterModel:
88
104
  self.vsource_skip_gpio_logging: bool = False
89
105
 
90
106
  def calc_inp_power(self, input_voltage_uV: float, input_current_nA: float) -> int:
91
- # Next 2 lines are Python-specific
107
+ # Next 2 lines are Python-specific (model unsigned int)
92
108
  input_voltage_uV = max(0.0, input_voltage_uV)
93
109
  input_current_nA = max(0.0, input_current_nA)
94
110
 
111
+ # Input diode
95
112
  if input_voltage_uV > self._cfg.V_input_drop_uV:
96
113
  input_voltage_uV -= self._cfg.V_input_drop_uV
97
114
  else:
@@ -108,8 +125,6 @@ class VirtualConverterModel:
108
125
  if self.enable_boost:
109
126
  if input_voltage_uV < self._cfg.V_input_boost_threshold_uV:
110
127
  input_voltage_uV = 0.0
111
- if input_voltage_uV > self.V_mid_uV:
112
- input_voltage_uV = self.V_mid_uV
113
128
  elif not self.enable_storage:
114
129
  # direct connection
115
130
  self.V_mid_uV = input_voltage_uV
@@ -134,7 +149,7 @@ class VirtualConverterModel:
134
149
  return round(self.P_inp_fW) # Python-specific, added for easier testing
135
150
 
136
151
  def calc_out_power(self, current_adc_raw: int) -> int:
137
- # Next 2 lines are Python-specific
152
+ # Next 2 lines are Python-specific (model unsigned int)
138
153
  current_adc_raw = max(0, current_adc_raw)
139
154
  current_adc_raw = min((2**18) - 1, current_adc_raw)
140
155