This is machine translation

Translated by Microsoft
Mouseover text to see original. Click the button below to return to the English version of the page.

Note: This page has been translated by MathWorks. Click here to see
To view all translated materials including this page, select Country from the country navigator on the bottom of this page.

802.11ax Signal Recovery with Preamble Decoding

This example shows how to detect a packet and decode payload bits in a received IEEE® 802.11ax™ waveform. The receiver recovers the packet format parameters from the preamble fields to decode the data field and the MAC frame.

Introduction

In an 802.11ax packet the transmission parameters are signaled to the receiver using the L-SIG, HE-SIG-A, and HE-SIG-B preamble fields [ 1 ]:

  • The L-SIG field contains information to allow the receiver to determine the transmission time of the packet.

  • The HE-SIG-A field contains common transmission parameters for HE-MU users and all transmission parameters for HE-SU and HE-EXT-SU packets.

  • The combination of length information in the L-SIG field, the modulation type, and the number of OFDM symbols in the HE-SIG-A field determines the HE packet format.

  • The HE-SIG-B field contains Resource unit (RU) allocation information and the transmission parameters for the users in an HE-MU packet.

In this example we detect and decode an HE-MU packet within a generated waveform. This example can also recover HE-SU and HE-EXT-SU packets. All transmission parameters apart from the channel bandwidth are assumed to be unknown and are therefore retrieved from the decoded L-SIG, HE-SIG-A, and HE-SIG-B preamble fields. The recovered transmission parameters are used to decode the HE-Data field. Additionally, the following analysis is performed:

  • The waveform of the detected packet is recovered and displayed.

  • The spectrum of the detected packet is recovered and displayed.

  • The constellation of the equalized data symbols for all spatial streams is displayed.

  • The Error Vector Magnitude (EVM) of each field is measured.

  • An AMPDU is detected and Frame Check Sequence (FCS) is determined for the recovered MAC frame.

Waveform Transmission

In this example an 802.11ax HE-MU waveform is generated locally but you can use a captured waveform. You can use MATLAB® to acquire I/Q data from a wide range of instruments with the Instrument Control Toolbox™ and software defined radio platforms.

The locally generated waveform is impaired by a 2x2 TGax indoor fading channel, additive white Gaussian noise, and carrier frequency offset. To generate an HE-MU waveform locally we configure an HE-MU format configuration object wlanHEMUConfig. Note that the wlanHEMUConfig configuration object is used at the transmitter side only. The receiver will formulate an HE recovery configuration object wlanHERecoveryConfig. The unknown properties of the HE recovery configuration object are set after decoding the information bits in the L-SIG, HE-SIG-A, and HE-SIG-B fields. The helper function heSigRecGenerateWaveform generates the impaired waveform locally. The following processing steps are performed:

  • A random payload of MSDUs is created for the MAC frame, which is encoded into an HE-MU packet.

  • The waveform is passed through a TGax indoor fading channel model.

  • Carrier frequency offset (CFO) and Additive white Gaussian noise (AWGN) are added to the waveform.

% A mixed OFDMA and MU-MIMO configuration is defined for an HE-MU packet.
% The allocation index 17 defines two 52-tone RUs, with one user in each
% RU, and one 106-tone RU. The 106-tone RU has two users in a MU-MIMO
% configuration.
cfgMU = wlanHEMUConfig(17);
cfgMU.NumTransmitAntennas = 2;

% Configure RU 1 and user 1
cfgMU.RU{1}.SpatialMapping = 'Direct';
cfgMU.User{1}.STAID = 1;
cfgMU.User{1}.APEPLength = 1e3;
cfgMU.User{1}.MCS = 5;
cfgMU.User{1}.NumSpaceTimeStreams = 2;
cfgMU.User{1}.ChannelCoding = 'LDPC';

% Configure RU 2 and user 2
cfgMU.RU{2}.SpatialMapping = 'Fourier';
cfgMU.User{2}.STAID = 2;
cfgMU.User{2}.APEPLength = 500;
cfgMU.User{2}.MCS = 4;
cfgMU.User{2}.NumSpaceTimeStreams = 1;
cfgMU.User{2}.ChannelCoding = 'BCC';

% Configure RU 3 and user 1
cfgMU.RU{3}.SpatialMapping = 'Fourier';
cfgMU.User{3}.STAID = 3;
cfgMU.User{3}.APEPLength = 100;
cfgMU.User{3}.MCS = 2;
cfgMU.User{3}.NumSpaceTimeStreams = 1;
cfgMU.User{3}.ChannelCoding = 'BCC';

