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}
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.
constexpr double radToDeg(double rad)
constexpr double degToRad(double deg)
qint64 toJulianDay() const const
QTime addSecs(int s) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:11:11 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.