use crate::astro::earth_rotation::jd_ut1_from_tt_eop;
use crate::astro::eop::EopProvider;
use crate::astro::nutation::nutation_iau2000b;
use crate::astro::precession;
use crate::astro::sidereal::gast_iau2006;
use crate::coordinates::centers::Geodetic;
use crate::coordinates::frames::ECEF;
use crate::coordinates::transform::AstroContext;
use crate::coordinates::{cartesian, centers::*, frames, spherical};
use crate::time::JulianDate;
use qtty::{AstronomicalUnits, Degree, LengthUnit, Meter, Quantity, Radian, Radians};
pub fn geocentric_j2000_to_apparent_topocentric<U: LengthUnit>(
geo_cart_j2000: &cartesian::Position<Geocentric, frames::EquatorialMeanJ2000, U>,
site: Geodetic<frames::ECEF>,
jd: JulianDate,
) -> spherical::Position<Topocentric, frames::EquatorialTrueOfDate, U>
where
Quantity<U>: From<Quantity<Meter>> + From<AstronomicalUnits>,
{
let ctx: AstroContext = AstroContext::default();
geocentric_j2000_to_apparent_topocentric_with_ctx(geo_cart_j2000, site, jd, &ctx)
}
pub fn geocentric_j2000_to_apparent_topocentric_with_ctx<
U: LengthUnit,
Eph,
Eop: EopProvider,
Nut,
>(
geo_cart_j2000: &cartesian::Position<Geocentric, frames::EquatorialMeanJ2000, U>,
site: Geodetic<frames::ECEF>,
jd: JulianDate,
ctx: &AstroContext<Eph, Eop, Nut>,
) -> spherical::Position<Topocentric, frames::EquatorialTrueOfDate, U>
where
Quantity<U>: From<Quantity<Meter>> + From<AstronomicalUnits>,
{
use crate::coordinates::transform::to_topocentric_with_ctx;
let topo_cart_j2000 = to_topocentric_with_ctx(geo_cart_j2000, site, jd, ctx);
let rot_prec = precession::precession_matrix_iau2006(jd);
let [x_m, y_m, z_m] = rot_prec
* [
topo_cart_j2000.x(),
topo_cart_j2000.y(),
topo_cart_j2000.z(),
];
let topo_cart_mod =
cartesian::Position::<Topocentric, frames::EquatorialMeanOfDate, U>::new_with_params(
*topo_cart_j2000.center_params(),
x_m,
y_m,
z_m,
);
let rot_nut = crate::astro::nutation::nutation_rotation_iau2000b(jd);
let [x_t, y_t, z_t] = rot_nut * [topo_cart_mod.x(), topo_cart_mod.y(), topo_cart_mod.z()];
let topo_cart_true =
cartesian::Position::<Topocentric, frames::EquatorialTrueOfDate, U>::new_with_params(
*topo_cart_mod.center_params(),
x_t,
y_t,
z_t,
);
spherical::Position::from_cartesian(&topo_cart_true)
}
pub fn equatorial_to_horizontal<U: LengthUnit>(
eq_position: &spherical::Position<Topocentric, frames::EquatorialTrueOfDate, U>,
site: Geodetic<frames::ECEF>,
jd: JulianDate,
) -> spherical::Position<Topocentric, frames::Horizontal, U> {
equatorial_to_horizontal_true_of_date(eq_position, site, jd)
}
pub fn equatorial_to_horizontal_true_of_date<U: LengthUnit>(
eq_position: &spherical::Position<Topocentric, frames::EquatorialTrueOfDate, U>,
site: Geodetic<frames::ECEF>,
jd: JulianDate,
) -> spherical::Position<Topocentric, frames::Horizontal, U> {
let ctx: AstroContext = AstroContext::default();
equatorial_to_horizontal_true_of_date_with_ctx(eq_position, site, jd, &ctx)
}
pub fn equatorial_to_horizontal_true_of_date_with_ctx<U: LengthUnit, Eph, Eop: EopProvider, Nut>(
eq_position: &spherical::Position<Topocentric, frames::EquatorialTrueOfDate, U>,
site: Geodetic<ECEF>,
jd: JulianDate,
ctx: &AstroContext<Eph, Eop, Nut>,
) -> spherical::Position<Topocentric, frames::Horizontal, U> {
let ra = eq_position.azimuth;
let dec = eq_position.polar;
let distance = eq_position.distance;
let eop = ctx.eop_at(jd);
let jd_ut1 = jd_ut1_from_tt_eop(jd, &eop);
let nut = nutation_iau2000b(jd);
let gast = gast_iau2006(jd_ut1, jd, nut.dpsi, nut.true_obliquity());
let lst = gast + site.lon.to::<Radian>();
let ha = (lst - ra.to::<Radian>())
.value()
.rem_euclid(std::f64::consts::TAU);
let ha = Radians::new(ha);
let lat = site.lat.to::<Radian>();
let dec_rad = dec.to::<Radian>();
let sin_alt = dec_rad.sin() * lat.sin() + dec_rad.cos() * lat.cos() * ha.cos();
let alt = Radians::new(sin_alt.asin()).to::<Degree>();
let az_rad = (-dec_rad.cos() * ha.sin())
.atan2(dec_rad.sin() * lat.cos() - dec_rad.cos() * ha.cos() * lat.sin());
let az = Radians::new(az_rad).normalize().to::<Degree>();
Topocentric::horizontal(site, alt, az, distance)
}
#[inline]
pub fn equatorial_to_horizontal_with_ctx<U: LengthUnit, Eph, Eop: EopProvider, Nut>(
eq_position: &spherical::Position<Topocentric, frames::EquatorialTrueOfDate, U>,
site: Geodetic<frames::ECEF>,
jd: JulianDate,
ctx: &AstroContext<Eph, Eop, Nut>,
) -> spherical::Position<Topocentric, frames::Horizontal, U> {
equatorial_to_horizontal_true_of_date_with_ctx(eq_position, site, jd, ctx)
}
pub fn star_horizontal(
ra_j2000: qtty::Degrees,
dec_j2000: qtty::Degrees,
site: &Geodetic<frames::ECEF>,
jd: JulianDate,
) -> spherical::Direction<frames::Horizontal> {
let ra_rad = ra_j2000.to::<Radian>();
let dec_rad = dec_j2000.to::<Radian>();
let (sin_ra, cos_ra) = ra_rad.sin_cos();
let (sin_dec, cos_dec) = dec_rad.sin_cos();
let x0 = cos_dec * cos_ra;
let y0 = cos_dec * sin_ra;
let z0 = sin_dec;
let nut = nutation_iau2000b(jd);
let npb = precession::precession_nutation_matrix(jd, nut.dpsi, nut.deps);
let [x_t, y_t, z_t] = npb.apply_array([x0, y0, z0]);
let ra_tod = Radians::new(y_t.atan2(x_t));
let dec_tod = Radians::new(z_t.asin());
let ctx: AstroContext = AstroContext::default();
let eop = ctx.eop_at(jd);
let jd_ut1 = jd_ut1_from_tt_eop(jd, &eop);
let gast = gast_iau2006(jd_ut1, jd, nut.dpsi, nut.true_obliquity());
let lst = gast + site.lon.to::<Radian>();
let ha = Radians::new((lst - ra_tod).value().rem_euclid(std::f64::consts::TAU));
let lat = site.lat.to::<Radian>();
let sin_alt = dec_tod.sin() * lat.sin() + dec_tod.cos() * lat.cos() * ha.cos();
let alt = Radians::new(sin_alt.asin()).to::<Degree>();
let az_rad = (-dec_tod.cos() * ha.sin())
.atan2(dec_tod.sin() * lat.cos() - dec_tod.cos() * ha.cos() * lat.sin());
let az = Radians::new(az_rad).normalize().to::<Degree>();
spherical::Direction::<frames::Horizontal>::new(alt, az)
}