% Configure RU 3 and user 2
cfgMU.User{4}.STAID = 4;
cfgMU.User{4}.APEPLength = 500;
cfgMU.User{4}.MCS = 3;
cfgMU.User{4}.NumSpaceTimeStreams = 1;
cfgMU.User{4}.ChannelCoding = 'LDPC';

% Specify propagation channel
numRx = 2; % Number of receive antennas
delayProfile = 'Model-D'; % TGax channel delay profile

% Specify impairments
noisePower = -40; % Noise power to apply in dBW
cfo = 62e3;       % Carrier frequency offset Hz

% Specify waveform parameters
numTxPkt = 1;     % Number of transmitted packets
idleTime = 20e-6; % Idle time before and after each packet

% Generate waveform
rx = heSigRecGenerateWaveform(cfgMU,numRx,delayProfile,noisePower,cfo,numTxPkt,idleTime);

Packet Recovery

The signal to process is stored in the variable rx. The processing steps to recover a packet are:

  • An HE recovery configuration object, wlanHERecoveryConfig is created. The object properties are updated as preamble fields are decoded.

  • The packet is detected and synchronized.

  • The L-LTF is extracted to determine the channel and noise estimates.

  • The L-SIG and RL-SIG fields are extracted. The channel is estimated on an extra four subcarriers per subchannel in the L-SIG and RL-SIG fields. The L-LTF channel estimates are updated to include the channel estimates on the extra subcarriers.

  • The information bits in the L-SIG field are recovered to determine the length of the packet in microseconds.

  • The HE-SIG-A field is extracted. Both demodulated HE-SIG-A symbols and the length information in the L-SIG field are used to determine the HE packet format. The packet format is updated in the wlanHERecoveryConfig object.

  • After HE-SIG-A decoding, the recovery configuration object is updated with common transmission parameters for an HE-MU packet and all transmission parameters for HE-SU and HE-EXT-SU packets.

  • For an HE-MU packet format the HE-SIG-B field is decoded. For a non-compressed SIGB waveform the HE-SIG-B common field is decoded first followed by HE-SIG-B user field. For a compressed SIGB waveform only the HE-SIG-B user field is decoded.

  • For an HE-MU format without SIGB compression the RU allocation and user transmission parameters are recovered from the HE-SIG-B field. For a compressed SIGB waveform the RU allocation information is inferred from HE-SIG-A field and the user transmission parameters are determined from the HE-SIG-B user field bits.

  • The wlanHERecoveryConfig object is created using the recovered transmission parameters for each user after HE-SIG-B decoding.

  • The HE-LTF field is extracted and the channel is estimated on the subcarrier allocated to the user of interest. The MIMO channel estimates are used to decode the HE-Data field.

  • The HE-Data field is extracted and the PSDU bits are recovered using the wlanHERecoveryConfig object for each user.

  • Detect AMPDU within the recovered PSDU and check the FCS for the recovered MAC frame.

Setup Waveform Recovery Parameters

In this example all transmission parameters apart from the channel bandwidth are assumed to be unknown and will be recovered. A recovery configuration object, wlanHERecoveryConfig, is created to store the recovered information in the L-SIG, HE-SIG-A, and HE-SIG-B preamble fields. The transmission properties in wlanHERecoveryConfig are updated after subsequent decoding of the preamble fields. The following code configures objects and variables for processing.

chanBW = cfgMU.ChannelBandwidth; % Assume channel bandwidth is known
sr = wlanSampleRate(cfgMU); % Sample rate

% Create an HE recovery configuration object and set the channel bandwidth
cfgRx = wlanHERecoveryConfig;
cfgRx.ChannelBandwidth = chanBW;

% The recovery configuration object is used to get the start and end
% indices of the pre-HE-SIG-B field.
ind = wlanFieldIndices(cfgRx);

% Setup plots for the example
[spectrumAnalyzer,timeScope] = heSigRecSetupPlots(sr);

% 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

The front-end processing consists of packet detection, coarse carrier frequency offset correction, timing synchronization, and fine carrier frequency offset correction. A while loop is used to detect and synchronize a packet within the received waveform. The sample offset searchOffset is used to index into rx to detect a packet. The first packet within rx is detected and processed. If the synchronization fails for the detected packet, the sample index offset searchOffset is incremented to move beyond the processed packet in rx. This is repeated until a packet has been successfully detected and synchronized.

