A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-emlsr-basic-exchanges-test.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
11#include "ns3/advanced-emlsr-manager.h"
12#include "ns3/boolean.h"
13#include "ns3/config.h"
14#include "ns3/eht-configuration.h"
15#include "ns3/eht-frame-exchange-manager.h"
16#include "ns3/log.h"
17#include "ns3/mgt-action-headers.h"
18#include "ns3/qos-txop.h"
19#include "ns3/rr-multi-user-scheduler.h"
20#include "ns3/simulator.h"
21#include "ns3/string.h"
22#include "ns3/wifi-net-device.h"
23
24#include <algorithm>
25#include <functional>
26#include <iomanip>
27
28using namespace ns3;
29
30NS_LOG_COMPONENT_DEFINE("WifiEmlsrBasicExchangesTest");
31
33 : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" +
34 std::to_string(params.nEmlsrStations) + "," +
35 std::to_string(params.nNonEmlsrStations) + ")"),
36 m_emlsrLinks(params.linksToEnableEmlsrOn),
37 m_emlsrEnabledTime(0),
38 m_fe2to3delay(MilliSeconds(20)),
39 m_countQoSframes(0),
40 m_countBlockAck(0)
41{
42 m_nEmlsrStations = params.nEmlsrStations;
43 m_nNonEmlsrStations = params.nNonEmlsrStations;
44 m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
45 m_mainPhyId = 1;
46 m_paddingDelay = params.paddingDelay;
47 m_transitionDelay = params.transitionDelay;
48 m_transitionTimeout = params.transitionTimeout;
49 m_establishBaDl = {0};
50 m_putAuxPhyToSleep = params.putAuxPhyToSleep;
51 m_duration = Seconds(1.5);
52
53 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
54 "This test requires at least two links to be configured as EMLSR links");
55}
56
57void
59 uint8_t phyId,
60 WifiConstPsduMap psduMap,
61 WifiTxVector txVector,
62 double txPowerW)
63{
64 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
65 auto linkId = m_txPsdus.back().linkId;
66
67 auto psdu = psduMap.begin()->second;
68 auto nodeId = mac->GetDevice()->GetNode()->GetId();
69
70 switch (psdu->GetHeader(0).GetType())
71 {
73 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
74 if (nodeId <= m_nEmlsrStations)
75 {
76 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
77 // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
78 // in powersave mode after association; we let the non-EMLSR links transition to
79 // active mode (by sending data null frames) after association
80 for (const auto id : m_staMacs.at(nodeId - 1)->GetLinkIds())
81 {
82 if (id != linkId && m_emlsrLinks.count(id) == 1)
83 {
84 m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
85 }
86 }
87 }
88 break;
89
91 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
92
93 if ((category == WifiActionHeader::PROTECTED_EHT) &&
94 (action.protectedEhtAction ==
96 {
97 nodeId == 0 ? CheckApEmlNotificationFrame(*psdu->begin(), txVector, linkId)
98 : CheckStaEmlNotificationFrame(*psdu->begin(), txVector, linkId);
99 }
100 else if (category == WifiActionHeader::BLOCK_ACK &&
102 {
103 CheckPmModeAfterAssociation(psdu->GetAddr1());
104 }
105 }
106 break;
107
109 CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
110 break;
111
112 case WIFI_MAC_QOSDATA:
113 CheckQosFrames(psduMap, txVector, linkId);
114 break;
115
117 CheckBlockAck(psduMap, txVector, phyId);
118 break;
119
120 case WIFI_MAC_CTL_END:
121 if (auto apMac = DynamicCast<ApWifiMac>(mac))
122 {
123 const auto txDuration =
125 txVector,
126 apMac->GetDevice()->GetPhy(phyId)->GetPhyBand());
127 for (std::size_t i = 0; i < m_nEmlsrStations; ++i)
128 {
129 if (m_staMacs[i]->IsEmlsrLink(linkId) &&
130 m_staMacs[i]->GetWifiPhy(linkId) ==
131 m_staMacs[i]->GetDevice()->GetPhy(m_mainPhyId))
132 {
133 // AP is terminating a TXOP on an EMLSR link on which the main PHY is operating,
134 // aux PHYs should resume from sleep
135 Simulator::Schedule(txDuration + TimeStep(1),
137 this,
138 m_staMacs[i],
139 false);
140 }
141 }
142 }
143 break;
144
145 default:;
146 }
147}
148
149void
151{
152 // Channel switch delay should be less than the ICF padding duration, otherwise
153 // DL TXOPs cannot be initiated on auxiliary links
154 auto delay = std::min(MicroSeconds(100),
155 *std::min_element(m_paddingDelay.cbegin(), m_paddingDelay.cend()));
156 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
157
159
161 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
162 {
163 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
164 }
165
166 m_apMac->GetQosTxop(AC_BE)->SetTxopLimits(
167 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
168
170 {
171 auto muScheduler =
173 m_apMac->AggregateObject(muScheduler);
174 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
175 {
176 m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
177 "DlMuAckSequenceType",
179 }
180 }
181}
182
183void
185{
187 {
188 // we are done with association and Block Ack agreement; we can now enable EMLSR mode
189 m_lastAid = 0;
191 return;
192 }
193
194 // we are done with sending EML Operating Mode Notification frames. We can now generate
195 // packets for all non-AP MLDs
196 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
197 {
198 // when multiple non-AP MLDs are present, MU transmission are used. Given that the
199 // available bandwidth decreases as the number of non-AP MLDs increases, compute the
200 // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
201 std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
202 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, i, count, 450));
203 }
204
205 // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
206 // packet to each EMLSR client to test transition delay
207 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
208 {
210 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
211 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
212 });
213 }
214
215 // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
216 // and the generation of other packets destined to the EMLSR clients
217 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
218 {
219 Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=, this]() {
220 m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
221 "EmlsrLinkSet",
223 });
224
226 m_apMac->GetDevice()->GetNode()->AddApplication(
228 });
229 }
230}
231
232void
234{
235 m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
236 "EmlsrLinkSet",
238 m_lastAid++;
239 Simulator::Schedule(MilliSeconds(5), [=, this]() {
241 {
242 // make the next STA send EML Notification frame
244 return;
245 }
246 // all stations enabled EMLSR mode; start traffic
248 StartTraffic();
249 });
250}
251
252void
254{
255 auto psduIt = m_txPsdus.cbegin();
256
257 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
258 // to an EMLSR client
259 auto jumpToQosDataOrMuRts = [&]() {
260 while (psduIt != m_txPsdus.cend() &&
261 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
262 {
263 auto psdu = psduIt->psduMap.cbegin()->second;
264 if (psdu->GetHeader(0).IsTrigger())
265 {
266 CtrlTriggerHeader trigger;
267 psdu->GetPayload(0)->PeekHeader(trigger);
268 if (trigger.IsMuRts())
269 {
270 break;
271 }
272 }
273 psduIt++;
274 }
275 };
276
277 /**
278 * Before enabling EMLSR mode, no MU-RTS TF should be sent. Four packets are generated
279 * after association to trigger the establishment of a Block Ack agreement. The TXOP Limit
280 * and the MCS are set such that two packets can be transmitted in a TXOP, hence we expect
281 * that the AP MLD sends two A-MPDUs to each non-AP MLD.
282 *
283 * EMLSR client with EMLSR mode to be enabled on all links: after ML setup, all other links
284 * stay in power save mode, hence BA establishment occurs on the same link.
285 *
286 * [link 0]
287 * ───────────────────────────────────────────────────────────────────────────
288 * | power save mode
289 *
290 * ┌─────┐ ┌─────┐ ┌───┬───┐ ┌───┬───┐
291 * ┌───┐ │Assoc│ │ADDBA│ ┌───┐ │QoS│QoS│ │QoS│QoS│
292 * [link 1] │ACK│ │Resp │ │ Req │ │ACK│ │ 0 │ 1 │ │ 2 │ 3 │
293 * ───┬─────┬┴───┴──┴─────┴┬───┬─┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─┴───┴───┴┬──┬───
294 * │Assoc│ │ACK│ │ACK│ │ADDBA│ │BA│ │BA│
295 * │ Req │ └───┘ └───┘ │Resp │ └──┘ └──┘
296 * └─────┘ └─────┘
297 *
298 * [link 2]
299 * ───────────────────────────────────────────────────────────────────────────
300 * | power save mode
301 *
302 *
303 * EMLSR client with EMLSR mode to be enabled on not all the links: after ML setup,
304 * the other EMLSR links stay in power save mode, the non-EMLSR link (link 1) transitions
305 * to active mode.
306 *
307 * ┌─────┐ ┌───┬───┐
308 * ┌───┐ │ADDBA│ ┌───┐ │QoS│QoS│
309 * [link 0 - non EMLSR] │ACK│ │ Req │ │ACK│ │ 2 │ 3 │
310 * ──────────────────────────────┬────┬┴───┴──┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─
311 * │Data│ │ACK│ │ADDBA│ │BA│
312 * │Null│ └───┘ │Resp │ └──┘
313 * └────┘ └─────┘
314 * ┌─────┐ ┌───┬───┐
315 * ┌───┐ │Assoc│ │QoS│QoS│
316 * [link 1] │ACK│ │Resp │ │ 0 │ 1 │
317 * ───┬─────┬┴───┴──┴─────┴┬───┬──────────────────────────────────┴───┴───┴┬──┬───────
318 * │Assoc│ │ACK│ │BA│
319 * │ Req │ └───┘ └──┘
320 * └─────┘
321 *
322 * [link 2]
323 * ───────────────────────────────────────────────────────────────────────────
324 * | power save mode
325 *
326 * Non-EMLSR client (not shown): after ML setup, all other links transition to active mode
327 * by sending a Data Null frame; QoS data frame exchanges occur on two links simultaneously.
328 */
329 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
330 {
331 std::set<uint8_t> linkIds;
332
333 jumpToQosDataOrMuRts();
334 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
335 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
336 true,
337 "Expected at least one QoS data frame before enabling EMLSR mode");
338 linkIds.insert(psduIt->linkId);
339 const auto firstAmpduTxEnd =
340 psduIt->startTx +
341 WifiPhy::CalculateTxDuration(psduIt->psduMap,
342 psduIt->txVector,
343 m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
344 auto firstQos = psduIt++;
345
346 jumpToQosDataOrMuRts();
347 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
348 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
349 true,
350 "Expected at least two QoS data frames before enabling EMLSR mode");
351 linkIds.insert(psduIt->linkId);
352 const auto secondAmpduTxStart = psduIt->startTx;
353
354 auto beaconInBetween{false};
355 while (++firstQos != psduIt)
356 {
357 if (firstQos->psduMap.cbegin()->second->GetHeader(0).IsBeacon())
358 {
359 beaconInBetween = true;
360 break;
361 }
362 }
363
364 psduIt++;
365
366 /**
367 * If this is an EMLSR client and there is no setup link other than the one used to
368 * establish association that is not an EMLSR link, then the two A-MPDUs are sent one
369 * after another on the link used to establish association.
370 */
371 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
372
373 bool areAllSetupLinksEmlsr =
374 std::all_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
375 return linkId == m_mainPhyId || m_emlsrLinks.contains(linkId);
376 });
377
378 if (i < m_nEmlsrStations && areAllSetupLinksEmlsr)
379 {
380 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
381 1,
382 "Expected both A-MPDUs to be sent on the same link");
383 NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(), +m_mainPhyId, "A-MPDUs sent on incorrect link");
384 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
385 secondAmpduTxStart,
386 "A-MPDUs are not sent one after another");
387 }
388 /**
389 * Otherwise, the two A-MPDUs can be sent concurrently on two distinct links (may be
390 * the link used to establish association and a non-EMLSR link).
391 */
392 else if (!beaconInBetween)
393 {
394 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
395 2,
396 "Expected A-MPDUs to be sent on distinct links");
397 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
398 secondAmpduTxStart,
399 "A-MPDUs are not sent concurrently");
400 }
401 }
402
403 /**
404 * After enabling EMLSR mode, MU-RTS TF should only be sent on EMLSR links. After the exchange
405 * of EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
406 * to prepare two A-MPDUs for each non-AP MLD.
407 *
408 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
409 * non-EMLSR client):
410 * ┌─────┬─────┐
411 * │QoS 4│QoS 5│
412 * │ to A│ to A│
413 * ┌───┐ ├─────┼─────┤
414 * │MU │ │QoS 4│QoS 5│
415 * [link 0] │RTS│ │ to B│ to B│
416 * ──────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬────────────
417 * │CTS│ │BA│
418 * ├───┤ ├──┤
419 * │CTS│ │BA│
420 * └───┘ └──┘
421 * ┌───┐ ┌─────┬─────┐
422 * ┌───┐ │EML│ │QoS 6│QoS 7│
423 * [link 1] │ACK│ │OM │ │ to B│ to B│
424 * ────┬───┬┴───┴──┴───┴┬───┬─┴─────┴─────┴┬──┬────────────────────────────────────
425 * │EML│ │ACK│ │BA│
426 * │OM │ └───┘ └──┘
427 * └───┘
428 * ┌───┐ ┌─────┬─────┐
429 * │MU │ │QoS 6│QoS 7│
430 * [link 2] │RTS│ │ to A│ to A│
431 * ─────────────────────────────────────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬─
432 * │CTS│ │BA│
433 * └───┘ └──┘
434 *
435 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
436 * B is the non-EMLSR client):
437 * ┌─────┬─────┐
438 * │QoS 4│QoS 5│
439 * │ to A│ to A│
440 * ├─────┼─────┤
441 * │QoS 4│QoS 5│
442 * [link 0 - non EMLSR] │ to B│ to B│
443 * ───────────────────────────┴─────┴─────┴┬──┬───────────────────────────
444 * │BA│
445 * ├──┤
446 * │BA│
447 * └──┘
448 * ┌─────┬─────┐
449 * │QoS 6│QoS 7│
450 * │ to A│ to A│
451 * ┌───┐ ┌───┐ ├─────┼─────┤
452 * ┌───┐ │EML│ │MU │ │QoS 6│QoS 7│
453 * [link 1] │ACK│ │OM │ │RTS│ │ to B│ to B│
454 * ────┬───┬┴───┴──┴───┴┬───┬─┴───┴┬───┬┴─────┴─────┴┬──┬────────────
455 * │EML│ │ACK│ │CTS│ │BA│
456 * │OM │ └───┘ ├───┤ ├──┤
457 * └───┘ │CTS│ │BA│
458 * └───┘ └──┘
459 *
460 * [link 2]
461 * ────────────────────────────────────────────────────────────────────────────────
462 */
463
464 /// Store a QoS data frame or an MU-RTS TF followed by a QoS data frame
465 using FrameExchange = std::list<decltype(psduIt)>;
466
467 std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
468
469 // compute all frame exchanges involving EMLSR clients
470 while (psduIt != m_txPsdus.cend())
471 {
472 jumpToQosDataOrMuRts();
473 if (psduIt == m_txPsdus.cend())
474 {
475 break;
476 }
477
478 if (IsTrigger(psduIt->psduMap))
479 {
480 CtrlTriggerHeader trigger;
481 psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
482 // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
483 // the addressed EMLSR clients
485 true,
486 "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
487 for (const auto& userInfo : trigger)
488 {
489 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
490 {
491 if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
492 {
493 frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
494 break;
495 }
496 }
497 }
498 psduIt++;
499 continue;
500 }
501
502 // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
503 // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
504 // frame exchange sequence
505 for (const auto& staIdPsduPair : psduIt->psduMap)
506 {
507 std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
508 if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
509 {
510 // not addressed to this non-AP MLD
511 return;
512 }
513 // a QoS data frame starts a new frame exchange sequence if there is no previous
514 // MU-RTS TF that has been sent on the same link and is not already followed by
515 // a QoS data frame
516 std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
517 for (auto& frameExchange : frameExchanges.at(id))
518 {
519 if (IsTrigger(frameExchange.front()->psduMap) &&
520 frameExchange.front()->linkId == psduIt->linkId &&
521 frameExchange.size() == 1)
522 {
523 auto it = std::next(frameExchange.front());
524 while (it != m_txPsdus.end())
525 {
526 // stop at the first frame other than CTS sent on this link
527 if (it->linkId == psduIt->linkId &&
528 !it->psduMap.begin()->second->GetHeader(0).IsCts())
529 {
530 break;
531 }
532 ++it;
533 }
534 if (it == psduIt)
535 {
536 // the QoS data frame actually followed the MU-RTS TF
537 frameExchange.emplace_back(psduIt);
538 return;
539 }
540 }
541 }
542 frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
543 });
544 }
545 psduIt++;
546 }
547
548 /**
549 * Let's focus on the first two frame exchanges for each EMLSR clients. If all setup links are
550 * EMLSR links, both frame exchanges are protected by MU-RTS TF and occur one after another.
551 * Otherwise, one frame exchange occurs on the non-EMLSR link and is not protected by
552 * MU-RTS TF; the other frame exchange occurs on an EMLSR link and is protected by MU-RTS TF.
553 */
554 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
555 {
556 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
557 2,
558 "Expected at least 2 frame exchange sequences "
559 << "involving EMLSR client " << i);
560
561 auto firstExchangeIt = frameExchanges.at(i).begin();
562 auto secondExchangeIt = std::next(firstExchangeIt);
563
564 const auto firstAmpduTxEnd =
565 firstExchangeIt->back()->startTx +
567 firstExchangeIt->back()->psduMap,
568 firstExchangeIt->back()->txVector,
569 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
570 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
571
572 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
573 {
574 // all links are EMLSR links
575 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
576 true,
577 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
579 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
580 true,
581 "Expected a QoS data frame in the first frame exchange sequence");
582
583 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
584 true,
585 "Expected an MU-RTS TF as ICF of second frame exchange sequence");
587 secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
588 true,
589 "Expected a QoS data frame in the second frame exchange sequence");
590
591 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
592 secondAmpduTxStart,
593 "A-MPDUs are not sent one after another");
594 }
595 else
596 {
597 std::vector<uint8_t> nonEmlsrIds;
598 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
599 std::set_difference(setupLinks.begin(),
600 setupLinks.end(),
601 m_emlsrLinks.begin(),
602 m_emlsrLinks.end(),
603 std::back_inserter(nonEmlsrIds));
604 NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
605
606 auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
607 ? firstExchangeIt
608 : secondExchangeIt;
609 NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
610 false,
611 "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
613 nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
614 true,
615 "Expected a QoS data frame on the non-EMLSR link");
616
617 auto emlsrLinkExchangeIt =
618 nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
619 NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
620 +nonEmlsrIds[0],
621 "Expected this exchange not to occur on non-EMLSR link");
622 NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
623 true,
624 "Expected an MU-RTS TF as ICF on the EMLSR link");
626 emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
627 true,
628 "Expected a QoS data frame on the EMLSR link");
629
630 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
631 secondAmpduTxStart,
632 "A-MPDUs are not sent concurrently");
633 }
634
635 // we are done with processing the first two frame exchanges, remove them
636 frameExchanges.at(i).erase(firstExchangeIt);
637 frameExchanges.at(i).erase(secondExchangeIt);
638 }
639
640 /**
641 * A and B are two EMLSR clients. No ICF before the second QoS data frame because B
642 * has not switched to listening mode. ICF is sent before the third QoS data frame because
643 * A has switched to listening mode. C is a non-EMLSR client.
644 *
645 * ┌─────┐ A switches to listening
646 * │QoS x│ after transition delay
647 * │ to A│ |
648 * ┌───┐ ├─────┤ ┌─────┐
649 * │MU │ │QoS x│ │QoS y│
650 * [link 0] │RTS│ │ to B│ │ to B│
651 * ────────────┴───┴┬───┬┴─────┴┬──┬┴─────┴┬──┬────────────
652 * │CTS│ │BA│ │BA│
653 * ├───┤ ├──┤ └──┘
654 * │CTS│ │BA│
655 * └───┘ └──┘ AP continues the TXOP A switches to listening
656 * after PIFS recovery after transition delay
657 * │ │
658 * ┌─────┐ ┌───┐ ┌─────┐ │┌───┐ ┌───┐
659 * │QoS z│ │MU │ │QoS x│ ││MU │ ┌───┐ │CF-│
660 * [link 1] │ to C│ │RTS│ │ to A│ ││RTS│ │BAR│ │End│
661 * ───────────────────────────────┴─────┴┬──┬┴───┴┬───┬┴─────┴┬──┬┴───┴┬───┬┴───┴┬──┬┴───┴─
662 * │BA│ │CTS│ │BA│ │CTS│ │BA│
663 * └──┘ └───┘ └──x └───┘ └──┘
664 */
665 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
666 {
667 // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
668 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
669 {
670 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
671 2,
672 "Expected at least 2 frame exchange sequences "
673 << "involving EMLSR client " << i);
674 // the first frame exchange must start with an ICF
675 auto firstExchangeIt = frameExchanges.at(i).begin();
676
677 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
678 true,
679 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
681 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
682 true,
683 "Expected a QoS data frame in the first frame exchange sequence");
684 }
685
686 // the second frame exchange is the one that starts first
687 auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
688 std::next(frameExchanges.at(1).begin())->front()->startTx
689 ? std::next(frameExchanges.at(0).begin())
690 : std::next(frameExchanges.at(1).begin());
691 decltype(secondExchangeIt) thirdExchangeIt;
692 std::size_t thirdExchangeStaId;
693
694 if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
695 {
696 thirdExchangeIt = std::next(frameExchanges.at(1).begin());
697 thirdExchangeStaId = 1;
698 }
699 else
700 {
701 thirdExchangeIt = std::next(frameExchanges.at(0).begin());
702 thirdExchangeStaId = 0;
703 }
704
705 // the second frame exchange is not protected by the ICF and starts a SIFS after the end
706 // of the previous one
707 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
708 false,
709 "Expected no ICF for the second frame exchange sequence");
711 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
712 true,
713 "Expected a QoS data frame in the second frame exchange sequence");
714
715 // the first two frame exchanges occur on the same link
716 NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
717 +frameExchanges.at(0).begin()->front()->linkId,
718 "Expected the first two frame exchanges to occur on the same link");
719
720 auto bAckRespIt = std::prev(secondExchangeIt->front());
721 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
722 true,
723 "Expected a BlockAck response before the second frame exchange");
724 auto bAckRespTxEnd =
725 bAckRespIt->startTx +
726 WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
727 bAckRespIt->txVector,
728 m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
729
730 // the second frame exchange starts a SIFS after the previous one
732 bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
733 secondExchangeIt->front()->startTx,
734 "Expected the second frame exchange to start a SIFS after the first one");
735
736 // the third frame exchange is protected by MU-RTS and occurs on a different link
737 NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
738 true,
739 "Expected an MU-RTS as ICF for the third frame exchange sequence");
741 thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
742 true,
743 "Expected a QoS data frame in the third frame exchange sequence");
744
746 +secondExchangeIt->front()->linkId,
747 +thirdExchangeIt->front()->linkId,
748 "Expected the second and third frame exchanges to occur on distinct links");
749
750 auto secondQosIt = secondExchangeIt->front();
751 auto secondQosTxEnd =
752 secondQosIt->startTx +
753 WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
754 secondQosIt->txVector,
755 m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
756
757 NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
758 secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
759 "Transmission started before transition delay");
760
761 // the BlockAck of the third frame exchange is not received correctly, so there should be
762 // another frame exchange
763 NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
764 true,
765 "Expected a fourth frame exchange");
766 auto fourthExchangeIt = std::next(thirdExchangeIt);
767
768 // the fourth frame exchange is protected by MU-RTS
769 NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
770 true,
771 "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
772
773 bAckRespIt = std::prev(fourthExchangeIt->front());
774 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
775 true,
776 "Expected a BlockAck response before the fourth frame exchange");
777 auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
778 bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
779 bAckRespIt->txVector,
780 phy->GetPhyBand());
781 auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
782
783 // the fourth frame exchange starts a PIFS after the previous one because the AP
784 // performs PIFS recovery (the initial frame in the TXOP was successfully received by
785 // a non-EMLSR client)
786 NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
787 bAckRespTxEnd + phy->GetPifs(),
788 "Transmission started less than a PIFS after BlockAck");
789 NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
790 bAckRespTxEnd + phy->GetPifs() +
791 MicroSeconds(1) /* propagation delay upper bound */,
792 "Transmission started too much time after BlockAck");
793
794 auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
795 NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
796 true,
797 "Expected a BlockAck request in the fourth frame exchange");
798
799 // we are done with processing the frame exchanges, remove them (two frame exchanges
800 // per EMLSR client, plus the last one)
801 frameExchanges.at(0).pop_front();
802 frameExchanges.at(0).pop_front();
803 frameExchanges.at(1).pop_front();
804 frameExchanges.at(1).pop_front();
805 frameExchanges.at(thirdExchangeStaId).pop_front();
806 }
807
808 /**
809 * After disabling EMLSR mode, no MU-RTS TF should be sent. After the exchange of
810 * EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
811 * to prepare two A-MPDUs for each EMLSR client.
812 *
813 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
814 * non-EMLSR client):
815 *
816 * [link 0] | power save mode
817 * ────────────────────────────────────────────────────────
818 * ┌─────┬─────┐ ┌──────┬──────┐
819 * │QoS 8│QoS 9│ │QoS 10│QoS 11│
820 * │ to A│ to A│ │ to A │ to A │
821 * ┌───┐ ┌───┐ ├─────┼─────┤ ├──────┼──────┤
822 * ┌───┐ │MU │ │EML│ │QoS 8│QoS 9│ │QoS 10│QoS 11│
823 * [link 1] │ACK│ │RTS│ │OM │ │ to B│ to B│ │ to B │ to B │
824 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴─────┴─────┴┬──┬────┴──────┴──────┴┬──┬─────
825 * │EML│ │CTS│ │ACK│ │BA│ │BA│
826 * │OM │ └───┘ └───┘ ├──┤ ├──┤
827 * └───┘ │BA│ │BA│
828 * └──┘ └──┘
829 *
830 * [link 2] | power save mode
831 * ────────────────────────────────────────────────────────────────────────────
832 *
833 *
834 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
835 * B is the non-EMLSR client):
836 * ┌─────┬─────┐
837 * │QoS 8│QoS 9│
838 * │ to A│ to A│
839 * ├─────┼─────┤
840 * │QoS 8│QoS 9│
841 * [link 0 - non EMLSR] │ to B│ to B│
842 * ─────────────────────────────────────────┴─────┴─────┴┬──┬─────────────
843 * │BA│
844 * ├──┤
845 * │BA│
846 * └──┘
847 * ┌──────┬──────┐
848 * │QoS 10│QoS 11│
849 * │ to A │ to A │
850 * ┌───┐ ┌───┐ ├──────┼──────┤
851 * ┌───┐ │MU │ │EML│ │QoS 10│QoS 11│
852 * [link 1] │ACK│ │RTS│ │OM │ │ to B │ to B │
853 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴──────┴──────┴┬──┬─────
854 * │EML│ │CTS│ │ACK│ │BA│
855 * │OM │ └───┘ └───┘ ├──┤
856 * └───┘ │BA│
857 * └──┘
858 *
859 * [link 2] | power save mode
860 * ────────────────────────────────────────────────────────────────────────────
861 *
862 */
863
864 // for each EMLSR client, there should be a frame exchange with ICF and no data frame
865 // (ICF protects the EML Notification response) if the EML Notification response is sent
866 // while EMLSR mode is still enabled and two frame exchanges with data frames
867 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
868 {
869 // the default EMLSR Manager requests to send EML Notification frames on the link where
870 // the main PHY is operating; if EMLSR mode is still enabled on this link when the AP MLD
871 // sends the EML Notification response, the latter is protected by an ICF
872 auto exchangeIt = frameExchanges.at(i).cbegin();
873
874 auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
875 NS_TEST_ASSERT_MSG_EQ(linkIdOpt.has_value(),
876 true,
877 "Didn't find a link on which the main PHY is operating");
878
879 if (IsTrigger(exchangeIt->front()->psduMap))
880 {
881 NS_TEST_EXPECT_MSG_EQ(+exchangeIt->front()->linkId,
882 +linkIdOpt.value(),
883 "ICF was not sent on the expected link");
884 NS_TEST_EXPECT_MSG_EQ(exchangeIt->size(),
885 1,
886 "Expected no data frame in the first frame exchange sequence");
887 frameExchanges.at(i).pop_front();
888 }
889
890 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
891 2,
892 "Expected at least 2 frame exchange sequences "
893 << "involving EMLSR client " << i);
894
895 auto firstExchangeIt = frameExchanges.at(i).cbegin();
896 auto secondExchangeIt = std::next(firstExchangeIt);
897
898 const auto firstAmpduTxEnd =
899 firstExchangeIt->back()->startTx +
901 firstExchangeIt->back()->psduMap,
902 firstExchangeIt->back()->txVector,
903 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
904 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
905
907 firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
908 true,
909 "Expected a QoS data frame in the first frame exchange sequence");
910 NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
911 1,
912 "Expected one frame only in the first frame exchange sequence");
913
915 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
916 true,
917 "Expected a QoS data frame in the second frame exchange sequence");
918 NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
919 1,
920 "Expected one frame only in the second frame exchange sequence");
921
922 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
923 {
924 // all links are EMLSR links: the two QoS data frames are sent one after another on
925 // the link used for sending EML OMN
927 +firstExchangeIt->front()->linkId,
928 +linkIdOpt.value(),
929 "First frame exchange expected to occur on link used to send EML OMN");
930
932 +secondExchangeIt->front()->linkId,
933 +linkIdOpt.value(),
934 "Second frame exchange expected to occur on link used to send EML OMN");
935
936 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
937 secondAmpduTxStart,
938 "A-MPDUs are not sent one after another");
939 }
940 else
941 {
942 // the two QoS data frames are sent concurrently on distinct links
943 NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
944 +secondExchangeIt->front()->linkId,
945 "Frame exchanges expected to occur on distinct links");
946
947 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
948 secondAmpduTxStart,
949 "A-MPDUs are not sent concurrently");
950 }
951 }
952}
953
954void
956{
957 std::optional<std::size_t> staId;
958 for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
959 {
960 if (m_staMacs.at(id)->GetLinkIdByAddress(address))
961 {
962 staId = id;
963 break;
964 }
965 }
966 NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
967
968 // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
969 // are considered to be in power save mode by the AP MLD; all the other links have
970 // transitioned to active mode instead
971 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
972 {
973 bool psModeExpected =
974 *staId < m_nEmlsrStations && linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 1;
975 auto addr = m_staMacs.at(*staId)->GetAddress();
976 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
978 psModeExpected,
979 "EMLSR link " << +linkId << " of EMLSR client " << *staId
980 << " not in " << (psModeExpected ? "PS" : "active")
981 << " mode");
982 // check that AP is blocking transmission of QoS data frames on this link
984 addr,
985 linkId,
986 WifiQueueBlockedReason::POWER_SAVE_MODE,
987 psModeExpected,
988 "Checking PM mode after association on AP MLD for EMLSR client " +
989 std::to_string(*staId),
990 false);
991 }
992}
993
994void
996 const WifiTxVector& txVector,
997 uint8_t linkId)
998{
999 // the AP is replying to a received EMLSR Notification frame
1000 auto pkt = mpdu->GetPacket()->Copy();
1001 const auto& hdr = mpdu->GetHeader();
1003 MgtEmlOmn frame;
1004 pkt->RemoveHeader(frame);
1005
1006 std::optional<std::size_t> staId;
1007 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1008 {
1009 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
1010 {
1011 staId = id;
1012 break;
1013 }
1014 }
1015 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1016 true,
1017 "Not an address of an EMLSR client " << hdr.GetAddr1());
1018
1019 // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
1020 auto phy = m_apMac->GetWifiPhy(linkId);
1021 auto txDuration =
1022 WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
1023 txVector,
1024 phy->GetPhyBand());
1025 WifiTxVector ackTxVector =
1026 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
1027 txVector);
1028 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
1029 ackTxVector,
1030 phy->GetPhyBand());
1031
1032 Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=, this]() {
1033 if (frame.m_emlControl.emlsrMode == 1)
1034 {
1035 // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
1036 // to be in active mode by the AP MLD
1037 for (const auto linkId : m_emlsrLinks)
1038 {
1039 auto addr = m_staMacs.at(*staId)->GetAddress();
1040 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1041 NS_TEST_EXPECT_MSG_EQ(psMode,
1042 false,
1043 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1044 << " not in active mode");
1045 // check that AP is not blocking transmission of QoS data frames on this link
1046 CheckBlockedLink(
1047 m_apMac,
1048 addr,
1049 linkId,
1050 WifiQueueBlockedReason::POWER_SAVE_MODE,
1051 false,
1052 "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
1053 std::to_string(*staId),
1054 false);
1055 }
1056 }
1057 else
1058 {
1059 // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
1060 // EML Notification frame) of the EMLSR clients are considered to be in power save
1061 // mode by the AP MLD; the other links are in active mode
1062 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1063 {
1064 bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
1065 auto addr = m_staMacs.at(*staId)->GetAddress();
1066 auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
1067 NS_TEST_EXPECT_MSG_EQ(psMode,
1068 psModeExpected,
1069 "EMLSR link "
1070 << +id << " of EMLSR client " << *staId << " not in "
1071 << (psModeExpected ? "PS" : "active") << " mode");
1072 // check that AP is blocking transmission of QoS data frames on this link
1073 CheckBlockedLink(
1074 m_apMac,
1075 addr,
1076 id,
1077 WifiQueueBlockedReason::POWER_SAVE_MODE,
1078 psModeExpected,
1079 "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
1080 std::to_string(*staId),
1081 false);
1082 }
1083 }
1084 });
1085}
1086
1087void
1089 const WifiTxVector& txVector,
1090 uint8_t linkId)
1091{
1092 // an EMLSR client is sending an EMLSR Notification frame
1093 auto pkt = mpdu->GetPacket()->Copy();
1094 const auto& hdr = mpdu->GetHeader();
1096 MgtEmlOmn frame;
1097 pkt->RemoveHeader(frame);
1098
1099 std::optional<std::size_t> staId;
1100 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1101 {
1102 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr2())
1103 {
1104 staId = id;
1105 break;
1106 }
1107 }
1108 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1109 true,
1110 "Not an address of an EMLSR client " << hdr.GetAddr1());
1111
1112 auto phy = m_staMacs.at(*staId)->GetWifiPhy(linkId);
1113 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(), txVector, phy->GetPhyBand());
1114 auto ackTxVector =
1115 m_apMac->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(), txVector);
1116 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize(), ackTxVector, phy->GetPhyBand());
1117 auto cfEndDuration = WifiPhy::CalculateTxDuration(
1119 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(
1121 txVector.GetChannelWidth()),
1122 phy->GetPhyBand());
1123
1124 if (frame.m_emlControl.emlsrMode != 0)
1125 {
1126 return;
1127 }
1128
1129 // EMLSR mode disabled
1130 auto timeToCfEnd = txDuration + phy->GetSifs() + ackDuration + phy->GetSifs() + cfEndDuration;
1131
1132 // before the end of the CF-End frame, this link only is not blocked on both the
1133 // EMLSR client and the AP MLD
1134 Simulator::Schedule(timeToCfEnd - MicroSeconds(1), [=, this]() {
1135 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1136 {
1137 CheckBlockedLink(m_staMacs.at(*staId),
1138 m_apMac->GetAddress(),
1139 id,
1140 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1141 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1142 "Checking links on EMLSR client " + std::to_string(*staId) +
1143 " before the end of CF-End frame");
1145 m_staMacs.at(*staId)->GetAddress(),
1146 id,
1147 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1148 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1149 "Checking links of EMLSR client " + std::to_string(*staId) +
1150 " on the AP MLD before the end of CF-End frame");
1151 }
1152 });
1153 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
1154 // AP MLD
1155 Simulator::Schedule(timeToCfEnd + MicroSeconds(1), [=, this]() {
1156 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1157 {
1158 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1159 {
1160 CheckBlockedLink(
1161 m_apMac,
1162 m_staMacs.at(*staId)->GetAddress(),
1163 id && m_staMacs.at(*staId)->IsEmlsrLink(id),
1164 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1165 true,
1166 "Checking links of EMLSR client " + std::to_string(*staId) +
1167 " are all blocked on the AP MLD right after the end of CF-End");
1168 }
1169 }
1170 });
1171 // before the end of the transition delay, all links for the EMLSR client are still
1172 // blocked on the AP MLD
1173 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) - MicroSeconds(1), [=, this]() {
1174 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1175 {
1176 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1177 {
1178 CheckBlockedLink(m_apMac,
1179 m_staMacs.at(*staId)->GetAddress(),
1180 id,
1181 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1182 true,
1183 "Checking links of EMLSR client " + std::to_string(*staId) +
1184 " are all blocked on the AP MLD before the end of "
1185 "transition delay");
1186 }
1187 }
1188 });
1189 // immediately after the transition delay, all links for the EMLSR client are unblocked
1190 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) + MicroSeconds(1), [=, this]() {
1191 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1192 {
1193 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1194 {
1195 CheckBlockedLink(m_apMac,
1196 m_staMacs.at(*staId)->GetAddress(),
1197 id,
1198 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1199 false,
1200 "Checking links of EMLSR client " + std::to_string(*staId) +
1201 " are all unblocked on the AP MLD after the transition delay");
1202 }
1203 }
1204 });
1205}
1206
1207void
1209 const WifiTxVector& txVector,
1210 uint8_t linkId)
1211{
1212 CtrlTriggerHeader trigger;
1213 mpdu->GetPacket()->PeekHeader(trigger);
1214 if (!trigger.IsMuRts())
1215 {
1216 return;
1217 }
1218
1220 true,
1221 "Did not expect an ICF before enabling EMLSR mode");
1222
1225 "Unexpected preamble type for the Initial Control frame");
1226 auto rate = txVector.GetMode().GetDataRate(txVector);
1227 NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
1228 true,
1229 "Unexpected rate for the Initial Control frame: " << rate);
1230
1231 bool found = false;
1232 Time maxPaddingDelay{};
1233
1234 for (const auto& userInfo : trigger)
1235 {
1236 auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
1237 NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
1238 true,
1239 "AID " << userInfo.GetAid12() << " not found");
1240
1241 if (m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
1242 {
1243 found = true;
1244
1245 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1246 {
1247 if (m_staMacs.at(i)->GetAddress() == *addr)
1248 {
1249 maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
1250 break;
1251 }
1252 }
1253
1254 // check that the AP has blocked transmission on all other EMLSR links
1255 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1256 {
1257 if (!m_apMac->GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*addr))
1258 {
1259 continue;
1260 }
1261
1263 *addr,
1264 id,
1265 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1266 id != linkId,
1267 "Checking that AP blocked transmissions on all other EMLSR "
1268 "links after sending ICF to client with AID=" +
1269 std::to_string(userInfo.GetAid12()),
1270 false);
1271 }
1272 }
1273 }
1274
1275 NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
1276
1277 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
1278 txVector,
1279 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1280
1281 if (maxPaddingDelay.IsStrictlyPositive())
1282 {
1283 // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
1284 // padding added
1285 trigger.SetPaddingSize(0);
1286 auto pkt = Create<Packet>();
1287 pkt->AddHeader(trigger);
1288 auto txDurationWithout =
1289 WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
1290 txVector,
1291 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1292
1293 NS_TEST_EXPECT_MSG_EQ(txDuration,
1294 txDurationWithout + maxPaddingDelay,
1295 "Unexpected TX duration of the MU-RTS TF with padding "
1296 << maxPaddingDelay.As(Time::US));
1297 }
1298
1299 // check that the EMLSR clients have blocked transmissions on other links, switched their main
1300 // PHY (if needed) and have put aux PHYs to sleep after receiving this ICF
1301 for (const auto& userInfo : trigger)
1302 {
1303 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1304 {
1305 if (m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
1306 {
1307 continue;
1308 }
1309
1310 const auto mainPhyLinkId = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
1311
1312 Simulator::Schedule(txDuration + NanoSeconds(5), [=, this]() {
1313 for (uint8_t id = 0; id < m_staMacs[i]->GetNLinks(); id++)
1314 {
1315 // non-EMLSR links or links on which ICF is received are not blocked
1317 m_apMac->GetAddress(),
1318 id,
1319 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1320 id != linkId && m_staMacs[i]->IsEmlsrLink(id),
1321 "Checking EMLSR links on EMLSR client " + std::to_string(i) +
1322 " after receiving ICF");
1323 }
1324
1325 if (mainPhyLinkId != linkId)
1326 {
1327 CheckMainPhyTraceInfo(i, "DlTxopIcfReceivedByAuxPhy", mainPhyLinkId, linkId);
1328 }
1329
1331 });
1332
1333 break;
1334 }
1335 }
1336}
1337
1338void
1340 const WifiTxVector& txVector,
1341 uint8_t linkId)
1342{
1343 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
1345
1346 {
1347 // we are interested in frames sent to test transition delay
1348 return;
1349 }
1350
1351 std::size_t firstClientId = 0;
1352 std::size_t secondClientId = 1;
1353 auto addr = m_staMacs[secondClientId]->GetAddress();
1354 auto txDuration =
1355 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1356
1358
1359 switch (m_countQoSframes)
1360 {
1361 case 1:
1362 // generate another small packet addressed to the first EMLSR client only
1363 m_apMac->GetDevice()->GetNode()->AddApplication(
1364 GetApplication(DOWNLINK, firstClientId, 1, 40));
1365 // both EMLSR clients are about to receive a QoS data frame
1366 for (std::size_t clientId : {firstClientId, secondClientId})
1367 {
1368 Simulator::Schedule(txDuration, [=, this]() {
1369 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
1370 {
1371 // link on which QoS data is received is not blocked
1372 CheckBlockedLink(m_staMacs[clientId],
1373 m_apMac->GetAddress(),
1374 id,
1375 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1376 id != linkId,
1377 "Checking EMLSR links on EMLSR client " +
1378 std::to_string(clientId) +
1379 " after receiving the first QoS data frame");
1380 }
1381 });
1382 }
1383 break;
1384 case 2:
1385 // generate another small packet addressed to the second EMLSR client
1386 m_apMac->GetDevice()->GetNode()->AddApplication(
1387 GetApplication(DOWNLINK, secondClientId, 1, 40));
1388
1389 // when the transmission of the second QoS data frame starts, both EMLSR clients are
1390 // still blocking all the links but the one used to receive the QoS data frame
1391 for (std::size_t clientId : {firstClientId, secondClientId})
1392 {
1393 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
1394 {
1395 // link on which QoS data is received is not blocked
1396 CheckBlockedLink(m_staMacs[clientId],
1397 m_apMac->GetAddress(),
1398 id,
1399 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1400 id != linkId,
1401 "Checking EMLSR links on EMLSR client " +
1402 std::to_string(clientId) +
1403 " when starting the reception of the second QoS frame");
1404 }
1405 }
1406
1407 // the EMLSR client that is not the recipient of the QoS frame being transmitted will
1408 // switch back to listening mode after a transition delay starting from the end of
1409 // the PPDU carrying this QoS data frame
1410
1411 // immediately before the end of the PPDU, this link only is not blocked for the EMLSR
1412 // client on the AP MLD
1413 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
1414 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1415 {
1417 addr,
1418 id,
1419 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1420 id != linkId,
1421 "Checking that links of EMLSR client " +
1422 std::to_string(secondClientId) +
1423 " are blocked on the AP MLD before the end of the PPDU");
1424 }
1425 });
1426 // immediately before the end of the PPDU, all the links on the EMLSR client that is not
1427 // the recipient of the second QoS frame are unblocked (they are unblocked when the
1428 // PHY-RXSTART.indication is not received)
1429 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
1430 for (uint8_t id = 0; id < m_staMacs[secondClientId]->GetNLinks(); id++)
1431 {
1432 CheckBlockedLink(m_staMacs[secondClientId],
1433 m_apMac->GetAddress(),
1434 id,
1435 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1436 false,
1437 "Checking that links of EMLSR client " +
1438 std::to_string(secondClientId) +
1439 " are unblocked before the end of the second QoS frame");
1440 }
1441 });
1442 // immediately after the end of the PPDU, all links are blocked for the EMLSR client
1443 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
1444 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1445 {
1447 addr,
1448 id,
1449 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1450 true,
1451 "Checking links of EMLSR client " +
1452 std::to_string(secondClientId) +
1453 " are all blocked on the AP MLD after the end of the PPDU");
1454 }
1455 });
1456 // immediately before the transition delay, all links are still blocked for the EMLSR client
1458 txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
1459 [=, this]() {
1460 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1461 {
1463 m_apMac,
1464 addr,
1465 id,
1466 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1467 true,
1468 "Checking links of EMLSR client " + std::to_string(secondClientId) +
1469 " are all blocked on the AP MLD before the transition delay",
1470 false);
1471 }
1472 });
1473
1474 // 100 us before the transition delay expires, generate another small packet addressed
1475 // to a non-EMLSR client. The AP will start a TXOP to transmit this frame, while the
1476 // frame addressed to the EMLSR client is still queued because the transition delay has
1477 // not yet elapsed. The transition delay will expire while the AP is transmitting the
1478 // frame to the non-EMLSR client, so that the AP continues the TXOP to transmit the frame
1479 // to the EMLSR client and we can check that the AP performs PIFS recovery after missing
1480 // the BlockAck from the EMLSR client
1481 Simulator::Schedule(txDuration + m_transitionDelay.at(secondClientId) - MicroSeconds(100),
1482 [=, this]() {
1483 m_apMac->GetDevice()->GetNode()->AddApplication(
1485 });
1486
1487 break;
1488 case 3:
1489 // this is the frame addressed to a non-EMLSR client, which is transmitted before the
1490 // frame addressed to the EMLSR client, because the links of the latter are still blocked
1491 // at the AP because the transition delay has not yet elapsed
1493 psduMap.cbegin()->second->GetAddr1(),
1494 m_staMacs[m_nEmlsrStations]->GetFrameExchangeManager(linkId)->GetAddress(),
1495 "QoS frame not addressed to a non-EMLSR client");
1496
1497 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1498 {
1500 addr,
1501 id,
1502 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1503 true,
1504 "Checking links of EMLSR client " + std::to_string(secondClientId) +
1505 " are all blocked on the AP MLD before the transition delay");
1506 }
1507 // Block transmissions to the EMLSR client on all the links but the one on which this
1508 // frame is sent, so that the AP will continue this TXOP to send the queued frame to the
1509 // EMLSR client once the transition delay elapses
1510 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1511 {
1512 if (id != linkId)
1513 {
1514 m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
1515 }
1516 }
1517 break;
1518 case 4:
1519 // the AP is continuing the TXOP, no need to block transmissions anymore
1520 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1521 {
1522 m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
1523 }
1524 // at the end of the fourth QoS frame, this link only is not blocked on the EMLSR
1525 // client receiving the frame
1526 Simulator::Schedule(txDuration, [=, this]() {
1527 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1528 {
1529 CheckBlockedLink(m_staMacs[secondClientId],
1530 m_apMac->GetAddress(),
1531 id,
1532 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1533 id != linkId,
1534 "Checking EMLSR links on EMLSR client " +
1535 std::to_string(secondClientId) +
1536 " after receiving the fourth QoS data frame");
1537 }
1538 });
1539 break;
1540 }
1541}
1542
1543void
1545 const WifiTxVector& txVector,
1546 uint8_t phyId)
1547{
1548 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
1550 {
1551 // we are interested in frames sent to test transition delay
1552 return;
1553 }
1554
1555 if (++m_countBlockAck == 4)
1556 {
1557 // fourth BlockAck is sent by a non-EMLSR client
1558 return;
1559 }
1560
1561 auto taddr = psduMap.cbegin()->second->GetAddr2();
1562 std::size_t clientId;
1563 if (m_staMacs[0]->GetLinkIdByAddress(taddr))
1564 {
1565 clientId = 0;
1566 }
1567 else
1568 {
1569 NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
1570 true,
1571 "Unexpected TA for BlockAck: " << taddr);
1572 clientId = 1;
1573 }
1574
1575 // find the link on which the main PHY is operating
1576 auto currMainPhyLinkId = m_staMacs[clientId]->GetLinkForPhy(phyId);
1578 currMainPhyLinkId.has_value(),
1579 true,
1580 "Didn't find the link on which the PHY sending the BlockAck is operating");
1581 auto linkId = *currMainPhyLinkId;
1582
1583 // we need the MLD address to check the status of the container queues
1584 auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
1585 NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
1586
1587 auto apPhy = m_apMac->GetWifiPhy(linkId);
1588 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
1589 auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
1591 m_apMac->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(Mac48Address::GetBroadcast(),
1592 txVector.GetChannelWidth()),
1593 apPhy->GetPhyBand());
1594
1595 switch (m_countBlockAck)
1596 {
1597 case 5:
1598 // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
1599 // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
1600 // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
1601 // not responded and makes an attempt at continuing the TXOP
1602
1603 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
1604 // the AP MLD
1605 Simulator::Schedule(txDuration, [=, this]() {
1606 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1607 {
1608 CheckBlockedLink(m_staMacs[clientId],
1609 m_apMac->GetAddress(),
1610 id,
1611 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1612 id != linkId,
1613 "Checking links on EMLSR client " + std::to_string(clientId) +
1614 " at the end of fourth BlockAck");
1616 *addr,
1617 id,
1618 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1619 id != linkId,
1620 "Checking links of EMLSR client " + std::to_string(clientId) +
1621 " on the AP MLD at the end of fourth BlockAck");
1622 }
1623 });
1624 // a SIFS after the end of the PPDU, still this link only is not blocked on both the
1625 // EMLSR client and the AP MLD
1626 Simulator::Schedule(txDuration + apPhy->GetSifs(), [=, this]() {
1627 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1628 {
1629 CheckBlockedLink(m_staMacs[clientId],
1630 m_apMac->GetAddress(),
1631 id,
1632 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1633 id != linkId,
1634 "Checking links on EMLSR client " + std::to_string(clientId) +
1635 " a SIFS after the end of fourth BlockAck");
1636 CheckBlockedLink(m_apMac,
1637 *addr,
1638 id,
1639 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1640 id != linkId,
1641 "Checking links of EMLSR client " + std::to_string(clientId) +
1642 " a SIFS after the end of fourth BlockAck");
1643 }
1644 });
1645 // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
1646 {
1647 auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
1648 m_errorModel->SetList({uid});
1649 }
1650 break;
1651 case 6:
1652 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
1653 // the AP MLD
1654 Simulator::Schedule(txDuration, [=, this]() {
1655 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1656 {
1657 CheckBlockedLink(m_staMacs[clientId],
1658 m_apMac->GetAddress(),
1659 id,
1660 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1661 id != linkId,
1662 "Checking links on EMLSR client " + std::to_string(clientId) +
1663 " at the end of fifth BlockAck");
1665 *addr,
1666 id,
1667 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1668 id != linkId,
1669 "Checking links of EMLSR client " + std::to_string(clientId) +
1670 " on the AP MLD at the end of fifth BlockAck");
1671 }
1672 });
1673 // before the end of the CF-End frame, still this link only is not blocked on both the
1674 // EMLSR client and the AP MLD
1676 txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
1677 [=, this]() {
1678 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1679 {
1680 CheckBlockedLink(m_staMacs[clientId],
1681 m_apMac->GetAddress(),
1682 id,
1683 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1684 id != linkId,
1685 "Checking links on EMLSR client " + std::to_string(clientId) +
1686 " before the end of CF-End frame");
1688 *addr,
1689 id,
1690 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1691 id != linkId,
1692 "Checking links of EMLSR client " + std::to_string(clientId) +
1693 " on the AP MLD before the end of CF-End frame");
1694 }
1695 });
1696 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
1697 // AP MLD
1699 txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
1700 [=, this]() {
1701 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1702 {
1704 m_apMac,
1705 *addr,
1706 id,
1707 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1708 true,
1709 "Checking links of EMLSR client " + std::to_string(clientId) +
1710 " are all blocked on the AP MLD right after the end of CF-End");
1711 }
1712 });
1713 // before the end of the transition delay, all links for the EMLSR client are still
1714 // blocked on the AP MLD
1716 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
1717 MicroSeconds(1),
1718 [=, this]() {
1719 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1720 {
1722 m_apMac,
1723 *addr,
1724 id,
1725 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1726 true,
1727 "Checking links of EMLSR client " + std::to_string(clientId) +
1728 " are all blocked on the AP MLD before the end of transition delay");
1729 }
1730 });
1731 // immediately after the transition delay, all links for the EMLSR client are unblocked
1733 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
1734 MicroSeconds(1),
1735 [=, this]() {
1736 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1737 {
1739 m_apMac,
1740 *addr,
1741 id,
1742 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1743 false,
1744 "Checking links of EMLSR client " + std::to_string(clientId) +
1745 " are all unblocked on the AP MLD after the transition delay");
1746 }
1747 });
1748 break;
1749 }
1750}
1751
1752void
1762
1764 : EmlsrOperationsTestBase("Check EML UL TXOP transmissions (genBackoffIfTxopWithoutTx=" +
1765 std::to_string(params.genBackoffIfTxopWithoutTx)),
1766 m_emlsrLinks(params.linksToEnableEmlsrOn),
1767 m_channelWidth(params.channelWidth),
1768 m_auxPhyChannelWidth(params.auxPhyChannelWidth),
1769 m_mediumSyncDuration(params.mediumSyncDuration),
1770 m_msdMaxNTxops(params.msdMaxNTxops),
1771 m_emlsrEnabledTime(0),
1772 m_firstUlPktsGenTime(0),
1773 m_unblockMainPhyLinkDelay(MilliSeconds(20)),
1774 m_checkBackoffStarted(false),
1775 m_countQoSframes(0),
1776 m_countBlockAck(0),
1777 m_countRtsframes(0),
1778 m_genBackoffIfTxopWithoutTx(params.genBackoffIfTxopWithoutTx)
1779{
1780 m_nEmlsrStations = 1;
1782 m_linksToEnableEmlsrOn = params.linksToEnableEmlsrOn;
1783 m_mainPhyId = 1;
1784
1785 // when aux PHYs do not switch link, the main PHY switches back to its previous link after
1786 // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
1788 m_establishBaDl = {0};
1789 m_establishBaUl = {0};
1790 m_putAuxPhyToSleep = params.putAuxPhyToSleep;
1791 m_duration = Seconds(1);
1792
1793 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
1794 "This test requires at least two links to be configured as EMLSR links");
1795 for (uint8_t id = 0; id < 3; id++)
1796 {
1797 if (!m_emlsrLinks.contains(id))
1798 {
1799 // non-EMLSR link found
1800 m_nonEmlsrLink = id;
1801 break;
1802 }
1803 }
1804}
1805
1806void
1808{
1809 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth",
1811 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false));
1812 // switch main PHY back delay should be at least a PIFS for the switch to occur
1813 Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration",
1815 Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops));
1816 Config::SetDefault("ns3::ChannelAccessManager::GenerateBackoffIfTxopWithoutTx",
1818 // Channel switch delay should be less than RTS TX time + SIFS + CTS TX time, otherwise
1819 // UL TXOPs cannot be initiated by aux PHYs
1820 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
1821 Config::SetDefault("ns3::WifiPhy::NotifyMacHdrRxEnd", BooleanValue(true));
1822
1824
1825 m_staMacs[0]->GetQosTxop(AC_BE)->TraceConnectWithoutContext(
1826 "BackoffTrace",
1828
1829 uint8_t linkId = 0;
1830 // configure channels of the given width
1832 {
1833 MHz_u bw{20};
1834 uint8_t number = band == WIFI_PHY_BAND_5GHZ ? 36 : 1;
1835
1836 auto width =
1837 std::min(m_channelWidth, band == WIFI_PHY_BAND_2_4GHZ ? MHz_u{40} : MHz_u{160});
1838 while (bw < width)
1839 {
1840 bw *= 2;
1841 number += Count20MHzSubchannels(bw);
1842 }
1843
1844 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
1845 {
1846 mac->GetWifiPhy(linkId)->SetOperatingChannel(
1847 WifiPhy::ChannelTuple{number, width, band, 0});
1848 }
1849 linkId++;
1850 }
1851
1852 // install post reception error model on the AP affiliated with the AP MLD and operating on
1853 // the same link as the main PHY of the EMLSR client
1855 m_apMac->GetWifiPhy(m_mainPhyId)->SetPostReceptionErrorModel(m_errorModel);
1856}
1857
1858void
1860{
1861 NS_LOG_INFO("Backoff value " << backoff << " generated by EMLSR client on link " << +linkId
1862 << "\n");
1863 if (linkId != m_mainPhyId)
1864 {
1865 return; // we are only interested in backoff on main PHY link
1866 }
1867
1868 if (m_backoffEndTime)
1869 {
1871 {
1872 // another backoff value while checkBackoffStarted is true is generated only if
1873 // GenerateBackoffIfTxopWithoutTx is true
1876 true,
1877 "Another backoff value should not be generated while the main PHY link is blocked");
1878
1881 "Backoff generated at unexpected time");
1882 }
1883 else
1884 {
1885 // we are done checking the backoff
1886 m_backoffEndTime.reset();
1887 }
1888 }
1889
1891 {
1892 if (!m_backoffEndTime.has_value())
1893 {
1894 // this is the first time we set m_backoffEndTime, which is done right after receiving
1895 // a BlockAck, thus we have to wait an AIFS before invoking backoff
1897 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
1898 m_staMacs[0]->GetQosTxop(AC_BE)->GetAifsn(linkId) *
1899 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
1900 }
1901 else
1902 {
1903 // we get here when the backoff expired but no transmission occurred, thus we have
1904 // generated a new backoff value and we will start decrementing the counter in a slot
1906 Simulator::Now() + m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
1907 }
1908 // add the time corresponding to the generated number of slots
1909 m_backoffEndTime.value() +=
1910 backoff * m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
1911 NS_LOG_DEBUG("Expected backoff end time = " << m_backoffEndTime->As(Time::US) << "\n");
1912 }
1913}
1914
1915void
1917 uint8_t phyId,
1918 WifiConstPsduMap psduMap,
1919 WifiTxVector txVector,
1920 double txPowerW)
1921{
1922 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
1923 auto linkId = m_txPsdus.back().linkId;
1924
1925 auto psdu = psduMap.begin()->second;
1926 auto nodeId = mac->GetDevice()->GetNode()->GetId();
1927
1928 switch (psdu->GetHeader(0).GetType())
1929 {
1931 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
1932 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
1933 break;
1934
1935 case WIFI_MAC_CTL_RTS:
1936 CheckRtsFrames(*psdu->begin(), txVector, linkId);
1937 break;
1938
1939 case WIFI_MAC_CTL_CTS:
1940 CheckCtsFrames(*psdu->begin(), txVector, linkId);
1941 break;
1942
1943 case WIFI_MAC_QOSDATA:
1944 CheckQosFrames(psduMap, txVector, linkId);
1945 break;
1946
1948 CheckBlockAck(psduMap, txVector, linkId);
1949 break;
1950
1951 default:;
1952 }
1953}
1954
1955void
1957{
1958 // initially, we prevent transmissions on aux PHY links
1959 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
1960 auxPhyLinks.erase(m_mainPhyId);
1961 if (m_nonEmlsrLink)
1962 {
1963 auxPhyLinks.erase(*m_nonEmlsrLink);
1964 }
1965 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
1966 m_apMac->GetAddress(),
1967 auxPhyLinks);
1968
1969 // Association, Block Ack agreement establishment and enabling EMLSR mode have been done.
1970 // After 50ms, schedule:
1971 // - block of transmissions on the link where the main PHY is operating and on the non-EMLSR
1972 // link (if any)
1973 // - the generation of two UL packets
1974 // - after m_unblockMainPhyLinkDelay, unblock transmissions on the link where the main PHY
1975 // is operating, so that the first data frame is transmitted on that link
1977 std::set<uint8_t> linkIds;
1978 linkIds.insert(*m_staMacs[0]->GetLinkForPhy(m_mainPhyId));
1979 if (m_nonEmlsrLink)
1980 {
1981 linkIds.insert(*m_nonEmlsrLink);
1982 }
1983 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
1984 m_apMac->GetAddress(),
1985 linkIds);
1986
1987 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
1988 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
1990
1992 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
1993 m_apMac->GetAddress(),
1994 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
1995 });
1996 });
1997}
1998
1999void
2001 const WifiTxVector& txVector,
2002 uint8_t linkId)
2003{
2005
2006 auto txDuration =
2007 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2008
2009 switch (m_countQoSframes)
2010 {
2011 case 1:
2012 case 2:
2013 // do nothing, these are the QoS data frames sent to establish BA agreements in DL and UL
2014 // direction
2015 break;
2016 case 3:
2017 // first UL data frame (transmitted by the main PHY)
2018 if (m_nonEmlsrLink)
2019 {
2020 // generate data packets for another UL data frame, which will be sent on the
2021 // non-EMLSR link
2022 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2023 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2024 GetApplication(UPLINK, 0, 2, 1000));
2025
2026 // unblock transmissions on the non-EMLSR link once the two packets are queued
2027 Simulator::ScheduleNow([=, this]() {
2028 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2029 m_apMac->GetAddress(),
2030 {*m_nonEmlsrLink});
2031 });
2032 }
2033
2034 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2035 // after this QoS data frame is received
2036 Simulator::ScheduleNow([=, this]() {
2037 auto phyHdrTxTime = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
2038 auto macHdrSize = (*psduMap.at(SU_STA_ID)->begin())->GetHeader().GetSerializedSize() +
2039 4 /* A-MPDU subframe header size */;
2040 auto macHdrTxTime =
2041 DataRate(txVector.GetMode().GetDataRate(txVector)).CalculateBytesTxTime(macHdrSize);
2042
2043 for (auto id : m_staMacs[0]->GetLinkIds())
2044 {
2046 m_staMacs[0],
2047 m_apMac->GetAddress(),
2048 id,
2049 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2050 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && m_staMacs[0]->IsEmlsrLink(id),
2051 "Checking EMLSR links on EMLSR client while sending the first data frame",
2052 false);
2053
2054 Simulator::Schedule(phyHdrTxTime + macHdrTxTime + MicroSeconds(1), [=, this]() {
2056 m_staMacs[0]->GetAddress(),
2057 id,
2058 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2059 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2060 m_staMacs[0]->IsEmlsrLink(id),
2061 "Checking EMLSR links on AP MLD right after receiving the MAC "
2062 "header of the first data frame");
2063 });
2064
2065 Simulator::Schedule(txDuration + MAX_PROPAGATION_DELAY, [=, this]() {
2067 m_apMac,
2068 m_staMacs[0]->GetAddress(),
2069 id,
2070 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2071 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2072 m_staMacs[0]->IsEmlsrLink(id),
2073 "Checking EMLSR links on AP MLD after sending the first data frame");
2074 });
2075 }
2076 });
2077
2078 if (m_nonEmlsrLink)
2079 {
2080 break;
2081 }
2082 m_countQoSframes++; // if all EMLSR links, next case is already executed now
2083 [[fallthrough]];
2084 case 4:
2085 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2086 // after this QoS data frame is received
2087 Simulator::ScheduleNow([=, this]() {
2088 // make aux PHYs capable of transmitting frames
2089 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2090 auxPhyLinks.erase(m_mainPhyId);
2091 if (m_nonEmlsrLink)
2092 {
2093 auxPhyLinks.erase(*m_nonEmlsrLink);
2094 }
2095 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2096 m_apMac->GetAddress(),
2097 auxPhyLinks);
2098
2099 // block transmissions on the link where the main PHY is operating
2100 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2101 m_apMac->GetAddress(),
2102 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2103
2104 // generate data packets for another UL data frame, which will be sent on a link on
2105 // which an aux PHY is operating
2106 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2107 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2108 GetApplication(UPLINK, 0, 2, 1000));
2109 });
2110 break;
2111 case 5:
2112 // check that other EMLSR links are now blocked on both the EMLSR client and the AP MLD
2113 Simulator::ScheduleNow([=, this]() {
2114 for (auto id : m_staMacs[0]->GetLinkIds())
2115 {
2117 m_staMacs[0],
2118 m_apMac->GetAddress(),
2119 id,
2120 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2121 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2122 "Checking EMLSR links on EMLSR client while sending the second data frame",
2123 false);
2124
2126 m_apMac,
2127 m_staMacs[0]->GetAddress(),
2128 id,
2129 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2130 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2131 "Checking EMLSR links on AP MLD while sending the second data frame",
2132 false);
2133 }
2134
2135 // unblock transmission on the link where the main PHY is operating
2136 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
2137 WifiQueueBlockedReason::TID_NOT_MAPPED,
2138 AC_BE,
2140 m_apMac->GetAddress(),
2141 m_staMacs[0]->GetAddress(),
2142 {0},
2143 {m_mainPhyId});
2144 });
2145 break;
2146 }
2147}
2148
2149void
2151 const WifiTxVector& txVector,
2152 uint8_t linkId)
2153{
2155
2156 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2157 auxPhyLinks.erase(m_mainPhyId);
2158 if (m_nonEmlsrLink)
2159 {
2160 auxPhyLinks.erase(*m_nonEmlsrLink);
2161 }
2162
2163 auto txDuration =
2164 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2165
2166 // in this test, BlockAck frames terminates TXOP, thus aux PHYs shall be in sleep mode before
2167 // the end of BlockAck reception and awake right afterwards
2168 if (linkId != m_nonEmlsrLink)
2169 {
2170 Simulator::Schedule(txDuration - TimeStep(1),
2172 this,
2173 m_staMacs[0],
2174 true);
2175 Simulator::Schedule(txDuration + TimeStep(1),
2177 this,
2178 m_staMacs[0],
2179 false);
2180
2181 // if the TXOP has been carried out on a link other than the preferred link, the main PHY
2182 // switches back to the preferred link when the TXOP ends
2183 if (m_staMacs[0]->GetLinkForPhy(m_mainPhyId) != linkId)
2184 {
2185 Simulator::Schedule(txDuration + TimeStep(1), [=, this]() {
2186 CheckMainPhyTraceInfo(0, "TxopEnded", linkId, m_mainPhyId);
2187 });
2188 }
2189 }
2190
2191 switch (m_countBlockAck)
2192 {
2193 case 1:
2194 case 2:
2195 // do nothing, these are BlockAcks in response to the QoS data frames sent to establish
2196 // BA agreements in DL and UL direction
2197 break;
2198 case 3:
2199 if (linkId == m_nonEmlsrLink)
2200 {
2201 // this BlockAck has been sent on the non-EMLSR link, ignore it
2202 break;
2203 }
2204 m_checkBackoffStarted = true;
2205 if (!m_nonEmlsrLink)
2206 {
2207 m_countBlockAck++; // if all EMLSR links, next case is already executed now
2208 }
2209 [[fallthrough]];
2210 case 4:
2211 if (m_nonEmlsrLink && m_countBlockAck == 4)
2212 {
2213 // block transmissions on the non-EMLSR link
2214 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2215 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2216 m_apMac->GetAddress(),
2217 {*m_nonEmlsrLink});
2218 });
2219 }
2220 if (linkId == m_nonEmlsrLink)
2221 {
2222 // this BlockAck has been sent on the non-EMLSR link, ignore it
2223 break;
2224 }
2225 m_checkBackoffStarted = true;
2226 break;
2227 case 5:
2228 // Block Ack in response to the second data frame sent by the EMLSR client on EMLSR links.
2229 // Check that MediumSyncDelay timer starts running on the link where the main PHY switches
2230 // to when the channel switch is completed
2232 txDuration + m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelSwitchDelay() +
2233 NanoSeconds(1),
2234 [=, this]() {
2235 auto elapsed =
2236 m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_mainPhyId);
2238 elapsed.has_value(),
2239 true,
2240 "MediumSyncDelay timer not running on link where main PHY is operating");
2242 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
2243 *elapsed;
2244 });
2245
2246 // Check that the number of backoff slots is not changed since the beginning of the TXOP
2247 Simulator::Schedule(txDuration, [=, this]() {
2248 m_checkBackoffStarted = false;
2250 true,
2251 "Backoff end time should have been calculated");
2252 // when this BlockAck is received, the TXOP ends and the main PHY link is unblocked,
2253 // which causes a new backoff timer to be generated if the backoff timer is not
2254 // already running
2256 });
2257
2258 // make aux PHYs not capable of transmitting frames
2259 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2260 m_apMac->GetAddress(),
2261 auxPhyLinks);
2262
2263 // generate data packets for another UL data frame, which will be sent on the link where
2264 // the main PHY is operating
2265 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2266 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2267 break;
2268 case 6: {
2269 // block transmission on the main PHY link and on the non-EMLSR link (if any), so that
2270 // the next QoS frames are sent on a link where an aux PHY is operating
2271 std::set<uint8_t> linkIds{m_mainPhyId};
2272 if (m_nonEmlsrLink)
2273 {
2274 linkIds.insert(*m_nonEmlsrLink);
2275 }
2276 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2277 AC_BE,
2279 m_apMac->GetAddress(),
2280 m_staMacs[0]->GetAddress(),
2281 {0},
2282 linkIds);
2283 }
2284 // make sure aux PHYs are capable of transmitting frames
2285 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2286 m_apMac->GetAddress(),
2287 auxPhyLinks);
2288
2289 // generate data packets for another UL data frame
2290 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2291 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2292 break;
2293 }
2294}
2295
2296void
2298 const WifiTxVector& txVector,
2299 uint8_t linkId)
2300{
2302 {
2303 // this function only considers RTS frames sent after the first QoS data frame
2304 return;
2305 }
2306
2307 if (linkId != m_mainPhyId)
2308 {
2309 if (m_countRtsframes > 0 && !m_corruptCts.has_value())
2310 {
2311 // we get here for the frame exchange in which the CTS response must be corrupted.
2312 // Install post reception error model on the STA affiliated with the EMLSR client that
2313 // is transmitting this RTS frame
2315 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
2316 m_corruptCts = true;
2317 }
2318
2319 return;
2320 }
2321
2322 // we get here for RTS frames sent by the main PHY while the MediumSyncDelay timer is running
2324
2326 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
2327 "RTS sent by main PHY on an unexpected width");
2328
2329 // corrupt reception at AP MLD
2330 NS_LOG_INFO("CORRUPTED");
2331 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2332}
2333
2334void
2336 const WifiTxVector& txVector,
2337 uint8_t linkId)
2338{
2340 {
2341 // this function only considers CTS frames sent after the first QoS data frame
2342 return;
2343 }
2344
2345 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
2346 txVector,
2347 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2348 const auto doCorruptCts = m_corruptCts.has_value() && *m_corruptCts;
2349
2350 if (linkId != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && linkId != m_nonEmlsrLink &&
2351 mpdu->GetHeader().GetAddr1() == m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress())
2352 {
2353 // this is a CTS sent to an aux PHY starting an UL TXOP. Given that aux PHYs do not
2354 // switch channel, they are put in sleep mode when the main PHY starts operating on their
2355 // link, which coincides with the end of CTS plus two propagation delays
2356 const auto auxPhy = m_staMacs[0]->GetWifiPhy(linkId);
2357 const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
2358 Simulator::Schedule(txDuration, [=, this]() {
2359 // when CTS ends, the main PHY is still switching and the aux PHY is not yet sleeping
2360 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
2361 true,
2362 "Expecting the main PHY to be switching link");
2363 NS_TEST_EXPECT_MSG_EQ(auxPhy->IsStateSleep(),
2364 false,
2365 "Aux PHY on link " << +linkId << " already in sleep mode");
2366 // when CTS is sent, the main PHY may have already started switching, thus we may not
2367 // know which link the main PHY is moving from
2368 CheckMainPhyTraceInfo(0, "UlTxopRtsSentByAuxPhy", std::nullopt, linkId, false);
2369 });
2370 Simulator::Schedule(txDuration + (2 * MAX_PROPAGATION_DELAY) + TimeStep(1), [=, this]() {
2371 // aux PHYs are put to sleep if and only if CTS is not corrupted
2372 // (causing the end of the TXOP)
2373 CheckAuxPhysSleepMode(m_staMacs[0], !doCorruptCts);
2374 // if CTS is corrupted, TXOP ends and the main PHY switches back to the preferred
2375 // link, unless channel access is obtained on another link before the main PHY
2376 // completes the switch to the link on which CTS timeout occurred
2377 if (auto ehtFem = StaticCast<EhtFrameExchangeManager>(
2378 m_staMacs[0]->GetFrameExchangeManager(linkId));
2379 doCorruptCts && !ehtFem->UsingOtherEmlsrLink())
2380 {
2381 // check the traced elapsed time since CTS timeout before calling
2382 // CheckMainPhyTraceInfo
2383 if (const auto traceInfoIt = m_traceInfo.find(0);
2384 traceInfoIt != m_traceInfo.cend() &&
2385 traceInfoIt->second->GetName() == "CtsAfterRtsTimeout")
2386 {
2387 const auto& traceInfo =
2388 static_cast<const EmlsrCtsAfterRtsTimeoutTrace&>(*traceInfoIt->second);
2389 NS_TEST_EXPECT_MSG_GT(traceInfo.sinceCtsTimeout,
2390 Time{0},
2391 "Expected non-zero remaining time because main PHY "
2392 "was switching when CTS timeout occurred");
2393 }
2394
2395 CheckMainPhyTraceInfo(0, "CtsAfterRtsTimeout", linkId, m_mainPhyId);
2396 }
2397 });
2398 }
2399
2400 if (doCorruptCts)
2401 {
2402 // corrupt reception at EMLSR client
2403 NS_LOG_INFO("CORRUPTED");
2404 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2405 m_corruptCts = false;
2406 }
2407}
2408
2409void
2419
2420void
2422{
2423 if (m_msdMaxNTxops > 0)
2424 {
2428 "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
2429 }
2430
2431 auto psduIt = m_txPsdus.cbegin();
2432
2433 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame or RTS transmitted
2434 // to/by an EMLSR client
2435 auto jumpToQosDataOrMuRts = [&]() {
2436 while (psduIt != m_txPsdus.cend() &&
2437 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
2438 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
2439 {
2440 auto psdu = psduIt->psduMap.cbegin()->second;
2441 if (psdu->GetHeader(0).IsTrigger())
2442 {
2443 CtrlTriggerHeader trigger;
2444 psdu->GetPayload(0)->PeekHeader(trigger);
2445 if (trigger.IsMuRts())
2446 {
2447 break;
2448 }
2449 }
2450 psduIt++;
2451 }
2452 };
2453
2454 /**
2455 * EMLSR client with EMLSR mode enabled on all links (main PHY ID = 1).
2456 *
2457 * main PHY│
2458 * blocked,│
2459 * aux PHYs││main PHY blocked│
2460 * cannot │
2461 * transmit│
2462 * │ ┌───┐ ┌──┐
2463 * [link 0] │CTS│ │BA│
2464 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
2465 * │RTS│ │QoS│QoS│
2466 * └───┘ │ 6 │ 7 │
2467 * └───┴───┘
2468 * gen backoff gen backoff if MediumSyncDelay
2469 * ┌──┐ (also many times) not running timer expired ┌──┐
2470 * [link 1] │BA│ │ if allowed │ │ │BA│
2471 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
2472 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
2473 * │ 4 │ 5 │ └───┘ └───┘ │ 8 │ 9 │
2474 * └───┴───┘ └───┴───┘
2475 *
2476 * [link 2]
2477 * ───────────────────────────────────────────────────────────────────────────
2478 *
2479 *
2480 *
2481 * EMLSR client with EMLSR mode enabled on links 0 and 1 (main PHY ID = 1).
2482 *
2483 * main PHY │
2484 * and │
2485 * non-EMLSR│
2486 * link │
2487 * blocked,│
2488 * aux PHYs││main PHY blocked│
2489 * cannot │
2490 * transmit│
2491 * │ ┌───┐ ┌──┐
2492 * [link 0] │CTS│ │BA│
2493 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
2494 * │RTS│ │QoS│QoS│
2495 * └───┘ │ 8 │ 9 │
2496 * └───┴───┘
2497 * gen backoff gen backoff if MediumSyncDelay
2498 * ┌──┐ (also many times) not running timer expired ┌──┐
2499 * [link 1] │BA│ │ if allowed │ │ │BA│
2500 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
2501 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
2502 * │ 4 │ 5 │ └───┘ └───┘ │ 10│ 11│
2503 * └───┴───┘ └───┴───┘
2504 * ┌──┐
2505 * [link 2] │BA│
2506 * ──────────┬───┬───┬┴──┴────────────────────────────────────────────────────────────
2507 * │QoS│QoS│
2508 * │ 6 │ 7 │
2509 * └───┴───┘
2510 *
2511 * For both scenarios, after the last frame exchange on the main PHY link, we have the
2512 * following frame exchanges on an EMLSR link where an aux PHY is operating on.
2513 *
2514 *
2515 * [ link ] ┌───┐ ┌───┐ ┌──┐
2516 * [0 or 2] │CTS│ │CTS│ │BA│
2517 * ──────┬───┬┴───X────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
2518 * │RTS│ │RTS│ │QoS│QoS│
2519 * └───┘ └───┘ │ X │ Y │
2520 * └───┴───┘
2521 * For all EMLSR links scenario, X=10, Y=11
2522 * For the scenario with a non-EMLSR link, X=12, Y=13
2523 */
2524
2525 // jump to the first (non-Beacon) frame transmitted after establishing BA agreements and
2526 // enabling EMLSR mode
2527 while (psduIt != m_txPsdus.cend() &&
2528 (psduIt->startTx < m_firstUlPktsGenTime ||
2529 psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
2530 {
2531 ++psduIt;
2532 }
2533
2534 // the first QoS data frame is transmitted by the main PHY without RTS protection as soon
2535 // as transmissions on the link where the main PHY is operating are unblocked (at this
2536 // moment, aux PHYs cannot transmit)
2537 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2538 true,
2539 "First QoS data frame has not been transmitted");
2540 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2541 true,
2542 "First QoS data frame should be transmitted without protection");
2543 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2544 +m_mainPhyId,
2545 "First QoS data frame should be transmitted by the main PHY");
2546 NS_TEST_EXPECT_MSG_GT_OR_EQ(psduIt->startTx,
2548 "First QoS data frame sent too early");
2549
2550 auto prevPsduIt = psduIt++;
2551 jumpToQosDataOrMuRts();
2552
2553 if (m_nonEmlsrLink)
2554 {
2555 // an additional data frame is sent concurrently on the non-EMLSR link
2557 (psduIt != m_txPsdus.cend()),
2558 true,
2559 "Expected another QoS data frame sent concurrently with the first frame");
2561 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2562 true,
2563 "First data frame on non-EMLSR link should be transmitted without protection");
2564 NS_TEST_EXPECT_MSG_EQ(+psduIt->linkId,
2565 +m_nonEmlsrLink.value(),
2566 "First data frame expected to be transmitted on the non-EMLSR link");
2567 const auto txDuration =
2568 WifiPhy::CalculateTxDuration(prevPsduIt->psduMap,
2569 prevPsduIt->txVector,
2570 m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
2571 NS_TEST_EXPECT_MSG_LT(psduIt->startTx,
2572 prevPsduIt->startTx + txDuration,
2573 "First data frame on the non-EMLSR link not sent concurrently");
2574 psduIt++;
2575 jumpToQosDataOrMuRts();
2576 }
2577
2578 // the second QoS data frame is transmitted by the main PHY after that the aux PHY has
2579 // obtained a TXOP and sent an RTS
2580 // RTS
2581 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2582 true,
2583 "RTS before second QoS data frame has not been transmitted");
2584 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
2585 true,
2586 "Second QoS data frame should be transmitted with protection");
2588 +psduIt->phyId,
2589 +m_mainPhyId,
2590 "RTS before second QoS data frame should not be transmitted by the main PHY");
2591 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2593 "RTS before second data frame transmitted on an unexpected width");
2594 psduIt++;
2595 // CTS
2596 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2597 true,
2598 "CTS before second QoS data frame has not been transmitted");
2599 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
2600 true,
2601 "CTS before second QoS data frame has not been transmitted");
2602 psduIt++;
2603 // QoS Data
2604 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2605 true,
2606 "Second QoS data frame has not been transmitted");
2607 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2608 true,
2609 "Second QoS data frame has not been transmitted");
2610 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2611 +m_mainPhyId,
2612 "Second QoS data frame should be transmitted by the main PHY");
2613 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2615 "Second data frame not transmitted on the same width as RTS");
2616
2617 bool moreQosDataFound = false;
2618
2619 while (++psduIt != m_txPsdus.cend())
2620 {
2621 jumpToQosDataOrMuRts();
2622 if (psduIt != m_txPsdus.cend() &&
2623 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
2624 {
2625 moreQosDataFound = true;
2626
2627 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2628 +m_mainPhyId,
2629 "Third QoS data frame should be transmitted by the main PHY");
2630 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2631 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
2632 "Expecting TX width of third data frame to equal the channel "
2633 "width used by the main PHY");
2635 psduIt->startTx,
2637 "Third QoS data frame sent before MediumSyncDelay timer expired");
2638
2639 break;
2640 }
2641 }
2642
2643 NS_TEST_EXPECT_MSG_EQ(moreQosDataFound,
2644 true,
2645 "Third QoS data frame transmitted by the main PHY not found");
2646
2647 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
2648 ++psduIt;
2649 jumpToQosDataOrMuRts();
2650
2651 // the first attempt at transmitting the fourth QoS data frame fails because CTS is corrupted
2652 // RTS
2653 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2654 true,
2655 "RTS before fourth QoS data frame has not been transmitted");
2656 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
2657 true,
2658 "Fourth QoS data frame should be transmitted with protection");
2660 +psduIt->phyId,
2661 +m_mainPhyId,
2662 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
2663 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2665 "RTS before fourth data frame transmitted on an unexpected width");
2666 psduIt++;
2667 // CTS
2668 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2669 true,
2670 "CTS before fourth QoS data frame has not been transmitted");
2671 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
2672 true,
2673 "CTS before fourth QoS data frame has not been transmitted");
2674 psduIt++;
2675 jumpToQosDataOrMuRts();
2676
2677 // the fourth QoS data frame is transmitted by an aux PHY after that the aux PHY has
2678 // obtained a TXOP and sent an RTS
2679 // RTS
2680 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2681 true,
2682 "RTS before fourth QoS data frame has not been transmitted");
2683 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
2684 true,
2685 "Fourth QoS data frame should be transmitted with protection");
2687 +psduIt->phyId,
2688 +m_mainPhyId,
2689 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
2690 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2692 "RTS before fourth data frame transmitted on an unexpected width");
2693 psduIt++;
2694 // CTS
2695 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2696 true,
2697 "CTS before fourth QoS data frame has not been transmitted");
2698 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
2699 true,
2700 "CTS before fourth QoS data frame has not been transmitted");
2701 psduIt++;
2702 // QoS Data
2703 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2704 true,
2705 "Fourth QoS data frame has not been transmitted");
2706 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2707 true,
2708 "Fourth QoS data frame has not been transmitted");
2709 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2710 +m_mainPhyId,
2711 "Fourth QoS data frame should be transmitted by the main PHY");
2712 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2714 "Fourth data frame not transmitted on the same width as RTS");
2715}
2716
2718 : EmlsrOperationsTestBase("Check UL OFDMA operations with an EMLSR client"),
2719 m_enableBsrp(enableBsrp),
2720 m_txPsdusPos(0),
2721 m_startAccessReq(0)
2722{
2723 m_linksToEnableEmlsrOn = {0, 1, 2};
2724 m_nEmlsrStations = 1;
2726 m_establishBaDl = {};
2727 m_establishBaUl = {0};
2728 m_mainPhyId = 1;
2729 m_duration = Seconds(1.0);
2730}
2731
2732void
2734{
2735 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(m_transitionDelay.at(0)));
2736
2738
2739 m_apMac->GetQosTxop(AC_BE)->SetTxopLimits(
2740 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
2741
2742 auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma",
2743 BooleanValue(true),
2744 "EnableBsrp",
2746 m_apMac->AggregateObject(muScheduler);
2747}
2748
2749void
2751 uint8_t phyId,
2752 WifiConstPsduMap psduMap,
2753 WifiTxVector txVector,
2754 double txPowerW)
2755{
2756 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2757 auto linkId = m_txPsdus.back().linkId;
2758
2759 auto psdu = psduMap.begin()->second;
2760
2761 switch (psdu->GetHeader(0).GetType())
2762 {
2765 {
2766 // this is the first Trigger Frame sent after the AP requested channel access
2767 // through the Multi-user scheduler and it is an ICF for the EMLSR client
2768 m_txPsdusPos = m_txPsdus.size() - 1;
2769 auto txDuration = WifiPhy::CalculateTxDuration(psduMap,
2770 txVector,
2771 mac->GetWifiPhy(linkId)->GetPhyBand());
2772 NS_LOG_INFO("This is the first Trigger Frame\n");
2773 // once the Trigger Frame is received by the EMLSR client, make the client application
2774 // on the EMLSR client generate two packets. These packets will be sent via UL OFDMA
2775 // because the EMLSR client has blocked transmissions on other links when receiving
2776 // this Trigger Frame, hence it will not try to get access on other links via EDCA
2778 txDuration + MicroSeconds(1), // to account for propagation delay
2779 [=, this]() {
2780 for (const auto id : m_staMacs[0]->GetLinkIds())
2781 {
2783 m_staMacs[0]->GetFrameExchangeManager(id));
2785 ehtFem->UsingOtherEmlsrLink(),
2786 (id != linkId),
2787 "Link " << +id << " was" << (id == linkId ? " not" : "")
2788 << " expected to be blocked on EMLSR client at time "
2789 << Simulator::Now().As(Time::NS));
2790 }
2791 NS_LOG_INFO("Generate two packets\n");
2792 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2793 GetApplication(UPLINK, 0, 2, 100));
2794 });
2795 }
2796 break;
2797
2800 {
2801 CtrlBAckResponseHeader blockAck;
2802 psdu->GetPayload(0)->PeekHeader(blockAck);
2803 if (blockAck.IsMultiSta())
2804 {
2805 auto txDuration =
2807 txVector,
2808 mac->GetWifiPhy(linkId)->GetPhyBand());
2809 Simulator::Stop(txDuration + MicroSeconds(1));
2810 }
2811 }
2812 break;
2813
2814 default:;
2815 }
2816
2817 if (psdu->GetHeader(0).IsCfEnd())
2818 {
2819 // we do not check CF-End frames
2820 m_txPsdus.pop_back();
2821 }
2822}
2823
2824void
2834
2835void
2837{
2838 auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
2839 NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "No MU scheduler installed on AP MLD");
2840
2841 NS_LOG_INFO("Setting Access Request interval");
2842
2843 const auto interval = MilliSeconds(50);
2844 muScheduler->SetAccessReqInterval(interval);
2845 m_startAccessReq = Simulator::Now() + interval;
2846}
2847
2848void
2850{
2851 /**
2852 * Sending BSRP TF disabled.
2853 *
2854 * The figure assumes that link 0 is used to send the first Trigger Frame after that the
2855 * AP MLD requests channel access through the Multi-user scheduler. The first Trigger Frame
2856 * is MU-RTS because EMLSR client needs an ICF; the other Trigger Frames are Basic TFs and
2857 * do not solicit the EMLSR client.
2858 * ┌─────┐ ┌─────┐ ┌──────┐
2859 * │ MU │ │Basic│ │Multi-│
2860 * [link 0] │ RTS │ │ TF │ │STA BA│
2861 * ───────────┴─────┴┬───┬┴─────┴┬────────┬─┴──────┴───────────────
2862 * │CTS│ │QoS Null│
2863 * ├───┤ ├────────┤
2864 * │CTS│ │QoS Data│
2865 * └───┘ └────────┘
2866 *
2867 * ┌─────┐
2868 * │Basic│
2869 * [link 1] │ TF │
2870 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2871 * │QoS │
2872 * │Null│
2873 * └────┘
2874 *
2875 * ┌─────┐
2876 * │Basic│
2877 * [link 2] │ TF │
2878 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2879 * │QoS │
2880 * │Null│
2881 * └────┘
2882 *
2883 * Sending BSRP TF enabled.
2884 *
2885 * The figure assumes that link 0 is used to send the first Trigger Frame after that the
2886 * AP MLD requests channel access through the Multi-user scheduler. The first Trigger Frames
2887 * are all BSRP Trigger Frames, but only the first one solicits the EMLSR client, too.
2888 * ┌─────┐ ┌─────┐ ┌──────┐
2889 * │BSRP │ │Basic│ │Multi-│
2890 * [link 0] │ TF │ │ TF │ │STA BA│
2891 * ───────────┴─────┴┬────────┬┴─────┴┬────────┬─┴──────┴──────────
2892 * │QoS Null│ │QoS Data│
2893 * ├────────┤ └────────┘
2894 * │QoS Null│
2895 * └────────┘
2896 *
2897 * ┌─────┐
2898 * │BSRP │
2899 * [link 1] │ TF │
2900 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2901 * │QoS │
2902 * │Null│
2903 * └────┘
2904 *
2905 * ┌─────┐
2906 * │BSRP │
2907 * [link 2] │ TF │
2908 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2909 * │QoS │
2910 * │Null│
2911 * └────┘
2912 */
2913
2914 NS_TEST_ASSERT_MSG_GT(m_txPsdusPos, 0, "First Trigger Frame not detected");
2915
2916 // Check the Trigger Frames (one per link) after requesting channel access
2917 auto index = m_txPsdusPos;
2918 const auto firstLinkId = m_txPsdus[m_txPsdusPos].linkId;
2919 for (; index < m_txPsdusPos + 3; ++index)
2920 {
2921 NS_TEST_ASSERT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsTrigger(),
2922 true,
2923 "Expected a Trigger Frame");
2924 CtrlTriggerHeader trigger;
2925 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
2926
2927 TriggerFrameType triggerType =
2928 m_enableBsrp ? TriggerFrameType::BSRP_TRIGGER
2929 : (index == m_txPsdusPos ? TriggerFrameType::MU_RTS_TRIGGER
2930 : TriggerFrameType::BASIC_TRIGGER);
2931 NS_TEST_EXPECT_MSG_EQ(+static_cast<uint8_t>(trigger.GetType()),
2932 +static_cast<uint8_t>(triggerType),
2933 "Unexpected Trigger Frame type on link " << +m_txPsdus[index].linkId);
2934
2935 // only the first TF solicits the EMLSR client and the non-AP MLD
2937 trigger.GetNUserInfoFields(),
2938 (index == m_txPsdusPos ? 2 : 1),
2939 "Unexpected number of User Info fields for Trigger Frame, index=" << index);
2940 }
2941
2942 auto startIndex = index;
2943 std::size_t ctsCount = 0;
2944 std::size_t qosNullCount = 0;
2945 // Check responses to Trigger Frames
2946 for (; index < startIndex + 4; ++index)
2947 {
2948 const auto& hdr = m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
2949
2950 if (hdr.IsCts())
2951 {
2952 ++ctsCount;
2953 continue;
2954 }
2955
2956 if (hdr.IsQosData() && !hdr.HasData())
2957 {
2958 ++qosNullCount;
2959 // if BSRP is enabled, the QoS Null frame sent by the EMLSR client in response to the
2960 // first BSRP TF reports a non-null buffer status
2961 if (m_enableBsrp &&
2962 hdr.GetAddr2() == m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress())
2963 {
2964 NS_TEST_EXPECT_MSG_GT(+hdr.GetQosQueueSize(), 0, "Unexpected buffer size");
2965 }
2966 else
2967 {
2968 NS_TEST_EXPECT_MSG_EQ(+hdr.GetQosQueueSize(), 0, "Unexpected buffer size");
2969 }
2970 continue;
2971 }
2972 }
2973 NS_TEST_EXPECT_MSG_EQ(ctsCount, (m_enableBsrp ? 0 : 2), "Unexpected number of CTS frames");
2974 NS_TEST_EXPECT_MSG_EQ(qosNullCount,
2975 (m_enableBsrp ? 4 : 2),
2976 "Unexpected number of QoS Null frames");
2977
2978 // we expect only one Basic Trigger Frame (sent on the same link as the first Trigger Frame),
2979 // because the buffer status reported on the other links by the non-EMLSR client is zero
2980 NS_TEST_ASSERT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsTrigger(),
2981 true,
2982 "Expected a Trigger Frame");
2983 NS_TEST_EXPECT_MSG_EQ(+m_txPsdus[index].linkId,
2984 +firstLinkId,
2985 "Unexpected link ID for Basic TF");
2986 CtrlTriggerHeader trigger;
2987 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
2988
2989 NS_TEST_EXPECT_MSG_EQ(+static_cast<uint8_t>(trigger.GetType()),
2990 +static_cast<uint8_t>(TriggerFrameType::BASIC_TRIGGER),
2991 "Unexpected Trigger Frame type");
2992
2993 // when BSRP TF is enabled, the non-EMLSR client has already communicated a buffer status of
2994 // zero, so it is not solicited by the AP through the Basic Trigger Frame. Otherwise, it is
2995 // solicited because buffer status was not known when the BSRP TF was prepared (before sending
2996 // MU-RTS)
2998 (m_enableBsrp ? 1 : 2),
2999 "Unexpected number of User Info fields for Basic Trigger Frame");
3000
3001 // Response(s) to the Basic Trigger Frame
3002 startIndex = ++index;
3003 for (; index < startIndex + (m_enableBsrp ? 1 : 2); ++index)
3004 {
3005 const auto& hdr = m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
3006
3007 NS_TEST_EXPECT_MSG_EQ(hdr.IsQosData(), true, "Expected a QoS frame");
3008
3009 // EMLSR client sends a QoS Data frame, non-EMLSR client sends a QoS Null frame
3011 hdr.HasData(),
3012 (hdr.GetAddr2() == m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress()),
3013 "Unexpected type of QoS data frame");
3014
3015 if (hdr.HasData())
3016 {
3017 NS_TEST_EXPECT_MSG_EQ(m_txPsdus[index].txVector.IsUlMu(),
3018 true,
3019 "QoS Data frame should be sent in a TB PPDU");
3020 }
3021 }
3022
3023 // Finally, the AP MLD sends a Multi-STA BlockAck
3024 NS_TEST_EXPECT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
3025 true,
3026 "Expected a BlockAck frame");
3027 CtrlBAckResponseHeader blockAck;
3028 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(blockAck);
3029 NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA BlockAck");
3030}
3031
3033 : TestSuite("wifi-emlsr-basic-exchanges", Type::UNIT)
3034{
3035 for (const auto& emlsrLinks :
3036 {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
3037 {
3039 0,
3040 emlsrLinks,
3041 {MicroSeconds(32)},
3042 {MicroSeconds(32)},
3043 MicroSeconds(512),
3044 true /* putAuxPhyToSleep */}),
3045 TestCase::Duration::QUICK);
3047 1,
3048 emlsrLinks,
3049 {MicroSeconds(64)},
3050 {MicroSeconds(64)},
3051 MicroSeconds(512),
3052 false /* putAuxPhyToSleep */}),
3053 TestCase::Duration::QUICK);
3055 2,
3056 emlsrLinks,
3057 {MicroSeconds(128), MicroSeconds(256)},
3058 {MicroSeconds(128), MicroSeconds(256)},
3059 MicroSeconds(512),
3060 true /* putAuxPhyToSleep */}),
3061 TestCase::Duration::QUICK);
3062 }
3063
3064 for (auto genBackoffIfTxopWithoutTx : {true, false})
3065 {
3066 AddTestCase(new EmlsrUlTxopTest({{0, 1, 2},
3067 MHz_u{40},
3068 MHz_u{20},
3069 MicroSeconds(5504),
3070 3,
3071 genBackoffIfTxopWithoutTx,
3072 true, /* putAuxPhyToSleep */
3073 false /* switchMainPhyBackDelayTimeout */}),
3074 TestCase::Duration::QUICK);
3075 AddTestCase(new EmlsrUlTxopTest({{0, 1},
3076 MHz_u{40},
3077 MHz_u{20},
3078 MicroSeconds(5504),
3079 1,
3080 genBackoffIfTxopWithoutTx,
3081 false, /* putAuxPhyToSleep */
3082 true /* switchMainPhyBackDelayTimeout */}),
3083 TestCase::Duration::QUICK);
3084 }
3085
3086 AddTestCase(new EmlsrUlOfdmaTest(false), TestCase::Duration::QUICK);
3087 AddTestCase(new EmlsrUlOfdmaTest(true), TestCase::Duration::QUICK);
3088}
3089
#define Max(a, b)
Test the transmission of DL frames to EMLSR clients.
void CheckInitialControlFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the AP MLD transmitting an initial Control frame to an EM...
const Time m_fe2to3delay
time interval between 2nd and 3rd frame exchange sequences after the enablement of EMLSR mode
void CheckResults()
Check that the simulation produced the expected results.
void CheckPmModeAfterAssociation(const Mac48Address &address)
Check that the AP MLD considers the correct Power Management mode for the links setup with the given ...
EmlsrDlTxopTest(const Params &params)
Constructor.
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt BlockAck at AP MLD
void CheckStaEmlNotificationFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an EMLSR client transmits an EML Operating Mode Notific...
std::size_t m_countQoSframes
counter for QoS frames (transition delay test)
void CheckApEmlNotificationFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when the AP MLD transmits an EML Operating Mode Notification...
Time m_emlsrEnabledTime
when EMLSR mode has been enabled on all EMLSR clients
std::set< uint8_t > m_emlsrLinks
IDs of the links on which EMLSR mode has to be enabled.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void CheckQosFrames(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing QoS data frames...
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void EnableEmlsrMode()
Enable EMLSR mode on the next EMLSR client.
void CheckBlockAck(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t phyId)
Check that appropriate actions are taken by the AP MLD receiving a PPDU containing BlockAck frames fr...
void DoRun() override
Implementation to actually run this TestCase.
std::size_t m_countBlockAck
counter for BlockAck frames (transition delay test)
Base class for EMLSR Operations tests.
std::size_t m_nNonEmlsrStations
number of stations to create that do not activate EMLSR
std::vector< uint8_t > m_establishBaDl
the TIDs for which BA needs to be established with the AP as originator
void CheckBlockedLink(Ptr< WifiMac > mac, Mac48Address dest, uint8_t linkId, WifiQueueBlockedReason reason, bool blocked, std::string description, bool testUnblockedForOtherReasons=true)
Check whether QoS data unicast transmissions addressed to the given destination on the given link are...
std::size_t m_nEmlsrStations
number of stations to create that activate EMLSR
std::vector< Time > m_paddingDelay
Padding Delay advertised by the non-AP MLD.
std::set< uint8_t > m_linksToEnableEmlsrOn
IDs of the links on which EMLSR mode has to be enabled.
Ptr< ApWifiMac > m_apMac
AP wifi MAC.
bool m_putAuxPhyToSleep
whether aux PHYs are put to sleep during DL/UL TXOPs
void DoSetup() override
Implementation to do any local setup required for this TestCase.
uint8_t m_mainPhyId
ID of the main PHY.
Time m_duration
simulation duration
Ptr< PacketSocketClient > GetApplication(TrafficDirection dir, std::size_t staId, std::size_t count, std::size_t pktSize, uint8_t priority=0) const
std::vector< FrameInfo > m_txPsdus
transmitted PSDUs
virtual void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when a FEM passes PSDUs to the PHY.
uint16_t m_lastAid
AID of last associated station.
std::vector< Time > m_transitionDelay
Transition Delay advertised by the non-AP MLD.
void CheckMainPhyTraceInfo(std::size_t index, std::string_view reason, const std::optional< uint8_t > &fromLinkId, uint8_t toLinkId, bool checkFromLinkId=true, bool checkToLinkId=true)
Check information provided by the EMLSR Manager MainPhySwitch trace.
std::map< std::size_t, std::shared_ptr< EmlsrMainPhySwitchTrace > > m_traceInfo
EMLSR client ID-indexed map of trace info from last main PHY switch.
Time m_transitionTimeout
Transition Timeout advertised by the AP MLD.
void CheckAuxPhysSleepMode(Ptr< StaWifiMac > staMac, bool sleep)
Check whether aux PHYs of the given device are in sleep mode/awake.
std::vector< Ptr< StaWifiMac > > m_staMacs
MACs of the non-AP MLDs.
std::vector< uint8_t > m_establishBaUl
the TIDs for which BA needs to be established with the AP as recipient
Check UL OFDMA operations with EMLSR clients.
Time m_startAccessReq
start time of the first AP MLD access request via MU scheduler
void DoRun() override
Implementation to actually run this TestCase.
void CheckResults()
Check that the simulation produced the expected results.
EmlsrUlOfdmaTest(bool enableBsrp)
Constructor.
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
bool m_enableBsrp
whether MU scheduler sends BSRP TFs
std::size_t m_txPsdusPos
position in the vector of TX PSDUs of the first ICF
Test the transmission of UL frames from EMLSR clients.
std::size_t m_countQoSframes
counter for QoS frames
const Time m_unblockMainPhyLinkDelay
delay between the time the first two UL packets are generated and the time transmissions are unblocke...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets
MHz_u m_auxPhyChannelWidth
max width supported by aux PHYs
void BackoffGenerated(uint32_t backoff, uint8_t linkId)
Callback invoked when a new backoff value is generated by the EMLSR client.
std::optional< uint8_t > m_nonEmlsrLink
ID of the non-EMLSR link (if any)
Time m_lastMsdExpiryTime
expiry time of the last MediumSyncDelay timer
void DoSetup() override
Implementation to do any local setup required for this TestCase.
std::size_t m_countRtsframes
counter for RTS frames
void CheckCtsFrames(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the EMLSR client when receiving a CTS frame on the given ...
void DoRun() override
Implementation to actually run this TestCase.
Time m_firstUlPktsGenTime
generation time of the first two UL packets
std::optional< bool > m_corruptCts
whether the transmitted CTS must be corrupted
void CheckBlockAck(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an MLD transmits a PPDU containing BlockAck frames on t...
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
std::optional< Time > m_backoffEndTime
expected backoff end time on main PHY link
MHz_u m_channelWidth
width of the channels used by MLDs
std::set< uint8_t > m_emlsrLinks
IDs of the links on which EMLSR mode has to be enabled.
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
void CheckRtsFrames(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the EMLSR client when transmitting an RTS frame on the gi...
void CheckQosFrames(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an MLD transmits a PPDU containing QoS data frames on t...
bool m_genBackoffIfTxopWithoutTx
whether the backoff should be invoked when the AC gains the right to start a TXOP but it does not tra...
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_checkBackoffStarted
whether we are checking the generated backoff values
std::size_t m_countBlockAck
counter for BlockAck frames
void CheckResults()
Check that the simulation produced the expected results.
uint8_t m_msdMaxNTxops
Max number of TXOPs that an EMLSR client is allowed to attempt to initiate while the MediumSyncDelay ...
EmlsrUlTxopTest(const Params &params)
Constructor.
wifi EMLSR suite to test basic frame exchanges.
A container for one type of attribute.
AttributeValue implementation for Boolean.
Definition boolean.h:26
Headers for BlockAck response.
Headers for Trigger frames.
bool IsMuRts() const
Check if this is a MU-RTS Trigger frame.
void SetPaddingSize(std::size_t size)
Set the size in bytes of the Padding field.
TriggerFrameType GetType() const
Get the Trigger Frame type.
std::size_t GetNUserInfoFields() const
Get the number of User Info fields in this Trigger Frame.
Class for representing data rates.
Definition data-rate.h:78
Time CalculateBytesTxTime(uint32_t bytes) const
Calculate transmission time.
Definition data-rate.cc:220
Hold variables of type enum.
Definition enum.h:52
void SetList(const std::list< uint64_t > &packetlist)
an EUI-48 address
static Mac48Address GetBroadcast()
Implement the header for Action frames of type EML Operating Mode Notification.
EmlControl m_emlControl
EML Control field.
MultiUserScheduler is an abstract base class defining the API that APs supporting at least VHT can us...
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 void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:131
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static void Run()
Run the simulation.
Definition simulator.cc:167
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:595
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition simulator.cc:175
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
A suite of tests to run.
Definition test.h:1267
Type
Type of test.
Definition test.h:1274
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
@ NS
nanosecond
Definition nstime.h:108
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
AttributeValue implementation for Time.
Definition nstime.h:1432
Hold an unsigned integer type.
Definition uinteger.h:34
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
static std::pair< CategoryValue, ActionValue > Remove(Ptr< Packet > pkt)
Remove an Action header from the given packet.
Implements the IEEE 802.11 MAC header.
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
Definition wifi-mode.cc:110
std::tuple< uint8_t, MHz_u, WifiPhyBand, uint8_t > ChannelTuple
Tuple identifying a segment of an operating channel.
Definition wifi-phy.h:937
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1563
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1556
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.
WifiPreamble GetPreambleType() const
MHz_u GetChannelWidth() const
#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
void SetDefault(std::string name, const AttributeValue &value)
Definition config.cc:886
#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_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
Ptr< T > CreateObjectWithAttributes(Args... args)
Allocate an Object on the heap and initialize with a set of attributes.
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
#define NS_TEST_EXPECT_MSG_GT_OR_EQ(actual, limit, msg)
Test that an actual value is greater than or equal to limit and report if not.
Definition test.h:986
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition test.h:134
#define NS_TEST_EXPECT_MSG_LT_OR_EQ(actual, limit, msg)
Test that an actual value is less than or equal to a limit and report if not.
Definition test.h:820
#define NS_TEST_EXPECT_MSG_LT(actual, limit, msg)
Test that an actual value is less than a limit and report if not.
Definition test.h:780
#define NS_TEST_EXPECT_MSG_GT(actual, limit, msg)
Test that an actual value is greater than a limit and report if not.
Definition test.h:946
#define NS_TEST_EXPECT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report if not.
Definition test.h:656
#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report if not.
Definition test.h:241
#define NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report and abort if not.
Definition test.h:554
#define NS_TEST_ASSERT_MSG_GT(actual, limit, msg)
Test that an actual value is greater than a limit and report and abort if not.
Definition test.h:864
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1381
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1345
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1357
TriggerFrameType
The different Trigger frame types.
@ WIFI_PREAMBLE_HT_MF
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
@ WIFI_PHY_BAND_5GHZ
The 5 GHz band.
@ AC_BE
Best Effort.
Definition qos-utils.h:64
Every class exported by the ns3 library is enclosed in the ns3 namespace.
const Time MAX_PROPAGATION_DELAY
maximum propagation delay
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:684
bool IsTrigger(const WifiPsduMap &psduMap)
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:138
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_CTL_RTS
@ WIFI_MAC_CTL_CTS
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_CTL_END
@ WIFI_MAC_QOSDATA
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition wifi-utils.cc:51
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
STL namespace.
phy
Definition third.py:78
ns3::Time timeout
Parameters for the EMLSR DL TXOP test.
Parameters for the EMLSR UL TXOP test.
Struct to trace that main PHY started switching after a CTS timeout occurred on the link on which an ...
static WifiEmlsrBasicExchangesTestSuite g_wifiEmlsrBasicExchangesTestSuite
the test suite