XLALCivilTime.c

Go to the documentation of this file.
00001 /*
00002 *  Copyright (C) 2007 Bernd Machenschalk, Jolien Creighton, Kipp Cannon
00003 *
00004 *  This program is free software; you can redistribute it and/or modify
00005 *  it under the terms of the GNU General Public License as published by
00006 *  the Free Software Foundation; either version 2 of the License, or
00007 *  (at your option) any later version.
00008 *
00009 *  This program is distributed in the hope that it will be useful,
00010 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 *  GNU General Public License for more details.
00013 *
00014 *  You should have received a copy of the GNU General Public License
00015 *  along with with program; see the file COPYING. If not, write to the
00016 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00017 *  MA  02111-1307  USA
00018 */
00019 
00020 /** \file
00021  * \ingroup std
00022  * \author Chin, D. W. and Creighton, J. D. E.
00023  * \brief XLAL routines for converting civil time structures to GPS times.
00024  *
00025  * Civil time structures, represented in the C library by the \c struct \c tm
00026  * structure, known as a broken down time structure, gives the time by normal
00027  * measures (hours, minutes, seconds since midnight, day of the month, month of
00028  * the year, and year).  LAL uses seconds since the the GPS epoch as its
00029  * reference.  The GPS epoch is defined to be 0h UTC 6 Jan 1980 in civil time.
00030  * The process of converting between a civil time and a GPS time is almost done
00031  * by standard C library functions, though the C library functions use a
00032  * different reference epoch, which is called the UNIX epoch.
00033  *
00034  * The tricky bits are:
00035  *   - What about time zones?
00036  *   - What about leap seconds?
00037  *
00038  * This code does not deal with time zones at all.  All civil time structures
00039  * are taken to be in Coordinated Universal Time or UTC.  The user must convert
00040  * local times into UTC before using these routines.
00041  *
00042  * Leap seconds are accounted for.  But since the addition and subtraction of
00043  * leap seconds is not deterministic, a leap second table needs to be
00044  * maintained to account for the number of leap seconds in effect at a
00045  * particular time.
00046  *
00047  * Leap seconds are defined as the difference between UTC and a (yet another)
00048  * standard of time called the International Atomic Time or TAI.  UTC is
00049  * ultimately determined by the position of the stars, so it is occationally
00050  * altered by a second to keep the location of fixed points on the celestial
00051  * sphere correct to within plus or minus 0.9 seconds.  TAI, like the GPS time
00052  * used in LAL, is just the number of seconds since some epoch and is not
00053  * affected by leap seconds.  The difference between TAI and UTC, TAI-UTC,
00054  * is the number of leap seconds.
00055  *
00056  * Note that UTC is fixed to atomic time, though there are an integer number
00057  * of leap seconds.  The real civil time, as measured in terms of points on
00058  * the celestial sphere, is UT1.  UTC and UT1 are kept within 0.9 seconds of
00059  * each other by the introduction of leap seconds.  But if what you want is
00060  * UT1 note that UTC can be off by up to about a seconds.  For this reason,
00061  * we assume that accuracy at the second scale is sufficient, so the routines
00062  * have only second precision.  If you need more accuracy, you'll need to be
00063  * monitoring UT1.
00064  *
00065  * Another way of representing the civil time in in terms of Julian days.
00066  * There is a routine for converting a UTC time into Julian days.
00067  * The inverse conversion is not attempted.
00068  *
00069  */
00070 
00071 #include <math.h>
00072 #include <time.h>
00073 #include <string.h>
00074 #include <lal/LALStdlib.h>
00075 #include <lal/Date.h>
00076 
00077 #include "XLALLeapSeconds.h" /* contains the leap second table */
00078 
00079 #include <lal/LALRCSID.h>
00080 NRCSID (XLALCIVILTIMEC,"$Id: XLALCivilTime.c,v 1.8 2008/09/06 08:00:49 reinhard Exp $");
00081 
00082 /* in case this is not prototyped ... */
00083 struct tm * gmtime_r(const time_t *, struct tm *);
00084 
00085 
00086 /* change in TAI-UTC from previous second:
00087  *
00088  * return values:
00089  *   -1: TAI-UTC has decreased by one second (this hasn't happened yet).
00090  *       In this case, UTC will skip a second going from 23:59:58 at
00091  *       gpssec-1 to 00:00:00 (of the following day) at gpssec.
00092  *    0: This is not a leap second: UTC has just advanced one second
00093  *       in going from gpssec-1 to gpssec.
00094  *   +1: TAI-UTC has increased by one second (this hasn't happened yet)
00095  *       In this case, UTC will add a second going from 23:59:59 at
00096  *       gpssec-1 to 23:59:60 (of the same day) at gpssec.
00097  */
00098 static int delta_tai_utc( INT4 gpssec )
00099 {
00100   int leap;
00101 
00102   /* assume calling function has already checked this */
00103   /*
00104   if ( gpssec <= leaps[0].gpssec )
00105   {
00106     fprintf( stderr, "error: don't know leap seconds before gps time %d\n",
00107         leaps[0].gpssec );
00108     abort();
00109   }
00110   */
00111 
00112   for ( leap = 1; leap < numleaps; ++leap )
00113     if ( gpssec == leaps[leap].gpssec )
00114       return leaps[leap].taiutc - leaps[leap-1].taiutc;
00115 
00116   return 0;
00117 }
00118 
00119 
00120 /** Returns the leap seconds TAI-UTC at a given GPS second. */
00121 int XLALLeapSeconds( INT4 gpssec /**< [In] Seconds relative to GPS epoch.*/ )
00122 {
00123   static const char func[] = "XLALLeapSeconds";
00124   int leap;
00125 
00126   if ( gpssec < leaps[0].gpssec )
00127   {
00128     XLALPrintError( "XLAL Error - Don't know leap seconds before GPS time %d\n",
00129         leaps[0].gpssec );
00130     XLAL_ERROR( func, XLAL_EDOM );
00131   }
00132 
00133   /* scan leap second table and locate the appropriate interval */
00134   for ( leap = 1; leap < numleaps; ++leap )
00135     if ( gpssec < leaps[leap].gpssec )
00136       break;
00137 
00138   return leaps[leap-1].taiutc;
00139 }
00140 
00141 
00142 /** Returns the leap seconds GPS-UTC at a given GPS second. */
00143 int XLALGPSLeapSeconds( INT4 gpssec /**< [In] Seconds relative to GPS epoch.*/ )
00144 {
00145   static const char func[] = "XLALGPSLeapSeconds";
00146   int leapTAI;
00147   int leapGPS;
00148 
00149   if ( gpssec < leaps[0].gpssec )
00150   {
00151     XLALPrintError( "XLAL Error - Don't know leap seconds before GPS time %d\n",
00152         leaps[0].gpssec );
00153     XLAL_ERROR( func, XLAL_EDOM );
00154   }
00155 
00156   leapTAI = XLALLeapSeconds ( gpssec );
00157 
00158   leapGPS = leapTAI - 19;       /* subtract 19 seconds to get leap-seconds wrt to GPS epoch */
00159 
00160   return leapGPS;
00161 }
00162 
00163 
00164 /** Returns the leap seconds TAI-UTC for a given UTC broken down time. */
00165 int XLALLeapSecondsUTC( const struct tm *utc /**< [In] UTC as a broken down time.*/ )
00166 {
00167   static const char func[] = "XLALLeapSecondsUTC";
00168   REAL8 jd;
00169   int leap;
00170 
00171   jd = XLALJulianDay( utc );
00172   if ( XLAL_IS_REAL8_FAIL_NAN( jd ) )
00173     XLAL_ERROR( func, XLAL_EFUNC );
00174 
00175   if ( jd < leaps[0].jd )
00176   {
00177     XLALPrintError( "XLAL Error - Don't know leap seconds before Julian Day %9.1f\n", leaps[0].jd );
00178     XLAL_ERROR( func, XLAL_EDOM );
00179   }
00180 
00181   /* scan leap second table and locate the appropriate interval */
00182   for ( leap = 1; leap < numleaps; ++leap )
00183     if ( jd < leaps[leap].jd )
00184       break;
00185 
00186   return leaps[leap-1].taiutc;
00187 }
00188 
00189 
00190 /** Returns the GPS seconds since the GPS epoch for a
00191  * specified UTC time structure. */
00192 INT4 XLALUTCToGPS( const struct tm *utc /**< [In] UTC time in a broken down time structure. */ )
00193 {
00194   static const char func[] = "XLALUTCToGPS";
00195   time_t unixsec;
00196   INT4 gpssec;
00197   int leapsec;
00198 
00199   /* compute leap seconds */
00200   leapsec = XLALLeapSecondsUTC( utc );
00201   if ( leapsec < 0 )
00202     XLAL_ERROR( func, XLAL_EFUNC );
00203   /* compute unix epoch time: seconds since 1970 JAN 1 0h UTC */
00204   /* POSIX:2001 definition of seconds since the (UNIX) Epoch */
00205   unixsec = utc->tm_sec + utc->tm_min*60 + utc->tm_hour*3600 
00206     + utc->tm_yday*86400 + (utc->tm_year-70)*31536000
00207     + ((utc->tm_year-69)/4)*86400 - ((utc->tm_year-1)/100)*86400
00208     + ((utc->tm_year+299)/400)*86400;
00209   gpssec  = unixsec;
00210   gpssec -= XLAL_EPOCH_UNIX_GPS; /* change to gps epoch */
00211   gpssec += leapsec - XLAL_EPOCH_GPS_TAI_UTC;
00212   /* now check to see if this is an additional leap second */
00213   if ( utc->tm_sec == 60 )
00214     --gpssec;
00215   return gpssec;
00216 }
00217 
00218 
00219 /** Returns a pointer to a tm structure representing the time
00220  * specified in seconds since the GPS epoch.  */
00221 struct tm * XLALGPSToUTC(
00222     struct tm *utc, /**< [Out] Pointer to tm struct where result is stored. */
00223     INT4 gpssec /**< [In] Seconds since the GPS epoch. */
00224     )
00225 {
00226   static const char func[] = "XLALGPSToUTC";
00227   time_t unixsec;
00228   int leapsec;
00229   int delta;
00230   leapsec = XLALLeapSeconds( gpssec );
00231   if ( leapsec < 0 )
00232     XLAL_ERROR_NULL( func, XLAL_EFUNC );
00233   unixsec  = gpssec - leapsec + XLAL_EPOCH_GPS_TAI_UTC; /* get rid of leap seconds */
00234   unixsec += XLAL_EPOCH_UNIX_GPS; /* change to unix epoch */
00235   memset( utc, 0, sizeof( *utc ) ); /* blank out utc structure */
00236   utc = gmtime_r( &unixsec, utc ); /* FIXME: use gmtime ?? */
00237   /* now check to see if we need to add a 60th second to UTC */
00238   if ( ( delta = delta_tai_utc( gpssec ) ) > 0 )
00239     utc->tm_sec += 1; /* delta only ever is one, right?? */
00240   return utc;
00241 }
00242 
00243 
00244 /** Returns the Julian Day (JD) corresponding to the date given in a broken
00245  * down time structure.
00246  *
00247  * See \cite{esaa:1992} and \cite{green:1985} for details.  First, some
00248  * definitions:
00249  *
00250  * Mean Julian Year = 365.25 days
00251  * Julian Epoch = 1 Jan 4713BCE, 12:00 GMT (4713 BC Jan 01d.5 GMT)
00252  * Fundamental Epoch J2000.0 = 2001-01-01.5 TDB
00253  *
00254  * Julian Date is the amount of time elapsed since the Julian Epoch,
00255  * measured in days and fractions of a day.  There are a couple of
00256  * complications arising from the length of a year:  the Tropical Year is
00257  * 365.2422 days.  First, the Gregorian correction where 10 days
00258  * (1582-10-05 through 1582-10-14) were eliminated.  Second, leap years:
00259  * years ending with two zeroes (e.g., 1700, 1800) are leap only if
00260  * divisible by 400;  so, 400 civil years contain 400 * 365.25 - 3 = 146097
00261  * days.  So, the Julian Date of J2000.0 is JD 2451545.0, and thus the
00262  * Julian Epoch = J2000.0 + (JD - 2451545) / 365.25, i.e., number of years
00263  * elapsed since J2000.0.
00264  *
00265  * One algorithm for computing the Julian Day is from \cite{vfp:1979} based
00266  * on a formula in \cite{esaa:1992} where the algorithm is due to
00267  * \cite{fvf:1968} and ``compactified'' by P. M. Muller and R. N. Wimberly.
00268  * The formula is
00269  *
00270  * jd = 367 \times y - 7 \times (y + (m + 9)/12)/4 - 3 \times ((y + (m -
00271  * 9)/7)/100 + 1)/4 + 275 \times m/9 + d + 1721029
00272  *
00273  * where jd is the Julian day number, y is the year, m is the month (1-12),
00274  * and d is the day (1-31).  This formula is valid only for JD > 0, i.e.,
00275  * after -4713 Nov 23 = 4712 BCE Nov 23.
00276  *
00277  * A shorter formula from the same reference, but which only works for
00278  * dates since 1900 March is:
00279  *
00280  * jd = 367 \times y - 7 \times (y + (m + 9)/12)/4 + 275 \times m/9 + d +
00281  * 1721014
00282  *
00283  * We will use this shorter formula since there is unlikely to be any
00284  * analyzable data from before 1900 March.
00285  *
00286  *
00287  * References:
00288  *
00289  * \bibitem{esaa:1992} \textit{Explanatory Supplement to the Astronomical
00290  * Almanac}, P.~K. Seidelmann, \textit{ed.} (University Science Books, Mill
00291  * Valley, 1992)
00292  *
00293  * \bibitem{green:1985} R.~M. Green, \textit{Spherical Astronomy}
00294  * (Cambridge University Press, Cambridge, 1985)
00295  *
00296  * \bibitem{vfp:1979} T.~C. Van Flandern, and K.~F. Pulkkinen,
00297  * \textit{Astrophysical Journal Supplement Series}, \textbf{41}, 391-411,
00298  * 1979 Nov.
00299  *
00300  * \bibitem{fvf:1968} Fliegen, and Van~Flandern, \textit{Communications of
00301  * the ACM}, \textbf{11}, 657, 1968
00302  */
00303 
00304 
00305 REAL8 XLALJulianDay( const struct tm *utc /**< [In] UTC time in a broken down time structure. */ )
00306 {
00307   static const char func[] = "XLALJulianDay";
00308   const int sec_per_day = 60 * 60 * 24; /* seconds in a day */
00309   int year, month, day, sec;
00310   REAL8 jd;
00311 
00312   /* this routine only works for dates after 1900 */
00313   if ( utc->tm_year <= 0 )
00314   {
00315     XLALPrintError( "XLAL Error - Year must be after 1900\n" );
00316     XLAL_ERROR_REAL8( func, XLAL_EDOM );
00317   }
00318 
00319   year  = utc->tm_year + 1900; 
00320   month = utc->tm_mon + 1;     /* month is in range 1-12 */
00321   day   = utc->tm_mday;        /* day is in range 1-31 */
00322   sec   = utc->tm_sec + 60*(utc->tm_min + 60*(utc->tm_hour)); /* seconds since midnight */
00323 
00324   jd = 367*year - 7*(year + (month + 9)/12)/4 + 275*month/9 + day + 1721014;
00325   /* note: Julian days start at noon: subtract half a day */
00326   jd += (REAL8)sec/(REAL8)sec_per_day - 0.5;
00327   return jd;
00328 }
00329 
00330 
00331 /** Returns the Modified Julian Day (MJD) corresponding to the date given
00332  * in a broken down time structure.
00333  *
00334  * Note:
00335  *   - By convention, MJD is an integer.
00336  *   - MJD number starts at midnight rather than noon.
00337  *
00338  * If you want a Modified Julian Day that has a fractional part, simply use
00339  * the macro:
00340  *
00341  * #define XLAL_MODIFIED_JULIAN_DAY(utc) (XLALJulianDay(utc)-XLAL_MJD_REF)
00342  */
00343 INT4 XLALModifiedJulianDay( const struct tm *utc /**< [In] UTC time in a broken down time structure. */ )
00344 {
00345   static const char func[] = "XLALModifiedJulianDay";
00346   REAL8 jd;
00347   INT4 mjd;
00348   jd = XLALJulianDay( utc );
00349   if ( XLAL_IS_REAL8_FAIL_NAN( jd ) )
00350     XLAL_ERROR( func, XLAL_EFUNC );
00351   mjd = floor( jd - XLAL_MJD_REF );
00352   return mjd;
00353 }

Generated on Tue Oct 14 02:32:38 2008 for LAL by  doxygen 1.5.2