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.
- shepherd_core/__init__.py +5 -4
- shepherd_core/calibration_hw_def.py +9 -1
- shepherd_core/commons.py +2 -0
- shepherd_core/data_models/__init__.py +11 -0
- shepherd_core/data_models/base/__init__.py +4 -1
- shepherd_core/data_models/base/cal_measurement.py +18 -6
- shepherd_core/data_models/base/calibration.py +41 -16
- shepherd_core/data_models/base/content.py +20 -5
- shepherd_core/data_models/base/shepherd.py +23 -12
- shepherd_core/data_models/base/timezone.py +5 -0
- shepherd_core/data_models/base/wrapper.py +3 -3
- shepherd_core/data_models/content/__init__.py +5 -4
- shepherd_core/data_models/content/_external_fixtures.yaml +32 -16
- shepherd_core/data_models/content/energy_environment.py +7 -5
- shepherd_core/data_models/content/energy_environment_fixture.yaml +3 -0
- shepherd_core/data_models/content/firmware.py +12 -5
- shepherd_core/data_models/content/firmware_datatype.py +7 -0
- shepherd_core/data_models/content/virtual_harvester.py +25 -20
- shepherd_core/data_models/content/virtual_harvester_fixture.yaml +1 -0
- shepherd_core/data_models/content/virtual_source.py +40 -23
- shepherd_core/data_models/content/virtual_source_fixture.yaml +1 -0
- shepherd_core/data_models/experiment/__init__.py +5 -4
- shepherd_core/data_models/experiment/experiment.py +16 -15
- shepherd_core/data_models/experiment/observer_features.py +18 -12
- shepherd_core/data_models/experiment/target_config.py +11 -7
- shepherd_core/data_models/readme.md +88 -0
- shepherd_core/data_models/task/__init__.py +10 -3
- shepherd_core/data_models/task/emulation.py +9 -6
- shepherd_core/data_models/task/firmware_mod.py +4 -2
- shepherd_core/data_models/task/harvest.py +5 -4
- shepherd_core/data_models/task/observer_tasks.py +4 -2
- shepherd_core/data_models/task/programming.py +3 -1
- shepherd_core/data_models/task/testbed_tasks.py +10 -4
- shepherd_core/data_models/testbed/__init__.py +5 -2
- shepherd_core/data_models/testbed/cape.py +8 -6
- shepherd_core/data_models/testbed/gpio.py +11 -9
- shepherd_core/data_models/testbed/mcu.py +10 -10
- shepherd_core/data_models/testbed/observer.py +10 -5
- shepherd_core/data_models/testbed/observer_fixture.yaml +23 -22
- shepherd_core/data_models/testbed/target.py +5 -3
- shepherd_core/data_models/testbed/target_fixture.yaml +11 -11
- shepherd_core/data_models/testbed/testbed.py +6 -3
- shepherd_core/decoder_waveform/__init__.py +2 -0
- shepherd_core/decoder_waveform/uart.py +44 -25
- shepherd_core/fw_tools/__init__.py +2 -0
- shepherd_core/fw_tools/converter.py +20 -9
- shepherd_core/fw_tools/converter_elf.py +3 -0
- shepherd_core/fw_tools/patcher.py +16 -4
- shepherd_core/fw_tools/validation.py +25 -5
- shepherd_core/inventory/__init__.py +66 -6
- shepherd_core/inventory/python.py +4 -0
- shepherd_core/inventory/system.py +13 -1
- shepherd_core/inventory/target.py +4 -0
- shepherd_core/logger.py +5 -0
- shepherd_core/reader.py +44 -26
- shepherd_core/testbed_client/__init__.py +2 -0
- shepherd_core/testbed_client/cache_path.py +17 -0
- shepherd_core/testbed_client/client.py +14 -8
- shepherd_core/testbed_client/fixtures.py +30 -11
- shepherd_core/testbed_client/user_model.py +13 -6
- shepherd_core/vsource/__init__.py +2 -0
- shepherd_core/vsource/virtual_converter_model.py +11 -4
- shepherd_core/vsource/virtual_harvester_model.py +8 -1
- shepherd_core/vsource/virtual_source_model.py +10 -5
- shepherd_core/writer.py +28 -20
- {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/METADATA +50 -34
- shepherd_core-2024.4.2.dist-info/RECORD +75 -0
- {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/WHEEL +1 -1
- {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/top_level.txt +0 -1
- shepherd_core-2023.12.1.dist-info/RECORD +0 -117
- tests/__init__.py +0 -0
- tests/conftest.py +0 -64
- tests/data_models/__init__.py +0 -0
- tests/data_models/conftest.py +0 -14
- tests/data_models/example_cal_data.yaml +0 -31
- tests/data_models/example_cal_data_faulty.yaml +0 -29
- tests/data_models/example_cal_meas.yaml +0 -178
- tests/data_models/example_cal_meas_faulty1.yaml +0 -142
- tests/data_models/example_cal_meas_faulty2.yaml +0 -136
- tests/data_models/example_config_emulator.yaml +0 -41
- tests/data_models/example_config_experiment.yaml +0 -16
- tests/data_models/example_config_experiment_alternative.yaml +0 -14
- tests/data_models/example_config_harvester.yaml +0 -15
- tests/data_models/example_config_testbed.yaml +0 -26
- tests/data_models/example_config_virtsource.yaml +0 -78
- tests/data_models/test_base_models.py +0 -205
- tests/data_models/test_content_fixtures.py +0 -41
- tests/data_models/test_content_models.py +0 -282
- tests/data_models/test_examples.py +0 -48
- tests/data_models/test_experiment_models.py +0 -277
- tests/data_models/test_task_generation.py +0 -52
- tests/data_models/test_task_models.py +0 -131
- tests/data_models/test_testbed_fixtures.py +0 -47
- tests/data_models/test_testbed_models.py +0 -187
- tests/decoder_waveform/__init__.py +0 -0
- tests/decoder_waveform/test_decoder.py +0 -34
- tests/fw_tools/__init__.py +0 -0
- tests/fw_tools/conftest.py +0 -5
- tests/fw_tools/test_converter.py +0 -76
- tests/fw_tools/test_patcher.py +0 -66
- tests/fw_tools/test_validation.py +0 -56
- tests/inventory/__init__.py +0 -0
- tests/inventory/test_inventory.py +0 -20
- tests/test_cal_hw.py +0 -34
- tests/test_examples.py +0 -40
- tests/test_logger.py +0 -15
- tests/test_reader.py +0 -283
- tests/test_writer.py +0 -169
- tests/testbed_client/__init__.py +0 -0
- tests/vsource/__init__.py +0 -0
- tests/vsource/conftest.py +0 -49
- tests/vsource/test_converter.py +0 -161
- tests/vsource/test_harvester.py +0 -73
- tests/vsource/test_z.py +0 -5
- /shepherd_core/data_models/{doc_virtual_source.py → virtual_source_doc.txt} +0 -0
- {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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
28
|
-
eth_port:
|
|
29
|
-
description:
|
|
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:
|
|
42
|
-
eth_port:
|
|
43
|
-
description:
|
|
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:
|
|
56
|
-
eth_port:
|
|
57
|
-
description: north-
|
|
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:
|
|
70
|
-
eth_port:
|
|
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:
|
|
84
|
-
eth_port:
|
|
85
|
-
description:
|
|
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:
|
|
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:
|
|
184
|
-
eth_port:
|
|
185
|
-
description:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
92
|
+
msg = f"Target-ID {target_id} was not found in Testbed '{self.name}'"
|
|
93
|
+
raise ValueError(msg)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
"""UART - Decoder
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
175
|
-
|
|
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
|
-
"""
|
|
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
|
+
"""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
|
-
"""
|
|
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
|
|
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
|
|
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
|
-
"""
|
|
75
|
-
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
87
|
-
|
|
88
|
-
|
|
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)
|