Code covered by the BSD License  

Highlights from
GUI for Asymptotic Bode Plots

image thumbnail

GUI for Asymptotic Bode Plots

by

 

07 Nov 2002 (Updated )

A tool for generating piecewise linear asymptotic Bode diagrams.

BodePlotGui(varargin)
function varargout = BodePlotGui(varargin)
% BODEPLOTGUI Application M-file for BodePlotGui.fig
%    FIG = BODEPLOTGUI launch BodePlotGui GUI.
%    BODEPLOTGUI('callback_name', ...) invoke the named callback.

% Last Modified by GUIDE v2.5 18-Oct-2011 14:11:08

%Written by Erik Cheever (Copyright 2002)
%Contact: erik_cheever@swarthmore.edu
% Erik Cheever
% Dept. of Engineering
% Swarthmore College
% 500 College Avenue
% Swarthmore, PA 19081  USA

%This function acts as a switchyard for several callbacks.  It also intializes
%the variables used by the GUI.  Note that all variables are initialized here to
%default values.  A brief description of each variable is included.

if (nargin == 0) ||  (isa(varargin{1},'tf')) %If no arguments, or first
    fig = openfig(mfilename,'new');
    handles = guihandles(fig);  %Get handles structure.
    initBodePlotGui(handles);
    handles=guidata(handles.AsymBodePlot);       %Reload handles after function call.
    
    if nargout > 0              %If output argument is used, set it to figure.
        varargout{1} = fig;
    end
    
    if ((nargin~=0) && (isa(varargin{1},'tf'))),
        %Transfer function chosen.
        handles.Sys=varargin{1};    %The variable Sys is the transfer function.
        doBodeGUI(handles);
    end
    
elseif ischar(varargin{1}) % INVOKE NAMED SUBFUNCTION OR CALLBACK
    try
        [varargout{1:nargout}] = feval(varargin{:}); % FEVAL switchyard
    catch
        disp(lasterr);
    end
end
end
% ------------------End of function BodePlotGui ----------------------


function initBodePlotGui(handles)
handles.IncludeString=[];   %An array of strings representing terms to include in the plot.
handles.ExcludeString=[];   %An array of strings representing terms to exclude from plot.
handles.IncElem=[];         %An array of indices of terms corresponding to their
% location in the IncludeString array.
handles.ExcElem=[];         %An array of indices of terms corresponding to their
% location in the ExcludeString array.
handles.FirstPlot=1;        %This term is 1 the first time a plot is made.  This lets
% Matlab do the original autoscaling.  The scales are then
% saved and reused.
handles.MagLims=[];         %The limits on the magnitude plot determined by MatLab autoscaling.
handles.PhaseLims=[];       %The limits on the phase plot determined by MatLab autoscaling.
handles.LnWdth=2;
set(handles.LineWidth,'String',num2str(handles.LnWdth));

%Set the color of lines used in gray scale.  The plotting functions
%cycle through these colors (and then cycle through the linestyles).
handles.Gray=[0.75 0.75 0.75; 0.5 0.5 0.5; 0.25 0.25 0.25];
handles.GrayZero=[0.9 0.9 0.9];  %This is the color used for the zero reference.

%Set the color of lines used in color plots.  The plotting functions
%cycle through these colors (and then cycle through the linestyles).
handles.Color=[0 1 1; 0 0 1; 0 1 0; 1 0 0; 1 0 1;1 0.52 0.40];
handles.ColorZero=[1 1 0];  %Yellow, this is the color used for the zero reference.

%Sets order of linestyles used.
handles.linestyle={':','--','-.'};

%This sets the default scheme to color (GUI can set them to gray scale).
handles.colors=handles.Color;
handles.zrefColor=handles.ColorZero;
handles.exactColor=[0 0 0]; %Black

handles.Sys=[];
handles.SysInc=[];

handles.Terms=[];
%The structure "Term" has three elements.
%            type: this can be any of the 7 types listed below.
%                   1) The multiplicative constant.
%                   2) Real poles
%                   3) Real zeros
%                   4) Complex poles
%                   5) Complex zeros
%                   6) Poles at the origin
%                   7) Zeros at the origin
%           value: this is the location of the pole or zero (or in the case
%                  of the multiplicative constant, its value).
%    multiplicity: this gives the multiplicity of the pole or zero.  It has
%                  no meaning in the case of the multiplicative constant.

%The variable "Acc" is a relative accuracy used to determine whether or not
%two poles (or zeros) are the same.  Because Matlab uses an approximate
%technique to find roots of an equation, it is likely to give slightly
%different locations to identical roots.
handles.Acc=1E-3;
set(handles.TransferFunctionText,'String',...
    {' ','No transfer function chosen',' '});

guidata(handles.AsymBodePlot, handles); %save changes to handles.
loadSystems(handles);
handles=guidata(handles.AsymBodePlot);       %Reload handles after function call.

guidata(handles.AsymBodePlot, handles); %save changes to handles.
end

function simpleTF = makeSimple(origTF)
simpleTF=minreal(origTF);   %Get minimum realization.
% Get numerator and denominator of two realizations.  If their
% lengths are unequal, it means that there were poles and zeros that
% cancelled.
[n1,d1]=tfdata(simpleTF,'v');
[n2,d2]=tfdata(origTF,'v');
if (length(n1)~=length(n2)),
    disp(' ');
    disp(' ');
    disp(' ');
    disp('************Warning******************');
    disp('Original transfer function was:');
    origTF
    disp('Some poles and zeros were equal.  After cancellation:');
    simpleTF
    disp('The simplified transfer function is the one that will be used.');
    disp('*************************************');
    disp(' ');
    beep;
    waitfor(warndlg('System has poles and zeros that cancel.  See Command Window for caveats.'));
end
end

function doBodeGUI(handles)
handles.Sys=makeSimple(handles.Sys);
handles.SysInc=handles.Sys; %The variable sysInc is that part of the transfer
% function that will be plotted (with no poles or zeros excluded).  Start with
% it equal to Sys.  This variable is modified in BodePlotSys

