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,46 @@
1
+ """Eye diagram analysis module.
2
+
3
+ This module provides eye diagram generation and metrics for serial
4
+ data signal quality analysis.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.eye import generate_eye, eye_height, eye_width
9
+ >>> eye = generate_eye(trace, unit_interval=1e-9)
10
+ >>> height = eye_height(eye)
11
+ >>> width = eye_width(eye)
12
+
13
+ References:
14
+ IEEE 802.3: Ethernet Physical Layer Specifications
15
+ OIF CEI: Common Electrical I/O
16
+ """
17
+
18
+ from oscura.analyzers.eye.diagram import (
19
+ EyeDiagram,
20
+ generate_eye,
21
+ generate_eye_from_edges,
22
+ )
23
+ from oscura.analyzers.eye.metrics import (
24
+ EyeMetrics,
25
+ crossing_percentage,
26
+ eye_contour,
27
+ eye_height,
28
+ eye_width,
29
+ measure_eye,
30
+ q_factor,
31
+ )
32
+
33
+ __all__ = [
34
+ # Diagram generation
35
+ "EyeDiagram",
36
+ # Metrics
37
+ "EyeMetrics",
38
+ "crossing_percentage",
39
+ "eye_contour",
40
+ "eye_height",
41
+ "eye_width",
42
+ "generate_eye",
43
+ "generate_eye_from_edges",
44
+ "measure_eye",
45
+ "q_factor",
46
+ ]
@@ -0,0 +1,434 @@
1
+ """Eye diagram generation from serial data.
2
+
3
+ This module generates eye diagrams by folding waveform data
4
+ at the unit interval boundary.
5
+
6
+ Example:
7
+ >>> from oscura.analyzers.eye.diagram import generate_eye
8
+ >>> eye = generate_eye(trace, unit_interval=1e-9)
9
+ >>> print(f"Eye diagram: {eye.n_traces} traces, {eye.samples_per_ui} samples/UI")
10
+
11
+ References:
12
+ IEEE 802.3: Ethernet Physical Layer Specifications
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass
18
+ from typing import TYPE_CHECKING
19
+
20
+ import numpy as np
21
+
22
+ from oscura.core.exceptions import AnalysisError, InsufficientDataError
23
+
24
+ if TYPE_CHECKING:
25
+ from numpy.typing import NDArray
26
+
27
+ from oscura.core.types import WaveformTrace
28
+
29
+
30
+ @dataclass
31
+ class EyeDiagram:
32
+ """Eye diagram data structure.
33
+
34
+ Attributes:
35
+ data: 2D array of eye traces (n_traces x samples_per_ui).
36
+ time_axis: Time axis in UI (0.0 to 2.0 for 2-UI eye).
37
+ unit_interval: Unit interval in seconds.
38
+ samples_per_ui: Number of samples per unit interval.
39
+ n_traces: Number of overlaid traces.
40
+ sample_rate: Original sample rate in Hz.
41
+ histogram: Optional 2D histogram (voltage x time bins).
42
+ voltage_bins: Bin edges for voltage axis.
43
+ time_bins: Bin edges for time axis.
44
+ """
45
+
46
+ data: NDArray[np.float64]
47
+ time_axis: NDArray[np.float64]
48
+ unit_interval: float
49
+ samples_per_ui: int
50
+ n_traces: int
51
+ sample_rate: float
52
+ histogram: NDArray[np.float64] | None = None
53
+ voltage_bins: NDArray[np.float64] | None = None
54
+ time_bins: NDArray[np.float64] | None = None
55
+
56
+
57
+ def generate_eye(
58
+ trace: WaveformTrace,
59
+ unit_interval: float,
60
+ *,
61
+ n_ui: int = 2,
62
+ trigger_level: float = 0.5,
63
+ trigger_edge: str = "rising",
64
+ max_traces: int | None = None,
65
+ generate_histogram: bool = True,
66
+ histogram_bins: tuple[int, int] = (100, 100),
67
+ ) -> EyeDiagram:
68
+ """Generate eye diagram from waveform data.
69
+
70
+ Folds the waveform at unit interval boundaries to create
71
+ an overlaid eye pattern for signal quality analysis.
72
+
73
+ Args:
74
+ trace: Input waveform trace.
75
+ unit_interval: Unit interval (bit period) in seconds.
76
+ n_ui: Number of unit intervals to display (1 or 2).
77
+ trigger_level: Trigger level as fraction of amplitude.
78
+ trigger_edge: Trigger on "rising" or "falling" edges.
79
+ max_traces: Maximum number of traces to include.
80
+ generate_histogram: Generate 2D histogram for persistence.
81
+ histogram_bins: (voltage_bins, time_bins) for histogram.
82
+
83
+ Returns:
84
+ EyeDiagram with overlaid traces and optional histogram.
85
+
86
+ Raises:
87
+ AnalysisError: If unit interval is too short.
88
+ InsufficientDataError: If not enough data for eye generation.
89
+
90
+ Example:
91
+ >>> eye = generate_eye(trace, unit_interval=1e-9)
92
+ >>> print(f"Generated {eye.n_traces} traces")
93
+
94
+ References:
95
+ OIF CEI: Common Electrical I/O Eye Diagram Methodology
96
+ """
97
+ data = trace.data
98
+ sample_rate = trace.metadata.sample_rate
99
+ 1.0 / sample_rate
100
+
101
+ # Calculate samples per UI
102
+ samples_per_ui = round(unit_interval * sample_rate)
103
+
104
+ if samples_per_ui < 4:
105
+ raise AnalysisError(
106
+ f"Unit interval too short: {samples_per_ui} samples/UI. Need at least 4 samples per UI."
107
+ )
108
+
109
+ n_samples = len(data)
110
+ total_ui_samples = samples_per_ui * n_ui
111
+
112
+ if n_samples < total_ui_samples * 2:
113
+ raise InsufficientDataError(
114
+ f"Need at least {total_ui_samples * 2} samples for eye diagram",
115
+ required=total_ui_samples * 2,
116
+ available=n_samples,
117
+ analysis_type="eye_diagram_generation",
118
+ )
119
+
120
+ # Find trigger points
121
+ low = np.percentile(data, 10)
122
+ high = np.percentile(data, 90)
123
+ threshold = low + trigger_level * (high - low)
124
+
125
+ if trigger_edge == "rising":
126
+ trigger_mask = (data[:-1] < threshold) & (data[1:] >= threshold)
127
+ else:
128
+ trigger_mask = (data[:-1] >= threshold) & (data[1:] < threshold)
129
+
130
+ trigger_indices = np.where(trigger_mask)[0]
131
+
132
+ if len(trigger_indices) < 2:
133
+ raise InsufficientDataError(
134
+ "Not enough trigger events for eye diagram",
135
+ required=2,
136
+ available=len(trigger_indices),
137
+ analysis_type="eye_diagram_generation",
138
+ )
139
+
140
+ # Extract eye traces
141
+ eye_traces = []
142
+ half_ui = samples_per_ui // 2 # Start half UI before trigger
143
+
144
+ for trig_idx in trigger_indices:
145
+ start_idx = trig_idx - half_ui
146
+ end_idx = start_idx + total_ui_samples
147
+
148
+ if start_idx >= 0 and end_idx <= n_samples:
149
+ eye_traces.append(data[start_idx:end_idx])
150
+
151
+ if max_traces is not None and len(eye_traces) >= max_traces:
152
+ break
153
+
154
+ if len(eye_traces) == 0:
155
+ raise InsufficientDataError(
156
+ "Could not extract any complete eye traces",
157
+ required=1,
158
+ available=0,
159
+ analysis_type="eye_diagram_generation",
160
+ )
161
+
162
+ # Stack into 2D array
163
+ eye_data = np.array(eye_traces, dtype=np.float64)
164
+
165
+ # Generate time axis in UI
166
+ time_axis = np.linspace(0, n_ui, total_ui_samples, endpoint=False)
167
+
168
+ # Optional: Generate 2D histogram
169
+ histogram = None
170
+ voltage_bins = None
171
+ time_bins = None
172
+
173
+ if generate_histogram:
174
+ # Flatten for histogram
175
+ all_voltages = eye_data.flatten()
176
+ all_times = np.tile(time_axis, len(eye_traces))
177
+
178
+ # Create histogram
179
+ voltage_range = (np.min(all_voltages), np.max(all_voltages))
180
+ time_range = (0, n_ui)
181
+
182
+ histogram, voltage_edges, time_edges = np.histogram2d(
183
+ all_voltages,
184
+ all_times,
185
+ bins=histogram_bins,
186
+ range=[voltage_range, time_range],
187
+ )
188
+
189
+ voltage_bins = voltage_edges
190
+ time_bins = time_edges
191
+
192
+ return EyeDiagram(
193
+ data=eye_data,
194
+ time_axis=time_axis,
195
+ unit_interval=unit_interval,
196
+ samples_per_ui=samples_per_ui,
197
+ n_traces=len(eye_traces),
198
+ sample_rate=sample_rate,
199
+ histogram=histogram,
200
+ voltage_bins=voltage_bins,
201
+ time_bins=time_bins,
202
+ )
203
+
204
+
205
+ def generate_eye_from_edges(
206
+ trace: WaveformTrace,
207
+ edge_timestamps: NDArray[np.float64],
208
+ *,
209
+ n_ui: int = 2,
210
+ samples_per_ui: int = 100,
211
+ max_traces: int | None = None,
212
+ ) -> EyeDiagram:
213
+ """Generate eye diagram using recovered clock edges.
214
+
215
+ Uses pre-recovered clock edges for triggering, which can provide
216
+ more accurate alignment than threshold-based triggering.
217
+
218
+ Args:
219
+ trace: Input waveform trace.
220
+ edge_timestamps: Array of clock edge timestamps in seconds.
221
+ n_ui: Number of unit intervals to display.
222
+ samples_per_ui: Samples per UI in resampled eye.
223
+ max_traces: Maximum traces to include.
224
+
225
+ Returns:
226
+ EyeDiagram with overlaid traces.
227
+
228
+ Raises:
229
+ InsufficientDataError: If not enough edge timestamps or traces.
230
+
231
+ Example:
232
+ >>> edges = recover_clock_edges(trace)
233
+ >>> eye = generate_eye_from_edges(trace, edges)
234
+ """
235
+ data = trace.data
236
+ sample_rate = trace.metadata.sample_rate
237
+
238
+ if len(edge_timestamps) < 3:
239
+ raise InsufficientDataError(
240
+ "Need at least 3 edge timestamps",
241
+ required=3,
242
+ available=len(edge_timestamps),
243
+ analysis_type="eye_diagram_generation",
244
+ )
245
+
246
+ # Calculate unit interval from edges
247
+ periods = np.diff(edge_timestamps)
248
+ unit_interval = float(np.median(periods))
249
+
250
+ # Create time vector for original data
251
+ original_time = np.arange(len(data)) / sample_rate
252
+
253
+ # Extract and resample traces around each edge
254
+ eye_traces = []
255
+ total_samples = samples_per_ui * n_ui
256
+ half_ui = unit_interval / 2
257
+
258
+ for edge_time in edge_timestamps:
259
+ # Define window around edge
260
+ start_time = edge_time - half_ui
261
+ end_time = start_time + unit_interval * n_ui
262
+
263
+ if start_time < 0 or end_time > original_time[-1]:
264
+ continue
265
+
266
+ # Find samples within window
267
+ mask = (original_time >= start_time) & (original_time <= end_time)
268
+ window_time = original_time[mask] - start_time
269
+ window_data = data[mask]
270
+
271
+ if len(window_data) < 4:
272
+ continue
273
+
274
+ # Resample to consistent samples_per_ui
275
+ resample_time = np.linspace(0, unit_interval * n_ui, total_samples)
276
+ resampled = np.interp(resample_time, window_time, window_data)
277
+
278
+ eye_traces.append(resampled)
279
+
280
+ if max_traces is not None and len(eye_traces) >= max_traces:
281
+ break
282
+
283
+ if len(eye_traces) == 0:
284
+ raise InsufficientDataError(
285
+ "Could not extract any eye traces",
286
+ required=1,
287
+ available=0,
288
+ analysis_type="eye_diagram_generation",
289
+ )
290
+
291
+ eye_data = np.array(eye_traces, dtype=np.float64)
292
+ time_axis = np.linspace(0, n_ui, total_samples, endpoint=False)
293
+
294
+ return EyeDiagram(
295
+ data=eye_data,
296
+ time_axis=time_axis,
297
+ unit_interval=unit_interval,
298
+ samples_per_ui=samples_per_ui,
299
+ n_traces=len(eye_traces),
300
+ sample_rate=sample_rate,
301
+ )
302
+
303
+
304
+ def auto_center_eye_diagram(
305
+ eye: EyeDiagram,
306
+ *,
307
+ trigger_fraction: float = 0.5,
308
+ symmetric_range: bool = True,
309
+ ) -> EyeDiagram:
310
+ """Auto-center eye diagram on optimal crossing point.
311
+
312
+ Automatically centers eye diagrams on the optimal trigger point
313
+ and scales amplitude for maximum eye opening visibility with
314
+ symmetric vertical centering.
315
+
316
+ Args:
317
+ eye: Input EyeDiagram to center.
318
+ trigger_fraction: Trigger level as fraction of amplitude (default 0.5 = 50%).
319
+ symmetric_range: Use symmetric amplitude range ±max(abs(signal)).
320
+
321
+ Returns:
322
+ Centered EyeDiagram with adjusted data.
323
+
324
+ Raises:
325
+ ValueError: If trigger_fraction is not in [0, 1].
326
+
327
+ Example:
328
+ >>> eye = generate_eye(trace, unit_interval=1e-9)
329
+ >>> centered = auto_center_eye_diagram(eye)
330
+ >>> # Centered at 50% crossing with symmetric amplitude
331
+
332
+ References:
333
+ VIS-021: Eye Diagram Auto-Centering
334
+ """
335
+ if not 0 <= trigger_fraction <= 1:
336
+ raise ValueError(f"trigger_fraction must be in [0, 1], got {trigger_fraction}")
337
+
338
+ data = eye.data
339
+
340
+ # Calculate optimal trigger point using histogram-based threshold
341
+ # Find median value (represents middle level)
342
+ np.median(data)
343
+
344
+ # Calculate amplitude range
345
+ low = np.percentile(data, 10)
346
+ high = np.percentile(data, 90)
347
+ amplitude_range = high - low
348
+
349
+ # Trigger threshold at specified fraction
350
+ threshold = low + trigger_fraction * amplitude_range
351
+
352
+ # Find crossing points for each trace
353
+ # A crossing is where signal crosses threshold
354
+ n_traces, samples_per_trace = data.shape
355
+ crossing_indices = []
356
+
357
+ for trace_idx in range(n_traces):
358
+ trace = data[trace_idx, :]
359
+
360
+ # Find zero-crossings relative to threshold
361
+ crossings = np.where((trace[:-1] < threshold) & (trace[1:] >= threshold))[0]
362
+
363
+ if len(crossings) > 0:
364
+ # Use first crossing in this trace
365
+ crossing_indices.append(crossings[0])
366
+
367
+ if len(crossing_indices) == 0:
368
+ # No crossings found, return original
369
+ import warnings
370
+
371
+ warnings.warn(
372
+ "No crossing points found, cannot auto-center eye diagram",
373
+ UserWarning,
374
+ stacklevel=2,
375
+ )
376
+ return eye
377
+
378
+ # Calculate median crossing position
379
+ int(np.median(crossing_indices))
380
+
381
+ # Align all traces to common crossing point
382
+ # This requires resampling/shifting each trace
383
+ aligned_data = np.zeros_like(data)
384
+ target_crossing = samples_per_trace // 2 # Center of trace
385
+
386
+ for trace_idx in range(n_traces):
387
+ trace = data[trace_idx, :]
388
+
389
+ # Find crossing for this trace
390
+ crossings = np.where((trace[:-1] < threshold) & (trace[1:] >= threshold))[0]
391
+
392
+ if len(crossings) > 0:
393
+ crossing = crossings[0]
394
+ shift = target_crossing - crossing
395
+
396
+ # Shift trace by interpolation
397
+ if shift != 0:
398
+ # Simple roll (circular shift)
399
+ aligned_data[trace_idx, :] = np.roll(trace, shift)
400
+ else:
401
+ aligned_data[trace_idx, :] = trace
402
+ else:
403
+ # No crossing, keep original
404
+ aligned_data[trace_idx, :] = trace
405
+
406
+ # Scale amplitude to symmetric range if requested
407
+ if symmetric_range:
408
+ max_abs = np.max(np.abs(aligned_data))
409
+ if max_abs > 0:
410
+ # Center on zero
411
+ aligned_data = aligned_data - np.mean(aligned_data)
412
+ # Scale to ±max for symmetric range
413
+ # No additional scaling needed, data already centered
414
+
415
+ # Create centered eye diagram
416
+ return EyeDiagram(
417
+ data=aligned_data,
418
+ time_axis=eye.time_axis,
419
+ unit_interval=eye.unit_interval,
420
+ samples_per_ui=eye.samples_per_ui,
421
+ n_traces=eye.n_traces,
422
+ sample_rate=eye.sample_rate,
423
+ histogram=None, # Invalidate histogram after centering
424
+ voltage_bins=None,
425
+ time_bins=None,
426
+ )
427
+
428
+
429
+ __all__ = [
430
+ "EyeDiagram",
431
+ "auto_center_eye_diagram",
432
+ "generate_eye",
433
+ "generate_eye_from_edges",
434
+ ]