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
@@ -180,94 +180,28 @@ class CANMessageWrapper:
180
180
  >>> print(result.summary())
181
181
  """
182
182
  # Create signal definition
183
- definition = SignalDefinition(
184
- name=signal_name,
185
- start_bit=start_byte * 8,
186
- length=bit_length,
187
- byte_order=byte_order,
188
- value_type=value_type,
189
- scale=scale,
190
- offset=offset,
191
- unit=unit,
183
+ definition = _create_signal_definition(
184
+ signal_name, start_byte, bit_length, byte_order, value_type, scale, offset, unit
192
185
  )
193
186
 
194
- # Get all messages with this ID
195
- filtered = self._session._messages.filter_by_id(self._arbitration_id)
196
-
197
187
  # Decode all values
198
- decoded_values = []
199
- for msg in filtered.messages:
200
- try:
201
- value = definition.decode(msg.data)
202
- decoded_values.append(value)
203
- except Exception:
204
- # Skip messages that can't be decoded
205
- pass
188
+ decoded_values = _decode_hypothesis_values(self._session, self._arbitration_id, definition)
206
189
 
190
+ # Handle no decoded values
207
191
  if not decoded_values:
208
- return HypothesisResult(
209
- signal_name=signal_name,
210
- definition=definition,
211
- values=[],
212
- min_value=0.0,
213
- max_value=0.0,
214
- mean=0.0,
215
- std=0.0,
216
- is_valid=False,
217
- confidence=0.0,
218
- feedback="Failed to decode any messages with this definition",
219
- )
192
+ return _create_failed_hypothesis_result(signal_name, definition)
220
193
 
221
194
  # Calculate statistics
222
- arr = np.array(decoded_values)
223
- min_val = float(np.min(arr))
224
- max_val = float(np.max(arr))
225
- mean_val = float(np.mean(arr))
226
- std_val = float(np.std(arr))
195
+ stats = _calculate_hypothesis_statistics(decoded_values)
227
196
 
228
197
  # Validate hypothesis
229
- is_valid = True
230
- confidence = 1.0
231
- feedback_parts = []
232
-
233
- # Check expected range if provided
234
- if expected_min is not None and min_val < expected_min:
235
- is_valid = False
236
- confidence *= 0.5
237
- feedback_parts.append(f"Min value {min_val:.2f} below expected {expected_min:.2f}")
238
-
239
- if expected_max is not None and max_val > expected_max:
240
- is_valid = False
241
- confidence *= 0.5
242
- feedback_parts.append(f"Max value {max_val:.2f} above expected {expected_max:.2f}")
243
-
244
- # Check for reasonable value distribution
245
- if std_val == 0:
246
- confidence *= 0.7
247
- feedback_parts.append("Warning: All values are identical - might be a constant field")
248
-
249
- # Check for extremely large range (might indicate wrong scaling)
250
- value_range = max_val - min_val
251
- if value_range > 1e6:
252
- confidence *= 0.6
253
- feedback_parts.append("Warning: Very large value range - check scaling factor")
254
-
255
- # Positive feedback
256
- if is_valid and not feedback_parts:
257
- feedback_parts.append(f"Values in expected range [{min_val:.2f}, {max_val:.2f}]")
258
- if std_val > 0:
259
- feedback_parts.append("Signal shows variation - likely represents real data")
260
-
261
- feedback = "; ".join(feedback_parts) if feedback_parts else "Hypothesis test passed"
198
+ is_valid, confidence, feedback = _validate_hypothesis(stats, expected_min, expected_max)
262
199
 
263
200
  return HypothesisResult(
264
201
  signal_name=signal_name,
265
202
  definition=definition,
266
203
  values=decoded_values,
267
- min_value=min_val,
268
- max_value=max_val,
269
- mean=mean_val,
270
- std=std_val,
204
+ **stats,
271
205
  is_valid=is_valid,
272
206
  confidence=confidence,
273
207
  feedback=feedback,
@@ -373,3 +307,116 @@ class CANMessageWrapper:
373
307
  def __repr__(self) -> str:
374
308
  """Human-readable representation."""
375
309
  return f"CANMessageWrapper(id=0x{self._arbitration_id:03X}, documented_signals={len(self._documented_signals)})"
310
+
311
+
312
+ def _create_signal_definition(
313
+ signal_name: str,
314
+ start_byte: int,
315
+ bit_length: int,
316
+ byte_order: Literal["big_endian", "little_endian"],
317
+ value_type: Literal["unsigned", "signed", "float"],
318
+ scale: float,
319
+ offset: float,
320
+ unit: str,
321
+ ) -> SignalDefinition:
322
+ """Create signal definition from parameters."""
323
+ return SignalDefinition(
324
+ name=signal_name,
325
+ start_bit=start_byte * 8,
326
+ length=bit_length,
327
+ byte_order=byte_order,
328
+ value_type=value_type,
329
+ scale=scale,
330
+ offset=offset,
331
+ unit=unit,
332
+ )
333
+
334
+
335
+ def _decode_hypothesis_values(
336
+ session: CANSession, arbitration_id: int, definition: SignalDefinition
337
+ ) -> list[float]:
338
+ """Decode all values using signal definition."""
339
+
340
+ filtered = session._messages.filter_by_id(arbitration_id)
341
+ decoded_values = []
342
+ for msg in filtered.messages:
343
+ try:
344
+ value = definition.decode(msg.data)
345
+ decoded_values.append(value)
346
+ except Exception:
347
+ pass
348
+ return decoded_values
349
+
350
+
351
+ def _create_failed_hypothesis_result(
352
+ signal_name: str, definition: SignalDefinition
353
+ ) -> HypothesisResult:
354
+ """Create result for failed decoding."""
355
+ return HypothesisResult(
356
+ signal_name=signal_name,
357
+ definition=definition,
358
+ values=[],
359
+ min_value=0.0,
360
+ max_value=0.0,
361
+ mean=0.0,
362
+ std=0.0,
363
+ is_valid=False,
364
+ confidence=0.0,
365
+ feedback="Failed to decode any messages with this definition",
366
+ )
367
+
368
+
369
+ def _calculate_hypothesis_statistics(decoded_values: list[float]) -> dict[str, float]:
370
+ """Calculate statistics for decoded values."""
371
+ arr = np.array(decoded_values)
372
+ return {
373
+ "min_value": float(np.min(arr)),
374
+ "max_value": float(np.max(arr)),
375
+ "mean": float(np.mean(arr)),
376
+ "std": float(np.std(arr)),
377
+ }
378
+
379
+
380
+ def _validate_hypothesis(
381
+ stats: dict[str, float], expected_min: float | None, expected_max: float | None
382
+ ) -> tuple[bool, float, str]:
383
+ """Validate hypothesis and generate feedback."""
384
+ is_valid = True
385
+ confidence = 1.0
386
+ feedback_parts = []
387
+
388
+ # Check expected range
389
+ if expected_min is not None and stats["min_value"] < expected_min:
390
+ is_valid = False
391
+ confidence *= 0.5
392
+ feedback_parts.append(
393
+ f"Min value {stats['min_value']:.2f} below expected {expected_min:.2f}"
394
+ )
395
+
396
+ if expected_max is not None and stats["max_value"] > expected_max:
397
+ is_valid = False
398
+ confidence *= 0.5
399
+ feedback_parts.append(
400
+ f"Max value {stats['max_value']:.2f} above expected {expected_max:.2f}"
401
+ )
402
+
403
+ # Check value distribution
404
+ if stats["std"] == 0:
405
+ confidence *= 0.7
406
+ feedback_parts.append("Warning: All values are identical - might be a constant field")
407
+
408
+ value_range = stats["max_value"] - stats["min_value"]
409
+ if value_range > 1e6:
410
+ confidence *= 0.6
411
+ feedback_parts.append("Warning: Very large value range - check scaling factor")
412
+
413
+ # Positive feedback
414
+ if is_valid and not feedback_parts:
415
+ feedback_parts.append(
416
+ f"Values in expected range [{stats['min_value']:.2f}, {stats['max_value']:.2f}]"
417
+ )
418
+ if stats["std"] > 0:
419
+ feedback_parts.append("Signal shows variation - likely represents real data")
420
+
421
+ feedback = "; ".join(feedback_parts) if feedback_parts else "Hypothesis test passed"
422
+ return is_valid, confidence, feedback
@@ -8,7 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  from collections import defaultdict
10
10
  from dataclasses import dataclass
11
- from typing import TYPE_CHECKING
11
+ from typing import TYPE_CHECKING, Any
12
12
 
13
13
  import numpy as np
14
14
 
@@ -233,7 +233,13 @@ class PatternAnalyzer:
233
233
  Raises:
234
234
  ValueError: If max_sequence_length is invalid (<2 or >10) or
235
235
  min_support is not in range [0.0, 1.0].
236
+
237
+ Example:
238
+ >>> from oscura.automotive.can import CANSession
239
+ >>> session = CANSession()
240
+ >>> sequences = PatternAnalyzer.find_message_sequences(session, max_sequence_length=3)
236
241
  """
