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