LALPulsar  6.1.0.1-b72065a
SFTfileIO.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010, 2013, 2014, 2016, 2019--2022 Karl Wette
3  * Copyright (C) 2010 Chris Messenger
4  * Copyright (C) 2009, 2011 Adam Mercer
5  * Copyright (C) 2006 Badri Krishnan
6  * Copyright (C) 2004--2007, 2010, 2012, 2017 Bernd Machenschalk
7  * Copyright (C) 2004--2008, 2010--2016 Reinhard Prix
8  * Copyright (C) 2004, 2005 Alicia Sintes
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with with program; see the file COPYING. If not, write to the
22  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23  * MA 02110-1301 USA
24  */
25 
26 /*---------- includes ----------*/
27 
28 #include "SFTinternal.h"
29 #include "SFTReferenceLibrary.h"
30 
31 /*---------- constants ----------*/
32 
33 /** blocksize used in SFT-reading for the CRC-checksum computation (has to be multiple of 8 !!) */
34 #define BLOCKSIZE 8192 * 8
35 
36 /*---------- internal types ----------*/
37 
38 typedef struct {
50 
51 /** segments read so far from one SFT */
52 typedef struct {
53  UINT4 first; /**< first bin in this segment */
54  UINT4 last; /**< last bin in this segment */
55  LIGOTimeGPS epoch; /**< timestamp of this SFT */
56  struct tagSFTLocator *lastfrom; /**< last bin read from this locator */
58 
59 /*---------- internal prototypes ----------*/
60 
61 static int read_header_from_fp( FILE *fp, SFTtype *header, UINT4 *nsamples, UINT8 *header_crc64, UINT8 *ref_crc64, UINT2 *SFTwindowspec, CHAR **SFTcomment, BOOLEAN swapEndian );
62 
63 /*========== function definitions ==========*/
64 
65 /// \addtogroup SFTfileIO_h
66 /// @{
67 
68 /**
69  * Load the given frequency-band <tt>[fMin, fMax)</tt> (half-open) from the SFT-files listed in the
70  * SFT-'catalogue' ( returned by XLALSFTdataFind() ).
71  *
72  * Note: \a fMin (or \a fMax) is allowed to be set to \c -1, which means to read in all
73  * Frequency-bins from the lowest (or up to the highest) found in all SFT-files of the catalog.
74  *
75  * Note 2: The returned frequency-interval is guaranteed to contain <tt>[fMin, fMax)</tt>,
76  * but is allowed to be larger, as it must be an interval of discrete frequency-bins as found
77  * in the SFT-file.
78  *
79  * Note 3: This function has the capability to read sequences of SFT segments and
80  * putting them together to single SFTs while reading.
81  *
82  * Note 4: The 'fudge region' allowing for numerical noise is fudge= 10*LAL_REAL8_EPS ~2e-15
83  * relative deviation: ie if the SFT contains a bin at 'fi', then we consider for example
84  * "fMin == fi" if fabs(fi - fMin)/fi < fudge.
85  */
86 SFTVector *
87 XLALLoadSFTs( const SFTCatalog *catalog, /**< The 'catalogue' of SFTs to load */
88  REAL8 fMin, /**< minumum requested frequency (-1 = read from lowest) */
89  REAL8 fMax /**< maximum requested frequency (-1 = read up to highest) */
90  )
91 {
92  UINT4 catPos; /**< current file in catalog */
93  UINT4 firstbin, lastbin; /**< the first and last bin we want to read */
94  UINT4 minbin, maxbin; /**< min and max bin of all SFTs in the catalog */
95  UINT4 nSFTs = 1; /**< number of SFTs, i.e. different GPS timestamps */
96  REAL8 deltaF; /**< frequency spacing of SFT */
97  SFTCatalog locatalog; /**< local copy of the catalog to be sorted by 'locator' */
98  SFTVector *sftVector = NULL; /**< the vector of SFTs to be returned */
99  SFTReadSegment *segments = NULL; /**< array of segments already read of an SFT */
100  char empty = '\0'; /**< empty string */
101  char *fname = &empty; /**< name of currently open file, initially "" */
102  FILE *fp = NULL; /**< open file */
103  SFTtype *thisSFT = NULL; /**< SFT to read from file */
104 
105  /* error handler: free memory and return with error */
106 #define XLALLOADSFTSERROR(eno) { \
107  if(fp) \
108  fclose(fp); \
109  if(segments) \
110  XLALFree(segments); \
111  if(locatalog.data) \
112  XLALFree(locatalog.data); \
113  if(thisSFT) \
114  XLALDestroySFT(thisSFT); \
115  if(sftVector) \
116  XLALDestroySFTVector(sftVector); \
117  XLAL_ERROR_NULL(eno); \
118  }
119 
120  /* initialize locatalog.data so it doesn't get free()d on early error */
121  locatalog.data = NULL;
122 
123  /* check function parameters */
124  if ( !catalog ) {
126  }
127 
128  /* determine number of SFTs, i.e. number of different GPS timestamps.
129  The catalog should be sorted by GPS time, so just count changes.
130  Record the 'index' of GPS time in the 'isft' field of the locator,
131  so that we know later in which SFT to put this segment
132 
133  while at it, record max and min bin of all SFTs in the catalog */
134 
135  LIGOTimeGPS epoch = catalog->data[0].header.epoch;
136  catalog->data[0].locator->isft = nSFTs - 1;
137  deltaF = catalog->data[0].header.deltaF; /* Hz/bin */
138  minbin = firstbin = lround( catalog->data[0].header.f0 / deltaF );
139  maxbin = lastbin = firstbin + catalog->data[0].numBins - 1;
140  for ( catPos = 1; catPos < catalog->length; catPos++ ) {
141  firstbin = lround( catalog->data[catPos].header.f0 / deltaF );
142  lastbin = firstbin + catalog->data[catPos].numBins - 1;
143  if ( firstbin < minbin ) {
144  minbin = firstbin;
145  }
146  if ( lastbin > maxbin ) {
147  maxbin = lastbin;
148  }
149  if ( !GPSEQUAL( epoch, catalog->data[catPos].header.epoch ) ) {
150  epoch = catalog->data[catPos].header.epoch;
151  nSFTs++;
152  }
153  catalog->data[catPos].locator->isft = nSFTs - 1;
154  }
155  XLALPrintInfo( "%s: fMin: %f, fMax: %f, deltaF: %f, minbin: %u, maxbin: %u\n", __func__, fMin, fMax, deltaF, minbin, maxbin );
156 
157  /* calculate first and last frequency bin to read */
158  if ( fMin < 0 ) {
159  firstbin = minbin;
160  } else {
162  }
163  if ( fMax < 0 ) {
164  lastbin = maxbin;
165  } else {
166  lastbin = XLALRoundFrequencyUpToSFTBin( fMax, deltaF ) - 1;
167  if ( ( lastbin == 0 ) && ( fMax != 0 ) ) {
168  XLALPrintError( "ERROR: last bin to read is 0 (fMax: %f, deltaF: %f)\n", fMax, deltaF );
170  }
171  }
172  XLALPrintInfo( "%s: Reading from first bin: %u, last bin: %u\n", __func__, firstbin, lastbin );
173 
174  /* allocate the SFT vector that will be returned */
175  if ( !( sftVector = XLALCreateSFTVector( nSFTs, lastbin + 1 - firstbin ) ) ) {
176  XLALPrintError( "ERROR: Couldn't create sftVector\n" );
178  }
179 
180  /* allocate an additional single SFT where SFTs are read in */
181  if ( !( thisSFT = XLALCreateSFT( lastbin + 1 - firstbin ) ) ) {
182  XLALPrintError( "ERROR: Couldn't create thisSFT\n" );
184  }
185 
186  /* make a copy of the catalog that gets sorted by locator.
187  Eases maintaing a correctly (epoch-)sorted catalog, particulary in case of errors
188  note: only the pointers to the SFTdescriptors are copied & sorted, not the descriptors */
189  locatalog.length = catalog->length;
190  {
191  UINT4 size = catalog->length * sizeof( catalog->data[0] );
192  if ( !( locatalog.data = XLALMalloc( size ) ) ) {
193  XLALPrintError( "ERROR: Couldn't allocate locatalog.data\n" );
195  }
196  memcpy( locatalog.data, catalog->data, size );
197  }
198 
199  /* sort catalog by f0, locator */
200  qsort( ( void * )locatalog.data, locatalog.length, sizeof( locatalog.data[0] ), compareSFTloc );
201 
202  /* allocate segment vector, one element per final SFT */
203  if ( !( segments = XLALCalloc( nSFTs, sizeof( SFTReadSegment ) ) ) ) {
204  XLALPrintError( "ERROR: Couldn't allocate locatalog.data\n" );
206  }
207 
208  /* loop over all files (actually locators) in the catalog */
209  for ( catPos = 0; catPos < catalog->length; catPos++ ) {
210 
211  struct tagSFTLocator *locator = locatalog.data[catPos].locator;
212  UINT4 isft = locator->isft;;
213  UINT4 firstBinRead;
214  UINT4 lastBinRead;
215 
216  if ( locatalog.data[catPos].header.data ) {
217  /* the SFT data has already been read into the catalog,
218  copy the relevant part to thisSFT */
219 
220  volatile REAL8 tmp = locatalog.data[catPos].header.f0 / deltaF;
221  UINT4 firstSFTbin = lround( tmp );
222  UINT4 lastSFTbin = firstSFTbin + locatalog.data[catPos].numBins - 1;
223  UINT4 firstBin2read = firstbin;
224  UINT4 lastBin2read = lastbin;
225  UINT4 numBins2read, offsetBins;
226 
227  /* limit the interval to be read to what's actually in the SFT */
228  if ( firstBin2read < firstSFTbin ) {
229  firstBin2read = firstSFTbin;
230  }
231  if ( lastBin2read > lastSFTbin ) {
232  lastBin2read = lastSFTbin;
233  }
234 
235  /* check that requested interval is found in SFT */
236  if ( firstBin2read <= lastBin2read ) {
237 
238  /* keep a copy of the data pointer */
239  COMPLEX8Sequence *data = thisSFT->data;
240 
241  firstBinRead = firstBin2read;
242  lastBinRead = lastBin2read;
243  offsetBins = firstBin2read - firstSFTbin;
244  numBins2read = lastBin2read - firstBin2read + 1;
245 
246  /* copy the header */
247  *thisSFT = locatalog.data[catPos].header;
248  /* restore data pointer */
249  thisSFT->data = data;
250  /* copy the data */
251  memcpy( thisSFT->data->data,
252  locatalog.data[catPos].header.data + offsetBins,
253  numBins2read * sizeof( COMPLEX8 ) );
254 
255  /* update the start-frequency entry in the SFT-header to the new value */
256  thisSFT->f0 = 1.0 * firstBin2read * thisSFT->deltaF;
257 
258  } else {
259  /* no data was needed from this SFT (segment) */
260  firstBinRead = 0;
261  lastBinRead = 0;
262  }
263 
264  } else {
265  /* SFT data had not yet been read - read it */
266 
267  /* open and close a file only when necessary, i.e. reading a different file */
268  if ( strcmp( fname, locator->fname ) ) {
269  if ( fp ) {
270  fclose( fp );
271  fp = NULL;
272  }
273  fname = locator->fname;
274  fp = fopen( fname, "rb" );
275  XLALPrintInfo( "%s: Opening file '%s'\n", __func__, fname );
276  if ( !fp ) {
277  XLALPrintError( "ERROR: Couldn't open file '%s'\n", fname );
279  }
280  }
281 
282  /* seek to the position of the SFT in the file (if necessary) */
283  if ( locator->offset )
284  if ( fseek( fp, locator->offset, SEEK_SET ) == -1 ) {
285  XLALPrintError( "ERROR: Couldn't seek to position %ld in file '%s'\n",
286  locator->offset, fname );
288  }
289 
290  /* read SFT data */
291  lastBinRead = read_sft_bins_from_fp( thisSFT, &firstBinRead, firstbin, lastbin, fp );
292  XLALPrintInfo( "%s: Read data from %s:%lu: %u - %u\n", __func__, locator->fname, locator->offset, firstBinRead, lastBinRead );
293  }
294  /* SFT data has been read from file or taken from catalog */
295 
296  if ( lastBinRead ) {
297  /* data was actually read */
298 
299  if ( segments[isft].last == 0 ) {
300 
301  /* no data was read for this SFT yet: must be first segment */
302  if ( firstBinRead != firstbin ) {
303  XLALPrintError( "ERROR: data gap or overlap at first bin of SFT#%u (GPS %lf)"
304  " expected bin %u, bin %u read from file '%s'\n",
305  isft, GPS2REAL8( thisSFT->epoch ),
306  firstbin, firstBinRead, fname );
308  }
309  segments[isft].first = firstBinRead;
310  segments[isft].epoch = thisSFT->epoch;
311 
312  /* if not first segment, segment must fit at the end of previous data */
313  } else if ( firstBinRead != segments[isft].last + 1 ) {
314  XLALPrintError( "ERROR: data gap or overlap in SFT#%u (GPS %lf)"
315  " between bin %u read from file '%s' and bin %u read from file '%s'\n",
316  isft, GPS2REAL8( thisSFT->epoch ),
317  segments[isft].last, segments[isft].lastfrom->fname,
318  firstBinRead, fname );
320  }
321 
322  /* consistency checks */
323  if ( deltaF != thisSFT->deltaF ) {
324  XLALPrintError( "ERROR: deltaF mismatch (%f/%f) in SFT read from file '%s'\n",
325  thisSFT->deltaF, deltaF, fname );
327  }
328  if ( !GPSEQUAL( segments[isft].epoch, thisSFT->epoch ) ) {
329  XLALPrintError( "ERROR: GPS epoch mismatch (%f/%f) in SFT read from file '%s'\n",
330  GPS2REAL8( segments[isft].epoch ), GPS2REAL8( thisSFT->epoch ), fname );
332  }
333 
334  /* data is ok, add to SFT */
335  segments[isft].last = lastBinRead;
336  segments[isft].lastfrom = locator;
337  memcpy( sftVector->data[isft].name, locatalog.data[catPos].header.name, sizeof( sftVector->data[isft].name ) );
338  sftVector->data[isft].sampleUnits = locatalog.data[catPos].header.sampleUnits;
339  memcpy( sftVector->data[isft].data->data + ( firstBinRead - firstbin ),
340  thisSFT->data->data,
341  ( lastBinRead - firstBinRead + 1 ) * sizeof( COMPLEX8 ) );
342 
343  } else if ( !firstBinRead ) {
344  /* no needed data had been in this segment */
345  XLALPrintInfo( "%s: No data read from %s:%lu\n", __func__, locator->fname, locator->offset );
346 
347  /* set epoch if not yet set, if already set, check it */
348  if ( GPSZERO( segments[isft].epoch ) ) {
349  segments[isft].epoch = thisSFT->epoch;
350  } else if ( !GPSEQUAL( segments[isft].epoch, thisSFT->epoch ) ) {
351  XLALPrintError( "ERROR: GPS epoch mismatch (%f/%f) in SFT read from file '%s'\n",
352  GPS2REAL8( segments[isft].epoch ), GPS2REAL8( thisSFT->epoch ), fname );
354  }
355 
356  } else {
357  /* failed to read data */
358 
359  XLALPrintError( "ERROR: Error (%u) reading SFT from file '%s'\n", firstBinRead, fname );
361  }
362  }
363 
364  /* close the last file */
365  if ( fp ) {
366  fclose( fp );
367  fp = NULL;
368  }
369 
370  /* check that all SFTs are complete */
371  for ( UINT4 isft = 0; isft < nSFTs; isft++ ) {
372  if ( segments[isft].last == lastbin ) {
373  sftVector->data[isft].f0 = 1.0 * firstbin * deltaF;
374  sftVector->data[isft].epoch = segments[isft].epoch;
375  sftVector->data[isft].deltaF = deltaF;
376  } else {
377  if ( segments[isft].last )
378  XLALPrintError( "ERROR: data missing at end of SFT#%u (GPS %lf)"
379  " expected bin %u, bin %u read from file '%s'\n",
380  isft, GPS2REAL8( segments[isft].epoch ),
381  lastbin, segments[isft].last,
382  segments[isft].lastfrom->fname );
383  else
384  XLALPrintError( "ERROR: no data could be read for SFT#%u (GPS %lf)\n",
385  isft, GPS2REAL8( segments[isft].epoch ) );
387  }
388  }
389 
390  /* cleanup */
391  XLALFree( segments );
392  XLALFree( locatalog.data );
393  XLALDestroySFT( thisSFT );
394 
395  return ( sftVector );
396 
397 } /* XLALLoadSFTs() */
398 
399 
400 /**
401  * Function to load a catalog of SFTs from possibly different detectors.
402  * This is similar to XLALLoadSFTs except that the input SFT catalog is
403  * allowed to contain multiple ifos. The output is the structure
404  * MultiSFTVector which is a vector of (pointers to) SFTVectors, one for
405  * each ifo found in the catalog. As in XLALLoadSFTs, fMin and fMax can be
406  * set to -1 to get the full SFT from the lowest to the highest frequency
407  * bin found in the SFT.
408  *
409  * output SFTvectors are sorted alphabetically by detector-name
410  *
411  * NOTE: this is basically a backwards-compatible API wrapper to
412  * XLALLoadMultiSFTsFromView(), which takes a MultiSFTCatalogView as input instead.
413  *
414  */
416 XLALLoadMultiSFTs( const SFTCatalog *inputCatalog, /**< The 'catalogue' of SFTs to load */
417  REAL8 fMin, /**< minumum requested frequency (-1 = read from lowest) */
418  REAL8 fMax /**< maximum requested frequency (-1 = read up to highest) */
419  )
420 
421 {
422  XLAL_CHECK_NULL( ( inputCatalog != NULL ) && ( inputCatalog->length != 0 ), XLAL_EINVAL );
423 
424  MultiSFTCatalogView *multiCatalogView;
425  // get the (alphabetically-sorted!) multiSFTCatalogView
426  XLAL_CHECK_NULL( ( multiCatalogView = XLALGetMultiSFTCatalogView( inputCatalog ) ) != NULL, XLAL_EFUNC );
427 
429  XLAL_CHECK_NULL( ( multiSFTs = XLALLoadMultiSFTsFromView( multiCatalogView, fMin, fMax ) ) != NULL, XLAL_EFUNC );
430 
431  /* free memory and exit */
432  XLALDestroyMultiSFTCatalogView( multiCatalogView );
433 
434  return multiSFTs;
435 
436 } /* XLALLoadMultiSFTs() */
437 
438 
439 /**
440  * This function loads a MultiSFTVector from a given input MultiSFTCatalogView,
441  * otherwise the documentation of XLALLoadMultiSFTs() applies.
442  *
443  * Note: this is basically the core-function of XLALLoadMultiSFTs() doing the
444  * actual work.
445  *
446  * Note2: we keep the IFO sort-order of the input multiCatalogView
447  */
449 XLALLoadMultiSFTsFromView( const MultiSFTCatalogView *multiCatalogView, /**< The multi-SFT catalogue view of SFTs to load */
450  REAL8 fMin, /**< minumum requested frequency (-1 = read from lowest) */
451  REAL8 fMax /**< maximum requested frequency (-1 = read up to highest) */
452  )
453 {
454  XLAL_CHECK_NULL( multiCatalogView != NULL, XLAL_EINVAL );
455  XLAL_CHECK_NULL( multiCatalogView->length != 0, XLAL_EINVAL );
456 
457  UINT4 numIFOs = multiCatalogView->length;
458 
459  /* create multi sft vector */
461  XLAL_CHECK_NULL( ( multiSFTs = XLALCalloc( 1, sizeof( *multiSFTs ) ) ) != NULL, XLAL_ENOMEM );
462  XLAL_CHECK_NULL( ( multiSFTs->data = XLALCalloc( numIFOs, sizeof( *multiSFTs->data ) ) ) != NULL, XLAL_ENOMEM );
463  multiSFTs->length = numIFOs;
464 
465  for ( UINT4 X = 0; X < numIFOs; X++ ) {
466  if ( ( multiSFTs->data[X] = XLALLoadSFTs( &( multiCatalogView->data[X] ), fMin, fMax ) ) == NULL ) {
467  /* free sft vectors created previously in loop */
469  XLAL_ERROR_NULL( XLAL_EFUNC, "Failed to XLALLoadSFTs() for IFO X = %d\n", X );
470  } // if XLALLoadSFTs() failed
471 
472  } // for X < numIFOs
473 
474  // return final multi-SFT vector
475  return multiSFTs;
476 
477 } // XLALLoadMultiSFTsFromView()
478 
479 
480 /**
481  * Write the given SFTtype to a FILE pointer.
482  * Add the comment to SFT if SFTcomment != NULL.
483  *
484  * NOTE: the comment written into the SFT-file contains the 'sft->name' field concatenated with
485  * the user-specified 'SFTcomment'
486  */
487 int
489  const SFTtype *sft, /**< SFT to write to disk */
490  FILE *fp, /**< pointer to open file */
491  const CHAR *SFTwindowtype, /**< window applied to SFT, if any */
492  const REAL8 SFTwindowparam, /**< parameter of window */
493  const CHAR *SFTcomment /**< optional comment */
494 )
495 {
496  UINT4 comment_len = 0;
497  CHAR *_SFTcomment;
498  UINT4 pad_len = 0;
499  CHAR pad[] = {0, 0, 0, 0, 0, 0, 0}; /* for comment-padding */
500  _SFT_header_t rawheader;
501 
502  /* check input consistency */
503  if ( !sft || !sft->data || sft->deltaF <= 0 || sft->f0 < 0 || sft->data->length == 0 ) {
505  }
506  if ( !( ( sft->epoch.gpsSeconds >= 0 ) && ( sft->epoch.gpsNanoSeconds >= 0 ) ) ) {
508  }
509  if ( !( sft->epoch.gpsNanoSeconds < 1000000000 ) ) {
511  }
512  if ( !XLALIsValidCWDetector( sft->name ) ) {
513  XLALPrintError( "\nInvalid detector prefix '%c%c'\n\n", sft->name[0], sft->name[1] );
515  }
516 
517  if ( !fp ) {
519  }
520 
521  XLAL_CHECK( SFTwindowtype != NULL, XLAL_EFAULT );
522 
523  /* concat sft->name + SFTcomment for SFT-file comment-field */
524  comment_len = strlen( sft->name ) + 1;
525  if ( SFTcomment ) {
526  comment_len += strlen( SFTcomment ) + 1; /* separate by "\n" */
527  }
528 
529  if ( ( _SFTcomment = XLALCalloc( comment_len, sizeof( CHAR ) ) ) == NULL ) {
531  }
532  strcpy( _SFTcomment, sft->name );
533  if ( SFTcomment ) {
534  strcat( _SFTcomment, "\n" );
535  strcat( _SFTcomment, SFTcomment );
536  }
537 
538  /* comment length including null terminator to string must be an
539  * integer multiple of eight bytes.
540  */
541  pad_len = ( 8 - ( comment_len % 8 ) ) % 8;
542 
543  /* ----- fill out header */
544  rawheader.version = MAX_SFT_VERSION;
545  rawheader.gps_sec = sft->epoch.gpsSeconds;
546  rawheader.gps_nsec = sft->epoch.gpsNanoSeconds;
547  rawheader.tbase = TSFTfromDFreq( sft->deltaF );
548  rawheader.first_frequency_index = lround( sft->f0 / sft->deltaF );
549  rawheader.nsamples = sft->data->length;
550  rawheader.crc64 = 0; /* set to 0 for crc-calculation */
551  rawheader.detector[0] = sft->name[0];
552  rawheader.detector[1] = sft->name[1];
553  rawheader.comment_length = comment_len + pad_len;
554 
555  XLAL_CHECK( build_sft_windowspec( &rawheader.windowspec, NULL, SFTwindowtype, SFTwindowparam ) == XLAL_SUCCESS, XLAL_EFUNC );
556 
557  /* ----- compute CRC */
558  rawheader.crc64 = crc64( ( const unsigned char * )&rawheader, sizeof( rawheader ), ~( 0ULL ) );
559 
560  rawheader.crc64 = crc64( ( const unsigned char * )_SFTcomment, comment_len, rawheader.crc64 );
561  rawheader.crc64 = crc64( ( const unsigned char * )pad, pad_len, rawheader.crc64 );
562 
563  rawheader.crc64 = crc64( ( const unsigned char * ) sft->data->data, sft->data->length * sizeof( *sft->data->data ), rawheader.crc64 );
564 
565  /* ----- write the header to file */
566  if ( 1 != fwrite( &rawheader, sizeof( rawheader ), 1, fp ) ) {
567  XLAL_ERROR( XLAL_EIO );
568  }
569 
570  /* ----- write the comment to file */
571  if ( comment_len != fwrite( _SFTcomment, 1, comment_len, fp ) ) {
572  XLAL_ERROR( XLAL_EIO );
573  }
574  if ( pad_len != fwrite( pad, 1, pad_len, fp ) ) {
575  XLAL_ERROR( XLAL_EIO );
576  }
577 
578  XLALFree( _SFTcomment );
579 
580  /* write the data to the file. Data must be packed REAL,IMAG,REAL,IMAG,... */
581  if ( sft->data->length != fwrite( sft->data->data, sizeof( *sft->data->data ), sft->data->length, fp ) ) {
582  XLAL_ERROR( XLAL_EIO );
583  }
584 
585  return XLAL_SUCCESS;
586 
587 } /* XLALWriteSFT2FilePointer() */
588 
589 
590 /**
591  * Write the given SFTtype to a SFT file with the supplied filename.
592  * Add the comment to SFT if SFTcomment != NULL.
593  *
594  * NOTE: the comment written into the SFT-file contains the 'sft->name' field concatenated with
595  * the user-specified 'SFTcomment'
596  */
597 int
599  const SFTtype *sft, /**< SFT to write to disk */
600  const CHAR *SFTfilename, /**< SFT filename */
601  const CHAR *SFTwindowtype, /**< window applied to SFT, if any */
602  const REAL8 SFTwindowparam, /**< parameter of window */
603  const CHAR *SFTcomment /**< optional comment */
604 )
605 {
606  FILE *fp = NULL;
607 
608  /* Make sure the arguments are not NULL */
609  if ( !( sft ) ) {
611  }
612  if ( !( sft->data ) ) {
614  }
615  if ( !( SFTfilename ) ) {
617  }
618 
619  if ( !XLALIsValidCWDetector( sft->name ) ) {
620  XLALPrintError( "\nInvalid detector prefix '%c%c'\n\n", sft->name[0], sft->name[1] );
622  }
623 
624  XLAL_CHECK( SFTwindowtype != NULL, XLAL_EFAULT );
625 
626  /* open SFT-file for writing */
627  if ( ( fp = fopen( SFTfilename, "wb" ) ) == NULL ) {
628  XLALPrintError( "\nFailed to open file '%s' for writing: %s\n\n", SFTfilename, strerror( errno ) );
629  XLAL_ERROR( XLAL_EIO );
630  }
631 
632  /* write SFT to file */
633  if ( XLALWriteSFT2FilePointer( sft, fp, SFTwindowtype, SFTwindowparam, SFTcomment ) != XLAL_SUCCESS ) {
634  XLAL_ERROR( XLAL_EIO );
635  }
636 
637  fclose( fp );
638 
639  return XLAL_SUCCESS;
640 
641 } /* XLALWriteSFT2NamedFile() */
642 
643 
644 /**
645  * Write the given SFTtype to a SFT file with a standard (\cite SFT-spec) filename.
646  * Add the comment to SFT if SFTcomment != NULL.
647  *
648  * NOTE: the comment written into the SFT-file contains the 'sft->name' field concatenated with
649  * the user-specified 'SFTcomment'
650  *
651  * NOTE: The SFT filename spec is updated to reflect the filename of the written SFT, by
652  * setting the \c numSFTs, \c detector, \c SFTtimebase, \c gpsStart, and \c SFTspan fields.
653  * if needed, the SFT filename can be reconstructed with XLALBuildSFTFilenameFromSpec()
654  */
655 int
657  const SFTtype *sft, /**< SFT to write to disk */
658  SFTFilenameSpec *SFTfnspec, /**< SFT filename specification used to construct filename */
659  const CHAR *SFTcomment /**< optional comment */
660 )
661 {
662 
663  /* Make sure the arguments are not NULL */
664  if ( !( sft ) ) {
666  }
667  if ( !( sft->data ) ) {
669  }
670 
671  if ( !XLALIsValidCWDetector( sft->name ) ) {
672  XLALPrintError( "\nInvalid detector prefix '%c%c'\n\n", sft->name[0], sft->name[1] );
674  }
675 
676  XLAL_CHECK( SFTfnspec != NULL, XLAL_EFAULT );
677 
678  const UINT4 Tsft = ( UINT4 ) round( 1.0 / sft->deltaF );
679 
680  SFTfnspec->numSFTs = 1;
681  SFTfnspec->detector[0] = sft->name[0];
682  SFTfnspec->detector[1] = sft->name[1];
683  SFTfnspec->detector[2] = 0;
684  SFTfnspec->SFTtimebase = Tsft;
685  SFTfnspec->gpsStart = sft->epoch.gpsSeconds;
686 
687  /* calculate sft 'duration' -- may be different from timebase if nanosecond of sft-epoch is non-zero */
688  SFTfnspec->SFTspan = Tsft;
689  if ( sft->epoch.gpsNanoSeconds > 0 ) {
690  SFTfnspec->SFTspan += 1;
691  }
692 
693  // build SFT filename
694  char *SFTfilename = XLALBuildSFTFilenameFromSpec( SFTfnspec );
695  XLAL_CHECK( SFTfilename != NULL, XLAL_EFUNC );
696 
697  // write SFT
698  XLAL_CHECK( XLALWriteSFT2NamedFile( sft, SFTfilename, SFTfnspec->window_type, SFTfnspec->window_param, SFTcomment ) == XLAL_SUCCESS, XLAL_EFUNC );
699 
700  XLALFree( SFTfilename );
701 
702  return XLAL_SUCCESS;
703 
704 } /* XLALWriteSFT2StandardFile() */
705 
706 
707 /**
708  * Write the given SFTVector to a single merged SFT file with the supplied filename.
709  * Add the comment to SFT if SFTcomment != NULL.
710  */
711 int
713  const SFTVector *sftVect, /**< SFT vector to write to disk */
714  const CHAR *SFTfilename, /**< SFT filename */
715  const CHAR *SFTwindowtype, /**< window applied to SFT, if any */
716  const REAL8 SFTwindowparam, /**< parameter of window */
717  const CHAR *SFTcomment /**< optional comment */
718 )
719 {
720  XLAL_CHECK( sftVect != NULL, XLAL_EINVAL );
721  XLAL_CHECK( sftVect->data != NULL, XLAL_EINVAL );
722  XLAL_CHECK( sftVect->length > 0, XLAL_EINVAL );
723  XLAL_CHECK( SFTfilename != NULL, XLAL_EINVAL );
724 
725  const UINT4 numSFTs = sftVect->length;
726 
727  /* open SFT-file for writing */
728  FILE *fp;
729  XLAL_CHECK( ( fp = fopen( SFTfilename, "wb" ) ) != NULL, XLAL_EIO, "Failed to open '%s' for writing: %s\n\n", SFTfilename, strerror( errno ) );
730 
731  for ( UINT4 k = 0; k < numSFTs; k++ ) {
732  SFTtype *sft = &( sftVect->data[k] );
733 
734  /* write the k^th sft */
735  XLAL_CHECK( XLALWriteSFT2FilePointer( sft, fp, SFTwindowtype, SFTwindowparam, SFTcomment ) == XLAL_SUCCESS, XLAL_EFUNC );
736 
737  } // for k < numSFTs
738 
739  fclose( fp );
740 
741  return XLAL_SUCCESS;
742 
743 } /* XLALWriteSFTVector2NamedFile() */
744 
745 
746 /**
747  * Write the given SFTVector to SFT file(s) with a standard (\cite SFT-spec) filename(s).
748  * Add the comment to SFT if SFTcomment != NULL.
749  *
750  * NOTE: The SFT filename spec is updated to reflect the filename of the written SFT, by
751  * setting the \c numSFTs, \c detector, \c SFTtimebase, \c gpsStart, and \c SFTspan fields.
752  * if needed, the SFT filename can be reconstructed with XLALBuildSFTFilenameFromSpec()
753  */
754 int
756  const SFTVector *sftVect, /**< SFT vector to write to disk */
757  SFTFilenameSpec *SFTfnspec, /**< SFT filename specification used to construct filename(s) */
758  const CHAR *SFTcomment, /**< optional comment */
759  const BOOLEAN merged /**< If true, write a single merged SFT file; otherwise, write individual SFT files */
760 )
761 {
762  XLAL_CHECK( sftVect != NULL, XLAL_EINVAL );
763  XLAL_CHECK( sftVect->data != NULL, XLAL_EINVAL );
764  XLAL_CHECK( sftVect->length > 0, XLAL_EINVAL );
765 
766  XLAL_CHECK( SFTfnspec != NULL, XLAL_EFAULT );
767 
768  const UINT4 numSFTs = sftVect->length;
769  const SFTtype *sftStart = &( sftVect->data[0] );
770  const SFTtype *sftEnd = &( sftVect->data[numSFTs - 1] );
771  const LIGOTimeGPS *epochStart = &( sftStart->epoch );
772  const LIGOTimeGPS *epochEnd = &( sftEnd->epoch );
773 
774  const UINT4 Tsft = ( UINT4 ) round( 1.0 / sftStart->deltaF );
775 
776  if ( merged ) { // write a single merged SFT file
777 
778  SFTfnspec->numSFTs = numSFTs;
779  SFTfnspec->detector[0] = sftStart->name[0];
780  SFTfnspec->detector[1] = sftStart->name[1];
781  SFTfnspec->detector[2] = 0;
782  SFTfnspec->SFTtimebase = Tsft;
783  SFTfnspec->gpsStart = sftStart->epoch.gpsSeconds;
784 
785  /* calculate time interval covered -- may be different from timebase if nanosecond of sft-epochs are non-zero */
786  SFTfnspec->SFTspan = epochEnd->gpsSeconds - epochStart->gpsSeconds + Tsft;
787  if ( epochStart->gpsNanoSeconds > 0 ) {
788  SFTfnspec->SFTspan += 1;
789  }
790  if ( epochEnd->gpsNanoSeconds > 0 ) {
791  SFTfnspec->SFTspan += 1;
792  }
793 
794  // build SFT filename
795  char *SFTfilename = XLALBuildSFTFilenameFromSpec( SFTfnspec );
796  XLAL_CHECK( SFTfilename != NULL, XLAL_EFUNC );
797 
798  // write SFT
799  XLAL_CHECK( XLALWriteSFTVector2NamedFile( sftVect, SFTfilename, SFTfnspec->window_type, SFTfnspec->window_param, SFTcomment ) == XLAL_SUCCESS, XLAL_EFUNC );
800 
801  XLALFree( SFTfilename );
802 
803  } else { // write individual SFT files
804 
805  // local copy of 'SFTfnspec' so that we can return name of first written SFT
806  SFTFilenameSpec spec = *SFTfnspec;
807 
808  for ( UINT4 k = 0; k < numSFTs; k++ ) {
809 
810  SFTtype *sft = &( sftVect->data[k] );
811 
812  // return spec of first written SFT in 'SFTfnspec'; otherwise use local copy
813  SFTFilenameSpec *p_spec = ( k == 0 ) ? SFTfnspec : &spec;
814 
815  XLAL_CHECK( XLALWriteSFT2StandardFile( sft, p_spec, SFTcomment ) == XLAL_SUCCESS, XLAL_EFUNC );
816 
817  } // for k < numSFTs
818 
819  }
820 
821  return XLAL_SUCCESS;
822 
823 } /* XLALWriteSFTVector2StandardFile() */
824 
825 
826 /**
827  * Verify that the contents of a SFT file are valid.
828  *
829  * This is just an XLAL wrapper to the SFTReferenceLibrary function ValidateSFTFile().
830  *
831  * \return: XLAL_SUCCESS if no validation errors encountered.
832  */
833 int
835 {
836  int errcode = ValidateSFTFile( fname );
837  XLAL_CHECK( errcode == 0, XLAL_EFUNC, "SFT validation error on file '%s': code %d (%s)", fname, errcode, SFTErrorMessage( errcode ) );
838  return XLAL_SUCCESS;
839 } /* XLALCheckSFTFileIsValid */
840 
841 
842 /* a little endian-swapper needed for SFT reading/writing */
843 void
844 endian_swap( CHAR *pdata, size_t dsize, size_t nelements )
845 {
846  UINT4 i, j, indx;
847  CHAR tempbyte;
848 
849  if ( dsize <= 1 ) {
850  return;
851  }
852 
853  for ( i = 0; i < nelements; i++ ) {
854  indx = dsize;
855  for ( j = 0; j < dsize / 2; j++ ) {
856  tempbyte = pdata[j];
857  indx = indx - 1;
858  pdata[j] = pdata[indx];
859  pdata[indx] = tempbyte;
860  }
861 
862  pdata = pdata + dsize;
863  }
864 
865  return;
866 
867 } /* endian swap */
868 
869 
870 /**
871  * Open an "SFT" defined by the SFT-locator, return a FILE-pointer to the beginning of this SFT.
872  * \note The returned filepointer could point to an SFT-block within a merged SFT-file,
873  * so you should not assume that SEEK_SET takes you to the beginning of this block!
874  * (instead you'd have to save the current position returned by this function, which \
875  * points to the beginning of the block!)
876  *
877  * NOTE: Ideally this should be the *ONLY* function using the internal structure of the opaque
878  * SFTLocator type
879  *
880  */
881 FILE *
882 fopen_SFTLocator( const struct tagSFTLocator *locator )
883 {
884  FILE *fp = NULL;
885  CHAR *fname;
886 
887  if ( !locator ) {
888  return NULL;
889  }
890 
891  fname = locator->fname;
892  if ( ( fp = fopen( fname, "rb" ) ) == NULL ) {
893  XLALPrintError( "\nFailed to open SFT '%s' for reading: %s\n\n", fname, strerror( errno ) );
894  return NULL;
895  }
896 
897  if ( fseek( fp, locator->offset, SEEK_SET ) == -1 ) {
898  XLALPrintError( "\nFailed to set fp-offset to '%ld': %s\n\n", locator->offset, strerror( errno ) );
899  fclose( fp );
900  return NULL;
901  }
902 
903  return fp;
904 
905 } /* fopen_SFTLocator() */
906 
907 
908 /**
909  * Read valid SFT version-number at position fp, and determine if we need to
910  * endian-swap the data.
911  * Restores filepointer to original position before returning.
912  *
913  * RETURN: 0 = OK, -1 = ERROR
914  */
915 int
917 {
918  long save_filepos;
919  REAL8 ver;
920 
921  /* store fileposition for restoring in case of failure */
922  if ( ( save_filepos = ftell( fp ) ) == -1 ) {
923  XLALPrintError( "\nftell() failed: %s\n\n", strerror( errno ) );
924  return -1;
925  }
926 
927  /* read version-number */
928  if ( 1 != fread( &ver, sizeof( ver ), 1, fp ) ) {
929  if ( lalDebugLevel ) {
930  XLALPrintError( "\nCould not read version-number from file\n\n" );
931  }
932  goto failed;
933  }
934 
935 
936  /* figure out endian-ness and check version-range */
937  for ( *version = MAX_SFT_VERSION; *version >= MIN_SFT_VERSION; --( *version ) ) {
938  REAL8 vertest = *version;
939  if ( ! memcmp( &ver, &vertest, sizeof( ver ) ) ) {
940  *need_swap = FALSE;
941  break;
942  }
943  endian_swap( ( char * )( &vertest ), sizeof( vertest ), 1 );
944  if ( ! memcmp( &ver, &vertest, sizeof( ver ) ) ) {
945  *need_swap = TRUE;
946  break;
947  }
948  }
949  if ( *version < MIN_SFT_VERSION ) {
950  if ( lalDebugLevel ) {
951  unsigned char *v = ( unsigned char * )( &ver );
952  XLALPrintError( "\nERROR: illegal SFT-version (%X %X %X %X %X %X %X %X) not within [%.0f, %.0f]\n",
953  v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7],
954  ( float )MIN_SFT_VERSION, ( float )MAX_SFT_VERSION );
955  }
956  goto failed;
957  }
958 
959  /* restore initial filepointer position */
960  if ( fseek( fp, save_filepos, SEEK_SET ) == -1 ) {
961  XLALPrintError( "\nfseek() failed to return to intial fileposition: %s\n\n", strerror( errno ) );
962  goto failed;
963  }
964 
965  return 0;
966 
967 failed:
968  fseek( fp, save_filepos, SEEK_SET );
969  return -1;
970 
971 } /* read_SFTversion_from_fp() */
972 
973 
974 /* Try to read an SFT-header (of ANY VALID SFT-VERSION) at the given FILE-pointer fp,
975  * and return the SFT-header, SFT-version-number and number of frequency-samples in the SFT.
976  *
977  * Sets the filepointer fp at the end of the header if successful, leaves it at
978  * initial position if not.
979  *
980  * RETURN 0 = OK, -1 on ERROR
981  *
982  * We do basic checking of compliance with the SFT-spec (<tt>LIGO-T04164-01-Z</tt>)
983  * as far as a single header is concerned.
984  *
985  * NOTE: fatal errors will produce a XLALPrintError() error-message, but
986  * non-fatal 'SFT-format'-errors will only output error-messages if lalDebugLevel > 0.
987  * --> this function can therefore be used to check if a given file actually contains SFTs
988  *
989  *
990  */
991 int
992 read_sft_header_from_fp( FILE *fp, SFTtype *header, UINT4 *version, UINT8 *crc64, UINT2 *SFTwindowspec, BOOLEAN *swapEndian, CHAR **SFTcomment, UINT4 *numBins )
993 {
995  UINT4 nsamples;
996  CHAR *comm = NULL;
997  UINT8 ref_crc = 0;
998  UINT8 header_crc;
999  UINT2 windowspec;
1000 
1001  UINT4 ver;
1002  BOOLEAN need_swap;
1003  long save_filepos;
1004 
1005  if ( !header || !version || !numBins || !fp ) {
1006  XLALPrintError( "\nERROR read_sft_header_from_fp(): called with NULL input\n\n" );
1007  return -1;
1008  }
1009  if ( SFTcomment && ( ( *SFTcomment ) != NULL ) ) {
1010  XLALPrintError( "\nERROR: Comment-string passed to read_sft_header_from_fp() is not empty!\n\n" );
1011  return -1;
1012  }
1013 
1014  /* store fileposition for restoring in case of failure */
1015  if ( ( save_filepos = ftell( fp ) ) == -1 ) {
1016  XLALPrintError( "\nftell() failed: %s\n\n", strerror( errno ) );
1017  return -1;
1018  }
1019 
1020  if ( read_SFTversion_from_fp( &ver, &need_swap, fp ) != 0 ) {
1021  return -1;
1022  }
1023 
1024  /* read this SFT-header */
1025  XLAL_INIT_MEM( head );
1026 
1027  if ( MIN_SFT_VERSION <= ver && ver <= MAX_SFT_VERSION ) {
1028  if ( read_header_from_fp( fp, &head, &nsamples, &header_crc, &ref_crc, &windowspec, &comm, need_swap ) != 0 ) {
1029  goto failed;
1030  }
1031  } else {
1032  XLALPrintError( "\nUnsupported SFT-version %d.\n\n", ver );
1033  goto failed;
1034  } /* switch(ver) */
1035 
1036 
1037  /* ----- some general SFT-header consistency-checks */
1038  if ( ( head.epoch.gpsSeconds < 0 ) || ( head.epoch.gpsNanoSeconds < 0 ) || ( head.epoch.gpsNanoSeconds >= 1000000000 ) ) {
1039  XLALPrintError( "\nInvalid GPS-epoch in SFT : [%d, %d]!\n\n",
1040  head.epoch.gpsSeconds, head.epoch.gpsNanoSeconds );
1041  goto failed;
1042  }
1043 
1044  if ( head.deltaF <= 0 ) {
1045  XLALPrintError( "\nNegative frequency-spacing in SFT!\n\n" );
1046  goto failed;
1047  }
1048 
1049  if ( head.f0 < 0 ) {
1050  XLALPrintError( "\nNegative start-frequency in SFT!\n\n" );
1051  goto failed;
1052  }
1053 
1054  /* ok */
1055  ( *header ) = head;
1056  ( *version ) = ver;
1057 
1058  if ( SFTcomment ) { /* return of comment is optional */
1059  ( *SFTcomment ) = comm;
1060  } else if ( comm ) {
1061  LALFree( comm );
1062  }
1063 
1064  ( *swapEndian ) = need_swap;
1065  ( *crc64 ) = ref_crc;
1066  ( *numBins ) = nsamples;
1067 
1068  if ( SFTwindowspec ) { /* return of windowspec is optional */
1069  ( *SFTwindowspec ) = windowspec;
1070  }
1071 
1072  return 0;
1073 
1074  /* ---------- */
1075 failed:
1076  /* restore filepointer initial position */
1077  if ( fseek( fp, save_filepos, SEEK_SET ) == -1 ) {
1078  XLALPrintError( "\nfseek() failed to return to intial fileposition: %s\n\n", strerror( errno ) );
1079  }
1080 
1081  /* free comment if we allocated one */
1082  if ( comm ) {
1083  LALFree( comm );
1084  }
1085 
1086  return -1;
1087 
1088 } /* read_sft_header_from_fp() */
1089 
1090 
1091 /* ----- SFT header-reading function:
1092  *
1093  * return general SFTtype header, place filepointer at the end of the header if it succeeds,
1094  * set fp to initial position if it fails.
1095  * RETURN: 0 = OK, -1 = ERROR
1096  *
1097  * NOTE: fatal errors will produce a XLALPrintError() error-message, but
1098  * non-fatal 'SFT-format'-errors will only output error-messages if lalDebugLevel > 0.
1099  *
1100  */
1101 int
1102 read_header_from_fp( FILE *fp, SFTtype *header, UINT4 *nsamples, UINT8 *header_crc64, UINT8 *ref_crc64, UINT2 *SFTwindowspec, CHAR **SFTcomment, BOOLEAN swapEndian )
1103 {
1104  _SFT_header_t rawheader;
1105  long save_filepos;
1106  CHAR *comm = NULL;
1107  UINT8 crc;
1108 
1109 
1110  /* check input-consistency */
1111  if ( !fp || !header || !nsamples || !SFTcomment ) {
1112  XLALPrintError( "\nERROR read_header_from_fp(): called with NULL input!\n\n" );
1113  return -1;
1114  }
1115  if ( SFTcomment && ( *SFTcomment != NULL ) ) {
1116  XLALPrintError( "\nERROR: Comment-string passed to read_header_from_fp() is not NULL!\n\n" );
1117  return -1;
1118  }
1119 
1120  /* store fileposition for restoring in case of failure */
1121  if ( ( save_filepos = ftell( fp ) ) == -1 ) {
1122  XLALPrintError( "\nERROR: ftell() failed: %s\n\n", strerror( errno ) );
1123  return -1;
1124  }
1125 
1126  /* read the whole header */
1127  if ( fread( &rawheader, sizeof( rawheader ), 1, fp ) != 1 ) {
1128  if ( lalDebugLevel ) {
1129  XLALPrintError( "\nCould not read header. %s\n\n", strerror( errno ) );
1130  }
1131  goto failed;
1132  }
1133 
1134  /* ----- compute CRC for the header:
1135  * NOTE: the CRC checksum is computed on the *bytes*, not the numbers,
1136  * so this must be computed before any endian-swapping.
1137  */
1138  {
1139  UINT8 save_crc = rawheader.crc64;
1140  rawheader.crc64 = 0;
1141 
1142  crc = crc64( ( const unsigned char * )&rawheader, sizeof( rawheader ), ~( 0ULL ) );
1143 
1144  rawheader.crc64 = save_crc;
1145  /* NOTE: we're not done with crc yet, because we also need to
1146  * include the comment's CRC , see below
1147  */
1148  }/* compute crc64 checksum */
1149 
1150  /* ----- swap endian-ness if required ----- */
1151  if ( swapEndian ) {
1152  endian_swap( ( CHAR * )( &rawheader.version ), sizeof( rawheader.version ), 1 );
1153  endian_swap( ( CHAR * )( &rawheader.gps_sec ), sizeof( rawheader.gps_sec ), 1 );
1154  endian_swap( ( CHAR * )( &rawheader.gps_nsec ), sizeof( rawheader.gps_nsec ), 1 );
1155  endian_swap( ( CHAR * )( &rawheader.tbase ), sizeof( rawheader.tbase ), 1 );
1156  endian_swap( ( CHAR * )( &rawheader.first_frequency_index ), sizeof( rawheader.first_frequency_index ), 1 );
1157  endian_swap( ( CHAR * )( &rawheader.nsamples ), sizeof( rawheader.nsamples ), 1 );
1158  endian_swap( ( CHAR * )( &rawheader.crc64 ), sizeof( rawheader.crc64 ), 1 );
1159  endian_swap( ( CHAR * )( &rawheader.windowspec ), sizeof( rawheader.windowspec ), 1 );
1160  endian_swap( ( CHAR * )( &rawheader.comment_length ), sizeof( rawheader.comment_length ), 1 );
1161  /* ----- */
1162 
1163  } /* if endian_swap */
1164 
1165  /* double-check version-number */
1166  if ( !( MIN_SFT_VERSION <= rawheader.version && rawheader.version <= MAX_SFT_VERSION ) ) {
1167  XLALPrintError( "\nWrong SFT-version %g in read_header_from_fp()\n\n", rawheader.version );
1168  goto failed;
1169  }
1170 
1171  if ( rawheader.nsamples <= 0 ) {
1172  XLALPrintError( "\nNon-positive number of samples in SFT!\n\n" );
1173  goto failed;
1174  }
1175 
1176  if ( rawheader.comment_length < 0 ) {
1177  XLALPrintError( "\nNegative comment-length in SFT!\n\n" );
1178  goto failed;
1179  }
1180 
1181  if ( rawheader.comment_length % 8 != 0 ) {
1182  XLALPrintError( "\nComment-length must be multiple of 8 bytes!\n\n" );
1183  goto failed;
1184  }
1185 
1186  const CHAR detector[3] = { rawheader.detector[0], rawheader.detector[1], 0 };
1187  if ( ! XLALIsValidCWDetector( detector ) ) {
1188  XLALPrintError( "\nIllegal detector-name in SFT: '%s'\n\n", detector );
1189  goto failed;
1190  }
1191 
1192  if ( rawheader.version == 2 ) {
1193  rawheader.windowspec = 0; /* window of version 2 SFTs is unknown */
1194  }
1195 
1196  /* ----- Now read comment (if any) ----- */
1197  comm = NULL;
1198  if ( rawheader.comment_length ) {
1199  CHAR *ptr;
1200  if ( ( comm = LALCalloc( 1, rawheader.comment_length ) ) == NULL ) {
1201  XLALPrintError( "\nFATAL: out of memory ...\n\n" );
1202  goto failed;
1203  }
1204  if ( ( size_t )rawheader.comment_length != fread( comm, 1, rawheader.comment_length, fp ) ) {
1205  XLALPrintError( "\nCould not read %d-bytes comment\n\n", rawheader.comment_length );
1206  goto failed;
1207  }
1208 
1209  /* check that comment is 0-terminated */
1210  if ( comm[ rawheader.comment_length - 1] != 0 ) {
1211  XLALPrintError( "\nComment is not properly 0-terminated!\n\n" );
1212  goto failed;
1213  }
1214 
1215  /* check that no NON-NULL bytes after first NULL in comment (->spec) */
1216  ptr = strchr( comm, 0 ); /* guaranteed to find sth, after previous check */
1217  while ( ptr < ( comm + rawheader.comment_length - 1 ) )
1218  if ( *ptr++ != 0 ) {
1219  XLALPrintError( "\nNon-NULL bytes found after comment-end!\n\n" );
1220  goto failed;
1221  }
1222 
1223  /* comment length including null terminator to string must be an
1224  * integer multiple of eight bytes. comment==NULL means 'no
1225  * comment'
1226  */
1227  if ( SFTcomment ) {
1228  CHAR pad[] = {0, 0, 0, 0, 0, 0, 0}; /* for comment-padding */
1229  UINT4 comment_len = strlen( comm ) + 1;
1230  UINT4 pad_len = ( 8 - ( comment_len % 8 ) ) % 8;
1231 
1232  crc = crc64( ( const unsigned char * )comm, comment_len, crc );
1233  crc = crc64( ( const unsigned char * )pad, pad_len, crc );
1234  }
1235 
1236  } /* if comment_length > 0 */
1237 
1238  /* ok: */
1239  memset( header, 0, sizeof( *header ) );
1240 
1241  strncpy( header->name, detector, sizeof( header->name ) );
1242 
1243  header->epoch.gpsSeconds = rawheader.gps_sec;
1244  header->epoch.gpsNanoSeconds = rawheader.gps_nsec;
1245 
1246  header->f0 = rawheader.first_frequency_index / rawheader.tbase;
1247  header->deltaF = 1.0 / rawheader.tbase;
1248 
1249  ( *nsamples ) = rawheader.nsamples;
1250  ( *ref_crc64 ) = rawheader.crc64;
1251  ( *SFTcomment ) = comm;
1252  ( *header_crc64 ) = crc;
1253  ( *SFTwindowspec ) = rawheader.windowspec;
1254 
1255  return 0;
1256 
1257 failed:
1258  /* restore filepointer initial position */
1259  if ( fseek( fp, save_filepos, SEEK_SET ) == -1 ) {
1260  XLALPrintError( "\nfseek() failed to return to intial fileposition: %s\n\n", strerror( errno ) );
1261  }
1262 
1263  /* free comment if we allocated one */
1264  if ( comm ) {
1265  LALFree( comm );
1266  }
1267 
1268  return -1;
1269 
1270 } /* read_header_from_fp() */
1271 
1272 
1273 /*
1274  This function reads an SFT (segment) from an open file pointer into a buffer.
1275  firstBin2read specifies the first bin to read from the SFT, lastBin2read is the last bin.
1276  If the SFT contains fewer bins than specified, all bins from the SFT are read.
1277  The function returns the last bin actually read, firstBinRead
1278  is set to the first bin actually read. In case of an error, 0 is returned
1279  and firstBinRead is set to a code further decribing the error condition.
1280 */
1281 UINT4
1282 read_sft_bins_from_fp( SFTtype *ret, UINT4 *firstBinRead, UINT4 firstBin2read, UINT4 lastBin2read, FILE *fp )
1283 {
1284  UINT4 version;
1285  UINT8 crc64;
1286  BOOLEAN swapEndian;
1287  UINT4 numBins2read;
1288  UINT4 firstSFTbin, lastSFTbin, numSFTbins;
1289  INT4 offsetBins;
1290  long offsetBytes;
1291  volatile REAL8 tmp; /* intermediate results: try to force IEEE-arithmetic */
1292 
1293  if ( !firstBinRead ) {
1294  XLALPrintError( "read_sft_bins_from_fp(): got passed NULL *firstBinRead\n" );
1295  return ( ( UINT4 ) - 1 );
1296  }
1297 
1298  *firstBinRead = 0;
1299 
1300  if ( ( ret == NULL ) ||
1301  ( ret->data == NULL ) ||
1302  ( ret->data->data == NULL ) ) {
1303  XLALPrintError( "read_sft_bins_from_fp(): got passed NULL SFT*\n" );
1304  *firstBinRead = 1;
1305  return ( 0 );
1306  }
1307 
1308  if ( !fp ) {
1309  XLALPrintError( "read_sft_bins_from_fp(): got passed NULL FILE*\n" );
1310  *firstBinRead = 1;
1311  return ( 0 );
1312  }
1313 
1314  if ( firstBin2read > lastBin2read ) {
1315  XLALPrintError( "read_sft_bins_from_fp(): Empty frequency-interval requested [%d, %d] bins\n",
1316  firstBin2read, lastBin2read );
1317  *firstBinRead = 1;
1318  return ( 0 );
1319  }
1320 
1321  {
1322  COMPLEX8Sequence *data = ret->data;
1323  if ( read_sft_header_from_fp( fp, ret, &version, &crc64, NULL, &swapEndian, NULL, &numSFTbins ) != 0 ) {
1324  XLALPrintError( "read_sft_bins_from_fp(): Failed to read SFT-header!\n" );
1325  *firstBinRead = 2;
1326  return ( 0 );
1327  }
1328  ret->data = data;
1329  }
1330 
1331  tmp = ret->f0 / ret->deltaF;
1332  firstSFTbin = lround( tmp );
1333  lastSFTbin = firstSFTbin + numSFTbins - 1;
1334 
1335  /* limit the interval to be read to what's actually in the SFT */
1336  if ( firstBin2read < firstSFTbin ) {
1337  firstBin2read = firstSFTbin;
1338  }
1339  if ( lastBin2read > lastSFTbin ) {
1340  lastBin2read = lastSFTbin;
1341  }
1342 
1343  /* check that requested interval is found in SFT */
1344  /* return 0 (no bins read) if this isn't the case */
1345  if ( firstBin2read > lastBin2read ) {
1346  *firstBinRead = 0;
1347  return ( 0 );
1348  }
1349 
1350  *firstBinRead = firstBin2read;
1351 
1352  offsetBins = firstBin2read - firstSFTbin;
1353  offsetBytes = offsetBins * 2 * sizeof( REAL4 );
1354  numBins2read = lastBin2read - firstBin2read + 1;
1355 
1356  if ( ret->data->length < numBins2read ) {
1357  XLALPrintError( "read_sft_bins_from_fp(): passed SFT has not enough bins (%u/%u)\n",
1358  ret->data->length, numBins2read );
1359  *firstBinRead = 1;
1360  return ( 0 );
1361  }
1362 
1363  /* seek to the desired bins */
1364  if ( fseek( fp, offsetBytes, SEEK_CUR ) != 0 ) {
1365  XLALPrintError( "read_sft_bins_from_fp(): Failed to fseek() to first frequency-bin %d: %s\n",
1366  firstBin2read, strerror( errno ) );
1367  *firstBinRead = 3;
1368  return ( 0 );
1369  }
1370 
1371  /* actually read the data */
1372  if ( numBins2read != fread( ret->data->data, sizeof( COMPLEX8 ), numBins2read, fp ) ) {
1373  XLALPrintError( "read_sft_bins_from_fp(): Failed to read %d bins from SFT!\n", numBins2read );
1374  *firstBinRead = 4;
1375  return ( 0 );
1376  }
1377 
1378  /* update the start-frequency entry in the SFT-header to the new value */
1379  ret->f0 = 1.0 * firstBin2read * ret->deltaF;
1380 
1381  /* take care of normalization and endian-swapping */
1382  if ( swapEndian ) {
1383  UINT4 i;
1384 
1385  for ( i = 0; i < numBins2read; i ++ ) {
1386  REAL4 re = crealf( ret->data->data[i] );
1387  REAL4 im = cimagf( ret->data->data[i] );
1388 
1389  if ( swapEndian ) {
1390  endian_swap( ( CHAR * ) &re, sizeof( re ), 1 );
1391  endian_swap( ( CHAR * ) &im, sizeof( im ), 1 );
1392  }
1393 
1394  ret->data->data[i] = crectf( re, im );
1395  } /* for i < numBins2read */
1396  } /* swapEndian */
1397 
1398  /* return last bin read */
1399  return ( lastBin2read );
1400 
1401 } /* read_sft_bins_from_fp() */
1402 
1403 
1404 /**
1405  * Check the SFT-block starting at fp for valid crc64 checksum.
1406  * Restores filepointer before leaving.
1407  */
1408 BOOLEAN
1410 {
1411  long save_filepos;
1412  UINT8 computed_crc, ref_crc;
1413  SFTtype header;
1414  UINT4 numBins;
1415  UINT2 windowspec;
1416  CHAR *SFTcomment = NULL;
1417  UINT4 data_len;
1418  char block[BLOCKSIZE];
1419  UINT4 version;
1420  BOOLEAN need_swap;
1421 
1422  /* input consistency */
1423  if ( !fp ) {
1424  XLALPrintError( "\nhas_valid_crc64() was called with NULL filepointer!\n\n" );
1425  return FALSE;
1426  }
1427 
1428  /* store fileposition for restoring in case of failure */
1429  if ( ( save_filepos = ftell( fp ) ) == -1 ) {
1430  XLALPrintError( "\nERROR: ftell() failed: %s\n\n", strerror( errno ) );
1431  return -1;
1432  }
1433 
1434  if ( read_SFTversion_from_fp( &version, &need_swap, fp ) != 0 ) {
1435  return -1;
1436  }
1437 
1438  if ( !( MIN_SFT_VERSION <= version && version <= MAX_SFT_VERSION ) ) {
1439  XLALPrintError( "\nhas_valid_crc64() was called on an invalid version(=%u) SFT.\n\n", version );
1440  return -1;
1441  }
1442 
1443  /* ----- compute CRC ----- */
1444  /* read the header, unswapped, only to obtain it's crc64 checksum */
1445  if ( read_header_from_fp( fp, &header, &numBins, &computed_crc, &ref_crc, &windowspec, &SFTcomment, need_swap ) != 0 ) {
1446  if ( SFTcomment ) {
1447  LALFree( SFTcomment );
1448  }
1449  return FALSE;
1450  }
1451  if ( SFTcomment ) {
1452  LALFree( SFTcomment );
1453  }
1454 
1455  /* read data in blocks of BLOCKSIZE, computing CRC */
1456  data_len = numBins * 8 ; /* COMPLEX8 data */
1457  while ( data_len > 0 ) {
1458  /* read either BLOCKSIZE or amount remaining */
1459  int toread = ( BLOCKSIZE < data_len ) ? BLOCKSIZE : data_len;
1460  if ( toread != ( int )fread( block, 1, toread, fp ) ) {
1461  XLALPrintError( "\nFailed to read all frequency-bins from SFT.\n\n" );
1462  return FALSE;
1463  }
1464  data_len -= toread;
1465 
1466  /* compute CRC64: don't endian-swap for that! */
1467  computed_crc = crc64( ( const unsigned char * )block, toread, computed_crc );
1468 
1469  } /* while data */
1470 
1471  /* check that checksum is consistent */
1472  return ( computed_crc == ref_crc );
1473 
1474 } /* has_valid_crc64 */
1475 
1476 /// @}
#define __func__
log an I/O error, i.e.
#define GPS2REAL8(gps)
convert GPS-time to REAL8
int j
ProcessParamsTable * ptr
int k
#define LALCalloc(m, n)
#define LALFree(p)
#define XLALLOADSFTSERROR(eno)
#define BLOCKSIZE
blocksize used in SFT-reading for the CRC-checksum computation (has to be multiple of 8 !...
Definition: SFTfileIO.c:34
Internal SFT types and functions.
#define GPSZERO(gps)
Definition: SFTinternal.h:81
unsigned long long crc64(const unsigned char *data, unsigned int length, unsigned long long crc)
#define MAX_SFT_VERSION
Definition: SFTinternal.h:54
#define GPSEQUAL(gps1, gps2)
Definition: SFTinternal.h:80
#define MIN_SFT_VERSION
Definition: SFTinternal.h:53
unsigned char BOOLEAN
#define XLAL_INIT_MEM(x)
uint64_t UINT8
double REAL8
#define XLAL_INIT_DECL(var,...)
#define crectf(re, im)
uint16_t UINT2
char CHAR
uint32_t UINT4
float complex COMPLEX8
int32_t INT4
float REAL4
#define lalDebugLevel
void * XLALCalloc(size_t m, size_t n)
void * XLALMalloc(size_t n)
void XLALFree(void *p)
const char * SFTErrorMessage(int errorcode)
int ValidateSFTFile(const char *fname)
Verify that the contents of a SFT file are valid.
SFTtype * XLALCreateSFT(UINT4 numBins)
XLAL function to create one SFT-struct.
Definition: SFTtypes.c:152
MultiSFTVector * XLALLoadMultiSFTs(const SFTCatalog *inputCatalog, REAL8 fMin, REAL8 fMax)
Function to load a catalog of SFTs from possibly different detectors.
Definition: SFTfileIO.c:416
void XLALDestroyMultiSFTCatalogView(MultiSFTCatalogView *multiView)
Destroys a MultiSFTCatalogView, without freeing the original catalog that the 'view' was referring to...
Definition: SFTcatalog.c:496
UINT4 read_sft_bins_from_fp(SFTtype *ret, UINT4 *firstBinRead, UINT4 firstBin2read, UINT4 lastBin2read, FILE *fp)
Definition: SFTfileIO.c:1282
void XLALDestroyMultiSFTVector(MultiSFTVector *multvect)
Destroy a multi SFT-vector.
Definition: SFTtypes.c:424
static int read_header_from_fp(FILE *fp, SFTtype *header, UINT4 *nsamples, UINT8 *header_crc64, UINT8 *ref_crc64, UINT2 *SFTwindowspec, CHAR **SFTcomment, BOOLEAN swapEndian)
Definition: SFTfileIO.c:1102
MultiSFTCatalogView * XLALGetMultiSFTCatalogView(const SFTCatalog *catalog)
Return a MultiSFTCatalogView generated from an input SFTCatalog.
Definition: SFTcatalog.c:380
REAL8 TSFTfromDFreq(REAL8 dFreq)
Definition: SFTtypes.c:1144
void endian_swap(CHAR *pdata, size_t dsize, size_t nelements)
Definition: SFTfileIO.c:844
int build_sft_windowspec(UINT2 *windowspec, CHAR(*windowspec_str)[9], const char *window_type, REAL8 window_param)
Build an SFT 2-byte 'windowspec' or filename field 'windowspec_str' for the window given by 'window_t...
Definition: SFTnaming.c:691
int XLALWriteSFT2FilePointer(const SFTtype *sft, FILE *fp, const CHAR *SFTwindowtype, const REAL8 SFTwindowparam, const CHAR *SFTcomment)
Write the given SFTtype to a FILE pointer.
Definition: SFTfileIO.c:488
MultiSFTVector * XLALLoadMultiSFTsFromView(const MultiSFTCatalogView *multiCatalogView, REAL8 fMin, REAL8 fMax)
This function loads a MultiSFTVector from a given input MultiSFTCatalogView, otherwise the documentat...
Definition: SFTfileIO.c:449
char * XLALBuildSFTFilenameFromSpec(const SFTFilenameSpec *spec)
Build an SFT file name from the given specification.
Definition: SFTnaming.c:302
UINT4 XLALRoundFrequencyUpToSFTBin(const REAL8 freq, const REAL8 df)
Round a REAL8 frequency up to the nearest integer SFT bin number.
Definition: SFTtypes.c:82
SFTVector * XLALLoadSFTs(const SFTCatalog *catalog, REAL8 fMin, REAL8 fMax)
Load the given frequency-band [fMin, fMax) (half-open) from the SFT-files listed in the SFT-'catalogu...
Definition: SFTfileIO.c:87
int read_sft_header_from_fp(FILE *fp, SFTtype *header, UINT4 *version, UINT8 *crc64, UINT2 *SFTwindowspec, BOOLEAN *swapEndian, CHAR **SFTcomment, UINT4 *numBins)
Definition: SFTfileIO.c:992
UINT4 XLALRoundFrequencyDownToSFTBin(const REAL8 freq, const REAL8 df)
Round a REAL8 frequency down to the nearest integer SFT bin number.
Definition: SFTtypes.c:70
void XLALDestroySFT(SFTtype *sft)
Destructor for one SFT.
Definition: SFTtypes.c:176
FILE * fopen_SFTLocator(const struct tagSFTLocator *locator)
Open an "SFT" defined by the SFT-locator, return a FILE-pointer to the beginning of this SFT.
Definition: SFTfileIO.c:882
int XLALWriteSFTVector2NamedFile(const SFTVector *sftVect, const CHAR *SFTfilename, const CHAR *SFTwindowtype, const REAL8 SFTwindowparam, const CHAR *SFTcomment)
Write the given SFTVector to a single merged SFT file with the supplied filename.
Definition: SFTfileIO.c:712
int XLALWriteSFTVector2StandardFile(const SFTVector *sftVect, SFTFilenameSpec *SFTfnspec, const CHAR *SFTcomment, const BOOLEAN merged)
Write the given SFTVector to SFT file(s) with a standard () filename(s).
Definition: SFTfileIO.c:755
int XLALWriteSFT2StandardFile(const SFTtype *sft, SFTFilenameSpec *SFTfnspec, const CHAR *SFTcomment)
Write the given SFTtype to a SFT file with a standard () filename.
Definition: SFTfileIO.c:656
int compareSFTloc(const void *ptr1, const void *ptr2)
Definition: SFTtypes.c:1182
int XLALWriteSFT2NamedFile(const SFTtype *sft, const CHAR *SFTfilename, const CHAR *SFTwindowtype, const REAL8 SFTwindowparam, const CHAR *SFTcomment)
Write the given SFTtype to a SFT file with the supplied filename.
Definition: SFTfileIO.c:598
BOOLEAN has_valid_crc64(FILE *fp)
Check the SFT-block starting at fp for valid crc64 checksum.
Definition: SFTfileIO.c:1409
SFTVector * XLALCreateSFTVector(UINT4 numSFTs, UINT4 numBins)
XLAL function to create an SFTVector of numSFT SFTs with SFTlen frequency-bins (which will be allocat...
Definition: SFTtypes.c:230
int XLALCheckSFTFileIsValid(const char *fname)
Verify that the contents of a SFT file are valid.
Definition: SFTfileIO.c:834
int read_SFTversion_from_fp(UINT4 *version, BOOLEAN *need_swap, FILE *fp)
Read valid SFT version-number at position fp, and determine if we need to endian-swap the data.
Definition: SFTfileIO.c:916
BOOLEAN XLALIsValidCWDetector(const CHAR *name)
Determine if 'name' is a valid detector name or prefix.
Definition: SFTnaming.c:188
#define XLAL_ERROR_NULL(...)
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_CHECK_NULL(assertion,...)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EIO
XLAL_EINVAL
head
size
int deltaF
merged
#define TRUE
#define FALSE
REAL8 version
Definition: SFTfileIO.c:39
INT4 comment_length
Definition: SFTfileIO.c:48
INT4 nsamples
Definition: SFTfileIO.c:44
UINT8 crc64
Definition: SFTfileIO.c:45
CHAR detector[2]
Definition: SFTfileIO.c:46
INT4 gps_nsec
Definition: SFTfileIO.c:41
INT4 first_frequency_index
Definition: SFTfileIO.c:43
UINT2 windowspec
Definition: SFTfileIO.c:47
INT4 gps_sec
Definition: SFTfileIO.c:40
REAL8 tbase
Definition: SFTfileIO.c:42
CHAR name[LALNameLength]
COMPLEX8Sequence * data
A vector of COMPLEX8FrequencySeries.
COMPLEX8FrequencySeries * data
Pointer to the data array.
UINT4 length
Number of elements in array.
COMPLEX8 * data
INT4 gpsNanoSeconds
A multi-SFT-catalogue "view": a multi-IFO vector of SFT-catalogs.
Definition: SFTfileIO.h:255
SFTCatalog * data
array of SFT-catalog pointers
Definition: SFTfileIO.h:260
UINT4 length
number of detectors
Definition: SFTfileIO.h:259
A collection of SFT vectors – one for each IFO in a multi-IFO search.
Definition: SFTfileIO.h:179
An "SFT-catalogue": a vector of SFTdescriptors, as returned by XLALSFTdataFind()
Definition: SFTfileIO.h:238
SFTDescriptor * data
array of data-entries describing matched SFTs
Definition: SFTfileIO.h:243
UINT4 length
number of SFTs in catalog
Definition: SFTfileIO.h:242
SFTtype header
SFT-header info.
Definition: SFTfileIO.h:228
struct tagSFTLocator * locator
internal description of where to find this SFT [opaque!]
Definition: SFTfileIO.h:227
UINT4 numBins
number of frequency-bins in this SFT
Definition: SFTfileIO.h:232
Structure specifying an SFT file name, following the convention in .
Definition: SFTfileIO.h:266
UINT4 SFTtimebase
Timebase in seconds of the SFT; set by XLALWriteSFT[Vector]2StandardFile()
Definition: SFTfileIO.h:271
UINT4 SFTspan
Total time interval in seconds covered by SFT file; set by XLALWriteSFT[Vector]2StandardFile()
Definition: SFTfileIO.h:275
UINT4 gpsStart
GPS time in seconds at the beginning of the first SFT in the file; set by XLALWriteSFT[Vector]2Standa...
Definition: SFTfileIO.h:274
CHAR detector[3]
2-character detector prefix (e.g.
Definition: SFTfileIO.h:270
UINT4 numSFTs
Number of SFTs in the file; set by XLALWriteSFT[Vector]2StandardFile()
Definition: SFTfileIO.h:269
CHAR window_type[32]
window function applied to SFT
Definition: SFTfileIO.h:272
REAL8 window_param
parameter of window function, if required
Definition: SFTfileIO.h:273
segments read so far from one SFT
Definition: SFTfileIO.c:52
UINT4 last
last bin in this segment
Definition: SFTfileIO.c:54
struct tagSFTLocator * lastfrom
last bin read from this locator
Definition: SFTfileIO.c:56
LIGOTimeGPS epoch
timestamp of this SFT
Definition: SFTfileIO.c:55
UINT4 first
first bin in this segment
Definition: SFTfileIO.c:53
CHAR * fname
Definition: SFTinternal.h:90
enum @1 epoch
double pad