ofdm beacon receiver using software-凯发k8网页登录
this example shows how to retrieve information about wifi networks on the 5 ghz band using a software-defined radio (sdr). the example scans over the 5 ghz beacon channels and captures waveforms for analysis within matlab®. the example then decodes the ofdm packets to determine which packets are access point (ap) beacons. the ap beacon information includes the service set identifier (ssid), media access control (mac) address (also known as the basic ssid, or bssid), ap channel bandwidth, and 802.11 standard used by the ap.
introduction
this example scans through a set of wifi channels in the 5 ghz band to detect ap beacons that are transmitted on 20 mhz subchannels.
the scanning procedure comprises these steps.
set the frequency band and channels for the sdr to capture.
capture a waveform for a set duration for each specified channel.
process the waveform in matlab by searching for beacon frames in the captured waveform and extracting relevant information from each successfully decoded beacon frame.
display key information about the detected aps.
this example supports these sdrs for capturing waveforms in the 5 ghz band.
adalm-pluto from the communications toolbox support package for analog devices® adalm-pluto radio
usrp™ e310/e312 from the communications toolbox support package for usrp™ embedded series radio
ad936x/fmcomms5 from the communications toolbox support package for xilinx® zynq®-based radio
usrp™ n200/n210/usrp2/n320/n321/b200/b210/x300/x310 from the communications toolbox support package for usrp™ radio
alternatively, if an sdr is not available for waveform capture, the example supports importing a file with a precaptured waveform.
example setup
before running the example, ensure that you have installed the appropriate support package for the sdr that you intend to use and that you have set up the hardware.
the receiveonsdr
field of the rxsim
structure determines whether the example receives a waveform off the air or imports a waveform from a mat file.
rxsim.receiveonsdr = false;
specify the file name of a precaptured waveform in the filename
variable. confirm that the mat file contains these variables: capturedwaveforms
, channels
, radiosamplerate
, and band
.
filename = "capturedbeacons.mat";
by default, the example processes waveforms that are stored in a mat file. if using an sdr to perform a live scan of the 5 ghz band, specify the sdr name, radio identifier, radio sample rate, channel numbers, and other parameters. the valid channel numbers for the 5 ghz band are between 1 and 200. however, the valid 20 mhz control channels for an ap are 32, 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, and 177.
if rxsim.receiveonsdr rxsim.sdrdevicename = "ad936x"; % sdr that is used for waveform reception rxsim.radioidentifier = '192.168.3.2'; % value used to identify radio, for example, ip address, usb port, or serial number 30ad2d5 rxsim.radiosamplerate = 20000000; % configured for 20e6 hz as this is beacon transmission bw rxsim.radiogain = 50; rxsim.frequencyband = 5; rxsim.channelnumbers = [32, 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177]; % default scans all 5 ghz beacon channels rxsim.receiveantenna = 1; % configure to work with only a single antenna rxsim.capturetime = milliseconds(100); % value expected to be of type duration % derived parameters rxsim.centerfrequencies = wlanchannelfrequency(rxsim.channelnumbers,rxsim.frequencyband); rxsim.numsamplestocapture = seconds(rxsim.capturetime)*rxsim.radiosamplerate; else rx = load(filename); rxsim.channelnumbers = rx.channels; rxsim.radiosamplerate = rx.radiosamplerate; rxsim.frequencyband = rx.band; % derived parameters rxsim.numsamplestocapture = size(rx.capturedwaveforms,1); end osf = rxsim.radiosamplerate/20e6;
set optional information to display
to determine the hardware manufacturer of the ap, select the retrievevendorinfo
box. selecting the retrievevendorinfo
box downloads the organizationally unique identifier (oui) csv file from the ieee® registration authority website for vendor ap identification.
retrievevendorinfo = true;
to display additional packet information such as payload size, code rate, and modulation for all successfully decoded non-ht packets, select the displayadditionalinfo
box.
displayadditionalinfo = false;
to display a spectrum and spectrogram for the captured waveform, select the displayscope
box.
displayscope = false;
scan 5 ghz channels
initialize sdr object
this example communicates with the radio hardware using the object pertaining to the selected radio.
create an sdr object by either calling sdrrx
or comm.sdrureceiver
. then, apply the parameters set in the rxsim
structure above to the properties of that object.
if rxsim.receiveonsdr if matches(rxsim.sdrdevicename, ["ad936x", "fmcomms5", "pluto", "e3xx"]) sdrreceiver = sdrrx( ... rxsim.sdrdevicename, ... basebandsamplerate=rxsim.radiosamplerate, ... gainsource="manual"); if matches(rxsim.sdrdevicename, ["ad936x", "fmcomms5", "e3xx"]) % when capturing a waveform, skip the fpga and have the data % sent straight to the host sdrreceiver.showadvancedproperties = true; sdrreceiver.bypassuserlogic = true; sdrreceiver.ipaddress = rxsim.radioidentifier; else sdrreceiver.radioid = rxsim.radioidentifier; end else % for the usrp sdrs sdrreceiver = comm.sdrureceiver( ... platform=rxsim.sdrdevicename, ... enableburstmode=true); % determine burst capture values % each frame will have 100 symbols sdrreceiver.samplesperframe = 4e-6*rxsim.radiosamplerate*100; sdrreceiver.numframesinburst = rxsim.numsamplestocapture/sdrreceiver.samplesperframe; [sdrreceiver.masterclockrate, sdrreceiver.decimationfactor] = ... hgetusrprateinformation(rxsim.sdrdevicename,rxsim.radiosamplerate); if matches(rxsim.sdrdevicename, ["b200", "b210"]) % change the serial number as needed for usrp b200/b210 sdrreceiver.serialnum = rxsim.radioidentifier; else sdrreceiver.ipaddress = rxsim.radioidentifier; end end sdrreceiver.gain = rxsim.radiogain; sdrreceiver.outputdatatype = "double"; sdrreceiver.channelmapping = rxsim.receiveantenna; end
receiver design
this diagram shows an overview of the receiver for scanning the selected channels and frequency band and recovering beacon information.
these steps provide further information on the diagram.
set the center frequency of the sdr, then initialize the capture of a waveform for a set duration.
determine and apply frequency and timing corrections on the waveform, then attempt to recover the legacy signal (l-sig) field bits.
check that the packet format is non-ht.
from the recovered l-sig, extract the modulation and coding scheme (mcs) and the length of the plcp service data unit (psdu). then recover the non-ht data and subsequently decode the mac protocol data unit (mpdu).
using the recovered mac frame configuration, check if the non-ht packet is a beacon.
recover the ssid, bssid, vendor of the ap, snr, primary 20 mhz channel, current channel center frequency index, supported channel width, frequency band, and wireless standard used by the ap.
check if the waveform contains another packet that you can decode.
begin packet capture and processing
create a structure (aps
) for storing this information for each successfully decoded beacon.
ssid
bssid
vendor of ap
signal-to-noise ratio (snr)
primary 20 mhz channel
current channel center frequency index
channel width
frequency band
operating mode supported by the ap
mac frame configuration
waveform in which the beacon exists
index value at which the non-ht beacon packet begins in the captured waveform
aps = struct(... "ssid",[],"bssid",[],"vendor",[],"snr_db",[],"beacon_channel",[], ... "operating_channel",[],"channel_width_mhz",[],"band",[],"mode",[], ... "mac_config",wlanmacframeconfig,"waveform",[],"offset",[]);
initialize a non-ht config object to use for processing incoming waveforms.
cbw = "cbw20";
cfg = wlannonhtconfig(channelbandwidth=cbw);
ind = wlanfieldindices(cfg);
indexap = 1;
begin scanning and decoding for specified channels.
for i = 1:length(rxsim.channelnumbers) fprintf("scanning channel %d on band %.1f.\n",rxsim.channelnumbers(i),rxsim.frequencyband); if rxsim.receiveonsdr sdrreceiver.centerfrequency = rxsim.centerfrequencies(i); captureddata = capturewaveform(sdrreceiver,rxsim.numsamplestocapture); else captureddata = rx.capturedwaveforms(:,i); end % display spectrum and spectrogram if displayscope %#ok<*unrch> scope = spectrumanalyzer(viewtype="spectrum-and-spectrogram",samplerate=rxsim.radiosamplerate,... timespansource="property",timespan=rxsim.numsamplestocapture/rxsim.radiosamplerate); scope(captureddata); end % resample the captured data to 20 mhz for beacon processing. if osf ~= 1 captureddata = resample(captureddata,20e6,rxsim.radiosamplerate); end searchoffset = 0; while searchoffset% recoverpreamble detects a packet and performs analysis of the non-ht preamble. [preamblestatus,res] = recoverpreamble(captureddata,cbw,searchoffset); if matches(preamblestatus,"no packet detected") break; end % retrieve synchronized data and scale it with lstf power as done % in the recoverpreamble function. syncdata = captureddata(res.packetoffset 1:end)./sqrt(res.lstfpower); syncdata = frequencyoffset(syncdata,rxsim.radiosamplerate/osf,-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*rxsim.radiosamplerate/osf*3)); [lsigbits, failcheck] = wlanlsigrecover(fmtdetect(1:4e-6*rxsim.radiosamplerate/osf*1), ... res.chanestnonht,res.noiseestnonht,cbw); 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 % determine psdu length from l-sig. cfg.psdulength = double(bit2int(lsigbits(6:17),12,0)); ind.nonhtdata = wlanfieldindices(cfg,"nonht-data"); if double(ind.nonhtdata(2)-ind.nonhtdata(1))> ... length(syncdata(ind.nonhtdata(1):end)) % exit while loop as full packet not captured. break; end nonhtdata = syncdata(ind.nonhtdata(1):ind.nonhtdata(2)); bitsdata = wlannonhtdatarecover(nonhtdata,res.chanestnonht, ... res.noiseestnonht,cfg); [cfgmac, ~, decodestatus] = wlanmpdudecode(bitsdata,cfg, ... suppresswarnings=true); % print additional information on all successfully packets if ~decodestatus && displayadditionalinfo payloadsize = floor(length(bitsdata)/8); [modulation,coderate] = getrateinfo(cfg.mcs); fprintf("payload size: %d | modulation: %s | code rate: %s \n",payloadsize,modulation,coderate); fprintf("type: %s | sub-type: %s",cfgmac.gettype,cfgmac.getsubtype); end % extract information about channel from the beacon. if ~decodestatus && matches(cfgmac.frametype,"beacon") % populate the table with information about the beacon. if isempty(cfgmac.managementconfig.ssid) aps(indexap).ssid = "hidden"; else aps(indexap).ssid = string(cfgmac.managementconfig.ssid); end fprintf("%s beacon detected on channel %d in band %.1f.\n",aps(indexap).ssid,rxsim.channelnumbers(i),rxsim.frequencyband); aps(indexap).bssid = string(cfgmac.address3); if retrievevendorinfo aps(indexap).vendor = determinevendor(cfgmac.address3); else aps(indexap).vendor = "skipped"; end [aps(indexap).mode, aps(indexap).channel_width_mhz, operatingchannel] = ... determinemode(cfgmac.managementconfig.informationelements); if isempty(operatingchannel) % default to scanning channel if operating channel % cannot be determined. operatingchannel = rxsim.channelnumbers(i); end aps(indexap).beacon_channel = rxsim.channelnumbers(i); aps(indexap).operating_channel = operatingchannel; aps(indexap).snr_db = res.lltfsnr; aps(indexap).mac_config = cfgmac; aps(indexap).offset = res.packetoffset; aps(indexap).waveform = captureddata; indexap = indexap 1; end % 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*rxsim.radiosamplerate/osf*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*rxsim.radiosamplerate/osf*10; end end end
scanning channel 52 on band 5.0.
wlan_5g beacon detected on channel 52 in band 5.0.
downloading oui.csv from ieee registration authority...
scanning channel 56 on band 5.0.
w-inside beacon detected on channel 56 in band 5.0. w-mobile beacon detected on channel 56 in band 5.0. w-guest beacon detected on channel 56 in band 5.0.
scanning channel 157 on band 5.0.
w-inside beacon detected on channel 157 in band 5.0. w-mobile beacon detected on channel 157 in band 5.0. w-guest beacon detected on channel 157 in band 5.0.
convert the aps
structure to a table and display the information specified in by using the local function generatebeacontable
.
detectedbeaconsinfo = generatebeacontable(aps,rxsim.frequencyband,retrievevendorinfo)
detectedbeaconsinfo=7×9 table
ssid bssid vendor snr (db) primary 20 mhz channel current channel center frequency index channel width (mhz) band mode
__________ ______________ ____________________________ ________ ______________________ ______________________________________ ___________________ ____ __________
"wlan_5g" "04d4c451c584" "asustek computer inc." 34.57 52 50 "160" 5 "802.11ax"
"w-inside" "b0b867f6b2d0" "hewlett packard enterprise" 26.24 56 58 "80" 5 "802.11ac"
"w-mobile" "b0b867f6b2d1" "hewlett packard enterprise" 26.251 56 58 "80" 5 "802.11ac"
"w-guest" "b0b867f6b2d2" "hewlett packard enterprise" 25.843 56 58 "80" 5 "802.11ac"
"w-inside" "b0b867f3d9b0" "hewlett packard enterprise" 31.592 157 155 "80" 5 "802.11ac"
"w-mobile" "b0b867f3d9b1" "hewlett packard enterprise" 31.971 157 155 "80" 5 "802.11ac"
"w-guest" "b0b867f3d9b2" "hewlett packard enterprise" 33.3 157 155 "80" 5 "802.11ac"
if rxsim.receiveonsdr release(sdrreceiver); end
further exploration
the
detectedbeaconsinfo
table shows only key information about the aps. to get further information about the beacons, such as data rates supported by the ap, explore the mac frame configuration in theaps
structure.if you have access to a configurable ap, change the channel width of your ap and rerun the example to confirm the channel width.
local functions
these functions assist in processing the incoming beacons.
function waveform = capturewaveform(sdrreceiver,numsamplestocapture) % capturewaveform returns a column vector of complex values given an % sdrreceiver object and a scalar numsamplestocapture value. % for a comm.sdrureceiver object, use the burst capture technique to % acquire the waveform if isa(sdrreceiver,'comm.sdrureceiver') waveform = complex(zeros(numsamplestocapture,1)); samplesperframe = sdrreceiver.samplesperframe; for i = 1:sdrreceiver.numframesinburst waveform(samplesperframe*(i-1) (1:samplesperframe)) = sdrreceiver(); end else waveform = capture(sdrreceiver,numsamplestocapture); 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 function vendor = determinevendor(mac) % determinevendor returns the vendor name of the ap by extracting the % organizationally unique identifier (oui) from the specified mac address. persistent ouis vendor = strings(0); try if isempty(ouis) if ~exist("oui.csv","file") disp("downloading oui.csv from ieee registration authority...") options = weboptions("timeout",10); websave("oui.csv","http://standards-oui.ieee.org/oui/oui.csv",options); end ouis = readtable("oui.csv",variablenamingrule="preserve"); end % extract oui from mac address. oui = mac(1:6); % extract vendors name based on oui. vendor = string(cell2mat(ouis.("organization name")(matches(ouis.assignment,oui)))); catch me % rethrow caught error as warning. warning(me.message "\nto skip the determinevendor function call, set retrievevendorinfo to false.",[]); end if isempty(vendor) vendor = "unknown"; end end function [mode,bw,operatingchannel] = determinemode(informationelements) % determinemode determines the 802.11 standard that the ap uses. % the function checks for the presence of ht, vht, and he capability % elements and determines the 802.11 standard that the ap uses. the element % ids are defined in ieee std 802.11-2020 and ieee std 802.11ax-2021. elementids = cell2mat(informationelements(:,1)); ids = elementids(:,1); if any(ids==255) if any(elementids(ids==255,2)==35) % he packet format mode = "802.11ax"; else mode = "unknown"; end vhtelement = informationelements{ids==192,2}; htelement = informationelements{ids==61,2}; [bw,operatingchannel] = determinechannelwidth(htelement,vhtelement); elseif any(ids==191) % vht packet format mode = "802.11ac"; vhtelement = informationelements{ids==192,2}; htelement = informationelements{ids==61,2}; [bw,operatingchannel] = determinechannelwidth(htelement,vhtelement); elseif any(ids==45) % ht packet format mode = "802.11n"; htelement = informationelements{ids==61,2}; [bw,operatingchannel] = determinechannelwidth(htelement); else % non-ht packet format % exclude b as only dsss is supported mode ="802.11a/g/j/p"; bw = "unknown"; operatingchannel = []; end end function [bw,operatingchannel] = determinechannelwidth(htelement,varargin) % determinechannelwidth returns the bandwidth of the channel from the % beacons operation information elements as defined in ieee std 802.11-2020 % table 11-23. msbfirst = false; % convert to bits to get sta channel width value in 3rd bit. htoperationinfobits = int2bit(htelement(2),5*8,msbfirst); operatingchannel = 0; if nargin == 2 vhtelement = varargin{1}; % vht operation channel width field cw = vhtelement(1); % channel center frequency segment 0 ccfs0 = vhtelement(2); % channel center frequency segment 1 ccfs1 = vhtelement(3); if htoperationinfobits(3) == 0 bw = "20"; operatingchannel = ccfs0; elseif cw == 0 % ht operation channel width field is 1 bw = "40"; operatingchannel = ccfs0; elseif ccfs1 == 0 % ht operation channel width field is 1 and % vht operation channel width field is 1 bw = "80"; operatingchannel = ccfs0; elseif abs(ccfs1 - ccfs0) == 8 % ht operation channel width field is 1 and % vht operation channel width field is 1 and % ccfs1 is greater than 0 bw = "160"; operatingchannel = ccfs1; else % ht operation channel width field is 1 and % vht operation channel width field is 1 and % ccfs1 is greater than 0 and % |ccfs1 - ccfs0| is greater than 16 bw = "80 80"; end end if operatingchannel == 0 if htoperationinfobits(3) == 1 bw = "40"; secondarychanneloffset = bit2int(htoperationinfobits(1:2),2,false); if secondarychanneloffset == 1 % secondary channel is above the primary channel. operatingchannel = htelement(1) 2; elseif secondarychanneloffset == 3 % secondary channel is below the primary channel. operatingchannel = htelement(1) - 2; else warning("could not determine operating channel.") end else bw = "20"; operatingchannel = htelement(1); end end end function tbl = generatebeacontable(aps,band,retrievevendorinfo) % generatebeacontable converts the access point structure to a table and % cleans up the variable names. tbl = struct2table(aps,"asarray",true); tbl.band = repmat(band,length(tbl.ssid),1); tbl = renamevars(tbl,["snr_db","beacon_channel","operating_channel","channel_width_mhz"], ... ["snr (db)","primary 20 mhz channel","current channel center frequency index", ... "channel width (mhz)"]); if retrievevendorinfo tbl = tbl(:,1:9); else tbl = tbl(:,[1:2,4:9]); end end