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<74>> m_received; /**< whether the last packet transmitted to/from each
201 of the (up to 74 per BSS) stations was received */
202 std::vector<std::bitset<74>> m_processed; /**< whether the last packet transmitted to/from each
203 of the (up to 74 per BSS) stations was processed */
204 Time m_time; ///< the time when the current action is executed
205 Ptr<WifiPsdu> m_trigger; ///< Basic Trigger Frame
206 WifiTxVector m_triggerTxVector; ///< TX vector for Basic Trigger Frame
207 Time m_triggerTxDuration; ///< TX duration for Basic Trigger Frame
208};
209
211 MHz_u channelWidth,
212 bool useDistinctBssColors)
213 : TestCase("Check correct transmissions for various primary channel settings"),
214 m_standard(standard),
215 m_channelWidth(channelWidth),
216 m_useDistinctBssColors(useDistinctBssColors)
217{
218}
219
220void
222 WifiConstPsduMap psduMap,
223 WifiTxVector txVector,
224 double txPowerW)
225{
226 for (const auto& psduPair : psduMap)
227 {
228 std::stringstream ss;
229
230 if (psduPair.first != SU_STA_ID)
231 {
232 ss << " STA-ID " << psduPair.first;
233 }
234 ss << " " << psduPair.second->GetHeader(0).GetTypeString() << " seq "
235 << psduPair.second->GetHeader(0).GetSequenceNumber() << " from "
236 << psduPair.second->GetAddr2() << " to " << psduPair.second->GetAddr1();
237 NS_LOG_INFO(ss.str());
238 }
239 NS_LOG_INFO(" TXVECTOR " << txVector);
240}
241
242void
244 uint8_t station,
246 RxSignalInfo rxSignalInfo,
247 const WifiTxVector& txVector,
248 const std::vector<bool>& perMpduStatus)
249{
250 if (psdu->GetNMpdus() == 1)
251 {
252 const WifiMacHeader& hdr = psdu->GetHeader(0);
253
254 if (hdr.IsQosData() || hdr.IsTrigger())
255 {
256 NS_LOG_INFO("RECEIVED BY BSS=" << +bss << " STA=" << +station << " " << *psdu);
257 // the MAC received a PSDU from the PHY
259 false,
260 "Station [" << +bss << "][" << +station
261 << "] received a frame twice");
262 m_received[bss].set(station);
263 // check if we are the intended destination of the PSDU
264 auto dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(station));
265 if ((hdr.IsQosData() && hdr.GetAddr1() == dev->GetMac()->GetAddress()) ||
266 (hdr.IsTrigger() && hdr.GetAddr1() == Mac48Address::GetBroadcast()))
267 {
269 false,
270 "Station [" << +bss << "][" << +station
271 << "] processed a frame twice");
272 m_processed[bss].set(station);
273 }
274 }
275 }
276}
277
278void
281 RxSignalInfo rxSignalInfo,
282 const WifiTxVector& txVector,
283 const std::vector<bool>& perMpduStatus)
284{
285 // if the BSS color is zero, this AP might receive the frame sent by another AP. Given that
286 // stations only send TB PPDUs, we ignore this frame if the TX vector is not UL MU.
287 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsQosData() && txVector.IsUlMu())
288 {
290
291 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
292 uint8_t station = staId - 1;
293 NS_LOG_INFO("RECEIVED FROM BSSID=" << psdu->GetHeader(0).GetAddr3() << " STA=" << +station
294 << " " << *psdu);
295 // the MAC received a PSDU containing a QoS data frame from the PHY
297 false,
298 "AP of BSS " << +bss << " received a frame from station " << +station
299 << " twice");
300 m_received[bss].set(station);
301 // check if we are the intended destination of the PSDU
302 if (psdu->GetHeader(0).GetAddr1() == dev->GetMac()->GetAddress())
303 {
305 false,
306 "AP of BSS " << +bss << " received a frame from station "
307 << +station << " twice");
308 m_processed[bss].set(station);
309 }
310 }
311}
312
313void
315{
318 int64_t streamNumber = 100;
319 uint8_t channelNum;
320
321 // we create as many stations per BSS as the number of 26-tone RUs in a channel
322 // of the configured width
323 switch (static_cast<uint16_t>(m_channelWidth))
324 {
325 case 20:
327 channelNum = 1;
328 break;
329 case 40:
331 channelNum = 3;
332 break;
333 case 80:
335 channelNum = 7;
336 break;
337 case 160:
339 channelNum = 15;
340 break;
341 case 320:
342 m_nStationsPerBss = 144;
343 channelNum = 31;
344 break;
345 default:
346 NS_ABORT_MSG("Channel width (" << m_channelWidth << ") not allowed");
347 }
348
349 // we create as many BSSes as the number of 20 MHz subchannels
351
352 NodeContainer wifiApNodes;
353 wifiApNodes.Create(m_nBss);
354
355 std::vector<NodeContainer> wifiStaNodes(m_nBss);
356 for (auto& container : wifiStaNodes)
357 {
358 container.Create(m_nStationsPerBss);
359 }
360
363 spectrumChannel->AddPropagationLossModel(lossModel);
366 spectrumChannel->SetPropagationDelayModel(delayModel);
367
369 phy.SetChannel(spectrumChannel);
370
371 WifiHelper wifi;
372 wifi.SetStandard(m_standard);
373 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager");
374
375 WifiMacHelper mac;
376 mac.SetType("ns3::StaWifiMac",
377 "Ssid",
378 SsidValue(Ssid("non-existent-ssid")),
379 "MaxMissedBeacons",
380 UintegerValue(20),
381 "WaitBeaconTimeout",
382 TimeValue(MicroSeconds(102400))); // same as BeaconInterval
383
386 ';'>
387 channelValue;
388
389 // Each BSS uses a distinct primary20 channel
390 for (uint8_t bss = 0; bss < m_nBss; bss++)
391 {
392 channelValue.Set(
394 phy.Set("ChannelSettings", channelValue);
395
396 m_staDevices.push_back(wifi.Install(phy, mac, wifiStaNodes[bss]));
397 }
398
399 for (uint8_t bss = 0; bss < m_nBss; bss++)
400 {
401 channelValue.Set(
403 phy.Set("ChannelSettings", channelValue);
404
405 mac.SetType("ns3::ApWifiMac",
406 "Ssid",
407 SsidValue(Ssid("wifi-ssid-" + std::to_string(bss))),
408 "BeaconInterval",
409 TimeValue(MicroSeconds(102400)),
410 "EnableBeaconJitter",
411 BooleanValue(false));
412
413 m_apDevices.Add(wifi.Install(phy, mac, wifiApNodes.Get(bss)));
414 }
415
416 // Assign fixed streams to random variables in use
417 streamNumber = WifiHelper::AssignStreams(m_apDevices, streamNumber);
418 for (uint8_t bss = 0; bss < m_nBss; bss++)
419 {
420 streamNumber = WifiHelper::AssignStreams(m_staDevices[bss], streamNumber);
421 }
422
423 // set BSS color
425 {
426 for (uint8_t bss = 0; bss < m_nBss; bss++)
427 {
429 dev->GetHeConfiguration()->m_bssColor = bss + 1;
430 }
431 }
432
433 MobilityHelper mobility;
435
436 positionAlloc->Add(Vector(0.0, 0.0, 0.0)); // all stations are co-located
437 mobility.SetPositionAllocator(positionAlloc);
438
439 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
440 mobility.Install(wifiApNodes);
441 for (uint8_t bss = 0; bss < m_nBss; bss++)
442 {
443 mobility.Install(wifiStaNodes[bss]);
444 }
445
446 m_received.resize(m_nBss);
447 m_processed.resize(m_nBss);
448
449 // pre-compute the Basic Trigger Frame to send
451
452 WifiMacHeader hdr;
455 // Addr2 has to be set
456 hdr.SetSequenceNumber(1);
457
459 CtrlTriggerHeader trigger;
460 trigger.SetType(TriggerFrameType::BASIC_TRIGGER);
461 pkt->AddHeader(trigger);
462
464 0,
466 NanoSeconds(800),
467 1,
468 1,
469 0,
470 MHz_u{20},
471 false,
472 false,
473 false);
474 m_trigger = Create<WifiPsdu>(pkt, hdr);
475
478 apDev->GetMac()->GetWifiPhy()->GetPhyBand());
479}
480
481void
483{
484 // schedule association requests at different times. One station's SSID is
485 // set to the correct value before initialization, so that such a station
486 // starts the scanning procedure by looking for the correct SSID
488
489 // association can be done in parallel over the multiple BSSes
490 for (uint8_t bss = 0; bss < m_nBss; bss++)
491 {
492 dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(0));
493 dev->GetMac()->SetSsid(Ssid("wifi-ssid-" + std::to_string(bss)));
494
495 for (uint16_t i = 1; i < m_nStationsPerBss; i++)
496 {
497 dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(i));
500 dev->GetMac(),
501 Ssid("wifi-ssid-" + std::to_string(bss)));
502 }
503 }
504
505 // just before sending the beacon preceding the last association, increase the beacon
506 // interval (to the max allowed value) so that beacons do not interfere with data frames
507 for (uint8_t bss = 0; bss < m_nBss; bss++)
508 {
510 auto mac = DynamicCast<ApWifiMac>(dev->GetMac());
511
514 mac,
515 MicroSeconds(1024 * 65535));
516 }
517
518 m_time = (m_nStationsPerBss + 1) * MicroSeconds(102400);
519
521
522 // we are done with association. We now intercept frames received by the
523 // PHY layer on stations and APs, which will no longer be passed to the FEM.
524 for (uint8_t bss = 0; bss < m_nBss; bss++)
525 {
526 for (uint8_t i = 0; i < m_nStationsPerBss; i++)
527 {
528 auto dev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(i));
530 m_time,
532 dev->GetPhy(),
534 }
538 dev->GetPhy(),
540 }
541
542 /*
543 * We start generating (downlink) SU PPDUs.
544 * First, APs operating on non-adjacent primary20 channels send a frame simultaneously
545 * in their primary20. This is done in two rounds. As an example, we consider the
546 * case of an 160 MHz operating channel:
547 *
548 * AP0 AP2 AP4 AP6
549 * |-----| |-----| |-----| |-----| |
550 *
551 * AP1 AP3 AP5 AP7
552 * | |-----| |-----| |-----| |-----|
553 *
554 * Then, we double the transmission channel width. We will have four rounds
555 * of transmissions. We avoid using adjacent channels to avoid interference
556 * among transmissions:
557 *
558 * AP0 AP4
559 * |-----------| |-----------| |
560 * AP1 AP5
561 * |-----------| |-----------| |
562 * AP2 AP6
563 * | |-----------| |-----------|
564 * AP3 AP7
565 * | |-----------| |-----------|
566 *
567 * We double the transmission channel width again. We will have eight rounds
568 * of transmissions:
569 *
570 * AP0
571 * |-----------------------| |
572 * AP1
573 * |-----------------------| |
574 * AP2
575 * |-----------------------| |
576 * AP3
577 * |-----------------------| |
578 * AP4
579 * | |-----------------------|
580 * AP5
581 * | |-----------------------|
582 * AP6
583 * | |-----------------------|
584 * AP7
585 * | |-----------------------|
586 *
587 * We double the transmission channel width again. We will have eight rounds
588 * of transmissions:
589 *
590 * AP0
591 * |-----------------------------------------------|
592 * AP1
593 * |-----------------------------------------------|
594 * AP2
595 * |-----------------------------------------------|
596 * AP3
597 * |-----------------------------------------------|
598 * AP4
599 * |-----------------------------------------------|
600 * AP5
601 * |-----------------------------------------------|
602 * AP6
603 * |-----------------------------------------------|
604 * AP7
605 * |-----------------------------------------------|
606 *
607 * The transmission channel width reached the operating channel width, we are done.
608 */
609
610 Time roundDuration = MilliSeconds(5); // upper bound on the duration of a round
611
612 // To have simultaneous transmissions on adjacent channels, just initialize
613 // nRounds to 1 and nApsPerRound to m_channelWidth / 20. Of course, the test
614 // will fail because some stations will not receive some frames due to interference
615 uint16_t nRounds = 2;
616 uint16_t nApsPerRound = Count20MHzSubchannels(m_channelWidth) / 2;
617 for (MHz_u txChannelWidth{20}; txChannelWidth <= m_channelWidth;
618 txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
619 {
620 nRounds = std::min<uint16_t>(nRounds, m_nBss);
621 nApsPerRound = std::max<uint16_t>(nApsPerRound, 1);
622
623 for (uint16_t round = 0; round < nRounds; round++)
624 {
625 std::set<uint8_t> txBss;
626
627 for (uint16_t i = 0; i < nApsPerRound; i++)
628 {
629 uint16_t ap = round + i * nRounds;
630 txBss.insert(ap);
633 this,
634 ap,
635 txChannelWidth);
636 }
637 // check that the SU frames were correctly received
638 Simulator::Schedule(m_time + roundDuration,
640 this,
641 txBss,
642 txChannelWidth);
643 m_time += roundDuration;
644 }
645 }
646
647 /*
648 * Repeat the same scheme as before with DL MU transmissions. For each transmission
649 * channel width, every round is repeated as many times as the number of ways in
650 * which we can partition the transmission channel width in equal sized RUs.
651 */
652 nRounds = 2;
653 nApsPerRound = Count20MHzSubchannels(m_channelWidth) / 2;
654 for (MHz_u txChannelWidth{20}; txChannelWidth <= m_channelWidth;
655 txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
656 {
657 nRounds = std::min<uint16_t>(nRounds, m_nBss);
658 nApsPerRound = std::max<uint16_t>(nApsPerRound, 1);
659
660 for (uint16_t round = 0; round < nRounds; round++)
661 {
662 for (uint32_t type = 0; type < static_cast<uint32_t>(WifiRu::GetMaxRuType(
664 ++type)
665 {
666 auto ruType = static_cast<RuType>(type);
667 std::size_t nRus = HeRu::GetNRus(txChannelWidth, ruType);
668 std::set<uint8_t> txBss;
669 if (nRus > 0)
670 {
671 for (uint16_t i = 0; i < nApsPerRound; i++)
672 {
673 uint16_t ap = round + i * nRounds;
674 txBss.insert(ap);
677 this,
678 ap,
679 txChannelWidth,
680 ruType,
681 nRus);
682 }
683 // check that the MU frame was correctly received
684 Simulator::Schedule(m_time + roundDuration,
686 this,
687 txBss,
688 txChannelWidth,
689 ruType,
690 nRus,
691 /* isDlMu */ true);
692 m_time += roundDuration;
693 }
694 }
695 }
696 }
697
698 /*
699 * Repeat the same scheme as before with UL MU transmissions. For each transmission
700 * channel width, every round is repeated as many times as the number of ways in
701 * which we can partition the transmission channel width in equal sized RUs.
702 */
703 nRounds = 2;
704 nApsPerRound = Count20MHzSubchannels(m_channelWidth) / 2;
705 for (MHz_u txChannelWidth{20}; txChannelWidth <= m_channelWidth;
706 txChannelWidth *= 2, nRounds *= 2, nApsPerRound /= 2)
707 {
708 nRounds = std::min<uint16_t>(nRounds, m_nBss);
709 nApsPerRound = std::max<uint16_t>(nApsPerRound, 1);
710
711 for (uint16_t round = 0; round < nRounds; round++)
712 {
713 for (uint32_t type = 0; type < static_cast<uint32_t>(WifiRu::GetMaxRuType(
715 ++type)
716 {
717 auto ruType = static_cast<RuType>(type);
718 std::size_t nRus = HeRu::GetNRus(txChannelWidth, ruType);
719 std::set<uint8_t> txBss;
720 if (nRus > 0)
721 {
722 for (uint16_t i = 0; i < nApsPerRound; i++)
723 {
724 uint16_t ap = round + i * nRounds;
725 txBss.insert(ap);
728 this,
729 ap,
730 txChannelWidth,
731 ruType,
732 nRus);
733 }
734 // check that Trigger Frames and TB PPDUs were correctly received
736 MicroSeconds(10), /* during SIFS */
738 this,
739 txBss,
740 txChannelWidth);
741 Simulator::Schedule(m_time + roundDuration,
743 this,
744 txBss,
745 txChannelWidth,
746 ruType,
747 nRus,
748 /* isDlMu */ false);
749 m_time += roundDuration;
750 }
751 }
752 }
753 }
754
755 // Trace PSDUs passed to the PHY on all devices
756 Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyTxPsduBegin",
758
761
763}
764
765void
767{
768 NS_LOG_INFO("*** BSS " << +bss << " transmits on primary " << txChannelWidth << " MHz channel");
769
770 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
771 auto staDev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(0));
772
773 uint8_t bssColor = apDev->GetHeConfiguration()->m_bssColor;
774 auto txVector = WifiTxVector(
776 0,
778 NanoSeconds(800),
779 1,
780 1,
781 0,
782 txChannelWidth,
783 false,
784 false,
785 false,
786 bssColor);
787 WifiMacHeader hdr;
789 hdr.SetQosTid(0);
790 hdr.SetAddr1(staDev->GetMac()->GetAddress());
791 hdr.SetAddr2(apDev->GetMac()->GetAddress());
792 hdr.SetAddr3(apDev->GetMac()->GetBssid(0));
793 hdr.SetSequenceNumber(1);
795 apDev->GetPhy()->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
796}
797
798void
800 MHz_u txChannelWidth,
801 RuType ruType,
802 std::size_t nRus)
803{
804 NS_LOG_INFO("*** BSS " << +bss << " transmits on primary " << txChannelWidth
805 << " MHz channel a DL MU PPDU "
806 << "addressed to " << nRus << " stations (RU type: " << ruType << ")");
807
808 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
809 uint8_t bssColor = apDev->GetHeConfiguration()->m_bssColor;
810
811 auto txVector = WifiTxVector(
813 0,
815 NanoSeconds(800),
816 1,
817 1,
818 0,
819 txChannelWidth,
820 false,
821 false,
822 false,
823 bssColor);
825 {
826 txVector.SetEhtPpduType(0);
827 }
828 WifiMacHeader hdr;
830 hdr.SetQosTid(0);
831 hdr.SetAddr2(apDev->GetMac()->GetAddress());
832 hdr.SetAddr3(apDev->GetMac()->GetBssid(0));
833 hdr.SetSequenceNumber(1);
834
835 WifiConstPsduMap psduMap;
836
837 for (std::size_t i = 1; i <= nRus; i++)
838 {
839 auto primary80OrLow80{true};
840 auto primary160{true};
841 auto index{i};
842 if (txChannelWidth > MHz_u{80})
843 {
845 {
846 index = HeRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
847 primary80OrLow80 = HeRu::GetPrimary80MHzFlag(txChannelWidth,
848 ruType,
849 i,
850 apDev->GetPhy()->GetPrimary20Index());
851 }
852 else
853 {
854 index = EhtRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
855 const auto& [p160, p80OrLow80] =
856 EhtRu::GetPrimaryFlags(txChannelWidth,
857 ruType,
858 i,
859 apDev->GetPhy()->GetPrimary20Index());
860 primary160 = p160;
861 primary80OrLow80 = p80OrLow80;
862 }
863 }
864 const auto ru =
866 ? WifiRu::RuSpec(HeRu::RuSpec{ruType, index, primary80OrLow80})
867 : WifiRu::RuSpec(EhtRu::RuSpec{ruType, index, primary160, primary80OrLow80});
868
869 auto staDev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(i - 1));
870 uint16_t staId = DynamicCast<StaWifiMac>(staDev->GetMac())->GetAssociationId();
871 txVector.SetHeMuUserInfo(staId, {ru, 8, 1});
872 hdr.SetAddr1(staDev->GetMac()->GetAddress());
873 psduMap[staId] = Create<const WifiPsdu>(Create<Packet>(1000), hdr);
874 }
875 txVector.SetSigBMode(VhtPhy::GetVhtMcs5());
876
877 apDev->GetPhy()->Send(psduMap, txVector);
878}
879
880void
882 MHz_u txChannelWidth,
883 RuType ruType,
884 std::size_t nRus)
885{
886 NS_LOG_INFO("*** BSS " << +bss << " transmits a Basic Trigger Frame");
887
888 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
889
890 m_trigger->GetHeader(0).SetAddr2(apDev->GetMac()->GetAddress());
891
892 apDev->GetPhy()->Send(m_trigger, m_triggerTxVector);
893
894 // schedule the transmission of HE TB PPDUs
895 Simulator::Schedule(m_triggerTxDuration + apDev->GetPhy()->GetSifs(),
897 this,
898 bss,
899 txChannelWidth,
900 ruType,
901 nRus);
902}
903
904void
906 MHz_u txChannelWidth,
907 RuType ruType,
908 std::size_t nRus)
909{
910 auto apDev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
911 uint8_t bssColor = apDev->GetHeConfiguration()->m_bssColor;
912
913 WifiMacHeader hdr;
915 hdr.SetQosTid(0);
916 hdr.SetAddr1(apDev->GetMac()->GetAddress());
917 hdr.SetAddr3(apDev->GetMac()->GetBssid(0));
918 hdr.SetSequenceNumber(1);
919
920 Time duration;
921 uint16_t length = 0;
922 WifiTxVector trigVector(
924 0,
926 NanoSeconds(3200),
927 1,
928 1,
929 0,
930 txChannelWidth,
931 false,
932 false,
933 false,
934 bssColor);
935
936 for (std::size_t i = 1; i <= nRus; i++)
937 {
938 NS_LOG_INFO("*** BSS " << +bss << " STA " << i - 1 << " transmits on primary "
939 << txChannelWidth
940 << " MHz channel an HE TB PPDU (RU type: " << ruType << ")");
941
942 auto primary80OrLow80{true};
943 auto primary160{true};
944 auto index{i};
945 if (txChannelWidth > MHz_u{80})
946 {
948 {
949 index = HeRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
950 primary80OrLow80 = HeRu::GetPrimary80MHzFlag(txChannelWidth,
951 ruType,
952 i,
953 apDev->GetPhy()->GetPrimary20Index());
954 }
955 else
956 {
957 index = EhtRu::GetIndexIn80MHzSegment(txChannelWidth, ruType, i);
958 const auto& [p160, p80OrLow80] =
959 EhtRu::GetPrimaryFlags(txChannelWidth,
960 ruType,
961 i,
962 apDev->GetPhy()->GetPrimary20Index());
963 primary160 = p160;
964 primary80OrLow80 = p80OrLow80;
965 }
966 }
967 const auto ru =
969 ? WifiRu::RuSpec(HeRu::RuSpec{ruType, index, primary80OrLow80})
970 : WifiRu::RuSpec(EhtRu::RuSpec{ruType, index, primary160, primary80OrLow80});
971
972 auto staDev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(i - 1));
973 uint16_t staId = DynamicCast<StaWifiMac>(staDev->GetMac())->GetAssociationId();
974
975 WifiTxVector txVector(
977 0,
979 NanoSeconds(3200),
980 1,
981 1,
982 0,
983 txChannelWidth,
984 false,
985 false,
986 false,
987 bssColor);
988 txVector.SetHeMuUserInfo(staId, {ru, 8, 1});
989 trigVector.SetHeMuUserInfo(staId, {ru, 8, 1});
990
991 hdr.SetAddr2(staDev->GetMac()->GetAddress());
993
994 if (duration.IsZero())
995 {
996 // calculate just once
997 duration = WifiPhy::CalculateTxDuration(psdu->GetSize(),
998 txVector,
999 staDev->GetMac()->GetWifiPhy()->GetPhyBand(),
1000 staId);
1001 std::tie(length, duration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
1002 duration,
1003 txVector,
1004 staDev->GetMac()->GetWifiPhy()->GetPhyBand());
1005 }
1006 txVector.SetLength(length);
1007
1008 staDev->GetPhy()->Send(WifiConstPsduMap{{staId, psdu}}, txVector);
1009 }
1010
1011 // AP's PHY expects to receive a TRIGVECTOR (just once)
1012 trigVector.SetLength(length);
1013 auto apHePhy = StaticCast<HePhy>(apDev->GetPhy()->GetLatestPhyEntity());
1014 apHePhy->SetTrigVector(trigVector, duration);
1015}
1016
1017void
1019{
1020 for (uint8_t bss = 0; bss < m_nBss; bss++)
1021 {
1023 auto mac = DynamicCast<ApWifiMac>(dev->GetMac());
1024 NS_TEST_EXPECT_MSG_EQ(mac->GetStaList(SINGLE_LINK_OP_ID).size(),
1026 "Not all the stations completed association");
1027 }
1028}
1029
1030void
1031WifiPrimaryChannelsTest::CheckReceivedSuPpdus(std::set<uint8_t> txBss, MHz_u txChannelWidth)
1032{
1033 for (uint8_t bss = 0; bss < m_nBss; bss++)
1034 {
1035 if (txBss.find(bss) != txBss.end())
1036 {
1037 // Every station in the BSS of an AP that transmitted the frame hears (i.e.,
1038 // passes to the MAC) the frame
1039 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1040 {
1042 true,
1043 "Station [" << +bss << "][" << +sta
1044 << "] did not receive the SU frame on primary"
1045 << txChannelWidth << " channel");
1046 }
1047 // only the first station actually processed the frames
1049 true,
1050 "Station [" << +bss << "][0]"
1051 << " did not process the SU frame on primary"
1052 << txChannelWidth << " channel");
1053 for (uint8_t sta = 1; sta < m_nStationsPerBss; sta++)
1054 {
1056 false,
1057 "Station [" << +bss << "][" << +sta
1058 << "] processed the SU frame on primary"
1059 << txChannelWidth << " channel");
1060 }
1061 }
1062 else
1063 {
1064 // There was no transmission in this BSS. If BSS Color filtering is enabled or no frame
1065 // transmission overlaps with the primary20 channel of this BSS, stations in this BSS
1066 // did not hear any frame.
1068 std::none_of(txBss.begin(), txBss.end(), [&](const uint8_t& txAp) {
1069 auto txApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(txAp))->GetPhy();
1070 auto thisApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss))->GetPhy();
1071 return txApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth) ==
1072 thisApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth);
1073 }))
1074 {
1075 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1076 {
1078 false,
1079 "Station [" << +bss << "][" << +sta
1080 << "] received the SU frame on primary"
1081 << txChannelWidth << " channel");
1082 }
1083 }
1084 else
1085 {
1086 // all stations heard the frame but no station processed it
1087 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1088 {
1090 true,
1091 "Station [" << +bss << "][" << +sta
1092 << "] did not receive the SU frame on primary"
1093 << txChannelWidth << " channel");
1095 false,
1096 "Station [" << +bss << "][" << +sta
1097 << "] processed the SU frame on primary"
1098 << txChannelWidth << " channel");
1099 }
1100 }
1101 }
1102 // reset bitmaps
1103 m_received[bss].reset();
1104 m_processed[bss].reset();
1105 }
1106}
1107
1108void
1110 MHz_u txChannelWidth,
1111 RuType ruType,
1112 std::size_t nRus,
1113 bool isDlMu)
1114{
1115 for (uint8_t bss = 0; bss < m_nBss; bss++)
1116 {
1117 if (txBss.find(bss) != txBss.end())
1118 {
1119 // There was a transmission in this BSS.
1120 // [DL] Due to AID filtering, only stations that are addressed by the MU PPDU do hear
1121 // the frame [UL] The AP hears a TB PPDU sent by all and only the solicited stations
1122 for (std::size_t sta = 0; sta < nRus; sta++)
1123 {
1125 m_received[bss].test(sta),
1126 true,
1127 (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
1128 << " station [" << +bss << "][" << +sta << "] on primary" << txChannelWidth
1129 << " channel, RU type " << ruType << " was not received");
1130 }
1131 for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
1132 {
1134 false,
1135 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1136 << " transmitted on primary" << txChannelWidth
1137 << " channel, RU type " << ruType << " was received "
1138 << (isDlMu ? "by" : "from") << " station [" << +bss
1139 << "][" << +sta << "]");
1140 }
1141 // [DL] Only the addressed stations actually processed the frames
1142 // [UL] The AP processed the frames sent by all and only the addressed stations
1143 for (std::size_t sta = 0; sta < nRus; sta++)
1144 {
1146 m_processed[bss].test(sta),
1147 true,
1148 (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
1149 << " station [" << +bss << "][" << +sta << "] on primary" << txChannelWidth
1150 << " channel, RU type " << ruType << " was not processed");
1151 }
1152 for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
1153 {
1155 false,
1156 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1157 << " transmitted on primary" << txChannelWidth
1158 << " channel, RU type " << ruType << " was received "
1159 << (isDlMu ? "by" : "from") << " station [" << +bss
1160 << "][" << +sta << "] and processed");
1161 }
1162 }
1163 else
1164 {
1165 // There was no transmission in this BSS.
1166 // [DL] If BSS Color filtering is enabled or no frame transmission overlaps with
1167 // the primary20 channel of this BSS, stations in this BSS did not hear any frame.
1168 // [UL] The AP did not hear any TB PPDU because no TRIGVECTOR was passed to the PHY
1169 if (!isDlMu || m_useDistinctBssColors ||
1170 std::none_of(txBss.begin(), txBss.end(), [&](const uint8_t& txAp) {
1171 auto txApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(txAp))->GetPhy();
1172 auto thisApPhy = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss))->GetPhy();
1173 return txApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth) ==
1174 thisApPhy->GetOperatingChannel().GetPrimaryChannelIndex(txChannelWidth);
1175 }))
1176 {
1177 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1178 {
1180 false,
1181 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1182 << " transmitted on primary" << txChannelWidth
1183 << " channel, RU type " << ruType << " was received "
1184 << (isDlMu ? "by" : "from") << " station [" << +bss
1185 << "][" << +sta << "]");
1186 }
1187 }
1188 else
1189 {
1190 // [DL] Stations having the same AID of the stations addressed by the MU PPDU
1191 // received the frame
1192 for (std::size_t sta = 0; sta < nRus; sta++)
1193 {
1195 m_received[bss].test(sta),
1196 true,
1197 (isDlMu ? "A DL MU PPDU transmitted to" : "An HE TB PPDU transmitted by")
1198 << " station [" << +bss << "][" << +sta << "] on primary"
1199 << txChannelWidth << " channel, RU type " << ruType
1200 << " was not received");
1201 }
1202 for (uint8_t sta = nRus; sta < m_nStationsPerBss; sta++)
1203 {
1205 false,
1206 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1207 << " transmitted on primary" << txChannelWidth
1208 << " channel, RU type " << ruType << " was received "
1209 << (isDlMu ? "by" : "from") << " station [" << +bss
1210 << "][" << +sta << "]");
1211 }
1212 // no station processed the frame
1213 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1214 {
1216 false,
1217 (isDlMu ? "A DL MU PPDU" : "An HE TB PPDU")
1218 << " transmitted on primary" << txChannelWidth
1219 << " channel, RU type " << ruType << " was received "
1220 << (isDlMu ? "by" : "from") << " station [" << +bss
1221 << "][" << +sta << "] and processed");
1222 }
1223 }
1224 }
1225 // reset bitmaps
1226 m_received[bss].reset();
1227 m_processed[bss].reset();
1228 }
1229}
1230
1231void
1232WifiPrimaryChannelsTest::CheckReceivedTriggerFrames(std::set<uint8_t> txBss, MHz_u txChannelWidth)
1233{
1234 for (uint8_t bss = 0; bss < m_nBss; bss++)
1235 {
1236 if (txBss.find(bss) != txBss.end())
1237 {
1238 // Every station in the BSS of an AP that transmitted the Trigger Frame hears (i.e.,
1239 // passes to the MAC) and processes the frame
1240 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1241 {
1243 true,
1244 "Station [" << +bss << "][" << +sta
1245 << "] did not receive the Trigger Frame "
1246 "soliciting a transmission on primary"
1247 << txChannelWidth << " channel");
1249 true,
1250 "Station [" << +bss << "][" << +sta
1251 << "] did not process the Trigger Frame "
1252 "soliciting a transmission on primary"
1253 << txChannelWidth << " channel");
1254 }
1255 }
1256 else
1257 {
1258 // Given that a Trigger Frame is transmitted on the primary20 channel and all the
1259 // primary20 channels are distinct, stations in other BSSes did not hear the frame
1260 for (uint8_t sta = 0; sta < m_nStationsPerBss; sta++)
1261 {
1263 m_received[bss].test(sta),
1264 false,
1265 "Station ["
1266 << +bss << "][" << +sta
1267 << "] received the Trigger Frame soliciting a transmission on primary"
1268 << txChannelWidth << " channel");
1269 }
1270 }
1271 // reset bitmaps
1272 m_received[bss].reset();
1273 m_processed[bss].reset();
1274 }
1275}
1276
1277/**
1278 * @ingroup wifi-test
1279 * @ingroup tests
1280 *
1281 * @brief Test functions returning the indices of primary and secondary channels
1282 * of different width.
1283 */
1285{
1286 public:
1287 /**
1288 * Constructor
1289 */
1291 ~Wifi20MHzChannelIndicesTest() override = default;
1292
1293 /**
1294 * Check that the indices of the 20 MHz channels included in all the primary
1295 * and secondary channels are correct when setting the given primary20 channel.
1296 *
1297 * @param primary20 the index of the primary20 channel to configure
1298 * @param secondary20 the expected index of the secondary20 channel
1299 * @param primary40 the expected indices of the 20 MHz channels in the primary40 channel
1300 * @param secondary40 the expected indices of the 20 MHz channels in the secondary40 channel
1301 * @param primary80 the expected indices of the 20 MHz channels in the primary80 channel
1302 * @param secondary80 the expected indices of the 20 MHz channels in the secondary80 channel
1303 * @param primary160 the expected indices of the 20 MHz channels in the primary160 channel
1304 * @param secondary160 the expected indices of the 20 MHz channels in the secondary160 channel
1305 */
1306 void RunOne(uint8_t primary20,
1307 const std::set<uint8_t>& secondary20,
1308 const std::set<uint8_t>& primary40,
1309 const std::set<uint8_t>& secondary40,
1310 const std::set<uint8_t>& primary80,
1311 const std::set<uint8_t>& secondary80,
1312 const std::set<uint8_t>& primary160,
1313 const std::set<uint8_t>& secondary160);
1314
1315 private:
1316 void DoRun() override;
1317
1318 WifiPhyOperatingChannel m_channel; //!< operating channel
1319};
1320
1322 : TestCase("Check computation of primary and secondary channel indices")
1323{
1324}
1325
1326void
1328 const std::set<uint8_t>& secondary20,
1329 const std::set<uint8_t>& primary40,
1330 const std::set<uint8_t>& secondary40,
1331 const std::set<uint8_t>& primary80,
1332 const std::set<uint8_t>& secondary80,
1333 const std::set<uint8_t>& primary160,
1334 const std::set<uint8_t>& secondary160)
1335{
1336 auto printToStr = [](const std::set<uint8_t>& s) {
1337 std::stringstream ss;
1338 ss << "{";
1339 for (const auto& index : s)
1340 {
1341 ss << +index << " ";
1342 }
1343 ss << "}";
1344 return ss.str();
1345 };
1346
1347 m_channel.SetPrimary20Index(primary20);
1348
1349 auto actualPrimary20 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{20});
1350 NS_TEST_ASSERT_MSG_EQ((actualPrimary20 == std::set<uint8_t>{primary20}),
1351 true,
1352 "Expected Primary20 {" << +primary20 << "}"
1353 << " differs from actual "
1354 << printToStr(actualPrimary20));
1355
1356 auto actualSecondary20 = m_channel.GetAll20MHzChannelIndicesInSecondary(actualPrimary20);
1357 NS_TEST_ASSERT_MSG_EQ((actualSecondary20 == secondary20),
1358 true,
1359 "Expected Secondary20 " << printToStr(secondary20)
1360 << " differs from actual "
1361 << printToStr(actualSecondary20));
1362
1363 auto actualPrimary40 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{40});
1364 NS_TEST_ASSERT_MSG_EQ((actualPrimary40 == primary40),
1365 true,
1366 "Expected Primary40 " << printToStr(primary40) << " differs from actual "
1367 << printToStr(actualPrimary40));
1368
1369 auto actualSecondary40 = m_channel.GetAll20MHzChannelIndicesInSecondary(primary40);
1370 NS_TEST_ASSERT_MSG_EQ((actualSecondary40 == secondary40),
1371 true,
1372 "Expected Secondary40 " << printToStr(secondary40)
1373 << " differs from actual "
1374 << printToStr(actualSecondary40));
1375
1376 auto actualPrimary80 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{80});
1377 NS_TEST_ASSERT_MSG_EQ((actualPrimary80 == primary80),
1378 true,
1379 "Expected Primary80 " << printToStr(primary80) << " differs from actual "
1380 << printToStr(actualPrimary80));
1381
1382 auto actualSecondary80 = m_channel.GetAll20MHzChannelIndicesInSecondary(primary80);
1383 NS_TEST_ASSERT_MSG_EQ((actualSecondary80 == secondary80),
1384 true,
1385 "Expected Secondary80 " << printToStr(secondary80)
1386 << " differs from actual "
1387 << printToStr(actualSecondary80));
1388
1389 auto actualPrimary160 = m_channel.GetAll20MHzChannelIndicesInPrimary(MHz_u{160});
1390 NS_TEST_ASSERT_MSG_EQ((actualPrimary160 == primary160),
1391 true,
1392 "Expected Primary160 " << printToStr(primary160)
1393 << " differs from actual "
1394 << printToStr(actualPrimary160));
1395
1396 auto actualSecondary160 = m_channel.GetAll20MHzChannelIndicesInSecondary(primary160);
1397 NS_TEST_ASSERT_MSG_EQ((actualSecondary160 == secondary160),
1398 true,
1399 "Expected Secondary160 " << printToStr(secondary160)
1400 << " differs from actual "
1401 << printToStr(actualSecondary160));
1402}
1403
1404void
1406{
1407 /* 20 MHz channel */
1409 RunOne(0, {}, {}, {}, {}, {}, {}, {});
1410
1411 /* 40 MHz channel */
1413 RunOne(0, {1}, {0, 1}, {}, {}, {}, {}, {});
1414 RunOne(1, {0}, {0, 1}, {}, {}, {}, {}, {});
1415
1416 /* 80 MHz channel */
1418 RunOne(0, {1}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {}, {}, {});
1419 RunOne(1, {0}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {}, {}, {});
1420 RunOne(2, {3}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {}, {}, {});
1421 RunOne(3, {2}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {}, {}, {});
1422
1423 /* 160 MHz channel */
1425 RunOne(0, {1}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1426 RunOne(1, {0}, {0, 1}, {2, 3}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1427 RunOne(2, {3}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1428 RunOne(3, {2}, {2, 3}, {0, 1}, {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1429 RunOne(4, {5}, {4, 5}, {6, 7}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1430 RunOne(5, {4}, {4, 5}, {6, 7}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1431 RunOne(6, {7}, {6, 7}, {4, 5}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1432 RunOne(7, {6}, {6, 7}, {4, 5}, {4, 5, 6, 7}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5, 6, 7}, {});
1433
1434 /* 320 MHz channel */
1436 RunOne(0,
1437 {1},
1438 {0, 1},
1439 {2, 3},
1440 {0, 1, 2, 3},
1441 {4, 5, 6, 7},
1442 {0, 1, 2, 3, 4, 5, 6, 7},
1443 {8, 9, 10, 11, 12, 13, 14, 15});
1444 RunOne(1,
1445 {0},
1446 {0, 1},
1447 {2, 3},
1448 {0, 1, 2, 3},
1449 {4, 5, 6, 7},
1450 {0, 1, 2, 3, 4, 5, 6, 7},
1451 {8, 9, 10, 11, 12, 13, 14, 15});
1452 RunOne(2,
1453 {3},
1454 {2, 3},
1455 {0, 1},
1456 {0, 1, 2, 3},
1457 {4, 5, 6, 7},
1458 {0, 1, 2, 3, 4, 5, 6, 7},
1459 {8, 9, 10, 11, 12, 13, 14, 15});
1460 RunOne(3,
1461 {2},
1462 {2, 3},
1463 {0, 1},
1464 {0, 1, 2, 3},
1465 {4, 5, 6, 7},
1466 {0, 1, 2, 3, 4, 5, 6, 7},
1467 {8, 9, 10, 11, 12, 13, 14, 15});
1468 RunOne(4,
1469 {5},
1470 {4, 5},
1471 {6, 7},
1472 {4, 5, 6, 7},
1473 {0, 1, 2, 3},
1474 {0, 1, 2, 3, 4, 5, 6, 7},
1475 {8, 9, 10, 11, 12, 13, 14, 15});
1476 RunOne(5,
1477 {4},
1478 {4, 5},
1479 {6, 7},
1480 {4, 5, 6, 7},
1481 {0, 1, 2, 3},
1482 {0, 1, 2, 3, 4, 5, 6, 7},
1483 {8, 9, 10, 11, 12, 13, 14, 15});
1484 RunOne(6,
1485 {7},
1486 {6, 7},
1487 {4, 5},
1488 {4, 5, 6, 7},
1489 {0, 1, 2, 3},
1490 {0, 1, 2, 3, 4, 5, 6, 7},
1491 {8, 9, 10, 11, 12, 13, 14, 15});
1492 RunOne(7,
1493 {6},
1494 {6, 7},
1495 {4, 5},
1496 {4, 5, 6, 7},
1497 {0, 1, 2, 3},
1498 {0, 1, 2, 3, 4, 5, 6, 7},
1499 {8, 9, 10, 11, 12, 13, 14, 15});
1500 RunOne(8,
1501 {9},
1502 {8, 9},
1503 {10, 11},
1504 {8, 9, 10, 11},
1505 {12, 13, 14, 15},
1506 {8, 9, 10, 11, 12, 13, 14, 15},
1507 {0, 1, 2, 3, 4, 5, 6, 7});
1508 RunOne(9,
1509 {8},
1510 {8, 9},
1511 {10, 11},
1512 {8, 9, 10, 11},
1513 {12, 13, 14, 15},
1514 {8, 9, 10, 11, 12, 13, 14, 15},
1515 {0, 1, 2, 3, 4, 5, 6, 7});
1516 RunOne(10,
1517 {11},
1518 {10, 11},
1519 {8, 9},
1520 {8, 9, 10, 11},
1521 {12, 13, 14, 15},
1522 {8, 9, 10, 11, 12, 13, 14, 15},
1523 {0, 1, 2, 3, 4, 5, 6, 7});
1524 RunOne(11,
1525 {10},
1526 {10, 11},
1527 {8, 9},
1528 {8, 9, 10, 11},
1529 {12, 13, 14, 15},
1530 {8, 9, 10, 11, 12, 13, 14, 15},
1531 {0, 1, 2, 3, 4, 5, 6, 7});
1532 RunOne(12,
1533 {13},
1534 {12, 13},
1535 {14, 15},
1536 {12, 13, 14, 15},
1537 {8, 9, 10, 11},
1538 {8, 9, 10, 11, 12, 13, 14, 15},
1539 {0, 1, 2, 3, 4, 5, 6, 7});
1540 RunOne(13,
1541 {12},
1542 {12, 13},
1543 {14, 15},
1544 {12, 13, 14, 15},
1545 {8, 9, 10, 11},
1546 {8, 9, 10, 11, 12, 13, 14, 15},
1547 {0, 1, 2, 3, 4, 5, 6, 7});
1548 RunOne(14,
1549 {15},
1550 {14, 15},
1551 {12, 13},
1552 {12, 13, 14, 15},
1553 {8, 9, 10, 11},
1554 {8, 9, 10, 11, 12, 13, 14, 15},
1555 {0, 1, 2, 3, 4, 5, 6, 7});
1556 RunOne(15,
1557 {14},
1558 {14, 15},
1559 {12, 13},
1560 {12, 13, 14, 15},
1561 {8, 9, 10, 11},
1562 {8, 9, 10, 11, 12, 13, 14, 15},
1563 {0, 1, 2, 3, 4, 5, 6, 7});
1564}
1565
1566/**
1567 * @ingroup wifi-test
1568 * @ingroup tests
1569 *
1570 * @brief wifi primary channels test suite
1571 */
1573{
1574 public:
1576};
1577
1579 : TestSuite("wifi-primary-channels", Type::UNIT)
1580{
1581 for (const auto standard : {WIFI_STANDARD_80211ax, WIFI_STANDARD_80211be})
1582 {
1583 // Test cases for 20 MHz can be added, but are not that useful (there would be a single BSS)
1584 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{40}, true),
1585 TestCase::Duration::QUICK);
1586 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{40}, false),
1587 TestCase::Duration::QUICK);
1588 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{80}, true),
1589 TestCase::Duration::EXTENSIVE);
1590 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{80}, false),
1591 TestCase::Duration::EXTENSIVE);
1592 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{160}, true),
1593 TestCase::Duration::TAKES_FOREVER);
1594 AddTestCase(new WifiPrimaryChannelsTest(standard, MHz_u{160}, false),
1595 TestCase::Duration::TAKES_FOREVER);
1596 }
1598 TestCase::Duration::TAKES_FOREVER);
1600 TestCase::Duration::TAKES_FOREVER);
1601 AddTestCase(new Wifi20MHzChannelIndicesTest(), TestCase::Duration::QUICK);
1602}
1603
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.
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
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...
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
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...
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.
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)
A container for one type of attribute.
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 std::size_t GetNRus(MHz_u bw, RuType ruType)
Get the number of distinct RUs of the given type (number of tones) available in a HE PPDU of the give...
Definition he-ru.cc:491
static Mac48Address GetBroadcast()
Helper class used to assign positions and mobility models to nodes.
holds a vector of ns3::NetDevice pointers
void Add(NetDeviceContainer other)
Append the contents of another NetDeviceContainer to the end of this container.
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
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.
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
encapsulates test code
Definition test.h:1050
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
A suite of tests to run.
Definition test.h:1267
Type
Type of test.
Definition test.h:1274
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
AttributeValue implementation for Time.
Definition nstime.h:1432
AttributeValue implementation for Tuple.
Definition tuple.h:67
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:1563
std::vector< ChannelTuple > ChannelSegments
segments identifying an operating channel
Definition wifi-phy.h:943
void SetReceiveOkCallback(RxOkCallback callback)
Definition wifi-phy.cc:487
Class that keeps track of all information about the current PHY operating channel.
std::set< uint8_t > GetAll20MHzChannelIndicesInSecondary(MHz_u width) const
Get the channel indices of all the 20 MHz channels included in the secondary channel of the given wid...
void SetPrimary20Index(uint8_t index)
Set the index of the primary 20 MHz channel (0 indicates the 20 MHz subchannel with the lowest center...
std::set< uint8_t > GetAll20MHzChannelIndicesInPrimary(MHz_u width) const
Get the channel indices of all the 20 MHz channels included in the primary channel of the given width...
void SetDefault(MHz_u width, WifiStandard standard, WifiPhyBand band)
Set the default channel of the given width and for the given standard and band.
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition wifi-psdu.cc:274
uint32_t GetSize() const
Return the size of the PSDU in bytes.
Definition wifi-psdu.cc:268
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 SetEhtPpduType(uint8_t type)
Set the EHT_PPDU_TYPE parameter.
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_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:436
#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:134
#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:241
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1381
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1357
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:99
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:138
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:280
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_QOSDATA
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
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:79
static WifiPrimaryChannelsTestSuite g_wifiPrimaryChannelsTestSuite
the test suite