wifi: Connecting PHY to a new link is postponed if an ICF is being received

This commit is contained in:
Stefano Avallone
2024-09-30 10:26:41 +02:00
parent b90af21863
commit 4516f1347e
7 changed files with 162 additions and 52 deletions

View File

@@ -215,7 +215,7 @@ cpp_examples = [
"True",
),
(
"wifi-eht-network --simulationTime=0.3s --frequency=2.4 --frequency2=5 --frequency3=6 --guardInterval=1600 --udp=0 --downlink=1 --useRts=0 --mpduBufferSize=512 --emlsrLinks=0,1,2 --emlsrPaddingDelay=32 --emlsrTransitionDelay=32 --channelSwitchDelay=32us --emlsrAuxSwitch=True --emlsrAuxTxCapable=True --nStations=4 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=0,3,5,9,10 --minExpectedThroughput=8 --maxExpectedThroughput=300 --RngRun=2",
"wifi-eht-network --simulationTime=0.3s --frequency=2.4 --frequency2=5 --frequency3=6 --guardInterval=1600 --udp=0 --downlink=1 --useRts=0 --mpduBufferSize=512 --emlsrLinks=0,1,2 --emlsrPaddingDelay=32 --emlsrTransitionDelay=32 --channelSwitchDelay=32us --emlsrAuxSwitch=True --emlsrAuxTxCapable=True --nStations=4 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=0,3,5,9,10 --minExpectedThroughput=8 --maxExpectedThroughput=300 --RngRun=6",
"True",
"True",
),

View File

