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
49 : m_mainPhySwitchInfo{}
50{
51 NS_LOG_FUNCTION(this);
52}
53
58
59void
61{
62 NS_LOG_FUNCTION(this << *mpdu << linkId);
63}
64
65uint8_t
67{
68 NS_LOG_FUNCTION(this);
69 auto linkId = GetStaMac()->GetLinkForPhy(m_mainPhyId);
70 NS_ASSERT_MSG(linkId, "Link on which the main PHY is operating not found");
71 return *linkId;
72}
73
74std::optional<uint8_t>
76{
77 NS_LOG_FUNCTION(this);
78 auto linkId = GetStaMac()->GetLinkForPhy(m_mainPhyId);
79 NS_ASSERT_MSG(linkId, "Link on which the main PHY is operating not found");
80 return *linkId;
81}
82
83void
88
89void
90DefaultEmlsrManager::NotifyMainPhySwitch(std::optional<uint8_t> currLinkId,
91 uint8_t nextLinkId,
92 Ptr<WifiPhy> auxPhy,
93 Time duration)
94{
95 NS_LOG_FUNCTION(this << (currLinkId ? std::to_string(*currLinkId) : "") << nextLinkId << auxPhy
96 << duration.As(Time::US));
97
98 // if currLinkId has no value (i.e., the main PHY is not operating on any link), it means that
99 // the main PHY is switching
100 const auto now = Simulator::Now();
101 NS_ASSERT_MSG(currLinkId || m_mainPhySwitchInfo.end >= now,
102 "No current link ID provided nor valid main PHY switch information stored");
103 m_mainPhySwitchInfo.from = currLinkId.value_or(m_mainPhySwitchInfo.from);
104 m_mainPhySwitchInfo.to = nextLinkId;
105 m_mainPhySwitchInfo.end = now + duration;
106
107 if (m_switchAuxPhy)
108 {
109 // cancel any previously requested aux PHY switch
111
112 if (nextLinkId == m_mainPhySwitchInfo.from)
113 {
114 // the main PHY is now switching to the link where it is coming from; nothing else
115 // needs to be done
116 return;
117 }
118
119 // schedule Aux PHY switch so that it operates on the link on which the main PHY was
120 // operating
121 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") operating on link " << +nextLinkId
122 << " will switch to link " << +currLinkId.value() << " in "
123 << duration.As(Time::US));
124
125 if (duration.IsStrictlyPositive())
126 {
128 Simulator::Schedule(duration, [=, this, prevLinkId = m_mainPhySwitchInfo.from]() {
129 SwitchAuxPhy(auxPhy, nextLinkId, prevLinkId);
130 });
131 }
132 else
133 {
134 SwitchAuxPhy(auxPhy, nextLinkId, m_mainPhySwitchInfo.from);
135 }
136
137 return;
138 }
139
140 if (currLinkId.has_value() && currLinkId != GetMainPhyId())
141 {
142 // the main PHY is leaving an auxiliary link, hence an aux PHY needs to be reconnected
145 "There should be an aux PHY to reconnect when the main PHY leaves an auxiliary link");
146
147 // the Aux PHY is not actually switching (hence no switching delay)
148 GetStaMac()->NotifySwitchingEmlsrLink(m_auxPhyToReconnect, *currLinkId, Seconds(0));
150 }
151
152 // if currLinkId has no value, it means that the main PHY switch is interrupted, hence reset
153 // the aux PHY to reconnect. Doing so when the main PHY is leaving the preferred link makes
154 // no harm (the aux PHY to reconnect is set below), thus no need to add an 'if' condition
155 m_auxPhyToReconnect = nullptr;
156
157 if (nextLinkId != GetMainPhyId())
158 {
159 // the main PHY is moving to an auxiliary link and the aux PHY does not switch link
160 m_auxPhyToReconnect = auxPhy;
161 }
162}
163
164std::pair<bool, Time>
166{
167 NS_LOG_FUNCTION(this << linkId);
168 return {true, Time{0}}; // start the TXOP
169}
170
171void
173{
174 NS_LOG_FUNCTION(this << linkId);
175}
176
177void
179{
180 NS_LOG_FUNCTION(this << linkId);
181}
182
183void
185{
186 NS_LOG_FUNCTION(this << linkId);
187
188 // switch main PHY to the previous link, if needed
189 if (!m_switchAuxPhy)
190 {
191 const auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
192 const auto delay = mainPhy->IsStateSwitching() ? mainPhy->GetDelayUntilIdle() : Time{0};
194 }
195}
196
197void
199 EmlsrMainPhySwitchTrace&& traceInfo)
200{
201 NS_LOG_FUNCTION(this << linkId << traceInfo.GetName());
202
203 NS_ABORT_MSG_IF(m_switchAuxPhy, "This method can only be called when SwitchAuxPhy is false");
204
206 {
207 return;
208 }
209
210 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
211
212 // the main PHY may be switching at the end of a TXOP when, e.g., the main PHY starts
213 // switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
214 // is not received and the UL TXOP ends before the main PHY channel switch is completed.
215 // In such cases, wait until the main PHY channel switch is completed before requesting
216 // a new channel switch.
217 // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and
218 // a new backoff value must be generated.
219 if (!mainPhy->IsStateSwitching())
220 {
222 false,
225 std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
226 }
227 else
228 {
229 Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, info = traceInfo.Clone(), this]() {
230 // request the main PHY to switch back to the preferred link only if
231 // in the meantime no TXOP started on another link (which will
232 // require the main PHY to switch link)
233 if (!GetEhtFem(linkId)->UsingOtherEmlsrLink())
234 {
235 SwitchMainPhy(GetMainPhyId(),
236 false,
237 DONT_RESET_BACKOFF,
238 REQUEST_ACCESS,
239 std::move(*info));
240 }
241 });
242 }
243}
244
245void
246DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci)
247{
248 NS_LOG_FUNCTION(this << linkId << aci);
249 NS_LOG_DEBUG("Do nothing, aux PHY is not TX capable");
250}
251
252Time
253DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId) const
254{
255 NS_LOG_FUNCTION(this << linkId);
256
257 const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId);
258 const auto bssid = GetEhtFem(linkId)->GetBssid();
259 const auto allowedWidth = GetEhtFem(linkId)->GetAllowedWidth();
260
261 return GetTimeToCtsEnd(linkId, stationManager->GetRtsTxVector(bssid, allowedWidth));
262}
263
264Time
265DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId, const WifiTxVector& rtsTxVector) const
266{
267 NS_LOG_FUNCTION(this << linkId << rtsTxVector);
268
269 auto phy = GetStaMac()->GetWifiPhy(linkId);
270 NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId);
271
272 const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId);
273 const auto bssid = GetEhtFem(linkId)->GetBssid();
274 const auto ctsTxVector = stationManager->GetCtsTxVector(bssid, rtsTxVector.GetMode());
275
276 const auto rtsTxTime = phy->CalculateTxDuration(GetRtsSize(), rtsTxVector, phy->GetPhyBand());
277 const auto ctsTxTime = phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, phy->GetPhyBand());
278
279 // the main PHY shall terminate the channel switch at the end of CTS reception;
280 // the time remaining to the end of CTS reception includes two propagation delays
281 return rtsTxTime + phy->GetSifs() + ctsTxTime + MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC);
282}
283
284std::pair<bool, Time>
285DefaultEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)
286{
287 NS_LOG_FUNCTION(this << linkId);
288
289 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
290 const auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
291 auto switchingTime = mainPhy->GetChannelSwitchDelay();
292
293 switch (mainPhy->GetState()->GetState())
294 {
295 case WifiPhyState::SWITCHING:
296 // the main PHY is switching (to another link), hence the remaining time to
297 // the end of the current channel switch needs to be added up
298 switchingTime += mainPhy->GetDelayUntilIdle();
299 [[fallthrough]];
300 case WifiPhyState::RX:
301 case WifiPhyState::IDLE:
302 case WifiPhyState::CCA_BUSY:
303 if (switchingTime > timeToCtsEnd)
304 {
305 // switching takes longer than RTS/CTS exchange, release channel
306 NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
307 << mainPhy->GetState()->GetState() << ")");
308 // retry channel access when the CTS was expected to be received
309 return {false, timeToCtsEnd};
310 }
311 break;
312 default:
313 NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState()->GetState());
314 }
315
316 // TXOP can be started, main PHY will be scheduled to switch by NotifyRtsSent as soon as the
317 // transmission of the RTS is notified
318 m_switchMainPhyOnRtsTx[linkId] = Simulator::Now();
319
320 return {true, Time{0}};
321}
322
323void
324DefaultEmlsrManager::NotifyRtsSent(uint8_t linkId,
326 const WifiTxVector& txVector)
327{
328 NS_LOG_FUNCTION(this << *rts << txVector);
329
330 const auto it = m_switchMainPhyOnRtsTx.find(linkId);
331
332 if (it == m_switchMainPhyOnRtsTx.cend() || it->second != Simulator::Now())
333 {
334 // No request for main PHY to switch or obsolete request
335 return;
336 }
337
338 // Main PHY shall terminate the channel switch at the end of CTS reception
339 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
340 const auto delay = GetTimeToCtsEnd(linkId, txVector) - mainPhy->GetChannelSwitchDelay();
341 NS_ASSERT_MSG(delay.IsPositive(),
342 "RTS is being sent, but not enough time for main PHY to switch");
343
344 NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
345 m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay, [=, this]() {
346 SwitchMainPhy(linkId,
347 false,
348 RESET_BACKOFF,
349 DONT_REQUEST_ACCESS,
351 });
352
353 m_switchMainPhyOnRtsTx.erase(it);
354}
355
356} // 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...
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.
MainPhySwitchInfo m_mainPhySwitchInfo
main PHY switch info
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.
static constexpr bool REQUEST_ACCESS
request channel access when PHY switch ends
Ptr< StaWifiMac > GetStaMac() const
uint8_t GetMainPhyId() const
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
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: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:48
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: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.
uint32_t GetRtsSize()
Return the total RTS size (including FCS trailer).
Definition wifi-utils.cc:95
uint32_t GetCtsSize()
Return the total CTS size (including FCS trailer).
uint8_t to
ID of the link which the main PHY is moving to.
uint8_t from
ID of the link which the main PHY is/has been leaving.
Base struct for EMLSR Main PHY switch traces.
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.