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/main.py CHANGED
@@ -1,19 +1,23 @@
1
- """Oscura Core CLI Framework implementing CLI-001.
1
+ """Oscura Core CLI Framework implementing CLI-001 (Enhanced Edition).
2
2
 
3
3
  Provides the main entry point for the oscura command-line interface with
4
- support for multiple output formats and verbose logging.
4
+ comprehensive subcommands, interactive mode, batch processing, configuration
5
+ management, and shell completion support.
5
6
 
6
7
 
7
8
  Example:
8
9
  $ oscura --help
9
- $ oscura characterize signal.wfm --output json
10
- $ oscura decode uart.wfm -vv
10
+ $ oscura analyze signal.wfm --output json
11
+ $ oscura decode uart capture.wfm --baud-rate 115200
11
12
  $ oscura shell # Interactive REPL
13
+ $ oscura config --show # View configuration
14
+ $ oscura plugins list # Manage plugins
12
15
  """
13
16
 
14
17
  import json
15
18
  import logging
16
19
  import sys
20
+ from pathlib import Path
17
21
  from typing import Any
18
22
 
19
23
  import click
@@ -126,37 +130,98 @@ def format_output(data: dict[str, Any], format_type: str) -> str:
126
130
  return formatter(data)
127
131
 
128
132
 
129
- @click.group() # type: ignore[misc]
130
- @click.option( # type: ignore[misc]
133
+ def load_config_file(config_path: Path | None = None) -> dict[str, Any]:
134
+ """Load configuration from YAML file.
135
+
136
+ Args:
137
+ config_path: Path to config file. If None, searches for config in:
138
+ 1. .oscura.yaml in current directory
139
+ 2. ~/.config/oscura/config.yaml
140
+
141
+ Returns:
142
+ Configuration dictionary.
143
+ """
144
+ import yaml
145
+
146
+ if config_path is None:
147
+ # Search for config files
148
+ candidates = [
149
+ Path(".oscura.yaml"),
150
+ Path.home() / ".config" / "oscura" / "config.yaml",
151
+ ]
152
+ for candidate in candidates:
153
+ if candidate.exists():
154
+ config_path = candidate
155
+ break
156
+
157
+ if config_path is None or not config_path.exists():
158
+ return {}
159
+
160
+ with open(config_path) as f:
161
+ return yaml.safe_load(f) or {}
162
+
163
+
164
+ @click.group()
165
+ @click.option(
131
166
  "-v",
132
167
  "--verbose",
133
168
  count=True,
134
169
  help="Increase verbosity (-v for INFO, -vv for DEBUG).",
135
170
  )
136
- @click.version_option(version="0.1.0", prog_name="oscura") # type: ignore[misc]
137
- @click.pass_context # type: ignore[misc]
138
- def cli(ctx: click.Context, verbose: int) -> None:
139
- """Oscura - Signal Analysis Framework for Oscilloscope Data.
140
-
141
- Command-line tools for characterizing buffers, decoding protocols,
142
- analyzing spectra, and comparing signals.
171
+ @click.option(
172
+ "--config",
173
+ type=click.Path(exists=True),
174
+ default=None,
175
+ help="Path to configuration file (YAML).",
176
+ )
177
+ @click.option(
178
+ "--quiet",
179
+ "-q",
180
+ is_flag=True,
181
+ help="Quiet mode (suppress non-error output).",
182
+ )
183
+ @click.option(
184
+ "--json",
185
+ "json_output",
186
+ is_flag=True,
187
+ help="JSON output mode for scripting.",
188
+ )
189
+ @click.version_option(prog_name="oscura") # Version auto-detected from package metadata
190
+ @click.pass_context
191
+ def cli(
192
+ ctx: click.Context,
193
+ verbose: int,
194
+ config: str | None,
195
+ quiet: bool,
196
+ json_output: bool,
197
+ ) -> None:
198
+ """Oscura - Hardware Reverse Engineering Framework.
199
+
200
+ Unified framework for extracting all information from hardware systems through
201
+ signals and data. Features unknown protocol discovery, state machine extraction,
202
+ CRC recovery, and security analysis.
143
203
 
144
204
  Args:
145
205
  ctx: Click context object.
146
206
  verbose: Verbosity level (0=WARNING, 1=INFO, 2+=DEBUG).
207
+ config: Path to configuration file.
208
+ quiet: Quiet mode flag.
209
+ json_output: JSON output mode flag.
147
210
 
148
211
  Examples:
149
- oscura characterize signal.wfm
212
+ oscura analyze signal.wfm
150
213
  oscura decode uart.wfm --protocol auto
151
214
  oscura batch '*.wfm' --analysis characterize
152
- oscura compare before.wfm after.wfm
215
+ oscura visualize trace.wfm
153
216
  oscura shell # Interactive REPL
154
217
  """
