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,581 @@
1
+ """Pulse width and glitch triggering for TraceKit.
2
+
3
+ Provides pulse width triggering, glitch detection, and runt pulse
4
+ detection for signal integrity analysis.
5
+
6
+ Example:
7
+ >>> from oscura.triggering.pulse import PulseWidthTrigger, find_glitches
8
+ >>> # Find pulses between 100ns and 200ns
9
+ >>> trigger = PulseWidthTrigger(level=1.5, min_width=100e-9, max_width=200e-9)
10
+ >>> events = trigger.find_events(trace)
11
+ >>> # Find glitches shorter than 50ns
12
+ >>> glitches = find_glitches(trace, max_width=50e-9)
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass
18
+ from typing import Literal
19
+
20
+ import numpy as np
21
+
22
+ from oscura.core.exceptions import AnalysisError
23
+ from oscura.core.types import DigitalTrace, WaveformTrace
24
+ from oscura.triggering.base import (
25
+ Trigger,
26
+ TriggerEvent,
27
+ TriggerType,
28
+ interpolate_crossing,
29
+ )
30
+
31
+
32
+ @dataclass
33
+ class PulseInfo:
34
+ """Information about a detected pulse.
35
+
36
+ Attributes:
37
+ start_time: Start time of pulse in seconds.
38
+ end_time: End time of pulse in seconds.
39
+ width: Pulse width in seconds.
40
+ polarity: "positive" or "negative".
41
+ start_index: Sample index at pulse start.
42
+ end_index: Sample index at pulse end.
43
+ amplitude: Peak amplitude during pulse.
44
+ """
45
+
46
+ start_time: float
47
+ end_time: float
48
+ width: float
49
+ polarity: Literal["positive", "negative"]
50
+ start_index: int
51
+ end_index: int
52
+ amplitude: float
53
+
54
+
55
+ class PulseWidthTrigger(Trigger):
56
+ """Pulse width trigger for detecting pulses in a width range.
57
+
58
+ Triggers on pulses that fall within the specified width range.
59
+
60
+ Attributes:
61
+ level: Threshold level for pulse detection.
62
+ polarity: Pulse polarity - "positive", "negative", or "either".
63
+ min_width: Minimum pulse width (None for no minimum).
64
+ max_width: Maximum pulse width (None for no maximum).
65
+ """
66
+
67
+ def __init__(
68
+ self,
69
+ level: float,
70
+ polarity: Literal["positive", "negative", "either"] = "positive",
71
+ min_width: float | None = None,
72
+ max_width: float | None = None,
73
+ ) -> None:
74
+ """Initialize pulse width trigger.
75
+
76
+ Args:
77
+ level: Threshold level for pulse detection.
78
+ polarity: Pulse polarity to detect.
79
+ min_width: Minimum pulse width in seconds.
80
+ max_width: Maximum pulse width in seconds.
81
+
82
+ Raises:
83
+ AnalysisError: If min_width is greater than max_width.
84
+ """
85
+ self.level = level
86
+ self.polarity = polarity
87
+ self.min_width = min_width
88
+ self.max_width = max_width
89
+
90
+ if min_width is not None and max_width is not None and min_width > max_width:
91
+ raise AnalysisError("min_width cannot be greater than max_width")
92
+
93
+ def find_events(
94
+ self,
95
+ trace: WaveformTrace | DigitalTrace,
96
+ ) -> list[TriggerEvent]:
97
+ """Find pulses matching the width criteria.
98
+
99
+ Args:
100
+ trace: Input trace.
101
+
102
+ Returns:
103
+ List of trigger events for matching pulses.
104
+ """
105
+ pulses = self._find_all_pulses(trace)
106
+
107
+ # Filter by width
108
+ events: list[TriggerEvent] = []
109
+ for pulse in pulses:
110
+ if self.min_width is not None and pulse.width < self.min_width:
111
+ continue
112
+ if self.max_width is not None and pulse.width > self.max_width:
113
+ continue
114
+
115
+ events.append(
116
+ TriggerEvent(
117
+ timestamp=pulse.start_time,
118
+ sample_index=pulse.start_index,
119
+ event_type=TriggerType.PULSE_WIDTH,
120
+ level=pulse.amplitude,
121
+ duration=pulse.width,
122
+ data={
123
+ "polarity": pulse.polarity,
124
+ "end_time": pulse.end_time,
125
+ "end_index": pulse.end_index,
126
+ },
127
+ )
128
+ )
129
+
130
+ return events
131
+
132
+ def _find_all_pulses(
133
+ self,
134
+ trace: WaveformTrace | DigitalTrace,
135
+ ) -> list[PulseInfo]:
136
+ """Find all pulses in the trace."""
137
+ if isinstance(trace, DigitalTrace):
138
+ data = trace.data.astype(np.float64)
139
+ level = 0.5
140
+ else:
141
+ data = trace.data
142
+ level = self.level
143
+
144
+ sample_period = trace.metadata.time_base
145
+ pulses: list[PulseInfo] = []
146
+
147
+ # Find all threshold crossings
148
+ above = data >= level
149
+ below = data < level
150
+
151
+ # Rising edges: transition from below to above
152
+ rising = np.where(below[:-1] & above[1:])[0]
153
+ # Falling edges: transition from above to below
154
+ falling = np.where(above[:-1] & below[1:])[0]
155
+
156
+ if self.polarity in ("positive", "either"):
157
+ # Positive pulses: rising -> falling
158
+ for r_idx in rising:
159
+ # Find next falling edge
160
+ next_falling = falling[falling > r_idx]
161
+ if len(next_falling) == 0:
162
+ continue
163
+ f_idx = next_falling[0]
164
+
165
+ start_time = interpolate_crossing(data, r_idx, level, sample_period, True)
166
+ end_time = interpolate_crossing(data, f_idx, level, sample_period, False)
167
+ width = end_time - start_time
168
+
169
+ # Get peak amplitude
170
+ pulse_data = data[r_idx : f_idx + 1]
171
+ amplitude = float(np.max(pulse_data)) if len(pulse_data) > 0 else level
172
+
173
+ pulses.append(
174
+ PulseInfo(
175
+ start_time=start_time,
176
+ end_time=end_time,
177
+ width=width,
178
+ polarity="positive",
179
+ start_index=int(r_idx),
180
+ end_index=int(f_idx),
181
+ amplitude=amplitude,
182
+ )
183
+ )
184
+
185
+ if self.polarity in ("negative", "either"):
186
+ # Negative pulses: falling -> rising
187
+ for f_idx in falling:
188
+ # Find next rising edge
189
+ next_rising = rising[rising > f_idx]
190
+ if len(next_rising) == 0:
191
+ continue
192
+ r_idx = next_rising[0]
193
+
194
+ start_time = interpolate_crossing(data, f_idx, level, sample_period, False)
195
+ end_time = interpolate_crossing(data, r_idx, level, sample_period, True)
196
+ width = end_time - start_time
197
+
198
+ # Get peak (minimum) amplitude
199
+ pulse_data = data[f_idx : r_idx + 1]
200
+ amplitude = float(np.min(pulse_data)) if len(pulse_data) > 0 else level
201
+
202
+ pulses.append(
203
+ PulseInfo(
204
+ start_time=start_time,
205
+ end_time=end_time,
206
+ width=width,
207
+ polarity="negative",
208
+ start_index=int(f_idx),
209
+ end_index=int(r_idx),
210
+ amplitude=amplitude,
211
+ )
212
+ )
213
+
214
+ # Sort by start time
215
+ pulses.sort(key=lambda p: p.start_time)
216
+ return pulses
217
+
218
+
219
+ class GlitchTrigger(Trigger):
220
+ """Glitch trigger for detecting narrow pulses.
221
+
222
+ Glitches are pulses shorter than a maximum width threshold.
223
+
224
+ Attributes:
225
+ level: Threshold level.
226
+ max_width: Maximum pulse width to be considered a glitch.
227
+ polarity: Glitch polarity - "positive", "negative", or "either".
228
+ """
229
+
230
+ def __init__(
231
+ self,
232
+ level: float,
233
+ max_width: float = 100e-9,
234
+ polarity: Literal["positive", "negative", "either"] = "either",
235
+ ) -> None:
236
+ """Initialize glitch trigger.
237
+
238
+ Args:
239
+ level: Threshold level.
240
+ max_width: Maximum pulse width to trigger (in seconds).
241
+ polarity: Glitch polarity to detect.
242
+ """
243
+ self.level = level
244
+ self.max_width = max_width
245
+ self.polarity = polarity
246
+
247
+ def find_events(
248
+ self,
249
+ trace: WaveformTrace | DigitalTrace,
250
+ ) -> list[TriggerEvent]:
251
+ """Find all glitches in the trace.
252
+
253
+ Args:
254
+ trace: Input trace.
255
+
256
+ Returns:
257
+ List of trigger events for each glitch.
258
+ """
259
+ pulse_trigger = PulseWidthTrigger(
260
+ level=self.level,
261
+ polarity=self.polarity,
262
+ min_width=None,
263
+ max_width=self.max_width,
264
+ )
265
+
266
+ events = pulse_trigger.find_events(trace)
267
+
268
+ # Reclassify as glitch events
269
+ for event in events:
270
+ event.event_type = TriggerType.GLITCH
271
+
272
+ return events
273
+
274
+
275
+ class RuntTrigger(Trigger):
276
+ """Runt pulse trigger for detecting incomplete transitions.
277
+
278
+ Runt pulses cross one threshold but not the other, indicating
279
+ incomplete signal transitions.
280
+
281
+ Attributes:
282
+ low_threshold: Lower threshold level.
283
+ high_threshold: Upper threshold level.
284
+ polarity: Runt polarity - "positive", "negative", or "either".
285
+ """
286
+
287
+ def __init__(
288
+ self,
289
+ low_threshold: float,
290
+ high_threshold: float,
291
+ polarity: Literal["positive", "negative", "either"] = "either",
292
+ ) -> None:
293
+ """Initialize runt trigger.
294
+
295
+ Args:
296
+ low_threshold: Lower threshold (e.g., logic low).
297
+ high_threshold: Upper threshold (e.g., logic high).
298
+ polarity: "positive" for rising runts, "negative" for falling.
299
+
300
+ Raises:
301
+ AnalysisError: If low_threshold is not less than high_threshold.
302
+ """
303
+ if low_threshold >= high_threshold:
304
+ raise AnalysisError("low_threshold must be less than high_threshold")
305
+
306
+ self.low_threshold = low_threshold
307
+ self.high_threshold = high_threshold
308
+ self.polarity = polarity
309
+
310
+ def find_events(
311
+ self,
312
+ trace: WaveformTrace | DigitalTrace,
313
+ ) -> list[TriggerEvent]:
314
+ """Find all runt pulses in the trace.
315
+
316
+ Args:
317
+ trace: Input trace.
318
+
319
+ Returns:
320
+ List of trigger events for each runt pulse.
321
+ """
322
+ if isinstance(trace, DigitalTrace):
323
+ # Digital traces don't have runts
324
+ return []
325
+
326
+ data = trace.data
327
+ sample_period = trace.metadata.time_base
328
+ events: list[TriggerEvent] = []
329
+
330
+ # Track signal zones
331
+ # Zone 0: below low_threshold
332
+ # Zone 1: between thresholds
333
+ # Zone 2: above high_threshold
334
+ def get_zone(value: float) -> int:
335
+ if value < self.low_threshold:
336
+ return 0
337
+ elif value > self.high_threshold:
338
+ return 2
339
+ else:
340
+ return 1
341
+
342
+ zones = np.array([get_zone(v) for v in data])
343
+
344
+ # Find runt pulses: transitions that enter zone 1 but don't reach the other side
345
+ i = 0
346
+ while i < len(zones) - 1:
347
+ curr_zone = zones[i]
348
+
349
+ if curr_zone == 0:
350
+ # Starting low - look for positive runt
351
+ if self.polarity in ("positive", "either"):
352
+ # Find transition to zone 1
353
+ if zones[i + 1] == 1:
354
+ start_idx = i
355
+ # Track through zone 1
356
+ j = i + 1
357
+ while j < len(zones) and zones[j] == 1:
358
+ j += 1
359
+ if j < len(zones) and zones[j] == 0:
360
+ # Returned to low without reaching high - RUNT
361
+ peak = float(np.max(data[start_idx : j + 1]))
362
+ events.append(
363
+ TriggerEvent(
364
+ timestamp=start_idx * sample_period,
365
+ sample_index=start_idx,
366
+ event_type=TriggerType.RUNT,
367
+ level=peak,
368
+ duration=(j - start_idx) * sample_period,
369
+ data={
370
+ "polarity": "positive",
371
+ "expected_high": self.high_threshold,
372
+ "actual_peak": peak,
373
+ },
374
+ )
375
+ )
376
+ i = j
377
+ continue
378
+
379
+ elif curr_zone == 2:
380
+ # Starting high - look for negative runt
381
+ if self.polarity in ("negative", "either") and zones[i + 1] == 1:
382
+ start_idx = i
383
+ j = i + 1
384
+ while j < len(zones) and zones[j] == 1:
385
+ j += 1
386
+ if j < len(zones) and zones[j] == 2:
387
+ # Returned to high without reaching low - RUNT
388
+ trough = float(np.min(data[start_idx : j + 1]))
389
+ events.append(
390
+ TriggerEvent(
391
+ timestamp=start_idx * sample_period,
392
+ sample_index=start_idx,
393
+ event_type=TriggerType.RUNT,
394
+ level=trough,
395
+ duration=(j - start_idx) * sample_period,
396
+ data={
397
+ "polarity": "negative",
398
+ "expected_low": self.low_threshold,
399
+ "actual_trough": trough,
400
+ },
401
+ )
402
+ )
403
+ i = j
404
+ continue
405
+
406
+ i += 1
407
+
408
+ return events
409
+
410
+
411
+ def find_pulses(
412
+ trace: WaveformTrace,
413
+ *,
414
+ level: float | None = None,
415
+ polarity: Literal["positive", "negative", "either"] = "positive",
416
+ min_width: float | None = None,
417
+ max_width: float | None = None,
418
+ ) -> list[TriggerEvent]:
419
+ """Find pulses matching width criteria.
420
+
421
+ Args:
422
+ trace: Input waveform trace.
423
+ level: Threshold level. If None, uses 50% of amplitude.
424
+ polarity: Pulse polarity to find.
425
+ min_width: Minimum pulse width in seconds.
426
+ max_width: Maximum pulse width in seconds.
427
+
428
+ Returns:
429
+ List of trigger events for matching pulses.
430
+
431
+ Example:
432
+ >>> # Find all positive pulses between 1us and 10us
433
+ >>> pulses = find_pulses(trace, min_width=1e-6, max_width=10e-6)
434
+ """
435
+ if level is None:
436
+ level = (np.min(trace.data) + np.max(trace.data)) / 2
437
+
438
+ trigger = PulseWidthTrigger(
439
+ level=level,
440
+ polarity=polarity,
441
+ min_width=min_width,
442
+ max_width=max_width,
443
+ )
444
+ return trigger.find_events(trace)
445
+
446
+
447
+ def find_glitches(
448
+ trace: WaveformTrace,
449
+ max_width: float = 100e-9,
450
+ *,
451
+ level: float | None = None,
452
+ polarity: Literal["positive", "negative", "either"] = "either",
453
+ ) -> list[TriggerEvent]:
454
+ """Find glitches (narrow pulses) in a trace.
455
+
456
+ Args:
457
+ trace: Input waveform trace.
458
+ max_width: Maximum width to be considered a glitch (default 100ns).
459
+ level: Threshold level. If None, uses 50% of amplitude.
460
+ polarity: Glitch polarity to find.
461
+
462
+ Returns:
463
+ List of trigger events for each glitch.
464
+
465
+ Example:
466
+ >>> # Find all glitches shorter than 50ns
467
+ >>> glitches = find_glitches(trace, max_width=50e-9)
468
+ >>> print(f"Found {len(glitches)} glitches")
469
+ """
470
+ if level is None:
471
+ level = (np.min(trace.data) + np.max(trace.data)) / 2
472
+
473
+ trigger = GlitchTrigger(
474
+ level=level,
475
+ max_width=max_width,
476
+ polarity=polarity,
477
+ )
478
+ return trigger.find_events(trace)
479
+
480
+
481
+ def find_runt_pulses(
482
+ trace: WaveformTrace,
483
+ low_threshold: float | None = None,
484
+ high_threshold: float | None = None,
485
+ *,
486
+ polarity: Literal["positive", "negative", "either"] = "either",
487
+ ) -> list[TriggerEvent]:
488
+ """Find runt pulses (incomplete transitions) in a trace.
489
+
490
+ Args:
491
+ trace: Input waveform trace.
492
+ low_threshold: Lower threshold. If None, uses 20% of amplitude.
493
+ high_threshold: Upper threshold. If None, uses 80% of amplitude.
494
+ polarity: Runt polarity to find.
495
+
496
+ Returns:
497
+ List of trigger events for each runt pulse.
498
+
499
+ Example:
500
+ >>> # Find runts using standard 20%/80% thresholds
501
+ >>> runts = find_runt_pulses(trace)
502
+ >>> for runt in runts:
503
+ ... print(f"Runt at {runt.timestamp*1e6:.2f} us")
504
+ """
505
+ if low_threshold is None:
506
+ amplitude = np.max(trace.data) - np.min(trace.data)
507
+ low_threshold = np.min(trace.data) + 0.2 * amplitude
508
+
509
+ if high_threshold is None:
510
+ amplitude = np.max(trace.data) - np.min(trace.data)
511
+ high_threshold = np.min(trace.data) + 0.8 * amplitude
512
+
513
+ trigger = RuntTrigger(
514
+ low_threshold=low_threshold,
515
+ high_threshold=high_threshold,
516
+ polarity=polarity,
517
+ )
518
+ return trigger.find_events(trace)
519
+
520
+
521
+ def pulse_statistics(
522
+ trace: WaveformTrace,
523
+ *,
524
+ level: float | None = None,
525
+ polarity: Literal["positive", "negative"] = "positive",
526
+ ) -> dict[str, float]:
527
+ """Calculate pulse width statistics.
528
+
529
+ Args:
530
+ trace: Input waveform trace.
531
+ level: Threshold level.
532
+ polarity: Pulse polarity to analyze.
533
+
534
+ Returns:
535
+ Dictionary with pulse statistics:
536
+ - count: Number of pulses
537
+ - min_width: Minimum pulse width
538
+ - max_width: Maximum pulse width
539
+ - mean_width: Mean pulse width
540
+ - std_width: Standard deviation of pulse widths
541
+
542
+ Example:
543
+ >>> stats = pulse_statistics(trace)
544
+ >>> print(f"Mean pulse width: {stats['mean_width']*1e6:.2f} us")
545
+ """
546
+ if level is None:
547
+ level = (np.min(trace.data) + np.max(trace.data)) / 2
548
+
549
+ trigger = PulseWidthTrigger(level=level, polarity=polarity)
550
+ events = trigger.find_events(trace)
551
+
552
+ if len(events) == 0:
553
+ return {
554
+ "count": 0,
555
+ "min_width": np.nan,
556
+ "max_width": np.nan,
557
+ "mean_width": np.nan,
558
+ "std_width": np.nan,
559
+ }
560
+
561
+ widths = np.array([e.duration for e in events if e.duration is not None])
562
+
563
+ return {
564
+ "count": len(widths),
565
+ "min_width": float(np.min(widths)),
566
+ "max_width": float(np.max(widths)),
567
+ "mean_width": float(np.mean(widths)),
568
+ "std_width": float(np.std(widths)),
569
+ }
570
+
571
+
572
+ __all__ = [
573
+ "GlitchTrigger",
574
+ "PulseInfo",
575
+ "PulseWidthTrigger",
576
+ "RuntTrigger",
577
+ "find_glitches",
578
+ "find_pulses",
579
+ "find_runt_pulses",
580
+ "pulse_statistics",
581
+ ]