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
@@ -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