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
@@ -0,0 +1,553 @@
1
+ """Specialized plot types for protocol analysis and state visualization.
2
+
3
+ This module provides specialized visualizations including protocol timing
4
+ diagrams, state machine views, and bus transaction timelines.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.visualization.specialized import plot_protocol_timing
9
+ >>> fig = plot_protocol_timing(decoded_packets, sample_rate=1e6)
10
+
11
+ References:
12
+ - Wavedrom-style digital waveform rendering
13
+ - State machine diagram standards
14
+ - Bus protocol visualization best practices
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass
20
+ from typing import TYPE_CHECKING, Literal
21
+
22
+ import numpy as np
23
+
24
+ if TYPE_CHECKING:
25
+ from matplotlib.axes import Axes
26
+ from matplotlib.figure import Figure
27
+ from numpy.typing import NDArray
28
+
29
+ try:
30
+ import matplotlib.pyplot as plt
31
+ from matplotlib import patches
32
+
33
+ HAS_MATPLOTLIB = True
34
+ except ImportError:
35
+ HAS_MATPLOTLIB = False
36
+
37
+
38
+ @dataclass
39
+ class ProtocolSignal:
40
+ """Protocol signal for timing diagram.
41
+
42
+ Attributes:
43
+ name: Signal name
44
+ data: Signal data (0/1 for digital, values for analog)
45
+ type: Signal type ("digital", "clock", "bus", "analog")
46
+ transitions: List of transition times
47
+ annotations: Dict of time -> annotation text
48
+ """
49
+
50
+ name: str
51
+ data: NDArray[np.float64]
52
+ type: Literal["digital", "clock", "bus", "analog"] = "digital"
53
+ transitions: list[float] | None = None
54
+ annotations: dict[float, str] | None = None
55
+
56
+
57
+ @dataclass
58
+ class StateTransition:
59
+ """State machine transition.
60
+
61
+ Attributes:
62
+ from_state: Source state name
63
+ to_state: Target state name
64
+ condition: Transition condition/label
65
+ style: Line style ("solid", "dashed", "dotted")
66
+ """
67
+
68
+ from_state: str
69
+ to_state: str
70
+ condition: str = ""
71
+ style: Literal["solid", "dashed", "dotted"] = "solid"
72
+
73
+
74
+ def plot_protocol_timing(
75
+ signals: list[ProtocolSignal],
76
+ sample_rate: float,
77
+ *,
78
+ time_range: tuple[float, float] | None = None,
79
+ time_unit: str = "auto",
80
+ style: Literal["wavedrom", "classic"] = "wavedrom",
81
+ figsize: tuple[float, float] | None = None,
82
+ title: str | None = None,
83
+ ) -> Figure:
84
+ """Plot protocol timing diagram in wavedrom style.
85
+
86
+ Creates a timing diagram showing digital signals, clock edges, and
87
+ bus transactions with annotations for protocol events.
88
+
89
+ Args:
90
+ signals: List of ProtocolSignal objects to plot.
91
+ sample_rate: Sample rate in Hz.
92
+ time_range: Time range to plot (t_min, t_max) in seconds. None = full range.
93
+ time_unit: Time unit for x-axis ("s", "ms", "us", "ns", "auto").
94
+ style: Diagram style ("wavedrom" = clean digital, "classic" = traditional).
95
+ figsize: Figure size (width, height). Auto-calculated if None.
96
+ title: Plot title.
97
+
98
+ Returns:
99
+ Matplotlib Figure object.
100
+
101
+ Raises:
102
+ ImportError: If matplotlib is not available.
103
+ ValueError: If signals list is empty.
104
+
105
+ Example:
106
+ >>> sda = ProtocolSignal("SDA", sda_data, type="digital")
107
+ >>> scl = ProtocolSignal("SCL", scl_data, type="clock")
108
+ >>> fig = plot_protocol_timing(
109
+ ... [scl, sda],
110
+ ... sample_rate=1e6,
111
+ ... style="wavedrom",
112
+ ... title="I2C Transaction"
113
+ ... )
114
+
115
+ References:
116
+ VIS-021: Specialized - Protocol Timing Diagram
117
+ Wavedrom digital waveform rendering
118
+ """
119
+ if not HAS_MATPLOTLIB:
120
+ raise ImportError("matplotlib is required for visualization")
121
+
122
+ if len(signals) == 0:
123
+ raise ValueError("signals list cannot be empty")
124
+
125
+ n_signals = len(signals)
126
+
127
+ # Auto-calculate figure size
128
+ if figsize is None:
129
+ width = 12
130
+ height = max(4, n_signals * 0.8 + 1)
131
+ figsize = (width, height)
132
+
133
+ fig, axes = plt.subplots(
134
+ n_signals,
135
+ 1,
136
+ figsize=figsize,
137
+ sharex=True,
138
+ gridspec_kw={"hspace": 0.1},
139
+ )
140
+
141
+ if n_signals == 1:
142
+ axes = [axes]
143
+
144
+ # Determine time range
145
+ if time_range is None:
146
+ max_len = max(len(sig.data) for sig in signals)
147
+ t_min = 0.0
148
+ t_max = max_len / sample_rate
149
+ else:
150
+ t_min, t_max = time_range
151
+
152
+ # Select time unit
153
+ if time_unit == "auto":
154
+ time_range_val = t_max - t_min
155
+ if time_range_val < 1e-6:
156
+ time_unit = "ns"
157
+ time_mult = 1e9
158
+ elif time_range_val < 1e-3:
159
+ time_unit = "us"
160
+ time_mult = 1e6
161
+ elif time_range_val < 1:
162
+ time_unit = "ms"
163
+ time_mult = 1e3
164
+ else:
165
+ time_unit = "s"
166
+ time_mult = 1.0
167
+ else:
168
+ time_mult = {"s": 1.0, "ms": 1e3, "us": 1e6, "ns": 1e9}.get(time_unit, 1.0)
169
+
170
+ # Plot each signal
171
+ for _idx, (signal, ax) in enumerate(zip(signals, axes, strict=False)):
172
+ # Create time vector
173
+ time = np.arange(len(signal.data)) / sample_rate * time_mult
174
+
175
+ # Filter to time range
176
+ mask = (time >= t_min * time_mult) & (time <= t_max * time_mult)
177
+ time = time[mask]
178
+ data = signal.data[mask]
179
+
180
+ if style == "wavedrom":
181
+ _plot_wavedrom_signal(ax, time, data, signal)
182
+ else:
183
+ _plot_classic_signal(ax, time, data, signal)
184
+
185
+ # Add signal name label
186
+ ax.set_ylabel(signal.name, rotation=0, ha="right", va="center", fontsize=10)
187
+ ax.set_ylim(-0.2, 1.3)
188
+
189
+ # Remove y-axis ticks
190
+ ax.set_yticks([])
191
+
192
+ # Grid for timing
193
+ ax.grid(True, axis="x", alpha=0.3, linestyle=":")
194
+
195
+ # Add annotations
196
+ if signal.annotations:
197
+ for t, text in signal.annotations.items():
198
+ if t_min <= t <= t_max:
199
+ ax.annotate(
200
+ text,
201
+ xy=(t * time_mult, 1.2),
202
+ fontsize=8,
203
+ ha="center",
204
+ bbox={"boxstyle": "round,pad=0.3", "facecolor": "yellow", "alpha": 0.7},
205
+ )
206
+
207
+ # X-axis label only on bottom plot
208
+ axes[-1].set_xlabel(f"Time ({time_unit})", fontsize=11)
209
+
210
+ if title:
211
+ fig.suptitle(title, fontsize=14, y=0.98)
212
+
213
+ fig.tight_layout()
214
+ return fig
215
+
216
+
217
+ def _plot_wavedrom_signal(
218
+ ax: Axes,
219
+ time: NDArray[np.float64],
220
+ data: NDArray[np.float64],
221
+ signal: ProtocolSignal,
222
+ ) -> None:
223
+ """Plot signal in wavedrom style (clean digital waveform)."""
224
+ if signal.type == "clock":
225
+ # Clock signal: square wave
226
+ for i in range(len(time) - 1):
227
+ level = 1 if data[i] > 0.5 else 0
228
+ ax.plot(
229
+ [time[i], time[i + 1]],
230
+ [level, level],
231
+ "b-",
232
+ linewidth=1.5,
233
+ )
234
+ # Vertical transition
235
+ if i < len(time) - 1:
236
+ next_level = 1 if data[i + 1] > 0.5 else 0
237
+ if level != next_level:
238
+ ax.plot(
239
+ [time[i + 1], time[i + 1]],
240
+ [level, next_level],
241
+ "b-",
242
+ linewidth=1.5,
243
+ )
244
+
245
+ elif signal.type == "digital":
246
+ # Digital signal: step function with transitions
247
+ for i in range(len(time) - 1):
248
+ level = 1 if data[i] > 0.5 else 0
249
+ ax.plot(
250
+ [time[i], time[i + 1]],
251
+ [level, level],
252
+ "k-",
253
+ linewidth=1.5,
254
+ )
255
+ # Vertical transition with slight slant for visual clarity
256
+ if i < len(time) - 1:
257
+ next_level = 1 if data[i + 1] > 0.5 else 0
258
+ if level != next_level:
259
+ transition_width = (time[i + 1] - time[i]) * 0.1
260
+ ax.plot(
261
+ [time[i + 1] - transition_width, time[i + 1]],
262
+ [level, next_level],
263
+ "k-",
264
+ linewidth=1.5,
265
+ )
266
+
267
+ elif signal.type == "bus":
268
+ # Bus signal: show as high-impedance or data values
269
+ ax.fill_between(time, 0.3, 0.7, alpha=0.3, color="gray")
270
+ ax.plot(time, np.full_like(time, 0.5), "k-", linewidth=0.5)
271
+
272
+ else:
273
+ # Analog signal
274
+ ax.plot(time, data, "r-", linewidth=1.2)
275
+
276
+
277
+ def _plot_classic_signal(
278
+ ax: Axes,
279
+ time: NDArray[np.float64],
280
+ data: NDArray[np.float64],
281
+ signal: ProtocolSignal,
282
+ ) -> None:
283
+ """Plot signal in classic style (traditional oscilloscope-like)."""
284
+ ax.plot(time, data, "b-", linewidth=1.2)
285
+ ax.axhline(0.5, color="gray", linestyle="--", linewidth=0.5, alpha=0.5)
286
+
287
+
288
+ def plot_state_machine(
289
+ states: list[str],
290
+ transitions: list[StateTransition],
291
+ *,
292
+ initial_state: str | None = None,
293
+ final_states: list[str] | None = None,
294
+ layout: Literal["circular", "hierarchical", "force"] = "circular",
295
+ figsize: tuple[float, float] = (10, 8),
296
+ title: str | None = None,
297
+ ) -> Figure:
298
+ """Plot state machine diagram.
299
+
300
+ Creates a state diagram showing states as nodes and transitions as
301
+ directed edges with condition labels.
302
+
303
+ Args:
304
+ states: List of state names.
305
+ transitions: List of StateTransition objects.
306
+ initial_state: Initial state (marked with double circle).
307
+ final_states: List of final states (marked with double circle).
308
+ layout: Layout algorithm for state positioning.
309
+ figsize: Figure size (width, height).
310
+ title: Plot title.
311
+
312
+ Returns:
313
+ Matplotlib Figure object.
314
+
315
+ Raises:
316
+ ImportError: If matplotlib is not available.
317
+
318
+ Example:
319
+ >>> states = ["IDLE", "ACTIVE", "WAIT", "DONE"]
320
+ >>> transitions = [
321
+ ... StateTransition("IDLE", "ACTIVE", "START"),
322
+ ... StateTransition("ACTIVE", "WAIT", "BUSY"),
323
+ ... StateTransition("WAIT", "ACTIVE", "RETRY"),
324
+ ... StateTransition("ACTIVE", "DONE", "COMPLETE"),
325
+ ... ]
326
+ >>> fig = plot_state_machine(
327
+ ... states, transitions, initial_state="IDLE", final_states=["DONE"]
328
+ ... )
329
+
330
+ References:
331
+ VIS-022: Specialized - State Machine View
332
+ """
333
+ if not HAS_MATPLOTLIB:
334
+ raise ImportError("matplotlib is required for visualization")
335
+
336
+ fig, ax = plt.subplots(figsize=figsize)
337
+
338
+ # Calculate state positions using selected layout
339
+ positions = _calculate_state_positions(states, layout)
340
+
341
+ # Draw states as circles
342
+ state_radius = 0.15
343
+
344
+ for state, (x, y) in positions.items():
345
+ # Draw state circle
346
+ circle = patches.Circle(
347
+ (x, y),
348
+ state_radius,
349
+ fill=True,
350
+ facecolor="lightblue",
351
+ edgecolor="black",
352
+ linewidth=2.0,
353
+ )
354
+ ax.add_patch(circle)
355
+
356
+ # Mark initial state with double circle
357
+ if state == initial_state:
358
+ outer_circle = patches.Circle(
359
+ (x, y),
360
+ state_radius * 1.2,
361
+ fill=False,
362
+ edgecolor="black",
363
+ linewidth=2.0,
364
+ )
365
+ ax.add_patch(outer_circle)
366
+
367
+ # Mark final states with double circle
368
+ if final_states and state in final_states:
369
+ inner_circle = patches.Circle(
370
+ (x, y),
371
+ state_radius * 0.8,
372
+ fill=False,
373
+ edgecolor="black",
374
+ linewidth=2.0,
375
+ )
376
+ ax.add_patch(inner_circle)
377
+
378
+ # Add state label
379
+ ax.text(
380
+ x,
381
+ y,
382
+ state,
383
+ ha="center",
384
+ va="center",
385
+ fontsize=10,
386
+ fontweight="bold",
387
+ )
388
+
389
+ # Draw transitions as arrows
390
+ for trans in transitions:
391
+ if trans.from_state not in positions or trans.to_state not in positions:
392
+ continue
393
+
394
+ x1, y1 = positions[trans.from_state]
395
+ x2, y2 = positions[trans.to_state]
396
+
397
+ # Calculate arrow start/end on circle perimeter
398
+ dx = x2 - x1
399
+ dy = y2 - y1
400
+ dist = np.sqrt(dx**2 + dy**2)
401
+
402
+ if dist < 1e-6:
403
+ # Self-loop
404
+ _draw_self_loop(ax, x1, y1, state_radius, trans.condition)
405
+ continue
406
+
407
+ # Normalize
408
+ dx_norm = dx / dist
409
+ dy_norm = dy / dist
410
+
411
+ # Arrow start/end on circle edges
412
+ arrow_start_x = x1 + dx_norm * state_radius
413
+ arrow_start_y = y1 + dy_norm * state_radius
414
+ arrow_end_x = x2 - dx_norm * state_radius
415
+ arrow_end_y = y2 - dy_norm * state_radius
416
+
417
+ # Line style
418
+ linestyle = {
419
+ "solid": "-",
420
+ "dashed": "--",
421
+ "dotted": ":",
422
+ }.get(trans.style, "-")
423
+
424
+ # Draw arrow
425
+ ax.annotate(
426
+ "",
427
+ xy=(arrow_end_x, arrow_end_y),
428
+ xytext=(arrow_start_x, arrow_start_y),
429
+ arrowprops={
430
+ "arrowstyle": "->",
431
+ "lw": 1.5,
432
+ "linestyle": linestyle,
433
+ "color": "black",
434
+ },
435
+ )
436
+
437
+ # Add transition label
438
+ if trans.condition:
439
+ mid_x = (x1 + x2) / 2
440
+ mid_y = (y1 + y2) / 2
441
+ ax.text(
442
+ mid_x,
443
+ mid_y,
444
+ trans.condition,
445
+ fontsize=8,
446
+ ha="center",
447
+ bbox={
448
+ "boxstyle": "round,pad=0.3",
449
+ "facecolor": "white",
450
+ "edgecolor": "gray",
451
+ "alpha": 0.9,
452
+ },
453
+ )
454
+
455
+ # Set axis properties
456
+ ax.set_aspect("equal")
457
+ ax.axis("off")
458
+ ax.set_xlim(-0.2, 1.2)
459
+ ax.set_ylim(-0.2, 1.2)
460
+
461
+ if title:
462
+ ax.set_title(title, fontsize=14, pad=20)
463
+
464
+ fig.tight_layout()
465
+ return fig
466
+
467
+
468
+ def _calculate_state_positions(
469
+ states: list[str],
470
+ layout: str,
471
+ ) -> dict[str, tuple[float, float]]:
472
+ """Calculate state positions using layout algorithm."""
473
+ n_states = len(states)
474
+ positions = {}
475
+
476
+ if layout == "circular":
477
+ # Arrange states in a circle
478
+ angle_step = 2 * np.pi / n_states
479
+ for i, state in enumerate(states):
480
+ angle = i * angle_step
481
+ x = 0.5 + 0.4 * np.cos(angle)
482
+ y = 0.5 + 0.4 * np.sin(angle)
483
+ positions[state] = (x, y)
484
+
485
+ elif layout == "hierarchical":
486
+ # Arrange in rows (simplified hierarchical)
487
+ states_per_row = int(np.ceil(np.sqrt(n_states)))
488
+ for i, state in enumerate(states):
489
+ row = i // states_per_row
490
+ col = i % states_per_row
491
+ x = (col + 0.5) / states_per_row
492
+ y = 1.0 - (row + 0.5) / np.ceil(n_states / states_per_row)
493
+ positions[state] = (x, y)
494
+
495
+ else: # force-directed (simplified)
496
+ # Use random positions as a placeholder for true force-directed layout
497
+ np.random.seed(42)
498
+ for i, state in enumerate(states): # noqa: B007
499
+ x = 0.2 + 0.6 * np.random.rand()
500
+ y = 0.2 + 0.6 * np.random.rand()
501
+ positions[state] = (x, y)
502
+
503
+ return positions
504
+
505
+
506
+ def _draw_self_loop(
507
+ ax: Axes,
508
+ x: float,
509
+ y: float,
510
+ radius: float,
511
+ label: str,
512
+ ) -> None:
513
+ """Draw self-loop transition on state."""
514
+ # Draw arc above state
515
+ arc = patches.Arc(
516
+ (x, y + radius),
517
+ width=radius * 1.5,
518
+ height=radius * 1.5,
519
+ angle=0,
520
+ theta1=0,
521
+ theta2=180,
522
+ linewidth=1.5,
523
+ edgecolor="black",
524
+ fill=False,
525
+ )
526
+ ax.add_patch(arc)
527
+
528
+ # Add arrow head
529
+ ax.annotate(
530
+ "",
531
+ xy=(x - radius * 0.7, y + radius * 0.3),
532
+ xytext=(x - radius * 0.5, y + radius * 0.5),
533
+ arrowprops={"arrowstyle": "->", "lw": 1.5, "color": "black"},
534
+ )
535
+
536
+ # Add label
537
+ if label:
538
+ ax.text(
539
+ x,
540
+ y + radius * 2.2,
541
+ label,
542
+ fontsize=8,
543
+ ha="center",
544
+ bbox={"boxstyle": "round,pad=0.2", "facecolor": "white", "alpha": 0.9},
545
+ )
546
+
547
+
548
+ __all__ = [
549
+ "ProtocolSignal",
550
+ "StateTransition",
551
+ "plot_protocol_timing",
552
+ "plot_state_machine",
553
+ ]