@@ -46,7 +46,6 @@ DefaultEmlsrManager::GetTypeId()
}
DefaultEmlsrManager::DefaultEmlsrManager()
: m_mainPhySwitchInfo{}
{
NS_LOG_FUNCTION(this);
}
@@ -95,15 +94,6 @@ DefaultEmlsrManager::NotifyMainPhySwitch(std::optional<uint8_t> currLinkId,
NS_LOG_FUNCTION(this << (currLinkId ? std::to_string(*currLinkId) : "") << nextLinkId << auxPhy
<< duration.As(Time::US));
// if currLinkId has no value (i.e., the main PHY is not operating on any link), it means that
// the main PHY is switching
const auto now = Simulator::Now();
NS_ASSERT_MSG(currLinkId || m_mainPhySwitchInfo.end >= now,
"No current link ID provided nor valid main PHY switch information stored");
m_mainPhySwitchInfo.from = currLinkId.value_or(m_mainPhySwitchInfo.from);
m_mainPhySwitchInfo.to = nextLinkId;
m_mainPhySwitchInfo.end = now + duration;
if (m_switchAuxPhy)
{
// cancel any previously requested aux PHY switch
@@ -119,21 +109,9 @@ DefaultEmlsrManager::NotifyMainPhySwitch(std::optional<uint8_t> currLinkId,
// schedule Aux PHY switch so that it operates on the link on which the main PHY was
// operating
NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") operating on link " << +nextLinkId
<< " will switch to link " << +currLinkId.value() << " in "
<< " will switch to link " << +m_mainPhySwitchInfo.from << " in "
<< duration.As(Time::US));
if (duration.IsStrictlyPositive())
{
m_auxPhySwitchEvent =
Simulator::Schedule(duration, [=, this, prevLinkId = m_mainPhySwitchInfo.from]() {
SwitchAuxPhy(auxPhy, nextLinkId, prevLinkId);
});
}
else
{
SwitchAuxPhy(auxPhy, nextLinkId, m_mainPhySwitchInfo.from);
}
SwitchAuxPhyAfterMainPhy(auxPhy, nextLinkId, m_mainPhySwitchInfo.from, duration);
return;
}
@@ -161,6 +139,52 @@ DefaultEmlsrManager::NotifyMainPhySwitch(std::optional<uint8_t> currLinkId,
}
}
void
DefaultEmlsrManager::SwitchAuxPhyAfterMainPhy(Ptr<WifiPhy> auxPhy,
uint8_t currLinkId,
uint8_t nextLinkId,
Time duration)
{
NS_LOG_FUNCTION(this << auxPhy << currLinkId << nextLinkId << duration.As(Time::US));
if (duration.IsStrictlyPositive())
{
auto lambda = [=, this]() {
if (GetStaMac()->GetWifiPhy(currLinkId) == auxPhy)
{
// the aux PHY is still operating on the link, likely because it is receiving a
// PPDU and connecting the main PHY to the link has been postponed
const auto [maybeIcf, extension] = CheckPossiblyReceivingIcf(currLinkId);
if (maybeIcf && extension.IsStrictlyPositive())
{
NS_LOG_DEBUG("Switching aux PHY to link " << +nextLinkId << " is postponed by "
<< extension.As(Time::US));
SwitchAuxPhyAfterMainPhy(auxPhy, currLinkId, nextLinkId, extension);
return;
}
}
const auto isSleeping = auxPhy->IsStateSleep();
if (isSleeping)
{
// if the aux PHY is sleeping, it cannot switch channel
auxPhy->ResumeFromSleep();
}
SwitchAuxPhy(auxPhy, currLinkId, nextLinkId);
if (isSleeping)
{
// sleep mode will be postponed until the end of channel switch
auxPhy->SetSleepMode(true);
}
};
m_auxPhySwitchEvent = Simulator::Schedule(duration, lambda);
}
else
{
SwitchAuxPhy(auxPhy, currLinkId, nextLinkId);
}
}
std::pair<bool, Time>
DefaultEmlsrManager::DoGetDelayUntilAccessRequest(uint8_t linkId)
{

View File

@@ -75,26 +75,34 @@ class DefaultEmlsrManager : public EmlsrManager
*/
void SwitchMainPhyBackToPreferredLink(uint8_t linkId, EmlsrMainPhySwitchTrace&& traceInfo);
/// Store information about a main PHY switch.
struct MainPhySwitchInfo
{
Time end; //!< end of channel switching
uint8_t from; //!< ID of the link which the main PHY is/has been leaving
uint8_t to; //!< ID of the link which the main PHY is moving to
};
bool m_switchAuxPhy; /**< whether Aux PHY should switch channel to operate on the link on which
the Main PHY was operating before moving to the link of the Aux PHY */
Ptr<WifiPhy> m_auxPhyToReconnect; //!< Aux PHY the ChannelAccessManager of the link on which
//!< the main PHY is operating has to connect a listener to
//!< when the main PHY is back operating on its previous link
EventId m_auxPhySwitchEvent; //!< event scheduled for an aux PHY to switch link
MainPhySwitchInfo m_mainPhySwitchInfo; //!< main PHY switch info
std::map<uint8_t, Time> m_switchMainPhyOnRtsTx; //!< link ID-indexed map of the time when an RTS
//!< that requires the main PHY to switch link
//!< is expected to be transmitted on the link
private:
/**
* This function shall be called when the main PHY starts switching to a link on which an aux
* PHY that is capable of switching link is operating. This function schedules the aux PHY
* switch to occur when the main PHY completes the switch and, in case the connection of the
* main PHY to the aux PHY link is postponed because the aux PHY is receiving a PPDU, the
* aux PHY switch is postponed accordingly.
*
* @param auxPhy the aux PHY that has to switch link
* @param currLinkId the link on which the aux PHY is operating
* @param nextLinkId the link to which the aux PHY will switch
* @param duration the remaining time until the aux PHY switch starts
*/
void SwitchAuxPhyAfterMainPhy(Ptr<WifiPhy> auxPhy,
uint8_t currLinkId,
uint8_t nextLinkId,
Time duration);
void DoNotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId) override;
void NotifyMainPhySwitch(std::optional<uint8_t> currLinkId,
uint8_t nextLinkId,

View File

@@ -143,9 +143,10 @@ EmlsrManager::GetTypeId()
}
EmlsrManager::EmlsrManager()
// The STA initializes dot11MSDTimerDuration to aPPDUMaxTime defined in Table 36-70
// (Sec. 35.3.16.8.1 of 802.11be D3.1)
: m_mediumSyncDuration(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
: m_mainPhySwitchInfo{},
// The STA initializes dot11MSDTimerDuration to aPPDUMaxTime defined in Table 36-70
// (Sec. 35.3.16.8.1 of 802.11be D3.1)
m_mediumSyncDuration(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
// The default value of dot11MSDOFDMEDthreshold is 72 dBm and the default value of
// dot11MSDTXOPMax is 1, respectively (Sec. 35.3.16.8.1 of 802.11be D3.1)
m_msdOfdmEdThreshold(DEFAULT_MSD_OFDM_ED_THRESH),
@@ -449,19 +450,53 @@ EmlsrManager::NotifyIcfReceived(uint8_t linkId)
}
auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
auto auxPhy = m_staMac->GetWifiPhy(linkId);
auto rxPhy = m_staMac->GetWifiPhy(linkId);
if (m_staMac->GetWifiPhy(linkId) != mainPhy)
const auto receivedByAuxPhy = (rxPhy != mainPhy);
const auto mainPhyOnALink = (m_staMac->GetLinkForPhy(mainPhy).has_value());
const auto mainPhyIsSwitching =
(mainPhy->GetState()->GetLastTime({WifiPhyState::SWITCHING}) == Simulator::Now());
// if the main PHY is not operating on a link and it is not switching, then we have postponed
// the reconnection of the main PHY to a link because a PPDU reception was ongoing on that link
const auto mainPhyToConnect = (!mainPhyOnALink && !mainPhyIsSwitching);
const auto mainPhyToConnectToOtherLink = mainPhyToConnect && (m_mainPhySwitchInfo.to != linkId);
if (mainPhyToConnect)
{
// If ICF was received on a link other than the one the main PHY is waiting to be connected
// to, we need to cancel the pending reconnection and request a new main PHY switch.
// If ICF was received on the link the main PHY is waiting to be connected to, we cancel
// the pending reconnection and explicitly request the reconnection below
GetStaMac()->CancelEmlsrPhyConnectEvent(mainPhy->GetPhyId());
}
// We need to request a main PHY switch if:
// - the ICF was received by an aux PHY, AND
// - the main PHY is not waiting to be connected to a link OR it is waiting to be connected
// to a link other than the link on which the ICF is received.
if (receivedByAuxPhy && (!mainPhyToConnect || mainPhyToConnectToOtherLink))
{
// an aux PHY received the ICF
SwitchMainPhy(linkId,
true, // channel switch should occur instantaneously
RESET_BACKOFF,
DONT_REQUEST_ACCESS,
EmlsrDlTxopIcfReceivedByAuxPhyTrace{});
}
else if (mainPhyToConnect && !mainPhyToConnectToOtherLink)
{
// If the main PHY is waiting to be connected to the link on which the ICF was received, we
// have to explicitly perform the connection because the pending event was cancelled above.
// We do this way in order to reconnect the main PHY before putting aux PHYs to sleep: if
// the aux PHY is put to sleep while still operating on the link on which it received the
// ICF, all the MAC events (including scheduled CTS transmission) will be cancelled.
m_staMac->NotifySwitchingEmlsrLink(mainPhy, linkId, Time{0});
}
if (receivedByAuxPhy)
{
// aux PHY received the ICF but main PHY will send the response
auto uid = auxPhy->GetPreviouslyRxPpduUid();
auto uid = rxPhy->GetPreviouslyRxPpduUid();
mainPhy->SetPreviouslyRxPpduUid(uid);
}
@@ -784,9 +819,6 @@ EmlsrManager::SwitchMainPhy(uint8_t linkId,
traceInfo.toLinkId = linkId;
m_mainPhySwitchTrace(traceInfo);
NS_ASSERT_MSG(currMainPhyLinkId.has_value() || mainPhy->IsStateSwitching(),
"If the main PHY is not operating on a link, it must be switching");
const auto newMainPhyChannel = GetChannelForMainPhy(linkId);
NS_LOG_DEBUG("Main PHY (" << mainPhy << ") is about to switch to " << newMainPhyChannel
@@ -861,6 +893,10 @@ EmlsrManager::SwitchMainPhy(uint8_t linkId,
});
}
m_mainPhySwitchInfo.from = currMainPhyLinkId.value_or(m_mainPhySwitchInfo.from);
m_mainPhySwitchInfo.to = linkId;
m_mainPhySwitchInfo.end = Simulator::Now() + timeToSwitchEnd;
SetCcaEdThresholdOnLinkSwitch(mainPhy, linkId);
NotifyMainPhySwitch(currMainPhyLinkId, linkId, auxPhy, timeToSwitchEnd);
}

