Mercurial > forge
diff main/signal/grpdelay.m @ 0:6b33357c7561 octave-forge
Initial revision
author | pkienzle |
---|---|
date | Wed, 10 Oct 2001 19:54:49 +0000 |
parents | |
children | 05e7e4427c6c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/grpdelay.m Wed Oct 10 19:54:49 2001 +0000 @@ -0,0 +1,240 @@ +## Copyright (C) 2000 Paul Kienzle +## +## This program is free software; you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2, or (at your option) +## any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; see the file COPYING. If not, write to the Free +## Software Foundation, 59 Temple Place - Suite 330, Boston, MA +## 02111-1307, USA. +## +## Based on freqz.m, Copyright (C) 1996, 1997 John W. Eaton + +## Compute the group delay of a filter. +## +## [g, w] = grpdelay(b) +## returns the group delay g of the FIR filter with coefficients b. +## The response is evaluated at 512 angular frequencies between 0 and +## pi. w is a vector containing the 512 frequencies. +## +## [g, w] = grpdelay(b,a) +## returns the group delay of the rational IIR filter whose numerator +## has coefficients b and denominator coefficients a. +## +## [g, w] = grpdelay(b,a,n) +## returns the group delay evaluated at n angular frequencies. For fastest +## computation n should factor into a small number of small primes. +## +## [g, w] = grpdelay(b,a,n,"whole") +## evaluates the group delay at n frequencies between 0 and 2*pi. +## +## [g, w] = grpdelay(b,a,n,Fs) +## evaluates the group delay at n frequencies between 0 and Fs/2. +## +## [g, w] = grpdelay(b,a,n,"whole",Fs) +## evaluates the group delay at n frequencies between 0 and Fs. +## +## grpdelay(...) +## plots the group delay vs. frequency. +## +## This computation is unstable since it involves cancellation of very +## small values. If the denominator becomes too small, the group delay +## is artificially set to 0. The computation is also unstable since the +## group delay can go to infinity for some filters. These points are +## set to zero as well so that the graph looks reasonable. +## +## Theory: group delay, g(w) = -d/dw [arg{H(e^jw)}], is the rate of change of +## phase with respect to frequency. It can be computed as: +## +## d/dw H(e^-jw) +## g(w) = ------------- +## H(e^-jw) +## +## where +## H(z) = B(z)/A(z) = sum(b_k z^k)/sum(a_k z^k). +## +## By the quotient rule, +## A(z) d/dw B(z) - B(z) d/dw A(z) +## d/dw H(z) = ------------------------------- +## A(z) A(z) +## Substituting into the expression above yields: +## A dB - B dA +## g(w) = ----------- = dB/B - dA/A +## A B +## +## Note that, +## d/dw B(e^-jw) = sum(k b_k e^-jwk) +## d/dw A(e^-jw) = sum(k a_k e^-jwk) +## which is just the FFT of the coefficients multiplied by a ramp. + +## TODO: demo("grpdelay",4) seems wrong. The delays in the detail plot +## TODO: are opposite those in the overall plot. +## TODO: combine with freqz since the two are almost identical +## TODO: don't reset graph state before exiting since the user may +## TODO: want to further decorate the graph. +function [g_r, w_r] = grpdelay(b, a, n, region, Fs) + + if (nargin<1 || nargin>5) + usage("[g, w]=grpdelay(b [, a [, n [, 'whole' [, Fs]]]])"); + elseif (nargin == 1) + ## Response of an FIR filter. + a=[]; n=[]; region=[]; Fs=[]; + elseif (nargin == 2) + ## Response of an IIR filter + n=[]; region=[]; Fs=[]; + elseif (nargin == 3) + region=[]; Fs=[]; + elseif (nargin == 4) + Fs=[]; + if !isstr(region) && !isempty(region) + Fs = region; region=[]; + endif + endif + + if isempty(a) a=1; endif + if isempty(n) n=512; endif + if isempty(region) + if isreal(b) && isreal(a) + region = "half"; + else + region = "whole"; + endif + endif + if isempty(Fs) + if (nargout==0) Fs = 2; else Fs = 2*pi; endif + endif + + if !is_scalar(n) + if nargin==4 ## Fs was specified + w = 2*pi*n/Fs; + else + w = n; + endif + n = length(n); + extent = 0; + elseif (strcmp(region,"whole")) + w = 2*pi*[0:(n-1)]/n; + extent = n; + else + w = pi*[0:(n-1)]/n; + extent = 2*n; + endif + + la = length(a); + a = reshape(a,1,la); + lb = length(b); + b = reshape(b,1,lb); + k = max([la, lb]); + + if (length(b) == 1 && length(a)>1) + hb = 1; + if length(a) == 1 + dhb = zeros(1,n); + else + dhb = 0; + endif + elseif( extent >= k) + hb = fft(postpad(b,extent)); + dhb = fft(postpad(b,extent).*[0:extent-1]); + else + hb = polyval(postpad(b,k),exp(j*w)); + dhb = polyval(postpad(b,k).*[0:k-1],exp(j*w)); + endif + if (length(a) == 1) + ha = a; + dha = 0; + elseif( extent >= k) + ha = fft(postpad(a,extent)); + dha = fft(postpad(a,extent).*[0:extent-1]); + else + ha = polyval(postpad(a,k),exp(j*w)); + dha = polyval(postpad(a,k).*[0:k-1],exp(j*w)); + endif + + g = dhb./hb - dha./ha; + idx = find(abs(hb.*ha)<100*eps); + g(idx)=zeros(size(idx)); + w = Fs*w/(2*pi); + + if nargout >= 1 # return values but don't plot + g_r = g(1:n); + w_r = w(1:n); + else # plot but don't return values + unwind_protect + grid; + xlabel(["Frequency (Fs=", num2str(Fs), ")"]); + ylabel("Group delay (samples)"); + plot(w(1:n), real(g(1:n)), ";;"); + unwind_protect_cleanup + grid("off"); + xlabel(""); + ylabel(""); + end_unwind_protect + endif + +endfunction + + +%!demo +%! subplot(211); +%! title ("zero at .9"); +%! grpdelay (poly (0.9 * exp(1i*pi))); +%! hold on; grid("on"); +%! stem (1, -9, "bo;target;"); +%! hold off; +%! +%! subplot(212); axis ([.9, 1.1, -9, 0]); +%! grpdelay (poly(0.9*exp(1i*pi)),[],[.9:.0001:1.1]*pi); +%! hold on; grid("on"); +%! stem(1,-9,"bo;target;"); +%! hold off; +%! axis(); oneplot(); +%! %-------------------------------------------------------------- +%! % From Oppenheim and Schafer, a single zero of radius r=0.9 at +%! % angle pi should have a group delay of about -9 at 1 and 1/2 +%! % at zero and 2*pi. + +%!demo +%! grpdelay(poly([1/0.9*exp(1i*pi*0.2), 0.9*exp(1i*pi*0.6)]), ... +%! poly([0.9*exp(-1i*pi*0.6), 1/0.9*exp(-1i*pi*0.2)])); grid('on'); +%! hold on; stem([0.2, 0.6, 1.4, 1.8], [9, -9, 9, -9],"bo;target;"); hold off; +%! %-------------------------------------------------------------- +%! % confirm the group delays approximately meet the targets +%! % don't worry that it is not exact, as I have not entered +%! % the exact targets. + +%!test +%! Fs = 8000; +%! [b, a] = cheby1(3, 3, 2*[1000, 3000]/Fs, 'stop'); +%! [h, w] = grpdelay(b, a, 256, "half", Fs); +%! [h2, w2] = grpdelay(b, a, 512, "whole", Fs); +%! assert (size(h), size(w)); +%! assert (length(h), 256); +%! assert (size(h2), size(w2)); +%! assert (length(h2), 512); +%! assert (h, h2(1:256)); +%! assert (w, w2(1:256)); + +%!demo +%! Fs = 8000; +%! [b, a] = cheby1(3, 3, 2*[1000, 3000]/Fs, 'stop'); +%! grpdelay(b,a,[],Fs); +%! %-------------------------------------------------------------- +%! % IIR bandstop filter has delays at [1000, 3000] + +%!demo +%! subplot(211); +%! b = fir1(40,0.3); +%! grpdelay(b); +%! subplot(212); axis([0.3, 0.5]); +%! grpdelay(b,[],pi*[.3:.0001:.5]); axis(); oneplot(); +%! %-------------------------------------------------------------- +%! % fir lowpass order 40 with cutoff at w=0.3 and details of +%! % the transition band [.3, .5]