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,513 @@
1
+ """Fuzzy matching for timing and pattern analysis.
2
+
3
+ This module provides fuzzy matching capabilities for tolerating
4
+ timing variations and pattern deviations in real-world signals.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.exploratory.fuzzy import fuzzy_timing_match
9
+ >>> result = fuzzy_timing_match(edges, expected_period=1e-6, tolerance=0.1)
10
+ >>> print(f"Match confidence: {result.confidence:.1%}")
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ import numpy as np
19
+
20
+ from oscura.core.types import WaveformTrace
21
+
22
+ if TYPE_CHECKING:
23
+ from numpy.typing import NDArray
24
+
25
+
26
+ @dataclass
27
+ class FuzzyTimingResult:
28
+ """Result of fuzzy timing match.
29
+
30
+ Attributes:
31
+ match: True if timing matches within tolerance.
32
+ confidence: Match confidence (0.0 to 1.0).
33
+ period: Detected period.
34
+ deviation: Deviation from expected period.
35
+ jitter_rms: RMS timing jitter.
36
+ outlier_count: Number of timing outliers.
37
+ outlier_indices: Indices of outlier edges.
38
+ """
39
+
40
+ match: bool
41
+ confidence: float
42
+ period: float
43
+ deviation: float
44
+ jitter_rms: float
45
+ outlier_count: int
46
+ outlier_indices: list[int]
47
+
48
+
49
+ def fuzzy_timing_match(
50
+ trace_or_edges: WaveformTrace | NDArray[np.float64],
51
+ *,
52
+ expected_period: float | None = None,
53
+ tolerance: float = 0.1,
54
+ sample_rate: float | None = None,
55
+ ) -> FuzzyTimingResult:
56
+ """Match timing with fuzzy tolerance.
57
+
58
+ Allows timing variations while still detecting protocol patterns.
59
+ Useful for signals with jitter or clock drift.
60
+
61
+ Args:
62
+ trace_or_edges: WaveformTrace or array of edge times.
63
+ expected_period: Expected period in seconds.
64
+ tolerance: Tolerance as fraction (0.1 = 10%).
65
+ sample_rate: Sample rate (required if trace provided).
66
+
67
+ Returns:
68
+ FuzzyTimingResult with match information.
69
+
70
+ Raises:
71
+ ValueError: If sample_rate is invalid when WaveformTrace provided.
72
+
73
+ Example:
74
+ >>> result = fuzzy_timing_match(trace, expected_period=1e-6, tolerance=0.1)
75
+ >>> print(f"Period match: {result.match}")
76
+ >>> print(f"Actual period: {result.period:.3e} s")
77
+
78
+ References:
79
+ FUZZY-001: Fuzzy Timing Tolerance
80
+ """
81
+ # Extract edges if WaveformTrace provided
82
+ if isinstance(trace_or_edges, WaveformTrace):
83
+ data = trace_or_edges.data
84
+ sample_rate = sample_rate or trace_or_edges.metadata.sample_rate
85
+
86
+ if sample_rate is None or sample_rate <= 0:
87
+ raise ValueError("Valid sample_rate required for WaveformTrace")
88
+
89
+ # Threshold and find edges
90
+ v_min = np.percentile(data, 5)
91
+ v_max = np.percentile(data, 95)
92
+ threshold = (v_min + v_max) / 2
93
+ digital = data > threshold
94
+
95
+ # Find all transitions (rising and falling edges)
96
+ edge_samples = np.where(np.abs(np.diff(digital.astype(int))) > 0)[0]
97
+ edges = edge_samples / sample_rate
98
+ else:
99
+ edges = trace_or_edges
100
+
101
+ if len(edges) < 2:
102
+ return FuzzyTimingResult(
103
+ match=False,
104
+ confidence=0.0,
105
+ period=0.0,
106
+ deviation=1.0,
107
+ jitter_rms=0.0,
108
+ outlier_count=0,
109
+ outlier_indices=[],
110
+ )
111
+
112
+ # Calculate inter-edge intervals
113
+ intervals = np.diff(edges)
114
+
115
+ # Detect period (use median for robustness)
116
+ detected_period = np.median(intervals)
117
+
118
+ # Use expected period if provided, otherwise use detected
119
+ if expected_period is None:
120
+ expected_period = detected_period
121
+
122
+ # Calculate deviation
123
+ deviation = abs(detected_period - expected_period) / expected_period
124
+
125
+ # Determine match
126
+ match = bool(deviation <= tolerance)
127
+
128
+ # Calculate jitter
129
+ normalized_intervals = intervals / detected_period
130
+ jitter_rms = np.std(normalized_intervals - 1.0) * detected_period
131
+
132
+ # Find outliers
133
+ outlier_threshold = expected_period * tolerance * 3 # 3x tolerance
134
+ deviations = np.abs(intervals - expected_period)
135
+ outlier_mask = deviations > outlier_threshold
136
+ outlier_count = int(np.sum(outlier_mask))
137
+ outlier_indices = list(np.where(outlier_mask)[0])
138
+
139
+ # Calculate confidence
140
+ # Higher confidence for lower deviation and fewer outliers
141
+ confidence = max(0.0, 1.0 - deviation / tolerance)
142
+ confidence *= max(0.0, 1.0 - outlier_count / max(len(intervals), 1))
143
+ confidence = min(1.0, confidence)
144
+
145
+ return FuzzyTimingResult(
146
+ match=match,
147
+ confidence=confidence,
148
+ period=detected_period,
149
+ deviation=deviation,
150
+ jitter_rms=jitter_rms,
151
+ outlier_count=outlier_count,
152
+ outlier_indices=outlier_indices,
153
+ )
154
+
155
+
156
+ @dataclass
157
+ class FuzzyPatternResult:
158
+ """Result of fuzzy pattern match.
159
+
160
+ Attributes:
161
+ matches: List of match locations with scores.
162
+ best_match_score: Score of best match.
163
+ total_matches: Total number of matches found.
164
+ pattern_variations: Common pattern variations found.
165
+ """
166
+
167
+ matches: list[dict[str, Any]]
168
+ best_match_score: float
169
+ total_matches: int
170
+ pattern_variations: list[tuple[tuple[int, ...], int]]
171
+
172
+
173
+ def fuzzy_pattern_match(
174
+ trace: WaveformTrace,
175
+ pattern: list[int] | tuple[int, ...],
176
+ *,
177
+ max_errors: int = 1,
178
+ error_weight: float = 0.5,
179
+ ) -> FuzzyPatternResult:
180
+ """Match pattern with allowed bit errors.
181
+
182
+ Finds pattern occurrences allowing for bit errors, useful for
183
+ noisy signals or partial matches.
184
+
185
+ Args:
186
+ trace: Signal trace to search.
187
+ pattern: Bit pattern to find (list of 0s and 1s).
188
+ max_errors: Maximum allowed bit errors.
189
+ error_weight: Weight reduction per error.
190
+
191
+ Returns:
192
+ FuzzyPatternResult with match locations.
193
+
194
+ Example:
195
+ >>> result = fuzzy_pattern_match(trace, [0, 1, 0, 1, 0, 1], max_errors=1)
196
+ >>> print(f"Found {result.total_matches} matches")
197
+ >>> for match in result.matches[:5]:
198
+ ... print(f" Position {match['position']}: score {match['score']:.2f}")
199
+
200
+ References:
201
+ FUZZY-002: Fuzzy Pattern Matching
202
+ """
203
+ pattern = tuple(pattern)
204
+ pattern_len = len(pattern)
205
+
206
+ # Handle empty pattern
207
+ if pattern_len == 0:
208
+ return FuzzyPatternResult(
209
+ matches=[],
210
+ best_match_score=0.0,
211
+ total_matches=0,
212
+ pattern_variations=[],
213
+ )
214
+
215
+ # Convert trace to digital
216
+ data = trace.data
217
+ v_min = np.percentile(data, 5)
218
+ v_max = np.percentile(data, 95)
219
+ threshold = (v_min + v_max) / 2
220
+ digital = (data > threshold).astype(int)
221
+
222
+ # Find edges and sample at bit centers
223
+ edges = np.where(np.diff(digital) != 0)[0]
224
+
225
+ if len(edges) < 2:
226
+ return FuzzyPatternResult(
227
+ matches=[],
228
+ best_match_score=0.0,
229
+ total_matches=0,
230
+ pattern_variations=[],
231
+ )
232
+
233
+ # Estimate bit period from edges
234
+ # Note: This algorithm works best with signals that have frequent transitions.
235
+ # For patterns with long runs of identical bits, edge density may be insufficient
236
+ # for accurate bit recovery. Best suited for alternating patterns or noisy signals.
237
+ gaps = np.diff(edges)
238
+
239
+ # Use minimum gap as bit period estimate
240
+ # The smallest gap between edges is typically one bit period
241
+ estimated_bit_period = float(np.min(gaps))
242
+
243
+ # Sample bits at regular intervals
244
+ bits_list = []
245
+ sample_pos = edges[0] + estimated_bit_period / 2
246
+
247
+ while sample_pos < len(digital):
248
+ idx = int(sample_pos)
249
+ if idx < len(digital):
250
+ bits_list.append(digital[idx])
251
+ sample_pos += estimated_bit_period
252
+
253
+ bits = np.array(bits_list)
254
+
255
+ # Search for pattern with fuzzy matching
256
+ matches = []
257
+ variations: dict[tuple[int, ...], int] = {}
258
+
259
+ for i in range(len(bits) - pattern_len + 1):
260
+ window = tuple(bits[i : i + pattern_len])
261
+
262
+ # Count errors
263
+ errors = sum(1 for a, b in zip(window, pattern, strict=False) if a != b)
264
+
265
+ if errors <= max_errors:
266
+ score = 1.0 - errors * error_weight
267
+ matches.append(
268
+ {
269
+ "position": i,
270
+ "sample_position": int(edges[0] + i * estimated_bit_period),
271
+ "errors": errors,
272
+ "score": score,
273
+ "actual_pattern": window,
274
+ }
275
+ )
276
+
277
+ # Track variations
278
+ if window != pattern:
279
+ variations[window] = variations.get(window, 0) + 1
280
+
281
+ # Sort matches by score
282
+ matches.sort(key=lambda x: x["score"], reverse=True) # type: ignore[arg-type, return-value]
283
+
284
+ best_score = matches[0]["score"] if matches else 0.0
285
+
286
+ # Sort variations by frequency
287
+ variation_list = sorted(variations.items(), key=lambda x: x[1], reverse=True)
288
+
289
+ return FuzzyPatternResult(
290
+ matches=matches,
291
+ best_match_score=best_score, # type: ignore[arg-type]
292
+ total_matches=len(matches),
293
+ pattern_variations=variation_list[:10],
294
+ )
295
+
296
+
297
+ @dataclass
298
+ class FuzzyProtocolResult:
299
+ """Result of fuzzy protocol detection.
300
+
301
+ Attributes:
302
+ detected_protocol: Most likely protocol.
303
+ confidence: Detection confidence.
304
+ alternatives: Alternative protocol candidates.
305
+ timing_score: Score based on timing match.
306
+ pattern_score: Score based on pattern match.
307
+ recommendations: Suggestions for improving detection.
308
+ """
309
+
310
+ detected_protocol: str
311
+ confidence: float
312
+ alternatives: list[tuple[str, float]]
313
+ timing_score: float
314
+ pattern_score: float
315
+ recommendations: list[str]
316
+
317
+
318
+ # Protocol signatures for fuzzy matching
319
+ PROTOCOL_SIGNATURES = {
320
+ "UART": {
321
+ "start_bit": 0,
322
+ "stop_bits": 1,
323
+ "frame_size": [8, 9, 10, 11], # With start/stop
324
+ "typical_rates": [9600, 19200, 38400, 57600, 115200],
325
+ },
326
+ "I2C": {
327
+ "start_pattern": [1, 0], # SDA falls while SCL high
328
+ "stop_pattern": [0, 1], # SDA rises while SCL high
329
+ "ack_bit": 0,
330
+ "typical_rates": [100e3, 400e3, 1e6, 3.4e6],
331
+ },
332
+ "SPI": {
333
+ "idle_clock": [0, 1], # CPOL options
334
+ "clock_phase": [0, 1], # CPHA options
335
+ "frame_size": [8, 16],
336
+ "typical_rates": [1e6, 5e6, 10e6, 20e6, 40e6],
337
+ },
338
+ "CAN": {
339
+ "start_of_frame": 0,
340
+ "frame_patterns": ["standard", "extended"],
341
+ "typical_rates": [125e3, 250e3, 500e3, 1e6],
342
+ },
343
+ }
344
+
345
+
346
+ def fuzzy_protocol_detect(
347
+ trace: WaveformTrace,
348
+ *,
349
+ candidates: list[str] | None = None,
350
+ timing_tolerance: float = 0.15,
351
+ pattern_tolerance: int = 2,
352
+ ) -> FuzzyProtocolResult:
353
+ """Detect protocol with fuzzy matching.
354
+
355
+ Uses timing tolerance and pattern flexibility to identify
356
+ protocols even with non-ideal signals.
357
+
358
+ Args:
359
+ trace: Signal trace to analyze.
360
+ candidates: List of protocols to consider (None = all).
361
+ timing_tolerance: Timing tolerance as fraction.
362
+ pattern_tolerance: Maximum pattern bit errors.
363
+
364
+ Returns:
365
+ FuzzyProtocolResult with detection results.
366
+
367
+ Example:
368
+ >>> result = fuzzy_protocol_detect(trace)
369
+ >>> print(f"Detected: {result.detected_protocol}")
370
+ >>> print(f"Confidence: {result.confidence:.1%}")
371
+
372
+ References:
373
+ FUZZY-003: Fuzzy Protocol Detection
374
+ """
375
+ data = trace.data
376
+ sample_rate = trace.metadata.sample_rate
377
+
378
+ if candidates is None:
379
+ candidates = list(PROTOCOL_SIGNATURES.keys())
380
+
381
+ # Analyze signal characteristics
382
+ v_min = np.percentile(data, 5)
383
+ v_max = np.percentile(data, 95)
384
+ threshold = (v_min + v_max) / 2
385
+ digital = data > threshold
386
+
387
+ edges = np.where(np.diff(digital.astype(int)) != 0)[0]
388
+
389
+ if len(edges) < 4:
390
+ return FuzzyProtocolResult(
391
+ detected_protocol="Unknown",
392
+ confidence=0.0,
393
+ alternatives=[],
394
+ timing_score=0.0,
395
+ pattern_score=0.0,
396
+ recommendations=["Insufficient edges for protocol detection"],
397
+ )
398
+
399
+ # Estimate bit rate
400
+ intervals = np.diff(edges)
401
+ median_interval = np.median(intervals)
402
+ estimated_bitrate = sample_rate / median_interval
403
+
404
+ # Score each protocol
405
+ scores: dict[str, dict[str, float]] = {}
406
+
407
+ for protocol in candidates:
408
+ if protocol not in PROTOCOL_SIGNATURES:
409
+ continue
410
+
411
+ sig = PROTOCOL_SIGNATURES[protocol]
412
+ timing_score = 0.0
413
+ pattern_score = 0.0
414
+
415
+ # Check timing against typical rates
416
+ if "typical_rates" in sig:
417
+ rates = sig["typical_rates"]
418
+ if hasattr(rates, "__iter__"):
419
+ for rate in rates:
420
+ if isinstance(rate, int | float):
421
+ ratio = estimated_bitrate / rate
422
+ if (1 - timing_tolerance) <= ratio <= (1 + timing_tolerance):
423
+ timing_score = max(timing_score, 1 - abs(1 - ratio) / timing_tolerance)
424
+
425
+ # Check patterns
426
+ if "start_pattern" in sig:
427
+ # Sample first few bits
428
+ bits = []
429
+ pos = edges[0] + median_interval / 2
430
+ for _ in range(4):
431
+ if pos < len(digital):
432
+ bits.append(int(digital[int(pos)]))
433
+ pos += median_interval
434
+
435
+ expected = sig["start_pattern"]
436
+ if len(bits) >= len(expected): # type: ignore[arg-type]
437
+ errors = sum(
438
+ 1 # type: ignore[misc]
439
+ for a, b in zip(bits[: len(expected)], expected, strict=False) # type: ignore[call-overload, arg-type]
440
+ if a != b # type: ignore[misc, call-overload, arg-type]
441
+ )
442
+ if errors <= pattern_tolerance:
443
+ pattern_score = 1 - errors * 0.3
444
+
445
+ # Check frame size
446
+ if "frame_size" in sig:
447
+ # Estimate frame size from inter-frame gaps
448
+ gap_threshold = median_interval * 2
449
+ long_gaps = intervals[intervals > gap_threshold]
450
+ if len(long_gaps) > 0:
451
+ frame_samples = np.median(long_gaps)
452
+ frame_bits = frame_samples / median_interval
453
+ for valid_size in sig["frame_size"]: # type: ignore[attr-defined]
454
+ if abs(frame_bits - valid_size) < 1.5:
455
+ pattern_score = max(pattern_score, 0.7)
456
+
457
+ scores[protocol] = {
458
+ "timing": timing_score,
459
+ "pattern": pattern_score,
460
+ "total": timing_score * 0.5 + pattern_score * 0.5,
461
+ }
462
+
463
+ # Find best match
464
+ if not scores:
465
+ return FuzzyProtocolResult(
466
+ detected_protocol="Unknown",
467
+ confidence=0.0,
468
+ alternatives=[],
469
+ timing_score=0.0,
470
+ pattern_score=0.0,
471
+ recommendations=["No matching protocols found"],
472
+ )
473
+
474
+ sorted_protocols = sorted(scores.items(), key=lambda x: x[1]["total"], reverse=True)
475
+ best_protocol, best_scores = sorted_protocols[0]
476
+
477
+ confidence = best_scores["total"]
478
+ alternatives = [(p, s["total"]) for p, s in sorted_protocols[1:4] if s["total"] > 0.2]
479
+
480
+ # Generate recommendations
481
+ recommendations = []
482
+
483
+ if confidence < 0.5:
484
+ recommendations.append("Low confidence - verify with protocol-specific decoder")
485
+
486
+ if best_scores["timing"] > best_scores["pattern"]:
487
+ recommendations.append("Timing matched better than patterns - check signal quality")
488
+
489
+ if best_scores["pattern"] > best_scores["timing"]:
490
+ recommendations.append("Patterns matched but timing off - check clock accuracy")
491
+
492
+ if not alternatives:
493
+ recommendations.append("No alternative protocols detected")
494
+
495
+ return FuzzyProtocolResult(
496
+ detected_protocol=best_protocol,
497
+ confidence=confidence,
498
+ alternatives=alternatives,
499
+ timing_score=best_scores["timing"],
500
+ pattern_score=best_scores["pattern"],
501
+ recommendations=recommendations,
502
+ )
503
+
504
+
505
+ __all__ = [
506
+ "PROTOCOL_SIGNATURES",
507
+ "FuzzyPatternResult",
508
+ "FuzzyProtocolResult",
509
+ "FuzzyTimingResult",
510
+ "fuzzy_pattern_match",
511
+ "fuzzy_protocol_detect",
512
+ "fuzzy_timing_match",
513
+ ]