Added Keeloq
This commit is contained in:
@@ -767,6 +767,7 @@ impl App {
|
||||
data_count_bit: capture.data_count_bit,
|
||||
encoder_capable: true,
|
||||
extra: capture.data_extra,
|
||||
protocol_display_name: None,
|
||||
};
|
||||
|
||||
// Generate the signal with the new button
|
||||
|
||||
@@ -32,6 +32,8 @@ pub struct DecodedSignal {
|
||||
pub encoder_capable: bool,
|
||||
/// Protocol-specific extra data for encoding (e.g. VAG: vag_type + key_idx)
|
||||
pub extra: Option<u64>,
|
||||
/// Optional protocol display name (e.g. "KeeLoq (DoorHan)"). When set, used as the protocol name for this decode.
|
||||
pub protocol_display_name: Option<String>,
|
||||
}
|
||||
|
||||
impl DecodedSignal {
|
||||
@@ -46,6 +48,7 @@ impl DecodedSignal {
|
||||
data_count_bit: bit_count,
|
||||
encoder_capable: false,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ impl FiatV0Decoder {
|
||||
data_count_bit: 71,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,6 +617,7 @@ impl ProtocolDecoder for FordV0Decoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
};
|
||||
|
||||
self.data_low = 0;
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
//! KeeLoq protocol decoder and encoder (unleashed format).
|
||||
//!
|
||||
//! Timing and state machine match Flipper Unleashed:
|
||||
//! `REFERENCES/unleashed-firmware/lib/subghz/protocols/keeloq.c`
|
||||
//! - te_short=400µs, te_long=800µs, te_delta=140µs, 64 data bits.
|
||||
//! - Preamble: HIGH pulses ~400µs; when LOW and header_count>2 and LOW ~4000µs, start data.
|
||||
//! - Data: short HIGH + long LOW = 1, long HIGH + short LOW = 0. End when LOW ≥ 940µs.
|
||||
//! Decryption tries all keystore keys with simple, normal, secure, magic_xor_type1,
|
||||
//! magic_serial type1/2/3 (and both key byte orders). Encoder uses simple learning
|
||||
//! and the key stored in DecodedSignal::extra (set when decoded).
|
||||
|
||||
use super::common::DecodedSignal;
|
||||
use super::keeloq_common::{
|
||||
keeloq_decrypt, keeloq_encrypt, keeloq_magic_serial_type1_learning,
|
||||
keeloq_magic_serial_type2_learning, keeloq_magic_serial_type3_learning,
|
||||
keeloq_magic_xor_type1_learning, keeloq_normal_learning, keeloq_secure_learning,
|
||||
reverse_key,
|
||||
};
|
||||
use crate::keystore;
|
||||
use crate::radio::demodulator::LevelDuration;
|
||||
|
||||
const TE_SHORT: u32 = 400;
|
||||
const TE_LONG: u32 = 800;
|
||||
const TE_DELTA: u32 = 140;
|
||||
const MIN_COUNT_BIT: usize = 64;
|
||||
|
||||
fn duration_diff(a: u32, b: u32) -> u32 {
|
||||
if a < b {
|
||||
b - a
|
||||
} else {
|
||||
a - b
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum Step {
|
||||
Reset,
|
||||
CheckPreamble,
|
||||
SaveDuration,
|
||||
CheckDuration,
|
||||
}
|
||||
|
||||
/// Try to decrypt (fix, hop) with all keystore keys and learning types.
|
||||
/// Returns (manufacture_name, serial, counter, button, key_for_encode) on success.
|
||||
/// key_for_encode is Some(mf_key) when a keystore key was used, None for AN-Motors/HCS101.
|
||||
fn try_keeloq_decrypt(
|
||||
fix: u32,
|
||||
hop: u32,
|
||||
seed: u32,
|
||||
) -> Option<(String, u32, u16, u8, Option<u64>)> {
|
||||
let end_serial = (fix & 0xFF) as u8;
|
||||
let btn = (fix >> 28) as u8;
|
||||
|
||||
fn check_decrypt(decrypt: u32, btn: u8, end_serial: u8) -> bool {
|
||||
(decrypt >> 28) as u8 == btn
|
||||
&& (((decrypt >> 16) & 0xFF) as u8 == end_serial || ((decrypt >> 16) & 0xFF) == 0)
|
||||
}
|
||||
|
||||
let keys = keystore::keeloq_mf_keys_with_names();
|
||||
for (name, mf_key) in keys {
|
||||
for key in [mf_key, mf_key.swap_bytes()] {
|
||||
if key == 0 {
|
||||
continue;
|
||||
}
|
||||
// Simple
|
||||
let decrypt = keeloq_decrypt(hop, key);
|
||||
if check_decrypt(decrypt, btn, end_serial) {
|
||||
let cnt = (decrypt & 0xFFFF) as u16;
|
||||
let serial = fix & 0x0FFFFFFF;
|
||||
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||
}
|
||||
// Normal
|
||||
let man = keeloq_normal_learning(fix, key);
|
||||
let decrypt = keeloq_decrypt(hop, man);
|
||||
if check_decrypt(decrypt, btn, end_serial) {
|
||||
let cnt = (decrypt & 0xFFFF) as u16;
|
||||
let serial = fix & 0x0FFFFFFF;
|
||||
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||
}
|
||||
// Secure (seed 0 and seed = fix for BFT-style)
|
||||
for s in [0u32, seed] {
|
||||
let man = keeloq_secure_learning(fix, s, key);
|
||||
let decrypt = keeloq_decrypt(hop, man);
|
||||
if check_decrypt(decrypt, btn, end_serial) {
|
||||
let cnt = (decrypt & 0xFFFF) as u16;
|
||||
let serial = fix & 0x0FFFFFFF;
|
||||
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||
}
|
||||
}
|
||||
// Magic XOR type1
|
||||
let man = keeloq_magic_xor_type1_learning(fix, key);
|
||||
let decrypt = keeloq_decrypt(hop, man);
|
||||
if check_decrypt(decrypt, btn, end_serial) {
|
||||
let cnt = (decrypt & 0xFFFF) as u16;
|
||||
let serial = fix & 0x0FFFFFFF;
|
||||
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||
}
|
||||
// Magic serial type 1/2/3
|
||||
for man in [
|
||||
keeloq_magic_serial_type1_learning(fix, key),
|
||||
keeloq_magic_serial_type2_learning(fix, key),
|
||||
keeloq_magic_serial_type3_learning(fix & 0xFFFFFF, key),
|
||||
] {
|
||||
let decrypt = keeloq_decrypt(hop, man);
|
||||
if check_decrypt(decrypt, btn, end_serial) {
|
||||
let cnt = (decrypt & 0xFFFF) as u16;
|
||||
let serial = fix & 0x0FFFFFFF;
|
||||
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AN-Motors / HCS101 special cases (no decrypt, no key for encode)
|
||||
if (hop >> 24) == ((hop >> 16) & 0xFF)
|
||||
&& (fix >> 28) == ((hop >> 12) & 0x0F)
|
||||
&& (hop & 0xFFF) == 0x404
|
||||
{
|
||||
return Some((
|
||||
"AN-Motors".to_string(),
|
||||
fix & 0x0FFFFFFF,
|
||||
(hop >> 16) as u16,
|
||||
btn,
|
||||
None,
|
||||
));
|
||||
}
|
||||
if (hop & 0xFFF) == 0 && (fix >> 28) == ((hop >> 12) & 0x0F) {
|
||||
return Some((
|
||||
"HCS101".to_string(),
|
||||
fix & 0x0FFFFFFF,
|
||||
(hop >> 16) as u16,
|
||||
btn,
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub struct KeeloqDecoder {
|
||||
step: Step,
|
||||
header_count: u16,
|
||||
te_last: u32,
|
||||
decode_data: u64,
|
||||
decode_count_bit: usize,
|
||||
/// For secure learning when we don't have a prior decode (use fix as seed hint)
|
||||
seed: u32,
|
||||
}
|
||||
|
||||
impl KeeloqDecoder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::Reset,
|
||||
header_count: 0,
|
||||
te_last: 0,
|
||||
decode_data: 0,
|
||||
decode_count_bit: 0,
|
||||
seed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_bit(&mut self, bit: u8) {
|
||||
self.decode_data = (self.decode_data << 1) | (bit as u64);
|
||||
self.decode_count_bit += 1;
|
||||
}
|
||||
|
||||
fn reset_to(&mut self, step: Step) {
|
||||
self.step = step;
|
||||
if step == Step::Reset {
|
||||
self.header_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for KeeloqDecoder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::ProtocolDecoder for KeeloqDecoder {
|
||||
fn name(&self) -> &'static str {
|
||||
"KeeLoq"
|
||||
}
|
||||
|
||||
fn timing(&self) -> super::ProtocolTiming {
|
||||
super::ProtocolTiming {
|
||||
te_short: TE_SHORT,
|
||||
te_long: TE_LONG,
|
||||
te_delta: TE_DELTA,
|
||||
min_count_bit: MIN_COUNT_BIT,
|
||||
}
|
||||
}
|
||||
|
||||
fn supported_frequencies(&self) -> &[u32] {
|
||||
&[315_000_000, 433_920_000, 868_350_000]
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.step = Step::Reset;
|
||||
self.header_count = 0;
|
||||
self.te_last = 0;
|
||||
self.decode_data = 0;
|
||||
self.decode_count_bit = 0;
|
||||
}
|
||||
|
||||
fn feed(&mut self, level: bool, duration_us: u32) -> Option<DecodedSignal> {
|
||||
match self.step {
|
||||
Step::Reset => {
|
||||
if level && duration_diff(duration_us, TE_SHORT) < TE_DELTA {
|
||||
self.step = Step::CheckPreamble;
|
||||
self.header_count = self.header_count.saturating_add(1);
|
||||
}
|
||||
}
|
||||
Step::CheckPreamble => {
|
||||
if !level && duration_diff(duration_us, TE_SHORT) < TE_DELTA {
|
||||
self.step = Step::Reset;
|
||||
return None;
|
||||
}
|
||||
if self.header_count > 2
|
||||
&& duration_diff(duration_us, TE_SHORT * 10) < TE_DELTA * 10
|
||||
{
|
||||
self.step = Step::SaveDuration;
|
||||
self.decode_data = 0;
|
||||
self.decode_count_bit = 0;
|
||||
} else {
|
||||
self.step = Step::Reset;
|
||||
self.header_count = 0;
|
||||
}
|
||||
}
|
||||
Step::SaveDuration => {
|
||||
if level {
|
||||
self.te_last = duration_us;
|
||||
self.step = Step::CheckDuration;
|
||||
}
|
||||
}
|
||||
Step::CheckDuration => {
|
||||
if !level {
|
||||
let end_threshold = TE_SHORT * 2 + TE_DELTA;
|
||||
if duration_us >= end_threshold {
|
||||
self.step = Step::Reset;
|
||||
if self.decode_count_bit >= MIN_COUNT_BIT
|
||||
&& self.decode_count_bit <= MIN_COUNT_BIT + 2
|
||||
{
|
||||
let raw_data = self.decode_data;
|
||||
let reversed = reverse_key(raw_data, MIN_COUNT_BIT);
|
||||
let key_fix = (reversed >> 32) as u32;
|
||||
let key_hop = (reversed & 0xFFFFFFFF) as u32;
|
||||
if let Some((_mf_name, serial, cnt, btn, key_for_encode)) =
|
||||
try_keeloq_decrypt(key_fix, key_hop, self.seed)
|
||||
{
|
||||
if self.seed == 0 {
|
||||
self.seed = key_fix & 0x0FFFFFFF;
|
||||
}
|
||||
self.decode_data = 0;
|
||||
self.decode_count_bit = 0;
|
||||
self.header_count = 0;
|
||||
return Some(DecodedSignal {
|
||||
serial: Some(serial),
|
||||
button: Some(btn),
|
||||
counter: Some(cnt),
|
||||
crc_valid: true,
|
||||
data: raw_data,
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: key_for_encode,
|
||||
protocol_display_name: Some(format!("KeeLoq ({})", _mf_name)),
|
||||
});
|
||||
}
|
||||
}
|
||||
self.decode_data = 0;
|
||||
self.decode_count_bit = 0;
|
||||
self.header_count = 0;
|
||||
return None;
|
||||
}
|
||||
// Bit 1: te_last short, duration long
|
||||
if duration_diff(self.te_last, TE_SHORT) < TE_DELTA
|
||||
&& duration_diff(duration_us, TE_LONG) < TE_DELTA * 2
|
||||
{
|
||||
if self.decode_count_bit < MIN_COUNT_BIT {
|
||||
self.add_bit(1);
|
||||
} else {
|
||||
self.decode_count_bit += 1;
|
||||
}
|
||||
self.step = Step::SaveDuration;
|
||||
return None;
|
||||
}
|
||||
// Bit 0: te_last long, duration short
|
||||
if duration_diff(self.te_last, TE_LONG) < TE_DELTA * 2
|
||||
&& duration_diff(duration_us, TE_SHORT) < TE_DELTA
|
||||
{
|
||||
if self.decode_count_bit < MIN_COUNT_BIT {
|
||||
self.add_bit(0);
|
||||
} else {
|
||||
self.decode_count_bit += 1;
|
||||
}
|
||||
self.step = Step::SaveDuration;
|
||||
return None;
|
||||
}
|
||||
self.reset_to(Step::Reset);
|
||||
} else {
|
||||
self.reset_to(Step::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn supports_encoding(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
|
||||
let serial = decoded.serial?;
|
||||
let counter = decoded.counter.unwrap_or(0).wrapping_add(1);
|
||||
let key = decoded.extra;
|
||||
let fix = ((button as u32) << 28) | (serial & 0x0FFFFFFF);
|
||||
let plaintext = ((button as u32) << 28)
|
||||
| ((serial & 0x3FF) << 16)
|
||||
| (counter as u32);
|
||||
let hop = if let Some(k) = key {
|
||||
keeloq_encrypt(plaintext, k)
|
||||
} else {
|
||||
let reversed = reverse_key(decoded.data, MIN_COUNT_BIT);
|
||||
(reversed & 0xFFFFFFFF) as u32
|
||||
};
|
||||
let yek = ((fix as u64) << 32) | (hop as u64);
|
||||
let data = reverse_key(yek, MIN_COUNT_BIT);
|
||||
|
||||
let mut signal = Vec::with_capacity(256);
|
||||
for _ in 0..11 {
|
||||
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||
signal.push(LevelDuration::new(false, TE_SHORT));
|
||||
}
|
||||
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||
signal.push(LevelDuration::new(false, TE_SHORT * 10));
|
||||
|
||||
for i in (0..MIN_COUNT_BIT).rev() {
|
||||
if (data >> i) & 1 == 1 {
|
||||
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||
signal.push(LevelDuration::new(false, TE_LONG));
|
||||
} else {
|
||||
signal.push(LevelDuration::new(true, TE_LONG));
|
||||
signal.push(LevelDuration::new(false, TE_SHORT));
|
||||
}
|
||||
}
|
||||
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||
signal.push(LevelDuration::new(false, TE_LONG));
|
||||
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||
signal.push(LevelDuration::new(false, TE_SHORT * 40));
|
||||
|
||||
Some(signal)
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,7 @@ fn try_kia_v3_v4_format(
|
||||
data_count_bit: KIA_V3_V4_BITS,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
},
|
||||
));
|
||||
}
|
||||
@@ -148,6 +149,7 @@ fn try_star_line_format(
|
||||
data_count_bit: STAR_LINE_BITS,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
},
|
||||
));
|
||||
}
|
||||
@@ -169,6 +171,7 @@ fn try_star_line_format(
|
||||
data_count_bit: STAR_LINE_BITS,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ impl KiaV0Decoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ impl KiaV1Decoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ impl KiaV2Decoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ impl KiaV3V4Decoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ impl KiaV5Decoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: false, // V5 is decode-only
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,6 +539,7 @@ impl ProtocolDecoder for KiaV6Decoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: false,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+18
-3
@@ -11,6 +11,7 @@
|
||||
|
||||
mod common;
|
||||
pub mod keeloq_common;
|
||||
mod keeloq;
|
||||
mod keeloq_generic;
|
||||
#[allow(dead_code)]
|
||||
pub mod aut64;
|
||||
@@ -102,6 +103,7 @@ impl ProtocolRegistry {
|
||||
Box::new(suzuki::SuzukiDecoder::new()),
|
||||
Box::new(scher_khan::ScherKhanDecoder::new()),
|
||||
Box::new(star_line::StarLineDecoder::new()),
|
||||
Box::new(keeloq::KeeloqDecoder::new()),
|
||||
Box::new(psa::PsaDecoder::new()),
|
||||
];
|
||||
|
||||
@@ -171,7 +173,11 @@ impl ProtocolRegistry {
|
||||
continue;
|
||||
}
|
||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||
hit = Some((decoder.name().to_string(), decoded));
|
||||
let name = decoded
|
||||
.protocol_display_name
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| decoder.name());
|
||||
hit = Some((name.to_string(), decoded));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -220,7 +226,11 @@ impl ProtocolRegistry {
|
||||
}
|
||||
|
||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||
return Some((decoder.name().to_string(), decoded));
|
||||
let name = decoded
|
||||
.protocol_display_name
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| decoder.name());
|
||||
return Some((name.to_string(), decoded));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,9 +256,14 @@ impl ProtocolRegistry {
|
||||
|
||||
/// Get a decoder by name
|
||||
pub fn get(&self, name: &str) -> Option<&dyn ProtocolDecoder> {
|
||||
let lookup = if name.starts_with("KeeLoq") {
|
||||
"KeeLoq"
|
||||
} else {
|
||||
name
|
||||
};
|
||||
self.decoders
|
||||
.iter()
|
||||
.find(|d| d.name().eq_ignore_ascii_case(name))
|
||||
.find(|d| d.name().eq_ignore_ascii_case(lookup))
|
||||
.map(|d| d.as_ref())
|
||||
}
|
||||
|
||||
|
||||
@@ -263,6 +263,7 @@ impl PsaDecoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
} else {
|
||||
DecodedSignal {
|
||||
@@ -274,6 +275,7 @@ impl PsaDecoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: false,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ impl ScherKhanDecoder {
|
||||
data_count_bit: bit_count,
|
||||
encoder_capable: false,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ impl StarLineDecoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ impl SubaruDecoder {
|
||||
data_count_bit: 64,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ impl SuzukiDecoder {
|
||||
data_count_bit: MIN_COUNT_BIT,
|
||||
encoder_capable: true,
|
||||
extra: None,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,6 +795,7 @@ impl VagDecoder {
|
||||
data_count_bit: self.data_count_bit,
|
||||
encoder_capable: self.decrypted,
|
||||
extra,
|
||||
protocol_display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ pub fn render_captures_list(frame: &mut Frame, area: Rect, app: &App) {
|
||||
/// Render the compact signal table
|
||||
fn render_table(frame: &mut Frame, area: Rect, app: &App) {
|
||||
let header_cells = [
|
||||
"ID", "Time", "Protocol", "Freq", "Serial", "Btn", "Cnt", "Mod", "CRC", "Status",
|
||||
"ID", "Time", "Protocol", "Freq", "Serial", "Btn", "Cnt", "Modulation", "CRC", "Status",
|
||||
]
|
||||
.iter()
|
||||
.map(|h| Cell::from(*h).style(Style::default().add_modifier(Modifier::BOLD)));
|
||||
@@ -99,12 +99,12 @@ fn render_table(frame: &mut Frame, area: Rect, app: &App) {
|
||||
let widths = [
|
||||
Constraint::Length(4), // ID
|
||||
Constraint::Length(9), // Time
|
||||
Constraint::Length(10), // Protocol
|
||||
Constraint::Length(24), // Protocol (e.g. KeeLoq (DoorHan))
|
||||
Constraint::Length(11), // Freq
|
||||
Constraint::Length(9), // Serial
|
||||
Constraint::Length(6), // Btn
|
||||
Constraint::Length(6), // Cnt
|
||||
Constraint::Length(7), // Mod
|
||||
Constraint::Length(12), // Modulation
|
||||
Constraint::Length(5), // CRC
|
||||
Constraint::Length(10), // Status
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user