classdef bleAngleEstimateConfig < comm.internal.ConfigBase
%bleAngleEstimateConfig Bluetooth LE angle estimation configuration
%parameters
%
%   CFG = bleAngleEstimateConfig creates a default Bluetooth(R) low energy
%   (LE) angle estimation configuration object, CFG.
%
%   CFG = bleAngleEstimateConfig(Name,Value) creates a Bluetooth LE angle
%   estimation configuration object, CFG, with the specified property Name
%   set to the specified Value. You can specify additional name-value pair
%   arguments in any order as (Name1,Value1,...,NameN,ValueN).
%
%   bleAngleEstimateConfig methods:
%   getElementPosition - Returns positions of array elements
%   getNumElements     - Returns number of elements in the array
%
%   bleAngleEstimateConfig properties:
%
%   ArraySize          - Size of array
%   ElementSpacing     - Normalized element spacing with respect to
%                        wavelength
%   EnableCustomArray  - Flag to enable or disable the custom array
%   ElementPosition    - Normalized element positions with respect to
%                        wavelength
%   SlotDuration       - Switch and sample slot duration in microseconds
%   SwitchingPattern   - Antenna switching pattern
%
%   % Examples:
%
%   % Example 1:
%   % Create a Bluetooth LE angle estimation configuration object to
%   % estimate single angle from a signal using 4-element uniform linear
%   % array (ULA).
%   cfg = bleAngleEstimateConfig;
%   cfg.ArraySize = 4; % Scalar value represents ULA
%
%   % Example 2:
%   % Create a Bluetooth LE angle estimation configuration object to
%   % estimate two angles (azimuth and elevation) from a signal using 4x4
%   % uniform rectangular array (URA). Specify normalized element row and
%   % column spacing of 0.4 and 0.3, respectively.
%   cfg = bleAngleEstimateConfig;
%   cfg.ArraySize = [4 4];
%   cfg.ElementSpacing = [0.4 0.3];
%   cfg.SwitchingPattern = 1:16;
%
%   % Example 3:
%   % Create a Bluetooth LE angle estimation configuration object to
%   % estimate two angles (azimuth and elevation) from a signal using
%   % 4-element uniform circular array (UCA).
%
%   % Provide normalized element positions of UCA by enabling custom array
%   cfg = bleAngleEstimateConfig;
%   cfg.EnableCustomArray = 1; % To enable custom array
%   cfg.ElementPosition = [0 0 0 0;0 0.5 1 0.5;0 -0.5 0 0.5];
%   cfg.SwitchingPattern = 1:4;
%
%   See also bleAngleEstimate, bleWaveformGenerator, bleIdealReceiver,
%   bleCTEIQSample, blePositionEstimate.

%   Copyright 2020-2022 The MathWorks, Inc.

%#codegen

properties
    %ArraySize Size of array
    %   Specify the array size as a scalar positive integer or a 1x2
    %   integer vector. To enable this property, set the EnableCustomArray
    %   property to false. A scalar ArraySize specifies ULA antenna array
    %   design, where array elements are along y-axis. A 1x2 vector
    %   ArraySize specifies URA antenna array design and is in the form of
    %   [NumberOfRowElements NumberOfColumnElements], where row elements
    %   are along y-axis and column elements are along z-axis. The default
    %   value is 4.
    ArraySize

    %ElementSpacing Normalized element spacing with respect to wavelength
    %   Specify the element spacing of the array as a scalar or a 1x2
    %   vector. To enable this property, set the EnableCustomArray property
    %   to false. A scalar ElementSpacing specifies ULA antenna array
    %   design, where antenna elements are along y-axis. A 1x2 vector
    %   ElementSpacing specifies URA antenna array design and is in the
    %   form of [SpacingBetweenRows SpacingBetweenColumns], where rows are
    %   along y-axis and columns are along z-axis. The default value is
    %   0.5.
    ElementSpacing

    %EnableCustomArray Flag to enable or disable the custom array
    %   Specify the flag to enable or disable custom array as a logical
    %   scalar. True indicates that custom array is enabled. False
    %   indicates that custom arrray is disabled. The default value is
    %   false.
    EnableCustomArray = false

    %ElementPosition Normalized element positions with respect to
    %wavelength
    %   Specify the element position of the array as a 3xN matrix, where N
    %   indicates the number of elements in the custom array. To enable
    %   this property, set the EnableCustomArray property to true. Each
    %   column of the ElementPosition specifies the element position in the
    %   form of [x; y; z] in the local coordinate system. The first column
    %   must be [0;0;0]. The default value is [0 0.5 1 1.5;0 0.5 1 1.5;0
    %   0.5 1 1.5].
    ElementPosition

    %SlotDuration Switch and sample slot duration in microseconds
    %   Specify the switch and sample slot duration (in microseconds) as 1
    %   or 2. The default value is 2.
    SlotDuration = 2

    %SwitchingPattern Antenna switching pattern
    %   Specify the switching pattern as a 1xM row vector, where M must be
    %   in the range [2,74/SlotDuration+1]. The default value is [1 2 3 4].
    SwitchingPattern
