classdef(Hidden) ScanningMount < fusion.internal.remotesensors.MountedDevice
    %   This class is used to define mounted devices that can scan
    %
    %   This class is for internal use only. It may be removed in the future.
    
    %   Copyright 2018-2019 The MathWorks, Inc.

    %#codegen
    
    % "Has" properties
    properties(Dependent,Nontunable)
        %HasElevation  Enable elevation scan and measurements
        %   Set to true to model a sensor capable of scanning in elevation
        %   and estimating elevation from target detections. Set to false
        %   to model sensors which cannot scan and measure elevation.
        %
        %   Default: false
        HasElevation
    end
    
    properties(Abstract, Nontunable)
        %ScanMode  Scan mode used by sensor
        %   Specify the scan mode used by the sensor as one of 'No
        %   scanning' | 'Mechanical' | 'Electronic' | 'Mechanical and
        %   electronic'. When set to 'No scanning', no scanning is
        %   performed by the sensor. When set to 'Mechanical', the sensor
        %   scans mechanically across the azimuth and elevation limits
        %   specified by the MechanicalScanLimits property. When set to
        %   'Electronic', the sensor scans electronically across the azimuth
        %   and elevation limits specified by the ElectronicScanLimits
        %   property. When set to 'Mechanical and electronic', the sensor
        %   mechanically scans the antenna boresight across the mechanical
        %   scan limits and electronically scans beams relative to the
        %   antenna boresight across the electronic scan limits. The total
        %   field of regard scanned in this mode is the combination of the
        %   mechanical and electronic scan limits.
        %
        %   In all scan modes except 'No scanning', the scan positions step
        %   by the sensor's field of view between dwells.
        %
        %   Default: 'Mechanical'
        ScanMode
    end
    properties(Abstract, Constant, Hidden)
        ScanModeSet
    end
