Accelerate End-to-End Simulation with Frequency-Domain Channel Modeling
This example shows how to use frequency-domain channel modeling in an end-to-end simulation using 5G Toolbox™ features. The example demonstrates this workflow by modeling an NR physical downlink shared channel (PDSCH). The steps for a physical uplink shared channel (PUSCH) simulation are similar.
Introduction
Time-domain modeling requires OFDM modulation, time-domain channel filtering of the OFDM modulated waveform, and OFDM demodulation. You then add noise to the received waveform. In contrast, frequency-domain modeling applies perfect OFDM channel state information (CSI) to the transmitted resource grid. In this case, you add noise directly to the received resource grid. Frequency-domain modeling does not require the signal to be OFDM-modulated and filtered through the channel. Therefore, frequency-domain modeling can have faster computation time than time-domain modeling. For example, a frequency-domain throughput simulation with a high number of antennas typically runs faster than an equivalent time-domain simulation. However, frequency-domain modeling is adequate only for low mobility channels where intercarrier interference is negligible compared to other impairments. To minimize the impact of intercarrier interference on the signal, the channel coherence time must be significantly larger than the OFDM symbol length. You can define the channel coherence time as inversely proportional to the maximum Doppler shift and the OFDM symbol length depends on the subcarrier spacing.
This example shows how to accelerate an NR PDSCH end-to-end simulation by using an nrCDLChannel
System object™ to perform frequency-domain channel modeling. The example also performs time-domain channel modeling for comparison.
This figure shows the link elements that this example models in the context of a 5G downlink link:
Generation of a PDSCH and corresponding demodulation reference signal (DM-RS)
MIMO precoding and mapping of the PDSCH and PDSCH DM-RS to the resource grid
Frequency-domain filtering
Addition of noise
Channel estimation
Equalization of received PDSCH symbols
Measurement of error vector magnitude (EVM)
Configure Carrier
Create a 10 MHz bandwidth carrier configuration object and initialize the random stream.
carrier = nrCarrierConfig; carrier.NSizeGrid = 52; carrier.SubcarrierSpacing = 15; rng("default") % Enable reproducible simulation results
Set Up Channel Model
Create an nrCDLChannel
object and set the simulation parameters of the channel model.
channel = nrCDLChannel;
channel.DelayProfile = 'CDL-C';
channel.DelaySpread = 300e-9;
Define the maximum Doppler shift as 5 Hz.
channel.MaximumDopplerShift = 5;
Define the antenna configurations for the transmitter and receiver and extract the numbers of individual antennas.
channel.TransmitAntennaArray.Size = [1 2 2 1 1]; channel.ReceiveAntennaArray.Size = [1 1 2 1 1]; nTxAnts = prod(channel.TransmitAntennaArray.Size); nRxAnts = prod(channel.ReceiveAntennaArray.Size);
Set the sample rate of the channel by using OFDM information from the carrier.
ofdmInfo = nrOFDMInfo(carrier); channel.SampleRate = ofdmInfo.SampleRate;
Define the signal-to-noise ratio (SNR) in decibels.
SNRdB = 10; SNR = 10^(SNRdB/10);
Map PDSCH and PDSCH DM-RS to the Resource Grid
Generate PDSCH and PDSCH DM-RS symbols. Then apply MIMO precoding and map the symbols to the resource grid. For details on this process, see Map 5G Physical Channels and Signals to the Resource Grid.
[pdsch,pdschSymbols,pdschIndices,w,resourceGrid] = pdschConfigurationAndMapping(carrier,nTxAnts);
Enable Frequency-Domain Modeling
By default, the nrCDLChannel
object uses time-domain modeling. To enable frequency-domain modeling:
Set the
ChannelFiltering
property tofalse
to obtain the channel path gains without sending a signal through the channel System object.Set the correct duration of the fading process realization by obtaining the number of time samples for a single slot. Because the cyclic prefix lengths are different at the start of each half frame, this example calculates the number of time samples on a slot-by-slot basis.
channel.ChannelFiltering = false; nSlotSamples = sum(ofdmInfo.SymbolLengths(1:ofdmInfo.SymbolsPerSlot)); channel.NumTimeSamples = nSlotSamples;
To minimize the impact of intercarrier interference on the signal, the channel coherence time must be significantly larger than the OFDM symbol length.
Calculate the OFDM symbol length in seconds for a frame length of 10 ms.
slotLength = 0.01 / carrier.SlotsPerFrame; symbolLength = slotLength / carrier.SymbolsPerSlot
symbolLength = 7.1429e-05
Calculate the channel coherence time in seconds as the reciprocal of the maximum Doppler shift.
channelCoherenceTime = 1/channel.MaximumDopplerShift
channelCoherenceTime = 0.2000
Since the coherence time is several orders of magnitude larger than the symbol length in this scenario, frequency-domain modeling is a good candidate to accelerate the simulation.
Perform Frequency-Domain Modeling
Get the channel path gains, sample times, and path filters from the nrCDLChannel
object.
[pathGains,sampleTimes] = channel(); pathFilters = getPathFilters(channel);
Use frequency-domain filtering to generate the received resource grid.
[rxGrid,ofdmChannelResponse] = hApplyFrequencyDomainChannel(carrier,pathGains,pathFilters,sampleTimes,resourceGrid);
Generate additive white Gaussian noise (AWGN) based on the SNR. Then add the noise to the received resource grid. For information on SNR calculation, see SNR Definition Used in Link Simulations.
N0 = 1 / sqrt(nRxAnts*SNR);
noiseGrid = N0*randn(size(rxGrid),'like',rxGrid);
rxGrid = rxGrid + noiseGrid;
Equalize the received PDSCH symbols. Use the OFDM channel response as a perfect channel estimate for equalization.
noiseEst = N0^2; pdschEq = equalizePDSCH(carrier,pdsch,rxGrid,w,ofdmChannelResponse,noiseEst);
Perform Time-Domain Modeling
Reset and release the channel model object. Turn channel filtering on and perform time-domain channel modeling.
reset(channel) release(channel) channel.ChannelFiltering = true;
Use OFDM modulation to create the transmitter waveform.
txWaveform = nrOFDMModulate(carrier,resourceGrid);
Pass this waveform through the channel to obtain the received waveform.
[rxWaveform,timePathGains,timeSampleTimes,timeNoise] = timeDomainChannelFiltering(txWaveform,ofdmInfo,channel,nRxAnts,SNR);
Get path filters from the nrCDLChannel object.
timePathFilters = getPathFilters(channel);
Use perfect channel and timing estimation, OFDM demodulation, and equalization to generate the received, equalized PDSCH symbols.
timePDSCHEq = timeDomainReceiver(carrier,pdsch,rxWaveform,w,timePathFilters,timePathGains,timeSampleTimes,timeNoise);
Compare Results
Visualise the received symbols in frequency-domain and time-domain modeling. The plots show that the results of frequency-domain modeling match the results of time-domain modeling.
plotSymbols(pdschEq,timePDSCHEq,pdschSymbols)
Plot the EVM to compare the differences between frequency-domain modeling and time-domain modeling in the received data. The plots show that EVM follows a similar trend with respect to the resource blocks and the OFDM symbols for both frequency-domain modeling and time-domain modeling. Both types of modeling provide a similar effect in both dimensions of the resource grid.
fig2 = figure(); subplot(1,2,1) evmPerRB_freq = plotEVM(pdsch,pdschIndices,size(resourceGrid),pdschSymbols,pdschEq,'perRB'); hold on evmPerRB_time = plotEVM(pdsch,pdschIndices,size(resourceGrid),pdschSymbols,timePDSCHEq,'perRB'); legend(["Frequency-domain modeling","Time-domain modeling"]) axis square hold off subplot(1,2,2) axis manual fill([1.5 1.5 2.5 2.5], [0 100 100 0], 'b','FaceAlpha',0.05,'EdgeColor','none'); hold on evmPerSym_freq = plotEVM(pdsch,pdschIndices,size(resourceGrid),pdschSymbols,pdschEq,'perSym'); evmPerSym_time = plotEVM(pdsch,pdschIndices,size(resourceGrid),pdschSymbols,timePDSCHEq,'perSym'); legend(["PDSCH DM-RS","Frequency-domain modeling","Time-domain modeling"]) axis square hold off set(fig2, 'Position', [0 0 1000 500])
Compare Execution Times
Compare the execution times of frequency-domain modeling and time-domain modeling. In this configuration, by running a link-level simulation for 10 frames, you can achieve a three-fold speedup when using frequency-domain modeling.
nFrames = 10; [freqExecutionTime,timeExecutionTime] = compareExecutionTime(carrier,ofdmInfo,channel,nFrames,nTxAnts,nRxAnts,SNR); for x = 1 fprintf("Frequency-domain execution time: %f seconds\n",freqExecutionTime) fprintf("Time-domain execution time: %f seconds\n",timeExecutionTime) end
Frequency-domain execution time: 2.216527 seconds
Time-domain execution time: 7.684882 seconds
Further Exploration
This example demonstrates how frequency-domain channel modeling can accelerate NR end-to-end simulations in specific cases. To learn more about when frequency-domain modeling can accelerate your simulation, try increasing the channel.MaximumDopplerShift
parameter and analyze how the results change as the channel coherence time approaches the OFDM symbol length.
You can also try applying this frequency-domain channel modeling technique to accelerate the simulation of the NR PDSCH Throughput Using Channel State Information Feedback and NR PUSCH Throughput examples.
Local functions
function [pdsch,pdschSymbols,pdschIndices,w,resourceGrid] = pdschConfigurationAndMapping(carrier,nTxAnts) pdsch = nrPDSCHConfig; pdsch.Modulation = "16QAM"; pdsch.NumLayers = 1; pdsch.PRBSet = 0:carrier.NSizeGrid-1; % Full band allocation [pdschIndices,pdschInfo] = nrPDSCHIndices(carrier,pdsch); pdschBits = randi([0 1],pdschInfo.G,1); pdschSymbols = nrPDSCH(carrier,pdsch,pdschBits); dmrsSymbols = nrPDSCHDMRS(carrier,pdsch); dmrsIndices = nrPDSCHDMRSIndices(carrier,pdsch); % Precoding weights W = fft(eye(nTxAnts))/sqrt(nTxAnts); % Unitary precoding matrix w = W(1:pdsch.NumLayers,:)/sqrt(pdsch.NumLayers); % Normalize by number of layers pdschSymbolsPrecoded = pdschSymbols*w; resourceGrid = nrResourceGrid(carrier,nTxAnts); [~,pdschAntIndices] = nrExtractResources(pdschIndices,resourceGrid); resourceGrid(pdschAntIndices) = pdschSymbolsPrecoded; % PDSCH DM-RS precoding and mapping for p = 1:size(dmrsSymbols,2) [~,dmrsAntIndices] = nrExtractResources(dmrsIndices(:,p),resourceGrid); resourceGrid(dmrsAntIndices) = resourceGrid(dmrsAntIndices) + dmrsSymbols(:,p)*w(p,:); end end function pdschEq = equalizePDSCH(carrier,pdsch,rxGrid,w,estChannelGrid,noiseEst) [pdschIndices,~] = nrPDSCHIndices(carrier,pdsch); [pdschRx,pdschHest,~,pdschHestIndices] = nrExtractResources(pdschIndices,rxGrid,estChannelGrid); pdschHest = nrPDSCHPrecode(carrier,pdschHest,pdschHestIndices,permute(w,[2 1 3])); [pdschEq,~] = nrEqualizeMMSE(pdschRx,pdschHest,noiseEst); end function [rxWaveform,timePathGains,timeSampleTimes,timeNoise] = timeDomainChannelFiltering(txWaveform,ofdmInfo,channel,nRxAnts,SNR) chInfo = info(channel); maxChDelay = chInfo.MaximumChannelDelay; % Pass this waveform through the channel and obtain a received waveform, channel path gains and channel sample times. txWaveform = [txWaveform; zeros(maxChDelay,size(txWaveform,2))]; [rxWaveform,timePathGains,timeSampleTimes] = channel(txWaveform); % Use SNR to generate time domain noise timeN0 = 1/sqrt(nRxAnts*double(ofdmInfo.Nfft)*SNR); timeNoise = timeN0*randn(size(rxWaveform),'like',rxWaveform); rxWaveform = rxWaveform + timeNoise; end function timePDSCHEq = timeDomainReceiver(carrier,pdsch,rxWaveform,w,timePathFilters,timePathGains,timeSampleTimes,timeNoise) % Get the perfect timing offset that should be applied to the received waveform. timeOffset = nrPerfectTimingEstimate(timePathGains,timePathFilters); estChannelGrid = nrPerfectChannelEstimate(carrier,timePathGains,timePathFilters,timeOffset,timeSampleTimes); % Get perfect noise estimate (from the noise realization) timeNoiseGrid = nrOFDMDemodulate(carrier,timeNoise(1+timeOffset:end ,:)); timeNoiseEst = var(timeNoiseGrid(:)); % Offset received waveform rxWaveform = rxWaveform(1+timeOffset:end,:); % Use the function nrOFDMDemodulate to generate the received resource grid. timeRxGrid = nrOFDMDemodulate(carrier,rxWaveform); timePDSCHEq = equalizePDSCH(carrier,pdsch,timeRxGrid,w,estChannelGrid,timeNoiseEst); end function plotSymbols(pdschEq,timePDSCHEq,pdschSymbols) fig1 = figure; subplot(1,2,1) plot(pdschEq(:),"o"); hold on; plot(pdschSymbols(:),"rx"); xlim([min(real(pdschEq(:)))-0.5 max(real(pdschEq(:)))+0.5]) ylim([min(imag(pdschEq(:)))-0.5 max(imag(pdschEq(:)))+0.5]) title("Received PDSCH Symbols, Equalized"); subtitle("Frequency-Domain Modeling"); xlabel("In-Phase Amplitude"); ylabel("Quadrature Amplitude"); legend(["Equalized PDSCH symbols" "Transmitted PDSCH symbols"]); axis square; hold off subplot(1,2,2) plot(timePDSCHEq(:),"o"); hold on; plot(pdschSymbols(:),"rx"); xlim([min(real(timePDSCHEq(:)))-0.5 max(real(timePDSCHEq(:)))+0.5]) ylim([min(imag(timePDSCHEq(:)))-0.5 max(imag(timePDSCHEq(:)))+0.5]) title("Received PDSCH Symbols, Equalized"); subtitle("Time-Domain Modeling"); xlabel("In-Phase Amplitude"); ylabel("Quadrature Amplitude"); legend(["Equalized PDSCH symbols" "Transmitted PDSCH symbols"]); axis square; hold off set(fig1,'Position',[0 0 1000 500]) end function evm = plotEVM(pdsch,pdschIndices,siz,pdschSymbols,pdschEq,type) rbEVM = comm.EVM; symEVM = comm.EVM; NRB = siz(1) / 12; Nsym = siz(2); evmPerRB = NaN(NRB,pdsch.NumLayers); evmPerSym = NaN(Nsym,pdsch.NumLayers); [k,l,m] = ind2sub(siz,pdschIndices); rbsubs = floor((k-1) / 12); symsubs = l; for nu = 1:pdsch.NumLayers if strcmp(type,'perRB') for rb = unique(rbsubs).' this = (rbsubs==rb & m==nu); evmPerRB(rb+1,nu) = rbEVM(pdschSymbols(this),pdschEq(this)); end evm = evmPerRB; elseif strcmp(type,'perSym') for sym = unique(symsubs).' this = (symsubs==sym & m==nu); evmPerSym(sym,nu) = symEVM(pdschSymbols(this),pdschEq(this)); end evmPerSym = rmmissing(evmPerSym); evm = evmPerSym; end end if strcmp(type,'perRB') plot(0:NRB-1,evmPerRB,'x-'); xlabel('Resource Block'); ylabel('EVM (%)'); xlim([0 NRB-1]); title('EVM per Resource Block'); elseif strcmp(type,'perSym') plot(unique(symsubs)-1,evmPerSym,'x-'); xlabel('OFDM Symbol'); ylabel('EVM (%)'); xlim([0 Nsym-1]); ylim([min(evmPerSym(:))-3 max(evmPerSym(:))+3]) title('EVM per OFDM Symbol'); end end function [freqTime,timeTime] = compareExecutionTime(carrier,ofdmInfo,channel,nFrames,nTxAnts,nRxAnts,SNR) % Reset channel object reset(channel) % Get path filters from nrCDLChannel object pathFilters = getPathFilters(channel); % Calculate the number of slots from the slots per frame nSlots = nFrames * carrier.SlotsPerFrame; tic for slot = 1:nSlots-1 carrier.NSlot = slot; [pdsch,~,~,w,pdschGrid] = pdschConfigurationAndMapping(carrier,nTxAnts); txWaveform = nrOFDMModulate(carrier,pdschGrid); [rxWaveform,timePathGains,timeSampleTimes,timeNoise] = timeDomainChannelFiltering(txWaveform,ofdmInfo,channel,nRxAnts,SNR); timePDSCHEq = timeDomainReceiver(carrier,pdsch,rxWaveform,w,pathFilters,timePathGains,timeSampleTimes,timeNoise); end timeTime = toc; reset(channel) release(channel) channel.ChannelFiltering = false; tic for slot = 1:nSlots-1 carrier.NSlot = slot; [pdsch,~,~,w,pdschGrid] = pdschConfigurationAndMapping(carrier,nTxAnts); [pathGains,sampleTimes] = channel(); [rxGrid,ofdmChannelResponse] = hApplyFrequencyDomainChannel(carrier,pathGains,pathFilters,sampleTimes,pdschGrid); N0 = 1 / sqrt(nRxAnts*SNR); noiseGrid = N0*randn(size(rxGrid),'like',rxGrid); rxGrid = rxGrid + noiseGrid; noiseEst = N0^2; pdschEq = equalizePDSCH(carrier,pdsch,rxGrid,w,ofdmChannelResponse,noiseEst); end freqTime = toc; end