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,413 @@
1
+ """Interpolation and resampling operations for Oscura.
2
+
3
+ This module provides interpolation, resampling, and trace alignment
4
+ functions for waveform data.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.math import resample, align_traces
9
+ >>> resampled = resample(trace, new_sample_rate=1e6)
10
+ >>> aligned = align_traces(trace1, trace2)
11
+
12
+ References:
13
+ IEEE 181-2011: Standard for Transitional Waveform Definitions
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import warnings
19
+ from typing import TYPE_CHECKING, Literal
20
+
21
+ import numpy as np
22
+ from scipy import interpolate as sp_interp
23
+ from scipy import signal as sp_signal
24
+
25
+ from oscura.core.exceptions import InsufficientDataError
26
+ from oscura.core.types import TraceMetadata, WaveformTrace
27
+
28
+ if TYPE_CHECKING:
29
+ from numpy.typing import NDArray
30
+
31
+
32
+ def interpolate(
33
+ trace: WaveformTrace,
34
+ new_time: NDArray[np.float64],
35
+ *,
36
+ method: Literal["linear", "cubic", "nearest", "zero"] = "linear",
37
+ fill_value: float | tuple[float, float] = np.nan,
38
+ channel_name: str | None = None,
39
+ ) -> WaveformTrace:
40
+ """Interpolate trace to new time points.
41
+
42
+ Interpolates the waveform data to a new set of time points using
43
+ the specified interpolation method.
44
+
45
+ Args:
46
+ trace: Input trace.
47
+ new_time: New time points in seconds.
48
+ method: Interpolation method:
49
+ - "linear": Linear interpolation (default)
50
+ - "cubic": Cubic spline interpolation
51
+ - "nearest": Nearest neighbor
52
+ - "zero": Zero-order hold (step function)
53
+ fill_value: Value for points outside original range.
54
+ Can be a single value or (below, above) tuple.
55
+ channel_name: Name for the result trace (optional).
56
+
57
+ Returns:
58
+ Interpolated WaveformTrace at new time points.
59
+
60
+ Raises:
61
+ InsufficientDataError: If trace has insufficient samples.
62
+ ValueError: If interpolation method is unknown.
63
+
64
+ Example:
65
+ >>> new_time = np.linspace(0, 1e-3, 2000)
66
+ >>> interpolated = interpolate(trace, new_time, method="cubic")
67
+ """
68
+ if len(trace.data) < 2:
69
+ raise InsufficientDataError(
70
+ "Need at least 2 samples for interpolation",
71
+ required=2,
72
+ available=len(trace.data),
73
+ analysis_type="interpolate",
74
+ )
75
+
76
+ original_time = trace.time_vector
77
+ data = trace.data.astype(np.float64)
78
+
79
+ # Create interpolator
80
+ if method == "linear":
81
+ interp_func = sp_interp.interp1d(
82
+ original_time,
83
+ data,
84
+ kind="linear",
85
+ bounds_error=False,
86
+ fill_value=fill_value,
87
+ )
88
+ elif method == "cubic":
89
+ interp_func = sp_interp.interp1d(
90
+ original_time,
91
+ data,
92
+ kind="cubic",
93
+ bounds_error=False,
94
+ fill_value=fill_value,
95
+ )
96
+ elif method == "nearest":
97
+ interp_func = sp_interp.interp1d(
98
+ original_time,
99
+ data,
100
+ kind="nearest",
101
+ bounds_error=False,
102
+ fill_value=fill_value,
103
+ )
104
+ elif method == "zero":
105
+ interp_func = sp_interp.interp1d(
106
+ original_time,
107
+ data,
108
+ kind="zero",
109
+ bounds_error=False,
110
+ fill_value=fill_value,
111
+ )
112
+ else:
113
+ raise ValueError(f"Unknown interpolation method: {method}")
114
+
115
+ # Interpolate
116
+ result_data = interp_func(new_time)
117
+
118
+ # Calculate new sample rate from time points
119
+ if len(new_time) > 1:
120
+ new_sample_rate = 1.0 / np.mean(np.diff(new_time))
121
+ else:
122
+ new_sample_rate = trace.metadata.sample_rate
123
+
124
+ new_metadata = TraceMetadata(
125
+ sample_rate=new_sample_rate,
126
+ vertical_scale=trace.metadata.vertical_scale,
127
+ vertical_offset=trace.metadata.vertical_offset,
128
+ acquisition_time=trace.metadata.acquisition_time,
129
+ trigger_info=trace.metadata.trigger_info,
130
+ source_file=trace.metadata.source_file,
131
+ channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_interp",
132
+ )
133
+
134
+ return WaveformTrace(data=result_data.astype(np.float64), metadata=new_metadata)
135
+
136
+
137
+ def resample(
138
+ trace: WaveformTrace,
139
+ new_sample_rate: float | None = None,
140
+ *,
141
+ num_samples: int | None = None,
142
+ method: Literal["fft", "polyphase", "interp"] = "fft",
143
+ anti_alias: bool = True,
144
+ channel_name: str | None = None,
145
+ ) -> WaveformTrace:
146
+ """Resample trace to new sample rate or number of samples.
147
+
148
+ Resamples the waveform to a different sample rate using high-quality
149
+ resampling algorithms. Applies anti-aliasing filter when downsampling.
150
+
151
+ Args:
152
+ trace: Input trace.
153
+ new_sample_rate: Target sample rate in Hz. Mutually exclusive
154
+ with num_samples.
155
+ num_samples: Target number of samples. Mutually exclusive with
156
+ new_sample_rate.
157
+ method: Resampling method:
158
+ - "fft": FFT-based resampling (default, best quality)
159
+ - "polyphase": Polyphase filter resampling (efficient)
160
+ - "interp": Linear interpolation (fastest)
161
+ anti_alias: Apply anti-aliasing filter before downsampling.
162
+ channel_name: Name for the result trace (optional).
163
+
164
+ Returns:
165
+ Resampled WaveformTrace.
166
+
167
+ Raises:
168
+ ValueError: If neither or both rate/samples specified.
169
+ InsufficientDataError: If trace has insufficient samples.
170
+
171
+ Example:
172
+ >>> upsampled = resample(trace, new_sample_rate=2e9)
173
+ >>> downsampled = resample(trace, num_samples=1000)
174
+
175
+ References:
176
+ MEM-012 (downsampling for memory management)
177
+ """
178
+ if (new_sample_rate is None) == (num_samples is None):
179
+ raise ValueError("Specify exactly one of new_sample_rate or num_samples")
180
+
181
+ if len(trace.data) < 2:
182
+ raise InsufficientDataError(
183
+ "Need at least 2 samples for resampling",
184
+ required=2,
185
+ available=len(trace.data),
186
+ analysis_type="resample",
187
+ )
188
+
189
+ data = trace.data.astype(np.float64)
190
+ original_rate = trace.metadata.sample_rate
191
+ original_samples = len(data)
192
+
193
+ # Calculate target parameters
194
+ if new_sample_rate is not None:
195
+ target_rate = new_sample_rate
196
+ target_samples = round(original_samples * target_rate / original_rate)
197
+ else:
198
+ target_samples = num_samples # type: ignore[assignment]
199
+ target_rate = original_rate * target_samples / original_samples
200
+
201
+ if target_samples < 1:
202
+ raise ValueError("Target number of samples must be at least 1")
203
+
204
+ # REQ: API-019 - Validate Nyquist criterion when downsampling
205
+ if target_rate < original_rate:
206
+ # Estimate maximum frequency using FFT
207
+ fft_data = np.fft.rfft(data)
208
+ fft_freqs = np.fft.rfftfreq(len(data), 1 / original_rate)
209
+ # Find frequency with 90% of max power as max frequency
210
+ power = np.abs(fft_data) ** 2
211
+ power_threshold = 0.01 * np.max(power) # 1% of max power
212
+ significant_freqs = fft_freqs[power > power_threshold]
213
+ if len(significant_freqs) > 0:
214
+ max_frequency = np.max(significant_freqs)
215
+ nyquist_required = 2 * max_frequency
216
+ if target_rate < nyquist_required:
217
+ warnings.warn(
218
+ f"Downsampling to {target_rate:.2e} Hz violates Nyquist criterion. "
219
+ f"Maximum signal frequency is ~{max_frequency:.2e} Hz, "
220
+ f"requiring ≥{nyquist_required:.2e} Hz sample rate. "
221
+ f"Aliasing may occur.",
222
+ UserWarning,
223
+ stacklevel=2,
224
+ )
225
+
226
+ # Check if downsampling and apply anti-alias filter
227
+ if anti_alias and target_samples < original_samples:
228
+ # Lowpass filter at Nyquist of new rate
229
+ nyquist = target_rate / 2
230
+ cutoff = nyquist / original_rate * 2 # Normalized frequency
231
+ if cutoff < 1.0:
232
+ # Design lowpass filter
233
+ b, a = sp_signal.butter(8, min(cutoff * 0.9, 0.99), btype="low")
234
+ data = sp_signal.filtfilt(b, a, data)
235
+
236
+ # Resample
237
+ if method == "fft":
238
+ result_data = sp_signal.resample(data, target_samples)
239
+ elif method == "polyphase":
240
+ # Find rational approximation for polyphase resampling
241
+ from fractions import Fraction
242
+
243
+ ratio = Fraction(target_samples, original_samples).limit_denominator(1000)
244
+ up, down = ratio.numerator, ratio.denominator
245
+ result_data = sp_signal.resample_poly(data, up, down)
246
+ # Trim to exact length
247
+ result_data = result_data[:target_samples]
248
+ elif method == "interp":
249
+ # Simple interpolation
250
+ old_time = np.arange(original_samples) / original_rate
251
+ new_time = np.arange(target_samples) / target_rate
252
+ result_data = np.interp(new_time, old_time, data)
253
+ else:
254
+ raise ValueError(f"Unknown resampling method: {method}")
255
+
256
+ new_metadata = TraceMetadata(
257
+ sample_rate=target_rate,
258
+ vertical_scale=trace.metadata.vertical_scale,
259
+ vertical_offset=trace.metadata.vertical_offset,
260
+ acquisition_time=trace.metadata.acquisition_time,
261
+ trigger_info=trace.metadata.trigger_info,
262
+ source_file=trace.metadata.source_file,
263
+ channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_resampled",
264
+ )
265
+
266
+ return WaveformTrace(data=result_data.astype(np.float64), metadata=new_metadata)
267
+
268
+
269
+ def align_traces(
270
+ trace1: WaveformTrace,
271
+ trace2: WaveformTrace,
272
+ *,
273
+ method: Literal["interpolate", "resample"] = "interpolate",
274
+ reference: Literal["first", "second", "higher"] = "higher",
275
+ channel_names: tuple[str | None, str | None] | None = None,
276
+ ) -> tuple[WaveformTrace, WaveformTrace]:
277
+ """Align two traces to have the same sample rate and length.
278
+
279
+ Adjusts two traces to be compatible for arithmetic operations by
280
+ resampling to a common sample rate and time base.
281
+
282
+ Args:
283
+ trace1: First trace.
284
+ trace2: Second trace.
285
+ method: Alignment method:
286
+ - "interpolate": Interpolate to common time points
287
+ - "resample": Resample to common rate
288
+ reference: Which trace to use as reference:
289
+ - "first": Use trace1's sample rate
290
+ - "second": Use trace2's sample rate
291
+ - "higher": Use the higher sample rate (default)
292
+ channel_names: Optional names for the aligned traces.
293
+
294
+ Returns:
295
+ Tuple of (aligned_trace1, aligned_trace2) with matching parameters.
296
+
297
+ Example:
298
+ >>> aligned1, aligned2 = align_traces(trace1, trace2)
299
+ >>> diff = subtract(aligned1, aligned2)
300
+ """
301
+ rate1 = trace1.metadata.sample_rate
302
+ rate2 = trace2.metadata.sample_rate
303
+
304
+ # Determine reference sample rate
305
+ if reference == "first":
306
+ target_rate = rate1
307
+ elif reference == "second":
308
+ target_rate = rate2
309
+ else: # "higher"
310
+ target_rate = max(rate1, rate2)
311
+
312
+ # Determine time span (use overlapping portion)
313
+ t1_end = len(trace1.data) / rate1
314
+ t2_end = len(trace2.data) / rate2
315
+ common_end = min(t1_end, t2_end)
316
+
317
+ # Calculate number of samples
318
+ num_samples = round(common_end * target_rate)
319
+
320
+ # Create common time vector
321
+ common_time = np.arange(num_samples) / target_rate
322
+
323
+ name1 = channel_names[0] if channel_names else None
324
+ name2 = channel_names[1] if channel_names else None
325
+
326
+ if method == "interpolate":
327
+ # Interpolate both traces to common time points
328
+ aligned1 = interpolate(trace1, common_time, channel_name=name1)
329
+ aligned2 = interpolate(trace2, common_time, channel_name=name2)
330
+ else: # "resample"
331
+ # Resample both to common rate
332
+ aligned1 = resample(trace1, num_samples=num_samples, channel_name=name1)
333
+ aligned2 = resample(trace2, num_samples=num_samples, channel_name=name2)
334
+
335
+ return aligned1, aligned2
336
+
337
+
338
+ def downsample(
339
+ trace: WaveformTrace,
340
+ factor: int,
341
+ *,
342
+ anti_alias: bool = True,
343
+ method: Literal["decimate", "average", "max", "min"] = "decimate",
344
+ channel_name: str | None = None,
345
+ ) -> WaveformTrace:
346
+ """Downsample trace by an integer factor.
347
+
348
+ Reduces the sample rate by keeping every Nth sample (decimate)
349
+ or by aggregating N samples (average/max/min).
350
+
351
+ Args:
352
+ trace: Input trace.
353
+ factor: Downsampling factor (must be >= 1).
354
+ anti_alias: Apply anti-aliasing filter before decimation.
355
+ method: Downsampling method:
356
+ - "decimate": Keep every Nth sample (default)
357
+ - "average": Average every N samples
358
+ - "max": Maximum of every N samples
359
+ - "min": Minimum of every N samples
360
+ channel_name: Name for the result trace (optional).
361
+
362
+ Returns:
363
+ Downsampled WaveformTrace.
364
+
365
+ Raises:
366
+ ValueError: If factor is less than 1 or method is unknown.
367
+
368
+ Example:
369
+ >>> small = downsample(large_trace, factor=10)
370
+
371
+ References:
372
+ MEM-012 (memory management)
373
+ """
374
+ if factor < 1:
375
+ raise ValueError(f"Factor must be >= 1, got {factor}")
376
+
377
+ if factor == 1:
378
+ return trace # No change needed
379
+
380
+ data = trace.data.astype(np.float64)
381
+
382
+ if anti_alias and method == "decimate":
383
+ # Apply anti-aliasing filter
384
+ nyquist = 0.5 / factor
385
+ b, a = sp_signal.butter(8, min(nyquist * 0.9, 0.99), btype="low")
386
+ data = sp_signal.filtfilt(b, a, data)
387
+
388
+ # Truncate to multiple of factor
389
+ n = len(data) // factor * factor
390
+ data = data[:n]
391
+
392
+ if method == "decimate":
393
+ result_data = data[::factor]
394
+ elif method == "average":
395
+ result_data = data.reshape(-1, factor).mean(axis=1)
396
+ elif method == "max":
397
+ result_data = data.reshape(-1, factor).max(axis=1)
398
+ elif method == "min":
399
+ result_data = data.reshape(-1, factor).min(axis=1)
400
+ else:
401
+ raise ValueError(f"Unknown method: {method}")
402
+
403
+ new_metadata = TraceMetadata(
404
+ sample_rate=trace.metadata.sample_rate / factor,
405
+ vertical_scale=trace.metadata.vertical_scale,
406
+ vertical_offset=trace.metadata.vertical_offset,
407
+ acquisition_time=trace.metadata.acquisition_time,
408
+ trigger_info=trace.metadata.trigger_info,
409
+ source_file=trace.metadata.source_file,
410
+ channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_ds{factor}",
411
+ )
412
+
413
+ return WaveformTrace(data=result_data, metadata=new_metadata)
@@ -0,0 +1,39 @@
1
+ """Onboarding and help system for Oscura.
2
+
3
+ This package provides interactive tutorials, context-sensitive help,
4
+ and guided analysis features for new users.
5
+ """
6
+
7
+ from oscura.onboarding.help import (
8
+ explain_result,
9
+ get_example,
10
+ get_help,
11
+ suggest_commands,
12
+ )
13
+ from oscura.onboarding.tutorials import (
14
+ Tutorial,
15
+ TutorialStep,
16
+ get_tutorial,
17
+ list_tutorials,
18
+ run_tutorial,
19
+ )
20
+ from oscura.onboarding.wizard import (
21
+ AnalysisWizard,
22
+ WizardStep,
23
+ run_wizard,
24
+ )
25
+
26
+ __all__ = [
27
+ "AnalysisWizard",
28
+ "Tutorial",
29
+ "TutorialStep",
30
+ "WizardStep",
31
+ "explain_result",
32
+ "get_example",
33
+ "get_help",
34
+ "get_tutorial",
35
+ "list_tutorials",
36
+ "run_tutorial",
37
+ "run_wizard",
38
+ "suggest_commands",
39
+ ]