use crate::lighting::solar_position::EARTH_AXIAL_TILT_DEG;
use crate::temporal::epoch::J2000_EPOCH;
pub const AXIAL_TILT_DEG: f64 = EARTH_AXIAL_TILT_DEG;
pub const TROPICAL_YEAR_DAYS: f64 = 365.24219;
pub const VERNAL_EQUINOX_JD: f64 = 2451623.80;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Season {
Spring,
Summer,
Autumn,
Winter,
}
pub struct SeasonalState {
pub season_north: Season,
pub season_south: Season,
pub solar_declination_deg: f64,
pub day_length_hours: f64,
}
pub fn season_at(julian_date: f64, lat_deg: f64) -> SeasonalState {
let days_since_equinox = julian_date - VERNAL_EQUINOX_JD;
let year_fraction = (days_since_equinox % TROPICAL_YEAR_DAYS) / TROPICAL_YEAR_DAYS;
let solar_declination = AXIAL_TILT_DEG * (2.0 * std::f64::consts::PI * year_fraction).sin();
let north_season = match year_fraction {
f if f < 0.25 => Season::Spring,
f if f < 0.5 => Season::Summer,
f if f < 0.75 => Season::Autumn,
_ => Season::Winter,
};
let south_season = match north_season {
Season::Spring => Season::Autumn,
Season::Summer => Season::Winter,
Season::Autumn => Season::Spring,
Season::Winter => Season::Summer,
};
let lat_rad = lat_deg.to_radians();
let dec_rad = solar_declination.to_radians();
let cos_hour_angle = (-lat_rad.tan() * dec_rad.tan()).clamp(-1.0, 1.0);
let hour_angle = cos_hour_angle.acos();
let day_length_hours = 24.0 * hour_angle / std::f64::consts::PI;
SeasonalState {
season_north: north_season,
season_south: south_season,
solar_declination_deg: solar_declination,
day_length_hours,
}
}
pub fn subsolar_point(julian_date: f64) -> (f64, f64) {
let d = julian_date - J2000_EPOCH;
let mean_anomaly = (357.5291 + 0.98560028 * d) % 360.0;
let m_rad = mean_anomaly.to_radians();
let eoc = 1.9148 * m_rad.sin() + 0.02 * (2.0 * m_rad).sin();
let ecl_lon = (mean_anomaly + eoc + 180.0 + 102.9372) % 360.0;
let ecl_rad = ecl_lon.to_radians();
let obliquity_rad = AXIAL_TILT_DEG.to_radians();
let declination = (obliquity_rad.sin() * ecl_rad.sin()).asin().to_degrees();
let eot_minutes = -7.655 * m_rad.sin() + 9.873 * (2.0 * ecl_rad - m_rad).sin();
let solar_noon_offset = -eot_minutes / 60.0 * 15.0;
let gmst = (280.46061837 + 360.98564736629 * d) % 360.0;
let subsolar_lon = -(gmst + solar_noon_offset) % 360.0;
(declination, subsolar_lon)
}