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 }
1.5.2