Main Content

NR PDSCH Throughput Using Channel State Information Feedback

This reference simulation measures the physical downlink shared channel (PDSCH) throughput of a 5G new radio (NR) link. The simulation uses channel state information (CSI) feedback to adjust these transmit parameters: modulation and coding scheme (MCS), precoding matrix, and number of layers.


This figure shows the steps of PDSCH throughput calculation that this reference simulation models. The transmitter model includes transport channel coding stages and PDSCH generation. The transmission parameters are based on the CSI measurements that the receiver performs and feeds back to the transmitter. The aim of this mechanism is to adapt transmission parameters to the channel conditions. The simulation also controls the CSI feedback delay.

The simulation is based on the implementation of the NR PDSCH Throughput example with the addition of using CSI feedback from the receiver to adjust transmission parameters.

The receiver uses the channel state information reference signal (CSI-RS) to calculate these CSI elements.

  • Rank indicator (RI)

  • Precoding matrix indicator (PMI)

  • Channel quality indicator (CQI)

For more information on how to calculate RI, PMI, and CQI, see the 5G NR Downlink CSI Reporting example.

The transmitter uses the CSI elements to set these parameters.

  • Number of layers

  • Precoding matrix

  • MCS

The simulation assumes these conditions:

  • The gNodeB adapts the transmission parameters, strictly following the CSI that the user equipment (UE) reports after a configurable delay.

  • Single user link. Therefore, the number of layers and the MCS are fixed for the PSDCH physical resource block (PRB) allocation according to the RI and wideband CQI that the UE reports.

  • Subband precoding is supported.

  • FDD operation.

  • No HARQ support.

  • Fixed PDSCH PRB allocation for the length of the simulation. No scheduler exists to adapt the allocation to the channel conditions.

Simulation Length and SNR Points

Set the length of the simulation in terms of the number of 10 ms frames. To produce statistically meaningful throughput results, you must update the number of frames (NFrames) to a large number. Set the signal-to-noise ratio (SNR) points to simulate. The SNR for each layer is defined per resource element (RE) and includes the effect of signal and noise across all antennas. For an explanation of the SNR definition that this example uses, see the SNR Definition Used in Link Simulations example.

simParameters = struct();       % Clear simParameters variable to contain all key simulation parameters 
simParameters.NFrames = 2;      % Number of 10 ms frames
simParameters.SNRIn = [-10 10]; % SNR range (dB)

Channel Estimator Configuration

The logical variable PerfectChannelEstimator controls channel estimation and synchronization behavior. When this variable is set to true, the simulation uses perfect channel estimation and synchronization. When this variable is set to false, the simulation uses practical channel estimation and synchronization based on the values of the received PDSCH demodulation reference signal (DM-RS).

simParameters.PerfectChannelEstimator = true;

Simulation Diagnostics

The variable DisplaySimulationInformation controls the display of simulation information.

simParameters.DisplaySimulationInformation = true;

The DisplayDiagnostics flag enables the plotting of the error vector magnitude (EVM) per layer. This plot monitors the quality of the received signal after equalization. The EVM per layer figure shows:

  • The EVM per layer per slot, which shows the EVM evolving with time.

  • The EVM per layer per resource block, which shows the EVM in frequency.

This figure evolves with the simulation and is updated with each slot. Typically, low SNR or channel fades can result in decreased signal quality (high EVM). The channel affects each layer differently. Therefore, the EVM values can differ across layers.

simParameters.DisplayDiagnostics = false;

Carrier and PDSCH Configuration

Set waveform type, PDSCH numerology (subcarrier spacing (SCS) and cyclic prefix (CP) type), and other transmission configuration parameters.

% SCS carrier parameters
simParameters.Carrier = nrCarrierConfig;         % Carrier resource grid configuration
simParameters.Carrier.NSizeGrid = 52;            % Bandwidth in number of resource blocks
simParameters.Carrier.SubcarrierSpacing = 15;    % 15, 30, 60, 120 (kHz)
simParameters.Carrier.CyclicPrefix = 'Normal';   % 'Normal' or 'Extended' (Extended CP is relevant for 60 kHz SCS only)
simParameters.Carrier.NCellID = 1;               % Cell identity

% PDSCH/DL-SCH parameters
simParameters.PDSCH = nrPDSCHConfig;      % This PDSCH definition is the basis for all PDSCH transmissions in the BLER simulation
simParameters.PDSCHExtension = struct();  % This structure is to hold additional simulation parameters for the DL-SCH and PDSCH

% Define PDSCH time resource allocation. Reserve the first 2 OFDM symbols
% for PDCCH transmissions.
symAlloc = [2 simParameters.Carrier.SymbolsPerSlot-2];      % Starting symbol and number of symbols of each PDSCH allocation
simParameters.PDSCH.SymbolAllocation = symAlloc;  
simParameters.PDSCH.MappingType = 'A';                      % PDSCH mapping type ('A'(slot-wise),'B'(non slot-wise))

% Define PDSCH frequency resource allocation per slot to be full grid 
simParameters.PDSCH.PRBSet = 0:simParameters.Carrier.NSizeGrid-1;

% Scrambling identifiers
simParameters.PDSCH.NID = simParameters.Carrier.NCellID;
simParameters.PDSCH.RNTI = 1;

