%% NR FDD Scheduling Performance Evaluation
% This example shows the scheduling functionality of the medium access control (MAC) layer of the 5G New Radio (NR) stack. The scheduling strategy assigns the physical uplink shared channel (PUSCH) and the physical downlink shared channel (PDSCH) resources to a set of static user equipment (UEs) connected to a gNB. The example uses frequency division duplexing (FDD) mode. Hence, uplink (UL) and downlink (DL) transmissions use all the slots. This example also shows the functionality of the radio link control (RLC) layer in unacknowledged mode (UM). The example abstracts the physical layer and adopts a probability-based approach by using the assumed uplink and downlink channel quality indicator (CQI) values to model packet reception failures. The performance of the scheduling strategy is evaluated in terms of achieved MAC throughput and fairness in resource sharing.
%% Introduction
% This example shows how a scheduling strategy (controlled by the gNB) assigns UL and DL resources among the UEs. The example considers the following operations at gNB and UEs that facilitate UL and DL transmissions.
%
% The schedulers (UL and DL) run every p slots to assign the UL and DL resources, where p is the configured periodicity of the scheduler. In each run, the number of slots scheduled is equal to the periodicity of scheduler run, p.
% UL Scheduler
% The first slot, among the p slots to be scheduled in a run, is the nearest upcoming slot which satisfies the PUSCH preparation time capability of UEs. For example, the figure shows the way scheduler selects the slots during two consecutive runs. It assumes scheduler periodicity (p) as 3 slots. Hence, the scheduler runs after every 3 slots, and schedules resources for 3 slots. PUSCH preparation time capability for all the UEs is assumed as greater than 1 slot (14 symbols) but less than 2 slots (28 symbols).
% Run-1: When scheduler runs at the start of Slot-A, it schedules 3 slots starting from Slot-C, because for Slot-A and Slot-B the UEs do not get enough PUSCH preparation time (UEs gets a time of 0 symbols at start of Slot-A and 14 symbols at start of Slot-B). For Slot-C, UEs get 28 symbols for PUSCH preparation and this satisfies the PUSCH preparation time capability. Hence, Slot-C, D, and E are scheduled in this run.
% Run-2: When scheduler runs at the start of Slot-D, it schedules the next 3 contiguous slots starting from Slot-F (Slot-F, G, and H).
%
% DL Scheduler
% The first slot, among the p slots to be scheduled in a run, is the immediate next slot.
% Run-1: When scheduler runs at the start of Slot-A, it schedules 3 contiguous slots Slot-B, C, and D.
% Run-2: When scheduler runs at the start of Slot-D, it schedules 3 contiguous slots Slot-E, F, and G.
%
%
% You can choose any one of the implemented scheduling strategies: proportional fair (PF), best CQI, or round robin (RR). The various supported inputs to the UL scheduler are listed along with the scheduling strategies that consider them.
%
% The control packets required are assumed to be sent out of band without the need of resources for transmission. The control packets are UL assignment, DL assignment, buffer status report (BSR), and PDSCH feedback.
% The demodulation reference signal (DM-RS) is not modeled in this example. Two symbols in a slot are kept unused across the frequency domain of the resource allocations.
% This example models:
% Slot-based UL and DL scheduling. The time-domain granularity of the UL assignment and DL assignment is one slot.
% Noncontiguous allocation of frequency-domain resources in terms of resource block groups (RBGs).
% Configurable subcarrier spacing (15, 30, 60 kHz) resulting in different slot durations.
% Asynchronous adaptive hybrid automatic repeat request (HARQ) mechanism in UL and DL.
% Multiple logical channels to support different kind of applications.
% Logical channel prioritization (LCP) to distribute the received assignment among logical channels per UE for UL and DL.
%% Scenario Configuration
% For simulation, set the following key configuration parameters.
% Simulation time
% Number of UEs
% Distance of UEs from gNB (affects the UL and DL CQI values for UEs)
% UL and DL application traffic patterns
% Logical channel configuration and its associated RLC UM entity configuration
% Periodicity of BSRs sent by UEs to inform gNB about pending buffer amount
% Scheduling strategy: PF, Best CQI, RR
% PUSCH preparation time for UEs
% Carrier bandwidth (both UL and DL) in terms of number of resource blocks (RBs)
% Subcarrier spacing: 15, 30, 60 (kHz)
% Initial UL and DL CQI values for UEs and their update mechanism
% Relation of successful reception probability with channel quality

rng('default'); % Reset the random number generator
simParameters = []; % Clear the simParameters variable

simParameters.NumFramesSim = 100; % Simulation time in terms of number of 10 ms frames
% Number of UEs in the simulation. UEs are assumed to have sequential radio
% network temporary identifiers (RNTIs) from 1 to NumUEs. If you change the
% number of UEs, ensure that the length of simParameters.UEDistance is equal to NumUEs
simParameters.NumUEs = 4;
simParameters.UEDistance = [100 250 700 750]; % Distance of UEs from gNB (in meters)

% Set the channel bandwidth to 30 MHz and subcarrier spacing (SCS) to 15
% kHz as defined in 3GPP TS 38.104 Section 5.3.2. The complete UL and
% DL bandwidth is assumed to be allotted for PUSCH and PDSCH. The
% UL and DL carriers are assumed to have symmetric channel
% bandwidth
simParameters.NumRBs = 160;
simParameters.SCS = 15;

