Measure Impact of Sub-THz Hardware Impairments on 6G Waveforms

Since R2024a

This example shows how to explore the impact of hardware impairments at sub-THz frequencies on a candidate 6G waveform. The hardware impairments are: phase noise, power amplifier (PA) nonlinearity, and a filter to limit spectral emissions outside of the channel bandwidth. The examples measures the adjacent channel power ratio (ACPR) and error vector magnitude (EVM) of the impaired waveform to demonstrate the impact of impairments.

Set System Parameters

This example models a 6G-like OFDM waveform with system parameters defined in Hexa-X Deliverable D2.3 Table 5-7 [1] to investigate the impact of hardware impairments on candidate 6G waveforms.

```carrierFrequency = 140; % GHz bandwidth = 2160; % MHz - transmission bandwidth and guardbands subcarrierSpacing = 3840; % kHz```

Set the bandwidth occupancy. This is the ratio between the transmission bandwidth and channel bandwidth. A lower bandwidth occupancy increases the size of guardbands, reducing spectral emissions at the cost of reduced spectral efficiency.

`bandwidthOccupancy = 0.8; % Ratio of transmission bandwidth to channel bandwidth`

Create a carrier configuration. Calculate the transmission bandwidth and required number of resource blocks (`NSizeGrid`) given the bandwidth occupancy.

```carrier = pre6GCarrierConfig; carrier.SubcarrierSpacing = subcarrierSpacing; channelBandwidth = bandwidth*1e6; % Bandwidth of RBs and guard carriers in Hz carrier.NSizeGrid = floor((channelBandwidth/(carrier.SubcarrierSpacing*1e3)*bandwidthOccupancy)/12); % 12 subcarriers per RB transmissionBandwidth = carrier.SubcarrierSpacing*1e3*carrier.NSizeGrid*12; % Bandwidth of RBs in Hz```

This example creates a time division duplex (TDD) waveform consisting of a pattern of downlink slots followed by uplink slots with a specified period. Set the length of the waveform and TDD parameters.

```numSubframes = 0.5; % Number of subframes to simulate tddConfig.TDDPeriod = 10; % TDD period in slots tddConfig.NumDownlinkSlots = 9; % Number of slots in TDD period containing PDSCH numSlots = numSubframes*carrier.SlotsPerSubframe; tddConfig.SlotAllocation = 0:tddConfig.NumDownlinkSlots-1; % Assume downlink slots at start of TDD period disp(join(["Simulating" num2str(numSlots) "slots"]))```
```Simulating 128 slots ```
`visualizeTDDAllocation(tddConfig);`

Configure a physical downlink shared channel (PDSCH) for full resource block (RB) allocation and enable the phase tracking reference signal (PT-RS).

```pdsch = pre6GPDSCHConfig; pdsch.PRBSet = 0:(carrier.NSizeGrid-1); pdsch.Modulation = "16QAM"; pdsch.EnablePTRS = true;```

Generate Waveform

Create a resource grid containing a PDSCH transmission, demodulation reference signal (DM-RS), and PT-RS in appropriate TDD slots.

```waveGrid = []; % Store resource grid to transmit for nSlot = 0:(numSlots-1) carrier.NSlot = nSlot; slotGrid = hpre6GResourceGrid(carrier,pdsch.NumLayers); % Generate content if slot allocated to PDSCH if isSlotActive(carrier.NSlot,tddConfig) [ind,indInfo] = hpre6GPDSCHIndices(carrier,pdsch); % Generate and map PDSCH for a random codeword cw = randi([0 1],indInfo.G,1); sym = hpre6GPDSCH(carrier,pdsch,cw); slotGrid(ind) = sym; % Generate and map DMRS dmrsInd = hpre6GPDSCHDMRSIndices(carrier,pdsch); dmrsSym = hpre6GPDSCHDMRS(carrier,pdsch); slotGrid(dmrsInd) = dmrsSym; % Generate and map PT-RS ptrsInd = hpre6GPDSCHPTRSIndices(carrier,pdsch); ptrsSym = hpre6GPDSCHPTRS(carrier,pdsch); slotGrid(ptrsInd) = ptrsSym; end waveGrid = [waveGrid slotGrid]; end```

OFDM-modulate the resource grid with an oversampling factor. Oversample the waveform to measure spectral emissions outside of the channel bandwidth. The example sets the FFT size such that it spans the specified oversampled channel bandwidth and is an integer multiple of 128 for a normal cyclic prefix length.

