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,487 @@
1
+ """Golden waveform comparison for TraceKit.
2
+
3
+ This module provides golden reference waveform management and comparison
4
+ functions for pass/fail testing against known-good waveforms.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.comparison import create_golden, compare_to_golden
9
+ >>> golden = create_golden(reference_trace)
10
+ >>> result = compare_to_golden(measured_trace, golden)
11
+
12
+ References:
13
+ IEEE 181-2011: Standard for Transitional Waveform Definitions
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ from dataclasses import dataclass, field
20
+ from datetime import datetime
21
+ from pathlib import Path
22
+ from typing import TYPE_CHECKING, Any, Literal
23
+
24
+ import numpy as np
25
+
26
+ from oscura.core.exceptions import AnalysisError, LoaderError
27
+
28
+ if TYPE_CHECKING:
29
+ from numpy.typing import NDArray
30
+
31
+ from oscura.core.types import WaveformTrace
32
+
33
+
34
+ @dataclass
35
+ class GoldenReference:
36
+ """Golden reference waveform for comparison.
37
+
38
+ Contains a reference waveform with tolerance bounds for pass/fail
39
+ testing of measured waveforms.
40
+
41
+ Attributes:
42
+ data: Reference waveform data.
43
+ sample_rate: Sample rate in Hz.
44
+ upper_bound: Upper tolerance bound.
45
+ lower_bound: Lower tolerance bound.
46
+ tolerance: Tolerance used to create bounds.
47
+ tolerance_type: How tolerance was applied.
48
+ name: Reference name.
49
+ description: Optional description.
50
+ created: Creation timestamp.
51
+ metadata: Additional metadata.
52
+ """
53
+
54
+ data: NDArray[np.float64]
55
+ sample_rate: float
56
+ upper_bound: NDArray[np.float64]
57
+ lower_bound: NDArray[np.float64]
58
+ tolerance: float
59
+ tolerance_type: Literal["absolute", "percentage", "sigma"] = "absolute"
60
+ name: str = "golden"
61
+ description: str = ""
62
+ created: datetime = field(default_factory=datetime.now)
63
+ metadata: dict[str, Any] = field(default_factory=dict)
64
+
65
+ @property
66
+ def num_samples(self) -> int:
67
+ """Number of samples in the reference."""
68
+ return len(self.data)
69
+
70
+ @property
71
+ def duration(self) -> float:
72
+ """Duration in seconds."""
73
+ return self.num_samples / self.sample_rate
74
+
75
+ def to_dict(self) -> dict[str, Any]:
76
+ """Convert to dictionary for serialization."""
77
+ return {
78
+ "data": self.data.tolist(),
79
+ "sample_rate": self.sample_rate,
80
+ "upper_bound": self.upper_bound.tolist(),
81
+ "lower_bound": self.lower_bound.tolist(),
82
+ "tolerance": self.tolerance,
83
+ "tolerance_type": self.tolerance_type,
84
+ "name": self.name,
85
+ "description": self.description,
86
+ "created": self.created.isoformat(),
87
+ "metadata": self.metadata,
88
+ }
89
+
90
+ @classmethod
91
+ def from_dict(cls, data: dict[str, Any]) -> GoldenReference:
92
+ """Create from dictionary."""
93
+ return cls(
94
+ data=np.array(data["data"], dtype=np.float64),
95
+ sample_rate=data["sample_rate"],
96
+ upper_bound=np.array(data["upper_bound"], dtype=np.float64),
97
+ lower_bound=np.array(data["lower_bound"], dtype=np.float64),
98
+ tolerance=data["tolerance"],
99
+ tolerance_type=data.get("tolerance_type", "absolute"),
100
+ name=data.get("name", "golden"),
101
+ description=data.get("description", ""),
102
+ created=datetime.fromisoformat(data["created"])
103
+ if "created" in data
104
+ else datetime.now(),
105
+ metadata=data.get("metadata", {}),
106
+ )
107
+
108
+ def save(self, path: str | Path) -> None:
109
+ """Save golden reference to file.
110
+
111
+ Args:
112
+ path: File path (JSON format).
113
+ """
114
+ path = Path(path)
115
+ with open(path, "w") as f:
116
+ json.dump(self.to_dict(), f, indent=2)
117
+
118
+ @classmethod
119
+ def load(cls, path: str | Path) -> GoldenReference:
120
+ """Load golden reference from file.
121
+
122
+ Args:
123
+ path: File path.
124
+
125
+ Returns:
126
+ GoldenReference instance.
127
+
128
+ Raises:
129
+ LoaderError: If golden reference file not found.
130
+ """
131
+ path = Path(path)
132
+ if not path.exists():
133
+ raise LoaderError(
134
+ f"Golden reference file not found: {path}",
135
+ file_path=str(path),
136
+ )
137
+
138
+ with open(path) as f:
139
+ data = json.load(f)
140
+
141
+ return cls.from_dict(data)
142
+
143
+
144
+ @dataclass
145
+ class GoldenComparisonResult:
146
+ """Result of golden waveform comparison.
147
+
148
+ Attributes:
149
+ passed: True if measured waveform is within tolerance.
150
+ num_violations: Number of samples outside tolerance.
151
+ violation_rate: Fraction of samples outside tolerance.
152
+ max_deviation: Maximum deviation from reference.
153
+ rms_deviation: RMS deviation from reference.
154
+ upper_violations: Indices exceeding upper bound.
155
+ lower_violations: Indices below lower bound.
156
+ margin: Minimum margin to tolerance bound.
157
+ margin_percentage: Margin as percentage of tolerance.
158
+ statistics: Additional comparison statistics.
159
+ """
160
+
161
+ passed: bool
162
+ num_violations: int
163
+ violation_rate: float
164
+ max_deviation: float
165
+ rms_deviation: float
166
+ upper_violations: NDArray[np.int64] | None = None
167
+ lower_violations: NDArray[np.int64] | None = None
168
+ margin: float | None = None
169
+ margin_percentage: float | None = None
170
+ statistics: dict[str, Any] = field(default_factory=dict)
171
+
172
+
173
+ def create_golden(
174
+ trace: WaveformTrace,
175
+ *,
176
+ tolerance: float | None = None,
177
+ tolerance_pct: float | None = None,
178
+ tolerance_sigma: float | None = None,
179
+ name: str = "golden",
180
+ description: str = "",
181
+ ) -> GoldenReference:
182
+ """Create a golden reference from a trace.
183
+
184
+ Creates a golden reference waveform with tolerance bounds for
185
+ subsequent comparison testing.
186
+
187
+ Args:
188
+ trace: Reference waveform trace.
189
+ tolerance: Absolute tolerance value.
190
+ tolerance_pct: Percentage tolerance (0-100).
191
+ tolerance_sigma: Tolerance as multiple of standard deviation.
192
+ name: Name for the reference.
193
+ description: Optional description.
194
+
195
+ Returns:
196
+ GoldenReference for comparison testing.
197
+
198
+ Example:
199
+ >>> golden = create_golden(trace, tolerance_pct=5) # 5% tolerance
200
+ >>> golden = create_golden(trace, tolerance=0.01) # 10mV tolerance
201
+ """
202
+ data = trace.data.astype(np.float64)
203
+
204
+ # Determine tolerance and type
205
+ if tolerance is not None:
206
+ tol = tolerance
207
+ tol_type: Literal["absolute", "percentage", "sigma"] = "absolute"
208
+ elif tolerance_pct is not None:
209
+ # Calculate absolute tolerance from percentage
210
+ data_range = float(np.ptp(data))
211
+ tol = data_range * tolerance_pct / 100.0
212
+ tol_type = "percentage"
213
+ elif tolerance_sigma is not None:
214
+ # Calculate tolerance from standard deviation
215
+ tol = float(np.std(data)) * tolerance_sigma
216
+ tol_type = "sigma"
217
+ else:
218
+ # Default: 1% of range
219
+ data_range = float(np.ptp(data))
220
+ tol = data_range * 0.01
221
+ tol_type = "percentage"
222
+
223
+ # Create bounds
224
+ upper_bound = data + tol
225
+ lower_bound = data - tol
226
+
227
+ return GoldenReference(
228
+ data=data,
229
+ sample_rate=trace.metadata.sample_rate,
230
+ upper_bound=upper_bound,
231
+ lower_bound=lower_bound,
232
+ tolerance=tol,
233
+ tolerance_type=tol_type,
234
+ name=name,
235
+ description=description,
236
+ metadata={
237
+ "source_file": trace.metadata.source_file,
238
+ "channel_name": trace.metadata.channel_name,
239
+ },
240
+ )
241
+
242
+
243
+ def tolerance_envelope(
244
+ trace: WaveformTrace,
245
+ *,
246
+ absolute: float | None = None,
247
+ percentage: float | None = None,
248
+ sigma: float | None = None,
249
+ tolerance: float | None = None, # Alias for absolute
250
+ tolerance_pct: float | None = None, # Alias for percentage
251
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
252
+ """Create tolerance envelope around a trace.
253
+
254
+ Generates upper and lower bounds based on the specified tolerance.
255
+
256
+ Args:
257
+ trace: Reference trace.
258
+ absolute: Absolute tolerance value.
259
+ percentage: Percentage tolerance (0-100).
260
+ sigma: Tolerance as multiple of standard deviation.
261
+ tolerance: Alias for absolute (deprecated, use absolute).
262
+ tolerance_pct: Alias for percentage (deprecated, use percentage).
263
+
264
+ Returns:
265
+ Tuple of (upper_bound, lower_bound) arrays.
266
+
267
+ Raises:
268
+ ValueError: If no tolerance type specified.
269
+
270
+ Example:
271
+ >>> upper, lower = tolerance_envelope(trace, percentage=5)
272
+ >>> upper, lower = tolerance_envelope(trace, tolerance=0.1) # absolute
273
+ """
274
+ data = trace.data.astype(np.float64)
275
+
276
+ # Handle legacy parameter names
277
+ if tolerance is not None:
278
+ absolute = tolerance
279
+ if tolerance_pct is not None:
280
+ percentage = tolerance_pct
281
+
282
+ if absolute is not None:
283
+ tol = absolute
284
+ elif percentage is not None:
285
+ data_range = float(np.ptp(data))
286
+ tol = data_range * percentage / 100.0
287
+ elif sigma is not None:
288
+ tol = float(np.std(data)) * sigma
289
+ else:
290
+ raise ValueError("Must specify absolute, percentage, or sigma tolerance")
291
+
292
+ return data + tol, data - tol
293
+
294
+
295
+ def compare_to_golden(
296
+ trace: WaveformTrace,
297
+ golden: GoldenReference,
298
+ *,
299
+ align: bool = True,
300
+ interpolate: bool = True,
301
+ ) -> GoldenComparisonResult:
302
+ """Compare a trace to a golden reference.
303
+
304
+ Tests if the measured trace falls within the tolerance bounds
305
+ of the golden reference.
306
+
307
+ Args:
308
+ trace: Measured trace to compare.
309
+ golden: Golden reference to compare against.
310
+ align: Attempt to align traces by cross-correlation.
311
+ interpolate: Interpolate if sample counts differ.
312
+
313
+ Returns:
314
+ GoldenComparisonResult with pass/fail status.
315
+
316
+ Example:
317
+ >>> result = compare_to_golden(measured, golden)
318
+ >>> if result.passed:
319
+ ... print("PASS")
320
+ """
321
+ measured = trace.data.astype(np.float64)
322
+ reference = golden.data.copy()
323
+ upper = golden.upper_bound.copy()
324
+ lower = golden.lower_bound.copy()
325
+
326
+ # Handle length mismatch
327
+ if len(measured) != len(reference):
328
+ if interpolate:
329
+ # Interpolate measured to match reference length
330
+ x_measured = np.linspace(0, 1, len(measured))
331
+ x_reference = np.linspace(0, 1, len(reference))
332
+ measured = np.interp(x_reference, x_measured, measured)
333
+ else:
334
+ # Truncate to shorter length
335
+ min_len = min(len(measured), len(reference))
336
+ measured = measured[:min_len]
337
+ reference = reference[:min_len]
338
+ upper = upper[:min_len]
339
+ lower = lower[:min_len]
340
+
341
+ # Optionally align by cross-correlation
342
+ if align and len(measured) > 10:
343
+ from scipy import signal as sp_signal
344
+
345
+ corr = sp_signal.correlate(measured, reference, mode="same")
346
+ shift = len(measured) // 2 - np.argmax(corr)
347
+ if abs(shift) < len(measured) // 4: # Only shift if reasonable
348
+ measured = np.roll(measured, -shift)
349
+
350
+ # Find violations
351
+ upper_viol = np.where(measured > upper)[0]
352
+ lower_viol = np.where(measured < lower)[0]
353
+ all_violations = np.union1d(upper_viol, lower_viol)
354
+
355
+ num_violations = len(all_violations)
356
+ violation_rate = num_violations / len(measured) if len(measured) > 0 else 0.0
357
+
358
+ # Compute deviation statistics
359
+ deviation = measured - reference
360
+ max_deviation = float(np.max(np.abs(deviation)))
361
+ rms_deviation = float(np.sqrt(np.mean(deviation**2)))
362
+
363
+ # Compute margin
364
+ upper_margin = float(np.min(upper - measured))
365
+ lower_margin = float(np.min(measured - lower))
366
+ margin = min(upper_margin, lower_margin)
367
+
368
+ # Margin as percentage of tolerance
369
+ margin_pct = (margin / golden.tolerance * 100) if golden.tolerance > 0 else None
370
+
371
+ # Additional statistics
372
+ # Handle constant data (zero std) for correlation calculation
373
+ measured_std = np.std(measured)
374
+ reference_std = np.std(reference)
375
+ if measured_std == 0 or reference_std == 0:
376
+ # For constant data, correlation is undefined (NaN) or 1.0 if both are equal
377
+ if np.allclose(measured, reference):
378
+ correlation = 1.0
379
+ else:
380
+ correlation = float("nan")
381
+ else:
382
+ correlation = float(np.corrcoef(measured, reference)[0, 1])
383
+
384
+ statistics = {
385
+ "mean_deviation": float(np.mean(deviation)),
386
+ "std_deviation": float(np.std(deviation)),
387
+ "max_positive_deviation": float(np.max(deviation)),
388
+ "max_negative_deviation": float(np.min(deviation)),
389
+ "correlation": correlation,
390
+ }
391
+
392
+ return GoldenComparisonResult(
393
+ passed=num_violations == 0,
394
+ num_violations=num_violations,
395
+ violation_rate=violation_rate,
396
+ max_deviation=max_deviation,
397
+ rms_deviation=rms_deviation,
398
+ upper_violations=upper_viol if len(upper_viol) > 0 else None,
399
+ lower_violations=lower_viol if len(lower_viol) > 0 else None,
400
+ margin=margin,
401
+ margin_percentage=margin_pct,
402
+ statistics=statistics,
403
+ )
404
+
405
+
406
+ def batch_compare_to_golden(
407
+ traces: list[WaveformTrace],
408
+ golden: GoldenReference,
409
+ *,
410
+ align: bool = True,
411
+ ) -> list[GoldenComparisonResult]:
412
+ """Compare multiple traces to a golden reference.
413
+
414
+ Tests a batch of measured traces against the same golden reference.
415
+
416
+ Args:
417
+ traces: List of traces to compare.
418
+ golden: Golden reference.
419
+ align: Attempt to align traces.
420
+
421
+ Returns:
422
+ List of comparison results.
423
+
424
+ Example:
425
+ >>> results = batch_compare_to_golden(traces, golden)
426
+ >>> pass_rate = sum(r.passed for r in results) / len(results)
427
+ """
428
+ return [compare_to_golden(trace, golden, align=align) for trace in traces]
429
+
430
+
431
+ def golden_from_average(
432
+ traces: list[WaveformTrace],
433
+ *,
434
+ tolerance_sigma: float = 3.0,
435
+ name: str = "averaged_golden",
436
+ ) -> GoldenReference:
437
+ """Create golden reference from averaged traces.
438
+
439
+ Creates a golden reference from the average of multiple traces,
440
+ with tolerance based on the standard deviation.
441
+
442
+ Args:
443
+ traces: List of traces to average.
444
+ tolerance_sigma: Number of standard deviations for tolerance.
445
+ name: Name for the reference.
446
+
447
+ Returns:
448
+ GoldenReference based on averaged data.
449
+
450
+ Raises:
451
+ AnalysisError: If no traces provided for averaging.
452
+
453
+ Example:
454
+ >>> golden = golden_from_average(sample_traces, tolerance_sigma=3)
455
+ """
456
+ if not traces:
457
+ raise AnalysisError("No traces provided for averaging")
458
+
459
+ # Get common length
460
+ lengths = [len(t.data) for t in traces]
461
+ min_len = min(lengths)
462
+
463
+ # Stack and average
464
+ stacked = np.array([t.data[:min_len] for t in traces], dtype=np.float64)
465
+ avg_data = np.mean(stacked, axis=0)
466
+ std_data = np.std(stacked, axis=0)
467
+
468
+ # Create tolerance from standard deviation
469
+ tolerance = std_data * tolerance_sigma
470
+
471
+ # Use constant tolerance (max of varying tolerance)
472
+ max_tol = float(np.max(tolerance))
473
+
474
+ return GoldenReference(
475
+ data=avg_data,
476
+ sample_rate=traces[0].metadata.sample_rate,
477
+ upper_bound=avg_data + tolerance,
478
+ lower_bound=avg_data - tolerance,
479
+ tolerance=max_tol,
480
+ tolerance_type="sigma",
481
+ name=name,
482
+ description=f"Averaged from {len(traces)} traces, {tolerance_sigma} sigma tolerance",
483
+ metadata={
484
+ "num_traces_averaged": len(traces),
485
+ "tolerance_sigma": tolerance_sigma,
486
+ },
487
+ )