1use super::*;
18
19#[derive(Default, Clone, Debug, PartialEq)]
23pub struct AidToNavigationReport {
24 pub own_vessel: bool,
26
27 pub station: Station,
29
30 pub mmsi: u32,
32
33 pub aid_type: NavAidType,
35
36 pub name: String,
38
39 high_position_accuracy: bool,
41
42 pub latitude: Option<f64>,
44
45 pub longitude: Option<f64>,
47
48 pub dimension_to_bow: Option<u16>,
50 pub dimension_to_stern: Option<u16>,
52 pub dimension_to_port: Option<u16>,
54 pub dimension_to_starboard: Option<u16>,
56
57 pub position_fix_type: Option<PositionFixType>,
59
60 pub timestamp_seconds: u8,
62
63 pub off_position_indicator: bool,
66
67 pub regional: u8,
69
70 pub raim_flag: bool,
74
75 pub virtual_aid_flag: bool,
79
80 pub assigned_mode_flag: bool,
82}
83
84impl LatLon for AidToNavigationReport {
85 fn latitude(&self) -> Option<f64> {
86 self.latitude
87 }
88
89 fn longitude(&self) -> Option<f64> {
90 self.longitude
91 }
92}
93
94#[derive(Clone, Copy, Debug, PartialEq)]
96pub enum NavAidType {
97 NotSpecified, ReferencePoint, Racon, FixedStructure, Reserved4, LightWithoutSectors, LightWithSectors, LeadingLightFront, LeadingLightRear, BeaconCardinalNorth, BeaconCardinalEast, BeaconCardinalSouth, BeaconCardinalWest, BeaconLateralPort, BeaconLateralStarboard, BeaconLateralPreferredChannelPort, BeaconLateralPreferredChannelStarboard, BeaconIsolatedDanger, BeaconSafeWater, BeaconSpecialMark, CardinalMarkNorth, CardinalMarkEast, CardinalMarkSouth, CardinalMarkWest, PortHandMark, StarboardHandMark, PreferredChannelPort, PreferredChannelStarboard, IsolatedDanger, SafeWater, SpecialMark, LightVessel, }
193
194impl NavAidType {
195 fn new(raw: u8) -> Result<NavAidType, ParseError> {
196 match raw {
197 0 => Ok(NavAidType::NotSpecified),
198 1 => Ok(NavAidType::ReferencePoint),
199 2 => Ok(NavAidType::Racon),
200 3 => Ok(NavAidType::FixedStructure),
201 4 => Ok(NavAidType::Reserved4),
202 5 => Ok(NavAidType::LightWithoutSectors),
203 6 => Ok(NavAidType::LightWithSectors),
204 7 => Ok(NavAidType::LeadingLightFront),
205 8 => Ok(NavAidType::LeadingLightRear),
206 9 => Ok(NavAidType::BeaconCardinalNorth),
207 10 => Ok(NavAidType::BeaconCardinalEast),
208 11 => Ok(NavAidType::BeaconCardinalSouth),
209 12 => Ok(NavAidType::BeaconCardinalWest),
210 13 => Ok(NavAidType::BeaconLateralPort),
211 14 => Ok(NavAidType::BeaconLateralStarboard),
212 15 => Ok(NavAidType::BeaconLateralPreferredChannelPort),
213 16 => Ok(NavAidType::BeaconLateralPreferredChannelStarboard),
214 17 => Ok(NavAidType::BeaconIsolatedDanger),
215 18 => Ok(NavAidType::BeaconSafeWater),
216 19 => Ok(NavAidType::BeaconSpecialMark),
217 20 => Ok(NavAidType::CardinalMarkNorth),
218 21 => Ok(NavAidType::CardinalMarkEast),
219 22 => Ok(NavAidType::CardinalMarkSouth),
220 23 => Ok(NavAidType::CardinalMarkWest),
221 24 => Ok(NavAidType::PortHandMark),
222 25 => Ok(NavAidType::StarboardHandMark),
223 26 => Ok(NavAidType::PreferredChannelPort),
224 27 => Ok(NavAidType::PreferredChannelStarboard),
225 28 => Ok(NavAidType::IsolatedDanger),
226 29 => Ok(NavAidType::SafeWater),
227 30 => Ok(NavAidType::SpecialMark),
228 31 => Ok(NavAidType::LightVessel),
229 _ => Err(format!("Unrecognized Nav aid type code: {}", raw).into()),
230 }
231 }
232}
233
234impl Default for NavAidType {
235 fn default() -> NavAidType {
236 NavAidType::NotSpecified
237 }
238}
239
240impl core::fmt::Display for NavAidType {
241 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
242 match self {
243 NavAidType::NotSpecified => write!(f, "not specified"),
244 NavAidType::ReferencePoint => write!(f, "reference point"),
245 NavAidType::Racon => write!(f, "RACON"),
246 NavAidType::FixedStructure => write!(f, "FixedStructure"),
247 NavAidType::Reserved4 => write!(f, "(reserved)"),
248 NavAidType::LightWithoutSectors => write!(f, "light without sectors"),
249 NavAidType::LightWithSectors => write!(f, "light with sectors"),
250 NavAidType::LeadingLightFront => write!(f, "leading light front"),
251 NavAidType::LeadingLightRear => write!(f, "leading light rear"),
252 NavAidType::BeaconCardinalNorth => write!(f, "cardinal beacon, north"),
253 NavAidType::BeaconCardinalEast => write!(f, "cardinal beacon, east"),
254 NavAidType::BeaconCardinalSouth => write!(f, "cardinal beacon, south"),
255 NavAidType::BeaconCardinalWest => write!(f, "cardinal beacon, west"),
256 NavAidType::BeaconLateralPort => write!(f, "lateral beacon, port side"),
257 NavAidType::BeaconLateralStarboard => write!(f, "lateral beacon, starboard side"),
258 NavAidType::BeaconLateralPreferredChannelPort => {
259 write!(f, "lateral beacon, preferred channel, port side")
260 }
261 NavAidType::BeaconLateralPreferredChannelStarboard => {
262 write!(f, "lateral beacon, preferred channel, starboard side")
263 }
264 NavAidType::BeaconIsolatedDanger => write!(f, "isolated danger beacon"),
265 NavAidType::BeaconSafeWater => write!(f, "safe water"),
266 NavAidType::BeaconSpecialMark => write!(f, "special mark"),
267 NavAidType::CardinalMarkNorth => write!(f, "cardinal mark, north"),
268 NavAidType::CardinalMarkEast => write!(f, "cardinal mark, east"),
269 NavAidType::CardinalMarkSouth => write!(f, "cardinal mark, south"),
270 NavAidType::CardinalMarkWest => write!(f, "cardinal mark, west"),
271 NavAidType::PortHandMark => write!(f, "port hand mark"),
272 NavAidType::StarboardHandMark => write!(f, "starboard hand mark"),
273 NavAidType::PreferredChannelPort => write!(f, "preferred channel, port side"),
274 NavAidType::PreferredChannelStarboard => write!(f, "preferred channel, starboard side"),
275 NavAidType::IsolatedDanger => write!(f, "isolated danger"),
276 NavAidType::SafeWater => write!(f, "safe water"),
277 NavAidType::SpecialMark => write!(f, "special mark"),
278 NavAidType::LightVessel => write!(f, "light vessel"),
279 }
280 }
281}
282
283pub(crate) fn handle(
287 bv: &BitVec,
288 station: Station,
289 own_vessel: bool,
290) -> Result<ParsedMessage, ParseError> {
291 Ok(ParsedMessage::AidToNavigationReport(
292 AidToNavigationReport {
293 own_vessel: { own_vessel },
294 station: { station },
295 mmsi: { pick_u64(bv, 8, 30) as u32 },
296 aid_type: {
297 NavAidType::new(pick_u64(bv, 38, 5) as u8)
298 .ok()
299 .unwrap_or(NavAidType::NotSpecified)
300 },
301 name: {
302 let mut s = pick_string(bv, 43, 20);
303 s.push_str(&pick_string(bv, 272, 14));
304 s
305 },
306 high_position_accuracy: { pick_u64(bv, 163, 1) != 0 },
307 latitude: {
308 let lat_raw = pick_i64(bv, 192, 27) as i32;
309 if lat_raw != 0x3412140 {
310 Some((lat_raw as f64) / 600000.0)
311 } else {
312 None
313 }
314 },
315 longitude: {
316 let lon_raw = pick_i64(bv, 164, 28) as i32;
317 if lon_raw != 0x6791AC0 {
318 Some((lon_raw as f64) / 600000.0)
319 } else {
320 None
321 }
322 },
323 dimension_to_bow: { Some(pick_u64(bv, 219, 9) as u16) },
324 dimension_to_stern: { Some(pick_u64(bv, 228, 9) as u16) },
325 dimension_to_port: { Some(pick_u64(bv, 237, 6) as u16) },
326 dimension_to_starboard: { Some(pick_u64(bv, 243, 6) as u16) },
327 position_fix_type: { Some(PositionFixType::new(pick_u64(bv, 249, 4) as u8)) },
328 timestamp_seconds: { pick_u64(bv, 253, 6) as u8 },
329 off_position_indicator: { pick_u64(bv, 243, 1) != 0 },
330 regional: { pick_u64(bv, 260, 8) as u8 },
331 raim_flag: { pick_u64(bv, 268, 1) != 0 },
332 virtual_aid_flag: { pick_u64(bv, 269, 1) != 0 },
333 assigned_mode_flag: { pick_u64(bv, 270, 1) != 0 },
334 },
335 ))
336}
337
338#[cfg(test)]
341mod test {
342 use super::*;
343
344 #[test]
345 fn test_parse_vdm_type21() {
346 let mut p = NmeaParser::new();
347 match p.parse_sentence("!AIVDM,2,1,5,B,E1mg=5J1T4W0h97aRh6ba84<h2d;W:Te=eLvH50```q,0*46") {
348 Ok(ps) => match ps {
349 ParsedMessage::Incomplete => {
350 assert!(true);
351 }
352 _ => {
353 assert!(false);
354 }
355 },
356 Err(e) => {
357 assert_eq!(e.to_string(), "OK");
358 }
359 }
360
361 match p.parse_sentence("!AIVDM,2,2,5,B,:D44QDlp0C1DU00,2*36") {
362 Ok(ps) => {
363 match ps {
364 ParsedMessage::AidToNavigationReport(atnr) => {
366 assert_eq!(atnr.mmsi, 123456789);
367 assert_eq!(atnr.aid_type, NavAidType::CardinalMarkNorth);
368 assert_eq!(atnr.name, "CHINA ROSE MURPHY EXPRESS ALERT");
369 assert!(!atnr.high_position_accuracy);
370 assert::close(atnr.latitude.unwrap_or(0.0), 47.9206183333, 0.00000001);
371 assert::close(atnr.longitude.unwrap_or(0.0), -122.698591667, 0.00000001);
372 assert_eq!(atnr.dimension_to_bow, Some(5));
373 assert_eq!(atnr.dimension_to_stern, Some(5));
374 assert_eq!(atnr.dimension_to_port, Some(5));
375 assert_eq!(atnr.dimension_to_starboard, Some(5));
376 assert_eq!(atnr.position_fix_type, Some(PositionFixType::GPS));
377 assert_eq!(atnr.timestamp_seconds, 50);
378 assert!(!atnr.off_position_indicator);
379 assert_eq!(atnr.regional, 165);
380 assert!(!atnr.raim_flag);
381 assert!(!atnr.virtual_aid_flag);
382 assert!(!atnr.assigned_mode_flag);
383 }
384 ParsedMessage::Incomplete => {
385 assert!(false);
386 }
387 _ => {
388 assert!(false);
389 }
390 }
391 }
392 Err(e) => {
393 assert_eq!(e.to_string(), "OK");
394 }
395 }
396 }
397}