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/he-phy.h" //includes OFDM PHY
10#include "ns3/interference-helper.h"
11#include "ns3/log.h"
12#include "ns3/mobility-helper.h"
13#include "ns3/multi-model-spectrum-channel.h"
14#include "ns3/nist-error-rate-model.h"
15#include "ns3/non-communicating-net-device.h"
16#include "ns3/ofdm-ppdu.h"
17#include "ns3/pointer.h"
18#include "ns3/spectrum-wifi-helper.h"
19#include "ns3/spectrum-wifi-phy.h"
20#include "ns3/string.h"
21#include "ns3/test.h"
22#include "ns3/waveform-generator.h"
23#include "ns3/wifi-mac-header.h"
24#include "ns3/wifi-net-device.h"
25#include "ns3/wifi-phy-listener.h"
26#include "ns3/wifi-psdu.h"
27#include "ns3/wifi-spectrum-phy-interface.h"
28#include "ns3/wifi-spectrum-signal-parameters.h"
29#include "ns3/wifi-spectrum-value-helper.h"
30#include "ns3/wifi-utils.h"
31
32#include <memory>
33#include <optional>
34#include <tuple>
35#include <vector>
36
37using namespace ns3;
38
39NS_LOG_COMPONENT_DEFINE("SpectrumWifiPhyTest");
40
41static const uint8_t CHANNEL_NUMBER = 36;
42static const MHz_u CHANNEL_WIDTH{20};
43static const MHz_u GUARD_WIDTH = CHANNEL_WIDTH; // expanded to channel width to model spectrum mask
44
45/**
46 * Extended SpectrumWifiPhy class for the purpose of the tests.
47 */
49{
50 public:
51 using WifiPhy::GetBand;
52};
53
54/**
55 * Extended InterferenceHelper class for the purpose of the tests.
56 */
58{
59 public:
60 /**
61 * @brief Get the type ID.
62 * @return the object TypeId
63 */
65 {
66 static TypeId tid = TypeId("ns3::ExtInterferenceHelper")
68 .SetGroupName("Wifi")
69 .AddConstructor<ExtInterferenceHelper>();
70 return tid;
71 }
72
73 /**
74 * Indicate whether the interference helper is in receiving state
75 *
76 * @return true if the interference helper is in receiving state, false otherwise
77 */
78 bool IsRxing() const
79 {
80 return std::any_of(m_rxing.cbegin(), m_rxing.cend(), [](const auto& rxing) {
81 return rxing.second;
82 });
83 }
84
85 /**
86 * Indicate whether a given band is tracked by the interference helper
87 *
88 * @param startStopFreqs the start and stop frequencies per segment of the band
89 *
90 * @return true if the specified band is tracked by the interference helper, false otherwise
91 */
92 bool IsBandTracked(const std::vector<WifiSpectrumBandFrequencies>& startStopFreqs) const
93 {
94 for (const auto& [band, nis] : m_niChanges)
95 {
96 if (band.frequencies == startStopFreqs)
97 {
98 return true;
99 }
100 }
101 return false;
102 }
103};
104
106
107/**
108 * @ingroup wifi-test
109 * @ingroup tests
110 *
111 * @brief Spectrum Wifi Phy Basic Test
112 */
114{
115 public:
117 /**
118 * Constructor
119 *
120 * @param name reference name
121 */
122 SpectrumWifiPhyBasicTest(std::string name);
123 ~SpectrumWifiPhyBasicTest() override;
124
125 protected:
126 void DoSetup() override;
127 void DoTeardown() override;
129 /**
130 * Make signal function
131 * @param txPower the transmit power
132 * @param channel the operating channel of the PHY used for the transmission
133 * @returns Ptr<SpectrumSignalParameters>
134 */
136 const WifiPhyOperatingChannel& channel);
137 /**
138 * Send signal function
139 * @param txPower the transmit power
140 */
141 void SendSignal(Watt_u txPower);
142 /**
143 * Spectrum wifi receive success function
144 * @param psdu the PSDU
145 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
146 * @param txVector the transmit vector
147 * @param statusPerMpdu reception status per MPDU
148 */
150 RxSignalInfo rxSignalInfo,
151 const WifiTxVector& txVector,
152 const std::vector<bool>& statusPerMpdu);
153 /**
154 * Spectrum wifi receive failure function
155 * @param psdu the PSDU
156 */
158 uint32_t m_count; ///< count
159
160 private:
161 void DoRun() override;
162
163 uint64_t m_uid; //!< the UID to use for the PPDU
164};
165
167 : SpectrumWifiPhyBasicTest("SpectrumWifiPhy test case receives one packet")
168{
169}
170
172 : TestCase(name),
173 m_count(0),
174 m_uid(0)
175{
176}
177
178// Make a Wi-Fi signal to inject directly to the StartRx() method
181{
183 0,
185 NanoSeconds(800),
186 1,
187 1,
188 0,
190 false};
191
192 auto pkt = Create<Packet>(1000);
193 WifiMacHeader hdr;
194
196 hdr.SetQosTid(0);
197
198 auto psdu = Create<WifiPsdu>(pkt, hdr);
199 const auto txDuration =
200 m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
201
202 auto ppdu = Create<OfdmPpdu>(psdu, txVector, channel, m_uid++);
203
205 channel.GetPrimaryChannelCenterFrequency(CHANNEL_WIDTH),
207 txPower,
209 auto txParams = Create<WifiSpectrumSignalParameters>();
210 txParams->psd = txPowerSpectrum;
211 txParams->txPhy = nullptr;
212 txParams->duration = txDuration;
213 txParams->ppdu = ppdu;
214
215 return txParams;
216}
217
218// Make a Wi-Fi signal to inject directly to the StartRx() method
219void
221{
222 m_phy->StartRx(MakeSignal(txPower, m_phy->GetOperatingChannel()), nullptr);
223}
224
225void
227 RxSignalInfo rxSignalInfo,
228 const WifiTxVector& txVector,
229 const std::vector<bool>& statusPerMpdu)
230{
231 NS_LOG_FUNCTION(this << *psdu << rxSignalInfo << txVector);
232 m_count++;
233}
234
235void
241
245
246// Create necessary objects, and inject signals. Test that the expected
247// number of packet receptions occur.
248void
250{
251 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
252 auto node = CreateObject<Node>();
253 auto dev = CreateObject<WifiNetDevice>();
255 auto interferenceHelper = CreateObject<InterferenceHelper>();
256 m_phy->SetInterferenceHelper(interferenceHelper);
258 m_phy->SetErrorRateModel(error);
259 m_phy->SetDevice(dev);
260 m_phy->AddChannel(spectrumChannel);
261 m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
262 m_phy->ConfigureStandard(WIFI_STANDARD_80211n);
263 m_phy->SetReceiveOkCallback(
265 m_phy->SetReceiveErrorCallback(
267 dev->SetPhy(m_phy);
268 node->AddDevice(dev);
269}
270
271void
273{
274 m_phy->Dispose();
275 m_phy = nullptr;
276}
277
278// Test that the expected number of packet receptions occur.
279void
281{
282 Watt_u txPower{0.01};
283 // Send packets spaced 1 second apart; all should be received
287 // Send packets spaced 1 microsecond second apart; none should be received (PHY header reception
288 // failure)
291 this,
292 txPower);
295 this,
296 txPower);
299
300 NS_TEST_ASSERT_MSG_EQ(m_count, 3, "Didn't receive right number of packets");
301}
302
303/**
304 * @ingroup wifi-test
305 * @ingroup tests
306 *
307 * @brief Test Phy Listener
308 */
310{
311 public:
312 /**
313 * Create a test PhyListener
314 */
315 TestPhyListener() = default;
316 ~TestPhyListener() override = default;
317
318 void NotifyRxStart(Time duration) override
319 {
320 NS_LOG_FUNCTION(this << duration);
322 }
323
324 void NotifyRxEndOk() override
325 {
326 NS_LOG_FUNCTION(this);
328 }
329
330 void NotifyRxEndError() override
331 {
332 NS_LOG_FUNCTION(this);
334 }
335
336 void NotifyTxStart(Time duration, dBm_u txPower) override
337 {
338 NS_LOG_FUNCTION(this << duration << txPower);
339 }
340
342 WifiChannelListType channelType,
343 const std::vector<Time>& /*per20MhzDurations*/) override
344 {
345 NS_LOG_FUNCTION(this << duration << channelType);
346 if (duration.IsStrictlyPositive())
347 {
350 {
352 }
353 m_ccaBusyEnd = std::max(m_ccaBusyEnd, Simulator::Now() + duration);
354 }
355 }
356
357 void NotifySwitchingStart(Time duration) override
358 {
359 }
360
361 void NotifySleep() override
362 {
363 }
364
365 void NotifyOff() override
366 {
367 }
368
369 void NotifyWakeup() override
370 {
371 }
372
373 void NotifyOn() override
374 {
375 }
376
377 /**
378 * Reset function
379 */
380 void Reset()
381 {
382 NS_LOG_FUNCTION(this);
383 m_notifyRxStart = 0;
384 m_notifyRxEndOk = 0;
389 }
390
391 uint32_t m_notifyRxStart{0}; ///< notify receive start
392 uint32_t m_notifyRxEndOk{0}; ///< notify receive end OK
393 uint32_t m_notifyRxEndError{0}; ///< notify receive end error
394 uint32_t m_notifyMaybeCcaBusyStart{0}; ///< notify maybe CCA busy start
395 Time m_ccaBusyStart{}; ///< CCA_BUSY start time
396 Time m_ccaBusyEnd{}; ///< CCA_BUSY end time
397};
398
399/**
400 * @ingroup wifi-test
401 * @ingroup tests
402 *
403 * @brief Spectrum Wifi Phy Listener Test
404 */
406{
407 public:
409
410 private:
411 void DoSetup() override;
412 void DoRun() override;
413 std::shared_ptr<TestPhyListener> m_listener; ///< listener
414};
415
417 : SpectrumWifiPhyBasicTest("SpectrumWifiPhy test operation of WifiPhyListener")
418{
419}
420
421void
423{
425 m_listener = std::make_shared<TestPhyListener>();
426 m_phy->RegisterListener(m_listener);
427}
428
429void
431{
432 Watt_u txPower{0.01};
435
436 NS_TEST_ASSERT_MSG_EQ(m_count, 1, "Didn't receive right number of packets");
439 2,
440 "Didn't receive NotifyCcaBusyStart (once preamble is detected + prolonged by L-SIG "
441 "reception, then switched to Rx by at the beginning of data)");
442 NS_TEST_ASSERT_MSG_EQ(m_listener->m_notifyRxStart, 1, "Didn't receive NotifyRxStart");
443 NS_TEST_ASSERT_MSG_EQ(m_listener->m_notifyRxEndOk, 1, "Didn't receive NotifyRxEnd");
444
446 m_listener.reset();
447}
448
449/**
450 * @ingroup wifi-test
451 * @ingroup tests
452 *
453 * @brief Spectrum Wifi Phy Filter Test
454 */
456{
457 public:
459 /**
460 * Constructor
461 *
462 * @param name reference name
463 */
464 SpectrumWifiPhyFilterTest(std::string name);
466
467 private:
468 void DoSetup() override;
469 void DoTeardown() override;
470 void DoRun() override;
471
472 /**
473 * Run one function
474 */
475 void RunOne();
476
477 /**
478 * Send PPDU function
479 */
480 void SendPpdu();
481
482 /**
483 * Callback triggered when a packet is received by the PHYs
484 * @param p the received packet
485 * @param rxPowersW the received power per channel band in watts
486 */
488
491
492 MHz_u m_txChannelWidth; ///< TX channel width
493 MHz_u m_rxChannelWidth; ///< RX channel width
494};
495
497 : TestCase("SpectrumWifiPhy test RX filters"),
498 m_txChannelWidth(MHz_u{20}),
499 m_rxChannelWidth(MHz_u{20})
500{
501}
502
507
508void
510{
512 0,
514 NanoSeconds(800),
515 1,
516 1,
517 0,
519 false,
520 false};
521 auto pkt = Create<Packet>(1000);
522 WifiMacHeader hdr;
524 hdr.SetQosTid(0);
525 hdr.SetAddr1(Mac48Address("00:00:00:00:00:01"));
526 hdr.SetSequenceNumber(1);
527 auto psdu = Create<WifiPsdu>(pkt, hdr);
528 m_txPhy->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
529}
530
536
537void
539{
540 for (const auto& [band, powerW] : rxPowersW)
541 {
543 "band: (" << band << ") -> powerW=" << powerW
544 << (powerW > 0.0 ? " (" + std::to_string(WToDbm(powerW)) + " dBm)" : ""));
545 }
546
547 size_t numBands = rxPowersW.size();
548 auto expectedNumBands = std::max<std::size_t>(1, m_rxChannelWidth / MHz_u{20});
549 expectedNumBands += (m_rxChannelWidth / MHz_u{40});
550 expectedNumBands += (m_rxChannelWidth / MHz_u{80});
551 expectedNumBands += (m_rxChannelWidth / MHz_u{160});
552 expectedNumBands += m_rxPhy
555 m_rxPhy->GetCurrentInterface()->GetChannelWidth()))
556 .size();
557
558 NS_TEST_ASSERT_MSG_EQ(numBands,
559 expectedNumBands,
560 "Total number of bands handled by the receiver is incorrect");
561
562 MHz_u channelWidth = std::min(m_txChannelWidth, m_rxChannelWidth);
563 auto band = m_rxPhy->GetBand(channelWidth, 0);
564 auto it = rxPowersW.find(band);
565 NS_LOG_INFO("powerW total band: " << it->second << " (" << WToDbm(it->second) << " dBm)");
566 int totalRxPower = static_cast<int>(WToDbm(it->second) + 0.5);
567 int expectedTotalRxPower;
569 {
570 // PHY sends at 16 dBm, and since there is no loss, this should be the total power at the
571 // receiver.
572 expectedTotalRxPower = 16;
573 }
574 else
575 {
576 // Only a part of the transmitted power is received
577 expectedTotalRxPower =
578 16 - static_cast<int>(RatioToDb(m_txChannelWidth / m_rxChannelWidth));
579 }
580 NS_TEST_ASSERT_MSG_EQ(totalRxPower,
581 expectedTotalRxPower,
582 "Total received power is not correct");
583
584 if ((m_txChannelWidth <= m_rxChannelWidth) && (channelWidth >= MHz_u{20}))
585 {
586 band = m_rxPhy->GetBand(MHz_u{20}, 0); // primary 20 MHz
587 it = rxPowersW.find(band);
588 NS_LOG_INFO("powerW in primary 20 MHz channel: " << it->second << " (" << WToDbm(it->second)
589 << " dBm)");
590 const auto rxPowerPrimaryChannel20 = static_cast<int>(WToDbm(it->second) + 0.5);
591 const auto expectedRxPowerPrimaryChannel20 =
592 16 - static_cast<int>(RatioToDb(Count20MHzSubchannels(channelWidth)));
593 NS_TEST_ASSERT_MSG_EQ(rxPowerPrimaryChannel20,
594 expectedRxPowerPrimaryChannel20,
595 "Received power in the primary 20 MHz band is not correct");
596 }
597}
598
599void
601{
602 // WifiHelper::EnableLogComponents();
603 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
604
605 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
607 lossModel->SetFrequency(5.180e9);
608 spectrumChannel->AddPropagationLossModel(lossModel);
610 spectrumChannel->SetPropagationDelayModel(delayModel);
611
612 auto txNode = CreateObject<Node>();
613 auto txDev = CreateObject<WifiNetDevice>();
615 auto txInterferenceHelper = CreateObject<InterferenceHelper>();
616 m_txPhy->SetInterferenceHelper(txInterferenceHelper);
617 auto txErrorModel = CreateObject<NistErrorRateModel>();
618 m_txPhy->SetErrorRateModel(txErrorModel);
619 m_txPhy->SetDevice(txDev);
620 m_txPhy->AddChannel(spectrumChannel);
623 m_txPhy->SetMobility(apMobility);
624 txDev->SetPhy(m_txPhy);
625 txNode->AggregateObject(apMobility);
626 txNode->AddDevice(txDev);
627
628 auto rxNode = CreateObject<Node>();
629 auto rxDev = CreateObject<WifiNetDevice>();
631 auto rxInterferenceHelper = CreateObject<InterferenceHelper>();
632 m_rxPhy->SetInterferenceHelper(rxInterferenceHelper);
633 auto rxErrorModel = CreateObject<NistErrorRateModel>();
634 m_rxPhy->SetErrorRateModel(rxErrorModel);
635 m_rxPhy->AddChannel(spectrumChannel);
638 m_rxPhy->SetMobility(sta1Mobility);
639 rxDev->SetPhy(m_rxPhy);
640 rxNode->AggregateObject(sta1Mobility);
641 rxNode->AddDevice(rxDev);
644}
645
646void
648{
649 m_txPhy->Dispose();
650 m_txPhy = nullptr;
651 m_rxPhy->Dispose();
652 m_rxPhy = nullptr;
653}
654
655void
657{
658 MHz_u txFrequency;
659 switch (static_cast<uint16_t>(m_txChannelWidth))
660 {
661 case 20:
662 default:
663 txFrequency = MHz_u{5180};
664 break;
665 case 40:
666 txFrequency = MHz_u{5190};
667 break;
668 case 80:
669 txFrequency = MHz_u{5210};
670 break;
671 case 160:
672 txFrequency = MHz_u{5250};
673 break;
674 }
675 auto txChannelNum = WifiPhyOperatingChannel::FindFirst(0,
676 txFrequency,
680 ->number;
683
684 MHz_u rxFrequency;
685 switch (static_cast<uint16_t>(m_rxChannelWidth))
686 {
687 case 20:
688 default:
689 rxFrequency = MHz_u{5180};
690 break;
691 case 40:
692 rxFrequency = MHz_u{5190};
693 break;
694 case 80:
695 rxFrequency = MHz_u{5210};
696 break;
697 case 160:
698 rxFrequency = MHz_u{5250};
699 break;
700 }
701 auto rxChannelNum = WifiPhyOperatingChannel::FindFirst(0,
702 rxFrequency,
706 ->number;
709
711
713}
714
715void
717{
720 RunOne();
721
724 RunOne();
725
728 RunOne();
729
730 m_txChannelWidth = MHz_u{160};
731 m_rxChannelWidth = MHz_u{160};
732 RunOne();
733
736 RunOne();
737
740 RunOne();
741
744 RunOne();
745
747 m_rxChannelWidth = MHz_u{160};
748 RunOne();
749
751 m_rxChannelWidth = MHz_u{160};
752 RunOne();
753
755 m_rxChannelWidth = MHz_u{160};
756 RunOne();
757
760 RunOne();
761
764 RunOne();
765
768 RunOne();
769
770 m_txChannelWidth = MHz_u{160};
772 RunOne();
773
774 m_txChannelWidth = MHz_u{160};
776 RunOne();
777
778 m_txChannelWidth = MHz_u{160};
780 RunOne();
781
783}
784
785/**
786 * @ingroup wifi-test
787 * @ingroup tests
788 *
789 * @brief Spectrum Wifi Phy Bands Calculations Test
790 *
791 * This test verifies SpectrumWifiPhy::GetBand produces the expected results, for both contiguous
792 * (160 MHz) and non-contiguous (80+80MHz) operating channel
793 */
795{
796 public:
798
799 private:
800 void DoSetup() override;
801 void DoTeardown() override;
802 void DoRun() override;
803
804 /**
805 * Run one function
806 * @param channelNumberPerSegment the channel number for each segment of the operating channel
807 * @param bandWidth the width of the band to test
808 * @param bandIndex the index of the band to test
809 * @param expectedIndices the expected start and stop indices returned by
810 * SpectrumWifiPhy::GetBand \param expectedFrequencies the expected start and stop frequencies
811 * returned by SpectrumWifiPhy::GetBand
812 */
813 void RunOne(const std::vector<uint8_t>& channelNumberPerSegment,
814 MHz_u bandWidth,
815 uint8_t bandIndex,
816 const std::vector<WifiSpectrumBandIndices>& expectedIndices,
817 const std::vector<WifiSpectrumBandFrequencies>& expectedFrequencies);
818
820};
821
823 : TestCase("SpectrumWifiPhy test bands calculations")
824{
825}
826
827void
829{
830 // WifiHelper::EnableLogComponents();
831 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
832
833 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
835 lossModel->SetFrequency(5.180e9);
836 spectrumChannel->AddPropagationLossModel(lossModel);
838 spectrumChannel->SetPropagationDelayModel(delayModel);
839
840 auto node = CreateObject<Node>();
841 auto dev = CreateObject<WifiNetDevice>();
843 auto interferenceHelper = CreateObject<InterferenceHelper>();
844 m_phy->SetInterferenceHelper(interferenceHelper);
845 auto errorModel = CreateObject<NistErrorRateModel>();
846 m_phy->SetErrorRateModel(errorModel);
847 m_phy->SetDevice(dev);
848 m_phy->AddChannel(spectrumChannel);
849 m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
850 dev->SetPhy(m_phy);
851 node->AddDevice(dev);
852}
853
854void
856{
857 m_phy->Dispose();
858 m_phy = nullptr;
859}
860
861void
863 const std::vector<uint8_t>& channelNumberPerSegment,
864 MHz_u bandWidth,
865 uint8_t bandIndex,
866 const std::vector<WifiSpectrumBandIndices>& expectedIndices,
867 const std::vector<WifiSpectrumBandFrequencies>& expectedFrequencies)
868{
869 WifiPhy::ChannelSegments channelSegments;
870 for (auto channelNumber : channelNumberPerSegment)
871 {
872 const auto& channelInfo = WifiPhyOperatingChannel::FindFirst(channelNumber,
873 MHz_u{0},
874 MHz_u{0},
877 channelSegments.emplace_back(channelInfo->number, channelInfo->width, channelInfo->band, 0);
878 }
879 m_phy->SetOperatingChannel(channelSegments);
880
881 const auto& bandInfo = m_phy->GetBand(bandWidth, bandIndex);
882 NS_ASSERT(expectedIndices.size() == expectedFrequencies.size());
883 NS_ASSERT(expectedIndices.size() == bandInfo.indices.size());
884 NS_ASSERT(expectedFrequencies.size() == bandInfo.frequencies.size());
885 for (std::size_t i = 0; i < expectedIndices.size(); ++i)
886 {
887 NS_ASSERT(bandInfo.indices.at(i).first == expectedIndices.at(i).first);
888 NS_TEST_ASSERT_MSG_EQ(bandInfo.indices.at(i).first,
889 expectedIndices.at(i).first,
890 "Incorrect start indice for segment " << i);
891 NS_TEST_ASSERT_MSG_EQ(bandInfo.indices.at(i).second,
892 expectedIndices.at(i).second,
893 "Incorrect stop indice for segment " << i);
894 NS_TEST_ASSERT_MSG_EQ(bandInfo.frequencies.at(i).first,
895 expectedFrequencies.at(i).first,
896 "Incorrect start frequency for segment " << i);
897 NS_TEST_ASSERT_MSG_EQ(bandInfo.frequencies.at(i).second,
898 expectedFrequencies.at(i).second,
899 "Incorrect stop frequency for segment " << i);
900 }
901}
902
903void
905{
906 const uint32_t indicesPer20MhzBand = 256; // based on 802.11ax carrier spacing
907 const MHz_u channelWidth{160}; // we consider the largest channel width
908 const uint8_t channelNumberContiguous160Mhz =
909 50; // channel number of the first 160 MHz band in 5 GHz band
910 const std::vector<uint8_t> channelNumberPerSegment = {42,
911 106}; // channel numbers used for 80+80MHz
912 // separation between segment at channel number 42 and segment at channel number 106
913 const MHz_u separationWidth{240};
914 for (bool contiguous160Mhz : {true /* 160 MHz */, false /* 80+80MHz */})
915 {
916 const auto guardWidth = contiguous160Mhz ? channelWidth : (channelWidth / 2);
917 uint32_t guardStopIndice = (indicesPer20MhzBand * Count20MHzSubchannels(guardWidth)) - 1;
918 std::vector<WifiSpectrumBandIndices> previousExpectedIndices{};
919 std::vector<WifiSpectrumBandFrequencies> previousExpectedFrequencies{};
920 for (auto bandWidth : {MHz_u{20}, MHz_u{40}, MHz_u{80}, MHz_u{160}})
921 {
922 const uint32_t expectedStartIndice = guardStopIndice + 1;
923 const uint32_t expectedStopIndice =
924 expectedStartIndice + (indicesPer20MhzBand * Count20MHzSubchannels(bandWidth)) - 1;
925 std::vector<WifiSpectrumBandIndices> expectedIndices{
926 {expectedStartIndice, expectedStopIndice}};
927 const Hz_u expectedStartFrequency = MHzToHz(5170);
928 const Hz_u expectedStopFrequency = MHzToHz(5170 + bandWidth);
929 std::vector<WifiSpectrumBandFrequencies> expectedFrequencies{
930 {expectedStartFrequency, expectedStopFrequency}};
931 const std::size_t numBands = (channelWidth / bandWidth);
932 for (std::size_t i = 0; i < numBands; ++i)
933 {
934 if ((bandWidth != channelWidth) && (i >= (numBands / 2)))
935 {
936 // skip DC
937 expectedIndices.at(0).first++;
938 }
939 if ((bandWidth == channelWidth) && !contiguous160Mhz)
940 {
941 // For contiguous 160 MHz, band is made of the two 80 MHz segments (previous run
942 // in the loop)
943 expectedIndices = previousExpectedIndices;
944 expectedFrequencies = previousExpectedFrequencies;
945 }
946 else if ((i == (numBands / 2)) && !contiguous160Mhz)
947 {
948 expectedIndices.at(0).first +=
949 (indicesPer20MhzBand * Count20MHzSubchannels(separationWidth));
950 expectedIndices.at(0).second +=
951 (indicesPer20MhzBand * Count20MHzSubchannels(separationWidth));
952 expectedFrequencies.at(0).first += MHzToHz(separationWidth);
953 expectedFrequencies.at(0).second += MHzToHz(separationWidth);
954 }
955 RunOne(contiguous160Mhz ? std::vector<uint8_t>{channelNumberContiguous160Mhz}
956 : channelNumberPerSegment,
957 bandWidth,
958 i,
959 expectedIndices,
960 expectedFrequencies);
961 if (!contiguous160Mhz && (bandWidth == (channelWidth / 2)))
962 {
963 previousExpectedIndices.emplace_back(expectedIndices.front());
964 previousExpectedFrequencies.emplace_back(expectedFrequencies.front());
965 }
966 expectedIndices.at(0).first = expectedIndices.at(0).second + 1;
967 expectedIndices.at(0).second =
968 expectedIndices.at(0).first +
969 (indicesPer20MhzBand * Count20MHzSubchannels(bandWidth)) - 1;
970 expectedFrequencies.at(0).first += MHzToHz(bandWidth);
971 expectedFrequencies.at(0).second += MHzToHz(bandWidth);
972 }
973 }
974 }
975
977}
978
979/**
980 * @ingroup wifi-test
981 * @ingroup tests
982 *
983 * @brief Test tracked bands in interference helper upon channel switching
984 *
985 * The test is verifying that the correct bands are tracked by the interference helper upon channel
986 * switching. It focuses on 80 and 160 MHz bands while considering 160 MHz operating channels, for
987 * both contiguous and non-contiguous cases.
988 */
990{
991 public:
993
994 private:
995 void DoSetup() override;
996 void DoTeardown() override;
997 void DoRun() override;
998
999 /**
1000 * Switch channel function
1001 *
1002 * @param channelNumberPerSegment the channel number for each segment of the operating channel
1003 * to switch to
1004 */
1005 void SwitchChannel(const std::vector<uint8_t>& channelNumberPerSegment);
1006
1007 /**
1008 * Verify the bands tracked by the interference helper
1009 *
1010 * @param expectedTrackedBands the bands that are expected to be tracked by the interference
1011 * helper
1012 * @param expectedUntrackedBands the bands that are expected to be untracked by the
1013 * interference helper
1014 */
1015 void VerifyTrackedBands(
1016 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
1017 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBands);
1018
1019 /**
1020 * Run one function
1021 * @param channelNumberPerSegmentBeforeSwitching the channel number for each segment of the
1022 * operating channel to switch from \param channelNumberPerSegmentAfterSwitching the channel
1023 * number for each segment of the operating channel to switch to \param expectedTrackedBands the
1024 * bands that are expected to be tracked by the interference helper \param expectedUntrackedBand
1025 * the bands that are expected to be untracked by the interference helper
1026 */
1027 void RunOne(const std::vector<uint8_t>& channelNumberPerSegmentBeforeSwitching,
1028 const std::vector<uint8_t>& channelNumberPerSegmentAfterSwitching,
1029 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
1030 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBand);
1031
1033};
1034
1036 : TestCase("SpectrumWifiPhy test channel switching for non-contiguous operating channels")
1037{
1038}
1039
1040void
1042{
1043 // WifiHelper::EnableLogComponents();
1044 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
1045
1046 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
1047 auto lossModel = CreateObject<FriisPropagationLossModel>();
1048 lossModel->SetFrequency(5.180e9);
1049 spectrumChannel->AddPropagationLossModel(lossModel);
1051 spectrumChannel->SetPropagationDelayModel(delayModel);
1052
1053 auto node = CreateObject<Node>();
1054 auto dev = CreateObject<WifiNetDevice>();
1056 auto interferenceHelper = CreateObject<ExtInterferenceHelper>();
1057 m_phy->SetInterferenceHelper(interferenceHelper);
1058 auto errorModel = CreateObject<NistErrorRateModel>();
1059 m_phy->SetErrorRateModel(errorModel);
1060 m_phy->SetDevice(dev);
1061 m_phy->AddChannel(spectrumChannel);
1063 dev->SetPhy(m_phy);
1064 node->AddDevice(dev);
1065}
1066
1067void
1073
1074void
1075SpectrumWifiPhyTrackedBandsTest::SwitchChannel(const std::vector<uint8_t>& channelNumberPerSegment)
1076{
1077 NS_LOG_FUNCTION(this);
1078 WifiPhy::ChannelSegments channelSegments;
1079 for (auto channelNumber : channelNumberPerSegment)
1080 {
1081 const auto& channelInfo = WifiPhyOperatingChannel::FindFirst(channelNumber,
1082 MHz_u{0},
1083 MHz_u{0},
1086 channelSegments.emplace_back(channelInfo->number, channelInfo->width, channelInfo->band, 0);
1087 }
1088 m_phy->SetOperatingChannel(channelSegments);
1089}
1090
1091void
1093 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
1094 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBands)
1095{
1096 NS_LOG_FUNCTION(this);
1097 PointerValue ptr;
1098 m_phy->GetAttribute("InterferenceHelper", ptr);
1099 auto interferenceHelper = DynamicCast<ExtInterferenceHelper>(ptr.Get<ExtInterferenceHelper>());
1100 NS_ASSERT(interferenceHelper);
1101 auto printBand = [](const std::vector<WifiSpectrumBandFrequencies>& v) {
1102 std::stringstream ss;
1103 for (const auto& [start, stop] : v)
1104 {
1105 ss << "[" << start << "-" << stop << "] ";
1106 }
1107 return ss.str();
1108 };
1109 for (const auto& expectedTrackedBand : expectedTrackedBands)
1110 {
1111 auto bandTracked = interferenceHelper->IsBandTracked(expectedTrackedBand);
1112 NS_TEST_ASSERT_MSG_EQ(bandTracked,
1113 true,
1114 "Band " << printBand(expectedTrackedBand) << " is not tracked");
1115 }
1116 for (const auto& expectedUntrackedBand : expectedUntrackedBands)
1117 {
1118 auto bandTracked = interferenceHelper->IsBandTracked(expectedUntrackedBand);
1119 NS_TEST_ASSERT_MSG_EQ(bandTracked,
1120 false,
1121 "Band " << printBand(expectedUntrackedBand)
1122 << " is unexpectedly tracked");
1123 }
1124}
1125
1126void
1128 const std::vector<uint8_t>& channelNumberPerSegmentBeforeSwitching,
1129 const std::vector<uint8_t>& channelNumberPerSegmentAfterSwitching,
1130 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedTrackedBands,
1131 const std::vector<std::vector<WifiSpectrumBandFrequencies>>& expectedUntrackedBands)
1132{
1133 NS_LOG_FUNCTION(this);
1134
1137 this,
1138 channelNumberPerSegmentBeforeSwitching);
1139
1142 this,
1143 channelNumberPerSegmentAfterSwitching);
1144
1147 this,
1148 expectedTrackedBands,
1149 expectedUntrackedBands);
1150
1152}
1153
1154void
1156{
1157 // switch from 160 MHz to 80+80 MHz
1158 RunOne({50},
1159 {42, 106},
1160 {{{MHzToHz(5170), MHzToHz(5250)}} /* first 80 MHz segment */,
1161 {{MHzToHz(5490), MHzToHz(5570)}} /* second 80 MHz segment */,
1162 {{MHzToHz(5170), MHzToHz(5250)},
1163 {MHzToHz(5490),
1164 MHzToHz(5570)}} /* non-contiguous 160 MHz band made of the two segments */},
1165 {{{MHzToHz(5170), MHzToHz(5330)}} /* full 160 MHz band should have been removed */});
1166
1167 // switch from 80+80 MHz to 160 MHz
1168 RunOne(
1169 {42, 106},
1170 {50},
1171 {{{MHzToHz(5170), MHzToHz(5330)}} /* full 160 MHz band */,
1172 {{MHzToHz(5170), MHzToHz(5250)}} /* first 80 MHz segment is part of the 160 MHz channel*/},
1173 {{{MHzToHz(5490), MHzToHz(5570)}} /* second 80 MHz segment should have been removed */,
1174 {{MHzToHz(5170), MHzToHz(5250)},
1175 {MHzToHz(5490),
1176 MHzToHz(5570)}} /* non-contiguous 160 MHz band should have been removed */});
1177
1178 // switch from 80+80 MHz to 80+80 MHz with segment swap
1179 RunOne({42, 106},
1180 {106, 42},
1181 {{{MHzToHz(5490), MHzToHz(5570)}} /* first 80 MHz segment */,
1182 {{MHzToHz(5490), MHzToHz(5570)}} /* second 80 MHz segment */,
1183 {{MHzToHz(5170), MHzToHz(5250)},
1184 {MHzToHz(5490),
1185 MHzToHz(5570)}} /* non-contiguous 160 MHz band made of the two segments */},
1186 {});
1187
1188 // switch from 80+80 MHz to another 80+80 MHz with one common segment
1189 RunOne({42, 106},
1190 {106, 138},
1191 {{{MHzToHz(5490), MHzToHz(5570)}} /* first 80 MHz segment */,
1192 {{MHzToHz(5650), MHzToHz(5730)}} /* second 80 MHz segment */,
1193 {{MHzToHz(5490), MHzToHz(5570)},
1194 {MHzToHz(5650),
1195 MHzToHz(5730)}} /* non-contiguous 160 MHz band made of the two segments */},
1196 {{{MHzToHz(5170),
1197 MHzToHz(5250)}} /* 80 MHz segment at channel 42 should have been removed */,
1198 {{MHzToHz(5170), MHzToHz(5250)},
1199 {MHzToHz(5490),
1200 MHzToHz(5570)}} /* previous non-contiguous 160 MHz band should have been removed */});
1201
1202 // switch from 80+80 MHz to another 80+80 MHz with no common segment
1203 RunOne({42, 106},
1204 {122, 155},
1205 {{{MHzToHz(5570), MHzToHz(5650)}} /* first 80 MHz segment */,
1206 {{MHzToHz(5735), MHzToHz(5815)}} /* second 80 MHz segment */,
1207 {{MHzToHz(5570), MHzToHz(5650)},
1208 {MHzToHz(5735),
1209 MHzToHz(5815)}} /* non-contiguous 160 MHz band made of the two segments */},
1210 {{{MHzToHz(5170),
1211 MHzToHz(5250)}} /* previous first 80 MHz segment should have been removed */,
1212 {{MHzToHz(5490),
1213 MHzToHz(5570)}} /* previous second 80 MHz segment should have been removed */,
1214 {{MHzToHz(5170), MHzToHz(5250)},
1215 {MHzToHz(5490),
1216 MHzToHz(5570)}} /* previous non-contiguous 160 MHz band should have been removed */});
1217
1219}
1220
1221/**
1222 * @ingroup wifi-test
1223 * @ingroup tests
1224 *
1225 * @brief Test 80+80MHz transmission
1226 *
1227 * The test verifies that two non-contiguous segments are handled by the spectrum PHY
1228 * to transmit 160 MHz PPDUs when the operating channel is configured as 80+80MHz.
1229 *
1230 * The test first considers a contiguous 160 MHz segment and generate interference on the second
1231 * 80 MHz band to verify reception fails in this scenario. Then, a similar interference
1232 * is generated when a 80+80MHz operating channel is configured, where the first frequency segment
1233 * occupies the first 80 MHz band of the previous 160 MHz operating channel. The reception should
1234 * succeed in that scenario, which demonstrates the second 80 MHz band of the operating channel is
1235 * no longer occupying that spectrum portion (the interference is hence is the gap between the two
1236 * frequency segments). Finally, the test also generates interference on each of the frequency
1237 * segments when the operating channel is 80+80MHz, to demonstrate the frequency segments are
1238 * positioned as expected.
1239 */
1241{
1242 public:
1244
1245 private:
1246 void DoSetup() override;
1247 void DoTeardown() override;
1248 void DoRun() override;
1249
1250 /**
1251 * Run one function
1252 * @param channelNumbers the channel number for each segment of the operating channel
1253 * @param interferenceCenterFrequency the center frequency of the interference signal to
1254 * generate
1255 * @param interferenceBandWidth the band width of the interference signal to generate
1256 * @param expectSuccess flag to indicate whether reception is expected to be successful
1257 */
1258 void RunOne(const std::vector<uint8_t>& channelNumbers,
1259 MHz_u interferenceCenterFrequency,
1260 MHz_u interferenceBandWidth,
1261 bool expectSuccess);
1262
1263 /**
1264 * Switch channel function
1265 *
1266 * @param channelNumbers the channel number for each segment of the operating channel
1267 * to switch to
1268 */
1269 void SwitchChannel(const std::vector<uint8_t>& channelNumbers);
1270
1271 /**
1272 * Send 160MHz PPDU function
1273 */
1274 void Send160MhzPpdu();
1275
1276 /**
1277 * Generate interference function
1278 * @param interferencePsd the PSD of the interference to be generated
1279 * @param duration the duration of the interference
1280 */
1281 void GenerateInterference(Ptr<SpectrumValue> interferencePsd, Time duration);
1282
1283 /**
1284 * Stop interference function
1285 */
1286 void StopInterference();
1287
1288 /**
1289 * Receive success function for STA
1290 * @param psdu the PSDU
1291 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
1292 * @param txVector the transmit vector
1293 * @param statusPerMpdu reception status per MPDU
1294 */
1296 RxSignalInfo rxSignalInfo,
1297 const WifiTxVector& txVector,
1298 const std::vector<bool>& statusPerMpdu);
1299
1300 /**
1301 * Receive failure function for STA
1302 * @param psdu the PSDU
1303 */
1305
1306 /**
1307 * Verify results
1308 *
1309 * @param expectSuccess flag to indicate whether reception is expected to be successful
1310 */
1311 void CheckResults(bool expectSuccess);
1312
1316
1317 uint32_t m_countRxSuccessSta; ///< count RX success for STA
1318 uint32_t m_countRxFailureSta; ///< count RX failure for STA
1319};
1320
1322 : TestCase("SpectrumWifiPhy test 80+80MHz transmission"),
1323 m_countRxSuccessSta(0),
1324 m_countRxFailureSta(0)
1325{
1326}
1327
1328void
1329SpectrumWifiPhy80Plus80Test::SwitchChannel(const std::vector<uint8_t>& channelNumbers)
1330{
1331 NS_LOG_FUNCTION(this);
1332 WifiPhy::ChannelSegments channelSegments;
1333 for (auto channelNumber : channelNumbers)
1334 {
1335 const auto& channelInfo = WifiPhyOperatingChannel::FindFirst(channelNumber,
1336 MHz_u{0},
1337 MHz_u{0},
1340 channelSegments.emplace_back(channelInfo->number, channelInfo->width, channelInfo->band, 0);
1341 }
1342 m_phyAp->SetOperatingChannel(channelSegments);
1343 m_phySta->SetOperatingChannel(channelSegments);
1344}
1345
1346void
1348{
1349 NS_LOG_FUNCTION(this);
1350
1351 WifiTxVector txVector{HePhy::GetHeMcs7(),
1352 0,
1354 NanoSeconds(800),
1355 1,
1356 1,
1357 0,
1358 MHz_u{160},
1359 false,
1360 false,
1361 false};
1362
1363 auto pkt = Create<Packet>(1000);
1364 WifiMacHeader hdr;
1366 auto psdu = Create<WifiPsdu>(pkt, hdr);
1367
1368 m_phyAp->Send(psdu, txVector);
1369}
1370
1371void
1373{
1374 m_phyInterferer->SetTxPowerSpectralDensity(interferencePsd);
1375 m_phyInterferer->SetPeriod(duration);
1376 m_phyInterferer->Start();
1378}
1379
1380void
1385
1386void
1388 RxSignalInfo rxSignalInfo,
1389 const WifiTxVector& txVector,
1390 const std::vector<bool>& /*statusPerMpdu*/)
1391{
1392 NS_LOG_FUNCTION(this << *psdu << rxSignalInfo << txVector);
1394}
1395
1396void
1402
1403void
1405{
1406 NS_LOG_FUNCTION(this << expectSuccess);
1408 expectSuccess,
1409 "Reception should be "
1410 << (expectSuccess ? "successful" : "unsuccessful"));
1411}
1412
1413void
1415{
1416 // WifiHelper::EnableLogComponents();
1417 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
1418
1419 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
1420 auto lossModel = CreateObject<FriisPropagationLossModel>();
1421 spectrumChannel->AddPropagationLossModel(lossModel);
1423 spectrumChannel->SetPropagationDelayModel(delayModel);
1424
1425 auto apNode = CreateObject<Node>();
1426 auto apDev = CreateObject<WifiNetDevice>();
1428 auto apInterferenceHelper = CreateObject<InterferenceHelper>();
1429 m_phyAp->SetInterferenceHelper(apInterferenceHelper);
1430 auto apErrorModel = CreateObject<NistErrorRateModel>();
1431 m_phyAp->SetErrorRateModel(apErrorModel);
1432 m_phyAp->SetDevice(apDev);
1433 m_phyAp->AddChannel(spectrumChannel);
1434 m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
1436 m_phyAp->SetMobility(apMobility);
1437 apDev->SetPhy(m_phyAp);
1438 apNode->AggregateObject(apMobility);
1439 apNode->AddDevice(apDev);
1440
1441 auto staNode = CreateObject<Node>();
1442 auto staDev = CreateObject<WifiNetDevice>();
1444 auto staInterferenceHelper = CreateObject<InterferenceHelper>();
1445 m_phySta->SetInterferenceHelper(staInterferenceHelper);
1446 auto staErrorModel = CreateObject<NistErrorRateModel>();
1447 m_phySta->SetErrorRateModel(staErrorModel);
1448 m_phySta->SetDevice(staDev);
1449 m_phySta->AddChannel(spectrumChannel);
1450 m_phySta->ConfigureStandard(WIFI_STANDARD_80211ax);
1452 m_phySta->SetReceiveErrorCallback(
1455 m_phySta->SetMobility(staMobility);
1456 staDev->SetPhy(m_phySta);
1457 staNode->AggregateObject(staMobility);
1458 staNode->AddDevice(staDev);
1459
1460 auto interfererNode = CreateObject<Node>();
1461 auto interfererDev = CreateObject<NonCommunicatingNetDevice>();
1463 m_phyInterferer->SetDevice(interfererDev);
1464 m_phyInterferer->SetChannel(spectrumChannel);
1465 m_phyInterferer->SetDutyCycle(1);
1466 interfererNode->AddDevice(interfererDev);
1467}
1468
1469void
1471{
1472 m_phyAp->Dispose();
1473 m_phyAp = nullptr;
1474 m_phySta->Dispose();
1475 m_phySta = nullptr;
1476 m_phyInterferer->Dispose();
1477 m_phyInterferer = nullptr;
1478}
1479
1480void
1481SpectrumWifiPhy80Plus80Test::RunOne(const std::vector<uint8_t>& channelNumbers,
1482 MHz_u interferenceCenterFrequency,
1483 MHz_u interferenceBandWidth,
1484 bool expectSuccess)
1485{
1486 // reset counters
1489
1492 this,
1493 channelNumbers);
1494
1495 // create info about interference to generate
1496 BandInfo bandInfo{.fl = MHzToHz(interferenceCenterFrequency - (interferenceBandWidth / 2)),
1497 .fc = MHzToHz(interferenceCenterFrequency),
1498 .fh = MHzToHz(interferenceCenterFrequency + (interferenceBandWidth / 2))};
1499 auto spectrumInterference = Create<SpectrumModel>(Bands{bandInfo});
1500 auto interferencePsd = Create<SpectrumValue>(spectrumInterference);
1501 Watt_u interferencePower{0.1};
1502 *interferencePsd = interferencePower / (interferenceBandWidth * 20e6);
1503
1506 this,
1507 interferencePsd,
1508 MilliSeconds(100));
1509
1511
1514 this,
1515 expectSuccess);
1516
1518}
1519
1520void
1522{
1523 // Test transmission over contiguous 160 MHz (channel 50) and interference generated in
1524 // the second half of the channel width (channel 58, i.e. center frequency 5290 and bandwidth 80
1525 // MHz). The reception should fail because the interference occupies half the channel width used
1526 // for the transmission.
1527 // ┌──────────────────┐
1528 // Interference │ channel 58 │
1529 // │ 5290 MHz, 80 MHz │
1530 // └──────────────────┘
1531 //
1532 // ┌──────────────────────────────────────┐
1533 // Operating Channel │ channel 50 │
1534 // │ 5250 MHz, 160 MHz │
1535 // └──────────────────────────────────────┘
1536 //
1537 RunOne({50}, MHz_u{5290}, MHz_u{80}, false);
1538
1539 // Test transmission over non-contiguous 160 MHz (i.e. 80+80MHz) and same interference as in
1540 // previous run. The reception should succeed because the interference is located between the
1541 // two segments.
1542 // ┌──────────────────┐
1543 // Interference │ channel 58 │
1544 // │ 5290 MHz, 80 MHz │
1545 // └──────────────────┘
1546 //
1547 // ┌──────────────────┐ ┌──────────────────┐
1548 // Operating Channel │ channel 42 │ │ channel 106 │
1549 // │80+80MHz segment 0│ │80+80MHz segment 1│
1550 // └──────────────────┘ └──────────────────┘
1551 //
1552 RunOne({42, 106}, MHz_u{5290}, MHz_u{80}, true);
1553
1554 // Test transmission over non-contiguous 160 MHz (i.e. 80+80MHz) and interference generated on
1555 // the first segment of the channel width (channel 42, i.e. center frequency 5210 and bandwidth
1556 // 80 MHz). The reception should fail because the interference occupies half the channel width
1557 // used for the transmission.
1558 // ┌──────────────────┐
1559 // Interference │ channel 42 │
1560 // │ 5210 MHz, 80 MHz │
1561 // └──────────────────┘
1562 //
1563 // ┌──────────────────┐ ┌──────────────────┐
1564 // Operating Channel │ channel 42 │ │ channel 106 │
1565 // │80+80MHz segment 0│ │80+80MHz segment 1│
1566 // └──────────────────┘ └──────────────────┘
1567 //
1568 RunOne({42, 106}, MHz_u{5210}, MHz_u{80}, false);
1569
1570 // Test transmission over non-contiguous 160 MHz (i.e. 80+80MHz) and interference generated on
1571 // the second segment of the channel width (channel 42, i.e. center frequency 5210 and bandwidth
1572 // 80 MHz). The reception should fail because the interference occupies half the channel width
1573 // used for the transmission.
1574 // ┌──────────────────┐
1575 // Interference │ channel 106 │
1576 // │ 5530 MHz, 80 MHz │
1577 // └──────────────────┘
1578 //
1579 // ┌──────────────────┐ ┌──────────────────┐
1580 // Operating Channel │ channel 42 │ │ channel 106 │
1581 // │80+80MHz segment 0│ │80+80MHz segment 1│
1582 // └──────────────────┘ └──────────────────┘
1583 //
1584 RunOne({42, 106}, MHz_u{5530}, MHz_u{80}, false);
1585
1587}
1588
1589/**
1590 * @ingroup wifi-test
1591 * @ingroup tests
1592 *
1593 * @brief Spectrum Wifi Phy Multiple Spectrum Test
1594 *
1595 * This test is testing the ability to plug multiple spectrum channels to the spectrum wifi PHY.
1596 * It considers 4 TX-RX PHY pairs that are independent from each others and are plugged to different
1597 * spectrum channels that are covering different frequency range. Each RX PHY is also attached to
1598 * each of the other 3 spectrum channels it can switch to.
1599 *
1600 * In the first scenario, we consider the default case where each TX-RX PHY pairs are operating on
1601 * different frequency ranges and hence using independent spectrum channels. We validate that no
1602 * packets is received from other TX PHYs attached to different spectrum channels and we also verify
1603 * the amount of connected PHYs to each spectrum channel is exactly 2. The test also makes sure each
1604 * PHY has only one active spectrum channel and that the active one is operating at the expected
1605 * frequency range.
1606 *
1607 * In the second scenario, we consecutively switch the channel of all RX PHYs to the one of each TX
1608 * PHY. We validate that packets are received by all PHYs and we also verify the amount of connected
1609 * PHYs to each spectrum channels is either 5 (1 TX PHY and 4 RX PHYs) or 1 (the TX PHY left alone).
1610 */
1612{
1613 public:
1614 /// Enumeration for channel switching scenarios
1615 enum class ChannelSwitchScenario : uint8_t
1616 {
1617 BEFORE_TX = 0, //!< start TX after channel switch is completed
1618 BETWEEN_TX_RX //!< perform channel switch during propagation delay (after TX and before RX)
1619 };
1620
1621 /**
1622 * Constructor
1623 *
1624 * @param trackSignalsInactiveInterfaces flag to indicate whether signals coming from inactive
1625 * spectrum PHY interfaces shall be tracked during the test
1626 * @param chanSwitchScenario the channel switching scenario to consider for the test
1627 */
1628 SpectrumWifiPhyMultipleInterfacesTest(bool trackSignalsInactiveInterfaces,
1629 ChannelSwitchScenario chanSwitchScenario);
1630
1631 private:
1632 void DoSetup() override;
1633 void DoTeardown() override;
1634 void DoRun() override;
1635
1636 /**
1637 * Switch channel function
1638 *
1639 * @param phy the PHY to switch
1640 * @param band the PHY band to use
1641 * @param channelNumber number the channel number to use
1642 * @param channelWidth the channel width to use
1643 * @param listenerIndex index of the listener for that PHY, if PHY is a RX PHY
1644 */
1646 WifiPhyBand band,
1647 uint8_t channelNumber,
1648 MHz_u channelWidth,
1649 std::optional<std::size_t> listenerIndex);
1650
1651 /**
1652 * Send PPDU function
1653 *
1654 * @param phy the PHY to transmit the signal
1655 * @param txPower the power to transmit the signal (this is also the received power since we do
1656 * not have propagation loss to simplify) \param payloadSize the payload size in bytes
1657 */
1658 void SendPpdu(Ptr<SpectrumWifiPhy> phy, dBm_u txPower, uint32_t payloadSize);
1659
1660 /**
1661 * Callback triggered when a packet is received by a PHY
1662 * @param index the index to identify the RX PHY
1663 * @param packet the received packet
1664 * @param rxPowersW the received power per channel band in watts
1665 */
1666 void RxCallback(std::size_t index,
1667 Ptr<const Packet> packet,
1668 RxPowerWattPerChannelBand rxPowersW);
1669
1670 /**
1671 * Receive success function
1672 * @param index index of the RX STA
1673 * @param psdu the PSDU
1674 * @param rxSignalInfo the info on the received signal (\see RxSignalInfo)
1675 * @param txVector the transmit vector
1676 * @param statusPerMpdu reception status per MPDU
1677 */
1678 void RxSuccess(std::size_t index,
1680 RxSignalInfo rxSignalInfo,
1681 const WifiTxVector& txVector,
1682 const std::vector<bool>& statusPerMpdu);
1683
1684 /**
1685 * Receive failure function
1686 * @param index index of the RX STA
1687 * @param psdu the PSDU
1688 */
1689 void RxFailure(std::size_t index, Ptr<const WifiPsdu> psdu);
1690
1691 /**
1692 * Schedule now to check the interferences
1693 * @param phy the PHY for which the check has to be executed
1694 * @param freqRange the frequency range for which the check has to be executed
1695 * @param band the band for which the check has to be executed
1696 * @param interferencesExpected flag whether interferences are expected to have been tracked
1697 */
1699 const FrequencyRange& freqRange,
1700 const WifiSpectrumBandInfo& band,
1701 bool interferencesExpected);
1702
1703 /**
1704 * Check the interferences
1705 * @param phy the PHY for which the check has to be executed
1706 * @param band the band for which the check has to be executed
1707 * @param interferencesExpected flag whether interferences are expected to have been tracked
1708 */
1710 const WifiSpectrumBandInfo& band,
1711 bool interferencesExpected);
1712
1713 /**
1714 * Verify results
1715 *
1716 * @param index the index to identify the RX PHY to check
1717 * @param expectedNumRx the expected number of RX events for that PHY
1718 * @param expectedNumRxSuccess the expected amount of successfully received packets
1719 * @param expectedRxBytes the expected amount of received bytes
1720 * @param expectedFrequencyRangeActiveRfInterface the expected frequency range (in MHz) of the
1721 * active RF interface
1722 * @param expectedConnectedPhysPerChannel the expected number of PHYs attached for each spectrum
1723 * channel
1724 */
1725 void CheckResults(std::size_t index,
1726 uint32_t expectedNumRx,
1727 uint32_t expectedNumRxSuccess,
1728 uint32_t expectedRxBytes,
1729 FrequencyRange expectedFrequencyRangeActiveRfInterface,
1730 const std::vector<std::size_t>& expectedConnectedPhysPerChannel);
1731
1732 /**
1733 * Verify CCA indication reported by a given PHY
1734 *
1735 * @param index the index to identify the RX PHY to check
1736 * @param expectedCcaBusyIndication flag to indicate whether a CCA BUSY notification is expected
1737 * @param switchingDelay delay between the TX has started and the time RX switched to the TX
1738 * channel
1739 * @param propagationDelay the propagation delay
1740 */
1741 void CheckCcaIndication(std::size_t index,
1742 bool expectedCcaBusyIndication,
1743 Time switchingDelay,
1744 Time propagationDelay);
1745
1746 /**
1747 * Verify rxing state of the interference helper
1748 *
1749 * @param phy the PHY to which the interference helper instance is attached
1750 * @param rxingExpected flag whether the interference helper is expected to be in rxing state or
1751 * not
1752 */
1753 void CheckRxingState(Ptr<SpectrumWifiPhy> phy, bool rxingExpected);
1754
1755 /**
1756 * Reset function
1757 */
1758 void Reset();
1759
1760 bool
1761 m_trackSignalsInactiveInterfaces; //!< flag to indicate whether signals coming from inactive
1762 //!< spectrum PHY interfaces are tracked during the test
1764 m_chanSwitchScenario; //!< the channel switch scenario to consider for the test
1765 std::vector<Ptr<MultiModelSpectrumChannel>> m_spectrumChannels; //!< Spectrum channels
1766 std::vector<Ptr<SpectrumWifiPhy>> m_txPhys{}; //!< TX PHYs
1767 std::vector<Ptr<SpectrumWifiPhy>> m_rxPhys{}; //!< RX PHYs
1768 std::vector<std::shared_ptr<TestPhyListener>> m_listeners{}; //!< listeners
1769
1770 std::vector<uint32_t> m_counts; //!< count number of packets received by PHYs
1771 std::vector<uint32_t>
1772 m_countRxSuccess; //!< count number of packets successfully received by PHYs
1773 std::vector<uint32_t>
1774 m_countRxFailure; //!< count number of packets unsuccessfully received by PHYs
1775 std::vector<uint32_t> m_rxBytes; //!< count number of received bytes
1776
1777 Time m_lastTxStart{0}; //!< hold the time at which the last transmission started
1778 Time m_lastTxEnd{0}; //!< hold the time at which the last transmission ended
1779};
1780
1782 bool trackSignalsInactiveInterfaces,
1783 ChannelSwitchScenario chanSwitchScenario)
1784 : TestCase{"SpectrumWifiPhy test operation with multiple RF interfaces"},
1785 m_trackSignalsInactiveInterfaces{trackSignalsInactiveInterfaces},
1786 m_chanSwitchScenario{chanSwitchScenario}
1787{
1788}
1789
1790void
1792 WifiPhyBand band,
1793 uint8_t channelNumber,
1794 MHz_u channelWidth,
1795 std::optional<std::size_t> listenerIndex)
1796{
1797 NS_LOG_FUNCTION(this << phy << band << +channelNumber << channelWidth);
1798 if (listenerIndex)
1799 {
1800 auto& listener = m_listeners.at(*listenerIndex);
1801 listener->m_notifyMaybeCcaBusyStart = 0;
1802 listener->m_ccaBusyStart = Seconds(0);
1803 listener->m_ccaBusyEnd = Seconds(0);
1804 }
1805 phy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNumber, channelWidth, band, 0});
1806 // verify rxing state of interference helper is reset after channel switch
1808 this,
1809 phy,
1810 false);
1811}
1812
1813void
1815 dBm_u txPower,
1816 uint32_t payloadSize)
1817{
1818 NS_LOG_FUNCTION(this << phy << txPower << payloadSize << phy->GetCurrentFrequencyRange()
1819 << phy->GetChannelWidth() << phy->GetChannelNumber());
1820
1822 0,
1824 NanoSeconds(800),
1825 1,
1826 1,
1827 0,
1828 MHz_u{20},
1829 false,
1830 false};
1831 Ptr<Packet> pkt = Create<Packet>(payloadSize);
1832 WifiMacHeader hdr;
1834 hdr.SetQosTid(0);
1835 hdr.SetAddr1(Mac48Address("00:00:00:00:00:01"));
1836 hdr.SetSequenceNumber(1);
1837 Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
1838
1841 txVector,
1842 phy->GetPhyBand());
1843 phy->SetTxPowerStart(txPower);
1844 phy->SetTxPowerEnd(txPower);
1845 phy->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
1846}
1847
1848void
1850 Ptr<const Packet> packet,
1851 RxPowerWattPerChannelBand /*rxPowersW*/)
1852{
1853 auto phy = m_rxPhys.at(index);
1854 const auto payloadBytes = packet->GetSize() - 30;
1855 NS_LOG_FUNCTION(this << index << payloadBytes << phy->GetCurrentFrequencyRange()
1856 << phy->GetChannelWidth() << phy->GetChannelNumber());
1857 m_counts.at(index)++;
1858 m_rxBytes.at(index) += payloadBytes;
1859}
1860
1861void
1864 RxSignalInfo rxSignalInfo,
1865 const WifiTxVector& txVector,
1866 const std::vector<bool>& /*statusPerMpdu*/)
1867{
1868 NS_LOG_FUNCTION(this << index << *psdu << rxSignalInfo << txVector);
1869 m_countRxSuccess.at(index)++;
1870}
1871
1872void
1874{
1875 NS_LOG_FUNCTION(this << index << *psdu);
1876 m_countRxFailure.at(index)++;
1877}
1878
1879void
1881 const FrequencyRange& freqRange,
1882 const WifiSpectrumBandInfo& band,
1883 bool interferencesExpected)
1884{
1885 if ((!m_trackSignalsInactiveInterfaces) && (phy->GetCurrentFrequencyRange() != freqRange))
1886 {
1887 // ignore since no bands for that range exists in interference helper in that case
1888 return;
1889 }
1890 // This is needed to make sure PHY state will be checked as the last event if a state change
1891 // occurred at the exact same time as the check
1893 this,
1894 phy,
1895 band,
1896 interferencesExpected);
1897}
1898
1899void
1901 const WifiSpectrumBandInfo& band,
1902 bool interferencesExpected)
1903{
1904 NS_LOG_FUNCTION(this << phy << band << interferencesExpected);
1905 PointerValue ptr;
1906 phy->GetAttribute("InterferenceHelper", ptr);
1907 auto interferenceHelper = DynamicCast<InterferenceHelper>(ptr.Get<InterferenceHelper>());
1908 NS_ASSERT(interferenceHelper);
1909 const auto energyDuration = interferenceHelper->GetEnergyDuration(Watt_u{0}, band);
1910 NS_TEST_ASSERT_MSG_EQ(energyDuration.IsStrictlyPositive(),
1911 interferencesExpected,
1912 "Incorrect interferences detection");
1913}
1914
1915void
1917 std::size_t index,
1918 uint32_t expectedNumRx,
1919 uint32_t expectedNumRxSuccess,
1920 uint32_t expectedRxBytes,
1921 FrequencyRange expectedFrequencyRangeActiveRfInterface,
1922 const std::vector<std::size_t>& expectedConnectedPhysPerChannel)
1923{
1924 NS_LOG_FUNCTION(this << index << expectedNumRx << expectedNumRxSuccess << expectedRxBytes
1925 << expectedFrequencyRangeActiveRfInterface);
1926 const auto phy = m_rxPhys.at(index);
1927 std::size_t numActiveInterfaces = 0;
1928 for (const auto& [freqRange, interface] : phy->GetSpectrumPhyInterfaces())
1929 {
1930 const auto expectedActive = (freqRange == expectedFrequencyRangeActiveRfInterface);
1931 const auto isActive = (interface == phy->GetCurrentInterface());
1932 NS_TEST_ASSERT_MSG_EQ(isActive, expectedActive, "Incorrect active interface");
1933 if (isActive)
1934 {
1935 numActiveInterfaces++;
1936 }
1937 }
1938 NS_TEST_ASSERT_MSG_EQ(numActiveInterfaces, 1, "There should always be one active interface");
1939 NS_ASSERT(expectedConnectedPhysPerChannel.size() == m_spectrumChannels.size());
1940 for (std::size_t i = 0; i < m_spectrumChannels.size(); ++i)
1941 {
1942 NS_TEST_ASSERT_MSG_EQ(m_spectrumChannels.at(i)->GetNDevices(),
1943 expectedConnectedPhysPerChannel.at(i),
1944 "Incorrect number of PHYs attached to the spectrum channel");
1945 }
1946 NS_TEST_ASSERT_MSG_EQ(m_counts.at(index), expectedNumRx, "Unexpected amount of RX events");
1948 expectedNumRxSuccess,
1949 "Unexpected amount of successfully received packets");
1951 0,
1952 "Unexpected amount of unsuccessfully received packets");
1953 NS_TEST_ASSERT_MSG_EQ(m_listeners.at(index)->m_notifyRxStart,
1954 expectedNumRxSuccess,
1955 "Unexpected amount of RX payload start indication");
1956}
1957
1958void
1960 bool expectedCcaBusyIndication,
1961 Time switchingDelay,
1962 Time propagationDelay)
1963{
1964 const auto expectedCcaBusyStart =
1965 expectedCcaBusyIndication ? m_lastTxStart + switchingDelay : Seconds(0);
1966 const auto expectedCcaBusyEnd =
1967 expectedCcaBusyIndication ? m_lastTxEnd + propagationDelay : Seconds(0);
1968 NS_LOG_FUNCTION(this << index << expectedCcaBusyIndication << expectedCcaBusyStart
1969 << expectedCcaBusyEnd);
1970 auto& listener = m_listeners.at(index);
1971 const auto ccaBusyIndication = (listener->m_notifyMaybeCcaBusyStart > 0);
1972 const auto ccaBusyStart = listener->m_ccaBusyStart;
1973 const auto ccaBusyEnd = listener->m_ccaBusyEnd;
1974 NS_TEST_ASSERT_MSG_EQ(ccaBusyIndication,
1975 expectedCcaBusyIndication,
1976 "CCA busy indication check failed");
1977 NS_TEST_ASSERT_MSG_EQ(ccaBusyStart, expectedCcaBusyStart, "CCA busy start mismatch");
1978 NS_TEST_ASSERT_MSG_EQ(ccaBusyEnd, expectedCcaBusyEnd, "CCA busy end mismatch");
1979}
1980
1981void
1983{
1984 NS_LOG_FUNCTION(this << phy << rxingExpected);
1985 PointerValue ptr;
1986 phy->GetAttribute("InterferenceHelper", ptr);
1987 auto interferenceHelper = DynamicCast<ExtInterferenceHelper>(ptr.Get<ExtInterferenceHelper>());
1988 NS_ASSERT(interferenceHelper);
1989 NS_TEST_ASSERT_MSG_EQ(interferenceHelper->IsRxing(), rxingExpected, "Incorrect rxing state");
1990}
1991
1992void
1994{
1995 NS_LOG_FUNCTION(this);
1996 for (auto& count : m_counts)
1997 {
1998 count = 0;
1999 }
2000 for (auto& listener : m_listeners)
2001 {
2002 listener->Reset();
2003 }
2004 // restore all RX PHYs to initial channels
2005 for (std::size_t rxPhyIndex = 0; rxPhyIndex < m_rxPhys.size(); ++rxPhyIndex)
2006 {
2007 auto txPhy = m_txPhys.at(rxPhyIndex);
2008 SwitchChannel(m_rxPhys.at(rxPhyIndex),
2009 txPhy->GetPhyBand(),
2010 txPhy->GetChannelNumber(),
2011 txPhy->GetChannelWidth(),
2012 rxPhyIndex);
2013 }
2014 // reset counters
2015 for (auto& countRxSuccess : m_countRxSuccess)
2016 {
2017 countRxSuccess = 0;
2018 }
2019 for (auto& countRxFailure : m_countRxFailure)
2020 {
2021 countRxFailure = 0;
2022 }
2023 for (auto& rxBytes : m_rxBytes)
2024 {
2025 rxBytes = 0;
2026 }
2027}
2028
2029void
2031{
2032 NS_LOG_FUNCTION(this);
2033
2034 // WifiHelper::EnableLogComponents();
2035 // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
2036
2037 NodeContainer wifiApNode(1);
2038 NodeContainer wifiStaNode(1);
2039
2040 WifiHelper wifi;
2041 wifi.SetStandard(WIFI_STANDARD_80211be);
2042
2043 SpectrumWifiPhyHelper phyHelper(4);
2044 phyHelper.SetInterferenceHelper("ns3::ExtInterferenceHelper");
2046
2047 struct SpectrumPhyInterfaceInfo
2048 {
2049 FrequencyRange range; ///< frequency range covered by the interface
2050 uint8_t number; ///< channel number the interface operates on
2051 WifiPhyBand band; ///< PHY band the interface operates on
2052 std::string bandName; ///< name of the PHY band the interface operates on
2053 };
2054
2055 const FrequencyRange WIFI_SPECTRUM_5_GHZ_LOW{
2059 const FrequencyRange WIFI_SPECTRUM_5_GHZ_HIGH{
2063
2064 const std::vector<SpectrumPhyInterfaceInfo> interfaces{
2065 {WIFI_SPECTRUM_2_4_GHZ, 2, WIFI_PHY_BAND_2_4GHZ, "BAND_2_4GHZ"},
2066 {WIFI_SPECTRUM_5_GHZ_LOW, 42, WIFI_PHY_BAND_5GHZ, "BAND_5GHZ"},
2067 {WIFI_SPECTRUM_5_GHZ_HIGH, 163, WIFI_PHY_BAND_5GHZ, "BAND_5GHZ"},
2068 {WIFI_SPECTRUM_6_GHZ, 215, WIFI_PHY_BAND_6GHZ, "BAND_6GHZ"}};
2069
2070 for (std::size_t i = 0; i < interfaces.size(); ++i)
2071 {
2072 auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
2074 spectrumChannel->SetPropagationDelayModel(delayModel);
2075 std::ostringstream oss;
2076 oss << "{" << +interfaces.at(i).number << ", 0, " << interfaces.at(i).bandName << ", 0}";
2077 phyHelper.Set(i, "ChannelSettings", StringValue(oss.str()));
2078 phyHelper.AddChannel(spectrumChannel, interfaces.at(i).range);
2079 m_spectrumChannels.emplace_back(spectrumChannel);
2080 }
2081
2082 WifiMacHelper mac;
2083 mac.SetType("ns3::ApWifiMac", "BeaconGeneration", BooleanValue(false));
2084 phyHelper.Set("TrackSignalsFromInactiveInterfaces", BooleanValue(false));
2085 auto apDevice = wifi.Install(phyHelper, mac, wifiApNode.Get(0));
2086
2087 mac.SetType("ns3::StaWifiMac", "ActiveProbing", BooleanValue(false));
2088 phyHelper.Set("TrackSignalsFromInactiveInterfaces",
2090 auto staDevice = wifi.Install(phyHelper, mac, wifiStaNode.Get(0));
2091
2092 MobilityHelper mobility;
2093 auto positionAlloc = CreateObject<ListPositionAllocator>();
2094
2095 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
2096 positionAlloc->Add(Vector(10.0, 0.0, 0.0));
2097 mobility.SetPositionAllocator(positionAlloc);
2098
2099 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
2100 mobility.Install(wifiApNode);
2101 mobility.Install(wifiStaNode);
2102
2103 for (std::size_t i = 0; i < interfaces.size(); ++i)
2104 {
2105 auto txPhy =
2106 DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetPhy(i));
2108 {
2109 txPhy->SetAttribute("ChannelSwitchDelay", TimeValue(NanoSeconds(1)));
2110 }
2111 m_txPhys.push_back(txPhy);
2112
2113 const auto index = m_rxPhys.size();
2114 auto rxPhy =
2115 DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(staDevice.Get(0))->GetPhy(i));
2117 {
2118 rxPhy->SetAttribute("ChannelSwitchDelay", TimeValue(NanoSeconds(1)));
2119 }
2120 rxPhy->TraceConnectWithoutContext(
2121 "PhyRxBegin",
2123
2124 rxPhy->SetReceiveOkCallback(
2126 rxPhy->SetReceiveErrorCallback(
2128
2129 auto listener = std::make_shared<TestPhyListener>();
2130 rxPhy->RegisterListener(listener);
2131 m_listeners.push_back(std::move(listener));
2132
2133 m_rxPhys.push_back(rxPhy);
2134 m_counts.push_back(0);
2135 m_countRxSuccess.push_back(0);
2136 m_countRxFailure.push_back(0);
2137 m_rxBytes.push_back(0);
2138 }
2139}
2140
2141void
2143{
2144 NS_LOG_FUNCTION(this);
2145 for (auto& phy : m_txPhys)
2146 {
2147 phy->Dispose();
2148 phy = nullptr;
2149 }
2150 for (auto& phy : m_rxPhys)
2151 {
2152 phy->Dispose();
2153 phy = nullptr;
2154 }
2156}
2157
2158void
2160{
2161 NS_LOG_FUNCTION(this);
2162
2163 const dBm_u ccaEdThreshold{-62.0}; ///< CCA-ED threshold
2164 const auto txAfterChannelSwitchDelay =
2166 ? 250
2167 : 0); ///< delay in seconds between channel switch is triggered and a
2168 ///< transmission gets started
2169 const auto checkResultsDelay =
2170 Seconds(0.5); ///< delay in seconds between start of test and moment results are verified
2171 const auto flushResultsDelay =
2172 Seconds(0.9); ///< delay in seconds between start of test and moment results are flushed
2173 const auto txOngoingAfterTxStartedDelay =
2174 MicroSeconds(50); ///< delay in microseconds between a transmission has started and a point
2175 ///< in time the transmission is ongoing
2176 const auto propagationDelay = NanoSeconds(33); // propagation delay for the test scenario
2177
2178 Time delay{0};
2179
2180 // default channels active for all PHYs: each PHY only receives from its associated TX
2181 std::vector<std::size_t> expectedConnectedPhysPerChannel =
2182 m_trackSignalsInactiveInterfaces ? std::vector<std::size_t>{5, 5, 5, 5}
2183 : // all RX PHYs keep all channels active when tracking
2184 // interferences on inactive interfaces
2185 std::vector<std::size_t>{2, 2, 2, 2}; // default channels active for all PHYs: each PHY
2186 // only receives from its associated TX
2187
2188 for (std::size_t i = 0; i < 4; ++i)
2189 {
2190 auto txPpduPhy = m_txPhys.at(i);
2191 delay += Seconds(1);
2192 Simulator::Schedule(delay,
2194 this,
2195 txPpduPhy,
2196 dBm_u{0},
2197 1000);
2198 for (std::size_t j = 0; j < 4; ++j)
2199 {
2200 auto txPhy = m_txPhys.at(j);
2201 auto rxPhy = m_rxPhys.at(j);
2202 const auto& expectedFreqRange = txPhy->GetCurrentFrequencyRange();
2203 Simulator::Schedule(delay + txOngoingAfterTxStartedDelay,
2205 this,
2206 rxPhy,
2207 txPpduPhy->GetCurrentFrequencyRange(),
2208 txPpduPhy->GetBand(txPpduPhy->GetChannelWidth(), 0),
2209 true);
2210 Simulator::Schedule(delay + checkResultsDelay,
2212 this,
2213 j,
2214 (i == j) ? 1 : 0,
2215 (i == j) ? 1 : 0,
2216 (i == j) ? 1000 : 0,
2217 expectedFreqRange,
2218 expectedConnectedPhysPerChannel);
2219 }
2220 Simulator::Schedule(delay + flushResultsDelay,
2222 this);
2223 }
2224
2225 // same channel active for all PHYs: all PHYs receive from TX
2226 for (std::size_t i = 0; i < 4; ++i)
2227 {
2228 delay += Seconds(1);
2229 auto txPpduPhy = m_txPhys.at(i);
2230 Simulator::Schedule(delay + txAfterChannelSwitchDelay,
2232 this,
2233 txPpduPhy,
2234 dBm_u{0},
2235 1000);
2236 const auto& expectedFreqRange = txPpduPhy->GetCurrentFrequencyRange();
2237 for (std::size_t j = 0; j < 4; ++j)
2238 {
2240 {
2241 for (std::size_t k = 0; k < expectedConnectedPhysPerChannel.size(); ++k)
2242 {
2243 expectedConnectedPhysPerChannel.at(k) = (k == i) ? 5 : 1;
2244 }
2245 }
2246 auto rxPhy = m_rxPhys.at(j);
2247 Simulator::Schedule(delay,
2249 this,
2250 rxPhy,
2251 txPpduPhy->GetPhyBand(),
2252 txPpduPhy->GetChannelNumber(),
2253 txPpduPhy->GetChannelWidth(),
2254 j);
2255 Simulator::Schedule(delay + txAfterChannelSwitchDelay + txOngoingAfterTxStartedDelay,
2257 this,
2258 rxPhy,
2259 txPpduPhy->GetCurrentFrequencyRange(),
2260 txPpduPhy->GetBand(txPpduPhy->GetChannelWidth(), 0),
2261 true);
2262 Simulator::Schedule(delay + checkResultsDelay,
2264 this,
2265 j,
2266 1,
2267 1,
2268 1000,
2269 expectedFreqRange,
2270 expectedConnectedPhysPerChannel);
2271 }
2272 Simulator::Schedule(delay + flushResultsDelay,
2274 this);
2275 }
2276
2277 // Switch all PHYs to channel 36: all PHYs switch to the second spectrum channel
2278 // since second spectrum channel is 42 (80 MHz) and hence covers channel 36 (20 MHz)
2279 const auto secondSpectrumChannelIndex = 1;
2280 auto channel36TxPhy = m_txPhys.at(secondSpectrumChannelIndex);
2281 const auto& expectedFreqRange = channel36TxPhy->GetCurrentFrequencyRange();
2282 for (std::size_t i = 0; i < 4; ++i)
2283 {
2284 delay += Seconds(1);
2285 auto txPpduPhy = m_txPhys.at(i);
2286 Simulator::Schedule(delay + txAfterChannelSwitchDelay,
2288 this,
2289 txPpduPhy,
2290 dBm_u{0},
2291 1000);
2292 for (std::size_t j = 0; j < 4; ++j)
2293 {
2295 {
2296 for (std::size_t k = 0; k < expectedConnectedPhysPerChannel.size(); ++k)
2297 {
2298 expectedConnectedPhysPerChannel.at(k) =
2299 (k == secondSpectrumChannelIndex) ? 5 : 1;
2300 }
2301 }
2302 Simulator::Schedule(delay,
2304 this,
2305 m_rxPhys.at(j),
2309 j);
2310 Simulator::Schedule(delay + checkResultsDelay,
2312 this,
2313 j,
2314 (i == secondSpectrumChannelIndex) ? 1 : 0,
2315 (i == secondSpectrumChannelIndex) ? 1 : 0,
2316 (i == secondSpectrumChannelIndex) ? 1000 : 0,
2317 expectedFreqRange,
2318 expectedConnectedPhysPerChannel);
2319 }
2320 Simulator::Schedule(delay + flushResultsDelay,
2322 this);
2323 }
2324
2325 // verify CCA indication when switching to a channel with an ongoing transmission
2326 for (const auto txPower : {dBm_u{-60} /* above CCA-ED */, dBm_u{-70} /* below CCA-ED */})
2327 {
2328 for (std::size_t i = 0; i < 4; ++i)
2329 {
2330 for (std::size_t j = 0; j < 4; ++j)
2331 {
2332 auto txPpduPhy = m_txPhys.at(i);
2333 const auto startChannel = WifiPhyOperatingChannel::FindFirst(
2334 txPpduPhy->GetPrimaryChannelNumber(MHz_u{20}),
2335 MHz_u{0},
2336 MHz_u{20},
2338 txPpduPhy->GetPhyBand());
2339 for (auto bw = txPpduPhy->GetChannelWidth(); bw >= MHz_u{20}; bw /= 2)
2340 {
2341 const auto& channelInfo =
2343 MHz_u{0},
2344 bw,
2346 txPpduPhy->GetPhyBand(),
2347 startChannel));
2348 delay += Seconds(1);
2349 Simulator::Schedule(delay,
2351 this,
2352 txPpduPhy,
2353 txPower,
2354 1000);
2355 Simulator::Schedule(delay + txOngoingAfterTxStartedDelay,
2357 this,
2358 m_rxPhys.at(j),
2359 channelInfo.band,
2360 channelInfo.number,
2361 channelInfo.width,
2362 j);
2363 for (std::size_t k = 0; k < 4; ++k)
2364 {
2365 if ((i != j) && (k == i))
2366 {
2367 continue;
2368 }
2369 const auto expectCcaBusyIndication =
2370 (k == i) ? (txPower >= ccaEdThreshold)
2372 ? ((txPower >= ccaEdThreshold) ? (j == k) : false)
2373 : false);
2375 delay + checkResultsDelay,
2377 this,
2378 k,
2379 expectCcaBusyIndication,
2380 txOngoingAfterTxStartedDelay,
2381 propagationDelay);
2382 }
2383 Simulator::Schedule(delay + flushResultsDelay,
2385 this);
2386 }
2387 }
2388 }
2389 }
2390
2392 {
2393 /* Reproduce an EMLSR scenario where a PHY is on an initial band and receives a packet.
2394 * Then, the PHY switches to another band where it starts receiving another packet.
2395 * During reception of the PHY header, the PHY switches back to the initial band and starts
2396 * receiving yet another packet. In this case, first and last packets should be successfully
2397 * received (no interference), the second packet reception has been interrupted (before the
2398 * payload reception does start, hence it does not reach the RX state). */
2399 {
2400 // first TX on initial band
2401 auto txPpduPhy = m_txPhys.at(0);
2402 delay += Seconds(1);
2403 Simulator::Schedule(delay,
2405 this,
2406 txPpduPhy,
2407 dBm_u{20},
2408 500);
2409
2410 // switch channel to other band
2411 delay += Seconds(1);
2412 txPpduPhy = m_txPhys.at(1);
2413 Simulator::Schedule(delay,
2415 this,
2416 m_rxPhys.at(0),
2417 txPpduPhy->GetPhyBand(),
2418 txPpduPhy->GetChannelNumber(),
2419 txPpduPhy->GetChannelWidth(),
2420 0);
2421
2422 // TX on other band
2423 delay += Seconds(1);
2424 Simulator::Schedule(delay,
2426 this,
2427 txPpduPhy,
2428 dBm_u{0},
2429 1000);
2430
2431 // switch back to initial band during PHY header reception
2432 txPpduPhy = m_txPhys.at(0);
2433 delay += MicroSeconds(20); // right after legacy PHY header reception
2434 Simulator::Schedule(delay,
2436 this,
2437 m_rxPhys.at(0),
2438 txPpduPhy->GetPhyBand(),
2439 txPpduPhy->GetChannelNumber(),
2440 txPpduPhy->GetChannelWidth(),
2441 0);
2442
2443 // TX once more on the initial band
2444 delay += Seconds(1);
2445 Simulator::Schedule(delay,
2447 this,
2448 txPpduPhy,
2449 dBm_u{0},
2450 1500);
2451
2452 // check results
2454 delay + checkResultsDelay,
2456 this,
2457 0,
2458 3, // 3 RX events
2459 2, // 2 packets should have been successfully received, 1 packet should
2460 // have been interrupted (switch during PHY header reception)
2461 2000, // 500 bytes (firstpacket) and 1500 bytes (third packet)
2462 txPpduPhy->GetCurrentFrequencyRange(),
2463 expectedConnectedPhysPerChannel);
2464
2465 // reset
2466 Simulator::Schedule(delay + flushResultsDelay,
2468 this);
2469 }
2470
2471 /* Reproduce an EMLSR scenario where a PHY is on an initial band and receives a packet
2472 * but switches to another band during preamble detection period. Then, it starts receiving
2473 * two packets which interfere with each other. Afterwards, the PHY goes back to its initial
2474 * band and starts receiving yet another packet. In this case, only the last packet should
2475 * be successfully received (no interference). */
2476 {
2477 // switch channel of PHY index 0 to 5 GHz low band (operating channel of TX PHY index 1)
2478 auto txPpduPhy = m_txPhys.at(1);
2479 delay += Seconds(1);
2480 Simulator::Schedule(delay,
2482 this,
2483 m_rxPhys.at(0),
2484 txPpduPhy->GetPhyBand(),
2485 txPpduPhy->GetChannelNumber(),
2486 txPpduPhy->GetChannelWidth(),
2487 0);
2488
2489 // start transmission on 5 GHz low band
2490 delay += Seconds(1);
2491 Simulator::Schedule(delay,
2493 this,
2494 txPpduPhy,
2495 dBm_u{20},
2496 500);
2497
2498 // switch channel back to previous channel before preamble detection is finished:
2499 // this is needed to verify interference helper rxing state is properly reset
2500 // since ongoing reception is aborted when switching operating channel
2503 this,
2504 m_rxPhys.at(0),
2505 m_txPhys.at(0)->GetPhyBand(),
2506 m_txPhys.at(0)->GetChannelNumber(),
2507 m_txPhys.at(0)->GetChannelWidth(),
2508 0);
2509
2510 delay += Seconds(1);
2511 // we need 2 TX PHYs on the 5 GHz low band to have simultaneous transmissions
2512 // switch operating channel of TX PHY index 2 to the 5 GHz low band
2513 Simulator::Schedule(delay,
2515 this,
2516 m_txPhys.at(2),
2517 txPpduPhy->GetPhyBand(),
2518 txPpduPhy->GetChannelNumber(),
2519 txPpduPhy->GetChannelWidth(),
2520 std::nullopt);
2521
2522 // first transmission on 5 GHz low band with high power
2523 delay += Seconds(1);
2524 Simulator::Schedule(delay,
2526 this,
2527 txPpduPhy,
2528 dBm_u{20},
2529 1000);
2530
2531 // second transmission on 5 GHz low band with high power a bit later:
2532 // first powers get updated updated in the corresponding bands
2533 txPpduPhy = m_txPhys.at(2);
2534 Simulator::Schedule(delay + NanoSeconds(10),
2536 this,
2537 txPpduPhy,
2538 dBm_u{20},
2539 1000);
2540
2541 // restore channel for TX PHY index 2
2542 delay += Seconds(1);
2543 Simulator::Schedule(delay,
2545 this,
2546 m_txPhys.at(2),
2547 m_rxPhys.at(2)->GetPhyBand(),
2548 m_rxPhys.at(2)->GetChannelNumber(),
2549 m_rxPhys.at(2)->GetChannelWidth(),
2550 std::nullopt);
2551
2552 // switch channel of PHY index 0 to 5 GHz low band again
2553 delay += Seconds(1);
2554 txPpduPhy = m_txPhys.at(1);
2555 Simulator::Schedule(delay,
2557 this,
2558 m_rxPhys.at(0),
2559 txPpduPhy->GetPhyBand(),
2560 txPpduPhy->GetChannelNumber(),
2561 txPpduPhy->GetChannelWidth(),
2562 0);
2563
2564 // transmit PPDU on 5 GHz low band (no interference)
2565 delay += Seconds(1);
2566 Simulator::Schedule(delay,
2568 this,
2569 txPpduPhy,
2570 dBm_u{0},
2571 1500);
2572
2573 // check results
2574 Simulator::Schedule(delay + checkResultsDelay,
2576 this,
2577 0,
2578 1, // 1 RX event
2579 1, // last transmitted packet should have been successfully received
2580 1500, // 1500 bytes (payload of last transmitted packet)
2581 txPpduPhy->GetCurrentFrequencyRange(),
2582 expectedConnectedPhysPerChannel);
2583
2584 // reset
2585 Simulator::Schedule(delay + flushResultsDelay,
2587 this);
2588 }
2589 }
2590
2591 delay += Seconds(1);
2592 Simulator::Stop(delay);
2594}
2595
2596/**
2597 * @ingroup wifi-test
2598 * @ingroup tests
2599 *
2600 * @brief Spectrum Wifi Phy Interfaces Helper Test
2601 *
2602 * This test checks the expected interfaces are added to the spectrum PHY instances
2603 * created by the helper.
2604 */
2606{
2607 public:
2610
2611 private:
2612 void DoRun() override;
2613};
2614
2616 : TestCase("Check PHY interfaces added to PHY instances using helper")
2617{
2618}
2619
2620void
2622{
2623 WifiHelper wifiHelper;
2625
2626 SpectrumWifiPhyHelper phyHelper(3);
2627 phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
2628 phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
2629 phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
2630
2634
2635 WifiMacHelper macHelper;
2637
2638 /* Default case: all interfaces are added to each link */
2639 auto device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(0));
2640
2641 // Verify each PHY has 3 interfaces
2642 auto phyLink0 =
2644 NS_ASSERT(phyLink0);
2645 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2646 3,
2647 "Incorrect number of PHY interfaces added to PHY link ID 0");
2648
2649 auto phyLink1 =
2651 NS_ASSERT(phyLink1);
2652 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2653 3,
2654 "Incorrect number of PHY interfaces added to PHY link ID 1");
2655
2656 auto phyLink2 =
2658 NS_ASSERT(phyLink2);
2659 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2660 3,
2661 "Incorrect number of PHY interfaces added to PHY link ID 2");
2662
2663 /* each PHY has a single interface */
2667 device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(1));
2668
2669 // Verify each PHY has a single interface
2670 phyLink0 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(0));
2671 NS_ASSERT(phyLink0);
2672 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2673 1,
2674 "Incorrect number of PHY interfaces added to PHY link ID 0");
2675 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2676 1,
2677 "Incorrect PHY interfaces added to PHY link ID 0");
2678
2679 phyLink1 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(1));
2680 NS_ASSERT(phyLink1);
2681 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2682 1,
2683 "Incorrect number of PHY interfaces added to PHY link ID 1");
2684 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2685 1,
2686 "Incorrect PHY interfaces added to PHY link ID 1");
2687
2688 phyLink2 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(2));
2689 NS_ASSERT(phyLink2);
2690 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().size(),
2691 1,
2692 "Incorrect number of PHY interfaces added to PHY link ID 2");
2693 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2694 1,
2695 "Incorrect PHY interfaces added to PHY link ID 2");
2696
2697 /* add yet another interface to PHY 0 */
2699 device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(2));
2700
2701 // Verify each PHY has a single interface except PHY 0 that should have 2 interfaces
2702 phyLink0 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(0));
2703 NS_ASSERT(phyLink0);
2704 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2705 2,
2706 "Incorrect number of PHY interfaces added to PHY link ID 0");
2707 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2708 1,
2709 "Incorrect PHY interfaces added to PHY link ID 0");
2710 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2711 1,
2712 "Incorrect PHY interfaces added to PHY link ID 0");
2713
2714 phyLink1 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(1));
2715 NS_ASSERT(phyLink1);
2716 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2717 1,
2718 "Incorrect number of PHY interfaces added to PHY link ID 1");
2719 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2720 1,
2721 "Incorrect PHY interfaces added to PHY link ID 1");
2722
2723 phyLink2 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(2));
2724 NS_ASSERT(phyLink2);
2725 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().size(),
2726 1,
2727 "Incorrect number of PHY interfaces added to PHY link ID 2");
2728 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2729 1,
2730 "Incorrect PHY interfaces added to PHY link ID 2");
2731
2732 /* reset mapping previously configured to helper: back to default */
2733 phyHelper.ResetPhyToFreqRangeMapping();
2734 device = wifiHelper.Install(phyHelper, macHelper, nodes.Get(3));
2735
2736 // Verify each PHY has 3 interfaces
2737 phyLink0 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(0));
2738 NS_ASSERT(phyLink0);
2739 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().size(),
2740 3,
2741 "Incorrect number of PHY interfaces added to PHY link ID 0");
2742 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2743 1,
2744 "Incorrect PHY interfaces added to PHY link ID 0");
2745 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2746 1,
2747 "Incorrect PHY interfaces added to PHY link ID 0");
2748 NS_TEST_ASSERT_MSG_EQ(phyLink0->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2749 1,
2750 "Incorrect PHY interfaces added to PHY link ID 0");
2751
2752 phyLink1 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(1));
2753 NS_ASSERT(phyLink1);
2754 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().size(),
2755 3,
2756 "Incorrect number of PHY interfaces added to PHY link ID 1");
2757 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2758 1,
2759 "Incorrect PHY interfaces added to PHY link ID 0");
2760 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2761 1,
2762 "Incorrect PHY interfaces added to PHY link ID 0");
2763 NS_TEST_ASSERT_MSG_EQ(phyLink1->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2764 1,
2765 "Incorrect PHY interfaces added to PHY link ID 0");
2766
2767 phyLink2 = DynamicCast<SpectrumWifiPhy>(DynamicCast<WifiNetDevice>(device.Get(0))->GetPhy(2));
2768 NS_ASSERT(phyLink2);
2769 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().size(),
2770 3,
2771 "Incorrect number of PHY interfaces added to PHY link ID 2");
2772 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_2_4_GHZ),
2773 1,
2774 "Incorrect PHY interfaces added to PHY link ID 0");
2775 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_5_GHZ),
2776 1,
2777 "Incorrect PHY interfaces added to PHY link ID 0");
2778 NS_TEST_ASSERT_MSG_EQ(phyLink2->GetSpectrumPhyInterfaces().count(WIFI_SPECTRUM_6_GHZ),
2779 1,
2780 "Incorrect PHY interfaces added to PHY link ID 0");
2781
2783}
2784
2785/**
2786 * @ingroup wifi-test
2787 * @ingroup tests
2788 *
2789 * @brief Spectrum Wifi Phy Test Suite
2790 */
2792{
2793 public:
2795};
2796
2798 : TestSuite("wifi-spectrum-phy", Type::UNIT)
2799{
2800 AddTestCase(new SpectrumWifiPhyBasicTest, TestCase::Duration::QUICK);
2801 AddTestCase(new SpectrumWifiPhyListenerTest, TestCase::Duration::QUICK);
2802 AddTestCase(new SpectrumWifiPhyFilterTest, TestCase::Duration::QUICK);
2803 AddTestCase(new SpectrumWifiPhyGetBandTest, TestCase::Duration::QUICK);
2804 AddTestCase(new SpectrumWifiPhyTrackedBandsTest, TestCase::Duration::QUICK);
2805 AddTestCase(new SpectrumWifiPhy80Plus80Test, TestCase::Duration::QUICK);
2807 false,
2809 TestCase::Duration::QUICK);
2811 true,
2813 TestCase::Duration::QUICK);
2815 true,
2817 TestCase::Duration::QUICK);
2818 AddTestCase(new SpectrumWifiPhyInterfacesHelperTest, TestCase::Duration::QUICK);
2819}
2820
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.
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
void NotifyRxEndError() override
We have received the last bit of a packet for which NotifyRxStart was invoked first and,...
uint32_t m_notifyRxEndError
notify receive end error
~TestPhyListener() override=default
AttributeValue implementation for Boolean.
Definition boolean.h:26
static WifiMode GetHeMcs11()
Return MCS 11 from HE MCS values.
static WifiMode GetHeMcs0()
Return MCS 0 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:560
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:594
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.
HeRuBands GetHeRuBands(Ptr< WifiSpectrumPhyInterface > spectrumPhyInterface, MHz_u guardBandwidth)
This function computes the RU bands that belong to a given spectrum PHY interface.
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.
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:1431
a unique identifier for an interface.
Definition type-id.h:48
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:678
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:1807
void SetErrorRateModel(const Ptr< ErrorRateModel > model)
Sets the error rate model.
Definition wifi-phy.cc:687
std::tuple< uint8_t, MHz_u, WifiPhyBand, uint8_t > ChannelTuple
Tuple identifying a segment of an operating channel.
Definition wifi-phy.h:921
virtual void ConfigureStandard(WifiStandard standard)
Configure the PHY-level parameters for different Wi-Fi standard.
Definition wifi-phy.cc:1009
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1587
std::vector< ChannelTuple > ChannelSegments
segments identifying an operating channel
Definition wifi-phy.h:927
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:1135
void SetMobility(const Ptr< MobilityModel > mobility)
assign a mobility model to this device
Definition wifi-phy.cc:653
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:1368
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1380
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1356
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_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:44
std::vector< BandInfo > Bands
Container of BandInfo.
dBm_u WToDbm(Watt_u val)
Convert from Watts to dBm.
Definition wifi-utils.cc:37
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:134
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:109
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)
Definition wifi-mode.h:24
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:72
WifiSpectrumBandInfo structure containing info about a spectrum band.