use crate::bodies::solar_system::Sun;
use crate::astro::nutation::nutation_iau2000b;
use crate::astro::precession;
use crate::calculus::horizontal;
use crate::coordinates::{
cartesian,
centers::*,
frames, spherical,
transform::{Transform, TransformFrame},
};
use crate::qtty::{
AstronomicalUnit, AstronomicalUnits, LengthUnit, Meter, Quantity, Radian, Radians,
};
use crate::time::JulianDate;
impl Sun {
pub fn get_apparent_geocentric_equ<U: LengthUnit>(
jd: JulianDate,
) -> spherical::position::EquatorialTrueOfDate<U>
where
Quantity<U>: From<AstronomicalUnits>,
{
let helio = cartesian::position::EclipticMeanJ2000::<U, Heliocentric>::CENTER;
let geo_ecl: cartesian::position::EclipticMeanJ2000<U, Geocentric> = helio.transform(jd);
let geo_cart: cartesian::position::EquatorialMeanJ2000<U, Geocentric> = geo_ecl.to_frame();
let nut = nutation_iau2000b(jd);
let npb = precession::precession_nutation_matrix(jd, nut.dpsi, nut.deps);
let [x_t, y_t, z_t] = npb * [geo_cart.x(), geo_cart.y(), geo_cart.z()];
let true_cart =
cartesian::Position::<Geocentric, frames::EquatorialTrueOfDate, U>::new(x_t, y_t, z_t);
spherical::Position::from_cartesian(&true_cart)
}
pub fn get_apparent_topocentric_equ<U: LengthUnit>(
jd: JulianDate,
site: Geodetic<frames::ECEF>,
) -> spherical::Position<Topocentric, frames::EquatorialTrueOfDate, U>
where
Quantity<U>: From<Quantity<Meter>> + From<AstronomicalUnits>,
{
let helio = cartesian::position::EclipticMeanJ2000::<U, Heliocentric>::CENTER;
let geo_ecl_j2000: cartesian::position::EclipticMeanJ2000<U, Geocentric> =
helio.transform(jd);
let geo_cart_j2000: cartesian::position::EquatorialMeanJ2000<U, Geocentric> =
geo_ecl_j2000.to_frame();
horizontal::geocentric_j2000_to_apparent_topocentric(&geo_cart_j2000, site, jd)
}
pub fn get_horizontal<U: LengthUnit>(
time: impl Into<JulianDate>,
site: Geodetic<frames::ECEF>,
) -> spherical::Position<Topocentric, frames::Horizontal, U>
where
Quantity<U>: From<Quantity<Meter>> + From<AstronomicalUnits>,
{
let jd = time.into();
let eq = Self::get_apparent_topocentric_equ::<U>(jd, site);
horizontal::equatorial_to_horizontal(&eq, site, jd)
}
pub fn ecliptic_longitude_geocentric(jd: JulianDate) -> Radians {
let helio =
cartesian::position::EclipticMeanJ2000::<AstronomicalUnit, Heliocentric>::CENTER;
let geo_ecl: cartesian::position::EclipticMeanJ2000<AstronomicalUnit, Geocentric> =
helio.transform(jd);
spherical::Position::from_cartesian(&geo_ecl)
.direction()
.lon()
.to::<Radian>()
}
}
#[cfg(test)]
mod tests {
use crate::bodies::solar_system::Sun;
use crate::qtty::{AstronomicalUnit, AstronomicalUnits, Degrees, Radians};
use crate::time::JulianDate;
#[test]
fn apparent_sun_position_j2000() {
let pos = Sun::get_apparent_geocentric_equ::<AstronomicalUnit>(JulianDate::J2000);
let expected_ra = 281.2; let expected_dec = -23.0; let expected_dist = 1.0;
eprintln!(
"Got RA: {}, Dec: {}, Dist: {}",
pos.ra(),
pos.dec(),
pos.distance
);
assert!(
(pos.ra() - Degrees::new(expected_ra)).abs() < Degrees::new(2.0),
"RA mismatch: got {}, expected ~{}",
pos.ra(),
expected_ra
);
assert!(
(pos.dec() - Degrees::new(expected_dec)).abs() < Degrees::new(2.0),
"Dec mismatch: got {}, expected ~{}",
pos.dec(),
expected_dec
);
assert!(
(pos.distance - AstronomicalUnits::new(expected_dist)).abs()
< AstronomicalUnits::new(0.2)
);
}
#[test]
fn ecliptic_longitude_geocentric_j2000() {
let lon: Radians = Sun::ecliptic_longitude_geocentric(JulianDate::J2000);
let expected = 4.8935_f64;
assert!(
(lon.value() - expected).abs() < 1e-3,
"ecliptic longitude mismatch: got {:.6} rad, expected ~{:.4} rad",
lon.value(),
expected
);
}
}