% DM-RS and antenna port configuration (TS 38.211 Section
% DM-RS port set is dynamic and based on CSI feedback (RI)
simParameters.PDSCH.DMRS.DMRSTypeAPosition = 2;      % Mapping type A only. First DM-RS symbol position (2,3)
simParameters.PDSCH.DMRS.DMRSLength = 1;             % Number of front-loaded DM-RS symbols (1(single symbol),2(double symbol))
simParameters.PDSCH.DMRS.DMRSAdditionalPosition = 2; % Additional DM-RS symbol positions (max range 0...3)
simParameters.PDSCH.DMRS.DMRSConfigurationType = 2;  % DM-RS configuration type (1,2)
simParameters.PDSCH.DMRS.NumCDMGroupsWithoutData = 3;

% Additional simulation and DL-SCH related parameters
% PDSCH PRB bundling (TS 38.214 Section
simParameters.PDSCHExtension.PRGBundleSize = 2;     % 2, 4, or [] to signify "wideband"

% LDPC decoder parameters
% Available algorithms: 'Belief propagation', 'Layered belief propagation', 'Normalized min-sum', 'Offset min-sum'
simParameters.PDSCHExtension.LDPCDecodingAlgorithm = 'Normalized min-sum';
simParameters.PDSCHExtension.MaximumLDPCIterationCount = 6;

Antenna Panel Configuration

Configure the antenna panel geometry. Ng is the number of column array panels. N1 and N2 are the number of cross-polarized antenna elements in horizontal and vertical directions for each panel, respectively. Due to polarization, the overall number of antennas in a panel is 2×N1×N2. To configure single-input single-output (SISO) configurations with single-polarized antenna elements, set the panel dimensions to [1 1 1]. This simulation assumes one-row array panels.

simParameters.TransmitAntennaArray.PanelDimensions = [1 2 2]; % Ng N1 N2
simParameters.ReceiveAntennaArray.PanelDimensions = [1 2 1];  % Ng N1 N2

CSI-RS and CSI Feedback Configuration

Configure the CSI-RS. The default parameters configure an 8-port CSI-RS. The number of CSI-RS must match the transmit panel dimensions, as defined in TS 38.214 Table for Type I single-panel codebooks and Table for Type I multi-panel codebooks.

simParameters.CSIRS = nrCSIRSConfig;
simParameters.CSIRS.CSIRSType = 'nzp';
simParameters.CSIRS.RowNumber = 6; 
simParameters.CSIRS.NumRB = simParameters.Carrier.NSizeGrid - simParameters.CSIRS.RBOffset;
simParameters.CSIRS.CSIRSPeriod = [4 0];
simParameters.CSIRS.SymbolLocations = 4;
simParameters.CSIRS.SubcarrierLocations = [0,3,6,9];
simParameters.CSIRS.Density = 'one';

disp(['Number of CSI-RS ports: ' num2str(simParameters.CSIRS.NumCSIRSPorts) '.'])
Number of CSI-RS ports: 8.
csirsCDMLengths = getCSIRSCDMLengths(simParameters.CSIRS);

Specify the parameters required for the CQI, PMI, and RI report. For more information on the report configuration, see the 5G NR Downlink CSI Reporting example. The antenna panel dimensions specify the number of antenna elements in horizontal (N1) and vertical (N2) directions and number of panels (Ng).

simParameters.CSIReportConfig = struct();
simParameters.CSIReportConfig.Period = [4 3];                       % Peridocity and offset of the CSI report in slots
simParameters.CSIReportConfig.CodebookType = 'Type1SinglePanel' ; % 'Type1SinglePanel','Type1MultiPanel','Type2'
simParameters.CSIReportConfig.CQIMode = 'Wideband';       % 'Wideband','Subband'
simParameters.CSIReportConfig.PMIMode = 'Subband';       % 'Wideband','Subband'
simParameters.CSIReportConfig.SubbandSize = 4;   % Subband size in RB for PMI report
simParameters.CSIReportConfig.PRGSize = [];                         % PRG size for the CQI calculation
simParameters.CSIReportConfig.CodebookMode = 1;
simParameters.CSIReportConfig.CodebookSubsetRestriction = [];       % Empty for no codebook subset restriction
simParameters.CSIReportConfig.i2Restriction = [];                   % Empty for no i2 restriction
simParameters.CSIReportConfig.RIRestriction = [];                   % Empty for no rank restriction  

% Configure parameters which are specific to Type II codebooks
simParameters.CSIReportConfig.NumberOfBeams = 2;      % 2,3,4
simParameters.CSIReportConfig.PhaseAlphabetSize = 8;  % 4,8
simParameters.CSIReportConfig.SubbandAmplitude = true;                    % true/false

% Configure the CSI report with the antenna panel dimensions specified
simParameters.CSIReportConfig.PanelDimensions = simParameters.TransmitAntennaArray.PanelDimensions;

% Set to empty to signal the entire carrier
simParameters.CSIReportConfig.NStartBWP = [];
simParameters.CSIReportConfig.NSizeBWP = [];

Configure the CSI processing delay in slots. For the UE, this delay is the number of time slots between the reception of the CSI-RS and the availability of the CSI feedback. For the base station (BS), the delay is the number of time slots between the reception of the CSI report and the transmission using the recommended RI, CQI, and PMI.

simParameters.UEProcessingDelay = 6;
simParameters.BSProcessingDelay = 1;

CDL Propagation Channel Parameters

The panel dimensions and cross-polarized elements define the geometry of the antenna arrays.

simParameters.DelayProfile = 'CDL-C';
simParameters.DelaySpread = 300e-9;
simParameters.MaximumDopplerShift = 5;

% Configure channel parameters
[channel,simParameters] = hChannelConfiguration(simParameters);


Set the channel sampling rate based on the OFDM signal sampling rate.

waveformInfo = nrOFDMInfo(simParameters.Carrier); % Get information about the baseband waveform after OFDM modulation step

% Update channel sample rate based on carrier information
channel.SampleRate = waveformInfo.SampleRate;

% Get the channel information
chInfo = info(channel);

Processing Loop

To determine the PDSCH throughput for each SNR point, the simulation performs these steps:

  1. Update CSI-related transmission parameters (number of layers, MCS, and precoding matrix). This step applies only when a CSI report is available.

  2. Map CSI-RS signals to the resource grid.

  3. Perform channel coding (DL-SCH) and PDSCH encoding. Map PDSCH and PDSCH DM-RS to the resource grid.

  4. OFDM modulate the generated grid.

  5. Pass the signal through a fading channel with AWGN.

  6. Perform synchronization and OFDM demodulation.

  7. Perform PDSCH DM-RS based channel estimation.

  8. Perform equalization.

  9. Decode the PDSCH and DL-SCH, and measure the PDSCH throughput.

  10. Perform CSI-RS based channel estimation.

  11. Calculate CSI: RI, PMI, and CQI.

  12. Feed the CSI back to the transmitter with the appropriate delay.

% Array to store the maximum throughput for all SNR points
maxThroughput = zeros(length(simParameters.SNRIn),1); 
% Array to store the simulation throughput for all SNR points
simThroughput = zeros(length(simParameters.SNRIn),1);

% Create DL-SCH encoder and decoder system objects to perform transport
% channel encoding and decoding
encodeDLSCH = nrDLSCH;
decodeDLSCH = nrDLSCHDecoder;
decodeDLSCH.LDPCDecodingAlgorithm = simParameters.PDSCHExtension.LDPCDecodingAlgorithm;
decodeDLSCH.MaximumLDPCIterationCount = simParameters.PDSCHExtension.MaximumLDPCIterationCount;

% Ignore number of panels if codebook Type I single panel or Type II
if any(strcmpi(simParameters.CSIReportConfig.CodebookType,{'Type1SinglePanel','Type2'})) && (length(simParameters.CSIReportConfig.PanelDimensions)>2)
    simParameters.CSIReportConfig.PanelDimensions(1:end-2) = []; % Ignore number of panels if present

% Adjust XOverhead for transport block size calculation based on the CSI-RS
% allocated REs. XOverhead can be 0, 6, 12, or 18.
[~,csirsInfo] = nrCSIRSIndices(simParameters.Carrier,simParameters.CSIRS);
csirsRE = length(csirsInfo.KBarLBar{1})*length(csirsInfo.KPrime{1})*length(csirsInfo.LPrime{1});
[~,XOverhead] = quantiz(csirsRE,[0 6 12],[0 6 12 18]);
simParameters.PDSCHExtension.XOverhead = XOverhead;

% CSI feedback structure array, one structure per slot, the array contains
% info on the RI, PMI and CQI.
csiFeedback = initCSIFeedback(simParameters);

% CSI Feedback per SNIR
CSIFB = {};

maxChannelDelay = chInfo.MaximumChannelDelay;

for snrIdx = 1:numel(simParameters.SNRIn)
%parfor snrIdx = 1:numel(simParameters.SNRIn)
% To reduce the total simulation time, you can execute this loop in
% parallel by using the Parallel Computing Toolbox. Comment out the 'for'
% statement and uncomment the 'parfor' statement. If the Parallel Computing
% Toolbox is not installed, 'parfor' defaults to normal 'for' statement.
% Because parfor-loop iterations are executed in parallel in a
% nondeterministic order, the simulation information displayed for each SNR
% point can be intertwined. To switch off simulation information display,
% set the 'displaySimulationInformation' variable above to false
    % Reset the random number generator so that each SNR point experiences
    % the same noise realization
    % Take full copies of the simulation-level parameter structures so that they are not 
    % PCT broadcast variables when using parfor 
    simLocal = simParameters;
    waveinfoLocal = waveformInfo;
    % Take copies of channel-level parameters to simplify subsequent parameter referencing 
    carrier = simLocal.Carrier;
    pdsch = simLocal.PDSCH;
    pdschextra = simLocal.PDSCHExtension;
    csirs = simParameters.CSIRS;
    csiReportConfig = simParameters.CSIReportConfig;

    % Local copies to help PCT classification of variables
    csiFeedbackLocal = csiFeedback;
    encodeDLSCHLocal = encodeDLSCH;
    encodeDLSCHLocal.reset();        % Reset encoder at the start of each SNR point
    decodeDLSCHLocal = decodeDLSCH;  
    decodeDLSCHLocal.reset();        % Reset decoder at the start of each SNR point
    channelLocal = channel;
    channelLocal.reset();            % Reset channel at the start of each SNR point

    pathFilters = [];
    % Prepare simulation for new SNR point
    SNRdB = simLocal.SNRIn(snrIdx);
    fprintf('\nSimulating transmission scheme 1 (%dx%d) and SCS=%dkHz with %s channel at %gdB SNR for %d 10ms frame(s)\n', ...
        simLocal.NTxAnts,simLocal.NRxAnts,carrier.SubcarrierSpacing, ...
    % Total number of slots in the simulation period
    NSlots = simLocal.NFrames * carrier.SlotsPerFrame;

    % Obtain a PDSCH configuration and precoding matrix (wtx) to be used in
    % the transmission of the first transport blocks until CSI feedback is
    % available. This assumes perfect channel estimation.
    [Hest,nVar] = getInitialChannelEstimate(carrier,channelLocal,SNRdB);
    initCSI = hCSISelect(carrier,csirs,csiReportConfig,Hest,nVar,simLocal.PerfectChannelEstimator);
    [pdsch,tcr,wtx] = updatePDSCHConfig(carrier,pdsch,pdschextra,csiReportConfig,initCSI);

    % Update target code rate for initial transmission
    pdschextra.TargetCodeRate = tcr;
    encodeDLSCHLocal.TargetCodeRate = tcr;

    % Update CSI available to the transmitter in the first slot
    csiFeedbackLocal(1) = initCSI;
    csifbSlot = initCSI;

    % Timing offset, updated in every slot for perfect synchronization and
    % when the correlation is strong for practical synchronization
    timingOffset = 0;

    % Loop over the entire waveform length
    for nslot = 0:NSlots-1

        % Update the carrier slot numbers for new slot
        carrier.NSlot = nslot;

        % Use  the newest available CSI feedback to configure the number of
        % layers and modulation of the PDSCH and target code rate of the
        % DL-SCH.
        isNewCSI = (nslot>0) && ~isequal(csifbSlot,csiFeedbackLocal(nslot+1));
        if isNewCSI
            csifbSlot = csiFeedbackLocal(nslot+1);
            [pdsch,tcr,wtx] = updatePDSCHConfig(carrier,pdsch,pdschextra,csiReportConfig,csiFeedbackLocal(nslot+1));
            pdschextra.TargetCodeRate = tcr;
            encodeDLSCHLocal.TargetCodeRate = tcr;

        % Create an OFDM resource grid for a slot
        dlGrid = nrResourceGrid(carrier,csirs.NumCSIRSPorts);

        % CSI-RS mapping to the slot resource grid
        [csirsInd,csirsInfo] = nrCSIRSIndices(carrier,csirs);
        csirsSym = nrCSIRS(carrier,csirs);
        dlGrid(csirsInd) = csirsSym;
        csirsTransmission = ~isempty(csirsInd);

        % PDSCH reserved REs for CSI-RS
        pdsch.ReservedRE = csirsInd-1; % 0-based indices
        % PDSCH generation
        % Calculate the transport block sizes for the transmission in the slot
        [pdschIndices,pdschIndicesInfo] = nrPDSCHIndices(carrier,pdsch);
        trBlkSizes = nrTBS(pdsch.Modulation,pdsch.NumLayers,numel(pdsch.PRBSet),pdschIndicesInfo.NREPerPRB,pdschextra.TargetCodeRate,pdschextra.XOverhead);

        % Transport block generation
        for cwIdx = 1:pdsch.NumCodewords
            % New data for current process and codeword then create a new DL-SCH transport block
            trBlk = randi([0 1],trBlkSizes(cwIdx),1);

        % Encode the DL-SCH transport blocks
        RV = zeros(1,pdsch.NumCodewords);
        codedTrBlocks = encodeDLSCHLocal(pdsch.Modulation,pdsch.NumLayers, ...

        % PDSCH modulation and precoding
        pdschSymbols = nrPDSCH(carrier,pdsch,codedTrBlocks);
        [pdschAntSymbols,pdschAntIndices] = hPRGPrecode(size(dlGrid),carrier.NStartGrid,pdschSymbols,pdschIndices,wtx);
        dlGrid(pdschAntIndices) = pdschAntSymbols;        

        % PDSCH DM-RS precoding and mapping
        dmrsSymbols = nrPDSCHDMRS(carrier,pdsch);
        dmrsIndices = nrPDSCHDMRSIndices(carrier,pdsch);
        [dmrsAntSymbols,dmrsAntIndices] = hPRGPrecode(size(dlGrid),carrier.NStartGrid,dmrsSymbols,dmrsIndices,wtx);
        dlGrid(dmrsAntIndices) = dmrsAntSymbols;

        % OFDM modulation
        txWaveform = nrOFDMModulate(carrier,dlGrid);

        % Pass data through channel model. 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(maxChannelDelay,size(txWaveform,2))]; %#ok<AGROW> 
        [rxWaveform,pathGains,sampleTimes] = channelLocal(txWaveform);
        % Add AWGN to the received time-domain waveform
        % Normalize noise power by the IFFT size used in OFDM modulation,
        % as the OFDM modulator applies this normalization to the
        % transmitted waveform. Also normalize by the number of receive
        % antennas, as the channel model applies this normalization to the
        % received waveform, by default
        SNR = 10^(SNRdB/10);
        N0 = 1/sqrt(2.0*simLocal.NRxAnts*double(waveinfoLocal.Nfft)*SNR);
        noise = N0*complex(randn(size(rxWaveform)),randn(size(rxWaveform)));
        rxWaveform = rxWaveform + noise;

        if (simLocal.PerfectChannelEstimator)
            % Perfect synchronization. Use information provided by the
            % channel to find the strongest multipath component
            pathFilters = getPathFilters(channelLocal);
            [timingOffset,mag] = nrPerfectTimingEstimate(pathGains,pathFilters);
            % Practical synchronization. Correlate the received waveform
            % with the PDSCH DM-RS to give timing offset estimate 't' and
            % correlation magnitude 'mag'. The function
            % hSkipWeakTimingOffset is used to update the receiver timing
            % offset. If the correlation peak in 'mag' is weak, the current
            % timing estimate 't' is ignored and the previous estimate
            % 'offset' is used
            [t,mag] = nrTimingEstimate(carrier,rxWaveform,dmrsIndices,dmrsSymbols); 
            timingOffset = hSkipWeakTimingOffset(timingOffset,t,mag);
            % Display a warning if the estimated timing offset exceeds the
            % maximum channel delay
            if timingOffset > maxChannelDelay
                warning(['Estimated timing offset (%d) is greater than the maximum channel delay (%d).' ...
                    ' This will result in a decoding failure. This may be caused by low SNR,' ...
                    ' or not enough DM-RS symbols to synchronize successfully.'],timingOffset,maxChannelDelay);
        rxWaveform = rxWaveform(1+timingOffset:end,:);

        % Perform OFDM demodulation on the received data to recreate the
        % resource grid, including padding in the event that practical
        % synchronization results in an incomplete slot being demodulated
        rxGrid = nrOFDMDemodulate(carrier,rxWaveform);
        [K,L,R] = size(rxGrid);
        if (L < carrier.SymbolsPerSlot)
            rxGrid = cat(2,rxGrid,zeros(K,carrier.SymbolsPerSlot-L,R));

        if (simLocal.PerfectChannelEstimator)
            % Perfect channel estimation, using the value of the path gains
            % provided by the channel. This channel estimate does not
            % include the effect of transmitter precoding
            Hest = nrPerfectChannelEstimate(carrier,pathGains,pathFilters,timingOffset,sampleTimes);

            % Get perfect noise estimate (from the noise realization)
            noiseGrid = nrOFDMDemodulate(carrier,noise(1+timingOffset:end ,:));
            noiseEst = var(noiseGrid(:));

            % Get PDSCH resource elements from the received grid and 
            % channel estimate
            [pdschRx,pdschHest,~,pdschHestIndices] = nrExtractResources(pdschIndices,rxGrid,Hest);

            % Apply precoding to channel estimate
            pdschHest = hPRGPrecode(size(Hest),carrier.NStartGrid,pdschHest,pdschHestIndices,permute(wtx,[2 1 3]));
            % Practical channel estimation between the received grid and
            % each transmission layer, using the PDSCH DM-RS for each
            % layer. This channel estimate includes the effect of
            % transmitter precoding
            [Hest,noiseEst] = subbandPDSCHChannelEstimation(carrier,pdsch,pdschextra,rxGrid);

            % Get PDSCH resource elements from the received grid and
            % channel estimate
            [pdschRx,pdschHest] = nrExtractResources(pdschIndices,rxGrid,Hest);

        % Equalization
        [pdschEq,eqCSIScaling] = nrEqualizeMMSE(pdschRx,pdschHest,noiseEst);

        % Decode PDSCH physical channel
        [dlschLLRs,rxSymbols] = nrPDSCHDecode(carrier,pdsch,pdschEq,noiseEst);
        % Display EVM per layer, per slot and per RB
        if (simLocal.DisplayDiagnostics)
        % Scale LLRs
        eqCSIScaling = nrLayerDemap(eqCSIScaling); % CSI scaling layer demapping
        for cwIdx = 1:pdsch.NumCodewords
            Qm = length(dlschLLRs{cwIdx})/length(rxSymbols{cwIdx});        % bits per symbol
            eqCSIScaling{cwIdx} = repmat(eqCSIScaling{cwIdx}.',Qm,1);      % expand by each bit per symbol
            dlschLLRs{cwIdx} = dlschLLRs{cwIdx} .* eqCSIScaling{cwIdx}(:); % scale LLRs
        % Decode the DL-SCH transport channel
        decodeDLSCHLocal.TransportBlockLength = trBlkSizes;
        decodeDLSCHLocal.TargetCodeRate = tcr;
        [decbits,blkerr] = decodeDLSCHLocal(dlschLLRs,pdsch.Modulation,pdsch.NumLayers,RV);

        % Store values to calculate throughput
        simThroughput(snrIdx) = simThroughput(snrIdx) + sum(~blkerr .* trBlkSizes);
        maxThroughput(snrIdx) = maxThroughput(snrIdx) + sum(trBlkSizes);

        % CSI measurements and selection 
        if csirsTransmission
            if (simLocal.PerfectChannelEstimator)
                csiHest = Hest;
                csiNVar = noiseEst;
                % Consider only the NZP-CSI-RS symbols and indices for CSI-RS based
                % channel estimation
                csirsInd = csirsInd(csirsInd ~= 0);
                csirsSym = csirsSym(csirsSym ~= 0);

                % Calculate practical channel estimate based on CSI-RS. Use
                % a time-averaging window that covers all of the
                % transmitted CSI-RS symbols.
                [csiHest,csiNVar] = nrChannelEstimate(carrier,rxGrid, ...
            % Select RI, PMI and CQI using channel estimates
            csifbs = hCSISelect(carrier,csirs,csiReportConfig,csiHest,csiNVar,simLocal.PerfectChannelEstimator);

            % Store the selected CSI feedback for use at the transmitter.
            % The CSI feedback is subject to a delay that depends on the
            % CSI report periodicity and the UE processing delays. The slot
            % in which the CSI is available to use at the transmitter
            % depends on the BS processing delay as well.
            csiFeedbackSlot = nextCSISlot(1+nslot+simLocal.UEProcessingDelay,csiReportConfig);
            csiAvailableSlot = 1+csiFeedbackSlot+simLocal.BSProcessingDelay;
            csiFeedbackLocal(1+csiAvailableSlot) = csifbs;

        % If new CSI feedback is not available, hold the existing one.
        if isnan(csiFeedbackLocal(2+nslot).RI)
            csiFeedbackLocal(2+nslot) = csiFeedbackLocal(1+nslot);

        % Update current process with CRC error and advance to next process
        if (simLocal.DisplaySimulationInformation)

    % Store CSI feedback for each SNR point
    CSIFB{snrIdx} = csiFeedbackLocal; %#ok<SAGROW> 
    % Display the results dynamically in the command window
    if (simLocal.DisplaySimulationInformation)
    fprintf('\nThroughput(Mbps) for %d frame(s) = %.4f\n',simLocal.NFrames,1e-6*simThroughput(snrIdx)/(simLocal.NFrames*10e-3));

Simulating transmission scheme 1 (8x4) and SCS=15kHz with CDL-C channel at -10dB SNR for 2 10ms frame(s)
(  5.00%) NSlot= 0: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.419). CSI from NSlot= 0.  CSI-RS transmission. 
( 10.00%) NSlot= 1: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 15.00%) NSlot= 2: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 20.00%) NSlot= 3: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 25.00%) NSlot= 4: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.419). CSI from NSlot= 0.  CSI-RS transmission. 
( 30.00%) NSlot= 5: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 35.00%) NSlot= 6: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 40.00%) NSlot= 7: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 45.00%) NSlot= 8: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.419). CSI from NSlot= 0.  CSI-RS transmission. 
( 50.00%) NSlot= 9: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 55.00%) NSlot=10: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 60.00%) NSlot=11: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 0. 
( 65.00%) NSlot=12: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.419). CSI from NSlot= 0.  CSI-RS transmission. 
( 70.00%) NSlot=13: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 4. 
( 75.00%) NSlot=14: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 4. 
( 80.00%) NSlot=15: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 4. 
( 85.00%) NSlot=16: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.419). CSI from NSlot= 4.  CSI-RS transmission. 
( 90.00%) NSlot=17: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 8. 
( 95.00%) NSlot=18: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 8. 
(100.00%) NSlot=19: Transmission succeeded (Layers=2,Mod= QPSK,TCR=0.438,CR=0.388). CSI from NSlot= 8. 
Throughput(Mbps) for 2 frame(s) = 8.7120
Simulating transmission scheme 1 (8x4) and SCS=15kHz with CDL-C channel at 10dB SNR for 2 10ms frame(s)
(  5.00%) NSlot= 0: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.624). CSI from NSlot= 0.  CSI-RS transmission. 
( 10.00%) NSlot= 1: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 15.00%) NSlot= 2: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 20.00%) NSlot= 3: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 25.00%) NSlot= 4: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.624). CSI from NSlot= 0.  CSI-RS transmission. 
( 30.00%) NSlot= 5: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 35.00%) NSlot= 6: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 40.00%) NSlot= 7: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 45.00%) NSlot= 8: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.624). CSI from NSlot= 0.  CSI-RS transmission. 
( 50.00%) NSlot= 9: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 55.00%) NSlot=10: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 60.00%) NSlot=11: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 0. 
( 65.00%) NSlot=12: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.624). CSI from NSlot= 0.  CSI-RS transmission. 
( 70.00%) NSlot=13: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 4. 
( 75.00%) NSlot=14: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 4. 
( 80.00%) NSlot=15: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 4. 
( 85.00%) NSlot=16: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.624). CSI from NSlot= 4.  CSI-RS transmission. 
( 90.00%) NSlot=17: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 8. 
( 95.00%) NSlot=18: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 8. 
(100.00%) NSlot=19: Transmission succeeded (Layers=3,Mod=64QAM,TCR=0.650,CR=0.578). CSI from NSlot= 8. 
Throughput(Mbps) for 2 frame(s) = 58.3840


