nyx_space/tools/lambert/
mod.rs1use crate::cosmic::AstroError;
20use crate::errors::LambertError;
21use crate::linalg::Vector3;
22use std::f64::consts::PI;
23
24mod godding;
25mod izzo;
26
27use anise::errors::PhysicsError;
28use anise::prelude::Orbit;
29pub use godding::gooding;
30pub use izzo::izzo;
31
32const TAU: f64 = 2.0 * PI;
33const LAMBERT_EPSILON: f64 = 1e-4; const LAMBERT_EPSILON_TIME: f64 = 1e-4; const LAMBERT_EPSILON_RAD: f64 = (5e-5 / 180.0) * PI; const MAX_ITERATIONS: usize = 1000;
39
40pub enum TransferKind {
42 Auto,
43 ShortWay,
44 LongWay,
45 NRevs(u8),
46}
47
48impl TransferKind {
49 fn direction_of_motion(
60 self,
61 r_final: &Vector3<f64>,
62 r_init: &Vector3<f64>,
63 ) -> Result<f64, LambertError> {
64 match self {
65 TransferKind::Auto => {
66 let mut dnu = r_final[1].atan2(r_final[0]) - r_init[1].atan2(r_final[1]);
67 if dnu > TAU {
68 dnu -= TAU;
69 } else if dnu < 0.0 {
70 dnu += TAU;
71 }
72
73 if dnu > std::f64::consts::PI {
74 Ok(-1.0)
75 } else {
76 Ok(1.0)
77 }
78 }
79 TransferKind::ShortWay => Ok(1.0),
80 TransferKind::LongWay => Ok(-1.0),
81 _ => Err(LambertError::MultiRevNotSupported),
82 }
83 }
84}
85
86#[derive(Copy, Clone, Debug)]
87pub struct LambertInput {
88 pub initial_state: Orbit,
89 pub final_state: Orbit,
90}
91
92impl LambertInput {
93 pub fn from_planetary_states(
94 initial_state: Orbit,
95 final_state: Orbit,
96 ) -> Result<Self, AstroError> {
97 if final_state.frame != initial_state.frame {
98 return Err(AstroError::AstroPhysics {
99 source: PhysicsError::FrameMismatch {
100 action: "Lambert solver requires both states to be in the same frame",
101 frame1: final_state.frame.into(),
102 frame2: initial_state.frame.into(),
103 },
104 });
105 }
106 initial_state
108 .frame
109 .mu_km3_s2()
110 .map_err(|e| AstroError::AstroPhysics { source: e })?;
111
112 Ok(Self {
113 initial_state,
114 final_state,
115 })
116 }
117
118 pub fn mu_km2_s3(&self) -> f64 {
120 self.initial_state.frame.mu_km3_s2().unwrap()
121 }
122}
123
124#[derive(Copy, Clone, Debug)]
126pub struct LambertSolution {
127 pub v_init_km_s: Vector3<f64>,
128 pub v_final_km_s: Vector3<f64>,
129 pub phi_rad: f64,
131 pub input: LambertInput,
132}
133
134impl LambertSolution {
135 pub fn v_inf_depart_km_s(&self) -> Vector3<f64> {
137 self.input.initial_state.velocity_km_s - self.v_init_km_s
138 }
139
140 pub fn v_inf_arrive_km_s(&self) -> Vector3<f64> {
142 self.input.final_state.velocity_km_s - self.v_final_km_s
143 }
144
145 pub fn transfer_orbit(mut self) -> Orbit {
147 self.input.initial_state.velocity_km_s = self.v_init_km_s;
148 self.input.initial_state
149 }
150
151 pub fn arrival_orbit(mut self) -> Orbit {
153 self.input.final_state.velocity_km_s = self.v_final_km_s;
154 self.input.final_state
155 }
156
157 pub fn v_inf_depart_declination_deg(&self) -> f64 {
159 let v_inf_km_s = self.v_inf_depart_km_s();
160 (v_inf_km_s.z / v_inf_km_s.norm()).asin().to_degrees()
161 }
162
163 pub fn c3_km2_s2(&self) -> f64 {
165 self.v_inf_depart_km_s().norm_squared()
166 }
167}