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,331 @@
1
+ """ASAM MDF/MF4 (Measurement Data Format) file loader.
2
+
3
+ This module provides loading of MDF4/MF4 files using the asammdf library.
4
+ MDF is an industry-standard format for storing measurement data,
5
+ commonly used with CAN bus loggers like CANedge.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from oscura.automotive.can.models import CANMessage, CANMessageList
14
+
15
+ if TYPE_CHECKING:
16
+ import numpy.typing as npt
17
+
18
+ __all__ = ["load_mdf"]
19
+
20
+
21
+ def load_mdf(file_path: Path | str) -> CANMessageList:
22
+ """Load CAN messages from an MDF/MF4 file.
23
+
24
+ This loader supports both MDF3 and MDF4 (MF4) formats. It handles various
25
+ CAN logging structures commonly found in MDF files from tools like CANedge,
26
+ Vector CANalyzer, ETAS INCA, and others.
27
+
28
+ The loader attempts multiple extraction strategies:
29
+ 1. Bus logging format (CAN frames as structured records)
30
+ 2. Signal-based format (CAN ID, data, timestamps as separate channels)
31
+ 3. Raw frame format (binary CAN frames)
32
+
33
+ Args:
34
+ file_path: Path to the MDF/MF4 file.
35
+
36
+ Returns:
37
+ CANMessageList containing all parsed CAN messages.
38
+
39
+ Raises:
40
+ ImportError: If asammdf is not installed.
41
+ FileNotFoundError: If file doesn't exist.
42
+ ValueError: If file cannot be parsed or contains no CAN data.
43
+
44
+ Example:
45
+ >>> messages = load_mdf("capture.mf4")
46
+ >>> print(f"Loaded {len(messages)} messages")
47
+ >>> print(f"Unique IDs: {len(messages.unique_ids())}")
48
+ """
49
+ try:
50
+ from asammdf import MDF # type: ignore[import-untyped]
51
+ except ImportError as e:
52
+ raise ImportError(
53
+ "asammdf is required for MDF/MF4 file support. "
54
+ "Install with: pip install 'oscura[automotive]'"
55
+ ) from e
56
+
57
+ path = Path(file_path)
58
+ if not path.exists():
59
+ raise FileNotFoundError(f"MDF file not found: {path}")
60
+
61
+ messages = CANMessageList()
62
+
63
+ try:
64
+ # Open MDF file (supports both MDF3 and MDF4)
65
+ with MDF(str(path)) as mdf:
66
+ # Strategy 1: Try to extract CAN bus logging data
67
+ # Look for channels with CAN-related names
68
+ _extract_can_bus_logging(mdf, messages)
69
+
70
+ # Strategy 2: If no messages found, try signal-based extraction
71
+ # Some MDF files store CAN ID, data, timestamp as separate signals
72
+ if not messages.messages:
73
+ _extract_can_signals(mdf, messages)
74
+
75
+ # Strategy 3: Try iterating through all channels looking for CAN data patterns
76
+ if not messages.messages:
77
+ _extract_can_channels(mdf, messages)
78
+
79
+ except Exception as e:
80
+ raise ValueError(f"Failed to parse MDF file {path}: {e}") from e
81
+
82
+ if not messages.messages:
83
+ raise ValueError(
84
+ f"No CAN messages found in MDF file {path}. "
85
+ "File may not contain CAN bus data or uses unsupported format."
86
+ )
87
+
88
+ return messages
89
+
90
+
91
+ def _extract_can_bus_logging(mdf: Any, messages: CANMessageList) -> None:
92
+ """Extract CAN messages from bus logging format.
93
+
94
+ This handles MDF files that store CAN frames as structured bus logging data,
95
+ common in Vector tools and CANedge loggers.
96
+
97
+ Args:
98
+ mdf: Opened MDF file object.
99
+ messages: CANMessageList to append extracted messages to.
100
+ """
101
+ # Common CAN bus logging channel patterns
102
+ can_bus_patterns = [
103
+ "CAN_DataFrame",
104
+ "CANBus",
105
+ "CAN_Message",
106
+ "CAN.DataFrames",
107
+ "CAN Bus",
108
+ ]
109
+
110
+ for channel_name in mdf.channels_db:
111
+ # Check if channel name matches CAN bus logging pattern
112
+ if any(pattern in channel_name for pattern in can_bus_patterns):
113
+ try:
114
+ signal = mdf.get(channel_name)
115
+
116
+ # Extract timestamps
117
+ timestamps = signal.timestamps
118
+
119
+ # CAN data could be in samples (structured) or as raw bytes
120
+ samples = signal.samples
121
+
122
+ # Handle different sample structures
123
+ if hasattr(samples, "dtype") and samples.dtype.names:
124
+ # Structured array with fields
125
+ _extract_structured_can_frames(samples, timestamps, messages)
126
+ else:
127
+ # Try to interpret as raw CAN frames
128
+ _extract_raw_can_frames(samples, timestamps, messages)
129
+
130
+ except Exception:
131
+ # Skip channels that fail to parse
132
+ continue
133
+
134
+
135
+ def _extract_structured_can_frames(
136
+ samples: npt.NDArray[Any], timestamps: npt.NDArray[Any], messages: CANMessageList
137
+ ) -> None:
138
+ """Extract CAN messages from structured array format.
139
+
140
+ Args:
141
+ samples: Structured numpy array with CAN frame fields.
142
+ timestamps: Array of timestamps.
143
+ messages: CANMessageList to append to.
144
+ """
145
+ # Common field names in structured CAN logging
146
+ id_fields = ["ID", "id", "BusID", "Identifier", "ArbitrationID"]
147
+ data_fields = ["Data", "data", "DataBytes", "Payload"]
148
+ dlc_fields = ["DLC", "dlc", "DataLength"]
149
+
150
+ # Find which fields are present
151
+ field_names = samples.dtype.names
152
+ if not field_names:
153
+ return
154
+
155
+ id_field = next((f for f in id_fields if f in field_names), None)
156
+ data_field = next((f for f in data_fields if f in field_names), None)
157
+ dlc_field = next((f for f in dlc_fields if f in field_names), None)
158
+
159
+ if not id_field:
160
+ return
161
+
162
+ for i in range(len(samples)):
163
+ try:
164
+ # Extract CAN ID
165
+ arb_id = int(samples[id_field][i])
166
+
167
+ # Extract data bytes
168
+ if data_field:
169
+ data_bytes = samples[data_field][i]
170
+ if isinstance(data_bytes, bytes):
171
+ data = data_bytes
172
+ else:
173
+ # Convert array to bytes
174
+ data = bytes(data_bytes)
175
+ else:
176
+ data = b""
177
+
178
+ # Extract DLC if available
179
+ if dlc_field:
180
+ dlc = int(samples[dlc_field][i])
181
+ data = data[:dlc]
182
+
183
+ # Determine if extended ID
184
+ is_extended = arb_id > 0x7FF
185
+
186
+ # Create message
187
+ can_msg = CANMessage(
188
+ arbitration_id=arb_id,
189
+ timestamp=float(timestamps[i]),
190
+ data=data,
191
+ is_extended=is_extended,
192
+ is_fd=False, # MDF doesn't typically indicate CAN-FD
193
+ channel=0, # Channel info not always available in MDF
194
+ )
195
+ messages.append(can_msg)
196
+
197
+ except (IndexError, ValueError, TypeError):
198
+ # Skip malformed frames
199
+ continue
200
+
201
+
202
+ def _extract_raw_can_frames(
203
+ samples: npt.NDArray[Any], timestamps: npt.NDArray[Any], messages: CANMessageList
204
+ ) -> None:
205
+ """Extract CAN messages from raw byte format.
206
+
207
+ Args:
208
+ samples: Raw bytes array.
209
+ timestamps: Array of timestamps.
210
+ messages: CANMessageList to append to.
211
+ """
212
+ # Attempt to parse raw CAN frames
213
+ # Typical raw CAN frame: [ID (4 bytes)] [DLC (1 byte)] [Data (up to 8 bytes)]
214
+ for i in range(len(samples)):
215
+ try:
216
+ frame = samples[i]
217
+ if isinstance(frame, bytes) and len(frame) >= 5:
218
+ # Parse standard raw CAN frame structure
219
+ arb_id = int.from_bytes(frame[0:4], byteorder="little")
220
+ dlc = min(frame[4], 8)
221
+ data = frame[5 : 5 + dlc]
222
+
223
+ # Determine if extended ID
224
+ is_extended = arb_id > 0x7FF
225
+
226
+ can_msg = CANMessage(
227
+ arbitration_id=arb_id,
228
+ timestamp=float(timestamps[i]),
229
+ data=data,
230
+ is_extended=is_extended,
231
+ is_fd=False,
232
+ channel=0,
233
+ )
234
+ messages.append(can_msg)
235
+
236
+ except (IndexError, ValueError, TypeError):
237
+ continue
238
+
239
+
240
+ def _extract_can_signals(mdf: Any, messages: CANMessageList) -> None:
241
+ """Extract CAN messages from separate signal channels.
242
+
243
+ This handles MDF files where CAN ID, data, and timestamps are stored
244
+ as separate channels/signals.
245
+
246
+ Args:
247
+ mdf: Opened MDF file object.
248
+ messages: CANMessageList to append to.
249
+ """
250
+ # Look for separate CAN ID and data channels
251
+ id_patterns = ["CAN_ID", "CANID", "CAN.ID", "Identifier"]
252
+ data_patterns = ["CAN_Data", "CANData", "CAN.Data", "Payload"]
253
+
254
+ id_channels = []
255
+ data_channels = []
256
+
257
+ for channel_name in mdf.channels_db:
258
+ if any(pattern in channel_name for pattern in id_patterns):
259
+ id_channels.append(channel_name)
260
+ elif any(pattern in channel_name for pattern in data_patterns):
261
+ data_channels.append(channel_name)
262
+
263
+ # Try to match ID and data channels
264
+ for id_channel in id_channels:
265
+ for data_channel in data_channels:
266
+ try:
267
+ id_signal = mdf.get(id_channel)
268
+ data_signal = mdf.get(data_channel)
269
+
270
+ # Ensure same length
271
+ if len(id_signal.timestamps) != len(data_signal.timestamps):
272
+ continue
273
+
274
+ # Extract messages
275
+ for i in range(len(id_signal.timestamps)):
276
+ arb_id = int(id_signal.samples[i])
277
+ data = bytes(data_signal.samples[i])
278
+ timestamp = float(id_signal.timestamps[i])
279
+
280
+ is_extended = arb_id > 0x7FF
281
+
282
+ can_msg = CANMessage(
283
+ arbitration_id=arb_id,
284
+ timestamp=timestamp,
285
+ data=data,
286
+ is_extended=is_extended,
287
+ is_fd=False,
288
+ channel=0,
289
+ )
290
+ messages.append(can_msg)
291
+
292
+ except Exception:
293
+ continue
294
+
295
+
296
+ def _extract_can_channels(mdf: Any, messages: CANMessageList) -> None:
297
+ """Extract CAN messages by searching all channels for CAN patterns.
298
+
299
+ This is a fallback strategy that looks for any channel containing
300
+ 'CAN' in the name and attempts to extract CAN data.
301
+
302
+ Args:
303
+ mdf: Opened MDF file object.
304
+ messages: CANMessageList to append to.
305
+ """
306
+ can_keywords = ["CAN", "can"]
307
+
308
+ for channel_name in mdf.channels_db:
309
+ # Only process channels with CAN in the name
310
+ if not any(keyword in channel_name for keyword in can_keywords):
311
+ continue
312
+
313
+ try:
314
+ signal = mdf.get(channel_name)
315
+ timestamps = signal.timestamps
316
+ samples = signal.samples
317
+
318
+ # Try different interpretations
319
+ if hasattr(samples, "dtype"):
320
+ if samples.dtype.names:
321
+ # Structured
322
+ _extract_structured_can_frames(samples, timestamps, messages)
323
+ elif samples.dtype == "uint32" or samples.dtype == "int32":
324
+ # Could be CAN IDs
325
+ pass
326
+ elif len(samples.shape) == 2 and samples.shape[1] >= 8:
327
+ # Could be CAN data bytes
328
+ pass
329
+
330
+ except Exception:
331
+ continue
@@ -0,0 +1,132 @@
1
+ """PCAP file loader for CAN bus data.
2
+
3
+ This module provides loading of PCAP files containing SocketCAN frames.
4
+ PCAP is a common packet capture format that can contain CAN frames from
5
+ network interfaces or recorded with tools like Wireshark or tcpdump.
6
+
7
+ Supported formats:
8
+ - SocketCAN frames (Linux can0, can1, etc.)
9
+ - CAN frames from pcap-ng format
10
+
11
+ Requirements:
12
+ - scapy library (install with: uv pip install scapy)
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from pathlib import Path
18
+ from typing import TYPE_CHECKING
19
+
20
+ from oscura.automotive.can.models import CANMessage, CANMessageList
21
+
22
+ if TYPE_CHECKING:
23
+ from scapy.packet import Packet # type: ignore[import-not-found]
24
+
25
+ __all__ = ["load_pcap"]
26
+
27
+
28
+ def load_pcap(file_path: Path | str) -> CANMessageList:
29
+ """Load CAN messages from a PCAP file.
30
+
31
+ This function reads PCAP files containing SocketCAN frames and converts
32
+ them to Oscura's CANMessage format. It uses scapy to parse the PCAP
33
+ file and extract CAN frames.
34
+
35
+ Args:
36
+ file_path: Path to the PCAP or PCAPNG file.
37
+
38
+ Returns:
39
+ CANMessageList containing all parsed CAN messages.
40
+
41
+ Raises:
42
+ FileNotFoundError: If file doesn't exist.
43
+ ImportError: If scapy is not installed.
44
+ ValueError: If file cannot be parsed or contains no CAN frames.
45
+
46
+ Example:
47
+ >>> messages = load_pcap("capture.pcap")
48
+ >>> print(f"Loaded {len(messages)} messages")
49
+
50
+ Note:
51
+ Requires scapy to be installed:
52
+ uv pip install oscura[automotive]
53
+
54
+ Or manually:
55
+ uv pip install scapy
56
+ """
57
+ path = Path(file_path)
58
+ if not path.exists():
59
+ raise FileNotFoundError(f"PCAP file not found: {path}")
60
+
61
+ try:
62
+ from scapy.all import rdpcap # type: ignore[import-not-found]
63
+ from scapy.layers.can import CAN # type: ignore[import-not-found]
64
+ except ImportError as e:
65
+ msg = "scapy library is required for PCAP loading. Install with: uv pip install scapy"
66
+ raise ImportError(msg) from e
67
+
68
+ messages = CANMessageList()
69
+
70
+ try:
71
+ # Read PCAP file
72
+ packets = rdpcap(str(path))
73
+
74
+ # Extract CAN frames
75
+ first_timestamp: float | None = None
76
+ for packet in packets:
77
+ # Check if packet contains CAN layer
78
+ if CAN in packet:
79
+ can_frame: Packet = packet[CAN]
80
+
81
+ # Get timestamp
82
+ if hasattr(packet, "time"):
83
+ if first_timestamp is None:
84
+ first_timestamp = float(packet.time)
85
+ timestamp = float(packet.time) - first_timestamp
86
+ else:
87
+ timestamp = 0.0
88
+
89
+ # Extract CAN ID and data
90
+ arb_id = int(can_frame.identifier)
91
+
92
+ # Get data bytes (scapy stores CAN data as bytes)
93
+ if hasattr(can_frame, "data"):
94
+ data = bytes(can_frame.data)
95
+ else:
96
+ data = b""
97
+
98
+ # Determine if extended ID (bit 31 indicates extended format)
99
+ # SocketCAN uses bit 31 for extended frame flag
100
+ is_extended = bool(arb_id & 0x80000000)
101
+ if is_extended:
102
+ arb_id = arb_id & 0x1FFFFFFF # Mask to get 29-bit ID
103
+
104
+ # Determine if CAN-FD (scapy may have an FD flag)
105
+ is_fd = hasattr(can_frame, "flags") and (can_frame.flags & 0x01)
106
+
107
+ # Extract channel if available
108
+ channel = 0
109
+ if hasattr(can_frame, "channel"):
110
+ channel = int(can_frame.channel)
111
+
112
+ # Create CANMessage
113
+ can_msg = CANMessage(
114
+ arbitration_id=arb_id,
115
+ timestamp=timestamp,
116
+ data=data,
117
+ is_extended=is_extended,
118
+ is_fd=is_fd,
119
+ channel=channel,
120
+ )
121
+ messages.append(can_msg)
122
+
123
+ except Exception as e:
124
+ raise ValueError(f"Failed to parse PCAP file {path}: {e}") from e
125
+
126
+ if len(messages) == 0:
127
+ raise ValueError(
128
+ f"No CAN frames found in PCAP file {path}. "
129
+ "Ensure the capture contains SocketCAN or CAN frames."
130
+ )
131
+
132
+ return messages
@@ -0,0 +1,14 @@
1
+ """OBD-II diagnostic protocol support.
2
+
3
+ This module provides OBD-II (On-Board Diagnostics) protocol decoding
4
+ for standard vehicle diagnostics.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ["PID", "OBD2Decoder", "OBD2Response"]
10
+
11
+ try:
12
+ from oscura.automotive.obd.decoder import PID, OBD2Decoder, OBD2Response
13
+ except ImportError:
14
+ pass