gusucode.com > 信号处理工具箱 - signal源码程序 > signal\signal\signal\freqz.m
function [hh,ww] = freqz(varargin) %FREQZ Z-transform digital filter frequency response. % When N is an integer, [H,W] = FREQZ(B,A,N) returns the N-point frequency % vector W in radians and the N-point complex frequency response vector H % of the filter B/A: % jw -jw -jnbw % jw B(e) b(1) + b(2)e + .... + b(nb+1)e % H(e) = ---- = ---------------------------- % jw -jw -jnaw % A(e) a(1) + a(2)e + .... + a(na+1)e % given numerator and denominator coefficients in vectors B and A. The % frequency response is evaluated at N points equally spaced around the % upper half of the unit circle. If N isn't specified, it defaults to 512. % % [H,W] = FREQZ(B,A,N,'whole') uses N points around the whole unit circle. % % H = FREQZ(B,A,W) returns the frequency response at frequencies % designated in vector W, in [radians/sample] (normally between 0 and pi). % % [H,F] = FREQZ(B,A,N,Fs) and [H,F] = FREQZ(B,A,N,'whole',Fs) given a % sampling freq Fs in Hz return a frequency vector F in Hz. % % H = FREQZ(B,A,F,Fs) given sampling frequency Fs in Hz returns the % complex frequency response at the frequencies designated in vector F, % also in Hz. % % FREQZ(B,A,...) with no output arguments plots the magnitude and % unwrapped phase of B/A in the current figure window. % % See also FILTER, FFT, INVFREQZ, FREQS and GRPDELAY. % CAUTION, parameter-value pairs can be used, but this feature will be % removed in the next release. % Author(s): J.N. Little, 6-26-86 % Copyright (c) 1988-98 by The MathWorks, Inc. % $Revision: 1.25.1.2 $ $Date: 1999/01/22 03:42:34 $ % ------------------------- % % Parse the input arguments % % ------------------------- % [params,fvflag] = freqzparse(varargin{:}); % fvflag will be set to one if a freqvector was specified by the user. b = params.numerator(:).'; % Make it a row vector a = params.denominator(:).'; % fs = params.fs; nb = length(b); na = length(a); % ------------------------------------- % % Actual Frequency Response Computation % % ------------------------------------- % if fvflag, % Frequency vector specified. Use Horner's method of polynomial % evaluation at the frequency points and divide the numerator % by the denominator. % % Note: we use positive i here because of the relationship % polyval(a,exp(i*w)) = fft(a).*exp(i*w*(length(a)-1)) % ( assuming w = 2*pi*(0:length(a)-1)/length(a) ) % a = [a zeros(1,nb-na)]; % Make sure a and b have the same length b = [b zeros(1,na-nb)]; [digw,xlab,xtickflag,xlim] = freqconv(params.freqvector,fs,... params.frequency,params.normfreq,params.range,'freq2dig'); % Scale w correctly s = exp(i*digw); % Digital frequency must be used for this calculation h = polyval(b,s) ./ polyval(a,s); w = params.freqvector; % Assign the correct freqvector to w else % freqvector not specified, use nfft and RANGE in calculation s = strmatch(lower(params.range),{'whole','half'}); n = params.nfft; if s*n < na | s*n < nb nfft = lcm(n,max(na,nb)); % dividenowarn temporarily shuts off warnings to avoid "Divide by zero" h = dividenowarn(fft([b zeros(1,s*nfft-nb)]),... fft([a zeros(1,s*nfft-na)])).'; h = h(1+(0:n-1)*nfft/n); else % dividenowarn temporarily shuts off warnings to avoid "Divide by zero" h = dividenowarn(fft([b zeros(1,s*n-nb)]),... fft([a zeros(1,s*n-na)])).'; h = h(1:n); end h = h(:); % Make it a column only when nfft is given (backwards comp.) [w,xlab,xtickflag,xlim] = freqconv(n,fs,params.frequency,... params.normfreq,params.range,'dig2freq',params.return_nyquist); % Scale w correctly w = w(:); % Make it a column only when nfft is given (backwards comp.) end % ------------------------------ % % Plot or Compute the Output % % ------------------------------ % if nargout == 0, % Plot when no output arguments are given % If it's normalized angular frequency divide by pi % because the plot's x domain is 0 to 1. if strmatch(params.normfreq,'yes') & strmatch(params.frequency,'angular'), w = w./pi; end freqzplot(h,w,params,fvflag,xlim,xlab,xtickflag); else % Don't plot, just return the (complex) frequency response. hh = h; ww = w; end % ------------------------------ % % Local Functions % % ------------------------------ % %------------------------------------------------------------------------------ function freqzplot(h,w,params,fvflag,xlim,xlab,xtickflag) % FREQZPLOT Plots the Magnitude and Phase response. % % Inputs: % h - complex frequency response vector % w - frequency vector % params - structure containing the following fields % normmag - YES/NO flag indicating if magnitude is normalized % magnitude - magnitude units string: 'DB', 'LINEAR' or 'SQUARED' % phase - YES/NO flag indicating if phase plot is required % range - frequency range, 'WHOLE' or 'HALF', which corresponds to % [0,Fs) or [0,Fs/2) respectively % fvflag - flag indicating if a frequency vector was specified by user % xlim - x-limits which corresponds to correct x-axis units % xlab - x-axis label with correct units % xtickFlag - specifies the frequency units (angular, normalized angular, % linear, etc.) newplot; if ishold, holdflag = 1; else holdflag = 0; end % Check if we should scale the magnitude, this is for plotting only [magh,ylab] = correctmag(h,params.normmag,params.magnitude); if strmatch(lower(params.phase),'yes'), subplot(212); % We plot the phase first to retain the functionality of freqz when hold is on plot(w,unwrap(angle(h))*180/pi); ax = gca; xlabel(xlab); ylabel('Phase (degrees)'); subplot(211); plot(w,magh); xlabel(xlab); ylabel(ylab); ax = [ax gca]; if ~holdflag, hfig = get(ax(1),'parent'); set(hfig,'nextplot','replace'); % Resets the figure so that next plot does not subplot end else, plot(w,magh); xlabel(xlab); ylabel(ylab); ax = gca; end axes(ax(1)); % Always bring the plot to the top set(ax,'xgrid','on','ygrid','on'); if fvflag, xlim = [w(1) w(end)]; % If a freqvector was given, overwrite the xlim returned by freqconv end % If hold is on, check if xlim in current plot is greater than the new calculated xlim if holdflag, currxlim = get(ax(end),'xlim'); xlim(1) = min([currxlim(1) xlim(1)]); xlim(2) = max([currxlim(2) xlim(2)]); end set(ax,'xlim',xlim); if ~fvflag, % If xtickflag='normang' sets the ticks of x-axis in units of pi. %setxticks(ax,params.range,xtickflag); end %------------------------------------------------------------------------------ function [magh,ylab] = correctmag(h,normmag,magnitude) % CORRECTMAG scale the magnitude correctly according to given parameters. % CORRECTMAG will take the precomputed frequency response vector, h, % and calculate its magnitude according to the desired parameters. % MAGNITUDE can be either 'DB','LINEAR' or 'SQUARED' and NORMMAG can be % either 'YES' (divide everything by maximum value) or 'NO'. % [MAGH,YLAB] = correctmag(...) returns the correct y-axis label % YLAB. magh = abs(h); ylab = 'Magnitude'; if strmatch(lower(normmag),'yes'), magh = magh./max(magh); ylab = ['Normalized ' ylab]; end if strmatch(lower(magnitude),'db'), % These next few lines of code are here to avoid "Log of zero" warnings zerosIndx = find(magh==0); % Find the indicies where magnitude is zero magh(zerosIndx) = 1; % Place holder magh = 20*log10(magh); magh(zerosIndx) = -inf; % Set these magnitude values to log10(0)=-inf ylab = [ylab ' (dB)']; elseif strmatch(lower(magnitude),'squared'), magh = magh.*magh; ylab = [ylab ' Squared']; end %------------------------------------------------------------------------------ function [params,fvflag] = freqzparse(varargin) %FREQZPARSE Input parameter parser for the FREQZ function. % FREQZPARSE returns a structure, PARAMS, that contains all % relevant information for the FREQZ function. The elements % of the structure are assigned according to the input arguments. % Unspecified elements within the structure (that is, elements % corresponding to missing input arguments) are replaced by % default values according to the help given in FREQZ. % Search for 'nyquist' trailing option return_nyquist = 0; if nargin > 0, return_nyquist = strcmp(varargin{end},'return_nyquist'); if return_nyquist, varargin = varargin(1:end-1); end end nargin = length(varargin); %initialize flags oldsyntax = 0; mixedsyntax1 = 0; mixedsyntax2 = 0; newsyntax = 0; fvflag = 0; % Initialize the freqvector flag at zero rangeopts = {'half','whole'}; % These will be used in two different places rangeerr = 'Options for ''RANGE'' are either ''HALF'' or ''WHOLE'''; switch nargin, % set flags for input argument types as follows: % oldsyntax = 1, freqz(B,A,N,'whole',Fs) % mixedsyntax1 = 1, freqz(B,'parameter',value,...) % mixedsyntax2 = 1, freqz(B,A,'parameter',value,...) % newsyntax = 1, freqz('param1',val1,'param2',val2,...) case 1, % Numerator specified oldsyntax = 1; case 2, % May be Num and Den or p-v pair if ~ischar(varargin{1}), % Numerator and denominator given oldsyntax = 1; else % P-V pair given newsyntax = 1; end case 3, % May be old syntax (B,A,N) or Num and one p-v pair if ~ischar(varargin{2}), oldsyntax = 1; else % Num and one p-v pair mixedsyntax1 = 1; end case 4, if ischar(varargin{1}), % 2 p-v pairs given newsyntax = 1; elseif ischar(varargin{3}), % (B,A,'param',val) mixedsyntax2 = 1; else oldsyntax = 1; end case 5, if ischar(varargin{2}), % (B,'param1',val1,'param2',val2) mixedsyntax1 = 1; else oldsyntax = 1; end otherwise, % From now on it can only be mixed syntax or new syntax if rem(nargin,2), % (B,'param1',val1,'param2',val2,...) mixedsyntax1 = 1; elseif ischar(varargin{1}), newsyntax = 1; else % (B,A,'param',val,...) mixedsyntax2 = 1; end end % To use the old syntax, we must check for empty input arguments and % assign defaults if oldsyntax, % Set default values b = varargin{1}; a = 1; nfft = 512; range = 'half'; freq = 'angular'; Fs = 1; freqvector = 0:pi/512:pi-pi/512; normfreq = 'yes'; if nargin > 1, a = varargin{2}; end if nargin == 4, if ischar(varargin{4}), range = varargin{4}; else Fs = varargin{4}; freq = 'linear'; normfreq = 'no'; end elseif nargin == 5, Fs = varargin{5}; freq = 'linear'; range = varargin{4}; normfreq = 'no'; end % Check that the given range is valid rangeindex = strmatch(lower(range),rangeopts); if isempty(rangeindex), error(rangeerr); else, range = rangeopts{rangeindex}; end % The following code must be executed after Fs has been updated. if nargin > 2, % We must determine if it is (B,A,N) or (B,A,W) [m3,n3] = size(varargin{3}); % Get the size of the third argument if m3 > 1 & n3 > 1, error('Third argument must be either a frequency vector or a scalar'); elseif m3 * n3 <= 1 , % nfft given nfft = varargin{3}; else % frequency vector given, may be linear or angular frequency freqvector = varargin{3}; nfft = length(freqvector); fvflag = 1; % set a flag to indicate that a freqvector was given end end param = {'Numerator',b,'Denominator',a,'nfft',nfft,'range',range,... 'frequency',freq,'Fs',Fs,'Freqvector',freqvector,'normfreq',normfreq}; end if mixedsyntax1, param = {'Numerator',varargin{1},varargin{2:end}}; elseif mixedsyntax2, param = {'Numerator',varargin{1},'Denominator',varargin{2},varargin{3:end}}; elseif newsyntax, param = varargin; elseif ~oldsyntax, error('Invalid input syntax'); end % Search param to see if a freqvector is given, if so flag it. % search also if Fs was given, if so, set normfreq to 'no' if ~oldsyntax, % Default values normfreq = 'yes'; freq = 'angular'; if mixedsyntax1, istart = 2; % start searching at the second varargin for this syntax else istart = 1; % start searching at the first varargin for the other two possible syntaxs. end for i = istart:2:nargin if strmatch('freqv',lower(varargin{i})), fvflag = 1; % set freqvector flag, this will ignore 'nfft' and 'range' if given end if strmatch('fs',lower(varargin{i})), % if Fs is given, these are the defaults normfreq = 'no'; freq = 'linear'; end end end % Set up default values and replace with specified values where appropriate msg = ''; [params, msg] = parse_pv_pairs({'nfft',512;'fs',1;'freqvector',0:pi/512:pi-pi/512;... 'range','half';'phase','yes';'normfreq',normfreq;'normmag','no';... 'frequency',freq;'magnitude','db';... 'numerator',1;'denominator',1},param{:}); error(msg); % Now check that all string values set are valid. Note that this is not % necessary when the old syntax is used since in that case the strings % are automatically set if ~oldsyntax % setup all options ynopts = {'yes','no'}; rangeopts = {'half','whole'}; phaseopts = ynopts; normfreqopts = ynopts; normmagopts = ynopts; freqopts = {'angular','linear'}; magopts = {'db','linear','squared'}; % search for parameter matching idx = strmatch(lower(params.range),rangeopts); if isempty(idx), error(rangeerr); end idx = strmatch(lower(params.phase),phaseopts); if isempty(idx), error('Options for ''PHASE'' are either ''YES'' or ''NO'''); end idx = strmatch(lower(params.normfreq),normfreqopts); if isempty(idx), error('Options for ''NORMFREQ'' are either ''YES'' or ''NO'''); end idx = strmatch(lower(params.normmag),normmagopts); if isempty(idx), error('Options for ''NORMMAG'' are either ''YES'' or ''NO'''); end idx = strmatch(lower(params.frequency),freqopts); if isempty(idx), error('Options for ''FREQUENCY'' are either ''ANGULAR'' or ''LINEAR'''); end idx = strmatch(lower(params.magnitude),magopts); if isempty(idx), error('Options for ''MAGNITUDE'' are either ''DB'',''LINEAR'' or ''SQUARED'''); end end params.return_nyquist = return_nyquist; % [EOF] freqz.m