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));
127 }
128
129 // if currLinkId has no value, it means that the main PHY switch is interrupted, hence reset
130 // the aux PHY to reconnect. Doing so when the main PHY is leaving the preferred link makes
131 // no harm (the aux PHY to reconnect is set below), thus no need to add an 'if' condition
132 m_auxPhyToReconnect = nullptr;
133
134 if (nextLinkId != GetMainPhyId())
135 {
136 // the main PHY is moving to an auxiliary link and the aux PHY does not switch link
137 m_auxPhyToReconnect = auxPhy;
138 }
139}
140
141void
143 uint8_t currLinkId,
144 uint8_t nextLinkId,
145 Time duration)
146{
147 NS_LOG_FUNCTION(this << auxPhy << currLinkId << nextLinkId << duration.As(Time::US));
148
149 if (duration.IsStrictlyPositive())
150 {
151 auto lambda = [=, this]() {
152 if (GetStaMac()->GetWifiPhy(currLinkId) == auxPhy)
153 {
154 // the aux PHY is still operating on the link, likely because it is receiving a
155 // PPDU and connecting the main PHY to the link has been postponed
156 const auto [maybeIcf, extension] = CheckPossiblyReceivingIcf(currLinkId);
157 if (maybeIcf && extension.IsStrictlyPositive())
158 {
159 NS_LOG_DEBUG("Switching aux PHY to link " << +nextLinkId << " is postponed by "
160 << extension.As(Time::US));
161 SwitchAuxPhyAfterMainPhy(auxPhy, currLinkId, nextLinkId, extension);
162 return;
163 }
164 }
165 const auto isSleeping = auxPhy->IsStateSleep();
166 if (isSleeping)
167 {
168 // if the aux PHY is sleeping, it cannot switch channel
169 auxPhy->ResumeFromSleep();
170 }
171 SwitchAuxPhy(auxPhy, currLinkId, nextLinkId);
172 if (isSleeping)
173 {
174 // sleep mode will be postponed until the end of channel switch
175 auxPhy->SetSleepMode(true);
176 }
177 };
178
179 m_auxPhySwitchEvent = Simulator::Schedule(duration, lambda);
180 }
181 else
182 {
183 SwitchAuxPhy(auxPhy, currLinkId, nextLinkId);
184 }
185}
186
187std::pair<bool, Time>
189{
190 NS_LOG_FUNCTION(this << linkId);
191 auto phy = GetStaMac()->GetWifiPhy(linkId);
192 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
193
194 if (phy == mainPhy)
195 {
196 // UL TXOP is going to start
197 m_rtsStartingUlTxop[linkId] = {Simulator::Now(), false};
198 }
199
200 return {true, Time{0}}; // start the TXOP
201}
202
203void
205{
206 NS_LOG_FUNCTION(this << linkId);
207}
208
209void
211{
212 NS_LOG_FUNCTION(this << linkId);
213}
214
215void
217{
218 NS_LOG_FUNCTION(this << linkId << edca);
219
220 if (m_switchAuxPhy)
221 {
222 return; // nothing to do
223 }
224
225 // switch main PHY to the previous link, if needed
226 if (const auto it = m_rtsStartingUlTxop.find(linkId);
227 it != m_rtsStartingUlTxop.cend() && it->second.second)
228 {
229 // TXOP ended due to a CTS timeout following the RTS that started a TXOP
230 const auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
231 const auto delay = mainPhy->IsStateSwitching() ? mainPhy->GetDelayUntilIdle() : Time{0};
233 m_rtsStartingUlTxop.erase(it);
234 return;
235 }
236
238}
239
240void
242 EmlsrMainPhySwitchTrace&& traceInfo)
243{
244 NS_LOG_FUNCTION(this << linkId << traceInfo.GetName());
245
246 NS_ABORT_MSG_IF(m_switchAuxPhy, "This method can only be called when SwitchAuxPhy is false");
247
249 {
250 return;
251 }
252
253 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
254
255 // the main PHY may be switching at the end of a TXOP when, e.g., the main PHY starts
256 // switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
257 // is not received and the UL TXOP ends before the main PHY channel switch is completed.
258 // In such cases, wait until the main PHY channel switch is completed before requesting
259 // a new channel switch.
260 // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and
261 // a new backoff value must be generated.
262 if (!mainPhy->IsStateSwitching())
263 {
265 false,
267 std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
268 }
269 else
270 {
271 Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, info = traceInfo.Clone(), this]() {
272 // request the main PHY to switch back to the preferred link only if
273 // in the meantime no TXOP started on another link (which will
274 // require the main PHY to switch link)
275 if (!GetEhtFem(linkId)->UsingOtherEmlsrLink())
276 {
277 SwitchMainPhy(GetMainPhyId(), false, REQUEST_ACCESS, std::move(*info));
278 }
279 });
280 }
281}
282
283void
284DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci)
285{
286 NS_LOG_FUNCTION(this << linkId << aci);
287 NS_LOG_DEBUG("Do nothing, aux PHY is not TX capable");
288}
289
290Time
291DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId) const
292{
293 NS_LOG_FUNCTION(this << linkId);
294
295 const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId);
296 const auto bssid = GetEhtFem(linkId)->GetBssid();
297 const auto allowedWidth = GetEhtFem(linkId)->GetAllowedWidth();
298
299 return GetTimeToCtsEnd(linkId, stationManager->GetRtsTxVector(bssid, allowedWidth));
300}
301
302Time
303DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId, const WifiTxVector& rtsTxVector) const
304{
305 NS_LOG_FUNCTION(this << linkId << rtsTxVector);
306
307 auto phy = GetStaMac()->GetWifiPhy(linkId);
308 NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId);
309
310 const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId);
311 const auto bssid = GetEhtFem(linkId)->GetBssid();
312 const auto ctsTxVector = stationManager->GetCtsTxVector(bssid, rtsTxVector.GetMode());
313
314 const auto rtsTxTime =
315 WifiPhy::CalculateTxDuration(GetRtsSize(), rtsTxVector, phy->GetPhyBand());
316 const auto ctsTxTime =
317 WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, phy->GetPhyBand());
318
319 // the main PHY shall terminate the channel switch at the end of CTS reception;
320 // the time remaining to the end of CTS reception includes two propagation delays
321 return rtsTxTime + phy->GetSifs() + ctsTxTime + MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC);
322}
323
324std::pair<bool, Time>
325DefaultEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)
326{
327 NS_LOG_FUNCTION(this << linkId);
328
329 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
330 const auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
331 auto switchingTime = mainPhy->GetChannelSwitchDelay();
332
333 switch (mainPhy->GetState()->GetState())
334 {
335 case WifiPhyState::SWITCHING:
336 // the main PHY is switching (to another link), hence the remaining time to
337 // the end of the current channel switch needs to be added up
338 switchingTime += mainPhy->GetDelayUntilIdle();
339 [[fallthrough]];
340 case WifiPhyState::RX:
341 case WifiPhyState::IDLE:
342 case WifiPhyState::CCA_BUSY:
343 if (switchingTime > timeToCtsEnd)
344 {
345 // switching takes longer than RTS/CTS exchange, release channel
346 NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
347 << mainPhy->GetState()->GetState() << ")");
348 // retry channel access when the CTS was expected to be received
349 return {false, timeToCtsEnd};
350 }
351 break;
352 default:
353 NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState()->GetState());
354 }
355
356 // TXOP can be started, main PHY will be scheduled to switch by NotifyRtsSent as soon as the
357 // transmission of the RTS is notified
358 m_rtsStartingUlTxop[linkId] = {Simulator::Now(), false};
359
360 return {true, Time{0}};
361}
362
363void
364DefaultEmlsrManager::NotifyRtsSent(uint8_t linkId,
366 const WifiTxVector& txVector)
367{
368 NS_LOG_FUNCTION(this << *rts << txVector);
369
370 const auto it = m_rtsStartingUlTxop.find(linkId);
371
372 if (it == m_rtsStartingUlTxop.cend() || it->second.first != Simulator::Now())
373 {
374 return; // Not an RTS starting an UL TXOP
375 }
376 it->second.second = true;
377
378 auto phy = GetStaMac()->GetWifiPhy(linkId);
379 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
380
381 if (phy == mainPhy)
382 {
383 return; // RTS sent by the main PHY
384 }
385
386 // Main PHY shall terminate the channel switch at the end of CTS reception
387 const auto delay = GetTimeToCtsEnd(linkId, txVector) - mainPhy->GetChannelSwitchDelay();
388 NS_ASSERT_MSG(delay.IsPositive(),
389 "RTS is being sent, but not enough time for main PHY to switch");
390
391 NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
392 m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay, [=, this]() {
393 SwitchMainPhy(linkId, false, DONT_REQUEST_ACCESS, EmlsrUlTxopRtsSentByAuxPhyTrace{});
394 });
395}
396
397void
398DefaultEmlsrManager::DoNotifyProtectionCompleted(uint8_t linkId)
399{
400 NS_LOG_FUNCTION(this << linkId);
401 m_rtsStartingUlTxop.erase(linkId);
402}
403
404} // namespace ns3
AttributeValue implementation for Boolean.
Definition boolean.h:26
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...
std::map< uint8_t, std::pair< Time, bool > > m_rtsStartingUlTxop
link ID-indexed map indicating the time when an UL TXOP is going to start and whether it is starting ...
void DoNotifyTxopEnd(uint8_t linkId, Ptr< QosTxop > edca) override
Notify the subclass of the end of a TXOP on the given link.
static TypeId GetTypeId()
Get the type ID.
void DoNotifyDlTxopStart(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.
virtual void SwitchMainPhyBackToPreferredLink(uint8_t linkId, EmlsrMainPhySwitchTrace &&traceInfo)
This method can only be called when aux PHYs do not switch 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...
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool requestAccess, EmlsrMainPhySwitchTrace &&traceInfo)
Switch channel on the Main PHY so that it operates on the given link.
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...
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.
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:561
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
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:403
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
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition boolean.h:70
#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:1369
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1345
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.
uint32_t GetRtsSize()
Return the total RTS size (including FCS trailer).
Definition wifi-utils.cc:98
uint32_t GetCtsSize()
Return the total CTS size (including FCS trailer).
Struct to trace that main PHY started switching after a CTS timeout occurred on the link on which an ...
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.