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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,616 @@
1
+ """Analysis quality scoring for TraceKit.
2
+
3
+ This module provides quality scoring and reliability categorization for
4
+ analysis results, enabling users to assess confidence in automated findings.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.quality.scoring import AnalysisQualityScore, ReliabilityCategory
9
+ >>> score = AnalysisQualityScore(
10
+ ... confidence=0.85,
11
+ ... category=ReliabilityCategory.HIGH,
12
+ ... data_quality_factor=0.9,
13
+ ... sample_sufficiency=0.8,
14
+ ... method_reliability=0.85,
15
+ ... )
16
+ >>> print(score.explain())
17
+ >>> recommendations = score.get_recommendations()
18
+
19
+ References:
20
+ - Quality scoring for automated analysis results
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import logging
26
+ from dataclasses import dataclass, field
27
+ from enum import Enum
28
+ from typing import TYPE_CHECKING, Any
29
+
30
+ import numpy as np
31
+
32
+ if TYPE_CHECKING:
33
+ from numpy.typing import NDArray
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ class ReliabilityCategory(Enum):
39
+ """Reliability categories for analysis results.
40
+
41
+ Attributes:
42
+ HIGH: Result is highly reliable (confidence >= 0.8)
43
+ MEDIUM: Result has moderate reliability (0.6 <= confidence < 0.8)
44
+ LOW: Result has low reliability (0.4 <= confidence < 0.6)
45
+ UNRELIABLE: Result is unreliable (confidence < 0.4)
46
+ """
47
+
48
+ HIGH = "high"
49
+ MEDIUM = "medium"
50
+ LOW = "low"
51
+ UNRELIABLE = "unreliable"
52
+
53
+ @classmethod
54
+ def from_confidence(cls, confidence: float) -> ReliabilityCategory:
55
+ """Get category from confidence score.
56
+
57
+ Args:
58
+ confidence: Confidence value in range [0, 1]
59
+
60
+ Returns:
61
+ Appropriate ReliabilityCategory
62
+ """
63
+ if confidence >= 0.8:
64
+ return cls.HIGH
65
+ elif confidence >= 0.6:
66
+ return cls.MEDIUM
67
+ elif confidence >= 0.4:
68
+ return cls.LOW
69
+ else:
70
+ return cls.UNRELIABLE
71
+
72
+
73
+ @dataclass
74
+ class AnalysisQualityScore:
75
+ """Quality score for an analysis result.
76
+
77
+ Attributes:
78
+ confidence: Overall confidence in result (0-1)
79
+ category: Reliability category
80
+ data_quality_factor: Quality of input data (0-1)
81
+ sample_sufficiency: Sufficiency of sample count (0-1)
82
+ method_reliability: Inherent reliability of method (0-1)
83
+ factors: Additional contributing factors
84
+ warnings: Quality warnings
85
+ metadata: Additional metadata
86
+
87
+ Example:
88
+ >>> score = AnalysisQualityScore(
89
+ ... confidence=0.85,
90
+ ... category=ReliabilityCategory.HIGH,
91
+ ... data_quality_factor=0.9,
92
+ ... sample_sufficiency=0.8,
93
+ ... method_reliability=0.85,
94
+ ... )
95
+ >>> if score.is_reliable:
96
+ ... print("Result is reliable")
97
+ """
98
+
99
+ confidence: float
100
+ category: ReliabilityCategory
101
+ data_quality_factor: float
102
+ sample_sufficiency: float
103
+ method_reliability: float
104
+ factors: dict[str, float] = field(default_factory=dict)
105
+ warnings: list[str] = field(default_factory=list)
106
+ metadata: dict[str, Any] = field(default_factory=dict)
107
+
108
+ def __post_init__(self) -> None:
109
+ """Validate score values."""
110
+ # Ensure confidence is in valid range
111
+ if not 0 <= self.confidence <= 1:
112
+ raise ValueError(f"Confidence must be in [0, 1], got {self.confidence}")
113
+
114
+ # Validate factors
115
+ for name, value in [
116
+ ("data_quality_factor", self.data_quality_factor),
117
+ ("sample_sufficiency", self.sample_sufficiency),
118
+ ("method_reliability", self.method_reliability),
119
+ ]:
120
+ if not 0 <= value <= 1:
121
+ raise ValueError(f"{name} must be in [0, 1], got {value}")
122
+
123
+ @property
124
+ def is_reliable(self) -> bool:
125
+ """Check if result is reliable (medium confidence or higher).
126
+
127
+ Returns:
128
+ True if category is HIGH or MEDIUM
129
+ """
130
+ return self.category in (ReliabilityCategory.HIGH, ReliabilityCategory.MEDIUM)
131
+
132
+ def explain(self, include_factors: bool = True) -> str:
133
+ """Generate human-readable explanation of the quality score.
134
+
135
+ Args:
136
+ include_factors: Whether to include factor breakdown
137
+
138
+ Returns:
139
+ Human-readable explanation string
140
+
141
+ Example:
142
+ >>> print(score.explain())
143
+ ✓ High confidence result (85.0%)
144
+
145
+ Contributing factors:
146
+ ✓ Data Quality Factor: 90.0%
147
+ ✓ Sample Sufficiency: 80.0%
148
+ ✓ Method Reliability: 85.0%
149
+ """
150
+ lines = []
151
+
152
+ # Overall assessment
153
+ if self.category == ReliabilityCategory.HIGH:
154
+ lines.append(f"✓ High confidence result ({self.confidence:.1%})")
155
+ elif self.category == ReliabilityCategory.MEDIUM:
156
+ lines.append(f"◐ Medium confidence result ({self.confidence:.1%})")
157
+ elif self.category == ReliabilityCategory.LOW:
158
+ lines.append(f"◯ Low confidence result ({self.confidence:.1%})")
159
+ else:
160
+ lines.append(f"✗ Unreliable result ({self.confidence:.1%})")
161
+
162
+ # Factor breakdown
163
+ if include_factors and self.factors:
164
+ lines.append("\nContributing factors:")
165
+ for factor_name, factor_value in sorted(self.factors.items()):
166
+ status = "✓" if factor_value >= 0.7 else "◐" if factor_value >= 0.4 else "✗"
167
+ lines.append(
168
+ f" {status} {factor_name.replace('_', ' ').title()}: {factor_value:.1%}"
169
+ )
170
+
171
+ # Warnings
172
+ if self.warnings:
173
+ lines.append("\nWarnings:")
174
+ for warning in self.warnings:
175
+ lines.append(f" ⚠ {warning}")
176
+
177
+ return "\n".join(lines)
178
+
179
+ def get_recommendations(self) -> list[str]:
180
+ """Get actionable recommendations to improve result quality.
181
+
182
+ Returns:
183
+ List of recommendation strings
184
+
185
+ Example:
186
+ >>> recommendations = score.get_recommendations()
187
+ >>> for rec in recommendations:
188
+ ... print(rec)
189
+ Consider improving input signal quality (filtering, averaging)
190
+ """
191
+ recommendations = []
192
+
193
+ if self.data_quality_factor < 0.5:
194
+ recommendations.append("Consider improving input signal quality (filtering, averaging)")
195
+
196
+ if self.sample_sufficiency < 0.5:
197
+ recommendations.append("Capture more data points for reliable analysis")
198
+
199
+ if "snr" in str(self.warnings).lower():
200
+ recommendations.append("Use a bandpass filter to improve SNR")
201
+
202
+ if "clipping" in str(self.warnings).lower():
203
+ recommendations.append("Adjust input gain to avoid signal clipping")
204
+
205
+ if not recommendations:
206
+ recommendations.append("Result quality is acceptable")
207
+
208
+ return recommendations
209
+
210
+ def to_dict(self) -> dict[str, Any]:
211
+ """Convert to dictionary for serialization.
212
+
213
+ Returns:
214
+ Dictionary representation of quality score.
215
+ """
216
+ return {
217
+ "confidence": self.confidence,
218
+ "category": self.category.value,
219
+ "is_reliable": self.is_reliable,
220
+ "data_quality_factor": self.data_quality_factor,
221
+ "method_reliability": self.method_reliability,
222
+ "sample_sufficiency": self.sample_sufficiency,
223
+ "factors": self.factors,
224
+ "warnings": self.warnings,
225
+ "metadata": self.metadata,
226
+ }
227
+
228
+
229
+ def calculate_quality_score(
230
+ data_quality_factor: float,
231
+ sample_sufficiency: float,
232
+ method_reliability: float,
233
+ *,
234
+ weights: tuple[float, float, float] | None = None,
235
+ warnings: list[str] | None = None,
236
+ factors: dict[str, float] | None = None,
237
+ metadata: dict[str, Any] | None = None,
238
+ ) -> AnalysisQualityScore:
239
+ """Calculate overall quality score from component factors.
240
+
241
+ Args:
242
+ data_quality_factor: Quality of input data (0-1)
243
+ sample_sufficiency: Sufficiency of sample count (0-1)
244
+ method_reliability: Inherent reliability of method (0-1)
245
+ weights: Optional custom weights (data, sample, method), defaults to (0.4, 0.3, 0.3)
246
+ warnings: Optional quality warnings
247
+ factors: Optional additional factors
248
+ metadata: Optional metadata
249
+
250
+ Returns:
251
+ AnalysisQualityScore with computed confidence
252
+
253
+ Example:
254
+ >>> score = calculate_quality_score(
255
+ ... data_quality_factor=0.9,
256
+ ... sample_sufficiency=0.8,
257
+ ... method_reliability=0.85,
258
+ ... )
259
+ >>> print(f"Confidence: {score.confidence:.1%}")
260
+ """
261
+ if weights is None:
262
+ weights = (0.4, 0.3, 0.3)
263
+
264
+ w_data, w_sample, w_method = weights
265
+
266
+ # Calculate weighted confidence
267
+ confidence = (
268
+ w_data * data_quality_factor + w_sample * sample_sufficiency + w_method * method_reliability
269
+ )
270
+
271
+ # Determine category
272
+ category = ReliabilityCategory.from_confidence(confidence)
273
+
274
+ # Build factors dictionary
275
+ all_factors = {
276
+ "data_quality_factor": data_quality_factor,
277
+ "sample_sufficiency": sample_sufficiency,
278
+ "method_reliability": method_reliability,
279
+ }
280
+ if factors:
281
+ all_factors.update(factors)
282
+
283
+ return AnalysisQualityScore(
284
+ confidence=confidence,
285
+ category=category,
286
+ data_quality_factor=data_quality_factor,
287
+ sample_sufficiency=sample_sufficiency,
288
+ method_reliability=method_reliability,
289
+ factors=all_factors,
290
+ warnings=warnings or [],
291
+ metadata=metadata or {},
292
+ )
293
+
294
+
295
+ @dataclass
296
+ class DataQualityMetrics:
297
+ """Metrics describing input data quality.
298
+
299
+ QUAL-002: Data quality assessment
300
+
301
+ Attributes:
302
+ snr_db: Signal-to-noise ratio in decibels
303
+ sample_count: Number of samples in the data
304
+ has_clipping: Whether the signal shows clipping
305
+ has_saturation: Whether the signal shows saturation
306
+ noise_floor: Estimated noise floor level
307
+ completeness: Fraction of non-NaN values (0-1)
308
+ """
309
+
310
+ snr_db: float | None = None
311
+ sample_count: int = 0
312
+ has_clipping: bool = False
313
+ has_saturation: bool = False
314
+ noise_floor: float | None = None
315
+ completeness: float = 1.0 # Fraction of non-NaN values
316
+
317
+ def to_factor(self) -> float:
318
+ """Convert metrics to single quality factor (0-1).
319
+
320
+ Returns:
321
+ Quality factor between 0 and 1.
322
+ """
323
+ factors = []
324
+
325
+ # SNR contribution
326
+ if self.snr_db is not None:
327
+ snr_factor = min(1.0, max(0.0, self.snr_db / 40.0))
328
+ factors.append(snr_factor)
329
+
330
+ # Sample count contribution (diminishing returns after 1000)
331
+ sample_factor = min(1.0, np.log10(max(1, self.sample_count)) / 4.0)
332
+ factors.append(sample_factor)
333
+
334
+ # Clipping/saturation penalties
335
+ if self.has_clipping:
336
+ factors.append(0.7)
337
+ if self.has_saturation:
338
+ factors.append(0.6)
339
+
340
+ # Completeness
341
+ factors.append(self.completeness)
342
+
343
+ return float(np.mean(factors)) if factors else 0.5
344
+
345
+
346
+ # Method reliability scores (based on algorithm characteristics)
347
+ #: Method reliability tracking
348
+ METHOD_RELIABILITY: dict[str, float] = {
349
+ # High reliability methods
350
+ "fft": 0.95,
351
+ "welch": 0.90,
352
+ "autocorrelation": 0.85,
353
+ "histogram": 0.95,
354
+ "statistics": 0.95,
355
+ # Medium reliability methods
356
+ "edge_detection": 0.80,
357
+ "zero_crossing": 0.75,
358
+ "peak_detection": 0.70,
359
+ "pattern_matching": 0.75,
360
+ # Lower reliability methods (heuristic-based)
361
+ "protocol_inference": 0.60,
362
+ "signal_classification": 0.65,
363
+ "anomaly_detection": 0.60,
364
+ }
365
+
366
+
367
+ def assess_data_quality(
368
+ data: NDArray[np.float64], sample_rate: float | None = None
369
+ ) -> DataQualityMetrics:
370
+ """Assess quality of input data.
371
+
372
+ QUAL-002: Data quality assessment
373
+
374
+ Args:
375
+ data: Input data array
376
+ sample_rate: Sample rate in Hz (optional)
377
+
378
+ Returns:
379
+ DataQualityMetrics with quality assessment
380
+ """
381
+ metrics = DataQualityMetrics()
382
+
383
+ try:
384
+ # Sample count
385
+ metrics.sample_count = len(data)
386
+
387
+ # Check for NaN/Inf
388
+ valid_mask = np.isfinite(data)
389
+ metrics.completeness = float(np.mean(valid_mask))
390
+
391
+ if metrics.completeness < 0.01:
392
+ return metrics
393
+
394
+ valid_data = data[valid_mask]
395
+
396
+ # Check for clipping (values at min/max bounds)
397
+ data_range = np.ptp(valid_data)
398
+ if data_range > 0:
399
+ min_count = np.sum(valid_data == np.min(valid_data))
400
+ max_count = np.sum(valid_data == np.max(valid_data))
401
+ clip_threshold = 0.01 * len(valid_data)
402
+ metrics.has_clipping = min_count > clip_threshold or max_count > clip_threshold
403
+
404
+ # Estimate SNR using signal variance vs noise floor
405
+ # Use median absolute deviation for robust noise estimation
406
+ median = np.median(valid_data)
407
+ mad = np.median(np.abs(valid_data - median)) * 1.4826
408
+ metrics.noise_floor = float(mad)
409
+
410
+ signal_power = float(np.var(valid_data))
411
+ noise_power = mad**2
412
+
413
+ if noise_power > 0:
414
+ snr_linear = signal_power / noise_power
415
+ metrics.snr_db = float(10 * np.log10(max(1e-10, snr_linear)))
416
+
417
+ except Exception as e:
418
+ logger.debug(f"Error assessing data quality: {e}")
419
+
420
+ return metrics
421
+
422
+
423
+ def score_analysis_result(
424
+ result: Any,
425
+ method_name: str,
426
+ data: NDArray[np.float64] | None = None,
427
+ data_quality: DataQualityMetrics | None = None,
428
+ min_samples: int = 10,
429
+ ) -> AnalysisQualityScore:
430
+ """Score the quality of an analysis result.
431
+
432
+ QUAL-001: Quality scoring foundation
433
+
434
+ Args:
435
+ result: The analysis result to score
436
+ method_name: Name of the analysis method
437
+ data: Input data (for quality assessment)
438
+ data_quality: Pre-computed data quality metrics
439
+ min_samples: Minimum samples for reliable result
440
+
441
+ Returns:
442
+ AnalysisQualityScore with confidence and factors
443
+ """
444
+ factors = {}
445
+ warnings = []
446
+
447
+ # Get data quality
448
+ if data_quality is None and data is not None:
449
+ data_quality = assess_data_quality(data)
450
+
451
+ # Data quality factor
452
+ if data_quality is not None:
453
+ data_factor = data_quality.to_factor()
454
+ factors["data_quality"] = data_factor
455
+
456
+ if data_quality.has_clipping:
457
+ warnings.append("Input data shows clipping")
458
+ if data_quality.snr_db is not None and data_quality.snr_db < 20:
459
+ warnings.append(f"Low SNR ({data_quality.snr_db:.1f} dB)")
460
+ else:
461
+ data_factor = 0.5
462
+ factors["data_quality"] = data_factor
463
+
464
+ # Method reliability
465
+ method_key = method_name.lower().split(".")[-1].replace("_", "")
466
+ method_reliability = METHOD_RELIABILITY.get(method_key, 0.7)
467
+
468
+ # Check for partial matches
469
+ for key, reliability in METHOD_RELIABILITY.items():
470
+ if key in method_name.lower():
471
+ method_reliability = reliability
472
+ break
473
+
474
+ factors["method_reliability"] = method_reliability
475
+
476
+ # Sample sufficiency
477
+ if data_quality is not None:
478
+ sample_sufficiency = min(1.0, data_quality.sample_count / (min_samples * 10))
479
+ if data_quality.sample_count < min_samples:
480
+ warnings.append(f"Insufficient samples ({data_quality.sample_count} < {min_samples})")
481
+ else:
482
+ sample_sufficiency = 0.5
483
+ factors["sample_sufficiency"] = sample_sufficiency
484
+
485
+ # Result-specific scoring
486
+ result_factor = _score_result_value(result)
487
+ factors["result_validity"] = result_factor
488
+
489
+ # Combine factors
490
+ confidence = (
491
+ data_factor * 0.3
492
+ + method_reliability * 0.25
493
+ + sample_sufficiency * 0.25
494
+ + result_factor * 0.2
495
+ )
496
+
497
+ # Determine category from confidence
498
+ category = ReliabilityCategory.from_confidence(confidence)
499
+
500
+ return AnalysisQualityScore(
501
+ confidence=confidence,
502
+ category=category,
503
+ data_quality_factor=data_factor,
504
+ method_reliability=method_reliability,
505
+ sample_sufficiency=sample_sufficiency,
506
+ factors=factors,
507
+ warnings=warnings,
508
+ )
509
+
510
+
511
+ def _score_result_value(result: Any) -> float:
512
+ """Score result validity based on value characteristics.
513
+
514
+ Args:
515
+ result: Analysis result to score.
516
+
517
+ Returns:
518
+ Validity score between 0 and 1.
519
+ """
520
+ if result is None:
521
+ return 0.0
522
+
523
+ # Handle numeric results
524
+ if isinstance(result, int | float):
525
+ if np.isnan(result) or np.isinf(result):
526
+ return 0.0
527
+ return 1.0
528
+
529
+ # Handle array results
530
+ if isinstance(result, np.ndarray):
531
+ valid_ratio = np.mean(np.isfinite(result))
532
+ return float(valid_ratio)
533
+
534
+ # Handle dict results
535
+ if isinstance(result, dict):
536
+ if not result:
537
+ return 0.3
538
+ return 1.0
539
+
540
+ # Handle list results
541
+ if isinstance(result, list):
542
+ if not result:
543
+ return 0.3
544
+ return 1.0
545
+
546
+ return 0.7 # Default for other types
547
+
548
+
549
+ def combine_quality_scores(
550
+ scores: list[AnalysisQualityScore],
551
+ weights: list[float] | None = None,
552
+ ) -> AnalysisQualityScore:
553
+ """Combine multiple quality scores into one.
554
+
555
+ Args:
556
+ scores: List of quality scores to combine
557
+ weights: Optional weights for each score
558
+
559
+ Returns:
560
+ Combined quality score
561
+ """
562
+ if not scores:
563
+ return AnalysisQualityScore(
564
+ confidence=0.0,
565
+ category=ReliabilityCategory.UNRELIABLE,
566
+ data_quality_factor=0.0,
567
+ method_reliability=0.0,
568
+ sample_sufficiency=0.0,
569
+ )
570
+
571
+ if weights is None:
572
+ weights = [1.0] * len(scores)
573
+
574
+ total_weight = sum(weights)
575
+
576
+ combined_confidence = (
577
+ sum(s.confidence * w for s, w in zip(scores, weights, strict=True)) / total_weight
578
+ )
579
+ combined_data = (
580
+ sum(s.data_quality_factor * w for s, w in zip(scores, weights, strict=True)) / total_weight
581
+ )
582
+ combined_method = (
583
+ sum(s.method_reliability * w for s, w in zip(scores, weights, strict=True)) / total_weight
584
+ )
585
+ combined_samples = (
586
+ sum(s.sample_sufficiency * w for s, w in zip(scores, weights, strict=True)) / total_weight
587
+ )
588
+
589
+ # Aggregate warnings
590
+ all_warnings = []
591
+ for score in scores:
592
+ all_warnings.extend(score.warnings)
593
+
594
+ # Determine category
595
+ category = ReliabilityCategory.from_confidence(combined_confidence)
596
+
597
+ return AnalysisQualityScore(
598
+ confidence=combined_confidence,
599
+ category=category,
600
+ data_quality_factor=combined_data,
601
+ method_reliability=combined_method,
602
+ sample_sufficiency=combined_samples,
603
+ warnings=list(set(all_warnings)),
604
+ )
605
+
606
+
607
+ __all__ = [
608
+ "METHOD_RELIABILITY",
609
+ "AnalysisQualityScore",
610
+ "DataQualityMetrics",
611
+ "ReliabilityCategory",
612
+ "assess_data_quality",
613
+ "calculate_quality_score",
614
+ "combine_quality_scores",
615
+ "score_analysis_result",
616
+ ]