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/loaders/csv.py ADDED
@@ -0,0 +1,26 @@
1
+ """Alias module for CSV loader - provides expected import path.
2
+
3
+ Why two CSV loader files exist:
4
+ - `oscura/loaders/csv_loader.py`: Canonical implementation with full CSV
5
+ parsing logic, format detection, and validation
6
+ - `oscura/loaders/csv.py` (this file): Convenience re-export to support
7
+ natural import syntax like `from oscura.loaders.csv import load_csv`
8
+
9
+ The name `csv_loader.py` avoids shadowing Python's built-in `csv` module
10
+ within the implementation file. This alias module provides a cleaner import
11
+ path for external users.
12
+
13
+ Usage patterns:
14
+ # Recommended - explicit module name
15
+ from oscura.loaders.csv_loader import load_csv
16
+
17
+ # Also supported - natural import path via this alias
18
+ from oscura.loaders.csv import load_csv
19
+
20
+ # Via loaders package __init__
21
+ from oscura.loaders import load_csv
22
+ """
23
+
24
+ from oscura.loaders.csv_loader import load_csv
25
+
26
+ __all__ = ["load_csv"]
@@ -0,0 +1,473 @@
1
+ """CSV file loader for waveform data.
2
+
3
+ This module provides loading of waveform data from CSV files with
4
+ automatic header detection and column mapping.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.loaders.csv_loader import load_csv
9
+ >>> trace = load_csv("oscilloscope_export.csv")
10
+ >>> print(f"Sample rate: {trace.metadata.sample_rate} Hz")
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import csv
16
+ from io import StringIO
17
+ from pathlib import Path
18
+ from typing import TYPE_CHECKING, Any
19
+
20
+ import numpy as np
21
+
22
+ from oscura.core.exceptions import FormatError, LoaderError
23
+ from oscura.core.types import TraceMetadata, WaveformTrace
24
+
25
+ if TYPE_CHECKING:
26
+ from os import PathLike
27
+
28
+ # Try to import pandas for better CSV handling
29
+ try:
30
+ import pandas as pd
31
+
32
+ PANDAS_AVAILABLE = True
33
+ except ImportError:
34
+ PANDAS_AVAILABLE = False
35
+
36
+
37
+ # Common column names for time data
38
+ TIME_COLUMN_NAMES = [
39
+ "time",
40
+ "t",
41
+ "time_s",
42
+ "time_sec",
43
+ "seconds",
44
+ "timestamp",
45
+ "x",
46
+ "Time",
47
+ "TIME",
48
+ ]
49
+
50
+ # Common column names for voltage data
51
+ VOLTAGE_COLUMN_NAMES = [
52
+ "voltage",
53
+ "v",
54
+ "volt",
55
+ "volts",
56
+ "amplitude",
57
+ "signal",
58
+ "y",
59
+ "value",
60
+ "data",
61
+ "ch1",
62
+ "ch2",
63
+ "ch3",
64
+ "ch4",
65
+ "channel1",
66
+ "channel2",
67
+ "Voltage",
68
+ "VOLTAGE",
69
+ ]
70
+
71
+
72
+ def load_csv(
73
+ path: str | PathLike[str],
74
+ *,
75
+ time_column: str | int | None = None,
76
+ voltage_column: str | int | None = None,
77
+ sample_rate: float | None = None,
78
+ delimiter: str | None = None,
79
+ skip_rows: int = 0,
80
+ encoding: str = "utf-8",
81
+ mmap: bool = False,
82
+ ) -> WaveformTrace | Any:
83
+ """Load waveform data from a CSV file.
84
+
85
+ Parses CSV files exported from oscilloscopes or other data sources.
86
+ Automatically detects header rows and maps columns for time and
87
+ voltage data.
88
+
89
+ Args:
90
+ path: Path to the CSV file.
91
+ time_column: Name or index of time column. If None, auto-detects.
92
+ voltage_column: Name or index of voltage column. If None, auto-detects.
93
+ sample_rate: Override sample rate. If None, computed from time column.
94
+ delimiter: Column delimiter. If None, auto-detects.
95
+ skip_rows: Number of rows to skip before header.
96
+ encoding: File encoding (default: utf-8).
97
+ mmap: If True, return memory-mapped trace for large files.
98
+
99
+ Returns:
100
+ WaveformTrace containing the waveform data and metadata.
101
+ If mmap=True, returns MmapWaveformTrace instead.
102
+
103
+ Raises:
104
+ LoaderError: If the file cannot be loaded.
105
+
106
+ Example:
107
+ >>> trace = load_csv("oscilloscope.csv")
108
+ >>> print(f"Sample rate: {trace.metadata.sample_rate} Hz")
109
+
110
+ >>> # Specify columns explicitly
111
+ >>> trace = load_csv("data.csv", time_column="Time", voltage_column="CH1")
112
+
113
+ >>> # Load as memory-mapped for large files
114
+ >>> trace = load_csv("huge_capture.csv", mmap=True)
115
+ """
116
+ path = Path(path)
117
+
118
+ if not path.exists():
119
+ raise LoaderError(
120
+ "File not found",
121
+ file_path=str(path),
122
+ )
123
+
124
+ if PANDAS_AVAILABLE:
125
+ trace = _load_with_pandas(
126
+ path,
127
+ time_column=time_column,
128
+ voltage_column=voltage_column,
129
+ sample_rate=sample_rate,
130
+ delimiter=delimiter,
131
+ skip_rows=skip_rows,
132
+ encoding=encoding,
133
+ )
134
+ else:
135
+ trace = _load_basic(
136
+ path,
137
+ time_column=time_column,
138
+ voltage_column=voltage_column,
139
+ sample_rate=sample_rate,
140
+ delimiter=delimiter,
141
+ skip_rows=skip_rows,
142
+ encoding=encoding,
143
+ )
144
+
145
+ # Convert to memory-mapped if requested
146
+ if mmap:
147
+ import tempfile
148
+
149
+ from oscura.loaders.mmap_loader import load_mmap
150
+
151
+ # Save data to temporary .npy file for memory mapping
152
+ with tempfile.NamedTemporaryFile(suffix=".npy", delete=False) as tmp:
153
+ tmp_path = Path(tmp.name)
154
+
155
+ np.save(tmp_path, trace.data)
156
+
157
+ # Load as memory-mapped trace
158
+ return load_mmap(
159
+ tmp_path,
160
+ sample_rate=trace.metadata.sample_rate,
161
+ )
162
+
163
+ return trace
164
+
165
+
166
+ def _load_with_pandas(
167
+ path: Path,
168
+ *,
169
+ time_column: str | int | None,
170
+ voltage_column: str | int | None,
171
+ sample_rate: float | None,
172
+ delimiter: str | None,
173
+ skip_rows: int,
174
+ encoding: str,
175
+ ) -> WaveformTrace:
176
+ """Load CSV using pandas for better parsing."""
177
+ try:
178
+ # Auto-detect delimiter if not specified
179
+ if delimiter is None:
180
+ delimiter = _detect_delimiter(path, encoding)
181
+
182
+ # Read CSV with pandas
183
+ df = pd.read_csv(
184
+ path,
185
+ delimiter=delimiter,
186
+ skiprows=skip_rows,
187
+ encoding=encoding,
188
+ engine="python", # More flexible parsing
189
+ )
190
+
191
+ if df.empty:
192
+ raise FormatError(
193
+ "CSV file is empty",
194
+ file_path=str(path),
195
+ )
196
+
197
+ # Find time column
198
+ time_data = None
199
+ time_col_name = None
200
+
201
+ if time_column is not None:
202
+ if isinstance(time_column, int):
203
+ if time_column < len(df.columns):
204
+ time_col_name = df.columns[time_column]
205
+ time_data = df.iloc[:, time_column].values
206
+ elif time_column in df.columns:
207
+ time_col_name = time_column
208
+ time_data = df[time_column].values
209
+ else:
210
+ # Auto-detect time column
211
+ for col in df.columns:
212
+ col_lower = col.lower().strip()
213
+ if col_lower in [n.lower() for n in TIME_COLUMN_NAMES]:
214
+ time_col_name = col
215
+ time_data = df[col].values
216
+ break
217
+
218
+ # Find voltage column
219
+ voltage_data = None
220
+ voltage_col_name = None
221
+
222
+ if voltage_column is not None:
223
+ if isinstance(voltage_column, int):
224
+ if voltage_column < len(df.columns):
225
+ voltage_col_name = df.columns[voltage_column]
226
+ voltage_data = df.iloc[:, voltage_column].values
227
+ elif voltage_column in df.columns:
228
+ voltage_col_name = voltage_column
229
+ voltage_data = df[voltage_column].values
230
+ else:
231
+ # Auto-detect voltage column (first non-time numeric column)
232
+ for col in df.columns:
233
+ if col == time_col_name:
234
+ continue
235
+ col_lower = col.lower().strip()
236
+ # Check if numeric
237
+ if pd.api.types.is_numeric_dtype(df[col]):
238
+ # Prefer columns with voltage-like names
239
+ if col_lower in [n.lower() for n in VOLTAGE_COLUMN_NAMES]:
240
+ voltage_col_name = col
241
+ voltage_data = df[col].values
242
+ break
243
+ elif voltage_data is None:
244
+ voltage_col_name = col
245
+ voltage_data = df[col].values
246
+
247
+ if voltage_data is None:
248
+ raise FormatError(
249
+ "No voltage data found in CSV",
250
+ file_path=str(path),
251
+ expected="Numeric column for voltage data",
252
+ got=f"Columns: {', '.join(df.columns)}",
253
+ )
254
+
255
+ # Convert to float64
256
+ data = np.asarray(voltage_data, dtype=np.float64)
257
+
258
+ # Determine sample rate
259
+ detected_sample_rate = sample_rate
260
+ if detected_sample_rate is None and time_data is not None:
261
+ time_data = np.asarray(time_data, dtype=np.float64)
262
+ if len(time_data) > 1:
263
+ # Calculate sample rate from time intervals
264
+ dt = np.median(np.diff(time_data))
265
+ if dt > 0:
266
+ detected_sample_rate = 1.0 / dt
267
+
268
+ if detected_sample_rate is None:
269
+ detected_sample_rate = 1e6 # Default to 1 MSa/s
270
+
271
+ # Build metadata
272
+ metadata = TraceMetadata(
273
+ sample_rate=detected_sample_rate,
274
+ source_file=str(path),
275
+ channel_name=voltage_col_name or "CH1",
276
+ )
277
+
278
+ return WaveformTrace(data=data, metadata=metadata)
279
+
280
+ except pd.errors.ParserError as e:
281
+ raise FormatError(
282
+ "Failed to parse CSV file",
283
+ file_path=str(path),
284
+ details=str(e),
285
+ ) from e
286
+ except Exception as e:
287
+ if isinstance(e, LoaderError | FormatError):
288
+ raise
289
+ raise LoaderError(
290
+ "Failed to load CSV file",
291
+ file_path=str(path),
292
+ details=str(e),
293
+ ) from e
294
+
295
+
296
+ def _load_basic(
297
+ path: Path,
298
+ *,
299
+ time_column: str | int | None,
300
+ voltage_column: str | int | None,
301
+ sample_rate: float | None,
302
+ delimiter: str | None,
303
+ skip_rows: int,
304
+ encoding: str,
305
+ ) -> WaveformTrace:
306
+ """Basic CSV loader without pandas."""
307
+ try:
308
+ with open(path, encoding=encoding) as f:
309
+ # Skip rows
310
+ for _ in range(skip_rows):
311
+ next(f)
312
+
313
+ content = f.read()
314
+
315
+ # Auto-detect delimiter
316
+ if delimiter is None:
317
+ delimiter = _detect_delimiter_from_content(content)
318
+
319
+ # Parse CSV
320
+ reader = csv.reader(StringIO(content), delimiter=delimiter)
321
+ rows = list(reader)
322
+
323
+ if not rows:
324
+ raise FormatError("CSV file is empty", file_path=str(path))
325
+
326
+ # Detect header
327
+ header = None
328
+ data_start = 0
329
+ first_row = rows[0]
330
+
331
+ # Check if first row is a header (contains non-numeric values)
332
+ is_header = False
333
+ for cell in first_row:
334
+ try:
335
+ float(cell)
336
+ except ValueError:
337
+ if cell.strip(): # Non-empty, non-numeric
338
+ is_header = True
339
+ break
340
+
341
+ if is_header:
342
+ header = [cell.strip() for cell in first_row]
343
+ data_start = 1
344
+
345
+ # Determine column indices
346
+ time_idx = None
347
+ voltage_idx = None
348
+
349
+ if header:
350
+ # Find columns by name
351
+ if time_column is not None:
352
+ if isinstance(time_column, int):
353
+ time_idx = time_column
354
+ elif time_column in header:
355
+ time_idx = header.index(time_column)
356
+ else:
357
+ # Auto-detect
358
+ for i, col in enumerate(header):
359
+ if col.lower() in [n.lower() for n in TIME_COLUMN_NAMES]:
360
+ time_idx = i
361
+ break
362
+
363
+ if voltage_column is not None:
364
+ if isinstance(voltage_column, int):
365
+ voltage_idx = voltage_column
366
+ elif voltage_column in header:
367
+ voltage_idx = header.index(voltage_column)
368
+ else:
369
+ # Auto-detect (first column that's not time)
370
+ for i, col in enumerate(header):
371
+ if i == time_idx:
372
+ continue
373
+ if col.lower() in [n.lower() for n in VOLTAGE_COLUMN_NAMES]:
374
+ voltage_idx = i
375
+ break
376
+ if voltage_idx is None:
377
+ voltage_idx = 1 if time_idx == 0 else 0
378
+ else:
379
+ # No header - use indices
380
+ if isinstance(time_column, int):
381
+ time_idx = time_column
382
+ else:
383
+ time_idx = 0 # Assume first column is time
384
+
385
+ if isinstance(voltage_column, int):
386
+ voltage_idx = voltage_column
387
+ else:
388
+ voltage_idx = 1 # Assume second column is voltage
389
+
390
+ # Extract data
391
+ time_data = []
392
+ voltage_data = []
393
+
394
+ for row in rows[data_start:]:
395
+ if not row:
396
+ continue
397
+ try:
398
+ if voltage_idx is not None and voltage_idx < len(row):
399
+ voltage_data.append(float(row[voltage_idx]))
400
+ if time_idx is not None and time_idx < len(row):
401
+ time_data.append(float(row[time_idx]))
402
+ except (ValueError, IndexError):
403
+ continue # Skip malformed rows
404
+
405
+ if not voltage_data:
406
+ raise FormatError(
407
+ "No valid voltage data found in CSV",
408
+ file_path=str(path),
409
+ )
410
+
411
+ data = np.array(voltage_data, dtype=np.float64)
412
+
413
+ # Determine sample rate
414
+ detected_sample_rate = sample_rate
415
+ if detected_sample_rate is None and time_data:
416
+ time_arr = np.array(time_data, dtype=np.float64)
417
+ if len(time_arr) > 1:
418
+ dt = np.median(np.diff(time_arr))
419
+ if dt > 0:
420
+ detected_sample_rate = 1.0 / dt
421
+
422
+ if detected_sample_rate is None:
423
+ detected_sample_rate = 1e6
424
+
425
+ # Channel name
426
+ channel_name = "CH1"
427
+ if header and voltage_idx is not None and voltage_idx < len(header):
428
+ channel_name = header[voltage_idx]
429
+
430
+ metadata = TraceMetadata(
431
+ sample_rate=detected_sample_rate,
432
+ source_file=str(path),
433
+ channel_name=channel_name,
434
+ )
435
+
436
+ return WaveformTrace(data=data, metadata=metadata)
437
+
438
+ except Exception as e:
439
+ if isinstance(e, LoaderError | FormatError):
440
+ raise
441
+ raise LoaderError(
442
+ "Failed to load CSV file",
443
+ file_path=str(path),
444
+ details=str(e),
445
+ ) from e
446
+
447
+
448
+ def _detect_delimiter(path: Path, encoding: str) -> str:
449
+ """Detect the delimiter used in a CSV file."""
450
+ try:
451
+ with open(path, encoding=encoding) as f:
452
+ sample = f.read(4096)
453
+ return _detect_delimiter_from_content(sample)
454
+ except Exception:
455
+ return ","
456
+
457
+
458
+ def _detect_delimiter_from_content(content: str) -> str:
459
+ """Detect delimiter from CSV content."""
460
+ # Try common delimiters and count occurrences
461
+ delimiters = [",", "\t", ";", "|", " "]
462
+ counts: dict[str, int] = {}
463
+
464
+ for delim in delimiters:
465
+ counts[delim] = content.count(delim)
466
+
467
+ # Return the most common delimiter
468
+ if counts:
469
+ return max(counts, key=lambda d: counts[d])
470
+ return ","
471
+
472
+
473
+ __all__ = ["load_csv"]
oscura/loaders/hdf5.py ADDED
@@ -0,0 +1,9 @@
1
+ """Alias module for HDF5 loader - provides expected import path.
2
+
3
+ This module re-exports from hdf5_loader.py to support:
4
+ from oscura.loaders.hdf5 import load_hdf5
5
+ """
6
+
7
+ from oscura.loaders.hdf5_loader import load_hdf5
8
+
9
+ __all__ = ["load_hdf5"]