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,497 @@
1
+ """Intelligent trace comparison for auto-discovery.
2
+
3
+ This module provides automatic trace comparison with alignment, difference
4
+ detection, and plain-language explanations.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.discovery import compare_traces
9
+ >>> diff = compare_traces(trace1, trace2)
10
+ >>> for d in diff.differences:
11
+ ... print(f"{d.category}: {d.description}")
12
+
13
+ References:
14
+ Oscura Auto-Discovery Specification
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass, field
20
+ from typing import TYPE_CHECKING, Literal
21
+
22
+ import numpy as np
23
+ from scipy import signal as sp_signal
24
+
25
+ if TYPE_CHECKING:
26
+ from numpy.typing import NDArray
27
+
28
+ from oscura.core.types import WaveformTrace
29
+
30
+
31
+ @dataclass
32
+ class Difference:
33
+ """Individual difference between traces.
34
+
35
+ Attributes:
36
+ category: Difference category (timing, amplitude, pattern, transitions).
37
+ timestamp_us: Timestamp in microseconds.
38
+ description: Plain language explanation.
39
+ severity: Severity level (CRITICAL, WARNING, INFO).
40
+ impact_score: Impact score (0.0-1.0, higher = more severe).
41
+ expected_value: Expected value from reference.
42
+ actual_value: Actual value from measured trace.
43
+ delta_value: Absolute difference.
44
+ delta_percent: Percentage difference.
45
+ confidence: Confidence in this difference detection.
46
+ """
47
+
48
+ category: str
49
+ timestamp_us: float
50
+ description: str
51
+ severity: str
52
+ impact_score: float
53
+ expected_value: float | None = None
54
+ actual_value: float | None = None
55
+ delta_value: float | None = None
56
+ delta_percent: float | None = None
57
+ confidence: float = 1.0
58
+
59
+
60
+ @dataclass
61
+ class TraceDiff:
62
+ """Result of intelligent trace comparison.
63
+
64
+ Attributes:
65
+ summary: High-level summary of comparison.
66
+ alignment_method: Method used to align traces.
67
+ similarity_score: Overall similarity (0.0-1.0).
68
+ differences: List of detected differences, sorted by impact.
69
+ visual_path: Path to generated visual comparison (if created).
70
+ stats: Statistical comparison metrics.
71
+ """
72
+
73
+ summary: str
74
+ alignment_method: str
75
+ similarity_score: float
76
+ differences: list[Difference] = field(default_factory=list)
77
+ visual_path: str | None = None
78
+ stats: dict | None = None # type: ignore[type-arg]
79
+
80
+
81
+ def _align_time_based(
82
+ trace1: WaveformTrace,
83
+ trace2: WaveformTrace,
84
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64], int]:
85
+ """Align traces based on time (sync to t=0).
86
+
87
+ Args:
88
+ trace1: First trace.
89
+ trace2: Second trace.
90
+
91
+ Returns:
92
+ Tuple of (data1, data2, offset_samples).
93
+ """
94
+ # Simply align to start (t=0)
95
+ min_len = min(len(trace1.data), len(trace2.data))
96
+ data1 = trace1.data[:min_len].astype(np.float64)
97
+ data2 = trace2.data[:min_len].astype(np.float64)
98
+
99
+ return data1, data2, 0
100
+
101
+
102
+ def _align_trigger_based(
103
+ trace1: WaveformTrace,
104
+ trace2: WaveformTrace,
105
+ threshold_pct: float = 50.0,
106
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64], int]:
107
+ """Align traces based on trigger point (first edge).
108
+
109
+ Args:
110
+ trace1: First trace.
111
+ trace2: Second trace.
112
+ threshold_pct: Threshold percentage for edge detection.
113
+
114
+ Returns:
115
+ Tuple of (data1, data2, offset_samples).
116
+ """
117
+ data1 = trace1.data.astype(np.float64)
118
+ data2 = trace2.data.astype(np.float64)
119
+
120
+ # Find first significant edge in each trace
121
+ range1 = np.ptp(data1)
122
+ range2 = np.ptp(data2)
123
+
124
+ threshold1 = np.min(data1) + range1 * threshold_pct / 100.0
125
+ threshold2 = np.min(data2) + range2 * threshold_pct / 100.0
126
+
127
+ # Find first crossing
128
+ idx1 = np.where(data1 > threshold1)[0]
129
+ idx2 = np.where(data2 > threshold2)[0]
130
+
131
+ offset1 = idx1[0] if len(idx1) > 0 else 0
132
+ offset2 = idx2[0] if len(idx2) > 0 else 0
133
+
134
+ # Align to earliest trigger
135
+ if offset1 <= offset2:
136
+ offset_samples = offset2 - offset1
137
+ data1_aligned = data1[offset1:]
138
+ data2_aligned = data2[offset2:]
139
+ else:
140
+ offset_samples = offset1 - offset2
141
+ data1_aligned = data1[offset1:]
142
+ data2_aligned = data2[offset2:]
143
+
144
+ # Truncate to same length
145
+ min_len = min(len(data1_aligned), len(data2_aligned))
146
+ return data1_aligned[:min_len], data2_aligned[:min_len], offset_samples
147
+
148
+
149
+ def _align_pattern_based(
150
+ trace1: WaveformTrace,
151
+ trace2: WaveformTrace,
152
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64], int]:
153
+ """Align traces using cross-correlation.
154
+
155
+ Args:
156
+ trace1: First trace.
157
+ trace2: Second trace.
158
+
159
+ Returns:
160
+ Tuple of (data1, data2, offset_samples).
161
+ """
162
+ data1 = trace1.data.astype(np.float64)
163
+ data2 = trace2.data.astype(np.float64)
164
+
165
+ # Normalize for correlation
166
+ data1_norm = (data1 - np.mean(data1)) / (np.std(data1) + 1e-10)
167
+ data2_norm = (data2 - np.mean(data2)) / (np.std(data2) + 1e-10)
168
+
169
+ # Cross-correlation
170
+ correlation = sp_signal.correlate(data1_norm, data2_norm, mode="full")
171
+
172
+ # Find peak
173
+ peak_idx = np.argmax(np.abs(correlation))
174
+ offset_samples = peak_idx - (len(data2) - 1)
175
+
176
+ # Align based on offset
177
+ if offset_samples >= 0:
178
+ data1_aligned = data1[offset_samples:]
179
+ data2_aligned = data2
180
+ else:
181
+ data1_aligned = data1
182
+ data2_aligned = data2[-offset_samples:]
183
+
184
+ # Truncate to same length
185
+ min_len = min(len(data1_aligned), len(data2_aligned))
186
+ return data1_aligned[:min_len], data2_aligned[:min_len], int(offset_samples)
187
+
188
+
189
+ def _detect_timing_differences(
190
+ data1: NDArray[np.float64],
191
+ data2: NDArray[np.float64],
192
+ sample_rate: float,
193
+ ) -> list[Difference]:
194
+ """Detect timing differences between aligned traces.
195
+
196
+ Args:
197
+ data1: First trace data.
198
+ data2: Second trace data.
199
+ sample_rate: Sample rate in Hz.
200
+
201
+ Returns:
202
+ List of timing differences.
203
+ """
204
+ differences = []
205
+
206
+ # Look for timing shifts in edges
207
+ # Compute derivatives to find edges
208
+ diff1 = np.diff(data1)
209
+ diff2 = np.diff(data2)
210
+
211
+ # Find significant edges (> 10% of range per sample)
212
+ range1 = np.ptp(data1)
213
+ range2 = np.ptp(data2)
214
+
215
+ edge_threshold1 = range1 * 0.1
216
+ edge_threshold2 = range2 * 0.1
217
+
218
+ edges1 = np.where(np.abs(diff1) > edge_threshold1)[0]
219
+ edges2 = np.where(np.abs(diff2) > edge_threshold2)[0]
220
+
221
+ # Compare edge counts
222
+ if abs(len(edges1) - len(edges2)) > 2:
223
+ delta_edges = abs(len(edges1) - len(edges2))
224
+ timestamp_us = 0.0
225
+
226
+ differences.append(
227
+ Difference(
228
+ category="timing",
229
+ timestamp_us=timestamp_us,
230
+ description=f"Trace 1 has {len(edges1)} transitions while Trace 2 has {len(edges2)} transitions (difference: {delta_edges})",
231
+ severity="WARNING" if delta_edges > 5 else "INFO",
232
+ impact_score=min(1.0, delta_edges / 10.0),
233
+ confidence=0.90,
234
+ )
235
+ )
236
+
237
+ return differences
238
+
239
+
240
+ def _detect_amplitude_differences(
241
+ data1: NDArray[np.float64],
242
+ data2: NDArray[np.float64],
243
+ sample_rate: float,
244
+ ) -> list[Difference]:
245
+ """Detect amplitude differences between aligned traces.
246
+
247
+ Args:
248
+ data1: First trace data.
249
+ data2: Second trace data.
250
+ sample_rate: Sample rate in Hz.
251
+
252
+ Returns:
253
+ List of amplitude differences.
254
+ """
255
+ differences = [] # type: ignore[var-annotated]
256
+
257
+ # Compute amplitude difference
258
+ amp_diff = np.abs(data1 - data2)
259
+ ref_range = np.ptp(data2)
260
+
261
+ if ref_range == 0:
262
+ return differences
263
+
264
+ # Find points with significant amplitude difference
265
+ threshold = ref_range * 0.05 # 5% of swing
266
+
267
+ significant_diffs = np.where(amp_diff > threshold)[0]
268
+
269
+ if len(significant_diffs) > len(data1) * 0.1: # More than 10% of samples
270
+ max_diff_idx = np.argmax(amp_diff)
271
+ max_diff = amp_diff[max_diff_idx]
272
+ timestamp_us = max_diff_idx / sample_rate * 1e6
273
+
274
+ delta_percent = (max_diff / ref_range) * 100.0
275
+
276
+ severity = "CRITICAL" if delta_percent > 20 else "WARNING" if delta_percent > 5 else "INFO"
277
+
278
+ differences.append(
279
+ Difference(
280
+ category="amplitude",
281
+ timestamp_us=float(timestamp_us),
282
+ description=f"Voltage differs by {max_diff:.3f}V ({delta_percent:.1f}% of signal swing)",
283
+ severity=severity,
284
+ impact_score=min(1.0, delta_percent / 20.0),
285
+ expected_value=float(data2[max_diff_idx]),
286
+ actual_value=float(data1[max_diff_idx]),
287
+ delta_value=float(max_diff),
288
+ delta_percent=delta_percent,
289
+ confidence=0.95,
290
+ )
291
+ )
292
+
293
+ return differences
294
+
295
+
296
+ def _detect_pattern_differences(
297
+ data1: NDArray[np.float64],
298
+ data2: NDArray[np.float64],
299
+ sample_rate: float,
300
+ ) -> list[Difference]:
301
+ """Detect pattern differences between aligned traces.
302
+
303
+ Args:
304
+ data1: First trace data.
305
+ data2: Second trace data.
306
+ sample_rate: Sample rate in Hz.
307
+
308
+ Returns:
309
+ List of pattern differences.
310
+ """
311
+ differences = [] # type: ignore[var-annotated]
312
+
313
+ # Compute correlation
314
+ if len(data1) < 2:
315
+ return differences
316
+
317
+ data1_norm = (data1 - np.mean(data1)) / (np.std(data1) + 1e-10)
318
+ data2_norm = (data2 - np.mean(data2)) / (np.std(data2) + 1e-10)
319
+
320
+ correlation = np.corrcoef(data1_norm, data2_norm)[0, 1]
321
+
322
+ if correlation < 0.95:
323
+ severity = "CRITICAL" if correlation < 0.8 else "WARNING" if correlation < 0.95 else "INFO"
324
+
325
+ differences.append(
326
+ Difference(
327
+ category="pattern",
328
+ timestamp_us=0.0,
329
+ description=f"Signal patterns differ (correlation: {correlation:.2f}, expected: >0.95)",
330
+ severity=severity,
331
+ impact_score=1.0 - correlation,
332
+ confidence=0.88,
333
+ )
334
+ )
335
+
336
+ return differences
337
+
338
+
339
+ def compare_traces(
340
+ trace1: WaveformTrace,
341
+ trace2: WaveformTrace,
342
+ *,
343
+ alignment: Literal["time", "trigger", "pattern", "auto"] = "auto",
344
+ difference_types: list[str] | None = None,
345
+ severity_threshold: str | None = None,
346
+ ) -> TraceDiff:
347
+ """Compare traces with intelligent alignment and difference detection.
348
+
349
+ Automatically aligns traces and identifies timing, amplitude, pattern,
350
+ and transition differences with plain-language explanations.
351
+
352
+ Args:
353
+ trace1: First trace (typically measured/actual).
354
+ trace2: Second trace (typically reference/expected).
355
+ alignment: Alignment method:
356
+ - "time": Sync to t=0
357
+ - "trigger": Sync to first edge (≥50% swing)
358
+ - "pattern": Cross-correlation alignment
359
+ - "auto": Try all methods, use best
360
+ difference_types: Types to detect (default: all).
361
+ severity_threshold: Only return differences at or above this level.
362
+
363
+ Returns:
364
+ TraceDiff with alignment method, differences, and summary.
365
+
366
+ Example:
367
+ >>> diff = compare_traces(measured, golden)
368
+ >>> for d in diff.differences[:5]:
369
+ ... print(f"{d.severity}: {d.description}")
370
+
371
+ References:
372
+ DISC-004: Intelligent Trace Comparison
373
+ """
374
+ difference_types = difference_types or [
375
+ "timing",
376
+ "amplitude",
377
+ "pattern",
378
+ "transitions",
379
+ ]
380
+
381
+ # Try alignment methods
382
+ if alignment == "auto":
383
+ # Try all methods and pick best correlation
384
+ methods = ["time", "trigger", "pattern"]
385
+ best_corr = -1
386
+ best_method = "time"
387
+ best_aligned = None
388
+
389
+ for method in methods:
390
+ if method == "time":
391
+ d1, d2, offset = _align_time_based(trace1, trace2)
392
+ elif method == "trigger":
393
+ d1, d2, offset = _align_trigger_based(trace1, trace2)
394
+ else: # pattern
395
+ d1, d2, offset = _align_pattern_based(trace1, trace2)
396
+
397
+ # Compute correlation
398
+ if len(d1) > 1:
399
+ d1_norm = (d1 - np.mean(d1)) / (np.std(d1) + 1e-10)
400
+ d2_norm = (d2 - np.mean(d2)) / (np.std(d2) + 1e-10)
401
+ corr = np.corrcoef(d1_norm, d2_norm)[0, 1]
402
+
403
+ if corr > best_corr:
404
+ best_corr = corr
405
+ best_method = method
406
+ best_aligned = (d1, d2, offset)
407
+
408
+ data1, data2, offset = best_aligned # type: ignore[misc]
409
+ alignment_method = f"{best_method}-based"
410
+ else:
411
+ # Use specified method
412
+ if alignment == "time":
413
+ data1, data2, offset = _align_time_based(trace1, trace2)
414
+ elif alignment == "trigger":
415
+ data1, data2, offset = _align_trigger_based(trace1, trace2)
416
+ else: # pattern
417
+ data1, data2, offset = _align_pattern_based(trace1, trace2)
418
+
419
+ alignment_method = f"{alignment}-based"
420
+
421
+ sample_rate = trace1.metadata.sample_rate
422
+
423
+ # Detect differences
424
+ all_differences = []
425
+
426
+ if "timing" in difference_types:
427
+ all_differences.extend(_detect_timing_differences(data1, data2, sample_rate))
428
+
429
+ if "amplitude" in difference_types:
430
+ all_differences.extend(_detect_amplitude_differences(data1, data2, sample_rate))
431
+
432
+ if "pattern" in difference_types:
433
+ all_differences.extend(_detect_pattern_differences(data1, data2, sample_rate))
434
+
435
+ # Sort by impact score (descending)
436
+ all_differences.sort(key=lambda d: d.impact_score, reverse=True)
437
+
438
+ # Filter by severity threshold
439
+ if severity_threshold:
440
+ severity_order = {"INFO": 0, "WARNING": 1, "CRITICAL": 2}
441
+ threshold_level = severity_order.get(severity_threshold, 0)
442
+
443
+ filtered = [
444
+ d for d in all_differences if severity_order.get(d.severity, 0) >= threshold_level
445
+ ]
446
+ all_differences = filtered
447
+
448
+ # Compute similarity score
449
+ if len(data1) > 1:
450
+ data1_norm = (data1 - np.mean(data1)) / (np.std(data1) + 1e-10)
451
+ data2_norm = (data2 - np.mean(data2)) / (np.std(data2) + 1e-10)
452
+ correlation = np.corrcoef(data1_norm, data2_norm)[0, 1]
453
+ similarity_score = float((correlation + 1) / 2) # Map [-1,1] to [0,1]
454
+ else:
455
+ similarity_score = 1.0 if len(data1) == 0 or data1[0] == data2[0] else 0.0
456
+
457
+ # Build summary
458
+ if similarity_score > 0.95:
459
+ summary = "Signals are very similar"
460
+ elif similarity_score > 0.85:
461
+ summary = "Signals are similar with minor differences"
462
+ elif similarity_score > 0.70:
463
+ summary = "Signals show moderate differences"
464
+ else:
465
+ summary = "Signals are significantly different"
466
+
467
+ critical_count = sum(1 for d in all_differences if d.severity == "CRITICAL")
468
+ warning_count = sum(1 for d in all_differences if d.severity == "WARNING")
469
+
470
+ if critical_count > 0:
471
+ summary += f" ({critical_count} critical issue(s))"
472
+ elif warning_count > 0:
473
+ summary += f" ({warning_count} warning(s))"
474
+
475
+ # Statistics
476
+ stats = {
477
+ "correlation": float(correlation) if len(data1) > 1 else 1.0,
478
+ "rms_error": float(np.sqrt(np.mean((data1 - data2) ** 2))),
479
+ "max_deviation": float(np.max(np.abs(data1 - data2))),
480
+ "max_deviation_time": float(np.argmax(np.abs(data1 - data2)) / sample_rate),
481
+ "avg_timing_offset": float(offset / sample_rate * 1e9), # ns
482
+ }
483
+
484
+ return TraceDiff(
485
+ summary=summary,
486
+ alignment_method=alignment_method,
487
+ similarity_score=similarity_score,
488
+ differences=all_differences,
489
+ stats=stats,
490
+ )
491
+
492
+
493
+ __all__ = [
494
+ "Difference",
495
+ "TraceDiff",
496
+ "compare_traces",
497
+ ]