Skip to main content

aprs_decode/
user_defined.rs

1/// A user-defined / experimental APRS packet.
2///
3/// DTI: `{`
4///
5/// The first byte after the DTI is a single-character experimenter ID, the
6/// second is a packet type character, and the rest is opaque payload.
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct AprsUserDefined {
10    /// Experimenter / user ID (single byte).
11    pub user_id: u8,
12    /// User-defined packet type (single byte).
13    pub packet_type: u8,
14    /// Opaque payload.
15    pub data: Vec<u8>,
16}
17
18impl AprsUserDefined {
19    /// Decode from the information field (including the leading `{` DTI byte).
20    pub(crate) fn parse(info: &[u8]) -> Self {
21        let body = info.get(1..).unwrap_or_default();
22        Self {
23            user_id: body.first().copied().unwrap_or(0),
24            packet_type: body.get(1).copied().unwrap_or(0),
25            data: body.get(2..).unwrap_or_default().to_vec(),
26        }
27    }
28
29    pub fn encode(&self) -> Vec<u8> {
30        let mut out = vec![b'{', self.user_id, self.packet_type];
31        out.extend_from_slice(&self.data);
32        out
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn round_trip() {
42        let raw = b"{Qhello world";
43        let ud = AprsUserDefined::parse(raw);
44        assert_eq!(ud.user_id, b'Q');
45        assert_eq!(ud.packet_type, b'h');
46        assert_eq!(ud.data, b"ello world");
47        assert_eq!(ud.encode().as_slice(), raw.as_slice());
48    }
49
50    #[test]
51    fn minimal() {
52        let ud = AprsUserDefined::parse(b"{AB");
53        assert_eq!(ud.user_id, b'A');
54        assert_eq!(ud.packet_type, b'B');
55        assert!(ud.data.is_empty());
56    }
57}