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
@@ -17,7 +17,7 @@ References:
17
17
  from __future__ import annotations
18
18
 
19
19
  from dataclasses import dataclass
20
- from typing import TYPE_CHECKING, Literal
20
+ from typing import TYPE_CHECKING, Any, Literal
21
21
 
22
22
  import numpy as np
23
23
 
@@ -122,9 +122,28 @@ def plot_protocol_timing(
122
122
  if len(signals) == 0:
123
123
  raise ValueError("signals list cannot be empty")
124
124
 
125
+ # Setup figure and axes
126
+ fig, axes = _setup_timing_figure(signals, figsize)
127
+
128
+ # Determine time parameters
129
+ t_min, t_max = _determine_time_range(signals, sample_rate, time_range)
130
+ time_unit_final, time_mult = _select_timing_unit(time_unit, t_min, t_max)
131
+
132
+ # Plot each signal
133
+ _plot_all_signals(axes, signals, sample_rate, t_min, t_max, time_mult, style)
134
+
135
+ # Finalize plot
136
+ _finalize_timing_plot(fig, axes, time_unit_final, title)
137
+
138
+ return fig
139
+
140
+
141
+ def _setup_timing_figure(
142
+ signals: list[ProtocolSignal], figsize: tuple[float, float] | None
143
+ ) -> tuple[Figure, list[Any]]:
144
+ """Setup figure and axes for timing diagram."""
125
145
  n_signals = len(signals)
126
146
 
127
- # Auto-calculate figure size
128
147
  if figsize is None:
129
148
  width = 12
130
149
  height = max(4, n_signals * 0.8 + 1)
@@ -141,77 +160,106 @@ def plot_protocol_timing(
141
160
  if n_signals == 1:
142
161
  axes = [axes]
143
162
 
144
- # Determine time range
145
- if time_range is None:
146
- max_len = max(len(sig.data) for sig in signals)
147
- t_min = 0.0
148
- t_max = max_len / sample_rate
149
- else:
150
- t_min, t_max = time_range
163
+ return fig, axes
164
+
165
+
166
+ def _determine_time_range(
167
+ signals: list[ProtocolSignal],
168
+ sample_rate: float,
169
+ time_range: tuple[float, float] | None,
170
+ ) -> tuple[float, float]:
171
+ """Determine time range for plotting."""
172
+ if time_range is not None:
173
+ return time_range
174
+
175
+ max_len = max(len(sig.data) for sig in signals)
176
+ return 0.0, max_len / sample_rate
177
+
151
178
 
152
- # Select time unit
179
+ def _select_timing_unit(time_unit: str, t_min: float, t_max: float) -> tuple[str, float]:
180
+ """Select appropriate time unit and multiplier."""
153
181
  if time_unit == "auto":
154
182
  time_range_val = t_max - t_min
155
183
  if time_range_val < 1e-6:
156
- time_unit = "ns"
157
- time_mult = 1e9
184
+ return "ns", 1e9
158
185
  elif time_range_val < 1e-3:
159
- time_unit = "us"
160
- time_mult = 1e6
186
+ return "us", 1e6
161
187
  elif time_range_val < 1:
162
- time_unit = "ms"
163
- time_mult = 1e3
188
+ return "ms", 1e3
164
189
  else:
165
- time_unit = "s"
166
- time_mult = 1.0
190
+ return "s", 1.0
167
191
  else:
168
192
  time_mult = {"s": 1.0, "ms": 1e3, "us": 1e6, "ns": 1e9}.get(time_unit, 1.0)
193
+ return time_unit, time_mult
169
194
 
170
- # Plot each signal
195
+
196
+ def _plot_all_signals(
197
+ axes: list[Any],
198
+ signals: list[ProtocolSignal],
199
+ sample_rate: float,
200
+ t_min: float,
201
+ t_max: float,
202
+ time_mult: float,
203
+ style: str,
204
+ ) -> None:
205
+ """Plot all signals on their respective axes."""
171
206
  for _idx, (signal, ax) in enumerate(zip(signals, axes, strict=False)):
172
- # Create time vector
173
207
  time = np.arange(len(signal.data)) / sample_rate * time_mult
174
208
 
175
209
  # Filter to time range
176
210
  mask = (time >= t_min * time_mult) & (time <= t_max * time_mult)
177
- time = time[mask]
178
- data = signal.data[mask]
211
+ time_filtered = time[mask]
212
+ data_filtered = signal.data[mask]
179
213
 
214
+ # Plot signal
180
215
  if style == "wavedrom":
181
- _plot_wavedrom_signal(ax, time, data, signal)
216
+ _plot_wavedrom_signal(ax, time_filtered, data_filtered, signal)
182
217
  else:
183
- _plot_classic_signal(ax, time, data, signal)
218
+ _plot_classic_signal(ax, time_filtered, data_filtered, signal)
184
219
 
185
- # Add signal name label
186
- ax.set_ylabel(signal.name, rotation=0, ha="right", va="center", fontsize=10)
187
- ax.set_ylim(-0.2, 1.3)
220
+ # Format axes
221
+ _format_signal_axes(ax, signal.name)
188
222
 
189
- # Remove y-axis ticks
190
- ax.set_yticks([])
223
+ # Add annotations
224
+ _add_signal_annotations(ax, signal, t_min, t_max, time_mult)
191
225
 
192
- # Grid for timing
193
- ax.grid(True, axis="x", alpha=0.3, linestyle=":")
194
226
 
195
- # Add annotations
196
- if signal.annotations:
197
- for t, text in signal.annotations.items():
198
- if t_min <= t <= t_max:
199
- ax.annotate(
200
- text,
201
- xy=(t * time_mult, 1.2),
202
- fontsize=8,
203
- ha="center",
204
- bbox={"boxstyle": "round,pad=0.3", "facecolor": "yellow", "alpha": 0.7},
205
- )
227
+ def _format_signal_axes(ax: Axes, signal_name: str) -> None:
228
+ """Format axes for a single signal."""
229
+ ax.set_ylabel(signal_name, rotation=0, ha="right", va="center", fontsize=10)
230
+ ax.set_ylim(-0.2, 1.3)
231
+ ax.set_yticks([])
232
+ ax.grid(True, axis="x", alpha=0.3, linestyle=":")
206
233
 
207
- # X-axis label only on bottom plot
234
+
235
+ def _add_signal_annotations(
236
+ ax: Axes,
237
+ signal: ProtocolSignal,
238
+ t_min: float,
239
+ t_max: float,
240
+ time_mult: float,
241
+ ) -> None:
242
+ """Add annotations to signal plot."""
243
+ if signal.annotations:
244
+ for t, text in signal.annotations.items():
245
+ if t_min <= t <= t_max:
246
+ ax.annotate(
247
+ text,
248
+ xy=(t * time_mult, 1.2),
249
+ fontsize=8,
250
+ ha="center",
251
+ bbox={"boxstyle": "round,pad=0.3", "facecolor": "yellow", "alpha": 0.7},
252
+ )
253
+
254
+
255
+ def _finalize_timing_plot(fig: Figure, axes: list[Any], time_unit: str, title: str | None) -> None:
256
+ """Finalize timing plot with labels and title."""
208
257
  axes[-1].set_xlabel(f"Time ({time_unit})", fontsize=11)
209
258
 
210
259
  if title:
211
260
  fig.suptitle(title, fontsize=14, y=0.98)
212
261
 
213
262
  fig.tight_layout()
214
- return fig
215
263
 
216
264
 
217
265
  def _plot_wavedrom_signal(
@@ -333,14 +381,32 @@ def plot_state_machine(
333
381
  if not HAS_MATPLOTLIB:
334
382
  raise ImportError("matplotlib is required for visualization")
335
383
 
384
+ # Setup figure
336
385
  fig, ax = plt.subplots(figsize=figsize)
337
-
338
- # Calculate state positions using selected layout
339
386
  positions = _calculate_state_positions(states, layout)
340
-
341
- # Draw states as circles
342
387
  state_radius = 0.15
343
388
 
389
+ # Draw all states
390
+ _draw_all_states(ax, positions, state_radius, initial_state, final_states)
391
+
392
+ # Draw all transitions
393
+ _draw_all_transitions(ax, transitions, positions, state_radius)
394
+
395
+ # Finalize axes
396
+ _finalize_state_machine_plot(ax, title)
397
+
398
+ fig.tight_layout()
399
+ return fig
400
+
401
+
402
+ def _draw_all_states(
403
+ ax: Axes,
404
+ positions: dict[str, tuple[float, float]],
405
+ state_radius: float,
406
+ initial_state: str | None,
407
+ final_states: list[str] | None,
408
+ ) -> None:
409
+ """Draw all state nodes with appropriate markers."""
344
410
  for state, (x, y) in positions.items():
345
411
  # Draw state circle
346
412
  circle = patches.Circle(
@@ -386,7 +452,14 @@ def plot_state_machine(
386
452
  fontweight="bold",
387
453
  )
388
454
 
389
- # Draw transitions as arrows
455
+
456
+ def _draw_all_transitions(
457
+ ax: Axes,
458
+ transitions: list[StateTransition],
459
+ positions: dict[str, tuple[float, float]],
460
+ state_radius: float,
461
+ ) -> None:
462
+ """Draw all transition arrows between states."""
390
463
  for trans in transitions:
391
464
  if trans.from_state not in positions or trans.to_state not in positions:
392
465
  continue
@@ -394,65 +467,85 @@ def plot_state_machine(
394
467
  x1, y1 = positions[trans.from_state]
395
468
  x2, y2 = positions[trans.to_state]
396
469
 
397
- # Calculate arrow start/end on circle perimeter
470
+ # Check for self-loop
398
471
  dx = x2 - x1
399
472
  dy = y2 - y1
400
473
  dist = np.sqrt(dx**2 + dy**2)
401
474
 
402
475
  if dist < 1e-6:
403
- # Self-loop
404
476
  _draw_self_loop(ax, x1, y1, state_radius, trans.condition)
405
477
  continue
406
478
 
407
- # Normalize
408
- dx_norm = dx / dist
409
- dy_norm = dy / dist
410
-
411
- # Arrow start/end on circle edges
412
- arrow_start_x = x1 + dx_norm * state_radius
413
- arrow_start_y = y1 + dy_norm * state_radius
414
- arrow_end_x = x2 - dx_norm * state_radius
415
- arrow_end_y = y2 - dy_norm * state_radius
416
-
417
- # Line style
418
- linestyle = {
419
- "solid": "-",
420
- "dashed": "--",
421
- "dotted": ":",
422
- }.get(trans.style, "-")
423
-
424
- # Draw arrow
425
- ax.annotate(
426
- "",
427
- xy=(arrow_end_x, arrow_end_y),
428
- xytext=(arrow_start_x, arrow_start_y),
429
- arrowprops={
430
- "arrowstyle": "->",
431
- "lw": 1.5,
432
- "linestyle": linestyle,
433
- "color": "black",
479
+ # Draw regular transition arrow
480
+ _draw_transition_arrow(ax, x1, y1, x2, y2, state_radius, trans)
481
+
482
+
483
+ def _draw_transition_arrow(
484
+ ax: Axes,
485
+ x1: float,
486
+ y1: float,
487
+ x2: float,
488
+ y2: float,
489
+ state_radius: float,
490
+ trans: StateTransition,
491
+ ) -> None:
492
+ """Draw single transition arrow with label."""
493
+ # Calculate arrow start/end on circle perimeter
494
+ dx = x2 - x1
495
+ dy = y2 - y1
496
+ dist = np.sqrt(dx**2 + dy**2)
497
+
498
+ # Normalize
499
+ dx_norm = dx / dist
500
+ dy_norm = dy / dist
501
+
502
+ # Arrow start/end on circle edges
503
+ arrow_start_x = x1 + dx_norm * state_radius
504
+ arrow_start_y = y1 + dy_norm * state_radius
505
+ arrow_end_x = x2 - dx_norm * state_radius
506
+ arrow_end_y = y2 - dy_norm * state_radius
507
+
508
+ # Line style
509
+ linestyle = {
510
+ "solid": "-",
511
+ "dashed": "--",
512
+ "dotted": ":",
513
+ }.get(trans.style, "-")
514
+
515
+ # Draw arrow
516
+ ax.annotate(
517
+ "",
518
+ xy=(arrow_end_x, arrow_end_y),
519
+ xytext=(arrow_start_x, arrow_start_y),
520
+ arrowprops={
521
+ "arrowstyle": "->",
522
+ "lw": 1.5,
523
+ "linestyle": linestyle,
524
+ "color": "black",
525
+ },
526
+ )
527
+
528
+ # Add transition label
529
+ if trans.condition:
530
+ mid_x = (x1 + x2) / 2
531
+ mid_y = (y1 + y2) / 2
532
+ ax.text(
533
+ mid_x,
534
+ mid_y,
535
+ trans.condition,
536
+ fontsize=8,
537
+ ha="center",
538
+ bbox={
539
+ "boxstyle": "round,pad=0.3",
540
+ "facecolor": "white",
541
+ "edgecolor": "gray",
542
+ "alpha": 0.9,
434
543
  },
435
544
  )
436
545
 
437
- # Add transition label
438
- if trans.condition:
439
- mid_x = (x1 + x2) / 2
440
- mid_y = (y1 + y2) / 2
441
- ax.text(
442
- mid_x,
443
- mid_y,
444
- trans.condition,
445
- fontsize=8,
446
- ha="center",
447
- bbox={
448
- "boxstyle": "round,pad=0.3",
449
- "facecolor": "white",
450
- "edgecolor": "gray",
451
- "alpha": 0.9,
452
- },
453
- )
454
546
 
455
- # Set axis properties
547
+ def _finalize_state_machine_plot(ax: Axes, title: str | None) -> None:
548
+ """Set axis properties and title for state machine plot."""
456
549
  ax.set_aspect("equal")
457
550
  ax.axis("off")
458
551
  ax.set_xlim(-0.2, 1.2)
@@ -461,9 +554,6 @@ def plot_state_machine(
461
554
  if title:
462
555
  ax.set_title(title, fontsize=14, pad=20)
463
556
 
464
- fig.tight_layout()
465
- return fig
466
-
467
557
 
468
558
  def _calculate_state_positions(
469
559
  states: list[str],
@@ -495,7 +585,7 @@ def _calculate_state_positions(
495
585
  else: # force-directed (simplified)
496
586
  # Use random positions as a placeholder for true force-directed layout
497
587
  np.random.seed(42)
498
- for i, state in enumerate(states): # noqa: B007
588
+ for i, state in enumerate(states):
499
589
  x = 0.2 + 0.6 * np.random.rand()
500
590
  y = 0.2 + 0.6 * np.random.rand()
501
591
  positions[state] = (x, y)