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 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 **'); 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
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 **'); end
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 **'); else disp('L-SIG check pass'); end % 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 **'); else disp('VHT-SIG-A CRC pass'); end % 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]));
Decoding VHT-SIG-A... VHT-SIG-A CRC pass VHT-SIG-A EVM: 2.06% RMS
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 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 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'); end
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 release(EVM); 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... VHT-SIG-B EVM: 5.21% RMS 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)); end % Measure EVM of VHT-Data symbols release(EVM); 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 **'); else disp('VHT-SIG-B CRC pass'); end
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); else fprintf('FCS passed for MPDU-%d\n', i); end end
FCS passed for MPDU-1
Selected Bibliography
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.