% Configure parameters to update UL channel quality at gNB and DL
% channel quality at gNB and UE. Channel conditions are periodically
% improved or deteriorated by CQIDelta every channelUpdatePeriodicity
% seconds for all RBs of a UE. Whether channel conditions for a particular
% UE improve or deteriorate is randomly determined
% RBCQI = RBCQI +/- CQIDelta
simParameters.ChannelUpdatePeriodicity = 0.2; % In sec
simParameters.CQIDelta = 2;
% Mapping between distance from gNB (first column in meters) and maximum
% achievable UL CQI value (second column). For example, if a UE is 700
% meters away from the gNB, it can achieve a maximum CQI value of 10 as the
% distance falls within the [501, 800] meters range, as per the mapping.
% Set the distance in increasing order and the maximum achievable CQI value in
% decreasing order
simParameters.CQIvsDistance = [
    200  15;
    500  12;
    800  10;
    1000  8;
    1200  7];
% Expression to calculate reception success probability of a PUSCH and
% PDSCH TBs based on the channel quality. 'x' represents the average CQI of
% the RBs over which the PUSCH and PDSCH packets are received
simParameters.CQISuccessProb = '.5 + (x/20)';

simParameters.BSRPeriodicity = 5; % In ms
simParameters.EnableHARQ = true; % Flag to enable or disable HARQ. If disabled, there are no retransmissions
simParameters.NumHARQ = 16; % Number of HARQ processes in UEs

% Set the scheduler run periodicity in terms of number of slots. Value must
% be less than the number of slots in a 10 ms frame
simParameters.SchedulerPeriodicity = 4;
simParameters.SchedulerStrategy = 'PF'; % Supported scheduling strategies: 'PF', 'RR' and 'BestCQI'
% Moving average parameter within the range [0, 1] to calculate average
% data rate for a UE in either UL and DL. This value is used in
% the PF scheduling strategy. Parameter value closer to 1 implies more
% weight on the instantaneous data rate. Parameter value closer to 0
% implies more weight on the past data rate
% AverageDataRate = ((1 - MovingAvgDataRateWeight) * PastDataRate) + (MovingAvgDataRateWeight * InstantaneousDataRate)
simParameters.MovingAvgDataRateWeight = 0.5;
% gNB ensures that PUSCH assignment is received at UEs PUSCHPrepTime ahead
% of the transmission time
simParameters.PUSCHPrepTime = 200; % In microseconds
% Maximum RBs allotted to a UE in a slot for the UL and DL
% transmission (limit is applicable for new PUSCH and PDSCH assignments and
% not for the retransmissions)
simParameters.RBAllocationLimitUL = 100; % For PUSCH
simParameters.RBAllocationLimitDL = 100; % For PDSCH

%
% Load the logical channel configuration table containing these fields.
% RNTI - Radio network temporary identifier of a UE.
% LCID - Logical channel identifier.
% LCGID - Logical channel group identifier.
% SnFieldLength - Defines the sequence number field length. It takes either 6 or 12.
% MaxTxBufferLength - Maximum Tx buffer size (in bytes).
% ReassemblyTime - Defines the reassembly timer (in ms).
% EntityDir - Defines the RLC entity set up direction. It takes values 0, 1, or 2. The values indicate RLC entity setup in DL direction, UL direction, or both UL and DL direction respectively.
% Priority - Priority of the logical channel.
% PBR - Prioritized bit rate (in kBps).
% BSD - Bucket size duration (in ms).

load('NRFDDRLCChannelConfig.mat')
simParameters.RLCChannelConfig = RLCChannelConfig;

%
% Load the application configuration table containing these fields.
% PacketInterval - Interval between two consecutive packet generations (in ms).
% PacketSize - Size of the packet (in bytes).
% HostDevice - Defines the device (UE or gNB) on which the application is installed with the specified configuration. The device takes values 0, 1, or 2. The values indicate the application is configured on gNB side, UE side, or both UE and gNB respectively.
% RNTI - Radio network temporary identifier of a UE.
% LCID - Logical channel identifier.

load('NRFDDAppConfig.mat');
simParameters.AppConfig = AppConfig;

%
% Logging and Visualization Configuration

% The parameters CQIVisualization and RBVisualization control the display
% of these visualizations: (i) CQI visualization of RBs (ii) RB assignment
% visualization. By default, these plots are disabled. You can enable them
% by setting to 'true'
simParameters.CQIVisualization = false;
simParameters.RBVisualization = false;
% The output metrics plots are updated periodically NumMetricsSteps times within the
% simulation duration
simParameters.NumMetricsSteps = 20;
% MAT-files to write the logs into. They are used for post simulation analysis and visualization
simParameters.ParametersLogFile = 'simParameters'; % For logging the simulation parameters
simParameters.SimulationLogFile = 'simulationLogs'; % For logging the simulation logs

% Validate the simulation configuration
hNRSchedulingFDDValidateConfig(simParameters);

%
%% Derived Parameters
% Based on the primary configuration parameters, compute the derived parameters. Additionally, set some example specific constants.

simParameters.DuplexMode = 0; % FDD
% Slot duration for the selected SCS and number of slots in a 10 ms frame
slotDuration = 1/(simParameters.SCS/15); % In ms
numSlotsFrame = 10/slotDuration; % Number of slots in a 10 ms frame
numSlotsSim = simParameters.NumFramesSim * numSlotsFrame; % Number of slots in the simulation

% Periodicity of channel update in terms of number of slots
channelUpdatePeriodicitySlots = floor((simParameters.ChannelUpdatePeriodicity * 1000)/ ...
    slotDuration);

