oscura 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
oscura/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
+ ]