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