/************************************************************************** * Parks-McClellan algorithm for FIR filter design (C version) *------------------------------------------------- * Copyright (c) 1995,1998 Jake Janovetz (janovetz@uiuc.edu) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * Sep 1999 - Paul Kienzle (pkienzle@cs.indiana.edu) * Modified for use in octave as a replacement for the matlab function * remez.mex. In particular, magnitude responses are required for all * band edges rather than one per band, griddensity is a parameter, * and errors are returned rather than printed directly. * Mar 2000 - Kai Habel (kahacjde@linux.zrz.tu-berlin.de) * Change: ColumnVector x=arg(i).vector_value(); * to: ColumnVector x(arg(i).vector_value()); * There appear to be some problems with the routine Search. See comments * therein [search for PAK:]. I haven't looked closely at the rest * of the code---it may also have some problems. *************************************************************************/ #include #include #define CONST const #define BANDPASS 1 #define DIFFERENTIATOR 2 #define HILBERT 3 #define NEGATIVE 0 #define POSITIVE 1 #define Pi 3.1415926535897932 #define Pi2 6.2831853071795865 #define GRIDDENSITY 16 #define MAXITERATIONS 40 /******************* * CreateDenseGrid *================= * Creates the dense grid of frequencies from the specified bands. * Also creates the Desired Frequency Response function (D[]) and * the Weight function (W[]) on that dense grid * * * INPUT: * ------ * int r - 1/2 the number of filter coefficients * int numtaps - Number of taps in the resulting filter * int numband - Number of bands in user specification * double bands[] - User-specified band edges [2*numband] * double des[] - Desired response per band [2*numband] * double weight[] - Weight per band [numband] * int symmetry - Symmetry of filter - used for grid check * int griddensity * * OUTPUT: * ------- * int gridsize - Number of elements in the dense frequency grid * double Grid[] - Frequencies (0 to 0.5) on the dense grid [gridsize] * double D[] - Desired response on the dense grid [gridsize] * double W[] - Weight function on the dense grid [gridsize] *******************/ void CreateDenseGrid(int r, int numtaps, int numband, const double bands[], const double des[], const double weight[], int gridsize, double Grid[], double D[], double W[], int symmetry, int griddensity) { int i, j, k, band, band0_override; double delf, lowf, highf, grid0; delf = 0.5/(griddensity*r); /* * For differentiator, hilbert, * symmetry is odd and Grid[0] = max(delf, bands[0]) */ grid0 = (symmetry == NEGATIVE) && (delf > bands[0]) ? delf : bands[0]; j=0; for (band=0; band < numband; band++) { lowf = (band==0 ? grid0 : bands[2*band]); highf = bands[2*band + 1]; k = (int)((highf - lowf)/delf + 0.5); /* .5 for rounding */ for (i=0; i (0.5 - delf)) && (numtaps % 2)) { Grid[gridsize-1] = 0.5-delf; } } /******************** * InitialGuess *============== * Places Extremal Frequencies evenly throughout the dense grid. * * * INPUT: * ------ * int r - 1/2 the number of filter coefficients * int gridsize - Number of elements in the dense frequency grid * * OUTPUT: * ------- * int Ext[] - Extremal indexes to dense frequency grid [r+1] ********************/ void InitialGuess(int r, int Ext[], int gridsize) { int i; for (i=0; i<=r; i++) Ext[i] = i * (gridsize-1) / r; } /*********************** * CalcParms *=========== * * * INPUT: * ------ * int r - 1/2 the number of filter coefficients * int Ext[] - Extremal indexes to dense frequency grid [r+1] * double Grid[] - Frequencies (0 to 0.5) on the dense grid [gridsize] * double D[] - Desired response on the dense grid [gridsize] * double W[] - Weight function on the dense grid [gridsize] * * OUTPUT: * ------- * double ad[] - 'b' in Oppenheim & Schafer [r+1] * double x[] - [r+1] * double y[] - 'C' in Oppenheim & Schafer [r+1] ***********************/ void CalcParms(int r, int Ext[], double Grid[], double D[], double W[], double ad[], double x[], double y[]) { int i, j, k, ld; double sign, xi, delta, denom, numer; /* * Find x[] */ for (i=0; i<=r; i++) x[i] = cos(Pi2 * Grid[Ext[i]]); /* * Calculate ad[] - Oppenheim & Schafer eq 7.132 */ ld = (r-1)/15 + 1; /* Skips around to avoid round errors */ for (i=0; i<=r; i++) { denom = 1.0; xi = x[i]; for (j=0; j0.0) && (E[0]>E[1])) || ((E[0]<0.0) && (E[0]=E[i-1]) && (E[i]>E[i+1]) && (E[i]>0.0)) || ((E[i]<=E[i-1]) && (E[i]= 2*r) return -3; foundExt[k++] = i; } } /* * Check for extremum at 0.5 */ j = gridsize-1; if (((E[j]>0.0) && (E[j]>E[j-1])) || ((E[j]<0.0) && (E[j]= 2*r) return -3; foundExt[k++] = j; } // PAK: we sometimes get not enough extremal frequencies if (k < r+1) return -2; /* * Remove extra extremals */ extra = k - (r+1); assert(extra >= 0); while (extra > 0) { if (E[foundExt[0]] > 0.0) up = 1; /* first one is a maxima */ else up = 0; /* first one is a minima */ l=0; alt = 1; for (j=1; j 0.0)) up = 1; /* switch to a maxima */ else { alt = 0; // PAK: break now and you will delete the smallest overall // extremal. If you want to delete the smallest of the // pair of non-alternating extremals, then you must do: // // if (fabs(E[foundExt[j]]) < fabs(E[foundExt[j-1]])) l=j; // else l=j-1; break; /* Ooops, found two non-alternating */ } /* extrema. Delete smallest of them */ } /* if the loop finishes, all extrema are alternating */ /* * If there's only one extremal and all are alternating, * delete the smallest of the first/last extremals. */ if ((alt) && (extra == 1)) { if (fabs(E[foundExt[k-1]]) < fabs(E[foundExt[0]])) /* Delete last extremal */ l = k-1; // PAK: changed from l = foundExt[k-1]; else /* Delete first extremal */ l = 0; // PAK: changed from l = foundExt[0]; } for (j=l; j max) max = current; } return (((max-min)/max) < 0.0001); } /******************** * remez *======= * Calculates the optimal (in the Chebyshev/minimax sense) * FIR filter impulse response given a set of band edges, * the desired reponse on those bands, and the weight given to * the error in those bands. * * INPUT: * ------ * int numtaps - Number of filter coefficients * int numband - Number of bands in filter specification * double bands[] - User-specified band edges [2 * numband] * double des[] - User-specified band responses [numband] * double weight[] - User-specified error weights [numband] * int type - Type of filter * * OUTPUT: * ------- * double h[] - Impulse response of final filter [numtaps] * returns - true on success, false on failure to converge ********************/ int remez(double h[], int numtaps, int numband, const double bands[], const double des[], const double weight[], int type, int griddensity) { double *Grid, *W, *D, *E; int i, iter, gridsize, r, *Ext; double *taps, c; double *x, *y, *ad; int symmetry; if (type == BANDPASS) symmetry = POSITIVE; else symmetry = NEGATIVE; r = numtaps/2; /* number of extrema */ if ((numtaps%2) && (symmetry == POSITIVE)) r++; /* * Predict dense grid size in advance for memory allocation * .5 is so we round up, not truncate */ gridsize = 0; for (i=0; i 0.0001) W[i] = W[i]/Grid[i]; } } /* * For odd or Negative symmetry filters, alter the * D[] and W[] according to Parks McClellan */ if (symmetry == POSITIVE) { if (numtaps % 2 == 0) { for (i=0; i 6) { print_usage("remez"); return retval; } int numtaps = NINT (args(0).double_value()) + 1; // #coeff = filter order+1 if (numtaps < 4) { error("remez: number of taps must be an integer greater than 3"); return retval; } ColumnVector o_bands(args(1).vector_value()); int numbands = o_bands.length()/2; double bands[numbands*2]; if (numbands < 1 || o_bands.length()%2 == 1) { error("remez: must have an even number of band edges"); return retval; } for (i=1; i < o_bands.length(); i++) { if (o_bands(i) 1) { error("band edges must be in the range [0,1]"); return retval; } for(i=0; i < 2*numbands; i++) bands[i] = o_bands(i)/2.0; ColumnVector o_response(args(2).vector_value()); double response[numbands*2]; if (o_response.length() != o_bands.length()) { error("remez: must have one response magnitude for each band edge"); return retval; } for(i=0; i < 2*numbands; i++) response[i] = o_response(i); string stype = string("bandpass"); int density = 16; double weight[numbands]; for (i=0; i < numbands; i++) weight[i] = 1.0; if (nargin > 3) { if (args(3).is_real_matrix()) { ColumnVector o_weight(args(3).vector_value()); if (o_weight.length() != numbands) { error("remez: need one weight for each band [=length(band)/2]"); return retval; } for (i=0; i < numbands; i++) weight[i] = o_weight(i); } else if (args(3).is_string()) stype = args(3).string_value(); else if (args(3).is_real_scalar()) density = NINT(args(3).double_value()); else { error("remez: incorrect argument list"); return retval; } } if (nargin > 4) { if (args(4).is_string() && !args(3).is_string()) stype = args(4).string_value(); else if (args(4).is_real_scalar() && !args(3).is_real_scalar()) density = NINT(args(4).double_value()); else { error("remez: incorrect argument list"); return retval; } } if (nargin > 5) { if (args(5).is_real_scalar() && !args(4).is_real_scalar() && !args(3).is_real_scalar()) density = NINT(args(4).double_value()); else { error("remez: incorrect argument list"); return retval; } } int itype; if (stype == "bandpass") itype = BANDPASS; else if (stype == "differentiator") itype = DIFFERENTIATOR; else if (stype == "hilbert") itype = HILBERT; else { error("remez: unknown ftype '%s'", stype.data()); return retval; } if (density < 16) { error("remez: griddensity is too low; must be greater than 16"); return retval; } double coeff[numtaps+5]; int err = remez(coeff,numtaps,numbands,bands,response,weight,itype,density); if (err == -1) warning("remez: -- failed to converge -- returned filter may be bad."); else if (err == -2) { error("remez: insufficient extremals--cannot continue"); return retval; } else if (err == -3) { error("remez: too many extremals--cannot continue"); return retval; } ColumnVector h(numtaps); while(numtaps--) h(numtaps) = coeff[numtaps]; return octave_value(h); }