use crate::{Dt, Real, Scale, cos, floor_f, rem_euclid_f, sin};
pub const MARS_SOL_LENGTH_SEC: Real = 88_775.244_146_88;
pub const MARS_SOL_ATTOS: i128 = 88_775_244_146_880_000_000_000;
pub const MARS_REF_TT: Dt = Dt::new(-3_976_386_952, 650_560_000_000_000_000);
pub const MARS_REF_TT_ATTOS: i128 = MARS_REF_TT.to_attos();
pub const MARS_LS_M0: Real = f!(19.3871);
pub const MARS_LS_M_RATE: Real = f!(0.52402073);
pub const MARS_LS_ALPHA_FMS0: Real = f!(270.3871);
pub const MARS_LS_ALPHA_FMS_RATE: Real = f!(0.524038496);
pub const MARS_EOT_COEFF_2LS: Real = f!(2.861);
pub const MARS_EOT_COEFF_4LS: Real = f!(-0.071);
pub const MARS_EOT_COEFF_6LS: Real = f!(0.002);
pub const MARS_YEAR_EPOCH_JD: Real = f!(2435208.456);
pub const MARS_TROPICAL_YEAR_DAYS: Real = f!(686.9725);
impl Dt {
#[inline]
pub(crate) const fn to_attos_since_mars_msd_epoch(numerical_tt: Dt) -> i128 {
numerical_tt.to_attos() - MARS_REF_TT_ATTOS
}
pub const fn to_msd(&self, current: Scale) -> (i64, u128) {
let tt = self.to(current, Scale::TT);
let elapsed = Self::to_attos_since_mars_msd_epoch(tt);
let whole_sols = elapsed.div_euclid(MARS_SOL_ATTOS);
let frac_attos = elapsed.rem_euclid(MARS_SOL_ATTOS) as u128;
(Dt::clamp_i128_to_i64(whole_sols), frac_attos)
}
#[inline]
pub const fn to_mtc(&self, current: Scale) -> Dt {
let (_, frac_attos) = self.to_msd(current);
Dt::from_attos(frac_attos as i128, Scale::TAI)
}
pub const fn from_msd(whole_sols: i64, frac_attos: u128) -> Self {
let elapsed_attos = (whole_sols as i128) * MARS_SOL_ATTOS + frac_attos as i128;
let tt = MARS_REF_TT.add(Dt::from_attos(elapsed_attos, Scale::TAI));
Self::from(tt.sec, tt.attos, Scale::TT)
}
pub const fn from_msd_f(msd: Real) -> Self {
let whole = floor_f(msd) as i64;
let frac = msd - f!(whole);
let frac_span = Dt::from_sec_f(frac * MARS_SOL_LENGTH_SEC);
Self::from_msd(whole, frac_span.to_attos() as u128)
}
#[inline]
pub const fn to_msd_f(&self, current: Scale) -> Real {
let (whole, frac) = self.to_msd(current);
f!(whole) + Dt::attos_to_sec_f(frac) / MARS_SOL_LENGTH_SEC
}
pub const fn to_mars_ls(&self, current: Scale) -> Real {
let tt = self.to(current, Scale::TT);
let jd_tt = tt.to_jd_f();
let dt_j2000 = jd_tt - f!(2451545.0);
let m = MARS_LS_M0 + MARS_LS_M_RATE * dt_j2000;
let alpha_fms = MARS_LS_ALPHA_FMS0 + MARS_LS_ALPHA_FMS_RATE * dt_j2000;
let pbs = Self::mars_perturber_sum(dt_j2000);
let eq_center = (f!(10.691) + f!(3.0e-7) * dt_j2000) * sin(m.to_radians())
+ f!(0.623) * sin((f!(2.0) * m).to_radians())
+ f!(0.050) * sin((f!(3.0) * m).to_radians())
+ f!(0.005) * sin((f!(4.0) * m).to_radians())
+ f!(0.0005) * sin((f!(5.0) * m).to_radians())
+ pbs;
let mut ls = alpha_fms + eq_center;
ls = ls % f!(360.0);
if ls < f!(0.0) {
ls += f!(360.0);
}
ls
}
#[inline]
const fn mars_perturber_sum(dt_j2000: Real) -> Real {
let base = f!(0.985626) * dt_j2000;
let mut sum = f!(0.0);
sum += f!(0.0071) * cos(base / f!(2.2353) + f!(49.409));
sum += f!(0.0057) * cos(base / f!(2.7543) + f!(168.173));
sum += f!(0.0039) * cos(base / f!(1.1177) + f!(191.837));
sum += f!(0.0037) * cos(base / f!(15.7866) + f!(21.736));
sum += f!(0.0021) * cos(base / f!(2.1354) + f!(15.704));
sum += f!(0.0020) * cos(base / f!(2.4694) + f!(95.528));
sum += f!(0.0018) * cos(base / f!(32.8493) + f!(49.095));
sum
}
pub const fn to_mars_lmst(&self, current: Scale, east_longitude_deg: Real) -> Dt {
let tt = self.to(current, Scale::TT);
let jd_tt = tt.to_jd_f();
let mst = (f!(24.0)
* ((jd_tt - f!(2451549.5)) / f!(1.0274912517) + f!(44796.0) - f!(0.0009626)))
% f!(24.0);
let lambda_west = rem_euclid_f(-east_longitude_deg, f!(360.0));
let mut lmst_hours = mst - lambda_west / f!(15.0);
if lmst_hours < f!(0.0) {
lmst_hours += f!(24.0);
}
let seconds_into_sol = lmst_hours * f!(3600.0);
Dt::from_sec_f(seconds_into_sol)
}
pub const fn to_mars_ltst(&self, current: Scale, east_longitude_deg: Real) -> Dt {
let lmst = self.to_mars_lmst(current, east_longitude_deg);
let ls = self.to_mars_ls(current);
let dt_j2000 = self.to(current, Scale::TT).to_jd_f() - f!(2451545.0);
let m = MARS_LS_M0 + MARS_LS_M_RATE * dt_j2000;
let pbs = Self::mars_perturber_sum(dt_j2000);
let eq_center = (f!(10.691) + f!(3.0e-7) * dt_j2000) * sin(m.to_radians())
+ f!(0.623) * sin((f!(2.0) * m).to_radians())
+ f!(0.050) * sin((f!(3.0) * m).to_radians())
+ f!(0.005) * sin((f!(4.0) * m).to_radians())
+ f!(0.0005) * sin((f!(5.0) * m).to_radians())
+ pbs;
let eot_deg = MARS_EOT_COEFF_2LS * sin(f!(2.0) * ls.to_radians())
+ MARS_EOT_COEFF_4LS * sin(f!(4.0) * ls.to_radians())
+ MARS_EOT_COEFF_6LS * sin(f!(6.0) * ls.to_radians())
- eq_center;
let eot_seconds = eot_deg * f!(240.0);
lmst.add(Dt::from_sec_f(eot_seconds))
}
pub const fn to_mars_year(&self, current: Scale) -> i64 {
let tt = self.to(current, Scale::TT);
let jd_tt = tt.to_jd_f();
let days_since_epoch = jd_tt - MARS_YEAR_EPOCH_JD;
let years_elapsed = floor_f(days_since_epoch / MARS_TROPICAL_YEAR_DAYS);
1 + (years_elapsed as i64)
}
}