oscura 0.5.1__py3-none-any.whl → 0.7.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 (497) 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/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.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,