oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.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.0.dist-info}/WHEEL +0 -0
oscura/cli/decode.py ADDED
@@ -0,0 +1,551 @@
1
+ """TraceKit Decode Command implementing CLI-003.
2
+
3
+ Provides CLI for protocol decoding with automatic protocol detection and
4
+ error highlighting.
5
+
6
+
7
+ Example:
8
+ $ oscura decode serial_capture.wfm
9
+ $ oscura decode i2c_bus.wfm --protocol I2C
10
+ $ oscura decode uart.wfm --protocol UART --baud-rate 115200
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ import click
20
+ import numpy as np
21
+
22
+ from oscura.cli.main import format_output
23
+ from oscura.core.types import DigitalTrace, ProtocolPacket, WaveformTrace
24
+
25
+ logger = logging.getLogger("oscura.cli.decode")
26
+
27
+
28
+ @click.command() # type: ignore[misc]
29
+ @click.argument("file", type=click.Path(exists=True)) # type: ignore[misc]
30
+ @click.option( # type: ignore[misc]
31
+ "--protocol",
32
+ type=click.Choice(["uart", "spi", "i2c", "can", "auto"], case_sensitive=False),
33
+ default="auto",
34
+ help="Protocol type (default: auto-detect).",
35
+ )
36
+ @click.option( # type: ignore[misc]
37
+ "--baud-rate",
38
+ type=int,
39
+ default=None,
40
+ help="Baud rate for UART (auto-detect if not specified).",
41
+ )
42
+ @click.option( # type: ignore[misc]
43
+ "--parity",
44
+ type=click.Choice(["none", "even", "odd"], case_sensitive=False),
45
+ default="none",
46
+ help="Parity for UART (default: none).",
47
+ )
48
+ @click.option( # type: ignore[misc]
49
+ "--stop-bits",
50
+ type=click.Choice(["1", "2"]),
51
+ default="1",
52
+ help="Stop bits for UART (default: 1).",
53
+ )
54
+ @click.option( # type: ignore[misc]
55
+ "--show-errors",
56
+ is_flag=True,
57
+ help="Show only errors with context.",
58
+ )
59
+ @click.option( # type: ignore[misc]
60
+ "--output",
61
+ type=click.Choice(["json", "csv", "html", "table"], case_sensitive=False),
62
+ default="table",
63
+ help="Output format (default: table).",
64
+ )
65
+ @click.pass_context # type: ignore[misc]
66
+ def decode(
67
+ ctx: click.Context,
68
+ file: str,
69
+ protocol: str,
70
+ baud_rate: int | None,
71
+ parity: str,
72
+ stop_bits: str,
73
+ show_errors: bool,
74
+ output: str,
75
+ ) -> None:
76
+ """Decode serial protocol data.
77
+
78
+ Automatically detects and decodes common serial protocols (UART, SPI, I2C, CAN).
79
+ Can highlight errors with surrounding context for debugging.
80
+
81
+ Args:
82
+ ctx: Click context object.
83
+ file: Path to waveform file to decode.
84
+ protocol: Protocol type (uart, spi, i2c, can, auto).
85
+ baud_rate: Baud rate for UART (None for auto-detect).
86
+ parity: Parity setting for UART (none, even, odd).
87
+ stop_bits: Number of stop bits for UART (1 or 2).
88
+ show_errors: Show only packets with errors.
89
+ output: Output format (json, csv, html, table).
90
+
91
+ Raises:
92
+ Exception: If decoding fails or file cannot be loaded.
93
+
94
+ Examples:
95
+
96
+ \b
97
+ # Auto-detect and decode protocol
98
+ $ oscura decode serial_capture.wfm
99
+
100
+ \b
101
+ # Decode specific protocol with parameters
102
+ $ oscura decode uart.wfm \\
103
+ --protocol UART \\
104
+ --baud-rate 9600 \\
105
+ --parity even \\
106
+ --stop-bits 2
107
+
108
+ \b
109
+ # Show only errors for debugging
110
+ $ oscura decode problematic.wfm --show-errors
111
+
112
+ \b
113
+ # Generate JSON output
114
+ $ oscura decode i2c.wfm --protocol I2C --output json
115
+ """
116
+ verbose = ctx.obj.get("verbose", 0)
117
+
118
+ if verbose:
119
+ logger.info(f"Decoding: {file}")
120
+ logger.info(f"Protocol: {protocol}")
121
+ if protocol.lower() == "uart" and baud_rate:
122
+ logger.info(f"Baud rate: {baud_rate}")
123
+
124
+ try:
125
+ # Import here to avoid circular imports
126
+ from oscura.loaders import load
127
+
128
+ # Load the trace
129
+ logger.debug(f"Loading trace from {file}")
130
+ trace = load(file)
131
+
132
+ # Perform protocol decoding
133
+ results = _perform_decoding(
134
+ trace=trace, # type: ignore[arg-type]
135
+ protocol=protocol,
136
+ baud_rate=baud_rate,
137
+ parity=parity,
138
+ stop_bits=int(stop_bits),
139
+ show_errors=show_errors,
140
+ )
141
+
142
+ # Add metadata
143
+ results["file"] = str(Path(file).name)
144
+
145
+ # Output results
146
+ formatted = format_output(results, output)
147
+ click.echo(formatted)
148
+
149
+ except Exception as e:
150
+ logger.error(f"Decoding failed: {e}")
151
+ if verbose > 1:
152
+ raise
153
+ click.echo(f"Error: {e}", err=True)
154
+ ctx.exit(1)
155
+
156
+
157
+ def _to_digital(trace: WaveformTrace | DigitalTrace) -> DigitalTrace:
158
+ """Convert waveform trace to digital trace.
159
+
160
+ Args:
161
+ trace: Input trace (waveform or digital).
162
+
163
+ Returns:
164
+ Digital trace with boolean data.
165
+ """
166
+ if isinstance(trace, DigitalTrace):
167
+ return trace
168
+
169
+ # Use midpoint threshold for digitization
170
+ data = trace.data
171
+ threshold = (np.max(data) + np.min(data)) / 2
172
+ digital_data = data > threshold
173
+
174
+ return DigitalTrace(
175
+ data=digital_data,
176
+ metadata=trace.metadata,
177
+ )
178
+
179
+
180
+ def _perform_decoding(
181
+ trace: WaveformTrace | DigitalTrace,
182
+ protocol: str,
183
+ baud_rate: int | None,
184
+ parity: str,
185
+ stop_bits: int,
186
+ show_errors: bool,
187
+ ) -> dict[str, Any]:
188
+ """Perform protocol decoding using actual decoders.
189
+
190
+ Args:
191
+ trace: Trace to decode.
192
+ protocol: Protocol type or 'auto'.
193
+ baud_rate: Optional baud rate for UART.
194
+ parity: Parity setting for UART.
195
+ stop_bits: Stop bits for UART.
196
+ show_errors: Whether to show only errors.
197
+
198
+ Returns:
199
+ Dictionary of decoding results.
200
+ """
201
+ # Import protocol decoders
202
+ from oscura.inference.protocol import detect_protocol
203
+
204
+ sample_rate = trace.metadata.sample_rate
205
+ duration_ms = len(trace.data) / sample_rate * 1e3
206
+
207
+ results: dict[str, Any] = {
208
+ "sample_rate": f"{sample_rate / 1e6:.1f} MHz",
209
+ "samples": len(trace.data),
210
+ "duration": f"{duration_ms:.3f} ms",
211
+ }
212
+
213
+ # Auto-detect protocol if requested
214
+ detected_protocol = protocol
215
+ detection_confidence = 1.0
216
+
217
+ if protocol.lower() == "auto":
218
+ try:
219
+ detection = detect_protocol(trace, min_confidence=0.5, return_candidates=True) # type: ignore[arg-type]
220
+ detected_protocol = detection["protocol"].lower()
221
+ detection_confidence = detection["confidence"]
222
+ results["auto_detection"] = {
223
+ "protocol": detection["protocol"],
224
+ "confidence": f"{detection_confidence:.1%}",
225
+ "candidates": [
226
+ {"protocol": c["protocol"], "confidence": f"{c['confidence']:.1%}"}
227
+ for c in detection.get("candidates", [])[:3]
228
+ ],
229
+ }
230
+ # Extract config suggestions
231
+ if "config" in detection:
232
+ if detected_protocol == "uart" and baud_rate is None:
233
+ baud_rate = detection["config"].get("baud_rate")
234
+ except Exception as e:
235
+ logger.warning(f"Auto-detection failed: {e}, defaulting to UART")
236
+ detected_protocol = "uart"
237
+ detection_confidence = 0.0
238
+
239
+ results["protocol"] = detected_protocol.upper()
240
+
241
+ # Convert to digital trace for decoding
242
+ digital_trace = _to_digital(trace)
243
+
244
+ # Decode based on protocol
245
+ packets: list[ProtocolPacket] = []
246
+ errors: list[dict[str, Any]] = []
247
+
248
+ if detected_protocol == "uart":
249
+ packets, errors, protocol_info = _decode_uart(
250
+ digital_trace, baud_rate, parity, stop_bits, show_errors
251
+ )
252
+ results.update(protocol_info)
253
+
254
+ elif detected_protocol == "spi":
255
+ packets, errors, protocol_info = _decode_spi(digital_trace, show_errors)
256
+ results.update(protocol_info)
257
+
258
+ elif detected_protocol == "i2c":
259
+ packets, errors, protocol_info = _decode_i2c(digital_trace, show_errors)
260
+ results.update(protocol_info)
261
+
262
+ elif detected_protocol == "can":
263
+ packets, errors, protocol_info = _decode_can(digital_trace, baud_rate, show_errors)
264
+ results.update(protocol_info)
265
+
266
+ # Filter to errors only if requested
267
+ if show_errors:
268
+ packets = [p for p in packets if p.errors]
269
+
270
+ # Summarize results
271
+ results["packets_decoded"] = len(packets)
272
+ results["errors_found"] = len(errors)
273
+
274
+ # Add packet details
275
+ results["packets"] = [
276
+ {
277
+ "index": i,
278
+ "timestamp": f"{p.timestamp * 1e3:.3f} ms",
279
+ "data": p.data.hex() if p.data else "",
280
+ "errors": p.errors,
281
+ **{k: v for k, v in (p.annotations or {}).items() if k != "data_bits"},
282
+ }
283
+ for i, p in enumerate(packets[:100]) # Limit to first 100 packets
284
+ ]
285
+
286
+ if len(packets) > 100:
287
+ results["note"] = f"Showing first 100 of {len(packets)} packets"
288
+
289
+ # Add error details if any
290
+ if errors:
291
+ results["error_details"] = errors[:20] # Limit to first 20 errors
292
+
293
+ return results
294
+
295
+
296
+ def _decode_uart(
297
+ trace: DigitalTrace,
298
+ baud_rate: int | None,
299
+ parity: str,
300
+ stop_bits: int,
301
+ show_errors: bool,
302
+ ) -> tuple[list[ProtocolPacket], list[dict[str, Any]], dict[str, Any]]:
303
+ """Decode UART protocol.
304
+
305
+ Args:
306
+ trace: Digital trace to decode.
307
+ baud_rate: Baud rate (None for auto-detect).
308
+ parity: Parity mode.
309
+ stop_bits: Number of stop bits.
310
+ show_errors: Whether to filter to errors only.
311
+
312
+ Returns:
313
+ Tuple of (packets, errors, protocol_info).
314
+ """
315
+ from oscura.analyzers.protocols.uart import UARTDecoder
316
+
317
+ # Create decoder with parameters
318
+ decoder = UARTDecoder(
319
+ baudrate=baud_rate or 0, # 0 triggers auto-detection
320
+ data_bits=8,
321
+ parity=parity, # type: ignore[arg-type]
322
+ stop_bits=stop_bits,
323
+ )
324
+
325
+ packets = list(decoder.decode(trace))
326
+ errors = []
327
+
328
+ # Extract errors from packets
329
+ for i, pkt in enumerate(packets):
330
+ if pkt.errors:
331
+ for err in pkt.errors:
332
+ errors.append(
333
+ {
334
+ "packet_index": i,
335
+ "timestamp": f"{pkt.timestamp * 1e3:.3f} ms",
336
+ "type": err,
337
+ "data": pkt.data.hex() if pkt.data else "",
338
+ }
339
+ )
340
+
341
+ # Determine actual baud rate used
342
+ actual_baud = decoder._baudrate if hasattr(decoder, "_baudrate") else baud_rate
343
+
344
+ protocol_info = {
345
+ "baud_rate": actual_baud,
346
+ "parity": parity,
347
+ "stop_bits": stop_bits,
348
+ "data_bits": 8,
349
+ }
350
+
351
+ return packets, errors, protocol_info
352
+
353
+
354
+ def _decode_spi(
355
+ trace: DigitalTrace,
356
+ show_errors: bool,
357
+ ) -> tuple[list[ProtocolPacket], list[dict[str, Any]], dict[str, Any]]:
358
+ """Decode SPI protocol.
359
+
360
+ Note: SPI requires multiple signals (CLK, MOSI, optionally MISO, CS).
361
+ This function assumes the trace contains clock data and will attempt
362
+ to decode what's available.
363
+
364
+ Args:
365
+ trace: Digital trace to decode (assumed to be CLK or combined).
366
+ show_errors: Whether to filter to errors only.
367
+
368
+ Returns:
369
+ Tuple of (packets, errors, protocol_info).
370
+ """
371
+ from oscura.analyzers.protocols.spi import SPIDecoder
372
+
373
+ # For single-channel decode, we can only analyze timing
374
+ # Full SPI decode requires separate CLK, MOSI, MISO channels
375
+ decoder = SPIDecoder(cpol=0, cpha=0, word_size=8)
376
+
377
+ # Create MOSI from the trace data (treating as data line)
378
+ clk = trace.data
379
+ mosi = trace.data # Same data for single-channel analysis
380
+
381
+ packets = list(decoder.decode(clk=clk, mosi=mosi, sample_rate=trace.metadata.sample_rate))
382
+ errors = []
383
+
384
+ for i, pkt in enumerate(packets):
385
+ if pkt.errors:
386
+ for err in pkt.errors:
387
+ errors.append(
388
+ {
389
+ "packet_index": i,
390
+ "timestamp": f"{pkt.timestamp * 1e3:.3f} ms",
391
+ "type": err,
392
+ }
393
+ )
394
+
395
+ # Estimate clock frequency from edge timing
396
+ edges = np.where(np.diff(clk.astype(int)) != 0)[0]
397
+ if len(edges) > 1:
398
+ avg_period = np.mean(np.diff(edges)) / trace.metadata.sample_rate
399
+ clock_freq = 1 / (2 * avg_period) if avg_period > 0 else 0
400
+ else:
401
+ clock_freq = 0
402
+
403
+ protocol_info = {
404
+ "clock_frequency": f"{clock_freq / 1e6:.2f} MHz" if clock_freq > 0 else "Unknown",
405
+ "mode": "0 (CPOL=0, CPHA=0)",
406
+ "word_size": 8,
407
+ "note": "Single-channel decode. For full SPI decode, provide separate CLK/MOSI/MISO signals.",
408
+ }
409
+
410
+ return packets, errors, protocol_info
411
+
412
+
413
+ def _decode_i2c(
414
+ trace: DigitalTrace,
415
+ show_errors: bool,
416
+ ) -> tuple[list[ProtocolPacket], list[dict[str, Any]], dict[str, Any]]:
417
+ """Decode I2C protocol.
418
+
419
+ Note: I2C requires two signals (SCL, SDA). This function assumes
420
+ the trace is SDA and attempts to detect SCL from timing patterns.
421
+
422
+ Args:
423
+ trace: Digital trace to decode.
424
+ show_errors: Whether to filter to errors only.
425
+
426
+ Returns:
427
+ Tuple of (packets, errors, protocol_info).
428
+ """
429
+ from oscura.analyzers.protocols.i2c import I2CDecoder
430
+
431
+ # For single-channel, assume it's SDA and create synthetic SCL from edges
432
+ sda = trace.data
433
+ sample_rate = trace.metadata.sample_rate
434
+
435
+ # Try to find clock pattern from edge timing
436
+ edges = np.where(np.diff(sda.astype(int)) != 0)[0]
437
+ if len(edges) < 20:
438
+ # Not enough edges for I2C
439
+ return [], [], {"error": "Insufficient edges for I2C decode"}
440
+
441
+ # Create synthetic SCL (toggle at each edge for analysis)
442
+ scl = np.ones_like(sda, dtype=bool)
443
+ for i, edge in enumerate(edges):
444
+ if i % 2 == 0 and edge + 1 < len(scl):
445
+ scl[edge : edge + 10] = False # Create clock pulses
446
+
447
+ decoder = I2CDecoder()
448
+ packets = list(decoder.decode(scl=scl, sda=sda, sample_rate=sample_rate))
449
+ errors = []
450
+
451
+ addresses_seen: set[int] = set()
452
+ for i, pkt in enumerate(packets):
453
+ addr = pkt.annotations.get("address", 0) if pkt.annotations else 0
454
+ addresses_seen.add(addr)
455
+
456
+ if pkt.errors:
457
+ for err in pkt.errors:
458
+ errors.append(
459
+ {
460
+ "packet_index": i,
461
+ "timestamp": f"{pkt.timestamp * 1e3:.3f} ms",
462
+ "type": err,
463
+ "address": f"0x{addr:02X}",
464
+ }
465
+ )
466
+
467
+ # Estimate clock rate from edge intervals
468
+ if len(edges) > 1:
469
+ avg_interval = np.mean(np.diff(edges)) / sample_rate
470
+ clock_rate = 1 / (2 * avg_interval) if avg_interval > 0 else 0
471
+ else:
472
+ clock_rate = 0
473
+
474
+ protocol_info = {
475
+ "clock_frequency": f"{clock_rate / 1e3:.1f} kHz" if clock_rate > 0 else "Unknown",
476
+ "addresses_seen": [f"0x{a:02X}" for a in sorted(addresses_seen)],
477
+ "transactions": len(packets),
478
+ "note": "Single-channel decode. For accurate I2C decode, provide separate SCL/SDA signals.",
479
+ }
480
+
481
+ return packets, errors, protocol_info
482
+
483
+
484
+ def _decode_can(
485
+ trace: DigitalTrace,
486
+ baud_rate: int | None,
487
+ show_errors: bool,
488
+ ) -> tuple[list[ProtocolPacket], list[dict[str, Any]], dict[str, Any]]:
489
+ """Decode CAN protocol.
490
+
491
+ Args:
492
+ trace: Digital trace to decode.
493
+ baud_rate: CAN bit rate (None for common rate detection).
494
+ show_errors: Whether to filter to errors only.
495
+
496
+ Returns:
497
+ Tuple of (packets, errors, protocol_info).
498
+ """
499
+ from oscura.analyzers.protocols.can import CANDecoder
500
+
501
+ # Try common CAN baud rates if not specified
502
+ if baud_rate is None:
503
+ common_rates = [500000, 250000, 125000, 1000000]
504
+ best_rate = 500000
505
+ max_packets = 0
506
+
507
+ for rate in common_rates:
508
+ try:
509
+ decoder = CANDecoder(bitrate=rate)
510
+ test_packets = list(decoder.decode(trace))
511
+ if len(test_packets) > max_packets:
512
+ max_packets = len(test_packets)
513
+ best_rate = rate
514
+ except Exception:
515
+ continue
516
+
517
+ baud_rate = best_rate
518
+
519
+ decoder = CANDecoder(bitrate=baud_rate)
520
+ packets = list(decoder.decode(trace))
521
+ errors = []
522
+
523
+ arbitration_ids: set[int] = set()
524
+ for i, pkt in enumerate(packets):
525
+ arb_id = pkt.annotations.get("arbitration_id", 0) if pkt.annotations else 0
526
+ arbitration_ids.add(arb_id)
527
+
528
+ if pkt.errors:
529
+ for err in pkt.errors:
530
+ errors.append(
531
+ {
532
+ "packet_index": i,
533
+ "timestamp": f"{pkt.timestamp * 1e3:.3f} ms",
534
+ "type": err,
535
+ "arbitration_id": f"0x{arb_id:03X}",
536
+ }
537
+ )
538
+
539
+ protocol_info = {
540
+ "bit_rate": f"{baud_rate / 1000:.0f} kbps",
541
+ "messages": len(packets),
542
+ "arbitration_ids": [f"0x{a:03X}" for a in sorted(arbitration_ids)[:10]],
543
+ "extended_frames": sum(
544
+ 1 for p in packets if p.annotations and p.annotations.get("is_extended")
545
+ ),
546
+ }
547
+
548
+ if len(arbitration_ids) > 10:
549
+ protocol_info["note"] = f"Showing first 10 of {len(arbitration_ids)} arbitration IDs"
550
+
551
+ return packets, errors, protocol_info