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,391 @@
1
+ """Chunked wavelet transform for memory-bounded processing.
2
+
3
+ This module implements memory-bounded wavelet transforms (CWT and DWT)
4
+ with segment processing and boundary handling.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.spectral.chunked_wavelet import cwt_chunked, dwt_chunked
9
+ >>> coeffs = cwt_chunked('large_signal.bin', scales=[1, 2, 4, 8], wavelet='morl')
10
+ >>> print(f"CWT coefficients shape: {coeffs.shape}")
11
+
12
+ References:
13
+ pywt (PyWavelets) for wavelet transforms
14
+ Mallat, S. (1999). "A Wavelet Tour of Signal Processing"
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from pathlib import Path
20
+ from typing import TYPE_CHECKING, Any
21
+
22
+ import numpy as np
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import Iterator, Sequence
26
+
27
+ from numpy.typing import NDArray
28
+
29
+
30
+ def cwt_chunked(
31
+ file_path: str | Path,
32
+ scales: Sequence[float],
33
+ wavelet: str = "morl",
34
+ *,
35
+ chunk_size: int | float = 1e6,
36
+ overlap_factor: float = 2.0,
37
+ sample_rate: float = 1.0,
38
+ dtype: str = "float32",
39
+ ) -> tuple[NDArray[Any], NDArray[Any]]:
40
+ """Compute continuous wavelet transform for large files.
41
+
42
+
43
+ Processes signal in chunks with overlap to handle boundaries,
44
+ computes CWT per chunk, and stitches results.
45
+
46
+ Args:
47
+ file_path: Path to signal file (binary format).
48
+ scales: Scales for CWT (wavelet dilations).
49
+ wavelet: Wavelet name ('morl', 'mexh', 'cmor', etc.).
50
+ chunk_size: Chunk size in samples.
51
+ overlap_factor: Overlap factor for boundaries (e.g., 2.0 = 2x max scale).
52
+ sample_rate: Sample rate in Hz.
53
+ dtype: Data type of input file ('float32' or 'float64').
54
+
55
+ Returns:
56
+ Tuple of (coefficients, frequencies) where:
57
+ - coefficients: CWT coefficients (scales x time).
58
+ - frequencies: Corresponding frequencies for each scale.
59
+
60
+ Raises:
61
+ ImportError: If pywt (PyWavelets) is not installed.
62
+ ValueError: If file cannot be read or scales invalid.
63
+
64
+ Example:
65
+ >>> scales = np.arange(1, 128)
66
+ >>> coeffs, freqs = cwt_chunked(
67
+ ... 'signal.bin',
68
+ ... scales=scales,
69
+ ... wavelet='morl',
70
+ ... chunk_size=1e6,
71
+ ... sample_rate=1e6
72
+ ... )
73
+ >>> print(f"CWT shape: {coeffs.shape}")
74
+
75
+ References:
76
+ MEM-007: Chunked Wavelet Transform
77
+ """
78
+ try:
79
+ import pywt
80
+ except ImportError as e:
81
+ raise ImportError(
82
+ "pywt (PyWavelets) is required for wavelet transforms. "
83
+ "Install with: pip install PyWavelets"
84
+ ) from e
85
+
86
+ chunk_size = int(chunk_size)
87
+ scales: NDArray[np.float64] = np.asarray(scales) # type: ignore[no-redef]
88
+
89
+ # Calculate boundary overlap (proportional to max scale)
90
+ max_scale = np.max(scales)
91
+ boundary_overlap = int(overlap_factor * max_scale)
92
+
93
+ # Determine dtype
94
+ np_dtype = np.float32 if dtype == "float32" else np.float64
95
+ bytes_per_sample = 4 if dtype == "float32" else 8
96
+
97
+ # Open file and get total size
98
+ file_path = Path(file_path)
99
+ file_size_bytes = file_path.stat().st_size
100
+ total_samples = file_size_bytes // bytes_per_sample
101
+
102
+ # Process chunks
103
+ coeffs_list: list[NDArray[Any]] = []
104
+ chunks = _generate_chunks(file_path, total_samples, chunk_size, boundary_overlap, np_dtype)
105
+
106
+ for chunk_data in chunks:
107
+ # Compute CWT for this chunk
108
+ coeffs_chunk, freqs = pywt.cwt(
109
+ chunk_data,
110
+ scales,
111
+ wavelet,
112
+ sampling_period=1 / sample_rate,
113
+ )
114
+
115
+ # Remove boundary overlap regions (except first/last chunk)
116
+ if len(coeffs_list) > 0:
117
+ # Remove left boundary
118
+ trim_left = boundary_overlap
119
+ coeffs_chunk = coeffs_chunk[:, trim_left:]
120
+
121
+ coeffs_list.append(coeffs_chunk)
122
+
123
+ # Concatenate all chunks
124
+ if len(coeffs_list) == 0:
125
+ raise ValueError(f"No chunks processed from {file_path}")
126
+
127
+ coefficients = np.concatenate(coeffs_list, axis=1)
128
+
129
+ return coefficients, freqs
130
+
131
+
132
+ def dwt_chunked(
133
+ file_path: str | Path,
134
+ wavelet: str = "db4",
135
+ level: int | None = None,
136
+ *,
137
+ chunk_size: int | float = 1e6,
138
+ mode: str = "symmetric",
139
+ dtype: str = "float32",
140
+ ) -> list[NDArray[Any]]:
141
+ """Compute discrete wavelet transform for large files.
142
+
143
+
144
+ Processes signal in chunks and computes multilevel DWT.
145
+ Handles boundaries using specified extension mode.
146
+
147
+ Args:
148
+ file_path: Path to signal file (binary format).
149
+ wavelet: Wavelet name ('db4', 'haar', 'sym5', etc.).
150
+ level: Decomposition level (None = maximum level).
151
+ chunk_size: Chunk size in samples.
152
+ mode: Signal extension mode for boundaries.
153
+ dtype: Data type of input file ('float32' or 'float64').
154
+
155
+ Returns:
156
+ List of coefficient arrays [cA_n, cD_n, ..., cD_1] where:
157
+ - cA_n: Approximation coefficients at level n.
158
+ - cD_i: Detail coefficients at level i.
159
+
160
+ Raises:
161
+ ImportError: If pywt is not installed.
162
+ ValueError: If file cannot be read.
163
+
164
+ Example:
165
+ >>> coeffs = dwt_chunked(
166
+ ... 'signal.bin',
167
+ ... wavelet='db4',
168
+ ... level=5,
169
+ ... chunk_size=1e6
170
+ ... )
171
+ >>> print(f"Approximation shape: {coeffs[0].shape}")
172
+
173
+ References:
174
+ MEM-007: Chunked Wavelet Transform
175
+ Daubechies, I. (1992). "Ten Lectures on Wavelets"
176
+ """
177
+ try:
178
+ import pywt
179
+ except ImportError as e:
180
+ raise ImportError(
181
+ "pywt (PyWavelets) is required for wavelet transforms. "
182
+ "Install with: pip install PyWavelets"
183
+ ) from e
184
+
185
+ chunk_size = int(chunk_size)
186
+
187
+ # Determine dtype
188
+ np_dtype = np.float32 if dtype == "float32" else np.float64
189
+ bytes_per_sample = 4 if dtype == "float32" else 8
190
+
191
+ # Get wavelet filter length for overlap calculation
192
+ wavelet_obj = pywt.Wavelet(wavelet)
193
+ filter_len = wavelet_obj.dec_len
194
+ boundary_overlap = filter_len * (2 ** (level or 1))
195
+
196
+ # Open file and get total size
197
+ file_path = Path(file_path)
198
+ file_size_bytes = file_path.stat().st_size
199
+ total_samples = file_size_bytes // bytes_per_sample
200
+
201
+ # Process chunks
202
+ coeffs_list: list[list[NDArray[Any]]] = []
203
+ chunk_starts: list[int] = [] # Track chunk start positions in original signal
204
+ chunks = _generate_chunks(file_path, total_samples, chunk_size, boundary_overlap, np_dtype)
205
+
206
+ current_offset = 0
207
+ for chunk_data in chunks:
208
+ # Compute DWT for this chunk
209
+ coeffs_chunk = pywt.wavedec(chunk_data, wavelet, mode=mode, level=level)
210
+ coeffs_list.append(coeffs_chunk)
211
+ chunk_starts.append(current_offset)
212
+ # Move offset by chunk_size (not including overlap)
213
+ current_offset = min(current_offset + chunk_size, total_samples)
214
+
215
+ # Merge coefficients from all chunks
216
+ if len(coeffs_list) == 0:
217
+ raise ValueError(f"No chunks processed from {file_path}")
218
+
219
+ # For DWT, we need to handle overlaps properly at each decomposition level
220
+ # At each level j, the downsampling factor is 2^j
221
+ # We trim the overlap region at each level based on the decomposition level
222
+
223
+ num_levels = len(coeffs_list[0])
224
+ merged_coeffs = []
225
+
226
+ for level_idx in range(num_levels):
227
+ # Calculate the effective overlap for this decomposition level
228
+ # Each level is downsampled by factor of 2 from previous level
229
+ downsample_factor = 2**level_idx
230
+ level_overlap = boundary_overlap // downsample_factor
231
+
232
+ if len(coeffs_list) == 1:
233
+ # Only one chunk - no overlap to handle
234
+ merged_coeffs.append(coeffs_list[0][level_idx])
235
+ else:
236
+ # Multiple chunks - need to handle overlaps
237
+ merged_level_coeffs = []
238
+
239
+ for chunk_idx, chunk_coeffs in enumerate(coeffs_list):
240
+ level_coeffs = chunk_coeffs[level_idx]
241
+
242
+ if chunk_idx == 0:
243
+ # First chunk - keep all except right overlap
244
+ if level_overlap > 0 and len(level_coeffs) > level_overlap:
245
+ merged_level_coeffs.append(level_coeffs[:-level_overlap])
246
+ else:
247
+ merged_level_coeffs.append(level_coeffs)
248
+ elif chunk_idx == len(coeffs_list) - 1:
249
+ # Last chunk - trim left overlap, keep rest
250
+ if level_overlap > 0 and len(level_coeffs) > level_overlap:
251
+ merged_level_coeffs.append(level_coeffs[level_overlap:])
252
+ else:
253
+ # If overlap is too large, keep small center portion
254
+ center_start = max(0, level_overlap // 2)
255
+ merged_level_coeffs.append(level_coeffs[center_start:])
256
+ else:
257
+ # Middle chunks - trim both sides
258
+ if level_overlap > 0 and len(level_coeffs) > 2 * level_overlap:
259
+ merged_level_coeffs.append(level_coeffs[level_overlap:-level_overlap])
260
+ else:
261
+ # If overlap is too large, keep small center portion
262
+ center_start = max(0, level_overlap // 2)
263
+ center_end = max(center_start + 1, len(level_coeffs) - level_overlap // 2)
264
+ merged_level_coeffs.append(level_coeffs[center_start:center_end])
265
+
266
+ # Concatenate the trimmed coefficients
267
+ if merged_level_coeffs:
268
+ merged_coeffs.append(np.concatenate(merged_level_coeffs))
269
+ else:
270
+ # Fallback: concatenate all if trimming failed
271
+ level_coeffs_list = [chunk_coeffs[level_idx] for chunk_coeffs in coeffs_list]
272
+ merged_coeffs.append(np.concatenate(level_coeffs_list))
273
+
274
+ return merged_coeffs
275
+
276
+
277
+ def _generate_chunks(
278
+ file_path: Path,
279
+ total_samples: int,
280
+ chunk_size: int,
281
+ boundary_overlap: int,
282
+ dtype: type,
283
+ ) -> Iterator[NDArray[Any]]:
284
+ """Generate overlapping chunks from file.
285
+
286
+ Args:
287
+ file_path: Path to binary file.
288
+ total_samples: Total number of samples in file.
289
+ chunk_size: Samples per chunk.
290
+ boundary_overlap: Overlap samples between chunks.
291
+ dtype: NumPy dtype for data.
292
+
293
+ Yields:
294
+ Chunk arrays with boundary overlap.
295
+ """
296
+ offset = 0
297
+
298
+ with open(file_path, "rb") as f:
299
+ while offset < total_samples:
300
+ # Calculate chunk boundaries
301
+ chunk_start = max(0, offset - boundary_overlap)
302
+ chunk_end = min(total_samples, offset + chunk_size)
303
+ chunk_len = chunk_end - chunk_start
304
+
305
+ # Seek and read
306
+ f.seek(chunk_start * dtype().itemsize)
307
+ chunk_data: NDArray[np.float64] = np.fromfile(f, dtype=dtype, count=chunk_len)
308
+
309
+ if len(chunk_data) == 0:
310
+ break
311
+
312
+ yield chunk_data
313
+
314
+ # Advance offset
315
+ offset += chunk_size
316
+
317
+
318
+ def cwt_chunked_generator(
319
+ file_path: str | Path,
320
+ scales: Sequence[float],
321
+ wavelet: str = "morl",
322
+ *,
323
+ chunk_size: int | float = 1e6,
324
+ **kwargs: Any, # type: ignore[name-defined]
325
+ ) -> Iterator[tuple[NDArray[Any], NDArray[Any]]]: # type: ignore[name-defined]
326
+ """Generator version that yields CWT chunks.
327
+
328
+ Yields CWT coefficients for each chunk, useful for streaming processing.
329
+
330
+ Args:
331
+ file_path: Path to signal file.
332
+ scales: Scales for CWT.
333
+ wavelet: Wavelet name.
334
+ chunk_size: Chunk size in samples.
335
+ **kwargs: Additional arguments.
336
+
337
+ Yields:
338
+ Tuples of (coefficients, frequencies) for each chunk.
339
+
340
+ Raises:
341
+ ImportError: If pywt (PyWavelets) is not installed.
342
+
343
+ Example:
344
+ >>> for coeffs_chunk, freqs in cwt_chunked_generator('file.bin', scales=[1, 2, 4]):
345
+ ... # Process each chunk separately
346
+ ... print(f"Chunk shape: {coeffs_chunk.shape}")
347
+ """
348
+ try:
349
+ import pywt
350
+ except ImportError as e:
351
+ raise ImportError(
352
+ "pywt (PyWavelets) is required for wavelet transforms. "
353
+ "Install with: pip install PyWavelets"
354
+ ) from e
355
+
356
+ chunk_size = int(chunk_size)
357
+ scales: NDArray[np.float64] = np.asarray(scales) # type: ignore[no-redef]
358
+
359
+ # Determine dtype
360
+ dtype = kwargs.get("dtype", "float32")
361
+ np_dtype = np.float32 if dtype == "float32" else np.float64
362
+ bytes_per_sample = 4 if dtype == "float32" else 8
363
+
364
+ # Open file and get total size
365
+ file_path = Path(file_path)
366
+ file_size_bytes = file_path.stat().st_size
367
+ total_samples = file_size_bytes // bytes_per_sample
368
+
369
+ # Calculate boundary overlap
370
+ max_scale = np.max(scales)
371
+ boundary_overlap = int(kwargs.get("overlap_factor", 2.0) * max_scale)
372
+
373
+ # Process chunks
374
+ sample_rate = kwargs.get("sample_rate", 1.0)
375
+ chunks = _generate_chunks(file_path, total_samples, chunk_size, boundary_overlap, np_dtype)
376
+
377
+ for chunk_data in chunks:
378
+ coeffs_chunk, freqs = pywt.cwt(
379
+ chunk_data,
380
+ scales,
381
+ wavelet,
382
+ sampling_period=1 / sample_rate,
383
+ )
384
+ yield coeffs_chunk, freqs
385
+
386
+
387
+ __all__ = [
388
+ "cwt_chunked",
389
+ "cwt_chunked_generator",
390
+ "dwt_chunked",
391
+ ]
@@ -0,0 +1,92 @@
1
+ """FFT analysis functions.
2
+
3
+ This module provides FFT-based spectral analysis including chunked processing
4
+ for large datasets.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ import numpy as np
12
+ from numpy.typing import NDArray
13
+
14
+ # Re-export file-based chunked FFT functions
15
+ from oscura.analyzers.spectral.chunked_fft import (
16
+ fft_chunked as fft_chunked_file,
17
+ )
18
+ from oscura.analyzers.spectral.chunked_fft import (
19
+ fft_chunked_parallel,
20
+ streaming_fft,
21
+ welch_psd_chunked,
22
+ )
23
+
24
+
25
+ def fft_chunked(
26
+ data: NDArray[np.floating[Any]],
27
+ chunk_size: int = 8192,
28
+ *,
29
+ window: str = "hann",
30
+ overlap_pct: float = 50.0,
31
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
32
+ """Compute FFT on large array using chunked processing.
33
+
34
+ Processes array in overlapping segments to handle large datasets
35
+ that may not fit in FFT at once.
36
+
37
+ Args:
38
+ data: Input signal array.
39
+ chunk_size: Size of each processing chunk.
40
+ window: Window function name.
41
+ overlap_pct: Overlap percentage between chunks (0-100).
42
+
43
+ Returns:
44
+ Tuple of (frequencies, magnitudes) arrays.
45
+
46
+ Example:
47
+ >>> signal = np.sin(2 * np.pi * 1000 * np.linspace(0, 1, 1000000))
48
+ >>> freqs, mags = fft_chunked(signal, chunk_size=4096)
49
+ """
50
+ data = np.asarray(data, dtype=np.float64).ravel()
51
+
52
+ # Calculate overlap
53
+ hop_size = int(chunk_size * (1 - overlap_pct / 100))
54
+
55
+ # Generate window
56
+ from scipy import signal as sp_signal
57
+
58
+ window_arr = sp_signal.get_window(window, chunk_size)
59
+
60
+ # Process chunks
61
+ chunks = []
62
+ for i in range(0, len(data) - chunk_size + 1, hop_size):
63
+ chunk = data[i : i + chunk_size]
64
+ windowed = chunk * window_arr
65
+
66
+ # Compute FFT
67
+ fft_result = np.fft.rfft(windowed)
68
+ magnitude = np.abs(fft_result)
69
+ chunks.append(magnitude)
70
+
71
+ # Average all chunks
72
+ if chunks:
73
+ avg_magnitude = np.mean(chunks, axis=0)
74
+ else:
75
+ # Handle case where signal shorter than chunk_size
76
+ windowed = data * sp_signal.get_window(window, len(data))
77
+ fft_result = np.fft.rfft(windowed)
78
+ avg_magnitude = np.abs(fft_result)
79
+
80
+ # Generate frequency array
81
+ frequencies = np.fft.rfftfreq(chunk_size, d=1.0)
82
+
83
+ return frequencies, avg_magnitude
84
+
85
+
86
+ __all__ = [
87
+ "fft_chunked",
88
+ "fft_chunked_file",
89
+ "fft_chunked_parallel",
90
+ "streaming_fft",
91
+ "welch_psd_chunked",
92
+ ]