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,185 @@
1
+ """FlexRay CRC calculation algorithms.
2
+
3
+ This module implements FlexRay CRC algorithms:
4
+ - Header CRC-11 (protects frame header)
5
+ - Frame CRC-24 (protects header + payload)
6
+
7
+ References:
8
+ FlexRay Communications System Protocol Specification Version 3.0.1
9
+ Section 4.5: Error Detection Mechanisms
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+
15
+ def calculate_header_crc(
16
+ reserved: int,
17
+ payload_preamble: int,
18
+ null_frame: int,
19
+ sync_frame: int,
20
+ startup_frame: int,
21
+ frame_id: int,
22
+ payload_length: int,
23
+ ) -> int:
24
+ """Calculate 11-bit FlexRay header CRC.
25
+
26
+ The header CRC protects the first 29 bits of the header (excluding the CRC
27
+ itself and the cycle count field).
28
+
29
+ CRC polynomial: x^11 + x^9 + x^8 + x^7 + x^2 + 1 (0x385)
30
+ Initial value: 0x01A
31
+
32
+ Args:
33
+ reserved: Reserved bit (1 bit).
34
+ payload_preamble: Payload preamble indicator (1 bit).
35
+ null_frame: Null frame indicator (1 bit).
36
+ sync_frame: Sync frame indicator (1 bit).
37
+ startup_frame: Startup frame indicator (1 bit).
38
+ frame_id: Frame ID (11 bits, 1-2047).
39
+ payload_length: Payload length in 16-bit words (7 bits, 0-127).
40
+
41
+ Returns:
42
+ 11-bit header CRC value.
43
+
44
+ Example:
45
+ >>> crc = calculate_header_crc(0, 0, 0, 0, 0, 100, 5)
46
+ >>> print(f"Header CRC: 0x{crc:03X}")
47
+ """
48
+ # Combine header fields (29 bits without CRC and cycle count)
49
+ # Bit positions from MSB:
50
+ # [28] reserved
51
+ # [27] payload_preamble
52
+ # [26] null_frame
53
+ # [25] sync_frame
54
+ # [24] startup_frame
55
+ # [23:13] frame_id (11 bits)
56
+ # [12:6] payload_length (7 bits)
57
+ # [5:0] padding (will be shifted out during calculation)
58
+ data = (
59
+ (reserved << 28)
60
+ | (payload_preamble << 27)
61
+ | (null_frame << 26)
62
+ | (sync_frame << 25)
63
+ | (startup_frame << 24)
64
+ | (frame_id << 13)
65
+ | (payload_length << 6)
66
+ )
67
+
68
+ # CRC-11 calculation
69
+ crc = 0x01A # Initial value
70
+ polynomial = 0x385 # x^11 + x^9 + x^8 + x^7 + x^2 + 1
71
+
72
+ # Process 29 data bits
73
+ for i in range(29):
74
+ bit = (data >> (28 - i)) & 1
75
+ msb = (crc >> 10) & 1
76
+
77
+ crc = (crc << 1) & 0x7FF # Shift and keep 11 bits
78
+ if msb ^ bit:
79
+ crc ^= polynomial
80
+
81
+ return crc & 0x7FF
82
+
83
+
84
+ def calculate_frame_crc(header: bytes, payload: bytes) -> int:
85
+ """Calculate 24-bit FlexRay frame CRC.
86
+
87
+ The frame CRC protects the entire frame (header + payload).
88
+
89
+ CRC polynomial: x^24 + x^22 + x^20 + x^19 + x^18 + x^16 + x^14 +
90
+ x^13 + x^11 + x^10 + x^8 + x^7 + x^6 + x^3 + x^1 + 1
91
+ (0x5D6DCB)
92
+ Initial value: 0xFEDCBA
93
+
94
+ Args:
95
+ header: Frame header bytes (5 bytes).
96
+ payload: Frame payload bytes (0-254 bytes).
97
+
98
+ Returns:
99
+ 24-bit frame CRC value.
100
+
101
+ Example:
102
+ >>> header = bytes([0x00, 0x64, 0x12, 0x34, 0x05])
103
+ >>> payload = bytes([0x01, 0x02, 0x03, 0x04, 0x05])
104
+ >>> crc = calculate_frame_crc(header, payload)
105
+ >>> print(f"Frame CRC: 0x{crc:06X}")
106
+ """
107
+ data = header + payload
108
+
109
+ crc = 0xFEDCBA # Initial value
110
+ polynomial = 0x5D6DCB # FlexRay CRC-24 polynomial
111
+
112
+ for byte in data:
113
+ crc ^= byte << 16
114
+
115
+ for _ in range(8):
116
+ if crc & 0x800000:
117
+ crc = ((crc << 1) ^ polynomial) & 0xFFFFFF
118
+ else:
119
+ crc = (crc << 1) & 0xFFFFFF
120
+
121
+ return crc
122
+
123
+
124
+ def verify_header_crc(
125
+ reserved: int,
126
+ payload_preamble: int,
127
+ null_frame: int,
128
+ sync_frame: int,
129
+ startup_frame: int,
130
+ frame_id: int,
131
+ payload_length: int,
132
+ received_crc: int,
133
+ ) -> bool:
134
+ """Verify FlexRay header CRC.
135
+
136
+ Args:
137
+ reserved: Reserved bit.
138
+ payload_preamble: Payload preamble indicator.
139
+ null_frame: Null frame indicator.
140
+ sync_frame: Sync frame indicator.
141
+ startup_frame: Startup frame indicator.
142
+ frame_id: Frame ID (11 bits).
143
+ payload_length: Payload length in words (7 bits).
144
+ received_crc: Received CRC value to verify.
145
+
146
+ Returns:
147
+ True if CRC is valid, False otherwise.
148
+
149
+ Example:
150
+ >>> valid = verify_header_crc(0, 0, 0, 0, 0, 100, 5, 0x3A5)
151
+ >>> print(f"Header CRC valid: {valid}")
152
+ """
153
+ computed_crc = calculate_header_crc(
154
+ reserved, payload_preamble, null_frame, sync_frame, startup_frame, frame_id, payload_length
155
+ )
156
+ return computed_crc == received_crc
157
+
158
+
159
+ def verify_frame_crc(header: bytes, payload: bytes, received_crc: int) -> bool:
160
+ """Verify FlexRay frame CRC.
161
+
162
+ Args:
163
+ header: Frame header bytes (5 bytes).
164
+ payload: Frame payload bytes.
165
+ received_crc: Received CRC value to verify.
166
+
167
+ Returns:
168
+ True if CRC is valid, False otherwise.
169
+
170
+ Example:
171
+ >>> header = bytes([0x00, 0x64, 0x12, 0x34, 0x05])
172
+ >>> payload = bytes([0x01, 0x02, 0x03, 0x04, 0x05])
173
+ >>> valid = verify_frame_crc(header, payload, 0x123456)
174
+ >>> print(f"Frame CRC valid: {valid}")
175
+ """
176
+ computed_crc = calculate_frame_crc(header, payload)
177
+ return computed_crc == received_crc
178
+
179
+
180
+ __all__ = [
181
+ "calculate_frame_crc",
182
+ "calculate_header_crc",
183
+ "verify_frame_crc",
184
+ "verify_header_crc",
185
+ ]
@@ -0,0 +1,449 @@
1
+ """FIBEX (FlexRay Interface Bus Exchange) format support.
2
+
3
+ This module implements FIBEX XML import and export for FlexRay network
4
+ configuration, frame definitions, and signal definitions.
5
+
6
+ References:
7
+ ASAM FIBEX 4.0.0 Specification
8
+ FlexRay Communications System Protocol Specification Version 3.0.1
9
+
10
+ Example:
11
+ >>> from oscura.automotive.flexray import FIBEXExporter, FlexRayAnalyzer
12
+ >>> analyzer = FlexRayAnalyzer()
13
+ >>> # ... parse frames and add signals ...
14
+ >>> exporter = FIBEXExporter(analyzer)
15
+ >>> exporter.export(Path("network.xml"))
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import xml.etree.ElementTree as ET
21
+ from pathlib import Path
22
+ from typing import TYPE_CHECKING, Any
23
+
24
+ if TYPE_CHECKING:
25
+ from oscura.automotive.flexray.analyzer import FlexRayAnalyzer, FlexRaySignal
26
+
27
+
28
+ class FIBEXExporter:
29
+ """FIBEX XML exporter for FlexRay networks.
30
+
31
+ Exports FlexRay network configuration as FIBEX 4.0 XML format.
32
+
33
+ Attributes:
34
+ analyzer: FlexRay analyzer with frames and signals.
35
+
36
+ Example:
37
+ >>> exporter = FIBEXExporter(analyzer)
38
+ >>> exporter.export(Path("flexray_network.xml"))
39
+ """
40
+
41
+ FIBEX_NAMESPACE = "http://www.asam.net/xml/fbx"
42
+ FIBEX_VERSION = "4.0.0"
43
+
44
+ def __init__(self, analyzer: FlexRayAnalyzer) -> None:
45
+ """Initialize FIBEX exporter.
46
+
47
+ Args:
48
+ analyzer: FlexRay analyzer containing parsed frames and signals.
49
+ """
50
+ self.analyzer = analyzer
51
+
52
+ def export(self, output_path: Path) -> None:
53
+ """Export FIBEX XML file.
54
+
55
+ Creates a FIBEX XML file containing:
56
+ - Project information
57
+ - Cluster configuration
58
+ - Frame definitions
59
+ - Signal definitions
60
+ - Coding information
61
+
62
+ Args:
63
+ output_path: Output file path for FIBEX XML.
64
+
65
+ Example:
66
+ >>> exporter.export(Path("output/flexray_network.xml"))
67
+ """
68
+ # Create root element
69
+ root = ET.Element("FIBEX")
70
+ root.set("xmlns", self.FIBEX_NAMESPACE)
71
+ root.set("VERSION", self.FIBEX_VERSION)
72
+
73
+ # Add project
74
+ project = ET.SubElement(root, "PROJECT")
75
+ project.set("ID", "FlexRay_Network")
76
+
77
+ project_name = ET.SubElement(project, "SHORT-NAME")
78
+ project_name.text = "FlexRay Network"
79
+
80
+ # Add cluster
81
+ clusters = ET.SubElement(project, "CLUSTERS")
82
+ cluster = ET.SubElement(clusters, "CLUSTER")
83
+ cluster.set("ID", "FlexRay_Cluster_1")
84
+
85
+ cluster_name = ET.SubElement(cluster, "SHORT-NAME")
86
+ cluster_name.text = "FlexRay_Cluster"
87
+
88
+ # Add cluster parameters
89
+ self._add_cluster_parameters(cluster)
90
+
91
+ # Add channels
92
+ self._add_channels(cluster)
93
+
94
+ # Add frames
95
+ self._add_frames(cluster)
96
+
97
+ # Add signals
98
+ self._add_signals(project)
99
+
100
+ # Write XML with pretty formatting
101
+ self._indent(root)
102
+ tree = ET.ElementTree(root)
103
+ tree.write(output_path, encoding="utf-8", xml_declaration=True)
104
+
105
+ def _add_cluster_parameters(self, cluster: ET.Element) -> None:
106
+ """Add cluster parameters to FIBEX.
107
+
108
+ Args:
109
+ cluster: Cluster XML element.
110
+ """
111
+ params = ET.SubElement(cluster, "CLUSTER-PARAMS")
112
+
113
+ # Get configuration
114
+ config = self.analyzer.cluster_config
115
+
116
+ # Speed
117
+ speed = ET.SubElement(params, "SPEED")
118
+ speed.text = "10000" # 10 Mbps
119
+
120
+ # Static slots
121
+ static_slots = ET.SubElement(params, "NUMBER-OF-STATIC-SLOTS")
122
+ static_slots.text = str(config.get("static_slot_count", 100))
123
+
124
+ # Dynamic slots
125
+ dynamic_slots = ET.SubElement(params, "NUMBER-OF-DYNAMIC-SLOTS")
126
+ dynamic_slots.text = str(config.get("dynamic_slot_count", 50))
127
+
128
+ # Cycle length
129
+ cycle_length = ET.SubElement(params, "CYCLE-LENGTH-IN-MACROTICKS")
130
+ cycle_length.text = str(config.get("cycle_length", 5000))
131
+
132
+ def _add_channels(self, cluster: ET.Element) -> None:
133
+ """Add channel definitions to FIBEX.
134
+
135
+ Args:
136
+ cluster: Cluster XML element.
137
+ """
138
+ channels = ET.SubElement(cluster, "CHANNELS")
139
+
140
+ # Get unique channels from frames
141
+ unique_channels = {frame.channel for frame in self.analyzer.frames}
142
+
143
+ for channel_id in sorted(unique_channels):
144
+ channel = ET.SubElement(channels, "CHANNEL")
145
+ channel.set("ID", f"Channel_{channel_id}")
146
+
147
+ channel_name = ET.SubElement(channel, "SHORT-NAME")
148
+ channel_name.text = f"Channel_{channel_id}"
149
+
150
+ def _add_frames(self, cluster: ET.Element) -> None:
151
+ """Add frame definitions to FIBEX.
152
+
153
+ Args:
154
+ cluster: Cluster XML element.
155
+ """
156
+ frames_elem = ET.SubElement(cluster, "FRAMES")
157
+
158
+ # Get unique frame IDs
159
+ unique_frame_ids = sorted({frame.header.frame_id for frame in self.analyzer.frames})
160
+
161
+ for frame_id in unique_frame_ids:
162
+ # Get representative frame for this ID
163
+ frame_example = next(f for f in self.analyzer.frames if f.header.frame_id == frame_id)
164
+
165
+ frame_elem = ET.SubElement(frames_elem, "FRAME")
166
+ frame_elem.set("ID", f"Frame_{frame_id}")
167
+
168
+ # Frame name
169
+ frame_name = ET.SubElement(frame_elem, "SHORT-NAME")
170
+ frame_name.text = f"Frame_{frame_id}"
171
+
172
+ # Slot ID
173
+ slot_id_elem = ET.SubElement(frame_elem, "SLOT-ID")
174
+ slot_id_elem.text = str(frame_id)
175
+
176
+ # Frame length
177
+ frame_length = ET.SubElement(frame_elem, "FRAME-LENGTH")
178
+ frame_length.text = str(frame_example.header.payload_length)
179
+
180
+ # Segment type
181
+ segment_type = ET.SubElement(frame_elem, "SEGMENT-TYPE")
182
+ segment_type.text = frame_example.segment_type.upper()
183
+
184
+ # Add signals in this frame
185
+ frame_signals = [s for s in self.analyzer.signals if s.frame_id == frame_id]
186
+ if frame_signals:
187
+ signals_elem = ET.SubElement(frame_elem, "SIGNALS")
188
+ for signal in frame_signals:
189
+ signal_ref = ET.SubElement(signals_elem, "SIGNAL-REF")
190
+ signal_ref.set("ID-REF", signal.name)
191
+
192
+ def _add_signals(self, project: ET.Element) -> None:
193
+ """Add signal definitions to FIBEX.
194
+
195
+ Args:
196
+ project: Project XML element.
197
+ """
198
+ if not self.analyzer.signals:
199
+ return
200
+
201
+ signals_elem = ET.SubElement(project, "SIGNALS")
202
+
203
+ for signal in self.analyzer.signals:
204
+ signal_elem = ET.SubElement(signals_elem, "SIGNAL")
205
+ signal_elem.set("ID", signal.name)
206
+
207
+ # Signal name
208
+ signal_name = ET.SubElement(signal_elem, "SHORT-NAME")
209
+ signal_name.text = signal.name
210
+
211
+ # Bit position
212
+ bit_position = ET.SubElement(signal_elem, "BIT-POSITION")
213
+ bit_position.text = str(signal.start_bit)
214
+
215
+ # Bit length
216
+ bit_length = ET.SubElement(signal_elem, "BIT-LENGTH")
217
+ bit_length.text = str(signal.bit_length)
218
+
219
+ # Byte order
220
+ byte_order = ET.SubElement(signal_elem, "BYTE-ORDER")
221
+ byte_order.text = "BIG-ENDIAN" if signal.byte_order == "big_endian" else "LITTLE-ENDIAN"
222
+
223
+ # Coding
224
+ coding = ET.SubElement(signal_elem, "CODING")
225
+
226
+ # Factor
227
+ factor_elem = ET.SubElement(coding, "FACTOR")
228
+ factor_elem.text = str(signal.factor)
229
+
230
+ # Offset
231
+ offset_elem = ET.SubElement(coding, "OFFSET")
232
+ offset_elem.text = str(signal.offset)
233
+
234
+ # Unit
235
+ if signal.unit:
236
+ unit_elem = ET.SubElement(coding, "UNIT")
237
+ unit_elem.text = signal.unit
238
+
239
+ def _indent(self, elem: ET.Element, level: int = 0) -> None:
240
+ """Add pretty-printing indentation to XML tree.
241
+
242
+ Args:
243
+ elem: XML element to indent.
244
+ level: Current indentation level.
245
+ """
246
+ indent = " "
247
+ i = "\n" + level * indent
248
+ if len(elem):
249
+ if not elem.text or not elem.text.strip():
250
+ elem.text = i + indent
251
+ if not elem.tail or not elem.tail.strip():
252
+ elem.tail = i
253
+ for child in elem:
254
+ self._indent(child, level + 1)
255
+ if elem and (not elem[-1].tail or not elem[-1].tail.strip()):
256
+ elem[-1].tail = i
257
+ else:
258
+ if level and (not elem.tail or not elem.tail.strip()):
259
+ elem.tail = i
260
+
261
+
262
+ class FIBEXImporter:
263
+ """FIBEX XML importer for FlexRay networks.
264
+
265
+ Imports FlexRay network configuration from FIBEX 4.0 XML format.
266
+
267
+ Example:
268
+ >>> importer = FIBEXImporter()
269
+ >>> cluster_config, signals = importer.load(Path("network.xml"))
270
+ >>> analyzer = FlexRayAnalyzer(cluster_config=cluster_config)
271
+ >>> for signal in signals:
272
+ ... analyzer.add_signal(signal)
273
+ """
274
+
275
+ def __init__(self) -> None:
276
+ """Initialize FIBEX importer."""
277
+
278
+ def load(self, fibex_path: Path) -> tuple[dict[str, Any], list[FlexRaySignal]]:
279
+ """Load FIBEX XML file.
280
+
281
+ Args:
282
+ fibex_path: Path to FIBEX XML file.
283
+
284
+ Returns:
285
+ Tuple of (cluster_config, signals) where:
286
+ - cluster_config: Dictionary with cluster parameters
287
+ - signals: List of FlexRaySignal definitions
288
+
289
+ Raises:
290
+ FileNotFoundError: If FIBEX file not found.
291
+ ValueError: If FIBEX file is invalid.
292
+
293
+ Example:
294
+ >>> config, signals = importer.load(Path("flexray_network.xml"))
295
+ >>> print(f"Loaded {len(signals)} signals")
296
+ """
297
+ # Import here to avoid circular dependency
298
+
299
+ if not fibex_path.exists():
300
+ raise FileNotFoundError(f"FIBEX file not found: {fibex_path}")
301
+
302
+ tree = ET.parse(fibex_path)
303
+ root = tree.getroot()
304
+
305
+ # Extract cluster configuration
306
+ cluster_config = self._parse_cluster_config(root)
307
+
308
+ # Extract signal definitions
309
+ signals = self._parse_signals(root)
310
+
311
+ return cluster_config, signals
312
+
313
+ def _parse_cluster_config(self, root: ET.Element) -> dict[str, Any]:
314
+ """Parse cluster configuration from FIBEX.
315
+
316
+ Args:
317
+ root: FIBEX root element.
318
+
319
+ Returns:
320
+ Cluster configuration dictionary.
321
+ """
322
+ config: dict[str, Any] = {}
323
+
324
+ # Find cluster parameters
325
+ cluster_params = root.find(".//{*}CLUSTER-PARAMS")
326
+ if cluster_params is not None:
327
+ # Static slots
328
+ static_slots = cluster_params.find("{*}NUMBER-OF-STATIC-SLOTS")
329
+ if static_slots is not None and static_slots.text:
330
+ config["static_slot_count"] = int(static_slots.text)
331
+
332
+ # Dynamic slots
333
+ dynamic_slots = cluster_params.find("{*}NUMBER-OF-DYNAMIC-SLOTS")
334
+ if dynamic_slots is not None and dynamic_slots.text:
335
+ config["dynamic_slot_count"] = int(dynamic_slots.text)
336
+
337
+ # Cycle length
338
+ cycle_length = cluster_params.find("{*}CYCLE-LENGTH-IN-MACROTICKS")
339
+ if cycle_length is not None and cycle_length.text:
340
+ config["cycle_length"] = int(cycle_length.text)
341
+
342
+ return config
343
+
344
+ def _parse_signals(self, root: ET.Element) -> list[FlexRaySignal]:
345
+ """Parse signal definitions from FIBEX.
346
+
347
+ Args:
348
+ root: FIBEX root element.
349
+
350
+ Returns:
351
+ List of FlexRay signal definitions.
352
+ """
353
+ # Import here to avoid circular dependency
354
+ from oscura.automotive.flexray.analyzer import FlexRaySignal
355
+
356
+ signals: list[FlexRaySignal] = []
357
+
358
+ # Find all signal elements
359
+ for signal_elem in root.findall(".//{*}SIGNAL"):
360
+ signal_id = signal_elem.get("ID", "")
361
+
362
+ # Signal name
363
+ name_elem = signal_elem.find("{*}SHORT-NAME")
364
+ name = name_elem.text if name_elem is not None and name_elem.text else signal_id
365
+
366
+ # Bit position
367
+ bit_pos_elem = signal_elem.find("{*}BIT-POSITION")
368
+ start_bit = (
369
+ int(bit_pos_elem.text) if bit_pos_elem is not None and bit_pos_elem.text else 0
370
+ )
371
+
372
+ # Bit length
373
+ bit_len_elem = signal_elem.find("{*}BIT-LENGTH")
374
+ bit_length = (
375
+ int(bit_len_elem.text) if bit_len_elem is not None and bit_len_elem.text else 8
376
+ )
377
+
378
+ # Byte order
379
+ byte_order_elem = signal_elem.find("{*}BYTE-ORDER")
380
+ byte_order = "big_endian"
381
+ if byte_order_elem is not None and byte_order_elem.text:
382
+ byte_order = (
383
+ "big_endian" if byte_order_elem.text == "BIG-ENDIAN" else "little_endian"
384
+ )
385
+
386
+ # Coding
387
+ coding_elem = signal_elem.find("{*}CODING")
388
+ factor = 1.0
389
+ offset = 0.0
390
+ unit = ""
391
+
392
+ if coding_elem is not None:
393
+ factor_elem = coding_elem.find("{*}FACTOR")
394
+ if factor_elem is not None and factor_elem.text:
395
+ factor = float(factor_elem.text)
396
+
397
+ offset_elem = coding_elem.find("{*}OFFSET")
398
+ if offset_elem is not None and offset_elem.text:
399
+ offset = float(offset_elem.text)
400
+
401
+ unit_elem = coding_elem.find("{*}UNIT")
402
+ if unit_elem is not None and unit_elem.text:
403
+ unit = unit_elem.text
404
+
405
+ # Find frame ID (need to search for frame containing this signal)
406
+ frame_id = self._find_frame_id_for_signal(root, signal_id)
407
+
408
+ signal = FlexRaySignal(
409
+ name=name,
410
+ frame_id=frame_id,
411
+ start_bit=start_bit,
412
+ bit_length=bit_length,
413
+ byte_order=byte_order,
414
+ factor=factor,
415
+ offset=offset,
416
+ unit=unit,
417
+ )
418
+
419
+ signals.append(signal)
420
+
421
+ return signals
422
+
423
+ def _find_frame_id_for_signal(self, root: ET.Element, signal_id: str) -> int:
424
+ """Find frame ID containing a signal.
425
+
426
+ Args:
427
+ root: FIBEX root element.
428
+ signal_id: Signal ID to find.
429
+
430
+ Returns:
431
+ Frame ID (slot ID) containing the signal, or 0 if not found.
432
+ """
433
+ # Find frame containing this signal reference
434
+ for frame_elem in root.findall(".//{*}FRAME"):
435
+ signal_refs = frame_elem.findall(".//{*}SIGNAL-REF")
436
+ for sig_ref in signal_refs:
437
+ if sig_ref.get("ID-REF") == signal_id:
438
+ # Found frame, get slot ID
439
+ slot_id_elem = frame_elem.find("{*}SLOT-ID")
440
+ if slot_id_elem is not None and slot_id_elem.text:
441
+ return int(slot_id_elem.text)
442
+
443
+ return 0 # Default if not found
444
+
445
+
446
+ __all__ = [
447
+ "FIBEXExporter",
448
+ "FIBEXImporter",
449
+ ]
@@ -1,12 +1,49 @@
1
- """J1939 heavy-duty vehicle protocol support.
1
+ """J1939 (SAE J1939) protocol support for heavy-duty vehicles.
2
2
 
