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
@@ -0,0 +1,436 @@
1
+ """Modbus function code parsers.
2
+
3
+ This module provides parsers for all standard Modbus function codes,
4
+ supporting both request and response parsing.
5
+
6
+ References:
7
+ Modbus Application Protocol V1.1b3
8
+ https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any
14
+
15
+
16
+ def parse_read_coils_request(data: bytes) -> dict[str, Any]:
17
+ """Parse Read Coils (FC 01) request.
18
+
19
+ Request Format:
20
+ - Starting Address (2 bytes, big-endian)
21
+ - Quantity of Coils (2 bytes, big-endian, 1-2000)
22
+
23
+ Args:
24
+ data: Function data (excludes address and function code).
25
+
26
+ Returns:
27
+ Parsed request data.
28
+
29
+ Raises:
30
+ ValueError: If data is invalid.
31
+ """
32
+ if len(data) < 4:
33
+ raise ValueError("Insufficient data for Read Coils request")
34
+
35
+ starting_address = int.from_bytes(data[0:2], "big")
36
+ quantity = int.from_bytes(data[2:4], "big")
37
+
38
+ if quantity < 1 or quantity > 2000:
39
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-2000)")
40
+
41
+ return {
42
+ "starting_address": starting_address,
43
+ "quantity": quantity,
44
+ }
45
+
46
+
47
+ def parse_read_coils_response(data: bytes) -> dict[str, Any]:
48
+ """Parse Read Coils (FC 01) response.
49
+
50
+ Response Format:
51
+ - Byte Count (1 byte)
52
+ - Coil Status (N bytes, LSB first)
53
+
54
+ Args:
55
+ data: Function data.
56
+
57
+ Returns:
58
+ Parsed response data with coil values.
59
+
60
+ Raises:
61
+ ValueError: If data is invalid.
62
+ """
63
+ if len(data) < 1:
64
+ raise ValueError("Insufficient data for Read Coils response")
65
+
66
+ byte_count = data[0]
67
+ if len(data) < 1 + byte_count:
68
+ raise ValueError("Insufficient coil data")
69
+
70
+ # Parse coil values (each byte contains 8 coils, LSB first)
71
+ coils = []
72
+ for i in range(byte_count):
73
+ byte_val = data[1 + i]
74
+ for bit in range(8):
75
+ coils.append(bool(byte_val & (1 << bit)))
76
+
77
+ return {
78
+ "byte_count": byte_count,
79
+ "coils": coils,
80
+ }
81
+
82
+
83
+ def parse_read_discrete_inputs_request(data: bytes) -> dict[str, Any]:
84
+ """Parse Read Discrete Inputs (FC 02) request.
85
+
86
+ Same format as Read Coils request.
87
+
88
+ Args:
89
+ data: Function data.
90
+
91
+ Returns:
92
+ Parsed request data.
93
+ """
94
+ return parse_read_coils_request(data)
95
+
96
+
97
+ def parse_read_discrete_inputs_response(data: bytes) -> dict[str, Any]:
98
+ """Parse Read Discrete Inputs (FC 02) response.
99
+
100
+ Same format as Read Coils response.
101
+
102
+ Args:
103
+ data: Function data.
104
+
105
+ Returns:
106
+ Parsed response data.
107
+ """
108
+ return parse_read_coils_response(data)
109
+
110
+
111
+ def parse_read_holding_registers_request(data: bytes) -> dict[str, Any]:
112
+ """Parse Read Holding Registers (FC 03) request.
113
+
114
+ Request Format:
115
+ - Starting Address (2 bytes, big-endian)
116
+ - Quantity of Registers (2 bytes, big-endian, 1-125)
117
+
118
+ Args:
119
+ data: Function data.
120
+
121
+ Returns:
122
+ Parsed request data.
123
+
124
+ Raises:
125
+ ValueError: If data is invalid.
126
+ """
127
+ if len(data) < 4:
128
+ raise ValueError("Insufficient data for Read Holding Registers request")
129
+
130
+ starting_address = int.from_bytes(data[0:2], "big")
131
+ quantity = int.from_bytes(data[2:4], "big")
132
+
133
+ if quantity < 1 or quantity > 125:
134
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-125)")
135
+
136
+ return {
137
+ "starting_address": starting_address,
138
+ "quantity": quantity,
139
+ }
140
+
141
+
142
+ def parse_read_holding_registers_response(data: bytes) -> dict[str, Any]:
143
+ """Parse Read Holding Registers (FC 03) response.
144
+
145
+ Response Format:
146
+ - Byte Count (1 byte)
147
+ - Register Values (N bytes, 2 bytes per register, big-endian)
148
+
149
+ Args:
150
+ data: Function data.
151
+
152
+ Returns:
153
+ Parsed response data with register values.
154
+
155
+ Raises:
156
+ ValueError: If data is invalid.
157
+ """
158
+ if len(data) < 1:
159
+ raise ValueError("Insufficient data for Read Holding Registers response")
160
+
161
+ byte_count = data[0]
162
+ if len(data) < 1 + byte_count:
163
+ raise ValueError("Insufficient register data")
164
+
165
+ if byte_count % 2 != 0:
166
+ raise ValueError(f"Byte count must be even, got {byte_count}")
167
+
168
+ # Parse registers (2 bytes each, big-endian)
169
+ registers = []
170
+ for i in range(byte_count // 2):
171
+ offset = 1 + i * 2
172
+ reg_value = int.from_bytes(data[offset : offset + 2], "big")
173
+ registers.append(reg_value)
174
+
175
+ return {
176
+ "byte_count": byte_count,
177
+ "registers": registers,
178
+ }
179
+
180
+
181
+ def parse_read_input_registers_request(data: bytes) -> dict[str, Any]:
182
+ """Parse Read Input Registers (FC 04) request.
183
+
184
+ Same format as Read Holding Registers request.
185
+
186
+ Args:
187
+ data: Function data.
188
+
189
+ Returns:
190
+ Parsed request data.
191
+ """
192
+ return parse_read_holding_registers_request(data)
193
+
194
+
195
+ def parse_read_input_registers_response(data: bytes) -> dict[str, Any]:
196
+ """Parse Read Input Registers (FC 04) response.
197
+
198
+ Same format as Read Holding Registers response.
199
+
200
+ Args:
201
+ data: Function data.
202
+
203
+ Returns:
204
+ Parsed response data.
205
+ """
206
+ return parse_read_holding_registers_response(data)
207
+
208
+
209
+ def parse_write_single_coil(data: bytes) -> dict[str, Any]:
210
+ """Parse Write Single Coil (FC 05) request/response.
211
+
212
+ Format:
213
+ - Output Address (2 bytes, big-endian)
214
+ - Output Value (2 bytes, 0x0000 or 0xFF00)
215
+
216
+ Args:
217
+ data: Function data.
218
+
219
+ Returns:
220
+ Parsed data.
221
+
222
+ Raises:
223
+ ValueError: If data is invalid.
224
+ """
225
+ if len(data) < 4:
226
+ raise ValueError("Insufficient data for Write Single Coil")
227
+
228
+ output_address = int.from_bytes(data[0:2], "big")
229
+ output_value = int.from_bytes(data[2:4], "big")
230
+
231
+ if output_value not in (0x0000, 0xFF00):
232
+ raise ValueError(f"Invalid coil value: 0x{output_value:04X} (must be 0x0000 or 0xFF00)")
233
+
234
+ return {
235
+ "output_address": output_address,
236
+ "output_value": output_value,
237
+ "coil_state": output_value == 0xFF00,
238
+ }
239
+
240
+
241
+ def parse_write_single_register(data: bytes) -> dict[str, Any]:
242
+ """Parse Write Single Register (FC 06) request/response.
243
+
244
+ Format:
245
+ - Register Address (2 bytes, big-endian)
246
+ - Register Value (2 bytes, big-endian)
247
+
248
+ Args:
249
+ data: Function data.
250
+
251
+ Returns:
252
+ Parsed data.
253
+
254
+ Raises:
255
+ ValueError: If data is invalid.
256
+ """
257
+ if len(data) < 4:
258
+ raise ValueError("Insufficient data for Write Single Register")
259
+
260
+ register_address = int.from_bytes(data[0:2], "big")
261
+ register_value = int.from_bytes(data[2:4], "big")
262
+
263
+ return {
264
+ "register_address": register_address,
265
+ "register_value": register_value,
266
+ }
267
+
268
+
269
+ def parse_write_multiple_coils_request(data: bytes) -> dict[str, Any]:
270
+ """Parse Write Multiple Coils (FC 15) request.
271
+
272
+ Request Format:
273
+ - Starting Address (2 bytes, big-endian)
274
+ - Quantity of Outputs (2 bytes, big-endian, 1-1968)
275
+ - Byte Count (1 byte)
276
+ - Output Values (N bytes)
277
+
278
+ Args:
279
+ data: Function data.
280
+
281
+ Returns:
282
+ Parsed request data.
283
+
284
+ Raises:
285
+ ValueError: If data is invalid.
286
+ """
287
+ if len(data) < 5:
288
+ raise ValueError("Insufficient data for Write Multiple Coils request")
289
+
290
+ starting_address = int.from_bytes(data[0:2], "big")
291
+ quantity = int.from_bytes(data[2:4], "big")
292
+ byte_count = data[4]
293
+
294
+ if quantity < 1 or quantity > 1968:
295
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-1968)")
296
+
297
+ if len(data) < 5 + byte_count:
298
+ raise ValueError("Insufficient coil data")
299
+
300
+ # Parse coil values
301
+ coils: list[bool] = []
302
+ for i in range(byte_count):
303
+ byte_val = data[5 + i]
304
+ for bit in range(8):
305
+ if len(coils) < quantity:
306
+ coils.append(bool(byte_val & (1 << bit)))
307
+
308
+ return {
309
+ "starting_address": starting_address,
310
+ "quantity": quantity,
311
+ "byte_count": byte_count,
312
+ "coils": coils,
313
+ }
314
+
315
+
316
+ def parse_write_multiple_coils_response(data: bytes) -> dict[str, Any]:
317
+ """Parse Write Multiple Coils (FC 15) response.
318
+
319
+ Response Format:
320
+ - Starting Address (2 bytes, big-endian)
321
+ - Quantity of Outputs (2 bytes, big-endian)
322
+
323
+ Args:
324
+ data: Function data.
325
+
326
+ Returns:
327
+ Parsed response data.
328
+
329
+ Raises:
330
+ ValueError: If data is invalid.
331
+ """
332
+ if len(data) < 4:
333
+ raise ValueError("Insufficient data for Write Multiple Coils response")
334
+
335
+ starting_address = int.from_bytes(data[0:2], "big")
336
+ quantity = int.from_bytes(data[2:4], "big")
337
+
338
+ return {
339
+ "starting_address": starting_address,
340
+ "quantity": quantity,
341
+ }
342
+
343
+
344
+ def parse_write_multiple_registers_request(data: bytes) -> dict[str, Any]:
345
+ """Parse Write Multiple Registers (FC 16) request.
346
+
347
+ Request Format:
348
+ - Starting Address (2 bytes, big-endian)
349
+ - Quantity of Registers (2 bytes, big-endian, 1-123)
350
+ - Byte Count (1 byte)
351
+ - Register Values (N bytes, 2 bytes per register)
352
+
353
+ Args:
354
+ data: Function data.
355
+
356
+ Returns:
357
+ Parsed request data.
358
+
359
+ Raises:
360
+ ValueError: If data is invalid.
361
+ """
362
+ if len(data) < 5:
363
+ raise ValueError("Insufficient data for Write Multiple Registers request")
364
+
365
+ starting_address = int.from_bytes(data[0:2], "big")
366
+ quantity = int.from_bytes(data[2:4], "big")
367
+ byte_count = data[4]
368
+
369
+ if quantity < 1 or quantity > 123:
370
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-123)")
371
+
372
+ if byte_count != quantity * 2:
373
+ raise ValueError(f"Byte count mismatch: {byte_count} != {quantity * 2}")
374
+
375
+ if len(data) < 5 + byte_count:
376
+ raise ValueError("Insufficient register data")
377
+
378
+ # Parse registers
379
+ registers = []
380
+ for i in range(quantity):
381
+ offset = 5 + i * 2
382
+ reg_value = int.from_bytes(data[offset : offset + 2], "big")
383
+ registers.append(reg_value)
384
+
385
+ return {
386
+ "starting_address": starting_address,
387
+ "quantity": quantity,
388
+ "byte_count": byte_count,
389
+ "registers": registers,
390
+ }
391
+
392
+
393
+ def parse_write_multiple_registers_response(data: bytes) -> dict[str, Any]:
394
+ """Parse Write Multiple Registers (FC 16) response.
395
+
396
+ Response Format:
397
+ - Starting Address (2 bytes, big-endian)
398
+ - Quantity of Registers (2 bytes, big-endian)
399
+
400
+ Args:
401
+ data: Function data.
402
+
403
+ Returns:
404
+ Parsed response data.
405
+
406
+ Raises:
407
+ ValueError: If data is invalid.
408
+ """
409
+ if len(data) < 4:
410
+ raise ValueError("Insufficient data for Write Multiple Registers response")
411
+
412
+ starting_address = int.from_bytes(data[0:2], "big")
413
+ quantity = int.from_bytes(data[2:4], "big")
414
+
415
+ return {
416
+ "starting_address": starting_address,
417
+ "quantity": quantity,
418
+ }
419
+
420
+
421
+ __all__ = [
422
+ "parse_read_coils_request",
423
+ "parse_read_coils_response",
424
+ "parse_read_discrete_inputs_request",
425
+ "parse_read_discrete_inputs_response",
426
+ "parse_read_holding_registers_request",
427
+ "parse_read_holding_registers_response",
428
+ "parse_read_input_registers_request",
429
+ "parse_read_input_registers_response",
430
+ "parse_write_multiple_coils_request",
431
+ "parse_write_multiple_coils_response",
432
+ "parse_write_multiple_registers_request",
433
+ "parse_write_multiple_registers_response",
434
+ "parse_write_single_coil",
435
+ "parse_write_single_register",
436
+ ]
@@ -0,0 +1,21 @@
1
+ """OPC UA (Unified Architecture) protocol analyzer.
2
+
3
+ This package provides comprehensive OPC UA binary protocol analysis for
4
+ industrial automation and SCADA systems.
5
+
6
+ Example:
7
+ >>> from oscura.analyzers.protocols.industrial.opcua import OPCUAAnalyzer
8
+ >>> analyzer = OPCUAAnalyzer()
9
+ >>> # Parse OPC UA Hello message
10
+ >>> hello = bytes([0x48, 0x45, 0x4C, 0x46, ...]) # "HEL" + "F"
11
+ >>> msg = analyzer.parse_message(hello, timestamp=0.0)
12
+ >>> print(f"{msg.message_type}: {msg.decoded_service}")
13
+ """
14
+
15
+ from oscura.analyzers.protocols.industrial.opcua.analyzer import (
16
+ OPCUAAnalyzer,
17
+ OPCUAMessage,
18
+ OPCUANode,
19
+ )
20
+
21
+ __all__ = ["OPCUAAnalyzer", "OPCUAMessage", "OPCUANode"]