|
| 1 | +/* |
| 2 | +Adopted from SunCalc by Vladimir Agafonkin |
| 3 | +https://github.com/mourner/suncalc |
| 4 | +*/ |
| 5 | + |
| 6 | +#ifndef airsim_core_EarthCelestial_hpp |
| 7 | +#define airsim_core_EarthCelestial_hpp |
| 8 | + |
| 9 | + |
| 10 | +#include "common/Common.hpp" |
| 11 | +#include "EarthUtils.hpp" |
| 12 | +#include <chrono> |
| 13 | +#include <ctime> |
| 14 | + |
| 15 | +namespace msr { namespace airlib { |
| 16 | + |
| 17 | + |
| 18 | +class EarthCelestial { |
| 19 | +public: |
| 20 | + |
| 21 | + struct CelestialGlobalCoord |
| 22 | + { |
| 23 | + double declination; |
| 24 | + double rightAscension; |
| 25 | + double distance = Utils::nan<double>(); |
| 26 | + double parallacticAngle = Utils::nan<double>(); |
| 27 | + }; |
| 28 | + |
| 29 | + struct CelestialLocalCoord |
| 30 | + { |
| 31 | + double azimuth; |
| 32 | + double altitude; |
| 33 | + double distance = Utils::nan<double>(); |
| 34 | + double parallacticAngle = Utils::nan<double>(); |
| 35 | + }; |
| 36 | + |
| 37 | + struct CelestialPhase |
| 38 | + { |
| 39 | + double fraction; |
| 40 | + double phase; |
| 41 | + double angle; |
| 42 | + }; |
| 43 | + |
| 44 | + |
| 45 | +public: |
| 46 | + static CelestialLocalCoord getSunCoordinates(uint64_t date, double lat, double lng) |
| 47 | + { |
| 48 | + double lw = Utils::degreesToRadians(-lng); |
| 49 | + double phi = Utils::degreesToRadians(lat); |
| 50 | + double d = toDays(date); |
| 51 | + |
| 52 | + CelestialGlobalCoord c = getGlobalSunCoords(d); |
| 53 | + double H = siderealTime(d, lw) - c.rightAscension; |
| 54 | + |
| 55 | + CelestialLocalCoord coord; |
| 56 | + coord.azimuth = Utils::radiansToDegrees( azimuth(H, phi, c.declination) ) + 180.0; |
| 57 | + coord.altitude = Utils::radiansToDegrees( altitude(H, phi, c.declination) ); |
| 58 | + |
| 59 | + return coord; |
| 60 | + } |
| 61 | + |
| 62 | + |
| 63 | + static CelestialLocalCoord getMoonCoordinates(uint64_t date, double lat, double lng) |
| 64 | + { |
| 65 | + |
| 66 | + double lw = Utils::degreesToRadians(-lng); |
| 67 | + double phi = Utils::degreesToRadians(lat); |
| 68 | + double d = toDays(date); |
| 69 | + |
| 70 | + CelestialGlobalCoord c = getGlobalMoonCoords(d); |
| 71 | + double H = siderealTime(d, lw) - c.rightAscension; |
| 72 | + |
| 73 | + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. |
| 74 | + double pa = std::atan2(std::sin(H), std::tan(phi) * std::cos(c.declination) - std::sin(c.declination) * std::cos(H)); |
| 75 | + |
| 76 | + double h = altitude(H, phi, c.declination); |
| 77 | + h = h + astroRefraction(h); // altitude correction for refraction |
| 78 | + |
| 79 | + CelestialLocalCoord coord; |
| 80 | + coord.azimuth = Utils::radiansToDegrees( azimuth(H, phi, c.declination) ); |
| 81 | + coord.altitude = Utils::radiansToDegrees(h); |
| 82 | + coord.distance = c.distance; |
| 83 | + coord.parallacticAngle = Utils::radiansToDegrees(pa); |
| 84 | + return coord; |
| 85 | + }; |
| 86 | + |
| 87 | + |
| 88 | + // calculations for illumination parameters of the moon, |
| 89 | + // based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and |
| 90 | + // Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. |
| 91 | + static CelestialPhase getMoonPhase(uint64_t date) |
| 92 | + { |
| 93 | + double d = toDays(date); |
| 94 | + CelestialGlobalCoord s = getGlobalSunCoords(d); |
| 95 | + CelestialGlobalCoord m = getGlobalMoonCoords(d); |
| 96 | + |
| 97 | + double sdist = EarthUtils::DistanceFromSun / 1000; // distance from Earth to Sun in km |
| 98 | + |
| 99 | + double phi = std::acos(std::sin(s.declination) * std::sin(m.declination) + std::cos(s.declination) * std::cos(m.declination) * std::cos(s.rightAscension - m.rightAscension)); |
| 100 | + double inc = std::atan2(sdist * std::sin(phi), m.distance - sdist * std::cos(phi)); |
| 101 | + double angle = std::atan2(std::cos(s.declination) * std::sin(s.rightAscension - m.rightAscension), std::sin(s.declination) * std::cos(m.declination) - std::cos(s.declination) * std::sin(m.declination) * std::cos(s.rightAscension - m.rightAscension)); |
| 102 | + |
| 103 | + CelestialPhase moonPhase; |
| 104 | + moonPhase.fraction = (1 + cos(inc)) / 2; |
| 105 | + moonPhase.phase = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / M_PI; |
| 106 | + moonPhase.angle = angle; |
| 107 | + return moonPhase; |
| 108 | + }; |
| 109 | + |
| 110 | + |
| 111 | +private: |
| 112 | + |
| 113 | + static double toDays(uint64_t date) |
| 114 | + { |
| 115 | + static constexpr double kJulianDaysOnY2000 = 2451545; |
| 116 | + static constexpr double kDaysToHours = 60 * 60 * 24; |
| 117 | + static constexpr double kJulianDaysOnEpoch = 2440588; |
| 118 | + |
| 119 | + double julian_days = date / kDaysToHours - 0.5 + kJulianDaysOnEpoch;; |
| 120 | + return julian_days - kJulianDaysOnY2000; |
| 121 | + } |
| 122 | + |
| 123 | + |
| 124 | + static double rightAscension(double l, double b) |
| 125 | + { |
| 126 | + return std::atan2(std::sin(l) * std::cos(EarthUtils::Obliquity) - std::tan(b) * std::sin(EarthUtils::Obliquity), std::cos(l)); |
| 127 | + } |
| 128 | + |
| 129 | + static double declination(double l, double b) |
| 130 | + { |
| 131 | + return std::asin(std::sin(b) * std::cos(EarthUtils::Obliquity) + std::cos(b) * std::sin(EarthUtils::Obliquity) * std::sin(l)); |
| 132 | + } |
| 133 | + |
| 134 | + static double azimuth(double H, double phi, double declination) |
| 135 | + { |
| 136 | + return std::atan2(std::sin(H), std::cos(H) * std::sin(phi) - std::tan(declination) * std::cos(phi)); |
| 137 | + } |
| 138 | + |
| 139 | + static double altitude(double H, double phi, double declination) |
| 140 | + { |
| 141 | + return std::asin(std::sin(phi) * std::sin(declination) + std::cos(phi) * std::cos(declination) * std::cos(H)); |
| 142 | + } |
| 143 | + |
| 144 | + static double siderealTime(double d, double lw) |
| 145 | + { |
| 146 | + return Utils::degreesToRadians((280.16 + 360.9856235 * d)) - lw; |
| 147 | + } |
| 148 | + |
| 149 | + static double astroRefraction(double h) |
| 150 | + { |
| 151 | + if (h < 0) // the following formula works for positive altitudes only. |
| 152 | + h = 0; // if h = -0.08901179 a div/0 would occur. |
| 153 | + |
| 154 | + // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. |
| 155 | + // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: |
| 156 | + return 0.0002967 / std::tan(h + 0.00312536 / (h + 0.08901179)); |
| 157 | + } |
| 158 | + |
| 159 | + |
| 160 | + static double solarMeanAnomaly(double d) |
| 161 | + { |
| 162 | + return Utils::degreesToRadians((357.5291 + 0.98560028 * d)); |
| 163 | + } |
| 164 | + |
| 165 | + static double eclipticLongitude(double M) |
| 166 | + { |
| 167 | + double C = Utils::degreesToRadians((1.9148 * std::sin(M) + 0.02 * std::sin(2 * M) + 0.0003 * std::sin(3 * M))); // equation of center |
| 168 | + |
| 169 | + return M + C + EarthUtils::Perihelion + M_PI; |
| 170 | + } |
| 171 | + |
| 172 | + static CelestialGlobalCoord getGlobalSunCoords(double d) |
| 173 | + { |
| 174 | + double M = solarMeanAnomaly(d); |
| 175 | + double L = eclipticLongitude(M); |
| 176 | + |
| 177 | + CelestialGlobalCoord sunCoords; |
| 178 | + sunCoords.declination = declination(L, 0); |
| 179 | + sunCoords.rightAscension = rightAscension(L, 0); |
| 180 | + |
| 181 | + return sunCoords; |
| 182 | + } |
| 183 | + |
| 184 | + |
| 185 | + // moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas |
| 186 | + static CelestialGlobalCoord getGlobalMoonCoords(double d) |
| 187 | + { |
| 188 | + // geocentric ecliptic coordinates of the moon |
| 189 | + |
| 190 | + double L = Utils::degreesToRadians((218.316 + 13.176396 * d)); // ecliptic longitude |
| 191 | + double M = Utils::degreesToRadians((134.963 + 13.064993 * d)); // mean anomaly |
| 192 | + double F = Utils::degreesToRadians((93.272 + 13.229350 * d)); // mean distance |
| 193 | + |
| 194 | + double l = L + Utils::degreesToRadians(6.289 * std::sin(M)); // longitude |
| 195 | + double b = Utils::degreesToRadians(5.128 * std::sin(F)); // latitude |
| 196 | + double dt = 385001 - 20905 * std::cos(M); // distance to the moon in km |
| 197 | + |
| 198 | + CelestialGlobalCoord moonCoords; |
| 199 | + moonCoords.rightAscension = rightAscension(l, b); |
| 200 | + moonCoords.declination = declination(l, b); |
| 201 | + moonCoords.distance = dt; |
| 202 | + |
| 203 | + return moonCoords; |
| 204 | + } |
| 205 | +}; |
| 206 | + |
| 207 | + |
| 208 | +}} //namespace |
| 209 | +#endif |
0 commit comments