%% 802.11ac Transmitter Measurements
% This example shows how to perform these transmitter measurements on an IEEE® 802.11ac™ waveform:
% Modulation accuracy
% Spectrum emission mask
% Spectrum flatness
% In-phase and quadrature (IQ) gain and phase imbalance
%% Introduction
% The transmitter modulation accuracy, required spectrum mask, and required spectral flatness for a given configuration is specified in Section 21.3.17 of [ 1 ]. This example shows how to perform these measurements on a waveform. This example also models, measures, and corrects the IQ gain and phase imbalance. Generate the waveform with WLAN Toolbox™ or use a captured waveform with a spectrum analyzer.
% The example generates 20 upsampled VHT packets with an 80 MHz channel bandwidth and a 10 microsecond gap between packets. Each packet contains random data and uses 256-QAM modulation. Add the IQ gain and phase imbalance to the filtered waveform. Use a high-power amplifier (HPA) model to introduce inband distortion and spectral regrowth. Perform the spectral emission mask measurement on the upsampled waveform after the high-power amplifier modeling. Downsample and correct the waveform with the estimated IQ gain and phase imbalance. Measure the error vector magnitude (EVM) of the VHT Data field to determine the modulation accuracy. Additionally, measure the spectral flatness. This diagram shows the workflow contained in the example.
%
%
%% 802.11ac VHT Packet Configuration
% This example generates an IEEE 802.11ac waveform consisting of multiple VHT format packets. Use the VHT format configuration object, wlanVHTConfig, to configure transmission properties of a VHT packet. This example configures the VHT waveform for a 80 MHz bandwidth. Because this example does not use space-time block coding, it can measure the modulation accuracy per spatial stream.

cfgVHT = wlanVHTConfig;            % Create packet configuration
cfgVHT.ChannelBandwidth = 'CBW80'; % 80 MHz
cfgVHT.NumTransmitAntennas = 1;    % One transmit antenna
cfgVHT.NumSpaceTimeStreams = 1;    % One space-time stream
cfgVHT.STBC = false;               % No STBC so one spatial stream
cfgVHT.MCS = 8;                    % Modulation: 256-QAM
cfgVHT.APEPLength = 3000;          % A-MPDU length pre-EOF padding in bytes

%
%% Waveform Generation
% Generate the VHT waveform for the specified bits and configuration by using the wlanWaveformGenerator function, specifying the desired oversampling factor, number of packets, and idle time between each packet.

osf = 3;          % 3x oversampling factor
numPackets = 20;  % Generate 20 packets
idleTime = 10e-6; % 10 microseconds idle time between packets

% Create random bits for all packets, data, and pass as an argument to wlanWaveformGenerator along with the VHT packet configuration object cfgVHT. This configures the waveform generator to synthesize an 802.11ac VHT waveform. Additionally, configure the waveform generator by using name-value pairs to generate multiple oversampled packets with a specified idle time between each packet.

% Create random data; PSDULength is in bytes
savedState = rng(0); % Set random state
data = randi([0 1],cfgVHT.PSDULength*8*numPackets,1);

% Generate a multi-packet waveform
txWaveform = wlanWaveformGenerator(data,cfgVHT, ...
    'OversamplingFactor',osf,'NumPackets',numPackets,'IdleTime',idleTime);

fs = wlanSampleRate(cfgVHT); % Baseband sampling rate of the waveform

%
%% Add Impariments
%% IQ Imbalance Modeling
% IQ imbalance arises when a front-end component does not respect the power balance or the orthogonality between the I and Q branches. This example adds IQ gain and phase imbalance to the transmitted waveform based on the flag modelIQImbalance. At the receiver, estimate the IQ gain and phase imbalance and correct the waveform as per the compensation scheme specified in [ 5 ].

modelIQImbalance = true; % Set to true to add IQ gain and phase imbalance

if modelIQImbalance
    iqGaindB = 1; % IQ gain imbalance in dB, specify from the range [-1 1]
    iqPhaseDeg = 1; % IQ phase imbalance in degrees, specify from the range [-2 2]
    iqGainLin = db2mag(iqGaindB); % Convert gain from dB to linear value
    txWaveform = real(txWaveform) + 1i*imag(txWaveform)*iqGainLin*exp(1j*iqPhaseDeg*pi/180); % As specified in Equation-1 of [5]
end

%
%% High-Power Amplifier Modeling
% The high-power amplifier introduces nonlinear behavior in the form of inband distortion and spectral regrowth. This example simulates the power amplifiers by using the Rapp model in 802.11ac [ 2 ], which introduces AM/AM distortion.
% Model the amplifier by using the comm.MemorylessNonlinearity object, and configure reduced distortion by specifying a backoff, hpaBackoff, such that the amplifier operates below its saturation point. You can increase the backoff to reduce EVM for higher MCS values.

