oscura 0.0.1__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
oscura/cli/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """Oscura Command-Line Interface.
2
+
3
+ This module provides command-line tools for signal analysis workflows.
4
+
5
+
6
+ Example:
7
+ $ oscura --help
8
+ $ oscura characterize signal.wfm
9
+ $ oscura decode uart.wfm --protocol uart
10
+ """
11
+
12
+ from oscura.cli.main import cli
13
+
14
+ __all__ = ["cli"]
oscura/cli/batch.py ADDED
@@ -0,0 +1,339 @@
1
+ """Oscura Batch Command implementing CLI-004.
2
+
3
+ Provides CLI for batch processing multiple files with parallel execution support.
4
+
5
+
6
+ Example:
7
+ $ oscura batch '*.wfm' --analysis characterize
8
+ $ oscura batch 'test_*.wfm' --analysis decode --parallel 4
9
+ $ oscura batch 'captures/*.wfm' --analysis spectrum --save-summary results.csv
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import csv
15
+ import glob
16
+ import logging
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ import click
21
+
22
+ from oscura.cli.main import format_output
23
+
24
+ logger = logging.getLogger("oscura.cli.batch")
25
+
26
+
27
+ @click.command() # type: ignore[misc]
28
+ @click.argument("pattern") # type: ignore[misc]
29
+ @click.option( # type: ignore[misc]
30
+ "--analysis",
31
+ type=click.Choice(["characterize", "decode", "spectrum"], case_sensitive=False),
32
+ required=True,
33
+ help="Type of analysis to perform on each file.",
34
+ )
35
+ @click.option( # type: ignore[misc]
36
+ "--parallel",
37
+ type=int,
38
+ default=1,
39
+ help="Number of files to process concurrently (default: 1).",
40
+ )
41
+ @click.option( # type: ignore[misc]
42
+ "--output",
43
+ type=click.Choice(["json", "csv", "html", "table"], case_sensitive=False),
44
+ default="table",
45
+ help="Output format (default: table).",
46
+ )
47
+ @click.option( # type: ignore[misc]
48
+ "--save-summary",
49
+ type=click.Path(),
50
+ default=None,
51
+ help="Save aggregated results to file (CSV format).",
52
+ )
53
+ @click.option( # type: ignore[misc]
54
+ "--continue-on-error",
55
+ is_flag=True,
56
+ help="Continue processing even if individual files fail.",
57
+ )
58
+ @click.pass_context # type: ignore[misc]
59
+ def batch(
60
+ ctx: click.Context,
61
+ pattern: str,
62
+ analysis: str,
63
+ parallel: int,
64
+ output: str,
65
+ save_summary: str | None,
66
+ continue_on_error: bool,
67
+ ) -> None:
68
+ """Batch process multiple files.
69
+
70
+ Processes all files matching the given pattern with the specified analysis.
71
+ Supports parallel processing for faster execution on multi-core systems.
72
+
73
+ Args:
74
+ ctx: Click context object.
75
+ pattern: Glob pattern to match files.
76
+ analysis: Type of analysis (characterize, decode, spectrum).
77
+ parallel: Number of parallel workers.
78
+ output: Output format (json, csv, html, table).
79
+ save_summary: Path to save CSV summary file.
80
+ continue_on_error: Continue processing if individual files fail.
81
+
82
+ Raises:
83
+ Exception: If batch processing fails or no files found.
84
+
85
+ Examples:
86
+
87
+ \b
88
+ # Process all WFM files with characterization
89
+ $ oscura batch '*.wfm' --analysis characterize
90
+
91
+ \b
92
+ # Parallel processing with 4 workers
93
+ $ oscura batch 'test_run_*/*.wfm' \\
94
+ --analysis characterize \\
95
+ --parallel 4 \\
96
+ --save-summary results.csv
97
+
98
+ \b
99
+ # Decode all captures, continue on errors
100
+ $ oscura batch 'captures/*.wfm' \\
101
+ --analysis decode \\
102
+ --continue-on-error
103
+ """
104
+ verbose = ctx.obj.get("verbose", 0)
105
+
106
+ if verbose:
107
+ logger.info(f"Batch processing pattern: {pattern}")
108
+ logger.info(f"Analysis type: {analysis}")
109
+ logger.info(f"Parallel workers: {parallel}")
110
+
111
+ try:
112
+ # Expand glob pattern
113
+ files = glob.glob(pattern, recursive=True) # noqa: PTH207
114
+
115
+ if not files:
116
+ click.echo(f"No files matched pattern: {pattern}", err=True)
117
+ ctx.exit(1)
118
+
119
+ logger.info(f"Found {len(files)} files to process")
120
+
121
+ # Perform batch analysis
122
+ results = _perform_batch_analysis(
123
+ files=files,
124
+ analysis_type=analysis,
125
+ parallel=parallel,
126
+ continue_on_error=continue_on_error,
127
+ verbose=verbose,
128
+ )
129
+
130
+ # Save summary if requested
131
+ if save_summary:
132
+ _save_summary(results, save_summary)
133
+ logger.info(f"Summary saved to {save_summary}")
134
+
135
+ # Output aggregated results
136
+ summary = _generate_summary(results)
137
+ formatted = format_output(summary, output)
138
+ click.echo(formatted)
139
+
140
+ except Exception as e:
141
+ logger.error(f"Batch processing failed: {e}")
142
+ if verbose > 1:
143
+ raise
144
+ click.echo(f"Error: {e}", err=True)
145
+ ctx.exit(1)
146
+
147
+
148
+ def _perform_batch_analysis(
149
+ files: list[str],
150
+ analysis_type: str,
151
+ parallel: int,
152
+ continue_on_error: bool,
153
+ verbose: int,
154
+ ) -> list[dict[str, Any]]:
155
+ """Perform batch analysis on multiple files.
156
+
157
+ Uses concurrent.futures for parallel processing when parallel > 1.
158
+
159
+ Args:
160
+ files: List of file paths to process.
161
+ analysis_type: Type of analysis to perform.
162
+ parallel: Number of parallel workers.
163
+ continue_on_error: Whether to continue on errors.
164
+ verbose: Verbosity level.
165
+
166
+ Returns:
167
+ List of result dictionaries, one per file.
168
+
169
+ Raises:
170
+ Exception: If analysis fails and continue_on_error is False.
171
+ """
172
+ from concurrent.futures import ThreadPoolExecutor, as_completed
173
+
174
+ def analyze_single_file(file_path: str) -> dict[str, Any]:
175
+ """Analyze a single file and return results.
176
+
177
+ Args:
178
+ file_path: Path to waveform file to analyze.
179
+
180
+ Returns:
181
+ Dictionary containing analysis results.
182
+ """
183
+ import numpy as np
184
+
185
+ from oscura.analyzers.waveform.measurements import fall_time, rise_time
186
+ from oscura.analyzers.waveform.spectral import fft, thd
187
+ from oscura.inference import detect_protocol
188
+ from oscura.loaders import load
189
+
190
+ # Load trace
191
+ trace = load(file_path)
192
+ sample_rate = trace.metadata.sample_rate
193
+
194
+ # Base result
195
+ result: dict[str, Any] = {
196
+ "file": str(Path(file_path).name),
197
+ "status": "success",
198
+ "analysis_type": analysis_type,
199
+ "samples": len(trace.data), # type: ignore[union-attr]
200
+ "sample_rate": f"{sample_rate / 1e6:.1f} MHz",
201
+ }
202
+
203
+ # Add analysis-specific results
204
+ if analysis_type == "characterize":
205
+ # Pass WaveformTrace directly to functions (they expect WaveformTrace)
206
+ rt = rise_time(trace) # type: ignore[arg-type]
207
+ ft = fall_time(trace) # type: ignore[arg-type]
208
+ result.update(
209
+ {
210
+ "rise_time": f"{rt * 1e9:.2f} ns" if not np.isnan(rt) else "N/A",
211
+ "fall_time": f"{ft * 1e9:.2f} ns" if not np.isnan(ft) else "N/A",
212
+ }
213
+ )
214
+ elif analysis_type == "decode":
215
+ detected = detect_protocol(trace) # type: ignore[arg-type]
216
+ result.update(
217
+ {
218
+ "protocol": detected.get("protocol", "unknown"),
219
+ "confidence": f"{detected.get('confidence', 0) * 100:.0f}%",
220
+ }
221
+ )
222
+ elif analysis_type == "spectrum":
223
+ # Pass WaveformTrace directly to FFT functions
224
+ freqs, mags = fft(trace) # type: ignore[misc, arg-type]
225
+ if len(mags) > 0:
226
+ peak_idx = int(np.argmax(mags))
227
+ peak_freq = freqs[peak_idx]
228
+ else:
229
+ peak_freq = 0.0
230
+ thd_val = thd(trace) # type: ignore[arg-type]
231
+ result.update(
232
+ {
233
+ "peak_frequency": f"{peak_freq / 1e6:.3f} MHz",
234
+ "thd": f"{thd_val:.1f} dB" if not np.isnan(thd_val) else "N/A",
235
+ }
236
+ )
237
+
238
+ return result
239
+
240
+ results: list[dict[str, Any]] = []
241
+
242
+ if parallel > 1:
243
+ # Parallel processing using ThreadPoolExecutor
244
+ with ThreadPoolExecutor(max_workers=parallel) as executor:
245
+ future_to_file = {executor.submit(analyze_single_file, f): f for f in files}
246
+
247
+ for i, future in enumerate(as_completed(future_to_file), 1):
248
+ file_path = future_to_file[future]
249
+ if verbose:
250
+ logger.info(f"[{i}/{len(files)}] Completed {Path(file_path).name}")
251
+
252
+ try:
253
+ result = future.result()
254
+ results.append(result)
255
+ except Exception as e:
256
+ logger.error(f"Failed to process {file_path}: {e}")
257
+ if continue_on_error:
258
+ results.append(
259
+ {
260
+ "file": str(Path(file_path).name),
261
+ "status": "error",
262
+ "error": str(e),
263
+ }
264
+ )
265
+ else:
266
+ raise
267
+ else:
268
+ # Sequential processing
269
+ for i, file_path in enumerate(files, 1):
270
+ if verbose:
271
+ logger.info(f"[{i}/{len(files)}] Processing {file_path}")
272
+
273
+ try:
274
+ result = analyze_single_file(file_path)
275
+ results.append(result)
276
+ except Exception as e:
277
+ logger.error(f"Failed to process {file_path}: {e}")
278
+ if continue_on_error:
279
+ results.append(
280
+ {
281
+ "file": str(Path(file_path).name),
282
+ "status": "error",
283
+ "error": str(e),
284
+ }
285
+ )
286
+ else:
287
+ raise
288
+
289
+ return results
290
+
291
+
292
+ def _generate_summary(results: list[dict[str, Any]]) -> dict[str, Any]:
293
+ """Generate summary statistics from batch results.
294
+
295
+ Args:
296
+ results: List of individual file results.
297
+
298
+ Returns:
299
+ Summary dictionary with aggregated statistics.
300
+ """
301
+ total = len(results)
302
+ successful = sum(1 for r in results if r.get("status") == "success")
303
+ failed = total - successful
304
+
305
+ summary: dict[str, Any] = {
306
+ "total_files": total,
307
+ "successful": successful,
308
+ "failed": failed,
309
+ "success_rate": f"{successful / total * 100:.1f}%" if total > 0 else "N/A",
310
+ }
311
+
312
+ # Add analysis-specific aggregations
313
+ if results and successful > 0:
314
+ summary["note"] = "Detailed per-file results available in JSON/CSV output"
315
+
316
+ return summary
317
+
318
+
319
+ def _save_summary(results: list[dict[str, Any]], output_path: str) -> None:
320
+ """Save batch results to CSV file.
321
+
322
+ Args:
323
+ results: List of result dictionaries.
324
+ output_path: Path to save CSV file.
325
+ """
326
+ if not results:
327
+ return
328
+
329
+ # Get all unique keys across all results
330
+ all_keys: set[str] = set()
331
+ for result in results:
332
+ all_keys.update(result.keys())
333
+
334
+ fieldnames = sorted(all_keys)
335
+
336
+ with open(output_path, "w", newline="") as f:
337
+ writer = csv.DictWriter(f, fieldnames=fieldnames)
338
+ writer.writeheader()
339
+ writer.writerows(results)
@@ -0,0 +1,273 @@
1
+ """Oscura Characterize Command implementing CLI-002.
2
+
3
+ Provides CLI for buffer/signal characterization with automatic logic family
4
+ detection and optional reference comparison.
5
+
6
+
7
+ Example:
8
+ $ oscura characterize 74hc04_output.wfm
9
+ $ oscura characterize signal.wfm --logic-family CMOS_3V3
10
+ $ oscura characterize signal.wfm --compare reference.wfm --output html
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ from pathlib import Path
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ import click
20
+
21
+ from oscura.cli.main import format_output
22
+
23
+ if TYPE_CHECKING:
24
+ from oscura.core.types import WaveformTrace
25
+
26
+ logger = logging.getLogger("oscura.cli.characterize")
27
+
28
+
29
+ @click.command() # type: ignore[misc]
30
+ @click.argument("file", type=click.Path(exists=True)) # type: ignore[misc]
31
+ @click.option( # type: ignore[misc]
32
+ "--type",
33
+ "analysis_type",
34
+ type=click.Choice(["buffer", "signal", "power"], case_sensitive=False),
35
+ default="buffer",
36
+ help="Type of characterization to perform.",
37
+ )
38
+ @click.option( # type: ignore[misc]
39
+ "--logic-family",
40
+ type=click.Choice(
41
+ ["TTL", "CMOS", "CMOS_3V3", "CMOS_5V", "LVTTL", "LVCMOS", "auto"],
42
+ case_sensitive=False,
43
+ ),
44
+ default="auto",
45
+ help="Logic family for buffer characterization (default: auto-detect).",
46
+ )
47
+ @click.option( # type: ignore[misc]
48
+ "--compare",
49
+ type=click.Path(exists=True),
50
+ default=None,
51
+ help="Reference file for comparison analysis.",
52
+ )
53
+ @click.option( # type: ignore[misc]
54
+ "--output",
55
+ type=click.Choice(["json", "csv", "html", "table"], case_sensitive=False),
56
+ default="table",
57
+ help="Output format (default: table).",
58
+ )
59
+ @click.option( # type: ignore[misc]
60
+ "--save-report",
61
+ type=click.Path(),
62
+ default=None,
63
+ help="Save HTML report to file.",
64
+ )
65
+ @click.pass_context # type: ignore[misc]
66
+ def characterize(
67
+ ctx: click.Context,
68
+ file: str,
69
+ analysis_type: str,
70
+ logic_family: str,
71
+ compare: str | None,
72
+ output: str,
73
+ save_report: str | None,
74
+ ) -> None:
75
+ """Characterize buffer, signal, or power measurements.
76
+
77
+ Analyzes a waveform file and extracts timing, quality, and performance
78
+ characteristics. Supports automatic logic family detection and optional
79
+ comparison to a reference signal.
80
+
81
+ Args:
82
+ ctx: Click context object.
83
+ file: Path to waveform file to characterize.
84
+ analysis_type: Type of characterization (buffer, signal, power).
85
+ logic_family: Logic family for buffer characterization.
86
+ compare: Path to reference file for comparison analysis.
87
+ output: Output format (json, csv, html, table).
88
+ save_report: Path to save HTML report file.
89
+
90
+ Raises:
91
+ Exception: If characterization fails or file cannot be loaded.
92
+
93
+ Examples:
94
+
95
+ \b
96
+ # Simple buffer characterization
97
+ $ oscura characterize 74hc04_output.wfm
98
+
99
+ \b
100
+ # Full characterization with reference
101
+ $ oscura characterize signal.wfm \\
102
+ --logic-family CMOS_3V3 \\
103
+ --compare golden_reference.wfm \\
104
+ --save-report report.html
105
+
106
+ \b
107
+ # Power analysis
108
+ $ oscura characterize power_rail.wfm --type power --output json
109
+ """
110
+ verbose = ctx.obj.get("verbose", 0)
111
+
112
+ if verbose:
113
+ logger.info(f"Characterizing: {file}")
114
+ logger.info(f"Analysis type: {analysis_type}")
115
+ logger.info(f"Logic family: {logic_family}")
116
+
117
+ try:
118
+ # Import here to avoid circular imports
119
+ from oscura.loaders import load
120
+
121
+ # Load the main trace
122
+ logger.debug(f"Loading trace from {file}")
123
+ trace = load(file)
124
+
125
+ # Load reference trace if provided
126
+ reference_trace: WaveformTrace | None = None
127
+ if compare:
128
+ logger.debug(f"Loading reference trace from {compare}")
129
+ reference_trace = load(compare) # type: ignore[assignment]
130
+
131
+ # Perform characterization based on type
132
+ results = _perform_characterization(
133
+ trace=trace,
134
+ reference_trace=reference_trace,
135
+ analysis_type=analysis_type,
136
+ logic_family=logic_family,
137
+ )
138
+
139
+ # Add metadata
140
+ results["file"] = str(Path(file).name)
141
+ if compare:
142
+ results["reference_file"] = str(Path(compare).name)
143
+
144
+ # Generate HTML report if requested
145
+ if save_report:
146
+ html_content = format_output(results, "html")
147
+ with open(save_report, "w") as f:
148
+ f.write(html_content)
149
+ logger.info(f"Report saved to {save_report}")
150
+
151
+ # Output results
152
+ formatted = format_output(results, output)
153
+ click.echo(formatted)
154
+
155
+ except Exception as e:
156
+ logger.error(f"Characterization failed: {e}")
157
+ if verbose > 1:
158
+ raise
159
+ click.echo(f"Error: {e}", err=True)
160
+ ctx.exit(1)
161
+
162
+
163
+ def _perform_characterization(
164
+ trace: Any,
165
+ reference_trace: Any | None,
166
+ analysis_type: str,
167
+ logic_family: str,
168
+ ) -> dict[str, Any]:
169
+ """Perform characterization analysis.
170
+
171
+ Calls actual Oscura analysis functions based on the analysis type.
172
+
173
+ Args:
174
+ trace: Main trace to analyze.
175
+ reference_trace: Optional reference trace for comparison.
176
+ analysis_type: Type of analysis ('buffer', 'signal', 'power').
177
+ logic_family: Logic family for digital analysis.
178
+
179
+ Returns:
180
+ Dictionary of analysis results.
181
+ """
182
+ import numpy as np
183
+
184
+ from oscura.analyzers.waveform.measurements import (
185
+ fall_time,
186
+ overshoot,
187
+ rise_time,
188
+ undershoot,
189
+ )
190
+ from oscura.comparison.compare import compare_traces, similarity_score
191
+ from oscura.inference import detect_logic_family
192
+
193
+ sample_rate = trace.metadata.sample_rate
194
+ data = trace.data
195
+
196
+ results: dict[str, Any] = {
197
+ "analysis_type": analysis_type,
198
+ "logic_family": logic_family,
199
+ "sample_rate": f"{sample_rate / 1e6:.1f} MHz",
200
+ "samples": len(data),
201
+ "duration": f"{len(data) / sample_rate * 1e3:.3f} ms",
202
+ }
203
+
204
+ if analysis_type == "buffer":
205
+ # Buffer characterization using actual workflow (WRK-001)
206
+ # Functions expect WaveformTrace, not ndarray
207
+ rt = rise_time(trace)
208
+ ft = fall_time(trace)
209
+ os_pct = overshoot(trace)
210
+ us_pct = undershoot(trace)
211
+
212
+ results.update(
213
+ {
214
+ "rise_time": f"{rt * 1e9:.2f} ns" if not np.isnan(rt) else "N/A",
215
+ "fall_time": f"{ft * 1e9:.2f} ns" if not np.isnan(ft) else "N/A",
216
+ "overshoot": f"{os_pct:.1f} %" if not np.isnan(os_pct) else "N/A",
217
+ "undershoot": f"{us_pct:.1f} %" if not np.isnan(us_pct) else "N/A",
218
+ "status": "PASS",
219
+ }
220
+ )
221
+
222
+ # Logic family detection
223
+ if logic_family == "auto":
224
+ detected = detect_logic_family(trace)
225
+ # detect_logic_family returns dict with 'primary' key
226
+ primary = detected.get("primary", {})
227
+ results["logic_family_detected"] = primary.get("name", "unknown")
228
+ results["confidence"] = f"{primary.get('confidence', 0) * 100:.0f}%"
229
+ else:
230
+ results["logic_family_detected"] = logic_family
231
+
232
+ elif analysis_type == "signal":
233
+ # Signal analysis
234
+ results.update(
235
+ {
236
+ "amplitude": f"{float(data.max() - data.min()):.3f} V",
237
+ "peak_to_peak": f"{float(data.max() - data.min()):.3f} V",
238
+ "mean": f"{float(data.mean()):.3f} V",
239
+ "rms": f"{float(np.sqrt((data**2).mean())):.3f} V",
240
+ }
241
+ )
242
+
243
+ elif analysis_type == "power":
244
+ # Power analysis - compute power statistics directly from voltage
245
+ # Assume voltage trace, compute power (P = V^2/R, assume R=1 for relative)
246
+ power_data = data**2
247
+ avg_pwr = float(np.mean(power_data))
248
+ peak_pwr = float(np.max(power_data))
249
+ total_energy = float(np.sum(power_data) / sample_rate)
250
+
251
+ results.update(
252
+ {
253
+ "average_power": f"{avg_pwr * 1e3:.3f} mW",
254
+ "peak_power": f"{peak_pwr * 1e3:.3f} mW",
255
+ "energy": f"{total_energy * 1e6:.3f} uJ",
256
+ }
257
+ )
258
+
259
+ # Add comparison results if reference provided
260
+ if reference_trace is not None:
261
+ ref_data = reference_trace.data
262
+ # similarity_score expects WaveformTrace objects
263
+ sim = similarity_score(trace, reference_trace)
264
+ # compare_traces returns ComparisonResult dataclass
265
+ comparison_result = compare_traces(trace, reference_trace)
266
+
267
+ results["comparison"] = {
268
+ "correlation": f"{comparison_result.correlation:.4f}",
269
+ "amplitude_difference": f"{abs(float(data.mean()) - float(ref_data.mean())):.3f} V",
270
+ "similarity": f"{sim * 100:.1f}%",
271
+ }
272
+
273
+ return results