wifi: Modularize code handling UL TXOP start

This commit is contained in:
Stefano Avallone
2024-03-13 16:30:41 +01:00
parent 0e631b08e3
commit 95d19f033c
9 changed files with 215 additions and 153 deletions

View File

@@ -89,7 +89,7 @@ AdvancedEmlsrManager::DoSetWifiMac(Ptr<StaWifiMac> mac)
}
Time
AdvancedEmlsrManager::GetDelayUntilAccessRequest(uint8_t linkId)
AdvancedEmlsrManager::DoGetDelayUntilAccessRequest(uint8_t linkId)
{
NS_LOG_FUNCTION(this << linkId);

View File

@@ -31,11 +31,10 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager
AdvancedEmlsrManager();
~AdvancedEmlsrManager() override;
Time GetDelayUntilAccessRequest(uint8_t linkId) override;
protected:
void DoDispose() override;
void DoSetWifiMac(Ptr<StaWifiMac> mac) override;
Time DoGetDelayUntilAccessRequest(uint8_t linkId) override;
/**
* Possibly take actions when notified of the MAC header of the MPDU being received by the

View File

@@ -172,7 +172,7 @@ DefaultEmlsrManager::NotifyMainPhySwitch(std::optional<uint8_t> currLinkId,
}
Time
DefaultEmlsrManager::GetDelayUntilAccessRequest(uint8_t linkId)
DefaultEmlsrManager::DoGetDelayUntilAccessRequest(uint8_t linkId)
{
NS_LOG_FUNCTION(this << linkId);
return Time{0}; // start the TXOP
@@ -228,7 +228,7 @@ DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId)
}
}
bool
void
DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId)
{
NS_LOG_FUNCTION(this << linkId);
@@ -280,7 +280,7 @@ DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId)
// switch main PHY
SwitchMainPhy(linkId, false, RESET_BACKOFF, REQUEST_ACCESS);
return true;
return;
}
// Determine if and when we need to request channel access again for the aux PHY based on
@@ -307,7 +307,7 @@ DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId)
if (!mainPhy->IsStateSwitching() && !mainPhy->IsStateCcaBusy())
{
NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState() << ". Do nothing");
return false;
return;
}
auto delay = mainPhy->GetDelayUntilIdle();
@@ -329,8 +329,78 @@ DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId)
}
}
});
}
return false;
Time
DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId) const
{
NS_LOG_FUNCTION(this << linkId);
auto phy = GetStaMac()->GetWifiPhy(linkId);
NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId);
// we have to check whether the main PHY can switch to take over the UL TXOP
const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId);
const auto bssid = GetEhtFem(linkId)->GetBssid();
const auto allowedWidth = GetEhtFem(linkId)->GetAllowedWidth();
const auto rtsTxVector = stationManager->GetRtsTxVector(bssid, allowedWidth);
const auto rtsTxTime = phy->CalculateTxDuration(GetRtsSize(), rtsTxVector, phy->GetPhyBand());
const auto ctsTxVector = stationManager->GetCtsTxVector(bssid, rtsTxVector.GetMode());
const auto ctsTxTime = phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, phy->GetPhyBand());
// the main PHY shall terminate the channel switch at the end of CTS reception;
// the time remaining to the end of CTS reception includes two propagation delays
return rtsTxTime + phy->GetSifs() + ctsTxTime + MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC);
}
Time
DefaultEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)
{
NS_LOG_FUNCTION(this << linkId);
auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
auto switchingTime = mainPhy->GetChannelSwitchDelay();
switch (mainPhy->GetState()->GetState())
{
case WifiPhyState::SWITCHING:
// the main PHY is switching (to another link), hence the remaining time to
// the end of the current channel switch needs to be added up
switchingTime += mainPhy->GetDelayUntilIdle();
[[fallthrough]];
case WifiPhyState::RX:
case WifiPhyState::IDLE:
case WifiPhyState::CCA_BUSY:
if (switchingTime > timeToCtsEnd)
{
// switching takes longer than RTS/CTS exchange, release channel
NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
<< mainPhy->GetState()->GetState() << ")");
// retry channel access when the CTS was expected to be received
return timeToCtsEnd;
}
break;
default:
NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState()->GetState());
}
// TXOP can be started, schedule main PHY switch. Main PHY shall terminate the channel switch
// at the end of CTS reception
const auto delay = timeToCtsEnd - mainPhy->GetChannelSwitchDelay();
NS_ASSERT(delay.IsPositive());
NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay,
&DefaultEmlsrManager::SwitchMainPhy,
this,
linkId,
false,
RESET_BACKOFF,
DONT_REQUEST_ACCESS);
return Time{0};
}
} // namespace ns3

View File

@@ -33,17 +33,21 @@ class DefaultEmlsrManager : public EmlsrManager
DefaultEmlsrManager();
~DefaultEmlsrManager() override;
bool SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) override;
/**
* \param linkId the ID of the link on which TXOP is gained
* \return zero, indicating that the TXOP can be started
*/
Time GetDelayUntilAccessRequest(uint8_t linkId) override;
protected:
uint8_t GetLinkToSendEmlOmn() override;
std::optional<uint8_t> ResendNotification(Ptr<const WifiMpdu> mpdu) override;
Time DoGetDelayUntilAccessRequest(uint8_t linkId) override;
void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) override;
Time GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) override;
/**
* This function is intended to be called when an aux PHY is about to transmit an RTS on
* the given link to calculate the time remaining to the end of the CTS reception.
*
* \param linkId the ID of the given link
* \return the time remaining to the end of the CTS reception
*/
Time GetTimeToCtsEnd(uint8_t linkId) const;
/// Store information about a main PHY switch.
struct MainPhySwitchInfo