155
218
  # Ensure ctx.obj exists
156
219
  ctx.ensure_object(dict)
157
220
 
158
221
  # Set logging level based on verbosity
159
- if verbose == 0:
222
+ if quiet:
223
+ logger.setLevel(logging.ERROR)
224
+ elif verbose == 0:
160
225
  logger.setLevel(logging.WARNING)
161
226
  elif verbose == 1:
162
227
  logger.setLevel(logging.INFO)
@@ -166,9 +231,28 @@ def cli(ctx: click.Context, verbose: int) -> None:
166
231
  logger.debug("Debug mode enabled")
167
232
 
168
233
  ctx.obj["verbose"] = verbose
234
+ ctx.obj["quiet"] = quiet
235
+ ctx.obj["json_output"] = json_output
236
+
237
+ # Load configuration
238
+ config_path = Path(config) if config else None
239
+ ctx.obj["config"] = load_config_file(config_path)
169
240
 
170
241
 
171
- @click.command() # type: ignore[misc]
242
+ # Enhanced subcommands - imported below
243
+ from oscura.cli.analyze import analyze
244
+ from oscura.cli.batch import batch
245
+ from oscura.cli.benchmark import benchmark
246
+ from oscura.cli.characterize import characterize
247
+ from oscura.cli.compare import compare
248
+ from oscura.cli.config_cmd import config as config_cmd
249
+ from oscura.cli.decode import decode
250
+ from oscura.cli.export import export
251
+ from oscura.cli.validate_cmd import validate
252
+ from oscura.cli.visualize import visualize
253
+
254
+
255
+ @click.command()
172
256
  def shell() -> None:
173
257
  """Start an interactive Oscura shell.
174
258
 
@@ -177,7 +261,7 @@ def shell() -> None:
177
261
 
178
262
  Example:
179
263
  $ oscura shell
180
- Oscura Shell v0.1.0
264
+ Oscura Shell v0.6.0
181
265
  >>> trace = load("signal.wfm")
182
266
  >>> rise_time(trace)
183
267
  """
@@ -186,9 +270,9 @@ def shell() -> None:
186
270
  start_shell()
187
271
 
188
272
 
189
- @click.command() # type: ignore[misc]
190
- @click.argument("tutorial_id", required=False, default=None) # type: ignore[misc]
191
- @click.option("--list", "list_tutorials", is_flag=True, help="List available tutorials") # type: ignore[misc]
273
+ @click.command()
274
+ @click.argument("tutorial_id", required=False, default=None)
275
+ @click.option("--list", "list_tutorials", is_flag=True, help="List available tutorials")
192
276
  def tutorial(tutorial_id: str | None, list_tutorials: bool) -> None:
193
277
  """Run an interactive tutorial.
194
278
 
@@ -202,8 +286,8 @@ def tutorial(tutorial_id: str | None, list_tutorials: bool) -> None:
202
286
  oscura tutorial --list # List available tutorials
203
287
  oscura tutorial getting_started # Run the getting started tutorial
204
288
  """
205
- from oscura.onboarding import list_tutorials as list_tut
206
- from oscura.onboarding import run_tutorial
289
+ from oscura.cli.onboarding import list_tutorials as list_tut
290
+ from oscura.cli.onboarding import run_tutorial
207
291
 
208
292
  if list_tutorials or tutorial_id is None:
209
293
  tutorials = list_tut()
@@ -217,15 +301,15 @@ def tutorial(tutorial_id: str | None, list_tutorials: bool) -> None:
217
301
  run_tutorial(tutorial_id, interactive=True)
218
302
 
219
303
 
220
- # Import subcommands
221
- from oscura.cli.batch import batch # noqa: E402
222
- from oscura.cli.characterize import characterize # noqa: E402
223
- from oscura.cli.compare import compare # noqa: E402
224
- from oscura.cli.decode import decode # noqa: E402
225
-
226
- # Register subcommands
227
- cli.add_command(characterize) # type: ignore[has-type]
304
+ # Register all subcommands
305
+ cli.add_command(analyze) # type: ignore[has-type]
228
306
  cli.add_command(decode) # type: ignore[has-type]
