A couple of days ago, I mentioned calculating the sunrise and sunset times. This is an interesting problem, so after reading Astronomy Answers – Position of the Sun, I quickly implemented it using C language. It is only an experiment, so results are approximate.
What can be easily calculated?
Sunset/Sunrise
Sunrise and sunset calculations for 5/2013, Gdansk/Poland (GMT=1, DST=1) Day JDN Sunrise Transit Sunset 1 2456414 5:08 12:44 20:19 2 2456415 5:06 12:44 20:21 3 2456416 5:04 12:43 20:23 4 2456417 5:02 12:43 20:25 5 2456418 5:00 12:43 20:26 6 2456419 4:58 12:43 20:28 7 2456420 4:56 12:43 20:30 8 2456421 4:54 12:43 20:32 9 2456422 4:52 12:43 20:34 10 2456423 4:51 12:43 20:36 11 2456424 4:49 12:43 20:37 12 2456425 4:47 12:43 20:39 13 2456426 4:45 12:43 20:41 14 2456427 4:43 12:43 20:43 15 2456428 4:42 12:43 20:45 16 2456429 4:40 12:43 20:46 17 2456430 4:38 12:43 20:48 18 2456431 4:37 12:43 20:50 19 2456432 4:35 12:43 20:51 20 2456433 4:34 12:43 20:53 21 2456434 4:32 12:43 20:55 22 2456435 4:31 12:43 20:56 23 2456436 4:29 12:44 20:58 24 2456437 4:28 12:44 20:59 25 2456438 4:27 12:44 21:01 26 2456439 4:26 12:44 21:02 27 2456440 4:24 12:44 21:04 28 2456441 4:23 12:44 21:05 29 2456442 4:22 12:44 21:07 30 2456443 4:21 12:44 21:08 31 2456444 4:20 12:45 21:09
Astronomical seasons
Astronomical seasons 2014, Gdansk/Poland (northern hemisphere) Day JDN 81 2456739 first day of spring 174 2456832 first day of summer 267 2456925 first day of autumn 357 2457015 first day of winter
Day and night
Day and night, Gdansk/Poland, UTC Date & time Day and night (one character = 6 degrees) 01/01/2013 00:00 +++++++++-------------------------------------------++++++++ 01/01/2013 06:00 -------------------------------------+++++++++++++++++------ 01/01/2013 12:00 ----------------------+++++++++++++++++--------------------- 01/01/2013 18:00 -------+++++++++++++++++------------------------------------ 21/05/2013 00:00 ++++++++++++++++++++--------------------++++++++++++++++++++ 21/05/2013 06:00 +++++--------------------+++++++++++++++++++++++++++++++++++ 21/05/2013 12:00 ----------++++++++++++++++++++++++++++++++++++++++---------- 21/05/2013 18:00 +++++++++++++++++++++++++++++++++++--------------------+++++
First step – JDN
At first, you need to calculate Julian Day Number.
It is the first and most important step, but very easy to implement.
/* source: http://aa.quae.nl/en/reken/zonpositie.html */ /* convert Gregorian calendar date to Julian Day Number */ /* source: http://en.wikipedia.org/wiki/Julian_day */ int julian_jdn(int day, int month, int year) { int a, y, m; a = (14 - month) / 12; y = year + 4800 - a; me = month + 12 * a - 3; return (day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045); }
If you want to convert JDN to the Gregorian date (d/m/y), read my previous blog post.
Second step – Sun calculations
In the next step, I will use functions defined below.
/* source: http://aa.quae.nl/en/reken/zonpositie.html * milosz at sleeplessbeastie.eu */ /* convert degrees to radians */ double sun_to_rad(double angle) { return (M_PI * angle / 180); } /* convert radians to degrees */ double sun_to_deg(double angle) { return (180 * angle / M_PI); } /* draw a full circle (370' to (370'- 360') = 10') */ double sun_full_circle(double angle) { return (angle - (int)(angle / 360) * 360); } /* Earth - mean anomaly */ double sun_earth_mean_anomaly(double jdn) { return sun_full_circle(357.5291 + 0.98560028 * (jdn - 2451545)); } /* Earth - the equation of center */ double sun_earth_equation_of_center(double mean_anomaly) { return 1.9148 * sin(sun_to_rad(mean_anomaly)) + 0.0200 * sin(2 * sun_to_rad(mean_anomaly)) + 0.0003 * sin(3 * sun_to_rad(mean_anomaly)); } /* Earth - true anomaly */ double sun_earth_true_anomaly(double jdn) { return sun_earth_mean_anomaly(jdn) + sun_earth_equation_of_center(sun_earth_mean_anomaly(jdn)); } /* Ecliptic longitude of the sun */ double sun_sun_ecliptic_longitude(double true_anomaly) { return sun_full_circle(true_anomaly + 102.9372 + 180); } /* check for specific season using ecliptic longitude of the sun*/ /* for northern hemisphere */ /* 0 - 90 spring */ /* 90 - 180 summer */ /* 180 - 270 autumn */ /* 270 - 360 winter */ int sun_check_season(int jdn, int a, int b) { double el_curr = sun_sun_ecliptic_longitude(sun_earth_true_anomaly(jdn)); double el_prev = sun_sun_ecliptic_longitude(sun_earth_true_anomaly(jdn - 1)); if ((el_prev <= b && el_curr >= b) || (b == 360 && el_prev >= a && el_prev <= b && el_curr >= 0 && el_curr < a)) return 0; else if ((el_prev >= a && el_prev < b) && (el_prev >= a && el_prev < b)) return 1; else return -1; } /* check if JDN is spring */ /* 1 - true */ /* 0 - true, last day */ /* -1 - false */ int sun_is_spring(int jdn) { return sun_check_season(jdn, 0, 90); } /* check if JDN is summer */ /* 1 - true */ /* 0 - true, last day */ /* -1 - false */ int sun_is_summer(int jdn) { return sun_check_season(jdn, 90, 180); } /* check if JDN is autumn */ /* 1 - true */ /* 0 - true, last day */ /* -1 - false */ int sun_is_autumn(int jdn) { return sun_check_season(jdn, 180, 270); } /* check if JDN is spring */ /* 1 - true */ /* 0 - true, last day */ /* -1 - false */ int sun_is_winter(int jdn) { return sun_check_season(jdn, 270, 360); } /* Earth - right ascension */ double sun_earth_right_ascension(double ecliptic_longitude) { return sun_to_deg( atan2( sin(sun_to_rad(ecliptic_longitude)) * cos(sun_to_rad(23.45)), cos(sun_to_rad(ecliptic_longitude)) ) ); } /* Earth - declination */ double sun_earth_declination(double ecliptic_longitude) { return sun_to_deg( asin( sin(sun_to_rad(ecliptic_longitude)) * sin(sun_to_rad(23.45)) ) ); } /* Earth - sidereal time */ double sun_earth_sideral_time(double jdn, double lw) { return sun_full_circle( 280.1600 + 360.9856235 * (jdn - 2451545) - lw ); } /* Earth - hour angle */ double sun_earth_hour_angle(double sideral_time, double right_ascension) { return sideral_time - right_ascension; } /* Earth - azimuth */ double sun_earth_azimuth(double hour_angle, double declination, double ln) { return sun_full_circle( sun_to_deg( atan2( sin(sun_to_rad(hour_angle)), cos(sun_to_rad(hour_angle)) * sin(sun_to_rad(ln)) - tan(sun_to_rad(declination)) * cos(sun_to_rad(ln)) ) ) ); } /* Earth - altitude */ double sun_earth_altitude(double hour_angle, double declination, double ln) { return sun_full_circle( sun_to_deg( asin( sin(sun_to_rad(ln)) * sin(sun_to_rad(declination)) + cos(sun_to_rad(ln)) * cos(sun_to_rad(declination)) * cos(sun_to_rad(hour_angle)) ) ) ); } /* Sun - transtit */ double sun_sun_transit(double jdn, double lw, double mean_anomaly) { return 2451545 + 0.0009 + (lw / 360) + (int)((jdn - 2451545 - 0.0009) / 1 - lw / 360) + 0.0053 * sin(sun_to_rad(mean_anomaly)) - 0.0069 * sin(sun_to_rad(2 * sun_full_circle(mean_anomaly + 102.9372 + 180))); } /* Sun - sunrise */ double sun_sun_sunrise(double ln, double declination, double jdn, double lw, double mean_anomaly) { double h; h = -sun_full_circle( sun_to_deg( acos( ( sin(sun_to_rad(-0.83)) - sin(sun_to_rad(ln)) * sin(sun_to_rad(declination)) ) / ( cos(sun_to_rad(ln)) * cos(sun_to_rad(declination)) ) ) ) ); return 2451545 + 0.0009 + (h + lw) / 360 + (int)((jdn - 2451545 - 0.0009) / 1 - (h + lw) / 360) + 0.0053 * sin(sun_to_rad(mean_anomaly)) - 0.0069 * sin(sun_to_rad(2 * sun_full_circle(mean_anomaly + 102.9372 + 180))); } /* Sun - sunset */ double sun_sun_sunset(double ln, double declination, double jdn, double lw, double mean_anomaly) { double h; h = sun_full_circle( sun_to_deg( acos( ( sin(sun_to_rad(-0.83)) - sin(sun_to_rad(ln)) * sin(sun_to_rad(declination)) ) / ( cos(sun_to_rad(ln)) * cos(sun_to_rad(declination)) ) ) ) ); return 2 + 2451545 + 0.0009 + (h + lw) / 360 + (int)((jdn - 2451545 - 0.0009) / 1 - (h + lw) / 360) + 0.0053 * sin(sun_to_rad(mean_anomaly)) - 0.0069 * sin(sun_to_rad(2 * sun_full_circle(mean_anomaly + 102.9372 + 180))); }
Third step – Example
Sunrise/Sunset
Source code:
double jdn; /* Julian Day Number */ double ln, lw; /* position */ ln = 54.4; /* north latitude */ lw = -18.5; /* west longitude */ /* time modifier */ int gmt = +1; int dst = +1; /* it is very simplified as this is only an example */ int time_mod = gmt + dst; int year = 2013, month = 6; double ta, l, delta; double transit, sunrise, sunset; double temp; int i; printf("\nSunrise and sunset calculations for %d/%d (GMT=%d, DST=%d)\n\n", month, year, gmt, dst); printf("%5s %10s %9s %9s %9s\n", "Day", "JDN", "Sunrise", "Transit", "Sunset"); for (i = julian_jdn(1, month, 2013); i < julian_jdn(1, month + 1, 2013); i++) { ta = sun_earth_true_anomaly(i); l = sun_sun_ecliptic_longitude(ta); delta = sun_earth_declination(l); printf("%5d %10d ", i + 1 - julian_jdn(1, month, 2013), i); sunrise = sun_sun_sunrise(ln, delta, i, lw, ta); temp = (sunrise - (int) sunrise) > 0.5 ? (sunrise - (int) sunrise) - 0.5 : (sunrise - (int) sunrise) + 0.5; printf("%6d:%02d ", (int)(temp * 24) + time_mod, (int)(temp * 24 * 60) % 60); transit = sun_sun_transit(i, lw, ta); temp = (transit - (int) transit) > 0.5 ? (transit - (int) transit) - 0.5 : (transit - (int) transit) + 0.5; printf("%6d:%02d ", (int)(temp * 24) + time_mod, (int)(temp * 24 * 60) % 60); sunset = sun_sun_sunset(ln, delta, i, lw, ta); temp = (sunset - (int) sunset) > 0.5 ? (sunset - (int) sunset) - 0.5 : (sunset - (int) sunset) + 0.5; printf("%6d:%02d\n", (int)(temp * 24) + time_mod , (int)(temp * 24 * 60) % 60); }
Output:
Sunrise and sunset calculations for 6/2013 (GMT=1, DST=1)
Day JDN Sunrise Transit Sunset
1 2456445 4:19 12:45 21:10
2 2456446 4:18 12:45 21:12
3 2456447 4:17 12:45 21:13
4 2456448 4:16 12:45 21:14
5 2456449 4:16 12:45 21:15
6 2456450 4:15 12:46 21:16
7 2456451 4:14 12:46 21:17
8 2456452 4:14 12:46 21:18
9 2456453 4:13 12:46 21:19
10 2456454 4:13 12:46 21:20
11 2456455 4:12 12:46 21:21
12 2456456 4:12 12:47 21:21
13 2456457 4:12 12:47 21:22
14 2456458 4:11 12:47 21:23
15 2456459 4:11 12:47 21:23
16 2456460 4:11 12:47 21:24
17 2456461 4:11 12:48 21:24
18 2456462 4:11 12:48 21:25
19 2456463 4:11 12:48 21:25
20 2456464 4:11 12:48 21:25
21 2456465 4:11 12:49 21:26
22 2456466 4:12 12:49 21:26
23 2456467 4:12 12:49 21:26
24 2456468 4:12 12:49 21:26
25 2456469 4:13 12:49 21:26
26 2456470 4:13 12:50 21:26
27 2456471 4:14 12:50 21:26
28 2456472 4:14 12:50 21:26
29 2456473 4:15 12:50 21:25
30 2456474 4:16 12:50 21:25
Notes
You need to note that Julian Day 2456474 starts at 2456473.5 (00:00) and ends at 2456474.5. 2456474.0 is equal to 30/06/2013 12:00.
temp = (sunrise - (int) sunrise) > 0.5 ? (sunrise - (int) sunrise) - 0.5 : (sunrise - (int) sunrise) + 0.5;
printf("%6d:%02d ", (int)(temp * 24) + time_mod, (int)(temp * 24 * 60) % 60);
It is easier to determine time using the Julian Day fraction part when the above range is shifted.
You need to apply GMT and DST as by default sunrise/transit/sunset/...
functions return values using Universal timezone.
Astronomical seasons
Source code:
year = 2013;
printf("\nAstronomical seasons %d\n\n", year);
printf("%5s %10s\n", "Day", "JDN");
for (i = julian_jdn(1, 1, year); i < julian_jdn(1, 1, year + 1); i++) {
if (sun_is_winter(i) == 0) { /* -1 - no, 0 - last day, 1 - yes */
printf("%5d %10d %20s\n", i + 1 - julian_jdn(1, 1, year) + 1, i + 1, "first day of spring");
} else if (sun_is_spring(i) == 0) {
printf("%5d %10d %20s\n", i + 1 - julian_jdn(1, 1, year) + 1, i + 1, "first day of summer");
} else if (sun_is_summer(i) == 0) {
printf("%5d %10d %20s\n", i + 1 - julian_jdn(1, 1, year) + 1, i + 1, "first day of autumn");
} else if (sun_is_autumn(i) == 0) {
printf("%5d %10d %20s\n", i + 1 - julian_jdn(1, 1, year) + 1, i + 1, "first day of winter");
}
}
Output:
Day JDN
81 2456374 first day of spring
173 2456466 first day of summer
267 2456560 first day of autumn
357 2456650 first day of winter
This is true only for the northern hemisphere.
This code prints the first day after the change.
For example sun_is_winter(jdn)
function will return:
-1
– it is not winter- `` – it is the last day of winter (time of change)
1
– it is winter
Day and night
Source code:
printf("\nDay and night\n\n");
int date[2][3] = {
{21, 5, 2013},
{22, 5, 2013}
};
double time[4][2] = {
{ -0.5 , 0 }, /* 00:00 hour */
{ -0.25, 6 }, /* 06:00 hour */
{ 0 , 12 }, /* 12:00 hour */
{ 0.25, 18 } /* 18:00 hour */
};
int day_and_night[360];
int k, j;
int h, r, s;
printf("%17s %12s %60s\n", "Date & time", "JDN", "Day and night");
for (j = 0; j < (sizeof(date) / sizeof(date[0])); j++) {
for (k = 0; k < (sizeof(time) / sizeof(time[0])); k++) {
jdn = julian_jdn(date[j][0], date[j][1], date[j][2]);
jdn += time[k][0];
ta = sun_earth_true_anomaly(jdn);
printf(" %02d/%02d/%4d %02.0f:00 %12.2f ", date[j][0], date[j][1], date[j][2], time[k][1], jdn);
for (i = -180; i <= 180; i++) {
l = sun_sun_ecliptic_longitude(ta);
delta = sun_earth_declination(l);
s = sun_earth_sideral_time(jdn, -i);
r = sun_earth_right_ascension(l);
h = sun_earth_hour_angle(s, r);
day_and_night[i + 180] = sun_earth_altitude(h, delta, ln);
}
for (i = 0; i < 60; i++) {
temp = (day_and_night[6 * i] +
day_and_night[6 * i + 1] +
day_and_night[6 * i + 2] +
day_and_night[6 * i + 3] +
day_and_night[6 * i + 4] +
day_and_night[6 * i + 5]) / 6;
temp > 0 ? printf("+") : printf("-");
}
printf("\n");
}
}
Output:
Day and night
Date & time JDN Day and night
21/05/2013 00:00 2456433.50 ++++++++++++++++++++--------------------++++++++++++++++++++
21/05/2013 06:00 2456433.75 +++++--------------------+++++++++++++++++++++++++++++++++++
21/05/2013 12:00 2456434.00 ----------++++++++++++++++++++++++++++++++++++++++----------
21/05/2013 18:00 2456434.25 +++++++++++++++++++++++++++++++++++--------------------+++++
22/05/2013 00:00 2456434.50 ++++++++++++++++++++--------------------++++++++++++++++++++
22/05/2013 06:00 2456434.75 +++++--------------------+++++++++++++++++++++++++++++++++++
22/05/2013 12:00 2456435.00 ----------++++++++++++++++++++++++++++++++++++++++----------
22/05/2013 18:00 2456435.25 +++++++++++++++++++++++++++++++++++--------------------+++++
Notes
Each +/-
character is equal to six degrees as there is no need to use full resolution in this example (it wouldn’t be readable).
This code does not take timezone and daylight saving time into consideration.
Additional notes
Download sun_example.tar.gz file and play with the source code.