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 #include <sstream>
37 
38 using namespace ns3;
39 
40 NS_LOG_COMPONENT_DEFINE ("WifiPrimaryChannelsTest");
41 
59 {
60 public:
67  WifiPrimaryChannelsTest (uint16_t channelWidth, bool useDistinctBssColors);
68  virtual ~WifiPrimaryChannelsTest ();
69 
79  void Transmit (std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW);
87  void SendDlSuPpdu (uint8_t bss, uint16_t txChannelWidth);
97  void SendDlMuPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus);
108  void SendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus);
118  void DoSendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus);
129  void ReceiveDl (uint8_t bss, uint8_t station, Ptr<WifiPsdu> psdu, RxSignalInfo rxSignalInfo,
130  WifiTxVector txVector, std::vector<bool> perMpduStatus);
140  void ReceiveUl (uint8_t bss, Ptr<WifiPsdu> psdu, RxSignalInfo rxSignalInfo,
141  WifiTxVector txVector, std::vector<bool> perMpduStatus);
145  void CheckAssociation (void);
156  void CheckReceivedSuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth);
170  void CheckReceivedMuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth, HeRu::RuType ruType,
171  std::size_t nRus, bool isDlMu);
181  void CheckReceivedTriggerFrames (std::set<uint8_t> txBss, uint16_t txChannelWidth);
182 
183 private:
184  void DoSetup (void) override;
185  void DoRun (void) override;
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  for (const auto& psduPair : psduMap)
218  {
219  std::stringstream ss;
220 
221  if (psduPair.first != SU_STA_ID)
222  {
223  ss << " STA-ID " << psduPair.first;
224  }
225  ss << " " << psduPair.second->GetHeader (0).GetTypeString ()
226  << " seq " << psduPair.second->GetHeader (0).GetSequenceNumber ()
227  << " from " << psduPair.second->GetAddr2 ()
228  << " to " << psduPair.second->GetAddr1 ();
229  NS_LOG_INFO (ss.str ());
230  }
231  NS_LOG_INFO (" TXVECTOR " << txVector);
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  NS_LOG_INFO ("RECEIVED BY BSS=" << +bss << " STA=" << +station << " " << *psdu);
246  // the MAC received a PSDU from the PHY
247  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (station), false, "Station [" << +bss << "]["
248  << +station << "] received a frame twice");
249  m_received[bss].set (station);
250  // check if we are the intended destination of the PSDU
251  auto dev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (station));
252  if ((hdr.IsQosData () && hdr.GetAddr1 () == dev->GetMac ()->GetAddress ())
253  || (hdr.IsTrigger () && hdr.GetAddr1 () == Mac48Address::GetBroadcast ()))
254  {
255  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (station), false, "Station [" << +bss << "]["
256  << +station << "] processed a frame twice");
257  m_processed[bss].set (station);
258  }
259  }
260  }
261 }
262 
263 void
265  WifiTxVector txVector, std::vector<bool> perMpduStatus)
266 {
267  // if the BSS color is zero, this AP might receive the frame sent by another AP. Given that
268  // stations only send TB PPDUs, we discard this frame if the TX vector is UL MU.
269  if (psdu->GetNMpdus () == 1 && psdu->GetHeader (0).IsQosData () && txVector.IsUlMu ())
270  {
271  auto dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
272 
273  uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first;
274  uint8_t station = staId - 1;
275  NS_LOG_INFO ("RECEIVED FROM BSS=" << +bss << " STA=" << +station
276  << " " << *psdu);
277  // the MAC received a PSDU containing a QoS data frame from the PHY
278  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (station), false, "AP of BSS " << +bss
279  << " received a frame from station " << +station << " twice");
280  m_received[bss].set (station);
281  // check if we are the intended destination of the PSDU
282  if (psdu->GetHeader (0).GetAddr1 () == dev->GetMac ()->GetAddress ())
283  {
284  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (station), false, "AP of BSS " << +bss
285  << " received a frame from station " << +station << " twice");
286  m_processed[bss].set (station);
287  }
288  }
289 }
290 
291 void
293 {
294  RngSeedManager::SetSeed (1);
295  RngSeedManager::SetRun (40);
296  int64_t streamNumber = 100;
297  uint16_t frequency;
298 
299  // we create as many stations per BSS as the number of 26-tone RUs in a channel
300  // of the configured width
301  switch (m_channelWidth)
302  {
303  case 20:
304  m_nStationsPerBss = 9;
305  frequency = 5180;
306  break;
307  case 40:
308  m_nStationsPerBss = 18;
309  frequency = 5190;
310  break;
311  case 80:
312  m_nStationsPerBss = 37;
313  frequency = 5210;
314  break;
315  case 160:
316  m_nStationsPerBss = 74;
317  frequency = 5250;
318  break;
319  default:
320  NS_ABORT_MSG ("Channel width (" << m_channelWidth << ") not allowed");
321  }
322 
323  // we create as many BSSes as the number of 20 MHz subchannels
324  m_nBss = m_channelWidth / 20;
325 
326  NodeContainer wifiApNodes;
327  wifiApNodes.Create (m_nBss);
328 
329  std::vector<NodeContainer> wifiStaNodes (m_nBss);
330  for (auto& container : wifiStaNodes)
331  {
332  container.Create (m_nStationsPerBss);
333  }
334 
335  Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel> ();
336  Ptr<FriisPropagationLossModel> lossModel = CreateObject<FriisPropagationLossModel> ();
337  spectrumChannel->AddPropagationLossModel (lossModel);
338  Ptr<ConstantSpeedPropagationDelayModel> delayModel = CreateObject<ConstantSpeedPropagationDelayModel> ();
339  spectrumChannel->SetPropagationDelayModel (delayModel);
340 
342  phy.SetChannel (spectrumChannel);
343  phy.Set ("Frequency", UintegerValue (frequency)); // same frequency for all BSSes
344 
346  wifi.SetStandard (WIFI_STANDARD_80211ax_5GHZ);
347  wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager");
348 
350  mac.SetType ("ns3::StaWifiMac",
351  "Ssid", SsidValue (Ssid ("non-existent-ssid")),
352  "WaitBeaconTimeout", TimeValue (MicroSeconds (102400))); // same as BeaconInterval
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  Ptr<WifiNetDevice> dev;
434 
435  for (uint16_t i = 0; i < m_nStationsPerBss; i++)
436  {
437  // association can be done in parallel over the multiple BSSes
438  for (uint8_t bss = 0; bss < m_nBss; bss++)
439  {
440  dev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i));
441  Simulator::Schedule (i * MicroSeconds (102400), &WifiMac::SetSsid,
442  dev->GetMac (), Ssid ("wifi-ssid-" + std::to_string (bss)));
443  }
444  }
445 
446  // just before sending the beacon preceding the last association, increase the beacon
447  // interval (to the max allowed value) so that beacons do not interfere with data frames
448  for (uint8_t bss = 0; bss < m_nBss; bss++)
449  {
450  dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
451  auto mac = DynamicCast<ApWifiMac> (dev->GetMac ());
452 
453  Simulator::Schedule ((m_nStationsPerBss - 1) * MicroSeconds (102400),
454  &ApWifiMac::SetBeaconInterval, mac, MicroSeconds (1024 * 65535));
455  }
456 
457  m_time = (m_nStationsPerBss + 1) * MicroSeconds (102400);
458 
459  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::CheckAssociation, this);
460 
461  // we are done with association. We now intercept frames received by the
462  // PHY layer on stations and APs, which will no longer be passed to the FEM.
463  for (uint8_t bss = 0; bss < m_nBss; bss++)
464  {
465  for (uint8_t i = 0; i < m_nStationsPerBss; i++)
466  {
467  auto dev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i));
468  Simulator::Schedule (m_time, &WifiPhy::SetReceiveOkCallback, dev->GetPhy (),
470  .TwoBind (bss, i));
471  }
472  auto dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
473  Simulator::Schedule (m_time, &WifiPhy::SetReceiveOkCallback, dev->GetPhy (),
475  .Bind (bss));
476  }
477 
478  /*
479  * We start generating (downlink) SU PPDUs.
480  * First, APs operating on non-adjacent primary20 channels send a frame simultaneously
481  * in their primary20. This is done in two rounds. As an example, we consider the
482  * case of an 160 MHz operating channel:
483  *
484  * AP0 AP2 AP4 AP6
485  * |-----| |-----| |-----| |-----| |
486  *
487  * AP1 AP3 AP5 AP7
488  * | |-----| |-----| |-----| |-----|
489  *
490  * Then, we double the transmission channel width. We will have four rounds
491  * of transmissions. We avoid using adjacent channels to avoid interfence
492  * among transmissions:
493  *
494  * AP0 AP4
495  * |-----------| |-----------| |
496  * AP1 AP5
497  * |-----------| |-----------| |
498  * AP2 AP6
499  * | |-----------| |-----------|
500  * AP3 AP7
501  * | |-----------| |-----------|
502  *
503  * We double the transmission channel width again. We will have eight rounds
504  * of transmissions:
505  *
506  * AP0
507  * |-----------------------| |
508  * AP1
509  * |-----------------------| |
510  * AP2
511  * |-----------------------| |
512  * AP3
513  * |-----------------------| |
514  * AP4
515  * | |-----------------------|
516  * AP5
517  * | |-----------------------|
518  * AP6
519  * | |-----------------------|
520  * AP7
521  * | |-----------------------|
522  *
523  * We double the transmission channel width again. We will have eight rounds
524  * of transmissions:
525  *
526  * AP0
527  * |-----------------------------------------------|
528  * AP1
529  * |-----------------------------------------------|
530  * AP2
531  * |-----------------------------------------------|
532  * AP3
533  * |-----------------------------------------------|
534  * AP4
535  * |-----------------------------------------------|
536  * AP5
537  * |-----------------------------------------------|
538  * AP6
539  * |-----------------------------------------------|
540  * AP7
541  * |-----------------------------------------------|
542  *
543  * The transmission channel width reached the operating channel width, we are done.
544  */
545 
546  Time roundDuration = MilliSeconds (5); // upper bound on the duration of a round
547 
548  // To have simultaneous transmissions on adjacent channels, just initialize
549  // nRounds to 1 and nApsPerRound to m_channelWidth / 20. Of course, the test
550  // will fail because some stations will not receive some frames due to interfence
551  for (uint16_t txChannelWidth = 20, nRounds = 2, nApsPerRound = m_channelWidth / 20 / 2;
552  txChannelWidth <= m_channelWidth;
553  txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
554  {
555  nRounds = std::min<uint16_t> (nRounds, m_nBss);
556  nApsPerRound = std::max<uint16_t> (nApsPerRound, 1);
557 
558  for (uint16_t round = 0; round < nRounds; round++)
559  {
560  std::set<uint8_t> txBss;
561 
562  for (uint16_t i = 0; i < nApsPerRound; i++)
563  {
564  uint16_t ap = round + i * nRounds;
565  txBss.insert (ap);
566  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::SendDlSuPpdu, this,
567  ap, txChannelWidth);
568  }
569  // check that the SU frames were correctly received
570  Simulator::Schedule (m_time + roundDuration, &WifiPrimaryChannelsTest::CheckReceivedSuPpdus,
571  this, txBss, txChannelWidth);
572  m_time += roundDuration;
573  }
574  }
575 
576  /*
577  * Repeat the same scheme as before with DL MU transmissions. For each transmission
578  * channel width, every round is repeated as many times as the number of ways in
579  * which we can partition the transmission channel width in equal sized RUs.
580  */
581  for (uint16_t txChannelWidth = 20, nRounds = 2, nApsPerRound = m_channelWidth / 20 / 2;
582  txChannelWidth <= m_channelWidth;
583  txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
584  {
585  nRounds = std::min<uint16_t> (nRounds, m_nBss);
586  nApsPerRound = std::max<uint16_t> (nApsPerRound, 1);
587 
588  for (uint16_t round = 0; round < nRounds; round++)
589  {
590  for (unsigned int type = 0; type < 7; type++)
591  {
592  HeRu::RuType ruType = static_cast <HeRu::RuType> (type);
593  std::size_t nRus = HeRu::GetNRus (txChannelWidth, ruType);
594  std::set<uint8_t> txBss;
595  if (nRus > 0)
596  {
597  for (uint16_t i = 0; i < nApsPerRound; i++)
598  {
599  uint16_t ap = round + i * nRounds;
600  txBss.insert (ap);
601  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::SendDlMuPpdu, this,
602  ap, txChannelWidth, ruType, nRus);
603  }
604  // check that the MU frame was correctly received
605  Simulator::Schedule (m_time + roundDuration, &WifiPrimaryChannelsTest::CheckReceivedMuPpdus,
606  this, txBss, txChannelWidth, ruType, nRus, /* isDlMu */ true);
607  m_time += roundDuration;
608  }
609  }
610  }
611  }
612 
613  /*
614  * Repeat the same scheme as before with DL MU transmissions. For each transmission
615  * channel width, every round is repeated as many times as the number of ways in
616  * which we can partition the transmission channel width in equal sized RUs.
617  */
618  for (uint16_t txChannelWidth = 20, nRounds = 2, nApsPerRound = m_channelWidth / 20 / 2;
619  txChannelWidth <= m_channelWidth;
620  txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
621  {
622  nRounds = std::min<uint16_t> (nRounds, m_nBss);
623  nApsPerRound = std::max<uint16_t> (nApsPerRound, 1);
624 
625  for (uint16_t round = 0; round < nRounds; round++)
626  {
627  for (unsigned int type = 0; type < 7; type++)
628  {
629  HeRu::RuType ruType = static_cast <HeRu::RuType> (type);
630  std::size_t nRus = HeRu::GetNRus (txChannelWidth, ruType);
631  std::set<uint8_t> txBss;
632  if (nRus > 0)
633  {
634  for (uint16_t i = 0; i < nApsPerRound; i++)
635  {
636  uint16_t ap = round + i * nRounds;
637  txBss.insert (ap);
638  Simulator::Schedule (m_time, &WifiPrimaryChannelsTest::SendHeTbPpdu, this,
639  ap, txChannelWidth, ruType, nRus);
640  }
641  // check that Trigger Frames and TB PPDUs were correctly received
642  Simulator::Schedule (m_time + m_triggerTxDuration + MicroSeconds (10), /* during SIFS */
644  this, txBss, txChannelWidth);
645  Simulator::Schedule (m_time + roundDuration, &WifiPrimaryChannelsTest::CheckReceivedMuPpdus,
646  this, txBss, txChannelWidth, ruType, nRus, /* isDlMu */ false);
647  m_time += roundDuration;
648  }
649  }
650  }
651  }
652 
653  // Trace PSDUs passed to the PHY on all devices
654  Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyTxPsduBegin",
656 
657  Simulator::Stop (m_time);
658  Simulator::Run ();
659 
660  Simulator::Destroy ();
661 }
662 
663 void
664 WifiPrimaryChannelsTest::SendDlSuPpdu (uint8_t bss, uint16_t txChannelWidth)
665 {
666  NS_LOG_INFO ("*** BSS " << +bss << " transmits on primary " << txChannelWidth << " MHz channel");
667 
668  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
669  auto staDev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (0));
670 
671  uint8_t bssColor = apDev->GetHeConfiguration ()->GetBssColor ();
672  WifiTxVector txVector = WifiTxVector (HePhy::GetHeMcs8 (), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, txChannelWidth, false, false, false, bssColor);
673  WifiMacHeader hdr;
675  hdr.SetQosTid (0);
676  hdr.SetAddr1 (staDev->GetMac ()->GetAddress ());
677  hdr.SetAddr2 (apDev->GetMac ()->GetAddress ());
678  hdr.SetAddr3 (apDev->GetMac ()->GetBssid ());
679  hdr.SetSequenceNumber (1);
680  Ptr<WifiPsdu> psdu = Create<WifiPsdu> (Create<Packet> (1000), hdr);
681  apDev->GetPhy ()->Send (WifiConstPsduMap ({std::make_pair (SU_STA_ID, psdu)}), txVector);
682 }
683 
684 void
685 WifiPrimaryChannelsTest::SendDlMuPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
686 {
687  NS_LOG_INFO ("*** BSS " << +bss << " transmits on primary " << txChannelWidth
688  << " MHz channel a DL MU PPDU " << "addressed to " << nRus
689  << " stations (RU type: " << ruType << ")");
690 
691  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
692  uint8_t bssColor = apDev->GetHeConfiguration ()->GetBssColor ();
693 
694  WifiTxVector txVector = WifiTxVector (HePhy::GetHeMcs8 (), 0, WIFI_PREAMBLE_HE_MU, 800, 1, 1, 0, txChannelWidth, false, false, false, bssColor);
695  WifiMacHeader hdr;
697  hdr.SetQosTid (0);
698  hdr.SetAddr2 (apDev->GetMac ()->GetAddress ());
699  hdr.SetAddr3 (apDev->GetMac ()->GetBssid ());
700  hdr.SetSequenceNumber (1);
701 
702  WifiConstPsduMap psduMap;
703 
704  for (std::size_t i = 1; i <= nRus; i++)
705  {
706  std::size_t index = (txChannelWidth == 160 && i > nRus / 2 ? i - nRus / 2 : i);
707  bool primary80 = (txChannelWidth == 160 && i > nRus / 2 ? false : true);
708 
709  auto staDev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i - 1));
710  uint16_t staId = DynamicCast<StaWifiMac> (staDev->GetMac ())->GetAssociationId ();
711  txVector.SetHeMuUserInfo (staId, {{ruType, index, primary80}, HePhy::GetHeMcs8 (), 1});
712  hdr.SetAddr1 (staDev->GetMac ()->GetAddress ());
713  psduMap[staId] = Create<const WifiPsdu> (Create<Packet> (1000), hdr);
714  }
715  apDev->GetPhy ()->Send (psduMap, txVector);
716 }
717 
718 void
719 WifiPrimaryChannelsTest::SendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
720 {
721  NS_LOG_INFO ("*** BSS " << +bss << " transmits a Basic Trigger Frame");
722 
723  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
724 
725  m_trigger->GetHeader (0).SetAddr2 (apDev->GetMac ()->GetAddress ());
726 
727  apDev->GetPhy ()->Send (m_trigger, m_triggerTxVector);
728 
729  // schedule the transmission of HE TB PPDUs
730  Simulator::Schedule (m_triggerTxDuration + apDev->GetPhy ()->GetSifs (),
732  bss, txChannelWidth, ruType, nRus);
733 }
734 
735 void
736 WifiPrimaryChannelsTest::DoSendHeTbPpdu (uint8_t bss, uint16_t txChannelWidth, HeRu::RuType ruType, std::size_t nRus)
737 {
738  auto apDev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
739  uint8_t bssColor = apDev->GetHeConfiguration ()->GetBssColor ();
740 
741  WifiMacHeader hdr;
743  hdr.SetQosTid (0);
744  hdr.SetAddr1 (apDev->GetMac ()->GetAddress ());
745  hdr.SetAddr3 (apDev->GetMac ()->GetBssid ());
746  hdr.SetSequenceNumber (1);
747 
748  Time duration = Seconds (0);
749  uint16_t length = 0;
750 
751  for (std::size_t i = 1; i <= nRus; i++)
752  {
753  NS_LOG_INFO ("*** BSS " << +bss << " STA " << i - 1 << " transmits on primary "
754  << txChannelWidth << " MHz channel an HE TB PPDU (RU type: " << ruType << ")");
755 
756  std::size_t index = (txChannelWidth == 160 && i > nRus / 2 ? i - nRus / 2 : i);
757  bool primary80 = (txChannelWidth == 160 && i > nRus / 2 ? false : true);
758 
759  auto staDev = DynamicCast<WifiNetDevice> (m_staDevices[bss].Get (i - 1));
760  uint16_t staId = DynamicCast<StaWifiMac> (staDev->GetMac ())->GetAssociationId ();
761 
762  WifiTxVector txVector (HePhy::GetHeMcs8 (), 0, WIFI_PREAMBLE_HE_TB, 3200, 1, 1, 0, txChannelWidth, false, false, false, bssColor);
763  txVector.SetHeMuUserInfo (staId, {{ruType, index, primary80}, HePhy::GetHeMcs8 (), 1});
764 
765  hdr.SetAddr2 (staDev->GetMac ()->GetAddress ());
766  Ptr<const WifiPsdu> psdu = Create<const WifiPsdu> (Create<Packet> (1000), hdr);
767 
768  if (duration.IsZero ())
769  {
770  // calculate just once
771  duration = WifiPhy::CalculateTxDuration (psdu->GetSize (), txVector,
772  staDev->GetMac ()->GetWifiPhy ()->GetPhyBand (), staId);
773  length = HePhy::ConvertHeTbPpduDurationToLSigLength (duration,
774  staDev->GetMac ()->GetWifiPhy ()->GetPhyBand ());
775  }
776  txVector.SetLength (length);
777 
778  staDev->GetPhy ()->Send (WifiConstPsduMap {{staId, psdu}}, txVector);
779  }
780 }
781 
782 void
784 {
785  for (uint8_t bss = 0; bss < m_nBss; bss++)
786  {
787  auto dev = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss));
788  auto mac = DynamicCast<ApWifiMac> (dev->GetMac ());
789  NS_TEST_EXPECT_MSG_EQ (mac->GetStaList ().size (), m_nStationsPerBss,
790  "Not all the stations completed association");
791  }
792 }
793 
794 void
795 WifiPrimaryChannelsTest::CheckReceivedSuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth)
796 {
797  for (uint8_t bss = 0; bss < m_nBss; bss++)
798  {
799  if (txBss.find (bss) != txBss.end ())
800  {
801  // Every station in the BSS of an AP that transmitted the frame hears (i.e.,
802  // passes to the MAC) the frame
803  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
804  {
805  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true, "Station [" << +bss << "][" << +sta
806  << "] did not receive the SU frame on primary" << txChannelWidth << " channel");
807  }
808  // only the first station actually processed the frames
809  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (0), true, "Station [" << +bss << "][0]"
810  << " did not process the SU frame on primary" << txChannelWidth << " channel");
811  for (uint8_t sta = 1; sta < m_nStationsPerBss; sta++)
812  {
813  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false, "Station [" << +bss << "][" << +sta
814  << "] processed the SU frame on primary" << txChannelWidth << " channel");
815  }
816  }
817  else
818  {
819  // There was no transmission in this BSS. If BSS Color filtering is enabled or no frame
820  // transmission overlaps with the primary20 channel of this BSS, stations in this BSS
821  // did not hear any frame.
823  || std::none_of (txBss.begin (), txBss.end (),
824  [&](const uint8_t& txAp)
825  {
826  auto txApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (txAp))->GetPhy ();
827  auto thisApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss))->GetPhy ();
828  return txApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth)
829  == thisApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth);
830  }))
831  {
832  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
833  {
834  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false, "Station [" << +bss << "][" << +sta
835  << "] received the SU frame on primary" << txChannelWidth << " channel");
836  }
837  }
838  else
839  {
840  // all stations heard the frame but no station processed it
841  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
842  {
843  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true, "Station [" << +bss << "][" << +sta
844  << "] did not receive the SU frame on primary" << txChannelWidth << " channel");
845  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false, "Station [" << +bss << "][" << +sta
846  << "] processed the SU frame on primary" << txChannelWidth << " channel");
847  }
848  }
849  }
850  // reset bitmaps
851  m_received[bss].reset ();
852  m_processed[bss].reset ();
853  }
854 }
855 
856 void
857 WifiPrimaryChannelsTest::CheckReceivedMuPpdus (std::set<uint8_t> txBss, uint16_t txChannelWidth,
858  HeRu::RuType ruType, std::size_t nRus, bool isDlMu)
859 {
860  for (uint8_t bss = 0; bss < m_nBss; bss++)
861  {
862  if (txBss.find (bss) != txBss.end ())
863  {
864  // Due to AID filtering, only stations that are addressed by the MU PPDU do hear the frame
865  for (uint8_t sta = 0; sta < nRus; sta++)
866  {
867  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true,
868  (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
869  << " station [" << +bss << "][" << +sta << "] on primary"
870  << txChannelWidth << " channel, RU type " << ruType
871  << " was not received");
872  }
873  for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
874  {
875  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false,
876  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
877  << " transmitted on primary" << txChannelWidth
878  << " channel, RU type " << ruType << " was received "
879  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
880  << +sta << "]");
881  }
882  // only the addressed stations actually processed the frames
883  for (uint8_t sta = 0; sta < nRus; sta++)
884  {
885  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), true,
886  (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
887  << " station [" << +bss << "][" << +sta << "] on primary"
888  << txChannelWidth << " channel, RU type " << ruType
889  << " was not processed");
890  }
891  for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
892  {
893  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false,
894  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
895  << " transmitted on primary" << txChannelWidth
896  << " channel, RU type " << ruType << " was received "
897  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
898  << +sta << "] and processed");
899  }
900  }
901  else
902  {
903  // There was no transmission in this BSS. If BSS Color filtering is enabled or no frame
904  // transmission overlaps with the primary20 channel of this BSS, stations in this BSS
905  // did not hear any frame.
907  || std::none_of (txBss.begin (), txBss.end (),
908  [&](const uint8_t& txAp)
909  {
910  auto txApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (txAp))->GetPhy ();
911  auto thisApPhy = DynamicCast<WifiNetDevice> (m_apDevices.Get (bss))->GetPhy ();
912  return txApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth)
913  == thisApPhy->GetOperatingChannel ().GetPrimaryChannelIndex (txChannelWidth);
914  }))
915  {
916  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
917  {
918  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false,
919  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
920  << " transmitted on primary" << txChannelWidth
921  << " channel, RU type " << ruType << " was received "
922  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
923  << +sta << "]");
924  }
925  }
926  else
927  {
928  // stations having the same AID of the stations addressed by the MU PPDI received the frame
929  for (uint8_t sta = 0; sta < nRus; sta++)
930  {
931  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true,
932  (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
933  << " station [" << +bss << "][" << +sta << "] on primary"
934  << txChannelWidth << " channel, RU type " << ruType
935  << " was not received");
936  }
937  for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
938  {
939  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false,
940  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
941  << " transmitted on primary" << txChannelWidth
942  << " channel, RU type " << ruType << " was received "
943  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
944  << +sta << "]");
945  }
946  // no station processed the frame
947  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
948  {
949  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), false,
950  (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
951  << " transmitted on primary" << txChannelWidth
952  << " channel, RU type " << ruType << " was received "
953  << (isDlMu ? "by" : "from") << " station [" << +bss << "]["
954  << +sta << "] and processed");
955  }
956  }
957  }
958  // reset bitmaps
959  m_received[bss].reset ();
960  m_processed[bss].reset ();
961  }
962 }
963 
964 void
965 WifiPrimaryChannelsTest::CheckReceivedTriggerFrames (std::set<uint8_t> txBss, uint16_t txChannelWidth)
966 {
967  for (uint8_t bss = 0; bss < m_nBss; bss++)
968  {
969  if (txBss.find (bss) != txBss.end ())
970  {
971  // Every station in the BSS of an AP that transmitted the Trigger Frame hears (i.e.,
972  // passes to the MAC) and processes the frame
973  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
974  {
975  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), true, "Station [" << +bss << "][" << +sta
976  << "] did not receive the Trigger Frame soliciting a transmission on primary"
977  << txChannelWidth << " channel");
978  NS_TEST_EXPECT_MSG_EQ (m_processed[bss].test (sta), true, "Station [" << +bss << "][" << +sta
979  << "] did not process the Trigger Frame soliciting a transmission on primary"
980  << txChannelWidth << " channel");
981  }
982  }
983  else
984  {
985  // Given that a Trigger Frame is transmitted on the primary20 channel and all the
986  // primary20 channels are distinct, stations in other BSSes did not hear the frame
987  for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
988  {
989  NS_TEST_EXPECT_MSG_EQ (m_received[bss].test (sta), false, "Station [" << +bss << "][" << +sta
990  << "] received the Trigger Frame soliciting a transmission on primary"
991  << txChannelWidth << " channel");
992  }
993  }
994  // reset bitmaps
995  m_received[bss].reset ();
996  m_processed[bss].reset ();
997  }
998 }
999 
1000 
1008 {
1009 public:
1011 };
1012 
1014  : TestSuite ("wifi-primary-channels", UNIT)
1015 {
1016  // Test cases for 20 MHz can be added, but are not that useful (there would be a single BSS)
1017  AddTestCase (new WifiPrimaryChannelsTest (40, true), TestCase::QUICK);
1018  AddTestCase (new WifiPrimaryChannelsTest (40, false), TestCase::QUICK);
1019  AddTestCase (new WifiPrimaryChannelsTest (80, true), TestCase::EXTENSIVE);
1020  AddTestCase (new WifiPrimaryChannelsTest (80, false), TestCase::EXTENSIVE);
1021  AddTestCase (new WifiPrimaryChannelsTest (160, true), TestCase::TAKES_FOREVER);
1022  AddTestCase (new WifiPrimaryChannelsTest (160, false), TestCase::TAKES_FOREVER);
1023 }
1024 
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 DoSetup(void) override
Implementation to do any local setup required for this TestCase.
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:885
#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...
void DoRun(void) override
Implementation to actually run this TestCase.
Mac48Address GetAddr1(void) const
Return the address in the Address 1 field.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
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
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
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.
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.
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
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 a const reference to 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.