307
+ cli.add_command(export)
308
+ cli.add_command(visualize)
309
+ cli.add_command(benchmark)
310
+ cli.add_command(validate)
311
+ cli.add_command(config_cmd, name="config")
312
+ cli.add_command(characterize) # type: ignore[has-type]
229
313
  cli.add_command(batch) # type: ignore[has-type]
230
314
  cli.add_command(compare) # type: ignore[has-type]
231
315
  cli.add_command(shell)
@@ -4,20 +4,20 @@ This package provides interactive tutorials, context-sensitive help,
4
4
  and guided analysis features for new users.
5
5
  """
6
6
 
7
- from oscura.onboarding.help import (
7
+ from oscura.cli.onboarding.help import (
8
8
  explain_result,
9
9
  get_example,
10
10
  get_help,
11
11
  suggest_commands,
12
12
  )
13
- from oscura.onboarding.tutorials import (
13
+ from oscura.cli.onboarding.tutorials import (
14
14
  Tutorial,
15
15
  TutorialStep,
16
16
  get_tutorial,
17
17
  list_tutorials,
18
18
  run_tutorial,
19
19
  )
20
- from oscura.onboarding.wizard import (
20
+ from oscura.cli.onboarding.wizard import (
21
21
  AnalysisWizard,
22
22
  WizardStep,
23
23
  run_wizard,
@@ -5,7 +5,7 @@ context, and result explanations for non-expert users.
5
5
 
6
6
 
7
7
  Example:
8
- >>> from oscura.onboarding import get_help, suggest_commands
8
+ >>> from oscura.cli.onboarding import get_help, suggest_commands
9
9
  >>> get_help("rise_time")
10
10
  >>> suggest_commands(trace)
11
11
  """
@@ -252,22 +252,7 @@ def suggest_commands(trace: Any = None, context: str | None = None) -> list[dict
252
252
  suggestions = []
253
253
 
254
254
  if trace is None:
255
- # No trace loaded - suggest loading
256
- suggestions.append(
257
- {
258
- "command": "trace = load('file.csv')",
259
- "description": "Load a trace file to get started",
260
- "reason": "No trace loaded yet",
261
- }
262
- )
263
- suggestions.append(
264
- {
265
- "command": "formats = get_supported_formats()",
266
- "description": "See what file formats are supported",
267
- "reason": "Helpful for knowing what files you can load",
268
- }
269
- )
270
- return suggestions
255
+ return _suggest_loading_commands()
271
256
 
272
257
  # Trace is loaded - suggest measurements
273
258
  suggestions.append(
@@ -278,7 +263,48 @@ def suggest_commands(trace: Any = None, context: str | None = None) -> list[dict
278
263
  }
279
264
  )
280
265
 
281
- # Check if it looks like digital signal
266
+ # Add signal type-specific suggestions
267
+ _add_signal_type_suggestions(suggestions, trace)
268
+
269
+ # Always suggest filtering for noisy signals
270
+ suggestions.append(
271
+ {
272
+ "command": "filtered = low_pass(trace, cutoff_hz)",
273
+ "description": "Apply low-pass filter to remove noise",
274
+ "reason": "Clean up high-frequency noise",
275
+ }
276
+ )
277
+
278
+ # Add context-specific suggestions
279
+ if context:
280
+ _add_context_suggestions(suggestions, context)
281
+
282
+ return suggestions
283
+
284
+
285
+ def _suggest_loading_commands() -> list[dict[str, str]]:
286
+ """Get suggestions when no trace is loaded."""
287
+ return [
288
+ {
289
+ "command": "trace = load('file.csv')",
290
+ "description": "Load a trace file to get started",
291
+ "reason": "No trace loaded yet",
292
+ },
293
+ {
294
+ "command": "formats = get_supported_formats()",
295
+ "description": "See what file formats are supported",
296
+ "reason": "Helpful for knowing what files you can load",
297
+ },
298
+ ]
299
+
300
+
301
+ def _add_signal_type_suggestions(suggestions: list[dict[str, str]], trace: Any) -> None:
302
+ """Add signal type-specific suggestions.
303
+
304
+ Args:
305
+ suggestions: List to append suggestions to.
306
+ trace: Trace object to analyze.
307
+ """
282
308
  if hasattr(trace, "data"):
283
309
  import numpy as np
284
310
 
@@ -318,47 +344,43 @@ def suggest_commands(trace: Any = None, context: str | None = None) -> list[dict
318
344
  }
319
345
  )
