1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Errors produced by the `orbitprop` module.
use numeris::ode;
use thiserror::Error;
use crate::Frame;
use crate::Instant;
/// Errors that can occur while configuring or executing orbit propagation.
#[derive(Debug, Error)]
pub enum Error {
// -- propagator-internal errors --------------------------------------
/// Returned when the integrated state matrix has an unexpected
/// number of columns.
#[error("Invalid number of columns: {c}")]
InvalidStateColumns { c: usize },
/// Returned by the dense-output interp helpers when the underlying
/// ODE solution does not carry interpolation data.
#[error("No Dense Output in Solution")]
NoDenseOutputInSolution,
/// Wraps an [`ode::OdeError`] surfaced by the chosen integrator.
/// `OdeError` does not implement `std::error::Error` (numeris keeps
/// it as a plain `Display`-only enum), so this variant is built
/// manually rather than via `#[from]`.
#[error("ODE Error: {0}")]
OdeError(ode::OdeError),
/// RODAS4 does not support state transition matrix propagation
/// (`C == 7`).
#[error("RODAS4 does not support state transition matrix propagation")]
RODAS4NoSTM,
/// Gauss-Jackson 8 does not support state transition matrix
/// propagation (`C == 7`).
#[error("Gauss-Jackson 8 does not support state transition matrix propagation")]
GaussJackson8NoSTM,
// -- precomputed.rs --------------------------------------------------
/// Returned by the [`Precomputed`](crate::orbitprop::Precomputed)
/// constructors when the interpolation step is zero, negative, or
/// non-finite (a zero step would otherwise attempt a `usize::MAX`
/// allocation; a negative one builds a table covering the wrong range).
#[error("Precomputed interpolation step must be finite and > 0, got {step}")]
InvalidPrecomputeStep { step: f64 },
/// Returned by [`Precomputed::interp`](crate::orbitprop::Precomputed::interp)
/// when the requested time falls outside the precomputed range.
#[error("Precomputed::interp: time {time} is outside of precomputed range : {begin} to {end}")]
PrecomputedOutOfRange {
time: String,
begin: String,
end: String,
},
/// Wraps an error surfaced while building a
/// [`Precomputed`](crate::orbitprop::Precomputed) interp table from
/// JPL ephemeris data.
#[error(transparent)]
Jplephem(#[from] crate::jplephem::Error),
// -- satstate.rs -----------------------------------------------------
/// Returned by [`SatState::set_pos_uncertainty`](crate::orbitprop::SatState::set_pos_uncertainty),
/// [`SatState::set_vel_uncertainty`](crate::orbitprop::SatState::set_vel_uncertainty),
/// and the internal `cov_frame_to_gcrf` helper when the supplied
/// frame is not one of the supported orbital or inertial frames.
#[error("Unsupported frame for uncertainty: {frame}. Must be GCRF, LVLH, RIC, or NTW")]
UnsupportedUncertaintyFrame { frame: Frame },
/// Returned by [`SatState::propagate`](crate::orbitprop::SatState::propagate)
/// when a scheduled maneuver uses a frame in which a delta-v cannot be
/// resolved. Validated up front so it surfaces as an error rather than a
/// panic inside the force evaluation.
#[error("Unsupported frame for maneuver: {frame}. Must be GCRF, RTN (RIC), NTW, or LVLH")]
UnsupportedManeuverFrame { frame: Frame },
/// Returned by [`ContinuousThrust::new`](crate::orbitprop::ContinuousThrust::new)
/// when the thrust frame is not one in which a thrust acceleration can be
/// resolved. Validated at construction so it surfaces as an error rather
/// than a panic inside the force evaluation.
#[error("Unsupported frame for thrust: {frame}. Must be GCRF, RTN (RIC), NTW, or LVLH")]
UnsupportedThrustFrame { frame: Frame },
// -- settings.rs -----------------------------------------------------
/// Returned by [`PropSettings::set_gravity`](crate::orbitprop::PropSettings::set_gravity)
/// when `order > degree`.
#[error("Gravity order ({order}) must be ≤ degree ({degree})")]
InvalidGravityOrder { order: u16, degree: u16 },
/// Returned when the requested gravity degree exceeds what the built-in
/// coefficient tables and evaluator support
/// ([`MAX_GRAVITY_DEGREE`](crate::earthgravity::MAX_GRAVITY_DEGREE)).
/// Previously such requests were silently evaluated at the maximum.
#[error("Gravity degree ({degree}) exceeds the maximum supported degree ({max})")]
InvalidGravityDegree { degree: u16, max: u16 },
/// Returned by [`Precomputed::new_padded`](crate::orbitprop::Precomputed::new_padded)
/// when the padding is not a finite number.
#[error("Precomputed table padding must be finite, got {padding}")]
InvalidPrecomputePadding { padding: f64 },
/// Returned by [`Precomputed::new_padded`](crate::orbitprop::Precomputed::new_padded)
/// when the span / step combination would need more table entries than
/// [`MAX_PRECOMPUTE_ENTRIES`](crate::orbitprop::MAX_PRECOMPUTE_ENTRIES)
/// (≈1.3 GB). Use a larger step or a shorter span.
#[error(
"Precomputed table would need {entries} entries (max {max}); \
use a larger interpolation step or a shorter propagation span"
)]
PrecomputeTooLarge { entries: u64, max: usize },
/// Returned when a Gauss-Jackson 8 propagation is requested over a span
/// shorter than its 8-step startup requires. Reduce `gj_step_seconds` or
/// use an adaptive integrator (e.g. RKV98) for short arcs.
#[error(
"Propagation span ({span} s) is too short for Gauss-Jackson 8, which \
needs at least 8 steps ({min} s at the configured step). Reduce \
gj_step_seconds or use an adaptive integrator."
)]
GJIntervalTooShort { span: f64, min: f64 },
// -- EOP coverage (precomputed.rs / propagator.rs) ---------------------
/// No Earth Orientation Parameters table is loaded (file missing and
/// download failed, or an empty table installed). A propagation would
/// then run with zero polar motion / UT1−UTC / nutation corrections and
/// be silently wrong by metres, so it is refused. Run
/// `satkit::utils::update_datafiles()` or point `SATKIT_DATA` at a
/// directory containing `EOP-All.csv`.
#[error(
"no Earth Orientation Parameters (EOP) table is loaded; run \
satkit::utils::update_datafiles() or set SATKIT_DATA to a directory \
containing EOP-All.csv"
)]
EopUnavailable,
/// Returned by [`propagate`](crate::orbitprop::propagate) when
/// [`PropSettings::require_eop_coverage`](crate::orbitprop::PropSettings::require_eop_coverage)
/// is set and the propagation span (plus integrator padding) extends
/// past the end of the loaded EOP table. Without the flag the last EOP
/// row is held constant and a one-time warning is printed instead.
#[error(
"propagation span extends to {span_end} but EOP data ends at {table_end} \
(PropSettings::require_eop_coverage is set); refresh the data files with \
satkit::utils::update_datafiles() or clear the flag to extrapolate"
)]
EopCoverage {
span_end: Instant,
table_end: Instant,
},
/// Wraps a frame-transform error — in practice a missing or unreadable
/// IERS precession-nutation table detected by
/// [`ierstable::preload`](crate::frametransform::ierstable::preload)
/// while building a [`Precomputed`](crate::orbitprop::Precomputed) table.
#[error(transparent)]
FrameTransform(#[from] crate::frametransform::Error),
}
impl From<ode::OdeError> for Error {
fn from(e: ode::OdeError) -> Self {
Self::OdeError(e)
}
}
/// Convenient type alias used throughout the `orbitprop` module.
pub type Result<T> = std::result::Result<T, Error>;