use chrono::{DateTime, NaiveDateTime, Utc};
use crate::utilities::{take_expecting, take_or_empty};
#[derive(Debug, PartialEq)]
pub struct EphemerisVectorItem {
pub time: DateTime<Utc>,
pub position: [f32; 3],
pub velocity: [f32; 3],
}
#[derive(Debug, PartialEq)]
pub struct EphemerisOrbitalElementsItem {
pub time: DateTime<Utc>,
pub eccentricity: f32,
pub periapsis_distance: f32,
pub inclination: f32,
pub longitude_of_ascending_node: f32,
pub argument_of_perifocus: f32,
pub time_of_periapsis: f32,
pub mean_motion: f32,
pub mean_anomaly: f32,
pub true_anomaly: f32,
pub semi_major_axis: f32,
pub apoapsis_distance: f32,
pub siderral_orbit_period: f32,
}
enum EphemerisVectorParserState {
WaitingForSoe,
WaitingForDate,
Date(DateTime<Utc>),
Position {
time: DateTime<Utc>,
position: [f32; 3],
},
Complete {
time: DateTime<Utc>,
position: [f32; 3],
velocity: [f32; 3],
},
End,
}
enum EphemerisOrbitalElementsParserState {
WaitingForSoe,
WaitingForDate,
Date(DateTime<Utc>),
FirstRow {
time: DateTime<Utc>,
eccentricity: f32,
periapsis_distance: f32,
inclination: f32,
},
SecondRow {
time: DateTime<Utc>,
eccentricity: f32,
periapsis_distance: f32,
inclination: f32,
longitude_of_ascending_node: f32,
argument_of_perifocus: f32,
time_of_periapsis: f32,
},
ThirdRow {
time: DateTime<Utc>,
eccentricity: f32,
periapsis_distance: f32,
inclination: f32,
longitude_of_ascending_node: f32,
argument_of_perifocus: f32,
time_of_periapsis: f32,
mean_motion: f32,
mean_anomaly: f32,
true_anomaly: f32,
},
End,
}
pub struct EphemerisVectorParser<'a, Input: Iterator<Item = &'a str>> {
state: EphemerisVectorParserState,
input: Input,
}
pub struct EphemerisOrbitalElementsParser<'a, Input: Iterator<Item = &'a str>> {
state: EphemerisOrbitalElementsParserState,
input: Input,
}
impl<'a, Input: Iterator<Item = &'a str>> EphemerisVectorParser<'a, Input> {
pub fn parse(input: Input) -> Self {
Self {
state: EphemerisVectorParserState::WaitingForSoe,
input,
}
}
}
impl<'a, Input: Iterator<Item = &'a str>> EphemerisOrbitalElementsParser<'a, Input> {
pub fn parse(input: Input) -> Self {
Self {
state: EphemerisOrbitalElementsParserState::WaitingForSoe,
input,
}
}
}
impl<'a, Input: Iterator<Item = &'a str>> Iterator for EphemerisVectorParser<'a, Input> {
type Item = EphemerisVectorItem;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(line) = self.input.next() {
match self.state {
EphemerisVectorParserState::WaitingForSoe => {
if line == "$$SOE" {
self.state = EphemerisVectorParserState::WaitingForDate;
}
}
EphemerisVectorParserState::WaitingForDate => {
if line == "$$EOE" {
self.state = EphemerisVectorParserState::End;
} else {
let time = parse_date_time(line);
self.state = EphemerisVectorParserState::Date(time);
}
}
EphemerisVectorParserState::Date(time) => {
let line = take_expecting(line, " X =").unwrap();
let (x, line) = take_or_empty(line, 22);
let line = take_expecting(line, " Y =").unwrap();
let (y, line) = take_or_empty(line, 22);
let line = take_expecting(line, " Z =").unwrap();
let (z, _) = take_or_empty(line, 22);
self.state = EphemerisVectorParserState::Position {
time,
position: [
x.trim().parse::<f32>().unwrap(),
y.trim().parse::<f32>().unwrap(),
z.trim().parse::<f32>().unwrap(),
],
};
}
EphemerisVectorParserState::Position { time, position } => {
let line = take_expecting(line, " VX=").unwrap();
let (vx, line) = take_or_empty(line, 22);
let line = take_expecting(line, " VY=").unwrap();
let (vy, line) = take_or_empty(line, 22);
let line = take_expecting(line, " VZ=").unwrap();
let (vz, _) = take_or_empty(line, 22);
self.state = EphemerisVectorParserState::Complete {
time,
position,
velocity: [
vx.trim().parse::<f32>().unwrap(),
vy.trim().parse::<f32>().unwrap(),
vz.trim().parse::<f32>().unwrap(),
],
};
}
EphemerisVectorParserState::Complete {
time,
position,
velocity,
} => {
self.state = EphemerisVectorParserState::WaitingForDate;
return Some(EphemerisVectorItem {
time,
position,
velocity,
});
}
EphemerisVectorParserState::End => {
return None;
}
}
} else {
return None;
}
}
}
}
impl<'a, Input: Iterator<Item = &'a str>> Iterator for EphemerisOrbitalElementsParser<'a, Input> {
type Item = EphemerisOrbitalElementsItem;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(line) = self.input.next() {
match self.state {
EphemerisOrbitalElementsParserState::WaitingForSoe => {
if line == "$$SOE" {
self.state = EphemerisOrbitalElementsParserState::WaitingForDate;
}
}
EphemerisOrbitalElementsParserState::WaitingForDate => {
if line == "$$EOE" {
self.state = EphemerisOrbitalElementsParserState::End;
} else {
let time = parse_date_time(line);
self.state = EphemerisOrbitalElementsParserState::Date(time);
}
}
EphemerisOrbitalElementsParserState::Date(time) => {
let line = take_expecting(line, " EC=").unwrap();
let (eccentricity, line) = take_or_empty(line, 22);
let line = take_expecting(line, " QR=").unwrap();
let (periapsis_distance, line) = take_or_empty(line, 22);
let line = take_expecting(line, " IN=").unwrap();
let (inclination, _) = take_or_empty(line, 22);
self.state = EphemerisOrbitalElementsParserState::FirstRow {
time,
eccentricity: eccentricity.trim().parse::<f32>().unwrap(),
periapsis_distance: periapsis_distance.trim().parse::<f32>().unwrap(),
inclination: inclination.trim().parse::<f32>().unwrap(),
};
}
EphemerisOrbitalElementsParserState::FirstRow {
time,
eccentricity,
periapsis_distance,
inclination,
} => {
let line = take_expecting(line, " OM=").unwrap();
let (longitude_of_ascending_node, line) = take_or_empty(line, 22);
let line = take_expecting(line, " W =").unwrap();
let (argument_of_perifocus, line) = take_or_empty(line, 22);
let line = take_expecting(line, " Tp=").unwrap();
let (time_of_periapsis, _) = take_or_empty(line, 22);
self.state = EphemerisOrbitalElementsParserState::SecondRow {
time,
eccentricity,
periapsis_distance,
inclination,
longitude_of_ascending_node: longitude_of_ascending_node
.trim()
.parse::<f32>()
.unwrap(),
argument_of_perifocus: argument_of_perifocus
.trim()
.parse::<f32>()
.unwrap(),
time_of_periapsis: time_of_periapsis.trim().parse::<f32>().unwrap(),
};
}
EphemerisOrbitalElementsParserState::SecondRow {
time,
eccentricity,
periapsis_distance,
inclination,
longitude_of_ascending_node,
argument_of_perifocus,
time_of_periapsis,
} => {
let line = take_expecting(line, " N =").unwrap();
let (mean_motion, line) = take_or_empty(line, 22);
let line = take_expecting(line, " MA=").unwrap();
let (mean_anomaly, line) = take_or_empty(line, 22);
let line = take_expecting(line, " TA=").unwrap();
let (true_anomaly, _) = take_or_empty(line, 22);
self.state = EphemerisOrbitalElementsParserState::ThirdRow {
time,
eccentricity,
periapsis_distance,
inclination,
longitude_of_ascending_node,
argument_of_perifocus,
time_of_periapsis,
mean_motion: mean_motion.trim().parse::<f32>().unwrap(),
mean_anomaly: mean_anomaly.trim().parse::<f32>().unwrap(),
true_anomaly: true_anomaly.trim().parse::<f32>().unwrap(),
};
}
EphemerisOrbitalElementsParserState::ThirdRow {
time,
eccentricity,
periapsis_distance,
inclination,
longitude_of_ascending_node,
argument_of_perifocus,
time_of_periapsis,
mean_motion,
mean_anomaly,
true_anomaly,
} => {
let line = take_expecting(line, " A =").unwrap();
let (semi_major_axis, line) = take_or_empty(line, 22);
let line = take_expecting(line, " AD=").unwrap();
let (apoapsis_distance, line) = take_or_empty(line, 22);
let line = take_expecting(line, " PR=").unwrap();
let (siderral_orbit_period, _) = take_or_empty(line, 22);
self.state = EphemerisOrbitalElementsParserState::WaitingForDate;
return Some(EphemerisOrbitalElementsItem {
time,
eccentricity,
periapsis_distance,
inclination,
longitude_of_ascending_node,
argument_of_perifocus,
time_of_periapsis,
mean_motion,
mean_anomaly,
true_anomaly,
semi_major_axis: semi_major_axis.trim().parse::<f32>().unwrap(),
apoapsis_distance: apoapsis_distance.trim().parse::<f32>().unwrap(),
siderral_orbit_period: siderral_orbit_period
.trim()
.parse::<f32>()
.unwrap(),
});
}
EphemerisOrbitalElementsParserState::End => {
return None;
}
}
} else {
return None;
}
}
}
}
fn parse_date_time(line: &str) -> DateTime<Utc> {
let date_time_str: &str = line.split_terminator('=').collect::<Vec<_>>()[1].trim();
let date_time_str = take_expecting(date_time_str, "A.D. ").unwrap();
let (time, _) = take_or_empty(date_time_str, 20);
NaiveDateTime::parse_from_str(time, "%Y-%b-%d %H:%M:%S")
.unwrap()
.and_utc()
}
#[cfg(test)]
mod tests {
use chrono::TimeZone;
use super::*;
#[test]
fn test_parsing_ephemeris_vector() {
let data = include_str!("vector.txt");
let ephem: Vec<_> = EphemerisVectorParser::parse(data.lines()).collect();
assert_eq!(4, ephem.len());
assert_eq!(
EphemerisVectorItem {
time: Utc.with_ymd_and_hms(2022, 8, 13, 19, 55, 56).unwrap(), position: [
1.870010427985840E+02,
2.484687803242536E+03,
-5.861602653492581E+03
],
velocity: [
-3.362664133558439E-01,
1.344100266143978E-02,
-5.030275220358716E-03
]
},
ephem[0]
);
}
#[test]
fn test_parsing_ephemeris_orbital_elements() {
let data = include_str!("orbital_elements.txt");
let ephem: Vec<_> = EphemerisOrbitalElementsParser::parse(data.lines()).collect();
assert_eq!(4, ephem.len());
assert_eq!(
EphemerisOrbitalElementsItem {
time: Utc.with_ymd_and_hms(2022, 6, 19, 18, 0, 0).unwrap(),
eccentricity: 1.711794334680415E-02,
periapsis_distance: 1.469885520304013E+08,
inclination: 3.134746902320420E-03,
longitude_of_ascending_node: 1.633896137466430E+02,
argument_of_perifocus: 3.006492364709574E+02,
time_of_periapsis: 2459584.392523936927,
mean_motion: 1.141316101270797E-05,
mean_anomaly: 1.635515780663357E+02,
true_anomaly: 1.640958153023696E+02,
semi_major_axis: 1.495485150384278E+08,
apoapsis_distance: 1.521084780464543E+08,
siderral_orbit_period: 3.154253230977451E+07,
},
ephem[0]
);
}
#[test]
fn test_parsing_date_time() {
let lines: [&str; 4] = [
"2459750.250000000 = A.D. 2022-Jun-19 18:00:00.0000 TDB ", "2459750.375000000 = A.D. 2022-Jun-19 21:00:00.0000 TDB ",
"2459805.372175926 = A.D. 2022-Aug-13 20:55:56.0000 TDB ", "2459805.455509259 = A.D. 2022-Aug-13 22:55:56.0000 TDB ",
];
let expected: [DateTime<Utc>; 4] = [
Utc.with_ymd_and_hms(2022, 6, 19, 18, 0, 0).unwrap(),
Utc.with_ymd_and_hms(2022, 6, 19, 21, 0, 0).unwrap(),
Utc.with_ymd_and_hms(2022, 8, 13, 20, 55, 56).unwrap(),
Utc.with_ymd_and_hms(2022, 8, 13, 22, 55, 56).unwrap(),
];
for (i, line) in lines.into_iter().enumerate() {
let time = parse_date_time(line);
assert_eq!(time, expected[i]);
}
}
}