% Calculate maximum achievable CQI value for the UEs based on their distance from
% the gNB
maxUECQIs = zeros(simParameters.NumUEs, 1); % To store the maximum achievable CQI value for UEs
for ueIdx = 1:simParameters.NumUEs
    % Based on the distance of the UE from gNB, find matching row in
    % CQIvsDistance mapping
    matchingRowIdx = find(simParameters.CQIvsDistance(:, 1) > simParameters.UEDistance(ueIdx));
    if isempty(matchingRowIdx)
        maxUECQIs(ueIdx) = simParameters.CQIvsDistance(end, 2);
    else
        maxUECQIs(ueIdx) = simParameters.CQIvsDistance(matchingRowIdx(1), 2);
    end
end

% Interval at which metrics visualization updates in terms of number of
% slots. As one slot is the finest time-granularity of the simulation, make
% sure that MetricsStepSize is an integer
simParameters.MetricsStepSize = ceil(numSlotsSim / simParameters.NumMetricsSteps);
if mod(numSlotsSim, simParameters.NumMetricsSteps) ~= 0
    % Update the NumMetricsSteps parameter if NumSlotsSim is not
    % completely divisible by it
    simParameters.NumMetricsSteps = floor(numSlotsSim / simParameters.MetricsStepSize);
end

%
%% gNB and UEs Setup
% Create the gNB and UE objects, initialize the channel quality information for UEs, and set up the logical channel at gNB and UE. The helper classes hNRGNB.m and hNRUE.m create gNB and UE node respectively, containing the RLC and MAC layer. For MAC layer, hNRGNB.m uses the helper class hNRGNBMAC.m to implement the gNB MAC functionality and  hNRUE.m uses hNRUEMAC.m to implement the UE MAC functionality. Scheduler is implemented in hNRGNBMACScheduler.m. For RLC layer, both hNRGNB.m and hNRUE.m use hNRUMTransmitter.m to implement the functionality of the RLC transmitter and hNRUMReceiver.m to implement the functionality of the RLC receiver.

gNB = hNRGNB(simParameters); % Create gNB node
scheduler = hNRGNBMACScheduler(simParameters); % Create scheduler
addScheduler(gNB, scheduler); % Add scheduler to gNB

% Create the set of UE nodes
UEs = cell(simParameters.NumUEs, 1);
for ueIdx=1:simParameters.NumUEs
    UEs{ueIdx} = hNRUE(simParameters, ueIdx);
end

% Define initial UL and DL channel quality as an N-by-P matrix,
% where N is the number of UEs and P is the number of RBs in the carrier
% bandwidth. The initial value of CQI for each RB, for each UE, is given
% randomly and is limited by the maximum achievable CQI value corresponding
% to the distance of the UE from gNB
uplinkChannelQuality = zeros(simParameters.NumUEs, simParameters.NumRBs); % To store current UL CQI values on the RBs for different UEs
downlinkChannelQuality = zeros(simParameters.NumUEs, simParameters.NumRBs); % To store current DL CQI values on the RBs for different UEs
for ueIdx = 1:simParameters.NumUEs
    % Assign random CQI values for the RBs, limited by the maximum achievable CQI value
    uplinkChannelQuality(ueIdx, :) = randi([1 maxUECQIs(ueIdx)], 1, simParameters.NumRBs);
    % Update the UL CQI values at gNB
    updateChannelQualityStatus(gNB, uplinkChannelQuality(ueIdx, :), 1, ueIdx); % 1 for UL
    
    % Initially, DL and UL CQI values are assumed to be equal
    downlinkChannelQuality(ueIdx, :) = uplinkChannelQuality(ueIdx, :);
    % Update the DL CQI values at gNB and UE. The DL CQI values
    % help gNB in scheduling, and UE in packet error probability estimation
    updateChannelQualityStatus(gNB, downlinkChannelQuality(ueIdx, :), 0, ueIdx); % 0 for DL
    updateChannelQualityStatus(UEs{ueIdx}, downlinkChannelQuality(ueIdx, :));
end

% Setup logical channels
for lchInfoIdx = 1:size(simParameters.RLCChannelConfig, 1)
    rlcChannelConfigStruct = table2struct(simParameters.RLCChannelConfig(lchInfoIdx, :));
    ueIdx = simParameters.RLCChannelConfig.RNTI(lchInfoIdx);
    % Setup the logical channel at gNB and UE
    gNB.configureLogicalChannel(ueIdx, rlcChannelConfigStruct);
    UEs{ueIdx}.configureLogicalChannel(ueIdx, rlcChannelConfigStruct);
end

% Initialize buffers to exchange packets between UEs and gNB
hNRPacketExchangeBuffer(simParameters);

