Skip to content

Commit

Permalink
RINEX v2 ionosphere parameter parsing (#239)
Browse files Browse the repository at this point in the history
* Add IonMessage::from_rinex2_header.

* Implement parsing ionosphere headers from RINEX2 files.

* RINEX3 Ionospheric Correction

   * Fix previous incapability to store several models (one per constellation)
   * Support all RINEX3 constellations
   * Introduce new tests
   * Improve API documentation and add more examples

---------

Signed-off-by: Guillaume W. Bres <guillaume.bressaix@gmail.com>
Co-authored-by: Guillaume W. Bres <guillaume.bressaix@gmail.com>
  • Loading branch information
fedosgad and gwbres committed Apr 26, 2024
1 parent 5f40818 commit 0ec6df1
Show file tree
Hide file tree
Showing 9 changed files with 584 additions and 216 deletions.
39 changes: 14 additions & 25 deletions rinex-cli/src/positioning/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,32 +117,21 @@ pub fn tropo_components(meteo: Option<&Rinex>, t: Epoch, lat_ddeg: f64) -> Optio
* Grabs nearest KB model (in time)
*/
pub fn kb_model(nav: &Rinex, t: Epoch) -> Option<KbModel> {
let kb_model = nav
let (_, sv, kb_model) = nav
.klobuchar_models()
.min_by_key(|(t_i, _, _)| (t - *t_i).abs());

if let Some((_, sv, kb_model)) = kb_model {
Some(KbModel {
h_km: {
match sv.constellation {
Constellation::BeiDou => 375.0,
// we only expect GPS or BDS here,
// badly formed RINEX will generate errors in the solutions
_ => 350.0,
}
},
alpha: kb_model.alpha,
beta: kb_model.beta,
})
} else {
/* RINEX 3 case */
let iono_corr = nav.header.ionod_correction?;
iono_corr.as_klobuchar().map(|kb_model| KbModel {
h_km: 350.0, //TODO improve this
alpha: kb_model.alpha,
beta: kb_model.beta,
})
}
.min_by_key(|(t_i, _, _)| (t - *t_i).abs())?;
Some(KbModel {
h_km: {
match sv.constellation {
Constellation::BeiDou => 375.0,
// we only expect GPS or BDS here,
// badly formed RINEX will generate errors in the solutions
_ => 350.0,
}
},
alpha: kb_model.alpha,
beta: kb_model.beta,
})
}

/*
Expand Down
158 changes: 127 additions & 31 deletions rinex/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,9 @@ pub struct Header {
/// attached to a specifid SV, only exists in ANTEX records
#[cfg_attr(feature = "serde", serde(default))]
pub sv_antenna: Option<SvAntenna>,
/// Possible Ionospheric Delay correction model.
/// Only exists in NAV V3 headers. In modern NAV, this
/// is regularly updated in the file's body.
pub ionod_correction: Option<IonMessage>,
/// Possible Ionospheric Delay correction model, described in
/// header section of old RINEX files (<V4).
pub ionod_corrections: HashMap<Constellation, IonMessage>,
/// Possible DCBs compensation information
pub dcb_compensations: Vec<DcbCompensation>,
/// Possible PCVs compensation information
Expand Down Expand Up @@ -257,7 +256,7 @@ impl Header {
let mut sampling_interval: Option<Duration> = None;
let mut ground_position: Option<GroundPosition> = None;
let mut dcb_compensations: Vec<DcbCompensation> = Vec::new();
let mut ionod_correction = Option::<IonMessage>::None;
let mut ionod_corrections = HashMap::<Constellation, IonMessage>::with_capacity(4);
let mut pcv_compensations: Vec<PcvCompensation> = Vec::new();
// RINEX specific fields
let mut current_constell: Option<Constellation> = None;
Expand Down Expand Up @@ -876,37 +875,134 @@ impl Header {
//TODO
// This will help RTK solving against GLONASS SV
} else if marker.contains("ION ALPHA") {
//TODO
//0.7451D-08 -0.1490D-07 -0.5960D-07 0.1192D-06 ION ALPHA
// RINEX v2 Ionospheric correction. We tolerate BETA/ALPHA order mixup, as per
// RINEX v2 standards [https://files.igs.org/pub/data/format/rinex211.txt] paragraph 5.2.
match IonMessage::from_rinex2_header(content, marker) {
Ok(IonMessage::KlobucharModel(KbModel {
alpha,
beta,
region,
})) => {
// Support GPS|GLO|BDS|GAL|QZSS|SBAS|IRNSS
for c in [
Constellation::GPS,
Constellation::Glonass,
Constellation::BeiDou,
Constellation::Galileo,
Constellation::IRNSS,
Constellation::QZSS,
Constellation::SBAS,
] {
if let Some(correction) = ionod_corrections.get_mut(&c) {
// Only Klobuchar models in RINEX2
let kb_model = correction.as_klobuchar_mut().unwrap();
kb_model.alpha = alpha;
kb_model.region = region;
} else {
ionod_corrections.insert(
c,
IonMessage::KlobucharModel(KbModel {
alpha,
beta,
region,
}),
);
}
}
},
_ => {},
}
} else if marker.contains("ION BETA") {
//TODO
//0.9011D+05 -0.6554D+05 -0.1311D+06 0.4588D+06 ION BETA
// RINEX v2 Ionospheric correction. We are flexible in their order of appearance,
// RINEX v2 standards do NOT guarantee that (header fields are free order).
// [https://files.igs.org/pub/data/format/rinex211.txt] paragraph 5.2.
match IonMessage::from_rinex2_header(content, marker) {
Ok(IonMessage::KlobucharModel(KbModel {
alpha,
beta,
region,
})) => {
// Support GPS|GLO|BDS|GAL|QZSS|SBAS|IRNSS
for c in [
Constellation::GPS,
Constellation::Glonass,
Constellation::BeiDou,
Constellation::Galileo,
Constellation::IRNSS,
Constellation::QZSS,
Constellation::SBAS,
] {
if let Some(correction) = ionod_corrections.get_mut(&c) {
// Only Klobuchar models in RINEX2
let kb_model = correction.as_klobuchar_mut().unwrap();
kb_model.beta = beta;
} else {
ionod_corrections.insert(
c,
IonMessage::KlobucharModel(KbModel {
alpha,
beta,
region,
}),
);
}
}
},
_ => {},
}
} else if marker.contains("IONOSPHERIC CORR") {
/*
* RINEX < 4 IONOSPHERIC Correction
* we still use the IonMessage (V4 compatible),
* the record will just contain a single model for the entire day course
* RINEX3 IONOSPHERIC CORRECTION
* We support both model in all RINEX2|RINEX3 constellations.
* RINEX4 replaces that with actual file content (body) for improved correction accuracy.
* The description requires 2 lines when dealing with KB model and we tolerate order mixup.
*/
if let Ok(model) = IonMessage::from_rinex3_header(content) {
// The Klobuchar model needs two lines to be entirely described.
if let Some(kb_model) = model.as_klobuchar() {
let correction_type = content.split_at(5).0.trim();
if correction_type.ends_with('B') {
let alpha = ionod_correction.unwrap().as_klobuchar().unwrap().alpha;
let (beta, region) = (kb_model.beta, kb_model.region);
ionod_correction = Some(IonMessage::KlobucharModel(KbModel {
alpha,
beta,
region,
}));
let model_id = content.split_at(5).0;
if model_id.len() < 3 {
/* BAD RINEX */
continue;
}
let constell_id = &model_id[..3];
let constell = match constell_id {
"GPS" => Constellation::GPS,
"GAL" => Constellation::Galileo,
"BDS" => Constellation::BeiDou,
"QZS" => Constellation::QZSS,
"IRN" => Constellation::IRNSS,
"GLO" => Constellation::Glonass,
_ => continue,
};
match IonMessage::from_rinex3_header(content) {
Ok(IonMessage::KlobucharModel(KbModel {
alpha,
beta,
region,
})) => {
// KB requires two lines
if let Some(ionod_model) = ionod_corrections.get_mut(&constell) {
let kb_model = ionod_model.as_klobuchar_mut().unwrap();
if model_id.ends_with('A') {
kb_model.alpha = alpha;
kb_model.region = region;
} else {
kb_model.beta = beta;
}
} else {
ionod_correction = Some(IonMessage::KlobucharModel(*kb_model));
// latch new model
ionod_corrections.insert(
constell,
IonMessage::KlobucharModel(KbModel {
alpha,
beta,
region,
}),
);
}
} else {
// The NequickG model fits on a single line.
// The BDGIM does not exist until RINEX4
ionod_correction = Some(model);
}
},
Ok(ion) => {
ionod_corrections.insert(constell, ion);
},
_ => {},
}
} else if marker.contains("TIME SYSTEM CORR") {
// GPUT 0.2793967723E-08 0.000000000E+00 147456 1395
Expand Down Expand Up @@ -1014,7 +1110,7 @@ impl Header {
glo_channels,
leap,
ground_position,
ionod_correction,
ionod_corrections,
dcb_compensations,
pcv_compensations,
wavelengths: None,
Expand Down

0 comments on commit 0ec6df1

Please sign in to comment.