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

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