Loading [MathJax]/extensions/TeX/AMSsymbols.js
LAL 7.7.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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{
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 */
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 */
174int 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
207static 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 * XLALGPSDivide(LIGOTimeGPS *gps, REAL8 x)
Divide a GPS time by a number.
Definition: XLALTime.c:290
LIGOTimeGPS * XLALGPSSetREAL8(LIGOTimeGPS *epoch, REAL8 t)
Sets GPS time given GPS seconds as a REAL8.
Definition: XLALTime.c:73
LIGOTimeGPS * XLALGPSSet(LIGOTimeGPS *epoch, INT4 gpssec, INT8 gpsnan)
Sets GPS time given GPS integer seconds and residual nanoseconds.
Definition: XLALTime.c:63
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 * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
Adds a double to a GPS time.
Definition: XLALTime.c:131
LIGOTimeGPS * XLALGPSSubGPS(LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
Difference between two GPS times.
Definition: XLALTime.c:144
static void split_double(double x, double *hi, double *lo)
Definition: XLALTime.c:207
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 * XLALGPSAddGPS(LIGOTimeGPS *epoch, const LIGOTimeGPS *dt)
Adds two GPS times.
Definition: XLALTime.c:117
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
Difference between two GPS times as double.
Definition: XLALTime.c:157
LIGOTimeGPS * XLALGPSMultiply(LIGOTimeGPS *gps, REAL8 x)
Multiply a GPS time by a number.
Definition: XLALTime.c:228
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
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