pub fn solar_declination_deg(ls_deg: f64) -> f64 {
let eps = crate::AXIAL_TILT_DEG.to_radians();
let ls = ls_deg.to_radians();
(eps.sin() * ls.sin()).asin().to_degrees()
}
pub fn equation_of_time_minutes(ls_deg: f64) -> f64 {
let e = crate::ECCENTRICITY;
let ls = ls_deg.to_radians();
let peri = crate::ARGUMENT_PERIHELION_DEG.to_radians();
let eot_rad = -2.0 * e * (ls - peri).sin() + 1.25 * e * e * (2.0 * (ls - peri)).sin();
eot_rad.to_degrees() * 4.0
}
pub struct SolarPosition {
pub azimuth_deg: f64,
pub elevation_deg: f64,
pub distance_au: f64,
pub direction: [f64; 3],
}
impl SolarPosition {
pub fn compute(ls_deg: f64, local_time_h: f64, lat_deg: f64, lon_deg: f64) -> Self {
if !lon_deg.is_finite() {
return Self {
azimuth_deg: f64::NAN,
elevation_deg: f64::NAN,
distance_au: f64::NAN,
direction: [f64::NAN; 3],
};
}
let decl = solar_declination_deg(ls_deg);
let eot = equation_of_time_minutes(ls_deg);
let hour_angle = (local_time_h - 12.0) * 15.0 + eot / 4.0;
let ha = hour_angle.to_radians();
let lat = lat_deg.to_radians();
let dec = decl.to_radians();
let sin_elev = lat.sin() * dec.sin() + lat.cos() * dec.cos() * ha.cos();
let elevation = sin_elev.asin();
let cos_az = (dec.sin() - lat.sin() * sin_elev) / (lat.cos() * elevation.cos().max(1e-10));
let mut azimuth = cos_az.clamp(-1.0, 1.0).acos();
if ha > 0.0 {
azimuth = 2.0 * std::f64::consts::PI - azimuth;
}
let az = azimuth;
let el = elevation;
let direction = [-az.sin() * el.cos(), el.sin(), -az.cos() * el.cos()];
let ls = ls_deg.to_radians();
let e = crate::ECCENTRICITY;
let peri = crate::ARGUMENT_PERIHELION_DEG.to_radians();
let nu = ls - peri;
let r_au = crate::SEMI_MAJOR_AXIS_AU * (1.0 - e * e) / (1.0 + e * nu.cos());
Self {
azimuth_deg: azimuth.to_degrees(),
elevation_deg: elevation.to_degrees(),
distance_au: r_au,
direction,
}
}
pub fn is_above_horizon(&self) -> bool {
self.elevation_deg > 0.0
}
pub fn distance_m(&self) -> f64 {
self.distance_au * sciforge::hub::domain::common::constants::AU
}
}