%
%% Processing Loop
% Simulation is run slot by slot. In each slot, these operations are executed:
% Update channel conditions: Update UL and DL CQI values at RBs for each UE based on the channel update periodicity.
% Generate application traffic: Generate DL and UL traffic as per the specified application configurations.
% Run the scheduler: If the scheduler is set to run in this slot (based on its configured periodicity), grant UL resources to UEs for transmission and assign DL resources to UEs for reception. In one scheduler run, you can schedule resources of multiple upcoming UL and DL slots. For each scheduled slot, assignments of RBs for retransmissions are performed first, followed by assignments for new transmissions if there are any RBs left. A UE is limited to a maximum of 1 PUSCH and 1 PDSCH assignment per slot.
% Control Tx (gNB): Create and send UL and DL assignments based on the assignments done by the scheduler.
% Control Rx (UE): UEs receive their respective UL and DL assignments. UEs process the assignments and store the information to be used for the PUSCH transmission and PDSCH reception.
% Data Tx (gNB): gNB sends PDSCH packets adhering to the earlier DL assignments for this slot.
% Data Tx (UE): UEs send UL MAC packets adhering to the UL assignments received in earlier slots (if any for this slot).
% Control Tx (UE): Based on the configured BSR periodicity, UEs send the BSR to convey the pending buffer amount. UEs also send the PDSCH feedback.
% Data Rx (UE): UEs receive PDSCH packet scheduled to transmit in this slot. UEs store the reception success or failure result and they later use it to send PDSCH feedback as positive acknowledgment (ACK) or negative acknowledgment (NACK). The timing of sending feedback for a PDSCH is based on feedback slot offset (k1) received in the PDSCH assignment.
% Data Rx (gNB): gNB receives PUSCH packets from the UEs scheduled to transmit in this slot. If a reception fails, create the retransmission context for the particular UE, which is used for assignments for retransmissions when the scheduler runs next.
% Control Rx (gNB): Receive BSR and PDSCH feedback.
% Logging and Visualization: The output of operations at gNB and UEs are used for logging and visualization.
%
%
%

% To store these UE metrics for each slot: throughput bytes
% transmitted, goodput bytes transmitted, and pending buffer amount bytes.
% The number of goodput bytes is calculated by excluding the
% retransmissions from the total transmissions
UESlotMetricsUL = zeros(simParameters.NumUEs, 3);
UESlotMetricsDL = zeros(simParameters.NumUEs, 3);

% To store the RLC statistics for each slot
ueRLCStats = cell(simParameters.NumUEs, 1);
gNBRLCStats = cell(simParameters.NumUEs, 1);

% Create an object for MAC (UL & DL) scheduling information visualization and logging
simSchedulingLogger = hNRSchedulingLogger(simParameters);

% To store the logical channel information associated per UE
lchInfo = repmat(struct('LCID',[],'EntityDir',[]), [simParameters.NumUEs 1]);
for ueIdx = 1:simParameters.NumUEs
    lchInfo(ueIdx).LCID = simParameters.RLCChannelConfig.LCID(simParameters.AppConfig.RNTI == ueIdx);
    lchInfo(ueIdx).EntityDir = simParameters.RLCChannelConfig.EntityDir(simParameters.AppConfig.RNTI == ueIdx);
end
% Create an object for RLC visualization and logging
simRLCLogger = hNRRLCLogger(simParameters, lchInfo);

