Skip to main content

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 {}