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/api/__init__.py CHANGED
@@ -1,15 +1,24 @@
1
1
  """Expert API module for Oscura.
2
2
 
3
3
  This module provides advanced APIs for power users including DSL,
4
- fluent interfaces, performance profiling, and advanced workflow control.
4
+ fluent interfaces, performance profiling, REST API server, and advanced workflow control.
5
5
  """
6
6
 
7
7
  from oscura.api.dsl import (
8
- DSLExpression,
9
- DSLParser,
10
- analyze,
11
- parse_expression,
8
+ Expression,
12
9
  )
10
+ from oscura.api.dsl import (
11
+ Interpreter as DSLParser,
12
+ )
13
+ from oscura.api.dsl import (
14
+ execute_dsl as analyze,
15
+ )
16
+ from oscura.api.dsl import (
17
+ parse_dsl as parse_expression,
18
+ )
19
+
20
+ # Alias for backward compatibility
21
+ DSLExpression = Expression
13
22
  from oscura.api.fluent import (
14
23
  FluentResult,
15
24
  FluentTrace,
@@ -33,11 +42,24 @@ from oscura.api.profiling import (
33
42
  ProfileReport,
34
43
  profile,
35
44
  )
45
+ from oscura.api.rest_server import (
46
+ AnalysisRequest,
47
+ AnalysisResponse,
48
+ ErrorResponse,
49
+ ProtocolResponse,
50
+ RESTAPIServer,
51
+ SessionManager,
52
+ SessionResponse,
53
+ )
36
54
 
37
55
  __all__ = [
56
+ # REST API Server
57
+ "AnalysisRequest",
58
+ "AnalysisResponse",
38
59
  # DSL (API-010)
39
60
  "DSLExpression",
40
61
  "DSLParser",
62
+ "ErrorResponse",
41
63
  # Fluent (API-019)
42
64
  "FluentResult",
43
65
  "FluentTrace",
@@ -49,6 +71,10 @@ __all__ = [
49
71
  "ParameterSpace",
50
72
  "ProfileReport",
51
73
  "Profiler",
74
+ "ProtocolResponse",
75
+ "RESTAPIServer",
76
+ "SessionManager",
77
+ "SessionResponse",
52
78
  # Operators (API-015, API-016, API-018)
53
79
  "TimeIndex",
54
80
  "UnitConverter",
@@ -0,0 +1,582 @@
1
+ """Oscura DSL - Domain-Specific Language for trace analysis.
2
+
3
+ Provides a simple, declarative language for defining trace analysis workflows.
4
+
5
+ Example usage:
6
+ ```python
7
+ from oscura.api.dsl import execute_dsl
8
+
9
+ # Execute DSL script
10
+ script = '''
11
+ $data = load "capture.csv"
12
+ $filtered = $data | filter lowpass 1000
13
+ $rise = $filtered | measure rise_time
14
+ '''
15
+
16
+ env = execute_dsl(script)
17
+ print(f"Rise time: {env['$rise']}")
18
+ ```
19
+
20
+ Or start interactive REPL:
21
+ ```python
22
+ from oscura.api.dsl import start_repl
23
+ start_repl()
24
+ ```
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from dataclasses import dataclass, field
30
+ from typing import TYPE_CHECKING, Any
31
+
32
+ if TYPE_CHECKING:
33
+ import numpy as np
34
+
35
+ from oscura.api.dsl.commands import BUILTIN_COMMANDS
36
+ from oscura.api.dsl.interpreter import Interpreter, InterpreterError, execute_dsl
37
+ from oscura.api.dsl.parser import (
38
+ Assignment,
39
+ Command,
40
+ Expression,
41
+ ForLoop,
42
+ FunctionCall,
43
+ Lexer,
44
+ Literal,
45
+ Parser,
46
+ Pipeline,
47
+ Statement,
48
+ Token,
49
+ TokenType,
50
+ Variable,
51
+ parse_dsl,
52
+ )
53
+ from oscura.api.dsl.repl import REPL, start_repl
54
+
55
+
56
+ # Legacy API dataclass for backwards compatibility
57
+ @dataclass
58
+ class DSLExpression:
59
+ """Legacy DSL expression dataclass for backwards compatibility.
60
+
61
+ Represents a single DSL operation with arguments and optional chaining.
62
+
63
+ Attributes:
64
+ operation: Operation name (e.g., "fft", "lowpass", "mean").
65
+ args: Positional arguments.
66
+ kwargs: Keyword arguments.
67
+ chain: Optional chained expression to execute after this one.
68
+ """
69
+
70
+ operation: str
71
+ args: list[Any] = field(default_factory=list)
72
+ kwargs: dict[str, Any] = field(default_factory=dict)
73
+ chain: DSLExpression | None = None
74
+
75
+ def to_dict(self) -> dict[str, Any]:
76
+ """Convert expression to dictionary representation.
77
+
78
+ Returns:
79
+ Dictionary with operation, args, kwargs, and optionally chain.
80
+ """
81
+ result: dict[str, Any] = {
82
+ "operation": self.operation,
83
+ "args": self.args,
84
+ "kwargs": self.kwargs,
85
+ }
86
+ if self.chain is not None:
87
+ result["chain"] = self.chain.to_dict()
88
+ return result
89
+
90
+
91
+ # Legacy API parser class
92
+ class DSLParser:
93
+ """Legacy parser class for backwards compatibility.
94
+
95
+ Parses DSL expression strings into DSLExpression objects.
96
+ """
97
+
98
+ def __init__(self) -> None:
99
+ """Initialize parser state."""
100
+ self._pos = 0
101
+ self._text = ""
102
+
103
+ def parse(self, text: str) -> DSLExpression:
104
+ """Parse DSL expression string.
105
+
106
+ Args:
107
+ text: Expression string to parse.
108
+
109
+ Returns:
110
+ Parsed DSLExpression.
111
+
112
+ Raises:
113
+ ValueError: If expression is invalid.
114
+ """
115
+ self._text = text.strip()
116
+ self._pos = 0
117
+
118
+ if not self._text:
119
+ raise ValueError("Expected identifier")
120
+
121
+ return self._parse_chain()
122
+
123
+ def _parse_chain(self) -> DSLExpression:
124
+ """Parse potentially chained expressions."""
125
+ expr = self._parse_operation()
126
+
127
+ # Check for pipe operator
128
+ self._skip_whitespace()
129
+ if self._peek() == "|":
130
+ self._advance() # consume |
131
+ chain = self._parse_chain()
132
+ expr.chain = chain
133
+
134
+ return expr
135
+
136
+ def _parse_operation(self) -> DSLExpression:
137
+ """Parse a single operation."""
138
+ self._skip_whitespace()
139
+
140
+ # Parse operation name
141
+ operation = self._parse_identifier()
142
+ if not operation:
143
+ raise ValueError("Expected identifier")
144
+
145
+ # Validate operation
146
+ valid_ops = {
147
+ "mean",
148
+ "std",
149
+ "min",
150
+ "max",
151
+ "rms",
152
+ "fft",
153
+ "psd",
154
+ "lowpass",
155
+ "highpass",
156
+ "bandpass",
157
+ "normalize",
158
+ "slice",
159
+ "resample",
160
+ "load",
161
+ "filter",
162
+ }
163
+ if operation not in valid_ops:
164
+ raise ValueError(f"Unknown operation: {operation}")
165
+
166
+ # Parse arguments if present
167
+ args: list[Any] = []
168
+ kwargs: dict[str, Any] = {}
169
+
170
+ self._skip_whitespace()
171
+ if self._peek() == "(":
172
+ self._advance() # consume (
173
+ args, kwargs = self._parse_args()
174
+ if self._peek() != ")":
175
+ raise ValueError("Expected ')'")
176
+ self._advance() # consume )
177
+
178
+ return DSLExpression(operation=operation, args=args, kwargs=kwargs)
179
+
180
+ def _parse_args(self) -> tuple[list[Any], dict[str, Any]]:
181
+ """Parse function arguments."""
182
+ args: list[Any] = []
183
+ kwargs: dict[str, Any] = {}
184
+
185
+ self._skip_whitespace()
186
+ if self._peek() == ")":
187
+ return args, kwargs
188
+
189
+ while True:
190
+ self._skip_whitespace()
191
+
192
+ # Try to parse keyword argument (identifier=value)
193
+ saved_pos = self._pos
194
+ identifier = self._parse_identifier()
195
+ self._skip_whitespace()
196
+
197
+ if identifier and self._peek() == "=":
198
+ # It's a keyword argument
199
+ self._advance() # consume =
200
+ value = self._parse_value()
201
+ kwargs[identifier] = value
202
+ else:
203
+ # It's a positional argument - restore position and parse value
204
+ self._pos = saved_pos
205
+ value = self._parse_value()
206
+ args.append(value)
207
+
208
+ self._skip_whitespace()
209
+ if self._peek() == ",":
210
+ self._advance()
211
+ self._skip_whitespace()
212
+ # Check for trailing comma before closing paren
213
+ if self._peek() == ")":
214
+ break
215
+ continue
216
+ break
217
+
218
+ return args, kwargs
219
+
220
+ def _parse_value(self) -> Any:
221
+ """Parse a value (number, string, list, bool, etc)."""
222
+ self._skip_whitespace()
223
+
224
+ ch = self._peek()
225
+
226
+ # String
227
+ if ch in ('"', "'"):
228
+ return self._parse_string()
229
+
230
+ # List
231
+ if ch == "[":
232
+ return self._parse_list()
233
+
234
+ # Number or identifier
235
+ if ch.isdigit() or ch in ("-", "."):
236
+ return self._parse_number()
237
+
238
+ # Boolean/None/identifier
239
+ identifier = self._parse_identifier()
240
+ if identifier == "True":
241
+ return True
242
+ elif identifier == "False":
243
+ return False
244
+ elif identifier == "None":
245
+ return None
246
+ elif identifier:
247
+ return identifier
248
+ else:
249
+ raise ValueError(f"Unexpected character: {ch}")
250
+
251
+ def _parse_number(self) -> int | float:
252
+ """Parse number (int or float)."""
253
+ start = self._pos
254
+ has_dot = False
255
+ has_e = False
256
+
257
+ # Handle negative sign
258
+ if self._peek() == "-":
259
+ self._advance()
260
+
261
+ # Leading dot
262
+ if self._peek() == ".":
263
+ has_dot = True
264
+ self._advance()
265
+
266
+ while self._pos < len(self._text):
267
+ ch = self._peek()
268
+ if ch.isdigit():
269
+ self._advance()
270
+ elif ch == "." and not has_dot and not has_e:
271
+ has_dot = True
272
+ self._advance()
273
+ elif ch in ("e", "E") and not has_e:
274
+ has_e = True
275
+ self._advance()
276
+ # Handle +/- after e
277
+ if self._peek() in ("+", "-"):
278
+ self._advance()
279
+ else:
280
+ break
281
+
282
+ num_str = self._text[start : self._pos]
283
+ if "." in num_str or "e" in num_str or "E" in num_str:
284
+ return float(num_str)
285
+ else:
286
+ return int(num_str)
287
+
288
+ def _parse_string(self) -> str:
289
+ """Parse string literal."""
290
+ quote = self._advance() # get quote character
291
+ start = self._pos
292
+ while self._pos < len(self._text):
293
+ ch = self._peek()
294
+ if ch == quote:
295
+ result = self._text[start : self._pos]
296
+ self._advance() # consume closing quote
297
+ return result
298
+ elif ch == "\\":
299
+ # Skip escaped character
300
+ self._advance()
301
+ if self._pos < len(self._text):
302
+ self._advance()
303
+ else:
304
+ self._advance()
305
+
306
+ raise ValueError("Unterminated string")
307
+
308
+ def _parse_list(self) -> list[Any]:
309
+ """Parse list literal."""
310
+ self._advance() # consume [
311
+ items: list[Any] = []
312
+
313
+ self._skip_whitespace()
314
+ if self._peek() == "]":
315
+ self._advance()
316
+ return items
317
+
318
+ while True:
319
+ items.append(self._parse_value())
320
+ self._skip_whitespace()
321
+ if self._peek() == ",":
322
+ self._advance()
323
+ self._skip_whitespace()
324
+ # Handle trailing comma
325
+ if self._peek() == "]":
326
+ break
327
+ continue
328
+ break
329
+
330
+ if self._peek() != "]":
331
+ raise ValueError("Unterminated list")
332
+ self._advance()
333
+
334
+ return items
335
+
336
+ def _parse_identifier(self) -> str:
337
+ """Parse identifier."""
338
+ start = self._pos
339
+ while self._pos < len(self._text):
340
+ ch = self._peek()
341
+ if ch.isalnum() or ch == "_":
342
+ self._advance()
343
+ else:
344
+ break
345
+ return self._text[start : self._pos]
346
+
347
+ def _skip_whitespace(self) -> None:
348
+ """Skip whitespace characters."""
349
+ while self._pos < len(self._text) and self._text[self._pos].isspace():
350
+ self._pos += 1
351
+
352
+ def _peek(self) -> str:
353
+ """Peek at current character."""
354
+ if self._pos < len(self._text):
355
+ return self._text[self._pos]
356
+ return ""
357
+
358
+ def _advance(self) -> str:
359
+ """Advance and return current character."""
360
+ if self._pos < len(self._text):
361
+ ch = self._text[self._pos]
362
+ self._pos += 1
363
+ return ch
364
+ return ""
365
+
366
+
367
+ def parse_expression(text: str) -> DSLExpression:
368
+ """Parse DSL expression string (legacy API).
369
+
370
+ Args:
371
+ text: Expression string to parse.
372
+
373
+ Returns:
374
+ Parsed DSLExpression.
375
+
376
+ Example:
377
+ >>> expr = parse_expression("lowpass(cutoff=1e6)")
378
+ >>> print(expr.operation)
379
+ lowpass
380
+ """
381
+ parser = DSLParser()
382
+ return parser.parse(text)
383
+
384
+
385
+ def analyze(data: Any, expression_str: str) -> Any:
386
+ """Execute DSL expression on data (legacy API).
387
+
388
+ Args:
389
+ data: NumPy array to analyze.
390
+ expression_str: DSL expression string.
391
+
392
+ Returns:
393
+ Analysis result.
394
+
395
+ Example:
396
+ >>> import numpy as np
397
+ >>> data = np.array([1, 2, 3, 4, 5])
398
+ >>> analyze(data, "mean")
399
+ 3.0
400
+ """
401
+ import numpy as np
402
+
403
+ # Parse expression using legacy parser
404
+ expr = parse_expression(expression_str)
405
+
406
+ # Execute using legacy executor
407
+ executor = DSLExecutor()
408
+ return executor.execute(expr, data)
409
+
410
+
411
+ # DSLExecutor class for backwards compatibility
412
+ class DSLExecutor:
413
+ """Legacy executor class for backwards compatibility.
414
+
415
+ Executes DSL operations on numpy arrays for testing purposes.
416
+ """
417
+
418
+ def __init__(self) -> None:
419
+ """Initialize executor with operation registry."""
420
+ self._operations: dict[str, Any] = {
421
+ "mean": lambda data, **kw: float(__import__("numpy").mean(data)),
422
+ "std": lambda data, **kw: float(__import__("numpy").std(data)),
423
+ "min": lambda data, **kw: float(__import__("numpy").min(data)),
424
+ "max": lambda data, **kw: float(__import__("numpy").max(data)),
425
+ "rms": lambda data, **kw: float(
426
+ __import__("numpy").sqrt(__import__("numpy").mean(data**2))
427
+ ),
428
+ "fft": lambda data, **kw: __import__("numpy").fft.fft(
429
+ data, n=kw.get("nfft", len(data))
430
+ ),
431
+ "normalize": self._normalize,
432
+ "lowpass": self._lowpass,
433
+ "highpass": self._highpass,
434
+ "bandpass": self._bandpass,
435
+ "slice": self._slice,
436
+ "resample": self._resample,
437
+ "psd": self._psd,
438
+ }
439
+
440
+ def execute(self, expr: DSLExpression, data: Any) -> Any:
441
+ """Execute DSL expression on data.
442
+
443
+ Args:
444
+ expr: DSLExpression to execute.
445
+ data: NumPy array to process.
446
+
447
+ Returns:
448
+ Execution result.
449
+
450
+ Raises:
451
+ ValueError: If operation is unknown.
452
+ """
453
+ import numpy as np
454
+
455
+ # Execute main operation
456
+ if expr.operation not in self._operations:
457
+ raise ValueError(f"Unknown operation: {expr.operation}")
458
+
459
+ # Call operation with positional args and keyword args
460
+ # Convert positional args to operation-specific kwargs for known operations
461
+ kwargs = dict(expr.kwargs)
462
+
463
+ # Handle slice operation with positional args: slice(start, end)
464
+ if expr.operation == "slice" and expr.args:
465
+ if len(expr.args) >= 1 and "start" not in kwargs:
466
+ kwargs["start"] = expr.args[0]
467
+ if len(expr.args) >= 2 and "end" not in kwargs:
468
+ kwargs["end"] = expr.args[1]
469
+
470
+ result = self._operations[expr.operation](data, **kwargs)
471
+
472
+ # Execute chain if present
473
+ if expr.chain is not None:
474
+ if not isinstance(result, np.ndarray):
475
+ raise ValueError(f"Cannot chain after {expr.operation}")
476
+ result = self.execute(expr.chain, result)
477
+
478
+ return result
479
+
480
+ def _normalize(self, data: Any, method: str = "minmax", **kwargs: Any) -> Any:
481
+ """Normalize data."""
482
+ import numpy as np
483
+
484
+ if method == "minmax":
485
+ data_min = np.min(data)
486
+ data_max = np.max(data)
487
+ if data_max == data_min:
488
+ return data
489
+ return (data - data_min) / (data_max - data_min)
490
+ elif method == "zscore":
491
+ mean = np.mean(data)
492
+ std = np.std(data)
493
+ if std == 0:
494
+ return data - mean
495
+ return (data - mean) / std
496
+ else:
497
+ return data
498
+
499
+ def _lowpass(self, data: Any, cutoff: float = 1e5, fs: float = 1e6, **kwargs: Any) -> Any:
500
+ """Apply lowpass filter."""
501
+ from scipy.signal import butter, filtfilt
502
+
503
+ nyquist = fs / 2
504
+ normalized_cutoff = cutoff / nyquist
505
+ b, a = butter(4, normalized_cutoff, btype="low")
506
+ return filtfilt(b, a, data)
507
+
508
+ def _highpass(self, data: Any, cutoff: float = 1e5, fs: float = 1e6, **kwargs: Any) -> Any:
509
+ """Apply highpass filter."""
510
+ from scipy.signal import butter, filtfilt
511
+
512
+ nyquist = fs / 2
513
+ normalized_cutoff = cutoff / nyquist
514
+ b, a = butter(4, normalized_cutoff, btype="high")
515
+ return filtfilt(b, a, data)
516
+
517
+ def _bandpass(
518
+ self,
519
+ data: Any,
520
+ low: float = 1e3,
521
+ high: float = 1e6,
522
+ fs: float = 1e6,
523
+ **kwargs: Any,
524
+ ) -> Any:
525
+ """Apply bandpass filter."""
526
+ from scipy.signal import butter, filtfilt
527
+
528
+ nyquist = fs / 2
529
+ normalized_low = low / nyquist
530
+ normalized_high = high / nyquist
531
+ b, a = butter(4, [normalized_low, normalized_high], btype="band")
532
+ return filtfilt(b, a, data)
533
+
534
+ def _slice(self, data: Any, start: int = 0, end: int | None = None, **kwargs: Any) -> Any:
535
+ """Slice data."""
536
+ return data[start:end]
537
+
538
+ def _resample(self, data: Any, factor: int = 2, **kwargs: Any) -> Any:
539
+ """Resample data by decimation factor."""
540
+ return data[::factor]
541
+
542
+ def _psd(self, data: Any, nperseg: int = 256, **kwargs: Any) -> Any:
543
+ """Compute power spectral density."""
544
+ from scipy.signal import welch
545
+
546
+ _, psd = welch(data, nperseg=nperseg)
547
+ return psd
548
+
549
+
550
+ __all__ = [
551
+ # Commands
552
+ "BUILTIN_COMMANDS",
553
+ # REPL
554
+ "REPL",
555
+ # AST nodes
556
+ "Assignment",
557
+ "Command",
558
+ # Legacy API (backwards compatibility)
559
+ "DSLExecutor",
560
+ "DSLExpression",
561
+ "DSLParser",
562
+ "Expression",
563
+ "ForLoop",
564
+ "FunctionCall",
565
+ # Interpreter
566
+ "Interpreter",
567
+ "InterpreterError",
568
+ # Parser
569
+ "Lexer",
570
+ "Literal",
571
+ "Parser",
572
+ "Pipeline",
573
+ "Statement",
574
+ "Token",
575
+ "TokenType",
576
+ "Variable",
577
+ "analyze",
578
+ "execute_dsl",
579
+ "parse_dsl",
580
+ "parse_expression",
581
+ "start_repl",
582
+ ]