use binrw::BinRead;
use alloc::vec::Vec;
use bitflags::bitflags;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum PvtMode {
#[default]
NoPvt = 0,
StandAlone = 1,
Differential = 2,
FixedLocation = 3,
RtkFixed = 4,
RtkFloat = 5,
Sbas = 6,
MovingBaseRtkFixed = 7,
MovingBaseRtkFloat = 8,
Ppp = 10,
}
impl From<u8> for PvtMode {
fn from(value: u8) -> Self {
match value & 0x0F {
0 => PvtMode::NoPvt,
1 => PvtMode::StandAlone,
2 => PvtMode::Differential,
3 => PvtMode::FixedLocation,
4 => PvtMode::RtkFixed,
5 => PvtMode::RtkFloat,
6 => PvtMode::Sbas,
7 => PvtMode::MovingBaseRtkFixed,
8 => PvtMode::MovingBaseRtkFloat,
10 => PvtMode::Ppp,
_ => PvtMode::NoPvt,
}
}
}
impl fmt::Display for PvtMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PvtMode::NoPvt => write!(f, "No PVT"),
PvtMode::StandAlone => write!(f, "Stand Alone"),
PvtMode::Differential => write!(f, "Differential"),
PvtMode::FixedLocation => write!(f, "Fixed Location"),
PvtMode::RtkFixed => write!(f, "RTK Fixed"),
PvtMode::RtkFloat => write!(f, "RTK Float"),
PvtMode::Sbas => write!(f, "SBAS"),
PvtMode::MovingBaseRtkFixed => write!(f, "Moving Base RTK Fixed"),
PvtMode::MovingBaseRtkFloat => write!(f, "Moving Base RTK Float"),
PvtMode::Ppp => write!(f, "PPP"),
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct PvtModeFlags: u8 {
const DETERMINING_STATIC = 1 << 6;
const MODE_2D = 1 << 7;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum Datum {
#[default]
Wgs84 = 0,
DgnssBaseStation = 19,
Etrs89 = 30,
Nad83_2011 = 31,
Nad83Pa11 = 32,
Nad83Ma11 = 33,
Gda94 = 34,
Gda2020 = 35,
Jgd2011 = 36,
UserDefined1 = 250,
UserDefined2 = 251,
Unknown,
}
impl From<u8> for Datum {
fn from(value: u8) -> Self {
match value {
0 => Datum::Wgs84,
19 => Datum::DgnssBaseStation,
30 => Datum::Etrs89,
31 => Datum::Nad83_2011,
32 => Datum::Nad83Pa11,
33 => Datum::Nad83Ma11,
34 => Datum::Gda94,
35 => Datum::Gda2020,
36 => Datum::Jgd2011,
250 => Datum::UserDefined1,
251 => Datum::UserDefined2,
_ => Datum::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum PvtError {
#[default]
None = 0,
NotEnoughMeasurements = 1,
NotEnoughEphemerides = 2,
DopTooLarge = 3,
ResidualsTooLarge = 4,
NoConvergence = 5,
NotEnoughAfterOutlierRejection = 6,
PositionProhibited = 7,
NotEnoughDiffCorrections = 8,
BaseCoordsUnavailable = 9,
AmbiguitiesNotFixed = 10,
}
impl From<u8> for PvtError {
fn from(value: u8) -> Self {
match value {
0 => PvtError::None,
1 => PvtError::NotEnoughMeasurements,
2 => PvtError::NotEnoughEphemerides,
3 => PvtError::DopTooLarge,
4 => PvtError::ResidualsTooLarge,
5 => PvtError::NoConvergence,
6 => PvtError::NotEnoughAfterOutlierRejection,
7 => PvtError::PositionProhibited,
8 => PvtError::NotEnoughDiffCorrections,
9 => PvtError::BaseCoordsUnavailable,
10 => PvtError::AmbiguitiesNotFixed,
_ => PvtError::None,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct WACorrFlags: u8 {
const ORBIT_CLOCK = 1 << 0;
const RANGE = 1 << 1;
const IONO = 1 << 2;
const ORBIT_ACCURACY = 1 << 3;
const DO229_PRECISION_APPROACH = 1 << 4;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum DiffCorrType {
#[default]
Unknown = 0,
PhysicalBase = 1,
VirtualBase = 2,
Ssr = 3,
}
impl From<u8> for DiffCorrType {
fn from(value: u8) -> Self {
match (value >> 5) & 0x03 {
0 => DiffCorrType::Unknown,
1 => DiffCorrType::PhysicalBase,
2 => DiffCorrType::VirtualBase,
3 => DiffCorrType::Ssr,
_ => DiffCorrType::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum RaimIntegrity {
#[default]
NotActive = 0,
TestSuccessful = 1,
TestFailed = 2,
Reserved = 3,
}
impl From<u8> for RaimIntegrity {
fn from(value: u8) -> Self {
match value & 0x03 {
0 => RaimIntegrity::NotActive,
1 => RaimIntegrity::TestSuccessful,
2 => RaimIntegrity::TestFailed,
3 => RaimIntegrity::Reserved,
_ => RaimIntegrity::NotActive,
}
}
}
#[derive(Debug, BinRead)]
pub struct PVTGeodetic {
#[br(map = |x: u32| if x == crate::DO_NOT_USE_U4 { None } else { Some(x) })]
pub tow: Option<u32>,
#[br(map = |x: u16| if x == crate::DO_NOT_USE_U2 { None } else { Some(x) })]
pub wnc: Option<u16>,
mode_raw: u8,
#[br(map = |x: u8| PvtError::from(x))]
pub error: PvtError,
#[br(map = |x: f64| if x == crate::DO_NOT_USE_F8 { None } else { Some(x) })]
pub latitude: Option<f64>,
#[br(map = |x: f64| if x == crate::DO_NOT_USE_F8 { None } else { Some(x) })]
pub longitude: Option<f64>,
#[br(map = |x: f64| if x == crate::DO_NOT_USE_F8 { None } else { Some(x) })]
pub height: Option<f64>,
#[br(map = |x: f32| if x == crate::DO_NOT_USE_F4 { None } else { Some(x) })]
pub undulation: Option<f32>,
#[br(map = |x: f32| if x == crate::DO_NOT_USE_F4 { None } else { Some(x) })]
pub vn: Option<f32>,
#[br(map = |x: f32| if x == crate::DO_NOT_USE_F4 { None } else { Some(x) })]
pub ve: Option<f32>,
#[br(map = |x: f32| if x == crate::DO_NOT_USE_F4 { None } else { Some(x) })]
pub vu: Option<f32>,
#[br(map = |x: f32| if x == crate::DO_NOT_USE_F4 { None } else { Some(x) })]
pub cog: Option<f32>,
#[br(map = |x: f64| if x == crate::DO_NOT_USE_F8 { None } else { Some(x) })]
pub rx_clk_bias: Option<f64>,
#[br(map = |x: f32| if x == crate::DO_NOT_USE_F4 { None } else { Some(x) })]
pub rx_clk_drift: Option<f32>,
#[br(map = |x: u8| if x == crate::DO_NOT_USE_U1 { None } else { Some(x) })]
pub time_system: Option<u8>,
#[br(map = |x: u8| if x == crate::DO_NOT_USE_U1 { None } else { Some(Datum::from(x)) })]
pub datum: Option<Datum>,
#[br(map = |x: u8| if x == crate::DO_NOT_USE_U1 { None } else { Some(x) })]
pub nr_sv: Option<u8>,
wa_corr_info_raw: u8,
#[br(map = |x: u16| if x == crate::DO_NOT_USE_U2 { None } else { Some(x) })]
pub reference_id: Option<u16>,
#[br(map = |x: u16| if x == crate::DO_NOT_USE_U2 { None } else { Some(x) })]
pub mean_corr_age: Option<u16>,
pub signal_info: u32,
alert_flag_raw: u8,
pub nr_bases: u8,
pub ppp_info: u16,
#[br(map = |x: u16| if x == crate::DO_NOT_USE_U2 { None } else { Some(x) })]
pub latency: Option<u16>,
#[br(map = |x: u16| if x == crate::DO_NOT_USE_U2 { None } else { Some(x) })]
pub h_accuracy: Option<u16>,
#[br(map = |x: u16| if x == crate::DO_NOT_USE_U2 { None } else { Some(x) })]
pub v_accuracy: Option<u16>,
pub misc: u8,
#[br(parse_with = binrw::helpers::until_eof)]
pub padding: Vec<u8>,
}
impl PVTGeodetic {
pub fn pvt_mode(&self) -> PvtMode {
PvtMode::from(self.mode_raw)
}
pub fn mode_flags(&self) -> PvtModeFlags {
PvtModeFlags::from_bits_truncate(self.mode_raw)
}
pub fn wa_corr_flags(&self) -> WACorrFlags {
WACorrFlags::from_bits_truncate(self.wa_corr_info_raw)
}
pub fn diff_corr_type(&self) -> DiffCorrType {
DiffCorrType::from(self.wa_corr_info_raw)
}
pub fn raim_integrity(&self) -> RaimIntegrity {
RaimIntegrity::from(self.alert_flag_raw)
}
pub fn galileo_hpca_failed(&self) -> bool {
self.alert_flag_raw & (1 << 2) != 0
}
pub fn galileo_iono_storm(&self) -> bool {
self.alert_flag_raw & (1 << 3) != 0
}
}