home *** CD-ROM | disk | FTP | other *** search
- function [gm,pm,Wcg,Wcp] = imargin(mag,phase,w)
- %IMARGIN Gain and phase margins using interpolation.
- %
- % [Gm,Pm,Wcg,Wcp] = IMARGIN(MAG,PHASE,W) returns gain margin Gm,
- % phase margin Pm, and associated frequencies Wcg and Wcp, given
- % the Bode magnitude, phase, and frequency vectors MAG, PHASE,
- % and W from a system. Interpolation is performed between frequency
- % points to find the correct values.
- %
- % When invoked without left hand arguments IMARGIN(MAG,PHASE,W) plots
- % the Bode plot with the gain and phase margins marked with a
- % vertical line.
- %
- % IMARGIN works with the frequency response of both continuous and
- % discrete systems. For continuous systems it preferable to use the
- % MARGIN function which calculates margins analytically given transfer
- % function or state-space form.
- %
- % Example of IMARGIN:
- % [mag,phase.w] = dbode(a,b,c,d,1) or [mag,phase,w] = dbode(num,den)
- % [Gm,Pm,Wcg,Wcp] = imargin(mag,phase,w)
- %
- % See also: BODE, DBODE, and MARGIN.
-
- % Clay M. Thompson 7-25-90
- % Revised A.C.W.Grace 3-2-91, 6-21-92
- % Copyright (c) 1986-93 by the MathWorks, Inc.
-
- error(nargchk(3,3,nargin));
-
- Gm = []; Pm = []; Wcg = []; Wcp = [];
-
- w = w(:); % Make sure freq. is a column
- magdb = 20*log10(mag);
- logw = log10(w);
-
- % Find the points where the phase wraps.
- % The following code is based on the algorithm in the unwrap function.
-
- cutoff = 200; % Arbitrary value > 180
- [m, n] = size(phase); % Assume column orientation.
- p = rem(phase-360, 360); % Phases modulo 360.
- dp = [p(1,:);diff(p)]; % Differentiate phases.
- jumps = (dp > cutoff) + (dp < -cutoff); % Array of jumps locations
- jvec = (jumps~=0);
-
- % Find points where phase crosses -180 degrees
-
- pad = 360*ones(1,n);
- upcross = (([p;-pad] >= -180)&([pad;p] < -180));
- downcross = (([p;pad] <= -180)&([-pad;p] > -180));
- crossings = upcross + downcross;
- pvec = (crossings~=0);
-
- % Find points where magnitude crosses 0 db
-
- pad = ones(1,n);
- upcross = (([magdb;-pad] >= 0)&([pad;magdb] < 0));
- downcross = (([magdb;pad] <= 0)&([-pad;magdb] > 0));
- crossings = upcross + downcross;
- mvec = (crossings~=0);
-
- for i=1:n
- jloc = find(jvec(:,i)~=0);
- nj = length(jloc);
-
- % Remove points where phase has wrapped from pvec
- if ~isempty(jloc), pvec(jloc,i) = zeros(nj,1); end
-
- ploc = find(pvec(:,i)~=0);
- mloc = find(mvec(:,i)~=0);
-
- % Find phase crossover points and interpolate gain in db and log(freq)
- % at each point.
- lambda = (-180-p(ploc-1,i)) ./ (p(ploc,i)-p(ploc-1,i));
- gain = magdb(ploc-1,i) + lambda .* (magdb(ploc,i)-magdb(ploc-1,i));
- freq = logw(ploc-1) + lambda .* (logw(ploc)-logw(ploc-1));
-
- % Look for asymptotic behavior near -180 degrees. (30 degree tolerance).
- % Linearly extrapolate gain and frequency based on first 2 or last 2 points.
- tol = 30;
- if m>=2,
- if (abs(p(1,i)+180)<tol), % Starts near -180 degrees.
- lambda = (-180-p(1,i)) / (p(2,i)-p(1,i));
- exgain = magdb(1,i) + lambda * (magdb(2,i)-magdb(1,i));
- exfreq = logw(1) + lambda * (logw(2)-logw(1));
- gain = [gain;exgain]; freq = [freq;exfreq];
- end
- if (abs(p(m,i)+180)<tol), % Ends near -180 degrees.
- lambda = (-180-p(m-1,i)) / (p(m,i)-p(m-1,i));
- exgain = magdb(m-1,i) + lambda * (magdb(m,i)-magdb(m-1,i));
- exfreq = logw(m-1) + lambda * (logw(m)-logw(m-1));
- gain = [gain;exgain]; freq = [freq;exfreq];
- end
- end
-
- if isempty(gain),
- Gm = [Gm,inf]; Wcg = [Wcg,NaN];
- ndx = [];
- else
- [Gmargin,ndx] = min(abs(gain));
- Gm = [Gm,-gain(ndx)]; Wcg = [Wcg,freq(ndx)];
- end
-
- % Find gain crossover points and interpolate phase in degrees and log(freq)
- % at each point.
- lambda = -magdb(mloc-1,i) ./ (magdb(mloc,i)-magdb(mloc-1,i));
- ph = p(mloc-1,i) + lambda .* (p(mloc,i)-p(mloc-1,i));
- freq = logw(mloc-1) + lambda .* (logw(mloc)-logw(mloc-1));
-
- if isempty(ph),
- Pm = [Pm,inf]; Wcp = [Wcp,NaN];
- ndx = [];
- else
- [Pmargin,ndx] = min(abs(ph+180));
- Pm = [Pm,(ph(ndx)+180)]; Wcp = [Wcp,freq(ndx)];
- end
-
- end
-
- % Convert frequency back to rad/sec and gain back to magnitudes.
- Wcg(finite(Wcg)) = 10 .^ Wcg(finite(Wcg));
- Wcp(finite(Wcp)) = 10 .^ Wcp(finite(Wcp));
- Gm(finite(Gm)) = 10 .^ (Gm(finite(Gm))/20);
-
- % If no left hand arguments then plot graph and show location of margins.
- if nargout==0,
- holdon = ishold;
- subplot(211)
- if holdon
- hold on
- end
-
- % Plot bode magnitudes
- semilogx(w,20*log10(mag))
- hold on
- xlabel('Frequency (rad/sec)'), ylabel('Gain dB')
-
- limits = axis;
- % Plot gain and phase margin lines
- if Wcg ~= 0 & finite(Wcg)
- semilogx([Wcg;Wcg],[-20*log10(Gm);zeros(1,length(Gm))],'w-')
- semilogx([Wcg;Wcg],limits(3:4)','w:') %,[Wcp;Wcp],[0;limits(3)],'w:')
- end
- if finite(Wcp)
- semilogx([Wcp;Wcp],[0;limits(3)],'w:')
- end
- limits = axis;
- plot(limits(1:2),[0,0],'w:') % 0 dB line
-
-
- title(['Gm=',num2str(20*log10(Gm)), ' dB,',...
- ' (w= ',num2str(Wcg), ...
- ') Pm=', num2str(Pm), ' deg.', ...
- ' (w=', num2str(Wcp),')' ]);
-
- if ~holdon, hold off, end;
-
- subplot(212)
- semilogx(w,p)
- hold on
- xlabel('Frequency (rad/sec)'), ylabel('Phase deg')
-
- phase180 = -180*ones(1,length(Pm));
- if finite(Wcp)
- semilogx([Wcp;Wcp],[Pm+phase180;phase180],'w-') % Plot phase margins
- semilogx([Wcp;Wcp],[0 -360],'W:')
- end
- if Wcg ~= 0 & finite(Wcg)
- semilogx([Wcg;Wcg],[0;-180],'w:')
- end
- limits = axis;
- plot(limits(1:2),[-180 -180],'w:') % 180 degree line
- set(gca,'ylim',[-360, 0]);
- set(gca,'ytick',[0 -90 -180 -270 -360])
- if ~holdon, hold off, end; % Return hold to previous status
- subplot(111)
- else
- gm = Gm;
- pm = Pm;
- end
-