symbolNum = 0;
% Run processing loop
for slotNum = 1:numSlotsSim

    % Update the UL and DL channel quality periodically
    if mod(slotNum, channelUpdatePeriodicitySlots) == 0
        for ueIdx = 1:simParameters.NumUEs
            uplinkCQIRBs = getChannelQualityStatus(gNB, 1, ueIdx); % Current UL CQI values
            downlinkCQIRBs = getChannelQualityStatus(gNB, 0, ueIdx); % Current DL CQI values
            % Update CQI values for RBs by randomly varying the current
            % values by CQIDelta, limited by the minimum and maximum
            % achievable CQI index value. You can customize the updating of
            % CQI values as necessary.
            updateType = [1 -1]; % Improvement/deterioration
            uplinkChannelQualityChange = updateType(randi(length(updateType)));
            downlinkChannelQualityChange = updateType(randi(length(updateType)));

            updatedCQIs = min(max(uplinkCQIRBs + simParameters.CQIDelta*uplinkChannelQualityChange, 1), maxUECQIs(ueIdx));
            % Update the UL channel quality at gNB (1 for UL)
            updateChannelQualityStatus(gNB, updatedCQIs, 1, ueIdx);

            updatedCQIs = min(max(downlinkCQIRBs + simParameters.CQIDelta*downlinkChannelQualityChange, 1), maxUECQIs(ueIdx));
            % Update the DL channel quality at gNB and UE (0 for DL)
            updateChannelQualityStatus(gNB, updatedCQIs, 0, ueIdx);
            updateChannelQualityStatus(UEs{ueIdx}, updatedCQIs);
        end
    end

    % Generate the UL and DL application traffic
    for ueIdx = 1:simParameters.NumUEs
        appConfigIndexList = find(simParameters.AppConfig.RNTI == ueIdx);
        % Receive data from upper layers based on application traffic
        % pattern
        for appIdx = 1:numel(appConfigIndexList)
            lcid = simParameters.AppConfig.LCID(appConfigIndexList(appIdx));
            % Application packet interval in terms of number of slots
            packetIntervalSlots = simParameters.AppConfig.PacketInterval(appConfigIndexList(appIdx))/slotDuration;
            if mod(slotNum, packetIntervalSlots) == 0
                packetSize = simParameters.AppConfig.PacketSize(appConfigIndexList(appIdx));
                deviceType = simParameters.AppConfig.HostDevice(appConfigIndexList(appIdx));
                % If the packet size is more than 9000 bytes, segment it
                % into multiples of 9000. In this case, the last segmented
                % packet need not be of 9000 bytes
                numRLCSDUs = ceil(packetSize / 9000);
                rlcSDU = randi([0 255], 9000, 1);
                for rlcSDUIdx = 1:numRLCSDUs
                    if (rlcSDUIdx == numRLCSDUs) && (mod(packetSize, 9000) ~= 0)
                        rlcSDU = randi([0 255], mod(packetSize, 9000), 1);
                    end
                    if (deviceType == 1) || (deviceType == 2)
                        % Send UL application traffic to UE RLC layer
                        receiveRLCSDU(UEs{ueIdx}, ueIdx, lcid , rlcSDU);
                    end
                    if (deviceType == 0) || (deviceType == 2)
                        % Send DL application traffic to gNB RLC layer
                        receiveRLCSDU(gNB, ueIdx, lcid , rlcSDU);
                    end
                end
            end
        end
    end

    % Run the gNB scheduler. Check if the scheduler is set to run in this
    % slot. If yes, schedule the resources and send the corresponding
    % UL assignments and DL assignments to UEs
    [resourceAssignmentsUL, resourceAssignmentsDL] = runSchedulerFDD(gNB);

    % Check if UL/DL assignments are done
    if ~isempty(resourceAssignmentsUL) || ~isempty(resourceAssignmentsDL)
        % Construct and send UL assignments and DL assignments to UEs
        controlTx(gNB, resourceAssignmentsUL, resourceAssignmentsDL);
    end

    % Run the Rx operation at UEs for simultaneous reception of control
    % packets
    for ueIdx = 1:simParameters.NumUEs
        controlRx(UEs{ueIdx}); % Receive UL assignments and DL assignments, if any sent by gNB
    end

    % Send the PDSCH packets, if any scheduled
    dataTx(gNB);
    % Read throughput and goodput bytes sent for each UE
    [UESlotMetricsDL(:, 1), UESlotMetricsDL(:, 2)] = getCurrTTIBytes(gNB);
    UESlotMetricsDL(:, 3) = getBufferStatus(gNB); % Read pending buffer (in bytes) on gNB

    % Send the UL packets, if any scheduled
    for ueIdx = 1:simParameters.NumUEs
        % UE sends the PUSCH packet if it is a Tx slot (as conveyed by gNB in earlier received UL assignments)
        dataTx(UEs{ueIdx});
        % Read throughput and goodput bytes transmitted for this UE in the current TTI (for logging purpose)
        [UESlotMetricsUL(ueIdx, 1), UESlotMetricsUL(ueIdx, 2)] = getCurrTTIBytes(UEs{ueIdx});
        UESlotMetricsUL(ueIdx, 3) = getBufferStatus(UEs{ueIdx}); % Read pending buffer (in bytes) on UE
        % UE sends BSR packet and PDSCH ACK/NACK
        controlTx(UEs{ueIdx});
    end

    rxResultUEsDL = cell(simParameters.NumUEs, 1);
    for ueIdx = 1:simParameters.NumUEs
        % Receive the PDSCH packets from gNB that are scheduled to
        % receive in this slot. Return the information of Rx
        % success/failure
        rxResultUEsDL{ueIdx} = dataRx(UEs{ueIdx});
    end

    % Receive the PUSCH packets from UEs that are scheduled to receive in
    % this slot. Return the information of Rx success/failure
    rxResultUEsUL = dataRx(gNB);
    % Run the Rx operation at gNB for simultaneous reception of control
    % packets
    controlRx(gNB); % Receive BSR and PDSCH ACK/NACK

    HARQProcessStatusUL = zeros(simParameters.NumUEs, simParameters.NumHARQ); % To store last sent HARQ process NDI flag
    HARQProcessStatusDL = zeros(simParameters.NumUEs, simParameters.NumHARQ); % To store last sent HARQ process NDI flag
    % Get RLC statistics, HARQ process context, and channel quality
    % information for logging purpose
    for ueIdx = 1:simParameters.NumUEs
        ueRLCStats{ueIdx} = getRLCStatistics(UEs{ueIdx}, ueIdx);
        gNBRLCStats{ueIdx} = getRLCStatistics(gNB, ueIdx);

        % Read the last received NDI flags for UE HARQ processes (for
        % logging purpose)
        HARQProcessStatusUL(ueIdx, :) = getLastNDIFlagHarq(UEs{ueIdx}, 1); % 1 for UL
        HARQProcessStatusDL(ueIdx, :) = getLastNDIFlagHarq(UEs{ueIdx}, 0); % 0 for DL

        % Read the UL channel quality at gNB for each of the UEs (For
        % logging purpose)
        uplinkChannelQuality(ueIdx,:) = getChannelQualityStatus(gNB, 1, ueIdx); % 1 for UL
        % Read the DL channel quality at gNB for each of the UEs (For
        % logging purpose)
        downlinkChannelQuality(ueIdx,:) = getChannelQualityStatus(gNB, 0, ueIdx); % 0 for DL
    end
    logRLCStats(simRLCLogger, ueRLCStats, gNBRLCStats);
    % Update scheduling logs based on the current slot run of UEs and gNB.
    % Logs are updated in each slot, RB grid visualizations are updated
    % every frame, and metrics plots are updated every metricsStepSize
    % slots
    logScheduling(simSchedulingLogger, symbolNum + 1, resourceAssignmentsUL, UESlotMetricsUL, uplinkChannelQuality, HARQProcessStatusUL, rxResultUEsUL, 1);
    logScheduling(simSchedulingLogger, symbolNum + 1, resourceAssignmentsDL, UESlotMetricsDL, downlinkChannelQuality, HARQProcessStatusDL, rxResultUEsDL, 0);

    % RB assignment visualization (if enabled)
    if simParameters.RBVisualization
        if mod(slotNum, numSlotsFrame) == 0
            plotRBGrids(simSchedulingLogger);
        end
    end

    % CQI grid visualization (if enabled)
    if simParameters.CQIVisualization
        if mod(slotNum, numSlotsFrame) == 0
            plotCQIRBGrids(simSchedulingLogger);
        end
    end

    % Plot scheduler metrics and RLC metrics visualization at slot
    % boundary, if the update periodicity is reached
    if mod(slotNum, simParameters.MetricsStepSize) == 0
        plotMetrics(simSchedulingLogger);
        plotMetrics(simRLCLogger);
    end

    % Advance timer ticks for gNB and UEs by the number of symbols per slot
    advanceTimer(gNB, 14);
    for ueIdx = 1:simParameters.NumUEs
        advanceTimer(UEs{ueIdx}, 14);
    end

    % Symbol number in the simulation
    symbolNum = symbolNum + 14; 
