KHolidays

sunriseset.cpp
1/*
2 This file is part of the kholidays library.
3
4 Adapted from the Javascript found at https://www.esrl.noaa.gov/gmd/grad/solcalc
5 which is in the public domain.
6
7 SPDX-FileCopyrightText: 2012 Allen Winter <winter@kde.org>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10*/
11
12#include "sunriseset.h"
13
14#include <cmath>
15#include <numbers>
16
17static const double MaxLat = 89.99;
18static const double MaxLong = 179.99;
19
20using namespace KHolidays;
21using namespace SunRiseSet;
22
23static double degToRad(double degree)
24{
25 return (degree * std::numbers::pi) / 180.00;
26}
27
28static double radToDeg(double rad)
29{
30 return (rad * 180.0) / std::numbers::pi;
31}
32
33// convert Julian Day to centuries since J2000.0.
34static double calcTimeJulianCent(int jday)
35{
36 return (double(jday) - 2451545.0) / 36525.0;
37}
38
39// calculate the mean obliquity of the ecliptic (in degrees)
40static double calcMeanObliquityOfEcliptic(double t)
41{
42 double seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * 0.001813));
43 double e0 = 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
44 return e0; // in degrees
45}
46
47// calculate the corrected obliquity of the ecliptic (in degrees)
48static double calcObliquityCorrection(double t)
49{
50 double e0 = calcMeanObliquityOfEcliptic(t);
51 double omega = 125.04 - 1934.136 * t;
52 double e = e0 + 0.00256 * cos(degToRad(omega));
53 return e; // in degrees
54}
55
56// calculate the Geometric Mean Longitude of the Sun (in degrees)
57static double calcGeomMeanLongSun(double t)
58{
59 double L0 = 280.46646 + t * (36000.76983 + 0.0003032 * t);
60 while (L0 > 360.0) {
61 L0 -= 360.0;
62 }
63 while (L0 < 0.0) {
64 L0 += 360.0;
65 }
66 return L0; // in degrees
67}
68
69// calculate the Geometric Mean Anomaly of the Sun (in degrees)
70static double calcGeomMeanAnomalySun(double t)
71{
72 double M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
73 return M; // in degrees
74}
75
76// calculate the equation of center for the sun (in degrees)
77static double calcSunEqOfCenter(double t)
78{
79 double m = calcGeomMeanAnomalySun(t);
80 double mrad = degToRad(m);
81 double sinm = sin(mrad);
82 double sin2m = sin(mrad + mrad);
83 double sin3m = sin(mrad + mrad + mrad);
84 double C = (sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) //
85 + sin2m * (0.019993 - 0.000101 * t) //
86 + sin3m * 0.000289);
87 return C; // in degrees
88}
89
90// calculate the true longitude of the sun (in degrees)
91static double calcSunTrueLong(double t)
92{
93 double l0 = calcGeomMeanLongSun(t);
94 double c = calcSunEqOfCenter(t);
95 double O = l0 + c;
96 return O; // in degrees
97}
98
99// calculate the apparent longitude of the sun (in degrees)
100static double calcSunApparentLong(double t)
101{
102 double o = calcSunTrueLong(t);
103 double omega = 125.04 - 1934.136 * t;
104 double lambda = o - 0.00569 - 0.00478 * sin(degToRad(omega));
105 return lambda; // in degrees
106}
107
108// calculate the declination of the sun (in degrees)
109static double calcSunDeclination(double t)
110{
111 double e = calcObliquityCorrection(t);
112 double lambda = calcSunApparentLong(t);
113
114 double sint = sin(degToRad(e)) * sin(degToRad(lambda));
115 double theta = radToDeg(asin(sint));
116 return theta; // in degrees
117}
118
119// calculate the eccentricity of earth's orbit (unitless)
120static double calcEccentricityEarthOrbit(double t)
121{
122 double e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
123 return e; // unitless
124}
125
126// calculate the difference between true solar time and mean solar time
127//(output: equation of time in minutes of time)
128static double calcEquationOfTime(double t)
129{
130 double epsilon = calcObliquityCorrection(t);
131 double l0 = calcGeomMeanLongSun(t);
132 double e = calcEccentricityEarthOrbit(t);
133 double m = calcGeomMeanAnomalySun(t);
134
135 double y = tan(degToRad(epsilon) / 2.0);
136 y *= y;
137
138 double sin2l0 = sin(2.0 * degToRad(l0));
139 double sinm = sin(degToRad(m));
140 double cos2l0 = cos(2.0 * degToRad(l0));
141 double sin4l0 = sin(4.0 * degToRad(l0));
142 double sin2m = sin(2.0 * degToRad(m));
143
144 /* clang-format off */
145 double Etime = (y * sin2l0
146 - 2.0 * e * sinm
147 + 4.0 * e * y * sinm * cos2l0
148 - 0.5 * y * y * sin4l0
149 - 1.25 * e * e * sin2m);
150 /* clang-format on */
151 return radToDeg(Etime) * 4.0; // in minutes of time
152}
153
154// sun positions (in degree relative to the horizon) for certain events
155static constexpr const double Sunrise = -0.833; // see https://en.wikipedia.org/wiki/Sunrise
156static constexpr const double CivilTwilight = -6.0; // see https://en.wikipedia.org/wiki/Twilight
157
158static constexpr const double Up = 1.0;
159static constexpr const double Down = -1.0;
160
161// calculate the hour angle of the sun at angle @p sunHeight for the latitude (in radians)
162static double calcHourAngleSunrise(double latitude, double solarDec, double sunHeight)
163{
164 double latRad = degToRad(latitude);
165 double sdRad = degToRad(solarDec);
166 double HAarg = (cos(degToRad(90.0 - sunHeight)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad));
167 double HA = acos(HAarg);
168 return HA; // in radians (for sunset, use -HA)
169}
170
171static QTime calcSunEvent(const QDate &date, double latitude, double longitude, double sunHeight, double direction)
172{
173 latitude = qMax(qMin(MaxLat, latitude), -MaxLat);
174 longitude = qMax(qMin(MaxLong, longitude), -MaxLong);
175
176 double t = calcTimeJulianCent(date.toJulianDay());
177 double eqTime = calcEquationOfTime(t);
178 double solarDec = calcSunDeclination(t);
179 double hourAngle = direction * calcHourAngleSunrise(latitude, solarDec, sunHeight);
180 double delta = longitude + radToDeg(hourAngle);
181 if (std::isnan(delta)) {
182 return {};
183 }
184 QTime timeUTC(0, 0);
185 timeUTC = timeUTC.addSecs((720 - (4.0 * delta) - eqTime) * 60);
186
187 // round to the nearest minute
188 if (timeUTC.second() > 29) {
189 return QTime(timeUTC.hour(), timeUTC.minute()).addSecs(60);
190 }
191 return QTime(timeUTC.hour(), timeUTC.minute());
192}
193
194QTime KHolidays::SunRiseSet::utcSunrise(const QDate &date, double latitude, double longitude)
195{
196 return calcSunEvent(date, latitude, longitude, Sunrise, Up);
197}
198
199QTime KHolidays::SunRiseSet::utcSunset(const QDate &date, double latitude, double longitude)
200{
201 return calcSunEvent(date, latitude, longitude, Sunrise, Down);
202}
203
204QTime SunRiseSet::utcDawn(const QDate &date, double latitude, double longitude)
205{
206 return calcSunEvent(date, latitude, longitude, CivilTwilight, Up);
207}
208
209QTime SunRiseSet::utcDusk(const QDate &date, double latitude, double longitude)
210{
211 return calcSunEvent(date, latitude, longitude, CivilTwilight, Down);
212}
213
214// see https://en.wikipedia.org/wiki/Solar_zenith_angle
215bool SunRiseSet::isPolarDay(const QDate &date, double latitude)
216{
217 const double t = calcTimeJulianCent(date.toJulianDay());
218 const double solarDec = calcSunDeclination(t);
219 const double maxSolarZenithAngle = 180.0 - std::abs(latitude + solarDec);
220
221 return maxSolarZenithAngle <= 90.0 - Sunrise;
222}
223
224bool SunRiseSet::isPolarTwilight(const QDate &date, double latitude)
225{
226 const double t = calcTimeJulianCent(date.toJulianDay());
227 const double solarDec = calcSunDeclination(t);
228 const double minSolarZenithAngle = std::abs(latitude - solarDec);
229
230 return minSolarZenithAngle > 90.0 - Sunrise && minSolarZenithAngle <= 90.0 - CivilTwilight;
231}
232
233bool SunRiseSet::isPolarNight(const QDate &date, double latitude)
234{
235 const double t = calcTimeJulianCent(date.toJulianDay());
236 const double solarDec = calcSunDeclination(t);
237 const double minSolarZenithAngle = std::abs(latitude - solarDec);
238
239 return minSolarZenithAngle > 90.0 - CivilTwilight;
240}
Methods for determining the sunrise and sunset times for a given date and Earth location.
Definition sunriseset.h:23
KHOLIDAYS_EXPORT bool isPolarTwilight(const QDate &date, double latitude)
Checks whether it is polar twilight on day date at latitude.
KHOLIDAYS_EXPORT QTime utcSunset(const QDate &date, double latitude, double longitude)
Compute the sunset time (UTC) for a date and Earth location.
KHOLIDAYS_EXPORT QTime utcSunrise(const QDate &date, double latitude, double longitude)
Compute the sunrise time (UTC) for a date and Earth location.
KHOLIDAYS_EXPORT bool isPolarDay(const QDate &date, double latitude)
Checks whether it is polar day on day date at latitude.
KHOLIDAYS_EXPORT bool isPolarNight(const QDate &date, double latitude)
Checks whether it is polar night on day date at latitude.
KHOLIDAYS_EXPORT QTime utcDawn(const QDate &date, double latitude, double longitude)
Compute the civil dawn time (UTC) for a date and Earth location.
KHOLIDAYS_EXPORT QTime utcDusk(const QDate &date, double latitude, double longitude)
Compute the civil dawn time (UTC) for a date and Earth location.
qint64 toJulianDay() const const
QTime addSecs(int s) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:55:49 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.