View File

@@ -169,6 +169,8 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, MHz_u allowedWidth)
{
NS_LOG_FUNCTION(this << edca << allowedWidth);
m_allowedWidth = allowedWidth;
if (m_apMac)
{
for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
@@ -273,6 +275,9 @@ 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);
delay.IsStrictlyPositive())
@@ -288,98 +293,44 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, MHz_u allowedWidth)
return false;
}
if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
mainPhy != m_phy)
// 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())
{
// an aux PHY is operating on this link
NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU");
if (!emlsrManager->GetAuxPhyTxCapable())
if (!mainPhySwitching && mainPhy->IsStateSwitching())
{
NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU");
// 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 (emlsrManager->SwitchMainPhyIfTxopGainedByAuxPhy(m_linkId))
{
NS_ASSERT_MSG(mainPhy->IsStateSwitching(),
"SwitchMainPhyIfTxopGainedByAuxPhy returned true but main PHY is "
"not switching");
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;
}
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, std::nullopt);
}
};
Simulator::Schedule(mainPhy->GetDelayUntilIdle() + pifs, checkMediumLastPifs);
}
NotifyChannelReleased(edca);
return false;
// 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);
}
// we have to check whether the main PHY can switch to take over the UL TXOP
const auto rtsTxVector =
GetWifiRemoteStationManager()->GetRtsTxVector(m_bssid, allowedWidth);
const auto rtsTxTime =
m_phy->CalculateTxDuration(GetRtsSize(), rtsTxVector, m_phy->GetPhyBand());
const auto ctsTxVector =
GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, rtsTxVector.GetMode());
const auto ctsTxTime =
m_phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand());
// the main PHY shall terminate the channel switch at the end of CTS reception;
// the time remaining to the end of CTS reception includes two propagation delays
timeToCtsEnd = rtsTxTime + m_phy->GetSifs() + ctsTxTime +
MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC);
auto switchingTime = mainPhy->GetChannelSwitchDelay();
switch (mainPhy->GetState()->GetState())
{
case WifiPhyState::SWITCHING:
// the main PHY is switching (to another link), hence the remaining time to
// the end of the current channel switch needs to be added up
switchingTime += mainPhy->GetDelayUntilIdle();
[[fallthrough]];
case WifiPhyState::RX:
case WifiPhyState::IDLE:
case WifiPhyState::CCA_BUSY:
if (switchingTime <= timeToCtsEnd)
{
break; // start TXOP
}
// switching takes longer than RTS/CTS exchange, release channel
NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
<< mainPhy->GetState() << ")");
// retry channel access when the CTS was expected to be received
NotifyChannelReleased(edca);
Simulator::Schedule(*timeToCtsEnd,
&Txop::StartAccessAfterEvent,
edca,
m_linkId,
Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be
// transmitted now
Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy
return false;
default:
NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState());
}
NotifyChannelReleased(edca);
return false;
}
}
@@ -389,7 +340,7 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, MHz_u allowedWidth)
{
// notify the EMLSR Manager of the UL TXOP start on an EMLSR link
NS_ASSERT(m_staMac->GetEmlsrManager());
m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId, timeToCtsEnd);
m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId);
}
if (started)

