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.
Introduction
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 7.4.1.1) % 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 5.1.2.3) 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. is the number of column array panels. and 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 . 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 5.2.2.2.1-2 for Type I single-panel codebooks and Table 5.2.2.2.2-1 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); validateCSIRSConfig(simParameters.Carrier,simParameters.CSIRS,simParameters.NTxAnts);
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:
Update CSI-related transmission parameters (number of layers, MCS, and precoding matrix). This step applies only when a CSI report is available.
Map CSI-RS signals to the resource grid.
Perform channel coding (DL-SCH) and PDSCH encoding. Map PDSCH and PDSCH DM-RS to the resource grid.
OFDM modulate the generated grid.
Pass the signal through a fading channel with AWGN.
Perform synchronization and OFDM demodulation.
Perform PDSCH DM-RS based channel estimation.
Perform equalization.
Decode the PDSCH and DL-SCH, and measure the PDSCH throughput.
Perform CSI-RS based channel estimation.
Calculate CSI: RI, PMI, and CQI.
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 end % 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 rng('default'); % 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, ... channelLocal.DelayProfile,SNRdB,simLocal.NFrames); % 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; end % 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); setTransportBlock(encodeDLSCHLocal,trBlk,cwIdx-1); resetSoftBuffer(decodeDLSCHLocal,cwIdx-1); end % Encode the DL-SCH transport blocks RV = zeros(1,pdsch.NumCodewords); codedTrBlocks = encodeDLSCHLocal(pdsch.Modulation,pdsch.NumLayers, ... pdschIndicesInfo.G,RV); % 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); else % 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); end end 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)); end 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])); else % 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); end % 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) plotLayerEVM(NSlots,nslot,pdsch,size(dlGrid),pdschIndices,pdschSymbols,pdschEq); end % 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 end % 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; else % 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, ... csirsInd,csirsSym,'CDMLengths',csirsCDMLengths); end % 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; end % If new CSI feedback is not available, hold the existing one. if isnan(csiFeedbackLocal(2+nslot).RI) csiFeedbackLocal(2+nslot) = csiFeedbackLocal(1+nslot); end % Update current process with CRC error and advance to next process if (simLocal.DisplaySimulationInformation) printStatus(NSlots,carrier,pdsch,pdschextra,blkerr,trBlkSizes./pdschIndicesInfo.G,csirsTransmission,csiFeedbackLocal(1+nslot)) end end % Store CSI feedback for each SNR point CSIFB{snrIdx} = csiFeedbackLocal; %#ok<SAGROW> % Display the results dynamically in the command window if (simLocal.DisplaySimulationInformation) fprintf('\n'); end fprintf('\nThroughput(Mbps) for %d frame(s) = %.4f\n',simLocal.NFrames,1e-6*simThroughput(snrIdx)/(simLocal.NFrames*10e-3)); end
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
Results
Display the measured throughput as a function of the SNR.
figure; plot(simParameters.SNRIn,1e-6*simThroughput/(simParameters.NFrames*10e-3),'o-.') xlabel('SNR (dB)'); ylabel('Throughput (Mbps)'); grid on; title(sprintf('%s (%dx%d) / NRB=%d / SCS=%dkHz', ... channel.DelayProfile,simParameters.NTxAnts,simParameters.NRxAnts, ... simParameters.Carrier.NSizeGrid,simParameters.Carrier.SubcarrierSpacing));
% 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); release(channel); 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); end 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); figure; end [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); subplot(2,1,1); plot(0:(NSlots-1),evmPerSlot,'o-'); xlabel('Slot number'); ylabel('EVM (%)'); legend("layer " + (1:pdsch.NumLayers),'Location','EastOutside'); title('EVM per layer per slot'); subplot(2,1,2); [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)); end end plot(0:(NRB-1),evmPerRB,'x-'); xlabel('Resource block'); ylabel('EVM (%)'); legend("layer " + (1:pdsch.NumLayers),'Location','EastOutside'); title(['EVM per layer per resource block, slot #' num2str(nslot)]); drawnow; end 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 % NTXANTS. % Validate the number of CSI-RS ports if ~isscalar(unique(csirs.NumCSIRSPorts)) error('nr5g:InvalidCSIRSPorts',... 'All the CSI-RS resources must be configured to have the same number of CSI-RS ports.'); end % 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))]; error(str) end % Validate the CDM lengths if ~iscell(csirs.CDMType) cdmType = {csirs.CDMType}; else cdmType = csirs.CDMType; end if (~all(strcmpi(cdmType,cdmType{1}))) error('nr5g:InvalidCSIRSCDMTypes',... 'All the CSI-RS resources must be configured to have the same CDM lengths.'); end 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)) ').']); end % 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.']); end 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) '.']); end end end end end 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}; end CDMTypeOpts = {'noCDM','fd-CDM2','CDM4','CDM8'}; CDMLengthOpts = {[1 1],[2 1],[2 2],[2 4]}; cdmLengths = CDMLengthOpts{strcmpi(CDMTypeOpts,CDMType{1})}; end 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); end function newWtx = hPMISubandToPRGPrecodingMatrix(carrier,prgbundlesize,reportConfig,W) % Map codebook-based precoding matrices from subbands to PRGs if isequal(reportConfig.PanelDimensions,[1 1]) newWtx = 1; return; end % 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)).'; end end end 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; end % Calculate the RI value using channel estimate csirs.CSIRSPeriod = 'on'; rankInd = hRISelect(carrier,csirs,reportConfig,Hest,nVar); if isnan(rankInd) rankInd = 1; end [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; end 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); end 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; else error('Channel not supported.') end % Configure common channel parameters: delay profile, delay spread and % maximum Doppler shift channel.DelayProfile = simParameters.DelayProfile; channel.DelaySpread = simParameters.DelaySpread; channel.MaximumDopplerShift = simParameters.MaximumDopplerShift; end 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; end 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"; else procstatuscw = "Transmission succeeded"; end 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); else procstatus = procstatuscw; end end csirsStatus = []; if csirsTransmission csirsStatus = "CSI-RS transmission. "; end 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])); end 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; end end % Average noise estimate across PRGs and layers noiseEst = mean(nVarPRGs(uprg,:),'all'); end 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).'; end