function cl_bode_plots
%
% Plots closed-loop transfer functions and responses for several gains
%
%   Outputs: cl_bode_plots.eps       (Frequency magnitude response plots)
%            cl_impulse_response.eps (Impulse responses)
%            cl_step_response.eps    (Step responses)
%
% Actually, this script is meant to be a prototype for a PSTricks plot and
% an example verifying the math.
%

%
% Copyright (c) 2008 by Theodore P. Pavlic
%
% This work is licensed under the Creative Commons
% Attribution-Noncommercial 3.0 United States License. To view a copy of
% this license, visit http://creativecommons.org/licenses/by-nc/3.0/us/
% or send a letter to Creative Commons, 171 Second Street, Suite 300,
% San Francisco, California, 94105, USA.
%

% Upper-case    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
% Lower-case    a b c d e f g h i j k l m n o p q r s t u v w x y z
% Digits        0 1 2 3 4 5 6 7 8 9
% Exclamation   !           Double quote "          Hash (number) #
% Dollar        $           Percent      %          Ampersand     &
% Acute accent  '           Left paren   (          Right paren   )
% Asterisk      *           Plus         +          Comma         ,
% Minus         -           Point        .          Solidus       /
% Colon         :           Semicolon    ;          Less than     <
% Equals        =           Greater than >          Question mark ?
% At            @           Left bracket [          Backslash     \
% Right bracket ]           Circumflex   ^          Underscore    _
% Grave accent  `           Left brace   {          Vertical bar  |
% Right brace   }           Tilde        ~

%%%%%%%%%%%%%%%%%%%%%%%%%%% Start of Script %%%%%%%%%%%%%%%%%%%%%%%%%%%

figure(1); clf;
subplot(131); cl_bode_plot(1, -5, -2);
subplot(132); cl_bode_plot(5, -5, -2);
subplot(133); cl_bode_plot(50, -5, -2);

% Uncomment these lines if you want to save the EPS
%
%set( gcf, ...
%    'Position', [0 0 800 600], ...
%    'PaperType', 'usletter', ...
%    'PaperOrientation', 'portrait', ...
%    'PaperPosition', [0.0 0.0 15 4.5] );
%saveas( gcf, 'cl_bode_plots.eps', 'epsc2' );

figure(2); clf;
subplot(131); cl_impulse_response(1, -5, -2);
subplot(132); cl_impulse_response(5, -5, -2);
subplot(133); cl_impulse_response(50, -5, -2);

% Uncomment these lines if you want to save the EPS
%
%set( gcf, ...
%    'Position', [0 0 800 600], ...
%    'PaperType', 'usletter', ...
%    'PaperOrientation', 'portrait', ...
%    'PaperPosition', [0.0 0.0 15 4.5] );
%saveas( gcf, 'cl_impulse_response.eps', 'epsc2' );

figure(3); clf;
subplot(131); cl_step_response(1, -5, -2);
subplot(132); cl_step_response(5, -5, -2);
subplot(133); cl_step_response(50, -5, -2);

% Uncomment these lines if you want to save the EPS
%
%set( gcf, ...
%    'Position', [0 0 800 600], ...
%    'PaperType', 'usletter', ...
%    'PaperOrientation', 'portrait', ...
%    'PaperPosition', [0.0 0.0 15 4.5] );
%saveas( gcf, 'cl_step_response.eps', 'epsc2' );


    function [sigma, dampfreq, zeta] = second_order_closed_loop( K, a, b )

        % Open-loop parameters are K (gain), a and b (poles)

        % Closed-loop poles
        p = roots( [1 -(a+b) (1+K)*a*b ] );

        % Damping and damped frequency
        sigma = -real(p(1));
        dampfreq = imag(p(1));

        % Damping ratio (sigma/(natural frequency))
        % zeta = sigma(1)/abs(p(1));
        zeta = sigma(1)./sqrt( sigma(1).^2 + dampfreq(1).^2 );

    end

    function cl_bode_plot(K, a, b)

        % Frequency range, on a log scale (i.e., range of exponents of 10)
        logw = linspace(0,2,1000);

        % Open-loop parameters are K (gain), a and b (poles)

        % Find real and imaginary parts of CL poles and damping ratio
        [sigma, dampfreq, zeta] = second_order_closed_loop( K, a, b );

        % Plot the closed-loop bode
        % (note: The elegant MATLAB way to do this would be to use complex
        %        numbers, the abs function, and a loglog plot. However,
        %        I'm trying to generate a nice prototype for pst-plot.
        %        Plus, doing things "raw" like this might be a good
        %        example.)
        plot( logw, ...
              20*log10( K*a*b./sqrt( ((1+K)*a*b - (10.^logw).^2).^2 + (a+b)^2*(10.^logw).^2 ) ), ...
              '-', ...
              logw, 0*logw, '--' );
        ylim( [-20 15] );
        grid on;

        if( length(zeta) > 0 )
            legstr = [ '$-\sigma = ', num2str(-sigma(1)), '$', ...
                       '\\$j\omega_d = j', num2str(dampfreq(1)), '$', ...
                       '\\$\zeta = ', num2str(zeta(1), 3) , '$' ];
        else

            if( a < b )
                legstr = [ '$-\sigma_a = ', num2str( -sigma(1) ), '$', ...
                           '\\$-\sigma_b = ', num2str( -sigma(2) ), '$' ];
            else
                legstr = [ '$-\sigma_a = ', num2str( -sigma(2) ), '$', ...
                           '\\$-\sigma_b = ', num2str( -sigma(1) ), '$' ];
            end

        end

        legstr = [ legstr, ...
                   '\\$e_{SS} = ', num2str(1/(1+K)), '$' ];

        title( [ '$K = ' num2str(K) '$, ' ...
                 '$a = ' num2str(a) '$, ' ...
                 '$b = ' num2str(b) '$' ], ...
               'Interpreter', 'latex', 'FontSize', 14 );

        xlabel( '$\log_{10}(\omega/(1$\,rad$/$s$))$', ...
                'Interpreter', 'latex', 'FontSize', 12 );
        ylabel( '$|G_{CL}|$ (dB)', ...
                'Interpreter', 'latex', 'FontSize', 12 );

        lfig = legend( '', 'Location', 'NorthWest' );
        set( lfig, 'Interpreter', 'latex', ...
                   'FontSize', 12, ...
                   'String', [ '\shortstack{' legstr '}' ] );

    end

    function cl_impulse_response(K, a, b, Tmax)

        if( nargin < 4 )
            Tmax = 2;
        end

        % Time range
        t = linspace(0, Tmax, 2048);

        % Open-loop parameters are K (gain), a and b (poles)

        % Find real and imaginary parts of CL poles and damping ratio
        [sigma, dampfreq, zeta] = second_order_closed_loop( K, a, b );

        % Plot the closed-loop bode
        % (note: The elegant MATLAB way to do this would be to use the
        %        toolboxes (e.g., the impulse function).)
        plot( t, ...
              K/(1+K) * ...
              (dampfreq^2+sigma^2) / dampfreq * ...
              exp(-sigma*t) .* ...
              sin(dampfreq*t), ...
              '-', ...
              t, 0*t, '--' );
        ylim( [-10 20] );
        grid on;

        if( length(zeta) > 0 )
            legstr = [ '$-\sigma = ', num2str(-sigma(1)), '$', ...
                       '\\$j\omega_d = j', num2str(dampfreq(1)), '$', ...
                       '\\$\zeta = ', num2str(zeta(1), 3) , '$' ];
        else

            if( a < b )
                legstr = [ '$-\sigma_a = ', num2str( -sigma(1) ), '$', ...
                           '\\$-\sigma_b = ', num2str( -sigma(2) ), '$' ];
            else
                legstr = [ '$-\sigma_a = ', num2str( -sigma(2) ), '$', ...
                           '\\$-\sigma_b = ', num2str( -sigma(1) ), '$' ];
            end

        end

        legstr = [ legstr, ...
                   '\\$e_{SS} = ', num2str(1/(1+K)), '$' ];

        title( [ '$K = ' num2str(K) '$, ' ...
                 '$a = ' num2str(a) '$, ' ...
                 '$b = ' num2str(b) '$' ], ...
               'Interpreter', 'latex', 'FontSize', 14 );

        xlabel( 'Time (s)', ...
                'Interpreter', 'latex', 'FontSize', 12 );
        ylabel( 'Impulse response (e.g., velocity profile)', ...
                'Interpreter', 'latex', 'FontSize', 12 );

        lfig = legend( '', 'Location', 'NorthEast' );
        set( lfig, 'Interpreter', 'latex', ...
                   'FontSize', 12, ...
                   'String', [ '\shortstack{' legstr '}' ] );

    end

    function cl_step_response(K, a, b, Tmax)

        if( nargin < 4 )
            Tmax = 2;
        end

        % Time range
        t = linspace(0, Tmax, 2048);

        % Open-loop parameters are K (gain), a and b (poles)

        % Find real and imaginary parts of CL poles and damping ratio
        [sigma, dampfreq, zeta] = second_order_closed_loop( K, a, b );

        % Plot the closed-loop bode
        % (note: The elegant MATLAB way to do this would be to use the
        %        toolboxes (e.g., the impulse function).)
        plot( t, ...
              K/(1+K) * ...
                 ( 1 - ...
                       ( cos(dampfreq*t) - ...
                         (sigma/dampfreq)*sin(dampfreq*t) ) .* ...
                       exp(-sigma*t) ), '-', ...
              t, K/(1+K)*ones(size(t)), '--', ...
              t, ones(size(t)), '-.' );
        ylim( [0 2] );
        grid on;

        if( length(zeta) > 0 )
            legstr = [ '$-\sigma = ', num2str(-sigma(1)), '$', ...
                       '\\$j\omega_d = j', num2str(dampfreq(1)), '$', ...
                       '\\$\zeta = ', num2str(zeta(1), 3) , '$' ];
        else

            if( a < b )
                legstr = [ '$-\sigma_a = ', num2str( -sigma(1) ), '$', ...
                           '\\$-\sigma_b = ', num2str( -sigma(2) ), '$' ];
            else
                legstr = [ '$-\sigma_a = ', num2str( -sigma(2) ), '$', ...
                           '\\$-\sigma_b = ', num2str( -sigma(1) ), '$' ];
            end

        end

        legstr = [ legstr, ...
                   '\\$e_{SS} = ', num2str(1/(1+K)), '$' ];

        title( [ '$K = ' num2str(K) '$, ' ...
                 '$a = ' num2str(a) '$, ' ...
                 '$b = ' num2str(b) '$' ], ...
               'Interpreter', 'latex', 'FontSize', 14 );

        xlabel( 'Time (s)', ...
                'Interpreter', 'latex', 'FontSize', 12 );
        ylabel( 'Impulse response (e.g., velocity profile)', ...
                'Interpreter', 'latex', 'FontSize', 12 );

        lfig = legend( '', 'Location', 'NorthEast' );
        set( lfig, 'Interpreter', 'latex', ...
                   'FontSize', 12, ...
                   'String', [ '\shortstack{' legstr '}' ] );

    end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%% End of Script %%%%%%%%%%%%%%%%%%%%%%%%%%%
