use crate::{
epoch::parse_in_timescale as parse_epoch_in_timescale,
error::FormattingError,
fmt_rinex,
navigation::formatting::NavFormatter,
parse_f64,
prelude::{Constellation, Epoch, ParsingError, TimeScale},
};
use bitflags::bitflags;
use std::io::{BufWriter, Write};
bitflags! {
#[derive(Debug, Default, Clone, Copy)]
#[derive(PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct NgRegionFlags: u16 {
const REGION5 = 0x01;
const REGION4 = 0x02;
const REGION3 = 0x04;
const REGION2 = 0x08;
const REGION1 = 0x10;
}
}
#[derive(Debug, Clone, Default, Copy, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct NgModel {
pub a: (f64, f64, f64),
pub region: NgRegionFlags,
}
impl NgModel {
pub(crate) fn parse(
mut lines: std::str::Lines<'_>,
ts: TimeScale,
) -> Result<(Epoch, Self), ParsingError> {
let line = match lines.next() {
Some(l) => l,
_ => return Err(ParsingError::EmptyEpoch),
};
let (epoch, rem) = line.split_at(23);
let (a0, rem) = rem.split_at(19);
let (a1, rem) = rem.split_at(19);
let line = match lines.next() {
Some(l) => l,
_ => return Err(ParsingError::EmptyEpoch),
};
let epoch = parse_epoch_in_timescale(epoch.trim(), ts)?;
let a = (
parse_f64(a0.trim()).map_err(|_| ParsingError::NequickGData)?,
parse_f64(a1.trim()).map_err(|_| ParsingError::NequickGData)?,
parse_f64(rem.trim()).map_err(|_| ParsingError::NequickGData)?,
);
let f = parse_f64(line.trim()).map_err(|_| ParsingError::NequickGData)?;
Ok((
epoch,
Self {
a,
region: NgRegionFlags::from_bits(f as u16).unwrap_or(NgRegionFlags::empty()),
},
))
}
pub fn format_header<W: Write>(
&self,
w: &mut BufWriter<W>,
constellation: &Constellation,
) -> Result<(), FormattingError> {
let formatted = format!(
"{:E} {} {} {} {}",
constellation,
NavFormatter::new_iono_alpha_beta(self.a.0),
NavFormatter::new_iono_alpha_beta(self.a.1),
NavFormatter::new_iono_alpha_beta(self.a.2),
NavFormatter::new_iono_alpha_beta(self.region.bits() as f64),
);
write!(w, "{}\n", fmt_rinex(&formatted, "IONOSPHERIC CORR"))?;
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::navigation::IonosphereModel;
use super::*;
#[test]
fn test_nequick_g_model() {
let content =
" 2022 06 08 09 59 57 7.850000000000E+01 5.390625000000E-01 2.713012695312E-02
0.000000000000E+00";
let content = content.lines();
let parsed = NgModel::parse(content, TimeScale::UTC);
assert!(parsed.is_ok());
let (epoch, message) = parsed.unwrap();
assert_eq!(
epoch,
Epoch::from_gregorian_utc(2022, 06, 08, 09, 59, 57, 00)
);
assert_eq!(
message,
NgModel {
a: (7.850000000000E+01, 5.390625000000E-01, 2.713012695312E-02),
region: NgRegionFlags::empty(),
},
);
}
#[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(),
})
);
}
}