use affn::cartesian::{Position, Velocity};
use affn::centers::ReferenceCenter;
use affn::frames::ReferenceFrame;
use qtty::dynamics::{GravitationalParameter, KmPerSecond};
use qtty::length::Kilometer;
use qtty::Second;
use super::error::LambertError;
use super::izzo::{
solve_lambert as solve_lambert_arr, solve_lambert_n_rev as solve_lambert_n_rev_arr,
LambertBranch, LambertDiagnostics, LambertSolution, NRevBranch,
};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TypedLambertSolution<F: ReferenceFrame> {
pub v1: Velocity<F, KmPerSecond>,
pub v2: Velocity<F, KmPerSecond>,
pub diagnostics: LambertDiagnostics,
}
pub fn lambert<C, F>(
r1: Position<C, F, Kilometer>,
r2: Position<C, F, Kilometer>,
tof: Second,
mu: GravitationalParameter,
branch: LambertBranch,
) -> Result<TypedLambertSolution<F>, LambertError>
where
C: ReferenceCenter<Params = ()>,
F: ReferenceFrame,
{
let LambertSolution {
v1,
v2,
diagnostics,
} = solve_lambert_arr(
position_to_array(&r1),
position_to_array(&r2),
tof.value(),
mu.value(),
branch,
)?;
Ok(TypedLambertSolution {
v1: Velocity::<F, KmPerSecond>::new(v1[0], v1[1], v1[2]),
v2: Velocity::<F, KmPerSecond>::new(v2[0], v2[1], v2[2]),
diagnostics,
})
}
pub fn lambert_n_rev<C, F>(
r1: Position<C, F, Kilometer>,
r2: Position<C, F, Kilometer>,
tof: Second,
mu: GravitationalParameter,
branch: LambertBranch,
revolutions: u32,
side: NRevBranch,
) -> Result<TypedLambertSolution<F>, LambertError>
where
C: ReferenceCenter<Params = ()>,
F: ReferenceFrame,
{
let LambertSolution {
v1,
v2,
diagnostics,
} = solve_lambert_n_rev_arr(
position_to_array(&r1),
position_to_array(&r2),
tof.value(),
mu.value(),
branch,
revolutions,
side,
)?;
Ok(TypedLambertSolution {
v1: Velocity::<F, KmPerSecond>::new(v1[0], v1[1], v1[2]),
v2: Velocity::<F, KmPerSecond>::new(v2[0], v2[1], v2[2]),
diagnostics,
})
}
fn position_to_array<C, F>(p: &Position<C, F, Kilometer>) -> [f64; 3]
where
C: ReferenceCenter,
F: ReferenceFrame,
{
[p.x().value(), p.y().value(), p.z().value()]
}
#[cfg(test)]
mod tests {
use super::*;
use affn::frames::ICRS;
#[test]
fn typed_zero_rev_matches_array_kernel() {
let r1 = Position::<(), ICRS, Kilometer>::new(15945.34, 0.0, 0.0);
let r2 = Position::<(), ICRS, Kilometer>::new(12214.83899, 10249.46731, 0.0);
let tof = Second::new(4_560.0);
let mu = GravitationalParameter::new(398_600.441_8);
let typed = lambert(r1, r2, tof, mu, LambertBranch::Prograde).unwrap();
let raw = solve_lambert_arr(
[15945.34, 0.0, 0.0],
[12214.83899, 10249.46731, 0.0],
4_560.0,
398_600.441_8,
LambertBranch::Prograde,
)
.unwrap();
for i in 0..3 {
assert!((typed.v1.as_array()[i].value() - raw.v1[i]).abs() < 1e-12);
assert!((typed.v2.as_array()[i].value() - raw.v2[i]).abs() < 1e-12);
}
}
#[test]
fn typed_n_rev_propagates_error() {
let r1 = Position::<(), ICRS, Kilometer>::new(15945.34, 0.0, 0.0);
let r2 = Position::<(), ICRS, Kilometer>::new(12214.83899, 10249.46731, 0.0);
let tof = Second::new(4_560.0);
let mu = GravitationalParameter::new(398_600.441_8);
let err = lambert_n_rev(
r1,
r2,
tof,
mu,
LambertBranch::Prograde,
1,
NRevBranch::Left,
)
.unwrap_err();
match err {
LambertError::RevolutionsExceedNMax { requested, .. } => {
assert_eq!(requested, 1);
}
other => panic!("unexpected error: {other:?}"),
}
}
#[test]
fn n_rev_valid_geometry_paths_are_exercised() {
let r1 = Position::<(), ICRS, Kilometer>::new(7000.0, 0.0, 0.0);
let r2 = Position::<(), ICRS, Kilometer>::new(0.0, 7000.0, 0.0);
let tof = Second::new(10800.0);
let mu = GravitationalParameter::new(398600.4418);
let _ = lambert_n_rev(
r1,
r2,
tof,
mu,
LambertBranch::Prograde,
1,
NRevBranch::Left,
);
let _ = lambert_n_rev(
r1,
r2,
tof,
mu,
LambertBranch::Retrograde,
1,
NRevBranch::Right,
);
}
}