use crate::bodies::solar_system::Sun;
use crate::calculus::math_core::intervals;
use crate::coordinates::centers::Geodetic;
use crate::coordinates::frames::ECEF;
use crate::time::{complement_within, JulianDate, ModifiedJulianDate, Period, MJD};
use qtty::*;
const SCAN_STEP: Days = Quantity::<Hour>::new(2.0).to_const::<Day>();
pub(crate) fn sun_altitude_rad(mjd: ModifiedJulianDate, site: &Geodetic<ECEF>) -> Quantity<Radian> {
let jd: JulianDate = mjd.into();
Sun::get_horizontal::<AstronomicalUnit>(jd, *site)
.alt()
.to::<Radian>()
}
pub(crate) fn find_day_periods(
site: Geodetic<ECEF>,
period: Period<MJD>,
threshold: Degrees,
) -> Vec<Period<MJD>> {
let thr = threshold.to::<Radian>();
let f = |t: ModifiedJulianDate| -> Radians { sun_altitude_rad(t, &site) };
intervals::above_threshold_periods(period, SCAN_STEP, &f, thr)
}
pub(crate) fn find_night_periods(
site: Geodetic<ECEF>,
period: Period<MJD>,
twilight: Degrees,
) -> Vec<Period<MJD>> {
let days = find_day_periods(site, period, twilight);
complement_within(period, &days)
}
pub(crate) fn find_sun_range_periods(
site: Geodetic<ECEF>,
period: Period<MJD>,
range: (Degrees, Degrees),
) -> Vec<Period<MJD>> {
let h_min = range.0.to::<Radian>();
let h_max = range.1.to::<Radian>();
let f = |t: ModifiedJulianDate| -> Radians { sun_altitude_rad(t, &site) };
intervals::in_range_periods(period, SCAN_STEP, &f, h_min, h_max)
}
#[cfg(test)]
mod tests {
use super::*;
fn greenwich_site() -> Geodetic<ECEF> {
Geodetic::<ECEF>::new(
Degrees::new(0.0),
Degrees::new(51.4769),
Quantity::<Meter>::new(0.0),
)
}
#[test]
fn test_sun_altitude_basic() {
let site = greenwich_site();
let mjd: ModifiedJulianDate = crate::time::JulianDate::J2000.into();
let alt = sun_altitude_rad(mjd, &site);
assert!(
alt > Radians::new(-std::f64::consts::FRAC_PI_2)
&& alt < Radians::new(std::f64::consts::FRAC_PI_2)
);
}
#[test]
fn test_find_night_periods() {
use crate::calculus::solar::twilight;
let site = greenwich_site();
let mjd_start = ModifiedJulianDate::new(60000.0);
let mjd_end = ModifiedJulianDate::new(60007.0);
let period = Period::new(mjd_start, mjd_end);
let nights = find_night_periods(site, period, twilight::ASTRONOMICAL);
assert!(
!nights.is_empty(),
"Should find night periods at 51° latitude"
);
for night in &nights {
assert!(
night.duration_days() > 0.0,
"Night duration should be positive"
);
assert!(
night.duration_days() < 1.0,
"Night should be less than 24 hours"
);
}
}
#[test]
fn test_find_altitude_range_periods() {
let site = greenwich_site();
let mjd_start = ModifiedJulianDate::new(60000.0);
let mjd_end = ModifiedJulianDate::new(60007.0);
let period = Period::new(mjd_start, mjd_end);
let nights =
find_sun_range_periods(site, period, (Degrees::new(-90.0), Degrees::new(-18.0)));
assert!(!nights.is_empty(), "Should find night periods using range");
let nautical =
find_sun_range_periods(site, period, (Degrees::new(-18.0), Degrees::new(-12.0)));
assert!(
!nautical.is_empty(),
"Should find nautical twilight periods"
);
}
}