wifi: Improve logic for main PHY to start TXOP with non-TX capable aux PHY
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
#include "ns3/wifi-net-device.h"
|
||||
#include "ns3/wifi-phy.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ns3
|
||||
{
|
||||
|
||||
@@ -48,7 +50,24 @@ AdvancedEmlsrManager::GetTypeId()
|
||||
"switching to another link.",
|
||||
BooleanValue(false),
|
||||
MakeBooleanAccessor(&AdvancedEmlsrManager::m_interruptSwitching),
|
||||
MakeBooleanChecker());
|
||||
MakeBooleanChecker())
|
||||
.AddAttribute("UseAuxPhyCca",
|
||||
"Whether the CCA performed in the last PIFS interval by a non-TX "
|
||||
"capable aux PHY should be used when the main PHY ends switching to "
|
||||
"the aux PHY's link to determine whether TX can start or not (and what "
|
||||
"bandwidth can be used for transmission) independently of whether the "
|
||||
"aux PHY bandwidth is smaller than the main PHY bandwidth or not.",
|
||||
BooleanValue(false),
|
||||
MakeBooleanAccessor(&AdvancedEmlsrManager::m_useAuxPhyCca),
|
||||
MakeBooleanChecker())
|
||||
.AddAttribute("SwitchMainPhyBackDelay",
|
||||
"Duration of the timer started in case of non-TX capable aux PHY (that "
|
||||
"does not switch link) when medium is sensed busy during the PIFS "
|
||||
"interval preceding/following the main PHY switch end. When the timer "
|
||||
"expires, the main PHY is switched back to the primary link.",
|
||||
TimeValue(MilliSeconds(5)),
|
||||
MakeTimeAccessor(&AdvancedEmlsrManager::m_switchMainPhyBackDelay),
|
||||
MakeTimeChecker());
|
||||
return tid;
|
||||
}
|
||||
|
||||
@@ -317,6 +336,174 @@ AdvancedEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)
|
||||
return Time{0};
|
||||
}
|
||||
|
||||
void
|
||||
AdvancedEmlsrManager::CheckNavAndCcaLastPifs(Ptr<WifiPhy> phy, uint8_t linkId, Ptr<QosTxop> edca)
|
||||
{
|
||||
NS_LOG_FUNCTION(this << phy->GetPhyId() << linkId << edca->GetAccessCategory());
|
||||
|
||||
const auto caManager = GetStaMac()->GetChannelAccessManager(linkId);
|
||||
const auto pifs = phy->GetSifs() + phy->GetSlot();
|
||||
|
||||
const auto isBusy = caManager->IsBusy(); // check NAV and CCA on primary20
|
||||
// check CCA on the entire channel
|
||||
auto width = caManager->GetLargestIdlePrimaryChannel(pifs, Simulator::Now());
|
||||
|
||||
if (!isBusy && width > 0)
|
||||
{
|
||||
// medium idle, start TXOP
|
||||
width = std::min(width, GetChannelForMainPhy(linkId).GetTotalWidth());
|
||||
|
||||
// if this function is called at the end of the main PHY switch, it is executed before the
|
||||
// main PHY is connected to this link in order to use the CCA information of the aux PHY.
|
||||
// Schedule now the TXOP start so that we first connect the main PHY to this link.
|
||||
m_ccaLastPifs = Simulator::ScheduleNow([=, this]() {
|
||||
if (GetEhtFem(linkId)->HeFrameExchangeManager::StartTransmission(edca, width))
|
||||
{
|
||||
NotifyUlTxopStart(linkId);
|
||||
}
|
||||
else if (!m_switchAuxPhy)
|
||||
{
|
||||
// switch main PHY back to primary link if SwitchAuxPhy is false
|
||||
SwitchMainPhyBackToPrimaryLink(linkId);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// medium busy, restart channel access
|
||||
NS_LOG_DEBUG("Medium busy in the last PIFS interval");
|
||||
edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED
|
||||
edca->StartAccessAfterEvent(linkId,
|
||||
Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
|
||||
Txop::CHECK_MEDIUM_BUSY);
|
||||
|
||||
// the main PHY must stay for some time on this link to check if it gets channel access.
|
||||
// The timer is stopped if a DL or UL TXOP is started. When the timer expires, the main PHY
|
||||
// switches back to the preferred link if SwitchAuxPhy is false
|
||||
m_switchMainPhyBackEvent = Simulator::Schedule(m_switchMainPhyBackDelay, [this, linkId]() {
|
||||
if (!m_switchAuxPhy)
|
||||
{
|
||||
SwitchMainPhyBackToPrimaryLink(linkId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AdvancedEmlsrManager::DoNotifyIcfReceived(uint8_t linkId)
|
||||
{
|
||||
NS_LOG_FUNCTION(this << linkId);
|
||||
m_switchMainPhyBackEvent.Cancel();
|
||||
m_ccaLastPifs.Cancel();
|
||||
}
|
||||
|
||||
void
|
||||
AdvancedEmlsrManager::DoNotifyUlTxopStart(uint8_t linkId)
|
||||
{
|
||||
NS_LOG_FUNCTION(this << linkId);
|
||||
m_switchMainPhyBackEvent.Cancel();
|
||||
m_ccaLastPifs.Cancel();
|
||||
}
|
||||
|
||||
bool
|
||||
AdvancedEmlsrManager::RequestMainPhyToSwitch(uint8_t linkId, AcIndex aci)
|
||||
{
|
||||
NS_LOG_FUNCTION(this << linkId << aci);
|
||||
|
||||
// the aux PHY is not TX capable; check if main PHY has to switch to the aux PHY's link
|
||||
auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
|
||||
const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy);
|
||||
|
||||
// if main PHY is not operating on a link, it is switching, hence do not request another switch
|
||||
if (!mainPhyLinkId.has_value())
|
||||
{
|
||||
NS_LOG_DEBUG("Main PHY is not operating on any link");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the main PHY is already trying to get access on a link, do not request another switch
|
||||
if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending())
|
||||
{
|
||||
NS_LOG_DEBUG("Main PHY is trying to get access on another link");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mainPhy->GetState()->GetState())
|
||||
{
|
||||
case WifiPhyState::IDLE:
|
||||
// proceed to try requesting main PHY to switch
|
||||
break;
|
||||
case WifiPhyState::CCA_BUSY:
|
||||
// if the main PHY is receiving the PHY header of a PPDU, we decide to proceed or give up
|
||||
// based on the AllowUlTxopInRx attribute
|
||||
if (mainPhy->IsReceivingPhyHeader() && !m_allowUlTxopInRx)
|
||||
{
|
||||
NS_LOG_DEBUG("Main PHY receiving PHY header and AllowUlTxopInRx is false");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case WifiPhyState::RX:
|
||||
if (auto macHdr = GetEhtFem(*mainPhyLinkId)->GetReceivedMacHdr())
|
||||
{
|
||||
// information on the MAC header of the PSDU being received is available; if we cannot
|
||||
// use it or the main PHY is receiving an ICF, give up requesting main PHY to switch
|
||||
if (const auto& hdr = macHdr->get();
|
||||
!m_useNotifiedMacHdr ||
|
||||
(hdr.IsTrigger() && (hdr.GetAddr1().IsBroadcast() ||
|
||||
hdr.GetAddr1() == GetEhtFem(*mainPhyLinkId)->GetAddress())))
|
||||
{
|
||||
NS_LOG_DEBUG("Receiving an ICF or cannot use MAC header information");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// information on the MAC header of the PSDU being received is not available, we decide to
|
||||
// proceed or give up based on the AllowUlTxopInRx attribute
|
||||
else if (!m_allowUlTxopInRx)
|
||||
{
|
||||
NS_LOG_DEBUG("Receiving PSDU, no MAC header information, AllowUlTxopInRx is false");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NS_LOG_DEBUG("Cannot request main PHY to switch when in state "
|
||||
<< mainPhy->GetState()->GetState());
|
||||
return false;
|
||||
}
|
||||
|
||||
// request to switch main PHY if we expect the main PHY to get channel access on this link
|
||||
// more quickly, i.e., if ALL the ACs with queued frames (that can be transmitted on the link
|
||||
// on which the main PHY is currently operating) and with priority higher than or equal to
|
||||
// that of the AC for which Aux PHY gained TXOP have their backoff counter greater than the
|
||||
// channel switch delay plus PIFS
|
||||
|
||||
auto requestSwitch = false;
|
||||
const auto now = Simulator::Now();
|
||||
|
||||
for (const auto& [acIndex, ac] : wifiAcList)
|
||||
{
|
||||
if (auto edca = GetStaMac()->GetQosTxop(acIndex);
|
||||
acIndex >= aci && edca->HasFramesToTransmit(linkId))
|
||||
{
|
||||
requestSwitch = true;
|
||||
|
||||
const auto backoffEnd =
|
||||
GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
|
||||
NS_LOG_DEBUG("Backoff end for " << acIndex
|
||||
<< " on primary link: " << backoffEnd.As(Time::US));
|
||||
|
||||
if (backoffEnd <= now + mainPhy->GetChannelSwitchDelay() +
|
||||
GetStaMac()->GetWifiPhy(linkId)->GetPifs() &&
|
||||
edca->HasFramesToTransmit(*mainPhyLinkId))
|
||||
{
|
||||
requestSwitch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return requestSwitch;
|
||||
}
|
||||
|
||||
void
|
||||
AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci)
|
||||
{
|
||||
@@ -324,106 +511,90 @@ AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex
|
||||
|
||||
NS_ASSERT_MSG(!m_auxPhyTxCapable,
|
||||
"This function should only be called if aux PHY is not TX capable");
|
||||
|
||||
// the aux PHY is not TX capable; check if main PHY has to switch to the aux PHY's link
|
||||
auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
|
||||
|
||||
// if the main PHY is idle, switch main PHY if we expect the main PHY to get channel access on
|
||||
// this link more quicky, i.e., if ALL the ACs with queued frames and with priority higher than
|
||||
// or equal to that of the AC for which Aux PHY gained TXOP have their backoff counter greater
|
||||
// than the channel switch delay plus PIFS
|
||||
|
||||
auto requestSwitch = false;
|
||||
|
||||
if (mainPhy->IsStateIdle())
|
||||
if (RequestMainPhyToSwitch(linkId, aci))
|
||||
{
|
||||
auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy);
|
||||
NS_ASSERT(mainPhyLinkId.has_value());
|
||||
const auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
|
||||
const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot();
|
||||
|
||||
// update backoff on main PHY link for all ACs
|
||||
GetStaMac()
|
||||
->GetChannelAccessManager(*mainPhyLinkId)
|
||||
->NeedBackoffUponAccess(GetStaMac()->GetQosTxop(AC_BE),
|
||||
Txop::HAD_FRAMES_TO_TRANSMIT,
|
||||
Txop::CHECK_MEDIUM_BUSY);
|
||||
|
||||
for (const auto& [acIndex, ac] : wifiAcList)
|
||||
// schedule actions to take based on CCA sensing for a PIFS
|
||||
if (m_useAuxPhyCca || GetChannelForAuxPhy(linkId).GetTotalWidth() >=
|
||||
GetChannelForMainPhy(linkId).GetTotalWidth())
|
||||
{
|
||||
if (auto edca = GetStaMac()->GetQosTxop(acIndex);
|
||||
acIndex >= aci && edca->HasFramesToTransmit(linkId))
|
||||
{
|
||||
requestSwitch = true;
|
||||
|
||||
auto backoffEnd =
|
||||
GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
|
||||
NS_LOG_DEBUG("Backoff end for " << acIndex
|
||||
<< " on primary link: " << backoffEnd.As(Time::US));
|
||||
|
||||
if (backoffEnd <= Simulator::Now() + mainPhy->GetChannelSwitchDelay() +
|
||||
GetStaMac()->GetWifiPhy(linkId)->GetPifs() &&
|
||||
edca->HasFramesToTransmit(*mainPhyLinkId))
|
||||
{
|
||||
requestSwitch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// use aux PHY CCA in the last PIFS interval before main PHY switch end
|
||||
NS_LOG_DEBUG("Schedule CCA check at the end of main PHY switch");
|
||||
m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay(),
|
||||
&AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
|
||||
this,
|
||||
auxPhy,
|
||||
linkId,
|
||||
GetStaMac()->GetQosTxop(aci));
|
||||
}
|
||||
else
|
||||
{
|
||||
// use main PHY CCA in the last PIFS interval after main PHY switch end
|
||||
NS_LOG_DEBUG("Schedule CCA check a PIFS after the end of main PHY switch");
|
||||
m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + pifs,
|
||||
&AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
|
||||
this,
|
||||
mainPhy,
|
||||
linkId,
|
||||
GetStaMac()->GetQosTxop(aci));
|
||||
}
|
||||
}
|
||||
|
||||
if ((mainPhy->IsStateCcaBusy() && !mainPhy->IsReceivingPhyHeader()) ||
|
||||
(mainPhy->IsStateIdle() && requestSwitch))
|
||||
{
|
||||
// switch main PHY
|
||||
SwitchMainPhy(linkId, false, RESET_BACKOFF, REQUEST_ACCESS);
|
||||
|
||||
SwitchMainPhy(linkId, false, RESET_BACKOFF, DONT_REQUEST_ACCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if and when we need to request channel access again for the aux PHY based on
|
||||
// the main PHY state.
|
||||
// Note that, if we have requested the main PHY to switch (above), the function has returned
|
||||
// and the EHT FEM will start a TXOP if the medium is idle for a PIFS interval following
|
||||
// and the EHT FEM will start a TXOP if medium is idle for a PIFS interval preceding/following
|
||||
// the end of the main PHY channel switch.
|
||||
// If the state is switching, but we have not requested the main PHY to switch, then we
|
||||
// request channel access again for the aux PHY a PIFS after that the main PHY state is back
|
||||
// to IDLE (to avoid stealing the main PHY from the non-primary link which the main PHY is
|
||||
// switching to), and then we will determine if the main PHY has to switch link.
|
||||
// If the state is CCA_BUSY, the medium is busy but the main PHY is not receiving a PPDU.
|
||||
// In this case, we request channel access again for the aux PHY a PIFS after that the main
|
||||
// PHY state is back to IDLE, and then we will determine if the main PHY has to switch link.
|
||||
// If the state is TX or RX, it means that the main PHY is involved in a TXOP. In this
|
||||
// case, do nothing because the channel access will be requested when unblocking links
|
||||
// at the end of the TXOP.
|
||||
// If the main PHY has been requested to switch by another aux PHY, this aux PHY will request
|
||||
// channel access again when we have completed the CCA assessment on the other link.
|
||||
// If the state is switching, CCA_BUSY or RX, then we request channel access again for the
|
||||
// aux PHY when the main PHY state is back to IDLE.
|
||||
// If the state is TX, it means that the main PHY is involved in a TXOP. Do nothing because
|
||||
// the channel access will be requested when unblocking links at the end of the TXOP.
|
||||
// If the state is IDLE, then either no AC has traffic to send or the backoff on the link
|
||||
// of the main PHY is shorter than the channel switch delay. In the former case, do
|
||||
// nothing because channel access will be triggered when new packets arrive; in the latter
|
||||
// case, do nothing because the main PHY will start a TXOP and at the end of such TXOP
|
||||
// links will be unblocked and the channel access requested on all links
|
||||
|
||||
if (!mainPhy->IsStateSwitching() && !mainPhy->IsStateCcaBusy())
|
||||
Time delay{};
|
||||
|
||||
if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending())
|
||||
{
|
||||
NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState() << ". Do nothing");
|
||||
delay = std::max(Simulator::GetDelayLeft(m_ccaLastPifs),
|
||||
Simulator::GetDelayLeft(m_switchMainPhyBackEvent));
|
||||
}
|
||||
else if (mainPhy->IsStateSwitching() || mainPhy->IsStateCcaBusy() || mainPhy->IsStateRx())
|
||||
{
|
||||
delay = mainPhy->GetDelayUntilIdle();
|
||||
NS_ASSERT(delay.IsStrictlyPositive());
|
||||
}
|
||||
|
||||
NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState());
|
||||
|
||||
if (delay.IsZero())
|
||||
{
|
||||
NS_LOG_DEBUG("Do nothing");
|
||||
return;
|
||||
}
|
||||
|
||||
auto delay = mainPhy->GetDelayUntilIdle();
|
||||
NS_ASSERT(delay.IsStrictlyPositive());
|
||||
delay += mainPhy->GetSifs() + mainPhy->GetSlot();
|
||||
auto edca = GetStaMac()->GetQosTxop(aci);
|
||||
edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED
|
||||
|
||||
NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState()
|
||||
<< ". Schedule channel access request on link " << +linkId
|
||||
<< " at time " << (Simulator::Now() + delay).As(Time::NS));
|
||||
Simulator::Schedule(delay, [=, this]() {
|
||||
for (const auto& [aci, ac] : wifiAcList)
|
||||
{
|
||||
auto edca = GetStaMac()->GetQosTxop(aci);
|
||||
if (edca->GetAccessStatus(linkId) != Txop::REQUESTED &&
|
||||
edca->HasFramesToTransmit(linkId))
|
||||
{
|
||||
NS_LOG_DEBUG("Request channel access on link " << +linkId << " for " << aci);
|
||||
GetStaMac()->GetChannelAccessManager(linkId)->RequestAccess(edca);
|
||||
}
|
||||
}
|
||||
NS_LOG_DEBUG("Schedule channel access request on link "
|
||||
<< +linkId << " at time " << (Simulator::Now() + delay).As(Time::NS));
|
||||
Simulator::Schedule(delay, [=]() {
|
||||
edca->StartAccessAfterEvent(linkId,
|
||||
Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
|
||||
Txop::CHECK_MEDIUM_BUSY);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -52,15 +52,52 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager
|
||||
const WifiTxVector& txVector,
|
||||
Time psduDuration);
|
||||
|
||||
/**
|
||||
* Use information from NAV and CCA performed by the given PHY on the given link in the last
|
||||
* PIFS interval to determine whether the given EDCAF can start a TXOP. This function is
|
||||
* intended to be used when the main PHY switches channel to start an UL TXOP on a link where
|
||||
* channel access was obtained by a non-TX capable aux PHY.
|
||||
*
|
||||
* \param phy the PHY that performed CCA in the last PIFS interval
|
||||
* \param linkId the ID of the given link
|
||||
* \param edca the given EDCAF
|
||||
*/
|
||||
void CheckNavAndCcaLastPifs(Ptr<WifiPhy> phy, uint8_t linkId, Ptr<QosTxop> edca);
|
||||
|
||||
/**
|
||||
* Determine whether the main PHY shall be requested to switch to the link of an aux PHY that
|
||||
* has gained channel access through the given AC but it is not TX capable.
|
||||
*
|
||||
* \param linkId the ID of the link on which the aux PHY is operating
|
||||
* \param aci the index of the given AC
|
||||
* \return whether the main PHY shall be requested to switch to the link of the aux PHY
|
||||
*/
|
||||
bool RequestMainPhyToSwitch(uint8_t linkId, AcIndex aci);
|
||||
|
||||
private:
|
||||
void DoNotifyTxopEnd(uint8_t linkId) override;
|
||||
void DoNotifyIcfReceived(uint8_t linkId) override;
|
||||
void DoNotifyUlTxopStart(uint8_t linkId) override;
|
||||
|
||||
bool m_useNotifiedMacHdr; //!< whether to use the information about the MAC header of
|
||||
//!< the MPDU being received (if notified by the PHY)
|
||||
bool m_allowUlTxopInRx; //!< whether a (main or aux) PHY is allowed to start an UL
|
||||
//!< TXOP if another PHY is receiving a PPDU
|
||||
bool m_interruptSwitching; //!< whether a main PHY switching can be interrupted to start
|
||||
//!< switching to another link
|
||||
bool m_useNotifiedMacHdr; //!< whether to use the information about the MAC header of
|
||||
//!< the MPDU being received (if notified by the PHY)
|
||||
bool m_allowUlTxopInRx; //!< whether a (main or aux) PHY is allowed to start an UL
|
||||
//!< TXOP if another PHY is receiving a PPDU
|
||||
bool m_interruptSwitching; //!< whether a main PHY switching can be interrupted to start
|
||||
//!< switching to another link
|
||||
bool m_useAuxPhyCca; //!< whether the CCA performed in the last PIFS interval by a
|
||||
//!< non-TX capable aux PHY should be used when the main PHY
|
||||
//!< ends switching to the aux PHY's link to determine whether
|
||||
//!< TX can start or not
|
||||
Time m_switchMainPhyBackDelay; //!< duration of the timer started in case of non-TX capable aux
|
||||
//!< PHY when medium is sensed busy during the PIFS interval
|
||||
//!< preceding/following the main PHY switch end
|
||||
EventId m_ccaLastPifs; //!< event scheduled in case of non-TX capable aux PHY to
|
||||
//!< determine whether TX can be started based on whether
|
||||
//!< the medium has been idle during the last PIFS interval
|
||||
EventId m_switchMainPhyBackEvent; //!< event scheduled in case of non-TX capable aux PHY when
|
||||
//!< medium is sensed busy during the PIFS interval
|
||||
//!< preceding/following the main PHY switch end
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
@@ -275,9 +275,6 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, MHz_u allowedWidth)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
|
||||
const auto mainPhySwitching = mainPhy->IsStateSwitching();
|
||||
|
||||
// let EMLSR manager decide whether to prevent or allow this UL TXOP
|
||||
if (auto delay = emlsrManager->GetDelayUntilAccessRequest(
|
||||
m_linkId,
|
||||
@@ -297,41 +294,10 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, MHz_u allowedWidth)
|
||||
|
||||
// in case of aux PHY that is not TX capable, the main PHY can transmit if the medium is
|
||||
// sensed idle for a PIFS after the end of channel switch (assuming main PHY is switching)
|
||||
if (m_phy != mainPhy && !emlsrManager->GetAuxPhyTxCapable())
|
||||
if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
|
||||
m_phy != mainPhy && !emlsrManager->GetAuxPhyTxCapable())
|
||||
{
|
||||
NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU");
|
||||
|
||||
if (!mainPhySwitching && mainPhy->IsStateSwitching())
|
||||
{
|
||||
// main PHY switch has been requested by GetDelayUntilAccessRequest
|
||||
const auto pifs = m_phy->GetSifs() + m_phy->GetSlot();
|
||||
auto checkMediumLastPifs = [=, this]() {
|
||||
// check if the medium has been idle for the last PIFS interval
|
||||
auto width =
|
||||
m_staMac->GetChannelAccessManager(m_linkId)->GetLargestIdlePrimaryChannel(
|
||||
pifs,
|
||||
Simulator::Now());
|
||||
|
||||
if (width == 0)
|
||||
{
|
||||
NS_LOG_DEBUG("Medium busy in the last PIFS after channel switch end");
|
||||
edca->StartAccessAfterEvent(m_linkId,
|
||||
Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
|
||||
Txop::CHECK_MEDIUM_BUSY);
|
||||
return;
|
||||
}
|
||||
|
||||
// medium idle, start a TXOP
|
||||
if (HeFrameExchangeManager::StartTransmission(edca, width))
|
||||
{
|
||||
// notify the EMLSR Manager of the UL TXOP start on an EMLSR link
|
||||
emlsrManager->NotifyUlTxopStart(m_linkId);
|
||||
}
|
||||
};
|
||||
Simulator::Schedule(mainPhy->GetDelayUntilIdle() + pifs, checkMediumLastPifs);
|
||||
}
|
||||
|
||||
NotifyChannelReleased(edca);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2386,7 +2386,8 @@ EmlsrUlTxopTest::EmlsrUlTxopTest(const Params& params)
|
||||
m_countQoSframes(0),
|
||||
m_countBlockAck(0),
|
||||
m_countRtsframes(0),
|
||||
m_genBackoffIfTxopWithoutTx(params.genBackoffIfTxopWithoutTx)
|
||||
m_genBackoffIfTxopWithoutTx(params.genBackoffAndUseAuxPhyCca),
|
||||
m_useAuxPhyCca(params.genBackoffAndUseAuxPhyCca)
|
||||
{
|
||||
m_nEmlsrStations = 1;
|
||||
m_nNonEmlsrStations = 0;
|
||||
@@ -2419,6 +2420,7 @@ EmlsrUlTxopTest::DoSetup()
|
||||
Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth",
|
||||
UintegerValue(m_auxPhyChannelWidth));
|
||||
Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false));
|
||||
Config::SetDefault("ns3::AdvancedEmlsrManager::UseAuxPhyCca", BooleanValue(m_useAuxPhyCca));
|
||||
Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration",
|
||||
TimeValue(m_mediumSyncDuration));
|
||||
Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops));
|
||||
@@ -2956,11 +2958,11 @@ EmlsrUlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
|
||||
}
|
||||
|
||||
// if the backoff on a link has expired before the end of the main PHY channel
|
||||
// switch, the main PHY will be requested to switch again a PIFS after the end
|
||||
// of the channel switch. Otherwise, it will be requested to switch when the
|
||||
// backoff expires.
|
||||
// switch, the main PHY will be requested to switch again at the first slot
|
||||
// boundary after the end of the channel switch. Otherwise, it will be requested
|
||||
// to switch when the backoff expires.
|
||||
auto expected2ndSwitchDelay = (minBackoff <= Simulator::Now())
|
||||
? mainPhy->GetSifs() + mainPhy->GetSlot()
|
||||
? mainPhy->GetSlot()
|
||||
: (minBackoff - Simulator::Now());
|
||||
|
||||
// check that the main PHY is requested to switch to a non-primary link after
|
||||
@@ -2987,11 +2989,13 @@ EmlsrUlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
|
||||
->GetChannelAccessManager(*nonPrimLinkId)
|
||||
->NeedBackoffUponAccess(acBe, true, true);
|
||||
// record the time the transmission of the QoS data frames must have
|
||||
// started: a PIFS after end of channel switch, if the backoff counter
|
||||
// on the non-primary link is null; when the backoff expires, otherwise
|
||||
// started: (a PIFS after) end of channel switch, if the backoff counter
|
||||
// on the non-primary link is null and UseAuxPhyCca is true (false); when
|
||||
// the backoff expires, otherwise
|
||||
if (auto slots = acBe->GetBackoffSlots(*nonPrimLinkId); slots == 0)
|
||||
{
|
||||
m_5thQosFrameTxTime = Simulator::Now() + mainPhy->GetPifs();
|
||||
m_5thQosFrameTxTime =
|
||||
Simulator::Now() + (m_useAuxPhyCca ? Time{0} : mainPhy->GetPifs());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3463,8 +3467,8 @@ EmlsrUlTxopTest::CheckResults()
|
||||
+m_mainPhyId,
|
||||
"Fifth QoS data frame should be transmitted on a non-primary link");
|
||||
NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
|
||||
m_channelWidth,
|
||||
"Fifth data frame not transmitted on the channel width used by main PHY");
|
||||
(m_useAuxPhyCca ? m_auxPhyChannelWidth : m_channelWidth),
|
||||
"Fifth data frame not transmitted on the correct channel width");
|
||||
// Do not check the start transmission time if a backoff is generated even when no
|
||||
// transmission is done (if the backoff expires while the main PHY is switching, a new
|
||||
// backoff is generated and, before this backoff expires, the main PHY may be requested
|
||||
@@ -4300,13 +4304,13 @@ WifiEmlsrTestSuite::WifiEmlsrTestSuite()
|
||||
TestCase::Duration::QUICK);
|
||||
}
|
||||
|
||||
for (auto genBackoffIfTxopWithoutTx : {true, false})
|
||||
for (auto genBackoffAndUseAuxPhyCca : {true, false})
|
||||
{
|
||||
AddTestCase(new EmlsrUlTxopTest(
|
||||
{{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffIfTxopWithoutTx}),
|
||||
{{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffAndUseAuxPhyCca}),
|
||||
TestCase::Duration::QUICK);
|
||||
AddTestCase(
|
||||
new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffIfTxopWithoutTx}),
|
||||
new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffAndUseAuxPhyCca}),
|
||||
TestCase::Duration::QUICK);
|
||||
}
|
||||
|
||||
|
||||
@@ -536,9 +536,13 @@ class EmlsrUlTxopTest : public EmlsrOperationsTestBase
|
||||
uint8_t msdMaxNTxops; //!< Max number of TXOPs that an EMLSR client is allowed
|
||||
//!< to attempt to initiate while the MediumSyncDelay
|
||||
//!< timer is running (zero indicates no limit)
|
||||
bool genBackoffIfTxopWithoutTx; //!< whether the backoff should be invoked when the AC
|
||||
//!< gains the right to start a TXOP but it does not
|
||||
//!< transmit any frame
|
||||
bool genBackoffAndUseAuxPhyCca; //!< this variable controls two boolean values that are
|
||||
//!< either both set to true or both set to false;
|
||||
//!< the first value controls whether the backoff should be
|
||||
//!< invoked when the AC gains the right to start a TXOP
|
||||
//!< but it does not transmit any frame, the second value
|
||||
//!< controls whether CCA info from aux PHY is used when
|
||||
//!< aux PHY is not TX capable
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -653,6 +657,8 @@ class EmlsrUlTxopTest : public EmlsrOperationsTestBase
|
||||
bool m_genBackoffIfTxopWithoutTx; //!< whether the backoff should be invoked when the AC
|
||||
//!< gains the right to start a TXOP but it does not
|
||||
//!< transmit any frame
|
||||
bool m_useAuxPhyCca; //!< whether CCA info from aux PHY is used when
|
||||
//!< aux PHY is not TX capable
|
||||
std::optional<bool> m_corruptCts; //!< whether the transmitted CTS must be corrupted
|
||||
Time m_5thQosFrameTxTime; //!< start transmission time of the 5th QoS data frame
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user