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,254 @@
1
+ """Default configuration values and injection.
2
+
3
+ This module provides default configuration values and utilities
4
+ for injecting defaults into user configurations.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.config.defaults import inject_defaults
9
+ >>> config = {"name": "test"}
10
+ >>> full_config = inject_defaults(config, "protocol")
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import copy
16
+ from typing import Any
17
+
18
+ # Default configuration values
19
+ DEFAULT_CONFIG: dict[str, Any] = {
20
+ "version": "1.0",
21
+ "defaults": {
22
+ "sample_rate": 1e6, # 1 MHz default
23
+ "window_function": "hann",
24
+ "fft_size": 1024,
25
+ },
26
+ "loaders": {
27
+ "auto_detect": True,
28
+ "formats": ["wfm", "csv", "npz", "hdf5", "tdms", "vcd", "sr", "wav", "pcap"],
29
+ "tektronix": {"byte_order": "little"},
30
+ "csv": {"delimiter": ",", "skip_header": 0},
31
+ },
32
+ "measurements": {
33
+ "rise_time": {"ref_levels": [0.1, 0.9]},
34
+ "fall_time": {"ref_levels": [0.9, 0.1]},
35
+ "frequency": {"min_periods": 3},
36
+ },
37
+ "spectral": {
38
+ "default_window": "hann",
39
+ "overlap": 0.5,
40
+ "nfft": None, # Auto-determine from signal length
41
+ },
42
+ "visualization": {
43
+ "default_style": "seaborn",
44
+ "figure_size": [10, 6],
45
+ "dpi": 100,
46
+ "colormap": "viridis",
47
+ },
48
+ "export": {
49
+ "csv": {"precision": 6},
50
+ "hdf5": {"compression": "gzip", "compression_opts": 4},
51
+ },
52
+ "logging": {
53
+ "level": "INFO",
54
+ "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
55
+ },
56
+ }
57
+
58
+
59
+ # Schema-specific default values
60
+ SCHEMA_DEFAULTS: dict[str, dict[str, Any]] = {
61
+ "protocol": {
62
+ "version": "1.0.0",
63
+ "timing": {
64
+ "data_bits": [8],
65
+ "stop_bits": [1],
66
+ "parity": ["none"],
67
+ },
68
+ },
69
+ "pipeline": {
70
+ "version": "1.0.0",
71
+ "parallel_groups": [],
72
+ },
73
+ "logic_family": {
74
+ "temperature_range": {
75
+ "min": 0,
76
+ "max": 70,
77
+ },
78
+ },
79
+ "threshold_profile": {
80
+ "tolerance": 0,
81
+ "overrides": {},
82
+ },
83
+ "preferences": {
84
+ "defaults": {
85
+ "sample_rate": 1e6,
86
+ "window_function": "hann",
87
+ },
88
+ "visualization": {
89
+ "style": "seaborn",
90
+ "dpi": 100,
91
+ },
92
+ "export": {
93
+ "default_format": "csv",
94
+ "precision": 6,
95
+ },
96
+ "logging": {
97
+ "level": "INFO",
98
+ },
99
+ },
100
+ }
101
+
102
+
103
+ def deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
104
+ """Recursively merge two dictionaries.
105
+
106
+ Values from override take precedence. Nested dictionaries are
107
+ merged recursively.
108
+
109
+ Args:
110
+ base: Base dictionary.
111
+ override: Dictionary with values to override.
112
+
113
+ Returns:
114
+ Merged dictionary (new instance).
115
+
116
+ Example:
117
+ >>> base = {"a": 1, "b": {"c": 2}}
118
+ >>> override = {"b": {"d": 3}}
119
+ >>> deep_merge(base, override)
120
+ {'a': 1, 'b': {'c': 2, 'd': 3}}
121
+ """
122
+ result = copy.deepcopy(base)
123
+
124
+ for key, value in override.items():
125
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
126
+ result[key] = deep_merge(result[key], value)
127
+ else:
128
+ result[key] = copy.deepcopy(value)
129
+
130
+ return result
131
+
132
+
133
+ def inject_defaults(
134
+ config: dict[str, Any],
135
+ schema_name: str,
136
+ ) -> dict[str, Any]:
137
+ """Inject default values into configuration.
138
+
139
+ Adds default values for missing fields based on schema type.
140
+
141
+ Args:
142
+ config: User configuration dictionary.
143
+ schema_name: Schema name to determine defaults.
144
+
145
+ Returns:
146
+ Configuration with defaults injected.
147
+
148
+ Example:
149
+ >>> config = {"name": "uart", "timing": {"baud_rates": [9600]}}
150
+ >>> full = inject_defaults(config, "protocol")
151
+ >>> print(full["timing"]["data_bits"])
152
+ [8]
153
+ """
154
+ defaults = SCHEMA_DEFAULTS.get(schema_name, {})
155
+
156
+ if not defaults:
157
+ return copy.deepcopy(config)
158
+
159
+ # Merge defaults with config (config takes precedence)
160
+ return deep_merge(defaults, config)
161
+
162
+
163
+ def get_effective_config(
164
+ user_config: dict[str, Any] | None = None,
165
+ schema_name: str | None = None,
166
+ ) -> dict[str, Any]:
167
+ """Get effective configuration with all defaults applied.
168
+
169
+ Combines base defaults, schema-specific defaults, and user configuration.
170
+
171
+ Args:
172
+ user_config: User-provided configuration.
173
+ schema_name: Schema to apply defaults for.
174
+
175
+ Returns:
176
+ Complete configuration with all defaults.
177
+
178
+ Example:
179
+ >>> config = get_effective_config({"defaults": {"sample_rate": 2e6}})
180
+ >>> print(config["defaults"]["sample_rate"])
181
+ 2000000.0
182
+ """
183
+ # Start with base defaults
184
+ result = copy.deepcopy(DEFAULT_CONFIG)
185
+
186
+ # Add schema-specific defaults
187
+ if schema_name and schema_name in SCHEMA_DEFAULTS:
188
+ schema_defaults = SCHEMA_DEFAULTS[schema_name]
189
+ result = deep_merge(result, schema_defaults)
190
+
191
+ # Apply user configuration
192
+ if user_config:
193
+ result = deep_merge(result, user_config)
194
+
195
+ return result
196
+
197
+
198
+ def get_default(
199
+ key_path: str,
200
+ schema_name: str | None = None,
201
+ ) -> Any:
202
+ """Get default value for a configuration key.
203
+
204
+ Args:
205
+ key_path: Dot-separated path (e.g., "defaults.sample_rate").
206
+ schema_name: Optional schema for schema-specific defaults.
207
+
208
+ Returns:
209
+ Default value or None if not found.
210
+
211
+ Example:
212
+ >>> get_default("defaults.sample_rate")
213
+ 1000000.0
214
+ """
215
+ # Check schema-specific defaults first
216
+ if schema_name and schema_name in SCHEMA_DEFAULTS:
217
+ value = _get_nested(SCHEMA_DEFAULTS[schema_name], key_path)
218
+ if value is not None:
219
+ return value
220
+
221
+ # Fall back to base defaults
222
+ return _get_nested(DEFAULT_CONFIG, key_path)
223
+
224
+
225
+ def _get_nested(config: dict[str, Any], key_path: str) -> Any:
226
+ """Get nested value by dot-separated path.
227
+
228
+ Args:
229
+ config: Configuration dictionary.
230
+ key_path: Dot-separated path.
231
+
232
+ Returns:
233
+ Value or None if not found.
234
+ """
235
+ keys = key_path.split(".")
236
+ value = config
237
+
238
+ for key in keys:
239
+ if isinstance(value, dict) and key in value:
240
+ value = value[key]
241
+ else:
242
+ return None
243
+
244
+ return value
245
+
246
+
247
+ __all__ = [
248
+ "DEFAULT_CONFIG",
249
+ "SCHEMA_DEFAULTS",
250
+ "deep_merge",
251
+ "get_default",
252
+ "get_effective_config",
253
+ "inject_defaults",
254
+ ]
@@ -0,0 +1,348 @@
1
+ """Configuration file loading utilities.
2
+
3
+ This module provides unified configuration loading from YAML and JSON files
4
+ with support for schema validation, default injection, and path resolution.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.config.loader import load_config_file
9
+ >>> config = load_config_file("pipeline.yaml", schema="pipeline")
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ from oscura.core.exceptions import ConfigurationError
19
+
20
+ # Try to import yaml
21
+ try:
22
+ import yaml
23
+
24
+ YAML_AVAILABLE = True
25
+ except ImportError:
26
+ YAML_AVAILABLE = False
27
+
28
+
29
+ def load_config_file(
30
+ path: str | Path,
31
+ *,
32
+ schema: str | None = None,
33
+ validate: bool = True,
34
+ inject_defaults: bool = True,
35
+ ) -> dict[str, Any]:
36
+ """Load configuration from YAML or JSON file.
37
+
38
+ Automatically detects file format from extension.
39
+ Optionally validates against schema and injects defaults.
40
+
41
+ Args:
42
+ path: Path to configuration file.
43
+ schema: Schema name to validate against (e.g., "protocol").
44
+ validate: If True and schema provided, validate configuration.
45
+ inject_defaults: If True, inject default values from schema.
46
+
47
+ Returns:
48
+ Configuration dictionary.
49
+
50
+ Raises:
51
+ ConfigurationError: If file cannot be loaded or parsed.
52
+
53
+ Example:
54
+ >>> config = load_config_file("uart.yaml", schema="protocol")
55
+ >>> print(config["name"])
56
+ 'uart'
57
+ """
58
+ path = Path(path).expanduser().resolve()
59
+
60
+ if not path.exists():
61
+ raise ConfigurationError(
62
+ "Configuration file not found",
63
+ config_key=str(path),
64
+ )
65
+
66
+ # Determine format from extension
67
+ ext = path.suffix.lower()
68
+
69
+ if ext in (".yaml", ".yml"):
70
+ config = _load_yaml(path)
71
+ elif ext == ".json":
72
+ config = _load_json(path)
73
+ else:
74
+ # Try YAML first, then JSON
75
+ try:
76
+ config = _load_yaml(path)
77
+ except ConfigurationError:
78
+ try:
79
+ config = _load_json(path)
80
+ except ConfigurationError:
81
+ raise ConfigurationError( # noqa: B904
82
+ f"Unsupported configuration format: {ext}",
83
+ config_key=str(path),
84
+ fix_hint="Use .yaml, .yml, or .json extension",
85
+ )
86
+
87
+ # Validate against schema if requested
88
+ if validate and schema is not None:
89
+ from oscura.config.schema import validate_against_schema
90
+
91
+ validate_against_schema(config, schema)
92
+
93
+ # Inject defaults if requested
94
+ if inject_defaults and schema is not None:
95
+ from oscura.config.defaults import inject_defaults as do_inject
96
+
97
+ config = do_inject(config, schema)
98
+
99
+ return config
100
+
101
+
102
+ def _load_yaml(path: Path) -> dict[str, Any]:
103
+ """Load YAML configuration file.
104
+
105
+ Args:
106
+ path: Path to YAML file.
107
+
108
+ Returns:
109
+ Parsed configuration dictionary.
110
+
111
+ Raises:
112
+ ConfigurationError: If YAML parsing fails.
113
+ """
114
+ if not YAML_AVAILABLE:
115
+ raise ConfigurationError(
116
+ "YAML support not available",
117
+ fix_hint="Install PyYAML: pip install pyyaml",
118
+ )
119
+
120
+ try:
121
+ with open(path, encoding="utf-8") as f:
122
+ content = yaml.safe_load(f)
123
+
124
+ if content is None:
125
+ return {}
126
+
127
+ if not isinstance(content, dict):
128
+ raise ConfigurationError(
129
+ "Configuration must be a dictionary",
130
+ config_key=str(path),
131
+ expected_type="object",
132
+ actual_value=type(content).__name__,
133
+ )
134
+
135
+ return content
136
+
137
+ except yaml.YAMLError as e:
138
+ # Extract line number from YAML error
139
+ line = None
140
+ if hasattr(e, "problem_mark") and e.problem_mark is not None:
141
+ line = e.problem_mark.line + 1
142
+
143
+ raise ConfigurationError(
144
+ "Failed to parse YAML configuration",
145
+ config_key=str(path),
146
+ details=f"Line {line}: {e}" if line else str(e),
147
+ ) from e
148
+ except OSError as e:
149
+ raise ConfigurationError(
150
+ "Failed to read configuration file",
151
+ config_key=str(path),
152
+ details=str(e),
153
+ ) from e
154
+
155
+
156
+ def _load_json(path: Path) -> dict[str, Any]:
157
+ """Load JSON configuration file.
158
+
159
+ Args:
160
+ path: Path to JSON file.
161
+
162
+ Returns:
163
+ Parsed configuration dictionary.
164
+
165
+ Raises:
166
+ ConfigurationError: If JSON parsing fails.
167
+ """
168
+ try:
169
+ with open(path, encoding="utf-8") as f:
170
+ content = json.load(f)
171
+
172
+ if content is None:
173
+ return {}
174
+
175
+ if not isinstance(content, dict):
176
+ raise ConfigurationError(
177
+ "Configuration must be a dictionary",
178
+ config_key=str(path),
179
+ expected_type="object",
180
+ actual_value=type(content).__name__,
181
+ )
182
+
183
+ return content
184
+
185
+ except json.JSONDecodeError as e:
186
+ raise ConfigurationError(
187
+ "Failed to parse JSON configuration",
188
+ config_key=str(path),
189
+ details=f"Line {e.lineno}, column {e.colno}: {e.msg}",
190
+ ) from e
191
+ except OSError as e:
192
+ raise ConfigurationError(
193
+ "Failed to read configuration file",
194
+ config_key=str(path),
195
+ details=str(e),
196
+ ) from e
197
+
198
+
199
+ def load_config(
200
+ config_path: str | Path | None = None,
201
+ *,
202
+ use_defaults: bool = True,
203
+ ) -> dict[str, Any]:
204
+ """Load TraceKit configuration.
205
+
206
+ Searches for configuration in standard locations if no path provided.
207
+
208
+ Args:
209
+ config_path: Path to configuration file. If None, searches
210
+ standard locations.
211
+ use_defaults: If True, merge with default configuration.
212
+
213
+ Returns:
214
+ Configuration dictionary.
215
+
216
+ Example:
217
+ >>> config = load_config() # Auto-find config
218
+ >>> config = load_config("~/.oscura/config.yaml")
219
+ """
220
+ from oscura.config.defaults import DEFAULT_CONFIG, deep_merge
221
+
222
+ config: dict[str, Any] = {}
223
+
224
+ if use_defaults:
225
+ import copy
226
+
227
+ config = copy.deepcopy(DEFAULT_CONFIG)
228
+
229
+ if config_path is None:
230
+ # Search standard locations
231
+ search_paths = [
232
+ Path.cwd() / "oscura.yaml",
233
+ Path.cwd() / ".oscura.yaml",
234
+ Path.cwd() / "oscura.json",
235
+ Path.home() / ".oscura" / "config.yaml",
236
+ Path.home() / ".config" / "oscura" / "config.yaml",
237
+ ]
238
+
239
+ for path in search_paths:
240
+ if path.exists():
241
+ config_path = path
242
+ break
243
+
244
+ if config_path is not None:
245
+ user_config = load_config_file(
246
+ config_path,
247
+ validate=False,
248
+ inject_defaults=False,
249
+ )
250
+ config = deep_merge(config, user_config) if use_defaults else user_config
251
+
252
+ return config
253
+
254
+
255
+ def save_config(
256
+ config: dict[str, Any],
257
+ path: str | Path,
258
+ *,
259
+ format: str | None = None,
260
+ ) -> None:
261
+ """Save configuration to file.
262
+
263
+ Args:
264
+ config: Configuration dictionary to save.
265
+ path: Output file path.
266
+ format: Output format ("yaml" or "json"). Auto-detected from
267
+ extension if not specified.
268
+
269
+ Raises:
270
+ ConfigurationError: If configuration cannot be saved.
271
+
272
+ Example:
273
+ >>> save_config(config, "config.yaml")
274
+ >>> save_config(config, "config.json")
275
+ """
276
+ path = Path(path).expanduser().resolve()
277
+
278
+ # Determine format
279
+ if format is None:
280
+ ext = path.suffix.lower()
281
+ if ext in (".yaml", ".yml"):
282
+ format = "yaml"
283
+ elif ext == ".json":
284
+ format = "json"
285
+ else:
286
+ format = "yaml" # Default
287
+
288
+ # Create parent directory
289
+ path.parent.mkdir(parents=True, exist_ok=True)
290
+
291
+ try:
292
+ if format == "yaml":
293
+ if not YAML_AVAILABLE:
294
+ raise ConfigurationError(
295
+ "YAML support not available",
296
+ fix_hint="Install PyYAML: pip install pyyaml",
297
+ )
298
+ with open(path, "w", encoding="utf-8") as f:
299
+ yaml.dump(config, f, default_flow_style=False, sort_keys=False)
300
+ else:
301
+ with open(path, "w", encoding="utf-8") as f:
302
+ json.dump(config, f, indent=2)
303
+
304
+ except OSError as e:
305
+ raise ConfigurationError(
306
+ "Failed to save configuration",
307
+ config_key=str(path),
308
+ details=str(e),
309
+ ) from e
310
+
311
+
312
+ def get_config_value(
313
+ config: dict[str, Any],
314
+ key_path: str,
315
+ default: Any = None,
316
+ ) -> Any:
317
+ """Get configuration value by dot-separated path.
318
+
319
+ Args:
320
+ config: Configuration dictionary.
321
+ key_path: Dot-separated path (e.g., "defaults.sample_rate").
322
+ default: Default value if key not found.
323
+
324
+ Returns:
325
+ Configuration value or default.
326
+
327
+ Example:
328
+ >>> get_config_value(config, "defaults.sample_rate", 1e6)
329
+ 1000000.0
330
+ """
331
+ keys = key_path.split(".")
332
+ value = config
333
+
334
+ for key in keys:
335
+ if isinstance(value, dict) and key in value:
336
+ value = value[key]
337
+ else:
338
+ return default
339
+
340
+ return value
341
+
342
+
343
+ __all__ = [
344
+ "get_config_value",
345
+ "load_config",
346
+ "load_config_file",
347
+ "save_config",
348
+ ]