nyx_space/md/trajectory/
interpolatable.rs1use anise::analysis::prelude::OrbitalElement;
20use anise::math::interpolation::{hermite_eval, InterpolationError};
21
22pub(crate) const INTERPOLATION_SAMPLES: usize = 13;
23
24use super::StateParameter;
25use crate::cosmic::Frame;
26use crate::linalg::allocator::Allocator;
27use crate::linalg::DefaultAllocator;
28use crate::time::Epoch;
29use crate::{Orbit, Spacecraft, State};
30
31pub trait Interpolatable: State
33where
34 Self: Sized,
35 DefaultAllocator:
36 Allocator<Self::Size> + Allocator<Self::Size, Self::Size> + Allocator<Self::VecLength>,
37{
38 fn interpolate(self, epoch: Epoch, states: &[Self]) -> Result<Self, InterpolationError>;
40
41 fn frame(&self) -> Frame;
43
44 fn set_frame(&mut self, frame: Frame);
46
47 fn export_params() -> Vec<StateParameter>;
49}
50
51impl Interpolatable for Spacecraft {
52 fn interpolate(mut self, epoch: Epoch, states: &[Self]) -> Result<Self, InterpolationError> {
53 let mut epochs_tdb = [0.0; INTERPOLATION_SAMPLES];
56 let mut xs = [0.0; INTERPOLATION_SAMPLES];
57 let mut ys = [0.0; INTERPOLATION_SAMPLES];
58 let mut zs = [0.0; INTERPOLATION_SAMPLES];
59 let mut vxs = [0.0; INTERPOLATION_SAMPLES];
60 let mut vys = [0.0; INTERPOLATION_SAMPLES];
61 let mut vzs = [0.0; INTERPOLATION_SAMPLES];
62
63 for (cno, state) in states.iter().enumerate() {
64 xs[cno] = state.orbit.radius_km.x;
65 ys[cno] = state.orbit.radius_km.y;
66 zs[cno] = state.orbit.radius_km.z;
67 vxs[cno] = state.orbit.velocity_km_s.x;
68 vys[cno] = state.orbit.velocity_km_s.y;
69 vzs[cno] = state.orbit.velocity_km_s.z;
70 epochs_tdb[cno] = state.epoch().to_et_seconds();
71 }
72
73 let n = states.len();
75
76 let (x_km, vx_km_s) =
77 hermite_eval(&epochs_tdb[..n], &xs[..n], &vxs[..n], epoch.to_et_seconds())?;
78
79 let (y_km, vy_km_s) =
80 hermite_eval(&epochs_tdb[..n], &ys[..n], &vys[..n], epoch.to_et_seconds())?;
81
82 let (z_km, vz_km_s) =
83 hermite_eval(&epochs_tdb[..n], &zs[..n], &vzs[..n], epoch.to_et_seconds())?;
84
85 self.orbit = Orbit::new(
86 x_km,
87 y_km,
88 z_km,
89 vx_km_s,
90 vy_km_s,
91 vz_km_s,
92 epoch,
93 self.orbit.frame,
94 );
95
96 let first = states.first().unwrap();
98 let last = states.last().unwrap();
99 let prop_kg_dt = (last.mass.prop_mass_kg - first.mass.prop_mass_kg)
100 / (last.epoch() - first.epoch()).to_seconds();
101
102 self.mass.prop_mass_kg += prop_kg_dt * (epoch - first.epoch()).to_seconds();
103
104 Ok(self)
105 }
106
107 fn frame(&self) -> Frame {
108 self.orbit.frame
109 }
110
111 fn set_frame(&mut self, frame: Frame) {
112 self.orbit.frame = frame;
113 }
114
115 fn export_params() -> Vec<StateParameter> {
116 vec![
117 StateParameter::Element(OrbitalElement::X),
118 StateParameter::Element(OrbitalElement::Y),
119 StateParameter::Element(OrbitalElement::Z),
120 StateParameter::Element(OrbitalElement::VX),
121 StateParameter::Element(OrbitalElement::VY),
122 StateParameter::Element(OrbitalElement::VZ),
123 StateParameter::Element(OrbitalElement::SemiMajorAxis),
124 StateParameter::Element(OrbitalElement::Eccentricity),
125 StateParameter::Element(OrbitalElement::Inclination),
126 StateParameter::Element(OrbitalElement::RAAN),
127 StateParameter::Element(OrbitalElement::AoP),
128 StateParameter::Element(OrbitalElement::TrueAnomaly),
129 StateParameter::Element(OrbitalElement::AoL),
130 StateParameter::Element(OrbitalElement::TrueLongitude),
131 StateParameter::DryMass,
132 StateParameter::PropMass,
133 StateParameter::Cr,
134 StateParameter::Cd,
135 StateParameter::Isp,
136 StateParameter::GuidanceMode,
137 StateParameter::Thrust,
138 ]
139 }
140}