A Discrete-Event Network Simulator
API
wifi-primary-channels-test.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Stefano Avallone <stavallo@unina.it>
19  */
20 
21 #include "ns3/test.h"
22 #include "ns3/wifi-net-device.h"
23 #include "ns3/mobility-helper.h"
24 #include "ns3/spectrum-wifi-helper.h"
25 #include "ns3/multi-model-spectrum-channel.h"
26 #include "ns3/config.h"
27 #include "ns3/rng-seed-manager.h"
28 #include "ns3/wifi-psdu.h"
29 #include "ns3/ap-wifi-mac.h"
30 #include "ns3/sta-wifi-mac.h"
31 #include "ns3/he-phy.h"
32 #include "ns3/he-configuration.h"
33 #include "ns3/ctrl-headers.h"
34 #include <bitset>
35 #include <algorithm>
36 
37 using namespace ns3;
38 
39 
57 {
58 public:
65  WifiPrimaryChannelsTest (uint16_t channelWidth, bool useDistinctBssColors);
66  virtual ~WifiPrimaryChannelsTest ();
67 
77  void Transmit (std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW);
85  void SendDlSuPpdu (uint8_t bss, uint16_t txChannelWidth);
95  void SendDlMuPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus);
106  void SendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus);
116  void DoSendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus);
127  void ReceiveDl (uint8_t bss, uint8_t station, Ptr<WifiPsdu> psdu, RxSignalInfo rxSignalInfo,
128  WifiTxVector txVector, std::vector<bool> perMpduStatus);
138  void ReceiveUl (uint8_t bss, Ptr<WifiPsdu> psdu, RxSignalInfo rxSignalInfo,
139  WifiTxVector txVector, std::vector<bool> perMpduStatus);
143  void CheckAssociation (void);
154  void CheckReceivedSuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth);
168  void CheckReceivedMuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth, HeRu::RuType ruType,
169  std::size_t nRus, bool isDlMu);
179  void CheckReceivedTriggerFrames (std::set<uint8_t> txBss, uint16_t txChannelWidth);
180 
181 protected:
182  virtual void DoSetup (void);
183 
184 private:
185  virtual void DoRun (void);
186 
187  uint16_t m_channelWidth;
189  uint8_t m_nBss;
191  std::vector<NetDeviceContainer> m_staDevices;
193  std::vector<std::bitset<74>> m_received;
195  std::vector<std::bitset<74>> m_processed;
201 };
202 
203 WifiPrimaryChannelsTest::WifiPrimaryChannelsTest (uint16_t channelWidth, bool useDistinctBssColors)
204  : TestCase ("Check correct transmissions for various primary channel settings"),
205  m_channelWidth (channelWidth),
206  m_useDistinctBssColors (useDistinctBssColors)
207 {
208 }
209 
211 {
212 }
213 
214 void
215 WifiPrimaryChannelsTest::Transmit (std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
216 {
217  // Print all the transmitted frames if the test is executed through test-runner
218  std::cout << Simulator::Now () << std::endl;
219 
220  for (const auto& psduPair : psduMap)
221  {
222  if (psduPair.first != SU_STA_ID)
223  {
224  std::cout << " STA-ID " << psduPair.first;
225  }
226  std::cout << " " << psduPair.second->GetHeader (0).GetTypeString ()
227  << " seq " << psduPair.second->GetHeader (0).GetSequenceNumber ()
228  << " from " << psduPair.second->GetAddr2 ()
229  << " to " << psduPair.second->GetAddr1 () << std::endl;
230  }
231  std::cout << " TXVECTOR " << txVector << std::endl << std::endl;
232 }
233 
234 void
235 WifiPrimaryChannelsTest::ReceiveDl (uint8_t bss, uint8_t station, Ptr<WifiPsdu> psdu,
236  RxSignalInfo rxSignalInfo, WifiTxVector txVector,
237  std::vector<bool> perMpduStatus)
238 {
239  if (psdu->GetNMpdus () == 1)
240  {
241  const WifiMacHeader& hdr = psdu->GetHeader (0);
242 
243  if (hdr.IsQosData () || hdr.IsTrigger ())
244  {
245  std::cout << "RECEIVED BY BSS=" << +bss << " STA=" << +station
246  << " " << *psdu << std::endl;
247  // the MAC received a PSDU from the PHY
248  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (station), false, "Station [" << +bss << "]["
249  << +station << "] received a frame twice");
250  m_received[bss].set (station);
251  // check if we are the intended destination of the PSDU
252  auto dev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (station));
253  if ((hdr.IsQosData () && hdr.GetAddr1 () == dev->GetMac ()->GetAddress ())
254  || (hdr.IsTrigger () && hdr.GetAddr1 () == Mac48Address::GetBroadcast ()))
255  {
256  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (station), false, "Station [" << +bss << "]["
257  << +station << "] processed a frame twice");
258  m_processed[bss].set (station);
259  }
260  }
261  }
262 }
263 
264 void
266  WifiTxVector txVector, std::vector<bool> perMpduStatus)
267 {
268  // if the BSS color is zero, this AP might receive the frame sent by another AP. Given that
269  // stations only send TB PPDUs, we discard this frame if the TX vector is UL MU.
270  if (psdu->GetNMpdus () == 1 && psdu->GetHeader (0).IsQosData () && txVector.IsUlMu ())
271  {
272  auto dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
273 
274  uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first;
275  uint8_t station = staId - 1;
276  std::cout << "RECEIVED FROM BSS=" << +bss << " STA=" << +station
277  << " " << *psdu << std::endl;
278  // the MAC received a PSDU containing a QoS data frame from the PHY
279  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (station), false, "AP of BSS " << +bss
280  << " received a frame from station " << +station << " twice");
281  m_received[bss].set (station);
282  // check if we are the intended destination of the PSDU
283  if (psdu->GetHeader (0).GetAddr1 () == dev->GetMac ()->GetAddress ())
284  {
285  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (station), false, "AP of BSS " << +bss
286  << " received a frame from station " << +station << " twice");
287  m_processed[bss].set (station);
288  }
289  }
290 }
291 
292 void
294 {
295  RngSeedManager::SetSeed (1);
296  RngSeedManager::SetRun (40);
297  int64_t streamNumber = 100;
298  uint16_t frequency;
299 
300  // we create as many stations per BSS as the number of 26-tone RUs in a channel
301  // of the configured width
302  switch (m_channelWidth)
303  {
304  case 20:
305  m_nStationsPerBss = 9;
306  frequency = 5180;
307  break;
308  case 40:
309  m_nStationsPerBss = 18;
310  frequency = 5190;
311  break;
312  case 80:
313  m_nStationsPerBss = 37;
314  frequency = 5210;
315  break;
316  case 160:
317  m_nStationsPerBss = 74;
318  frequency = 5250;
319  break;
320  default:
321  NS_ABORT_MSG ("Channel width (" << m_channelWidth << ") not allowed");
322  }
323 
324  // we create as many BSSes as the number of 20 MHz subchannels
325  m_nBss = m_channelWidth / 20;
326 
327  NodeContainer wifiApNodes;
328  wifiApNodes.Create (m_nBss);
329 
330  std::vector<NodeContainer> wifiStaNodes (m_nBss);
331  for (auto& container : wifiStaNodes)
332  {
333  container.Create (m_nStationsPerBss);
334  }
335 
336  Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel> ();
337  Ptr<FriisPropagationLossModel> lossModel = CreateObject<FriisPropagationLossModel> ();
338  spectrumChannel->AddPropagationLossModel (lossModel);
339  Ptr<ConstantSpeedPropagationDelayModel> delayModel = CreateObject<ConstantSpeedPropagationDelayModel> ();
340  spectrumChannel->SetPropagationDelayModel (delayModel);
341 
343  phy.SetChannel (spectrumChannel);
344  phy.Set ("Frequency", UintegerValue (frequency)); // same frequency for all BSSes
345 
347  wifi.SetStandard (WIFI_STANDARD_80211ax_5GHZ);
348  wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager");
349 
351  mac.SetType ("ns3::StaWifiMac",
352  "Ssid", SsidValue (Ssid ("non-existent-ssid")));
353 
354  // Each BSS uses a distinct primary20 channel
355  for (uint8_t bss = 0; bss < m_nBss; bss++)
356  {
357  phy.Set ("Primary20MHzIndex", UintegerValue (bss));
358 
359  m_staDevices.push_back (wifi.Install (phy, mac, wifiStaNodes[bss]));
360  }
361 
362  for (uint8_t bss = 0; bss < m_nBss; bss++)
363  {
364  phy.Set ("Primary20MHzIndex", UintegerValue (bss));
365 
366  mac.SetType ("ns3::ApWifiMac",
367  "Ssid", SsidValue (Ssid ("wifi-ssid-" + std::to_string (bss))),
368  "BeaconInterval", TimeValue (MicroSeconds (102400)),
369  "EnableBeaconJitter", BooleanValue (false));
370 
371  m_apDevices.Add (wifi.Install (phy, mac, wifiApNodes.Get (bss)));
372  }
373 
374  // Assign fixed streams to random variables in use
375  streamNumber = wifi.AssignStreams (m_apDevices, streamNumber);
376  for (uint8_t bss = 0; bss < m_nBss; bss++)
377  {
378  streamNumber = wifi.AssignStreams (m_staDevices[bss], streamNumber);
379  }
380 
381  // set BSS color
383  {
384  for (uint8_t bss = 0; bss < m_nBss; bss++)
385  {
386  auto dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
387  dev->GetHeConfiguration ()->SetBssColor (bss + 1);
388  }
389  }
390 
392  Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator> ();
393 
394  positionAlloc->Add (Vector (0.0, 0.0, 0.0)); // all stations are co-located
395  mobility.SetPositionAllocator (positionAlloc);
396 
397  mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
398  mobility.Install (wifiApNodes);
399  for (uint8_t bss = 0; bss < m_nBss; bss++)
400  {
401  mobility.Install (wifiStaNodes[bss]);
402  }
403 
404  m_received.resize (m_nBss);
405  m_processed.resize (m_nBss);
406 
407  // pre-compute the Basic Trigger Frame to send
408  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (0));
409 
410  WifiMacHeader hdr;
412  hdr.SetAddr1 (Mac48Address::GetBroadcast ());
413  // Addr2 has to be set
414  hdr.SetSequenceNumber (1);
415 
416  Ptr<Packet> pkt = Create<Packet> ();
417  CtrlTriggerHeader trigger;
419  pkt->AddHeader (trigger);
420 
421  m_triggerTxVector = WifiTxVector (OfdmPhy::GetOfdmRate6Mbps (), 0, WIFI_PREAMBLE_LONG, 800, 1, 1, 0,
422  20, false, false, false);
423  m_trigger = Create<WifiPsdu> (pkt, hdr);
424 
425  m_triggerTxDuration = WifiPhy::CalculateTxDuration (m_trigger->GetSize (), m_triggerTxVector,
426  apDev->GetMac ()->GetWifiPhy ()->GetPhyBand ());
427 }
428 
429 void
431 {
432  // schedule association requests at different times
433  Time init = MilliSeconds (100);
434  Ptr<WifiNetDevice> dev;
435 
436  for (uint16_t i = 0; i < m_nStationsPerBss; i++)
437  {
438  // association can be done in parallel over the multiple BSSes
439  for (uint8_t bss = 0; bss < m_nBss; bss++)
440  {
441  dev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i));
442  Simulator::Schedule (init + i * MicroSeconds (102400), &WifiMac::SetSsid,
443  dev->GetMac (), Ssid ("wifi-ssid-" + std::to_string (bss)));
444  }
445  }
446 
447  // just before sending the beacon preceding the last association, increase the beacon
448  // interval (to the max allowed value) so that beacons do not interfere with data frames
449  for (uint8_t bss = 0; bss < m_nBss; bss++)
450  {
451  dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
452  auto mac = DynamicCast<ApWifiMac> (dev->GetMac ());
453 
454  Simulator::Schedule (init + (m_nStationsPerBss - 1) * MicroSeconds (102400),
455  &ApWifiMac::SetBeaconInterval, mac, MicroSeconds (1024 * 65535));
456  }
457 
458  m_time = (m_nStationsPerBss + 1) * MicroSeconds (102400);
459 
460  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::CheckAssociation, this);
461 
462  // we are done with association. We now intercept frames received by the
463  // PHY layer on stations and APs, which will no longer be passed to the FEM.
464  for (uint8_t bss = 0; bss < m_nBss; bss++)
465  {
466  for (uint8_t i = 0; i < m_nStationsPerBss; i++)
467  {
468  auto dev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i));
469  Simulator::Schedule (m_time, &WifiPhy::SetReceiveOkCallback, dev->GetPhy (),
471  .TwoBind (bss, i));
472  }
473  auto dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
474  Simulator::Schedule (m_time, &WifiPhy::SetReceiveOkCallback, dev->GetPhy (),
476  .Bind (bss));
477  }
478 
479  /*
480  * We start generating (downlink) SU PPDUs.
481  * First, APs operating on non-adjacent primary20 channels send a frame simultaneously
482  * in their primary20. This is done in two rounds. As an example, we consider the
483  * case of an 160 MHz operating channel:
484  *
485  * AP0 AP2 AP4 AP6
486  * |-----| |-----| |-----| |-----| |
487  *
488  * AP1 AP3 AP5 AP7
489  * | |-----| |-----| |-----| |-----|
490  *
491  * Then, we double the transmission channel width. We will have four rounds
492  * of transmissions. We avoid using adjacent channels to avoid interfence
493  * among transmissions:
494  *
495  * AP0 AP4
496  * |-----------| |-----------| |
497  * AP1 AP5
498  * |-----------| |-----------| |
499  * AP2 AP6
500  * | |-----------| |-----------|
501  * AP3 AP7
502  * | |-----------| |-----------|
503  *
504  * We double the transmission channel width again. We will have eight rounds
505  * of transmissions:
506  *
507  * AP0
508  * |-----------------------| |
509  * AP1
510  * |-----------------------| |
511  * AP2
512  * |-----------------------| |
513  * AP3
514  * |-----------------------| |
515  * AP4
516  * | |-----------------------|
517  * AP5
518  * | |-----------------------|
519  * AP6
520  * | |-----------------------|
521  * AP7
522  * | |-----------------------|
523  *
524  * We double the transmission channel width again. We will have eight rounds
525  * of transmissions:
526  *
527  * AP0
528  * |-----------------------------------------------|
529  * AP1
530  * |-----------------------------------------------|
531  * AP2
532  * |-----------------------------------------------|
533  * AP3
534  * |-----------------------------------------------|
535  * AP4
536  * |-----------------------------------------------|
537  * AP5
538  * |-----------------------------------------------|
539  * AP6
540  * |-----------------------------------------------|
541  * AP7
542  * |-----------------------------------------------|
543  *
544  * The transmission channel width reached the operating channel width, we are done.
545  */
546 
547  Time roundDuration = MilliSeconds (5); // upper bound on the duration of a round
548 
549  // To have simultaneous transmissions on adjacent channels, just initialize
550  // nRounds to 1 and nApsPerRound to m_channelWidth / 20. Of course, the test
551  // will fail because some stations will not receive some frames due to interfence
552  for (uint16_t txChannelWidth = 20, nRounds = 2, nApsPerRound = m_channelWidth / 20 / 2;
553  txChannelWidth <= m_channelWidth;
554  txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
555  {
556  nRounds = std::min<uint16_t> (nRounds, m_nBss);
557  nApsPerRound = std::max<uint16_t> (nApsPerRound, 1);
558 
559  for (uint16_t round = 0; round < nRounds; round++)
560  {
561  std::set<uint8_t> txBss;
562 
563  for (uint16_t i = 0; i < nApsPerRound; i++)
564  {
565  uint16_t ap = round + i * nRounds;
566  txBss.insert (ap);
567  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::SendDlSuPpdu, this,
568  ap, txChannelWidth);
569  }
570  // check that the SU frames were correctly received
571  Simulator::Schedule (m_time + roundDuration, &WifiPrimaryChannelsTest::CheckReceivedSuPpdus,
572  this, txBss, txChannelWidth);
573  m_time += roundDuration;
574  }
575  }
576 
577  /*
578  * Repeat the same scheme as before with DL MU transmissions. For each transmission
579  * channel width, every round is repeated as many times as the number of ways in
580  * which we can partition the transmission channel width in equal sized RUs.
581  */
582  for (uint16_t txChannelWidth = 20, nRounds = 2, nApsPerRound = m_channelWidth / 20 / 2;
583  txChannelWidth <= m_channelWidth;
584  txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
585  {
586  nRounds = std::min<uint16_t> (nRounds, m_nBss);
587  nApsPerRound = std::max<uint16_t> (nApsPerRound, 1);
588 
589  for (uint16_t round = 0; round < nRounds; round++)
590  {
591  for (unsigned int type = 0; type < 7; type++)
592  {
593  HeRu::RuType ruType = static_cast <HeRu::RuType> (type);
594  std::size_t nRus = HeRu::GetNRus (txChannelWidth, ruType);
595  std::set<uint8_t> txBss;
596  if (nRus > 0)
597  {
598  for (uint16_t i = 0; i < nApsPerRound; i++)
599  {
600  uint16_t ap = round + i * nRounds;
601  txBss.insert (ap);
602  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::SendDlMuPpdu, this,
603  ap, txChannelWidth, ruType, nRus);
604  }
605  // check that the MU frame was correctly received
606  Simulator::Schedule (m_time + roundDuration, &WifiPrimaryChannelsTest::CheckReceivedMuPpdus,
607  this, txBss, txChannelWidth, ruType, nRus, /* isDlMu */ true);
608  m_time += roundDuration;
609  }
610  }
611  }
612  }
613 
614  /*
615  * Repeat the same scheme as before with DL MU transmissions. For each transmission
616  * channel width, every round is repeated as many times as the number of ways in
617  * which we can partition the transmission channel width in equal sized RUs.
618  */
619  for (uint16_t txChannelWidth = 20, nRounds = 2, nApsPerRound = m_channelWidth / 20 / 2;
620  txChannelWidth <= m_channelWidth;
621  txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
622  {
623  nRounds = std::min<uint16_t> (nRounds, m_nBss);
624  nApsPerRound = std::max<uint16_t> (nApsPerRound, 1);
625 
626  for (uint16_t round = 0; round < nRounds; round++)
627  {
628  for (unsigned int type = 0; type < 7; type++)
629  {
630  HeRu::RuType ruType = static_cast <HeRu::RuType> (type);
631  std::size_t nRus = HeRu::GetNRus (txChannelWidth, ruType);
632  std::set<uint8_t> txBss;
633  if (nRus > 0)
634  {
635  for (uint16_t i = 0; i < nApsPerRound; i++)
636  {
637  uint16_t ap = round + i * nRounds;
638  txBss.insert (ap);
639  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::SendHeTbPpdu, this,
640  ap, txChannelWidth, ruType, nRus);
641  }
642  // check that Trigger Frames and TB PPDUs were correctly received
643  Simulator::Schedule (m_time + m_triggerTxDuration + MicroSeconds (10), /* during SIFS */
645  this, txBss, txChannelWidth);
646  Simulator::Schedule (m_time + roundDuration, &WifiPrimaryChannelsTest::CheckReceivedMuPpdus,
647  this, txBss, txChannelWidth, ruType, nRus, /* isDlMu */ false);
648  m_time += roundDuration;
649  }
650  }
651  }
652  }
653 
654  // Trace PSDUs passed to the PHY on all devices
655  Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyTxPsduBegin",
657 
658  Simulator::Stop (m_time);
659  Simulator::Run ();
660 
661  Simulator::Destroy ();
662 }
663 
664 void
665 WifiPrimaryChannelsTest::SendDlSuPpdu (uint8_t bss, uint16_t txChannelWidth)
666 {
667  std::cout << "*** BSS " << +bss << " transmits on primary " << txChannelWidth
668  << " MHz channel" << std::endl;
669 
670  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
671  auto staDev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (0));
672 
673  uint8_t bssColor = apDev->GetHeConfiguration ()->GetBssColor ();
674  WifiTxVector txVector = WifiTxVector (HePhy::GetHeMcs8 (), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, txChannelWidth, false, false, false, bssColor);
675  WifiMacHeader hdr;
677  hdr.SetQosTid (0);
678  hdr.SetAddr1 (staDev->GetMac ()->GetAddress ());
679  hdr.SetAddr2 (apDev->GetMac ()->GetAddress ());
680  hdr.SetAddr3 (apDev->GetMac ()->GetBssid ());
681  hdr.SetSequenceNumber (1);
682  Ptr<WifiPsdu> psdu = Create<WifiPsdu> (Create<Packet> (1000), hdr);
683  apDev->GetPhy ()->Send (WifiConstPsduMap ({std::make_pair (SU_STA_ID, psdu)}), txVector);
684 }
685 
686 void
687 WifiPrimaryChannelsTest::SendDlMuPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
688 {
689  std::cout << "*** BSS " << +bss << " transmits on primary " << txChannelWidth
690  << " MHz channel a DL MU PPDU " << "addressed to " << nRus
691  << " stations (RU type: " << ruType << ")" << std::endl;
692 
693  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
694  uint8_t bssColor = apDev->GetHeConfiguration ()->GetBssColor ();
695 
696  WifiTxVector txVector = WifiTxVector (HePhy::GetHeMcs8 (), 0, WIFI_PREAMBLE_HE_MU, 800, 1, 1, 0, txChannelWidth, false, false, false, bssColor);
697  WifiMacHeader hdr;
699  hdr.SetQosTid (0);
700  hdr.SetAddr2 (apDev->GetMac ()->GetAddress ());
701  hdr.SetAddr3 (apDev->GetMac ()->GetBssid ());
702  hdr.SetSequenceNumber (1);
703 
704  WifiConstPsduMap psduMap;
705 
706  for (std::size_t i = 1; i <= nRus; i++)
707  {
708  std::size_t index = (txChannelWidth == 160 && i > nRus / 2 ? i - nRus / 2 : i);
709  bool primary80 = (txChannelWidth == 160 && i > nRus / 2 ? false : true);
710 
711  auto staDev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i - 1));
712  uint16_t staId = DynamicCast<StaWifiMac> (staDev->GetMac ())->GetAssociationId ();
713  txVector.SetHeMuUserInfo (staId, {{primary80, ruType, index}, HePhy::GetHeMcs8 (), 1});
714  hdr.SetAddr1 (staDev->GetMac ()->GetAddress ());
715  psduMap[staId] = Create<const WifiPsdu> (Create<Packet> (1000), hdr);
716  }
717  apDev->GetPhy ()->Send (psduMap, txVector);
718 }
719 
720 void
721 WifiPrimaryChannelsTest::SendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
722 {
723  std::cout << "*** BSS " << +bss << " transmits a Basic Trigger Frame" << std::endl;
724 
725  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
726 
727  m_trigger->GetHeader (0).SetAddr2 (apDev->GetMac ()->GetAddress ());
728 
729  apDev->GetPhy ()->Send (m_trigger, m_triggerTxVector);
730 
731  // schedule the transmission of HE TB PPDUs
732  Simulator::Schedule (m_triggerTxDuration + apDev->GetPhy ()->GetSifs (),
734  bss, txChannelWidth, ruType, nRus);
735 }
736 
737 void
738 WifiPrimaryChannelsTest::DoSendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
739 {
740  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
741  uint8_t bssColor = apDev->GetHeConfiguration ()->GetBssColor ();
742 
743  WifiMacHeader hdr;
745  hdr.SetQosTid (0);
746  hdr.SetAddr1 (apDev->GetMac ()->GetAddress ());
747  hdr.SetAddr3 (apDev->GetMac ()->GetBssid ());
748  hdr.SetSequenceNumber (1);
749 
750  Time duration = Seconds (0);
751  uint16_t length = 0;
752 
753  for (std::size_t i = 1; i <= nRus; i++)
754  {
755  std::cout << "*** BSS " << +bss << " STA " << i - 1 << " transmits on primary "
756  << txChannelWidth << " MHz channel an HE TB PPDU (RU type: " << ruType << ")" << std::endl;
757 
758  std::size_t index = (txChannelWidth == 160 && i > nRus / 2 ? i - nRus / 2 : i);
759  bool primary80 = (txChannelWidth == 160 && i > nRus / 2 ? false : true);
760 
761  auto staDev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i - 1));
762  uint16_t staId = DynamicCast<StaWifiMac> (staDev->GetMac ())->GetAssociationId ();
763 
764  WifiTxVector txVector (HePhy::GetHeMcs8 (), 0, WIFI_PREAMBLE_HE_TB, 3200, 1, 1, 0, txChannelWidth, false, false, false, bssColor);
765  txVector.SetHeMuUserInfo (staId, {{primary80, ruType, index}, HePhy::GetHeMcs8 (), 1});
766 
767  hdr.SetAddr2 (staDev->GetMac ()->GetAddress ());
768  Ptr<const WifiPsdu> psdu = Create<const WifiPsdu> (Create<Packet> (1000), hdr);
769 
770  if (duration.IsZero ())
771  {
772  // calculate just once
773  duration = WifiPhy::CalculateTxDuration (psdu->GetSize (), txVector,
774  staDev->GetMac ()->GetWifiPhy ()->GetPhyBand (), staId);
775  length = HePhy::ConvertHeTbPpduDurationToLSigLength (duration,
776  staDev->GetMac ()->GetWifiPhy ()->GetPhyBand ());
777  }
778  txVector.SetLength (length);
779 
780  staDev->GetPhy ()->Send (WifiConstPsduMap {{staId, psdu}}, txVector);
781  }
782 }
783 
784 void
786 {
787  for (uint8_t bss = 0; bss < m_nBss; bss++)
788  {
789  auto dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
790  auto mac = DynamicCast<ApWifiMac> (dev->GetMac ());
791  NS_TEST_EXPECT_MSG_EQ (mac->GetStaList ().size (), m_nStationsPerBss,
792  "Not all the stations completed association");
793  }
794 }
795 
796 void
797 WifiPrimaryChannelsTest::CheckReceivedSuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth)
798 {
799  for (uint8_t bss = 0; bss < m_nBss; bss++)
800  {
801  if (txBss.find (bss) != txBss.end ())
802  {
803  // Every station in the BSS of an AP that transmitted the frame hears (i.e.,
804  // passes to the MAC) the frame
805  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
806  {
807  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true, "Station [" << +bss << "][" << +sta
808  << "] did not receive the SU frame on primary" << txChannelWidth << " channel");
809  }
810  // only the first station actually processed the frames
811  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (0), true, "Station [" << +bss << "][0]"
812  << " did not process the SU frame on primary" << txChannelWidth << " channel");
813  for (uint8_t sta = 1; sta < m_nStationsPerBss; sta++)
814  {
815  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false, "Station [" << +bss << "][" << +sta
816  << "] processed the SU frame on primary" << txChannelWidth << " channel");
817  }
818  }
819  else
820  {
821  // There was no transmission in this BSS. If BSS Color filtering is enabled or no frame
822  // transmission overlaps with the primary20 channel of this BSS, stations in this BSS
823  // did not hear any frame.
825  || std::none_of (txBss.begin (), txBss.end (),
826  [&](const uint8_t& txAp)
827  {
828  auto txApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (txAp))->GetPhy ();
829  auto thisApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss))->GetPhy ();
830  return txApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth)
831  == thisApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth);
832  }))
833  {
834  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
835  {
836  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false, "Station [" << +bss << "][" << +sta
837  << "] received the SU frame on primary" << txChannelWidth << " channel");
838  }
839  }
840  else
841  {
842  // all stations heard the frame but no station processed it
843  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
844  {
845  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true, "Station [" << +bss << "][" << +sta
846  << "] did not receive the SU frame on primary" << txChannelWidth << " channel");
847  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false, "Station [" << +bss << "][" << +sta
848  << "] processed the SU frame on primary" << txChannelWidth << " channel");
849  }
850  }
851  }
852  // reset bitmaps
853  m_received[bss].reset ();
854  m_processed[bss].reset ();
855  }
856 }
857 
858 void
859 WifiPrimaryChannelsTest::CheckReceivedMuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth,
860  HeRu::RuType ruType, std::size_t nRus, bool isDlMu)
861 {
862  for (uint8_t bss = 0; bss < m_nBss; bss++)
863  {
864  if (txBss.find (bss) != txBss.end ())
865  {
866  // Due to AID filtering, only stations that are addressed by the MU PPDU do hear the frame
867  for (uint8_t sta = 0; sta < nRus; sta++)
868  {
869  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true,
870  (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
871  << " station [" << +bss << "][" << +sta << "] on primary"
872  << txChannelWidth << " channel, RU type " << ruType
873  << " was not received");
874  }
875  for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
876  {
877  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false,
878  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
879  << " transmitted on primary" << txChannelWidth
880  << " channel, RU type " << ruType << " was received "
881  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
882  << +sta << "]");
883  }
884  // only the addressed stations actually processed the frames
885  for (uint8_t sta = 0; sta < nRus; sta++)
886  {
887  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), true,
888  (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
889  << " station [" << +bss << "][" << +sta << "] on primary"
890  << txChannelWidth << " channel, RU type " << ruType
891  << " was not processed");
892  }
893  for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
894  {
895  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false,
896  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
897  << " transmitted on primary" << txChannelWidth
898  << " channel, RU type " << ruType << " was received "
899  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
900  << +sta << "] and processed");
901  }
902  }
903  else
904  {
905  // There was no transmission in this BSS. If BSS Color filtering is enabled or no frame
906  // transmission overlaps with the primary20 channel of this BSS, stations in this BSS
907  // did not hear any frame.
909  || std::none_of (txBss.begin (), txBss.end (),
910  [&](const uint8_t& txAp)
911  {
912  auto txApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (txAp))->GetPhy ();
913  auto thisApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss))->GetPhy ();
914  return txApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth)
915  == thisApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth);
916  }))
917  {
918  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
919  {
920  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false,
921  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
922  << " transmitted on primary" << txChannelWidth
923  << " channel, RU type " << ruType << " was received "
924  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
925  << +sta << "]");
926  }
927  }
928  else
929  {
930  // stations having the same AID of the stations addressed by the MU PPDI received the frame
931  for (uint8_t sta = 0; sta < nRus; sta++)
932  {
933  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true,
934  (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
935  << " station [" << +bss << "][" << +sta << "] on primary"
936  << txChannelWidth << " channel, RU type " << ruType
937  << " was not received");
938  }
939  for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
940  {
941  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false,
942  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
943  << " transmitted on primary" << txChannelWidth
944  << " channel, RU type " << ruType << " was received "
945  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
946  << +sta << "]");
947  }
948  // no station processed the frame
949  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
950  {
951  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false,
952  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
953  << " transmitted on primary" << txChannelWidth
954  << " channel, RU type " << ruType << " was received "
955  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
956  << +sta << "] and processed");
957  }
958  }
959  }
960  // reset bitmaps
961  m_received[bss].reset ();
962  m_processed[bss].reset ();
963  }
964 }
965 
966 void
967 WifiPrimaryChannelsTest::CheckReceivedTriggerFrames (std::set<uint8_t> txBss, uint16_t txChannelWidth)
968 {
969  for (uint8_t bss = 0; bss < m_nBss; bss++)
970  {
971  if (txBss.find (bss) != txBss.end ())
972  {
973  // Every station in the BSS of an AP that transmitted the Trigger Frame hears (i.e.,
974  // passes to the MAC) and processes the frame
975  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
976  {
977  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true, "Station [" << +bss << "][" << +sta
978  << "] did not receive the Trigger Frame soliciting a transmission on primary"
979  << txChannelWidth << " channel");
980  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), true, "Station [" << +bss << "][" << +sta
981  << "] did not process the Trigger Frame soliciting a transmission on primary"
982  << txChannelWidth << " channel");
983  }
984  }
985  else
986  {
987  // Given that a Trigger Frame is transmitted on the primary20 channel and all the
988  // primary20 channels are distinct, stations in other BSSes did not hear the frame
989  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
990  {
991  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false, "Station [" << +bss << "][" << +sta
992  << "] received the Trigger Frame soliciting a transmission on primary"
993  << txChannelWidth << " channel");
994  }
995  }
996  // reset bitmaps
997  m_received[bss].reset ();
998  m_processed[bss].reset ();
999  }
1000 }
1001 
1002 
1010 {
1011 public:
1013 };
1014 
1016  : TestSuite ("wifi-primary-channels", UNIT)
1017 {
1018  // Test cases for 20 MHz can be added, but are not that useful (there would be a single BSS)
1019  AddTestCase (new WifiPrimaryChannelsTest (40, true), TestCase::QUICK);
1020  AddTestCase (new WifiPrimaryChannelsTest (40, false), TestCase::QUICK);
1021  AddTestCase (new WifiPrimaryChannelsTest (80, true), TestCase::EXTENSIVE);
1022  AddTestCase (new WifiPrimaryChannelsTest (80, false), TestCase::EXTENSIVE);
1023  AddTestCase (new WifiPrimaryChannelsTest (160, true), TestCase::TAKES_FOREVER);
1024  AddTestCase (new WifiPrimaryChannelsTest (160, false), TestCase::TAKES_FOREVER);
1025 }
1026 
Time m_triggerTxDuration
TX duration for Basic Trigger Frame.
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:103
Time m_time
the time when the current action is executed
void SetType(TriggerFrameType type)
Set the Trigger frame type.
AttributeValue implementation for Boolean.
Definition: boolean.h:36
Headers for Trigger frames.
Definition: ctrl-headers.h:751
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:50
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void CheckReceivedMuPpdus(std::set< uint8_t > txBss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus, bool isDlMu)
Check that (i) all stations/APs belonging to the given BSSes received the DL/UL MU PPDUs transmitted ...
A suite of tests to run.
Definition: test.h:1343
void CheckReceivedSuPpdus(std::set< uint8_t > txBss, uint16_t txChannelWidth)
Check that (i) all stations belonging to the given BSSes received the SU PPDUs transmitted over the g...
Mac48Address GetAddr1(void) const
Return the address in the Address 1 field.
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1297
void SendDlMuPpdu(uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
Have the AP of the given BSS transmit a MU PPDU using the given transmission channel width and RU typ...
#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report if not.
Definition: test.h:283
std::vector< NetDeviceContainer > m_staDevices
containers for stations&#39; NetDevices
encapsulates test code
Definition: test.h:1153
void AddPropagationLossModel(Ptr< PropagationLossModel > loss)
Add the single-frequency propagation loss model to be used.
helps to create WifiNetDevice objects
Definition: wifi-helper.h:326
uint8_t m_nStationsPerBss
number of stations per AP
bool IsTrigger(void) const
Return true if the header is a Trigger header.
void SendDlSuPpdu(uint8_t bss, uint16_t txChannelWidth)
Have the AP of the given BSS transmit a SU PPDU using the given transmission channel width...
mobility
Definition: third.py:108
uint32_t GetSize(void) const
Return the size of the PSDU in bytes.
Definition: wifi-psdu.cc:260
phy
Definition: third.py:93
std::vector< std::bitset< 74 > > m_received
whether the last packet transmitted to/from each of the (up to 74 per BSS) stations was received ...
RxSignalInfo structure containing info on the received signal.
Definition: phy-entity.h:66
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
bool IsZero(void) const
Exactly equivalent to t == 0.
Definition: nstime.h:301
AttributeValue implementation for Time.
Definition: nstime.h:1353
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:299
uint16_t m_channelWidth
operating channel width in MHz
void Add(NetDeviceContainer other)
Append the contents of another NetDeviceContainer to the end of this container.
void CheckAssociation(void)
Check that all stations associated with an AP.
void SendHeTbPpdu(uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
Have the AP of the given BSS transmit a Basic Trigger Frame.
Hold an unsigned integer type.
Definition: uinteger.h:44
holds a vector of ns3::NetDevice pointers
mac
Definition: third.py:99
void DoSendHeTbPpdu(uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
Have the STAs of the given BSS transmit an HE TB PPDU using the given transmission channel width and ...
void Connect(std::string path, const CallbackBase &cb)
Definition: config.cc:920
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set the HE MU user-specific transmission information for the given STA-ID.
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition: wifi-psdu.cc:266
void SetQosTid(uint8_t tid)
Set the TID for the QoS header.
static WifiPrimaryChannelsTestSuite g_wifiPrimaryChannelsTestSuite
the test suite
Ptr< WifiPhy > GetPhy(void) const
Ptr< WifiPsdu > m_trigger
Basic Trigger Frame.
std::size_t GetNMpdus(void) const
Return the number of MPDUs constituting the PSDU.
Definition: wifi-psdu.cc:319
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::vector< std::bitset< 74 > > m_processed
whether the last packet transmitted to/from each of the (up to 74 per BSS) stations was processed ...
keep track of a set of node pointers.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
wifi primary channels test suite
RuType
The different HE Resource Unit (RU) types.
Definition: he-ru.h:41
create MAC layers for a ns3::WifiNetDevice.
virtual void DoSetup(void)
Implementation to do any local setup required for this TestCase.
The IEEE 802.11 SSID Information Element.
Definition: ssid.h:35
wifi
Definition: third.py:96
Helper class used to assign positions and mobility models to nodes.
void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Ptr< WifiMac > GetMac(void) const
void SetSequenceNumber(uint16_t seq)
Set the sequence number of the header.
virtual void DoRun(void)
Implementation to actually run this TestCase.
NetDeviceContainer m_apDevices
container for AP&#39;s NetDevice
bool IsUlMu(void) const
Return true if this TX vector is used for an uplink multi-user transmission.
void SetPropagationDelayModel(Ptr< PropagationDelayModel > delay)
Set the propagation delay model to be used.
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1289
AttributeValue implementation for Ssid.
Definition: ssid.h:105
bool m_useDistinctBssColors
true to set distinct BSS colors to BSSes
void Add(Vector v)
Add a position to the list of positions.
void Transmit(std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when PHY receives a PSDU to transmit.
void CheckReceivedTriggerFrames(std::set< uint8_t > txBss, uint16_t txChannelWidth)
Check that (i) all stations belonging to the given BSSes received the transmitted Trigger Frame; and ...
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
WifiTxVector m_triggerTxVector
TX vector for Basic Trigger Frame.
bool IsQosData(void) const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data...
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1305
Time Now(void)
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:287
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
void SetLength(uint16_t length)
Set the LENGTH field of the L-SIG.
wifiStaNodes
Definition: third.py:88
const HeMuUserInfoMap & GetHeMuUserInfoMap(void) const
Get the map HE MU user-specific transmission information indexed by STA-ID.
void ReceiveDl(uint8_t bss, uint8_t station, Ptr< WifiPsdu > psdu, RxSignalInfo rxSignalInfo, WifiTxVector txVector, std::vector< bool > perMpduStatus)
Callback invoked when a station receives a DL PPDU.
void ReceiveUl(uint8_t bss, Ptr< WifiPsdu > psdu, RxSignalInfo rxSignalInfo, WifiTxVector txVector, std::vector< bool > perMpduStatus)
Callback invoked when an AP receives an UL PPDU.
Test transmissions under different primary channel settings.
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
#define SU_STA_ID
Definition: wifi-mode.h:32
Callback< R, Ts... > MakeCallback(R(T::*memPtr)(Ts...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:1642
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:256
Implements the IEEE 802.11 MAC header.
WifiPrimaryChannelsTest(uint16_t channelWidth, bool useDistinctBssColors)
Constructor.
Make it easy to create and manage PHY objects for the spectrum model.