#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub mod bits;
pub mod error;
pub mod message;
pub mod nmea;
pub mod string;
pub use error::AisError;
pub use message::AisMessage;
pub use nmea::{Channel, Sentence};
pub fn decode_line(line: &str) -> Result<AisMessage, AisError> {
let sentence = Sentence::parse(line)?;
if sentence.fragment_count != 1 {
return Err(AisError::IncompleteFragment);
}
decode_payload(sentence.payload, sentence.fill_bits)
}
pub fn decode_payload(armored: &[u8], fill_bits: u8) -> Result<AisMessage, AisError> {
let mut reader = bits::BitReader::new(armored, fill_bits);
AisMessage::decode(&mut reader).map_err(AisError::from)
}
pub fn encode_payload<'a>(
message: &AisMessage,
buf: &'a mut [u8],
) -> Result<(&'a [u8], u8), AisError> {
let mut writer = bits::BitWriter::new(buf);
message.encode(&mut writer)?;
Ok(writer.finish()?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decodes_a_real_position_report() {
let msg = decode_line("!AIVDM,1,1,,B,15M67FC000G?ufbE`FepT@3n00Sa,0*5C").unwrap();
assert_eq!(msg.message_type(), 1);
let AisMessage::PositionReportClassA(p) = msg else {
panic!("expected PositionReportClassA");
};
assert_eq!(p.mmsi.raw(), 366_053_209);
assert_eq!(
p.navigation_status,
crate::message::common::NavigationStatus::RestrictedManoeuvrability
);
assert!((p.longitude.as_degrees().unwrap() - (-122.341_618_333)).abs() < 1e-6);
assert!((p.latitude.as_degrees().unwrap() - 37.802_118_333).abs() < 1e-6);
}
#[test]
fn multi_fragment_sentence_reports_incomplete() {
let err = decode_line("!AIVDM,2,1,7,B,15M67FC000,0*6C").unwrap_err();
assert_eq!(err, AisError::IncompleteFragment);
}
#[test]
fn encode_round_trips_a_real_position_report() {
let original_payload = b"15M67FC000G?ufbE`FepT@3n00Sa";
let msg = decode_payload(original_payload, 0).unwrap();
let mut buf = [0u8; 32];
let (armored, fill_bits) = encode_payload(&msg, &mut buf).unwrap();
assert_eq!(fill_bits, 0);
assert_eq!(armored, original_payload);
let re_decoded = decode_payload(armored, fill_bits).unwrap();
assert_eq!(re_decoded, msg);
}
}