A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
default-emlsr-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
12
13#include "ns3/boolean.h"
14#include "ns3/channel-access-manager.h"
15#include "ns3/log.h"
16#include "ns3/qos-txop.h"
17#include "ns3/wifi-mpdu.h"
18#include "ns3/wifi-net-device.h"
19#include "ns3/wifi-phy.h"
20
21namespace ns3
22{
23
24NS_LOG_COMPONENT_DEFINE("DefaultEmlsrManager");
25
26NS_OBJECT_ENSURE_REGISTERED(DefaultEmlsrManager);
27
28TypeId
30{
31 static TypeId tid =
32 TypeId("ns3::DefaultEmlsrManager")
34 .SetGroupName("Wifi")
35 .AddConstructor<DefaultEmlsrManager>()
36 .AddAttribute("SwitchAuxPhy",
37 "Whether Aux PHY should switch channel to operate on the link on which "
38 "the Main PHY was operating before moving to the link of the Aux PHY. "
39 "Note that, if the Aux PHY does not switch channel, the main PHY will "
40 "switch back to its previous link once the TXOP terminates (otherwise, "
41 "no PHY will be listening on that EMLSR link).",
42 BooleanValue(true),
45 return tid;
46}
47
52
57
58void
60{
61 NS_LOG_FUNCTION(this << *mpdu << linkId);
62}
63
64uint8_t
66{
67 NS_LOG_FUNCTION(this);
68 auto linkId = GetStaMac()->GetLinkForPhy(m_mainPhyId);
69 NS_ASSERT_MSG(linkId, "Link on which the main PHY is operating not found");
70 return *linkId;
71}
72
73std::optional<uint8_t>
75{
76 NS_LOG_FUNCTION(this);
77 auto linkId = GetStaMac()->GetLinkForPhy(m_mainPhyId);
78 NS_ASSERT_MSG(linkId, "Link on which the main PHY is operating not found");
79 return *linkId;
80}
81
82void
87
88void
89DefaultEmlsrManager::NotifyMainPhySwitch(std::optional<uint8_t> currLinkId,
90 uint8_t nextLinkId,
91 Ptr<WifiPhy> auxPhy,
92 Time duration)
93{
94 NS_LOG_FUNCTION(this << (currLinkId ? std::to_string(*currLinkId) : "") << nextLinkId << auxPhy
95 << duration.As(Time::US));
96
98 {
99 // cancel any previously requested aux PHY switch
101
102 if (nextLinkId == m_mainPhySwitchInfo.from)
103 {
104 // the main PHY is now switching to the link where it is coming from; nothing else
105 // needs to be done
106 return;
107 }
108
109 // schedule Aux PHY switch so that it operates on the link on which the main PHY was
110 // operating
111 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") operating on link " << +nextLinkId
112 << " will switch to link " << +m_mainPhySwitchInfo.from << " in "
113 << duration.As(Time::US));
114 SwitchAuxPhyAfterMainPhy(auxPhy, nextLinkId, m_mainPhySwitchInfo.from, duration);
115 return;
116 }
117
118 if (currLinkId.has_value() && currLinkId != GetMainPhyId())
119 {
120 // the main PHY is leaving an auxiliary link, hence an aux PHY needs to be reconnected
123 "There should be an aux PHY to reconnect when the main PHY leaves an auxiliary link");
124
125 // the Aux PHY is not actually switching (hence no switching delay)
126 GetStaMac()->NotifySwitchingEmlsrLink(m_auxPhyToReconnect, *currLinkId, Seconds(0));
128 }
129
130 // if currLinkId has no value, it means that the main PHY switch is interrupted, hence reset
131 // the aux PHY to reconnect. Doing so when the main PHY is leaving the preferred link makes
132 // no harm (the aux PHY to reconnect is set below), thus no need to add an 'if' condition
133 m_auxPhyToReconnect = nullptr;
134
135 if (nextLinkId != GetMainPhyId())
136 {
137 // the main PHY is moving to an auxiliary link and the aux PHY does not switch link
138 m_auxPhyToReconnect = auxPhy;
139 }
140}
141
142void
144 uint8_t currLinkId,
145 uint8_t nextLinkId,
146 Time duration)
147{
148 NS_LOG_FUNCTION(this << auxPhy << currLinkId << nextLinkId << duration.As(Time::US));
149
150 if (duration.IsStrictlyPositive())
151 {
152 auto lambda = [=, this]() {
153 if (GetStaMac()->GetWifiPhy(currLinkId) == auxPhy)
154 {
155 // the aux PHY is still operating on the link, likely because it is receiving a
156 // PPDU and connecting the main PHY to the link has been postponed
157 const auto [maybeIcf, extension] = CheckPossiblyReceivingIcf(currLinkId);
158 if (maybeIcf && extension.IsStrictlyPositive())
159 {
160 NS_LOG_DEBUG("Switching aux PHY to link " << +nextLinkId << " is postponed by "
161 << extension.As(Time::US));
162 SwitchAuxPhyAfterMainPhy(auxPhy, currLinkId, nextLinkId, extension);
163 return;
164 }
165 }
166 const auto isSleeping = auxPhy->IsStateSleep();
167 if (isSleeping)
168 {
169 // if the aux PHY is sleeping, it cannot switch channel
170 auxPhy->ResumeFromSleep();
171 }
172 SwitchAuxPhy(auxPhy, currLinkId, nextLinkId);
173 if (isSleeping)
174 {
175 // sleep mode will be postponed until the end of channel switch
176 auxPhy->SetSleepMode(true);
177 }
178 };
179
180 m_auxPhySwitchEvent = Simulator::Schedule(duration, lambda);
181 }
182 else
183 {
184 SwitchAuxPhy(auxPhy, currLinkId, nextLinkId);
185 }
186}
187
188std::pair<bool, Time>
190{
191 NS_LOG_FUNCTION(this << linkId);
192 return {true, Time{0}}; // start the TXOP
193}
194
195void
197{
198 NS_LOG_FUNCTION(this << linkId);
199}
200
201void
203{
204 NS_LOG_FUNCTION(this << linkId);
205}
206
207void
209{
210 NS_LOG_FUNCTION(this << linkId);
211
212 // switch main PHY to the previous link, if needed
213 if (!m_switchAuxPhy)
214 {
215 const auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
216 const auto delay = mainPhy->IsStateSwitching() ? mainPhy->GetDelayUntilIdle() : Time{0};
218 }
219}
220
221void
223 EmlsrMainPhySwitchTrace&& traceInfo)
224{
225 NS_LOG_FUNCTION(this << linkId << traceInfo.GetName());
226
227 NS_ABORT_MSG_IF(m_switchAuxPhy, "This method can only be called when SwitchAuxPhy is false");
228
230 {
231 return;
232 }
233
234 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
235
236 // the main PHY may be switching at the end of a TXOP when, e.g., the main PHY starts
237 // switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
238 // is not received and the UL TXOP ends before the main PHY channel switch is completed.
239 // In such cases, wait until the main PHY channel switch is completed before requesting
240 // a new channel switch.
241 // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and
242 // a new backoff value must be generated.
243 if (!mainPhy->IsStateSwitching())
244 {
246 false,
249 std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
250 }
251 else
252 {
253 Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, info = traceInfo.Clone(), this]() {
254 // request the main PHY to switch back to the preferred link only if
255 // in the meantime no TXOP started on another link (which will
256 // require the main PHY to switch link)
257 if (!GetEhtFem(linkId)->UsingOtherEmlsrLink())
258 {
259 SwitchMainPhy(GetMainPhyId(),
260 false,
261 DONT_RESET_BACKOFF,
262 REQUEST_ACCESS,
263 std::move(*info));
264 }
265 });
266 }
267}
268
269void
270DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci)
271{
272 NS_LOG_FUNCTION(this << linkId << aci);
273 NS_LOG_DEBUG("Do nothing, aux PHY is not TX capable");
274}
275
276Time
277DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId) const
278{
279 NS_LOG_FUNCTION(this << linkId);
280
281 const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId);
282 const auto bssid = GetEhtFem(linkId)->GetBssid();
283 const auto allowedWidth = GetEhtFem(linkId)->GetAllowedWidth();
284
285 return GetTimeToCtsEnd(linkId, stationManager->GetRtsTxVector(bssid, allowedWidth));
286}
287
288Time
289DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId, const WifiTxVector& rtsTxVector) const
290{
291 NS_LOG_FUNCTION(this << linkId << rtsTxVector);
292
293 auto phy = GetStaMac()->GetWifiPhy(linkId);
294 NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId);
295
296 const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId);
297 const auto bssid = GetEhtFem(linkId)->GetBssid();
298 const auto ctsTxVector = stationManager->GetCtsTxVector(bssid, rtsTxVector.GetMode());
299
300 const auto rtsTxTime =
301 WifiPhy::CalculateTxDuration(GetRtsSize(), rtsTxVector, phy->GetPhyBand());
302 const auto ctsTxTime =
303 WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, phy->GetPhyBand());
304
305 // the main PHY shall terminate the channel switch at the end of CTS reception;
306 // the time remaining to the end of CTS reception includes two propagation delays
307 return rtsTxTime + phy->GetSifs() + ctsTxTime + MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC);
308}
309
310std::pair<bool, Time>
311DefaultEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)
312{
313 NS_LOG_FUNCTION(this << linkId);
314
315 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
316 const auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
317 auto switchingTime = mainPhy->GetChannelSwitchDelay();
318
319 switch (mainPhy->GetState()->GetState())
320 {
321 case WifiPhyState::SWITCHING:
322 // the main PHY is switching (to another link), hence the remaining time to
323 // the end of the current channel switch needs to be added up
324 switchingTime += mainPhy->GetDelayUntilIdle();
325 [[fallthrough]];
326 case WifiPhyState::RX:
327 case WifiPhyState::IDLE:
328 case WifiPhyState::CCA_BUSY:
329 if (switchingTime > timeToCtsEnd)
330 {
331 // switching takes longer than RTS/CTS exchange, release channel
332 NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
333 << mainPhy->GetState()->GetState() << ")");
334 // retry channel access when the CTS was expected to be received
335 return {false, timeToCtsEnd};
336 }
337 break;
338 default:
339 NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState()->GetState());
340 }
341
342 // TXOP can be started, main PHY will be scheduled to switch by NotifyRtsSent as soon as the
343 // transmission of the RTS is notified
344 m_switchMainPhyOnRtsTx[linkId] = Simulator::Now();
345
346 return {true, Time{0}};
347}
348
349void
350DefaultEmlsrManager::NotifyRtsSent(uint8_t linkId,
352 const WifiTxVector& txVector)
353{
354 NS_LOG_FUNCTION(this << *rts << txVector);
355
356 const auto it = m_switchMainPhyOnRtsTx.find(linkId);
357
358 if (it == m_switchMainPhyOnRtsTx.cend() || it->second != Simulator::Now())
359 {
360 // No request for main PHY to switch or obsolete request
361 return;
362 }
363
364 // Main PHY shall terminate the channel switch at the end of CTS reception
365 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
366 const auto delay = GetTimeToCtsEnd(linkId, txVector) - mainPhy->GetChannelSwitchDelay();
367 NS_ASSERT_MSG(delay.IsPositive(),
368 "RTS is being sent, but not enough time for main PHY to switch");
369
370 NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
371 m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay, [=, this]() {
372 SwitchMainPhy(linkId,
373 false,
374 RESET_BACKOFF,
375 DONT_REQUEST_ACCESS,
377 });
378
379 m_switchMainPhyOnRtsTx.erase(it);
380}
381
382} // namespace ns3
DefaultEmlsrManager is the default EMLSR manager.
void DoNotifyMgtFrameReceived(Ptr< const WifiMpdu > mpdu, uint8_t linkId) override
Notify the subclass of the reception of a management frame addressed to us.
void NotifyEmlsrModeChanged() override
Notify subclass that EMLSR mode changed.
EventId m_auxPhySwitchEvent
event scheduled for an aux PHY to switch link
Ptr< WifiPhy > m_auxPhyToReconnect
Aux PHY the ChannelAccessManager of the link on which the main PHY is operating has to connect a list...
void DoNotifyUlTxopStart(uint8_t linkId) override
Notify the subclass of the start of an UL TXOP on the given link.
void NotifyMainPhySwitch(std::optional< uint8_t > currLinkId, uint8_t nextLinkId, Ptr< WifiPhy > auxPhy, Time duration) override
Notify subclass that the main PHY is switching channel to operate on another link.
bool m_switchAuxPhy
whether Aux PHY should switch channel to operate on the link on which the Main PHY was operating befo...
std::pair< bool, Time > DoGetDelayUntilAccessRequest(uint8_t linkId) override
Subclasses have to provide an implementation for this method, that is called by the base class when t...
static TypeId GetTypeId()
Get the type ID.
void DoNotifyIcfReceived(uint8_t linkId) override
Notify the subclass of the reception of an initial Control frame on the given link.
std::optional< uint8_t > ResendNotification(Ptr< const WifiMpdu > mpdu) override
A previous EML Operating Mode Notification frame was dropped.
void SwitchMainPhyBackToPreferredLink(uint8_t linkId, EmlsrMainPhySwitchTrace &&traceInfo)
This method can only be called when aux PHYs do not switch link.
void DoNotifyTxopEnd(uint8_t linkId) override
Notify the subclass of the end of a TXOP on the given link.
void SwitchAuxPhyAfterMainPhy(Ptr< WifiPhy > auxPhy, uint8_t currLinkId, uint8_t nextLinkId, Time duration)
This function shall be called when the main PHY starts switching to a link on which an aux PHY that i...
uint8_t GetLinkToSendEmlOmn() override
EmlsrManager is an abstract base class defining the API that EHT non-AP MLDs with EMLSR activated can...
uint8_t m_mainPhyId
ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
void SwitchAuxPhy(Ptr< WifiPhy > auxPhy, uint8_t currLinkId, uint8_t nextLinkId)
Switch channel on the Aux PHY operating on the given current link so that it operates on the given ne...
void SetCcaEdThresholdOnLinkSwitch(Ptr< WifiPhy > phy, uint8_t linkId)
Set the CCA ED threshold (if needed) on the given PHY that is switching channel to operate on the giv...
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess, EmlsrMainPhySwitchTrace &&traceInfo)
Switch channel on the Main PHY so that it operates on the given link.
MainPhySwitchInfo m_mainPhySwitchInfo
main PHY switch info
static constexpr bool REQUEST_ACCESS
request channel access when PHY switch ends
Ptr< StaWifiMac > GetStaMac() const
uint8_t GetMainPhyId() const
std::pair< bool, Time > CheckPossiblyReceivingIcf(uint8_t linkId) const
Check whether a PPDU that may be an ICF is being received on the given link.
static constexpr bool DONT_RESET_BACKOFF
do not reset backoff on main PHY switch
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
Smart pointer class similar to boost::intrusive_ptr.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
@ US
microsecond
Definition nstime.h:107
a unique identifier for an interface.
Definition type-id.h:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1368
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition qos-utils.h:62
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
uint32_t GetRtsSize()
Return the total RTS size (including FCS trailer).
Definition wifi-utils.cc:97
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition boolean.h:70
uint32_t GetCtsSize()
Return the total CTS size (including FCS trailer).
Base struct for EMLSR Main PHY switch traces.
uint8_t from
ID of the link which the main PHY is/has been leaving.
Struct to trace that main PHY switched when a (DL or UL) TXOP ended.
Struct to trace that main PHY switched to start an UL TXOP after that an aux PHY transmitted an RTS.