lox_orbits/
propagators.rs1use lox_bodies::Origin;
6use lox_frames::ReferenceFrame;
7use lox_time::Time;
8use lox_time::intervals::TimeInterval;
9use lox_time::time_scales::{DynTimeScale, TimeScale};
10
11use crate::orbits::{CartesianOrbit, DynTrajectory, TrajectorError, Trajectory};
12
13use self::numerical::{DynJ2Propagator, J2Error};
14use self::semi_analytical::{DynVallado, ValladoError};
15use self::sgp4::{Sgp4, Sgp4Error};
16
17pub mod numerical;
19pub mod semi_analytical;
21pub mod sgp4;
23mod stumpff;
24
25pub trait Propagator<T, O>
27where
28 T: TimeScale + Copy,
29 O: Origin + Copy,
30{
31 type Frame: ReferenceFrame + Copy;
33 type Error: std::error::Error + 'static;
35
36 fn state_at(&self, time: Time<T>) -> Result<CartesianOrbit<T, O, Self::Frame>, Self::Error>;
38
39 fn propagate(
42 &self,
43 interval: TimeInterval<T>,
44 ) -> Result<Trajectory<T, O, Self::Frame>, Self::Error>;
45
46 fn propagate_to(
48 &self,
49 times: impl IntoIterator<Item = Time<T>>,
50 ) -> Result<Trajectory<T, O, Self::Frame>, Self::Error>
51 where
52 Self::Error: From<TrajectorError>,
53 {
54 let states: Result<Vec<_>, _> = times.into_iter().map(|t| self.state_at(t)).collect();
55 Ok(Trajectory::try_new(states?)?)
56 }
57}
58
59#[derive(Debug, Clone)]
65pub enum OrbitSource {
66 Sgp4(Sgp4),
68 Vallado(DynVallado),
70 J2(DynJ2Propagator),
72 Trajectory(DynTrajectory),
74}
75
76#[derive(Debug, thiserror::Error)]
78pub enum PropagateError {
79 #[error(transparent)]
81 Sgp4(#[from] Sgp4Error),
82 #[error(transparent)]
84 Vallado(#[from] ValladoError),
85 #[error(transparent)]
87 J2(#[from] J2Error),
88}
89
90impl OrbitSource {
91 pub fn propagate(
94 &self,
95 interval: TimeInterval<DynTimeScale>,
96 ) -> Result<DynTrajectory, PropagateError> {
97 match self {
98 Self::Sgp4(sgp4) => {
99 let tai_interval = TimeInterval::new(
100 interval.start().to_scale(lox_time::time_scales::Tai),
101 interval.end().to_scale(lox_time::time_scales::Tai),
102 );
103 let traj = Propagator::propagate(sgp4, tai_interval)?;
104 Ok(traj.into_dyn())
105 }
106 Self::Vallado(v) => Ok(Propagator::propagate(v, interval)?),
107 Self::J2(j2) => Ok(Propagator::propagate(j2, interval)?),
108 Self::Trajectory(t) => Ok(t.clone()),
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use lox_bodies::DynOrigin;
117 use lox_frames::DynFrame;
118 use lox_time::time_scales::DynTimeScale;
119
120 fn make_trajectory() -> DynTrajectory {
121 DynTrajectory::from_csv_dyn(
122 &lox_test_utils::read_data_file("trajectory_lunar.csv"),
123 DynOrigin::Earth,
124 DynFrame::Icrf,
125 )
126 .unwrap()
127 }
128
129 #[test]
130 fn test_orbit_source_trajectory_propagate() {
131 let traj = make_trajectory();
132 let interval = TimeInterval::new(
133 traj.start_time().to_scale(DynTimeScale::Tai),
134 traj.end_time().to_scale(DynTimeScale::Tai),
135 );
136 let source = OrbitSource::Trajectory(traj.clone());
137 let result = source.propagate(interval).unwrap();
138 assert_eq!(result.states().len(), traj.states().len());
139 }
140}