320
346
 
321
- # Always suggest filtering for noisy signals
322
- suggestions.append(
323
- {
324
- "command": "filtered = low_pass(trace, cutoff_hz)",
325
- "description": "Apply low-pass filter to remove noise",
326
- "reason": "Clean up high-frequency noise",
327
- }
328
- )
329
347
 
330
- # Context-specific suggestions
331
- if context:
332
- context_lower = context.lower()
333
- if "uart" in context_lower or "serial" in context_lower:
334
- suggestions.insert(
335
- 0,
336
- {
337
- "command": "packets = decode_uart(trace)",
338
- "description": "Decode UART serial data",
339
- "reason": "You mentioned UART/serial",
340
- },
341
- )
342
- elif "spi" in context_lower:
343
- suggestions.insert(
344
- 0,
345
- {
346
- "command": "packets = decode_spi(clk_trace, data_trace)",
347
- "description": "Decode SPI bus",
348
- "reason": "You mentioned SPI",
349
- },
350
- )
351
- elif "i2c" in context_lower:
352
- suggestions.insert(
353
- 0,
354
- {
355
- "command": "packets = decode_i2c(scl_trace, sda_trace)",
356
- "description": "Decode I2C bus",
357
- "reason": "You mentioned I2C",
358
- },
359
- )
348
+ def _add_context_suggestions(suggestions: list[dict[str, str]], context: str) -> None:
349
+ """Add context-specific suggestions based on user intent.
360
350
 
361
- return suggestions
351
+ Args:
352
+ suggestions: List to prepend suggestions to.
353
+ context: User context string.
354
+ """
355
+ context_lower = context.lower()
356
+
357
+ if "uart" in context_lower or "serial" in context_lower:
358
+ suggestions.insert(
359
+ 0,
360
+ {
361
+ "command": "packets = decode_uart(trace)",
362
+ "description": "Decode UART serial data",
363
+ "reason": "You mentioned UART/serial",
364
+ },
365
+ )
366
+ elif "spi" in context_lower:
367
+ suggestions.insert(
368
+ 0,
369
+ {
370
+ "command": "packets = decode_spi(clk_trace, data_trace)",
371
+ "description": "Decode SPI bus",
372
+ "reason": "You mentioned SPI",
373
+ },
374
+ )
375
+ elif "i2c" in context_lower:
376
+ suggestions.insert(
377
+ 0,
378
+ {
379
+ "command": "packets = decode_i2c(scl_trace, sda_trace)",
380
+ "description": "Decode I2C bus",
381
+ "reason": "You mentioned I2C",
382
+ },
383
+ )
362
384
 
363
385
 
