oscura 0.0.1__py3-none-any.whl → 0.1.1__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.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.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.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,707 @@
1
+ """Unknown signal analysis and reverse engineering.
2
+
3
+ This module provides tools for analyzing signals from unknown systems
4
+ and protocols, including binary field detection and pattern analysis.
5
+
6
+ - UNKNOWN-001: Binary Field Detection
7
+ - UNKNOWN-002: Protocol Auto-Detection with Fuzzy Matching
8
+ - UNKNOWN-003: Unknown Signal Characterization
9
+ - UNKNOWN-004: Pattern Frequency Analysis
10
+ - UNKNOWN-005: Reverse Engineering Workflow
11
+
12
+ Example:
13
+ >>> from oscura.exploratory.unknown import characterize_unknown_signal
14
+ >>> result = characterize_unknown_signal(trace)
15
+ >>> print(f"Signal type: {result.signal_type}")
16
+ >>> print(f"Suggested protocols: {result.suggested_protocols}")
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from dataclasses import dataclass, field
22
+ from typing import TYPE_CHECKING, Any, Literal
23
+
24
+ import numpy as np
25
+
26
+ if TYPE_CHECKING:
27
+ from numpy.typing import NDArray
28
+
29
+ from oscura.core.types import WaveformTrace
30
+
31
+
32
+ @dataclass
33
+ class BinaryFieldResult:
34
+ """Result of binary field detection.
35
+
36
+ Attributes:
37
+ fields: List of detected fields with positions.
38
+ field_count: Total number of fields detected.
39
+ bit_rate: Estimated bit rate in bps.
40
+ encoding: Detected encoding type.
41
+ confidence: Detection confidence.
42
+ """
43
+
44
+ fields: list[dict[str, Any]]
45
+ field_count: int
46
+ bit_rate: float | None
47
+ encoding: str
48
+ confidence: float
49
+
50
+
51
+ def detect_binary_fields(
52
+ trace: WaveformTrace,
53
+ *,
54
+ min_field_bits: int = 4,
55
+ max_gap_ratio: float = 2.0,
56
+ ) -> BinaryFieldResult:
57
+ """Detect binary fields in unknown signal per UNKNOWN-001.
58
+
59
+ Analyzes signal for structured binary data patterns including
60
+ start/stop markers, length fields, and data payloads.
61
+
62
+ Args:
63
+ trace: Signal trace to analyze.
64
+ min_field_bits: Minimum bits to consider a field.
65
+ max_gap_ratio: Maximum gap ratio for field boundaries.
66
+
67
+ Returns:
68
+ BinaryFieldResult with detected fields.
69
+
70
+ Example:
71
+ >>> result = detect_binary_fields(trace)
72
+ >>> for field in result.fields:
73
+ ... print(f"Field at {field['start_sample']}: {field['length']} bits")
74
+
75
+ References:
76
+ UNKNOWN-001: Binary Field Detection
77
+ """
78
+ data = trace.data
79
+ sample_rate = trace.metadata.sample_rate
80
+
81
+ # Threshold for digital conversion
82
+ v_min = np.percentile(data, 5)
83
+ v_max = np.percentile(data, 95)
84
+ threshold = (v_min + v_max) / 2
85
+
86
+ # Convert to digital
87
+ digital = (data > threshold).astype(int)
88
+
89
+ # Find edges
90
+ edges = np.where(np.diff(digital) != 0)[0]
91
+
92
+ if len(edges) < 2:
93
+ return BinaryFieldResult(
94
+ fields=[],
95
+ field_count=0,
96
+ bit_rate=None,
97
+ encoding="unknown",
98
+ confidence=0.0,
99
+ )
100
+
101
+ # Estimate bit period from edge spacing
102
+ edge_gaps = np.diff(edges)
103
+ median_gap = np.median(edge_gaps)
104
+ bit_period = median_gap
105
+
106
+ # Group edges into fields
107
+ fields = []
108
+ current_field_start = edges[0]
109
+ current_field_edges = [edges[0]]
110
+
111
+ for i in range(1, len(edges)):
112
+ gap = edges[i] - edges[i - 1]
113
+
114
+ if gap > max_gap_ratio * bit_period:
115
+ # End current field
116
+ if len(current_field_edges) >= min_field_bits:
117
+ current_field_edges[-1] - current_field_edges[0]
118
+ n_bits = len(current_field_edges) - 1
119
+
120
+ # Extract bit pattern
121
+ bits = []
122
+ for j in range(len(current_field_edges) - 1):
123
+ start = current_field_edges[j]
124
+ end = current_field_edges[j + 1]
125
+ mid = (start + end) // 2
126
+ bits.append(digital[mid])
127
+
128
+ fields.append(
129
+ {
130
+ "start_sample": int(current_field_start),
131
+ "end_sample": int(current_field_edges[-1]),
132
+ "length": n_bits,
133
+ "bits": bits,
134
+ "timestamp": current_field_start / sample_rate,
135
+ }
136
+ )
137
+
138
+ # Start new field
139
+ current_field_start = edges[i]
140
+ current_field_edges = [edges[i]]
141
+ else:
142
+ current_field_edges.append(edges[i])
143
+
144
+ # Handle last field
145
+ if len(current_field_edges) >= min_field_bits:
146
+ current_field_edges[-1] - current_field_edges[0]
147
+ n_bits = len(current_field_edges) - 1
148
+ bits = []
149
+ for j in range(len(current_field_edges) - 1):
150
+ start = current_field_edges[j]
151
+ end = current_field_edges[j + 1]
152
+ mid = (start + end) // 2
153
+ bits.append(digital[mid])
154
+
155
+ fields.append(
156
+ {
157
+ "start_sample": int(current_field_start),
158
+ "end_sample": int(current_field_edges[-1]),
159
+ "length": n_bits,
160
+ "bits": bits,
161
+ "timestamp": current_field_start / sample_rate,
162
+ }
163
+ )
164
+
165
+ # Estimate bit rate
166
+ bit_rate = sample_rate / bit_period if bit_period > 0 else None
167
+
168
+ # Detect encoding
169
+ encoding = _detect_encoding(digital, edges, bit_period)
170
+
171
+ # Calculate confidence
172
+ confidence = min(1.0, len(fields) / 10.0) * 0.8
173
+ if bit_rate is not None:
174
+ confidence += 0.2
175
+
176
+ return BinaryFieldResult(
177
+ fields=fields,
178
+ field_count=len(fields),
179
+ bit_rate=bit_rate,
180
+ encoding=encoding,
181
+ confidence=confidence,
182
+ )
183
+
184
+
185
+ def _detect_encoding(
186
+ digital: NDArray[np.int_],
187
+ edges: NDArray[np.int_],
188
+ bit_period: float,
189
+ ) -> str:
190
+ """Detect signal encoding type.
191
+
192
+ Args:
193
+ digital: Digital signal.
194
+ edges: Edge positions.
195
+ bit_period: Estimated bit period.
196
+
197
+ Returns:
198
+ Encoding type name.
199
+ """
200
+ if len(edges) < 4:
201
+ return "unknown"
202
+
203
+ # Analyze edge spacing patterns
204
+ gaps = np.diff(edges)
205
+
206
+ # Check for Manchester (edges every half bit)
207
+ if np.std(gaps) < bit_period * 0.3:
208
+ return "manchester"
209
+
210
+ # Check for NRZ (edges at bit boundaries)
211
+ normalized_gaps = gaps / bit_period
212
+ integer_gaps = np.round(normalized_gaps)
213
+ residuals = np.abs(normalized_gaps - integer_gaps)
214
+
215
+ if np.mean(residuals) < 0.2:
216
+ return "nrz"
217
+
218
+ # Check for NRZI
219
+ if np.mean(normalized_gaps > 0.8) > 0.7:
220
+ return "nrzi"
221
+
222
+ return "unknown"
223
+
224
+
225
+ @dataclass
226
+ class UnknownSignalCharacterization:
227
+ """Comprehensive characterization of unknown signal.
228
+
229
+ Attributes:
230
+ signal_type: 'digital', 'analog', or 'mixed'.
231
+ is_periodic: True if signal is periodic.
232
+ fundamental_frequency: Fundamental frequency if periodic.
233
+ dc_offset: DC offset voltage.
234
+ amplitude: Signal amplitude.
235
+ rise_time: Estimated rise time.
236
+ fall_time: Estimated fall time.
237
+ suggested_protocols: List of possible protocols.
238
+ noise_floor: Estimated noise floor.
239
+ snr_db: Signal-to-noise ratio in dB.
240
+ features: Dictionary of extracted features.
241
+ """
242
+
243
+ signal_type: Literal["digital", "analog", "mixed"]
244
+ is_periodic: bool
245
+ fundamental_frequency: float | None
246
+ dc_offset: float
247
+ amplitude: float
248
+ rise_time: float | None
249
+ fall_time: float | None
250
+ suggested_protocols: list[tuple[str, float]]
251
+ noise_floor: float
252
+ snr_db: float
253
+ features: dict[str, Any] = field(default_factory=dict)
254
+
255
+
256
+ def characterize_unknown_signal(
257
+ trace: WaveformTrace,
258
+ ) -> UnknownSignalCharacterization:
259
+ """Comprehensive characterization of unknown signal per UNKNOWN-003.
260
+
261
+ Analyzes signal characteristics to determine type, periodicity,
262
+ and suggest possible protocols.
263
+
264
+ Args:
265
+ trace: Signal trace to characterize.
266
+
267
+ Returns:
268
+ UnknownSignalCharacterization with all extracted features.
269
+
270
+ Example:
271
+ >>> result = characterize_unknown_signal(trace)
272
+ >>> print(f"Signal type: {result.signal_type}")
273
+ >>> print(f"Periodic: {result.is_periodic}")
274
+ >>> for protocol, confidence in result.suggested_protocols:
275
+ ... print(f" {protocol}: {confidence:.1%}")
276
+
277
+ References:
278
+ UNKNOWN-003: Unknown Signal Characterization
279
+ """
280
+ data = trace.data
281
+ sample_rate = trace.metadata.sample_rate
282
+
283
+ # Handle edge case of very short traces
284
+ if len(data) < 2:
285
+ return UnknownSignalCharacterization(
286
+ signal_type="analog",
287
+ is_periodic=False,
288
+ fundamental_frequency=None,
289
+ dc_offset=float(data[0]) if len(data) > 0 else 0.0,
290
+ amplitude=0.0,
291
+ rise_time=None,
292
+ fall_time=None,
293
+ suggested_protocols=[],
294
+ noise_floor=0.0,
295
+ snr_db=float("inf"),
296
+ features={},
297
+ )
298
+
299
+ # Basic statistics
300
+ v_min = np.min(data)
301
+ v_max = np.max(data)
302
+ v_mean = np.mean(data)
303
+ v_std = np.std(data)
304
+
305
+ dc_offset = v_mean
306
+ amplitude = (v_max - v_min) / 2
307
+
308
+ # Determine signal type
309
+ # Digital signals have bimodal distribution
310
+ hist, bin_edges = np.histogram(data, bins=50)
311
+ centers = (bin_edges[:-1] + bin_edges[1:]) / 2
312
+
313
+ # Find peaks in histogram
314
+ peaks = []
315
+ for i in range(1, len(hist) - 1):
316
+ if hist[i] > hist[i - 1] and hist[i] > hist[i + 1] and hist[i] > 0.1 * np.max(hist):
317
+ peaks.append((centers[i], hist[i]))
318
+
319
+ if len(peaks) >= 4:
320
+ # Many peaks suggest analog signal (e.g., sine wave with noisy histogram)
321
+ signal_type: Literal["digital", "analog", "mixed"] = "analog"
322
+ elif len(peaks) == 2 or len(peaks) == 3:
323
+ # Two peaks suggest digital (bimodal), but check if they're well-separated
324
+ peak_positions = [p[0] for p in peaks]
325
+ # Normalize peak positions to 0-1 range
326
+ normalized_peaks = [(p - v_min) / (v_max - v_min) for p in peak_positions]
327
+
328
+ # If peaks are well-separated (one < 0.4, one > 0.6), likely digital
329
+ has_low_peak = any(p < 0.4 for p in normalized_peaks)
330
+ has_high_peak = any(p > 0.6 for p in normalized_peaks)
331
+
332
+ if has_low_peak and has_high_peak:
333
+ signal_type = "digital"
334
+ else:
335
+ # Peaks not well separated, likely analog
336
+ signal_type = "analog"
337
+ elif len(peaks) == 1:
338
+ # Check for modulated signal
339
+ signal_type = "mixed" if v_std > 0.2 * amplitude else "analog"
340
+ else:
341
+ signal_type = "analog"
342
+
343
+ # Check periodicity via FFT
344
+ from scipy import signal as sp_signal
345
+
346
+ n = len(data)
347
+ # Need at least 4 samples for meaningful FFT analysis
348
+ if n >= 4:
349
+ f, psd = sp_signal.welch(data, fs=sample_rate, nperseg=min(4096, n))
350
+
351
+ # Find dominant frequency (excluding DC)
352
+ psd_no_dc = psd.copy()
353
+ psd_no_dc[0] = 0
354
+
355
+ if len(psd_no_dc) > 0 and np.any(psd_no_dc > 0):
356
+ peak_idx = np.argmax(psd_no_dc)
357
+ mean_psd = np.mean(psd_no_dc[psd_no_dc > 0]) if np.any(psd_no_dc > 0) else 0
358
+ fundamental_frequency = f[peak_idx] if psd_no_dc[peak_idx] > 10 * mean_psd else None
359
+ else:
360
+ fundamental_frequency = None
361
+
362
+ is_periodic = fundamental_frequency is not None
363
+ else:
364
+ fundamental_frequency = None
365
+ is_periodic = False
366
+
367
+ # Estimate noise floor
368
+ if n >= 4:
369
+ noise_floor = np.median(np.sort(psd)[: len(psd) // 4]) if len(psd) > 0 else 0.0
370
+ signal_power = np.max(psd) - noise_floor if len(psd) > 0 else 0.0
371
+ else:
372
+ noise_floor = 0.0
373
+ signal_power = 0.0
374
+ snr_db = 10 * np.log10(signal_power / noise_floor) if noise_floor > 0 else 0
375
+
376
+ # Estimate rise/fall times for digital signals
377
+ rise_time = None
378
+ fall_time = None
379
+
380
+ if signal_type == "digital":
381
+ threshold_low = v_min + 0.1 * (v_max - v_min)
382
+ threshold_high = v_min + 0.9 * (v_max - v_min)
383
+
384
+ # Find rising edges
385
+ rising_times = []
386
+ falling_times = []
387
+
388
+ for i in range(1, len(data) - 1):
389
+ if data[i - 1] < threshold_low and data[i + 1] > threshold_high:
390
+ # Rising edge
391
+ rising_times.append(1 / sample_rate)
392
+ elif data[i - 1] > threshold_high and data[i + 1] < threshold_low:
393
+ # Falling edge
394
+ falling_times.append(1 / sample_rate)
395
+
396
+ if rising_times:
397
+ rise_time = float(np.median(rising_times))
398
+ if falling_times:
399
+ fall_time = float(np.median(falling_times))
400
+
401
+ # Suggest protocols
402
+ suggested_protocols = _suggest_protocols(signal_type, fundamental_frequency, sample_rate, data)
403
+
404
+ # Collect features
405
+ features = {
406
+ "v_min": v_min,
407
+ "v_max": v_max,
408
+ "v_mean": v_mean,
409
+ "v_std": v_std,
410
+ "crest_factor": v_max / np.sqrt(np.mean(data**2)) if np.mean(data**2) > 0 else 0,
411
+ "n_peaks": len(peaks),
412
+ "peak_positions": [p[0] for p in peaks],
413
+ }
414
+
415
+ return UnknownSignalCharacterization(
416
+ signal_type=signal_type,
417
+ is_periodic=is_periodic,
418
+ fundamental_frequency=fundamental_frequency,
419
+ dc_offset=dc_offset,
420
+ amplitude=amplitude,
421
+ rise_time=rise_time,
422
+ fall_time=fall_time,
423
+ suggested_protocols=suggested_protocols,
424
+ noise_floor=noise_floor,
425
+ snr_db=snr_db,
426
+ features=features,
427
+ )
428
+
429
+
430
+ def _suggest_protocols(
431
+ signal_type: str,
432
+ frequency: float | None,
433
+ sample_rate: float,
434
+ data: NDArray[np.float64],
435
+ ) -> list[tuple[str, float]]:
436
+ """Suggest possible protocols based on signal characteristics.
437
+
438
+ Args:
439
+ signal_type: Signal type (digital/analog/mixed).
440
+ frequency: Fundamental frequency.
441
+ sample_rate: Sample rate.
442
+ data: Signal data.
443
+
444
+ Returns:
445
+ List of (protocol_name, confidence) tuples.
446
+ """
447
+ suggestions = [] # type: ignore[var-annotated]
448
+
449
+ if signal_type != "digital":
450
+ return suggestions
451
+
452
+ # Estimate bit rate from signal
453
+ v_min = np.percentile(data, 5)
454
+ v_max = np.percentile(data, 95)
455
+ threshold = (v_min + v_max) / 2
456
+ digital = data > threshold
457
+ edges = np.where(np.diff(digital.astype(int)) != 0)[0]
458
+
459
+ if len(edges) < 2:
460
+ return suggestions
461
+
462
+ median_gap = np.median(np.diff(edges))
463
+ estimated_bitrate = sample_rate / median_gap
464
+
465
+ # Check common baud rates for UART
466
+ uart_rates = [9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600]
467
+ for rate in uart_rates:
468
+ ratio = estimated_bitrate / rate
469
+ if 0.9 <= ratio <= 1.1:
470
+ suggestions.append(("UART", 0.7 + 0.3 * (1 - abs(1 - ratio))))
471
+ break
472
+
473
+ # Check for I2C (two-wire, specific timing)
474
+ if 50e3 <= estimated_bitrate <= 400e3:
475
+ suggestions.append(("I2C", 0.5))
476
+ elif 400e3 < estimated_bitrate <= 3.4e6:
477
+ suggestions.append(("I2C Fast Mode", 0.5))
478
+
479
+ # Check for SPI (higher speeds)
480
+ if estimated_bitrate >= 1e6:
481
+ suggestions.append(("SPI", 0.4))
482
+
483
+ # Check for CAN
484
+ can_rates = [125e3, 250e3, 500e3, 1e6]
485
+ for rate in can_rates: # type: ignore[assignment]
486
+ if 0.9 <= estimated_bitrate / rate <= 1.1:
487
+ suggestions.append(("CAN", 0.6))
488
+ break
489
+
490
+ # Sort by confidence
491
+ suggestions.sort(key=lambda x: x[1], reverse=True)
492
+
493
+ return suggestions
494
+
495
+
496
+ @dataclass
497
+ class PatternFrequencyResult:
498
+ """Result of pattern frequency analysis.
499
+
500
+ Attributes:
501
+ patterns: Dictionary of pattern to count.
502
+ most_common: List of (pattern, count) for most common patterns.
503
+ entropy: Shannon entropy of pattern distribution.
504
+ repetition_rate: Rate of pattern repetition.
505
+ """
506
+
507
+ patterns: dict[tuple[int, ...], int]
508
+ most_common: list[tuple[tuple[int, ...], int]]
509
+ entropy: float
510
+ repetition_rate: float
511
+
512
+
513
+ def analyze_pattern_frequency(
514
+ trace: WaveformTrace,
515
+ *,
516
+ pattern_length: int = 8,
517
+ min_occurrences: int = 2,
518
+ ) -> PatternFrequencyResult:
519
+ """Analyze frequency of bit patterns per UNKNOWN-004.
520
+
521
+ Identifies recurring patterns that may indicate protocol structure
522
+ or data framing.
523
+
524
+ Args:
525
+ trace: Signal trace to analyze.
526
+ pattern_length: Length of patterns to search for.
527
+ min_occurrences: Minimum occurrences to report.
528
+
529
+ Returns:
530
+ PatternFrequencyResult with pattern statistics.
531
+
532
+ Example:
533
+ >>> result = analyze_pattern_frequency(trace, pattern_length=8)
534
+ >>> for pattern, count in result.most_common[:5]:
535
+ ... print(f"Pattern {pattern}: {count} occurrences")
536
+
537
+ References:
538
+ UNKNOWN-004: Pattern Frequency Analysis
539
+ """
540
+ data = trace.data
541
+
542
+ # Convert to digital
543
+ v_min = np.percentile(data, 5)
544
+ v_max = np.percentile(data, 95)
545
+ threshold = (v_min + v_max) / 2
546
+ digital = (data > threshold).astype(int)
547
+
548
+ # Find bit boundaries from edges
549
+ edges = np.where(np.diff(digital) != 0)[0]
550
+
551
+ if len(edges) < 2:
552
+ return PatternFrequencyResult(
553
+ patterns={},
554
+ most_common=[],
555
+ entropy=0.0,
556
+ repetition_rate=0.0,
557
+ )
558
+
559
+ # Estimate bit period
560
+ median_gap = np.median(np.diff(edges))
561
+
562
+ # Sample at bit centers
563
+ bits = []
564
+ sample_pos = edges[0] + median_gap / 2
565
+
566
+ while sample_pos < len(digital):
567
+ idx = int(sample_pos)
568
+ if idx < len(digital):
569
+ bits.append(digital[idx])
570
+ sample_pos += median_gap
571
+
572
+ # Count patterns
573
+ patterns: dict[tuple[int, ...], int] = {}
574
+
575
+ for i in range(len(bits) - pattern_length + 1):
576
+ pattern = tuple(bits[i : i + pattern_length])
577
+ patterns[pattern] = patterns.get(pattern, 0) + 1
578
+
579
+ # Filter by minimum occurrences
580
+ patterns = {p: c for p, c in patterns.items() if c >= min_occurrences}
581
+
582
+ # Find most common
583
+ most_common = sorted(patterns.items(), key=lambda x: x[1], reverse=True)[:20]
584
+
585
+ # Calculate entropy
586
+ total = sum(patterns.values())
587
+ if total > 0:
588
+ probs = np.array(list(patterns.values())) / total
589
+ entropy = -np.sum(probs * np.log2(probs + 1e-10))
590
+ else:
591
+ entropy = 0.0
592
+
593
+ # Repetition rate
594
+ repetition_rate = 1 - len(patterns) / total if total > 0 else 0.0
595
+
596
+ return PatternFrequencyResult(
597
+ patterns=patterns,
598
+ most_common=most_common,
599
+ entropy=entropy,
600
+ repetition_rate=repetition_rate,
601
+ )
602
+
603
+
604
+ @dataclass
605
+ class ReverseEngineeringResult:
606
+ """Result of reverse engineering workflow.
607
+
608
+ Attributes:
609
+ signal_char: Signal characterization.
610
+ binary_fields: Detected binary fields.
611
+ pattern_analysis: Pattern frequency analysis.
612
+ protocol_hypothesis: Most likely protocol.
613
+ confidence: Overall confidence.
614
+ recommendations: List of next steps.
615
+ """
616
+
617
+ signal_char: UnknownSignalCharacterization
618
+ binary_fields: BinaryFieldResult
619
+ pattern_analysis: PatternFrequencyResult
620
+ protocol_hypothesis: str
621
+ confidence: float
622
+ recommendations: list[str]
623
+
624
+
625
+ def reverse_engineer_protocol(
626
+ trace: WaveformTrace,
627
+ ) -> ReverseEngineeringResult:
628
+ """Comprehensive reverse engineering workflow per UNKNOWN-005.
629
+
630
+ Combines all unknown signal analysis techniques to build
631
+ a hypothesis about the protocol in use.
632
+
633
+ Args:
634
+ trace: Signal trace to reverse engineer.
635
+
636
+ Returns:
637
+ ReverseEngineeringResult with comprehensive analysis.
638
+
639
+ Example:
640
+ >>> result = reverse_engineer_protocol(trace)
641
+ >>> print(f"Protocol hypothesis: {result.protocol_hypothesis}")
642
+ >>> print(f"Confidence: {result.confidence:.1%}")
643
+ >>> for rec in result.recommendations:
644
+ ... print(f"- {rec}")
645
+
646
+ References:
647
+ UNKNOWN-005: Reverse Engineering Workflow
648
+ """
649
+ # Run all analysis steps
650
+ signal_char = characterize_unknown_signal(trace)
651
+ binary_fields = detect_binary_fields(trace)
652
+ pattern_analysis = analyze_pattern_frequency(trace)
653
+
654
+ # Build hypothesis
655
+ protocol_hypothesis = "Unknown"
656
+ confidence = 0.0
657
+
658
+ if signal_char.suggested_protocols:
659
+ protocol_hypothesis = signal_char.suggested_protocols[0][0]
660
+ confidence = signal_char.suggested_protocols[0][1]
661
+
662
+ # Generate recommendations
663
+ recommendations = []
664
+
665
+ if signal_char.signal_type != "digital":
666
+ recommendations.append("Signal appears analog - check if correct probe/channel")
667
+
668
+ if binary_fields.field_count == 0:
669
+ recommendations.append("No binary fields detected - try adjusting threshold")
670
+
671
+ if binary_fields.encoding == "manchester":
672
+ recommendations.append("Manchester encoding detected - common in Ethernet, 1-Wire")
673
+
674
+ if pattern_analysis.repetition_rate > 0.5:
675
+ recommendations.append(
676
+ "High pattern repetition - likely periodic protocol (e.g., I2C polling)"
677
+ )
678
+
679
+ if signal_char.snr_db < 10:
680
+ recommendations.append("Low SNR - consider using averaging or filtering")
681
+
682
+ if not signal_char.suggested_protocols:
683
+ recommendations.append("No protocol match - try capturing with different settings")
684
+
685
+ if binary_fields.bit_rate is not None:
686
+ recommendations.append(f"Estimated bit rate: {binary_fields.bit_rate:.0f} bps")
687
+
688
+ return ReverseEngineeringResult(
689
+ signal_char=signal_char,
690
+ binary_fields=binary_fields,
691
+ pattern_analysis=pattern_analysis,
692
+ protocol_hypothesis=protocol_hypothesis,
693
+ confidence=confidence,
694
+ recommendations=recommendations,
695
+ )
696
+
697
+
698
+ __all__ = [
699
+ "BinaryFieldResult",
700
+ "PatternFrequencyResult",
701
+ "ReverseEngineeringResult",
702
+ "UnknownSignalCharacterization",
703
+ "analyze_pattern_frequency",
704
+ "characterize_unknown_signal",
705
+ "detect_binary_fields",
706
+ "reverse_engineer_protocol",
707
+ ]