nmea_parser/ais/
vdm_t9.rs

1/*
2Copyright 2020 Timo Saarinen
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17use super::*;
18
19// -------------------------------------------------------------------------------------------------
20
21/// Type 9: Standard SAR Aircraft Position Report
22#[derive(Default, Clone, Debug, PartialEq)]
23pub struct StandardSarAircraftPositionReport {
24    /// True if the data is about own vessel, false if about other.
25    pub own_vessel: bool,
26
27    /// AIS station type.
28    pub station: Station,
29
30    /// User ID (30 bits)
31    pub mmsi: u32,
32
33    /// Altitude
34    pub altitude: Option<u16>,
35
36    /// Speed over ground in knots. Value 1022 means 1022 knots or more.
37    pub sog_knots: Option<u16>,
38
39    /// Position accuracy: true = high (<= 10 m), false = low (> 10 m)
40    pub high_position_accuracy: bool,
41
42    /// Latitude
43    pub latitude: Option<f64>,
44
45    /// Longitude
46    pub longitude: Option<f64>,
47
48    /// Course over ground
49    pub cog: Option<f64>,
50
51    /// Derived from UTC second (6 bits)
52    pub timestamp_seconds: u8,
53
54    /// Regional, reserved.
55    pub regional: u8,
56
57    /// Data terminal ready:
58    /// true = ready,
59    /// false = not ready
60    pub dte: bool,
61
62    /// Assigned flag.
63    pub assigned: bool,
64
65    /// Riverine And Inland Navigation systems blue sign:
66    /// RAIM (Receiver autonomous integrity monitoring) flag of electronic position
67    /// fixing device; false = RAIM not in use = default; true = RAIM in use
68    pub raim_flag: bool,
69
70    /// Radio status (20 bits).
71    pub radio_status: u32,
72}
73
74impl LatLon for StandardSarAircraftPositionReport {
75    fn latitude(&self) -> Option<f64> {
76        self.latitude
77    }
78
79    fn longitude(&self) -> Option<f64> {
80        self.longitude
81    }
82}
83
84// -------------------------------------------------------------------------------------------------
85
86/// AIS VDM/VDO type 9: Standard SAR Aircraft Position Report
87pub(crate) fn handle(
88    bv: &BitVec,
89    station: Station,
90    own_vessel: bool,
91) -> Result<ParsedMessage, ParseError> {
92    Ok(ParsedMessage::StandardSarAircraftPositionReport(
93        StandardSarAircraftPositionReport {
94            own_vessel: { own_vessel },
95            station: { station },
96            mmsi: { pick_u64(bv, 8, 30) as u32 },
97            altitude: {
98                let raw = pick_u64(bv, 38, 12) as u16;
99                if raw != 4095 {
100                    Some(raw)
101                } else {
102                    None
103                }
104            },
105            sog_knots: {
106                let raw = pick_u64(bv, 50, 10) as u16;
107                if raw != 1023 {
108                    Some(raw)
109                } else {
110                    None
111                }
112            },
113            high_position_accuracy: { pick_u64(bv, 60, 1) != 0 },
114            latitude: {
115                let lat_raw = pick_i64(bv, 89, 27) as i32;
116                if lat_raw != 0x3412140 {
117                    Some((lat_raw as f64) / 600000.0)
118                } else {
119                    None
120                }
121            },
122            longitude: {
123                let lon_raw = pick_i64(bv, 61, 28) as i32;
124                if lon_raw != 0x6791AC0 {
125                    Some((lon_raw as f64) / 600000.0)
126                } else {
127                    None
128                }
129            },
130            cog: {
131                let cog_raw = pick_u64(bv, 116, 12);
132                if cog_raw != 0xE10 {
133                    Some(cog_raw as f64 * 0.1)
134                } else {
135                    None
136                }
137            },
138            timestamp_seconds: pick_u64(bv, 128, 6) as u8,
139            regional: { pick_u64(bv, 134, 8) as u8 },
140            dte: { pick_u64(bv, 142, 1) == 0 },
141            assigned: { pick_u64(bv, 146, 1) != 0 },
142            raim_flag: { pick_u64(bv, 147, 1) != 0 },
143            radio_status: { pick_u64(bv, 148, 20) as u32 },
144        },
145    ))
146}
147
148// -------------------------------------------------------------------------------------------------
149
150#[cfg(test)]
151mod test {
152    use super::*;
153
154    #[test]
155    fn test_parse_vdm_type9() {
156        let mut p = NmeaParser::new();
157        match p.parse_sentence("!AIVDM,1,1,,B,91b55wi;hbOS@OdQAC062Ch2089h,0*30") {
158            Ok(ps) => {
159                match ps {
160                    // The expected result
161                    ParsedMessage::StandardSarAircraftPositionReport(sapr) => {
162                        assert_eq!(sapr.mmsi, 111232511);
163                        assert_eq!(sapr.altitude, Some(303));
164                        assert_eq!(sapr.sog_knots, Some(42));
165                        assert!(!sapr.high_position_accuracy);
166                        assert::close(sapr.longitude.unwrap_or(0.0), -6.27884, 0.00001);
167                        assert::close(sapr.latitude.unwrap_or(0.0), 58.144, 0.00001);
168                        assert_eq!(sapr.cog, Some(154.5));
169                        assert_eq!(sapr.timestamp_seconds, 15);
170                        assert_eq!(sapr.regional, 0);
171                        assert!(!sapr.dte);
172                        assert!(!sapr.assigned);
173                        assert!(!sapr.raim_flag);
174                        assert_eq!(sapr.radio_status, 33392);
175                    }
176                    ParsedMessage::Incomplete => {
177                        assert!(false);
178                    }
179                    _ => {
180                        assert!(false);
181                    }
182                }
183            }
184            Err(e) => {
185                assert_eq!(e.to_string(), "OK");
186            }
187        }
188    }
189}