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
+203
View File
@@ -0,0 +1,203 @@
//! Suzuki protocol decoder/encoder
//!
//! Ported from protopirate's suzuki.c
//!
//! Protocol characteristics:
//! - PWM encoding: 250/500µs timing
//! - 64 bits total
//! - 350 preamble pairs
//! - 2000µs gap between transmissions
use super::{DecodedSignal, ProtocolDecoder, ProtocolTiming};
use crate::duration_diff;
use crate::radio::demodulator::LevelDuration;
const TE_SHORT: u32 = 250;
const TE_LONG: u32 = 500;
const TE_DELTA: u32 = 99;
const MIN_COUNT_BIT: usize = 64;
const PREAMBLE_COUNT: u16 = 350;
const GAP_TIME: u32 = 2000;
const GAP_DELTA: u32 = 399;
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CountPreamble,
DecodeData,
}
/// Suzuki protocol decoder
pub struct SuzukiDecoder {
step: DecoderStep,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
te_last: u32,
}
impl SuzukiDecoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
te_last: 0,
}
}
fn add_bit(&mut self, bit: u8) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
fn parse_data(data: u64) -> DecodedSignal {
let data_high = (data >> 32) as u32;
let data_low = data as u32;
let serial = ((data_high & 0xFFF) << 16) | (data_low >> 16);
let btn = ((data_low >> 12) & 0xF) as u8;
let cnt = ((data_high << 4) >> 16) as u16;
DecodedSignal {
serial: Some(serial),
button: Some(btn),
counter: Some(cnt),
crc_valid: true, // CRC checked via structure
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for SuzukiDecoder {
fn name(&self) -> &'static str {
"Suzuki"
}
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.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
self.te_last = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if !level {
return None;
}
if duration_diff!(duration, TE_SHORT) > TE_DELTA {
return None;
}
self.decode_data = 0;
self.decode_count_bit = 0;
self.step = DecoderStep::CountPreamble;
self.header_count = 0;
}
DecoderStep::CountPreamble => {
if level {
// HIGH pulse
if self.header_count >= 300 {
if duration_diff!(duration, TE_LONG) <= TE_DELTA {
self.step = DecoderStep::DecodeData;
self.add_bit(1);
}
}
} else {
// LOW pulse
if duration_diff!(duration, TE_SHORT) <= TE_DELTA {
self.te_last = duration;
self.header_count += 1;
} else {
self.step = DecoderStep::Reset;
}
}
}
DecoderStep::DecodeData => {
if level {
// HIGH pulse - determines bit value
let diff_long = duration_diff!(duration, TE_LONG);
let diff_short = duration_diff!(duration, TE_SHORT);
if diff_long <= TE_DELTA {
self.add_bit(1);
} else if diff_short <= TE_DELTA {
self.add_bit(0);
}
} else {
// LOW pulse - check for gap (end of transmission)
let diff_gap = duration_diff!(duration, GAP_TIME);
if diff_gap <= GAP_DELTA {
if self.decode_count_bit == MIN_COUNT_BIT {
let result = Self::parse_data(self.decode_data);
self.decode_data = 0;
self.decode_count_bit = 0;
self.step = DecoderStep::Reset;
return Some(result);
}
self.decode_data = 0;
self.decode_count_bit = 0;
self.step = DecoderStep::Reset;
}
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
let data = decoded.data;
let mut signal = Vec::with_capacity(1024);
// Preamble: SHORT HIGH / SHORT LOW pairs
for _ in 0..PREAMBLE_COUNT {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
// Data: 64 bits, MSB first
// SHORT HIGH (~250µs) = 0, LONG HIGH (~500µs) = 1
for bit in (0..64).rev() {
if (data >> bit) & 1 == 1 {
signal.push(LevelDuration::new(true, TE_LONG));
} else {
signal.push(LevelDuration::new(true, TE_SHORT));
}
signal.push(LevelDuration::new(false, TE_SHORT));
}
// End gap
signal.push(LevelDuration::new(false, GAP_TIME));
Some(signal)
}
}