Main Content

Recovery Procedure for an 802.11ac Packet

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


In a single-user 802.11ac packet the transmission parameters are signaled to the receiver using the L-SIG and VHT-SIG-A preamble fields [ 1 ]:

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

  • The VHT-SIG-A field contains the transmission parameters including the modulation and coding scheme, number of space-time streams and channel coding.

In this example we detect and decode a packet within a generated waveform containing a valid MAC frame with frame check sequence (FCS). All transmission parameters apart from the channel bandwidth are assumed unknown and are therefore retrieved from the decoded L-SIG and VHT-SIG-A preamble fields in each packet. The retrieved transmission configuration is used to decode the VHT-SIG-B and VHT Data fields. Additionally the following analysis is performed:

  • The waveform of the detected packet is displayed.

  • The spectrum of the detected packet is displayed.

  • The constellation of the equalized data symbols per spatial stream is displayed.

  • The error vector magnitude (EVM) of each field is measured.

Waveform Transmission

In this example an 802.11ac VHT single-user waveform is generated locally but a captured waveform could be used. MATLAB® can be used to acquire I/Q data from a wide range of instruments using the Instrument Control Toolbox™ and software defined radio platforms.

The locally generated waveform is impaired by a 3x3 TGac fading channel, additive white Gaussian noise, and carrier frequency offset. To generate a waveform locally we configure a VHT packet format configuration object. Note that the VHT packet configuration object is used at the transmitter side only. The receiver will dynamically formulate another VHT configuration object when the packet is decoded. The helper function vhtSigRecGenerateWaveform generates the impaired waveform locally. The processing steps within the helper function are:

  • A valid MAC frame is generated and encoded into a VHT waveform.

  • The waveform is passed through a TGac fading channel model.

  • Carrier frequency offset is added to the waveform.

  • Additive white Gaussian noise is added to the waveform.

% VHT link parameters
cfgVHTTx = wlanVHTConfig( ...
    'ChannelBandwidth',    'CBW80', ...
    'NumTransmitAntennas', 3, ...
    'NumSpaceTimeStreams', 2, ...
    'SpatialMapping',      'Hadamard', ...
    'STBC',                true, ...
    'MCS',                 5, ...
    'GuardInterval',       'Long', ...
    'APEPLength',          1052);

% Propagation channel
numRx = 3;                  % Number of receive antennas
delayProfile = 'Model-C';   % TGac channel delay profile

% Impairments
noisePower = -30;  % Noise power to apply in dBW
cfo = 62e3;        % Carrier frequency offset (Hz)

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

% Generate waveform
rx = vhtSigRecGenerateWaveform(cfgVHTTx, 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:

  • The packet is detected and synchronized.

  • The format of the packet is detected.

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

  • The VHT-SIG-A field is extracted and its information bits are recovered.

  • The packet format parameters are retrieved from the decoded L-SIG and VHT-SIG-A bits.

  • The VHT-LTF field is extracted to perform MIMO channel estimation for decoding the VHT-SIG-B and VHT Data fields.

  • The VHT-SIG-B field is extracted and its information bits recovered.

  • The VHT-Data field is extracted and the PSDU and VHT-SIG-B CRC bits recovered using the retrieved packet parameters.

The start and end indices for some preamble fields depend on the channel bandwidth, but are independent of all other transmission parameters. These indices are calculated using a default transmission configuration object with the known bandwidth.

cfgVHTRx = wlanVHTConfig('ChannelBandwidth', cfgVHTTx.ChannelBandwidth);
idxLSTF = wlanFieldIndices(cfgVHTRx, 'L-STF');
idxLLTF = wlanFieldIndices(cfgVHTRx, 'L-LTF');
idxLSIG = wlanFieldIndices(cfgVHTRx, 'L-SIG');
idxSIGA = wlanFieldIndices(cfgVHTRx, 'VHT-SIG-A');

The following code configures objects and variables for processing.

chanBW = cfgVHTTx.ChannelBandwidth;
sr = wlanSampleRate(cfgVHTTx);

% Setup plots for example
[spectrumAnalyzer, timeScope, constellationDiagram] = vhtSigRecSetupPlots(sr);

% Minimum packet length is 10 OFDM symbols
lstfLen = double(idxLSTF(2)); % Number of samples in L-STF
minPktLen = lstfLen*5;

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 elements within the array 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 + idxLSIG(2) > rxWaveLen)
        error('** No packet detected **');

    % Coarse frequency offset estimation using L-STF
    LSTF = rx(pktOffset + (idxLSTF(1):idxLSTF(2)), :);
    coarseFreqOffset = wlanCoarseCFOEstimate(LSTF, chanBW);

    % Coarse frequency offset compensation
    rx = frequencyOffset(rx,sr,-coarseFreqOffset);

    % Symbol timing synchronization
    LLTFSearchBuffer = rx(pktOffset+(idxLSTF(1):idxLSIG(2)),:);
    pktOffset = pktOffset+wlanSymbolTimingEstimate(LLTFSearchBuffer,chanBW);
    if (pktOffset + minPktLen) > rxWaveLen
        fprintf('** Not enough samples to recover packet **\n\n');

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

    % Fine frequency offset estimation using L-LTF
    LLTF = rx(pktOffset + (idxLLTF(1):idxLLTF(2)), :);
    fineFreqOffset = wlanFineCFOEstimate(LLTF, chanBW);

    % Fine frequency offset compensation
    rx = frequencyOffset(rx, sr, -fineFreqOffset);

    % 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
