wifi: Improve logic for main PHY to start TXOP with non-TX capable aux PHY

This commit is contained in:
Stefano Avallone
2024-03-29 19:12:38 +01:00
parent b9c871bbb3
commit d05d899523
5 changed files with 317 additions and 133 deletions

View File

@@ -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);
});
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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
};