diff --git a/rinex/src/algorithm/target.rs b/rinex/src/algorithm/target.rs index 1514f9558..99d7d4078 100644 --- a/rinex/src/algorithm/target.rs +++ b/rinex/src/algorithm/target.rs @@ -29,7 +29,7 @@ pub enum Error { #[error("constellation parsing error")] ConstellationParing(#[from] gnss::constellation::ParsingError), #[error("failed to parse epoch flag")] - EpochFlagParsing(#[from] crate::epoch::flag::Error), + EpochFlagParsing(#[from] crate::observation::flag::Error), #[error("failed to parse constellation")] ConstellationParsing, #[error("invalid nav item")] diff --git a/rinex/src/clock/record.rs b/rinex/src/clock/record.rs index 7e76132af..adc9ea37d 100644 --- a/rinex/src/clock/record.rs +++ b/rinex/src/clock/record.rs @@ -194,7 +194,7 @@ pub(crate) fn parse_epoch( const OFFSET: usize = "yyyy mm dd hh mm sssssssssss".len(); let (epoch, rem) = rem.split_at(OFFSET); - let (epoch, _) = epoch::parse_utc(epoch.trim())?; + let epoch = epoch::parse_utc(epoch.trim())?; // nb of data fields let (_n, rem) = rem.split_at(4); diff --git a/rinex/src/epoch/mod.rs b/rinex/src/epoch.rs similarity index 72% rename from rinex/src/epoch/mod.rs rename to rinex/src/epoch.rs index 5e7a1084f..aa1d88044 100644 --- a/rinex/src/epoch/mod.rs +++ b/rinex/src/epoch.rs @@ -1,18 +1,14 @@ +//! Epoch parsing helpers use crate::types::Type; use hifitime::{Duration, Epoch, TimeScale, Unit}; use std::str::FromStr; use thiserror::Error; -pub mod flag; -pub use flag::EpochFlag; - #[derive(Error, Debug)] pub enum ParsingError { - #[error("failed to parse epoch flag")] - EpochFlag(#[from] flag::Error), #[error("failed to parse utc timestamp")] EpochError(#[from] hifitime::Errors), - #[error("expecting \"yyyy mm dd hh mm ss.ssss xx\" format")] + #[error("expecting \"yyyy mm dd hh mm ss.ssss\" format")] FormatError, #[error("failed to parse seconds + nanos")] SecsNanosError(#[from] std::num::ParseFloatError), @@ -42,7 +38,7 @@ pub(crate) fn now() -> Epoch { /* * Formats given epoch to string, matching standard specifications */ -pub(crate) fn format(epoch: Epoch, flag: Option, t: Type, revision: u8) -> String { +pub(crate) fn format(epoch: Epoch, t: Type, revision: u8) -> String { // Hifitime V3 does not have a gregorian decomposition method let (y, m, d, hh, mm, ss, nanos) = match epoch.time_scale { TimeScale::GPST => (epoch + Duration::from_seconds(37.0)).to_gregorian_utc(), @@ -61,7 +57,7 @@ pub(crate) fn format(epoch: Epoch, flag: Option, t: Type, revision: u y += 100; } format!( - "{:02} {:>2} {:>2} {:>2} {:>2} {:>2}.{:07} {}", + "{:02} {:>2} {:>2} {:>2} {:>2} {:>2}.{:07}", y, m, d, @@ -69,11 +65,10 @@ pub(crate) fn format(epoch: Epoch, flag: Option, t: Type, revision: u mm, ss, nanos / 100, - flag.unwrap_or(EpochFlag::Ok) ) } else { format!( - "{:04} {:02} {:02} {:02} {:02} {:>2}.{:07} {}", + "{:04} {:02} {:02} {:02} {:02} {:>2}.{:07}", y, m, d, @@ -81,7 +76,6 @@ pub(crate) fn format(epoch: Epoch, flag: Option, t: Type, revision: u mm, ss, nanos / 100, - flag.unwrap_or(EpochFlag::Ok) ) } }, @@ -128,12 +122,9 @@ pub(crate) fn format(epoch: Epoch, flag: Option, t: Type, revision: u } /* - * Parses an Epoch and optional flag, interpreted as a datetime within specified TimeScale. + * Parses an Epoch, interpreted as a datetime within specified TimeScale. */ -pub(crate) fn parse_in_timescale( - content: &str, - ts: TimeScale, -) -> Result<(Epoch, EpochFlag), ParsingError> { +pub(crate) fn parse_in_timescale(content: &str, ts: TimeScale) -> Result { let mut y = 0_i32; let mut m = 0_u8; let mut d = 0_u8; @@ -141,7 +132,10 @@ pub(crate) fn parse_in_timescale( let mut mm = 0_u8; let mut ss = 0_u8; let mut ns = 0_u32; - let mut flag = EpochFlag::default(); + + if content.split_ascii_whitespace().count() < 6 { + return Err(ParsingError::FormatError); + } for (field_index, item) in content.split_ascii_whitespace().enumerate() { match field_index { @@ -207,15 +201,12 @@ pub(crate) fn parse_in_timescale( .map_err(|_| ParsingError::SecondsField(item.to_string()))?; } }, - 6 => { - flag = EpochFlag::from_str(item.trim())?; - }, _ => {}, } } //println!("content \"{}\"", content); // DEBUG - //println!("Y {} M {} D {} HH {} MM {} SS {} NS {} FLAG {}", y, m, d, hh, mm, ss, ns, flag); // DEBUG + //println!("Y {} M {} D {} HH {} MM {} SS {} NS {}", y, m, d, hh, mm, ss, ns); // DEBUG match ts { TimeScale::UTC => { @@ -226,7 +217,7 @@ pub(crate) fn parse_in_timescale( } let epoch = Epoch::from_gregorian_utc(y, m, d, hh, mm, ss, ns); - Ok((epoch, flag)) + Ok(epoch) }, _ => { // in case provided content is totally invalid, @@ -245,12 +236,12 @@ pub(crate) fn parse_in_timescale( ns / 100_000_000, ts ))?; - Ok((epoch, flag)) + Ok(epoch) }, } } -pub(crate) fn parse_utc(s: &str) -> Result<(Epoch, EpochFlag), ParsingError> { +pub(crate) fn parse_utc(s: &str) -> Result { parse_in_timescale(s, TimeScale::UTC) } @@ -278,7 +269,7 @@ mod test { fn epoch_parse_nav_v2() { let e = parse_utc("20 12 31 23 45 0.0"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2020); assert_eq!(m, 12); @@ -288,15 +279,11 @@ mod test { assert_eq!(ss, 0); assert_eq!(ns, 0); assert_eq!(e.time_scale, TimeScale::UTC); - assert_eq!(flag, EpochFlag::Ok); - assert_eq!( - format(e, None, Type::NavigationData, 2), - "20 12 31 23 45 0.0" - ); + assert_eq!(format(e, Type::NavigationData, 2), "20 12 31 23 45 0.0"); let e = parse_utc("21 1 1 16 15 0.0"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2021); assert_eq!(m, 1); @@ -306,30 +293,23 @@ mod test { assert_eq!(ss, 0); assert_eq!(ns, 0); assert_eq!(e.time_scale, TimeScale::UTC); - assert_eq!(flag, EpochFlag::Ok); - assert_eq!( - format(e, None, Type::NavigationData, 2), - "21 1 1 16 15 0.0" - ); + assert_eq!(format(e, Type::NavigationData, 2), "21 1 1 16 15 0.0"); } #[test] fn epoch_parse_nav_v2_nanos() { let e = parse_utc("20 12 31 23 45 0.1"); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (_, _, _, _, _, ss, ns) = e.to_gregorian_utc(); assert_eq!(ss, 0); assert_eq!(ns, 100_000_000); - assert_eq!( - format(e, None, Type::NavigationData, 2), - "20 12 31 23 45 0.1" - ); + assert_eq!(format(e, Type::NavigationData, 2), "20 12 31 23 45 0.1"); } #[test] fn epoch_parse_nav_v3() { let e = parse_utc("2021 01 01 00 00 00 "); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2021); assert_eq!(m, 1); @@ -339,14 +319,11 @@ mod test { assert_eq!(ss, 0); assert_eq!(ns, 0); assert_eq!(e.time_scale, TimeScale::UTC); - assert_eq!( - format(e, None, Type::NavigationData, 3), - "2021 01 01 00 00 00" - ); + assert_eq!(format(e, Type::NavigationData, 3), "2021 01 01 00 00 00"); let e = parse_utc("2021 01 01 09 45 00 "); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2021); assert_eq!(m, 1); @@ -355,14 +332,11 @@ mod test { assert_eq!(mm, 45); assert_eq!(ss, 0); assert_eq!(ns, 0); - assert_eq!( - format(e, None, Type::NavigationData, 3), - "2021 01 01 09 45 00" - ); + assert_eq!(format(e, Type::NavigationData, 3), "2021 01 01 09 45 00"); let e = parse_utc("2020 06 25 00 00 00"); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2020); assert_eq!(m, 6); @@ -371,14 +345,11 @@ mod test { assert_eq!(mm, 00); assert_eq!(ss, 0); assert_eq!(ns, 0); - assert_eq!( - format(e, None, Type::NavigationData, 3), - "2020 06 25 00 00 00" - ); + assert_eq!(format(e, Type::NavigationData, 3), "2020 06 25 00 00 00"); let e = parse_utc("2020 06 25 09 49 04"); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2020); assert_eq!(m, 6); @@ -387,16 +358,13 @@ mod test { assert_eq!(mm, 49); assert_eq!(ss, 04); assert_eq!(ns, 0); - assert_eq!( - format(e, None, Type::NavigationData, 3), - "2020 06 25 09 49 04" - ); + assert_eq!(format(e, Type::NavigationData, 3), "2020 06 25 09 49 04"); } #[test] fn epoch_parse_obs_v2() { - let e = parse_utc(" 21 12 21 0 0 0.0000000 0"); + let e = parse_utc(" 21 12 21 0 0 0.0000000"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2021); assert_eq!(m, 12); @@ -406,15 +374,14 @@ mod test { assert_eq!(ss, 0); assert_eq!(ns, 0); assert_eq!(e.time_scale, TimeScale::UTC); - assert_eq!(flag, EpochFlag::Ok); assert_eq!( - format(e, None, Type::ObservationData, 2), - "21 12 21 0 0 0.0000000 0" + format(e, Type::ObservationData, 2), + "21 12 21 0 0 0.0000000" ); - let e = parse_utc(" 21 12 21 0 0 30.0000000 0"); + let e = parse_utc(" 21 12 21 0 0 30.0000000"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2021); assert_eq!(m, 12); @@ -423,46 +390,14 @@ mod test { assert_eq!(mm, 00); assert_eq!(ss, 30); assert_eq!(ns, 0); - assert_eq!(flag, EpochFlag::Ok); assert_eq!( - format(e, None, Type::ObservationData, 2), - "21 12 21 0 0 30.0000000 0" + format(e, Type::ObservationData, 2), + "21 12 21 0 0 30.0000000" ); - let e = parse_utc(" 21 12 21 0 0 30.0000000 1"); - assert!(e.is_ok()); - let (_e, flag) = e.unwrap(); - assert_eq!(flag, EpochFlag::PowerFailure); - //assert_eq!(format!("{:o}", e), "21 12 21 0 0 30.0000000 1"); - - let e = parse_utc(" 21 12 21 0 0 30.0000000 2"); - assert!(e.is_ok()); - let (_e, flag) = e.unwrap(); - assert_eq!(flag, EpochFlag::AntennaBeingMoved); - - let e = parse_utc(" 21 12 21 0 0 30.0000000 3"); - assert!(e.is_ok()); - let (_e, flag) = e.unwrap(); - assert_eq!(flag, EpochFlag::NewSiteOccupation); - - let e = parse_utc(" 21 12 21 0 0 30.0000000 4"); - assert!(e.is_ok()); - let (_e, flag) = e.unwrap(); - assert_eq!(flag, EpochFlag::HeaderInformationFollows); - - let e = parse_utc(" 21 12 21 0 0 30.0000000 5"); - assert!(e.is_ok()); - let (_e, flag) = e.unwrap(); - assert_eq!(flag, EpochFlag::ExternalEvent); - - let e = parse_utc(" 21 12 21 0 0 30.0000000 6"); + let e = parse_utc(" 21 1 1 0 0 0.0000000"); assert!(e.is_ok()); - let (_e, flag) = e.unwrap(); - assert_eq!(flag, EpochFlag::CycleSlip); - - let e = parse_utc(" 21 1 1 0 0 0.0000000 0"); - assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2021); assert_eq!(m, 1); @@ -471,12 +406,14 @@ mod test { assert_eq!(mm, 00); assert_eq!(ss, 0); assert_eq!(ns, 0); - assert_eq!(flag, EpochFlag::Ok); - //assert_eq!(format!("{:o}", e), "21 1 1 0 0 0.0000000 0"); + assert_eq!( + format(e, Type::ObservationData, 2), + "21 1 1 0 0 0.0000000" + ); - let e = parse_utc(" 21 1 1 0 7 30.0000000 0"); + let e = parse_utc(" 21 1 1 0 7 30.0000000"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2021); assert_eq!(m, 1); @@ -485,14 +422,16 @@ mod test { assert_eq!(mm, 7); assert_eq!(ss, 30); assert_eq!(ns, 0); - assert_eq!(flag, EpochFlag::Ok); - //assert_eq!(format!("{:o}", e), "21 1 1 0 7 30.0000000 0"); + assert_eq!( + format(e, Type::ObservationData, 2), + "21 1 1 0 7 30.0000000" + ); } #[test] fn epoch_parse_obs_v3() { - let e = parse_utc(" 2022 01 09 00 00 0.0000000 0"); + let e = parse_utc(" 2022 01 09 00 00 0.0000000"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2022); assert_eq!(m, 1); @@ -501,12 +440,14 @@ mod test { assert_eq!(mm, 0); assert_eq!(ss, 00); assert_eq!(ns, 0); - assert_eq!(flag, EpochFlag::Ok); - //assert_eq!(format!("{}", e), "2022 01 09 00 00 0.0000000 0"); + assert_eq!( + format(e, Type::ObservationData, 3), + "2022 01 09 00 00 0.0000000" + ); - let e = parse_utc(" 2022 01 09 00 13 30.0000000 0"); + let e = parse_utc(" 2022 01 09 00 13 30.0000000"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2022); assert_eq!(m, 1); @@ -515,12 +456,14 @@ mod test { assert_eq!(mm, 13); assert_eq!(ss, 30); assert_eq!(ns, 0); - assert_eq!(flag, EpochFlag::Ok); - //assert_eq!(format!("{}", e), "2022 01 09 00 13 30.0000000 0"); + assert_eq!( + format(e, Type::ObservationData, 3), + "2022 01 09 00 13 30.0000000" + ); - let e = parse_utc(" 2022 03 04 00 52 30.0000000 0"); + let e = parse_utc(" 2022 03 04 00 52 30.0000000"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2022); assert_eq!(m, 3); @@ -529,12 +472,14 @@ mod test { assert_eq!(mm, 52); assert_eq!(ss, 30); assert_eq!(ns, 0); - assert_eq!(flag, EpochFlag::Ok); - //assert_eq!(format!("{}", e), "2022 03 04 00 52 30.0000000 0"); + assert_eq!( + format(e, Type::ObservationData, 3), + "2022 03 04 00 52 30.0000000" + ); - let e = parse_utc(" 2022 03 04 00 02 30.0000000 0"); + let e = parse_utc(" 2022 03 04 00 02 30.0000000"); assert!(e.is_ok()); - let (e, flag) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2022); assert_eq!(m, 3); @@ -543,49 +488,64 @@ mod test { assert_eq!(mm, 02); assert_eq!(ss, 30); assert_eq!(ns, 0); - assert_eq!(flag, EpochFlag::Ok); - //assert_eq!(format!("{}", e), "2022 03 04 00 02 30.0000000 0"); + assert_eq!( + format(e, Type::ObservationData, 3), + "2022 03 04 00 02 30.0000000" + ); } #[test] fn epoch_parse_obs_v2_nanos() { - let e = parse_utc(" 21 1 1 0 7 39.1234567 0"); + let e = parse_utc(" 21 1 1 0 7 39.1234567"); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (_, _, _, _, _, ss, ns) = e.to_gregorian_utc(); assert_eq!(ss, 39); assert_eq!(ns, 123_456_700); + assert_eq!( + format(e, Type::ObservationData, 2), + "21 1 1 0 7 39.1234567" + ); } #[test] fn epoch_parse_obs_v3_nanos() { - let e = parse_utc("2022 01 09 00 00 0.1000000 0"); + let e = parse_utc("2022 01 09 00 00 0.1000000"); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (_, _, _, _, _, ss, ns) = e.to_gregorian_utc(); assert_eq!(ss, 0); assert_eq!(ns, 100_000_000); - //assert_eq!(format!("{}", e), "2022 01 09 00 00 0.1000000 0"); + assert_eq!( + format(e, Type::ObservationData, 3), + "2022 01 09 00 00 0.1000000" + ); - let e = parse_utc(" 2022 01 09 00 00 0.1234000 0"); + let e = parse_utc(" 2022 01 09 00 00 0.1234000"); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (_, _, _, _, _, ss, ns) = e.to_gregorian_utc(); assert_eq!(ss, 0); assert_eq!(ns, 123_400_000); - //assert_eq!(format!("{}", e), "2022 01 09 00 00 0.1234000 0"); + assert_eq!( + format(e, Type::ObservationData, 3), + "2022 01 09 00 00 0.1234000" + ); - let e = parse_utc(" 2022 01 09 00 00 8.7654321 0"); + let e = parse_utc(" 2022 01 09 00 00 8.7654321"); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (_, _, _, _, _, ss, ns) = e.to_gregorian_utc(); assert_eq!(ss, 8); assert_eq!(ns, 765_432_100); - //assert_eq!(format!("{}", e), "2022 01 09 00 00 8.7654321 0"); + assert_eq!( + format(e, Type::ObservationData, 3), + "2022 01 09 00 00 8.7654321" + ); } #[test] fn epoch_parse_meteo_v2() { let e = parse_utc(" 22 1 4 0 0 0 "); assert!(e.is_ok()); - let (e, _) = e.unwrap(); + let e = e.unwrap(); let (y, m, d, hh, mm, ss, ns) = e.to_gregorian_utc(); assert_eq!(y, 2022); assert_eq!(m, 1); @@ -594,7 +554,7 @@ mod test { assert_eq!(mm, 00); assert_eq!(ss, 00); assert_eq!(ns, 0); - //assert_eq!(format!("{}", e), "2022 03 04 00 02 30.0000000 0"); + assert_eq!(format(e, Type::MeteoData, 2), "22 1 4 0 0 0"); } #[test] fn epoch_decomposition() { diff --git a/rinex/src/ionex/record.rs b/rinex/src/ionex/record.rs index d40ba1ea5..d0e6c5e7c 100644 --- a/rinex/src/ionex/record.rs +++ b/rinex/src/ionex/record.rs @@ -171,7 +171,7 @@ pub(crate) fn parse_plane( // debug // println!("NEW GRID : h: {} lat : {} lon : {}, dlon: {}", altitude, latitude, longitude, dlon); } else if marker.contains("EPOCH OF CURRENT MAP") { - epoch = epoch::parse_utc(content)?.0; + epoch = epoch::parse_utc(content)?; } else if marker.contains("EXPONENT") { // update current scaling if let Ok(e) = content.trim().parse::() { diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index 2e9c628a2..146679667 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -80,10 +80,10 @@ pub mod prelude { #[cfg(feature = "sp3")] pub use crate::context::{ProductType, RnxContext}; pub use crate::domes::Domes; - pub use crate::epoch::EpochFlag; pub use crate::ground_position::GroundPosition; pub use crate::header::Header; pub use crate::observable::Observable; + pub use crate::observation::EpochFlag; pub use crate::types::Type as RinexType; pub use crate::Error; pub use crate::Rinex; diff --git a/rinex/src/meteo/record.rs b/rinex/src/meteo/record.rs index e3b3cd010..9eba01043 100644 --- a/rinex/src/meteo/record.rs +++ b/rinex/src/meteo/record.rs @@ -18,7 +18,7 @@ pub type Record = BTreeMap>; * we should initiate the parsing of a meteo record entry. */ pub(crate) fn is_new_epoch(line: &str, v: version::Version) -> bool { - if v.major < 4 { + if v.major < 3 { let min_len = " 15 1 1 0 0 0"; if line.len() < min_len.len() { // minimum epoch descriptor @@ -65,7 +65,7 @@ pub(crate) fn parse_epoch( offset += 2; // YYYY } - let (epoch, _) = epoch::parse_utc(&line[0..offset])?; + let epoch = epoch::parse_utc(&line[0..offset])?; let codes = &header.meteo.as_ref().unwrap().codes; let nb_codes = codes.len(); @@ -117,7 +117,7 @@ pub(crate) fn fmt_epoch( let mut lines = String::with_capacity(128); lines.push_str(&format!( " {}", - epoch::format(*epoch, None, Type::MeteoData, header.version.major) + epoch::format(*epoch, Type::MeteoData, header.version.major) )); let observables = &header.meteo.as_ref().unwrap().codes; let mut index = 0; diff --git a/rinex/src/navigation/eopmessage.rs b/rinex/src/navigation/eopmessage.rs index 333645f4c..ad7a52def 100644 --- a/rinex/src/navigation/eopmessage.rs +++ b/rinex/src/navigation/eopmessage.rs @@ -60,7 +60,7 @@ impl EopMessage { let (dut, rem) = rem.split_at(19); let (ddut, dddut) = rem.split_at(19); - let (epoch, _) = epoch::parse_in_timescale(epoch.trim(), ts)?; + let epoch = epoch::parse_in_timescale(epoch.trim(), ts)?; let x = ( f64::from_str(xp.trim()).unwrap_or(0.0_f64), f64::from_str(dxp.trim()).unwrap_or(0.0_f64), diff --git a/rinex/src/navigation/ephemeris.rs b/rinex/src/navigation/ephemeris.rs index 6fcbf63d8..6c4dc9cd5 100644 --- a/rinex/src/navigation/ephemeris.rs +++ b/rinex/src/navigation/ephemeris.rs @@ -213,7 +213,7 @@ impl Ephemeris { .ok_or(Error::TimescaleIdentification(sv))?; //println!("V2/V3 CONTENT \"{}\" TIMESCALE {}", line, ts); //DEBUG - let (epoch, _) = epoch::parse_in_timescale(date.trim(), ts)?; + let epoch = epoch::parse_in_timescale(date.trim(), ts)?; let clock_bias = f64::from_str(clk_bias.replace('D', "E").trim())?; let clock_drift = f64::from_str(clk_dr.replace('D', "E").trim())?; @@ -249,7 +249,7 @@ impl Ephemeris { let (svnn, rem) = line.split_at(4); let sv = SV::from_str(svnn.trim())?; let (epoch, rem) = rem.split_at(19); - let (epoch, _) = epoch::parse_in_timescale(epoch.trim(), ts)?; + let epoch = epoch::parse_in_timescale(epoch.trim(), ts)?; let (clk_bias, rem) = rem.split_at(19); let (clk_dr, clk_drr) = rem.split_at(19); diff --git a/rinex/src/navigation/ionmessage.rs b/rinex/src/navigation/ionmessage.rs index aa860630e..7859301cc 100644 --- a/rinex/src/navigation/ionmessage.rs +++ b/rinex/src/navigation/ionmessage.rs @@ -124,7 +124,7 @@ impl KbModel { }, }; - let (epoch, _) = parse_in_timescale(epoch.trim(), ts)?; + let epoch = parse_in_timescale(epoch.trim(), ts)?; let alpha = ( f64::from_str(a0.trim()).map_err(|_| Error::KbAlphaValueError)?, f64::from_str(a1.trim()).map_err(|_| Error::KbAlphaValueError)?, @@ -257,7 +257,7 @@ impl NgModel { _ => return Err(Error::NgModelMissing2ndLine), }; - let (epoch, _) = parse_in_timescale(epoch.trim(), ts)?; + let epoch = parse_in_timescale(epoch.trim(), ts)?; let a = ( f64::from_str(a0.trim()).map_err(|_| Error::NgValueError)?, f64::from_str(a1.trim()).map_err(|_| Error::NgValueError)?, @@ -316,7 +316,7 @@ impl BdModel { }; let (a7, a8) = line.split_at(23); - let (epoch, _) = parse_in_timescale(epoch.trim(), ts)?; + let epoch = parse_in_timescale(epoch.trim(), ts)?; let alpha = ( f64::from_str(a0.trim()).unwrap_or(0.0_f64), f64::from_str(a1.trim()).unwrap_or(0.0_f64), diff --git a/rinex/src/navigation/record.rs b/rinex/src/navigation/record.rs index 2d8bfb212..490aa0ce4 100644 --- a/rinex/src/navigation/record.rs +++ b/rinex/src/navigation/record.rs @@ -396,7 +396,7 @@ fn fmt_epoch_v2v3(epoch: &Epoch, data: &Vec, header: &Header) -> Resul } lines.push_str(&format!( "{} ", - epoch::format(*epoch, None, Type::NavigationData, header.version.major) + epoch::format(*epoch, Type::NavigationData, header.version.major) )); lines.push_str(&format!( "{:14.11E} {:14.11E} {:14.11E}\n ", @@ -469,7 +469,7 @@ fn fmt_epoch_v4(epoch: &Epoch, data: &Vec, header: &Header) -> Result< } lines.push_str(&format!( "{} ", - epoch::format(*epoch, None, Type::NavigationData, header.version.major) + epoch::format(*epoch, Type::NavigationData, header.version.major) )); lines.push_str(&format!( "{:14.13E} {:14.13E} {:14.13E}\n", @@ -506,7 +506,7 @@ fn fmt_epoch_v4(epoch: &Epoch, data: &Vec, header: &Header) -> Result< )); lines.push_str(&format!( " {} {} {}\n", - epoch::format(*epoch, None, Type::NavigationData, header.version.major), + epoch::format(*epoch, Type::NavigationData, header.version.major), sto.system, sto.utc )); diff --git a/rinex/src/navigation/stomessage.rs b/rinex/src/navigation/stomessage.rs index cc20f6126..a75f991d3 100644 --- a/rinex/src/navigation/stomessage.rs +++ b/rinex/src/navigation/stomessage.rs @@ -37,7 +37,7 @@ impl StoMessage { let (epoch, rem) = line.split_at(23); let (system, _) = rem.split_at(5); - let (epoch, _) = epoch::parse_in_timescale(epoch.trim(), ts)?; + let epoch = epoch::parse_in_timescale(epoch.trim(), ts)?; let line = match lines.next() { Some(l) => l, diff --git a/rinex/src/epoch/flag.rs b/rinex/src/observation/flag.rs similarity index 100% rename from rinex/src/epoch/flag.rs rename to rinex/src/observation/flag.rs diff --git a/rinex/src/observation/mod.rs b/rinex/src/observation/mod.rs index c4a551d21..8a98e998e 100644 --- a/rinex/src/observation/mod.rs +++ b/rinex/src/observation/mod.rs @@ -4,6 +4,9 @@ use std::collections::HashMap; pub mod record; +pub mod flag; +pub use flag::EpochFlag; + mod snr; pub use snr::SNR; diff --git a/rinex/src/observation/record.rs b/rinex/src/observation/record.rs index 2fe3c9331..666349bd1 100644 --- a/rinex/src/observation/record.rs +++ b/rinex/src/observation/record.rs @@ -8,11 +8,14 @@ use crate::{ Carrier, Observable, }; +use crate::observation::EpochFlag; use crate::observation::SNR; use hifitime::Duration; #[derive(Error, Debug)] pub enum Error { + #[error("failed to parse epoch flag")] + EpochFlag(#[from] crate::observation::flag::Error), #[error("failed to parse epoch")] EpochError(#[from] epoch::ParsingError), #[error("constellation parsing error")] @@ -141,11 +144,28 @@ pub(crate) fn is_new_epoch(line: &str, v: Version) -> bool { if line.len() < 30 { false } else { - epoch::parse_utc(&line[0..29]).is_ok() + // SPLICE flag handling (still an Observation::flag) + let significant = line[0..26].trim().len() != 0; + let epoch = epoch::parse_utc(&line[0..26]); + let flag = EpochFlag::from_str(&line[26..29].trim()); + if significant { + epoch.is_ok() && flag.is_ok() + } else { + if flag.is_err() { + false + } else { + match flag.unwrap() { + EpochFlag::AntennaBeingMoved + | EpochFlag::NewSiteOccupation + | EpochFlag::HeaderInformationFollows + | EpochFlag::ExternalEvent => true, + _ => false, + } + } + } } } else { - // Modern RINEX - // OBS::V3 behaves like all::V4 + // Modern RINEX has a simple marker, like all V4 modern files match line.chars().next() { Some(c) => { c == '>' // epochs always delimited @@ -193,14 +213,12 @@ pub(crate) fn parse_epoch( line = line.split_at(1).1; } - let (date, rem) = line.split_at(offset + 3); + let (date, rem) = line.split_at(offset); + let epoch = epoch::parse_in_timescale(date, ts)?; + let (flag, rem) = rem.split_at(3); + let flag = EpochFlag::from_str(flag.trim())?; let (n_sat, rem) = rem.split_at(3); let n_sat = n_sat.trim().parse::()?; - let epoch = epoch::parse_in_timescale(date, ts)?; - - // previously identified observables (that we expect) - let obs = header.obs.as_ref().unwrap(); - let observables = &obs.codes; // grab possible clock offset let offs: Option<&str> = match header.version.major < 2 { @@ -242,6 +260,34 @@ pub(crate) fn parse_epoch( false => None, // empty field }; + match flag { + EpochFlag::Ok | EpochFlag::PowerFailure | EpochFlag::CycleSlip => { + parse_normal(header, epoch, flag, n_sat, clock_offset, rem, lines) + }, + _ => parse_event(header, epoch, flag, n_sat, clock_offset, rem, lines), + } +} + +fn parse_normal( + header: &Header, + epoch: Epoch, + flag: EpochFlag, + n_sat: u16, + clock_offset: Option, + rem: &str, + mut lines: std::str::Lines<'_>, +) -> Result< + ( + (Epoch, EpochFlag), + Option, + BTreeMap>, + ), + Error, +> { + // previously identified observables (that we expect) + let obs = header.obs.as_ref().unwrap(); + let observables = &obs.codes; + let data = match header.version.major { 2 => { // grab system descriptions @@ -262,7 +308,30 @@ pub(crate) fn parse_epoch( }, _ => parse_v3(observables, lines), }; - Ok((epoch, clock_offset, data)) + Ok(((epoch, flag), clock_offset, data)) +} + +fn parse_event( + header: &Header, + epoch: Epoch, + flag: EpochFlag, + n_records: u16, + clock_offset: Option, + rem: &str, + mut lines: std::str::Lines<'_>, +) -> Result< + ( + (Epoch, EpochFlag), + Option, + BTreeMap>, + ), + Error, +> { + // TODO: Verify that the number of lines of data + // to read matches the number of records expected + + // TODO: Actually process event data + Err(Error::MissingData) } /* @@ -586,8 +655,9 @@ fn fmt_epoch_v3( let observables = &header.obs.as_ref().unwrap().codes; lines.push_str(&format!( - "> {} {:2}", - epoch::format(epoch, Some(flag), Type::ObservationData, 3), + "> {} {} {:2}", + epoch::format(epoch, Type::ObservationData, 3), + flag, data.len() )); @@ -638,8 +708,9 @@ fn fmt_epoch_v2( let observables = &header.obs.as_ref().unwrap().codes; lines.push_str(&format!( - " {} {:2}", - epoch::format(epoch, Some(flag), Type::ObservationData, 2), + " {} {} {:2}", + epoch::format(epoch, Type::ObservationData, 2), + flag, data.len() )); @@ -1744,6 +1815,124 @@ pub(crate) fn code_multipath( #[cfg(test)] mod test { use super::*; + fn parse_and_format_helper(ver: Version, epoch_str: &str, expected_flag: EpochFlag) { + let first = epoch::parse_utc("2020 01 01 00 00 0.1000000").unwrap(); + let data: BTreeMap> = BTreeMap::new(); + let header = Header::default().with_version(ver).with_observation_fields( + crate::observation::HeaderFields::default().with_time_of_first_obs(first), + ); + let ts = TimeScale::UTC; + let clock_offset: Option = None; + + let e = parse_epoch(&header, epoch_str, ts); + + match expected_flag { + EpochFlag::Ok | EpochFlag::PowerFailure | EpochFlag::CycleSlip => { + assert!(e.is_ok()) + }, + _ => { + // TODO: Update alongside parse_event + assert!(e.is_err()); + return; + }, + } + let ((e, flag), _, _) = e.unwrap(); + assert_eq!(flag, expected_flag); + if ver.major < 3 { + assert_eq!( + fmt_epoch_v2(e, flag, &clock_offset, &data, &header) + .lines() + .next() + .unwrap(), + epoch_str + ); + } else { + assert_eq!( + fmt_epoch_v3(e, flag, &clock_offset, &data, &header) + .lines() + .next() + .unwrap(), + epoch_str + ); + } + } + + #[test] + fn obs_v2_parse_and_format() { + parse_and_format_helper( + Version { major: 2, minor: 0 }, + " 21 12 21 0 0 30.0000000 0 0", + EpochFlag::Ok, + ); + parse_and_format_helper( + Version { major: 2, minor: 0 }, + " 21 12 21 0 0 30.0000000 1 0", + EpochFlag::PowerFailure, + ); + parse_and_format_helper( + Version { major: 2, minor: 0 }, + " 21 12 21 0 0 30.0000000 2 0", + EpochFlag::AntennaBeingMoved, + ); + parse_and_format_helper( + Version { major: 2, minor: 0 }, + " 21 12 21 0 0 30.0000000 3 0", + EpochFlag::NewSiteOccupation, + ); + parse_and_format_helper( + Version { major: 2, minor: 0 }, + " 21 12 21 0 0 30.0000000 4 0", + EpochFlag::HeaderInformationFollows, + ); + parse_and_format_helper( + Version { major: 2, minor: 0 }, + " 21 12 21 0 0 30.0000000 5 0", + EpochFlag::ExternalEvent, + ); + parse_and_format_helper( + Version { major: 2, minor: 0 }, + " 21 12 21 0 0 30.0000000 6 0", + EpochFlag::CycleSlip, + ); + } + #[test] + fn obs_v3_parse_and_format() { + parse_and_format_helper( + Version { major: 3, minor: 0 }, + "> 2021 12 21 00 00 30.0000000 0 0", + EpochFlag::Ok, + ); + parse_and_format_helper( + Version { major: 3, minor: 0 }, + "> 2021 12 21 00 00 30.0000000 1 0", + EpochFlag::PowerFailure, + ); + parse_and_format_helper( + Version { major: 3, minor: 0 }, + "> 2021 12 21 00 00 30.0000000 2 0", + EpochFlag::AntennaBeingMoved, + ); + parse_and_format_helper( + Version { major: 3, minor: 0 }, + "> 2021 12 21 00 00 30.0000000 3 0", + EpochFlag::NewSiteOccupation, + ); + parse_and_format_helper( + Version { major: 3, minor: 0 }, + "> 2021 12 21 00 00 30.0000000 4 0", + EpochFlag::HeaderInformationFollows, + ); + parse_and_format_helper( + Version { major: 3, minor: 0 }, + "> 2021 12 21 00 00 30.0000000 5 0", + EpochFlag::ExternalEvent, + ); + parse_and_format_helper( + Version { major: 3, minor: 0 }, + "> 2021 12 21 00 00 30.0000000 6 0", + EpochFlag::CycleSlip, + ); + } #[test] fn obs_record_is_new_epoch() { assert!(is_new_epoch(