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/utils/lazy.py ADDED
@@ -0,0 +1,407 @@
1
+ """Lazy evaluation utilities for deferred computation.
2
+
3
+ This module provides lazy evaluation proxies that defer computation until
4
+ results are actually needed, enabling memory-efficient operation chaining.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.utils.lazy import LazyArray, lazy_operation
9
+ >>> # Operations are deferred until .compute() is called
10
+ >>> lazy_result = lazy_operation(large_data, lambda x: x ** 2)
11
+ >>> result = lazy_result.compute() # Only now is computation performed
12
+
13
+ References:
14
+ Dask documentation on lazy evaluation
15
+ NumPy lazy evaluation patterns
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from abc import ABC, abstractmethod
21
+ from typing import TYPE_CHECKING, Any, TypeVar
22
+
23
+ import numpy as np
24
+ from numpy.typing import NDArray
25
+
26
+ if TYPE_CHECKING:
27
+ from collections.abc import Callable
28
+
29
+ T = TypeVar("T")
30
+
31
+
32
+ class LazyProxy[T](ABC):
33
+ """Abstract base class for lazy evaluation proxies.
34
+
35
+ Defers computation until explicitly requested via .compute().
36
+ """
37
+
38
+ def __init__(self) -> None:
39
+ self._computed: bool = False
40
+ self._result: T | None = None
41
+
42
+ @abstractmethod
43
+ def _evaluate(self) -> T:
44
+ """Perform the actual computation.
45
+
46
+ Returns:
47
+ Computed result.
48
+ """
49
+
50
+ def compute(self) -> T:
51
+ """Evaluate and return the result.
52
+
53
+ Returns:
54
+ Computed result (cached after first evaluation).
55
+
56
+ Example:
57
+ >>> lazy_obj = LazyArray(lambda: np.arange(1000))
58
+ >>> result = lazy_obj.compute()
59
+ """
60
+ if not self._computed:
61
+ self._result = self._evaluate()
62
+ self._computed = True
63
+ return self._result # type: ignore[return-value]
64
+
65
+ def is_computed(self) -> bool:
66
+ """Check if result has been computed.
67
+
68
+ Returns:
69
+ True if compute() has been called.
70
+ """
71
+ return self._computed
72
+
73
+ def reset(self) -> None:
74
+ """Clear cached result, forcing re-evaluation on next compute()."""
75
+ self._computed = False
76
+ self._result = None
77
+
78
+
79
+ class LazyArray(LazyProxy[NDArray[np.floating[Any]]]):
80
+ """Lazy evaluation proxy for numpy arrays.
81
+
82
+ Wraps a computation that returns a numpy array, deferring
83
+ execution until the result is needed.
84
+
85
+ Args:
86
+ func: Callable that returns a numpy array.
87
+ args: Positional arguments for func.
88
+ kwargs: Keyword arguments for func.
89
+
90
+ Example:
91
+ >>> def expensive_computation():
92
+ ... return np.random.randn(1000000)
93
+ >>> lazy = LazyArray(expensive_computation)
94
+ >>> # No computation yet
95
+ >>> result = lazy.compute() # Now it runs
96
+ """
97
+
98
+ def __init__(
99
+ self,
100
+ func: Callable[..., NDArray[np.floating[Any]]],
101
+ *args: Any,
102
+ **kwargs: Any,
103
+ ) -> None:
104
+ super().__init__()
105
+ self._func = func
106
+ self._args = args
107
+ self._kwargs = kwargs
108
+
109
+ def _evaluate(self) -> NDArray[np.floating[Any]]:
110
+ """Execute the deferred computation."""
111
+ return self._func(*self._args, **self._kwargs)
112
+
113
+ def __len__(self) -> int:
114
+ """Get length (triggers computation)."""
115
+ return len(self.compute())
116
+
117
+ def __getitem__(self, key: Any) -> Any:
118
+ """Get item (triggers computation)."""
119
+ return self.compute()[key]
120
+
121
+ def shape(self) -> tuple[int, ...]:
122
+ """Get shape (triggers computation)."""
123
+ return self.compute().shape # type: ignore[no-any-return]
124
+
125
+ def dtype(self) -> np.dtype[Any]:
126
+ """Get dtype (triggers computation)."""
127
+ return self.compute().dtype
128
+
129
+
130
+ class LazyOperation(LazyProxy[Any]):
131
+ """Lazy evaluation of an operation on data.
132
+
133
+ Chains operations without intermediate materialization.
134
+
135
+ Args:
136
+ operation: Callable that performs the operation.
137
+ *operands: Input data or other lazy proxies.
138
+ **kwargs: Keyword arguments for the operation.
139
+
140
+ Example:
141
+ >>> data = np.arange(1000)
142
+ >>> # Chain operations without computing intermediate results
143
+ >>> op1 = LazyOperation(lambda x: x ** 2, data)
144
+ >>> op2 = LazyOperation(lambda x: x + 1, op1)
145
+ >>> result = op2.compute()
146
+ """
147
+
148
+ def __init__(
149
+ self,
150
+ operation: Callable[..., Any],
151
+ *operands: Any,
152
+ **kwargs: Any,
153
+ ) -> None:
154
+ super().__init__()
155
+ self._operation = operation
156
+ self._operands = operands
157
+ self._kwargs = kwargs
158
+
159
+ def _evaluate(self) -> Any:
160
+ """Evaluate the operation, computing operands if needed."""
161
+ # Evaluate any lazy operands
162
+ evaluated_operands = []
163
+ for operand in self._operands:
164
+ if isinstance(operand, LazyProxy):
165
+ evaluated_operands.append(operand.compute())
166
+ else:
167
+ evaluated_operands.append(operand)
168
+
169
+ return self._operation(*evaluated_operands, **self._kwargs)
170
+
171
+
172
+ def lazy_operation[T](
173
+ func: Callable[..., T],
174
+ *args: Any,
175
+ **kwargs: Any,
176
+ ) -> LazyOperation:
177
+ """Create a lazy operation from a function.
178
+
179
+ Args:
180
+ func: Function to defer.
181
+ *args: Arguments to pass to func.
182
+ **kwargs: Keyword arguments to pass to func.
183
+
184
+ Returns:
185
+ LazyOperation that will execute func when computed.
186
+
187
+ Example:
188
+ >>> import numpy as np
189
+ >>> data = np.arange(1000)
190
+ >>> lazy_result = lazy_operation(np.fft.fft, data)
191
+ >>> # Computation happens here
192
+ >>> result = lazy_result.compute()
193
+ """
194
+ return LazyOperation(func, *args, **kwargs)
195
+
196
+
197
+ def auto_preview(
198
+ data: NDArray[np.floating[Any]],
199
+ *,
200
+ downsample_factor: int = 10,
201
+ preview_only: bool = False,
202
+ ) -> NDArray[np.float64]:
203
+ """Generate preview of large dataset with automatic downsampling.
204
+
205
+ Two-stage analysis: quick preview before full processing.
206
+
207
+ Args:
208
+ data: Input data array.
209
+ downsample_factor: Factor to downsample by for preview (default 10).
210
+ preview_only: If True, return only preview. If False, return full data.
211
+
212
+ Returns:
213
+ Preview (downsampled) or full data based on preview_only flag.
214
+
215
+ Example:
216
+ >>> import numpy as np
217
+ >>> large_data = np.random.randn(10_000_000)
218
+ >>> # Quick preview
219
+ >>> preview = auto_preview(large_data, preview_only=True)
220
+ >>> print(f"Preview shape: {preview.shape}")
221
+ >>> # Full data
222
+ >>> full = auto_preview(large_data, preview_only=False)
223
+
224
+ References:
225
+ MEM-026: Two-Stage Analysis (Preview + Full)
226
+ """
227
+ if preview_only or len(data) > 1_000_000:
228
+ # Generate downsampled preview
229
+ preview = data[::downsample_factor].copy()
230
+ return preview.astype(np.float64)
231
+ else:
232
+ # Small enough, return full data
233
+ return data.astype(np.float64)
234
+
235
+
236
+ def select_roi(
237
+ data: NDArray[np.floating[Any]],
238
+ start: int | None = None,
239
+ end: int | None = None,
240
+ *,
241
+ start_time: float | None = None,
242
+ end_time: float | None = None,
243
+ sample_rate: float | None = None,
244
+ ) -> NDArray[np.float64]:
245
+ """Select region of interest from data.
246
+
247
+ Allows selection by sample indices or time values.
248
+
249
+ Args:
250
+ data: Input data array.
251
+ start: Start sample index (inclusive).
252
+ end: End sample index (exclusive).
253
+ start_time: Start time in seconds (alternative to start).
254
+ end_time: End time in seconds (alternative to end).
255
+ sample_rate: Sample rate in Hz (required if using time-based selection).
256
+
257
+ Returns:
258
+ Selected region of interest.
259
+
260
+ Raises:
261
+ ValueError: If time-based selection used without sample_rate.
262
+
263
+ Example:
264
+ >>> import numpy as np
265
+ >>> data = np.random.randn(10_000_000)
266
+ >>> # Select by sample indices
267
+ >>> roi = select_roi(data, start=1000, end=2000)
268
+ >>> # Select by time
269
+ >>> roi_time = select_roi(
270
+ ... data, start_time=0.001, end_time=0.002, sample_rate=1e6
271
+ ... )
272
+
273
+ References:
274
+ MEM-027: Region-of-Interest Selection from Preview
275
+ """
276
+ # Convert time-based to sample-based
277
+ if start_time is not None or end_time is not None:
278
+ if sample_rate is None:
279
+ raise ValueError("sample_rate required for time-based selection")
280
+
281
+ if start_time is not None:
282
+ start = int(start_time * sample_rate)
283
+ if end_time is not None:
284
+ end = int(end_time * sample_rate)
285
+
286
+ # Apply defaults
287
+ if start is None:
288
+ start = 0
289
+ if end is None:
290
+ end = len(data)
291
+
292
+ # Validate and clip to bounds
293
+ start = max(0, start)
294
+ end = min(len(data), end)
295
+
296
+ if start >= end:
297
+ raise ValueError(f"Invalid ROI: start ({start}) >= end ({end})")
298
+
299
+ # Extract region
300
+ return data[start:end].astype(np.float64)
301
+
302
+
303
+ class ProgressiveResolution:
304
+ """Progressive resolution analyzer for large datasets.
305
+
306
+ Implements coarse-to-fine analysis: preview then zoom into ROI.
307
+
308
+ Args:
309
+ data: Input data array or lazy proxy.
310
+ sample_rate: Sample rate in Hz.
311
+
312
+ Example:
313
+ >>> import numpy as np
314
+ >>> data = np.random.randn(100_000_000)
315
+ >>> analyzer = ProgressiveResolution(data, sample_rate=1e6)
316
+ >>> # Stage 1: Preview
317
+ >>> preview = analyzer.get_preview(downsample_factor=100)
318
+ >>> # Stage 2: User selects ROI
319
+ >>> roi_data = analyzer.get_roi(start_time=0.5, end_time=0.6)
320
+
321
+ References:
322
+ MEM-013: Progressive Resolution (Coarse-to-Fine)
323
+ """
324
+
325
+ def __init__(
326
+ self,
327
+ data: NDArray[np.floating[Any]] | LazyProxy[NDArray[np.floating[Any]]],
328
+ sample_rate: float,
329
+ ) -> None:
330
+ self._data = data
331
+ self._sample_rate = sample_rate
332
+ self._preview: NDArray[np.float64] | None = None
333
+ self._preview_factor: int | None = None
334
+
335
+ def get_preview(
336
+ self,
337
+ downsample_factor: int = 10,
338
+ force_recompute: bool = False,
339
+ ) -> NDArray[np.float64]:
340
+ """Generate low-resolution preview.
341
+
342
+ Args:
343
+ downsample_factor: Factor to downsample by.
344
+ force_recompute: If True, recompute even if cached.
345
+
346
+ Returns:
347
+ Downsampled preview of data.
348
+ """
349
+ if self._preview is not None and not force_recompute:
350
+ if self._preview_factor == downsample_factor:
351
+ return self._preview
352
+
353
+ # Get full data
354
+ data = self._data.compute() if isinstance(self._data, LazyProxy) else self._data
355
+
356
+ # Downsample
357
+ self._preview = data[::downsample_factor].copy().astype(np.float64)
358
+ self._preview_factor = downsample_factor
359
+
360
+ return self._preview
361
+
362
+ def get_roi(
363
+ self,
364
+ start: int | None = None,
365
+ end: int | None = None,
366
+ *,
367
+ start_time: float | None = None,
368
+ end_time: float | None = None,
369
+ ) -> NDArray[np.float64]:
370
+ """Get high-resolution region of interest.
371
+
372
+ Args:
373
+ start: Start sample index.
374
+ end: End sample index.
375
+ start_time: Start time in seconds (alternative).
376
+ end_time: End time in seconds (alternative).
377
+
378
+ Returns:
379
+ Full-resolution ROI data.
380
+ """
381
+ # Get full data
382
+ data = self._data.compute() if isinstance(self._data, LazyProxy) else self._data
383
+
384
+ return select_roi(
385
+ data,
386
+ start=start,
387
+ end=end,
388
+ start_time=start_time,
389
+ end_time=end_time,
390
+ sample_rate=self._sample_rate,
391
+ )
392
+
393
+ @property
394
+ def sample_rate(self) -> float:
395
+ """Sample rate in Hz."""
396
+ return self._sample_rate
397
+
398
+
399
+ __all__ = [
400
+ "LazyArray",
401
+ "LazyOperation",
402
+ "LazyProxy",
403
+ "ProgressiveResolution",
404
+ "auto_preview",
405
+ "lazy_operation",
406
+ "select_roi",
407
+ ]
@@ -0,0 +1,147 @@
1
+ """Lazy import utilities for optional heavy dependencies.
2
+
3
+ Delays import of heavy libraries (matplotlib, plotly, asammdf) until first use.
4
+ This significantly reduces import time for CLI and lightweight operations.
5
+
6
+ Example:
7
+ >>> from oscura.utils.lazy_imports import lazy_import
8
+ >>> plt = lazy_import('matplotlib.pyplot')
9
+ >>> # matplotlib not imported yet
10
+ >>> plt.figure() # matplotlib imported on first use
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import importlib
16
+ import sys
17
+ from types import ModuleType
18
+ from typing import Any
19
+
20
+
21
+ class LazyModule:
22
+ """Lazy module loader that delays import until attribute access.
23
+
24
+ Args:
25
+ name: Module name to import.
26
+ package: Package name for relative imports.
27
+
28
+ Example:
29
+ >>> plt = LazyModule('matplotlib.pyplot')
30
+ >>> # No import yet
31
+ >>> fig = plt.figure() # Import happens here
32
+ """
33
+
34
+ def __init__(self, name: str, package: str | None = None) -> None:
35
+ """Initialize lazy module loader.
36
+
37
+ Args:
38
+ name: Module name to import.
39
+ package: Package name for relative imports.
40
+ """
41
+ self._module_name = name
42
+ self._module_package = package
43
+ self._module: ModuleType | None = None
44
+
45
+ def _load(self) -> ModuleType:
46
+ """Load the module if not already loaded.
47
+
48
+ Returns:
49
+ Loaded module.
50
+ """
51
+ if self._module is None:
52
+ self._module = importlib.import_module(self._module_name, self._module_package)
53
+ return self._module
54
+
55
+ def __getattr__(self, name: str) -> Any:
56
+ """Get attribute from lazy-loaded module.
57
+
58
+ Args:
59
+ name: Attribute name.
60
+
61
+ Returns:
62
+ Attribute value from loaded module.
63
+ """
64
+ return getattr(self._load(), name)
65
+
66
+ def __dir__(self) -> list[str]:
67
+ """Get directory of lazy-loaded module.
68
+
69
+ Returns:
70
+ List of attribute names.
71
+ """
72
+ return dir(self._load())
73
+
74
+
75
+ def lazy_import(name: str, package: str | None = None) -> LazyModule:
76
+ """Create a lazy-loading module proxy.
77
+
78
+ Module is not imported until first attribute access.
79
+ Reduces startup time and memory usage.
80
+
81
+ Args:
82
+ name: Module name to import.
83
+ package: Package name for relative imports.
84
+
85
+ Returns:
86
+ Lazy module proxy.
87
+
88
+ Example:
89
+ >>> plt = lazy_import('matplotlib.pyplot')
90
+ >>> # matplotlib.pyplot not imported yet
91
+ >>> fig = plt.figure() # Import happens here
92
+ >>> plt.show()
93
+ """
94
+ return LazyModule(name, package)
95
+
96
+
97
+ def is_available(module_name: str) -> bool:
98
+ """Check if a module is available without importing it.
99
+
100
+ Args:
101
+ module_name: Module name to check.
102
+
103
+ Returns:
104
+ True if module can be imported.
105
+
106
+ Example:
107
+ >>> if is_available('matplotlib'):
108
+ ... import matplotlib.pyplot as plt
109
+ ... plt.figure()
110
+ """
111
+ if module_name in sys.modules:
112
+ return True
113
+
114
+ try:
115
+ importlib.util.find_spec(module_name) # type: ignore[attr-defined]
116
+ return True
117
+ except (ImportError, ModuleNotFoundError, ValueError, AttributeError):
118
+ return False
119
+
120
+
121
+ def require_module(module_name: str, feature: str = "") -> None:
122
+ """Raise informative error if required module is not available.
123
+
124
+ Args:
125
+ module_name: Module name to check.
126
+ feature: Feature name that requires the module (for error message).
127
+
128
+ Raises:
129
+ ImportError: If module is not available.
130
+
131
+ Example:
132
+ >>> require_module('matplotlib', 'plotting')
133
+ >>> import matplotlib.pyplot as plt # Safe to import now
134
+ """
135
+ if not is_available(module_name):
136
+ feature_msg = f" for {feature}" if feature else ""
137
+ raise ImportError(
138
+ f"{module_name} is required{feature_msg}. Install with: pip install {module_name}"
139
+ )
140
+
141
+
142
+ __all__ = [
143
+ "LazyModule",
144
+ "is_available",
145
+ "lazy_import",
146
+ "require_module",
147
+ ]