242
+ # Validate parameters
237
243
  if max_sequence_length < 2:
238
244
  raise ValueError("max_sequence_length must be at least 2")
239
245
  if max_sequence_length > 10:
@@ -242,25 +248,61 @@ class PatternAnalyzer:
242
248
  raise ValueError("min_support must be between 0.0 and 1.0")
243
249
 
244
250
  time_window_s = time_window_ms / 1000.0
245
-
246
- # Get all messages sorted by timestamp
247
251
  all_messages = sorted(session._messages.messages, key=lambda m: m.timestamp)
248
252
 
249
253
  if not all_messages:
250
254
  return []
251
255
 
252
- # Find maximum message frequency (for support calculation)
256
+ # Calculate maximum message frequency for support calculation
257
+ max_count = PatternAnalyzer._calculate_max_message_frequency(all_messages)
258
+
259
+ # Mine sequences from message stream
260
+ sequences = PatternAnalyzer._mine_sequences(
261
+ all_messages, max_sequence_length, time_window_s
262
+ )
263
+
264
+ # Build and filter result objects
265
+ result = PatternAnalyzer._build_sequence_results(sequences, max_count, min_support)
266
+
267
+ # Sort by support (descending), then by occurrences
268
+ result.sort(key=lambda s: (s.support, s.occurrences), reverse=True)
269
+
270
+ return result
271
+
272
+ @staticmethod
273
+ def _calculate_max_message_frequency(messages: list[Any]) -> int:
274
+ """Calculate maximum message frequency for support calculation.
275
+
276
+ Args:
277
+ messages: List of CAN messages.
278
+
279
+ Returns:
280
+ Maximum frequency of any message ID.
281
+ """
253
282
  id_counts: dict[int, int] = defaultdict(int)