end
% Get MAC scheduling logs
[dlTimeStepLogs, ulTimeStepLogs] = getSchedulingLogs(simSchedulingLogger);
schedulingLogs = getGrantLogs(simSchedulingLogger); % Scheduling assignments log
% Get RLC statistics logs
rlcLogs = getRLCLogs(simRLCLogger);

%
%% Simulation Visualization
% The five types of run-time visualization shown are:
% Display of CQI values for UEs over the PUSCH/PDSCH bandwidth: For details, see the 'Channel Quality Visualization' figure in NR PUSCH FDD Scheduling example.
% Display of resource grid assignment to UEs: The 2D time-frequency grid shows the resource allocation to the UEs. It updates every 10 ms (frame length), and shows the RB allocation to the UEs in the previous frame. For details, see the 'Resource Grid Allocation' figure in NR PUSCH FDD Scheduling example.
% Display of UL MAC metrics plots: The four plots displayed in 'Uplink Scheduler Performance Metrics' figure represent: UL throughput (per UE and cell), UL goodput (per UE and cell), resource share percentage among UEs (out of the total UL resources) to convey the fairness of scheduling, and pending UL buffer-status of UEs to show whether UEs are getting sufficient resources. The performance metrics plots update for every metricsStepSize slots.
% Display of DL MAC metrics plots: Like UL metrics plots, 'Downlink Scheduler Performance Metrics' displays corresponding subplots for DL direction. The performance metrics plots update for every metricsStepSize slots.
% Display of RLC metrics plots: The 'RLC Throughput Visualization' figure shows the throughput of the RLC logical channel for the selected UE. The RLC metrics plot update for every metricsStepSize slots.
%% Simulation Logs
% The parameters used for simulation and the simulation logs are saved in MAT-files for post simulation analysis and visualization. The simulation parameters are saved in a MAT-file with the file name as the value of configuration parameter simParameters.ParametersLogFile. The per time step logs, scheduling assignment logs, and RLC logs are saved in the MAT-file simParameters.SimulationLogFile. After the simulation, open the file to load timeStepLogs, schedulingAssignmentLogs, and rlcLogs in the workspace.
% Time step logs: The table shows a sample time step log entry of UL and DL. Each row of the table represents a slot.
%
% Each row contains:
% Frame: Frame number.
% Slot: Slot number in the frame.
% RBG Allocation Bitmap: N-by-P bitmap matrix where N is the number of UEs and P is the number of RBGs. If an RBG is assigned to a particular UE, the corresponding bit is set to 1. For example: [ 0 0 1 1 0 1 0 1 0 1 0 0 0; 1 1 0 0 0 0 0 0 0 0 1 0 0; 0 0 0 0 1 0 1 0 1 0 0 1 1; 0 0 0 0 0 0 0 0 0 0 0 0 0] implies that bandwidth has 13 RBGs and UE-1 is assigned RBG indices: 2, 3, 5, 7, 9; UE-2 is assigned RBG indices: 0, 1, 10; UE-3 is assigned RBG indices: 4, 6, 8, 11, 12; and UE-4 is not assigned any RBG.
% MCS: Row vector of length N where N is the number of UEs. Each value corresponds to the modulation and coding scheme (MCS) index for the PUSCH/PDSCH transmission. For example: [10 12 8 -1] implies that only UE-1, UE-2, and UE-3 are assigned resources for this slot and they use MCS values 10, 12, and 8 respectively.
% HARQ Process: Row vector of length N where N is the number of UEs. The value is HARQ process ID used by UE for the PUSCH transmission or used by gNB for PDSCH transmission. For example: [0 3 6 -1] implies that only UE-1, UE-2, and UE-3 are assigned resources for this slot and they use HARQ process ID 0, 3, and 6 respectively.
% NDI: Row vector of length N where N is the number of UEs. The value is NDI flag value in grant for PUSCH/PDSCH transmission. For example: [0 0 1 -1] implies that only UE-1, UE-2, and UE-3 are assigned resources for this slot and NDI flag values (to decide whether to do a new transmission or a retransmission) are 0, 0, and 1 respectively.
% Tx Type: Transmission type (new transmission or retransmission). Row vector of length N where N is the number of UEs. The values specified are either 'newTx', 'reTx', or 'noTx'. 'noTx' implies that the UE is not allotted PUSCH/PDSCH resources. For example: ['newTx' 'newTx' 'reTx' 'noTx'] implies that only UE-1, UE-2, and UE-3 are assigned resources for this slot. UE-1 and UE-2 transmit a new packet from the specified HARQ process while UE-3 just retransmits the packet in the buffer of the specified HARQ process.
% CQI for UEs: N-by-P matrix where N is the number of UEs and P is the number of RBs in the bandwidth. A matrix element at position (i, j) corresponds to CQI value for UE with RNTI i at RB j.
% HARQ NDI Status: N-by-P matrix where N is number of UEs and P is number of HARQ processes on UEs. A matrix element at position (i, j) is the last received NDI flag at UE i for DL or UL HARQ process ID j. In the case of a new transmission, this value and the NDI flag in the PUSCH/PDSCH assignment must toggle. For example, in slot-1 of frame-5 described in the preceding log, UE-1 uses HARQ ID '0' and the last NDI flag value for HARQ ID '0' at UE-1 is 1. The NDI flag value is toggled to '0' in the PUSCH assignment to indicate a new transmission.
% Throughput Bytes: Row vector of length N where N is the number of UEs. The values represent UL/DL MAC bytes transmitted by and for UEs in this slot. The total throughput bytes for a PUSCH/PDSCH transmission are shown in the row corresponding to first slot of the transmission.
% Goodput Bytes: Row vector of length N where N is the number of UEs. The values represent UL/DL new transmission MAC bytes transmitted by and for UEs in this slot. Like throughput, all the goodput bytes are shown in the row corresponding to first slot of the transmission.
% Buffer Status of UEs: Row vector of length N where N is the number of UEs. The values represent the amount of UL direction pending buffers at UEs (or DL direction pending buffers for UEs at gNB)
% Rx Success/Failure: Row vector of length N where N is the number of UEs. The values specified are either 'RxSuccess', 'RxFailure', or 'noRx'. 'noRx' implies no DL (or UL) scheduled. For example: ['RxSuccess' 'RxSuccess' 'RxFailure' 'noRx'] implies that packets from UE-1 and UE-2 are received successfully while the packet from UE-3 was either not received or was corrupted. UE-4 was not scheduled for this slot.
% Scheduling assignment logs: Information of all the scheduling assignments and related the information is logged in this file. The table shows sample log entries.
%
% RLC logs: Each row in the RLC logs represents a slot and contains:
% Frame: Frame number.
% Slot: Slot number in the frame.
% UE RLC statistics: N-by-P cell, where N is the product of the number of UEs and the number of logical channels, and P is the number of statistics collected. Each row represents statistics of a logical channel in a UE.
% gNB RLC statistics: N-by-P cell, where N is the product of the number of UEs and the number of logical channels, and P is the number of statistics collected. Each row represents statistics of a logical channel of a UE at gNB.
% The last row contains the cumulative RLC statistics of the entire simulation.
% The table shows a sample entry in UE RLC statistics. Similar logs are available in gNB RLC statistics.
%
% Each row of the table represents a logical channel of a UE and contains:
% RNTI: Radio network temporary identifier of a UE.
% LCID: Logical channel identifier.
% TxPDCPPDU: Number of PDUs sent by packet data convergence protocol (PDCP) to RLC layer.
% TxPDCPPDUBytes: Number of bytes sent by PDCP to RLC layer.
% TxRLCSDUDiscarded: Number of RLC SDUs discarded by RLC layer due to Tx buffer overflow.
% TxRLCSDUDiscardedBytes: Number of discarded bytes by RLC layer due to Tx buffer overflow.
% TxRLCPDU: Number of PDUs sent by RLC to MAC layer.
% TxRLCPDUBytes: Number of bytes sent by RLC to MAC layer.
% RxPDCPPDU: Number of PDUs received by PDCP from RLC layer.
% RxPDCPPDUBytes: Number of bytes received by PDCP from RLC layer.
% RxRLCPDU: Number of PDUs received by RLC from MAC layer.
% RxRLCPDUBytes: Number of bytes received by RLC from MAC layer.
% RxRLCPDUDuplicate: Number of duplicate PDUs received by RLC from MAC layer.
% RxRLCPDUDuplicateBytes: Number of duplicate bytes received by RLC from MAC layer.
% RxRLCPDUDropped: Number of received PDUs dropped by RLC from MAC layer.
% RxRLCPDUDroppedBytes: Number of dropped bytes by RLC received from MAC layer.
% TimerReassemblyTimedOut: Number of times the reassembly timer expired.
% You can run the script NRSchedulingFDDPostSimVisualization.m to get a post simulation visualization of logs. In the post simulation script, you are provided with variable isLogReplay, which provides these options to visualize 'Resource Grid Allocation' and 'Channel Quality Visualization' figures.
% Set isLogReplay to true for replay of simulation logs.
% Set isLogReplay to false to analyze the details of a particular frame. In the 'Resource Grid Allocation' window, input the frame number to visualize the resource assignment for the entire frame. The frame number entered here controls the frame number for the 'Channel Quality Visualization' figure too.

