oscura 0.0.1__py3-none-any.whl → 0.1.1__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 (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,381 @@
1
+ """Multi-message pattern learning for CAN bus analysis.
2
+
3
+ This module provides algorithms for discovering relationships between CAN messages,
4
+ including message pairs, sequences, and temporal correlations.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections import defaultdict
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING
12
+
13
+ import numpy as np
14
+
15
+ if TYPE_CHECKING:
16
+ from oscura.automotive.can.session import CANSession
17
+
18
+ __all__ = [
19
+ "MessagePair",
20
+ "MessageSequence",
21
+ "PatternAnalyzer",
22
+ "TemporalCorrelation",
23
+ ]
24
+
25
+
26
+ @dataclass
27
+ class MessagePair:
28
+ """A pair of CAN messages that frequently occur together.
29
+
30
+ Attributes:
31
+ id_a: First message arbitration ID.
32
+ id_b: Second message arbitration ID.
33
+ occurrences: Number of times this pair occurred within time window.
34
+ avg_delay_ms: Average time delay from id_a to id_b in milliseconds.
35
+ confidence: Confidence score (0.0-1.0) based on consistency.
36
+ """
37
+
38
+ id_a: int
39
+ id_b: int
40
+ occurrences: int
41
+ avg_delay_ms: float
42
+ confidence: float
43
+
44
+ def __repr__(self) -> str:
45
+ """Human-readable representation."""
46
+ return (
47
+ f"MessagePair(0x{self.id_a:03X} → 0x{self.id_b:03X}, "
48
+ f"occurrences={self.occurrences}, "
49
+ f"delay={self.avg_delay_ms:.2f}ms, "
50
+ f"confidence={self.confidence:.2f})"
51
+ )
52
+
53
+
54
+ @dataclass
55
+ class MessageSequence:
56
+ """A sequence of CAN messages that occur in order.
57
+
58
+ Attributes:
59
+ ids: List of message arbitration IDs in sequence order.
60
+ occurrences: Number of times this sequence occurred.
61
+ avg_timing: Average delays between consecutive messages in milliseconds.
62
+ support: Support score (0.0-1.0) - fraction of time sequence appears.
63
+ """
64
+
65
+ ids: list[int]
66
+ occurrences: int
67
+ avg_timing: list[float]
68
+ support: float
69
+
70
+ def __repr__(self) -> str:
71
+ """Human-readable representation."""
72
+ id_str = " → ".join(f"0x{id_:03X}" for id_ in self.ids)
73
+ timing_str = " → ".join(f"{t:.1f}ms" for t in self.avg_timing)
74
+ return (
75
+ f"MessageSequence({id_str}, "
76
+ f"occurrences={self.occurrences}, "
77
+ f"timing=[{timing_str}], "
78
+ f"support={self.support:.2f})"
79
+ )
80
+
81
+
82
+ @dataclass
83
+ class TemporalCorrelation:
84
+ """Temporal correlation between two CAN messages.
85
+
86
+ Attributes:
87
+ leader_id: Message ID that typically appears first.
88
+ follower_id: Message ID that typically appears after leader.
89
+ avg_delay_ms: Average delay from leader to follower in milliseconds.
90
+ std_delay_ms: Standard deviation of delay in milliseconds.
91
+ occurrences: Number of times this correlation was observed.
92
+ """
93
+
94
+ leader_id: int
95
+ follower_id: int
96
+ avg_delay_ms: float
97
+ std_delay_ms: float
98
+ occurrences: int
99
+
100
+ def __repr__(self) -> str:
101
+ """Human-readable representation."""
102
+ return (
103
+ f"TemporalCorrelation(0x{self.leader_id:03X} → 0x{self.follower_id:03X}, "
104
+ f"delay={self.avg_delay_ms:.2f}±{self.std_delay_ms:.2f}ms, "
105
+ f"occurrences={self.occurrences})"
106
+ )
107
+
108
+
109
+ class PatternAnalyzer:
110
+ """Detect patterns in CAN message sequences.
111
+
112
+ This class provides static methods for discovering relationships between
113
+ CAN messages, useful for understanding message dependencies and control flows.
114
+
115
+ Example - Find message pairs:
116
+ >>> session = CANSession.from_log("capture.blf")
117
+ >>> pairs = PatternAnalyzer.find_message_pairs(session, time_window_ms=100)
118
+ >>> for pair in pairs:
119
+ ... print(pair)
120
+
121
+ Example - Find message sequences:
122
+ >>> sequences = PatternAnalyzer.find_message_sequences(
123
+ ... session,
124
+ ... max_sequence_length=3,
125
+ ... time_window_ms=500
126
+ ... )
127
+
128
+ Example - Find temporal correlations:
129
+ >>> correlations = PatternAnalyzer.find_temporal_correlations(
130
+ ... session,
131
+ ... max_delay_ms=100
132
+ ... )
133
+ """
134
+
135
+ @staticmethod
136
+ def find_message_pairs(
137
+ session: CANSession,
138
+ time_window_ms: float = 100,
139
+ min_occurrence: int = 3,
140
+ ) -> list[MessagePair]:
141
+ """Find message pairs that frequently occur together.
142
+
143
+ Uses a sliding time window to detect messages that consistently appear
144
+ within a short time of each other. This can reveal request-response
145
+ patterns or coordinated control messages.
146
+
147
+ Args:
148
+ session: CAN session to analyze.
149
+ time_window_ms: Maximum time window in milliseconds to consider
150
+ messages as a pair.
151
+ min_occurrence: Minimum number of occurrences to report a pair.
152
+
153
+ Returns:
154
+ List of MessagePair objects, sorted by occurrence count (descending).
155
+ """
156
+ time_window_s = time_window_ms / 1000.0
157
+
158
+ # Get all messages sorted by timestamp
159
+ all_messages = sorted(session._messages.messages, key=lambda m: m.timestamp)
160
+
161
+ # Track pairs: (id_a, id_b) -> list of delays
162
+ pair_delays: dict[tuple[int, int], list[float]] = defaultdict(list)
163
+
164
+ # Use sliding window approach
165
+ for i, msg_a in enumerate(all_messages):
166
+ # Look forward in time window
167
+ for msg_b in all_messages[i + 1 :]:
168
+ delay = msg_b.timestamp - msg_a.timestamp
169
+ if delay > time_window_s:
170
+ break # Outside window, stop searching
171
+
172
+ # Don't pair message with itself (same ID)
173
+ if msg_a.arbitration_id != msg_b.arbitration_id:
174
+ pair_key = (msg_a.arbitration_id, msg_b.arbitration_id)
175
+ pair_delays[pair_key].append(delay * 1000) # Convert to ms
176
+
177
+ # Build MessagePair objects
178
+ pairs = []
179
+ for (id_a, id_b), delays in pair_delays.items():
180
+ occurrences = len(delays)
181
+ if occurrences >= min_occurrence:
182
+ avg_delay = np.mean(delays)
183
+ std_delay = np.std(delays)
184
+
185
+ # Confidence based on consistency (lower std = higher confidence)
186
+ # Normalize std by average delay
187
+ if avg_delay > 0:
188
+ cv = std_delay / avg_delay # Coefficient of variation
189
+ confidence = float(1.0 / (1.0 + cv)) # Higher confidence for lower CV
190
+ else:
191
+ confidence = 1.0
192
+
193
+ pairs.append(
194
+ MessagePair(
195
+ id_a=id_a,
196
+ id_b=id_b,
197
+ occurrences=occurrences,
198
+ avg_delay_ms=float(avg_delay),
199
+ confidence=float(confidence),
200
+ )
201
+ )
202
+
203
+ # Sort by occurrence count (descending)
204
+ pairs.sort(key=lambda p: p.occurrences, reverse=True)
205
+
206
+ return pairs
207
+
208
+ @staticmethod
209
+ def find_message_sequences(
210
+ session: CANSession,
211
+ max_sequence_length: int = 5,
212
+ time_window_ms: float = 500,
213
+ min_support: float = 0.7,
214
+ ) -> list[MessageSequence]:
215
+ """Find message sequences (A → B → C patterns).
216
+
217
+ Uses sequential pattern mining to discover message sequences that
218
+ frequently occur in order. This can reveal multi-step control
219
+ sequences or protocol handshakes.
220
+
221
+ Args:
222
+ session: CAN session to analyze.
223
+ max_sequence_length: Maximum length of sequences to find (2-10).
224
+ time_window_ms: Maximum time window for entire sequence.
225
+ min_support: Minimum support (0.0-1.0) - fraction of occurrences
226
+ relative to most frequent message.
227
+
228
+ Returns:
229
+ List of MessageSequence objects, sorted by support (descending).
230
+
231
+ Raises:
232
+ ValueError: If max_sequence_length is invalid (<2 or >10) or
233
+ min_support is not in range [0.0, 1.0].
234
+ """
235
+ if max_sequence_length < 2:
236
+ raise ValueError("max_sequence_length must be at least 2")
237
+ if max_sequence_length > 10:
238
+ raise ValueError("max_sequence_length cannot exceed 10")
239
+ if not 0.0 <= min_support <= 1.0:
240
+ raise ValueError("min_support must be between 0.0 and 1.0")
241
+
242
+ time_window_s = time_window_ms / 1000.0
243
+
244
+ # Get all messages sorted by timestamp
245
+ all_messages = sorted(session._messages.messages, key=lambda m: m.timestamp)
246
+
247
+ if not all_messages:
248
+ return []
249
+
250
+ # Find maximum message frequency (for support calculation)
251
+ id_counts: dict[int, int] = defaultdict(int)
252
+ for msg in all_messages:
253
+ id_counts[msg.arbitration_id] += 1
254
+ max_count = max(id_counts.values()) if id_counts else 1
255
+
256
+ # Mine sequences of increasing length
257
+ sequences: dict[tuple[int, ...], list[list[float]]] = defaultdict(list)
258
+
259
+ # Start with 2-message sequences
260
+ for i, msg_a in enumerate(all_messages):
261
+ # Look forward in time window
262
+ sequence_start = msg_a.timestamp
263
+ current_sequence = [msg_a.arbitration_id]
264
+ timing = []
265
+
266
+ for msg_b in all_messages[i + 1 :]:
267
+ if msg_b.timestamp - sequence_start > time_window_s:
268
+ break
269
+
270
+ # Extend sequence
271
+ current_sequence.append(msg_b.arbitration_id)
272
+ timing.append((msg_b.timestamp - msg_a.timestamp) * 1000) # ms
273
+
274
+ # Record sequences of desired length
275
+ if 2 <= len(current_sequence) <= max_sequence_length:
276
+ seq_key = tuple(current_sequence)
277
+ sequences[seq_key].append(timing.copy())
278
+
279
+ # Update for next iteration
280
+ msg_a = msg_b
281
+ if len(current_sequence) >= max_sequence_length:
282
+ break
283
+
284
+ # Build MessageSequence objects
285
+ result = []
286
+ for seq_ids, timing_lists in sequences.items():
287
+ occurrences = len(timing_lists)
288
+ support = occurrences / max_count
289
+
290
+ if support >= min_support:
291
+ # Calculate average timing between consecutive messages
292
+ avg_timing = []
293
+ if timing_lists:
294
+ # timing_lists[i] has len(seq_ids) - 1 elements
295
+ num_gaps = len(seq_ids) - 1
296
+ for gap_idx in range(num_gaps):
297
+ gap_times = [t[gap_idx] for t in timing_lists if gap_idx < len(t)]
298
+ if gap_times:
299
+ avg_timing.append(float(np.mean(gap_times)))
300
+
301
+ result.append(
302
+ MessageSequence(
303
+ ids=list(seq_ids),
304
+ occurrences=occurrences,
305
+ avg_timing=avg_timing,
306
+ support=float(support),
307
+ )
308
+ )
309
+
310
+ # Sort by support (descending), then by occurrences
311
+ result.sort(key=lambda s: (s.support, s.occurrences), reverse=True)
312
+
313
+ return result
314
+
315
+ @staticmethod
316
+ def find_temporal_correlations(
317
+ session: CANSession,
318
+ max_delay_ms: float = 100,
319
+ ) -> dict[tuple[int, int], TemporalCorrelation]:
320
+ """Find temporal correlations between messages.
321
+
322
+ Analyzes timing relationships to determine which messages consistently
323
+ follow others with predictable delays. Unlike message pairs, this focuses
324
+ on statistical correlation rather than simple co-occurrence.
325
+
326
+ Args:
327
+ session: CAN session to analyze.
328
+ max_delay_ms: Maximum delay to consider for correlations.
329
+
330
+ Returns:
331
+ Dictionary mapping (leader_id, follower_id) to TemporalCorrelation.
332
+ """
333
+ max_delay_s = max_delay_ms / 1000.0
334
+
335
+ # Get all messages sorted by timestamp
336
+ all_messages = sorted(session._messages.messages, key=lambda m: m.timestamp)
337
+
338
+ # Track correlations: (leader_id, follower_id) -> list of delays
339
+ correlation_delays: dict[tuple[int, int], list[float]] = defaultdict(list)
340
+
341
+ # For each message, find the next occurrence of each other message ID
342
+ # within the time window
343
+ for i, leader_msg in enumerate(all_messages):
344
+ leader_id = leader_msg.arbitration_id
345
+
346
+ # Track which follower IDs we've seen (only count first occurrence)
347
+ seen_followers = set()
348
+
349
+ # Look forward for followers
350
+ for follower_msg in all_messages[i + 1 :]:
351
+ delay = follower_msg.timestamp - leader_msg.timestamp
352
+ if delay > max_delay_s:
353
+ break # Outside window
354
+
355
+ follower_id = follower_msg.arbitration_id
356
+
357
+ # Skip same ID
358
+ if follower_id == leader_id:
359
+ continue
360
+
361
+ # Only record first occurrence of each follower ID
362
+ if follower_id not in seen_followers:
363
+ correlation_delays[(leader_id, follower_id)].append(delay * 1000)
364
+ seen_followers.add(follower_id)
365
+
366
+ # Build TemporalCorrelation objects
367
+ correlations = {}
368
+ for (leader_id, follower_id), delays in correlation_delays.items():
369
+ if len(delays) >= 2: # Need at least 2 samples for std
370
+ avg_delay = np.mean(delays)
371
+ std_delay = np.std(delays)
372
+
373
+ correlations[(leader_id, follower_id)] = TemporalCorrelation(
374
+ leader_id=leader_id,
375
+ follower_id=follower_id,
376
+ avg_delay_ms=float(avg_delay),
377
+ std_delay_ms=float(std_delay),
378
+ occurrences=len(delays),
379
+ )
380
+
381
+ return correlations