use super::*;
#[derive(Default, Clone, Debug, PartialEq)]
pub struct GroupAssignmentCommand {
pub own_vessel: bool,
pub station: Station,
pub mmsi: u32,
pub ne_lat: Option<f64>,
pub ne_lon: Option<f64>,
pub sw_lat: Option<f64>,
pub sw_lon: Option<f64>,
pub station_type: StationType,
pub ship_type: ShipType,
pub cargo_type: CargoType,
pub txrx: u8,
pub interval: StationInterval,
pub quiet: Option<u8>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StationType {
AllTypes,
Reserved1,
AllTypesOfClassBMobile,
SarAirborneMobile,
AidToNavigation,
ClassBShipBorneMobile,
Regional6,
Regional7,
Regional8,
Regional9,
Reserved10,
Reserved11,
Reserved12,
Reserved13,
Reserved14,
Reserved15,
}
impl Default for StationType {
fn default() -> Self {
StationType::AllTypes
}
}
impl StationType {
fn new(val: u8) -> Result<StationType, String> {
match val {
0 => Ok(StationType::AllTypes),
1 => Ok(StationType::Reserved1),
2 => Ok(StationType::AllTypesOfClassBMobile),
3 => Ok(StationType::SarAirborneMobile),
4 => Ok(StationType::AidToNavigation),
5 => Ok(StationType::ClassBShipBorneMobile),
6 => Ok(StationType::Regional6),
7 => Ok(StationType::Regional7),
8 => Ok(StationType::Regional8),
9 => Ok(StationType::Regional9),
10 => Ok(StationType::Reserved10),
11 => Ok(StationType::Reserved11),
12 => Ok(StationType::Reserved12),
13 => Ok(StationType::Reserved13),
14 => Ok(StationType::Reserved14),
15 => Ok(StationType::Reserved15),
_ => Err(format!("Station type value out of range: {}", val)),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StationInterval {
Autonomous,
Time10min,
Time6min,
Time3min,
Time1min,
Time30sec,
Time15sec,
Time10sec,
Time5sec,
NextShorterReportingInverval,
NextLongerReportingInverval,
Reserved11,
Reserved12,
Reserved13,
Reserved14,
Reserved15,
}
impl StationInterval {
fn new(val: u8) -> Result<StationInterval, String> {
match val {
0 => Ok(StationInterval::Autonomous),
1 => Ok(StationInterval::Time10min),
2 => Ok(StationInterval::Time6min),
3 => Ok(StationInterval::Time3min),
4 => Ok(StationInterval::Time1min),
5 => Ok(StationInterval::Time30sec),
6 => Ok(StationInterval::Time15sec),
7 => Ok(StationInterval::Time10sec),
8 => Ok(StationInterval::Time5sec),
9 => Ok(StationInterval::NextShorterReportingInverval),
10 => Ok(StationInterval::NextLongerReportingInverval),
11 => Ok(StationInterval::Reserved11),
12 => Ok(StationInterval::Reserved12),
13 => Ok(StationInterval::Reserved13),
14 => Ok(StationInterval::Reserved14),
15 => Ok(StationInterval::Reserved15),
_ => Err(format!("Station interval value out of range: {}", val)),
}
}
}
impl Default for StationInterval {
fn default() -> Self {
StationInterval::Autonomous
}
}
pub(crate) fn handle(
bv: &BitVec,
station: Station,
own_vessel: bool,
) -> Result<ParsedMessage, ParseError> {
Ok(ParsedMessage::GroupAssignmentCommand(
GroupAssignmentCommand {
own_vessel: { own_vessel },
station: { station },
mmsi: { pick_u64(&bv, 8, 30) as u32 },
ne_lat: { Some(pick_i64(&bv, 58, 17) as f64 / 600.0) },
ne_lon: { Some(pick_i64(&bv, 40, 18) as f64 / 600.0) },
sw_lat: { Some(pick_i64(&bv, 93, 17) as f64 / 600.0) },
sw_lon: { Some(pick_i64(&bv, 75, 18) as f64 / 600.0) },
station_type: StationType::new(pick_u64(&bv, 110, 4) as u8)?,
ship_type: ShipType::new(pick_u64(&bv, 114, 8) as u8),
cargo_type: CargoType::new(pick_u64(&bv, 114, 8) as u8),
txrx: {
let val = pick_u64(&bv, 144, 2) as u8;
if val < 4 {
val
} else {
return Err(format!("Tx/Tr mode field out of range: {}", val).into());
}
},
interval: StationInterval::new(pick_u64(&bv, 146, 4) as u8)?,
quiet: {
let val = pick_u64(&bv, 150, 4) as u8;
match val {
0 => None,
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 => Some(val),
_ => {
unreachable!("This should never be reached as all four bit cases are covered (value: {})", val);
}
}
},
},
))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_vdm_type23() {
let mut p = NmeaParser::new();
match p.parse_sentence("!AIVDM,1,1,,B,G02:Kn01R`sn@291nj600000900,2*12") {
Ok(ps) => {
match ps {
ParsedMessage::GroupAssignmentCommand(gac) => {
assert_eq!(gac.mmsi, 2268120);
assert::close(gac.ne_lat.unwrap_or(0.0), 30642.0 / 600.0, 0.1);
assert::close(gac.ne_lon.unwrap_or(0.0), 1578.0 / 600.0, 0.1);
assert::close(gac.sw_lat.unwrap_or(0.0), 30408.0 / 600.0, 0.1);
assert::close(gac.sw_lon.unwrap_or(0.0), 1096.0 / 600.0, 0.1);
assert_eq!(gac.station_type, StationType::Regional6);
assert_eq!(gac.ship_type, ShipType::NotAvailable);
assert_eq!(gac.cargo_type, CargoType::Undefined);
assert_eq!(gac.txrx, 0);
assert_eq!(gac.interval, StationInterval::NextShorterReportingInverval);
assert_eq!(gac.quiet, None);
}
ParsedMessage::Incomplete => {
assert!(false);
}
_ => {
assert!(false);
}
}
}
Err(e) => {
assert_eq!(e.to_string(), "OK");
}
}
}
}