%The function BodePlotTerms separates the transfer function into its consituent parts.
% The variable DoQuit will come back as non-zero if there was a problem.
DoQuit=BodePlotTerms(handles);
handles=guidata(handles.AsymBodePlot);       %Reload handles after function call.

%if DoQuit is zero, there were no problems and we may continue.
if ~DoQuit,
    BodePlotter(handles);       %Make plot
    handles=guidata(handles.AsymBodePlot);       %Reload handles after function call.
    %DoQuit was non-zero, so there was a problem.  Quit program.
else
    CloseButton_Callback(fig,'',handles,'');
end
end

%| ABOUT CALLBACKS:
%| GUIDE automatically appends subfunction prototypes to this file, and
%| sets objects' callback properties to call them through the FEVAL
%| switchyard above. This comment describes that mechanism.
%|
%| Each callback subfunction declaration has the following form:
%| <SUBFUNCTION_NAME>(H, EVENTDATA, HANDLES, VARARGIN)
%|
%| The subfunction name is composed using the object's Tag and the
%| callback type separated by '_', e.g. 'slider2_Callback',
%| 'figure1_CloseRequestFcn', 'axis1_ButtondownFcn'.
%|
%| H is the callback object's handle (obtained using GCBO).
%|
%| EVENTDATA is empty, but reserved for future use.
%|
%| HANDLES is a structure containing handles of components in GUI using
%| tags as fieldnames, e.g. handles.figure1, handles.slider2. This
%| structure is created at GUI startup using GUIHANDLES and stored in
%| the figure's application data using GUIDATA. A copy of the structure
%| is passed to each callback.  You can store additional information in
%| this structure at GUI startup, and you can change the structure
%| during callbacks.  Call guidata(h, handles) after changing your
%| copy to replace the stored original so that subsequent callbacks see
%| the updates. Type "help guihandles" and "help guidata" for more
%| information.
%|
%| VARARGIN contains any extra arguments you have passed to the
%| callback. Specify the extra arguments by editing the callback
%| property in the inspector. By default, GUIDE sets the property to:
%| <MFILENAME>('<SUBFUNCTION_NAME>', gcbo, [], guidata(gcbo))
%| Add any extra arguments after the last argument, before the final
%| closing parenthesis.


% --------------------------------------------------------------------
function varargout = IncludedElements_Callback(~, ~, handles, varargin)
% Stub for Callback of the uicontrol handles.IncludedElements.
% If a term in the "Included Elements" box is clicked, this callback is invoked.
%Get index of element in box that is chosen.%If the index corresponds to one of the terms of the transfer function, deal with it.
i=get(handles.IncludedElements,'Value');
% The alternative is that it corresponds to another string in the box (there is a blank
% line, a line with dashes "----" and a line instructing the user to click on an element
% to include it).
if i<=length(handles.IncElem)
    TermsInd=handles.IncElem(i);        %Get the index of the included element.
    handles.Terms(TermsInd).display=0;  %Set display to 0 (to exclude it)
    guidata(handles.AsymBodePlot, handles); %save changes to handles.
    BodePlotter(handles);                   %Plot the Transfer function.
    handles=guidata(handles.AsymBodePlot);  %Reload handles after function call.
end
end
% ------------------End of function IncludedElements_Callback --------


% --------------------------------------------------------------------
function varargout = ExcludedElements_Callback(~, ~, handles, varargin)
% Callback of the uicontrol handles.ExcludedElements.
% If a term in the "Excluded Elements" box is clicked, this callback is invoked.
i=get(handles.ExcludedElements,'Value');  %Get index of element in box that is chosen.
%If the index corresponds to one of the terms of the transfer function, deal with it.
% The alternative is that it corresponds to another string in the box (there is a blank
% line, a line with dashes "----" and a line instructing the user to click on an element
% to exclude it).
if i<=length(handles.ExcElem)
    TermsInd=handles.ExcElem(i);        %Get the index of the excluded element.
    handles.Terms(TermsInd).display=1;  %Set display to 1 (to include it)
    guidata(handles.AsymBodePlot, handles); %save changes to handles.
    BodePlotter(handles);                   %Plot the Transfer function.
    handles=guidata(handles.AsymBodePlot);  %Reload handles after function call.
end
end
% ------------------End of function ExcludedElements_Callback --------


% --------------------------------------------------------------------
function varargout = CloseButton_Callback(~, ~, handles, varargin)
% Callback for the uicontrol handles.CloseButton.
%This function closes the window, and displays a message.
disp(' '); disp('Asymptotic Bode Plotter closed.'); disp(' '); disp(' ');
delete(handles.AsymBodePlot);
end
% ------------------End of function CloseButton_Callback -------------


% --------------------------------------------------------------------
function DoQuit=BodePlotTerms(handles)
%This function takes a system and splits it up into terms that are plotted
%individually when making a Bode plot by hand (it finds
%1) The multiplicative constant.
%2) All real poles
%3) All real zeros
%4) All complex poles
%5) All complex zeros
%6) All poles at the origin
%7) All zeros at the origin
%
%In addition to finding the poles and zeros, it determines their multiplicity.
%If two poles or zeros are very close they are determined to be the same pole
%or zero.

sys=handles.Sys;
Acc=handles.Acc;   %Relative accuracy.

[z,p,k]=zpkdata(sys,'v');   %Get pole and zero data.

%Find gain term.
[n,d]=tfdata(sys,'v');
k=n(max(find(n~=0)))/d(max(find(d~=0)));
term(1).type='Constant';
term(1).value=k;
term(1).multiplicity=1;

%Get all poles.
j=length(term);
for i=1:length(p),
    term(j+i).value=p(i);
    term(j+i).multiplicity=1;
    term(j+i).type='Pole';
end
%Get all zeros.
j=length(term);
for i=1:length(z),
    term(j+i).value=z(i);
    term(j+i).multiplicity=1;
    term(j+i).type='Zero';
end

