/** * Freq-shift the given COMPLEX8Timeseries by an amount of 'shift' Hz, * using the time-domain expression y(t) = x(t) * e^(-i 2pi df t), * which shifts x(f) into y(f) = x(f + df) * * NOTE: this <b>modifies</b> the COMPLEX8TimeSeries in place */ int XLALFrequencyShiftCOMPLEX8TimeSeries ( COMPLEX8TimeSeries *x, /**< [in/out] timeseries to time-shift */ const REAL8 shift /**< [in] freq-shift in Hz */ ) { XLAL_CHECK ( (x != NULL) && ( x->data != NULL ), XLAL_EINVAL ); if ( shift == 0 ) { return XLAL_SUCCESS; } /* get timeseries time-step */ REAL8 deltat = x->deltaT; /* loop over COMPLEX8TimeSeries elements */ for ( UINT4 k=0; k < x->data->length; k++) { REAL8 tk = k * deltat; /* time of k-th bin */ REAL8 shiftCycles = shift * tk; REAL4 fact_re, fact_im; /* complex phase-shift factor e^(-2pi f tau) */ /* use a sin/cos look-up-table for speed */ XLAL_CHECK( XLALSinCos2PiLUT ( &fact_im, &fact_re, shiftCycles ) == XLAL_SUCCESS, XLAL_EFUNC ); COMPLEX8 fact = crectf(fact_re, fact_im); x->data->data[k] *= fact; } /* for k < numBins */ /* adjust timeseries heterodyne frequency to the shift */ x->f0 -= shift; return XLAL_SUCCESS; } /* XLALFrequencyShiftCOMPLEX8TimeSeries() */
/** * Time-shift the given SFT by an amount of 'shift' seconds, * using the frequency-domain expression * \f$\widetilde{y}(f) = \widetilde{x}(f) \, e^{i 2\pi\,f\,\tau}\f$, * which shifts \f$x(t)\f$ into \f$y(t) = x(t + \tau)\f$ * * NOTE: this <b>modifies</b> the SFT in place */ int XLALTimeShiftSFT ( SFTtype *sft, /**< [in/out] SFT to time-shift */ REAL8 shift /**< time-shift \f$\tau\f$ in seconds */ ) { XLAL_CHECK ( (sft != NULL) && (sft->data != NULL), XLAL_EINVAL ); if ( shift == 0 ) { return XLAL_SUCCESS; } for ( UINT4 k=0; k < sft->data->length; k++ ) { REAL8 fk = sft->f0 + k * sft->deltaF; /* frequency of k-th bin */ REAL8 shiftCyles = shift * fk; REAL4 fact_re, fact_im; /* complex phase-shift factor e^(-2pi f tau) */ XLAL_CHECK ( XLALSinCos2PiLUT ( &fact_im, &fact_re, shiftCyles ) == XLAL_SUCCESS, XLAL_EFUNC ); COMPLEX8 fact = crectf(fact_re, fact_im); sft->data->data[k] *= fact; } /* for k < numBins */ /* adjust SFTs epoch to the shift */ XLALGPSAdd ( &sft->epoch, shift ); return XLAL_SUCCESS; } // XLALTimeShiftSFT()
/** * Apply a spin-down correction to the complex8 timeseries * using the time-domain expression y(t) = x(t) * e^(-i 2pi sum f_k * (t-tref)^(k+1)), * * NOTE: this <b>modifies</b> the input COMPLEX8TimeSeries in place */ int XLALSpinDownCorrectionMultiTS ( MultiCOMPLEX8TimeSeries *multiTimeSeries, /**< [in/out] timeseries to time-shift */ const PulsarDopplerParams *doppler /**< parameter-space point to correct for */ ) { // check input sanity XLAL_CHECK ( (multiTimeSeries != NULL) && (multiTimeSeries->data != NULL) && (multiTimeSeries->length > 0), XLAL_EINVAL ); XLAL_CHECK ( doppler != NULL, XLAL_EINVAL ); UINT4 numDetectors = multiTimeSeries->length; LIGOTimeGPS *epoch = &(multiTimeSeries->data[0]->epoch); UINT4 numSamples = multiTimeSeries->data[0]->data->length; REAL8 dt = multiTimeSeries->data[0]->deltaT; /* determine number of spin down's and check if sensible */ UINT4 nspins = PULSAR_MAX_SPINS - 1; while ( (nspins > 0) && (doppler->fkdot[nspins] == 0) ) { nspins--; } /* apply spin derivitive correction to resampled timeseries */ REAL8 tk = XLALGPSDiff ( epoch, &(doppler->refTime) ); for ( UINT4 k=0; k < numSamples; k++ ) { REAL8 tk_pow_jp1 = tk; REAL8 cycles_k = 0; for ( UINT4 j=1; j <= nspins; j++ ) { tk_pow_jp1 *= tk; /* compute fractional number of cycles the spin-derivitive has added since the reftime */ cycles_k += LAL_FACT_INV[j+1] * doppler->fkdot[j] * tk_pow_jp1; } // for j < nspins REAL4 cosphase, sinphase; XLAL_CHECK( XLALSinCos2PiLUT ( &sinphase, &cosphase, -cycles_k ) == XLAL_SUCCESS, XLAL_EFUNC ); COMPLEX8 em2piphase = crectf ( cosphase, sinphase ); /* loop over detectors */ for ( UINT4 X=0; X < numDetectors; X++ ) { multiTimeSeries->data[X]->data->data[k] *= em2piphase; } // for X < numDetectors tk += dt; } // for k < numSamples return XLAL_SUCCESS; } // XLALSpinDownCorrectionMultiTS()
/** * Turn the given SFTvector into one long time-series, properly dealing with gaps. */ COMPLEX8TimeSeries * XLALSFTVectorToCOMPLEX8TimeSeries ( const SFTVector *sftsIn /**< [in] SFT vector */ ) { // check input sanity XLAL_CHECK_NULL ( (sftsIn !=NULL) && (sftsIn->length > 0), XLAL_EINVAL ); // create a local copy of the input SFTs, as they will be locally modified! SFTVector *sfts; XLAL_CHECK_NULL ( (sfts = XLALDuplicateSFTVector ( sftsIn )) != NULL, XLAL_EFUNC ); /* define some useful shorthands */ UINT4 numSFTs = sfts->length; SFTtype *firstSFT = &(sfts->data[0]); SFTtype *lastSFT = &(sfts->data[numSFTs-1]); UINT4 numFreqBinsSFT = firstSFT->data->length; REAL8 dfSFT = firstSFT->deltaF; REAL8 Tsft = 1.0 / dfSFT; REAL8 deltaT = Tsft / numFreqBinsSFT; // complex FFT: numSamplesSFT = numFreqBinsSFT REAL8 f0SFT = firstSFT->f0; /* if the start and end input pointers are NOT NULL then determine start and time-span of the final long time-series */ LIGOTimeGPS start = firstSFT->epoch; LIGOTimeGPS end = lastSFT->epoch; XLALGPSAdd ( &end, Tsft ); /* determine output time span */ REAL8 Tspan; XLAL_CHECK_NULL ( (Tspan = XLALGPSDiff ( &end, &start ) ) > 0, XLAL_EINVAL ); UINT4 numSamples = lround ( Tspan / deltaT ); /* determine the heterodyning frequency */ /* fHet = DC of our internal DFTs */ UINT4 NnegSFT = NhalfNeg ( numFreqBinsSFT ); REAL8 fHet = f0SFT + 1.0 * NnegSFT * dfSFT; /* ----- Prepare invFFT of SFTs: compute plan for FFTW */ COMPLEX8FFTPlan *SFTplan; XLAL_CHECK_NULL ( (SFTplan = XLALCreateReverseCOMPLEX8FFTPlan( numFreqBinsSFT, 0 )) != NULL, XLAL_EFUNC ); /* ----- Prepare short time-series holding ONE invFFT of a single SFT */ LIGOTimeGPS XLAL_INIT_DECL(epoch); COMPLEX8TimeSeries *sTS; XLAL_CHECK_NULL ( (sTS = XLALCreateCOMPLEX8TimeSeries ( "short timeseries", &epoch, 0, deltaT, &emptyLALUnit, numFreqBinsSFT )) != NULL, XLAL_EFUNC ); /* ----- prepare long TimeSeries container ---------- */ COMPLEX8TimeSeries *lTS; XLAL_CHECK_NULL ( (lTS = XLALCreateCOMPLEX8TimeSeries ( firstSFT->name, &start, fHet, deltaT, &emptyLALUnit, numSamples )) != NULL, XLAL_EFUNC ); memset ( lTS->data->data, 0, numSamples * sizeof(*lTS->data->data)); /* set all time-samples to zero (in case there are gaps) */ /* ---------- loop over all SFTs and inverse-FFT them ---------- */ for ( UINT4 n = 0; n < numSFTs; n ++ ) { SFTtype *thisSFT = &(sfts->data[n]); /* find bin in long timeseries corresponding to starttime of *this* SFT */ REAL8 offset_n = XLALGPSDiff ( &(thisSFT->epoch), &start ); UINT4 bin0_n = lround ( offset_n / deltaT ); /* round to closest bin */ REAL8 nudge_n = bin0_n * deltaT - offset_n; /* rounding error */ nudge_n = 1e-9 * round ( nudge_n * 1e9 ); /* round to closest nanosecond */ /* nudge SFT into integer timestep bin if necessary */ XLAL_CHECK_NULL ( XLALTimeShiftSFT ( thisSFT, nudge_n ) == XLAL_SUCCESS, XLAL_EFUNC ); /* determine heterodyning phase-correction for this SFT */ REAL8 offset = XLALGPSDiff ( &thisSFT->epoch, &start ); // updated value after time-shift // fHet * Tsft is an integer by construction, because fHet was chosen as a frequency-bin of the input SFTs // therefore we only need the remainder (offset % Tsft) REAL8 offsetEff = fmod ( offset, Tsft ); REAL8 hetCycles = fmod ( fHet * offsetEff, 1); // heterodyning phase-correction for this SFT if ( nudge_n != 0 ){ XLALPrintInfo("n = %d, offset_n = %g, nudge_n = %g, offset = %g, offsetEff = %g, hetCycles = %g\n", n, offset_n, nudge_n, offset, offsetEff, hetCycles ); } REAL4 hetCorrection_re, hetCorrection_im; XLAL_CHECK_NULL ( XLALSinCos2PiLUT ( &hetCorrection_im, &hetCorrection_re, -hetCycles ) == XLAL_SUCCESS, XLAL_EFUNC ); COMPLEX8 hetCorrection = crectf( hetCorrection_re, hetCorrection_im ); /* Note: we also bundle the overall normalization of 'df' into the het-correction. * This ensures that the resulting timeseries will have the correct normalization, according to * x_l = invFT[sft]_l = df * sum_{k=0}^{N-1} xt_k * e^(i 2pi k l / N ) * where x_l is the l-th timestamp, and xt_k is the k-th frequency bin of the SFT. * See the LAL-conventions on FFTs: http://www.ligo.caltech.edu/docs/T/T010095-00.pdf * (the FFTw convention does not contain the factor of 'df', which is why we need to * apply it ourselves) * */ hetCorrection *= dfSFT; XLAL_CHECK_NULL ( XLALReorderSFTtoFFTW (thisSFT->data) == XLAL_SUCCESS, XLAL_EFUNC ); XLAL_CHECK_NULL ( XLALCOMPLEX8VectorFFT( sTS->data, thisSFT->data, SFTplan ) == XLAL_SUCCESS, XLAL_EFUNC ); for ( UINT4 j=0; j < sTS->data->length; j++) { sTS->data->data[j] *= hetCorrection; } // for j < numFreqBinsSFT // copy the short (shifted) heterodyned timeseries into correct location within long timeseries UINT4 binsLeft = numSamples - bin0_n; UINT4 copyLen = MYMIN ( numFreqBinsSFT, binsLeft ); /* make sure not to write past the end of the long TS */ memcpy ( &lTS->data->data[bin0_n], sTS->data->data, copyLen * sizeof(lTS->data->data[0]) ); } /* for n < numSFTs */ // cleanup memory XLALDestroySFTVector ( sfts ); XLALDestroyCOMPLEX8TimeSeries ( sTS ); XLALDestroyCOMPLEX8FFTPlan ( SFTplan ); return lTS; } // XLALSFTVectorToCOMPLEX8TimeSeries()
/* Revamped version of LALDemod() (based on TestLALDemod() in CFS). * Compute JKS's Fa and Fb, which are ingredients for calculating the F-statistic. */ static int XLALComputeFaFb ( Fcomponents *FaFb, /* [out] Fa,Fb (and possibly atoms) returned */ const SFTVector *sfts, /* [in] input SFTs */ const PulsarSpins fkdot, /* [in] frequency and derivatives fkdot = d^kf/dt^k */ const SSBtimes *tSSB, /* [in] SSB timing series for particular sky-direction */ const AMCoeffs *amcoe, /* [in] antenna-pattern coefficients for this sky-direction */ const ComputeFParams *params ) /* addition computational params */ { UINT4 alpha; /* loop index over SFTs */ UINT4 spdnOrder; /* maximal spindown-orders */ UINT4 numSFTs; /* number of SFTs (M in the Notes) */ COMPLEX16 Fa, Fb; REAL8 Tsft; /* length of SFTs in seconds */ INT4 freqIndex0; /* index of first frequency-bin in SFTs */ INT4 freqIndex1; /* index of last frequency-bin in SFTs */ REAL4 *a_al, *b_al; /* pointer to alpha-arrays over a and b */ REAL8 *DeltaT_al, *Tdot_al; /* pointer to alpha-arrays of SSB-timings */ SFTtype *SFT_al; /* SFT alpha */ UINT4 Dterms = params->Dterms; REAL8 norm = OOTWOPI; /* ----- check validity of input */ #ifndef LAL_NDEBUG if ( !FaFb ) { XLALPrintError ("\nOutput-pointer is NULL !\n\n"); XLAL_ERROR ( XLAL_EINVAL); } if ( !sfts || !sfts->data ) { XLALPrintError ("\nInput SFTs are NULL!\n\n"); XLAL_ERROR ( XLAL_EINVAL); } if ( !tSSB || !tSSB->DeltaT || !tSSB->Tdot || !amcoe || !amcoe->a || !amcoe->b || !params) { XLALPrintError ("\nIllegal NULL in input !\n\n"); XLAL_ERROR ( XLAL_EINVAL); } if ( PULSAR_MAX_SPINS > LAL_FACT_MAX ) { XLALPrintError ("\nInverse factorials table only up to order s=%d, can't handle %d spin-order\n\n", LAL_FACT_MAX, PULSAR_MAX_SPINS - 1 ); XLAL_ERROR ( XLAL_EINVAL); } #endif /* ----- prepare convenience variables */ numSFTs = sfts->length; Tsft = 1.0 / sfts->data[0].deltaF; { REAL8 dFreq = sfts->data[0].deltaF; freqIndex0 = lround ( sfts->data[0].f0 / dFreq ); /* lowest freqency-index */ freqIndex1 = freqIndex0 + sfts->data[0].data->length; } /* ----- prepare return of 'FstatAtoms' if requested */ if ( params->returnAtoms ) { if ( (FaFb->multiFstatAtoms = LALMalloc ( sizeof(*FaFb->multiFstatAtoms) )) == NULL ){ XLAL_ERROR ( XLAL_ENOMEM ); } FaFb->multiFstatAtoms->length = 1; /* in this function: single-detector only */ if ( (FaFb->multiFstatAtoms->data = LALMalloc ( 1 * sizeof( *FaFb->multiFstatAtoms->data) )) == NULL ){ LALFree (FaFb->multiFstatAtoms); XLAL_ERROR ( XLAL_ENOMEM ); } if ( (FaFb->multiFstatAtoms->data[0] = XLALCreateFstatAtomVector ( numSFTs )) == NULL ) { LALFree ( FaFb->multiFstatAtoms->data ); LALFree ( FaFb->multiFstatAtoms ); XLAL_ERROR( XLAL_ENOMEM ); } FaFb->multiFstatAtoms->data[0]->TAtom = Tsft; /* time-baseline of returned atoms is Tsft */ } /* if returnAtoms */ /* ----- find highest non-zero spindown-entry */ for ( spdnOrder = PULSAR_MAX_SPINS - 1; spdnOrder > 0 ; spdnOrder -- ) if ( fkdot[spdnOrder] ) break; Fa = 0.0f; Fb = 0.0f; a_al = amcoe->a->data; /* point to beginning of alpha-arrays */ b_al = amcoe->b->data; DeltaT_al = tSSB->DeltaT->data; Tdot_al = tSSB->Tdot->data; SFT_al = sfts->data; /* Loop over all SFTs */ for ( alpha = 0; alpha < numSFTs; alpha++ ) { REAL4 a_alpha, b_alpha; INT4 kstar; /* central frequency-bin k* = round(xhat_alpha) */ INT4 k0, k1; COMPLEX8 *Xalpha = SFT_al->data->data; /* pointer to current SFT-data */ COMPLEX8 *Xalpha_l; /* pointer to frequency-bin k in current SFT */ REAL4 s_alpha=0, c_alpha=0;/* sin(2pi kappa_alpha) and (cos(2pi kappa_alpha)-1) */ REAL4 realQ, imagQ; /* Re and Im of Q = e^{-i 2 pi lambda_alpha} */ REAL4 realXP, imagXP; /* Re/Im of sum_k X_ak * P_ak */ REAL4 realQXP, imagQXP; /* Re/Im of Q_alpha R_alpha */ REAL8 lambda_alpha, kappa_max, kappa_star; COMPLEX8 Fa_alpha, Fb_alpha; /* ----- calculate kappa_max and lambda_alpha */ { UINT4 s; /* loop-index over spindown-order */ REAL8 phi_alpha, Dphi_alpha, DT_al; REAL8 Tas; /* temporary variable to calculate (DeltaT_alpha)^s */ /* init for s=0 */ phi_alpha = 0.0; Dphi_alpha = 0.0; DT_al = (*DeltaT_al); Tas = 1.0; /* DeltaT_alpha ^ 0 */ for (s=0; s <= spdnOrder; s++) { REAL8 fsdot = fkdot[s]; Dphi_alpha += fsdot * Tas * LAL_FACT_INV[s]; /* here: DT^s/s! */ Tas *= DT_al; /* now: DT^(s+1) */ phi_alpha += fsdot * Tas * LAL_FACT_INV[s+1]; } /* for s <= spdnOrder */ /* Step 3: apply global factors to complete Dphi_alpha */ Dphi_alpha *= Tsft * (*Tdot_al); /* guaranteed > 0 ! */ lambda_alpha = phi_alpha - 0.5 * Dphi_alpha; /* real- and imaginary part of e^{-i 2 pi lambda_alpha } */ if ( XLALSinCos2PiLUT ( &imagQ, &realQ, - lambda_alpha ) ) { XLAL_ERROR ( XLAL_EFUNC); } kstar = (INT4) (Dphi_alpha); /* k* = floor(Dphi_alpha) for positive Dphi */ kappa_star = Dphi_alpha - 1.0 * kstar; /* remainder of Dphi_alpha: >= 0 ! */ kappa_max = kappa_star + 1.0 * Dterms - 1.0; /* ----- check that required frequency-bins are found in the SFTs ----- */ k0 = kstar - Dterms + 1; k1 = k0 + 2 * Dterms - 1; if ( (k0 < freqIndex0) || (k1 > freqIndex1) ) { XLALPrintError ("Required frequency-bins [%d, %d] not covered by SFT-interval [%d, %d]\n\n", k0, k1, freqIndex0, freqIndex1 ); XLAL_ERROR(XLAL_EDOM); } } /* compute kappa_star, lambda_alpha */ /* NOTE: sin[ 2pi (Dphi_alpha - k) ] = sin [ 2pi Dphi_alpha ], therefore * the trig-functions need to be calculated only once! * We choose the value sin[ 2pi(Dphi_alpha - kstar) ] because it is the * closest to zero and will pose no numerical difficulties ! */ XLAL_CHECK( XLALSinCos2PiLUT ( &s_alpha, &c_alpha, kappa_star ) == XLAL_SUCCESS, XLAL_EFUNC ); c_alpha -= 1.0f; /* ---------- calculate the (truncated to Dterms) sum over k ---------- */ /* ---------- ATTENTION: this the "hot-loop", which will be * executed many millions of times, so anything in here * has a HUGE impact on the whole performance of the code. * * DON'T touch *anything* in here unless you really know * what you're doing !! *------------------------------------------------------------ */ Xalpha_l = Xalpha + k0 - freqIndex0; /* first frequency-bin in sum */ realXP = 0; imagXP = 0; /* if no danger of denominator -> 0 */ if ( ( kappa_star > LD_SMALL4 ) && (kappa_star < 1.0 - LD_SMALL4) ) { /* improved hotloop algorithm by Fekete Akos: * take out repeated divisions into a single common denominator, * plus use extra cleverness to compute the nominator efficiently... */ REAL4 Sn = crealf(*Xalpha_l); REAL4 Tn = cimagf(*Xalpha_l); REAL4 pn = kappa_max; REAL4 qn = pn; REAL4 U_alpha, V_alpha; /* recursion with 2*Dterms steps */ UINT4 l; for ( l = 1; l < 2*Dterms; l ++ ) { Xalpha_l ++; pn = pn - 1.0f; /* p_(n+1) */ Sn = pn * Sn + qn * crealf(*Xalpha_l); /* S_(n+1) */ Tn = pn * Tn + qn * cimagf(*Xalpha_l); /* T_(n+1) */ qn *= pn; /* q_(n+1) */ } /* for l <= 2*Dterms */ U_alpha = Sn / qn; V_alpha = Tn / qn; #ifndef LAL_NDEBUG if ( !isfinite(U_alpha) || !isfinite(V_alpha) || !isfinite(pn) || !isfinite(qn) || !isfinite(Sn) || !isfinite(Tn) ) { XLALPrintError("XLALComputeFaFb() returned non-finite: U_alpha=%f, V_alpha=%f, pn=%f, qn=%f, Sn=%f, Tn=%f\n", U_alpha, V_alpha, pn, qn, Sn, Tn); XLAL_ERROR (XLAL_EFPINVAL); } #endif realXP = s_alpha * U_alpha - c_alpha * V_alpha; imagXP = c_alpha * U_alpha + s_alpha * V_alpha; } /* if |remainder| > LD_SMALL4 */ else { /* otherwise: lim_{rem->0}P_alpha,k = 2pi delta_{k,kstar} */ UINT4 ind0; if ( kappa_star <= LD_SMALL4 ) ind0 = Dterms - 1; else ind0 = Dterms; realXP = TWOPI_FLOAT * crealf(Xalpha_l[ind0]); imagXP = TWOPI_FLOAT * cimagf(Xalpha_l[ind0]); } /* if |remainder| <= LD_SMALL4 */ realQXP = realQ * realXP - imagQ * imagXP; imagQXP = realQ * imagXP + imagQ * realXP; /* we're done: ==> combine these into Fa and Fb */ a_alpha = (*a_al); b_alpha = (*b_al); Fa_alpha = crectf( a_alpha * realQXP, a_alpha * imagQXP ); Fa += Fa_alpha; Fb_alpha = crectf( b_alpha * realQXP, b_alpha * imagQXP ); Fb += Fb_alpha; /* store per-SFT F-stat 'atoms' for transient-CW search */ if ( params->returnAtoms ) { FaFb->multiFstatAtoms->data[0]->data[alpha].timestamp = (UINT4)XLALGPSGetREAL8( &SFT_al->epoch ); FaFb->multiFstatAtoms->data[0]->data[alpha].a2_alpha = a_alpha * a_alpha; FaFb->multiFstatAtoms->data[0]->data[alpha].b2_alpha = b_alpha * b_alpha; FaFb->multiFstatAtoms->data[0]->data[alpha].ab_alpha = a_alpha * b_alpha; FaFb->multiFstatAtoms->data[0]->data[alpha].Fa_alpha = norm * Fa_alpha; FaFb->multiFstatAtoms->data[0]->data[alpha].Fb_alpha = norm * Fb_alpha; } /* advance pointers over alpha */ a_al ++; b_al ++; DeltaT_al ++; Tdot_al ++; SFT_al ++; } /* for alpha < numSFTs */ /* return result */ FaFb->Fa = norm * Fa; FaFb->Fb = norm * Fb; return XLAL_SUCCESS; } // XLALComputeFaFb()