You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
12 KiB
281 lines
12 KiB
function [chanEstRU,varargout] = heLTFChannelEstimate(demodHELTFRU,cfg,varargin)
|
|
%heLTFChannelEstimate Channel estimation using HE-LTF
|
|
% CHANESTRU = heLTFChannelEstimate(RXSYM,CFGHE) returns the estimated
|
|
% channel between all space-time streams and receive antennas using
|
|
% HE-LTF of an HE single user, extended range single user, multi-user or
|
|
% trigger-based (HE TB) packet. The channel estimate includes the
|
|
% effect of the applied spatial mapping matrix and cyclic shifts at the
|
|
% transmitter. If HE-LTF compression is used, linear interpolation is
|
|
% performed to create a channel estimate for all subcarriers.
|
|
%
|
|
% CHANESTRU is an array characterizing the estimated channel for the data
|
|
% and pilot subcarriers. EST is a complex Nst-by-Nsts-by-Nr array
|
|
% characterizing the estimated channel for the data and pilot
|
|
% subcarriers, where Nst is the number of occupied subcarriers, Nsts is
|
|
% the total number of space-time streams, and Nr is the number of receive
|
|
% antennas. If CFGHE is a MU configuration, then the channel estimate for
|
|
% all RUs is returned.
|
|
%
|
|
% RXSYM is a complex Nst-by-Nsym-by-Nr array containing demodulated
|
|
% concatenated HE-LTF. Nsym is the number of demodulated HE-LTF symbols.
|
|
%
|
|
% CFGHE is a format configuration object of type <a href="matlab:help('wlanHESUConfig')">wlanHESUConfig</a>
|
|
% <a href="matlab:help('wlanHEMUConfig')">wlanHEMUConfig</a>, <a href="matlab:help('wlanHETBConfig')">wlanHETBConfig</a>, <a href="matlab:help('heTBSystemConfig')">heTBSystemConfig</a> or <a href="matlab:help('wlanHERecoveryConfig')">wlanHERecoveryConfig</a>.
|
|
%
|
|
% CHANESTRU = heLTFChannelEstimate(RXSYM,CFGMU,RUOFINTEREST) returns the
|
|
% channel estimate for the RU of interest index RUOFINTEREST for a
|
|
% multi-user configuration. CFGMU is of type <a href="matlab:help('wlanHEMUConfig')">wlanHEMUConfig</a> or
|
|
% <a href="matlab:help('heTBSystemConfig')">heTBSystemConfig</a>. If not provided the default is 1.
|
|
%
|
|
% [...,CHANESTSSPILOTS] = heLTFChannelEstimate(...) additionally returns
|
|
% a Nsp-by-Nsym-by-Nr array characterizing the estimated channel for
|
|
% pilot subcarrier locations for each symbol, assuming one space-time
|
|
% stream at the transmitter.
|
|
%
|
|
% Examples:
|
|
% % Decode the HE data field for each user in and OFDMA and MU-MIMO
|
|
% % transmission with a fading channel model. Estimate the channel for
|
|
% % each user.
|
|
%
|
|
% % Create packet configuration
|
|
% allocationIndex = [192 193]; % Two 242 RUs, the second with 2 users
|
|
% cfg = wlanHEMUConfig(allocationIndex);
|
|
% cfg.NumTransmitAntennas = 2;
|
|
% cfg.User{1}.NumSpaceTimeStreams = 2;
|
|
%
|
|
% % Generate MU waveform
|
|
% txWaveform = wlanWaveformGenerator([1;0;0;1],cfg);
|
|
%
|
|
% % Channel and receiver per user
|
|
% ind = wlanFieldIndices(cfg);
|
|
% allocationInfo = ruInfo(cfg);
|
|
% for ruIdx = 1:allocationInfo.NumRUs
|
|
% for userIdx = 1:allocationInfo.NumUsersPerRU(ruIdx)
|
|
% % Add channel and noise
|
|
% snr = 20;
|
|
% channel = wlanTGaxChannel;
|
|
% channel.NumTransmitAntennas = 2;
|
|
% channel.NumReceiveAntennas = 2;
|
|
% channel.SampleRate = wlanSampleRate(cfg);
|
|
% rxWaveform = awgn(channel([txWaveform; zeros(10,2)]),snr);
|
|
%
|
|
% % Synchronize
|
|
% rxWaveform = rxWaveform(1+4:end,:);
|
|
%
|
|
% % Extract and OFDM demodulate the HE-LTF for the RU of
|
|
% % interest
|
|
% rxHETLF = rxWaveform(ind.HELTF(1): ind.HELTF(2),:);
|
|
% demodHELTF = wlanHEDemodulate(rxHETLF,'HE-LTF',cfg,ruIdx);
|
|
%
|
|
% % Channel estimate for RU of interest
|
|
% chanEst = heLTFChannelEstimate(demodHELTF,cfg,ruIdx);
|
|
%
|
|
% % Extract and OFDM demodulate the data field for the RU of
|
|
% % interest
|
|
% rxData = rxWaveform(ind.HEData(1):ind.HEData(2),:);
|
|
% demodData = wlanHEDemodulate(rxData,'HE-Data',cfg,ruIdx);
|
|
%
|
|
% % Equalize data symbols - extract the space-time streams for
|
|
% % the user of interest after equalization
|
|
% nVar = 10^-(snr/10);
|
|
% [eqSym,csi] = heEqualizeCombine(demodData,chanEst,nVar, ...
|
|
% cfg,userIdx);
|
|
%
|
|
% % Discard pilot carriers and decode
|
|
% info = wlanHEOFDMInfo('HE-Data',cfg,ruIdx);
|
|
% rxBits = wlanHEDataBitRecover(eqSym(info.DataIndices,:,:), ...
|
|
% nVar,csi(info.DataIndices,:),cfg,userIdx);
|
|
% end
|
|
% end
|
|
|
|
% Copyright 2017-2019 The MathWorks, Inc.
|
|
|
|
%#codegen
|
|
|
|
if nargin>2
|
|
ruOfInterest = varargin{1};
|
|
else
|
|
ruOfInterest = 1;
|
|
end
|
|
|
|
% Validate the format configuration object is a valid type
|
|
validateattributes(cfg,{'wlanHESUConfig','wlanHEMUConfig','wlanHETBConfig','heTBSystemConfig','wlanHERecoveryConfig'},{'scalar'},mfilename,'format configuration object');
|
|
|
|
% Get allocation information
|
|
if isa(cfg,'wlanHERecoveryConfig')
|
|
ruSizeRU = cfg.RUSize;
|
|
ruIndexRU = cfg.RUIndex;
|
|
pktFormat = cfg.PacketFormat;
|
|
if strcmp(pktFormat,'HE-MU')
|
|
numSTSRU = cfg.RUTotalSpaceTimeStreams;
|
|
else % SU, EXT SU
|
|
numSTSRU = cfg.NumSpaceTimeStreams;
|
|
end
|
|
else
|
|
allocInfo = ruInfo(cfg);
|
|
coder.internal.errorIf(ruOfInterest>allocInfo.NumRUs,'wlan:he:InvalidRUOfInterest',ruOfInterest,allocInfo.NumRUs);
|
|
ruSizeRU = allocInfo.RUSizes(ruOfInterest);
|
|
ruIndexRU = allocInfo.RUIndices(ruOfInterest);
|
|
numSTSRU = allocInfo.NumSpaceTimeStreamsPerRU(ruOfInterest);
|
|
pktFormat = packetFormat(cfg);
|
|
end
|
|
|
|
% Validate symbol type
|
|
validateattributes(demodHELTFRU,{'single','double'},{'3d'},mfilename,'HE-LTF OFDM symbol(s)');
|
|
[numST,numLTF,numRx] = size(demodHELTFRU);
|
|
tac = wlan.internal.heRUToneAllocationConstants(ruSizeRU);
|
|
coder.internal.errorIf(numST~=tac.NST,'wlan:wlanChannelEstimate:IncorrectNumSC',tac.NST,numST);
|
|
ofdmInfo = wlanHEOFDMInfo('HE-LTF',cfg.ChannelBandwidth,cfg.GuardInterval,[ruSizeRU ruIndexRU]);
|
|
if numLTF==0
|
|
chanEstRU = zeros(numST,numSTSRU,numRx);
|
|
varargout{1} = zeros(numel(ofdmInfo.PilotIndices),numLTF,numRx); % For codegen
|
|
return;
|
|
end
|
|
minNumLTF = wlan.internal.numVHTLTFSymbols(numSTSRU);
|
|
coder.internal.errorIf(numLTF<minNumLTF,'wlan:he:InvalidNumLTF',numLTF,minNumLTF);
|
|
|
|
% Get the HE-LTF sequence
|
|
cbw = wlan.internal.cbwStr2Num(cfg.ChannelBandwidth);
|
|
[HELTF,kHELTFSeq] = wlan.internal.heLTFSequence(cbw,cfg.HELTFType);
|
|
|
|
% Extract the RU of interest from the full-bandwidth HELTF
|
|
kRU = ofdmInfo.ActiveFrequencyIndices;
|
|
[~,ruIdx] = intersect(kHELTFSeq,kRU);
|
|
HELTFRU = HELTF(ruIdx);
|
|
|
|
switch cfg.HELTFType
|
|
% IEEE P802.11ax/D4.1, Equation 27-52
|
|
case 1
|
|
N_HE_LTF_Mode = 4; % undefined
|
|
case 2
|
|
N_HE_LTF_Mode = 2;
|
|
otherwise % 4
|
|
N_HE_LTF_Mode = 1;
|
|
end
|
|
|
|
isaTBConfig = isa(cfg,'heTBSystemConfig') || isa(cfg,'wlanHETBConfig');
|
|
if numSTSRU==1
|
|
% Single STS
|
|
|
|
% When more than one LTF we can average over the LTFs for data and
|
|
% pilots to improve the estimate. As there is only one space-time
|
|
% stream, the pilots and data essentially both use the P matrix which
|
|
% does not change per space-time stream (only per symbol), therefore
|
|
% this "MIMO" estimate performs the averaging of the number of symbols.
|
|
chanEstRU = wlan.internal.mimoChannelEstimate(demodHELTFRU,HELTFRU,numSTSRU);
|
|
|
|
% Remove orthogonal sequence across subcarriers (if used)
|
|
if isaTBConfig && cfg.SingleStreamPilots==false
|
|
chanEstRU = removeOrthogonalSequence(chanEstRU,numSTSRU,kRU,N_HE_LTF_Mode);
|
|
end
|
|
|
|
% Interpolate if HE-LTF compression used
|
|
if N_HE_LTF_Mode>1
|
|
chanEstRU = chanEstInterp(chanEstRU,cbw,N_HE_LTF_Mode,ruSizeRU,ruIndexRU);
|
|
end
|
|
else
|
|
% MIMO channel estimation as per Perahia, Eldad, and Robert Stacey.
|
|
% Next Generation Wireless LANs: 802.11 n and 802.11 ac. Cambridge
|
|
% University Press, 2013, page 100, Equation 4.39.
|
|
% Remove orthogonal sequence across subcarriers (if used)
|
|
if isaTBConfig && cfg.SingleStreamPilots==false
|
|
% Only perform channel estimate for non-pilot subcarriers as pilots
|
|
% are single stream
|
|
kMIMO = kRU; % All subcarriers MIMO estimates
|
|
mimoInd = 1:numST;
|
|
chanEstRUMIMO = wlan.internal.mimoChannelEstimate(demodHELTFRU,HELTFRU,numSTSRU);
|
|
chanEstRUMIMO = removeOrthogonalSequence(chanEstRUMIMO,numSTSRU,kRU,N_HE_LTF_Mode);
|
|
else
|
|
% Only perform channel estimate for non-pilot subcarriers as pilots
|
|
% are single stream
|
|
mimoInd = ofdmInfo.DataIndices;
|
|
kMIMO = kRU(mimoInd); % Only data subcarriers MIMO estimates
|
|
chanEstRUMIMO = wlan.internal.mimoChannelEstimate(demodHELTFRU(ofdmInfo.DataIndices,:,:),HELTFRU(mimoInd),numSTSRU);
|
|
end
|
|
|
|
% Undo cyclic shift for each STS before averaging and interpolation
|
|
nfft = (cbw/20)*256;
|
|
numSTSTotal = size(chanEstRUMIMO,2);
|
|
csh = wlan.internal.getCyclicShiftVal('VHT',numSTSTotal,cbw);
|
|
chanEstRUMIMO = wlan.internal.cyclicShiftChannelEstimate(chanEstRUMIMO,-csh,nfft,kMIMO);
|
|
|
|
% Interpolate over pilot locations, and any compressed subcarriers
|
|
chanEstRU = chanEstInterp(chanEstRUMIMO,cbw,N_HE_LTF_Mode,ruSizeRU,ruIndexRU,mimoInd);
|
|
|
|
% Re-apply cyclic shift after interpolation
|
|
chanEstRU = wlan.internal.cyclicShiftChannelEstimate(chanEstRU,csh,nfft,kRU);
|
|
end
|
|
|
|
% If extended range SU, then the HE-LTF are boosted by sqrt(2). If we
|
|
% don't remove this at demodulation then we must de-scale the channel
|
|
% estimate as the data field is not scaled.
|
|
if strcmp(pktFormat,'HE-EXT-SU')
|
|
eta = 1/sqrt(2);
|
|
else
|
|
eta = 1;
|
|
end
|
|
chanEstRU = chanEstRU*eta; % Scale for HE-EXT-SU
|
|
|
|
% Channel estimate for pilots
|
|
if nargout>1
|
|
if isaTBConfig && cfg.SingleStreamPilots==false
|
|
% Create single stream from MIMO pilot estimates by summing across
|
|
% space-time streams (2nd dimension)
|
|
varargout{1} = sum(chanEstRU(ofdmInfo.PilotIndices,:,:),2);
|
|
else
|
|
% Channel estimate for single-stream pilots
|
|
Pheltf = wlan.internal.mappingMatrix(numLTF);
|
|
R = Pheltf(1,1:numLTF); % R matrix changes pilot polarity per symbol
|
|
% Estimate the channel at pilot subcarriers accounting for polarity
|
|
chanEstSSPilots = bsxfun(@rdivide,demodHELTFRU(ofdmInfo.PilotIndices,:,:),bsxfun(@times,HELTFRU(ofdmInfo.PilotIndices),R));
|
|
varargout{1} = chanEstSSPilots*eta; % Scale for HE_EXT_SU
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function chanEstRUInterp = chanEstInterp(chanEstRU,cbw,N_HE_LTF_Mode,ruSize,ruIndex,varargin)
|
|
% Interpolate over pilot locations and compressed subcarriers
|
|
|
|
Nfft = 256*cbw/20;
|
|
|
|
% Get the subcarrier indices within the FFT for the channel estimate
|
|
% input
|
|
kAct = wlan.internal.heRUSubcarrierIndices(cbw,ruSize,ruIndex)+Nfft/2+1;
|
|
% If the channelEstRU is not the entire RU, then we need to make sure
|
|
% we know the subcarrier indices, so use the ruInd input. For example
|
|
% this allows us to pass in only the data subcarriers.
|
|
if nargin>5
|
|
ruInd = varargin{1};
|
|
kChanEstInputs = kAct(ruInd);
|
|
else
|
|
% Assume chanEstRU is the whole RU
|
|
kChanEstInputs = kAct;
|
|
end
|
|
|
|
% Get the indices within the FFT which contain actual estimates
|
|
% (excluding the guard bands). This is how the pattern is structured
|
|
kAll = 1:N_HE_LTF_Mode:Nfft;
|
|
|
|
% Find the subcarrier indices within the FFT which contain actual data
|
|
% within the channel estimate input (kToInterp) and the indices of
|
|
% these within the chanEstDataRU input array (toInterpInd)
|
|
[kToInterp,toInterpInd] = intersect(kChanEstInputs,kAll);
|
|
|
|
% Interpolate and extrapolate over all RU subcarrier indices to
|
|
% interpolate over compressed region and pilots
|
|
magPart = interp1(kToInterp.',abs(chanEstRU(toInterpInd,:,:)),kAct,'linear','extrap');
|
|
phasePart = interp1(kToInterp.',unwrap(angle(chanEstRU(toInterpInd,:,:))),kAct,'linear','extrap');
|
|
[realPart,imagPart] = pol2cart(phasePart,magPart);
|
|
chanEstRUInterp = complex(realPart,imagPart);
|
|
|
|
end
|
|
|
|
function chanEstRUData = removeOrthogonalSequence(chanEstRUData,numSTSRU,k,N_HE_LTF_Mode)
|
|
% Remove the orthogonal sequence across subcarriers
|
|
M = 0; % Assume space-time streams of all users in estimate
|
|
m = 1:numSTSRU;
|
|
Pheltf = wlan.internal.mappingMatrix(8);
|
|
seq = Pheltf(M+m,mod(ceil(k/N_HE_LTF_Mode)-1,8)+1).'; % Nsts-by-Nst
|
|
chanEstRUData = chanEstRUData./seq;
|
|
end
|
|
|