Packet detected at index 1600

Estimated CFO: 61954.3 Hz

Format Detection

The format of the packet is detected using the three OFDM symbols immediately following the L-LTF. An estimate of the channel and noise power obtained from the L-LTF is required.

% Channel estimation using L-LTF
LLTF = rx(pktOffset + (idxLLTF(1):idxLLTF(2)), :);
demodLLTF = wlanLLTFDemodulate(LLTF, chanBW);
chanEstLLTF = wlanLLTFChannelEstimate(demodLLTF, chanBW);

% Estimate noise power in non-HT fields
noiseVarNonHT = wlanLLTFNoiseEstimate(demodLLTF);

% Detect the format of the packet
fmt = wlanFormatDetect(rx(pktOffset + (idxLSIG(1):idxSIGA(2)), :), ...
    chanEstLLTF, noiseVarNonHT, chanBW);
disp([fmt ' format detected']);
if ~strcmp(fmt,'VHT')
    error('** A format other than VHT has been detected **');
VHT format detected

L-SIG Decoding

In a VHT transmission the L-SIG field is used to determine the receive time, or RXTIME, of the packet. RXTIME is calculated using the field bits of the L-SIG payload [ 1 Eq. 22-105]. The number of samples which contain the packet within rx can then be calculated. The L-SIG payload is decoded using an estimate of the channel and noise power obtained from the L-LTF.

% Recover L-SIG field bits
disp('Decoding L-SIG... ');
[rxLSIGBits, failCheck, eqLSIGSym] = wlanLSIGRecover(rx(pktOffset + (idxLSIG(1):idxLSIG(2)), :), ...
    chanEstLLTF, noiseVarNonHT, chanBW);

if failCheck % Skip L-STF length of samples and continue searching
    disp('** L-SIG check fail **');
    disp('L-SIG check pass');

% Measure EVM of L-SIG symbol
EVM = comm.EVM;
EVM.ReferenceSignalSource = 'Estimated from reference constellation';
EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK');
rmsEVM = EVM(eqLSIGSym);
fprintf('L-SIG EVM: %2.2f%% RMS\n', rmsEVM);

% Calculate the receive time and corresponding number of samples in the
% packet
lengthBits = rxLSIGBits(6:17);
RXTime = ceil((bit2int(double(lengthBits),12,false) + 3)/3) * 4 + 20; % us
numRxSamples = RXTime * 1e-6 * sr; % Number of samples in receive time

fprintf('RXTIME: %dus\n', RXTime);
fprintf('Number of samples in packet: %d\n\n', numRxSamples);
Decoding L-SIG... 
L-SIG check pass
L-SIG EVM: 1.83% RMS
RXTIME: 84us
Number of samples in packet: 6720

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

sampleOffset = max((-lstfLen + pktOffset), 1); % First index to plot
sampleSpan = numRxSamples + 2*lstfLen;         % Number of 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 ,:)));

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

VHT-SIG-A Decoding

The VHT-SIG-A field contains the transmission configuration of the packet. The VHT-SIG-A bits are recovered using the channel and noise power estimates obtained from the L-LTF.

