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,368 @@
1
+ """I2C protocol decoder.
2
+
3
+ This module provides I2C (Inter-Integrated Circuit) protocol decoding
4
+ with ACK/NAK detection, arbitration monitoring, and multi-speed support.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.protocols.i2c import I2CDecoder
9
+ >>> decoder = I2CDecoder()
10
+ >>> for packet in decoder.decode(sda=sda, scl=scl):
11
+ ... print(f"Address: 0x{packet.annotations['address']:02X}")
12
+
13
+ References:
14
+ I2C Specification (NXP UM10204)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass
20
+ from enum import Enum
21
+ from typing import TYPE_CHECKING
22
+
23
+ import numpy as np
24
+
25
+ from oscura.analyzers.protocols.base import (
26
+ AnnotationLevel,
27
+ ChannelDef,
28
+ OptionDef,
29
+ SyncDecoder,
30
+ )
31
+ from oscura.core.types import DigitalTrace, ProtocolPacket
32
+
33
+ if TYPE_CHECKING:
34
+ from collections.abc import Iterator
35
+
36
+ from numpy.typing import NDArray
37
+
38
+
39
+ class I2CCondition(Enum):
40
+ """I2C bus conditions."""
41
+
42
+ START = "start"
43
+ STOP = "stop"
44
+ REPEATED_START = "repeated_start"
45
+ ACK = "ack"
46
+ NAK = "nak"
47
+
48
+
49
+ @dataclass
50
+ class I2CTransaction:
51
+ """I2C transaction record.
52
+
53
+ Attributes:
54
+ address: 7-bit or 10-bit device address.
55
+ read: True for read, False for write.
56
+ data: Data bytes transferred.
57
+ acks: List of ACK (True) / NAK (False) for each byte.
58
+ errors: List of detected errors.
59
+ """
60
+
61
+ address: int
62
+ read: bool
63
+ data: list[int]
64
+ acks: list[bool]
65
+ errors: list[str]
66
+
67
+
68
+ class I2CDecoder(SyncDecoder):
69
+ """I2C protocol decoder.
70
+
71
+ Decodes I2C bus transactions with ACK/NAK detection,
72
+ arbitration monitoring, and support for standard, fast,
73
+ and high-speed modes.
74
+
75
+ Example:
76
+ >>> decoder = I2CDecoder()
77
+ >>> for packet in decoder.decode(sda=sda, scl=scl, sample_rate=10e6):
78
+ ... print(f"Addr: 0x{packet.annotations['address']:02X}")
79
+ ... print(f"Data: {packet.data.hex()}")
80
+ """
81
+
82
+ id = "i2c"
83
+ name = "I2C"
84
+ longname = "Inter-Integrated Circuit"
85
+ desc = "I2C bus protocol decoder"
86
+
87
+ channels = [ # noqa: RUF012
88
+ ChannelDef("scl", "SCL", "Clock line", required=True),
89
+ ChannelDef("sda", "SDA", "Data line", required=True),
90
+ ]
91
+
92
+ optional_channels = [] # noqa: RUF012
93
+
94
+ options = [ # noqa: RUF012
95
+ OptionDef(
96
+ "address_format",
97
+ "Address format",
98
+ "7-bit or 10-bit",
99
+ default="auto",
100
+ values=["auto", "7bit", "10bit"],
101
+ ),
102
+ ]
103
+
104
+ annotations = [ # noqa: RUF012
105
+ ("start", "Start condition"),
106
+ ("stop", "Stop condition"),
107
+ ("address", "Device address"),
108
+ ("data", "Data byte"),
109
+ ("ack", "ACK"),
110
+ ("nak", "NAK"),
111
+ ("error", "Error"),
112
+ ]
113
+
114
+ def __init__(
115
+ self,
116
+ address_format: str = "auto",
117
+ ) -> None:
118
+ """Initialize I2C decoder.
119
+
120
+ Args:
121
+ address_format: Address format ("auto", "7bit", "10bit").
122
+ """
123
+ super().__init__(address_format=address_format)
124
+ self._address_format = address_format
125
+
126
+ def decode( # type: ignore[override]
127
+ self,
128
+ trace: DigitalTrace | None = None,
129
+ *,
130
+ scl: NDArray[np.bool_] | None = None,
131
+ sda: NDArray[np.bool_] | None = None,
132
+ sample_rate: float = 1.0,
133
+ ) -> Iterator[ProtocolPacket]:
134
+ """Decode I2C transactions.
135
+
136
+ Args:
137
+ trace: Optional primary trace.
138
+ scl: Clock signal.
139
+ sda: Data signal.
140
+ sample_rate: Sample rate in Hz.
141
+
142
+ Yields:
143
+ Decoded I2C transactions as ProtocolPacket objects.
144
+
145
+ Example:
146
+ >>> decoder = I2CDecoder()
147
+ >>> for pkt in decoder.decode(scl=scl, sda=sda, sample_rate=10e6):
148
+ ... print(f"Address: 0x{pkt.annotations['address']:02X}")
149
+ """
150
+ if scl is None or sda is None:
151
+ return
152
+
153
+ n_samples = min(len(scl), len(sda))
154
+ scl = scl[:n_samples]
155
+ sda = sda[:n_samples]
156
+
157
+ # Find start and stop conditions
158
+ # START: SDA falls while SCL is high
159
+ # STOP: SDA rises while SCL is high
160
+
161
+ conditions = []
162
+
163
+ for i in range(1, n_samples):
164
+ if scl[i] and scl[i - 1]: # SCL is high
165
+ if sda[i - 1] and not sda[i]: # SDA falling
166
+ conditions.append((i, I2CCondition.START))
167
+ elif not sda[i - 1] and sda[i]: # SDA rising
168
+ conditions.append((i, I2CCondition.STOP))
169
+
170
+ if len(conditions) == 0:
171
+ return
172
+
173
+ # Process transactions between START and STOP
174
+ trans_idx = 0
175
+ i = 0
176
+
177
+ while i < len(conditions):
178
+ if conditions[i][1] != I2CCondition.START:
179
+ i += 1
180
+ continue
181
+
182
+ start_idx = conditions[i][0]
183
+ start_time = start_idx / sample_rate
184
+
185
+ # Find corresponding STOP or next START
186
+ end_cond_idx = i + 1
187
+ while end_cond_idx < len(conditions):
188
+ if conditions[end_cond_idx][1] == I2CCondition.STOP:
189
+ break
190
+ if conditions[end_cond_idx][1] == I2CCondition.START:
191
+ # Repeated START
192
+ break
193
+ end_cond_idx += 1
194
+
195
+ if end_cond_idx >= len(conditions):
196
+ break
197
+
198
+ end_idx = conditions[end_cond_idx][0]
199
+ is_repeated = conditions[end_cond_idx][1] == I2CCondition.START
200
+
201
+ # Extract bytes from this transaction
202
+ bytes_data, acks = self._extract_bytes(
203
+ scl[start_idx:end_idx],
204
+ sda[start_idx:end_idx],
205
+ )
206
+
207
+ if len(bytes_data) == 0:
208
+ i = end_cond_idx
209
+ continue
210
+
211
+ # First byte is address + R/W
212
+ address_byte = bytes_data[0]
213
+ address = address_byte >> 1
214
+ is_read = (address_byte & 1) == 1
215
+
216
+ # Check for 10-bit address
217
+ is_10bit = False
218
+ actual_address = address
219
+
220
+ if self._address_format == "10bit" or (
221
+ self._address_format == "auto" and (address_byte >> 3) == 0b11110
222
+ ):
223
+ # 10-bit address format
224
+ if len(bytes_data) >= 2:
225
+ is_10bit = True
226
+ high_bits = (address_byte >> 1) & 0b11
227
+ low_bits = bytes_data[1]
228
+ actual_address = (high_bits << 8) | low_bits
229
+ data_bytes = bytes_data[2:]
230
+ data_acks = acks[2:] if len(acks) > 2 else []
231
+ else:
232
+ data_bytes = []
233
+ data_acks = []
234
+ else:
235
+ actual_address = address
236
+ data_bytes = bytes_data[1:]
237
+ data_acks = acks[1:] if len(acks) > 1 else []
238
+
239
+ # Check for errors
240
+ errors = []
241
+ if len(acks) > 0 and not acks[0]:
242
+ errors.append("NAK on address")
243
+
244
+ for j, (_byte, ack) in enumerate(zip(data_bytes, data_acks, strict=False)):
245
+ if not ack and not is_read:
246
+ errors.append(f"NAK on byte {j}")
247
+
248
+ end_time = end_idx / sample_rate
249
+
250
+ # Add annotations
251
+ self.put_annotation(
252
+ start_time,
253
+ start_time + 1e-6,
254
+ AnnotationLevel.BITS,
255
+ "START" if not is_repeated else "Sr",
256
+ )
257
+
258
+ addr_text = f"0x{actual_address:02X}" if not is_10bit else f"0x{actual_address:03X}"
259
+ self.put_annotation(
260
+ start_time,
261
+ end_time,
262
+ AnnotationLevel.FIELDS,
263
+ f"{addr_text} {'R' if is_read else 'W'}",
264
+ )
265
+
266
+ # Create packet
267
+ annotations = {
268
+ "address": actual_address,
269
+ "address_10bit": is_10bit,
270
+ "read": is_read,
271
+ "bytes": bytes_data,
272
+ "acks": acks,
273
+ "transaction_num": trans_idx,
274
+ }
275
+
276
+ packet = ProtocolPacket(
277
+ timestamp=start_time,
278
+ protocol="i2c",
279
+ data=bytes(data_bytes),
280
+ annotations=annotations,
281
+ errors=errors,
282
+ )
283
+
284
+ yield packet
285
+
286
+ trans_idx += 1
287
+ i = end_cond_idx
288
+
289
+ if is_repeated:
290
+ continue
291
+ else:
292
+ i += 1
293
+
294
+ def _extract_bytes(
295
+ self,
296
+ scl: NDArray[np.bool_],
297
+ sda: NDArray[np.bool_],
298
+ ) -> tuple[list[int], list[bool]]:
299
+ """Extract bytes from I2C transaction.
300
+
301
+ Args:
302
+ scl: Clock signal segment.
303
+ sda: Data signal segment.
304
+
305
+ Returns:
306
+ (bytes, acks) - List of byte values and ACK flags.
307
+ """
308
+ # Find rising edges of SCL (data sampling points)
309
+ rising_edges = np.where(~scl[:-1] & scl[1:])[0] + 1
310
+
311
+ if len(rising_edges) < 9: # Need at least 8 data bits + ACK
312
+ return [], []
313
+
314
+ bytes_data = []
315
+ acks = []
316
+
317
+ i = 0
318
+ while i + 9 <= len(rising_edges):
319
+ # Extract 8 data bits (MSB first)
320
+ byte_val = 0
321
+ for bit_idx in range(8):
322
+ sample_idx = rising_edges[i + bit_idx]
323
+ if sample_idx < len(sda):
324
+ bit = 1 if sda[sample_idx] else 0
325
+ byte_val = (byte_val << 1) | bit
326
+
327
+ # Extract ACK bit (9th bit, low = ACK, high = NAK)
328
+ ack_idx = rising_edges[i + 8]
329
+ if ack_idx < len(sda):
330
+ ack = not sda[ack_idx] # Low = ACK
331
+ else:
332
+ ack = False
333
+
334
+ bytes_data.append(byte_val)
335
+ acks.append(ack)
336
+
337
+ i += 9
338
+
339
+ return bytes_data, acks
340
+
341
+
342
+ def decode_i2c(
343
+ scl: NDArray[np.bool_],
344
+ sda: NDArray[np.bool_],
345
+ sample_rate: float = 1.0,
346
+ address_format: str = "auto",
347
+ ) -> list[ProtocolPacket]:
348
+ """Convenience function to decode I2C transactions.
349
+
350
+ Args:
351
+ scl: Clock signal.
352
+ sda: Data signal.
353
+ sample_rate: Sample rate in Hz.
354
+ address_format: Address format ("auto", "7bit", "10bit").
355
+
356
+ Returns:
357
+ List of decoded I2C transactions.
358
+
359
+ Example:
360
+ >>> packets = decode_i2c(scl, sda, sample_rate=10e6)
361
+ >>> for pkt in packets:
362
+ ... print(f"Address: 0x{pkt.annotations['address']:02X}")
363
+ """
364
+ decoder = I2CDecoder(address_format=address_format)
365
+ return list(decoder.decode(scl=scl, sda=sda, sample_rate=sample_rate))
366
+
367
+
368
+ __all__ = ["I2CCondition", "I2CDecoder", "I2CTransaction", "decode_i2c"]
@@ -0,0 +1,296 @@
1
+ """I2S protocol decoder.
2
+
3
+ This module provides Inter-IC Sound (I2S) audio protocol decoding
4
+ with support for standard, left-justified, and right-justified modes.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.protocols.i2s import I2SDecoder
9
+ >>> decoder = I2SDecoder(bit_depth=16)
10
+ >>> for packet in decoder.decode(bck=bck, ws=ws, sd=sd):
11
+ ... print(f"Left: {packet.annotations['left_sample']}")
12
+
13
+ References:
14
+ I2S Bus Specification (Philips Semiconductors)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from enum import Enum
20
+ from typing import TYPE_CHECKING, Literal
21
+
22
+ import numpy as np
23
+
24
+ from oscura.analyzers.protocols.base import (
25
+ AnnotationLevel,
26
+ ChannelDef,
27
+ OptionDef,
28
+ SyncDecoder,
29
+ )
30
+ from oscura.core.types import DigitalTrace, ProtocolPacket
31
+
32
+ if TYPE_CHECKING:
33
+ from collections.abc import Iterator
34
+
35
+ from numpy.typing import NDArray
36
+
37
+
38
+ class I2SMode(Enum):
39
+ """I2S alignment modes."""
40
+
41
+ STANDARD = "standard" # MSB 1 clock after WS change
42
+ LEFT_JUSTIFIED = "left_justified" # MSB at WS change
43
+ RIGHT_JUSTIFIED = "right_justified" # MSB before WS change
44
+
45
+
46
+ class I2SDecoder(SyncDecoder):
47
+ """I2S protocol decoder.
48
+
49
+ Decodes I2S audio bus transactions with configurable bit depth
50
+ and alignment modes (standard, left-justified, right-justified).
51
+
52
+ Attributes:
53
+ id: "i2s"
54
+ name: "I2S"
55
+ channels: [bck, ws, sd] (required)
56
+
57
+ Example:
58
+ >>> decoder = I2SDecoder(bit_depth=24, mode="standard")
59
+ >>> for packet in decoder.decode(bck=bck, ws=ws, sd=sd, sample_rate=1e6):
60
+ ... print(f"Stereo: L={packet.annotations['left']} R={packet.annotations['right']}")
61
+ """
62
+
63
+ id = "i2s"
64
+ name = "I2S"
65
+ longname = "Inter-IC Sound"
66
+ desc = "I2S audio bus protocol decoder"
67
+
68
+ channels = [ # noqa: RUF012
69
+ ChannelDef("bck", "BCK", "Bit Clock (SCLK)", required=True),
70
+ ChannelDef("ws", "WS", "Word Select (LRCLK)", required=True),
71
+ ChannelDef("sd", "SD", "Serial Data", required=True),
72
+ ]
73
+
74
+ optional_channels = [] # noqa: RUF012
75
+
76
+ options = [ # noqa: RUF012
77
+ OptionDef(
78
+ "bit_depth",
79
+ "Bit depth",
80
+ "Bits per sample",
81
+ default=16,
82
+ values=[8, 16, 24, 32],
83
+ ),
84
+ OptionDef(
85
+ "mode",
86
+ "Mode",
87
+ "Alignment mode",
88
+ default="standard",
89
+ values=["standard", "left_justified", "right_justified"],
90
+ ),
91
+ ]
92
+
93
+ annotations = [ # noqa: RUF012
94
+ ("left", "Left channel sample"),
95
+ ("right", "Right channel sample"),
96
+ ("word", "Word boundary"),
97
+ ]
98
+
99
+ def __init__(
100
+ self,
101
+ bit_depth: int = 16,
102
+ mode: Literal["standard", "left_justified", "right_justified"] = "standard",
103
+ ) -> None:
104
+ """Initialize I2S decoder.
105
+
106
+ Args:
107
+ bit_depth: Bits per sample (8, 16, 24, 32).
108
+ mode: Alignment mode.
109
+ """
110
+ super().__init__(bit_depth=bit_depth, mode=mode)
111
+ self._bit_depth = bit_depth
112
+ self._mode = I2SMode(mode)
113
+
114
+ def decode( # type: ignore[override]
115
+ self,
116
+ trace: DigitalTrace | None = None,
117
+ *,
118
+ bck: NDArray[np.bool_] | None = None,
119
+ ws: NDArray[np.bool_] | None = None,
120
+ sd: NDArray[np.bool_] | None = None,
121
+ sample_rate: float = 1.0,
122
+ ) -> Iterator[ProtocolPacket]:
123
+ """Decode I2S audio data.
124
+
125
+ Args:
126
+ trace: Optional primary trace.
127
+ bck: Bit Clock signal.
128
+ ws: Word Select signal (0=left, 1=right).
129
+ sd: Serial Data signal.
130
+ sample_rate: Sample rate in Hz.
131
+
132
+ Yields:
133
+ Decoded I2S samples as ProtocolPacket objects.
134
+
135
+ Example:
136
+ >>> decoder = I2SDecoder(bit_depth=16)
137
+ >>> for pkt in decoder.decode(bck=bck, ws=ws, sd=sd, sample_rate=1e6):
138
+ ... print(f"Left: {pkt.annotations['left_sample']}")
139
+ """
140
+ if bck is None or ws is None or sd is None:
141
+ return
142
+
143
+ n_samples = min(len(bck), len(ws), len(sd))
144
+ bck = bck[:n_samples]
145
+ ws = ws[:n_samples]
146
+ sd = sd[:n_samples]
147
+
148
+ # Find rising edges of BCK (data sampled on rising edge in I2S)
149
+ rising_edges = np.where(~bck[:-1] & bck[1:])[0] + 1
150
+
151
+ # Find WS transitions to identify word boundaries
152
+ ws_transitions = np.where(ws[:-1] != ws[1:])[0] + 1
153
+
154
+ if len(rising_edges) == 0 or len(ws_transitions) == 0:
155
+ return
156
+
157
+ trans_num = 0
158
+ ws_idx = 0
159
+
160
+ while ws_idx < len(ws_transitions) - 1:
161
+ # Get word boundaries
162
+ word_start_idx = ws_transitions[ws_idx]
163
+ word_end_idx = ws_transitions[ws_idx + 1]
164
+
165
+ # Determine channel (WS=0 is left, WS=1 is right in standard I2S)
166
+ is_left = not ws[word_start_idx]
167
+
168
+ # Find BCK edges in this word period
169
+ word_edges = rising_edges[
170
+ (rising_edges >= word_start_idx) & (rising_edges < word_end_idx)
171
+ ]
172
+
173
+ if len(word_edges) == 0:
174
+ ws_idx += 1
175
+ continue
176
+
177
+ # In standard I2S mode, data starts 1 clock after WS change
178
+ # In left-justified mode, data starts at WS change
179
+ # In right-justified mode, data is aligned to end of word period
180
+ if self._mode == I2SMode.STANDARD:
181
+ # Skip first edge (data starts on second edge)
182
+ data_edges = word_edges[1:] if len(word_edges) > 1 else []
183
+ elif self._mode == I2SMode.LEFT_JUSTIFIED:
184
+ data_edges = word_edges
185
+ else: # RIGHT_JUSTIFIED
186
+ # Take last bit_depth edges
187
+ data_edges = (
188
+ word_edges[-self._bit_depth :]
189
+ if len(word_edges) >= self._bit_depth
190
+ else word_edges
191
+ )
192
+
193
+ # Extract sample data (MSB first)
194
+ sample_bits = []
195
+ for edge_idx in data_edges[: self._bit_depth]:
196
+ if edge_idx < len(sd):
197
+ sample_bits.append(1 if sd[edge_idx] else 0)
198
+
199
+ if len(sample_bits) < self._bit_depth:
200
+ # Incomplete sample, pad with zeros
201
+ sample_bits.extend([0] * (self._bit_depth - len(sample_bits)))
202
+
203
+ # Convert to signed integer value (MSB first, two's complement)
204
+ sample_value = 0
205
+ for bit in sample_bits:
206
+ sample_value = (sample_value << 1) | bit
207
+
208
+ # Convert from unsigned to signed (two's complement)
209
+ if sample_bits[0] == 1: # Negative number
210
+ sample_value = sample_value - (1 << self._bit_depth)
211
+
212
+ # Calculate timing
213
+ start_time = word_start_idx / sample_rate
214
+ end_time = word_end_idx / sample_rate
215
+
216
+ # Store left and right channels
217
+ if ws_idx % 2 == 0:
218
+ # First word of stereo pair
219
+ left_sample = sample_value if is_left else 0
220
+ right_sample = 0 if is_left else sample_value
221
+ first_start_time = start_time
222
+ else:
223
+ # Second word of stereo pair - emit packet
224
+ if is_left:
225
+ left_sample = sample_value
226
+ else:
227
+ right_sample = sample_value
228
+
229
+ # Add annotation
230
+ self.put_annotation(
231
+ first_start_time,
232
+ end_time,
233
+ AnnotationLevel.PACKETS,
234
+ f"L: {left_sample} / R: {right_sample}",
235
+ )
236
+
237
+ # Create packet
238
+ annotations = {
239
+ "sample_num": trans_num,
240
+ "left_sample": left_sample,
241
+ "right_sample": right_sample,
242
+ "bit_depth": self._bit_depth,
243
+ "mode": self._mode.value,
244
+ }
245
+
246
+ # Encode as bytes (little-endian, signed)
247
+ byte_count = (self._bit_depth + 7) // 8
248
+ left_bytes = left_sample.to_bytes(byte_count, "little", signed=True)
249
+ right_bytes = right_sample.to_bytes(byte_count, "little", signed=True)
250
+ data_bytes = left_bytes + right_bytes
251
+
252
+ packet = ProtocolPacket(
253
+ timestamp=first_start_time,
254
+ protocol="i2s",
255
+ data=data_bytes,
256
+ annotations=annotations,
257
+ errors=[],
258
+ )
259
+
260
+ yield packet
261
+ trans_num += 1
262
+
263
+ ws_idx += 1
264
+
265
+
266
+ def decode_i2s(
267
+ bck: NDArray[np.bool_],
268
+ ws: NDArray[np.bool_],
269
+ sd: NDArray[np.bool_],
270
+ sample_rate: float = 1.0,
271
+ bit_depth: int = 16,
272
+ mode: Literal["standard", "left_justified", "right_justified"] = "standard",
273
+ ) -> list[ProtocolPacket]:
274
+ """Convenience function to decode I2S audio data.
275
+
276
+ Args:
277
+ bck: Bit Clock signal.
278
+ ws: Word Select signal.
279
+ sd: Serial Data signal.
280
+ sample_rate: Sample rate in Hz.
281
+ bit_depth: Bits per sample (8, 16, 24, 32).
282
+ mode: Alignment mode.
283
+
284
+ Returns:
285
+ List of decoded I2S stereo samples.
286
+
287
+ Example:
288
+ >>> packets = decode_i2s(bck, ws, sd, sample_rate=1e6, bit_depth=16)
289
+ >>> for pkt in packets:
290
+ ... print(f"L={pkt.annotations['left_sample']}, R={pkt.annotations['right_sample']}")
291
+ """
292
+ decoder = I2SDecoder(bit_depth=bit_depth, mode=mode)
293
+ return list(decoder.decode(bck=bck, ws=ws, sd=sd, sample_rate=sample_rate))
294
+
295
+
296
+ __all__ = ["I2SDecoder", "I2SMode", "decode_i2s"]