View File

@@ -408,8 +408,43 @@ EmlsrManager::NotifyIcfReceived(uint8_t linkId)
});
}
Time
EmlsrManager::GetDelayUntilAccessRequest(uint8_t linkId)
{
auto phy = m_staMac->GetWifiPhy(linkId);
NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId);
auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
// check possible reasons to give up the TXOP that apply to both main PHY and aux PHYs
if (auto delay = DoGetDelayUntilAccessRequest(linkId); delay.IsStrictlyPositive())
{
return delay;
}
if (phy == mainPhy)
{
// no more constraints to check if medium was gained by main PHY
return Time{0};
}
// an aux PHY is operating on the given link; call the appropriate method depending on
// whether the aux PHY is TX capable or not
if (!m_auxPhyTxCapable)
{
SwitchMainPhyIfTxopGainedByAuxPhy(linkId);
// if the aux PHY is not TX capable, we don't have to request channel access: if the main
// PHY switches link, the UL TXOP will be started; if the main PHY does not switch, it is
// because it is going to start an UL TXOP on another link and this link will be restarted
// at the end of that UL TXOP when this link will be unblocked
return Time{0};
}
return GetDelayUnlessMainPhyTakesOverUlTxop(linkId);
}
void
EmlsrManager::NotifyUlTxopStart(uint8_t linkId, std::optional<Time> timeToCtsEnd)
EmlsrManager::NotifyUlTxopStart(uint8_t linkId)
{
NS_LOG_FUNCTION(this << linkId);
@@ -428,35 +463,6 @@ EmlsrManager::NotifyUlTxopStart(uint8_t linkId, std::optional<Time> timeToCtsEnd
}
}
// if this TXOP is being started by an aux PHY, schedule a channel switch for the main PHY
// such that the channel switch is completed by the time the CTS response is received. The
// delay has been passed by the FEM.
if (m_staMac->GetLinkForPhy(m_mainPhyId) != linkId)
{
auto stateHelper = m_staMac->GetWifiPhy(linkId)->GetState();
NS_ASSERT(stateHelper);
NS_ASSERT_MSG(stateHelper->GetState() == WifiPhyState::TX,
"Expecting the aux PHY to be transmitting (an RTS frame)");
NS_ASSERT_MSG(timeToCtsEnd.has_value(),
"Aux PHY is sending RTS, expected to get the time to CTS end");
auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
// the main PHY shall terminate the channel switch at the end of CTS reception;
// the time remaining to the end of CTS reception includes two propagation delays
const auto delay = *timeToCtsEnd - mainPhy->GetChannelSwitchDelay();
NS_ASSERT(delay.IsPositive());
NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay,
&EmlsrManager::SwitchMainPhy,
this,
linkId,
false,
RESET_BACKOFF,
DONT_REQUEST_ACCESS);
}
DoNotifyUlTxopStart(linkId);
}

View File

