dcc_rs/packets/
baseline.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! This module provides types and serialisers for each "baseline"
6//! packet type defined by the NMRA standard.
7//!
8//! <https://www.nmra.org/sites/default/files/s-92-2004-07.pdf>
9
10use super::{Preamble, Result, SerialiseBuffer};
11use crate::Error;
12use bitvec::prelude::*;
13
14impl Default for Preamble {
15    fn default() -> Self {
16        Self(BitArray::from([0xff, 0xff]))
17    }
18}
19
20/// Possible directions, usually referenced to the "forward" direction
21/// of a loco
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "use-defmt", derive(defmt::Format))]
24pub enum Direction {
25    /// Forward
26    Forward,
27    /// Backward
28    Backward,
29}
30
31impl Default for Direction {
32    fn default() -> Self {
33        Self::Forward
34    }
35}
36
37impl Direction {
38    /// Switches a direction to the opposite one
39    pub fn toggle(&mut self) {
40        use Direction::*;
41        *self = match *self {
42            Forward => Backward,
43            Backward => Forward,
44        }
45    }
46}
47
48/// Speed and Direction packet. Used to command a loco to move in the
49/// given direction at the given speed.
50///
51/// The speed part of the instruction is five bits wide, with the bits
52/// ordered `04321`, where `0` is LSB and `4` is MSB. The speed
53/// instructions are defined by the following list:
54/// ```ignore
55///  0 4321 | meaning
56///  ---------------------------------------------
57///  0 0000 | stop
58///  1 0000 | also stop
59///  0 0001 | e-stop
60///  1 0001 | also e-stop
61///  0 0010 | speed 1 (0x04)
62///   ...   |   ...
63///  1 1111 | speed 28 (0x1f)
64/// ```
65pub struct SpeedAndDirection {
66    address: u8,
67    instruction: u8,
68}
69
70impl SpeedAndDirection {
71    /// Builder interface for `SpeedAndDirection`. Use of the Builder
72    /// pattern ensures that only valid packets are produced.
73    pub fn builder() -> SpeedAndDirectionBuilder {
74        SpeedAndDirectionBuilder::default()
75    }
76
77    /// Serialise the packed into the provided buffer
78    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
79        super::serialise(
80            &[
81                self.address,
82                self.instruction,
83                self.address ^ self.instruction,
84            ],
85            buf,
86        )
87    }
88}
89
90/// Builder used to construct a SpeedAndDirection packet
91#[derive(Default)]
92pub struct SpeedAndDirectionBuilder {
93    address: Option<u8>,
94    speed: Option<u8>,
95    e_stop: bool,
96    direction: Option<Direction>,
97}
98
99impl SpeedAndDirectionBuilder {
100    /// Sets the address. In short mode the address has to be between 1
101    /// and 126. Returns `Error::InvalidAddress` if the provided address
102    /// is outside this range.
103    pub fn address(&mut self, address: u8) -> Result<&mut Self> {
104        if address == 0 || address > 0x7f {
105            Err(Error::InvalidAddress)
106        } else {
107            self.address = Some(address);
108            Ok(self)
109        }
110    }
111
112    /// Sets the speed. In short mode the speed has to be between 0 and
113    /// 16. Returns `Error::InvalidSpeed` if the provided speed is outside
114    /// this range.
115    pub fn speed(&mut self, speed: u8) -> Result<&mut Self> {
116        if speed > 28 {
117            Err(Error::InvalidSpeed)
118        } else {
119            self.speed = Some(speed);
120            Ok(self)
121        }
122    }
123
124    /// Sets the direction
125    pub fn direction(&mut self, direction: Direction) -> &mut Self {
126        self.direction = Some(direction);
127        self
128    }
129
130    /// Sends the e-stop signal. Overrides any other set speed value
131    pub fn e_stop(&mut self, e_stop: bool) -> &mut Self {
132        self.e_stop = e_stop;
133        self
134    }
135
136    /// Build a `SpeedAndDirection` packet using the provided values,
137    /// falling back to sensible defaults if not all fields have been
138    /// provided.
139    ///
140    /// Defaults:
141    /// * `speed = 0`
142    /// * `direction = Forward`
143    /// * `address = 3`
144    /// * `headlight = false`
145    pub fn build(&mut self) -> SpeedAndDirection {
146        let address = self.address.unwrap_or(3);
147        // add the weird offset to the speed
148        let speed = match self.speed {
149            Some(0) | None => 0,
150            Some(speed) => speed + 3,
151        };
152        #[cfg(test)]
153        eprintln!("Speed is {speed} = {speed:08b}");
154        let mut instruction = 0b0100_0000; // packet type
155        if let Direction::Forward = self.direction.unwrap_or_default() {
156            instruction |= 0b0010_0000;
157        }
158
159        // e-stop overrides other speed setting
160        if self.e_stop {
161            instruction |= 0x01;
162        } else {
163            // upper four bits of speed
164            instruction |= (speed >> 1) & 0x0f;
165
166            // LSB of speed
167            instruction |= (speed & 0x01) << 4;
168        }
169
170        SpeedAndDirection {
171            address,
172            instruction,
173        }
174    }
175}
176
177/// A Reset packet is one in which the address, instruction, and ECC are
178/// all zero. All decoders will, upon receiving this packet, reset to their
179/// normal power-up state. Any speed or direction will be cleared and
180/// locomotives stopped.
181pub struct Reset;
182
183impl Reset {
184    /// Serialise the packed into the provided buffer
185    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
186        super::serialise(&[0x00, 0x00, 0x00], buf)
187    }
188}
189
190/// An Idle packet is one in which the address is 0xff and instruction 0x00.
191/// Upon receiving this, a decoder performs no new action.
192pub struct Idle;
193
194impl Idle {
195    /// Serialise the packed into the provided buffer
196    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
197        super::serialise(&[0xff, 0x00, 0xff], buf)
198    }
199}
200
201/// Instruct all decoders to stop, either immediately or by simply
202/// removing motor power ("float"). The specification allows a direction
203/// field, but that is not implemented here because in situations where
204/// a BroadcastStop is being sent it is unlikely that direction settings
205/// will be important. (whereas a regular stop might wish to retain
206/// headlight states)
207pub struct BroadcastStop {
208    float: bool,
209}
210
211impl BroadcastStop {
212    /// Bring all locomotives to an immediate stop
213    pub fn immediate() -> Self {
214        Self { float: false }
215    }
216
217    /// Bring all locomotives to a gently/floating stop
218    pub fn float() -> Self {
219        Self { float: true }
220    }
221
222    /// Serialise the packed into the provided buffer
223    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
224        let instr = if self.float { 0b0101_0000 } else { 0b0100_0000 };
225
226        super::serialise(&[0x00, instr, instr], buf)
227    }
228}
229
230#[cfg(test)]
231mod test {
232    use super::*;
233
234    fn display_serialise_buffer(buf: &SerialiseBuffer) {
235        println!("{buf:?}");
236        //        15              1 8        1 8        1 8        1
237        //        15              16 24      25 33      34 42      43
238        println!("ppppppppppppppp s aaaaaaaa s 01dvvvvv s cccccccc s");
239        println!(
240            "{} {} {} {} {} {} {} {}",
241            buf[..15]
242                .iter()
243                .map(|b| if *b { "1" } else { "0" })
244                .collect::<Vec<_>>()
245                .join(""),
246            if *buf.get(15).unwrap() { "1" } else { "0" },
247            buf[16..24]
248                .iter()
249                .map(|b| if *b { "1" } else { "0" })
250                .collect::<Vec<_>>()
251                .join(""),
252            if *buf.get(24).unwrap() { "1" } else { "0" },
253            buf[25..33]
254                .iter()
255                .map(|b| if *b { "1" } else { "0" })
256                .collect::<Vec<_>>()
257                .join(""),
258            if *buf.get(33).unwrap() { "1" } else { "0" },
259            buf[34..42]
260                .iter()
261                .map(|b| if *b { "1" } else { "0" })
262                .collect::<Vec<_>>()
263                .join(""),
264            if *buf.get(42).unwrap() { "1" } else { "0" },
265        );
266    }
267
268    #[test]
269    fn make_speed_and_direction() -> Result<()> {
270        let pkt = SpeedAndDirection::builder()
271            .address(35)?
272            .speed(14)?
273            .direction(Direction::Forward)
274            .build();
275        assert_eq!(pkt.address, 35);
276        let expected = 0b0111_1000;
277        eprintln!("Got instruction: {:08b}", pkt.instruction);
278        eprintln!("Expected:        {expected:08b}");
279        assert_eq!(pkt.instruction, expected);
280
281        Ok(())
282    }
283
284    #[test]
285    fn serialise_speed_and_direction() -> Result<()> {
286        let pkt = SpeedAndDirection::builder()
287            .address(35)?
288            .speed(14)?
289            .direction(Direction::Forward)
290            .build();
291        let mut buf = SerialiseBuffer::default();
292        let len = pkt.serialise(&mut buf)?;
293        // instruction is:
294        // 01 D S SSSS
295        // 01 1 1 1101
296        #[allow(clippy::unusual_byte_groupings)]
297        let expected_arr = [
298            0xff_u8,      // preamble
299            0b1111_1110,  // preamble + start
300            35,           // address
301            0b0_0111_100, // start + instr[..7]
302            0b0_0_010110, // instr[7] + start + ecc[..6]
303            0b11_1_00000, // ecc[6..] + stop + 5 zeroes
304        ];
305        let mut expected = SerialiseBuffer::default();
306        expected[..43]
307            .copy_from_bitslice(&expected_arr.view_bits::<Msb0>()[..43]);
308        println!("got:");
309        display_serialise_buffer(&buf);
310        println!("expected:");
311        display_serialise_buffer(&expected);
312        assert_eq!(len, 43);
313        assert_eq!(buf[..len], expected[..43]);
314        Ok(())
315    }
316
317    #[test]
318    fn serialise_reset_packet() -> Result<()> {
319        let pkt = Reset;
320        let mut buf = SerialiseBuffer::default();
321        let len = pkt.serialise(&mut buf)?;
322
323        #[allow(clippy::unusual_byte_groupings)]
324        let expected_arr = [
325            0xff_u8,      // preamble
326            0b1111_1110,  // preamble + start
327            0x00,         // address
328            0b0_0000_000, // start + instr[..7]
329            0b0_0_000000, // instr[7] + start + ecc[..6]
330            0b00_1_00000, // ecc[6..] + stop + 5 zeroes
331        ];
332
333        let mut expected = SerialiseBuffer::default();
334        expected[..43]
335            .copy_from_bitslice(&expected_arr.view_bits::<Msb0>()[..43]);
336        println!("got:");
337        display_serialise_buffer(&buf);
338        println!("expected:");
339        display_serialise_buffer(&expected);
340        assert_eq!(len, 43);
341        assert_eq!(buf[..len], expected[..43]);
342        Ok(())
343    }
344
345    #[test]
346    fn serialise_idle_packet() -> Result<()> {
347        let pkt = Idle;
348        let mut buf = SerialiseBuffer::default();
349        let len = pkt.serialise(&mut buf)?;
350
351        #[allow(clippy::unusual_byte_groupings)]
352        let expected_arr = [
353            0xff_u8,      // preamble
354            0b1111_1110,  // preamble + start
355            0xff,         // address
356            0b0_0000_000, // start + instr[..7]
357            0b0_0_111111, // instr[7] + start + ecc[..6]
358            0b11_1_00000, // ecc[6..] + stop + 5 zeroes
359        ];
360
361        let mut expected = SerialiseBuffer::default();
362        expected[..43]
363            .copy_from_bitslice(&expected_arr.view_bits::<Msb0>()[..43]);
364        println!("got:");
365        display_serialise_buffer(&buf);
366        println!("expected:");
367        display_serialise_buffer(&expected);
368        assert_eq!(len, 43);
369        assert_eq!(buf[..len], expected[..43]);
370        Ok(())
371    }
372
373    #[test]
374    fn serialise_broadcast_stop_packet() -> Result<()> {
375        let pkt = BroadcastStop::float();
376        let mut buf = SerialiseBuffer::default();
377        let len = pkt.serialise(&mut buf)?;
378
379        #[allow(clippy::unusual_byte_groupings)]
380        let expected_arr = [
381            0xff_u8,      // preamble
382            0b1111_1110,  // preamble + start
383            0x00,         // address
384            0b0_0101_000, // start + instr[..7]
385            0b0_0_010100, // instr[7] + start + ecc[..6]
386            0b00_1_00000, // ecc[6..] + stop + 5 zeroes
387        ];
388
389        let mut expected = SerialiseBuffer::default();
390        expected[..43]
391            .copy_from_bitslice(&expected_arr.view_bits::<Msb0>()[..43]);
392        println!("got:");
393        display_serialise_buffer(&buf);
394        println!("expected:");
395        display_serialise_buffer(&expected);
396        assert_eq!(len, 43);
397        assert_eq!(buf[..len], expected[..43]);
398        Ok(())
399    }
400}