From octave-sources-request at bevo dot che dot wisc dot edu Thu Oct 18 21:19:42 2001 Subject: An octave port of Remez algorithm to design linear phase FIR filters From: Chee-Kiang dot Goh at infineon dot com To: octave-sources at bevo dot che dot wisc dot edu Date: Fri, 19 Oct 2001 10:19:22 +0800 This is a multi-part message in MIME format. --------------3E3A35CA73E3429EAB62C0FD Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Good day, I have created an octave DLD interface to the remez algorithm by Jake Janovetz. This algorithm can be used to design linear phase FIR filters. The interface is similar to that in Matlab. The original C source is by Jake Janovetz (janovetz at uiuc dot edu) dot I guess all the GPL stuff should apply to his code. Tested on solaris-2.5.1 with gcc 2.95.2 and octave 2.1.34 only. From my personal experience, it is not as numerically stable as that in Matlab, but does the job well for most cases with <=100 taps FIR filters. Please see attached files. On my system, the command to generate the oct file is mkoctfile remez.cc Hope you find it useful. Best Regards. -- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Goh Chee Kiang \e/ email: Chee-Kiang dot Goh at infineon dot com __o __o I Tel: +65-840-0334 `\<, `\<, `\\, Infineon Technologies Asia Pacific Pte Ltd _O/ O_________O/_O______O/_O_ 168, Kallang Way. Singapore 349253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --------------3E3A35CA73E3429EAB62C0FD Content-Type: text/plain; charset=us-ascii; name="remez.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="remez.c" /************************************************************************** * Parks-McClellan algorithm for FIR filter design (C version) *------------------------------------------------- * Copyright (c) 1995,1998 Jake Janovetz (janovetz at uiuc dot 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 * *************************************************************************/ #include "remez.h" #include /******************* * 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 [numband] * double weight[] - Weight per band [numband] * int symmetry - Symmetry of filter - used for grid check * * 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, double bands[], double des[], double weight[], int *gridsize, double Grid[], double D[], double W[], int symmetry) { int i, j, k, band; double delf, lowf, highf; delf = 0.5/(GRIDDENSITY*r); /* * For differentiator, hilbert, * symmetry is odd and Grid[0] = max(delf, band[0]) */ if ((symmetry == NEGATIVE) && (delf > bands[0])) bands[0] = delf; j=0; for (band=0; band < numband; band++) { Grid[j] = bands[2*band]; lowf = 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]0.0) && (E[j]>E[j-1])) || ((E[j]<0.0) && (E[j] 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; 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]])) l = foundExt[k-1]; /* Delete last extremal */ else l = foundExt[0]; /* Delete first extremal */ } for (j=l; j max) max = current; } *maxErr=max; if (((max-min)/max) < 0.001) return 1; return 0; } /******************** * 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] ********************/ void remez(double h[], int numtaps, int numband, double bands[], double des[], double weight[], int type, double *maxErr) { 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 * Share your ideas and innovation ! *************************************************************************/ #include #include "remez.c" #include #include DEFUN_DLD ( remez, args, , "\ remez(): Remez (AKA Parks-McCellan) algorithm for linear phase\n\ FIR filter design.\n\ Usages:\n\ [h,err]=remez(N,F,A)\n\ [h,err]=remez(N,F,A,'Hilbert')\n\ [h,err]=remez(N,F,A,'differentiator')\n\ [h,err]=remez(N,F,A,W)\n\ [h,err]=remez(N,F,A,W,'Hilbert')\n\ [h,err]=remez(N,F,A,W,'differentiator')\n\ \n\ N: FIR filter length\n\ F: Vector of frequency band edges (0-1) in pairs.\n\ e.g. F=[0 0.5 0.7 1] specifies two bands 0-0.5 and 0.7-1. 0.5-0.7 \n\ is considered as a transition band (don't care).\n\ A: Vector of the desired amplitude at each band edge.\n\ Should be same length as F. e.g. A=[1 1 0 0] specifies 0-0.5 have\n\ gain of 1, and 0.7-0.1 have gain of 0. NB: only flat gain function is\n\ implemented for now (10-2001)\n\ W: Vector of weighting function for the ripple ratio between each band.\n\ Same specification as A.\n\ 'Hilbert', 'differentiator': specifies either a hilbert transformer or\n\ differentiator\n") { octave_value_list retval; double *weights, *desired, *bands; double *h, maxErr; int i,type=BANDPASS, flagweight=0; int nargin=(args.length()); if (nargin<3) { print_usage("remez"); return retval; } int fl (args(0).int_value()); if (fl<2) { printf("remez problem: we need at least an FIR filter of 2 taps !!\n"); return retval; } ColumnVector X ( fl ); int nband (args(1).length()/2); if (args(2).vector_value().length()=4) { if (args(3).is_string()) { if (strcmp(args(3).string_value().data(),"Hilbert")==0) type = HILBERT; else if (strcmp(args(3).string_value().data(),"differentiator")==0) type = DIFFERENTIATOR; } else { if (args(3).vector_value().length()4) if (strcmp(args(4).string_value().data(),"Hilbert")==0) type = HILBERT; else if (strcmp(args(4).string_value().data(),"differentiator")==0) type = DIFFERENTIATOR; } } bands = (double *)malloc(nband*2 * sizeof(double)); weights = (double *)malloc(nband * sizeof(double)); desired = (double *)malloc(nband * sizeof(double)); h = (double *)malloc(fl * sizeof(double)); if (h==NULL || desired ==NULL || weights==NULL || bands ==NULL) { if (h) free(h); if (desired) free(desired); if (weights) free(weights); if (bands) free(bands); printf("remez problem: can't allocate memory !\n"); return retval; } for (i=0;i0.5) { printf("remez problem: found a -ve freq. band edge !!\n"); return retval; } } for (i=0;i