function hNRSchedulingFDDValidateConfig(simParameters) %#codegen
%hNRSchedulingFDDValidateConfig Validate the configuration parameters
%
%   This is a helper function for an example
%
%   hNRSchedulingFDDValidateConfig(SIMPARAMETERS) Validates the simulation
%   parameters
%
%   SIMPARAMETERS is a structure with these fields:
%   NumFramesSim               - Number of 10 ms frames to simulate
%   NumUEs                     - Number of UEs in the cell
%   UEDistance                 - Distance of UEs from gNB (in meters)
%   CQIvsDistance              - Mapping between distance (in meters) from
%                                gNB and maximum achievable CQI value (1-15)
%   AppConfig                  - Application configuration
%   RLCChannelConfig           - Logical channel and its associated RLC
%                                entity configuration
%                                of its logical channels
%   NumRBs                     - Number of resource blocks in carrier
%                                bandwidth
%   SCS                        - Subcarrier spacing (in kHz)
%   SchedulerPeriodicity       - Scheduler run periodicity in slots
%   SchedulerStrategy          - Scheduler strategy
%   MovingAvgDataRateWeight    - Moving average parameter within range
%                                (0, 1) to calculate average data rate for
%                                the UEs
%   EnableHARQ                 - Flag to enable or disable HARQ
%   RBAllocationLimitUL        - Maximum limit on the numbers of uplink RBs
%                                allotted to a UE in a slot
%   RBAllocationLimitDL        - Maximum limit on the numbers of downlink
%                                RBs allotted to a UE in a slot
%   NumHARQ                    - Number of HARQ processes on UEs
%   BSRPeriodicity             - Periodicity for BSR packet generation
%   ChannelUpdatePeriodicity   - Periodicity for channel update
%   CQIDelta                   - Value with which channel will be improved
%                                or deteriorated
%   CQISuccessProb             - Expression to calculate reception success
%                                probability of a PUSCH and PDSCH packet
%                                based on the channel quality
%   CQIVisualization           - Flag to enable or disable CQI grid
%                                visualization
%   RBVisualization            - Flag to enable or disable resource grid
%                                visualization
%   NumMetricsSteps            - Number of times metrics plots get updated
%                                in the simulation

