use crate::formats::tle::TLE;
use crate::qtty::Minutes;
use alloc::string::ToString;
use tempoch::{JulianDate, TimeContext, UTC};
use super::elements::tle_to_elements;
use super::model::Constants;
use super::state::TemeState;
use super::Sgp4Error;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum GravityModel {
#[default]
Wgs72,
Wgs72Iau,
Wgs84,
}
#[derive(Clone, Debug)]
pub struct TlePropagator {
context: TimeContext,
constants: Constants,
epoch_calendar_utc: chrono::NaiveDateTime,
model: GravityModel,
epoch_jd_utc: JulianDate<UTC>,
}
impl TlePropagator {
pub fn from_tle(tle: &TLE) -> Result<Self, Sgp4Error> {
Self::from_tle_with_model(tle, GravityModel::default())
}
pub fn from_tle_with_context(tle: &TLE, context: TimeContext) -> Result<Self, Sgp4Error> {
Self::from_tle_with_model_and_context(tle, GravityModel::default(), context)
}
pub fn from_tle_with_model(tle: &TLE, model: GravityModel) -> Result<Self, Sgp4Error> {
Self::from_tle_with_model_and_context(tle, model, TimeContext::new())
}
pub fn from_tle_with_model_and_context(
tle: &TLE,
model: GravityModel,
context: TimeContext,
) -> Result<Self, Sgp4Error> {
let elements = tle_to_elements(tle, &context)?;
let constants = Constants::new(&elements, model)?;
let epoch_jd_utc = tle
.epoch
.to_with::<tempoch::JD>(&context)
.map_err(|e| Sgp4Error::TimeConversion(e.to_string()))?;
Ok(Self {
context,
constants,
epoch_calendar_utc: elements.datetime,
model,
epoch_jd_utc,
})
}
pub fn time_context(&self) -> &TimeContext {
&self.context
}
pub fn gravity_model(&self) -> GravityModel {
self.model
}
pub fn epoch_jd_utc(&self) -> JulianDate<UTC> {
self.epoch_jd_utc
}
pub fn propagate_at(&self, target_jd_utc: JulianDate<UTC>) -> Result<TemeState, Sgp4Error> {
target_jd_utc
.try_raw_with(&self.context)
.map_err(|e| Sgp4Error::TimeConversion(e.to_string()))?;
let datetime = target_jd_utc
.try_to_chrono_with(&self.context)
.map_err(|e| Sgp4Error::TimeConversion(e.to_string()))?
.naive_utc();
let minutes = super::time::minutes_between(self.epoch_calendar_utc, datetime)?;
self.propagate_internal(minutes, target_jd_utc)
}
pub fn propagate_minutes(&self, dt_minutes: Minutes) -> Result<TemeState, Sgp4Error> {
if !dt_minutes.value().is_finite() {
return Err(Sgp4Error::TimeConversion(
"minutes offset is non-finite".into(),
));
}
let datetime = super::time::offset_epoch(self.epoch_calendar_utc, dt_minutes)?;
let target = tempoch::Time::<UTC>::try_from_chrono_with(datetime.and_utc(), &self.context)
.and_then(|t| t.to_with::<tempoch::JD>(&self.context))
.map_err(|e| Sgp4Error::TimeConversion(e.to_string()))?;
self.propagate_internal(dt_minutes, target)
}
fn propagate_internal(
&self,
dt_minutes: Minutes,
target_jd_utc: JulianDate<UTC>,
) -> Result<TemeState, Sgp4Error> {
let (position, velocity) = self.constants.propagate(dt_minutes)?;
Ok(TemeState::from_arrays(target_jd_utc, position, velocity))
}
}