Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
Sun.cpp
Go to the documentation of this file.
1
2
3#include "Headers/Sun.h"
4
5namespace Divide {
6
7namespace {
9
10 constexpr D64 SunDia = 0.53; // Sun radius degrees
11 constexpr D64 AirRefr = 34.0 / 60.0; // Atmospheric refraction degrees
12 constexpr D64 TwoPi = 2 * M_PI;
13 // Sun computation coefficients
14 constexpr D64 Longitude_A = 282.9404;
15 constexpr D64 Longitude_B = 4.70935E-5;
16 constexpr D64 Mean_A = 356.047;
17 constexpr D64 Mean_B = 0.9856002585;
18 constexpr D64 Eccentricity_A = 0.016709;
19 constexpr D64 Eccentricity_B = 1.151E-9;
21 constexpr D64 Oblique_B = Angle::DegreesToRadians(3.563E-7);
24
25 constexpr D64 FNrange(const D64 x) noexcept {
26 const D64 b = x / TwoPi;
27 const D64 a = TwoPi * (b - to_I32(b));
28 return a < 0 ? TwoPi + a : a;
29 }
30
31 // Calculating the hourangle
32 D64 f0(const D64 lat, const D64 declin) noexcept {
34 if (lat < 0.0) {
35 dfo = -dfo; // Southern hemisphere
36 }
37 D64 fo = std::tan(declin + dfo) * std::tan(Angle::DegreesToRadians(lat));
38 if (fo > 0.99999) {
39 fo = 1.0; // to avoid overflow //
40 }
41 return std::asin(fo) + M_PI_2;
42 }
43
44 D64 FNsun(const D64 d, D64& RA, D64& delta, D64& L) noexcept {
45 // mean longitude of the Sun
46 const D64 W_DEG = Longitude_A + Longitude_B * d;
47 const D64 W_RAD = Angle::DegreesToRadians(W_DEG);
48 const D64 M_DEG = Mean_A + Mean_B * d;
49 const D64 M_RAD = Angle::DegreesToRadians(M_DEG);
50 // mean anomaly of the Sun
51 const D64 g = FNrange(M_RAD);
52 // eccentricity
53 const D64 ECC_RAD = Eccentricity_A - Eccentricity_B * d;
54 const D64 ECC_DEG = Angle::RadiansToDegrees(ECC_RAD);
55 // Obliquity of the ecliptic
56 const D64 obliq = Oblique_A - Oblique_B * d;
57
58 const D64 E_DEG = M_DEG + ECC_DEG * std::sin(g) * (1.0 + ECC_RAD * std::cos(g));
59 const D64 E_RAD = FNrange(Angle::DegreesToRadians(E_DEG));
60 D64 x = std::cos(E_RAD) - ECC_RAD;
61 D64 y = std::sin(E_RAD) * Sqrt(1.0 - SQUARED(ECC_RAD));
62
63 const D64 r = Sqrt(SQUARED(x) + SQUARED(y));
64 const D64 v = Angle::RadiansToDegrees(std::atan2(y, x));
65
66 // longitude of sun
67 const D64 lonsun = v + W_DEG;
68 const D64 lonsun_rad = Angle::DegreesToRadians(lonsun - 360.0 * (lonsun > 360.0 ? 1 : 0));
69
70 // sun's ecliptic rectangular coordinates
71 x = r * std::cos(lonsun_rad);
72 y = r * std::sin(lonsun_rad);
73 const D64 yequat = y * std::cos(obliq);
74 const D64 zequat = y * std::sin(obliq);
75
76 // Sun's mean longitude
77 L = FNrange(W_RAD + M_RAD);
78 delta = std::atan2(zequat, Sqrt(SQUARED(x) + SQUARED(yequat)));
79 RA = Angle::RadiansToDegrees(std::atan2(yequat, x));
80
81 // Ecliptic longitude of the Sun
82 return FNrange(L + Ecliptic_A * std::sin(g) + Ecliptic_B * std::sin(2 * g));
83 }
84}
85
86SunInfo SunPosition::CalculateSunPosition(const struct tm &dateTime, const F32 latitude, const F32 longitude) {
87
88 const D64 longit = to_D64(longitude);
89 const D64 latit = to_D64(latitude);
90 const D64 latit_rad = Angle::DegreesToRadians(latit);
91
92 // this is Y2K compliant method
93 const I32 year = dateTime.tm_year + 1900;
94 const I32 m = dateTime.tm_mon + 1;
95 const I32 day = dateTime.tm_mday;
96 // clock time just now
97 const auto h = dateTime.tm_hour + dateTime.tm_min / 60.0;
98 const auto tzone = std::chrono::current_zone()->get_info( std::chrono::system_clock::now() ).offset.count() / 3600.0;
99
100 // year = 1990; m=4; day=19; h=11.99; // local time
101 const auto UT = h - tzone; // universal time
102
103 // Get the days to J2000
104 // h is UT in decimal hours
105 // FNday only works between 1901 to 2099 - see Meeus chapter 7
106 const D64 jd = [year, h, m, day]() noexcept {
107 const I32 luku = -7 * (year + (m + 9) / 12) / 4 + 275 * m / 9 + day;
108 // type casting necessary on PC DOS and TClite to avoid overflow
109 return to_D64(luku + year * 367) - 730530.0 + h / 24.0;
110 }();
111
112 // Use FNsun to find the ecliptic longitude of the Sun
113 D64 RA = 0.0, delta = 0.0, L = 0.0;
114 const D64 lambda = FNsun(jd, RA, delta, L);
115 const D64 cos_delta = std::cos(delta);
116 const D64 delta_deg = Angle::RadiansToDegrees(delta);
117
118 // Obliquity of the ecliptic
119 const D64 obliq = Oblique_A - Oblique_B * jd;
120
121 // Sidereal time at Greenwich meridian
122 const D64 GMST0 = Angle::RadiansToDegrees(L) / 15.0 + 12.0; // hours
123 const D64 SIDTIME = GMST0 + UT + longit / 15.0;
124
125 // Hour Angle
126 D64 ha = FNrange(Angle::DegreesToRadians(15.0 * SIDTIME - RA));// degrees
127
128 const D64 x = std::cos(ha) * cos_delta;
129 const D64 y = std::sin(ha) * cos_delta;
130 const D64 z = std::sin(delta);
131 const D64 xhor = x * std::sin(latit_rad) - z * std::cos(latit_rad);
132 const D64 yhor = y;
133 const D64 zhor = x * std::cos(latit_rad) + z * std::sin(latit_rad);
134 const D64 azim = FNrange(std::atan2(yhor, xhor) + M_PI);
135 const D64 altit = std::asin(zhor);
136
137 // delta = asin(sin(obliq) * sin(lambda));
138 const D64 alpha = std::atan2(std::cos(obliq) * std::sin(lambda), std::cos(lambda));
139
140 // Find the Equation of Time in minutes
141 const D64 equation = 1440 - Angle::RadiansToDegrees(L - alpha) * 4;
142
143 ha = f0(latit, delta);
144
145 // arctic winter //
146
147 D64 riset = 12.0 - 12.0 * ha / M_PI + tzone - longit / 15.0 + equation / 60.0;
148 D64 settm = 12.0 + 12.0 * ha / M_PI + tzone - longit / 15.0 + equation / 60.0;
149 D64 noont = riset + 12.0 * ha / M_PI;
150 D64 altmax = 90.0 + delta_deg - latit;
151 if (altmax > 90.0) {
152 altmax = 180.0 - altmax; //to express as degrees from the N horizon
153 }
154
155 noont -= 24 * (noont > 24 ? 1 : 0);
156 riset -= 24 * (riset > 24 ? 1 : 0);
157 settm -= 24 * (settm > 24 ? 1 : 0);
158
159 const auto calcTime = [](const D64 dhr) noexcept
160 {
161 SimpleTime ret;
162 ret._hour = to_U8(dhr);
163 ret._minutes = to_U8((dhr - to_D64(ret._hour)) * 60);
164 return ret;
165 };
166
167 return SunInfo
168 {
169 .sunriseTime = calcTime(riset),
170 .sunsetTime = calcTime(settm),
171 .noonTime = calcTime(noont),
172 .altitude = to_F32(altit),
173 .azimuth = to_F32(azim),
174 .altitudeMax = to_F32(altmax),
175 .declination = to_F32(delta_deg)
176 };
177}
178
179D64 SunPosition::CorrectAngle(const D64 angleInRadians) noexcept {
180 if (angleInRadians < 0) {
181 return TwoPi - std::fmod(std::abs(angleInRadians), TwoPi);
182 }
183 if (angleInRadians > TwoPi) {
184 return std::fmod(angleInRadians, TwoPi);
185 }
186
187 return angleInRadians;
188}
189
190void Sun::SetLocation(const F32 longitude, const F32 latitude) noexcept {
191 if (!COMPARE(_longitude, longitude)) {
192 _longitude = longitude;
193 _dirty = true;
194 }
195 if (!COMPARE(_latitude, latitude)) {
196 _latitude = latitude;
197 _dirty = true;
198 }
199}
200
201void Sun::SetDate(struct tm &dateTime) noexcept {
202 const time_t t1 = mktime(&_dateTime);
203 const time_t t2 = mktime(&dateTime);
204 const D64 diffSecs = std::abs(difftime(t1, t2));
205 if (t1 == -1 || diffSecs > g_numSecondsUpdateInterval) {
206 _dateTime = dateTime;
207 _dirty = true;
208 }
209}
210
212 return SimpleTime{
213 to_U8(_dateTime.tm_hour),
214 to_U8(_dateTime.tm_min)
215 };
216}
217
219 return SimpleLocation{
220 _latitude,
222 };
223}
224
225const SunInfo& Sun::GetDetails() const {
226 if (_dirty) {
228 _dirty = false;
229 }
230
231 return _cachedDetails;
232}
233
234[[nodiscard]] vec3<F32> Sun::GetSunPosition(const F32 radius) const {
235 const SunInfo& info = GetDetails();
236
238 const F32 theta = info.azimuth;
239 const F32 sinPhiRadius = std::sin(phi) * radius;
240
241 return vec3<F32> {
242 sinPhiRadius * std::sin(theta),
243 std::cos(phi) * radius,
244 sinPhiRadius * std::cos(theta)
245 };
246}
247
248} //namespace Divide
constexpr T DegreesToRadians(T angleDegrees) noexcept
Return the radian equivalent of the given degree value.
Definition: MathHelper.inl:429
constexpr T RadiansToDegrees(T angleRadians) noexcept
Return the degree equivalent of the given radian value.
Definition: MathHelper.inl:436
constexpr D64 FNrange(const D64 x) noexcept
Definition: Sun.cpp:25
D64 FNsun(const D64 d, D64 &RA, D64 &delta, D64 &L) noexcept
Definition: Sun.cpp:44
D64 f0(const D64 lat, const D64 declin) noexcept
Definition: Sun.cpp:32
constexpr D64 g_numSecondsUpdateInterval
Definition: Sun.cpp:8
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
constexpr T SQUARED(T input) noexcept
Definition: MathHelper.inl:156
T Sqrt(T input) noexcept
Definition: MathHelper.inl:252
int32_t I32
constexpr F32 to_F32(const T value)
constexpr D64 to_D64(const T value)
constexpr D64 M_PI_2
Definition: MathHelper.h:77
constexpr U8 to_U8(const T value)
double D64
constexpr D64 M_PI
Definition: MathHelper.h:76
bool COMPARE(T X, U Y) noexcept
constexpr I32 to_I32(const T value)
vec3< F32 > GetSunPosition(F32 radius=1.f) const
Definition: Sun.cpp:234
void SetLocation(F32 longitude, F32 latitude) noexcept
Definition: Sun.cpp:190
const SunInfo & GetDetails() const
Definition: Sun.cpp:225
SimpleLocation GetGeographicLocation() const noexcept
Definition: Sun.cpp:218
void SetDate(struct tm &dateTime) noexcept
Definition: Sun.cpp:201
SunInfo _cachedDetails
Definition: Sun.h:67
F32 _latitude
Definition: Sun.h:69
bool _dirty
Definition: Sun.h:71
SimpleTime GetTimeOfDay() const noexcept
Definition: Sun.cpp:211
F32 _longitude
Definition: Sun.h:68
Angle::RADIANS< F32 > azimuth
Definition: Sun.h:45
SimpleTime sunriseTime
Definition: Sun.h:41
Angle::RADIANS< F32 > altitude
Definition: Sun.h:44
static D64 CorrectAngle(D64 angleInRadians) noexcept
Definition: Sun.cpp:179
static SunInfo CalculateSunPosition(const struct tm &dateTime, F32 latitude, F32 longitude)
Definition: Sun.cpp:86