%% 5G NR CQI Reporting
% The example performs channel quality indicator (CQI) calculation, as defined in TS 38.214 Section 5.2.2, over a clustered delayed line (CDL) channel. The example evaluates the accuracy of the CQI reported by using the practical channel estimation.
%% Introduction
% CQI is an indicator of channel quality. The UE reports the CQI index to the gNB, as a part of channel state information (CSI) by using the CSI-RS for measurement.
% The CQI index is a value between 0 to 15. It provides information about the highest modulation scheme and code rate suitable for the downlink transmission to achieve the required block error rate (BLER) condition. TS 38.214 Section 5.2.2.1 defines two conditions for maximum allowable BLER, which are obtained from the higher layer parameter 'cqi-Table'. This example considers 0.1 BLER condition and cqi-Table as ‘table-1’ (TS 38.214 Table 5.2.2.1-2).
% The example performs CQI reporting for a single input single output (SISO) scenario over a CDL channel with delay profile CDL-C and delay spread 300e-9 sec. The CQI value obtained by using practical channel estimation is compared with CQI obtained by using perfect channel estimation.
%% CQI versus SINR table
% Given the signal to interference and noise ratio (SINR), the CQI is obtained by using the pre-calculated lookup tables of CQI versus SINR. The lookup table is calculated to provide a CQI resulting in a maximum of 0.1 BLER for the corresponding SINR. It considers the number of transmission layers, the number of transmit and receive antennas, and the channel conditions.
% When CQI reporting is performed, the lookup tables are taken as reference. The observed SINR is compared with the table and the highest CQI value for the SINR, where the BLER is less than 0.1, is reported.
% The lookup tables in the example are created with the help of the NR PDSCH Throughput example. Simulations for downlink shared channel throughput are performed considering the modulation scheme and code rate which corresponds to each CQI, across a finite range of signal to noise ratio (SNR) values. The SINR value observed at the receiver, where a 0.1 BLER is achieved, gets mapped against each CQI index. The lookup tables in the example are created for CDL-C channel by using both practical channel estimation and perfect channel estimation.
% The following figure represents the BLER simulation results performed across a finite range of SNRs, for all CQI values. Each curve represents the BLER corresponding to the CQI value. The simulation is performed for 100 frames, with the step size of 0.5 dB and TS 38.214 Table 5.2.2.1-2 as cqi-table. The simulations consider the practical channel estimation method, with 'AveragingWindow' as [5 1], for a SISO scenario. 'AveragingWindow' specifies the number of adjacent reference symbols in the frequency domain and time domain, over which averaging must be performed in the channel estimation procedure. The SINR at the receiver, where 0.1 BLER is observed, is shown for each CQI value in the data tip.
%
%% Initialize Configuration Objects
% Create a carrier configuration object representing a 20MHz carrier with subcarrier spacing 30 kHz.

carrier = nrCarrierConfig;
carrier.SubcarrierSpacing = 30;
carrier.NSizeGrid = 52;

% Create a bandwidth part structure with the active bandwidth part (BWP) size and start fields. BWP start field is relative to common resource block 0 (CRB 0).

bwp.NSizeBWP = 41;
bwp.NStartBWP = carrier.NStartGrid; % Consider the start of BWP aligning with the start of the carrier

% Create a CSI-RS configuration object with one non-zero-power NZP-CSI-RS resource occupying entire BWP.

csirs = nrCSIRSConfig;
csirs.RowNumber = 2;        % Possible row numbers for single transmit antenna case are 1 and 2
csirs.NumRB = bwp.NSizeBWP;
csirs.RBOffset = bwp.NStartBWP - carrier.NStartGrid;
csirs.CSIRSPeriod = [4 0];
csirs.SymbolLocations = 0;

%  Configure the number of transmit and receive antennas, considering a SISO scenario.

nTxAnts = 1;
nRxAnts = 1;

%
%% Generate CSI-RS Symbols and Indices for One Frame and Add to Grid
% This section generates the CSI-RS symbols and indices for the specified carrier and CSI-RS configuration parameters.
% Initialize an empty transmission grid.

txGrid = [];

% Calculate the carrier grid dimensions and create an empty grid for one slot. Perform resource element mapping.

K = carrier.NSizeGrid*12;                       % Number of subcarriers
L = carrier.SymbolsPerSlot;                     % Number of OFDM symbols per slot
ports = max(csirs.NumCSIRSPorts);               % Number of antenna ports
gridSize = [K L ports];                         % Size of grid for one slot
txSlotGrid = complex(zeros(gridSize));

