File indexing completed on 2024-05-19 05:37:53
0001 /* 0002 SPDX-FileCopyrightText: 2009 Petri Damsten <damu@iki.fi> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "solarsystem.h" 0008 #include <QList> 0009 #include <math.h> 0010 0011 /* 0012 * Mathematics, ideas, public domain code used for these classes from: 0013 * https://www.stjarnhimlen.se/comp/tutorial.html 0014 * https://www.stjarnhimlen.se/comp/riset.html 0015 * https://www.esrl.noaa.gov/gmd/grad/solcalc/azel.html 0016 * https://www.esrl.noaa.gov/gmd/grad/solcalc/sunrise.html 0017 * http://web.archive.org/web/20080309162302/http://bodmas.org/astronomy/riset.html 0018 * moontool.c by John Walker 0019 * Wikipedia 0020 */ 0021 0022 Sun::Sun() 0023 : SolarSystemObject() 0024 { 0025 } 0026 0027 void Sun::calcForDateTime(const QDateTime &local, int offset) 0028 { 0029 SolarSystemObject::calcForDateTime(local, offset); 0030 0031 N = 0.0; 0032 i = 0.0; 0033 w = rev(282.9404 + 4.70935E-5 * m_day); 0034 a = 1.0; 0035 e = rev(0.016709 - 1.151E-9 * m_day); 0036 M = rev(356.0470 + 0.9856002585 * m_day); 0037 0038 calc(); 0039 } 0040 0041 void Sun::rotate(double *y, double *z) 0042 { 0043 *y *= cosd(m_obliquity); 0044 *z *= sind(m_obliquity); 0045 } 0046 0047 Moon::Moon(Sun *sun) 0048 : m_sun(sun) 0049 { 0050 } 0051 0052 void Moon::calcForDateTime(const QDateTime &local, int offset) 0053 { 0054 if (m_sun->dateTime() != local) { 0055 m_sun->calcForDateTime(local, offset); 0056 } 0057 0058 SolarSystemObject::calcForDateTime(local, offset); 0059 0060 N = rev(125.1228 - 0.0529538083 * m_day); 0061 i = 5.1454; 0062 w = rev(318.0634 + 0.1643573223 * m_day); 0063 a = 60.2666; 0064 e = 0.054900; 0065 M = rev(115.3654 + 13.0649929509 * m_day); 0066 0067 calc(); 0068 } 0069 0070 bool Moon::calcPerturbations(double *lo, double *la, double *r) 0071 { 0072 double Ms = m_sun->meanAnomaly(); 0073 double D = L - m_sun->meanLongitude(); 0074 double F = L - N; 0075 // clang-format off 0076 *lo += -1.274 * sind(M - 2 * D) 0077 +0.658 * sind(2 * D) 0078 -0.186 * sind(Ms) 0079 -0.059 * sind(2 * M - 2 * D) 0080 -0.057 * sind(M - 2 * D + Ms) 0081 +0.053 * sind(M + 2 * D) 0082 +0.046 * sind(2 * D - Ms) 0083 +0.041 * sind(M - Ms) 0084 -0.035 * sind(D) 0085 -0.031 * sind(M + Ms) 0086 -0.015 * sind(2 * F - 2 * D) 0087 +0.011 * sind(M - 4 * D); 0088 *la += -0.173 * sind(F - 2 * D) 0089 -0.055 * sind(M - F - 2 * D) 0090 -0.046 * sind(M + F - 2 * D) 0091 +0.033 * sind(F + 2 * D) 0092 +0.017 * sind(2 * M + F); 0093 *r += -0.58 * cosd(M - 2 * D) 0094 -0.46 * cosd(2 * D); 0095 // clang-format on 0096 return true; 0097 } 0098 0099 void Moon::topocentricCorrection(double *RA, double *dec) 0100 { 0101 double HA = rev(siderealTime() - *RA); 0102 double gclat = m_latitude - 0.1924 * sind(2 * m_latitude); 0103 double rho = 0.99833 + 0.00167 * cosd(2 * m_latitude); 0104 double mpar = asind(1 / rad); 0105 double g = atand(tand(gclat) / cosd(HA)); 0106 0107 *RA -= mpar * rho * cosd(gclat) * sind(HA) / cosd(*dec); 0108 *dec -= mpar * rho * sind(gclat) * sind(g - *dec) / sind(g); 0109 } 0110 0111 double Moon::phase() 0112 { 0113 return rev(m_eclipticLongitude - m_sun->lambda()); 0114 } 0115 0116 void Moon::rotate(double *y, double *z) 0117 { 0118 double t = *y; 0119 *y = t * cosd(m_obliquity) - *z * sind(m_obliquity); 0120 *z = t * sind(m_obliquity) + *z * cosd(m_obliquity); 0121 } 0122 0123 void SolarSystemObject::calc() 0124 { 0125 double x, y, z; 0126 double la, r; 0127 0128 L = rev(N + w + M); 0129 double E0 = 720.0; 0130 double E = M + (180.0 / M_PI) * e * sind(M) * (1.0 + e * cosd(M)); 0131 for (int j = 0; fabs(E0 - E) > 0.005 && j < 10; ++j) { 0132 E0 = E; 0133 E = E0 - (E0 - (180.0 / M_PI) * e * sind(E0) - M) / (1 - e * cosd(E0)); 0134 } 0135 x = a * (cosd(E) - e); 0136 y = a * sind(E) * sqrt(1.0 - e * e); 0137 r = sqrt(x * x + y * y); 0138 double v = rev(atan2d(y, x)); 0139 m_lambda = rev(v + w); 0140 x = r * (cosd(N) * cosd(m_lambda) - sind(N) * sind(m_lambda) * cosd(i)); 0141 y = r * (sind(N) * cosd(m_lambda) + cosd(N) * sind(m_lambda) * cosd(i)); 0142 z = r * sind(m_lambda); 0143 if (!qFuzzyCompare(i, 0.0)) { 0144 z *= sind(i); 0145 } 0146 toSpherical(x, y, z, &m_eclipticLongitude, &la, &r); 0147 if (calcPerturbations(&m_eclipticLongitude, &la, &r)) { 0148 toRectangular(m_eclipticLongitude, la, r, &x, &y, &z); 0149 } 0150 rotate(&y, &z); 0151 toSpherical(x, y, z, &RA, &dec, &rad); 0152 topocentricCorrection(&RA, &dec); 0153 0154 HA = rev(siderealTime() - RA); 0155 x = cosd(HA) * cosd(dec) * sind(m_latitude) - sind(dec) * cosd(m_latitude); 0156 y = sind(HA) * cosd(dec); 0157 z = cosd(HA) * cosd(dec) * cosd(m_latitude) + sind(dec) * sind(m_latitude); 0158 m_azimuth = atan2d(y, x) + 180.0; 0159 m_altitude = asind(z); 0160 } 0161 0162 double SolarSystemObject::siderealTime() 0163 { 0164 double UT = m_utc.time().hour() + m_utc.time().minute() / 60.0 + m_utc.time().second() / 3600.0; 0165 double GMST0 = rev(282.9404 + 4.70935E-5 * m_day + 356.0470 + 0.9856002585 * m_day + 180.0); 0166 return GMST0 + UT * 15.0 + m_longitude; 0167 } 0168 0169 void SolarSystemObject::calcForDateTime(const QDateTime &local, int offset) 0170 { 0171 m_local = local; 0172 m_utc = local.addSecs(-offset); 0173 m_day = 367 * m_utc.date().year() - (7 * (m_utc.date().year() + ((m_utc.date().month() + 9) / 12))) / 4 + (275 * m_utc.date().month()) / 9 0174 + m_utc.date().day() - 730530; 0175 m_day += m_utc.time().hour() / 24.0 + m_utc.time().minute() / (24.0 * 60.0) + m_utc.time().second() / (24.0 * 60.0 * 60.0); 0176 m_obliquity = 23.4393 - 3.563E-7 * m_day; 0177 } 0178 0179 SolarSystemObject::SolarSystemObject() 0180 : m_latitude(0.0) 0181 , m_longitude(0.0) 0182 { 0183 } 0184 0185 SolarSystemObject::~SolarSystemObject() 0186 { 0187 } 0188 0189 void SolarSystemObject::setPosition(double latitude, double longitude) 0190 { 0191 m_latitude = latitude; 0192 m_longitude = longitude; 0193 } 0194 0195 double SolarSystemObject::rev(double x) 0196 { 0197 return x - floor(x / 360.0) * 360.0; 0198 } 0199 0200 double SolarSystemObject::asind(double x) 0201 { 0202 return asin(x) * 180.0 / M_PI; 0203 } 0204 0205 double SolarSystemObject::sind(double x) 0206 { 0207 return sin(x * M_PI / 180.0); 0208 } 0209 0210 double SolarSystemObject::cosd(double x) 0211 { 0212 return cos(x * M_PI / 180.0); 0213 } 0214 0215 double SolarSystemObject::tand(double x) 0216 { 0217 return tan(x * M_PI / 180.0); 0218 } 0219 0220 double SolarSystemObject::atan2d(double y, double x) 0221 { 0222 return atan2(y, x) * 180.0 / M_PI; 0223 } 0224 0225 double SolarSystemObject::atand(double x) 0226 { 0227 return atan(x) * 180.0 / M_PI; 0228 } 0229 0230 void SolarSystemObject::toRectangular(double lo, double la, double r, double *x, double *y, double *z) 0231 { 0232 *x = r * cosd(lo) * cosd(la); 0233 *y = r * sind(lo) * cosd(la); 0234 *z = r * sind(la); 0235 } 0236 0237 void SolarSystemObject::toSpherical(double x, double y, double z, double *lo, double *la, double *r) 0238 { 0239 *r = sqrt(x * x + y * y + z * z); 0240 *la = asind(z / *r); 0241 *lo = rev(atan2d(y, x)); 0242 } 0243 0244 QPair<double, double> SolarSystemObject::zeroPoints(QPointF p1, QPointF p2, QPointF p3) 0245 { 0246 double a = ((p2.y() - p1.y()) * (p1.x() - p3.x()) + (p3.y() - p1.y()) * (p2.x() - p1.x())) 0247 / ((p1.x() - p3.x()) * (p2.x() * p2.x() - p1.x() * p1.x()) + (p2.x() - p1.x()) * (p3.x() * p3.x() - p1.x() * p1.x())); 0248 double b = ((p2.y() - p1.y()) - a * (p2.x() * p2.x() - p1.x() * p1.x())) / (p2.x() - p1.x()); 0249 double c = p1.y() - a * p1.x() * p1.x() - b * p1.x(); 0250 double discriminant = b * b - 4.0 * a * c; 0251 double z1 = -1.0, z2 = -1.0; 0252 0253 if (discriminant >= 0.0) { 0254 z1 = (-b + sqrt(discriminant)) / (2 * a); 0255 z2 = (-b - sqrt(discriminant)) / (2 * a); 0256 } 0257 return QPair<double, double>(z1, z2); 0258 } 0259 0260 QList<QPair<QDateTime, QDateTime>> SolarSystemObject::timesForAngles(const QList<double> &angles, const QDateTime &dt, int offset) 0261 { 0262 QList<double> altitudes; 0263 QDate d = dt.date(); 0264 QDateTime local(d, QTime(0, 0)); 0265 for (int j = 0; j <= 25; ++j) { 0266 calcForDateTime(local, offset); 0267 altitudes.append(altitude()); 0268 local = local.addSecs(60 * 60); 0269 } 0270 QList<QPair<QDateTime, QDateTime>> result; 0271 QTime rise, set; 0272 foreach (double angle, angles) { 0273 for (int j = 3; j <= 25; j += 2) { 0274 QPointF p1((j - 2) * 60 * 60, altitudes[j - 2] - angle); 0275 QPointF p2((j - 1) * 60 * 60, altitudes[j - 1] - angle); 0276 QPointF p3(j * 60 * 60, altitudes[j] - angle); 0277 QPair<double, double> z = zeroPoints(p1, p2, p3); 0278 if (z.first > p1.x() && z.first < p3.x()) { 0279 if (p1.y() < 0.0) { 0280 rise = QTime(0, 0).addSecs(z.first); 0281 } else { 0282 set = QTime(0, 0).addSecs(z.first); 0283 } 0284 } 0285 if (z.second > p1.x() && z.second < p3.x()) { 0286 if (p3.y() < 0.0) { 0287 set = QTime(0, 0).addSecs(z.second); 0288 } else { 0289 rise = QTime(0, 0).addSecs(z.second); 0290 } 0291 } 0292 } 0293 result.append(QPair<QDateTime, QDateTime>(QDateTime(d, rise), QDateTime(d, set))); 0294 } 0295 return result; 0296 } 0297 0298 double SolarSystemObject::calcElevation() 0299 { 0300 double refractionCorrection; 0301 0302 if (m_altitude > 85.0) { 0303 refractionCorrection = 0.0; 0304 } else { 0305 double te = tand(m_altitude); 0306 if (m_altitude > 5.0) { 0307 refractionCorrection = 58.1 / te - 0.07 / (te * te * te) + 0.000086 / (te * te * te * te * te); 0308 } else if (m_altitude > -0.575) { 0309 refractionCorrection = 1735.0 + m_altitude * (-518.2 + m_altitude * (103.4 + m_altitude * (-12.79 + m_altitude * 0.711))); 0310 } else { 0311 refractionCorrection = -20.774 / te; 0312 } 0313 refractionCorrection = refractionCorrection / 3600.0; 0314 } 0315 return m_altitude + refractionCorrection; 0316 }