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,532 @@
1
+ """Parameter optimization via grid search and random search.
2
+
3
+ This module provides tools for finding optimal analysis parameters through
4
+ systematic or random search of the parameter space.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import itertools
10
+ from collections.abc import Callable
11
+ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
12
+ from dataclasses import dataclass
13
+ from typing import TYPE_CHECKING, Any, Literal
14
+
15
+ import numpy as np
16
+ import pandas as pd
17
+
18
+ from oscura.analyzers.waveform.spectral import thd as compute_thd
19
+ from oscura.core.exceptions import AnalysisError
20
+
21
+ if TYPE_CHECKING:
22
+ from numpy.typing import NDArray
23
+
24
+ from oscura.core.types import WaveformTrace
25
+
26
+ ScoringFunction = Callable[[WaveformTrace, dict[str, Any]], float]
27
+ else:
28
+ ScoringFunction = Callable
29
+
30
+
31
+ @dataclass
32
+ class SearchResult:
33
+ """Result from parameter search.
34
+
35
+ Attributes:
36
+ best_params: Dictionary of best parameters found.
37
+ best_score: Best score achieved.
38
+ all_results: DataFrame with all parameter combinations and scores.
39
+ cv_scores: Cross-validation scores if CV was used.
40
+
41
+ Example:
42
+ >>> result = search.fit(traces)
43
+ >>> print(f"Best params: {result.best_params}")
44
+ >>> print(f"Best score: {result.best_score}")
45
+
46
+ References:
47
+ API-014: Parameter Grid Search
48
+ """
49
+
50
+ best_params: dict[str, Any]
51
+ best_score: float
52
+ all_results: pd.DataFrame
53
+ cv_scores: NDArray[np.float64] | None = None
54
+
55
+
56
+ def _default_snr_scorer(trace: WaveformTrace, params: dict[str, Any]) -> float:
57
+ """Default SNR scoring function.
58
+
59
+ Args:
60
+ trace: Waveform trace to analyze.
61
+ params: Parameters to apply (not used in basic SNR).
62
+
63
+ Returns:
64
+ Signal-to-noise ratio in dB.
65
+ """
66
+ # Simple SNR: signal power / noise power
67
+ # Assume first half is signal, second half is noise (oversimplified)
68
+ data = trace.data
69
+ mid = len(data) // 2
70
+ signal_power = np.mean(data[:mid] ** 2)
71
+ noise_power = np.mean((data[mid:] - np.mean(data[mid:])) ** 2)
72
+
73
+ if noise_power == 0:
74
+ return float("inf")
75
+
76
+ snr = signal_power / noise_power
77
+ return float(10 * np.log10(snr))
78
+
79
+
80
+ def _default_thd_scorer(trace: WaveformTrace, params: dict[str, Any]) -> float:
81
+ """Default THD scoring function.
82
+
83
+ Args:
84
+ trace: Waveform trace to analyze.
85
+ params: Parameters to apply (not used in basic THD).
86
+
87
+ Returns:
88
+ Negative THD percentage (negative because lower THD is better, but we maximize scores).
89
+ """
90
+ # Compute THD and return negative value (lower THD = better = higher score)
91
+ thd_value = compute_thd(trace)
92
+ return float(-thd_value)
93
+
94
+
95
+ class GridSearchCV:
96
+ """Grid search over parameter space with optional cross-validation.
97
+
98
+ Systematically evaluates all combinations of parameters to find the
99
+ optimal configuration.
100
+
101
+ Example:
102
+ >>> from oscura.optimization.search import GridSearchCV
103
+ >>> param_grid = {
104
+ ... 'cutoff': [1e5, 5e5, 1e6],
105
+ ... 'order': [2, 4, 6]
106
+ ... }
107
+ >>> search = GridSearchCV(
108
+ ... param_grid=param_grid,
109
+ ... scoring='snr',
110
+ ... cv=3
111
+ ... )
112
+ >>> result = search.fit(traces, apply_filter)
113
+ >>> print(result.best_params)
114
+
115
+ References:
116
+ API-014: Parameter Grid Search
117
+ """
118
+
119
+ def __init__(
120
+ self,
121
+ param_grid: dict[str, list[Any]],
122
+ scoring: Literal["snr", "thd"] | ScoringFunction = "snr",
123
+ cv: int | None = None,
124
+ *,
125
+ parallel: bool = True,
126
+ max_workers: int | None = None,
127
+ use_threads: bool = True,
128
+ ) -> None:
129
+ """Initialize grid search.
130
+
131
+ Args:
132
+ param_grid: Dictionary mapping parameter names to lists of values.
133
+ scoring: Scoring function. Built-in: 'snr', 'thd', or custom callable.
134
+ cv: Number of cross-validation folds. None for no CV.
135
+ parallel: Enable parallel evaluation.
136
+ max_workers: Maximum parallel workers.
137
+ use_threads: Use threads instead of processes.
138
+
139
+ Raises:
140
+ AnalysisError: If scoring function is invalid.
141
+
142
+ Example:
143
+ >>> param_grid = {'cutoff': [1e6, 2e6], 'order': [4, 6]}
144
+ >>> search = GridSearchCV(param_grid, scoring='snr', cv=3)
145
+ """
146
+ self.param_grid = param_grid
147
+ self.cv = cv
148
+ self.parallel = parallel
149
+ self.max_workers = max_workers
150
+ self.use_threads = use_threads
151
+
152
+ # Set scoring function
153
+ if scoring == "snr":
154
+ self.scoring_fn = _default_snr_scorer
155
+ elif scoring == "thd":
156
+ self.scoring_fn = _default_thd_scorer
157
+ elif callable(scoring):
158
+ self.scoring_fn = scoring # type: ignore[assignment]
159
+ else:
160
+ raise AnalysisError(f"Unknown scoring function: {scoring}")
161
+
162
+ self.best_params_: dict[str, Any] | None = None
163
+ self.best_score_: float | None = None
164
+ self.results_df_: pd.DataFrame | None = None
165
+
166
+ def fit(
167
+ self,
168
+ traces: list[WaveformTrace] | WaveformTrace,
169
+ transform_fn: Callable[[WaveformTrace, dict[str, Any]], WaveformTrace],
170
+ ) -> SearchResult:
171
+ """Fit grid search on traces.
172
+
173
+ Evaluates all parameter combinations and finds the best.
174
+
175
+ Args:
176
+ traces: Trace or list of traces to evaluate on.
177
+ transform_fn: Function that applies parameters to trace.
178
+ Should accept (trace, **params) and return transformed trace.
179
+
180
+ Returns:
181
+ SearchResult with best parameters and all results.
182
+
183
+ Example:
184
+ >>> def apply_filter(trace, cutoff, order):
185
+ ... return lowpass_filter(trace, cutoff=cutoff, order=order)
186
+ >>> result = search.fit(traces, apply_filter)
187
+
188
+ References:
189
+ API-014: Parameter Grid Search
190
+ """
191
+ # Convert single trace to list
192
+ if not isinstance(traces, list):
193
+ traces = [traces]
194
+
195
+ # Generate all parameter combinations
196
+ param_combinations = self._generate_combinations()
197
+
198
+ # Evaluate each combination
199
+ results = self._evaluate_combinations(param_combinations, traces, transform_fn)
200
+
201
+ # Convert to DataFrame
202
+ self.results_df_ = pd.DataFrame(results)
203
+
204
+ # Find best
205
+ best_idx = self.results_df_["mean_score"].idxmax()
206
+ best_row = self.results_df_.iloc[best_idx]
207
+
208
+ self.best_params_ = {k: best_row[k] for k in self.param_grid}
209
+ self.best_score_ = float(best_row["mean_score"])
210
+
211
+ # Collect CV scores if available
212
+ cv_scores = None
213
+ if self.cv:
214
+ cv_cols = [c for c in self.results_df_.columns if c.startswith("cv_")]
215
+ if cv_cols:
216
+ cv_scores = self.results_df_.loc[best_idx, cv_cols].values
217
+
218
+ return SearchResult(
219
+ best_params=self.best_params_,
220
+ best_score=self.best_score_,
221
+ all_results=self.results_df_,
222
+ cv_scores=cv_scores,
223
+ )
224
+
225
+ def _generate_combinations(self) -> list[dict[str, Any]]:
226
+ """Generate all parameter combinations from grid.
227
+
228
+ Returns:
229
+ List of parameter dictionaries.
230
+ """
231
+ keys = list(self.param_grid.keys())
232
+ values = [self.param_grid[k] for k in keys]
233
+
234
+ combinations = []
235
+ for combo in itertools.product(*values):
236
+ combinations.append(dict(zip(keys, combo, strict=False)))
237
+
238
+ return combinations
239
+
240
+ def _evaluate_combinations(
241
+ self,
242
+ param_combinations: list[dict[str, Any]],
243
+ traces: list[WaveformTrace],
244
+ transform_fn: Callable[[WaveformTrace, dict[str, Any]], WaveformTrace],
245
+ ) -> list[dict[str, Any]]:
246
+ """Evaluate all parameter combinations.
247
+
248
+ Args:
249
+ param_combinations: List of parameter dicts to evaluate.
250
+ traces: Traces to evaluate on.
251
+ transform_fn: Transformation function.
252
+
253
+ Returns:
254
+ List of result dictionaries.
255
+ """
256
+ if self.parallel:
257
+ return self._evaluate_parallel(param_combinations, traces, transform_fn)
258
+ else:
259
+ return self._evaluate_sequential(param_combinations, traces, transform_fn)
260
+
261
+ def _evaluate_one(
262
+ self,
263
+ params: dict[str, Any],
264
+ traces: list[WaveformTrace],
265
+ transform_fn: Callable[[WaveformTrace, dict[str, Any]], WaveformTrace],
266
+ ) -> dict[str, Any]:
267
+ """Evaluate one parameter combination.
268
+
269
+ Args:
270
+ params: Parameters to evaluate.
271
+ traces: Traces to evaluate on.
272
+ transform_fn: Transformation function.
273
+
274
+ Returns:
275
+ Result dictionary with scores.
276
+ """
277
+ scores: list[float] = []
278
+
279
+ if self.cv:
280
+ # Cross-validation - split traces into folds
281
+ fold_size = len(traces) // self.cv
282
+ for i in range(self.cv):
283
+ # Select fold
284
+ start = i * fold_size
285
+ end = start + fold_size if i < self.cv - 1 else len(traces)
286
+ fold_traces = traces[start:end]
287
+
288
+ # Evaluate on fold
289
+ fold_scores = []
290
+ for trace in fold_traces:
291
+ transformed = transform_fn(trace, **params) # type: ignore[call-arg]
292
+ score = self.scoring_fn(transformed, params)
293
+ fold_scores.append(score)
294
+
295
+ scores.append(float(np.mean(fold_scores)))
296
+
297
+ else:
298
+ # No CV - evaluate on all traces
299
+ for trace in traces:
300
+ transformed = transform_fn(trace, **params) # type: ignore[call-arg]
301
+ score = self.scoring_fn(transformed, params)
302
+ scores.append(score)
303
+
304
+ # Build result
305
+ result = params.copy()
306
+ result["mean_score"] = float(np.mean(scores))
307
+ result["std_score"] = float(np.std(scores))
308
+
309
+ if self.cv:
310
+ for i, score in enumerate(scores):
311
+ result[f"cv_{i}"] = float(score)
312
+
313
+ return result
314
+
315
+ def _evaluate_sequential(
316
+ self,
317
+ param_combinations: list[dict[str, Any]],
318
+ traces: list[WaveformTrace],
319
+ transform_fn: Callable[[WaveformTrace, dict[str, Any]], WaveformTrace],
320
+ ) -> list[dict[str, Any]]:
321
+ """Evaluate combinations sequentially.
322
+
323
+ Args:
324
+ param_combinations: Parameter combinations.
325
+ traces: Traces to evaluate on.
326
+ transform_fn: Transformation function.
327
+
328
+ Returns:
329
+ List of results.
330
+ """
331
+ results = []
332
+ for params in param_combinations:
333
+ result = self._evaluate_one(params, traces, transform_fn)
334
+ results.append(result)
335
+ return results
336
+
337
+ def _evaluate_parallel(
338
+ self,
339
+ param_combinations: list[dict[str, Any]],
340
+ traces: list[WaveformTrace],
341
+ transform_fn: Callable[[WaveformTrace, dict[str, Any]], WaveformTrace],
342
+ ) -> list[dict[str, Any]]:
343
+ """Evaluate combinations in parallel.
344
+
345
+ Args:
346
+ param_combinations: Parameter combinations.
347
+ traces: Traces to evaluate on.
348
+ transform_fn: Transformation function.
349
+
350
+ Returns:
351
+ List of results.
352
+ """
353
+ executor_class = ThreadPoolExecutor if self.use_threads else ProcessPoolExecutor
354
+
355
+ with executor_class(max_workers=self.max_workers) as executor:
356
+ futures = {
357
+ executor.submit(self._evaluate_one, params, traces, transform_fn): params
358
+ for params in param_combinations
359
+ }
360
+
361
+ results = []
362
+ for future in as_completed(futures):
363
+ result = future.result()
364
+ results.append(result)
365
+
366
+ return results
367
+
368
+
369
+ class RandomizedSearchCV:
370
+ """Random search over parameter distributions.
371
+
372
+ Samples random combinations from parameter distributions rather than
373
+ exhaustively evaluating all combinations.
374
+
375
+ Example:
376
+ >>> from oscura.optimization.search import RandomizedSearchCV
377
+ >>> import numpy as np
378
+ >>> param_distributions = {
379
+ ... 'cutoff': lambda: np.random.uniform(1e5, 1e7),
380
+ ... 'order': lambda: np.random.choice([2, 4, 6, 8])
381
+ ... }
382
+ >>> search = RandomizedSearchCV(
383
+ ... param_distributions=param_distributions,
384
+ ... n_iter=20,
385
+ ... scoring='snr'
386
+ ... )
387
+ >>> result = search.fit(traces, apply_filter)
388
+
389
+ References:
390
+ API-014: Parameter Grid Search
391
+ """
392
+
393
+ def __init__(
394
+ self,
395
+ param_distributions: dict[str, Callable[[], Any]],
396
+ n_iter: int = 10,
397
+ scoring: Literal["snr", "thd"] | ScoringFunction = "snr",
398
+ cv: int | None = None,
399
+ *,
400
+ parallel: bool = True,
401
+ max_workers: int | None = None,
402
+ use_threads: bool = True,
403
+ random_state: int | None = None,
404
+ ) -> None:
405
+ """Initialize randomized search.
406
+
407
+ Args:
408
+ param_distributions: Dict mapping parameter names to sampling functions.
409
+ n_iter: Number of parameter combinations to sample.
410
+ scoring: Scoring function.
411
+ cv: Number of cross-validation folds.
412
+ parallel: Enable parallel evaluation.
413
+ max_workers: Maximum parallel workers.
414
+ use_threads: Use threads instead of processes.
415
+ random_state: Random seed for reproducibility.
416
+
417
+ Raises:
418
+ AnalysisError: If scoring function is invalid.
419
+
420
+ Example:
421
+ >>> param_dist = {'cutoff': lambda: np.random.uniform(1e5, 1e7)}
422
+ >>> search = RandomizedSearchCV(param_dist, n_iter=50)
423
+ """
424
+ self.param_distributions = param_distributions
425
+ self.n_iter = n_iter
426
+ self.cv = cv
427
+ self.parallel = parallel
428
+ self.max_workers = max_workers
429
+ self.use_threads = use_threads
430
+
431
+ if random_state is not None:
432
+ np.random.seed(random_state)
433
+
434
+ # Set scoring function
435
+ if scoring == "snr":
436
+ self.scoring_fn = _default_snr_scorer
437
+ elif scoring == "thd":
438
+ self.scoring_fn = _default_thd_scorer
439
+ elif callable(scoring):
440
+ self.scoring_fn = scoring # type: ignore[assignment]
441
+ else:
442
+ raise AnalysisError(f"Unknown scoring function: {scoring}")
443
+
444
+ self.best_params_: dict[str, Any] | None = None
445
+ self.best_score_: float | None = None
446
+ self.results_df_: pd.DataFrame | None = None
447
+
448
+ def fit(
449
+ self,
450
+ traces: list[WaveformTrace] | WaveformTrace,
451
+ transform_fn: Callable[[WaveformTrace, dict[str, Any]], WaveformTrace],
452
+ ) -> SearchResult:
453
+ """Fit randomized search on traces.
454
+
455
+ Args:
456
+ traces: Trace or list of traces to evaluate on.
457
+ transform_fn: Function that applies parameters to trace.
458
+
459
+ Returns:
460
+ SearchResult with best parameters.
461
+
462
+ Example:
463
+ >>> result = search.fit(traces, apply_filter)
464
+ >>> print(f"Best cutoff: {result.best_params['cutoff']:.2e}")
465
+
466
+ References:
467
+ API-014: Parameter Grid Search
468
+ """
469
+ # Convert single trace to list
470
+ if not isinstance(traces, list):
471
+ traces = [traces]
472
+
473
+ # Sample parameter combinations
474
+ param_combinations = self._sample_combinations()
475
+
476
+ # Reuse grid search evaluation logic
477
+ grid_search = GridSearchCV(
478
+ param_grid={}, # Not used
479
+ scoring=self.scoring_fn,
480
+ cv=self.cv,
481
+ parallel=self.parallel,
482
+ max_workers=self.max_workers,
483
+ use_threads=self.use_threads,
484
+ )
485
+
486
+ results = grid_search._evaluate_combinations(param_combinations, traces, transform_fn)
487
+
488
+ # Convert to DataFrame
489
+ self.results_df_ = pd.DataFrame(results)
490
+
491
+ # Find best
492
+ best_idx = self.results_df_["mean_score"].idxmax()
493
+ best_row = self.results_df_.iloc[best_idx]
494
+
495
+ self.best_params_ = {k: best_row[k] for k in self.param_distributions}
496
+ self.best_score_ = float(best_row["mean_score"])
497
+
498
+ # Collect CV scores if available
499
+ cv_scores = None
500
+ if self.cv:
501
+ cv_cols = [c for c in self.results_df_.columns if c.startswith("cv_")]
502
+ if cv_cols:
503
+ cv_scores = self.results_df_.loc[best_idx, cv_cols].values
504
+
505
+ return SearchResult(
506
+ best_params=self.best_params_,
507
+ best_score=self.best_score_,
508
+ all_results=self.results_df_,
509
+ cv_scores=cv_scores,
510
+ )
511
+
512
+ def _sample_combinations(self) -> list[dict[str, Any]]:
513
+ """Sample random parameter combinations.
514
+
515
+ Returns:
516
+ List of sampled parameter dictionaries.
517
+ """
518
+ combinations = []
519
+
520
+ for _ in range(self.n_iter):
521
+ params = {key: sampler() for key, sampler in self.param_distributions.items()}
522
+ combinations.append(params)
523
+
524
+ return combinations
525
+
526
+
527
+ __all__ = [
528
+ "GridSearchCV",
529
+ "RandomizedSearchCV",
530
+ "ScoringFunction",
531
+ "SearchResult",
532
+ ]
@@ -0,0 +1,43 @@
1
+ """Pipeline architecture for composable trace transformations.
2
+
3
+ This package provides pipeline composition, functional operators, and
4
+ base classes for building reusable trace processing workflows.
5
+
6
+ - RE-INT-001: RE Pipeline Integration
7
+ """
8
+
9
+ from .base import TraceTransformer
10
+ from .composition import Composable, compose, curry, make_composable, pipe
11
+ from .pipeline import Pipeline
12
+
13
+ # RE-INT-001: Reverse Engineering Pipeline
14
+ from .reverse_engineering import (
15
+ FlowInfo,
16
+ MessageTypeInfo,
17
+ ProtocolCandidate,
18
+ REAnalysisResult,
19
+ REPipeline,
20
+ StageResult,
21
+ analyze,
22
+ )
23
+
24
+ __all__ = [
25
+ "Composable",
26
+ # RE-INT-001: Reverse Engineering Pipeline
27
+ "FlowInfo",
28
+ "MessageTypeInfo",
29
+ # Pipeline
30
+ "Pipeline",
31
+ "ProtocolCandidate",
32
+ "REAnalysisResult",
33
+ "REPipeline",
34
+ "StageResult",
35
+ # Base classes
36
+ "TraceTransformer",
37
+ "analyze",
38
+ # Composition
39
+ "compose",
40
+ "curry",
41
+ "make_composable",
42
+ "pipe",
43
+ ]