1 ## Copyright (C) 2011 Alan J. Greenberger <alanjg@ptd.net>
3 ## This program is free software; you can redistribute it and/or modify it under
4 ## the terms of the GNU General Public License as published by the Free Software
5 ## Foundation; either version 3 of the License, or (at your option) any later
8 ## This program is distributed in the hope that it will be useful, but WITHOUT
9 ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 ## You should have received a copy of the GNU General Public License along with
14 ## this program; if not, see <http://www.gnu.org/licenses/>.
16 ## IIR Low Pass Filter to Multiband Filter Transformation
18 ## [Num,Den,AllpassNum,AllpassDen] = iirlp2mb(B,A,Wo,Wt)
19 ## [Num,Den,AllpassNum,AllpassDen] = iirlp2mb(B,A,Wo,Wt,Pass)
21 ## Num,Den: numerator,denominator of the transformed filter
22 ## AllpassNum,AllpassDen: numerator,denominator of allpass transform,
23 ## B,A: numerator,denominator of prototype low pass filter
24 ## Wo: normalized_angular_frequency/pi to be transformed
25 ## Wt: [phi=normalized_angular_frequencies]/pi target vector
26 ## Pass: This parameter may have values 'pass' or 'stop'. If
27 ## not given, it defaults to the value of 'pass'.
29 ## With normalized ang. freq. targets 0 < phi(1) < ... < phi(n) < pi radians
31 ## for Pass == 'pass', the target multiband magnitude will be:
32 ## -------- ---------- -----------...
34 ## 0 phi(1) phi(2) phi(3) phi(4) phi(5) (phi(6)) pi
36 ## for Pass == 'stop', the target multiband magnitude will be:
37 ## ------- --------- ----------...
39 ## 0 phi(1) phi(2) phi(3) phi(4) (phi(5)) pi
42 ## [B, A] = butter(6, 0.5);
43 ## [Num, Den] = iirlp2mb(B, A, 0.5, [.2 .4 .6 .8]);
45 function [Num,Den,AllpassNum,AllpassDen] = iirlp2mb(varargin)
47 "%s: Usage: [Num,Den,AllpassNum,AllpassDen]=iirlp2mb(B,A,Wo,Wt[,Pass])\n"
49 B = varargin{1}; # numerator polynomial of prototype low pass filter
50 A = varargin{2}; # denominator polynomial of prototype low pass filter
51 Wo = varargin{3}; # (normalized angular frequency)/pi to be transformed
52 Wt = varargin{4}; # vector of (norm. angular frequency)/pi transform targets
53 # [phi(1) phi(2) ... ]/pi
54 if(nargin < 4 || nargin > 5)
65 error("Pass must be 'pass' or 'stop'\n%s",usage)
68 pass_stop = -1; # Pass == 'pass' is the default
71 error("Wo is %f <= 0\n%s",Wo,usage);
74 error("Wo is %f >= 1\n%s",Wo,usage);
77 for i = 1 : length(Wt)
79 error("Wt(%d) is %f <= 0\n%s",i,Wt(i),usage);
82 error("Wt(%d) is %f >= 1\n%s",i,Wt(i),usage);
85 error("Wt(%d) = %f, not monotonically increasing\n%s",i,Wt(i),usage);
92 # Inputs B,A specify the low pass IIR prototype filter G(z) = ---- .
94 # This module transforms G(z) into a multiband filter using the iterative
96 # [FFM] G. Feyh, J. Franchitti, and C. Mullis, "All-Pass Filter
97 # Interpolation and Frequency Transformation Problem", Proceedings 20th
98 # Asilomar Conference on Signals, Systems and Computers, Nov. 1986, pp.
100 # [FFM] moves the prototype filter position at normalized angular frequency
101 # .5*pi to the places specified in the Wt vector times pi. In this module,
102 # a generalization allows the position to be moved on the prototype filter
103 # to be specified as Wo*pi instead of being fixed at .5*pi. This is
104 # implemented using two successive allpass transformations.
106 # In the first stage, find allpass J(z) = ---- such that
109 # J(e ) = e (low pass to low pass transformation)
112 # In the second stage, find allpass H(z) = ---- such that
114 # jWt(k)*pi -j(2k - 1)*.5*pi
115 # H(e ) = e (low pass to multiband transformation)
118 # The variable PP used here corresponds to P in [FFM].
119 # len = length(P(z)) == length(PP(z)), the number of polynomial coefficients
122 # P(z) = SUM P(i)z ; PP(z) = SUM PP(i)z ; PP(i) == P(len + 1 - i)
123 # i=1 i=1 (allpass condition)
124 # Note: (len - 1) == n in [FFM] eq. 3
126 # The first stage computes the denominator of an allpass for translating
127 # from a prototype with position .5 to one with a position of Wo. It has the
135 # From the low pass to low pass tranformation in Table 7.1 p. 529 of A.
136 # Oppenheim and R. Schafer, Discrete-Time Signal Processing 3rd edition,
137 # Prentice Hall 2010, one can see that the denominator of an allpass for
138 # going in the opposite direction can be obtained by a sign reversal of the
139 # second coefficient, K(2), of the vector K (the index 2 not to be confused
140 # with a value of z, which is implicit).
142 # The first stage allpass denominator computation
145 # The second stage allpass computation
146 phi = pi * Wt; # vector of normalized angular frequencies between 0 and pi
147 P = apd(phi); # calculate denominator of allpass for this target vector
148 PP = revco(P); # numerator of allpass has reversed coefficients of P
150 # The total allpass filter from the two consecutive stages can be written as
158 AllpassDen = P - (K(2) * PP);
159 AllpassDen /= AllpassDen(1); # normalize
160 AllpassNum = pass_stop * revco(AllpassDen);
161 [Num,Den] = transform(B,A,AllpassNum,AllpassDen,pass_stop);
164 function [Num,Den] = transform(B,A,PP,P,pass_stop)
165 # Given G(Z) = B(Z)/A(Z) and allpass H(z) = PP(z)/P(z), compute G(H(z))
166 # For Pass = 'pass', transformed filter is:
168 # B1 + B2(PP/P) + B3(PP/P)^ + ... + Bnb(PP/P)^
169 # -------------------------------------------------
171 # A1 + A2(PP/P) + A3(PP/P)^ + ... + Ana(PP/P)^
172 # For Pass = 'stop', use powers of (-PP/P)
174 na = length(A); # the number of coefficients in A
175 nb = length(B); # the number of coefficients in B
176 # common low pass iir filters have na == nb but in general might not
177 n = max(na,nb); # the greater of the number of coefficients
179 # Multiply top and bottom by P^ yields:
181 # n-1 n-2 2 n-3 nb-1 n-nb
182 # B1(P^ ) + B2(PP)(P^ ) + B3(PP^ )(P^ ) + ... + Bnb(PP^ )(P^ )
183 # ---------------------------------------------------------------------
184 # n-1 n-2 2 n-3 na-1 n-na
185 # A1(P^ ) + A2(PP)(P^ ) + A3(PP^ )(P^ ) + ... + Ana(PP^ )(P^ )
187 # Compute and store powers of P as a matrix of coefficients because we will
188 # need to use them in descending power order
189 global Ppower; # to hold coefficients of powers of P, access inside ppower()
191 powcols = np + (np-1)*(n-2); # number of coefficients in P^(n-1)
192 # initialize to "Not Available" with n-1 rows for powers 1 to (n-1) and
193 # the number of columns needed to hold coefficients for P^(n-1)
194 Ppower = NA(n-1,powcols);
195 Ptemp = P; # start with P to the 1st power
196 for i = 1 : n-1 # i is the power
197 for j = 1 : length(Ptemp) # j is the coefficient index for this power
198 Ppower(i,j) = Ptemp(j);
200 Ptemp = conv(Ptemp,P); # increase power of P by one
203 # Compute numerator and denominator of transformed filter
208 # Regenerate P^ (p_pownmi)
212 p_pownmi = ppower(n-i,powcols);
215 # Regenerate PP^ (pp_powim1)
219 pp_powim1 = revco(ppower(i-1,powcols));
222 Bterm = (pass_stop^(i-1))*B(i)*conv(pp_powim1,p_pownmi);
223 Num = polysum(Num,Bterm);
226 Aterm = (pass_stop^(i-1))*A(i)*conv(pp_powim1,p_pownmi);
227 Den = polysum(Den,Aterm);
230 # Scale both numerator and denominator to have Den(1) = 1
232 for i = 1 : length(Den)
233 Den(i) = Den(i) / temp;
235 for i = 1 : length(Num)
236 Num(i) = Num(i) / temp;
240 function P = apd(phi) # all pass denominator
241 # Given phi, a vector of normalized angular frequency transformation targets,
242 # return P, the denominator of an allpass H(z)
243 lenphi = length(phi);
244 Pkm1 = 1; # P0 initial condition from [FFM] eq. 22
246 P = pk(Pkm1, k, phi(k)); # iterate
251 function Pk = pk(Pkm1, k, phik) # kth iteration of P(z)
252 # Given Pkminus1, k, and phi(k) in radians , return Pk
254 # From [FFM] eq. 19 : k
255 # Pk = (z+1 )sin(phi(k)/2)Pkm1 - (-1) (z-1 )cos(phi(k)/2)PPkm1
258 # = z((1+z )sin(phi(k)/2)Pkm1 - (-1) (1-z )cos(phi(k)/2)PPkm1)
259 # PPk can also have z factored out. In H=PP/P, z in PPk will cancel z in Pk,
260 # so just leave out. Use
262 # PK = (1+z )sin(phi(k)/2)Pkm1 - (-1) (1-z )cos(phi(k)/2)PPkm1
264 # = sin(phi(k)/2)Pkm1 - (-1) cos(phi(k)/2)PPkm1
267 # + z sin(phi(k)/2)Pkm1 + (-1) z cos(phi(k)/2)PPkm1
268 Pk = zeros(1,k+1); # there are k+1 coefficients in Pk
272 Pk(i) += sin_k * Pkm1(i) - ((-1)^k * cos_k * Pkm1(k+1-i));
275 # Multiplication by z just shifts by one coefficient
276 Pk(i+1) += sin_k * Pkm1(i) + ((-1)^k * cos_k * Pkm1(k+1-i));
278 # now normalize to Pk(1) = 1 (again will cancel with same factor in PPk)
285 function PP = revco(p) # reverse components of vector
288 PP(l + 1 - i) = p(i);
292 function p = ppower(i,powcols) # Regenerate ith power of P from stored PPower
299 if(isna(Ppower(i,j)))
302 p = horzcat(p, Ppower(i,j));
307 function poly = polysum(p1,p2) # add polynomials of possibly different length
312 p2 = horzcat(p2, zeros(1,n1-n2));
315 p1 = horzcat(p1, zeros(1,n2-n1));