```% OFDM-modulate with specified oversampling factor oversamplingFactor = 3; % Multiple of the channel bandwidth carrier.NSlot = 0; % OFDM-modulate from slot 0 nFFTWaveform = ceil((channelBandwidth/(carrier.SubcarrierSpacing*1e3))*oversamplingFactor/128)*128; [txWaveform,ofdmInfo] = hpre6GOFDMModulate(carrier,waveGrid,Nfft=nFFTWaveform); % Normalize waveform to maximum amplitude for PA modeling txWaveform = txWaveform/max(abs(txWaveform),[],"all"); % Prepend slot to the waveform to allow to allow for filter delay waveformLength = height(txWaveform); slotLength = waveformLength/numSlots; txWaveform = [txWaveform(end-slotLength+1:end,:); txWaveform];```

In this section, you model phase noise and power amplifier (PA) nonlinearity impairments, and apply a filter to limit spectral emissions outside of the channel bandwidth.

Phase Noise

Introduce phase noise distortion. This example uses a phase noise model defined in Hexa-X Deliverable D2.3 Section 4.2.1.1, scaled to 140 GHz as specified in Hexa-X Deliverable D2.3 Table 5-7. This Hexa-X model is based on measurements of 20 GHz wideband RF synthesizer TI LMX2596. Set the minimum frequency offset the phase noise model uses to compute the phase noise spectrum mask. By default, this example uses a 1 MHz minimum frequency offset, therefore the phase noise level below 1 MHz does not match the Hexa-X model. Simulating with a lower minimum frequency offset will more accurately model the low frequency phase noise of the Hexa-X model but result in a longer time to design the phase noise filter.

```enablePN = true; % Enable phase noise model if enablePN minimumFrequencyOffset = 1e6; % Hz mdl = "HexaX Model 2"; phaseNoise = hpre6GPhaseNoise(carrierFrequency*1e9,ofdmInfo.SampleRate, ... MinFrequencyOffset=minimumFrequencyOffset,Model=mdl, ... RandomStream="mt19937ar with seed"); visualize(phaseNoise); % Show phase noise PSD rxWaveform = phaseNoise(txWaveform); % Apply phase noise model else rxWaveform = txWaveform; %#ok<UNRCH> end```

Low-Pass Filter

Filter the baseband waveform to limit emissions outside the channel bandwidth. If the current passband and stopband frequencies, `PassbandFrequency` and `StopbandFrequency`, result in high EVM values, use a wider filter by increasing `PassbandFrequency` and `StopbandFrequency`. To use a narrower filter, reduce `PassbandFrequency` and `StopbandFrequency`. You can also modify the `PassbandRipple` and the `StopbandAttenuation`.

```enableLPF = true; % Enable low-pass filter if enableLPF % Create low-pass filter object LPF = dsp.LowpassFilter; LPF.SampleRate = ofdmInfo.SampleRate; LPF.FilterType = "IIR"; LPF.PassbandFrequency = (transmissionBandwidth + 24*carrier.SubcarrierSpacing*1e3)/2; LPF.StopbandFrequency = channelBandwidth/2; % TransmissionBandwidth and guards LPF.PassbandRipple = 0.2; LPF.StopbandAttenuation = 40; figure; freqz(LPF); % Plot the response of the low-pass filter % Filter the waveform rxWaveform = LPF(rxWaveform); release(LPF); end```

Power Amplifier

Apply a PA impairment model. This example applies a memoryless GaN PA model as described in Hexa-X Deliverable D2.3 Table 5-7 and Hexa-X Deliverable D2.2 Section 3.6.6.1 [2]. This model assumes the ACPR characteristic with respect to PA output power shift according to the expected downscaling of maximum PA output power when increasing frequency.

Set the model backoff to reduce the amplitude of the input signal and reduce distortion.

```enablePA = true; % Enable power amplifier model if enablePA backoff = 6; % In dB rxWaveform = db2mag(-backoff)*rxWaveform; % Apply PA backoff visualizeAMAMCharacteristic(@paMemorylessGaN,"GaN"); rxWaveform = paMemorylessGaN(rxWaveform); end```

Measure ACPR

Measure the ACPR to study spectral regrowth caused by the nonlinear PA model.