save(simParameters.ParametersLogFile, 'simParameters'); % Save simulation parameters in a MAT-file
save(simParameters.SimulationLogFile, 'ulTimeStepLogs', ...
    'dlTimeStepLogs', 'schedulingLogs', 'rlcLogs'); % Save simulation logs in a MAT-file

%
%% Further Exploration
% You can modify the existing UL and DL scheduling functionality to schedule resources of a slot. The scheduling of UL resources of a slot is implemented in the scheduleULResourcesSlot function of hNRGNBMACScheduler.m. Populate the output slotULGrants of this function, with an array of valid UL scheduling assignments. slotULGrants can have one assignment per UE. Each element in this array is PUSCH grant for a UE. PUSCH grant structure has these fields:
% RNTI: RNTI of the assigned UE. This field is not part of actual grant packet sent to UEs.
% Type: Whether assignment is for new transmission ('newTx') or retransmission ('reTx') ). This field is not part of the actual grant packet sent to UEs because the NDI field is sufficient to convey that information.
% HARQIndex: Selected UL HARQ process ID.
% RBGAllocationBitmap: Frequency-domain resource assignment. A bitmap of resource-block-groups of the PUSCH bandwidth. Value 1 indicates RBG is assigned to the UE.
% StartSymbol: Start symbol of PUSCH grant resources. Keep at 0.
% NumSymbols: Number of symbols in PUSCH grant. Keep at 14.
% SlotOffset: Slot offset of PUSCH assignments with respect to the current slot.
% MCS: Selected modulation and coding scheme.
% NDI: New data indicator flag.
% RV: Redundancy version.
% The figure shows UL resources of a slot being distributed in the four PUSCH assignments. It also shows content of the first grant.
%
% Similarly, the scheduleDLResourcesSlot function of hNRGNBMACScheduler.m implements the corresponding DL functionality. PDSCH assignment has one extra field 'FeedbackSlotOffset'. The field is the slot offset of ACK/NACK transmission time from the PDSCH reception slot.
%
% Setting output for functions scheduleULResourcesSlot and scheduleDLResourcesSlot enables you to control the assignment of all the RBGs of a slot at once. You can also control the assignment of each RBG of the slot. For reference, you can see the existing scheduling strategies present in the helper functions hNRSchedulingStrategyBestCQI.m, hNRSchedulingStrategyPF.m, and hNRSchedulingStrategyRR.m. Scheduling strategy function is called once for each of the remaining RBGs, after assignments for retransmissions are completed. The function takes a structure as an input and returns the selected UE to which the RBG is assigned along with the corresponding MCS index. To write a custom scheduling strategy, use the information in the input structure to set the output. The input structure contains these fields:
% eligibleUEs: RNTI of the eligible UEs contending for the RBG.
% slotNum: Slot number in the frame to be scheduled.
% RBGIndex: RBG index in the slotNum to be scheduled.
% RBGSize: Size of the RBG in terms of number of RBs.
% cqiRBG: Channel quality of RBG. N-by-P matrix with UL CQI values for eligible UEs on different RBs of the RBG. N is the number of eligible UEs and P is RBGSize.
% pastDataRate: Row vector of length N containing the historical achieved data rate of UEs. N is the number of eligible UEs.
% bufferStatus Row vector of length N containing pending buffer status for UEs. N is the number of eligible UEs.
% mcsRBG: N-by-2 matrix containing the MCS index and corresponding efficiency (suitable as per the CQI values on the RBs of RBG). N is the number of eligible UEs. Efficiency is bits/symbol taking into consideration both the modulation scheme and coding rate.
% ttiDur: TTI duration in ms.
% UEs: RNTI of all the UEs.
% lastSelectedUE: The RNTI of UE, which was assigned to the last scheduled RBG.
% A scheduling strategy can use a subset of the preceding fields to make a decision. To implement a custom scheduling strategy, create the function file NewStrategy.m and in the scenario configuration, set the parameter simParameters.SchedulerStrategy to 'NewStrategy'. Follow the template of existing strategies for the function signature.
% Based on the chosen scheduling strategy, this example shows the assignment of UL and DL resources to multiple UEs by the gNB. The physical layer is abstracted in the example. A run-time visualization display of the resource grid gives detailed information about the RBs allocated to each UE and the HARQ process ID assigned for transmission. UL and DL scheduling performance is analyzed based on run-time plots of MAC throughput, MAC goodput, resource share fairness, and pending buffer status on the UEs. A more thorough post simulation analysis by using the saved logs gives a detailed picture of the operations happening on a per slot basis.
%% Appendix
% The example uses these helper functions and classes:
% hNRNodeBase.m: NR node base class
% hNRGNB.m: gNB node functionality
% hNRUE.m: UE node functionality
% hNRMACBase.m: NR MAC base class functionality
% hNRGNBMAC.m: gNB MAC functionality
% hNRGNBMACScheduler.m: gNB MAC scheduler functionality
% hNRUEMAC.m: UE MAC functionality
% hNRMACBSR.m: Generates buffer status report
% hNRMACBSRParser.m: Parses buffer status report
% hNRMACSubPDU.m: Generates MAC subPDU
% hNRMACPaddingSubPDU.m: Generates MAC subPDU with padding
% hNRMACMultiplex.m: Generates MAC PDU
% hNRMACPDUParser.m: Parses MAC PDU
% hNewHARQProcesses.m: Creates new HARQ process
% hUpdateHARQProcess.m: Updates HARQ process
% hPDSCHTBS.m: Calculates PDSCH transport block size
% hPUSCHTBS.m: Calculates PUSCH transport block size
% hNRUplinkGrantFormat.m: UL grant format
% hNRDownlinkGrantFormat.m: DL grant format
% hNRSchedulingStrategyBestCQI.m: Implements best CQI scheduling strategy
% hNRSchedulingStrategyPF.m: Implements proportional fair scheduling strategy
% hNRSchedulingStrategyRR.m: Implements round robin scheduling strategy
% hNRUMTransmitter.m: RLC UM transmitter functionality
% hNRUMReceiver.m: RLC UM receiver functionality
% hNRUMDataHeader.m: Generates RLC UM PDU header
% hNRUMDataPDUDecode.m: Decodes RLC PDU
% hNRUMDataPDUInfo.m: Creates RLC PDU information object
% hNRRLCBufferStatus.m: Generates RLC buffer status information object
% hNRPacketExchangeBuffer.m: Implements packet exchange buffers between UE and gNB
% hNRSchedulingLogger.m: Implements scheduling information logging and visualization functionality
% hNRRLCLogger.m: Implements RLC statistics logging and visualization functionality
% hNRSchedulingFDDValidateConfig.m: Validates simulation configuration
% Copyright 2019 The MathWorks, Inc.
%