%     properties(Constant, Hidden)
%         ScanModeSet = matlab.system.StringSet({'No scanning','Mechanical','Electronic','Mechanical and electronic'});
%     end
    
    properties(Dependent,Nontunable)
        %MaxMechanicalScanRate  Maximum mechanical scan rate (deg/s)
        %   Maximum mechanical scan rate of sensor. When HasElevation is
        %   true, this is specified as a 2-by-1 column vector, [azRate;
        %   elRate]. When HasElevation is false this is a scalar specifying
        %   the maximum mechanical azimuth scan rate. Scan rates are
        %   nonnegative values specifying the maximum rate at which the
        %   sensor can mechanically scan. The sensor sets its scan rate to
        %   step the sensor's mechanical angle by the sensor's field of
        %   regard. When the required scan rate exceeds the maximum scan
        %   rate, the maximum scan rate is used. Units are degrees per
        %   second.
        %
        %   Default: [75;75]
        MaxMechanicalScanRate
        
        %MechanicalScanLimits  Mechanical scan angle limits (deg)
        %   Mechanical scan limits of sensor. When HasElevation is true,
        %   this is specified as a 2-by-2 matrix, [minAz maxAz; minEl
        %   maxEl]. When HasElevation is false, this is a 1-by-2 row vector
        %   specifying the azimuthal mechanical scan limits of the sensor.
        %   The mechanical scan limits define the minimum and maximum
        %   mechanical angle the sensor can scan from its mounted
        %   orientation. Azimuthal scan limits cannot span more than 360
        %   degrees and elevation scan limits must lie in the closed
        %   interval [-90 90]. Units are in degrees.
        %
        %   Default: [0 360;-10 0]
        MechanicalScanLimits
        
        %ElectronicScanLimits  Electronic scan angle limits (deg)
        %   Electronic scan limits of sensor. When HasElevation is true,
        %   this is specified as a 2-by-2 matrix, [minAz maxAz; minEl
        %   maxEl]. When HasElevation is false, this is a 1-by-2 row vector
        %   specifying the azimuthal electronic scan limits of the sensor.
        %   The electronic scan limits define the minimum and maximum
        %   electronic angle the sensor can scan from its current mechanical
        %   angle. Azimuthal and elevation scan limits must lie in the
        %   closed interval [-90 90]. Units are in degrees.
        %
        %   Default: [-45 45;-45 45]
        ElectronicScanLimits
    end

    properties(Dependent,SetAccess=private)
        %MechanicalAngle  Mechanical scan angle of sensor (deg)
        %   Read-only property reporting the current mechanical scan angle
        %   of the sensor relative to its mounted angle on the platform.
        %   When HasElevation is true, the mechanical scan angle is
        %   reported as a 2-by-1 column vector, [az; el], otherwise it is a
        %   scalar value denoting the azimuthal mechanical scan angle of
        %   the sensor.
        MechanicalAngle
        
        %ElectronicAngle  Electronic scan angle of sensor (deg)
        %   Read-only property reporting the current electronic scan angle
        %   of the sensor relative to its current mechanical angle. When
        %   HasElevation is true, the electronic scan angle is reported as
        %   a 2-by-1 column vector, [az; el], otherwise it is a scalar
        %   value denoting the azimuthal electronic scan angle of the
        %   sensor.
        ElectronicAngle

        %LookAngle  Look angle of scanned mount (deg)
        %   Read-only property reporting the current look angle which is
        %   the combination of the device's current mechanical and
        %   electronic angles. If ScanMode is 'Mechanical', then LookAngle
        %   will match MechanicalAngle, since no electronic scanning is
        %   present. When ScanMode is 'Electronic', LookAngle will match
        %   ElectronicAngle. When HasElevation is true, the LookAngle is
        %   reported as a 2-by-1 column vector, [az; el], otherwise it is a
        %   scalar value denoting the azimuthal look angle of the device.
        LookAngle
    end
    
    % Properties for public properties dependent on HasElevation
    properties(Access = protected)
        pHasElevation (1, 1) logical = false
        pMaxMechanicalScanRate = [75;75]
        pMechanicalScanLimits = [0 360;-10 0]
        pElectronicScanLimits = [-45 45;-45 45]
    end
    
    properties(Nontunable,Access=protected)
        %HasMechanicalScan  Enable mechanical scan
        %   Set to true to model a sensor which can mechanically scan its
        %   antenna. When HasElevation is true, the sensor can mechanically
        %   scan in azimuth and elevation, otherwise, it can only scan in
        %   azimuth. The sensor's mechanical scan angle is defined relative
        %   to the mounting orientation of the antenna. The mechanical scan
        %   rate and scan angle limits are set by the MaxMechanicalScanRate
        %   and MechanicalScanLimits properties.
        pHasMechanicalScan
        
        %HasElectronicScan  Enable electronic scan
        %   Set to true to model a sensor which can electronically scan
        %   beams from its phased array. When HasElevation is true, the
        %   sensor can electronically scan in azimuth and elevation,
        %   otherwise, it can only scan in azimuth. The azimuth and
        %   elevation scan angles are defined relative to the current
        %   mechanical scan angle of the antenna. The mechanical scan rate
        %   and scan limits are set by the MaxMechanicalScanRate and
        %   MechanicalScanLimits properties.
        pHasElectronicScan

        % Object used to scan device
        pScanner
    end

    % Convenience syntax configuration
    properties(Access = protected)
        pIsSettingConfig = false
    end
    
    % -------------------
    % Setters and getters
    % -------------------
    methods
        function set.MaxMechanicalScanRate(obj,val)
            coder.varsize('valVar',[2 2],[1 1]);
            valVar = val;
            
            flag3D = obj.pIsSettingConfig || obj.HasElevation;
            if flag3D
                obj.checkVectorRealNonnegativeFinite(valVar,mfilename,'MaxMechanicalScanRate',2);
            else
                cond = ~isempty(val) && ~isscalar(val);
                if cond
                    coder.internal.error('shared_radarfusion:RemoteSensors:elNotSupported','MaxMechanicalScanRate')
                end
                obj.checkScalarRealNonnegativeFinite(valVar,mfilename,'MaxMechanicalScanRate');
            end
            
            out = cast(obj.pMaxMechanicalScanRate,'like',val);
            out(1:numel(valVar)) = valVar;
            obj.pMaxMechanicalScanRate = out(1:2);
        end
        function val = get.MaxMechanicalScanRate(obj)
            flag3D = obj.pIsSettingConfig || obj.HasElevation;

            if flag3D
                val = obj.pMaxMechanicalScanRate;
            else
                val = obj.pMaxMechanicalScanRate(1);
            end
        end
        
        function set.MechanicalScanLimits(obj,val)
            coder.varsize('valVar',[2 2],[1 1]);
            valVar = val;
            
            is3D = obj.pIsSettingConfig || obj.HasElevation;
            obj.checkAngleLimits(valVar,mfilename,'MechanicalScanLimits',is3D);
            
            out = cast(obj.pMechanicalScanLimits,'like',val);
            if isvector(valVar)
                out(1,1:2) = valVar(:)';
            else
                out(1:2,1:2) = valVar(1:2,1:2);
            end
            obj.pMechanicalScanLimits = out(1:2,1:2);
        end
        function val = get.MechanicalScanLimits(obj)
            flag3D = obj.pIsSettingConfig || obj.HasElevation;
            
            if flag3D
                val = obj.pMechanicalScanLimits;
            else
                val = obj.pMechanicalScanLimits(1,:);
            end
        end
        
        function set.ElectronicScanLimits(obj,val)
            coder.varsize('valVar',[2 2],[1 1]);
            valVar = val;
            
            is3D = obj.pIsSettingConfig || obj.HasElevation;
            obj.checkAngleLimits(valVar,mfilename,'ElectronicScanLimits',is3D,[-90 90]);
            
            out = cast(obj.pElectronicScanLimits,'like',val);
            if isvector(valVar)
                out(1,1:2) = valVar(:)';
            else
                out(1:2,1:2) = valVar(1:2,1:2);
            end
            obj.pElectronicScanLimits = out(1:2,1:2);
        end
        function val = get.ElectronicScanLimits(obj)
            flag3D = obj.pIsSettingConfig || obj.HasElevation;

            if flag3D
                val = obj.pElectronicScanLimits;
            else
                val = obj.pElectronicScanLimits(1,:);
            end
        end
        
        function val = get.MechanicalAngle(obj)
            coder.varsize('val',[2 1],[1 0]);
            if isLocked(obj)
                val = obj.pScanner.CurrentMechanicalAngle;
            else
                val = zeros(2,1);
            end
            
            flag3D = obj.pIsSettingConfig || obj.HasElevation;
            if ~flag3D
                val = val(1);
            end
        end
        
        function val = get.ElectronicAngle(obj)
            coder.varsize('val',[2 1],[1 0]);
            if isLocked(obj)
                val = obj.pScanner.ElectronicAngle;
            else
                val = zeros(2,1);
            end
            
            flag3D = obj.pIsSettingConfig || obj.HasElevation;
            if ~flag3D
                val = val(1);
            end
        end
        
        function val = get.LookAngle(obj)
            coder.varsize('val',[2 1],[1 0]);
            if isLocked(obj)
                val = obj.pScanner.LookAngle;
            else
                val = zeros(2,1);
            end
            
            flag3D = obj.pIsSettingConfig || obj.HasElevation;
            if ~flag3D
                val = val(1);
            end
        end
        
        function set.HasElevation(obj,val)
            if ~isempty(val)
                validateattributes(val, {'numeric', 'logical'}, {'scalar', 'real'}, 'ScanningMount', 'HasElevation');
            end
            obj.pHasElevation = val;
        end
        function val = get.HasElevation(obj)
            val = obj.pHasElevation;
        end
    end
    methods(Access=protected)
        function val = getLookOrientation(obj)
            if isLocked(obj)
                val = obj.pScanner.LookOrientation;
            else
                val = eye(3);
            end
        end
    end
    
    % -----------
    % Constructor
    % -----------
    methods
        function obj = ScanningMount(varargin)
            % Support name-value pair arguments when constructing object
            coder.extrinsic('fusion.internal.remotesensors.ScanningMount.orderConstructorInputs');
            coder.extrinsic('fusion.internal.remotesensors.ScanningMount.isLoading');
            
            % Check for scan configuration convenience syntax
            % - e.g. 'rotator', 'sector', 'raster'
            isValidConfig = false;
            checkForConfig = false;
            sysIndexArg = false;
            if nargin
                if isnumeric(varargin{1}) % system index specified
                    sysIndexArg = true;
                    sysIndex = varargin{1};
                    if nargin>1
                        checkForConfig = true;
                        arg1 = {varargin{2:end}};
                    else
                        arg1 = {};
                    end
                else
                    arg1 = varargin;
                    checkForConfig = true;
                end
            else
                arg1 = varargin;
            end

            if checkForConfig
                [isValidConfig, config, arg2] = verifyScanConfig(obj, arg1{:});
            else
                arg2 = arg1;
            end
            
            % Check SensorIndex / EmitterIndex (must be specified at time
            % of construction)
            if isa(obj, 'fusion.internal.remotesensors.ScanningSensor')
                idPropName = 'SensorIndex';
            else % scanning emitter
                idPropName = 'EmitterIndex';
            end
            if sysIndexArg
                arg3 = {idPropName, sysIndex, arg2{:}};
            else
                arg3 = arg2;
            end
            
            % Apply configuration for convenience syntax
            if isValidConfig
                obj.pIsSettingConfig = true;
                setProperties(obj,numel(config),config{:});
                obj.pIsSettingConfig = false;
            end
            
            % Order constructor's argument list
            if coder.target('MATLAB')
                argOut = fusion.internal.remotesensors.ScanningMount.orderConstructorInputs(arg3);
            else
                argOut = coder.const(@fusion.internal.remotesensors.ScanningMount.orderConstructorInputs,arg3);
            end
            
            numArg = numel(argOut);
            setProperties(obj,numArg,argOut{:});

            % SensorIndex and EmitterIndex must be defined by user at time
            % of construction
            cond = ~coder.const(fusion.internal.remotesensors.ScanningMount.isLoading) && ...
                ~coder.internal.is_defined(obj.(idPropName));
            coder.internal.errorIf(cond, 'shared_radarfusion:RemoteSensors:mustSpecifyProperty', idPropName);
        end
    end
    
    methods
        function config = coverageConfig(obj,position,orientation)
            narginchk(1,3)
            if nargin < 2
                position = zeros(1,3);
            else % validate input
                validateattributes(position,{'numeric'},...
                    {'vector','numel',3,'real'},class(obj),'position');
            end
            if nargin < 3
                orientation = quaternion(1,0,0,0);
            else % validate input
                validateattributes(orientation,{'quaternion'},{'scalar'},...
                    class(obj),'orientation');
            end
            
            if isa(obj,'fusion.internal.remotesensors.ScanningSensor')
                config.Index = obj.SensorIndex;
            else 
                config.Index = - obj.EmitterIndex; % save emitter indices as negative
            end
            config.LookAngle = obj.LookAngle;
            config.FieldOfView = obj.FieldOfView;
            switch obj.ScanMode
                case 'Mechanical'
                    scanLimits = obj.MechanicalScanLimits;
                case 'Electronic'
                    scanLimits = obj.ElectronicScanLimits;
                case 'Mechanical and electronic'
                    scanLimits = [obj.MechanicalScanLimits(:,1)+obj.ElectronicScanLimits(:,1),...
                        obj.MechanicalScanLimits(:,2)+obj.ElectronicScanLimits(:,2)];
                otherwise
                    scanLimits = zeros(1,2);
            end
            config.ScanLimits = scanLimits;
            
            config.Range = 1e7;
            
            %Combine the frame transforms --> plat --> sensor mounting
            scene2plat = rotmat(orientation,'frame');
            position = position(:) + scene2plat'*obj.MountingLocation(:);
            
            plat2sens = quaternion(obj.MountingAngles,'eulerd','ZYX','frame');
            scene2sens = orientation*plat2sens;
            
            config.Position = position';
            config.Orientation = scene2sens;
        end
        
    end
            
    
    methods(Access = private)
        function [isValidConfig, config, argIn] = verifyScanConfig(obj, configName, varargin)
            coder.extrinsic('fusion.internal.remotesensors.ScannerConfigurations.VerifyScanConfig');
            coder.extrinsic('fusion.internal.remotesensors.ScannerConfigurations.ScanConfig');
            
            if coder.target('MATLAB')
                [isValidConfig, fullConfigName] = fusion.internal.remotesensors.ScannerConfigurations.VerifyScanConfig(configName);
            else
                [isValidConfig, fullConfigName] = coder.const(@fusion.internal.remotesensors.ScannerConfigurations.VerifyScanConfig,configName);
            end
            
            if isValidConfig
                if coder.target('MATLAB')
                    config = fusion.internal.remotesensors.ScannerConfigurations.ScanConfig(fullConfigName,isDetectionGenerator(obj),varargin{:});
                else
                    config = coder.const(@fusion.internal.remotesensors.ScannerConfigurations.ScanConfig,fullConfigName,isDetectionGenerator(obj),varargin{:});
                end
                argIn = varargin;
            else
                config = {};
                argIn = {configName, varargin{:}};
            end
        end
    end
    
    methods(Static,Hidden)
        function argOut = orderConstructorInputs(argin)
            % Handle special enabling properties
            numArgs = numel(argin);
            if numArgs>0 && mod(numArgs,2)==0
                argSingle = removeMultipleVarargin(argin);
                
                % Since ScanMode enables other properties, ensure that it
                % is applied before other properties
                hasScanMode = false;
                iFnd = find(strcmp('ScanMode',argSingle));
                if ~isempty(iFnd)
                    scanMode = argSingle{iFnd+1};
                    hasScanMode = (ischar(scanMode) || isstring(scanMode)) && ...
                        isvector(scanMode);
                    if hasScanMode
                        if isstring(scanMode)
                            hasScanMode = isscalar(scanMode);
                        else
                            hasScanMode = size(scanMode,1)==1;
                        end
                    end
                end
                
                if hasScanMode
                    argScan = removePropVarargin('ScanMode',argSingle);
                    argScan = {'ScanMode',scanMode,argScan{:}};
                else
                    argScan = argSingle;
                end
                
                % Ensure that HasElevation is applied before other
                % properties since it enables the elevation values of some
                % properties
                hasEl = false;
                iFnd = find(strcmp('HasElevation',argScan),1,'last');
                if ~isempty(iFnd)
                    hasEl = argScan{iFnd(1)+1};
                    hasEl = isscalar(hasEl) && (islogical(hasEl) || (isnumeric(hasEl) && isreal(hasEl))) && hasEl;
                end
                
                if hasEl
                    argOut = removePropVarargin('HasElevation',argScan);
                    argOut = {'HasElevation',true,argOut{:}};
                else
                    argOut = argScan;
                end
            else
                argOut = argin;
            end
        end
    end
    
    % --------------
    % Implementation
    % --------------
    methods(Access = protected)
        function stepImpl(obj, time)
            % Implement algorithm. Calculate y as a function of input u and
            % discrete states.
            step(obj.pScanner, time);
        end
        
        function setupImpl(obj)
            % Perform one-time calculations, such as computing constants
            setupImpl@fusion.internal.remotesensors.MountedDevice(obj);
            
            % Setup scan sensor
            switch obj.ScanMode
                case 'Mechanical'
                    hasMechScan = true;
                    hasElecScan = false;
                case 'Electronic'
                    hasMechScan = false;
                    hasElecScan = true;
                case 'Mechanical and electronic'
                    hasMechScan = true;
                    hasElecScan = true;
                otherwise % No scanning
                    hasMechScan = false;
                    hasElecScan = false;
            end
            obj.pHasMechanicalScan = hasMechScan;
            obj.pHasElectronicScan = hasElecScan;

            obj.pScanner = fusion.internal.remotesensors.Scanner( ...
                'UpdateRate', obj.UpdateRate, ...
                'FieldOfView', obj.FieldOfView(1:2), ...
                'MountingLocation', obj.MountingLocation(1:3), ...
                'MountingAngles', obj.MountingAngles(1:3), ...
                'HasMechanicalScan', hasMechScan, ...
                'HasElectronicScan', hasElecScan);
           
            rate = obj.pMaxMechanicalScanRate;
            mlim = obj.pMechanicalScanLimits;
            elim = obj.pElectronicScanLimits;
            if ~obj.HasElevation
                rate(2) = 0;
                mlim(2,:) = 0;
                elim(2,:) = 0;
            end
            obj.pScanner.MaxMechanicalScanRate = rate;
            obj.pScanner.MechanicalScanLimits = mlim;
            obj.pScanner.ElectronicScanLimits = elim;
        end
        
        function resetImpl(obj)
            % Initialize / reset discrete-state properties
            resetImpl@fusion.internal.remotesensors.MountedDevice(obj);
            reset(obj.pScanner);
        end
    end
    
    % -------------
    % Save and load
    % -------------
    methods(Access = protected)
        function loadObjectImpl(obj,s,wasLocked)
            % Set properties in object obj to values in structure s

            % Set private and protected properties
            obj.pIsSettingConfig = s.pIsSettingConfig;
            obj.pHasElevation = s.pHasElevation;
            obj.pMaxMechanicalScanRate = s.pMaxMechanicalScanRate;
            obj.pMechanicalScanLimits = s.pMechanicalScanLimits;
            obj.pElectronicScanLimits = s.pElectronicScanLimits;
            
            if wasLocked
                obj.pHasMechanicalScan = s.pHasMechanicalScan;
                obj.pHasElectronicScan = s.pHasElectronicScan;
                obj.pScanner = fusion.internal.remotesensors.Scanner.loadobj(s.pScanner);
            end

            % Set public properties and states
            loadObjectImpl@fusion.internal.remotesensors.MountedDevice(obj,s,wasLocked);
        end

        function s = saveObjectImpl(obj)
            % Set properties in structure s to values in object obj

            % Set public properties and states
            s = saveObjectImpl@fusion.internal.remotesensors.MountedDevice(obj);
            
            % Set private and protected properties
            s.pIsSettingConfig = obj.pIsSettingConfig;
            s.pHasElevation = obj.pHasElevation;
            s.pMaxMechanicalScanRate = obj.pMaxMechanicalScanRate;
            s.pMechanicalScanLimits = obj.pMechanicalScanLimits;
            s.pElectronicScanLimits = obj.pElectronicScanLimits;
            
            if isLocked(obj)
                s.pHasMechanicalScan = obj.pHasMechanicalScan;
                s.pHasElectronicScan = obj.pHasElectronicScan;
                s.pScanner = saveobj(obj.pScanner);
            end
        end
    end
    
    methods (Access = protected)
        function config = systemConfig(obj,isValidTime,isScanDone,transforms)
            if isa(obj, 'fusion.internal.remotesensors.ScanningSensor')
                config = fusion.internal.interfaces.DataStructures.sensorConfigStruct();
                indexPropName = 'SensorIndex';
            else % a scanning emitter
                config = fusion.internal.interfaces.DataStructures.emitterConfigStruct();
                indexPropName = 'EmitterIndex';
            end
            
            config.(indexPropName) = obj.(indexPropName);
            config.IsValidTime = isValidTime;
            config.IsScanDone = isScanDone;
            config.FieldOfView(:) = obj.FieldOfView;
            config.MeasurementParameters = transforms;
        end
        
        function transformsOut = configTransforms(obj,ins,hasEl,hasRg,hasVel)
            % Returns the transforms needed to transform the detections
            % from the top-level coordinate frame back to the sensor
            % rectangular coordinate frame
            
            % Sensor->Body
            transformBody = fusion.internal.interfaces.DataStructures.measurementParametersStruct();
            transformBody.Frame = 'Rectangular';
            transformBody.OriginPosition(:) = obj.MountingLocation;
            transformBody.Orientation(:) = getLookOrientation(obj);
            transformBody.HasAzimuth = true;
            transformBody.HasElevation = hasEl;
            transformBody.HasRange = hasRg;
            transformBody.HasVelocity = hasVel;
            
            % Body->Scenario
            if ~isempty(ins)
                transformINS = insTransform(obj,ins,hasVel);
                transformsOut = [transformBody;transformINS];
            else
                transformsOut = transformBody;
            end
        end
        
        function transformINS = insTransform(~,ins,hasVel)
            % Returns the transforms needed to transform the detections
            % from the top-level coordinate frame to the body rectangular
            % coordinate frame
            
            transformINS = fusion.internal.interfaces.DataStructures.measurementParametersStruct();
            transformINS.Frame = 'Rectangular';
            transformINS.OriginPosition(:) = ins.Position;
            transformINS.OriginVelocity(:) = ins.Velocity;
            transformINS.Orientation(:) = ins.Orientation;
            transformINS.HasAzimuth = true;
            transformINS.HasElevation = true;
            transformINS.HasRange = true;
            transformINS.HasVelocity = hasVel;
        end
    end
    
    % ----------------
    % Display settings
    % ----------------
    
    % General
    methods(Access=protected)
        function flag = isInactivePropertyImpl(obj, prop)
            % Return false if property is visible based on object 
            % configuration, for the command line and System block dialog
            flag = false;
            if obj.pIsSettingConfig
                return
            end
            
            flag = isInactivePropertyImpl@fusion.internal.remotesensors.MountedDevice(obj, prop);
            if flag
                return
            end
            
            if ~any(strcmp(obj.ScanMode,{'Electronic','Mechanical and electronic'})) && ...
                    any(strcmp(prop,{'ElectronicAngle','ElectronicScanLimits'}))
                flag = true;
            end
            
            if ~any(strcmp(obj.ScanMode,{'Mechanical','Mechanical and electronic'})) && ...
                    any(strcmp(prop,{'MechanicalScanLimits','MaxMechanicalScanRate'}))
                flag = true;
            end
        end
    end
    
    methods(Access = protected, Static)
        function groups = getPropertyGroupsImpl
            % Define property section(s) for System block dialog
            mountedGroups = getPropertyGroupsImpl@fusion.internal.remotesensors.MountedDevice();

            mainGroup = mountedGroups(1);
            mntGroup = mountedGroups(2);
            limitGroup = mountedGroups(3);

            pList = {'ScanMode'};
            pList = [mainGroup.PropertyList,pList];
            mainGroup = matlab.system.display.Section('PropertyList',pList);

            pList = {'MaxMechanicalScanRate','MechanicalScanLimits','ElectronicScanLimits','MechanicalAngle','ElectronicAngle','LookAngle'};
            pList = [limitGroup.PropertyList,pList];
            limitGroup = matlab.system.display.Section('PropertyList',pList);
            
            pList = {'HasElevation'};
            hasGroup = matlab.system.display.Section('PropertyList',pList);
            
            groups = [mainGroup,mntGroup,limitGroup,hasGroup];
        end
    end
    
    % -------------------
    % Input validation
    % -------------------
    methods(Access = protected)
        function validateInputsImpl(obj, time)
            % Validate inputs to the step method at initialization (or
            % whenever an input size changes)
            validateInputsImpl@fusion.internal.remotesensors.MountedDevice(obj, time);
        end
    end
    
    methods(Static, Hidden)
        function flag = isLoading()
            % Returns true if the object is currently being loaded
            
            flag = false;
            st = dbstack;
            for m = numel(st):-1:1
                flag = strcmp(st(m).name,'System.loadobj');
                if flag
                    break
                end
            end
        end
    end