```measureACPR = true; if measureACPR % Calculate the number of adjacent channels which can be measured given % the sample rate numAdjacentChannels = floor((ofdmInfo.SampleRate/channelBandwidth-1)/2); if numAdjacentChannels>0 sa = spectrumAnalyzer; sa.SampleRate = ofdmInfo.SampleRate; sa.ChannelMeasurements.Type = "acpr"; sa.ChannelMeasurements.Enabled = true; sa.ChannelMeasurements.Span = transmissionBandwidth; sa.ChannelMeasurements.NumOffsets = numAdjacentChannels; sa.ChannelMeasurements.AdjacentBW = transmissionBandwidth; sa.ChannelMeasurements.ACPROffsets = (1:numAdjacentChannels)*channelBandwidth; sa(rxWaveform); else warning('Sample rate too low to measure ACPR, increase oversamplingFactor') end end```

Measure EVM

Perform the following steps to measure the EVM:

3. Perform channel estimation

4. Equalize the PDSCH symbols

5. Estimate and compensate the common phase error (CPE)

6. Compute PDSCH EVM

Show the equalized PDSCH symbols for each slot.

```% Ignore transients in first part of waveform rxWaveform(1:slotLength,:) = []; % Timing synchronization refSlotGrid = waveGrid(:,1:carrier.SymbolsPerSlot,:); offset = hpre6GTimingEstimate(carrier,rxWaveform,refSlotGrid,Nfft=ofdmInfo.Nfft); rxWaveform = rxWaveform(1+offset:end,:); % Demodulate and measure EVM of each slot rxWaveGrid = hpre6GOFDMDemodulate(carrier,rxWaveform,Nfft=ofdmInfo.Nfft); % Setup constellation diagram constDiagram = comm.ConstellationDiagram; constDiagram.ShowReferenceConstellation = true; L = carrier.SymbolsPerSlot; numRxSlots = floor(size(rxWaveGrid,2)/L); if numRxSlots<1 error('Not enough data to measure EVM, increase numSubframes') end evGrid = []; % Store error vector for each resource element evm = []; % Store EVM measurement for each slot containing PDSCH for nSlot = 0:(numRxSlots-1) carrier.NSlot = nSlot; evSlotGrid = NaN*hpre6GResourceGrid(carrier,pdsch.NumLayers); % Store error vector if isSlotActive(nSlot,tddConfig) % Extract slot from grid rxSlotGrid = rxWaveGrid(:,nSlot*L+(1:L),:); % Estimate channel pdschDMRSInd = hpre6GPDSCHDMRSIndices(carrier,pdsch); pdschDMRSSymbols = hpre6GPDSCHDMRS(carrier,pdsch); [hest,nVar] = hpre6GChannelEstimate(carrier,rxSlotGrid,pdschDMRSInd,pdschDMRSSymbols,CDMLengths=pdsch.DMRS.CDMLengths); % Equalize PDSCH [pdschInd,pdschIndInfo] = hpre6GPDSCHIndices(carrier,pdsch); [pdschRxSym,pdschHest] = nrExtractResources(pdschInd,rxSlotGrid,hest); pdschEqSym = nrEqualizeMMSE(pdschRxSym,pdschHest,nVar); % Correct common phase error pdschEqSym = correctCPE(carrier,pdsch,pdschInd,pdschIndInfo,rxSlotGrid,hest,nVar,pdschEqSym); % Demap, decode, and re-modulate to obtain layer demapped equalized % symbols (rxSym) and reference symbols (refSym) for EVM % measurement. [cw,rxSym] = hpre6GPDSCHDecode(carrier,pdsch,pdschEqSym,nVar); expectedCW = cw{1}<0; % LLR<0 is bit 1, otherwise bit 0 expectedPDSCHSym = hpre6GPDSCH(carrier,pdsch,expectedCW); [~,refSym] = hpre6GPDSCHDecode(carrier,pdsch,expectedPDSCHSym,0); % Show equalized PDSCH constellation constDiagram.ReferenceConstellation = unique(refSym{1}); constDiagram.Title = join(["Equalized PDSCH slot" num2str(nSlot)]); constDiagram(rxSym{1}); % Calculate error vector and EVM slotEVM = measureEVM(refSym{1},rxSym{1}); fprintf("Slot %d PDSCH EVM, RMS: %0.3f%% Peak: %0.3f%%\n",nSlot,slotEVM.RMS*100,slotEVM.Peak*100); evm = [evm slotEVM]; % Store error vector in grid evSlotGrid = NaN(size(rxSlotGrid)); evSlotGrid(pdschInd) = slotEVM.EV; end evGrid = [evGrid evSlotGrid]; %#ok<*AGROW> end```
```Slot 0 PDSCH EVM, RMS: 3.755% Peak: 16.844% Slot 1 PDSCH EVM, RMS: 3.100% Peak: 15.873% Slot 2 PDSCH EVM, RMS: 3.036% Peak: 12.505% Slot 3 PDSCH EVM, RMS: 2.989% Peak: 11.587% Slot 4 PDSCH EVM, RMS: 2.901% Peak: 11.346% Slot 5 PDSCH EVM, RMS: 2.573% Peak: 8.262% Slot 6 PDSCH EVM, RMS: 2.675% Peak: 8.874% Slot 7 PDSCH EVM, RMS: 2.788% Peak: 10.717% Slot 8 PDSCH EVM, RMS: 2.911% Peak: 13.210% Slot 10 PDSCH EVM, RMS: 3.032% Peak: 8.778% Slot 11 PDSCH EVM, RMS: 2.729% Peak: 8.867% Slot 12 PDSCH EVM, RMS: 3.036% Peak: 11.233% Slot 13 PDSCH EVM, RMS: 3.365% Peak: 11.645% Slot 14 PDSCH EVM, RMS: 2.763% Peak: 10.647% Slot 15 PDSCH EVM, RMS: 2.611% Peak: 8.835% Slot 16 PDSCH EVM, RMS: 3.086% Peak: 12.029% Slot 17 PDSCH EVM, RMS: 3.428% Peak: 12.731% Slot 18 PDSCH EVM, RMS: 2.794% Peak: 8.649% Slot 20 PDSCH EVM, RMS: 3.044% Peak: 13.066% Slot 21 PDSCH EVM, RMS: 2.572% Peak: 8.695% Slot 22 PDSCH EVM, RMS: 2.999% Peak: 11.147% Slot 23 PDSCH EVM, RMS: 3.131% Peak: 13.562% Slot 24 PDSCH EVM, RMS: 3.420% Peak: 12.666% Slot 25 PDSCH EVM, RMS: 4.019% Peak: 16.212% Slot 26 PDSCH EVM, RMS: 2.689% Peak: 10.144% Slot 27 PDSCH EVM, RMS: 2.467% Peak: 7.728% Slot 28 PDSCH EVM, RMS: 2.804% Peak: 9.291% Slot 30 PDSCH EVM, RMS: 2.796% Peak: 13.387% Slot 31 PDSCH EVM, RMS: 3.154% Peak: 11.011% Slot 32 PDSCH EVM, RMS: 2.747% Peak: 9.354% Slot 33 PDSCH EVM, RMS: 2.636% Peak: 9.154% Slot 34 PDSCH EVM, RMS: 2.934% Peak: 11.586% Slot 35 PDSCH EVM, RMS: 2.540% Peak: 8.860% Slot 36 PDSCH EVM, RMS: 3.053% Peak: 9.888% Slot 37 PDSCH EVM, RMS: 3.156% Peak: 12.925% Slot 38 PDSCH EVM, RMS: 3.190% Peak: 15.825% Slot 40 PDSCH EVM, RMS: 3.005% Peak: 10.599% Slot 41 PDSCH EVM, RMS: 2.528% Peak: 8.828% Slot 42 PDSCH EVM, RMS: 2.824% Peak: 10.614% Slot 43 PDSCH EVM, RMS: 2.726% Peak: 10.576% Slot 44 PDSCH EVM, RMS: 2.735% Peak: 10.777% Slot 45 PDSCH EVM, RMS: 2.748% Peak: 9.195% Slot 46 PDSCH EVM, RMS: 2.985% Peak: 9.066% Slot 47 PDSCH EVM, RMS: 2.539% Peak: 8.803% Slot 48 PDSCH EVM, RMS: 3.371% Peak: 13.485% Slot 50 PDSCH EVM, RMS: 2.805% Peak: 8.677% Slot 51 PDSCH EVM, RMS: 2.428% Peak: 9.136% Slot 52 PDSCH EVM, RMS: 3.684% Peak: 17.042% Slot 53 PDSCH EVM, RMS: 2.802% Peak: 10.523% Slot 54 PDSCH EVM, RMS: 2.804% Peak: 9.368% Slot 55 PDSCH EVM, RMS: 2.788% Peak: 10.153% Slot 56 PDSCH EVM, RMS: 3.136% Peak: 12.831% Slot 57 PDSCH EVM, RMS: 2.739% Peak: 8.204% Slot 58 PDSCH EVM, RMS: 2.984% Peak: 11.302% Slot 60 PDSCH EVM, RMS: 3.007% Peak: 10.761% Slot 61 PDSCH EVM, RMS: 2.622% Peak: 8.684% Slot 62 PDSCH EVM, RMS: 3.276% Peak: 11.094% Slot 63 PDSCH EVM, RMS: 3.290% Peak: 13.024% Slot 64 PDSCH EVM, RMS: 2.962% Peak: 10.227% Slot 65 PDSCH EVM, RMS: 2.817% Peak: 9.630% Slot 66 PDSCH EVM, RMS: 3.134% Peak: 11.550% Slot 67 PDSCH EVM, RMS: 3.220% Peak: 12.505% Slot 68 PDSCH EVM, RMS: 3.263% Peak: 13.111% Slot 70 PDSCH EVM, RMS: 2.905% Peak: 10.428% Slot 71 PDSCH EVM, RMS: 2.768% Peak: 10.422% Slot 72 PDSCH EVM, RMS: 2.759% Peak: 9.903% Slot 73 PDSCH EVM, RMS: 3.405% Peak: 15.982% Slot 74 PDSCH EVM, RMS: 2.293% Peak: 6.821% Slot 75 PDSCH EVM, RMS: 2.835% Peak: 8.695% Slot 76 PDSCH EVM, RMS: 3.074% Peak: 10.207% Slot 77 PDSCH EVM, RMS: 2.463% Peak: 8.090% Slot 78 PDSCH EVM, RMS: 2.713% Peak: 8.993% Slot 80 PDSCH EVM, RMS: 3.273% Peak: 12.462% Slot 81 PDSCH EVM, RMS: 2.916% Peak: 10.495% Slot 82 PDSCH EVM, RMS: 3.408% Peak: 14.847% Slot 83 PDSCH EVM, RMS: 3.049% Peak: 10.221% Slot 84 PDSCH EVM, RMS: 3.153% Peak: 11.632% Slot 85 PDSCH EVM, RMS: 3.357% Peak: 11.146% Slot 86 PDSCH EVM, RMS: 3.099% Peak: 11.029% Slot 87 PDSCH EVM, RMS: 3.073% Peak: 12.753% Slot 88 PDSCH EVM, RMS: 3.166% Peak: 13.436% Slot 90 PDSCH EVM, RMS: 2.896% Peak: 11.020% Slot 91 PDSCH EVM, RMS: 2.797% Peak: 9.538% Slot 92 PDSCH EVM, RMS: 2.919% Peak: 13.055% Slot 93 PDSCH EVM, RMS: 2.505% Peak: 9.229% Slot 94 PDSCH EVM, RMS: 2.592% Peak: 8.633% Slot 95 PDSCH EVM, RMS: 2.651% Peak: 9.568% Slot 96 PDSCH EVM, RMS: 3.260% Peak: 10.910% Slot 97 PDSCH EVM, RMS: 2.999% Peak: 9.820% Slot 98 PDSCH EVM, RMS: 2.925% Peak: 8.608% Slot 100 PDSCH EVM, RMS: 3.065% Peak: 11.772% Slot 101 PDSCH EVM, RMS: 2.958% Peak: 9.612% Slot 102 PDSCH EVM, RMS: 2.985% Peak: 12.338% Slot 103 PDSCH EVM, RMS: 2.939% Peak: 14.105% Slot 104 PDSCH EVM, RMS: 2.889% Peak: 10.277% Slot 105 PDSCH EVM, RMS: 2.746% Peak: 11.697% Slot 106 PDSCH EVM, RMS: 2.597% Peak: 8.738% Slot 107 PDSCH EVM, RMS: 3.008% Peak: 13.573% Slot 108 PDSCH EVM, RMS: 3.126% Peak: 10.318% Slot 110 PDSCH EVM, RMS: 3.155% Peak: 12.611% Slot 111 PDSCH EVM, RMS: 3.385% Peak: 13.498% Slot 112 PDSCH EVM, RMS: 2.775% Peak: 9.324% Slot 113 PDSCH EVM, RMS: 3.258% Peak: 12.520% Slot 114 PDSCH EVM, RMS: 2.816% Peak: 13.212% Slot 115 PDSCH EVM, RMS: 3.293% Peak: 12.403% Slot 116 PDSCH EVM, RMS: 2.683% Peak: 9.193% Slot 117 PDSCH EVM, RMS: 3.166% Peak: 13.103% Slot 118 PDSCH EVM, RMS: 3.128% Peak: 11.523% Slot 120 PDSCH EVM, RMS: 3.040% Peak: 12.156% Slot 121 PDSCH EVM, RMS: 3.374% Peak: 13.415% Slot 122 PDSCH EVM, RMS: 3.069% Peak: 13.670% Slot 123 PDSCH EVM, RMS: 2.866% Peak: 10.491% Slot 124 PDSCH EVM, RMS: 2.876% Peak: 11.730% Slot 125 PDSCH EVM, RMS: 2.654% Peak: 9.544% Slot 126 PDSCH EVM, RMS: 2.971% Peak: 10.974% ```