3
- This module provides J1939 protocol decoding for heavy-duty vehicles
4
- (trucks, buses, agriculture, marine).
3
+ This module provides comprehensive J1939 protocol analysis including:
4
+ - Message decoding and PGN extraction
5
+ - Transport protocol (TP.CM, TP.DT, BAM) multi-packet reassembly
6
+ - Suspect Parameter Number (SPN) decoding
7
+ - Standard SPN definitions
8
+
9
+ Example:
10
+ >>> from oscura.automotive.j1939 import J1939Analyzer, J1939SPN
11
+ >>> analyzer = J1939Analyzer()
12
+ >>> msg = analyzer.parse_message(0x0CF00400, b'\\xff' * 8)
13
+ >>> print(msg.identifier.pgn)
14
+ 61444
5
15
  """
6
16
 
7
- __all__ = ["J1939Decoder", "J1939Message", "extract_pgn"]
17
+ from oscura.automotive.j1939.analyzer import (
18
+ J1939SPN,
19
+ J1939Analyzer,
20
+ J1939Identifier,
21
+ J1939Message,
22
+ )
23
+ from oscura.automotive.j1939.decoder import (
24
+ J1939Decoder,
25
+ extract_pgn,
26
+ )
27
+ from oscura.automotive.j1939.decoder import (
28
+ J1939Message as DecoderMessage,
29
+ )
30
+ from oscura.automotive.j1939.spns import STANDARD_SPNS, get_standard_spns
31
+ from oscura.automotive.j1939.transport import TransportProtocol, TransportSession
8
32
 
9
- try:
10
- from oscura.automotive.j1939.decoder import J1939Decoder, J1939Message, extract_pgn
11
- except ImportError:
12
- pass
33
+ __all__ = [
34
+ "J1939SPN",
35
+ # SPN definitions
36
+ "STANDARD_SPNS",
37
+ "DecoderMessage",
38
+ # Analyzer (new comprehensive analyzer)
39
+ "J1939Analyzer",
40
+ # Decoder (legacy decoder)
41
+ "J1939Decoder",
42
+ "J1939Identifier",
43
+ "J1939Message",
44
+ # Transport protocol
45
+ "TransportProtocol",
46
+ "TransportSession",
47
+ "extract_pgn",
48
+ "get_standard_spns",
49
+ ]