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
@@ -0,0 +1,412 @@
1
+ """BACnet encoding and decoding utilities.
2
+
3
+ This module provides low-level BACnet encoding/decoding functions for tags,
4
+ object identifiers, and application data types according to ASHRAE 135-2020.
5
+
6
+ References:
7
+ ANSI/ASHRAE Standard 135-2020, Clause 20: Encoding of BACnet Tags
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any
13
+
14
+
15
+ def parse_tag(data: bytes, offset: int) -> tuple[dict[str, Any], int]:
16
+ """Parse BACnet tag (application or context).
17
+
18
+ Args:
19
+ data: Raw bytes to parse.
20
+ offset: Starting offset in data.
21
+
22
+ Returns:
23
+ Tuple of (tag_dict, bytes_consumed) where tag_dict contains:
24
+ - tag_number: Tag number (0-254)
25
+ - context_specific: True if context tag, False if application tag
26
+ - length: Value/length field
27
+ - value: Decoded value (if applicable)
28
+
29
+ Raises:
30
+ ValueError: If data is too short or invalid tag format.
31
+
32
+ Example:
33
+ >>> data = bytes([0xC4, 0x02, 0x00, 0x00, 0x08])
34
+ >>> tag, consumed = parse_tag(data, 0)
35
+ >>> print(f"Tag {tag['tag_number']}, length {tag['length']}")
36
+ """
37
+ if offset >= len(data):
38
+ raise ValueError("Offset beyond data length")
39
+
40
+ initial_offset = offset
41
+ tag_byte = data[offset]
42
+ offset += 1
43
+
44
+ # Parse tag number (bits 4-7)
45
+ tag_number = (tag_byte >> 4) & 0x0F
46
+ # Bit 3: class (0=application, 1=context)
47
+ context_specific = bool(tag_byte & 0x08)
48
+ # Bits 0-2: length/value/type
49
+ length_value_type = tag_byte & 0x07
50
+
51
+ # Extended tag number (tag number 15 means next byte has actual tag)
52
+ if tag_number == 15:
53
+ if offset >= len(data):
54
+ raise ValueError("Extended tag number missing")
55
+ tag_number = data[offset]
56
+ offset += 1
57
+
58
+ # Decode length/value/type field
59
+ # For context tags: lvt=6 is opening tag, lvt=7 is closing tag
60
+ # For application tags: lvt=5-7 are special (5 is used normally for length 5, but 6/7 are reserved)
61
+ # Actually in BACnet: for application tags, lvt=7 is NOT used for opening/closing (those are context-only)
62
+ # Instead, lvt in 0-4 gives the length directly, lvt=5 means length 5 for most tags except
63
+ # for constructed data (which uses context tags with opening/closing)
64
+ # Let me simplify: lvt=6/7 only have special meaning for context tags
65
+ if context_specific and length_value_type == 6: # Opening tag (context only)
66
+ length = 0
67
+ is_opening = True
68
+ is_closing = False
69
+ elif context_specific and length_value_type == 7: # Closing tag (context only)
70
+ length = 0
71
+ is_opening = False
72
+ is_closing = True
73
+ elif not context_specific and length_value_type == 5: # Extended length (application only)
74
+ # For application tags, lvt=5 means extended length follows
75
+ if offset >= len(data):
76
+ raise ValueError("Extended length missing")
77
+ length = data[offset]
78
+ offset += 1
79
+ if length == 254: # 16-bit length
80
+ if offset + 1 >= len(data):
81
+ raise ValueError("16-bit length incomplete")
82
+ length = int.from_bytes(data[offset : offset + 2], "big")
83
+ offset += 2
84
+ elif length == 255: # 32-bit length
85
+ if offset + 3 >= len(data):
86
+ raise ValueError("32-bit length incomplete")
87
+ length = int.from_bytes(data[offset : offset + 4], "big")
88
+ offset += 4
89
+ is_opening = False
90
+ is_closing = False
91
+ else:
92
+ # For lengths 0-4, lvt directly encodes the length
93
+ length = length_value_type
94
+ is_opening = False
95
+ is_closing = False
96
+
97
+ tag_dict = {
98
+ "tag_number": tag_number,
99
+ "context_specific": context_specific,
100
+ "length": length,
101
+ "is_opening": is_opening,
102
+ "is_closing": is_closing,
103
+ }
104
+
105
+ bytes_consumed = offset - initial_offset
106
+ return tag_dict, bytes_consumed
107
+
108
+
109
+ def parse_unsigned(data: bytes, offset: int, length: int) -> tuple[int, int]:
110
+ """Parse BACnet unsigned integer.
111
+
112
+ Args:
113
+ data: Raw bytes to parse.
114
+ offset: Starting offset in data.
115
+ length: Number of bytes (1-4).
116
+
117
+ Returns:
118
+ Tuple of (value, bytes_consumed).
119
+
120
+ Raises:
121
+ ValueError: If length is invalid or data too short.
122
+
123
+ Example:
124
+ >>> data = bytes([0x00, 0x01, 0x23, 0x45])
125
+ >>> value, consumed = parse_unsigned(data, 0, 4)
126
+ >>> print(f"Value: {value}") # 74565
127
+ """
128
+ if length < 1 or length > 4:
129
+ raise ValueError(f"Invalid unsigned integer length: {length}")
130
+ if offset + length > len(data):
131
+ raise ValueError("Data too short for unsigned integer")
132
+
133
+ value = int.from_bytes(data[offset : offset + length], "big")
134
+ return value, length
135
+
136
+
137
+ def parse_enumerated(data: bytes, offset: int, length: int) -> tuple[int, int]:
138
+ """Parse BACnet enumerated value (same encoding as unsigned).
139
+
140
+ Args:
141
+ data: Raw bytes to parse.
142
+ offset: Starting offset in data.
143
+ length: Number of bytes (1-4).
144
+
145
+ Returns:
146
+ Tuple of (value, bytes_consumed).
147
+
148
+ Raises:
149
+ ValueError: If length is invalid or data too short.
150
+
151
+ Example:
152
+ >>> data = bytes([0x03]) # Segmentation: both
153
+ >>> value, consumed = parse_enumerated(data, 0, 1)
154
+ """
155
+ return parse_unsigned(data, offset, length)
156
+
157
+
158
+ def parse_object_identifier(data: bytes, offset: int) -> tuple[dict[str, Any], int]:
159
+ """Parse BACnet object identifier (32-bit: 10-bit type + 22-bit instance).
160
+
161
+ Args:
162
+ data: Raw bytes to parse.
163
+ offset: Starting offset in data.
164
+
165
+ Returns:
166
+ Tuple of (obj_id_dict, bytes_consumed) where obj_id_dict contains:
167
+ - object_type: Object type number
168
+ - object_type_name: Human-readable type name
169
+ - instance: Instance number
170
+
171
+ Raises:
172
+ ValueError: If data too short.
173
+
174
+ Example:
175
+ >>> data = bytes([0x02, 0x00, 0x00, 0x08])
176
+ >>> obj_id, consumed = parse_object_identifier(data, 0)
177
+ >>> print(f"{obj_id['object_type_name']} #{obj_id['instance']}")
178
+ """
179
+ if offset + 4 > len(data):
180
+ raise ValueError("Data too short for object identifier")
181
+
182
+ obj_id_bytes = int.from_bytes(data[offset : offset + 4], "big")
183
+ object_type = (obj_id_bytes >> 22) & 0x3FF # Top 10 bits
184
+ instance = obj_id_bytes & 0x3FFFFF # Bottom 22 bits
185
+
186
+ # Object type names (ASHRAE 135-2020, Clause 21)
187
+ object_type_names = {
188
+ 0: "analog-input",
189
+ 1: "analog-output",
190
+ 2: "analog-value",
191
+ 3: "binary-input",
192
+ 4: "binary-output",
193
+ 5: "binary-value",
194
+ 6: "calendar",
195
+ 7: "command",
196
+ 8: "device",
197
+ 9: "event-enrollment",
198
+ 10: "file",
199
+ 11: "group",
200
+ 12: "loop",
201
+ 13: "multi-state-input",
202
+ 14: "multi-state-output",
203
+ 15: "notification-class",
204
+ 16: "program",
205
+ 17: "schedule",
206
+ 18: "averaging",
207
+ 19: "multi-state-value",
208
+ 20: "trend-log",
209
+ 21: "life-safety-point",
210
+ 22: "life-safety-zone",
211
+ 23: "accumulator",
212
+ 24: "pulse-converter",
213
+ }
214
+
215
+ obj_id_dict = {
216
+ "object_type": object_type,
217
+ "object_type_name": object_type_names.get(object_type, f"type-{object_type}"),
218
+ "instance": instance,
219
+ }
220
+
221
+ return obj_id_dict, 4
222
+
223
+
224
+ def parse_character_string(data: bytes, offset: int, length: int) -> tuple[str, int]:
225
+ """Parse BACnet character string (encoding byte + UTF-8 string).
226
+
227
+ Args:
228
+ data: Raw bytes to parse.
229
+ offset: Starting offset in data.
230
+ length: Total length including encoding byte.
231
+
232
+ Returns:
233
+ Tuple of (string_value, bytes_consumed).
234
+
235
+ Raises:
236
+ ValueError: If data too short.
237
+
238
+ Example:
239
+ >>> data = bytes([0x00]) + b"Building 1" # 0x00 = UTF-8
240
+ >>> string, consumed = parse_character_string(data, 0, 11)
241
+ """
242
+ if offset + length > len(data):
243
+ raise ValueError("Data too short for character string")
244
+
245
+ encoding = data[offset] # 0=UTF-8, 1=IBM/MS DBCS, 2=JIS C 6226, 3=UCS-4, 4=UCS-2, 5=ISO 8859-1
246
+ string_data = data[offset + 1 : offset + length]
247
+
248
+ if encoding == 0: # UTF-8
249
+ string_value = string_data.decode("utf-8", errors="replace")
250
+ elif encoding == 5: # ISO 8859-1 (Latin-1)
251
+ string_value = string_data.decode("latin-1", errors="replace")
252
+ else:
253
+ # For unsupported encodings, return hex representation
254
+ string_value = string_data.hex()
255
+
256
+ return string_value, length
257
+
258
+
259
+ def _parse_signed_integer(
260
+ data: bytes, value_offset: int, length: int, tag_size: int
261
+ ) -> tuple[int, int]:
262
+ """Parse signed integer application tag.
263
+
264
+ Args:
265
+ data: Raw bytes.
266
+ value_offset: Offset to value data.
267
+ length: Value length.
268
+ tag_size: Size of tag header.
269
+
270
+ Returns:
271
+ Tuple of (value, bytes_consumed).
272
+
273
+ Raises:
274
+ ValueError: If data too short.
275
+ """
276
+ if value_offset + length > len(data):
277
+ raise ValueError("Data too short for signed integer")
278
+ int_value = int.from_bytes(data[value_offset : value_offset + length], "big", signed=True)
279
+ return int_value, tag_size + length
280
+
281
+
282
+ def _parse_real(data: bytes, value_offset: int, length: int, tag_size: int) -> tuple[float, int]:
283
+ """Parse real (float) application tag.
284
+
285
+ Args:
286
+ data: Raw bytes.
287
+ value_offset: Offset to value data.
288
+ length: Value length.
289
+ tag_size: Size of tag header.
290
+
291
+ Returns:
292
+ Tuple of (value, bytes_consumed).
293
+
294
+ Raises:
295
+ ValueError: If invalid length or data too short.
296
+ """
297
+ import struct
298
+
299
+ if length != 4:
300
+ raise ValueError(f"Invalid real length: {length}")
301
+ if value_offset + 4 > len(data):
302
+ raise ValueError("Data too short for real")
303
+
304
+ float_value = struct.unpack(">f", data[value_offset : value_offset + 4])[0]
305
+ return float_value, tag_size + 4
306
+
307
+
308
+ def _parse_double(data: bytes, value_offset: int, length: int, tag_size: int) -> tuple[float, int]:
309
+ """Parse double application tag.
310
+
311
+ Args:
312
+ data: Raw bytes.
313
+ value_offset: Offset to value data.
314
+ length: Value length.
315
+ tag_size: Size of tag header.
316
+
317
+ Returns:
318
+ Tuple of (value, bytes_consumed).
319
+
320
+ Raises:
321
+ ValueError: If invalid length or data too short.
322
+ """
323
+ import struct
324
+
325
+ if length != 8:
326
+ raise ValueError(f"Invalid double length: {length}")
327
+ if value_offset + 8 > len(data):
328
+ raise ValueError("Data too short for double")
329
+
330
+ double_value = struct.unpack(">d", data[value_offset : value_offset + 8])[0]
331
+ return double_value, tag_size + 8
332
+
333
+
334
+ def _parse_object_id_tag(
335
+ data: bytes, value_offset: int, length: int, tag_size: int
336
+ ) -> tuple[dict[str, Any], int]:
337
+ """Parse object identifier application tag.
338
+
339
+ Args:
340
+ data: Raw bytes.
341
+ value_offset: Offset to value data.
342
+ length: Value length.
343
+ tag_size: Size of tag header.
344
+
345
+ Returns:
346
+ Tuple of (obj_id_dict, bytes_consumed).
347
+
348
+ Raises:
349
+ ValueError: If invalid length.
350
+ """
351
+ if length != 4:
352
+ raise ValueError(f"Invalid object identifier length: {length}")
353
+ obj_id, _ = parse_object_identifier(data, value_offset)
354
+ return obj_id, tag_size + 4
355
+
356
+
357
+ def parse_application_tag(data: bytes, offset: int) -> tuple[Any, int]:
358
+ """Parse application-tagged data (standard BACnet data types).
359
+
360
+ Args:
361
+ data: Raw bytes to parse.
362
+ offset: Starting offset in data.
363
+
364
+ Returns:
365
+ Tuple of (decoded_value, bytes_consumed).
366
+
367
+ Raises:
368
+ ValueError: If invalid tag or data too short.
369
+
370
+ Example:
371
+ >>> data = bytes([0x21, 0x05]) # Unsigned int, length 1, value 5
372
+ >>> value, consumed = parse_application_tag(data, 0)
373
+ """
374
+ tag, tag_size = parse_tag(data, offset)
375
+
376
+ if tag["context_specific"]:
377
+ raise ValueError("Expected application tag, got context tag")
378
+
379
+ if tag["is_opening"] or tag["is_closing"]:
380
+ raise ValueError("Unexpected opening/closing tag")
381
+
382
+ value_offset = offset + tag_size
383
+ length = tag["length"]
384
+ tag_number = tag["tag_number"]
385
+
386
+ # Application tag numbers (ASHRAE 135-2020, Clause 20.2.1)
387
+ if tag_number == 0: # Null
388
+ return None, tag_size
389
+ elif tag_number == 1: # Boolean
390
+ return bool(length), tag_size
391
+ elif tag_number == 2: # Unsigned Integer
392
+ uint_value, _ = parse_unsigned(data, value_offset, length)
393
+ return uint_value, tag_size + length
394
+ elif tag_number == 3: # Signed Integer
395
+ return _parse_signed_integer(data, value_offset, length, tag_size)
396
+ elif tag_number == 4: # Real
397
+ return _parse_real(data, value_offset, length, tag_size)
398
+ elif tag_number == 5: # Double
399
+ return _parse_double(data, value_offset, length, tag_size)
400
+ elif tag_number == 7: # Character String
401
+ str_value, _ = parse_character_string(data, value_offset, length)
402
+ return str_value, tag_size + length
403
+ elif tag_number == 9: # Enumerated
404
+ enum_value, _ = parse_enumerated(data, value_offset, length)
405
+ return enum_value, tag_size + length
406
+ elif tag_number == 12: # Object Identifier
407
+ return _parse_object_id_tag(data, value_offset, length, tag_size)
408
+ else:
409
+ # Unknown/unsupported type - return raw bytes
410
+ if value_offset + length > len(data):
411
+ raise ValueError(f"Data too short for tag type {tag_number}")
412
+ return data[value_offset : value_offset + length], tag_size + length