classdef StaticModel < InputOutputModel
   % Static Model objects.
   %
   %   Static models are input/output models that generalize the notions of matrix
   %   and numeric array. Their main purpose is to create parametric expressions
   %   that can be used to construct parametric models of dynamic systems or tunable
   %   components of control systems. The main categories of static models are:
   %     * Real parameter (REALP): this is the building block for creating
   %       parametric expressions
   %     * Generalized matrix (GENMAT), which arises when combining real parameters
   %       with each other or with numerical values.
   %
   %   See also REALP, GENMAT, CONTROLDESIGNBLOCK, INPUTOUTPUTMODEL.
   
   %   Author(s): P. Gahinet
   %   Copyright 1986-2011 The MathWorks, Inc.
   
   % PUBLIC METHODS
   methods
      
      function varargout = connect(varargin) %#ok<STOUT>
         %CONNECT  Block-diagram interconnections of dynamic systems.
         %
         %   See also DYNAMICSYSTEM/CONNECT.
         
         % No input argument should be static (static models do not have I/O names)
         error(message('Control:lftmodel:connect1'))
      end
      
      function M = genmat(M)
         %GENMAT  Converts static model to generalized matrix type.
         %
         %   M = GENMAT(M) converts the static model M to a generalized matrix.
         %
         %   See also GENMAT, STATICMODEL.
         try
            M = copyMetaData(M,genmat_(M));
         catch E
            throw(E)
         end
      end
      
      function M = umat(M)
         %UMAT  Converts static model to uncertain matrix.
         %
         %   UM = UMAT(M) converts the static model M to an uncertain matrix.
         %   All blocks in M that do not represent uncertainty are replaced
         %   by their current value and UM contains only uncertain blocks.
         %
         %   See also UMAT, UNCERTAINBLOCK, STATICMODEL, GENMAT.
         try
            M = copyMetaData(M,umat_(M));
         catch E
            throw(E)
         end
      end
      
   end
   
   
   %% DATA ABSTRACTION INTERFACE
   methods (Access = protected)
      
      %% TRANSFORMATIONS
      function D = getValue_(M)
         % Returns current value
         D = double(M);
      end
      
   end
   
   methods (Hidden)
      
      function sys = iorep(sys,s)
         % Standardized I/O replication function for I/O models
         sys = repmat(sys,s);
      end
      
   end
   
   %% Utility
   methods(Static, Access = protected)
      function sys = utConstructPID(Kp,Ki,Kd,Tf,Ts,IF,DF,Options)
         % Construct the tunable PID using basic interconnection functions
         sys = ss(Kp,'Ts',Ts);
         if ~isequal(Ki,[])
            sys = sys + Ki * localGetPIDFormula(IF,Ts);
         end
         if ~isequal(Kd,[])
            Tfval = double(Tf);
            if any(Tfval<0)
               error(message('Control:ltiobject:pidSet2','Tf'))
            elseif any(Tfval==0)
               % No improper tunable or uncertain PID
               error(message('Control:lftmodel:pid4'))
            end
            sys = sys + Kd * feedback(1/Tf,localGetPIDFormula(DF,Ts));
         end
         
         % Process additional settings and validate system
         if ~isempty(Options)
            sys = set(sys,Options{:});
         end
      end
      function value = utCheckPIDGain(name,value)
         % Validates each PID input
         s = size(value);
         if isnumeric(value)
            if ~(isreal(value) && allfinite(value))
               error(message('Control:ltiobject:pidSet1',name))
            end
            if any(s(1:2)~=1)
               % Array of numeric gains
               value = reshape(value,[1 1 s]);
            end
            % Return [] for Ki and Kd when all zero
            if any(strcmp(name,{'Ki','Kd'})) && ~any(value(:))
               value = [];
            end
         elseif isa(value,'StaticModel')
            if any(s(1:2)~=1)
               error(message('Control:lftmodel:pid2',name))
            end
         else
            error(message('Control:lftmodel:pid1'))
         end
      end
      
      function [DataInputs,Ts,IF,DF,Options] = utProcessPIDInputs(type,varargin)
         ni = nargin-1;
         % Dissect input list
         PVStart = min([ni+1 find(cellfun(@(x) (ischar(x) || isStringScalar(x)),varargin),1)]);
         DataInputs = PVStart-1;
         % Sample time
         switch type
            case '1dof'
               idTs = 5;
            case '2dof'
               idTs = 7;
         end
         if DataInputs==idTs
            Ts = ltipack.utValidateTs(varargin{idTs});
         else
            Ts = 0;
         end
         % Discrete PID formula depends on Ts, IFormula, DFormula. Let users
         % specify these settings as P/V pairs and use dummy PID to resolve them
         Options = varargin(PVStart:ni);
         idx = find(~cellfun('isempty',...
            regexpi(Options(1:2:end),'(^IF)?(^DF)?(^Ts)?')));
         idx = [2*idx-1 ; 2*idx];
         idx = idx(idx<=numel(Options));  % watch for ill-formed P/V pairs
         dummy = pid(1,0,0,0,Ts,Options{idx(:)});
         Ts = dummy.Ts;
         IF = dummy.IF;
         DF = dummy.DF;
         Options(idx) = [];
      end
   end
   
end

function T = localGetPIDFormula(Formula,Ts)
% Returns state-space model of integration formula
if Ts==0
   T = ss(0,1,1,0);
else
   tau = sqrt(Ts);
   switch Formula(1)
      case 'F'
         T = ss(1,tau,tau,0,Ts);
      case 'B'
         T = ss(1,tau,tau,Ts,Ts);
      case 'T'
         T = ss(1,tau,tau,Ts/2,Ts);
   end
end
end