ais/messages/
dgnss_broadcast_binary_message.rs

1//! Standard Class B Position Report (type 18)
2use super::parsers::*;
3use super::AisMessageType;
4use crate::errors::Result;
5use crate::lib;
6use nom::bits::{bits, complete::take as take_bits};
7use nom::combinator::map;
8use nom::IResult;
9
10#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
11const MAX_DATA_SIZE_BYTES: usize = 119;
12
13#[cfg(any(feature = "std", feature = "alloc"))]
14pub type CorrectionData = lib::std::vec::Vec<u8>;
15#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
16pub type CorrectionData = lib::std::vec::Vec<u8, MAX_DATA_SIZE_BYTES>;
17
18#[derive(Debug, PartialEq)]
19pub struct DgnssBroadcastBinaryMessage {
20    pub message_type: u8,
21    pub repeat_indicator: u8,
22    pub mmsi: u32,
23    pub longitude: Option<f32>,
24    pub latitude: Option<f32>,
25    pub payload: DifferentialCorrectionData,
26}
27
28#[derive(Debug, PartialEq, Eq)]
29pub struct DifferentialCorrectionData {
30    pub message_type: u8,
31    pub station_id: u16,
32    pub z_count: u16,
33    pub sequence_number: u8,
34    pub n: u8,
35    pub health: u8,
36    pub data: CorrectionData,
37}
38
39impl DifferentialCorrectionData {
40    fn parse(data: (&[u8], usize)) -> IResult<(&[u8], usize), Self> {
41        let (data, message_type) = take_bits(6u8)(data)?;
42        let (data, station_id) = take_bits(10u8)(data)?;
43        let (data, z_count) = take_bits(13u8)(data)?;
44        let (data, sequence_number) = take_bits(3u8)(data)?;
45        let (data, n) = take_bits(5u8)(data)?;
46        let (data, health) = take_bits(3u8)(data)?;
47        #[cfg(any(feature = "std", feature = "alloc"))]
48        let data_owned = data.0.into();
49        #[cfg(all(not(feature = "std"), not(feature = "alloc")))]
50        let data_owned = data.0.try_into().map_err(|_| {
51            nom::Err::Failure(nom::error::Error::new(
52                data,
53                nom::error::ErrorKind::TooLarge,
54            ))
55        })?;
56        Ok((
57            (<&[u8]>::default(), 0),
58            Self {
59                message_type,
60                station_id,
61                z_count,
62                sequence_number,
63                n,
64                health,
65                data: data_owned,
66            },
67        ))
68    }
69}
70
71impl<'a> AisMessageType<'a> for DgnssBroadcastBinaryMessage {
72    fn name(&self) -> &'static str {
73        "DGNSS Broadcast Binary Message"
74    }
75
76    fn parse(data: &'a [u8]) -> Result<Self> {
77        let (_, message) = parse_base(data)?;
78        Ok(message)
79    }
80}
81
82fn parse_longitude_min_10(data: i32) -> Option<f32> {
83    match data {
84        108_600 => None,
85        _ => Some(data as f32 / 600.0),
86    }
87}
88
89fn parse_latitude_min_10(data: i32) -> Option<f32> {
90    match data {
91        54_600 => None,
92        _ => Some(data as f32 / 600.0),
93    }
94}
95
96fn parse_base(data: &[u8]) -> IResult<&[u8], DgnssBroadcastBinaryMessage> {
97    bits(move |data| -> IResult<_, _> {
98        let (data, message_type) = take_bits(6u8)(data)?;
99        let (data, repeat_indicator) = take_bits(2u8)(data)?;
100        let (data, mmsi) = take_bits(30u32)(data)?;
101        let (data, _spare) = take_bits::<_, u8, _, _>(2u8)(data)?;
102        let (data, longitude) = map(|data| signed_i32(data, 18), parse_longitude_min_10)(data)?;
103        let (data, latitude) = map(|data| signed_i32(data, 17), parse_latitude_min_10)(data)?;
104        let (data, _spare) = take_bits::<_, u8, _, _>(5u8)(data)?;
105        let (data, payload) = DifferentialCorrectionData::parse(data)?;
106        Ok((
107            data,
108            DgnssBroadcastBinaryMessage {
109                message_type,
110                repeat_indicator,
111                mmsi,
112                longitude,
113                latitude,
114                payload,
115            },
116        ))
117    })(data)
118}
119
120#[cfg(test)]
121mod tests {
122    #![allow(clippy::unreadable_literal)]
123    use super::*;
124    use crate::test_helpers::*;
125
126    #[test]
127    fn test_message() {
128        let bytestream =
129            b"A02VqLPA4I6C07h5Ed1h<OrsuBTTwS?r:C?w`?la<gno1RTRwSP9:BcurA8a:Oko02TSwu8<:Jbb";
130        let bitstream = crate::messages::unarmor(bytestream, 0).unwrap();
131        let message = DgnssBroadcastBinaryMessage::parse(bitstream.as_ref()).unwrap();
132        assert_eq!(message.message_type, 17);
133        assert_eq!(message.repeat_indicator, 0);
134        assert_eq!(message.mmsi, 2734450);
135        f32_equal_naive(message.longitude.unwrap(), 29.13);
136        f32_equal_naive(message.latitude.unwrap(), 59.986668);
137        assert_eq!(message.payload.z_count, 2776);
138        assert_eq!(message.payload.data.len(), 42);
139    }
140}