pSaturation = 25; % Saturation power of a power amplifier in dBm
hpaBackoff = 13; % Power amplifier backoff in dB

% Create and configure a memoryless nonlinearity to model the amplifier
nonLinearity = comm.MemorylessNonlinearity;
nonLinearity.Method = 'Rapp model';
nonLinearity.Smoothness = 3; % p parameter
nonLinearity.LinearGain = -hpaBackoff;
nonLinearity.OutputSaturationLevel = db2mag(pSaturation-30);

% Apply the model to each transmit antenna
txWaveform = nonLinearity(txWaveform);

%
%% Thermal Noise
% Add thermal noise to each transmit antenna by using the comm.ThermalNoise object with a noise figure of 6 dB [ 3 ].

thNoise = comm.ThermalNoise('NoiseMethod','Noise Figure','SampleRate',fs*osf,'NoiseFigure',6);
for i = 1:cfgVHT.NumTransmitAntennas
    txWaveform(:,i) = thNoise(txWaveform(:,i));
end

%
%% Modulation Accuracy (EVM), Spectral Flatness and IQ Imbalance Measurements
%% Downsampling and Filtering
% Resample the oversampled waveform down to baseband for physical layer processing and EVM and spectral flatness measurements, applying a low-pass anti-aliasing filter before downsampling. The impact of the low-pass filter is visible in the spectral flatness measurement. The anti-aliasing filter is designed so that all active subcarriers are within the filter passband.
% Design resampling filter.

aStop = 40; % Stopband attenuation
ofdmInfo = wlanVHTOFDMInfo('VHT-Data',cfgVHT); % OFDM parameters
SCS = fs/ofdmInfo.FFTLength; % Subcarrier spacing
txbw = max(abs(ofdmInfo.ActiveFrequencyIndices))*2*SCS; % Occupied bandwidth
[L,M] = rat(osf);
maxLM = max([L M]);
R = (fs-txbw)/fs;
TW = 2*R/maxLM; % Transition width
b = designMultirateFIR(L,M,TW,aStop);

% Resample the waveform to baseband.

firinterp = dsp.FIRRateConverter(M,L,b);
rxWaveform = firinterp(txWaveform);

%
%% Receiver Processing
% This section detects, synchronizes, and extracts each packet in rxWaveform, then measures the EVM, spectral flatness, and IQ imbalance. For each packet, the example performs these steps:
% Detect the start of the packet
% Extract the non-HT fields
% Estimate and correct coarse carrier frequency offset (CFO)
% Perform fine symbol timing estimate by using the frequency-corrected non-HT fields
% Extract the packet from the waveform by using the fine symbol timing offset
% Correct the extracted packet with the coarse CFO estimate
% Extract the L-LTF, then estimate the fine CFO and correct for the whole packet
% Extract the VHT-LTF and perform channel estimation for each of the transmit streams
% Measure the IQ imbalance from the channel estimate and perform correction on the channel estimate
% Measure the spectral flatness by using the channel estimate
% Extract and OFDM demodulate the VHT data field
% Perform noise estimation by using the demodulated data field pilots and single-stream channel estimate at pilot subcarriers
% Phase-correct and equalize the VHT data field by using the channel and noise estimates
% Correct the equalized data subcarriers with the IQ imbalance estimate
% For each data-carrying subcarrier in each spatial stream, find the closest constellation point and measure the EVM
% The diagram shows the processing chain:
%
% The VHT-LTF symbols include pilot symbols to allow for phase tracking, but this example does not perform phase tracking.
% Test the spectral flatness for each packet by measuring the deviation in the magnitude of individual subcarriers in the channel estimate against the average [ 1 ]. Plot these deviations for each packet using the helper function vhtTxSpectralFlatnessMeasurement. Plot the average EVM per data-carrying subcarrier and the equalized symbols for each packet.
% Demodulate, equalize, and decode the VHT Data symbols by using the wlanVHTDataRecover function. Parameterize this function to perform pilot phase tracking and zero-forcing equalization as required by the standard. This example measures the modulation accuracy from the equalized symbols.
% This example makes two different EVM measurements using two instances of comm.EVM.
% RMS EVM per packet, which comprises averaging the EVM over subcarriers, OFDM symbols, and spatial streams.
% RMS EVM per subcarrier per spatial stream for a packet. Because this configuration maps spatial streams directly to antennas, this measurement can help detect frequency-dependent impairments, which may affect individual RF chains differently. This measurement averages the EVM over OFDM symbols only.

% Setup EVM measurements
[EVMPerPkt,EVMPerSC] = vhtEVMSetup(cfgVHT);

% This code configures objects and variables for processing.

% Get indices for accessing each field within the time-domain packet
ind = wlanFieldIndices(cfgVHT);

