classdef (Abstract,StrictDefaults) idRecursiveEstimator < matlab.System
    %

    %   Copyright 2015-2021 The MathWorks, Inc.
    
    %#codegen
    
    properties(Dependent,SetAccess=protected)
        ParameterCovariance;
    end
    
    properties(Dependent)
        %InitialParameterCovariance Covariance of the initial parameter estimates
        %   This property is used only when the EstimationMethod is
        %   'ForgettingFactor' or 'KalmanFilter'
        %
        %   InitialParameterCovariance is the covariance of the initial
        %   parameter estimates. In other words, it represents the amount
        %   of uncertainty in the initial parameter estimates you
        %   (optionally) provide.  If you specify a large value, the
        %   estimator will place less importance on the initial parameter
        %   values and more importance on the measured signals at the
        %   beginning of the estimation.
        %
        %   It can be specified as: (1) non-negative real scalar, (2)
        %   N-vector of real nonnegative scalars, (3) N-by-N positive-semi
        %   definite matrix. N is the number of estimated parameters. The
        %   default value is 1e4.
        InitialParameterCovariance;
    end
    
    properties(Dependent,Nontunable)        
        %EstimationMethod Estimation method used for parameter estimation
        %   There are four options: 'ForgettingFactor' (default),
        %   'KalmanFilter', 'NormalizedGradient' and 'Gradient'.
        %
        %   Forgetting factor algorithm requires you to specify the
        %   ForgettingFactor property, which represents the measurement
        %   window relevant for parameter estimation. In other words, this
        %   is how fast the old measurements are discounted from
        %   estimation. This is closely related to how fast the parameters
        %   are varying. See the help for the ForgettingFactor property for
        %   further details.
        %
        %   Kalman filter algorithm treats the estimated parameters as the
        %   states of a dynamical system and uses a Kalman filter to
        %   estimates these states. In this case you specify
        %   ProcessNoiseCovariance that is the covariance of the process
        %   noise acting on these states (parameters). In other words, it
        %   captures how much the parameters can vary in one time-step in a
        %   probabilistic sense.
        %
        %   Gradient and normalized gradient methods are also known as the
        %   least mean squares (LMS) methods. These have lower memory
        %   requirements compared to the forgetting factor and Kalman
        %   filter algorithms, but typically have slower convergence
        %   properties. These algorithms require you to specify
        %   AdaptationGain, which is typically set based on the relative
        %   information content in the measurements. See documentation for
        %   further details and the differences between these algorithms.
        EstimationMethod;
    end
    
    properties(Dependent)
        %ForgettingFactor Measurement window relevant for parameter estimation
        %   This property is used only when the EstimationMethod is
        %   'ForgettingFactor'. It is a scalar in the (0 1] range and
        %   represents how fast the estimator should discount old
        %   measurements. If the parameters are approximately constant over
        %   T0 samples, you can choose it as 1-(1/T0). Setting it to 1
        %   corresponds to "no forgetting" and estimating constant
        %   coefficients. Setting it less than 1 implies that past
        %   measurements are less significant for parameter estimation and
        %   can be "forgotten". Set it less than 1 to estimate time-varying
        %   coefficients. Typical choices are in the [0.98 0.995] range.
        ForgettingFactor;
                
        %ProcessNoiseCovariance Covariance of parameter variations
        %   This property is only used when the 'EstimationMethod' is
        %   'KalmanFilter'.
        %
        %   Kalman filter algorithm treats the estimated parameters as the
        %   states of a dynamical system and uses a Kalman filter to
        %   estimates these states. ProcessNoiseCovariance is the
        %   covariance of the process noise acting on these states
        %   (parameters). In other words, it captures how much the
        %   parameters can vary in one time-step in a probabilistic sense.
        %   Set it to larger values when the parameters are changing
        %   rapidly. Note that the trade-off is more noisy parameter
        %   estimates.
        %
        %   ProcessNoiseCovariance can be specified as: (1) non-negative
        %   real scalar, (2) N-vector of real nonnegative scalars, (3)
        %   N-by-N positive-semi definite matrix. N is the number of
        %   estimated parameters. The default value is 0.1.
        ProcessNoiseCovariance;
        
        %AdaptationGain Adaptation gain for parameter estimation
        %   This property is used only when the EstimationMethod is
        %   'Gradient' or 'NormalizedGradient'. It is a positive scalar and
        %   represents the relative information content in the measurements.
        %   That is, when your measurements are trustworthy, specify a large
        %   value for AdaptationGain. The default value is 1.
        AdaptationGain;        
        
        %NormalizationBias Bias on gain scaling of the NormalizedGradient method
        %
        %   This property is used only when the EstimationMethod is
        %   'NormalizedGradient'. This algorithm divides the adaptation gain
        %   at each step by the square of the 2-norm of the regressor
        %   vector. This can cause jumps in the estimated parameters if the
        %   regressors are close to zero. Increase this value if you
        %   observe such jumps in estimated parameters.
        %
        %   It is a non-negative scalar. The default value is eps.
        NormalizationBias;        
    end
    
    properties(Access=protected)
        % Internal storage of the public, tunable dependent properties
        ForgettingFactor_;
        ProcessNoiseCovariance_;
        AdaptationGain_;
        NormalizationBias_;
        InitialParameters_;
        InitialParameterCovariance_;       

        % Estimator_ - Object responsible for handling parameter estimation
        Estimator_;
    end
    
    properties
        %EnableAdaptation Enable or disable parameter estimation
        %   When EnableAdaptation is true() (default), the parameter
        %   estimated are updated at each time step. When it is false(), the
        %   estimator outputs the last estimated parameter values. Use this
        %   option, for example, to disable parameter estimation when the
        %   system enters a mode where the parameter values do not vary
        %   with time.
        EnableAdaptation (1, 1) logical;
    end
    
    
    properties(Nontunable,Dependent,SetAccess=protected)
        %History Set if the estimator uses all past data or a finite window
        %   There are two options: 'Infinite' (default), 'Finite'
        %
        %   'Infinite' means that the parameter estimator potentially uses 
        %   data from an infinite number of past time steps.
        %
        %   'Finite' means that data from a finite number of past time
        %   steps are used. See the WindowLength property to adjust how
        %   many past time steps of data can contribute to parameter
        %   estimates.
        %
        %   This property can only be set during object construction, via
        %   Name-Value pairs.
        History
        
        %WindowLength Adjust how many past time steps of data are used for
        %             parameter estimates
        %   This property is used only when the History property is set to
        %   'Finite'.
        %
        %   Specify as a positive integer scalar. WindowLength must be
        %   greater than the number of estimated parameters.
        %
        %   This property can only be set during object construction, via
        %   Name-Value pairs.        
        WindowLength
        
        %InputProcessing Set if data are stored in frames
        %   There are two options: 'Sample-based' (default), 'Frame-based'
        %
        %   If each piece of data provided to the estimator contains
        %   information for a single time-step, use 'Sample-based'.
        %
        %   If each piece of data provided to the estimator contains
        %   information from multiple time-steps, use 'Frame-based'.
        %
        %   This property can only be set during object construction, via
        %   Name-Value pairs.
        InputProcessing
    end
    
    properties(Nontunable,SetAccess=protected)
        %DataType The floating-point data type used by the estimator
        %   There are two options: 'double' (default) and 'single'.
        %
        %   This is the data type used by the estimator internally for
        %   floating-point variables. Single-precision floating-point
        %   variables use less memory compared to double precision, but
        %   sacrifice numerical accuracy. The most typical use case for
        %   this property is when you want to deploy the estimator on a
        %   processor that does not support double precision variables.
        %
        %   This property can only be specified when you are initializing
        %   the estimator and it cannot be changed later.
        DataType = 'double';
    end
    
    properties(Nontunable,Access=protected)
        EstimationMethod_ = uint8(1); % 'ForgettingFactor'
        AlgorithmParameters_;
        IsUsingFrames_;
        IsHistoryFinite_;
        WindowLength_;
    end
        
    properties(DiscreteState,Hidden,SetAccess=protected)
        % Subclasses, such as recursiveARX, initialize these two discrete
        % states in their resetImpl() methods
        Parameters_; %#ok<SOINITPROP> 
        ParameterCovariance_; %#ok<SOINITPROP>
    end
    
    properties(Constant,Access=protected)
        % Default values of the optional, tunable NV pairs
        cDefaultForgettingFactor = 1;
        cDefaultAdaptationGain = 1;
        cDefaultProcessNoiseCovariance = 0.1;
        cDefaultNormalizationBias = eps;
        cDefaultInitialParameterCovariance = 1e4;
        cDefaultEnableAdaptation = true();
        % Defaults for nontunable properties
        cDefaultInputProcessing = 'Sample-based';
        cDefaultHistory = 'Infinite';
        cDefaultWindowLength = 200;
        % Internal constants
        cVersion = uint8(1);
    end
    
    methods(Abstract,Access=protected)
        [nontunableNVPairs,tunableNVPairs] = getNameValuePairOptionsSimulation(this,varargin);
        nontunableNVPairs = getNameValuePairOptionsCodegenNontunable(this,varargin);
        tunableNVPairs = getNameValuePairOptionsCodegenTunable(this,varargin);
        
        setNumberOfParametersImpl(this,orders);
        setInitialParameters(this,tunableNVPairs,firstNVIndex,varargin);
        setInitialIOBuffers(this,tunableNVPairs);
        orders = defaultOrders(this);
        setEstimator(this);
        val = latestPossibleNVPairPosition(this);
        val = getNumberOfParameters(this);
    end
    
    methods
        function this = idRecursiveEstimator(varargin)
            % Constructor
            
            % Find the position of the first name-value pair
            firstNVIndex = idRecursiveEstimator.findFirstNVPair(varargin{:});
            latestPossibleNVPairPosition = this.latestPossibleNVPairPosition;
            coder.internal.errorIf(firstNVIndex>latestPossibleNVPairPosition,...
                'Ident:simulink:recursiveEstimationInvalidArgumentsInConstructor',0,latestPossibleNVPairPosition-1);
            
            % Must do the initialization in the following order:
            % 1) DataType: Sets other properties' data type
            % 2) IsUsingFrames
            % 3) History, then WindowLength (only used if History='Finite')
            % 4) NumberOfParameters: Sets other properties' dimensions
            % 5) Estimator (finite versus infinite history)
            % 6) Initialize parameters and IO buffers
            % 7) Optional NV pairs
            if isempty(coder.target)
                [nontunableData,tunableData] = ...
                    getNameValuePairOptionsSimulation(this,varargin{firstNVIndex:end});
            else
                nontunableData = getNameValuePairOptionsCodegenNontunable(this,varargin{firstNVIndex:end});
                tunableData = getNameValuePairOptionsCodegenTunable(this,varargin{firstNVIndex:end});
            end
            % 1)
            if ~isempty(nontunableData.DataType) % otherwise the default value is taken from the property declaration
                this.DataType = char(nontunableData.DataType); % ensure char (not string)
            end
            % 2)
            if ~isempty(nontunableData.InputProcessing)
                this.InputProcessing = nontunableData.InputProcessing;
            else
                this.InputProcessing = this.cDefaultInputProcessing;
            end
            % 3)
            if ~isempty(nontunableData.History)
                this.History = nontunableData.History;
            else
                this.History = this.cDefaultHistory;
            end
            % 4)
            if firstNVIndex>1
                setNumberOfParameters(this,varargin{1});
            else
                setNumberOfParameters(this,this.defaultOrders);
            end            
            % Set WindowLength, which is used only when History='Finite'
            if this.IsHistoryFinite_
                if ~isempty(nontunableData.WindowLength)
                    this.WindowLength = nontunableData.WindowLength;
                else
                    this.WindowLength = this.cDefaultWindowLength;
                end
            end
            % 5)
            setEstimator(this);
            % 6)
            setInitialParameters(this, tunableData,firstNVIndex,varargin{:});
            setInitialIOBuffers(this, tunableData);
            % 7) Optional NV pairs
            %
            % Set nontunable properties here (instead of in
            % setCommonOptionalNameValuePairs, g1784708). Set these only if
            % they are not empty. This allows users to change them after
            % object construction (for code generation, especially).
            if ~isempty(nontunableData.EstimationMethod)
                this.EstimationMethod = nontunableData.EstimationMethod;
            end            
            % Tunable NV pairs
            setCommonOptionalNameValuePairs(this, tunableData);
        end
    end
    
    methods(Access = protected)
        function setNumberOfParameters(this,orders)
            % model type specific implementation by subclasses
            setNumberOfParametersImpl(this,orders);
            this.InitialParameters_ = zeros(getNumberOfParameters(this),1,this.DataType);
        end
        
        function validateInputsImpl(this,y,u)
            % Validate the inputs of the estimator: u and y. Check data
            % types. Subclasses check the dimensions by calling this method
            % and extending it. Unlike the Simulink case we don't
            % need to check the sample time.
            
            % y must be either single or double. It must also match with
            % the DataType property.
            dataType = this.DataType;
            coder.internal.assert(isa(y,'double') || isa(y,'single'),...
                'Ident:simulink:recursiveEstimationYUnsupportedDataType',class(y));
            coder.internal.assert(isa(y,dataType),...
                'Ident:simulink:recursiveEstimationInputAndPropertyDataTypeMismatch',...
                class(y),dataType);

            if nargin>2
                % I/O models, not time-series, need to validate signal u
                coder.internal.assert(isa(u,'double') || isa(u,'single'),...
                    'Ident:simulink:recursiveEstimationUUnsupportedDataType',class(u));
                % the classes of u and y must match
                coder.internal.assert(isa(u,class(y)),...
                    'Ident:simulink:recursiveEstimationInputDataTypeMismatch',...
                    class(u),class(y));
            end
        end
        
        function isInactive = isInactivePropertyImpl(this,propName)
            isInactive = false();
            switch propName
                case {'ParameterCovariance', 'InitialParameterCovariance'}
                    if ~isEstimationMethodForgettingFactor(this) && ...
                            ~isEstimationMethodKalmanFilter(this)
                        isInactive = true();
                    end
                case 'ForgettingFactor'
                    if this.IsHistoryFinite_ || ~isEstimationMethodForgettingFactor(this)
                        isInactive = true();
                    end
                case 'ProcessNoiseCovariance'
                    if this.IsHistoryFinite_ || ~isEstimationMethodKalmanFilter(this)
                        isInactive = true();
                    end
                case 'AdaptationGain'
                    if this.IsHistoryFinite_ || ~isEstimationMethodNormalizedGradient(this) && ...
                            ~isEstimationMethodGradient(this)
                        isInactive = true();
                    end
                case 'NormalizationBias'
                    if this.IsHistoryFinite_ || ~isEstimationMethodNormalizedGradient(this)
                        isInactive = true();
                    end
                case 'EstimationMethod'
                    if this.IsHistoryFinite_
                        isInactive = true();
                    end
                case 'WindowLength'
                    if ~this.IsHistoryFinite_
                        isInactive = true();
                    end
            end
        end
        
        function s = saveObjectImpl(this)
            % Save the private properties, protected properties and states
            % when saving the system object
            
            % Call the base class method
            s = saveObjectImpl@matlab.System(this);
            
            % Save the protected & private properties
            s.ForgettingFactor_ = this.ForgettingFactor_;
            s.ProcessNoiseCovariance_ = this.ProcessNoiseCovariance_;
            s.AdaptationGain_ = this.AdaptationGain_;
            s.NormalizationBias_ = this.NormalizationBias_;
            s.InitialParameters_ = this.InitialParameters_;
            s.InitialParameterCovariance_ = this.InitialParameterCovariance_;
            s.AlgorithmParameters_ = this.AlgorithmParameters_;
            
            s.EstimationMethod_ = this.EstimationMethod_;
            s.IsUsingFrames_ = this.IsUsingFrames_; % InputProcessing
            s.IsHistoryFinite_ = this.IsHistoryFinite_; % History
            s.WindowLength_ = this.WindowLength_;
            s.Estimator_ = this.Estimator_;
            s.DataType = this.DataType;
            
            s.cVersion = this.cVersion;
            
            % Save the state only if object locked
            if isLocked(this)
                s.Parameters_ = this.Parameters_;
                s.ParameterCovariance_ = this.ParameterCovariance_;
            end
        end
        
        function loadObjectImpl(this,s,wasLocked)
            % Load the private properties, protected properties and states
            % when loading the system object
                        
            % Non-tunable properties that are never empty: DataType,
            % History, InputProcessing. Users cannot change these after
            % load (or object construction)
            this.DataType = s.DataType;
            this.IsHistoryFinite_ = s.IsHistoryFinite_;
            this.IsUsingFrames_ = s.IsUsingFrames_;
            
            % Non-tunable properties that can be empty: Don't set them
            % unless needed. Scripts can change these after load and still
            % support codegen
            if ~isempty(s.WindowLength_) 
                this.WindowLength_ = s.WindowLength_;
            end                     
            if ~isempty(s.EstimationMethod_) 
                this.EstimationMethod_ = s.EstimationMethod_;
            end
            if ~isempty(s.Estimator_)
                this.Estimator_ = s.Estimator_;
            end            
            if ~isempty(s.AlgorithmParameters_)
                this.AlgorithmParameters_ = s.AlgorithmParameters_;
            end
            
            % Tunable and (protected or private) properties
            this.ForgettingFactor_ = s.ForgettingFactor_;
            this.ProcessNoiseCovariance_ = s.ProcessNoiseCovariance_;
            this.AdaptationGain_ = s.AdaptationGain_;
            this.NormalizationBias_ = s.NormalizationBias_;
            this.InitialParameters_ = s.InitialParameters_;
            this.InitialParameterCovariance_ = s.InitialParameterCovariance_;            
                        
            % Load the state only if object locked
            if wasLocked
                this.Parameters_ = s.Parameters_;
                this.ParameterCovariance_ = s.ParameterCovariance_;
            end
            
            % Call base class method to load public properties
            loadObjectImpl@matlab.System(this,s,wasLocked);
        end

        function flag = isInputSizeMutableImpl(~,~)
            % input may be row or col vector
            flag = true;
        end
        
        function s = getDiscreteStateImpl(this)
            s = struct();
            % The ParameterCovariance only applies to the
            % ForgettingFactor and KalmanFilter methods
            if isEstimationMethodForgettingFactor(this) || ...
                    isEstimationMethodKalmanFilter(this)
                L = this.ParameterCovariance_;
                s.ParameterCovariance =  L*L.';
            end
        end
        
        function adg = getAdaptationParameter(this)
            if isEstimationMethodForgettingFactor(this)
                adg = this.ForgettingFactor_;
            elseif isEstimationMethodKalmanFilter(this)
                adg = this.ProcessNoiseCovariance_;
            elseif isEstimationMethodNormalizedGradient(this) || ...
                    isEstimationMethodGradient(this)
                adg = this.AdaptationGain_;
            end
        end
        
        function adg = getAdaptationParameter2(this)
            % As of now only the NormalizedGradient method uses 2
            % parameters, which is the NormalizationBias
            if isEstimationMethodNormalizedGradient(this)
                adg = this.NormalizationBias_;
            else
                adg = cast(0,this.DataType);
            end
        end        
        
        function validateParameterDataType(this,val,propName)
            % validateParameterDataType Ensure all properties' data type
            % matches with the data type of the estimator, which cannot be
            % changed after object construction. This is to ensure that any
            % simulation config that runs can be used for codegen.
            validateattributes(val,{this.DataType},{},'',propName);
        end
        
        function setCommonOptionalNameValuePairs(this,tunablePropNVPairs)
            % Set the values of the properties shared by all subclasses
            % during construction.
            %
            % This is invoked from the constructor. Nontunable are assigned
            % in the constructor (g1784708)

            % Tunable properties
            %
            % For the numeric properties, cast their type to the data type
            % of the estimator. Do this only if user already provided a
            % numeric value. Otherwise, unexpected situations can arise,
            % such as silent char->float conversion
            dataType = this.DataType;
            if isnumeric(tunablePropNVPairs.ForgettingFactor)
                this.ForgettingFactor = cast(tunablePropNVPairs.ForgettingFactor,dataType);
            else
                this.ForgettingFactor = tunablePropNVPairs.ForgettingFactor;
            end
            if isnumeric(tunablePropNVPairs.ProcessNoiseCovariance)
                this.ProcessNoiseCovariance = cast(tunablePropNVPairs.ProcessNoiseCovariance,dataType);
            else
                this.ProcessNoiseCovariance = tunablePropNVPairs.ProcessNoiseCovariance;
            end
            if isnumeric(tunablePropNVPairs.AdaptationGain)
                this.AdaptationGain = cast(tunablePropNVPairs.AdaptationGain,dataType);
            else
                this.AdaptationGain = tunablePropNVPairs.AdaptationGain;
            end
            if isnumeric(tunablePropNVPairs.NormalizationBias)
                this.NormalizationBias = cast(tunablePropNVPairs.NormalizationBias,dataType);
            else
                this.NormalizationBias = tunablePropNVPairs.NormalizationBias;
            end            
            if isnumeric(tunablePropNVPairs.InitialParameterCovariance)
                this.InitialParameterCovariance = cast(tunablePropNVPairs.InitialParameterCovariance,dataType);
            else
                this.InitialParameterCovariance = tunablePropNVPairs.InitialParameterCovariance;
            end
            this.EnableAdaptation = tunablePropNVPairs.EnableAdaptation;
        end
        
        function tf = isEstimationMethodForgettingFactor(this)
            if this.EstimationMethod_==1
                tf = true();
            else
                tf = false();
            end
        end
               
        function tf = isEstimationMethodKalmanFilter(this)
            if this.EstimationMethod_==2
                tf = true();
            else
                tf = false();
            end
        end
        
        function tf = isEstimationMethodNormalizedGradient(this)
            if this.EstimationMethod_==3
                tf = true();
            else
                tf = false();
            end
        end
        
        function tf = isEstimationMethodGradient(this)
            if this.EstimationMethod_==4
                tf = true();
            else
                tf = false();
            end
        end            
    end
    
    methods % set&get methods
        function set.History(this,val)
            % Set method for the Dependent property History
            %
            % This is internally stored as a boolean, in the property
            % IsHistoryFinite_
            
            coder.internal.prefer_const(val);
            % Validate
            if isempty(coder.target)
                try 
                    validatedVal = controllib.internal.blocks.rls.validateHistory(val);
                catch ME
                    throw(ME)
                end
            else
                validatedVal = controllib.internal.blocks.rls.validateHistory(val);
            end
            
            % Assign
            switch validatedVal
                case 'Finite'
                    this.IsHistoryFinite_ = true();
                case 'Infinite'
                    this.IsHistoryFinite_ = false();
                otherwise
                    assert(false());
            end
        end
        
        function val = get.History(this)
            % Get method for the Dependent property History. Return the
            % value stored in the protected property IsHistoryFinite_
            if this.IsHistoryFinite_
                val = slsvInternal('slsvGetEnStringFromCatalog','Controllib:blocks:recursiveEstimationMaskFinite');
            else
                val = slsvInternal('slsvGetEnStringFromCatalog','Controllib:blocks:recursiveEstimationMaskInfinite');
            end
        end
        
        function set.InputProcessing(this,val)
            % Set method for the Dependent property InputProcessing
            %
            % This is internally stored as a boolean, in the property
            % IsUsingFrames_
            
            % Validate and assign
            if isempty(coder.target)
                try 
                    this.IsUsingFrames_ = controllib.internal.blocks.rls.validateInputProcessing(val);
                catch ME
                    throw(ME)
                end
            else
                this.IsUsingFrames_ = controllib.internal.blocks.rls.validateInputProcessing(val);
            end
        end
        
        function val = get.InputProcessing(this)
            % Get method for the Dependent property History. Return the
            % value stored in the protected property IsUsingFrames_
            if this.IsUsingFrames_
                val = slsvInternal('slsvGetEnStringFromCatalog','Controllib:blocks:recursiveEstimationMaskFrameBased');
            else
                val = slsvInternal('slsvGetEnStringFromCatalog','Controllib:blocks:recursiveEstimationMaskSampleBased');
            end
        end
        
        function set.WindowLength(this,val)
            % Validate and assign WindowLength
            
            % WindowLength must be equal to or greater than number of
            % estimated parameters
            if isempty(coder.target)
                try 
                    controllib.internal.blocks.rls.validateWindowLength(val,getNumberOfParameters(this));
                catch ME
                    throw(ME)
                end
            else
                controllib.internal.blocks.rls.validateWindowLength(val,getNumberOfParameters(this));
            end
            
            % Assign
            assert(val <= intmax('int32'));
            this.WindowLength_ = int32(val);
        end
        
        function val = get.WindowLength(this)
            % Return the value stored in the protected property
            val = this.WindowLength_;
        end        
        
        function set.EstimationMethod(this,val)
            % ignore - characters
            val = strrep(val,'-',''); 
            val = validatestring(...
                val,...
                {'forgettingfactor','kalmanfilter','normalizedgradient','gradient'},...
                'set.EstimationMethod');
            switch val
                case 'forgettingfactor'
                    methodId = uint8(1);
                case 'kalmanfilter'
                    methodId = uint8(2);
                case 'normalizedgradient'
                    methodId = uint8(3);
                case 'gradient'
                    methodId = uint8(4);
            end
            controllib.internal.blocks.rls.validateEstimationMethod(methodId);
            this.EstimationMethod_ = methodId;
        end
        
        function val = get.EstimationMethod(this)
            if isEstimationMethodForgettingFactor(this)
                val = 'ForgettingFactor';
            elseif isEstimationMethodKalmanFilter(this)
                val = 'KalmanFilter';
            elseif isEstimationMethodNormalizedGradient(this)
                val = 'NormalizedGradient';
            elseif isEstimationMethodGradient(this)
                val = 'Gradient';
            end
        end
        
        function set.ForgettingFactor(this,val)
            if isempty(coder.target)
                try 
                    controllib.internal.blocks.rls.validateForgettingFactor(val,'ForgettingFactor');
                    validateParameterDataType(this,val,'ForgettingFactor');
                catch ME
                   throw(ME) 
                end
            else
                controllib.internal.blocks.rls.validateForgettingFactor(val,'ForgettingFactor');
                validateParameterDataType(this,val,'ForgettingFactor');
            end
            this.ForgettingFactor_ = val;
        end
        
        function val = get.ForgettingFactor(this)
            val = this.ForgettingFactor_;
        end
        
        function set.ProcessNoiseCovariance(this,val)
            numParams = getNumberOfParameters(this);
            if isempty(coder.target)
                try 
                    controllib.internal.blocks.rls.validateProcessNoiseCovariance(val,numParams,'ProcessNoiseCovariance');
                    validateParameterDataType(this,val,'ProcessNoiseCovariance');
                catch ME
                    throw(ME)
                end
            else
                controllib.internal.blocks.rls.validateProcessNoiseCovariance(val,numParams,'ProcessNoiseCovariance');
                validateParameterDataType(this,val,'ProcessNoiseCovariance');
            end
            this.ProcessNoiseCovariance_ = ctrlScalarExpansion(val,numParams,false(),false());
        end
        
        function val = get.ProcessNoiseCovariance(this)
            val = this.ProcessNoiseCovariance_;
        end
        
        function set.AdaptationGain(this,val)
            if isempty(coder.target)
                try 
                    controllib.internal.blocks.rls.validateAdaptationGain(val,'AdaptationGain');
                    validateParameterDataType(this,val,'AdaptationGain');
                catch ME
                    throw(ME)
                end
            else
                controllib.internal.blocks.rls.validateAdaptationGain(val,'AdaptationGain');
                validateParameterDataType(this,val,'AdaptationGain');
            end
            this.AdaptationGain_ = val;
        end
        
        function val = get.AdaptationGain(this)
            val = this.AdaptationGain_;
        end        
        
        function set.NormalizationBias(this,val)
            if isempty(coder.target)
                try 
                    controllib.internal.blocks.rls.validateNormalizationBias(val,'NormalizationBias');
                    validateParameterDataType(this,val,'NormalizationBias');
                catch ME
                    throw(ME)
                end
            else
                controllib.internal.blocks.rls.validateNormalizationBias(val,'NormalizationBias');
                validateParameterDataType(this,val,'NormalizationBias');
            end
            this.NormalizationBias_ = val;
        end        
        
        function val = get.NormalizationBias(this)
            val = this.NormalizationBias_;
        end
        
        function set.InitialParameterCovariance(this,val)
            numParams = getNumberOfParameters(this);
            if isempty(coder.target)
                try 
                    controllib.internal.blocks.rls.validateParameterCovariance(val,numParams,'InitialParameterCovariance');
                    validateParameterDataType(this,val,'InitialParameterCovariance');
                catch ME
                    throw(ME)
                end
            else
                controllib.internal.blocks.rls.validateParameterCovariance(val,numParams,'InitialParameterCovariance');
                validateParameterDataType(this,val,'InitialParameterCovariance');
            end
            this.InitialParameterCovariance_ = ctrlScalarExpansion(val,numParams,true(),true());
        end
        
        function val = get.InitialParameterCovariance(this)
            L = this.InitialParameterCovariance_;
            val = L*L.';
        end
        
        function val = get.ParameterCovariance(this)
            L = this.ParameterCovariance_;
            val = L*L.';
        end
        
        function set.DataType(this,val)
            coder.internal.assert(ischar(val) && ...
                (strcmp(val,'double') || strcmp(val,'single')),...
                'Ident:simulink:recursiveEstimationDataTypePropertyError');
            this.DataType = val;
        end
    end
    
    methods(Static,Access=protected)
        function v = scalarToVectorExpansion(u,n)
            % Expand the scalar u to a column vector of size n
            if isscalar(u)
                v = u*ones(n,1,'like',u);
            else
                % Assume u is of right dimension.
                v = u;
            end
        end
        
        function idx = findFirstNVPair(varargin)
            % The returned value is (nargin+1) if there is no NV pair.
            idx = nargin+1;
            for k = coder.unroll(1:nargin)
                if ischar(varargin{k}) || isstruct(varargin{k}) || ...
                        (isstring(varargin{k}) && isscalar(varargin{k}))
                    idx = k;
                    return;
                end
            end
        end
        
        function inputParserSimulationAddCommonParameters(parser)
            % Add common NV pairs to the inputParser parser (handle obj)
            
            % Non-tunable properties: default values are set in the
            % property declaration. Therefore we do the assignment to the
            % default value only if the user has not specified them. This
            % single assignment is important for codegen, where the
            % non-tunable properties can only be assigned once.
            addParameter(parser,'DataType', []);
            addParameter(parser,'EstimationMethod', []);
            addParameter(parser,'InputProcessing', []);
            addParameter(parser,'History', []);
            addParameter(parser,'WindowLength', []);
            
            % Tunable properties
            addParameter(parser,'ForgettingFactor', idRecursiveEstimator.cDefaultForgettingFactor);
            addParameter(parser,'AdaptationGain', idRecursiveEstimator.cDefaultAdaptationGain);
            addParameter(parser,'NormalizationBias', idRecursiveEstimator.cDefaultNormalizationBias);
            addParameter(parser,'ProcessNoiseCovariance', idRecursiveEstimator.cDefaultProcessNoiseCovariance);
            addParameter(parser,'InitialParameterCovariance', idRecursiveEstimator.cDefaultInitialParameterCovariance);
            addParameter(parser,'EnableAdaptation', idRecursiveEstimator.cDefaultEnableAdaptation);
        end
    end
    
    methods(Hidden,Static) % utility methods used both by the block and the command line
        function v = shiftRowVectorToRight(v)
            % v(2:end) = v(1:end-1);
            for kk=numel(v):-1:2
                v(kk) = v(kk-1);
            end
        end
    end
    
    methods(Hidden)
        % Methods for internal testing
        function s = getAllPublicProperties(this)
            % Get all GetAccess=public properties
            m = metaclass(this);
            s = struct();
            for kk=1:numel(m.PropertyList)
                if strcmp(m.PropertyList(kk).GetAccess,'public')
                    s.(m.PropertyList(kk).Name) = this.(m.PropertyList(kk).Name);
                end
            end
        end
        
        function s = getDisplayedPublicProperties(this)
            % Get all active, non-hidden, non-state public properties of
            % this class. Active status of properties is defined by
            % isInactivePropertyImpl() system object method.
            m = metaclass(this);
            s = struct();
            for kk=1:numel(m.PropertyList)
                if strcmp(m.PropertyList(kk).GetAccess,'public') && ...
                        ~isInactivePropertyImpl(this,m.PropertyList(kk).Name) && ...
                        ~m.PropertyList(kk).Hidden && ...
                        ~m.PropertyList(kk).DiscreteState && ...
                        ~m.PropertyList(kk).ContinuousState
                    s.(m.PropertyList(kk).Name) = this.(m.PropertyList(kk).Name);
                end
            end
        end
    end
end

% LocalWords:  regressor Controllib forgettingfactor kalmanfilter nontunable
% LocalWords:  normalizedgradient
