Matlab:实现计算接收波形的误差矢量幅值EVM(附完整源码)
·
Matlab:实现计算接收波形的误差矢量幅值EVM
function [evmInfo,eqSym,refSym] = hNRPDSCHEVM(waveConfig,rxWaveform,cfg)
narginchk(3,3);
if ~isfield(cfg,'Evm3GPP')
evm3GPP = false;
else
evm3GPP = cfg.Evm3GPP;
end
if ~isfield(cfg,'TargetRNTIs')
targetRNTIs = [];
else
targetRNTIs = cfg.TargetRNTIs;
end
if ~isfield(cfg,'PlotEVM')
plotEVM = true;
else
plotEVM = cfg.PlotEVM;
end
if ~isfield(cfg,'DisplayEVM')
displayEVM = true;
else
displayEVM = cfg.DisplayEVM;
end
if ~isfield(cfg,'Label') || (isfield(cfg,'Label') && isempty(cfg.Label))
label = '';
else
label = cfg.Label;
end
if ~isfield(cfg,'InitialNSlot')
initialNSlot = 0;
else
initialNSlot = cfg.InitialNSlot;
end
% Derive per-slot PDSCH resources (waveformResources) used as reference for EVM calculation
[~,winfo] = nrWaveformGenerator(waveConfig);
waveformResources = winfo.WaveformResources;
waveformResources.Label = label;
numBWPs = length(waveConfig.BandwidthParts);
eqSym = cell(2,numBWPs); % Equalized symbols for constellation plot, for each low/high EVM window location and BWP
refSym = cell(2,numBWPs); % Reference symbols for constellation plot, for each low/high EVM window location and BWP
evmInfo = [];
% Store the received waveform for reuse in each BWP
% Loop over each BWP
rxWaveformOrig = rxWaveform;
for bwpIdx = 1:numBWPs
% Display error messages for the below checks for pdschArray
% Ensure modulation is same across the valid RNTI set
% At least one non-empty resource field should be present
% pdschArray should have at least one non-empty DM-RS resources
% At least one PDSCH Configuration should be enabled
resourceEmptyCount = 0;
dmrsEmptyCount = 0;
pdschDisabledCount = 0;
% Check validity of each BWP configuration
invalidBWPConfigFlag = false;
bwpCfg = waveConfig.BandwidthParts{bwpIdx};
[pdschArray,~,carrier] = hListTargetPDSCHs(waveConfig,waveformResources,targetRNTIs,bwpIdx);
if ~isempty(pdschArray)
bwpStart = bwpCfg.NStartBWP-carrier.NStartGrid;
else
invalidBWPConfigFlag = true;
end
pdschConfigLen = length(pdschArray);
for idx = 1:pdschConfigLen
if idx > 1 && ~strcmp(pdschArray(idx-1).PDSCH.Modulation,pdschArray(idx).PDSCH.Modulation)
error('All RNTIs must have the same modulation.');
end
if isempty(pdschArray(idx).Resources) || isempty(pdschArray(idx).PDSCH.PRBSet) || ...
isempty(pdschArray(idx).PDSCH.SymbolAllocation) || pdschArray(idx).PDSCH.SymbolAllocation(2) == 0
resourceEmptyCount = resourceEmptyCount + 1;
end
nonEmptyCount = 0;
for rIdx = 1:length(pdschArray(idx).Resources)
if ~isempty(pdschArray(idx).Resources(rIdx).DMRSSymbols)
nonEmptyCount = nonEmptyCount + 1;
end
end
if nonEmptyCount == 0
dmrsEmptyCount = dmrsEmptyCount + 1;
end
if ~pdschArray(idx).PDSCH.Enable
pdschDisabledCount = pdschDisabledCount + 1;
end
end
% Avoid displaying below warnings if 'invalidBWPConfigFlag' is
% already true
if invalidBWPConfigFlag == false
if resourceEmptyCount == pdschConfigLen
warning('Input configuration does not contain adequate resources to proceed with EVM measurement');
invalidBWPConfigFlag = true;
end
if dmrsEmptyCount == pdschConfigLen
warning('Input configuration does not contain DM-RS resources to proceed with EVM measurement');
invalidBWPConfigFlag = true;
end
if pdschDisabledCount == pdschConfigLen
warning('Input configuration does not contain valid PDSCH resources to proceed with EVM measurement');
invalidBWPConfigFlag = true;
end
end
% Skip this BWP due to unexpected configuration
if invalidBWPConfigFlag
evmInfo(bwpIdx).SubcarrierRMS = [];
evmInfo(bwpIdx).SubcarrierPeak = [];
evmInfo(bwpIdx).SymbolRMS = [];
evmInfo(bwpIdx).SymbolPeak = [];
evmInfo(bwpIdx).SlotRMS = [];
evmInfo(bwpIdx).SlotPeak = [];
evmInfo(bwpIdx).EVMGrid = [];
evmInfo(bwpIdx).OverallEVM.EV = [];
evmInfo(bwpIdx).OverallEVM.RMS = [];
evmInfo(bwpIdx).OverallEVM.Peak = [];
continue;
end
% Obtain OFDM related info
carrier.NSlot = initialNSlot;
ofdmInfo = nrOFDMInfo(carrier);
L = ofdmInfo.SymbolsPerSlot;
sampleRate = winfo.ResourceGrids(bwpIdx).Info.SampleRate;
if ~isfield(cfg,'SampleRate') || isempty(cfg.SampleRate)
rxWaveform = rxWaveformOrig;
else
rxWaveform = resample(rxWaveformOrig,sampleRate,cfg.SampleRate);
end
k0 = winfo.ResourceGrids(bwpIdx).Info.k0;
scs = carrier.SubcarrierSpacing;
ofdmInfo.SamplesPerSubframe = sampleRate/1000;
% Generate a reference grid of length two frames for timing synchronization
refGrid = referenceGrid(carrier,bwpCfg,pdschArray,ofdmInfo.SlotsPerFrame*2);
% Shift in frequency the waveform, taking into account the 'k0' for the current BWP
t = (0:size(rxWaveform,1)-1).'/sampleRate;
k0Offset = k0*scs*1e3;
rxWaveformk0Shifted = rxWaveform.*exp(-1i*2*pi*k0Offset*t);
% Time synchronization of input waveform
offset = nrTimingEstimate(carrier,rxWaveformk0Shifted,refGrid,'SampleRate',sampleRate);
rxWaveform = rxWaveformk0Shifted(1+offset:end,:);
% Calculate overSamplingFactor
overSamplingFactor = sampleRate/sum(ofdmInfo.SymbolLengths)/1000;
% Calculate number of subframes, slots and frames
% Slot samples length may vary in a given waveform for some Subcarrier
% spacings. remSfSamples and remSlot are used to account for this
% variation.
remSfSamples = mod(size(rxWaveform, 1),ofdmInfo.SamplesPerSubframe);
remSlot = 0;
remSfSamples = floor(remSfSamples/overSamplingFactor);
if remSfSamples
remSlot = floor(remSfSamples/sum(ofdmInfo.SymbolLengths(1:L)));
end
nSubframes = floor(size(rxWaveform, 1)/ofdmInfo.SamplesPerSubframe);
nSlots = nSubframes*ofdmInfo.SlotsPerSubframe+ remSlot;
nFrames = floor(nSlots/(10*ofdmInfo.SlotsPerSubframe));
nLayers = pdschArray(1).PDSCH.NumLayers;
if nSlots == 0
warning('No scheduled slots found for EVM processing.');
evmInfo(bwpIdx).SubcarrierRMS = [];
evmInfo(bwpIdx).SubcarrierPeak = [];
evmInfo(bwpIdx).SymbolRMS = [];
evmInfo(bwpIdx).SymbolPeak = [];
evmInfo(bwpIdx).SlotRMS = [];
evmInfo(bwpIdx).SlotPeak = [];
evmInfo(bwpIdx).EVMGrid = [];
evmInfo(bwpIdx).OverallEVM.EV = [];
evmInfo(bwpIdx).OverallEVM.RMS = [];
evmInfo(bwpIdx).OverallEVM.Peak = [];
continue;
end
% Generate a reference grid, refGrid, for slots corresponding to
% the length of the input waveform. This grid contains only the
% DM-RS and is primarily used for channel estimation
refGrid = referenceGrid(carrier,bwpCfg,pdschArray,nSlots);
% Slot allocation of the PDSCH configurations may overlap with each
% other. Extract unique allocated slots
activeSlots = [];
for pIdx = 1:pdschConfigLen
if ~isempty(pdschArray(pIdx).Resources)
activeSlots = [activeSlots pdschArray(pIdx).Resources.NSlot]; %#ok<*AGROW>
end
end
activeSlots = unique(activeSlots);
% Number of FFT Locations in each CP, based on EVM mode (Standard / 3GPP)
nEVMWindowLocations = 1;
if evm3GPP
nEVMWindowLocations = 2;
end
% Resize refGrid based on BWP dimensions
refGrid = refGrid(12*bwpStart+1:12*(bwpStart+bwpCfg.NSizeBWP),:,:);
% Declare storage variables
refSlotGrid = zeros(size(refGrid,1),nSlots*L,size(refGrid,3),nEVMWindowLocations); % 4-D Grid of reference IQs
eqSlotGrid = refSlotGrid;
rxGridLow = []; % Demodulated OFDM grid, for 1st CP position
rxGridHigh = []; % Demodulated OFDM grid, for 2nd CP position
HestLow = []; % Channel estimation for 1st CP position
HestHigh = []; % Channel estimation for 2nd CP position
% Restrict CP length as per TS 38.104, Annex B.5.1 (FR1) / Annex C.5.1 (FR2)
cpLength = double(ofdmInfo.CyclicPrefixLengths(2));
% Populate pdschObj of type 'nrPDSCHConfig'. It is to be used for
% Common Phase error (CPE) estimation and decoding PDSCH
pdschObj = extractPdschCfg(pdschArray);
for idx = 1:pdschConfigLen
pdschObj{idx}.NStartBWP = waveConfig.BandwidthParts{bwpIdx}.NStartBWP;
pdschObj{idx}.NSizeBWP = waveConfig.BandwidthParts{bwpIdx}.NSizeBWP;
if isempty(pdschObj{idx}.NID)
pdschObj{idx}.NID = carrier.NCellID;
end
end
% If the waveform contains an encoded transport block, generate
% reference parameters needed for decoding and re-encoding. The
% re-encoded IQ samples are used as a reference for EVM calculation.
pdschEncodingOn = false;
for pIdx = 1:length(waveConfig.PDSCH)
if waveConfig.PDSCH{pIdx}.Coding
pdschEncodingOn = true;
break;
end
end
if pdschEncodingOn
decodeDLSCH = nrDLSCHDecoder;
decodeDLSCH.LDPCDecodingAlgorithm = 'Normalized min-sum';
dlsch = nrDLSCH('MultipleHARQProcesses',false);
end
frameEVM = repmat(hRawEVM([]), 1, max(nFrames,1));
% When evm3GPP is true, two EVM window locations and two CP fractions
% are selected for 3GPP EVM for OFDM demodulation. If false, a single
% EVM window location is used, which is centred in the middle of the CP
if evm3GPP
W = getEVMWindow(carrier,waveConfig.FrequencyRange,waveConfig.ChannelBandwidth,ofdmInfo.Nfft);
nEVMWindowLocations = 2;
cpFraction = [0 ; W/cpLength];
else
nEVMWindowLocations = 1;
cpFraction = 0.5; % Use default value
end
rxGridLow = nrOFDMDemodulate(carrier, rxWaveform, 'CyclicPrefixFraction',cpFraction(1),'SampleRate',sampleRate);
if nEVMWindowLocations == 2
rxGridHigh = nrOFDMDemodulate(carrier, rxWaveform, 'CyclicPrefixFraction',cpFraction(2),'SampleRate',sampleRate);
end
% Work only on the relevant BWP in the waveform to simplify indexing
rxGridLow = rxGridLow(12*bwpStart+1:12*(bwpStart+bwpCfg.NSizeBWP),:,:);
if evm3GPP
rxGridHigh = rxGridHigh(12*bwpStart+1:12*(bwpStart+bwpCfg.NSizeBWP),:,:);
end
% For 3GPP EVM processing, extract IQ samples in blocks of 10 ms each (TS 38.104, Annex B.6/C.6)
nSlots10Ms = min(nSlots,10*carrier.SubcarrierSpacing/15);
% Each block is 10 ms in length, can go upto 2 for TDD FRC
nBlocks = ceil(nSlots/nSlots10Ms);
if evm3GPP
% For each 10 ms block, estimate the channel coefficients
for blkIdx = 0:(nBlocks-1)
% Index of symbols within current block. If a symbol index
% exceeds the length of the reference grid, remove it
symIdx = blkIdx*L*nSlots10Ms+(1:(L*nSlots10Ms));
symIdx(symIdx>size(refGrid, 2)) = [];
HestLowBlk = hChannelEstimateEVM3GPP(rxGridLow(:, symIdx, :),refGrid(:, symIdx,:),'movingAvgFilter','CDMLengths',pdschArray(1).CDMLengths);
HestHighBlk = hChannelEstimateEVM3GPP(rxGridHigh(:, symIdx, :),refGrid(:, symIdx,:),'movingAvgFilter','CDMLengths',pdschArray(1).CDMLengths);
HestLow = [HestLow HestLowBlk];
HestHigh = [HestHigh HestHighBlk];
end
else
% Compute channel estimates for each slot
for slotIdx = 1:nSlots
symIdx = (slotIdx-1)*L+1:slotIdx*L;
symIdx(symIdx>size(rxGridLow, 2)) = [];
HestLowBlk = nrChannelEstimate(rxGridLow(:, symIdx, :),refGrid(:, symIdx,:),'CDMLengths',pdschArray(1).CDMLengths);
HestLow = [HestLow HestLowBlk];
end
end
numSCs = size(rxGridLow,1);
% PDSCH REs may not always be present on the same set of RBs as DM-RS.
% For each such slot, extrapolate the channel coefficients to span the
% location of these PDSCH allocation regions
% Ensure slot has channel coefficients for the corresponding PDSCH
% allocation
for slotIdx = 1:nSlots
symIdx = (slotIdx-1)*L+1:slotIdx*L;
symIdx(symIdx>size(rxGridLow, 2)) = [];
% Locate RBs where channel coefficients are present
[row ,~]= find(HestLow(:,symIdx));
row = unique(row);
HestRb = unique(floor((row-1)/12));
rxRb = [];
for idx = 1:length(pdschArray)
if any(pdschArray(idx).PDSCH.SlotAllocation == (slotIdx-1+initialNSlot))
rxRb = [rxRb pdschArray(idx).PDSCH.PRBSet];
end
end
% Set extrapolateHest to true if allocated RB list does not match
% list of RBs containing channel coefficients
extrapolateHest = false;
for rbIdx = 1:length(rxRb)
if ~any(HestRb == rxRb(rbIdx))
extrapolateHest = true;
break;
end
end
% Process only for slots where PDSCH RBs do not contain channel
% estimates. Using the 'nearest' interpolation method, extrapolate
% the channel coefficients over the slot span. This method ensures
% the same channel coefficient is extrapolated over the neighboring
% frequency region. The first entry in the pdschArray is sufficient
% for this purpose
if extrapolateHest && ~isempty(HestRb)
firstDmrsLocInSlot = pdschArray(1).PDSCH.DMRS.DMRSSubcarrierLocations(1)+1;
R = size(HestLow,3);
P = size(HestLow,4);
for p = 1:P
for r = 1:R
H_tmp = HestLow(:,symIdx(firstDmrsLocInSlot),r,p);
if sum(abs(H_tmp)) == 0
interpEqCoeff = 1e-16.*ones(size(H_tmp,1),1);
else
interpEqCoeff = interp1(find(H_tmp~=0),H_tmp(H_tmp~=0),(1:numSCs).','nearest','extrap');
end
interpEqCoeff = repmat(interpEqCoeff,1,L);
HestLow(:,symIdx,r,p) = interpEqCoeff;
end
end
end
end
% In case of non-3GPP case, only a single EVM grid is used.
% Compute the DL EVM for each active/valid DL slot, store the results
% in a cell-array for later processing. Skip slots which are not DL.
slotRange = activeSlots(activeSlots < (initialNSlot+nSlots));
slotRange = slotRange(slotRange >= initialNSlot);
if isempty(slotRange)
slotRange = [];
plotEVM = false;
warning('No scheduled slots found for EVM processing');
end
% Iterate for each active slot
for slotIdx=slotRange
carrier.NSlot = slotIdx;
[pdschIndices,refSymbols,ptrsIndices,ptrsSymbols] = hSlotResources(pdschArray,slotIdx);
% Do not include first two slot symbols for PDSCH EVM (used for
% control as specified in TS 38.141-1 table 4.9.2.2-2 (NR-TMs) /
% TS 38.101-1 table A.3.1-1 (FRCs))
% This step is not performed when
% * extrapolateHest is true, or
% * label is absent, or
% * coding is enabled
% Ensure that the symbol allocation does not include the first two
% OFDM symbols when the 'label' field is set. A non-empty 'label'
% is assumed to contain a string associated with Release 15
% NR-TMs/FRCs
if ~isempty(label) && ~extrapolateHest && ~pdschEncodingOn
idx = pdschIndices(:,1) <= (2*numSCs);
pdschIndices(idx,:) = [];
refSymbols(idx,:) = [];
end
% Extract the relevant slot, channel estimates and PDSCH allocated REs
currentSlotIdx = slotIdx - initialNSlot;
currentRxGridLow = rxGridLow(:, currentSlotIdx*L+(1:L), :);
currentHestLow = HestLow(:, currentSlotIdx*L+(1:L),:,:);
[pdschRxLow,pdschHestLow] = nrExtractResources(pdschIndices,currentRxGridLow,currentHestLow);
noiseEst = 0; % ZF based equalization, as per TS 38.104, Annex B.1 (FR1) / Annex C.1 (FR2)
[eqGridLow,csiLow] = nrEqualizeMMSE(pdschRxLow,pdschHestLow,noiseEst);
if evm3GPP
currentRxGridHigh = rxGridHigh(:, currentSlotIdx*L+(1:L), :);
currentHestHigh = HestHigh(:, currentSlotIdx*L+(1:L),:,:);
[pdschRxHigh,pdschHestHigh] = nrExtractResources(pdschIndices,currentRxGridHigh,currentHestHigh);
[eqGridHigh,csiHigh] = nrEqualizeMMSE(pdschRxHigh,pdschHestHigh,noiseEst);
end
% Estimate and compensate phase error
% Process only active configurations, one per slot. If more
% than one configuration is present, process the first one as
% the indices (pdschIndices, dmrsIndices) collectively include
% all active ones in current slot
for idx = 1:length(pdschObj)
period = pdschArray(idx).PDSCH.Period;
if isempty(period)
period = 0;
end
if any(mod(slotIdx,period) == pdschArray(idx).PDSCH.SlotAllocation) && pdschArray(idx).PDSCH.EnablePTRS
eqGridLow = [];
eqGridHigh = [];
eqGridLow = [eqGridLow; pdschCPE(carrier,pdschObj{idx},pdschIndices,ptrsIndices,ptrsSymbols,currentRxGridLow,currentHestLow)];
if evm3GPP
eqGridHigh = [eqGridHigh; pdschCPE(carrier,pdschObj{idx},pdschIndices,ptrsIndices,ptrsSymbols,currentRxGridHigh,currentHestHigh)];
end
break;
end
end
% Accumulate equalized IQs
eqSym{1,bwpIdx} = [eqSym{1,bwpIdx}; eqGridLow];
if evm3GPP
eqSym{2,bwpIdx} = [eqSym{2,bwpIdx}; eqGridHigh];
end
% Create vectors needed for decoding each pdschObj when 'Coding' is
% enabled
if pdschEncodingOn
trBlkSizes = [];
G = [];
rv = [];
for pIdx = 1:pdschConfigLen
period = pdschArray(pIdx).PDSCH.Period;
if isempty(period)
period = 0;
end
if any(mod(slotIdx,period) == pdschArray(pIdx).PDSCH.SlotAllocation)
currentSlotRange = [];
currentSlotRange = [currentSlotRange pdschArray(pIdx).Resources.NSlot];
resourceIdx = find(slotIdx == currentSlotRange);
trBlkSizes = [trBlkSizes;pdschArray(pIdx).Resources(resourceIdx).TransportBlockSize];
G = [G;pdschArray(pIdx).Resources(resourceIdx).G];
rv = [rv;pdschArray(pIdx).Resources(resourceIdx).RV];
end
end
end
% For low edge EVM and high edge EVM
for e = 1:nEVMWindowLocations
% Select the low or high edge equalizer output
if (e == 1)
eqGrid = eqGridLow;
csi = csiLow;
else
eqGrid = eqGridHigh;
csi = csiHigh;
end
if ~pdschEncodingOn || ~any(trBlkSizes)
rxSymbols = eqGrid;
else
refSymbols = [];
eqOffset = 0;
rx = cell(1);
tbIdx = 1;
for pIdx = 1:pdschConfigLen
% Decode rxSymbols. If CRC passes, re-encode them for
% obtaining reference IQs for EVM calculation. Extract
% the equalized symbols and csi for each pdschObj.
% Skip processing if not allocated in current slot.
period = pdschArray(pIdx).PDSCH.Period;
if isempty(period)
period = 0;
end
if ~any(mod(slotIdx,period) == pdschArray(pIdx).PDSCH.SlotAllocation)
continue;
end
eqLen = length(pdschArray(pIdx).Resources(resourceIdx).ChannelIndices);
eq = eqGrid(1+eqOffset:eqOffset+eqLen,:);
% Decode PDSCH TB only when 'Coding' is enabled
if ~pdschArray(pIdx).PDSCH.Coding
rx{1} = [rx{1} ; eq];
[~,ref,~,~] = hSlotResources(pdschArray(pIdx),slotIdx);
refSymbols = [refSymbols;ref];
continue;
end
currentCsi = csi(1+eqOffset:eqOffset+eqLen,:);
eqOffset = eqOffset+ eqLen;
[dlschLLRs,rxSymbols] = nrPDSCHDecode(eq,pdschObj{pIdx}.Modulation,pdschObj{pIdx}.NID,pdschObj{pIdx}.RNTI,noiseEst);
rx{1} = [rx{1} ; rxSymbols{1}];
% Scale LLRs by CSI
numCWs = size(dlschLLRs,2);
currentCsi = nrLayerDemap(currentCsi); % CSI layer demapping
for cwIdx = 1:numCWs
Qm = length(dlschLLRs{cwIdx})/length(rxSymbols{cwIdx}); % bits per symbol
currentCsi{cwIdx} = repmat(currentCsi{cwIdx}.',Qm,1); % expand by each bit per symbol
dlschLLRs{cwIdx} = dlschLLRs{cwIdx} .* currentCsi{cwIdx}(:); % scale
end
decodeDLSCH.TransportBlockLength = trBlkSizes(tbIdx);
decodeDLSCH.TargetCodeRate = pdschArray(pIdx).PDSCH.TargetCodeRate;
[demodBits,blkerr] = decodeDLSCH(dlschLLRs,pdschObj{pIdx}.Modulation,nLayers,rv(tbIdx));
if any(blkerr)
warning('CRC failed on decoded data, using sliced received symbols, EVM may be inaccurate.');
% Attempt to gracefully handle bad CRC. Generate some
% bits for this slot by hard slicing LLRs, so that the
% simulation can continue
recodedBits = cellfun(@(x) x<0, dlschLLRs, 'UniformOutput', false);
else
trBlk = demodBits;
dlsch.TargetCodeRate = decodeDLSCH.TargetCodeRate;
setTransportBlock(dlsch,trBlk);
recodedBits = dlsch(pdschObj{pIdx}.Modulation,nLayers,G(tbIdx),waveConfig.PDSCH{pIdx}.RVSequence(1));
end
ref = nrPDSCH(recodedBits,pdschObj{pIdx}.Modulation,nLayers,pdschObj{pIdx}.NID,pdschObj{pIdx}.RNTI);
refSymbols = [refSymbols;ref];
tbIdx = tbIdx+1;
end
end
if pdschEncodingOn && any(trBlkSizes)
rxSymbols = cell2mat(rx);
end
refSym{e,bwpIdx} = [refSym{e,bwpIdx}; refSymbols];
% Map each reference-slot , equalized slot to its correct position in grid, layer, EVM-edge
rbsPerSlot = numSCs*carrier.SymbolsPerSlot;
tmpGrid = zeros(numSCs,carrier.SymbolsPerSlot);
if pdschEncodingOn && any(trBlkSizes)
rxSymbols = reshape(rxSymbols,nLayers,length(rxSymbols)/nLayers).';
end
ind = zeros(size(pdschIndices,1),nLayers);
for layerIdx = 1:nLayers
if layerIdx <= size(pdschIndices,2)
ind(:,layerIdx) = pdschIndices(:,layerIdx) - rbsPerSlot*(layerIdx -1);
tmpGrid(ind(:,layerIdx)) = refSymbols(:,layerIdx);
symIdx = (slotIdx)*L+1:(slotIdx+1)*L;
refSlotGrid(:,symIdx,layerIdx,e) = tmpGrid;
tmpGrid(:) = 0;
tmpGrid(ind(:,layerIdx)) = rxSymbols(:,layerIdx);
eqSlotGrid(:,symIdx,layerIdx,e) = tmpGrid;
tmpGrid(:) = 0;
end
end
end
end
% Evaluate detailed EVM statistics for a grid of equalized and reference slots
evmInfoBWP = hEVM(carrier,eqSlotGrid,refSlotGrid);
evm = evmInfoBWP.EVM;
% Remove EVM field as it not part of the output
evmInfoBWP = rmfield(evmInfoBWP,'EVM');
evmInfo = [evmInfo;evmInfoBWP];
% Display per slot, and per EVM edge EVM statistics
if displayEVM
disp("EVM stats for BWP idx : " + num2str(bwpIdx));
end
for slotIdx = slotRange
for e = 1:nEVMWindowLocations
if (e == 1)
edge = 'Low edge';
if nEVMWindowLocations == 1
edge = ''; % Print only single EVM per slot
end
else
edge = 'High edge';
end
if displayEVM
fprintf('%s RMS EVM, Peak EVM, slot %d: %0.3f %0.3f%%\n',edge,slotIdx,evm(e, slotIdx+1).RMS*100,evm(e, slotIdx+1).Peak*100);
end
end
end
printFrameAvg = 1; % Ensures only fully occupied frames are printed
% After we've filled a frame or if we're at the end of a signal
% shorter than a frame, do EVM averaging
if (nFrames == 0)
nFrames = 1; % Below loop needs to run at least once
printFrameAvg = 0; % Don't print low/high EVM as we don't have sufficient slots to fill up a frame
end
% 1-based indexing for accessing evm
% Limit frame-averaging to complete frames only
slotRange = slotRange+1;
slotRange = slotRange(slotRange <= (nFrames*10*ofdmInfo.SlotsPerSubframe));
% loop through each frame, selecting the frames with higher RMS( when
% measuring 3GPP EVM)
for frameIdx = 0:nFrames-1
frameLowEVM = hRawEVM(cat(1,evm(1,slotRange).EV));
frameEVM(frameIdx+1) = frameLowEVM;
if evm3GPP
frameHighEVM = hRawEVM(cat(1,evm(2,slotRange).EV));
if frameHighEVM.RMS > frameLowEVM.RMS
frameEVM(frameIdx+1) = frameHighEVM;
end
end
if printFrameAvg && displayEVM
if evm3GPP
fprintf('Averaged low edge RMS EVM, frame %d: %0.3f%%\n',frameIdx,frameLowEVM.RMS*100);
fprintf('Averaged high edge RMS EVM, frame %d: %0.3f%%\n',frameIdx,frameHighEVM.RMS*100);
end
fprintf('Averaged RMS EVM frame %d: %0.3f%%\n',frameIdx,frameEVM(frameIdx+1).RMS*100);
end
end
if plotEVM
% For each valid slot, update the plot (symbol,SC,slot,grid-wise)
hEVMPlots(evmInfo(bwpIdx),eqSym{1,bwpIdx},refSym{1,bwpIdx},bwpIdx);
end
if displayEVM
fprintf('Averaged overall RMS EVM: %0.3f%%\n', evmInfo(bwpIdx).OverallEVM.RMS*100);
disp("Overall Peak EVM = " + string((evmInfo(bwpIdx).OverallEVM.Peak)*100) + "%");
end
end
end
function W = getEVMWindow(carrier,frequencyRange,channelBandwidth,nFFT)
% W = getEVMWindow(CARRIER,FREQUENCYRANGE,CHANNELBANDWIDTH,NFFT) is the
% error vector magnitude window length, as mentioned in TS 38.104, Section
% B.5.2 < FR1 /FR2 >. W is defined for a given combination of subcarrier
% spacing, channel bandwidth/fft length, frequency range and CP type. W
% is subsequently used as an intermediate value to decide the CP Fraction
% for OFDM demodulation
scsFR1 = [15 30 60];
scsFR2 = [60 120];
% BW MHz 5 10 15 20 25 30 40 50 60 70 80 90 100
nfftFR1 = [256 384 512 768 1024 1536 2048 3072 4096];
WsFR1 = [NaN NaN 14 NaN 28 44 58 108 144; % NormalCp, 15kHz
8 NaN 14 22 28 54 72 130 172; % NormalCp, 30kHz
8 11 14 26 36 64 86 NaN NaN; % NormalCp, 60kHz
54 80 106 164 220 340 454 NaN NaN]; % ExtendedCp, 60kHz
% BW MHz 50 100 200 400
nfftFR2 = [512 1024 2048 4096];
WsFR2 = [NaN 36 72 144; % NormalCP, 60kHz
18 36 72 144; % NormalCP, 120kHz
NaN 220 440 880]; % ExtendedCP, 60kHz
W = []; %#ok<NASGU>
if (strcmpi(frequencyRange,'FR1'))
rowIdx = find(carrier.SubcarrierSpacing == scsFR1) + double(strcmpi(carrier.CyclicPrefix,'extended'));
W = WsFR1(rowIdx,nFFT == nfftFR1);
if channelBandwidth == 25
if nFFT == 512 && carrier.SubcarrierSpacing == 60
if strcmpi(carrier.CyclicPrefix,'extended')
W = 110;
else
W = 18;
end
elseif nFFT == 1024 && carrier.SubcarrierSpacing == 30
W = 36;
elseif nFFT == 2048 && carrier.SubcarrierSpacing == 15
W = 72;
end
end
else
rowIdx = find(carrier.SubcarrierSpacing == scsFR2) + double(strcmpi(carrier.CyclicPrefix,'extended'))*2;
W = WsFR2(rowIdx,nFFT == nfftFR2);
end
% Filter out invalid combinations
if isnan(W) || isempty(W)
error('Invalid FFT/SCS/BW combination');
end
end
function refGrid = referenceGrid(carrier,bwpCfg,pdschArray,nSlots)
nSubcarriers = carrier.NSizeGrid * 12;
L = carrier.SymbolsPerSlot*nSlots; % Number of OFDM symbols in the reference grid
nLayers = pdschArray(1).PDSCH.NumLayers;
bwpStart = bwpCfg.NStartBWP-carrier.NStartGrid;
bwpLen = bwpCfg.NSizeBWP;
refGrid = zeros(nSubcarriers,L,nLayers); % empty grid
bwpGrid = zeros(bwpLen*12,L,nLayers);
resPerSlot = bwpLen*12*carrier.SymbolsPerSlot;
% Populate the DM-RS symbols in the reference grid for all slots
% Place the bwpGrid in a carrier grid (at an appropriate
% location) in case the BWP size is not the same as the carrier grid
for slotIdx=carrier.NSlot+(0:nSlots-1)
[~,~,dmrsIndices,dmrsSymbols] = hSlotResources(pdschArray,slotIdx);
if ~isempty(dmrsIndices)
for layerIdx = 1:nLayers
if layerIdx <= size(dmrsIndices,2)
dmrsIndices(:,layerIdx) = dmrsIndices(:,layerIdx) - resPerSlot*(layerIdx -1)+ (L*bwpLen*12*(layerIdx-1));
bwpGrid(dmrsIndices(:,layerIdx)+(slotIdx-carrier.NSlot)*resPerSlot) = dmrsSymbols(:,layerIdx);
end
end
refGrid(12*bwpStart+1:12*(bwpStart+bwpLen),:,:) = bwpGrid;
end
end
end
function [pdschObj] = extractPdschCfg(pdschWaveCfg)
% Extract relevant parameters from waveform generator PDSCH struct to build an obj of type 'nrPDSCHConfig'
% Set PDSCH parameters
pdschObj = {};
for idx = 1:length(pdschWaveCfg)
pdschObj{idx} = nrPDSCHConfig;
pdsch = pdschWaveCfg(idx).PDSCH;
pdschObj{idx}.PRBSet = pdsch.PRBSet;
pdschObj{idx}.SymbolAllocation = pdsch.SymbolAllocation;
pdschObj{idx}.Modulation = pdsch.Modulation;
pdschObj{idx}.NumLayers = pdsch.NumLayers;
pdschObj{idx}.MappingType = pdsch.MappingType;
pdschObj{idx}.RNTI = pdsch.RNTI;
pdschObj{idx}.NID = pdsch.NID;
pdschObj{idx}.VRBToPRBInterleaving = pdsch.VRBToPRBInterleaving;
pdschObj{idx}.VRBBundleSize = pdsch.VRBBundleSize;
for idx2 = 1:length(pdsch.ReservedPRB)
pdschObj{idx}.ReservedPRB{idx2} = nrPDSCHReservedConfig;
pdschObj{idx}.ReservedPRB{idx2}.PRBSet = pdsch.ReservedPRB{idx2}.PRBSet;
pdschObj{idx}.ReservedPRB{idx2}.SymbolSet = pdsch.ReservedPRB{idx2}.SymbolSet;
pdschObj{idx}.ReservedPRB{idx2}.Period = pdsch.ReservedPRB{idx2}.Period;
end
% Set DM-RS parameters
pdschObj{idx}.DMRS = pdsch.DMRS;
% Set PT-RS parameters
pdschObj{idx}.EnablePTRS = pdsch.EnablePTRS;
pdschObj{idx}.PTRS = pdsch.PTRS;
end
end
function pdschEq = pdschCPE(carrier,pdsch,pdschIndices,ptrsIndices,ptrsSymbols,rxGrid,estChannelGrid)
[~,pdschIndicesInfo] = nrPDSCHIndices(carrier,pdsch);
% Set noiseEst to zero as it was not estimated along with the
% corresponding estChannelGrid
noiseEst = 0;
% Get PDSCH resource elements from the received grid
[pdschRx,pdschHest] = nrExtractResources(pdschIndices,rxGrid,estChannelGrid);
% Equalization
pdschEq = nrEqualizeMMSE(pdschRx,pdschHest,noiseEst);
% Initialize temporary grid to store equalized symbols
carrier.NSizeGrid = pdsch.NSizeBWP;
tempGrid = nrResourceGrid(carrier,pdsch.NumLayers);
% Extract PT-RS symbols from received grid and estimated
% channel grid
[ptrsRx,ptrsHest,~,~,~,ptrsLayerIndices] = nrExtractResources(ptrsIndices,rxGrid,estChannelGrid,tempGrid);
% Equalize PT-RS symbols and map them to tempGrid
ptrsEq = nrEqualizeMMSE(ptrsRx,ptrsHest,noiseEst);
tempGrid(ptrsLayerIndices) = ptrsEq;
% Estimate the residual channel at the PT-RS locations in
% tempGrid
cpe = nrChannelEstimate(tempGrid,ptrsIndices,ptrsSymbols,'CDMLengths',pdsch.DMRS.CDMLengths,'CyclicPrefix',carrier.CyclicPrefix);
% Sum estimates across subcarriers, receive antennas, and
% layers. Then, get the CPE by taking the angle of the
% resultant sum
cpe = angle(sum(cpe,[1 3 4]));
% Map the equalized PDSCH symbols to tempGrid
tempGrid(pdschIndices) = pdschEq;
% Correct CPE in each OFDM symbol within the range of reference
% PT-RS OFDM symbols
if numel(pdschIndicesInfo.PTRSSymbolSet) > 0
symLoc = pdschIndicesInfo.PTRSSymbolSet(1)+1:pdschIndicesInfo.PTRSSymbolSet(end)+1;
tempGrid(:,symLoc,:) = tempGrid(:,symLoc,:).*exp(-1i*cpe(symLoc));
end
% Extract PDSCH symbols
pdschEq = tempGrid(pdschIndices);
end
该博文为原创文章,未经博主同意不得转。
本文章博客地址:https://cplusplus.blog.csdn.net/article/details/129287471
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)