classdef objectDetection
%objectDetection  Contains a report for a single object detection
%  objectDetection captures the information reported by a sensor for a
%  single object. You can use the objectDetection output as an input to
%  multi-object trackers.
%
%   detection = objectDetection(time, measurement) creates a detection
%   object with the minimum required properties. Default values are
%   assigned to all the other properties.
%   
%   detection = objectDetection(..., 'Name', value) specifies additional
%   name-value pair arguments that define the properties described below:
%
%  objectDetection Properties:
%    Time                       - time corresponding to the Measurement.
%    Measurement                - properties measured by the sensor. 
%    MeasurementNoise           - measurement noise covariance.
%    SensorIndex                - unique identifier of the sensor system.
%    ObjectClassID              - object classification.
%    ObjectClassParameters      - object classification parameters.
%    MeasurementParameters      - parameters required by the nonlinear filter. 
%    ObjectAttributes           - attributes to pass through the tracker.
%
% Class Support
% -------------
%   Time, Measurement, MeasurementNoise, SensorIndex, and ObjectClassID
%   must be numeric values. MeasurementParameters, and ObjectAttributes
%   must be a cell array or a struct. Measurement must be either single or
%   double precision. Its class will determine the class of
%   MeasurementNoise. If MeasurementNoise is a scalar, it will be expanded
%   to a diagonal square matrix with that scalar along the diagonal.
%   ObjectClassParameters must be a struct or empty.
%   
% Examples
% --------
%   % Create an objectDetection with minimum required properties
%   % Define a detection made at time = 1 with a measurement of [0;0;0].
%   detection = objectDetection(1, [0;0;0])
%
%   % Create an objectDetection with additional optional properties
%   detection = objectDetection(1, [0;0;0], 'SensorIndex', 1, ...
%       'ObjectAttributes', {'Example object', 5})
%
% See also: trackingKF, trackingEKF, trackingUKF

% Copyright 2016-2022 The MathWorks, Inc.

%#codegen

% Public properties:
    properties (Access=public)
        % Time. Time corresponding to the Measurement.
        %   A nonnegative real scalar.
        Time
        
        % Measurement. Properties measured by the sensor. 
        %   The measurement has to fit the same size and type as expected
        %   by the measurement model of the tracking filter. 
        %   A real vector.
        Measurement
    end
    properties (Access=public, Dependent=true)
        % MeasurementNoise. Measurement noise covariance.
        %   Can be specified as a scalar or a matrix. If specified as a
        %   scalar it will be expanded to a square diagonal matrix with the
        %   same dimensions and type as the measurement. 
        %   A real positive semi-definite matrix. 
        %
        %   Default: a unity matrix of the same dimension and type as the
        %   measurement.
        MeasurementNoise
    end
    properties (Access=public)
        % SensorIndex. Unique identifier of the sensor system.
        %   In a multi-sensor system, it is necessary to distinguish
        %   between detections that come from different sensors. Use
        %   SensorIndex to provide a unique identification of each sensor.
        %   A positive integer scalar.
        %
        %   Default: 1.
        SensorIndex
        
        % ObjectClassID. Object classification.
        %   Use this property to identify an object class. 
        %   For example, use 1 for objects of type 'Car', 2 for objects of
        %   type 'Pedestrian', etc.
        %   Note: 0 is reserved for unknown classification. When a known
        %   classification is specified, additional information may be
        %   specified in ObjectClassParameters. An integer scalar.
        %
        %   Default: 0.
        ObjectClassID
        
        %ObjectClassParameters Classifier output parameters
        %  Use this property to specify parameters of the object
        %  classification. Specify as a scalar struct. For class fusion
        %  with multi-object trackers, you may specify the following field:
        %
        %      - ConfusionMatrix:   Use this field to specify the 
        %                           classification confusion matrix as a
        %                           square matrix of integers, or as a
        %                           square matrix of probability values
        %                           (reals between 0 and 1). The size of
        %                           the matrix must match the total number
        %                           of classifications possible. For
        %                           example, if the classification outputs
        %                           2 classes, and the correct
        %                           classification is obtained 95% of the
        %                           time, specify the confusion matrix as
        %                           [0.95 0.05; 0.05 0.95].
        %
        % Default: [].
        ObjectClassParameters

        % MeasurementParameters. Parameters required by the nonlinear filter. 
        %   Use this property to specify additional parameters if both the
        %   following conditions are met:
        %       1. The filter is nonlinear (trackingEKF or trackingUKF) 
        %       2. The filter's MeasurementFcn requires additional
        %          parameters in addition to the state.
        %   A cell array. 
        %
        %   Default: {}.
        MeasurementParameters
        
        % ObjectAttributes. Attributes to pass through the tracker.
        %   Use ObjectAttributes to provide additional information about
        %   the object that the multiObjectTracker will not use. These
        %   attributes will be added to the output of the tracker.
        %   For example, use ObjectAttributes to specify an object
        %   identification, if  provided by the sensor, or a bounding box
        %   in a video frame.
        %   A cell array.
        %
        %   Default = {}.
        ObjectAttributes
    end
    
