earths 0.0.1

High-fidelity Earth simulation engine — orbit, atmosphere, geology, hydrology, biosphere, terrain, lighting, rendering, satellites, and temporal systems with full scientific coupling
Documentation
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)
}