rxWaveformLength = size(rxWaveform,1);
pktLength = double(ind.VHTData(2));

% Define the minimum length of data we can detect; length of the L-STF in
% samples
minPktLen = double(ind.LSTF(2)-ind.LSTF(1))+1;

% Setup the measurement plots
[hSF,hCon,hEVM] = vhtTxSetupPlots(cfgVHT);

rmsEVM = zeros(numPackets,1);
pktOffsetStore = zeros(numPackets,1);

rng(savedState); % Restore random state

% Detect and process packets within the received waveform, rxWaveform by using a while loop, which performs these steps.
% Detect a packet by indexing into rxWaveform with the sample offset, searchOffset
% Detect and process the first packet within rxWaveform
% Detect and process the next packet by incrementing the sample index offset, searchOffset
% Repeat until no further packets are detected

pktNum = 0;
searchOffset = 0; % Start at first sample (no offset)
while (searchOffset+minPktLen)<=rxWaveformLength
    % Packet detect
    pktOffset = wlanPacketDetect(rxWaveform,cfgVHT.ChannelBandwidth);
    % Packet offset from start of waveform
    pktOffset = searchOffset+pktOffset;
    % If no packet detected or offset outwith bounds of waveform then stop
    if isempty(pktOffset) || (pktOffset<0) || ...
            ((pktOffset+ind.LSIG(2))>rxWaveformLength)
        break;
    end

    % Extract non-HT fields and perform coarse frequency offset correction
    % to allow for reliable symbol timing
    nonht = rxWaveform(pktOffset+(ind.LSTF(1):ind.LSIG(2)),:);
    coarsefreqOff = wlanCoarseCFOEstimate(nonht,cfgVHT.ChannelBandwidth);
    nonht = helperFrequencyOffset(nonht,fs,-coarsefreqOff);

    % Determine offset between the expected start of L-LTF and actual start
    % of L-LTF
    lltfOffset = wlanSymbolTimingEstimate(nonht,cfgVHT.ChannelBandwidth);
    % Determine packet offset
    pktOffset = pktOffset+lltfOffset;
    % If offset is without bounds of waveform skip samples and continue
    % searching within remainder of the waveform
    if (pktOffset<0) || ((pktOffset+pktLength)>rxWaveformLength)
        searchOffset = pktOffset+double(ind.LSTF(2))+1;
        continue;
    end

    % Timing synchronization complete; extract the detected packet
    rxPacket = rxWaveform(pktOffset+(1:pktLength),:);
    pktNum = pktNum+1;
    disp(['  Packet ' num2str(pktNum) ' at index: ' num2str(pktOffset+1)]);

    % Apply coarse frequency correction to the extracted packet
    rxPacket = helperFrequencyOffset(rxPacket,fs,-coarsefreqOff);

    % Perform fine frequency offset correction on the extracted packet
    lltf = rxPacket(ind.LLTF(1):ind.LLTF(2),:); % Extract L-LTF
    fineFreqOff = wlanFineCFOEstimate(lltf,cfgVHT.ChannelBandwidth);
    rxPacket = helperFrequencyOffset(rxPacket,fs,-fineFreqOff);

    % Extract VHT-LTF samples, demodulate and perform channel estimation
    vhtltf = rxPacket(ind.VHTLTF(1):ind.VHTLTF(2),:);
    vhtltfDemod = wlanVHTLTFDemodulate(vhtltf,cfgVHT);

    % Get single stream channel estimate
    chanEstSSPilots = vhtSingleStreamChannelEstimate(vhtltfDemod,cfgVHT);

    % Channel estimate
    chanEst = wlanVHTLTFChannelEstimate(vhtltfDemod,cfgVHT);

    % Perform IQ gain and phase imbalance estimation
    [gainEst,phaseEst,alphaEst,betaEst,gamma,dataRot] = ...
                                    helperIQImbalanceEstimate(chanEst,cfgVHT);
    fprintf('    Measured IQ gain & phase imbalance: %2.2f dB, %2.2f deg\n',gainEst,phaseEst);

    % Perform IQ gain and phase imbalance correction on channel
    % estimates
    chanEst = chanEst./(alphaEst + betaEst.*gamma); % As specified in Equation-29 of [5]

    % Spectral flatness measurement
    vhtTxSpectralFlatnessMeasurement(chanEst,cfgVHT,pktNum,hSF);

    % Extract VHT Data samples from the waveform
    vhtdata = rxPacket(ind.VHTData(1):ind.VHTData(2),:);

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

    % Extract VHT Data samples and perform OFDM demodulation, equalization
    % and phase tracking
    [~,~,eqSym] = wlanVHTDataRecover(vhtdata,chanEst,noiseVarVHT,cfgVHT,...
        'EqualizationMethod','ZF','PilotPhaseTracking','PreEQ'); % Use zero forcing algorithm for equalization

    % Perform IQ gain and phase imbalance correction on VHT data
    eqSym = eqSym.*dataRot; % Carrier rotation on data subcarriers
    eqSym = ((conj(alphaEst)*eqSym)-(betaEst*conj(eqSym(end:-1:1,:,:))))/((abs(alphaEst)^2)-(abs(betaEst)^2));  % As specified in Equation-30 of [5]

    % Compute RMS EVM over all spatial streams for packet
    rmsEVM(pktNum) = EVMPerPkt(eqSym);
    fprintf('    RMS EVM: %2.2f%%, %2.2fdB\n',rmsEVM(pktNum),20*log10(rmsEVM(pktNum)/100));

    % Compute RMS EVM per subcarrier and spatial stream for the packet
    evmPerSC = EVMPerSC(eqSym); % Nst-by-1-by-Nss

    % Plot RMS EVM per subcarrier and equalized constellation
    vhtTxEVMConstellationPlots(eqSym,evmPerSC,cfgVHT,pktNum,hCon,hEVM);

    % Store the offset of each packet within the waveform
    pktOffsetStore(pktNum) = pktOffset;

    % Increment waveform offset and search remaining waveform for a packet
    searchOffset = pktOffset+pktLength+minPktLen;
