GeneratePulsarSignal.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004, 2005 Reinhard Prix
00003  * Copyright (C) 2004, 2005 Greg Mendell
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with with program; see the file COPYING. If not, write to the 
00017  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
00018  *  MA  02111-1307  USA
00019  */
00020 
00021 /* NOTES: */
00022 /* 07/14/04 gam; add functions LALComputeSkyAndZeroPsiAMResponse and LALFastGeneratePulsarSFTs */
00023 /* 10/08/04 gam; fix indexing into trig lookup tables (LUTs) by having table go from -2*pi to 2*pi */
00024 /* 10/12/04 gam; When computing fCross and fPlus need to use 2.0*psi. */
00025 /* 09/07/05 gam; Add Dterms parameter to LALFastGeneratePulsarSFTs; use this to fill in SFT bins with fake data as per LALDemod else fill in bin with zero */
00026 
00027 /**
00028  * \author Reinhard Prix, Greg Mendell
00029  * \date 2005
00030  * \file 
00031  * \ingroup moduleGeneratePulsarSignal
00032  *
00033  * \brief Module to generate simulated pulsar-signals.
00034  *
00035  * $Id: GeneratePulsarSignal.c,v 1.57 2008/08/14 17:04:11 evang Exp $
00036  *
00037  */
00038 #include <math.h>
00039 
00040 #include <lal/AVFactories.h>
00041 #include <lal/SeqFactories.h>
00042 #include <lal/RealFFT.h>
00043 #include <lal/SFTutils.h>
00044 #include <lal/Window.h>
00045 
00046 #include <lal/GeneratePulsarSignal.h>
00047 
00048 /*----------------------------------------------------------------------*/
00049 /* Internal helper functions */
00050 static int check_timestamp_bounds (const LIGOTimeGPSVector *timestamps, LIGOTimeGPS t0, LIGOTimeGPS t1);
00051 static void checkNoiseSFTs (LALStatus *, const SFTVector *sfts, REAL8 f0, REAL8 f1, REAL8 deltaF);
00052 static void correct_phase (LALStatus *, SFTtype *sft, LIGOTimeGPS tHeterodyne);
00053 
00054 /*----------------------------------------------------------------------*/
00055 
00056 NRCSID( GENERATEPULSARSIGNALC, "$Id: GeneratePulsarSignal.c,v 1.57 2008/08/14 17:04:11 evang Exp $");
00057 
00058 extern INT4 lalDebugLevel;
00059 
00060 static REAL8 eps = 1.e-14;      /* maximal REAL8 roundoff-error (used for determining if some number is an INT) */
00061 
00062 /* ----- DEFINES ----- */
00063 #define GPS2REAL(gps) ((gps).gpsSeconds + 1e-9 * (gps).gpsNanoSeconds )
00064 
00065 /*---------- Global variables ----------*/
00066 /* empty init-structs for the types defined in here */
00067 static LALStatus emptyStatus;   
00068 static SpinOrbitCWParamStruc emptyCWParams;
00069 static CoherentGW emptySignal;
00070 
00071 const PulsarSignalParams empty_PulsarSignalParams;
00072 const SFTParams empty_SFTParams;
00073 const SFTandSignalParams empty_SFTandSignalParams;
00074 
00075 
00076 /** Generate a time-series at the detector for a given pulsar.
00077  */
00078 void
00079 LALGeneratePulsarSignal (LALStatus *status, 
00080                          REAL4TimeSeries **signal,         /**< output time-series */
00081                          const PulsarSignalParams *params) /**< input params */
00082 { 
00083   SpinOrbitCWParamStruc sourceParams = emptyCWParams; 
00084   CoherentGW sourceSignal = emptySignal;
00085   DetectorResponse detector;
00086   REAL8 SSBduration;
00087   LIGOTimeGPS t0, t1, tmpTime;
00088   REAL4TimeSeries *output;
00089   UINT4 i;
00090 
00091   INITSTATUS( status, "LALGeneratePulsarSignal", GENERATEPULSARSIGNALC );
00092   ATTATCHSTATUSPTR (status);
00093 
00094   ASSERT (signal != NULL, status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL);
00095   ASSERT (*signal == NULL, status,  GENERATEPULSARSIGNALH_ENONULL,  GENERATEPULSARSIGNALH_MSGENONULL);
00096 
00097   /*----------------------------------------------------------------------
00098    *
00099    * First call GenerateSpinOrbitCW() to generate the source-signal
00100    *
00101    *----------------------------------------------------------------------*/
00102   sourceParams.psi = params->pulsar.psi;
00103   sourceParams.aPlus = params->pulsar.aPlus;
00104   sourceParams.aCross = params->pulsar.aCross;
00105   sourceParams.phi0 = params->pulsar.phi0;
00106   sourceParams.f0 = params->pulsar.f0;
00107   /* set source position: make sure it's "normalized", i.e. [0<=alpha<2pi]x[-pi/2<=delta<=pi/2] */
00108   TRY( LALNormalizeSkyPosition(status->statusPtr, &(sourceParams.position), &(params->pulsar.position)), status);
00109 
00110   /* if pulsar is in binary-orbit, set binary parameters */
00111   if (params->orbit)
00112     {
00113       /*------------------------------------------------------------ */
00114       /* temporary fix for comparison with Chris' code */
00115       /*
00116         TRY (LALConvertGPS2SSB (status->statusPtr, &tmpTime, params->orbit->orbitEpoch, params), status);
00117         sourceParams.orbitEpoch = tmpTime;
00118       */
00119       sourceParams.orbitEpoch =  params->orbit->tp;
00120       sourceParams.omega = params->orbit->argp;
00121       /* ------- here we do conversion to Teviets preferred variables -------*/
00122       sourceParams.rPeriNorm = params->orbit->asini*(1.0 - params->orbit->ecc);
00123       sourceParams.oneMinusEcc = 1.0 - params->orbit->ecc;
00124       sourceParams.angularSpeed = (LAL_TWOPI/params->orbit->period)*sqrt((1.0 +params->orbit->ecc)/pow((1.0 - params->orbit->ecc),3.0));
00125     }
00126   else
00127     sourceParams.rPeriNorm = 0.0;               /* this defines an isolated pulsar */
00128 
00129   if ( params->pulsar.refTime.gpsSeconds != 0)
00130     sourceParams.spinEpoch = params->pulsar.refTime;   /* pulsar reference-time in SSB frame (TDB) */
00131   else  /* if not given: use startTime converted to SSB as tRef ! */
00132     {
00133       TRY ( LALConvertGPS2SSB(status->statusPtr, &tmpTime, params->startTimeGPS, params), status);
00134       sourceParams.spinEpoch = tmpTime;
00135     }
00136 
00137   /* sampling-timestep and length for source-parameters */
00138   /* in seconds; hardcoded; was 60s in makefakedata_v2,
00139    * but for fast binaries (e.g. SCO-X1) we need faster sampling 
00140    * This does not seem to affect performance a lot (~4% in makefakedata),
00141    * but we'll nevertheless make this sampling faster for binaries and slower
00142    * for isolated pulsars */
00143   if (params->orbit)
00144     sourceParams.deltaT = 5;    /* for binaries */
00145   else
00146     sourceParams.deltaT = 60;   /* for isolated pulsars */
00147 
00148   /* start-time in SSB time */
00149   TRY (LALConvertGPS2SSB(status->statusPtr, &t0, params->startTimeGPS, params), status);
00150   t0.gpsSeconds -= (UINT4)sourceParams.deltaT; /* start one time-step earlier to be safe */
00151 
00152   /* end time in SSB */
00153   t1 = params->startTimeGPS;
00154   TRY ( LALAddFloatToGPS(status->statusPtr, &t1, &t1, params->duration), status);
00155   TRY (LALConvertGPS2SSB(status->statusPtr, &t1, t1, params), status);   /* convert time to SSB */
00156 
00157   /* get duration of source-signal */
00158   TRY (LALDeltaFloatGPS(status->statusPtr, &SSBduration, &t1, &t0), status);
00159   SSBduration += 2.0 * sourceParams.deltaT; /* add two time-steps to be safe */
00160 
00161   sourceParams.epoch = t0; 
00162   sourceParams.length = (UINT4) ceil( SSBduration / sourceParams.deltaT );
00163 
00164   /* we use frequency-spindowns, but GenerateSpinOrbitCW wants f_k = fkdot / (f0 * k!) */
00165   sourceParams.f = NULL;
00166   if (params->pulsar.spindown)
00167     {
00168       UINT4 kFact = 1;
00169       TRY ( LALDCreateVector(status->statusPtr, &(sourceParams.f), params->pulsar.spindown->length), status);
00170       for (i=0; i < sourceParams.f->length; i++ )
00171         {
00172           sourceParams.f->data[i] = params->pulsar.spindown->data[i] / (kFact * params->pulsar.f0);
00173           kFact *= i + 2;
00174         }
00175     }
00176 
00177   /* finally, call the function to generate the source waveform */
00178   TRY ( LALGenerateSpinOrbitCW(status->statusPtr, &sourceSignal, &sourceParams), status);
00179 
00180   /* free spindown-vector right away, so we don't forget */
00181   if (sourceParams.f) {
00182     TRY ( LALDDestroyVector(status->statusPtr, &(sourceParams.f) ), status);
00183   }
00184 
00185   /* check that sampling interval was short enough */
00186   if ( sourceParams.dfdt > 2.0 )  /* taken from makefakedata_v2 */
00187     {
00188       LALPrintError ("GenerateSpinOrbitCW() returned df*dt = %f > 2.0", sourceParams.dfdt);
00189       ABORT (status, GENERATEPULSARSIGNALH_ESAMPLING, GENERATEPULSARSIGNALH_MSGESAMPLING);
00190     }
00191 
00192   /*----------------------------------------------------------------------
00193    *
00194    * Now call the function to translate the source-signal into a (heterodyned)
00195    * signal at the detector 
00196    *
00197    *----------------------------------------------------------------------*/
00198   /* first set up the detector-response */
00199   detector.transfer = params->transfer;
00200   detector.site = params->site;
00201   detector.ephemerides = params->ephemerides;
00202 
00203   /* *contrary* to makefakedata_v2, we use the GPS start-time of the timeseries as
00204    * the heterodyning epoch, in order to make sure that the global phase of the 
00205    * SFTs it correct: this is necessary to allow correct parameter-estimation on the 
00206    * pulsar signal-phase in heterodyned SFTs 
00207    */
00208   detector.heterodyneEpoch = params->startTimeGPS;
00209 
00210   /* ok, we  need to prepare the output time-series */
00211   if ( (output = LALCalloc (1, sizeof (*output) )) == NULL) {
00212     ABORT (status,  GENERATEPULSARSIGNALH_EMEM,  GENERATEPULSARSIGNALH_MSGEMEM);
00213   }
00214 
00215   /* NOTE: a timeseries of length N*dT has no timestep at N*dT !! (convention) */
00216   LALCreateVector(status->statusPtr, &(output->data), (UINT4) ceil( params->samplingRate * params->duration) );
00217   BEGINFAIL(status) {
00218     LALFree (output);
00219   } ENDFAIL(status);
00220 
00221   output->deltaT = 1.0 / params->samplingRate;
00222   output->f0 = params->fHeterodyne;
00223   output->epoch = params->startTimeGPS;
00224 
00225   
00226   TRY ( LALSimulateCoherentGW(status->statusPtr, output, &sourceSignal, &detector ), status );
00227 
00228   /* set 'name'-field of timeseries to contain the right "channel prefix" for the detector */
00229   {
00230     CHAR *name = XLALGetChannelPrefix ( params->site->frDetector.name );
00231     if ( name == NULL ) {
00232       ABORT (status, GENERATEPULSARSIGNALH_EDETECTOR, GENERATEPULSARSIGNALH_MSGEDETECTOR );
00233     }
00234     strcpy ( output->name, name );
00235     LALFree ( name );
00236   }
00237                                
00238   /*----------------------------------------------------------------------*/
00239   /* Free all allocated memory that is not returned */
00240   TRY (LALSDestroyVectorSequence( status->statusPtr, &(sourceSignal.a->data)), status);
00241   LALFree( sourceSignal.a );
00242   TRY (LALSDestroyVector(status->statusPtr, &(sourceSignal.f->data ) ), status);
00243   LALFree(sourceSignal.f);
00244   TRY (LALDDestroyVector(status->statusPtr, &(sourceSignal.phi->data )), status);
00245   LALFree(sourceSignal.phi);
00246 
00247   *signal = output;
00248 
00249   DETATCHSTATUSPTR (status);
00250   RETURN (status);
00251 
00252 } /* LALGeneratePulsarSignal() */
00253 
00254 
00255 
00256 /** Turn the given time-series into (v2-)SFTs and add noise if given.
00257  */
00258 void
00259 LALSignalToSFTs (LALStatus *status, 
00260                  SFTVector **outputSFTs,        /**< [out] SFT-vector */
00261                  const REAL4TimeSeries *signal, /**< input time-series */
00262                  const SFTParams *params)       /**< params for output-SFTs */
00263 {
00264   UINT4 numSFTs;                        /* number of SFTs */
00265   UINT4 numTimesteps;                   /* number of time-samples in an Tsft */
00266   UINT4 numBins;                        /* number of frequency-bins in SFT */
00267   UINT4 iSFT;
00268   UINT4 idatabin;
00269   REAL8 Band, f0, deltaF, dt;
00270   LIGOTimeGPSVector *timestamps = NULL;
00271   REAL4Vector timeStretch = {0,0};
00272   LIGOTimeGPS tStart;                   /* start time of input time-series */
00273   LIGOTimeGPS tLast;                    /* start-time of last _possible_ SFT */
00274   LIGOTimeGPS tmpTime;
00275   REAL8 duration, delay;
00276   UINT4 index0n;                        /* first frequency-bin to use from noise-SFT */
00277   SFTtype *thisSFT, *thisNoiseSFT;      /* SFT-pointers */
00278   SFTVector *sftvect = NULL;            /* return value. For better readability */
00279   UINT4 j;
00280   RealFFTPlan *pfwd = NULL;             /* FFTW plan */
00281   LIGOTimeGPS tPrev;                    /* real timstamp of previous SFT */
00282   UINT4 totalIndex;                     /* timestep-index to start next FFT from */
00283   INT4 relIndexShift;                   /* relative index-shift from previous SFT (number of timesteps) */
00284 
00285   INITSTATUS( status, "LALSignalToSFTs", GENERATEPULSARSIGNALC);
00286   ATTATCHSTATUSPTR( status );
00287   
00288   ASSERT (outputSFTs != NULL, status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL);
00289   ASSERT (*outputSFTs == NULL, status,  GENERATEPULSARSIGNALH_ENONULL,  GENERATEPULSARSIGNALH_MSGENONULL);
00290   ASSERT (signal != NULL, status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL);
00291   ASSERT (params != NULL, status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL);
00292 
00293   /* UPGRADING switch: complain loudly if user didn't set 'make_v2SFTs' to encourage upgrading */
00294   if (  params-> make_v2SFTs != 1 )
00295     {
00296       fprintf (stderr, "\n********************************************************************************\n\n");
00297       fprintf (stderr, "     WARNING: LALSignalToSFTs() now returns properly *v2-normalized* SFTs\n");
00298       fprintf (stderr, "    (see  http://www.lsc-group.phys.uwm.edu/lal/slug/nightly/doxygen/html/group__SFTfileIO.html \n");
00299       fprintf (stderr, "    for more details on what that means).\n\n");
00300       fprintf (stderr, "    Please adapt your code correspondingly and set SFTParams.make_v2SFTs=1 to acknowledge this.\n");
00301       fprintf (stderr, "    This parameter and warning will be removed once the transition is complete.\n\n");
00302       fprintf (stderr, "********************************************************************************\n\n");
00303     }
00304 
00305   f0 = signal->f0;                              /* lowest frequency */
00306   dt = signal->deltaT;          /* timeseries timestep */
00307   Band = 1.0 / (2.0 * dt);              /* NOTE: frequency-band is determined by sampling-rate! */
00308   deltaF = 1.0 / params->Tsft;                  /* frequency-resolution */
00309 
00310   /* if noiseSFTs are given: check they are consistent with signal! */
00311   if (params->noiseSFTs) {
00312     TRY (checkNoiseSFTs(status->statusPtr, params->noiseSFTs, f0, f0 + Band, deltaF), status);
00313   }
00314     
00315   /* make sure that number of timesamples/SFT is an integer (up to possible rounding errors) */
00316   {
00317     REAL8 timesteps = params->Tsft / dt;                /* this is a float!*/
00318     numTimesteps = (UINT4) (timesteps + 0.5);           /* round to closest int */
00319     ASSERT ( fabs(timesteps - numTimesteps)/timesteps < eps, status, 
00320              GENERATEPULSARSIGNALH_EINCONSBAND, GENERATEPULSARSIGNALH_MSGEINCONSBAND);
00321   }
00322   /* Prepare FFT: compute plan for FFTW */
00323   TRY (LALCreateForwardRealFFTPlan(status->statusPtr, &pfwd, numTimesteps, 0), status);         
00324 
00325   /* get some info about time-series */
00326   tStart = signal->epoch;                                       /* start-time of time-series */
00327 
00328   /* get last possible start-time for an SFT */
00329   duration =  (UINT4) (1.0* signal->data->length * dt + 0.5); /* total duration rounded to seconds */
00330   TRY ( LALAddFloatToGPS(status->statusPtr, &tLast, &tStart, duration - params->Tsft), status);
00331 
00332   /* for simplicity we _always_ work with timestamps. 
00333    * Therefore, we have to generate them now if none have been provided by the user. */
00334   if (params->timestamps == NULL) 
00335     {
00336       LIGOTimeGPS lastTs;
00337       TRY(LALMakeTimestamps(status->statusPtr, &timestamps, tStart, duration, params->Tsft ), status);
00338       /* see if the last timestamp is valid (can fit a full SFT in?), if not, drop it */
00339       lastTs = timestamps->data[timestamps->length-1];
00340       if ( GPS2REAL(lastTs) > GPS2REAL(tLast) )
00341         timestamps->length --;
00342     }
00343   else  /* if given, use those, and check they are valid */
00344     {
00345       timestamps = params->timestamps;
00346     }
00347   
00348   /* check that all timestamps lie within [tStart, tLast] */
00349   if ( check_timestamp_bounds(timestamps, tStart, tLast) != 0) {
00350     ABORT (status, GENERATEPULSARSIGNALH_ETIMEBOUND, GENERATEPULSARSIGNALH_MSGETIMEBOUND);
00351   }
00352 
00353   /* check that we have the right number of noise-SFTs */
00354   if (  params->noiseSFTs && ( params->noiseSFTs->length != timestamps->length)  ) {
00355     ABORT ( status, GENERATEPULSARSIGNALH_ENUMSFTS,  GENERATEPULSARSIGNALH_MSGENUMSFTS );
00356   }
00357 
00358   /* prepare SFT-vector for return */
00359   numSFTs = timestamps->length;                 /* number of SFTs to produce */
00360   numBins = (UINT4)(numTimesteps/2) + 1;                /* number of frequency-bins per SFT */
00361 
00362   LALCreateSFTVector (status->statusPtr, &sftvect, numSFTs, numBins);
00363   BEGINFAIL (status) {
00364     if (params->timestamps == NULL)
00365       LALDestroyTimestampVector(status->statusPtr, &timestamps);
00366   } ENDFAIL (status);
00367 
00368   tPrev = tStart;       /* initialize */
00369   totalIndex = 0;       /* start from first timestep by default */
00370 
00371   /* main loop: apply FFT the requested time-stretches */
00372   for (iSFT = 0; iSFT < numSFTs; iSFT++)
00373     {
00374       REAL8 realDelay;
00375 
00376       thisSFT = &(sftvect->data[iSFT]); /* point to current SFT-slot */
00377 
00378       /* find the start-bin for this SFT in the time-series */
00379       TRY ( LALDeltaFloatGPS(status->statusPtr, &delay, &(timestamps->data[iSFT]), &tPrev), status);
00380       /* round properly: picks *closest* timestep (==> "nudging") !!  */
00381       relIndexShift = (INT4) (delay / signal->deltaT + 0.5);    
00382       totalIndex += relIndexShift;
00383 
00384       timeStretch.length = numTimesteps;
00385       timeStretch.data = signal->data->data + totalIndex; /* point to the right sample-bin */
00386 
00387       /* fill the header of the i'th output SFT */
00388       realDelay = (REAL4)(relIndexShift * signal->deltaT);  /* avoid rounding-errors*/
00389       TRY (LALAddFloatToGPS(status->statusPtr, &tmpTime,&tPrev, realDelay),status);
00390 
00391       strcpy ( thisSFT->name, signal->name );
00392       /* set the ACTUAL timestamp! (can be different from requested one ==> "nudging") */
00393       thisSFT->epoch = tmpTime;                 
00394       thisSFT->f0 = signal->f0;                 /* minimum frequency */
00395       thisSFT->deltaF = 1.0 / params->Tsft;     /* frequency-spacing */
00396 
00397       tPrev = tmpTime;                          /* prepare next loop */
00398 
00399       /* ok, issue at least a warning if we have "nudged" an SFT-timestamp */
00400       if (lalDebugLevel > 0)
00401         {
00402           REAL8 diff;
00403           TRY (LALDeltaFloatGPS(status->statusPtr, &diff, &(timestamps->data[iSFT]),&tmpTime),status);
00404           if (diff != 0) 
00405             {
00406               LALPrintError ("Warning: timestamp %d had to be 'nudged' by %e s to fit"
00407                              "with time-series\n", iSFT, diff);
00408               /* double check if magnitude of nudging seems reasonable .. */
00409               if ( fabs(diff) >= signal->deltaT ) 
00410                 {
00411                   LALPrintError ("WARNING: nudged by more than deltaT=%e... "
00412                                  "this sounds wrong! (We better stop)\n");
00413                   ABORT (status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL );
00414                 }
00415             } /* if nudging */
00416         } /* if lalDebugLevel */
00417 
00418       /* Now window the current time series stretch, if necessary */
00419       if ( params->window ) {
00420           const float A = 1.0 / sqrt(params->window->sumofsquares / params->window->data->length);
00421           for( idatabin = 0; idatabin < timeStretch.length; idatabin++ ) {
00422               timeStretch.data[idatabin] *= A * params->window->data->data[idatabin];
00423           }
00424       }
00425 
00426       /* the central step: FFT the ith time-stretch into an SFT-slot */
00427       LALForwardRealFFT(status->statusPtr, thisSFT->data, &timeStretch, pfwd);
00428       BEGINFAIL(status) {
00429         LALDestroySFTVector(status->statusPtr, &sftvect);
00430       } ENDFAIL(status);
00431 
00432 
00433       /* normalize DFT-data to conform to v2 ( ie. COMPLEX8FrequencySeries ) specification ==> multiply DFT by dt */
00434       {
00435         UINT4 i;
00436         COMPLEX8 *data = &( thisSFT->data->data[0] );
00437         for ( i=0; i < numBins ; i ++ )
00438         {
00439           data->re *= dt;
00440           data->im *= dt;
00441           data ++;
00442         } /* for i < numBins */
00443       } /* normalize data */
00444       
00445       /* correct heterodyning-phase, IF NECESSARY */
00446       if ( ( (INT4)signal->f0 != signal->f0  )
00447            || (signal->epoch.gpsNanoSeconds != 0)
00448            || (thisSFT->epoch.gpsNanoSeconds != 0) )
00449         {
00450           /* theterodyne = signal->epoch!*/
00451           correct_phase(status->statusPtr, thisSFT, signal->epoch);
00452           BEGINFAIL (status) {
00453             LALDestroySFTVector(status->statusPtr, &sftvect);
00454           } ENDFAIL (status);
00455         } /* if phase-correction necessary */
00456 
00457       /* Now add the noise-SFTs if given */
00458       if (params->noiseSFTs)
00459         {
00460           COMPLEX8 *data, *noise;
00461           thisNoiseSFT = &(params->noiseSFTs->data[iSFT]);
00462           index0n = (UINT4) ((thisSFT->f0 - thisNoiseSFT->f0) / thisSFT->deltaF);
00463 
00464           data = &(thisSFT->data->data[0]);
00465           noise = &(thisNoiseSFT->data->data[index0n]);
00466           for (j=0; j < numBins; j++)
00467             {
00468               data->re += noise->re;
00469               data->im += noise->im;
00470               data++;
00471               noise++;
00472             } /* for j < numBins */
00473         
00474         } /* if noiseSFTs */
00475 
00476     } /* for iSFT < numSFTs */ 
00477 
00478   /* free stuff */
00479   LALDestroyRealFFTPlan(status->statusPtr, &pfwd);
00480 
00481   /* did we get timestamps or did we make them? */
00482   if (params->timestamps == NULL)
00483     {
00484       LALFree(timestamps->data);
00485       LALFree(timestamps);
00486       timestamps = NULL;
00487     }
00488 
00489   *outputSFTs = sftvect;
00490 
00491   DETATCHSTATUSPTR( status );
00492   RETURN (status);
00493 
00494 } /* LALSignalToSFTs() */
00495 
00496 
00497 
00498 /* 07/14/04 gam */
00499 /**
00500  * Wrapper for LALComputeSky() and  LALComputeDetAMResponse() that finds the sky
00501  * constants and \f$F_+\f$ and \f$F_\times\f$ for use with LALFastGeneratePulsarSFTs().
00502  *  Uses the output of LALComputeSkyAndZeroPsiAMResponse() and the same inputs as
00503  * LALGeneratePulsarSignal() and LALSignalToSFTs().
00504  * This function used LALComputeSkyBinary() if params->pSigParams->orbit is not
00505  * NULL, else it uses LALComputeSky() to find the skyConsts.
00506  * NOTE THAT THIS FUNCTION COMPUTES \f$F_+\f$ and \f$F_x\f$ for ZERO Psi!!!
00507  * LALFastGeneratePulsarSFTs() used these to find \f$F_+\f$ and \f$F_x\f$ for NONZERO Psi.
00508  */
00509 void
00510 LALComputeSkyAndZeroPsiAMResponse (LALStatus *status,
00511                                    SkyConstAndZeroPsiAMResponse *output,        /**< output */
00512                                    const SFTandSignalParams *params)            /**< params */
00513 {
00514   INT4 i;
00515   INT4 numSFTs;                      /* number of SFTs */
00516   BarycenterInput baryinput;         /* Stores detector location and other barycentering data */  
00517   CSParams *csParams   = NULL;       /* ComputeSky parameters */
00518   CSBParams *csbParams = NULL;       /* ComputeSkyBinary parameters */
00519   SkyPosition tmp;    
00520   EarthState earth;
00521   EmissionTime emit;
00522   LALDetAMResponse response;  /* output of LALComputeDetAMResponse */
00523   LALDetAndSource      *das;  /* input for LALComputeDetAMResponse */
00524   LALGPSandAcc   timeAndAcc;  /* input for LALComputeDetAMResponse */
00525   REAL8 halfTsft;             /* half the time of one SFT */
00526   LIGOTimeGPS midTS;          /* midpoint time for an SFT */
00527 
00528   INITSTATUS( status, "LALComputeSkyAndZeroPsiAMResponse", GENERATEPULSARSIGNALC);
00529   ATTATCHSTATUSPTR( status );
00530   
00531   numSFTs = params->pSFTParams->timestamps->length; /* number of SFTs */
00532   halfTsft = 0.5*params->pSFTParams->Tsft;          /* half the time of one SFT */
00533 
00534   /* setup baryinput for LALComputeSky */
00535   baryinput.site = *(params->pSigParams->site);
00536   /* account for a quirk in LALBarycenter(): -> see documentation of type BarycenterInput */
00537   baryinput.site.location[0] /= LAL_C_SI;
00538   baryinput.site.location[1] /= LAL_C_SI;
00539   baryinput.site.location[2] /= LAL_C_SI;
00540   if (params->pSigParams->pulsar.position.system != COORDINATESYSTEM_EQUATORIAL) {
00541       ABORT (status, GENERATEPULSARSIGNALH_EBADCOORDS, GENERATEPULSARSIGNALH_MSGEBADCOORDS);
00542   }
00543   TRY( LALNormalizeSkyPosition (status->statusPtr, &tmp, &(params->pSigParams->pulsar.position)), status);
00544   baryinput.alpha = tmp.longitude;
00545   baryinput.delta = tmp.latitude;
00546   baryinput.dInv = 0.e0;      /* following makefakedata_v2 */
00547   
00548   if (params->pSigParams->orbit) {
00549     /* LALComputeSkyBinary parameters */
00550     csbParams=(CSBParams *)LALMalloc(sizeof(CSBParams));
00551     csbParams->skyPos=(REAL8 *)LALMalloc(2*sizeof(REAL8));
00552     if (params->pSigParams->pulsar.spindown) {
00553        csbParams->spinDwnOrder=params->pSigParams->pulsar.spindown->length;
00554     } else {
00555        csbParams->spinDwnOrder=0;
00556     }
00557     csbParams->mObsSFT=numSFTs;
00558     csbParams->tSFT=params->pSFTParams->Tsft;
00559     csbParams->tGPS=params->pSFTParams->timestamps->data;
00560     csbParams->skyPos[0]=params->pSigParams->pulsar.position.longitude;
00561     csbParams->skyPos[1]=params->pSigParams->pulsar.position.latitude;
00562     csbParams->OrbitalEccentricity = params->pSigParams->orbit->ecc; /* Orbital eccentricy */
00563     csbParams->ArgPeriapse = params->pSigParams->orbit->argp;       /* argument of periapsis (radians) */
00564     csbParams->TperiapseSSB = params->pSigParams->orbit->tp; /* time of periapsis passage (in SSB) */
00565     /* compute semi-major axis and orbital period */
00566     csbParams->SemiMajorAxis = params->pSigParams->orbit->asini;
00567     csbParams->OrbitalPeriod = params->pSigParams->orbit->period;
00568     csbParams->baryinput=&baryinput;
00569     csbParams->emit = &emit;
00570     csbParams->earth = &earth;
00571     csbParams->edat=params->pSigParams->ephemerides;
00572 
00573     /* Call LALComputeSkyBinary */
00574     TRY ( LALComputeSkyBinary (status->statusPtr, output->skyConst, 0, csbParams), status);
00575     LALFree(csbParams->skyPos);
00576     LALFree(csbParams);
00577   } else {
00578     /* LALComputeSky parameters */
00579     csParams=(CSParams *)LALMalloc(sizeof(CSParams));
00580     csParams->tGPS=params->pSFTParams->timestamps->data;
00581     csParams->skyPos=(REAL8 *)LALMalloc(2*sizeof(REAL8));
00582     csParams->mObsSFT=numSFTs;
00583     csParams->tSFT=params->pSFTParams->Tsft;
00584     csParams->edat=params->pSigParams->ephemerides;
00585     csParams->baryinput=&baryinput;
00586     if (params->pSigParams->pulsar.spindown) {
00587        csParams->spinDwnOrder=params->pSigParams->pulsar.spindown->length;
00588     } else {
00589        csParams->spinDwnOrder=0;
00590     }
00591     csParams->skyPos[0]=params->pSigParams->pulsar.position.longitude;
00592     csParams->skyPos[1]=params->pSigParams->pulsar.position.latitude;
00593     csParams->earth = &earth;
00594     csParams->emit = &emit;
00595 
00596     /* Call LALComputeSky */
00597     TRY ( LALComputeSky (status->statusPtr, output->skyConst, 0, csParams), status);
00598     LALFree(csParams->skyPos);
00599     LALFree(csParams);
00600   }
00601   
00602   /* Set up das, the Detector and Source info */
00603   das = (LALDetAndSource *)LALMalloc(sizeof(LALDetAndSource));
00604   das->pSource = (LALSource *)LALMalloc(sizeof(LALSource));
00605   das->pDetector = params->pSigParams->site;
00606   das->pSource->equatorialCoords.latitude = params->pSigParams->pulsar.position.latitude;
00607   das->pSource->equatorialCoords.longitude = params->pSigParams->pulsar.position.longitude;
00608   das->pSource->orientation = 0.0;  /* NOTE THIS FUNCTION COMPUTE F_+ and F_x for ZERO Psi!!! */
00609   das->pSource->equatorialCoords.system = params->pSigParams->pulsar.position.system;
00610   timeAndAcc.accuracy=LALLEAPSEC_STRICT;
00611  
00612   /* loop that calls LALComputeDetAMResponse to find F_+ and F_x at the midpoint of each SFT for ZERO Psi */
00613   for(i=0; i<numSFTs; i++) {
00614       /* Find mid point from timestamp, half way through SFT. */
00615       TRY ( LALAddFloatToGPS (status->statusPtr, &midTS, &(params->pSFTParams->timestamps->data[i]), halfTsft), status);
00616       timeAndAcc.gps=midTS;
00617       TRY ( LALComputeDetAMResponse(status->statusPtr, &response, das, &timeAndAcc), status);
00618       output->fPlusZeroPsi[i] = response.plus;
00619       output->fCrossZeroPsi[i] = response.cross;
00620   }
00621   LALFree(das->pSource);
00622   LALFree(das);
00623 
00624   DETATCHSTATUSPTR( status );
00625   RETURN (status);
00626 } /* LALComputeSkyAndZeroPsiAMResponse */
00627 
00628 /* 07/14/04 gam */
00629 /** 
00630  * Fast generation of Fake SFTs for a pure pulsar signal.
00631  * Uses the output of LALComputeSkyAndZeroPsiAMResponse and the same inputs
00632  * as LALGeneratePulsarSignal and LALSignalToSFTs.  The fake signal is
00633  * Taylor expanded to first order about the midpoint time of each SFT.
00634  * Analytic expressions are used to find each SFT
00635  */
00636 void 
00637 LALFastGeneratePulsarSFTs (LALStatus *status,
00638                            SFTVector **outputSFTs,
00639                            const SkyConstAndZeroPsiAMResponse *input,
00640                            const SFTandSignalParams *params)
00641 {
00642   INT4 numSFTs;                 /* number of SFTs */
00643   REAL4 N;                      /* N = number of time-samples that would have been used to generate SFTs directly */
00644   INT4 iSFT;                    /* index that gives which SFT in an SFTVector */
00645   INT4 SFTlen;                  /* number of frequency bins in an SFT */
00646   REAL8 tSFT, f0, band, f0Signal, deltaF;
00647   REAL4 fPlus, fCross, psi, phi0Signal;
00648   /* REAL4 halfAPlus, halfACross, cosPsi, sinPsi; */ /* 10/12/04 gam */
00649   REAL4 halfAPlus, halfACross, cos2Psi, sin2Psi;
00650   REAL8 real