/** * Unit-Test for function XLALSFTVectorToLFT(). * Generates random data (timeseries + corresponding SFTs), * then feeds the SFTs into XLALSFTVectorToLFT() * and checks correctness of output Fourier transform by * comparing to FT of original real-valued timeseries. * * \note This indirectly also checks XLALSFTVectorToCOMPLEX8TimeSeries() * which is used by XLALSFTVectorToLFT(). * * returns XLAL_SUCCESS on success, XLAL-error otherwise */ int test_XLALSFTVectorToLFT ( void ) { // ----- generate real-valued random timeseries and corresponding SFTs REAL4TimeSeries *tsR4 = NULL; SFTVector *sfts0 = NULL; XLAL_CHECK ( XLALgenerateRandomData ( &tsR4, &sfts0 ) == XLAL_SUCCESS, XLAL_EFUNC ); UINT4 numSamplesR4 = tsR4->data->length; REAL8 dt_R4 = tsR4->deltaT; REAL8 TspanR4 = numSamplesR4 * dt_R4; // ----- consider only the frequency band [3Hz, 4Hz] REAL8 out_fMin = 3; REAL8 out_Band = 1; SFTVector *sftsBand; XLAL_CHECK ( (sftsBand = XLALExtractBandFromSFTVector ( sfts0, out_fMin, out_Band )) != NULL, XLAL_EFUNC ); XLALDestroySFTVector ( sfts0 ); // ----- 1) compute FFT on original REAL4 timeseries ----- REAL4FFTPlan *planR4; XLAL_CHECK ( (planR4 = XLALCreateForwardREAL4FFTPlan ( numSamplesR4, 0 )) != NULL, XLAL_EFUNC ); SFTtype *lft0; XLAL_CHECK ( (lft0 = XLALCreateSFT ( numSamplesR4/2+1 )) != NULL, XLAL_EFUNC ); XLAL_CHECK ( XLALREAL4ForwardFFT ( lft0->data, tsR4->data, planR4 ) == XLAL_SUCCESS, XLAL_EFUNC ); strcpy ( lft0->name, tsR4->name ); lft0->f0 = 0; lft0->epoch = tsR4->epoch; REAL8 dfLFT = 1.0 / TspanR4; lft0->deltaF = dfLFT; // ----- extract frequency band of interest SFTtype *lftR4 = NULL; XLAL_CHECK ( XLALExtractBandFromSFT ( &lftR4, lft0, out_fMin, out_Band ) == XLAL_SUCCESS, XLAL_EFUNC ); for ( UINT4 k=0; k < lftR4->data->length; k ++ ) { lftR4->data->data[k] *= dt_R4; } // ----- 2) compute LFT directly from SFTs ---------- SFTtype *lftSFTs0; XLAL_CHECK ( (lftSFTs0 = XLALSFTVectorToLFT ( sftsBand, 1 )) != NULL, XLAL_EFUNC ); XLALDestroySFTVector ( sftsBand ); // ----- re-extract frequency band of interest SFTtype *lftSFTs = NULL; XLAL_CHECK ( XLALExtractBandFromSFT ( &lftSFTs, lftSFTs0, out_fMin, out_Band ) == XLAL_SUCCESS, XLAL_EFUNC ); if ( lalDebugLevel & LALINFO ) { // ----- write debug output XLAL_CHECK ( XLALdumpREAL4TimeSeries ( "TS_R4.dat", tsR4 ) == XLAL_SUCCESS, XLAL_EFUNC ); XLAL_CHECK ( write_SFTdata ("LFT_R4T.dat", lftR4 ) == XLAL_SUCCESS, XLAL_EFUNC ); XLAL_CHECK ( write_SFTdata ("LFT_SFTs.dat", lftSFTs ) == XLAL_SUCCESS, XLAL_EFUNC ); } // end: debug output // ========== compare resulting LFTs ========== VectorComparison XLAL_INIT_DECL(tol0); XLALPrintInfo ("Comparing LFT with itself: should give 0 for all measures\n"); XLAL_CHECK ( XLALCompareSFTs ( lftR4, lftR4, &tol0 ) == XLAL_SUCCESS, XLAL_EFUNC ); XLAL_CHECK ( XLALCompareSFTs ( lftSFTs, lftSFTs, &tol0 ) == XLAL_SUCCESS, XLAL_EFUNC ); VectorComparison XLAL_INIT_DECL(tol); tol.relErr_L1 = 4e-2; tol.relErr_L2 = 5e-2; tol.angleV = 5e-2; // rad tol.relErr_atMaxAbsx = 1.2e-2; tol.relErr_atMaxAbsy = 1.2e-2; XLALPrintInfo ("Comparing LFT from REAL4-timeseries, to LFT from heterodyned COMPLEX8-timeseries:\n"); XLAL_CHECK ( XLALCompareSFTs ( lftR4, lftSFTs, &tol ) == XLAL_SUCCESS, XLAL_EFUNC ); // ---------- free memory ---------- XLALDestroyREAL4TimeSeries ( tsR4 ); XLALDestroyREAL4FFTPlan ( planR4 ); XLALDestroySFT ( lft0 ); XLALDestroySFT ( lftR4 ); XLALDestroySFT ( lftSFTs ); XLALDestroySFT ( lftSFTs0 ); return XLAL_SUCCESS; } // test_XLALSFTVectorToLFT()
/** * Single-IFO version of XLALCWMakeFakeMultiData(), handling the actual * work, but same input API. The 'detectorIndex' has the index of the detector * to be used from the multi-IFO arrays. */ int XLALCWMakeFakeData ( SFTVector **SFTvect, REAL8TimeSeries **Tseries, const PulsarParamsVector *injectionSources, const CWMFDataParams *dataParams, UINT4 detectorIndex, /* index for current detector in dataParams */ const EphemerisData *edat ) { XLAL_CHECK ( (SFTvect == NULL) || ((*SFTvect) == NULL ), XLAL_EINVAL ); XLAL_CHECK ( (Tseries == NULL) || ((*Tseries) == NULL ), XLAL_EINVAL ); XLAL_CHECK ( (SFTvect != NULL) || (Tseries != NULL), XLAL_EINVAL ); XLAL_CHECK ( edat != NULL, XLAL_EINVAL ); XLAL_CHECK ( dataParams != NULL, XLAL_EINVAL ); XLAL_CHECK ( detectorIndex < dataParams->multiIFO.length, XLAL_EINVAL ); XLAL_CHECK ( detectorIndex < dataParams->multiNoiseFloor.length, XLAL_EINVAL ); XLAL_CHECK ( detectorIndex < dataParams->multiTimestamps.length, XLAL_EINVAL ); XLAL_CHECK ( (dataParams->inputMultiTS == NULL) || (detectorIndex < dataParams->inputMultiTS->length), XLAL_EINVAL ); XLAL_CHECK ( (dataParams->inputMultiTS == NULL) || (dataParams->fMin == 0 && dataParams->Band == 0), XLAL_EINVAL, "If given time-series, must have fMin=Band=0\n"); // initial default values fMin, sampling rate from caller input or timeseries REAL8 fMin = dataParams->fMin; REAL8 fBand = dataParams->Band; REAL8 fSamp = 2.0 * fBand; if ( dataParams->inputMultiTS != NULL ) { XLAL_CHECK ( (fMin == 0) && (fBand == 0), XLAL_EINVAL, "fMin and fBand must be 0 if input timeseries is given\n"); const REAL8TimeSeries *ts = dataParams->inputMultiTS->data[detectorIndex]; XLAL_CHECK ( ts != NULL, XLAL_EINVAL ); REAL8 dt = ts->deltaT; fMin = ts->f0; fSamp = 1.0 / dt; fBand = 0.5 * fSamp; } const LIGOTimeGPSVector *timestamps = dataParams->multiTimestamps.data[detectorIndex]; const LALDetector *site = &dataParams->multiIFO.sites[detectorIndex]; REAL8 Tsft = timestamps->deltaT; // if SFT output requested: need *effective* fMin and Band consistent with SFT bins if ( SFTvect ) { UINT4 firstBinEff, numBinsEff; XLAL_CHECK ( XLALFindCoveringSFTBins ( &firstBinEff, &numBinsEff, fMin, fBand, Tsft ) == XLAL_SUCCESS, XLAL_EFUNC ); REAL8 fBand_eff = (numBinsEff - 1.0) / Tsft; REAL8 fMin_eff = firstBinEff / Tsft; REAL8 fMax = fMin + dataParams->Band; REAL8 fMax_eff = fMin_eff + fBand_eff; if ( (fMin_eff != fMin) || (fBand_eff != fBand ) ) { XLALPrintWarning("Caller asked for Band [%.16g, %.16g] Hz, effective SFT-Band produced is [%.16g, %.16g] Hz\n", fMin, fMax, fMin_eff, fMax_eff ); XLAL_CHECK ( dataParams->inputMultiTS == NULL, XLAL_EINVAL, "Cannot expand effective frequency band with input timeseries given. Timeseries seems inconsistent with SFTs\n"); fMin = fMin_eff; // (potentially) lower minimal frequency to fit SFT bins fBand = fBand_eff; fSamp = 2.0 * fBand_eff; // (potentially) higher sampling rate required to fit SFT bins } // if (fMin_eff != fMin) || (fBand_eff != fBand) } // if SFT-output requested // characterize the output time-series UINT4 n0_fSamp = (UINT4) round ( Tsft * fSamp ); // by construction, fSamp * Tsft = integer, but if there are gaps between SFTs, // then we might have to sample at higher rates in order for all SFT-boundaries to // fall on exact timesteps of the timeseries. // ie we start from fsamp0 = n0_fSamp/Tsft, and then try to find the smallest // n1_fSamp >= n0_fSamp, such that for fsamp1 = n1_fSamp/Tsft, for all gaps i: Dt_i * fsamp1 = int UINT4 n1_fSamp; XLAL_CHECK ( XLALFindSmallestValidSamplingRate ( &n1_fSamp, n0_fSamp, timestamps ) == XLAL_SUCCESS, XLAL_EFUNC ); if ( n1_fSamp != n0_fSamp ) { REAL8 fSamp1 = n1_fSamp / Tsft; // increased sampling rate to fit all gaps XLALPrintWarning ( "GAPS: Initial SFT sampling frequency fSamp0= %d/%.0f = %g had to be increased to fSamp1 = %d/%.0f = %g\n", n0_fSamp, Tsft, fSamp, n1_fSamp, Tsft, fSamp1 ); XLAL_CHECK ( dataParams->inputMultiTS == NULL, XLAL_EINVAL, "Cannot expand effective frequency band with input timeseries given. Timeseries seems inconsistent with SFT timestamps\n"); fSamp = fSamp1; } // if higher effective sampling rate required // ----- start-time and duration ----- LIGOTimeGPS firstGPS = timestamps->data[0]; REAL8 firstGPS_REAL8 = XLALGPSGetREAL8 ( &firstGPS ); LIGOTimeGPS lastGPS = timestamps->data [ timestamps->length - 1 ]; REAL8 lastGPS_REAL8 = XLALGPSGetREAL8 ( &lastGPS ); XLALGPSAdd( &lastGPS, Tsft ); REAL8 duration = XLALGPSDiff ( &lastGPS, &firstGPS ); // start with an empty output time-series REAL4TimeSeries *Tseries_sum; { REAL8 numSteps = ceil ( fSamp * duration ); XLAL_CHECK ( numSteps < (REAL8)LAL_UINT4_MAX, XLAL_EDOM, "Sorry, time-series of %g samples too long to fit into REAL4TimeSeries (maxLen = %g)\n", numSteps, (REAL8)LAL_UINT4_MAX ); REAL8 dt = 1.0 / fSamp; REAL8 fHeterodyne = fMin; // heterodyne signals at lower end of frequency-band CHAR *detPrefix = XLALGetChannelPrefix ( site->frDetector.name ); XLAL_CHECK ( (Tseries_sum = XLALCreateREAL4TimeSeries ( detPrefix, &firstGPS, fHeterodyne, dt, &lalStrainUnit, (UINT4)numSteps )) != NULL, XLAL_EFUNC ); memset ( Tseries_sum->data->data, 0, Tseries_sum->data->length * sizeof(Tseries_sum->data->data[0]) ); XLALFree ( detPrefix ); } // generate empty timeseries // add CW signals, if any UINT4 numPulsars = injectionSources ? injectionSources->length : 0; for ( UINT4 iInj = 0; iInj < numPulsars; iInj ++ ) { // truncate any transient-CW timeseries to the actual support of the transient signal, // in order to make the generation more efficient, these 'partial timeseries' // will then be added to the full timeseries const PulsarParams *pulsarParams = &( injectionSources->data[iInj] ); UINT4 t0, t1; XLAL_CHECK ( XLALGetTransientWindowTimespan ( &t0, &t1, pulsarParams->Transient ) == XLAL_SUCCESS, XLAL_EFUNC ); // use latest possible start-time: max(t0,firstGPS), but not later than than lastGPS LIGOTimeGPS XLAL_INIT_DECL(signalStartGPS); if ( t0 <= firstGPS_REAL8 ) { signalStartGPS = firstGPS; } else if ( t0 >= lastGPS_REAL8 ) { signalStartGPS = lastGPS; } else { signalStartGPS.gpsSeconds = t0; } // use earliest possible end-time: min(t1,lastGPS), but not earlier than firstGPS LIGOTimeGPS XLAL_INIT_DECL(signalEndGPS); if ( t1 >= lastGPS_REAL8 ) { signalEndGPS = lastGPS; } else if ( t1 <= firstGPS_REAL8 ) { signalEndGPS = firstGPS; } else { signalEndGPS.gpsSeconds = t1; } REAL8 signalDuration = XLALGPSDiff ( &signalEndGPS, &signalStartGPS ); XLAL_CHECK ( signalDuration >= 0, XLAL_EFAILED, "Something went wrong, got negative signal duration = %g\n", signalDuration ); if ( signalDuration > 0 ) // only need to do sth if transient-window had finite overlap with output TS { REAL4TimeSeries *Tseries_i = NULL; XLAL_CHECK ( (Tseries_i = XLALGenerateCWSignalTS ( pulsarParams, site, signalStartGPS, signalDuration, fSamp, fMin, edat )) != NULL, XLAL_EFUNC ); XLAL_CHECK ( (Tseries_sum = XLALAddREAL4TimeSeries ( Tseries_sum, Tseries_i )) != NULL, XLAL_EFUNC ); XLALDestroyREAL4TimeSeries ( Tseries_i ); } } // for iInj < numSources /* add Gaussian noise if requested */ REAL8 sqrtSn = dataParams->multiNoiseFloor.sqrtSn[detectorIndex]; if ( sqrtSn > 0) { REAL8 noiseSigma = sqrtSn * sqrt ( 0.5 * fSamp ); INT4 randSeed = (dataParams->randSeed == 0) ? 0 : (dataParams->randSeed + detectorIndex); // seed=0 means to use /dev/urandom, so don't touch it XLAL_CHECK ( XLALAddGaussianNoise ( Tseries_sum, noiseSigma, randSeed ) == XLAL_SUCCESS, XLAL_EFUNC ); } // convert final signal+Gaussian-noise timeseries into REAL8 precision: REAL8TimeSeries *outTS; XLAL_CHECK ( (outTS = XLALConvertREAL4TimeSeriesToREAL8 ( Tseries_sum )) != NULL, XLAL_EFUNC ); XLALDestroyREAL4TimeSeries ( Tseries_sum ); // add input noise time-series here if given if ( dataParams->inputMultiTS != NULL ) { XLAL_CHECK ( (outTS = XLALAddREAL8TimeSeries ( outTS, dataParams->inputMultiTS->data[detectorIndex] )) != NULL, XLAL_EFUNC ); } // turn final timeseries into SFTs, if requested if ( SFTvect != NULL ) { // compute SFTs from timeseries SFTVector *sftVect; XLAL_CHECK ( (sftVect = XLALMakeSFTsFromREAL8TimeSeries ( outTS, timestamps, dataParams->SFTWindowType, dataParams->SFTWindowBeta)) != NULL, XLAL_EFUNC ); // extract effective band from this, if neccessary (ie if faster-sampled output SFTs) if ( n1_fSamp != n0_fSamp ) { XLAL_CHECK ( ((*SFTvect) = XLALExtractBandFromSFTVector ( sftVect, fMin, fBand )) != NULL, XLAL_EFUNC ); XLALDestroySFTVector ( sftVect ); } else { (*SFTvect) = sftVect; } } // if SFTvect // return timeseries if requested if ( Tseries != NULL ) { (*Tseries) = outTS; } else { XLALDestroyREAL8TimeSeries ( outTS ); } return XLAL_SUCCESS; } // XLALCWMakeFakeData()