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,358 @@
1
+ """Error-tolerant protocol parsing with timestamp correction.
2
+
3
+
4
+ This module provides robust protocol decoding that continues after errors
5
+ and timestamp correction for jittery captures.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from typing import Any, Literal
11
+
12
+ import numpy as np
13
+ from numpy.typing import NDArray
14
+ from scipy import signal
15
+
16
+
17
+ class ErrorTolerance(Enum):
18
+ """Error tolerance modes for protocol decoding.
19
+
20
+ Attributes:
21
+ STRICT: Abort on first error (backward compatible)
22
+ TOLERANT: Skip error frame, resync, continue (default)
23
+ PERMISSIVE: Best-effort decode, report all errors
24
+ """
25
+
26
+ STRICT = "strict"
27
+ TOLERANT = "tolerant"
28
+ PERMISSIVE = "permissive"
29
+
30
+
31
+ @dataclass
32
+ class DecodedFrame:
33
+ """Decoded protocol frame with error annotation.
34
+
35
+ Attributes:
36
+ data: Decoded data bytes
37
+ timestamp: Frame timestamp in seconds
38
+ valid: Whether frame is valid or has errors
39
+ error_type: Type of error if invalid (e.g., 'framing', 'parity')
40
+ position: Byte position in original trace
41
+ """
42
+
43
+ data: bytes
44
+ timestamp: float
45
+ valid: bool
46
+ error_type: str | None
47
+ position: int
48
+
49
+
50
+ @dataclass
51
+ class TimestampCorrection:
52
+ """Result from timestamp jitter correction.
53
+
54
+ Attributes:
55
+ corrected_timestamps: Array of corrected timestamps
56
+ original_jitter_rms: RMS jitter before correction
57
+ corrected_jitter_rms: RMS jitter after correction
58
+ reduction_ratio: Jitter reduction factor (before/after)
59
+ samples_corrected: Number of samples that were adjusted
60
+ max_correction: Maximum correction applied to any sample
61
+ """
62
+
63
+ corrected_timestamps: NDArray[np.float64]
64
+ original_jitter_rms: float
65
+ corrected_jitter_rms: float
66
+ reduction_ratio: float
67
+ samples_corrected: int
68
+ max_correction: float
69
+
70
+
71
+ def correct_timestamp_jitter(
72
+ timestamps: NDArray[np.float64],
73
+ expected_rate: float,
74
+ *,
75
+ method: Literal["lowpass", "pll"] = "lowpass",
76
+ max_correction_factor: float = 2.0,
77
+ ) -> TimestampCorrection:
78
+ """Correct timestamp jitter using filtering or PLL model.
79
+
80
+ : Compensates for clock jitter in logic analyzer
81
+ captures (e.g., USB transmission jitter) while preserving phase.
82
+
83
+ Correction constraints (DAQ-003):
84
+ - Max correction per sample: ±max_correction_factor × expected_period # noqa: RUF002, RUF003
85
+ - Filter cutoff: expected_rate / 10 (removes 10× jitter frequency) # noqa: RUF002, RUF003
86
+ - Target reduction: ≥5× for typical USB jitter # noqa: RUF002, RUF003
87
+
88
+ Args:
89
+ timestamps: Original jittery timestamps in seconds
90
+ expected_rate: Expected nominal sample rate in Hz
91
+ method: Correction method ('lowpass' or 'pll')
92
+ max_correction_factor: Max correction as multiple of period
93
+
94
+ Returns:
95
+ TimestampCorrection with corrected timestamps and metrics
96
+
97
+ Raises:
98
+ ValueError: If timestamps array is empty
99
+ ValueError: If expected_rate <= 0
100
+ ValueError: If max_correction_factor <= 0
101
+
102
+ Examples:
103
+ >>> # Correct jittery timestamps from USB logic analyzer
104
+ >>> import numpy as np
105
+ >>> timestamps = np.linspace(0, 1e-3, 1000)
106
+ >>> jitter = np.random.normal(0, 1e-7, 1000) # 100ns jitter
107
+ >>> jittery = timestamps + jitter
108
+ >>> result = correct_timestamp_jitter(jittery, expected_rate=1e6)
109
+ >>> print(f"Jitter reduced by {result.reduction_ratio:.1f}x")
110
+
111
+ References:
112
+ DAQ-003: Timestamp Jitter Compensation and Clock Correction
113
+ """
114
+ if len(timestamps) == 0:
115
+ raise ValueError("Timestamps array cannot be empty")
116
+
117
+ if expected_rate <= 0:
118
+ raise ValueError("expected_rate must be positive")
119
+
120
+ if max_correction_factor <= 0:
121
+ raise ValueError("max_correction_factor must be positive")
122
+
123
+ if len(timestamps) < 3:
124
+ # Not enough data to filter
125
+ return TimestampCorrection(
126
+ corrected_timestamps=timestamps.copy(),
127
+ original_jitter_rms=0.0,
128
+ corrected_jitter_rms=0.0,
129
+ reduction_ratio=1.0,
130
+ samples_corrected=0,
131
+ max_correction=0.0,
132
+ )
133
+
134
+ expected_period = 1.0 / expected_rate
135
+ max_correction = max_correction_factor * expected_period
136
+
137
+ # Calculate original jitter
138
+ diffs = np.diff(timestamps)
139
+ original_jitter = diffs - expected_period
140
+ original_jitter_rms = float(np.sqrt(np.mean(original_jitter**2)))
141
+
142
+ # If jitter is negligible (below 1 ns), no correction needed
143
+ # This avoids correcting floating-point rounding errors in perfect timestamps
144
+ if original_jitter_rms < 1e-9:
145
+ return TimestampCorrection(
146
+ corrected_timestamps=timestamps.copy(),
147
+ original_jitter_rms=original_jitter_rms,
148
+ corrected_jitter_rms=original_jitter_rms,
149
+ reduction_ratio=1.0,
150
+ samples_corrected=0,
151
+ max_correction=0.0,
152
+ )
153
+
154
+ if method == "lowpass":
155
+ # Low-pass filter approach
156
+ # Design Butterworth filter: cutoff at expected_rate / 10
157
+ cutoff_freq = expected_rate / 10.0
158
+ nyquist = 0.5 * expected_rate
159
+
160
+ # Ensure cutoff is valid
161
+ if cutoff_freq >= nyquist:
162
+ cutoff_freq = nyquist * 0.8
163
+
164
+ # Design 2nd order Butterworth
165
+ sos = signal.butter(2, cutoff_freq / nyquist, btype="low", output="sos")
166
+
167
+ # Filter the timestamps
168
+ # Need to detrend first to avoid edge effects
169
+ t_mean = np.mean(timestamps)
170
+ t_detrended = timestamps - t_mean
171
+
172
+ # Apply filter
173
+ filtered = signal.sosfiltfilt(sos, t_detrended)
174
+ corrected = filtered + t_mean
175
+
176
+ else: # pll
177
+ # Phase-locked loop model
178
+ # Simple PLL: track expected phase and correct deviations
179
+ corrected = np.zeros_like(timestamps)
180
+ corrected[0] = timestamps[0]
181
+
182
+ # PLL state
183
+ phase = 0.0
184
+ phase_increment = 2 * np.pi * expected_rate
185
+
186
+ for i in range(1, len(timestamps)):
187
+ # Predict next timestamp based on expected rate
188
+ predicted = corrected[i - 1] + expected_period
189
+
190
+ # Measure phase error
191
+ actual = timestamps[i]
192
+ error = actual - predicted
193
+
194
+ # Apply correction with limiting
195
+ correction = np.clip(error * 0.5, -max_correction, max_correction)
196
+ corrected[i] = predicted + correction
197
+
198
+ # Update phase
199
+ phase += phase_increment * (corrected[i] - corrected[i - 1])
200
+
201
+ # Limit corrections to max_correction
202
+ corrections = corrected - timestamps
203
+ exceeded = np.abs(corrections) > max_correction
204
+ corrections[exceeded] = np.sign(corrections[exceeded]) * max_correction
205
+ corrected = timestamps + corrections
206
+
207
+ # Calculate corrected jitter
208
+ corrected_diffs = np.diff(corrected)
209
+ corrected_jitter = corrected_diffs - expected_period
210
+ corrected_jitter_rms = float(np.sqrt(np.mean(corrected_jitter**2)))
211
+
212
+ # Calculate metrics
213
+ samples_corrected = int(np.sum(np.abs(corrections) > 1e-12))
214
+ max_correction_applied = float(np.max(np.abs(corrections)))
215
+
216
+ # original_jitter_rms is always > 0 here (early return handles negligible jitter)
217
+ reduction_ratio = original_jitter_rms / max(corrected_jitter_rms, 1e-15)
218
+
219
+ return TimestampCorrection(
220
+ corrected_timestamps=corrected,
221
+ original_jitter_rms=original_jitter_rms,
222
+ corrected_jitter_rms=corrected_jitter_rms,
223
+ reduction_ratio=reduction_ratio,
224
+ samples_corrected=samples_corrected,
225
+ max_correction=max_correction_applied,
226
+ )
227
+
228
+
229
+ def decode_with_error_tolerance(
230
+ data: NDArray[np.uint8],
231
+ protocol: Literal["uart", "spi", "i2c", "can"],
232
+ *,
233
+ tolerance: ErrorTolerance = ErrorTolerance.TOLERANT,
234
+ **protocol_params: Any,
235
+ ) -> list[DecodedFrame]:
236
+ """Decode protocol with error tolerance and resynchronization.
237
+
238
+ : Continues decoding after framing/parity/stop-bit
239
+ errors instead of aborting. Applies to all protocol decoders.
240
+
241
+ Error tolerance modes (DAQ-004):
242
+ - STRICT: Abort on first error (backward compatible)
243
+ - TOLERANT: Skip error frame, resync, continue (default)
244
+ - PERMISSIVE: Best-effort decode, report all errors
245
+
246
+ Resynchronization strategies (DAQ-004):
247
+ - UART: Search for next valid start bit + stop bit pattern
248
+ - SPI: Re-align on next CS edge
249
+ - I2C: Search for next START condition
250
+ - CAN: Wait for recessive bus + next SOF
251
+
252
+ Args:
253
+ data: Raw protocol data bytes
254
+ protocol: Protocol type ('uart', 'spi', 'i2c', 'can')
255
+ tolerance: Error tolerance mode
256
+ **protocol_params: Protocol-specific parameters (baud, parity, etc.)
257
+
258
+ Returns:
259
+ List of DecodedFrame objects with data and error annotations
260
+
261
+ Raises:
262
+ ValueError: If protocol not supported
263
+ ValueError: If required protocol_params missing
264
+ Exception: Re-raised in STRICT mode if decoding fails
265
+
266
+ Examples:
267
+ >>> # Decode UART with error tolerance
268
+ >>> data = np.array([0xFF, 0x55, 0xAA, 0x00], dtype=np.uint8)
269
+ >>> frames = decode_with_error_tolerance(
270
+ ... data, 'uart', tolerance=ErrorTolerance.TOLERANT, baud=9600
271
+ ... )
272
+ >>> valid_frames = [f for f in frames if f.valid]
273
+
274
+ References:
275
+ DAQ-004: Error-Tolerant Protocol Decoding with Resynchronization
276
+ """
277
+ if protocol not in ("uart", "spi", "i2c", "can"):
278
+ raise ValueError(f"Unsupported protocol: {protocol}")
279
+
280
+ frames: list[DecodedFrame] = []
281
+ pos = 0
282
+
283
+ # Protocol-specific decode logic
284
+ # This is a simplified implementation showing the error handling pattern
285
+ # Full protocol decoders are in oscura.analyzers.protocols
286
+
287
+ if protocol == "uart":
288
+ # UART parameters
289
+ if "baud" not in protocol_params:
290
+ raise ValueError("UART requires 'baud' parameter")
291
+
292
+ # Simplified UART frame extraction with error tolerance
293
+ while pos < len(data):
294
+ try:
295
+ # Try to decode frame at current position
296
+ # This is simplified - real UART decoder would analyze bit timing
297
+
298
+ # Check for valid frame (simplified)
299
+ if pos + 1 >= len(data):
300
+ break
301
+
302
+ frame_data = bytes([data[pos]])
303
+ timestamp = float(pos) / protocol_params["baud"]
304
+
305
+ # Validate frame (simplified - would check start/stop bits)
306
+ is_valid = True
307
+ error_type = None
308
+
309
+ # Example: detect framing error (no proper stop bit)
310
+ if data[pos] == 0xFF: # Example error condition
311
+ is_valid = False
312
+ error_type = "framing"
313
+
314
+ frames.append(
315
+ DecodedFrame(
316
+ data=frame_data,
317
+ timestamp=timestamp,
318
+ valid=is_valid,
319
+ error_type=error_type,
320
+ position=pos,
321
+ )
322
+ )
323
+
324
+ if not is_valid and tolerance == ErrorTolerance.STRICT:
325
+ # Strict mode: abort on error
326
+ break
327
+ elif not is_valid and tolerance == ErrorTolerance.TOLERANT:
328
+ # Tolerant: skip error frame, resync
329
+ # Search for next valid start bit
330
+ pos += 1
331
+ # In real implementation, would search for start bit pattern
332
+ else:
333
+ # Permissive: record error, continue
334
+ pos += 1
335
+
336
+ except Exception:
337
+ if tolerance == ErrorTolerance.STRICT:
338
+ raise
339
+ else:
340
+ # Log error and continue
341
+ pos += 1
342
+
343
+ elif protocol == "spi":
344
+ # SPI: Re-align on CS edge
345
+ # Simplified placeholder
346
+ pass
347
+
348
+ elif protocol == "i2c":
349
+ # I2C: Search for START condition
350
+ # Simplified placeholder
351
+ pass
352
+
353
+ elif protocol == "can":
354
+ # CAN: Wait for SOF after error
355
+ # Simplified placeholder
356
+ pass
357
+
358
+ return frames
@@ -0,0 +1,275 @@
1
+ """Bit error pattern analysis and capture diagnostics.
2
+
3
+
4
+ This module characterizes bit error patterns to diagnose capture quality
5
+ issues (EMI, USB problems, clock jitter) and suggests likely causes.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+
11
+ import numpy as np
12
+ from numpy.typing import NDArray
13
+
14
+
15
+ class ErrorPattern(Enum):
16
+ """Classified error pattern types.
17
+
18
+ Attributes:
19
+ RANDOM: Errors uniformly distributed, no clustering (likely EMI)
20
+ BURST: Errors clustered together (likely USB transmission issue)
21
+ PERIODIC: Errors repeat at regular intervals (likely clock jitter)
22
+ UNKNOWN: Pattern doesn't match known types
23
+ """
24
+
25
+ RANDOM = "random"
26
+ BURST = "burst"
27
+ PERIODIC = "periodic"
28
+ UNKNOWN = "unknown"
29
+
30
+
31
+ @dataclass
32
+ class ErrorAnalysis:
33
+ """Result from bit error pattern analysis.
34
+
35
+ Attributes:
36
+ bit_error_rate: Ratio of errors to total bits
37
+ error_count: Total number of bit errors detected
38
+ total_bits: Total bits examined
39
+ pattern_type: Classified error pattern (random, burst, periodic)
40
+ mean_error_gap: Mean number of bits between errors
41
+ error_positions: Array of bit positions where errors occurred
42
+ diagnosis: Suggested cause based on pattern
43
+ severity: Error severity level (low, moderate, severe)
44
+ """
45
+
46
+ bit_error_rate: float
47
+ error_count: int
48
+ total_bits: int
49
+ pattern_type: ErrorPattern
50
+ mean_error_gap: float
51
+ error_positions: NDArray[np.int64]
52
+ diagnosis: str
53
+ severity: str
54
+
55
+
56
+ def analyze_bit_errors(
57
+ received: NDArray[np.uint8],
58
+ expected: NDArray[np.uint8],
59
+ *,
60
+ burst_threshold: int = 100,
61
+ periodicity_threshold: float = 0.1,
62
+ ) -> ErrorAnalysis:
63
+ """Characterize bit error patterns for capture diagnostics.
64
+
65
+ : Analyzes bit errors to diagnose capture quality
66
+ issues and distinguish between EMI, USB problems, and clock jitter.
67
+
68
+ Error pattern classification (DAQ-005):
69
+ - Random: Errors uniformly distributed, no clustering
70
+ - Burst: Errors clustered, mean_gap < 100 bits
71
+ - Periodic: Errors repeat at regular intervals (FFT peak in positions)
72
+
73
+ Diagnosis suggestions (DAQ-005):
74
+ - BER > 0.01: Severe capture issue, check connections
75
+ - BER 0.001-0.01: Moderate errors, reduce sample rate
76
+ - BER < 0.001: Acceptable, likely EMI
77
+ - Burst errors: USB transmission issue
78
+ - Periodic errors: Clock jitter or interference
79
+
80
+ Args:
81
+ received: Received bit array (actual capture)
82
+ expected: Expected bit array (golden reference)
83
+ burst_threshold: Mean gap threshold for burst classification
84
+ periodicity_threshold: FFT peak threshold for periodic detection
85
+
86
+ Returns:
87
+ ErrorAnalysis with BER, pattern type, and diagnosis
88
+
89
+ Raises:
90
+ ValueError: If received and expected have different lengths
91
+ ValueError: If arrays are empty
92
+
93
+ Examples:
94
+ >>> # Analyze random EMI errors
95
+ >>> import numpy as np
96
+ >>> expected = np.random.randint(0, 2, 10000, dtype=np.uint8)
97
+ >>> received = expected.copy()
98
+ >>> errors = np.random.choice(10000, 50, replace=False)
99
+ >>> received[errors] = 1 - received[errors] # Flip bits
100
+ >>> analysis = analyze_bit_errors(received, expected)
101
+ >>> print(f"BER: {analysis.bit_error_rate:.6f}")
102
+ >>> print(f"Pattern: {analysis.pattern_type.value}")
103
+
104
+ >>> # Analyze burst errors (USB issue)
105
+ >>> received = expected.copy()
106
+ >>> received[1000:1050] = 1 - received[1000:1050] # 50-bit burst
107
+ >>> analysis = analyze_bit_errors(received, expected)
108
+ >>> print(analysis.diagnosis)
109
+ 'USB transmission issue'
110
+
111
+ References:
112
+ DAQ-005: Bit Error Pattern Analysis and Capture Diagnostics
113
+ """
114
+ if len(received) != len(expected):
115
+ raise ValueError("Received and expected arrays must have same length")
116
+
117
+ if len(received) == 0:
118
+ raise ValueError("Arrays cannot be empty")
119
+
120
+ # Find bit errors (XOR)
121
+ errors = received != expected
122
+ error_positions = np.where(errors)[0]
123
+ error_count = len(error_positions)
124
+ total_bits = len(received)
125
+
126
+ # Calculate BER
127
+ bit_error_rate = error_count / total_bits if total_bits > 0 else 0.0
128
+
129
+ if error_count == 0:
130
+ # No errors
131
+ return ErrorAnalysis(
132
+ bit_error_rate=0.0,
133
+ error_count=0,
134
+ total_bits=total_bits,
135
+ pattern_type=ErrorPattern.RANDOM,
136
+ mean_error_gap=float(total_bits),
137
+ error_positions=error_positions,
138
+ diagnosis="No errors detected - good capture quality",
139
+ severity="low",
140
+ )
141
+
142
+ # Calculate error gaps
143
+ if error_count > 1:
144
+ error_gaps = np.diff(error_positions)
145
+ mean_gap = float(np.mean(error_gaps))
146
+ else:
147
+ mean_gap = float(total_bits)
148
+
149
+ # Classify error pattern
150
+ pattern_type = ErrorPattern.UNKNOWN
151
+ diagnosis = ""
152
+
153
+ # Check for burst pattern (errors clustered)
154
+ if error_count > 1 and mean_gap < burst_threshold:
155
+ pattern_type = ErrorPattern.BURST
156
+ diagnosis = "Burst errors detected - likely USB transmission issue"
157
+
158
+ # Check for periodic pattern (FFT analysis)
159
+ # Need at least 10 errors for reliable periodicity detection
160
+ elif error_count >= 10:
161
+ # Create binary error signal
162
+ error_signal = errors.astype(float)
163
+
164
+ # Compute FFT to detect periodicity
165
+ fft = np.fft.rfft(error_signal)
166
+ fft_mag = np.abs(fft[1:]) # Skip DC component
167
+
168
+ if len(fft_mag) > 0:
169
+ # Check if there's a strong peak relative to mean (not just max)
170
+ mean_mag = np.mean(fft_mag)
171
+ max_mag = np.max(fft_mag)
172
+ peak_ratio = max_mag / (mean_mag + 1e-12)
173
+
174
+ # Require strong peak (>10x mean) and exceeds threshold
175
+ if peak_ratio > 10 and (max_mag / (np.max(fft_mag) + 1e-12)) > periodicity_threshold:
176
+ pattern_type = ErrorPattern.PERIODIC
177
+ diagnosis = "Periodic errors detected - likely clock jitter or interference"
178
+
179
+ # If not burst or periodic, classify as random
180
+ if pattern_type == ErrorPattern.UNKNOWN:
181
+ # Check if errors are uniformly distributed
182
+ if error_count > 2:
183
+ # Use coefficient of variation of gaps
184
+ if error_count > 1:
185
+ gap_std = float(np.std(error_gaps))
186
+ gap_cv = gap_std / (mean_gap + 1e-12)
187
+
188
+ if gap_cv < 1.0: # Relatively uniform spacing
189
+ pattern_type = ErrorPattern.RANDOM
190
+ diagnosis = "Random errors detected - likely EMI or noise"
191
+ else:
192
+ diagnosis = "Mixed error pattern - multiple causes possible"
193
+ else:
194
+ pattern_type = ErrorPattern.RANDOM
195
+ diagnosis = "Single error - insufficient data for classification"
196
+ else:
197
+ pattern_type = ErrorPattern.RANDOM
198
+ diagnosis = "Few errors - likely random EMI or noise"
199
+
200
+ # Determine severity based on BER
201
+ if bit_error_rate > 0.01:
202
+ severity = "severe"
203
+ diagnosis += ". SEVERE: Check connections and hardware"
204
+ elif bit_error_rate > 0.001:
205
+ severity = "moderate"
206
+ diagnosis += ". MODERATE: Consider reducing sample rate"
207
+ else:
208
+ severity = "low"
209
+ diagnosis += ". Acceptable error rate"
210
+
211
+ return ErrorAnalysis(
212
+ bit_error_rate=bit_error_rate,
213
+ error_count=error_count,
214
+ total_bits=total_bits,
215
+ pattern_type=pattern_type,
216
+ mean_error_gap=mean_gap,
217
+ error_positions=error_positions,
218
+ diagnosis=diagnosis,
219
+ severity=severity,
220
+ )
221
+
222
+
223
+ def generate_error_visualization_data(
224
+ analysis: ErrorAnalysis,
225
+ *,
226
+ histogram_bins: int = 50,
227
+ ) -> dict[str, NDArray[np.float64]]:
228
+ """Generate data for error distribution visualization.
229
+
230
+ Creates histogram and timeline data suitable for plotting error patterns.
231
+
232
+ Args:
233
+ analysis: ErrorAnalysis result from analyze_bit_errors()
234
+ histogram_bins: Number of bins for error position histogram
235
+
236
+ Returns:
237
+ Dictionary with 'histogram_counts', 'histogram_edges', and
238
+ 'timeline' arrays for visualization
239
+
240
+ Examples:
241
+ >>> # Generate visualization data
242
+ >>> analysis = analyze_bit_errors(received, expected)
243
+ >>> viz_data = generate_error_visualization_data(analysis)
244
+ >>> # Plot with matplotlib
245
+ >>> import matplotlib.pyplot as plt
246
+ >>> plt.hist(analysis.error_positions, bins=viz_data['histogram_edges'])
247
+ >>> plt.xlabel('Bit Position')
248
+ >>> plt.ylabel('Error Count')
249
+ >>> plt.show()
250
+
251
+ References:
252
+ DAQ-005: Bit Error Pattern Analysis and Capture Diagnostics
253
+ """
254
+ if len(analysis.error_positions) == 0:
255
+ # No errors - return empty data
256
+ return {
257
+ "histogram_counts": np.array([], dtype=np.float64),
258
+ "histogram_edges": np.array([], dtype=np.float64),
259
+ "timeline": np.array([], dtype=np.float64),
260
+ }
261
+
262
+ # Generate histogram over full bit range
263
+ counts, edges = np.histogram(
264
+ analysis.error_positions, bins=histogram_bins, range=(0, analysis.total_bits), density=False
265
+ )
266
+
267
+ # Timeline: binary array with 1s at error positions
268
+ timeline = np.zeros(analysis.total_bits, dtype=np.float64)
269
+ timeline[analysis.error_positions] = 1.0
270
+
271
+ return {
272
+ "histogram_counts": counts.astype(np.float64),
273
+ "histogram_edges": edges.astype(np.float64),
274
+ "timeline": timeline,
275
+ }