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
@@ -22,6 +22,47 @@ if TYPE_CHECKING:
22
22
  ChartType = Literal["line", "scatter", "bar", "histogram", "heatmap", "pie", "spectrum"]
23
23
 
24
24
 
25
+ def _should_use_pie_chart(data_shape: tuple[int, ...], data: NDArray[np.float64] | None) -> bool:
26
+ """Check if data is suitable for pie chart.
27
+
28
+ Args:
29
+ data_shape: Shape of data array
30
+ data: Optional data array
31
+
32
+ Returns:
33
+ True if pie chart is appropriate
34
+ """
35
+ if not (len(data_shape) > 0 and data_shape[0] <= 6 and data is not None):
36
+ return False
37
+
38
+ all_non_negative = np.all(data >= 0)
39
+ if not bool(all_non_negative):
40
+ return False
41
+
42
+ total = np.sum(data)
43
+ return bool(total > 0 and np.allclose(data / total * 100, data / total * 100))
44
+
45
+
46
+ def _select_chart_by_shape(data_shape: tuple[int, ...]) -> ChartType:
47
+ """Select chart type based on data shape.
48
+
49
+ Args:
50
+ data_shape: Shape of data array
51
+
52
+ Returns:
53
+ Appropriate chart type
54
+ """
55
+ if len(data_shape) == 1:
56
+ return "bar" if data_shape[0] < 20 else "histogram"
57
+
58
+ if len(data_shape) == 2:
59
+ if data_shape[0] > 50 and data_shape[1] > 50:
60
+ return "heatmap"
61
+ return "scatter"
62
+
63
+ return "line"
64
+
65
+
25
66
  def auto_select_chart(
26
67
  data_type: str,
27
68
  data_shape: tuple[int, ...],
@@ -67,62 +108,33 @@ def auto_select_chart(
67
108
  References:
68
109
  REPORT-028: Automated Chart Type Selection
69
110
  """
70
- # Time series → line plot
71
- if data_type == "time_series":
72
- return "line"
73
-
74
- # Frequency data → spectrum plot (log scale)
75
- if data_type == "frequency":
76
- return "spectrum"
111
+ # Direct type mappings
112
+ type_map: dict[str, ChartType] = {
113
+ "time_series": "line",
114
+ "frequency": "spectrum",
115
+ "distribution": "histogram",
116
+ "correlation": "scatter",
117
+ "matrix": "heatmap",
118
+ "parts": "pie",
119
+ }
77
120
 
78
- # Distribution histogram or box plot
79
- if data_type == "distribution":
80
- return "histogram"
121
+ if data_type in type_map:
122
+ return type_map[data_type]
81
123
 
82
- # Categorical comparison bar chart
124
+ # Categorical with possible pie chart
83
125
  if data_type == "categorical":
84
- # If very few categories, pie chart might be appropriate
85
- if len(data_shape) > 0 and data_shape[0] <= 6 and data is not None and np.all(data >= 0):
86
- # Check if data represents parts of a whole
87
- total = np.sum(data)
88
- if total > 0 and np.allclose(data / total * 100, data / total * 100):
89
- return "pie"
126
+ if _should_use_pie_chart(data_shape, data):
127
+ return "pie"
90
128
  return "bar"
91
129
 
92
- # Comparison (continuous) scatter plot
130
+ # Comparison with size-based selection
93
131
  if data_type == "comparison":
94
- # If 2D data with moderate size, scatter plot
95
132
  if len(data_shape) >= 2 and data_shape[0] < 10000:
96
133
  return "scatter"
97
- # Large comparison data → bar chart
98
134
  return "bar"
99
135
 
100
- # Correlation → scatter plot with potential regression
101
- if data_type == "correlation":
102
- return "scatter"
103
-
104
- # 2D matrix → heatmap
105
- if data_type == "matrix":
106
- return "heatmap"
107
-
108
- # Parts-to-whole → pie chart
109
- if data_type == "parts":
110
- return "pie"
111
-
112
136
  # Default based on shape
113
- if len(data_shape) == 1:
114
- # 1D data: histogram for distributions, bar for small sets
115
- if data_shape[0] < 20:
116
- return "bar"
117
- return "histogram"
118
- elif len(data_shape) == 2:
119
- # 2D data: heatmap for square-ish matrices, scatter for point clouds
120
- if data_shape[0] > 50 and data_shape[1] > 50:
121
- return "heatmap"
122
- return "scatter"
123
-
124
- # Fallback to line plot
125
- return "line"
137
+ return _select_chart_by_shape(data_shape)
126
138
 
127
139
 
128
140
  def recommend_chart_with_reasoning(
@@ -82,12 +82,33 @@ def _generate_comparison_summary(
82
82
  current: dict[str, Any],
83
83
  ) -> str:
84
84
  """Generate comparison summary."""
85
- summary_parts = []
85
+ summary_parts: list[str] = []
86
86
 
87
- # Count changes
88
87
  baseline_meas = baseline.get("measurements", {})
89
88
  current_meas = current.get("measurements", {})
90
89
 
90
+ changed, improved, degraded = _categorize_parameter_changes(baseline_meas, current_meas)
91
+
92
+ _add_parameter_change_summary(
93
+ summary_parts, baseline_meas, current_meas, changed, improved, degraded
94
+ )
95
+ _add_pass_rate_comparison(summary_parts, baseline, current)
96
+
97
+ return "\n".join(summary_parts)
98
+
99
+
100
+ def _categorize_parameter_changes(
101
+ baseline_meas: dict[str, Any], current_meas: dict[str, Any]
102
+ ) -> tuple[list[str], list[str], list[str]]:
103
+ """Categorize parameter changes.
104
+
105
+ Args:
106
+ baseline_meas: Baseline measurements.
107
+ current_meas: Current measurements.
108
+
109
+ Returns:
110
+ Tuple of (changed, improved, degraded) parameter lists.
111
+ """
91
112
  all_params = set(baseline_meas.keys()) | set(current_meas.keys())
92
113
  changed_params = []
93
114
  improved_params = []
@@ -98,10 +119,9 @@ def _generate_comparison_summary(
98
119
  curr_val = current_meas.get(param, {}).get("value")
99
120
 
100
121
  if base_val is not None and curr_val is not None:
101
- if abs(curr_val - base_val) / abs(base_val) > 0.05: # >5% change
122
+ if abs(curr_val - base_val) / abs(base_val) > 0.05:
102
123
  changed_params.append(param)
103
124
 
104
- # Determine if improved or degraded
105
125
  base_passed = baseline_meas.get(param, {}).get("passed", True)
106
126
  curr_passed = current_meas.get(param, {}).get("passed", True)
107
127
 
@@ -110,22 +130,50 @@ def _generate_comparison_summary(
110
130
  elif base_passed and not curr_passed:
111
131
  degraded_params.append(param)
112
132
 
133
+ return changed_params, improved_params, degraded_params
134
+
135
+
136
+ def _add_parameter_change_summary(
137
+ summary_parts: list[str],
138
+ baseline_meas: dict[str, Any],
139
+ current_meas: dict[str, Any],
140
+ changed: list[str],
141
+ improved: list[str],
142
+ degraded: list[str],
143
+ ) -> None:
144
+ """Add parameter change summary.
145
+
146
+ Args:
147
+ summary_parts: List to append to.
148
+ baseline_meas: Baseline measurements.
149
+ current_meas: Current measurements.
150
+ changed: Changed parameters.
151
+ improved: Improved parameters.
152
+ degraded: Degraded parameters.
153
+ """
154
+ all_params = set(baseline_meas.keys()) | set(current_meas.keys())
113
155
  summary_parts.append(f"Comparing {len(all_params)} parameter(s) between baseline and current.")
114
156
 
115
- if changed_params:
116
- summary_parts.append(f"\n{len(changed_params)} measurement(s) changed significantly (>5%).")
157
+ if changed:
158
+ summary_parts.append(f"\n{len(changed)} measurement(s) changed significantly (>5%).")
117
159
 
118
- if improved_params:
119
- summary_parts.append(
120
- f"\n✓ {len(improved_params)} parameter(s) improved (failures → passes)."
121
- )
160
+ if improved:
161
+ summary_parts.append(f"\n✓ {len(improved)} parameter(s) improved (failures → passes).")
162
+
163
+ if degraded:
164
+ summary_parts.append(f"\n✗ {len(degraded)} parameter(s) degraded (passes → failures).")
122
165
 
123
- if degraded_params:
124
- summary_parts.append(
125
- f"\n✗ {len(degraded_params)} parameter(s) degraded (passes → failures)."
126
- )
127
166
 
128
- # Pass/fail comparison
167
+ def _add_pass_rate_comparison(
168
+ summary_parts: list[str], baseline: dict[str, Any], current: dict[str, Any]
169
+ ) -> None:
170
+ """Add pass rate comparison.
171
+
172
+ Args:
173
+ summary_parts: List to append to.
174
+ baseline: Baseline results.
175
+ current: Current results.
176
+ """
129
177
  baseline_pass = baseline.get("pass_count", 0)
130
178
  baseline_total = baseline.get("total_count", 0)
131
179
  current_pass = current.get("pass_count", 0)
@@ -140,8 +188,6 @@ def _generate_comparison_summary(
140
188
  f"\nPass rate: {baseline_rate:.1f}% → {current_rate:.1f}% ({delta:+.1f}% change)"
141
189
  )
142
190
 
143
- return "\n".join(summary_parts)
144
-
145
191
 
146
192
  def _create_changes_section(
147
193
  baseline: dict[str, Any],
@@ -67,18 +67,61 @@ def generate_executive_summary(
67
67
  References:
68
68
  REPORT-004: Executive Summary Auto-Generation
69
69
  """
70
- # Extract basic counts
70
+ # Extract counts and violations
71
+ pass_count, total_count, fail_count, overall_status = _extract_counts(results)
72
+ critical_violations = _extract_critical_violations(results)
73
+
74
+ # Build key findings
75
+ min_margin = results.get("min_margin")
76
+ key_findings = _build_key_findings(
77
+ critical_violations, results.get("violations", []), min_margin
78
+ )
79
+
80
+ # Build summary text
81
+ summary_text = _build_summary_text(
82
+ overall_status,
83
+ pass_count,
84
+ total_count,
85
+ fail_count,
86
+ min_margin,
87
+ critical_violations,
88
+ key_findings,
89
+ length,
90
+ max_findings,
91
+ )
92
+
93
+ return ExecutiveSummary(
94
+ overall_status=overall_status,
95
+ pass_count=pass_count,
96
+ total_count=total_count,
97
+ key_findings=key_findings[:max_findings],
98
+ critical_violations=[str(v) for v in critical_violations],
99
+ min_margin_pct=min_margin,
100
+ summary_text=summary_text,
101
+ )
102
+
103
+
104
+ def _extract_counts(results: dict[str, Any]) -> tuple[int, int, int, bool]:
105
+ """Extract pass/fail counts from results."""
71
106
  pass_count = results.get("pass_count", 0)
72
107
  total_count = results.get("total_count", 0)
73
108
  fail_count = total_count - pass_count if total_count else 0
74
109
  overall_status = fail_count == 0
110
+ return pass_count, total_count, fail_count, overall_status
75
111
 
76
- # Extract violations
112
+
113
+ def _extract_critical_violations(results: dict[str, Any]) -> list[Any]:
114
+ """Extract critical violations from results."""
77
115
  violations = results.get("violations", [])
78
- critical_violations = [v for v in violations if v.get("severity", "").lower() == "critical"]
116
+ return [v for v in violations if v.get("severity", "").lower() == "critical"]
117
+
79
118
 
80
- # Extract key findings
119
+ def _build_key_findings(
120
+ critical_violations: list[Any], violations: list[Any], min_margin: float | None
121
+ ) -> list[str]:
122
+ """Build key findings list."""
81
123
  key_findings: list[str] = []
124
+
82
125
  if critical_violations:
83
126
  key_findings.append(
84
127
  f"{len(critical_violations)} critical violation(s) require immediate attention"
@@ -86,42 +129,51 @@ def generate_executive_summary(
86
129
  elif violations:
87
130
  key_findings.append(f"{len(violations)} violation(s) detected")
88
131
 
89
- # Add margin information
90
- min_margin = results.get("min_margin")
91
132
  if min_margin is not None and min_margin < 20:
92
133
  status = "critical" if min_margin < 10 else "marginal"
93
134
  key_findings.append(f"Minimum margin is {min_margin:.1f}% ({status})")
94
135
 
95
- # Build summary text in natural language
136
+ return key_findings
137
+
138
+
139
+ def _build_summary_text(
140
+ overall_status: bool,
141
+ pass_count: int,
142
+ total_count: int,
143
+ fail_count: int,
144
+ min_margin: float | None,
145
+ critical_violations: list[Any],
146
+ key_findings: list[str],
147
+ length: Literal["short", "detailed"],
148
+ max_findings: int,
149
+ ) -> str:
150
+ """Build natural language summary text."""
151
+ # Use list + join for O(n) string building instead of O(n²) +=
152
+ text_parts: list[str] = []
153
+
154
+ # Base status message
96
155
  if overall_status and total_count > 0:
97
- summary_text = f"All {pass_count} tests passed."
156
+ text_parts.append(f"All {pass_count} tests passed.")
98
157
  if min_margin is not None and min_margin > 20:
99
- summary_text += f" Minimum margin: {min_margin:.1f}%."
158
+ text_parts.append(f" Minimum margin: {min_margin:.1f}%.")
100
159
  elif total_count > 0:
101
160
  pct = fail_count / total_count * 100
102
- summary_text = f"{fail_count} of {total_count} tests failed ({pct:.0f}% failure rate)."
161
+ text_parts.append(f"{fail_count} of {total_count} tests failed ({pct:.0f}% failure rate).")
103
162
  else:
104
- summary_text = "Analysis completed successfully."
163
+ text_parts.append("Analysis completed successfully.")
105
164
 
165
+ # Add critical violations warning
106
166
  if critical_violations:
107
- summary_text += (
167
+ text_parts.append(
108
168
  f" Critical: {len(critical_violations)} violation(s) require immediate action."
109
169
  )
110
170
 
111
- # Add detailed findings for detailed mode
171
+ # Add detailed findings if requested
112
172
  if length == "detailed" and key_findings:
113
- summary_text += "\n\nKey Findings:\n"
114
- summary_text += "\n".join(f" - {finding}" for finding in key_findings[:max_findings])
173
+ text_parts.append("\n\nKey Findings:\n")
174
+ text_parts.append("\n".join(f" - {finding}" for finding in key_findings[:max_findings]))
115
175
 
116
- return ExecutiveSummary(
117
- overall_status=overall_status,
118
- pass_count=pass_count,
119
- total_count=total_count,
120
- key_findings=key_findings[:max_findings],
121
- critical_violations=[str(v) for v in critical_violations],
122
- min_margin_pct=min_margin,
123
- summary_text=summary_text,
124
- )
176
+ return "".join(text_parts)
125
177
 
126
178
 
127
179
  __all__ = ["ExecutiveSummary", "generate_executive_summary"]
@@ -151,27 +151,30 @@ def _render_html(report: Report, path: Path, **kwargs: Any) -> None:
151
151
 
152
152
  def _render_markdown(report: Report, path: Path, **kwargs: Any) -> None:
153
153
  """Render to Markdown."""
154
- # Generate Markdown content
155
- content = f"# {report.config.title}\n\n"
154
+ # Use list + join for O(n) string building instead of O(n²) +=
155
+ content_parts = [f"# {report.config.title}\n\n"]
156
156
 
157
157
  if hasattr(report.config, "author") and report.config.author:
158
- content += f"**Author:** {report.config.author} \n"
158
+ content_parts.append(f"**Author:** {report.config.author} \n")
159
159
 
160
- content += f"**Date:** {report.config.created.strftime('%Y-%m-%d')}\n\n"
160
+ content_parts.append(f"**Date:** {report.config.created.strftime('%Y-%m-%d')}\n\n")
161
161
 
162
- # Add sections
162
+ # Add sections with list.append instead of += in loops
163
163
  for section in report.sections:
164
164
  if not section.visible:
165
165
  continue
166
166
 
167
- content += f"{'#' * (section.level + 1)} {section.title}\n\n"
167
+ content_parts.append(f"{'#' * (section.level + 1)} {section.title}\n\n")
168
168
 
169
169
  if isinstance(section.content, str):
170
- content += section.content + "\n\n"
170
+ content_parts.append(section.content)
171
+ content_parts.append("\n\n")
171
172
  elif isinstance(section.content, list):
172
173
  for item in section.content:
173
- content += str(item) + "\n\n"
174
+ content_parts.append(str(item))
175
+ content_parts.append("\n\n")
174
176
 
177
+ content = "".join(content_parts)
175
178
  path.write_text(content)
176
179
 
177
180