oscura 0.0.1__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
oscura/core/types.py ADDED
@@ -0,0 +1,504 @@
1
+ """Core data types for Oscura signal analysis framework.
2
+
3
+ This module implements the fundamental data structures for oscilloscope
4
+ and logic analyzer data analysis.
5
+
6
+ Requirements addressed:
7
+ - CORE-001: TraceMetadata Data Class
8
+ - CORE-002: WaveformTrace Data Class
9
+ - CORE-003: DigitalTrace Data Class
10
+ - CORE-004: ProtocolPacket Data Class
11
+ - CORE-005: CalibrationInfo Data Class (regulatory compliance)
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass, field
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ import numpy as np
20
+
21
+ if TYPE_CHECKING:
22
+ from datetime import datetime
23
+
24
+ from numpy.typing import NDArray
25
+
26
+
27
+ @dataclass
28
+ class CalibrationInfo:
29
+ """Calibration and instrument provenance information.
30
+
31
+ Stores traceability metadata for measurements performed on oscilloscope
32
+ or logic analyzer data. Essential for regulatory compliance and quality
33
+ assurance in DOD/aerospace/medical applications.
34
+
35
+ Attributes:
36
+ instrument: Instrument make and model (e.g., "Tektronix DPO7254C").
37
+ serial_number: Instrument serial number for traceability (optional).
38
+ calibration_date: Date of last calibration (optional).
39
+ calibration_due_date: Date when next calibration is due (optional).
40
+ firmware_version: Instrument firmware version (optional).
41
+ calibration_lab: Calibration lab name or accreditation (optional).
42
+ calibration_cert_number: Calibration certificate number (optional).
43
+ probe_attenuation: Probe attenuation factor (e.g., 10.0 for 10x probe) (optional).
44
+ coupling: Input coupling ("DC", "AC", "GND") (optional).
45
+ bandwidth_limit: Bandwidth limit in Hz, None if disabled (optional).
46
+ vertical_resolution: ADC resolution in bits (optional).
47
+
48
+ Example:
49
+ >>> from datetime import datetime
50
+ >>> cal_info = CalibrationInfo(
51
+ ... instrument="Tektronix DPO7254C",
52
+ ... serial_number="C012345",
53
+ ... calibration_date=datetime(2024, 12, 15),
54
+ ... probe_attenuation=10.0,
55
+ ... vertical_resolution=8
56
+ ... )
57
+ >>> print(f"Instrument: {cal_info.instrument}")
58
+ Instrument: Tektronix DPO7254C
59
+
60
+ References:
61
+ ISO/IEC 17025: General Requirements for Testing/Calibration Laboratories
62
+ NIST Handbook 150: Laboratory Accreditation Program Requirements
63
+ 21 CFR Part 11: Electronic Records (FDA)
64
+ """
65
+
66
+ instrument: str
67
+ serial_number: str | None = None
68
+ calibration_date: datetime | None = None
69
+ calibration_due_date: datetime | None = None
70
+ firmware_version: str | None = None
71
+ calibration_lab: str | None = None
72
+ calibration_cert_number: str | None = None
73
+ probe_attenuation: float | None = None
74
+ coupling: str | None = None
75
+ bandwidth_limit: float | None = None
76
+ vertical_resolution: int | None = None
77
+
78
+ def __post_init__(self) -> None:
79
+ """Validate calibration info after initialization."""
80
+ if self.probe_attenuation is not None and self.probe_attenuation <= 0:
81
+ raise ValueError(f"probe_attenuation must be positive, got {self.probe_attenuation}")
82
+ if self.bandwidth_limit is not None and self.bandwidth_limit <= 0:
83
+ raise ValueError(f"bandwidth_limit must be positive, got {self.bandwidth_limit}")
84
+ if self.vertical_resolution is not None and self.vertical_resolution <= 0:
85
+ raise ValueError(
86
+ f"vertical_resolution must be positive, got {self.vertical_resolution}"
87
+ )
88
+
89
+ @property
90
+ def is_calibration_current(self) -> bool | None:
91
+ """Check if calibration is current.
92
+
93
+ Returns:
94
+ True if calibration is current, False if expired, None if dates not set.
95
+ """
96
+ if self.calibration_date is None or self.calibration_due_date is None:
97
+ return None
98
+ from datetime import datetime
99
+
100
+ return datetime.now() < self.calibration_due_date
101
+
102
+ @property
103
+ def traceability_summary(self) -> str:
104
+ """Generate a traceability summary string.
105
+
106
+ Returns:
107
+ Human-readable summary of calibration traceability.
108
+ """
109
+ parts = [f"Instrument: {self.instrument}"]
110
+ if self.serial_number:
111
+ parts.append(f"S/N: {self.serial_number}")
112
+ if self.calibration_date:
113
+ parts.append(f"Cal Date: {self.calibration_date.strftime('%Y-%m-%d')}")
114
+ if self.calibration_due_date:
115
+ parts.append(f"Due: {self.calibration_due_date.strftime('%Y-%m-%d')}")
116
+ if self.calibration_cert_number:
117
+ parts.append(f"Cert: {self.calibration_cert_number}")
118
+ return ", ".join(parts)
119
+
120
+
121
+ @dataclass
122
+ class TraceMetadata:
123
+ """Metadata describing a captured trace.
124
+
125
+ Contains sample rate, scaling information, acquisition details,
126
+ and provenance information for a captured waveform or digital trace.
127
+
128
+ Attributes:
129
+ sample_rate: Sample rate in Hz (required).
130
+ vertical_scale: Vertical scale in volts/division (optional).
131
+ vertical_offset: Vertical offset in volts (optional).
132
+ acquisition_time: Time of acquisition (optional).
133
+ trigger_info: Trigger configuration dictionary (optional).
134
+ source_file: Path to source file (optional).
135
+ channel_name: Name of the channel (optional).
136
+ calibration_info: Calibration and instrument traceability information (optional).
137
+
138
+ Example:
139
+ >>> metadata = TraceMetadata(sample_rate=1e9) # 1 GSa/s
140
+ >>> print(f"Time base: {metadata.time_base} s/sample")
141
+ Time base: 1e-09 s/sample
142
+
143
+ Example with calibration info:
144
+ >>> from datetime import datetime
145
+ >>> cal = CalibrationInfo(
146
+ ... instrument="Tektronix DPO7254C",
147
+ ... calibration_date=datetime(2024, 12, 15)
148
+ ... )
149
+ >>> metadata = TraceMetadata(sample_rate=1e9, calibration_info=cal)
150
+ >>> print(metadata.calibration_info.traceability_summary)
151
+ Instrument: Tektronix DPO7254C, Cal Date: 2024-12-15
152
+
153
+ References:
154
+ IEEE 181-2011: Standard for Transitional Waveform Definitions
155
+ ISO/IEC 17025: General Requirements for Testing/Calibration Laboratories
156
+ """
157
+
158
+ sample_rate: float
159
+ vertical_scale: float | None = None
160
+ vertical_offset: float | None = None
161
+ acquisition_time: datetime | None = None
162
+ trigger_info: dict[str, Any] | None = None
163
+ source_file: str | None = None
164
+ channel_name: str | None = None
165
+ calibration_info: CalibrationInfo | None = None
166
+
167
+ def __post_init__(self) -> None:
168
+ """Validate metadata after initialization."""
169
+ if self.sample_rate <= 0:
170
+ raise ValueError(f"sample_rate must be positive, got {self.sample_rate}")
171
+
172
+ @property
173
+ def time_base(self) -> float:
174
+ """Time between samples in seconds (derived from sample_rate).
175
+
176
+ Returns:
177
+ Time per sample in seconds (1 / sample_rate).
178
+ """
179
+ return 1.0 / self.sample_rate
180
+
181
+
182
+ @dataclass
183
+ class WaveformTrace:
184
+ """Analog waveform data with metadata.
185
+
186
+ Stores sampled analog voltage data as a numpy array along with
187
+ associated metadata for timing and scaling.
188
+
189
+ Attributes:
190
+ data: Waveform samples as numpy float array.
191
+ metadata: Associated trace metadata.
192
+
193
+ Example:
194
+ >>> import numpy as np
195
+ >>> data = np.sin(2 * np.pi * 1e6 * np.linspace(0, 1e-3, 1000))
196
+ >>> trace = WaveformTrace(data=data, metadata=TraceMetadata(sample_rate=1e6))
197
+ >>> print(f"Duration: {trace.time_vector[-1]:.6f} seconds")
198
+ Duration: 0.000999 seconds
199
+
200
+ References:
201
+ IEEE 1241-2010: Standard for Terminology and Test Methods for ADCs
202
+ """
203
+
204
+ data: NDArray[np.floating[Any]]
205
+ metadata: TraceMetadata
206
+
207
+ def __post_init__(self) -> None:
208
+ """Validate waveform data after initialization."""
209
+ if not isinstance(self.data, np.ndarray):
210
+ raise TypeError(f"data must be a numpy array, got {type(self.data).__name__}")
211
+ if not np.issubdtype(self.data.dtype, np.floating):
212
+ # Convert to float64 if not already floating point
213
+ self.data = self.data.astype(np.float64)
214
+
215
+ @property
216
+ def time_vector(self) -> NDArray[np.float64]:
217
+ """Time axis in seconds.
218
+
219
+ Computes a time vector starting from 0, with intervals
220
+ determined by the sample rate.
221
+
222
+ Returns:
223
+ Array of time values in seconds, same length as data.
224
+ """
225
+ n_samples = len(self.data)
226
+ return np.arange(n_samples, dtype=np.float64) * self.metadata.time_base
227
+
228
+ @property
229
+ def duration(self) -> float:
230
+ """Total duration of the trace in seconds.
231
+
232
+ Returns:
233
+ Duration from first to last sample in seconds.
234
+ """
235
+ if len(self.data) == 0:
236
+ return 0.0
237
+ return (len(self.data) - 1) * self.metadata.time_base
238
+
239
+ def __len__(self) -> int:
240
+ """Return number of samples in the trace."""
241
+ return len(self.data)
242
+
243
+
244
+ @dataclass
245
+ class DigitalTrace:
246
+ """Digital/logic signal data with metadata.
247
+
248
+ Stores sampled digital signal data as a boolean numpy array,
249
+ with optional edge timestamp information.
250
+
251
+ Attributes:
252
+ data: Digital samples as numpy boolean array.
253
+ metadata: Associated trace metadata.
254
+ edges: Optional list of (timestamp, is_rising) tuples.
255
+
256
+ Example:
257
+ >>> import numpy as np
258
+ >>> data = np.array([False, False, True, True, False], dtype=bool)
259
+ >>> trace = DigitalTrace(data=data, metadata=TraceMetadata(sample_rate=1e6))
260
+ >>> print(f"High samples: {np.sum(trace.data)}")
261
+ High samples: 2
262
+
263
+ References:
264
+ IEEE 1076.6-2004: Standard for VHDL Register Transfer Level Synthesis
265
+ """
266
+
267
+ data: NDArray[np.bool_]
268
+ metadata: TraceMetadata
269
+ edges: list[tuple[float, bool]] | None = None
270
+
271
+ def __post_init__(self) -> None:
272
+ """Validate digital data after initialization."""
273
+ if not isinstance(self.data, np.ndarray):
274
+ raise TypeError(f"data must be a numpy array, got {type(self.data).__name__}")
275
+ if self.data.dtype != np.bool_:
276
+ # Convert to boolean if not already
277
+ self.data = self.data.astype(np.bool_)
278
+
279
+ @property
280
+ def time_vector(self) -> NDArray[np.float64]:
281
+ """Time axis in seconds.
282
+
283
+ Returns:
284
+ Array of time values in seconds, same length as data.
285
+ """
286
+ n_samples = len(self.data)
287
+ return np.arange(n_samples, dtype=np.float64) * self.metadata.time_base
288
+
289
+ @property
290
+ def duration(self) -> float:
291
+ """Total duration of the trace in seconds.
292
+
293
+ Returns:
294
+ Duration from first to last sample in seconds.
295
+ """
296
+ if len(self.data) == 0:
297
+ return 0.0
298
+ return (len(self.data) - 1) * self.metadata.time_base
299
+
300
+ @property
301
+ def rising_edges(self) -> list[float]:
302
+ """Timestamps of rising edges.
303
+
304
+ Returns:
305
+ List of timestamps where signal transitions from low to high.
306
+ """
307
+ if self.edges is None:
308
+ return []
309
+ return [ts for ts, is_rising in self.edges if is_rising]
310
+
311
+ @property
312
+ def falling_edges(self) -> list[float]:
313
+ """Timestamps of falling edges.
314
+
315
+ Returns:
316
+ List of timestamps where signal transitions from high to low.
317
+ """
318
+ if self.edges is None:
319
+ return []
320
+ return [ts for ts, is_rising in self.edges if not is_rising]
321
+
322
+ def __len__(self) -> int:
323
+ """Return number of samples in the trace."""
324
+ return len(self.data)
325
+
326
+
327
+ @dataclass
328
+ class IQTrace:
329
+ """I/Q (In-phase/Quadrature) waveform data with metadata.
330
+
331
+ Stores complex-valued signal data as separate I and Q components,
332
+ commonly used for RF and software-defined radio applications.
333
+
334
+ Attributes:
335
+ i_data: In-phase component samples as numpy float array.
336
+ q_data: Quadrature component samples as numpy float array.
337
+ metadata: Associated trace metadata.
338
+
339
+ Example:
340
+ >>> import numpy as np
341
+ >>> t = np.linspace(0, 1e-3, 1000)
342
+ >>> i_data = np.cos(2 * np.pi * 1e6 * t)
343
+ >>> q_data = np.sin(2 * np.pi * 1e6 * t)
344
+ >>> trace = IQTrace(i_data=i_data, q_data=q_data, metadata=TraceMetadata(sample_rate=1e6))
345
+ >>> print(f"Complex samples: {len(trace)}")
346
+ Complex samples: 1000
347
+
348
+ References:
349
+ IEEE Std 181-2011: Transitional Waveform Definitions
350
+ """
351
+
352
+ i_data: NDArray[np.floating[Any]]
353
+ q_data: NDArray[np.floating[Any]]
354
+ metadata: TraceMetadata
355
+
356
+ def __post_init__(self) -> None:
357
+ """Validate I/Q data after initialization."""
358
+ if not isinstance(self.i_data, np.ndarray):
359
+ raise TypeError(f"i_data must be a numpy array, got {type(self.i_data).__name__}")
360
+ if not isinstance(self.q_data, np.ndarray):
361
+ raise TypeError(f"q_data must be a numpy array, got {type(self.q_data).__name__}")
362
+ if len(self.i_data) != len(self.q_data):
363
+ raise ValueError(
364
+ f"I and Q data must have same length, got {len(self.i_data)} and {len(self.q_data)}"
365
+ )
366
+ # Convert to float64 if not already floating point
367
+ if not np.issubdtype(self.i_data.dtype, np.floating):
368
+ self.i_data = self.i_data.astype(np.float64)
369
+ if not np.issubdtype(self.q_data.dtype, np.floating):
370
+ self.q_data = self.q_data.astype(np.float64)
371
+
372
+ @property
373
+ def complex_data(self) -> NDArray[np.complex128]:
374
+ """Return I/Q data as complex array.
375
+
376
+ Returns:
377
+ Complex array where real=I, imag=Q.
378
+ """
379
+ return self.i_data + 1j * self.q_data
380
+
381
+ @property
382
+ def magnitude(self) -> NDArray[np.float64]:
383
+ """Magnitude (amplitude) of the complex signal.
384
+
385
+ Returns:
386
+ Array of magnitude values sqrt(I² + Q²).
387
+ """
388
+ return np.sqrt(self.i_data**2 + self.q_data**2)
389
+
390
+ @property
391
+ def phase(self) -> NDArray[np.float64]:
392
+ """Phase angle of the complex signal in radians.
393
+
394
+ Returns:
395
+ Array of phase values atan2(Q, I).
396
+ """
397
+ return np.arctan2(self.q_data, self.i_data)
398
+
399
+ @property
400
+ def time_vector(self) -> NDArray[np.float64]:
401
+ """Time axis in seconds.
402
+
403
+ Returns:
404
+ Array of time values in seconds, same length as data.
405
+ """
406
+ n_samples = len(self.i_data)
407
+ return np.arange(n_samples, dtype=np.float64) * self.metadata.time_base
408
+
409
+ @property
410
+ def duration(self) -> float:
411
+ """Total duration of the trace in seconds.
412
+
413
+ Returns:
414
+ Duration from first to last sample in seconds.
415
+ """
416
+ if len(self.i_data) == 0:
417
+ return 0.0
418
+ return (len(self.i_data) - 1) * self.metadata.time_base
419
+
420
+ def __len__(self) -> int:
421
+ """Return number of samples in the trace."""
422
+ return len(self.i_data)
423
+
424
+
425
+ @dataclass
426
+ class ProtocolPacket:
427
+ """Decoded protocol packet data.
428
+
429
+ Represents a decoded packet from a serial protocol (UART, SPI, I2C, etc.)
430
+ with timing, data content, annotations, and error information.
431
+
432
+ Attributes:
433
+ timestamp: Start time of the packet in seconds.
434
+ protocol: Name of the protocol (e.g., "UART", "SPI", "I2C").
435
+ data: Decoded data bytes.
436
+ annotations: Multi-level annotations dictionary (optional).
437
+ errors: List of detected errors (optional).
438
+ end_timestamp: End time of the packet in seconds (optional).
439
+
440
+ Example:
441
+ >>> packet = ProtocolPacket(
442
+ ... timestamp=1.23e-3,
443
+ ... protocol="UART",
444
+ ... data=b"Hello"
445
+ ... )
446
+ >>> print(f"Received at {packet.timestamp}s: {packet.data.decode()}")
447
+ Received at 0.00123s: Hello
448
+
449
+ References:
450
+ sigrok Protocol Decoder API
451
+ """
452
+
453
+ timestamp: float
454
+ protocol: str
455
+ data: bytes
456
+ annotations: dict[str, Any] = field(default_factory=dict)
457
+ errors: list[str] = field(default_factory=list)
458
+ end_timestamp: float | None = None
459
+
460
+ def __post_init__(self) -> None:
461
+ """Validate packet data after initialization."""
462
+ if self.timestamp < 0:
463
+ raise ValueError(f"timestamp must be non-negative, got {self.timestamp}")
464
+ if not isinstance(self.data, bytes):
465
+ raise TypeError(f"data must be bytes, got {type(self.data).__name__}")
466
+
467
+ @property
468
+ def duration(self) -> float | None:
469
+ """Duration of the packet in seconds.
470
+
471
+ Returns:
472
+ Duration if end_timestamp is set, None otherwise.
473
+ """
474
+ if self.end_timestamp is None:
475
+ return None
476
+ return self.end_timestamp - self.timestamp
477
+
478
+ @property
479
+ def has_errors(self) -> bool:
480
+ """Check if packet has any errors.
481
+
482
+ Returns:
483
+ True if errors list is non-empty.
484
+ """
485
+ return len(self.errors) > 0
486
+
487
+ def __len__(self) -> int:
488
+ """Return number of bytes in the packet."""
489
+ return len(self.data)
490
+
491
+
492
+ # Type aliases for convenience
493
+ Trace = WaveformTrace | DigitalTrace | IQTrace
494
+ """Union type for any trace type."""
495
+
496
+ __all__ = [
497
+ "CalibrationInfo",
498
+ "DigitalTrace",
499
+ "IQTrace",
500
+ "ProtocolPacket",
501
+ "Trace",
502
+ "TraceMetadata",
503
+ "WaveformTrace",
504
+ ]