use crate::formats::tle::TLE;
use alloc::format;
use chrono::Timelike;
use qtty::angular_rate::AngularRate;
use qtty::unit::{Minute, Radian};
use super::Sgp4Error;
#[derive(Clone, Debug)]
pub(super) struct NativeElements {
pub datetime: chrono::NaiveDateTime,
pub orbit: super::model::Orbit,
pub bstar: f64,
}
pub(crate) fn tle_to_elements(
tle: &TLE,
ctx: &tempoch::TimeContext,
) -> Result<NativeElements, Sgp4Error> {
let dt_utc = tle
.epoch
.try_to_chrono_with(ctx)
.map_err(|e| Sgp4Error::TimeConversion(format!("TLE epoch → chrono failed: {e:?}")))?;
let datetime = dt_utc.naive_utc();
if datetime.nanosecond() >= 1_000_000_000 {
return Err(Sgp4Error::InvalidEpoch(
"leap-second label is outside the SGP4 time grid",
));
}
if keplerian::Eccentricity::new_elliptic(tle.eccentricity).is_err() {
return Err(Sgp4Error::InvalidElements {
details: format!("eccentricity outside [0, 1): {}", tle.eccentricity),
});
}
if !tle.mean_motion.value().is_finite() || tle.mean_motion.value() <= 0.0 {
return Err(Sgp4Error::InvalidElements {
details: format!("non-positive mean motion: {}", tle.mean_motion.value()),
});
}
if [
tle.inclination.value(),
tle.raan.value(),
tle.argument_of_perigee.value(),
tle.mean_anomaly.value(),
tle.bstar,
]
.iter()
.any(|v| !v.is_finite())
{
return Err(Sgp4Error::InvalidElements {
details: "non-finite angle or BSTAR".into(),
});
}
if !(0.0..=180.0).contains(&tle.inclination.value()) {
return Err(Sgp4Error::InvalidElements {
details: "inclination outside [0, 180] degrees".into(),
});
}
if tle.mean_motion.value() > f64::MAX / core::f64::consts::PI {
return Err(Sgp4Error::InvalidElements {
details: "mean motion exceeds finite normalized range".into(),
});
}
let mean_motion: AngularRate<Radian, Minute> = tle.mean_motion.to();
Ok(NativeElements {
datetime,
orbit: super::model::Orbit {
e: tle.eccentricity,
i: tle.inclination.to::<qtty::unit::Radian>().value(),
node: tle.raan.to::<qtty::unit::Radian>().value(),
argp: tle.argument_of_perigee.to::<qtty::unit::Radian>().value(),
m: tle.mean_anomaly.to::<qtty::unit::Radian>().value(),
n: mean_motion.value(),
},
bstar: tle.bstar,
})
}