end

if pktNum>0
    fprintf('Average EVM for %d packets: %2.2f%%, %2.2fdB\n', ...
        pktNum,mean(rmsEVM(1:pktNum)),20*log10(mean(rmsEVM(1:pktNum))/100));
else
    disp('No complete packet detected');
end

%
%% Transmit Spectrum Emission Mask Measurement
% This section measures the spectral mask of the filtered and impaired waveform after high-power amplifier modeling.
% The transmitter spectral mask test [ 4 ] uses a time-gated spectral measurement of the VHT Data field. The example extracts the VHT Data field of each packet from the oversampled waveform, txWaveform, by using the start indices of each packet within the waveform. Any delay introduced in the baseband processing chain used to determine the packet indices must be accounted for when gating the VHT data field within txWaveform. Concatenate the extracted VHT Data fields in preparation for measurement.

% Indices for accessing each field within the time-domain packet
ind = wlanFieldIndices(cfgVHT,'OversamplingFactor', osf);
startIdx = ind.VHTData(1); % Start of Data
endIdx = ind.VHTData(2);   % End of Data
idleNSamps = idleTime*fs*osf; % Idle time samples
perPktLength = endIdx+idleNSamps;

idx = zeros(endIdx-startIdx+1,numPackets);
for i = 1:numPackets
    % Start of packet in txWaveform, accounting for the filter delay
    pktOffset = (i-1)*perPktLength;
    % Indices of non-HT Data in txWaveform
    idx(:,i) = pktOffset+(startIdx:endIdx);
end
gatedVHTData = txWaveform(idx(:),:);

% The 802.11ac standard specifies the spectral mask relative to the peak power spectral density. The helper function helperSpectralMaskTest generates a plot which overlays the required mask with the measured PSD.

if pktNum>0
    helperSpectralMaskTest(gatedVHTData,fs,osf);
end

%
%% Conclusion and Further Exploration
% This example plots four results: spectral flatness, RMS EVM per subcarrier, equalized constellation, and spectral mask.
% The high-power amplifier model introduces significant inband distortion and spectral regrowth which is visible in the EVM results, noisy constellation and out-of-band emissions in the spectral mask plot. Try increasing the high-power amplifier backoff and note the improved EVM, constellation and lower out-of-band emissions.
% Try using different values for iqGaindB and iqPhaseDeg and note the impact on EVM and constellation.
% Related examples:
% 802.11be 4096-QAM 320 MHz Waveform Generation and Analysis
% 802.11ba WUR Waveform Generation and Analysis
% 802.11ad Transmitter Spectral Emission Mask Testing
% 802.11p Spectral Emission Mask Testing
% Recover and Analyze Packets in 802.11 Waveform
%% Selected Bibliography
% IEEE Std 802.11™-2016 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.
% Loc and Cheong. IEEE P802.11 Wireless LANs. TGac Functional Requirements and Evaluation Methodology Rev. 16. 2011-01-19.
% Perahia, E., and R. Stacey. Next Generation Wireless LANs: 802.11n and 802.11ac. 2nd Edition. United Kingdom: Cambridge University Press, 2013.
% Archambault, Jerry, and Shravan Surineni. "IEEE 802.11 spectral measurements using vector signal analyzers." RF Design 27.6 (2004): 38-49.
% M. Janaswamy, N. K. Chavali and S. Batabyal, "Measurement of transmitter IQ parameters in HT and VHT wireless LAN systems," 2016 International Conference on Signal Processing and Communications (SPCOM), Bangalore.
% Copyright 2015-2021 The MathWorks, Inc.
%