www.gusucode.com > signal 工具箱matlab源码程序 > signal/zp2sos.m
function [sos,g] = zp2sos(varargin) %ZP2SOS Zero-pole-gain to second-order sections model conversion. % [SOS,G] = ZP2SOS(Z,P,K) finds a matrix SOS in second-order sections % form and a gain G which represent the same system H(z) as the one % with zeros in vector Z, poles in vector P and gain in scalar K. % The poles and zeros must be in complex conjugate pairs. % % SOS is an L by 6 matrix with the following structure: % SOS = [ b01 b11 b21 1 a11 a21 % b02 b12 b22 1 a12 a22 % ... % b0L b1L b2L 1 a1L a2L ] % % Each row of the SOS matrix describes a 2nd order transfer function: % b0k + b1k z^-1 + b2k z^-2 % Hk(z) = ---------------------------- % 1 + a1k z^-1 + a2k z^-2 % where k is the row index. % % G is a scalar which accounts for the overall gain of the system. If % G is not specified, the gain is embedded in the first section. % The second order structure thus describes the system H(z) as: % H(z) = G*H1(z)*H2(z)*...*HL(z) % % NOTE: Embedding the gain in the first section when scaling a % direct-form II structure is not recommended and may result in erratic % scaling. To avoid embedding the gain, use zp2sos with two outputs. % % ZP2SOS(Z,P,K,DIR_FLAG) specifies the ordering of the 2nd order % sections. If DIR_FLAG is equal to 'UP', the first row will contain % the poles closest to the origin, and the last row will contain the % poles closest to the unit circle. If DIR_FLAG is equal to 'DOWN', the % sections are ordered in the opposite direction. The zeros are always % paired with the poles closest to them. DIR_FLAG defaults to 'UP'. % % ZP2SOS(Z,P,K,DIR_FLAG,SCALE) specifies the desired scaling of the gain % and the numerator coefficients of all 2nd order sections. SCALE can be % either 'NONE', Inf or 2 which correspond to no scaling, infinity % norm scaling and 2-norm scaling respectively. SCALE defaults to 'NONE'. % The filter must be stable in order to scale in the 2-norm or inf-norm sense. % Using infinity-norm scaling in conjunction with 'UP' ordering will % minimize the probability of overflow in the realization. On the other % hand, using 2-norm scaling in conjunction with 'DOWN' ordering will % minimize the peak roundoff noise. % % ZP2SOS(Z,P,K,DIR_FLAG,SCALE,KRZFLAG) specifies whether or not to keep % real zeros that are the negative of each other together rather than % ordering according to their proximity to poles. If KRZFLAG is true, % this is done and the result is a numerator with a middle coefficient % equal to zero. The default is false. % % NOTE: Infinity-norm and 2-norm scaling are appropriate only for direct % form II structures. % % % Example: % % Find a second-order section form of a Butterworth lowpass filter. % % [z,p,k] = butter(5,0.2); % 5th order Butterworth filter (Wn =0.2) % sos = zp2sos(z,p,k) % Obtain second-order sections form % fvtool(sos) % Visualize the filter % % See also TF2SOS, SOS2ZP, SOS2TF, SOS2SS, SS2SOS, CPLXPAIR. % NOTE: restricted to real coefficient systems (poles and zeros % must be in conjugate pairs) % References: % [1] L. B. Jackson, DIGITAL FILTERS AND SIGNAL PROCESSING, 3rd Ed. % Kluwer Academic Publishers, 1996, Chapter 11. % [2] S.K. Mitra, DIGITAL SIGNAL PROCESSING. A Computer Based Approach. % McGraw-Hill, 1998, Chapter 9. % [3] P.P. Vaidyanathan. ROBUST DIGITAL FILTER STRUCTURES. Ch 7 in % HANDBOOK FOR DIGITAL SIGNAL PROCESSING. S.K. Mitra and J.F. % Kaiser Eds. Wiley-Interscience, N.Y. % Copyright 1988-2013 The MathWorks, Inc. narginchk(2,6) [z,p,k,direction_flag,scale,krzflag] = parseinput(varargin{:}); % Order the poles and zeros in complex conj. pairs and return the number of % poles and zeros [z,p,lz,lp,L] = orderzp(z,p); % Break up conjugate pairs and real poles and zeros [z_conj,z_real,p_conj,p_real] = breakconjrealzp(z,lz,p,lp); % Order poles according to proximity to unit circle [new_p,p_conj,p_real] = orderp(p_conj,p_real); % Order zeros according to proximity to pole pairs new_z = orderz(z_conj,p_conj,z_real,p_real,krzflag); % Form SOS matrix sos = formsos(lz,lp,new_z,new_p,L); % Change direction if requested if strcmp(direction_flag,'down'), sos = flipud(sos); end % At this point no scaling has been performed % The leading coefficients of both num and den are one. % Perform appropriate scaling [sos,k] = scaling(sos,k,L,scale); % Embed the gain if only one output argument was specified if nargout == 1, sos(1,1:3) = k*sos(1,1:3); else g = k; end %------------------------------------------------------------------------------------ function [z,p,k,direction_flag,scale,krzflag] = parseinput(varargin) z = varargin{1}; p = varargin{2}; if all(size(z) > 1) || all(size(p) > 1) error(message('signal:zp2sos:InvalidDimiensions')) end % Make sure z,p inputs are converted to columns z = z(:); p = p(:); % Setup default values k = 1; direction_flag = 'up'; scale = 'none'; krzflag = false; % Replace with given values if nargin > 2, if ~isempty(varargin{3}), k = varargin{3}; end if nargin > 3, if ~isempty(varargin{4}), direction_flag = varargin{4}; end if nargin > 4, if ~isempty(varargin{5}), scale = varargin{5}; end if nargin > 5, if ~isempty(varargin{6}), krzflag = varargin{6}; end end end end end % Input check diropts = {'up','down'}; indx1 = find(strncmpi(direction_flag, diropts, length(direction_flag))); if isempty(indx1), error(message('signal:zp2sos:DirFlag', 'DIR_FLAG', 'UP', 'DOWN')); end direction_flag = diropts{indx1}; % Scale must be one of 'none', 2 or inf. For backwards compatibility we allow 'two' % and 'inf' as well. if ischar(scale), scaleopts = {'none','inf','two'}; indx2 = find(strncmpi(scale, scaleopts, length(scale))); if isempty(indx2), error(message('signal:zp2sos:BadScale', 'SCALE', 'NONE', 'inf', 2)); end scale = scaleopts{indx2}; if strcmpi(scale,'two'), scale = 2; end if strcmpi(scale,'inf'), scale = inf; end else if ~(isinf(scale) || scale == 2), error(message('signal:zp2sos:BadScale', 'SCALE', 'NONE', 'inf', 2)); end end if ~islogical(krzflag), error(message('signal:zp2sos:BadLogical', 'KRZFLAG')); end %------------------------------------------------------------------------------------ function [z,p,lz,lp,L] = orderzp(z,p) % Order the poles and zeros in complex conj. pairs and return the number of poles and % zeros % Order the poles and zeros in complex conj. pairs z = cplxpair(z); p = cplxpair(p); % Get the number of poles and zeros lz = length(z); lp = length(p); L = ceil(lp/2); if lz > lp, error(message('signal:zp2sos:TooManyZeros')); end %------------------------------------------------------------------------------------ function [z_conj,z_real,p_conj,p_real] = breakconjrealzp(z,lz,p,lp) % break up conjugate pairs and real poles and zeros ind = find(abs(imag(p))>0); p_conj = p(ind); % the poles that have conjugate pairs ind_complement = 1:lp; if ~isempty(ind) ind_complement(ind) = []; end p_real = p(ind_complement); % the poles that are real % break up conjugate pairs and real zeros ind = find(abs(imag(z))>0); z_conj = z(ind); % the zeros that have conjugate pairs ind_complement = 1:lz; if ~isempty(ind) ind_complement(ind) = []; end z_real = z(ind_complement); % the zeros that are real %------------------------------------------------------------------------------------ function [new_p,p_conj,p_real] = orderp(p_conj,p_real) % Order poles according to proximity to unit circle % order the conjugate pole pairs according to proximity to unit circle [temp,ind] = sort(abs(p_conj - exp(1i*angle(p_conj)))); %#ok p_conj = p_conj(ind); % order the real poles according to proximity to unit circle too [temp,ind] = sort(abs(p_real - sign(p_real))); %#ok p_real = p_real(ind); % Save the ordered poles new_p = [p_conj;p_real]; %------------------------------------------------------------------------------------ function new_z = orderz(z_conj,p_conj,z_real,p_real,krzflag) % Order zeros according to proximity to pole pairs % order the conjugate zero pairs according to proximity to pole pairs new_z = []; for i = 1:length(z_conj)/2, if ~isempty(p_conj), [temp,ind1] = min(abs(z_conj-p_conj(1))); %#ok new_z = [new_z; z_conj(ind1)]; %#ok<*AGROW> z_conj(ind1) = []; [temp,ind2] = min(abs(z_conj-p_conj(2))); %#ok new_z = [new_z; z_conj(ind2)]; z_conj(ind2) = []; p_conj([1 2]) = []; elseif ~isempty(p_real), [temp,ind] = min(abs(z_conj-p_real(1))); %#ok new_z = [new_z; z_conj(ind); z_conj(ind+1)]; z_conj([ind ind+1]) = []; p_real(1) = []; if ~isempty(p_real), p_real(1) = []; end else new_z = [new_z; z_conj]; break end end % If KRZFLAG is true, keep real zeros along with their negatives if present if krzflag && ~isempty(z_real) remaining_z = []; while ~isempty(z_real) realindx = find(z_real == -z_real(1)); if ~isempty(realindx) && z_real(1) ~= 0 new_z = [new_z; z_real(1); z_real(realindx(1))]; z_real(realindx(1)) = []; z_real(1) = []; else remaining_z = [remaining_z z_real(1)]; z_real(1) = []; end end else remaining_z = z_real; end % order remaining real zeros according to proximity to pole pairs too for i = 1:length(remaining_z), if ~isempty(p_conj), [temp,ind] = min(abs(remaining_z-p_conj(1))); %#ok new_z = [new_z; remaining_z(ind)]; remaining_z(ind) = []; p_conj(1) = []; elseif ~isempty(p_real), [temp,ind] = min(abs(remaining_z-p_real(1))); %#ok new_z = [new_z; remaining_z(ind)]; remaining_z(ind) = []; p_real(1) = []; else new_z = [new_z; remaining_z]; break end end %------------------------------------------------------------------------------------ function sos = formsos(lz,lp,new_z,new_p,L) % Form SOS matrix % Initialize SOS matrix sos = []; if lz == 0, if lp == 0, sos = [1 0 0 1 0 0]; elseif ~rem(lp,2), sos = sosfun(1,2*L-1,new_p,sos); %even number of poles else sos = sosfun(1,2*(L-1)-1,new_p,sos); %odd number of poles sos = last_pole([],new_p(end),sos);%handle the last pole separately end else if ~rem(lz,2), sos = sosfun2(1,lz-1,new_z,new_p,sos); %even number of zeros % Now continue for the excess poles if any if ~rem(lp,2), sos = sosfun(lz+1,lp,new_p,sos); %even number of poles else sos = sosfun(lz+1,lp-1,new_p,sos); %odd number of poles sos = last_pole([],new_p(end),sos);%handle the last pole separately end else sos = sosfun2(1,lz-1,new_z,new_p,sos); %odd number of zeros %handle the last zero separately if lz == lp, %if number of poles = number of zeros sos = last_pole(new_z(lz),new_p(lz),sos);%handle the last pole separately else % more poles than zeros [num,den] = zp2tf(new_z(lz),new_p(lz:lz+1),1); sos = [num den;sos]; % Now continue for the excess poles if any if ~rem(lp,2), sos = sosfun(lz+2,lp,new_p,sos); %even number of poles else sos = sosfun(lz+2,lp-1,new_p,sos); %odd number of poles sos = last_pole([],new_p(end),sos);%handle the last pole separately end end end end %------------------------------------------------------------------------------------ function sos = last_pole(z,p,sos) % Handle the last pole separately [num,den] = zp2tf(z,p,1); num = [num 0]; den = [den 0]; sos = [num den;sos]; %------------------------------------------------------------------------------------ function sos = sosfun(start,stop,p,sos) % This function was made to not repeat code in several places for m = start:2:stop, [num,den] = zp2tf([],p(m:m+1),1); sos = [num den;sos]; end %------------------------------------------------------------------------------------ function sos = sosfun2(start,stop,z,p,sos) % This function was made to not repeat code in several places for m = start:2:stop, [num,den] = zp2tf(z(m:m+1),p(m:m+1),1); sos = [num den;sos]; end %------------------------------------------------------------------------------------ function [sos,g] = scaling(sos,k,L,scale) % SCALING, scale the cascaded sos filters using the infinity norm % or the 2 norm as specified. if strcmpi(scale,'none'), g = k; return end % Find the L scaling factors s(m) and perform the scaling Fnum = 1; Fden = 1; den = sos(1,4:6); % Compute the first scaling constant separetely s(1) = filternorm(1,den,scale); % Now compute the other scaling constants for m = 2:L den = sos(m,4:6); Fnum = conv(Fnum,sos(m-1,1:3)); Fden = conv(Fden,sos(m-1,4:6)); Fden2 = conv(Fden,den); s(m) = filternorm(Fnum,Fden2,scale); % Now perform the scaling for the first L-1 sos sections sos(m-1,1:3) = s(m-1)./s(m).*sos(m-1,1:3); end % And now scale the last section sos(end,1:3) = k*s(end)*sos(end,1:3); g = 1/s(1); % [EOF]