for slotIdx = 0:carrier.SlotsPerFrame - 1
    carrier.NSlot = slotIdx;

    csirsInd = nrCSIRSIndices(carrier,csirs);
    csirsSym = nrCSIRS(carrier,csirs);              % Generate CSI-RS symbols with slot periodicity 4 and offset 0

    % Placing the CSI-RS in the transmit grid
    txSlotGrid(csirsInd) = csirsSym;                % Mapping CSI-RS symbols to the current slot grid
    % txGrid for one frame is generated by concatenating slot by slot
    txGrid = [txGrid txSlotGrid];                   %#ok<AGROW>
end

%
%% Perform OFDM Modulation
% Create a gNB structure with the required fields to create the transmit waveform.

gnb.NRB = carrier.NSizeGrid;
gnb.SubcarrierSpacing = carrier.SubcarrierSpacing;
gnb.CyclicPrefix = carrier.CyclicPrefix;

% Perform OFDM modulation to generate the time-domain waveform.

[txWaveform,OFDMInfo] = hOFDMModulate(gnb,txGrid);

%
%% Transmit Waveform Through Channel and Add AWGN to the Received Waveform
% Consider a CDL channel with delay profile CDL-C and delay spread 300e-9 sec.
% Create a channel object with specified configurations.

channel = nrCDLChannel;
channel.DelayProfile = 'CDL-C';
channel.DelaySpread = 300e-9;
% Turn the overall number of antennas into a specific antenna panel array geometry
[channel.TransmitAntennaArray.Size, channel.ReceiveAntennaArray.Size] = ...
    hArrayGeometry(nTxAnts,nRxAnts);
channel.SampleRate = OFDMInfo.SamplingRate;

% Get channel information
chInfo = info(channel);
maxChDelay = ceil(max(chInfo.PathDelays*channel.SampleRate)) + chInfo.ChannelFilterDelay; 

% Append zeros at the end of the transmitted waveform to flush channel content. These zeros take into account any delay introduced in the channel. This is a mix of multipath delay and implementation delay. This value may change depending on the sampling rate, delay profile and delay spread. Append zeros at the end of the transmitted waveform to flush channel content. These zeros take into account any delay introduced in the channel. This is a mix of multipath delay and implementation delay. This value may change depending on the sampling rate, delay profile and delay spread.

txWaveform = [txWaveform; zeros(maxChDelay, size(txWaveform,2))];

% Transmit the waveform through the channel to obtain the received time-domain waveform and path gains.

[rxWaveform,pathGains,sampleTimes] = channel(txWaveform);

% Generate and add AWGN to the received waveform.

SNRdB = 15;                                              % in dB
SNR = 10^(SNRdB/20);                                     % Linear value
N0 = 1/(sqrt(2.0*nRxAnts*double(OFDMInfo.Nfft))*SNR);    % Noise variance
rng('default');
noise = N0*complex(randn(size(rxWaveform)),randn(size(rxWaveform)));
rxWaveform = rxWaveform + noise;

%
%% Perform CQI Reporting with Practical Channel Estimation
% This section shows how CQI reporting with practical channel estimation is performed.
%% Perform Timing Estimate, Offset Correction, and OFDM Demodulation

% Generate reference grid for timing estimation
refGrid = txGrid;     % Consider reference grid as txGrid since we only have CSI-RS in the txGrid
initialNSlot = 0;     % To perform timing estimate, note the first NSlot value where the carrier starts
offsetPractical = 0;
[t,mag] = nrTimingEstimate(rxWaveform,gnb.NRB,gnb.SubcarrierSpacing,initialNSlot,refGrid,'CyclicPrefix',gnb.CyclicPrefix);
offsetPractical = hSkipWeakTimingOffset(offsetPractical,t,mag);
rxWaveformPractical = rxWaveform(1+offsetPractical:end, :);

% Perform OFDM demodulation to the rxWaveform
rxGridPractical = hOFDMDemodulate(gnb,rxWaveformPractical);

%% Configure Information Required to Calculate CQI
% The information required for CQI reporting is the CQI reporting mode, the size of the subband, and the SINR lookup table for the particular channel model.
% CQI reporting modes: The CQI reporting supports wideband or subband modes, as requested by the gNB or decided by the UE. In the case of wideband mode, one CQI value is reported for the entire BWP. In the case of subband mode, one CQI value for each subband is reported as per the subband size indicated by the higher layers. The size of subband, according to Table 5.2.1.4-2 in TS 38.214 depends on the size of BWP.
% The following lookup table is computed for 90% throughput (which is equal to 0.1 BLER) over a CDL-C channel with a delay spread of 300e-9 sec by using the practical channel estimator.

cqiConfig.CQIMode  = 'Subband';  % Select one of two possible choices from 'Wideband' or 'Subband'
cqiConfig.NSBPRB   = 4;          % The size of subband in resource blocks
cqiConfig.SINR90pc = [-1.89 -0.82 0.95 2.95 4.90 7.39 8.89 ...
    11.02 13.32 14.68 16.62 18.91 21.58 24.88 29.32];

