use crate::ais::armor::{extract_i32, extract_string, extract_u32};
use super::utils::{decode_latitude, decode_longitude};
#[derive(Debug, Clone, PartialEq)]
pub struct AidToNavigation {
pub repeat_indicator: u8,
pub mmsi: u32,
pub aid_type: u8,
pub name: String,
pub lat: Option<f64>,
pub lon: Option<f64>,
pub position_accuracy: bool,
pub dimension_to_bow: u16,
pub dimension_to_stern: u16,
pub dimension_to_port: u8,
pub dimension_to_starboard: u8,
pub position_fixing_device: u8,
pub timestamp: Option<u8>,
pub off_position: bool,
pub regional_application: u8,
pub raim: bool,
pub virtual_aid: bool,
pub assigned_mode: bool,
pub name_extension: Option<String>,
}
impl AidToNavigation {
pub(crate) fn decode(bits: &[u8]) -> Option<Self> {
if bits.len() < 272 {
return None;
}
let repeat_indicator = extract_u32(bits, 6, 2)? as u8;
let mmsi = extract_u32(bits, 8, 30)?;
let aid_type = extract_u32(bits, 38, 5)? as u8;
let name = extract_string(bits, 43, 20)?;
let lon_raw = extract_i32(bits, 164, 28)?;
let lat_raw = extract_i32(bits, 192, 27)?;
let timestamp_raw = extract_u32(bits, 253, 6)? as u8;
let name_extension = if bits.len() > 272 {
let char_count = (bits.len() - 272) / 6;
if char_count == 0 {
None
} else {
Some(extract_string(bits, 272, char_count)?)
}
} else {
None
};
Some(Self {
repeat_indicator,
mmsi,
aid_type,
name,
lat: decode_latitude(lat_raw),
lon: decode_longitude(lon_raw),
position_accuracy: extract_u32(bits, 163, 1)? == 1,
dimension_to_bow: extract_u32(bits, 219, 9)? as u16,
dimension_to_stern: extract_u32(bits, 228, 9)? as u16,
dimension_to_port: extract_u32(bits, 237, 6)? as u8,
dimension_to_starboard: extract_u32(bits, 243, 6)? as u8,
position_fixing_device: extract_u32(bits, 249, 4)? as u8,
timestamp: if timestamp_raw < 60 {
Some(timestamp_raw)
} else {
None
},
off_position: extract_u32(bits, 259, 1)? == 1,
regional_application: extract_u32(bits, 260, 8)? as u8,
raim: extract_u32(bits, 268, 1)? == 1,
virtual_aid: extract_u32(bits, 269, 1)? == 1,
assigned_mode: extract_u32(bits, 270, 1)? == 1,
name_extension,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ais::messages::test_helpers::set_bits;
#[test]
fn aton_name_preserves_leading_space() {
let mut bits = vec![0u8; 272];
set_bits(&mut bits, 0, 6, 21); set_bits(&mut bits, 43, 6, 32); set_bits(&mut bits, 49, 6, 1); let aton = AidToNavigation::decode(&bits).expect("decode");
assert_eq!(
aton.name, " A",
"leading space must be preserved (trailing-only trim)"
);
}
}