Sun position
Sun position algorithm
sky-time.c
1 /*==============================================================================
2  * sky-time.c - astronomical time routines
3  *
4  * Author: David Hoadley
5  *
6  * Description: (see the "Time routines" sections of sky.h)
7  *
8  * Copyright (c) 2020, David Hoadley <vcrumble@westnet.com.au>
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions are met:
13  *
14  * * Redistributions of source code must retain the above copyright notice, this
15  * list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above copyright notice,
17  * this list of conditions and the following disclaimer in the documentation
18  * and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  *==============================================================================
33  */
34 /*------------------------------------------------------------------------------
35  * Notes:
36  * Character set: UTF-8. (Non ASCII characters appear in this file)
37  *----------------------------------------------------------------------------*/
38 
39 /* ANSI includes etc. */
40 #include <math.h>
41 #include <time.h>
42 
43 /* Local and project includes */
44 #include "sky.h"
45 
46 #include "astron.h"
47 #include "general.h"
48 
49 /*
50  * Local #defines and typedefs
51  */
52 DEFINE_THIS_FILE; // For use by REQUIRE() - assertions.
53 
54 #ifdef INCLUDE_MJD_ROUTINES
55 #define MJD_1JAN1970 40587
56 #endif
57 #if 0
58 #define MJD_1JAN1904 16480
59 #define MJD_0JAN1900 15019
60 #define MJD_30DEC1899 15018
61 #define MJD_BASE 2400000.5 /* 17-Nov-1858, as a Julian Date */
62 #endif
63 
64 #define START_GREGORIAN 2299160.0 /* Start Gregorian calendar 15-Oct-1582 */
65 #define TIME_T_J2000 946728000 /* J2000.0 in Unix/C time_t format (s) */
66 
67 /*
68  * Prototypes for local functions (not called from other modules).
69  */
70 LOCAL double getDeltaUT1_s(double mjdUtc,
71  double usnoMjdBase,
72  double usnoCoeffC11,
73  double usnoCoeffC12);
74 LOCAL double calendarToJ2kd(int year, int month, int day);
75 
76 /*
77  * Global variables accessible by other modules
78  */
79 
80 
81 /*
82  * Local variables (not accessed by other modules)
83  */
84 
85 /*
86  *==============================================================================
87  *
88  * Implementation
89  *
90  *==============================================================================
91  *
92  * Global functions callable by other modules
93  *
94  *------------------------------------------------------------------------------
95  */
96 GLOBAL void sky_initTime(int deltaAT_s, double deltaUT_s, Sky_DeltaTs *d)
97 /*! This is one of three alternative routines for setting up the various delta
98  time values for use in ongoing calculations. (The other two are
99  sky_initTimeDetailed() and sky_initTimeSimple()). For an explanation of
100  these delta times, see \ref page-timescales.
101  \param[in] deltaAT_s (= TAI - UTC). Cumulative number of leap seconds
102  (seconds)
103  \param[in] deltaUT_s (= UT1 - UTC) (seconds). Valid range [-0.9,+0.9]
104  \param[out] d Fields initialised as follows:
105  - \a d->deltaUT_d initialised to \a deltaUT_s / 86400
106  - \a d->deltaT_d initialised to
107  (\a deltaAT_s + 32.184 - \a deltaUT_s) / 86400
108  - \a d->deltaTT_d initialised to
109  (\a deltaAT_s + 32.184) / 86400
110 
111  \par When to call this function
112  Call at program startup time.
113  Use this routine if you have opted for option 3A, 3B or 3D in the note at
114  the bottom of this file (see \ref page-timescales).
115  (Use sky_initTimeDetailed() if you have opted for option 3C.)
116 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
117 {
118  const double deltaTAI_s = 32.184; // TT - TAI (seconds)
119  double deltaTT_s; // TT - UTC (seconds)
120  double deltaT_s; // TT - UT1 (seconds)
121 
122  REQUIRE_NOT_NULL(d);
123 
124  deltaTT_s = deltaTAI_s + deltaAT_s;
125  d->deltaTT_d = deltaTT_s / 86400.0;
126 
127  deltaT_s = deltaTT_s - deltaUT_s;
128  d->deltaT_d = deltaT_s / 86400.0;
129  d->deltaUT_d = deltaUT_s / 86400.0;
130 }
131 
132 
133 
134 GLOBAL void sky_initTimeDetailed(double mjdUtc,
135  double usnoMjdBase,
136  double usnoCoeffC11,
137  double usnoCoeffC12,
138  int deltaAT_s,
139  Sky_DeltaTs *d)
140 /*! This is one of three alternative routines for setting up the various delta
141  time values for use in ongoing calculations. (The other two are
142  sky_initTime() and sky_initTimeSimple()). This routine uses a prediction
143  formula to calculate the value of delta_UT, rather than having it specified
144  directly. For an explanation of these delta times, see \ref page-timescales.
145  \param[in] mjdUtc Modified Julian Date (= JD - 2 400 000.5), UTC timescale
146  \param[in] usnoMjdBase,
147  usnoCoeffC11,
148  usnoCoeffC12
149  US Naval Observatory prediction formula values. These
150  three values are obtained from "Bulletin A" of the
151  International Earth Rotation Service (IERS), updated at
152  the IERS (& USNO) website from time to time
153  (see \ref page-timescales)
154  \param[in] deltaAT_s (= TAI - UTC). Cumulative number of leap seconds
155  (seconds)
156  \param[out] d Fields initialised as follows:
157  - \a d->deltaUT_d initialised using USNO prediction
158  formula
159  - \a d->deltaT_d initialised to
160  (\a deltaAT_s + 32.184) / 86400 - \a d->deltaUT_d
161  - \a d->deltaTT_d initialised to
162  (\a deltaAT_s + 32.184) / 86400
163 
164  \par When to call this function
165  Call this function at program startup time.
166  Use this routine if you have opted for option 3C in the note at the bottom
167  of this file (see \ref page-timescales). (Use sky_initTime() instead, if you
168  have opted for option 3A, 3B or 3D.) If your program runs uninterrupted for
169  many days, you may wish to call this function once per day with an updated
170  value for \a mjdUtc.
171 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
172 {
173  double deltaUT_s; // UT1 - UTC (seconds)
174 
175  deltaUT_s = getDeltaUT1_s(mjdUtc, usnoMjdBase, usnoCoeffC11, usnoCoeffC12);
176  sky_initTime(deltaAT_s, deltaUT_s, d);
177 }
178 
179 
180 
182 /*! This is one of three alternative routines for setting up the various delta
183  time values for use in ongoing calculations. (The other two are
184  sky_initTime() and sky_initTimeDetailed()). This function initialises the
185  delta times with simnple default values. For an explanation of
186  these delta times, see \ref page-timescales.
187  \param[out] d Fields initialised as follows:
188  - \a d->deltaUT_d initialised to 0.0
189  - \a d->deltaT_d initialised to (37 + 32.184) / 86400
190  - \a d->deltaTT_d initialised to (37 + 32.184) / 86400
191 
192  \par When to call this function
193  Call this function at program startup time.
194  Use this routine if you don't need the high accuracy of sub-second time. But
195  if you do need it, call sky_initTime() or sky_initTimeDetailed() instead.
196 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
197 {
198  /* Use the 2019 value of deltaAT_s. This value will slowly go out of date,
199  but in practice, accuracy will hardly be affected.
200  Assume deltaUT = 0.0 */
201  sky_initTime(37, 0.0, d);
202 }
203 
204 
205 
206 GLOBAL void sky_updateTimes(double j2kUtc_d,
207  const Sky_DeltaTs *d,
208  Sky_Times *t)
209 /*! Convert the given "J2KD" in the UTC timescale to the other timescales, and
210  pre-calculate some other quantities
211  \param[in] j2kUtc_d Date in "J2KD" form - i.e. the number of days elapsed
212  since 2000 Jan 1.5 (= JD - 2 451 545.0), UTC timescale,
213  as returned by sky_calTimeToJ2kd(), sky_unixTimeToJ2kd()
214  or sky_unixTimespecToJ2kd().
215  \param[in] d The various delta T values as set by one of
216  sky_initTime(), sky_initTimeDetailed() or
217  sky_initTimeSimple()
218  \param[out] t All fields are updated
219 
220  \par Reference
221  _Astronomical Almanac_ 2007, page B9 (for Earth Rotation Angle)
222 
223  \par When to call this function
224  Call this routine before calling any routine that calculates a celestial
225  position.
226 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
227 {
228  REQUIRE_NOT_NULL(d);
229  REQUIRE_NOT_NULL(t);
230 
231  t->mjdUtc = j2kUtc_d + MJD_J2000;
232 
233  t->j2kUT1_d = j2kUtc_d + d->deltaUT_d;
234  t->j2kTT_d = j2kUtc_d + d->deltaTT_d;
235  t->j2kTT_cy = t->j2kTT_d / JUL_CENT;
236  t->era_rad = (0.7790572732640 + 1.00273781191135488 * t->j2kUT1_d) * TWOPI;
237 }
238 
239 
240 
241 GLOBAL double sky_calTimeToJ2kd(int year, int month, int day,
242  int hour, int minute, double second,
243  double tz_h)
244 /*! Return the number of days (and fraction of a day) since noon 2000 Jan 1
245  * (UTC) of the given calendar date and time. This is one of three alternative
246  * routines that return a J2KD - the other two are sky_unixTimeToJ2kd() and
247  * sky_unixTimespecToJ2kd().
248  \returns days since Julian date 2 451 545.0, UTC timescale
249  \param[in] year, month, day calendar date
250  \param[in] hour valid range [0, 23]
251  \param[in] minute valid range [0, 59]
252  \param[in] second valid range [0.0, 60.0)
253  \param[in] tz_h time zone offset (hours), positive for zones
254  east of Greenwich (e.g. Australian time zones
255  AEST = +10.0, ACST = +9.5, AEDT = +11.0;
256  UTC = 0.0;
257  USA time zones are negative)
258  \par When to call this routine
259  Call this (or one of the alternative routines) before each call to
260  sky_updateTimes().
261 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
262 {
263  double jd2k;
264  double timeOfDay_d;
265 
266  jd2k = calendarToJ2kd(year, month, day);
267 
268  timeOfDay_d = (second + 60.0 * (minute + 60.0 * (hour - tz_h))) / 86400.0;
269  jd2k += timeOfDay_d;
270  return jd2k;
271 }
272 
273 
274 
275 GLOBAL double sky_unixTimeToJ2kd(time_t unixTime)
276 /*! Convert a time in Unix system time format to days since 2000 Jan 1, noon
277  UTC. This is one of three alternative
278  routines that return a J2KD - the other two are sky_calTimeToJ2kd() and
279  sky_unixTimespecToJ2kd().
280  \returns days since Julian Date 2 451 545.0, UTC timescale
281  \param[in] unixTime - time in C standard \c time_t (or unix) format
282  ((sort of) seconds since 1-Jan-1970 UTC)
283 
284  \note This routine has no better resolution than one second. Use routine
285  sky_unixTimespecToMjd() or sky_calTimeToMjd() instead, if you need
286  sub-second resolution.
287  \par When to call this routine
288  Call this (or one of the alternative routines) before each call to
289  sky_updateTimes().
290 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
291 {
292  return (double)(unixTime - TIME_T_J2000) / 86400.0;
293 }
294 
295 
296 
297 #ifdef POSIX_SYSTEM
298 GLOBAL double sky_unixTimespecToJ2kd(struct timespec uTs)
299 /*! Convert a time in Unix timespec format to days since 2000 Jan 1, noon UTC.
300  This is one of three alternative routines that return a J2KD - the other two
301  are sky_calTimeToJ2kd() and sky_unixTimeToJ2kd().
302  \returns days since Julian Date 2 451 545.0, UTC timescale
303  \param[in] uTs - time in "timespec" format, a combination of (sort of) seconds
304  since 1-Jan-1970 UTC, and nanoseconds of the current second.
305  This is the form returned by the POSIX \c clock_gettime()
306  function.
307  \note
308  The macro POSIX_SYSTEM must be defined at compile time to use this function.
309  \par When to call this routine
310  Call this (or one of the alternative routines) before each call to
311  sky_updateTimes().
312 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
313 {
314  return ((double)(uTs.tv_sec - TIME_T_J2000) + (double)uTs.tv_nsec / 1e9)
315  / 86400.0;
316 }
317 #endif
318 
319 
320 
321 GLOBAL void sky_j2kdToCalTime(double j2k_d,
322  int *year,
323  int *month,
324  int *day,
325  int *hour,
326  int *minute,
327  double *second)
328 /*! This procedure converts the integral part of the date in "J2KD" form to a
329  calendar date and the fractional part to hours, minutes and seconds. If the
330  returned date is from 1582-10-15 or later, it is a Gregorian calendar date.
331  If the returned date is 1582-10-04 or earlier, it is a Julian calendar date.
332  \param[in] j2k_d Days since J2000.0 (= Julian Date - 2 451 545.0)
333  Valid range: j2k_d >= -2447065, otherwise incorrect results
334 
335  \param[out] year, month, day calendar date
336  \param[out] hour, minute, second time of day
337 
338  \par Reference
339  The algorithm is based on a method of D A Hatcher, _Quarterly Journal of the
340  Royal Astronomical Society_ 1984, Vol 25, pp 53-55. It is valid for dates
341  after JD = 4480 (7 April 4701 BC, Julian calendar).
342 
343  \note To obtain calendar date and time for any timezone other than UTC, add
344  the time zone offset (in units of fraction of a day) to \a mjd before
345  calling this routine. The \a timezone_d field of the site properties struct
346  (type Sky_SiteProp) provides this value.
347  \par
348  If \a second turns out to be within half a millisecond of the next round
349  minute, this routine rounds time upwards.
350 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
351 {
352  int32_t j;
353  int32_t n4;
354  int32_t n10;
355  double timeOfDay;
356  double j2kdate;
357 
358  j2k_d += 0.5; // Change from elapsed since noon to elapsed since 00:00
359  j2kdate = floor(j2k_d);
360  timeOfDay = (j2k_d - j2kdate) * 24.0;
361  *hour = (int)timeOfDay;
362  timeOfDay = (timeOfDay - *hour) * 60.0;
363  *minute = (int)timeOfDay;
364  *second = (timeOfDay - *minute) * 60.0;
365 
366  /* Round up if within half a millisecond of a round minute */
367  if ((int)(*second + 0.0005) == 60) {
368  *second = 0.0;
369  (*minute)++;
370  if (*minute == 60) {
371  *minute = 0;
372  (*hour)++;
373  if (*hour == 24) {
374  *hour = 0;
375  j2kdate++;
376  }
377  }
378  }
379 
380 
381  j = (int32_t)j2kdate + 2451545;
382 
383  if (j <= START_GREGORIAN) {
384  n4 = 4 * j;
385  } else {
386  n4 = 4 * (j + ((((4 * j - 17918) / 146097) * 3 + 2) >> 2) - 37);
387  }
388 
389  n10 = 10 * (((n4 - 237) % 1461) >> 2) + 5;
390 
391  *year = (int)(n4 / 1461 - 4712);
392  *month = (n10 / 306 + 2) % 12 + 1;
393  *day = (n10 % 306) / 10 + 1;
394 }
395 
396 
397 
398 #ifdef INCLUDE_MJD_ROUTINES
399 GLOBAL double sky_calTimeToMjd(int year, int month, int day,
400  int hour, int minute, double second,
401  double tz_h)
402 /*! Return the Modified Julian Date (= Julian Date - 2 400 000.5) of a calendar
403  date and time.
404  \returns the Modified Julian Date, UTC timescale
405  \param[in] year, month, day calendar date
406  \param[in] hour valid range [0, 23]
407  \param[in] minute valid range [0, 59]
408  \param[in] second valid range [0.0, 60.0)
409  \param[in] tz_h time zone offset (hours), positive for zones
410  east of Greenwich (e.g. Australian time zones
411  AEST = +10.0, ACST = +9.5, AEDT = +11.0;
412  UTC = 0.0;
413  USA time zones are negative)
414  \note
415  The macro INCLUDE_MJD_ROUTINES must be defined at compile time to use this
416  function.
417 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
418 {
419  double mjd;
420  double timeOfDay_d;
421 
422  mjd = calendarToJ2kd(year,month, day) + MJD_J2000;
423 
424  timeOfDay_d = (second + 60.0 * (minute + 60.0 * (hour - tz_h))) / 86400.0;
425  mjd += timeOfDay_d;
426  return mjd;
427 }
428 
429 
430 
431 GLOBAL double sky_unixTimeToMjd(time_t unixTime)
432 /*! Convert a time in Unix system time format to Modified Julian Date
433  \returns the Modified Julian Date (= Julian Date - 2 400 000.5), UTC timescale
434  \param[in] unixTime - time in C standard \c time_t (or unix) format
435  ((sort of) seconds since 1-Jan-1970 UTC)
436 
437  \note This routine has no better resolution than one second. Use routine
438  sky_unixTimespecToMjd() or sky_calTimeToMjd() instead, if you need
439  sub-second resolution
440  \note
441  The macro INCLUDE_MJD_ROUTINES must be defined at compile time to use this
442  function.
443 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
444 {
445  return (double)unixTime / 86400.0 + MJD_1JAN1970;
446 }
447 
448 
449 
450 #ifdef POSIX_SYSTEM
451 GLOBAL double sky_unixTimespecToMjd(struct timespec uTs)
452 /*! Convert a time in Unix timespec format to Modified Julian Date
453  \returns the Modified Julian Date (= Julian Date - 2 400 000.5), UTC timescale
454  \param[in] uTs - time in "timespec" format, a combination of (sort of) seconds
455  since 1-Jan-1970 UTC, and nanoseconds of the current second.
456  This is the form returned by the POSIX \c clock_gettime()
457  function.
458  \note
459  The macros INCLUDE_MJD_ROUTINES and POSIX_SYSTEM must be defined at compile
460  time to use this function.
461 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
462 {
463  return ((double)uTs.tv_sec + (double)uTs.tv_nsec / 1e9) / 86400.0
464  + MJD_1JAN1970;
465 }
466 #endif
467 
468 
469 
470 GLOBAL void sky_updateTimesFromMjd(double mjdUtc,
471  const Sky_DeltaTs *d,
472  Sky_Times *t)
473 /*! Convert the given MJD in the UTC timescale to the other timescales, and
474  pre-calculate some other quantities
475  Inputs
476  \param[in] mjdUtc Modified Julian Date (= JD - 2 400 000.5), UTC timescale
477  \param[in] d The various delta T values as set by one of
478  sky_initTime(), sky_initTimeDetailed() or
479  sky_initTimeSimple()
480  \param[out] t All fields except \a gmst_rad and \a gast_rad are updated
481 
482  \par Reference
483  _Astronomical Almanac_ 2007, page B9 (for Earth Rotation Angle)
484  \note
485  The macro INCLUDE_MJD_ROUTINES must be defined at compile time to use this
486  function.
487 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
488 {
489  double mjdUT1; // Modified Julian Date (= JD - 2 400 000.5), UT1 timescale
490  double mjdTT; // MJD as above, TT timescale
491 
492  REQUIRE_NOT_NULL(d);
493  REQUIRE_NOT_NULL(t);
494 
495  t->mjdUtc = mjdUtc;
496  mjdUT1 = mjdUtc + d->deltaUT_d;
497  mjdTT = mjdUtc + d->deltaTT_d;
498 
499  t->j2kUT1_d = mjdUT1 - MJD_J2000;
500  t->j2kTT_d = mjdTT - MJD_J2000;
501  t->j2kTT_cy = t->j2kTT_d / JUL_CENT;
502  t->era_rad = (0.7790572732640 + 1.00273781191135488 * t->j2kUT1_d) * TWOPI;
503 }
504 
505 
506 
507 GLOBAL void sky_mjdToCalTime(double mjd,
508  int *year,
509  int *month,
510  int *day,
511  int *hour,
512  int *minute,
513  double *second)
514 /*! This procedure converts the integral part of the Modified Julian Date to a
515  calendar date and the fractional part to hours, minutes and seconds. If the
516  returned date is from 1582-10-15 or later, it is a Gregorian calendar date.
517  If the returned date is 1582-10-04 or earlier, it is a Julian calendar date.
518  \param[in] mjd - Modified Julian Date (= Julian Date - 2 400 000.5)
519  Valid range: mjd > -2395520, otherwise incorrect results
520 
521  \param[out] year, month, day - calendar date
522  \param[out] hour, minute, second - time of day
523 
524  \par Reference
525  The algorithm is based on a method of D A Hatcher, _Quarterly Journal of the
526  Royal Astronomical Society_ 1984, Vol 25, pp 53-55. It is valid for dates
527  after MJD = -2395520 (1st March 4701 BC).
528 
529  \note To obtain calendar date and time for any timezone other than UTC, add
530  the time zone offset (in units of fraction of a day) to \a mjd before
531  calling this routine. The \a timezone_d field of the site properties struct
532  (type Sky_SiteProp) provides this value.
533 
534  \note
535  The macro INCLUDE_MJD_ROUTINES must be defined at compile time to use this
536  function.
537 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
538 {
539  int32_t j;
540  int32_t n4;
541  int32_t n10;
542  double timeOfDay;
543  double mjdate;
544 
545  mjdate = floor(mjd);
546  timeOfDay = (mjd - mjdate) * 24.0;
547  *hour = (int)timeOfDay;
548  timeOfDay = (timeOfDay - *hour) * 60.0;
549  *minute = (int)timeOfDay;
550  *second = (timeOfDay - *minute) * 60.0;
551 
552  j = (int32_t)mjdate + 2400001;
553 
554  if (j <= START_GREGORIAN) {
555  n4 = 4 * j;
556  } else {
557  n4 = 4 * (j + (((4 * j - 17918) / 146097) * 3 + 2) / 4 - 37);
558  }
559 
560  n10 = 10 * (((n4 - 237) % 1461) / 4) + 5;
561 
562  *year = (int)(n4 / 1461 - 4712);
563  *month = (n10 / 306 + 2) % 12 + 1;
564  *day = (n10 % 306) / 10 + 1;
565 }
566 #endif
567 
568 
569 
570 GLOBAL void sky_setPolarMotion(double xPolar_as,
571  double yPolar_as,
572  double t_cy,
573  Sky_PolarMot *polar)
574 /*! Update polar motion correction.
575  \param[in] xPolar_as Polar motion in x (arcseconds)
576  \param[in] yPolar_as Polar motion in y (arcseconds)
577  \param[in] t_cy Julian centuries since J2000.0, TT timescale
578  \param[out] polar Polar motion params and rotation matrix, calculated from
579  R1(-y) * R2(-x) * R3(s')
580 
581  \par Reference
582  _Astronomical Almanac_ 2007, page B80
583 
584  \par When to call this function
585  This routine needs to be called only when polar motion parameters
586  change - which is no more frequently than once per day. This effect is so
587  small that it can be ignored altogether with only a tiny loss of accuracy.
588 
589  \note Every time this routine is called, the routine
590  sky_adjustSiteForPolarMotion() must be called for every site. (Typically
591  this will be only one site.)
592 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
593 {
594  double sPrime_as; // Secular drift
595  V3D_Matrix yM, xM, sM, tempM; // temporary rotation matrices
596 
597  REQUIRE_NOT_NULL(polar);
598 
599  polar->xPolar_as = xPolar_as;
600  polar->yPolar_as = yPolar_as;
601 
602  if ((xPolar_as == 0.0) && (yPolar_as == 0.0)) {
603  /* Yes I know one is not supposed to compare floats for equality, but
604  these two values are entered directly by a user, not calculated. So
605  being exactly zero is quite likely. */
606 
607  polar->correctionInUse = false;
608 
609  } else {
610  polar->correctionInUse = true;
611 
612  /* Secular drift of TIO. This is so tiny it can be ignored altogether.
613  But if we are to take it into account (as we do here) it only needs
614  to be updated very infrequently. */
615  sPrime_as = -0.000047 * t_cy;
616 
617  /* matrix is R1(-y) * R2(-x) * R3(s') */
618  v3d_createRotationMatrix(&sM, Zaxis, arcsecToRad(sPrime_as));
619  v3d_createRotationMatrix(&xM, Yaxis, arcsecToRad(-xPolar_as));
620  v3d_createRotationMatrix(&yM, Xaxis, arcsecToRad(-yPolar_as));
621  v3d_multMxM(&polar->corrM, &yM, v3d_multMxM(&tempM, &xM, &sM));
622  }
623 }
624 
625 
626 
627 /*
628  *------------------------------------------------------------------------------
629  *
630  * Local functions (not called from other modules).
631  *
632  *------------------------------------------------------------------------------
633  */
634 LOCAL double getDeltaUT1_s(double mjdUtc,
635  double usnoMjdBase,
636  double usnoCoeffC11,
637  double usnoCoeffC12)
638 /* Calculate deltaUT1 using the prediction formula from the US Naval Observatory
639  deltaUT1 is the difference between UTC and the earth's actual rotational
640  position, and it is subject to variations which are difficult to predict.
641  Therefore this prediction is approximate.
642  Returns - deltaUT1 (= UT1 - UTC) (seconds)
643  Inputs
644  mjdUtc - Modified Julian Date (= JD - 2 400 000.5), UTC timescale
645  usnoMjdBase - US Naval Observatory prediction formula values. These three
646  usnoCoeffC11 values are updated at the USNO website from time to time
647  usnoCoeffC12
648 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
649 {
650  const double A = 0.022; // Markowitz time series constants (units = s)
651  const double B = -0.012;
652  const double C = -0.006;
653  const double D = 0.007;
654  const double bYear = TROP_CENT / 100.0; // No of days in Besselian year
655  // (= tropical year at B1900.0)
656  double n;
657  double mjDateBYR;
658  double delBYR;
659  double seasonalVariation;
660  double deltaUT_s;
661 
662  /* Calculate the MJD of the beginning of the current Besselian year and
663  the fractional part of the Besselian year to date */
664  n = floor((mjdUtc - MJD_B1900) / bYear);
665  mjDateBYR = MJD_B1900 + n * bYear;
666  delBYR = (mjdUtc - mjDateBYR) / bYear;
667 
668  /* Calculate the seasonal variation of UT in seconds, using the standard
669  Markowitz time series (this is UT2 - UT1) */
670  seasonalVariation = A * sin(TWOPI * delBYR)
671  + B * cos(TWOPI * delBYR)
672  + C * sin(4.0 * PI * delBYR)
673  + D * cos(4.0 * PI * delBYR);
674 
675  /* Calculate the current value of Delta UT1 (= UT1 - UTC) in seconds from
676  the seasonal variations and the USNO prediction formula.
677  UT1 - UTC = (UT2 - UTC) - Seasonal_Variation */
678  deltaUT_s = usnoCoeffC11 + usnoCoeffC12 * (mjdUtc - usnoMjdBase)
679  - seasonalVariation;
680 
681  return deltaUT_s;
682 }
683 
684 
685 
686 LOCAL double calendarToJ2kd(int year, int month, int day)
687 /* Return the number of days elapsed since Greenwich noon on 2000-01-01 of a
688  calendar date, as at midnight at the beginning of the day, UTC.
689  Returns - the days elapsed since Julian date 2 451 545.0 (2000 Jan 1.5).
690  Because of the way this is defined, the result will always be an
691  integer plus 0.5
692  Inputs
693  year, month, day - calendar date
694 
695  Reference
696  The algorithm is inspired by a method of D A Hatcher, _Quarterly Journal of
697  the Royal Astronomical Society_ 1984, Vol 25, pp 53-55.
698 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
699 {
700  /* Force 32-bit integer arithmetic, in case type int is only a 16-bit type */
701  int32_t year32 = year;
702  int32_t month32 = month - 3;
703  int32_t day32 = day;
704  int32_t j2kdi_d; // Integer part of the J2KD date.
705 
706 
707  if (month32 < 0) {
708  month32 += 12;
709  year32--;
710  }
711  j2kdi_d = ((1461 * (year32 + 4712)) >> 2) - 2451487;
712  j2kdi_d += (306 * month32 + 5) / 10 + day32;
713  if (j2kdi_d > -152386) {
714  /* Supplied date must have been later than 4-Oct-1582. So use Gregorian
715  calendar */
716  j2kdi_d -= ((3 * (year32 / 100 - 11)) >> 2) + 7;
717  }
718  return (double)j2kdi_d + 0.5;
719 }
720 
721 
722 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
723 
724 /*! \page page-timescales Timescales, and converting between them
725 
726  There are five timescales of interest:
727  - TAI - International Atomic Time, as maintained by a network of atomic
728  clocks
729  - TT - Terrestrial Time. Used for astronomical observations. It is designed
730  to be free of the irregularities of the rotation of the earth. It is
731  based on TAI, and for historical reasons has the following
732  relationship
733  TT = TAI + 32.184 seconds
734  - UT1 - Universal Time. This is the timescale that essentially represents
735  the rotation of the earth, and therefore is subject to the
736  fluctuations that occur in that rotation. (Also called simply UT)
737  - UTC - Coordinated Universal Time. This is the time we all use. It is
738  based on TAI, but occasionally a leap second is inserted (or, in
739  theory, removed) to keep UTC within 0.9 seconds of UT1.
740  - local civil time - This is UTC + (local timezone offset)
741 
742  What this means for astronomical observations is that we need to obtain the
743  time in the UTC timescale (either directly, or from the local civil time)
744  and calculate TT and UT1 from it.
745  TT is needed to calculate the celestial positions of objects in the sky, and
746  UT1 is needed for converting that celestial coordinate into a direction as
747  seen from a site on the surface of the earth, as it rotates.
748 
749  We need three pieces of information.
750  1. ΔTAI = TT - TAI = 32.184 s (a fixed value, as described above)
751  2. ΔAT = TAI - UTC. This is the number of leap seconds that have been
752  added in the past. This figure is available from the International
753  Earth Rotation Service (IERS) at the Observatoire de Paris, via
754  its regular Bulletin C, available at
755  https://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat
756  When accessed in August 2019, this value was 37 seconds. (Note that
757  Bulletin C expresses the relation as UTC - TAI, so it said -37 s.)
758  3. ΔUT = UT1 - UTC. This is available from the IERS, at the US Naval
759  Observatory website, in its Bulletin A.
760  https://maia.usno.navy.mil/ser7/ser7.dat or from
761  https://www.iers.org/IERS/EN/Publications/Bulletins/bulletins.html
762  It also gives the leap seconds, this time in the form TAI - UTC.
763  The value of deltaUT varies from day to day, but because its value
764  is always within the range -0.9..+0.9, you have four choices as to
765  what to do about it.
766  - \b A. Ignore it altogether, and assume it is equal to zero. This will
767  have no effect on the calculation of celestial coordinates, but
768  could introduce an error of up to 0.00375° in the direction from
769  the ground, if you are unlucky. For most purposes, this is good
770  enough. Initialise your code with the function
771  sky_initTimeSimple(), or call sky_initTime() with \a deltaUT_s
772  set to 0.0
773  - \b B. Check Bulletin A every few months, and call sky_initTime() with
774  \a deltaUT_s set to the value of DUT1 given near the top of the
775  file, described as "transmitted with time signals". This will
776  give a better approximation.
777  - \b C. Check Bulletin A every few months, and find the PREDICTIONS
778  section, and look for a line that looks like the following:
779 
780  UT1-UTC = -0.1733 - 0.00056 (MJD - 58725) - (UT2-UT1)
781  ^^^^^^^ ^^^^^^^^^ ^^^^^
782  CoeffC11 CoeffC12 MJDbase
783 
784  Take the three numbers and pass them to the function
785  sky_initTimeDetailed()
786  which will use this formula to predict the value. This method
787  is good enough be used by large astronomical telescopes.
788  - \b D. Check Bulletin A every week, and use the values in the table for
789  each day. Almost nobody should need to do this.
790  .
791  There are two more such quantities, but we can calculate them from the above
792  4. ΔT = TT - UT1. This is also obtainable from
793  ΔT = ΔTAI + ΔAT - ΔUT.
794  5. ΔTT = TT - UTC. This is also obtainable from
795  ΔTT = ΔTAI + ΔAT.
796 
797  Once you have set the above with sky_initTime(), sky_initTimeSimple() or
798  sky_initTimeDetailed(), then calling sky_updateTimes() will convert a
799  time in the UTC timescale to various times in the other timescales, ready
800  for use.
801 
802  Pursuit of really accurate UT1 (as outlined in 3C and 3D above) really only
803  makes sense if your system clock is accurately synchronised to UTC.
804 
805  But wait! There's more! If you want really accurate coordinates, you have to
806  take into account another variation in the earth's rotation. The polar axis
807  itself moves around a tiny amount. This is called polar motion. If this
808  matters to you, the values of polar motion can be obtained from the same
809  Bulletin A described above. But for normal purposes, you can just enter zero
810  for these parameters (or just not call sky_setPolarMotion() at all).
811 */
812 
813 /*
814  ~~~~~~~~~~~~~~
815 Eh? 2007 edition of Astronomical almanac, page B8, suggests an error of 1 s in
816 deltaT (and therefore in deltaUT) here gives an error of 1.5e-6 arcseconds or
817 0.1e-6 seconds of time in the sidereal time calculations. This is clearly a
818 factor of 10^7 smaller than I calculated above. What have I done wrong?
819 Maybe it is referring to an error in TT when calculating GMST, rather than an
820 error in Du. If Du has the error, it gives an error in earth rotation angle.
821 
822 Yes, on further examination, this is assuming you have the correct UT1, but
823 your TT is wrong. That is, deltaT is wrong, but deltaUT is correct, so it
824 must be deltaTT (or deltaAT) which is wrong by some number of whole seconds.
825  ~~~~~~~~~~~~~~
826 */
827 
828 /*! \page page-time-variables Time variables
829  * Astronomical algorithms typically use a simple number as the time variable.
830  * This can take one of several forms:
831  * - Julian Date (JD). The number of days since Greenwich noon, 1 January
832  * 4713 BC (on the Julian calendar, as if it had existed back then). So
833  * for example 2020-04-13 9:00 am is 2458952.875
834  * - Modified Julian Date (MJD). The Julian Date minus 2 400 000.5. This
835  * makes it the number of days since Greenwich midnight at the start of
836  * 1858-11-17. So for example, 2020-04-13 9:00 am is 58952.375
837  * - J2KD (My abbreviation). Days since Greenwich noon, 2000-01-01.
838  * Equals JD - 2 451 545.0 (= MJD - 51544.5). So for example,
839  * 2020-04-13 9:00 am is 7407.875
840  *
841  * The three above could easily represent times in the TT or UT1 timescales,
842  * so the relevant timescale should be specified. (See \ref page-timescales)
843  * .
844  * - J2KC (My abbreviation). Julian centuries since Greenwich noon,
845  * 2000-01-01. Equals J2KD ÷ 36525
846  * - J2KM (My abbreviation). Julian millennia since Greenwich noon,
847  * 2000-01-01. Equals J2KC ÷ 10
848  *
849  * Typically the two above will be used for times in the TT timescale.
850  * .
851  * - Julian epoch (e.g. J2000.0 or J2020.5). Calculated from
852  * J[2000.0 + J2KD / 365.25], TT timescale. This is used
853  * for star catalogue positions. For example, J2000.0 is JD 2 451 545.0.
854  * And 2-Jul-2020 3:00:00 (TT) is J2020.5
855  * - Besselian epoch (e.g. B1950.0). This is calculated from
856  * B[1900.0 + JD - (2 415 020.313 52) / 365.242 198 781].
857  * So B1950.0 is JD 2 433 282.423. Besselian epochs are often associated
858  * with FK4 catalogue positions. These are not supported by this
859  * software (yet).
860  *
861  * Many astronomical algorithms use one of the forms based on time since
862  * J2000.0 (i.e. either J2KD, J2KC or J2KM) as their time variable.
863  * For this reason, the conversion routines from calendar date/time
864  * (sky_calTimeToJ2kd())and from the unix \c time_t and \c timespec formats
865  * (sky_unixTimeToJ2kd() and sky_unixTimespecToJ2kd()) return a
866  * variable in the J2KD form (rather than the JD or MJD forms).
867  */
868 
TROP_CENT
#define TROP_CENT
Length of Tropical Century in days.
Definition: astron.h:26
v3d_multMxM
V3D_Matrix * v3d_multMxM(V3D_Matrix *destM, const V3D_Matrix *srcM1, const V3D_Matrix *srcM2)
Multiply 3x3 matrix by 3x3 matrix to give a new 3x3 matrix, as per [destM] = [srcM1]*[srcM2].
Definition: vectors3d.c:517
general.h
general.h - definitions of general use to (standard) C programs
sky_j2kdToCalTime
void sky_j2kdToCalTime(double j2k_d, int *year, int *month, int *day, int *hour, int *minute, double *second)
This procedure converts the integral part of the date in "J2KD" form to a calendar date and the fract...
Definition: sky-time.c:321
Sky_DeltaTs::deltaT_d
double deltaT_d
TT - UT1, scaled to days.
Definition: sky.h:168
Sky_DeltaTs
This structure contains relatively constant data, and is set up by one of the three functions sky_ini...
Definition: sky.h:166
Sky_DeltaTs::deltaTT_d
double deltaTT_d
TT - UTC, scaled to days.
Definition: sky.h:169
astron.h
astron.h - assorted definitions useful for astronomy
sky_setPolarMotion
void sky_setPolarMotion(double xPolar_as, double yPolar_as, double t_cy, Sky_PolarMot *polar)
Update polar motion correction.
Definition: sky-time.c:570
sky_updateTimes
void sky_updateTimes(double j2kUtc_d, const Sky_DeltaTs *d, Sky_Times *t)
Convert the given "J2KD" in the UTC timescale to the other timescales, and pre-calculate some other q...
Definition: sky-time.c:206
MJD_J2000
#define MJD_J2000
MJD of Fundamental Epoch J2000.0.
Definition: astron.h:25
LOCAL
#define LOCAL
C has some very silly default behaviours.
Definition: general.h:133
PI
#define PI
,
Definition: general.h:158
GLOBAL
#define GLOBAL
See above.
Definition: general.h:134
Sky_Times::j2kTT_cy
double j2kTT_cy
Julian centuries since J2000.0, TT timescale [T].
Definition: sky.h:188
REQUIRE_NOT_NULL
#define REQUIRE_NOT_NULL(pointer_)
,
Definition: general.h:308
Sky_Times
This structure contains the continuously varying time (and earth rotation) data, in various forms tha...
Definition: sky.h:184
Sky_Times::era_rad
double era_rad
Earth Rotation Angle (radian) [θ].
Definition: sky.h:189
Sky_DeltaTs::deltaUT_d
double deltaUT_d
UT1 - UTC, scaled to days.
Definition: sky.h:167
TWOPI
#define TWOPI
,
Definition: general.h:160
MJD_B1900
#define MJD_B1900
MJD of Besselian std epoch B1900.0.
Definition: astron.h:23
V3D_Matrix
3x3 matrix.
Definition: vectors3d.h:51
arcsecToRad
static double arcsecToRad(double angle_as)
Returns angle_as converted from arcseconds to radians.
Definition: astron.h:43
sky_initTimeDetailed
void sky_initTimeDetailed(double mjdUtc, double usnoMjdBase, double usnoCoeffC11, double usnoCoeffC12, int deltaAT_s, Sky_DeltaTs *d)
This is one of three alternative routines for setting up the various delta time values for use in ong...
Definition: sky-time.c:134
Sky_Times::j2kTT_d
double j2kTT_d
days since J2000.0, TT timescale [D]
Definition: sky.h:187
sky_unixTimeToJ2kd
double sky_unixTimeToJ2kd(time_t unixTime)
Convert a time in Unix system time format to days since 2000 Jan 1, noon UTC.
Definition: sky-time.c:275
Sky_PolarMot
This structure contains polar motion parameters and a rotation matrix.
Definition: sky.h:196
DEFINE_THIS_FILE
#define DEFINE_THIS_FILE
Leave DEFINE_THIS_FILE undefined.
Definition: general.h:252
sky_initTime
void sky_initTime(int deltaAT_s, double deltaUT_s, Sky_DeltaTs *d)
This is one of three alternative routines for setting up the various delta time values for use in ong...
Definition: sky-time.c:96
sky.h
sky.h - structures and routines for astronomical observing & tracking
v3d_createRotationMatrix
V3D_Matrix * v3d_createRotationMatrix(V3D_Matrix *destM, V3D_AxisNames axis, double angle_rad)
Creates a matrix to rotate a coordinate system about an axis.
Definition: vectors3d.c:377
JUL_CENT
#define JUL_CENT
Length of Julian Century in days.
Definition: astron.h:27
Sky_Times::mjdUtc
double mjdUtc
Modified Julian Date (= JD - 2 400 000.5), UTC.
Definition: sky.h:185
Sky_Times::j2kUT1_d
double j2kUT1_d
days since J2000.0, UT1 timescale [Du]
Definition: sky.h:186
sky_initTimeSimple
void sky_initTimeSimple(Sky_DeltaTs *d)
This is one of three alternative routines for setting up the various delta time values for use in ong...
Definition: sky-time.c:181
sky_calTimeToJ2kd
double sky_calTimeToJ2kd(int year, int month, int day, int hour, int minute, double second, double tz_h)
Return the number of days (and fraction of a day) since noon 2000 Jan 1 (UTC) of the given calendar d...
Definition: sky-time.c:241