A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-primary-channels-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
9#include "ns3/ap-wifi-mac.h"
10#include "ns3/attribute-container.h"
11#include "ns3/boolean.h"
12#include "ns3/config.h"
13#include "ns3/ctrl-headers.h"
14#include "ns3/eht-phy.h"
15#include "ns3/enum.h"
16#include "ns3/he-configuration.h"
17#include "ns3/mobility-helper.h"
18#include "ns3/multi-model-spectrum-channel.h"
19#include "ns3/rng-seed-manager.h"
20#include "ns3/spectrum-wifi-helper.h"
21#include "ns3/sta-wifi-mac.h"
22#include "ns3/test.h"
23#include "ns3/tuple.h"
24#include "ns3/wifi-net-device.h"
25#include "ns3/wifi-psdu.h"
26
27#include <algorithm>
28#include <bitset>
29#include <sstream>
30
31using namespace ns3;
32
33NS_LOG_COMPONENT_DEFINE("WifiPrimaryChannelsTest");
34
35/**
36 * @ingroup wifi-test
37 * @ingroup tests
38 *
39 * @brief Test transmissions under different primary channel settings
40 *
41 * This test can be repeated for different widths of the operating channel. We
42 * configure as many BSSes as the number of distinct 20 MHz subchannels in the
43 * operating channel, so that each BSS is assigned a distinct primary20 channel.
44 * For each BSS, we test the transmission of SU PPDUs, DL MU PPDUs and HE TB PPDUs
45 * of all the widths (20 MHz, 40 MHz, etc.) allowed by the operating channel.
46 * Transmissions of a given type take place simultaneously in BSSes that do not
47 * operate on adjacent primary channels of the considered width (so that
48 * transmissions do not interfere with each other). It is also possible to
49 * select whether BSSes should be assigned (distinct) BSS colors or not.
50 */
52{
53 public:
54 /**
55 * Constructor
56 *
57 * @param standard the standard
58 * @param channelWidth the operating channel width
59 * @param useDistinctBssColors whether to set distinct BSS colors to BSSes
60 */
61 WifiPrimaryChannelsTest(WifiStandard standard, MHz_u channelWidth, bool useDistinctBssColors);
62
63 /**
64 * Callback invoked when PHY receives a PSDU to transmit. Used to print
65 * transmitted PSDUs for debug purposes.
66 *
67 * @param context the context
68 * @param psduMap the PSDU map
69 * @param txVector the TX vector
70 * @param txPowerW the tx power in Watts
71 */
72 void Transmit(std::string context,
73 WifiConstPsduMap psduMap,
74 WifiTxVector txVector,
75 double txPowerW);
76 /**
77 * Have the AP of the given BSS transmit a SU PPDU using the given
78 * transmission channel width
79 *
80 * @param bss the given BSS
81 * @param txChannelWidth the given transmission channel width
82 */
83 void SendDlSuPpdu(uint8_t bss, MHz_u txChannelWidth);
84 /**
85 * Have the AP of the given BSS transmit a MU PPDU using the given
86 * transmission channel width and RU type
87 *
88 * @param bss the given BSS
89 * @param txChannelWidth the given transmission channel width
90 * @param ruType the given RU type
91 * @param nRus the number of RUs
92 */
93 void SendDlMuPpdu(uint8_t bss, MHz_u txChannelWidth, RuType ruType, std::size_t nRus);
94 /**
95 * Have the AP of the given BSS transmit a Basic Trigger Frame. This method calls
96 * DoSendHeTbPpdu to actually have STAs transmit HE TB PPDUs using the given
97 * transmission channel width and RU type
98 *
99 * @param bss the given BSS
100 * @param txChannelWidth the given transmission channel width
101 * @param ruType the given RU type
102 * @param nRus the number of RUs
103 */
104 void SendHeTbPpdu(uint8_t bss, MHz_u txChannelWidth, RuType ruType, std::size_t nRus);
105 /**
106 * Have the STAs of the given BSS transmit an HE TB PPDU using the given
107 * transmission channel width and RU type
108 *
109 * @param bss the given BSS
110 * @param txChannelWidth the given transmission channel width
111 * @param ruType the given RU type
112 * @param nRus the number of RUs
113 */
114 void DoSendHeTbPpdu(uint8_t bss, MHz_u txChannelWidth, RuType ruType, std::size_t nRus);
115 /**
116 * Callback invoked when a station receives a DL PPDU.
117 *
118 * @param bss the BSS the receiving STA belongs to
119 * @param station the receiving station
120 * @param psdu the received PSDU
121 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
122 * @param txVector TxVector of the received PSDU
123 * @param perMpduStatus per MPDU reception status
124 */
125 void ReceiveDl(uint8_t bss,
126 uint8_t station,
128 RxSignalInfo rxSignalInfo,
129 const WifiTxVector& txVector,
130 const std::vector<bool>& perMpduStatus);
131 /**
132 * Callback invoked when an AP receives an UL PPDU.
133 *
134 * @param bss the BSS the receiving AP belongs to
135 * @param psdu the received PSDU
136 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
137 * @param txVector TxVector of the received PSDU
138 * @param perMpduStatus per MPDU reception status
139 */
140 void ReceiveUl(uint8_t bss,
142 RxSignalInfo rxSignalInfo,
143 const WifiTxVector& txVector,
144 const std::vector<bool>& perMpduStatus);
145 /**
146 * Check that all stations associated with an AP.
147 */
148 void CheckAssociation();
149 /**
150 * Check that (i) all stations belonging to the given BSSes received the SU PPDUs
151 * transmitted over the given channel width; and (ii) all stations belonging to
152 * the other BSSes did not receive any frame if BSS Color is set (due to BSS Color
153 * filtering) or if no transmission was performed on a channel adjacent to the one
154 * they operate on, otherwise.
155 *
156 * @param txBss the set of BSSes that transmitted an SU PPDU
157 * @param txChannelWidth the given transmission channel width
158 */
159 void CheckReceivedSuPpdus(std::set<uint8_t> txBss, MHz_u txChannelWidth);
160 /**
161 * Check that (i) all stations/APs belonging to the given BSSes received the DL/UL MU PPDUs
162 * transmitted over the given channel width and RU width; and (ii) stations/APs belonging to
163 * the other BSSes did not receive any frame if BSS Color is set (due to BSS Color
164 * filtering) or if no transmission addressed to/from stations with the same AID was
165 * performed on a channel adjacent to the one they operate on, otherwise.
166 *
167 * @param txBss the set of BSSes that transmitted an SU PPDU
168 * @param txChannelWidth the given transmission channel width
169 * @param ruType the given RU type
170 * @param nRus the number of RUs
171 * @param isDlMu true for DL MU PPDU, false for HE TB PPDU
172 */
173 void CheckReceivedMuPpdus(std::set<uint8_t> txBss,
174 MHz_u txChannelWidth,
175 RuType ruType,
176 std::size_t nRus,
177 bool isDlMu);
178 /**
179 * Check that (i) all stations belonging to the given BSSes received the transmitted
180 * Trigger Frame; and (ii) all stations belonging to the other BSSes did not receive
181 * any frame Trigger Frame (given that a Trigger Frame is transmitted on the primary20
182 * channel and all the primary20 channels are distinct).
183 *
184 * @param txBss the set of BSSes that transmitted a Trigger Frame
185 * @param txChannelWidth the given transmission channel width
186 */
187 void CheckReceivedTriggerFrames(std::set<uint8_t> txBss, MHz_u txChannelWidth);
188
189 private:
190 void DoSetup() override;
191 void DoRun() override;
192
194 MHz_u m_channelWidth; ///< operating channel width
195 bool m_useDistinctBssColors; ///< true to set distinct BSS colors to BSSes
196 uint8_t m_nBss; ///< number of BSSes
197 uint8_t m_nStationsPerBss; ///< number of stations per AP
198 std::vector<NetDeviceContainer> m_staDevices; ///< containers for stations' NetDevices
199 NetDeviceContainer m_apDevices; ///< container for AP's NetDevice
200 std::vector<std::bitset<144>> m_received; /**< whether the last packet transmitted to/from each
201 of the (up to 144 per BSS) stations was received */
202 std::vector<std::bitset<144>>
203 m_processed; /**< whether the last packet transmitted to/from each
204 of the (up to 144 per BSS) stations was processed */
205 Time m_time; ///< the time when the current action is executed
206 Ptr<WifiPsdu> m_trigger; ///< Basic Trigger Frame
207 WifiTxVector m_triggerTxVector; ///< TX vector for Basic Trigger Frame
208 Time m_triggerTxDuration; ///< TX duration for Basic Trigger Frame
209};
210
212 MHz_u channelWidth,
213 bool useDistinctBssColors)
214 : TestCase("Check correct transmissions for various primary channel settings"),
215 m_standard(standard),
216 m_channelWidth(channelWidth),
217 m_useDistinctBssColors(useDistinctBssColors)
218{
219}
220
221void
223 WifiConstPsduMap psduMap,
224 WifiTxVector txVector,
225 double txPowerW)
226{
227 for (const auto& psduPair : psduMap)
228 {
229 std::stringstream ss;
230
231 if (psduPair.first != SU_STA_ID)
232 {
233 ss << " STA-ID " << psduPair.first;
234 }
235 ss << " " << psduPair.second->GetHeader(0).GetTypeString() << " seq "
236 << psduPair.second->GetHeader(0).GetSequenceNumber() << " from "
237 << psduPair.second->GetAddr2() << " to " << psduPair.second->GetAddr1();
238 NS_LOG_INFO(ss.str());
239 }
240 NS_LOG_INFO(" TXVECTOR " << txVector);
241}
242
243void
245 uint8_t station,
247 RxSignalInfo rxSignalInfo,
248 const WifiTxVector& txVector,
249 const std::vector<bool>& perMpduStatus)
250{
251 if (psdu->GetNMpdus() == 1)
252 {
253 const WifiMacHeader& hdr = psdu->GetHeader(0);
254
255 if (hdr.IsQosData() || hdr.IsTrigger())
256 {
257 NS_LOG_INFO("RECEIVED BY BSS=" << +bss << " STA=" << +station << " " << *psdu);
258 // the MAC received a PSDU from the PHY
260 false,
261 "Station [" << +bss << "][" << +station
262 << "] received a frame twice");
263 m_received[bss].set(station);
264 // check if we are the intended destination of the PSDU
265 auto dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(station));
266 if ((hdr.IsQosData() && hdr.GetAddr1() == dev->GetMac()->GetAddress()) ||
267 (hdr.IsTrigger() && hdr.GetAddr1() == Mac48Address::GetBroadcast()))
268 {
270 false,
271 "Station [" << +bss << "][" << +station
272 << "] processed a frame twice");
273 m_processed[bss].set(station);
274 }
275 }
276 }
277}
278
279void
282 RxSignalInfo rxSignalInfo,
283 const WifiTxVector& txVector,
284 const std::vector<bool>& perMpduStatus)
285{
286 // if the BSS color is zero, this AP might receive the frame sent by another AP. Given that
287 // stations only send TB PPDUs, we ignore this frame if the TX vector is not UL MU.
288 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsQosData() && txVector.IsUlMu())
289 {
290 auto dev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
291
292 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
293 uint8_t station = staId - 1;
294 NS_LOG_INFO("RECEIVED FROM BSSID=" << psdu->GetHeader(0).GetAddr3() << " STA=" << +station
295 << " " << *psdu);
296 // the MAC received a PSDU containing a QoS data frame from the PHY
298 false,
299 "AP of BSS " << +bss << " received a frame from station " << +station
300 << " twice");
301 m_received[bss].set(station);
302 // check if we are the intended destination of the PSDU
303 if (psdu->GetHeader(0).GetAddr1() == dev->GetMac()->GetAddress())
304 {
306 false,
307 "AP of BSS " << +bss << " received a frame from station "
308 << +station << " twice");
309 m_processed[bss].set(station);
310 }
311 }
312}
313
314void
316{
319 int64_t streamNumber = 100;
320 uint8_t channelNum;
321
322 // we create as many stations per BSS as the number of 26-tone RUs in a channel
323 // of the configured width
324 switch (static_cast<uint16_t>(m_channelWidth))
325 {
326 case 20:
328 channelNum = 1;
329 break;
330 case 40:
332 channelNum = 3;
333 break;
334 case 80:
336 channelNum = 7;
337 break;
338 case 160:
340 channelNum = 15;
341 break;
342 case 320:
343 m_nStationsPerBss = 144;
344 channelNum = 31;
345 break;
346 default:
347 NS_ABORT_MSG("Channel width (" << m_channelWidth << ") not allowed");
348 }
349
350 // we create as many BSSes as the number of 20 MHz subchannels
352
353 NodeContainer wifiApNodes;
354 wifiApNodes.Create(m_nBss);
355
356 std::vector<NodeContainer> wifiStaNodes(m_nBss);
357 for (auto& container : wifiStaNodes)
358 {
359 container.Create(m_nStationsPerBss);
360 }
361
364 spectrumChannel->AddPropagationLossModel(lossModel);
367 spectrumChannel->SetPropagationDelayModel(delayModel);
368
370 phy.SetChannel(spectrumChannel);
371
372 WifiHelper wifi;
373 wifi.SetStandard(m_standard);
374 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager");
375
376 WifiMacHelper mac;
377 mac.SetType("ns3::StaWifiMac",
378 "Ssid",
379 SsidValue(Ssid("non-existent-ssid")),
380 "MaxMissedBeacons",
381 UintegerValue(20),
382 "WaitBeaconTimeout",
383 TimeValue(MicroSeconds(102400))); // same as BeaconInterval
384
386
387 // Each BSS uses a distinct primary20 channel
388 for (uint8_t bss = 0; bss < m_nBss; bss++)
389 {
390 channelValue.Set(
392 phy.Set("ChannelSettings", channelValue);
393
394 m_staDevices.push_back(wifi.Install(phy, mac, wifiStaNodes[bss]));
395 }
396
397 for (uint8_t bss = 0; bss < m_nBss; bss++)
398 {
399 channelValue.Set(
401 phy.Set("ChannelSettings", channelValue);
402
403 mac.SetType("ns3::ApWifiMac",
404 "Ssid",
405 SsidValue(Ssid("wifi-ssid-" + std::to_string(bss))),
406 "BeaconInterval",
407 TimeValue(MicroSeconds(102400)),
408 "EnableBeaconJitter",
409 BooleanValue(false));
410
411 m_apDevices.Add(wifi.Install(phy, mac, wifiApNodes.Get(bss)));
412 }
413
414 // Assign fixed streams to random variables in use
415 streamNumber = WifiHelper::AssignStreams(m_apDevices, streamNumber);
416 for (uint8_t bss = 0; bss < m_nBss; bss++)
417 {
418 streamNumber = WifiHelper::AssignStreams(m_staDevices[bss], streamNumber);
419 }
420
421 // set BSS color
423 {
424 for (uint8_t bss = 0; bss < m_nBss; bss++)
425 {
426 auto dev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
427 dev->GetHeConfiguration()->m_bssColor = bss + 1;
428 }
429 }
430
431 MobilityHelper mobility;
433
434 positionAlloc->Add(Vector(0.0, 0.0, 0.0)); // all stations are co-located
435 mobility.SetPositionAllocator(positionAlloc);
436
437 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
438 mobility.Install(wifiApNodes);
439 for (uint8_t bss = 0; bss < m_nBss; bss++)
440 {
441 mobility.Install(wifiStaNodes[bss]);
442 }
443
444 m_received.resize(m_nBss);
445 m_processed.resize(m_nBss);
446
447 // pre-compute the Basic Trigger Frame to send
448 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(0));
449
450 WifiMacHeader hdr;
453 // Addr2 has to be set
454 hdr.SetSequenceNumber(1);
455
457 CtrlTriggerHeader trigger;
459 pkt->AddHeader(trigger);
460
462 0,
464 NanoSeconds(800),
465 1,
466 1,
467 0,
468 MHz_u{20},
469 false,
470 false,
471 false);
472 m_trigger = Create<WifiPsdu>(pkt, hdr);
473
476 apDev->GetMac()->GetWifiPhy()->GetPhyBand());
477}
478
479void
481{
482 // schedule association requests at different times. One station's SSID is
483 // set to the correct value before initialization, so that such a station
484 // starts the scanning procedure by looking for the correct SSID
486
487 // association can be done in parallel over the multiple BSSes
488 for (uint8_t bss = 0; bss < m_nBss; bss++)
489 {
490 dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(0));
491 dev->GetMac()->SetSsid(Ssid("wifi-ssid-" + std::to_string(bss)));
492
493 for (uint16_t i = 1; i < m_nStationsPerBss; i++)
494 {
495 dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(i));
498 dev->GetMac(),
499 Ssid("wifi-ssid-" + std::to_string(bss)));
500 }
501 }
502
503 // just before sending the beacon preceding the last association, increase the beacon
504 // interval (to the max allowed value) so that beacons do not interfere with data frames
505 for (uint8_t bss = 0; bss < m_nBss; bss++)
506 {
508 auto mac = DynamicCast<ApWifiMac>(dev->GetMac());
509
512 mac,
513 MicroSeconds(1024 * 65535));
514 }
515
516 m_time = (m_nStationsPerBss + 1) * MicroSeconds(102400);
517
519
520 // we are done with association. We now intercept frames received by the
521 // PHY layer on stations and APs, which will no longer be passed to the FEM.
522 for (uint8_t bss = 0; bss < m_nBss; bss++)
523 {
524 for (uint8_t i = 0; i < m_nStationsPerBss; i++)
525 {
526 auto dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(i));
528 m_time,
530 dev->GetPhy(),
532 }
533 auto dev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
536 dev->GetPhy(),
538 }
539
540 /*
541 * We start generating (downlink) SU PPDUs.
542 * First, APs operating on non-adjacent primary20 channels send a frame simultaneously
543 * in their primary20. This is done in two rounds. As an example, we consider the
544 * case of an 160 MHz operating channel:
545 *
546 * AP0 AP2 AP4 AP6
547 * |-----| |-----| |-----| |-----| |
548 *
549 * AP1 AP3 AP5 AP7
550 * | |-----| |-----| |-----| |-----|
551 *
552 * Then, we double the transmission channel width. We will have four rounds
553 * of transmissions. We avoid using adjacent channels to avoid interference
554 * among transmissions:
555 *
556 * AP0 AP4
557 * |-----------| |-----------| |
558 * AP1 AP5
559 * |-----------| |-----------| |
560 * AP2 AP6
561 * | |-----------| |-----------|
562 * AP3 AP7
563 * | |-----------| |-----------|
564 *
565 * We double the transmission channel width again. We will have eight rounds
566 * of transmissions:
567 *
568 * AP0
569 * |-----------------------| |
570 * AP1
571 * |-----------------------| |
572 * AP2
573 * |-----------------------| |
574 * AP3
575 * |-----------------------| |
576 * AP4
577 * | |-----------------------|
578 * AP5
579 * | |-----------------------|
580 * AP6
581 * | |-----------------------|
582 * AP7
583 * | |-----------------------|
584 *
585 * We double the transmission channel width again. We will have eight rounds
586 * of transmissions:
587 *
588 * AP0
589 * |-----------------------------------------------|
590 * AP1
591 * |-----------------------------------------------|
592 * AP2
593 * |-----------------------------------------------|
594 * AP3
595 * |-----------------------------------------------|
596 * AP4
597 * |-----------------------------------------------|
598 * AP5
599 * |-----------------------------------------------|
600 * AP6
601 * |-----------------------------------------------|
602 * AP7
603 * |-----------------------------------------------|
604 *
605 * The transmission channel width reached the operating channel width, we are done.
606 */
607
608 Time roundDuration = MilliSeconds(5); // upper bound on the duration of a round
609
610 // To have simultaneous transmissions on adjacent channels, just initialize
611 // nRounds to 1 and nApsPerRound to m_channelWidth / 20. Of course, the test
612 // will fail because some stations will not receive some frames due to interference
613 uint16_t nRounds = 2;
614 uint16_t nApsPerRound = Count20MHzSubchannels(m_channelWidth) / 2;
615 for (MHz_u txChannelWidth{20}; txChannelWidth <= m_channelWidth;
616 txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
617 {
618 nRounds = std::min<uint16_t>(nRounds, m_nBss);
619 nApsPerRound = std::max<uint16_t>(nApsPerRound, 1);
620
621 for (uint16_t round = 0; round < nRounds; round++)
622 {
623 std::set<uint8_t> txBss;
624
625 for (uint16_t i = 0; i < nApsPerRound; i++)
626 {
627 uint16_t ap = round + i * nRounds;
628 txBss.insert(ap);
631 this,
632 ap,
633 txChannelWidth);
634 }
635 // check that the SU frames were correctly received
636 Simulator::Schedule(m_time + roundDuration,
638 this,
639 txBss,
640 txChannelWidth);
641 m_time += roundDuration;
642 }
643 }
644
645 /*
646 * Repeat the same scheme as before with DL MU transmissions. For each transmission
647 * channel width, every round is repeated as many times as the number of ways in
648 * which we can partition the transmission channel width in equal sized RUs.
649 */
650 nRounds = 2;
651 nApsPerRound = Count20MHzSubchannels(m_channelWidth) / 2;
653 for (MHz_u txChannelWidth{20}; txChannelWidth <= m_channelWidth;
654 txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
655 {
656 nRounds = std::min<uint16_t>(nRounds, m_nBss);
657 nApsPerRound = std::max<uint16_t>(nApsPerRound, 1);
658
659 for (uint16_t round = 0; round < nRounds; round++)
660 {
661 for (uint32_t type = 0; type < static_cast<uint32_t>(WifiRu::GetMaxRuType(mc)); ++type)
662 {
663 auto ruType = static_cast<RuType>(type);
664 std::size_t nRus = WifiRu::GetNRus(txChannelWidth, ruType, mc);
665 std::set<uint8_t> txBss;
666 if (nRus > 0)
667 {
668 for (uint16_t i = 0; i < nApsPerRound; i++)
669 {
670 uint16_t ap = round + i * nRounds;
671 txBss.insert(ap);
674 this,
675 ap,
676 txChannelWidth,
677 ruType,
678 nRus);
679 }
680 // check that the MU frame was correctly received
681 Simulator::Schedule(m_time + roundDuration,
683 this,
684 txBss,
685 txChannelWidth,
686 ruType,
687 nRus,
688 /* isDlMu */ true);
689 m_time += roundDuration;
690 }
691 }
692 }
693 }
694
695 /*
696 * Repeat the same scheme as before with UL MU transmissions. For each transmission
697 * channel width, every round is repeated as many times as the number of ways in
698 * which we can partition the transmission channel width in equal sized RUs.
699 */
700 nRounds = 2;
701 nApsPerRound = Count20MHzSubchannels(m_channelWidth) / 2;
702 for (MHz_u txChannelWidth{20}; txChannelWidth <= m_channelWidth;
703 txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
704 {
705 nRounds = std::min<uint16_t>(nRounds, m_nBss);
706 nApsPerRound = std::max<uint16_t>(nApsPerRound, 1);
707
708 for (uint16_t round = 0; round < nRounds; round++)
709 {
710 for (uint32_t type = 0; type < static_cast<uint32_t>(WifiRu::GetMaxRuType(mc)); ++type)
711 {
712 auto ruType = static_cast<RuType>(type);
713 std::size_t nRus = WifiRu::GetNRus(txChannelWidth, ruType, mc);
714 std::set<uint8_t> txBss;
715 if (nRus > 0)
716 {
717 for (uint16_t i = 0; i < nApsPerRound; i++)
718 {
719 uint16_t ap = round + i * nRounds;
720 txBss.insert(ap);
723 this,
724 ap,
725 txChannelWidth,
726 ruType,
727 nRus);
728 }
729 // check that Trigger Frames and TB PPDUs were correctly received
731 MicroSeconds(10), /* during SIFS */
733 this,
734 txBss,
735 txChannelWidth);
736 Simulator::Schedule(m_time + roundDuration,
738 this,
739 txBss,
740 txChannelWidth,
741 ruType,
742 nRus,
743 /* isDlMu */ false);
744 m_time += roundDuration;
745 }
746 }
747 }
748 }
749
750 // Trace PSDUs passed to the PHY on all devices
751 Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyTxPsduBegin",
753
756
758}
759
760void
762{
763 NS_LOG_INFO("*** BSS " << +bss << " transmits on primary " << txChannelWidth << " MHz channel");
764
765 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
766 auto staDev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(0));
767
768 uint8_t bssColor = apDev->GetHeConfiguration()->m_bssColor;
769 auto txVector = WifiTxVector(
771 0,
773 NanoSeconds(800),
774 1,
775 1,
776 0,
777 txChannelWidth,
778 false,
779 false,
780 false,
781 bssColor);
782 WifiMacHeader hdr;
784 hdr.SetQosTid(0);
785 hdr.SetAddr1(staDev->GetMac()->GetAddress());
786 hdr.SetAddr2(apDev->GetMac()->GetAddress());
787 hdr.SetAddr3(apDev->GetMac()->GetBssid(0));
788 hdr.SetSequenceNumber(1);
790 apDev->GetPhy()->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
791}
792
793void
795 MHz_u txChannelWidth,
796 RuType ruType,
797 std::size_t nRus)
798{
799 NS_LOG_INFO("*** BSS " << +bss << " transmits on primary " << txChannelWidth
800 << " MHz channel a DL MU PPDU "
801 << "addressed to " << nRus << " stations (RU type: " << ruType << ")");
802
803 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
804 uint8_t bssColor = apDev->GetHeConfiguration()->m_bssColor;
805
806 auto txVector = WifiTxVector(
808 0,
810 NanoSeconds(800),
811 1,
812 1,
813 0,
814 txChannelWidth,
815 false,
816 false,
817 false,
818 bssColor);
820 {
821 txVector.SetEhtPpduType(0);
822 }
823 WifiMacHeader hdr;
825 hdr.SetQosTid(0);
826 hdr.SetAddr2(apDev->GetMac()->GetAddress());
827 hdr.SetAddr3(apDev->GetMac()->GetBssid(0));
828 hdr.SetSequenceNumber(1);
829
830 WifiConstPsduMap psduMap;
831
832 auto numRus{nRus};
834 {
835 // for the loop, take undefined RUs for EHT into account
836 numRus += (txChannelWidth / MHz_u{80});
837 }
838 std::size_t staIdx{0};
839 for (std::size_t i = 1; i <= numRus; ++i)
840 {
841 auto primary80OrLow80{true};
842 auto primary160{true};
843 auto index{i};
844 if (txChannelWidth > MHz_u{80})
845 {
847 {
848 index = HeRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
849 primary80OrLow80 = HeRu::GetPrimary80MHzFlag(txChannelWidth,
850 ruType,
851 i,
852 apDev->GetPhy()->GetPrimary20Index());
853 }
854 else
855 {
856 index = EhtRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
857 const auto& [p160, p80OrLow80] =
858 EhtRu::GetPrimaryFlags(txChannelWidth,
859 ruType,
860 i,
861 apDev->GetPhy()->GetPrimary20Index());
862 primary160 = p160;
863 primary80OrLow80 = p80OrLow80;
864 }
865 }
866 if ((m_standard != WIFI_STANDARD_80211ax) && (txChannelWidth >= MHz_u{80}) &&
867 (ruType == RuType::RU_26_TONE) && (index == 19))
868 {
869 // skip undefined RU for EHT
870 continue;
871 }
872 const auto ru =
874 ? WifiRu::RuSpec(HeRu::RuSpec{ruType, index, primary80OrLow80})
875 : WifiRu::RuSpec(EhtRu::RuSpec{ruType, index, primary160, primary80OrLow80});
876
877 NS_ABORT_IF((bss >= m_staDevices.size()) || (staIdx >= m_staDevices[bss].GetN()));
878 auto staDev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(staIdx));
879 ++staIdx;
880
881 NS_ABORT_IF(!staDev);
882 const auto staId = DynamicCast<StaWifiMac>(staDev->GetMac())->GetAssociationId();
883
884 txVector.SetHeMuUserInfo(staId, {ru, 8, 1});
885 hdr.SetAddr1(staDev->GetMac()->GetAddress());
886 psduMap[staId] = Create<const WifiPsdu>(Create<Packet>(1000), hdr);
887 }
888 txVector.SetSigBMode(VhtPhy::GetVhtMcs5());
889
890 apDev->GetPhy()->Send(psduMap, txVector);
891}
892
893void
895 MHz_u txChannelWidth,
896 RuType ruType,
897 std::size_t nRus)
898{
899 NS_LOG_INFO("*** BSS " << +bss << " transmits a Basic Trigger Frame");
900
901 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
902
903 m_trigger->GetHeader(0).SetAddr2(apDev->GetMac()->GetAddress());
904
905 apDev->GetPhy()->Send(m_trigger, m_triggerTxVector);
906
907 // schedule the transmission of HE TB PPDUs
908 Simulator::Schedule(m_triggerTxDuration + apDev->GetPhy()->GetSifs(),
910 this,
911 bss,
912 txChannelWidth,
913 ruType,
914 nRus);
915}
916
917void
919 MHz_u txChannelWidth,
920 RuType ruType,
921 std::size_t nRus)
922{
923 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
924 uint8_t bssColor = apDev->GetHeConfiguration()->m_bssColor;
925
926 WifiMacHeader hdr;
928 hdr.SetQosTid(0);
929 hdr.SetAddr1(apDev->GetMac()->GetAddress());
930 hdr.SetAddr3(apDev->GetMac()->GetBssid(0));
931 hdr.SetSequenceNumber(1);
932
933 Time duration;
934 uint16_t length = 0;
935 WifiTxVector trigVector(
937 0,
939 NanoSeconds(3200),
940 1,
941 1,
942 0,
943 txChannelWidth,
944 false,
945 false,
946 false,
947 bssColor);
948
949 auto numRus{nRus};
951 {
952 // for the loop, take undefined RUs for EHT into account
953 numRus += (txChannelWidth / MHz_u{80});
954 }
955 std::size_t staIdx{0};
956 for (std::size_t i = 1; i <= numRus; ++i)
957 {
958 NS_LOG_INFO("*** BSS " << +bss << " STA " << i - 1 << " transmits on primary "
959 << txChannelWidth
960 << " MHz channel an HE TB PPDU (RU type: " << ruType << ")");
961
962 auto primary80OrLow80{true};
963 auto primary160{true};
964 auto index{i};
965 if (txChannelWidth > MHz_u{80})
966 {
968 {
969 index = HeRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
970 primary80OrLow80 = HeRu::GetPrimary80MHzFlag(txChannelWidth,
971 ruType,
972 i,
973 apDev->GetPhy()->GetPrimary20Index());
974 }
975 else
976 {
977 index = EhtRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
978 const auto& [p160, p80OrLow80] =
979 EhtRu::GetPrimaryFlags(txChannelWidth,
980 ruType,
981 i,
982 apDev->GetPhy()->GetPrimary20Index());
983 primary160 = p160;
984 primary80OrLow80 = p80OrLow80;
985 }
986 }
987 if ((m_standard != WIFI_STANDARD_80211ax) && (txChannelWidth >= MHz_u{80}) &&
988 (ruType == RuType::RU_26_TONE) && (index == 19))
989 {
990 // skip undefined RU for EHT
991 continue;
992 }
993 const auto ru =
995 ? WifiRu::RuSpec(HeRu::RuSpec{ruType, index, primary80OrLow80})
996 : WifiRu::RuSpec(EhtRu::RuSpec{ruType, index, primary160, primary80OrLow80});
997
998 NS_ABORT_IF((bss >= m_staDevices.size()) || (staIdx >= m_staDevices[bss].GetN()));
999 auto staDev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(staIdx));
1000 ++staIdx;
1001
1002 const auto staId = DynamicCast<StaWifiMac>(staDev->GetMac())->GetAssociationId();
1003 WifiTxVector txVector(
1005 0,
1007 NanoSeconds(3200),
1008 1,
1009 1,
1010 0,
1011 txChannelWidth,
1012 false,
1013 false,
1014 false,
1015 bssColor);
1016 txVector.SetHeMuUserInfo(staId, {ru, 8, 1});
1017 trigVector.SetHeMuUserInfo(staId, {ru, 8, 1});
1018
1019 hdr.SetAddr2(staDev->GetMac()->GetAddress());
1021
1022 if (duration.IsZero())
1023 {
1024 // calculate just once
1025 duration = WifiPhy::CalculateTxDuration(psdu->GetSize(),
1026 txVector,
1027 staDev->GetMac()->GetWifiPhy()->GetPhyBand(),
1028 staId);
1029 std::tie(length, duration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
1030 duration,
1031 txVector,
1032 staDev->GetMac()->GetWifiPhy()->GetPhyBand());
1033 }
1034 txVector.SetLength(length);
1035
1036 staDev->GetPhy()->Send(WifiConstPsduMap{{staId, psdu}}, txVector);
1037 }
1038
1039 // AP's PHY expects to receive a TRIGVECTOR (just once)
1040 trigVector.SetLength(length);
1041 auto apHePhy = std::static_pointer_cast<HePhy>(apDev->GetPhy()->GetLatestPhyEntity());
1042 apHePhy->SetTrigVector(trigVector, duration);
1043}
1044
1045void
1047{
1048 for (uint8_t bss = 0; bss < m_nBss; bss++)
1049 {
1050 auto dev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
1051 auto mac = DynamicCast<ApWifiMac>(dev->GetMac());
1052 NS_TEST_EXPECT_MSG_EQ(mac->GetStaList(SINGLE_LINK_OP_ID).size(),
1054 "Not all the stations completed association");
1055 }
1056}
1057
1058void
1059WifiPrimaryChannelsTest::CheckReceivedSuPpdus(std::set<uint8_t> txBss, MHz_u txChannelWidth)
1060{
1061 for (uint8_t bss = 0; bss < m_nBss; bss++)
1062 {
1063 if (txBss.find(bss) != txBss.end())
1064 {
1065 // Every station in the BSS of an AP that transmitted the frame hears (i.e.,
1066 // passes to the MAC) the frame
1067 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1068 {
1070 true,
1071 "Station [" << +bss << "][" << +sta
1072 << "] did not receive the SU frame on primary"
1073 << txChannelWidth << " channel");
1074 }
1075 // only the first station actually processed the frames
1077 true,
1078 "Station [" << +bss << "][0]"
1079 << " did not process the SU frame on primary"
1080 << txChannelWidth << " channel");
1081 for (uint8_t sta = 1; sta < m_nStationsPerBss; sta++)
1082 {
1084 false,
1085 "Station [" << +bss << "][" << +sta
1086 << "] processed the SU frame on primary"
1087 << txChannelWidth << " channel");
1088 }
1089 }
1090 else
1091 {
1092 // There was no transmission in this BSS. If BSS Color filtering is enabled or no frame
1093 // transmission overlaps with the primary20 channel of this BSS, stations in this BSS
1094 // did not hear any frame.
1096 std::none_of(txBss.begin(), txBss.end(), [&](const uint8_t& txAp) {
1097 auto txApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(txAp))->GetPhy();
1098 auto thisApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss))->GetPhy();
1099 return txApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth) ==
1100 thisApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth);
1101 }))
1102 {
1103 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1104 {
1106 false,
1107 "Station [" << +bss << "][" << +sta
1108 << "] received the SU frame on primary"
1109 << txChannelWidth << " channel");
1110 }
1111 }
1112 else
1113 {
1114 // all stations heard the frame but no station processed it
1115 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1116 {
1118 true,
1119 "Station [" << +bss << "][" << +sta
1120 << "] did not receive the SU frame on primary"
1121 << txChannelWidth << " channel");
1123 false,
1124 "Station [" << +bss << "][" << +sta
1125 << "] processed the SU frame on primary"
1126 << txChannelWidth << " channel");
1127 }
1128 }
1129 }
1130 // reset bitmaps
1131 m_received[bss].reset();
1132 m_processed[bss].reset();
1133 }
1134}
1135
1136void
1138 MHz_u txChannelWidth,
1139 RuType ruType,
1140 std::size_t nRus,
1141 bool isDlMu)
1142{
1143 for (uint8_t bss = 0; bss < m_nBss; bss++)
1144 {
1145 if (txBss.find(bss) != txBss.end())
1146 {
1147 // There was a transmission in this BSS.
1148 // [DL] Due to AID filtering, only stations that are addressed by the MU PPDU do hear
1149 // the frame [UL] The AP hears a TB PPDU sent by all and only the solicited stations
1150 for (std::size_t sta = 0; sta < nRus; sta++)
1151 {
1153 m_received[bss].test(sta),
1154 true,
1155 (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
1156 << " station [" << +bss << "][" << +sta << "] on primary" << txChannelWidth
1157 << " channel, RU type " << ruType << " was not received");
1158 }
1159 for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
1160 {
1162 false,
1163 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1164 << " transmitted on primary" << txChannelWidth
1165 << " channel, RU type " << ruType << " was received "
1166 << (isDlMu ? "by" : "from") << " station [" << +bss
1167 << "][" << +sta << "]");
1168 }
1169 // [DL] Only the addressed stations actually processed the frames
1170 // [UL] The AP processed the frames sent by all and only the addressed stations
1171 for (std::size_t sta = 0; sta < nRus; sta++)
1172 {
1174 m_processed[bss].test(sta),
1175 true,
1176 (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
1177 << " station [" << +bss << "][" << +sta << "] on primary" << txChannelWidth
1178 << " channel, RU type " << ruType << " was not processed");
1179 }
1180 for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
1181 {
1183 false,
1184 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1185 << " transmitted on primary" << txChannelWidth
1186 << " channel, RU type " << ruType << " was received "
1187 << (isDlMu ? "by" : "from") << " station [" << +bss
1188 << "][" << +sta << "] and processed");
1189 }
1190 }
1191 else
1192 {
1193 // There was no transmission in this BSS.
1194 // [DL] If BSS Color filtering is enabled or no frame transmission overlaps with
1195 // the primary20 channel of this BSS, stations in this BSS did not hear any frame.
1196 // [UL] The AP did not hear any TB PPDU because no TRIGVECTOR was passed to the PHY
1197 if (!isDlMu || m_useDistinctBssColors ||
1198 std::none_of(txBss.begin(), txBss.end(), [&](const uint8_t& txAp) {
1199 auto txApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(txAp))->GetPhy();
1200 auto thisApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss))->GetPhy();
1201 return txApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth) ==
1202 thisApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth);
1203 }))
1204 {
1205 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1206 {
1208 false,
1209 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1210 << " transmitted on primary" << txChannelWidth
1211 << " channel, RU type " << ruType << " was received "
1212 << (isDlMu ? "by" : "from") << " station [" << +bss
1213 << "][" << +sta << "]");
1214 }
1215 }
1216 else
1217 {
1218 // [DL] Stations having the same AID of the stations addressed by the MU PPDU
1219 // received the frame
1220 for (std::size_t sta = 0; sta < nRus; sta++)
1221 {
1223 m_received[bss].test(sta),
1224 true,
1225 (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
1226 << " station [" << +bss << "][" << +sta << "] on primary"
1227 << txChannelWidth << " channel, RU type " << ruType
1228 << " was not received");
1229 }
1230 for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
1231 {
1233 false,
1234 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1235 << " transmitted on primary" << txChannelWidth
1236 << " channel, RU type " << ruType << " was received "
1237 << (isDlMu ? "by" : "from") << " station [" << +bss
1238 << "][" << +sta << "]");
1239 }
1240 // no station processed the frame
1241 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1242 {
1244 false,
1245 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1246 << " transmitted on primary" << txChannelWidth
1247 << " channel, RU type " << ruType << " was received "
1248 << (isDlMu ? "by" : "from") << " station [" << +bss
1249 << "][" << +sta << "] and processed");
1250 }
1251 }
1252 }
1253 // reset bitmaps
1254 m_received[bss].reset();
1255 m_processed[bss].reset();
1256 }
1257}
1258
1259void
1260WifiPrimaryChannelsTest::CheckReceivedTriggerFrames(std::set<uint8_t> txBss, MHz_u txChannelWidth)
1261{
1262 for (uint8_t bss = 0; bss < m_nBss; bss++)
1263 {
1264 if (txBss.find(bss) != txBss.end())
1265 {
1266 // Every station in the BSS of an AP that transmitted the Trigger Frame hears (i.e.,
1267 // passes to the MAC) and processes the frame
1268 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1269 {
1271 true,
1272 "Station [" << +bss << "][" << +sta
1273 << "] did not receive the Trigger Frame "
1274 "soliciting a transmission on primary"
1275 << txChannelWidth << " channel");
1277 true,
1278 "Station [" << +bss << "][" << +sta
1279 << "] did not process the Trigger Frame "
1280 "soliciting a transmission on primary"
1281 << txChannelWidth << " channel");
1282 }
1283 }
1284 else
1285 {
1286 // Given that a Trigger Frame is transmitted on the primary20 channel and all the
1287 // primary20 channels are distinct, stations in other BSSes did not hear the frame
1288 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1289 {
1291 m_received[bss].test(sta),
1292 false,
1293 "Station ["
1294 << +bss << "][" << +sta
1295 << "] received the Trigger Frame soliciting a transmission on primary"
1296 << txChannelWidth << " channel");
1297 }
1298 }
1299 // reset bitmaps
1300 m_received[bss].reset();
1301 m_processed[bss].reset();
1302 }
1303}
1304
1305/**
1306 * @ingroup wifi-test
1307 * @ingroup tests
1308 *
1309 * @brief Test functions returning the indices of primary and secondary channels
1310 * of different width.
1311 */
1313{
1314 public:
1315 /**
1316 * Constructor
1317 */
1319 ~Wifi20MHzChannelIndicesTest() override = default;
1320
1321 /**
1322 * Check that the indices of the 20 MHz channels included in all the primary
1323 * and secondary channels are correct when setting the given primary20 channel.
1324 *
1325 * @param primary20 the index of the primary20 channel to configure
1326 * @param secondary20 the expected index of the secondary20 channel
1327 * @param primary40 the expected indices of the 20 MHz channels in the primary40 channel
1328 * @param secondary40 the expected indices of the 20 MHz channels in the secondary40 channel
1329 * @param primary80 the expected indices of the 20 MHz channels in the primary80 channel
1330 * @param secondary80 the expected indices of the 20 MHz channels in the secondary80 channel
1331 * @param primary160 the expected indices of the 20 MHz channels in the primary160 channel
1332 * @param secondary160 the expected indices of the 20 MHz channels in the secondary160 channel
1333 */
1334 void RunOne(uint8_t primary20,
1335 const std::set<uint8_t>& secondary20,
1336 const std::set<uint8_t>& primary40,
1337 const std::set<uint8_t>& secondary40,
1338 const std::set<uint8_t>& primary80,
1339 const std::set<uint8_t>& secondary80,
1340 const std::set<uint8_t>& primary160,
1341 const std::set<uint8_t>& secondary160);
1342
1343 private:
1344 void DoRun() override;
1345
1346 WifiPhyOperatingChannel m_channel; //!< operating channel
1347};
1348
1350 : TestCase("Check computation of primary and secondary channel indices")
1351{
1352}
1353
1354void
1356 const std::set<uint8_t>& secondary20,
1357 const std::set<uint8_t>& primary40,
1358 const std::set<uint8_t>& secondary40,
1359 const std::set<uint8_t>& primary80,
1360 const std::set<uint8_t>& secondary80,
1361 const std::set<uint8_t>& primary160,
1362 const std::set<uint8_t>& secondary160)
1363{
1364 auto printToStr = [](const std::set<uint8_t>& s) {
1365 std::stringstream ss;
1366 ss << "{";
1367 for (const auto& index : s)
1368 {
1369 ss << +index << " ";
1370 }
1371 ss << "}";
1372 return ss.str();
1373 };
1374
1375 m_channel.SetPrimary20Index(primary20);
1376
1377 auto actualPrimary20 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{20});
1378 NS_TEST_ASSERT_MSG_EQ((actualPrimary20 == std::set<uint8_t>{primary20}),
1379 true,
1380 "Expected Primary20 {" << +primary20 << "}"
1381 << " differs from actual "
1382 << printToStr(actualPrimary20));
1383
1384 auto actualSecondary20 = m_channel.GetAll20MHzChannelIndicesInSecondary(actualPrimary20);
1385 NS_TEST_ASSERT_MSG_EQ((actualSecondary20 == secondary20),
1386 true,
1387 "Expected Secondary20 " << printToStr(secondary20)
1388 << " differs from actual "
1389 << printToStr(actualSecondary20));
1390
1391 auto actualPrimary40 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{40});
1392 NS_TEST_ASSERT_MSG_EQ((actualPrimary40 == primary40),
1393 true,
1394 "Expected Primary40 " << printToStr(primary40) << " differs from actual "
1395 << printToStr(actualPrimary40));
1396
1397 auto actualSecondary40 = m_channel.GetAll20MHzChannelIndicesInSecondary(primary40);
1398 NS_TEST_ASSERT_MSG_EQ((actualSecondary40 == secondary40),
1399 true,
1400 "Expected Secondary40 " << printToStr(secondary40)
1401 << " differs from actual "
1402 << printToStr(actualSecondary40));
1403
1404 auto actualPrimary80 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{80});
1405 NS_TEST_ASSERT_MSG_EQ((actualPrimary80 == primary80),
1406 true,
1407 "Expected Primary80 " << printToStr(primary80) << " differs from actual "
1408 << printToStr(actualPrimary80));
1409
1410 auto actualSecondary80 = m_channel.GetAll20MHzChannelIndicesInSecondary(primary80);
1411 NS_TEST_ASSERT_MSG_EQ((actualSecondary80 == secondary80),
1412 true,
1413 "Expected Secondary80 " << printToStr(secondary80)
1414 << " differs from actual "
1415 << printToStr(actualSecondary80));
1416
1417 auto actualPrimary160 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{160});
1418 NS_TEST_ASSERT_MSG_EQ((actualPrimary160 == primary160),
1419 true,
1420 "Expected Primary160 " << printToStr(primary160)
1421 << " differs from actual "
1422 << printToStr(actualPrimary160));
1423
1424 auto actualSecondary160 = m_channel.GetAll20MHzChannelIndicesInSecondary(primary160);
1425 NS_TEST_ASSERT_MSG_EQ((actualSecondary160 == secondary160),
1426 true,
1427 "Expected Secondary160 " << printToStr(secondary160)
1428 << " differs from actual "
1429 << printToStr(actualSecondary160));
1430}
1431
1432void
1434{
1435 /* 20 MHz channel */
1437 RunOne(0, {}, {}, {}, {}, {}, {}, {});
1438
1439 /* 40 MHz channel */
1441 RunOne(0, {1}, {0, 1}, {}, {}, {}, {}, {});
1442 RunOne(1, {0}, {0, 1}, {}, {}, {}, {}, {});
1443
1444 /* 80 MHz channel */
1446 RunOne(0, {1}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {}, {}, {});
1447 RunOne(1, {0}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {}, {}, {});
1448 RunOne(2, {3}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {}, {}, {});
1449 RunOne(3, {2}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {}, {}, {});
1450
1451 /* 160 MHz channel */
1453 RunOne(0, {1}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1454 RunOne(1, {0}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1455 RunOne(2, {3}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1456 RunOne(3, {2}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1457 RunOne(4, {5}, {4, 5}, {6, 7}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1458 RunOne(5, {4}, {4, 5}, {6, 7}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1459 RunOne(6, {7}, {6, 7}, {4, 5}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1460 RunOne(7, {6}, {6, 7}, {4, 5}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1461
1462 /* 320 MHz channel */
1464 RunOne(0,
1465 {1},
1466 {0, 1},
1467 {2, 3},
1468 {0, 1, 2, 3},
1469 {4, 5, 6, 7},
1470 {0, 1, 2, 3, 4, 5, 6, 7},
1471 {8, 9, 10, 11, 12, 13, 14, 15});
1472 RunOne(1,
1473 {0},
1474 {0, 1},
1475 {2, 3},
1476 {0, 1, 2, 3},
1477 {4, 5, 6, 7},
1478 {0, 1, 2, 3, 4, 5, 6, 7},
1479 {8, 9, 10, 11, 12, 13, 14, 15});
1480 RunOne(2,
1481 {3},
1482 {2, 3},
1483 {0, 1},
1484 {0, 1, 2, 3},
1485 {4, 5, 6, 7},
1486 {0, 1, 2, 3, 4, 5, 6, 7},
1487 {8, 9, 10, 11, 12, 13, 14, 15});
1488 RunOne(3,
1489 {2},
1490 {2, 3},
1491 {0, 1},
1492 {0, 1, 2, 3},
1493 {4, 5, 6, 7},
1494 {0, 1, 2, 3, 4, 5, 6, 7},
1495 {8, 9, 10, 11, 12, 13, 14, 15});
1496 RunOne(4,
1497 {5},
1498 {4, 5},
1499 {6, 7},
1500 {4, 5, 6, 7},
1501 {0, 1, 2, 3},
1502 {0, 1, 2, 3, 4, 5, 6, 7},
1503 {8, 9, 10, 11, 12, 13, 14, 15});
1504 RunOne(5,
1505 {4},
1506 {4, 5},
1507 {6, 7},
1508 {4, 5, 6, 7},
1509 {0, 1, 2, 3},
1510 {0, 1, 2, 3, 4, 5, 6, 7},
1511 {8, 9, 10, 11, 12, 13, 14, 15});
1512 RunOne(6,
1513 {7},
1514 {6, 7},
1515 {4, 5},
1516 {4, 5, 6, 7},
1517 {0, 1, 2, 3},
1518 {0, 1, 2, 3, 4, 5, 6, 7},
1519 {8, 9, 10, 11, 12, 13, 14, 15});
1520 RunOne(7,
1521 {6},
1522 {6, 7},
1523 {4, 5},
1524 {4, 5, 6, 7},
1525 {0, 1, 2, 3},
1526 {0, 1, 2, 3, 4, 5, 6, 7},
1527 {8, 9, 10, 11, 12, 13, 14, 15});
1528 RunOne(8,
1529 {9},
1530 {8, 9},
1531 {10, 11},
1532 {8, 9, 10, 11},
1533 {12, 13, 14, 15},
1534 {8, 9, 10, 11, 12, 13, 14, 15},
1535 {0, 1, 2, 3, 4, 5, 6, 7});
1536 RunOne(9,
1537 {8},
1538 {8, 9},
1539 {10, 11},
1540 {8, 9, 10, 11},
1541 {12, 13, 14, 15},
1542 {8, 9, 10, 11, 12, 13, 14, 15},
1543 {0, 1, 2, 3, 4, 5, 6, 7});
1544 RunOne(10,
1545 {11},
1546 {10, 11},
1547 {8, 9},
1548 {8, 9, 10, 11},
1549 {12, 13, 14, 15},
1550 {8, 9, 10, 11, 12, 13, 14, 15},
1551 {0, 1, 2, 3, 4, 5, 6, 7});
1552 RunOne(11,
1553 {10},
1554 {10, 11},
1555 {8, 9},
1556 {8, 9, 10, 11},
1557 {12, 13, 14, 15},
1558 {8, 9, 10, 11, 12, 13, 14, 15},
1559 {0, 1, 2, 3, 4, 5, 6, 7});
1560 RunOne(12,
1561 {13},
1562 {12, 13},
1563 {14, 15},
1564 {12, 13, 14, 15},
1565 {8, 9, 10, 11},
1566 {8, 9, 10, 11, 12, 13, 14, 15},
1567 {0, 1, 2, 3, 4, 5, 6, 7});
1568 RunOne(13,
1569 {12},
1570 {12, 13},
1571 {14, 15},
1572 {12, 13, 14, 15},
1573 {8, 9, 10, 11},
1574 {8, 9, 10, 11, 12, 13, 14, 15},
1575 {0, 1, 2, 3, 4, 5, 6, 7});
1576 RunOne(14,
1577 {15},
1578 {14, 15},
1579 {12, 13},
1580 {12, 13, 14, 15},
1581 {8, 9, 10, 11},
1582 {8, 9, 10, 11, 12, 13, 14, 15},
1583 {0, 1, 2, 3, 4, 5, 6, 7});
1584 RunOne(15,
1585 {14},
1586 {14, 15},
1587 {12, 13},
1588 {12, 13, 14, 15},
1589 {8, 9, 10, 11},
1590 {8, 9, 10, 11, 12, 13, 14, 15},
1591 {0, 1, 2, 3, 4, 5, 6, 7});
1592}
1593
1594/**
1595 * @ingroup wifi-test
1596 * @ingroup tests
1597 *
1598 * @brief wifi primary channels test suite
1599 */
1601{
1602 public:
1604};
1605
1607 : TestSuite("wifi-primary-channels", Type::UNIT)
1608{
1609 for (const auto standard : {WIFI_STANDARD_80211ax, WIFI_STANDARD_80211be})
1610 {
1611 // Test cases for 20 MHz can be added, but are not that useful (there would be a single BSS)
1612 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{40}, true),
1614 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{40}, false),
1616 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{80}, true),
1618 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{80}, false),
1620 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{160}, true),
1622 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{160}, false),
1624 }
1625 // TODO: activate WifiPrimaryChannelsTest for 320 MHz channels once PHY operations are optimized
1626 // to run faster, otherwise run-time is too long
1627 /*AddTestCase(new WifiPrimaryChannelsTest(WIFI_STANDARD_80211be, MHz_u{320}, true),
1628 TestCase::Duration::TAKES_FOREVER);
1629 AddTestCase(new WifiPrimaryChannelsTest(WIFI_STANDARD_80211be, MHz_u{320}, false),
1630 TestCase::Duration::TAKES_FOREVER);*/
1632}
1633
Test functions returning the indices of primary and secondary channels of different width.
void DoRun() override
Implementation to actually run this TestCase.
~Wifi20MHzChannelIndicesTest() override=default
void RunOne(uint8_t primary20, const std::set< uint8_t > &secondary20, const std::set< uint8_t > &primary40, const std::set< uint8_t > &secondary40, const std::set< uint8_t > &primary80, const std::set< uint8_t > &secondary80, const std::set< uint8_t > &primary160, const std::set< uint8_t > &secondary160)
Check that the indices of the 20 MHz channels included in all the primary and secondary channels are ...
WifiPhyOperatingChannel m_channel
operating channel
Test transmissions under different primary channel settings.
void CheckAssociation()
Check that all stations associated with an AP.
void CheckReceivedSuPpdus(std::set< uint8_t > txBss, MHz_u txChannelWidth)
Check that (i) all stations belonging to the given BSSes received the SU PPDUs transmitted over the g...
void CheckReceivedTriggerFrames(std::set< uint8_t > txBss, MHz_u txChannelWidth)
Check that (i) all stations belonging to the given BSSes received the transmitted Trigger Frame; and ...
void Transmit(std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when PHY receives a PSDU to transmit.
MHz_u m_channelWidth
operating channel width
void ReceiveUl(uint8_t bss, Ptr< const WifiPsdu > psdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &perMpduStatus)
Callback invoked when an AP receives an UL PPDU.
Ptr< WifiPsdu > m_trigger
Basic Trigger Frame.
void DoRun() override
Implementation to actually run this TestCase.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
Time m_time
the time when the current action is executed
Time m_triggerTxDuration
TX duration for Basic Trigger Frame.
void SendDlMuPpdu(uint8_t bss, MHz_u txChannelWidth, 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...
std::vector< std::bitset< 144 > > m_processed
whether the last packet transmitted to/from each of the (up to 144 per BSS) stations was processed
uint8_t m_nStationsPerBss
number of stations per AP
void ReceiveDl(uint8_t bss, uint8_t station, Ptr< const WifiPsdu > psdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &perMpduStatus)
Callback invoked when a station receives a DL PPDU.
std::vector< NetDeviceContainer > m_staDevices
containers for stations' NetDevices
void SendHeTbPpdu(uint8_t bss, MHz_u txChannelWidth, RuType ruType, std::size_t nRus)
Have the AP of the given BSS transmit a Basic Trigger Frame.
std::vector< std::bitset< 144 > > m_received
whether the last packet transmitted to/from each of the (up to 144 per BSS) stations was received
NetDeviceContainer m_apDevices
container for AP's NetDevice
WifiPrimaryChannelsTest(WifiStandard standard, MHz_u channelWidth, bool useDistinctBssColors)
Constructor.
void DoSendHeTbPpdu(uint8_t bss, MHz_u txChannelWidth, 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 CheckReceivedMuPpdus(std::set< uint8_t > txBss, MHz_u txChannelWidth, 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 ...
bool m_useDistinctBssColors
true to set distinct BSS colors to BSSes
WifiTxVector m_triggerTxVector
TX vector for Basic Trigger Frame.
void SendDlSuPpdu(uint8_t bss, MHz_u txChannelWidth)
Have the AP of the given BSS transmit a SU PPDU using the given transmission channel width.
wifi primary channels test suite
void SetBeaconInterval(Time interval)
void Set(const T &c)
Copy items from container c.
AttributeValue implementation for Boolean.
Definition boolean.h:26
Headers for Trigger frames.
void SetType(TriggerFrameType type)
Set the Trigger frame type.
static WifiMode GetEhtMcs8()
Return MCS 8 from EHT MCS values.
RU Specification.
Definition eht-ru.h:34
static std::pair< bool, bool > GetPrimaryFlags(MHz_u bw, RuType ruType, std::size_t phyIndex, uint8_t p20Index)
Get the primary flags of a given RU transmitted in a PPDU.
Definition eht-ru.cc:912
static std::size_t GetIndexIn80MHzSegment(MHz_u bw, RuType ruType, std::size_t phyIndex)
Get the index of a given RU transmitted in a PPDU within its 80 MHz segment.
Definition eht-ru.cc:945
static WifiMode GetHeMcs8()
Return MCS 8 from HE MCS values.
static std::pair< uint16_t, Time > ConvertHeTbPpduDurationToLSigLength(Time ppduDuration, const WifiTxVector &txVector, WifiPhyBand band)
Compute the L-SIG length value corresponding to the given HE TB PPDU duration.
Definition he-phy.cc:263
RU Specification.
Definition he-ru.h:37
static std::size_t GetIndexIn80MHzSegment(MHz_u bw, RuType ruType, std::size_t phyIndex)
Get the index of a given RU transmitted in a PPDU within its 80 MHz segment.
Definition he-ru.cc:475
static bool GetPrimary80MHzFlag(MHz_u bw, RuType ruType, std::size_t phyIndex, uint8_t p20Index)
Get the primary 80 MHz flag of a given RU transmitted in a PPDU.
Definition he-ru.cc:462
static Mac48Address GetBroadcast()
Helper class used to assign positions and mobility models to nodes.
holds a vector of ns3::NetDevice pointers
keep track of a set of node pointers.
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
static WifiMode GetOfdmRate6Mbps()
Return a WifiMode for OFDM at 6 Mbps.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:67
static void SetRun(uint64_t run)
Set the run number of simulation.
static void SetSeed(uint32_t seed)
Set the seed.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:131
static void Run()
Run the simulation.
Definition simulator.cc:167
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition simulator.cc:175
Make it easy to create and manage PHY objects for the spectrum model.
The IEEE 802.11 SSID Information Element.
Definition ssid.h:25
AttributeValue implementation for Ssid.
Definition ssid.h:85
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
@ EXTENSIVE
Medium length test.
Definition test.h:1055
@ QUICK
Fast test.
Definition test.h:1054
@ TAKES_FOREVER
Very long running test.
Definition test.h:1056
TestCase(const TestCase &)=delete
Type
Type of test.
Definition test.h:1257
@ UNIT
This test suite implements a Unit Test.
Definition test.h:1259
TestSuite(std::string name, Type type=Type::UNIT)
Construct a new test suite.
Definition test.cc:490
Simulation virtual time values and global simulation resolution.
Definition nstime.h:96
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:306
AttributeValue implementation for Time.
Definition nstime.h:1456
Hold an unsigned integer type.
Definition uinteger.h:34
static WifiMode GetVhtMcs5()
Return MCS 5 from VHT MCS values.
helps to create WifiNetDevice objects
static int64_t AssignStreams(NetDeviceContainer c, int64_t stream)
Assign a fixed random variable stream number to the random variables used by the PHY and MAC aspects ...
Implements the IEEE 802.11 MAC header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
bool IsTrigger() const
Return true if the header is a Trigger header.
void SetSequenceNumber(uint16_t seq)
Set the sequence number of the header.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
virtual void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
void SetQosTid(uint8_t tid)
Set the TID for the QoS header.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
bool IsQosData() const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
create MAC layers for a ns3::WifiNetDevice.
void SetSsid(Ssid ssid)
Definition wifi-mac.cc:527
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1568
std::list< WifiChannelConfig::TupleWithoutUnits > ChannelSegments
segments identifying an operating channel
Definition wifi-phy.h:952
AttributeContainerValue< ChannelTupleValue, ';'> ChannelSettingsValue
AttributeValue type of a ChannelSegments object.
Definition wifi-phy.h:959
void SetReceiveOkCallback(RxOkCallback callback)
Definition wifi-phy.cc:487
Class that keeps track of all information about the current PHY operating channel.
static std::size_t GetNRus(MHz_u bw, RuType ruType, WifiModulationClass mc)
Get the number of distinct RUs of the given type (number of tones) available in a PPDU of the given b...
Definition wifi-ru.cc:132
std::variant< HeRu::RuSpec, EhtRu::RuSpec > RuSpec
variant of the RU specification
Definition wifi-ru.h:27
static RuType GetMaxRuType(WifiModulationClass mc)
Get the maximum RU type supported by a given modulation class.
Definition wifi-ru.cc:63
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set the HE MU user-specific transmission information for the given STA-ID.
const HeMuUserInfoMap & GetHeMuUserInfoMap() const
Get a const reference to the map HE MU user-specific transmission information indexed by STA-ID.
void SetLength(uint16_t length)
Set the LENGTH field of the L-SIG.
void Connect(std::string path, const CallbackBase &cb)
Definition config.cc:970
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition abort.h:65
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:439
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition test.h:133
#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:240
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1393
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1405
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1381
WifiStandard
Identifies the IEEE 802.11 specifications that a Wifi device can be configured to use.
@ WIFI_STANDARD_80211be
@ WIFI_STANDARD_80211ax
@ WIFI_PREAMBLE_LONG
@ WIFI_PREAMBLE_EHT_TB
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PREAMBLE_EHT_MU
@ WIFI_PREAMBLE_HE_MU
@ WIFI_PREAMBLE_HE_SU
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:684
RuType
The different Resource Unit (RU) types.
Definition wifi-types.h:240
double MHz_u
MHz weak type.
Definition wifi-units.h:31
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:585
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:145
static constexpr uint8_t SINGLE_LINK_OP_ID
Link ID for single link operations (helps tracking places where correct link ID is to be used to supp...
Definition wifi-utils.h:295
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_QOSDATA
WifiModulationClass GetModulationClassForStandard(WifiStandard standard)
Return the modulation class corresponding to a given standard.
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
-ns3 Test suite for the ns3 wrapper script
RxSignalInfo structure containing info on the received signal.
Definition wifi-types.h:84
static WifiPrimaryChannelsTestSuite g_wifiPrimaryChannelsTestSuite
the test suite