364
386
  def explain_result(
@@ -389,7 +411,7 @@ def explain_result(
389
411
  }
390
412
 
391
413
  if measurement.lower() in explanations:
392
- return explanations[measurement.lower()](value) # type: ignore[no-untyped-call]
414
+ return explanations[measurement.lower()](value)
393
415
 
394
416
  # Generic explanation
395
417
  return f"{measurement}: {value}"
@@ -9,7 +9,7 @@ covering common analysis workflows.
9
9
  - Progress tracking
10
10
 
11
11
  Example:
12
- >>> from oscura.onboarding import run_tutorial
12
+ >>> from oscura.cli.onboarding import run_tutorial
13
13
  >>> run_tutorial("getting_started")
14
14
  Welcome to Oscura!
15
15
  Step 1/5: Loading a trace file
@@ -71,16 +71,47 @@ TUTORIALS: dict[str, Tutorial] = {}
71
71
  def _register_getting_started() -> None:
72
72
  """Register the getting started tutorial."""
73
73
  steps = [
74
- TutorialStep(
75
- title="Loading a Trace File",
76
- description="""
74
+ _create_loading_step(),
75
+ _create_measurements_step(),
76
+ _create_spectral_step(),
77
+ _create_protocol_step(),
78
+ _create_discovery_step(),
79
+ ]
80
+
81
+ tutorial = Tutorial(
82
+ id="getting_started",
83
+ title="Getting Started with Oscura",
84
+ description="""
85
+ Welcome to Oscura! This tutorial will teach you the basics of
86
+ signal analysis in 5 easy steps:
87
+
88
+ 1. Loading trace files
89
+ 2. Making basic measurements
90
+ 3. Spectral analysis
91
+ 4. Protocol decoding
92
+ 5. Auto-discovery
93
+
94
+ No prior signal analysis experience required!
95
+ """,
96
+ steps=steps,
97
+ difficulty="beginner",
98
+ )
99
+
100
+ TUTORIALS[tutorial.id] = tutorial
101
+
102
+
103
+ def _create_loading_step() -> TutorialStep:
104
+ """Create loading trace file tutorial step."""
105
+ return TutorialStep(
106
+ title="Loading a Trace File",
107
+ description="""
77
108
  Oscura can load waveform data from many file formats.
78
109
  The simplest way is to use the load() function, which auto-detects the format.
79
110
 
80
111
  Think of a trace like a recording of an electrical signal over time -
81
112
  similar to how an audio file stores sound waves.
82
113
  """,
83
- code="""
114
+ code="""
84
115
  import oscura as osc
85
116
 
86
117
  # Load a waveform file (replace with your file path)
@@ -90,15 +121,19 @@ trace = osc.load("signal.csv")
90
121
  print(f"Loaded {len(trace.data)} samples")
91
122
  print(f"Sample rate: {trace.metadata.sample_rate} Hz")
92
123
  """,
93
- expected_output="Loaded 10000 samples\nSample rate: 1000000.0 Hz",
94
- hints=[
95
- "Try loading a CSV file with two columns: time and voltage",
96
- "Supported formats: .csv, .wfm, .npz, .hdf5, and more",
97
- ],
98
- ),
99
- TutorialStep(
100
- title="Making Basic Measurements",
101
- description="""
124
+ expected_output="Loaded 10000 samples\nSample rate: 1000000.0 Hz",
125
+ hints=[
126
+ "Try loading a CSV file with two columns: time and voltage",
127
+ "Supported formats: .csv, .wfm, .npz, .hdf5, and more",
128
+ ],
129
+ )
130
+
131
+
132
+ def _create_measurements_step() -> TutorialStep:
133
+ """Create measurements tutorial step."""
134
+ return TutorialStep(
135
+ title="Making Basic Measurements",
136
+ description="""
102
137
  Once you have a trace, you can measure things like:
103
138
  - Rise time: How fast a signal goes from low to high
104
139
  - Frequency: How many times per second the signal repeats
@@ -106,7 +141,7 @@ Once you have a trace, you can measure things like:
106
141
 
107
142
  These are the same measurements an oscilloscope would show you!
108
143
  """,
109
- code="""
144
+ code="""
110
145
  import oscura as osc
111
146
 
112
147
  trace = osc.load("signal.csv")
@@ -124,15 +159,19 @@ results = osc.measure(trace)
124
159
  for name, value in results.items():
125
160
  print(f"{name}: {value}")
126
161
  """,
127
- expected_output="Rise time: 2.50 nanoseconds\nFrequency: 10.00 MHz",
128
- hints=[
129
- "rise_time() measures 10%-90% transition by default",
130
- "Use measure() to get all measurements in one call",
131
- ],
132
- ),
133
- TutorialStep(
134
- title="Spectral Analysis (Frequency Domain)",
135
- description="""
162
+ expected_output="Rise time: 2.50 nanoseconds\nFrequency: 10.00 MHz",
163
+ hints=[
164
+ "rise_time() measures 10%-90% transition by default",
165
+ "Use measure() to get all measurements in one call",
166
+ ],
167
+ )
168
+
169
+
170
+ def _create_spectral_step() -> TutorialStep:
171
+ """Create spectral analysis tutorial step."""
172
+ return TutorialStep(
173
+ title="Spectral Analysis (Frequency Domain)",
174
+ description="""
136
175
  Spectral analysis shows you what frequencies are present in your signal.
137
176
  This is useful for:
138
177
  - Finding the main frequency of a clock signal
@@ -141,7 +180,7 @@ This is useful for:
141
180
 
142
181
  It's like looking at a music equalizer that shows bass, mid, and treble!
143
182
  """,
144
- code="""
183
+ code="""
145
184
  import oscura as osc
146
185
 
147
186
  trace = osc.load("signal.csv")
@@ -160,21 +199,25 @@ snr_value = osc.snr(trace)
160
199
  print(f"THD: {thd_value:.1f} dB")
161
200
  print(f"SNR: {snr_value:.1f} dB")
162
201
  """,
163
- expected_output="Dominant frequency: 10.00 MHz\nTHD: -45.2 dB\nSNR: 52.3 dB",
164
- hints=[
165
- "THD (Total Harmonic Distortion) should be negative in dB - more negative is better",
166
- "SNR (Signal-to-Noise Ratio) should be positive - higher is better",
167
- ],
168
- ),
169
- TutorialStep(
170
- title="Protocol Decoding",
171
- description="""
202
+ expected_output="Dominant frequency: 10.00 MHz\nTHD: -45.2 dB\nSNR: 52.3 dB",
203
+ hints=[
204
+ "THD (Total Harmonic Distortion) should be negative in dB - more negative is better",
205
+ "SNR (Signal-to-Noise Ratio) should be positive - higher is better",
206
+ ],
207
+ )
208
+
209
+
210
+ def _create_protocol_step() -> TutorialStep:
211
+ """Create protocol decoding tutorial step."""
212
+ return TutorialStep(
213
+ title="Protocol Decoding",
214
+ description="""
172
215
  If your signal is a digital communication protocol like UART, SPI, or I2C,
173
216
  Oscura can decode it to show you the actual data being transmitted.
174
217
 
175
218
  Think of it like translating Morse code back into text!
176
219
  """,
177
- code="""
220
+ code="""
178
221
  import oscura as osc
179
222
 
180
223
  # Load a UART signal
@@ -188,15 +231,19 @@ packets = decode_uart(trace)
188
231
  for pkt in packets[:5]: # First 5 packets
189
232
  print(f"Time: {pkt.timestamp:.6f}s, Data: 0x{pkt.data:02X} ('{chr(pkt.data)}')")
190
233
  """,
191
- expected_output="Time: 0.000001s, Data: 0x48 ('H')\nTime: 0.000086s, Data: 0x65 ('e')",
192
- hints=[
193
- "UART baud rate is auto-detected by default",
194
- "Supported protocols: UART, SPI, I2C, CAN, and many more",
195
- ],
196
- ),
197
- TutorialStep(
198
- title="Auto-Discovery for Beginners",
199
- description="""
234
+ expected_output="Time: 0.000001s, Data: 0x48 ('H')\nTime: 0.000086s, Data: 0x65 ('e')",
235
+ hints=[
236
+ "UART baud rate is auto-detected by default",
237
+ "Supported protocols: UART, SPI, I2C, CAN, and many more",
238
+ ],
239
+ )
240
+
241
+
242
+ def _create_discovery_step() -> TutorialStep:
243
+ """Create auto-discovery tutorial step."""
244
+ return TutorialStep(
245
+ title="Auto-Discovery for Beginners",
246
+ description="""
200
247
  Not sure what your signal is? Oscura can analyze it automatically!
201
248
 
202
249
  The characterize_signal() function examines your trace and tells you:
@@ -206,7 +253,7 @@ The characterize_signal() function examines your trace and tells you:
206
253
 
207
254
  It's like having an expert look at your signal and give you hints!
208
255
  """,
209
- code="""
256
+ code="""
210
257
  import oscura as osc
211
258
  from oscura.discovery import characterize_signal
212
259
 
@@ -226,35 +273,13 @@ else:
226
273
  for alt in result.alternatives:
227
274
  print(f" - {alt.signal_type}: {alt.confidence:.1%}")
228
275
  """,
229
- expected_output="Signal type: digital\nConfidence: 94.0%",
230
- hints=[
231
- "Confidence >= 80% means high confidence in the detection",
232
- "Low confidence? Check the alternatives for other possibilities",
233
- ],
234
- ),
235
- ]
236
-
237
- tutorial = Tutorial(
238
- id="getting_started",
239
- title="Getting Started with Oscura",
240
- description="""
241
- Welcome to Oscura! This tutorial will teach you the basics of
242
- signal analysis in 5 easy steps:
243
-
244
- 1. Loading trace files
245
- 2. Making basic measurements
246
- 3. Spectral analysis
247
- 4. Protocol decoding
248
- 5. Auto-discovery
249
-
250
- No prior signal analysis experience required!
251
- """,
252
- steps=steps,
253
- difficulty="beginner",
276
+ expected_output="Signal type: digital\nConfidence: 94.0%",
277
+ hints=[
278
+ "Confidence >= 80% means high confidence in the detection",
279
+ "Low confidence? Check the alternatives for other possibilities",
280
+ ],
254
281
  )
255
282
 
256
- TUTORIALS[tutorial.id] = tutorial
257
-
258
283
 
259
284
  def _register_spectral_analysis() -> None:
260
285
  """Register the spectral analysis tutorial."""