use super::*;
#[derive(Default, Clone, Debug, PartialEq)]
pub struct AidToNavigationReport {
pub own_vessel: bool,
pub station: Station,
pub mmsi: u32,
pub aid_type: NavAidType,
pub name: String,
high_position_accuracy: bool,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub dimension_to_bow: Option<u16>,
pub dimension_to_stern: Option<u16>,
pub dimension_to_port: Option<u16>,
pub dimension_to_starboard: Option<u16>,
pub position_fix_type: Option<PositionFixType>,
pub timestamp_seconds: u8,
pub off_position_indicator: bool,
pub regional: u8,
pub raim_flag: bool,
pub virtual_aid_flag: bool,
pub assigned_mode_flag: bool,
}
impl LatLon for AidToNavigationReport {
fn latitude(&self) -> Option<f64> {
self.latitude
}
fn longitude(&self) -> Option<f64> {
self.longitude
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NavAidType {
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, }
impl NavAidType {
fn new(raw: u8) -> Result<NavAidType, ParseError> {
match raw {
0 => Ok(NavAidType::NotSpecified),
1 => Ok(NavAidType::ReferencePoint),
2 => Ok(NavAidType::Racon),
3 => Ok(NavAidType::FixedStructure),
4 => Ok(NavAidType::Reserved4),
5 => Ok(NavAidType::LightWithoutSectors),
6 => Ok(NavAidType::LightWithSectors),
7 => Ok(NavAidType::LeadingLightFront),
8 => Ok(NavAidType::LeadingLightRear),
9 => Ok(NavAidType::BeaconCardinalNorth),
10 => Ok(NavAidType::BeaconCardinalEast),
11 => Ok(NavAidType::BeaconCardinalSouth),
12 => Ok(NavAidType::BeaconCardinalWest),
13 => Ok(NavAidType::BeaconLateralPort),
14 => Ok(NavAidType::BeaconLateralStarboard),
15 => Ok(NavAidType::BeaconLateralPreferredChannelPort),
16 => Ok(NavAidType::BeaconLateralPreferredChannelStarboard),
17 => Ok(NavAidType::BeaconIsolatedDanger),
18 => Ok(NavAidType::BeaconSafeWater),
19 => Ok(NavAidType::BeaconSpecialMark),
20 => Ok(NavAidType::CardinalMarkNorth),
21 => Ok(NavAidType::CardinalMarkEast),
22 => Ok(NavAidType::CardinalMarkSouth),
23 => Ok(NavAidType::CardinalMarkWest),
24 => Ok(NavAidType::PortHandMark),
25 => Ok(NavAidType::StarboardHandMark),
26 => Ok(NavAidType::PreferredChannelPort),
27 => Ok(NavAidType::PreferredChannelStarboard),
28 => Ok(NavAidType::IsolatedDanger),
29 => Ok(NavAidType::SafeWater),
30 => Ok(NavAidType::SpecialMark),
31 => Ok(NavAidType::LightVessel),
_ => Err(format!("Unrecognized Nav aid type code: {}", raw).into()),
}
}
}
impl Default for NavAidType {
fn default() -> NavAidType {
NavAidType::NotSpecified
}
}
impl core::fmt::Display for NavAidType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
NavAidType::NotSpecified => write!(f, "not specified"),
NavAidType::ReferencePoint => write!(f, "reference point"),
NavAidType::Racon => write!(f, "RACON"),
NavAidType::FixedStructure => write!(f, "FixedStructure"),
NavAidType::Reserved4 => write!(f, "(reserved)"),
NavAidType::LightWithoutSectors => write!(f, "light without sectors"),
NavAidType::LightWithSectors => write!(f, "light with sectors"),
NavAidType::LeadingLightFront => write!(f, "leading light front"),
NavAidType::LeadingLightRear => write!(f, "leading light rear"),
NavAidType::BeaconCardinalNorth => write!(f, "cardinal beacon, north"),
NavAidType::BeaconCardinalEast => write!(f, "cardinal beacon, east"),
NavAidType::BeaconCardinalSouth => write!(f, "cardinal beacon, south"),
NavAidType::BeaconCardinalWest => write!(f, "cardinal beacon, west"),
NavAidType::BeaconLateralPort => write!(f, "lateral beacon, port side"),
NavAidType::BeaconLateralStarboard => write!(f, "lateral beacon, starboard side"),
NavAidType::BeaconLateralPreferredChannelPort => {
write!(f, "lateral beacon, preferred channel, port side")
}
NavAidType::BeaconLateralPreferredChannelStarboard => {
write!(f, "lateral beacon, preferred channel, starboard side")
}
NavAidType::BeaconIsolatedDanger => write!(f, "isolated danger beacon"),
NavAidType::BeaconSafeWater => write!(f, "safe water"),
NavAidType::BeaconSpecialMark => write!(f, "special mark"),
NavAidType::CardinalMarkNorth => write!(f, "cardinal mark, north"),
NavAidType::CardinalMarkEast => write!(f, "cardinal mark, east"),
NavAidType::CardinalMarkSouth => write!(f, "cardinal mark, south"),
NavAidType::CardinalMarkWest => write!(f, "cardinal mark, west"),
NavAidType::PortHandMark => write!(f, "port hand mark"),
NavAidType::StarboardHandMark => write!(f, "starboard hand mark"),
NavAidType::PreferredChannelPort => write!(f, "preferred channel, port side"),
NavAidType::PreferredChannelStarboard => write!(f, "preferred channel, starboard side"),
NavAidType::IsolatedDanger => write!(f, "isolated danger"),
NavAidType::SafeWater => write!(f, "safe water"),
NavAidType::SpecialMark => write!(f, "special mark"),
NavAidType::LightVessel => write!(f, "light vessel"),
}
}
}
pub(crate) fn handle(
bv: &BitVec,
station: Station,
own_vessel: bool,
) -> Result<ParsedMessage, ParseError> {
Ok(ParsedMessage::AidToNavigationReport(
AidToNavigationReport {
own_vessel: { own_vessel },
station: { station },
mmsi: { pick_u64(bv, 8, 30) as u32 },
aid_type: {
NavAidType::new(pick_u64(bv, 38, 5) as u8)
.ok()
.unwrap_or(NavAidType::NotSpecified)
},
name: {
let mut s = pick_string(bv, 43, 20);
s.push_str(&pick_string(bv, 272, 14));
s
},
high_position_accuracy: { pick_u64(bv, 163, 1) != 0 },
latitude: {
let lat_raw = pick_i64(bv, 192, 27) as i32;
if lat_raw != 0x3412140 {
Some((lat_raw as f64) / 600000.0)
} else {
None
}
},
longitude: {
let lon_raw = pick_i64(bv, 164, 28) as i32;
if lon_raw != 0x6791AC0 {
Some((lon_raw as f64) / 600000.0)
} else {
None
}
},
dimension_to_bow: { Some(pick_u64(bv, 219, 9) as u16) },
dimension_to_stern: { Some(pick_u64(bv, 228, 9) as u16) },
dimension_to_port: { Some(pick_u64(bv, 237, 6) as u16) },
dimension_to_starboard: { Some(pick_u64(bv, 243, 6) as u16) },
position_fix_type: { Some(PositionFixType::new(pick_u64(bv, 249, 4) as u8)) },
timestamp_seconds: { pick_u64(bv, 253, 6) as u8 },
off_position_indicator: { pick_u64(bv, 243, 1) != 0 },
regional: { pick_u64(bv, 260, 8) as u8 },
raim_flag: { pick_u64(bv, 268, 1) != 0 },
virtual_aid_flag: { pick_u64(bv, 269, 1) != 0 },
assigned_mode_flag: { pick_u64(bv, 270, 1) != 0 },
},
))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_vdm_type21() {
let mut p = NmeaParser::new();
match p.parse_sentence("!AIVDM,2,1,5,B,E1mg=5J1T4W0h97aRh6ba84<h2d;W:Te=eLvH50```q,0*46") {
Ok(ps) => match ps {
ParsedMessage::Incomplete => {
assert!(true);
}
_ => {
assert!(false);
}
},
Err(e) => {
assert_eq!(e.to_string(), "OK");
}
}
match p.parse_sentence("!AIVDM,2,2,5,B,:D44QDlp0C1DU00,2*36") {
Ok(ps) => {
match ps {
ParsedMessage::AidToNavigationReport(atnr) => {
assert_eq!(atnr.mmsi, 123456789);
assert_eq!(atnr.aid_type, NavAidType::CardinalMarkNorth);
assert_eq!(atnr.name, "CHINA ROSE MURPHY EXPRESS ALERT");
assert!(!atnr.high_position_accuracy);
assert::close(atnr.latitude.unwrap_or(0.0), 47.9206183333, 0.00000001);
assert::close(atnr.longitude.unwrap_or(0.0), -122.698591667, 0.00000001);
assert_eq!(atnr.dimension_to_bow, Some(5));
assert_eq!(atnr.dimension_to_stern, Some(5));
assert_eq!(atnr.dimension_to_port, Some(5));
assert_eq!(atnr.dimension_to_starboard, Some(5));
assert_eq!(atnr.position_fix_type, Some(PositionFixType::GPS));
assert_eq!(atnr.timestamp_seconds, 50);
assert!(!atnr.off_position_indicator);
assert_eq!(atnr.regional, 165);
assert!(!atnr.raim_flag);
assert!(!atnr.virtual_aid_flag);
assert!(!atnr.assigned_mode_flag);
}
ParsedMessage::Incomplete => {
assert!(false);
}
_ => {
assert!(false);
}
}
}
Err(e) => {
assert_eq!(e.to_string(), "OK");
}
}
}
}