/**
 * 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()
Esempio n. 2
0
/**
 * 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()