//! Suzuki protocol decoder/encoder //! //! Aligned with ProtoPirate reference: `REFERENCES/ProtoPirate/protocols/suzuki.c`. //! Decode/encode logic (preamble count 350, gap 2000µs, short=0/long=1, field layout) matches reference. //! //! Protocol characteristics: //! - PWM encoding: 250µs HIGH = 0, 500µs HIGH = 1; LOW 250µs after each bit //! - 64 bits total; preamble: 300+ short LOW pulses then long HIGH starts data //! - 350 preamble pairs (SHORT HIGH / SHORT LOW); 2000µs gap at end //! - Field layout: serial = (data_high&0xFFF)<<16 | data_low>>16; btn = (data_low>>12)&0xF; cnt = (data_high<<4)>>16 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 (matches protopirate's SuzukiDecoderStep) #[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; } /// Parse 64-bit data (matches suzuki.c: serial, btn, cnt layout) fn parse_data(data: u64) -> DecodedSignal { let data_high = (data >> 32) as u32; let data_low = data as u32; // Reference: instance->generic.serial = ((data_high & 0xFFF) << 16) | (data_low >> 16); etc. 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 { 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> { let data = decoded.data; let mut signal = Vec::with_capacity(1024); // Preamble + data + gap (matches subghz_protocol_encoder_suzuki_get_upload in suzuki.c) 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 = 0, LONG HIGH = 1; LOW = 250µs after each 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)); } signal.push(LevelDuration::new(false, GAP_TIME)); Some(signal) } } impl Default for SuzukiDecoder { fn default() -> Self { Self::new() } }