GPStoUTC.c

Go to the documentation of this file.
00001 /*
00002 *  Copyright (C) 2007 Bruce Allen, Duncan Brown, David Chin, Jolien Creighton, Patrick Brady, Peter Shawhan, Reinhard Prix
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 /* <lalVerbatim file="GPStoUTCCV">
00021 Author: David Chin <dwchin@umich.edu> +1-734-709-9119
00022 $Id: GPStoUTC.c,v 1.32 2008/04/29 21:31:12 kipp Exp $
00023 </lalVerbatim> */
00024 
00025 /* <lalLaTeX>
00026 \subsection{Module \texttt{GPStoUTC.c}}
00027 \label{ss:GPStoUTC.c}
00028 
00029 Converts between GPS time (in seconds and nanoseconds) and UTC in a
00030 \texttt{LALDate} structure.
00031 
00032 \subsection*{Prototypes}
00033 \vspace{0.1in}
00034 \input{GPStoUTCCP}
00035 \idx{LALGPStoUTC()}
00036 \idx{LALUTCtoGPS()}
00037 \idx{LALLeapSecs()}
00038 
00039 \subsubsection*{Description}
00040 
00041 \texttt{LALGPStoUTC()} and \texttt{LALUTCtoGPS} convert time in GPS seconds
00042 and nanoseconds (\texttt{LIGOTimeGPS}) and time in UTC (\texttt{LALDate}),
00043 taking into account leap seconds until 2006-Dec-31 23:59:59 UTC. % UPDATEME %
00044 
00045 \texttt{LALLeapSecs()} returns the number of leap seconds introduced since
00046 the GPS epoch 1980-Jan-06, abbreviated GPS-UTC.
00047 
00048 
00049 \subsubsection*{Algorithms}
00050 
00051 The conversion from GPS to UTC is copied directly from
00052 GRASP~\cite{grasp:194}.  It does the conversion by counting TAI seconds
00053 starting from the Unix epoch origin, 1970-Jan-01 00:00:00 UTC.  A static
00054 table of leap seconds is compiled in: this \emph{must} be updated whenever
00055 a new leap second is introduced.  The latest leap second included is
00056 2006-Jan-01. % UPDATEME %
00057 
00058 The conversion from UTC to GPS is done by counting the amount of elapsed 
00059 time since the GPS epoch origin, 1980-Jan-06 00:00:00 UTC.  Again, leap
00060 seconds are accounted for by a static table (different from the one used in
00061 GPS to UTC) which \emph{must} be updated whenever a new leap second is
00062 introduced.  The latest leap second included is 2006-Jan-01. % UPDATEME %
00063 
00064 The computation of GPS-UTC is from a static table published by the USNO
00065 at \url{ftp://maia.usno.navy.mil/ser7/tai-utc.dat}.  The latest leap second
00066 included is 2006-Jan-01. % UPDATEME %
00067 
00068 \subsubsection*{Uses}
00069 
00070 \subsubsection*{Notes}
00071 
00072 These routines will not work for times before 1980-01-06 00:00:00 UTC (GPS
00073 0).  The latest leap second that can be accounted for is the one added at
00074 the end of 2005-Dec. % UPDATEME %  These routines have accurate leap second
00075 information until 2006-Dec-31. % UPDATEME %
00076 
00077 \textbf{Example:} To convert a GPS time to UTC, and then back to GPS:
00078 
00079 \begin{verbatim}
00080 
00081 #include <lal/LALStdlib.h>
00082 #include <lal/Date.h>
00083 
00084 struct tm *gmtime_r( const time_t *, struct tm * );
00085 char *asctime_r( const struct tm *, char *, int );
00086 
00087 int main(int argc, char *argv[])
00088 {
00089     static LALStatus   status;
00090     LIGOTimeGPS        gps = {615081613, 123456789};
00091     LALDate            date;
00092     LALLeapSecAccuracy accuracy = LALLEAPSEC_STRICT;
00093     CHARVector        *timestamp = NULL;
00094 
00095     LALCHARCreateVector(&status, &timestamp, (UINT4)128);
00096 
00097     LALGPStoUTC(&status, &date, &gps, &accuracy);
00098 
00099     LALDateString(&status, timestamp, &date);
00100 
00101     printf("GPS (%d, %d) = %s\n", gps.gpsSeconds, gps.gpsNanoSeconds,
00102            timestamp->data);
00103 
00104     LALUTCtoGPS(&status, &gps, &date, &accuracy);
00105 
00106     printf("%s = GPS (%d, %d)\n", timestamp->data, gps.gpsSeconds,
00107            gps.gpsNanoSeconds);
00108 
00109     return 0;
00110 }
00111 
00112 \end{verbatim}
00113 
00114 For an example of how \texttt{LALLeapSecs()} is used, see the test program
00115 \texttt{TestLeapSecs.c} in the \texttt{packages/date/test} directory.
00116 
00117 
00118 </lalLaTeX> */
00119 
00120 #include <lal/LALRCSID.h>
00121 
00122 NRCSID (GPSTOUTCC, "$Id: GPStoUTC.c,v 1.32 2008/04/29 21:31:12 kipp Exp $");
00123 
00124 #include <lal/LALStdio.h>
00125 #include <lal/Date.h>
00126 #include "date_value.h"
00127 #include <lal/XLALError.h>
00128 
00129 #define INFOSTR_LEN 256
00130 
00131 struct tm *gmtime_r( const time_t *, struct tm * );
00132 char *asctime_r( const struct tm *, char * );
00133 
00134 
00135 /* UPDATEME */
00136 /* The international earth rotation service announces leap seconds;
00137  * their data is posted in bulletin C at
00138  *    http://www.iers.org/MainDisp.csl?pid=36-9
00139  * Another useful web site is at
00140  *    http://maia.usno.navy.mil/
00141  * which carries these announcements too.  The latest time for which 
00142  * this routine will work: 2006-12-31 23:59:59 UTC 
00143  */
00144 
00145 /* GPS for maxtestedGPS computed using lalapps_tconvert (part of ligotools) */
00146 static const INT4 maxtestedGPS = 851644813;
00147 
00148 /*
00149  * Convert GPS seconds to UTC date-time contained in LALDate structure
00150  */
00151 /* <lalVerbatim file="GPStoUTCCP"> */
00152 void
00153 LALGPStoUTC (LALStatus                *status,
00154              LALDate                  *p_utcDate,  /* output - date */
00155              const LIGOTimeGPS        *p_gpsTime,  /* input - GPS seconds */
00156              const LALLeapSecAccuracy *p_accuracy) /* accuracy of
00157                                                       leap-second
00158                                                       accounting:
00159                                                       LALLEAPSEC_LOOSE, or LALLEAPSEC_STRICT */
00160 { /* </lalVerbatim> */
00161   /* UPDATEME -- to update, add an entry at the end listing
00162    * the Unix epoch time when a leap second is introduced */
00163   /* this is a table of Unix epoch times when leap
00164    * seconds were introduced */
00165   /* What's the funny format? These Unix epoch times are expressed
00166    * in terms of Julian Day Numbers, i.e. say JD1 = Julian Day number
00167    * of the day when a leap sec was added, JD0 = Unix epoch origin (i.e. 1970-01-01),
00168    * then the Unix epoch time is given by 
00169    * (JD1 - JD0) * SECS_PER_DAY 
00170    * FIXME: at some point, we should just dispense with this wacky Julian Day
00171    * stuff. It's just confusing. */
00172   /* As of 2005-07-05 08:05 UTC-4, Bulletin C has been released, but the Naval
00173    * Observatory's tai-utc.dat hasn't been updated. --dwchin */
00174   static const time_t leaps[] = {
00175              0,
00176       33350400,
00177       63072000,
00178       78796800,
00179       94694400,
00180      126230400,
00181      157766400,
00182      189302400,
00183      220924800,
00184      252460800,
00185      283996800,
00186      315532800,
00187      362793600,
00188      394329600,
00189      425865600,
00190      489024000,
00191      567993600,
00192      631152000,
00193      662688000,
00194      709948800,
00195      741484800,
00196      773020800,
00197      820454400,
00198      867715200,
00199      915148800,
00200     1136073600,
00201   };
00202 
00203   /* number of times leap seconds occur */
00204   static const INT4   numleaps = sizeof(leaps)/sizeof(time_t);
00205   time_t       unixTime;
00206   time_t       tmptime;
00207   LALUnixDate  tmputc;
00208   char         tmpstamp[32];
00209   CHAR         infostr[INFOSTR_LEN];
00210   INT4         i;
00211 
00212   INITSTATUS (status, "LALGPStoUTC", GPSTOUTCC);
00213 
00214   ASSERT (p_gpsTime != (LIGOTimeGPS *)NULL, status,
00215           DATEH_ENULLINPUT, DATEH_MSGENULLINPUT);
00216 
00217   ASSERT (p_gpsTime->gpsSeconds >= 0, status,
00218           DATEH_ERANGEGPSABS, DATEH_MSGERANGEGPSABS);
00219 
00220   ASSERT (p_accuracy != (LALLeapSecAccuracy *)NULL, status,
00221           DATEH_ENULLINPUT, DATEH_MSGENULLINPUT);
00222 
00223   ASSERT ((*p_accuracy == LALLEAPSEC_STRICT ||
00224            *p_accuracy == LALLEAPSEC_LOOSE), status,
00225           DATEH_EACCPARAMOUTOFRANGE, DATEH_MSGEACCPARAMOUTOFRANGE);
00226 
00227   ASSERT (p_utcDate != (LALDate *)NULL, status,
00228           DATEH_ENULLOUTPUT, DATEH_MSGENULLOUTPUT);
00229 
00230   XLALPrintDeprecationWarning("LALGPStoUTC", "XLALGPSToUTC");
00231 
00232   /* we use Unix epoch as our origin */
00233   unixTime = p_gpsTime->gpsSeconds + UNIXGPS;
00234 
00235   if (lalDebugLevel & LALINFO)
00236   {
00237     LALSnprintf(infostr, INFOSTR_LEN, "Max. tested GPS is %d\n", maxtestedGPS);
00238     LALInfo(status, infostr);
00239   }
00240       
00241   /* 1998-Dec-31 23:59:60 (if leap seconds are taken into account) */
00242   tmptime = (22*365 + 7*366)* SECS_PER_DAY + 23;
00243   gmtime_r(&tmptime, &tmputc);
00244 
00245   if (lalDebugLevel & LALINFO)
00246   {
00247     asctime_r(&tmputc, tmpstamp);
00248     LALSnprintf(infostr, INFOSTR_LEN, "tmputc = %s\n", tmpstamp);
00249     LALInfo(status, infostr);
00250   }
00251 
00252   /*
00253    * if GPS is later than maxtestedGPS
00254    *    check accuracy param
00255    *        if anal accuracy
00256    *            die
00257    *        else
00258    *            print warning message
00259    */
00260   if (p_gpsTime->gpsSeconds > maxtestedGPS)
00261     {
00262       /* check accuracy param */
00263       if (*p_accuracy == LALLEAPSEC_STRICT)
00264         {
00265           ABORT(status, DATEH_ERANGEGPSABS, DATEH_MSGERANGEGPSABS);
00266         }
00267       else if (*p_accuracy == LALLEAPSEC_LOOSE)
00268         {
00269           LALWarning(status, "may be missing leap seconds");
00270         }
00271       else
00272         {
00273           LALWarning(status, "may be missing leap seconds");
00274         }
00275     }
00276 
00277 
00278 
00279   
00280   /* system gmtime does take leap seconds into account */
00281   if (tmputc.tm_sec == 60)
00282     {
00283       LALInfo(status, "gmtime_r() takes leap seconds into account");
00284       
00285       /* check that date requested is not later than 2002-Mar-31 23:59:59,
00286        * which is when the next possible leap second will be. IERS has
00287        * announced that there will be NO leap second at the end of 2001
00288        * or any time before */
00289 
00290       /* NOTE: this will break if system gmtime() has taken leap seconds
00291        * into account in the past (i.e. before the test date) */
00292 
00293       /* compute date struct */
00294       gmtime_r(&unixTime, &tmputc);
00295       p_utcDate->unixDate.tm_sec   = tmputc.tm_sec;
00296       p_utcDate->unixDate.tm_min   = tmputc.tm_min;
00297       p_utcDate->unixDate.tm_hour  = tmputc.tm_hour;
00298       p_utcDate->unixDate.tm_mday  = tmputc.tm_mday;
00299       p_utcDate->unixDate.tm_mon   = tmputc.tm_mon;
00300       p_utcDate->unixDate.tm_year  = tmputc.tm_year;
00301       p_utcDate->unixDate.tm_wday  = tmputc.tm_wday;
00302       p_utcDate->unixDate.tm_yday  = tmputc.tm_yday;
00303       p_utcDate->unixDate.tm_isdst = 0;    /* always ignore tm_isdst field */
00304     }
00305   else /* system gmtime() does NOT take leap secs into account */
00306     {
00307       LALInfo(status, "gmtime_r() does not figure in leap seconds");
00308 
00309       /* fix up leap seconds */
00310       i       = 0;
00311       while (i < numleaps && leaps[i] + i - 1 < unixTime)
00312         ++i;
00313 
00314       if (lalDebugLevel & LALINFO)
00315       {
00316         LALSnprintf(infostr, INFOSTR_LEN, "unixTime = %ld; leaps[%d] = %ld",
00317             unixTime, i, leaps[i]);
00318         LALInfo(status, infostr);
00319       }
00320 
00321       if (unixTime == (leaps[i] + i - 1))
00322         {
00323           unixTime -= i;
00324           gmtime_r(&unixTime, &tmputc);
00325           p_utcDate->unixDate.tm_sec   = 60;
00326           p_utcDate->unixDate.tm_min   = tmputc.tm_min;
00327           p_utcDate->unixDate.tm_hour  = tmputc.tm_hour;
00328           p_utcDate->unixDate.tm_mday  = tmputc.tm_mday;
00329           p_utcDate->unixDate.tm_mon   = tmputc.tm_mon;
00330           p_utcDate->unixDate.tm_year  = tmputc.tm_year;
00331           p_utcDate->unixDate.tm_wday  = tmputc.tm_wday;
00332           p_utcDate->unixDate.tm_yday  = tmputc.tm_yday;
00333           p_utcDate->unixDate.tm_isdst = 0;
00334         }
00335       else
00336         {
00337           unixTime -= (i - 1);
00338           gmtime_r(&unixTime, &tmputc);
00339           p_utcDate->unixDate.tm_sec   = tmputc.tm_sec;
00340           p_utcDate->unixDate.tm_min   = tmputc.tm_min;
00341           p_utcDate->unixDate.tm_hour  = tmputc.tm_hour;
00342           p_utcDate->unixDate.tm_mday  = tmputc.tm_mday;
00343           p_utcDate->unixDate.tm_mon   = tmputc.tm_mon;
00344           p_utcDate->unixDate.tm_year  = tmputc.tm_year;
00345           p_utcDate->unixDate.tm_wday  = tmputc.tm_wday;
00346           p_utcDate->unixDate.tm_yday  = tmputc.tm_yday;
00347           p_utcDate->unixDate.tm_isdst = 0;
00348         }
00349     }
00350       
00351   /* set residual nanoseconds */
00352   p_utcDate->residualNanoSeconds = p_gpsTime->gpsNanoSeconds;
00353 
00354   RETURN (status);
00355 } /* END: LALGPStoUTC() */
00356 
00357 
00358 /*
00359  * Returns no. of days in year of given date
00360  */
00361 static int days_in_year(const LALDate *p_utcDate)
00362 {
00363   int year = p_utcDate->unixDate.tm_year + 1900;
00364 
00365   /* Deal with the years ending with '00: only multiples of 400 are leap */
00366   if (year % 100  == 0)
00367     {
00368       if (year % 400 == 0)
00369         return 366;
00370       else
00371         return 365;
00372     }
00373 
00374   /* non-'00' years */
00375   if (year % 4 == 0)
00376     return 366;
00377 
00378   return 365;
00379 }
00380 
00381 /*
00382  * Returns no. of days in month of given date
00383  */
00384 static int days_in_month(const LALDate *p_utcDate)
00385 {
00386   int month = p_utcDate->unixDate.tm_mon;
00387 
00388   switch (month) {
00389   case LALMONTH_JAN:
00390   case LALMONTH_MAR:
00391   case LALMONTH_MAY:
00392   case LALMONTH_JUL:
00393   case LALMONTH_AUG:
00394   case LALMONTH_OCT:
00395   case LALMONTH_DEC:
00396     return 31;
00397 
00398   case LALMONTH_APR:
00399   case LALMONTH_JUN:
00400   case LALMONTH_SEP:
00401   case LALMONTH_NOV:
00402     return 30;
00403 
00404   case 1:
00405     if (days_in_year(p_utcDate) == 366)
00406       return 29;
00407     else
00408       return 28;
00409   }
00410   
00411   return -1;
00412 }
00413 
00414 /*
00415  * Struct for leap seconds
00416  */
00417 typedef struct leap_sec
00418 {
00419   int    year;       /* year - 1900 */
00420   int    mon;        /* 0 through 11 */
00421   INT4   leapsec;
00422 }
00423 leap_sec_t;
00424 
00425 /* <lalVerbatim file="GPStoUTCCP"> */
00426 void
00427 LALUTCtoGPS (LALStatus                *status,
00428              LIGOTimeGPS              *p_gpsTime,  /* output - GPS seconds */
00429              const LALDate            *p_utcDate,  /* input - date in UTC */
00430              const LALLeapSecAccuracy *p_accuracy) /* accuracy of
00431                                                       leap-second
00432                                                       accounting:
00433                                                       LALLEAPSEC_LOOSE, or LALLEAPSEC_STRICT */
00434 { /* </lalVerbatim> */
00435 
00436   /* UPDATEME */
00437   /*
00438    * Table of leap seconds
00439    * Format: Year, Month, and Day of Month in struct tm definition.
00440    *    (see ctime(3))
00441    */
00442   static leap_sec_t leap_sec_data[] =
00443     {
00444       {72, LALMONTH_JAN, 1},
00445       {73, LALMONTH_JAN, 1},
00446       {74, LALMONTH_JAN, 1},
00447       {75, LALMONTH_JAN, 1},
00448       {76, LALMONTH_JAN, 1},
00449       {77, LALMONTH_JAN, 1},
00450       {78, LALMONTH_JAN, 1},
00451       {79, LALMONTH_JAN, 1},
00452       {80, LALMONTH_JAN, 1},
00453       {81, LALMONTH_JUL, 1},
00454       {82, LALMONTH_JUL, 1},
00455       {83, LALMONTH_JUL, 1},
00456       {85, LALMONTH_JUL, 1},
00457       {88, LALMONTH_JAN, 1},
00458       {90, LALMONTH_JAN, 1},
00459       {91, LALMONTH_JAN, 1},
00460       {92, LALMONTH_JUL, 1},
00461       {93, LALMONTH_JUL, 1},
00462       {94, LALMONTH_JUL, 1},
00463       {96, LALMONTH_JAN, 1},
00464       {97, LALMONTH_JUL, 1},
00465       {99, LALMONTH_JAN, 1},
00466       {106, LALMONTH_JAN, 1},
00467     };
00468 
00469 
00470   int ddays = 0;
00471   int dsecs = 0;
00472   LALDate tmpdate;
00473   static LALDate gpsref;
00474   int i = 0;
00475   char infostr[256];
00476   static const int nleaps = sizeof(leap_sec_data)/sizeof(leap_sec_t);
00477 
00478   XLALPrintDeprecationWarning("LALUTCtoGPS", "XLALUTCToGPS");
00479 
00480   /* When GPS began */
00481   gpsref.unixDate.tm_sec  = 0;
00482   gpsref.unixDate.tm_min  = 0;
00483   gpsref.unixDate.tm_hour = 0;
00484   gpsref.unixDate.tm_mday = 6;
00485   gpsref.unixDate.tm_mon  = LALMONTH_JAN;
00486   gpsref.unixDate.tm_year = 80;
00487   gpsref.unixDate.tm_wday = 0;
00488   gpsref.unixDate.tm_yday = 0;
00489 
00490   if (lalDebugLevel & LALINFO)
00491   {
00492     LALSnprintf(infostr, INFOSTR_LEN, "Date given: %d-%d-%d %d:%d:%d %d\n",
00493         p_utcDate->unixDate.tm_year+1900, p_utcDate->unixDate.tm_mon+1,
00494         p_utcDate->unixDate.tm_mday, p_utcDate->unixDate.tm_hour,
00495         p_utcDate->unixDate.tm_min, p_utcDate->unixDate.tm_sec,
00496         p_utcDate->residualNanoSeconds);
00497 
00498     LALInfo(status, infostr);
00499   }
00500 
00501   INITSTATUS(status, "LALUTCtoGPS", GPSTOUTCC);
00502 
00503   ASSERT (p_gpsTime != (LIGOTimeGPS *)NULL, status,
00504           DATEH_ENULLOUTPUT, DATEH_MSGENULLOUTPUT);
00505 
00506   ASSERT (p_accuracy != (LALLeapSecAccuracy *)NULL, status,
00507           DATEH_ENULLINPUT, DATEH_MSGENULLINPUT);
00508 
00509   ASSERT ((*p_accuracy == LALLEAPSEC_STRICT ||
00510            *p_accuracy == LALLEAPSEC_LOOSE), status,
00511           DATEH_EACCPARAMOUTOFRANGE, DATEH_MSGEACCPARAMOUTOFRANGE);
00512 
00513   ASSERT (p_utcDate != (LALDate *)NULL, status,
00514           DATEH_ENULLINPUT, DATEH_MSGENULLINPUT);
00515 
00516   /* Can't convert dates before 1980-Jan-06 */
00517   ASSERT (p_utcDate->unixDate.tm_year > 80 ||
00518           (p_utcDate->unixDate.tm_year == 80 &&
00519            (p_utcDate->unixDate.tm_mon > LALMONTH_JAN ||
00520             (p_utcDate->unixDate.tm_mon == LALMONTH_JAN &&
00521              p_utcDate->unixDate.tm_mday >= 6))), status,
00522           DATEH_EGPSDATETOOEARLY, DATEH_MSGEGPSDATETOOEARLY);
00523 
00524   /* UPDATEME -- to update, fix the comment and the first if() statement */
00525   /*
00526    * Check that time asked for is not after last known leap sec
00527    * Use by: 2006-Dec-31 23:59:59 UTC
00528    * Check bulletins such as the following to see if additional ones are needed:
00529    * http://hpiers.obspm.fr/eoppc/bul/bulc/bulletinc.dat
00530    * if date is later
00531    *    check accuracy param
00532    *        if anal accuracy
00533    *            die
00534    *        else
00535    *            print warning message
00536    *
00537    * // date is not later
00538    * do the conversion
00539    */
00540   if (p_utcDate->unixDate.tm_year > 106 ||
00541       (p_utcDate->unixDate.tm_year == 106 &&
00542        (p_utcDate->unixDate.tm_mon > LALMONTH_DEC ||
00543         (p_utcDate->unixDate.tm_mon == LALMONTH_DEC &&
00544          p_utcDate->unixDate.tm_mday == 31 &&
00545          p_utcDate->unixDate.tm_hour == 23 &&
00546          p_utcDate->unixDate.tm_min  == 59 &&
00547          p_utcDate->unixDate.tm_sec > 59))))
00548     {
00549       /* check accuracy param */
00550       if (*p_accuracy == LALLEAPSEC_STRICT) /* strict accuracy */
00551         {
00552           ABORT(status, DATEH_ERANGEGPSABS, DATEH_MSGERANGEGPSABS);
00553         }
00554       else if (*p_accuracy == LALLEAPSEC_LOOSE)
00555         {
00556           LALWarning(status, "may be missing leap seconds");
00557         }
00558       else
00559         {
00560           LALWarning(status, "may be missing leap seconds");
00561         }
00562     }
00563   
00564   
00565   /* start counting from the origin of GPS */
00566   tmpdate.unixDate.tm_year = 80;
00567   tmpdate.unixDate.tm_mon  = LALMONTH_JAN;
00568   tmpdate.unixDate.tm_mday =  6;
00569   tmpdate.unixDate.tm_hour =  0;
00570   tmpdate.unixDate.tm_min  =  0;
00571   tmpdate.unixDate.tm_sec  =  0;
00572   tmpdate.residualNanoSeconds = 0;
00573       
00574   while (tmpdate.unixDate.tm_year < p_utcDate->unixDate.tm_year)
00575     {
00576       ddays += days_in_year(&tmpdate);
00577       tmpdate.unixDate.tm_year++;
00578     }
00579   ddays -= 5; /* 5 days in early Jan 1980 */
00580 
00581   while (tmpdate.unixDate.tm_mon < p_utcDate->unixDate.tm_mon)
00582     {
00583       ddays += days_in_month(&tmpdate);
00584       tmpdate.unixDate.tm_mon++;
00585     }
00586 
00587   ddays += p_utcDate->unixDate.tm_mday - 1;
00588   dsecs  = ddays * SECS_PER_DAY;
00589 
00590   dsecs += p_utcDate->unixDate.tm_hour * SECS_PER_HOUR +
00591     p_utcDate->unixDate.tm_min * SECS_PER_MIN +
00592     p_utcDate->unixDate.tm_sec;
00593 
00594   /* add in leap seconds */
00595   i = 9;   /* corresponds to the leap sec data for 1981-Jul-1 */
00596   while (i < nleaps)
00597     {
00598       if (leap_sec_data[i].year < p_utcDate->unixDate.tm_year)
00599         dsecs++;
00600       else if (leap_sec_data[i].year == p_utcDate->unixDate.tm_year &&
00601                leap_sec_data[i].mon <= p_utcDate->unixDate.tm_mon)
00602         dsecs++;
00603           
00604       ++i;
00605     }
00606 
00607   p_gpsTime->gpsSeconds = dsecs;
00608   p_gpsTime->gpsNanoSeconds = p_utcDate->residualNanoSeconds;
00609 
00610 
00611   RETURN (status);
00612 } /* END: LALUTCtoGPS() */
00613 
00614 
00615 
00616 /*
00617  * LALLeapSecs()
00618  * Compute TAI-UTC for a given GPS time.
00619  * To reduce complications, we'll only deal with dates since the
00620  * beginning of GPS time, i.e. 1980-Jan-06  00:00:00 UTC
00621  * The source code for this function MUST be updated whenever a new leap
00622  * second is announced.
00623 
00624  ftp://maia.usno.navy.mil/ser7/tai-utc.dat
00625  
00626  1961 JAN  1 =JD 2437300.5  TAI-UTC=   1.4228180 S + (MJD - 37300.) X 0.001296 S
00627  1961 AUG  1 =JD 2437512.5  TAI-UTC=   1.3728180 S + (MJD - 37300.) X 0.001296 S
00628  1962 JAN  1 =JD 2437665.5  TAI-UTC=   1.8458580 S + (MJD - 37665.) X 0.0011232S
00629  1963 NOV  1 =JD 2438334.5  TAI-UTC=   1.9458580 S + (MJD - 37665.) X 0.0011232S
00630  1964 JAN  1 =JD 2438395.5  TAI-UTC=   3.2401300 S + (MJD - 38761.) X 0.001296 S
00631  1964 APR  1 =JD 2438486.5  TAI-UTC=   3.3401300 S + (MJD - 38761.) X 0.001296 S
00632  1964 SEP  1 =JD 2438639.5  TAI-UTC=   3.4401300 S + (MJD - 38761.) X 0.001296 S
00633  1965 JAN  1 =JD 2438761.5  TAI-UTC=   3.5401300 S + (MJD - 38761.) X 0.001296 S
00634  1965 MAR  1 =JD 2438820.5  TAI-UTC=   3.6401300 S + (MJD - 38761.) X 0.001296 S
00635  1965 JUL  1 =JD 2438942.5  TAI-UTC=   3.7401300 S + (MJD - 38761.) X 0.001296 S
00636  1965 SEP  1 =JD 2439004.5  TAI-UTC=   3.8401300 S + (MJD - 38761.) X 0.001296 S
00637  1966 JAN  1 =JD 2439126.5  TAI-UTC=   4.3131700 S + (MJD - 39126.) X 0.002592 S
00638  1968 FEB  1 =JD 2439887.5  TAI-UTC=   4.2131700 S + (MJD - 39126.) X 0.002592 S
00639  1972 JAN  1 =JD 2441317.5  TAI-UTC=  10.0       S + (MJD - 41317.) X 0.0      S
00640  1972 JUL  1 =JD 2441499.5  TAI-UTC=  11.0       S + (MJD - 41317.) X 0.0      S
00641  1973 JAN  1 =JD 2441683.5  TAI-UTC=  12.0       S + (MJD - 41317.) X 0.0      S
00642  1974 JAN  1 =JD 2442048.5  TAI-UTC=  13.0       S + (MJD - 41317.) X 0.0      S
00643  1975 JAN  1 =JD 2442413.5  TAI-UTC=  14.0       S + (MJD - 41317.) X 0.0      S
00644  1976 JAN  1 =JD 2442778.5  TAI-UTC=  15.0       S + (MJD - 41317.) X 0.0      S
00645  1977 JAN  1 =JD 2443144.5  TAI-UTC=  16.0       S + (MJD - 41317.) X 0.0      S
00646  1978 JAN  1 =JD 2443509.5  TAI-UTC=  17.0       S + (MJD - 41317.) X 0.0      S
00647  1979 JAN  1 =JD 2443874.5  TAI-UTC=  18.0       S + (MJD - 41317.) X 0.0      S
00648  1980 JAN  1 =JD 2444239.5  TAI-UTC=  19.0       S + (MJD - 41317.) X 0.0      S
00649  1981 JUL  1 =JD 2444786.5  TAI-UTC=  20.0       S + (MJD - 41317.) X 0.0      S
00650  1982 JUL  1 =JD 2445151.5  TAI-UTC=  21.0       S + (MJD - 41317.) X 0.0      S
00651  1983 JUL  1 =JD 2445516.5  TAI-UTC=  22.0       S + (MJD - 41317.) X 0.0      S
00652  1985 JUL  1 =JD 2446247.5  TAI-UTC=  23.0       S + (MJD - 41317.) X 0.0      S
00653  1988 JAN  1 =JD 2447161.5  TAI-UTC=  24.0       S + (MJD - 41317.) X 0.0      S
00654  1990 JAN  1 =JD 2447892.5  TAI-UTC=  25.0       S + (MJD - 41317.) X 0.0      S
00655  1991 JAN  1 =JD 2448257.5  TAI-UTC=  26.0       S + (MJD - 41317.) X 0.0      S
00656  1992 JUL  1 =JD 2448804.5  TAI-UTC=  27.0       S + (MJD - 41317.) X 0.0      S
00657  1993 JUL  1 =JD 2449169.5  TAI-UTC=  28.0       S + (MJD - 41317.) X 0.0      S
00658  1994 JUL  1 =JD 2449534.5  TAI-UTC=  29.0       S + (MJD - 41317.) X 0.0      S
00659  1996 JAN  1 =JD 2450083.5  TAI-UTC=  30.0       S + (MJD - 41317.) X 0.0      S
00660  1997 JUL  1 =JD 2450630.5  TAI-UTC=  31.0       S + (MJD - 41317.) X 0.0      S
00661  1999 JAN  1 =JD 2451179.5  TAI-UTC=  32.0       S + (MJD - 41317.) X 0.0      S
00662  2006 JAN  1 =JD 2453736.5  TAI-UTC=  33.0       S + (MJD - 41317.) X 0.0      S
00663  */
00664 
00665 typedef struct gps_leap_sec {
00666   time_t gps;       /* GPS time when leap sec was introduced */
00667   INT4   tai_utc;   /* TAI-UTC at GPS time gps */
00668 } gps_leap_sec_t;
00669 
00670 /* <lalVerbatim file="GPStoUTCCP"> */
00671 void
00672 LALLeapSecs (LALStatus                    *status,
00673              INT4                         *p_leapSecs,  /* output - GPS-UTC,
00674                                                            i.e. the number of 
00675                                                            leap seconds introduced
00676                                                            since the GPS epoch 
00677                                                            1980-Jan-06 */
00678              const LIGOTimeGPS            *p_gpsTime,   /* input - GPS time */
00679              const LALLeapSecFormatAndAcc *p_formatAndAcc)  /* format and
00680                                                                accuracy parameters */
00681 { /* </lalVerbatim> */
00682 
00683   /* UPDATEME -- to update, add the new (GPS, TAI - UTC) entry at the end
00684    *             see http://hpiers.obspm.fr/eoppc/bul/bulc/bulletinc.dat */
00685   /* Table of TAI-UTC */
00686   static const gps_leap_sec_t gpsLeaps[] =
00687     {
00688       {0,         19},  /* 1980-Jan-06 */
00689       {46828801,  20},  /* 1981-Jul-01 */
00690       {78364802,  21},  /* 1982-Jul-01 */
00691       {109900803, 22},  /* 1983-Jul-01 */
00692       {173059204, 23},  /* 1985-Jul-01 */
00693       {252028805, 24},  /* 1988-Jan-01 */
00694       {315187206, 25},  /* 1990-Jan-01 */
00695       {346723207, 26},  /* 1991-Jan-01 */
00696       {393984008, 27},  /* 1992-Jul-01 */
00697       {425520009, 28},  /* 1993-Jul-01 */
00698       {457056010, 29},  /* 1994-Jul-01 */
00699       {504489611, 30},  /* 1996-Jan-01 */
00700       {551750412, 31},  /* 1997-Jul-01 */
00701       {599184013, 32},  /* 1999-Jan-01 */
00702       {820108814, 33},  /* 2006-Jan-01 */
00703     };
00704   
00705   /* number of times leap seconds occur */
00706   static const INT4   numleaps = sizeof(gpsLeaps)/sizeof(gps_leap_sec_t);
00707   char   infostr[256];
00708   INT4   i;
00709 
00710 
00711   INITSTATUS (status, "LALLeapSecs", GPSTOUTCC);
00712 
00713   ASSERT (p_leapSecs != (INT4 *)NULL, status,
00714           DATEH_ENULLOUTPUT, DATEH_MSGENULLOUTPUT);
00715 
00716   ASSERT (p_gpsTime != (LIGOTimeGPS *)NULL, status,
00717           DATEH_ENULLINPUT, DATEH_MSGENULLINPUT);
00718 
00719   ASSERT (p_gpsTime->gpsSeconds >= 0, status,
00720           DATEH_ERANGEGPSABS, DATEH_MSGERANGEGPSABS);
00721 
00722   ASSERT (p_formatAndAcc != (LALLeapSecFormatAndAcc *)NULL, status,
00723           DATEH_ENULLINPUT, DATEH_MSGENULLINPUT);
00724 
00725   ASSERT ((p_formatAndAcc->format == LALLEAPSEC_TAIUTC ||
00726            p_formatAndAcc->format == LALLEAPSEC_GPSUTC),
00727           status, DATEH_EFORMATPARAMOUTOFRANGE,
00728           DATEH_MSGEFORMATPARAMOUTOFRANGE);
00729 
00730   ASSERT ((p_formatAndAcc->accuracy == LALLEAPSEC_LOOSE ||
00731            p_formatAndAcc->accuracy == LALLEAPSEC_STRICT),
00732           status, DATEH_EACCPARAMOUTOFRANGE,
00733           DATEH_MSGEACCPARAMOUTOFRANGE);
00734 
00735   XLALPrintDeprecationWarning("LALLeapSecs", "XLALLeapSeconds");
00736 
00737   if (lalDebugLevel & LALINFO)
00738   {
00739     LALSnprintf(infostr, INFOSTR_LEN, "Max. tested GPS is %d\n", maxtestedGPS);
00740     LALInfo(status, infostr);
00741   }
00742 
00743   /*
00744    * if GPS is later than maxtestedGPS
00745    *    check accuracy param
00746    *        if anal accuracy
00747    *            die
00748    *        else
00749    *            print warning message
00750    */
00751   if (p_gpsTime->gpsSeconds > maxtestedGPS)
00752     {
00753       /* check accuracy param */
00754       if (p_formatAndAcc->accuracy == LALLEAPSEC_STRICT)
00755         {
00756           ABORT(status, DATEH_ERANGEGPSABS, DATEH_MSGERANGEGPSABS);
00757         }
00758       else if (p_formatAndAcc->accuracy == LALLEAPSEC_LOOSE)
00759         {
00760           LALWarning(status, "may be missing leap seconds");
00761         }
00762       else
00763         {
00764           LALWarning(status, "may be missing leap seconds");
00765         }
00766     }
00767 
00768   if (p_gpsTime->gpsSeconds == 0)
00769     {
00770       *p_leapSecs = gpsLeaps[0].tai_utc;
00771     }
00772   else
00773     {
00774       i = 1;
00775       while (p_gpsTime->gpsSeconds > gpsLeaps[i].gps &&
00776              i < numleaps)
00777         ++i;
00778 
00779       *p_leapSecs = gpsLeaps[i-1].tai_utc;
00780     }
00781 
00782   if (lalDebugLevel & LALINFO)
00783   {
00784     LALSnprintf(infostr, INFOSTR_LEN, "Format = %d\n", p_formatAndAcc->format);
00785     LALInfo(status, infostr);
00786   }
00787 
00788   if (p_formatAndAcc->format == LALLEAPSEC_GPSUTC)
00789     {
00790       *p_leapSecs -= 19;
00791     }
00792 
00793   RETURN(status);
00794 } /* LALLeapSecs */

Generated on Sat Sep 6 03:07:01 2008 for LAL by  doxygen 1.5.2