/* * Parts of this file have been taken from the Redshift project: * https://github.com/jonls/redshift/ * * Copyright 2010 Jon Lund Steffensen * Copyright 2023 Robert Tari * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Jon Lund Steffensen * Robert Tari */ #include #include "solar.h" #define RAD(x) ((x)*(M_PI/180)) #define DEG(x) ((x)*(180/M_PI)) /* Julian centuries since J2000.0 from Julian day */ static double jcent_from_jd(double jd) { return (jd - 2451545.0) / 36525.0; } /* Angular elevation at the location for the given hour angle. lat: Latitude of location in degrees decl: Declination in radians ha: Hour angle in radians Return: Angular elevation in radians */ static double elevation_from_hour_angle(double lat, double decl, double ha) { return asin(cos(ha)*cos(RAD(lat))*cos(decl) + sin(RAD(lat))*sin(decl)); } /* Geometric mean anomaly of the sun. t: Julian centuries since J2000.0 Return: Geometric mean anomaly in radians. */ static double sun_geom_mean_anomaly(double t) { return RAD(357.52911 + t*(35999.05029 - t*0.0001537)); } /* Equation of center of the sun. t: Julian centuries since J2000.0 Return: Center(?) in radians */ static double sun_equation_of_center(double t) { /* Use the first three terms of the equation. */ double m = sun_geom_mean_anomaly(t); double c = sin(m)*(1.914602 - t*(0.004817 + 0.000014*t)) + sin(2*m)*(0.019993 - 0.000101*t) + sin(3*m)*0.000289; return RAD(c); } /* Geometric mean longitude of the sun. t: Julian centuries since J2000.0 Return: Geometric mean logitude in radians. */ static double sun_geom_mean_lon(double t) { /* FIXME returned value should always be positive */ return RAD(fmod(280.46646 + t*(36000.76983 + t*0.0003032), 360)); } /* True longitude of the sun. t: Julian centuries since J2000.0 Return: True longitude in radians */ static double sun_true_lon(double t) { double l_0 = sun_geom_mean_lon(t); double c = sun_equation_of_center(t); return l_0 + c; } /* Apparent longitude of the sun. (Right ascension). t: Julian centuries since J2000.0 Return: Apparent longitude in radians */ static double sun_apparent_lon(double t) { double o = sun_true_lon(t); return RAD(DEG(o) - 0.00569 - 0.00478*sin(RAD(125.04 - 1934.136*t))); } /* Mean obliquity of the ecliptic t: Julian centuries since J2000.0 Return: Mean obliquity in radians */ static double mean_ecliptic_obliquity(double t) { double sec = 21.448 - t*(46.815 + t*(0.00059 - t*0.001813)); return RAD(23.0 + (26.0 + (sec/60.0))/60.0); } /* Corrected obliquity of the ecliptic. t: Julian centuries since J2000.0 Return: Currected obliquity in radians */ static double obliquity_corr(double t) { double e_0 = mean_ecliptic_obliquity(t); double omega = 125.04 - t*1934.136; return RAD(DEG(e_0) + 0.00256*cos(RAD(omega))); } /* Declination of the sun. t: Julian centuries since J2000.0 Return: Declination in radians */ static double solar_declination(double t) { double e = obliquity_corr(t); double lambda = sun_apparent_lon(t); return asin(sin(e)*sin(lambda)); } /* Eccentricity of earth orbit. t: Julian centuries since J2000.0 Return: Eccentricity (unitless). */ static double earth_orbit_eccentricity(double t) { return 0.016708634 - t*(0.000042037 + t*0.0000001267); } /* Difference between true solar time and mean solar time. t: Julian centuries since J2000.0 Return: Difference in minutes */ static double equation_of_time(double t) { double epsilon = obliquity_corr(t); double l_0 = sun_geom_mean_lon(t); double e = earth_orbit_eccentricity(t); double m = sun_geom_mean_anomaly(t); double y = pow(tan(epsilon/2.0), 2.0); double eq_time = y*sin(2*l_0) - 2*e*sin(m) + 4*e*y*sin(m)*cos(2*l_0) - 0.5*y*y*sin(4*l_0) - 1.25*e*e*sin(2*m); return 4*DEG(eq_time); } /* Julian day from Julian centuries since J2000.0 */ static double jd_from_jcent(double t) { return 36525.0*t + 2451545.0; } /* Solar angular elevation at the given location and time. t: Julian centuries since J2000.0 lat: Latitude of location lon: Longitude of location Return: Solar angular elevation in radians */ static double solar_elevation_from_time(double t, double lat, double lon) { /* Minutes from midnight */ double jd = jd_from_jcent(t); double offset = (jd - round(jd) - 0.5)*1440.0; double eq_time = equation_of_time(t); double ha = RAD((720 - offset - eq_time)/4 - lon); double decl = solar_declination(t); return elevation_from_hour_angle(lat, decl, ha); } /* Julian day from unix epoch */ static double jd_from_epoch(double t) { return (t / 86400.0) + 2440587.5; } /* Solar angular elevation at the given location and time. date: Seconds since unix epoch lat: Latitude of location lon: Longitude of location Return: Solar angular elevation in degrees */ double solar_elevation(double date, double lat, double lon) { double jd = jd_from_epoch(date); return DEG(solar_elevation_from_time(jcent_from_jd(jd), lat, lon)); }