@@ -137,14 +137,14 @@ class EmlsrManager : public Object
bool GetCamStateReset() const;
/**
* Notify that a TXOP is gained on the given link. This method has to determine whether to
* start the TXOP or release the channel.
* Notify that an UL TXOP is gained on the given link. This method has to determine whether to
* start the UL TXOP or release the channel.
*
* \param linkId the ID of the given link
* \return zero, if the TXOP can be started, or the delay after which the EMLSR restarts
* channel access, otherwise
* \return zero, if the UL TXOP can be started, or the delay after which the EMLSR client
* restarts channel access, otherwise
*/
virtual Time GetDelayUntilAccessRequest(uint8_t linkId) = 0;
Time GetDelayUntilAccessRequest(uint8_t linkId);
/**
* Set the member variable indicating whether Aux PHYs are capable of transmitting PPDUs.
@@ -192,10 +192,8 @@ class EmlsrManager : public Object
* Notify the start of an UL TXOP on the given link
*
* \param linkId the ID of the given link
* \param timeToCtsEnd time remaining to the end of CTS reception, in case the UL TXOP is
* started by an aux PHY
*/
void NotifyUlTxopStart(uint8_t linkId, std::optional<Time> timeToCtsEnd);
void NotifyUlTxopStart(uint8_t linkId);
/**
* Notify the end of a TXOP on the given link.
@@ -208,16 +206,6 @@ class EmlsrManager : public Object
*/
void NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted = false, bool ongoingDlTxop = false);
/**
* This method is intended to notify the EMLSR Manager that an aux PHY that is NOT TX capable
* has gained a TXOP on a given link and returns whether the main PHY has been requested to
* switch to the given link to take over the TXOP.
*
* \param linkId the ID of the given link
* \return whether main PHY has been requested to switch
*/
virtual bool SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) = 0;
/**
* Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
* If so, returns the time elapsed since the timer started.
@@ -357,12 +345,49 @@ class EmlsrManager : public Object
*/
MgtEmlOmn GetEmlOmn();
/**
* Subclasses have to provide an implementation for this method, that is called by the base
* class when the EMLSR client gets channel access on the given link. This method has to
* check possible reasons to give up the TXOP that apply to both main PHY and aux PHYs.
*
* \param linkId the ID of the given link
* \return zero, if the UL TXOP can be started, or the delay after which the EMLSR client
* restarts channel access, otherwise
*/
virtual Time DoGetDelayUntilAccessRequest(uint8_t linkId) = 0;
/**
* Subclasses have to provide an implementation for this method, that is called by the base
* class when the EMLSR client gets channel access on the given link, on which an aux PHY that
* is not TX capable is operating. This method has to request the main PHY to switch to the
* given link to take over the TXOP, unless it is decided to give up the TXOP.
*
* \param linkId the ID of the given link
*/
virtual void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) = 0;
/**
* Subclasses have to provide an implementation for this method, that is called by the base
* class when the EMLSR client gets channel access on the given link, on which an aux PHY that
* is TX capable is operating. This method has to request the main PHY to switch to the
* given link to take over the TXOP, if possible, or determine the delay after which the
* EMLSR client restarts channel access on the given link, otherwise.
*
* \param linkId the ID of the given link
* \return zero, if the UL TXOP can be started, or the delay after which the EMLSR client
* restarts channel access, otherwise
*/
virtual Time GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) = 0;
Time m_emlsrPaddingDelay; //!< EMLSR Padding delay
Time m_emlsrTransitionDelay; //!< EMLSR Transition delay
uint8_t m_mainPhyId; //!< ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
MHz_u m_auxPhyMaxWidth; //!< max channel width supported by aux PHYs
WifiModulationClass m_auxPhyMaxModClass; //!< max modulation class supported by aux PHYs
bool m_auxPhyTxCapable; //!< whether Aux PHYs are capable of transmitting PPDUs
std::map<uint8_t, EventId> m_ulMainPhySwitch; //!< link ID-indexed map of timers started when
//!< an aux PHY gains an UL TXOP and schedules
//!< a channel switch for the main PHY
private:
/**
@@ -514,9 +539,6 @@ class EmlsrManager : public Object
m_mainPhyChannels; //!< link ID-indexed map of operating channels for the main PHY
std::map<uint8_t, WifiPhyOperatingChannel>
m_auxPhyChannels; //!< link ID-indexed map of operating channels for the aux PHYs
std::map<uint8_t, EventId> m_ulMainPhySwitch; //!< link ID-indexed map of timers started when
//!< an aux PHY gains an UL TXOP and schedules
//!< a channel switch for the main PHY
};
} // namespace ns3

View File

@@ -226,6 +226,12 @@ FrameExchangeManager::GetBssid() const
return m_bssid;
}
MHz_u
FrameExchangeManager::GetAllowedWidth() const
{
return m_allowedWidth;
}
void
FrameExchangeManager::SetDroppedMpduCallback(DroppedMpdu callback)
{

View File

@@ -200,6 +200,10 @@ class FrameExchangeManager : public Object
* \return the BSSID
*/
Mac48Address GetBssid() const;
/**
* \return the width of the channel that the FEM is allowed to use for the current transmission
*/
MHz_u GetAllowedWidth() const;
/**
* Set the callback to invoke when an MPDU is dropped.
*