use hifitime::{ut1::Ut1Provider, Epoch};
use nalgebra::Vector3;
use ordered_float::NotNan;
use photom::{constants::ERAU, observer::Observer};
use crate::{
cache::{
observer_centric_cache::{
ObserverGeocentricPosition, ObserverGeocentricVelocity, ObserverHeliocentricPosition,
ObserverHeliocentricVelocity,
},
observer_fixed_cache::{ObserverFixedCache, ObserverFixedPosition, ObserverFixedVelocity},
},
constants::{EARTH_ROTATION, ROT_ECLMJ2000_TO_EQUMJ2000},
conversion::ToNotNan,
earth_orientation::equequ,
ref_system::{rotmt, rotpn, RefEpoch, RefSystem},
time::gmst,
JPLEphem, OutfitError,
};
pub trait ResolvedObserver {
fn earth_fixed_position(&self) -> Result<ObserverFixedPosition, OutfitError>;
fn earth_fixed_velocity(&self) -> Result<ObserverFixedVelocity, OutfitError>;
fn pvobs(
tmjd: &Epoch,
ut1_provider: &Ut1Provider,
observer_fixed_vectors: &ObserverFixedCache,
compute_velocity: bool,
) -> Result<(ObserverGeocentricPosition, ObserverGeocentricVelocity), OutfitError>;
fn helio_position(
jpl: &JPLEphem,
epoch: &Epoch,
observer_geocentric_position: &ObserverGeocentricPosition,
) -> Result<ObserverHeliocentricPosition, OutfitError>;
fn helio_velocity(
jpl: &JPLEphem,
epoch: &Epoch,
observer_geocentric_velocity: &ObserverGeocentricVelocity,
) -> Result<ObserverHeliocentricVelocity, OutfitError>;
}
impl ResolvedObserver for Observer {
fn earth_fixed_position(&self) -> Result<ObserverFixedPosition, OutfitError> {
let (sin_lon, cos_lon): (NotNan<f64>, NotNan<f64>) = {
let (s, c) = self.longitude.sin_cos();
(s.to_notnan()?, c.to_notnan()?)
};
let erau_not_nan = ERAU.to_notnan()?;
Ok(Vector3::new(
erau_not_nan * self.rho_cos_phi * cos_lon,
erau_not_nan * self.rho_cos_phi * sin_lon,
erau_not_nan * self.rho_sin_phi,
))
}
#[inline]
fn earth_fixed_velocity(&self) -> Result<ObserverFixedVelocity, OutfitError> {
Ok(EARTH_ROTATION
.to_notnan()?
.cross(&self.earth_fixed_position()?))
}
fn pvobs(
tmjd: &Epoch,
ut1_provider: &Ut1Provider,
observer_fixed_vectors: &ObserverFixedCache,
compute_velocity: bool,
) -> Result<(ObserverGeocentricPosition, ObserverGeocentricVelocity), OutfitError> {
let dxbf = observer_fixed_vectors.position();
let mjd_ut1 = tmjd.to_ut1(ut1_provider);
let tut = mjd_ut1.to_mjd_tai_days();
let gast = gmst(tut) + equequ(tmjd.to_mjd_tt_days());
let rot = rotmt(-gast, 2);
let rer_sys1 = RefSystem::Equt(RefEpoch::Epoch(tmjd.to_mjd_tt_days()));
let rer_sys2 = RefSystem::Eclm(RefEpoch::J2000);
let rot1 = rotpn(&rer_sys1, &rer_sys2)?;
let rot1_mat = rot1.transpose().to_notnan()?;
let rot_mat = rot.transpose().to_notnan()?;
let rotmat = rot1_mat * rot_mat;
let dx = rotmat * dxbf;
let dv = if compute_velocity {
let dvbf = observer_fixed_vectors.velocity();
rotmat * dvbf
} else {
Vector3::zeros()
};
Ok((dx, dv))
}
fn helio_position(
jpl: &JPLEphem,
epoch: &Epoch,
observer_geocentric_position: &ObserverGeocentricPosition,
) -> Result<ObserverHeliocentricPosition, OutfitError> {
let earth_pos = jpl.earth_ephemeris(epoch, false).0.to_notnan()?;
let rot_matrix = ROT_ECLMJ2000_TO_EQUMJ2000.to_notnan()?;
let helio_pos = earth_pos + rot_matrix * observer_geocentric_position;
Ok(helio_pos)
}
fn helio_velocity(
jpl: &JPLEphem,
epoch: &Epoch,
observer_geocentric_velocity: &ObserverGeocentricVelocity,
) -> Result<ObserverHeliocentricVelocity, OutfitError> {
let earth_vel = jpl
.earth_ephemeris(epoch, true)
.1
.expect("Velocity is always available, this should not happen")
.to_notnan()?;
let rot_matrix = ROT_ECLMJ2000_TO_EQUMJ2000.to_notnan()?;
let helio_vel = earth_vel + rot_matrix * observer_geocentric_velocity;
Ok(helio_vel)
}
}