254
- for msg in all_messages:
283
+ for msg in messages:
255
284
  id_counts[msg.arbitration_id] += 1
256
- max_count = max(id_counts.values()) if id_counts else 1
285
+ return max(id_counts.values()) if id_counts else 1
257
286
 
258
- # Mine sequences of increasing length
287
+ @staticmethod
288
+ def _mine_sequences(
289
+ all_messages: list[Any],
290
+ max_sequence_length: int,
291
+ time_window_s: float,
292
+ ) -> dict[tuple[int, ...], list[list[float]]]:
293
+ """Mine message sequences from CAN message stream.
294
+
295
+ Args:
296
+ all_messages: Sorted list of CAN messages.
297
+ max_sequence_length: Maximum sequence length.
298
+ time_window_s: Time window in seconds.
299
+
300
+ Returns:
301
+ Dictionary mapping sequence IDs to timing lists.
302
+ """
259
303
  sequences: dict[tuple[int, ...], list[list[float]]] = defaultdict(list)
260
304
 
261
- # Start with 2-message sequences
262
305
  for i, msg_a in enumerate(all_messages):
263
- # Look forward in time window
264
306
  sequence_start = msg_a.timestamp
265
307
  current_sequence = [msg_a.arbitration_id]
266
308
  timing = []
@@ -276,6 +318,8 @@ class PatternAnalyzer:
276
318
  # Record sequences of desired length
