oscura 0.5.0__py3-none-any.whl → 0.6.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 (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -18,7 +18,7 @@ from __future__ import annotations
18
18
 
19
19
  from dataclasses import dataclass
20
20
  from enum import Enum
21
- from typing import TYPE_CHECKING
21
+ from typing import TYPE_CHECKING, Any
22
22
 
23
23
  import numpy as np
24
24
 
@@ -84,14 +84,14 @@ class I2CDecoder(SyncDecoder):
84
84
  longname = "Inter-Integrated Circuit"
85
85
  desc = "I2C bus protocol decoder"
86
86
 
87
- channels = [ # noqa: RUF012
87
+ channels = [
88
88
  ChannelDef("scl", "SCL", "Clock line", required=True),
89
89
  ChannelDef("sda", "SDA", "Data line", required=True),
90
90
  ]
91
91
 
92
- optional_channels = [] # noqa: RUF012
92
+ optional_channels = []
93
93
 
94
- options = [ # noqa: RUF012
94
+ options = [
95
95
  OptionDef(
96
96
  "address_format",
97
97
  "Address format",
@@ -101,7 +101,7 @@ class I2CDecoder(SyncDecoder):
101
101
  ),
102
102
  ]
103
103
 
104
- annotations = [ # noqa: RUF012
104
+ annotations = [
105
105
  ("start", "Start condition"),
106
106
  ("stop", "Stop condition"),
107
107
  ("address", "Device address"),
@@ -150,15 +150,47 @@ class I2CDecoder(SyncDecoder):
150
150
  if scl is None or sda is None:
151
151
  return
152
152
 
153
+ scl, sda = self._align_signals(scl, sda)
154
+ conditions = self._find_start_stop_conditions(scl, sda)
155
+
156
+ if len(conditions) == 0:
157
+ return
158
+
159
+ # Process each transaction
160
+ yield from self._process_transactions(scl, sda, conditions, sample_rate)
161
+
162
+ def _align_signals(
163
+ self, scl: NDArray[np.bool_], sda: NDArray[np.bool_]
164
+ ) -> tuple[NDArray[np.bool_], NDArray[np.bool_]]:
165
+ """Align SCL and SDA signals to same length.
166
+
167
+ Args:
168
+ scl: Clock signal.
169
+ sda: Data signal.
170
+
171
+ Returns:
172
+ Tuple of (aligned_scl, aligned_sda).
173
+ """
153
174
  n_samples = min(len(scl), len(sda))
154
- scl = scl[:n_samples]
155
- sda = sda[:n_samples]
175
+ return scl[:n_samples], sda[:n_samples]
176
+
177
+ def _find_start_stop_conditions(
178
+ self, scl: NDArray[np.bool_], sda: NDArray[np.bool_]
179
+ ) -> list[tuple[int, I2CCondition]]:
180
+ """Find START and STOP conditions in I2C signals.
181
+
182
+ START: SDA falls while SCL is high.
183
+ STOP: SDA rises while SCL is high.
156
184
 
157
- # Find start and stop conditions
158
- # START: SDA falls while SCL is high
159
- # STOP: SDA rises while SCL is high
185
+ Args:
186
+ scl: Clock signal.
187
+ sda: Data signal.
160
188
 
189
+ Returns:
190
+ List of (sample_index, condition_type) tuples.
191
+ """
161
192
  conditions = []
193
+ n_samples = len(scl)
162
194
 
163
195
  for i in range(1, n_samples):
164
196
  if scl[i] and scl[i - 1]: # SCL is high
@@ -167,10 +199,26 @@ class I2CDecoder(SyncDecoder):
167
199
  elif not sda[i - 1] and sda[i]: # SDA rising
168
200
  conditions.append((i, I2CCondition.STOP))
169
201
 
170
- if len(conditions) == 0:
171
- return
202
+ return conditions
172
203
 
173
- # Process transactions between START and STOP
204
+ def _process_transactions(
205
+ self,
206
+ scl: NDArray[np.bool_],
207
+ sda: NDArray[np.bool_],
208
+ conditions: list[tuple[int, I2CCondition]],
209
+ sample_rate: float,
210
+ ) -> Iterator[ProtocolPacket]:
211
+ """Process I2C transactions between START and STOP conditions.
212
+
213
+ Args:
214
+ scl: Clock signal.
215
+ sda: Data signal.
216
+ conditions: List of START/STOP conditions.
217
+ sample_rate: Sample rate in Hz.
218
+
219
+ Yields:
220
+ Decoded I2C transactions as ProtocolPacket objects.
221
+ """
174
222
  trans_idx = 0
175
223
  i = 0
176
224
 
@@ -179,117 +227,210 @@ class I2CDecoder(SyncDecoder):
179
227
  i += 1
180
228
  continue
181
229
 
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
230
+ # Find transaction boundaries
231
+ start_idx, end_idx, is_repeated, end_cond_idx = self._find_transaction_bounds(
232
+ conditions, i
233
+ )
194
234
 
195
- if end_cond_idx >= len(conditions):
235
+ if end_cond_idx is None:
196
236
  break
197
237
 
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(
238
+ # Extract and decode transaction
239
+ packet = self._decode_transaction(
203
240
  scl[start_idx:end_idx],
204
241
  sda[start_idx:end_idx],
242
+ start_idx,
243
+ end_idx,
244
+ sample_rate,
245
+ is_repeated,
246
+ trans_idx,
205
247
  )
206
248
 
207
- if len(bytes_data) == 0:
208
- i = end_cond_idx
209
- continue
249
+ if packet:
250
+ yield packet
251
+ trans_idx += 1
210
252
 
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
253
+ i = end_cond_idx if is_repeated else end_cond_idx + 1
215
254
 
216
- # Check for 10-bit address
217
- is_10bit = False
218
- actual_address = address
255
+ def _find_transaction_bounds(
256
+ self, conditions: list[tuple[int, I2CCondition]], start_cond_idx: int
257
+ ) -> tuple[int, int, bool, int | None]:
258
+ """Find start and end indices of an I2C transaction.
219
259
 
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
- )
260
+ Args:
261
+ conditions: List of START/STOP conditions.
262
+ start_cond_idx: Index of START condition.
257
263
 
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
- )
264
+ Returns:
265
+ Tuple of (start_idx, end_idx, is_repeated, end_condition_idx).
266
+ """
267
+ start_idx = conditions[start_cond_idx][0]
268
+
269
+ # Find corresponding STOP or next START
270
+ end_cond_idx = start_cond_idx + 1
271
+ while end_cond_idx < len(conditions):
272
+ if conditions[end_cond_idx][1] in (I2CCondition.STOP, I2CCondition.START):
273
+ break
274
+ end_cond_idx += 1
275
+
276
+ if end_cond_idx >= len(conditions):
277
+ return start_idx, start_idx, False, None
278
+
279
+ end_idx = conditions[end_cond_idx][0]
280
+ is_repeated = conditions[end_cond_idx][1] == I2CCondition.START
281
+
282
+ return start_idx, end_idx, is_repeated, end_cond_idx
283
+
284
+ def _decode_transaction(
285
+ self,
286
+ scl: NDArray[np.bool_],
287
+ sda: NDArray[np.bool_],
288
+ start_idx: int,
289
+ end_idx: int,
290
+ sample_rate: float,
291
+ is_repeated: bool,
292
+ trans_idx: int,
293
+ ) -> ProtocolPacket | None:
294
+ """Decode a single I2C transaction.
295
+
296
+ Args:
297
+ scl: Clock signal for transaction.
298
+ sda: Data signal for transaction.
299
+ start_idx: Absolute start sample index.
300
+ end_idx: Absolute end sample index.
301
+ sample_rate: Sample rate in Hz.
302
+ is_repeated: Whether this is a repeated START.
303
+ trans_idx: Transaction index.
265
304
 
266
- # Create packet
267
- annotations = {
268
- "address": actual_address,
269
- "address_10bit": is_10bit,
270
- "read": is_read,
305
+ Returns:
306
+ Decoded ProtocolPacket or None if invalid.
307
+ """
308
+ bytes_data, acks = self._extract_bytes(scl, sda)
309
+
310
+ if len(bytes_data) == 0:
311
+ return None
312
+
313
+ # Parse address and direction
314
+ address_info = self._parse_address(bytes_data, acks)
315
+
316
+ # Create annotations
317
+ start_time = start_idx / sample_rate
318
+ end_time = end_idx / sample_rate
319
+ self._add_transaction_annotations(start_time, end_time, address_info, is_repeated)
320
+
321
+ # Build packet
322
+ errors = self._check_transaction_errors(
323
+ acks, address_info["data_acks"], address_info["is_read"]
324
+ )
325
+
326
+ packet = ProtocolPacket(
327
+ timestamp=start_time,
328
+ protocol="i2c",
329
+ data=bytes(address_info["data_bytes"]),
330
+ annotations={
331
+ "address": address_info["address"],
332
+ "address_10bit": address_info["is_10bit"],
333
+ "read": address_info["is_read"],
271
334
  "bytes": bytes_data,
272
335
  "acks": acks,
273
336
  "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
- )
337
+ },
338
+ errors=errors,
339
+ )
283
340
 
284
- yield packet
341
+ return packet
285
342
 
286
- trans_idx += 1
287
- i = end_cond_idx
343
+ def _parse_address(self, bytes_data: list[int], acks: list[bool]) -> dict[str, Any]:
344
+ """Parse I2C address from first bytes.
288
345
 
289
- if is_repeated:
290
- continue
291
- else:
292
- i += 1
346
+ Args:
347
+ bytes_data: List of decoded bytes.
348
+ acks: List of ACK/NAK bits.
349
+
350
+ Returns:
351
+ Dictionary with address, is_10bit, is_read, data_bytes, data_acks.
352
+ """
353
+ address_byte = bytes_data[0]
354
+ address = address_byte >> 1
355
+ is_read = (address_byte & 1) == 1
356
+
357
+ # Check for 10-bit address
358
+ is_10bit = self._address_format == "10bit" or (
359
+ self._address_format == "auto" and (address_byte >> 3) == 0b11110
360
+ )
361
+
362
+ if is_10bit and len(bytes_data) >= 2:
363
+ high_bits = (address_byte >> 1) & 0b11
364
+ low_bits = bytes_data[1]
365
+ actual_address = (high_bits << 8) | low_bits
366
+ data_bytes = bytes_data[2:]
367
+ data_acks = acks[2:] if len(acks) > 2 else []
368
+ else:
369
+ actual_address = address
370
+ data_bytes = bytes_data[1:]
371
+ data_acks = acks[1:] if len(acks) > 1 else []
372
+
373
+ return {
374
+ "address": actual_address,
375
+ "is_10bit": is_10bit,
376
+ "is_read": is_read,
377
+ "data_bytes": data_bytes,
378
+ "data_acks": data_acks,
379
+ }
380
+
381
+ def _check_transaction_errors(
382
+ self, acks: list[bool], data_acks: list[bool], is_read: bool
383
+ ) -> list[str]:
384
+ """Check for NAK errors in transaction.
385
+
386
+ Args:
387
+ acks: All ACK/NAK bits.
388
+ data_acks: Data byte ACK/NAK bits.
389
+ is_read: Whether this is a read transaction.
390
+
391
+ Returns:
392
+ List of error messages.
393
+ """
394
+ errors = []
395
+
396
+ if len(acks) > 0 and not acks[0]:
397
+ errors.append("NAK on address")
398
+
399
+ for j, ack in enumerate(data_acks):
400
+ if not ack and not is_read:
401
+ errors.append(f"NAK on byte {j}")
402
+
403
+ return errors
404
+
405
+ def _add_transaction_annotations(
406
+ self, start_time: float, end_time: float, address_info: dict[str, Any], is_repeated: bool
407
+ ) -> None:
408
+ """Add annotations for I2C transaction.
409
+
410
+ Args:
411
+ start_time: Transaction start time.
412
+ end_time: Transaction end time.
413
+ address_info: Parsed address information.
414
+ is_repeated: Whether this is a repeated START.
415
+ """
416
+ self.put_annotation(
417
+ start_time,
418
+ start_time + 1e-6,
419
+ AnnotationLevel.BITS,
420
+ "START" if not is_repeated else "Sr",
421
+ )
422
+
423
+ addr_text = (
424
+ f"0x{address_info['address']:02X}"
425
+ if not address_info["is_10bit"]
426
+ else f"0x{address_info['address']:03X}"
427
+ )
428
+ self.put_annotation(
429
+ start_time,
430
+ end_time,
431
+ AnnotationLevel.FIELDS,
432
+ f"{addr_text} {'R' if address_info['is_read'] else 'W'}",
433
+ )
293
434
 
294
435
  def _extract_bytes(
295
436
  self,