Statistics
| Branch: | Tag: | Revision:

root / gnuradio-core / src / python / gnuradio / optfir.py @ 6d0c3329

History | View | Annotate | Download (7.6 kB)

1 5d69a524 jcorgan
#
2 5d69a524 jcorgan
# Copyright 2004,2005 Free Software Foundation, Inc.
3 5d69a524 jcorgan
# 
4 5d69a524 jcorgan
# This file is part of GNU Radio
5 5d69a524 jcorgan
# 
6 5d69a524 jcorgan
# GNU Radio is free software; you can redistribute it and/or modify
7 5d69a524 jcorgan
# it under the terms of the GNU General Public License as published by
8 937b719d eb
# the Free Software Foundation; either version 3, or (at your option)
9 5d69a524 jcorgan
# any later version.
10 5d69a524 jcorgan
# 
11 5d69a524 jcorgan
# GNU Radio is distributed in the hope that it will be useful,
12 5d69a524 jcorgan
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13 5d69a524 jcorgan
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 5d69a524 jcorgan
# GNU General Public License for more details.
15 5d69a524 jcorgan
# 
16 5d69a524 jcorgan
# You should have received a copy of the GNU General Public License
17 5d69a524 jcorgan
# along with GNU Radio; see the file COPYING.  If not, write to
18 86f5c924 eb
# the Free Software Foundation, Inc., 51 Franklin Street,
19 86f5c924 eb
# Boston, MA 02110-1301, USA.
20 5d69a524 jcorgan
# 
21 5d69a524 jcorgan
22 5d69a524 jcorgan
'''
23 5d69a524 jcorgan
Routines for designing optimal FIR filters.
24 5d69a524 jcorgan
25 5d69a524 jcorgan
For a great intro to how all this stuff works, see section 6.6 of
26 5d69a524 jcorgan
"Digital Signal Processing: A Practical Approach", Emmanuael C. Ifeachor
27 5d69a524 jcorgan
and Barrie W. Jervis, Adison-Wesley, 1993.  ISBN 0-201-54413-X.
28 5d69a524 jcorgan
'''
29 5d69a524 jcorgan
30 5d69a524 jcorgan
import math
31 5d69a524 jcorgan
from gnuradio import gr
32 5d69a524 jcorgan
33 5d69a524 jcorgan
remez = gr.remez
34 5d69a524 jcorgan
35 5d69a524 jcorgan
# ----------------------------------------------------------------
36 5d69a524 jcorgan
37 5d69a524 jcorgan
def low_pass (gain, Fs, freq1, freq2, passband_ripple_db, stopband_atten_db,
38 5d69a524 jcorgan
              nextra_taps=0):
39 5d69a524 jcorgan
    passband_dev = passband_ripple_to_dev (passband_ripple_db)
40 5d69a524 jcorgan
    stopband_dev = stopband_atten_to_dev (stopband_atten_db)
41 5d69a524 jcorgan
    desired_ampls = (gain, 0)
42 5d69a524 jcorgan
    (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls,
43 5d69a524 jcorgan
                               [passband_dev, stopband_dev], Fs)
44 5d69a524 jcorgan
    taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass")
45 5d69a524 jcorgan
    return taps
46 5d69a524 jcorgan
47 5d69a524 jcorgan
# FIXME high_passs is broken...
48 5d69a524 jcorgan
def high_pass (Fs, freq1, freq2, stopband_atten_db, passband_ripple_db, 
49 5d69a524 jcorgan
               nextra_taps=0):
50 5d69a524 jcorgan
    """FIXME: broken"""
51 5d69a524 jcorgan
    passband_dev = passband_ripple_to_dev (passband_ripple_db)
52 5d69a524 jcorgan
    stopband_dev = stopband_atten_to_dev (stopband_atten_db)
53 5d69a524 jcorgan
    desired_ampls = (0, 1)
54 5d69a524 jcorgan
    (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls,
55 5d69a524 jcorgan
                               [stopband_dev, passband_dev], Fs)
56 5d69a524 jcorgan
    taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass")
57 5d69a524 jcorgan
    return taps
