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,212 @@
1
+ """Signal and message correlation analysis.
2
+
3
+ This module provides correlation analysis for CAN signals to discover
4
+ relationships between different signals and messages.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ import numpy as np
12
+ from scipy.stats import pearsonr
13
+
14
+ if TYPE_CHECKING:
15
+ from oscura.automotive.can.models import CANMessageList, SignalDefinition
16
+ from oscura.automotive.can.session import CANSession
17
+
18
+ __all__ = ["CorrelationAnalyzer"]
19
+
20
+
21
+ class CorrelationAnalyzer:
22
+ """Analyze correlations between CAN signals and messages."""
23
+
24
+ @staticmethod
25
+ def correlate_signals(
26
+ messages1: CANMessageList,
27
+ signal_def1: SignalDefinition,
28
+ messages2: CANMessageList,
29
+ signal_def2: SignalDefinition,
30
+ max_time_shift: float = 0.0,
31
+ ) -> dict[str, float | int]:
32
+ """Compute correlation between two signals.
33
+
34
+ Args:
35
+ messages1: Messages containing first signal.
36
+ signal_def1: First signal definition.
37
+ messages2: Messages containing second signal.
38
+ signal_def2: Second signal definition.
39
+ max_time_shift: Maximum time shift to consider (seconds).
40
+
41
+ Returns:
42
+ Dictionary with correlation results:
43
+ - correlation: Pearson correlation coefficient
44
+ - p_value: Statistical significance
45
+ - lag: Time lag with maximum correlation (if time shift enabled)
46
+ - sample_count: Number of samples used
47
+ """
48
+ # Decode signals
49
+ values1 = []
50
+ timestamps1 = []
51
+ for msg in messages1.messages:
52
+ try:
53
+ value = signal_def1.decode(msg.data)
54
+ values1.append(value)
55
+ timestamps1.append(msg.timestamp)
56
+ except Exception:
57
+ pass
58
+
59
+ values2 = []
60
+ timestamps2 = []
61
+ for msg in messages2.messages:
62
+ try:
63
+ value = signal_def2.decode(msg.data)
64
+ values2.append(value)
65
+ timestamps2.append(msg.timestamp)
66
+ except Exception:
67
+ pass
68
+
69
+ if len(values1) < 2 or len(values2) < 2:
70
+ return {
71
+ "correlation": 0.0,
72
+ "p_value": 1.0,
73
+ "lag": 0.0,
74
+ "sample_count": 0,
75
+ }
76
+
77
+ # Align signals by timestamp (simple approach: nearest neighbor)
78
+ aligned_values1 = []
79
+ aligned_values2 = []
80
+
81
+ for t1, v1 in zip(timestamps1, values1, strict=False):
82
+ # Find closest timestamp in signal 2
83
+ time_diffs = [abs(t2 - t1) for t2 in timestamps2]
84
+ if min(time_diffs) <= max(0.1, max_time_shift): # Within 100ms or max_time_shift
85
+ closest_idx = time_diffs.index(min(time_diffs))
86
+ aligned_values1.append(v1)
87
+ aligned_values2.append(values2[closest_idx])
88
+
89
+ if len(aligned_values1) < 2:
90
+ return {
91
+ "correlation": 0.0,
92
+ "p_value": 1.0,
93
+ "lag": 0.0,
94
+ "sample_count": 0,
95
+ }
96
+
97
+ # Compute Pearson correlation
98
+ arr1 = np.array(aligned_values1)
99
+ arr2 = np.array(aligned_values2)
100
+
101
+ correlation, p_value = pearsonr(arr1, arr2)
102
+
103
+ return {
104
+ "correlation": float(correlation),
105
+ "p_value": float(p_value),
106
+ "lag": 0.0,
107
+ "sample_count": len(aligned_values1),
108
+ }
109
+
110
+ @staticmethod
111
+ def correlate_bytes(
112
+ messages1: CANMessageList,
113
+ byte_pos1: int,
114
+ messages2: CANMessageList,
115
+ byte_pos2: int,
116
+ ) -> float:
117
+ """Compute correlation between two byte positions.
118
+
119
+ Args:
120
+ messages1: First message collection.
121
+ byte_pos1: Byte position in first message.
122
+ messages2: Second message collection.
123
+ byte_pos2: Byte position in second message.
124
+
125
+ Returns:
126
+ Pearson correlation coefficient (-1.0 to 1.0).
127
+ """
128
+ # Extract byte values
129
+ values1 = []
130
+ for msg in messages1.messages:
131
+ if len(msg.data) > byte_pos1:
132
+ values1.append(msg.data[byte_pos1])
133
+
134
+ values2 = []
135
+ for msg in messages2.messages:
136
+ if len(msg.data) > byte_pos2:
137
+ values2.append(msg.data[byte_pos2])
138
+
139
+ if len(values1) < 2 or len(values2) < 2:
140
+ return 0.0
141
+
142
+ # Truncate to same length
143
+ min_len = min(len(values1), len(values2))
144
+ values1 = values1[:min_len]
145
+ values2 = values2[:min_len]
146
+
147
+ # Compute correlation
148
+ arr1 = np.array(values1, dtype=float)
149
+ arr2 = np.array(values2, dtype=float)
150
+
151
+ # Check for zero variance
152
+ if np.std(arr1) == 0 or np.std(arr2) == 0:
153
+ return 0.0
154
+
155
+ correlation, _ = pearsonr(arr1, arr2)
156
+
157
+ return float(correlation)
158
+
159
+ @staticmethod
160
+ def find_correlated_messages(
161
+ session: CANSession,
162
+ arbitration_id: int,
163
+ threshold: float = 0.7,
164
+ ) -> dict[int, float]:
165
+ """Find messages correlated with a given message ID.
166
+
167
+ Args:
168
+ session: CANSession to analyze.
169
+ arbitration_id: CAN ID to find correlations for.
170
+ threshold: Minimum correlation threshold (0.0-1.0).
171
+
172
+ Returns:
173
+ Dictionary mapping correlated message IDs to correlation scores.
174
+ """
175
+
176
+ correlations = {}
177
+
178
+ # Get messages for the target ID
179
+ target_messages = session._messages.filter_by_id(arbitration_id)
180
+ if not target_messages.messages:
181
+ return {}
182
+
183
+ # Determine max DLC for target
184
+ max_dlc_target = max(msg.dlc for msg in target_messages.messages)
185
+
186
+ # Check all other unique IDs
187
+ for other_id in session.unique_ids():
188
+ if other_id == arbitration_id:
189
+ continue
190
+
191
+ other_messages = session._messages.filter_by_id(other_id)
192
+ max_dlc_other = max(msg.dlc for msg in other_messages.messages)
193
+
194
+ # Check byte-by-byte correlation
195
+ max_correlation = 0.0
196
+
197
+ for byte_pos_target in range(max_dlc_target):
198
+ for byte_pos_other in range(max_dlc_other):
199
+ corr = CorrelationAnalyzer.correlate_bytes(
200
+ target_messages,
201
+ byte_pos_target,
202
+ other_messages,
203
+ byte_pos_other,
204
+ )
205
+
206
+ if abs(corr) > abs(max_correlation):
207
+ max_correlation = corr
208
+
209
+ if abs(max_correlation) >= threshold:
210
+ correlations[other_id] = max_correlation
211
+
212
+ return correlations
@@ -0,0 +1,355 @@
1
+ """Discovery documentation for CAN reverse engineering.
2
+
3
+ This module provides functionality to save and load CAN reverse engineering
4
+ discoveries in the .tkcan format (YAML-based with evidence tracking).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import asdict, dataclass, field
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Any, Literal
13
+
14
+ import yaml
15
+
16
+ if TYPE_CHECKING:
17
+ from oscura.automotive.can.models import SignalDefinition
18
+
19
+ __all__ = ["DiscoveryDocument", "Hypothesis", "MessageDiscovery", "SignalDiscovery"]
20
+
21
+
22
+ @dataclass
23
+ class SignalDiscovery:
24
+ """Documented signal discovery.
25
+
26
+ Attributes:
27
+ name: Signal name.
28
+ start_bit: Starting bit position.
29
+ length: Signal length in bits.
30
+ byte_order: Byte order.
31
+ value_type: Value type.
32
+ scale: Scaling factor.
33
+ offset: Offset value.
34
+ unit: Physical unit.
35
+ min_value: Observed minimum value.
36
+ max_value: Observed maximum value.
37
+ confidence: Confidence score (0.0-1.0).
38
+ evidence: List of evidence supporting this discovery.
39
+ comment: Additional notes.
40
+ """
41
+
42
+ name: str
43
+ start_bit: int
44
+ length: int
45
+ byte_order: Literal["big_endian", "little_endian"] = "big_endian"
46
+ value_type: Literal["unsigned", "signed", "float"] = "unsigned"
47
+ scale: float = 1.0
48
+ offset: float = 0.0
49
+ unit: str = ""
50
+ min_value: float | None = None
51
+ max_value: float | None = None
52
+ confidence: float = 0.0
53
+ evidence: list[str] = field(default_factory=list)
54
+ comment: str = ""
55
+
56
+ def to_dict(self) -> dict[str, Any]:
57
+ """Convert to dictionary."""
58
+ return {
59
+ k: v
60
+ for k, v in asdict(self).items()
61
+ if v is not None and (not isinstance(v, list) or v)
62
+ }
63
+
64
+ @classmethod
65
+ def from_definition(
66
+ cls,
67
+ definition: SignalDefinition,
68
+ confidence: float = 1.0,
69
+ evidence: list[str] | None = None,
70
+ ) -> SignalDiscovery:
71
+ """Create from SignalDefinition.
72
+
73
+ Args:
74
+ definition: Signal definition.
75
+ confidence: Confidence score.
76
+ evidence: Evidence list.
77
+
78
+ Returns:
79
+ SignalDiscovery instance.
80
+ """
81
+ return cls(
82
+ name=definition.name,
83
+ start_bit=definition.start_bit,
84
+ length=definition.length,
85
+ byte_order=definition.byte_order,
86
+ value_type=definition.value_type,
87
+ scale=definition.scale,
88
+ offset=definition.offset,
89
+ unit=definition.unit,
90
+ min_value=definition.min_value,
91
+ max_value=definition.max_value,
92
+ confidence=confidence,
93
+ evidence=evidence or [],
94
+ comment=definition.comment,
95
+ )
96
+
97
+
98
+ @dataclass
99
+ class Hypothesis:
100
+ """A hypothesis under testing.
101
+
102
+ Attributes:
103
+ message_id: CAN ID this hypothesis applies to.
104
+ signal: Signal name.
105
+ hypothesis: Description of hypothesis.
106
+ status: Status ('testing', 'confirmed', 'rejected').
107
+ test_plan: Test plan or next steps.
108
+ created: Creation timestamp.
109
+ updated: Last update timestamp.
110
+ """
111
+
112
+ message_id: int
113
+ signal: str
114
+ hypothesis: str
115
+ status: Literal["testing", "confirmed", "rejected"] = "testing"
116
+ test_plan: str = ""
117
+ created: str = field(default_factory=lambda: datetime.now().isoformat())
118
+ updated: str = field(default_factory=lambda: datetime.now().isoformat())
119
+
120
+ def to_dict(self) -> dict[str, Any]:
121
+ """Convert to dictionary."""
122
+ return {
123
+ "message_id": f"0x{self.message_id:03X}",
124
+ "signal": self.signal,
125
+ "hypothesis": self.hypothesis,
126
+ "status": self.status,
127
+ "test_plan": self.test_plan,
128
+ "created": self.created,
129
+ "updated": self.updated,
130
+ }
131
+
132
+
133
+ @dataclass
134
+ class MessageDiscovery:
135
+ """Documented message discovery.
136
+
137
+ Attributes:
138
+ id: CAN arbitration ID.
139
+ name: Message name.
140
+ length: Data length (bytes).
141
+ transmitter: Transmitter node (if known).
142
+ cycle_time_ms: Message period in milliseconds.
143
+ confidence: Confidence score (0.0-1.0).
144
+ evidence: List of evidence supporting this discovery.
145
+ signals: List of discovered signals.
146
+ comment: Additional notes.
147
+ """
148
+
149
+ id: int
150
+ name: str
151
+ length: int
152
+ transmitter: str | None = None
153
+ cycle_time_ms: float | None = None
154
+ confidence: float = 0.0
155
+ evidence: list[str] = field(default_factory=list)
156
+ signals: list[SignalDiscovery] = field(default_factory=list)
157
+ comment: str = ""
158
+
159
+ def to_dict(self) -> dict[str, Any]:
160
+ """Convert to dictionary."""
161
+ return {
162
+ "id": f"0x{self.id:03X}",
163
+ "name": self.name,
164
+ "length": self.length,
165
+ "transmitter": self.transmitter,
166
+ "cycle_time_ms": self.cycle_time_ms,
167
+ "confidence": self.confidence,
168
+ "evidence": self.evidence if self.evidence else None,
169
+ "signals": [sig.to_dict() for sig in self.signals] if self.signals else None,
170
+ "comment": self.comment if self.comment else None,
171
+ }
172
+
173
+
174
+ @dataclass
175
+ class VehicleInfo:
176
+ """Vehicle information.
177
+
178
+ Attributes:
179
+ make: Vehicle manufacturer.
180
+ model: Vehicle model.
181
+ year: Model year.
182
+ vin: Vehicle Identification Number.
183
+ notes: Additional notes.
184
+ """
185
+
186
+ make: str = "Unknown"
187
+ model: str = "Unknown"
188
+ year: str | None = None
189
+ vin: str | None = None
190
+ notes: str = ""
191
+
192
+ def to_dict(self) -> dict[str, Any]:
193
+ """Convert to dictionary."""
194
+ return {k: v for k, v in asdict(self).items() if v}
195
+
196
+
197
+ class DiscoveryDocument:
198
+ """CAN reverse engineering discovery document.
199
+
200
+ This class manages a collection of discovered messages, signals,
201
+ and hypotheses with evidence tracking.
202
+ """
203
+
204
+ def __init__(self) -> None:
205
+ """Initialize empty discovery document."""
206
+ self.format_version = "1.0"
207
+ self.vehicle = VehicleInfo()
208
+ self.messages: dict[int, MessageDiscovery] = {}
209
+ self.hypotheses: list[Hypothesis] = []
210
+
211
+ def add_message(self, discovery: MessageDiscovery) -> None:
212
+ """Add message discovery.
213
+
214
+ Args:
215
+ discovery: MessageDiscovery to add.
216
+ """
217
+ self.messages[discovery.id] = discovery
218
+
219
+ def add_hypothesis(self, hypothesis: Hypothesis) -> None:
220
+ """Add hypothesis.
221
+
222
+ Args:
223
+ hypothesis: Hypothesis to add.
224
+ """
225
+ self.hypotheses.append(hypothesis)
226
+
227
+ def save(self, file_path: Path | str) -> None:
228
+ """Save discoveries to .tkcan file.
229
+
230
+ Args:
231
+ file_path: Output file path.
232
+ """
233
+ path = Path(file_path)
234
+
235
+ # Build YAML structure
236
+ doc = {
237
+ "format_version": self.format_version,
238
+ "vehicle": self.vehicle.to_dict(),
239
+ "messages": [
240
+ msg.to_dict() for msg in sorted(self.messages.values(), key=lambda m: m.id)
241
+ ],
242
+ }
243
+
244
+ if self.hypotheses:
245
+ doc["hypotheses"] = [h.to_dict() for h in self.hypotheses]
246
+
247
+ # Write YAML
248
+ with open(path, "w", encoding="utf-8") as f:
249
+ yaml.safe_dump(doc, f, default_flow_style=False, sort_keys=False, allow_unicode=True)
250
+
251
+ @classmethod
252
+ def load(cls, file_path: Path | str) -> DiscoveryDocument:
253
+ """Load discoveries from .tkcan file.
254
+
255
+ Args:
256
+ file_path: Input file path.
257
+
258
+ Returns:
259
+ DiscoveryDocument instance.
260
+ """
261
+ path = Path(file_path)
262
+
263
+ with open(path, encoding="utf-8") as f:
264
+ data = yaml.safe_load(f)
265
+
266
+ doc = cls()
267
+
268
+ # Load metadata
269
+ doc.format_version = data.get("format_version", "1.0")
270
+
271
+ # Load vehicle info
272
+ if "vehicle" in data:
273
+ v = data["vehicle"]
274
+ doc.vehicle = VehicleInfo(
275
+ make=v.get("make", "Unknown"),
276
+ model=v.get("model", "Unknown"),
277
+ year=v.get("year"),
278
+ vin=v.get("vin"),
279
+ notes=v.get("notes", ""),
280
+ )
281
+
282
+ # Load messages
283
+ for msg_data in data.get("messages", []):
284
+ # Parse ID
285
+ id_str = msg_data["id"]
286
+ if isinstance(id_str, str):
287
+ msg_id = int(id_str, 16) if id_str.startswith("0x") else int(id_str)
288
+ else:
289
+ msg_id = msg_data["id"]
290
+
291
+ # Parse signals
292
+ signals = []
293
+ for sig_data in msg_data.get("signals", []):
294
+ sig = SignalDiscovery(
295
+ name=sig_data["name"],
296
+ start_bit=sig_data["start_bit"],
297
+ length=sig_data["length"],
298
+ byte_order=sig_data.get("byte_order", "big_endian"),
299
+ value_type=sig_data.get("value_type", "unsigned"),
300
+ scale=sig_data.get("scale", 1.0),
301
+ offset=sig_data.get("offset", 0.0),
302
+ unit=sig_data.get("unit", ""),
303
+ min_value=sig_data.get("min_value"),
304
+ max_value=sig_data.get("max_value"),
305
+ confidence=sig_data.get("confidence", 0.0),
306
+ evidence=sig_data.get("evidence", []),
307
+ comment=sig_data.get("comment", ""),
308
+ )
309
+ signals.append(sig)
310
+
311
+ # Create message discovery
312
+ msg_discovery = MessageDiscovery(
313
+ id=msg_id,
314
+ name=msg_data["name"],
315
+ length=msg_data["length"],
316
+ transmitter=msg_data.get("transmitter"),
317
+ cycle_time_ms=msg_data.get("cycle_time_ms"),
318
+ confidence=msg_data.get("confidence", 0.0),
319
+ evidence=msg_data.get("evidence", []),
320
+ signals=signals,
321
+ comment=msg_data.get("comment", ""),
322
+ )
323
+
324
+ doc.add_message(msg_discovery)
325
+
326
+ # Load hypotheses
327
+ for hyp_data in data.get("hypotheses", []):
328
+ # Parse message ID
329
+ id_str = hyp_data["message_id"]
330
+ if isinstance(id_str, str):
331
+ msg_id = int(id_str, 16) if id_str.startswith("0x") else int(id_str)
332
+ else:
333
+ msg_id = hyp_data["message_id"]
334
+
335
+ hyp = Hypothesis(
336
+ message_id=msg_id,
337
+ signal=hyp_data["signal"],
338
+ hypothesis=hyp_data["hypothesis"],
339
+ status=hyp_data.get("status", "testing"),
340
+ test_plan=hyp_data.get("test_plan", ""),
341
+ created=hyp_data.get("created", datetime.now().isoformat()),
342
+ updated=hyp_data.get("updated", datetime.now().isoformat()),
343
+ )
344
+ doc.add_hypothesis(hyp)
345
+
346
+ return doc
347
+
348
+ def __repr__(self) -> str:
349
+ """Human-readable representation."""
350
+ return (
351
+ f"DiscoveryDocument("
352
+ f"{len(self.messages)} messages, "
353
+ f"{sum(len(m.signals) for m in self.messages.values())} signals, "
354
+ f"{len(self.hypotheses)} hypotheses)"
355
+ )