% Recover VHT-SIG-A field bits
disp('Decoding VHT-SIG-A... ');
[rxSIGABits, failCRC, eqSIGASym] = wlanVHTSIGARecover(rx(pktOffset + (idxSIGA(1):idxSIGA(2)), :), ...
    chanEstLLTF, noiseVarNonHT, chanBW);

if failCRC
    disp('** VHT-SIG-A CRC fail **');
    disp('VHT-SIG-A CRC pass');

% Measure EVM of VHT-SIG-A symbols for BPSK and QBPSK modulation schemes
EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK');
rmsEVMSym1 = EVM(eqSIGASym(:,1));
EVM.ReferenceConstellation = wlanReferenceSymbols('QBPSK');
rmsEVMSym2 = EVM(eqSIGASym(:,2));
fprintf('VHT-SIG-A EVM: %2.2f%% RMS\n', mean([rmsEVMSym1 rmsEVMSym2]));
Decoding VHT-SIG-A... 

The helper function helperVHTConfigRecover returns a VHT format configuration object, cfgVHTRx, based on recovered VHT-SIG-A and L-SIG bits. Properties that are not required to decode the waveform are set to default values for a wlanVHTConfig object and therefore may differ from the value in cfgVHTTx. Examples of such properties include NumTransmitAntennas and SpatialMapping.

% Create a VHT format configuration object by retrieving packet parameters
% from the decoded L-SIG and VHT-SIG-A bits
cfgVHTRx = helperVHTConfigRecover(rxLSIGBits, rxSIGABits);

% Display the transmission configuration obtained from VHT-SIG-A
  Decoded VHT-SIG-A contents: 
       ChannelBandwidth: 'CBW80'
    NumSpaceTimeStreams: 2
                   STBC: 1
                    MCS: 5
          ChannelCoding: {'BCC'}
          GuardInterval: 'Long'
                GroupID: 63
             PartialAID: 275
            Beamforming: 0
             PSDULength: 1167

The information provided by VHT-SIG-A allows the location of subsequent fields within the received waveform to be calculated.

% Obtain starting and ending indices for VHT-LTF and VHT-Data fields
% using retrieved packet parameters
idxVHTLTF  = wlanFieldIndices(cfgVHTRx, 'VHT-LTF');
idxVHTSIGB = wlanFieldIndices(cfgVHTRx, 'VHT-SIG-B');
idxVHTData = wlanFieldIndices(cfgVHTRx, 'VHT-Data');

% Warn if waveform does not contain whole packet
if (pktOffset + double(idxVHTData(2))) > rxWaveLen
    fprintf('** Not enough samples to recover entire packet **\n\n');

VHT-SIG-B Decoding

The primary use of VHT-SIG-B is for signaling user information in a multi-user packet. In a single-user packet the VHT-SIG-B carries the length of the packet which can also be calculated using the L-SIG and VHT-SIG-A (which is demonstrated in the sections above). Despite not being required to decode a single-user packet, the VHT-SIG-B is recovered below and the bits interpreted. The VHT-SIG-B symbols are demodulated using a MIMO channel estimate obtained from the VHT-LTF. Note the CRC for VHT-SIG-B is carried in the VHT Data field.

% Estimate MIMO channel using VHT-LTF and retrieved packet parameters
demodVHTLTF = wlanVHTLTFDemodulate(rx(pktOffset + (idxVHTLTF(1):idxVHTLTF(2)), :), cfgVHTRx);
[chanEstVHTLTF, chanEstSSPilots] = wlanVHTLTFChannelEstimate(demodVHTLTF, cfgVHTRx);

% The L-LTF OFDM demodulator normalizes the output by the number of
% subcarriers. The VHT-SIG-B OFDM demodulator normalizes the output by the
% number of subcarriers and space-time streams. Therefore, estimate the
% noise power in VHT-SIG-B by scaling the L-LTF noise estimate by the ratio
% of the number of subcarriers in both fields, and the number of space-time
% streams.

numSTSTotal = sum(cfgVHTRx.NumSpaceTimeStreams, 1);
scalingFactor = (height(demodVHTLTF)/size(demodLLTF, 1))*numSTSTotal;
noiseVarVHT = noiseVarNonHT*scalingFactor;

