Impl task1 some part of task2
This commit is contained in:
@@ -0,0 +1,245 @@
|
|||||||
|
classdef hWLANPacketDetector < handle & comm.internal.ConfigBase
|
||||||
|
%hWLANPacketDetector OFDM packet detection using the L-STF
|
||||||
|
%
|
||||||
|
% WPD = hWLANPacketDetector(X,CBW) creates an hWLANPacketDetector object,
|
||||||
|
% WPD, that sets the Waveform property to X and ChannelBandwidth to CBW.
|
||||||
|
%
|
||||||
|
% WPD = hWLANPacketDetector(...,Name,Value) creates an
|
||||||
|
% hWLANPacketDetector object, WPD, with the specified property Name set
|
||||||
|
% to the specified Value. You can specify additional name-value pair
|
||||||
|
% arguments in any order as (Name1,Value1,...,NameN,ValueN).
|
||||||
|
%
|
||||||
|
% hWLANPacketDetector methods:
|
||||||
|
%
|
||||||
|
% findPacketStart - Returns the offset to the start of a detected
|
||||||
|
% packet in Waveform
|
||||||
|
%
|
||||||
|
% hWLANPacketDetector properties:
|
||||||
|
%
|
||||||
|
% Waveform - A time-domain signal specified as a Ns-by-Nr
|
||||||
|
% matrix of real or complex float values where Ns
|
||||||
|
% represents the number of time domain samples and
|
||||||
|
% Nr represents the number of receive antennas.
|
||||||
|
% ChannelBandwidth - A text scalar describing the channel bandwidth of
|
||||||
|
% WAVEFORM. The value must be 'CBW5', 'CBW10',
|
||||||
|
% 'CBW20', 'CBW40', 'CBW80', 'CBW160', or 'CBW320'.
|
||||||
|
% OversamplingFactor - The oversampling factor of WAVEFORM. The value
|
||||||
|
% must be greater than or equal to 1. The default is
|
||||||
|
% 1.
|
||||||
|
% Threshold - The threshold which the decision statistic must
|
||||||
|
% meet or exceed to detect a packet in WAVEFORM when
|
||||||
|
% calling FINDPACKETSTART. The value must be a real
|
||||||
|
% scalar that is greater than 0 and less than or
|
||||||
|
% equal to 1. The default is 0.5.
|
||||||
|
% SearchOffset - The index from which FINDPACKETSTART looks for a
|
||||||
|
% packet. The value must be a real scalar integer
|
||||||
|
% that is greater than or equal to 0 and less than
|
||||||
|
% Ns in WAVEFORM. The default is 0.
|
||||||
|
%
|
||||||
|
%
|
||||||
|
% % Example 1:
|
||||||
|
% % Detect a received 802.11a packet
|
||||||
|
% cfgNonHT = wlanNonHTConfig; % Create packet configuration
|
||||||
|
%
|
||||||
|
% % Generate transmit waveform
|
||||||
|
% txWaveform = wlanWaveformGenerator([1;0;0;1],cfgNonHT, ...
|
||||||
|
% 'WindowTransitionTime',0);
|
||||||
|
%
|
||||||
|
% % Delay the signal by appending zeros at the start
|
||||||
|
% rxWaveform = [zeros(20,1);txWaveform];
|
||||||
|
%
|
||||||
|
% wpd = hWLANPacketDetector(rxWaveform,cfgNonHT.ChannelBandwidth);
|
||||||
|
% wpd.SearchOffset = 5;
|
||||||
|
% wpd.Threshold = 0.99;
|
||||||
|
%
|
||||||
|
% startOffset = findPacketStart(wpd);
|
||||||
|
%
|
||||||
|
% disp("Packet start offset: " + startOffset)
|
||||||
|
|
||||||
|
% Copyright 2024 The MathWorks, Inc.
|
||||||
|
|
||||||
|
properties
|
||||||
|
Waveform {mustBeFloat,mustBeFinite} = [];
|
||||||
|
ChannelBandwidth {mustBeTextScalar} = 'CBW20';
|
||||||
|
OversamplingFactor (1,1) {mustBeNumeric, mustBeFinite, mustBeGreaterThanOrEqual(OversamplingFactor, 1)} = 1;
|
||||||
|
Threshold (1,1) {mustBeFloat, mustBeReal, mustBeInRange(Threshold,0,1,'exclude-lower')} = 0.5;
|
||||||
|
SearchOffset (1,1) {mustBeNumeric,mustBeInteger,mustBeNonnegative} = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
properties(Access=private)
|
||||||
|
UpdateDecisionStatistic = true;
|
||||||
|
UpdatePacketOffsets = true;
|
||||||
|
FoundOffsets;
|
||||||
|
DecisionStatistic;
|
||||||
|
DetectedColumnIndicies;
|
||||||
|
end
|
||||||
|
|
||||||
|
methods
|
||||||
|
function obj = hWLANPacketDetector(wav,cbw,opts)
|
||||||
|
arguments
|
||||||
|
wav;
|
||||||
|
cbw;
|
||||||
|
opts.Threshold
|
||||||
|
opts.OversamplingFactor
|
||||||
|
opts.SearchOffset
|
||||||
|
end
|
||||||
|
nvpairs = [{'Waveform' wav 'ChannelBandwidth' cbw} namedargs2cell(opts)];
|
||||||
|
obj@comm.internal.ConfigBase(nvpairs{:});
|
||||||
|
end
|
||||||
|
|
||||||
|
function [startOffset,M] = findPacketStart(obj)
|
||||||
|
%findPacketStart Return the offset of a detected packet
|
||||||
|
%
|
||||||
|
% [STARTOFFSET,M] = findPacketStart(OBJ) returns the index of a
|
||||||
|
% detected packet in WAVEFORM. The index returned is the next
|
||||||
|
% closest index to SEARCHOFFSET. If no packet is detected an empty
|
||||||
|
% value is returned.
|
||||||
|
%
|
||||||
|
% OBJ is a hWLANPacketDetector object.
|
||||||
|
%
|
||||||
|
% STARTOFFSET is an integer scalar indicating the location of the
|
||||||
|
% start of a detected packet.
|
||||||
|
%
|
||||||
|
% M is a real vector of size N-by-1, representing the decision
|
||||||
|
% statistics based on auto-correlation of WAVEFORM. The
|
||||||
|
% length of N depends on index of a successful detection of a
|
||||||
|
% packet.
|
||||||
|
|
||||||
|
if isempty(obj.Waveform)
|
||||||
|
startOffset = [];
|
||||||
|
M = [];
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
cbw = obj.ChannelBandwidth;
|
||||||
|
[fftLen,nsc] = wlan.internal.cbw2nfft(cbw);
|
||||||
|
osf = obj.OversamplingFactor;
|
||||||
|
wlan.internal.validateOFDMOSF(osf, fftLen, 0); % Validate OSF
|
||||||
|
|
||||||
|
Td = 0.8e-6; % Time period of a short training symbol for 20MHz
|
||||||
|
symbolLength = Td*osf*nsc*20e6;
|
||||||
|
|
||||||
|
lenLSTF = symbolLength*10; % Length of 10 L-STF symbols
|
||||||
|
lenHalfLSTF = lenLSTF/2; % Length of 5 L-STF symbols
|
||||||
|
|
||||||
|
if obj.UpdateDecisionStatistic
|
||||||
|
inpLength = size(obj.Waveform,1);
|
||||||
|
|
||||||
|
% Append zeros to make the input equal to multiple of L-STF/2
|
||||||
|
if inpLength<=lenHalfLSTF
|
||||||
|
numPadSamples = lenLSTF - inpLength;
|
||||||
|
else
|
||||||
|
numPadSamples = lenHalfLSTF*ceil(inpLength/lenHalfLSTF) - inpLength;
|
||||||
|
end
|
||||||
|
|
||||||
|
padSamples = zeros(numPadSamples,size(obj.Waveform,2),'like',obj.Waveform);
|
||||||
|
|
||||||
|
% Process the input waveform in blocks of L-STF length. The processing
|
||||||
|
% blocks are offset by half the L-STF length.
|
||||||
|
numBlocks = (inpLength + numPadSamples)/lenHalfLSTF;
|
||||||
|
|
||||||
|
searchBuffer = reshape([obj.Waveform;padSamples],lenHalfLSTF,numBlocks,size(obj.Waveform,2));
|
||||||
|
searchBuffer = [searchBuffer(:,1:end-1,:);searchBuffer(:,2:end,:)];
|
||||||
|
|
||||||
|
[obj.FoundOffsets,obj.DecisionStatistic,obj.DetectedColumnIndicies] = ...
|
||||||
|
wlan.internal.detectPackets(searchBuffer,symbolLength, ...
|
||||||
|
lenLSTF,obj.Threshold);
|
||||||
|
|
||||||
|
obj.UpdateDecisionStatistic = false;
|
||||||
|
obj.UpdatePacketOffsets = false;
|
||||||
|
elseif obj.UpdatePacketOffsets
|
||||||
|
% obj.Threshold was updated so update the offsets
|
||||||
|
[obj.FoundOffsets,obj.DetectedColumnIndicies] = ...
|
||||||
|
getPacketOffsets(obj.DecisionStatistic,symbolLength, ...
|
||||||
|
lenLSTF,obj.Threshold);
|
||||||
|
|
||||||
|
obj.UpdatePacketOffsets = false;
|
||||||
|
end
|
||||||
|
|
||||||
|
idx = find(obj.FoundOffsets >= obj.SearchOffset,1);
|
||||||
|
startOffset = obj.FoundOffsets(idx);
|
||||||
|
|
||||||
|
if isempty(idx)
|
||||||
|
endIdx = size(obj.DecisionStatistic,2);
|
||||||
|
else
|
||||||
|
endIdx = obj.DetectedColumnIndicies(idx);
|
||||||
|
end
|
||||||
|
|
||||||
|
M = double([reshape(obj.DecisionStatistic(1:lenHalfLSTF,1:endIdx-1),[],1); ...
|
||||||
|
reshape(obj.DecisionStatistic(:,endIdx),[],1)]);
|
||||||
|
end % function findPacketStart
|
||||||
|
|
||||||
|
% Setters
|
||||||
|
function set.Waveform(obj,value)
|
||||||
|
if ~isequal(obj.Waveform,value)
|
||||||
|
obj.Waveform = value;
|
||||||
|
obj.UpdateDecisionStatistic = true; %#ok<*MCSUP>
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set.ChannelBandwidth(obj,value)
|
||||||
|
if ~strcmpi(obj.ChannelBandwidth,value)
|
||||||
|
obj.ChannelBandwidth = wlan.internal.validateParam('NONHTEHTCHANBW', value, mfilename);
|
||||||
|
obj.UpdateDecisionStatistic = true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set.OversamplingFactor(obj,value)
|
||||||
|
if ~isequal(obj.OversamplingFactor,value)
|
||||||
|
obj.OversamplingFactor = value;
|
||||||
|
obj.UpdateDecisionStatistic = true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set.Threshold(obj,value)
|
||||||
|
if ~isequal(obj.Threshold,value)
|
||||||
|
obj.Threshold = value;
|
||||||
|
obj.UpdatePacketOffsets = true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set.SearchOffset(obj,value)
|
||||||
|
coder.internal.errorIf(~isempty(obj.Waveform) && value>size(obj.Waveform, 1)-1, 'wlan:shared:InvalidOffsetValue')
|
||||||
|
obj.SearchOffset = value;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function [packetStarts,colIdxs] = getPacketOffsets(mn,symbolLength,lenLSTF,threshold)
|
||||||
|
% mn: Decision statistic
|
||||||
|
% threshold: Decision threshold as specified by user
|
||||||
|
N = mn > threshold;
|
||||||
|
colDesc = sum(N) >= symbolLength*1.5;
|
||||||
|
N(:,~colDesc) = false;
|
||||||
|
colIdxs = find(colDesc);
|
||||||
|
|
||||||
|
% Create a matrix of indicies where each column has the value 1:corrLen
|
||||||
|
% then extract indices based on N and desc and calculate all possible
|
||||||
|
% packet start locations
|
||||||
|
corrLen = lenLSTF - (symbolLength*2) + 1;
|
||||||
|
idxs = repmat((1:corrLen)',1,size(N,2));
|
||||||
|
idxs(~N) = NaN;
|
||||||
|
idxs = idxs(:,colDesc);
|
||||||
|
packetStarts = min(idxs) + (colIdxs-1)*lenLSTF/2 - 1;
|
||||||
|
|
||||||
|
% Check relative distances between peaks for all detected packets
|
||||||
|
if ~isempty(packetStarts)
|
||||||
|
packetStarts = arrayfun(@(x)checkRelativeDist(packetStarts(x),idxs(:,x),symbolLength),1:length(packetStarts));
|
||||||
|
end
|
||||||
|
|
||||||
|
% Extract non-NaN values
|
||||||
|
colIdxs = colIdxs(~isnan(packetStarts));
|
||||||
|
packetStarts = packetStarts(~isnan(packetStarts));
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function pS = checkRelativeDist(pS,idxs,symbolLength)
|
||||||
|
% Check the relative distance between peaks relative to the first peak. If
|
||||||
|
% this exceed three times the symbol length then the packet is not
|
||||||
|
% detected.
|
||||||
|
nonan = idxs(~isnan(idxs));
|
||||||
|
if any(nonan(2:symbolLength) - nonan(1)>symbolLength*3)
|
||||||
|
pS = NaN;
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
function [decBits,decParams,searchOffset,res] = recoverOFDMBits(rx,searchOffset)
|
||||||
|
%recoverOFDMBits Performs non-HT OFDM signal recovery
|
||||||
|
% [DECBITS,DECPARAMS,SEARCHOFFSET,RES] = recoverOFDMBits(RX,SEARCHOFFSET)
|
||||||
|
% detects a OFDM packet and performs analysis of the non-HT preamble,
|
||||||
|
% L-SIG, data fields.
|
||||||
|
%
|
||||||
|
% DECBITS is a vector containing the decoded bits in a non-HT packet.
|
||||||
|
%
|
||||||
|
% DECPARAMS is a structure containing the decoded signal parameters.
|
||||||
|
%
|
||||||
|
% SEARCHOFFSET is the offset from the start of RX in samples to the
|
||||||
|
% next point from which to search for a packet.
|
||||||
|
%
|
||||||
|
% 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.
|
||||||
|
|
||||||
|
% Copyright 2024 The MathWorks, Inc.
|
||||||
|
|
||||||
|
% recoverPreamble detects a packet and performs analysis of the non-HT preamble.
|
||||||
|
decBits = [];
|
||||||
|
|
||||||
|
decParams = struct;
|
||||||
|
decParams.modulation = nan;
|
||||||
|
decParams.codeRate = nan;
|
||||||
|
decParams.MCS = nan;
|
||||||
|
decParams.PSDULength = nan;
|
||||||
|
decParams.failCheck = nan;
|
||||||
|
|
||||||
|
cbw = "CBW20";
|
||||||
|
cfg = wlanNonHTConfig(ChannelBandwidth=cbw);
|
||||||
|
ind = wlanFieldIndices(cfg);
|
||||||
|
sampleRate = 20e6;
|
||||||
|
maxNonHTPacketTime = 5.484e-3;
|
||||||
|
maxNonHTPacketSamples = maxNonHTPacketTime*sampleRate;
|
||||||
|
|
||||||
|
[preambleStatus,res] = recoverPreamble(rx,cbw,searchOffset);
|
||||||
|
|
||||||
|
if matches(preambleStatus,"No packet detected")
|
||||||
|
searchOffset = length(rx);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Retrieve synchronized data and scale it with LSTF power as done
|
||||||
|
% in the recoverPreamble function.
|
||||||
|
if maxNonHTPacketSamples <= (length(rx) - res.PacketOffset)
|
||||||
|
endIdx = maxNonHTPacketSamples + res.PacketOffset;
|
||||||
|
else
|
||||||
|
endIdx = length(rx);
|
||||||
|
end
|
||||||
|
syncData = rx(res.PacketOffset+1:endIdx)./sqrt(res.LSTFPower);
|
||||||
|
syncData = frequencyOffset(syncData,sampleRate,-res.CFOEstimate);
|
||||||
|
|
||||||
|
% Need only 4 OFDM symbols (LSIG + 3 more symbols) following LLTF
|
||||||
|
% for format detection
|
||||||
|
fmtDetect = syncData(ind.LSIG(1):(ind.LSIG(2)+4e-6*sampleRate*3));
|
||||||
|
|
||||||
|
[LSIGBits, failcheck] = wlanLSIGRecover(fmtDetect(1:4e-6*sampleRate*1), ...
|
||||||
|
res.ChanEstNonHT,res.NoiseEstNonHT,cbw);
|
||||||
|
|
||||||
|
decParams.failCheck = failcheck;
|
||||||
|
|
||||||
|
if ~failcheck
|
||||||
|
format = wlanFormatDetect(fmtDetect,res.ChanEstNonHT,res.NoiseEstNonHT,cbw);
|
||||||
|
if matches(format,"Non-HT")
|
||||||
|
|
||||||
|
% Extract MCS from first 3 bits of L-SIG.
|
||||||
|
rate = double(bit2int(LSIGBits(1:3),3));
|
||||||
|
if rate <= 1
|
||||||
|
cfg.MCS = rate + 6;
|
||||||
|
else
|
||||||
|
cfg.MCS = mod(rate,6);
|
||||||
|
end
|
||||||
|
[modulation,coderate] = getRateInfo(cfg.MCS);
|
||||||
|
decParams.modulation = modulation;
|
||||||
|
decParams.coderate = coderate;
|
||||||
|
decParams.MCS = cfg.MCS;
|
||||||
|
|
||||||
|
% Determine PSDU length from L-SIG.
|
||||||
|
cfg.PSDULength = double(bit2int(LSIGBits(6:17),12,0));
|
||||||
|
decParams.PSDULength = cfg.PSDULength;
|
||||||
|
ind.NonHTData = wlanFieldIndices(cfg,"NonHT-Data");
|
||||||
|
|
||||||
|
if double(ind.NonHTData(2)-ind.NonHTData(1))> ...
|
||||||
|
length(syncData(ind.NonHTData(1):end))
|
||||||
|
% Exit function as full packet not captured.
|
||||||
|
searchOffset = length(rx);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
nonHTData = syncData(ind.NonHTData(1):ind.NonHTData(2));
|
||||||
|
decBits = wlanNonHTDataRecover(nonHTData,res.ChanEstNonHT, ...
|
||||||
|
res.NoiseEstNonHT,cfg);
|
||||||
|
% Shift packet search offset for next iteration of while loop.
|
||||||
|
searchOffset = res.PacketOffset + double(ind.NonHTData(2));
|
||||||
|
else
|
||||||
|
% Packet is NOT non-HT; shift packet search offset by 10 OFDM symbols (minimum
|
||||||
|
% packet length of non-HT) for next iteration of while loop.
|
||||||
|
searchOffset = res.PacketOffset + 4e-6*sampleRate*10;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
% L-SIG recovery failed; shift packet search offset by 10 OFDM symbols (minimum
|
||||||
|
% packet length of non-HT) for next iteration of while loop.
|
||||||
|
searchOffset = res.PacketOffset + 4e-6*sampleRate*10;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function [modulation,coderate] = getRateInfo(mcs)
|
||||||
|
% GETRATEINFO returns the modulation scheme as a character array and the
|
||||||
|
% code rate of a packet given a scalar integer representing the modulation
|
||||||
|
% coding scheme
|
||||||
|
switch mcs
|
||||||
|
case 0 % BPSK
|
||||||
|
modulation = 'BPSK';
|
||||||
|
coderate = '1/2';
|
||||||
|
case 1 % BPSK
|
||||||
|
modulation = 'BPSK';
|
||||||
|
coderate = '3/4';
|
||||||
|
case 2 % QPSK
|
||||||
|
modulation = 'QPSK';
|
||||||
|
coderate = '1/2';
|
||||||
|
case 3 % QPSK
|
||||||
|
modulation = 'QPSK';
|
||||||
|
coderate = '3/4';
|
||||||
|
case 4 % 16QAM
|
||||||
|
modulation = '16QAM';
|
||||||
|
coderate = '1/2';
|
||||||
|
case 5 % 16QAM
|
||||||
|
modulation = '16QAM';
|
||||||
|
coderate = '3/4';
|
||||||
|
case 6 % 64QAM
|
||||||
|
modulation = '64QAM';
|
||||||
|
coderate = '2/3';
|
||||||
|
otherwise % 64QAM
|
||||||
|
modulation = '64QAM';
|
||||||
|
coderate = '3/4';
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
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)<threshold)
|
||||||
|
% If energy detected is not high enough continue searching
|
||||||
|
wpd.SearchOffset = packetOffset+minAdvLen;
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% Coarse Frequency Offset Estimation
|
||||||
|
% Extract non-HT fields and perform coarse frequency offset correction
|
||||||
|
% to allow for reliable symbol timing
|
||||||
|
preamble = rx(packetOffset+(index.LSTF(1):index.LSIG(2)),:);
|
||||||
|
coarseFreqOffset = wlanCoarseCFOEstimate(preamble,chanBW);
|
||||||
|
preamble = frequencyOffset(preamble,sr,-coarseFreqOffset);
|
||||||
|
|
||||||
|
% Timing Synchronization
|
||||||
|
% Symbol timing synchronization: 4 OFDM symbols to search for L-LTF
|
||||||
|
if cfgAlg.SkipPacketDetection
|
||||||
|
lltfStartOffset = 0;
|
||||||
|
else
|
||||||
|
lltfStartOffset = wlanSymbolTimingEstimate(preamble,chanBW);
|
||||||
|
end
|
||||||
|
|
||||||
|
% If packet offset is significantly less than search offset then
|
||||||
|
% likely a false detection
|
||||||
|
if (packetOffset+lltfStartOffset)<=(wpd.SearchOffset-minAdvLen)
|
||||||
|
% Skip 4/10 of L-STF length of samples and continue searching
|
||||||
|
wpd.SearchOffset = packetOffset+minAdvLen;
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
% End search if min packet length is outside of waveform
|
||||||
|
packetOffset = packetOffset+lltfStartOffset;
|
||||||
|
if (packetOffset+minPktLen)>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) || lltfSNREst<cfgAlg.LLTFSNRDetectionThreshold
|
||||||
|
% Skip L-STF length of samples and continue searching
|
||||||
|
wpd.SearchOffset = packetOffset+minAdvLen;
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% Packet detected
|
||||||
|
status = 'Success';
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if strcmp(status,'No packet detected')
|
||||||
|
res = defaultResults();
|
||||||
|
wpd = [];
|
||||||
|
else
|
||||||
|
res = struct;
|
||||||
|
res.PacketOffset = packetOffset;
|
||||||
|
res.CFOEstimate = cfoEstimate;
|
||||||
|
res.LSTFPower = lstfPower;
|
||||||
|
res.LLTFPower = lltfPower;
|
||||||
|
res.ChanEstNonHT = chanEstNonHT;
|
||||||
|
res.NoiseEstNonHT = noiseEstNonHT;
|
||||||
|
res.LLTFSNR = lltfSNREst;
|
||||||
|
res.DemodLLTF = demodLLTF;
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function res = defaultResults()
|
||||||
|
res = struct;
|
||||||
|
res.PacketOffset = nan;
|
||||||
|
res.CFOEstimate = nan;
|
||||||
|
res.LSTFPower = nan;
|
||||||
|
res.LLTFPower = nan;
|
||||||
|
res.ChanEstNonHT = nan;
|
||||||
|
res.NoiseEstNonHT = nan;
|
||||||
|
res.LLTFSNR = nan;
|
||||||
|
res.DemodLLTF = nan;
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfg = algorithmConfig(varargin)
|
||||||
|
|
||||||
|
if nargin>0
|
||||||
|
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
|
||||||
@@ -1 +1,4 @@
|
|||||||
|
calculations_only = true;
|
||||||
|
|
||||||
task1;
|
task1;
|
||||||
|
task2;
|
||||||
-116
@@ -1,116 +0,0 @@
|
|||||||
trace1 = load("traces/2412mhz.mat", "iq");
|
|
||||||
trace2 = load("traces/2432mhz.mat", "iq");
|
|
||||||
trace3 = load("traces/2452mhz.mat", "iq");
|
|
||||||
trace4 = load("traces/2472mhz.mat", "iq");
|
|
||||||
|
|
||||||
window = 512;
|
|
||||||
overlap = 64;
|
|
||||||
fft_precision = 2048;
|
|
||||||
|
|
||||||
sample_rate = 20e6; % 20 MS/s
|
|
||||||
|
|
||||||
% 1.
|
|
||||||
duration1 = length(trace1.iq) / sample_rate;
|
|
||||||
duration2 = length(trace2.iq) / sample_rate;
|
|
||||||
duration3 = length(trace3.iq) / sample_rate;
|
|
||||||
duration4 = length(trace4.iq) / sample_rate;
|
|
||||||
|
|
||||||
% 2.
|
|
||||||
% get all four plot values
|
|
||||||
[~,F1,T1,P1] = spectrogram(trace1.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
|
||||||
[~,F2,T2,P2] = spectrogram(trace2.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
|
||||||
[~,F3,T3,P3] = spectrogram(trace3.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
|
||||||
[~,F4,T4,P4] = spectrogram(trace4.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
|
||||||
|
|
||||||
% begin calibrated color scale
|
|
||||||
P1_dB = 10 * log10(P1);
|
|
||||||
P2_dB = 10 * log10(P2);
|
|
||||||
P3_dB = 10 * log10(P3);
|
|
||||||
P4_dB = 10 * log10(P4);
|
|
||||||
|
|
||||||
all_P_dB = [P1_dB(:); P2_dB(:); P3_dB(:); P4_dB(:)];
|
|
||||||
|
|
||||||
p_dB_min = min(all_P_dB) - 20;
|
|
||||||
p_dB_max = max(all_P_dB) + 20;
|
|
||||||
% end power calibrated color scale
|
|
||||||
|
|
||||||
% plot all figures like example shows
|
|
||||||
figure(1);
|
|
||||||
imagesc(T1, F1, P1_dB);
|
|
||||||
axis ij;
|
|
||||||
xlabel("Time [s]");
|
|
||||||
ylabel("Freq. [Hz]");
|
|
||||||
title("Spectrogram Trace 1");
|
|
||||||
cb = colorbar;
|
|
||||||
ylabel(cb, "Power [dB]");
|
|
||||||
clim([p_dB_min p_dB_max]);
|
|
||||||
xlim([0 0.05]);
|
|
||||||
|
|
||||||
figure(2);
|
|
||||||
imagesc(T2, F2, P2_dB);
|
|
||||||
axis ij;
|
|
||||||
xlabel("Time [s]");
|
|
||||||
ylabel("Freq. [Hz]");
|
|
||||||
title("Spectrogram Trace 2");
|
|
||||||
cb = colorbar;
|
|
||||||
ylabel(cb, "Power [dB]");
|
|
||||||
clim([p_dB_min p_dB_max]);
|
|
||||||
xlim([0 0.05]);
|
|
||||||
|
|
||||||
figure(3);
|
|
||||||
imagesc(T3, F3, P3_dB);
|
|
||||||
axis ij;
|
|
||||||
xlabel("Time [s]");
|
|
||||||
ylabel("Freq. [Hz]");
|
|
||||||
title("Spectrogram Trace 3");
|
|
||||||
cb = colorbar;
|
|
||||||
ylabel(cb, "Power [dB]");
|
|
||||||
clim([p_dB_min p_dB_max]);
|
|
||||||
xlim([0 0.05]);
|
|
||||||
|
|
||||||
figure(4);
|
|
||||||
imagesc(T4, F4, P4_dB);
|
|
||||||
axis ij;
|
|
||||||
xlabel("Time [s]");
|
|
||||||
ylabel("Freq. [Hz]");
|
|
||||||
title("Spectrogram Trace 4");
|
|
||||||
cb = colorbar;
|
|
||||||
ylabel(cb, "Power [dB]");
|
|
||||||
clim([p_dB_min p_dB_max]);
|
|
||||||
xlim([0 0.05]);
|
|
||||||
|
|
||||||
% 3.
|
|
||||||
noise_floor1 = prctile(P1_dB(:), 10);
|
|
||||||
noise_floor2 = prctile(P2_dB(:), 10);
|
|
||||||
noise_floor3 = prctile(P3_dB(:), 10);
|
|
||||||
noise_floor4 = prctile(P4_dB(:), 10);
|
|
||||||
|
|
||||||
% 4.
|
|
||||||
occupancy1 = sum(P1_dB(:) > noise_floor1 + 10) / numel(P1_dB) * 100;
|
|
||||||
occupancy2 = sum(P2_dB(:) > noise_floor2 + 10) / numel(P2_dB) * 100;
|
|
||||||
occupancy3 = sum(P3_dB(:) > noise_floor3 + 10) / numel(P3_dB) * 100;
|
|
||||||
occupancy4 = sum(P4_dB(:) > noise_floor4 + 10) / numel(P4_dB) * 100;
|
|
||||||
|
|
||||||
occupancies = [occupancy1 occupancy2 occupancy3 occupancy4];
|
|
||||||
channels = [1 5 9 13];
|
|
||||||
|
|
||||||
[occupancy_max, max_idx] = max(occupancies);
|
|
||||||
busiest_channel = channels(max_idx);
|
|
||||||
|
|
||||||
disp("Results:" + newline)
|
|
||||||
|
|
||||||
disp("-- Trace duration ---")
|
|
||||||
disp("Channel 1 duration: " + duration1 + "s")
|
|
||||||
disp("Channel 5 duration: " + duration2 + "s")
|
|
||||||
disp("Channel 9 duration: " + duration3 + "s")
|
|
||||||
disp("Channel 13 duration: " + duration4 + "s" + newline)
|
|
||||||
|
|
||||||
disp("--- Noise floor ---")
|
|
||||||
disp("Destinct:")
|
|
||||||
disp("Channel 1 noise floor: " + noise_floor1 + "dB")
|
|
||||||
disp("Channel 5 noise floor: " + noise_floor2 + "dB")
|
|
||||||
disp("Channel 9 noise floor: " + noise_floor3 + "dB")
|
|
||||||
disp("Channel 13 noise floor: " + noise_floor4 + "dB" )
|
|
||||||
disp(newline)
|
|
||||||
disp("Combined:")
|
|
||||||
disp("Channel all noise floor: " + sum([noise_floor1 noise_floor2, noise_floor3, noise_floor4]) / 4 + "dB")
|
|
||||||
+72
-67
@@ -17,10 +17,10 @@ duration4 = length(trace4.iq) / sample_rate;
|
|||||||
|
|
||||||
% 2.
|
% 2.
|
||||||
% get all four plot values
|
% get all four plot values
|
||||||
[~,F1,T1,P1] = spectrogram(trace1.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
[~,F1,T1,P1] = spectrogram(trace1.iq, window, overlap, fft_precision, sample_rate, "centered", "power");
|
||||||
[~,F2,T2,P2] = spectrogram(trace2.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
[~,F2,T2,P2] = spectrogram(trace2.iq, window, overlap, fft_precision, sample_rate, "centered", "power");
|
||||||
[~,F3,T3,P3] = spectrogram(trace3.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
[~,F3,T3,P3] = spectrogram(trace3.iq, window, overlap, fft_precision, sample_rate, "centered", "power");
|
||||||
[~,F4,T4,P4] = spectrogram(trace4.iq, window, overlap, fft_precision, sample_rate, "centered", "psd");
|
[~,F4,T4,P4] = spectrogram(trace4.iq, window, overlap, fft_precision, sample_rate, "centered", "power");
|
||||||
|
|
||||||
% begin calibrated color scale
|
% begin calibrated color scale
|
||||||
P1_dB = 10 * log10(P1);
|
P1_dB = 10 * log10(P1);
|
||||||
@@ -30,54 +30,57 @@ P4_dB = 10 * log10(P4);
|
|||||||
|
|
||||||
all_P_dB = [P1_dB(:); P2_dB(:); P3_dB(:); P4_dB(:)];
|
all_P_dB = [P1_dB(:); P2_dB(:); P3_dB(:); P4_dB(:)];
|
||||||
|
|
||||||
p_dB_min = min(all_P_dB) - 20;
|
% -50 to +50 so its like the example fig
|
||||||
p_dB_max = max(all_P_dB) + 20;
|
p_dB_min = min(all_P_dB) - 50;
|
||||||
|
p_dB_max = max(all_P_dB) + 50;
|
||||||
% end power calibrated color scale
|
% end power calibrated color scale
|
||||||
|
|
||||||
% plot all figures like example shows
|
if ~calculations_only
|
||||||
figure(1);
|
% plot all figures like example shows
|
||||||
imagesc(T1, F1, P1_dB);
|
figure(1);
|
||||||
axis ij;
|
imagesc(T1, F1, P1_dB);
|
||||||
xlabel("Time [s]");
|
axis ij;
|
||||||
ylabel("Freq. [Hz]");
|
xlabel("Time [s]");
|
||||||
title("Spectrogram Trace 1");
|
ylabel("Freq. [Hz]");
|
||||||
cb = colorbar;
|
title("Spectrogram Trace 1");
|
||||||
ylabel(cb, "Power [dB]");
|
cb = colorbar;
|
||||||
clim([p_dB_min p_dB_max]);
|
ylabel(cb, "Power [dB]");
|
||||||
xlim([0 0.05]);
|
clim([p_dB_min p_dB_max]);
|
||||||
|
xlim([0 0.05]);
|
||||||
|
|
||||||
figure(2);
|
figure(2);
|
||||||
imagesc(T2, F2, P2_dB);
|
imagesc(T2, F2, P2_dB);
|
||||||
axis ij;
|
axis ij;
|
||||||
xlabel("Time [s]");
|
xlabel("Time [s]");
|
||||||
ylabel("Freq. [Hz]");
|
ylabel("Freq. [Hz]");
|
||||||
title("Spectrogram Trace 2");
|
title("Spectrogram Trace 2");
|
||||||
cb = colorbar;
|
cb = colorbar;
|
||||||
ylabel(cb, "Power [dB]");
|
ylabel(cb, "Power [dB]");
|
||||||
clim([p_dB_min p_dB_max]);
|
clim([p_dB_min p_dB_max]);
|
||||||
xlim([0 0.05]);
|
xlim([0 0.05]);
|
||||||
|
|
||||||
figure(3);
|
figure(3);
|
||||||
imagesc(T3, F3, P3_dB);
|
imagesc(T3, F3, P3_dB);
|
||||||
axis ij;
|
axis ij;
|
||||||
xlabel("Time [s]");
|
xlabel("Time [s]");
|
||||||
ylabel("Freq. [Hz]");
|
ylabel("Freq. [Hz]");
|
||||||
title("Spectrogram Trace 3");
|
title("Spectrogram Trace 3");
|
||||||
cb = colorbar;
|
cb = colorbar;
|
||||||
ylabel(cb, "Power [dB]");
|
ylabel(cb, "Power [dB]");
|
||||||
clim([p_dB_min p_dB_max]);
|
clim([p_dB_min p_dB_max]);
|
||||||
xlim([0 0.05]);
|
xlim([0 0.05]);
|
||||||
|
|
||||||
figure(4);
|
figure(4);
|
||||||
imagesc(T4, F4, P4_dB);
|
imagesc(T4, F4, P4_dB);
|
||||||
axis ij;
|
axis ij; % flip so its like the example fig
|
||||||
xlabel("Time [s]");
|
xlabel("Time [s]");
|
||||||
ylabel("Freq. [Hz]");
|
ylabel("Freq. [Hz]");
|
||||||
title("Spectrogram Trace 4");
|
title("Spectrogram Trace 4");
|
||||||
cb = colorbar;
|
cb = colorbar;
|
||||||
ylabel(cb, "Power [dB]");
|
ylabel(cb, "Power [dB]");
|
||||||
clim([p_dB_min p_dB_max]);
|
clim([p_dB_min p_dB_max]);
|
||||||
xlim([0 0.05]);
|
xlim([0 0.05]); % reduce view so its like the example fig
|
||||||
|
end
|
||||||
|
|
||||||
% 3.
|
% 3.
|
||||||
noise_floor1 = prctile(P1_dB(:), 10);
|
noise_floor1 = prctile(P1_dB(:), 10);
|
||||||
@@ -99,28 +102,30 @@ channels = [1 5 9 13];
|
|||||||
[occupancy_max, max_idx] = max(occupancies);
|
[occupancy_max, max_idx] = max(occupancies);
|
||||||
busiest_channel = channels(max_idx);
|
busiest_channel = channels(max_idx);
|
||||||
|
|
||||||
disp("Results:" + newline)
|
if ~calculations_only
|
||||||
|
disp("Results Task 1:" + newline)
|
||||||
|
|
||||||
disp("-- Trace duration ---")
|
disp("-- Trace duration ---")
|
||||||
disp("Channel 1 duration: " + duration1 + "s")
|
disp("Channel 1 duration: " + duration1 + "s")
|
||||||
disp("Channel 5 duration: " + duration2 + "s")
|
disp("Channel 5 duration: " + duration2 + "s")
|
||||||
disp("Channel 9 duration: " + duration3 + "s")
|
disp("Channel 9 duration: " + duration3 + "s")
|
||||||
disp("Channel 13 duration: " + duration4 + "s" + newline)
|
disp("Channel 13 duration: " + duration4 + "s" + newline)
|
||||||
|
|
||||||
disp("--- Noise floor ---")
|
disp("--- Noise floor ---")
|
||||||
disp("Distinct:")
|
disp("Distinct:")
|
||||||
disp("Channel 1 noise floor: " + noise_floor1 + "dB")
|
disp("Channel 1 noise floor: " + noise_floor1 + "dB")
|
||||||
disp("Channel 5 noise floor: " + noise_floor2 + "dB")
|
disp("Channel 5 noise floor: " + noise_floor2 + "dB")
|
||||||
disp("Channel 9 noise floor: " + noise_floor3 + "dB")
|
disp("Channel 9 noise floor: " + noise_floor3 + "dB")
|
||||||
disp("Channel 13 noise floor: " + noise_floor4 + "dB" + newline)
|
disp("Channel 13 noise floor: " + noise_floor4 + "dB" + newline)
|
||||||
|
|
||||||
disp("Combined:")
|
disp("Combined:")
|
||||||
disp("Channel all noise floor: " + noise_floor_avg + "dB" + newline)
|
disp("Channel all noise floor: " + noise_floor_avg + "dB" + newline)
|
||||||
|
|
||||||
disp("-- Occupancy ---")
|
disp("-- Occupancy ---")
|
||||||
disp("Channel 1 occupancy: " + occupancy1)
|
disp("Channel 1 occupancy: " + occupancy1 + "%")
|
||||||
disp("Channel 5 occupancy: " + occupancy2)
|
disp("Channel 5 occupancy: " + occupancy2 + "%")
|
||||||
disp("Channel 9 occupancy: " + occupancy3)
|
disp("Channel 9 occupancy: " + occupancy3 + "%")
|
||||||
disp("Channel 13 occupancy: " + occupancy4 + newline)
|
disp("Channel 13 occupancy: " + occupancy4 + "%" + newline)
|
||||||
|
|
||||||
disp("Busiest channel: " + busiest_channel)
|
disp("Busiest channel: " + busiest_channel)
|
||||||
|
end
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
channel_bandwidth = "CBW20"; % 20MHz
|
||||||
|
|
||||||
|
rxFrame1 = trace1.iq(:);
|
||||||
|
rxFrame2 = trace2.iq(:);
|
||||||
|
rxFrame3 = trace3.iq(:);
|
||||||
|
rxFrame4 = trace4.iq(:);
|
||||||
|
|
||||||
|
function beaconFrames = extractBeaconFrames(rxFrame)
|
||||||
|
beaconFrames = [];
|
||||||
|
searchOffset = 0;
|
||||||
|
|
||||||
|
|
||||||
|
while searchOffset < length(rxFrame)
|
||||||
|
[bitsData, decParams, searchOffset, res] = recoverOFDMBits(rxFrame, searchOffset);
|
||||||
|
|
||||||
|
if isempty(bitsData)
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
|
||||||
|
[cfgMAC, ~, decodeStatus] = wlanMPDUDecode(bitsData, SuppressWarnings=true);
|
||||||
|
|
||||||
|
if ~decodeStatus && matches(cfgMAC.FrameType, "Beacon")
|
||||||
|
disp("Beacon at " + searchOffset);
|
||||||
|
beaconFrames(end + 1) = cfgMAC;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
beaconFrames1 = extractBeaconFrames(rxFrame1);
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
addpath("from_matlab_example");
|
||||||
|
|
||||||
|
rxFrame1 = trace1.iq(:);
|
||||||
|
rxFrame2 = trace2.iq(:);
|
||||||
|
rxFrame3 = trace3.iq(:);
|
||||||
|
rxFrame4 = trace4.iq(:);
|
||||||
|
|
||||||
|
beaconFrames1 = extractBeaconFrames(rxFrame1);
|
||||||
|
beaconFrames2 = extractBeaconFrames(rxFrame2);
|
||||||
|
beaconFrames3 = extractBeaconFrames(rxFrame3);
|
||||||
|
beaconFrames4 = extractBeaconFrames(rxFrame4);
|
||||||
|
|
||||||
|
cfgMAC = beaconFrames1(1).MAC_Config;
|
||||||
|
mgmt = cfgMAC.ManagementConfig;
|
||||||
|
|
||||||
|
disp(mgmt)
|
||||||
|
properties(mgmt)
|
||||||
|
|
||||||
|
printBeaconTable(beaconFrames1);
|
||||||
|
printBeaconTable(beaconFrames2);
|
||||||
|
printBeaconTable(beaconFrames3);
|
||||||
|
printBeaconTable(beaconFrames4);
|
||||||
|
|
||||||
|
function beaconFrames = extractBeaconFrames(rxFrame)
|
||||||
|
beaconFrames = struct( ...
|
||||||
|
"SSID", {}, ...
|
||||||
|
"BSSID", {}, ...
|
||||||
|
"Offset", {}, ...
|
||||||
|
"MAC_Config", {}, ...
|
||||||
|
"Bits", {}, ...
|
||||||
|
"SNR_dB", {} ...
|
||||||
|
);
|
||||||
|
|
||||||
|
channel_bandwidth = "CBW20"; % 20MHz
|
||||||
|
|
||||||
|
searchOffset = 0;
|
||||||
|
index = 1;
|
||||||
|
|
||||||
|
while searchOffset < length(rxFrame)
|
||||||
|
oldOffset = searchOffset;
|
||||||
|
[bitsData, decParams, searchOffset, res] = recoverOFDMBits(rxFrame, searchOffset);
|
||||||
|
|
||||||
|
if searchOffset <= oldOffset
|
||||||
|
searchOffset = oldOffset + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
if isempty(bitsData)
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
|
||||||
|
[cfgMAC, ~, decodeStatus] = wlanMPDUDecode(bitsData, SuppressWarnings=true);
|
||||||
|
|
||||||
|
if ~decodeStatus && matches(cfgMAC.FrameType, "Beacon")
|
||||||
|
if isempty(cfgMAC.ManagementConfig.SSID)
|
||||||
|
ssid = "Hidden";
|
||||||
|
else
|
||||||
|
ssid = string(cfgMAC.ManagementConfig.SSID);
|
||||||
|
end
|
||||||
|
|
||||||
|
if isfield(res, "PacketOffset")
|
||||||
|
beaconFrames(index).Offset = res.PacketOffset;
|
||||||
|
else
|
||||||
|
beaconFrames(index).Offset = oldOffset;
|
||||||
|
end
|
||||||
|
|
||||||
|
if isfield(res, "LLTFSNR")
|
||||||
|
beaconFrames(index).SNR_dB = res.LLTFSNR;
|
||||||
|
else
|
||||||
|
beaconFrames(index).SNR_dB = NaN;
|
||||||
|
end
|
||||||
|
|
||||||
|
beaconFrames(index).SSID = ssid;
|
||||||
|
beaconFrames(index).BSSID = string(cfgMAC.Address3);
|
||||||
|
beaconFrames(index).MAC_Config = cfgMAC;
|
||||||
|
beaconFrames(index).Bits = bitsData;
|
||||||
|
|
||||||
|
index = index + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function printBeaconTable(beaconFrames)
|
||||||
|
fprintf("\n");
|
||||||
|
fprintf("%-20s %-18s %-10s %-8s %-8s %-8s %-12s %-8s %-20s\n", ...
|
||||||
|
"SSID", "BSSID", "Interval", "B. ch.", "Op. ch.", "SNR", "Vendor", "Std.", "Rates");
|
||||||
|
|
||||||
|
for i = 1:numel(beaconFrames)
|
||||||
|
ssid = string(beaconFrames(i).SSID);
|
||||||
|
bssid = string(beaconFrames(i).BSSID);
|
||||||
|
interval = string(beaconFrames(i).MAC_Config.ManagementConfig.BeaconInterval);
|
||||||
|
snr = sprintf("%.1f", beaconFrames(i).SNR_dB);
|
||||||
|
|
||||||
|
bch = "-";
|
||||||
|
opch = "-";
|
||||||
|
vendor = "-";
|
||||||
|
std = "-";
|
||||||
|
rates = "-";
|
||||||
|
|
||||||
|
fprintf("%-20s %-18s %-10s %-8s %-8s %-8s %-12s %-8s %-20s\n", ...
|
||||||
|
ssid, bssid, interval, bch, opch, snr, vendor, std, rates);
|
||||||
|
end
|
||||||
|
|
||||||
|
fprintf("\n");
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user