Files
KAT/src/protocols/subaru.rs
T
2026-02-17 19:47:57 -05:00

337 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Subaru protocol decoder/encoder
//!
//! Aligned with ProtoPirate reference: `REFERENCES/ProtoPirate/protocols/subaru.c`.
//! Decode/encode logic (PWM preamble/gap/sync, short=1/long=0, counter decode) matches reference.
//!
//! Protocol characteristics:
//! - PWM encoding: 800µs HIGH = 1, 1600µs HIGH = 0; LOW is 800µs after each bit
//! - 64 bits total (8 bytes MSB first: button(4)+serial(24)+counter-related)
//! - Preamble: 79 full 1600µs pairs + 80th HIGH only; then gap 2800µs, sync 2800µs HIGH + 1600µs LOW
//! - Complex counter encoding (decode_counter) from bytes 47
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 800;
const TE_LONG: u32 = 1600;
const TE_DELTA: u32 = 200; // ref subaru.c
#[allow(dead_code)]
const MIN_COUNT_BIT: usize = 64;
const GAP_US: u32 = 2800;
const SYNC_US: u32 = 2800;
/// Decoder states (matches protopirate's SubaruDecoderStep)
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
FoundGap,
FoundSync,
SaveDuration,
CheckDuration,
}
/// Subaru protocol decoder
pub struct SubaruDecoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
data: [u8; 8],
bit_count: usize,
}
impl SubaruDecoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
data: [0u8; 8],
bit_count: 0,
}
}
/// Add a bit to the data buffer
fn add_bit(&mut self, bit: bool) {
if self.bit_count < 64 {
let byte_idx = self.bit_count / 8;
let bit_idx = 7 - (self.bit_count % 8);
if bit {
self.data[byte_idx] |= 1 << bit_idx;
} else {
self.data[byte_idx] &= !(1 << bit_idx);
}
self.bit_count += 1;
}
}
/// Decode 16-bit counter from bytes 47 (matches subaru.c complex encoding)
fn decode_counter(kb: &[u8; 8]) -> u16 {
let mut lo: u8 = 0;
if (kb[4] & 0x40) == 0 { lo |= 0x01; }
if (kb[4] & 0x80) == 0 { lo |= 0x02; }
if (kb[5] & 0x01) == 0 { lo |= 0x04; }
if (kb[5] & 0x02) == 0 { lo |= 0x08; }
if (kb[6] & 0x01) == 0 { lo |= 0x10; }
if (kb[6] & 0x02) == 0 { lo |= 0x20; }
if (kb[5] & 0x40) == 0 { lo |= 0x40; }
if (kb[5] & 0x80) == 0 { lo |= 0x80; }
let mut reg_sh1 = (kb[7] << 4) & 0xF0;
if kb[5] & 0x04 != 0 { reg_sh1 |= 0x04; }
if kb[5] & 0x08 != 0 { reg_sh1 |= 0x08; }
if kb[6] & 0x80 != 0 { reg_sh1 |= 0x02; }
if kb[6] & 0x40 != 0 { reg_sh1 |= 0x01; }
let reg_sh2 = ((kb[6] << 2) & 0xF0) | ((kb[7] >> 4) & 0x0F);
let mut ser0 = kb[3];
let mut ser1 = kb[1];
let mut ser2 = kb[2];
let total_rot = 4 + lo;
for _ in 0..total_rot {
let t_bit = (ser0 >> 7) & 1;
ser0 = ((ser0 << 1) & 0xFE) | ((ser1 >> 7) & 1);
ser1 = ((ser1 << 1) & 0xFE) | ((ser2 >> 7) & 1);
ser2 = ((ser2 << 1) & 0xFE) | t_bit;
}
let t1 = ser1 ^ reg_sh1;
let t2 = ser2 ^ reg_sh2;
let mut hi: u8 = 0;
if (t1 & 0x10) == 0 { hi |= 0x04; }
if (t1 & 0x20) == 0 { hi |= 0x08; }
if (t2 & 0x80) == 0 { hi |= 0x02; }
if (t2 & 0x40) == 0 { hi |= 0x01; }
if (t1 & 0x01) == 0 { hi |= 0x40; }
if (t1 & 0x02) == 0 { hi |= 0x80; }
if (t2 & 0x08) == 0 { hi |= 0x20; }
if (t2 & 0x04) == 0 { hi |= 0x10; }
((hi as u16) << 8) | (lo as u16)
}
/// Append level+duration, merging with previous if same level for correct replay timing
fn add_level(signal: &mut Vec<LevelDuration>, level: bool, duration: u32) {
if let Some(last) = signal.last_mut() {
if last.level == level {
*last = LevelDuration::new(level, last.duration_us + duration);
return;
}
}
signal.push(LevelDuration::new(level, duration));
}
/// Build DecodedSignal from 8-byte buffer: serial(bytes 13), button(byte0 low nibble), counter(decode_counter) — matches subaru.c
fn process_data(&self) -> Option<DecodedSignal> {
if self.bit_count < 64 {
return None;
}
let b = &self.data;
let key = ((b[0] as u64) << 56) | ((b[1] as u64) << 48) |
((b[2] as u64) << 40) | ((b[3] as u64) << 32) |
((b[4] as u64) << 24) | ((b[5] as u64) << 16) |
((b[6] as u64) << 8) | (b[7] as u64);
let serial = ((b[1] as u32) << 16) | ((b[2] as u32) << 8) | (b[3] as u32);
let button = b[0] & 0x0F;
let counter = Self::decode_counter(&self.data);
Some(DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid: true, // Subaru doesn't use CRC
data: key,
data_count_bit: 64,
encoder_capable: true,
extra: None,
protocol_display_name: None,
})
}
}
impl ProtocolDecoder for SubaruDecoder {
fn name(&self) -> &'static str {
"Subaru"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000, 315_000_000] // 433.92 MHz (EU/AU) and 315 MHz (US/JP)
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.data = [0u8; 8];
self.bit_count = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if level && duration_diff!(duration, TE_LONG) < TE_DELTA {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 1;
}
}
DecoderStep::CheckPreamble => {
if !level {
if duration_diff!(duration, TE_LONG) < TE_DELTA {
self.header_count += 1;
} else if duration > 2000 && duration < 3500 {
// Gap detected
if self.header_count > 20 {
self.step = DecoderStep::FoundGap;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
} else {
if duration_diff!(duration, TE_LONG) < TE_DELTA {
self.te_last = duration;
self.header_count += 1;
} else {
self.step = DecoderStep::Reset;
}
}
}
DecoderStep::FoundGap => {
if level && duration > 2000 && duration < 3500 {
self.step = DecoderStep::FoundSync;
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::FoundSync => {
if !level && duration_diff!(duration, TE_LONG) < TE_DELTA {
self.step = DecoderStep::SaveDuration;
self.bit_count = 0;
self.data = [0u8; 8];
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::SaveDuration => {
if level {
if duration_diff!(duration, TE_SHORT) < TE_DELTA {
// Short HIGH = bit 1
self.add_bit(true);
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
} else if duration_diff!(duration, TE_LONG) < TE_DELTA {
// Long HIGH = bit 0
self.add_bit(false);
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
} else if duration > 3000 {
// End of transmission
if self.bit_count >= 64 {
let result = self.process_data();
self.step = DecoderStep::Reset;
return result;
}
self.step = DecoderStep::Reset;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::CheckDuration => {
if !level {
if duration_diff!(duration, TE_SHORT) < TE_DELTA ||
duration_diff!(duration, TE_LONG) < TE_DELTA {
self.step = DecoderStep::SaveDuration;
} else if duration > 3000 {
// Gap - end of packet
if self.bit_count >= 64 {
let result = self.process_data();
self.step = DecoderStep::Reset;
return result;
}
self.step = DecoderStep::Reset;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
let key = decoded.data;
let mut signal = Vec::with_capacity(512);
// 3 bursts; add_level() merges same-level pulses for correct replay (ref subaru encode)
for burst in 0..3 {
if burst > 0 {
Self::add_level(&mut signal, false, 25000);
}
// Preamble: 79 full 1600µs pairs + 80th HIGH only; gap replaces 80th LOW
for i in 0..80 {
Self::add_level(&mut signal, true, TE_LONG);
if i < 79 {
Self::add_level(&mut signal, false, TE_LONG);
}
}
Self::add_level(&mut signal, false, GAP_US);
Self::add_level(&mut signal, true, SYNC_US);
Self::add_level(&mut signal, false, TE_LONG);
// Data: 64 bits MSB first; short HIGH = 1, long HIGH = 0; LOW = 800µs after each
for bit in (0..64).rev() {
if (key >> bit) & 1 == 1 {
Self::add_level(&mut signal, true, TE_SHORT);
} else {
Self::add_level(&mut signal, true, TE_LONG);
}
Self::add_level(&mut signal, false, TE_SHORT);
}
Self::add_level(&mut signal, false, TE_LONG * 2);
}
Some(signal)
}
}
impl Default for SubaruDecoder {
fn default() -> Self {
Self::new()
}
}