Display the measured throughput as a function of the SNR.


xlabel('SNR (dB)'); ylabel('Throughput (Mbps)'); grid on;
title(sprintf('%s (%dx%d) / NRB=%d / SCS=%dkHz', ...
              channel.DelayProfile,simParameters.NTxAnts,simParameters.NRxAnts, ...

Figure contains an axes object. The axes object with title CDL-C (8x4) / NRB=52 / SCS=15kHz contains an object of type line.

% Bundle key parameters and results into a combined structure for recording
simResults.simParameters = simParameters;
simResults.simThroughput = simThroughput;
simResults.maxThroughput = maxThroughput;

Local Functions

function [estChannelGrid,nVar] = getInitialChannelEstimate(carrier,propchannel,SNRdB)
% Obtain channel estimate before first transmission. This can be used to
% obtain a precoding matrix for the first slot.

    ofdmInfo = nrOFDMInfo(carrier);
    chInfo = info(propchannel);

    % Clone channel and get path gains and sample times for perfect timing
    % and channel estimation
    channel = clone(propchannel);
    channel.ChannelFiltering = false;
    channel.NumTimeSamples = (ofdmInfo.SampleRate/1000/carrier.SlotsPerSubframe)+chInfo.MaximumChannelDelay;
    [pathGains,sampleTimes] = channel();
    % Perfect timing synch    
    pathFilters = getPathFilters(channel);
    offset = nrPerfectTimingEstimate(pathGains,pathFilters);
    % Perfect channel estimate
    estChannelGrid = nrPerfectChannelEstimate(carrier,pathGains,pathFilters,offset,sampleTimes);

    % Noise variance (does not account for channel effects at this point.)
    nVar = 10^(-SNRdB/10)/size(estChannelGrid,3);

function plotLayerEVM(NSlots,nslot,pdsch,siz,pdschIndices,pdschSymbols,pdschEqSymbols)
% Plot EVM information

    persistent slotEVM;
    persistent rbEVM
    persistent evmPerSlot;
    maxNumLayers = 8;
    if (nslot==0)
        slotEVM = comm.EVM;
        rbEVM = comm.EVM;
        evmPerSlot = NaN(NSlots,maxNumLayers);
    [Ns,P] = size(pdschEqSymbols);
    pdschEqSym = zeros(Ns,maxNumLayers);
    pdschSym = zeros(Ns,maxNumLayers);
    pdschEqSym(:,1:P) = pdschEqSymbols;    
    pdschSym(:,1:P) = pdschSymbols;
    evmPerSlot(nslot+1,:) = slotEVM(pdschSym,pdschEqSym);
    xlabel('Slot number');
    ylabel('EVM (%)');
    legend("layer " + (1:pdsch.NumLayers),'Location','EastOutside');
    title('EVM per layer per slot');

    [k,~,p] = ind2sub(siz,pdschIndices);
    rbsubs = floor((k-1) / 12);
    NRB = siz(1) / 12;
    evmPerRB = NaN(NRB,pdsch.NumLayers);
    for nu = 1:pdsch.NumLayers
        for rb = unique(rbsubs).'
            this = (rbsubs==rb & p==nu);
            evmPerRB(rb+1,nu) = rbEVM(pdschSym(this),pdschEqSym(this));
    xlabel('Resource block');
    ylabel('EVM (%)');
    legend("layer " + (1:pdsch.NumLayers),'Location','EastOutside');
    title(['EVM per layer per resource block, slot #' num2str(nslot)]);

function validateCSIRSConfig(carrier,csirs,nTxAnts)
%   validateCSIRSConfig(CARRIER,CSIRS,NTXANTS) validates the CSI-RS
%   configuration, given the carrier specific configuration object CARRIER,
%   CSI-RS configuration object CSIRS, and the number of transmit antennas

    % Validate the number of CSI-RS ports
    if ~isscalar(unique(csirs.NumCSIRSPorts))
            'All the CSI-RS resources must be configured to have the same number of CSI-RS ports.');

    % Validate the CSI-RS and TX antenna array configuration
    if any(csirs.Ports_Options(csirs.RowNumber) ~= nTxAnts)
        rn = num2str(find(csirs.Ports_Options == nTxAnts),'%3d,');
        str = 'The number of CSI-RS ports must be equal to the number of Tx antenna elements. ';
        str = [str sprintf('For the Tx antenna array size configured, valid CSI-RS row numbers are (%s).',rn(1:end-1))];

    % Validate the CDM lengths
    if ~iscell(csirs.CDMType)
        cdmType = {csirs.CDMType};
        cdmType = csirs.CDMType;
    if (~all(strcmpi(cdmType,cdmType{1})))
            'All the CSI-RS resources must be configured to have the same CDM lengths.');
    if nTxAnts ~= csirs.NumCSIRSPorts(1)
        error('nr5g:InvalidNumTxAnts',['Number of transmit antennas (' num2str(nTxAnts)...
            ') must be equal to the number of CSI-RS ports (' num2str(csirs.NumCSIRSPorts(1)) ').']);

    % Check for the overlap between the CSI-RS indices
    csirsInd = nrCSIRSIndices(carrier,csirs,"OutputResourceFormat",'cell');
    numRes = numel(csirsInd);
    csirsIndAll = cell(1,numRes);
    ratioVal = csirs.NumCSIRSPorts(1)/prod(getCSIRSCDMLengths(csirs));
    for resIdx = 1:numRes
        if ~isempty(csirsInd{resIdx})
            grid = nrResourceGrid(carrier,csirs.NumCSIRSPorts(1));
            [~,tempInd] = nrExtractResources(csirsInd{resIdx},grid);
            if numel(tempInd)/numel(csirsInd{resIdx}) ~= ratioVal
                error('nr5g:OverlappedCSIRSREsSingleResource',['CSI-RS indices of resource '...
                    num2str(resIdx) ' must be unique. Try changing the symbol or subcarrier locations.']);
            csirsIndAll{resIdx} = tempInd(:);
            for idx = 1:resIdx-1
                overlappedInd = ismember(csirsIndAll{idx},csirsIndAll{resIdx});
                if any(overlappedInd)
                    error('nr5g:OverlappedCSIRSREsMultipleResources',['The resource elements of the '...
                        'configured CSI-RS resources must not overlap. Try changing the symbol or '...
                        'subcarrier locations of CSI-RS resource ' num2str(idx) ' and resource ' num2str(resIdx) '.']);