searchOffset = 0; % Offset from start of waveform in samples
while (searchOffset + minPktLen) <= rxWaveLen
    % Packet detection
    pktOffset = wlanPacketDetect(rx,chanBW,searchOffset);

    % Adjust packet offset
    pktOffset = searchOffset + pktOffset;
    if isempty(pktOffset) || (pktOffset + ind.LSIG(2) > rxWaveLen)
        error('** No packet detected **');
    end

    % Coarse frequency offset estimation and correction using L-STF
    LSTF = rx(pktOffset+(ind.LSTF(1):ind.LSTF(2)), :);
    coarseFreqOffset = wlanCoarseCFOEstimate(LSTF,chanBW);
    rx = helperFrequencyOffset(rx,sr,-coarseFreqOffset);

    % Symbol timing synchronization
    searchBufferLLTF = rx(pktOffset+(ind.LSTF(1):ind.LSIG(2)),:);
    pktOffset = pktOffset+wlanSymbolTimingEstimate(searchBufferLLTF,chanBW);

    % Fine frequency offset estimation and correction using L-STF
    rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:);
    fineFreqOffset = wlanFineCFOEstimate(rxLLTF,chanBW);
    rx = helperFrequencyOffset(rx,sr,-fineFreqOffset);

    % Timing synchronization complete: packet detected
    fprintf('Packet detected at index %d\n',pktOffset + 1);

    % Display estimated carrier frequency offset
    cfoCorrection = coarseFreqOffset + fineFreqOffset; % Total CFO
    fprintf('Estimated CFO: %5.1f Hz\n\n',cfoCorrection);

    break; % Front-end processing complete, stop searching for a packet
end
Packet detected at index 404
Estimated CFO: 61942.9 Hz

L-LTF Channel and Noise Power Estimation

Extract and demodulate the L-LTF and perform channel and noise power estimation. The L-LTF channel and noise power estimates are used to decode the pre HE-LTF.

rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:);
lltfDemod = wlanHEDemodulate(rxLLTF,'L-LTF',chanBW);
lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW);
noiseVar = helperNoiseEstimate(lltfDemod);

L-SIG and RL-SIG Decoding

The L-SIG field is used to determine the receive time, or RXTIME, of the packet. The RXTIME is calculated using the length bits of the L-SIG payload. The L-SIG and RL-SIG fields are recovered to perform the channel estimate on the extra subcarriers in the L-SIG and RL-SIG fields. The lltfChanEst channel estimates are updated to include the channel estimates on extra subcarriers in the L-SIG and RL-SIG fields. The L-SIG payload is decoded using an estimate of the channel and noise power obtained from the L-LTF field. The L-SIG length property in wlanHERecoveryConfig is updated after L-SIG decoding.

disp('Decoding L-SIG... ');
% 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);
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 * sr);   % Number of samples in time

fprintf(' RXTIME: %dus\n',RXTime);
fprintf(' Number of samples in the packet: %d\n\n',numRxSamples);
Decoding L-SIG... 
 L-SIG check pass
 L-SIG EVM: -36.90dB

 RXTIME: 536us
 Number of samples in the packet: 10720

The waveform and spectrum of the detected packet within rx is displayed given the calculated RXTIME and corresponding number of samples.

sampleOffset = max((-lstfLength + pktOffset),1); % First index to plot
sampleSpan = numRxSamples + 2*lstfLength; % Number samples to plot
% Plot as much of the packet (and extra samples) as we can
plotIdx = sampleOffset:min(sampleOffset + sampleSpan,rxWaveLen);

% Configure timeScope to display the packet
timeScope.TimeSpan = sampleSpan/sr;
timeScope.TimeDisplayOffset = sampleOffset/sr;
timeScope.YLimits = [0 max(abs(rx(:)))];
timeScope(abs(rx(plotIdx,:)));
release(timeScope);

% Display the spectrum of the detected packet
spectrumAnalyzer(rx(pktOffset + (1:numRxSamples),:));
release(spectrumAnalyzer);

Packet Format Detection

The length information in the L-SIG field and four OFDM symbols immediately following the RL-SIG field are used to determine the HE packet format [ 1 Figure. 28-62].

