%lteDLChannelEstimate Downlink channel estimation % [HEST NOISEEST] = lteDLChannelEstimate(...) returns HEST, the estimated % channel between each transmit and receive antenna and NOISEEST, an % estimate of the noise power spectral density on the reference signal % subcarriers. % % HEST is an M-by-N-by-NRxAnts-by-CellRefP (optionally % M-by-N-by-NRxAnts-by-NLayers for UE-specific beamforming transmission % schemes) array where M is the number of subcarriers, N is the number of % OFDM symbols, NRxAnts is the number of receive antennas, CellRefP is % the number of cell-specific reference signal antenna ports and NLayers % is the number of transmission layers. Using the reference signals, an % estimate of the power spectral density of the noise present on the % estimated channel response coefficients is returned. % % [HEST NOISEEST] = lteDLChannelEstimate(ENB, RXGRID) returns the % estimated channel coefficients using the method described in % TS36.104/TS36.141 Annex E/F for the purposes of transmitter EVM % testing. % % ENB is a structure and must contain the following fields: % NDLRB - Number of downlink resource blocks % CellRefP - Number of cell-specific reference signal antenna ports % (1,2,4) % NCellID - Physical layer cell identity % NSubframe - Subframe number % CyclicPrefix - Optional. Cyclic prefix length % ('Normal'(default),'Extended') % DuplexMode - Optional. Duplex mode ('FDD'(default),'TDD') % Only required for 'TDD' duplex mode: % TDDConfig - Optional. Uplink/Downlink Configuration (0...6) % (default 0) % SSC - Optional. Special Subframe Configuration (0...9) % (default 0) % Only required for CEC.Reference='CSIRS' below: % CSIRefP - Number of CSI-RS antenna ports (1,2,4,8) % CSIRSConfig - CSI-RS configuration index (TS 36.211 Table % 6.10.5.2-1) % CSIRSPeriod - Optional. CSI-RS subframe configuration: % ('On'(default),'Off',Icsi-rs,[Tcsi-rs Dcsi-rs]) % % RXGRID is a 3-dimensional M-by-N-by-NRxAnts array of resource elements. % The second dimension of RXGRID can contain any whole number of % subframes worth of OFDM symbols i.e. for normal cyclic prefix each % subframe contains 14 OFDM symbols, therefore N is a multiple of 14. % Note: to adhere to the estimation method defined in TS36.104/TS36.141, % RXGRID must contain 10 subframes. % % [HEST NOISEEST] = lteDLChannelEstimate(ENB, CEC, RXGRID) returns the % estimated channel using the method and parameters defined by the % configuration structure CEC. % % CEC is a structure which defines the type of channel estimation % performed. CEC must contain a set of the following fields: % PilotAverage - Type of pilot averaging ('TestEVM', 'UserDefined') % FreqWindow - Size of window in resource elements used to average % over frequency. % TimeWindow - Size of window in resource elements used to average % over time % InterpType - Type of 2D interpolation used(see griddata for types) % InterpWindow - Interpolation window type: ('Causal','Non-causal', % 'Centred','Centered'). Note: 'Centred' and 'Centered' % are equivalent % InterpWinSize - Interpolation window size: % 'Causal','Non-causal' - any number >=1 % 'Centred','Centered' - odd numbers >=1 % % The 'TestEVM' pilot averaging will ignore other structure fields in % CEC, and the method follows that described in TS36.104/TS36.141 Annex % E/F for the purposes of transmitter EVM testing. % % The 'UserDefined' pilot averaging uses a rectangular kernel of size % CEC.FreqWindow-by-CEC.TimeWindow and performs a 2D filtering operation % upon the pilots. Note that pilots near the edge of the resource grid % will be averaged less as they have no neighbors outside of the grid, % or a limited number of neighbors outside of the grid obtained by the % creation of virtual pilots. % % [HEST NOISEEST] = lteDLChannelEstimate(ENB, CHS, CEC, RXGRID) returns % the estimated channel given cell-wide settings structure ENB, PDSCH % transmission configuration CHS and channel estimator configuration CEC. % CHS must include the following fields: % TxScheme - Transmission scheme, one of: % 'Port0' - Single-antenna port, Port 0 % 'TxDiversity' - Transmit diversity scheme % 'CDD' - Large delay CDD scheme % 'SpatialMux' - Closed-loop spatial multiplexing scheme % 'MultiUser' - Multi-user MIMO scheme % 'Port5' - Single-antenna port, Port 5 % 'Port7-8' - Single-antenna port, port 7 (when % NLayers=1); Dual layer transmission, port 7 % and 8 (when NLayers=2) % 'Port8' - Single-antenna port, Port 8 % 'Port7-14' - Up to 8 layer transmission, ports 7-14 % PRBSet - A 1- or 2-column matrix, containing the 0-based Physical % Resource Block indices (PRBs) corresponding to the resource % allocations for this PDSCH. % RNTI - Radio Network Temporary Identifier (16-bit) % % For the 'Port5', 'Port7-8', 'Port8' and 'Port7-14' transmission % schemes, the channel estimator configuration structure CEC contains the % following additional field: % Reference - Optional. Specifies point of reference (signals to % internally generate) for channel estimation. % ('DMRS'(default), 'CSIRS') % % For the 'Port5', 'Port7-8', 'Port8' and 'Port7-14' transmission % schemes, with Reference='DMRS', the channel estimation is performed % using UE-specific reference signals and the returned channel estimate % will be of size M-by-N-by-NRxAnts-by-NLayers. Alternatively, with % Reference='CSIRS' the channel estimation is performed using the CSI % reference signals (CSI) and the returned channel estimate will be of % size M-by-N-by-NRxAnts-by-CSIRefP. For other transmission schemes the % channel estimation is performed using cell-specific reference signals % and the channel estimate will be of size M-by-N-by-NRxAnts-by-CellRefP. % Note that CSI-RS based channel estimation and hence Reference='CSIRS' % is strictly only valid within the standard for the 'Port7-14' % transmission scheme. The optional CSIRSPeriod parameter controls the % downlink subframes in which CSI-RS will be present, either always 'On' % or 'Off', or defined by the scalar subframe configuration index Icsi-rs % (0...154) or the explicit subframe periodicity and offset pair [Tcsi-rs % Dcsi-rs] (TS 36.211 Section 6.10.5.3). % % For the 'Port7-8' and 'Port7-14' transmission schemes with % 'UserDefined' pilot averaging, if CEC.TimeWindow = 2 or 4 and % CEC.FreqWindow=1 the estimator will enter a special case where an % averaging window of 2 or 4 pilots in time will be used to average the % pilot estimates; the averaging is always applied across 2 or 4 pilots, % regardless of their separation in OFDM symbols. This operation ensures % that averaging is always done on 2 or 4 pilots. This provides the % appropriate "despreading" operation required for the case of UE RS % ports / CSI-RS ports which occupy the same time/frequency locations but % use different orthogonal covers to allow them to be differentiated at % the receiver. For the CSI-RS and any number of configured CSI-RS ports % (given by ENB.CSIRefP), the pilot REs occur in pairs, one pair per % subframe, that require averaging with CEC.TimeWindow=2 and will result % in a single estimate per subframe. For the UE RS with between 1 and 4 % layers (given by CHS.NLayers), the pilot REs occur in pairs, repeated % in each slot, that require averaging with CEC.TimeWindow=2 and will % result in two estimates per subframe, one for each slot; for between 5 % and 8 layers, the pairs are distinct between the slots of the subframe % and the required averaging is CEC.TimeWindow=4, resulting in one % estimate per subframe. % % Example: % Transmit RMC R.12 (4-antenna transmit diversity), model the propagation % channel by combining all transmit antennas onto one receive antenna, % OFDM demodulate and finally channel estimate. % % enb = lteRMCDL('R.12'); % cec = struct('FreqWindow',1,'TimeWindow',1,'InterpType','cubic',... % 'PilotAverage','UserDefined','InterpWinSize',3,... % 'InterpWindow','Causal'); % txWaveform = lteRMCDLTool(enb,[1;0;0;1]); % rxWaveform = sum(txWaveform,2); % rxGrid = lteOFDMDemodulate(enb,rxWaveform); % hest = lteDLChannelEstimate(enb,cec,rxGrid); % % See also lteOFDMDemodulate, lteEqualizeMMSE, lteEqualizeZF, % lteDLPerfectChannelEstimate, griddata. % Copyright 2009-2013 The MathWorks, Inc. function [H_EST, NoisePowerEst, AvgEstimates, Estimates] = lteDLChannelEstimate2(varargin) if(isstruct(varargin{2})) if(isstruct(varargin{3})) PDSCH = varargin{2}; CEC = varargin{3}; RXGRID = varargin{4}; else PDSCH = []; CEC = varargin{2}; RXGRID = varargin{3}; avgrefs = varargin{4}; end else % If no configuration structure then use the TestEVM method PDSCH = []; CEC.PilotAverage = 'TestEVM'; RXGRID = varargin{2}; end ENB = varargin{1}; % Get dimensions of resource grid Dims = lteDLResourceGridSize(ENB); K = Dims(1); Lsf = Dims(2); % Determine number of subframes nsfs = size(RXGRID,2)/Dims(2); % Determine number of Tx- and RxAntennas NRxAnts = size(RXGRID,3); if (~isempty(PDSCH) && any(strcmpi(PDSCH.TxScheme,{'Port5' 'Port7-8' 'Port8' 'Port7-14'}))) if (~isfield(CEC,'Reference')) CEC.Reference='DMRS'; end if (strcmpi(CEC.Reference,'DMRS')==1) NTx = PDSCH.NLayers; elseif (strcmpi(CEC.Reference,'CSIRS')==1) NTx = ENB.CSIRefP; else error('lte:error','Reference must be "DMRS" or "CSIRS", see help for details.'); end else if (~isfield(CEC,'Reference')) CEC.Reference='CellRS'; end NTx = ENB.CellRefP; end % Initialize size of estimated channel grid H_EST = zeros([size(RXGRID,1) size(RXGRID,2) size(RXGRID,3) NTx]); % Preallocate noise power estimate vector for speed noiseVec = zeros(size(NRxAnts,NTx)); if (NTx == 4) nn=3; else nn=NTx; end RefEstimates=zeros(nn,2*2*2*ENB.NDLRB*10); for rxANT = 1:NRxAnts for nTxAntLayer = 1:NTx % Extract pilot symbols from received grid and calculate least % squares estimate [ls_estimates,specialvec,DwPTS]= GetPilotEstimates(ENB,nTxAntLayer-1,RXGRID(:,:,rxANT),PDSCH,CEC); Estimates(nTxAntLayer,1:length(ls_estimates(3,:))) = ls_estimates(3,:); % Average the pilots as defined by CEC.PilotAverage, this can % be 'UserDefined' or 'TestEVM'. % Note: Setting the window size to 1x1 is equivalent to no % averaging. It is recommended that no averaging should be % carried out when a high SNR is present, as this would have an % adverse effect on the least squares estimates if strcmpi(CEC.PilotAverage,'UserDefined')&&(CEC.FreqWindow==1)&&(CEC.TimeWindow==1) P_EST = ls_estimates; ScalingVec = 0; else [P_EST, ScalingVec]= PilotAverage(PDSCH,CEC,H_EST,ls_estimates); end %AvgEstimates(nTxAntLayer,1:length(P_EST(3,:))) = P_EST(3,:); P_EST(3,:)=avgrefs; if strcmpi(CEC.PilotAverage,'TestEVM') % Channel estimation as defined in TS36.141 Annex F.3.4 is % performed, the pilots are averaged in time and frequency % and the resulting vector is linearly interpolated. %---------------------- % Interpolate eq coefficients extrapolating to account for % missing pilots at the edges interpEqCoeff = interp1(find(P_EST~=0),P_EST(P_EST~=0),(1:length(P_EST)).','linear','extrap'); % --------------------- % The DC carrier needs to be accounted for during % interpolation % Get pilot symbol index values ind = lteCellRSIndices(ENB,0); % Using initial index value calculate position of pilot % symbols within interpolated coefficients if ind(1)-3>0 vec = (ind(1)-3):3:K; else vec = ind(1):3:K; end % Determine the index values of the pilots either side of % the DC carrier preDC = vec(length(vec)/2); postDC = vec(1+length(vec)/2); % Interpolate the section using the DC carrier and insert % this into the interpolated vector containing the % coefficients interpEqCoeffTemp = interp1(1:4:5,[interpEqCoeff(preDC) interpEqCoeff(postDC)],1:5,'linear'); interpEqCoeff(preDC:K/2) = interpEqCoeffTemp(1:length(interpEqCoeff(preDC:K/2))); interpEqCoeff(1+K/2:postDC) = interpEqCoeffTemp(2+length(interpEqCoeff(preDC:K/2)):end); %---------------------- % Generate grid by replicating estimated equalizer channel % coefficients H_EST(:,:,rxANT,nTxAntLayer) = repmat(interpEqCoeff,1,Lsf*nsfs); % The value of the noise averaged pilot symbols are placed % into P_EST(3,:) matrix and these are used to determine % the noise power present on the pilot symbol estimates of % the channel. P_EST = ls_estimates; tempGrid = squeeze(H_EST(:,:,rxANT,nTxAntLayer)); P_EST(3,:) = tempGrid(sub2ind(size(H_EST),P_EST(1,:),P_EST(2,:))); else % Channel estimation is performed by analyzing up to % 'CEC.InterpWinSize' subframes together and estimating % virtual pilots outwith the bounds of the subframe. % Averaging window can be set to Causal, Non-causal or % Centred, Centered. % Using the desired subframeAveraging type the initial % position of the window is determined if(CEC.InterpWinSize>=1) if strcmpi(CEC.InterpWindow,'Centred')||strcmpi(CEC.InterpWindow,'Centered') % For Centered averaging the window size must be odd if ~mod(CEC.InterpWinSize,2) error('lte:error','Window size must be odd for centered window type'); end % For Centered windowing both current,future and past % data are used to estimate current channel coefficients x = floor(CEC.InterpWinSize/2); y = floor(CEC.InterpWinSize/2); elseif strcmpi(CEC.InterpWindow,'Causal') % For causal windowing current and past data are used % to estimate current subframe channel coefficients x = 0; y = CEC.InterpWinSize-1; elseif strcmpi(CEC.InterpWindow,'Non-causal') % For non-causal windowing current and future data are % used to estimate current subframe channel % coefficients x = CEC.InterpWinSize-1; y = 0; else error('lte:error','Channel estimation structure field InterpWindow must be one of the following: Causal,Non-causal,Centred,Centered'); end else error('lte:error','InterpWinSize cannot be less than 1'); end % Interpolate using averaged pilot estimates and defined % interpolation settings for sf = 0:nsfs-1 if specialvec(sf+1)=='U' H_EST(:,sf*Lsf+1:(sf+1)*Lsf,rxANT,nTxAntLayer) = NaN; else % Extract the pilots from required subframes p_use = P_EST; p_use(:,p_use(4,:)>(sf+1)+x)=[]; p_use(:,p_use(4,:)<(sf+1)-y)=[]; % Account for DC offset p_use(1,(p_use(1,:)>K/2)) = p_use(1,(p_use(1,:)>K/2))+1; if strcmpi(CEC.InterpType,'Cubic')||strcmpi(CEC.InterpType,'Linear') % VPVEC is used to determine if virtual pilots are % needed at the beginning or end of the % interpolation window, if the current subframe,sf, % is located at beginning or end of the window then % virtual pilots are created accordingly vpvec = unique(p_use(4,:)); if (strcmp(CEC.Reference,'CellRS')==1) % Create virtual pilots and append to vector % containing pilots using cell-specific RS % methodology. vps = createVirtualPilots(ENB,p_use(1:3,:),1,1,(sf+1==min(vpvec)),(sf+1==max(vpvec))); p_use = [p_use(1:3,:) vps]; else % Create edge virtual pilots suitable for % UE RS which can have partial bandwidth, % or CSI-RS. if (~isempty(p_use)) vps = createEdgeVirtualPilots(ENB,p_use(1:3,:)); p_use = [p_use(1:3,:) vps]; end end end % Perform 2D interpolation % Interpolation is carried out on a (K+1)-by-L % matrix to account for DC offset being added in if (~isempty(p_use)) Htemp = griddata(p_use(2,:)-Lsf*sf,p_use(1,:),p_use(3,:),1:Lsf,(1:K+1)',CEC.InterpType); %#ok % Remove DC offset Htemp(1+(K/2),:) = []; if specialvec(sf+1)=='S' Htemp(:,DwPTS+1:Lsf) = NaN; end H_EST(:,sf*Lsf+1:(sf+1)*Lsf,rxANT,nTxAntLayer) = Htemp; if isnan(H_EST) error('lte:error','H_EST NaN'); end end end end end if nTxAntLayer<3 || (~isempty(PDSCH) && any(strcmpi(PDSCH.TxScheme,{'Port7-8' 'Port7-14'}))) % The noise level present can be determined using the noisy % least squares estimates of the channel at pilot symbol % locations and the noise averaged pilot symbol estimates of the % channel noise = ls_estimates(3,~isnan(ls_estimates(3,:))) - P_EST(3,~isnan(P_EST(3,:))); if strcmpi(CEC.PilotAverage,'UserDefined') noise = sqrt(ScalingVec./(ScalingVec+1)).*noise; end % Additional averaging for noise estimation in LTE-A case, % to suppress interference from orthogonal sequences on % other antennas in same time-frequency locations. if (~isempty(PDSCH) && any(strcmpi(PDSCH.TxScheme,{'Port7-8' 'Port7-14'})) &&... (CEC.TimeWindow==2 || CEC.TimeWindow==4) && CEC.FreqWindow==1) if (~isempty(ls_estimates)) if (strcmpi(CEC.Reference,'DMRS')==1) temp=[]; pilotSC = unique(ls_estimates(1,:)); for i = pilotSC x=find(ls_estimates(1,:)==i); x=[x(1) x(end)]; temp=[temp mean(noise(x))*2]; %#ok end noise=temp; end end end % Taking the variance of the noise present on the pilot symbols % results in a value of the noise power for each transmit and % receive antenna pair if (isempty(noise)) noiseVec(rxANT,nTxAntLayer)=NaN; else noiseVec(rxANT,nTxAntLayer) = mean(noise.*conj(noise)); end end end end % The mean of the noise power across all the transmit/receive antenna % pairs is used as the estimate of the noise power NoisePowerEst = mean(mean(noiseVec)); end % GetPilotEstimates Obtain the least squares estimates of the reference % signals % [ls_estimates] = GetPilotEstimates(ENB,nTxAntLayer,RXGRID) Extracts the % reference signals for a specific transmit/receive antenna pair and % calculates their least squares estimate. The results are placed in a % 3xNp matrix containing the subcarrier and OFDM symbol location, row and % column subscripts, and value. Np is the number of cell specific % reference (pilot) symbols per resource grid % % RXGRID is an MxN array where M is the number of subcarriers, % N is the number of OFDM symbols. Dimension M must be % 12*NDLRB where NDLRB must be {6,15,25,50,75,100}. % Dimension N must be a multiple of number of symbols in a subframe L, % where L=14 for normal cyclic prefix and L=12 for extended cyclic % prefix. % % nTxAntLayer defines which transmit antennas' or layers' pilot symbols to extract % % ENB is a structure and must contain the following fields: % NDLRB - Number of downlink resource blocks % NCellID - Physical layer cell identity % CellRefP - Number of transmit antenna ports {1,2,4} % CyclicPrefix - Optional. Cyclic prefix length{'Normal'(default),'Extended'} % % % Example % Return the least squares estimate of the pilots symbols in a 3xNp array % where the first row is the subcarrier index,k the second row is the OFDM % symbol,l and the third row defines the least squares estimate of the % pilot located at that position. % % enb=struct('NDLRB',6,'CellRefP',1,'NCellID',0,'CyclicPrefix','Normal'); % rxGrid=ones(lteDLResourceGridSize(enb)); % nTxAntLayer=0; % ls_estimates = GetPilotEstimates(enb,nTxAntLayer,rxGrid).' % ans = % 1.0000 1.0000 -0.7071 - 0.7071i % 7.0000 1.0000 -0.7071 - 0.7071i % 13.0000 1.0000 -0.7071 - 0.7071i % . . . % . . . % . . . % 58.0000 12.0000 -0.7071 + 0.7071i % 64.0000 12.0000 0.7071 - 0.7071i % 70.0000 12.0000 0.7071 - 0.7071i % Copyright 2009-2010 The MathWorks, Inc. function [ls_estimates,specialvec,DwPTS] = GetPilotEstimates(ENB,nTxAntLayer,RXGRID,PDSCH,CEC) % Get dimensions of resource grid Dims = lteDLResourceGridSize(ENB); nsfs = size(RXGRID,2)/Dims(2); K = Dims(1); L = Dims(2); if (rem(nsfs,1)~=0) error('lte:error','The received grid input must contain a whole number of subframes.'); end if (~isempty(PDSCH) && any(strcmpi(PDSCH.TxScheme,{'Port5' 'Port7-8' 'Port8' 'Port7-14'}))) if (strcmp(CEC.Reference,'DMRS')==1) % create UE RS indices PDSCH.NTxAnts=0; PDSCH.W=[]; linearIndSF = lteDMRSIndices(ENB,PDSCH,'mat'); else % create CSI-RS indices and symbols and deal with zero removal linearIndSF = lteCSIRSIndices(ENB,'mat'); CsiRS = lteCSIRS(ENB,'mat'); CsiRS = CsiRS(:,nTxAntLayer+1); linearIndSF(CsiRS==0,:)=[]; CsiRS(CsiRS==0)=[]; end % If more than one TxAntenna is used we need to adjust indices values so that % they start in first antenna plane linearIndSF = linearIndSF(:,nTxAntLayer+1) - (K*L*nTxAntLayer); else % Extract linear indices of reference signals for particular TxAntenna dumENB = ENB; dumENB.NSubframe = 0; linearIndSF = lteCellRSIndices(dumENB,nTxAntLayer); % If more than one TxAntenna is used we need to adjust indices values so that % they start in first antenna plane linearIndSF = linearIndSF - (K*L*nTxAntLayer); end % Create grid for pilot symbols linearIndGrid = zeros(size(linearIndSF,1),nsfs); idealPilotGrid = zeros(size(linearIndSF,1),nsfs); offset = double(ENB.NSubframe); specialvec = char(zeros(1,0)); DwPTS = 0; for sf = ENB.NSubframe:ENB.NSubframe+nsfs-1 ENB.NSubframe = mod(sf,10); linearIndGrid(:,sf-offset+1) = (linearIndSF+(sf*K*L)); if (~isempty(PDSCH) && any(strcmpi(PDSCH.TxScheme,{'Port5' 'Port7-8' 'Port8' 'Port7-14'}))) if (strcmp(CEC.Reference,'DMRS')==1) % create UE RS symbols DMRS = lteDMRS(ENB,PDSCH,'mat'); idealPilotGrid(1:length(DMRS),sf-offset+1) = DMRS(:,nTxAntLayer+1); else % use CSI-RS symbols created earlier idealPilotGrid(1:length(CsiRS),sf-offset+1) = CsiRS; end else CellRS = lteCellRS(ENB, nTxAntLayer); idealPilotGrid(1:length(CellRS),sf-offset+1) = CellRS; end dupinfo = lteDuplexingInfo(ENB); if strcmpi(dupinfo.SubframeType,'Special') specialvec = [specialvec 'S']; %#ok DwPTS = dupinfo.NSymbolsDL; elseif strcmpi(dupinfo.SubframeType,'Uplink') specialvec = [specialvec 'U']; %#ok elseif strcmpi(dupinfo.SubframeType,'Downlink') specialvec = [specialvec 'D']; %#ok else error('lte:error','Invalid SubframeType, in structure dupinfo'); end end if (~isempty(idealPilotGrid)) ulvec = find(idealPilotGrid(size(idealPilotGrid,1),:)==0); linearIndGrid(:,ulvec) = []; idealPilotGrid(:,ulvec) = []; else ulvec = []; end linearIndGrid = linearIndGrid - (offset*K*L); % Extract the row and column subscripts of the pilot symbols for entire % grid [p_estSC, p_estSym] = ind2sub(size(RXGRID),linearIndGrid); % Calculate least squares channel estimates at pilot locations p_est = RXGRID(linearIndGrid)./idealPilotGrid; % Create a vector - [k;l;p_est] sfref = repmat((1:nsfs),length(linearIndSF),1); sfref(:,ulvec) = []; ls_estimates = [double(p_estSC(:).') ; double(p_estSym(:).') ; p_est(:).';double(sfref(:)).']; end %PilotAverage Average reference signals % [P_EST] = PilotAverage(PDSCH,CEC,H_EST,LS_EST) performs a moving average of pilot % symbols % % LS_EST is a 3xNp matrix containing the least square estimates of the % pilots symbols and their column and row indices within the received % grid. % LS_EST = [k;l;p_est] % H_EST is an MxN matrix and defines the size of the grid that the % averaging will be performed on % % CEC is a structure which defines the type of channel estimation % performed. CEC must contain a set of the following fields: % PilotAverage - Type of pilot averaging {'TestEVM', 'UserDefined'} % FreqWindow - Size of window used to average in frequency in % resource elements. % TimeWindow - Size of window used to average in time in resource % elements % InterpType - Type of 2D interpolation used(see griddata for types) % % The dimensions of the averaging window are defined in structure CEC. % The window is defined in terms of Resource Elements, and depending on % the size of the averaging window, averaging will be performed in either % the time or frequency direction only, or a combination of both creating % a square/rectangular window. The pilot to be averaged will always be % placed at the center of the window, therefore the window size must be % an odd number. % % Frequency Direction: 9x1 Time Direction: 1x9 % % x % x % x % x % P x x x x P x x x x % x % x % x % x % % % Square window: 9x9 % x x x x x x x x x % P x x x x x x P x % x x x x x x x x x % x x x x x x x x x % x x x x P x x x x % x x x x x x x x x % x x x x x x x x x % P x x x x x x P x % x x x x x x x x x % % Performing EVM compliance testing as per TS36.141 AnnexF.3.4, requires % time averaging be done over 10 subframes(i.e. 1 Frame) across each % pilot symbol carrying subcarrier creating a TotalNumberPilotsx1 vector. % This is then frequency averaged using a moving window average with a window % size of 19. This type of averaging can be performed by setting the % PilotAverage type in structure CEC to 'TestEVM'. % Copyright 2009-2013 The MathWorks, Inc. function [P_EST, scalingVec] = PilotAverage(PDSCH,CEC,H_EST,P_EST) switch lower(CEC.PilotAverage) case 'userdefined' if(CEC.FreqWindow<1)||(CEC.TimeWindow<1) error('lte:error','Frequency and time averaging window size cannot be less than 1'); end if (~isempty(PDSCH) && any(strcmpi(PDSCH.TxScheme,{'Port7-8' 'Port7-14'})) &&... (CEC.TimeWindow==2 || CEC.TimeWindow==4) && CEC.FreqWindow==1) % Perform averaging in time direction using a moving window % of size N = CEC.TimeWindow N = CEC.TimeWindow; if (~isempty(P_EST)) % Average only subcarriers which contain RS symbols. Extract RS % symbols on a per subcarrier basis and use window to average. for subcarrier = unique(P_EST(1,:)) % Store DRS symbols from relevant slot in vector for easy access symbVec = (P_EST(3,P_EST(1,:)==subcarrier)).'; % Define temporary vector to store the averaged values avgVec = zeros(size(symbVec)); % Check length of input at least equal to size of window; % if not, reduce window size. Despreading will be incomplete % here if (length(symbVec) < N) N = length(symbVec); end % Determines position of window w.r.t element being averaged, % and performs the averaging for symbNo = 1:length(symbVec) if (symbNo-(N/2+1)<=0) avgVec(symbNo)= sum(symbVec(1:N))/N; else avgVec(symbNo) = sum(symbVec(end-(N-1):end))/N; end end % Update P_EST with averaged values P_EST(3,P_EST(1,:)==subcarrier)= avgVec; end end % This vector is used to scale the noise by the number of averaging % elements in the window scalingVec = ones(size(P_EST(3,:)))*N; else if (strcmpi(CEC.InterpWindow,'Centred')||strcmpi(CEC.InterpWindow,'Centered')) if (~mod(CEC.FreqWindow,2)||~mod(CEC.TimeWindow,2)) error('lte:error','Window size must be odd in time and frequency for centred/centered window type'); end end % Define number of subcarriers K = size(H_EST,1); % Define an empty resource grid with the DC offset subcarrier % inserted grid = zeros(size(H_EST,1)+1,size(H_EST,2)); % Account for DC offset P_EST(1,(P_EST(1,:)>K/2)) = P_EST(1,(P_EST(1,:)>K/2))+1; % Place the pilot symbols back into the received grid with the % DC offset subcarrier in place grid(sub2ind(size(grid),P_EST(1,:),P_EST(2,:))) = P_EST(3,:); % Define convolution window kernel = ones(CEC.FreqWindow,CEC.TimeWindow); % Perform convolution grid = conv2(grid,kernel,'same'); % Extract only pilot symbol location values and set the rest of % the grid to zero tempGrid = zeros(size(grid)); tempGrid(sub2ind(size(grid),P_EST(1,:),P_EST(2,:))) = grid(sub2ind(size(grid),P_EST(1,:),P_EST(2,:))); grid = tempGrid; % Normalize pilot symbol values after convolution [grid, scalingVec] = normalisePilotAverage(CEC,P_EST,grid); % Place averaged values back into pilot symbol matrix P_EST(3,:) = grid(sub2ind(size(tempGrid),P_EST(1,:),P_EST(2,:))); % Remove DC offset P_EST(1,(P_EST(1,:)>K/2)) = P_EST(1,(P_EST(1,:)>K/2))-1; end case 'testevm' % As defined in TS36.141 Annex F.3.4 pilots are averaged in % time across all pilot carrying subcarriers. The resulting % vector is averaged in frequency direction with a moving % averaging window of 19. % Get phase (theta) and magnitude(radius) of complex estimates [theta,radius] = cart2pol(real(P_EST(3,:)),imag(P_EST(3,:))); % Declare vector to temporarily store averaged phase and % magnitude phasemagVec = zeros(size(H_EST,1),2); % Perform averaging in time and frequency for phase and % magnitude separately and recombine at end for phaseOrMag = 1:2 % Define temporary P_EST vector and set estimates row to % phase then magnitude P_EST_temp = P_EST; if phaseOrMag==1 P_EST_temp(3,:) = theta; elseif phaseOrMag==2 P_EST_temp (3,:) = radius; end % Declare averaging vector avgVec = zeros(size(H_EST,1),1); vec = []; % Average across pilot symbol carrying subcarriers in time, % unwrapping performed on a subcarrier basis for i = 1:size(H_EST,1) if ~isempty(P_EST(3,(P_EST(1,:)==i))) if phaseOrMag==1 avgVec(i) = mean(unwrap(P_EST_temp(3,(P_EST(1,:)==i)))); elseif phaseOrMag==2 avgVec(i) = mean(P_EST_temp(3,(P_EST(1,:)==i))); end vec = [vec i]; %#ok end end % Remove subcarriers which contained no pilot symbols, then % perform frequency averaging avgVec = avgVec(vec); % Perform averaging in frequency direction using a moving % window of size N, N = 19 in TS36.141 Annex F.3.4 % Performs a moving average of window size N. At the edges, where less than % N samples are span the window size is reduce to span 1, 3, 5, 7 ... % samples. N = 19; % avgVec must be a column vector if (size(avgVec,2) > 1) error('lte:error','Input to moving average function must be a column vector.') end % N max window size 19 as defined in Annex F.3.4 if ~mod(N,2) error('lte:error','Window size N must be odd'); end if (length(avgVec) < N) % sprintf ('Input signal must have at least %d elements',N); error('lte:error','Input signal must have at least %d elements',N); end % Use filter to perform part of the averaging (not normalized) zeroPad = zeros(N-1,1); data = [avgVec; zeroPad]; weights = ones(N,1); freqAvg = filter(weights,1,data); % Remove unwanted elements removeIdx = [ 2:2:N ( length(data)-(2:2:N)+1 )]; freqAvg(removeIdx) = []; % Normalization factor normFactor = [1:2:N-2 N*ones(1,length(freqAvg)-(N-1)) N-2:-2:1]'; freqAvg = freqAvg./normFactor; % Place frequency averaged pilots into temporary storage % vector phasemagVec(vec,phaseOrMag) = freqAvg; end % Convert averaged symbols back to Cartesian coordinates [X,Y] = pol2cart(phasemagVec(:,1),phasemagVec(:,2)); P_EST = complex(X,Y); scalingVec = []; otherwise error('lte:error','Channel estimation structure field PilotAverage must be one of the following: "UserDefined" or "TestEVM"'); end end function [avgGrid, scalingVec] = normalisePilotAverage(CEC,p_est,grid) % Determine total number of pilots within half a subframe nPilots = length(p_est); avgGrid = zeros(size(grid)); scalingVec = zeros(1,size(p_est,2)); for n = 1:nPilots % Determine in which subcarrier and OFDM symbol pilot is located sc = p_est(1,n); sym = p_est(2,n); % Determine number of REs to look at either side of pilot % symbol in time and frequency half_freq_window = floor(CEC.FreqWindow/2); half_time_window = floor(CEC.TimeWindow/2); % Set the location of the window at the back of the pilot % to be averaged in frequency direction upperSC = sc-half_freq_window; % If this location is outwith the grid dimensions set it to % lowest subcarrier value if upperSC<1 upperSC = 1; end % Set the location of the window in front of the pilot to % be averaged in frequency direction lowerSC = sc+half_freq_window; % If this location is outwith the grid dimensions set it to % highest subcarrier value if lowerSC>size(grid,1) lowerSC = size(grid,1); end % Set the location of the window in front of the pilot to % be averaged in time direction leftSYM = sym-half_time_window; % If this location is outwith the grid dimensions set it to % lowest OFDM symbol value if leftSYM<1 leftSYM = 1; end % Set the location of the window at the back of the pilot % to be averaged in time direction rightSYM = sym+half_time_window; % If this location is outwith the grid dimensions set it to % highest OFDM symbol value if rightSYM>size(grid,2) rightSYM = size(grid,2); end % Define the window to average using the determined % subcarrier and OFDM symbol values avgVec = grid(upperSC:lowerSC,leftSYM:rightSYM); % Remove the zero values from the window so that the average is % calculated using only valid pilots avgVec = avgVec(avgVec~=0); % Average the desired pilot using all pilots within % averaging window if isempty(avgVec) avgVec = 0; end avgGrid(sc,sym) = grid(sc,sym)/numel(avgVec); scalingVec(n) = numel(avgVec); end end %createVirtualPilots Create virtual pilots. % [VP] = createVirtualPilots(..) returns a 3xNvp matrix which contains % the time/frequency and least squares estimate of virtual pilots. % Nvp is the number of virtual pilots. % % The inputs are: % % ENB structure which must contain the following fields: % NDLRB - Number of downlink resource blocks % CyclicPrefix - Optional. Cyclic prefix length {'Normal'(default),'Extended'} % % P_EST - A matrix containing the pilots within the grid. The matrix must % have three rows. Each column contains the information for each pilot: % [subcarrier_indices symbol_indices pilot_channel_estimate] % % ANTENNA - The current antenna port % CREATE_TOP,CREATE_BOTTOM,CREATE_FRONT,CREATE_END - a value {0 or 1} % indicating whether to return virtual pilots in each position. A 1 % indicates virtual pilots should be returned, a 0 indicates they should % not be returned. % % Example: % % Create virtual pilots on the top of a grid of pilots (estimates) % VP_TOP = createVirtualPilots(ENB,estimates,0,1,0,0,0); % % Add virtual pilots to current pilot estimates for interpolation % estimates = [estimates VP_TOP]; % Copyright 2009-2010 The MathWorks, Inc. function VP = createVirtualPilots(ENB,P_EST,CREATE_TOP,CREATE_BOTTOM,CREATE_FRONT,CREATE_END) % Initialize outputs VP = zeros(3,0); % Get resource grid dimensions dims = lteDLResourceGridSize(ENB); K = dims(1); % Virtual pilot cut-off values - indices in time or frequency at which % the virtual pilot shall be discarded. As the symbol indices of the % pilots passed in may be positive and negative the start and end of % the effective grid varies therefore affecting the front and end pilot % cut-off values coTop = -6; % coBottom = K+6; % coFront = min(P_EST(2,:))-7; % floor(min(P_EST(2,:))/L)*L-5; % coEnd = max(P_EST(2,:))+7; % ceil(max(P_EST(2,:))/L)*L+5; % Number of pilots in a subcarrier is dependent upon antenna % noPilotinSC = (2-floor(ANTENNA/2))*nSF; noPilotinSC = numel(P_EST(P_EST(1,:)==P_EST(1,1))); % Number of pilots in a symbol noPilotinSym = K/6; % Sort by subcarrier [~, indices] = sortrows(P_EST(1,:).'); p_est_sorted_sc = P_EST(:,indices).'; if CREATE_TOP % Repeat first and second SC containing pilots p_est_rep_above = p_est_sorted_sc(1+noPilotinSC:noPilotinSC*2,:); p_est_rep_above = [p_est_rep_above.' p_est_sorted_sc(1:noPilotinSC,:).'].'; % Subtract six to normalize subcarrier indices p_est_rep_above(:,1) = p_est_rep_above(:,1) - 6; % Remove virtual pilots which are too far p_est_rep_above(p_est_rep_above(:,1) < coTop,:) = []; % Initialize vp vector VP_TOP = zeros(size(p_est_rep_above)); % Create virtual pilots on top for p = 1:size(p_est_rep_above) VP_TOP(p,:) = calculatePilot(p_est_rep_above(p,:), p_est_sorted_sc); end % Transpose to make suitable for output VP = [VP VP_TOP.']; end if CREATE_BOTTOM % Repeat last and second last SC containing pilots p_est_rep_below = p_est_sorted_sc(end-noPilotinSC*2+1:end-noPilotinSC,:); p_est_rep_below = [p_est_rep_below.' p_est_sorted_sc(end-noPilotinSC+1:end,:).'].'; % Add six to normalize subcarrier indices p_est_rep_below(:,1) = p_est_rep_below(:,1) + 6; % Remove virtual pilots which are too far p_est_rep_below(p_est_rep_below(:,1) > coBottom,:) = []; % Initialize vp vector VP_BOTTOM = zeros(size(p_est_rep_below)); % Create virtual pilots on bottom for p = 1:size(p_est_rep_below) VP_BOTTOM(p,:) = calculatePilot(p_est_rep_below(p,:), p_est_sorted_sc); end % Transpose to make suitable for output VP = [VP VP_BOTTOM.']; end % Sort by symbol [~, indices] = sortrows(P_EST(2,:).'); p_est_sorted_sym = P_EST(:,indices).'; if CREATE_FRONT % Repeat first symbol containing pilots p_est_rep_front = p_est_sorted_sym(noPilotinSym+1:(noPilotinSym*2),:); % Add subcarrier above and below p_est_rep_front = addSCs(p_est_rep_front,noPilotinSym); % Repeat second symbol containing pilots p_est_rep_front_second = p_est_sorted_sym(1:noPilotinSym,:); % Add subcarrier above and below p_est_rep_front_second = addSCs(p_est_rep_front_second,noPilotinSym); % Concatenate to create virtual pilots p_est_rep_front = [p_est_rep_front.' p_est_rep_front_second.'].'; % Subtract seven to normalize symbol indices p_est_rep_front(:,2) = p_est_rep_front(:,2) - 7; % Remove virtual pilots which are too far p_est_rep_front = removeTooFar(p_est_rep_front,p_est_sorted_sym,coTop,coBottom,coFront,coEnd); % Initialize vp vector VP_FRONT = zeros(size(p_est_rep_front)); % Create virtual pilots on front for p = 1:length(p_est_rep_front) VP_FRONT(p,:) = calculatePilot(p_est_rep_front(p,:), p_est_sorted_sc); end % Transpose for output VP = [VP VP_FRONT.']; end if CREATE_END % Repeat last symbol containing pilots p_est_rep_end = p_est_sorted_sym(end-(noPilotinSym*2)+1:end-noPilotinSym,:); % Add subcarrier above and below p_est_rep_end = addSCs(p_est_rep_end,noPilotinSym); % Repeat second symbol containing pilots p_est_rep_end_second = p_est_sorted_sym(end-noPilotinSym+1:end,:); % Add subcarrier above and below p_est_rep_end_second = addSCs(p_est_rep_end_second,noPilotinSym); % Concatenate to create virtual pilots p_est_rep_end = [p_est_rep_end.' p_est_rep_end_second.'].'; % Add seven to normalize symbol indices, mod controls case when % only 2 pilots per subcarrier p_est_rep_end(:,2) = p_est_rep_end(:,2) + 7; % Remove virtual pilots which are too far p_est_rep_end = removeTooFar(p_est_rep_end,p_est_sorted_sym,coTop,coBottom,coFront,coEnd); % Initialize vp vector VP_END = zeros(size(p_est_rep_end)); % Create virtual pilots on front for p = 1:length(p_est_rep_end) VP_END(p,:) = calculatePilot(p_est_rep_end(p,:), p_est_sorted_sc); end % Transpose for output VP = [VP VP_END.']; end end function vp = calculatePilot(p_est_rep, p_est_sorted) % Calculate Euclidean distance between virtual pilot and other % pilots ind = sqrt((p_est_rep(1)-p_est_sorted(:,1)).^2+ (p_est_rep(2)-p_est_sorted(:,2)).^2); % ind = (abs(p_est_rep(1)-p_est_sorted(:,1))+ abs(p_est_rep(2)-p_est_sorted(:,2))); % Sort from shortest to longest distance [~, ind] = sortrows(ind); % Take three closest pilots ind = ind(1:10); pilots_use = p_est_sorted(ind,:); % If first 3 pilots used are in the same subcarrier or symbol then use 4th instead of 3rd while ((pilots_use(3,1) == pilots_use(2,1)) && (pilots_use(2,1) == pilots_use(1,1)) || (pilots_use(3,2) == pilots_use(2,2)) && (pilots_use(2,2) == pilots_use(1,2))) pilots_use(3,:) = []; end % Calculate virtual pilot value vp = calculateVirtualValue(pilots_use(3,:),pilots_use(1,:),pilots_use(2,:),p_est_rep(1),p_est_rep(2)); end function new = calculateVirtualValue(a,b,c,xnew,ynew) % Calculate vectors AB = b-a; AC = c-a; % Perform cross product cro = cross(AB,AC); % Break out X, Y and Z plane coeffs x = cro(1); y = cro(2); z = cro(3); % Calculate normal in equation c = sum(cro.*a); % Calculate new z value znew = (c - xnew*x - ynew*y)/z; % Return new point new = [xnew ynew znew]; end function pilots = addSCs(pilots, noPilotinSym) % Add on extra subcarrier above pilots(end+1,:) = pilots(1,:); pilots(end,1) = pilots(end,1) - 6; % Add on extra subcarrier below pilots(end+1,:) = pilots(noPilotinSym,:); pilots(end,1) = pilots(end,1) + 6; end function pilots = removeTooFar(pilots,p_est_sorted_sym,coTop,coBottom,coFront,coEnd) pilots((pilots(:,2) < coFront) | (pilots(:,1) < coTop) | (pilots(:,2)> coEnd) | (pilots(:,1) > coBottom),:) = []; % Remove virtual pilots which fall within resource grid removeInd = []; for i = 1:size(pilots) if (find(p_est_sorted_sym(:,2) == pilots(i,2))) removeInd = [removeInd i]; %#ok end end pilots(removeInd,:) = []; end %CreateEdgeVirtualPilots % Creates virtual pilots on the edges of the resource grid to improve % interpolation results. Also adds virtual pilots around the current pilot % estimates. function vps = createEdgeVirtualPilots(enb,p_est) % Determine dimensions of current subframe dims=lteDLResourceGridSize(enb); K=dims(1); L=dims(2); % Calculate virtual pilots beyond upper and lower bandwidth edge based % on pilots present on subcarriers. Also create virtual pilots 1/2 RB % above and below extent of current pilots. vps=createDimensionVirtualPilots(p_est,2,unique([-6 min(p_est(1,:)-6) max(p_est(1,:)+6) K+6])); % Combine these frequency-direction VPs with original pilots. temp = [p_est vps]; % Calculate virtual pilots beyond start and end of subframe based on % pilots and frequency-direction VPs present in symbols. Also create % virtual 1 OFDM symbol before and after extent of current pilots. vps = [vps createDimensionVirtualPilots(temp,1,unique([-1 min(p_est(2,:)-1) max(p_est(2,:)+1) L+1]))]; end % dim=1 adds VPs in time % dim=2 adds VPs in frequency function vps = createDimensionVirtualPilots(p_est,dim,points) vps=[]; pilots = unique(p_est(dim,:)); for i = pilots x=find(p_est(dim,:)==i); rep=1+double(length(x)==1); adj=(1:rep)-rep; pil = interp1(p_est(3-dim,x)+adj,repmat(p_est(3,x),1,rep),points,'linear','extrap'); for j=1:length(points) vps = [ vps [i;points(j);pil(j)] ]; %#ok end end if (dim==2) vps = vps([2 1 3],:); end end