LALPulsar  6.1.0.1-fe68b98
SimulatePulsarSignal.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004, 2005 Reinhard Prix
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with with program; see the file COPYING. If not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  */
19 
20 #include <lal/AVFactories.h>
21 #include <lal/TimeSeries.h>
22 #include <lal/GeneratePulsarSignal.h>
23 #include <lal/ComputeFstat.h>
24 #include <lal/ExtrapolatePulsarSpins.h>
25 #include <lal/SimulatePulsarSignal.h>
26 
27 /*----- Macros ----- */
28 
29 /** Simple Euklidean scalar product for two 3-dim vectors in cartesian coords */
30 #define SCALAR(u,v) ((u)[0]*(v)[0] + (u)[1]*(v)[1] + (u)[2]*(v)[2])
31 
32 // ----- global variables, initializers
34 
35 
36 // ----- function definitions
37 
38 
39 /**
40  * Simulate a pulsar signal to best accuracy possible.
41  * \author Reinhard Prix
42  * \date 2005
43  *
44  * The motivation for this function is to provide functions to
45  * simulate pulsar signals <em>with the best possible accuracy</em>,
46  * i.e. using no approximations, contrary to LALGeneratePulsarSignal().
47  *
48  * Obviously this is not meant as a fast code to be used in a Monte-Carlo
49  * simulation, but rather as a <em>reference</em> to compare other (faster)
50  * functions agains, in order to be able to gauge the quality of a given
51  * signal-generation routine.
52  *
53  * We want to calculate \f$ h(t) \f$ , given by
54  * \f[
55  * h(t) = F_+(t)\, h_+(t) + F_\times(t) \,h_\times(t)\,,
56  * \f]
57  * where \f$ F_+ \f$ and \f$ F_x \f$ are called the <em>beam-pattern</em> functions,
58  * which depend of the wave polarization \f$ \psi \f$ ,
59  * the source position \f$ \alpha \f$ , \f$ \delta \f$ and the detector position and
60  * orientation ( \f$ \gamma \f$ , \f$ \lambda \f$ , \f$ L \f$ and \f$ \xi \f$ ). The expressions for
61  * the beam-pattern functions are given in \cite JKS98 , which we write as
62  * \f{eqnarray}{
63  * F_+(t) = \sin \zeta \cos 2\psi \, a(t) + \sin \zeta \sin 2\psi \, b(t)\,,\\
64  * F_\times(t) = \sin\zeta \cos 2\psi \,b(t) - \sin\zeta \sin 2\psi \, a(t) \,,
65  * \f}
66  * where \f$ \zeta \f$ is the angle between the interferometer arms, and
67  * \f{eqnarray}{
68  * a(t) &=& a_1 \cos[ 2 (\alpha - T)) ] + a_2 \sin[ 2(\alpha - T)]
69  * + a_3 \cos[ \alpha - T ] + a_4 \sin [ \alpha - T ] + a_5\,,\\
70  * b(t) &=& b_1 \cos[ 2(\alpha - T)] + b_2 \sin[ 2(\alpha - T) ]
71  * + b_3 \cos[ \alpha - T ] + b_4 \sin[ \alpha - T]\,,
72  * \f}
73  * where \f$ T \f$ is the local (mean) sidereal time of the detector, and the
74  * time-independent coefficients \f$ a_i \f$ and \f$ b_i \f$ are given by
75  * \f{eqnarray}{
76  * a_1 &=& \frac{1}{16} \sin 2\gamma \,(3- \cos 2\lambda)\,(3 - \cos 2\delta)\,,\\
77  * a_2 &=& -\frac{1}{4}\cos 2\gamma \,\sin \lambda \,(3 - \cos 2\delta) \,,\\
78  * a_3 &=& \frac{1}{4} \sin 2\gamma \,\sin 2\lambda \,\sin 2\delta \,\\
79  * a_4 &=& -\frac{1}{2} \cos 2\gamma \,\cos \lambda \,\sin 2 \delta\,,\\
80  * a_5 &=& \frac{3}{4} \sin 2\gamma \, \cos^2 \lambda \,\cos^2 \delta\,,
81  * \f}
82  * and
83  * \f{eqnarray}{
84  * b_1 &=& \cos 2\gamma \,\sin \lambda \,\sin \delta\,,\\
85  * b_2 &=& \frac{1}{4} \sin 2\gamma \,(3-\cos 2\lambda)\, \sin \delta\,,\\
86  * b_3 &=& \cos 2\gamma \,\cos \lambda \,\cos\delta \,, \\
87  * b_4 &=& \frac{1}{2} \sin2\gamma \,\sin 2\lambda \,\cos\delta\,,
88  * \f}
89  *
90  * The source model considered is a plane-wave
91  * \f{eqnarray}{
92  * h_+(t) &=& A_+\, \cos \Psi(t)\,,\\
93  * h_\times(t) &=& A_\times \, \sin \Psi(t)\,,
94  * \f}
95  * where the wave-phase is \f$ \Psi(t) = \Phi_0 + \Phi(t) \f$ , and for an
96  * isolated pulsar we have
97  * \f{equation}{
98  * \Phi(t) = 2\pi \left[\sum_{s=0} \frac{f^{(s)}(\tau_\mathrm{ref})}{
99  * (s+1)!} \left( \tau(t) - \tau_\mathrm{ref} \right)^{s+1} \right]\,,
100  * \f}
101  * where \f$ \tau_\mathrm{ref} \f$ is the "reference time" for the definition
102  * of the pulsar-parameters \f$ f^{(s)} \f$ in the solar-system barycenter
103  * (SSB), and \f$ \tau(t) \f$ is the SSB-time of the phase arriving at the
104  * detector at UTC-time \f$ t \f$ , which depends on the source-position
105  * ( \f$ \alpha \f$ , \f$ \delta \f$ ) and the detector-position, namely
106  * \f{equation}{
107  * \tau (t) = t + \frac{ \vec{r}(t)\cdot\vec{n}}{c}\,,
108  * \f}
109  * where \f$ \vec{r}(t) \f$ is the vector from SSB to the detector, and \f$ \vec{n} \f$
110  * is the unit-vector pointing <em>to</em> the source.
111  *
112  * This is a standalone "clean-room" implementation using no other
113  * outside-functions <em>except</em> for LALGPStoLMST1() to calculate
114  * the local (mean) sidereal time at the detector for given GPS-time,
115  * (which I double-checked with an independent Mathematica script),
116  * and and XLALBarycenter() to calculate \f$ \tau(t) \f$ .
117  *
118  * NOTE: currently only isolated pulsars are supported
119  *
120  * NOTE2: we don't really use the highest possible accuracy right now,
121  * as we blatently neglect all relativistic timing effects (i.e. using dT=v.n/c)
122  *
123  * NOTE3: no heterodyning is performed here, the time-series is generated and sampled
124  * at the given rate, that's all! ==> the caller needs to make sure about the
125  * right sampling rate to use (->aliasing) and do the proper post-treatment...
126  *
127  */
130 {
131  XLAL_CHECK_NULL( params != NULL, XLAL_EINVAL, "Invalid NULL input 'params'\n" );
132  XLAL_CHECK_NULL( params->samplingRate > 0, XLAL_EDOM, "Sampling rate must be positive, got samplingRate = %g\n", params->samplingRate );
133 
134  /* don't accept heterodyning frequency */
135  XLAL_CHECK_NULL( params->fHeterodyne == 0, XLAL_EINVAL, "Heterodyning frequency must be set to 0, got params->fHeterodyne = %g\n", params->fHeterodyne );
136 
137  UINT4 numSpins = 3;
138 
139  /* get timestamps of timeseries plus detector-states */
140  REAL8 dt = 1.0 / params->samplingRate;
142  XLAL_CHECK_NULL( ( timestamps = XLALMakeTimestamps( params->startTimeGPS, params->duration, dt, 0 ) ) != NULL, XLAL_EFUNC );
143 
144  UINT4 numSteps = timestamps->length;
145 
146  DetectorStateSeries *detStates = XLALGetDetectorStates( timestamps, params->site, params->ephemerides, 0 );
147  XLAL_CHECK_NULL( detStates != NULL, XLAL_EFUNC, "XLALGetDetectorStates() failed.\n" );
148 
150  timestamps = NULL;
151 
152  AMCoeffs *amcoe = XLALComputeAMCoeffs( detStates, params->pulsar.position );
153  XLAL_CHECK_NULL( amcoe != NULL, XLAL_EFUNC, "XLALComputeAMCoeffs() failed.\n" );
154 
155  /* create output timeseries (FIXME: should really know *detector* here, not just site!!) */
156  const LALFrDetector *site = &( params->site->frDetector );
158  XLAL_CHECK_NULL( channel != NULL, XLAL_EFUNC, "XLALGetChannelPrefix( %s ) failed.\n", site->name );
159 
160  REAL4TimeSeries *ts = XLALCreateREAL4TimeSeries( channel, &( detStates->data[0].tGPS ), 0, dt, &emptyUnit, numSteps );
161  XLAL_CHECK_NULL( ts != NULL, XLAL_EFUNC, "XLALCreateREAL4TimeSeries() failed.\n" );
162  XLALFree( channel );
163  channel = NULL;
164 
165  /* orientation of detector arms */
166  REAL8 xAzi = site->xArmAzimuthRadians;
167  REAL8 yAzi = site->yArmAzimuthRadians;
168  REAL8 Zeta = xAzi - yAzi;
169  if ( Zeta < 0 ) {
170  Zeta = -Zeta;
171  }
172  if ( params->site->type == LALDETECTORTYPE_CYLBAR ) {
173  Zeta = LAL_PI_2;
174  }
175  REAL8 sinZeta = sin( Zeta );
176 
177  /* get source skyposition */
178  REAL8 Alpha = params->pulsar.position.longitude;
179  REAL8 Delta = params->pulsar.position.latitude;
180  REAL8 vn[3];
181  vn[0] = cos( Delta ) * cos( Alpha );
182  vn[1] = cos( Delta ) * sin( Alpha );
183  vn[2] = sin( Delta );
184 
185  /* get spin-parameters (restricted to maximally 3 spindowns right now) */
186  REAL8 phi0 = params->pulsar.phi0;
187  REAL8 f0 = params->pulsar.f0;
188 
189  REAL8 f1dot = 0, f2dot = 0, f3dot = 0;
190  if ( params->pulsar.spindown && ( params->pulsar.spindown->length > numSpins ) ) {
191  XLAL_ERROR_NULL( XLAL_EDOM, "Currently only supports up to %d spindowns!\n", numSpins );
192  }
193  if ( params->pulsar.spindown && ( params->pulsar.spindown->length >= 3 ) ) {
194  f3dot = params->pulsar.spindown->data[2];
195  }
196  if ( params->pulsar.spindown && ( params->pulsar.spindown->length >= 2 ) ) {
197  f2dot = params->pulsar.spindown->data[1];
198  }
199  if ( params->pulsar.spindown && ( params->pulsar.spindown->length >= 1 ) ) {
200  f1dot = params->pulsar.spindown->data[0];
201  }
202 
203  /* internally we always work with refTime = startTime->SSB, therefore
204  * we need to translate the pulsar spin-params and initial phase to the
205  * startTime
206  */
207  REAL8 startTimeSSB = XLALGPSGetREAL8( &( detStates->data[0].tGPS ) ) + SCALAR( vn, detStates->data[0].rDetector );
208  REAL8 refTime;
209  if ( params->pulsar.refTime.gpsSeconds != 0 ) {
210  REAL8 refTime0 = XLALGPSGetREAL8( &( params->pulsar.refTime ) );
211  REAL8 deltaRef = startTimeSSB - refTime0;
212  LIGOTimeGPS newEpoch;
213  PulsarSpins fkdotNew;
214 
215  XLALGPSSetREAL8( &newEpoch, startTimeSSB );
216 
217  PulsarSpins XLAL_INIT_DECL( fkdotOld );
218  fkdotOld[0] = f0;
219  fkdotOld[1] = f1dot;
220  fkdotOld[2] = f2dot;
221  fkdotOld[3] = f3dot;
222  REAL8 DeltaTau = XLALGPSDiff( &newEpoch, &( params->pulsar.refTime ) );
223 
224  int ret = XLALExtrapolatePulsarSpins( fkdotNew, fkdotOld, DeltaTau );
225  XLAL_CHECK_NULL( ret == XLAL_SUCCESS, XLAL_EFUNC, "XLALExtrapolatePulsarSpins() failed.\n" );
226 
227  /* Finally, need to propagate phase */
228  phi0 += LAL_TWOPI * ( f0 * deltaRef
229  + ( 1.0 / 2.0 ) * f1dot * deltaRef * deltaRef
230  + ( 1.0 / 6.0 ) * f2dot * deltaRef * deltaRef * deltaRef
231  + ( 1.0 / 24.0 ) * f3dot * deltaRef * deltaRef * deltaRef * deltaRef
232  );
233 
234  f0 = fkdotNew[0];
235  f1dot = fkdotNew[1];
236  f2dot = fkdotNew[2];
237  f3dot = fkdotNew[3];
238 
239  refTime = startTimeSSB;
240 
241  } /* if refTime given */
242  else { /* if not given: use startTime -> SSB */
243  refTime = startTimeSSB;
244  }
245 
246  /* get 4 amplitudes A_\mu */
247  REAL8 aPlus = sinZeta * params->pulsar.aPlus;
248  REAL8 aCross = sinZeta * params->pulsar.aCross;
249  REAL8 twopsi = 2.0 * params->pulsar.psi;
250 
251  REAL8 A1 = aPlus * cos( phi0 ) * cos( twopsi ) - aCross * sin( phi0 ) * sin( twopsi );
252  REAL8 A2 = aPlus * cos( phi0 ) * sin( twopsi ) + aCross * sin( phi0 ) * cos( twopsi );
253  REAL8 A3 = -aPlus * sin( phi0 ) * cos( twopsi ) - aCross * cos( phi0 ) * sin( twopsi );
254  REAL8 A4 = -aPlus * sin( phi0 ) * sin( twopsi ) + aCross * cos( phi0 ) * cos( twopsi );
255 
256  /* main loop: generate time-series */
257  for ( UINT4 i = 0; i < numSteps; i++ ) {
258  LIGOTimeGPS *tiGPS = &( detStates->data[i].tGPS );
259 
260  REAL8 ti = XLALGPSGetREAL8( tiGPS );
261  REAL8 deltati = ti - refTime;
262  REAL8 dT = SCALAR( vn, detStates->data[i].rDetector );
263  REAL8 taui = deltati + dT;
264 
265  REAL8 phi_i = LAL_TWOPI * ( f0 * taui
266  + ( 1.0 / 2.0 ) * f1dot * taui * taui
267  + ( 1.0 / 6.0 ) * f2dot * taui * taui * taui
268  + ( 1.0 / 24.0 ) * f3dot * taui * taui * taui * taui
269  );
270 
271  REAL8 cosphi_i = cos( phi_i );
272  REAL8 sinphi_i = sin( phi_i );
273 
274  REAL8 ai = amcoe->a->data[i];
275  REAL8 bi = amcoe->b->data[i];
276 
277  REAL8 hi = A1 * ai * cosphi_i
278  + A2 * bi * cosphi_i
279  + A3 * ai * sinphi_i
280  + A4 * bi * sinphi_i;
281 
282  ts->data->data[i] = ( REAL4 )hi;
283 
284  } /* for i < numSteps */
285 
286  XLALDestroyDetectorStateSeries( detStates );
287  XLALDestroyAMCoeffs( amcoe );
288 
289  return ts;
290 
291 } /* XLALSimulateExactPulsarSignal() */
REAL8 sinZeta
LIGOTimeGPSVector * timestamps
static LALUnit emptyUnit
#define SCALAR(u, v)
Simple Euklidean scalar product for two 3-dim vectors in cartesian coords.
void XLALDestroyDetectorStateSeries(DetectorStateSeries *detStates)
Get rid of a DetectorStateSeries.
DetectorStateSeries * XLALGetDetectorStates(const LIGOTimeGPSVector *timestamps, const LALDetector *detector, const EphemerisData *edat, REAL8 tOffset)
Get the 'detector state' (ie detector-tensor, position, velocity, etc) for the given vector of timest...
int XLALExtrapolatePulsarSpins(PulsarSpins fkdot1, const PulsarSpins fkdot0, REAL8 dtau)
Extrapolate the Pulsar spin-parameters (fkdot0) from the initial reference-epoch to the new referen...
AMCoeffs * XLALComputeAMCoeffs(const DetectorStateSeries *DetectorStates, SkyPosition skypos)
Compute the 'amplitude coefficients' , as defined in for a series of timestamps.
Definition: LALComputeAM.c:297
void XLALDestroyAMCoeffs(AMCoeffs *amcoef)
Destroy a AMCoeffs structure.
Definition: LALComputeAM.c:497
#define LAL_PI_2
#define LAL_TWOPI
double REAL8
#define XLAL_INIT_DECL(var,...)
char CHAR
uint32_t UINT4
float REAL4
LALDETECTORTYPE_CYLBAR
void XLALFree(void *p)
REAL8 PulsarSpins[PULSAR_MAX_SPINS]
Typedef for fixed-size array holding GW frequency and derivatives fk = d^k Freq/dt^k|(tau_ref)
LIGOTimeGPSVector * XLALMakeTimestamps(LIGOTimeGPS tStart, REAL8 Tspan, REAL8 Tsft, REAL8 Toverlap)
Given a start-time, Tspan, Tsft and Toverlap, returns a list of timestamps covering this time-stretch...
CHAR * XLALGetChannelPrefix(const CHAR *name)
Find the valid CW detector prefix.
Definition: SFTnaming.c:203
void XLALDestroyTimestampVector(LIGOTimeGPSVector *vect)
De-allocate a LIGOTimeGPSVector.
Definition: SFTtimestamps.c:69
REAL4TimeSeries * XLALSimulateExactPulsarSignal(const PulsarSignalParams *params)
Simulate a pulsar signal to best accuracy possible.
REAL4TimeSeries * XLALCreateREAL4TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
#define XLAL_ERROR_NULL(...)
#define XLAL_CHECK_NULL(assertion,...)
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EDOM
XLAL_EINVAL
LIGOTimeGPS * XLALGPSSetREAL8(LIGOTimeGPS *epoch, REAL8 t)
REAL8 XLALGPSGetREAL8(const LIGOTimeGPS *epoch)
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
ts
This structure contains the per-SFT (weighted) antenna-pattern functions , with the SFT-index,...
Definition: LALComputeAM.h:63
REAL4Vector * b
(weighted) per-SFT antenna-pattern function
Definition: LALComputeAM.h:65
REAL4Vector * a
(weighted) per-SFT antenna-pattern function
Definition: LALComputeAM.h:64
REAL8 rDetector[3]
Cartesian coords of detector position in ICRS J2000.
LIGOTimeGPS tGPS
GPS timestamps corresponding to this entry.
Timeseries of DetectorState's, representing the detector-info at different timestamps.
DetectorState * data
array of DetectorState entries
A vector of 'timestamps' of type LIGOTimeGPS.
Definition: SFTfileIO.h:188
UINT4 length
number of timestamps
Definition: SFTfileIO.h:192
Input parameters to GeneratePulsarSignal(), defining the source and the time-series.
REAL4 * data
double duration
double psi
enum @4 site