wifi: Connecting PHY to a new link is postponed if an ICF is being received
This commit is contained in:
@@ -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",
|
||||
),
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user