LAL  7.5.0.1-08ee4f4
XLALTime.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007 Jolien Creighton and Kipp Cannon
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12  * Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 
20 #include <math.h>
21 #include <lal/Date.h>
22 #include <lal/LALAtomicDatatypes.h>
23 #include <lal/LALStdio.h>
24 #include <lal/XLALError.h>
25 
26 /**
27  * \defgroup XLALTime_c GPS Time
28  * \ingroup Date_h
29  *
30  * \brief GPS time manipulation functions.
31  */
32 
33 /** @{ */
34 
35 /** Converts GPS time to nano seconds stored as an INT8. */
37 {
38  return XLAL_BILLION_INT8 * epoch->gpsSeconds + epoch->gpsNanoSeconds;
39 }
40 
41 
42 /**
43  * Converts nano seconds stored as an INT8 to GPS time. Returns epoch on
44  * success, NULL on error.
45  */
47 {
49  epoch->gpsSeconds = gpsSeconds;
50  epoch->gpsNanoSeconds = ns % XLAL_BILLION_INT8;
51  if( (INT8) epoch->gpsSeconds != gpsSeconds ) {
52  XLALPrintError( "%s(): overflow: %" LAL_INT8_FORMAT " out of range of a 32-bit signed integer\n", __func__, gpsSeconds );
54  }
55  return epoch;
56 }
57 
58 
59 /**
60  * Sets GPS time given GPS integer seconds and residual nanoseconds.
61  * Returns epoch on success, or NULL on error.
62  */
64 {
65  return XLALINT8NSToGPS( epoch, XLAL_BILLION_INT8 * gpssec + gpsnan );
66 }
67 
68 
69 /**
70  * Sets GPS time given GPS seconds as a REAL8. Returns epoch on success,
71  * NULL on error.
72  */
74 {
75  INT4 gpssec = floor(t);
76  INT4 gpsnan = nearbyint((t - gpssec) * XLAL_BILLION_REAL8);
77  if(isnan(t)) {
78  XLALPrintError("%s(): NaN", __func__);
80  }
81  if(fabs(t) > 0x7fffffff) {
82  XLALPrintError("%s(): overflow: %.17g out of range of a 32-bit signed integer\n", __func__, t);
84  }
85  /* use XLALGPSSet() to normalize the nanoseconds */
86  return XLALGPSSet(epoch, gpssec, gpsnan);
87 }
88 
89 
90 /** Returns the GPS time as a REAL8. */
92 {
93  return epoch->gpsSeconds + (epoch->gpsNanoSeconds / XLAL_BILLION_REAL8);
94 }
95 
96 /**
97  * Breaks the GPS time into REAL8 integral and fractional parts,
98  * each of which has the same sign as the epoch. Returns the
99  * fractional part, and stores the integral part (as a REAL8)
100  * in the object pointed to by iptr. Like the standard C math
101  * library function modf().
102  */
104 {
105  INT8 ns = XLALGPSToINT8NS(epoch);
106  INT8 rem; /* remainder */
107  *iptr = ns < 0 ? -floor(-ns / XLAL_BILLION_REAL8) : floor(ns / XLAL_BILLION_REAL8);
108  rem = ns - ((INT8)(*iptr) * XLAL_BILLION_INT8);
109  return (REAL8)(rem) / XLAL_BILLION_REAL8;
110 }
111 
112 
113 /**
114  * Adds two GPS times. Computes epoch + dt and places the result in epoch.
115  * Returns epoch on success, NULL on error.
116  */
118 {
119  /* when GPS times are converted to 8-byte counts of nanoseconds their sum
120  * cannot overflow, however it might not be possible to convert the sum
121  * back to a LIGOTimeGPS without overflowing. that is caught by the
122  * XLALINT8NSToGPS() function */
124 }
125 
126 
127 /**
128  * Adds a double to a GPS time. Computes epoch + dt and places the result
129  * in epoch. Returns epoch on success, NULL on error.
130  */
132 {
133  LIGOTimeGPS dt_gps;
134  if(!XLALGPSSetREAL8(&dt_gps, dt))
136  return XLALGPSAddGPS(epoch, &dt_gps);
137 }
138 
139 
140 /**
141  * Difference between two GPS times. Computes t1 - t0 and places the
142  * result in t1. Returns t1 on success, NULL on error.
143  */
145 {
146  /* when GPS times are converted to 8-byte counts of nanoseconds their
147  * difference cannot overflow, however it might not be possible to
148  * convert the difference back to a LIGOTimeGPS without overflowing.
149  * that is caught by the XLALINT8NSToGPS() function */
150  return XLALINT8NSToGPS(t1, XLALGPSToINT8NS(t1) - XLALGPSToINT8NS(t0));
151 }
152 
153 
154 /**
155  * Difference between two GPS times as double. Returns t1 - t0.
156  */
157 REAL8 XLALGPSDiff( const LIGOTimeGPS *t1, const LIGOTimeGPS *t0 )
158 {
159  double hi = t1->gpsSeconds - t0->gpsSeconds;
160  double lo = t1->gpsNanoSeconds - t0->gpsNanoSeconds;
161  return hi + lo / XLAL_BILLION_REAL8;
162 }
163 
164 
165 /**
166  * Compares two GPS times.
167  * Returns:
168  * - -1 if t0 < t1
169  * - 0 if t0 == t1
170  * - 1 if t0 > t1.
171  * A NULL GPS time is always less than a non-NULL GPS time,
172  * and two NULL GPS times are considered equal.
173  */
174 int XLALGPSCmp( const LIGOTimeGPS *t0, const LIGOTimeGPS *t1 )
175 {
176  if ( t0 == NULL || t1 == NULL ) {
177  return ( t1 != NULL ) ? -1 : ( ( t0 != NULL ) ? 1 : 0 );
178  }
179  else {
180  INT8 ns0 = XLALGPSToINT8NS( t0 );
181  INT8 ns1 = XLALGPSToINT8NS( t1 );
182  return ( ns0 > ns1 ) - ( ns0 < ns1 );
183  }
184 }
185 
186 
187 /*
188  * Split a double-precision float into "large" and "small" parts. The
189  * results are stored in the hi and lo arguments, respectively. hi and lo
190  * are such that
191  *
192  * hi + lo = x
193  *
194  * exactly, and
195  *
196  * hi / lo ~= 2^26
197  *
198  * (hi and lo have the same sign).
199  *
200  * If x is 0, hi and lo are set to 0. If x is positive (negative)
201  * infinity, hi is set to positive (negative) infinity and lo is set to 0.
202  * If x is NaN, hi and lo are set to NaN.
203  *
204  * No errors occur.
205  */
206 
207 static void split_double(double x, double *hi, double *lo)
208 {
209  /* special case for inf. nan will take care of itself */
210  if(isinf(x)) {
211  *hi = x;
212  *lo = 0.;
213  return;
214  }
215  /* save leading order bits in *hi */
216  *hi = (float) x;
217  /* tweak to ensure residual will have same sign as *hi */
218  *hi -= LAL_REAL4_EPS * *hi;
219  /* residual */
220  *lo = x - *hi;
221 }
222 
223 
224 /**
225  * Multiply a GPS time by a number. Computes gps * x and places the result
226  * in gps. Returns gps on success, NULL on failure.
227  */
229 {
230  LIGOTimeGPS workspace = *gps;
231  double slo, shi;
232  double xlo, xhi;
233  double addendlo[4], addendhi[4];
234 
235  if(isnan(x) || isinf(x)) {
236  XLALPrintError("%s(): invalid multiplicand %g", __func__, x);
238  }
239 
240  /* ensure the seconds and nanoseconds components have the same sign so
241  * that the addend fragments we compute below all have the same sign */
242 
243  if(workspace.gpsSeconds < 0 && workspace.gpsNanoSeconds > 0) {
244  workspace.gpsSeconds += 1;
245  workspace.gpsNanoSeconds -= 1000000000;
246  } else if(workspace.gpsSeconds > 0 && workspace.gpsNanoSeconds < 0) {
247  workspace.gpsSeconds -= 1;
248  workspace.gpsNanoSeconds += 1000000000;
249  }
250 
251  /* split seconds and multiplicand x into leading-order and low-order
252  * components */
253 
254  slo = workspace.gpsSeconds % (1<<16);
255  shi = workspace.gpsSeconds - slo;
256  split_double(x, &xhi, &xlo);
257 
258  /* the count of seconds and the multiplicand x have each been split into
259  * two parts, a high part and a low part. from these, there are 4 terms
260  * in their product, and each term has sufficiently low dynamic range
261  * that it can be computed using double precision floating point
262  * arithmetic. we compute the 4 terms, split each into an integer and
263  * fractional part on its own, then sum the fractional parts and integer
264  * parts separately, adding the product of the nanoseconds and x into the
265  * fractional parts when summing them. because the storage locations for
266  * those sums have relatively low dynamic range no care need be taken in
267  * computing the sums. */
268 
269  addendlo[0] = modf(slo * xlo, &addendhi[0]);
270  addendlo[1] = modf(shi * xlo, &addendhi[1]);
271  addendlo[2] = modf(slo * xhi, &addendhi[2]);
272  addendlo[3] = modf(shi * xhi, &addendhi[3]);
273 
274  /* initialize result with the sum of components that contribute to the
275  * fractional part */
276  if(!XLALGPSSetREAL8(gps, addendlo[0] + addendlo[1] + addendlo[2] + addendlo[3] + workspace.gpsNanoSeconds * x / XLAL_BILLION_REAL8))
278  /* now add the components that contribute only to the integer seconds
279  * part */
280  if(!XLALGPSSetREAL8(&workspace, addendhi[0] + addendhi[1] + addendhi[2] + addendhi[3]))
282  return XLALGPSAddGPS(gps, &workspace);
283 }
284 
285 
286 /**
287  * Divide a GPS time by a number. Computes gps / x and places the result
288  * in gps. Returns gps on success, NULL on failure.
289  */
291 {
292  LIGOTimeGPS quotient;
293  double residual;
294  /* see below */
295  double threshold = 0.5e-9 * (1. + fabs(1. / x));
296 
297  if(isnan(x)) {
298  XLALPrintError("%s(): NaN", __func__);
300  }
301  if(x == 0) {
302  XLALPrintError("%s(): divide by zero", __func__);
304  }
305 
306  /* initial guess */
307  if(!XLALGPSSetREAL8(&quotient, XLALGPSGetREAL8(gps) / x))
309  /* use Newton's method to iteratively solve for quotient. strictly
310  * speaking we're using Newton's method to solve for the inverse of
311  * XLALGPSMultiply(), which we assume implements multiplication. */
312  do {
313  LIGOTimeGPS workspace = quotient;
314  /* NOTE: this computes the exactly (we hope) rounded result, not the
315  * exact result */
316  if(!XLALGPSMultiply(&workspace, x))
318  /* NOTE: workspace can differ from gps by up to .5 nanoseconds even if
319  * quotient is exactly correct because workspace has been rounded.
320  * therefore we expect |residual| to be as large as (.5 ns / x) when
321  * quotient is exactly correct */
322  residual = XLALGPSDiff(gps, &workspace) / x;
323  /* if residual is < 0.5 ns this will not change the estimate of
324  * quotient */
325  if(!XLALGPSAdd(&quotient, residual))
327  /* threshold (precomputed above) is 0.5 ns because if residual is
328  * smaller than this it can't change the answer, but plus an additional
329  * |0.5 ns / x| to accomodate error in computing residual with finite
330  * precision (see above) */
331  } while(fabs(residual) > threshold);
332  *gps = quotient;
333 
334  return gps;
335 }
336 
337 /** @} */
#define XLAL_BILLION_INT8
Definition: Date.h:40
#define XLAL_BILLION_REAL8
Definition: Date.h:41
int XLALPrintError(const char *fmt,...)
Definition: XLALError.c:68
#define LAL_REAL4_EPS
Difference between 1 and the next resolvable REAL4 2^-23.
Definition: LALConstants.h:57
double REAL8
Double precision real floating-point number (8 bytes).
int64_t INT8
Eight-byte signed integer; on some platforms this is equivalent to long int instead.
int32_t INT4
Four-byte signed integer.
#define LAL_INT8_FORMAT
Definition: LALStdio.h:128
#define XLAL_ERROR_NULL(...)
Macro to invoke a failure from a XLAL routine returning a pointer.
Definition: XLALError.h:713
@ XLAL_EFPINVAL
IEEE Invalid floating point operation, eg sqrt(-1), 0/0.
Definition: XLALError.h:448
@ XLAL_EFUNC
Internal function call failed bit: "or" this with existing error number.
Definition: XLALError.h:462
@ XLAL_EDOM
Input domain error.
Definition: XLALError.h:410
@ XLAL_EFPDIV0
IEEE Division by zero floating point error.
Definition: XLALError.h:449
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
Adds a double to a GPS time.
Definition: XLALTime.c:131
REAL8 XLALGPSModf(REAL8 *iptr, const LIGOTimeGPS *epoch)
Breaks the GPS time into REAL8 integral and fractional parts, each of which has the same sign as the ...
Definition: XLALTime.c:103
LIGOTimeGPS * XLALGPSDivide(LIGOTimeGPS *gps, REAL8 x)
Divide a GPS time by a number.
Definition: XLALTime.c:290
static void split_double(double x, double *hi, double *lo)
Definition: XLALTime.c:207
LIGOTimeGPS * XLALGPSAddGPS(LIGOTimeGPS *epoch, const LIGOTimeGPS *dt)
Adds two GPS times.
Definition: XLALTime.c:117
LIGOTimeGPS * XLALGPSSetREAL8(LIGOTimeGPS *epoch, REAL8 t)
Sets GPS time given GPS seconds as a REAL8.
Definition: XLALTime.c:73
int XLALGPSCmp(const LIGOTimeGPS *t0, const LIGOTimeGPS *t1)
Compares two GPS times.
Definition: XLALTime.c:174
REAL8 XLALGPSGetREAL8(const LIGOTimeGPS *epoch)
Returns the GPS time as a REAL8.
Definition: XLALTime.c:91
LIGOTimeGPS * XLALGPSMultiply(LIGOTimeGPS *gps, REAL8 x)
Multiply a GPS time by a number.
Definition: XLALTime.c:228
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
Difference between two GPS times as double.
Definition: XLALTime.c:157
LIGOTimeGPS * XLALGPSSet(LIGOTimeGPS *epoch, INT4 gpssec, INT8 gpsnan)
Sets GPS time given GPS integer seconds and residual nanoseconds.
Definition: XLALTime.c:63
LIGOTimeGPS * XLALINT8NSToGPS(LIGOTimeGPS *epoch, INT8 ns)
Converts nano seconds stored as an INT8 to GPS time.
Definition: XLALTime.c:46
INT8 XLALGPSToINT8NS(const LIGOTimeGPS *epoch)
Converts GPS time to nano seconds stored as an INT8.
Definition: XLALTime.c:36
LIGOTimeGPS * XLALGPSSubGPS(LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
Difference between two GPS times.
Definition: XLALTime.c:144
Epoch relative to GPS epoch, see LIGOTimeGPS type for more details.
Definition: LALDatatypes.h:458
INT4 gpsSeconds
Seconds since 0h UTC 6 Jan 1980.
Definition: LALDatatypes.h:459
INT4 gpsNanoSeconds
Residual nanoseconds.
Definition: LALDatatypes.h:460
enum @1 epoch