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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
//! MT(1) Raw Data Serial Out Packet Format
//!
//! Data provided from the MT-RX via serial ports/TCP is in the following format:
//! - `MT1UUUNNNTFHHHHHHHHHHHHHHHSS112233N4445566WYYYY`
//!
//! Where:
//! - `MT1` is fixed and actually “MT1”
//! - `UUU` - is a 3 character MT-RX configurable ID – by default this is “001”
//! - `NNN` -is a 3 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.
//! - `T` – is a single character message type 'T' or 'A' (test or distress alert)
//! - `F` – is a single character format flag 'S' or 'L' (short or long) – this relates to the 406 beacon
//! transmission specification.
//! - `HHHHHHHHHHHHHHH` – is a 15 character hex code used to define beacon owner and beacon
//! capabilities as per the 406 beacon specification.
//! - `SS` – is a 2 character signal strength indication – “00” if not used.
//! - `11` – is a 2 decimal character latitude degrees
//! - `22` – is a 2 decimal character latitude minutes
//! - `33` – is a 2 decimal character latitude seconds
//! - `N` – is 'N' or 'S'
//! - `444` – is a 3 decimal character longitude degrees
//! - `22` – is a 2 decimal character longitude minutes
//! - `55` – is a 2 decimal character longitude seconds
//! - `W–` is 'W' or 'E'
//! - `YYYY` – is a 4 character checksum (calculated from M – the first character)
//! - If all location characters are '-' then there is no location information available.
//!
//! Legitimate example packet:
//! `MT1001000AL400C592753572B323433212S1723756E4706`
use crate::ParseError;
/// Represents a MT message type.
#[derive(Clone, Debug, PartialEq)]
pub enum MtMessageType {
Test,
Alert,
Unknown
}
impl std::convert::From<char> for MtMessageType {
fn from(c: char) -> Self {
match c {
'T' => MtMessageType::Test,
'A' => MtMessageType::Alert,
_ => MtMessageType::Unknown,
}
}
}
/// MT Serial Out Packet Format.
#[derive(Clone, Debug, PartialEq)]
pub struct MtStructured {
/// `MT1`.
pub header: String,
/// MT-RX configurable ID.
pub id: String,
/// Cycling packet sequence number.
pub sequence_number: usize,
/// Message type.
pub message_type: MtMessageType,
/// Character format flag.
pub format_flag: char,
/// Beacon owner and capabilities.
pub beacon: [u8; 15],
/// Signal strength indication.
pub signal_strength: [char; 2],
/// Latitude degrees.
pub lat_degrees: u8,
/// Latitude minutes.
pub lat_minutes: u8,
/// Latitude seconds.
pub lat_seconds: u8,
/// `N` or `S`.
pub n: char,
/// Longitude degrees.
pub long_degrees: u16,
/// Longitude minutes.
pub long_minutes: u8,
/// Longitude seconds.
pub long_seconds: u8,
/// `W` or `E`.
pub w: char,
/// Checksum.
/// If there is no location information available, hence the value will be `0`.
pub checksum: u16,
}
/// Returns whether `message` is a valid MT(1) message.
///
/// ## Examples
/// ```
/// use wte_mt_rx_parser::mt_structured;
/// println!("is it MT1? {}", mt_structured::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;
/// let parsed = mt_structured::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).into();
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).unwrap_or(0);
// 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)
}