%   Copyright 2019 The MathWorks, Inc.

    % Number of slots in a frame
    numSlotsFrame = 10 * (simParameters.SCS / 15);
    % Number of slots in the simulation
    numSlotsSim = simParameters.NumFramesSim * numSlotsFrame;

    % Validate the simulation time
    validateattributes(simParameters.NumFramesSim, {'numeric'}, {'nonempty', 'integer', 'scalar', 'finite', '>=', 0}, 'simParameters.NumFramesSim');

    % Validate the number of UEs
    validateattributes(simParameters.NumUEs, {'numeric'}, {'nonempty', 'integer', 'scalar', 'finite', '>=', 0}, 'simParameters.NumUEs');

    % Validate the distance of UEs from the gNB
    validateattributes(simParameters.UEDistance, {'numeric'}, {'nonempty', 'vector', 'finite', 'numel', simParameters.NumUEs, '>', 0}, 'simParameters.UEDistance');

    % Validate the mapping between distance and CQI
    validateattributes(simParameters.CQIvsDistance, {'numeric'}, {'nonempty', '2d'}, 'simParameters.CQIvsDistance');
    % Distance must be in strictly increasing order
    validateattributes(simParameters.CQIvsDistance(:, 1), {'numeric'}, {'finite', '>', 0, 'increasing'}, 'simParameters.CQIvsDistance(:, 1)');
    % CQI must be in strictly decreasing order
    validateattributes(simParameters.CQIvsDistance(:, 2), {'numeric'}, {'integer', '>', 0, '<=', 15, 'decreasing'}, 'simParameters.CQIvsDistance(:, 2)');

    % Verify the logical channel's priority, PBR, and BSD properties
    for lchIdx = 1:size(simParameters.RLCChannelConfig, 1)
        % Validate the UE id on which the logical channel is configured
        validateattributes(simParameters.RLCChannelConfig.RNTI(lchIdx), {'numeric'}, {'nonempty', 'integer', '>', 0, '<=', simParameters.NumUEs}, 'simParameters.RLCChannelConfig.RNTI');
        % Validate the logical channel id
        validateattributes(simParameters.RLCChannelConfig.LCID(lchIdx), {'numeric'}, {'nonempty', 'integer', '>', 0, '<=', 32}, 'simParameters.RLCChannelConfig.LCID');
        % Validate priority of the logical channel
        validateattributes(simParameters.RLCChannelConfig.Priority(lchIdx), {'numeric'}, {'nonempty', 'scalar', '>', 0, '<=', 16}, 'simParameters.RLCChannelConfig.Priority');
        % Valid PBR (in kBps)
        validPBR =  [0, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, Inf];
        % Validate the PBR
        validateattributes(simParameters.RLCChannelConfig.PBR(lchIdx), {'numeric'}, {'nonempty', 'scalar'}, 'simParameters.RLCChannelConfig.PBR');
        if ~ismember(simParameters.RLCChannelConfig.PBR, validPBR)
            error('PBR ( %d ) must be one of the set (0, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, Inf).', simParameters.RLCChannelConfig.PBR);
        end
        % Valid BSD (in ms)
        validBucketSizeDuration =  [5, 10, 20, 50, 100, 150, 300, 500, 1000];
        % Validate the BSD
        validateattributes(simParameters.RLCChannelConfig.BucketSizeDuration(lchIdx), {'numeric'}, {'nonempty', 'scalar', '>', 0}, 'simParameters.BSRPeriodicity');
        if ~ismember(simParameters.RLCChannelConfig.BucketSizeDuration, validBucketSizeDuration)
            error('BucketSizeDuration ( %d ) must be one of the set (5, 10, 20, 50, 100, 150, 300, 500, 1000).', simParameters.RLCChannelConfig.BucketSizeDuration);
        end
        % Validate the logical channel direction
        validateattributes(simParameters.RLCChannelConfig.EntityDir(lchIdx), {'numeric'}, {'nonempty', 'integer', '>=', 0, '<=', 2}, 'simParameters.RLCChannelConfig.EntityDir');
        % Check whether any logical channel information is duplicated
        rnti = simParameters.RLCChannelConfig.RNTI(lchIdx);
        lcid = simParameters.RLCChannelConfig.LCID(lchIdx);
        numLCHInfo = find((rnti == simParameters.RLCChannelConfig.RNTI) & ...
            (lcid == simParameters.RLCChannelConfig.LCID));
        if numel(numLCHInfo) ~= 1
            error('Duplicate logical channel configuration is present for LCID ( %d ) in UE ( %d ).', lcid, rnti);
        end
    end

    % Validate the app periodicity
    validateattributes(simParameters.AppConfig.PacketInterval, {'numeric'}, {'nonempty', 'integer', 'finite', '>', 0}, 'simParameters.AppConfig.PacketInterval');
    % Validate the size of packet generated by applications
    validateattributes(simParameters.AppConfig.PacketSize, {'numeric'}, {'nonempty', 'integer', 'finite', '>=', 0}, 'simParameters.AppConfig.PacketSize');
    % Validate the UE id on which the application is installed
    validateattributes(simParameters.AppConfig.RNTI, {'numeric'}, {'nonempty', 'integer', '>', 0, '<=', simParameters.NumUEs}, 'simParameters.AppConfig.RNTI');
    % Validate the logical channel id on which the application is installed
    validateattributes(simParameters.AppConfig.LCID, {'numeric'}, {'nonempty', 'integer', '>', 0, '<=', 32}, 'simParameters.AppConfig.LCID');
    % Validate the logical channel id on which the application is installed
    validateattributes(simParameters.AppConfig.HostDevice, {'numeric'}, {'nonempty', 'integer', '>=', 0, '<=', 2}, 'simParameters.AppConfig.HostDevice');

    % Verify that each application is associated with a logical channel
    for appIdx = 1:size(simParameters.AppConfig, 1)
        rnti = simParameters.AppConfig.RNTI(appIdx);
        lcid = simParameters.AppConfig.LCID(appIdx);
        deviceType = simParameters.AppConfig.HostDevice(appIdx);

        [lchInfoIdxList, ~] = find((rnti == simParameters.RLCChannelConfig.RNTI) & ...
            (lcid == simParameters.RLCChannelConfig.LCID));
        if ~isempty(lchInfoIdxList)
            isRLCEntityPresent = false;
            for lchInfoIdx = 1: numel(lchInfoIdxList)
                if (simParameters.RLCChannelConfig.EntityDir(lchInfoIdxList(lchInfoIdx)) == 2) || (simParameters.RLCChannelConfig.EntityDir(lchInfoIdxList(lchInfoIdx)) == deviceType)
                    isRLCEntityPresent = true;
                    break;
                end
            end
            if ~isRLCEntityPresent
                error('Application must be associated to a logical channel');
            end
        else
                error('Application must be associated to a logical channel');
        end
    end

    % Validate the subcarrier spacing
    validateattributes(simParameters.SCS, {'numeric'}, {'nonempty', 'integer', 'scalar', 'finite', '>', 0}, 'simParameters.SCS');
    if ~(simParameters.SCS == 15 || simParameters.SCS == 30 || simParameters.SCS == 60)
        error('The subcarrier spacing ( %d ) must be one of the set (15, 30, 60).', simParameters.SCS);
    end

    % As per 3GPP TS 38.104, the valid number of resource blocks for each
    % SCS in FDD mode
    valid15kHzRBs = [25, 52, 79, 106, 133, 160, 216, 270];
    valid30kHzRBs = [11, 24, 38, 51, 65, 78, 106, 133, 162, 189, 217, 245, 273];
    valid60kHzRBs = [11, 18, 24, 31, 38, 51 65, 79, 93, 107, 121, 135];
    if simParameters.SCS == 15
        if ~ismember(simParameters.NumRBs, valid15kHzRBs)
            error('For 15kHz subcarrier spacing, NumRBs ( %d ) must be one of the set (25, 52, 79, 106, 133, 160, 216, 270).', simParameters.NumRBs);
        end
    elseif simParameters.SCS == 30
        if ~ismember(simParameters.NumRBs, valid30kHzRBs)
            error('For 30kHz subcarrier spacing, NumRBs ( %d ) must be one of the set (11, 24, 38, 51, 65, 78, 106, 133, 162, 189, 217, 245, 273).', simParameters.NumRBs);
        end
    else
        if ~ismember(simParameters.NumRBs, valid60kHzRBs)
            error('For 60kHz subcarrier spacing, NumRBs ( %d ) must be one of the set (11, 18, 24, 31, 38, 51 65, 79, 93, 107, 121, 135).', simParameters.NumRBs);
        end
    end

    % Validate the scheduler run periodicity
    validateattributes(simParameters.SchedulerPeriodicity, {'numeric'}, {'nonempty', 'integer', 'scalar', 'finite', '>', 0, '<=', numSlotsFrame}, 'simParameters.SchedulerPeriodicity');

    % Validate the scheduler strategy
    validateattributes(simParameters.SchedulerStrategy, {'char'}, {'nonempty', 'vector'}, 'simParameters.SchedulerStrategy');

    % Validate the PUSCH preparation time
    validateattributes(simParameters.PUSCHPrepTime, {'numeric'}, {'nonempty', 'integer', 'scalar', 'finite', '>=', 0}, 'simParameters.PUSCHPrepTime');

    % Validate the moving average parameter to calculate average data rate
    % of a UE
    if(strcmp(simParameters.SchedulerStrategy, 'PF'))
        validateattributes(simParameters.MovingAvgDataRateWeight, {'numeric'}, {'nonempty', 'scalar', '>=', 0, '<=', 1}, 'simParameters.MovingAvgDataRateWeight');
    end

    % Validate the flag to enable/disable HARQ
    if islogical(simParameters.EnableHARQ)
        % To support true/false
        validateattributes(simParameters.EnableHARQ, {'logical'}, {'nonempty', 'scalar'}, 'simParameters.EnableHARQ');
    else
        % To support 0/1
        validateattributes(simParameters.EnableHARQ, {'numeric'}, {'nonempty', 'integer', 'scalar', '>=', 0, '<=', 1}, 'simParameters.EnableHARQ');
    end

    % Validate the RBG allocation limit for an UE
    validateattributes(simParameters.RBAllocationLimitUL, {'numeric'}, {'nonempty', 'integer', 'scalar', '>', 0, '<=',simParameters.NumRBs}, 'simParameters.RBAllocationLimitUL');
    validateattributes(simParameters.RBAllocationLimitDL, {'numeric'}, {'nonempty', 'integer', 'scalar', '>', 0, '<=',simParameters.NumRBs}, 'simParameters.RBAllocationLimitDL');

    % Validate the maximum number of HARQ processes allowed
    validateattributes(simParameters.NumHARQ, {'numeric'}, {'nonempty', 'integer', 'scalar', '>', 0, '<=', 16}, 'simParameters.NumHARQ');

    % Valid BSR periodicity in terms of number of subframes
    validBSRPeriodicity =  [1, 5, 10, 16, 20, 32, 40, 64, 80, 128, 160, 320, 640, 1280, 2560, inf];
    % Validate the BSR periodicity
    validateattributes(simParameters.BSRPeriodicity, {'numeric'}, {'nonempty', 'scalar', '>', 0}, 'simParameters.BSRPeriodicity');
    if ~ismember(simParameters.BSRPeriodicity, validBSRPeriodicity)
        error('BSRPeriodicity ( %d ) must be one of the set (1, 5, 10, 16, 20, 32, 40, 64, 80, 128, 160, 320, 640, 1280, 2560, inf).', simParameters.BSRPeriodicity);
    end

    % Validate the channel update periodicity
    validateattributes(simParameters.ChannelUpdatePeriodicity, {'numeric'}, {'nonempty', 'finite', 'scalar', '>', 0}, 'simParameters.ChannelUpdatePeriodicity');

    % Validate the channel improvement or deterioration value
    validateattributes(simParameters.CQIDelta, {'numeric'}, {'nonempty', 'scalar', 'finite', '>=', 0}, 'simParameters.CQIDelta');

    % Validate the RB visualization flag
    if islogical(simParameters.RBVisualization)
        % To support true/false
        validateattributes(simParameters.RBVisualization, {'logical'}, {'nonempty', 'scalar'}, 'simParameters.RBVisualization');
    else
        % To support 0/1
        validateattributes(simParameters.RBVisualization, {'numeric'}, {'nonempty', 'integer', 'scalar', '>=', 0, '<=', 1}, 'simParameters.RBVisualization');
    end
    
    % Validate the CQI visualization flag
    if islogical(simParameters.CQIVisualization)
        % To support true/false
        validateattributes(simParameters.CQIVisualization, {'logical'}, {'nonempty', 'scalar'}, 'simParameters.CQIVisualization');
    else
        % To support 0/1
        validateattributes(simParameters.CQIVisualization, {'numeric'}, {'nonempty', 'integer', 'scalar', '>=', 0, '<=', 1}, 'simParameters.CQIVisualization');
    end

    % Validate the metric plot step-count. Number of steps must be at least equal to number of slots in simulation
    validateattributes(simParameters.NumMetricsSteps, {'numeric'}, {'nonempty', 'integer', 'scalar', '>', 0, '<=', numSlotsSim}, 'simParameters.NumMetricsSteps');

    % Validate the success probability expression
    validateattributes(simParameters.CQISuccessProb, {'char'}, {'nonempty'}, 'simParameters.CQISuccessProb');
end
