use deku::prelude::*;
use serde::{Deserialize, Serialize};
use super::common::Timestamp;
use super::converters::*;
#[derive(Debug, Clone, PartialEq, DekuRead, Serialize, Deserialize)]
#[deku(endian = "big")]
pub struct SarAircraftPositionReport {
#[deku(bits = "6")]
pub msg_type: u8,
#[deku(bits = "2")]
pub repeat: u8,
#[deku(
bits = "30",
map = "|x: u32| -> Result<_, DekuError> { Ok(from_mmsi(x)) }"
)]
pub mmsi: u32,
#[deku(
bits = "12",
map = "|x: u16| -> Result<_, DekuError> { Ok(from_altitude(x)) }"
)]
pub alt: Option<u16>,
#[deku(
bits = "10",
map = "|x: u16| -> Result<_, DekuError> { Ok(from_speed_sar(x)) }"
)]
pub speed: Option<u16>,
#[deku(bits = "1", map = "|x: u8| -> Result<_, DekuError> { Ok(x != 0) }")]
pub accuracy: bool,
#[deku(
bits = "28",
map = "|x: u32| -> Result<_, DekuError> { Ok(from_longitude(x)) }"
)]
pub longitude: Option<f64>,
#[deku(
bits = "27",
map = "|x: u32| -> Result<_, DekuError> { Ok(from_latitude(x)) }"
)]
pub latitude: Option<f64>,
#[deku(
bits = "12",
map = "|x: u16| -> Result<_, DekuError> { Ok(from_course(x)) }"
)]
pub course: Option<f32>,
#[deku(
bits = "6",
map = "|x: u8| -> Result<_, DekuError> { Ok(Timestamp::from_bits(x)) }"
)]
pub second: Timestamp,
#[deku(bits = "8")]
pub reserved_1: u8,
#[deku(bits = "1", map = "|x: u8| -> Result<_, DekuError> { Ok(x != 0) }")]
pub dte: bool,
#[deku(bits = "3")]
#[serde(skip)]
pub spare_1: u8,
#[deku(bits = "1", map = "|x: u8| -> Result<_, DekuError> { Ok(x != 0) }")]
pub assigned: bool,
#[deku(bits = "1", map = "|x: u8| -> Result<_, DekuError> { Ok(x != 0) }")]
pub raim: bool,
#[deku(bits = "20")]
pub radio: u32,
}
impl SarAircraftPositionReport {
pub fn asdict(&self) -> serde_json::Value {
serde_json::json!({
"msg_type": self.msg_type,
"repeat": self.repeat,
"mmsi": self.mmsi,
"alt": self.alt,
"speed": self.speed,
"accuracy": self.accuracy,
"longitude": self.longitude,
"latitude": self.latitude,
"course": self.course,
"second": self.second,
"reserved_1": self.reserved_1,
"dte": self.dte,
"assigned": self.assigned,
"raim": self.raim,
"radio": self.radio,
})
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::decode::nmea::NmeaAisMessage;
fn decode(sentence: &str) -> SarAircraftPositionReport {
let nmea_msg = NmeaAisMessage::parse(sentence).unwrap();
let binary_data = nmea_msg.payload_to_binary().unwrap();
let (_, msg) = SarAircraftPositionReport::from_bytes((&binary_data, 0)).unwrap();
msg
}
#[test]
fn test_msg_type_9() {
let msg = decode("!AIVDM,1,1,,B,91b55wi;hbOS@OdQAC062Ch2089h,0*30");
assert_eq!(msg.msg_type, 9);
assert_eq!(msg.repeat, 0);
assert_eq!(msg.mmsi, 111232511);
assert_eq!(msg.alt, Some(303));
assert_eq!(msg.speed, Some(42));
assert!(!msg.accuracy);
assert!((msg.longitude.unwrap() - (-6.27884)).abs() < 0.00001);
assert!((msg.latitude.unwrap() - 58.144).abs() < 0.001);
assert!((msg.course.unwrap() - 154.5).abs() < 0.1);
assert_eq!(msg.second, Timestamp::Second(15));
assert!(msg.dte);
assert_eq!(msg.radio, 33392);
assert!(!msg.raim);
}
#[test]
fn test_message_serialization() {
let msg = decode("!AIVDM,1,1,,B,91b55wi;hbOS@OdQAC062Ch2089h,0*30");
let json = msg.to_json().unwrap();
let deserialized: SarAircraftPositionReport = serde_json::from_str(&json).unwrap();
assert_eq!(msg, deserialized);
}
#[test]
fn test_msg_type_9_fields() {
let msg = decode("!AIVDM,1,1,,B,91b55wi;hbOS@OdQAC062Ch2089h,0*30");
assert_eq!(msg.msg_type, 9);
assert_eq!(msg.repeat, 0);
assert!(msg.mmsi > 0);
assert!(msg.to_json().is_ok());
}
#[test]
fn test_asdict_compatibility() {
let msg = decode("!AIVDM,1,1,,B,91b55wi;hbOS@OdQAC062Ch2089h,0*30");
let dict = msg.asdict();
assert!(dict.get("longitude").is_some());
assert!(dict.get("latitude").is_some());
assert_eq!(dict["msg_type"], 9);
assert_eq!(dict["mmsi"], 111232511);
assert!((dict["longitude"].as_f64().unwrap() - (-6.27884)).abs() < 0.00001);
assert!((dict["latitude"].as_f64().unwrap() - 58.144).abs() < 0.001);
}
}