1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! MT(1) Raw Data Serial Out Packet Format

use crate::ParseError;

/// MT Serial Out Packet Format.
#[derive(Clone, Debug, PartialEq)]
pub struct MtStructured {
    /// Header, should be `MT1`.
    pub header: String,

    /// UUU - Three character MT-RX configurable ID – by default this is `001`.
    pub id: String,

    /// NNN - Three decimal digit cycling packet sequence number from `000` to `511`. This sequence number
    // increments after each new test or distress message is received. After `511` the sequence cycles to `000`
    // and begins again.
    pub sequence_number: usize,

    /// T - Single character message type 'T' or 'A' (test or distress alert)
    pub message_type: char,

    /// F - Single character format flag `S` or `L` (short or long) – this relates to the 406 beacon transmission specification.
    pub format_flag: char,

    /// 15 character hex code used to define beacon owner and beacon capabilities as per the 406 beacon specification.
    pub beacon: [u8; 15],

    /// SS - Two character signal strength indication – `00` if not used.
    pub signal_strength: [char; 2],

    /// 11 - Two decimal character latitude degrees.
    pub lat_degrees: u8,

    /// 22 - Two decimal character latitude minutes.
    pub lat_minutes: u8,

    /// 33 - Two decimal character latitude seconds.
    pub lat_seconds: u8,

    /// N - is `N` or `S`.
    pub n: char,

    /// 444 - Three decimal character longitude degrees.
    pub long_degrees: u16,

    /// 22 - Two decimal character longitude minutes.
    pub long_minutes: u8,

    /// 55 - Two decimal character longitude seconds.
    pub long_seconds: u8,

    /// W - is `W` or `E`.
    pub w: char,

    /// YYYY - Four character checksum (calculated from M – the first character).
    /// If all location characters are '-' then there is no location information available.
    pub checksum: u16,
}

impl MtStructured {
    /// Returns whether `message` is a valid MT(1) message.
    ///
    /// ## Examples
    /// ```
    /// use wte_mt_rx_parser::mt_structured::MtStructured;
    /// println!("is it MT1? {}", MtStructured::is_mt("MT1001000AL400C592753572B323433212S1723756E4706"));
    /// ```
    pub fn is_mt(message: &str) -> bool {
        return message.starts_with("MT1");
    }

    /// Tries to parse a "MT Serial Out Packet Format" `message`.
    ///
    /// ## Notes
    /// - Checksum is not calculated here.
    ///
    /// ## Examples
    /// ```
    /// use wte_mt_rx_parser::mt_structured::MtStructured;
    /// let parsed = MtStructured::parse("MT1001000AL400C592753572B323433212S1723756E4706").unwrap();
    /// // ...
    /// ```
    ///
    /// ## Message format
    /// Data provided should be in the following format:
    /// - `MT1UUUNNNTFHHHHHHHHHHHHHHHSS112233N4445566WYYYY`
    pub fn parse(message: &str) -> Result<MtStructured, ParseError> {
        // 012 345 678 9 0 123456789012345 67 89 01 23 4 567 89 01 2 3456
        // MT1 UUU NNN T F HHHHHHHHHHHHHHH SS 11 22 33 N 444 55 66 W YYYY

        const MT1_LEN: usize = 47;
        if message.len() != MT1_LEN {
            return Err(ParseError::SizeNotMatch {
                expected: MT1_LEN,
                found: message.len(),
            });
        }

        let header = message[0..3].to_string();
        let id = message[3..6].to_string();
        let sequence_number = message[6..9].parse::<usize>()?;
        let message_type = message.as_bytes()[9] as char;
        let format_flag = message.as_bytes()[10] as char;
        let beacon: [u8; 15] = message[11..26].as_bytes().try_into().unwrap();
        let signal_strength = [
            message.as_bytes()[26] as char,
            message.as_bytes()[27] as char,
        ];
        let lat_degrees = message[28..30].parse::<u8>()?;
        let lat_minutes = message[30..32].parse::<u8>()?;
        let lat_seconds = message[32..34].parse::<u8>()?;
        let n = message.as_bytes()[34] as char;
        let long_degrees = message[35..38].parse::<u16>()?;
        let long_minutes = message[38..40].parse::<u8>()?;
        let long_seconds = message[40..42].parse::<u8>()?;
        let w = message.as_bytes()[42] as char;
        let checksum = u16::from_str_radix(&message[43..47], 16)?;

        // TODO: calculate checksum here?

        let result = MtStructured {
            header,
            id,
            sequence_number,
            message_type,
            format_flag,
            beacon,
            signal_strength,
            lat_degrees,
            lat_minutes,
            lat_seconds,
            n,
            long_degrees,
            long_minutes,
            long_seconds,
            w,
            checksum,
        };

        Ok(result)
    }
}