use crate::parse_f64;
use crate::prelude::ParsingError;
mod bdgim;
mod klobuchar;
mod nequick_g;
#[cfg(feature = "ublox")]
#[cfg_attr(docsrs, doc(cfg(feature = "ublox")))]
pub mod ublox;
pub use bdgim::BdModel;
pub use klobuchar::{KbModel, KbRegionCode};
pub use nequick_g::{NgModel, NgRegionFlags};
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum IonosphereModel {
Klobuchar(KbModel),
NequickG(NgModel),
Bdgim(BdModel),
}
impl Default for IonosphereModel {
fn default() -> Self {
Self::Klobuchar(KbModel::default())
}
}
impl IonosphereModel {
pub(crate) fn from_rinex3_header(header: &str) -> Result<Self, ParsingError> {
let (system, model_params) = header.split_at(5);
let rem = model_params;
match system.trim() {
"GAL" => {
let (a0, rem) = rem.split_at(12);
let (a1, rem) = rem.split_at(12);
let (a2, _) = rem.split_at(12);
let a0 = parse_f64(a0.trim()).map_err(|_| ParsingError::NequickGData)?;
let a1 = parse_f64(a1.trim()).map_err(|_| ParsingError::NequickGData)?;
let a2 = parse_f64(a2.trim()).map_err(|_| ParsingError::NequickGData)?;
Ok(Self::NequickG(NgModel {
a: (a0, a1, a2),
region: NgRegionFlags::default(),
}))
},
system => {
let (a0, rem) = rem.split_at(12);
let (a1, rem) = rem.split_at(12);
let (a2, rem) = rem.split_at(12);
let (a3, _) = rem.split_at(12);
let region = match system.contains("QZS") {
true => KbRegionCode::Japan,
false => KbRegionCode::Worldwide,
};
if system.ends_with('A') {
let a0 = parse_f64(a0.trim()).map_err(|_| ParsingError::KlobucharData)?;
let a1 = parse_f64(a1.trim()).map_err(|_| ParsingError::KlobucharData)?;
let a2 = parse_f64(a2.trim()).map_err(|_| ParsingError::KlobucharData)?;
let a3 = parse_f64(a3.trim()).map_err(|_| ParsingError::KlobucharData)?;
Ok(Self::Klobuchar(KbModel {
alpha: (a0, a1, a2, a3),
beta: (0.0_f64, 0.0_f64, 0.0_f64, 0.0_f64),
region,
}))
} else {
let b0 = parse_f64(a0.trim()).map_err(|_| ParsingError::KlobucharData)?;
let b1 = parse_f64(a1.trim()).map_err(|_| ParsingError::KlobucharData)?;
let b2 = parse_f64(a2.trim()).map_err(|_| ParsingError::KlobucharData)?;
let b3 = parse_f64(a3.trim()).map_err(|_| ParsingError::KlobucharData)?;
Ok(Self::Klobuchar(KbModel {
alpha: (0.0_f64, 0.0_f64, 0.0_f64, 0.0_f64),
beta: (b0, b1, b2, b3),
region,
}))
}
},
}
}
pub(crate) fn from_rinex2_header(header: &str, marker: &str) -> Result<Self, ParsingError> {
let (_, rem) = header.split_at(2);
let (a0, rem) = rem.split_at(12);
let (a1, rem) = rem.split_at(12);
let (a2, rem) = rem.split_at(12);
let (a3, _) = rem.split_at(12);
if marker.contains("ALPHA") {
let a0 = parse_f64(a0.trim()).map_err(|_| ParsingError::KlobucharData)?;
let a1 = parse_f64(a1.trim()).map_err(|_| ParsingError::KlobucharData)?;
let a2 = parse_f64(a2.trim()).map_err(|_| ParsingError::KlobucharData)?;
let a3 = parse_f64(a3.trim()).map_err(|_| ParsingError::KlobucharData)?;
Ok(Self::Klobuchar(KbModel {
alpha: (a0, a1, a2, a3),
beta: (0.0_f64, 0.0_f64, 0.0_f64, 0.0_f64),
region: KbRegionCode::Worldwide,
}))
} else {
let b0 = parse_f64(a0.trim()).map_err(|_| ParsingError::KlobucharData)?;
let b1 = parse_f64(a1.trim()).map_err(|_| ParsingError::KlobucharData)?;
let b2 = parse_f64(a2.trim()).map_err(|_| ParsingError::KlobucharData)?;
let b3 = parse_f64(a3.trim()).map_err(|_| ParsingError::KlobucharData)?;
Ok(Self::Klobuchar(KbModel {
alpha: (0.0_f64, 0.0_f64, 0.0_f64, 0.0_f64),
beta: (b0, b1, b2, b3),
region: KbRegionCode::Worldwide,
}))
}
}
pub fn as_klobuchar(&self) -> Option<&KbModel> {
match self {
Self::Klobuchar(model) => Some(model),
_ => None,
}
}
pub fn as_klobuchar_mut(&mut self) -> Option<&mut KbModel> {
match self {
Self::Klobuchar(model) => Some(model),
_ => None,
}
}
pub fn as_nequick_g(&self) -> Option<&NgModel> {
match self {
Self::NequickG(model) => Some(model),
_ => None,
}
}
pub fn as_bdgim(&self) -> Option<&BdModel> {
match self {
Self::Bdgim(model) => Some(model),
_ => None,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn rinex3_ng_header_parsing() {
let ng = IonosphereModel::from_rinex3_header(
"GAL 6.6250e+01 -1.6406e-01 -2.4719e-03 0.0000e+00 ",
);
assert!(ng.is_ok(), "failed to parse GAL iono correction header");
let ng = ng.unwrap();
assert_eq!(
ng,
IonosphereModel::NequickG(NgModel {
a: (6.6250e+01, -1.6406e-01, -2.4719e-03),
region: NgRegionFlags::empty(),
})
);
}
#[test]
fn rinex3_kb_header_parsing() {
let kb = IonosphereModel::from_rinex3_header(
"GPSA 7.4506e-09 -1.4901e-08 -5.9605e-08 1.1921e-07 ",
);
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha: (7.4506E-9, -1.4901E-8, -5.9605E-8, 1.1921E-7),
beta: (0.0, 0.0, 0.0, 0.0),
region: KbRegionCode::Worldwide,
})
);
let kb = IonosphereModel::from_rinex3_header(
"GPSB 9.0112e+04 -6.5536e+04 -1.3107e+05 4.5875e+05 ",
);
assert!(kb.is_ok(), "failed to parse GPSB iono correction header");
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha: (0.0, 0.0, 0.0, 0.0),
beta: (9.0112E4, -6.5536E4, -1.3107E5, 4.5875E5),
region: KbRegionCode::Worldwide,
})
);
let kb = IonosphereModel::from_rinex3_header(
"GPSA 0.2794D-07 0.1490D-07 -0.1192D-06 -0.5960D-07 ",
);
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha: (0.2794E-07, 0.1490E-07, -0.1192E-06, -0.5960E-07),
beta: (0.0, 0.0, 0.0, 0.0),
region: KbRegionCode::Worldwide,
})
);
let kb = IonosphereModel::from_rinex3_header(
"GPSB 0.1290D+06 0.3277D+05 -0.2621D+06 0.2621D+06 ",
);
assert!(kb.is_ok(), "failed to parse GPSB iono correction header");
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha: (0.0, 0.0, 0.0, 0.0),
beta: (0.1290E+06, 0.3277E+05, -0.2621E+06, 0.2621E+06),
region: KbRegionCode::Worldwide,
})
);
let kb = IonosphereModel::from_rinex3_header(
"BDSA 1.1176e-08 2.9802e-08 -4.1723e-07 6.5565e-07 ",
);
assert!(kb.is_ok(), "failed to parse BDSA iono correction header");
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha: (1.1176E-8, 2.9802E-8, -4.1723E-7, 6.5565E-7),
beta: (0.0, 0.0, 0.0, 0.0),
region: KbRegionCode::Worldwide,
})
);
let kb = IonosphereModel::from_rinex3_header(
"BDSB 1.4131e+05 -5.2429e+05 1.6384e+06 -4.5875e+05 3 ",
);
assert!(kb.is_ok(), "failed to parse BDSB iono correction header");
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha: (0.0, 0.0, 0.0, 0.0),
beta: (1.4131E5, -5.2429E5, 1.6384E6, -4.5875E5),
region: KbRegionCode::Worldwide,
})
);
let kb = IonosphereModel::from_rinex3_header(
"QZSA 7.4506e-09 -1.4901e-08 -5.9605e-08 1.1921e-07 ",
);
assert!(kb.is_ok(), "failed to parse QZSA iono correction header");
let kb = kb.unwrap();
let kb = kb.as_klobuchar().unwrap();
assert_eq!(
kb.region,
KbRegionCode::Japan,
"QZSA ionospheric corr badly interprated as worldwide correction"
);
let kb = IonosphereModel::from_rinex3_header(
"QZSB 9.0112e+04 -6.5536e+04 -1.3107e+05 4.5875e+05 ",
);
assert!(kb.is_ok(), "failed to parse QZSB iono correction header");
let kb = kb.unwrap();
let kb = kb.as_klobuchar().unwrap();
assert_eq!(
kb.region,
KbRegionCode::Japan,
"QZSB ionospheric corr badly interprated as worldwide correction"
);
}
#[test]
fn rinex2_kb_header_parsing() {
for (line, alpha) in [
(
" .1676D-07 .2235D-07 -.1192D-06 -.1192D-06 ",
(0.1676E-07, 0.2235E-07, -0.1192E-06, -0.1192E-06),
),
(
" .2515D-07 .1490D-07 -.1192D-06 -.5960D-07 ",
(0.2515E-07, 0.1490E-07, -0.1192E-06, -0.5960E-07),
),
(
" 0.1211D-07 -0.7451D-08 -0.5960D-07 0.1192D-06 ",
(0.1211E-07, -0.7451E-08, -0.5960E-07, 0.1192E-06),
),
] {
let kb = IonosphereModel::from_rinex2_header(line, "ION ALPHA ");
assert!(kb.is_ok(), "failed to parse ION ALPHA header");
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha,
beta: (0.0, 0.0, 0.0, 0.0),
region: KbRegionCode::Worldwide,
})
);
}
for (line, beta) in [
(
" .1208D+06 .1310D+06 -.1310D+06 -.1966D+06 ",
(0.1208E+06, 0.1310E+06, -0.1310E+06, -0.1966E+06),
),
(
" .1290D+06 .4915D+05 -.2621D+06 .1966D+06 ",
(0.1290E+06, 0.4915E+05, -0.2621E+06, 0.1966E+06),
),
(
" 0.1167D+06 -0.2458D+06 -0.6554D+05 0.1114D+07 ",
(0.1167E+06, -0.2458E+06, -0.6554E+05, 0.1114E+07),
),
] {
let kb = IonosphereModel::from_rinex2_header(line, "ION BETA ");
assert!(kb.is_ok(), "failed to parse ION BETA header");
let kb = kb.unwrap();
assert_eq!(
kb,
IonosphereModel::Klobuchar(KbModel {
alpha: (0.0, 0.0, 0.0, 0.0),
beta,
region: KbRegionCode::Worldwide,
})
);
}
}
}