classdef (Hidden = true) standardPID
    % Class definition for @standardPID (pid object data)
    
    %   Author(s): B. Singh
    %   Copyright 2015 The MathWorks, Inc.
    
    % Formula: Kp*(1 + 1/Ti*(1/s) + Td*s/(Td/N*s+1)) or Kp*(1 + 1/Ti*(1/s) + Td/(Td/N+1/s))
    % Discretization means replace integrators above with discretizers
    %   for 'ForwardEuler':     1/s --> Ts/(z-1)
    %   for 'BackwardEuler':    1/s --> Ts*z/(z-1)
    %   for 'Trapezoidal':      1/s --> Ts*(z+1)/2/(z-1)
    % Delay structure is initialized as 0 and remains 0
    
    properties
        % 1DOF PID in Standard form
        Kp      % proportional gain (real, finite, scalar)
        Ti      % integral time (real, non-zero, scalar)
        Td      % derivative time (real, finite, scalar)
        N       % filter divisor (real, non-zero, scalar)
    end
    
    %% Public methods
    methods
        
        function [P,I,D,T] = utGetInternalParams(this)
            % abstract method converting PID to internal parameterization
            P = this.Kp;
            I = this.Kp/this.Ti;
            D = this.Kp*this.Td;
            T = this.Td/this.N;
        end
        
        function D = scaleTime(D,sf)
            % Time vector scaling tnew = sf * told
            D.Ti = sf * D.Ti;
            D.Td = sf * D.Td;
        end
        
        %% Transformation
        function PID = uminus(PID)
            % Computes -PID.
            PID.Kp = -PID.Kp;
        end
        
        function PID = minreal(PID,tol)
            % Pole/zero cancellations
            % (1) cancellation at s=0 or z=1 when Ti is very large
            % (2) cancellation at s=-N/Td or z=(Td-m*N)/(Td+n*N) when N is very small (m={Ts,0,Ts/2},n={0,Ts,Ts/2} for FE,BE and TR)
            % the reduced system can always be represented by PIDSTD
            % Compute reduced model in zpk format
            rsys = minreal(zpk(PID),tol);
            % Convert back to PIDSTD.
            PID = pidstd(rsys,PID);
        end
                
    end
    
    methods(Static)
        function [Kp,Ti,Td,N] = utChangeIFDF(Kp,Ti,Td,N,Ts,fromIF,fromDF,toIF,toDF)
            % Note: Kp/Ti and Kp*Td are constant.
            % Compute Kp for new IFormula (via Kp for IF='F')
            TdN = Td/N;
            Kp0 = Kp;
            if Kp~=0
                KpTi = Kp/Ti;
                KpTd = Kp*Td;
                switch fromIF
                    case 'F'
                        % do nothing
                    case 'B'
                        Kp = Kp + KpTi * Ts;
                    case 'T'
                        Kp = Kp + KpTi * Ts/2;
                end
                switch toIF
                    case 'B'
                        Kp = Kp - KpTi * Ts;
                    case 'T'
                        Kp = Kp - KpTi * Ts/2;
                end
                % when Kp is 0, no need to update Kp, Ti and Td
                if Kp*Kp0<0
                    % fail to generate positive Ti and Td
                    ctrlMsgUtils.error('Control:transformation:pid2')
                else
                    % update Kp, Ti and Td
                    Ti = Kp/KpTi;
                    Td = KpTd/Kp;
                end
            end
            % Compute N for new DFormula (via Td/N for DF='F')
            if Td~=0
                switch fromDF
                    case 'B'
                        TdN = TdN + Ts;
                    case 'T'
                        TdN = TdN + Ts/2;
                end
                switch toDF
                    case 'B'
                        TdN = TdN - Ts;
                    case 'T'
                        TdN = TdN - Ts/2;
                end
                if TdN<0
                    ctrlMsgUtils.error('Control:transformation:pid2')
                end
                N = Td/TdN;
            end
        end
    end
    
end
