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
@@ -83,7 +83,23 @@ def detect_protocol(
83
83
  characteristics = _analyze_signal_characteristics(trace)
84
84
 
85
85
  # Define protocol detectors
86
- protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]] = [
86
+ protocol_detectors = _build_protocol_detectors(characteristics)
87
+
88
+ # Score protocols
89
+ candidates = _score_protocols(protocol_detectors, characteristics, parallel)
90
+
91
+ # Validate and select primary
92
+ primary = _validate_detection(candidates, min_confidence)
93
+
94
+ # Build result
95
+ return _build_detection_result(primary, characteristics, candidates, return_candidates)
96
+
97
+
98
+ def _build_protocol_detectors(
99
+ characteristics: dict[str, Any],
100
+ ) -> list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]]:
101
+ """Build list of protocol detectors with configurations."""
102
+ return [
87
103
  (
88
104
  "UART",
89
105
  _score_uart,
@@ -121,65 +137,100 @@ def detect_protocol(
121
137
  ),
122
138
  ]
123
139
 
124
- # Score protocols (optionally in parallel)
125
- candidates = []
126
140
 
141
+ def _score_protocols(
142
+ protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]],
143
+ characteristics: dict[str, Any],
144
+ parallel: bool,
145
+ ) -> list[dict[str, Any]]:
146
+ """Score all protocol detectors."""
127
147
  if parallel:
128
- # Run scoring in parallel using ThreadPoolExecutor
129
- with ThreadPoolExecutor(max_workers=len(protocol_detectors)) as executor:
130
- # Submit all scoring tasks
131
- future_to_protocol = {
132
- executor.submit(scorer, characteristics): (name, config)
133
- for name, scorer, config in protocol_detectors
134
- }
135
-
136
- # Collect results
137
- for future in as_completed(future_to_protocol):
138
- name, config = future_to_protocol[future]
139
- try:
140
- score = future.result()
141
- if score > 0:
142
- candidates.append(
143
- {
144
- "protocol": name,
145
- "confidence": score,
146
- "config": config,
147
- }
148
- )
149
- except Exception:
150
- # If a scorer fails, skip that protocol
151
- pass
152
- else:
153
- # Sequential scoring (original behavior)
154
- for name, scorer, config in protocol_detectors:
155
- score = scorer(characteristics)
156
- if score > 0:
157
- candidates.append(
158
- {
159
- "protocol": name,
160
- "confidence": score,
161
- "config": config,
162
- }
163
- )
164
-
165
- # Sort by confidence
148
+ return _score_protocols_parallel(protocol_detectors, characteristics)
149
+ return _score_protocols_sequential(protocol_detectors, characteristics)
150
+
151
+
152
+ def _score_protocols_parallel(
153
+ protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]],
154
+ characteristics: dict[str, Any],
155
+ ) -> list[dict[str, Any]]:
156
+ """Score protocols in parallel using ThreadPoolExecutor."""
157
+ candidates = []
158
+
159
+ with ThreadPoolExecutor(max_workers=len(protocol_detectors)) as executor:
160
+ future_to_protocol = {
161
+ executor.submit(scorer, characteristics): (name, config)
162
+ for name, scorer, config in protocol_detectors
163
+ }
164
+
165
+ for future in as_completed(future_to_protocol):
166
+ name, config = future_to_protocol[future]
167
+ try:
168
+ score = future.result()
169
+ if score > 0:
170
+ candidates.append(
171
+ {
172
+ "protocol": name,
173
+ "confidence": score,
174
+ "config": config,
175
+ }
176
+ )
177
+ except Exception:
178
+ pass
179
+
180
+ candidates.sort(key=lambda x: x["confidence"], reverse=True) # type: ignore[arg-type, return-value]
181
+ return candidates
182
+
183
+
184
+ def _score_protocols_sequential(
185
+ protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]],
186
+ characteristics: dict[str, Any],
187
+ ) -> list[dict[str, Any]]:
188
+ """Score protocols sequentially."""
189
+ candidates = []
190
+
191
+ for name, scorer, config in protocol_detectors:
192
+ score = scorer(characteristics)
193
+ if score > 0:
194
+ candidates.append(
195
+ {
196
+ "protocol": name,
197
+ "confidence": score,
198
+ "config": config,
199
+ }
200
+ )
201
+
166
202
  candidates.sort(key=lambda x: x["confidence"], reverse=True) # type: ignore[arg-type, return-value]
