LALBurst  2.0.4.1-b72065a
EPFilters.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007,2011,2012 Kipp Cannon
3  * Copyright (C) 2012,2013 Reinhard Prix, Karl Wette
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13  * Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 
21 /*
22  * ============================================================================
23  *
24  * Preamble
25  *
26  * ============================================================================
27  */
28 
29 
30 #include <complex.h>
31 #include <math.h>
32 
33 
34 #include <lal/EPSearch.h>
35 #include <lal/FrequencySeries.h>
36 #include <lal/Sequence.h>
37 #include <lal/TimeFreqFFT.h>
38 #include <lal/Units.h>
39 #include <lal/Window.h>
40 #include <lal/XLALError.h>
41 
42 static double min(double a, double b) { return a < b ? a : b; }
43 static double max(double a, double b) { return a > b ? a : b; }
44 
45 
46 /*
47  * ============================================================================
48  *
49  * Channel Filter Management
50  *
51  * ============================================================================
52  */
53 
54 
55 /**
56  * Compute the magnitude of the inner product of two arbitrary channel
57  * filters. Note that the sums are done over only the positive frequency
58  * components, so this function multiplies by the required factor of 2.
59  * The result is the *full* inner product, not the half inner product. It
60  * is safe to pass the same filter as both arguments. If the PSD is set to
61  * NULL then no PSD weighting is applied. PSD weighting is only used in
62  * reconstructing h_rss.
63  *
64  * The return value is NaN if the input frequency series have incompatible
65  * parameters. Note that the two-point spectral correlation function does
66  * not carry enough metadata to determine if it is compatible with the
67  * filters or PSD, for example it does not carry a deltaF parameter. It is
68  * left as an excercise for the calling code to ensure the two-point
69  * spectral correlation is appropriate.
70  */
71 
72 
74  const COMPLEX16FrequencySeries *filter1, /**< frequency-domain filter */
75  const COMPLEX16FrequencySeries *filter2, /**< frequency-domain filter */
76  const REAL8Sequence *correlation, /**< two-point spectral correlation function. see XLALREAL8WindowTwoPointSpectralCorrelation(). */
77  const REAL8FrequencySeries *psd /**< power spectral density function. see XLALREAL8AverageSpectrumWelch() and friends. */
78 )
79 {
80  const int k10 = round(filter1->f0 / filter1->deltaF);
81  const int k20 = round(filter2->f0 / filter2->deltaF);
82  const double complex *f1data = filter1->data->data;
83  const double complex *f2data = filter2->data->data;
84  const double *pdata = psd ? psd->data->data - (int) round(psd->f0 / psd->deltaF) : NULL;
85  int k1, k2;
86  double complex sum = 0;
87 
88  /*
89  * check that filters have same frequency resolution, and if a PSD
90  * is provided that it also has the same frequency resolution and
91  * spans the frequencies spanned by the fitlers
92  */
93 
94  if(filter1->deltaF != filter2->deltaF || (psd &&
95  (psd->deltaF != filter1->deltaF || psd->f0 > min(filter1->f0, filter2->f0) || max(filter1->f0 + filter1->data->length * filter1->deltaF, filter2->f0 + filter2->data->length * filter2->deltaF) > psd->f0 + psd->data->length * psd->deltaF)
96  )) {
97  XLALPrintError("%s(): filters are incompatible or PSD does not span filters' frequencies", __func__);
99  }
100 
101  /*
102  * compute and return inner product
103  */
104 
105  for(k1 = 0; k1 < (int) filter1->data->length; k1++) {
106  for(k2 = 0; k2 < (int) filter2->data->length; k2++) {
107  const unsigned delta_k = abs(k10 + k1 - k20 - k2);
108  double sksk = (delta_k & 1 ? -1 : +1) * (delta_k < correlation->length ? correlation->data[delta_k] : 0);
109 
110  if(pdata)
111  sksk *= sqrt(pdata[k10 + k1] * pdata[k20 + k2]);
112 
113  sum += sksk * f1data[k1] * conj(f2data[k2]);
114  }
115  }
116 
117  return 2 * cabs(sum);
118 }
119 
120 
121 /**
122  * Generate the frequency domain channel filter function. The filter
123  * corresponds to a frequency band [channel_flow, channel_flow +
124  * channel_width]. The filter is nominally a Hann window twice the
125  * channel's width, centred on the channel's centre frequency. This makes
126  * a sum across channels equivalent to constructing a Tukey window spanning
127  * the same frequency band. This trick is one of the ingredients that
128  * allows us to accomplish a multi-resolution tiling using a single
129  * frequency channel projection (*).
130  *
131  * The filter is normalized so that its "magnitude" as defined by the inner
132  * product function XLALExcessPowerFilterInnerProduct() is N. Then the
133  * filter is divided by the square root of the PSD frequency series prior
134  * to normalilization. This has the effect of de-emphasizing frequency
135  * bins with high noise content, and is called "over whitening".
136  *
137  * Note: the number of samples in the window is odd, being one more than
138  * the number of frequency bins in twice the channel width. This gets the
139  * Hann windows to super-impose to form a Tukey window. (you'll have to
140  * draw yourself a picture).
141  *
142  * (*) Really, there's no need for the "effective window" resulting from
143  * summing across channels to be something that has a name, any channel
144  * filter at all would do, but this way the code's behaviour is more easily
145  * understood --- it's easy to say "the channel filter is a Tukey window of
146  * variable central width".
147  */
148 
149 
151  double channel_flow, /**< Hz */
152  double channel_width, /**< Hz */
153  const REAL8FrequencySeries *psd, /**< power spectral density function. see XLALREAL8AverageSpectrumWelch() and friends. */
154  const REAL8Sequence *correlation /**< two-point spectral correlation function. see XLALREAL8WindowTwoPointSpectralCorrelation(). */
155 )
156 {
157  char filter_name[100];
158  REAL8Window *hann;
159  COMPLEX16FrequencySeries *filter;
160  unsigned i;
161  double norm;
162 
163  /*
164  * create frequency series for filter
165  */
166 
167  sprintf(filter_name, "channel %g +/- %g Hz", channel_flow + channel_width / 2, channel_width / 2);
168  filter = XLALCreateCOMPLEX16FrequencySeries(filter_name, &psd->epoch, channel_flow - channel_width / 2, psd->deltaF, &lalDimensionlessUnit, 2 * channel_width / psd->deltaF + 1);
169  if(!filter)
171  if(filter->f0 < 0.0) {
172  XLALPrintError("%s(): channel_flow - channel_width / 2 >= 0.0 failed", __func__);
175  }
176 
177  /*
178  * build real-valued Hann window and copy into filter
179  */
180 
181  hann = XLALCreateHannREAL8Window(filter->data->length);
182  if(!hann) {
186  }
187  for(i = 0; i < filter->data->length; i++)
188  filter->data->data[i] = hann->data->data[i];
190 
191  /*
192  * divide by square root of PSD to whiten
193  */
194 
195  if(!XLALWhitenCOMPLEX16FrequencySeries(filter, psd)) {
198  }
199 
200  /*
201  * normalize the filter. the filter needs to be normalized so that
202  * it's inner product with itself is (width / delta F), the width
203  * of the filter in bins.
204  */
205 
206  norm = XLALExcessPowerFilterInnerProduct(filter, filter, correlation, NULL);
207  if(XLAL_IS_REAL8_FAIL_NAN(norm)) {
210  }
211  norm = sqrt(channel_width / filter->deltaF / norm);
212  for(i = 0; i < filter->data->length; i++)
213  filter->data->data[i] *= norm;
214 
215  /*
216  * success
217  */
218 
219  return filter;
220 }
static double max(double a, double b)
Definition: EPFilters.c:43
static double min(double a, double b)
Definition: EPFilters.c:42
double i
coeffs k2
coeffs k1
COMPLEX16FrequencySeries * XLALCreateExcessPowerFilter(double channel_flow, double channel_width, const REAL8FrequencySeries *psd, const REAL8Sequence *correlation)
Generate the frequency domain channel filter function.
Definition: EPFilters.c:150
double XLALExcessPowerFilterInnerProduct(const COMPLEX16FrequencySeries *filter1, const COMPLEX16FrequencySeries *filter2, const REAL8Sequence *correlation, const REAL8FrequencySeries *psd)
Compute the magnitude of the inner product of two arbitrary channel filters.
Definition: EPFilters.c:73
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
static const INT4 a
COMPLEX16FrequencySeries * XLALWhitenCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *fseries, const REAL8FrequencySeries *psd)
const LALUnit lalDimensionlessUnit
void XLALDestroyREAL8Window(REAL8Window *window)
REAL8Window * XLALCreateHannREAL8Window(UINT4 length)
#define XLAL_ERROR_REAL8(...)
#define XLAL_ERROR_NULL(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_IS_REAL8_FAIL_NAN(val)
XLAL_EFUNC
XLAL_EINVAL
COMPLEX16Sequence * data
COMPLEX16 * data
REAL8Sequence * data
REAL8 * data
REAL8Sequence * data