HE/heTrackingOFDMDemodulate.m

329 lines
13 KiB
Mathematica
Raw Normal View History

2024-03-30 16:35:40 +08:00
function [ofdmDemod,cpe,peg,pilotGain] = heTrackingOFDMDemodulate(rxData,chanEst,numOFDMSym,cfgHE,cfgTrack,varargin)
%heTrackingOFDMDemodulate OFDM demodulation with pilot tracking
%
% Note: This is an internal undocumented function and its API and/or
% functionality may change in subsequent releases.
%
% [DATASYM,CPE,PEG,PILOTGAIN] = trackingOFDMDemodulate(RXDATA,CHANEST,
% NUMOFDMSYM, CFGSU,CFGTRACK) performs OFDM demodulation of data
% symbols with optional pilot tracking.
%
% DATASYM is a complex Nsd-by-Nsym-by-Nr array containing the demodulated
% symbols at data carrying subcarriers. Nsd represents the number of data
% subcarriers, Nsym represents the number of OFDM symbols in the Data
% field, and Nr represents the number of receiver antennas.
%
% CPE is a column vector of length Nsym containing the common phase error
% between each received and expected OFDM symbol.
%
% PEG is a column vector of length Nsym containing the phase error
% gradient per OFDM symbol in degrees per subcarrier. This error is
% caused by a sample rate offset between transmitter and receiver.
%
% PILOTGAIN is an Nsym-by-Nsp array containing the gain of pilots. Nsp is
% the number of pilot subcarriers.
%
% RXDATA is the received time-domain Data field signal, specified as an
% Ns-by-Nr matrix of real or complex values. Ns represents the number of
% time-domain samples in the Data field and Nr represents the number of
% receive antennas. Ns can be greater than the Data field length; in this
% case additional samples at the end of RXDATA, if not required, are not
% used. When sample rate offset tracking is enabled using the optional
% CFTRACK argument, additional samples may be required in RXDATA. This is
% to allow for the receiver running at a higher sample rate than the
% transmitter and therefore more samples being required.
%
% CHANEST is a complex Nst-by-Nsts-by-Nr array containing the channel
% gains at active subcarriers, or a Nsp-by-Nsts-by-Nr array containing
% the channel gains at pilot subcarriers. Nsts is the number of
% space-time streams.
%
% NUMOFDMSYM is the number of OFDM symbols expected.
%
% CFGSU is a format configuration object of type <a href="matlab:help('wlanHESUConfig')">wlanHESUConfig</a> or
% <a href="matlab:help('wlanHERecoveryConfig')">wlanHERecoveryConfig</a>.
%
% CFGTRACK is a pilot tracking configuration structure.
%
% [...] = trackingOFDMDemodulate(RXDATA,CHANEST,NUMOFDMSYM,CFGMU,
% CFGTRACK,RUNUMBER) performs OFDM demodulation of data symbols with
% optional pilot tracking for a multi-user configuration. As a
% multi-user configuration is provided and resource unit number is
% required.
%
% CFGMU is the format configuration object of type <a href="matlab:help('wlanHEMUConfig')">wlanHEMUConfig</a>.
%
% RUNUMBER is the RU (resource unit) number.
% Copyright 2018-2020 The MathWorks, Inc.
cpe = nan(numOFDMSym,1); % Initialize in case not calculated
peg = nan(numOFDMSym,1); % Initialize in case not calculated
pilotGain = nan(numOFDMSym,1);
if strcmp(cfgTrack.PilotTracking,'Joint')
% Perform joint measurement and optionally correction
% Reduce the size of the averaging window if it exceeds the number of
% OFDM symbols. If it is even then use the largest odd window we can.
if cfgTrack.PilotTrackingWindow>numOFDMSym
cfgTrack.PilotTrackingWindow = numOFDMSym-(rem(numOFDMSym,2)==0);
end
[ofdmDemod,peg,cpe,pilotGain] = demodulateWithPhaseTracking(rxData,chanEst,numOFDMSym,cfgHE,cfgTrack,varargin{:});
else
% OFDM demodulate only when no joint tracking
if isa(cfgHE,'wlanHERecoveryConfig')
ofdmDemod = wlanHEDemodulate(rxData,'HE-Data',cfgHE.ChannelBandwidth,cfgHE.GuardInterval, ...
[cfgHE.RUSize cfgHE.RUIndex],'OFDMSymbolOffset',cfgTrack.OFDMSymbolOffset);
else
ofdmDemod = wlanHEDemodulate(rxData,'HE-Data',cfgHE,varargin{:},'OFDMSymbolOffset',cfgTrack.OFDMSymbolOffset);
end
ofdmDemod = ofdmDemod(:,1:numOFDMSym,:); % Truncate output as extra samples may be passed
if strcmp(cfgTrack.PilotTracking,'CPE')
% Pilot phase tracking
[ofdmDemod,cpe] = heCommonPhaseErrorTracking(ofdmDemod,chanEst,cfgHE,varargin{:});
cpe = cpe.'; % Permute to return
end
end
end
function [ofdmDemod,peg,cpe,pilotGain] = demodulateWithPhaseTracking(rxData,chanEst,numOFDMSym,cfg,cfgTrack,varargin)
if isa(cfg,'wlanHERecoveryConfig')
pktFormat = cfg.PacketFormat;
if strcmp(pktFormat,'HE-MU')
numSpaceTimeStreamsPerRU = cfg.RUTotalSpaceTimeStreams;
s = getSIGBLength(cfg);
numHESIGB = s.NumSIGBSymbols;
else % SU or EXT_SU
numSpaceTimeStreamsPerRU = cfg.NumSpaceTimeStreams;
numHESIGB = 0;
end
ruSize = cfg.RUSize;
ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg.ChannelBandwidth,cfg.GuardInterval,[cfg.RUSize cfg.RUIndex]);
else
pktFormat = packetFormat(cfg);
allocInfo = ruInfo(cfg);
if isa(cfg,'wlanHEMUConfig')
ruNumber = varargin{1};
ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg,ruNumber);
sigbInfo = wlan.internal.heSIGBCodingInfo(cfg);
numHESIGB = sigbInfo.NumSymbols;
numSpaceTimeStreamsPerRU = allocInfo.NumSpaceTimeStreamsPerRU(ruNumber);
ruSize = allocInfo.RUSizes(ruNumber);
else
% SU or EXT_SU
ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg);
numHESIGB = 0;
numSpaceTimeStreamsPerRU = allocInfo.NumSpaceTimeStreamsPerRU;
ruSize = allocInfo.RUSizes;
end
end
if strcmp(pktFormat,'HE-EXT-SU')
numHESIGA = 4;
else % SU or MU
numHESIGA = 2;
end
% OFDM demodulate configuration
prmStr = struct;
prmStr.NumReceiveAntennas = size(rxData,2);
prmStr.FFTLength = ofdmInfo.FFTLength;
prmStr.NumSymbols = 1;
prmStr.SymbolOffset = cfgTrack.OFDMSymbolOffset*ofdmInfo.CPLength(1);
prmStr.CyclicPrefixLength = ofdmInfo.CPLength(1);
N = ofdmInfo.FFTLength; % FFT length is samples
Ng = ofdmInfo.CPLength; % Number of samples in GI
Ns = (N+Ng); % Number of samples per symbols
kst = ofdmInfo.ActiveFrequencyIndices; % Indices of all active subcarriers
kd = kst(ofdmInfo.DataIndices); % Indices of data carrying subcarriers
kp = kst(ofdmInfo.PilotIndices); % Indices of pilot carrying subcarriers
Nd = numel(kd); % Number of data carrying subcarriers
Np = numel(kp); % Number of pilot carrying subcarriers
Nr = size(chanEst,3); % Number of receive antennas
n = (0:numOFDMSym-1);
z = 2+numHESIGA+numHESIGB; % Pilot symbol offset
if numel(ofdmInfo.PilotIndices)==size(chanEst,1)
% Assume channel estimate is only for pilots
chanEstPilots = chanEst;
else
% Otherwise extract pilots from channel estimate
chanEstPilots = chanEst(ofdmInfo.PilotIndices,:,:);
end
nsts = min(numSpaceTimeStreamsPerRU,size(chanEstPilots,2)); % Allow for single-stream or MIMO pilots
refPilots = wlan.internal.hePilots(ruSize,nsts,n,z);
% Reshape for computation
chanEstPilotsR = permute(chanEstPilots,[3 2 1]);
refPilotsR = permute(refPilots,[3 1 2]); % Generate reference pilots
% Calculate expected pilot values
pilotExp = complex(zeros(Np,numOFDMSym));
for n = 1:numOFDMSym
for p=1:Np
pilotExp(p,n) = sum(reshape(chanEstPilotsR(:,:,p)*refPilotsR(:,p,n),[],1));
end
end
ofdmDemodPilots = complex(zeros(Np,numOFDMSym,Nr));
ofdmDemod = complex(zeros(Nd+Np,numOFDMSym,Nr));
perr = complex(zeros(Np,numOFDMSym)); % Pilot error
delta = zeros(numOFDMSym,1);
omega = zeros(numOFDMSym,1);
skipDupStore = zeros(numOFDMSym,1);
skipdup = 0;
nD = 0; % Number of OFDM symbols demodulated
for n = 1:numOFDMSym
% Get index of samples to demodulate in current symbol
skipDupStore(n) = skipdup;
idx = (n-1)*Ns+(1:Ns)+skipdup;
if any(idx>size(rxData,1))
% Break from loop if we run out of data
coder.internal.warning('wlan:trackingOFDMDemodulate:NotEnoughSamples',numOFDMSym,n-1);
break;
end
% OFDM demodulation
demod = commonDemod(rxData(idx,:),ofdmInfo,prmStr);
ofdmDemodPilots(:,n,:) = demod(ofdmInfo.PilotIndices,1,1:Nr); % for codegen
ofdmDemod(:,n,:) = demod(:,1,1:Nr); % for codegen
% Calculate pilot error
ofdmDemodPilotsR = permute(ofdmDemodPilots(:,n,:),[2 3 1]);
for p = 1:Np
perr(p,n) = reshape(conj(ofdmDemodPilotsR(1,:,p))*chanEstPilotsR(:,:,p)*refPilotsR(:,p,n),1,1);
end
% Average pilots over time window
perridx = max((n-cfgTrack.PilotTrackingWindow+1),1):n;
% Find indices which span across a skip/dup
spanSkipDup = perridx(skipDupStore(perridx)~=skipDupStore(perridx(end)));
if any(spanSkipDup)
% Remove phase shift offset caused by skip/dup and average
skipdupVal = (skipDupStore(perridx)-skipDupStore(perridx(end))).';
perrav = sum(perr(:,perridx).*exp(1i*2*pi*bsxfun(@times,skipdupVal,kp)/N),2);
else
perrav = sum(perr(:,perridx),2);
end
if n>1
% Subtract the previous common phase from the current to avoid
% needing to wrap (use phasor to avoid angles wrapping across
% pilots before subtraction)
perrav = perrav*exp(-1i*omega(n-1));
end
% Least square estimation with covariance estimate per symbol
j = lscov([kp ones(size(kp))],angle(perrav),abs(perrav));
delta(n) = j(1); % Time offset
% If subtracting previous common phase then add it back on to
% common phase error
if n>1
omega(n) = j(2)+omega(n-1);
else
omega(n) = j(2);
end
% Skip or duplicate a sample in the next OFDM symbol if
% required
if delta(n)>=(2*pi/N)*0.9
skipdup = skipdup+1; % Skip
elseif delta(n)<=-(2*pi/N)*0.9
skipdup = skipdup-1; % Duplicate
end
nD = n; % Record number of demodulated symbols
end
pilotGain = movmean(abs(perr).',cfgTrack.PilotTrackingWindow/2);
% The averaging causes a delay which we correct for before applying
% correction
delay = (cfgTrack.PilotTrackingWindow-1)/2;
% When a skip-dup occurred we changed the phase to allow averaging over
% the skip/dup. Now correct for any phase change applied
skipindTmp = bsxfun(@plus,(find((diff(skipDupStore))==1)+1),(0:delay-1));
skipind = skipindTmp(:); % for codegen
dupindTmp = bsxfun(@plus,(find((diff(skipDupStore))==-1)+1),(0:delay-1));
dupind = dupindTmp(:); % for codegen
skipCorrIdx = skipind(skipind<=numOFDMSym);
delta(skipCorrIdx) = delta(skipCorrIdx)+2*pi/N;
dupCorrIdx = dupind(dupind<=numOFDMSym);
delta(dupCorrIdx) = delta(dupCorrIdx)-2*pi/N;
% Use shrinking window at end of waveform to average pilots and account
% for delay
keepIdx = setdiff(1:numOFDMSym,2:2:cfgTrack.PilotTrackingWindow); % Remove even averages at start when growing window
deltaTmp = [delta(keepIdx); zeros(delay,1)];
omegaTmp = [omega(keepIdx); zeros(delay,1)];
extDelta = zeros(delay,1);
for i = 1:delay
% Remove difference of phases due to skip/dup over averaging window
skipdupVal = (skipDupStore(nD-(cfgTrack.PilotTrackingWindow-2*i)+1:nD)-skipDupStore(nD)).';
perrav = sum(perr(:,nD-(cfgTrack.PilotTrackingWindow-2*i)+1:nD).*exp(1i*2*pi.*bsxfun(@times,skipdupVal,kp)/N),2);
% Remove previous CPE before LS estimation
perrav = perrav*exp(-1i*omegaTmp(nD-delay+i-1));
% Reapply phase offset removed for averaging due to skip/dup
angleperrav = angle(perrav)-skipdupVal(delay-i+1)*2*pi.*kp/N;
% Least-square estimation
jt = lscov([kp ones(size(kp))],angleperrav,abs(perrav));
extDelta(i) = jt(1);
omegaTmp(nD-delay+i) = jt(2)+omegaTmp(nD-delay+i-1); % Add previous CPE
end
delta(1:nD) = [deltaTmp(1:nD-delay); extDelta];
omega = omegaTmp;
% Apply correction if requested
if strcmp(cfgTrack.PilotTracking,'Joint')
% Correction for timing
corrst = exp(1i*bsxfun(@times,delta.',kst));
% Correction for phase
corrst = bsxfun(@times,corrst,exp(1i*omega.'));
% Apply per symbol correction of subcarriers
ofdmDemod(:,1:nD,:) = bsxfun(@times,ofdmDemod(:,1:nD,:),corrst(:,1:nD));
end
% Return estimate of impairments
cpe = -omega;
peg = -delta;
% Perform pilot gain tracking if requested
if cfgTrack.PilotGainTracking == true
normGain = pilotGain./pilotGain(1,:);
weightedAverages = pilotGain(1,:)./sum(pilotGain(1,:));
gainTracking = sum(normGain.*weightedAverages,2);
ofdmDemod = ofdmDemod./gainTracking.';
end
end
function x = lscov(A,b,V)
% Weights given, scale rows of design matrix and response.
D = sqrt(V(:));
A(:,1) = A(:,1).*D;
A(:,2) = A(:,2).*D;
b = b.*D;
% Factor the design matrix, incorporate covariances or weights into the
% system of equations, and transform the response vector.
[Q,R] = qr(A,0);
z = Q'*b;
% Compute the LS coefficients
x = real(R\z);
end
function demod = commonDemod(rx,cfgOFDM,prmStr)
fftout = comm.internal.ofdm.demodulate(rx,prmStr);
% Extract active subcarriers from full FFT
demod = fftout(cfgOFDM.ActiveFFTIndices,:,:);
% Scale by number of active tones and FFT length
demod = demod*sqrt(cfgOFDM.NumTones)/cfgOFDM.FFTLength;
end