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
@@ -61,15 +61,15 @@ class UARTDecoder(AsyncDecoder):
61
61
  longname = "Universal Asynchronous Receiver/Transmitter"
62
62
  desc = "UART/RS-232 serial protocol decoder"
63
63
 
64
- channels = [ # noqa: RUF012
64
+ channels = [
65
65
  ChannelDef("rx", "RX", "Receive data line", required=True),
66
66
  ]
67
67
 
68
- optional_channels = [ # noqa: RUF012
68
+ optional_channels = [
69
69
  ChannelDef("tx", "TX", "Transmit data line", required=False),
70
70
  ]
71
71
 
72
- options = [ # noqa: RUF012
72
+ options = [
73
73
  OptionDef("baudrate", "Baud rate", "Bits per second", default=0, values=None),
74
74
  OptionDef(
75
75
  "data_bits",
@@ -102,7 +102,7 @@ class UARTDecoder(AsyncDecoder):
102
102
  OptionDef("idle_level", "Idle level", "Idle line level", default=1, values=[0, 1]),
103
103
  ]
104
104
 
105
- annotations = [ # noqa: RUF012
105
+ annotations = [
106
106
  ("bit", "Bit value"),
107
107
  ("start", "Start bit"),
108
108
  ("data", "Data bits"),
@@ -164,155 +164,297 @@ class UARTDecoder(AsyncDecoder):
164
164
  >>> for packet in decoder.decode(trace):
165
165
  ... print(f"Byte: {packet.data.hex()}")
166
166
  """
167
- # Convert to digital if needed
167
+ digital_trace = self._convert_to_digital(trace)
168
+ data = digital_trace.data
169
+ sample_rate = digital_trace.metadata.sample_rate
170
+
171
+ self._auto_detect_baudrate(digital_trace)
172
+ bit_period = sample_rate / self._baudrate
173
+ frame_bits = self._calculate_frame_bits()
174
+
175
+ idx = 0
176
+ frame_num = 0
177
+
178
+ while idx < len(data) - int(frame_bits * bit_period):
179
+ start_idx = self._find_start_bit(data, idx)
180
+ if start_idx is None:
181
+ break
182
+
183
+ sample_points = self._get_sample_points(start_idx, bit_period, frame_bits, len(data))
184
+ if len(sample_points) < 1 + self._data_bits:
185
+ break
186
+
187
+ if not self._verify_start_bit(data, sample_points):
188
+ idx = start_idx + 1
189
+ continue
190
+
191
+ data_value, data_bits = self._extract_data_bits(data, sample_points)
192
+ errors = self._validate_frame(data, sample_points, data_bits)
193
+
194
+ packet = self._build_packet(
195
+ start_idx,
196
+ sample_rate,
197
+ bit_period,
198
+ frame_bits,
199
+ data_value,
200
+ data_bits,
201
+ frame_num,
202
+ errors,
203
+ )
204
+
205
+ yield packet
206
+
207
+ frame_num += 1
208
+ last_sample = sample_points[-1] if sample_points else start_idx
209
+ idx = last_sample + 1
210
+
211
+ def _convert_to_digital(self, trace: DigitalTrace | WaveformTrace) -> DigitalTrace:
212
+ """Convert trace to digital format if needed.
213
+
214
+ Args:
215
+ trace: Input trace.
216
+
217
+ Returns:
218
+ Digital trace.
219
+ """
168
220
  if isinstance(trace, WaveformTrace):
169
221
  from oscura.analyzers.digital.extraction import to_digital
170
222
 
171
- digital_trace = to_digital(trace, threshold="auto")
172
- else:
173
- digital_trace = trace
223
+ return to_digital(trace, threshold="auto")
224
+ return trace
174
225
 
175
- data = digital_trace.data
176
- sample_rate = digital_trace.metadata.sample_rate
226
+ def _auto_detect_baudrate(self, trace: DigitalTrace) -> None:
227
+ """Auto-detect baud rate if not specified.
177
228
 
178
- # Auto-detect baud rate if not specified
229
+ Args:
230
+ trace: Digital trace for detection.
231
+ """
179
232
  if self._baudrate == 0:
180
233
  from oscura.utils.autodetect import detect_baud_rate
181
234
 
182
- self._baudrate = detect_baud_rate(digital_trace) # type: ignore[assignment]
235
+ self._baudrate = detect_baud_rate(trace) # type: ignore[assignment]
183
236
  if self._baudrate == 0:
184
237
  self._baudrate = 9600 # Fallback
185
238
 
186
- bit_period = sample_rate / self._baudrate
187
- half_bit = bit_period / 2
239
+ def _calculate_frame_bits(self) -> float:
240
+ """Calculate total bits per frame.
188
241
 
189
- # Frame structure
242
+ Returns:
243
+ Number of bits in frame.
244
+ """
190
245
  frame_bits = 1 + self._data_bits # Start + data
191
246
  if self._parity != "none":
192
247
  frame_bits += 1
193
248
  frame_bits += self._stop_bits # type: ignore[assignment]
249
+ return frame_bits
194
250
 
195
- idx = 0
196
- frame_num = 0
251
+ def _get_sample_points(
252
+ self, start_idx: int, bit_period: float, frame_bits: float, data_len: int
253
+ ) -> list[int]:
254
+ """Get sample points for each bit in frame.
197
255
 
198
- while idx < len(data) - int(frame_bits * bit_period):
199
- # Look for start bit (transition from idle)
200
- start_idx = self._find_start_bit(data, idx)
201
- if start_idx is None:
202
- break
256
+ Args:
257
+ start_idx: Frame start index.
258
+ bit_period: Samples per bit.
259
+ frame_bits: Total bits in frame.
260
+ data_len: Length of data array.
203
261
 
204
- # Sample at center of each bit
205
- sample_points = []
206
- for bit_num in range(int(frame_bits)):
207
- sample_idx = int(start_idx + half_bit + bit_num * bit_period)
208
- if sample_idx < len(data):
209
- sample_points.append(sample_idx)
262
+ Returns:
263
+ List of sample indices.
264
+ """
265
+ half_bit = bit_period / 2
266
+ sample_points = []
267
+ for bit_num in range(int(frame_bits)):
268
+ sample_idx = int(start_idx + half_bit + bit_num * bit_period)
269
+ if sample_idx < data_len:
270
+ sample_points.append(sample_idx)
271
+ return sample_points
210
272
 
211
- if len(sample_points) < 1 + self._data_bits:
212
- break
273
+ def _verify_start_bit(self, data: NDArray[np.bool_], sample_points: list[int]) -> bool:
274
+ """Verify start bit is valid.
213
275
 
214
- # Verify start bit (should be opposite of idle)
215
- start_bit = data[sample_points[0]]
216
- if (self._idle_level == 1 and start_bit) or (self._idle_level == 0 and not start_bit):
217
- # Not a valid start bit
218
- idx = start_idx + 1
219
- continue
276
+ Args:
277
+ data: Digital data array.
278
+ sample_points: Sample point indices.
220
279
 
221
- # Extract data bits
222
- data_value = 0
223
- data_bits = []
224
-
225
- for i in range(self._data_bits):
226
- bit_idx = sample_points[1 + i]
227
- bit_val = 1 if data[bit_idx] else 0
228
- data_bits.append(bit_val)
229
-
230
- if self._bit_order == "lsb":
231
- data_value |= bit_val << i
232
- else:
233
- data_value |= bit_val << (self._data_bits - 1 - i)
234
-
235
- # Check parity if enabled
236
- errors = []
237
- parity_idx = 1 + self._data_bits
238
-
239
- if self._parity != "none" and parity_idx < len(sample_points):
240
- parity_bit = 1 if data[sample_points[parity_idx]] else 0
241
- ones_count = sum(data_bits)
242
-
243
- if self._parity == "odd":
244
- expected = (ones_count + 1) % 2
245
- elif self._parity == "even":
246
- expected = ones_count % 2
247
- elif self._parity == "mark":
248
- expected = 1
249
- else: # space
250
- expected = 0
251
-
252
- if parity_bit != expected:
253
- errors.append("Parity error")
254
-
255
- # Verify stop bit(s)
256
- stop_idx = parity_idx + (1 if self._parity != "none" else 0)
257
- if stop_idx < len(sample_points):
258
- stop_bit = data[sample_points[stop_idx]]
259
- expected_stop = self._idle_level == 1
260
-
261
- if stop_bit != expected_stop:
262
- errors.append("Framing error")
263
-
264
- # Calculate timestamps
265
- start_time = start_idx / sample_rate
266
- end_time = (start_idx + frame_bits * bit_period) / sample_rate
267
-
268
- # Add annotations
269
- self.put_annotation(
270
- start_time,
271
- start_time + bit_period / sample_rate,
272
- AnnotationLevel.BITS,
273
- "START",
274
- )
280
+ Returns:
281
+ True if start bit valid.
282
+ """
283
+ start_bit = data[sample_points[0]]
284
+ expected_start = self._idle_level == 0
285
+ return bool(start_bit == expected_start)
275
286
 
276
- for i, bit_val in enumerate(data_bits):
277
- bit_start = start_time + (1 + i) * bit_period / sample_rate
278
- bit_end = bit_start + bit_period / sample_rate
279
- self.put_annotation(
280
- bit_start,
281
- bit_end,
282
- AnnotationLevel.BITS,
283
- str(bit_val),
284
- )
287
+ def _extract_data_bits(
288
+ self, data: NDArray[np.bool_], sample_points: list[int]
289
+ ) -> tuple[int, list[int]]:
290
+ """Extract data bits and convert to byte value.
285
291
 
286
- self.put_annotation(
287
- start_time,
288
- end_time,
289
- AnnotationLevel.BYTES,
290
- f"0x{data_value:02X}",
291
- data=bytes([data_value]),
292
- )
292
+ Args:
293
+ data: Digital data array.
294
+ sample_points: Sample point indices.
293
295
 
294
- # Create packet
295
- packet = ProtocolPacket(
296
- timestamp=start_time,
297
- protocol="uart",
298
- data=bytes([data_value]),
299
- annotations={
300
- "frame_num": frame_num,
301
- "data_bits": data_bits,
302
- "baudrate": self._baudrate,
303
- },
304
- errors=errors,
305
- )
296
+ Returns:
297
+ Tuple of (byte_value, bit_list).
298
+ """
299
+ data_value = 0
300
+ data_bits = []
306
301
 
307
- self.put_packet(start_time, bytes([data_value]), packet.annotations, errors)
302
+ for i in range(self._data_bits):
303
+ bit_idx = sample_points[1 + i]
304
+ bit_val = 1 if data[bit_idx] else 0
305
+ data_bits.append(bit_val)
308
306
 
309
- yield packet
307
+ if self._bit_order == "lsb":
308
+ data_value |= bit_val << i
309
+ else:
310
+ data_value |= bit_val << (self._data_bits - 1 - i)
310
311
 
311
- frame_num += 1
312
- # Advance to the end of the frame
313
- # Use the last sample point + 1 to avoid re-detecting the same frame
314
- last_sample = sample_points[-1] if sample_points else start_idx
315
- idx = last_sample + 1
312
+ return data_value, data_bits
313
+
314
+ def _validate_frame(
315
+ self, data: NDArray[np.bool_], sample_points: list[int], data_bits: list[int]
316
+ ) -> list[str]:
317
+ """Validate parity and stop bits.
318
+
319
+ Args:
320
+ data: Digital data array.
321
+ sample_points: Sample point indices.
322
+ data_bits: Extracted data bits.
323
+
324
+ Returns:
325
+ List of error messages.
326
+ """
327
+ errors = []
328
+ parity_idx = 1 + self._data_bits
329
+
330
+ # Check parity
331
+ if self._parity != "none" and parity_idx < len(sample_points):
332
+ parity_bit = 1 if data[sample_points[parity_idx]] else 0
333
+ expected_parity = self._calculate_parity(data_bits)
334
+ if parity_bit != expected_parity:
335
+ errors.append("Parity error")
336
+
337
+ # Verify stop bit
338
+ stop_idx = parity_idx + (1 if self._parity != "none" else 0)
339
+ if stop_idx < len(sample_points):
340
+ stop_bit = data[sample_points[stop_idx]]
341
+ expected_stop = self._idle_level == 1
342
+ if stop_bit != expected_stop:
343
+ errors.append("Framing error")
344
+
345
+ return errors
346
+
347
+ def _calculate_parity(self, data_bits: list[int]) -> int:
348
+ """Calculate expected parity bit.
349
+
350
+ Args:
351
+ data_bits: Data bit values.
352
+
353
+ Returns:
354
+ Expected parity bit (0 or 1).
355
+ """
356
+ ones_count = sum(data_bits)
357
+
358
+ if self._parity == "odd":
359
+ return (ones_count + 1) % 2
360
+ elif self._parity == "even":
361
+ return ones_count % 2
362
+ elif self._parity == "mark":
363
+ return 1
364
+ else: # space
365
+ return 0
366
+
367
+ def _build_packet(
368
+ self,
369
+ start_idx: int,
370
+ sample_rate: float,
371
+ bit_period: float,
372
+ frame_bits: float,
373
+ data_value: int,
374
+ data_bits: list[int],
375
+ frame_num: int,
376
+ errors: list[str],
377
+ ) -> ProtocolPacket:
378
+ """Build protocol packet with annotations.
379
+
380
+ Args:
381
+ start_idx: Frame start index.
382
+ sample_rate: Sample rate in Hz.
383
+ bit_period: Samples per bit.
384
+ frame_bits: Total bits in frame.
385
+ data_value: Decoded byte value.
386
+ data_bits: Individual bit values.
387
+ frame_num: Frame number.
388
+ errors: List of errors.
389
+
390
+ Returns:
391
+ Protocol packet.
392
+ """
393
+ start_time = start_idx / sample_rate
394
+
395
+ # Add annotations
396
+ self._add_frame_annotations(start_time, bit_period, sample_rate, data_value, data_bits)
397
+
398
+ packet = ProtocolPacket(
399
+ timestamp=start_time,
400
+ protocol="uart",
401
+ data=bytes([data_value]),
402
+ annotations={
403
+ "frame_num": frame_num,
404
+ "data_bits": data_bits,
405
+ "baudrate": self._baudrate,
406
+ },
407
+ errors=errors,
408
+ )
409
+
410
+ self.put_packet(start_time, bytes([data_value]), packet.annotations, errors)
411
+ return packet
412
+
413
+ def _add_frame_annotations(
414
+ self,
415
+ start_time: float,
416
+ bit_period: float,
417
+ sample_rate: float,
418
+ data_value: int,
419
+ data_bits: list[int],
420
+ ) -> None:
421
+ """Add bit-level and byte-level annotations.
422
+
423
+ Args:
424
+ start_time: Frame start time.
425
+ bit_period: Samples per bit.
426
+ sample_rate: Sample rate in Hz.
427
+ data_value: Decoded byte value.
428
+ data_bits: Individual bit values.
429
+ """
430
+ # Start bit annotation
431
+ self.put_annotation(
432
+ start_time,
433
+ start_time + bit_period / sample_rate,
434
+ AnnotationLevel.BITS,
435
+ "START",
436
+ )
437
+
438
+ # Data bit annotations
439
+ for i, bit_val in enumerate(data_bits):
440
+ bit_start = start_time + (1 + i) * bit_period / sample_rate
441
+ bit_end = bit_start + bit_period / sample_rate
442
+ self.put_annotation(
443
+ bit_start,
444
+ bit_end,
445
+ AnnotationLevel.BITS,
446
+ str(bit_val),
447
+ )
448
+
449
+ # Byte annotation
450
+ end_time = start_time + (1 + len(data_bits)) * bit_period / sample_rate
451
+ self.put_annotation(
452
+ start_time,
453
+ end_time,
454
+ AnnotationLevel.BYTES,
455
+ f"0x{data_value:02X}",
456
+ data=bytes([data_value]),
457
+ )
316
458
 
317
459
  def _find_start_bit(
318
460
  self,