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
oscura/cli/decode.py CHANGED
@@ -25,44 +25,44 @@ from oscura.core.types import DigitalTrace, ProtocolPacket, WaveformTrace
25
25
  logger = logging.getLogger("oscura.cli.decode")
26
26
 
27
27
 
28
- @click.command() # type: ignore[misc]
29
- @click.argument("file", type=click.Path(exists=True)) # type: ignore[misc]
30
- @click.option( # type: ignore[misc]
28
+ @click.command()
29
+ @click.argument("file", type=click.Path(exists=True))
30
+ @click.option(
31
31
  "--protocol",
32
32
  type=click.Choice(["uart", "spi", "i2c", "can", "auto"], case_sensitive=False),
33
33
  default="auto",
34
34
  help="Protocol type (default: auto-detect).",
35
35
  )
36
- @click.option( # type: ignore[misc]
36
+ @click.option(
37
37
  "--baud-rate",
38
38
  type=int,
39
39
  default=None,
40
40
  help="Baud rate for UART (auto-detect if not specified).",
41
41
  )
42
- @click.option( # type: ignore[misc]
42
+ @click.option(
43
43
  "--parity",
44
44
  type=click.Choice(["none", "even", "odd"], case_sensitive=False),
45
45
  default="none",
46
46
  help="Parity for UART (default: none).",
47
47
  )
48
- @click.option( # type: ignore[misc]
48
+ @click.option(
49
49
  "--stop-bits",
50
50
  type=click.Choice(["1", "2"]),
51
51
  default="1",
52
52
  help="Stop bits for UART (default: 1).",
53
53
  )
54
- @click.option( # type: ignore[misc]
54
+ @click.option(
55
55
  "--show-errors",
56
56
  is_flag=True,
57
57
  help="Show only errors with context.",
58
58
  )
59
- @click.option( # type: ignore[misc]
59
+ @click.option(
60
60
  "--output",
61
61
  type=click.Choice(["json", "csv", "html", "table"], case_sensitive=False),
62
62
  default="table",
63
63
  help="Output format (default: table).",
64
64
  )
65
- @click.pass_context # type: ignore[misc]
65
+ @click.pass_context
66
66
  def decode(
67
67
  ctx: click.Context,
68
68
  file: str,
@@ -198,80 +198,113 @@ def _perform_decoding(
198
198
  Returns:
199
199
  Dictionary of decoding results.
200
200
  """
201
- # Import protocol decoders
202
- from oscura.inference.protocol import detect_protocol
201
+ # Build base results
202
+ results = _build_base_results(trace)
203
+
204
+ # Auto-detect protocol if needed
205
+ detected_protocol, baud_rate = _detect_protocol_if_auto(trace, protocol, baud_rate, results)
206
+ results["protocol"] = detected_protocol.upper()
207
+
208
+ # Decode with appropriate decoder
209
+ digital_trace = _to_digital(trace)
210
+ packets, errors = _dispatch_protocol_decode(
211
+ digital_trace, detected_protocol, baud_rate, parity, stop_bits, show_errors, results
212
+ )
203
213
 
214
+ # Filter and format results
215
+ if show_errors:
216
+ packets = [p for p in packets if p.errors]
217
+
218
+ _add_packet_summary(results, packets, errors)
219
+ return results
220
+
221
+
222
+ def _build_base_results(trace: WaveformTrace | DigitalTrace) -> dict[str, Any]:
223
+ """Build base results dictionary with trace metadata."""
204
224
  sample_rate = trace.metadata.sample_rate
205
225
  duration_ms = len(trace.data) / sample_rate * 1e3
206
-
207
- results: dict[str, Any] = {
226
+ return {
208
227
  "sample_rate": f"{sample_rate / 1e6:.1f} MHz",
209
228
  "samples": len(trace.data),
210
229
  "duration": f"{duration_ms:.3f} ms",
211
230
  }
212
231
 
213
- # Auto-detect protocol if requested
214
- detected_protocol = protocol
215
- detection_confidence = 1.0
216
232
 
217
- if protocol.lower() == "auto":
218
- try:
219
- detection = detect_protocol(trace, min_confidence=0.5, return_candidates=True) # type: ignore[arg-type]
220
- detected_protocol = detection["protocol"].lower()
221
- detection_confidence = detection["confidence"]
222
- results["auto_detection"] = {
223
- "protocol": detection["protocol"],
224
- "confidence": f"{detection_confidence:.1%}",
225
- "candidates": [
226
- {"protocol": c["protocol"], "confidence": f"{c['confidence']:.1%}"}
227
- for c in detection.get("candidates", [])[:3]
228
- ],
229
- }
230
- # Extract config suggestions
231
- if "config" in detection:
232
- if detected_protocol == "uart" and baud_rate is None:
233
- baud_rate = detection["config"].get("baud_rate")
234
- except Exception as e:
235
- logger.warning(f"Auto-detection failed: {e}, defaulting to UART")
236
- detected_protocol = "uart"
237
- detection_confidence = 0.0
233
+ def _detect_protocol_if_auto(
234
+ trace: WaveformTrace | DigitalTrace,
235
+ protocol: str,
236
+ baud_rate: int | None,
237
+ results: dict[str, Any],
238
+ ) -> tuple[str, int | None]:
239
+ """Auto-detect protocol if requested, otherwise return protocol as-is."""
240
+ if protocol.lower() != "auto":
241
+ return protocol, baud_rate
238
242
 
239
- results["protocol"] = detected_protocol.upper()
243
+ from oscura.inference.protocol import detect_protocol
240
244
 
241
- # Convert to digital trace for decoding
242
- digital_trace = _to_digital(trace)
245
+ try:
246
+ detection = detect_protocol(trace, min_confidence=0.5, return_candidates=True) # type: ignore[arg-type]
247
+ detected_protocol = detection["protocol"].lower()
248
+ results["auto_detection"] = _format_detection_results(detection)
249
+
250
+ # Extract config suggestions
251
+ if "config" in detection and detected_protocol == "uart" and baud_rate is None:
252
+ baud_rate = detection["config"].get("baud_rate")
253
+
254
+ return detected_protocol, baud_rate
255
+ except Exception as e:
256
+ logger.warning(f"Auto-detection failed: {e}, defaulting to UART")
257
+ return "uart", baud_rate
258
+
259
+
260
+ def _format_detection_results(detection: dict[str, Any]) -> dict[str, Any]:
261
+ """Format auto-detection results for display."""
262
+ return {
263
+ "protocol": detection["protocol"],
264
+ "confidence": f"{detection['confidence']:.1%}",
265
+ "candidates": [
266
+ {"protocol": c["protocol"], "confidence": f"{c['confidence']:.1%}"}
267
+ for c in detection.get("candidates", [])[:3]
268
+ ],
269
+ }
243
270
 
244
- # Decode based on protocol
245
- packets: list[ProtocolPacket] = []
246
- errors: list[dict[str, Any]] = []
247
271
 
248
- if detected_protocol == "uart":
272
+ def _dispatch_protocol_decode(
273
+ digital_trace: DigitalTrace,
274
+ protocol: str,
275
+ baud_rate: int | None,
276
+ parity: str,
277
+ stop_bits: int,
278
+ show_errors: bool,
279
+ results: dict[str, Any],
280
+ ) -> tuple[list[ProtocolPacket], list[dict[str, Any]]]:
281
+ """Dispatch to appropriate protocol decoder."""
282
+ if protocol == "uart":
249
283
  packets, errors, protocol_info = _decode_uart(
250
284
  digital_trace, baud_rate, parity, stop_bits, show_errors
251
285
  )
252
- results.update(protocol_info)
253
-
254
- elif detected_protocol == "spi":
286
+ elif protocol == "spi":
255
287
  packets, errors, protocol_info = _decode_spi(digital_trace, show_errors)
256
- results.update(protocol_info)
257
-
258
- elif detected_protocol == "i2c":
288
+ elif protocol == "i2c":
259
289
  packets, errors, protocol_info = _decode_i2c(digital_trace, show_errors)
260
- results.update(protocol_info)
261
-
262
- elif detected_protocol == "can":
290
+ elif protocol == "can":
263
291
  packets, errors, protocol_info = _decode_can(digital_trace, baud_rate, show_errors)
264
- results.update(protocol_info)
292
+ else:
293
+ packets, errors, protocol_info = [], [], {}
265
294
 
266
- # Filter to errors only if requested
267
- if show_errors:
268
- packets = [p for p in packets if p.errors]
295
+ results.update(protocol_info)
296
+ return packets, errors
269
297
 
270
- # Summarize results
298
+
299
+ def _add_packet_summary(
300
+ results: dict[str, Any],
301
+ packets: list[ProtocolPacket],
302
+ errors: list[dict[str, Any]],
303
+ ) -> None:
304
+ """Add packet summary and details to results."""
271
305
  results["packets_decoded"] = len(packets)
272
306
  results["errors_found"] = len(errors)
273
307
 
274
- # Add packet details
275
308
  results["packets"] = [
276
309
  {
277
310
  "index": i,
@@ -280,17 +313,14 @@ def _perform_decoding(
280
313
  "errors": p.errors,
281
314
  **{k: v for k, v in (p.annotations or {}).items() if k != "data_bits"},
282
315
  }
283
- for i, p in enumerate(packets[:100]) # Limit to first 100 packets
316
+ for i, p in enumerate(packets[:100])
284
317
  ]
285
318
 
286
319
  if len(packets) > 100:
287
320
  results["note"] = f"Showing first 100 of {len(packets)} packets"
288
321
 
289
- # Add error details if any
290
322
  if errors:
291
- results["error_details"] = errors[:20] # Limit to first 20 errors
292
-
293
- return results
323
+ results["error_details"] = errors[:20]
294
324
 
295
325
 
296
326
  def _decode_uart(
@@ -498,29 +528,60 @@ def _decode_can(
498
528
  """
499
529
  from oscura.analyzers.protocols.can import CANDecoder
500
530
 
501
- # Try common CAN baud rates if not specified
502
531
  if baud_rate is None:
503
- common_rates = [500000, 250000, 125000, 1000000]
504
- best_rate = 500000
505
- max_packets = 0
506
-
507
- for rate in common_rates:
508
- try:
509
- decoder = CANDecoder(bitrate=rate)
510
- test_packets = list(decoder.decode(trace))
511
- if len(test_packets) > max_packets:
512
- max_packets = len(test_packets)
513
- best_rate = rate
514
- except Exception:
515
- continue
516
-
517
- baud_rate = best_rate
532
+ baud_rate = _detect_can_baud_rate(trace)
518
533
 
519
534
  decoder = CANDecoder(bitrate=baud_rate)
520
535
  packets = list(decoder.decode(trace))
521
- errors = []
522
536
 
537
+ errors, arbitration_ids = _extract_can_errors(packets)
538
+ protocol_info = _build_can_protocol_info(baud_rate, packets, arbitration_ids)
539
+
540
+ return packets, errors, protocol_info
541
+
542
+
543
+ def _detect_can_baud_rate(trace: DigitalTrace) -> int:
544
+ """Detect CAN baud rate by trying common rates.
545
+
546
+ Args:
547
+ trace: Digital trace to analyze.
548
+
549
+ Returns:
550
+ Best matching baud rate.
551
+ """
552
+ from oscura.analyzers.protocols.can import CANDecoder
553
+
554
+ common_rates = [500000, 250000, 125000, 1000000]
555
+ best_rate = 500000
556
+ max_packets = 0
557
+
558
+ for rate in common_rates:
559
+ try:
560
+ decoder = CANDecoder(bitrate=rate)
561
+ test_packets = list(decoder.decode(trace))
562
+ if len(test_packets) > max_packets:
563
+ max_packets = len(test_packets)
564
+ best_rate = rate
565
+ except Exception:
566
+ continue
567
+
568
+ return best_rate
569
+
570
+
571
+ def _extract_can_errors(
572
+ packets: list[ProtocolPacket],
573
+ ) -> tuple[list[dict[str, Any]], set[int]]:
574
+ """Extract errors and arbitration IDs from CAN packets.
575
+
576
+ Args:
577
+ packets: Decoded packets.
578
+
579
+ Returns:
580
+ Tuple of (errors list, arbitration ID set).
581
+ """
582
+ errors = []
523
583
  arbitration_ids: set[int] = set()
584
+
524
585
  for i, pkt in enumerate(packets):
525
586
  arb_id = pkt.annotations.get("arbitration_id", 0) if pkt.annotations else 0
526
587
  arbitration_ids.add(arb_id)
@@ -536,16 +597,32 @@ def _decode_can(
536
597
  }
537
598
  )
538
599
 
539
- protocol_info = {
600
+ return errors, arbitration_ids
601
+
602
+
603
+ def _build_can_protocol_info(
604
+ baud_rate: int, packets: list[ProtocolPacket], arbitration_ids: set[int]
605
+ ) -> dict[str, Any]:
606
+ """Build CAN protocol info dictionary.
607
+
608
+ Args:
609
+ baud_rate: Detected baud rate.
610
+ packets: Decoded packets.
611
+ arbitration_ids: Set of arbitration IDs.
612
+
613
+ Returns:
614
+ Protocol info dictionary.
615
+ """
616
+ extended_count = sum(1 for p in packets if p.annotations and p.annotations.get("is_extended"))
617
+
618
+ info = {
540
619
  "bit_rate": f"{baud_rate / 1000:.0f} kbps",
541
620
  "messages": len(packets),
542
621
  "arbitration_ids": [f"0x{a:03X}" for a in sorted(arbitration_ids)[:10]],
543
- "extended_frames": sum(
544
- 1 for p in packets if p.annotations and p.annotations.get("is_extended")
545
- ),
622
+ "extended_frames": extended_count,
546
623
  }
547
624
 
548
625
  if len(arbitration_ids) > 10:
549
- protocol_info["note"] = f"Showing first 10 of {len(arbitration_ids)} arbitration IDs"
626
+ info["note"] = f"Showing first 10 of {len(arbitration_ids)} arbitration IDs"
550
627
 
551
- return packets, errors, protocol_info
628
+ return info
oscura/cli/export.py ADDED
@@ -0,0 +1,286 @@
1
+ """Oscura Export Command - Export Analysis Results.
2
+
3
+ Provides CLI for exporting analysis sessions and results to various formats.
4
+
5
+
6
+ Example:
7
+ $ oscura export json session.tks --output results.json
8
+ $ oscura export html session.tks --output report.html
9
+ $ oscura export wireshark session.tks --output dissector.lua
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ import click
19
+
20
+ logger = logging.getLogger("oscura.cli.export")
21
+
22
+
23
+ @click.command()
24
+ @click.argument(
25
+ "format",
26
+ type=click.Choice(
27
+ ["json", "html", "csv", "matlab", "wireshark", "scapy", "kaitai"], case_sensitive=False
28
+ ),
29
+ )
30
+ @click.argument("session", type=click.Path(exists=True))
31
+ @click.option(
32
+ "--output",
33
+ "-o",
34
+ type=click.Path(),
35
+ required=True,
36
+ help="Output file path.",
37
+ )
38
+ @click.option(
39
+ "--include-traces",
40
+ is_flag=True,
41
+ default=True,
42
+ help="Include trace data in export (default: True).",
43
+ )
44
+ @click.pass_context
45
+ def export(
46
+ ctx: click.Context,
47
+ format: str,
48
+ session: str,
49
+ output: str,
50
+ include_traces: bool,
51
+ ) -> None:
52
+ """Export analysis session to various formats.
53
+
54
+ Supports exporting to JSON, HTML, CSV, MATLAB, Wireshark dissector,
55
+ Scapy layer, and Kaitai struct formats.
56
+
57
+ Args:
58
+ ctx: Click context object.
59
+ format: Export format.
60
+ session: Path to session file (.tks).
61
+ output: Output file path.
62
+ include_traces: Include trace data in export.
63
+
64
+ Examples:
65
+
66
+ \b
67
+ # Export to JSON
68
+ $ oscura export json session.tks --output results.json
69
+
70
+ \b
71
+ # Generate HTML report
72
+ $ oscura export html session.tks --output report.html
73
+
74
+ \b
75
+ # Generate Wireshark dissector
76
+ $ oscura export wireshark session.tks --output protocol.lua
77
+ """
78
+ verbose = ctx.obj.get("verbose", 0)
79
+
80
+ if verbose:
81
+ logger.info(f"Exporting session {session} to {format}")
82
+
83
+ try:
84
+ # Note: Session loading functionality has been redesigned
85
+ # For now, this is a placeholder - session export needs to be reimplemented
86
+ # using the new AnalysisSession architecture
87
+ raise NotImplementedError(
88
+ "Session export has been redesigned. Use the new AnalysisSession API instead."
89
+ )
90
+
91
+ except Exception as e:
92
+ logger.error(f"Export failed: {e}")
93
+ if verbose > 1:
94
+ raise
95
+ click.echo(f"Error: {e}", err=True)
96
+ ctx.exit(1)
97
+
98
+
99
+ def _export_json(session: Any, output_path: Path, include_traces: bool) -> None:
100
+ """Export session to JSON.
101
+
102
+ Args:
103
+ session: Session object.
104
+ output_path: Output file path.
105
+ include_traces: Include trace data.
106
+ """
107
+ import json
108
+
109
+ data = session._to_dict(include_traces=include_traces)
110
+
111
+ with open(output_path, "w") as f:
112
+ json.dump(data, f, indent=2, default=str)
113
+
114
+
115
+ def _export_html(session: Any, output_path: Path) -> None:
116
+ """Export session to HTML report.
117
+
118
+ Args:
119
+ session: Session object.
120
+ output_path: Output file path.
121
+ """
122
+ html_content = f"""<!DOCTYPE html>
123
+ <html>
124
+ <head>
125
+ <meta charset='utf-8'>
126
+ <title>Oscura Analysis Report - {session.name}</title>
127
+ <style>
128
+ body {{ font-family: Arial, sans-serif; margin: 20px; }}
129
+ h1 {{ color: #4CAF50; }}
130
+ table {{ border-collapse: collapse; width: 100%; margin: 20px 0; }}
131
+ th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
132
+ th {{ background-color: #4CAF50; color: white; }}
133
+ tr:nth-child(even) {{ background-color: #f2f2f2; }}
134
+ .metadata {{ background: #f9f9f9; padding: 10px; margin: 10px 0; }}
135
+ </style>
136
+ </head>
137
+ <body>
138
+ <h1>Oscura Analysis Report</h1>
139
+ <div class="metadata">
140
+ <h2>Session: {session.name}</h2>
141
+ <p>Created: {session.created_at}</p>
142
+ <p>Modified: {session.modified_at}</p>
143
+ </div>
144
+
145
+ <h2>Traces ({len(session.traces)})</h2>
146
+ <ul>
147
+ """
148
+
149
+ for trace_name in session.list_traces():
150
+ html_content += f" <li>{trace_name}</li>\n"
151
+
152
+ html_content += """ </ul>
153
+
154
+ <h2>Measurements</h2>
155
+ <table>
156
+ <tr><th>Measurement</th><th>Value</th><th>Unit</th></tr>
157
+ """
158
+
159
+ for name, meas in session.get_measurements().items():
160
+ value = meas.get("value", "N/A")
161
+ unit = meas.get("unit", "")
162
+ html_content += f" <tr><td>{name}</td><td>{value}</td><td>{unit}</td></tr>\n"
163
+
164
+ html_content += """ </table>
165
+ </body>
166
+ </html>
167
+ """
168
+
169
+ with open(output_path, "w") as f:
170
+ f.write(html_content)
171
+
172
+
173
+ def _export_csv(session: Any, output_path: Path) -> None:
174
+ """Export measurements to CSV.
175
+
176
+ Args:
177
+ session: Session object.
178
+ output_path: Output file path.
179
+ """
180
+ import csv
181
+
182
+ with open(output_path, "w", newline="") as f:
183
+ writer = csv.writer(f)
184
+ writer.writerow(["Measurement", "Value", "Unit", "Trace"])
185
+
186
+ for name, meas in session.get_measurements().items():
187
+ writer.writerow(
188
+ [
189
+ name,
190
+ meas.get("value", ""),
191
+ meas.get("unit", ""),
192
+ meas.get("trace", ""),
193
+ ]
194
+ )
195
+
196
+
197
+ def _export_matlab(session: Any, output_path: Path) -> None:
198
+ """Export to MATLAB format.
199
+
200
+ Args:
201
+ session: Session object.
202
+ output_path: Output file path.
203
+ """
204
+ # MATLAB export has been redesigned - use new AnalysisSession API
205
+ raise NotImplementedError("MATLAB export needs reimplementation with new API")
206
+
207
+
208
+ def _export_wireshark(session: Any, output_path: Path) -> None:
209
+ """Export to Wireshark dissector.
210
+
211
+ Args:
212
+ session: Session object.
213
+ output_path: Output file path.
214
+ """
215
+ # Placeholder - requires protocol specification
216
+ lua_template = """-- Wireshark Dissector for Unknown Protocol
217
+ -- Generated by Oscura
218
+
219
+ local proto = Proto("unknown", "Unknown Protocol")
220
+
221
+ function proto.dissector(buffer, pinfo, tree)
222
+ pinfo.cols.protocol = "UNKNOWN"
223
+ local subtree = tree:add(proto, buffer(), "Unknown Protocol Data")
224
+ subtree:add(buffer(0), "Raw data: " .. buffer():bytes():tohex())
225
+ end
226
+
227
+ -- Register dissector
228
+ local udp_table = DissectorTable.get("udp.port")
229
+ -- udp_table:add(YOUR_PORT, proto)
230
+ """
231
+
232
+ with open(output_path, "w") as f:
233
+ f.write(lua_template)
234
+
235
+
236
+ def _export_scapy(session: Any, output_path: Path) -> None:
237
+ """Export to Scapy layer.
238
+
239
+ Args:
240
+ session: Session object.
241
+ output_path: Output file path.
242
+ """
243
+ # Placeholder - requires protocol specification
244
+ scapy_template = """# Scapy Layer for Unknown Protocol
245
+ # Generated by Oscura
246
+
247
+ from scapy.all import *
248
+
249
+ class UnknownProtocol(Packet):
250
+ name = "UnknownProtocol"
251
+ fields_desc = [
252
+ # Define fields here
253
+ XByteField("data", 0x00),
254
+ ]
255
+
256
+ # Bind to lower layer if needed
257
+ # bind_layers(UDP, UnknownProtocol, dport=YOUR_PORT)
258
+ """
259
+
260
+ with open(output_path, "w") as f:
261
+ f.write(scapy_template)
262
+
263
+
264
+ def _export_kaitai(session: Any, output_path: Path) -> None:
265
+ """Export to Kaitai struct.
266
+
267
+ Args:
268
+ session: Session object.
269
+ output_path: Output file path.
270
+ """
271
+ # Placeholder - requires protocol specification
272
+ kaitai_template = """meta:
273
+ id: unknown_protocol
274
+ title: Unknown Protocol
275
+ file-extension: dat
276
+ endian: le
277
+
278
+ seq:
279
+ - id: header
280
+ type: u1
281
+ - id: data
282
+ size-eos: true
283
+ """
284
+
285
+ with open(output_path, "w") as f:
286
+ f.write(kaitai_template)