Mercurial > mplayer.hg
comparison libao2/remez.c @ 3482:3f041e737e62
code by Jake Janovetz to find FIR filter coefficients using the Parks-McClellan algorithm
author | steve |
---|---|
date | Thu, 13 Dec 2001 23:33:50 +0000 |
parents | |
children | b608086bf84e |
comparison
equal
deleted
inserted
replaced
3481:79e046b9e877 | 3482:3f041e737e62 |
---|---|
1 /************************************************************************** | |
2 * Parks-McClellan algorithm for FIR filter design (C version) | |
3 *------------------------------------------------- | |
4 * Copyright (c) 1995,1998 Jake Janovetz (janovetz@uiuc.edu) | |
5 * | |
6 * This library is free software; you can redistribute it and/or | |
7 * modify it under the terms of the GNU Library General Public | |
8 * License as published by the Free Software Foundation; either | |
9 * version 2 of the License, or (at your option) any later version. | |
10 * | |
11 * This library is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * Library General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU Library General Public | |
17 * License along with this library; if not, write to the Free | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 * | |
20 *************************************************************************/ | |
21 | |
22 | |
23 #include "remez.h" | |
24 #include <math.h> | |
25 | |
26 /******************* | |
27 * CreateDenseGrid | |
28 *================= | |
29 * Creates the dense grid of frequencies from the specified bands. | |
30 * Also creates the Desired Frequency Response function (D[]) and | |
31 * the Weight function (W[]) on that dense grid | |
32 * | |
33 * | |
34 * INPUT: | |
35 * ------ | |
36 * int r - 1/2 the number of filter coefficients | |
37 * int numtaps - Number of taps in the resulting filter | |
38 * int numband - Number of bands in user specification | |
39 * double bands[] - User-specified band edges [2*numband] | |
40 * double des[] - Desired response per band [numband] | |
41 * double weight[] - Weight per band [numband] | |
42 * int symmetry - Symmetry of filter - used for grid check | |
43 * | |
44 * OUTPUT: | |
45 * ------- | |
46 * int gridsize - Number of elements in the dense frequency grid | |
47 * double Grid[] - Frequencies (0 to 0.5) on the dense grid [gridsize] | |
48 * double D[] - Desired response on the dense grid [gridsize] | |
49 * double W[] - Weight function on the dense grid [gridsize] | |
50 *******************/ | |
51 | |
52 void CreateDenseGrid(int r, int numtaps, int numband, double bands[], | |
53 double des[], double weight[], int *gridsize, | |
54 double Grid[], double D[], double W[], | |
55 int symmetry) | |
56 { | |
57 int i, j, k, band; | |
58 double delf, lowf, highf; | |
59 | |
60 delf = 0.5/(GRIDDENSITY*r); | |
61 | |
62 /* | |
63 * For differentiator, hilbert, | |
64 * symmetry is odd and Grid[0] = max(delf, band[0]) | |
65 */ | |
66 | |
67 if ((symmetry == NEGATIVE) && (delf > bands[0])) | |
68 bands[0] = delf; | |
69 | |
70 j=0; | |
71 for (band=0; band < numband; band++) | |
72 { | |
73 Grid[j] = bands[2*band]; | |
74 lowf = bands[2*band]; | |
75 highf = bands[2*band + 1]; | |
76 k = (int)((highf - lowf)/delf + 0.5); /* .5 for rounding */ | |
77 for (i=0; i<k; i++) | |
78 { | |
79 D[j] = des[band]; | |
80 W[j] = weight[band]; | |
81 Grid[j] = lowf; | |
82 lowf += delf; | |
83 j++; | |
84 } | |
85 Grid[j-1] = highf; | |
86 } | |
87 | |
88 /* | |
89 * Similar to above, if odd symmetry, last grid point can't be .5 | |
90 * - but, if there are even taps, leave the last grid point at .5 | |
91 */ | |
92 if ((symmetry == NEGATIVE) && | |
93 (Grid[*gridsize-1] > (0.5 - delf)) && | |
94 (numtaps % 2)) | |
95 { | |
96 Grid[*gridsize-1] = 0.5-delf; | |
97 } | |
98 } | |
99 | |
100 | |
101 /******************** | |
102 * InitialGuess | |
103 *============== | |
104 * Places Extremal Frequencies evenly throughout the dense grid. | |
105 * | |
106 * | |
107 * INPUT: | |
108 * ------ | |
109 * int r - 1/2 the number of filter coefficients | |
110 * int gridsize - Number of elements in the dense frequency grid | |
111 * | |
112 * OUTPUT: | |
113 * ------- | |
114 * int Ext[] - Extremal indexes to dense frequency grid [r+1] | |
115 ********************/ | |
116 | |
117 void InitialGuess(int r, int Ext[], int gridsize) | |
118 { | |
119 int i; | |
120 | |
121 for (i=0; i<=r; i++) | |
122 Ext[i] = i * (gridsize-1) / r; | |
123 } | |
124 | |
125 | |
126 /*********************** | |
127 * CalcParms | |
128 *=========== | |
129 * | |
130 * | |
131 * INPUT: | |
132 * ------ | |
133 * int r - 1/2 the number of filter coefficients | |
134 * int Ext[] - Extremal indexes to dense frequency grid [r+1] | |
135 * double Grid[] - Frequencies (0 to 0.5) on the dense grid [gridsize] | |
136 * double D[] - Desired response on the dense grid [gridsize] | |
137 * double W[] - Weight function on the dense grid [gridsize] | |
138 * | |
139 * OUTPUT: | |
140 * ------- | |
141 * double ad[] - 'b' in Oppenheim & Schafer [r+1] | |
142 * double x[] - [r+1] | |
143 * double y[] - 'C' in Oppenheim & Schafer [r+1] | |
144 ***********************/ | |
145 | |
146 void CalcParms(int r, int Ext[], double Grid[], double D[], double W[], | |
147 double ad[], double x[], double y[]) | |
148 { | |
149 int i, j, k, ld; | |
150 double sign, xi, delta, denom, numer; | |
151 | |
152 /* | |
153 * Find x[] | |
154 */ | |
155 for (i=0; i<=r; i++) | |
156 x[i] = cos(Pi2 * Grid[Ext[i]]); | |
157 | |
158 /* | |
159 * Calculate ad[] - Oppenheim & Schafer eq 7.132 | |
160 */ | |
161 ld = (r-1)/15 + 1; /* Skips around to avoid round errors */ | |
162 for (i=0; i<=r; i++) | |
163 { | |
164 denom = 1.0; | |
165 xi = x[i]; | |
166 for (j=0; j<ld; j++) | |
167 { | |
168 for (k=j; k<=r; k+=ld) | |
169 if (k != i) | |
170 denom *= 2.0*(xi - x[k]); | |
171 } | |
172 if (fabs(denom)<0.00001) | |
173 denom = 0.00001; | |
174 ad[i] = 1.0/denom; | |
175 } | |
176 | |
177 /* | |
178 * Calculate delta - Oppenheim & Schafer eq 7.131 | |
179 */ | |
180 numer = denom = 0; | |
181 sign = 1; | |
182 for (i=0; i<=r; i++) | |
183 { | |
184 numer += ad[i] * D[Ext[i]]; | |
185 denom += sign * ad[i]/W[Ext[i]]; | |
186 sign = -sign; | |
187 } | |
188 delta = numer/denom; | |
189 sign = 1; | |
190 | |
191 /* | |
192 * Calculate y[] - Oppenheim & Schafer eq 7.133b | |
193 */ | |
194 for (i=0; i<=r; i++) | |
195 { | |
196 y[i] = D[Ext[i]] - sign * delta/W[Ext[i]]; | |
197 sign = -sign; | |
198 } | |
199 } | |
200 | |
201 | |
202 /********************* | |
203 * ComputeA | |
204 *========== | |
205 * Using values calculated in CalcParms, ComputeA calculates the | |
206 * actual filter response at a given frequency (freq). Uses | |
207 * eq 7.133a from Oppenheim & Schafer. | |
208 * | |
209 * | |
210 * INPUT: | |
211 * ------ | |
212 * double freq - Frequency (0 to 0.5) at which to calculate A | |
213 * int r - 1/2 the number of filter coefficients | |
214 * double ad[] - 'b' in Oppenheim & Schafer [r+1] | |
215 * double x[] - [r+1] | |
216 * double y[] - 'C' in Oppenheim & Schafer [r+1] | |
217 * | |
218 * OUTPUT: | |
219 * ------- | |
220 * Returns double value of A[freq] | |
221 *********************/ | |
222 | |
223 double ComputeA(double freq, int r, double ad[], double x[], double y[]) | |
224 { | |
225 int i; | |
226 double xc, c, denom, numer; | |
227 | |
228 denom = numer = 0; | |
229 xc = cos(Pi2 * freq); | |
230 for (i=0; i<=r; i++) | |
231 { | |
232 c = xc - x[i]; | |
233 if (fabs(c) < 1.0e-7) | |
234 { | |
235 numer = y[i]; | |
236 denom = 1; | |
237 break; | |
238 } | |
239 c = ad[i]/c; | |
240 denom += c; | |
241 numer += c*y[i]; | |
242 } | |
243 return numer/denom; | |
244 } | |
245 | |
246 | |
247 /************************ | |
248 * CalcError | |
249 *=========== | |
250 * Calculates the Error function from the desired frequency response | |
251 * on the dense grid (D[]), the weight function on the dense grid (W[]), | |
252 * and the present response calculation (A[]) | |
253 * | |
254 * | |
255 * INPUT: | |
256 * ------ | |
257 * int r - 1/2 the number of filter coefficients | |
258 * double ad[] - [r+1] | |
259 * double x[] - [r+1] | |
260 * double y[] - [r+1] | |
261 * int gridsize - Number of elements in the dense frequency grid | |
262 * double Grid[] - Frequencies on the dense grid [gridsize] | |
263 * double D[] - Desired response on the dense grid [gridsize] | |
264 * double W[] - Weight function on the desnse grid [gridsize] | |
265 * | |
266 * OUTPUT: | |
267 * ------- | |
268 * double E[] - Error function on dense grid [gridsize] | |
269 ************************/ | |
270 | |
271 void CalcError(int r, double ad[], double x[], double y[], | |
272 int gridsize, double Grid[], | |
273 double D[], double W[], double E[]) | |
274 { | |
275 int i; | |
276 double A; | |
277 | |
278 for (i=0; i<gridsize; i++) | |
279 { | |
280 A = ComputeA(Grid[i], r, ad, x, y); | |
281 E[i] = W[i] * (D[i] - A); | |
282 } | |
283 } | |
284 | |
285 /************************ | |
286 * Search | |
287 *======== | |
288 * Searches for the maxima/minima of the error curve. If more than | |
289 * r+1 extrema are found, it uses the following heuristic (thanks | |
290 * Chris Hanson): | |
291 * 1) Adjacent non-alternating extrema deleted first. | |
292 * 2) If there are more than one excess extrema, delete the | |
293 * one with the smallest error. This will create a non-alternation | |
294 * condition that is fixed by 1). | |
295 * 3) If there is exactly one excess extremum, delete the smaller | |
296 * of the first/last extremum | |
297 * | |
298 * | |
299 * INPUT: | |
300 * ------ | |
301 * int r - 1/2 the number of filter coefficients | |
302 * int Ext[] - Indexes to Grid[] of extremal frequencies [r+1] | |
303 * int gridsize - Number of elements in the dense frequency grid | |
304 * double E[] - Array of error values. [gridsize] | |
305 * OUTPUT: | |
306 * ------- | |
307 * int Ext[] - New indexes to extremal frequencies [r+1] | |
308 ************************/ | |
309 | |
310 void Search(int r, int Ext[], | |
311 int gridsize, double E[]) | |
312 { | |
313 int i, j, k, l, extra; /* Counters */ | |
314 int up, alt; | |
315 int *foundExt; /* Array of found extremals */ | |
316 | |
317 /* | |
318 * Allocate enough space for found extremals. | |
319 */ | |
320 foundExt = (int *)malloc((2*r) * sizeof(int)); | |
321 k = 0; | |
322 | |
323 /* | |
324 * Check for extremum at 0. | |
325 */ | |
326 if (((E[0]>0.0) && (E[0]>E[1])) || | |
327 ((E[0]<0.0) && (E[0]<E[1]))) | |
328 foundExt[k++] = 0; | |
329 | |
330 /* | |
331 * Check for extrema inside dense grid | |
332 */ | |
333 for (i=1; i<gridsize-1; i++) | |
334 { | |
335 if (((E[i]>=E[i-1]) && (E[i]>E[i+1]) && (E[i]>0.0)) || | |
336 ((E[i]<=E[i-1]) && (E[i]<E[i+1]) && (E[i]<0.0))) | |
337 foundExt[k++] = i; | |
338 } | |
339 | |
340 /* | |
341 * Check for extremum at 0.5 | |
342 */ | |
343 j = gridsize-1; | |
344 if (((E[j]>0.0) && (E[j]>E[j-1])) || | |
345 ((E[j]<0.0) && (E[j]<E[j-1]))) | |
346 foundExt[k++] = j; | |
347 | |
348 | |
349 /* | |
350 * Remove extra extremals | |
351 */ | |
352 extra = k - (r+1); | |
353 | |
354 while (extra > 0) | |
355 { | |
356 if (E[foundExt[0]] > 0.0) | |
357 up = 1; /* first one is a maxima */ | |
358 else | |
359 up = 0; /* first one is a minima */ | |
360 | |
361 l=0; | |
362 alt = 1; | |
363 for (j=1; j<k; j++) | |
364 { | |
365 if (fabs(E[foundExt[j]]) < fabs(E[foundExt[l]])) | |
366 l = j; /* new smallest error. */ | |
367 if ((up) && (E[foundExt[j]] < 0.0)) | |
368 up = 0; /* switch to a minima */ | |
369 else if ((!up) && (E[foundExt[j]] > 0.0)) | |
370 up = 1; /* switch to a maxima */ | |
371 else | |
372 { | |
373 alt = 0; | |
374 break; /* Ooops, found two non-alternating */ | |
375 } /* extrema. Delete smallest of them */ | |
376 } /* if the loop finishes, all extrema are alternating */ | |
377 | |
378 /* | |
379 * If there's only one extremal and all are alternating, | |
380 * delete the smallest of the first/last extremals. | |
381 */ | |
382 if ((alt) && (extra == 1)) | |
383 { | |
384 if (fabs(E[foundExt[k-1]]) < fabs(E[foundExt[0]])) | |
385 l = foundExt[k-1]; /* Delete last extremal */ | |
386 else | |
387 l = foundExt[0]; /* Delete first extremal */ | |
388 } | |
389 | |
390 for (j=l; j<k; j++) /* Loop that does the deletion */ | |
391 { | |
392 foundExt[j] = foundExt[j+1]; | |
393 } | |
394 k--; | |
395 extra--; | |
396 } | |
397 | |
398 for (i=0; i<=r; i++) | |
399 { | |
400 Ext[i] = foundExt[i]; /* Copy found extremals to Ext[] */ | |
401 } | |
402 | |
403 free(foundExt); | |
404 } | |
405 | |
406 | |
407 /********************* | |
408 * FreqSample | |
409 *============ | |
410 * Simple frequency sampling algorithm to determine the impulse | |
411 * response h[] from A's found in ComputeA | |
412 * | |
413 * | |
414 * INPUT: | |
415 * ------ | |
416 * int N - Number of filter coefficients | |
417 * double A[] - Sample points of desired response [N/2] | |
418 * int symmetry - Symmetry of desired filter | |
419 * | |
420 * OUTPUT: | |
421 * ------- | |
422 * double h[] - Impulse Response of final filter [N] | |
423 *********************/ | |
424 void FreqSample(int N, double A[], double h[], int symm) | |
425 { | |
426 int n, k; | |
427 double x, val, M; | |
428 | |
429 M = (N-1.0)/2.0; | |
430 if (symm == POSITIVE) | |
431 { | |
432 if (N%2) | |
433 { | |
434 for (n=0; n<N; n++) | |
435 { | |
436 val = A[0]; | |
437 x = Pi2 * (n - M)/N; | |
438 for (k=1; k<=M; k++) | |
439 val += 2.0 * A[k] * cos(x*k); | |
440 h[n] = val/N; | |
441 } | |
442 } | |
443 else | |
444 { | |
445 for (n=0; n<N; n++) | |
446 { | |
447 val = A[0]; | |
448 x = Pi2 * (n - M)/N; | |
449 for (k=1; k<=(N/2-1); k++) | |
450 val += 2.0 * A[k] * cos(x*k); | |
451 h[n] = val/N; | |
452 } | |
453 } | |
454 } | |
455 else | |
456 { | |
457 if (N%2) | |
458 { | |
459 for (n=0; n<N; n++) | |
460 { | |
461 val = 0; | |
462 x = Pi2 * (n - M)/N; | |
463 for (k=1; k<=M; k++) | |
464 val += 2.0 * A[k] * sin(x*k); | |
465 h[n] = val/N; | |
466 } | |
467 } | |
468 else | |
469 { | |
470 for (n=0; n<N; n++) | |
471 { | |
472 val = A[N/2] * sin(Pi * (n - M)); | |
473 x = Pi2 * (n - M)/N; | |
474 for (k=1; k<=(N/2-1); k++) | |
475 val += 2.0 * A[k] * sin(x*k); | |
476 h[n] = val/N; | |
477 } | |
478 } | |
479 } | |
480 } | |
481 | |
482 /******************* | |
483 * isDone | |
484 *======== | |
485 * Checks to see if the error function is small enough to consider | |
486 * the result to have converged. | |
487 * | |
488 * INPUT: | |
489 * ------ | |
490 * int r - 1/2 the number of filter coeffiecients | |
491 * int Ext[] - Indexes to extremal frequencies [r+1] | |
492 * double E[] - Error function on the dense grid [gridsize] | |
493 * | |
494 * OUTPUT: | |
495 * ------- | |
496 * Returns 1 if the result converged | |
497 * Returns 0 if the result has not converged | |
498 ********************/ | |
499 | |
500 short isDone(int r, int Ext[], double E[]) | |
501 { | |
502 int i; | |
503 double min, max, current; | |
504 | |
505 min = max = fabs(E[Ext[0]]); | |
506 for (i=1; i<=r; i++) | |
507 { | |
508 current = fabs(E[Ext[i]]); | |
509 if (current < min) | |
510 min = current; | |
511 if (current > max) | |
512 max = current; | |
513 } | |
514 if (((max-min)/max) < 0.0001) | |
515 return 1; | |
516 return 0; | |
517 } | |
518 | |
519 /******************** | |
520 * remez | |
521 *======= | |
522 * Calculates the optimal (in the Chebyshev/minimax sense) | |
523 * FIR filter impulse response given a set of band edges, | |
524 * the desired reponse on those bands, and the weight given to | |
525 * the error in those bands. | |
526 * | |
527 * INPUT: | |
528 * ------ | |
529 * int numtaps - Number of filter coefficients | |
530 * int numband - Number of bands in filter specification | |
531 * double bands[] - User-specified band edges [2 * numband] | |
532 * double des[] - User-specified band responses [numband] | |
533 * double weight[] - User-specified error weights [numband] | |
534 * int type - Type of filter | |
535 * | |
536 * OUTPUT: | |
537 * ------- | |
538 * double h[] - Impulse response of final filter [numtaps] | |
539 ********************/ | |
540 | |
541 void remez(double h[], int numtaps, | |
542 int numband, double bands[], double des[], double weight[], | |
543 int type) | |
544 { | |
545 double *Grid, *W, *D, *E; | |
546 int i, iter, gridsize, r, *Ext; | |
547 double *taps, c; | |
548 double *x, *y, *ad; | |
549 int symmetry; | |
550 | |
551 if (type == BANDPASS) | |
552 symmetry = POSITIVE; | |
553 else | |
554 symmetry = NEGATIVE; | |
555 | |
556 r = numtaps/2; /* number of extrema */ | |
557 if ((numtaps%2) && (symmetry == POSITIVE)) | |
558 r++; | |
559 | |
560 /* | |
561 * Predict dense grid size in advance for memory allocation | |
562 * .5 is so we round up, not truncate | |
563 */ | |
564 gridsize = 0; | |
565 for (i=0; i<numband; i++) | |
566 { | |
567 gridsize += (int)(2*r*GRIDDENSITY*(bands[2*i+1] - bands[2*i]) + .5); | |
568 } | |
569 if (symmetry == NEGATIVE) | |
570 { | |
571 gridsize--; | |
572 } | |
573 | |
574 /* | |
575 * Dynamically allocate memory for arrays with proper sizes | |
576 */ | |
577 Grid = (double *)malloc(gridsize * sizeof(double)); | |
578 D = (double *)malloc(gridsize * sizeof(double)); | |
579 W = (double *)malloc(gridsize * sizeof(double)); | |
580 E = (double *)malloc(gridsize * sizeof(double)); | |
581 Ext = (int *)malloc((r+1) * sizeof(int)); | |
582 taps = (double *)malloc((r+1) * sizeof(double)); | |
583 x = (double *)malloc((r+1) * sizeof(double)); | |
584 y = (double *)malloc((r+1) * sizeof(double)); | |
585 ad = (double *)malloc((r+1) * sizeof(double)); | |
586 | |
587 /* | |
588 * Create dense frequency grid | |
589 */ | |
590 CreateDenseGrid(r, numtaps, numband, bands, des, weight, | |
591 &gridsize, Grid, D, W, symmetry); | |
592 InitialGuess(r, Ext, gridsize); | |
593 | |
594 /* | |
595 * For Differentiator: (fix grid) | |
596 */ | |
597 if (type == DIFFERENTIATOR) | |
598 { | |
599 for (i=0; i<gridsize; i++) | |
600 { | |
601 /* D[i] = D[i]*Grid[i]; */ | |
602 if (D[i] > 0.0001) | |
603 W[i] = W[i]/Grid[i]; | |
604 } | |
605 } | |
606 | |
607 /* | |
608 * For odd or Negative symmetry filters, alter the | |
609 * D[] and W[] according to Parks McClellan | |
610 */ | |
611 if (symmetry == POSITIVE) | |
612 { | |
613 if (numtaps % 2 == 0) | |
614 { | |
615 for (i=0; i<gridsize; i++) | |
616 { | |
617 c = cos(Pi * Grid[i]); | |
618 D[i] /= c; | |
619 W[i] *= c; | |
620 } | |
621 } | |
622 } | |
623 else | |
624 { | |
625 if (numtaps % 2) | |
626 { | |
627 for (i=0; i<gridsize; i++) | |
628 { | |
629 c = sin(Pi2 * Grid[i]); | |
630 D[i] /= c; | |
631 W[i] *= c; | |
632 } | |
633 } | |
634 else | |
635 { | |
636 for (i=0; i<gridsize; i++) | |
637 { | |
638 c = sin(Pi * Grid[i]); | |
639 D[i] /= c; | |
640 W[i] *= c; | |
641 } | |
642 } | |
643 } | |
644 | |
645 /* | |
646 * Perform the Remez Exchange algorithm | |
647 */ | |
648 for (iter=0; iter<MAXITERATIONS; iter++) | |
649 { | |
650 CalcParms(r, Ext, Grid, D, W, ad, x, y); | |
651 CalcError(r, ad, x, y, gridsize, Grid, D, W, E); | |
652 Search(r, Ext, gridsize, E); | |
653 if (isDone(r, Ext, E)) | |
654 break; | |
655 } | |
656 if (iter == MAXITERATIONS) | |
657 { | |
658 printf("Reached maximum iteration count.\nResults may be bad.\n"); | |
659 } | |
660 | |
661 CalcParms(r, Ext, Grid, D, W, ad, x, y); | |
662 | |
663 /* | |
664 * Find the 'taps' of the filter for use with Frequency | |
665 * Sampling. If odd or Negative symmetry, fix the taps | |
666 * according to Parks McClellan | |
667 */ | |
668 for (i=0; i<=numtaps/2; i++) | |
669 { | |
670 if (symmetry == POSITIVE) | |
671 { | |
672 if (numtaps%2) | |
673 c = 1; | |
674 else | |
675 c = cos(Pi * (double)i/numtaps); | |
676 } | |
677 else | |
678 { | |
679 if (numtaps%2) | |
680 c = sin(Pi2 * (double)i/numtaps); | |
681 else | |
682 c = sin(Pi * (double)i/numtaps); | |
683 } | |
684 taps[i] = ComputeA((double)i/numtaps, r, ad, x, y)*c; | |
685 } | |
686 | |
687 /* | |
688 * Frequency sampling design with calculated taps | |
689 */ | |
690 FreqSample(numtaps, taps, h, symmetry); | |
691 | |
692 /* | |
693 * Delete allocated memory | |
694 */ | |
695 free(Grid); | |
696 free(W); | |
697 free(D); | |
698 free(E); | |
699 free(Ext); | |
700 free(x); | |
701 free(y); | |
702 free(ad); | |
703 } | |
704 |