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,518 @@
1
+ """Interactive analysis wizard for guided workflows.
2
+
3
+ This module provides step-by-step guided workflows that walk non-experts
4
+ through signal analysis, asking simple questions and adapting based on
5
+ responses.
6
+
7
+
8
+ Example:
9
+ >>> from oscura.guidance import AnalysisWizard
10
+ >>> wizard = AnalysisWizard(trace)
11
+ >>> result = wizard.run()
12
+
13
+ References:
14
+ Oscura Auto-Discovery Specification
15
+ Phase 34 Task-247
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass, field
21
+ from datetime import datetime
22
+ from typing import TYPE_CHECKING, Any
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import Callable
26
+
27
+ from oscura.core.types import WaveformTrace
28
+
29
+
30
+ @dataclass
31
+ class WizardStep:
32
+ """Single step in the analysis wizard.
33
+
34
+ Attributes:
35
+ number: Step number (1-based).
36
+ id: Step identifier.
37
+ question: Question text in plain English.
38
+ options: List of answer options.
39
+ default: Default/recommended answer.
40
+ skip_if_confident: Skip if auto-detection confidence >= threshold.
41
+ user_response: User's answer.
42
+ confidence_before: Confidence before step.
43
+ confidence_after: Confidence after step.
44
+ preview: Preview of result.
45
+ """
46
+
47
+ number: int
48
+ id: str
49
+ question: str
50
+ options: list[str] = field(default_factory=list)
51
+ default: str | None = None
52
+ skip_if_confident: bool = False
53
+ user_response: str | None = None
54
+ confidence_before: float = 0.0
55
+ confidence_after: float = 0.0
56
+ preview: Any = None
57
+
58
+
59
+ @dataclass
60
+ class WizardResult:
61
+ """Result from wizard analysis.
62
+
63
+ Attributes:
64
+ summary: Summary of analysis.
65
+ signal_type: Detected signal type.
66
+ parameters: Signal parameters.
67
+ quality: Quality assessment.
68
+ decode: Decoded data (if applicable).
69
+ recommendations: Next step recommendations.
70
+ confidence: Overall confidence.
71
+ """
72
+
73
+ summary: str
74
+ signal_type: str | None = None
75
+ parameters: dict | None = None # type: ignore[type-arg]
76
+ quality: Any = None
77
+ decode: Any = None
78
+ recommendations: list = field(default_factory=list) # type: ignore[type-arg]
79
+ confidence: float = 0.0
80
+
81
+
82
+ class AnalysisWizard:
83
+ """Interactive analysis wizard for guided workflows.
84
+
85
+ Provides step-by-step guided analysis with smart defaults,
86
+ auto-skip based on confidence, progress tracking, and live previews.
87
+
88
+ Attributes:
89
+ trace: Waveform being analyzed.
90
+ max_questions: Maximum questions to ask (default 5).
91
+ auto_detect_threshold: Confidence threshold for auto-skip (default 0.8).
92
+ enable_preview: Enable live result preview.
93
+ allow_backtrack: Allow back/forward navigation.
94
+ interactive: Enable interactive mode.
95
+ step_history: History of completed steps.
96
+ steps_completed: Number of steps completed.
97
+ questions_asked: Number of questions asked.
98
+ questions_skipped: Number of questions skipped.
99
+ session_duration_seconds: Total session duration.
100
+ _start_time: Session start time.
101
+ _current_state: Current analysis state.
102
+ """
103
+
104
+ def __init__(
105
+ self,
106
+ trace: WaveformTrace,
107
+ *,
108
+ max_questions: int = 5,
109
+ auto_detect_threshold: float = 0.8,
110
+ enable_preview: bool = True,
111
+ allow_backtrack: bool = True,
112
+ interactive: bool = True,
113
+ ) -> None:
114
+ """Initialize analysis wizard.
115
+
116
+ Args:
117
+ trace: Waveform to analyze.
118
+ max_questions: Maximum questions (default 5, range 3-7).
119
+ auto_detect_threshold: Skip question if confidence >= this.
120
+ enable_preview: Enable live result preview after each step.
121
+ allow_backtrack: Allow back/forward navigation.
122
+ interactive: Enable interactive mode (vs programmatic).
123
+
124
+ References:
125
+ DISC-006: Interactive Analysis Wizard
126
+ """
127
+ self.trace = trace
128
+ self.max_questions = max(3, min(7, max_questions))
129
+ self.auto_detect_threshold = auto_detect_threshold
130
+ self.enable_preview = enable_preview
131
+ self.allow_backtrack = allow_backtrack
132
+ self.interactive = interactive
133
+
134
+ self.step_history: list[WizardStep] = []
135
+ self.steps_completed = 0
136
+ self.questions_asked = 0
137
+ self.questions_skipped = 0
138
+ self.session_duration_seconds = 0.0
139
+
140
+ self._start_time = datetime.now()
141
+ self._current_state: dict[str, Any] = {}
142
+ self._predefined_answers: dict[str, str] = {}
143
+
144
+ def add_custom_step(
145
+ self,
146
+ step_id: str,
147
+ *,
148
+ question: str,
149
+ options: list[str],
150
+ default: str | None = None,
151
+ skip_if_confident: bool = True,
152
+ ) -> None:
153
+ """Add a custom step to the wizard.
154
+
155
+ Args:
156
+ step_id: Unique step identifier.
157
+ question: Question text in plain English.
158
+ options: List of answer options.
159
+ default: Default/recommended answer.
160
+ skip_if_confident: Skip if auto-detection confident.
161
+ """
162
+ # Store for later use during run()
163
+ if not hasattr(self, "_custom_steps"):
164
+ self._custom_steps = []
165
+
166
+ self._custom_steps.append(
167
+ {
168
+ "id": step_id,
169
+ "question": question,
170
+ "options": options,
171
+ "default": default,
172
+ "skip_if_confident": skip_if_confident,
173
+ }
174
+ )
175
+
176
+ def set_answers(self, answers: dict[str, str]) -> None:
177
+ """Set predefined answers for programmatic mode.
178
+
179
+ Args:
180
+ answers: Dictionary mapping step IDs to answers.
181
+ """
182
+ self._predefined_answers = answers
183
+
184
+ def run(
185
+ self,
186
+ *,
187
+ preview_callback: Callable[[Any], None] | None = None,
188
+ ) -> WizardResult:
189
+ """Run the analysis wizard.
190
+
191
+ Guides user through analysis steps, auto-skipping where confident,
192
+ showing progress, and providing live previews.
193
+
194
+ Args:
195
+ preview_callback: Optional callback for step previews.
196
+
197
+ Returns:
198
+ WizardResult with analysis summary and findings.
199
+
200
+ Example:
201
+ >>> wizard = AnalysisWizard(trace)
202
+ >>> result = wizard.run()
203
+ >>> print(result.summary)
204
+
205
+ References:
206
+ DISC-006: Interactive Analysis Wizard
207
+ """
208
+ from oscura.discovery import (
209
+ assess_data_quality,
210
+ characterize_signal,
211
+ decode_protocol,
212
+ find_anomalies,
213
+ )
214
+ from oscura.guidance import suggest_next_steps
215
+
216
+ # Step 1: Auto-characterization
217
+ step1 = WizardStep(
218
+ number=1,
219
+ id="characterization",
220
+ question="What type of signal are you analyzing?",
221
+ options=[
222
+ "Serial data (UART, SPI, I2C)",
223
+ "PWM / Motor control",
224
+ "Analog sensor output",
225
+ "Not sure - auto-detect",
226
+ ],
227
+ default="Not sure - auto-detect",
228
+ skip_if_confident=False,
229
+ )
230
+
231
+ # Always do auto-characterization first
232
+ char_result = characterize_signal(self.trace)
233
+ step1.confidence_before = 0.0
234
+ step1.confidence_after = char_result.confidence
235
+ self._current_state["characterization"] = char_result
236
+
237
+ if self.interactive and char_result.confidence < self.auto_detect_threshold:
238
+ # Ask user to confirm
239
+ step1.user_response = self._predefined_answers.get("signal_type", step1.default)
240
+ self.questions_asked += 1
241
+ else:
242
+ # Auto-detected with high confidence
243
+ signal_type = getattr(char_result, "signal_type", "Unknown")
244
+ step1.user_response = f"Auto-detected: {signal_type}"
245
+ self.questions_skipped += 1
246
+
247
+ self.step_history.append(step1)
248
+ self.steps_completed += 1
249
+
250
+ # Preview callback
251
+ if preview_callback and self.enable_preview:
252
+ preview_callback(char_result)
253
+
254
+ # Step 2: Quality assessment
255
+ step2 = WizardStep(
256
+ number=2,
257
+ id="quality",
258
+ question="Check data quality?",
259
+ options=["Yes", "No"],
260
+ default="Yes",
261
+ skip_if_confident=True,
262
+ )
263
+
264
+ quality = assess_data_quality(self.trace)
265
+ step2.confidence_before = char_result.confidence
266
+ step2.confidence_after = quality.confidence
267
+ self._current_state["quality"] = quality
268
+
269
+ if self.interactive and self.questions_asked < self.max_questions:
270
+ step2.user_response = self._predefined_answers.get("check_quality", "Yes")
271
+ if step2.user_response == "Yes":
272
+ self.questions_asked += 1
273
+ else:
274
+ step2.user_response = "Skipped (auto-assessed)"
275
+ self.questions_skipped += 1
276
+
277
+ self.step_history.append(step2)
278
+ self.steps_completed += 1
279
+
280
+ if preview_callback and self.enable_preview:
281
+ preview_callback(quality)
282
+
283
+ # Step 3: Protocol decode (if applicable)
284
+ decode_result = None
285
+ if hasattr(char_result, "signal_type") and char_result.confidence >= 0.7:
286
+ signal_type = char_result.signal_type.lower()
287
+
288
+ if any(proto in signal_type for proto in ["uart", "spi", "i2c", "can"]):
289
+ step3 = WizardStep(
290
+ number=3,
291
+ id="decode",
292
+ question=f"Auto-detected {char_result.signal_type}. Decode data?",
293
+ options=["Yes", "No"],
294
+ default="Yes",
295
+ skip_if_confident=True,
296
+ )
297
+
298
+ if self.interactive and self.questions_asked < self.max_questions:
299
+ step3.user_response = self._predefined_answers.get("decode_data", "Yes")
300
+ if step3.user_response == "Yes":
301
+ decode_result = decode_protocol(self.trace)
302
+ self._current_state["decode"] = decode_result
303
+ self.questions_asked += 1
304
+ else:
305
+ # Auto-decode
306
+ decode_result = decode_protocol(self.trace)
307
+ self._current_state["decode"] = decode_result
308
+ step3.user_response = "Auto-decoded"
309
+ self.questions_skipped += 1
310
+
311
+ step3.confidence_before = char_result.confidence
312
+ step3.confidence_after = decode_result.overall_confidence if decode_result else 0.0
313
+
314
+ self.step_history.append(step3)
315
+ self.steps_completed += 1
316
+
317
+ if preview_callback and self.enable_preview and decode_result:
318
+ preview_callback(decode_result)
319
+
320
+ # Step 4: Anomaly detection (if quality issues)
321
+ anomalies = None
322
+ if quality.status in ["WARNING", "FAIL"]:
323
+ step4 = WizardStep(
324
+ number=self.steps_completed + 1,
325
+ id="anomalies",
326
+ question="Quality concerns detected. Check for anomalies?",
327
+ options=["Yes", "No"],
328
+ default="Yes",
329
+ )
330
+
331
+ if self.interactive and self.questions_asked < self.max_questions:
332
+ step4.user_response = self._predefined_answers.get("check_anomalies", "Yes")
333
+ if step4.user_response == "Yes":
334
+ anomalies = find_anomalies(self.trace)
335
+ self._current_state["anomalies"] = anomalies
336
+ self.questions_asked += 1
337
+ else:
338
+ # Auto-check
339
+ anomalies = find_anomalies(self.trace)
340
+ self._current_state["anomalies"] = anomalies
341
+ step4.user_response = "Auto-checked"
342
+ self.questions_skipped += 1
343
+
344
+ self.step_history.append(step4)
345
+ self.steps_completed += 1
346
+
347
+ if preview_callback and self.enable_preview and anomalies:
348
+ preview_callback(anomalies)
349
+
350
+ # Get recommendations for next steps
351
+ recommendations = suggest_next_steps(
352
+ self.trace,
353
+ current_state=self._current_state,
354
+ )
355
+
356
+ # Build summary
357
+ summary_parts = []
358
+
359
+ if hasattr(char_result, "signal_type") and char_result.signal_type:
360
+ summary_parts.append(f"Signal type: {char_result.signal_type}")
361
+ if hasattr(char_result, "parameters"):
362
+ summary_parts.append(f"Parameters: {_format_params(char_result.parameters)}")
363
+
364
+ if quality.status == "PASS":
365
+ summary_parts.append("Quality: Good")
366
+ elif quality.status == "WARNING":
367
+ summary_parts.append("Quality: Fair (some concerns)")
368
+ else:
369
+ summary_parts.append("Quality: Poor (issues detected)")
370
+
371
+ if decode_result:
372
+ byte_count = len(decode_result.data) if hasattr(decode_result, "data") else 0
373
+ summary_parts.append(f"Decoded: {byte_count} bytes")
374
+
375
+ if anomalies and len(anomalies) > 0:
376
+ critical = sum(1 for a in anomalies if a.severity == "CRITICAL")
377
+ if critical > 0:
378
+ summary_parts.append(f"Anomalies: {critical} critical issues")
379
+
380
+ summary = "\n".join(summary_parts)
381
+
382
+ # Session duration
383
+ self.session_duration_seconds = (datetime.now() - self._start_time).total_seconds()
384
+
385
+ return WizardResult(
386
+ summary=summary,
387
+ signal_type=char_result.signal_type if hasattr(char_result, "signal_type") else None,
388
+ parameters=char_result.parameters if hasattr(char_result, "parameters") else None,
389
+ quality=quality,
390
+ decode=decode_result,
391
+ recommendations=recommendations,
392
+ confidence=char_result.confidence,
393
+ )
394
+
395
+ @classmethod
396
+ def from_session(cls, session_file: str) -> AnalysisWizard:
397
+ """Load wizard from saved session file.
398
+
399
+ Args:
400
+ session_file: Path to session JSON file.
401
+
402
+ Returns:
403
+ AnalysisWizard instance configured from session.
404
+
405
+ Raises:
406
+ FileNotFoundError: If session file doesn't exist.
407
+ ValueError: If session file is invalid.
408
+ """
409
+ import json
410
+ from pathlib import Path
411
+
412
+ path = Path(session_file)
413
+ if not path.exists():
414
+ msg = f"Session file not found: {session_file}"
415
+ raise FileNotFoundError(msg)
416
+
417
+ with path.open() as f:
418
+ session_data = json.load(f)
419
+
420
+ # Extract trace path and load
421
+ from oscura import load
422
+
423
+ trace_path = session_data.get("trace_path")
424
+ if not trace_path:
425
+ msg = "Session file missing trace_path"
426
+ raise ValueError(msg)
427
+
428
+ trace = load(trace_path)
429
+
430
+ # Create wizard with saved settings
431
+ wizard = cls(
432
+ trace, # type: ignore[arg-type]
433
+ max_questions=session_data.get("max_questions", 5),
434
+ auto_detect_threshold=session_data.get("auto_detect_threshold", 0.8),
435
+ enable_preview=session_data.get("enable_preview", True),
436
+ allow_backtrack=session_data.get("allow_backtrack", True),
437
+ interactive=session_data.get("interactive", True),
438
+ )
439
+
440
+ # Set predefined answers
441
+ if "answers" in session_data:
442
+ wizard.set_answers(session_data["answers"])
443
+
444
+ return wizard
445
+
446
+ def save_session(self, output_path: str) -> None:
447
+ """Save wizard session to JSON file.
448
+
449
+ Args:
450
+ output_path: Path for output JSON file.
451
+ """
452
+ import json
453
+ from pathlib import Path
454
+
455
+ session_data = {
456
+ "trace_path": str(getattr(self.trace, "path", "")),
457
+ "max_questions": self.max_questions,
458
+ "auto_detect_threshold": self.auto_detect_threshold,
459
+ "enable_preview": self.enable_preview,
460
+ "allow_backtrack": self.allow_backtrack,
461
+ "interactive": self.interactive,
462
+ "steps_completed": self.steps_completed,
463
+ "questions_asked": self.questions_asked,
464
+ "questions_skipped": self.questions_skipped,
465
+ "session_duration_seconds": self.session_duration_seconds,
466
+ "answers": self._predefined_answers,
467
+ "step_history": [
468
+ {
469
+ "number": step.number,
470
+ "id": step.id,
471
+ "question": step.question,
472
+ "user_response": step.user_response,
473
+ "confidence_before": step.confidence_before,
474
+ "confidence_after": step.confidence_after,
475
+ }
476
+ for step in self.step_history
477
+ ],
478
+ }
479
+
480
+ path = Path(output_path)
481
+ path.parent.mkdir(parents=True, exist_ok=True)
482
+
483
+ with path.open("w") as f:
484
+ json.dump(session_data, f, indent=2)
485
+
486
+
487
+ def _format_params(params: dict) -> str: # type: ignore[type-arg]
488
+ """Format parameters dictionary for display.
489
+
490
+ Args:
491
+ params: Parameters dictionary.
492
+
493
+ Returns:
494
+ Formatted string.
495
+ """
496
+ if not params:
497
+ return ""
498
+
499
+ parts = []
500
+ for key, value in params.items():
501
+ if isinstance(value, int | float):
502
+ if key.endswith("_hz") or key.endswith("_freq"):
503
+ parts.append(f"{key}={value / 1e3:.1f}kHz")
504
+ elif key.endswith("_baud") or key.endswith("baud_rate"):
505
+ parts.append(f"{key}={value:.0f}")
506
+ else:
507
+ parts.append(f"{key}={value}")
508
+ else:
509
+ parts.append(f"{key}={value}")
510
+
511
+ return ", ".join(parts[:3]) # Limit to 3 params
512
+
513
+
514
+ __all__ = [
515
+ "AnalysisWizard",
516
+ "WizardResult",
517
+ "WizardStep",
518
+ ]