function [Bm,Am,FIR]=parfiltdesfr(W,H,p,NFIR,Wt);
% PARFILTDESFR - Direct design of second-order parallel filters for a given
% pole set in the frequency domain.
% [Bm,Am,FIRcoeff]=parfiltdesfr(W,H,P,NFIR,Wt); designs the second-order sections
% [Bm,Am] and the coefficients of the FIR part (FIRcoeff) from the
% transfer function H given at W angular frequencies (in radians from 0 to pi)
% for the given P. The number of taps in the parallel FIR filter is set
% by NFIR. The default is NFIR=1, in this case FIRcoeff is a simple gain.
% If Wt weights are given, it means minimizing Wt*|Hparfilt-H|^2.
%
% The Bm and Am matrices are containing the [b0 b1]' and [1 a0 a1]'
% coefficients for the different sections in their columns. For example,
% Bm(:,3) gives the [b0 b1]' parameters of the third second-order
% section. These can be used by the filter command separatelly (e.g., by
% y=filter(Bm(:,3),Am(:,3),x), or by the PARFILT command.
%
% Note that this function does not support pole multiplicity, so P should
% contain each pole only once.
%
% This function is based on the paper
%
% Balázs Bank, "Logarithmic Frequency Scale Parallel Filter Design with Complex
% and Magnitude-Only Specifications," IEEE Signal Processing Letters,
% Vol. 18, No. 2, pp. 138-141, Feb. 2011.
%
% http://www.mit.bme.hu/~bank/parfilt
%
% C. Balazs Bank, 2007-2011.
if nargin<3,
NFIR=1;
end;
Weights=0; % is there a weighting vector?
if nargin>4,
Weights=1;
end;
p=p(find(p)); %we don't want to have any poles in the origin - for that we have the parallel FIR part
for k=1:length(p), %making the filter stable by flipping the poles into the unit cirle
if abs(p(k))>1
p(k)=1/conj(p(k));
end;
end;
p=cplxpair(p); %order it to complex pole pairs + real ones afterwards
%in order to have second-order sections only (i.e., no first order)
pnum=length(p); %number of poles
ppnum=2*floor(pnum/2); %the even part of pnum
ODD=0;
if pnum>ppnum, %if pnum is odd
ODD=1;
end;
H=H(:); %making them column vectors
W=W(:);
L=length(H);
Z=exp(-j*W); %creating z^-1
Z2=Z.^2; %creating z^-2
%constructing the modeling signal matrix
clear M;
for k=1:2:ppnum-1, %second-order sections
A=poly(p(k:k+1)).'; % constructing the denominator from the poles
resp=1./(A(1)+A(2)*Z+A(3)*Z2);
M(:,k)=resp;
M(:,k+1)=Z.*resp; %the response delayed by one sample
end;
if ODD, %if the number of poles is odd, we have a first-order section
A=poly(p(pnum)).'; % constructing the denominator
resp=1./(A(1)+A(2)*Z);
M(:,pnum)=resp;
end;
for k=1:NFIR, %parallel FIR part
M(:,pnum+k)=Z.^(k-1);
end;
% weighting
% in theory, we should construct a large diagonal matrix and multiply
% WtM=diag(sqrt(Wt));
% M=WtM*M;
% H=WtM*H;
if Weights, % actually, we should just scale the rows of M and H - faster and eats less memory
Wt=Wt(:); % making column vector
siz=size(M);
for k=1:siz(2),
M(:,k)=M(:,k).*sqrt(Wt);
end;
H=H.*sqrt(Wt);
end;
%Looking for min(||H-M*par||) as a function of par:
%least squares solution by equation solving
% MR=[real(M); imag(M)]; % constructing the new real-valued modeling matrix
% HR=[real(H); imag(H)]; % constructing the real-valued output
% A=MR'*MR;
% b=MR'*HR;
% par=A\b;
A=real(M'*M); % instead, we may do this, because it results in the same results
b=real(M'*H);
par=A\b;
%constructing the Bm and Am matrices
for k=1:ppnum/2,
Am(:,k)=poly(p(2*k-1:2*k)).';
Bm(:,k)=par(2*k-1:2*k);
end;
if ODD, %we extend the first-order section to a second-order one by adding zero coefficients
Am(:,k+1)=[poly(p(pnum)).'; 0];
Bm(:,k+1)=[par(pnum); 0];
end;
FIR=0;
%constructing the FIR part
if NFIR>0,
FIR=par(pnum+1:pnum+NFIR);
end;