function cdmLengths = getCSIRSCDMLengths(csirs)
%   CDMLENGTHS = getCSIRSCDMLengths(CSIRS) returns the CDM lengths given
%   the CSI-RS configuration object CSIRS.

    CDMType = csirs.CDMType;
    if ~iscell(csirs.CDMType)
        CDMType = {csirs.CDMType};
    CDMTypeOpts = {'noCDM','fd-CDM2','CDM4','CDM8'};
    CDMLengthOpts = {[1 1],[2 1],[2 2],[2 4]};
    cdmLengths = CDMLengthOpts{strcmpi(CDMTypeOpts,CDMType{1})};

function [pdsch,tcr,wtx] = updatePDSCHConfig(carrier,pdsch,pdschExt,reportConfig,csifb)
% Update PDSCH configuration (number of layers and modulation). Return also
% the coding rate and the precoding matrices.

    % Configure number of layers based on rank
    pdsch.NumLayers = csifb.RI;

    % Configure MCS based on CQI
    cqi = csifb.CQI(1,:); % Wideband CQI
    ncw = pdsch.NumCodewords;
    cqi = max([ones(1,ncw); cqi],[],1); % map CQI 0 -> CQI 1
    mcs = hCQITables('table1',cqi);
    Qm = mcs(:,2).';
    tcr = mcs(:,3).'/1024;
    modLists = repmat({'QPSK','16QAM','64QAM','256QAM'}.',1,ncw);
    pdsch.Modulation = modLists(Qm==repmat([2 4 6 8].',1,ncw));

    % Map codebook-based precoding matrices from subbands to PRGs
    wtx = hPMISubandToPRGPrecodingMatrix(carrier,pdschExt.PRGBundleSize,reportConfig,csifb.W);


function newWtx = hPMISubandToPRGPrecodingMatrix(carrier,prgbundlesize,reportConfig,W)
% Map codebook-based precoding matrices from subbands to PRGs

    if isequal(reportConfig.PanelDimensions,[1 1])
        newWtx = 1;

    % Get PRG and PMI subband info for mapping between subbands and PRGs.
    prgInfo = hPRGInfo(carrier,prgbundlesize);
    subbandInfo = hDLPMISubbandInfo(carrier,reportConfig);

    % Initilize MIMO precoding matrix for each PRG. The number of PRGs is
    % measured from Point A.
    NPRG = prgInfo.NPRG;
    newWtx = zeros([size(W,[2 1]) NPRG]);    
    % Get the MIMO precoding matrix from the codebook for each PRG
    for prg = 1:NPRG

        % Logical index of the PRBs of this PRG
        prb = (prg == prgInfo.PRGSet); 
        % If this PRG is in the carrier
        if any(prb)
            % Map this PRG to the corresponding PMI subband to get the
            % appropriate i2 index of the codebook. This assumes that
            % carrier and BWP are of equal size.
            sb = subbandInfo.SubbandSet(prb); 

            % Get MIMO precoding matrix from codebook
            newWtx(:,:,prg) = W(:,:,sb(1)).'; 


function csi = hCSISelect(carrier,csirs,reportConfig,Hest,nVar,perfChannelEstimator)

    % The effective SINR mapping for CSI selection does not model the
    % effect of using an inaccurate (practical) channel estimate on the
    % PDSCH demodulation. Add an empirical scaling factor to the estimated
    % noise variance to compensate for this effect.
    if ~perfChannelEstimator
        nVar = nVar*1.602;

    % Calculate the RI value using channel estimate
    csirs.CSIRSPeriod = 'on';
    rankInd = hRISelect(carrier,csirs,reportConfig,Hest,nVar);
    if isnan(rankInd)
        rankInd = 1;
    [cqi,pmi,~,pmiInfo] = hCQISelect(carrier,csirs,reportConfig,rankInd,Hest,nVar);

    % Collect CSI
    csi.RI = rankInd;
    csi.CQI = cqi;
    csi.PMI = pmi;
    csi.W = pmiInfo.W;
    csi.NSlot = carrier.NSlot;


function csi = initCSIFeedback(simParameters)

    % Initialize CSI feedback struct to contain RI, PMI and CQI values
    csi = struct();
    csi.RI = nan;
    csi.CQI = nan;
    csi.PMI = struct('i1',nan,'i2',nan);
    csi.W = nan;
    csi.NSlot = nan;

    % Calculate the size of CSI feedback vector based on the number of
    % slots and the CSI report periodicity and procesing delays
    NSlots = simParameters.NFrames * simParameters.Carrier.SlotsPerFrame;
    csiFeedbackSlot = nextCSISlot(NSlots+simParameters.UEProcessingDelay,simParameters.CSIReportConfig);
    lastCSIFeedbackSlot = 1+csiFeedbackSlot+simParameters.BSProcessingDelay;

    csi = repmat(csi,1,1+lastCSIFeedbackSlot);


function [channel,simParameters] = hChannelConfiguration(simParameters)
% Create and parameterise channel object. Update simParameters with the
% appropriate antenna array sizes.

    % Calculate the total number of antenna elements including
    % polarization. If the panel dimensions are [1 1 1], assume only 1
    % polarization.
    numTxPol = 1 + any(simParameters.TransmitAntennaArray.PanelDimensions ~= 1);    
    numRxPol = 1 + any(simParameters.ReceiveAntennaArray.PanelDimensions ~= 1);
    simParameters.NTxAnts = numTxPol*prod(simParameters.TransmitAntennaArray.PanelDimensions);
    simParameters.NRxAnts = numRxPol*prod(simParameters.ReceiveAntennaArray.PanelDimensions);

    if contains(simParameters.DelayProfile,'CDL')

        channel = nrCDLChannel;

        % Tx antenna array configuration in CDL channel. The number of antenna
        % elements depends on the panel dimensions. The size of the antenna
        % array is [M,N,P,Mg,Ng]. M and N are the number of rows and columns in
        % the antenna array. P is the number of polarizations (1 or 2). Mg and
        % Ng are the number of row and column array panels respectively. Note
        % that N1 and N2 in the panel dimensions follow a different convention
        % and denote the number of columns and rows, respectively.
        M = simParameters.TransmitAntennaArray.PanelDimensions(3);
        N = simParameters.TransmitAntennaArray.PanelDimensions(2);
        Ng = simParameters.TransmitAntennaArray.PanelDimensions(1);
        channel.TransmitAntennaArray.Size = [M N numTxPol 1 Ng];
        channel.TransmitAntennaArray.ElementSpacing = [0.5 0.5 1 1]; % Element spacing in wavelengths
        channel.TransmitAntennaArray.PolarizationAngles = [-45 45];  % Polarization angles in degrees
        % Rx antenna array configuration in CDL channel
        M = simParameters.ReceiveAntennaArray.PanelDimensions(3);
        N = simParameters.ReceiveAntennaArray.PanelDimensions(2);
        Ng = simParameters.ReceiveAntennaArray.PanelDimensions(1);
        channel.ReceiveAntennaArray.Size = [M N numRxPol 1 Ng];
        channel.ReceiveAntennaArray.ElementSpacing = [0.5 0.5 1 1];  % Element spacing in wavelengths
        channel.ReceiveAntennaArray.PolarizationAngles = [0 90];     % Polarization angles in degrees

    elseif contains(simParameters.DelayProfile,'TDL')

        channel = nrTDLChannel;
        channel.NumTransmitAntennas = simParameters.NTxAnts;
        channel.NumReceiveAntennas = simParameters.NRxAnts;


        error('Channel not supported.')


    % Configure common channel parameters: delay profile, delay spread and
    % maximum Doppler shift
    channel.DelayProfile = simParameters.DelayProfile;
    channel.DelaySpread = simParameters.DelaySpread;
    channel.MaximumDopplerShift = simParameters.MaximumDopplerShift;


function csislot = nextCSISlot(nslot,csiReportConfig)
% Return the slot number of the first slot where CSI feedback can be
% reported according to the CSI report periodicity

    p = csiReportConfig.Period(1); % Slot periodicity
    o = csiReportConfig.Period(2); % Slot offset

    csislot = p*ceil((nslot-o)/p)+o;


function printStatus(NSlots,carrier,pdsch,pdschextra,blkerr,ECR,csirsTransmission,csifb)
% Print the status of the slot transmission

    ncw = pdsch.NumCodewords;
    cwLayers = floor((pdsch.NumLayers + (0:ncw-1)) / ncw);
    procstatus = [];
    for cwIdx = 1:ncw
        if blkerr
            procstatuscw = "Transmission failed";
            procstatuscw = "Transmission succeeded";
        procstatuscw = sprintf("%22s (Layers=%d,Mod=%5s,TCR=%.3f,CR=%.3f).",procstatuscw,cwLayers(cwIdx),pdsch.Modulation{cwIdx},pdschextra.TargetCodeRate(cwIdx),ECR(cwIdx));
        if (ncw>1)
            procstatus = sprintf('%s\n%s%s',procstatus,sprintf('CW%d: %s',cwIdx-1),procstatuscw);
            procstatus = procstatuscw;
    csirsStatus = [];
    if csirsTransmission
        csirsStatus = "CSI-RS transmission. ";
    csifbStatus = sprintf("CSI from NSlot=%2d. ",csifb.NSlot);

    nslot = carrier.NSlot;
    fprintf('\n(%6.2f%%) NSlot=%2d: %s',100*(nslot+1)/NSlots,nslot,join([procstatus,csifbStatus,csirsStatus]));

function [Hest, noiseEst] = subbandPDSCHChannelEstimation(carrier,pdsch,pdschExt,rxGrid)

    dmrsIndices = nrPDSCHDMRSIndices(carrier,pdsch);
    dmrsSymbols = nrPDSCHDMRS(carrier,pdsch);
    % Dimensionality information for subband channel estimation
    K = carrier.NSizeGrid * 12;
    L = carrier.SymbolsPerSlot;
    R = size(rxGrid,3);
    P = pdsch.NumLayers;
    % Get subcarrier indices 'k' used by the DM-RS, corresponding
    % PRG indices 'prg', and set of unique PRGs 'uprg'
    [k,~,~] = ind2sub([K L],dmrsIndices);
    [prg,uprg] = getPRGIndices(carrier,pdschExt,k(:,1));
    % Perform channel estimation for each PRG and layer
    Hest = zeros([K L R P]);
    prgInfo = hPRGInfo(carrier,pdschExt.PRGBundleSize);
    nVarPRGs = zeros([prgInfo.NPRG P]);
    for i = 1:numel(uprg)
        for p = 1:P
            [HPRG,nVarPRGs(uprg(i),p)] = nrChannelEstimate(rxGrid,dmrsIndices(prg==uprg(i),p),dmrsSymbols(prg==uprg(i),p),'CDMLengths',pdsch.DMRS.CDMLengths);
            Hest(:,:,:,1:p) = Hest(:,:,:,1:p) + HPRG;
    % Average noise estimate across PRGs and layers
    noiseEst = mean(nVarPRGs(uprg,:),'all');


function [prg,uprg] = getPRGIndices(carrier,pdschExt,k)
% Calculate PRG indices 'prg', and set of unique PRGs 'uprg' for subcarrier
% indices 'k'

    prgInfo = hPRGInfo(carrier,pdschExt.PRGBundleSize);
    rb = floor((k-1)/12);
    prg = prgInfo.PRGSet(rb+1);
    uprg = unique(prg).';


Related Topics