disp('Detect packet format...');
rxSIGA = rx(pktOffset+(ind.HESIGA(1):ind.HESIGA(2)),:);
pktFormat = hePacketFormat(rxSIGA,preHEChanEst,lsigLength,noiseVar,chanBW);
fprintf('  %s packet detected\n\n',pktFormat);

% Update the packet format in the recovery object and recompute the field
% indices
cfgRx.PacketFormat = pktFormat;
ind = wlanFieldIndices(cfgRx);
Detect packet format...
  HE-MU packet detected

HE-SIG-A Decoding

The HE-SIG-A field contains the transmission configuration of an HE packet [ 1 Table. 28-19]. An estimate of the channel and noise power obtained from the L-LTF is required to decode the HE-SIG-A field.

disp('Decoding HE-SIG-A...')
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));
Decoding HE-SIG-A...
 HE-SIG-A CRC pass
 HE-SIG-A EVM: -35.19dB

Interpret Recovered HE-SIG-A bits

The wlanHERecoveryConfig object is updated after interpreting the recovered HE-SIG-A bits [ 1 Table. 28-19].

cfgRx = interpretHESIGABits(cfgRx,sigaBits);
ind = wlanFieldIndices(cfgRx); % Update field indices

Display the common transmission configuration obtained from HE-SIG-A field for an HE-MU packet. The properties indicated by -1 are unknown or undefined. The unknown user-related properties are updated after successful decoding of the HE-SIG-B field.

disp(cfgRx)
  wlanHERecoveryConfig with properties:

                    PacketFormat: 'HE-MU'
                ChannelBandwidth: 'CBW20'
                      LSIGLength: 383
                 SIGBCompression: 0
                         SIGBMCS: 0
                         SIGBDCM: 0
          NumSIGBSymbolsSignaled: 5
                            STBC: 0
                 LDPCExtraSymbol: 0
             PreFECPaddingFactor: 4
                  PEDisambiguity: 0
                   GuardInterval: 3.2000
                       HELTFType: 4
                 NumHELTFSymbols: 2
                UplinkIndication: 0
                        BSSColor: 0
                    SpatialReuse: 0
                    TXOPDuration: 127
                     HighDoppler: 0
                 AllocationIndex: -1
       NumUsersPerContentChannel: -1
         RUTotalSpaceTimeStreams: -1
                          RUSize: -1
                         RUIndex: -1
                           STAID: -1
                             MCS: -1
                             DCM: -1
                   ChannelCoding: 'Unknown'
                     Beamforming: -1
             NumSpaceTimeStreams: -1
    SpaceTimeStreamStartingIndex: -1

HE-SIG-B Decoding

For an HE-MU packet the HE-SIG-B field contains:

  • The RU allocation information for a non-compressed SIGB waveform is inferred from HE-SIG-B Common field [ 1 Table. 28-23]. For a compressed SIGB waveform the RU allocation information is inferred from the recovered HE-SIG-A bits.

  • For a non-compressed SIGB waveform the number of HE-SIG-B symbols are updated in the wlanHERecoveryConfig object. The symbols are only updated if the number of HE-SIG-B symbols indicated in the HE-SIG-A field is set to 15 and all content channels pass the CRC. The number of HE-SIG-B symbols indicated in the HE-SIG-A field are not updated if any HE-SIG-B content channel fails the CRC.

  • The user transmission parameters for both SIGB compressed and non-compressed waveforms are inferred from the HE-SIG-B user field [ 1 Table. 28-25, 28-26].

An estimate of the channel and noise power obtained from the L-LTF is required to decode the HE-SIG-B field.

if strcmp(pktFormat,'HE-MU')
    fprintf('Decoding HE-SIG-B...\n');
    if ~cfgRx.SIGBCompression
        fprintf(' Decoding HE-SIG-B common field...\n');
        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);

    % CRC on HE-SIG-B users
    if ~all(failCRC)
        fprintf('  HE-SIG-B (user field) CRC pass\n\n');
        numUsers = sum(cfgRx.NumUsersPerContentChannel);
    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
Decoding HE-SIG-B...
 Decoding HE-SIG-B common field...
  HE-SIG-B (common field) CRC pass
 Decoding HE-SIG-B user field... 
  HE-SIG-B (user field) CRC pass

HE-Data Decoding

The updated wlanHERecoveryConfig object for each user can then be used to recover the PSDU bits for each user in the HE-Data field.

