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™ (Wi-Fi® 5) VHT waveform. The receiver recovers the packet format parameters from the preamble fields to decode the data.

Introduction

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

  • The L-SIG field contains information that allows 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.

This example generates a waveform, then detects and decodes a packet within it. The waveform contains a valid MAC frame with frame check sequence (FCS). Except for the channel bandwidth, the receiver does not know any of the transmission parameters. The receiver obtains the parameters by decoding the L-SIG and VHT-SIG-A preamble fields in the packet. The receiver then uses the retrieved transmission configuration to decode the VHT-SIG-B and VHT Data fields. Additionally, the example performs the following analysis:

  • Display the waveform of the received packet.

  • Display the spectrum of the detected packet.

  • Display the constellation of the equalized data symbols per spatial stream.

  • Measure the error vector magnitude (EVM) of each field.

Waveform Transmission

This example generates an 802.11ac VHT single-user waveform locally, but you can use a captured waveform. You can acquire I/Q data from a wide range of instruments using Instrument Control Toolbox™ and software-defined radio platforms.

The example impairs the locally generated waveform using a 3x3 TGac fading channel, additive white Gaussian noise, and carrier frequency offset. To generate a waveform locally, configure a wlanVHTConfig object. Only the transmitter side uses this VHT configuration object. The receiver creates another VHT configuration object when it decodes the packet. The vhtSigRecGenerateWaveform helper function generates the impaired waveform locally. The processing steps within the helper function are:

  • Generate and encode a valid MAC frame into a VHT waveform.

  • Pass the waveform through a TGac fading channel model.

  • Add carrier frequency offset to the waveform.

  • Add additive white Gaussian noise 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 examples stores the signal in the variable rx. The processing steps to recover a packet are:

  • Detect and synchronize the packet.

  • Detect the packet format.

  • Extract the L-SIG field and recover its information bits to determine the length of the packet in microseconds.

  • Extract the VHT-SIG-A field and recover its information bits.

  • Retrieve the packet format parameters from the decoded L-SIG and VHT-SIG-A bits.

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

  • Extract the VHT-SIG-B field and recover its information bits.

  • Extract the VHT-Data field and recover the PSDU and VHT-SIG-B cyclic redundancy check (CRC) bits 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. To calculate these indices, use a default transmission configuration object with the channel bandwidth you originally specified.

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

Configure objects and variables for processing.

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

% Set up 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. The example uses a while loop to detect and synchronize a packet within the received waveform. To detect a packet, use the sample offset searchOffset to index elements within the array rx. Detect and process the first packet within rx. If the synchronization fails for the detected packet, the example increments the sample index offset searchOffset to move beyond the processed packet in rx. This process repeats until the example successfully detects and synchronizes a packet.

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 **');
    end

    % 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');
        break;
    end
    
    % 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
end
Packet detected at index 1600
Estimated CFO: 61954.3 Hz

Format Detection

To detect the format of the packet, use the three OFDM symbols that immediately follow the L-LTF. This requires estimates of the channel and noise power from the L-LTF.

% 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']);
VHT format detected
if ~strcmp(fmt,'VHT')
    error('** A format other than VHT has been detected **');
end

L-SIG Decoding

VHT transmissions use the L-SIG field to determine the receive time, or RXTIME, of the packet. The field bits of the L-SIG payload determine RXTIME via Equation 22-105 in [1]. The receiver uses RXTIME to calculate the number of samples that contain the packet within rx. The example decodes the L-SIG payload using the estimates of the channel and noise power you obtained from the L-LTF.

% Recover L-SIG field bits
disp('Decoding L-SIG... ');
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 **');
else
    disp('L-SIG check pass');
end
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);
L-SIG EVM: 1.83% RMS
% 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);
RXTIME: 84us
fprintf('Number of samples in packet: %d\n\n', numRxSamples);
Number of samples in packet: 6720

Display the waveform and spectrum of the detected packet within rx 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 possible
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. To recover the VHT-SIG-A bits, use the channel and noise power estimates you obtained from the L-LTF.

% Recover VHT-SIG-A field bits
disp('Decoding VHT-SIG-A... ');
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 **');
else
    disp('VHT-SIG-A CRC pass');
end
VHT-SIG-A CRC pass
% Measure EVM of VHT-SIG-A symbols for BPSK and QBPSK modulation schemes
release(EVM);
EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK');
rmsEVMSym1 = EVM(eqSIGASym(:,1));
release(EVM);
EVM.ReferenceConstellation = wlanReferenceSymbols('QBPSK');
rmsEVMSym2 = EVM(eqSIGASym(:,2));
fprintf('VHT-SIG-A EVM: %2.2f%% RMS\n', mean([rmsEVMSym1 rmsEVMSym2]));
VHT-SIG-A EVM: 2.06% RMS

The helperVHTConfigRecover helper function returns a VHT format configuration object, cfgVHTRx, based on the recovered VHT-SIG-A and L-SIG bits. The helper function sets properties that are not required to decode the waveform to their default values for a wlanVHTConfig object. Therefore, these property values may differ from the corresponding values 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
vhtSigRecDisplaySIGAInfo(cfgVHTRx);
  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 in the VHT-SIG-A field enables the receiver to calculate the location of subsequent fields within the received waveform.

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

VHT-SIG-B Decoding

The primary use of the VHT-SIG-B field is signaling user information in a multi-user packet. In a single-user packet, the VHT-SIG-B field carries the length of the packet. As this example has shown, the receiver can also calculate the length of the packet using the L-SIG and VHT-SIG-A fields. Despite not being required to decode a single-user packet, the example recovers the VHT-SIG-B field and interprets its bits. The example demodulates the VHT-SIG-B symbols using a MIMO channel estimate it obtains from the VHT-LTF. Note the CRC for the VHT-SIG-B field 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...');
Decoding VHT-SIG-B...
[rxSIGBBits, eqSIGBSym] = wlanVHTSIGBRecover(rx(pktOffset + (idxVHTSIGB(1):idxVHTSIGB(2)),:), ...
    chanEstVHTLTF, noiseVarVHT, chanBW);
    
% Measure EVM of VHT-SIG-B symbol
release(EVM);
EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK');
rmsEVM = EVM(eqSIGBSym);
fprintf('VHT-SIG-B EVM: %2.2f%% RMS\n', rmsEVM);
VHT-SIG-B EVM: 5.21% RMS
    
% 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: ');
Decoded VHT-SIG-B contents: 
fprintf('  APEP Length (rounded up to 4 byte multiple): %d bytes\n\n', sigbAPEPLength);
  APEP Length (rounded up to 4 byte multiple): 1052 bytes

VHT Data Decoding

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

Then, analyze the recovered VHT data symbols as required. This example displays the equalized constellation of the recovered VHT data symbols per spatial stream.

% 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...');
Decoding VHT Data field...
[rxPSDU, rxSIGBCRC, eqDataSym] = wlanVHTDataRecover(vhtdata, chanEstVHTLTF, noiseVarVHT, cfgVHTRx, ...
     LDPCDecodingMethod='norm-min-sum', EarlyTermination=true);

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

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

To determine whether the VHT-SIG-B and VHT data service bits have been recovered successfully, compare the CRC bits for the VHT-SIG-B field to the locally generated reference .

% 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 **');
else
    disp('VHT-SIG-B CRC pass');
end
VHT-SIG-B CRC pass

The recovered PSDU contains an A-MPDU. Extract the MPDUs from the A-MPDU.

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 deaggregated list of MPDUs. Decode the MPDU and validate the FCS of each 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);
    else
        fprintf('FCS passed for MPDU-%d\n', i);
    end
end
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.

See Also

| |