end
methods
    function obj = bleAngleEstimateConfig(varargin)
        % Constructor
        obj = obj@comm.internal.ConfigBase('ArraySize',4,'ElementSpacing',0.5,...
            'ElementPosition', [0 0.5 1 1.5;0 0.5 1 1.5;0 0.5 1 1.5],...
            'SwitchingPattern',[1 2 3 4],varargin{:});
    end
    function obj = set.ArraySize(obj,value)
        validateattributes(value,{'double'},{'nonempty','nonnan','finite',...
                'row','integer','positive','>=',2},mfilename,'ArraySize');
        coder.internal.errorIf(length(value)>2,'bluetooth:bleAngleEstimate:InvalidSize',...
            'Array size',length(value));
        % Make array size to accept variable size during code generation
        temp = value;
        coder.varsize('temp',[1 2]);
        obj.ArraySize = temp;
    end
    function obj = set.ElementSpacing(obj,value)
        validateattributes(value,{'double'},{'nonempty','nonnan','finite','real',...
                  'row','positive'},mfilename,'ElementSpacing');
        coder.internal.errorIf(length(value)>2,'bluetooth:bleAngleEstimate:InvalidSize',...
            'Element spacing',length(value));
        % Make element spacing to accept variable size during code generation
        temp = value;
        coder.varsize('temp',[1 2]);
        obj.ElementSpacing = temp;
    end
    function obj = set.EnableCustomArray(obj, value)
        validateattributes(value,{'double','logical'}, ...
            {'scalar','binary'},mfilename,'EnableCustomArray');
        obj.EnableCustomArray = logical(value);
    end
    function obj = set.ElementPosition(obj,value)
        validateattributes(value,{'double'},{'nonempty','nonnan','finite','real',...
                    'nrows',3}, mfilename,'ElementPosition');
        % Validate size of element position
        coder.internal.errorIf(size(value,2)==1,...
        'bluetooth:bleAngleEstimate:MinElementPositionSize');
        % Validate element position value
        coder.internal.errorIf(any(value(:,1)~=[0;0;0]),...
        'bluetooth:bleAngleEstimate:InvalidElementPositionValue',...
        value(1,1),value(2,1),value(3,1));
        % Make element position to accept variable size during code generation
        temp = value;
        coder.varsize('temp',[3 75],[0 1]);
        obj.ElementPosition = temp;
    end
    function obj = set.SlotDuration(obj,value)
        validateattributes(value,{'double'},{'nonempty','integer','positive'...
                    'scalar','<=',2}, mfilename,'SlotDuration');
        obj.SlotDuration = value;
    end
    function obj = set.SwitchingPattern(obj,value)
        validateattributes(value,{'double'},{'nonempty','nonnan','finite','integer',...
            'positive','row'},mfilename,'SwitchingPattern');
        % Validate values of switching pattern
        coder.internal.errorIf(value(1)~=1,...
            'bluetooth:bleAngleEstimate:InvalidSwitchingFirstValue',value(1));
        % Make switching pattern to accept variable size during code generation
        temp = value;
        coder.varsize('temp',[1 75]);
        obj.SwitchingPattern = temp;
    end
    function validateConfig(obj)
    %validateConfig Validate the dependent properties of
    %bleAngleEstimateConfig object
        maxValue = 74/obj.SlotDuration+1; % Maximum number of elements
        numEle = getNumElements(obj); % Number of elements in an array
        % Validate number of elements based on slot duration
        if obj.EnableCustomArray
            coder.internal.errorIf((numEle>maxValue),...
                'bluetooth:bleAngleEstimate:MaxElementPositionSize',...
                maxValue,numEle);
        else
            if isscalar(obj.ArraySize)
                coder.internal.errorIf((numEle>maxValue),...
                'bluetooth:bleAngleEstimate:MaxULASize',...
                maxValue,numEle);
            else
                coder.internal.errorIf((numEle>maxValue),...
                'bluetooth:bleAngleEstimate:MaxURASize',...
                maxValue,numEle);
            end
        end

        % Validate maximum length and values of switching pattern
        coder.internal.errorIf((length(obj.SwitchingPattern)>maxValue),...
            'bluetooth:bleAngleEstimate:InvalidSwitchingLength',...
            maxValue,length(obj.SwitchingPattern));
        coder.internal.errorIf(any(obj.SwitchingPattern>numEle),...
            'bluetooth:bleAngleEstimate:MaxSwitchingValue',...
            numEle);
        coder.internal.errorIf(~all(ismember((1:numEle)',obj.SwitchingPattern','rows')==1),...
            'bluetooth:bleAngleEstimate:InvalidSwitchingValue',...
            numEle);
    end
    function pos = getElementPosition(obj)
    %getElementPosition Element positions of the array
    %   POS = getElementPosition(OBJ) returns the positions of array
    %   elements by considering origin of the local coordinate system as
    %   the first element position of the array. POS is a 3xN matrix, where
    %   N is the number of elements in OBJ. Each column of the POS
    %   specifies the element position in the form of [x; y; z] in the
    %   local coordinate system.
    %
    %   % Example:
    %   % Create a default bleAngleEstimateConfig object and obtain the
    %   % array element positions.
    %
    %   cfg = bleAngleEstimateConfig;
    %   pos = getElementPosition(cfg)
        numEle = getNumElements(obj);
        eleIdx = (0:numEle-1)';
        pos = zeros(3,numEle);
        if ~obj.EnableCustomArray
            % Validate the array size and element spacing
            coder.internal.errorIf(length(obj.ArraySize)~=length(obj.ElementSpacing),...
                'bluetooth:bleAngleEstimate:UnequalSize');
            if size(obj.ArraySize,2)==1
                temp = eleIdx*obj.ElementSpacing;
                pos(2,:) = temp(:,1);
            else
                arrSizeRow = obj.ArraySize(1);
                eleColIdx = rem(eleIdx, arrSizeRow);
                eleRowIdx = (eleIdx - eleColIdx)/arrSizeRow;
                pos(2,:) = eleRowIdx*obj.ElementSpacing(2);
                pos(3,:) = eleColIdx*obj.ElementSpacing(1);
            end
        else
            pos = obj.ElementPosition;
        end
    end
    function numElements = getNumElements(obj)
    %getNumElements Number of elements in the array
    %   NUMELEMENTS = getNumElements(OBJ) returns the number of elements,
    %   NUMELEMENTS, in the Bluetooth LE angle estimation configuration
    %   object, OBJ. For a SlotDuration of 1 and 2 microseconds, the
    %   NUMELEMENTS is in the range [2, 75] and [2, 38], respectively.
    %
    %   % Example:
    %   % Create a bleAngleEstimateConfig object using 2x3
    %   % URA and obtain its number of elements.
    %
    %   cfg = bleAngleEstimateConfig('ArraySize',[2 3]);
    %   N = getNumElements(cfg)
        if obj.EnableCustomArray
            numElements = size(obj.ElementPosition,2);
        else
            numElements = prod(obj.ArraySize);
        end
    end
end
methods (Access = protected)
    % Control the conditional display of properties
    function flag = isInactiveProperty(obj,propName)
        % Initialize
        flag = false;
        % ElementPosition is not applicable when EnableCustomArray is false
        if strcmp(propName,'ElementPosition')
            flag = ~obj.EnableCustomArray;
        % ArraySize and ElementSpacing are not applicable when
        % EnableCustomArray is true
        elseif any(strcmp(propName,{'ArraySize','ElementSpacing'}))
            flag = obj.EnableCustomArray;
        end
    end
end
end