#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
use binrw::{binrw, BinRead, BinWrite};
extern crate alloc;
pub mod messages;
pub mod parser;
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub mod reader;
mod binrw_util;
#[cfg(all(test, feature = "std"))]
mod mega_test;
pub use messages::{
AGCState, AttCovEuler, AttEuler, AttitudeMode, AuxAntPositionSub, AuxAntPositions,
BaseVectorCart, BaseVectorGeod, BaselineError, BDSIon, ChannelSatInfo, ChannelStateInfo,
ChannelStatus, Commands, Comment, DOP, Datum, DiffCorrIn, DiffCorrType, DiskData, DiskStatus,
EndOfAtt, EndOfMeas, EndOfPVT,
EventPolarity, EventSource, ExtError, ExtEvent, ExtEventINSNavCart, ExtEventINSNavCartAtt,
ExtEventINSNavCartAttStdDev, ExtEventINSNavCartPosStdDev, ExtEventINSNavCartVel,
ExtEventINSNavCartVelStdDev, ExtEventINSNavGeod, ExtEventINSNavGeodAtt,
ExtEventINSNavGeodAttStdDev, ExtEventINSNavGeodPosStdDev, ExtEventINSNavGeodVel,
ExtEventINSNavGeodVelStdDev, ExtSensorInfo, ExtSensorMeas, ExtSensorMeasAcceleration,
ExtSensorMeasAngularRate, ExtSensorMeasInfo, ExtSensorMeasSet, ExtSensorMeasSetType,
ExtSensorMeasVelocity, ExtSensorMeasZeroVelocityFlag, ExtSensorStatus, GALGstGps, GALIon,
GALNav, GALUtc, GEONav, GEORawL1, GPSCNav, GPSIon, GPSNav, GPSUtc, GnssMode, INSCouplingMode,
INSError, INSNavCart, INSNavCartAtt, INSNavCartAttCov, INSNavCartAttStdDev, INSNavCartPosCov,
INSNavCartPosStdDev, INSNavCartVel, INSNavCartVelCov, INSNavCartVelStdDev, INSNavGeod,
INSNavGeodAtt, INSNavGeodAttCov, INSNavGeodAttStdDev, INSNavGeodPosCov, INSNavGeodPosStdDev,
INSNavGeodVel, INSNavGeodVelCov, INSNavGeodVelStdDev, INSSolutionLocation, INSSupport,
ImuSetup, Meas3Doppler, Meas3Ranges, MeasEpoch, MeasEpochChannelType1, MeasEpochChannelType2,
MeasExtra, MeasExtraChannelSub, NavCart, PVTCartesian, PVTGeodetic, PosCart, PosCovCartesian,
PosCovGeodetic, PvtError, PvtMode, PvtModeFlags, QualityInd, QualityIndicator, RFBand,
RFStatus, RaimIntegrity, ReceiverSetup, ReceiverStatus, ReceiverTime, RiseSet, RxError, RxMessage,
RxState, SatInfo, SatVisibility, VectorInfoCart, VectorInfoGeod, VelCovCartesian,
VelCovGeodetic, VelSensorSetup, WACorrFlags, XPPSOffset,
};
pub use parser::{parse_datagram, DatagramError, MAX_UDP_PAYLOAD};
#[binrw]
#[derive(Debug)]
struct Id {
pub bytes: u16,
}
impl Id {
fn message_type(&self) -> MessageKind {
MessageKind::from(self.block_number())
}
fn block_number(&self) -> u16 {
self.bytes & 0x1FFF
}
fn _block_rev_number(&self) -> u16 {
self.bytes & 0xE000
}
}
#[binrw]
#[derive(Debug)]
struct Header {
pub crc: u16,
pub block_id: Id,
pub length: u16,
}
#[binrw]
#[derive(Clone, Debug)]
#[brw(import(sb_len: usize))]
pub(crate) struct SubBlock<T>
where
T: 'static,
for<'a> T: BinRead<Args<'a> = ()> + BinWrite<Args<'a> = ()>,
{
#[brw(pad_size_to = sb_len)]
data: T,
}
impl<T> From<T> for SubBlock<T>
where
T: 'static,
for<'a> T: BinRead<Args<'a> = ()> + BinWrite<Args<'a> = ()>,
{
fn from(data: T) -> Self {
Self { data }
}
}
impl<T> SubBlock<T>
where
T: 'static,
for<'a> T: BinRead<Args<'a> = ()> + BinWrite<Args<'a> = ()>,
{
pub(crate) fn into_inner(self) -> T {
self.data
}
}
pub(crate) trait NestedHeader {
fn nested_count(&self) -> usize;
}
#[binrw]
#[derive(Clone, Debug)]
#[brw(import(sb1_len: usize, sb2_len: usize))]
pub(crate) struct NestedBlock<H, T>
where
H: 'static,
T: 'static,
for<'a> H: BinRead<Args<'a> = ()> + BinWrite<Args<'a> = ()> + NestedHeader + Clone,
for<'a> T: BinRead<Args<'a> = ()> + BinWrite<Args<'a> = ()> + Clone,
{
#[brw(pad_size_to = sb1_len)]
pub header: H,
#[br(args { count: header.nested_count(), inner: (sb2_len,) })]
#[bw(args_raw = (sb2_len,))]
pub items: alloc::vec::Vec<SubBlock<T>>,
}
macro_rules! define_messages {
($($variant:ident => $code:literal,)+) => {
#[derive(Debug)]
enum MessageKind {
$( $variant, )+
Unsupported,
}
impl From<u16> for MessageKind {
fn from(block_number: u16) -> Self {
match block_number {
$( $code => MessageKind::$variant, )+
_ => MessageKind::Unsupported,
}
}
}
#[derive(Clone, Debug)]
pub enum Messages {
$( $variant($variant), )+
Unsupported(u16),
}
impl Messages {
pub fn type_name(&self) -> &'static str {
match self {
$( Messages::$variant(_) => stringify!($variant), )+
Messages::Unsupported(_) => "Unsupported",
}
}
pub fn tow(&self) -> Option<u32> {
match self {
$( Messages::$variant(m) => m.tow, )+
Messages::Unsupported(_) => None,
}
}
pub fn wnc(&self) -> Option<u16> {
match self {
$( Messages::$variant(m) => m.wnc, )+
Messages::Unsupported(_) => None,
}
}
pub(crate) fn parse_body(kind: MessageKind, payload: &[u8]) -> binrw::BinResult<Self> {
let mut cursor = binrw::io::Cursor::new(payload);
Ok(match kind {
$( MessageKind::$variant => Self::$variant($variant::read_le(&mut cursor)?), )+
MessageKind::Unsupported => unreachable!("Unsupported is surfaced before parse_body"),
})
}
}
};
}
define_messages!(
MeasExtra => 4000,
DOP => 4001,
GALNav => 4002,
PVTCartesian => 4006,
PVTGeodetic => 4007,
SatVisibility => 4012,
ChannelStatus => 4013,
ReceiverStatus => 4014,
Commands => 4015,
GEORawL1 => 4020,
MeasEpoch => 4027,
BaseVectorGeod => 4028,
GALIon => 4030,
GALUtc => 4031,
GALGstGps => 4032,
GPSCNav => 4042,
BaseVectorCart => 4043,
PosCart => 4044,
ExtSensorMeas => 4050,
DiskStatus => 4059,
INSSupport => 4077,
QualityInd => 4082,
RFStatus => 4092,
RxMessage => 4103,
Meas3Ranges => 4109,
Meas3Doppler => 4111,
BDSIon => 4120,
ExtSensorInfo => 4222,
ExtSensorStatus => 4223,
ImuSetup => 4224,
INSNavCart => 4225,
INSNavGeod => 4226,
ExtEventINSNavCart => 4229,
ExtEventINSNavGeod => 4230,
VelSensorSetup => 4244,
NavCart => 4272,
GPSNav => 5891,
GPSIon => 5893,
GPSUtc => 5894,
GEONav => 5896,
ReceiverSetup => 5902,
PosCovCartesian => 5905,
PosCovGeodetic => 5906,
VelCovCartesian => 5907,
VelCovGeodetic => 5908,
XPPSOffset => 5911,
ReceiverTime => 5914,
DiffCorrIn => 5919,
EndOfPVT => 5921,
EndOfMeas => 5922,
ExtEvent => 5924,
Comment => 5936,
AttEuler => 5938,
AttCovEuler => 5939,
AuxAntPositions => 5942,
EndOfAtt => 5943,
);
pub fn is_sync(bytes: &[u8; 2]) -> bool {
bytes == b"$@"
}