The recovered HE-Data symbols can be analyzed as required. In this example the equalized constellation of the recovered HE data symbols for the all users is displayed.

constellationDiagram = heSigEqualizeSetupPlots(numUsers);
fprintf('Decoding HE-Data...\n');
for iu = 1:numUsers
    % Get recovery configuration object for each user
    user = cfgUsers{iu};
    if strcmp(pktFormat,'HE-MU')
        fprintf(' Decoding User:%d, STAID:%d, RUSize:%d\n',iu,user.STAID,user.RUSize);
    else
        fprintf(' Decoding RUSize:%d\n',user.RUSize);
    end

    % 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);

    % HE-Data demodulate
    rxData = rx(pktOffset+(ind.HEData(1):ind.HEData(2)),:);
    demodSym = wlanHEDemodulate(rxData,'HE-Data',chanBW,user.GuardInterval, ...
        [user.RUSize user.RUIndex]);

    % Pilot phase tracking. Average single-stream pilot estimates over
    % symbols (2nd dimension)
    pilotEstTrack = mean(pilotEst,2);
    demodSym = heCommonPhaseErrorTracking(demodSym,pilotEstTrack,user);

    % Estimate noise power in HE fields
    preheInfo = wlanHEOFDMInfo('HE-Data',chanBW,user.GuardInterval,[user.RUSize user.RUIndex]);
    demodPilotSym = demodSym(preheInfo.PilotIndices,:,:);
    nVarEst = heNoiseEstimate(demodPilotSym,pilotEst,user);

    % Equalize
    [eqSym,csi] = heEqualizeCombine(demodSym,chanEst,nVarEst,user);

    % Discard pilot subcarriers
    eqSymUser = eqSym(preheInfo.DataIndices,:,:);
    csiData = csi(preheInfo.DataIndices,:);

    % Demap and decode bits
    rxPSDU = wlanHEDataBitRecover(eqSymUser,nVarEst,csiData,user);

    % Deaggregate the A-MPDU
    [mpduList,~,status] = wlanAMPDUDeaggregate(rxPSDU,wlanHESUConfig);
    if strcmp(status,'Success')
        fprintf('  AMPDU deaggregation successful \n');
    else
        fprintf('  AMPDU deaggregation unsuccessful \n');
    end

    % Decode the list of MPDUs and check the FCS for each MPDU
    for i = 1:numel(mpduList)
        [~,~,status] = wlanMPDUDecode(mpduList{i},wlanHESUConfig,'DataFormat','octets');
        if strcmp(status,'Success')
            fprintf('  FCS pass for MPDU:%d\n',i);
        else
            fprintf('  Decode fail for MPDU:%d\n',i);
        end
    end

    % Plot equalized constellation for all users
    modScheme = heModulationScheme(user);
    refConst = wlanReferenceSymbols(modScheme);
    [Nsd,Nsym,Nss] = size(eqSymUser);
    eqDataSymPerSS = reshape(eqSymUser,Nsd*Nsym,Nss);
    constellationDiagram{iu}.ReferenceConstellation = refConst;
    constellationDiagram{iu}(eqDataSymPerSS);
    release(constellationDiagram{iu});

    % Measure EVM of HE-Data symbols
    release(EVM);
    EVM.ReferenceConstellation = refConst;
    rmsEVM = EVM(eqSymUser(:));
    fprintf('  HE-Data EVM:%2.2fdB\n\n',20*log10(rmsEVM/100));
end
Decoding HE-Data...
 Decoding User:1, STAID:1, RUSize:52
  AMPDU deaggregation successful 
  FCS pass for MPDU:1
  HE-Data EVM:-28.85dB

 Decoding User:2, STAID:2, RUSize:52
  AMPDU deaggregation successful 
  FCS pass for MPDU:1
  HE-Data EVM:-39.68dB

 Decoding User:3, STAID:3, RUSize:106
  AMPDU deaggregation successful 
  FCS pass for MPDU:1
  HE-Data EVM:-28.22dB

 Decoding User:4, STAID:4, RUSize:106
  AMPDU deaggregation successful 
  FCS pass for MPDU:1
  HE-Data EVM:-31.69dB

Appendix

This example uses the following helper functions:

Selected Bibliography

  1. IEEE P802.11ax™/D3.1 Draft Standard for Information technology - Telecommunications and information exchange between systems - Local and metropolitan area networks - Specific requirements - Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications - Amendment 6: Enhancements for High Efficiency WLAN.