diff --git a/src/wifi/model/wifi-default-ack-manager.cc b/src/wifi/model/wifi-default-ack-manager.cc index fa770353c..0019b6b83 100644 --- a/src/wifi/model/wifi-default-ack-manager.cc +++ b/src/wifi/model/wifi-default-ack-manager.cc @@ -25,9 +25,9 @@ #include "qos-utils.h" #include "wifi-mac-queue.h" #include "wifi-protection.h" -#include "regular-wifi-mac.h" +#include "ap-wifi-mac.h" #include "ctrl-headers.h" -#include "wifi-phy.h" +#include "ns3/he-phy.h" namespace ns3 { @@ -58,6 +58,20 @@ WifiDefaultAckManager::GetTypeId (void) DoubleValue (0.0), MakeDoubleAccessor (&WifiDefaultAckManager::m_baThreshold), MakeDoubleChecker (0.0, 1.0)) + .AddAttribute ("DlMuAckSequenceType", + "Type of the acknowledgment sequence for DL MU PPDUs.", + EnumValue (WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE), + MakeEnumAccessor (&WifiDefaultAckManager::m_dlMuAckType), + MakeEnumChecker (WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE, "DL_MU_BAR_BA_SEQUENCE", + WifiAcknowledgment::DL_MU_TF_MU_BAR, "DL_MU_TF_MU_BAR", + WifiAcknowledgment::DL_MU_AGGREGATE_TF, "DL_MU_AGGREGATE_TF")) + .AddAttribute ("MaxBlockAckMcs", + "The MCS used to send a BlockAck in a TB PPDU is the minimum between " + "the MCS used for the PSDU sent in the preceding DL MU PPDU and the " + "value of this attribute.", + UintegerValue (5), + MakeUintegerAccessor (&WifiDefaultAckManager::m_maxMcsForBlockAckInTbPpdu), + MakeUintegerChecker (0, 11)) ; return tid; } @@ -154,6 +168,23 @@ WifiDefaultAckManager::TryAddMpdu (Ptr mpdu, { NS_LOG_FUNCTION (this << *mpdu << &txParams); + // If the TXVECTOR indicates a DL MU PPDU, call a separate method + if (txParams.m_txVector.IsDlMu ()) + { + switch (m_dlMuAckType) + { + case WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE: + return GetAckInfoIfBarBaSequence (mpdu, txParams); + case WifiAcknowledgment::DL_MU_TF_MU_BAR: + return GetAckInfoIfTfMuBar (mpdu, txParams); + case WifiAcknowledgment::DL_MU_AGGREGATE_TF: + return GetAckInfoIfAggregatedMuBar (mpdu, txParams); + default: + NS_ABORT_MSG ("Unknown DL acknowledgment method"); + return nullptr; + } + } + const WifiMacHeader& hdr = mpdu->GetHeader (); Mac48Address receiver = hdr.GetAddr1 (); @@ -264,4 +295,300 @@ WifiDefaultAckManager::TryAggregateMsdu (Ptr msdu, return nullptr; } +std::unique_ptr +WifiDefaultAckManager::GetAckInfoIfBarBaSequence (Ptr mpdu, + const WifiTxParameters& txParams) +{ + NS_LOG_FUNCTION (this << *mpdu << &txParams); + NS_ASSERT (txParams.m_txVector.IsDlMu ()); + NS_ASSERT (m_dlMuAckType == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE); + + const WifiMacHeader& hdr = mpdu->GetHeader (); + Mac48Address receiver = hdr.GetAddr1 (); + + const WifiTxParameters::PsduInfo* psduInfo = txParams.GetPsduInfo (receiver); + + NS_ABORT_MSG_IF (!hdr.IsQosData (), + "QoS data frames only can be aggregated when transmitting a " + "DL MU PPDU acknowledged via a sequence of BAR and BA frames"); + uint8_t tid = hdr.GetQosTid (); + Ptr edca = m_mac->GetQosTxop (QosUtilsMapTidToAc (tid)); + + NS_ASSERT (!txParams.m_acknowledgment + || txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE); + + WifiDlMuBarBaSequence* acknowledgment = nullptr; + if (txParams.m_acknowledgment) + { + acknowledgment = static_cast (txParams.m_acknowledgment.get ()); + } + + if (psduInfo != nullptr) + { + // an MPDU addressed to the same receiver has been already added + NS_ASSERT (acknowledgment != nullptr); + + if ((acknowledgment->stationsSendBlockAckReqTo.find (receiver) + != acknowledgment->stationsSendBlockAckReqTo.end ()) + || (acknowledgment->stationsReplyingWithBlockAck.find (receiver) + != acknowledgment->stationsReplyingWithBlockAck.end ())) + { + // the receiver either is already listed among the stations that will + // receive a BlockAckReq frame or is the station that will immediately + // respond with a BlockAck frame, hence no change is needed + return nullptr; + } + + // the receiver was scheduled for responding immediately with a Normal Ack. + // Given that we are adding an MPDU, the receiver must be scheduled for + // responding immediately with a Block Ack + NS_ASSERT (acknowledgment->stationsReplyingWithNormalAck.size () == 1 + && acknowledgment->stationsReplyingWithNormalAck.begin ()->first == receiver); + + + // acknowledgment points to the m_acknowledgment member of txParams, which is + // passed as const reference because it must not be modified. Therefore, we + // make a copy of the object pointed to by acknowledgment and make changes to + // the copy + acknowledgment = new WifiDlMuBarBaSequence (*acknowledgment); + acknowledgment->stationsReplyingWithNormalAck.clear (); + + acknowledgment->stationsReplyingWithBlockAck.emplace + (receiver, + WifiDlMuBarBaSequence::BlockAckInfo + { + m_mac->GetWifiRemoteStationManager ()->GetBlockAckTxVector (receiver, txParams.m_txVector), + edca->GetBlockAckType (receiver, tid) + }); + return std::unique_ptr (acknowledgment); + } + + // we get here if this is the first MPDU for this receiver + if (edca->GetBaManager ()->GetBar (true, tid, receiver) != 0 + || (acknowledgment != nullptr + && (!acknowledgment->stationsReplyingWithNormalAck.empty () + || !acknowledgment->stationsReplyingWithBlockAck.empty ()))) + { + // there is a pending BlockAckReq for this receiver or another receiver + // was selected for immediate response. + // Add this receiver to the list of stations receiving a BlockAckReq. + if (acknowledgment != nullptr) + { + // txParams.m_acknowledgment points to an existing WifiDlMuBarBaSequence object. + // We have to return a copy of this object including the needed changes + acknowledgment = new WifiDlMuBarBaSequence (*acknowledgment); + } + else + { + // we have to create a new WifiDlMuBarBaSequence object + acknowledgment = new WifiDlMuBarBaSequence; + } + + NS_LOG_DEBUG ("Adding STA " << receiver << " to the list of stations receiving a BlockAckReq"); + acknowledgment->stationsSendBlockAckReqTo.emplace + (receiver, + WifiDlMuBarBaSequence::BlockAckReqInfo + { + m_mac->GetWifiRemoteStationManager ()->GetBlockAckTxVector (receiver, txParams.m_txVector), + edca->GetBlockAckReqType (receiver, tid), + m_mac->GetWifiRemoteStationManager ()->GetBlockAckTxVector (receiver, txParams.m_txVector), + edca->GetBlockAckType (receiver, tid) + }); + + acknowledgment->SetQosAckPolicy (receiver, tid, WifiMacHeader::BLOCK_ACK); + return std::unique_ptr (acknowledgment); + } + + // Add the receiver as the station that will immediately reply with a Normal Ack + if (acknowledgment != nullptr) + { + // txParams.m_acknowledgment points to an existing WifiDlMuBarBaSequence object. + // We have to return a copy of this object including the needed changes + acknowledgment = new WifiDlMuBarBaSequence (*acknowledgment); + } + else + { + // we have to create a new WifiDlMuBarBaSequence object + acknowledgment = new WifiDlMuBarBaSequence; + } + + NS_LOG_DEBUG ("Adding STA " << receiver << " as the station that will immediately reply with a Normal Ack"); + acknowledgment->stationsReplyingWithNormalAck.emplace + (receiver, + WifiDlMuBarBaSequence::AckInfo + { + m_mac->GetWifiRemoteStationManager ()->GetAckTxVector (receiver, txParams.m_txVector) + }); + + acknowledgment->SetQosAckPolicy (receiver, tid, WifiMacHeader::NORMAL_ACK); + return std::unique_ptr (acknowledgment); +} + +std::unique_ptr +WifiDefaultAckManager::GetAckInfoIfTfMuBar (Ptr mpdu, + const WifiTxParameters& txParams) +{ + NS_LOG_FUNCTION (this << *mpdu << &txParams); + NS_ASSERT (txParams.m_txVector.IsDlMu ()); + NS_ASSERT (m_dlMuAckType == WifiAcknowledgment::DL_MU_TF_MU_BAR); + + const WifiMacHeader& hdr = mpdu->GetHeader (); + Mac48Address receiver = hdr.GetAddr1 (); + + const WifiTxParameters::PsduInfo* psduInfo = txParams.GetPsduInfo (receiver); + + NS_ASSERT (!txParams.m_acknowledgment + || txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR); + + WifiDlMuTfMuBar* acknowledgment = nullptr; + if (txParams.m_acknowledgment) + { + acknowledgment = static_cast (txParams.m_acknowledgment.get ()); + } + + if (psduInfo == nullptr) + { + // we get here if this is the first MPDU for this receiver. + Ptr apMac = DynamicCast (m_mac); + NS_ABORT_MSG_IF (apMac == 0, "HE APs only can send DL MU PPDUs"); + uint16_t staId = apMac->GetAssociationId (receiver); + + NS_ABORT_MSG_IF (!hdr.IsQosData (), + "QoS data frames only can be aggregated when transmitting a " + "DL MU PPDU acknowledged via a MU-BAR sent as SU frame"); + uint8_t tid = hdr.GetQosTid (); + + // Add the receiver to the list of stations that will reply with a Block Ack + if (acknowledgment != nullptr) + { + // txParams.m_acknowledgment points to an existing WifiDlMuTfMuBar object. + // We have to return a copy of this object including the needed changes + acknowledgment = new WifiDlMuTfMuBar (*acknowledgment); + } + else + { + // we have to create a new WifiDlMuTfMuBar object + acknowledgment = new WifiDlMuTfMuBar; + } + + // determine the TX vector used to send the BlockAck frame + WifiTxVector blockAckTxVector; + blockAckTxVector.SetPreambleType (WifiPreamble::WIFI_PREAMBLE_HE_TB); + blockAckTxVector.SetChannelWidth (txParams.m_txVector.GetChannelWidth ()); + blockAckTxVector.SetGuardInterval (txParams.m_txVector.GetGuardInterval ()); + const auto& userInfo = txParams.m_txVector.GetHeMuUserInfo (staId); + blockAckTxVector.SetHeMuUserInfo (staId, {userInfo.ru, + HePhy::GetHeMcs (std::min (userInfo.mcs.GetMcsValue (), + m_maxMcsForBlockAckInTbPpdu)), + userInfo.nss}); + + NS_LOG_DEBUG ("Adding STA " << receiver << " to the list of stations that will be solicited by the MU-BAR"); + Ptr edca = m_mac->GetQosTxop (QosUtilsMapTidToAc (tid)); + acknowledgment->stationsReplyingWithBlockAck.emplace + (receiver, + WifiDlMuTfMuBar::BlockAckInfo + { + edca->GetBaManager ()->GetBlockAckReqHeader (receiver, tid), + blockAckTxVector, + edca->GetBlockAckType (receiver, tid) + }); + + acknowledgment->barTypes.push_back (edca->GetBlockAckReqType (receiver, tid)); + acknowledgment->muBarTxVector = m_mac->GetWifiRemoteStationManager ()->GetRtsTxVector (receiver); + acknowledgment->SetQosAckPolicy (receiver, tid, WifiMacHeader::BLOCK_ACK); + return std::unique_ptr (acknowledgment); + } + + // an MPDU addressed to the same receiver has been already added + NS_ASSERT (acknowledgment != nullptr); + NS_ABORT_MSG_IF (!hdr.IsQosData (), + "QoS data frames only can be aggregated when transmitting a DL MU PPDU"); + + // no change is needed + return nullptr; +} + +std::unique_ptr +WifiDefaultAckManager::GetAckInfoIfAggregatedMuBar (Ptr mpdu, + const WifiTxParameters& txParams) +{ + NS_LOG_FUNCTION (this << *mpdu << &txParams); + NS_ASSERT (txParams.m_txVector.IsDlMu ()); + NS_ASSERT (m_dlMuAckType == WifiAcknowledgment::DL_MU_AGGREGATE_TF); + + const WifiMacHeader& hdr = mpdu->GetHeader (); + Mac48Address receiver = hdr.GetAddr1 (); + + const WifiTxParameters::PsduInfo* psduInfo = txParams.GetPsduInfo (receiver); + + NS_ASSERT (!txParams.m_acknowledgment + || txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF); + + WifiDlMuAggregateTf* acknowledgment = nullptr; + if (txParams.m_acknowledgment) + { + acknowledgment = static_cast (txParams.m_acknowledgment.get ()); + } + + if (psduInfo == nullptr) + { + // we get here if this is the first MPDU for this receiver. + Ptr apMac = DynamicCast (m_mac); + NS_ABORT_MSG_IF (apMac == 0, "HE APs only can send DL MU PPDUs"); + uint16_t staId = apMac->GetAssociationId (receiver); + + NS_ABORT_MSG_IF (!hdr.IsQosData (), + "QoS data frames only can be aggregated when transmitting a " + "DL MU PPDU acknowledged via a sequence of BAR and BA frames"); + uint8_t tid = hdr.GetQosTid (); + + // Add the receiver to the list of stations that will reply with a Block Ack + if (acknowledgment != nullptr) + { + // txParams.m_acknowledgment points to an existing WifiDlMuAggregateTf object. + // We have to return a copy of this object including the needed changes + acknowledgment = new WifiDlMuAggregateTf (*acknowledgment); + } + else + { + // we have to create a new WifiDlMuAggregateTf object + acknowledgment = new WifiDlMuAggregateTf; + } + + // determine the TX vector used to send the BlockAck frame + WifiTxVector blockAckTxVector; + blockAckTxVector.SetPreambleType (WifiPreamble::WIFI_PREAMBLE_HE_TB); + blockAckTxVector.SetChannelWidth (txParams.m_txVector.GetChannelWidth ()); + blockAckTxVector.SetGuardInterval (txParams.m_txVector.GetGuardInterval ()); + const auto& userInfo = txParams.m_txVector.GetHeMuUserInfo (staId); + blockAckTxVector.SetHeMuUserInfo (staId, {userInfo.ru, + HePhy::GetHeMcs (std::min (userInfo.mcs.GetMcsValue (), + m_maxMcsForBlockAckInTbPpdu)), + userInfo.nss}); + + NS_LOG_DEBUG ("Adding STA " << receiver << " to the list of stations that will reply with a Block Ack"); + Ptr edca = m_mac->GetQosTxop (QosUtilsMapTidToAc (tid)); + acknowledgment->stationsReplyingWithBlockAck.emplace + (receiver, + WifiDlMuAggregateTf::BlockAckInfo + { + GetMuBarSize ({edca->GetBlockAckReqType (receiver, tid)}), + edca->GetBaManager ()->GetBlockAckReqHeader (receiver, tid), + blockAckTxVector, + edca->GetBlockAckType (receiver, tid) + }); + + acknowledgment->SetQosAckPolicy (receiver, tid, WifiMacHeader::NO_EXPLICIT_ACK); + return std::unique_ptr (acknowledgment); + } + + // an MPDU addressed to the same receiver has been already added + NS_ASSERT (acknowledgment != nullptr); + NS_ABORT_MSG_IF (!hdr.IsQosData (), + "QoS data and MU-BAR Trigger frames only can be aggregated when transmitting a DL MU PPDU"); + + // no change is needed + return nullptr; +} + } //namespace ns3 diff --git a/src/wifi/model/wifi-default-ack-manager.h b/src/wifi/model/wifi-default-ack-manager.h index f366c5221..774f2b758 100644 --- a/src/wifi/model/wifi-default-ack-manager.h +++ b/src/wifi/model/wifi-default-ack-manager.h @@ -81,8 +81,57 @@ protected: bool IsResponseNeeded (Ptr mpdu, const WifiTxParameters& txParams) const; private: + /** + * Compute the information about the acknowledgment of the current multi-user frame + * (as described by the given TX parameters) if the given MPDU is added. If the + * computed information is the same as the current one, a null pointer is returned. + * Otherwise, the computed information is returned. + * This method can only be called if the selected acknowledgment method for DL + * multi-user frames consists of a sequence of BlockAckReq and BlockAck frames. + * + * \param mpdu the given MPDU + * \param txParams the TX parameters describing the current multi-user frame + * \return the new acknowledgment method or a null pointer if the acknowledgment method + * is unchanged + */ + virtual std::unique_ptr GetAckInfoIfBarBaSequence (Ptr mpdu, + const WifiTxParameters& txParams); + /** + * Compute the information about the acknowledgment of the current multi-user frame + * (as described by the given TX parameters) if the given MPDU is added. If the + * computed information is the same as the current one, a null pointer is returned. + * Otherwise, the computed information is returned. + * This method can only be called if the selected acknowledgment method for DL + * multi-user frames consists of a MU-BAR Trigger Frame sent as single-user frame. + * + * \param mpdu the given MPDU + * \param txParams the TX parameters describing the current multi-user frame + * \return the new acknowledgment method or a null pointer if the acknowledgment method + * is unchanged + */ + virtual std::unique_ptr GetAckInfoIfTfMuBar (Ptr mpdu, + const WifiTxParameters& txParams); + /** + * Compute the information about the acknowledgment of the current multi-user frame + * (as described by the given TX parameters) if the given MPDU is added. If the + * computed information is the same as the current one, a null pointer is returned. + * Otherwise, the computed information is returned. + * This method can only be called if the selected acknowledgment method for DL + * multi-user frames consists of MU-BAR Trigger Frames aggregated to the PSDUs of + * the MU PPDU. + * + * \param mpdu the given MPDU + * \param txParams the TX parameters describing the current multi-user frame + * \return the new acknowledgment method or a null pointer if the acknowledgment method + * is unchanged + */ + virtual std::unique_ptr GetAckInfoIfAggregatedMuBar (Ptr mpdu, + const WifiTxParameters& txParams); + bool m_useExplicitBar; //!< true for sending BARs, false for using Implicit BAR policy double m_baThreshold; //!< Threshold to determine when a BlockAck must be requested + WifiAcknowledgment::Method m_dlMuAckType; //!< Type of the ack sequence for DL MU PPDUs + uint8_t m_maxMcsForBlockAckInTbPpdu; //!< Max MCS used to send a BlockAck in a TB PPDU };