374 lines
14 KiB
Mathematica
374 lines
14 KiB
Mathematica
![]() |
function [rxPSDU,bitErrorRate,eqSymPlot,evm] = heMURx(rx,cfg,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot)
|
|||
|
% [rxPSDU,eqSymUser,csiData/csi,pktFormat,evm,cfoCorrection]=heMURx(rx,cfgMUMIMO,userIdx,cfgPara);
|
|||
|
% numBitError = 0;
|
|||
|
% wlan ug
|
|||
|
%% Setup Waveform Recovery Parameters
|
|||
|
% Perform synchronization with 11ac components
|
|||
|
chanBW = cfg.ChannelBandwidth;
|
|||
|
fs = wlanSampleRate(cfg);
|
|||
|
% Specify pilot tracking method for recovering the data field. This can be:
|
|||
|
% 'Joint' - use joint common phase error and sample rate offset tracking
|
|||
|
% 'CPE' - use only common phase error tracking
|
|||
|
% When recovering 26-tone RUs only CPE tracking is used as the joint
|
|||
|
% tracking algorithm is susceptible to noise.
|
|||
|
pilotTracking = 'Joint';
|
|||
|
% Create an HE recovery configuration object and set the channel bandwidth
|
|||
|
cfgRx = wlanHERecoveryConfig;
|
|||
|
cfgRx.ChannelBandwidth = chanBW;
|
|||
|
|
|||
|
% Get the field indices for extract fields from the PPDU
|
|||
|
% ind = wlanFieldIndices(cfg);
|
|||
|
ind = wlanFieldIndices(cfgRx);
|
|||
|
|
|||
|
% % Setup plots for the example
|
|||
|
% [spectrumAnalyzer,timeScope,ConstellationDiagram,EVMPerSubcarrier,EVMPerSymbol] = heSigRecSetupPlots(fs);
|
|||
|
|
|||
|
% Minimum packet length is 10 OFDM symbols
|
|||
|
% lstfLength = double(ind.LSTF(2));
|
|||
|
% minPktLen = lstfLength*5; % Number of samples in L-STF
|
|||
|
|
|||
|
rxWaveLen = size(rx,1);
|
|||
|
|
|||
|
%% Front-End Processing
|
|||
|
searchOffset = 0; % Offset from start of waveform in samples
|
|||
|
% Packet detection
|
|||
|
coarsePktOffset = wlanPacketDetect(rx,chanBW,searchOffset,0.8);
|
|||
|
LSTF = wlanLSTF(wlanNonHTConfig);
|
|||
|
% if isempty(coarsePktOffset)||(coarsePktOffset + ind.LSIG(2) > rxWaveLen) % If empty no L-STF detected; packet error
|
|||
|
% numBitError = numBitError+numBits;%%???????????????
|
|||
|
% userIdx = userIdx+1;
|
|||
|
% end
|
|||
|
% Coarse frequency offset estimation and correction using L-STF
|
|||
|
if isempty(coarsePktOffset)
|
|||
|
coarsePktOffset = 0;
|
|||
|
end
|
|||
|
rxLSTF = rx(coarsePktOffset+(ind.LSTF(1):ind.LSTF(2)), :);
|
|||
|
tmp_xcorr = abs(xcorr(LSTF,rxLSTF));
|
|||
|
coarseFreqOffset = wlanCoarseCFOEstimate(rxLSTF,chanBW);
|
|||
|
rx = helperFrequencyOffset(rx,fs,-coarseFreqOffset);
|
|||
|
% Extract the non-HT fields and determine fine packet offset
|
|||
|
nonhtfields = rx(coarsePktOffset+(ind.LSTF(1):ind.LSIG(2)),:);
|
|||
|
finePktOffset = wlanSymbolTimingEstimate(nonhtfields,chanBW);
|
|||
|
|
|||
|
% Determine final packet offset
|
|||
|
pktOffset = coarsePktOffset+finePktOffset;
|
|||
|
|
|||
|
% if pktOffset>50%%????????????????????????????///
|
|||
|
% % numPacketErrors(userIdx) = numPacketErrors(userIdx)+1;
|
|||
|
% numBitError = numBitError+numBits;
|
|||
|
% userIdx = userIdx+1;
|
|||
|
% % continue; % Go to next loop iteration
|
|||
|
% end
|
|||
|
|
|||
|
% Extract L-LTF and perform fine frequency offset correction
|
|||
|
rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:);
|
|||
|
fineFreqOff = wlanFineCFOEstimate(rxLLTF,chanBW);
|
|||
|
rx = helperFrequencyOffset(rx,fs,-fineFreqOff);
|
|||
|
|
|||
|
% Timing synchronization complete: packet detected
|
|||
|
fprintf('Packet detected at index %d\n',pktOffset + 1);
|
|||
|
|
|||
|
% Display estimated carrier frequency offset
|
|||
|
cfoCorrection = coarseFreqOffset + fineFreqOff; % Total CFO
|
|||
|
fprintf('Estimated CFO: %5.1f Hz\n\n',cfoCorrection);
|
|||
|
|
|||
|
|
|||
|
if max(tmp_xcorr)>50
|
|||
|
|
|||
|
% Scale the waveform based on L-STF power (AGC)
|
|||
|
gain = 1./(sqrt(mean(rxLSTF.*conj(rxLSTF))));
|
|||
|
rx = rx.*gain;
|
|||
|
|
|||
|
%% Packet Format Detection
|
|||
|
|
|||
|
rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:);
|
|||
|
lltfDemod = wlanLLTFDemodulate(rxLLTF,chanBW);
|
|||
|
lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW);
|
|||
|
noiseVar = helperNoiseEstimate(lltfDemod);
|
|||
|
|
|||
|
rxSIGA = rx(pktOffset+(ind.LSIG(1):ind.HESIGA(2)),:);
|
|||
|
pktFormat = wlanFormatDetect(rxSIGA,lltfChanEst,noiseVar,chanBW);
|
|||
|
fprintf(' %s packet detected\n\n',pktFormat);
|
|||
|
|
|||
|
% Set the packet format in the recovery object and update the field indices
|
|||
|
cfgRx.PacketFormat = pktFormat;
|
|||
|
ind = wlanFieldIndices(cfgRx);
|
|||
|
|
|||
|
%% L-LTF Channel Estimate
|
|||
|
lltfDemod = wlanHEDemodulate(rxLLTF,'L-LTF',chanBW);
|
|||
|
lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW);
|
|||
|
|
|||
|
%% L-SIG and RL-SIG Decoding
|
|||
|
% Extract L-SIG and RL-SIG fields
|
|||
|
rxLSIG = rx(pktOffset+(ind.LSIG(1):ind.RLSIG(2)),:);
|
|||
|
|
|||
|
% OFDM demodulate
|
|||
|
helsigDemod = wlanHEDemodulate(rxLSIG,'L-SIG',chanBW);
|
|||
|
|
|||
|
% Estimate CPE and phase correct symbols
|
|||
|
helsigDemod = preHECommonPhaseErrorTracking(helsigDemod,lltfChanEst,'L-SIG',chanBW);
|
|||
|
|
|||
|
% Estimate channel on extra 4 subcarriers per subchannel and create full
|
|||
|
% channel estimate
|
|||
|
preheInfo = wlanHEOFDMInfo('L-SIG',chanBW);
|
|||
|
preHEChanEst = preHEChannelEstimate(helsigDemod,lltfChanEst,preheInfo.NumSubchannels);
|
|||
|
|
|||
|
% Average L-SIG and RL-SIG before equalization
|
|||
|
helsigDemod = mean(helsigDemod,2);
|
|||
|
|
|||
|
% Equalize data carrying subcarriers, merging 20 MHz subchannels
|
|||
|
[eqLSIGSym,csi] = preHESymbolEqualize(helsigDemod(preheInfo.DataIndices,:,:), ...
|
|||
|
preHEChanEst(preheInfo.DataIndices,:,:),noiseVar,preheInfo.NumSubchannels);
|
|||
|
|
|||
|
% Decode L-SIG field
|
|||
|
[~,failCheck,lsigInfo] = wlanLSIGBitRecover(eqLSIGSym,noiseVar,csi);
|
|||
|
|
|||
|
if failCheck
|
|||
|
disp(' ** L-SIG check fail **');
|
|||
|
else
|
|||
|
disp(' L-SIG check pass');
|
|||
|
end
|
|||
|
% Get the length information from the recovered L-SIG bits and update the
|
|||
|
% L-SIG length property of the recovery configuration object
|
|||
|
lsigLength = lsigInfo.Length;
|
|||
|
cfgRx.LSIGLength = lsigLength;
|
|||
|
|
|||
|
% Measure EVM of L-SIG symbols
|
|||
|
EVM = comm.EVM;
|
|||
|
EVM.ReferenceSignalSource = 'Estimated from reference constellation';
|
|||
|
EVM.Normalization = 'Average constellation power';
|
|||
|
EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK');
|
|||
|
rmsEVM = EVM(eqLSIGSym);
|
|||
|
evm = 20*log10(rmsEVM/100);
|
|||
|
fprintf(' L-SIG EVM: %2.2fdB\n\n',20*log10(rmsEVM/100));
|
|||
|
|
|||
|
% Calculate the receive time and corresponding number of samples in the
|
|||
|
% packet
|
|||
|
RXTime = ceil((lsigLength + 3)/3) * 4 + 20; % In microseconds
|
|||
|
numRxSamples = round(RXTime * 1e-6 * fs); % Number of samples in time
|
|||
|
|
|||
|
%% HE-SIG-A Decoding
|
|||
|
rxSIGA = rx(pktOffset+(ind.HESIGA(1):ind.HESIGA(2)),:);
|
|||
|
sigaDemod = wlanHEDemodulate(rxSIGA,'HE-SIG-A',chanBW);
|
|||
|
hesigaDemod = preHECommonPhaseErrorTracking(sigaDemod,preHEChanEst,'HE-SIG-A',chanBW);
|
|||
|
|
|||
|
% Equalize data carrying subcarriers, merging 20 MHz subchannels
|
|||
|
preheInfo = wlanHEOFDMInfo('HE-SIG-A',chanBW);
|
|||
|
[eqSIGASym,csi] = preHESymbolEqualize(hesigaDemod(preheInfo.DataIndices,:,:), ...
|
|||
|
preHEChanEst(preheInfo.DataIndices,:,:), ...
|
|||
|
noiseVar,preheInfo.NumSubchannels);
|
|||
|
% Recover HE-SIG-A bits
|
|||
|
[sigaBits,failCRC] = wlanHESIGABitRecover(eqSIGASym,noiseVar,csi);
|
|||
|
|
|||
|
% Perform the CRC on HE-SIG-A bits
|
|||
|
if failCRC
|
|||
|
disp(' ** HE-SIG-A CRC fail **');
|
|||
|
else
|
|||
|
disp(' HE-SIG-A CRC pass');
|
|||
|
end
|
|||
|
|
|||
|
% Measure EVM of HE-SIG-A symbols
|
|||
|
% release(EVM);
|
|||
|
% if strcmp(pktFormat,'HE-EXT-SU')
|
|||
|
% % The second symbol of an HE-SIG-A field for an HE-EXT-SU packet is
|
|||
|
% % QBPSK.
|
|||
|
% EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK',[0 pi/2 0 0]);
|
|||
|
% % Account for scaling of L-LTF for an HE-EXT-SU packet
|
|||
|
% rmsEVM = EVM(eqSIGASym*sqrt(2));
|
|||
|
% else
|
|||
|
% EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK');
|
|||
|
% rmsEVM = EVM(eqSIGASym);
|
|||
|
% end
|
|||
|
% fprintf(' HE-SIG-A EVM: %2.2fdB\n\n',20*log10(mean(rmsEVM)/100));
|
|||
|
|
|||
|
%% Interpret Recovered HE-SIG-A bits
|
|||
|
cfgRx = interpretHESIGABits(cfgRx,sigaBits);
|
|||
|
ind = wlanFieldIndices(cfgRx); % Update field indices
|
|||
|
% disp(cfgRx)
|
|||
|
|
|||
|
|
|||
|
%% HE-SIG-B Decoding
|
|||
|
if strcmp(pktFormat,'HE-MU')
|
|||
|
if ~cfgRx.SIGBCompression
|
|||
|
s = getSIGBLength(cfgRx);
|
|||
|
% Get common field symbols. The start of HE-SIG-B field is known
|
|||
|
rxSym = rx(pktOffset+(ind.HESIGA(2)+(1:s.NumSIGBCommonFieldSamples)),:);
|
|||
|
% Decode HE-SIG-B common field
|
|||
|
[status,cfgRx] = heSIGBCommonFieldDecode(rxSym,preHEChanEst,noiseVar,cfgRx);
|
|||
|
|
|||
|
% CRC on HE-SIG-B content channels
|
|||
|
if strcmp(status,'Success')
|
|||
|
fprintf(' HE-SIG-B (common field) CRC pass\n');
|
|||
|
elseif strcmp(status,'ContentChannel1CRCFail')
|
|||
|
fprintf(' ** HE-SIG-B CRC fail for content channel-1\n **');
|
|||
|
elseif strcmp(status,'ContentChannel2CRCFail')
|
|||
|
fprintf(' ** HE-SIG-B CRC fail for content channel-2\n **');
|
|||
|
elseif any(strcmp(status,{'UnknownNumUsersContentChannel1','UnknownNumUsersContentChannel2'}))
|
|||
|
error(' ** Unknown packet length, discard packet\n **');
|
|||
|
else
|
|||
|
% Discard the packet if all HE-SIG-B content channels fail
|
|||
|
error(' ** HE-SIG-B CRC fail **');
|
|||
|
end
|
|||
|
% Update field indices as the number of HE-SIG-B symbols are
|
|||
|
% updated
|
|||
|
ind = wlanFieldIndices(cfgRx);
|
|||
|
end
|
|||
|
|
|||
|
% Get complete HE-SIG-B field samples
|
|||
|
rxSIGB = rx(pktOffset+(ind.HESIGB(1):ind.HESIGB(2)),:);
|
|||
|
fprintf(' Decoding HE-SIG-B user field... \n');
|
|||
|
% Decode HE-SIG-B user field
|
|||
|
[failCRC,cfgUsers] = heSIGBUserFieldDecode(rxSIGB,preHEChanEst,noiseVar,cfgRx);
|
|||
|
%*********************************03.22修改*********************************
|
|||
|
for ii = 1:numel(cfgUsers)
|
|||
|
if cfgUsers{ii}.STAID == userIdx
|
|||
|
user = cfgUsers{ii};
|
|||
|
break;
|
|||
|
end
|
|||
|
end
|
|||
|
% user = cfgUsers{1};
|
|||
|
%*********************************03.22修改*********************************
|
|||
|
% CRC on HE-SIG-B users
|
|||
|
if ~all(failCRC)
|
|||
|
fprintf(' HE-SIG-B (user field) CRC pass\n\n');
|
|||
|
numUsers = numel(cfgUsers);
|
|||
|
elseif all(failCRC)
|
|||
|
% Discard the packet if all users fail the CRC
|
|||
|
error(' ** HE-SIG-B CRC fail for all users **');
|
|||
|
else
|
|||
|
fprintf(' ** HE-SIG-B CRC fail for at least one user\n **');
|
|||
|
% Only process users with valid CRC
|
|||
|
numUsers = numel(cfgUsers);
|
|||
|
end
|
|||
|
|
|||
|
else % HE-SU, HE-EXT-SU
|
|||
|
cfgUsers = {cfgRx};
|
|||
|
numUsers = 1;
|
|||
|
end
|
|||
|
|
|||
|
%% HE-Data Decoding
|
|||
|
cfgDataRec = trackingRecoveryConfig;
|
|||
|
cfgDataRec.PilotTracking = pilotTracking;
|
|||
|
% Get recovery configuration object for each user
|
|||
|
user = cfgUsers{userIdx};
|
|||
|
if strcmp(pktFormat,'HE-MU')
|
|||
|
fprintf(' Decoding User:%d, STAID:%d, RUSize:%d\n',userIdx,user.STAID,user.RUSize);
|
|||
|
else
|
|||
|
fprintf(' Decoding RUSize:%d\n',user.RUSize);
|
|||
|
end
|
|||
|
|
|||
|
heInfo = wlanHEOFDMInfo('HE-Data',chanBW,user.GuardInterval,[user.RUSize user.RUIndex]);
|
|||
|
|
|||
|
% HE-LTF demodulation and channel estimation
|
|||
|
rxHELTF = rx(pktOffset+(ind.HELTF(1):ind.HELTF(2)),:);
|
|||
|
heltfDemod = wlanHEDemodulate(rxHELTF,'HE-LTF',chanBW,user.GuardInterval, ...
|
|||
|
user.HELTFType,[user.RUSize user.RUIndex]);
|
|||
|
[chanEst,pilotEst] = heLTFChannelEstimate(heltfDemod,user);
|
|||
|
|
|||
|
% Number of expected data OFDM symbols
|
|||
|
symLen = heInfo.FFTLength+heInfo.CPLength;
|
|||
|
numOFDMSym = (ind.HEData(2)-ind.HEData(1)+1)/symLen;
|
|||
|
numOFDMSym = double(numOFDMSym);
|
|||
|
|
|||
|
% HE-Data demodulation with pilot phase and timing tracking
|
|||
|
% Account for extra samples when extracting data field from the packet
|
|||
|
% for sample rate offset tracking. Extra samples may be required if the
|
|||
|
% receiver clock is significantly faster than the transmitter.
|
|||
|
maxSRO = 120; % Parts per million
|
|||
|
Ne = ceil(numRxSamples*maxSRO*1e-6); % Number of extra samples
|
|||
|
Ne = min(Ne,rxWaveLen-numRxSamples); % Limited to length of waveform
|
|||
|
numRxSamplesProcess = numRxSamples+Ne;
|
|||
|
rxData = rx(pktOffset+(ind.HEData(1):numRxSamplesProcess),:);
|
|||
|
if user.RUSize==26
|
|||
|
% Force CPE only tracking for 26-tone RU as algorithm susceptible
|
|||
|
% to noise
|
|||
|
cfgDataRec.PilotTracking = 'CPE';
|
|||
|
else
|
|||
|
cfgDataRec.PilotTracking = pilotTracking;
|
|||
|
end
|
|||
|
[demodSym,cpe,peg] = heTrackingOFDMDemodulate(rxData,chanEst,numOFDMSym,user,cfgDataRec);
|
|||
|
|
|||
|
% Estimate noise power in HE fields
|
|||
|
demodPilotSym = demodSym(heInfo.PilotIndices,:,:);
|
|||
|
nVarEst = heNoiseEstimate(demodPilotSym,pilotEst,user);
|
|||
|
|
|||
|
% Equalize
|
|||
|
[eqSym,csi] = heEqualizeCombine(demodSym,chanEst,nVarEst,user);
|
|||
|
|
|||
|
% Discard pilot subcarriers
|
|||
|
eqSymUser = eqSym(heInfo.DataIndices,:,:);
|
|||
|
csiData = csi(heInfo.DataIndices,:);
|
|||
|
eqSymPlot = eqSymUser(1:end);
|
|||
|
|
|||
|
% scatterplot
|
|||
|
% eqSymplot = eqSymUser(1:end);
|
|||
|
% scatterplot(eqSymplot);
|
|||
|
|
|||
|
% Demap and decode bits
|
|||
|
rxPSDUbits = wlanHEDataBitRecover(eqSymUser,nVarEst,csiData,user,'LDPCDecodingMethod','layered-bp');
|
|||
|
% rxPSDU(:,frameIdx) = RX_CRC32(double(rxPSDUbits));
|
|||
|
rxPSDU(:,frameIdx) = rxPSDUbits;
|
|||
|
% Compare bit error
|
|||
|
% bitError = sum(txPSDU{userIdx}~=rxPSDU);
|
|||
|
% numBitError = numBitError+bitError;
|
|||
|
|
|||
|
% Measure EVM of HE-Data symbols
|
|||
|
release(EVM);
|
|||
|
EVM.ReferenceConstellation = wlanReferenceSymbols(user);
|
|||
|
rmsEVM = EVM(eqSymUser(:));
|
|||
|
fprintf(' HE-Data EVM:%2.2fdB\n\n',20*log10(rmsEVM/100));
|
|||
|
rx = rx(pktOffset+ind.HEData(2)+100:end,:);
|
|||
|
% && mean(abs(rx)) > mean_power/2
|
|||
|
% else
|
|||
|
|
|||
|
% if length(rx) > ind.HEData(2)
|
|||
|
frameIdx = frameIdx+1;
|
|||
|
[rxPSDU,bitErrorRate,eqSymPlot] = heMURx(rx,cfg,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot);
|
|||
|
else
|
|||
|
[~,numPkt] = size(rxPSDU);
|
|||
|
psduLength = length(rxPSDU(:,1))/8;
|
|||
|
|
|||
|
switch cfgUI.DataType
|
|||
|
case 'test'
|
|||
|
txPSDU = DataGenerate(cfgUI,numPkt,SEED,psduLength,0);
|
|||
|
txPSDUuser = txPSDU{userIdx};% BER计算方式 用收发约定的种子数产生,解5个包确定ACK个数
|
|||
|
bitError = sum(txPSDUuser(1:end,:)~=rxPSDU(1:length(txPSDUuser(1:end,:)),:));
|
|||
|
bitErrorRate = bitError/length(rxPSDU);
|
|||
|
ACK = (bitErrorRate == rxPSDU(end));
|
|||
|
case 'text'
|
|||
|
case 'photo'
|
|||
|
[~,numPkt] = size(rxPSDU);
|
|||
|
bitstream = reshape(rxPSDU,1,[]); % 恢复比特变比特流
|
|||
|
imgLen = bin2dec(num2str(bitstream(1:16)));
|
|||
|
% bitstream = bitstream(1:len*width*8); % 取图像像素大小
|
|||
|
bit_array = reshape(bitstream,8,[])';
|
|||
|
byte_array = num2str(bit_array);
|
|||
|
img = bin2dec(byte_array); % 二进制转十进制
|
|||
|
if imgLen+3 > length(img)
|
|||
|
imgLen = length(img)-3;
|
|||
|
end
|
|||
|
rxPSDU = int16(img(1:imgLen+3)); % double转int16
|
|||
|
rxCRC = 0;
|
|||
|
for i = 1:imgLen+2
|
|||
|
rxCRC = mod(rxCRC+rxPSDU(i),256);
|
|||
|
end
|
|||
|
if rxCRC == rxPSDU(end)
|
|||
|
disp('Pass RX Sum Check!');
|
|||
|
bitErrorRate = zeros(1,numPkt);
|
|||
|
else
|
|||
|
disp('Fail RX Sum Check!');
|
|||
|
bitErrorRate = ones(1,numPkt);
|
|||
|
end
|
|||
|
% img = mat2gray(rxPSDU); % 恢复出的数据按图像大小排列
|
|||
|
% imshow(img); % 展示图像
|
|||
|
% pause(1);
|
|||
|
% close;
|
|||
|
ACK = (rxCRC == rxPSDU(end));
|
|||
|
end
|
|||
|
save(['TXPackets\ACKfeedback_for_User' num2str(userIdx) '.mat'],'ACK');
|
|||
|
% scatterplot(eqSymPlot(1:end));
|
|||
|
% pause(1);
|
|||
|
% close;
|
|||
|
end
|
|||
|
end
|