classdef RespConfig
   %RESPCONFIG  Step or impulse response configuration.
   %
   %   STEP applies step changes of the form
   %      u(t) = U + (t>=T0+TD) * DU
   %   where U is the baseline input (input offset), DU is the step change,
   %   T0 is the start time, and T0+TD is when the step change occurs.
   %   Similarly, IMPULSE applies impulse changes of the form 
   %      u(t) = U + DU * delta(t-(T0+TD))
   %   where delta(t) is the Dirac impulse. For multi-input systems, U and 
   %   DU are vectors and U is changed one entry at a time: First compute
   %   the response to a DU(1) change in the first input channel, then
   %   compute the response to a DU(2) change in the second input channel, 
   %   etc.
   %
   %   CONFIG = RESPCONFIG() returns the default configuration (U=0,DU=1,TD=0).
   %
   %   CONFIG = RESPCONFIG(Param1=Value1,Param2=Value2,...) overrides the
   %   default values by setting one or more of the following parameters:
   %
   %     InputOffset    Baseline input U (default = 0).
   %                    To specify a constant baseline U, set InputOffset to 
   %                    a scalar or vector value. For state-space models, 
   %                    set InputOffset='u0' to use the model offset u0, 
   %                    u0(t), or u0(t,p). The step change is then relative 
   %                    to u0.
   %
   %     Amplitude      Amplitude DU (default = 1).
   %                    Specifies the amplitude DU of the input level change
   %                    relative to U.
   %
   %     Delay          Step or impulse delay TD (default = 0).
   %                    Specifies when the step or impulse change occurs 
   %                    relative to the simulation start time T0. TD is  
   %                    nonnegative and expressed in the system time units.
   %
   %     InitialState   Initial state (default = [])
   %                    By default, simulation starts from the steady-state
   %                    condition with constant input U. For state-space
   %                    models, set InitialState to a value XINIT to start 
   %                    from a different initial state. For state-space models,
   %                    set InitialState='x0' to start from the model offset
   %                    x0, x0(t) or x0(t,p) at t=T0. For identified models, 
   %                    you can use an @initialCondition object to specify 
   %                    the initial state.
   %                    
   %     InitialParameter   Initial parameter (default = [])
   %                    Specifies initial value of p(t) for LPV models.  
   %                    This is only required when InputOffset='u0' or 
   %                    InitialState=[],'x0' and is ignored otherwise.
   %
   %   Example: Simulate the response to a step change of amplitude 2 at 
   %   time t=0.5 with initial state [0;-1]:
   %     sys = rss(2);
   %     Config = RespConfig(Amplitude=2,Delay=0.5,InitialState=[0;-1])
   %     [y,t] = step(sys,Config)
   %
   %   See also STEP, IMPULSE.

   %   Copyright 2022 The MathWorks, Inc.

   properties
      % Input offset (default=0).
      InputOffset = 0;

      % Amplitude (default=1).
      Amplitude = 1;

      % Delay (default = 0).
      Delay = 0;

      % Initial state (default=[])
      InitialState = [];

      % Initial parameter (default=[])
      InitialParameter = [];
   end

   properties (Dependent,Hidden)
      % Same as Amplitude
      StepAmplitude
   end

   methods
      % Constructor
      function this = RespConfig(propArgs)
         arguments
            propArgs.?RespConfig
         end
         try
            F = fieldnames(propArgs);
            for ct=1:numel(F)
               this.(F{ct}) = propArgs.(F{ct});
            end
         catch ME
            throw(ME)
         end
      end

      function this = set.InputOffset(this,Value)
         if isnumeric(Value) && isvector(Value) && allfinite(Value)
            this.InputOffset = double(Value(:));
         elseif strcmpi(Value,'u0')
            this.InputOffset = 'u0';
         else
            error(message('Control:analysis:RespConfig1'))
         end
      end

      function this = set.Amplitude(this,Value)
         if isnumeric(Value) && isvector(Value) && allfinite(Value)
            this.Amplitude = double(Value(:));
         else
            error(message('Control:analysis:RespConfig2'))
         end
      end

      function this = set.Delay(this,Value)
         if isnumeric(Value) && isreal(Value) && isscalar(Value) && Value>=0 && Value<Inf
            this.Delay = double(Value);
         else
            error(message('Control:analysis:RespConfig3'))
         end
      end

      function this = set.InitialState(this,Value)
         if isempty(Value)
            this.InitialState = [];
         elseif isnumeric(Value)
            if isvector(Value) && allfinite(Value)
               this.InitialState = double(Value(:));
            else
               error(message('Control:analysis:RespConfig4'))
            end
         elseif strcmpi(Value,'x0')
            this.InitialState = 'x0';
         elseif isa(Value,'initialCondition') && isscalar(Value)
            this.InitialState = Value;
         else
            error(message('Control:analysis:RespConfig4'))
         end
      end

      function this = set.InitialParameter(this,Value)
         if isempty(Value)
            this.InitialParameter = [];
         elseif isnumeric(Value) && isreal(Value) && isvector(Value) && allfinite(Value)
            this.InitialParameter = double(Value(:));
         else
            error(message('Control:analysis:RespConfig5'))
         end
      end

      function Value = get.StepAmplitude(this)
         Value = this.Amplitude;
      end
      function this = set.StepAmplitude(this,Value)
         this.Amplitude = Value;
      end

   end


   methods (Hidden)

      function this = validate(this,nu)
         u = this.InputOffset;
         du = this.Amplitude;
         if isnumeric(u)
            if isscalar(u)
               this.InputOffset = repmat(u,[nu,1]);
            elseif numel(u)~=nu
               error(message('Control:analysis:step1','InputOffset'))
            end
         end
         if isscalar(du)
            this.Amplitude = repmat(du,[nu,1]);
         elseif numel(du)~=nu
            error(message('Control:analysis:step1','Amplitude'))
         end
      end

      function SimInfo = getSignalParam(this,SimInfo)
         SimInfo.uinit = this.InputOffset;
         SimInfo.du = this.Amplitude;
         SimInfo.Delay = this.Delay;
      end

   end

end

