oscura 0.0.1__py3-none-any.whl → 0.1.0__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.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.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.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,338 @@
1
+ """Base classes for trace transformations and pipeline stages.
2
+
3
+ This module implements the foundational abstract base classes for creating
4
+ custom trace transformations compatible with the Pipeline architecture.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from abc import ABC, abstractmethod
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ if TYPE_CHECKING:
13
+ from ..core.types import WaveformTrace
14
+
15
+
16
+ class TraceTransformer(ABC):
17
+ """Abstract base class for trace transformations.
18
+
19
+ All pipeline stages and custom transformations must inherit from this class.
20
+ Provides the fit/transform pattern similar to sklearn transformers.
21
+
22
+ The TraceTransformer enforces a consistent interface:
23
+ - transform(trace) -> trace: Required transformation method
24
+ - fit(trace) -> self: Optional learning/calibration method
25
+ - fit_transform(trace) -> trace: Convenience method
26
+ - get_params() / set_params(): Hyperparameter access
27
+ - clone(): Create a copy of the transformer
28
+
29
+ Example:
30
+ >>> class AmplitudeScaler(TraceTransformer):
31
+ ... def __init__(self, scale_factor=1.0):
32
+ ... self.scale_factor = scale_factor
33
+ ...
34
+ ... def transform(self, trace):
35
+ ... scaled_data = trace.data * self.scale_factor
36
+ ... return WaveformTrace(
37
+ ... data=scaled_data,
38
+ ... metadata=trace.metadata
39
+ ... )
40
+ ...
41
+ >>> scaler = AmplitudeScaler(scale_factor=2.0)
42
+ >>> result = scaler.transform(trace)
43
+
44
+ References:
45
+ API-004: TraceTransformer Base Class
46
+ sklearn.base.BaseEstimator, TransformerMixin
47
+ """
48
+
49
+ @abstractmethod
50
+ def transform(self, trace: WaveformTrace) -> WaveformTrace:
51
+ """Transform a trace.
52
+
53
+ Args:
54
+ trace: Input WaveformTrace to transform.
55
+
56
+ Returns:
57
+ Transformed WaveformTrace.
58
+
59
+ Raises:
60
+ NotImplementedError: If not implemented by subclass.
61
+ """
62
+ raise NotImplementedError(f"{self.__class__.__name__} must implement transform() method")
63
+
64
+ def fit(self, trace: WaveformTrace) -> TraceTransformer:
65
+ """Fit transformer to a reference trace (optional for stateful transformers).
66
+
67
+ This method is optional and should be overridden by stateful transformers
68
+ that need to learn parameters from a reference trace (e.g., normalization
69
+ statistics, adaptive filters).
70
+
71
+ Args:
72
+ trace: Reference WaveformTrace to fit to.
73
+
74
+ Returns:
75
+ Self for method chaining.
76
+
77
+ Example:
78
+ >>> class AdaptiveNormalizer(TraceTransformer):
79
+ ... def __init__(self):
80
+ ... self.mean_ = None
81
+ ... self.std_ = None
82
+ ...
83
+ ... def fit(self, trace):
84
+ ... self.mean_ = trace.data.mean()
85
+ ... self.std_ = trace.data.std()
86
+ ... return self
87
+ ...
88
+ ... def transform(self, trace):
89
+ ... normalized = (trace.data - self.mean_) / self.std_
90
+ ... return WaveformTrace(
91
+ ... data=normalized,
92
+ ... metadata=trace.metadata
93
+ ... )
94
+ """
95
+ # Default implementation: no fitting required
96
+ return self
97
+
98
+ def fit_transform(self, trace: WaveformTrace) -> WaveformTrace:
99
+ """Fit to trace, then transform it.
100
+
101
+ Convenience method that calls fit() followed by transform().
102
+
103
+ Args:
104
+ trace: Input WaveformTrace to fit and transform.
105
+
106
+ Returns:
107
+ Transformed WaveformTrace.
108
+
109
+ Example:
110
+ >>> normalizer = AdaptiveNormalizer()
111
+ >>> result = normalizer.fit_transform(reference_trace)
112
+ """
113
+ return self.fit(trace).transform(trace)
114
+
115
+ def get_params(self, deep: bool = True) -> dict[str, Any]:
116
+ """Get parameters for this transformer.
117
+
118
+ Args:
119
+ deep: If True, will return parameters for nested objects.
120
+
121
+ Returns:
122
+ Dictionary of parameter names mapped to their values.
123
+
124
+ Example:
125
+ >>> scaler = AmplitudeScaler(scale_factor=2.0)
126
+ >>> params = scaler.get_params()
127
+ >>> print(params)
128
+ {'scale_factor': 2.0}
129
+ """
130
+ params = {}
131
+ for key in dir(self):
132
+ # Skip private/magic attributes and methods
133
+ if key.startswith("_") or callable(getattr(self, key)):
134
+ continue
135
+ value = getattr(self, key)
136
+ params[key] = value
137
+
138
+ # Handle nested transformers if deep=True
139
+ if deep and hasattr(value, "get_params"):
140
+ nested_params = value.get_params(deep=True)
141
+ for nested_key, nested_value in nested_params.items():
142
+ params[f"{key}__{nested_key}"] = nested_value
143
+
144
+ return params
145
+
146
+ def set_params(self, **params: Any) -> TraceTransformer:
147
+ """Set parameters for this transformer.
148
+
149
+ Args:
150
+ **params: Parameter names and values to set.
151
+
152
+ Returns:
153
+ Self for method chaining.
154
+
155
+ Raises:
156
+ ValueError: If parameter name is invalid.
157
+
158
+ Example:
159
+ >>> scaler = AmplitudeScaler(scale_factor=1.0)
160
+ >>> scaler.set_params(scale_factor=3.0)
161
+ >>> print(scaler.scale_factor)
162
+ 3.0
163
+ """
164
+ if not params:
165
+ return self
166
+
167
+ valid_params = self.get_params(deep=False)
168
+
169
+ for key, value in params.items():
170
+ # Handle nested parameters (e.g., 'filter__cutoff')
171
+ if "__" in key:
172
+ nested_obj, nested_key = key.split("__", 1)
173
+ if nested_obj not in valid_params:
174
+ raise ValueError(
175
+ f"Invalid parameter {nested_obj} for transformer {self.__class__.__name__}"
176
+ )
177
+ nested = getattr(self, nested_obj)
178
+ if hasattr(nested, "set_params"):
179
+ nested.set_params(**{nested_key: value})
180
+ else:
181
+ raise ValueError(f"Parameter {nested_obj} does not support set_params")
182
+ else:
183
+ if key not in valid_params:
184
+ raise ValueError(
185
+ f"Invalid parameter {key} for transformer "
186
+ f"{self.__class__.__name__}. "
187
+ f"Valid parameters: {list(valid_params.keys())}"
188
+ )
189
+ setattr(self, key, value)
190
+
191
+ return self
192
+
193
+ def clone(self) -> TraceTransformer:
194
+ """Create a copy of this transformer with the same parameters.
195
+
196
+ Returns:
197
+ New instance of the transformer with same parameters.
198
+
199
+ Example:
200
+ >>> scaler = AmplitudeScaler(scale_factor=2.0)
201
+ >>> scaler_copy = scaler.clone()
202
+ >>> scaler_copy.scale_factor
203
+ 2.0
204
+ """
205
+ params = self.get_params(deep=False)
206
+ return self.__class__(**params)
207
+
208
+ def __getstate__(self) -> dict[str, Any]:
209
+ """Get state for pickling.
210
+
211
+ Returns:
212
+ Dictionary containing transformer state.
213
+ """
214
+ return self.__dict__.copy()
215
+
216
+ def __setstate__(self, state: dict[str, Any]) -> None:
217
+ """Set state from unpickling.
218
+
219
+ Args:
220
+ state: Dictionary containing transformer state.
221
+ """
222
+ self.__dict__.update(state)
223
+
224
+ def get_intermediate_result(self, key: str) -> Any:
225
+ """Get intermediate result from last transformation.
226
+
227
+ Some transformers cache intermediate results (e.g., FFT coefficients,
228
+ filter states) that can be accessed after transformation.
229
+
230
+ Args:
231
+ key: Name of intermediate result to retrieve.
232
+
233
+ Returns:
234
+ Intermediate result value.
235
+
236
+ Raises:
237
+ KeyError: If key not found or transformer doesn't support intermediates.
238
+
239
+ Example:
240
+ >>> filter = LowPassFilter(cutoff=1e6)
241
+ >>> result = filter.transform(trace)
242
+ >>> transfer_func = filter.get_intermediate_result('transfer_function')
243
+
244
+ References:
245
+ API-005: Intermediate Result Access
246
+ """
247
+ # Check if transformer has _intermediates cache
248
+ if not hasattr(self, "_intermediates"):
249
+ raise KeyError(f"{self.__class__.__name__} does not cache intermediate results")
250
+
251
+ intermediates = self._intermediates
252
+ if key not in intermediates:
253
+ available = list(intermediates.keys())
254
+ raise KeyError(
255
+ f"Intermediate '{key}' not found in {self.__class__.__name__}. "
256
+ f"Available: {available}"
257
+ )
258
+
259
+ return intermediates[key]
260
+
261
+ def has_intermediate_result(self, key: str) -> bool:
262
+ """Check if intermediate result is available.
263
+
264
+ Args:
265
+ key: Name of intermediate result.
266
+
267
+ Returns:
268
+ True if intermediate result exists.
269
+
270
+ Example:
271
+ >>> if filter.has_intermediate_result('impulse_response'):
272
+ ... impulse = filter.get_intermediate_result('impulse_response')
273
+
274
+ References:
275
+ API-005: Intermediate Result Access
276
+ """
277
+ if not hasattr(self, "_intermediates"):
278
+ return False
279
+ return key in self._intermediates
280
+
281
+ def list_intermediate_results(self) -> list[str]:
282
+ """List all available intermediate result keys.
283
+
284
+ Returns:
285
+ List of intermediate result names, or empty list if none available.
286
+
287
+ Example:
288
+ >>> print(filter.list_intermediate_results())
289
+ ['transfer_function', 'impulse_response', 'frequency_response']
290
+
291
+ References:
292
+ API-005: Intermediate Result Access
293
+ """
294
+ if not hasattr(self, "_intermediates"):
295
+ return []
296
+ return list(self._intermediates.keys())
297
+
298
+ def _cache_intermediate(self, key: str, value: Any) -> None:
299
+ """Cache an intermediate result for later access.
300
+
301
+ This is a protected method for subclasses to use when storing
302
+ intermediate computation results.
303
+
304
+ Args:
305
+ key: Name of intermediate result.
306
+ value: Value to cache.
307
+
308
+ Example (in subclass):
309
+ >>> def transform(self, trace):
310
+ ... fft_coeffs = compute_fft(trace)
311
+ ... self._cache_intermediate('fft_coeffs', fft_coeffs)
312
+ ... return processed_trace
313
+
314
+ References:
315
+ API-005: Intermediate Result Access
316
+ """
317
+ if not hasattr(self, "_intermediates"):
318
+ self._intermediates = {}
319
+ self._intermediates[key] = value
320
+
321
+ def _clear_intermediates(self) -> None:
322
+ """Clear all cached intermediate results.
323
+
324
+ Useful for freeing memory when intermediate results are no longer needed.
325
+
326
+ Example (in subclass):
327
+ >>> def transform(self, trace):
328
+ ... self._clear_intermediates() # Clear previous results
329
+ ... # ... perform transformation ...
330
+
331
+ References:
332
+ API-005: Intermediate Result Access
333
+ """
334
+ if hasattr(self, "_intermediates"):
335
+ self._intermediates.clear()
336
+
337
+
338
+ __all__ = ["TraceTransformer"]
@@ -0,0 +1,242 @@
1
+ """Functional composition operators for trace transformations.
2
+
3
+ This module implements compose() and pipe() functions for functional-style
4
+ trace processing, with support for operator overloading.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Callable
10
+ from functools import reduce, wraps
11
+ from typing import Any, TypeVar
12
+
13
+ from ..core.types import WaveformTrace
14
+
15
+ # Type variables for generic composition
16
+ T = TypeVar("T")
17
+ TraceFunc = Callable[[WaveformTrace], WaveformTrace]
18
+
19
+
20
+ def compose(*funcs: TraceFunc) -> TraceFunc:
21
+ """Compose functions right-to-left: compose(f, g, h)(x) == f(g(h(x))).
22
+
23
+ Creates a single function that applies the given functions in reverse order.
24
+ This follows mathematical function composition notation.
25
+
26
+ Args:
27
+ *funcs: Variable number of functions to compose. Each function should
28
+ take a WaveformTrace and return a WaveformTrace.
29
+
30
+ Returns:
31
+ Composite function that applies all functions in reverse order.
32
+
33
+ Raises:
34
+ ValueError: If no functions provided.
35
+
36
+ Example:
37
+ >>> import oscura as tk
38
+ >>> from functools import partial
39
+ >>> # Create composed analysis function
40
+ >>> analyze_signal = tk.compose(
41
+ ... tk.extract_thd,
42
+ ... partial(tk.fft, nfft=8192, window='hann'),
43
+ ... partial(tk.normalize, method='peak'),
44
+ ... partial(tk.low_pass, cutoff=5e6)
45
+ ... )
46
+ >>> # Apply to trace: low_pass -> normalize -> fft -> extract_thd
47
+ >>> thd = analyze_signal(trace)
48
+
49
+ References:
50
+ API-002: Function Composition Operators
51
+ toolz.functoolz
52
+ https://github.com/pytoolz/toolz
53
+ """
54
+ if not funcs:
55
+ raise ValueError("compose() requires at least one function")
56
+
57
+ if len(funcs) == 1:
58
+ return funcs[0]
59
+
60
+ def composed(x: WaveformTrace) -> WaveformTrace:
61
+ """Apply composed functions right-to-left."""
62
+ # Apply functions in reverse order (right to left)
63
+ return reduce(lambda val, func: func(val), reversed(funcs), x)
64
+
65
+ # Preserve function metadata
66
+ composed.__name__ = "compose(" + ", ".join(f.__name__ for f in funcs) + ")"
67
+ composed.__doc__ = f"Composition of {len(funcs)} functions"
68
+
69
+ return composed
70
+
71
+
72
+ def pipe(data: WaveformTrace, *funcs: TraceFunc) -> WaveformTrace:
73
+ """Apply functions left-to-right: pipe(x, f, g, h) == h(g(f(x))).
74
+
75
+ Applies the given functions sequentially to the data, passing the output
76
+ of each function to the next. This is more intuitive for sequential
77
+ processing pipelines.
78
+
79
+ Args:
80
+ data: Initial WaveformTrace to process.
81
+ *funcs: Variable number of functions to apply sequentially.
82
+
83
+ Returns:
84
+ Transformed WaveformTrace after applying all functions.
85
+
86
+ Example:
87
+ >>> import oscura as tk
88
+ >>> # Apply operations left-to-right
89
+ >>> result = tk.pipe(
90
+ ... trace,
91
+ ... tk.low_pass(cutoff=1e6),
92
+ ... tk.resample(rate=1e9),
93
+ ... tk.fft(nfft=8192)
94
+ ... )
95
+ >>> # Equivalent to: fft(resample(low_pass(trace)))
96
+
97
+ Advanced Example:
98
+ >>> # Use with partial application
99
+ >>> from functools import partial
100
+ >>> result = tk.pipe(
101
+ ... trace,
102
+ ... partial(tk.low_pass, cutoff=1e6),
103
+ ... partial(tk.normalize, method='zscore'),
104
+ ... partial(tk.fft, nfft=8192, window='hann')
105
+ ... )
106
+
107
+ References:
108
+ API-002: Function Composition Operators
109
+ toolz.pipe
110
+ """
111
+ # Apply functions left-to-right
112
+ return reduce(lambda val, func: func(val), funcs, data)
113
+
114
+
115
+ class Composable:
116
+ """Mixin class to enable >> operator for function composition.
117
+
118
+ This class provides the __rshift__ operator to enable pipe-style
119
+ composition using the >> syntax. Intended to be mixed into WaveformTrace
120
+ or used as a wrapper for transformer functions.
121
+
122
+ Example:
123
+ >>> # Enable >> operator on WaveformTrace
124
+ >>> result = trace >> low_pass(1e6) >> normalize() >> fft()
125
+ >>> # Equivalent to: fft(normalize(low_pass(trace)))
126
+
127
+ References:
128
+ API-002: Function Composition Operators
129
+ """
130
+
131
+ def __rshift__(self, func: Callable[[Any], Any]) -> Any:
132
+ """Enable >> operator for function application.
133
+
134
+ Args:
135
+ func: Function to apply to self.
136
+
137
+ Returns:
138
+ Result of applying func to self.
139
+
140
+ Example:
141
+ >>> result = trace >> low_pass(1e6)
142
+ """
143
+ return func(self)
144
+
145
+
146
+ def make_composable(func: Callable[..., WaveformTrace]) -> Callable[..., TraceFunc]:
147
+ """Decorator to make a function support partial application and composition.
148
+
149
+ Wraps a function so it can be used in compose() and pipe() with
150
+ partial argument application.
151
+
152
+ Args:
153
+ func: Function to wrap.
154
+
155
+ Returns:
156
+ Wrapped function that returns a partially applied function when
157
+ called without a trace argument.
158
+
159
+ Example:
160
+ >>> @make_composable
161
+ ... def scale(trace, factor=1.0):
162
+ ... return WaveformTrace(
163
+ ... data=trace.data * factor,
164
+ ... metadata=trace.metadata
165
+ ... )
166
+ >>> # Use with partial application
167
+ >>> double = scale(factor=2.0)
168
+ >>> result = double(trace)
169
+ >>> # Or in pipe
170
+ >>> result = pipe(trace, scale(factor=2.0), scale(factor=0.5))
171
+
172
+ References:
173
+ API-002: Function Composition Operators
174
+ """
175
+
176
+ @wraps(func)
177
+ def wrapper(*args: Any, **kwargs: Any) -> TraceFunc | WaveformTrace:
178
+ # If first arg is a WaveformTrace, apply function immediately
179
+ if args and isinstance(args[0], WaveformTrace):
180
+ return func(*args, **kwargs)
181
+
182
+ # Otherwise, return a partially applied function
183
+ def partial_func(trace: WaveformTrace) -> WaveformTrace:
184
+ return func(trace, *args, **kwargs)
185
+
186
+ return partial_func
187
+
188
+ return wrapper # type: ignore[return-value]
189
+
190
+
191
+ def curry(func: Callable[..., WaveformTrace]) -> Callable[..., TraceFunc]:
192
+ """Curry a function for easier composition.
193
+
194
+ Transforms a multi-argument function into a series of single-argument
195
+ functions. Useful for creating reusable transformation functions.
196
+
197
+ Args:
198
+ func: Function to curry.
199
+
200
+ Returns:
201
+ Curried version of the function.
202
+
203
+ Example:
204
+ >>> @curry
205
+ ... def scale_and_offset(trace, scale, offset):
206
+ ... return WaveformTrace(
207
+ ... data=trace.data * scale + offset,
208
+ ... metadata=trace.metadata
209
+ ... )
210
+ >>> # Create specialized functions
211
+ >>> double_and_shift = scale_and_offset(scale=2.0, offset=1.0)
212
+ >>> result = double_and_shift(trace)
213
+
214
+ References:
215
+ API-002: Function Composition Operators
216
+ Functional programming currying
217
+ """
218
+
219
+ @wraps(func)
220
+ def curried(*args: Any, **kwargs: Any) -> TraceFunc | WaveformTrace:
221
+ # If we have a WaveformTrace as first arg, apply immediately
222
+ if args and isinstance(args[0], WaveformTrace):
223
+ return func(*args, **kwargs)
224
+
225
+ # Return a function that waits for the trace
226
+ def partial(*more_args: Any, **more_kwargs: Any) -> WaveformTrace:
227
+ all_args = args + more_args
228
+ all_kwargs = {**kwargs, **more_kwargs}
229
+ return func(*all_args, **all_kwargs)
230
+
231
+ return partial
232
+
233
+ return curried # type: ignore[return-value]
234
+
235
+
236
+ __all__ = [
237
+ "Composable",
238
+ "compose",
239
+ "curry",
240
+ "make_composable",
241
+ "pipe",
242
+ ]