use crate::error::{Error, Result};
use super::crc::crc24q;
pub const PREAMBLE: u8 = 0xD3;
pub const MAX_BODY_LEN: usize = 0x3FF;
pub const FRAME_OVERHEAD: usize = 6;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DecodedFrame<'a> {
pub body: &'a [u8],
pub frame_len: usize,
}
pub fn encode_frame(body: &[u8]) -> Result<Vec<u8>> {
if body.len() > MAX_BODY_LEN {
return Err(Error::InvalidInput(format!(
"RTCM body of {} bytes exceeds the 1023-byte frame limit",
body.len()
)));
}
let len = body.len() as u16;
let mut out = Vec::with_capacity(body.len() + FRAME_OVERHEAD);
out.push(PREAMBLE);
out.push((len >> 8) as u8 & 0x03);
out.push((len & 0xFF) as u8);
out.extend_from_slice(body);
let crc = crc24q(&out);
out.push((crc >> 16) as u8);
out.push((crc >> 8) as u8);
out.push(crc as u8);
Ok(out)
}
pub fn decode_frame(bytes: &[u8]) -> Result<DecodedFrame<'_>> {
if bytes.len() < FRAME_OVERHEAD {
return Err(Error::Parse(format!(
"RTCM frame too short: {} bytes, need at least {FRAME_OVERHEAD}",
bytes.len()
)));
}
if bytes[0] != PREAMBLE {
return Err(Error::Parse(format!(
"RTCM frame missing 0xD3 preamble (found {:#04x})",
bytes[0]
)));
}
let len = ((usize::from(bytes[1] & 0x03)) << 8) | usize::from(bytes[2]);
let total = 3 + len + 3;
if bytes.len() < total {
return Err(Error::Parse(format!(
"RTCM frame truncated: declares {len}-byte body needing {total} bytes, have {}",
bytes.len()
)));
}
let crc_computed = crc24q(&bytes[..3 + len]);
let crc_framed = (u32::from(bytes[3 + len]) << 16)
| (u32::from(bytes[3 + len + 1]) << 8)
| u32::from(bytes[3 + len + 2]);
if crc_computed != crc_framed {
return Err(Error::Parse(format!(
"RTCM CRC-24Q mismatch: computed {crc_computed:#08x}, frame carries {crc_framed:#08x}"
)));
}
Ok(DecodedFrame {
body: &bytes[3..3 + len],
frame_len: total,
})
}
pub struct FrameScanner<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> FrameScanner<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self { bytes, pos: 0 }
}
}
impl<'a> Iterator for FrameScanner<'a> {
type Item = DecodedFrame<'a>;
fn next(&mut self) -> Option<Self::Item> {
while self.pos < self.bytes.len() {
if self.bytes[self.pos] != PREAMBLE {
self.pos += 1;
continue;
}
match decode_frame(&self.bytes[self.pos..]) {
Ok(frame) => {
self.pos += frame.frame_len;
return Some(frame);
}
Err(_) => {
self.pos += 1;
}
}
}
None
}
}