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,501 @@
1
+ """Dallas/Maxim 1-Wire protocol decoder.
2
+
3
+ This module provides a 1-Wire protocol decoder for temperature sensors,
4
+ EEPROMs, and other 1-Wire devices with ROM command decoding.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.protocols.onewire import OneWireDecoder
9
+ >>> decoder = OneWireDecoder()
10
+ >>> for packet in decoder.decode(trace):
11
+ ... print(f"ROM: {packet.annotations['rom_id']}")
12
+
13
+ References:
14
+ Dallas/Maxim 1-Wire Protocol
15
+ DS18B20 Datasheet
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass
21
+ from enum import Enum
22
+ from typing import TYPE_CHECKING
23
+
24
+ import numpy as np
25
+
26
+ from oscura.analyzers.protocols.base import (
27
+ AnnotationLevel,
28
+ AsyncDecoder,
29
+ ChannelDef,
30
+ OptionDef,
31
+ )
32
+ from oscura.core.types import (
33
+ DigitalTrace,
34
+ ProtocolPacket,
35
+ TraceMetadata,
36
+ WaveformTrace,
37
+ )
38
+
39
+ if TYPE_CHECKING:
40
+ from collections.abc import Iterator
41
+
42
+ from numpy.typing import NDArray
43
+
44
+
45
+ class OneWireMode(Enum):
46
+ """1-Wire speed modes."""
47
+
48
+ STANDARD = "standard" # Standard speed (15.4 kbps)
49
+ OVERDRIVE = "overdrive" # Overdrive speed (~142 kbps)
50
+
51
+
52
+ class OneWireROMCommand(Enum):
53
+ """1-Wire ROM commands."""
54
+
55
+ SEARCH_ROM = 0xF0
56
+ READ_ROM = 0x33
57
+ MATCH_ROM = 0x55
58
+ SKIP_ROM = 0xCC
59
+ ALARM_SEARCH = 0xEC
60
+ OVERDRIVE_SKIP = 0x3C
61
+ OVERDRIVE_MATCH = 0x69
62
+
63
+
64
+ ROM_COMMAND_NAMES = {
65
+ 0xF0: "Search ROM",
66
+ 0x33: "Read ROM",
67
+ 0x55: "Match ROM",
68
+ 0xCC: "Skip ROM",
69
+ 0xEC: "Alarm Search",
70
+ 0x3C: "Overdrive Skip ROM",
71
+ 0x69: "Overdrive Match ROM",
72
+ }
73
+
74
+ # 1-Wire Family Codes
75
+ FAMILY_CODES = {
76
+ 0x01: "DS1990A/DS2401 Silicon Serial Number",
77
+ 0x10: "DS18S20/DS1820 Temperature Sensor",
78
+ 0x14: "DS2430A 1kb EEPROM",
79
+ 0x22: "DS1822 Econo Temperature Sensor",
80
+ 0x23: "DS2433 4kb EEPROM",
81
+ 0x28: "DS18B20 Temperature Sensor",
82
+ 0x29: "DS2408 8-Channel Addressable Switch",
83
+ 0x2D: "DS2431 1kb EEPROM",
84
+ 0x37: "DS1977 Password-Protected 32kb EEPROM",
85
+ 0x3B: "DS1825 Temperature Sensor",
86
+ 0x42: "DS28EA00 Temperature Sensor",
87
+ }
88
+
89
+
90
+ @dataclass
91
+ class OneWireTimings:
92
+ """1-Wire protocol timing specifications in microseconds."""
93
+
94
+ # Standard speed timings
95
+ reset_min: float = 480.0
96
+ reset_max: float = 960.0
97
+ presence_min: float = 60.0
98
+ presence_max: float = 240.0
99
+ slot_min: float = 60.0
100
+ slot_max: float = 120.0
101
+ write_0_low_min: float = 60.0
102
+ write_0_low_max: float = 120.0
103
+ write_1_low_min: float = 1.0
104
+ write_1_low_max: float = 15.0
105
+ read_sample_time: float = 15.0
106
+
107
+ @classmethod
108
+ def overdrive(cls) -> OneWireTimings:
109
+ """Return overdrive mode timings (approximately 10x faster)."""
110
+ return cls(
111
+ reset_min=48.0,
112
+ reset_max=80.0,
113
+ presence_min=8.0,
114
+ presence_max=24.0,
115
+ slot_min=6.0,
116
+ slot_max=16.0,
117
+ write_0_low_min=6.0,
118
+ write_0_low_max=16.0,
119
+ write_1_low_min=1.0,
120
+ write_1_low_max=2.0,
121
+ read_sample_time=1.0,
122
+ )
123
+
124
+
125
+ @dataclass
126
+ class OneWireROMID:
127
+ """1-Wire 64-bit ROM ID structure."""
128
+
129
+ family_code: int # 8 bits
130
+ serial_number: bytes # 48 bits (6 bytes)
131
+ crc: int # 8 bits
132
+
133
+ @classmethod
134
+ def from_bytes(cls, data: bytes) -> OneWireROMID:
135
+ """Parse ROM ID from 8 bytes (LSB first)."""
136
+ if len(data) < 8:
137
+ raise ValueError("ROM ID requires 8 bytes")
138
+
139
+ family_code = data[0]
140
+ serial_number = data[1:7]
141
+ crc = data[7]
142
+
143
+ return cls(family_code=family_code, serial_number=serial_number, crc=crc)
144
+
145
+ @property
146
+ def family_name(self) -> str:
147
+ """Get human-readable family name."""
148
+ return FAMILY_CODES.get(self.family_code, f"Unknown (0x{self.family_code:02X})")
149
+
150
+ def to_hex(self) -> str:
151
+ """Return ROM ID as hex string."""
152
+ return f"{self.family_code:02X}-{self.serial_number.hex().upper()}-{self.crc:02X}"
153
+
154
+ def verify_crc(self) -> bool:
155
+ """Verify CRC-8 of ROM ID."""
156
+ data = bytes([self.family_code]) + self.serial_number
157
+ return _crc8_maxim(data) == self.crc
158
+
159
+
160
+ def _crc8_maxim(data: bytes) -> int:
161
+ """Calculate CRC-8/MAXIM (Dallas 1-Wire CRC).
162
+
163
+ Polynomial: x^8 + x^5 + x^4 + 1 (0x31 reflected = 0x8C)
164
+
165
+ Args:
166
+ data: Input bytes to calculate CRC over
167
+
168
+ Returns:
169
+ 8-bit CRC value
170
+ """
171
+ crc = 0
172
+ for byte in data:
173
+ crc ^= byte
174
+ for _ in range(8):
175
+ if crc & 0x01:
176
+ crc = (crc >> 1) ^ 0x8C
177
+ else:
178
+ crc >>= 1
179
+ return crc
180
+
181
+
182
+ class OneWireDecoder(AsyncDecoder):
183
+ """Dallas/Maxim 1-Wire protocol decoder.
184
+
185
+ Decodes 1-Wire bus communication including reset/presence,
186
+ ROM commands, and data transfers. Supports both standard
187
+ and overdrive speeds.
188
+
189
+ Attributes:
190
+ id: "onewire"
191
+ name: "1-Wire"
192
+ channels: [data] (required)
193
+
194
+ Example:
195
+ >>> decoder = OneWireDecoder(mode="standard")
196
+ >>> for packet in decoder.decode(trace):
197
+ ... if packet.annotations.get('rom_id'):
198
+ ... print(f"Device: {packet.annotations['rom_id']}")
199
+ """
200
+
201
+ id = "onewire"
202
+ name = "1-Wire"
203
+ longname = "Dallas/Maxim 1-Wire Protocol"
204
+ desc = "1-Wire bus decoder with ROM ID extraction"
205
+
206
+ channels = [ # noqa: RUF012
207
+ ChannelDef("data", "DQ", "1-Wire data line", required=True),
208
+ ]
209
+
210
+ optional_channels = [] # noqa: RUF012
211
+
212
+ options = [ # noqa: RUF012
213
+ OptionDef(
214
+ "mode",
215
+ "Speed mode",
216
+ "Standard or overdrive",
217
+ default="standard",
218
+ values=["standard", "overdrive"],
219
+ ),
220
+ OptionDef(
221
+ "threshold",
222
+ "Voltage threshold",
223
+ "Logic threshold in volts (auto for midpoint)",
224
+ default="auto",
225
+ values=None,
226
+ ),
227
+ ]
228
+
229
+ annotations = [ # noqa: RUF012
230
+ ("reset", "Reset pulse"),
231
+ ("presence", "Presence pulse"),
232
+ ("bit", "Data bit"),
233
+ ("byte", "Decoded byte"),
234
+ ("rom_cmd", "ROM command"),
235
+ ("rom_id", "ROM ID"),
236
+ ("error", "Protocol error"),
237
+ ]
238
+
239
+ def __init__(
240
+ self,
241
+ mode: str = "standard",
242
+ threshold: str | float = "auto",
243
+ ) -> None:
244
+ """Initialize 1-Wire decoder.
245
+
246
+ Args:
247
+ mode: Speed mode ("standard" or "overdrive").
248
+ threshold: Logic threshold voltage or "auto".
249
+ """
250
+ super().__init__(mode=mode, threshold=threshold)
251
+ self._mode = OneWireMode(mode)
252
+ self._threshold = threshold
253
+ self._timings = (
254
+ OneWireTimings() if self._mode == OneWireMode.STANDARD else OneWireTimings.overdrive()
255
+ )
256
+
257
+ def decode(
258
+ self,
259
+ trace: DigitalTrace | WaveformTrace,
260
+ **channels: NDArray[np.bool_],
261
+ ) -> Iterator[ProtocolPacket]:
262
+ """Decode 1-Wire protocol data.
263
+
264
+ Args:
265
+ trace: Input trace (digital or analog).
266
+ **channels: Additional channel data.
267
+
268
+ Yields:
269
+ Decoded data as ProtocolPacket objects.
270
+
271
+ Example:
272
+ >>> decoder = OneWireDecoder()
273
+ >>> for packet in decoder.decode(trace):
274
+ ... print(f"Command: {packet.annotations.get('rom_command')}")
275
+ """
276
+ # Convert to digital if needed
277
+ if isinstance(trace, WaveformTrace):
278
+ from oscura.analyzers.digital.extraction import to_digital
279
+
280
+ threshold = self._threshold if self._threshold != "auto" else "auto"
281
+ digital_trace = to_digital(trace, threshold=threshold) # type: ignore[arg-type]
282
+ else:
283
+ digital_trace = trace
284
+
285
+ data = digital_trace.data.astype(bool)
286
+ sample_rate = digital_trace.metadata.sample_rate
287
+
288
+ # Convert timing specs to samples
289
+ us_to_samples = sample_rate / 1_000_000
290
+
291
+ # Find all falling and rising edges
292
+ falling = np.where((data[:-1]) & (~data[1:]))[0]
293
+ rising = np.where((~data[:-1]) & (data[1:]))[0]
294
+
295
+ if len(falling) == 0:
296
+ return
297
+
298
+ # State machine for decoding
299
+ decoded_bytes: list[int] = []
300
+ current_bits: list[int] = []
301
+ rom_id: OneWireROMID | None = None
302
+ rom_command: int | None = None
303
+ errors: list[str] = []
304
+ transaction_start: float = falling[0] / sample_rate
305
+
306
+ i = 0
307
+ while i < len(falling):
308
+ fall_idx = falling[i]
309
+ fall_time = fall_idx / sample_rate
310
+
311
+ # Find corresponding rising edge
312
+ rise_candidates = rising[rising > fall_idx]
313
+ if len(rise_candidates) == 0:
314
+ break
315
+
316
+ rise_idx = rise_candidates[0]
317
+ low_duration_us = (rise_idx - fall_idx) / us_to_samples
318
+
319
+ # Check for reset pulse
320
+ if low_duration_us >= self._timings.reset_min * 0.8:
321
+ # This is a reset pulse
322
+ if decoded_bytes:
323
+ # Yield previous transaction
324
+ annotations = self._build_annotations(decoded_bytes, rom_command, rom_id)
325
+ yield ProtocolPacket(
326
+ timestamp=transaction_start,
327
+ protocol="1-wire",
328
+ data=bytes(decoded_bytes),
329
+ annotations=annotations,
330
+ errors=errors.copy() if errors else None, # type: ignore[arg-type]
331
+ )
332
+
333
+ # Start new transaction
334
+ transaction_start = fall_time
335
+ decoded_bytes = []
336
+ current_bits = []
337
+ rom_id = None
338
+ rom_command = None
339
+ errors = []
340
+
341
+ self.put_annotation(
342
+ fall_time,
343
+ rise_idx / sample_rate,
344
+ AnnotationLevel.BITS,
345
+ "Reset",
346
+ )
347
+
348
+ # Look for presence pulse (pulled low by slave)
349
+ next_falls = falling[falling > rise_idx]
350
+ if len(next_falls) > 0:
351
+ next_fall = next_falls[0]
352
+ wait_time_us = (next_fall - rise_idx) / us_to_samples
353
+ if wait_time_us < self._timings.presence_max * 2:
354
+ # Found presence response
355
+ next_rises = rising[rising > next_fall]
356
+ if len(next_rises) > 0:
357
+ presence_end = next_rises[0]
358
+ presence_us = (presence_end - next_fall) / us_to_samples
359
+ if (
360
+ self._timings.presence_min * 0.5
361
+ <= presence_us
362
+ <= self._timings.presence_max * 1.5
363
+ ):
364
+ self.put_annotation(
365
+ next_fall / sample_rate,
366
+ presence_end / sample_rate,
367
+ AnnotationLevel.BITS,
368
+ "Presence",
369
+ )
370
+ i += 1
371
+ continue
372
+
373
+ # Data bit - determine if 0 or 1
374
+ # Short low pulse = write 1 or read 1
375
+ # Long low pulse = write 0 or read 0
376
+ if low_duration_us < self._timings.write_1_low_max * 2:
377
+ bit = 1
378
+ elif low_duration_us >= self._timings.write_0_low_min * 0.5:
379
+ bit = 0
380
+ else:
381
+ # Ambiguous timing
382
+ bit = 1 if low_duration_us < self._timings.slot_min * 0.5 else 0
383
+
384
+ current_bits.append(bit)
385
+
386
+ # Assemble bytes (LSB first)
387
+ if len(current_bits) == 8:
388
+ byte_val = sum(b << i for i, b in enumerate(current_bits))
389
+ decoded_bytes.append(byte_val)
390
+ current_bits = []
391
+
392
+ # Check for ROM command (first byte after reset)
393
+ if len(decoded_bytes) == 1:
394
+ rom_command = byte_val
395
+ cmd_name = ROM_COMMAND_NAMES.get(byte_val, f"Unknown (0x{byte_val:02X})")
396
+ self.put_annotation(
397
+ fall_time,
398
+ rise_idx / sample_rate,
399
+ AnnotationLevel.BYTES,
400
+ f"ROM Cmd: {cmd_name}",
401
+ )
402
+
403
+ # Check for ROM ID (8 bytes after ROM command)
404
+ if len(decoded_bytes) == 9 and rom_command in (
405
+ OneWireROMCommand.READ_ROM.value,
406
+ OneWireROMCommand.MATCH_ROM.value,
407
+ ):
408
+ try:
409
+ rom_id = OneWireROMID.from_bytes(bytes(decoded_bytes[1:9]))
410
+ if not rom_id.verify_crc():
411
+ errors.append("ROM ID CRC error")
412
+ self.put_annotation(
413
+ transaction_start,
414
+ rise_idx / sample_rate,
415
+ AnnotationLevel.BYTES,
416
+ f"ROM: {rom_id.to_hex()}",
417
+ )
418
+ except ValueError as e:
419
+ errors.append(f"ROM parse error: {e}")
420
+
421
+ i += 1
422
+
423
+ # Yield final transaction if any
424
+ if decoded_bytes:
425
+ annotations = self._build_annotations(decoded_bytes, rom_command, rom_id)
426
+ yield ProtocolPacket(
427
+ timestamp=transaction_start,
428
+ protocol="1-wire",
429
+ data=bytes(decoded_bytes),
430
+ annotations=annotations,
431
+ errors=errors if errors else None, # type: ignore[arg-type]
432
+ )
433
+
434
+ def _build_annotations(
435
+ self,
436
+ decoded_bytes: list[int],
437
+ rom_command: int | None,
438
+ rom_id: OneWireROMID | None,
439
+ ) -> dict: # type: ignore[type-arg]
440
+ """Build annotation dictionary for packet."""
441
+ annotations: dict = { # type: ignore[type-arg]
442
+ "mode": self._mode.value,
443
+ "byte_count": len(decoded_bytes),
444
+ }
445
+
446
+ if rom_command is not None:
447
+ annotations["rom_command"] = ROM_COMMAND_NAMES.get(rom_command, f"0x{rom_command:02X}")
448
+ annotations["rom_command_code"] = rom_command
449
+
450
+ if rom_id is not None:
451
+ annotations["rom_id"] = rom_id.to_hex()
452
+ annotations["family_code"] = rom_id.family_code
453
+ annotations["family_name"] = rom_id.family_name
454
+ annotations["serial_number"] = rom_id.serial_number.hex().upper()
455
+ annotations["crc_valid"] = rom_id.verify_crc()
456
+
457
+ return annotations
458
+
459
+
460
+ def decode_onewire(
461
+ data: NDArray[np.bool_] | WaveformTrace | DigitalTrace,
462
+ sample_rate: float = 1.0,
463
+ mode: str = "standard",
464
+ ) -> list[ProtocolPacket]:
465
+ """Convenience function to decode 1-Wire protocol data.
466
+
467
+ Args:
468
+ data: 1-Wire signal (digital array or trace).
469
+ sample_rate: Sample rate in Hz.
470
+ mode: Speed mode ("standard" or "overdrive").
471
+
472
+ Returns:
473
+ List of decoded packets.
474
+
475
+ Example:
476
+ >>> packets = decode_onewire(signal, sample_rate=1e6)
477
+ >>> for pkt in packets:
478
+ ... if 'rom_id' in pkt.annotations:
479
+ ... print(f"Device: {pkt.annotations['rom_id']}")
480
+ """
481
+ decoder = OneWireDecoder(mode=mode)
482
+ if isinstance(data, WaveformTrace | DigitalTrace):
483
+ return list(decoder.decode(data))
484
+ else:
485
+ trace = DigitalTrace(
486
+ data=data.astype(bool),
487
+ metadata=TraceMetadata(sample_rate=sample_rate),
488
+ )
489
+ return list(decoder.decode(trace))
490
+
491
+
492
+ __all__ = [
493
+ "FAMILY_CODES",
494
+ "ROM_COMMAND_NAMES",
495
+ "OneWireDecoder",
496
+ "OneWireMode",
497
+ "OneWireROMCommand",
498
+ "OneWireROMID",
499
+ "OneWireTimings",
500
+ "decode_onewire",
501
+ ]