277
319
  if 2 <= len(current_sequence) <= max_sequence_length:
278
320
  seq_key = tuple(current_sequence)
321
+ # NECESSARY COPY: timing list reused across iterations.
322
+ # Without .copy(), all sequences would reference final state.
279
323
  sequences[seq_key].append(timing.copy())
280
324
 
281
325
  # Update for next iteration
@@ -283,7 +327,24 @@ class PatternAnalyzer:
283
327
  if len(current_sequence) >= max_sequence_length:
284
328
  break
285
329
 
286
- # Build MessageSequence objects
330
+ return sequences
331
+
332
+ @staticmethod
333
+ def _build_sequence_results(
334
+ sequences: dict[tuple[int, ...], list[list[float]]],
335
+ max_count: int,
336
+ min_support: float,
337
+ ) -> list[MessageSequence]:
338
+ """Build MessageSequence objects from mined sequences.
339
+
340
+ Args:
341
+ sequences: Dictionary mapping sequence IDs to timing lists.
342
+ max_count: Maximum message frequency (for support calculation).
343
+ min_support: Minimum support threshold.
344
+
345
+ Returns:
346
+ List of MessageSequence objects that meet support threshold.
347
+ """
287
348
  result = []
288
349
  for seq_ids, timing_lists in sequences.items():
289
350
  occurrences = len(timing_lists)
@@ -291,14 +352,7 @@ class PatternAnalyzer:
291
352
 
292
353
  if support >= min_support:
293
354
  # Calculate average timing between consecutive messages
294
- avg_timing = []
295
- if timing_lists:
296
- # timing_lists[i] has len(seq_ids) - 1 elements
297
- num_gaps = len(seq_ids) - 1
298
- for gap_idx in range(num_gaps):
299
- gap_times = [t[gap_idx] for t in timing_lists if gap_idx < len(t)]
300
- if gap_times:
301
- avg_timing.append(float(np.mean(gap_times)))
355
+ avg_timing = PatternAnalyzer._calculate_average_timing(seq_ids, timing_lists)
302
356
 
303
357
  result.append(
304
358
  MessageSequence(
@@ -309,11 +363,34 @@ class PatternAnalyzer:
309
363
  )
310
364
  )
311
365
 
312
- # Sort by support (descending), then by occurrences
313
- result.sort(key=lambda s: (s.support, s.occurrences), reverse=True)
314
-
315
366
  return result
316
367
 
368
+ @staticmethod
369
+ def _calculate_average_timing(
370
+ seq_ids: tuple[int, ...],
371
+ timing_lists: list[list[float]],
372
+ ) -> list[float]:
373
+ """Calculate average timing between consecutive messages in sequence.
374
+
375
+ Args:
376
+ seq_ids: Sequence of message IDs.
377
+ timing_lists: List of timing lists for each occurrence.
378
+
379
+ Returns:
380
+ List of average timings for each gap in sequence.
381
+ """
382
+ avg_timing: list[float] = []
383
+ if not timing_lists:
384
+ return avg_timing
385
+
386
+ num_gaps = len(seq_ids) - 1
387
+ for gap_idx in range(num_gaps):
388
+ gap_times = [t[gap_idx] for t in timing_lists if gap_idx < len(t)]
389
+ if gap_times:
390
+ avg_timing.append(float(np.mean(gap_times)))
391
+
392
+ return avg_timing
393
+
317
394
  @staticmethod
318
395
  def find_temporal_correlations(
319
396
  session: CANSession,