RTL-SDR Support (Expermential)
This commit is contained in:
Generated
+189
-14
@@ -73,6 +73,12 @@ version = "3.19.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@@ -195,7 +201,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -206,9 +212,15 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "difflib"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "5.0.1"
|
version = "5.0.1"
|
||||||
@@ -230,6 +242,12 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -258,12 +276,27 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fragile"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "funty"
|
name = "funty"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -372,7 +405,16 @@ dependencies = [
|
|||||||
"indoc",
|
"indoc",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -415,6 +457,7 @@ dependencies = [
|
|||||||
"num-complex",
|
"num-complex",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"rtl-sdr-rs",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
@@ -455,6 +498,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libusb1-sys"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.15"
|
version = "0.4.15"
|
||||||
@@ -512,6 +567,39 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mockall"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"downcast",
|
||||||
|
"fragile",
|
||||||
|
"lazy_static",
|
||||||
|
"mockall_derive",
|
||||||
|
"predicates",
|
||||||
|
"predicates-tree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mockall_derive"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "normalize-line-endings"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.50.3"
|
version = "0.50.3"
|
||||||
@@ -592,6 +680,36 @@ version = "0.3.32"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates"
|
||||||
|
version = "2.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
|
||||||
|
dependencies = [
|
||||||
|
"difflib",
|
||||||
|
"float-cmp",
|
||||||
|
"itertools 0.10.5",
|
||||||
|
"normalize-line-endings",
|
||||||
|
"predicates-core",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-core"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-tree"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2"
|
||||||
|
dependencies = [
|
||||||
|
"predicates-core",
|
||||||
|
"termtree",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.106"
|
version = "1.0.106"
|
||||||
@@ -628,7 +746,7 @@ dependencies = [
|
|||||||
"crossterm",
|
"crossterm",
|
||||||
"indoc",
|
"indoc",
|
||||||
"instability",
|
"instability",
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"lru",
|
"lru",
|
||||||
"paste",
|
"paste",
|
||||||
"strum",
|
"strum",
|
||||||
@@ -657,6 +775,18 @@ dependencies = [
|
|||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@@ -674,6 +804,28 @@ version = "0.8.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtl-sdr-rs"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3e39d1d957178d9bc3d2641e41ea083205f304b6b2db4f6efd60bc45498aa82"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"log",
|
||||||
|
"mockall",
|
||||||
|
"rusb",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusb"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab9f9ff05b63a786553a4c02943b74b34a988448671001e9a27e2f0565cc05a4"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"libusb1-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.44"
|
version = "0.38.44"
|
||||||
@@ -732,7 +884,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -850,7 +1002,18 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -870,6 +1033,12 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termtree"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@@ -896,7 +1065,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -907,7 +1076,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -944,7 +1113,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1007,7 +1176,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1067,7 +1236,7 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width 0.1.14",
|
"unicode-width 0.1.14",
|
||||||
]
|
]
|
||||||
@@ -1090,6 +1259,12 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
@@ -1128,7 +1303,7 @@ dependencies = [
|
|||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1184,7 +1359,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1195,7 +1370,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.114",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+59
-56
@@ -1,56 +1,59 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kat"
|
name = "kat"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Keyfob Analysis Toolkit - HackRF signal capture, decode, and transmit tool"
|
description = "Keyfob Analysis Toolkit - HackRF/RTL-SDR signal capture, decode, and transmit (HackRF only)"
|
||||||
authors = ["KAT Team"]
|
authors = ["KAT Team"]
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# TUI
|
# TUI
|
||||||
ratatui = "0.29"
|
ratatui = "0.29"
|
||||||
crossterm = "0.28"
|
crossterm = "0.28"
|
||||||
|
|
||||||
# HackRF library
|
# HackRF library
|
||||||
libhackrf = "0.1"
|
libhackrf = "0.1"
|
||||||
|
|
||||||
# Signal processing
|
# RTL-SDR (receive-only support, e.g. RTL433 hardware)
|
||||||
num-complex = "0.4"
|
rtl-sdr-rs = "0.3"
|
||||||
|
|
||||||
# Async runtime
|
# Signal processing
|
||||||
tokio = { version = "1.43", features = ["full"] }
|
num-complex = "0.4"
|
||||||
|
|
||||||
# Serialization
|
# Async runtime
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
tokio = { version = "1.43", features = ["full"] }
|
||||||
serde_json = "1.0"
|
|
||||||
toml = "0.8"
|
# Serialization
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
# INI config
|
serde_json = "1.0"
|
||||||
configparser = "3"
|
toml = "0.8"
|
||||||
|
|
||||||
# Config directory
|
# INI config
|
||||||
dirs = "5.0"
|
configparser = "3"
|
||||||
|
|
||||||
# Time handling
|
# Config directory
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
dirs = "5.0"
|
||||||
|
|
||||||
# Error handling
|
# Time handling
|
||||||
anyhow = "1.0"
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
thiserror = "2.0"
|
|
||||||
|
# Error handling
|
||||||
# Logging
|
anyhow = "1.0"
|
||||||
tracing = "0.1"
|
thiserror = "2.0"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
||||||
|
# Logging
|
||||||
# Bit manipulation
|
tracing = "0.1"
|
||||||
bitvec = "1.0"
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
# TTY detection
|
# Bit manipulation
|
||||||
atty = "0.2"
|
bitvec = "1.0"
|
||||||
|
|
||||||
[build-dependencies]
|
# TTY detection
|
||||||
pkg-config = "0.3"
|
atty = "0.2"
|
||||||
|
|
||||||
[profile.release]
|
[build-dependencies]
|
||||||
opt-level = 3
|
pkg-config = "0.3"
|
||||||
lto = true
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
lto = true
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# KAT — Keyfob Analysis Toolkit
|
# KAT — Keyfob Analysis Toolkit
|
||||||
|
|
||||||
A terminal-based RF signal analysis tool for capturing, decoding, and retransmitting automotive keyfob signals using HackRF One. Built in Rust with a real-time TUI powered by `ratatui`. Protocol decoders are aligned with the [ProtoPirate](REFERENCES/ProtoPirate/) reference.
|
A terminal-based RF signal analysis tool for capturing, decoding, and retransmitting automotive keyfob signals. Built in Rust with a real-time TUI powered by `ratatui`. Protocol decoders are aligned with the [ProtoPirate](REFERENCES/ProtoPirate/) reference.
|
||||||
|
|
||||||
|
**Supported hardware:** KAT uses **HackRF One** (or compatible) when present for full receive and transmit. If no HackRF is found, it will use an **RTL-SDR** (e.g. RTL433-style dongles) for **receive-only** capture and decode; transmit (Lock/Unlock/Trunk/Panic, Replay) is disabled when using RTL-SDR.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -24,9 +26,14 @@ A terminal-based RF signal analysis tool for capturing, decoding, and retransmit
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- **HackRF One** (or compatible SDR)
|
- **Radio hardware (one of):**
|
||||||
|
- **HackRF One** (or compatible) — full receive and transmit
|
||||||
|
- **RTL-SDR** (e.g. RTL433 dongles) — receive-only; no transmit
|
||||||
- **Rust 1.75+** (for building from source)
|
- **Rust 1.75+** (for building from source)
|
||||||
- **libhackrf** — HackRF C library and headers
|
- **libhackrf** — HackRF C library and headers (required at build time even when using RTL-SDR)
|
||||||
|
- **libusb** — for RTL-SDR (usually provided by OS)
|
||||||
|
|
||||||
|
At runtime, KAT tries **HackRF first**; if none is found, it uses the first available **RTL-SDR**. If neither is connected, it runs in demo mode (no live capture).
|
||||||
|
|
||||||
### Installing Dependencies
|
### Installing Dependencies
|
||||||
|
|
||||||
@@ -34,26 +41,29 @@ A terminal-based RF signal analysis tool for capturing, decoding, and retransmit
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew install hackrf
|
brew install hackrf
|
||||||
|
# RTL-SDR: no extra system lib required; rtl-sdr-rs uses rusb
|
||||||
```
|
```
|
||||||
|
|
||||||
**Debian / Ubuntu:**
|
**Debian / Ubuntu:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install libhackrf-dev pkg-config
|
sudo apt install libhackrf-dev pkg-config libusb-1.0-0-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
**Fedora:**
|
**Fedora:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install hackrf-devel pkg-config
|
sudo dnf install hackrf-devel pkg-config libusb1-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
**Arch Linux:**
|
**Arch Linux:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo pacman -S hackrf
|
sudo pacman -S hackrf libusb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
On **Linux**, if an RTL-SDR is in use, you may need to unload DVB-T kernel modules so the device is not claimed by the kernel (see [rtl-sdr-rs](https://crates.io/crates/rtl-sdr-rs)).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -69,7 +79,7 @@ The binary is placed at `target/release/kat`.
|
|||||||
./target/release/kat
|
./target/release/kat
|
||||||
```
|
```
|
||||||
|
|
||||||
KAT starts in an interactive terminal UI. If a HackRF device is not connected, the application runs in demo/offline mode so you can still view, import, and export captures.
|
KAT starts in an interactive terminal UI. If no HackRF or RTL-SDR is detected, the application runs in demo/offline mode so you can still view, import, and export captures. The header shows the active device (e.g. **HackRF** or **RTL-SDR (RX only)**). When using RTL-SDR, transmit actions (Replay, TX Lock/Unlock/Trunk/Panic) are disabled and show a message that the device is receive-only.
|
||||||
|
|
||||||
### Keyboard Controls
|
### Keyboard Controls
|
||||||
|
|
||||||
@@ -267,6 +277,7 @@ src/
|
|||||||
│ └── ...
|
│ └── ...
|
||||||
├── radio/
|
├── radio/
|
||||||
│ ├── hackrf.rs # HackRF device control (RX/TX)
|
│ ├── hackrf.rs # HackRF device control (RX/TX)
|
||||||
|
│ ├── rtlsdr.rs # RTL-SDR device control (RX only)
|
||||||
│ ├── demodulator.rs # AM/OOK demodulator (IQ -> level/duration)
|
│ ├── demodulator.rs # AM/OOK demodulator (IQ -> level/duration)
|
||||||
│ └── modulator.rs # Level/duration -> TX waveform
|
│ └── modulator.rs # Level/duration -> TX waveform
|
||||||
└── ui/
|
└── ui/
|
||||||
|
|||||||
+1368
-1261
File diff suppressed because it is too large
Load Diff
@@ -160,6 +160,9 @@ fn run_app<B: ratatui::backend::Backend>(terminal: &mut Terminal<B>, app: &mut A
|
|||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
let command = app.command_input.clone();
|
let command = app.command_input.clone();
|
||||||
app.execute_command(&command)?;
|
app.execute_command(&command)?;
|
||||||
|
if app.quit_requested {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
app.command_input.clear();
|
app.command_input.clear();
|
||||||
if app.input_mode == InputMode::Command {
|
if app.input_mode == InputMode::Command {
|
||||||
app.input_mode = InputMode::Normal;
|
app.input_mode = InputMode::Normal;
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ impl HackRfController {
|
|||||||
self.hackrf_available
|
self.hackrf_available
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HackRF supports transmit.
|
||||||
|
pub fn supports_tx(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Start receiving at the specified frequency
|
/// Start receiving at the specified frequency
|
||||||
pub fn start_receiving(&mut self, frequency: u32) -> Result<()> {
|
pub fn start_receiving(&mut self, frequency: u32) -> Result<()> {
|
||||||
if self.receiving.load(Ordering::SeqCst) {
|
if self.receiving.load(Ordering::SeqCst) {
|
||||||
|
|||||||
+3
-1
@@ -1,11 +1,13 @@
|
|||||||
//! Radio subsystem for HackRF control.
|
//! Radio subsystem for HackRF and RTL-SDR control.
|
||||||
|
|
||||||
pub mod demodulator;
|
pub mod demodulator;
|
||||||
mod hackrf;
|
mod hackrf;
|
||||||
mod modulator;
|
mod modulator;
|
||||||
|
mod rtlsdr;
|
||||||
|
|
||||||
pub use demodulator::LevelDuration;
|
pub use demodulator::LevelDuration;
|
||||||
pub use hackrf::HackRfController;
|
pub use hackrf::HackRfController;
|
||||||
|
pub use rtlsdr::RtlSdrController;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use demodulator::Demodulator;
|
pub use demodulator::Demodulator;
|
||||||
|
|||||||
@@ -0,0 +1,316 @@
|
|||||||
|
//! RTL-SDR device control (receive-only).
|
||||||
|
//!
|
||||||
|
//! Provides an RX path compatible with the same IQ stream format and demodulators
|
||||||
|
//! as the HackRF path. Transmit is not supported; use HackRF for TX.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc, Mutex,
|
||||||
|
};
|
||||||
|
use std::thread::{self, JoinHandle};
|
||||||
|
|
||||||
|
use crate::app::RadioEvent;
|
||||||
|
use crate::capture::{Capture, RfModulation, StoredLevelDuration};
|
||||||
|
|
||||||
|
use super::demodulator::Demodulator;
|
||||||
|
use super::demodulator::FmDemodulator;
|
||||||
|
use super::demodulator::LevelDuration;
|
||||||
|
|
||||||
|
use rtl_sdr_rs::{DeviceId, RtlSdr, TunerGain, DEFAULT_BUF_LENGTH};
|
||||||
|
|
||||||
|
/// Sample rate (2 MHz, same as HackRF path for keyfob signals)
|
||||||
|
const SAMPLE_RATE: u32 = 2_000_000;
|
||||||
|
|
||||||
|
/// Tuner gain setting for RTL-SDR (manual gain in 0.1 dB units; None = auto)
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct TunerGainSetting {
|
||||||
|
/// Manual gain in tenths of dB, or None for auto
|
||||||
|
gain_tenths_db: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TunerGainSetting {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
gain_tenths_db: None, // auto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RTL-SDR controller: receive-only, no transmit.
|
||||||
|
pub struct RtlSdrController {
|
||||||
|
event_tx: Sender<RadioEvent>,
|
||||||
|
receiving: Arc<AtomicBool>,
|
||||||
|
rx_thread: Option<JoinHandle<()>>,
|
||||||
|
frequency: Arc<Mutex<u32>>,
|
||||||
|
demodulator_am: Arc<Mutex<Demodulator>>,
|
||||||
|
demodulator_fm: Arc<Mutex<FmDemodulator>>,
|
||||||
|
rtlsdr_available: bool,
|
||||||
|
gain_settings: Arc<Mutex<TunerGainSetting>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RtlSdrController {
|
||||||
|
/// Create a new RTL-SDR controller (receive-only).
|
||||||
|
pub fn new(event_tx: Sender<RadioEvent>) -> Result<Self> {
|
||||||
|
let demodulator_am = Demodulator::new(SAMPLE_RATE);
|
||||||
|
let demodulator_fm = FmDemodulator::new(SAMPLE_RATE);
|
||||||
|
|
||||||
|
let rtlsdr_available = check_rtlsdr_available();
|
||||||
|
|
||||||
|
if rtlsdr_available {
|
||||||
|
tracing::info!("RTL-SDR device detected (receive-only)");
|
||||||
|
} else {
|
||||||
|
tracing::debug!("RTL-SDR not detected");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
event_tx,
|
||||||
|
receiving: Arc::new(AtomicBool::new(false)),
|
||||||
|
rx_thread: None,
|
||||||
|
frequency: Arc::new(Mutex::new(433_920_000)),
|
||||||
|
demodulator_am: Arc::new(Mutex::new(demodulator_am)),
|
||||||
|
demodulator_fm: Arc::new(Mutex::new(demodulator_fm)),
|
||||||
|
rtlsdr_available,
|
||||||
|
gain_settings: Arc::new(Mutex::new(TunerGainSetting::default())),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if an RTL-SDR device was found.
|
||||||
|
pub fn is_available(&self) -> bool {
|
||||||
|
self.rtlsdr_available
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RTL-SDR is receive-only; transmit is never supported.
|
||||||
|
pub fn supports_tx(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start receiving at the specified frequency.
|
||||||
|
pub fn start_receiving(&mut self, frequency: u32) -> Result<()> {
|
||||||
|
if self.receiving.load(Ordering::SeqCst) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.frequency.lock().unwrap() = frequency;
|
||||||
|
self.receiving.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let receiving = self.receiving.clone();
|
||||||
|
let event_tx = self.event_tx.clone();
|
||||||
|
let freq = self.frequency.clone();
|
||||||
|
let demodulator_am = self.demodulator_am.clone();
|
||||||
|
let demodulator_fm = self.demodulator_fm.clone();
|
||||||
|
let rtlsdr_available = self.rtlsdr_available;
|
||||||
|
let gain_settings = self.gain_settings.clone();
|
||||||
|
|
||||||
|
self.rx_thread = Some(thread::spawn(move || {
|
||||||
|
if rtlsdr_available {
|
||||||
|
if let Err(e) = run_receiver_rtlsdr(
|
||||||
|
receiving.clone(),
|
||||||
|
event_tx.clone(),
|
||||||
|
freq,
|
||||||
|
demodulator_am,
|
||||||
|
demodulator_fm,
|
||||||
|
gain_settings,
|
||||||
|
) {
|
||||||
|
let _ = event_tx.send(RadioEvent::Error(format!("RTL-SDR receiver error: {}", e)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
run_demo_receiver(receiving, event_tx, freq);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
tracing::info!("Started RTL-SDR receiving at {} Hz", frequency);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop receiving.
|
||||||
|
pub fn stop_receiving(&mut self) -> Result<()> {
|
||||||
|
self.receiving.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
|
if let Some(handle) = self.rx_thread.take() {
|
||||||
|
let _ = handle.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!("Stopped RTL-SDR receiving");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the receive frequency.
|
||||||
|
pub fn set_frequency(&mut self, frequency: u32) -> Result<()> {
|
||||||
|
*self.frequency.lock().unwrap() = frequency;
|
||||||
|
tracing::info!("Set RTL-SDR frequency to {} Hz", frequency);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// No-op: RTL-SDR cannot transmit.
|
||||||
|
pub fn transmit(&mut self, _signal: &[LevelDuration], _frequency: u32) -> Result<()> {
|
||||||
|
tracing::warn!("Transmit not available – RTL-SDR is receive-only");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set tuner gain. For RTL-SDR we use a single gain; map LNA-style (0–40 dB) to tuner gain.
|
||||||
|
/// Pass None for auto gain.
|
||||||
|
pub fn set_tuner_gain_tenths_db(&mut self, gain_tenths_db: Option<i32>) -> Result<()> {
|
||||||
|
tracing::info!(
|
||||||
|
"Set RTL-SDR tuner gain to {}",
|
||||||
|
gain_tenths_db
|
||||||
|
.map(|g| format!("{} dB", g as f32 / 10.0))
|
||||||
|
.unwrap_or_else(|| "auto".to_string())
|
||||||
|
);
|
||||||
|
if let Ok(mut settings) = self.gain_settings.lock() {
|
||||||
|
settings.gain_tenths_db = gain_tenths_db;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set gain from a 0–40 dB value (e.g. LNA-style); stored as tenths of dB for RTL-SDR.
|
||||||
|
pub fn set_lna_gain(&mut self, gain: u32) -> Result<()> {
|
||||||
|
self.set_tuner_gain_tenths_db(Some((gain * 10) as i32))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RTL-SDR has a single tuner gain; VGA change maps to same tuner gain.
|
||||||
|
pub fn set_vga_gain(&mut self, gain: u32) -> Result<()> {
|
||||||
|
self.set_tuner_gain_tenths_db(Some((gain * 10) as i32))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RTL-SDR has no separate amp enable; no-op for UI compatibility.
|
||||||
|
pub fn set_amp_enable(&mut self, _enabled: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for RtlSdrController {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.receiving.store(false, Ordering::SeqCst);
|
||||||
|
if let Some(handle) = self.rx_thread.take() {
|
||||||
|
let _ = handle.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_rtlsdr_available() -> bool {
|
||||||
|
match RtlSdr::open(DeviceId::Index(0)) {
|
||||||
|
Ok(mut dev) => {
|
||||||
|
if let Err(e) = dev.close() {
|
||||||
|
tracing::debug!("RTL-SDR close after probe: {:?}", e);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::debug!("RTL-SDR not available: {:?}", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_demo_receiver(
|
||||||
|
receiving: Arc<AtomicBool>,
|
||||||
|
_event_tx: Sender<RadioEvent>,
|
||||||
|
_frequency: Arc<Mutex<u32>>,
|
||||||
|
) {
|
||||||
|
tracing::info!("Demo receiver thread started (no RTL-SDR)");
|
||||||
|
|
||||||
|
while receiving.load(Ordering::SeqCst) {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!("Demo receiver thread stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pairs_to_stored(pairs: &[LevelDuration]) -> Vec<StoredLevelDuration> {
|
||||||
|
pairs
|
||||||
|
.iter()
|
||||||
|
.map(|p| StoredLevelDuration {
|
||||||
|
level: p.level,
|
||||||
|
duration_us: p.duration_us,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert RTL-SDR u8 IQ (0–255) to signed i8 (-128..127) for the demodulators.
|
||||||
|
fn u8_iq_to_i8(buf: &[u8]) -> Vec<i8> {
|
||||||
|
buf.iter()
|
||||||
|
.map(|&b| (b as i16 - 128) as i8)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the receiver loop with an RTL-SDR device.
|
||||||
|
fn run_receiver_rtlsdr(
|
||||||
|
receiving: Arc<AtomicBool>,
|
||||||
|
event_tx: Sender<RadioEvent>,
|
||||||
|
frequency: Arc<Mutex<u32>>,
|
||||||
|
demodulator_am: Arc<Mutex<Demodulator>>,
|
||||||
|
demodulator_fm: Arc<Mutex<FmDemodulator>>,
|
||||||
|
gain_settings: Arc<Mutex<TunerGainSetting>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
|
tracing::info!("RTL-SDR receiver thread starting...");
|
||||||
|
|
||||||
|
let mut sdr = RtlSdr::open(DeviceId::Index(0)).context("Failed to open RTL-SDR device")?;
|
||||||
|
|
||||||
|
let freq = *frequency.lock().unwrap();
|
||||||
|
let initial_gain = *gain_settings.lock().unwrap();
|
||||||
|
|
||||||
|
sdr.reset_buffer().context("Failed to reset RTL-SDR buffer")?;
|
||||||
|
sdr.set_center_freq(freq).context("Failed to set RTL-SDR frequency")?;
|
||||||
|
sdr.set_sample_rate(SAMPLE_RATE).context("Failed to set RTL-SDR sample rate")?;
|
||||||
|
sdr.set_bias_tee(false).context("Failed to set bias-tee")?;
|
||||||
|
|
||||||
|
if let Some(tenths) = initial_gain.gain_tenths_db {
|
||||||
|
sdr.set_tuner_gain(TunerGain::Manual(tenths)).context("Failed to set RTL-SDR gain")?;
|
||||||
|
} else {
|
||||||
|
sdr.set_tuner_gain(TunerGain::Auto).context("Failed to set RTL-SDR gain")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
"RTL-SDR configured: freq={} Hz, sample_rate={} Hz",
|
||||||
|
freq, SAMPLE_RATE
|
||||||
|
);
|
||||||
|
|
||||||
|
let capture_id = std::sync::atomic::AtomicU32::new(0);
|
||||||
|
let mut buf = vec![0u8; DEFAULT_BUF_LENGTH];
|
||||||
|
|
||||||
|
while receiving.load(Ordering::SeqCst) {
|
||||||
|
match sdr.read_sync(&mut buf) {
|
||||||
|
Ok(n) if n == buf.len() => {
|
||||||
|
let current_freq = *frequency.lock().unwrap();
|
||||||
|
let samples = u8_iq_to_i8(&buf[..n]);
|
||||||
|
|
||||||
|
if let Ok(mut demod) = demodulator_am.lock() {
|
||||||
|
if let Some(pairs) = demod.process_samples(&samples) {
|
||||||
|
let id = capture_id.fetch_add(1, Ordering::SeqCst);
|
||||||
|
let capture = Capture::from_pairs_with_rf(
|
||||||
|
id,
|
||||||
|
current_freq,
|
||||||
|
pairs_to_stored(&pairs),
|
||||||
|
Some(RfModulation::AM),
|
||||||
|
);
|
||||||
|
let _ = event_tx.send(RadioEvent::SignalCaptured(capture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(mut demod) = demodulator_fm.lock() {
|
||||||
|
if let Some(pairs) = demod.process_samples(&samples) {
|
||||||
|
let id = capture_id.fetch_add(1, Ordering::SeqCst);
|
||||||
|
let capture = Capture::from_pairs_with_rf(
|
||||||
|
id,
|
||||||
|
current_freq,
|
||||||
|
pairs_to_stored(&pairs),
|
||||||
|
Some(RfModulation::FM),
|
||||||
|
);
|
||||||
|
let _ = event_tx.send(RadioEvent::SignalCaptured(capture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("RTL-SDR read error: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sdr.close().context("Failed to close RTL-SDR")?;
|
||||||
|
tracing::info!("RTL-SDR receiver thread stopped");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+15
-7
@@ -114,7 +114,7 @@ pub fn draw_ui(frame: &mut Frame, app: &App) {
|
|||||||
/// Render the header with title and radio status
|
/// Render the header with title and radio status
|
||||||
fn render_header(frame: &mut Frame, area: Rect, app: &App) {
|
fn render_header(frame: &mut Frame, area: Rect, app: &App) {
|
||||||
let (status_symbol, status_style) = match app.radio_state {
|
let (status_symbol, status_style) = match app.radio_state {
|
||||||
RadioState::Disconnected => ("○", Style::default().fg(Color::DarkGray)),
|
RadioState::Disconnected => ("○", Style::default().fg(Color::Red)),
|
||||||
RadioState::Idle => ("○", Style::default().fg(Color::Yellow)),
|
RadioState::Idle => ("○", Style::default().fg(Color::Yellow)),
|
||||||
RadioState::Receiving => ("●", Style::default().fg(Color::Green)),
|
RadioState::Receiving => ("●", Style::default().fg(Color::Green)),
|
||||||
RadioState::Transmitting => ("●", Style::default().fg(Color::Red)),
|
RadioState::Transmitting => ("●", Style::default().fg(Color::Red)),
|
||||||
@@ -122,12 +122,16 @@ fn render_header(frame: &mut Frame, area: Rect, app: &App) {
|
|||||||
|
|
||||||
let title = format!("Keyfob Analysis Toolkit v{}", VERSION);
|
let title = format!("Keyfob Analysis Toolkit v{}", VERSION);
|
||||||
|
|
||||||
// Build radio info string with all settings
|
// Build radio info string: device name (if any), state, freq, gains
|
||||||
let amp_str = if app.amp_enabled { "ON" } else { "OFF" };
|
let amp_str = if app.amp_enabled { "ON" } else { "OFF" };
|
||||||
|
let device_str = app
|
||||||
|
.radio_device_name()
|
||||||
|
.unwrap_or("No device");
|
||||||
let radio_info = format!(
|
let radio_info = format!(
|
||||||
"{} {} | {:.2} MHz | LNA:{} VGA:{} AMP:{}",
|
"{} {} | {} | {:.2} MHz | LNA:{} VGA:{} AMP:{}",
|
||||||
status_symbol,
|
status_symbol,
|
||||||
app.radio_state,
|
app.radio_state,
|
||||||
|
device_str,
|
||||||
app.frequency_mhz(),
|
app.frequency_mhz(),
|
||||||
app.lna_gain,
|
app.lna_gain,
|
||||||
app.vga_gain,
|
app.vga_gain,
|
||||||
@@ -196,10 +200,10 @@ fn centered_rect(width: u16, height: u16, area: Rect) -> Rect {
|
|||||||
Rect::new(x, y, width.min(area.width), height.min(area.height))
|
Rect::new(x, y, width.min(area.width), height.min(area.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the HackRF not detected warning (red box at startup)
|
/// Render the no-device warning (red box at startup when neither HackRF nor RTL-SDR found)
|
||||||
fn render_hackrf_not_detected(frame: &mut Frame, _app: &App) {
|
fn render_hackrf_not_detected(frame: &mut Frame, _app: &App) {
|
||||||
let area = frame.area();
|
let area = frame.area();
|
||||||
let popup = centered_rect(52, 7, area);
|
let popup = centered_rect(56, 8, area);
|
||||||
|
|
||||||
frame.render_widget(Clear, popup);
|
frame.render_widget(Clear, popup);
|
||||||
|
|
||||||
@@ -207,12 +211,16 @@ fn render_hackrf_not_detected(frame: &mut Frame, _app: &App) {
|
|||||||
let text = vec![
|
let text = vec![
|
||||||
Line::from(""),
|
Line::from(""),
|
||||||
Line::from(Span::styled(
|
Line::from(Span::styled(
|
||||||
" Your HackRF was not detected.",
|
" No HackRF or RTL-SDR detected.",
|
||||||
Style::default().fg(red).add_modifier(Modifier::BOLD),
|
Style::default().fg(red).add_modifier(Modifier::BOLD),
|
||||||
)),
|
)),
|
||||||
Line::from(""),
|
Line::from(""),
|
||||||
Line::from(Span::styled(
|
Line::from(Span::styled(
|
||||||
" Connect a HackRF One and restart, or continue in demo mode.",
|
" Connect a HackRF (TX+RX) or RTL-SDR (RX only) and restart,",
|
||||||
|
Style::default().fg(red),
|
||||||
|
)),
|
||||||
|
Line::from(Span::styled(
|
||||||
|
" or continue without TX/RX support.",
|
||||||
Style::default().fg(red),
|
Style::default().fg(red),
|
||||||
)),
|
)),
|
||||||
Line::from(""),
|
Line::from(""),
|
||||||
|
|||||||
+12
-1
@@ -77,8 +77,19 @@ pub fn render_signal_menu(frame: &mut Frame, app: &App) {
|
|||||||
" "
|
" "
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let is_tx_action = matches!(
|
||||||
|
action,
|
||||||
|
SignalAction::Replay | SignalAction::Lock | SignalAction::Unlock
|
||||||
|
| SignalAction::Trunk | SignalAction::Panic
|
||||||
|
);
|
||||||
|
let suffix = if is_tx_action && !app.can_transmit() {
|
||||||
|
" (no TX)"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
items.push(ListItem::new(Line::from(Span::styled(
|
items.push(ListItem::new(Line::from(Span::styled(
|
||||||
format!("{}{}", prefix, action.label()),
|
format!("{}{}{}", prefix, action.label(), suffix),
|
||||||
style,
|
style,
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user