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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,526 @@
1
+ """Oscura data loaders for various file formats.
2
+
3
+ This module provides a unified load() function that auto-detects file formats
4
+ and delegates to the appropriate loader.
5
+
6
+
7
+ Example:
8
+ >>> import oscura as osc
9
+ >>> trace = osc.load("capture.wfm")
10
+ >>> print(f"Loaded {len(trace.data)} samples")
11
+
12
+ >>> # Load all channels from multi-channel file
13
+ >>> channels = osc.load_all_channels("multi_channel.wfm")
14
+ >>> for name, trace in channels.items():
15
+ ... print(f"{name}: {len(trace.data)} samples")
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ import warnings
22
+ from pathlib import Path
23
+ from typing import TYPE_CHECKING, Any
24
+
25
+ from oscura.core.exceptions import LoaderError, UnsupportedFormatError
26
+ from oscura.core.types import DigitalTrace, WaveformTrace
27
+
28
+ # Import alias modules for DSL compatibility
29
+ from oscura.loaders import (
30
+ binary,
31
+ csv,
32
+ hdf5,
33
+ )
34
+
35
+ # Import configurable binary loading functionality
36
+ from oscura.loaders.configurable import (
37
+ BitfieldDef,
38
+ BitfieldExtractor,
39
+ ConfigurablePacketLoader,
40
+ DeviceConfig,
41
+ DeviceInfo,
42
+ DeviceMapper,
43
+ HeaderFieldDef,
44
+ PacketFormatConfig,
45
+ ParsedPacket,
46
+ SampleFormatDef,
47
+ detect_source_type,
48
+ extract_channels,
49
+ load_binary_packets,
50
+ load_packets_streaming,
51
+ )
52
+ from oscura.loaders.lazy import LazyWaveformTrace, load_trace_lazy
53
+ from oscura.loaders.preprocessing import (
54
+ IdleRegion,
55
+ IdleStatistics,
56
+ IdleStats,
57
+ detect_idle_regions,
58
+ get_idle_statistics,
59
+ trim_idle,
60
+ )
61
+ from oscura.loaders.validation import (
62
+ PacketValidator,
63
+ SequenceGap,
64
+ SequenceValidation,
65
+ ValidationResult,
66
+ ValidationStats,
67
+ )
68
+
69
+ if TYPE_CHECKING:
70
+ from os import PathLike
71
+
72
+ from oscura.core.types import Trace
73
+
74
+ # Logger for debug output
75
+ logger = logging.getLogger(__name__)
76
+
77
+ # Supported format extensions mapped to loader names
78
+ SUPPORTED_FORMATS: dict[str, str] = {
79
+ ".wfm": "auto_wfm", # Auto-detect Tektronix vs Rigol
80
+ ".npz": "numpy",
81
+ ".csv": "csv",
82
+ ".h5": "hdf5",
83
+ ".hdf5": "hdf5",
84
+ ".sr": "sigrok",
85
+ ".pcap": "pcap",
86
+ ".pcapng": "pcap",
87
+ ".wav": "wav",
88
+ ".vcd": "vcd",
89
+ ".tdms": "tdms",
90
+ # Touchstone S-parameter formats
91
+ ".s1p": "touchstone",
92
+ ".s2p": "touchstone",
93
+ ".s3p": "touchstone",
94
+ ".s4p": "touchstone",
95
+ ".s5p": "touchstone",
96
+ ".s6p": "touchstone",
97
+ ".s7p": "touchstone",
98
+ ".s8p": "touchstone",
99
+ }
100
+
101
+ # File size warning threshold for lazy loading suggestion (100 MB)
102
+ LARGE_FILE_WARNING_THRESHOLD = 100 * 1024 * 1024
103
+
104
+
105
+ def load(
106
+ path: str | PathLike[str],
107
+ *,
108
+ format: str | None = None,
109
+ channel: str | int | None = None,
110
+ lazy: bool = False,
111
+ **kwargs: Any,
112
+ ) -> Trace:
113
+ """Load trace data from file with automatic format detection.
114
+
115
+ This is the primary entry point for loading oscilloscope and logic
116
+ analyzer data. The file format is auto-detected from the extension
117
+ unless explicitly specified.
118
+
119
+ Supports both analog waveforms (WaveformTrace) and digital waveforms
120
+ (DigitalTrace) from mixed-signal oscilloscopes.
121
+
122
+ Args:
123
+ path: Path to the file to load.
124
+ format: Optional format override (e.g., "tektronix", "rigol", "csv").
125
+ If not specified, format is auto-detected from file extension.
126
+ channel: Optional channel name or index for multi-channel files.
127
+ lazy: If True, use lazy loading for huge files (see load_lazy).
128
+ **kwargs: Additional arguments passed to the specific loader.
129
+
130
+ Returns:
131
+ WaveformTrace or DigitalTrace depending on the file content.
132
+
133
+ Raises:
134
+ UnsupportedFormatError: If the file format is not recognized.
135
+ FileNotFoundError: If the file does not exist.
136
+
137
+ Example:
138
+ >>> import oscura as osc
139
+ >>> trace = osc.load("oscilloscope_capture.wfm")
140
+ >>> print(f"Loaded {len(trace.data)} samples at {trace.metadata.sample_rate} Hz")
141
+
142
+ >>> # Force specific loader
143
+ >>> trace = osc.load("data.bin", format="tektronix")
144
+
145
+ >>> # Check if digital trace
146
+ >>> if isinstance(trace, DigitalTrace):
147
+ ... print("Loaded digital waveform")
148
+ """
149
+ path = Path(path)
150
+
151
+ if not path.exists():
152
+ raise FileNotFoundError(f"File not found: {path}")
153
+
154
+ # Check file size and warn for large files
155
+ file_size = path.stat().st_size
156
+ if file_size > LARGE_FILE_WARNING_THRESHOLD and not lazy:
157
+ warnings.warn(
158
+ f"File is large ({file_size / 1024 / 1024:.1f} MB). "
159
+ "Consider using lazy=True for better memory efficiency.",
160
+ stacklevel=2,
161
+ )
162
+
163
+ # Handle lazy loading request
164
+ if lazy:
165
+ return load_lazy(path, **kwargs) # type: ignore[return-value]
166
+
167
+ # Determine format
168
+ if format is not None:
169
+ loader_name = format.lower()
170
+ else:
171
+ ext = path.suffix.lower()
172
+ if ext not in SUPPORTED_FORMATS:
173
+ raise UnsupportedFormatError(
174
+ ext,
175
+ list(SUPPORTED_FORMATS.keys()),
176
+ file_path=str(path),
177
+ )
178
+ loader_name = SUPPORTED_FORMATS[ext]
179
+
180
+ # Dispatch to appropriate loader
181
+ if loader_name == "auto_wfm":
182
+ return _load_wfm_auto(path, channel=channel, **kwargs)
183
+ elif loader_name in ("tektronix", "tek"):
184
+ from oscura.loaders.tektronix import load_tektronix_wfm
185
+
186
+ return load_tektronix_wfm(path, **kwargs)
187
+ elif loader_name == "rigol":
188
+ from oscura.loaders.rigol import load_rigol_wfm
189
+
190
+ return load_rigol_wfm(path, **kwargs)
191
+ elif loader_name == "numpy":
192
+ from oscura.loaders.numpy_loader import load_npz
193
+
194
+ return load_npz(path, channel=channel, **kwargs)
195
+ elif loader_name == "csv":
196
+ from oscura.loaders.csv_loader import load_csv
197
+
198
+ return load_csv(path, **kwargs) # type: ignore[return-value]
199
+ elif loader_name == "hdf5":
200
+ from oscura.loaders.hdf5_loader import load_hdf5
201
+
202
+ return load_hdf5(path, channel=channel, **kwargs) # type: ignore[return-value]
203
+ elif loader_name == "sigrok":
204
+ from oscura.loaders.sigrok import load_sigrok
205
+
206
+ return load_sigrok(path, channel=channel, **kwargs)
207
+ elif loader_name == "vcd":
208
+ from oscura.loaders.vcd import load_vcd
209
+
210
+ return load_vcd(path, **kwargs)
211
+ elif loader_name == "pcap":
212
+ from oscura.loaders.pcap import load_pcap
213
+
214
+ return load_pcap(path, **kwargs) # type: ignore[return-value]
215
+ elif loader_name == "wav":
216
+ from oscura.loaders.wav import load_wav
217
+
218
+ return load_wav(path, channel=channel, **kwargs)
219
+ elif loader_name == "tdms":
220
+ from oscura.loaders.tdms import load_tdms
221
+
222
+ return load_tdms(path, channel=channel, **kwargs)
223
+ elif loader_name == "touchstone":
224
+ from oscura.analyzers.signal_integrity.sparams import load_touchstone
225
+
226
+ return load_touchstone(path) # type: ignore[return-value]
227
+ else:
228
+ raise UnsupportedFormatError(
229
+ loader_name,
230
+ list(SUPPORTED_FORMATS.keys()),
231
+ file_path=str(path),
232
+ )
233
+
234
+
235
+ def _load_wfm_auto(
236
+ path: Path,
237
+ *,
238
+ channel: str | int | None = None,
239
+ **kwargs: Any,
240
+ ) -> Trace:
241
+ """Auto-detect WFM format (Tektronix vs Rigol) and load.
242
+
243
+ Distinguishes between Tektronix and Rigol WFM formats by examining
244
+ the file's magic bytes.
245
+
246
+ Args:
247
+ path: Path to the .wfm file.
248
+ channel: Optional channel for multi-channel files.
249
+ **kwargs: Additional arguments for the loader.
250
+
251
+ Returns:
252
+ WaveformTrace or DigitalTrace from the detected loader.
253
+
254
+ Raises:
255
+ LoaderError: If the WFM format cannot be determined.
256
+ """
257
+ # Read first bytes to detect format
258
+ try:
259
+ with open(path, "rb") as f:
260
+ magic = f.read(32)
261
+ except OSError as e:
262
+ raise LoaderError(
263
+ "Failed to read file for format detection",
264
+ file_path=str(path),
265
+ details=str(e),
266
+ ) from e
267
+
268
+ # Tektronix WFM files typically start with specific patterns
269
+ # Rigol files have different magic bytes
270
+ # This is a simplified detection - real implementation would be more robust
271
+
272
+ # Check for Rigol signature (often starts with certain patterns)
273
+ if magic[:4] in (b"\x00\x00\x01\x00", b"RIGOL"):
274
+ from oscura.loaders.rigol import load_rigol_wfm
275
+
276
+ return load_rigol_wfm(path, **kwargs)
277
+
278
+ # Default to Tektronix
279
+ from oscura.loaders.tektronix import load_tektronix_wfm
280
+
281
+ return load_tektronix_wfm(path, **kwargs)
282
+
283
+
284
+ def load_all_channels(
285
+ path: str | PathLike[str],
286
+ *,
287
+ format: str | None = None,
288
+ ) -> dict[str, WaveformTrace | DigitalTrace]:
289
+ """Load all channels from a multi-channel waveform file.
290
+
291
+ Reads the file once and extracts all available channels (both analog
292
+ and digital). This is more efficient than loading each channel
293
+ separately when you need multiple channels.
294
+
295
+ Args:
296
+ path: Path to the multi-channel waveform file.
297
+ format: Optional format override (e.g., "tektronix", "rigol").
298
+
299
+ Returns:
300
+ Dictionary mapping channel names to traces.
301
+ Analog channels are named "ch1", "ch2", etc.
302
+ Digital channels are named "d1", "d2", etc.
303
+
304
+ Raises:
305
+ UnsupportedFormatError: If the file format is not recognized.
306
+ FileNotFoundError: If the file does not exist.
307
+
308
+ Example:
309
+ >>> import oscura as osc
310
+ >>> channels = osc.load_all_channels("multi_channel.wfm")
311
+ >>> for name, trace in channels.items():
312
+ ... print(f"{name}: {len(trace.data)} samples")
313
+ ch1: 10000 samples
314
+ ch2: 10000 samples
315
+ d1: 10000 samples
316
+
317
+ >>> # Access specific channel
318
+ >>> analog_ch1 = channels["ch1"]
319
+ >>> digital_d1 = channels["d1"]
320
+ """
321
+ path = Path(path)
322
+
323
+ if not path.exists():
324
+ raise FileNotFoundError(f"File not found: {path}")
325
+
326
+ # Determine format
327
+ if format is not None:
328
+ loader_name = format.lower()
329
+ else:
330
+ ext = path.suffix.lower()
331
+ if ext not in SUPPORTED_FORMATS:
332
+ raise UnsupportedFormatError(
333
+ ext,
334
+ list(SUPPORTED_FORMATS.keys()),
335
+ file_path=str(path),
336
+ )
337
+ loader_name = SUPPORTED_FORMATS[ext]
338
+
339
+ # Currently only supports Tektronix WFM for multi-channel loading
340
+ if loader_name in ("auto_wfm", "tektronix", "tek"):
341
+ return _load_all_channels_tektronix(path)
342
+ else:
343
+ # For other formats, try loading as single channel
344
+ trace = load(path, format=format)
345
+ channel_name = getattr(trace.metadata, "channel_name", None) or "ch1"
346
+ return {channel_name: trace} # type: ignore[dict-item]
347
+
348
+
349
+ def _load_all_channels_tektronix(
350
+ path: Path,
351
+ ) -> dict[str, WaveformTrace | DigitalTrace]:
352
+ """Load all channels from a Tektronix WFM file.
353
+
354
+ Args:
355
+ path: Path to the Tektronix .wfm file.
356
+
357
+ Returns:
358
+ Dictionary mapping channel names to traces.
359
+
360
+ Raises:
361
+ LoaderError: If the file cannot be read or parsed.
362
+ """
363
+ try:
364
+ import tm_data_types # type: ignore[import-not-found, import-untyped]
365
+ except ImportError:
366
+ # Fall back to single channel loading
367
+ trace = load(path, format="tektronix")
368
+ channel_name = getattr(trace.metadata, "channel_name", None) or "ch1"
369
+ return {channel_name: trace} # type: ignore[dict-item]
370
+
371
+ try:
372
+ wfm = tm_data_types.read_file(str(path))
373
+ except Exception as e:
374
+ raise LoaderError(
375
+ "Failed to read Tektronix WFM file",
376
+ file_path=str(path),
377
+ details=str(e),
378
+ ) from e
379
+
380
+ channels: dict[str, WaveformTrace | DigitalTrace] = {}
381
+
382
+ # Extract analog waveforms
383
+ if hasattr(wfm, "analog_waveforms") and wfm.analog_waveforms:
384
+ import numpy as np
385
+
386
+ from oscura.loaders.tektronix import _build_waveform_trace
387
+
388
+ for i, awfm in enumerate(wfm.analog_waveforms):
389
+ try:
390
+ data = np.array(awfm.y_data, dtype=np.float64)
391
+ x_increment = getattr(awfm, "x_increment", 1e-6)
392
+ sample_rate = 1.0 / x_increment if x_increment > 0 else 1e6
393
+ vertical_scale = getattr(awfm, "y_scale", None)
394
+ vertical_offset = getattr(awfm, "y_offset", None)
395
+ channel_name = getattr(awfm, "name", f"CH{i + 1}")
396
+
397
+ trace = _build_waveform_trace(
398
+ data=data,
399
+ sample_rate=sample_rate,
400
+ vertical_scale=vertical_scale,
401
+ vertical_offset=vertical_offset,
402
+ channel_name=channel_name,
403
+ path=path,
404
+ wfm=awfm,
405
+ )
406
+ channels[f"ch{i + 1}"] = trace
407
+ except Exception as e:
408
+ logger.warning("Failed to extract analog channel %d: %s", i + 1, e)
409
+
410
+ # Extract digital waveforms
411
+ if hasattr(wfm, "digital_waveforms") and wfm.digital_waveforms:
412
+ from oscura.loaders.tektronix import _load_digital_waveform
413
+
414
+ for i, dwfm in enumerate(wfm.digital_waveforms):
415
+ try:
416
+ trace = _load_digital_waveform(dwfm, path, i)
417
+ channels[f"d{i + 1}"] = trace
418
+ except Exception as e:
419
+ logger.warning("Failed to extract digital channel %d: %s", i + 1, e)
420
+
421
+ # Handle direct waveform formats (single file = single channel)
422
+ if not channels:
423
+ wfm_type = type(wfm).__name__
424
+
425
+ if wfm_type == "DigitalWaveform" or hasattr(wfm, "y_axis_byte_values"):
426
+ from oscura.loaders.tektronix import _load_digital_waveform
427
+
428
+ trace = _load_digital_waveform(wfm, path, 0)
429
+ channel_name = trace.metadata.channel_name or "d1"
430
+ channels[channel_name.lower()] = trace
431
+
432
+ elif hasattr(wfm, "y_axis_values") or hasattr(wfm, "y_data"):
433
+ # Direct analog waveform
434
+ trace = load(path, format="tektronix")
435
+ channel_name = trace.metadata.channel_name or "ch1"
436
+ channels[channel_name.lower()] = trace # type: ignore[assignment]
437
+
438
+ if not channels:
439
+ raise LoaderError(
440
+ "No channels found in file",
441
+ file_path=str(path),
442
+ fix_hint="File may be empty or use an unsupported format variant.",
443
+ )
444
+
445
+ return channels
446
+
447
+
448
+ def get_supported_formats() -> list[str]:
449
+ """Get list of supported file formats.
450
+
451
+ Returns:
452
+ List of supported file extensions.
453
+
454
+ Example:
455
+ >>> from oscura.loaders import get_supported_formats
456
+ >>> print(get_supported_formats())
457
+ ['.wfm', '.npz', '.csv', '.h5', ...]
458
+ """
459
+ return list(SUPPORTED_FORMATS.keys())
460
+
461
+
462
+ def load_lazy(path: str | PathLike[str], **kwargs: Any) -> LazyWaveformTrace | WaveformTrace:
463
+ """Load trace with lazy loading for huge files.
464
+
465
+ Convenience wrapper for lazy loading. See load_trace_lazy for details.
466
+
467
+ Args:
468
+ path: Path to the file.
469
+ **kwargs: Additional arguments (sample_rate, lazy=True, etc.).
470
+
471
+ Returns:
472
+ LazyWaveformTrace or WaveformTrace.
473
+
474
+ Example:
475
+ >>> trace = osc.loaders.load_lazy("huge_trace.npy", sample_rate=1e9)
476
+ >>> print(f"Length: {trace.length}") # Metadata available immediately
477
+
478
+ References:
479
+ API-017: Lazy Loading for Huge Files
480
+ """
481
+ from oscura.loaders.lazy import load_trace_lazy
482
+
483
+ return load_trace_lazy(path, **kwargs) # type: ignore[arg-type]
484
+
485
+
486
+ __all__ = [
487
+ "LARGE_FILE_WARNING_THRESHOLD",
488
+ "SUPPORTED_FORMATS",
489
+ # Configurable binary loading
490
+ "BitfieldDef",
491
+ "BitfieldExtractor",
492
+ "ConfigurablePacketLoader",
493
+ "DeviceConfig",
494
+ "DeviceInfo",
495
+ "DeviceMapper",
496
+ "DigitalTrace",
497
+ "HeaderFieldDef",
498
+ "IdleRegion",
499
+ "IdleStatistics",
500
+ "IdleStats",
501
+ "LazyWaveformTrace",
502
+ "PacketFormatConfig",
503
+ "PacketValidator",
504
+ "ParsedPacket",
505
+ "SampleFormatDef",
506
+ "SequenceGap",
507
+ "SequenceValidation",
508
+ "ValidationResult",
509
+ "ValidationStats",
510
+ "WaveformTrace",
511
+ "binary",
512
+ "csv",
513
+ "detect_idle_regions",
514
+ "detect_source_type",
515
+ "extract_channels",
516
+ "get_idle_statistics",
517
+ "get_supported_formats",
518
+ "hdf5",
519
+ "load",
520
+ "load_all_channels",
521
+ "load_binary_packets",
522
+ "load_lazy",
523
+ "load_packets_streaming",
524
+ "load_trace_lazy",
525
+ "trim_idle",
526
+ ]
@@ -0,0 +1,69 @@
1
+ """Binary file loader for raw signal data.
2
+
3
+ Loads raw binary files containing signal data with user-specified format.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ import numpy as np
12
+
13
+ from oscura.core.types import TraceMetadata, WaveformTrace
14
+
15
+ if TYPE_CHECKING:
16
+ from os import PathLike
17
+
18
+
19
+ def load_binary(
20
+ path: str | PathLike[str],
21
+ *,
22
+ dtype: str | np.dtype[Any] = "float64",
23
+ sample_rate: float = 1.0,
24
+ channels: int = 1,
25
+ channel: int = 0,
26
+ offset: int = 0,
27
+ count: int = -1,
28
+ ) -> WaveformTrace:
29
+ """Load raw binary file as waveform trace.
30
+
31
+ Args:
32
+ path: Path to the binary file.
33
+ dtype: NumPy dtype for the data (default: float64).
34
+ sample_rate: Sample rate in Hz.
35
+ channels: Number of interleaved channels.
36
+ channel: Channel index to load (0-based).
37
+ offset: Number of samples to skip from start.
38
+ count: Number of samples to read (-1 for all).
39
+
40
+ Returns:
41
+ WaveformTrace containing the loaded data.
42
+
43
+ Example:
44
+ >>> from oscura.loaders.binary import load_binary
45
+ >>> trace = load_binary("signal.bin", dtype="int16", sample_rate=1e6)
46
+ """
47
+ path = Path(path)
48
+
49
+ # Load raw data
50
+ data = np.fromfile(path, dtype=dtype, count=count, offset=offset * np.dtype(dtype).itemsize)
51
+
52
+ # Handle multi-channel data
53
+ if channels > 1:
54
+ # Reshape and select channel
55
+ samples_per_channel = len(data) // channels
56
+ data = data[: samples_per_channel * channels].reshape(-1, channels)
57
+ data = data[:, channel]
58
+
59
+ # Create metadata
60
+ metadata = TraceMetadata(
61
+ sample_rate=sample_rate,
62
+ source_file=str(path),
63
+ channel_name=f"Channel {channel}",
64
+ )
65
+
66
+ return WaveformTrace(data=data.astype(np.float64), metadata=metadata)
67
+
68
+
69
+ __all__ = ["load_binary"]