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
@@ -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)