Measure RMS and peak EVM for whole waveform. Elements of `evGrid` contain NaN when no PDSCH is present, therefore, ignore these for the measurement (`omitmissing`).

```rmsEVM = sqrt(mean(abs(evGrid).^2,"all","omitmissing"))*100; peakEVM = max(abs(evGrid),[],"all","omitmissing")*100; fprintf("Overall PDSCH EVM, RMS: %0.3f%% Peak: %0.3f%%\n",rmsEVM,peakEVM);```
```Overall PDSCH EVM, RMS: 2.972% Peak: 17.042% ```

Plot EVM per subcarrier, averaged over OFDM symbols.

`evmSubcarrierRMS = plotEVMPerSubcarrier(evGrid);`

Plot EVM per symbol, averaged over subcarriers.

`evmSymbolRMS = plotEVMPerSymbol(evGrid);`

References

[1] Hexa-X Deliverable D2.3 - Radio models and enabling techniques towards ultra-high data rate links and capacity in 6G

[2] Hexa-X Deliverable D2.2 - Initial radio models and analysis towards ultra-high data rate links in 6G

Local Functions

```function y = paMemorylessGaN(x) % Model 28 GHz GaN power amplifier as defined in R4-165901 Section 2.1.4 ak = [-0.334697-0.942326i; 0.89015-0.72633i; -2.58056+4.81215i; 4.81548-9.54837i; -4.41452+8.63164i; 1.54271-2.94034i]; y = x.*abs(x).^(2*(0:5))*ak; end function visualizeAMAMCharacteristic(paModel,modelName) % Plot the nonlinear characteristic of the power amplifier impairment x = logspace(-1.5,0).'; % Input samples with normalized input power y = paModel(x); % Nonlinearity figure; plot(pow2db(abs(x).^2),pow2db(abs(y).^2),'-'); hold on; plot(pow2db(abs(x).^2),pow2db(abs(x).^2),'-.'); grid on xlabel("Instantaneous normalized Input Power (dBW)"); ylabel("Instanteneous Output Power (dBW)"); title(join(["AM/AM," modelName])) legend(join([modelName "characteristic"]),"Linear characteristic",Location="Northwest"); end function visualizeTDDAllocation(tddConfig) % Show the TDD allocation pattern x = [0 1 1 0]; y = [0 0 1 1]; xTxtPos = (x(2)-x(1))/2+x(1); yTxtPos = (y(3)-y(1))/2+y(1)-1; % Position text below patch blocks hp = []; t = []; f = figure; for i = 0:tddConfig.TDDPeriod-1 if isSlotActive(i,tddConfig) patchColorIdx = 1; % DL slot txt = "D"; else patchColorIdx = 2; % UL slot txt = "U"; end hp = [hp patch(x+i,y,0,CDataMode="auto",SeriesIndex=patchColorIdx)]; % Use patch color from axis color series t = [t txt]; hold on; text(xTxtPos+i,yTxtPos,txt,HorizontalAlignment="center") end title(join(["TDD allocation pattern," num2str(tddConfig.TDDPeriod) "slot period"])) axis off set(gca,"DataAspectRatio",[1 1 1],"YLim",[yTxtPos*2 y(3)+0.25]); % Set limits to create space between data and title pos = get(gcf,"Position"); yScaleFactor = 3; % Scale figure to avoid excesive empty space above and below set(f,"Position",[pos(1) pos(2) pos(3) pos(4)/yScaleFactor]); hpdl = hp(find(t=="D",1)); % Handle of first downlink slot hpul = hp(find(t=="U",1)); % Handle of first uplink slot legendTxt = ["Downlink slot" "Uplink slot"]; legendTxt = legendTxt([any(t=="D") any(t=="U")]); % Remove entry if no downlink or uplink slots legend([hpdl hpul],legendTxt,location="eastoutside"); end function isActive = isSlotActive(nSlot,tddConfig) % Returns true if the carrier slot number contains the PDSCH isActive = any(mod(nSlot,tddConfig.TDDPeriod) == mod(tddConfig.SlotAllocation,tddConfig.TDDPeriod)); end function pdschEqSym = correctCPE(carrier,pdsch,pdschInd,pdschIndInfo,rxSlotGrid,hest,nVar,pdschEqSym) % Correct PDSCH common phase error ptrsInd = hpre6GPDSCHPTRSIndices(carrier,pdsch); if isempty(ptrsInd) % No CPE correction return end % Map equalized PT-RS symbols to tempGrid tempGrid = hpre6GResourceGrid(carrier,pdsch.NumLayers); [ptrsRxSym,ptrsHest,~,~,~,ptrsLayerIndices] = nrExtractResources(ptrsInd,rxSlotGrid,hest,tempGrid); ptrsEqSym = nrEqualizeMMSE(ptrsRxSym,ptrsHest,nVar); tempGrid(ptrsLayerIndices) = ptrsEqSym; % Estimate the residual channel at the PT-RS locations in tempGrid ptrsSymbols = hpre6GPDSCHPTRS(carrier,pdsch); cpe = hpre6GChannelEstimate(carrier,tempGrid,ptrsInd,ptrsSymbols); % Sum estimates across subcarriers, receive antennas, and layers cpe = angle(sum(cpe,[1 3 4])); % Map equalized PDSCH symbols to tempGrid and correct CPE in each OFDM symbol tempGrid(pdschInd) = pdschEqSym; symLoc = pdschIndInfo.PTRSSymbolSet(1)+1:pdschIndInfo.PTRSSymbolSet(end)+1; tempGrid(:,symLoc,:) = tempGrid(:,symLoc,:).*exp(-1i*cpe(symLoc)); pdschEqSym = tempGrid(pdschInd); end function m = measureEVM(refSym,rxSym) % Returns a structure EVM containing: % EV - The error vector % RMS - Root Mean Square EVM (%) % Peak - Peak EVM (%) evm = comm.EVM; evm.AveragingDimensions = [1 2]; % Average across subcarriers and symbols evm.Normalization = "Average constellation power"; evm.AverageConstellationPower = 1; % All constellations have average unit power evm.MaximumEVMOutputPort = true; [rms,peak] = evm(refSym,rxSym); % Convert EVM percentages to decimal values m.EV = rxSym-refSym; % Error vector m.RMS = single(rms)/100; m.Peak = single(peak)/100; end function [evmSubcarrierRMS,evmSubcarrierPeak] = plotEVMPerSubcarrier(evGrid) % Plot EVM per subcarrier % Resource elements are NaN when they contain no PDSCH, so ignore for % measurement (omitmissing) evmSubcarrierRMS = sqrt(mean(abs(evGrid).^2,2,"omitmissing"))*100; evmSubcarrierPeak = sqrt(max(abs(evGrid).^2,[],2,"omitmissing"))*100; numSC = height(evmSubcarrierRMS); figure; plot(0:numSC-1,evmSubcarrierRMS,'.-'); hold on; plot(0:numSC-1,evmSubcarrierPeak,'.-'); legend(["RMS" "Peak"]); xlabel("Subcarrier Number"); ylabel("EVM (%)"); grid on title("PDSCH EVM per subcarrier") end function [evmSymbolRMS,evmSymbolPeak] = plotEVMPerSymbol(evGrid) % Plot EVM per OFDM symbol % Resource elements are NaN when they contain no PDSCH, so ignore for % measurement (omitmissing) evmSymbolRMS = sqrt(mean(abs(evGrid).^2,1,"omitmissing"))*100; evmSymbolPeak = sqrt(max(abs(evGrid).^2,[],1,"omitmissing"))*100; figure; plot(0:width(evmSymbolRMS)-1,evmSymbolRMS,'.-'); hold on; plot(0:width(evmSymbolRMS)-1,evmSymbolPeak,'.-'); legend(["RMS" "Peak"]); xlabel("Symbol Number"); ylabel("EVM (%)"); grid on title("PDSCH EVM per symbol") end```