oscura 0.5.1__py3-none-any.whl → 0.7.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 (497) 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/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.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,