function [status,res] = recoverPreamble(rx,chanBW,searchOffset,varargin) %recoverPreamble Preamble signal recovery % [STATUS,RES] = recoverPreamble(RX,CHANBW,SEARCHOFFSET) detects a packet % and performs analysis of the non-HT preamble. % % STATUS is the processing status and is either 'Success' or 'No packet % detected'. % % RES is a structure containing signal analysis. % % RX is the received time-domain waveform. It is a Ns-by-Nr matrix of % real or complex values, where Ns represents the number of time-domain % samples in the waveform, and Nr represents the number of receive % antennas. % % CHANBW is the channel bandwidth and must be 'CBW20', 'CBW40', 'CBW80', % 'CBW160', or 'CBW320'. % % SEARCHOFFSET is the offset from the start of RX in samples to begin % searching for a packet. % % [STATUS,RES] = recoverPreamble(...,CFGALG) optionally allows % algorithm options to be used as specified in the structure CFGALG. % Copyright 2019-2025 The MathWorks, Inc. persistent wpd cfgAlg = algorithmConfig(varargin{:}); if isempty(wpd) wpd = hWLANPacketDetector(rx,chanBW); else wpd.Waveform = rx; wpd.ChannelBandwidth = chanBW; end wpd.Threshold = cfgAlg.PacketDetectionThreshold; cfgBase = wlanEHTMUConfig(chanBW); index = wlanFieldIndices(cfgBase); sr = wlanSampleRate(cfgBase); if cfgAlg.EnergyDetection movrms = dsp.MovingRMS; movrms.WindowLength = cfgAlg.EnergyDetectionWindow; threshold = 10^(cfgAlg.EnergyDetectionThreshold/20); end % Minimum packet length is L-STF, L-LTF, L-SIG + 1 Data symbol lstfLen = double(index.LSTF(2)); % Number of samples in L-STF minPktLen = lstfLen*3; % Minimum number of samples to skip before searching for next packet minAdvLen = lstfLen*4/10; rxWaveformLen = size(rx,1); % Do not search for packets if waveform is too short if (searchOffset+minPktLen)>rxWaveformLen status = 'No packet detected'; res = defaultResults(); return end % Initialize incase no packets detected packetOffset = nan; cfoEstimate = nan; lstfPower = nan; lltfPower = nan; chanEstNonHT = []; noiseEstNonHT = nan; lltfSNREst = nan; status = 'No packet detected'; wpd.SearchOffset = searchOffset; while (wpd.SearchOffset+minPktLen)<=rxWaveformLen % Detect a packet if cfgAlg.SkipPacketDetection packetOffset = 0; else packetOffset = findPacketStart(wpd); end % Adjust packet offset if isempty(packetOffset) || (packetOffset<0) || (packetOffset+double(index.LSIG(2))>rxWaveformLen) status = 'No packet detected'; break end if cfgAlg.EnergyDetection % Run RMS over part of the waveform of interest - where we expect a ramp up reset(movrms) idx = (packetOffset+(-movrms.WindowLength+1:(2*movrms.WindowLength))); idx(idx<1) = []; % In case waveform detected as start rxRMS = movrms(rx(idx,:)); if all(mean(rxRMS(movrms.WindowLength+1:end,:),2)rxWaveformLen break end % Force packet offset not to be 0 to prevent hard errors packetOffset = max(packetOffset,0); % Extract preamble with fine timing sync preamble = rx(packetOffset+(index.LSTF(1):index.LLTF(2)),:); preamble = frequencyOffset(preamble,sr,-coarseFreqOffset); % Fine Frequency Offset Estimation % Perform fine frequency offset correction on the synchronized and % coarse corrected Non-HT fields lltf = preamble(index.LLTF(1):index.LLTF(2),:); % Extract L-LTF fineFreqOffset = wlanFineCFOEstimate(lltf,chanBW); preamble = frequencyOffset(preamble,sr,-fineFreqOffset); cfoEstimate = coarseFreqOffset+fineFreqOffset; % Total CFO % AGC % Scale preamble by rx power before performing channel estimation lstf = preamble(index.LSTF(1):index.LSTF(2),:); lstfPower = mean(lstf(:).*conj(lstf(:))); preamble = preamble/sqrt(lstfPower); % Channel and noise estimation using L-LTF lltf = preamble(index.LLTF(1):index.LLTF(2),:); demodLLTF = wlanLLTFDemodulate(lltf,chanBW); chanEstNonHT = wlanLLTFChannelEstimate(demodLLTF,chanBW,cfgAlg.LLTFChannelEstimateSmoothingSpan); noiseEstNonHT = wlanLLTFNoiseEstimate(demodLLTF); lltfPower = mean(lltf(:).*conj(lltf(:)))*lstfPower; % Subtract AGC scaling % Test if carrier lost (L-LTF power substantially less than L-STF) if cfgAlg.DetectCarrierLoss if lltfPower<(0.25*lstfPower) % Skip 4/10 of L-STF length of samples and continue searching wpd.SearchOffset = packetOffset+minAdvLen; continue end end % Test large difference in energy between L-STF and L-LTF which is suspicious if cfgAlg.DetectPowerFluctuation if lstfPower<(0.125*lltfPower) % Skip 4/10 of L-STF length of samples and continue searching wpd.SearchOffset = packetOffset+minAdvLen; continue end end % Estimate SNR from L-LTF lltfSNREst = 10*log10(mean(abs(chanEstNonHT(:)).^2)/noiseEstNonHT); % Test if SNR it too low or isnan (when channel and noise estimate are 0) if cfgAlg.DetectLLTFSNRTooLow if isnan(lltfSNREst) || lltfSNREst0 cfg = varargin{1}; if ~isfield(cfg,'DetectCarrierLoss') cfg.DetectCarrierLoss = true; end if ~isfield(cfg,'DetectPowerFluctuation') cfg.DetectPowerFluctuation = true; end if ~isfield(cfg,'DetectLLTFSNRTooLow') cfg.DetectLLTFSNRTooLow = true; end if ~isfield(cfg,'SkipPacketDetection') cfg.SkipPacketDetection = false; end else cfg = struct; cfg.PacketDetectionThreshold = 0.5; cfg.EnergyDetection = false; cfg.EnergyDetectionThreshold = 0; cfg.EnergyDetectionWindow = 20; cfg.LLTFChannelEstimateSmoothingSpan = 1; cfg.DetectCarrierLoss = true; cfg.DetectPowerFluctuation = true; cfg.DetectLLTFSNRTooLow = true; cfg.LLTFSNRDetectionThreshold = 0; cfg.SkipPacketDetection = false; end end