1use crate::Pulse;
5
6pub const CARRIER_HZ: u32 = 38_000;
8pub const FRAME_PULSES: usize = 34;
10
11const LEADER: Pulse = Pulse {
12 mark_us: 9000,
13 space_us: 4500,
14};
15const ZERO: Pulse = Pulse {
16 mark_us: 562,
17 space_us: 562,
18};
19const ONE: Pulse = Pulse {
20 mark_us: 562,
21 space_us: 1687,
22};
23const STOP: Pulse = Pulse {
24 mark_us: 562,
25 space_us: 0,
26};
27pub const REPEAT: [Pulse; 2] = [
29 Pulse {
30 mark_us: 9000,
31 space_us: 2250,
32 },
33 STOP,
34];
35
36pub fn encode(address: u8, command: u8) -> [Pulse; FRAME_PULSES] {
38 encode_raw(u32::from_le_bytes([address, !address, command, !command]))
39}
40
41pub fn encode_extended(address: u16, command: u8) -> [Pulse; FRAME_PULSES] {
43 let [lo, hi] = address.to_le_bytes();
44 encode_raw(u32::from_le_bytes([lo, hi, command, !command]))
45}
46
47fn encode_raw(word: u32) -> [Pulse; FRAME_PULSES] {
48 let mut out = [STOP; FRAME_PULSES];
49 out[0] = LEADER;
50 for bit in 0..32 {
51 out[1 + bit] = if word >> bit & 1 == 1 { ONE } else { ZERO };
52 }
53 out
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 fn decode(p: &[Pulse; FRAME_PULSES]) -> u32 {
62 p[1..33]
63 .iter()
64 .enumerate()
65 .fold(0, |w, (i, b)| w | (u32::from(b.space_us > 1000) << i))
66 }
67
68 #[test]
69 fn standard_frame() {
70 let f = encode(0x04, 0x08);
71 let us = |p: &Pulse| (p.mark_us, p.space_us);
72 assert_eq!(us(&f[0]), (9000, 4500), "leader");
73 assert_eq!(us(&f[33]), (562, 0), "trailing mark");
74 for (i, p) in f[1..33].iter().enumerate() {
75 assert!(matches!(us(p), (562, 562 | 1687)), "bit {i}: {p:?}");
76 }
77 assert_eq!(decode(&f).to_le_bytes(), [0x04, 0xFB, 0x08, 0xF7]);
78 }
79
80 #[test]
81 fn extended_frame_has_16_bit_address() {
82 let w = decode(&encode_extended(0xBEEF, 0x12));
83 assert_eq!(w.to_le_bytes(), [0xEF, 0xBE, 0x12, 0xED]);
84 }
85}