% Private properties
    properties (Access=private)
        % pMeasurementNoise.  Keeps the value of the dependent property
        % MeasurementNoise.
        pMeasurementNoise
    end    
    
    % Constant private parameters
    properties(Constant, Access = private)        
        constMeasurementNoise       = 1;
        constSensorIndex            = 1;
        constObjectClassID          = 0;
        constMeasurementParameters  = {};
        constObjectAttributes       = {};
    end
    
    methods
        % -----------------------------------------------------------------
        % Constructor 
        % -----------------------------------------------------------------
        function detection = objectDetection(time, measurement, varargin)
            % Construct a single detection object. 
            %   detection = objectDetection(time, measurement) allows the
            %   creation of a detection object with the mandatory
            %   information. All the other properties will be assigned
            %   default values.
            %
            %   detection = objectDetection(..., 'Name', value) allows the
            %   definition of optional values. Name-value pairs can be
            %   added in any order.

            narginchk(2,inf);
            detection = setProperties(detection,time, measurement, varargin{:});            
        end
        
        % -----------------------------------------------------------------
        % set-get methods:
        % -----------------------------------------------------------------
        function detection = set.Time(detection, value)
            % A real vector.
            validateattributes(value, {'numeric'}, ...
                {'real', 'finite', 'nonsparse', 'nonnegative', 'scalar'},...
                'objectDetection', 'Time');
            detection.Time = value;
        end        
        % -----------------------------------------------------------------
                
        function detection = set.Measurement(detection, value)
            % A real vector.
            validateattributes(value, {'single', 'double'}, ...
                {'real', 'finite', 'nonsparse', 'vector'},...
                'objectDetection', 'Measurement');
            detection.Measurement = value;
        end        
        % -----------------------------------------------------------------
        
        function detection = set.SensorIndex(detection, value)
            % A positive integer scalar
            validateattributes(value, {'numeric'}, {'real', 'positive'...
                'nonsparse', 'scalar', 'integer'}, 'objectDetection', 'SensorIndex');
            detection.SensorIndex = value;
        end              
        % -----------------------------------------------------------------
        
        function detection = set.ObjectClassID(detection, value)
            % A nonnegative integer scalar.
            validateattributes(value, {'numeric'}, {'real', 'integer',...
                'nonsparse', 'finite', 'nonnegative', 'scalar'}, ...
                'objectDetection', 'ObjectClassID');
            detection.ObjectClassID = value;
        end
        % -----------------------------------------------------------------
        function detection = set.ObjectClassParameters(detection, value)
            % A struct with mandatory field "ConfusionMatrix", [] is also
            % accepted
            if isnumeric(value) && isempty(value)
                detection.ObjectClassParameters = value;
            else

                validateattributes(value, {'struct'}, {'scalar'},...
                    'objectDetection', 'ObjectClassParameters');
                detection.ObjectClassParameters = value;
            end
        end
        % -----------------------------------------------------------------
        
        function detection = set.MeasurementNoise(detection, value)
            % A real positive semi-definite matrix. 
            % (Default = unity matrix of the same dimension and class as
            % the measurement)
            stateDims = size(detection.Measurement(:),1);
            validateattributes(value, {'numeric'}, {'real', ...
                'square', 'nonsparse', 'finite'}, ...
                'objectDetection', 'MeasurementNoise');
            if ~isempty(value)                
                if isscalar(value)
                    % Scalar is expanded to measurement size
                    detection.pMeasurementNoise = value * eye(stateDims, ...
                        'like', detection.Measurement);
                else
                    % Validate the size matches the measurement length
                    validateattributes(value, {'numeric'}, {'size',[stateDims,stateDims]}, ...
                        'objectDetection', 'MeasurementNoise');
                    % Validate that measurement noise is symmetric and
                    % positive-definite
                    matlabshared.tracking.internal.isSymmetricPositiveSemiDefinite('MeasurementNoise',value);
                    detection.pMeasurementNoise = ...
                        cast(value, class(detection.Measurement));
                end
            else
                detection.pMeasurementNoise = eye(stateDims, ...
                    'like', detection.Measurement);
            end            
        end
        % -----------------------------------------------------------------
        
        function value = get.MeasurementNoise(detection)
            value = detection.pMeasurementNoise;
        end        
        % -----------------------------------------------------------------
        
        function detection = set.MeasurementParameters(detection, value)
            % A cell array (Default = {}).
            % Also accept a struct for compatibility with Simulink buses
            validateattributes(value, {'cell','struct'}, {}, 'objectDetection', 'MeasurementParameters');
            detection.MeasurementParameters = value;
        end        
        % -----------------------------------------------------------------
        
        function detection = set.ObjectAttributes(detection, value)
            % A cell array (Default = {}).
            % also accept a struct for comatibility with Simulink buses and
            % code generation
            validateattributes(value, {'cell','struct'}, {}, 'objectDetection', 'ObjectAttributes');
            detection.ObjectAttributes = value;
        end
    end       
    
    %----------------------------------------------------------------------
    % Protected methods for assigning properties and defaults
    %----------------------------------------------------------------------
    methods (Access = protected)
        function detection = setProperties(detection,time, measurement, varargin)
            % Parse the properties of the detection object
            [measurementNoise, sensorIndex, objectClassID, ...
                objectClassParameters, measurementParameters, objectAttributes] = ...
                parseInputs(detection, varargin{:});
            
            % Set values
            detection.Measurement = measurement;                        
            detection.Time = time;            
            detection.MeasurementNoise = measurementNoise;
            detection.SensorIndex = sensorIndex;
            detection.ObjectClassID = objectClassID;
            detection.MeasurementParameters = measurementParameters;
            detection.ObjectAttributes = objectAttributes;
            if ~isempty(objectClassParameters)
                detection.ObjectClassParameters = objectClassParameters;
            end
        end
        %------------------------------------------------------------------
        function [measurementNoise, sensorIndex, objectClassID, ...
                objectClassParameters, measurementParameters, objectAttributes] = ...
                parseInputs(detection, varargin)

            NVPairNames = {'MeasurementNoise',...
                'SensorIndex',...
                'ObjectClassID',...
                'ObjectClassParameters',...
                'MeasurementParameters',...
                'ObjectAttributes'...
                };

            poptions = struct( ...
                'CaseSensitivity',false, ...
                'PartialMatching','unique', ...
                'IgnoreNulls',true, ...
                'SupportOverrides',true);

            pstruct = coder.internal.parseInputs({},NVPairNames,poptions,varargin{:});

            % Retrieve input values.
            measurementNoise = coder.internal.getParameterValue(pstruct.MeasurementNoise,detection.constMeasurementNoise,varargin{:});
            sensorIndex = coder.internal.getParameterValue(pstruct.SensorIndex,detection.constSensorIndex,varargin{:});
            objectClassID = coder.internal.getParameterValue(pstruct.ObjectClassID,detection.constObjectClassID,varargin{:});
            objectClassParameters = coder.internal.getParameterValue(pstruct.ObjectClassParameters,[],varargin{:});
            measurementParameters = coder.internal.getParameterValue(pstruct.MeasurementParameters,detection.constMeasurementParameters,varargin{:});
            objectAttributes = coder.internal.getParameterValue(pstruct.ObjectAttributes,detection.constObjectAttributes,varargin{:});

        end
    end

end