File: | weather-sun.c |
Warning: | line 165, column 13 Value stored to 'obsLon' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ |
2 | /* weather-sun.c - Astronomy calculations for cafeweather |
3 | * |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License as |
6 | * published by the Free Software Foundation; either version 2 of the |
7 | * License, or (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, but |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, see |
16 | * <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | /* |
20 | * Formulas from: |
21 | * "Practical Astronomy With Your Calculator" (3e), Peter Duffett-Smith |
22 | * Cambridge University Press 1988 |
23 | * Unless otherwise noted, comments referencing "steps" are related to |
24 | * the algorithm presented in section 49 of above |
25 | */ |
26 | |
27 | #ifdef HAVE_CONFIG_H1 |
28 | #include <config.h> |
29 | #endif |
30 | |
31 | #include <math.h> |
32 | #include <time.h> |
33 | #include <glib.h> |
34 | |
35 | #define CAFEWEATHER_I_KNOW_THIS_IS_UNSTABLE |
36 | #include "weather-priv.h" |
37 | |
38 | #define ECCENTRICITY(d)(0.01671123 - (d)/36525.*0.00004392) (0.01671123 - (d)/36525.*0.00004392) |
39 | |
40 | /* |
41 | * Ecliptic longitude of the sun at specified time (UT) |
42 | * The algoithm is described in section 47 of Duffett-Smith |
43 | * Return value is in radians |
44 | */ |
45 | gdouble |
46 | sunEclipLongitude(time_t t) |
47 | { |
48 | gdouble ndays, meanAnom, eccenAnom, delta, e, longitude; |
49 | |
50 | /* |
51 | * Start with an esticafe based on a fixed daily rate |
52 | */ |
53 | ndays = EPOCH_TO_J2000(t)((gdouble)(t)-946727935.816) / 86400.; |
54 | meanAnom = DEGREES_TO_RADIANS(MEAN_ECLIPTIC_LONGITUDE(ndays)((fmod (((280.46457166 + (ndays)/36525.*35999.37244981) - (282.93768193 + (ndays)/36525.*0.32327364)),360.) / 180.) * 3.14159265358979323846 ) |
55 | - PERIGEE_LONGITUDE(ndays))((fmod (((280.46457166 + (ndays)/36525.*35999.37244981) - (282.93768193 + (ndays)/36525.*0.32327364)),360.) / 180.) * 3.14159265358979323846 ); |
56 | |
57 | /* |
58 | * Approximate solution of Kepler's equation: |
59 | * Find E which satisfies E - e sin(E) = M (mean anomaly) |
60 | */ |
61 | eccenAnom = meanAnom; |
62 | e = ECCENTRICITY(ndays)(0.01671123 - (ndays)/36525.*0.00004392); |
63 | |
64 | while (1e-12 < fabs( delta = eccenAnom - e * sin(eccenAnom) - meanAnom)) |
65 | { |
66 | eccenAnom -= delta / (1.- e * cos(eccenAnom)); |
67 | } |
68 | |
69 | /* |
70 | * Earth's longitude on the ecliptic |
71 | */ |
72 | longitude = fmod( DEGREES_TO_RADIANS (PERIGEE_LONGITUDE(ndays))((fmod (((282.93768193 + (ndays)/36525.*0.32327364)),360.) / 180. ) * 3.14159265358979323846) |
73 | + 2. * atan (sqrt ((1.+e)/(1.-e)) |
74 | * tan (eccenAnom / 2.)), |
75 | 2. * M_PI3.14159265358979323846); |
76 | if (longitude < 0.) { |
77 | longitude += 2 * M_PI3.14159265358979323846; |
78 | } |
79 | return longitude; |
80 | } |
81 | |
82 | static gdouble |
83 | ecliptic_obliquity (gdouble time) |
84 | { |
85 | gdouble jc = EPOCH_TO_J2000 (time)((gdouble)(time)-946727935.816) / (36525. * 86400.); |
86 | gdouble eclip_secs = (84381.448 |
87 | - (46.84024 * jc) |
88 | - (59.e-5 * jc * jc) |
89 | + (1.813e-3 * jc * jc * jc)); |
90 | return DEGREES_TO_RADIANS(eclip_secs / 3600.)((fmod ((eclip_secs / 3600.),360.) / 180.) * 3.14159265358979323846 ); |
91 | } |
92 | |
93 | /* |
94 | * Convert ecliptic longitude and latitude (radians) to equitorial |
95 | * coordinates, expressed as right ascension (hours) and |
96 | * declination (radians) |
97 | */ |
98 | void |
99 | ecl2equ (gdouble time, |
100 | gdouble eclipLon, gdouble eclipLat, |
101 | gdouble *ra, gdouble *decl) |
102 | { |
103 | gdouble mEclipObliq = ecliptic_obliquity(time); |
104 | |
105 | if (ra) { |
106 | *ra = RADIANS_TO_HOURS (atan2 ((sin (eclipLon) * cos (mEclipObliq)((atan2 ((sin (eclipLon) * cos (mEclipObliq) - tan (eclipLat) * sin(mEclipObliq)), cos (eclipLon))) * 12. / 3.14159265358979323846 ) |
107 | - tan (eclipLat) * sin(mEclipObliq)),((atan2 ((sin (eclipLon) * cos (mEclipObliq) - tan (eclipLat) * sin(mEclipObliq)), cos (eclipLon))) * 12. / 3.14159265358979323846 ) |
108 | cos (eclipLon)))((atan2 ((sin (eclipLon) * cos (mEclipObliq) - tan (eclipLat) * sin(mEclipObliq)), cos (eclipLon))) * 12. / 3.14159265358979323846 ); |
109 | if (*ra < 0.) |
110 | *ra += 24.; |
111 | } |
112 | if (decl) { |
113 | *decl = asin (( sin (eclipLat) * cos (mEclipObliq)) |
114 | + cos (eclipLat) * sin (mEclipObliq) * sin(eclipLon)); |
115 | } |
116 | } |
117 | |
118 | /* |
119 | * Calculate rising and setting times for an object |
120 | * based on it equitorial coordinates (section 33 & 15) |
121 | * Returned "rise" and "set" values are sideral times in hours |
122 | */ |
123 | static void |
124 | gstObsv (gdouble ra, gdouble decl, |
125 | gdouble obsLat, gdouble obsLon, |
126 | gdouble *rise, gdouble *set) |
127 | { |
128 | double a = acos (-tan (obsLat) * tan (decl)); |
129 | double b; |
130 | |
131 | if (isnan (a)__builtin_isnan (a) != 0) { |
132 | *set = *rise = a; |
133 | return; |
134 | } |
135 | a = RADIANS_TO_HOURS (a)((a) * 12. / 3.14159265358979323846); |
136 | b = 24. - a + ra; |
137 | a += ra; |
138 | a -= RADIANS_TO_HOURS (obsLon)((obsLon) * 12. / 3.14159265358979323846); |
139 | b -= RADIANS_TO_HOURS (obsLon)((obsLon) * 12. / 3.14159265358979323846); |
140 | if ((a = fmod (a, 24.)) < 0) |
141 | a += 24.; |
142 | if ((b = fmod (b, 24.)) < 0) |
143 | b += 24.; |
144 | |
145 | *set = a; |
146 | *rise = b; |
147 | } |
148 | |
149 | |
150 | static gdouble |
151 | t0 (time_t date) |
152 | { |
153 | gdouble t = ((gdouble)(EPOCH_TO_J2000 (date)((gdouble)(date)-946727935.816) / 86400)) / 36525.0; |
154 | gdouble t0 = fmod (6.697374558 + 2400.051366 * t + 2.5862e-5 * t * t, 24.); |
155 | if (t0 < 0.) |
156 | t0 += 24.; |
157 | return t0; |
158 | } |
159 | |
160 | |
161 | static gboolean |
162 | calc_sun2 (WeatherInfo *info, time_t t) |
163 | { |
164 | gdouble obsLat = info->location->latitude; |
165 | gdouble obsLon = info->location->longitude; |
Value stored to 'obsLon' during its initialization is never read | |
166 | time_t gm_midn; |
167 | time_t lcl_midn; |
168 | gdouble gm_hoff, lambda; |
169 | gdouble ra1, ra2; |
170 | gdouble decl1, decl2; |
171 | gdouble decl_midn, decl_noon; |
172 | gdouble rise1, rise2; |
173 | gdouble set1, set2; |
174 | gdouble tt, t00; |
175 | gdouble x, u, dt; |
176 | |
177 | /* Approximate preceding local midnight at observer's longitude */ |
178 | obsLat = info->location->latitude; |
179 | obsLon = info->location->longitude; |
180 | gm_midn = t - (t % 86400); |
181 | gm_hoff = floor ((RADIANS_TO_DEGREES (obsLon)((obsLon) * 180. / 3.14159265358979323846) + 7.5) / 15.); |
182 | lcl_midn = gm_midn - 3600. * gm_hoff; |
183 | if (t - lcl_midn >= 86400) |
184 | lcl_midn += 86400; |
185 | else if (lcl_midn > t) |
186 | lcl_midn -= 86400; |
187 | |
188 | lambda = sunEclipLongitude (lcl_midn); |
189 | |
190 | /* |
191 | * Calculate equitorial coordinates of sun at previous |
192 | * and next local midnights |
193 | */ |
194 | ecl2equ (lcl_midn, lambda, 0., &ra1, &decl1); |
195 | ecl2equ (lcl_midn + 86400., |
196 | lambda + DEGREES_TO_RADIANS(SOL_PROGRESSION)((fmod (((360./365.242191)),360.) / 180.) * 3.14159265358979323846 ), 0., |
197 | &ra2, &decl2); |
198 | |
199 | /* |
200 | * If the observer is within the Arctic or Antarctic Circles then |
201 | * the sun may be above or below the horizon for the full day. |
202 | */ |
203 | decl_midn = MIN(decl1,decl2)(((decl1) < (decl2)) ? (decl1) : (decl2)); |
204 | decl_noon = (decl1+decl2)/2.; |
205 | info->midnightSun = |
206 | (obsLat > (M_PI3.14159265358979323846/2.-decl_midn)) || (obsLat < (-M_PI3.14159265358979323846/2.-decl_midn)); |
207 | info->polarNight = |
208 | (obsLat > (M_PI3.14159265358979323846/2.+decl_noon)) || (obsLat < (-M_PI3.14159265358979323846/2.+decl_noon)); |
209 | if (info->midnightSun || info->polarNight) { |
210 | info->sunriseValid = info->sunsetValid = FALSE(0); |
211 | return FALSE(0); |
212 | } |
213 | |
214 | /* |
215 | * Convert to rise and set times based positions for the preceding |
216 | * and following local midnights. |
217 | */ |
218 | gstObsv (ra1, decl1, obsLat, obsLon - (gm_hoff * M_PI3.14159265358979323846 / 12.), &rise1, &set1); |
219 | gstObsv (ra2, decl2, obsLat, obsLon - (gm_hoff * M_PI3.14159265358979323846 / 12.), &rise2, &set2); |
220 | |
221 | /* TODO: include calculations for regions near the poles. */ |
222 | if (isnan(rise1)__builtin_isnan (rise1) || isnan(rise2)__builtin_isnan (rise2)) { |
223 | info->sunriseValid = info->sunsetValid = FALSE(0); |
224 | return FALSE(0); |
225 | } |
226 | |
227 | if (rise2 < rise1) { |
228 | rise2 += 24.; |
229 | } |
230 | if (set2 < set1) { |
231 | set2 += 24.; |
232 | } |
233 | |
234 | tt = t0(lcl_midn); |
235 | t00 = tt - (gm_hoff + RADIANS_TO_HOURS(obsLon)((obsLon) * 12. / 3.14159265358979323846)) * 1.002737909; |
236 | |
237 | if (t00 < 0.) |
238 | t00 += 24.; |
239 | |
240 | if (rise1 < t00) { |
241 | rise1 += 24.; |
242 | rise2 += 24.; |
243 | } |
244 | if (set1 < t00) { |
245 | set1 += 24.; |
246 | set2 += 24.; |
247 | } |
248 | |
249 | /* |
250 | * Interpolate between the two to get a rise and set time |
251 | * based on the sun's position at local noon (step 8) |
252 | */ |
253 | rise1 = (24.07 * rise1 - t00 * (rise2 - rise1)) / (24.07 + rise1 - rise2); |
254 | set1 = (24.07 * set1 - t00 * (set2 - set1)) / (24.07 + set1 - set2); |
255 | |
256 | /* |
257 | * Calculate an adjustment value to account for parallax, |
258 | * refraction and the Sun's finite diameter (steps 9,10) |
259 | */ |
260 | decl2 = (decl1 + decl2) / 2.; |
261 | x = DEGREES_TO_RADIANS(0.830725)((fmod ((0.830725),360.) / 180.) * 3.14159265358979323846); |
262 | u = acos ( sin(obsLat) / cos(decl2) ); |
263 | dt = RADIANS_TO_HOURS ( asin ( sin(x) / sin(u) ) / cos(decl2) )((asin ( sin(x) / sin(u) ) / cos(decl2)) * 12. / 3.14159265358979323846 ); |
264 | |
265 | /* |
266 | * Subtract the correction value from sunrise and add to sunset, |
267 | * then (step 11) convert sideral times to UT |
268 | */ |
269 | rise1 = (rise1 - dt - tt) * 0.9972695661; |
270 | if (rise1 < 0.) |
271 | rise1 += 24; |
272 | else if (rise1 >= 24.) |
273 | rise1 -= 24.; |
274 | info->sunriseValid = ((rise1 >= 0.) && (rise1 < 24.)); |
275 | info->sunrise = (rise1 * 3600.) + lcl_midn; |
276 | |
277 | set1 = (set1 + dt - tt) * 0.9972695661; |
278 | if (set1 < 0.) |
279 | set1 += 24; |
280 | else if (set1 >= 24.) |
281 | set1 -= 24.; |
282 | info->sunsetValid = ((set1 >= 0.) && (set1 < 24.)); |
283 | info->sunset = (set1 * 3600.) + lcl_midn; |
284 | |
285 | return (info->sunriseValid || info->sunsetValid); |
286 | } |
287 | |
288 | |
289 | /** |
290 | * calc_sun_time: |
291 | * @info: #WeatherInfo structure containing the observer's latitude |
292 | * and longitude in radians, fills in the sunrise and sunset times. |
293 | * @t: time_t |
294 | * |
295 | * Returns: gboolean indicating if the results are valid. |
296 | */ |
297 | gboolean |
298 | calc_sun_time (WeatherInfo *info, time_t t) |
299 | { |
300 | return info->location->latlon_valid && calc_sun2 (info, t); |
301 | } |
302 | |
303 | /** |
304 | * calc_sun: |
305 | * @info: #WeatherInfo structure containing the observer's latitude |
306 | * and longitude in radians, fills in the sunrise and sunset times. |
307 | * |
308 | * Returns: gboolean indicating if the results are valid. |
309 | */ |
310 | gboolean |
311 | calc_sun (WeatherInfo *info) |
312 | { |
313 | return calc_sun_time(info, time(NULL((void*)0))); |
314 | } |
315 | |
316 | |
317 | /** |
318 | * weather_info_next_sun_event: |
319 | * @info: #WeatherInfo structure |
320 | * |
321 | * Returns: the interval, in seconds, until the next "sun event": |
322 | * - local midnight, when rise and set times are recomputed |
323 | * - next sunrise, when icon changes to daytime version |
324 | * - next sunset, when icon changes to nighttime version |
325 | */ |
326 | gint |
327 | weather_info_next_sun_event (WeatherInfo *info) |
328 | { |
329 | time_t now = time (NULL((void*)0)); |
330 | struct tm ltm; |
331 | time_t nxtEvent; |
332 | |
333 | g_return_val_if_fail (info != NULL, -1)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning ("CafeWeather", ((const char*) (__func__)), "info != NULL"); return (-1); } } while (0); |
334 | |
335 | if (!calc_sun (info)) |
336 | return -1; |
337 | |
338 | /* Determine when the next local midnight occurs */ |
339 | (void) localtime_r (&now, <m); |
340 | ltm.tm_sec = 0; |
341 | ltm.tm_min = 0; |
342 | ltm.tm_hour = 0; |
343 | ltm.tm_mday++; |
344 | nxtEvent = mktime (<m); |
345 | |
346 | if (info->sunsetValid && |
347 | (info->sunset > now) && (info->sunset < nxtEvent)) |
348 | nxtEvent = info->sunset; |
349 | if (info->sunriseValid && |
350 | (info->sunrise > now) && (info->sunrise < nxtEvent)) |
351 | nxtEvent = info->sunrise; |
352 | return (gint)(nxtEvent - now); |
353 | } |