203
+ return candidates
167
204
 
205
+
206
+ def _validate_detection(
207
+ candidates: list[dict[str, Any]],
208
+ min_confidence: float,
209
+ ) -> dict[str, Any]:
210
+ """Validate that detection meets confidence threshold."""
168
211
  if not candidates:
169
212
  raise AnalysisError(
170
213
  "Could not detect protocol type. Signal may be analog or unsupported protocol."
171
214
  )
172
215
 
173
- # Primary detection
174
216
  primary = candidates[0]
175
217
 
176
- if primary["confidence"] < min_confidence: # type: ignore[operator]
218
+ if float(primary["confidence"]) < min_confidence:
177
219
  raise AnalysisError(
178
220
  f"Protocol detection confidence too low: {primary['confidence']:.1%} "
179
221
  f"(minimum: {min_confidence:.1%}). Try specifying protocol manually."
180
222
  )
181
223
 
182
- # Build result
224
+ return primary
225
+
226
+
227
+ def _build_detection_result(
228
+ primary: dict[str, Any],
229
+ characteristics: dict[str, Any],
230
+ candidates: list[dict[str, Any]],
231
+ return_candidates: bool,
232
+ ) -> dict[str, Any]:
233
+ """Build detection result dictionary."""
183
234
  result = {
184
235
  "protocol": primary["protocol"],
185
236
  "confidence": primary["confidence"],
@@ -512,6 +512,79 @@ class ProtocolDecoder:
512
512
  return None
513
513
  return idx
514
514
 
515
+ def _decode_bytes_field(
516
+ self, data: bytes, field: FieldDefinition, context: dict[str, Any]
517
+ ) -> tuple[bytes, int]:
518
+ """Decode bytes field.
519
+
520
+ Args:
521
+ data: Binary data
522
+ field: Field definition
523
+ context: Previously decoded fields
524
+
525
+ Returns:
526
+ Tuple of (bytes value, bytes consumed)
527
+ """
528
+ size = self._resolve_size(field.size, context, data)
529
+ if size > len(data):
530
+ size = len(data) # Use remaining data
531
+ return bytes(data[:size]), size
532
+
533
+ def _decode_string_field(
534
+ self, data: bytes, field: FieldDefinition, context: dict[str, Any]
535
+ ) -> tuple[str, int]:
536
+ """Decode string field.
537
+
538
+ Args:
539
+ data: Binary data
540
+ field: Field definition
541
+ context: Previously decoded fields
542
+
543
+ Returns:
544
+ Tuple of (string value, bytes consumed)
545
+ """
546
+ size = self._resolve_size(field.size, context, data)
547
+ if size > len(data):
548
+ size = len(data) # Use remaining data
549
+ string_bytes = data[:size]
550
+
551
+ # Try to decode as UTF-8, fall back to latin-1
552
+ try:
553
+ value = string_bytes.decode("utf-8").rstrip("\x00")
554
+ except UnicodeDecodeError:
555
+ value = string_bytes.decode("latin-1").rstrip("\x00")
556
+
557
+ return value, size
558
+
559
+ def _decode_bitfield_field(
560
+ self, data: bytes, field: FieldDefinition, endian: str
561
+ ) -> tuple[int, int]:
562
+ """Decode bitfield field.
563
+
564
+ Args:
565
+ data: Binary data
566
+ field: Field definition
567
+ endian: Endianness marker
568
+
569
+ Returns:
570
+ Tuple of (bitfield value, bytes consumed)
571
+
572
+ Raises:
573
+ ValueError: If bitfield size is unsupported
574
+ """
575
+ field_size = field.size if isinstance(field.size, int) else 1
576
+
577
+ if field_size == 1:
578
+ bitfield_value = int(data[0])
579
+ elif field_size == 2:
580
+ bitfield_value = struct.unpack(f"{endian}H", data[:2])[0]
581
+ elif field_size == 4:
582
+ bitfield_value = struct.unpack(f"{endian}I", data[:4])[0]
583
+ else:
584
+ raise ValueError(f"Unsupported bitfield size: {field_size}")
585
+
586
+ return bitfield_value, field_size
587
+
515
588
  def _decode_field(
516
589
  self, data: bytes, field: FieldDefinition, context: dict[str, Any]
517
590
  ) -> tuple[Any, int]:
@@ -538,55 +611,30 @@ class ProtocolDecoder:
538
611
  return self._decode_integer(data, field_type, endian)
539
612
 
540
613
  # Float types
541
- elif field_type in ["float32", "float64"]:
614
+ if field_type in ["float32", "float64"]:
542
615
  return self._decode_float(data, field_type, endian)
543
616
 
544
617
  # Bytes
545
- elif field_type == "bytes":
546
- size = self._resolve_size(field.size, context, data)
547
- if size > len(data):
548
- size = len(data) # Use remaining data
549
- return bytes(data[:size]), size
618
+ if field_type == "bytes":
619
+ return self._decode_bytes_field(data, field, context)
550
620
 
551
621
  # String
552
- elif field_type == "string":
553
- size = self._resolve_size(field.size, context, data)
554
- if size > len(data):
555
- size = len(data) # Use remaining data
556
- string_bytes = data[:size]
557
- # Try to decode as UTF-8, fall back to latin-1
558
- try:
559
- value = string_bytes.decode("utf-8").rstrip("\x00")
560
- except UnicodeDecodeError:
561
- value = string_bytes.decode("latin-1").rstrip("\x00")
562
- return value, size
622
+ if field_type == "string":
623
+ return self._decode_string_field(data, field, context)
563
624
 
564
625
  # Bitfield
565
- elif field_type == "bitfield":
566
- # Decode as uint and extract bits
567
- field_size = field.size if isinstance(field.size, int) else 1
568
- if field_size == 1:
569
- bitfield_value = int(data[0])
570
- elif field_size == 2:
571
- bitfield_value = struct.unpack(f"{endian}H", data[:2])[0]
572
- elif field_size == 4:
573
- bitfield_value = struct.unpack(f"{endian}I", data[:4])[0]
574
- else:
575
- raise ValueError(f"Unsupported bitfield size: {field_size}")
576
-
577
- # Return as-is, caller can extract specific bits
578
- return bitfield_value, field_size
626
+ if field_type == "bitfield":
627
+ return self._decode_bitfield_field(data, field, endian)
579
628
 
580
629
  # Array
581
- elif field_type == "array":
630
+ if field_type == "array":
582
631
  return self._decode_array(data, field, context)
583
632
 
584
633
  # Struct (nested)
585
- elif field_type == "struct":
634
+ if field_type == "struct":
586
635
  return self._decode_struct(data, field, context)
587
636
 
588
- else:
589
- raise ValueError(f"Unknown field type: {field_type}")
637
+ raise ValueError(f"Unknown field type: {field_type}")
590
638
 
591
639
  def _decode_array(
592
640
  self, data: bytes, field: FieldDefinition, context: dict[str, Any]
@@ -910,71 +958,121 @@ class ProtocolEncoder:
910
958
  def _encode_field(self, value: Any, field: FieldDefinition) -> bytes:
911
959
  """Encode single field value.
912
960
 
913
- : Field encoding.
914
-
915
961
  Args:
916
- value: Field value
917
- field: Field definition
962
+ value: Field value.
963
+ field: Field definition.
918
964
 
919
965
  Returns:
920
- Encoded bytes
966
+ Encoded bytes.
921
967
 
922
968
  Raises:
923
- ValueError: If bytes value is invalid or field type is unknown for encoding
969
+ ValueError: If bytes value is invalid or field type is unknown for encoding.
970
+
971
+ Example:
972
+ >>> encoder._encode_field(42, FieldDefinition("counter", "uint16", "big"))
973
+ b'\\x00*'
974
+ """
975
+ field_type = field.field_type
976
+
977
+ # Dispatch to type-specific encoders
978
+ if field_type in {"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64"}:
979
+ return self._encode_integer_field(value, field)
980
+ elif field_type in {"float32", "float64"}:
981
+ return self._encode_float_field(value, field)
982
+ elif field_type == "bytes":
983
+ return self._encode_bytes_field(value)
984
+ elif field_type == "string":
985
+ return self._encode_string_field(value)
986
+ elif field_type == "array":
987
+ return self._encode_array(value, field)
988
+ elif field_type == "struct":
989
+ return self._encode_struct(value, field)
990
+ else:
991
+ raise ValueError(f"Unknown field type for encoding: {field_type}")
992
+
993
+ def _encode_integer_field(self, value: Any, field: FieldDefinition) -> bytes:
994
+ """Encode integer field types.
995
+
996
+ Args:
997
+ value: Integer value.
998
+ field: Field definition with type and endianness.
999
+
1000
+ Returns:
1001
+ Packed integer bytes.
924
1002
  """
925
1003
  endian = self._endian_map.get(field.endian, ">")
926
1004
  field_type = field.field_type
927
1005
 
928
- # Integer types
929
- if field_type == "uint8":
930
- return struct.pack("B", int(value))
931
- elif field_type == "int8":
932
- return struct.pack("b", int(value))
933
- elif field_type == "uint16":
934
- return struct.pack(f"{endian}H", int(value))
935
- elif field_type == "int16":
936
- return struct.pack(f"{endian}h", int(value))
937
- elif field_type == "uint32":
938
- return struct.pack(f"{endian}I", int(value))
939
- elif field_type == "int32":
940
- return struct.pack(f"{endian}i", int(value))
941
- elif field_type == "uint64":
942
- return struct.pack(f"{endian}Q", int(value))
943
- elif field_type == "int64":
944
- return struct.pack(f"{endian}q", int(value))
1006
+ # Map field types to struct format characters
1007
+ _INT_FORMATS = {
1008
+ "uint8": "B",
1009
+ "int8": "b",
1010
+ "uint16": "H",
1011
+ "int16": "h",
1012
+ "uint32": "I",
1013
+ "int32": "i",
1014
+ "uint64": "Q",
1015
+ "int64": "q",
1016
+ }
945
1017
 
946
- # Float types
947
- elif field_type == "float32":
1018
+ fmt_char = _INT_FORMATS[field_type]
1019
+ if fmt_char in {"B", "b"}:
1020
+ # uint8/int8 have no endianness
1021
+ return struct.pack(fmt_char, int(value))
1022
+ else:
1023
+ return struct.pack(f"{endian}{fmt_char}", int(value))
1024
+
1025
+ def _encode_float_field(self, value: Any, field: FieldDefinition) -> bytes:
1026
+ """Encode floating-point field types.
1027
+
1028
+ Args:
1029
+ value: Float value.
1030
+ field: Field definition with type and endianness.
1031
+
1032
+ Returns:
1033
+ Packed float bytes.
1034
+ """
1035
+ endian = self._endian_map.get(field.endian, ">")
1036
+
1037
+ if field.field_type == "float32":
948
1038
  return struct.pack(f"{endian}f", float(value))
949
- elif field_type == "float64":
1039
+ elif field.field_type == "float64":
950
1040
  return struct.pack(f"{endian}d", float(value))
1041
+ else:
1042
+ raise ValueError(f"Unknown float type: {field.field_type}")
951
1043
 
952
- # Bytes
953
- elif field_type == "bytes":
954
- if isinstance(value, bytes):
955
- return value
956
- elif isinstance(value, list | tuple):
957
- return bytes(value)
958
- else:
959
- raise ValueError(f"Invalid bytes value: {value}")
1044
+ def _encode_bytes_field(self, value: Any) -> bytes:
1045
+ """Encode bytes field.
960
1046
 
961
- # String
962
- elif field_type == "string":
963
- if isinstance(value, str):
964
- return value.encode("utf-8")
965
- else:
966
- return bytes(value)
1047
+ Args:
1048
+ value: Bytes, list, or tuple of byte values.
967
1049
 
968
- # Array
969
- elif field_type == "array":
970
- return self._encode_array(value, field)
1050
+ Returns:
1051
+ Byte sequence.
971
1052
 
972
- # Struct
973
- elif field_type == "struct":
974
- return self._encode_struct(value, field)
1053
+ Raises:
1054
+ ValueError: If value cannot be converted to bytes.
1055
+ """
1056
+ if isinstance(value, bytes):
1057
+ return value
1058
+ elif isinstance(value, list | tuple):
1059
+ return bytes(value)
1060
+ else:
1061
+ raise ValueError(f"Invalid bytes value: {value}")
975
1062
 
1063
+ def _encode_string_field(self, value: Any) -> bytes:
1064
+ """Encode string field.
1065
+
1066
+ Args:
1067
+ value: String or bytes.
1068
+
1069
+ Returns:
1070
+ UTF-8 encoded bytes.
1071
+ """
1072
+ if isinstance(value, str):
1073
+ return value.encode("utf-8")
976
1074
  else:
977
- raise ValueError(f"Unknown field type for encoding: {field_type}")
1075
+ return bytes(value)
978
1076
 
979
1077
  def _encode_array(self, value: list[Any], field: FieldDefinition) -> bytes:
980
1078
  """Encode array field.