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,429 @@
1
+ """Recommendation engine for guided analysis workflow.
2
+
3
+ This module provides contextual "What should I look at next?" recommendations
4
+ based on current analysis state.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.guidance import suggest_next_steps
9
+ >>> recommendations = suggest_next_steps(trace, current_state)
10
+ >>> for rec in recommendations:
11
+ ... print(f"{rec.title}: {rec.explanation}")
12
+
13
+ References:
14
+ Oscura Auto-Discovery Specification
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass, field
20
+ from datetime import datetime
21
+ from typing import TYPE_CHECKING, Any
22
+
23
+ if TYPE_CHECKING:
24
+ from collections.abc import Callable
25
+
26
+ from oscura.core.types import WaveformTrace
27
+
28
+
29
+ @dataclass
30
+ class Recommendation:
31
+ """Analysis step recommendation.
32
+
33
+ Attributes:
34
+ id: Unique recommendation ID.
35
+ title: Short title (≤50 chars).
36
+ explanation: Why this step is relevant (≤50 words).
37
+ rationale: Detailed reasoning.
38
+ priority: Priority score (0.0-1.0).
39
+ urgency: Urgency score (0.0-1.0).
40
+ ease: Ease of execution (0.0-1.0, higher = easier).
41
+ impact: Expected impact (0.0-1.0, higher = more valuable).
42
+ result_key: Key for storing result in state.
43
+ execute: Optional callable to execute this step.
44
+ impact_description: Description of expected impact.
45
+ """
46
+
47
+ id: str
48
+ title: str
49
+ explanation: str
50
+ priority: float
51
+ urgency: float = 0.5
52
+ ease: float = 0.5
53
+ impact: float = 0.5
54
+ rationale: str = ""
55
+ result_key: str = ""
56
+ execute: Callable | None = None # type: ignore[type-arg]
57
+ impact_description: str = ""
58
+
59
+
60
+ @dataclass
61
+ class AnalysisHistory:
62
+ """Track completed analysis steps.
63
+
64
+ Attributes:
65
+ steps_completed: List of completed step IDs.
66
+ step_timestamps: Timestamps for each step.
67
+ results: Stored results from each step.
68
+ """
69
+
70
+ steps_completed: list[str] = field(default_factory=list)
71
+ step_timestamps: dict[str, datetime] = field(default_factory=dict)
72
+ results: dict[str, Any] = field(default_factory=dict)
73
+
74
+ def add_step(self, step_id: str, result: Any = None) -> None:
75
+ """Record a completed step.
76
+
77
+ Args:
78
+ step_id: Step identifier.
79
+ result: Optional result from step.
80
+ """
81
+ if step_id not in self.steps_completed:
82
+ self.steps_completed.append(step_id)
83
+
84
+ self.step_timestamps[step_id] = datetime.now()
85
+
86
+ if result is not None:
87
+ self.results[step_id] = result
88
+
89
+ def was_recent(self, step_id: str, seconds: float = 60.0) -> bool:
90
+ """Check if step was completed recently.
91
+
92
+ Args:
93
+ step_id: Step identifier.
94
+ seconds: Time window in seconds.
95
+
96
+ Returns:
97
+ True if step was done within time window.
98
+ """
99
+ if step_id not in self.step_timestamps:
100
+ return False
101
+
102
+ elapsed = datetime.now() - self.step_timestamps[step_id]
103
+ return elapsed.total_seconds() < seconds
104
+
105
+
106
+ def _calculate_priority(
107
+ urgency: float,
108
+ ease: float,
109
+ impact: float,
110
+ ) -> float:
111
+ """Calculate recommendation priority.
112
+
113
+ Uses weighted scoring: urgency (40%), ease (30%), impact (30%).
114
+
115
+ Args:
116
+ urgency: Urgency score (0.0-1.0).
117
+ ease: Ease score (0.0-1.0).
118
+ impact: Impact score (0.0-1.0).
119
+
120
+ Returns:
121
+ Priority score (0.0-1.0).
122
+ """
123
+ priority = 0.4 * urgency + 0.3 * ease + 0.3 * impact
124
+ return round(priority, 2)
125
+
126
+
127
+ def _recommend_characterization(
128
+ trace: WaveformTrace,
129
+ state: dict, # type: ignore[type-arg]
130
+ history: AnalysisHistory,
131
+ ) -> Recommendation | None:
132
+ """Recommend signal characterization if not done.
133
+
134
+ Args:
135
+ trace: Waveform being analyzed.
136
+ state: Current analysis state.
137
+ history: Analysis history.
138
+
139
+ Returns:
140
+ Recommendation or None if already done.
141
+ """
142
+ if "characterization" in state or history.was_recent("characterization"):
143
+ return None
144
+
145
+ return Recommendation(
146
+ id="characterization",
147
+ title="Characterize signal",
148
+ explanation="Identify what type of signal this is (UART, SPI, analog, etc.) to guide further analysis.",
149
+ urgency=0.95,
150
+ ease=0.90,
151
+ impact=0.95,
152
+ priority=_calculate_priority(0.95, 0.90, 0.95),
153
+ rationale="Signal characterization is the foundation for all other analysis",
154
+ result_key="characterization",
155
+ impact_description="Enables protocol-specific analysis and targeted measurements",
156
+ )
157
+
158
+
159
+ def _recommend_anomaly_check(
160
+ trace: WaveformTrace,
161
+ state: dict, # type: ignore[type-arg]
162
+ history: AnalysisHistory,
163
+ ) -> Recommendation | None:
164
+ """Recommend anomaly detection.
165
+
166
+ Args:
167
+ trace: Waveform being analyzed.
168
+ state: Current analysis state.
169
+ history: Analysis history.
170
+
171
+ Returns:
172
+ Recommendation or None if not applicable.
173
+ """
174
+ if "anomalies" in state or history.was_recent("anomalies"):
175
+ return None
176
+
177
+ # Higher priority if characterization shows issues
178
+ urgency = 0.70
179
+
180
+ if "characterization" in state:
181
+ char = state["characterization"]
182
+ if hasattr(char, "confidence") and char.confidence < 0.8:
183
+ urgency = 0.85
184
+
185
+ if "quality" in state:
186
+ quality = state["quality"]
187
+ if hasattr(quality, "status") and quality.status in ["WARNING", "FAIL"]:
188
+ urgency = 0.90
189
+
190
+ return Recommendation(
191
+ id="anomaly_detection",
192
+ title="Check for anomalies",
193
+ explanation="Scan the signal for glitches, dropouts, noise spikes, and other problems that could affect data integrity.",
194
+ urgency=urgency,
195
+ ease=0.85,
196
+ impact=0.80,
197
+ priority=_calculate_priority(urgency, 0.85, 0.80),
198
+ rationale="Quality concerns detected - anomaly scan recommended",
199
+ result_key="anomalies",
200
+ impact_description="Identifies specific problem areas and their severity",
201
+ )
202
+
203
+
204
+ def _recommend_quality_assessment(
205
+ trace: WaveformTrace,
206
+ state: dict, # type: ignore[type-arg]
207
+ history: AnalysisHistory,
208
+ ) -> Recommendation | None:
209
+ """Recommend data quality assessment.
210
+
211
+ Args:
212
+ trace: Waveform being analyzed.
213
+ state: Current analysis state.
214
+ history: Analysis history.
215
+
216
+ Returns:
217
+ Recommendation or None if not applicable.
218
+ """
219
+ if "quality" in state or history.was_recent("quality"):
220
+ return None
221
+
222
+ # Higher priority early in analysis
223
+ urgency = 0.75 if len(state) < 2 else 0.65
224
+
225
+ return Recommendation(
226
+ id="quality_assessment",
227
+ title="Assess data quality",
228
+ explanation="Verify that sample rate and resolution are adequate for reliable analysis of this signal.",
229
+ urgency=urgency,
230
+ ease=0.90,
231
+ impact=0.75,
232
+ priority=_calculate_priority(urgency, 0.90, 0.75),
233
+ rationale="Ensures captured data is suitable for intended analysis",
234
+ result_key="quality",
235
+ impact_description="Confirms data is good enough or identifies capture improvements needed",
236
+ )
237
+
238
+
239
+ def _recommend_protocol_decode(
240
+ trace: WaveformTrace,
241
+ state: dict, # type: ignore[type-arg]
242
+ history: AnalysisHistory,
243
+ ) -> Recommendation | None:
244
+ """Recommend protocol decoding.
245
+
246
+ Args:
247
+ trace: Waveform being analyzed.
248
+ state: Current analysis state.
249
+ history: Analysis history.
250
+
251
+ Returns:
252
+ Recommendation or None if not applicable.
253
+ """
254
+ if "decode" in state or history.was_recent("decode"):
255
+ return None
256
+
257
+ # Only recommend if signal type is identified
258
+ if "characterization" not in state:
259
+ return None
260
+
261
+ char = state["characterization"]
262
+
263
+ # Check if it's a protocol signal
264
+ if hasattr(char, "signal_type"):
265
+ signal_type = char.signal_type.lower()
266
+
267
+ if any(proto in signal_type for proto in ["uart", "spi", "i2c", "can"]):
268
+ confidence = getattr(char, "confidence", 0.0)
269
+
270
+ if confidence >= 0.7:
271
+ urgency = 0.85
272
+ explanation = f"Signal identified as {char.signal_type} with high confidence. Decode to extract transmitted data."
273
+ else:
274
+ urgency = 0.60
275
+ explanation = f"Signal possibly {char.signal_type} but confidence is low. Decode may help verify."
276
+
277
+ return Recommendation(
278
+ id="protocol_decode",
279
+ title="Decode protocol data",
280
+ explanation=explanation,
281
+ urgency=urgency,
282
+ ease=0.80,
283
+ impact=0.90,
284
+ priority=_calculate_priority(urgency, 0.80, 0.90),
285
+ rationale=f"{char.signal_type} protocol detected",
286
+ result_key="decode",
287
+ impact_description="Extracts meaningful data from signal",
288
+ )
289
+
290
+ return None
291
+
292
+
293
+ def _recommend_spectral_analysis(
294
+ trace: WaveformTrace,
295
+ state: dict, # type: ignore[type-arg]
296
+ history: AnalysisHistory,
297
+ ) -> Recommendation | None:
298
+ """Recommend spectral analysis.
299
+
300
+ Args:
301
+ trace: Waveform being analyzed.
302
+ state: Current analysis state.
303
+ history: Analysis history.
304
+
305
+ Returns:
306
+ Recommendation or None if not applicable.
307
+ """
308
+ if "spectral" in state or history.was_recent("spectral"):
309
+ return None
310
+
311
+ # Recommend for analog/periodic signals
312
+ if "characterization" in state:
313
+ char = state["characterization"]
314
+
315
+ if hasattr(char, "signal_type"):
316
+ signal_type = char.signal_type.lower()
317
+
318
+ if "analog" in signal_type or "periodic" in signal_type:
319
+ return Recommendation(
320
+ id="spectral_analysis",
321
+ title="Perform spectral analysis",
322
+ explanation="Analyze frequency content to identify dominant frequencies and harmonics in this analog signal.",
323
+ urgency=0.65,
324
+ ease=0.75,
325
+ impact=0.80,
326
+ priority=_calculate_priority(0.65, 0.75, 0.80),
327
+ rationale="Periodic/analog signal detected",
328
+ result_key="spectral",
329
+ impact_description="Reveals frequency components and signal purity",
330
+ )
331
+
332
+ return None
333
+
334
+
335
+ def suggest_next_steps(
336
+ trace: WaveformTrace,
337
+ *,
338
+ current_state: dict[str, Any] | None = None,
339
+ max_suggestions: int = 3,
340
+ include_rationale: bool = False,
341
+ ) -> list[Recommendation]:
342
+ """Suggest next analysis steps based on current state.
343
+
344
+ Provides contextual recommendations guiding users through the investigation
345
+ process without requiring expertise.
346
+
347
+ Args:
348
+ trace: Waveform being analyzed.
349
+ current_state: Current analysis state with completed steps and results.
350
+ max_suggestions: Maximum number of suggestions (default 3, range 2-5).
351
+ include_rationale: Include detailed rationale in recommendations.
352
+
353
+ Returns:
354
+ List of 2-5 recommended next steps, ranked by priority.
355
+
356
+ Example:
357
+ >>> trace = load("capture.wfm")
358
+ >>> state = {"characterization": char_result}
359
+ >>> recommendations = suggest_next_steps(trace, current_state=state)
360
+ >>> for rec in recommendations:
361
+ ... print(f"{rec.priority:.2f}: {rec.title}")
362
+
363
+ References:
364
+ DISC-008: Recommendation Engine
365
+ """
366
+ current_state = current_state or {}
367
+
368
+ # Extract or create analysis history
369
+ if "steps_completed" in current_state:
370
+ history = AnalysisHistory(
371
+ steps_completed=current_state["steps_completed"],
372
+ )
373
+ else:
374
+ history = AnalysisHistory()
375
+
376
+ # Generate candidate recommendations
377
+ candidates = []
378
+
379
+ # Try each recommendation generator
380
+ generators = [
381
+ _recommend_characterization,
382
+ _recommend_quality_assessment,
383
+ _recommend_anomaly_check,
384
+ _recommend_protocol_decode,
385
+ _recommend_spectral_analysis,
386
+ ]
387
+
388
+ for generator in generators:
389
+ rec = generator(trace, current_state, history)
390
+ if rec is not None:
391
+ candidates.append(rec)
392
+
393
+ # If no specific recommendations, provide escape hatch
394
+ if not candidates:
395
+ candidates.append(
396
+ Recommendation(
397
+ id="basic_characterization",
398
+ title="Start with basic signal characterization",
399
+ explanation="Not sure where to start? Begin with automatic signal characterization to identify the signal type.",
400
+ urgency=0.50,
401
+ ease=0.95,
402
+ impact=0.85,
403
+ priority=_calculate_priority(0.50, 0.95, 0.85),
404
+ rationale="Default starting point when no analysis has been done",
405
+ result_key="characterization",
406
+ impact_description="Provides foundation for further analysis",
407
+ )
408
+ )
409
+
410
+ # Sort by priority (descending)
411
+ candidates.sort(key=lambda r: r.priority, reverse=True)
412
+
413
+ # Limit to max_suggestions
414
+ max_suggestions = max(2, min(5, max_suggestions))
415
+ recommendations = candidates[:max_suggestions]
416
+
417
+ # Remove rationale if not requested
418
+ if not include_rationale:
419
+ for rec in recommendations:
420
+ rec.rationale = ""
421
+
422
+ return recommendations
423
+
424
+
425
+ __all__ = [
426
+ "AnalysisHistory",
427
+ "Recommendation",
428
+ "suggest_next_steps",
429
+ ]