Version 1.0.0

This commit is contained in:
leviathan
2026-02-07 17:35:27 -05:00
parent c8bff9afd7
commit 4339895b41
43 changed files with 12218 additions and 3 deletions
+323
View File
@@ -0,0 +1,323 @@
//! Subaru protocol decoder
//!
//! Ported from protopirate's subaru.c
//!
//! Protocol characteristics:
//! - PWM encoding: short HIGH (800µs) = 1, long HIGH (1600µs) = 0
//! - 64 bits total
//! - Long preamble of 1600µs pulses
//! - Gap and sync pattern
//! - Complex counter encoding
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;
#[allow(dead_code)]
const MIN_COUNT_BIT: usize = 64;
const GAP_US: u32 = 2800;
const SYNC_US: u32 = 2800;
/// Decoder states
#[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 the counter from the complex Subaru 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)
}
/// Process the decoded data
fn process_data(&self) -> Option<DecodedSignal> {
if self.bit_count < 64 {
return None;
}
let b = &self.data;
// Build 64-bit key
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,
})
}
}
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]
}
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);
// Generate 3 bursts
for burst in 0..3 {
if burst > 0 {
signal.push(LevelDuration::new(false, 25000));
}
// Preamble: 80 long HIGH/LOW pairs
for _ in 0..80 {
signal.push(LevelDuration::new(true, TE_LONG));
signal.push(LevelDuration::new(false, TE_LONG));
}
// Gap
signal.push(LevelDuration::new(false, GAP_US));
// Sync
signal.push(LevelDuration::new(true, SYNC_US));
signal.push(LevelDuration::new(false, TE_LONG));
// Data: 64 bits (MSB first)
// Short HIGH = 1, Long HIGH = 0
for bit in (0..64).rev() {
if (key >> bit) & 1 == 1 {
signal.push(LevelDuration::new(true, TE_SHORT));
} else {
signal.push(LevelDuration::new(true, TE_LONG));
}
signal.push(LevelDuration::new(false, TE_SHORT));
}
// End marker
signal.push(LevelDuration::new(false, TE_LONG * 2));
}
Some(signal)
}
}