ButterworthTimeSeries.c

Go to the documentation of this file.
00001 /*
00002 *  Copyright (C) 2007 Jolien Creighton, Teviet Creighton
00003 *
00004 *  This program is free software; you can redistribute it and/or modify
00005 *  it under the terms of the GNU General Public License as published by
00006 *  the Free Software Foundation; either version 2 of the License, or
00007 *  (at your option) any later version.
00008 *
00009 *  This program is distributed in the hope that it will be useful,
00010 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 *  GNU General Public License for more details.
00013 *
00014 *  You should have received a copy of the GNU General Public License
00015 *  along with with program; see the file COPYING. If not, write to the
00016 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00017 *  MA  02111-1307  USA
00018 */
00019 
00020 /************************ <lalVerbatim file="ButterworthTimeSeriesCV">
00021 Author: Creighton, T. D.
00022 Revision: $Id: ButterworthTimeSeries.c,v 1.14 2007/06/08 14:41:56 bema Exp $
00023 **************************************************** </lalVerbatim> */
00024 
00025 /********************************************************** <lalLaTeX>
00026 
00027 \subsection{Module \texttt{ButterworthTimeSeries.c}}
00028 \label{ss:ButterworthTimeSeries.c}
00029 
00030 Applies a low- or high-pass Butterworth filter to a time series.
00031 
00032 \subsubsection*{Prototypes}
00033 \vspace{0.1in}
00034 \input{ButterworthTimeSeriesD}
00035 \idx{LALButterworthREAL4TimeSeries()}
00036 \idx{LALButterworthREAL8TimeSeries()}
00037 \idx{LALDButterworthREAL4TimeSeries()}
00038 
00039 \subsubsection*{Description}
00040 
00041 These routines perform an in-place time-domain band-pass filtering of
00042 a data sequence \verb@*series@, using a Butterworth filter generated
00043 from parameters \verb@*params@.  The routines construct a filter with
00044 the square root of the desired amplitude response, which it then
00045 applied to the data once forward and once in reverse.  This gives the
00046 full amplitude response with little or no frequency-dependent phase
00047 shift.
00048 
00049 The routine \verb@LALDButterworthREAL4TimeSeries()@ applies a
00050 double-precision filter to single-precision data, using
00051 \verb@LALDIIRFilterREAL4Vector()@ and
00052 \verb@LALDIIRFilterREAL4VectorR()@.
00053 
00054 \subsubsection*{Algorithm}
00055 
00056 The frequency response of a Butterworth low-pass filter is easiest to
00057 express in terms of the transformed frequency variable $w=\tan(\pi
00058 f\Delta t)$, where $\Delta t$ is the sampling interval (i.e.\
00059 \verb@series->deltaT@).  In this parameter, then, the \emph{power}
00060 response (attenuation) of the filter is:
00061 $$
00062 |R|^2 = \sqrt{a} = \frac{1}{1+(w/w_c)^{2n}} \; ,
00063 $$
00064 where $n$ is the filter order and $w_c$ is the characteristic
00065 frequency.  We have written the attenuation as $\sqrt{a}$ to emphasize
00066 that the full attenuation $a$ is achieved only after filtering twice
00067 (once forward, once in reverse).  Similarly, a Butterworth high-pass
00068 filter is given by
00069 $$
00070 |R|^2 = \sqrt{a} = \frac{1}{1+(w_c/w)^{2n}} \; .
00071 $$
00072 If one is given a filter order $n$, then the characteristic frequency
00073 can be determined from the attenuation at some any given frequency.
00074 Alternatively, $n$ and $w_c$ can both be computed given attenuations
00075 at two different frequencies.
00076 
00077 Frequencies in \verb@*params@ are assumed to be real frequencies $f$
00078 given in the inverse of the units used for the sampling interval
00079 \verb@series->deltaT@.  In order to be used, the pass band parameters
00080 must lie in the ranges given below; if a parameter lies outside of its
00081 range, then it is ignored and the filter is calculated from the
00082 remaining parameters.  If too many parameters are missing, the routine
00083 will fail.  The acceptable parameter ranges are:
00084 
00085 \begin{description}
00086 \item[\texttt{params->nMax}]   = 1, 2, $\ldots$
00087 \item[\texttt{params->f1}, \texttt{f2}] $\in
00088   (0,\{2\times\verb@series->deltaT@\}^{-1}) $
00089 \item[\texttt{params->a1}, \texttt{a2}] $\in (0,1) $
00090 \end{description}
00091 
00092 If both pairs of frequencies and amplitudes are given, then \verb@a1@,
00093 \verb@a2@ specify the minimal requirements on the attenuation of the
00094 filter at frequencies \verb@f1@, \verb@f2@.  Whether the filter is a
00095 low- or high-pass filter is determined from the relative sizes of
00096 these parameters.  In this case the \verb@nMax@ parameter is optional;
00097 if given, it specifies an upper limit on the filter order.  If the
00098 desired attenuations would require a higher order, then the routine
00099 will sacrifice performance in the stop band in order to remain within
00100 the specified \verb@nMax@.
00101 
00102 If one of the frequency/attenuation pairs is missing, then the filter
00103 is computed using the remaining pair and \verb@nMax@ (which must be
00104 given).  The filter is taken to be a low-pass filter if \verb@f1@,
00105 \verb@a1@ are given, and high-pass if \verb@f2@, \verb@a2@ are given.
00106 If only one frequency and no corresponding attenuation is specified,
00107 then it is taken to be the characteristic frequency (i.e. the
00108 corresponding attenuation is assumed to be $\sqrt{a}=1/2$).  If none
00109 of these conditions are met, the routine will return an error.
00110 
00111 The following table summarizes the decision algorithm.  A $\bullet$
00112 symbol indicates that the parameter is specified in the range given
00113 above.  A $\circ$ symbol indicates that the parameter is ``not
00114 given'', i.e.\ not specified in the valid range.
00115 \begin{center}
00116 \begin{tabular}{|cccccp{10cm}|}
00117 \hline
00118 \tt nMax & \tt f1 & \tt a1 & \tt f2 & \tt a2 & Procedure \\
00119 \hline
00120  $\circ$  & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ &
00121         Type of filter (low- or high-pass), $w_c$, and $n$ are
00122         computed from all four transition-band parameters. \\
00123 $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ &
00124         Ditto, but if the resulting $n>$ \texttt{nMax}, $w_c$ is
00125         computed from \texttt{nMax} and the (\texttt{f},\texttt{a})
00126         pair with the \emph{larger} \texttt{a}. \\
00127 $\bullet$ & $\bullet$ & $\bullet$ &  $\circ$  &  $\circ$  &
00128         Low-pass filter; $w_c$ is computed from \texttt{nMax},
00129         \texttt{f1}, and \texttt{a1}. \\
00130 $\bullet$ & $\bullet$ & $\bullet$ &  $\circ$  & $\bullet$ &
00131         Ditto; \texttt{a2} is ignored. \\
00132 $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ &  $\circ$  &
00133         Ditto; \texttt{f2} is ignored. \\
00134 $\bullet$ & $\bullet$ &  $\circ$  &  $\circ$  &  $\circ$  &
00135         Low-pass filter; $w_c$ is computed as above with \texttt{a1}
00136         treated as 1/4. \\
00137 $\bullet$ & $\bullet$ &  $\circ$  &  $\circ$  & $\bullet$ &
00138         Ditto; \texttt{a2} is ignored. \\
00139 $\bullet$ &  $\circ$  &  $\circ$  & $\bullet$ & $\bullet$ &
00140         High-pass filter; $w_c$ is computed from \texttt{nMax},
00141         \texttt{f2}, and \texttt{a2}. \\
00142 $\bullet$ &  $\circ$  & $\bullet$ & $\bullet$ & $\bullet$ &
00143         Ditto; \texttt{a1} is ignored. \\
00144 $\bullet$ & $\bullet$ &  $\circ$  & $\bullet$ & $\bullet$ &
00145         Ditto; \texttt{f1} is ignored. \\
00146 $\bullet$ &  $\circ$  &  $\circ$  & $\bullet$ &  $\circ$  &
00147         High-pass filter; $w_c$ is computed as above with \texttt{a2}
00148         treated as 1/4. \\
00149 $\bullet$ &  $\circ$  & $\bullet$ & $\bullet$ &  $\circ$  &
00150         Ditto; \texttt{a1} is ignored. \\
00151 \multicolumn{5}{|c}{Other} & Subroutine returns an error. \\
00152 \hline
00153 \end{tabular}
00154 \end{center}
00155 
00156 Once an order $n$ and characteristic frequency $w_c$ are known, the
00157 zeros and poles of a ZPG filter are readily determined.  A stable,
00158 physically realizable Butterworth filter will have $n$ poles evenly
00159 spaced on the upper half of a circle of radius $w_c$; that is,
00160 $$
00161 R = \frac{(-iw_c)^n}{\prod_{k=0}^{n-1}(w - w_c e^{2\pi i(k+1/2)/n})}
00162 $$
00163 for a low-pass filter, and
00164 $$
00165 R = \frac{w^n}{\prod_{k=0}^{n-1}(w - w_c e^{2\pi i(k+1/2)/n})}
00166 $$
00167 for a high-pass filter.  By choosing only poles on the upper-half
00168 plane, one ensures that after transforming to $z$ the poles will have
00169 $|z|<1$.  Furthermore, the phase factor $(-i)^n$ in the numerator of
00170 the low-pass filter is chosen so that the DC response is purely real;
00171 this ensures that the response function in the $z$-plane will have a
00172 real gain factor, and the resulting IIR filter will be physically
00173 realizable.  The high-pass filter has a purely real response at
00174 Nyquist ($w\rightarrow\infty$), which similarly gives a physical IIR
00175 filter.
00176 
00177 Although higher orders $n$ would appear to produce better (i.e.\
00178 sharper) filter responses, one rapidly runs into numerical errors, as
00179 one ends up adding and subtracting $n$ large numbers to obtain small
00180 filter responses.  One way around this is to break the filter up into
00181 several lower-order filters.  The routines in this module do just
00182 that.  Poles are paired up across the imaginary axis, (and combined
00183 with pairs of zeros at $w=0$ for high-pass filters,) to form $[n/2]$
00184 second-order filters.  If $n$ is odd, there will be an additional
00185 first-order filter, with one pole at $w=iw_c$ (and one zero at $w=0$
00186 for a high-pass filter).
00187 
00188 Each ZPG filter in the $w$-plane is first transformed to the $z$-plane
00189 by a bilinear transformation, and is then used to construct a
00190 time-domain IIR filter.  Each filter is then applied to the time
00191 series.  As mentioned in the description above, the filters are
00192 designed to give an overall amplitude response that is the square root
00193 of the desired attenuation; however, each time-domain filter is
00194 applied to the data stream twice: once in the normal sense, and once
00195 in the time-reversed sense.  This gives the full attenuation with very
00196 little frequency-dependent phase shift.
00197 
00198 \subsubsection*{Uses}
00199 \begin{verbatim}
00200 lalDebugLevel
00201 LALPrintError()                 LALWarning()
00202 LALCreateREAL4IIRFilter()       LALCreateREAL8IIRFilter()
00203 LALCreateCOMPLEX8ZPGFilter()    LALCreateCOMPLEX16ZPGFilter()
00204 LALDestroyREAL4IIRFilter()      LALDestroyREAL8IIRFilter()
00205 LALDestroyCOMPLEX8ZPGFilter()   LALDestroyCOMPLEX16ZPGFilter()
00206 LALWToZCOMPLEX8ZPGFilter()      LALWToZCOMPLEX16ZPGFilter()
00207 LALIIRFilterREAL4Vector()       LALIIRFilterREAL8Vector()
00208 LALIIRFilterREAL4VectorR()      LALIIRFilterREAL8VectorR()
00209 \end{verbatim}
00210 
00211 \subsubsection*{Notes}
00212 
00213 \vfill{\footnotesize\input{ButterworthTimeSeriesCV}}
00214 
00215 ******************************************************* </lalLaTeX> */
00216 
00217 #include <lal/LALStdlib.h>
00218 #include <lal/LALConstants.h>
00219 #include <lal/AVFactories.h>
00220 #include <math.h>
00221 #include <lal/IIRFilter.h>
00222 #include <lal/BandPassTimeSeries.h>
00223 
00224 NRCSID(BUTTERWORTHTIMESERIESC,"$Id: ButterworthTimeSeries.c,v 1.14 2007/06/08 14:41:56 bema Exp $");
00225 
00226 extern INT4 lalDebugLevel;
00227 
00228 static int
00229 XLALParsePassBandParamStruc( PassBandParamStruc *params,
00230                          INT4               *n,
00231                          REAL8              *wc,
00232                          REAL8              deltaT );
00233 /* Prototype for a local input parsing routine. */
00234 
00235 int XLALButterworthREAL4TimeSeries( REAL4TimeSeries *series, PassBandParamStruc *params )
00236 {
00237   static const char *func = "XLALButterworthREAL4TimeSeries";
00238   INT4 n;    /* The filter order. */
00239   INT4 type; /* The pass-band type: high, low, or undeterminable. */
00240   INT4 i;    /* An index. */
00241   INT4 j;    /* Another index. */
00242   REAL8 wc;  /* The filter's transformed frequency. */
00243 
00244   /* Make sure the input pointers are non-null. */
00245   if ( ! params || ! series || ! series->data || ! series->data->data )
00246     XLAL_ERROR( func, XLAL_EFAULT );
00247 
00248   /* Parse the pass-band parameter structure.  I separate this into a
00249      local static subroutine because it's an icky mess of conditionals
00250      that would clutter the logic of the main routine. */
00251   type=XLALParsePassBandParamStruc(params,&n,&wc,series->deltaT);
00252   if(type<0)
00253     XLAL_ERROR( func, XLAL_EINVAL );
00254 
00255   /* An order n Butterworth filter has n poles spaced evenly along a
00256      semicircle in the upper complex w-plane.  By pairing up poles
00257      symmetric across the imaginary axis, the filter gan be decomposed
00258      into [n/2] filters of order 2, plus perhaps an additional order 1
00259      filter.  The following loop pairs up poles and applies the
00260      filters with order 2. */
00261   for(i=0,j=n-1;i<j;i++,j--){
00262     REAL8 theta=LAL_PI*(i+0.5)/n;
00263     REAL8 ar=wc*cos(theta);
00264     REAL8 ai=wc*sin(theta);
00265     REAL8IIRFilter *iirFilter=NULL;
00266     COMPLEX16ZPGFilter *zpgFilter=NULL;
00267 
00268     /* Generate the filter in the w-plane. */
00269     if(type==2){
00270       zpgFilter = XLALCreateCOMPLEX16ZPGFilter(2,2);
00271       if ( ! zpgFilter )
00272         XLAL_ERROR( func, XLAL_EFUNC );
00273       zpgFilter->zeros->data[0].re=0.0;
00274       zpgFilter->zeros->data[0].im=0.0;
00275       zpgFilter->zeros->data[1].re=0.0;
00276       zpgFilter->zeros->data[1].im=0.0;
00277       zpgFilter->gain.re=1.0;
00278       zpgFilter->gain.im=0.0;
00279     }else{
00280       zpgFilter = XLALCreateCOMPLEX16ZPGFilter(0,2);
00281       if ( ! zpgFilter )
00282         XLAL_ERROR( func, XLAL_EFUNC );
00283       zpgFilter->gain.re=-wc*wc;
00284       zpgFilter->gain.im=0.0;
00285     }
00286     zpgFilter->poles->data[0].re=ar;
00287     zpgFilter->poles->data[0].im=ai;
00288     zpgFilter->poles->data[1].re=-ar;
00289     zpgFilter->poles->data[1].im=ai;
00290 
00291     /* Transform to the z-plane and create the IIR filter. */
00292     if (XLALWToZCOMPLEX16ZPGFilter(zpgFilter)<0)
00293     {
00294       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00295       XLAL_ERROR( func, XLAL_EFUNC );
00296     }
00297     iirFilter = XLALCreateREAL8IIRFilter(zpgFilter);
00298     if (!iirFilter)
00299     {
00300       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00301       XLAL_ERROR( func, XLAL_EFUNC );
00302     }
00303 
00304     /* Filter the data, once each way. */
00305     if (XLALIIRFilterREAL4Vector(series->data,iirFilter)<0
00306         || XLALIIRFilterReverseREAL4Vector(series->data,iirFilter)<0)
00307     {
00308       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00309       XLALDestroyREAL8IIRFilter(iirFilter);
00310       XLAL_ERROR( func, XLAL_EFUNC );
00311     }
00312 
00313     /* Free the filters. */
00314     XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00315     XLALDestroyREAL8IIRFilter(iirFilter);
00316   }
00317 
00318   /* Next, this conditional applies the possible order 1 filter
00319      corresponding to an unpaired pole on the imaginary w axis. */
00320   if(i==j){
00321     REAL8IIRFilter *iirFilter=NULL;
00322     COMPLEX16ZPGFilter *zpgFilter=NULL;
00323 
00324     /* Generate the filter in the w-plane. */
00325     if(type==2){
00326       zpgFilter=XLALCreateCOMPLEX16ZPGFilter(1,1);
00327       if(!zpgFilter)
00328         XLAL_ERROR(func,XLAL_EFUNC);
00329       zpgFilter->zeros->data->re=0.0;
00330       zpgFilter->zeros->data->im=0.0;
00331       zpgFilter->gain.re=1.0;
00332       zpgFilter->gain.im=0.0;
00333     }else{
00334       zpgFilter=XLALCreateCOMPLEX16ZPGFilter(0,1);
00335       if(!zpgFilter)
00336         XLAL_ERROR(func,XLAL_EFUNC);
00337       zpgFilter->gain.re=0.0;
00338       zpgFilter->gain.im=-wc;
00339     }
00340     zpgFilter->poles->data->re=0.0;
00341     zpgFilter->poles->data->im=wc;
00342 
00343     /* Transform to the z-plane and create the IIR filter. */
00344     if (XLALWToZCOMPLEX16ZPGFilter(zpgFilter)<0)
00345     {
00346       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00347       XLAL_ERROR(func,XLAL_EFUNC);
00348     }
00349     iirFilter=XLALCreateREAL8IIRFilter(zpgFilter);
00350     if (!iirFilter)
00351     {
00352       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00353       XLAL_ERROR(func,XLAL_EFUNC);
00354     }
00355 
00356     /* Filter the data, once each way. */
00357     if (XLALIIRFilterREAL4Vector(series->data,iirFilter)<0
00358         || XLALIIRFilterReverseREAL4Vector(series->data,iirFilter)<0)
00359     {
00360       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00361       XLALDestroyREAL8IIRFilter(iirFilter);
00362       XLAL_ERROR( func, XLAL_EFUNC );
00363     }
00364 
00365     /* Free the filters. */
00366     XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00367     XLALDestroyREAL8IIRFilter(iirFilter);
00368   }
00369 
00370   return 0;
00371 }
00372 
00373 
00374 int XLALButterworthREAL8TimeSeries( REAL8TimeSeries *series, PassBandParamStruc *params )
00375 {
00376   static const char *func = "XLALButterworthREAL8TimeSeries";
00377   INT4 n;    /* The filter order. */
00378   INT4 type; /* The pass-band type: high, low, or undeterminable. */
00379   INT4 i;    /* An index. */
00380   INT4 j;    /* Another index. */
00381   REAL8 wc;  /* The filter's transformed frequency. */
00382 
00383   /* Make sure the input pointers are non-null. */
00384   if ( ! params || ! series || ! series->data || ! series->data->data )
00385     XLAL_ERROR( func, XLAL_EFAULT );
00386 
00387   /* Parse the pass-band parameter structure.  I separate this into a
00388      local static subroutine because it's an icky mess of conditionals
00389      that would clutter the logic of the main routine. */
00390   type=XLALParsePassBandParamStruc(params,&n,&wc,series->deltaT);
00391   if(type<0)
00392     XLAL_ERROR( func, XLAL_EINVAL );
00393 
00394   /* An order n Butterworth filter has n poles spaced evenly along a
00395      semicircle in the upper complex w-plane.  By pairing up poles
00396      symmetric across the imaginary axis, the filter gan be decomposed
00397      into [n/2] filters of order 2, plus perhaps an additional order 1
00398      filter.  The following loop pairs up poles and applies the
00399      filters with order 2. */
00400   for(i=0,j=n-1;i<j;i++,j--){
00401     REAL8 theta=LAL_PI*(i+0.5)/n;
00402     REAL8 ar=wc*cos(theta);
00403     REAL8 ai=wc*sin(theta);
00404     REAL8IIRFilter *iirFilter=NULL;
00405     COMPLEX16ZPGFilter *zpgFilter=NULL;
00406 
00407     /* Generate the filter in the w-plane. */
00408     if(type==2){
00409       zpgFilter = XLALCreateCOMPLEX16ZPGFilter(2,2);
00410       if ( ! zpgFilter )
00411         XLAL_ERROR( func, XLAL_EFUNC );
00412       zpgFilter->zeros->data[0].re=0.0;
00413       zpgFilter->zeros->data[0].im=0.0;
00414       zpgFilter->zeros->data[1].re=0.0;
00415       zpgFilter->zeros->data[1].im=0.0;
00416       zpgFilter->gain.re=1.0;
00417       zpgFilter->gain.im=0.0;
00418     }else{
00419       zpgFilter = XLALCreateCOMPLEX16ZPGFilter(0,2);
00420       if ( ! zpgFilter )
00421         XLAL_ERROR( func, XLAL_EFUNC );
00422       zpgFilter->gain.re=-wc*wc;
00423       zpgFilter->gain.im=0.0;
00424     }
00425     zpgFilter->poles->data[0].re=ar;
00426     zpgFilter->poles->data[0].im=ai;
00427     zpgFilter->poles->data[1].re=-ar;
00428     zpgFilter->poles->data[1].im=ai;
00429 
00430     /* Transform to the z-plane and create the IIR filter. */
00431     if (XLALWToZCOMPLEX16ZPGFilter(zpgFilter)<0)
00432     {
00433       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00434       XLAL_ERROR( func, XLAL_EFUNC );
00435     }
00436     iirFilter = XLALCreateREAL8IIRFilter(zpgFilter);
00437     if (!iirFilter)
00438     {
00439       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00440       XLAL_ERROR( func, XLAL_EFUNC );
00441     }
00442 
00443     /* Filter the data, once each way. */
00444     if (XLALIIRFilterREAL8Vector(series->data,iirFilter)<0
00445         || XLALIIRFilterReverseREAL8Vector(series->data,iirFilter)<0)
00446     {
00447       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00448       XLALDestroyREAL8IIRFilter(iirFilter);
00449       XLAL_ERROR( func, XLAL_EFUNC );
00450     }
00451 
00452     /* Free the filters. */
00453     XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00454     XLALDestroyREAL8IIRFilter(iirFilter);
00455   }
00456 
00457   /* Next, this conditional applies the possible order 1 filter
00458      corresponding to an unpaired pole on the imaginary w axis. */
00459   if(i==j){
00460     REAL8IIRFilter *iirFilter=NULL;
00461     COMPLEX16ZPGFilter *zpgFilter=NULL;
00462 
00463     /* Generate the filter in the w-plane. */
00464     if(type==2){
00465       zpgFilter=XLALCreateCOMPLEX16ZPGFilter(1,1);
00466       if(!zpgFilter)
00467         XLAL_ERROR(func,XLAL_EFUNC);
00468       zpgFilter->zeros->data->re=0.0;
00469       zpgFilter->zeros->data->im=0.0;
00470       zpgFilter->gain.re=1.0;
00471       zpgFilter->gain.im=0.0;
00472     }else{
00473       zpgFilter=XLALCreateCOMPLEX16ZPGFilter(0,1);
00474       if(!zpgFilter)
00475         XLAL_ERROR(func,XLAL_EFUNC);
00476       zpgFilter->gain.re=0.0;
00477       zpgFilter->gain.im=-wc;
00478     }
00479     zpgFilter->poles->data->re=0.0;
00480     zpgFilter->poles->data->im=wc;
00481 
00482     /* Transform to the z-plane and create the IIR filter. */
00483     if (XLALWToZCOMPLEX16ZPGFilter(zpgFilter)<0)
00484     {
00485       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00486       XLAL_ERROR(func,XLAL_EFUNC);
00487     }
00488     iirFilter=XLALCreateREAL8IIRFilter(zpgFilter);
00489     if (!iirFilter)
00490     {
00491       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00492       XLAL_ERROR(func,XLAL_EFUNC);
00493     }
00494 
00495     /* Filter the data, once each way. */
00496     if (XLALIIRFilterREAL8Vector(series->data,iirFilter)<0
00497         || XLALIIRFilterReverseREAL8Vector(series->data,iirFilter)<0)
00498     {
00499       XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00500       XLALDestroyREAL8IIRFilter(iirFilter);
00501       XLAL_ERROR( func, XLAL_EFUNC );
00502     }
00503 
00504     /* Free the filters. */
00505     XLALDestroyCOMPLEX16ZPGFilter(zpgFilter);
00506     XLALDestroyREAL8IIRFilter(iirFilter);
00507   }
00508 
00509   return 0;
00510 }
00511 
00512 
00513 int XLALLowPassREAL4TimeSeries( REAL4TimeSeries *series,
00514     REAL8 frequency, REAL8 amplitude, INT4 filtorder )
00515 {
00516   static const char *func = "XLALLowPassREAL4TimeSeries";
00517   PassBandParamStruc params;
00518   params.nMax = filtorder;
00519   params.f1   = frequency;
00520   params.a1   = amplitude;
00521   params.f2   = -1;
00522   params.a2   = -1;
00523   if ( XLALButterworthREAL4TimeSeries( series, &params ) < 0 )
00524     XLAL_ERROR( func, XLAL_EFUNC );
00525   return 0;
00526 }
00527 
00528 int XLALLowPassREAL8TimeSeries( REAL8TimeSeries *series,
00529     REAL8 frequency, REAL8 amplitude, INT4 filtorder )
00530 {
00531   static const char *func = "XLALLowPassREAL8TimeSeries";
00532   PassBandParamStruc params;
00533   params.nMax = filtorder;
00534   params.f1   = frequency;
00535   params.a1   = amplitude;
00536   params.f2   = -1;
00537   params.a2   = -1;
00538   if ( XLALButterworthREAL8TimeSeries( series, &params ) < 0 )
00539     XLAL_ERROR( func, XLAL_EFUNC );
00540   return 0;
00541 }
00542 
00543 int XLALHighPassREAL4TimeSeries( REAL4TimeSeries *series,
00544     REAL8 frequency, REAL8 amplitude, INT4 filtorder )
00545 {
00546   static const char *func = "XLALHighPassREAL4TimeSeries";
00547   PassBandParamStruc params;
00548   params.nMax = filtorder;
00549   params.f2   = frequency;
00550   params.a2   = amplitude;
00551   params.f1   = -1;
00552   params.a1   = -1;
00553   if ( XLALButterworthREAL4TimeSeries( series, &params ) < 0 )
00554     XLAL_ERROR( func, XLAL_EFUNC );
00555   return 0;
00556 }
00557 
00558 int XLALHighPassREAL8TimeSeries( REAL8TimeSeries *series,
00559     REAL8 frequency, REAL8 amplitude, INT4 filtorder )
00560 {
00561   static const char *func = "XLALHighPassREAL8TimeSeries";
00562   PassBandParamStruc params;
00563   params.nMax = filtorder;
00564   params.f2   = frequency;
00565   params.a2   = amplitude;
00566   params.f1   = -1;
00567   params.a1   = -1;
00568   if ( XLALButterworthREAL8TimeSeries( series, &params ) < 0 )
00569     XLAL_ERROR( func, XLAL_EFUNC );
00570   return 0;
00571 }
00572 
00573 
00574 
00575 
00576 /*
00577  *
00578  * WARNING: THIS FUNCTION IS OBSOLETE.  DO NOT USE IT.
00579  *
00580  */ 
00581 
00582 /* <lalVerbatim file="ButterworthTimeSeriesD"> */
00583 void
00584 LALButterworthREAL4TimeSeries( LALStatus          *stat,
00585                                REAL4TimeSeries    *series,
00586                                PassBandParamStruc *params )
00587 { /* </lalVerbatim> */
00588   INT4 n;    /* The filter order. */
00589   INT4 type; /* The pass-band type: high, low, or undeterminable. */
00590   INT4 i;    /* An index. */
00591   INT4 j;    /* Another index. */
00592   REAL8 wc;  /* The filter's transformed frequency. */
00593 
00594   INITSTATUS(stat,"LALButterworthREAL4TimeSeries",BUTTERWORTHTIMESERIESC);
00595   ATTATCHSTATUSPTR(stat);
00596 
00597   /* Make sure the input pointers are non-null. */
00598   ASSERT(params,stat,BANDPASSTIMESERIESH_ENUL,
00599          BANDPASSTIMESERIESH_MSGENUL);
00600   ASSERT(series,stat,BANDPASSTIMESERIESH_ENUL,
00601          BANDPASSTIMESERIESH_MSGENUL);
00602   ASSERT(series->data,stat,BANDPASSTIMESERIESH_ENUL,
00603          BANDPASSTIMESERIESH_MSGENUL);
00604   ASSERT(series->data->data,stat,BANDPASSTIMESERIESH_ENUL,
00605          BANDPASSTIMESERIESH_MSGENUL);
00606 
00607   /* Parse the pass-band parameter structure.  I separate this into a
00608      local static subroutine because it's an icky mess of conditionals
00609      that would clutter the logic of the main routine. */
00610   type=XLALParsePassBandParamStruc(params,&n,&wc,series->deltaT);
00611   if(type<0) {
00612     XLALClearErrno();
00613     ABORT(stat,BANDPASSTIMESERIESH_EBAD,BANDPASSTIMESERIESH_MSGEBAD);
00614   }
00615 
00616   /* An order n Butterworth filter has n poles spaced evenly along a
00617      semicircle in the upper complex w-plane.  By pairing up poles
00618      symmetric across the imaginary axis, the filter gan be decomposed
00619      into [n/2] filters of order 2, plus perhaps an additional order 1
00620      filter.  The following loop pairs up poles and applies the
00621      filters with order 2. */
00622   for(i=0,j=n-1;i<j;i++,j--){
00623     REAL4 theta=LAL_PI*(i+0.5)/n;
00624     REAL4 ar=wc*cos(theta);
00625     REAL4 ai=wc*sin(theta);
00626     REAL4IIRFilter *iirFilter=NULL;
00627     COMPLEX8ZPGFilter *zpgFilter=NULL;
00628 
00629     /* Generate the filter in the w-plane. */
00630     if(type==2){
00631       TRY(LALCreateCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter,2,2),
00632           stat);
00633       zpgFilter->zeros->data[0].re=0.0;
00634       zpgFilter->zeros->data[0].im=0.0;
00635       zpgFilter->zeros->data[1].re=0.0;
00636       zpgFilter->zeros->data[1].im=0.0;
00637       zpgFilter->gain.re=1.0;
00638       zpgFilter->gain.im=0.0;
00639     }else{
00640       TRY(LALCreateCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter,0,2),
00641           stat);
00642       zpgFilter->gain.re=-wc*wc;
00643       zpgFilter->gain.im=0.0;
00644     }
00645     zpgFilter->poles->data[0].re=ar;
00646     zpgFilter->poles->data[0].im=ai;
00647     zpgFilter->poles->data[1].re=-ar;
00648     zpgFilter->poles->data[1].im=ai;
00649 
00650     /* Transform to the z-plane and create the IIR filter. */
00651     LALWToZCOMPLEX8ZPGFilter(stat->statusPtr,zpgFilter);
00652     BEGINFAIL(stat)
00653       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00654           stat);
00655     ENDFAIL(stat);
00656     LALCreateREAL4IIRFilter(stat->statusPtr,&iirFilter,zpgFilter);
00657     BEGINFAIL(stat)
00658       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00659           stat);
00660     ENDFAIL(stat);
00661 
00662     /* Filter the data, once each way. */
00663     LALIIRFilterREAL4Vector(stat->statusPtr,series->data,iirFilter);
00664     BEGINFAIL(stat) {
00665       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00666           stat);
00667       TRY(LALDestroyREAL4IIRFilter(stat->statusPtr,&iirFilter),stat);
00668     } ENDFAIL(stat);
00669     LALIIRFilterREAL4VectorR(stat->statusPtr,series->data,iirFilter);
00670     BEGINFAIL(stat) {
00671       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00672           stat);
00673       TRY(LALDestroyREAL4IIRFilter(stat->statusPtr,&iirFilter),stat);
00674     } ENDFAIL(stat);
00675 
00676     /* Free the filters. */
00677     TRY(LALDestroyREAL4IIRFilter(stat->statusPtr,&iirFilter),stat);
00678     TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),stat);
00679   }
00680 
00681   /* Next, this conditional applies the possible order 1 filter
00682      corresponding to an unpaired pole on the imaginary w axis. */
00683   if(i==j){
00684     REAL4IIRFilter *iirFilter=NULL;
00685     COMPLEX8ZPGFilter *zpgFilter=NULL;
00686 
00687     /* Generate the filter in the w-plane. */
00688     if(type==2){
00689       TRY(LALCreateCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter,1,1),
00690           stat);
00691       zpgFilter->zeros->data->re=0.0;
00692       zpgFilter->zeros->data->im=0.0;
00693       zpgFilter->gain.re=1.0;
00694       zpgFilter->gain.im=0.0;
00695     }else{
00696       TRY(LALCreateCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter,0,1),
00697           stat);
00698       zpgFilter->gain.re=0.0;
00699       zpgFilter->gain.im=-wc;
00700     }
00701     zpgFilter->poles->data->re=0.0;
00702     zpgFilter->poles->data->im=wc;
00703 
00704     /* Transform to the z-plane and create the IIR filter. */
00705     LALWToZCOMPLEX8ZPGFilter(stat->statusPtr,zpgFilter);
00706     BEGINFAIL(stat)
00707       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00708           stat);
00709     ENDFAIL(stat);
00710     LALCreateREAL4IIRFilter(stat->statusPtr,&iirFilter,zpgFilter);
00711     BEGINFAIL(stat)
00712       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00713           stat);
00714     ENDFAIL(stat);
00715 
00716     /* Filter the data, once each way. */
00717     LALIIRFilterREAL4Vector(stat->statusPtr,series->data,iirFilter);
00718     BEGINFAIL(stat) {
00719       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00720           stat);
00721       TRY(LALDestroyREAL4IIRFilter(stat->statusPtr,&iirFilter),stat);
00722     } ENDFAIL(stat);
00723     LALIIRFilterREAL4VectorR(stat->statusPtr,series->data,iirFilter);
00724     BEGINFAIL(stat) {
00725       TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),
00726           stat);
00727       TRY(LALDestroyREAL4IIRFilter(stat->statusPtr,&iirFilter),stat);
00728     } ENDFAIL(stat);
00729 
00730     /* Free the filters. */
00731     TRY(LALDestroyREAL4IIRFilter(stat->statusPtr,&iirFilter),stat);
00732     TRY(LALDestroyCOMPLEX8ZPGFilter(stat->statusPtr,&zpgFilter),stat);
00733   }
00734 
00735   /* Normal exit. */
00736   DETATCHSTATUSPTR(stat);
00737   RETURN(stat);
00738 }
00739 
00740 
00741 /* <lalVerbatim file="ButterworthTimeSeriesD"> */
00742 void
00743 LALButterworthREAL8TimeSeries( LALStatus          *stat,
00744                                REAL8TimeSeries    *series,
00745                                PassBandParamStruc *params )
00746 { /* </lalVerbatim> */
00747   INITSTATUS(stat,"LALButterworthREAL8TimeSeries",BUTTERWORTHTIMESERIESC);
00748 
00749   if (XLALButterworthREAL8TimeSeries(series,params)<0)
00750   {
00751     int code=xlalErrno;
00752     XLALClearErrno();
00753     switch(code){
00754       case XLAL_EFAULT:
00755         ABORT(stat,BANDPASSTIMESERIESH_ENUL,BANDPASSTIMESERIESH_MSGENUL);
00756       case XLAL_EINVAL:
00757         ABORT(stat,BANDPASSTIMESERIESH_EBAD,BANDPASSTIMESERIESH_MSGEBAD);
00758       default:
00759         ABORTXLAL(stat);
00760     }
00761   }
00762 
00763   RETURN(stat);
00764 }
00765 
00766 
00767 /* <lalVerbatim file="ButterworthTimeSeriesD"> */
00768 void
00769 LALDButterworthREAL4TimeSeries( LALStatus          *stat,
00770                                 REAL4TimeSeries    *series,
00771                                 PassBandParamStruc *params )
00772 { /* </lalVerbatim> */
00773   INITSTATUS(stat,"LALButterworthREAL8TimeSeries",BUTTERWORTHTIMESERIESC);
00774 
00775   if (XLALButterworthREAL4TimeSeries(series,params)<0)
00776   {
00777     int code=xlalErrno;
00778     XLALClearErrno();
00779     switch(code){
00780       case XLAL_EFAULT:
00781         ABORT(stat,BANDPASSTIMESERIESH_ENUL,BANDPASSTIMESERIESH_MSGENUL);
00782       case XLAL_EINVAL:
00783         ABORT(stat,BANDPASSTIMESERIESH_EBAD,BANDPASSTIMESERIESH_MSGEBAD);
00784       default:
00785         ABORTXLAL(stat);
00786     }
00787   }
00788 
00789   RETURN(stat);
00790 }
00791 
00792 
00793 static int
00794 XLALParsePassBandParamStruc( PassBandParamStruc *params,
00795                          INT4               *n,
00796                          REAL8              *wc,
00797                          REAL8              deltaT )
00798      /* This local function parses the pass band parameters according
00799         to the rules given in the documentation to this module,
00800         computing the order and characteristic frequency (in the
00801         w-variable) of the desired filter.  The function returns 2 for
00802         a high-pass filter, 1 for a low-pass filter, and -1 if the
00803         parameters were poorly specified. */
00804 {
00805   static const char *func = "XLALParsePassBandParamStruc";
00806   /* Okay, first define all the temporary variables, and figure out
00807      just which parameter combinations have been given. */
00808   REAL8 w1=deltaT*params->f1;    /* Dimensionless frequencies; */
00809   REAL8 w2=deltaT*params->f2;    /* later transformed to w-plane. */
00810   REAL8 a1=params->a1;           /* Attenuation factors; later a */
00811   REAL8 a2=