use crate::earthgravity::GravityModel;
use crate::orbitprop::Precomputed;
use crate::TimeLike;
use anyhow::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[derive(Default)]
pub enum Integrator {
#[default]
RKV98,
RKV98NoInterp,
RKV87,
RKV65,
RKTS54,
RODAS4,
GaussJackson8,
}
impl std::fmt::Display for Integrator {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::RKV98 => write!(f, "RKV98 (9th order, 26 stages)"),
Self::RKV98NoInterp => write!(f, "RKV98NoInterp (9th order, 16 stages)"),
Self::RKV87 => write!(f, "RKV87 (8th order, 21 stages)"),
Self::RKV65 => write!(f, "RKV65 (6th order, 10 stages)"),
Self::RKTS54 => write!(f, "RKTS54 (5th order, 7 stages, FSAL)"),
Self::RODAS4 => write!(f, "RODAS4 (4th order, 6 stages, L-stable)"),
Self::GaussJackson8 => write!(f, "Gauss-Jackson 8 (8th order, fixed-step multistep)"),
}
}
}
#[derive(Debug, Clone)]
pub struct PropSettings {
pub gravity_degree: u16,
pub gravity_order: u16,
pub gravity_model: GravityModel,
pub abs_error: f64,
pub rel_error: f64,
pub use_spaceweather: bool,
pub use_sun_gravity: bool,
pub use_moon_gravity: bool,
pub enable_interp: bool,
pub integrator: Integrator,
pub gj_step_seconds: f64,
pub max_steps: usize,
pub precomputed: Option<Precomputed>,
}
impl Default for PropSettings {
fn default() -> Self {
Self {
gravity_degree: 4,
gravity_order: 4,
gravity_model: GravityModel::EGM96,
abs_error: 1e-8,
rel_error: 1e-8,
use_spaceweather: true,
use_sun_gravity: true,
use_moon_gravity: true,
enable_interp: true,
integrator: Integrator::default(),
gj_step_seconds: 60.0,
max_steps: 1_000_000,
precomputed: None,
}
}
}
impl PropSettings {
pub fn set_gravity(&mut self, degree: u16, order: u16) -> Result<()> {
if order > degree {
anyhow::bail!(
"Gravity order ({}) must be ≤ degree ({})",
order,
degree
);
}
self.gravity_degree = degree;
self.gravity_order = order;
Ok(())
}
pub fn required_precompute_padding(&self) -> f64 {
match self.integrator {
Integrator::GaussJackson8 => {
(4.0 * self.gj_step_seconds.abs() + 60.0)
.max(crate::orbitprop::precomputed::DEFAULT_PADDING_SECS)
}
Integrator::RKV98
| Integrator::RKV98NoInterp
| Integrator::RKV87
| Integrator::RKV65
| Integrator::RKTS54
| Integrator::RODAS4 => crate::orbitprop::precomputed::DEFAULT_PADDING_SECS,
}
}
pub fn precompute_terms<T: TimeLike>(&mut self, begin: &T, end: &T) -> Result<()> {
let padding = self.required_precompute_padding();
self.precomputed = Some(Precomputed::new_padded(begin, end, 60.0, padding)?);
Ok(())
}
pub fn precompute_terms_with_step<T: TimeLike>(
&mut self,
begin: &T,
end: &T,
step_secs: f64,
) -> Result<()> {
let padding = self.required_precompute_padding();
self.precomputed = Some(Precomputed::new_padded(begin, end, step_secs, padding)?);
Ok(())
}
}
impl std::fmt::Display for PropSettings {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
r#"Orbit Propagation Settings
Gravity Degree: {},
Gravity Order: {},
Gravity Model: {},
Max Abs Error: {:e},
Max Rel Error: {:e},
Space Weather: {},
Sun Gravity: {},
Moon Gravity: {},
Interpolation: {},
Integrator: {},
Max Steps: {},
{}"#,
self.gravity_degree,
self.gravity_order,
self.gravity_model,
self.abs_error,
self.rel_error,
self.use_spaceweather,
self.use_sun_gravity,
self.use_moon_gravity,
self.enable_interp,
self.integrator,
self.max_steps,
self.precomputed.as_ref().map_or_else(
|| "No Precomputed".to_string(),
|p| format!("Precomputed: {} to {}", p.begin, p.end)
)
)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn testdisplay() {
let props = PropSettings::default();
println!("props = {}", props);
}
}