shepherd-core 2023.12.1__py3-none-any.whl → 2024.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. shepherd_core/__init__.py +5 -4
  2. shepherd_core/calibration_hw_def.py +9 -1
  3. shepherd_core/commons.py +2 -0
  4. shepherd_core/data_models/__init__.py +11 -0
  5. shepherd_core/data_models/base/__init__.py +4 -1
  6. shepherd_core/data_models/base/cal_measurement.py +18 -6
  7. shepherd_core/data_models/base/calibration.py +41 -16
  8. shepherd_core/data_models/base/content.py +20 -5
  9. shepherd_core/data_models/base/shepherd.py +23 -12
  10. shepherd_core/data_models/base/timezone.py +5 -0
  11. shepherd_core/data_models/base/wrapper.py +3 -3
  12. shepherd_core/data_models/content/__init__.py +5 -4
  13. shepherd_core/data_models/content/_external_fixtures.yaml +32 -16
  14. shepherd_core/data_models/content/energy_environment.py +7 -5
  15. shepherd_core/data_models/content/energy_environment_fixture.yaml +3 -0
  16. shepherd_core/data_models/content/firmware.py +12 -5
  17. shepherd_core/data_models/content/firmware_datatype.py +7 -0
  18. shepherd_core/data_models/content/virtual_harvester.py +25 -20
  19. shepherd_core/data_models/content/virtual_harvester_fixture.yaml +1 -0
  20. shepherd_core/data_models/content/virtual_source.py +40 -23
  21. shepherd_core/data_models/content/virtual_source_fixture.yaml +1 -0
  22. shepherd_core/data_models/experiment/__init__.py +5 -4
  23. shepherd_core/data_models/experiment/experiment.py +16 -15
  24. shepherd_core/data_models/experiment/observer_features.py +18 -12
  25. shepherd_core/data_models/experiment/target_config.py +11 -7
  26. shepherd_core/data_models/readme.md +88 -0
  27. shepherd_core/data_models/task/__init__.py +10 -3
  28. shepherd_core/data_models/task/emulation.py +9 -6
  29. shepherd_core/data_models/task/firmware_mod.py +4 -2
  30. shepherd_core/data_models/task/harvest.py +5 -4
  31. shepherd_core/data_models/task/observer_tasks.py +4 -2
  32. shepherd_core/data_models/task/programming.py +3 -1
  33. shepherd_core/data_models/task/testbed_tasks.py +10 -4
  34. shepherd_core/data_models/testbed/__init__.py +5 -2
  35. shepherd_core/data_models/testbed/cape.py +8 -6
  36. shepherd_core/data_models/testbed/gpio.py +11 -9
  37. shepherd_core/data_models/testbed/mcu.py +10 -10
  38. shepherd_core/data_models/testbed/observer.py +10 -5
  39. shepherd_core/data_models/testbed/observer_fixture.yaml +23 -22
  40. shepherd_core/data_models/testbed/target.py +5 -3
  41. shepherd_core/data_models/testbed/target_fixture.yaml +11 -11
  42. shepherd_core/data_models/testbed/testbed.py +6 -3
  43. shepherd_core/decoder_waveform/__init__.py +2 -0
  44. shepherd_core/decoder_waveform/uart.py +44 -25
  45. shepherd_core/fw_tools/__init__.py +2 -0
  46. shepherd_core/fw_tools/converter.py +20 -9
  47. shepherd_core/fw_tools/converter_elf.py +3 -0
  48. shepherd_core/fw_tools/patcher.py +16 -4
  49. shepherd_core/fw_tools/validation.py +25 -5
  50. shepherd_core/inventory/__init__.py +66 -6
  51. shepherd_core/inventory/python.py +4 -0
  52. shepherd_core/inventory/system.py +13 -1
  53. shepherd_core/inventory/target.py +4 -0
  54. shepherd_core/logger.py +5 -0
  55. shepherd_core/reader.py +44 -26
  56. shepherd_core/testbed_client/__init__.py +2 -0
  57. shepherd_core/testbed_client/cache_path.py +17 -0
  58. shepherd_core/testbed_client/client.py +14 -8
  59. shepherd_core/testbed_client/fixtures.py +30 -11
  60. shepherd_core/testbed_client/user_model.py +13 -6
  61. shepherd_core/vsource/__init__.py +2 -0
  62. shepherd_core/vsource/virtual_converter_model.py +11 -4
  63. shepherd_core/vsource/virtual_harvester_model.py +8 -1
  64. shepherd_core/vsource/virtual_source_model.py +10 -5
  65. shepherd_core/writer.py +28 -20
  66. {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/METADATA +50 -34
  67. shepherd_core-2024.4.2.dist-info/RECORD +75 -0
  68. {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/WHEEL +1 -1
  69. {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/top_level.txt +0 -1
  70. shepherd_core-2023.12.1.dist-info/RECORD +0 -117
  71. tests/__init__.py +0 -0
  72. tests/conftest.py +0 -64
  73. tests/data_models/__init__.py +0 -0
  74. tests/data_models/conftest.py +0 -14
  75. tests/data_models/example_cal_data.yaml +0 -31
  76. tests/data_models/example_cal_data_faulty.yaml +0 -29
  77. tests/data_models/example_cal_meas.yaml +0 -178
  78. tests/data_models/example_cal_meas_faulty1.yaml +0 -142
  79. tests/data_models/example_cal_meas_faulty2.yaml +0 -136
  80. tests/data_models/example_config_emulator.yaml +0 -41
  81. tests/data_models/example_config_experiment.yaml +0 -16
  82. tests/data_models/example_config_experiment_alternative.yaml +0 -14
  83. tests/data_models/example_config_harvester.yaml +0 -15
  84. tests/data_models/example_config_testbed.yaml +0 -26
  85. tests/data_models/example_config_virtsource.yaml +0 -78
  86. tests/data_models/test_base_models.py +0 -205
  87. tests/data_models/test_content_fixtures.py +0 -41
  88. tests/data_models/test_content_models.py +0 -282
  89. tests/data_models/test_examples.py +0 -48
  90. tests/data_models/test_experiment_models.py +0 -277
  91. tests/data_models/test_task_generation.py +0 -52
  92. tests/data_models/test_task_models.py +0 -131
  93. tests/data_models/test_testbed_fixtures.py +0 -47
  94. tests/data_models/test_testbed_models.py +0 -187
  95. tests/decoder_waveform/__init__.py +0 -0
  96. tests/decoder_waveform/test_decoder.py +0 -34
  97. tests/fw_tools/__init__.py +0 -0
  98. tests/fw_tools/conftest.py +0 -5
  99. tests/fw_tools/test_converter.py +0 -76
  100. tests/fw_tools/test_patcher.py +0 -66
  101. tests/fw_tools/test_validation.py +0 -56
  102. tests/inventory/__init__.py +0 -0
  103. tests/inventory/test_inventory.py +0 -20
  104. tests/test_cal_hw.py +0 -34
  105. tests/test_examples.py +0 -40
  106. tests/test_logger.py +0 -15
  107. tests/test_reader.py +0 -283
  108. tests/test_writer.py +0 -169
  109. tests/testbed_client/__init__.py +0 -0
  110. tests/vsource/__init__.py +0 -0
  111. tests/vsource/conftest.py +0 -49
  112. tests/vsource/test_converter.py +0 -161
  113. tests/vsource/test_harvester.py +0 -73
  114. tests/vsource/test_z.py +0 -5
  115. /shepherd_core/data_models/{doc_virtual_source.py → virtual_source_doc.txt} +0 -0
  116. {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/zip-safe +0 -0
@@ -1,3 +1,5 @@
1
+ """meta-data representation of a testbed-component (physical object)."""
2
+
1
3
  from datetime import datetime
2
4
  from typing import Optional
3
5
 
@@ -24,9 +26,9 @@ MACStr = Annotated[
24
26
 
25
27
 
26
28
  class Observer(ShpModel, title="Shepherd-Sheep"):
27
- """meta-data representation of a testbed-component (physical object)"""
29
+ """meta-data representation of a testbed-component (physical object)."""
28
30
 
29
- id: IdInt # noqa: A003
31
+ id: IdInt
30
32
  name: NameStr
31
33
  description: SafeStr
32
34
  comment: Optional[SafeStr] = None
@@ -63,7 +65,8 @@ class Observer(ShpModel, title="Shepherd-Sheep"):
63
65
  has_cape = self.cape is not None
64
66
  has_target = (self.target_a is not None) or (self.target_b is not None)
65
67
  if not has_cape and has_target:
66
- raise ValueError(f"Observer '{self.name}' is faulty -> has targets but no cape")
68
+ msg = f"Observer '{self.name}' is faulty -> has targets but no cape"
69
+ raise ValueError(msg)
67
70
  return self
68
71
 
69
72
  def has_target(self, target_id: int) -> bool:
@@ -79,7 +82,8 @@ class Observer(ShpModel, title="Shepherd-Sheep"):
79
82
  return TargetPort.A
80
83
  if self.target_b is not None and target_id == self.target_b.id:
81
84
  return TargetPort.B
82
- raise ValueError(f"Target-ID {target_id} was not found in Observer '{self.name}'")
85
+ msg = f"Target-ID {target_id} was not found in Observer '{self.name}'"
86
+ raise KeyError(msg)
83
87
 
84
88
  def get_target(self, target_id: int) -> Target:
85
89
  if self.has_target(target_id):
@@ -87,4 +91,5 @@ class Observer(ShpModel, title="Shepherd-Sheep"):
87
91
  return self.target_a
88
92
  if self.target_b is not None and target_id == self.target_b.id:
89
93
  return self.target_b
90
- raise ValueError(f"Target-ID {target_id} was not found in Observer '{self.name}'")
94
+ msg = f"Target-ID {target_id} was not found in Observer '{self.name}'"
95
+ raise KeyError(msg)
@@ -24,9 +24,9 @@
24
24
  name: sheep01
25
25
  ip: 192.168.165.201
26
26
  mac: 18:62:E4:E4:41:8D
27
- room: II56
28
- eth_port: J24
29
- description: lower mid on west-side
27
+ room: II70
28
+ eth_port: B22
29
+ description: mid-mid in room
30
30
  cape:
31
31
  name: cape51
32
32
  target_a:
@@ -38,9 +38,9 @@
38
38
  name: sheep02
39
39
  ip: 192.168.165.202
40
40
  mac: 18:62:E4:D0:DE:3F
41
- room: II57
42
- eth_port: J6
43
- description: mid on east-side
41
+ room: II66
42
+ eth_port: A18
43
+ description: south-west corner
44
44
  cape:
45
45
  name: cape52
46
46
  target_a:
@@ -52,9 +52,9 @@
52
52
  name: sheep03
53
53
  ip: 192.168.165.203
54
54
  mac: 18:62:E4:D0:9E:6A
55
- room: II59
56
- eth_port: I14
57
- description: north-west-corner
55
+ room: II69
56
+ eth_port: B10
57
+ description: north-east-corner
58
58
  cape:
59
59
  name: cape63
60
60
  target_a:
@@ -66,8 +66,8 @@
66
66
  name: sheep04
67
67
  ip: 192.168.165.204
68
68
  mac: 18:62:E4:D0:61:4D
69
- room: II61
70
- eth_port: H18
69
+ room: II72
70
+ eth_port: C6
71
71
  description: mid on east-side
72
72
  cape:
73
73
  name: cape54
@@ -80,9 +80,9 @@
80
80
  name: sheep05
81
81
  ip: 192.168.165.205
82
82
  mac: 18:62:E4:E3:6E:13
83
- room: II62
84
- eth_port: H14
85
- description: mid on south-side
83
+ room: II75
84
+ eth_port: D10
85
+ description: south-west corner
86
86
  cape:
87
87
  name: cape55
88
88
  target_a:
@@ -125,13 +125,14 @@
125
125
  ip: 192.168.165.208
126
126
  mac: 18:62:E4:D0:E5:FB
127
127
  room: II69
128
- eth_port: B10
128
+ eth_port: TBD
129
129
  description: north-east-corner
130
130
  cape:
131
131
  name: cape58
132
132
  target_a:
133
133
  name: nRF52_FRAM_016
134
134
  created: 2023-09-22 12:12:12
135
+ active: false # TODO: cape is broken for now
135
136
  - datatype: observer
136
137
  parameters:
137
138
  id: 9
@@ -168,7 +169,7 @@
168
169
  mac: 18:62:E4:E4:0E:5D
169
170
  room: II69
170
171
  eth_port: B14
171
- description: mid on east-side
172
+ description: lower mid on east-side
172
173
  cape:
173
174
  name: cape61
174
175
  target_a:
@@ -180,9 +181,9 @@
180
181
  name: sheep12
181
182
  ip: 192.168.165.212
182
183
  mac: 18:62:E4:D0:AA:F7
183
- room: II59
184
- eth_port: I10
185
- description: south-west-corner
184
+ room: II71
185
+ eth_port: B24
186
+ description: north-east-corner
186
187
  cape:
187
188
  name: cape62
188
189
  target_a:
@@ -197,7 +198,7 @@
197
198
  room: II64C
198
199
  eth_port: A4
199
200
  description: north-west-corner
200
- comment: might temporary be in II70
201
+ comment:
201
202
  cape:
202
203
  name: cape60
203
204
  target_a:
@@ -211,8 +212,8 @@
211
212
  mac: 18:62:E4:E3:89:43
212
213
  room: II64B
213
214
  eth_port: L24
214
- description: north-east-corner in cupboard
215
- comment: might temporary be in II70
215
+ description: north-east-corner in cupboard, elevated
216
+ comment:
216
217
  cape:
217
218
  name: cape64
218
219
  target_a:
@@ -1,3 +1,5 @@
1
+ """meta-data representation of a testbed-component (physical object)."""
2
+
1
3
  from datetime import datetime
2
4
  from typing import Optional
3
5
  from typing import Union
@@ -19,9 +21,9 @@ MCUPort = Annotated[int, Field(ge=1, le=2)]
19
21
 
20
22
 
21
23
  class Target(ShpModel, title="Target Node (DuT)"):
22
- """meta-data representation of a testbed-component (physical object)"""
24
+ """meta-data representation of a testbed-component (physical object)."""
23
25
 
24
- id: IdInt # noqa: A003
26
+ id: IdInt
25
27
  name: NameStr
26
28
  version: NameStr
27
29
  description: SafeStr
@@ -36,7 +38,7 @@ class Target(ShpModel, title="Target Node (DuT)"):
36
38
  mcu1: Union[MCU, NameStr]
37
39
  mcu2: Union[MCU, NameStr, None] = None
38
40
 
39
- # TODO programming pins per mcu should be here (or better in Cape)
41
+ # TODO: programming pins per mcu should be here (or better in Cape)
40
42
 
41
43
  def __str__(self) -> str:
42
44
  return self.name
@@ -16,37 +16,37 @@
16
16
  - datatype: target
17
17
  parameters:
18
18
  inherit_from: nRF52_FRAM_001
19
- id: 5
19
+ id: 10
20
20
  name: nRF52_FRAM_002
21
21
  comment: Test3 21nA sleep
22
22
  - datatype: target
23
23
  parameters:
24
24
  inherit_from: nRF52_FRAM_001
25
- id: 3
25
+ id: 7
26
26
  name: nRF52_FRAM_003
27
27
  comment: Test3 21nA sleep
28
28
  - datatype: target
29
29
  parameters:
30
30
  inherit_from: nRF52_FRAM_001
31
- id: 2
31
+ id: 3
32
32
  name: nRF52_FRAM_004
33
33
  comment: Test3 21nA sleep
34
34
  - datatype: target
35
35
  parameters:
36
36
  inherit_from: nRF52_FRAM_001
37
- id: 13
37
+ id: 11
38
38
  name: nRF52_FRAM_005
39
39
  comment: Test3 21nA sleep, changed Antenna to lambda/4
40
40
  - datatype: target
41
41
  parameters:
42
42
  inherit_from: nRF52_FRAM_001
43
- id: 14
43
+ id: 12
44
44
  name: nRF52_FRAM_006
45
45
  comment: Test3 21nA sleep, changed Antenna to lambda/4
46
46
  - datatype: target
47
47
  parameters:
48
48
  inherit_from: nRF52_FRAM_001
49
- id: 12
49
+ id: 8
50
50
  name: nRF52_FRAM_007
51
51
  comment: msp-programming is failing -> defective, msp removed & changed Antenna to lambda/4
52
52
  mcu2: null
@@ -54,7 +54,7 @@
54
54
  - datatype: target
55
55
  parameters:
56
56
  inherit_from: nRF52_FRAM_001
57
- id: 8
57
+ id: 9
58
58
  name: nRF52_FRAM_008
59
59
  comment: msp-programming is failing -> defective, msp removed & changed Antenna to lambda/4
60
60
  mcu2: null
@@ -62,13 +62,13 @@
62
62
  - datatype: target
63
63
  parameters:
64
64
  inherit_from: nRF52_FRAM_001
65
- id: 10
65
+ id: 5
66
66
  name: nRF52_FRAM_009
67
67
  comment: Test3 21nA sleep
68
68
  - datatype: target
69
69
  parameters:
70
70
  inherit_from: nRF52_FRAM_001
71
- id: 11
71
+ id: 2
72
72
  name: nRF52_FRAM_010
73
73
  comment: Test3 21nA sleep
74
74
  - datatype: target
@@ -107,13 +107,13 @@
107
107
  - datatype: target
108
108
  parameters:
109
109
  inherit_from: nRF52_FRAM_001
110
- id: 9
110
+ id: 666
111
111
  name: nRF52_FRAM_016
112
112
  comment: Test3 21nA sleep
113
113
  - datatype: target
114
114
  parameters:
115
115
  inherit_from: nRF52_FRAM_001
116
- id: 7
116
+ id: 13
117
117
  name: nRF52_FRAM_017
118
118
  comment: Test3 21nA sleep, nrf was miss-aligned -> hot air rework
119
119
  - datatype: target
@@ -1,3 +1,5 @@
1
+ """meta-data representation of a testbed-component (physical object)."""
2
+
1
3
  from datetime import timedelta
2
4
  from pathlib import Path
3
5
  from typing import List
@@ -18,9 +20,9 @@ from .observer import Observer
18
20
 
19
21
 
20
22
  class Testbed(ShpModel):
21
- """meta-data representation of a testbed-component (physical object)"""
23
+ """meta-data representation of a testbed-component (physical object)."""
22
24
 
23
- id: IdInt # noqa: A003
25
+ id: IdInt
24
26
  name: NameStr
25
27
  description: SafeStr
26
28
  comment: Optional[SafeStr] = None
@@ -87,4 +89,5 @@ class Testbed(ShpModel):
87
89
  continue
88
90
  if _observer.has_target(target_id):
89
91
  return _observer
90
- raise ValueError(f"Target-ID {target_id} was not found in Testbed '{self.name}'")
92
+ msg = f"Target-ID {target_id} was not found in Testbed '{self.name}'"
93
+ raise ValueError(msg)
@@ -1,3 +1,5 @@
1
+ """Module for making data in waveforms accessible by decoding protocols."""
2
+
1
3
  from .uart import Uart
2
4
 
3
5
  __all__ = ["Uart"]
@@ -1,4 +1,7 @@
1
- """UART - Decoder - Features:
1
+ """UART - Decoder.
2
+
3
+ Features:
4
+
2
5
  - 1 bit Start (LOW)
3
6
  - 1 .. **8** .. 64 bit data-frame
4
7
  - **1**, 1.5, 2 bit stop (HIGH) - don't care?
@@ -7,16 +10,17 @@
7
10
  - **no** inversion
8
11
 
9
12
  Todo:
10
- ----
11
- - detect bitOrder
12
- - detect dataframe length
13
- - detect parity
13
+ - detect bitOrder
14
+ - detect dataframe length
15
+ - detect parity
14
16
 
15
17
  More Info:
18
+
16
19
  https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter
17
20
  https://sigrok.org/wiki/Protocol_decoder:Uart
18
21
 
19
22
  """
23
+
20
24
  from enum import Enum
21
25
  from pathlib import Path
22
26
  from typing import Optional
@@ -28,19 +32,23 @@ from ..logger import logger
28
32
 
29
33
 
30
34
  class Parity(str, Enum):
35
+ """Options for parity-property of UART data-frame."""
36
+
31
37
  no = "no"
32
38
  even = "odd"
33
39
  odd = "even"
34
40
 
35
41
 
36
42
  class BitOrder(str, Enum):
37
- msb = "msb"
38
- msb_first = "msb"
39
- lsb = "lsb"
40
- lsb_first = "lsb"
43
+ """Options for bit-order-property of UART data-frame."""
44
+
45
+ msb = msb_first = "msb"
46
+ lsb = lsb_first = "lsb"
41
47
 
42
48
 
43
49
  class Uart:
50
+ """Specialized UART decoder."""
51
+
44
52
  def __init__(
45
53
  self,
46
54
  content: Union[Path, np.ndarray],
@@ -50,10 +58,14 @@ class Uart:
50
58
  parity: Optional[Parity] = Parity.no,
51
59
  bit_order: Optional[BitOrder] = BitOrder.lsb_first,
52
60
  ) -> None:
53
- """Provide a file with two columns:
54
- - timestamp (seconds with fraction) and signal (can be analog).
55
- - class-parameters that are None (above) get auto-detected
56
- (some detectors still missing)
61
+ """Provide a file with two columns: TS & Signal.
62
+
63
+ Constraints:
64
+ - timestamp -> seconds with fraction
65
+ - signal -> can be analog or digital
66
+
67
+ Note: class-parameters that are None (above) get auto-detected
68
+ (some detectors still missing).
57
69
  """
58
70
  if isinstance(content, Path):
59
71
  self.events_sig: np.ndarray = np.loadtxt(content.as_posix(), delimiter=",", skiprows=1)
@@ -108,7 +120,7 @@ class Uart:
108
120
  self.text: Optional[str] = None
109
121
 
110
122
  def _convert_analog2digital(self, *, invert: bool = False) -> None:
111
- """Divide dimension in two, divided by mean-value"""
123
+ """Divide dimension in two, divided by mean-value."""
112
124
  data = self.events_sig[:, 1]
113
125
  mean = np.mean(data)
114
126
  if invert:
@@ -117,7 +129,7 @@ class Uart:
117
129
  self.events_sig[:, 1] = data >= mean
118
130
 
119
131
  def _filter_redundant_states(self) -> None:
120
- """Sum of two sequential states is always 1 (True + False) if alternating"""
132
+ """Sum of two sequential states is always 1 (True + False) if alternating."""
121
133
  data_0 = self.events_sig[:, 1]
122
134
  data_1 = np.concatenate([[not data_0[0]], data_0[:-1]])
123
135
  data_f = data_0 + data_1
@@ -131,7 +143,7 @@ class Uart:
131
143
  )
132
144
 
133
145
  def _add_duration(self) -> None:
134
- """Calculate third column -> duration of state in [baud-ticks]"""
146
+ """Calculate third column -> duration of state in [baud-ticks]."""
135
147
  if self.events_sig.shape[1] > 2:
136
148
  logger.warning("Tried to add state-duration, but it seems already present")
137
149
  return
@@ -142,7 +154,8 @@ class Uart:
142
154
  self.events_sig = np.append(self.events_sig[:-1, :], dur_steps / self.dur_tick, axis=1)
143
155
 
144
156
  def detect_inversion(self) -> bool:
145
- """Analyze bit-state during long pauses (unchanged states)
157
+ """Analyze bit-state during long pauses (unchanged states).
158
+
146
159
  - pause should be HIGH for non-inverted mode (default)
147
160
  - assumes max frame size of 64 bit + x for safety
148
161
  """
@@ -156,7 +169,7 @@ class Uart:
156
169
  return mean_state < 0.5
157
170
 
158
171
  def detect_baud_rate(self) -> int:
159
- """Analyze the smallest step"""
172
+ """Analyze the smallest step."""
160
173
  events = self.events_sig[:1000, :] # speedup for large datasets
161
174
  dur_steps = events[1:, 0] - events[:-1, 0]
162
175
  def_step = np.percentile(dur_steps[dur_steps > 0], 10)
@@ -166,23 +179,29 @@ class Uart:
166
179
  return round(1 / mean_tick)
167
180
 
168
181
  def detect_half_stop(self) -> bool:
169
- """Looks into the spacing between time-steps"""
182
+ """Look into the spacing between time-steps to determine use of half stop."""
170
183
  events = self.events_sig[:1000, :] # speedup for large datasets
171
184
  return np.sum((events > 1.333 * self.dur_tick) & (events < 1.667 * self.dur_tick)) > 0
172
185
 
173
186
  def detect_dataframe_length(self) -> int:
174
- """Look after longest pauses
175
- - accumulate steps until a state with uneven step-size is found
187
+ """Try to determine length of dataframe.
188
+
189
+ Algo will look for longest pauses & accumulate steps until
190
+ a state with uneven step-size is found.
176
191
  """
192
+ raise NotImplementedError
177
193
 
178
194
  def get_symbols(self, *, force_redo: bool = False) -> np.ndarray:
179
- """Ways to detect EOF:
195
+ """Extract symbols from events.
196
+
197
+ Ways to detect EOF:
180
198
  - long pause on HIGH
181
199
  - off_tick pause on high
182
200
  - bit_pos > max
201
+
183
202
  # TODO:
184
203
  - slowest FN -> speedup with numba or parallelization?
185
- - dset could be divided (long pauses) and threaded for speedup
204
+ - dset could be divided (long pauses) and threaded for speedup.
186
205
  """
187
206
  if force_redo:
188
207
  self.events_symbols = None
@@ -234,7 +253,7 @@ class Uart:
234
253
  return self.events_symbols
235
254
 
236
255
  def get_lines(self, *, force_redo: bool = False) -> np.ndarray:
237
- """Timestamped symbols to line, cut at \r, \r\n or \n"""
256
+ r"""Timestamped symbols to line, cut at \r, \r\n or \n."""
238
257
  if force_redo:
239
258
  self.events_lines = None
240
259
  if self.events_lines is not None:
@@ -266,7 +285,7 @@ class Uart:
266
285
  return self.events_lines
267
286
 
268
287
  def get_text(self, *, force_redo: bool = False) -> str:
269
- """Remove timestamps and just return the whole string"""
288
+ """Remove timestamps and just return the whole string."""
270
289
  if force_redo:
271
290
  self.text = None
272
291
  if self.text is not None:
@@ -1,3 +1,5 @@
1
+ """Helper-functions for handling firmware around the testbed."""
2
+
1
3
  from typing import Any
2
4
 
3
5
  try:
@@ -1,3 +1,5 @@
1
+ """Content-Converters for firmwares."""
2
+
1
3
  import base64
2
4
  import hashlib
3
5
  import shutil
@@ -15,21 +17,25 @@ from .validation import is_hex
15
17
 
16
18
  @validate_call
17
19
  def firmware_to_hex(file_path: Path) -> Path:
18
- """Generic converter that handles ELF & HEX"""
20
+ """Convert ELF-Files to HEX.
21
+
22
+ Generic converter that handles ELF & HEX.
23
+ """
19
24
  if not file_path.is_file():
20
- raise ValueError("Fn needs an existing file as input")
25
+ raise FileNotFoundError("Fn needs an existing file as input")
21
26
  if is_elf(file_path):
22
27
  return elf_to_hex(file_path)
23
28
  if is_hex(file_path):
24
29
  return file_path
25
- raise ValueError("FW2Hex: unknown file '%s', it should be ELF or HEX", file_path.name)
30
+ raise FileNotFoundError("FW2Hex: unknown file '%s', it should be ELF or HEX", file_path.name)
26
31
 
27
32
 
28
33
  @validate_call
29
34
  def file_to_base64(file_path: Path) -> str:
30
- """Compress and encode content of file
35
+ """Compress and encode content of file.
36
+
31
37
  - base64 adds ~33 % overhead
32
- - zstd compression ~ 1:3
38
+ - zstd compression reduces to ~ 1:3
33
39
  """
34
40
  if not file_path.is_file():
35
41
  raise ValueError("Fn needs an existing file as input")
@@ -41,9 +47,10 @@ def file_to_base64(file_path: Path) -> str:
41
47
 
42
48
  @validate_call
43
49
  def base64_to_file(content: str, file_path: Path) -> None:
44
- """DeCompress and decode Content of file
50
+ """DeCompress and decode Content of file.
51
+
45
52
  - base64 adds ~33 % overhead
46
- - zstd compression ~ 1:3
53
+ - zstd compression reduces to ~ 1:3
47
54
  """
48
55
  file_cmpress = base64.b64decode(content)
49
56
  file_content = zstd.ZstdDecompressor().decompress(file_cmpress)
@@ -55,6 +62,7 @@ def base64_to_file(content: str, file_path: Path) -> None:
55
62
 
56
63
  @validate_call
57
64
  def file_to_hash(file_path: Path) -> str:
65
+ """Convert file-content to hash-value."""
58
66
  if not file_path.is_file():
59
67
  raise ValueError("Fn needs an existing file as input")
60
68
  with file_path.resolve().open("rb") as file:
@@ -64,6 +72,7 @@ def file_to_hash(file_path: Path) -> str:
64
72
 
65
73
  @validate_call
66
74
  def base64_to_hash(content: str) -> str:
75
+ """Convert base64-content to hash-value."""
67
76
  file_cmpress = base64.b64decode(content)
68
77
  file_content = zstd.ZstdDecompressor().decompress(file_cmpress)
69
78
  return hashlib.sha3_224(file_content).hexdigest()
@@ -71,8 +80,10 @@ def base64_to_hash(content: str) -> str:
71
80
 
72
81
  @validate_call
73
82
  def extract_firmware(data: Union[str, Path], data_type: FirmwareDType, file_path: Path) -> Path:
74
- """- base64-string will be transformed into file
75
- - if data is a path the file will be copied to the destination
83
+ """Make embedded firmware-data usable in filesystem.
84
+
85
+ - base64-string will be transformed to file
86
+ - if data is a path the file will be copied to the destination.
76
87
  """
77
88
  if data_type == FirmwareDType.base64_elf:
78
89
  file = file_path.with_suffix(".elf")
@@ -1,3 +1,5 @@
1
+ """Converter for ELF-files."""
2
+
1
3
  import subprocess
2
4
  from pathlib import Path
3
5
  from typing import Optional
@@ -9,6 +11,7 @@ from pydantic import validate_call
9
11
 
10
12
  @validate_call
11
13
  def elf_to_hex(file_elf: Path, file_hex: Optional[Path] = None) -> Path:
14
+ """Convert ELF to hex file using objcopy."""
12
15
  if not file_elf.is_file():
13
16
  raise ValueError("Fn needs an existing file as input")
14
17
  if not file_hex:
@@ -1,3 +1,5 @@
1
+ """Read and modify symbols in ELF-files."""
2
+
1
3
  from pathlib import Path
2
4
  from typing import Optional
3
5
 
@@ -22,6 +24,7 @@ except ImportError as e:
22
24
 
23
25
  @validate_call
24
26
  def find_symbol(file_elf: Path, symbol: str) -> bool:
27
+ """Find a symbol in the ELF file."""
25
28
  if symbol is None or not is_elf(file_elf):
26
29
  return False
27
30
  if ELF is None:
@@ -47,7 +50,10 @@ def find_symbol(file_elf: Path, symbol: str) -> bool:
47
50
 
48
51
  @validate_call
49
52
  def read_symbol(file_elf: Path, symbol: str, length: int = uid_len_default) -> Optional[int]:
50
- """Interpreted as int"""
53
+ """Read value of symbol in ELF-File.
54
+
55
+ Will be interpreted as int.
56
+ """
51
57
  if not find_symbol(file_elf, symbol):
52
58
  return None
53
59
  if ELF is None:
@@ -60,10 +66,12 @@ def read_symbol(file_elf: Path, symbol: str, length: int = uid_len_default) -> O
60
66
 
61
67
 
62
68
  def read_uid(file_elf: Path) -> Optional[int]:
69
+ """Read value of UID-symbol for shepherd testbed."""
63
70
  return read_symbol(file_elf, symbol=uid_str_default, length=uid_len_default)
64
71
 
65
72
 
66
73
  def read_arch(file_elf: Path) -> Optional[str]:
74
+ """Determine chip-architecture from elf-metadata."""
67
75
  if not is_elf(file_elf):
68
76
  return None
69
77
  if ELF is None:
@@ -83,9 +91,12 @@ def modify_symbol_value(
83
91
  *,
84
92
  overwrite: bool = False,
85
93
  ) -> Optional[Path]:
86
- """Replaces value of symbol in ELF-File, hardcoded for uint16_t (2 byte)
87
- testbed uses FN to patch firmware with custom target-ID
88
- NOTE: can overwrite provided file
94
+ """Replace value of uint16-symbol in ELF-File.
95
+
96
+ Hardcoded for uint16_t (2 byte).
97
+ The testbed uses this to patch firmware with custom target-ID.
98
+
99
+ NOTE: can overwrite provided file.
89
100
 
90
101
  """
91
102
  if not find_symbol(file_elf, symbol):
@@ -121,4 +132,5 @@ def modify_symbol_value(
121
132
 
122
133
 
123
134
  def modify_uid(file_elf: Path, value: int) -> Optional[Path]:
135
+ """Replace value of UID-symbol for shepherd testbed."""
124
136
  return modify_symbol_value(file_elf, symbol=uid_str_default, value=value, overwrite=True)