end

function argOut = removeMultipleVarargin(argIn)
% Remove multiple assignments to the same property in the constructor's
% argument list

numArgs = numel(argIn);
if numArgs>0 && mod(numArgs,2)==0
    propsIn = {argIn{1:2:end}};
    valsIn = {argIn{2:2:end}};

    % If strings were used for properties, convert to char so unique can be
    % used below
    [propsIn{:}] = convertStringsToChars(propsIn{:});
    
    [~,udx] = unique(lower(propsIn));
    propsOut = {propsIn{udx}};
    numProps = numel(propsOut);
    valsOut = cell(1,numProps);
    for iProp = 1:numProps
        thisProp = propsOut{iProp};
        iFnd = find(strcmpi(thisProp,propsIn),1,'last');
        valsOut{iProp} = valsIn{iFnd};
    end
    argOut = cell(1,2*numProps);
    for iProp = 1:numProps
        argOut{2*(iProp-1)+1} = propsOut{iProp};
        argOut{2*(iProp-1)+2} = valsOut{iProp};
    end
else
    argOut = argIn;
end
end

function argout = removePropVarargin(prop,argin)
% Removes all occurrences of property and its value from the constructor's
% argument list
isProp = strcmpi(prop,argin);
isVal = circshift(isProp,1);
isVal(1) = false;
isRm = isProp | isVal;
argout = {argin{~isRm}};
end
