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
oscura/session/session.py DELETED
@@ -1,520 +0,0 @@
1
- """Analysis session management.
2
-
3
- This module provides session save/restore functionality for Oscura.
4
-
5
-
6
- Example:
7
- >>> session = Session()
8
- >>> session.load_trace('capture.wfm')
9
- >>> session.save('debug_session.tks')
10
- >>>
11
- >>> # Later...
12
- >>> session = load_session('debug_session.tks')
13
- """
14
-
15
- from __future__ import annotations
16
-
17
- import gzip
18
- import hashlib
19
- import hmac
20
- import pickle
21
- from dataclasses import dataclass, field
22
- from datetime import datetime
23
- from pathlib import Path
24
- from typing import Any, cast
25
-
26
- import numpy as np
27
-
28
- from oscura.core.exceptions import SecurityError
29
- from oscura.session.annotations import AnnotationLayer
30
- from oscura.session.history import OperationHistory
31
-
32
- # Session file format constants
33
- _SESSION_MAGIC = b"OSC1" # Magic bytes for new format with signature
34
- _SESSION_SIGNATURE_SIZE = 32 # SHA256 hash size in bytes
35
- _SECURITY_KEY = hashlib.sha256(b"oscura-session-v1").digest()
36
-
37
-
38
- @dataclass
39
- class Session:
40
- """Analysis session container.
41
-
42
- Manages traces, annotations, measurements, and history for a complete
43
- analysis session. Sessions can be saved and restored.
44
-
45
- Attributes:
46
- name: Session name
47
- traces: Dictionary of loaded traces (name -> trace)
48
- annotation_layers: Annotation layers
49
- measurements: Recorded measurements
50
- history: Operation history
51
- metadata: Session metadata
52
- created_at: Creation timestamp
53
- modified_at: Last modification timestamp
54
- """
55
-
56
- name: str = "Untitled Session"
57
- traces: dict[str, Any] = field(default_factory=dict)
58
- annotation_layers: dict[str, AnnotationLayer] = field(default_factory=dict)
59
- measurements: dict[str, Any] = field(default_factory=dict)
60
- history: OperationHistory = field(default_factory=OperationHistory)
61
- metadata: dict[str, Any] = field(default_factory=dict)
62
- created_at: datetime = field(default_factory=datetime.now)
63
- modified_at: datetime = field(default_factory=datetime.now)
64
- _file_path: Path | None = None
65
-
66
- def __post_init__(self) -> None:
67
- """Initialize default annotation layer."""
68
- if "default" not in self.annotation_layers:
69
- self.annotation_layers["default"] = AnnotationLayer("Default")
70
-
71
- def load_trace(
72
- self,
73
- path: str | Path,
74
- name: str | None = None,
75
- **load_kwargs: Any,
76
- ) -> Any:
77
- """Load a trace into the session.
78
-
79
- Args:
80
- path: Path to trace file.
81
- name: Name for trace in session (default: filename).
82
- **load_kwargs: Additional arguments for load().
83
-
84
- Returns:
85
- Loaded trace.
86
- """
87
- from oscura.loaders import load
88
-
89
- path = Path(path)
90
- trace = load(str(path), **load_kwargs)
91
-
92
- if name is None:
93
- name = path.stem
94
-
95
- self.traces[name] = trace
96
- self._mark_modified()
97
-
98
- self.history.record(
99
- "load_trace",
100
- {"path": str(path), "name": name},
101
- result=f"Loaded {name}",
102
- )
103
-
104
- return trace
105
-
106
- def add_trace(
107
- self,
108
- name: str,
109
- trace: Any,
110
- ) -> None:
111
- """Add an in-memory trace to the session.
112
-
113
- This method allows adding traces that were created programmatically
114
- or loaded separately, rather than loading from a file.
115
-
116
- Args:
117
- name: Name for the trace in the session.
118
- trace: Trace object (WaveformTrace, DigitalTrace, etc.).
119
-
120
- Raises:
121
- ValueError: If name is empty or already exists.
122
- TypeError: If trace doesn't have expected attributes.
123
-
124
- Example:
125
- >>> session = Session()
126
- >>> data = np.sin(np.linspace(0, 2*np.pi, 1000))
127
- >>> trace = osc.WaveformTrace(data=data, metadata=osc.TraceMetadata(sample_rate=1e6))
128
- >>> session.add_trace("my_trace", trace)
129
- """
130
- if not name:
131
- raise ValueError("Trace name cannot be empty")
132
-
133
- if not hasattr(trace, "data"):
134
- raise TypeError("Trace must have a 'data' attribute")
135
-
136
- self.traces[name] = trace
137
- self._mark_modified()
138
-
139
- self.history.record(
140
- "add_trace",
141
- {"name": name, "type": type(trace).__name__},
142
- result=f"Added {name}",
143
- )
144
-
145
- def remove_trace(self, name: str) -> None:
146
- """Remove a trace from the session.
147
-
148
- Args:
149
- name: Name of the trace to remove.
150
-
151
- Raises:
152
- KeyError: If trace not found.
153
- """
154
- if name not in self.traces:
155
- raise KeyError(f"Trace '{name}' not found in session")
156
-
157
- del self.traces[name]
158
- self._mark_modified()
159
-
160
- self.history.record(
161
- "remove_trace",
162
- {"name": name},
163
- result=f"Removed {name}",
164
- )
165
-
166
- def get_trace(self, name: str) -> Any:
167
- """Get trace by name.
168
-
169
- Args:
170
- name: Trace name.
171
-
172
- Returns:
173
- Trace object.
174
- """
175
- return self.traces[name]
176
-
177
- def list_traces(self) -> list[str]:
178
- """List all trace names."""
179
- return list(self.traces.keys())
180
-
181
- def annotate(
182
- self,
183
- text: str,
184
- *,
185
- time: float | None = None,
186
- time_range: tuple[float, float] | None = None,
187
- layer: str = "default",
188
- **kwargs: Any,
189
- ) -> None:
190
- """Add annotation to session.
191
-
192
- Args:
193
- text: Annotation text.
194
- time: Time point for annotation.
195
- time_range: Time range for annotation.
196
- layer: Annotation layer name.
197
- **kwargs: Additional annotation parameters.
198
- """
199
- if layer not in self.annotation_layers:
200
- self.annotation_layers[layer] = AnnotationLayer(layer)
201
-
202
- self.annotation_layers[layer].add(
203
- text=text,
204
- time=time,
205
- time_range=time_range,
206
- **kwargs,
207
- )
208
- self._mark_modified()
209
-
210
- self.history.record(
211
- "annotate",
212
- {"text": text, "time": time, "layer": layer},
213
- )
214
-
215
- def get_annotations(
216
- self,
217
- layer: str | None = None,
218
- time_range: tuple[float, float] | None = None,
219
- ) -> list[Any]:
220
- """Get annotations.
221
-
222
- Args:
223
- layer: Filter by layer name (None for all layers).
224
- time_range: Filter by time range.
225
-
226
- Returns:
227
- List of annotations.
228
- """
229
- annotations = []
230
-
231
- layers = [self.annotation_layers[layer]] if layer else self.annotation_layers.values()
232
-
233
- for ann_layer in layers:
234
- if time_range:
235
- annotations.extend(ann_layer.find_in_range(time_range[0], time_range[1]))
236
- else:
237
- annotations.extend(ann_layer.annotations)
238
-
239
- return annotations
240
-
241
- def record_measurement(
242
- self,
243
- name: str,
244
- value: Any,
245
- unit: str = "",
246
- trace_name: str | None = None,
247
- **metadata: Any,
248
- ) -> None:
249
- """Record a measurement result.
250
-
251
- Args:
252
- name: Measurement name (e.g., 'rise_time').
253
- value: Measurement value.
254
- unit: Unit of measurement.
255
- trace_name: Associated trace name.
256
- **metadata: Additional metadata.
257
- """
258
- self.measurements[name] = {
259
- "value": value,
260
- "unit": unit,
261
- "trace": trace_name,
262
- "timestamp": datetime.now().isoformat(),
263
- **metadata,
264
- }
265
- self._mark_modified()
266
-
267
- self.history.record(
268
- f"measure_{name}",
269
- {"trace": trace_name},
270
- result=f"{value} {unit}".strip(),
271
- )
272
-
273
- def get_measurements(self) -> dict[str, Any]:
274
- """Get all recorded measurements."""
275
- return self.measurements.copy()
276
-
277
- def save(
278
- self,
279
- path: str | Path | None = None,
280
- *,
281
- include_traces: bool = True,
282
- compress: bool = True,
283
- ) -> Path:
284
- """Save session to file with HMAC signature for integrity verification.
285
-
286
- Args:
287
- path: Output path (default: use existing or generate).
288
- include_traces: Include trace data in session file.
289
- compress: Compress session file with gzip.
290
-
291
- Returns:
292
- Path to saved file.
293
-
294
- Example:
295
- >>> session.save('analysis.tks')
296
-
297
- Security Note:
298
- Session files now include HMAC signatures for integrity verification.
299
- Files are still pickle-based - only load from trusted sources.
300
- For secure data exchange with untrusted parties, use JSON or HDF5
301
- export formats instead.
302
- """
303
- if path is None:
304
- path = self._file_path or Path(f"{self.name.replace(' ', '_')}.tks")
305
- else:
306
- path = Path(path)
307
-
308
- self._file_path = path
309
- self._mark_modified()
310
-
311
- # Build session data
312
- data = self._to_dict(include_traces=include_traces)
313
-
314
- # Serialize with pickle
315
- serialized = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
316
-
317
- # Compute HMAC signature
318
- signature = hmac.new(_SECURITY_KEY, serialized, hashlib.sha256).digest()
319
-
320
- # Write: magic bytes + signature + pickled data
321
- if compress:
322
- with gzip.open(path, "wb") as f:
323
- f.write(_SESSION_MAGIC)
324
- f.write(signature)
325
- f.write(serialized)
326
- else:
327
- with open(path, "wb") as f:
328
- f.write(_SESSION_MAGIC)
329
- f.write(signature)
330
- f.write(serialized)
331
-
332
- self.history.record("save", {"path": str(path)})
333
-
334
- return path
335
-
336
- def _to_dict(self, include_traces: bool = True) -> dict[str, Any]:
337
- """Convert session to dictionary."""
338
- data: dict[str, Any] = {
339
- "version": "1.0",
340
- "name": self.name,
341
- "created_at": self.created_at.isoformat(),
342
- "modified_at": self.modified_at.isoformat(),
343
- "annotation_layers": {
344
- name: layer.to_dict() for name, layer in self.annotation_layers.items()
345
- },
346
- "measurements": self.measurements,
347
- "history": self.history.to_dict(),
348
- "metadata": self.metadata,
349
- }
350
-
351
- if include_traces:
352
- # Store traces with their data
353
- data["traces"] = {}
354
- for name, trace in self.traces.items():
355
- trace_data = {
356
- "type": type(trace).__name__,
357
- "data": trace.data.tolist() if hasattr(trace, "data") else None,
358
- "sample_rate": (
359
- trace.metadata.sample_rate if hasattr(trace, "metadata") else None
360
- ),
361
- }
362
- data["traces"][name] = trace_data
363
- else:
364
- data["traces"] = {}
365
-
366
- return data
367
-
368
- @classmethod
369
- def _from_dict(cls, data: dict[str, Any]) -> Session:
370
- """Create session from dictionary."""
371
- session = cls(
372
- name=data.get("name", "Untitled Session"),
373
- metadata=data.get("metadata", {}),
374
- )
375
-
376
- if "created_at" in data:
377
- session.created_at = datetime.fromisoformat(data["created_at"])
378
- if "modified_at" in data:
379
- session.modified_at = datetime.fromisoformat(data["modified_at"])
380
-
381
- # Restore annotation layers
382
- for name, layer_data in data.get("annotation_layers", {}).items():
383
- session.annotation_layers[name] = AnnotationLayer.from_dict(layer_data)
384
-
385
- # Restore measurements
386
- session.measurements = data.get("measurements", {})
387
-
388
- # Restore history
389
- if "history" in data:
390
- session.history = OperationHistory.from_dict(data["history"])
391
-
392
- # Restore traces (if included)
393
- if "traces" in data:
394
- from oscura.core.types import WaveformTrace
395
-
396
- for name, trace_data in data["traces"].items():
397
- if trace_data.get("data") is not None:
398
- session.traces[name] = WaveformTrace( # type: ignore[call-arg]
399
- data=np.array(trace_data["data"]),
400
- sample_rate=trace_data.get("sample_rate", 1.0),
401
- )
402
-
403
- return session
404
-
405
- def _mark_modified(self) -> None:
406
- """Update modification timestamp."""
407
- self.modified_at = datetime.now()
408
-
409
- def summary(self) -> str:
410
- """Get session summary."""
411
- lines = [
412
- f"Session: {self.name}",
413
- f"Created: {self.created_at.strftime('%Y-%m-%d %H:%M')}",
414
- f"Modified: {self.modified_at.strftime('%Y-%m-%d %H:%M')}",
415
- f"Traces: {len(self.traces)}",
416
- f"Annotations: {sum(len(l.annotations) for l in self.annotation_layers.values())}", # noqa: E741
417
- f"Measurements: {len(self.measurements)}",
418
- f"History entries: {len(self.history.entries)}",
419
- ]
420
- return "\n".join(lines)
421
-
422
-
423
- def load_session(path: str | Path) -> Session:
424
- """Load session from file with HMAC signature verification.
425
-
426
- Session files must be in the current OSC1 format with HMAC signature.
427
- Legacy session files without signatures are not supported.
428
-
429
- Args:
430
- path: Path to session file (.tks).
431
-
432
- Returns:
433
- Loaded Session object.
434
-
435
- Raises:
436
- SecurityError: If signature verification fails or file is not in OSC1 format.
437
- gzip.BadGzipFile: If file is neither valid gzip nor uncompressed session.
438
-
439
- Example:
440
- >>> session = load_session('debug_session.tks')
441
- >>> print(session.list_traces())
442
-
443
- Security Warning:
444
- Session files use pickle serialization. Only load session files from
445
- trusted sources. Loading a malicious .tks file could execute arbitrary
446
- code. Never load session files from untrusted or unknown sources.
447
-
448
- All session files must include HMAC signatures for integrity verification.
449
- For secure data exchange with untrusted parties, consider exporting to
450
- JSON or HDF5 formats instead of using pickle-based session files.
451
- """
452
- path = Path(path)
453
-
454
- def _load_with_verification(f: Any) -> dict[str, Any]:
455
- """Load and verify session file with HMAC signature.
456
-
457
- Args:
458
- f: File object (gzip or regular).
459
-
460
- Returns:
461
- Deserialized session dictionary.
462
-
463
- Raises:
464
- SecurityError: If magic bytes or signature verification fails.
465
- """
466
- # Read magic bytes
467
- magic = f.read(len(_SESSION_MAGIC))
468
-
469
- if magic != _SESSION_MAGIC:
470
- raise SecurityError(
471
- "This is a legacy session file. Please re-save with current version.",
472
- file_path=str(path),
473
- check_type="Session format",
474
- details="Expected OSC1 format with HMAC signature",
475
- )
476
-
477
- # Read signature and payload
478
- signature = f.read(_SESSION_SIGNATURE_SIZE)
479
- serialized = f.read()
480
-
481
- if not signature or not serialized:
482
- raise SecurityError(
483
- "This is a legacy session file. Please re-save with current version.",
484
- file_path=str(path),
485
- check_type="Session format",
486
- details="File is incomplete or corrupted",
487
- )
488
-
489
- # Verify HMAC signature
490
- expected = hmac.new(_SECURITY_KEY, serialized, hashlib.sha256).digest()
491
- if not hmac.compare_digest(signature, expected):
492
- raise SecurityError(
493
- "Session file signature verification failed",
494
- file_path=str(path),
495
- check_type="HMAC signature",
496
- details="File may be corrupted or tampered with",
497
- )
498
-
499
- # Deserialize verified data
500
- data = cast("dict[str, Any]", pickle.loads(serialized))
501
- return data
502
-
503
- # Try loading (compressed first, then uncompressed)
504
- try:
505
- with gzip.open(path, "rb") as f:
506
- data = _load_with_verification(f)
507
- except gzip.BadGzipFile:
508
- with open(path, "rb") as f: # type: ignore[assignment]
509
- data = _load_with_verification(f)
510
-
511
- session = Session._from_dict(data)
512
- session._file_path = path
513
-
514
- return session
515
-
516
-
517
- __all__ = [
518
- "Session",
519
- "load_session",
520
- ]