use super::eskf::NavFilter;
use deep_causality_algebra::RealField;
use deep_causality_num::FromPrimitive;
use deep_causality_num_complex::Quaternion;
use deep_causality_physics::{
KsPropagator, PhysicsError, ks_strang_step, relativistic_clock_drift_rate_kernel,
};
#[derive(Clone, Debug)]
pub struct ReentryNavEngine<R: RealField + FromPrimitive> {
gm: R,
position: [R; 3],
velocity: [R; 3],
filter: NavFilter<R>,
attitude: Quaternion<R>,
tau_offset: R,
elapsed: R,
}
impl<R: RealField + FromPrimitive> ReentryNavEngine<R> {
pub fn new(position: [R; 3], velocity: [R; 3], gm: R, filter: NavFilter<R>) -> Self {
Self {
gm,
position,
velocity,
filter,
attitude: Quaternion::identity(),
tau_offset: R::zero(),
elapsed: R::zero(),
}
}
pub fn predict(
&mut self,
dt: R,
aero_accel: [R; 3],
angular_rate: [R; 3],
process_noise: [R; 17],
) -> Result<(), PhysicsError> {
let delta = Quaternion::from_axis_angle(angular_rate, norm(angular_rate) * dt);
let attitude = (self.attitude * delta).normalize();
let (r1, v1) = ks_strang_step(self.position, self.velocity, self.gm, dt, |_r, _v| {
aero_accel
})?;
let radius = norm(r1);
let speed = norm(v1);
let rate = relativistic_clock_drift_rate_kernel(radius, speed, self.gm)?;
let f_nav = mat3_vec(&attitude.to_rotation_matrix(), aero_accel);
self.filter.predict(dt, f_nav, process_noise)?;
self.attitude = attitude;
self.position = r1;
self.velocity = v1;
self.tau_offset += rate * dt;
self.elapsed += dt;
Ok(())
}
pub fn correct_position(
&mut self,
measured_position: [R; 3],
r_var: R,
) -> Result<(), PhysicsError> {
let backup = self.filter.clone();
for (i, &m) in measured_position.iter().enumerate() {
let z = m - self.position[i];
let mut h = [R::zero(); 17];
h[i] = R::one();
if let Err(e) = self.filter.update_scalar(h, z, r_var) {
self.filter = backup;
return Err(e);
}
}
let est = *self.filter.state();
let dp = est.position_error();
let dv = est.velocity_error();
let dpsi = est.attitude_error();
self.position = core::array::from_fn(|i| self.position[i] + dp[i]);
self.velocity = core::array::from_fn(|i| self.velocity[i] + dv[i]);
let correction = Quaternion::from_axis_angle(dpsi, norm(dpsi));
self.attitude = (correction * self.attitude).normalize();
self.filter.reset_navigation_error();
Ok(())
}
pub fn position(&self) -> [R; 3] {
self.position
}
pub fn velocity(&self) -> [R; 3] {
self.velocity
}
pub fn carried_clock_offset(&self) -> R {
self.tau_offset
}
pub fn elapsed_time(&self) -> R {
self.elapsed
}
pub fn position_variance(&self) -> R {
self.filter.position_variance()
}
pub fn gm(&self) -> R {
self.gm
}
pub fn restore(
position: [R; 3],
velocity: [R; 3],
gm: R,
filter: NavFilter<R>,
attitude: Quaternion<R>,
tau_offset: R,
elapsed: R,
) -> Self {
Self {
gm,
position,
velocity,
filter,
attitude,
tau_offset,
elapsed,
}
}
pub fn filter(&self) -> &NavFilter<R> {
&self.filter
}
pub fn attitude(&self) -> Quaternion<R> {
self.attitude
}
pub fn is_on_orbit_manifold(&self) -> bool {
KsPropagator::from_state(self.position, self.velocity, self.gm).is_ok()
}
}
fn norm<R: RealField>(v: [R; 3]) -> R {
(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
}
fn mat3_vec<R: RealField>(m: &[[R; 3]; 3], v: [R; 3]) -> [R; 3] {
core::array::from_fn(|i| m[i][0] * v[0] + m[i][1] * v[1] + m[i][2] * v[2])
}