oscura 0.5.0__py3-none-any.whl → 0.6.0__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 (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,263 @@
1
+ """DCP (Discovery and Configuration Protocol) implementation for PROFINET.
2
+
3
+ DCP is used for device identification, configuration, and network assignment
4
+ in PROFINET networks. It runs directly on Ethernet Layer 2.
5
+
6
+ References:
7
+ PROFINET Specification V2.4 - Section 4.3 DCP
8
+ IEC 61158-6-10:2014
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, ClassVar
14
+
15
+
16
+ class DCPParser:
17
+ """DCP (Discovery and Configuration Protocol) frame parser.
18
+
19
+ DCP is used for device discovery, identification, and configuration
20
+ in PROFINET networks. It operates at Layer 2 (Ethernet).
21
+
22
+ Attributes:
23
+ SERVICE_IDS: Mapping of DCP service IDs to names.
24
+ SERVICE_TYPES: Mapping of service types to names.
25
+ OPTIONS: Mapping of DCP option codes to names.
26
+ """
27
+
28
+ # DCP Service IDs (Request/Response)
29
+ SERVICE_IDS: ClassVar[dict[int, str]] = {
30
+ 0x00: "Reserved",
31
+ 0x01: "Get",
32
+ 0x02: "Set",
33
+ 0x03: "Identify",
34
+ 0x04: "Hello",
35
+ }
36
+
37
+ # DCP Service Types
38
+ SERVICE_TYPES: ClassVar[dict[int, str]] = {
39
+ 0x00: "Request",
40
+ 0x01: "Response Success",
41
+ 0x05: "Response Not Supported",
42
+ }
43
+
44
+ # DCP Options
45
+ OPTIONS: ClassVar[dict[int, str]] = {
46
+ 0x01: "IP",
47
+ 0x02: "Device Properties",
48
+ 0x03: "DHCP",
49
+ 0x04: "Reserved",
50
+ 0x05: "Control",
51
+ 0x06: "Device Initiative",
52
+ 0xFF: "All Selector",
53
+ }
54
+
55
+ # DCP Suboptions for Device Properties (Option 0x02)
56
+ DEVICE_PROPS_SUBOPTIONS: ClassVar[dict[int, str]] = {
57
+ 0x01: "Manufacturer specific",
58
+ 0x02: "Name of Station",
59
+ 0x03: "Device ID",
60
+ 0x04: "Device Role",
61
+ 0x05: "Device Options",
62
+ 0x06: "Alias Name",
63
+ 0x07: "Device Instance",
64
+ 0x08: "OEM Device ID",
65
+ }
66
+
67
+ # DCP Suboptions for IP (Option 0x01)
68
+ IP_SUBOPTIONS: ClassVar[dict[int, str]] = {
69
+ 0x01: "MAC address",
70
+ 0x02: "IP parameter",
71
+ 0x03: "Full IP Suite",
72
+ }
73
+
74
+ @staticmethod
75
+ def parse_frame(data: bytes) -> dict[str, Any]:
76
+ """Parse complete DCP frame.
77
+
78
+ DCP Frame Format:
79
+ - Service ID (1 byte)
80
+ - Service Type (1 byte)
81
+ - XID (4 bytes) - Transaction ID
82
+ - Response Delay (2 bytes)
83
+ - DCPDataLength (2 bytes)
84
+ - Blocks (variable)
85
+
86
+ Args:
87
+ data: Raw DCP frame data (without Ethernet header).
88
+
89
+ Returns:
90
+ Parsed DCP frame data.
91
+
92
+ Raises:
93
+ ValueError: If frame is too short or invalid.
94
+
95
+ Example:
96
+ >>> parser = DCPParser()
97
+ >>> result = parser.parse_frame(dcp_data)
98
+ >>> print(f"Service: {result['service']}, Blocks: {len(result['blocks'])}")
99
+ """
100
+ if len(data) < 10:
101
+ raise ValueError(f"DCP frame too short: {len(data)} bytes (minimum 10)")
102
+
103
+ service_id = data[0]
104
+ service_type = data[1]
105
+ xid = int.from_bytes(data[2:6], "big")
106
+ response_delay = int.from_bytes(data[6:8], "big")
107
+ dcp_data_length = int.from_bytes(data[8:10], "big")
108
+
109
+ blocks = []
110
+ offset = 10
111
+
112
+ # Parse DCP blocks
113
+ while offset < len(data) and offset < 10 + dcp_data_length:
114
+ if offset + 4 > len(data):
115
+ break
116
+
117
+ option = data[offset]
118
+ suboption = data[offset + 1]
119
+ block_length = int.from_bytes(data[offset + 2 : offset + 4], "big")
120
+
121
+ if offset + 4 + block_length > len(data):
122
+ break
123
+
124
+ block_data = data[offset + 4 : offset + 4 + block_length]
125
+
126
+ # Parse block data based on option/suboption
127
+ parsed_block = DCPParser._parse_block(option, suboption, block_data)
128
+ blocks.append(parsed_block)
129
+
130
+ offset += 4 + block_length
131
+ # Align to 2-byte boundary
132
+ if block_length % 2:
133
+ offset += 1
134
+
135
+ return {
136
+ "service": DCPParser.SERVICE_IDS.get(service_id, f"Unknown (0x{service_id:02X})"),
137
+ "service_id": service_id,
138
+ "service_type": DCPParser.SERVICE_TYPES.get(
139
+ service_type, f"Unknown (0x{service_type:02X})"
140
+ ),
141
+ "service_type_raw": service_type,
142
+ "transaction_id": xid,
143
+ "response_delay": response_delay,
144
+ "data_length": dcp_data_length,
145
+ "blocks": blocks,
146
+ }
147
+
148
+ @staticmethod
149
+ def _parse_block(option: int, suboption: int, data: bytes) -> dict[str, Any]:
150
+ """Parse individual DCP block based on option/suboption.
151
+
152
+ Args:
153
+ option: DCP option code.
154
+ suboption: DCP suboption code.
155
+ data: Block data bytes.
156
+
157
+ Returns:
158
+ Parsed block data.
159
+ """
160
+ block: dict[str, Any] = {
161
+ "option": DCPParser.OPTIONS.get(option, f"Unknown (0x{option:02X})"),
162
+ "option_raw": option,
163
+ "suboption": suboption,
164
+ "length": len(data),
165
+ }
166
+
167
+ # Parse based on option type
168
+ if option == 0x01: # IP
169
+ DCPParser._parse_ip_option(block, suboption, data)
170
+ elif option == 0x02: # Device Properties
171
+ DCPParser._parse_device_properties(block, suboption, data)
172
+ else:
173
+ block["data_hex"] = data.hex()
174
+
175
+ return block
176
+
177
+ @staticmethod
178
+ def _parse_ip_option(block: dict[str, Any], suboption: int, data: bytes) -> None:
179
+ """Parse IP option suboptions.
180
+
181
+ Args:
182
+ block: Block dictionary to update.
183
+ suboption: IP suboption code.
184
+ data: Block data bytes.
185
+ """
186
+ block["suboption_name"] = DCPParser.IP_SUBOPTIONS.get(
187
+ suboption, f"Unknown (0x{suboption:02X})"
188
+ )
189
+
190
+ if suboption == 0x01 and len(data) >= 6: # MAC address
191
+ block["mac_address"] = ":".join(f"{b:02x}" for b in data[:6])
192
+ elif suboption == 0x02 and len(data) >= 12: # IP parameter
193
+ DCPParser._parse_ip_parameter(block, data)
194
+ else:
195
+ block["data_hex"] = data.hex()
196
+
197
+ @staticmethod
198
+ def _parse_ip_parameter(block: dict[str, Any], data: bytes) -> None:
199
+ """Parse IP parameter suboption (IP, subnet, gateway).
200
+
201
+ Args:
202
+ block: Block dictionary to update.
203
+ data: Block data bytes (must be >= 12 bytes).
204
+ """
205
+ block["ip_address"] = ".".join(str(b) for b in data[0:4])
206
+ block["subnet_mask"] = ".".join(str(b) for b in data[4:8])
207
+ block["gateway"] = ".".join(str(b) for b in data[8:12])
208
+
209
+ @staticmethod
210
+ def _parse_device_properties(block: dict[str, Any], suboption: int, data: bytes) -> None:
211
+ """Parse Device Properties option.
212
+
213
+ Args:
214
+ block: Block dictionary to update.
215
+ suboption: Device property suboption code.
216
+ data: Block data bytes.
217
+ """
218
+ block["suboption_name"] = DCPParser.DEVICE_PROPS_SUBOPTIONS.get(
219
+ suboption, f"Unknown (0x{suboption:02X})"
220
+ )
221
+
222
+ if suboption == 0x02: # Name of Station
223
+ DCPParser._parse_device_name(block, data)
224
+ elif suboption == 0x03 and len(data) >= 4: # Device ID
225
+ DCPParser._parse_device_id(block, data)
226
+ elif suboption == 0x04 and len(data) >= 2: # Device Role
227
+ DCPParser._parse_device_role(block, data)
228
+ else:
229
+ block["data_hex"] = data.hex()
230
+
231
+ @staticmethod
232
+ def _parse_device_name(block: dict[str, Any], data: bytes) -> None:
233
+ """Parse device name."""
234
+ try:
235
+ block["device_name"] = data.decode("ascii").rstrip("\x00")
236
+ except UnicodeDecodeError:
237
+ block["device_name_hex"] = data.hex()
238
+
239
+ @staticmethod
240
+ def _parse_device_id(block: dict[str, Any], data: bytes) -> None:
241
+ """Parse device ID (vendor + device)."""
242
+ block["vendor_id"] = int.from_bytes(data[0:2], "big")
243
+ block["device_id"] = int.from_bytes(data[2:4], "big")
244
+
245
+ @staticmethod
246
+ def _parse_device_role(block: dict[str, Any], data: bytes) -> None:
247
+ """Parse device role bitmask."""
248
+ device_role = int.from_bytes(data[0:2], "big")
249
+ block["device_role"] = device_role
250
+
251
+ roles = []
252
+ if device_role & 0x01:
253
+ roles.append("IO-Device")
254
+ if device_role & 0x02:
255
+ roles.append("IO-Controller")
256
+ if device_role & 0x04:
257
+ roles.append("IO-Multidevice")
258
+ if device_role & 0x08:
259
+ roles.append("IO-Supervisor")
260
+ block["role_names"] = roles
261
+
262
+
263
+ __all__ = ["DCPParser"]
@@ -0,0 +1,200 @@
1
+ """PTCP (Precision Transparent Clock Protocol) implementation for PROFINET.
2
+
3
+ PTCP provides time synchronization for PROFINET IRT (Isochronous Real-Time)
4
+ communication with sub-microsecond accuracy.
5
+
6
+ References:
7
+ PROFINET Specification V2.4 - Section 4.7 PTCP
8
+ IEC 61158-6-10:2014
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, ClassVar
14
+
15
+
16
+ class PTCPParser:
17
+ """PTCP (Precision Transparent Clock Protocol) frame parser.
18
+
19
+ PTCP provides precise time synchronization for PROFINET networks,
20
+ supporting IRT communication with sub-microsecond accuracy.
21
+
22
+ Attributes:
23
+ TLV_TYPES: Mapping of PTCP TLV (Type-Length-Value) types.
24
+ """
25
+
26
+ # PTCP Frame Types (based on Frame ID range 0xFF20-0xFF8F)
27
+ FRAME_TYPES: ClassVar[dict[int, str]] = {
28
+ 0xFF40: "RTSync PDU with Follow-Up",
29
+ 0xFF41: "RTSync PDU",
30
+ 0xFF42: "Follow-Up",
31
+ 0xFF43: "Delay Request",
32
+ 0xFF44: "Delay Response with Follow-Up",
33
+ 0xFF45: "Delay Response",
34
+ }
35
+
36
+ # PTCP TLV Types
37
+ TLV_TYPES: ClassVar[dict[int, str]] = {
38
+ 0x00: "End",
39
+ 0x01: "Subdomain UUID",
40
+ 0x02: "Time",
41
+ 0x03: "Time Extension",
42
+ 0x04: "Master Source Address",
43
+ 0x05: "Port Parameter",
44
+ 0x06: "Delay Parameter",
45
+ 0x07: "Port Time",
46
+ 0x08: "Optional",
47
+ }
48
+
49
+ @staticmethod
50
+ def parse_frame(frame_id: int, data: bytes) -> dict[str, Any]:
51
+ """Parse PTCP frame.
52
+
53
+ PTCP Frame Format:
54
+ - Sequence ID (2 bytes)
55
+ - Reserved (2 bytes)
56
+ - TLV blocks (variable)
57
+
58
+ Args:
59
+ frame_id: PROFINET frame ID (0xFF20-0xFF8F range).
60
+ data: Raw PTCP frame data.
61
+
62
+ Returns:
63
+ Parsed PTCP frame data.
64
+
65
+ Raises:
66
+ ValueError: If frame is too short or invalid.
67
+
68
+ Example:
69
+ >>> parser = PTCPParser()
70
+ >>> result = parser.parse_frame(0xFF41, ptcp_data)
71
+ >>> print(f"Type: {result['frame_type']}, Sequence: {result['sequence_id']}")
72
+ """
73
+ if len(data) < 4:
74
+ raise ValueError(f"PTCP frame too short: {len(data)} bytes (minimum 4)")
75
+
76
+ sequence_id = int.from_bytes(data[0:2], "big")
77
+ reserved = int.from_bytes(data[2:4], "big")
78
+
79
+ result: dict[str, Any] = {
80
+ "frame_id": frame_id,
81
+ "frame_type": PTCPParser.FRAME_TYPES.get(frame_id, f"Unknown (0x{frame_id:04X})"),
82
+ "sequence_id": sequence_id,
83
+ "reserved": reserved,
84
+ "tlv_blocks": [],
85
+ }
86
+
87
+ # Parse TLV blocks
88
+ offset = 4
89
+ while offset < len(data):
90
+ if offset + 2 > len(data):
91
+ break
92
+
93
+ tlv_type = data[offset]
94
+ tlv_length = data[offset + 1]
95
+
96
+ if tlv_type == 0x00: # End marker
97
+ break
98
+
99
+ if offset + 2 + tlv_length > len(data):
100
+ break
101
+
102
+ tlv_data = data[offset + 2 : offset + 2 + tlv_length]
103
+ tlv_block = PTCPParser._parse_tlv(tlv_type, tlv_data)
104
+ result["tlv_blocks"].append(tlv_block)
105
+
106
+ offset += 2 + tlv_length
107
+
108
+ return result
109
+
110
+ @staticmethod
111
+ def _parse_tlv(tlv_type: int, data: bytes) -> dict[str, Any]:
112
+ """Parse PTCP TLV (Type-Length-Value) block.
113
+
114
+ Args:
115
+ tlv_type: TLV type code.
116
+ data: TLV data bytes.
117
+
118
+ Returns:
119
+ Parsed TLV block data.
120
+ """
121
+ block: dict[str, Any] = {
122
+ "type": PTCPParser.TLV_TYPES.get(tlv_type, f"Unknown (0x{tlv_type:02X})"),
123
+ "type_raw": tlv_type,
124
+ "length": len(data),
125
+ }
126
+
127
+ # Parse based on TLV type
128
+ if tlv_type == 0x01 and len(data) >= 16:
129
+ PTCPParser._parse_subdomain_uuid(block, data)
130
+ elif tlv_type == 0x02 and len(data) >= 10:
131
+ PTCPParser._parse_time(block, data)
132
+ elif tlv_type == 0x03 and len(data) >= 6:
133
+ PTCPParser._parse_time_extension(block, data)
134
+ elif tlv_type == 0x04 and len(data) >= 6:
135
+ PTCPParser._parse_master_source_address(block, data)
136
+ elif tlv_type == 0x05 and len(data) >= 14:
137
+ PTCPParser._parse_port_parameter(block, data)
138
+ elif tlv_type == 0x06 and len(data) >= 20:
139
+ PTCPParser._parse_delay_parameter(block, data)
140
+ elif tlv_type == 0x07 and len(data) >= 10:
141
+ PTCPParser._parse_port_time(block, data)
142
+ else:
143
+ block["data_hex"] = data.hex()
144
+
145
+ return block
146
+
147
+ @staticmethod
148
+ def _parse_subdomain_uuid(block: dict[str, Any], data: bytes) -> None:
149
+ """Parse Subdomain UUID TLV."""
150
+ block["subdomain_uuid"] = data[:16].hex()
151
+
152
+ @staticmethod
153
+ def _parse_time(block: dict[str, Any], data: bytes) -> None:
154
+ """Parse Time TLV."""
155
+ seconds = int.from_bytes(data[0:6], "big")
156
+ nanoseconds = int.from_bytes(data[6:10], "big")
157
+ block["seconds"] = seconds
158
+ block["nanoseconds"] = nanoseconds
159
+ block["timestamp"] = seconds + nanoseconds / 1e9
160
+
161
+ @staticmethod
162
+ def _parse_time_extension(block: dict[str, Any], data: bytes) -> None:
163
+ """Parse Time Extension TLV."""
164
+ epoch = int.from_bytes(data[0:2], "big")
165
+ seconds_high = int.from_bytes(data[2:6], "big")
166
+ block["epoch"] = epoch
167
+ block["seconds_high"] = seconds_high
168
+
169
+ @staticmethod
170
+ def _parse_master_source_address(block: dict[str, Any], data: bytes) -> None:
171
+ """Parse Master Source Address TLV."""
172
+ block["mac_address"] = ":".join(f"{b:02x}" for b in data[:6])
173
+
174
+ @staticmethod
175
+ def _parse_port_parameter(block: dict[str, Any], data: bytes) -> None:
176
+ """Parse Port Parameter TLV."""
177
+ block["t2_port_rx_delay"] = int.from_bytes(data[0:4], "big")
178
+ block["t3_port_tx_delay"] = int.from_bytes(data[4:8], "big")
179
+ block["port_mac_address"] = ":".join(f"{b:02x}" for b in data[8:14])
180
+
181
+ @staticmethod
182
+ def _parse_delay_parameter(block: dict[str, Any], data: bytes) -> None:
183
+ """Parse Delay Parameter TLV."""
184
+ block["request_port_rx_delay"] = int.from_bytes(data[0:4], "big")
185
+ block["request_port_tx_delay"] = int.from_bytes(data[4:8], "big")
186
+ block["response_port_rx_delay"] = int.from_bytes(data[8:12], "big")
187
+ block["response_port_tx_delay"] = int.from_bytes(data[12:16], "big")
188
+ block["cable_delay"] = int.from_bytes(data[16:20], "big")
189
+
190
+ @staticmethod
191
+ def _parse_port_time(block: dict[str, Any], data: bytes) -> None:
192
+ """Parse Port Time TLV."""
193
+ seconds = int.from_bytes(data[0:6], "big")
194
+ nanoseconds = int.from_bytes(data[6:10], "big")
195
+ block["seconds"] = seconds
196
+ block["nanoseconds"] = nanoseconds
197
+ block["timestamp"] = seconds + nanoseconds / 1e9
198
+
199
+
200
+ __all__ = ["PTCPParser"]