File indexing completed on 2025-01-19 03:40:33
0001 /* 0002 This file is part of the kholidays library. 0003 0004 SPDX-FileCopyrightText: 2004, 2007, 2009 Allen Winter <winter@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "lunarphase.h" 0010 0011 #include <QCoreApplication> 0012 #include <QDateTime> 0013 #include <QTimeZone> 0014 0015 using namespace KHolidays; 0016 0017 QString LunarPhase::phaseNameAtDate(const QDate &date) 0018 { 0019 return phaseName(phaseAtDate(date)); 0020 } 0021 0022 QString LunarPhase::phaseName(LunarPhase::Phase phase) 0023 { 0024 switch (phase) { 0025 case NewMoon: 0026 return (QCoreApplication::translate("LunarPhase", "New Moon")); 0027 case FullMoon: 0028 return (QCoreApplication::translate("LunarPhase", "Full Moon")); 0029 case FirstQuarter: 0030 return (QCoreApplication::translate("LunarPhase", "First Quarter Moon")); 0031 case LastQuarter: 0032 return (QCoreApplication::translate("LunarPhase", "Last Quarter Moon")); 0033 case None: 0034 return QString(); 0035 case WaxingCrescent: 0036 return (QCoreApplication::translate("LunarPhase", "Waxing Crescent")); 0037 case WaxingGibbous: 0038 return (QCoreApplication::translate("LunarPhase", "Waxing Gibbous")); 0039 case WaningGibbous: 0040 return (QCoreApplication::translate("LunarPhase", "Waning Gibbous")); 0041 case WaningCrescent: 0042 return (QCoreApplication::translate("LunarPhase", "Waning Crescent")); 0043 } 0044 return QString(); 0045 } 0046 0047 static double phaseAngle(int64_t unixmsec); 0048 0049 LunarPhase::Phase LunarPhase::phaseAtDate(const QDate &date) 0050 { 0051 Phase retPhase = None; 0052 0053 const QTime midnight(0, 0, 0); 0054 const QDateTime todayStart(date, midnight, QTimeZone::utc()); 0055 const double startAngle = phaseAngle(todayStart.toMSecsSinceEpoch()); 0056 const QDateTime todayEnd(date.addDays(1), midnight, QTimeZone::utc()); 0057 const double endAngle = phaseAngle(todayEnd.toMSecsSinceEpoch()); 0058 0059 if (startAngle > endAngle) { 0060 retPhase = NewMoon; 0061 } else if (startAngle < 90.0 && endAngle > 90.0) { 0062 retPhase = FirstQuarter; 0063 } else if (startAngle < 180.0 && endAngle > 180.0) { 0064 retPhase = FullMoon; 0065 } else if (startAngle < 270.0 && endAngle > 270.0) { 0066 retPhase = LastQuarter; 0067 } else if (endAngle < 90.0) { 0068 retPhase = WaxingCrescent; 0069 } else if (endAngle < 180.0) { 0070 retPhase = WaxingGibbous; 0071 } else if (endAngle < 270.0) { 0072 retPhase = WaningGibbous; 0073 } else if (endAngle < 360.0) { 0074 retPhase = WaningCrescent; 0075 } 0076 0077 return retPhase; 0078 } 0079 0080 /* 0081 Algorithm adapted from Moontool by John Walker. 0082 Moontool is in the public domain: "Do what thou wilt shall be the whole of the law". 0083 Unnecessary calculations have been removed. 0084 See https://fourmilab.ch/moontool . 0085 */ 0086 0087 #include <cmath> 0088 0089 constexpr int64_t epoch = 315446400000; // "1980 January 0.0", a.k.a. midnight on 1979-12-31, in ms Unix time 0090 constexpr double elonge = 278.833540; // ecliptic longitude of sun at epoch 0091 constexpr double elongp = 282.596403; // ecliptic longitude of sun at perigee 0092 constexpr double earthEcc = 0.016718; // eccentricity of earth orbit 0093 static const double ecPrefactor = sqrt((1 + earthEcc) / (1 - earthEcc)); 0094 0095 constexpr double mmlong = 64.975464; // mean longitude of moon at epoch 0096 constexpr double mmlongp = 349.383063; // mean longitude of moon at perigee 0097 0098 constexpr double PI = 3.14159265358979323846; 0099 0100 static double fixAngle(double degrees) 0101 { 0102 return degrees - floor(degrees / 360.0) * 360.0; 0103 } 0104 0105 static constexpr double radToDeg(double rad) 0106 { 0107 return rad / PI * 180.0; 0108 } 0109 0110 static constexpr double degToRad(double deg) 0111 { 0112 return deg / 180.0 * PI; 0113 } 0114 0115 constexpr double epsilon = 1e-6; 0116 0117 static double kepler(double m, double ecc) 0118 { 0119 double mrad = degToRad(m); 0120 double e = mrad; 0121 double delta; 0122 do { 0123 delta = e - ecc * sin(e) - mrad; 0124 e -= delta / (1 - ecc * cos(e)); 0125 } while (abs(delta) > epsilon); 0126 return e; 0127 } 0128 0129 static double phaseAngle(int64_t unixmsec) 0130 { 0131 int64_t msec = unixmsec - epoch; 0132 0133 double sunMeanAnomaly = fixAngle(msec * (360.0 / 365.2422 / 86400000.0) + elonge - elongp); 0134 double trueAnomaly = 2 * radToDeg(atan(ecPrefactor * tan(kepler(sunMeanAnomaly, earthEcc) / 2))); 0135 double geocentricEclipticLong = fixAngle(trueAnomaly + elongp); 0136 0137 double moonMeanLong = fixAngle(msec * (13.1763966 / 86400000.0) + mmlong); 0138 double moonMeanAnomaly = fixAngle(moonMeanLong - msec * (0.1114041 / 86400000.0) - mmlongp); 0139 double evection = 1.2739 * sin(degToRad(2 * (moonMeanLong - geocentricEclipticLong) - moonMeanAnomaly)); 0140 double annualEquation = 0.1858 * sin(degToRad(sunMeanAnomaly)); 0141 double a3 = 0.37 * sin(degToRad(sunMeanAnomaly)); 0142 double correctedAnomaly = moonMeanAnomaly + evection - annualEquation - a3; 0143 double centreCorrection = 6.2886 * sin(degToRad(correctedAnomaly)); 0144 double a4 = 0.214 * sin(degToRad(2 * correctedAnomaly)); 0145 double correctedLong = moonMeanLong + evection + centreCorrection - annualEquation + a4; 0146 double variation = 0.6583 * sin(degToRad(2 * (correctedLong - geocentricEclipticLong))); 0147 double trueLong = correctedLong + variation; 0148 0149 return fixAngle(trueLong - geocentricEclipticLong); 0150 } 0151 0152 #include "moc_lunarphase.cpp"