diff --git a/src/wifi/test/wifi-phy-ofdma-test.cc b/src/wifi/test/wifi-phy-ofdma-test.cc new file mode 100644 index 000000000..b2c1065c7 --- /dev/null +++ b/src/wifi/test/wifi-phy-ofdma-test.cc @@ -0,0 +1,417 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2019 University of Washington + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Sébastien Deronne + */ + +#include "ns3/log.h" +#include "ns3/test.h" +#include "ns3/node.h" +#include "ns3/pointer.h" +#include "ns3/simulator.h" +#include "ns3/rng-seed-manager.h" +#include "ns3/constant-position-mobility-model.h" +#include "ns3/wifi-spectrum-signal-parameters.h" +#include "ns3/wifi-spectrum-value-helper.h" +#include "ns3/multi-model-spectrum-channel.h" +#include "ns3/spectrum-wifi-phy.h" +#include "ns3/nist-error-rate-model.h" +#include "ns3/wifi-mac-header.h" +#include "ns3/wifi-net-device.h" +#include "ns3/wifi-psdu.h" +#include "ns3/wifi-ppdu.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("WifiPhyOfdmaTest"); + +class OfdmaSpectrumWifiPhy : public SpectrumWifiPhy +{ +public: + /** + * Constructor + * + * \param staId the ID of the STA to which this PHY belongs to + */ + OfdmaSpectrumWifiPhy (uint16_t staId); + virtual ~OfdmaSpectrumWifiPhy (); + +private: + /** + * Return the STA ID that has been assigned to the station this PHY belongs to. + * This is typically called for MU PPDUs, in order to pick the correct PSDU. + * + * \return the STA ID + */ + uint16_t GetStaId (void) const override; + + uint16_t m_staId; ///< ID of the STA to which this PHY belongs to +}; + +OfdmaSpectrumWifiPhy::OfdmaSpectrumWifiPhy (uint16_t staId) + : SpectrumWifiPhy (), + m_staId (staId) +{ +} + +OfdmaSpectrumWifiPhy::~OfdmaSpectrumWifiPhy() +{ +} + +uint16_t +OfdmaSpectrumWifiPhy::GetStaId (void) const +{ + return m_staId; +} + +/** + * \ingroup wifi-test + * \ingroup tests + * + * \brief DL-OFDMA PHY reception test + */ +class TestDlOfdmaReception : public TestCase +{ +public: + TestDlOfdmaReception (); + virtual ~TestDlOfdmaReception (); + + /** + * Receive success function for STA 1 + * \param psdu the PSDU + * \param snr the SNR + * \param txVector the transmit vector + * \param statusPerMpdu reception status per MPDU + */ + void RxSuccessSta1 (Ptr psdu, double snr, WifiTxVector txVector, std::vector statusPerMpdu); + /** + * Receive success function for STA 2 + * \param psdu the PSDU + * \param snr the SNR + * \param txVector the transmit vector + * \param statusPerMpdu reception status per MPDU + */ + void RxSuccessSta2 (Ptr psdu, double snr, WifiTxVector txVector, std::vector statusPerMpdu); + /** + * Receive success function for STA 3 + * \param psdu the PSDU + * \param snr the SNR + * \param txVector the transmit vector + * \param statusPerMpdu reception status per MPDU + */ + void RxSuccessSta3 (Ptr psdu, double snr, WifiTxVector txVector, std::vector statusPerMpdu); + + /** + * Check the results + */ + void CheckResults (); + + uint32_t m_countRxPacketsSta1; ///< count RX packets for STA 1 + uint32_t m_countRxPacketsSta2; ///< count RX packets for STA 2 + uint32_t m_countRxPacketsSta3; ///< count RX packets for STA 3 + uint32_t m_countRxBytesSta1; ///< count RX bytes for STA 1 + uint32_t m_countRxBytesSta2; ///< count RX bytes for STA 2 + uint32_t m_countRxBytesSta3; ///< count RX bytes for STA 3 + +protected: + virtual void DoSetup (void); + Ptr m_phyAp; ///< PHY of AP + Ptr m_phySta1; ///< PHY of STA 1 + Ptr m_phySta2; ///< PHY of STA 2 + Ptr m_phySta3; ///< PHY of STA 3 + /** + * Send MU-PPDU function + * \param rxStaId1 the ID of the recipient STA for the first PSDU + * \param rxStaId2 the ID of the recipient STA for the second PSDU + */ + void SendMuPpdu (uint16_t rxStaId1, uint16_t rxStaId2); + +private: + virtual void DoRun (void); + + /** + * Schedule now to check the PHY state + * \param phy the PHY + * \param expectedState the expected state of the PHY + */ + void CheckPhyState (Ptr phy, WifiPhyState expectedState); + /** + * Check the PHY state now + * \param phy the PHY + * \param expectedState the expected state of the PHY + */ + void DoCheckPhyState (Ptr phy, WifiPhyState expectedState); +}; + +TestDlOfdmaReception::TestDlOfdmaReception () + : TestCase ("DL-OFDMA PHY reception test"), + m_countRxPacketsSta1 (0), + m_countRxPacketsSta2 (0), + m_countRxPacketsSta3 (0), + m_countRxBytesSta1 (0), + m_countRxBytesSta2 (0), + m_countRxBytesSta3 (0) +{ +} + +void +TestDlOfdmaReception::SendMuPpdu (uint16_t rxStaId1, uint16_t rxStaId2) +{ + NS_LOG_FUNCTION (this << rxStaId1 << rxStaId2); + WifiConstPsduMap psdus; + WifiTxVector txVector = WifiTxVector (WifiPhy::GetHeMcs7 (), 0, WIFI_PREAMBLE_HE_MU, 800, 1, 1, 0, 20, false, false); + + HeRu::RuSpec ru1; + ru1.primary80MHz = false; + ru1.ruType = HeRu::RU_106_TONE; + ru1.index = 1; + txVector.SetRu (ru1, rxStaId1); + txVector.SetMode (WifiPhy::GetHeMcs7 (), rxStaId1); + txVector.SetNss (1, rxStaId1); + + HeRu::RuSpec ru2; + ru2.primary80MHz = false; + ru2.ruType = HeRu::RU_106_TONE; + ru2.index = 2; + txVector.SetRu (ru2, rxStaId2); + txVector.SetMode (WifiPhy::GetHeMcs9 (), rxStaId2); + txVector.SetNss (1, rxStaId2); + + Ptr pkt1 = Create (1000); + WifiMacHeader hdr1; + hdr1.SetType (WIFI_MAC_QOSDATA); + hdr1.SetQosTid (0); + hdr1.SetAddr1 (Mac48Address ("00:00:00:00:00:01")); + hdr1.SetSequenceNumber (1); + Ptr psdu1 = Create (pkt1, hdr1); + psdus.insert (std::make_pair (rxStaId1, psdu1)); + + Ptr pkt2 = Create (1500); + WifiMacHeader hdr2; + hdr2.SetType (WIFI_MAC_QOSDATA); + hdr2.SetQosTid (0); + hdr2.SetAddr1 (Mac48Address ("00:00:00:00:00:02")); + hdr2.SetSequenceNumber (2); + Ptr psdu2 = Create (pkt2, hdr2); + psdus.insert (std::make_pair (rxStaId2, psdu2)); + + m_phyAp->Send (psdus, txVector); +} + +TestDlOfdmaReception::~TestDlOfdmaReception () +{ + m_phyAp = 0; + m_phySta1 = 0; + m_phySta2 = 0; + m_phySta3 = 0; +} + +void +TestDlOfdmaReception::RxSuccessSta1 (Ptr psdu, double snr, WifiTxVector txVector, std::vector /*statusPerMpdu*/) +{ + NS_LOG_FUNCTION (this << *psdu << snr << txVector); + m_countRxPacketsSta1++; + m_countRxBytesSta1 += (psdu->GetSize () - 30); +} + +void +TestDlOfdmaReception::RxSuccessSta2 (Ptr psdu, double snr, WifiTxVector txVector, std::vector /*statusPerMpdu*/) +{ + NS_LOG_FUNCTION (this << *psdu << snr << txVector); + m_countRxPacketsSta2++; + m_countRxBytesSta2 += (psdu->GetSize () - 30); +} + +void +TestDlOfdmaReception::RxSuccessSta3 (Ptr psdu, double snr, WifiTxVector txVector, std::vector /*statusPerMpdu*/) +{ + NS_LOG_FUNCTION (this << *psdu << snr << txVector); + m_countRxPacketsSta3++; + m_countRxBytesSta3 += (psdu->GetSize () - 30); +} + +void +TestDlOfdmaReception::CheckResults () +{ + NS_TEST_ASSERT_MSG_EQ (m_countRxPacketsSta1, 2, "The number of packets received by STA1 is not correct!"); + NS_TEST_ASSERT_MSG_EQ (m_countRxPacketsSta2, 1, "The number of packets received by STA2 is not correct!"); + NS_TEST_ASSERT_MSG_EQ (m_countRxPacketsSta3, 1, "The number of packets received by STA3 is not correct!"); + NS_TEST_ASSERT_MSG_EQ (m_countRxBytesSta1, 2000, "The number of bytes received by STA1 is not correct!"); + NS_TEST_ASSERT_MSG_EQ (m_countRxBytesSta2, 1500, "The number of bytes received by STA2 is not correct!"); + NS_TEST_ASSERT_MSG_EQ (m_countRxBytesSta3, 1500, "The number of bytes received by STA3 is not correct!"); +} + +void +TestDlOfdmaReception::CheckPhyState (Ptr phy, WifiPhyState expectedState) +{ + //This is needed to make sure PHY state will be checked as the last event if a state change occured at the exact same time as the check + Simulator::ScheduleNow (&TestDlOfdmaReception::DoCheckPhyState, this, phy, expectedState); +} + +void +TestDlOfdmaReception::DoCheckPhyState (Ptr phy, WifiPhyState expectedState) +{ + WifiPhyState currentState; + PointerValue ptr; + phy->GetAttribute ("State", ptr); + Ptr state = DynamicCast (ptr.Get ()); + currentState = state->GetState (); + NS_LOG_FUNCTION (this << currentState); + NS_TEST_ASSERT_MSG_EQ (currentState, expectedState, "PHY State " << currentState << " does not match expected state " << expectedState << " at " << Simulator::Now ()); +} + +void +TestDlOfdmaReception::DoSetup (void) +{ + uint32_t frequency = 5180; // MHz + + Ptr spectrumChannel = CreateObject (); + Ptr lossModel = CreateObject (); + lossModel->SetFrequency (frequency * 1e6); + spectrumChannel->AddPropagationLossModel (lossModel); + Ptr delayModel = CreateObject (); + spectrumChannel->SetPropagationDelayModel (delayModel); + + Ptr apNode = CreateObject (); + Ptr apDev = CreateObject (); + m_phyAp = CreateObject (); + m_phyAp->CreateWifiSpectrumPhyInterface (apDev); + m_phyAp->ConfigureStandardAndBand (WIFI_PHY_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ); + Ptr error = CreateObject (); + m_phyAp->SetErrorRateModel (error); + m_phyAp->SetFrequency (frequency); + m_phyAp->SetDevice (apDev); + m_phyAp->SetChannel (spectrumChannel); + Ptr apMobility = CreateObject (); + m_phyAp->SetMobility (apMobility); + apDev->SetPhy (m_phyAp); + apNode->AggregateObject (apMobility); + apNode->AddDevice (apDev); + + Ptr sta1Node = CreateObject (); + Ptr sta1Dev = CreateObject (); + m_phySta1 = CreateObject (1); + m_phySta1->CreateWifiSpectrumPhyInterface (sta1Dev); + m_phySta1->ConfigureStandardAndBand (WIFI_PHY_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ); + m_phySta1->SetErrorRateModel (error); + m_phySta1->SetFrequency (frequency); + m_phySta1->SetDevice (sta1Dev); + m_phySta1->SetChannel (spectrumChannel); + m_phySta1->SetReceiveOkCallback (MakeCallback (&TestDlOfdmaReception::RxSuccessSta1, this)); + Ptr sta1Mobility = CreateObject (); + m_phySta1->SetMobility (sta1Mobility); + sta1Dev->SetPhy (m_phySta1); + sta1Node->AggregateObject (sta1Mobility); + sta1Node->AddDevice (sta1Dev); + + Ptr sta2Node = CreateObject (); + Ptr sta2Dev = CreateObject (); + m_phySta2 = CreateObject (2); + m_phySta2->CreateWifiSpectrumPhyInterface (sta2Dev); + m_phySta2->ConfigureStandardAndBand (WIFI_PHY_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ); + m_phySta2->SetErrorRateModel (error); + m_phySta2->SetFrequency (frequency); + m_phySta2->SetDevice (sta2Dev); + m_phySta2->SetChannel (spectrumChannel); + m_phySta2->SetReceiveOkCallback (MakeCallback (&TestDlOfdmaReception::RxSuccessSta2, this)); + Ptr sta2Mobility = CreateObject (); + m_phySta2->SetMobility (sta2Mobility); + sta2Dev->SetPhy (m_phySta2); + sta2Node->AggregateObject (sta2Mobility); + sta2Node->AddDevice (sta2Dev); + + Ptr sta3Node = CreateObject (); + Ptr sta3Dev = CreateObject (); + m_phySta3 = CreateObject (3); + m_phySta3->CreateWifiSpectrumPhyInterface (sta3Dev); + m_phySta3->ConfigureStandardAndBand (WIFI_PHY_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ); + m_phySta3->SetErrorRateModel (error); + m_phySta3->SetFrequency (frequency); + m_phySta3->SetDevice (sta3Dev); + m_phySta3->SetChannel (spectrumChannel); + m_phySta3->SetReceiveOkCallback (MakeCallback (&TestDlOfdmaReception::RxSuccessSta3, this)); + Ptr sta3Mobility = CreateObject (); + m_phySta3->SetMobility (sta3Mobility); + sta3Dev->SetPhy (m_phySta3); + sta3Node->AggregateObject (sta3Mobility); + sta3Node->AddDevice (sta3Dev); +} + +void +TestDlOfdmaReception::DoRun (void) +{ + RngSeedManager::SetSeed (1); + RngSeedManager::SetRun (1); + int64_t streamNumber = 0; + m_phyAp->AssignStreams (streamNumber); + m_phySta1->AssignStreams (streamNumber); + m_phySta2->AssignStreams (streamNumber); + m_phySta3->AssignStreams (streamNumber); + + //Send MU PPDU with two PSDUs addressed to STA 1 and STA 2: + //Each STA should receive its PSDU. + Simulator::Schedule (Seconds (1.0), &TestDlOfdmaReception::SendMuPpdu, this, 1, 2); + + //Since it takes 306.4us to transmit the largest packet, + //all 3 PHYs should be back to IDLE at the same time time 306.4us + Simulator::Schedule (Seconds (1.0) + NanoSeconds (306399), &TestDlOfdmaReception::CheckPhyState, this, m_phySta1, WifiPhyState::RX); + Simulator::Schedule (Seconds (1.0) + NanoSeconds (306399), &TestDlOfdmaReception::CheckPhyState, this, m_phySta2, WifiPhyState::RX); + Simulator::Schedule (Seconds (1.0) + NanoSeconds (306399), &TestDlOfdmaReception::CheckPhyState, this, m_phySta3, WifiPhyState::CCA_BUSY); + Simulator::Schedule (Seconds (1.0) + NanoSeconds (306400), &TestDlOfdmaReception::CheckPhyState, this, m_phySta1, WifiPhyState::IDLE); + Simulator::Schedule (Seconds (1.0) + NanoSeconds (306400), &TestDlOfdmaReception::CheckPhyState, this, m_phySta2, WifiPhyState::IDLE); + Simulator::Schedule (Seconds (1.0) + NanoSeconds (306400), &TestDlOfdmaReception::CheckPhyState, this, m_phySta3, WifiPhyState::IDLE); + + //Send MU PPDU with two PSDUs addressed to STA 1 and STA 3: + //STA 1 should receive its PSDU, whereas STA 2 should not receive any PSDU + //but should keep its PHY busy during all PPDU duration. + Simulator::Schedule (Seconds (2.0), &TestDlOfdmaReception::SendMuPpdu, this, 1, 3); + + //Since it takes 306.4us to transmit the largest packet, + //all 3 PHYs should be back to IDLE at the same time time 306.4us, + //even the PHY that has no PSDU addressed to it. + Simulator::Schedule (Seconds (2.0) + NanoSeconds (306399), &TestDlOfdmaReception::CheckPhyState, this, m_phySta1, WifiPhyState::RX); + Simulator::Schedule (Seconds (2.0) + NanoSeconds (306399), &TestDlOfdmaReception::CheckPhyState, this, m_phySta2, WifiPhyState::CCA_BUSY); + Simulator::Schedule (Seconds (2.0) + NanoSeconds (306399), &TestDlOfdmaReception::CheckPhyState, this, m_phySta3, WifiPhyState::RX); + Simulator::Schedule (Seconds (2.0) + NanoSeconds (306400), &TestDlOfdmaReception::CheckPhyState, this, m_phySta1, WifiPhyState::IDLE); + Simulator::Schedule (Seconds (2.0) + NanoSeconds (306400), &TestDlOfdmaReception::CheckPhyState, this, m_phySta2, WifiPhyState::IDLE); + Simulator::Schedule (Seconds (2.0) + NanoSeconds (306400), &TestDlOfdmaReception::CheckPhyState, this, m_phySta3, WifiPhyState::IDLE); + + Simulator::Run (); + Simulator::Destroy (); + + CheckResults (); +} + +/** + * \ingroup wifi-test + * \ingroup tests + * + * \brief wifi PHY OFDMA Test Suite + */ +class WifiPhyOfdmaTestSuite : public TestSuite +{ +public: + WifiPhyOfdmaTestSuite (); +}; + +WifiPhyOfdmaTestSuite::WifiPhyOfdmaTestSuite () + : TestSuite ("wifi-phy-ofdma", UNIT) +{ + AddTestCase (new TestDlOfdmaReception, TestCase::QUICK); +} + +static WifiPhyOfdmaTestSuite wifiPhyOfdmaTestSuite; ///< the test suite diff --git a/src/wifi/wscript b/src/wifi/wscript index bd681ca7e..069b5d95f 100644 --- a/src/wifi/wscript +++ b/src/wifi/wscript @@ -124,6 +124,7 @@ def build(bld): 'test/wifi-phy-thresholds-test.cc', 'test/wifi-phy-reception-test.cc', 'test/inter-bss-test-suite.cc', + 'test/wifi-phy-ofdma-test.cc' ] # Tests encapsulating example programs should be listed here