58 5d69a524 jcorgan
59 5d69a524 jcorgan
# ----------------------------------------------------------------
60 5d69a524 jcorgan
61 5d69a524 jcorgan
def stopband_atten_to_dev (atten_db):
62 5d69a524 jcorgan
    """Convert a stopband attenuation in dB to an absolute value"""
63 5d69a524 jcorgan
    return 10**(-atten_db/20)
64 5d69a524 jcorgan
65 5d69a524 jcorgan
def passband_ripple_to_dev (ripple_db):
66 5d69a524 jcorgan
    """Convert passband ripple spec expressed in dB to an absolute value"""
67 5d69a524 jcorgan
    return (10**(ripple_db/20)-1)/(10**(ripple_db/20)+1)
68 5d69a524 jcorgan
69 5d69a524 jcorgan
# ----------------------------------------------------------------
70 5d69a524 jcorgan
71 5d69a524 jcorgan
def remezord (fcuts, mags, devs, fsamp = 2):
72 5d69a524 jcorgan
    '''
73 5d69a524 jcorgan
    FIR order estimator (lowpass, highpass, bandpass, mulitiband).
74 5d69a524 jcorgan
75 5d69a524 jcorgan
    (n, fo, ao, w) = remezord (f, a, dev)
76 5d69a524 jcorgan
    (n, fo, ao, w) = remezord (f, a, dev, fs)
77 5d69a524 jcorgan
78 5d69a524 jcorgan
    (n, fo, ao, w) = remezord (f, a, dev) finds the approximate order,
79 5d69a524 jcorgan
    normalized frequency band edges, frequency band amplitudes, and
80 5d69a524 jcorgan
    weights that meet input specifications f, a, and dev, to use with
81 5d69a524 jcorgan
    the remez command.
82 5d69a524 jcorgan
83 5d69a524 jcorgan
    * f is a sequence of frequency band edges (between 0 and Fs/2, where
84 5d69a524 jcorgan
      Fs is the sampling frequency), and a is a sequence specifying the
85 5d69a524 jcorgan
      desired amplitude on the bands defined by f. The length of f is
86 5d69a524 jcorgan
      twice the length of a, minus 2. The desired function is
87 5d69a524 jcorgan
      piecewise constant.
88 5d69a524 jcorgan
89 5d69a524 jcorgan
    * dev is a sequence the same size as a that specifies the maximum
90 5d69a524 jcorgan
      allowable deviation or ripples between the frequency response
91 5d69a524 jcorgan
      and the desired amplitude of the output filter, for each band.
92 5d69a524 jcorgan
93 5d69a524 jcorgan
    Use remez with the resulting order n, frequency sequence fo,
94 5d69a524 jcorgan
    amplitude response sequence ao, and weights w to design the filter b
95 5d69a524 jcorgan
    which approximately meets the specifications given by remezord
96 5d69a524 jcorgan
    input parameters f, a, and dev:
97 5d69a524 jcorgan
98 5d69a524 jcorgan
    b = remez (n, fo, ao, w)
99 5d69a524 jcorgan
100 5d69a524 jcorgan
    (n, fo, ao, w) = remezord (f, a, dev, Fs) specifies a sampling frequency Fs.
101 5d69a524 jcorgan
102 5d69a524 jcorgan
    Fs defaults to 2 Hz, implying a Nyquist frequency of 1 Hz. You can
103 5d69a524 jcorgan
    therefore specify band edges scaled to a particular applications
104 5d69a524 jcorgan
    sampling frequency.
105 5d69a524 jcorgan
106 5d69a524 jcorgan
    In some cases remezord underestimates the order n. If the filter
107 5d69a524 jcorgan
    does not meet the specifications, try a higher order such as n+1
108 5d69a524 jcorgan
    or n+2.
109 5d69a524 jcorgan
    '''
110 5d69a524 jcorgan
    # get local copies
111 5d69a524 jcorgan
    fcuts = fcuts[:]
112 5d69a524 jcorgan
    mags = mags[:]
113 5d69a524 jcorgan
    devs = devs[:]
114 5d69a524 jcorgan
    
115 5d69a524 jcorgan
    for i in range (len (fcuts)):
116 5d69a524 jcorgan
        fcuts[i] = float (fcuts[i]) / fsamp
