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}