%Check for multiplicity
for i=2:length(term),
    for k=(i+1):length(term),
        %Handle pole or zero at origin as special case.
        if (term(i).value==0),
            %Multiple pole or zero at origin.
            if (term(k).value==0),
                term(i).multiplicity=term(i).multiplicity+term(k).multiplicity;
                %Set multiplicity of kth term to 0 to signify that it has been
                % subsumed by term(i) (by increasing the ith term's multiplicity).
                term(k).multiplicity=0;
            end
            %We know term is not at origin, so check for (approximate) equality
            %  Since we know this term is not at origin, we can divide by value.
        elseif (abs((term(i).value-term(k).value)/term(i).value) < Acc),
            term(i).multiplicity=term(i).multiplicity+term(k).multiplicity;
            %Set multiplicity of kth term to 0 to signify that it has been
            % subsumed by term(i) (by increasing the ith term's multiplicity).
            term(k).multiplicity=0;
        end
    end
end

%Check for location of poles and zeros (and remove complex conjugates).
i=2;
while (i<=length(term)),
    %If root is at origin, handle it separately
    if (term(i).value==0),
        term(i).type=['Origin' term(i).type];
        %If imaginary part is sufficiently small...
    elseif (abs(imag(term(i).value)/term(i).value)<Acc),
        term(i).type=['Real' term(i).type];  %...Add "Real" to type
        term(i).value=real(term(i).value);   %...And set imaginary part=0
        %If imaginary part is *not* small...
    else
        term(i).type=['Complex' term(i).type];  %...Add "Complex" to type
        term(i+1).multiplicity=0;               %...Remove complex conjugate
        i=i+1;                                  %...And skip conjugate.
    end
    i=i+1;  %Go to next root.
end

%Remove all terms with multiplicity 0.
j=0;
for i=1:length(term),
    if (term(i).multiplicity~=0)
        j=j+1;
        term(j)=term(i);
        term(j).display=1;
    end
end

term=term(1:j);

DoQuit=0;
%Check for poles or zeros in right half plane,
% or on imaginary axis. Poles and zeros at origin are OK.
if any(real(p)>0),  %Poles in RHP.
    beep;
    waitfor(errordlg('System has poles with positive real part, cannot make plot.'));
    DoQuit=1;
    return;
end
if any(real(z)>0),  %Zeros in RHP.
    disp(' ');
    disp(' ');
    disp(' ');
    disp('************Warning******************');
    handles.Sys
    disp('is a nonminimum phase system (zeros in right half plane).');
    disp('The standard rules for phase do not apply.');
    disp(' ');
    disp('Also - The plots produced may be different than the Matlab Bode plot');
    disp('  by a factor of 360 degrees.  So though the graphs don''t look');
    disp('  the same, they are equivalent');
    disp(' ');
    disp('Location(s) of zero(s):');
    disp(z);
    disp('*************************************');
    disp(' ');
    beep;
    waitfor(warndlg('System has zeros with positive real part.  See Command Window for caveats.'));
end
%Check for terms near imaginary axis, or multiple poles or zeros at origin.
for i=2:length(term),
    if (term(i).value~=0),
        if (abs(real(term(i).value)/term(i).value)<Acc),
            disp(' ');
            disp(' ');
            disp(' ');
            disp('************Warning******************');
            handles.Sys
            disp('has a pole or zero near, or on, the imaginary axis.');
            disp('The plots may be inaccurate near that frequency.')
            disp(' ');
            disp('--------');
            disp('Pole(s):');
            disp(p)
            disp('--------');
            disp('Zero(s):');
            disp(z);
            disp('*************************************');
            disp(' ');
            disp(' ');
            disp(' ');
            beep;
            waitfor(warndlg('System has poles or zeros with real part near zero.  See Command Window.'));
        end
    elseif (term(i).multiplicity>1),
        disp(' ');
        disp(' ');
        disp(' ');
        disp('************Warning******************');
        handles.Sys
        disp('has multiple poles or zeros at the origin.');
        disp('Components of the phase plot may appear to disagree.');
        disp('This is because the phase of a complex number is');
        disp('not unique; the phase of -1 could be +180 degrees');
        disp('or -180 or +/-540...  Likewise the phase of 1/s^2');
        disp('could be +180 degrees, or -180 degrees (or +/-540');
        disp('Keep this in mind when looking at the phase plots.');
        disp('*************************************');
        disp(' ');
        beep;
        waitfor(warndlg('System has multiple poles or zeros at origin.  See Command Window.'));
    end
end

handles.Terms=term;
guidata(handles.AsymBodePlot, handles);  %save changes to handles.
end
% ------------------End of function BodePlotTerms --------------------


% --------------------------------------------------------------------
function BodePlotter(handles)

%Get the constituent terms and the system itself.
Terms=handles.Terms;
sys=handles.Sys;

%Call function to get a system with only included poles and zeros.
BodePlotSys(handles);
handles=guidata(handles.AsymBodePlot);  %Reload handles after function call.

%Get systems with included poles and zeros.
sysInc=handles.SysInc;

%Find min and max freq.
MinF=realmax;
MaxF=-realmax;
for i=2:length(Terms),
    if Terms(i).value~=0,
        MinF=min(MinF,abs(Terms(i).value));
        MaxF=max(MaxF,abs(Terms(i).value));
    end
end

%If there is exclusively a pole or zero at origin, MinF and MaxF will
% not have changed.  So set them arbitrarily to be near unity.
if MaxF==-realmax,
    MinF=0.9;
    MaxF=1.1;
end

%MinFreq is a bit more than an order of magnitude below lowest break.
MinFreq=10^(floor(log10(MinF)-1.01));
%MaxFreq is a bit more than an order of magnitude above highest break.
MaxFreq=10^(ceil(log10(MaxF)+1.01));
%Calculate 500 frequency points for plotting.
w=logspace(log10(MinFreq),log10(MaxFreq),1000);

%%%%%%%%%%%%%%%%%%%% Start Magnitude Plot %%%%%%%%%%
axes(handles.MagPlot);
cla;

%Plot line at 0 dB for reference.
semilogx([MinFreq MaxFreq],[0 0],...
    'Color',handles.zrefColor,...
    'LineWidth',1.5);

hold on;

%For each term, plot the magnitude accordingly.
%The variable mag_a has the combined asymptotic magnitude.
mag_a=zeros(size(w));
%The variable peakinds holds the indices at which peaks in underdamped
%responses occur.
peakinds=[];
for i=1:length(Terms),
    if Terms(i).display,
        switch Terms(i).type,
            case 'Constant',
                %A constant term is unchanging from beginning to end.
                f=[MinFreq MaxFreq];
                m=20*log10(abs([Terms(i).value Terms(i).value]));
                semilogx(f,m,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
                mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
            case 'RealPole',
                %A real pole has a single break frequency and then
                %decreases at 20 dB per decade (Or more if pole is multiple).
                wo=-Terms(i).value;
                f=[MinFreq wo MaxFreq];
                m=-20*log10([1 1 MaxFreq/wo])*Terms(i).multiplicity;
                semilogx(f,m,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
                mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
            case 'RealZero',
                %Similar to real pole, but increases instead of decreasing.
                wo=abs(Terms(i).value);
                f=[MinFreq wo MaxFreq];
                m=20*log10([1 1 MaxFreq/wo])*Terms(i).multiplicity;
                semilogx(f,m,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
                mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
            case 'ComplexPole',
                %A complex pole has a single break frequency and then
                %decreases at 40 dB per decade (Or more if pole is multiple).
                %There is also a peak value whose height and location are
                %determined by the natural frequency and damping coefficient.
                %We will plot a circle ('o') at the location of the peak.
                wn=abs(Terms(i).value);
                theta=atan(abs(imag(Terms(i).value)/real(Terms(i).value)));
                zeta=cos(theta);
                if (zeta < 0.5),  %Show peaking if zeta<0.5
                    peak=2*zeta;
                    f=[MinFreq wn wn wn MaxFreq];
                    m=-20*log10([1 1 peak 1 (MaxFreq/wn)^2])*Terms(i).multiplicity;
                    semilogx(f,m,...
                        'LineWidth',handles.LnWdth,...
                        'LineStyle',GetLineStyle(i,handles),...
                        'Color',GetLineColor(i,handles));
                    semilogx(wn,-20*log10(peak),'o','Color',GetLineColor(i,handles));
                    % Set up for interpolation (w/o peak)
                    f=[MinFreq wn MaxFreq];
                    m=-20*log10([1  1 (MaxFreq/wn)^2])*Terms(i).multiplicity;
                    mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
                    %Find location closest to peak, and adjust its amplitude.
                    index=find(w>=wn,1,'first');
                    mag_a(index)=mag_a(index)-20*log10(peak);
                    peakinds=[peakinds index];    %Save this index.
                else
                    f=[MinFreq wn MaxFreq];
                    m=-20*log10([1 1 (MaxFreq/wn)^2])*Terms(i).multiplicity;
                    semilogx(f,m,...
                        'LineWidth',handles.LnWdth,...
                        'LineStyle',GetLineStyle(i,handles),...
                        'Color',GetLineColor(i,handles));
                    % Set up for interpolation (w/o peak)
                    mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
                end
            case 'ComplexZero',
                %Similar to complex pole, but increases instead of decreasing.
                wn=abs(Terms(i).value);
                theta=atan(abs(imag(Terms(i).value)/real(Terms(i).value)));
                zeta=cos(theta);
                if (zeta < 0.5),  %Show peaking if zeta<0.5
                    peak=2*zeta;
                    f=[MinFreq wn wn wn MaxFreq];
                    m=20*log10([1 1 peak 1 (MaxFreq/wn)^2])*Terms(i).multiplicity;
                    semilogx(f,m,...
                        'LineWidth',handles.LnWdth,...
                        'LineStyle',GetLineStyle(i,handles),...
                        'Color',GetLineColor(i,handles));
                    semilogx(wn,20*log10(peak),'o','Color',GetLineColor(i,handles));
                    % Set up for interpolation (w/o peak)
                    f=[MinFreq wn MaxFreq];
                    m=20*log10([1  1 (MaxFreq/wn)^2])*Terms(i).multiplicity;
                    mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
                    %Find location closest to peak, and adjust its amplitude.
                    index=find(w>=wn,1,'first');
                    mag_a(index)=mag_a(index)+20*log10(peak);
                    peakinds=[peakinds index];    %Save this index.
                else
                    f=[MinFreq wn MaxFreq];
                    m=20*log10([1 1 (MaxFreq/wn)^2])*Terms(i).multiplicity;
                    semilogx(f,m,...
                        'LineWidth',handles.LnWdth,...
                        'LineStyle',GetLineStyle(i,handles),...
                        'Color',GetLineColor(i,handles));
                    % Set up for interpolation (w/o peak)
                    mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
                end
            case 'OriginPole',
                %A pole at the origin is a monotonically decreasing straigh line.
                f=[MinFreq MaxFreq];
                m=-20*log10([MinFreq MaxFreq])*Terms(i).multiplicity;
                semilogx(f,m,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
                mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
            case 'OriginZero',
                %Similar to pole at origin, but increases instead of decreasing.
                f=[MinFreq MaxFreq];
                m=20*log10([MinFreq MaxFreq])*Terms(i).multiplicity;
                semilogx(f,m,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
                mag_a=mag_a+interp1(log(f),m,log(w));  %Build up asymptotic approx.
        end
    end
end

%Set the x-axis limits to the minimum and maximum frequency.
set(gca,'XLim',[MinFreq MaxFreq]);

[mg,ph,w]=bode(sysInc,w);   %Calculate the exact bode plot.
semilogx(w,20*log10(mg(:)),...
    'Color',handles.exactColor,...
    'LineWidth',handles.LnWdth/2);  %Plot it.
if (get(handles.ShowAsymptoticCheckBox,'Value')~=0),
    semilogx(w,mag_a,...
        'Color',handles.exactColor,...
        'LineStyle',':',...
        'LineWidth',handles.LnWdth);  %Plot asymptotic approx.
    semilogx(w(peakinds),mag_a(peakinds),'o','Color',handles.exactColor);
end

if handles.FirstPlot,
    %If this is the first time, let Matlab do autoscaling, but save
    %the y-axis limits so that they will be unchanged as the plot changes.
    ylims=get(gca,'YLim')/20;
    ylims(1)=min(-20,ceil(ylims(1))*20);
    ylims(2)=max(20,floor(ylims(2))*20);
    handles.MagLims=ylims;
else
    %If this is not the first time, retrieve the old y-axis limits.
    ylims=handles.MagLims;
end
set(gca,'YLim',ylims);
set(gca,'YTick',ylims(1):20:ylims(2));  %Make ticks every 20 dB.

ylabel('Magnitude - dB');
xlabel('Frequency - \omega, rad-sec^{-1}')
title('Magnitude Plot','color','b','FontWeight','bold');
if get(handles.GridCheckBox,'Value')==1,
    grid on
end
hold off;
%%%%%%%%%%%%%%%%%%%% End Magnitude Plot %%%%%%%%%%


%%%%%%%%%%%%%%%%%%%% Start Phase Plot %%%%%%%%%%
%Much of this section mirrors the previous section and is not commented.
%One difference is that phase is calculated explicitly, rather than use
%Matlab's "bode" command.  Since phase is not unique (you can add or
%subtract multiples of 360 degrees) There were sometimes discrepancies
%between Matlab's phase calculations and mine
axes(handles.PhasePlot);
cla;

%Plot line at 0 degrees for reference.
semilogx([MinFreq MaxFreq],[0 0],...
    'Color',handles.zrefColor,...
    'LineWidth',1.5);
hold on;

%The variable phs_a has the combined asymptotic phase.
phs_a=zeros(size(w));
for i=1:length(Terms),
    if Terms(i).display,
        switch Terms(i).type,
            case 'Constant',
                f=[MinFreq MaxFreq];
                if Terms(i).value>0,
                    p=[0 0];
                else
                    p=[180 180];
                end
                if get(handles.RadianCheckBox,'Value')==1,
                    p=p/180;
                end
                semilogx(f,p,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
            case 'RealPole',
                wo=-Terms(i).value;
                f=[MinFreq wo/10 wo*10 MaxFreq];
                p=[0 0 -90 -90]*Terms(i).multiplicity;
                if get(handles.RadianCheckBox,'Value')==1,
                    p=p/180;
                end
                semilogx(f,p,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
            case 'RealZero',
                if Terms(i).value>0,  %Non-minimum phase
                    wo=Terms(i).value;
                    %Uncomment the next section to get agreement with Matlab plots.
                    %if Terms(1).value>0,  %Choose 0 or 360 to agree with MatLab plots
                    %    p=[0 0 0 0];      % (based on sign of constant term).
                    % else
                    %    p=[360 360 360 360];
                    %end
                    p=[0 0 -90 -90]*Terms(i).multiplicity;
                else
                    %Minimum phase
                    wo=-Terms(i).value;
                    p=[0 0 90 90]*Terms(i).multiplicity;
                end
                f=[MinFreq wo/10 wo*10 MaxFreq];
                if get(handles.RadianCheckBox,'Value')==1,
                    p=p/180;
                end
                semilogx(f,p,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
            case 'ComplexPole',
                wo=abs(Terms(i).value);
                bf=0.2^zeta;
                f=[MinFreq wo*bf wo/bf MaxFreq];
                p=[0 0 -180 -180]*Terms(i).multiplicity;
                if get(handles.RadianCheckBox,'Value')==1,
                    p=p/180;
                end
                semilogx(f,p,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
            case 'ComplexZero',
                wo=abs(Terms(i).value);
                bf=0.2^zeta;
                f=[MinFreq wo*bf wo/bf MaxFreq];
                if real(Terms(i).value)>0,   %Non-minimum phase
                    p=[0 0 -180 -180]*Terms(i).multiplicity;
                else
                    p=[0 0 180 180]*Terms(i).multiplicity;
                end
                if get(handles.RadianCheckBox,'Value')==1,
                    p=p/180;
                end
                semilogx(f,p,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
            case 'OriginPole',
                f=[MinFreq MaxFreq];
                p=[-90 -90]*Terms(i).multiplicity;
                if get(handles.RadianCheckBox,'Value')==1,
                    p=p/180;
                end
                semilogx(f,p,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
                
            case 'OriginZero',
                f=[MinFreq MaxFreq];
                p=[90 90]*Terms(i).multiplicity;
                if get(handles.RadianCheckBox,'Value')==1,
                    p=p/180;
                end
                semilogx(f,p,...
                    'LineWidth',handles.LnWdth,...
                    'LineStyle',GetLineStyle(i,handles),...
                    'Color',GetLineColor(i,handles));
        end
        phs_a=phs_a+interp1(log(f),p,log(w));  %Build up asymptotic approx.
    end
end

if get(handles.RadianCheckBox,'Value')==1,
    ph=ph/180;
end

semilogx(w,ph(:),...
    'Color',handles.exactColor,...
    'LineWidth',handles.LnWdth/2);  %Plot it.
if (get(handles.ShowAsymptoticCheckBox,'Value')~=0),
    % There can be discrepancies between Matlabs calculation of phase and
    % the quantity "phs_a."  This is because phase is not unique (you can
    % add or subtract multiples of 360 degrees).  The next line shifts the
    % value of phs_a so that phs_a(1)=ph(1).  Ensuring they are the same
    % at the beginning of the plot ensures that they align elsewhere.  Note
    % that if the plots are already aligned (ph(1)=phs_a(1)), that the next
    % line does nothing.
    phs_a=phs_a+(ph(1)-phs_a(1));
    semilogx(w,phs_a,...
        'Color',handles.exactColor,...
        'LineStyle',':',...
        'LineWidth',handles.LnWdth);  %Plot asymptotic approx.
end

set(gca,'XLim',[MinFreq MaxFreq]);
if handles.FirstPlot,
    ylims=get(gca,'YLim')/45;
    ylims(1)=ceil(ylims(1)-1)*45;
    ylims(2)=floor(ylims(2)+1)*45;
    handles.DPhaseLims=ylims;     %Find phase limits in degrees
    handles.RPhaseLims=ylims/180; %Find phase limits in radians/pi
else
    if get(handles.RadianCheckBox,'Value')==1,
        ylims=handles.RPhaseLims;
    else
        ylims=handles.DPhaseLims;
    end
end

if get(handles.RadianCheckBox,'Value')==1,
    set(gca,'YLim',ylims);
    set(gca,'YTick',ylims(1):0.25:ylims(2))
    ylabel('Phase - radians/\pi');
else
    set(gca,'YLim',ylims);
    set(gca,'YTick',ylims(1):45:ylims(2))
    ylabel('Phase - degrees');
end

xlabel('Frequency - \omega, rad-sec^{-1}');
title('Phase Plot','color','b','FontWeight','bold');
if get(handles.GridCheckBox,'Value')==1,
    grid on
end
hold off;
%%%%%%%%%%%%%%%%%%%% End Phase Plot %%%%%%%%%%

BodePlotDispTF(handles);    %Display the transfer function
BodePlotLegend(handles);    %Display the legend.
handles=guidata(handles.AsymBodePlot);  %Reload handles after function call.

%Set first plot to zero (so Matlab won't autoscale on subsequent calls).
handles.FirstPlot=0;

guidata(handles.AsymBodePlot, handles);  %save changes to handles.
end
% ----------------- End of function BodePlotter ----------------------


% --------------------------------------------------------------------
function BodePlotSys(handles)
%This function makes up a transfer function of all the terms that are not in
%the "Excluded Elements" box in the GUI.

Terms=handles.Terms;    %Get all terms from original transfer function.
p=[];                   %Start with no poles, or zeros, and a constant of 1
z=[];
k=1;

for i=1:length(Terms),      %For each term.
    %If the term is not in "Excluded Elements". then we want to display it.
    if Terms(i).display,
        switch Terms(i).type,
            case 'Constant',
                k=Terms(i).value;               %This is the constant.
            case 'RealPole',
                for j=1:Terms(i).multiplicity,
                    p=[p Terms(i).value];       %Add poles.
                end
            case 'RealZero',
                for j=1:Terms(i).multiplicity,
                    z=[z Terms(i).value];       %Add zeros.
                end
            case 'ComplexPole',
                for j=1:Terms(i).multiplicity,
                    p=[p Terms(i).value];       %Add poles.
                    p=[p conj(Terms(i).value)];
                end
            case 'ComplexZero',
                for j=1:Terms(i).multiplicity,
                    z=[z Terms(i).value];       %Add zeros.
                    z=[z conj(Terms(i).value)];
                end
            case 'OriginPole',
                for j=1:Terms(i).multiplicity,
                    p=[p 0];                    %Add poles.
                end
            case 'OriginZero',
                for j=1:Terms(i).multiplicity,
                    z=[z 0];                    %Add zeros.
                end
        end
    end
end

%Determine multiplicative constant in standard Bode Plot form.
for i=1:length(p),
    if p(i)~=0
        k=-k*p(i);
    end
end
for i=1:length(z),
    if z(i)~=0
        k=-k/z(i);
    end
end
%If poles and/or zeros were complex conjugate pairs, there may be
%some residual imaginary part due to finite precision.  Remove it.
k=real(k);

handles.SysInc=zpk(z,p,k);
guidata(handles.AsymBodePlot, handles);  %save changes to handles.
end
% ------------------End of function BodePlotSys ----------------------


% --------------------------------------------------------------------
function BodePlotDispTF(handles)
% This function displays a tranfer function that is a helper function for the
% BodePlotGui routine.  It takes the transfer function of the numerator and
% splits it into three lines so that it can be displayed nicely.  For example:
% "            s + 1"
% "H(s) = ---------------"
% "        s^2 + 2 s + 1"
%
% The numerator string is in the variable nStr,
% the second line is in divStr,
% and the denominator string is in dStr.

% Get numerator and denominator.
[n,d]=tfdata(handles.SysInc,'v');

% Get string representations of numerator and denominator
nStr=poly2str(n,'s');
dStr=poly2str(d,'s');

% Find length of strings.
LnStr=length(nStr);
LdStr=length(dStr);

if LnStr>LdStr,
    %the numerator is longer than denominator string, so pad denominator.
    n=LnStr;              %n is the length of the longer string.
    nStr=['     ' nStr];   %add spaces for characters at beginning of divStr.
    dStr=['     ' blanks(floor((LnStr-LdStr)/2)) dStr];  %pad denominator.
else
    %the demoninator is longer than numerator, pad numerator.
    n=LdStr;
    nStr=['     ' blanks(floor((LdStr-LnStr)/2)) nStr];
    dStr=['     ' dStr];
end

divStr=[];
for i=1:n,
    divStr=[divStr '-'];
end
divStr=['H(s) = ' divStr];

set(handles.TransferFunctionText,'String',strvcat(nStr,divStr,dStr));

%Change type font and size.
set(handles.TransferFunctionText,'FontName','Courier New')
%set(handles.TransferFunctionText,'FontSize',10)

guidata(handles.AsymBodePlot, handles);  %save changes to handles.
end
% ------------------End of function BodePlotDispTF -------------------


% --------------------------------------------------------------------
function BodePlotLegend(handles)
%This function creates the legends for the Bode plot being displayed.
%It also makes four changes to the "handles" structure.
%   1)  It updates the array "IncElem" that holds the indices that determine
%       which elements are included in the Bode plot.
%   2)  It updates the sring array "IncStr" that hold the description of
%       each included elements.
%   3)  Updates ExcElem that holds indices of excluded elements.
%   4)  Updates ExcStr that hold descriptions of excluded elements.

%Load the terms and the plotting strings into local variables for convenience.
Terms=handles.Terms;

axes(handles.LegendPlot);   %Set axes to the legend widow,
cla;                        %   and clear it.
Xleg=[0 0.1];               %Xleg holds start and end of line segment for legend.
XlegText=0.125;             %XlegText is location of text.
FntSz=8;                    %Font Size of text.

y=1-1/(length(Terms)+6);    %Vertical location of first text item
plot(Xleg,[y y],...
    'Color',handles.exactColor,...
    'LineWidth',handles.LnWdth/2);   %Plot line for legend.
text(XlegText,y,'Exact Bode Plot','FontSize',FntSz);        %Place text
hold on;

if (get(handles.ShowAsymptoticCheckBox,'Value')~=0),
    y=1-2/(length(Terms)+6);    %Vertical location of second item
    plot(Xleg,[y y],...
        'Color',handles.exactColor,...
        'LineStyle',':',...
        'LineWidth',handles.LnWdth);   %Plot line for legend.
    text(XlegText,y,'Asymptotic Plot','FontSize',FntSz);               %Place text
end

y=1-3/(length(Terms)+6);     %Vertical location of third item.
plot(Xleg,[y y],...                %Line.
    'Color',handles.zrefColor,...
    'LineWidth',2);
text(XlegText,y,'Zero Value (for reference only)','FontSize',FntSz); %Text.

IncElem=[]; %The indices of elements to be included in plot.
ExcElem=[]; %The indices of elements to be excluded from plot.
IncStr='';  %An array of strings describing included elements.
ExcStr='';  %An array of strings describing excluded elements.

%These variables are used as local counters later.  Here they are initialized.
i1=1;
i2=1;

for i=1:length(Terms),          %For each term,
    
    %Tv is a local variable representing the pole location.  It is used solely
    %   for convenience.
    Tv=Terms(i).value;
    %Tm is a local variable representing the pole multiplicity.  It is used solely
    %   for convenience.
    Tm=Terms(i).multiplicity;
    %S2 is a blank string to be added to later in the loop.
    S2='';
    
    %The next section of code ("switch" statement) plus a few lines, creates
    %a string that describes the pole or zero, its location, muliplicity...
    %The variable "DescStr" hold a Descriptive String for the pole or zero. The
    %string "S2" is a Second String that holds additional information (if needed)
    switch Terms(i).type,
        case 'Constant',
            %If the term is a consant, print its value in a string.
            DescStr=sprintf('Constant = %0.2g (%0.2g dB)',Tv,20*log10(abs(Tv)));
            if Tv>=0,
                DescStr=[DescStr ' phi=0'];
            else
                DescStr=[DescStr ' phi=180 (pi/2)'];
            end;
        case 'RealPole',
            %If the term is a real pole, print its value in string.
            DescStr=sprintf('Real Pole at %0.2g',Tv);
        case 'RealZero',
            %If the term is a real zero, print its value in string.
            DescStr=sprintf('Real Zero at %0.2g',Tv);
            if real(Tv)>0,
                DescStr=[DescStr ' RHP (Non-min phase)'];
            end;
        case 'ComplexPole',
            %If the term is a complex pole, print its value in string.
            %However, do this in terms of natural frequency and damping, as
            %well as the actual location of the pole (in S2).
            wn=abs(Tv);
            theta=atan(abs(imag(Tv)/real(Tv)));
            zeta=cos(theta);
            DescStr=sprintf('Complex Pole at wn=%0.2g, zeta=%0.2g',wn,zeta);
            if (zeta < 0.5),  %peaking only if zeta<0.5
                S2=sprintf('(%0.2g +/- %0.2gj)  Circle shows peak height.',real(Tv),imag(Tv));
            else
                S2=sprintf('(%0.2g +/- %0.2gj)  (no peaking shown, zeta>0.5)',real(Tv),imag(Tv));
            end
        case 'ComplexZero',
            %If the term is a complex zero, print its value in string.
            %However, do this in terms of natural frequency and damping, as
            %well as the actual location of the zero (in S2).
            %Also note if it is a non-minimum phase zero.
            wn=abs(Tv);
            theta=atan(abs(imag(Tv)/real(Tv)));
            zeta=cos(theta);
            DescStr=sprintf('Complex Zero at wn=%0.2g, zeta=%0.2g',wn,zeta);
            if real(Tv)>0,
                DescStr=[DescStr ' (RHP, Non-min phase)'];
            end;
            if (zeta < 0.5),  %peaking only if zeta<0.5
                S2=sprintf('(%0.2g +/- %0.2gj)  Circle shows peak height.',real(Tv),imag(Tv));
            else
                S2=sprintf('(%0.2g +/- %0.2gj)  (no peaking shown, zeta>0.5)',real(Tv),imag(Tv));
            end
        case 'OriginPole',
            %If pole is at origin, not this.
            DescStr=sprintf('Pole at origin');
        case 'OriginZero',
            %If zero is at origin, not this.
            DescStr=sprintf('Zero at origin');
    end
    %If multiplicity is greater than one, not this as well.
    if Tm>1,
        DescStr=[DescStr sprintf(', mult=%d',Tm)];
    end
    
    %At this point we have a string (in "DescStr" and "S2").
    if Terms(i).display,    %If the term is to be included in plot....
        IncStr=strvcat(IncStr,DescStr); %Add the Desriptive String to IncStr
        IncElem(i1)=i;  %Add the appropriate index to the Included Elements list.
        i1=i1+1;        %Increment the index counter
        y=1-(i1+2)/(length(Terms)+6);   %Calculate the vertical position.
        plot(Xleg,[y y],...             %Plot the line.
            'LineWidth',handles.LnWdth,...
            'LineStyle',GetLineStyle(i,handles),...
            'Color',GetLineColor(i,handles));
        text(XlegText,y,strvcat(DescStr,S2),'FontSize',FntSz);  %Add the text.
    else    %The term is *not* to be included in plot, so...
        ExcStr=strvcat(ExcStr,DescStr); %Add its Desriptive String to ExcStr
        ExcElem(i2)=i;  %Add the appropriate index to the Excluded Elements list.
        i2=i2+1;        %Increment the index counter.
    end
end
hold off;
%Get rid of ticks around plot.
axis([0 1 0 1]);
set(gca,'Xtick',[]);
set(gca,'Ytick',[]);

%At this point the legend is completed.  Next we will make up the strings
%for the boxes that separately list included and excluded elements.

IncStr=strvcat(IncStr,' ');         %Add a blank line to IncStr.
IncStr=strvcat(IncStr,'-------');   %Add a series of dashes.
%If there are any included elements, we can click on box to exclude it.
if i1~=1
    IncStr=strvcat(IncStr,'Select element to exclude from plot');
end

ExcStr=strvcat(ExcStr,' ');         %Add a blank line to ExcStr
ExcStr=strvcat(ExcStr,'-------');   %Add a series of dashes.
%If there are any excluded elements, we can click on box to include it.
if i2~=1
    ExcStr=strvcat(ExcStr,'Select element to include in plot');
end

%Set the strings for included and excluded elements.
set(handles.IncludedElements,'String',IncStr);
set(handles.ExcludedElements,'String',ExcStr);

%Change the arrays holding included and excluded elements in the handles array.
handles.IncElem=IncElem;
handles.ExcElem=ExcElem;

guidata(handles.AsymBodePlot, handles);  %save changes to handles.
end
% ------------------End of function BodePlotLegend --------


% --------------------------------------------------------------------
% --- Executes during object creation, after setting all properties.
function LineWidth_CreateFcn(hObject, ~, ~)
% hObject    handle to LineWidth (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end
end
% ------------------End of function BodePlotLegend --------


% --------------------------------------------------------------------
% Set the width of the lines used in plots.
function LineWidth_Callback(~, ~, handles)
% hObject    handle to LineWidth (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
handles.LnWdth=str2num(get(handles.LineWidth,'String'));
guidata(handles.AsymBodePlot, handles);  %save changes to handles.
BodePlotter(handles);                    %Plot the Transfer function.
handles=guidata(handles.AsymBodePlot);   %Reload handles after function call.
end
% ------------------End of function BodePlotLegend --------


% --------------------------------------------------------------------
% Determines the line color of the ith plot.
function linecolor=GetLineColor(i,handles)
numColors=size(handles.colors,1);
%Cycle through colors by using mod operator.
linecolor=handles.colors(mod(i-1,numColors)+1,:);
end
% ------------------End of function GetLineColor --------


% --------------------------------------------------------------------
% Determines the line style of the ith plot.
function linestyle=GetLineStyle(i,handles)
numColors=size(handles.colors,1);
numLnStl=size(handles.linestyle,2);
%Cycle through line styles, incrementing after all colors are used.
linestyle=handles.linestyle{mod(ceil(i/numColors)-1,numLnStl)+1};
end
% ------------------End of function GetLineStyle --------


% --------------------------------------------------------------------
% --- Executes on button press in GrayCheckBox.
% This function sets the sequence of colors used in plotting to gray scales.
function GrayCheckBox_Callback(~, ~, handles)
if get(handles.GrayCheckBox,'Value')==1,  %If button is not set,
    handles.colors=handles.Gray;             %and set colors to gray scale
    handles.zrefColor=handles.GrayZero;
else
    handles.colors=handles.Color;            %and set colors to RGB
    handles.zrefColor=handles.ColorZero;
end
guidata(handles.AsymBodePlot, handles);   %save changes to handles.
BodePlotter(handles);                     %Plot the Transfer function.
handles=guidata(handles.AsymBodePlot);    %Reload handles after function call.
end
% ------------------End of function BodePlotLegend --------


% --- Executes on button press in GridCheckBox.
function GridCheckBox_Callback(~, ~, handles)
BodePlotter(handles);                     %Plot the Transfer function.
end

% --- Executes on button press in RadianCheckBox.
function RadianCheckBox_Callback(~, ~, handles)
BodePlotter(handles);                     %Plot the Transfer function.
end

% --- Executes on button press in WebButton.
function WebButton_Callback(~, ~, ~)
web('http://lpsa.swarthmore.edu/Bode/Bode.html','-browser')
end

% --- Executes on button press in ShowAsymptoticCheckBox.
function ShowAsymptoticCheckBox_Callback(~, ~, handles)
BodePlotter(handles);                     %Plot the Transfer function.
end


% --- Executes on button press in limitButton.
function limitButton_Callback(~, ~, ~)
s{1}='Restrictions on systems:';
s{2}=' 1) SISO (Single Input Single Output);';
s{3}=' 2) Proper systems (order of num <= order of den);';
s{4}=' 4) System must be a transfer function (i.e., not state space...)';
s{5}=' 5) Time delays are ignored.';
helpdlg(s,'Valid Systems');
end

% --- Executes on selection change in popupSystems.
function popupSystems_Callback(hObject, ~, handles)
i=get(hObject,'Value');
if  i ~= 1,     %If this is not the "User Systems" choice
    if i==2,    %This is the refresh choice
        loadSystems(handles);
        handles=guidata(handles.AsymBodePlot);       %Reload handles after function call.
    else        %THis is a valid choice, pick transfer function.
        initBodePlotGui(handles);
        handles=guidata(handles.AsymBodePlot);       %Reload handles after function call.
        handles.Sys=handles.WorkSpaceTFs{i};
        doBodeGUI(handles);
        handles=guidata(handles.AsymBodePlot);       %Reload handles after function call.
    end
end
guidata(hObject, handles);
end

% --- Executes during object creation, after setting all properties.
function popupSystems_CreateFcn(hObject, ~, ~)
% hObject    handle to popupSystems (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end
end

function loadSystems(handles)
[v_name, v_tf]=getBaseTFs;
set(handles.popupSystems,'String',v_name);
handles.WorkSpaceTFs=v_tf;
guidata(handles.AsymBodePlot, handles);  %save changes to handles.
end

function [varName, varTF]=getBaseTFs
% Find all valid transfer functions in workspace
s=evalin('base','whos(''*'')');
tfs=strvcat(s.class);             %x=class of all variable
tfs=strcmp(cellstr(tfs),'tf');    %Convert to cell array and find tf's
s=s(tfs);                         %Get just tf's
vname=strvcat(s.name);

varName{1}='User Systems';
varTF{1}=[];
varName{2}='Refresh Systems';
varTF{2}=[];
j=2;
for i=1:length(s)
    myTF=evalin('base',vname(i,:));
    if (size(myTF.num)==[1 1]),     %Check for siso
        j=j+1;
        varName{j}=vname(i,:);
        varTF{j}=myTF;
    end
end
end

Contact us