Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP tweaks #150

Draft
wants to merge 3 commits into
base: nav-health
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rinex/src/bibliography.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ pub enum Bibliography {
/// [DOI](https://cdn.taoglas.com/wp-content/uploads/pdf/Multipath-Analysis-Using-Code-Minus-Carrier-Technique-in-GNSS-Antennas-_WhitePaper_VP__Final-1.pdf).
MpTaoglas,
/// QZSS Signal and Constellation interface specifications.
/// [DOI](https://qzss.go.jp/en/technical/download/pdf/ps-is-qzss/is-qzss-pnt-004.pdf)
/// [DOI](https://qzss.go.jp/en/technical/download/pdf/ps-is-qzss/is-qzss-pnt-005.pdf)
gwbres marked this conversation as resolved.
Show resolved Hide resolved
QzssPnt,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might also want to add reference to the Galileo and QZSS ICDs

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes definitely, this is work in progress, QZSS was the first dedicated specs I looked into, that's why it's there.

}
138 changes: 67 additions & 71 deletions rinex/src/navigation/health/gal.rs
Original file line number Diff line number Diff line change
@@ -1,88 +1,84 @@
//! GAL Sv Health specifications
use bitflags::bitflags;
//! GAL SV Health specifications
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed all Sv to SV as SV is an acronym for Satellite Vehicle


/// GAL Sv Health indication
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// GAL INAV & FNAV SV Health indication.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In RINEX v3 and v4 the Galileo health definition is the same for both INAV and FNAV

Though there is another field "data source" that indicates as well if INAV or FNAV (mainly for v3)

For INAV only bits for E1B and E5b can be set (since E5a not transmitted in INAV).

For FNAV only bits for E5a can be set (since E1B & E5b not transmitted in FNAV).

/// See [Bibliography::RINEX3] for more information.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum GALHealth {
/// GAL Legacy Sv Health indication
LNAV(LegacyHealth),
/// GAL INAV Sv Health indication
INAV(Health),
/// GAL FNAV Sv Health indication
FNAV(Health),
pub struct GALHealth {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to a struct (with to/from u32 conversions) as HS is actually a 2 bit integer and not two separate bit flags

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good idea

/// E1-B Data Validity Status
/// `false` = Navigation data valid
/// `true` = Working without guarantee
e1b_dvs: bool,
/// E1-B/C Signal Health Status
/// 0 = Signal OK
/// 1 = Signal out of service
/// 2 = Signal will be out of service
/// 3 = Signal Component currently in Test
e1b_hs: u8,
/// E5a Data Validity Status
/// `false` = Navigation data valid
/// `true` = Working without guarantee
e5a_dvs: bool,
/// E5a Signal Health Status
/// 0 = Signal OK
/// 1 = Signal out of service
/// 2 = Signal will be out of service
/// 3 = Signal Component currently in Test
e5a_hs: u8,
/// E5b Data Validity Status
/// `false` = Navigation data valid
/// `true` = Working without guarantee
e5b_dvs: bool,
/// E5b Signal Health Status
/// 0 = Signal OK
/// 1 = Signal out of service
/// 2 = Signal will be out of service
/// 3 = Signal Component currently in Test
e5b_hs: u8,
}