117 5d69a524 jcorgan
118 5d69a524 jcorgan
    nf = len (fcuts)
119 5d69a524 jcorgan
    nm = len (mags)
120 5d69a524 jcorgan
    nd = len (devs)
121 5d69a524 jcorgan
    nbands = nm
122 5d69a524 jcorgan
123 5d69a524 jcorgan
    if nm != nd:
124 5d69a524 jcorgan
        raise ValueError, "Length of mags and devs must be equal"
125 5d69a524 jcorgan
126 5d69a524 jcorgan
    if nf != 2 * (nbands - 1):
127 5d69a524 jcorgan
        raise ValueError, "Length of f must be 2 * len (mags) - 2"
128 5d69a524 jcorgan
    
129 5d69a524 jcorgan
    for i in range (len (mags)):
130 5d69a524 jcorgan
        if mags[i] != 0:                        # if not stopband, get relative deviation
131 5d69a524 jcorgan
            devs[i] = devs[i] / mags[i]
132 5d69a524 jcorgan
        
133 5d69a524 jcorgan
    # separate the passband and stopband edges
134 5d69a524 jcorgan
    f1 = fcuts[0::2]
135 5d69a524 jcorgan
    f2 = fcuts[1::2]
136 5d69a524 jcorgan
137 5d69a524 jcorgan
    n = 0
138 5d69a524 jcorgan
    min_delta = 2
139 5d69a524 jcorgan
    for i in range (len (f1)):
140 5d69a524 jcorgan
        if f2[i] - f1[i] < min_delta:
141 5d69a524 jcorgan
            n = i
142 5d69a524 jcorgan
            min_delta = f2[i] - f1[i]
143 5d69a524 jcorgan
144 5d69a524 jcorgan
    if nbands == 2:
145 5d69a524 jcorgan
        # lowpass or highpass case (use formula)
146 5d69a524 jcorgan
        l = lporder (f1[n], f2[n], devs[0], devs[1])
147 5d69a524 jcorgan
    else:
148 5d69a524 jcorgan
        # bandpass or multipass case
149 5d69a524 jcorgan
        # try different lowpasses and take the worst one that
150 5d69a524 jcorgan
        #  goes through the BP specs
151 5d69a524 jcorgan
        l = 0
152 5d69a524 jcorgan
        for i in range (1, nbands-1):
153 5d69a524 jcorgan
            l1 = lporder (f1[i-1], f2[i-1], devs[i], devs[i-1])
154 5d69a524 jcorgan
            l2 = lporder (f1[i], f2[i], devs[i], devs[i+1])
155 5d69a524 jcorgan
            l = max (l, l1, l2)
156 5d69a524 jcorgan
157 5d69a524 jcorgan
    n = int (math.ceil (l)) - 1               # need order, not length for remez
158 5d69a524 jcorgan
    
159 5d69a524 jcorgan
    # cook up remez compatible result
160 5d69a524 jcorgan
    ff = [0] + fcuts + [1]
161 5d69a524 jcorgan
    for i in range (1, len (ff) - 1):
162 5d69a524 jcorgan
        ff[i] *= 2
163 5d69a524 jcorgan
164 5d69a524 jcorgan
    aa = []
165 5d69a524 jcorgan
    for a in mags:
166 5d69a524 jcorgan
        aa = aa + [a, a]
167 5d69a524 jcorgan
168 5d69a524 jcorgan
    max_dev = max (devs)
169 5d69a524 jcorgan
    wts = [1] * len(devs)
170 5d69a524 jcorgan
    for i in range (len (wts)):
171 5d69a524 jcorgan
        wts[i] = max_dev / devs[i]
172 5d69a524 jcorgan
173 5d69a524 jcorgan
    return (n, ff, aa, wts)
