oscura 0.0.1__py3-none-any.whl → 0.1.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 (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
oscura/loaders/pcap.py ADDED
@@ -0,0 +1,432 @@
1
+ """PCAP/PCAPNG packet capture file loader.
2
+
3
+ This module provides loading of packet capture files using dpkt
4
+ when available, with a basic fallback implementation.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.loaders.pcap import load_pcap
9
+ >>> packets = load_pcap("capture.pcap")
10
+ >>> for packet in packets:
11
+ ... print(f"Time: {packet.timestamp}, Size: {len(packet.data)} bytes")
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import struct
17
+ from dataclasses import dataclass
18
+ from pathlib import Path
19
+ from typing import TYPE_CHECKING, Any
20
+
21
+ from oscura.core.exceptions import FormatError, LoaderError
22
+ from oscura.core.types import ProtocolPacket
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import Iterator
26
+ from os import PathLike
27
+
28
+ # Try to import dpkt for full PCAP support
29
+ try:
30
+ import dpkt # type: ignore[import-not-found]
31
+
32
+ DPKT_AVAILABLE = True
33
+ except ImportError:
34
+ DPKT_AVAILABLE = False
35
+
36
+
37
+ # PCAP file format constants
38
+ PCAP_MAGIC_LE = 0xA1B2C3D4
39
+ PCAP_MAGIC_BE = 0xD4C3B2A1
40
+ PCAP_MAGIC_NS_LE = 0xA1B23C4D # Nanosecond resolution
41
+ PCAP_MAGIC_NS_BE = 0x4D3CB2A1
42
+ PCAPNG_MAGIC = 0x0A0D0D0A
43
+
44
+
45
+ @dataclass
46
+ class PcapPacketList:
47
+ """Container for PCAP packets with metadata.
48
+
49
+ Allows iteration over packets while preserving capture metadata.
50
+
51
+ Attributes:
52
+ packets: List of ProtocolPacket objects.
53
+ link_type: Link layer type (e.g., Ethernet = 1).
54
+ snaplen: Maximum capture length per packet.
55
+ source_file: Path to the source PCAP file.
56
+ """
57
+
58
+ packets: list[ProtocolPacket]
59
+ link_type: int = 1 # Ethernet
60
+ snaplen: int = 65535
61
+ source_file: str = ""
62
+
63
+ def __iter__(self) -> Iterator[ProtocolPacket]:
64
+ """Iterate over packets."""
65
+ return iter(self.packets)
66
+
67
+ def __len__(self) -> int:
68
+ """Return number of packets."""
69
+ return len(self.packets)
70
+
71
+ def __getitem__(self, index: int) -> ProtocolPacket:
72
+ """Get packet by index."""
73
+ return self.packets[index]
74
+
75
+ def filter(
76
+ self,
77
+ protocol: str | None = None,
78
+ min_size: int | None = None,
79
+ max_size: int | None = None,
80
+ ) -> list[ProtocolPacket]:
81
+ """Filter packets by criteria.
82
+
83
+ Args:
84
+ protocol: Filter by protocol annotation.
85
+ min_size: Minimum packet size in bytes.
86
+ max_size: Maximum packet size in bytes.
87
+
88
+ Returns:
89
+ Filtered list of packets.
90
+ """
91
+ result = self.packets
92
+
93
+ if protocol is not None:
94
+ result = [
95
+ p
96
+ for p in result
97
+ if p.annotations.get("layer3_protocol") == protocol
98
+ or p.annotations.get("layer4_protocol") == protocol
99
+ ]
100
+
101
+ if min_size is not None:
102
+ result = [p for p in result if len(p.data) >= min_size]
103
+
104
+ if max_size is not None:
105
+ result = [p for p in result if len(p.data) <= max_size]
106
+
107
+ return result
108
+
109
+
110
+ def load_pcap(
111
+ path: str | PathLike[str],
112
+ *,
113
+ protocol_filter: str | None = None,
114
+ max_packets: int | None = None,
115
+ ) -> PcapPacketList:
116
+ """Load a PCAP or PCAPNG packet capture file.
117
+
118
+ Extracts packets with timestamps and optional protocol annotations.
119
+ Uses dpkt library when available for full protocol dissection.
120
+
121
+ Args:
122
+ path: Path to the PCAP/PCAPNG file.
123
+ protocol_filter: Optional protocol filter (e.g., "TCP", "UDP").
124
+ max_packets: Maximum number of packets to load.
125
+
126
+ Returns:
127
+ PcapPacketList containing packets and capture metadata.
128
+
129
+ Raises:
130
+ LoaderError: If the file cannot be loaded.
131
+
132
+ Example:
133
+ >>> packets = load_pcap("network.pcap")
134
+ >>> print(f"Captured {len(packets)} packets")
135
+ >>> for pkt in packets[:5]:
136
+ ... print(f" {pkt.timestamp:.6f}s: {len(pkt.data)} bytes")
137
+
138
+ >>> # Filter by protocol
139
+ >>> tcp_packets = packets.filter(protocol="TCP")
140
+ """
141
+ path = Path(path)
142
+
143
+ if not path.exists():
144
+ raise LoaderError(
145
+ "File not found",
146
+ file_path=str(path),
147
+ )
148
+
149
+ if DPKT_AVAILABLE:
150
+ return _load_with_dpkt(
151
+ path,
152
+ protocol_filter=protocol_filter,
153
+ max_packets=max_packets,
154
+ )
155
+ else:
156
+ return _load_basic(
157
+ path,
158
+ protocol_filter=protocol_filter,
159
+ max_packets=max_packets,
160
+ )
161
+
162
+
163
+ def _load_with_dpkt(
164
+ path: Path,
165
+ *,
166
+ protocol_filter: str | None = None,
167
+ max_packets: int | None = None,
168
+ ) -> PcapPacketList:
169
+ """Load PCAP using dpkt library.
170
+
171
+ Args:
172
+ path: Path to the PCAP file.
173
+ protocol_filter: Optional protocol filter.
174
+ max_packets: Maximum packets to load.
175
+
176
+ Returns:
177
+ PcapPacketList with parsed packets.
178
+
179
+ Raises:
180
+ LoaderError: If file cannot be read or dpkt version is incompatible.
181
+ """
182
+ try:
183
+ with open(path, "rb") as f:
184
+ # Detect file format
185
+ magic = f.read(4)
186
+ f.seek(0)
187
+
188
+ magic_int = struct.unpack("<I", magic)[0]
189
+
190
+ if magic_int == PCAPNG_MAGIC:
191
+ # PCAPNG format
192
+ try:
193
+ pcap_reader = dpkt.pcapng.Reader(f)
194
+ except AttributeError:
195
+ raise LoaderError( # noqa: B904
196
+ "PCAPNG support requires newer dpkt version",
197
+ file_path=str(path),
198
+ fix_hint="Install dpkt >= 1.9: pip install dpkt>=1.9",
199
+ )
200
+ else:
201
+ # Standard PCAP format
202
+ pcap_reader = dpkt.pcap.Reader(f)
203
+
204
+ packets: list[ProtocolPacket] = []
205
+ link_type = getattr(pcap_reader, "datalink", lambda: 1)()
206
+
207
+ for timestamp, raw_data in pcap_reader:
208
+ if max_packets is not None and len(packets) >= max_packets:
209
+ break
210
+
211
+ # Parse Ethernet frame
212
+ annotations: dict[str, Any] = {}
213
+ protocol = "RAW"
214
+
215
+ try:
216
+ if link_type == 1: # Ethernet
217
+ eth = dpkt.ethernet.Ethernet(raw_data)
218
+ annotations["src_mac"] = _format_mac(eth.src)
219
+ annotations["dst_mac"] = _format_mac(eth.dst)
220
+
221
+ # Parse IP layer
222
+ if isinstance(eth.data, dpkt.ip.IP):
223
+ ip = eth.data
224
+ protocol = "IP"
225
+ annotations["src_ip"] = _format_ip(ip.src)
226
+ annotations["dst_ip"] = _format_ip(ip.dst)
227
+ annotations["layer3_protocol"] = "IP"
228
+
229
+ # Parse transport layer
230
+ if isinstance(ip.data, dpkt.tcp.TCP):
231
+ tcp = ip.data
232
+ protocol = "TCP"
233
+ annotations["src_port"] = tcp.sport
234
+ annotations["dst_port"] = tcp.dport
235
+ annotations["layer4_protocol"] = "TCP"
236
+ annotations["tcp_flags"] = tcp.flags
237
+
238
+ elif isinstance(ip.data, dpkt.udp.UDP):
239
+ udp = ip.data
240
+ protocol = "UDP"
241
+ annotations["src_port"] = udp.sport
242
+ annotations["dst_port"] = udp.dport
243
+ annotations["layer4_protocol"] = "UDP"
244
+
245
+ elif isinstance(ip.data, dpkt.icmp.ICMP):
246
+ protocol = "ICMP"
247
+ annotations["layer4_protocol"] = "ICMP"
248
+
249
+ elif isinstance(eth.data, dpkt.ip6.IP6):
250
+ protocol = "IPv6"
251
+ annotations["layer3_protocol"] = "IPv6"
252
+
253
+ elif isinstance(eth.data, dpkt.arp.ARP):
254
+ protocol = "ARP"
255
+ annotations["layer3_protocol"] = "ARP"
256
+
257
+ except Exception:
258
+ # If parsing fails, store raw data
259
+ pass
260
+
261
+ # Apply protocol filter
262
+ if protocol_filter is not None and (
263
+ annotations.get("layer3_protocol") != protocol_filter
264
+ and annotations.get("layer4_protocol") != protocol_filter
265
+ and protocol != protocol_filter
266
+ ):
267
+ continue
268
+
269
+ packet = ProtocolPacket(
270
+ timestamp=float(timestamp),
271
+ protocol=protocol,
272
+ data=bytes(raw_data),
273
+ annotations=annotations,
274
+ )
275
+ packets.append(packet)
276
+
277
+ return PcapPacketList(
278
+ packets=packets,
279
+ link_type=link_type,
280
+ source_file=str(path),
281
+ )
282
+
283
+ except Exception as e:
284
+ if isinstance(e, LoaderError | FormatError):
285
+ raise
286
+ raise LoaderError(
287
+ "Failed to load PCAP file",
288
+ file_path=str(path),
289
+ details=str(e),
290
+ fix_hint="Ensure the file is a valid PCAP/PCAPNG format.",
291
+ ) from e
292
+
293
+
294
+ def _load_basic(
295
+ path: Path,
296
+ *,
297
+ protocol_filter: str | None = None,
298
+ max_packets: int | None = None,
299
+ ) -> PcapPacketList:
300
+ """Basic PCAP loader without dpkt.
301
+
302
+ Args:
303
+ path: Path to the PCAP file.
304
+ protocol_filter: Optional protocol filter (not supported in basic mode).
305
+ max_packets: Maximum packets to load.
306
+
307
+ Returns:
308
+ PcapPacketList with raw packet data.
309
+
310
+ Raises:
311
+ FormatError: If file is not a valid PCAP.
312
+ LoaderError: If file cannot be read.
313
+ """
314
+ try:
315
+ with open(path, "rb") as f:
316
+ # Read global header (24 bytes)
317
+ header = f.read(24)
318
+ if len(header) < 24:
319
+ raise FormatError(
320
+ "File too small to be a valid PCAP",
321
+ file_path=str(path),
322
+ expected="At least 24 bytes",
323
+ got=f"{len(header)} bytes",
324
+ )
325
+
326
+ # Parse magic number
327
+ magic = struct.unpack("<I", header[:4])[0]
328
+
329
+ if magic in (PCAP_MAGIC_LE, PCAP_MAGIC_NS_LE):
330
+ byte_order = "<"
331
+ nanosecond = magic == PCAP_MAGIC_NS_LE
332
+ elif magic in (PCAP_MAGIC_BE, PCAP_MAGIC_NS_BE):
333
+ byte_order = ">"
334
+ nanosecond = magic == PCAP_MAGIC_NS_BE
335
+ elif magic == PCAPNG_MAGIC:
336
+ raise LoaderError(
337
+ "PCAPNG format requires dpkt library",
338
+ file_path=str(path),
339
+ fix_hint="Install dpkt: pip install dpkt",
340
+ )
341
+ else:
342
+ raise FormatError(
343
+ "Invalid PCAP magic number",
344
+ file_path=str(path),
345
+ expected="PCAP magic (0xa1b2c3d4)",
346
+ got=f"0x{magic:08x}",
347
+ )
348
+
349
+ # Parse rest of header (version_major, version_minor, thiszone, sigfigs, snaplen, network)
350
+ _, _, _, _, snaplen, link_type = struct.unpack(f"{byte_order}HHiIII", header[4:])
351
+
352
+ packets: list[ProtocolPacket] = []
353
+
354
+ # Read packets
355
+ while True:
356
+ if max_packets is not None and len(packets) >= max_packets:
357
+ break
358
+
359
+ # Read packet header (16 bytes)
360
+ pkt_header = f.read(16)
361
+ if len(pkt_header) < 16:
362
+ break
363
+
364
+ ts_sec, ts_usec, incl_len, orig_len = struct.unpack(f"{byte_order}IIII", pkt_header)
365
+
366
+ # Calculate timestamp
367
+ if nanosecond:
368
+ timestamp = ts_sec + ts_usec / 1e9
369
+ else:
370
+ timestamp = ts_sec + ts_usec / 1e6
371
+
372
+ # Read packet data
373
+ pkt_data = f.read(incl_len)
374
+ if len(pkt_data) < incl_len:
375
+ break
376
+
377
+ packet = ProtocolPacket(
378
+ timestamp=timestamp,
379
+ protocol="RAW",
380
+ data=bytes(pkt_data),
381
+ annotations={"original_length": orig_len},
382
+ )
383
+ packets.append(packet)
384
+
385
+ return PcapPacketList(
386
+ packets=packets,
387
+ link_type=link_type,
388
+ snaplen=snaplen,
389
+ source_file=str(path),
390
+ )
391
+
392
+ except struct.error as e:
393
+ raise FormatError(
394
+ "Corrupted PCAP file",
395
+ file_path=str(path),
396
+ ) from e
397
+ except Exception as e:
398
+ if isinstance(e, LoaderError | FormatError):
399
+ raise
400
+ raise LoaderError(
401
+ "Failed to load PCAP file",
402
+ file_path=str(path),
403
+ details=str(e),
404
+ fix_hint="Install dpkt for full PCAP support: pip install dpkt",
405
+ ) from e
406
+
407
+
408
+ def _format_mac(mac_bytes: bytes) -> str:
409
+ """Format MAC address bytes to string.
410
+
411
+ Args:
412
+ mac_bytes: 6-byte MAC address.
413
+
414
+ Returns:
415
+ MAC address string (e.g., "00:11:22:33:44:55").
416
+ """
417
+ return ":".join(f"{b:02x}" for b in mac_bytes)
418
+
419
+
420
+ def _format_ip(ip_bytes: bytes) -> str:
421
+ """Format IPv4 address bytes to string.
422
+
423
+ Args:
424
+ ip_bytes: 4-byte IPv4 address.
425
+
426
+ Returns:
427
+ IPv4 address string (e.g., "192.168.1.1").
428
+ """
429
+ return ".".join(str(b) for b in ip_bytes)
430
+
431
+
432
+ __all__ = ["PcapPacketList", "load_pcap"]