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,528 @@
1
+ """Data quality assessment for signal analysis.
2
+
3
+ This module assesses whether captured data is sufficient and of adequate
4
+ quality for meaningful analysis.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.discovery import assess_data_quality
9
+ >>> quality = assess_data_quality(trace)
10
+ >>> print(f"Status: {quality.status}")
11
+ >>> for metric in quality.metrics:
12
+ ... print(f"{metric.name}: {metric.status}")
13
+
14
+ References:
15
+ IEEE 1241-2010: ADC Terminology and Test Methods
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass, field
21
+ from typing import TYPE_CHECKING, Any, Literal
22
+
23
+ import numpy as np
24
+
25
+ from oscura.analyzers.statistics.basic import basic_stats
26
+ from oscura.core.types import DigitalTrace, WaveformTrace
27
+
28
+ if TYPE_CHECKING:
29
+ from numpy.typing import NDArray
30
+
31
+ QualityStatus = Literal["PASS", "WARNING", "FAIL"]
32
+ AnalysisScenario = Literal["protocol_decode", "timing_analysis", "fft", "eye_diagram", "general"]
33
+
34
+
35
+ @dataclass
36
+ class QualityMetric:
37
+ """Individual quality metric result.
38
+
39
+ Attributes:
40
+ name: Metric name (e.g., "Sample Rate", "Resolution").
41
+ status: Quality status (PASS, WARNING, FAIL).
42
+ passed: Whether metric passes minimum requirements.
43
+ current_value: Measured value.
44
+ required_value: Required value for this scenario.
45
+ unit: Unit of measurement.
46
+ margin_percent: Margin relative to requirement (positive = good).
47
+ explanation: Plain-language explanation if failed.
48
+ recommendation: Actionable recommendation to fix issue.
49
+
50
+ Example:
51
+ >>> metric = QualityMetric(
52
+ ... name="Sample Rate",
53
+ ... status="WARNING",
54
+ ... passed=False,
55
+ ... current_value=50.0,
56
+ ... required_value=100.0,
57
+ ... unit="MS/s"
58
+ ... )
59
+ """
60
+
61
+ name: str
62
+ status: QualityStatus
63
+ passed: bool
64
+ current_value: float
65
+ required_value: float
66
+ unit: str
67
+ margin_percent: float = 0.0
68
+ explanation: str = ""
69
+ recommendation: str = ""
70
+
71
+
72
+ @dataclass
73
+ class DataQuality:
74
+ """Overall data quality assessment result.
75
+
76
+ Attributes:
77
+ status: Overall quality status (PASS, WARNING, FAIL).
78
+ confidence: Assessment confidence (0.0-1.0).
79
+ metrics: List of individual quality metrics.
80
+ improvement_suggestions: Suggested improvements if quality is poor.
81
+
82
+ Example:
83
+ >>> quality = assess_data_quality(trace)
84
+ >>> if quality.status != "PASS":
85
+ ... print("Quality issues detected:")
86
+ ... for metric in quality.metrics:
87
+ ... if not metric.passed:
88
+ ... print(f" - {metric.name}: {metric.explanation}")
89
+ """
90
+
91
+ status: QualityStatus
92
+ confidence: float
93
+ metrics: list[QualityMetric] = field(default_factory=list)
94
+ improvement_suggestions: list[dict[str, str]] = field(default_factory=list)
95
+
96
+
97
+ def assess_data_quality(
98
+ trace: WaveformTrace | DigitalTrace,
99
+ *,
100
+ scenario: AnalysisScenario = "general",
101
+ protocol_params: dict[str, Any] | None = None,
102
+ strict_mode: bool = False,
103
+ ) -> DataQuality:
104
+ """Assess whether captured data is adequate for analysis.
105
+
106
+ Evaluates sample rate, resolution, duration, and noise level against
107
+ scenario-specific requirements.
108
+
109
+ Args:
110
+ trace: Input waveform or digital trace.
111
+ scenario: Analysis scenario for scenario-specific thresholds.
112
+ protocol_params: Protocol-specific parameters (e.g., clock frequency).
113
+ strict_mode: If True, fail on any warnings.
114
+
115
+ Returns:
116
+ DataQuality assessment with overall status and individual metrics.
117
+
118
+ Raises:
119
+ ValueError: If trace is empty or invalid.
120
+
121
+ Example:
122
+ >>> quality = assess_data_quality(trace, scenario='protocol_decode')
123
+ >>> print(f"Overall: {quality.status} (confidence: {quality.confidence:.2f})")
124
+ >>> for metric in quality.metrics:
125
+ ... if metric.status != 'PASS':
126
+ ... print(f"Issue: {metric.name} - {metric.explanation}")
127
+ ... print(f"Fix: {metric.recommendation}")
128
+
129
+ References:
130
+ DISC-009: Data Quality Assessment
131
+ """
132
+ # Validate input
133
+ if len(trace) == 0:
134
+ raise ValueError("Cannot assess quality of empty trace")
135
+
136
+ # Get signal data
137
+ if isinstance(trace, WaveformTrace):
138
+ data = trace.data
139
+ sample_rate = trace.metadata.sample_rate
140
+ is_analog = True
141
+ else:
142
+ data = trace.data.astype(np.float64)
143
+ sample_rate = trace.metadata.sample_rate
144
+ is_analog = False
145
+
146
+ # Compute basic statistics
147
+ stats = basic_stats(data)
148
+ voltage_swing = stats["max"] - stats["min"]
149
+
150
+ # Protocol parameters
151
+ if protocol_params is None:
152
+ protocol_params = {}
153
+
154
+ # Assess individual metrics
155
+ metrics: list[QualityMetric] = []
156
+
157
+ # 1. Sample Rate Assessment
158
+ sample_rate_metric = _assess_sample_rate(sample_rate, data, stats, scenario, protocol_params)
159
+ metrics.append(sample_rate_metric)
160
+
161
+ # 2. Resolution Assessment
162
+ resolution_metric = _assess_resolution(data, voltage_swing, stats, is_analog, scenario)
163
+ metrics.append(resolution_metric)
164
+
165
+ # 3. Duration Assessment
166
+ duration_metric = _assess_duration(len(data), sample_rate, data, scenario, protocol_params)
167
+ metrics.append(duration_metric)
168
+
169
+ # 4. Noise Level Assessment
170
+ noise_metric = _assess_noise(data, voltage_swing, stats, scenario)
171
+ metrics.append(noise_metric)
172
+
173
+ # Determine overall status
174
+ failed_metrics = [m for m in metrics if m.status == "FAIL"]
175
+ warning_metrics = [m for m in metrics if m.status == "WARNING"]
176
+
177
+ if failed_metrics or (strict_mode and warning_metrics):
178
+ overall_status: QualityStatus = "FAIL"
179
+ elif warning_metrics:
180
+ overall_status = "WARNING"
181
+ else:
182
+ overall_status = "PASS"
183
+
184
+ # Calculate confidence (higher when more metrics pass)
185
+ passed_count = sum(1 for m in metrics if m.passed)
186
+ confidence = round(0.5 + (passed_count / len(metrics)) * 0.5, 2)
187
+
188
+ # Generate improvement suggestions
189
+ suggestions = []
190
+ for metric in metrics:
191
+ if not metric.passed and metric.recommendation:
192
+ suggestions.append(
193
+ {
194
+ "action": metric.recommendation,
195
+ "expected_benefit": f"Improves {metric.name.lower()} to required level",
196
+ "difficulty_level": "Easy"
197
+ if "setting" in metric.recommendation.lower()
198
+ else "Medium",
199
+ }
200
+ )
201
+
202
+ return DataQuality(
203
+ status=overall_status,
204
+ confidence=confidence,
205
+ metrics=metrics,
206
+ improvement_suggestions=suggestions,
207
+ )
208
+
209
+
210
+ def _assess_sample_rate(
211
+ sample_rate: float,
212
+ data: NDArray[np.floating[Any]],
213
+ stats: dict[str, float],
214
+ scenario: AnalysisScenario,
215
+ protocol_params: dict[str, Any],
216
+ ) -> QualityMetric:
217
+ """Assess sample rate adequacy.
218
+
219
+ Args:
220
+ sample_rate: Sample rate in Hz.
221
+ data: Signal data array.
222
+ stats: Basic statistics.
223
+ scenario: Analysis scenario.
224
+ protocol_params: Protocol-specific parameters.
225
+
226
+ Returns:
227
+ QualityMetric for sample rate.
228
+ """
229
+ # Estimate signal frequency
230
+ mean_val = stats["mean"]
231
+ crossings = np.where(np.diff(np.sign(data - mean_val)) != 0)[0]
232
+
233
+ if len(crossings) >= 2:
234
+ avg_half_period = np.mean(np.diff(crossings))
235
+ signal_freq = sample_rate / (avg_half_period * 2) if avg_half_period > 0 else 0
236
+ else:
237
+ signal_freq = 0
238
+
239
+ # Determine required sample rate based on scenario
240
+ if scenario == "protocol_decode":
241
+ # Need 10x the bit rate
242
+ if "clock_freq_mhz" in protocol_params:
243
+ clock_freq = protocol_params["clock_freq_mhz"] * 1e6
244
+ required_rate = clock_freq * 10
245
+ elif signal_freq > 0:
246
+ required_rate = signal_freq * 10
247
+ else:
248
+ required_rate = 10e6 # Default 10 MS/s minimum
249
+ elif scenario == "timing_analysis":
250
+ # Need 100x the edge rate
251
+ required_rate = signal_freq * 100 if signal_freq > 0 else 100e6
252
+ elif scenario == "fft":
253
+ # Nyquist + 20%
254
+ required_rate = signal_freq * 2.4 if signal_freq > 0 else 10e6
255
+ elif scenario == "eye_diagram":
256
+ # Need high oversampling
257
+ required_rate = signal_freq * 50 if signal_freq > 0 else 100e6
258
+ else: # general
259
+ # At least 10x signal frequency
260
+ required_rate = signal_freq * 10 if signal_freq > 0 else 10e6
261
+
262
+ # Calculate margin
263
+ margin_percent = ((sample_rate - required_rate) / required_rate) * 100
264
+
265
+ # Determine status
266
+ if margin_percent >= 0:
267
+ status: QualityStatus = "PASS"
268
+ passed = True
269
+ explanation = ""
270
+ recommendation = ""
271
+ elif margin_percent >= -20:
272
+ status = "WARNING"
273
+ passed = False
274
+ explanation = f"Sample rate is {abs(margin_percent):.0f}% below recommended"
275
+ recommendation = f"Increase sample rate to {required_rate / 1e6:.0f} MS/s (currently {sample_rate / 1e6:.0f} MS/s)"
276
+ else:
277
+ status = "FAIL"
278
+ passed = False
279
+ explanation = f"Sample rate is critically low ({abs(margin_percent):.0f}% below required)"
280
+ recommendation = f"Increase sample rate to at least {required_rate / 1e6:.0f} MS/s"
281
+
282
+ return QualityMetric(
283
+ name="Sample Rate",
284
+ status=status,
285
+ passed=passed,
286
+ current_value=sample_rate / 1e6,
287
+ required_value=required_rate / 1e6,
288
+ unit="MS/s",
289
+ margin_percent=margin_percent,
290
+ explanation=explanation,
291
+ recommendation=recommendation,
292
+ )
293
+
294
+
295
+ def _assess_resolution(
296
+ data: NDArray[np.floating[Any]],
297
+ voltage_swing: float,
298
+ stats: dict[str, float],
299
+ is_analog: bool,
300
+ scenario: AnalysisScenario,
301
+ ) -> QualityMetric:
302
+ """Assess vertical resolution adequacy.
303
+
304
+ Args:
305
+ data: Signal data array.
306
+ voltage_swing: Peak-to-peak voltage.
307
+ stats: Basic statistics.
308
+ is_analog: Whether signal is analog.
309
+ scenario: Analysis scenario.
310
+
311
+ Returns:
312
+ QualityMetric for resolution.
313
+ """
314
+ # Estimate effective number of bits (ENOB)
315
+ if voltage_swing > 0:
316
+ # Approximate ENOB from noise level
317
+ noise_rms = stats["std"]
318
+ snr_linear = (voltage_swing / 2) / (noise_rms + 1e-12)
319
+ snr_db = 20 * np.log10(snr_linear) if snr_linear > 0 else 0
320
+ else:
321
+ # No voltage swing - cannot assess SNR meaningfully
322
+ # Treat as infinite SNR (perfect) since there's no dynamic range
323
+ snr_db = 100.0
324
+
325
+ # Determine required resolution
326
+ if scenario in ("protocol_decode", "timing_analysis"):
327
+ required_snr = 20.0 # dB
328
+ elif scenario in ("fft", "eye_diagram"):
329
+ required_snr = 40.0 # dB
330
+ else:
331
+ required_snr = 20.0 # dB
332
+
333
+ # Use SNR for assessment
334
+ current_snr = snr_db
335
+ margin_percent = ((current_snr - required_snr) / required_snr) * 100
336
+
337
+ # Determine status
338
+ if current_snr >= required_snr:
339
+ status: QualityStatus = "PASS"
340
+ passed = True
341
+ explanation = ""
342
+ recommendation = ""
343
+ elif current_snr >= required_snr * 0.8:
344
+ status = "WARNING"
345
+ passed = False
346
+ explanation = f"SNR is {abs(margin_percent):.0f}% below recommended ({current_snr:.1f} dB)"
347
+ recommendation = "Reduce noise sources or increase signal amplitude"
348
+ else:
349
+ status = "FAIL"
350
+ passed = False
351
+ explanation = f"SNR is critically low ({current_snr:.1f} dB, need {required_snr:.0f} dB)"
352
+ recommendation = "Significantly improve signal quality or use higher resolution capture"
353
+
354
+ return QualityMetric(
355
+ name="Resolution",
356
+ status=status,
357
+ passed=passed,
358
+ current_value=current_snr,
359
+ required_value=required_snr,
360
+ unit="dB SNR",
361
+ margin_percent=margin_percent,
362
+ explanation=explanation,
363
+ recommendation=recommendation,
364
+ )
365
+
366
+
367
+ def _assess_duration(
368
+ n_samples: int,
369
+ sample_rate: float,
370
+ data: NDArray[np.floating[Any]],
371
+ scenario: AnalysisScenario,
372
+ protocol_params: dict[str, Any],
373
+ ) -> QualityMetric:
374
+ """Assess capture duration adequacy.
375
+
376
+ Args:
377
+ n_samples: Number of samples.
378
+ sample_rate: Sample rate in Hz.
379
+ data: Signal data array.
380
+ scenario: Analysis scenario.
381
+ protocol_params: Protocol-specific parameters.
382
+
383
+ Returns:
384
+ QualityMetric for duration.
385
+ """
386
+ duration_sec = n_samples / sample_rate
387
+
388
+ # Estimate signal period
389
+ mean_val = np.mean(data)
390
+ crossings = np.where(np.diff(np.sign(data - mean_val)) != 0)[0]
391
+
392
+ if len(crossings) >= 2:
393
+ avg_half_period = np.mean(np.diff(crossings))
394
+ signal_period = (avg_half_period * 2) / sample_rate
395
+ num_periods = duration_sec / signal_period if signal_period > 0 else 0
396
+ else:
397
+ num_periods = 0
398
+ signal_period = duration_sec / 10 # Assume at least 10 periods
399
+
400
+ # Determine required duration
401
+ if scenario in {"protocol_decode", "timing_analysis"}:
402
+ required_periods = 100
403
+ elif scenario == "fft":
404
+ required_periods = 10 # Need enough for frequency resolution
405
+ elif scenario == "eye_diagram":
406
+ required_periods = 1000 # Need many UIs
407
+ else:
408
+ required_periods = 100
409
+
410
+ required_duration = required_periods * signal_period
411
+ margin_percent = (
412
+ ((duration_sec - required_duration) / required_duration) * 100
413
+ if required_duration > 0
414
+ else 100
415
+ )
416
+
417
+ # Determine status
418
+ if num_periods >= required_periods or margin_percent >= 0:
419
+ status: QualityStatus = "PASS"
420
+ passed = True
421
+ explanation = ""
422
+ recommendation = ""
423
+ elif num_periods >= required_periods * 0.5:
424
+ status = "WARNING"
425
+ passed = False
426
+ explanation = f"Captured only {num_periods:.0f} signal periods, recommended minimum is {required_periods}"
427
+ recommendation = f"Increase capture duration to at least {required_duration * 1e3:.1f} ms (currently {duration_sec * 1e3:.1f} ms)"
428
+ else:
429
+ status = "FAIL"
430
+ passed = False
431
+ explanation = f"Capture duration is critically short ({num_periods:.0f} periods)"
432
+ recommendation = f"Increase capture duration to at least {required_duration * 1e3:.1f} ms"
433
+
434
+ return QualityMetric(
435
+ name="Duration",
436
+ status=status,
437
+ passed=passed,
438
+ current_value=duration_sec * 1e3,
439
+ required_value=required_duration * 1e3,
440
+ unit="ms",
441
+ margin_percent=margin_percent,
442
+ explanation=explanation,
443
+ recommendation=recommendation,
444
+ )
445
+
446
+
447
+ def _assess_noise(
448
+ data: NDArray[np.floating[Any]],
449
+ voltage_swing: float,
450
+ stats: dict[str, float],
451
+ scenario: AnalysisScenario,
452
+ ) -> QualityMetric:
453
+ """Assess noise level.
454
+
455
+ Args:
456
+ data: Signal data array.
457
+ voltage_swing: Peak-to-peak voltage.
458
+ stats: Basic statistics.
459
+ scenario: Analysis scenario.
460
+
461
+ Returns:
462
+ QualityMetric for noise level.
463
+ """
464
+ if voltage_swing == 0:
465
+ # No signal swing, can't assess noise
466
+ return QualityMetric(
467
+ name="Noise Level",
468
+ status="PASS",
469
+ passed=True,
470
+ current_value=0.0,
471
+ required_value=0.0,
472
+ unit="% of swing",
473
+ margin_percent=100.0,
474
+ )
475
+
476
+ # Noise RMS as percentage of swing
477
+ noise_rms = stats["std"]
478
+ noise_percent = (noise_rms / voltage_swing) * 100
479
+
480
+ # Determine acceptable noise level
481
+ if scenario in ("protocol_decode", "timing_analysis"):
482
+ max_noise_percent = 10.0
483
+ elif scenario in ("fft", "eye_diagram"):
484
+ max_noise_percent = 5.0
485
+ else:
486
+ max_noise_percent = 10.0
487
+
488
+ margin_percent = ((max_noise_percent - noise_percent) / max_noise_percent) * 100
489
+
490
+ # Determine status
491
+ if noise_percent <= max_noise_percent:
492
+ status: QualityStatus = "PASS"
493
+ passed = True
494
+ explanation = ""
495
+ recommendation = ""
496
+ elif noise_percent <= max_noise_percent * 1.5:
497
+ status = "WARNING"
498
+ passed = False
499
+ explanation = f"Noise level is {noise_percent:.1f}% of signal swing (max recommended: {max_noise_percent:.0f}%)"
500
+ recommendation = "Reduce noise sources, check grounding, or use averaging"
501
+ else:
502
+ status = "FAIL"
503
+ passed = False
504
+ explanation = f"Noise level is critically high ({noise_percent:.1f}% of swing)"
505
+ recommendation = (
506
+ "Significantly reduce noise through better probing, shielding, or bandwidth limiting"
507
+ )
508
+
509
+ return QualityMetric(
510
+ name="Noise Level",
511
+ status=status,
512
+ passed=passed,
513
+ current_value=noise_percent,
514
+ required_value=max_noise_percent,
515
+ unit="% of swing",
516
+ margin_percent=margin_percent,
517
+ explanation=explanation,
518
+ recommendation=recommendation,
519
+ )
520
+
521
+
522
+ __all__ = [
523
+ "AnalysisScenario",
524
+ "DataQuality",
525
+ "QualityMetric",
526
+ "QualityStatus",
527
+ "assess_data_quality",
528
+ ]