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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,599 @@
1
+ """Threshold configuration for voltage levels and logic families.
2
+
3
+ This module provides threshold configuration for digital signal analysis
4
+ including logic family definitions, threshold profiles, and per-analysis
5
+ overrides.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ import os
12
+ import threading
13
+ from dataclasses import dataclass, field
14
+ from pathlib import Path
15
+
16
+ import yaml
17
+
18
+ from oscura.config.schema import validate_against_schema
19
+ from oscura.core.exceptions import ConfigurationError
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ @dataclass
25
+ class LogicFamily:
26
+ """Logic family voltage threshold definition.
27
+
28
+ Defines voltage thresholds per IEEE/JEDEC standards for digital
29
+ signal interpretation.
30
+
31
+ Attributes:
32
+ name: Logic family name (e.g., "TTL", "CMOS_3V3")
33
+ VIH: Input high voltage threshold (V)
34
+ VIL: Input low voltage threshold (V)
35
+ VOH: Output high voltage (V)
36
+ VOL: Output low voltage (V)
37
+ VCC: Supply voltage (V)
38
+ description: Human-readable description
39
+ temperature_range: Operating temperature range (min, max) in C
40
+ noise_margin_high: High state noise margin (V)
41
+ noise_margin_low: Low state noise margin (V)
42
+ source: Origin of definition (builtin, user, file path)
43
+
44
+ Example:
45
+ >>> ttl = LogicFamily(
46
+ ... name="TTL",
47
+ ... VIH=2.0, VIL=0.8,
48
+ ... VOH=2.4, VOL=0.4,
49
+ ... VCC=5.0
50
+ ... )
51
+ >>> print(f"TTL noise margin high: {ttl.noise_margin_high}V")
52
+ """
53
+
54
+ name: str
55
+ VIH: float # Input high threshold
56
+ VIL: float # Input low threshold
57
+ VOH: float # Output high level
58
+ VOL: float # Output low level
59
+ VCC: float = 5.0
60
+ description: str = ""
61
+ temperature_range: tuple[float, float] = field(default_factory=lambda: (0, 70))
62
+ noise_margin_high: float | None = None
63
+ noise_margin_low: float | None = None
64
+ source: str = "builtin"
65
+
66
+ def __post_init__(self) -> None:
67
+ """Validate thresholds and compute noise margins."""
68
+ # Validate threshold ordering
69
+ if self.VIH <= self.VIL:
70
+ raise ConfigurationError(
71
+ f"Invalid thresholds for {self.name}: VIH ({self.VIH}V) must be > VIL ({self.VIL}V)"
72
+ )
73
+ if self.VOH <= self.VOL:
74
+ raise ConfigurationError(
75
+ f"Invalid thresholds for {self.name}: VOH ({self.VOH}V) must be > VOL ({self.VOL}V)"
76
+ )
77
+
78
+ # Compute noise margins if not provided
79
+ if self.noise_margin_high is None:
80
+ self.noise_margin_high = self.VOH - self.VIH
81
+ if self.noise_margin_low is None:
82
+ self.noise_margin_low = self.VIL - self.VOL
83
+
84
+ def get_threshold(self, percent: float = 50.0) -> float:
85
+ """Get threshold voltage at given percentage between VIL and VIH.
86
+
87
+ Args:
88
+ percent: Percentage between VIL (0%) and VIH (100%)
89
+
90
+ Returns:
91
+ Threshold voltage
92
+ """
93
+ return self.VIL + (self.VIH - self.VIL) * (percent / 100.0)
94
+
95
+ def with_temperature_derating(
96
+ self, temperature: float, derating_factor: float = 0.002
97
+ ) -> LogicFamily:
98
+ """Create copy with temperature-derated thresholds.
99
+
100
+ Args:
101
+ temperature: Operating temperature in Celsius
102
+ derating_factor: Derating factor per degree C (default 0.2%/C)
103
+
104
+ Returns:
105
+ New LogicFamily with adjusted thresholds
106
+ """
107
+ # Simple linear derating from nominal 25C
108
+ delta_t = temperature - 25.0
109
+ factor = 1.0 - (delta_t * derating_factor)
110
+
111
+ return LogicFamily(
112
+ name=f"{self.name}@{temperature}C",
113
+ VIH=self.VIH * factor,
114
+ VIL=self.VIL * factor,
115
+ VOH=self.VOH * factor,
116
+ VOL=self.VOL * factor,
117
+ VCC=self.VCC,
118
+ description=f"{self.description} (derated for {temperature}C)",
119
+ temperature_range=self.temperature_range,
120
+ source=self.source,
121
+ )
122
+
123
+
124
+ @dataclass
125
+ class ThresholdProfile:
126
+ """Named threshold profile combining logic family with adjustments.
127
+
128
+ Profiles allow users to save and reuse threshold configurations
129
+ for specific analysis scenarios.
130
+
131
+ Attributes:
132
+ name: Profile name
133
+ base_family: Base logic family name
134
+ overrides: Override values for specific thresholds
135
+ tolerance: Tolerance percentage (0-100)
136
+ description: Profile description
137
+
138
+ Example:
139
+ >>> profile = ThresholdProfile(
140
+ ... name="strict_ttl",
141
+ ... base_family="TTL",
142
+ ... overrides={"VIH": 2.2},
143
+ ... tolerance=0
144
+ ... )
145
+ """
146
+
147
+ name: str
148
+ base_family: str = "TTL"
149
+ overrides: dict[str, float] = field(default_factory=dict)
150
+ tolerance: float = 0.0 # 0-100%
151
+ description: str = ""
152
+
153
+ def apply_to(self, family: LogicFamily) -> LogicFamily:
154
+ """Apply profile overrides to a logic family.
155
+
156
+ Args:
157
+ family: Base logic family
158
+
159
+ Returns:
160
+ New LogicFamily with overrides applied
161
+ """
162
+ # Apply tolerance
163
+ factor = 1.0 + (self.tolerance / 100.0)
164
+
165
+ return LogicFamily(
166
+ name=f"{family.name}_{self.name}",
167
+ VIH=self.overrides.get("VIH", family.VIH),
168
+ VIL=self.overrides.get("VIL", family.VIL),
169
+ VOH=self.overrides.get("VOH", family.VOH * factor),
170
+ VOL=self.overrides.get("VOL", family.VOL / factor),
171
+ VCC=self.overrides.get("VCC", family.VCC),
172
+ description=f"{family.description} with {self.name} profile",
173
+ temperature_range=family.temperature_range,
174
+ source="profile",
175
+ )
176
+
177
+
178
+ class ThresholdRegistry:
179
+ """Registry for logic families and threshold profiles.
180
+
181
+ Manages built-in and user-defined logic families with support
182
+ for runtime overrides and profile switching.
183
+
184
+ Thread-safe singleton implementation with locks protecting shared state.
185
+
186
+ Example:
187
+ >>> registry = ThresholdRegistry()
188
+ >>> ttl = registry.get_family("TTL")
189
+ >>> cmos = registry.get_family("CMOS_3V3")
190
+ >>> families = registry.list_families()
191
+ """
192
+
193
+ _instance: ThresholdRegistry | None = None
194
+ _lock: threading.Lock = threading.Lock()
195
+
196
+ # Instance attributes (initialized in __new__)
197
+ _families: dict[str, LogicFamily]
198
+ _profiles: dict[str, ThresholdProfile]
199
+ _session_overrides: dict[str, float]
200
+ _state_lock: threading.Lock
201
+
202
+ def __new__(cls) -> ThresholdRegistry:
203
+ """Ensure singleton instance (thread-safe)."""
204
+ if cls._instance is None:
205
+ with cls._lock:
206
+ # Double-check locking pattern
207
+ if cls._instance is None:
208
+ cls._instance = super().__new__(cls)
209
+ cls._instance._families = {} # type: ignore[attr-defined]
210
+ cls._instance._profiles = {} # type: ignore[attr-defined]
211
+ cls._instance._session_overrides = {} # type: ignore[attr-defined]
212
+ cls._instance._state_lock = threading.Lock() # type: ignore[attr-defined]
213
+ cls._instance._register_builtins()
214
+ return cls._instance
215
+
216
+ def _register_builtins(self) -> None:
217
+ """Register built-in logic family definitions."""
218
+ builtins = [
219
+ # TTL
220
+ LogicFamily(
221
+ name="TTL",
222
+ VIH=2.0,
223
+ VIL=0.8,
224
+ VOH=2.4,
225
+ VOL=0.4,
226
+ VCC=5.0,
227
+ description="Standard TTL (74xx series)",
228
+ ),
229
+ # CMOS variants
230
+ LogicFamily(
231
+ name="CMOS_5V",
232
+ VIH=3.5,
233
+ VIL=1.5,
234
+ VOH=4.9,
235
+ VOL=0.1,
236
+ VCC=5.0,
237
+ description="CMOS 5V (74HCxx series)",
238
+ ),
239
+ LogicFamily(
240
+ name="LVTTL_3V3",
241
+ VIH=2.0,
242
+ VIL=0.8,
243
+ VOH=2.4,
244
+ VOL=0.4,
245
+ VCC=3.3,
246
+ description="Low Voltage TTL 3.3V",
247
+ ),
248
+ LogicFamily(
249
+ name="LVCMOS_3V3",
250
+ VIH=2.0,
251
+ VIL=0.7,
252
+ VOH=2.4,
253
+ VOL=0.4,
254
+ VCC=3.3,
255
+ description="Low Voltage CMOS 3.3V",
256
+ ),
257
+ LogicFamily(
258
+ name="LVCMOS_2V5",
259
+ VIH=1.7,
260
+ VIL=0.7,
261
+ VOH=2.0,
262
+ VOL=0.4,
263
+ VCC=2.5,
264
+ description="Low Voltage CMOS 2.5V",
265
+ ),
266
+ LogicFamily(
267
+ name="LVCMOS_1V8",
268
+ VIH=1.17,
269
+ VIL=0.63,
270
+ VOH=1.35,
271
+ VOL=0.45,
272
+ VCC=1.8,
273
+ description="Low Voltage CMOS 1.8V",
274
+ ),
275
+ LogicFamily(
276
+ name="LVCMOS_1V5",
277
+ VIH=0.975,
278
+ VIL=0.525,
279
+ VOH=1.125,
280
+ VOL=0.375,
281
+ VCC=1.5,
282
+ description="Low Voltage CMOS 1.5V",
283
+ ),
284
+ LogicFamily(
285
+ name="LVCMOS_1V2",
286
+ VIH=0.84, # 0.7 * 1.2
287
+ VIL=0.36, # 0.3 * 1.2
288
+ VOH=1.1,
289
+ VOL=0.1,
290
+ VCC=1.2,
291
+ description="Low Voltage CMOS 1.2V",
292
+ ),
293
+ # ECL
294
+ LogicFamily(
295
+ name="ECL",
296
+ VIH=-0.9,
297
+ VIL=-1.7,
298
+ VOH=-0.9,
299
+ VOL=-1.75,
300
+ VCC=-5.2,
301
+ description="Emitter-Coupled Logic (ECL 10K)",
302
+ ),
303
+ ]
304
+
305
+ for family in builtins:
306
+ self._families[family.name] = family # type: ignore[attr-defined]
307
+
308
+ # Built-in profiles
309
+ builtin_profiles = [
310
+ ThresholdProfile(
311
+ name="strict",
312
+ base_family="TTL",
313
+ tolerance=0,
314
+ description="Exact specification values",
315
+ ),
316
+ ThresholdProfile(
317
+ name="relaxed",
318
+ base_family="TTL",
319
+ tolerance=20,
320
+ description="20% tolerance for real-world signals",
321
+ ),
322
+ ThresholdProfile(
323
+ name="auto",
324
+ base_family="TTL",
325
+ tolerance=10,
326
+ description="Auto-adjusted based on signal confidence",
327
+ ),
328
+ ]
329
+
330
+ for profile in builtin_profiles:
331
+ self._profiles[profile.name] = profile # type: ignore[attr-defined]
332
+
333
+ def get_family(self, name: str) -> LogicFamily:
334
+ """Get logic family by name.
335
+
336
+ Args:
337
+ name: Logic family name (case-insensitive)
338
+
339
+ Returns:
340
+ Logic family definition
341
+
342
+ Raises:
343
+ KeyError: If family not found
344
+ """
345
+ with self._state_lock: # type: ignore[attr-defined]
346
+ # Try exact match first
347
+ if name in self._families: # type: ignore[attr-defined]
348
+ family = self._families[name] # type: ignore[attr-defined]
349
+ # Try case-insensitive match
350
+ elif name.upper() in self._families: # type: ignore[attr-defined]
351
+ family = self._families[name.upper()] # type: ignore[attr-defined]
352
+ else:
353
+ available = list(self._families.keys()) # type: ignore[attr-defined]
354
+ raise KeyError(f"Logic family '{name}' not found. Available: {available}")
355
+
356
+ # Apply session overrides
357
+ if self._session_overrides: # type: ignore[attr-defined]
358
+ return LogicFamily(
359
+ name=family.name,
360
+ VIH=self._session_overrides.get("VIH", family.VIH), # type: ignore[attr-defined]
361
+ VIL=self._session_overrides.get("VIL", family.VIL), # type: ignore[attr-defined]
362
+ VOH=self._session_overrides.get("VOH", family.VOH), # type: ignore[attr-defined]
363
+ VOL=self._session_overrides.get("VOL", family.VOL), # type: ignore[attr-defined]
364
+ VCC=self._session_overrides.get("VCC", family.VCC), # type: ignore[attr-defined]
365
+ description=family.description,
366
+ temperature_range=family.temperature_range,
367
+ source="override",
368
+ )
369
+
370
+ return family # type: ignore[no-any-return]
371
+
372
+ def list_families(self) -> list[str]:
373
+ """List all available logic families.
374
+
375
+ Returns:
376
+ List of family names
377
+ """
378
+ with self._state_lock: # type: ignore[attr-defined]
379
+ return sorted(self._families.keys()) # type: ignore[attr-defined]
380
+
381
+ def register_family(self, family: LogicFamily, *, namespace: str = "user") -> None:
382
+ """Register custom logic family.
383
+
384
+ Args:
385
+ family: Logic family definition
386
+ namespace: Namespace prefix for custom families
387
+
388
+ Example:
389
+ >>> custom = LogicFamily(name="my_custom", VIH=2.5, VIL=1.0, VOH=3.0, VOL=0.5)
390
+ >>> registry.register_family(custom)
391
+ >>> # Available as "user.my_custom"
392
+ """
393
+ with self._state_lock: # type: ignore[attr-defined]
394
+ # Namespace custom families
395
+ if namespace and not family.name.startswith(f"{namespace}."):
396
+ name = f"{namespace}.{family.name}"
397
+ else:
398
+ name = family.name
399
+
400
+ # Update family with new name
401
+ family = LogicFamily(
402
+ name=name,
403
+ VIH=family.VIH,
404
+ VIL=family.VIL,
405
+ VOH=family.VOH,
406
+ VOL=family.VOL,
407
+ VCC=family.VCC,
408
+ description=family.description,
409
+ temperature_range=family.temperature_range,
410
+ source=family.source,
411
+ )
412
+
413
+ self._families[name] = family # type: ignore[attr-defined]
414
+ logger.info(f"Registered custom logic family: {name}")
415
+
416
+ def set_threshold_override(self, **kwargs: float) -> None:
417
+ """Set session-level threshold overrides.
418
+
419
+ Overrides persist for session lifetime until reset.
420
+
421
+ Args:
422
+ **kwargs: Threshold overrides (VIH, VIL, VOH, VOL, VCC)
423
+
424
+ Raises:
425
+ ValueError: If invalid threshold key or value out of range.
426
+
427
+ Example:
428
+ >>> registry.set_threshold_override(VIH=2.5, VIL=0.7)
429
+ """
430
+ valid_keys = {"VIH", "VIL", "VOH", "VOL", "VCC"}
431
+ with self._state_lock: # type: ignore[attr-defined]
432
+ for key, value in kwargs.items():
433
+ if key not in valid_keys:
434
+ raise ValueError(f"Invalid threshold key: {key}. Valid: {valid_keys}")
435
+ if not 0.0 <= value <= 10.0:
436
+ raise ValueError(f"Threshold {key}={value}V out of range (0-10V)")
437
+ self._session_overrides[key] = value # type: ignore[attr-defined]
438
+
439
+ logger.info(f"Set threshold overrides: {kwargs}")
440
+
441
+ def reset_overrides(self) -> None:
442
+ """Reset session threshold overrides."""
443
+ with self._state_lock: # type: ignore[attr-defined]
444
+ self._session_overrides.clear() # type: ignore[attr-defined]
445
+ logger.info("Reset threshold overrides")
446
+
447
+ def get_profile(self, name: str) -> ThresholdProfile:
448
+ """Get threshold profile by name.
449
+
450
+ Args:
451
+ name: Profile name
452
+
453
+ Returns:
454
+ Threshold profile
455
+
456
+ Raises:
457
+ KeyError: If profile not found.
458
+ """
459
+ with self._state_lock: # type: ignore[attr-defined]
460
+ if name not in self._profiles: # type: ignore[attr-defined]
461
+ raise KeyError(
462
+ f"Profile '{name}' not found. Available: {list(self._profiles.keys())}"
463
+ ) # type: ignore[attr-defined]
464
+ return self._profiles[name] # type: ignore[no-any-return, attr-defined]
465
+
466
+ def apply_profile(self, name: str) -> LogicFamily:
467
+ """Apply a threshold profile.
468
+
469
+ Args:
470
+ name: Profile name
471
+
472
+ Returns:
473
+ Logic family with profile applied
474
+ """
475
+ profile = self.get_profile(name)
476
+ base_family = self.get_family(profile.base_family)
477
+ return profile.apply_to(base_family)
478
+
479
+ def save_profile(
480
+ self, name: str, base_family: str | None = None, path: str | Path | None = None
481
+ ) -> None:
482
+ """Save current settings as named profile.
483
+
484
+ Args:
485
+ name: Profile name
486
+ base_family: Base family name (default: "TTL")
487
+ path: Optional file path to save
488
+
489
+ Example:
490
+ >>> registry.set_threshold_override(VIH=2.5)
491
+ >>> registry.save_profile("my_profile")
492
+ """
493
+ with self._state_lock: # type: ignore[attr-defined]
494
+ profile = ThresholdProfile(
495
+ name=name,
496
+ base_family=base_family or "TTL",
497
+ overrides=dict(self._session_overrides), # type: ignore[attr-defined]
498
+ description=f"User profile {name}",
499
+ )
500
+ self._profiles[name] = profile # type: ignore[attr-defined]
501
+
502
+ if path:
503
+ path = Path(path)
504
+ data = {
505
+ "name": profile.name,
506
+ "base_family": profile.base_family,
507
+ "overrides": profile.overrides,
508
+ "tolerance": profile.tolerance,
509
+ "description": profile.description,
510
+ }
511
+ with open(path, "w", encoding="utf-8") as f:
512
+ yaml.dump(data, f)
513
+ logger.info(f"Saved profile to {path}")
514
+
515
+
516
+ def load_logic_family(path: str | Path) -> LogicFamily:
517
+ """Load logic family from YAML/JSON file.
518
+
519
+ Args:
520
+ path: Path to file
521
+
522
+ Returns:
523
+ Loaded logic family
524
+ """
525
+ path = Path(path)
526
+
527
+ with open(path, encoding="utf-8") as f:
528
+ data = yaml.safe_load(f)
529
+
530
+ # Validate against schema
531
+ validate_against_schema(data, "logic_family")
532
+
533
+ return LogicFamily(
534
+ name=data["name"],
535
+ VIH=data["VIH"],
536
+ VIL=data["VIL"],
537
+ VOH=data["VOH"],
538
+ VOL=data["VOL"],
539
+ VCC=data.get("VCC", 5.0),
540
+ description=data.get("description", ""),
541
+ temperature_range=tuple(data.get("temperature_range", {}).values()) or (0, 70),
542
+ noise_margin_high=data.get("noise_margin_high"),
543
+ noise_margin_low=data.get("noise_margin_low"),
544
+ source=str(path),
545
+ )
546
+
547
+
548
+ def get_threshold_registry() -> ThresholdRegistry:
549
+ """Get the global threshold registry.
550
+
551
+ Returns:
552
+ Global ThresholdRegistry instance
553
+ """
554
+ return ThresholdRegistry()
555
+
556
+
557
+ def get_user_logic_families_dir() -> Path:
558
+ """Get user directory for custom logic families.
559
+
560
+ Returns:
561
+ Path to ~/.oscura/logic_families/
562
+ """
563
+ home = Path.home()
564
+ xdg_config = os.environ.get("XDG_CONFIG_HOME")
565
+ base = Path(xdg_config) if xdg_config else home / ".config"
566
+
567
+ dir_path = base / "oscura" / "logic_families"
568
+ dir_path.mkdir(parents=True, exist_ok=True)
569
+ return dir_path
570
+
571
+
572
+ def load_user_logic_families() -> list[LogicFamily]:
573
+ """Load all user-defined logic families.
574
+
575
+ Returns:
576
+ List of loaded logic families
577
+ """
578
+ families = []
579
+ user_dir = get_user_logic_families_dir()
580
+
581
+ for file_path in user_dir.glob("*.yaml"):
582
+ try:
583
+ family = load_logic_family(file_path)
584
+ families.append(family)
585
+ except Exception as e:
586
+ logger.warning(f"Failed to load logic family from {file_path}: {e}")
587
+
588
+ return families
589
+
590
+
591
+ __all__ = [
592
+ "LogicFamily",
593
+ "ThresholdProfile",
594
+ "ThresholdRegistry",
595
+ "get_threshold_registry",
596
+ "get_user_logic_families_dir",
597
+ "load_logic_family",
598
+ "load_user_logic_families",
599
+ ]