%% Perform CQI Calculation
%  The CQI values are calculated using an NZP-CSI-RS resource with slot periodicity 4 and offset 0 over one frame.

cqiPracticalPerSlot = [];
for slotIdx = 0:carrier.SlotsPerFrame - 1
    carrier.NSlot =  slotIdx;
    csirsSym = nrCSIRS(carrier,csirs);
    csirsRefInd = nrCSIRSIndices(carrier,csirs);
    if (~isempty(csirsRefInd))
        % Perform channel estimate by considering 'AveragingWindow' as [5 1]
        [Hest,nVar] = nrChannelEstimate(rxGridPractical(:,(1:carrier.SymbolsPerSlot)+(carrier.SymbolsPerSlot*slotIdx)),csirsRefInd,csirsSym,'AveragingWindow',[5 1]);

         % CQI value reported for each slot is stored in a new column
         % In subband case, a column of CQI values is reported, where each element corresponds to each subband
        cqiPracticalPerSlot(:,slotIdx+1) = hCQISelect(carrier,bwp,cqiConfig,csirsRefInd,Hest,nVar); %#ok<SAGROW>
    end
end

% Plot the practical CQI estimates for one frame
plotCQI(cqiPracticalPerSlot,'CQI Reported with Practical Channel Estimation');

%
%% Perform CQI Reporting with Perfect Channel Estimation
% This section shows how you perform CQI reporting with perfect channel estimation.
%% Perform Perfect Timing Estimate, Offset Correction, and OFDM Demodulation

pathFilters = getPathFilters(channel);                                % Get path filters for perfect channel estimation
[offsetPerfect,mag] = nrPerfectTimingEstimate(pathGains,pathFilters);
rxWaveformPerfect = rxWaveform(1+offsetPerfect:end, :);
rxGridPerfect = hOFDMDemodulate(gnb,rxWaveformPerfect);

%% Configure Required Information to Calculate CQI
% The information required for CQI reporting is the CQI reporting mode, the size of the subband, and the SINR lookup table for the particular channel model. Consider the same mode and subband size used for practical channel.

cqiConfig.CQIMode = 'Subband';   % Select one of two possible choices from 'Wideband' or 'Subband'
cqiConfig.NSBPRB  = 4;           % The size of subband in resource blocks

%  The following lookup table is computed for 90% throughput over a CDL-C channel with a delay spread of 300e-9 sec considering the perfect channel estimator

cqiConfig.SINR90pc =  [-4.73, -3.04, -0.54, 1.87, 4.17, 6.77, 8.57, 10.57,...
    12.87, 14.27, 16.17, 18.37, 20.77, 23.67, 27.07];

%% Perform CQI Calculation
% The CQI values are calculated using an NZP-CSI-RS resource with slot periodicity 4 and offset 0 over one frame.

cqiPerfectPerSlot = [];
for slotIdx = 0:carrier.SlotsPerFrame - 1
    carrier.NSlot =  slotIdx;
    csirsSym = nrCSIRS(carrier,csirs);
    csirsRefInd = nrCSIRSIndices(carrier,csirs);
    if (~isempty(csirsRefInd))
        PerfectHest = nrPerfectChannelEstimate(pathGains,pathFilters,gnb.NRB,gnb.SubcarrierSpacing,carrier.NSlot,offsetPractical,sampleTimes,gnb.CyclicPrefix);

        % Get perfect noise estimate (from the noise realization)
        noiseGrid = hOFDMDemodulate(gnb,noise(1+offsetPractical:end ,:));
        nVar = var(noiseGrid(:));

        % CQI value reported for each slot is stored in a new column
        % In subband case, a column of CQI values, one value corresponding to each subband is reported
        cqiPerfectPerSlot(:,slotIdx+1)  = hCQISelect(carrier,bwp,cqiConfig,csirsRefInd,PerfectHest(:,(1:carrier.SymbolsPerSlot) + carrier.SymbolsPerSlot*slotIdx),nVar);  %#ok<SAGROW>
    end
end

% Plot the perfect CQI estimates for one frame
plotCQI(cqiPerfectPerSlot,'CQI Reported with Perfect Channel Estimation');

%
%% Compare the CQI Values Reported in Practical and Perfect Channel Estimation Scenarios
% Obtain the CQI reporting error due to practical channel estimation error:

cqiError = cqiPerfectPerSlot - cqiPracticalPerSlot;

% Plot the error
plotCQI(cqiError,'CQI Estimation Error (Perfect vs. Practical Channel Estimation)');

%
%% Local Functions

function plotCQI(cqi,Title)
%    plotCQI(CQI,TITLE) plots the CQI values with the title
%    TITLE.

    figure()
    imagesc(cqi)
    axis xy;
    colorbar;

    title(Title);
    xlabel('Slots');
    ylabel('Subbands');
end

% Copyright 2019-2020 The MathWorks, Inc.
%