View File

@@ -509,6 +509,14 @@ class EmlsrManager : public Object
*/
bool GetExpectedAccessWithinDelay(uint8_t linkId, const Time& delay) const;
/// Store information about a main PHY switch.
struct MainPhySwitchInfo
{
Time end; //!< end of channel switching
uint8_t from{}; //!< ID of the link which the main PHY is/has been leaving
uint8_t to{}; //!< ID of the link which the main PHY is moving to
};
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)
@@ -525,6 +533,7 @@ class EmlsrManager : public Object
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
MainPhySwitchInfo m_mainPhySwitchInfo; //!< main PHY switch info
private:
/**

View File

@@ -2105,17 +2105,32 @@ StaWifiMac::NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId, Time dela
};
// cancel any pending event for the given PHY to switch link
if (auto eventIt = m_emlsrLinkSwitch.find(phy->GetPhyId()); eventIt != m_emlsrLinkSwitch.end())
{
eventIt->second.Cancel();
m_emlsrLinkSwitch.erase(eventIt);
}
CancelEmlsrPhyConnectEvent(phy->GetPhyId());
// connect the PHY to the new link when the channel switch is completed, so that the PHY
// operating on the new link can possibly continue receiving frames in the meantime.
// connect the PHY to the new link when the channel switch is completed, unless there is a PHY
// operating on the new link that is possibly receiving an ICF, in which case the PHY is
// connected when the frame reception is completed
if (delay.IsStrictlyPositive())
{
m_emlsrLinkSwitch.emplace(phy->GetPhyId(), Simulator::Schedule(delay, connectPhy));
auto lambda = [=, this]() mutable {
const auto [maybeIcf, extension] = m_emlsrManager->CheckPossiblyReceivingIcf(linkId);
if (maybeIcf && extension.IsStrictlyPositive())
{
NS_ASSERT_MSG(phy->GetPhyId() == m_emlsrManager->GetMainPhyId(),
"Only the main PHY is expected to move to a link on which another "
"PHY is operating. PHY ID="
<< +phy->GetPhyId());
NS_LOG_DEBUG("Connecting main PHY to link " << +linkId << " is postponed by "
<< extension.As(Time::US));
NotifySwitchingEmlsrLink(phy, linkId, extension);
}
else
{
connectPhy();
}
};
m_emlsrLinkSwitch.emplace(phy->GetPhyId(), Simulator::Schedule(delay, lambda));
}
else
{
@@ -2123,6 +2138,17 @@ StaWifiMac::NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId, Time dela
}
}
void
StaWifiMac::CancelEmlsrPhyConnectEvent(uint8_t phyId)
{
NS_LOG_FUNCTION(this << phyId);
if (auto eventIt = m_emlsrLinkSwitch.find(phyId); eventIt != m_emlsrLinkSwitch.end())
{
eventIt->second.Cancel();
m_emlsrLinkSwitch.erase(eventIt);
}
}
void
StaWifiMac::NotifyChannelSwitching(uint8_t linkId)
{

View File

@@ -321,6 +321,13 @@ class StaWifiMac : public WifiMac
*/
void NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId, Time delay);
/**
* Cancel any scheduled event for connecting the given PHY to an EMLSR link.
*
* @param phyId the ID of the given PHY
*/
void CancelEmlsrPhyConnectEvent(uint8_t phyId);
/**
* Block transmissions on the given link for the given reason.
*