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,413 @@
1
+ """Digital signal extraction and edge detection.
2
+
3
+ This module provides functions for extracting digital signals from
4
+ analog waveforms and detecting edge transitions.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.digital import to_digital, detect_edges
9
+ >>> digital = to_digital(analog_trace, threshold=1.4)
10
+ >>> edges = detect_edges(digital, edge_type="rising")
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import TYPE_CHECKING, Any, Literal
16
+
17
+ import numpy as np
18
+
19
+ from oscura.core.exceptions import InsufficientDataError
20
+ from oscura.core.types import DigitalTrace, WaveformTrace
21
+
22
+ if TYPE_CHECKING:
23
+ from numpy.typing import NDArray
24
+
25
+ # Standard logic family threshold constants
26
+ # Reference: Various IC manufacturer datasheets
27
+ LOGIC_FAMILIES: dict[str, dict[str, float]] = {
28
+ "TTL": {
29
+ "VIL_max": 0.8, # Maximum input low voltage
30
+ "VIH_min": 2.0, # Minimum input high voltage
31
+ "VOL_max": 0.4, # Maximum output low voltage
32
+ "VOH_min": 2.4, # Minimum output high voltage
33
+ "VCC": 5.0,
34
+ },
35
+ "CMOS_5V": {
36
+ "VIL_max": 1.5,
37
+ "VIH_min": 3.5,
38
+ "VOL_max": 0.1,
39
+ "VOH_min": 4.9,
40
+ "VCC": 5.0,
41
+ },
42
+ "LVTTL": {
43
+ "VIL_max": 0.8,
44
+ "VIH_min": 1.5,
45
+ "VOL_max": 0.4,
46
+ "VOH_min": 2.4,
47
+ "VCC": 3.3,
48
+ },
49
+ "LVCMOS_3V3": {
50
+ "VIL_max": 0.3 * 3.3, # 30% of VCC
51
+ "VIH_min": 0.7 * 3.3, # 70% of VCC
52
+ "VOL_max": 0.1,
53
+ "VOH_min": 3.2,
54
+ "VCC": 3.3,
55
+ },
56
+ "LVCMOS_2V5": {
57
+ "VIL_max": 0.3 * 2.5,
58
+ "VIH_min": 0.7 * 2.5,
59
+ "VOL_max": 0.1,
60
+ "VOH_min": 2.4,
61
+ "VCC": 2.5,
62
+ },
63
+ "LVCMOS_1V8": {
64
+ "VIL_max": 0.3 * 1.8,
65
+ "VIH_min": 0.7 * 1.8,
66
+ "VOL_max": 0.1,
67
+ "VOH_min": 1.7,
68
+ "VCC": 1.8,
69
+ },
70
+ "LVCMOS_1V2": {
71
+ "VIL_max": 0.3 * 1.2,
72
+ "VIH_min": 0.7 * 1.2,
73
+ "VOL_max": 0.1,
74
+ "VOH_min": 1.1,
75
+ "VCC": 1.2,
76
+ },
77
+ }
78
+
79
+
80
+ def to_digital(
81
+ trace: WaveformTrace,
82
+ *,
83
+ threshold: float | Literal["auto"] = "auto",
84
+ hysteresis: float | tuple[float, float] | None = None,
85
+ ) -> DigitalTrace:
86
+ """Extract digital signal from analog waveform.
87
+
88
+ Converts an analog waveform to a digital (boolean) signal using
89
+ threshold comparison.
90
+
91
+ Args:
92
+ trace: Input analog waveform trace.
93
+ threshold: Voltage threshold for conversion. Can be:
94
+ - A float value for fixed threshold
95
+ - "auto" for adaptive threshold (midpoint of 10th-90th percentile)
96
+ hysteresis: Hysteresis for noise immunity. Can be:
97
+ - None: No hysteresis
98
+ - A float: Symmetric hysteresis band around threshold
99
+ - A tuple (low, high): Explicit low and high thresholds
100
+
101
+ Returns:
102
+ DigitalTrace with boolean data and detected edges.
103
+
104
+ Raises:
105
+ InsufficientDataError: If trace has insufficient data.
106
+
107
+ Example:
108
+ >>> digital = to_digital(analog_trace, threshold=1.4)
109
+ >>> print(f"High samples: {digital.data.sum()}")
110
+
111
+ >>> # With hysteresis for noisy signals
112
+ >>> digital = to_digital(analog_trace, threshold=1.4, hysteresis=0.2)
113
+
114
+ References:
115
+ TTL Logic thresholds: VIL_max=0.8V, VIH_min=2.0V
116
+ """
117
+ if len(trace.data) < 2:
118
+ raise InsufficientDataError(
119
+ "Trace too short for digital extraction",
120
+ required=2,
121
+ available=len(trace.data),
122
+ analysis_type="digital_extraction",
123
+ )
124
+
125
+ # Convert memoryview to ndarray if needed
126
+ data = np.asarray(trace.data)
127
+
128
+ # Determine threshold
129
+ if threshold == "auto":
130
+ # Adaptive threshold: midpoint of 10th-90th percentile
131
+ p10, p90 = np.percentile(data, [10, 90])
132
+ thresh_value = (p10 + p90) / 2.0
133
+ else:
134
+ thresh_value = float(threshold)
135
+
136
+ # Apply threshold with or without hysteresis
137
+ if hysteresis is not None:
138
+ if isinstance(hysteresis, tuple):
139
+ thresh_low, thresh_high = hysteresis
140
+ else:
141
+ thresh_low = thresh_value - hysteresis / 2
142
+ thresh_high = thresh_value + hysteresis / 2
143
+ digital_data = _apply_hysteresis(data, thresh_low, thresh_high)
144
+ else:
145
+ digital_data = data >= thresh_value
146
+
147
+ # Detect edges
148
+ edges = _detect_edges_internal(data, digital_data, trace.metadata.sample_rate, thresh_value)
149
+
150
+ return DigitalTrace(
151
+ data=digital_data,
152
+ metadata=trace.metadata,
153
+ edges=edges,
154
+ )
155
+
156
+
157
+ def _apply_hysteresis(
158
+ data: NDArray[np.floating[Any]],
159
+ thresh_low: float,
160
+ thresh_high: float,
161
+ ) -> NDArray[np.bool_]:
162
+ """Apply Schmitt trigger-style hysteresis thresholding.
163
+
164
+ Args:
165
+ data: Input analog data.
166
+ thresh_low: Lower threshold (switch to low when below).
167
+ thresh_high: Upper threshold (switch to high when above).
168
+
169
+ Returns:
170
+ Boolean array with hysteresis applied.
171
+ """
172
+ result = np.zeros(len(data), dtype=np.bool_)
173
+
174
+ # Initial state based on first sample
175
+ state = data[0] >= (thresh_low + thresh_high) / 2
176
+
177
+ for i, value in enumerate(data):
178
+ if state:
179
+ # Currently high, switch low if below thresh_low
180
+ if value < thresh_low:
181
+ state = False
182
+ # Currently low, switch high if above thresh_high
183
+ elif value >= thresh_high:
184
+ state = True
185
+ result[i] = state
186
+
187
+ return result
188
+
189
+
190
+ def detect_edges(
191
+ trace: WaveformTrace | DigitalTrace,
192
+ *,
193
+ edge_type: Literal["rising", "falling", "both"] = "both",
194
+ threshold: float | Literal["auto"] = "auto",
195
+ ) -> NDArray[np.float64]:
196
+ """Detect edge transitions in a signal.
197
+
198
+ Finds rising and/or falling edges with sub-sample timestamp
199
+ interpolation for improved accuracy.
200
+
201
+ Args:
202
+ trace: Input waveform (analog or digital).
203
+ edge_type: Type of edges to detect:
204
+ - "rising": Low-to-high transitions
205
+ - "falling": High-to-low transitions
206
+ - "both": All transitions
207
+ threshold: Threshold for edge detection (only for analog traces).
208
+
209
+ Returns:
210
+ Array of edge timestamps in seconds.
211
+
212
+ Raises:
213
+ InsufficientDataError: If trace has insufficient data.
214
+
215
+ Example:
216
+ >>> edges = detect_edges(trace, edge_type="rising")
217
+ >>> print(f"Found {len(edges)} rising edges")
218
+ """
219
+ if len(trace.data) < 2:
220
+ raise InsufficientDataError(
221
+ "Trace too short for edge detection",
222
+ required=2,
223
+ available=len(trace.data),
224
+ analysis_type="edge_detection",
225
+ )
226
+
227
+ # Convert to digital if analog
228
+ digital = to_digital(trace, threshold=threshold) if isinstance(trace, WaveformTrace) else trace
229
+
230
+ # Find transitions - ensure we have a numpy array
231
+ data = np.asarray(digital.data)
232
+
233
+ transitions = np.diff(data.astype(np.int8))
234
+
235
+ # Get edge indices
236
+ if edge_type == "rising":
237
+ edge_indices = np.where(transitions == 1)[0]
238
+ elif edge_type == "falling":
239
+ edge_indices = np.where(transitions == -1)[0]
240
+ else: # both
241
+ edge_indices = np.where(transitions != 0)[0]
242
+
243
+ # Convert indices to timestamps
244
+ sample_period = digital.metadata.time_base
245
+ timestamps = edge_indices.astype(np.float64) * sample_period
246
+
247
+ # Sub-sample interpolation for analog traces
248
+ if isinstance(trace, WaveformTrace) and threshold != "auto":
249
+ thresh_value = float(threshold)
250
+ timestamps = _interpolate_edges(trace.data, edge_indices, sample_period, thresh_value)
251
+
252
+ return timestamps
253
+
254
+
255
+ def _detect_edges_internal(
256
+ analog_data: NDArray[np.floating[Any]],
257
+ digital_data: NDArray[np.bool_],
258
+ sample_rate: float,
259
+ threshold: float,
260
+ ) -> list[tuple[float, bool]]:
261
+ """Detect edges and return as (timestamp, is_rising) tuples.
262
+
263
+ Args:
264
+ analog_data: Original analog data for interpolation.
265
+ digital_data: Thresholded digital data.
266
+ sample_rate: Sample rate in Hz.
267
+ threshold: Threshold used for conversion.
268
+
269
+ Returns:
270
+ List of (timestamp, is_rising) tuples.
271
+ """
272
+ sample_period = 1.0 / sample_rate
273
+ transitions = np.diff(digital_data.astype(np.int8))
274
+
275
+ edges: list[tuple[float, bool]] = []
276
+
277
+ # Rising edges
278
+ rising_indices = np.where(transitions == 1)[0]
279
+ for idx in rising_indices:
280
+ # Sub-sample interpolation
281
+ if 0 < idx < len(analog_data) - 1:
282
+ t = _interpolate_crossing(
283
+ analog_data[idx], analog_data[idx + 1], threshold, sample_period
284
+ )
285
+ timestamp = idx * sample_period + t
286
+ else:
287
+ timestamp = idx * sample_period
288
+ edges.append((timestamp, True))
289
+
290
+ # Falling edges
291
+ falling_indices = np.where(transitions == -1)[0]
292
+ for idx in falling_indices:
293
+ if 0 < idx < len(analog_data) - 1:
294
+ t = _interpolate_crossing(
295
+ analog_data[idx], analog_data[idx + 1], threshold, sample_period
296
+ )
297
+ timestamp = idx * sample_period + t
298
+ else:
299
+ timestamp = idx * sample_period
300
+ edges.append((timestamp, False))
301
+
302
+ # Sort by timestamp
303
+ edges.sort(key=lambda x: x[0])
304
+
305
+ return edges
306
+
307
+
308
+ def _interpolate_edges(
309
+ data: NDArray[np.floating[Any]],
310
+ edge_indices: NDArray[np.intp],
311
+ sample_period: float,
312
+ threshold: float,
313
+ ) -> NDArray[np.float64]:
314
+ """Interpolate edge timestamps for sub-sample accuracy.
315
+
316
+ Uses linear interpolation between samples to estimate the
317
+ exact crossing point.
318
+
319
+ Args:
320
+ data: Analog data array.
321
+ edge_indices: Indices of detected edges.
322
+ sample_period: Time between samples.
323
+ threshold: Threshold level.
324
+
325
+ Returns:
326
+ Array of interpolated timestamps.
327
+ """
328
+ timestamps = np.zeros(len(edge_indices), dtype=np.float64)
329
+
330
+ for i, idx in enumerate(edge_indices):
331
+ base_time = idx * sample_period
332
+
333
+ if 0 < idx < len(data) - 1:
334
+ # Linear interpolation between samples
335
+ t = _interpolate_crossing(data[idx], data[idx + 1], threshold, sample_period)
336
+ timestamps[i] = base_time + t
337
+ else:
338
+ timestamps[i] = base_time
339
+
340
+ return timestamps
341
+
342
+
343
+ def _interpolate_crossing(
344
+ v1: float,
345
+ v2: float,
346
+ threshold: float,
347
+ sample_period: float,
348
+ ) -> float:
349
+ """Linearly interpolate threshold crossing time.
350
+
351
+ Args:
352
+ v1: Voltage at sample before crossing.
353
+ v2: Voltage at sample after crossing.
354
+ threshold: Threshold level.
355
+ sample_period: Time between samples.
356
+
357
+ Returns:
358
+ Time offset from v1 to crossing point.
359
+ """
360
+ dv = v2 - v1
361
+ if abs(dv) < 1e-12:
362
+ return sample_period / 2 # Midpoint if no change
363
+
364
+ # Linear interpolation: t = (threshold - v1) / (v2 - v1) * period
365
+ t = (threshold - v1) / dv * sample_period
366
+ return max(0.0, min(sample_period, t))
367
+
368
+
369
+ def get_logic_threshold(
370
+ family: str,
371
+ threshold_type: Literal["midpoint", "VIH", "VIL"] = "midpoint",
372
+ ) -> float:
373
+ """Get threshold voltage for a logic family.
374
+
375
+ Args:
376
+ family: Logic family name (e.g., "TTL", "LVCMOS_3V3").
377
+ threshold_type: Type of threshold:
378
+ - "midpoint": Midpoint between VIL_max and VIH_min
379
+ - "VIH": Minimum input high voltage
380
+ - "VIL": Maximum input low voltage
381
+
382
+ Returns:
383
+ Threshold voltage.
384
+
385
+ Raises:
386
+ ValueError: If family or threshold_type is unknown.
387
+
388
+ Example:
389
+ >>> get_logic_threshold("TTL", "midpoint")
390
+ 1.4
391
+ """
392
+ if family not in LOGIC_FAMILIES:
393
+ available = ", ".join(LOGIC_FAMILIES.keys())
394
+ raise ValueError(f"Unknown logic family: {family}. Available: {available}")
395
+
396
+ levels = LOGIC_FAMILIES[family]
397
+
398
+ if threshold_type == "midpoint":
399
+ return (levels["VIL_max"] + levels["VIH_min"]) / 2
400
+ elif threshold_type == "VIH":
401
+ return levels["VIH_min"]
402
+ elif threshold_type == "VIL":
403
+ return levels["VIL_max"]
404
+ else:
405
+ raise ValueError(f"Unknown threshold_type: {threshold_type}")
406
+
407
+
408
+ __all__ = [
409
+ "LOGIC_FAMILIES",
410
+ "detect_edges",
411
+ "get_logic_threshold",
412
+ "to_digital",
413
+ ]