ako 0.0.3

Ako is a Rust crate that offers a practical and human-friendly approach to creating, manipulating, formatting and converting dates, times and timestamps.
Documentation
use core::f64::consts::PI;
use std::f64::consts::TAU;

use crate::astronomy::julian::j2000_century;
use crate::astronomy::nutation::nutation;
use crate::astronomy::{SphericalCoords, earth, eval_polynomial};

/// Returns the “true” position of the sun, in ecliptic coordinates, for the given
/// Julian ephemeris day (JDE).
fn position_true(date: f64) -> SphericalCoords {
    let coords = earth::position(date);

    // convert from the Dynamical frame to the FK5 frame

    let s = coords.longitude + PI;

    let (sp, cp) = eval_polynomial(j2000_century(date), &[
        s,
        -1.397_f64.to_radians(),
        -0.00031_f64.to_radians(),
    ])
    .sin_cos();

    let b = (0.03916 / 3600.0_f64).to_radians() * (cp - sp);

    let long = (s - (0.09033 / 3600.0_f64).to_radians()).rem_euclid(TAU);
    let lat = b - coords.latitude;

    SphericalCoords {
        latitude: lat,
        longitude: long,
        ..coords
    }
}

/// Returns the “apparent” position of the sun, in ecliptic coordinates, for the given
/// Julian ephemeris day (JDE).
pub fn position_apparent(date: f64) -> SphericalCoords {
    let coords = position_true(date);
    let nutation = nutation(date);
    let aberration = (-20.4898 / 3600.0_f64).to_radians() / coords.distance;

    SphericalCoords {
        longitude: coords.longitude + nutation + aberration,
        ..coords
    }
}

#[cfg(test)]
mod tests {
    use float_cmp::assert_approx_eq;
    use test_case::test_case;

    use super::{position_apparent, position_true};

    #[rustfmt::skip]
    #[test_case(2460665.885255802, 7.442085486363556e-7, 4.712406431570293, 0.9837312104362747)]
    fn expect_solar_position_true(date: f64, b: f64, l: f64, r: f64) {
        let coords = position_true(date);

        assert_approx_eq!(f64, coords.longitude, l);
        assert_approx_eq!(f64, coords.latitude, b);
        assert_approx_eq!(f64, coords.distance, r);
    }

    #[rustfmt::skip]
    #[test_case(2460665.885255802, 7.442085486363556e-7, 4.712303394455034, 0.9837312104362747)]
    fn expect_solar_position_apparent(date: f64, b: f64, l: f64, r: f64) {
        let coords = position_apparent(date);

        assert_approx_eq!(f64, coords.longitude, l);
        assert_approx_eq!(f64, coords.latitude, b);
        assert_approx_eq!(f64, coords.distance, r);
    }
}