gooding_lambert/lib.rs
1//! # gooding-lambert
2//!
3//! Gooding's method for solving Lambert's orbital boundary-value problem.
4//!
5//! Given two position vectors and a time of flight, computes the velocity
6//! vectors at each endpoint of the connecting Keplerian arc. Handles all
7//! conic sections (elliptic, parabolic, hyperbolic), all transfer angles,
8//! and multi-revolution solutions.
9//!
10//! ## Quick Start
11//!
12//! ```
13//! use gooding_lambert::{lambert, Direction, MultiRevPeriod};
14//!
15//! let mu = 398600.4418_f64; // Earth GM (km³/s²)
16//! let r1 = [6678.0, 0.0, 0.0]; // km
17//! let r2 = [0.0, 42164.0, 0.0]; // km (GEO)
18//! let tof = 5.0 * 3600.0; // seconds
19//!
20//! let sol = lambert(mu, r1, r2, tof, 0, Direction::Prograde, MultiRevPeriod::LongPeriod).unwrap();
21//! let speed = (sol.v1[0].powi(2) + sol.v1[1].powi(2) + sol.v1[2].powi(2)).sqrt();
22//! assert!(speed > 5.0 && speed < 15.0); // km/s at LEO departure
23//! ```
24//!
25//! ## References
26//!
27//! Gooding, R. H. (1990). "A procedure for the solution of Lambert's orbital
28//! boundary-value problem." *Celestial Mechanics and Dynamical Astronomy*, 48(2), 145–165.
29
30mod gooding;
31
32pub use gooding::gooding_lambert;
33pub use gooding::lambert;
34pub use gooding::MultiRevPeriod;
35
36/// Transfer direction for Lambert's problem.
37///
38/// Prograde means the transfer orbit has positive angular momentum aligned
39/// with `r1 × r2`. Retrograde uses the supplementary transfer angle
40/// (> 180°), with angular momentum aligned with `r2 × r1`.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum Direction {
43 /// Transfer angle ∈ (0°, 180°); angular momentum parallel to r1 × r2.
44 Prograde,
45 /// Transfer angle ∈ (180°, 360°); angular momentum parallel to r2 × r1.
46 Retrograde,
47}
48
49/// Velocity solution returned by [`lambert`].
50#[derive(Debug, Clone, PartialEq)]
51pub struct LambertSolution {
52 /// Velocity vector at the departure point (same units as position / time).
53 pub v1: [f64; 3],
54 /// Velocity vector at the arrival point (same units as position / time).
55 pub v2: [f64; 3],
56}
57
58/// Errors returned by [`lambert`].
59#[derive(Debug, Clone, PartialEq)]
60pub enum LambertError {
61 /// Transfer angle is exactly 180° — transfer plane undefined.
62 SingularTransfer,
63 /// No solution exists for the given revolution count and time of flight
64 /// (TOF is below the minimum time for the requested revolution count).
65 NoSolution,
66 /// Householder iteration failed to converge within the iteration limit.
67 ConvergenceFailed,
68 /// One or more inputs are invalid (zero radius, non-positive TOF, etc.).
69 InvalidInput(&'static str),
70}
71
72impl std::fmt::Display for LambertError {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 LambertError::SingularTransfer => {
76 write!(f, "transfer angle is exactly 180°; transfer plane is undefined")
77 }
78 LambertError::NoSolution => {
79 write!(
80 f,
81 "no solution exists for the given revolution count and time of flight"
82 )
83 }
84 LambertError::ConvergenceFailed => {
85 write!(
86 f,
87 "Householder iteration failed to converge within the iteration limit"
88 )
89 }
90 LambertError::InvalidInput(reason) => {
91 write!(f, "invalid input: {reason}")
92 }
93 }
94 }
95}
96
97impl std::error::Error for LambertError {}