1
/* $Id: weather-sun.c 10286 2007-07-12 08:52:56Z callum $ */
4
* Astronomy calculations for gweather
7
* "Practical Astronomy With Your Calculator" (3e), Peter Duffett-Smith
8
* Cambridge University Press 1988
10
* Planetary Mean Orbit parameters from http://ssd.jpl.nasa.gov/elem_planets.html,
11
* converting longitudes from heliocentric to geocentric coordinates
12
* epoch J2000 (2000 Jan 1 12:00:00 UTC)
20
#include <sys/types.h>
27
#include "weather-priv.h"
29
#define EPOCH_TO_J2000(t) (t-946728000)
30
#define MEAN_ECLIPTIC_LONGITUDE 280.46435
31
#define PERIGEE_LONGITUDE 282.94719
32
#define ECCENTRICITY 0.01671002
33
#define MEAN_ECLIPTIC_OBLIQUITY
34
#define SOL_PROGRESSION (360./365.242191)
37
* Convert ecliptic longitude (radians) to equitorial coordinates,
38
* expressed as right ascension (hours) and declination (radians)
39
* Assume ecliptic latitude is 0.0
41
static void ecl2equ (gdouble time, gdouble eclipLon,
42
gdouble *ra, gdouble *decl)
44
gdouble jc = EPOCH_TO_J2000(time) / (36525. * 86400.);
45
gdouble mEclipObliq = DEGREES_TO_RADIANS(23.439291667
47
- 1.666667e-7 * jc * jc
48
+ 5.027777e-7 * jc * jc * jc);
49
*ra = RADIANS_TO_HOURS(atan2 ( sin(eclipLon) * cos(mEclipObliq), cos(eclipLon)));
52
*decl = asin ( sin(mEclipObliq) * sin(eclipLon) );
56
* Calculate rising and setting times of an object
57
* based on it equitorial coordinates
59
static void gstObsv (gdouble ra, gdouble decl,
60
gdouble obsLat, gdouble obsLon,
61
gdouble *rise, gdouble *set)
63
double a = acos(-tan(obsLat) * tan(decl));
70
a = RADIANS_TO_HOURS(a);
73
a -= RADIANS_TO_HOURS(obsLon);
74
b -= RADIANS_TO_HOURS(obsLon);
75
if ((a = fmod(a, 24.)) < 0)
77
if ((b = fmod(b, 24.)) < 0)
85
static gdouble t0(time_t date)
87
register gdouble t = ((gdouble)(EPOCH_TO_J2000(date) / 86400)) / 36525.0;
88
gdouble t0 = fmod( 6.697374558 + 2400.051366 * t + 2.5862e-5 * t * t, 24.);
96
static gboolean calc_sun2 (time_t t, gdouble obsLat, gdouble obsLon,
97
time_t *rise, time_t *set)
101
gdouble gm_hoff, ndays, meanAnom, eccenAnom, delta, lambda;
102
gdouble ra1, ra2, decl1, decl2;
103
gdouble rise1, rise2, set1, set2;
107
/* Approximate preceding local midnight at observer's longitude */
108
gm_midn = t - (t % 86400);
109
gm_hoff = floor((RADIANS_TO_DEGREES(obsLon) + 7.5) / 15.);
110
lcl_midn = gm_midn - 3600. * gm_hoff;
111
if (t - lcl_midn >= 86400)
113
else if (lcl_midn > t)
117
* Ecliptic longitude of the sun at midnight local time
118
* Start with an estimate based on a fixed daily rate
120
ndays = EPOCH_TO_J2000(lcl_midn) / 86400.;
121
meanAnom = DEGREES_TO_RADIANS(ndays * SOL_PROGRESSION
122
+ MEAN_ECLIPTIC_LONGITUDE - PERIGEE_LONGITUDE);
125
* Approximate solution of Kepler's equation:
126
* Find E which satisfies E - e sin(E) = M (mean anomaly)
128
eccenAnom = meanAnom;
130
while (1e-12 < fabs( delta = eccenAnom - ECCENTRICITY * sin(eccenAnom) - meanAnom))
132
eccenAnom -= delta / (1.- ECCENTRICITY * cos(eccenAnom));
135
/* Sun's longitude on the ecliptic */
136
lambda = fmod( DEGREES_TO_RADIANS ( PERIGEE_LONGITUDE )
137
+ 2. * atan( sqrt((1.+ECCENTRICITY)/(1.-ECCENTRICITY))
138
* tan ( eccenAnom / 2. ) ),
141
/* Calculate equitorial coordinates of sun at previous and next local midnights */
143
ecl2equ (lcl_midn, lambda, &ra1, &decl1);
144
ecl2equ (lcl_midn + 86400., lambda + DEGREES_TO_RADIANS(SOL_PROGRESSION), &ra2, &decl2);
146
/* Convert to rise and set times assuming the earth were to stay in its position
147
* in its orbit around the sun. This will soon be followed by interpolating
151
gstObsv (ra1, decl1, obsLat, obsLon - (gm_hoff * M_PI / 12.), &rise1, &set1);
152
gstObsv (ra2, decl2, obsLat, obsLon - (gm_hoff * M_PI / 12.), &rise2, &set2);
154
/* TODO: include calculations for regions near the poles. */
155
if (isnan(rise1) || isnan(rise2))
166
t00 = tt - (gm_hoff + RADIANS_TO_HOURS(obsLon)) * 1.002737909;
181
* Interpolate between the two
183
rise1 = (24.07 * rise1 - t00 * (rise2 - rise1)) / (24.07 + rise1 - rise2);
184
set1 = (24.07 * set1 - t00 * (set2 - set1)) / (24.07 + set1 - set2);
186
decl2 = (decl1 + decl2) / 2.;
187
x = DEGREES_TO_RADIANS(0.830725);
188
u = acos ( sin(obsLat) / cos(decl2) );
189
dt = RADIANS_TO_HOURS ( asin ( sin(x) / sin(u) )
192
rise1 = (rise1 - dt - tt) * 0.9972695661;
193
set1 = (set1 + dt - tt) * 0.9972695661;
196
else if (rise1 >= 24.)
200
else if (set1 >= 24.)
203
*rise = (rise1 * 3600.) + lcl_midn;
204
*set = (set1 * 3600.) + lcl_midn;
209
gboolean calc_sun (WeatherInfo *info)
211
time_t now = time (NULL);
213
return info->location->latlon_valid
214
&& calc_sun2(now, info->location->latitude, info->location->longitude,
215
&info->sunrise, &info->sunset);
220
* Returns the interval, in seconds, until the next "sun event":
221
* - local midnight, when rise and set times are recomputed
222
* - next sunrise, when icon changes to daytime version
223
* - next sunset, when icon changes to nighttime version
225
gint weather_info_next_sun_event(WeatherInfo *info)
227
time_t now = time(NULL);
234
/* Determine when the next local midnight occurs */
235
(void) localtime_r (&now, <m);
240
nxtEvent = mktime(<m);
242
if (info->sunset > now && info->sunset < nxtEvent)
243
nxtEvent = info->sunset;
244
if (info->sunrise > now && info->sunrise < nxtEvent)
245
nxtEvent = info->sunrise;
246
return (gint)(nxtEvent - now);