From 9cd94d8a20f0b542e66f5b7c4f432fd23420bcad Mon Sep 17 00:00:00 2001 From: gwbres Date: Sat, 27 Apr 2024 11:44:33 +0200 Subject: [PATCH] V0.16 (#235) * V0.16 release * fix cggtts generation * improving help menu * run clippy * ionod_correction: fix - calculations are limited to Kb models --------- Signed-off-by: Guillaume W. Bres --- Cargo.toml | 1 + crx2rnx/Cargo.toml | 4 +- rinex-cli/Cargo.toml | 10 +- rinex-cli/src/cli/fops/filegen.rs | 19 ++- rinex-cli/src/cli/mod.rs | 41 ++++- rinex-cli/src/positioning/cggtts/mod.rs | 187 ++++++++++++---------- rinex-cli/src/positioning/interp/orbit.rs | 12 +- rinex-cli/src/positioning/interp/time.rs | 36 ++--- rinex-cli/src/positioning/mod.rs | 2 +- rinex-cli/src/positioning/ppp/mod.rs | 5 +- rinex-qc/Cargo.toml | 4 +- rinex/Cargo.toml | 2 +- rinex/src/cospar.rs | 3 +- rinex/src/doris/record.rs | 8 +- rinex/src/lib.rs | 56 +++---- rinex/src/record.rs | 3 +- rinex/src/tests/doris.rs | 6 +- rinex/src/tests/nav.rs | 6 +- rnx2crx/Cargo.toml | 4 +- sinex/Cargo.toml | 4 +- ublox-rnx/Cargo.toml | 2 +- 21 files changed, 233 insertions(+), 182 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 309d51bf1..7cc428e11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "rinex-cli", "rnx2crx", "sp3", + "sinex", "ublox-rnx", ] diff --git a/crx2rnx/Cargo.toml b/crx2rnx/Cargo.toml index 540c0da44..024c89c1f 100644 --- a/crx2rnx/Cargo.toml +++ b/crx2rnx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crx2rnx" -version = "2.3.2" +version = "2.3.3" license = "MIT OR Apache-2.0" authors = ["Guillaume W. Bres "] description = "RINEX data decompressor" @@ -12,4 +12,4 @@ readme = "README.md" [dependencies] clap = { version = "4.4.13", features = ["derive", "color"] } -rinex = { path = "../rinex", version = "=0.15.7", features = ["serde"] } +rinex = { path = "../rinex", version = "=0.16.0", features = ["serde"] } diff --git a/rinex-cli/Cargo.toml b/rinex-cli/Cargo.toml index 5b3211350..8e4c3fe29 100644 --- a/rinex-cli/Cargo.toml +++ b/rinex-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rinex-cli" -version = "0.10.2" +version = "0.11.0" license = "MIT OR Apache-2.0" authors = ["Guillaume W. Bres "] description = "Command line tool parse and analyze RINEX data" @@ -32,8 +32,8 @@ horrorshow = "0.8" clap = { version = "4.4.13", features = ["derive", "color"] } hifitime = { version = "3.9.0", features = ["serde", "std"] } gnss-rs = { version = "2.1.3" , features = ["serde"] } -rinex = { path = "../rinex", version = "=0.15.7", features = ["full"] } -rinex-qc = { path = "../rinex-qc", version = "=0.1.12", features = ["serde"] } +rinex = { path = "../rinex", version = "=0.16.0", features = ["full"] } +rinex-qc = { path = "../rinex-qc", version = "=0.1.13", features = ["serde"] } sp3 = { path = "../sp3", version = "=1.0.8", features = ["serde", "flate2"] } serde = { version = "1.0", default-features = false, features = ["derive"] } @@ -42,9 +42,9 @@ plotly = "0.8.4" # plotly = { git = "https://github.com/gwbres/plotly", branch = "density-mapbox" } # solver -# gnss-rtk = { version = "0.4.3", features = ["serde"] } +gnss-rtk = { version = "0.4.4", features = ["serde"] } # gnss-rtk = { path = "../../rtk-rs/gnss-rtk", features = ["serde"] } -gnss-rtk = { git = "https://github.com/rtk-rs/gnss-rtk", branch = "main", features = ["serde"] } +# gnss-rtk = { git = "https://github.com/rtk-rs/gnss-rtk", branch = "main", features = ["serde"] } # cggtts cggtts = { version = "4.1.4", features = ["serde", "scheduler"] } diff --git a/rinex-cli/src/cli/fops/filegen.rs b/rinex-cli/src/cli/fops/filegen.rs index b8f40e47f..46e76d988 100644 --- a/rinex-cli/src/cli/fops/filegen.rs +++ b/rinex-cli/src/cli/fops/filegen.rs @@ -8,8 +8,23 @@ pub fn subcommand() -> Command { .long_flag("filegen") .arg_required_else_help(false) .about( - "RINEX Data formatting. Use this option to preprocess, modify and dump results as RINEX. -You can use this for example, to generate a decimated RINEX file from an input Observations file.", + "Parse, preprocess and generate data while preserving input format. See --filegen --help." + ) + .long_about(" +Use this mode to generate all file formats we support after preprocessing them. + +Example (1): generate decimated RINEX Observations +rinex-cli \\ + -f test_resources/CRNX/V3/ESBC00DNK_R_20201770000_01D_30S_MO.crx.gz \\ + -P decim:5min \\ + --filegen + +Example (2): redefine production agency while we do that +rinex-cli \\ + -f test_resources/CRNX/V3/ESBC00DNK_R_20201770000_01D_30S_MO.crx.gz \\ + -P decim:5min \\ + --filegen -a AGENCY +" ) .next_help_heading("Production Environment") .args(SHARED_GENERAL_ARGS.iter()) diff --git a/rinex-cli/src/cli/mod.rs b/rinex-cli/src/cli/mod.rs index 7ff58fc91..b6013ff0f 100644 --- a/rinex-cli/src/cli/mod.rs +++ b/rinex-cli/src/cli/mod.rs @@ -126,7 +126,8 @@ impl Cli { .value_name("FILE") .action(ArgAction::Append) .required_unless_present("directory") - .help("Load a single file. Use this as many times as needed. + .help("Load a single file. See --help") + .long_help("Use this as many times as needed. Available operations and following behavior highly depends on input data. Supported formats are: - Observation RINEX @@ -136,34 +137,60 @@ Supported formats are: - SP3 (high precision orbits) - IONEX (Ionosphere Maps) - ANTEX (antenna calibration as RINEX) -- DORIS (special Observation RINEX)")) +- DORIS (special Observation RINEX) + +Example (1): Load a single file +rinex-cli \\ + -f test_resources/CRNX/V3/ESBC00DNK_R_20201770000_01D_30S_MO.crx.gz + +Example (2): define a PPP compliant context +rinex-cli \\ + -f test_resources/CRNX/V3/ESBC00DNK_R_20201770000_01D_30S_MO.crx.gz \\ + -f test_resources/NAV/V3/ESBC00DNK_R_20201770000_01D_MN.rnx.gz \\ + -f test_resources/CLK/V3/GRG0MGXFIN_20201770000_01D_30S_CLK.CLK.gz \\ + -f test_resources/SP3/GRG0MGXFIN_20201770000_01D_15M_ORB.SP3.gz +")) .arg(Arg::new("directory") .short('d') .long("dir") .value_name("DIRECTORY") .action(ArgAction::Append) .required_unless_present("filepath") - .help("Load directory recursively. Use this as many times as needed. Default recursive depth is set to 5, + .help("Directory recursivel loader. See --help.") + .long_help("Use this as many times as needed. Default recursive depth is set to 5, but you can extend that with --depth. Refer to -f for more information.")) .arg(Arg::new("depth") .long("depth") .action(ArgAction::Set) .required(false) .value_parser(value_parser!(u8)) - .help("Extend maximal recursive search depth of -d. The default is 5.")) + .help("Extend maximal recursive search depth of -d. The default is 5.") + .long_help("The default recursive depth already supports hierarchies like: +/YEAR1 + /DOY0 + /STATION1 + /DOY1 + /STATION2 +/YEAR2 + /DOY0 + /STATION1")) .arg(Arg::new("quiet") .short('q') .long("quiet") .action(ArgAction::SetTrue) - .help("Disable all terminal output. Also disables auto HTML reports opener.")) + .help("Disable all terminal output. Also disables automatic HTML reports opening.")) .arg(Arg::new("workspace") .short('w') .long("workspace") .value_name("FOLDER") .value_parser(value_parser!(PathBuf)) - .help("Define custom workspace location. The env. variable RINEX_WORKSPACE, if present, is prefered. -If none of those exist, we will generate local \"WORKSPACE\" folder.")) + .help("Define custom workspace location. See --help.") + .long_help("The Workspace is where Output Products are to be generated. +By default the $RINEX_WORKSPACE variable is prefered if it is defined. +You can also use this flag to customize it. +If none are defined, we will then try to create a local directory named \"WORKSPACE\" like it is possible in this very repo.")) .next_help_heading("Preprocessing") + .about("Preprocessing todo") .arg(Arg::new("gps-filter") .short('G') .action(ArgAction::SetTrue) diff --git a/rinex-cli/src/positioning/cggtts/mod.rs b/rinex-cli/src/positioning/cggtts/mod.rs index 9d6e61dca..04583f7e4 100644 --- a/rinex-cli/src/positioning/cggtts/mod.rs +++ b/rinex-cli/src/positioning/cggtts/mod.rs @@ -8,7 +8,7 @@ pub use post_process::{post_process, Error as PostProcessingError}; use gnss::prelude::{Constellation, SV}; -use rinex::{carrier::Carrier, navigation::Ephemeris, prelude::Observable}; +use rinex::{carrier::Carrier, prelude::Observable}; use super::interp::TimeInterpolator; @@ -18,11 +18,11 @@ use rtk::prelude::{ Epoch, InterpolationResult, IonosphereBias, + Method, Observation, PVTSolutionType, Solver, TroposphereBias, //TimeScale - Vector3, }; use cggtts::{ @@ -35,13 +35,13 @@ use crate::positioning::{ bd_model, kb_model, ng_model, tropo_components, Error as PositioningError, }; -fn reset_sv_tracker(sv: SV, trackers: &mut HashMap<(SV, Observable), SVTracker>) { - for ((k_sv, _), tracker) in trackers { - if *k_sv == sv { - tracker.reset(); - } - } -} +// fn reset_sv_tracker(sv: SV, trackers: &mut HashMap<(SV, Observable), SVTracker>) { +// for ((k_sv, _), tracker) in trackers { +// if *k_sv == sv { +// tracker.reset(); +// } +// } +// } //TODO: see TODO down below // fn reset_sv_sig_tracker( @@ -83,14 +83,14 @@ where // infaillible, at this point let obs_data = ctx.data.observation().unwrap(); let nav_data = ctx.data.brdc_navigation().unwrap(); - let meteo_data = ctx.data.meteo(); let clk_data = ctx.data.clock(); + let meteo_data = ctx.data.meteo(); let sp3_has_clock = ctx.data.sp3_has_clock(); if clk_data.is_none() && sp3_has_clock { if let Some(sp3) = ctx.data.sp3() { - warn!("Using clock states defined in SP3 file: CLK product should be prefered"); + warn!("Using clock states defined in SP3 file - CLK product should be prefered"); if sp3.epoch_interval >= Duration::from_seconds(300.0) { warn!("Interpolating clock states from low sample rate SP3 will most likely introduce errors"); } @@ -101,7 +101,7 @@ where .dominant_sample_rate() .expect("RNX2CGGTTS requires steady GNSS observations"); - let mut interp = TimeInterpolator::from_ctx(&ctx); + let mut interp = TimeInterpolator::from_ctx(ctx); debug!("Clock interpolator created"); // CGGTTS specifics @@ -113,7 +113,8 @@ where for ((t, flag), (_clk, vehicles)) in obs_data.observation() { /* - * we only consider "OK" Epochs + * We only consider _valid_ epochs" + * TODO: make use of LLI marker here */ if !flag.is_ok() { continue; @@ -127,7 +128,7 @@ where if sv_eph.is_none() { warn!("{:?} ({}) : undetermined ephemeris", t, sv); - reset_sv_tracker(*sv, &mut trackers); // reset for this SV entirely + // reset_sv_tracker(*sv, &mut trackers); continue; // can't proceed further } @@ -153,7 +154,7 @@ where zwd_zdd, }; - // form PVT "candidate" for each signal + // tries to form a candidate for each signal for (observable, data) in observations { let carrier = Carrier::from_observable(sv.constellation, observable); if carrier.is_err() { @@ -164,7 +165,8 @@ where let frequency = carrier.frequency(); let mut code = Option::::None; - let mut phase = Option::::None; + let phase = Option::::None; + let mut doppler = Option::::None; if observable.is_pseudorange_observable() { code = Some(Observation { @@ -172,67 +174,86 @@ where snr: { data.snr.map(|snr| snr.into()) }, value: data.obs, }); - } else if observable.is_phase_observable() { - phase = Some(Observation { - frequency, - snr: { data.snr.map(|snr| snr.into()) }, - value: data.obs, - }); - } - // we only one phase or code here - if code.is_none() && phase.is_none() { - continue; - } + // attach one phase, if need be + match solver.cfg.method { + Method::SPP => {}, // nothing to do + Method::CodePPP => {}, // nothing to do + //Method::PPP => { + // // try to attach phase data + // let to_match = + // Observable::from_str(&format!("L{}", &observable.to_string()[1..])).unwrap(); + // for (observable, data) in observations { + // if *observable == phase_to_match { + // phase = Some(Observation { + // frequency, + // snr: { data.snr.map(|snr| snr.into()) }, + // value: data.obs, + // }); + // } + // } + //}, + } - let mut doppler = Option::::None; - let doppler_to_match = - Observable::from_str(&format!("D{}", &observable.to_string()[..1])).unwrap(); - - for (observable, data) in observations { - if observable.is_doppler_observable() && observable == &doppler_to_match { - doppler = Some(Observation { - frequency, - snr: { data.snr.map(|snr| snr.into()) }, - value: data.obs, - }); + // try to attach doppler + let doppler_to_match = + Observable::from_str(&format!("D{}", &observable.to_string()[1..])) + .unwrap(); + for (observable, data) in observations { + if *observable == doppler_to_match { + doppler = Some(Observation { + frequency, + snr: { data.snr.map(|snr| snr.into()) }, + value: data.obs, + }); + break; + } } } - let candidate = match code { - Some(code) => { - let doppler = match doppler { - Some(doppler) => vec![doppler], - None => vec![], - }; - Candidate::new( - *sv, - *t, - clock_corr, - sv_eph.tgd(), - vec![code], - vec![], - doppler, - ) - }, - None => { - let phase = phase.unwrap(); // infaillible - let doppler = match doppler { - Some(doppler) => vec![doppler], - None => vec![], + if code.is_none() { + continue; + } + + let mut codes = vec![code.unwrap()]; + + // complete if need be + match solver.cfg.method { + Method::SPP => {}, // nothing to do + Method::CodePPP => { + let (freq_to_match, code_to_match) = match carrier { + Carrier::L1 => ( + Carrier::L2.frequency(), + Observable::from_str("C2C").unwrap(), + ), + _ => ( + Carrier::L1.frequency(), + Observable::from_str("C1C").unwrap(), + ), }; - Candidate::new( - *sv, - *t, - clock_corr, - sv_eph.tgd(), - vec![], - vec![phase], - doppler, - ) + for (observable, data) in observations { + if *observable == code_to_match { + codes.push(Observation { + frequency: freq_to_match, + snr: { data.snr.map(|snr| snr.into()) }, + value: data.obs, + }); + break; + } + } }, }; + let dopplers = match doppler { + Some(doppler) => vec![doppler], + None => vec![], + }; + let phases = match phase { + Some(phase) => vec![phase], + None => vec![], + }; + let candidate = + Candidate::new(*sv, *t, clock_corr, sv_eph.tgd(), codes, phases, dopplers); match solver.resolve( *t, PVTSolutionType::TimeOnly, @@ -259,9 +280,7 @@ where }; let mdio = pvt_data.iono_bias.modeled; - let msio = pvt_data.iono_bias.measured; - debug!( "{:?} : new {}:{} PVT solution (elev={:.2}°, azi={:.2}°, REFSV={:.3E}, REFSYS={:.3E})", t, sv, observable, elevation, azimuth, refsv, refsys @@ -288,17 +307,17 @@ where Some(tracker) => tracker, }; - // verify buffer continuity - if !tracker.no_gaps(dominant_sampling_period) { - // on any discontinuity we need to reset - // that tracker. This will abort the ongoing track. - tracker.reset(); - warn!("{:?} - discarding {} track due to data gaps", t, sv); + // // verify buffer continuity + // if !tracker.no_gaps(dominant_sampling_period) { + // // on any discontinuity we need to reset + // // that tracker. This will abort the ongoing track. + // tracker.reset(); + // warn!("{:?} - discarding {} track due to data gaps", t, sv); - // push new measurement - tracker.latch_measurement(t, fitdata); - continue; // abort for this SV - } + // // push new measurement + // tracker.latch_measurement(t, fitdata); + // continue; // abort for this SV + // } if next_release.is_some() { let next_release = next_release.unwrap(); @@ -380,14 +399,14 @@ where } }, Err(e) => { - warn!("{:?} - pvt resolution error \"{}\"", t, e); /* * Any PVT resolution failures would introduce a data gap * which is incompatible with CGGTTS track fitting */ - if let Some(tracker) = trackers.get_mut(&(*sv, observable.clone())) { - tracker.reset(); - } + error!("pvt solver error - {}", e); + // if let Some(tracker) = trackers.get_mut(&(*sv, observable.clone())) { + // tracker.reset(); + // } }, } //.pvt resolve } // for all OBS diff --git a/rinex-cli/src/positioning/interp/orbit.rs b/rinex-cli/src/positioning/interp/orbit.rs index 716aa686a..4ac556ed0 100644 --- a/rinex-cli/src/positioning/interp/orbit.rs +++ b/rinex-cli/src/positioning/interp/orbit.rs @@ -179,17 +179,13 @@ impl<'a> Interpolator<'a> { if let Some(latest) = self.latest(sv) { if *latest >= t + dt { break; - } else { - if self.consume(1) { - // end of stream - break; - } - } - } else { - if self.consume(1) { + } else if self.consume(1) { // end of stream break; } + } else if self.consume(1) { + // end of stream + break; } } diff --git a/rinex-cli/src/positioning/interp/time.rs b/rinex-cli/src/positioning/interp/time.rs index e650b05f4..d2dfd1353 100644 --- a/rinex-cli/src/positioning/interp/time.rs +++ b/rinex-cli/src/positioning/interp/time.rs @@ -128,17 +128,13 @@ impl<'a> Interpolator<'a> { if let Some(latest) = self.latest(sv) { if *latest >= t + self.sampling { break; - } else { - if self.consume(1) { - // end of stream - break; - } - } - } else { - if self.consume(1) { + } else if self.consume(1) { // end of stream break; } + } else if self.consume(1) { + // end of stream + break; } } @@ -148,20 +144,18 @@ impl<'a> Interpolator<'a> { // interpolate: if need be let dy: Option = if *before_x == t { Some(Duration::from_seconds(*before_y)) + } else if let Some((after_x, after_y)) = buf + .inner + .iter() + .filter(|(v_t, _)| *v_t > t) + .reduce(|k, _| k) + { + let dx = (*after_x - *before_x).to_seconds(); + let mut dy = (*after_x - t).to_seconds() / dx * *before_y; + dy += (t - *before_x).to_seconds() / dx * *after_y; + Some(Duration::from_seconds(dy)) } else { - if let Some((after_x, after_y)) = buf - .inner - .iter() - .filter(|(v_t, _)| *v_t > t) - .reduce(|k, _| k) - { - let dx = (*after_x - *before_x).to_seconds(); - let mut dy = (*after_x - t).to_seconds() / dx * *before_y; - dy += (t - *before_x).to_seconds() / dx * *after_y; - Some(Duration::from_seconds(dy)) - } else { - None - } + None }; // management: discard old samples diff --git a/rinex-cli/src/positioning/mod.rs b/rinex-cli/src/positioning/mod.rs index a2face36b..e8af1008c 100644 --- a/rinex-cli/src/positioning/mod.rs +++ b/rinex-cli/src/positioning/mod.rs @@ -193,7 +193,7 @@ pub fn precise_positioning(ctx: &Context, matches: &ArgMatches) -> Result<(), Er ); let orbit = RefCell::new(OrbitInterpolator::from_ctx( - &ctx, + ctx, cfg.interp_order, apriori.clone(), )); diff --git a/rinex-cli/src/positioning/ppp/mod.rs b/rinex-cli/src/positioning/ppp/mod.rs index fd26cc041..7f579275c 100644 --- a/rinex-cli/src/positioning/ppp/mod.rs +++ b/rinex-cli/src/positioning/ppp/mod.rs @@ -5,7 +5,6 @@ use std::collections::BTreeMap; use rinex::{ carrier::Carrier, - navigation::Ephemeris, prelude::{Duration, SV}, }; @@ -14,7 +13,7 @@ pub use post_process::{post_process, Error as PostProcessingError}; use rtk::prelude::{ Candidate, Epoch, InterpolationResult, IonosphereBias, Observation, PVTSolution, - PVTSolutionType, Solver, TroposphereBias, Vector3, + PVTSolutionType, Solver, TroposphereBias, }; use super::interp::TimeInterpolator; @@ -46,7 +45,7 @@ where } } - let mut interp = TimeInterpolator::from_ctx(&ctx); + let mut interp = TimeInterpolator::from_ctx(ctx); debug!("Clock interpolator created"); for ((t, flag), (_clk, vehicles)) in obs_data.observation() { diff --git a/rinex-qc/Cargo.toml b/rinex-qc/Cargo.toml index 386c2de1e..df45cc694 100644 --- a/rinex-qc/Cargo.toml +++ b/rinex-qc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rinex-qc" -version = "0.1.12" +version = "0.1.13" license = "MIT OR Apache-2.0" authors = ["Guillaume W. Bres "] description = "RINEX data analysis" @@ -28,7 +28,7 @@ itertools = "0.12.0" statrs = "0.16" sp3 = { path = "../sp3", version = "=1.0.8", features = ["serde"] } rinex-qc-traits = { path = "../qc-traits", version = "=0.1.1" } -rinex = { path = "../rinex", version = "=0.15.7", features = ["full"] } +rinex = { path = "../rinex", version = "=0.16.0", features = ["full"] } gnss-rs = { version = "2.1.3", features = ["serde"] } [dev-dependencies] diff --git a/rinex/Cargo.toml b/rinex/Cargo.toml index 8587178a7..7d42c3ef3 100644 --- a/rinex/Cargo.toml +++ b/rinex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rinex" -version = "0.15.7" +version = "0.16.0" license = "MIT OR Apache-2.0" authors = ["Guillaume W. Bres "] description = "Package to parse and analyze RINEX data" diff --git a/rinex/src/cospar.rs b/rinex/src/cospar.rs index 81ec09f04..9fe08fe0a 100644 --- a/rinex/src/cospar.rs +++ b/rinex/src/cospar.rs @@ -11,7 +11,8 @@ pub enum Error { } /// COSPAR ID number -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct COSPAR { /// Launch year year: u16, diff --git a/rinex/src/doris/record.rs b/rinex/src/doris/record.rs index 3cddd54ca..164762263 100644 --- a/rinex/src/doris/record.rs +++ b/rinex/src/doris/record.rs @@ -470,7 +470,7 @@ D02 -2069899.788 -407871.014 4677242.25714 4677392.20614 -119.05 }; let values = content .get(&station) - .expect(&format!("failed to identify {:?}", station)); + .unwrap_or_else(|| panic!("failed to identify {:?}", station)); for (observable, data) in [ ( @@ -556,7 +556,7 @@ D02 -2069899.788 -407871.014 4677242.25714 4677392.20614 -119.05 ] { let value = values .get(&observable) - .expect(&format!("failed to identify {:?}", observable)); + .unwrap_or_else(|| panic!("failed to identify {:?}", observable)); assert_eq!(value, &data, "wrong value parsed for {:?}", observable); } @@ -575,7 +575,7 @@ D02 -2069899.788 -407871.014 4677242.25714 4677392.20614 -119.05 }; let values = content .get(&station) - .expect(&format!("failed to identify {:?}", station)); + .unwrap_or_else(|| panic!("failed to identify {:?}", station)); for (observable, data) in [ ( @@ -661,7 +661,7 @@ D02 -2069899.788 -407871.014 4677242.25714 4677392.20614 -119.05 ] { let value = values .get(&observable) - .expect(&format!("failed to identify {:?}", observable)); + .unwrap_or_else(|| panic!("failed to identify {:?}", observable)); assert_eq!(value, &data, "wrong value parsed for {:?}", observable); } } diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index 43894f92c..b17963943 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -2258,7 +2258,6 @@ impl Rinex { // on both GLONASS and SBAS // therfore, disables rtk with these two constellations let toe = toe?; - let dt = t - toe; let max_dtoe = Ephemeris::max_dtoe(svnn.constellation)?; if (t - toe).abs() < max_dtoe { Some((toe, eph)) @@ -2516,15 +2515,13 @@ impl Rinex { * In RINEX2/3, midnight UTC is the publication datetime */ let t0 = self.first_epoch().unwrap(); // will fail on invalid RINEX - let t0 = Epoch::from_utc_days(t0.to_utc_days().ceil()); + let t0 = Epoch::from_utc_days(t0.to_utc_days().round()); + dbg!(t0); Box::new( self.header .ionod_corrections .iter() - .map(move |(c, ion)| match c { - //Constellation::BeiDou => (t0, (NavMsgType::D1D2, SV::new(*c, 1), *ion)), - _ => (t0, (NavMsgType::LNAV, SV::new(*c, 1), *ion)), - }) + .map(move |(c, ion)| (t0, (NavMsgType::LNAV, SV::new(*c, 1), *ion))) .chain(self.navigation().flat_map(|(t, frames)| { frames.iter().filter_map(move |fr| { let (msg, sv, ion) = fr.as_ion()?; @@ -2648,36 +2645,39 @@ impl Rinex { carrier: Carrier, ) -> Option { // determine nearest in time - let (_, (model_sv, model)) = self + let (t_i, (model_sv, model)) = self .ionod_correction_models() .filter_map(|(t_i, (_, sv_i, msg_i))| { + // TODO + // calculations currently limited to KB model: implement others + let _ = msg_i.as_klobuchar()?; + // At most 1 day from publication time if t_i <= t && (t - t_i) < 24.0 * Unit::Hour { Some((t_i, (sv_i, msg_i))) } else { None } }) - .min_by_key(|(t_i, _)| (t - *t_i).abs())?; - - if let Some(kb) = model.as_klobuchar() { - let h_km = match model_sv.constellation { - Constellation::BeiDou => 375.0, - // we only expect BDS or GPS here, - // wrongly formed RINEX will cause innacurate results - Constellation::GPS | _ => 350.0, - }; - Some(kb.meters_delay( - t, - sv_elevation, - sv_azimuth, - h_km, - user_lat_ddeg, - user_lon_ddeg, - carrier, - )) - } else { - None - } + .min_by_key(|(t_i, _)| (t - *t_i))?; + + // TODO + // calculations currently limited to KB model: implement others + let kb = model.as_klobuchar().unwrap(); + let h_km = match model_sv.constellation { + Constellation::BeiDou => 375.0, + // we only expect BDS or GPS here, + // wrongly formed RINEX will cause innacurate results + Constellation::GPS | _ => 350.0, + }; + Some(kb.meters_delay( + t, + sv_elevation, + sv_azimuth, + h_km, + user_lat_ddeg, + user_lon_ddeg, + carrier, + )) } /// Returns [`StoMessage`] frames Iterator /// ``` diff --git a/rinex/src/record.rs b/rinex/src/record.rs index 11192854b..2189b0088 100644 --- a/rinex/src/record.rs +++ b/rinex/src/record.rs @@ -7,6 +7,7 @@ use serde::Serialize; use super::{ antex, clock, + clock::{ClockKey, ClockProfile}, hatanaka::{Compressor, Decompressor}, header, ionex, is_rinex_comment, merge, merge::Merge, @@ -497,7 +498,6 @@ pub fn parse_record( } } }, - Type::DORIS => {}, // FIXME } // new comments ? @@ -600,7 +600,6 @@ pub fn parse_record( let (antenna, content) = antex::record::parse_antenna(&epoch_content).unwrap(); atx_rec.push((antenna, content)); }, - Type::DORIS => {}, //TODO } // new comments ? if !comment_content.is_empty() { diff --git a/rinex/src/tests/doris.rs b/rinex/src/tests/doris.rs index dcec26920..022520d4d 100644 --- a/rinex/src/tests/doris.rs +++ b/rinex/src/tests/doris.rs @@ -2,11 +2,11 @@ mod test { use crate::tests::toolkit::doris_check_observables; use crate::tests::toolkit::doris_check_stations; - use crate::{erratic_time_frame, evenly_spaced_time_frame, tests::toolkit::TestTimeFrame}; - use crate::{observation::*, prelude::*}; + + use crate::prelude::*; use itertools::Itertools; use std::path::Path; - use std::str::FromStr; + #[test] #[cfg(feature = "flate2")] fn v3_cs2rx18164() { diff --git a/rinex/src/tests/nav.rs b/rinex/src/tests/nav.rs index 578f8536c..cc6185970 100644 --- a/rinex/src/tests/nav.rs +++ b/rinex/src/tests/nav.rs @@ -1512,10 +1512,10 @@ mod test { let rinex = rinex.unwrap(); for (t0, should_work) in [ - // test publication datetime - (Epoch::from_str("2021-01-01T00:00:01 UTC").unwrap(), true), + // VALID : publication datetime + (Epoch::from_str("2021-01-01T00:00:00 UTC").unwrap(), true), // VALID day course : random into that dat - (Epoch::from_gregorian_utc(2021, 01, 01, 05, 33, 24, 0), true), + (Epoch::from_str("2021-01-01T05:33:24 UTC").unwrap(), true), // VALID day course : 30 sec prior next day (Epoch::from_str("2021-01-01T23:59:30 UTC").unwrap(), true), // VALID day course : 1 sec prior next publication diff --git a/rnx2crx/Cargo.toml b/rnx2crx/Cargo.toml index d63d0cf2b..d5ed8e8f7 100644 --- a/rnx2crx/Cargo.toml +++ b/rnx2crx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rnx2crx" -version = "1.2.2" +version = "1.2.3" license = "MIT OR Apache-2.0" authors = ["Guillaume W. Bres "] description = "RINEX data compressor" @@ -14,4 +14,4 @@ readme = "README.md" [dependencies] thiserror = "1" clap = { version = "4.4.13", features = ["derive", "color"] } -rinex = { path = "../rinex", version = "=0.15.7", features = ["serde"] } +rinex = { path = "../rinex", version = "=0.16.0", features = ["serde"] } diff --git a/sinex/Cargo.toml b/sinex/Cargo.toml index cea25b57d..879e367b2 100644 --- a/sinex/Cargo.toml +++ b/sinex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sinex" -version = "0.2.2" +version = "0.2.3" license = "MIT OR Apache-2.0" authors = ["Guillaume W. Bres "] description = "Package to parse and analyze SINEX data" @@ -20,4 +20,4 @@ chrono = "0.4" thiserror = "1" strum = { version = "0.26", features = ["derive"] } strum_macros = "0.26" -gnss-rs = { version = "2.1.2", features = ["serde"] } +gnss-rs = { version = "2.1.3", features = ["serde"] } diff --git a/ublox-rnx/Cargo.toml b/ublox-rnx/Cargo.toml index 9bd4e8751..ca99eafce 100644 --- a/ublox-rnx/Cargo.toml +++ b/ublox-rnx/Cargo.toml @@ -22,4 +22,4 @@ serialport = "4.2.0" ublox = "0.4.4" clap = { version = "4.4.10", features = ["derive", "color"] } gnss-rs = { version = "2.1.3", features = ["serde"] } -rinex = { path = "../rinex", version = "=0.15.7", features = ["serde", "nav", "obs", "clock"] } +rinex = { path = "../rinex", version = "=0.16.0", features = ["serde", "nav", "obs", "clock"] }