KHolidays

lunarphase.cpp
1/*
2 This file is part of the kholidays library.
3
4 SPDX-FileCopyrightText: 2004, 2007, 2009 Allen Winter <winter@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "lunarphase.h"
10
11#include <QCoreApplication>
12#include <QDateTime>
13#include <QTimeZone>
14
15using namespace KHolidays;
16
18{
19 return phaseName(phaseAtDate(date));
20}
21
23{
24 switch (phase) {
25 case NewMoon:
26 return (QCoreApplication::translate("LunarPhase", "New Moon"));
27 case FullMoon:
28 return (QCoreApplication::translate("LunarPhase", "Full Moon"));
29 case FirstQuarter:
30 return (QCoreApplication::translate("LunarPhase", "First Quarter Moon"));
31 case LastQuarter:
32 return (QCoreApplication::translate("LunarPhase", "Last Quarter Moon"));
33 case None:
34 return QString();
35 case WaxingCrescent:
36 return (QCoreApplication::translate("LunarPhase", "Waxing Crescent"));
37 case WaxingGibbous:
38 return (QCoreApplication::translate("LunarPhase", "Waxing Gibbous"));
39 case WaningGibbous:
40 return (QCoreApplication::translate("LunarPhase", "Waning Gibbous"));
41 case WaningCrescent:
42 return (QCoreApplication::translate("LunarPhase", "Waning Crescent"));
43 }
44 return QString();
45}
46
47static double phaseAngle(int64_t unixmsec);
48
50{
51 Phase retPhase = None;
52
53 const QTime midnight(0, 0, 0);
54 const QDateTime todayStart(date, midnight, QTimeZone::utc());
55 const double startAngle = phaseAngle(todayStart.toMSecsSinceEpoch());
56 const QDateTime todayEnd(date.addDays(1), midnight, QTimeZone::utc());
57 const double endAngle = phaseAngle(todayEnd.toMSecsSinceEpoch());
58
59 if (startAngle > endAngle) {
60 retPhase = NewMoon;
61 } else if (startAngle < 90.0 && endAngle > 90.0) {
62 retPhase = FirstQuarter;
63 } else if (startAngle < 180.0 && endAngle > 180.0) {
64 retPhase = FullMoon;
65 } else if (startAngle < 270.0 && endAngle > 270.0) {
66 retPhase = LastQuarter;
67 } else if (endAngle < 90.0) {
68 retPhase = WaxingCrescent;
69 } else if (endAngle < 180.0) {
70 retPhase = WaxingGibbous;
71 } else if (endAngle < 270.0) {
72 retPhase = WaningGibbous;
73 } else if (endAngle < 360.0) {
74 retPhase = WaningCrescent;
75 }
76
77 return retPhase;
78}
79
80/*
81 Algorithm adapted from Moontool by John Walker.
82 Moontool is in the public domain: "Do what thou wilt shall be the whole of the law".
83 Unnecessary calculations have been removed.
84 See https://fourmilab.ch/moontool .
85*/
86
87#include <cmath>
88
89constexpr int64_t epoch = 315446400000; // "1980 January 0.0", a.k.a. midnight on 1979-12-31, in ms Unix time
90constexpr double elonge = 278.833540; // ecliptic longitude of sun at epoch
91constexpr double elongp = 282.596403; // ecliptic longitude of sun at perigee
92constexpr double earthEcc = 0.016718; // eccentricity of earth orbit
93static const double ecPrefactor = sqrt((1 + earthEcc) / (1 - earthEcc));
94
95constexpr double mmlong = 64.975464; // mean longitude of moon at epoch
96constexpr double mmlongp = 349.383063; // mean longitude of moon at perigee
97
98constexpr double PI = 3.14159265358979323846;
99
100static double fixAngle(double degrees)
101{
102 return degrees - floor(degrees / 360.0) * 360.0;
103}
104
105static constexpr double radToDeg(double rad)
106{
107 return rad / PI * 180.0;
108}
109
110static constexpr double degToRad(double deg)
111{
112 return deg / 180.0 * PI;
113}
114
115constexpr double epsilon = 1e-6;
116
117static double kepler(double m, double ecc)
118{
119 double mrad = degToRad(m);
120 double e = mrad;
121 double delta;
122 do {
123 delta = e - ecc * sin(e) - mrad;
124 e -= delta / (1 - ecc * cos(e));
125 } while (abs(delta) > epsilon);
126 return e;
127}
128
129static double phaseAngle(int64_t unixmsec)
130{
131 int64_t msec = unixmsec - epoch;
132
133 double sunMeanAnomaly = fixAngle(msec * (360.0 / 365.2422 / 86400000.0) + elonge - elongp);
134 double trueAnomaly = 2 * radToDeg(atan(ecPrefactor * tan(kepler(sunMeanAnomaly, earthEcc) / 2)));
135 double geocentricEclipticLong = fixAngle(trueAnomaly + elongp);
136
137 double moonMeanLong = fixAngle(msec * (13.1763966 / 86400000.0) + mmlong);
138 double moonMeanAnomaly = fixAngle(moonMeanLong - msec * (0.1114041 / 86400000.0) - mmlongp);
139 double evection = 1.2739 * sin(degToRad(2 * (moonMeanLong - geocentricEclipticLong) - moonMeanAnomaly));
140 double annualEquation = 0.1858 * sin(degToRad(sunMeanAnomaly));
141 double a3 = 0.37 * sin(degToRad(sunMeanAnomaly));
142 double correctedAnomaly = moonMeanAnomaly + evection - annualEquation - a3;
143 double centreCorrection = 6.2886 * sin(degToRad(correctedAnomaly));
144 double a4 = 0.214 * sin(degToRad(2 * correctedAnomaly));
145 double correctedLong = moonMeanLong + evection + centreCorrection - annualEquation + a4;
146 double variation = 0.6583 * sin(degToRad(2 * (correctedLong - geocentricEclipticLong)));
147 double trueLong = correctedLong + variation;
148
149 return fixAngle(trueLong - geocentricEclipticLong);
150}
151
152#include "moc_lunarphase.cpp"
static QString phaseNameAtDate(const QDate &date)
Return the lunar phase as a text string for the specified date.
static Phase phaseAtDate(const QDate &date)
Return the lunar phase for the specified Gregorian date.
Phase
Phases of the moon, in traditional English notation.
Definition lunarphase.h:54
@ FullMoon
Full moon phase.
Definition lunarphase.h:58
@ FirstQuarter
First quarter of moon phase.
Definition lunarphase.h:56
@ None
Indication for error.
Definition lunarphase.h:59
@ LastQuarter
Last quarter of moon phase.
Definition lunarphase.h:57
@ NewMoon
New moon phase.
Definition lunarphase.h:55
static QString phaseName(Phase phase)
Return the string representation of phase.
constexpr double radToDeg(double rad)
constexpr double degToRad(double deg)
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
QDate addDays(qint64 ndays) const const
qint64 toMSecsSinceEpoch() const const
QTimeZone utc()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:37 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.