/** * @brief Seeks a LALFrStream stream by a time offset * @details * The position of a LALFrStream is set so that the net read will * be at the specified time offset. The offset @p dt is a number of * seconds relative to the @p whence postion, which can be * @p SEEK_SET to seek relative to the beginning of the stream, * @p SEEK_CUR to seek relative to the current position of the stream, * or @p SEEK_END to seek relative to the end of the stream. * The return codes and conditions are the same as XLALFrStreamSeek(). * @param stream Pointer to a #LALFrStream structure. * @param dt The offset time in seconds. * @param whence The position whence to seek: one of @p SEEK_SET, @p SEEK_CUR, * or @p SEEK_END. * @retval 3 Time requested is in a gap in the data. * @retval 2 Time requested is after the end of the stream. * @retval 1 Time requested is before the beginning of the stream. * @retval 0 Normal success. * @retval <0 Failure. */ int XLALFrStreamSeekO(LALFrStream * stream, double dt, int whence) { LIGOTimeGPS epoch; switch (whence) { case SEEK_SET: if (XLALFrStreamRewind(stream) < 0) XLAL_ERROR(XLAL_EFUNC); /* FALL THROUGH */ case SEEK_CUR: epoch = stream->epoch; break; case SEEK_END: /* go to the last frame */ XLALFrStreamFileClose(stream); if (XLALFrStreamFileOpen(stream, stream->cache->length - 1) < 0) XLAL_ERROR(XLAL_EFUNC); if ((stream->pos = XLALFrFileQueryNFrame(stream->file) - 1) < 0) XLAL_ERROR(XLAL_EFUNC); if (XLALFrFileQueryGTime(&epoch, stream->file, stream->pos) == NULL) XLAL_ERROR(XLAL_EFUNC); /* add duration of last frame to dt */ dt += XLALFrFileQueryDt(stream->file, stream->pos); break; default: XLAL_ERROR(XLAL_EINVAL, "Invalid whence value: use SEEK_SET, SEEK_CUR, or SEEK_END"); } XLALGPSAdd(&epoch, dt); if (XLALFrStreamSeek(stream, &epoch) < 0) XLAL_ERROR(XLAL_EFUNC); return 0; }
static int XLALFrStreamFileOpen(LALFrStream * stream, UINT4 fnum) { if (!stream->cache || !stream->cache->list) XLAL_ERROR(XLAL_EINVAL, "No files in stream file cache"); if (fnum >= stream->cache->length) XLAL_ERROR(XLAL_EINVAL, "File index too large"); if (stream->file) XLALFrStreamFileClose(stream); stream->pos = 0; stream->fnum = fnum; stream->file = XLALFrFileOpenURL(stream->cache->list[fnum].url); if (!stream->file) { stream->state |= LAL_FR_STREAM_ERR | LAL_FR_STREAM_URL; XLAL_ERROR(XLAL_EFUNC); } if (stream->mode & LAL_FR_STREAM_CHECKSUM_MODE) { if (!XLALFrFileCksumValid(stream->file)) { stream->state |= LAL_FR_STREAM_ERR; XLALFrStreamFileClose(stream); XLAL_ERROR(XLAL_EIO, "Invalid checksum in file %s", stream->cache->list[fnum].url); } } XLALFrFileQueryGTime(&stream->epoch, stream->file, 0); return 0; }
/** * @brief Opens a LALFrStream associated with a LALCache * @details * This routine creates a #LALFrStream that is a stream associated with * the frame files contained in a LALCache. * @param cache Pointer to a LALCache structure describing the frame files to stream. * @returns Pointer to a newly created #LALFrStream structure. * @retval NULL Failure. */ LALFrStream *XLALFrStreamCacheOpen(LALCache * cache) { LALFrStream *stream; size_t i; if (!cache) XLAL_ERROR_NULL(XLAL_EFAULT); stream = LALCalloc(1, sizeof(*stream)); if (!stream) XLAL_ERROR_NULL(XLAL_ENOMEM); stream->cache = XLALCacheDuplicate(cache); /* check cache entries for t0 and dt; if these are not set then read * the framefile to try to get them */ for (i = 0; i < stream->cache->length; ++i) { if (stream->cache->list[i].t0 == 0 || stream->cache->list[i].dt == 0) { LIGOTimeGPS end; size_t nFrame; if (XLALFrStreamFileOpen(stream, i) < 0) { XLALFrStreamClose(stream); XLAL_ERROR_NULL(XLAL_EIO); } nFrame = XLALFrFileQueryNFrame(stream->file); stream->cache->list[i].t0 = stream->epoch.gpsSeconds; XLALFrFileQueryGTime(&end, stream->file, nFrame - 1); XLALGPSAdd(&end, XLALFrFileQueryDt(stream->file, nFrame - 1)); stream->cache->list[i].dt = ceil(XLALGPSGetREAL8(&end)) - stream->cache->list[i].t0; XLALFrStreamFileClose(stream); } } /* sort and uniqify the cache */ if (XLALCacheSort(stream->cache) || XLALCacheUniq(stream->cache)) { XLALFrStreamClose(stream); XLAL_ERROR_NULL(XLAL_EFUNC); } stream->mode = LAL_FR_STREAM_DEFAULT_MODE; /* open up the first file */ if (XLALFrStreamFileOpen(stream, 0) < 0) { XLALFrStreamClose(stream); XLAL_ERROR_NULL(XLAL_EFUNC); } return stream; }
int STREAMGETSERIES(STYPE * series, LALFrStream * stream) { const REAL8 fuzz = 0.1 / 16384.0; /* smallest discernable time */ const size_t size = sizeof(TYPE); size_t noff; size_t need; size_t ncpy; TYPE *dest; STYPE *buffer; LIGOTimeGPS tend; INT8 tnow; INT8 tbeg; int gap = 0; XLAL_CHECK(!(stream->state & LAL_FR_STREAM_END), XLAL_EIO); XLAL_CHECK(!(stream->state & LAL_FR_STREAM_ERR), XLAL_EIO); /* if series does not have allocation for data, * we are to return metadata only, so we don't * need to load data in the next call */ if (series->data && series->data->length) buffer = READSERIES(stream->file, series->name, stream->pos); else buffer = READSERIESMETA(stream->file, series->name, stream->pos); if (!buffer) XLAL_ERROR(XLAL_EFUNC); tnow = XLALGPSToINT8NS(&stream->epoch); tbeg = XLALGPSToINT8NS(&buffer->epoch); /* Make sure that we aren't requesting data * that comes before the current frame. * Allow 1 millisecond padding to account * for double precision */ if (tnow + 1000 < tbeg) { DESTROYSERIES(buffer); XLAL_ERROR(XLAL_ETIME); } /* compute number of points offset very carefully: * if current time is within fuzz of a sample, get * that sample; otherwise get the sample just after * the requested time */ noff = ceil((1e-9 * (tnow - tbeg) - fuzz) / buffer->deltaT); /* adjust current time to be exactly the first sample * (rounded to nearest nanosecond) */ tnow = tbeg + floor(1e9 * noff * buffer->deltaT + 0.5); XLALINT8NSToGPS(&series->epoch, tnow); series->deltaT = buffer->deltaT; series->sampleUnits = buffer->sampleUnits; /* end here if all you want is metadata */ if (!series->data || !series->data->length) { DESTROYSERIES(buffer); return 0; } /* the rest of this function is to get the required * amount of data and copy it into the series */ dest = series->data->data; /* pointer to where to put the data */ need = series->data->length; /* number of points that are needed */ if (noff > buffer->data->length) { /* invalid time offset */ DESTROYSERIES(buffer); XLAL_ERROR(XLAL_ETIME); } /* copy as much of the buffer is needed */ ncpy = (buffer->data->length - noff) < need ? buffer->data->length - noff : need; memcpy(dest, buffer->data->data + noff, ncpy * size); dest += ncpy; need -= ncpy; DESTROYSERIES(buffer); /* continue while data is required */ while (need) { /* goto next frame */ if (XLALFrStreamNext(stream) < 0) XLAL_ERROR(XLAL_EFUNC); if (stream->state & LAL_FR_STREAM_END) XLAL_ERROR(XLAL_EIO, "End of frame stream while %zd points remain to be read", need); /* load more data */ buffer = READSERIES(stream->file, series->name, stream->pos); if (!buffer) XLAL_ERROR(XLAL_EFUNC); if (stream->state & LAL_FR_STREAM_GAP) { /* gap in data: reset dest and need and set epoch */ dest = series->data->data; need = series->data->length; series->epoch = buffer->epoch; gap = 1; } /* copy data */ ncpy = buffer->data->length < need ? buffer->data->length : need; memcpy(dest, buffer->data->data, ncpy * size); dest += ncpy; need -= ncpy; DESTROYSERIES(buffer); } /* update stream start time so that it corresponds to the * exact time of the next sample to be read */ stream->epoch = series->epoch; XLALGPSAdd(&stream->epoch, series->data->length * series->deltaT); /* are we still within the current frame? */ XLALFrFileQueryGTime(&tend, stream->file, stream->pos); XLALGPSAdd(&tend, XLALFrFileQueryDt(stream->file, stream->pos)); if (XLALGPSCmp(&tend, &stream->epoch) <= 0) { /* advance a frame... note that failure here is * benign so we suppress gap warnings: these will * be triggered on the next read (if one is done) */ int savemode = stream->mode; LIGOTimeGPS saveepoch = stream->epoch; stream->mode |= LAL_FR_STREAM_IGNOREGAP_MODE; /* ignore gaps for now */ if (XLALFrStreamNext(stream) < 0) { stream->mode = savemode; XLAL_ERROR(XLAL_EFUNC); } if (!(stream->state & LAL_FR_STREAM_GAP)) /* no gap: reset epoch */ stream->epoch = saveepoch; stream->mode = savemode; } /* make sure to set the gap flag in the stream state * if a gap had been encountered during the reading */ if (gap) stream->state |= LAL_FR_STREAM_GAP; /* FIXME: * does this need to cause a failure if mode is set to fail on gaps? */ /* if the stream state is an error then fail */ if (stream->state & LAL_FR_STREAM_ERR) XLAL_ERROR(XLAL_EIO); return 0; }
/** * @brief Seeks a LALFrStream stream to data at a given time * @details * The position of a LALFrStream is set so that the net read will * be at the specified time. #LAL_FR_STREAM_END and #LAL_FR_STREAM_GAP * bits are turned off in the #LALFrStreamState state. If the time is before * the beginning of the stream, the stream position is set to the beginning of * the stream and the routine returns with code 1. If the time is after the * end of the stream, the #LAL_FR_STREAM_END bit is set in the * #LALFrStreamState state, and the routine returns with code 2. If the time * is in a gap in the data, the #LAL_FR_STREAM_GAP bit is set in the * #LALFrStreamState state, the position is advanced to the next data, and the * routine returns with code 3. If, however, the * #LAL_FR_STREAM_IGNORETIME_MODE bit is not set in the LALFrStreamMode mode * then these conditions result in an error. * @param stream Pointer to a #LALFrStream structure. * @param epoch The LIGOTimeGPS time of the next data to read. * @retval 3 Time requested is in a gap in the data. * @retval 2 Time requested is after the end of the stream. * @retval 1 Time requested is before the beginning of the stream. * @retval 0 Normal success. * @retval <0 Failure. */ int XLALFrStreamSeek(LALFrStream * stream, const LIGOTimeGPS * epoch) { double twant = XLALGPSGetREAL8(epoch); LALCacheEntry *entry; /* close file if one is open */ XLALFrStreamFileClose(stream); /* clear EOF or GAP states; preserve ERR state */ if (stream->state & LAL_FR_STREAM_ERR) stream->state = LAL_FR_STREAM_ERR; else stream->state = LAL_FR_STREAM_OK; /* is epoch before first file? */ if (epoch->gpsSeconds < stream->cache->list->t0) { XLALFrStreamRewind(stream); stream->state |= LAL_FR_STREAM_GAP; /* is this reported as an error? */ if (!(stream->mode & LAL_FR_STREAM_IGNORETIME_MODE)) { /* FIXME: if this is an error, should the stream state say so? */ /* stream->state |= LAL_FR_STREAM_ERR; */ XLAL_ERROR(XLAL_ETIME); } if (stream->mode & LAL_FR_STREAM_TIMEWARN_MODE) XLAL_PRINT_WARNING("Requested time %d before first frame", epoch->gpsSeconds); return 1; /* before first file code */ } /* seek for the time in the cache */ entry = XLALCacheEntrySeek(stream->cache, twant); if (!entry) { /* seek failed: only happens if time is past end of cache */ stream->fnum = stream->cache->length; stream->epoch = *epoch; stream->state |= LAL_FR_STREAM_END; /* is this reported as an error? */ if (!(stream->mode & LAL_FR_STREAM_IGNORETIME_MODE)) { /* FIXME: if this is an error, should the stream state say so? */ /* stream->state |= LAL_FR_STREAM_ERR; */ XLAL_ERROR(XLAL_ETIME); } if (stream->mode & LAL_FR_STREAM_TIMEWARN_MODE) XLAL_PRINT_WARNING("Requested time %d after last frame", epoch->gpsSeconds); return 2; /* after last file code */ } /* now we must find the position within the frame file */ for (stream->fnum = entry - stream->cache->list; stream->fnum < stream->cache->length; ++stream->fnum) { /* check the file contents to determine the position that matches */ size_t nFrame; if (XLALFrStreamFileOpen(stream, stream->fnum) < 0) XLAL_ERROR(XLAL_EFUNC); if (epoch->gpsSeconds < stream->cache->list[stream->fnum].t0) { /* detect a gap between files */ stream->state |= LAL_FR_STREAM_GAP; break; } nFrame = XLALFrFileQueryNFrame(stream->file); for (stream->pos = 0; stream->pos < (int)nFrame; ++stream->pos) { LIGOTimeGPS start; int cmp; XLALFrFileQueryGTime(&start, stream->file, stream->pos); cmp = XLALGPSCmp(epoch, &start); if (cmp >= 0 && XLALGPSDiff(epoch, &start) < XLALFrFileQueryDt(stream->file, stream->pos)) break; /* this is the frame! */ if (cmp < 0) { /* detect a gap between frames within a file */ stream->state |= LAL_FR_STREAM_GAP; break; } } if (stream->pos < (int)nFrame) /* we've found the frame */ break; /* oops... not in this frame file, go on to the next one */ /* probably the frame file was mis-named.... */ XLALFrStreamFileClose(stream); } if (stream->fnum >= stream->cache->length) { /* we've gone right to the end without finding it! */ stream->fnum = stream->cache->length; stream->epoch = *epoch; stream->state |= LAL_FR_STREAM_END; /* is this reported as an error? */ if (!(stream->mode & LAL_FR_STREAM_IGNORETIME_MODE)) { /* FIXME: if this is an error, should the stream state say so? */ /* stream->state |= LAL_FR_STREAM_ERR; */ XLAL_ERROR(XLAL_ETIME); } if (stream->mode & LAL_FR_STREAM_TIMEWARN_MODE) XLAL_PRINT_WARNING("Requested time %d after last frame", epoch->gpsSeconds); return 2; /* after last file code */ } /* set the time of the stream */ if (stream->state & LAL_FR_STREAM_GAP) { XLALFrFileQueryGTime(&stream->epoch, stream->file, stream->pos); if (stream->mode & LAL_FR_STREAM_TIMEWARN_MODE) XLAL_PRINT_WARNING("Requested time %.6f in gap in frame data", twant); if (!(stream->mode & LAL_FR_STREAM_IGNORETIME_MODE)) XLAL_ERROR(XLAL_ETIME); return 3; /* in a gap code */ } stream->epoch = *epoch; return 0; }
/** * @brief Advance a LALFrStream stream to the beginning of the next frame * @details * The position of a LALFrStream is advanced so that the next read will * be at the next frame. If the stream is at the end, the #LAL_FR_STREAM_END * bit of the LALFrStreamState state is set, and the routine returns the * return code 1. If there is a gap in the data before the next frame, * the #LAL_FR_STREAM_GAP bit of the LALFrStreamState state is set, and the * routine returns the return code 2. If, however, the * #LAL_FR_STREAM_IGNOREGAP_MODE bit is not set in the LALFrStreamMode mode * then the routine produces an error if a gap is encountered. * @param stream Pointer to a #LALFrStream structure. * @retval 2 Gap in the data is encountered. * @retval 1 End of stream encountered. * @retval 0 Normal success. * @retval <0 Failure. */ int XLALFrStreamNext(LALFrStream * stream) { /* timing accuracy: tenth of a sample interval for a 16kHz fast channel */ const INT8 tacc = (INT8) floor(0.1 * 1e9 / 16384.0); const char *url1; const char *url2; int pos1; int pos2; INT8 texp = 0; INT8 tact; if (stream->state & LAL_FR_STREAM_END) return 1; /* end code */ /* turn off gap bit */ stream->state &= ~LAL_FR_STREAM_GAP; url2 = url1 = stream->cache->list[stream->fnum].url; pos2 = pos1 = stream->pos; /* open a new file if necessary */ if (!stream->file) { if (stream->fnum >= stream->cache->length) { stream->state |= LAL_FR_STREAM_END; return 1; } if (XLALFrStreamFileOpen(stream, stream->fnum) < 0) XLAL_ERROR(XLAL_EFUNC); } if (stream->file) { INT4 nFrame = XLALFrFileQueryNFrame(stream->file); if (stream->pos < nFrame) { LIGOTimeGPS gpstime; XLALGPSToINT8NS(XLALFrFileQueryGTime(&gpstime, stream->file, stream->pos)); texp = XLALGPSToINT8NS(XLALGPSAdd(&gpstime, XLALFrFileQueryDt(stream->file, stream->pos))); ++stream->pos; } if (stream->pos >= nFrame) { XLALFrStreamFileClose(stream); ++stream->fnum; } pos2 = stream->pos; } /* open a new file if necessary */ if (!stream->file) { if (stream->fnum >= stream->cache->length) { stream->state |= LAL_FR_STREAM_END; return 1; } if (XLALFrStreamFileOpen(stream, stream->fnum) < 0) XLAL_ERROR(XLAL_EFUNC); url2 = stream->cache->list[stream->fnum].url; pos2 = stream->pos; } /* compute actual start time of this new frame */ tact = XLALGPSToINT8NS(XLALFrFileQueryGTime(&stream->epoch, stream->file, stream->pos)); /* INT8 is platform dependent, cast to long long for llabs() call */ if (llabs((long long)(texp - tact)) > tacc) { /* there is a gap */ stream->state |= LAL_FR_STREAM_GAP; if (stream->mode & LAL_FR_STREAM_GAPINFO_MODE) { XLAL_PRINT_INFO("Gap in frame data between times %.6f and %.6f", 1e-9 * texp, 1e-9 * tact); } if (!(stream->mode & LAL_FR_STREAM_IGNOREGAP_MODE)) { XLAL_PRINT_ERROR("Gap in frame data"); XLAL_PRINT_ERROR("Time %.6f is end of frame %d of file %s", 1e-9 * texp, pos1, url1); XLAL_PRINT_ERROR("Time %.6f is start of frame %d of file %s", 1e-9 * tact, pos2, url2); XLAL_ERROR(XLAL_ETIME); } return 2; /* gap code */ } return 0; }