174 5d69a524 jcorgan
175 5d69a524 jcorgan
# ----------------------------------------------------------------
176 5d69a524 jcorgan
177 5d69a524 jcorgan
def lporder (freq1, freq2, delta_p, delta_s):
178 5d69a524 jcorgan
    '''
179 5d69a524 jcorgan
    FIR lowpass filter length estimator.  freq1 and freq2 are
180 5d69a524 jcorgan
    normalized to the sampling frequency.  delta_p is the passband
181 5d69a524 jcorgan
    deviation (ripple), delta_s is the stopband deviation (ripple).
182 5d69a524 jcorgan
183 5d69a524 jcorgan
    Note, this works for high pass filters too (freq1 > freq2), but
184 5d69a524 jcorgan
    doesnt work well if the transition is near f == 0 or f == fs/2
185 5d69a524 jcorgan
186 5d69a524 jcorgan
    From Herrmann et al (1973), Practical design rules for optimum
187 5d69a524 jcorgan
    finite impulse response filters.  Bell System Technical J., 52, 769-99
188 5d69a524 jcorgan
    '''
189 5d69a524 jcorgan
    df = abs (freq2 - freq1)
190 5d69a524 jcorgan
    ddp = math.log10 (delta_p)
191 5d69a524 jcorgan
    dds = math.log10 (delta_s)
192 5d69a524 jcorgan
193 5d69a524 jcorgan
    a1 = 5.309e-3
194 5d69a524 jcorgan
    a2 = 7.114e-2
195 5d69a524 jcorgan
    a3 = -4.761e-1
196 5d69a524 jcorgan
    a4 = -2.66e-3
197 5d69a524 jcorgan
    a5 = -5.941e-1
198 5d69a524 jcorgan
    a6 = -4.278e-1
199 5d69a524 jcorgan
200 5d69a524 jcorgan
    b1 = 11.01217
201 5d69a524 jcorgan
    b2 = 0.5124401
202 5d69a524 jcorgan
203 5d69a524 jcorgan
    t1 = a1 * ddp * ddp
204 5d69a524 jcorgan
    t2 = a2 * ddp
205 5d69a524 jcorgan
    t3 = a4 * ddp * ddp
206 5d69a524 jcorgan
    t4 = a5 * ddp
207 5d69a524 jcorgan
208 5d69a524 jcorgan
    dinf=((t1 + t2 + a3) * dds) + (t3 + t4 + a6)
209 5d69a524 jcorgan
    ff = b1 + b2 * (ddp - dds)
210 5d69a524 jcorgan
    n = dinf / df - ff * df + 1
211 5d69a524 jcorgan
    return n
212 5d69a524 jcorgan
213 5d69a524 jcorgan
214 5d69a524 jcorgan
def bporder (freq1, freq2, delta_p, delta_s):
215 5d69a524 jcorgan
    '''
216 5d69a524 jcorgan
    FIR bandpass filter length estimator.  freq1 and freq2 are
217 5d69a524 jcorgan
    normalized to the sampling frequency.  delta_p is the passband
218 5d69a524 jcorgan
    deviation (ripple), delta_s is the stopband deviation (ripple).
219 5d69a524 jcorgan
220 5d69a524 jcorgan
    From Mintzer and Liu (1979)
221 5d69a524 jcorgan
    '''
222 5d69a524 jcorgan
    df = abs (freq2 - freq1)
223 5d69a524 jcorgan
    ddp = math.log10 (delta_p)
224 5d69a524 jcorgan
    dds = math.log10 (delta_s)
225 5d69a524 jcorgan
226 5d69a524 jcorgan
    a1 = 0.01201
227 5d69a524 jcorgan
    a2 = 0.09664
228 5d69a524 jcorgan
    a3 = -0.51325
229 5d69a524 jcorgan
    a4 = 0.00203
230 5d69a524 jcorgan
    a5 = -0.57054
231 5d69a524 jcorgan
    a6 = -0.44314
232 5d69a524 jcorgan
233 5d69a524 jcorgan
    t1 = a1 * ddp * ddp
234 5d69a524 jcorgan
    t2 = a2 * ddp
235 5d69a524 jcorgan
    t3 = a4 * ddp * ddp
236 5d69a524 jcorgan
    t4 = a5 * ddp
237 5d69a524 jcorgan
238 5d69a524 jcorgan
    cinf = dds * (t1 + t2 + a3) + t3 + t4 + a6
239 5d69a524 jcorgan
    ginf = -14.6 * math.log10 (delta_p / delta_s) - 16.9
240 5d69a524 jcorgan
    n = cinf / df + ginf * df + 1
241 5d69a524 jcorgan
    return n