From b351edb9b7efced5535dea73f6a701d25b18e126 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 11 May 2022 11:45:20 +0200 Subject: [PATCH] wifi: Compute UL MU info independently of previous DL MU transmission --- src/wifi/model/he/rr-multi-user-scheduler.cc | 453 ++++++++++--------- src/wifi/model/he/rr-multi-user-scheduler.h | 16 +- 2 files changed, 243 insertions(+), 226 deletions(-) diff --git a/src/wifi/model/he/rr-multi-user-scheduler.cc b/src/wifi/model/he/rr-multi-user-scheduler.cc index 4fe518a59..a029cc5d2 100644 --- a/src/wifi/model/he/rr-multi-user-scheduler.cc +++ b/src/wifi/model/he/rr-multi-user-scheduler.cc @@ -112,7 +112,7 @@ RrMultiUserScheduler::DoInitialize (void) MakeCallback (&RrMultiUserScheduler::NotifyStationDeassociated, this)); for (const auto& ac : wifiAcList) { - m_staList.insert ({ac.first, {}}); + m_staListDl.insert ({ac.first, {}}); } MultiUserScheduler::DoInitialize (); } @@ -121,7 +121,8 @@ void RrMultiUserScheduler::DoDispose (void) { NS_LOG_FUNCTION (this); - m_staList.clear (); + m_staListDl.clear (); + m_staListUl.clear (); m_candidates.clear (); m_txParams.Clear (); m_apMac->TraceDisconnectWithoutContext ("AssociatedSta", @@ -145,11 +146,15 @@ RrMultiUserScheduler::SelectTxFormat (void) if (m_enableUlOfdma && m_enableBsrp && GetLastTxFormat () == DL_MU_TX) { - return TrySendingBsrpTf (); - } + TxFormat txFormat = TrySendingBsrpTf (); - if (m_enableUlOfdma && (GetLastTxFormat () == DL_MU_TX - || m_trigger.GetType () == TriggerFrameType::BSRP_TRIGGER)) + if (txFormat != DL_MU_TX) + { + return txFormat; + } + } + else if (m_enableUlOfdma && (GetLastTxFormat () == DL_MU_TX + || m_trigger.GetType () == TriggerFrameType::BSRP_TRIGGER)) { TxFormat txFormat = TrySendingBasicTf (); @@ -162,14 +167,113 @@ RrMultiUserScheduler::SelectTxFormat (void) return TrySendingDlMuPpdu (); } +template +WifiTxVector +RrMultiUserScheduler::GetTxVectorForUlMu (Func canbeSolicited) +{ + NS_LOG_FUNCTION (this); + + // determine RUs to allocate to stations + auto count = std::min (m_nStations, m_staListUl.size ()); + std::size_t nCentral26TonesRus; + HeRu::GetEqualSizedRusForStations (m_apMac->GetWifiPhy ()->GetChannelWidth (), count, + nCentral26TonesRus); + NS_ASSERT (count >= 1); + + if (!m_useCentral26TonesRus) + { + nCentral26TonesRus = 0; + } + + Ptr heConfiguration = m_apMac->GetHeConfiguration (); + NS_ASSERT (heConfiguration != 0); + + WifiTxVector txVector; + txVector.SetPreambleType (WIFI_PREAMBLE_HE_TB); + txVector.SetChannelWidth (m_apMac->GetWifiPhy ()->GetChannelWidth ()); + txVector.SetGuardInterval (heConfiguration->GetGuardInterval ().GetNanoSeconds ()); + txVector.SetBssColor (heConfiguration->GetBssColor ()); + + // iterate over the associated stations until an enough number of stations is identified + auto staIt = m_staListUl.begin (); + m_candidates.clear (); + + while (staIt != m_staListUl.end () + && txVector.GetHeMuUserInfoMap ().size () < std::min (m_nStations, count + nCentral26TonesRus)) + { + NS_LOG_DEBUG ("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")"); + + if (!canbeSolicited (*staIt)) + { + NS_LOG_DEBUG ("Skipping station based on provided function object"); + staIt++; + continue; + } + + uint8_t tid = 0; + while (tid < 8) + { + // check that a BA agreement is established with the receiver for the + // considered TID, since ack sequences for UL MU require block ack + if (m_heFem->GetBaAgreementEstablished (staIt->address, tid)) + { + break; + } + ++tid; + } + if (tid == 8) + { + NS_LOG_DEBUG ("No Block Ack agreement established with " << staIt->address); + staIt++; + continue; + } + + // prepare the MAC header of a frame that would be sent to the candidate station, + // just for the purpose of retrieving the TXVECTOR used to transmit to that station + WifiMacHeader hdr (WIFI_MAC_QOSDATA); + hdr.SetAddr1 (staIt->address); + hdr.SetAddr2 (m_apMac->GetAddress ()); + WifiTxVector suTxVector = GetWifiRemoteStationManager ()->GetDataTxVector (hdr); + txVector.SetHeMuUserInfo (staIt->aid, + {HeRu::RuSpec (), // assigned later by FinalizeTxVector + suTxVector.GetMode (), + suTxVector.GetNss ()}); + m_candidates.push_back ({staIt, nullptr}); + + // move to the next station in the list + staIt++; + } + + if (txVector.GetHeMuUserInfoMap ().empty ()) + { + // No suitable station + return txVector; + } + + FinalizeTxVector (txVector); + return txVector; +} + MultiUserScheduler::TxFormat RrMultiUserScheduler::TrySendingBsrpTf (void) { NS_LOG_FUNCTION (this); - m_trigger = CtrlTriggerHeader (TriggerFrameType::BSRP_TRIGGER, GetDlMuInfo ().txParams.m_txVector); + if (m_staListUl.empty ()) + { + NS_LOG_DEBUG ("No HE stations associated: return SU_TX"); + return TxFormat::SU_TX; + } - WifiTxVector txVector = GetDlMuInfo ().txParams.m_txVector; + WifiTxVector txVector = GetTxVectorForUlMu ([](const MasterInfo&){ return true; }); + + if (txVector.GetHeMuUserInfoMap ().empty ()) + { + NS_LOG_DEBUG ("No suitable station found"); + return TxFormat::DL_MU_TX; + } + + m_trigger = CtrlTriggerHeader (TriggerFrameType::BSRP_TRIGGER, txVector); txVector.SetGuardInterval (m_trigger.GetGuardInterval ()); auto item = GetTriggerFrame (m_trigger); @@ -231,197 +335,150 @@ RrMultiUserScheduler::TrySendingBasicTf (void) { NS_LOG_FUNCTION (this); + if (m_staListUl.empty ()) + { + NS_LOG_DEBUG ("No HE stations associated: return SU_TX"); + return TxFormat::SU_TX; + } + // check if an UL OFDMA transmission is possible after a DL OFDMA transmission NS_ABORT_MSG_IF (m_ulPsduSize == 0, "The UlPsduSize attribute must be set to a non-null value"); - // determine which of the stations served in DL have UL traffic - uint32_t maxBufferSize = 0; - // candidates sorted in decreasing order of queue size - std::multimap> ulCandidates; + // only consider stations that do not have reported a null queue size + WifiTxVector txVector = GetTxVectorForUlMu ([this](const MasterInfo& info) + { return m_apMac->GetMaxBufferStatus (info.address) > 0; }); - for (const auto& candidate : m_candidates) + if (txVector.GetHeMuUserInfoMap ().empty ()) { - uint8_t queueSize = m_apMac->GetMaxBufferStatus (candidate.first->address); + NS_LOG_DEBUG ("No suitable station found"); + return TxFormat::DL_MU_TX; + } + + uint32_t maxBufferSize = 0; + + for (const auto& candidate : txVector.GetHeMuUserInfoMap ()) + { + auto staIt = m_apMac->GetStaList ().find (candidate.first); + NS_ASSERT (staIt != m_apMac->GetStaList ().end ()); + uint8_t queueSize = m_apMac->GetMaxBufferStatus (staIt->second); if (queueSize == 255) { - NS_LOG_DEBUG ("Buffer status of station " << candidate.first->address << " is unknown"); + NS_LOG_DEBUG ("Buffer status of station " << staIt->second << " is unknown"); maxBufferSize = std::max (maxBufferSize, m_ulPsduSize); } else if (queueSize == 254) { - NS_LOG_DEBUG ("Buffer status of station " << candidate.first->address << " is not limited"); + NS_LOG_DEBUG ("Buffer status of station " << staIt->second << " is not limited"); maxBufferSize = 0xffffffff; } else { - NS_LOG_DEBUG ("Buffer status of station " << candidate.first->address << " is " << +queueSize); + NS_LOG_DEBUG ("Buffer status of station " << staIt->second << " is " << +queueSize); maxBufferSize = std::max (maxBufferSize, static_cast (queueSize * 256)); } - // serve the station if its queue size is not null - if (queueSize > 0) - { - ulCandidates.emplace (queueSize, candidate); - } } - // if the maximum buffer size is 0, skip UL OFDMA and proceed with trying DL OFDMA - if (maxBufferSize > 0) + if (maxBufferSize == 0) { - NS_ASSERT (!ulCandidates.empty ()); - std::size_t count = ulCandidates.size (); - std::size_t nCentral26TonesRus; - HeRu::RuType ruType = HeRu::GetEqualSizedRusForStations (m_apMac->GetWifiPhy ()->GetChannelWidth (), - count, nCentral26TonesRus); - if (!m_useCentral26TonesRus || ulCandidates.size () == count) + return DL_MU_TX; + } + + m_trigger = CtrlTriggerHeader (TriggerFrameType::BASIC_TRIGGER, txVector); + txVector.SetGuardInterval (m_trigger.GetGuardInterval ()); + + auto item = GetTriggerFrame (m_trigger); + m_triggerMacHdr = item->GetHeader (); + + // compute the maximum amount of time that can be granted to stations. + // This value is limited by the max PPDU duration + Time maxDuration = GetPpduMaxTime (txVector.GetPreambleType ()); + + m_txParams.Clear (); + // set the TXVECTOR used to send the Trigger Frame + m_txParams.m_txVector = m_apMac->GetWifiRemoteStationManager ()->GetRtsTxVector (m_triggerMacHdr.GetAddr1 ()); + + if (!m_heFem->TryAddMpdu (item, m_txParams, m_availableTime)) + { + // an UL OFDMA transmission is not possible, hence return NO_TX. In + // this way, no transmission will occur now and the next time we will + // try again performing an UL OFDMA transmission. + NS_LOG_DEBUG ("Remaining TXOP duration is not enough for UL MU exchange"); + return NO_TX; + } + + if (m_availableTime != Time::Min ()) + { + // TryAddMpdu only considers the time to transmit the Trigger Frame + NS_ASSERT (m_txParams.m_protection && m_txParams.m_protection->protectionTime != Time::Min ()); + NS_ASSERT (m_txParams.m_acknowledgment && m_txParams.m_acknowledgment->acknowledgmentTime != Time::Min ()); + NS_ASSERT (m_txParams.m_txDuration != Time::Min ()); + + maxDuration = Min (maxDuration, m_availableTime + - m_txParams.m_protection->protectionTime + - m_txParams.m_txDuration + - m_apMac->GetWifiPhy ()->GetSifs () + - m_txParams.m_acknowledgment->acknowledgmentTime); + if (maxDuration.IsNegative ()) { - nCentral26TonesRus = 0; - } - else - { - nCentral26TonesRus = std::min (ulCandidates.size () - count, nCentral26TonesRus); - } - - WifiTxVector txVector; - txVector.SetPreambleType (WIFI_PREAMBLE_HE_TB); - auto candidateIt = ulCandidates.begin (); - - if (GetLastTxFormat () == DL_MU_TX) - { - txVector.SetChannelWidth (GetDlMuInfo ().txParams.m_txVector.GetChannelWidth ()); - txVector.SetGuardInterval (CtrlTriggerHeader ().GetGuardInterval ()); - - for (std::size_t i = 0; i < count + nCentral26TonesRus; i++) - { - NS_ASSERT (candidateIt != ulCandidates.end ()); - uint16_t staId = candidateIt->second.first->aid; - // AssignRuIndices will be called below to set RuSpec - txVector.SetHeMuUserInfo (staId, - {{(i < count ? ruType : HeRu::RU_26_TONE), 1, false}, - GetDlMuInfo ().txParams.m_txVector.GetMode (staId), - GetDlMuInfo ().txParams.m_txVector.GetNss (staId)}); - - candidateIt++; - } - } - else - { - txVector.SetChannelWidth (GetUlMuInfo ().trigger.GetUlBandwidth ()); - txVector.SetGuardInterval (GetUlMuInfo ().trigger.GetGuardInterval ()); - - for (std::size_t i = 0; i < count + nCentral26TonesRus; i++) - { - NS_ASSERT (candidateIt != ulCandidates.end ()); - uint16_t staId = candidateIt->second.first->aid; - auto userInfoIt = GetUlMuInfo ().trigger.FindUserInfoWithAid (staId); - NS_ASSERT (userInfoIt != GetUlMuInfo ().trigger.end ()); - // AssignRuIndices will be called below to set RuSpec - txVector.SetHeMuUserInfo (staId, - {{(i < count ? ruType : HeRu::RU_26_TONE), 1, false}, - HePhy::GetHeMcs (userInfoIt->GetUlMcs ()), - userInfoIt->GetNss ()}); - - candidateIt++; - } - } - - // remove candidates that will not be served - ulCandidates.erase (candidateIt, ulCandidates.end ()); - AssignRuIndices (txVector); - - m_trigger = CtrlTriggerHeader (TriggerFrameType::BASIC_TRIGGER, txVector); - auto item = GetTriggerFrame (m_trigger); - m_triggerMacHdr = item->GetHeader (); - - // compute the maximum amount of time that can be granted to stations. - // This value is limited by the max PPDU duration - Time maxDuration = GetPpduMaxTime (txVector.GetPreambleType ()); - - m_txParams.Clear (); - // set the TXVECTOR used to send the Trigger Frame - m_txParams.m_txVector = m_apMac->GetWifiRemoteStationManager ()->GetRtsTxVector (m_triggerMacHdr.GetAddr1 ()); - - if (!m_heFem->TryAddMpdu (item, m_txParams, m_availableTime)) - { - // an UL OFDMA transmission is not possible, hence return NO_TX. In - // this way, no transmission will occur now and the next time we will - // try again performing an UL OFDMA transmission. NS_LOG_DEBUG ("Remaining TXOP duration is not enough for UL MU exchange"); return NO_TX; } + } - if (m_availableTime != Time::Min ()) - { - // TryAddMpdu only considers the time to transmit the Trigger Frame - NS_ASSERT (m_txParams.m_protection && m_txParams.m_protection->protectionTime != Time::Min ()); - NS_ASSERT (m_txParams.m_acknowledgment && m_txParams.m_acknowledgment->acknowledgmentTime != Time::Min ()); - NS_ASSERT (m_txParams.m_txDuration != Time::Min ()); + // Compute the time taken by each station to transmit a frame of maxBufferSize size + Time bufferTxTime = Seconds (0); + for (const auto& userInfo : m_trigger) + { + Time duration = WifiPhy::CalculateTxDuration (maxBufferSize, txVector, + m_apMac->GetWifiPhy ()->GetPhyBand (), + userInfo.GetAid12 ()); + bufferTxTime = Max (bufferTxTime, duration); + } - maxDuration = Min (maxDuration, m_availableTime - - m_txParams.m_protection->protectionTime - - m_txParams.m_txDuration - - m_apMac->GetWifiPhy ()->GetSifs () - - m_txParams.m_acknowledgment->acknowledgmentTime); - if (maxDuration.IsNegative ()) - { - NS_LOG_DEBUG ("Remaining TXOP duration is not enough for UL MU exchange"); - return NO_TX; - } - } - - // Compute the time taken by each station to transmit a frame of maxBufferSize size - Time bufferTxTime = Seconds (0); + if (bufferTxTime < maxDuration) + { + // the maximum buffer size can be transmitted within the allowed time + maxDuration = bufferTxTime; + } + else + { + // maxDuration may be a too short time. If it does not allow any station to + // transmit at least m_ulPsduSize bytes, give up the UL MU transmission for now + Time minDuration = Seconds (0); for (const auto& userInfo : m_trigger) { - Time duration = WifiPhy::CalculateTxDuration (maxBufferSize, txVector, + Time duration = WifiPhy::CalculateTxDuration (m_ulPsduSize, txVector, m_apMac->GetWifiPhy ()->GetPhyBand (), userInfo.GetAid12 ()); - bufferTxTime = Max (bufferTxTime, duration); + minDuration = (minDuration.IsZero () ? duration : Min (minDuration, duration)); } - if (bufferTxTime < maxDuration) + if (maxDuration < minDuration) { - // the maximum buffer size can be transmitted within the allowed time - maxDuration = bufferTxTime; + // maxDuration is a too short time, hence return NO_TX. In this way, + // no transmission will occur now and the next time we will try again + // performing an UL OFDMA transmission. + NS_LOG_DEBUG ("Available time " << maxDuration.As (Time::MS) << " is too short"); + return NO_TX; } - else - { - // maxDuration may be a too short time. If it does not allow any station to - // transmit at least m_ulPsduSize bytes, give up the UL MU transmission for now - Time minDuration = Seconds (0); - for (const auto& userInfo : m_trigger) - { - Time duration = WifiPhy::CalculateTxDuration (m_ulPsduSize, txVector, - m_apMac->GetWifiPhy ()->GetPhyBand (), - userInfo.GetAid12 ()); - minDuration = (minDuration.IsZero () ? duration : Min (minDuration, duration)); - } - - if (maxDuration < minDuration) - { - // maxDuration is a too short time, hence return NO_TX. In this way, - // no transmission will occur now and the next time we will try again - // performing an UL OFDMA transmission. - NS_LOG_DEBUG ("Available time " << maxDuration.As (Time::MS) << " is too short"); - return NO_TX; - } - } - - // maxDuration is the time to grant to the stations. Finalize the Trigger Frame - uint16_t ulLength; - std::tie (ulLength, maxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength (maxDuration, - txVector, - m_apMac->GetWifiPhy ()->GetPhyBand ()); - NS_LOG_DEBUG ("TB PPDU duration: " << maxDuration.As (Time::MS)); - m_trigger.SetUlLength (ulLength); - // set Preferred AC to the AC that gained channel access - for (auto& userInfo : m_trigger) - { - userInfo.SetBasicTriggerDepUserInfo (0, 0, m_edca->GetAccessCategory ()); - } - - return UL_MU_TX; } - return DL_MU_TX; + + // maxDuration is the time to grant to the stations. Finalize the Trigger Frame + uint16_t ulLength; + std::tie (ulLength, maxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength (maxDuration, + txVector, + m_apMac->GetWifiPhy ()->GetPhyBand ()); + NS_LOG_DEBUG ("TB PPDU duration: " << maxDuration.As (Time::MS)); + m_trigger.SetUlLength (ulLength); + // set Preferred AC to the AC that gained channel access + for (auto& userInfo : m_trigger) + { + userInfo.SetBasicTriggerDepUserInfo (0, 0, m_edca->GetAccessCategory ()); + } + + UpdateCredits (m_staListUl, maxDuration, txVector); + + return UL_MU_TX; } void @@ -431,10 +488,11 @@ RrMultiUserScheduler::NotifyStationAssociated (uint16_t aid, Mac48Address addres if (GetWifiRemoteStationManager ()->GetHeSupported (address)) { - for (auto& staList : m_staList) + for (auto& staList : m_staListDl) { staList.second.push_back (MasterInfo {aid, address, 0.0}); } + m_staListUl.push_back (MasterInfo {aid, address, 0.0}); } } @@ -445,11 +503,13 @@ RrMultiUserScheduler::NotifyStationDeassociated (uint16_t aid, Mac48Address addr if (GetWifiRemoteStationManager ()->GetHeSupported (address)) { - for (auto& staList : m_staList) + for (auto& staList : m_staListDl) { staList.second.remove_if ([&aid, &address] (const MasterInfo& info) { return info.aid == aid && info.address == address; }); } + m_staListUl.remove_if ([&aid, &address] (const MasterInfo& info) + { return info.aid == aid && info.address == address; }); } } @@ -460,13 +520,13 @@ RrMultiUserScheduler::TrySendingDlMuPpdu (void) AcIndex primaryAc = m_edca->GetAccessCategory (); - if (m_staList[primaryAc].empty ()) + if (m_staListDl[primaryAc].empty ()) { NS_LOG_DEBUG ("No HE stations associated: return SU_TX"); return TxFormat::SU_TX; } - std::size_t count = std::min (static_cast (m_nStations), m_staList[primaryAc].size ()); + std::size_t count = std::min (static_cast (m_nStations), m_staListDl[primaryAc].size ()); std::size_t nCentral26TonesRus; HeRu::RuType ruType = HeRu::GetEqualSizedRusForStations (m_apMac->GetWifiPhy ()->GetChannelWidth (), count, nCentral26TonesRus); @@ -519,10 +579,10 @@ RrMultiUserScheduler::TrySendingDlMuPpdu (void) Time actualAvailableTime = (m_initialFrame ? Time::Min () : m_availableTime); // iterate over the associated stations until an enough number of stations is identified - auto staIt = m_staList[primaryAc].begin (); + auto staIt = m_staListDl[primaryAc].begin (); m_candidates.clear (); - while (staIt != m_staList[primaryAc].end () + while (staIt != m_staListDl[primaryAc].end () && m_candidates.size () < std::min (static_cast (m_nStations), count + nCentral26TonesRus)) { NS_LOG_DEBUG ("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")"); @@ -765,62 +825,13 @@ RrMultiUserScheduler::ComputeDlMuInfo (void) } AcIndex primaryAc = m_edca->GetAccessCategory (); - UpdateCredits (m_staList[primaryAc], dlMuInfo.txParams.m_txDuration, dlMuInfo.txParams.m_txVector); + UpdateCredits (m_staListDl[primaryAc], dlMuInfo.txParams.m_txDuration, dlMuInfo.txParams.m_txVector); - NS_LOG_DEBUG ("Next station to serve has AID=" << m_staList[primaryAc].front ().aid); + NS_LOG_DEBUG ("Next station to serve has AID=" << m_staListDl[primaryAc].front ().aid); return dlMuInfo; } -void -RrMultiUserScheduler::AssignRuIndices (WifiTxVector& txVector) -{ - NS_LOG_FUNCTION (this << txVector); - - uint8_t bw = txVector.GetChannelWidth (); - - // find the RU types allocated in the TXVECTOR - std::set ruTypeSet; - for (const auto& userInfo : txVector.GetHeMuUserInfoMap ()) - { - ruTypeSet.insert (userInfo.second.ru.GetRuType ()); - } - - std::vector ruSet, central26TonesRus; - - // This scheduler allocates equal sized RUs and optionally the remaining 26-tone RUs - if (ruTypeSet.size () == 2) - { - // central 26-tone RUs have been allocated - NS_ASSERT (ruTypeSet.find (HeRu::RU_26_TONE) != ruTypeSet.end ()); - ruTypeSet.erase (HeRu::RU_26_TONE); - NS_ASSERT (ruTypeSet.size () == 1); - central26TonesRus = HeRu::GetCentral26TonesRus (bw, *ruTypeSet.begin ()); - } - - NS_ASSERT (ruTypeSet.size () == 1); - ruSet = HeRu::GetRusOfType (bw, *ruTypeSet.begin ()); - - auto ruSetIt = ruSet.begin (); - auto central26TonesRusIt = central26TonesRus.begin (); - - for (const auto& userInfo : txVector.GetHeMuUserInfoMap ()) - { - if (userInfo.second.ru.GetRuType () == *ruTypeSet.begin ()) - { - NS_ASSERT (ruSetIt != ruSet.end ()); - txVector.SetRu (*ruSetIt, userInfo.first); - ruSetIt++; - } - else - { - NS_ASSERT (central26TonesRusIt != central26TonesRus.end ()); - txVector.SetRu (*central26TonesRusIt, userInfo.first); - central26TonesRusIt++; - } - } -} - MultiUserScheduler::UlMuInfo RrMultiUserScheduler::ComputeUlMuInfo (void) { diff --git a/src/wifi/model/he/rr-multi-user-scheduler.h b/src/wifi/model/he/rr-multi-user-scheduler.h index 6986a5692..943c27ee9 100644 --- a/src/wifi/model/he/rr-multi-user-scheduler.h +++ b/src/wifi/model/he/rr-multi-user-scheduler.h @@ -87,12 +87,17 @@ private: virtual TxFormat TrySendingDlMuPpdu (void); /** - * Assign an RU index to all the RUs allocated by the given TXVECTOR. Allocated - * RUs must all have the same size, except for allocated central 26-tone RUs. + * Compute a TXVECTOR that can be used to construct a Trigger Frame to solicit + * transmissions from suitable stations, i.e., stations that have established a + * BlockAck agreement with the AP and for which the given predicate returns true. * - * \param txVector the given TXVECTOR + * \tparam Func \deduced the type of the given predicate + * \param canBeSolicited a predicate returning false for stations that shall not be solicited + * \return a TXVECTOR that can be used to construct a Trigger Frame to solicit + * transmissions from suitable stations */ - void AssignRuIndices (WifiTxVector& txVector); + template + WifiTxVector GetTxVectorForUlMu (Func canBeSolicited); /** * Notify the scheduler that a station associated with the AP @@ -153,7 +158,8 @@ private: bool m_enableBsrp; //!< send a BSRP before an UL MU transmission bool m_useCentral26TonesRus; //!< whether to allocate central 26-tone RUs uint32_t m_ulPsduSize; //!< the size in byte of the solicited PSDU - std::map> m_staList; //!< Per-AC list of stations (next to serve first) + std::map> m_staListDl; //!< Per-AC list of stations (next to serve for DL first) + std::list m_staListUl; //!< List of stations to serve for UL std::list m_candidates; //!< Candidate stations for MU TX Time m_maxCredits; //!< Max amount of credits a station can have CtrlTriggerHeader m_trigger; //!< Trigger Frame to send