diff --git a/src/wifi/model/channel-access-manager.cc b/src/wifi/model/channel-access-manager.cc index 70443a728..6afcc8456 100644 --- a/src/wifi/model/channel-access-manager.cc +++ b/src/wifi/model/channel-access-manager.cc @@ -627,6 +627,30 @@ ChannelAccessManager::GetLargestIdlePrimaryChannel(Time interval, Time end) return width; } +bool +ChannelAccessManager::GetPer20MHzBusy(const std::set& indices) const +{ + const auto now = Simulator::Now(); + + if (m_phy->GetChannelWidth() < 40) + { + NS_ASSERT_MSG(indices.size() == 1 && *indices.cbegin() == 0, + "Index 0 only can be specified if the channel width is less than 40 MHz"); + return m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now; + } + + for (const auto index : indices) + { + NS_ASSERT(index < m_lastPer20MHzBusyEnd.size()); + if (m_lastPer20MHzBusyEnd.at(index) > now) + { + NS_LOG_DEBUG("20 MHz channel with index " << +index << " is busy"); + return true; + } + } + return false; +} + void ChannelAccessManager::DisableEdcaFor(Ptr qosTxop, Time duration) { diff --git a/src/wifi/model/channel-access-manager.h b/src/wifi/model/channel-access-manager.h index 3addc8ba9..a4fc067a2 100644 --- a/src/wifi/model/channel-access-manager.h +++ b/src/wifi/model/channel-access-manager.h @@ -149,6 +149,13 @@ class ChannelAccessManager : public Object */ uint16_t GetLargestIdlePrimaryChannel(Time interval, Time end); + /** + * \param indices a set of indices (starting at 0) specifying the 20 MHz channels to test + * \return true if per-20 MHz CCA indicates busy for at least one of the + * specified 20 MHz channels, false otherwise + */ + bool GetPer20MHzBusy(const std::set& indices) const; + /** * \param duration expected duration of reception * diff --git a/src/wifi/model/he/he-frame-exchange-manager.cc b/src/wifi/model/he/he-frame-exchange-manager.cc index 51441e413..d97bd724c 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.cc +++ b/src/wifi/model/he/he-frame-exchange-manager.cc @@ -1752,6 +1752,38 @@ HeFrameExchangeManager::VirtualCsMediumIdle() const return m_navEnd <= Simulator::Now() && m_intraBssNavEnd <= Simulator::Now(); } +bool +HeFrameExchangeManager::UlMuCsMediumIdle(const CtrlTriggerHeader& trigger) const +{ + if (!trigger.GetCsRequired()) + { + NS_LOG_DEBUG("CS not required"); + return true; + } + + // A non-AP STA does not consider the intra-BSS NAV in determining whether to respond to a + // Trigger frame sent by the AP with which the non-AP STA is associated. + // A non-AP STA considers the basic NAV in determining whether to respond to a Trigger frame + // sent by the AP with which the non-AP STA is associated. (Sec. 26.5.2.5 of 802.11ax-2021) + const Time now = Simulator::Now(); + if (m_navEnd > now) + { + NS_LOG_DEBUG("Basic NAV indicates medium busy"); + return false; + } + + NS_ASSERT_MSG(m_staMac, "UL MU CS is only performed by non-AP STAs"); + const auto userInfoIt = trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()); + NS_ASSERT_MSG(userInfoIt != trigger.end(), + "No User Info field for STA (" << m_self + << ") AID=" << m_staMac->GetAssociationId()); + + const auto indices = + m_phy->GetOperatingChannel().Get20MHzIndicesCoveringRu(userInfoIt->GetRuAllocation(), + trigger.GetUlBandwidth()); + return !m_channelAccessManager->GetPer20MHzBusy(indices); +} + void HeFrameExchangeManager::ReceiveMpdu(Ptr mpdu, RxSignalInfo rxSignalInfo, diff --git a/src/wifi/model/he/he-frame-exchange-manager.h b/src/wifi/model/he/he-frame-exchange-manager.h index 43841a7c3..2ad54f529 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.h +++ b/src/wifi/model/he/he-frame-exchange-manager.h @@ -116,6 +116,16 @@ class HeFrameExchangeManager : public VhtFrameExchangeManager */ bool IsIntraBssPpdu(Ptr psdu, const WifiTxVector& txVector) const; + /** + * This method is intended to be called a SIFS after the reception of a Trigger Frame + * to determine whether the station is allowed to respond. + * + * \param trigger the Trigger Frame soliciting a response + * \return true if CS is not required or the UL MU CS mechanism indicates that the medium + * is idle, false otherwise + */ + bool UlMuCsMediumIdle(const CtrlTriggerHeader& trigger) const; + protected: void DoDispose() override; void Reset() override;