diff --git a/src/wifi/model/ht/ht-phy.cc b/src/wifi/model/ht/ht-phy.cc index 581bf12e9..b35c1fb38 100644 --- a/src/wifi/model/ht/ht-phy.cc +++ b/src/wifi/model/ht/ht-phy.cc @@ -776,6 +776,47 @@ HtPhy::GetMaxPsduSize (void) const return 65535; } +PhyEntity::CcaIndication +HtPhy::GetCcaIndication (const Ptr ppdu) +{ + NS_LOG_FUNCTION (this); + if (m_wifiPhy->GetChannelWidth () < 40) + { + return PhyEntity::GetCcaIndication (ppdu); + } + double ccaThresholdDbm = GetCcaThreshold (ppdu, WIFI_CHANLIST_PRIMARY); + Time delayUntilCcaEnd = GetDelayUntilCcaEnd (ccaThresholdDbm, GetPrimaryBand (20)); + if (delayUntilCcaEnd.IsStrictlyPositive ()) + { + return std::make_pair (delayUntilCcaEnd, WIFI_CHANLIST_PRIMARY); //if Primary is busy, ignore CCA for Secondary + } + if (ppdu != nullptr) + { + const uint16_t primaryWidth = 20; + uint16_t p20MinFreq = + m_wifiPhy->GetOperatingChannel ().GetPrimaryChannelCenterFrequency (primaryWidth) - (primaryWidth / 2); + uint16_t p20MaxFreq = + m_wifiPhy->GetOperatingChannel ().GetPrimaryChannelCenterFrequency (primaryWidth) + (primaryWidth / 2); + if (ppdu->DoesOverlapChannel (p20MinFreq, p20MaxFreq)) + { + /* + * PPDU occupies primary 20 MHz channel, hence we skip CCA sensitivity rules + * for signals not occupying the primary 20 MHz channel. + */ + return std::nullopt; + } + } + + ccaThresholdDbm = GetCcaThreshold (ppdu, WIFI_CHANLIST_SECONDARY); + delayUntilCcaEnd = GetDelayUntilCcaEnd (ccaThresholdDbm, GetSecondaryBand (20)); + if (delayUntilCcaEnd.IsStrictlyPositive ()) + { + return std::make_pair (delayUntilCcaEnd, WIFI_CHANLIST_SECONDARY); + } + + return std::nullopt; +} + } //namespace ns3 namespace { diff --git a/src/wifi/model/ht/ht-phy.h b/src/wifi/model/ht/ht-phy.h index e2dd05815..e025b1813 100644 --- a/src/wifi/model/ht/ht-phy.h +++ b/src/wifi/model/ht/ht-phy.h @@ -451,6 +451,7 @@ protected: bool IsConfigSupported (Ptr ppdu) const override; Ptr GetTxPowerSpectralDensity (double txPowerW, Ptr ppdu) const override; uint32_t GetMaxPsduSize (void) const override; + CcaIndication GetCcaIndication (const Ptr ppdu) override; /** * Build mode list. diff --git a/src/wifi/model/phy-entity.cc b/src/wifi/model/phy-entity.cc index 32376d171..8926689d7 100644 --- a/src/wifi/model/phy-entity.cc +++ b/src/wifi/model/phy-entity.cc @@ -1062,6 +1062,12 @@ PhyEntity::GetCcaThreshold (const Ptr ppdu, WifiChannelListType return (ppdu == nullptr) ? m_wifiPhy->GetCcaEdThreshold () : m_wifiPhy->GetCcaSensitivityThreshold (); } +Time +PhyEntity::GetDelayUntilCcaEnd (double thresholdDbm, WifiSpectrumBand band) +{ + return m_wifiPhy->m_interference->GetEnergyDuration (DbmToW (thresholdDbm), band); +} + void PhyEntity::SwitchMaybeToCcaBusy (const Ptr ppdu) { @@ -1069,15 +1075,31 @@ PhyEntity::SwitchMaybeToCcaBusy (const Ptr ppdu) //not going to be able to synchronize on it //In this model, CCA becomes busy when the aggregation of all signals as //tracked by the InterferenceHelper class is higher than the CcaBusyThreshold + const auto ccaIndication = GetCcaIndication (ppdu); + if (ccaIndication.has_value ()) + { + NS_LOG_DEBUG ("CCA busy for " << ccaIndication.value ().second << " during " << ccaIndication.value ().first.As (Time::S)); + m_state->SwitchMaybeToCcaBusy (ccaIndication.value ().first, ccaIndication.value ().second, {}); + return; + } + if (ppdu != nullptr) + { + SwitchMaybeToCcaBusy (nullptr); + } +} + +PhyEntity::CcaIndication +PhyEntity::GetCcaIndication (const Ptr ppdu) +{ const uint16_t channelWidth = GetMeasurementChannelWidth (ppdu); NS_LOG_FUNCTION (this << channelWidth); - double ccaThresholdW = DbmToW (GetCcaThreshold (ppdu, WIFI_CHANLIST_PRIMARY)); - const Time delayUntilCcaEnd = m_wifiPhy->m_interference->GetEnergyDuration (ccaThresholdW, GetPrimaryBand (channelWidth)); + const double ccaThresholdDbm = GetCcaThreshold (ppdu, WIFI_CHANLIST_PRIMARY); + const Time delayUntilCcaEnd = GetDelayUntilCcaEnd (ccaThresholdDbm, GetPrimaryBand (channelWidth)); if (delayUntilCcaEnd.IsStrictlyPositive ()) { - NS_LOG_DEBUG ("Calling SwitchMaybeToCcaBusy for " << delayUntilCcaEnd.As (Time::S)); - m_state->SwitchMaybeToCcaBusy (delayUntilCcaEnd); + return std::make_pair (delayUntilCcaEnd, WIFI_CHANLIST_PRIMARY); } + return std::nullopt; } uint64_t diff --git a/src/wifi/model/phy-entity.h b/src/wifi/model/phy-entity.h index b94484a4e..d78a346ab 100644 --- a/src/wifi/model/phy-entity.h +++ b/src/wifi/model/phy-entity.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "wifi-mpdu-type.h" #include "wifi-tx-vector.h" #include "wifi-phy-band.h" @@ -832,6 +833,15 @@ protected: */ virtual uint16_t GetRxChannelWidth (const WifiTxVector& txVector) const; + /** + * Return the delay until CCA busy is ended for a given sensitivity threshold (in dBm) and a given band. + * + * \param thresholdDbm the CCA sensitivity threshold in dBm + * \param band identify the requested band + * \return the delay until CCA busy is ended + */ + Time GetDelayUntilCcaEnd (double thresholdDbm, WifiSpectrumBand band); + /** * \param currentChannelWidth channel width of the current transmission (MHz) * \return the width of the guard band (MHz) @@ -850,6 +860,17 @@ protected: */ std::tuple GetTxMaskRejectionParams (void) const; + using CcaIndication = std::optional>; //!< CCA end time and its corresponding channel list type (can be std::nullopt if IDLE) + + /** + * Get CCA end time and its corresponding channel list type when a new signal has been received by the PHY. + * + * \param ppdu the incoming PPDU or nullptr for any signal + * \return CCA end time and its corresponding channel list type when a new signal has been received by the PHY, + * or std::nullopt if all channel list types are IDLE. + */ + virtual CcaIndication GetCcaIndication (const Ptr ppdu); + Ptr m_wifiPhy; //!< Pointer to the owning WifiPhy Ptr m_state; //!< Pointer to WifiPhyStateHelper of the WifiPhy (to make it reachable for child classes) diff --git a/src/wifi/model/vht/vht-phy.cc b/src/wifi/model/vht/vht-phy.cc index b2e03f99e..d5bf75ce0 100644 --- a/src/wifi/model/vht/vht-phy.cc +++ b/src/wifi/model/vht/vht-phy.cc @@ -70,6 +70,9 @@ const VhtPhy::NesExceptionMap VhtPhy::m_exceptionsMap { { std::make_tuple (160, 7, 9), 12 } //instead of 10 }; +/** + * \brief map a given channel list type to the corresponding scaling factor in dBm + */ const std::map channelTypeToScalingFactorDbm { {WIFI_CHANLIST_PRIMARY, 0.0}, {WIFI_CHANLIST_SECONDARY, 0.0}, @@ -77,6 +80,15 @@ const std::map channelTypeToScalingFactorDbm { {WIFI_CHANLIST_SECONDARY80, 6.0} }; +/** + * \brief map a given secondary channel width to its channel list type + */ +const std::map secondaryChannels { + {20, WIFI_CHANLIST_SECONDARY}, + {40, WIFI_CHANLIST_SECONDARY40}, + {80, WIFI_CHANLIST_SECONDARY80} +}; + /* *NS_CHECK_STYLE_ON* */ VhtPhy::VhtPhy (bool buildModeList /* = true */) @@ -576,6 +588,80 @@ VhtPhy::GetCcaThreshold (const Ptr ppdu, WifiChannelListType cha } } +PhyEntity::CcaIndication +VhtPhy::GetCcaIndication (const Ptr ppdu) +{ + NS_LOG_FUNCTION (this); + + if (m_wifiPhy->GetChannelWidth () < 80) + { + return HtPhy::GetCcaIndication (ppdu); + } + + double ccaThresholdDbm = GetCcaThreshold (ppdu, WIFI_CHANLIST_PRIMARY); + Time delayUntilCcaEnd = GetDelayUntilCcaEnd (ccaThresholdDbm, GetPrimaryBand (20)); + if (delayUntilCcaEnd.IsStrictlyPositive ()) + { + return std::make_pair (delayUntilCcaEnd, WIFI_CHANLIST_PRIMARY); //if Primary is busy, ignore CCA for Secondary + } + + if (ppdu != nullptr) + { + const uint16_t primaryWidth = 20; + uint16_t p20MinFreq = + m_wifiPhy->GetOperatingChannel ().GetPrimaryChannelCenterFrequency (primaryWidth) - (primaryWidth / 2); + uint16_t p20MaxFreq = + m_wifiPhy->GetOperatingChannel ().GetPrimaryChannelCenterFrequency (primaryWidth) + (primaryWidth / 2); + if (ppdu->DoesOverlapChannel (p20MinFreq, p20MaxFreq)) + { + /* + * PPDU occupies primary 20 MHz channel, hence we skip CCA sensitivity rules + * for signals not occupying the primary 20 MHz channel. + */ + return std::nullopt; + } + } + + std::vector secondaryWidthsToCheck; + if (ppdu != nullptr) + { + for (const auto& secondaryChannel : secondaryChannels) + { + uint16_t secondaryWidth = secondaryChannel.first; + uint16_t secondaryMinFreq = + m_wifiPhy->GetOperatingChannel ().GetSecondaryChannelCenterFrequency (secondaryWidth) - (secondaryWidth / 2); + uint16_t secondaryMaxFreq = + m_wifiPhy->GetOperatingChannel ().GetSecondaryChannelCenterFrequency (secondaryWidth) + (secondaryWidth / 2); + if ((m_wifiPhy->GetChannelWidth () > secondaryWidth) && ppdu->DoesOverlapChannel (secondaryMinFreq, secondaryMaxFreq)) + { + secondaryWidthsToCheck.push_back (secondaryWidth); + } + } + } + else + { + secondaryWidthsToCheck.push_back (20); + secondaryWidthsToCheck.push_back (40); + if (m_wifiPhy->GetChannelWidth () > 80) + { + secondaryWidthsToCheck.push_back (80); + } + } + + for (auto secondaryWidth : secondaryWidthsToCheck) + { + auto channelType = secondaryChannels.at (secondaryWidth); + ccaThresholdDbm = GetCcaThreshold (ppdu, channelType); + delayUntilCcaEnd = GetDelayUntilCcaEnd (ccaThresholdDbm, GetSecondaryBand (secondaryWidth)); + if (delayUntilCcaEnd.IsStrictlyPositive ()) + { + return std::make_pair (delayUntilCcaEnd, channelType); + } + } + + return std::nullopt; +} + } //namespace ns3 namespace { diff --git a/src/wifi/model/vht/vht-phy.h b/src/wifi/model/vht/vht-phy.h index b0b0345fa..29ee8fa26 100644 --- a/src/wifi/model/vht/vht-phy.h +++ b/src/wifi/model/vht/vht-phy.h @@ -271,6 +271,7 @@ protected: PhyFieldRxStatus DoEndReceiveField (WifiPpduField field, Ptr event) override; bool IsAllConfigSupported (WifiPpduField field, Ptr ppdu) const override; uint32_t GetMaxPsduSize (void) const override; + CcaIndication GetCcaIndication (const Ptr ppdu) override; /** * End receiving the SIG-A, perform VHT-specific actions, and diff --git a/src/wifi/test/wifi-phy-ofdma-test.cc b/src/wifi/test/wifi-phy-ofdma-test.cc index 2697f0abc..4e9a0829f 100644 --- a/src/wifi/test/wifi-phy-ofdma-test.cc +++ b/src/wifi/test/wifi-phy-ofdma-test.cc @@ -2887,7 +2887,7 @@ TestUlOfdmaPhyTransmission::RunOne (void) Simulator::Schedule (delay + MicroSeconds (50), &TestUlOfdmaPhyTransmission::GenerateInterference, this, interferencePsdRu2, MilliSeconds (100)); ScheduleTest (delay, true, - WifiPhyState::CCA_BUSY, //PHY should move to CCA_BUSY since measurement channel encompasses total channel width + (m_channelWidth >= 40) ? WifiPhyState::IDLE : WifiPhyState::CCA_BUSY, //PHY should move to CCA_BUSY if interference is generated in its primary channel 1, 0, 1000, //One PSDU of 1000 bytes should have been successfully received from STA 1 0, 1, 0); //Reception of the PSDU from STA 2 should have failed (since interference occupies RU 2) delay += Seconds (1.0); @@ -2966,7 +2966,7 @@ TestUlOfdmaPhyTransmission::RunOne (void) bytes = 0; } ScheduleTest (delay, true, - WifiPhyState::CCA_BUSY, //PHY should move to CCA_BUSY instead of IDLE due to the interference on measurement channel width + (m_channelWidth >= 40) ? WifiPhyState::IDLE : WifiPhyState::CCA_BUSY, //PHY should move to CCA_BUSY instead of IDLE if HE TB PPDU on primary channel succ, fail, bytes, 0, 1, 0); //Reception of the PSDU from STA 2 should have failed (since interference from STA 3 on same 20 MHz channel) delay += Seconds (1.0);