impl Default for GALHealth {
fn default() -> Self {
Self::LNAV(LegacyHealth::default())
impl std::fmt::UpperExp for GALHealth {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let value: u32 = u32::from(self);
write!(f, "{:e}", value as f32)
Copy link
Collaborator

@gwbres gwbres Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, all the NAV ecosystem implements the UpperExp formatting because that's how we (intend) to format those fields in file production contexts.

I say intend because NAV files production is the most problematic still to this day, while OBS and Meteo RINEX have been completed already.

}
}

impl GALHealth {
/// Unwraps self as [`LegacyHealth`] indicator
pub(crate) fn lnav(&self) -> Option<&LegacyHealth> {
match self {
Self::LNAV(h) => Some(h),
_ => None,
impl From<u32> for GALHealth {
fn from(value: u32) -> Self {
Self {
e1b_dvs: (value & 0b1) != 0,
e1b_hs: (value & 0b110 >> 1) as u8,
e5a_dvs: (value & 0b1000) != 0,
e5a_hs: (value & 0b110000 >> 4) as u8,
e5b_dvs: (value & 0b1000000) != 0,
e5b_hs: (value & 0b110000000 >> 7) as u8,
}
}
/// Unwraps self as [`Health`] indicator
pub(crate) fn fnav(&self) -> Option<&Health> {
match self {
Self::FNAV(h) => Some(h),
_ => None,
}

impl From<&GALHealth> for u32 {
fn from(value: &GALHealth) -> Self {
let mut ret: u32 = 0;

if value.e1b_dvs {
ret |= 0b1;
}
}
/// Unwraps self as [`Health`] indicator
pub(crate) fn inav(&self) -> Option<&Health> {
match self {
Self::INAV(h) => Some(h),
_ => None,

ret |= (value.e1b_hs as u32) & 0b11 << 1;

if value.e5a_dvs {
ret |= 0b1000;
}
}
}

bitflags! {
/// GAL Legacy Sv Health indication.
/// See [Bibliography::RINEX3] for more information.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LegacyHealth: u64 {
const E1B_DVS = 0x01;
const E1B_HS0 = 0x02;
const E1B_HS1 = 0x04;
const E5A_DVS = 0x08;
const E5A_HS0 = 0x10;
const E5A_HS1 = 0x20;
const E5B_HS0 = 0x40;
const E5B_HS1 = 0x80;
}
}
ret |= (value.e5a_hs as u32) & 0b11 << 4;

impl std::fmt::UpperExp for LegacyHealth {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:e}", self.bits() as f32)
}
}
if value.e5b_dvs {
ret |= 0b1000000;
}

bitflags! {
/// GAL FNAV and INAV Health indications.
/// See [Bibliography::RINEX4] for more information.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Health: u64 {
const L1Healthy = 0x01;
const L2Healthy = 0x02;
const L5Healthy = 0x04;
}
}
ret |= (value.e5b_hs as u32) & 0b11 << 7;

#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_gal() {
assert_eq!(GalHealth::default(), GalHealth::empty());
ret
}
}
138 changes: 96 additions & 42 deletions rinex/src/navigation/health/gps.rs
Original file line number Diff line number Diff line change
@@ -1,86 +1,140 @@
//! GPS Sv Health specifications
//! GPS SV Health specifications
use bitflags::bitflags;

/// GPS Sv Health indication
/// GPS SV Health indication
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum GPSHealth {
/// GPS Legacy Sv Health indication
LNAV(LegacyHealth),
/// GPS CNAV Sv Health indication
CNAV(CivilianHealth),
/// GPS CNV2 Sv Health indication
CNV2(Civilian2Health),
/// GPS Legacy SV Health indication
LNAV(LNAVHealth),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having the names of the structs similar to the enum cases is less confusing

/// GPS CNAV SV Health indication
CNAV(CNAVHealth),
/// GPS CNV2 SV Health indication
CNV2(CNAVHealth),
}

impl Default for GPSHealth {
fn default() -> Self {
Self::LNAV(LegacyHealth::default())
Self::LNAV(LNAVHealth::default())
}
}

impl GPSHealth {
/// Unwraps self as [`LegacyHealth`] indicator
pub(crate) fn lnav(&self) -> Option<&LegacyHealth> {
/// Unwraps self as [`LNAVHealth`] indicator
pub(crate) fn lnav(&self) -> Option<&LNAVHealth> {
match self {
Self::LNAV(h) => Some(h),
_ => None,
}
}
/// Unwraps self as [`CivilianHealth`] indicator
pub(crate) fn cnav(&self) -> Option<&CivilianHealth> {
/// Unwraps self as [`CNAVHealth`] indicator
pub(crate) fn cnav(&self) -> Option<&CNAVHealth> {
match self {
Self::CNAV(h) => Some(h),
_ => None,
}
}
/// Unwraps self as [`Civilian2Health`] indicator
pub(crate) fn cnv2(&self) -> Option<&Civilian2Health> {
/// Unwraps self as [`CNAVHealth`] indicator
pub(crate) fn cnv2(&self) -> Option<&CNAVHealth> {
match self {
Self::CNV2(h) => Some(h),
_ => None,
}
}
}

bitflags! {
/// GPS Legacy Sv Health indication.
/// See [Bibliography::RINEX3] for more information.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LegacyHealth: u64 {
const L1Healthy = 0x01;
const L2Healthy = 0x02;
const L5Healthy = 0x04;
}
/// GPS Legacy SV Health indication.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the Galileo struct, I also made this a struct as the lower 5 bits are actually a 5 bit integer

To tell health of just L1 or L2 some complex switch would be needed

/// Refer to [Bibliography::RINEX3] and [Bibliography::GpsIcd] 20.3.3.3.1.4 for more information.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LNAVHealth {
/// LNAV data health
/// `false` = all LNAV data are OK
/// `true` = some or all LNAV data are bad
data: bool,
Copy link
Collaborator

@gwbres gwbres Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data: bool,
unhealthy: bool,

easier to grasp ?

/// Signal component health
/// 0 = All Signals OK
/// 1 = All Signals Weak
/// 2 = All Signals Dead
/// 3 = All Signals Have No Data Modulation
/// 4 = L1 P Signal Weak
/// 5 = L1 P Signal Dead
/// 6 = L1 P Signal Has No Data Modulation
/// 7 = L2 P Signal Weak
/// 8 = L2 P Signal Dead
/// 9 = L2 P Signal Has No Data Modulation
/// 10 = L1C Signal Weak
/// 11 = L1C Signal Dead
/// 12 = L1C Signal Has No Data Modulation
/// 13 = L2C Signal Weak
/// 14 = L2C Signal Dead
/// 15 = L2C Signal Has No Data Modulation
/// 16 = L1 & L2 P Signal Weak
/// 17 = L1 & L2 P Signal Dead
/// 18 = L1 & L2 P Signal Has No Data Modulation
/// 19 = L1 & L2C Signal Weak
/// 20 = L1 & L2C Signal Dead
/// 21 = L1 & L2C Signal Has No Data Modulation
/// 22 = L1 Signal Weak
/// 23 = L1 Signal Dead
/// 24 = L1 Signal Has No Data Modulation
/// 25 = L2 Signal Weak
/// 26 = L2 Signal Dead
/// 27 = L2 Signal Has No Data Modulation
/// 28 = SV Is Temporarily Out
/// 29 = SV Will Be Temporarily Out
/// 30 = One Or More Signals Are Deformed, However The Relevant URA Parameters Are Valid
/// 31 = More Than One Combination Would Be Required To Describe Anomalies
signals: u8,
}

impl std::fmt::UpperExp for LegacyHealth {
impl std::fmt::UpperExp for LNAVHealth {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:e}", self.bits() as f32)
let value = u32::from(self);
write!(f, "{:e}", value as f32)
}
}

bitflags! {
/// GPS CNAV Sv Health indication.
/// See [Bibliography::RINEX4] for more information.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CivilianHealth: u64 {
const L1Healthy = 0x01;
const L2Healthy = 0x02;
const L5Healthy = 0x04;
impl From<u32> for LNAVHealth {
fn from(value: u32) -> Self {
Self {
data: (value & 0b100000) != 0,
signals: (value & 0b11111) as u8,
}
}
}

impl From<&LNAVHealth> for u32 {
fn from(value: &LNAVHealth) -> Self {
let mut ret: u32 = 0;

if value.data {
ret |= 0b100000;
}

ret |= (value.signals as u32) & 0b11111;

ret
}
}

bitflags! {
/// GPS CNAV-2 Sv Health indication.
/// See [Bibliography::RINEX4] for more information.
/// GPS CNAV & CNAV-2 SV Health indication.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GPS has same definition of health bits in CNAV and CNAV-2 so I changed this to a single struct used in both enum cases

/// Refer to [Bibliography::RINEX4] and [Bibliography::GpsIcd] 30.3.3.4.4 for more information.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Civilian2Health: u64 {
const L1Healthy = 0x01;
const L2Healthy = 0x02;
const L5Healthy = 0x04;
pub struct CNAVHealth: u8 {
/// L1 Signal Health
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed from L1Healthy to just L1 as Healthy makes it sound like true = healthy when the definition is actually the opposite

/// `false` = Some or all codes and data on this carrier are OK
/// `true` = All codes and data on this carrier are bad or unavailable
const L1 = 0x01;
/// L2 Signal Health
/// `false` = Some or all codes and data on this carrier are OK
/// `true` = All codes and data on this carrier are bad or unavailable
const L2 = 0x02;
/// L5 Signal Health
/// `false` = Some or all codes and data on this carrier are OK
/// `true` = All codes and data on this carrier are bad or unavailable
const L5 = 0x04;
}
}
Loading