A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
spectrum-wifi-phy-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015 University of Washington
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include "ns3/boolean.h"
8#include "ns3/constant-position-mobility-model.h"
9#include "ns3/eht-configuration.h"
10#include "ns3/eht-phy.h" //includes all previous PHYs
11#include "ns3/interference-helper.h"
12#include "ns3/log.h"
13#include "ns3/mobility-helper.h"
14#include "ns3/multi-model-spectrum-channel.h"
15#include "ns3/nist-error-rate-model.h"
16#include "ns3/non-communicating-net-device.h"
17#include "ns3/ofdm-ppdu.h"
18#include "ns3/pointer.h"
19#include "ns3/spectrum-wifi-helper.h"
20#include "ns3/spectrum-wifi-phy.h"
21#include "ns3/string.h"
22#include "ns3/test.h"
23#include "ns3/waveform-generator.h"
24#include "ns3/wifi-mac-header.h"
25#include "ns3/wifi-net-device.h"
26#include "ns3/wifi-phy-listener.h"
27#include "ns3/wifi-psdu.h"
28#include "ns3/wifi-spectrum-phy-interface.h"
29#include "ns3/wifi-spectrum-signal-parameters.h"
30#include "ns3/wifi-spectrum-value-helper.h"
31#include "ns3/wifi-utils.h"
32
33#include <memory>
34#include <optional>
35#include <tuple>
36#include <vector>
37
38using namespace ns3;
39
40NS_LOG_COMPONENT_DEFINE("SpectrumWifiPhyTest");
41
42static const uint8_t CHANNEL_NUMBER = 36;
43static const MHz_u CHANNEL_WIDTH{20};
44static const MHz_u GUARD_WIDTH = CHANNEL_WIDTH; // expanded to channel width to model spectrum mask
45
46/**
47 * Extended SpectrumWifiPhy class for the purpose of the tests.
48 */
50{
51 public:
52 using WifiPhy::GetBand;
53};
54
55/**
56 * Extended InterferenceHelper class for the purpose of the tests.
57 */
59{
60 public:
61 /**
62 * @brief Get the type ID.
63 * @return the object TypeId
64 */
66 {
67 static TypeId tid = TypeId("ns3::ExtInterferenceHelper")
69 .SetGroupName("Wifi")
70 .AddConstructor<ExtInterferenceHelper>();
71 return tid;
72 }
73
74 /**
75 * Indicate whether the interference helper is in receiving state
76 *
77 * @return true if the interference helper is in receiving state, false otherwise
78 */
79 bool IsRxing() const
80 {
81 return std::any_of(m_rxing.cbegin(), m_rxing.cend(), [](const auto& rxing) {
82 return rxing.second;
83 });
84 }
85
86 /**
87 * Indicate whether a given band is tracked by the interference helper
88 *
89 * @param startStopFreqs the start and stop frequencies per segment of the band
90 *
91 * @return true if the specified band is tracked by the interference helper, false otherwise
92 */
93 bool IsBandTracked(const std::vector<WifiSpectrumBandFrequencies>& startStopFreqs) const
94 {
95 for (const auto& [band, nis] : m_niChanges)
96 {
97 if (band.frequencies == startStopFreqs)
98 {
99 return true;
100 }
101 }
102 return false;
103 }
104};
105
107
108/**
109 * @ingroup wifi-test
110 * @ingroup tests
111 *
112 * @brief Spectrum Wifi Phy Basic Test
113 */
115{
116 public:
118 /**
119 * Constructor
120 *
121 * @param name reference name
122 */
123 SpectrumWifiPhyBasicTest(std::string name);
124 ~SpectrumWifiPhyBasicTest() override;
125
126 protected:
127 void DoSetup() override;
128 void DoTeardown() override;
130 /**
131 * Make signal function
132 * @param txPower the transmit power
133 * @param channel the operating channel of the PHY used for the transmission
134 * @returns Ptr<SpectrumSignalParameters>
135 */
137 const WifiPhyOperatingChannel& channel);
138 /**
139 * Send signal function
140 * @param txPower the transmit power
141 */
142 void SendSignal(Watt_u txPower);
143 /**
144 * Spectrum wifi receive success function
145 * @param psdu the PSDU
146 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
147 * @param txVector the transmit vector
148 * @param statusPerMpdu reception status per MPDU
149 */
151 RxSignalInfo rxSignalInfo,
152 const WifiTxVector& txVector,
153 const std::vector<bool>& statusPerMpdu);
154 /**
155 * Spectrum wifi receive failure function
156 * @param psdu the PSDU
157 */
159 uint32_t m_count; ///< count
160
161 private:
162 void DoRun() override;
163
164 uint64_t m_uid; //!< the UID to use for the PPDU
165};
166
168 : SpectrumWifiPhyBasicTest("SpectrumWifiPhy test case receives one packet")
169{
170}
171
173 : TestCase(name),
174 m_count(0),
175 m_uid(0)
176{
177}
178
179// Make a Wi-Fi signal to inject directly to the StartRx() method
182{
184 0,
186 NanoSeconds(800),
187 1,
188 1,
189 0,
191 false};
192
193 auto pkt = Create<Packet>(1000);
194 WifiMacHeader hdr;
195
197 hdr.SetQosTid(0);
198
199 auto psdu = Create<WifiPsdu>(pkt, hdr);
200 const auto txDuration =
201 SpectrumWifiPhy::CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
202
203 auto ppdu = Create<OfdmPpdu>(psdu, txVector, channel, m_uid++);
204
206 channel.GetPrimaryChannelCenterFrequency(CHANNEL_WIDTH),
208 txPower,
210 auto txParams = Create<WifiSpectrumSignalParameters>();
211 txParams->psd = txPowerSpectrum;
212 txParams->txPhy = nullptr;
213 txParams->duration = txDuration;
214 txParams->ppdu = ppdu;
215
216 return txParams;
217}
218
219// Make a Wi-Fi signal to inject directly to the StartRx() method
220void
222{
223 m_phy->StartRx(MakeSignal(txPower, m_phy->GetOperatingChannel()), nullptr);
224}
225
226void
228 RxSignalInfo rxSignalInfo,
229 const WifiTxVector& txVector,
230 const std::vector<bool>& statusPerMpdu)
231{
232 NS_LOG_FUNCTION(this << *psdu << rxSignalInfo << txVector);
233 m_count++;
234}
235
236void
242
246
247// Create necessary objects, and inject signals. Test that the expected
248// number of packet receptions occur.
249void
251{
252 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
253 auto node = CreateObject<Node>();
254 auto dev = CreateObject<WifiNetDevice>();
256 auto interferenceHelper = CreateObject<InterferenceHelper>();
257 m_phy->SetInterferenceHelper(interferenceHelper);
259 m_phy->SetErrorRateModel(error);
260 m_phy->SetDevice(dev);
261 m_phy->AddChannel(spectrumChannel);
262 m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
263 m_phy->ConfigureStandard(WIFI_STANDARD_80211n);
264 m_phy->SetReceiveOkCallback(
266 m_phy->SetReceiveErrorCallback(
268 dev->SetPhy(m_phy);
269 node->AddDevice(dev);
270}
271
272void
274{
275 m_phy->Dispose();
276 m_phy = nullptr;
277}
278
279// Test that the expected number of packet receptions occur.
280void
282{
283 Watt_u txPower{0.01};
284 // Send packets spaced 1 second apart; all should be received
288 // Send packets spaced 1 microsecond second apart; none should be received (PHY header reception
289 // failure)
292 this,
293 txPower);
296 this,
297 txPower);
300
301 NS_TEST_ASSERT_MSG_EQ(m_count, 3, "Didn't receive right number of packets");
302}
303
304/**
305 * @ingroup wifi-test
306 * @ingroup tests
307 *
308 * @brief Test Phy Listener
309 */
311{
312 public:
313 /**
314 * Create a test PhyListener
315 */
316 TestPhyListener() = default;
317 ~TestPhyListener() override = default;
318
319 void NotifyRxStart(Time duration) override
320 {
321 NS_LOG_FUNCTION(this << duration);
323 }
324
325 void NotifyRxEndOk() override
326 {
327 NS_LOG_FUNCTION(this);
329 }
330
331 void NotifyRxEndError(const WifiTxVector& txVector) override
332 {
333 NS_LOG_FUNCTION(this << txVector);
335 }
336
337 void NotifyTxStart(Time duration, dBm_u txPower) override
338 {
339 NS_LOG_FUNCTION(this << duration << txPower);
340 }
341
343 WifiChannelListType channelType,
344 const std::vector<Time>& /*per20MhzDurations*/) override
345 {
346 NS_LOG_FUNCTION(this << duration << channelType);
347 if (duration.IsStrictlyPositive())
348 {
351 {
353 }
354 m_ccaBusyEnd = std::max(m_ccaBusyEnd, Simulator::Now() + duration);
355 }
356 }
357
358 void NotifySwitchingStart(Time duration) override
359 {
360 }
361
362 void NotifySleep() override
363 {
364 }
365
366 void NotifyOff() override
367 {
368 }
369
370 void NotifyWakeup() override
371 {
372 }
373
374 void NotifyOn() override
375 {
376 }
377
378 /**
379 * Reset function
380 */
381 void Reset()
382 {
383 NS_LOG_FUNCTION(this);
384 m_notifyRxStart = 0;
385 m_notifyRxEndOk = 0;
390 }
391
392 uint32_t m_notifyRxStart{0}; ///< notify receive start
393 uint32_t m_notifyRxEndOk{0}; ///< notify receive end OK
394 uint32_t m_notifyRxEndError{0}; ///< notify receive end error
395 uint32_t m_notifyMaybeCcaBusyStart{0}; ///< notify maybe CCA busy start
396 Time m_ccaBusyStart{}; ///< CCA_BUSY start time
397 Time m_ccaBusyEnd{}; ///< CCA_BUSY end time
398};
399
400/**
401 * @ingroup wifi-test
402 * @ingroup tests
403 *
404 * @brief Spectrum Wifi Phy Listener Test
405 */
407{
408 public:
410
411 private:
412 void DoSetup() override;
413 void DoRun() override;
414 std::shared_ptr<TestPhyListener> m_listener; ///< listener
415};
416
418 : SpectrumWifiPhyBasicTest("SpectrumWifiPhy test operation of WifiPhyListener")
419{
420}
421
422void
424{
426 m_listener = std::make_shared<TestPhyListener>();
427 m_phy->RegisterListener(m_listener);
428}
429
430void
432{
433 Watt_u txPower{0.01};
436
437 NS_TEST_ASSERT_MSG_EQ(m_count, 1, "Didn't receive right number of packets");
440 2,
441 "Didn't receive NotifyCcaBusyStart (once preamble is detected + prolonged by L-SIG "
442 "reception, then switched to Rx by at the beginning of data)");
443 NS_TEST_ASSERT_MSG_EQ(m_listener->m_notifyRxStart, 1, "Didn't receive NotifyRxStart");
444 NS_TEST_ASSERT_MSG_EQ(m_listener->m_notifyRxEndOk, 1, "Didn't receive NotifyRxEnd");
445
447 m_listener.reset();
448}
449
450/**
451 * @ingroup wifi-test
452 * @ingroup tests
453 *
454 * @brief Spectrum Wifi Phy Filter Test
455 */
457{
458 public:
460 /**
461 * Constructor
462 *
463 * @param name reference name
464 */
465 SpectrumWifiPhyFilterTest(std::string name);
467
468 private:
469 void DoSetup() override;
470 void DoTeardown() override;
471 void DoRun() override;
472
473 /**
474 * Run one function
475 */
476 void RunOne();
477
478 /**
479 * Send PPDU function
480 */
481 void SendPpdu();
482
483 /**
484 * Callback triggered when a packet is received by the PHYs
485 * @param p the received packet
486 * @param rxPowersW the received power per channel band in watts
487 */
489
492
493 MHz_u m_txChannelWidth; ///< TX channel width
494 MHz_u m_rxChannelWidth; ///< RX channel width
495};
496
498 : TestCase("SpectrumWifiPhy test RX filters"),
499 m_txChannelWidth(MHz_u{20}),
500 m_rxChannelWidth(MHz_u{20})
501{
502}
503
508
509void
511{
513 0,
515 NanoSeconds(800),
516 1,
517 1,
518 0,
520 false,
521 false};
522 auto pkt = Create<Packet>(1000);
523 WifiMacHeader hdr;
525 hdr.SetQosTid(0);
526 hdr.SetAddr1(Mac48Address("00:00:00:00:00:01"));
527 hdr.SetSequenceNumber(1);
528 auto psdu = Create<WifiPsdu>(pkt, hdr);
529 m_txPhy->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
530}
531
537
538void
540{
541 for (const auto& [band, powerW] : rxPowersW)
542 {
544 "band: (" << band << ") -> powerW=" << powerW
545 << (powerW > 0.0 ? " (" + std::to_string(WToDbm(powerW)) + " dBm)" : ""));
546 }
547
548 size_t numBands = rxPowersW.size();
549 auto expectedNumBands = std::max<std::size_t>(1, m_rxChannelWidth / MHz_u{20});
550 expectedNumBands += (m_rxChannelWidth / MHz_u{40});
551 expectedNumBands += (m_rxChannelWidth / MHz_u{80});
552 expectedNumBands += (m_rxChannelWidth / MHz_u{160});
553 expectedNumBands += (m_rxChannelWidth / MHz_u{320});
554 expectedNumBands += m_rxPhy
557 m_rxPhy->GetCurrentInterface()->GetChannelWidth()))
558 .size();
559
560 NS_TEST_ASSERT_MSG_EQ(numBands,
561 expectedNumBands,
562 "Total number of bands handled by the receiver is incorrect");
563
564 MHz_u channelWidth = std::min(m_txChannelWidth, m_rxChannelWidth);
565 auto band = m_rxPhy->GetBand(channelWidth, 0);
566 auto it = rxPowersW.find(band);
567 NS_LOG_INFO("powerW total band: " << it->second << " (" << WToDbm(it->second) << " dBm)");
568 int totalRxPower = static_cast<int>(WToDbm(it->second) + 0.5);
569 int expectedTotalRxPower;
571 {
572 // PHY sends at 16 dBm, and since there is no loss, this should be the total power at the
573 // receiver.
574 expectedTotalRxPower = 16;
575 }
576 else
577 {
578 // Only a part of the transmitted power is received
579 expectedTotalRxPower =
580 16 - static_cast<int>(RatioToDb(m_txChannelWidth / m_rxChannelWidth));
581 }
582 NS_TEST_ASSERT_MSG_EQ(totalRxPower,
583 expectedTotalRxPower,
584 "Total received power is not correct");
585
586 if ((m_txChannelWidth <= m_rxChannelWidth) && (channelWidth >= MHz_u{20}))
587 {
588 band = m_rxPhy->GetBand(MHz_u{20}, 0); // primary 20 MHz
589 it = rxPowersW.find(band);
590 NS_LOG_INFO("powerW in primary 20 MHz channel: " << it->second << " (" << WToDbm(it->second)
591 << " dBm)");
592 const auto rxPowerPrimaryChannel20 = static_cast<int>(WToDbm(it->second) + 0.5);
593 const auto expectedRxPowerPrimaryChannel20 =
594 16 - static_cast<int>(RatioToDb(Count20MHzSubchannels(channelWidth)));
595 NS_TEST_ASSERT_MSG_EQ(rxPowerPrimaryChannel20,
596 expectedRxPowerPrimaryChannel20,
597 "Received power in the primary 20 MHz band is not correct");
598 }
599}
600
601void
603{
604 // WifiHelper::EnableLogComponents();
605 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
606
607 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
609 lossModel->SetFrequency(5.180e9);
610 spectrumChannel->AddPropagationLossModel(lossModel);
612 spectrumChannel->SetPropagationDelayModel(delayModel);
613
614 auto txNode = CreateObject<Node>();
615 auto txDev = CreateObject<WifiNetDevice>();
617 auto txInterferenceHelper = CreateObject<InterferenceHelper>();
618 m_txPhy->SetInterferenceHelper(txInterferenceHelper);
619 auto txErrorModel = CreateObject<NistErrorRateModel>();
620 m_txPhy->SetErrorRateModel(txErrorModel);
621 m_txPhy->SetDevice(txDev);
622 m_txPhy->AddChannel(spectrumChannel);
624 txDev->SetStandard(WIFI_STANDARD_80211be);
626 m_txPhy->SetMobility(apMobility);
627 txDev->SetPhy(m_txPhy);
628 txNode->AggregateObject(apMobility);
629 txNode->AddDevice(txDev);
630 auto txEhtConfiguration = CreateObject<EhtConfiguration>();
631 txDev->SetEhtConfiguration(txEhtConfiguration);
632
633 auto rxNode = CreateObject<Node>();
634 auto rxDev = CreateObject<WifiNetDevice>();
636 auto rxInterferenceHelper = CreateObject<InterferenceHelper>();
637 m_rxPhy->SetInterferenceHelper(rxInterferenceHelper);
638 auto rxErrorModel = CreateObject<NistErrorRateModel>();
639 m_rxPhy->SetErrorRateModel(rxErrorModel);
640 m_rxPhy->SetDevice(rxDev);
641 m_rxPhy->AddChannel(spectrumChannel);
643 rxDev->SetStandard(WIFI_STANDARD_80211be);
645 m_rxPhy->SetMobility(sta1Mobility);
646 rxDev->SetPhy(m_rxPhy);
647 rxNode->AggregateObject(sta1Mobility);
648 rxNode->AddDevice(rxDev);
649 auto rxEhtConfiguration = CreateObject<EhtConfiguration>();
650 rxDev->SetEhtConfiguration(rxEhtConfiguration);
653}
654
655void
657{
658 m_txPhy->Dispose();
659 m_txPhy = nullptr;
660 m_rxPhy->Dispose();
661 m_rxPhy = nullptr;
662}
663
664void
666{
667 MHz_u txFrequency;
668 switch (static_cast<uint16_t>(m_txChannelWidth))
669 {
670 case 20:
671 default:
672 txFrequency = MHz_u{5955};
673 break;
674 case 40:
675 txFrequency = MHz_u{5965};
676 break;
677 case 80:
678 txFrequency = MHz_u{5985};
679 break;
680 case 160:
681 txFrequency = MHz_u{6025};
682 break;
683 case 320:
684 txFrequency = MHz_u{6105};
685 break;
686 }
687 auto txChannelNum = WifiPhyOperatingChannel::FindFirst(0,
688 txFrequency,
692 ->number;
695
696 MHz_u rxFrequency;
697 switch (static_cast<uint16_t>(m_rxChannelWidth))
698 {
699 case 20:
700 default:
701 rxFrequency = MHz_u{5955};
702 break;
703 case 40:
704 rxFrequency = MHz_u{5965};
705 break;
706 case 80:
707 rxFrequency = MHz_u{5985};
708 break;
709 case 160:
710 rxFrequency = MHz_u{6025};
711 break;
712 case 320:
713 rxFrequency = MHz_u{6105};
714 break;
715 }
716 auto rxChannelNum = WifiPhyOperatingChannel::FindFirst(0,
717 rxFrequency,
721 ->number;
724
726
728}
729
730void
732{
733 for (const auto txBw : {MHz_u{20}, MHz_u{40}, MHz_u{80}, MHz_u{160}, MHz_u{320}})
734 {
735 for (const auto rxBw : {MHz_u{20}, MHz_u{40}, MHz_u{80}, MHz_u{160}, MHz_u{320}})
736 {
737 m_txChannelWidth = txBw;
738 m_rxChannelWidth = rxBw;
739 RunOne();
740 }
741 }
742
744}
745
746/**
747 * @ingroup wifi-test
748 * @ingroup tests
749 *
750 * @brief Spectrum Wifi Phy Bands Calculations Test
751 *
752 * This test verifies SpectrumWifiPhy::GetBand produces the expected results, for both contiguous
753 * (160 MHz) and non-contiguous (80+80MHz) operating channel
754 */
756{
757 public:
759
760 private:
761 void DoSetup() override;
762 void DoTeardown() override;
763 void DoRun() override;
764
765 /**
766 * Run one function
767 * @param channelNumberPerSegment the channel number for each segment of the operating channel
768 * @param bandWidth the width of the band to test
769 * @param bandIndex the index of the band to test
770 * @param expectedIndices the expected start and stop indices returned by
771 * SpectrumWifiPhy::GetBand \param expectedFrequencies the expected start and stop frequencies
772 * returned by SpectrumWifiPhy::GetBand
773 */
774 void RunOne(const std::vector<uint8_t>& channelNumberPerSegment,
775 MHz_u bandWidth,
776 uint8_t bandIndex,
777 const std::vector<WifiSpectrumBandIndices>& expectedIndices,
778 const std::vector<WifiSpectrumBandFrequencies>& expectedFrequencies);
779
781};
782
784 : TestCase("SpectrumWifiPhy test bands calculations")
785{
786}
787
788void
790{
791 // WifiHelper::EnableLogComponents();
792 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
793
794 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
796 lossModel->SetFrequency(5.180e9);
797 spectrumChannel->AddPropagationLossModel(lossModel);
799 spectrumChannel->SetPropagationDelayModel(delayModel);
800
801 auto node = CreateObject<Node>();
802 auto dev = CreateObject<WifiNetDevice>();
804 auto interferenceHelper = CreateObject<InterferenceHelper>();
805 m_phy->SetInterferenceHelper(interferenceHelper);
806 auto errorModel = CreateObject<NistErrorRateModel>();
807 m_phy->SetErrorRateModel(errorModel);
808 m_phy->SetDevice(dev);
809 m_phy->AddChannel(spectrumChannel);
810 m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
811 dev->SetPhy(m_phy);
812 node->AddDevice(dev);
813}
814
815void
817{
818 m_phy->Dispose();
819 m_phy = nullptr;
820}
821
822void
824 const std::vector<uint8_t>& channelNumberPerSegment,
825 MHz_u bandWidth,
826 uint8_t bandIndex,
827 const std::vector<WifiSpectrumBandIndices>& expectedIndices,
828 const std::vector<WifiSpectrumBandFrequencies>& expectedFrequencies)
829{
830 WifiPhy::ChannelSegments channelSegments;
831 for (auto channelNumber : channelNumberPerSegment)
832 {
833 const auto& channelInfo = WifiPhyOperatingChannel::FindFirst(channelNumber,
834 MHz_u{0},
835 MHz_u{0},
838 channelSegments.emplace_back(channelInfo->number, channelInfo->width, channelInfo->band, 0);
839 }
840 m_phy->SetOperatingChannel(channelSegments);
841
842 const auto& bandInfo = m_phy->GetBand(bandWidth, bandIndex);
843 NS_ASSERT(expectedIndices.size() == expectedFrequencies.size());
844 NS_ASSERT(expectedIndices.size() == bandInfo.indices.size());
845 NS_ASSERT(expectedFrequencies.size() == bandInfo.frequencies.size());
846 for (std::size_t i = 0; i < expectedIndices.size(); ++i)
847 {
848 NS_ASSERT(bandInfo.indices.at(i).first == expectedIndices.at(i).first);
849 NS_TEST_ASSERT_MSG_EQ(bandInfo.indices.at(i).first,
850 expectedIndices.at(i).first,
851 "Incorrect start indice for segment " << i);
852 NS_TEST_ASSERT_MSG_EQ(bandInfo.indices.at(i).second,
853 expectedIndices.at(i).second,
854 "Incorrect stop indice for segment " << i);
855 NS_TEST_ASSERT_MSG_EQ(bandInfo.frequencies.at(i).first,
856 expectedFrequencies.at(i).first,
857 "Incorrect start frequency for segment " << i);
858 NS_TEST_ASSERT_MSG_EQ(bandInfo.frequencies.at(i).second,
859 expectedFrequencies.at(i).second,
860 "Incorrect stop frequency for segment " << i);
861 }
862}
863
864void
866{
867 const uint32_t indicesPer20MhzBand = 256; // based on 802.11ax carrier spacing
868 const MHz_u channelWidth{160}; // we consider the largest channel width
869 const uint8_t channelNumberContiguous160Mhz =
870 50; // channel number of the first 160 MHz band in 5 GHz band
871 const std::vector<uint8_t> channelNumberPerSegment = {42,
872 106}; // channel numbers used for 80+80MHz
873 // separation between segment at channel number 42 and segment at channel number 106
874 const MHz_u separationWidth{240};
875 for (bool contiguous160Mhz : {true /* 160 MHz */, false /* 80+80MHz */})
876 {
877 const auto guardWidth = contiguous160Mhz ? channelWidth : (channelWidth / 2);
878 uint32_t guardStopIndice = (indicesPer20MhzBand * Count20MHzSubchannels(guardWidth)) - 1;
879 std::vector<WifiSpectrumBandIndices> previousExpectedIndices{};
880 std::vector<WifiSpectrumBandFrequencies> previousExpectedFrequencies{};
881 for (auto bandWidth : {MHz_u{20}, MHz_u{40}, MHz_u{80}, MHz_u{160}})
882 {
883 const uint32_t expectedStartIndice = guardStopIndice + 1;
884 const uint32_t expectedStopIndice =
885 expectedStartIndice + (indicesPer20MhzBand * Count20MHzSubchannels(bandWidth)) - 1;
886 std::vector<WifiSpectrumBandIndices> expectedIndices{
887 {expectedStartIndice, expectedStopIndice}};
888 const Hz_u expectedStartFrequency = MHzToHz(5170);
889 const Hz_u expectedStopFrequency = MHzToHz(5170 + bandWidth);
890 std::vector<WifiSpectrumBandFrequencies> expectedFrequencies{
891 {expectedStartFrequency, expectedStopFrequency}};
892 const std::size_t numBands = (channelWidth / bandWidth);
893 for (std::size_t i = 0; i < numBands; ++i)
894 {
895 if ((bandWidth != channelWidth) && (i >= (numBands / 2)))
896 {
897 // skip DC
898 expectedIndices.at(0).first++;
899 }
900 if ((bandWidth == channelWidth) && !contiguous160Mhz)
901 {
902 // For contiguous 160 MHz, band is made of the two 80 MHz segments (previous run
903 // in the loop)
904 expectedIndices = previousExpectedIndices;
905 expectedFrequencies = previousExpectedFrequencies;
906 }
907 else if ((i == (numBands / 2)) && !contiguous160Mhz)
908 {
909 expectedIndices.at(0).first +=
910 (indicesPer20MhzBand * Count20MHzSubchannels(separationWidth));
911 expectedIndices.at(0).second +=
912 (indicesPer20MhzBand * Count20MHzSubchannels(separationWidth));
913 expectedFrequencies.at(0).first += MHzToHz(separationWidth);
914 expectedFrequencies.at(0).second += MHzToHz(separationWidth);
915 }
916 RunOne(contiguous160Mhz ? std::vector<uint8_t>{channelNumberContiguous160Mhz}
917 : channelNumberPerSegment,
918 bandWidth,
919 i,
920 expectedIndices,
921 expectedFrequencies);
922 if (!contiguous160Mhz && (bandWidth == (channelWidth / 2)))
923 {
924 previousExpectedIndices.emplace_back(expectedIndices.front());
925 previousExpectedFrequencies.emplace_back(expectedFrequencies.front());
926 }
927 expectedIndices.at(0).first = expectedIndices.at(0).second + 1;
928 expectedIndices.at(0).second =
929 expectedIndices.at(0).first +
930 (indicesPer20MhzBand * Count20MHzSubchannels(bandWidth)) - 1;
931 expectedFrequencies.at(0).first += MHzToHz(bandWidth);
932 expectedFrequencies.at(0).second += MHzToHz(bandWidth);
933 }
934 }
935 }
936
938}
939
940/**
941 * @ingroup wifi-test
942 * @ingroup tests
943 *
944 * @brief Test tracked bands in interference helper upon channel switching
945 *
946 * The test is verifying that the correct bands are tracked by the interference helper upon channel
947 * switching. It focuses on 80 and 160 MHz bands while considering 160 MHz operating channels, for
948 * both contiguous and non-contiguous cases.
949 */
951{
952 public:
954
955 private:
956 void DoSetup() override;
957 void DoTeardown() override;
958 void DoRun() override;
959
960 /**
961 * Switch channel function
962 *
963 * @param channelNumberPerSegment the channel number for each segment of the operating channel
964 * to switch to
965 */
966 void SwitchChannel(const std::vector<uint8_t>& channelNumberPerSegment);
967
968 /**
969 * Verify the bands tracked by the interference helper
970 *
971 * @param expectedTrackedBands the bands that are expected to be tracked by the interference
972 * helper
973 * @param expectedUntrackedBands the bands that are expected to be untracked by the
974 * interference helper
975 */
977 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
978 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBands);
979
980 /**
981 * Run one function
982 * @param channelNumberPerSegmentBeforeSwitching the channel number for each segment of the
983 * operating channel to switch from \param channelNumberPerSegmentAfterSwitching the channel
984 * number for each segment of the operating channel to switch to \param expectedTrackedBands the
985 * bands that are expected to be tracked by the interference helper \param expectedUntrackedBand
986 * the bands that are expected to be untracked by the interference helper
987 */
988 void RunOne(const std::vector<uint8_t>& channelNumberPerSegmentBeforeSwitching,
989 const std::vector<uint8_t>& channelNumberPerSegmentAfterSwitching,
990 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
991 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBand);
992
994};
995
997 : TestCase("SpectrumWifiPhy test channel switching for non-contiguous operating channels")
998{
999}
1000
1001void
1003{
1004 // WifiHelper::EnableLogComponents();
1005 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
1006
1007 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
1008 auto lossModel = CreateObject<FriisPropagationLossModel>();
1009 lossModel->SetFrequency(5.180e9);
1010 spectrumChannel->AddPropagationLossModel(lossModel);
1012 spectrumChannel->SetPropagationDelayModel(delayModel);
1013
1014 auto node = CreateObject<Node>();
1015 auto dev = CreateObject<WifiNetDevice>();
1017 auto interferenceHelper = CreateObject<ExtInterferenceHelper>();
1018 m_phy->SetInterferenceHelper(interferenceHelper);
1019 auto errorModel = CreateObject<NistErrorRateModel>();
1020 m_phy->SetErrorRateModel(errorModel);
1021 m_phy->SetDevice(dev);
1022 m_phy->AddChannel(spectrumChannel);
1024 dev->SetPhy(m_phy);
1025 node->AddDevice(dev);
1026}
1027
1028void
1034
1035void
1036SpectrumWifiPhyTrackedBandsTest::SwitchChannel(const std::vector<uint8_t>& channelNumberPerSegment)
1037{
1038 NS_LOG_FUNCTION(this);
1039 WifiPhy::ChannelSegments channelSegments;
1040 for (auto channelNumber : channelNumberPerSegment)
1041 {
1042 const auto& channelInfo = WifiPhyOperatingChannel::FindFirst(channelNumber,
1043 MHz_u{0},
1044 MHz_u{0},
1047 channelSegments.emplace_back(channelInfo->number, channelInfo->width, channelInfo->band, 0);
1048 }
1049 m_phy->SetOperatingChannel(channelSegments);
1050}
1051
1052void
1054 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
1055 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBands)
1056{
1057 NS_LOG_FUNCTION(this);
1058 PointerValue ptr;
1059 m_phy->GetAttribute("InterferenceHelper", ptr);
1060 auto interferenceHelper = DynamicCast<ExtInterferenceHelper>(ptr.Get<ExtInterferenceHelper>());
1061 NS_ASSERT(interferenceHelper);
1062 auto printBand = [](const std::vector<WifiSpectrumBandFrequencies>& v) {
1063 std::stringstream ss;
1064 for (const auto& [start, stop] : v)
1065 {
1066 ss << "[" << start << "-" << stop << "] ";
1067 }
1068 return ss.str();
1069 };
1070 for (const auto& expectedTrackedBand : expectedTrackedBands)
1071 {
1072 auto bandTracked = interferenceHelper->IsBandTracked(expectedTrackedBand);
1073 NS_TEST_ASSERT_MSG_EQ(bandTracked,
1074 true,
1075 "Band " << printBand(expectedTrackedBand) << " is not tracked");
1076 }
1077 for (const auto& expectedUntrackedBand : expectedUntrackedBands)
1078 {
1079 auto bandTracked = interferenceHelper->IsBandTracked(expectedUntrackedBand);
1080 NS_TEST_ASSERT_MSG_EQ(bandTracked,
1081 false,
1082 "Band " << printBand(expectedUntrackedBand)
1083 << " is unexpectedly tracked");
1084 }
1085}
1086
1087void
1089 const std::vector<uint8_t>& channelNumberPerSegmentBeforeSwitching,
1090 const std::vector<uint8_t>& channelNumberPerSegmentAfterSwitching,
1091 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
1092 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBands)
1093{
1094 NS_LOG_FUNCTION(this);
1095
1098 this,
1099 channelNumberPerSegmentBeforeSwitching);
1100
1103 this,
1104 channelNumberPerSegmentAfterSwitching);
1105
1108 this,
1109 expectedTrackedBands,
1110 expectedUntrackedBands);
1111
1113}
1114
1115void
1117{
1118 // switch from 160 MHz to 80+80 MHz
1119 RunOne({50},
1120 {42, 106},
1121 {{{MHzToHz(5170), MHzToHz(5250)}} /* first 80 MHz segment */,
1122 {{MHzToHz(5490), MHzToHz(5570)}} /* second 80 MHz segment */,
1123 {{MHzToHz(5170), MHzToHz(5250)},
1124 {MHzToHz(5490),
1125 MHzToHz(5570)}} /* non-contiguous 160 MHz band made of the two segments */},
1126 {{{MHzToHz(5170), MHzToHz(5330)}} /* full 160 MHz band should have been removed */});
1127
1128 // switch from 80+80 MHz to 160 MHz
1129 RunOne(
1130 {42, 106},
1131 {50},
1132 {{{MHzToHz(5170), MHzToHz(5330)}} /* full 160 MHz band */,
1133 {{MHzToHz(5170), MHzToHz(5250)}} /* first 80 MHz segment is part of the 160 MHz channel*/},
1134 {{{MHzToHz(5490), MHzToHz(5570)}} /* second 80 MHz segment should have been removed */,
1135 {{MHzToHz(5170), MHzToHz(5250)},
1136 {MHzToHz(5490),
1137 MHzToHz(5570)}} /* non-contiguous 160 MHz band should have been removed */});
1138
1139 // switch from 80+80 MHz to 80+80 MHz with segment swap
1140 RunOne({42, 106},
1141 {106, 42},
1142 {{{MHzToHz(5490), MHzToHz(5570)}} /* first 80 MHz segment */,
1143 {{MHzToHz(5490), MHzToHz(5570)}} /* second 80 MHz segment */,
1144 {{MHzToHz(5170), MHzToHz(5250)},
1145 {MHzToHz(5490),
1146 MHzToHz(5570)}} /* non-contiguous 160 MHz band made of the two segments */},
1147 {});
1148
1149 // switch from 80+80 MHz to another 80+80 MHz with one common segment
1150 RunOne({42, 106},
1151 {106, 138},
1152 {{{MHzToHz(5490), MHzToHz(5570)}} /* first 80 MHz segment */,
1153 {{MHzToHz(5650), MHzToHz(5730)}} /* second 80 MHz segment */,
1154 {{MHzToHz(5490), MHzToHz(5570)},
1155 {MHzToHz(5650),
1156 MHzToHz(5730)}} /* non-contiguous 160 MHz band made of the two segments */},
1157 {{{MHzToHz(5170),
1158 MHzToHz(5250)}} /* 80 MHz segment at channel 42 should have been removed */,
1159 {{MHzToHz(5170), MHzToHz(5250)},
1160 {MHzToHz(5490),
1161 MHzToHz(5570)}} /* previous non-contiguous 160 MHz band should have been removed */});
1162
1163 // switch from 80+80 MHz to another 80+80 MHz with no common segment
1164 RunOne({42, 106},
1165 {122, 155},
1166 {{{MHzToHz(5570), MHzToHz(5650)}} /* first 80 MHz segment */,
1167 {{MHzToHz(5735), MHzToHz(5815)}} /* second 80 MHz segment */,
1168 {{MHzToHz(5570), MHzToHz(5650)},
1169 {MHzToHz(5735),
1170 MHzToHz(5815)}} /* non-contiguous 160 MHz band made of the two segments */},
1171 {{{MHzToHz(5170),
1172 MHzToHz(5250)}} /* previous first 80 MHz segment should have been removed */,
1173 {{MHzToHz(5490),
1174 MHzToHz(5570)}} /* previous second 80 MHz segment should have been removed */,
1175 {{MHzToHz(5170), MHzToHz(5250)},
1176 {MHzToHz(5490),
1177 MHzToHz(5570)}} /* previous non-contiguous 160 MHz band should have been removed */});
1178
1180}
1181
1182/**
1183 * @ingroup wifi-test
1184 * @ingroup tests
1185 *
1186 * @brief Test 80+80MHz transmission
1187 *
1188 * The test verifies that two non-contiguous segments are handled by the spectrum PHY
1189 * to transmit 160 MHz PPDUs when the operating channel is configured as 80+80MHz.
1190 *
1191 * The test first considers a contiguous 160 MHz segment and generate interference on the second
1192 * 80 MHz band to verify reception fails in this scenario. Then, a similar interference
1193 * is generated when a 80+80MHz operating channel is configured, where the first frequency segment
1194 * occupies the first 80 MHz band of the previous 160 MHz operating channel. The reception should
1195 * succeed in that scenario, which demonstrates the second 80 MHz band of the operating channel is
1196 * no longer occupying that spectrum portion (the interference is hence is the gap between the two
1197 * frequency segments). Finally, the test also generates interference on each of the frequency
1198 * segments when the operating channel is 80+80MHz, to demonstrate the frequency segments are
1199 * positioned as expected.
1200 */
1202{
1203 public:
1205
1206 private:
1207 void DoSetup() override;
1208 void DoTeardown() override;
1209 void DoRun() override;
1210
1211 /**
1212 * Run one function
1213 * @param channelNumbers the channel number for each segment of the operating channel
1214 * @param interferenceCenterFrequency the center frequency of the interference signal to
1215 * generate
1216 * @param interferenceBandWidth the band width of the interference signal to generate
1217 * @param expectSuccess flag to indicate whether reception is expected to be successful
1218 */
1219 void RunOne(const std::vector<uint8_t>& channelNumbers,
1220 MHz_u interferenceCenterFrequency,
1221 MHz_u interferenceBandWidth,
1222 bool expectSuccess);
1223
1224 /**
1225 * Switch channel function
1226 *
1227 * @param channelNumbers the channel number for each segment of the operating channel
1228 * to switch to
1229 */
1230 void SwitchChannel(const std::vector<uint8_t>& channelNumbers);
1231
1232 /**
1233 * Send 160MHz PPDU function
1234 */
1235 void Send160MhzPpdu();
1236
1237 /**
1238 * Generate interference function
1239 * @param interferencePsd the PSD of the interference to be generated
1240 * @param duration the duration of the interference
1241 */
1242 void GenerateInterference(Ptr<SpectrumValue> interferencePsd, Time duration);
1243
1244 /**
1245 * Stop interference function
1246 */
1247 void StopInterference();
1248
1249 /**
1250 * Receive success function for STA
1251 * @param psdu the PSDU
1252 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
1253 * @param txVector the transmit vector
1254 * @param statusPerMpdu reception status per MPDU
1255 */
1257 RxSignalInfo rxSignalInfo,
1258 const WifiTxVector& txVector,
1259 const std::vector<bool>& statusPerMpdu);
1260
1261 /**
1262 * Receive failure function for STA
1263 * @param psdu the PSDU
1264 */
1266
1267 /**
1268 * Verify results
1269 *
1270 * @param expectSuccess flag to indicate whether reception is expected to be successful
1271 */
1272 void CheckResults(bool expectSuccess);
1273
1277
1278 uint32_t m_countRxSuccessSta; ///< count RX success for STA
1279 uint32_t m_countRxFailureSta; ///< count RX failure for STA
1280};
1281
1283 : TestCase("SpectrumWifiPhy test 80+80MHz transmission"),
1284 m_countRxSuccessSta(0),
1285 m_countRxFailureSta(0)
1286{
1287}
1288
1289void
1290SpectrumWifiPhy80Plus80Test::SwitchChannel(const std::vector<uint8_t>& channelNumbers)
1291{
1292 NS_LOG_FUNCTION(this);
1293 WifiPhy::ChannelSegments channelSegments;
1294 for (auto channelNumber : channelNumbers)
1295 {
1296 const auto& channelInfo = WifiPhyOperatingChannel::FindFirst(channelNumber,
1297 MHz_u{0},
1298 MHz_u{0},
1301 channelSegments.emplace_back(channelInfo->number, channelInfo->width, channelInfo->band, 0);
1302 }
1303 m_phyAp->SetOperatingChannel(channelSegments);
1304 m_phySta->SetOperatingChannel(channelSegments);
1305}
1306
1307void
1309{
1310 NS_LOG_FUNCTION(this);
1311
1312 WifiTxVector txVector{HePhy::GetHeMcs7(),
1313 0,
1315 NanoSeconds(800),
1316 1,
1317 1,
1318 0,
1319 MHz_u{160},
1320 false,
1321 false,
1322 false};
1323
1324 auto pkt = Create<Packet>(1000);
1325 WifiMacHeader hdr;
1327 auto psdu = Create<WifiPsdu>(pkt, hdr);
1328
1329 m_phyAp->Send(psdu, txVector);
1330}
1331
1332void
1334{
1335 m_phyInterferer->SetTxPowerSpectralDensity(interferencePsd);
1336 m_phyInterferer->SetPeriod(duration);
1337 m_phyInterferer->Start();
1339}
1340
1341void
1346
1347void
1349 RxSignalInfo rxSignalInfo,
1350 const WifiTxVector& txVector,
1351 const std::vector<bool>& /*statusPerMpdu*/)
1352{
1353 NS_LOG_FUNCTION(this << *psdu << rxSignalInfo << txVector);
1355}
1356
1357void
1363
1364void
1366{
1367 NS_LOG_FUNCTION(this << expectSuccess);
1369 expectSuccess,
1370 "Reception should be "
1371 << (expectSuccess ? "successful" : "unsuccessful"));
1372}
1373
1374void
1376{
1377 // WifiHelper::EnableLogComponents();
1378 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
1379
1380 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
1381 auto lossModel = CreateObject<FriisPropagationLossModel>();
1382 spectrumChannel->AddPropagationLossModel(lossModel);
1384 spectrumChannel->SetPropagationDelayModel(delayModel);
1385
1386 auto apNode = CreateObject<Node>();
1387 auto apDev = CreateObject<WifiNetDevice>();
1389 auto apInterferenceHelper = CreateObject<InterferenceHelper>();
1390 m_phyAp->SetInterferenceHelper(apInterferenceHelper);
1391 auto apErrorModel = CreateObject<NistErrorRateModel>();
1392 m_phyAp->SetErrorRateModel(apErrorModel);
1393 m_phyAp->SetDevice(apDev);
1394 m_phyAp->AddChannel(spectrumChannel);
1395 m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
1397 m_phyAp->SetMobility(apMobility);
1398 apDev->SetPhy(m_phyAp);
1399 apNode->AggregateObject(apMobility);
1400 apNode->AddDevice(apDev);
1401
1402 auto staNode = CreateObject<Node>();
1403 auto staDev = CreateObject<WifiNetDevice>();
1405 auto staInterferenceHelper = CreateObject<InterferenceHelper>();
1406 m_phySta->SetInterferenceHelper(staInterferenceHelper);
1407 auto staErrorModel = CreateObject<NistErrorRateModel>();
1408 m_phySta->SetErrorRateModel(staErrorModel);
1409 m_phySta->SetDevice(staDev);
1410 m_phySta->AddChannel(spectrumChannel);
1411 m_phySta->ConfigureStandard(WIFI_STANDARD_80211ax);
1413 m_phySta->SetReceiveErrorCallback(
1416 m_phySta->SetMobility(staMobility);
1417 staDev->SetPhy(m_phySta);
1418 staNode->AggregateObject(staMobility);
1419 staNode->AddDevice(staDev);
1420
1421 auto interfererNode = CreateObject<Node>();
1422 auto interfererDev = CreateObject<NonCommunicatingNetDevice>();
1424 m_phyInterferer->SetDevice(interfererDev);
1425 m_phyInterferer->SetChannel(spectrumChannel);
1426 m_phyInterferer->SetDutyCycle(1);
1427 interfererNode->AddDevice(interfererDev);
1428}
1429
1430void
1432{
1433 m_phyAp->Dispose();
1434 m_phyAp = nullptr;
1435 m_phySta->Dispose();
1436 m_phySta = nullptr;
1437 m_phyInterferer->Dispose();
1438 m_phyInterferer = nullptr;
1439}
1440
1441void
1442SpectrumWifiPhy80Plus80Test::RunOne(const std::vector<uint8_t>& channelNumbers,
1443 MHz_u interferenceCenterFrequency,
1444 MHz_u interferenceBandWidth,
1445 bool expectSuccess)
1446{
1447 // reset counters
1450
1453 this,
1454 channelNumbers);
1455
1456 // create info about interference to generate
1457 BandInfo bandInfo{.fl = MHzToHz(interferenceCenterFrequency - (interferenceBandWidth / 2)),
1458 .fc = MHzToHz(interferenceCenterFrequency),
1459 .fh = MHzToHz(interferenceCenterFrequency + (interferenceBandWidth / 2))};
1460 auto spectrumInterference = Create<SpectrumModel>(Bands{bandInfo});
1461 auto interferencePsd = Create<SpectrumValue>(spectrumInterference);
1462 Watt_u interferencePower{0.1};
1463 *interferencePsd = interferencePower / (interferenceBandWidth * 20e6);
1464
1467 this,
1468 interferencePsd,
1469 MilliSeconds(100));
1470
1472
1475 this,
1476 expectSuccess);
1477
1479}
1480
1481void
1483{
1484 // Test transmission over contiguous 160 MHz (channel 50) and interference generated in
1485 // the second half of the channel width (channel 58, i.e. center frequency 5290 and bandwidth 80
1486 // MHz). The reception should fail because the interference occupies half the channel width used
1487 // for the transmission.
1488 // ┌──────────────────┐
1489 // Interference │ channel 58 │
1490 // │ 5290 MHz, 80 MHz │
1491 // └──────────────────┘
1492 //
1493 // ┌──────────────────────────────────────┐
1494 // Operating Channel │ channel 50 │
1495 // │ 5250 MHz, 160 MHz │
1496 // └──────────────────────────────────────┘
1497 //
1498 RunOne({50}, MHz_u{5290}, MHz_u{80}, false);
1499
1500 // Test transmission over non-contiguous 160 MHz (i.e. 80+80MHz) and same interference as in
1501 // previous run. The reception should succeed because the interference is located between the
1502 // two segments.
1503 // ┌──────────────────┐
1504 // Interference │ channel 58 │
1505 // │ 5290 MHz, 80 MHz │
1506 // └──────────────────┘
1507 //
1508 // ┌──────────────────┐ ┌──────────────────┐
1509 // Operating Channel │ channel 42 │ │ channel 106 │
1510 // │80+80MHz segment 0│ │80+80MHz segment 1│
1511 // └──────────────────┘ └──────────────────┘
1512 //
1513 RunOne({42, 106}, MHz_u{5290}, MHz_u{80}, true);
1514
1515 // Test transmission over non-contiguous 160 MHz (i.e. 80+80MHz) and interference generated on
1516 // the first segment of the channel width (channel 42, i.e. center frequency 5210 and bandwidth
1517 // 80 MHz). The reception should fail because the interference occupies half the channel width
1518 // used for the transmission.
1519 // ┌──────────────────┐
1520 // Interference │ channel 42 │
1521 // │ 5210 MHz, 80 MHz │
1522 // └──────────────────┘
1523 //
1524 // ┌──────────────────┐ ┌──────────────────┐
1525 // Operating Channel │ channel 42 │ │ channel 106 │
1526 // │80+80MHz segment 0│ │80+80MHz segment 1│
1527 // └──────────────────┘ └──────────────────┘
1528 //
1529 RunOne({42, 106}, MHz_u{5210}, MHz_u{80}, false);
1530
1531 // Test transmission over non-contiguous 160 MHz (i.e. 80+80MHz) and interference generated on
1532 // the second segment of the channel width (channel 42, i.e. center frequency 5210 and bandwidth
1533 // 80 MHz). The reception should fail because the interference occupies half the channel width
1534 // used for the transmission.
1535 // ┌──────────────────┐
1536 // Interference │ channel 106 │
1537 // │ 5530 MHz, 80 MHz │
1538 // └──────────────────┘
1539 //
1540 // ┌──────────────────┐ ┌──────────────────┐
1541 // Operating Channel │ channel 42 │ │ channel 106 │
1542 // │80+80MHz segment 0│ │80+80MHz segment 1│
1543 // └──────────────────┘ └──────────────────┘
1544 //
1545 RunOne({42, 106}, MHz_u{5530}, MHz_u{80}, false);
1546
1548}
1549
1550/**
1551 * @ingroup wifi-test
1552 * @ingroup tests
1553 *
1554 * @brief Spectrum Wifi Phy Multiple Spectrum Test
1555 *
1556 * This test is testing the ability to plug multiple spectrum channels to the spectrum wifi PHY.
1557 * It considers 4 TX-RX PHY pairs that are independent from each others and are plugged to different
1558 * spectrum channels that are covering different frequency range. Each RX PHY is also attached to
1559 * each of the other 3 spectrum channels it can switch to.
1560 *
1561 * In the first scenario, we consider the default case where each TX-RX PHY pairs are operating on
1562 * different frequency ranges and hence using independent spectrum channels. We validate that no
1563 * packets is received from other TX PHYs attached to different spectrum channels and we also verify
1564 * the amount of connected PHYs to each spectrum channel is exactly 2. The test also makes sure each
1565 * PHY has only one active spectrum channel and that the active one is operating at the expected
1566 * frequency range.
1567 *
1568 * In the second scenario, we consecutively switch the channel of all RX PHYs to the one of each TX
1569 * PHY. We validate that packets are received by all PHYs and we also verify the amount of connected
1570 * PHYs to each spectrum channels is either 5 (1 TX PHY and 4 RX PHYs) or 1 (the TX PHY left alone).
1571 */
1573{
1574 public:
1575 /// Enumeration for channel switching scenarios
1576 enum class ChannelSwitchScenario : uint8_t
1577 {
1578 BEFORE_TX = 0, //!< start TX after channel switch is completed
1579 BETWEEN_TX_RX //!< perform channel switch during propagation delay (after TX and before RX)
1580 };
1581
1582 /**
1583 * Constructor
1584 *
1585 * @param trackSignalsInactiveInterfaces flag to indicate whether signals coming from inactive
1586 * spectrum PHY interfaces shall be tracked during the test
1587 * @param chanSwitchScenario the channel switching scenario to consider for the test
1588 */
1589 SpectrumWifiPhyMultipleInterfacesTest(bool trackSignalsInactiveInterfaces,
1590 ChannelSwitchScenario chanSwitchScenario);
1591
1592 private:
1593 void DoSetup() override;
1594 void DoTeardown() override;
1595 void DoRun() override;
1596
1597 /**
1598 * Switch channel function
1599 *
1600 * @param phy the PHY to switch
1601 * @param band the PHY band to use
1602 * @param channelNumber number the channel number to use
1603 * @param channelWidth the channel width to use
1604 * @param listenerIndex index of the listener for that PHY, if PHY is a RX PHY
1605 */
1607 WifiPhyBand band,
1608 uint8_t channelNumber,
1609 MHz_u channelWidth,
1610 std::optional<std::size_t> listenerIndex);
1611
1612 /**
1613 * Send PPDU function
1614 *
1615 * @param phy the PHY to transmit the signal
1616 * @param txPower the power to transmit the signal (this is also the received power since we do
1617 * not have propagation loss to simplify) \param payloadSize the payload size in bytes
1618 */
1619 void SendPpdu(Ptr<SpectrumWifiPhy> phy, dBm_u txPower, uint32_t payloadSize);
1620
1621 /**
1622 * Callback triggered when a packet is received by a PHY
1623 * @param index the index to identify the RX PHY
1624 * @param packet the received packet
1625 * @param rxPowersW the received power per channel band in watts
1626 */
1627 void RxCallback(std::size_t index,
1628 Ptr<const Packet> packet,
1629 RxPowerWattPerChannelBand rxPowersW);
1630
1631 /**
1632 * Receive success function
1633 * @param index index of the RX STA
1634 * @param psdu the PSDU
1635 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
1636 * @param txVector the transmit vector
1637 * @param statusPerMpdu reception status per MPDU
1638 */
1639 void RxSuccess(std::size_t index,
1641 RxSignalInfo rxSignalInfo,
1642 const WifiTxVector& txVector,
1643 const std::vector<bool>& statusPerMpdu);
1644
1645 /**
1646 * Receive failure function
1647 * @param index index of the RX STA
1648 * @param psdu the PSDU
1649 */
1650 void RxFailure(std::size_t index, Ptr<const WifiPsdu> psdu);
1651
1652 /**
1653 * Schedule now to check the interferences
1654 * @param phy the PHY for which the check has to be executed
1655 * @param freqRange the frequency range for which the check has to be executed
1656 * @param band the band for which the check has to be executed
1657 * @param interferencesExpected flag whether interferences are expected to have been tracked
1658 */
1660 const FrequencyRange& freqRange,
1661 const WifiSpectrumBandInfo& band,
1662 bool interferencesExpected);
1663
1664 /**
1665 * Check the interferences
1666 * @param phy the PHY for which the check has to be executed
1667 * @param band the band for which the check has to be executed
1668 * @param interferencesExpected flag whether interferences are expected to have been tracked
1669 */
1671 const WifiSpectrumBandInfo& band,
1672 bool interferencesExpected);
1673
1674 /**
1675 * Verify results
1676 *
1677 * @param index the index to identify the RX PHY to check
1678 * @param expectedNumRx the expected number of RX events for that PHY
1679 * @param expectedNumRxSuccess the expected amount of successfully received packets
1680 * @param expectedRxBytes the expected amount of received bytes
1681 * @param expectedFrequencyRangeActiveRfInterface the expected frequency range (in MHz) of the
1682 * active RF interface
1683 * @param expectedConnectedPhysPerChannel the expected number of PHYs attached for each spectrum
1684 * channel
1685 */
1686 void CheckResults(std::size_t index,
1687 uint32_t expectedNumRx,
1688 uint32_t expectedNumRxSuccess,
1689 uint32_t expectedRxBytes,
1690 FrequencyRange expectedFrequencyRangeActiveRfInterface,
1691 const std::vector<std::size_t>& expectedConnectedPhysPerChannel);
1692
1693 /**
1694 * Verify CCA indication reported by a given PHY
1695 *
1696 * @param index the index to identify the RX PHY to check
1697 * @param expectedCcaBusyIndication flag to indicate whether a CCA BUSY notification is expected
1698 * @param switchingDelay delay between the TX has started and the time RX switched to the TX
1699 * channel
1700 * @param propagationDelay the propagation delay
1701 */
1702 void CheckCcaIndication(std::size_t index,
1703 bool expectedCcaBusyIndication,
1704 Time switchingDelay,
1705 Time propagationDelay);
1706
1707 /**
1708 * Verify rxing state of the interference helper
1709 *
1710 * @param phy the PHY to which the interference helper instance is attached
1711 * @param rxingExpected flag whether the interference helper is expected to be in rxing state or
1712 * not
1713 */
1714 void CheckRxingState(Ptr<SpectrumWifiPhy> phy, bool rxingExpected);
1715
1716 /**
1717 * Reset function
1718 */
1719 void Reset();
1720
1721 bool
1722 m_trackSignalsInactiveInterfaces; //!< flag to indicate whether signals coming from inactive
1723 //!< spectrum PHY interfaces are tracked during the test
1725 m_chanSwitchScenario; //!< the channel switch scenario to consider for the test
1726 std::vector<Ptr<MultiModelSpectrumChannel>> m_spectrumChannels; //!< Spectrum channels
1727 std::vector<Ptr<SpectrumWifiPhy>> m_txPhys{}; //!< TX PHYs
1728 std::vector<Ptr<SpectrumWifiPhy>> m_rxPhys{}; //!< RX PHYs
1729 std::vector<std::shared_ptr<TestPhyListener>> m_listeners{}; //!< listeners
1730
1731 std::vector<uint32_t> m_counts; //!< count number of packets received by PHYs
1732 std::vector<uint32_t>
1733 m_countRxSuccess; //!< count number of packets successfully received by PHYs
1734 std::vector<uint32_t>
1735 m_countRxFailure; //!< count number of packets unsuccessfully received by PHYs
1736 std::vector<uint32_t> m_rxBytes; //!< count number of received bytes
1737
1738 Time m_lastTxStart{0}; //!< hold the time at which the last transmission started
1739 Time m_lastTxEnd{0}; //!< hold the time at which the last transmission ended
1740};
1741
1743 bool trackSignalsInactiveInterfaces,
1744 ChannelSwitchScenario chanSwitchScenario)
1745 : TestCase{"SpectrumWifiPhy test operation with multiple RF interfaces"},
1746 m_trackSignalsInactiveInterfaces{trackSignalsInactiveInterfaces},
1747 m_chanSwitchScenario{chanSwitchScenario}
1748{
1749}
1750
1751void
1753 WifiPhyBand band,
1754 uint8_t channelNumber,
1755 MHz_u channelWidth,
1756 std::optional<std::size_t> listenerIndex)
1757{
1758 NS_LOG_FUNCTION(this << phy << band << +channelNumber << channelWidth);
1759 if (listenerIndex)
1760 {
1761 auto& listener = m_listeners.at(*listenerIndex);
1762 listener->m_notifyMaybeCcaBusyStart = 0;
1763 listener->m_ccaBusyStart = Seconds(0);
1764 listener->m_ccaBusyEnd = Seconds(0);
1765 }
1766 phy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNumber, channelWidth, band, 0});
1767 // verify rxing state of interference helper is reset after channel switch
1769 this,
1770 phy,
1771 false);
1772}
1773
1774void
1776 dBm_u txPower,
1777 uint32_t payloadSize)
1778{
1779 NS_LOG_FUNCTION(this << phy << txPower << payloadSize << phy->GetCurrentFrequencyRange()
1780 << phy->GetChannelWidth() << phy->GetChannelNumber());
1781
1783 0,
1785 NanoSeconds(800),
1786 1,
1787 1,
1788 0,
1789 MHz_u{20},
1790 false,
1791 false};
1792 Ptr<Packet> pkt = Create<Packet>(payloadSize);
1793 WifiMacHeader hdr;
1795 hdr.SetQosTid(0);
1796 hdr.SetAddr1(Mac48Address("00:00:00:00:00:01"));
1797 hdr.SetSequenceNumber(1);
1798 Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
1799
1802 txVector,
1803 phy->GetPhyBand());
1804 phy->SetTxPowerStart(txPower);
1805 phy->SetTxPowerEnd(txPower);
1806 phy->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
1807}
1808
1809void
1811 Ptr<const Packet> packet,
1812 RxPowerWattPerChannelBand /*rxPowersW*/)
1813{
1814 auto phy = m_rxPhys.at(index);
1815 const auto payloadBytes = packet->GetSize() - 30;
1816 NS_LOG_FUNCTION(this << index << payloadBytes << phy->GetCurrentFrequencyRange()
1817 << phy->GetChannelWidth() << phy->GetChannelNumber());
1818 m_counts.at(index)++;
1819 m_rxBytes.at(index) += payloadBytes;
1820}
1821
1822void
1825 RxSignalInfo rxSignalInfo,
1826 const WifiTxVector& txVector,
1827 const std::vector<bool>& /*statusPerMpdu*/)
1828{
1829 NS_LOG_FUNCTION(this << index << *psdu << rxSignalInfo << txVector);
1830 m_countRxSuccess.at(index)++;
1831}
1832
1833void
1835{
1836 NS_LOG_FUNCTION(this << index << *psdu);
1837 m_countRxFailure.at(index)++;
1838}
1839
1840void
1842 const FrequencyRange& freqRange,
1843 const WifiSpectrumBandInfo& band,
1844 bool interferencesExpected)
1845{
1846 if ((!m_trackSignalsInactiveInterfaces) && (phy->GetCurrentFrequencyRange() != freqRange))
1847 {
1848 // ignore since no bands for that range exists in interference helper in that case
1849 return;
1850 }
1851 // This is needed to make sure PHY state will be checked as the last event if a state change
1852 // occurred at the exact same time as the check
1854 this,
1855 phy,
1856 band,
1857 interferencesExpected);
1858}
1859
1860void
1862 const WifiSpectrumBandInfo& band,
1863 bool interferencesExpected)
1864{
1865 NS_LOG_FUNCTION(this << phy << band << interferencesExpected);
1866 PointerValue ptr;
1867 phy->GetAttribute("InterferenceHelper", ptr);
1868 auto interferenceHelper = DynamicCast<InterferenceHelper>(ptr.Get<InterferenceHelper>());
1869 NS_ASSERT(interferenceHelper);
1870 const auto energyDuration = interferenceHelper->GetEnergyDuration(Watt_u{0}, band);
1871 NS_TEST_ASSERT_MSG_EQ(energyDuration.IsStrictlyPositive(),
1872 interferencesExpected,
1873 "Incorrect interferences detection");
1874}
1875
1876void
1878 std::size_t index,
1879 uint32_t expectedNumRx,
1880 uint32_t expectedNumRxSuccess,
1881 uint32_t expectedRxBytes,
1882 FrequencyRange expectedFrequencyRangeActiveRfInterface,
1883 const std::vector<std::size_t>& expectedConnectedPhysPerChannel)
1884{
1885 NS_LOG_FUNCTION(this << index << expectedNumRx << expectedNumRxSuccess << expectedRxBytes
1886 << expectedFrequencyRangeActiveRfInterface);
1887 const auto phy = m_rxPhys.at(index);
1888 std::size_t numActiveInterfaces = 0;
1889 for (const auto& [freqRange, interface] : phy->GetSpectrumPhyInterfaces())
1890 {
1891 const auto expectedActive = (freqRange == expectedFrequencyRangeActiveRfInterface);
1892 const auto isActive = (interface == phy->GetCurrentInterface());
1893 NS_TEST_ASSERT_MSG_EQ(isActive, expectedActive, "Incorrect active interface");
1894 if (isActive)
1895 {
1896 numActiveInterfaces++;
1897 }
1898 }
1899 NS_TEST_ASSERT_MSG_EQ(numActiveInterfaces, 1, "There should always be one active interface");
1900 NS_ASSERT(expectedConnectedPhysPerChannel.size() == m_spectrumChannels.size());
1901 for (std::size_t i = 0; i < m_spectrumChannels.size(); ++i)
1902 {
1903 NS_TEST_ASSERT_MSG_EQ(m_spectrumChannels.at(i)->GetNDevices(),
1904 expectedConnectedPhysPerChannel.at(i),
1905 "Incorrect number of PHYs attached to the spectrum channel");
1906 }
1907 NS_TEST_ASSERT_MSG_EQ(m_counts.at(index), expectedNumRx, "Unexpected amount of RX events");
1909 expectedNumRxSuccess,
1910 "Unexpected amount of successfully received packets");
1912 0,
1913 "Unexpected amount of unsuccessfully received packets");
1914 NS_TEST_ASSERT_MSG_EQ(m_listeners.at(index)->m_notifyRxStart,
1915 expectedNumRxSuccess,
1916 "Unexpected amount of RX payload start indication");
1917}
1918
1919void
1921 bool expectedCcaBusyIndication,
1922 Time switchingDelay,
1923 Time propagationDelay)
1924{
1925 const auto expectedCcaBusyStart =
1926 expectedCcaBusyIndication ? m_lastTxStart + switchingDelay : Seconds(0);
1927 const auto expectedCcaBusyEnd =
1928 expectedCcaBusyIndication ? m_lastTxEnd + propagationDelay : Seconds(0);
1929 NS_LOG_FUNCTION(this << index << expectedCcaBusyIndication << expectedCcaBusyStart
1930 << expectedCcaBusyEnd);
1931 auto& listener = m_listeners.at(index);
1932 const auto ccaBusyIndication = (listener->m_notifyMaybeCcaBusyStart > 0);
1933 const auto ccaBusyStart = listener->m_ccaBusyStart;
1934 const auto ccaBusyEnd = listener->m_ccaBusyEnd;
1935 NS_TEST_ASSERT_MSG_EQ(ccaBusyIndication,
1936 expectedCcaBusyIndication,
1937 "CCA busy indication check failed");
1938 NS_TEST_ASSERT_MSG_EQ(ccaBusyStart, expectedCcaBusyStart, "CCA busy start mismatch");
1939 NS_TEST_ASSERT_MSG_EQ(ccaBusyEnd, expectedCcaBusyEnd, "CCA busy end mismatch");
1940}
1941
1942void
1944{
1945 NS_LOG_FUNCTION(this << phy << rxingExpected);
1946 PointerValue ptr;
1947 phy->GetAttribute("InterferenceHelper", ptr);
1948 auto interferenceHelper = DynamicCast<ExtInterferenceHelper>(ptr.Get<ExtInterferenceHelper>());
1949 NS_ASSERT(interferenceHelper);
1950 NS_TEST_ASSERT_MSG_EQ(interferenceHelper->IsRxing(), rxingExpected, "Incorrect rxing state");
1951}
1952
1953void
1955{
1956 NS_LOG_FUNCTION(this);
1957 for (auto& count : m_counts)
1958 {
1959 count = 0;
1960 }
1961 for (auto& listener : m_listeners)
1962 {
1963 listener->Reset();
1964 }
1965 // restore all RX PHYs to initial channels
1966 for (std::size_t rxPhyIndex = 0; rxPhyIndex < m_rxPhys.size(); ++rxPhyIndex)
1967 {
1968 auto txPhy = m_txPhys.at(rxPhyIndex);
1969 SwitchChannel(m_rxPhys.at(rxPhyIndex),
1970 txPhy->GetPhyBand(),
1971 txPhy->GetChannelNumber(),
1972 txPhy->GetChannelWidth(),
1973 rxPhyIndex);
1974 }
1975 // reset counters
1976 for (auto& countRxSuccess : m_countRxSuccess)
1977 {
1978 countRxSuccess = 0;
1979 }
1980 for (auto& countRxFailure : m_countRxFailure)
1981 {
1982 countRxFailure = 0;
1983 }
1984 for (auto& rxBytes : m_rxBytes)
1985 {
1986 rxBytes = 0;
1987 }
1988}
1989
1990void
1992{
1993 NS_LOG_FUNCTION(this);
1994
1995 // WifiHelper::EnableLogComponents();
1996 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
1997
1998 NodeContainer wifiApNode(1);
1999 NodeContainer wifiStaNode(1);
2000
2001 WifiHelper wifi;
2002 wifi.SetStandard(WIFI_STANDARD_80211be);
2003
2004 SpectrumWifiPhyHelper phyHelper(4);
2005 phyHelper.SetInterferenceHelper("ns3::ExtInterferenceHelper");
2007
2008 struct SpectrumPhyInterfaceInfo
2009 {
2010 FrequencyRange range; ///< frequency range covered by the interface
2011 uint8_t number; ///< channel number the interface operates on
2012 WifiPhyBand band; ///< PHY band the interface operates on
2013 std::string bandName; ///< name of the PHY band the interface operates on
2014 };
2015
2016 const FrequencyRange WIFI_SPECTRUM_5_GHZ_LOW{
2020 const FrequencyRange WIFI_SPECTRUM_5_GHZ_HIGH{
2024
2025 const std::vector<SpectrumPhyInterfaceInfo> interfaces{
2026 {WIFI_SPECTRUM_2_4_GHZ, 2, WIFI_PHY_BAND_2_4GHZ, "BAND_2_4GHZ"},
2027 {WIFI_SPECTRUM_5_GHZ_LOW, 42, WIFI_PHY_BAND_5GHZ, "BAND_5GHZ"},
2028 {WIFI_SPECTRUM_5_GHZ_HIGH, 163, WIFI_PHY_BAND_5GHZ, "BAND_5GHZ"},
2029 {WIFI_SPECTRUM_6_GHZ, 215, WIFI_PHY_BAND_6GHZ, "BAND_6GHZ"}};
2030
2031 for (std::size_t i = 0; i < interfaces.size(); ++i)
2032 {
2033 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
2035 spectrumChannel->SetPropagationDelayModel(delayModel);
2036 std::ostringstream oss;
2037 oss << "{" << +interfaces.at(i).number << ", 0, " << interfaces.at(i).bandName << ", 0}";
2038 phyHelper.Set(i, "ChannelSettings", StringValue(oss.str()));
2039 phyHelper.AddChannel(spectrumChannel, interfaces.at(i).range);
2040 m_spectrumChannels.emplace_back(spectrumChannel);
2041 }
2042
2043 WifiMacHelper mac;
2044 mac.SetType("ns3::ApWifiMac", "BeaconGeneration", BooleanValue(false));
2045 phyHelper.Set("TrackSignalsFromInactiveInterfaces", BooleanValue(false));
2046 auto apDevice = wifi.Install(phyHelper, mac, wifiApNode.Get(0));
2047
2048 mac.SetType("ns3::StaWifiMac", "ActiveProbing", BooleanValue(false));
2049 phyHelper.Set("TrackSignalsFromInactiveInterfaces",
2051 auto staDevice = wifi.Install(phyHelper, mac, wifiStaNode.Get(0));
2052
2053 MobilityHelper mobility;
2054 auto positionAlloc = CreateObject<ListPositionAllocator>();
2055
2056 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
2057 positionAlloc->Add(Vector(10.0, 0.0, 0.0));
2058 mobility.SetPositionAllocator(positionAlloc);
2059
2060 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
2061 mobility.Install(wifiApNode);
2062 mobility.Install(wifiStaNode);
2063
2064 for (std::size_t i = 0; i < interfaces.size(); ++i)
2065 {
2066 auto txPhy =
2067 DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetPhy(i));
2069 {
2070 txPhy->SetAttribute("ChannelSwitchDelay", TimeValue(NanoSeconds(1)));
2071 }
2072 m_txPhys.push_back(txPhy);
2073
2074 const auto index = m_rxPhys.size();
2075 auto rxPhy =
2076 DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(staDevice.Get(0))->GetPhy(i));
2078 {
2079 rxPhy->SetAttribute("ChannelSwitchDelay", TimeValue(NanoSeconds(1)));
2080 }
2081 rxPhy->TraceConnectWithoutContext(
2082 "PhyRxBegin",
2084
2085 rxPhy->SetReceiveOkCallback(
2087 rxPhy->SetReceiveErrorCallback(
2089
2090 auto listener = std::make_shared<TestPhyListener>();
2091 rxPhy->RegisterListener(listener);
2092 m_listeners.push_back(std::move(listener));
2093
2094 m_rxPhys.push_back(rxPhy);
2095 m_counts.push_back(0);
2096 m_countRxSuccess.push_back(0);
2097 m_countRxFailure.push_back(0);
2098 m_rxBytes.push_back(0);
2099 }
2100}
2101
2102void
2104{
2105 NS_LOG_FUNCTION(this);
2106 for (auto& phy : m_txPhys)
2107 {
2108 phy->Dispose();
2109 phy = nullptr;
2110 }
2111 for (auto& phy : m_rxPhys)
2112 {
2113 phy->Dispose();
2114 phy = nullptr;
2115 }
2117}
2118
2119void
2121{
2122 NS_LOG_FUNCTION(this);
2123
2124 const dBm_u ccaEdThreshold{-62.0}; ///< CCA-ED threshold
2125 const auto txAfterChannelSwitchDelay =
2127 ? 250
2128 : 0); ///< delay in seconds between channel switch is triggered and a
2129 ///< transmission gets started
2130 const auto checkResultsDelay =
2131 Seconds(0.5); ///< delay in seconds between start of test and moment results are verified
2132 const auto flushResultsDelay =
2133 Seconds(0.9); ///< delay in seconds between start of test and moment results are flushed
2134 const auto txOngoingAfterTxStartedDelay =
2135 MicroSeconds(50); ///< delay in microseconds between a transmission has started and a point
2136 ///< in time the transmission is ongoing
2137 const auto propagationDelay = NanoSeconds(33); // propagation delay for the test scenario
2138
2139 Time delay{0};
2140
2141 // default channels active for all PHYs: each PHY only receives from its associated TX
2142 std::vector<std::size_t> expectedConnectedPhysPerChannel =
2143 m_trackSignalsInactiveInterfaces ? std::vector<std::size_t>{5, 5, 5, 5}
2144 : // all RX PHYs keep all channels active when tracking
2145 // interferences on inactive interfaces
2146 std::vector<std::size_t>{2, 2, 2, 2}; // default channels active for all PHYs: each PHY
2147 // only receives from its associated TX
2148
2149 for (std::size_t i = 0; i < 4; ++i)
2150 {
2151 auto txPpduPhy = m_txPhys.at(i);
2152 delay += Seconds(1);
2153 Simulator::Schedule(delay,
2155 this,
2156 txPpduPhy,
2157 dBm_u{0},
2158 1000);
2159 for (std::size_t j = 0; j < 4; ++j)
2160 {
2161 auto txPhy = m_txPhys.at(j);
2162 auto rxPhy = m_rxPhys.at(j);
2163 const auto& expectedFreqRange = txPhy->GetCurrentFrequencyRange();
2164 Simulator::Schedule(delay + txOngoingAfterTxStartedDelay,
2166 this,
2167 rxPhy,
2168 txPpduPhy->GetCurrentFrequencyRange(),
2169 txPpduPhy->GetBand(txPpduPhy->GetChannelWidth(), 0),
2170 true);
2171 Simulator::Schedule(delay + checkResultsDelay,
2173 this,
2174 j,
2175 (i == j) ? 1 : 0,
2176 (i == j) ? 1 : 0,
2177 (i == j) ? 1000 : 0,
2178 expectedFreqRange,
2179 expectedConnectedPhysPerChannel);
2180 }
2181 Simulator::Schedule(delay + flushResultsDelay,
2183 this);
2184 }
2185
2186 // same channel active for all PHYs: all PHYs receive from TX
2187 for (std::size_t i = 0; i < 4; ++i)
2188 {
2189 delay += Seconds(1);
2190 auto txPpduPhy = m_txPhys.at(i);
2191 Simulator::Schedule(delay + txAfterChannelSwitchDelay,
2193 this,
2194 txPpduPhy,
2195 dBm_u{0},
2196 1000);
2197 const auto& expectedFreqRange = txPpduPhy->GetCurrentFrequencyRange();
2198 for (std::size_t j = 0; j < 4; ++j)
2199 {
2201 {
2202 for (std::size_t k = 0; k < expectedConnectedPhysPerChannel.size(); ++k)
2203 {
2204 expectedConnectedPhysPerChannel.at(k) = (k == i) ? 5 : 1;
2205 }
2206 }
2207 auto rxPhy = m_rxPhys.at(j);
2208 Simulator::Schedule(delay,
2210 this,
2211 rxPhy,
2212 txPpduPhy->GetPhyBand(),
2213 txPpduPhy->GetChannelNumber(),
2214 txPpduPhy->GetChannelWidth(),
2215 j);
2216 Simulator::Schedule(delay + txAfterChannelSwitchDelay + txOngoingAfterTxStartedDelay,
2218 this,
2219 rxPhy,
2220 txPpduPhy->GetCurrentFrequencyRange(),
2221 txPpduPhy->GetBand(txPpduPhy->GetChannelWidth(), 0),
2222 true);
2223 Simulator::Schedule(delay + checkResultsDelay,
2225 this,
2226 j,
2227 1,
2228 1,
2229 1000,
2230 expectedFreqRange,
2231 expectedConnectedPhysPerChannel);
2232 }
2233 Simulator::Schedule(delay + flushResultsDelay,
2235 this);
2236 }
2237
2238 // Switch all PHYs to channel 36: all PHYs switch to the second spectrum channel
2239 // since second spectrum channel is 42 (80 MHz) and hence covers channel 36 (20 MHz)
2240 const auto secondSpectrumChannelIndex = 1;
2241 auto channel36TxPhy = m_txPhys.at(secondSpectrumChannelIndex);
2242 const auto& expectedFreqRange = channel36TxPhy->GetCurrentFrequencyRange();
2243 for (std::size_t i = 0; i < 4; ++i)
2244 {
2245 delay += Seconds(1);
2246 auto txPpduPhy = m_txPhys.at(i);
2247 Simulator::Schedule(delay + txAfterChannelSwitchDelay,
2249 this,
2250 txPpduPhy,
2251 dBm_u{0},
2252 1000);
2253 for (std::size_t j = 0; j < 4; ++j)
2254 {
2256 {
2257 for (std::size_t k = 0; k < expectedConnectedPhysPerChannel.size(); ++k)
2258 {
2259 expectedConnectedPhysPerChannel.at(k) =
2260 (k == secondSpectrumChannelIndex) ? 5 : 1;
2261 }
2262 }
2263 Simulator::Schedule(delay,
2265 this,
2266 m_rxPhys.at(j),
2270 j);
2271 Simulator::Schedule(delay + checkResultsDelay,
2273 this,
2274 j,
2275 (i == secondSpectrumChannelIndex) ? 1 : 0,
2276 (i == secondSpectrumChannelIndex) ? 1 : 0,
2277 (i == secondSpectrumChannelIndex) ? 1000 : 0,
2278 expectedFreqRange,
2279 expectedConnectedPhysPerChannel);
2280 }
2281 Simulator::Schedule(delay + flushResultsDelay,
2283 this);
2284 }
2285
2286 // verify CCA indication when switching to a channel with an ongoing transmission
2287 for (const auto txPower : {dBm_u{-60} /* above CCA-ED */, dBm_u{-70} /* below CCA-ED */})
2288 {
2289 for (std::size_t i = 0; i < 4; ++i)
2290 {
2291 for (std::size_t j = 0; j < 4; ++j)
2292 {
2293 auto txPpduPhy = m_txPhys.at(i);
2294 const auto startChannel = WifiPhyOperatingChannel::FindFirst(
2295 txPpduPhy->GetPrimaryChannelNumber(MHz_u{20}),
2296 MHz_u{0},
2297 MHz_u{20},
2299 txPpduPhy->GetPhyBand());
2300 for (auto bw = txPpduPhy->GetChannelWidth(); bw >= MHz_u{20}; bw /= 2)
2301 {
2302 const auto& channelInfo =
2304 MHz_u{0},
2305 bw,
2307 txPpduPhy->GetPhyBand(),
2308 startChannel));
2309 delay += Seconds(1);
2310 Simulator::Schedule(delay,
2312 this,
2313 txPpduPhy,
2314 txPower,
2315 1000);
2316 Simulator::Schedule(delay + txOngoingAfterTxStartedDelay,
2318 this,
2319 m_rxPhys.at(j),
2320 channelInfo.band,
2321 channelInfo.number,
2322 channelInfo.width,
2323 j);
2324 for (std::size_t k = 0; k < 4; ++k)
2325 {
2326 if ((i != j) && (k == i))
2327 {
2328 continue;
2329 }
2330 const auto expectCcaBusyIndication =
2331 (k == i) ? (txPower >= ccaEdThreshold)
2333 ? ((txPower >= ccaEdThreshold) ? (j == k) : false)
2334 : false);
2336 delay + checkResultsDelay,
2338 this,
2339 k,
2340 expectCcaBusyIndication,
2341 txOngoingAfterTxStartedDelay,
2342 propagationDelay);
2343 }
2344 Simulator::Schedule(delay + flushResultsDelay,
2346 this);
2347 }
2348 }
2349 }
2350 }
2351
2353 {
2354 /* Reproduce an EMLSR scenario where a PHY is on an initial band and receives a packet.
2355 * Then, the PHY switches to another band where it starts receiving another packet.
2356 * During reception of the PHY header, the PHY switches back to the initial band and starts
2357 * receiving yet another packet. In this case, first and last packets should be successfully
2358 * received (no interference), the second packet reception has been interrupted (before the
2359 * payload reception does start, hence it does not reach the RX state). */
2360 {
2361 // first TX on initial band
2362 auto txPpduPhy = m_txPhys.at(0);
2363 delay += Seconds(1);
2364 Simulator::Schedule(delay,
2366 this,
2367 txPpduPhy,
2368 dBm_u{20},
2369 500);
2370
2371 // switch channel to other band
2372 delay += Seconds(1);
2373 txPpduPhy = m_txPhys.at(1);
2374 Simulator::Schedule(delay,
2376 this,
2377 m_rxPhys.at(0),
2378 txPpduPhy->GetPhyBand(),
2379 txPpduPhy->GetChannelNumber(),
2380 txPpduPhy->GetChannelWidth(),
2381 0);
2382
2383 // TX on other band
2384 delay += Seconds(1);
2385 Simulator::Schedule(delay,
2387 this,
2388 txPpduPhy,
2389 dBm_u{0},
2390 1000);
2391
2392 // switch back to initial band during PHY header reception
2393 txPpduPhy = m_txPhys.at(0);
2394 delay += MicroSeconds(20); // right after legacy PHY header reception
2395 Simulator::Schedule(delay,
2397 this,
2398 m_rxPhys.at(0),
2399 txPpduPhy->GetPhyBand(),
2400 txPpduPhy->GetChannelNumber(),
2401 txPpduPhy->GetChannelWidth(),
2402 0);
2403
2404 // TX once more on the initial band
2405 delay += Seconds(1);
2406 Simulator::Schedule(delay,
2408 this,
2409 txPpduPhy,
2410 dBm_u{0},
2411 1500);
2412
2413 // check results
2415 delay + checkResultsDelay,
2417 this,
2418 0,
2419 3, // 3 RX events
2420 2, // 2 packets should have been successfully received, 1 packet should
2421 // have been interrupted (switch during PHY header reception)
2422 2000, // 500 bytes (firstpacket) and 1500 bytes (third packet)
2423 txPpduPhy->GetCurrentFrequencyRange(),
2424 expectedConnectedPhysPerChannel);
2425
2426 // reset
2427 Simulator::Schedule(delay + flushResultsDelay,
2429 this);
2430 }
2431
2432 /* Reproduce an EMLSR scenario where a PHY is on an initial band and receives a packet
2433 * but switches to another band during preamble detection period. Then, it starts receiving
2434 * two packets which interfere with each other. Afterwards, the PHY goes back to its initial
2435 * band and starts receiving yet another packet. In this case, only the last packet should
2436 * be successfully received (no interference). */
2437 {
2438 // switch channel of PHY index 0 to 5 GHz low band (operating channel of TX PHY index 1)
2439 auto txPpduPhy = m_txPhys.at(1);
2440 delay += Seconds(1);
2441 Simulator::Schedule(delay,
2443 this,
2444 m_rxPhys.at(0),
2445 txPpduPhy->GetPhyBand(),
2446 txPpduPhy->GetChannelNumber(),
2447 txPpduPhy->GetChannelWidth(),
2448 0);
2449
2450 // start transmission on 5 GHz low band
2451 delay += Seconds(1);
2452 Simulator::Schedule(delay,
2454 this,
2455 txPpduPhy,
2456 dBm_u{20},
2457 500);
2458
2459 // switch channel back to previous channel before preamble detection is finished:
2460 // this is needed to verify interference helper rxing state is properly reset
2461 // since ongoing reception is aborted when switching operating channel
2464 this,
2465 m_rxPhys.at(0),
2466 m_txPhys.at(0)->GetPhyBand(),
2467 m_txPhys.at(0)->GetChannelNumber(),
2468 m_txPhys.at(0)->GetChannelWidth(),
2469 0);
2470
2471 delay += Seconds(1);
2472 // we need 2 TX PHYs on the 5 GHz low band to have simultaneous transmissions
2473 // switch operating channel of TX PHY index 2 to the 5 GHz low band
2474 Simulator::Schedule(delay,
2476 this,
2477 m_txPhys.at(2),
2478 txPpduPhy->GetPhyBand(),
2479 txPpduPhy->GetChannelNumber(),
2480 txPpduPhy->GetChannelWidth(),
2481 std::nullopt);
2482
2483 // first transmission on 5 GHz low band with high power
2484 delay += Seconds(1);
2485 Simulator::Schedule(delay,
2487 this,
2488 txPpduPhy,
2489 dBm_u{20},
2490 1000);
2491
2492 // second transmission on 5 GHz low band with high power a bit later:
2493 // first powers get updated updated in the corresponding bands
2494 txPpduPhy = m_txPhys.at(2);
2495 Simulator::Schedule(delay + NanoSeconds(10),
2497 this,
2498 txPpduPhy,
2499 dBm_u{20},
2500 1000);
2501
2502 // restore channel for TX PHY index 2
2503 delay += Seconds(1);
2504 Simulator::Schedule(delay,
2506 this,
2507 m_txPhys.at(2),
2508 m_rxPhys.at(2)->GetPhyBand(),
2509 m_rxPhys.at(2)->GetChannelNumber(),
2510 m_rxPhys.at(2)->GetChannelWidth(),
2511 std::nullopt);
2512
2513 // switch channel of PHY index 0 to 5 GHz low band again
2514 delay += Seconds(1);
2515 txPpduPhy = m_txPhys.at(1);
2516 Simulator::Schedule(delay,
2518 this,
2519 m_rxPhys.at(0),
2520 txPpduPhy->GetPhyBand(),
2521 txPpduPhy->GetChannelNumber(),
2522 txPpduPhy->GetChannelWidth(),
2523 0);
2524
2525 // transmit PPDU on 5 GHz low band (no interference)
2526 delay += Seconds(1);
2527 Simulator::Schedule(delay,
2529 this,
2530 txPpduPhy,
2531 dBm_u{0},
2532 1500);
2533
2534 // check results
2535 Simulator::Schedule(delay + checkResultsDelay,
2537 this,
2538 0,
2539 1, // 1 RX event
2540 1, // last transmitted packet should have been successfully received
2541 1500, // 1500 bytes (payload of last transmitted packet)
2542 txPpduPhy->GetCurrentFrequencyRange(),
2543 expectedConnectedPhysPerChannel);
2544
2545 // reset
2546 Simulator::Schedule(delay + flushResultsDelay,
2548 this);
2549 }
2550 }
2551
2552 delay += Seconds(1);
2553 Simulator::Stop(delay);
2555}
2556
2557/**
2558 * @ingroup wifi-test
2559 * @ingroup tests
2560 *
2561 * @brief Spectrum Wifi Phy Interfaces Helper Test
2562 *
2563 * This test checks the expected interfaces are added to the spectrum PHY instances
2564 * created by the helper.
2565 */
2567{
2568 public:
2571
2572 private:
2573 void DoRun() override;
2574};
2575
2577 : TestCase("Check PHY interfaces added to PHY instances using helper")
2578{
2579}
2580
2581void
2583{
2584 WifiHelper wifiHelper;
2586
2587 SpectrumWifiPhyHelper phyHelper(3);
2588 phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
2589 phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
2590 phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
2591
2595
2596 WifiMacHelper macHelper;
2598
2599 /* Default case: all interfaces are added to each link */
2600 auto device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(0));
2601
2602 // Verify each PHY has 3 interfaces
2603 auto phyLink0 =
2605 NS_ASSERT(phyLink0);
2606 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2607 3,
2608 "Incorrect number of PHY interfaces added to PHY link ID 0");
2609
2610 auto phyLink1 =
2612 NS_ASSERT(phyLink1);
2613 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2614 3,
2615 "Incorrect number of PHY interfaces added to PHY link ID 1");
2616
2617 auto phyLink2 =
2619 NS_ASSERT(phyLink2);
2620 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2621 3,
2622 "Incorrect number of PHY interfaces added to PHY link ID 2");
2623
2624 /* each PHY has a single interface */
2628 device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(1));
2629
2630 // Verify each PHY has a single interface
2631 phyLink0 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(0));
2632 NS_ASSERT(phyLink0);
2633 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2634 1,
2635 "Incorrect number of PHY interfaces added to PHY link ID 0");
2636 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2637 1,
2638 "Incorrect PHY interfaces added to PHY link ID 0");
2639
2640 phyLink1 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(1));
2641 NS_ASSERT(phyLink1);
2642 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2643 1,
2644 "Incorrect number of PHY interfaces added to PHY link ID 1");
2645 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2646 1,
2647 "Incorrect PHY interfaces added to PHY link ID 1");
2648
2649 phyLink2 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(2));
2650 NS_ASSERT(phyLink2);
2651 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().size(),
2652 1,
2653 "Incorrect number of PHY interfaces added to PHY link ID 2");
2654 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2655 1,
2656 "Incorrect PHY interfaces added to PHY link ID 2");
2657
2658 /* add yet another interface to PHY 0 */
2660 device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(2));
2661
2662 // Verify each PHY has a single interface except PHY 0 that should have 2 interfaces
2663 phyLink0 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(0));
2664 NS_ASSERT(phyLink0);
2665 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2666 2,
2667 "Incorrect number of PHY interfaces added to PHY link ID 0");
2668 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2669 1,
2670 "Incorrect PHY interfaces added to PHY link ID 0");
2671 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2672 1,
2673 "Incorrect PHY interfaces added to PHY link ID 0");
2674
2675 phyLink1 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(1));
2676 NS_ASSERT(phyLink1);
2677 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2678 1,
2679 "Incorrect number of PHY interfaces added to PHY link ID 1");
2680 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2681 1,
2682 "Incorrect PHY interfaces added to PHY link ID 1");
2683
2684 phyLink2 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(2));
2685 NS_ASSERT(phyLink2);
2686 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().size(),
2687 1,
2688 "Incorrect number of PHY interfaces added to PHY link ID 2");
2689 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2690 1,
2691 "Incorrect PHY interfaces added to PHY link ID 2");
2692
2693 /* reset mapping previously configured to helper: back to default */
2694 phyHelper.ResetPhyToFreqRangeMapping();
2695 device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(3));
2696
2697 // Verify each PHY has 3 interfaces
2698 phyLink0 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(0));
2699 NS_ASSERT(phyLink0);
2700 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2701 3,
2702 "Incorrect number of PHY interfaces added to PHY link ID 0");
2703 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2704 1,
2705 "Incorrect PHY interfaces added to PHY link ID 0");
2706 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2707 1,
2708 "Incorrect PHY interfaces added to PHY link ID 0");
2709 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2710 1,
2711 "Incorrect PHY interfaces added to PHY link ID 0");
2712
2713 phyLink1 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(1));
2714 NS_ASSERT(phyLink1);
2715 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2716 3,
2717 "Incorrect number of PHY interfaces added to PHY link ID 1");
2718 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2719 1,
2720 "Incorrect PHY interfaces added to PHY link ID 0");
2721 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2722 1,
2723 "Incorrect PHY interfaces added to PHY link ID 0");
2724 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2725 1,
2726 "Incorrect PHY interfaces added to PHY link ID 0");
2727
2728 phyLink2 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(2));
2729 NS_ASSERT(phyLink2);
2730 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().size(),
2731 3,
2732 "Incorrect number of PHY interfaces added to PHY link ID 2");
2733 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2734 1,
2735 "Incorrect PHY interfaces added to PHY link ID 0");
2736 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2737 1,
2738 "Incorrect PHY interfaces added to PHY link ID 0");
2739 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2740 1,
2741 "Incorrect PHY interfaces added to PHY link ID 0");
2742
2744}
2745
2746/**
2747 * @ingroup wifi-test
2748 * @ingroup tests
2749 *
2750 * @brief Spectrum Wifi Phy Test Suite
2751 */
2753{
2754 public:
2756};
2757
2759 : TestSuite("wifi-spectrum-phy", Type::UNIT)
2760{
2761 AddTestCase(new SpectrumWifiPhyBasicTest, TestCase::Duration::QUICK);
2762 AddTestCase(new SpectrumWifiPhyListenerTest, TestCase::Duration::QUICK);
2763 AddTestCase(new SpectrumWifiPhyFilterTest, TestCase::Duration::QUICK);
2764 AddTestCase(new SpectrumWifiPhyGetBandTest, TestCase::Duration::QUICK);
2765 AddTestCase(new SpectrumWifiPhyTrackedBandsTest, TestCase::Duration::QUICK);
2766 AddTestCase(new SpectrumWifiPhy80Plus80Test, TestCase::Duration::QUICK);
2768 false,
2770 TestCase::Duration::QUICK);
2772 true,
2774 TestCase::Duration::QUICK);
2776 true,
2778 TestCase::Duration::QUICK);
2779 AddTestCase(new SpectrumWifiPhyInterfacesHelperTest, TestCase::Duration::QUICK);
2780}
2781
Extended InterferenceHelper class for the purpose of the tests.
static TypeId GetTypeId()
Get the type ID.
bool IsRxing() const
Indicate whether the interference helper is in receiving state.
bool IsBandTracked(const std::vector< WifiSpectrumBandFrequencies > &startStopFreqs) const
Indicate whether a given band is tracked by the interference helper.
Extended SpectrumWifiPhy class for the purpose of the tests.
virtual WifiSpectrumBandInfo GetBand(MHz_u bandWidth, uint8_t bandIndex=0)=0
Get the info of a given band.
Test 80+80MHz transmission.
void StopInterference()
Stop interference function.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
Ptr< SpectrumWifiPhy > m_phyAp
PHY of AP.
void RunOne(const std::vector< uint8_t > &channelNumbers, MHz_u interferenceCenterFrequency, MHz_u interferenceBandWidth, bool expectSuccess)
Run one function.
uint32_t m_countRxSuccessSta
count RX success for STA
void DoRun() override
Implementation to actually run this TestCase.
Ptr< WaveformGenerator > m_phyInterferer
PHY of interferer.
void RxSuccessSta(Ptr< const WifiPsdu > psdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &statusPerMpdu)
Receive success function for STA.
void SwitchChannel(const std::vector< uint8_t > &channelNumbers)
Switch channel function.
Ptr< SpectrumWifiPhy > m_phySta
PHY of STA.
void RxFailureSta(Ptr< const WifiPsdu > psdu)
Receive failure function for STA.
uint32_t m_countRxFailureSta
count RX failure for STA
void DoTeardown() override
Implementation to do any local setup required for this TestCase.
void GenerateInterference(Ptr< SpectrumValue > interferencePsd, Time duration)
Generate interference function.
void CheckResults(bool expectSuccess)
Verify results.
void Send160MhzPpdu()
Send 160MHz PPDU function.
Spectrum Wifi Phy Basic Test.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void DoRun() override
Implementation to actually run this TestCase.
void SpectrumWifiPhyRxSuccess(Ptr< const WifiPsdu > psdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &statusPerMpdu)
Spectrum wifi receive success function.
void SpectrumWifiPhyRxFailure(Ptr< const WifiPsdu > psdu)
Spectrum wifi receive failure function.
Ptr< SpectrumSignalParameters > MakeSignal(Watt_u txPower, const WifiPhyOperatingChannel &channel)
Make signal function.
void SendSignal(Watt_u txPower)
Send signal function.
void DoTeardown() override
Implementation to do any local setup required for this TestCase.
uint64_t m_uid
the UID to use for the PPDU
Ptr< SpectrumWifiPhy > m_phy
Phy.
Spectrum Wifi Phy Filter Test.
void RxCallback(Ptr< const Packet > p, RxPowerWattPerChannelBand rxPowersW)
Callback triggered when a packet is received by the PHYs.
void DoRun() override
Implementation to actually run this TestCase.
void SendPpdu()
Send PPDU function.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void DoTeardown() override
Implementation to do any local setup required for this TestCase.
Ptr< ExtSpectrumWifiPhy > m_rxPhy
RX PHY.
MHz_u m_rxChannelWidth
RX channel width.
Ptr< ExtSpectrumWifiPhy > m_txPhy
TX PHY.
MHz_u m_txChannelWidth
TX channel width.
Spectrum Wifi Phy Bands Calculations Test.
void RunOne(const std::vector< uint8_t > &channelNumberPerSegment, MHz_u bandWidth, uint8_t bandIndex, const std::vector< WifiSpectrumBandIndices > &expectedIndices, const std::vector< WifiSpectrumBandFrequencies > &expectedFrequencies)
Run one function.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
Ptr< SpectrumWifiPhy > m_phy
PHY.
void DoRun() override
Implementation to actually run this TestCase.
void DoTeardown() override
Implementation to do any local setup required for this TestCase.
Spectrum Wifi Phy Interfaces Helper Test.
~SpectrumWifiPhyInterfacesHelperTest() override=default
void DoRun() override
Implementation to actually run this TestCase.
Spectrum Wifi Phy Listener Test.
void DoRun() override
Implementation to actually run this TestCase.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
std::shared_ptr< TestPhyListener > m_listener
listener
Spectrum Wifi Phy Multiple Spectrum Test.
void SendPpdu(Ptr< SpectrumWifiPhy > phy, dBm_u txPower, uint32_t payloadSize)
Send PPDU function.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
std::vector< uint32_t > m_rxBytes
count number of received bytes
std::vector< Ptr< SpectrumWifiPhy > > m_rxPhys
RX PHYs.
std::vector< uint32_t > m_countRxSuccess
count number of packets successfully received by PHYs
void DoTeardown() override
Implementation to do any local setup required for this TestCase.
ChannelSwitchScenario m_chanSwitchScenario
the channel switch scenario to consider for the test
void DoRun() override
Implementation to actually run this TestCase.
void DoCheckInterferences(Ptr< SpectrumWifiPhy > phy, const WifiSpectrumBandInfo &band, bool interferencesExpected)
Check the interferences.
void RxSuccess(std::size_t index, Ptr< const WifiPsdu > psdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &statusPerMpdu)
Receive success function.
bool m_trackSignalsInactiveInterfaces
flag to indicate whether signals coming from inactive spectrum PHY interfaces are tracked during the ...
void SwitchChannel(Ptr< SpectrumWifiPhy > phy, WifiPhyBand band, uint8_t channelNumber, MHz_u channelWidth, std::optional< std::size_t > listenerIndex)
Switch channel function.
std::vector< std::shared_ptr< TestPhyListener > > m_listeners
listeners
Time m_lastTxEnd
hold the time at which the last transmission ended
void RxFailure(std::size_t index, Ptr< const WifiPsdu > psdu)
Receive failure function.
Time m_lastTxStart
hold the time at which the last transmission started
std::vector< uint32_t > m_counts
count number of packets received by PHYs
void CheckResults(std::size_t index, uint32_t expectedNumRx, uint32_t expectedNumRxSuccess, uint32_t expectedRxBytes, FrequencyRange expectedFrequencyRangeActiveRfInterface, const std::vector< std::size_t > &expectedConnectedPhysPerChannel)
Verify results.
std::vector< Ptr< SpectrumWifiPhy > > m_txPhys
TX PHYs.
std::vector< Ptr< MultiModelSpectrumChannel > > m_spectrumChannels
Spectrum channels.
void CheckInterferences(Ptr< SpectrumWifiPhy > phy, const FrequencyRange &freqRange, const WifiSpectrumBandInfo &band, bool interferencesExpected)
Schedule now to check the interferences.
SpectrumWifiPhyMultipleInterfacesTest(bool trackSignalsInactiveInterfaces, ChannelSwitchScenario chanSwitchScenario)
Constructor.
void CheckCcaIndication(std::size_t index, bool expectedCcaBusyIndication, Time switchingDelay, Time propagationDelay)
Verify CCA indication reported by a given PHY.
void RxCallback(std::size_t index, Ptr< const Packet > packet, RxPowerWattPerChannelBand rxPowersW)
Callback triggered when a packet is received by a PHY.
void CheckRxingState(Ptr< SpectrumWifiPhy > phy, bool rxingExpected)
Verify rxing state of the interference helper.
ChannelSwitchScenario
Enumeration for channel switching scenarios.
@ BETWEEN_TX_RX
perform channel switch during propagation delay (after TX and before RX)
@ BEFORE_TX
start TX after channel switch is completed
std::vector< uint32_t > m_countRxFailure
count number of packets unsuccessfully received by PHYs
Spectrum Wifi Phy Test Suite.
Test tracked bands in interference helper upon channel switching.
void DoRun() override
Implementation to actually run this TestCase.
void RunOne(const std::vector< uint8_t > &channelNumberPerSegmentBeforeSwitching, const std::vector< uint8_t > &channelNumberPerSegmentAfterSwitching, const std::vector< std::vector< WifiSpectrumBandFrequencies > > &expectedTrackedBands, const std::vector< std::vector< WifiSpectrumBandFrequencies > > &expectedUntrackedBand)
Run one function.
void VerifyTrackedBands(const std::vector< std::vector< WifiSpectrumBandFrequencies > > &expectedTrackedBands, const std::vector< std::vector< WifiSpectrumBandFrequencies > > &expectedUntrackedBands)
Verify the bands tracked by the interference helper.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void SwitchChannel(const std::vector< uint8_t > &channelNumberPerSegment)
Switch channel function.
Ptr< ExtSpectrumWifiPhy > m_phy
PHY.
void DoTeardown() override
Implementation to do any local setup required for this TestCase.
Time m_ccaBusyEnd
CCA_BUSY end time.
void NotifyTxStart(Time duration, dBm_u txPower) override
void Reset()
Reset function.
void NotifyWakeup() override
Notify listeners that we woke up.
Time m_ccaBusyStart
CCA_BUSY start time.
void NotifyRxEndOk() override
We have received the last bit of a packet for which NotifyRxStart was invoked first and,...
void NotifyOff() override
Notify listeners that we went to switch off.
void NotifyRxEndError(const WifiTxVector &txVector) override
TestPhyListener()=default
Create a test PhyListener.
void NotifySleep() override
Notify listeners that we went to sleep.
void NotifySwitchingStart(Time duration) override
uint32_t m_notifyMaybeCcaBusyStart
notify maybe CCA busy start
uint32_t m_notifyRxStart
notify receive start
void NotifyCcaBusyStart(Time duration, WifiChannelListType channelType, const std::vector< Time > &) override
void NotifyRxStart(Time duration) override
void NotifyOn() override
Notify listeners that we went to switch on.
uint32_t m_notifyRxEndOk
notify receive end OK
uint32_t m_notifyRxEndError
notify receive end error
~TestPhyListener() override=default
AttributeValue implementation for Boolean.
Definition boolean.h:26
static WifiMode GetEhtMcs0()
Return MCS 0 from EHT MCS values.
static WifiMode GetHeMcs11()
Return MCS 11 from HE MCS values.
static WifiMode GetHeMcs7()
Return MCS 7 from HE MCS values.
handles interference calculations
std::map< FrequencyRange, bool > m_rxing
flag whether it is in receiving state for a given FrequencyRange
NiChangesPerBand m_niChanges
NI Changes for each band.
an EUI-48 address
Helper class used to assign positions and mobility models to nodes.
keep track of a set of node pointers.
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
void GetAttribute(std::string name, AttributeValue &value, bool permissive=false) const
Get the value of an attribute, raising fatal errors if unsuccessful.
void Dispose()
Dispose of this Object.
Definition object.cc:247
static WifiMode GetOfdmRate6Mbps()
Return a WifiMode for OFDM at 6 Mbps.
AttributeValue implementation for Pointer.
Ptr< T > Get() const
Definition pointer.h:223
Smart pointer class similar to boost::intrusive_ptr.
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 Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static void Run()
Run the simulation.
Definition simulator.cc:167
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:595
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.
void ResetPhyToFreqRangeMapping()
Reset mapping of the spectrum PHY interfaces added to the PHY instances.
void AddChannel(const Ptr< SpectrumChannel > channel, const FrequencyRange &freqRange=WHOLE_WIFI_SPECTRUM)
void AddPhyToFreqRangeMapping(uint8_t linkId, const FrequencyRange &freqRange)
Add a given spectrum PHY interface to the PHY instance corresponding of a given link.
802.11 PHY layer model
void SetDevice(const Ptr< WifiNetDevice > device) override
Sets the device this PHY is associated with.
MHz_u GetGuardBandwidth(MHz_u currentChannelWidth) const override
void AddChannel(const Ptr< SpectrumChannel > channel, const FrequencyRange &freqRange=WHOLE_WIFI_SPECTRUM)
Attach a SpectrumChannel to use for a given frequency range.
Ptr< WifiSpectrumPhyInterface > GetCurrentInterface() const
Get the currently active spectrum PHY interface.
RuBands GetRuBands(Ptr< WifiSpectrumPhyInterface > spectrumPhyInterface, MHz_u guardBandwidth)
This function computes the RU bands that belong to a given spectrum PHY interface.
Hold variables of type string.
Definition string.h:45
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 IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
AttributeValue implementation for Time.
Definition nstime.h:1432
a unique identifier for an interface.
Definition type-id.h:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
helps to create WifiNetDevice objects
virtual void SetStandard(WifiStandard standard)
virtual NetDeviceContainer Install(const WifiPhyHelper &phy, const WifiMacHelper &mac, NodeContainer::Iterator first, NodeContainer::Iterator last) const
Implements the IEEE 802.11 MAC 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.
create MAC layers for a ns3::WifiNetDevice.
void SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
Set the data link type of PCAP traces to be used.
void Set(std::string name, const AttributeValue &v)
void SetInterferenceHelper(std::string type, Args &&... args)
Helper function used to set the interference helper.
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
virtual void SetInterferenceHelper(const Ptr< InterferenceHelper > helper)
Sets the interference helper.
Definition wifi-phy.cc:687
void Send(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector)
This function is a wrapper for the Send variant that accepts a WifiConstPsduMap as first argument.
Definition wifi-phy.cc:1811
void SetErrorRateModel(const Ptr< ErrorRateModel > model)
Sets the error rate model.
Definition wifi-phy.cc:696
std::tuple< uint8_t, MHz_u, WifiPhyBand, uint8_t > ChannelTuple
Tuple identifying a segment of an operating channel.
Definition wifi-phy.h:937
virtual void ConfigureStandard(WifiStandard standard)
Configure the PHY-level parameters for different Wi-Fi standard.
Definition wifi-phy.cc:997
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 SetOperatingChannel(const WifiPhyOperatingChannel &channel)
If the standard for this object has not been set yet, store the channel settings corresponding to the...
Definition wifi-phy.cc:1123
void SetMobility(const Ptr< MobilityModel > mobility)
assign a mobility model to this device
Definition wifi-phy.cc:662
virtual WifiSpectrumBandInfo GetBand(MHz_u bandWidth, uint8_t bandIndex=0)=0
Get the info of a given band.
receive notifications about PHY events.
Class that keeps track of all information about the current PHY operating channel.
static ConstIterator FindFirst(uint8_t number, MHz_u frequency, MHz_u width, WifiStandard standard, WifiPhyBand band, ConstIterator start=m_frequencyChannels.begin())
Find the first frequency segment matching the specified parameters.
static Ptr< SpectrumValue > CreateOfdmTxPowerSpectralDensity(MHz_u centerFrequency, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=dBr_u{-20}, dBr_u minOuterband=dBr_u{-28}, dBr_u lowestPoint=dBr_u{-40})
Create a transmit power spectral density corresponding to OFDM (802.11a/g).
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#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
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
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
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 Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1345
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1357
WifiPhyBand
Identifies the PHY band.
WifiChannelListType
Enumeration of the possible channel-list parameter elements defined in Table 8-5 of IEEE 802....
@ WIFI_STANDARD_80211be
@ WIFI_STANDARD_80211n
@ WIFI_STANDARD_80211ax
@ WIFI_PREAMBLE_LONG
@ WIFI_PREAMBLE_EHT_MU
@ WIFI_PREAMBLE_HE_SU
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
@ WIFI_PHY_BAND_5GHZ
The 5 GHz band.
NodeContainer nodes
Every class exported by the ns3 library is enclosed in the ns3 namespace.
constexpr FrequencyRange WIFI_SPECTRUM_6_GHZ
Identifier for the frequency range covering the wifi spectrum in the 6 GHz band.
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
dB_u RatioToDb(double ratio)
Convert from ratio to dB.
Definition wifi-utils.cc:45
std::vector< BandInfo > Bands
Container of BandInfo.
dBm_u WToDbm(Watt_u val)
Convert from Watts to dBm.
Definition wifi-utils.cc:38
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
std::map< WifiSpectrumBandInfo, Watt_u > RxPowerWattPerChannelBand
A map of the received power for each band.
Definition phy-entity.h:45
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:138
constexpr FrequencyRange WIFI_SPECTRUM_5_GHZ
Identifier for the frequency range covering the wifi spectrum in the 5 GHz band.
@ WIFI_MAC_QOSDATA
Hz_u MHzToHz(MHz_u val)
Convert from MHz to Hz.
Definition wifi-utils.h:113
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)
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
static const uint8_t CHANNEL_NUMBER
static SpectrumWifiPhyTestSuite spectrumWifiPhyTestSuite
the test suite
static const MHz_u GUARD_WIDTH
static const MHz_u CHANNEL_WIDTH
The building block of a SpectrumModel.
double fl
lower limit of subband
Struct defining a frequency range between minFrequency and maxFrequency.
MHz_u minFrequency
the minimum frequency
MHz_u maxFrequency
the maximum frequency
RxSignalInfo structure containing info on the received signal.
Definition wifi-types.h:79
WifiSpectrumBandInfo structure containing info about a spectrum band.