wte_mt_rx_parser/
lib.rs

1//! A Rust parser for "MT-RX-3 AIS, 406 + 121.5/243 MHz ALERTING RECEIVER" messages.
2//!
3//! Please refer to [MT-RX-3 User Manual](https://www.wte.co.nz/uploads/9/9/8/6/99862766/mt-rx-3_406_epirb_receiver-manual_v2-62.pdf) for more information.
4
5use std::num::ParseIntError;
6
7use mt_raw::MtRaw;
8use mt_structured::MtStructured;
9use rss::Rss;
10use thiserror::Error;
11
12pub mod mt_raw;
13pub mod mt_structured;
14pub mod rss;
15
16/// Represents an error when parsing a message went wrong.
17#[derive(Error, Clone, Debug, PartialEq)]
18pub enum ParseError {
19    #[error("failed to parse number")]
20    ParseIntError(#[from] ParseIntError),
21
22    #[error("invalid message size (expected {expected:?}, found {found:?})")]
23    SizeNotMatch { expected: usize, found: usize },
24
25    #[error("invalid sentence, not parsable")]
26    Invalid,
27}
28
29/// Represents the parsed message.
30#[derive(Clone, Debug, PartialEq)]
31pub enum ParsedMessage {
32    /// RSS - Received Signal Strength message.
33    Rss(Rss),
34
35    /// MT Serial Out Packet Format message.
36    MtStructured(MtStructured),
37
38    /// MT Raw Data Serial Out Packet Format message.
39    MtRaw(MtRaw),
40
41    /// Invalid message.
42    Invalid,
43}
44
45/// Tries to parse `message` into one of [`ParsedMessage`] types.
46/// Returns [`ParsedMessage::Invalid`] if it's an invalid message, or [`ParseError`] if parsing went wrong.
47///
48/// ## Examples
49/// ```
50/// let samples = vec![
51///     "MT1001000AL400C592753572B323433212S1723756E4706",
52///     "MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79F84B",
53///     "SS,A,123",
54///     "SS,1,123",
55/// ];
56///
57/// for s in samples {
58///     println!("{:?}", wte_mt_rx_parser::parse(s));
59/// }
60/// ```
61pub fn parse(message: &str) -> Result<ParsedMessage, ParseError> {
62    let parsed = match message.trim() {
63        msg if rss::is_rss(msg) => ParsedMessage::Rss(rss::parse(msg)?),
64        msg if mt_structured::is_mt(msg) => ParsedMessage::MtStructured(mt_structured::parse(msg)?),
65        msg if mt_raw::is_mt(msg) => ParsedMessage::MtRaw(mt_raw::parse(msg)?),
66        _ => ParsedMessage::Invalid,
67    };
68    Ok(parsed)
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn sample_usage() {
77        let samples = vec![
78            "MT1001000AL400C592753572B323433212S1723756E4706",
79            "MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79F84B",
80            "SS,A,123",
81            "SS,1,123",
82        ];
83
84        for s in samples {
85            println!("{:?}", parse(s));
86        }
87    }
88
89    #[test]
90    fn mt_structured() {
91        assert!(parse("MT1001000AL400C592753572B323433212S1723756E4706").is_ok());
92        assert!(parse("MT1001000AL400C592753572B323433212S1723756E4706").is_ok());
93    }
94
95    #[test]
96    fn mt_raw() {
97        assert!(parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79F84B").is_ok());
98        assert!(parse("MT6001001FFFE2FA0062C93A9AB959E55EE7788C71B791131").is_ok());
99    }
100
101    #[test]
102    fn invalid_mt() {
103        // invalid headers
104        let invalid_samples = vec![
105            "MT2001000AL400C592753572B323433212S1723756E4706",
106            "MT3001000AL400C592753572B323433212S1723756E4706",
107            "MT4001000AL400C592753572B323433212S1723756E4706",
108            "MT5001000AL400C592753572B323433212S1723756E4706",
109            "MT8001000AL400C592753572B323433212S1723756E4706",
110            "MT9001000AL400C592753572B323433212S1723756E4706",
111            "MT0001000AL400C592753572B323433212S1723756E4706",
112        ];
113        for s in invalid_samples {
114            assert_eq!(parse(s).unwrap(), ParsedMessage::Invalid);
115        }
116
117        // smaller than expected
118        assert!(parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B").is_err());
119        assert!(parse("MT1001000AL400C592753572B323433212S172375").is_err());
120
121        // MT1 UUU NNN T F HHHHHHHHHHHHHHH SS 11 22 33 N 444 55 66 W YYYY
122        // MT6 UUU NNN RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR YYYY
123
124        // not parsable numbers
125
126        // unparsable NNN
127        assert!(parse("MT1001aaaAL400C592753572B323433212S1723756E4706").is_err());
128        assert!(parse("MT6001aaaFFFE2FA00E0000CBAB959DB0903788C71B79F84B").is_err());
129
130        // unparsable checksum
131        assert!(parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79ZZZZ").is_err());
132    }
133
134    #[test]
135    fn rss() {
136        assert!(parse("SS,A,123\n").is_ok());
137        assert!(parse("SS,1,123\n").is_ok());
138    }
139
140    #[test]
141    fn invalid_rss() {
142        assert!(parse("SS,1,666\n").is_err()); // nnn too big
143        assert!(parse("SS,1,12367\n").is_err()); // nnn too big
144        assert!(parse("SS,2,123\n").is_err()); // invalid type
145        assert!(parse("SS,A,1234\n").is_err()); // nnn too big
146        assert!(parse("SS,X,123\n").is_err()); // invalid type
147    }
148
149    #[test]
150    fn hardcoded_checksum() {
151        assert_eq!(
152            mt_raw::compute_checksum("FFFE2FA00E0000CBAB959DB0903788C71B79".as_bytes()),
153            0xf84b
154        );
155    }
156
157    #[test]
158    fn mt_raw_checksum() {
159        if let Ok(ParsedMessage::MtRaw(v)) =
160            parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79F84B")
161        {
162            assert_eq!(mt_raw::compute_checksum(&v.data.as_bytes()), v.checksum);
163        }
164    }
165}