% VHT-SIG-B Recover
disp('Decoding VHT-SIG-B...');
[rxSIGBBits, eqSIGBSym] = wlanVHTSIGBRecover(rx(pktOffset + (idxVHTSIGB(1):idxVHTSIGB(2)),:), ...
    chanEstVHTLTF, noiseVarVHT, chanBW);

% Measure EVM of VHT-SIG-B symbol
EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK');
rmsEVM = EVM(eqSIGBSym);
fprintf('VHT-SIG-B EVM: %2.2f%% RMS\n', rmsEVM);

% Interpret VHT-SIG-B bits to recover the APEP length (rounded up to a
% multiple of four bytes) and generate reference CRC bits
[refSIGBCRC, sigbAPEPLength] = helperInterpretSIGB(rxSIGBBits, chanBW, true);
disp('Decoded VHT-SIG-B contents: ');
fprintf('  APEP Length (rounded up to 4 byte multiple): %d bytes\n\n', sigbAPEPLength);
Decoding VHT-SIG-B...
Decoded VHT-SIG-B contents: 
  APEP Length (rounded up to 4 byte multiple): 1052 bytes

VHT Data Decoding

The reconstructed VHT configuration object can then be used to recover the VHT Data field. This includes the VHT-SIG-B CRC bits and PSDU.

The recovered VHT data symbols can then be analyzed as required. In this example the equalized constellation of the recovered VHT data symbols per spatial stream are displayed.

% Extract VHT Data samples from the waveform
vhtdata = rx(pktOffset + (idxVHTData(1):idxVHTData(2)), :);

% Estimate the noise power in VHT data field
noiseVarVHT = vhtNoiseEstimate(vhtdata, chanEstSSPilots, cfgVHTRx);

% Recover PSDU bits using retrieved packet parameters and channel
% estimates from VHT-LTF
disp('Decoding VHT Data field...');
[rxPSDU, rxSIGBCRC, eqDataSym] = wlanVHTDataRecover(vhtdata, chanEstVHTLTF, noiseVarVHT, cfgVHTRx, ...
    'LDPCDecodingMethod', 'norm-min-sum');

% Plot equalized constellation for each spatial stream
refConst = wlanReferenceSymbols(cfgVHTRx);
[Nsd, Nsym, Nss] = size(eqDataSym);
eqDataSymPerSS = reshape(eqDataSym, Nsd*Nsym, Nss);
for iss = 1:Nss
    constellationDiagram{iss}.ReferenceConstellation = refConst;
    constellationDiagram{iss}(eqDataSymPerSS(:, iss));

% Measure EVM of VHT-Data symbols
EVM.ReferenceConstellation = refConst;
rmsEVM = EVM(eqDataSym(:));
fprintf('VHT-Data EVM: %2.2f%% RMS\n', rmsEVM);
Decoding VHT Data field...
VHT-Data EVM: 4.68% RMS

The CRC bits for VHT-SIG-B recovered in VHT Data are then compared to the locally generated reference to determine whether the VHT-SIG-B and VHT data service bits have been recovered successfully.

% Test VHT-SIG-B CRC from service bits within VHT Data against
% reference calculated with VHT-SIG-B bits
if ~isequal(refSIGBCRC, rxSIGBCRC)
    disp('** VHT-SIG-B CRC fail **');
    disp('VHT-SIG-B CRC pass');

The FCS in the MAC frames can be validated using wlanMPDUDecode. As a VHT format frame is recovered, the PSDU contains an A-MPDU. The MPDUs are extracted from the A-MPDU using wlanAMPDUDeaggregate.

mpduList = wlanAMPDUDeaggregate(rxPSDU, cfgVHTRx);
fprintf('Number of MPDUs present in the A-MPDU: %d\n', numel(mpduList));
Number of MPDUs present in the A-MPDU: 1

The mpduList contains the de-aggregated list of MPDUs. Each MPDU in the list is passed to wlanMPDUDecode which validates the FCS and decodes the MPDU.

for i = 1:numel(mpduList)
    [macCfg, payload, decodeStatus] = wlanMPDUDecode(mpduList{i}, cfgVHTRx, ...
                                                    'DataFormat', 'octets');
    if strcmp(decodeStatus, 'FCSFailed')
        fprintf('** FCS failed for MPDU-%d **\n', i);
        fprintf('FCS passed for MPDU-%d\n', i);
FCS passed for MPDU-1

Selected Bibliography

  1. IEEE Std 802.11™-2020. IEEE 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.