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
2067 [=, this]() {
2069 m_apMac,
2070 m_staMacs[0]->GetAddress(),
2071 id,
2072 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2073 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2074 m_staMacs[0]->IsEmlsrLink(id),
2075 "Checking EMLSR links on AP MLD after sending the first data frame");
2076 });
2077 }
2078 });
2079
2080 if (m_nonEmlsrLink)
2081 {
2082 break;
2083 }
2084 m_countQoSframes++; // if all EMLSR links, next case is already executed now
2085 [[fallthrough]];
2086 case 4:
2087 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2088 // after this QoS data frame is received
2089 Simulator::ScheduleNow([=, this]() {
2090 // make aux PHYs capable of transmitting frames
2091 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2092 auxPhyLinks.erase(m_mainPhyId);
2093 if (m_nonEmlsrLink)
2094 {
2095 auxPhyLinks.erase(*m_nonEmlsrLink);
2096 }
2097 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2098 m_apMac->GetAddress(),
2099 auxPhyLinks);
2100
2101 // block transmissions on the link where the main PHY is operating
2102 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2103 m_apMac->GetAddress(),
2104 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2105
2106 // generate data packets for another UL data frame, which will be sent on a link on
2107 // which an aux PHY is operating
2108 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2109 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2110 GetApplication(UPLINK, 0, 2, 1000));
2111 });
2112 break;
2113 case 5:
2114 // check that other EMLSR links are now blocked on both the EMLSR client and the AP MLD
2115 Simulator::ScheduleNow([=, this]() {
2116 for (auto id : m_staMacs[0]->GetLinkIds())
2117 {
2119 m_staMacs[0],
2120 m_apMac->GetAddress(),
2121 id,
2122 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2123 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2124 "Checking EMLSR links on EMLSR client while sending the second data frame",
2125 false);
2126
2128 m_apMac,
2129 m_staMacs[0]->GetAddress(),
2130 id,
2131 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2132 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2133 "Checking EMLSR links on AP MLD while sending the second data frame",
2134 false);
2135 }
2136
2137 // unblock transmission on the link where the main PHY is operating
2138 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
2139 WifiQueueBlockedReason::TID_NOT_MAPPED,
2140 AC_BE,
2142 m_apMac->GetAddress(),
2143 m_staMacs[0]->GetAddress(),
2144 {0},
2145 {m_mainPhyId});
2146 });
2147 break;
2148 }
2149}
2150
2151void
2153 const WifiTxVector& txVector,
2154 uint8_t linkId)
2155{
2157
2158 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2159 auxPhyLinks.erase(m_mainPhyId);
2160 if (m_nonEmlsrLink)
2161 {
2162 auxPhyLinks.erase(*m_nonEmlsrLink);
2163 }
2164
2165 auto txDuration =
2166 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2167
2168 // in this test, BlockAck frames terminates TXOP, thus aux PHYs shall be in sleep mode before
2169 // the end of BlockAck reception and awake right afterwards
2170 if (linkId != m_nonEmlsrLink)
2171 {
2172 Simulator::Schedule(txDuration - TimeStep(1),
2174 this,
2175 m_staMacs[0],
2176 true);
2177 Simulator::Schedule(txDuration + TimeStep(1),
2179 this,
2180 m_staMacs[0],
2181 false);
2182
2183 // if the TXOP has been carried out on a link other than the preferred link, the main PHY
2184 // switches back to the preferred link when the TXOP ends
2185 if (m_staMacs[0]->GetLinkForPhy(m_mainPhyId) != linkId)
2186 {
2187 Simulator::Schedule(txDuration + TimeStep(1), [=, this]() {
2188 CheckMainPhyTraceInfo(0, "TxopEnded", linkId, m_mainPhyId);
2189 });
2190 }
2191 }
2192
2193 switch (m_countBlockAck)
2194 {
2195 case 1:
2196 case 2:
2197 // do nothing, these are BlockAcks in response to the QoS data frames sent to establish
2198 // BA agreements in DL and UL direction
2199 break;
2200 case 3:
2201 if (linkId == m_nonEmlsrLink)
2202 {
2203 // this BlockAck has been sent on the non-EMLSR link, ignore it
2204 break;
2205 }
2206 m_checkBackoffStarted = true;
2207 if (!m_nonEmlsrLink)
2208 {
2209 m_countBlockAck++; // if all EMLSR links, next case is already executed now
2210 }
2211 [[fallthrough]];
2212 case 4:
2213 if (m_nonEmlsrLink && m_countBlockAck == 4)
2214 {
2215 // block transmissions on the non-EMLSR link
2216 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2217 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2218 m_apMac->GetAddress(),
2219 {*m_nonEmlsrLink});
2220 });
2221 }
2222 if (linkId == m_nonEmlsrLink)
2223 {
2224 // this BlockAck has been sent on the non-EMLSR link, ignore it
2225 break;
2226 }
2227 m_checkBackoffStarted = true;
2228 break;
2229 case 5:
2230 // Block Ack in response to the second data frame sent by the EMLSR client on EMLSR links.
2231 // Check that MediumSyncDelay timer starts running on the link where the main PHY switches
2232 // to when the channel switch is completed
2234 txDuration + m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelSwitchDelay() +
2235 NanoSeconds(1),
2236 [=, this]() {
2237 auto elapsed =
2238 m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_mainPhyId);
2240 elapsed.has_value(),
2241 true,
2242 "MediumSyncDelay timer not running on link where main PHY is operating");
2244 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
2245 *elapsed;
2246 });
2247
2248 // Check that the number of backoff slots is not changed since the beginning of the TXOP
2249 Simulator::Schedule(txDuration, [=, this]() {
2250 m_checkBackoffStarted = false;
2252 true,
2253 "Backoff end time should have been calculated");
2254 // when this BlockAck is received, the TXOP ends and the main PHY link is unblocked,
2255 // which causes a new backoff timer to be generated if the backoff timer is not
2256 // already running
2258 });
2259
2260 // make aux PHYs not capable of transmitting frames
2261 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2262 m_apMac->GetAddress(),
2263 auxPhyLinks);
2264
2265 // generate data packets for another UL data frame, which will be sent on the link where
2266 // the main PHY is operating
2267 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2268 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2269 break;
2270 case 6: {
2271 // block transmission on the main PHY link and on the non-EMLSR link (if any), so that
2272 // the next QoS frames are sent on a link where an aux PHY is operating
2273 std::set<uint8_t> linkIds{m_mainPhyId};
2274 if (m_nonEmlsrLink)
2275 {
2276 linkIds.insert(*m_nonEmlsrLink);
2277 }
2278 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2279 AC_BE,
2281 m_apMac->GetAddress(),
2282 m_staMacs[0]->GetAddress(),
2283 {0},
2284 linkIds);
2285 }
2286 // make sure aux PHYs are capable of transmitting frames
2287 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2288 m_apMac->GetAddress(),
2289 auxPhyLinks);
2290
2291 // generate data packets for another UL data frame
2292 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2293 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2294 break;
2295 }
2296}
2297
2298void
2300 const WifiTxVector& txVector,
2301 uint8_t linkId)
2302{
2304 {
2305 // this function only considers RTS frames sent after the first QoS data frame
2306 return;
2307 }
2308
2309 if (linkId != m_mainPhyId)
2310 {
2311 if (m_countRtsframes > 0 && !m_corruptCts.has_value())
2312 {
2313 // we get here for the frame exchange in which the CTS response must be corrupted.
2314 // Install post reception error model on the STA affiliated with the EMLSR client that
2315 // is transmitting this RTS frame
2317 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
2318 m_corruptCts = true;
2319 }
2320
2321 return;
2322 }
2323
2324 // we get here for RTS frames sent by the main PHY while the MediumSyncDelay timer is running
2326
2328 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
2329 "RTS sent by main PHY on an unexpected width");
2330
2331 // corrupt reception at AP MLD
2332 NS_LOG_INFO("CORRUPTED");
2333 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2334}
2335
2336void
2338 const WifiTxVector& txVector,
2339 uint8_t linkId)
2340{
2342 {
2343 // this function only considers CTS frames sent after the first QoS data frame
2344 return;
2345 }
2346
2347 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
2348 txVector,
2349 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2350 const auto doCorruptCts = m_corruptCts.has_value() && *m_corruptCts;
2351
2352 if (linkId != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && linkId != m_nonEmlsrLink &&
2353 mpdu->GetHeader().GetAddr1() == m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress())
2354 {
2355 // this is a CTS sent to an aux PHY starting an UL TXOP. Given that aux PHYs do not
2356 // switch channel, they are put in sleep mode when the main PHY starts operating on their
2357 // link, which coincides with the end of CTS plus two propagation delays
2358 const auto auxPhy = m_staMacs[0]->GetWifiPhy(linkId);
2359 const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
2360 Simulator::Schedule(txDuration, [=, this]() {
2361 // when CTS ends, the main PHY is still switching and the aux PHY is not yet sleeping
2362 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
2363 true,
2364 "Expecting the main PHY to be switching link");
2365 NS_TEST_EXPECT_MSG_EQ(auxPhy->IsStateSleep(),
2366 false,
2367 "Aux PHY on link " << +linkId << " already in sleep mode");
2368 // when CTS is sent, the main PHY may have already started switching, thus we may not
2369 // know which link the main PHY is moving from
2370 CheckMainPhyTraceInfo(0, "UlTxopRtsSentByAuxPhy", std::nullopt, linkId, false);
2371 });
2373 txDuration + MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC) + TimeStep(1),
2374 [=, this]() {
2375 // aux PHYs are put to sleep if and only if CTS is not corrupted
2376 // (causing the end of the TXOP)
2377 CheckAuxPhysSleepMode(m_staMacs[0], !doCorruptCts);
2378 // if CTS is corrupted, TXOP ends and the main PHY switches back to the preferred
2379 // link, unless channel access is obtained on another link before the main PHY
2380 // completes the switch to the link on which CTS timeout occurred
2381 if (auto ehtFem = StaticCast<EhtFrameExchangeManager>(
2382 m_staMacs[0]->GetFrameExchangeManager(linkId));
2383 doCorruptCts && !ehtFem->UsingOtherEmlsrLink())
2384 {
2385 // check the traced elapsed time since CTS timeout before calling
2386 // CheckMainPhyTraceInfo
2387 if (const auto traceInfoIt = m_traceInfo.find(0);
2388 traceInfoIt != m_traceInfo.cend() &&
2389 traceInfoIt->second->GetName() == "CtsAfterRtsTimeout")
2390 {
2391 const auto& traceInfo =
2392 static_cast<const EmlsrCtsAfterRtsTimeoutTrace&>(*traceInfoIt->second);
2393 NS_TEST_EXPECT_MSG_GT(traceInfo.sinceCtsTimeout,
2394 Time{0},
2395 "Expected non-zero remaining time because main PHY "
2396 "was switching when CTS timeout occurred");
2397 }
2398
2399 CheckMainPhyTraceInfo(0, "CtsAfterRtsTimeout", linkId, m_mainPhyId);
2400 }
2401 });
2402 }
2403
2404 if (doCorruptCts)
2405 {
2406 // corrupt reception at EMLSR client
2407 NS_LOG_INFO("CORRUPTED");
2408 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2409 m_corruptCts = false;
2410 }
2411}
2412
2413void
2423
2424void
2426{
2427 if (m_msdMaxNTxops > 0)
2428 {
2432 "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
2433 }
2434
2435 auto psduIt = m_txPsdus.cbegin();
2436
2437 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame or RTS transmitted
2438 // to/by an EMLSR client
2439 auto jumpToQosDataOrMuRts = [&]() {
2440 while (psduIt != m_txPsdus.cend() &&
2441 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
2442 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
2443 {
2444 auto psdu = psduIt->psduMap.cbegin()->second;
2445 if (psdu->GetHeader(0).IsTrigger())
2446 {
2447 CtrlTriggerHeader trigger;
2448 psdu->GetPayload(0)->PeekHeader(trigger);
2449 if (trigger.IsMuRts())
2450 {
2451 break;
2452 }
2453 }
2454 psduIt++;
2455 }
2456 };
2457
2458 /**
2459 * EMLSR client with EMLSR mode enabled on all links (main PHY ID = 1).
2460 *
2461 * main PHY│
2462 * blocked,│
2463 * aux PHYs││main PHY blocked│
2464 * cannot │
2465 * transmit│
2466 * │ ┌───┐ ┌──┐
2467 * [link 0] │CTS│ │BA│
2468 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
2469 * │RTS│ │QoS│QoS│
2470 * └───┘ │ 6 │ 7 │
2471 * └───┴───┘
2472 * gen backoff gen backoff if MediumSyncDelay
2473 * ┌──┐ (also many times) not running timer expired ┌──┐
2474 * [link 1] │BA│ │ if allowed │ │ │BA│
2475 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
2476 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
2477 * │ 4 │ 5 │ └───┘ └───┘ │ 8 │ 9 │
2478 * └───┴───┘ └───┴───┘
2479 *
2480 * [link 2]
2481 * ───────────────────────────────────────────────────────────────────────────
2482 *
2483 *
2484 *
2485 * EMLSR client with EMLSR mode enabled on links 0 and 1 (main PHY ID = 1).
2486 *
2487 * main PHY │
2488 * and │
2489 * non-EMLSR│
2490 * link │
2491 * blocked,│
2492 * aux PHYs││main PHY blocked│
2493 * cannot │
2494 * transmit│
2495 * │ ┌───┐ ┌──┐
2496 * [link 0] │CTS│ │BA│
2497 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
2498 * │RTS│ │QoS│QoS│
2499 * └───┘ │ 8 │ 9 │
2500 * └───┴───┘
2501 * gen backoff gen backoff if MediumSyncDelay
2502 * ┌──┐ (also many times) not running timer expired ┌──┐
2503 * [link 1] │BA│ │ if allowed │ │ │BA│
2504 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
2505 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
2506 * │ 4 │ 5 │ └───┘ └───┘ │ 10│ 11│
2507 * └───┴───┘ └───┴───┘
2508 * ┌──┐
2509 * [link 2] │BA│
2510 * ──────────┬───┬───┬┴──┴────────────────────────────────────────────────────────────
2511 * │QoS│QoS│
2512 * │ 6 │ 7 │
2513 * └───┴───┘
2514 *
2515 * For both scenarios, after the last frame exchange on the main PHY link, we have the
2516 * following frame exchanges on an EMLSR link where an aux PHY is operating on.
2517 *
2518 *
2519 * [ link ] ┌───┐ ┌───┐ ┌──┐
2520 * [0 or 2] │CTS│ │CTS│ │BA│
2521 * ──────┬───┬┴───X────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
2522 * │RTS│ │RTS│ │QoS│QoS│
2523 * └───┘ └───┘ │ X │ Y │
2524 * └───┴───┘
2525 * For all EMLSR links scenario, X=10, Y=11
2526 * For the scenario with a non-EMLSR link, X=12, Y=13
2527 */
2528
2529 // jump to the first (non-Beacon) frame transmitted after establishing BA agreements and
2530 // enabling EMLSR mode
2531 while (psduIt != m_txPsdus.cend() &&
2532 (psduIt->startTx < m_firstUlPktsGenTime ||
2533 psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
2534 {
2535 ++psduIt;
2536 }
2537
2538 // the first QoS data frame is transmitted by the main PHY without RTS protection as soon
2539 // as transmissions on the link where the main PHY is operating are unblocked (at this
2540 // moment, aux PHYs cannot transmit)
2541 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2542 true,
2543 "First QoS data frame has not been transmitted");
2544 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2545 true,
2546 "First QoS data frame should be transmitted without protection");
2547 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2548 +m_mainPhyId,
2549 "First QoS data frame should be transmitted by the main PHY");
2550 NS_TEST_EXPECT_MSG_GT_OR_EQ(psduIt->startTx,
2552 "First QoS data frame sent too early");
2553
2554 auto prevPsduIt = psduIt++;
2555 jumpToQosDataOrMuRts();
2556
2557 if (m_nonEmlsrLink)
2558 {
2559 // an additional data frame is sent concurrently on the non-EMLSR link
2561 (psduIt != m_txPsdus.cend()),
2562 true,
2563 "Expected another QoS data frame sent concurrently with the first frame");
2565 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2566 true,
2567 "First data frame on non-EMLSR link should be transmitted without protection");
2568 NS_TEST_EXPECT_MSG_EQ(+psduIt->linkId,
2569 +m_nonEmlsrLink.value(),
2570 "First data frame expected to be transmitted on the non-EMLSR link");
2571 const auto txDuration =
2572 WifiPhy::CalculateTxDuration(prevPsduIt->psduMap,
2573 prevPsduIt->txVector,
2574 m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
2575 NS_TEST_EXPECT_MSG_LT(psduIt->startTx,
2576 prevPsduIt->startTx + txDuration,
2577 "First data frame on the non-EMLSR link not sent concurrently");
2578 psduIt++;
2579 jumpToQosDataOrMuRts();
2580 }
2581
2582 // the second QoS data frame is transmitted by the main PHY after that the aux PHY has
2583 // obtained a TXOP and sent an RTS
2584 // RTS
2585 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2586 true,
2587 "RTS before second QoS data frame has not been transmitted");
2588 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
2589 true,
2590 "Second QoS data frame should be transmitted with protection");
2592 +psduIt->phyId,
2593 +m_mainPhyId,
2594 "RTS before second QoS data frame should not be transmitted by the main PHY");
2595 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2597 "RTS before second data frame transmitted on an unexpected width");
2598 psduIt++;
2599 // CTS
2600 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2601 true,
2602 "CTS before second QoS data frame has not been transmitted");
2603 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
2604 true,
2605 "CTS before second QoS data frame has not been transmitted");
2606 psduIt++;
2607 // QoS Data
2608 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2609 true,
2610 "Second QoS data frame has not been transmitted");
2611 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2612 true,
2613 "Second QoS data frame has not been transmitted");
2614 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2615 +m_mainPhyId,
2616 "Second QoS data frame should be transmitted by the main PHY");
2617 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2619 "Second data frame not transmitted on the same width as RTS");
2620
2621 bool moreQosDataFound = false;
2622
2623 while (++psduIt != m_txPsdus.cend())
2624 {
2625 jumpToQosDataOrMuRts();
2626 if (psduIt != m_txPsdus.cend() &&
2627 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
2628 {
2629 moreQosDataFound = true;
2630
2631 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2632 +m_mainPhyId,
2633 "Third QoS data frame should be transmitted by the main PHY");
2634 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2635 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
2636 "Expecting TX width of third data frame to equal the channel "
2637 "width used by the main PHY");
2639 psduIt->startTx,
2641 "Third QoS data frame sent before MediumSyncDelay timer expired");
2642
2643 break;
2644 }
2645 }
2646
2647 NS_TEST_EXPECT_MSG_EQ(moreQosDataFound,
2648 true,
2649 "Third QoS data frame transmitted by the main PHY not found");
2650
2651 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
2652 ++psduIt;
2653 jumpToQosDataOrMuRts();
2654
2655 // the first attempt at transmitting the fourth QoS data frame fails because CTS is corrupted
2656 // RTS
2657 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2658 true,
2659 "RTS before fourth QoS data frame has not been transmitted");
2660 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
2661 true,
2662 "Fourth QoS data frame should be transmitted with protection");
2664 +psduIt->phyId,
2665 +m_mainPhyId,
2666 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
2667 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2669 "RTS before fourth data frame transmitted on an unexpected width");
2670 psduIt++;
2671 // CTS
2672 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2673 true,
2674 "CTS before fourth QoS data frame has not been transmitted");
2675 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
2676 true,
2677 "CTS before fourth QoS data frame has not been transmitted");
2678 psduIt++;
2679 jumpToQosDataOrMuRts();
2680
2681 // the fourth QoS data frame is transmitted by an aux PHY after that the aux PHY has
2682 // obtained a TXOP and sent an RTS
2683 // RTS
2684 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2685 true,
2686 "RTS before fourth QoS data frame has not been transmitted");
2687 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
2688 true,
2689 "Fourth QoS data frame should be transmitted with protection");
2691 +psduIt->phyId,
2692 +m_mainPhyId,
2693 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
2694 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2696 "RTS before fourth data frame transmitted on an unexpected width");
2697 psduIt++;
2698 // CTS
2699 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2700 true,
2701 "CTS before fourth QoS data frame has not been transmitted");
2702 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
2703 true,
2704 "CTS before fourth QoS data frame has not been transmitted");
2705 psduIt++;
2706 // QoS Data
2707 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2708 true,
2709 "Fourth QoS data frame has not been transmitted");
2710 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2711 true,
2712 "Fourth QoS data frame has not been transmitted");
2713 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2714 +m_mainPhyId,
2715 "Fourth QoS data frame should be transmitted by the main PHY");
2716 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2718 "Fourth data frame not transmitted on the same width as RTS");
2719}
2720
2722 : EmlsrOperationsTestBase("Check UL OFDMA operations with an EMLSR client"),
2723 m_enableBsrp(enableBsrp),
2724 m_txPsdusPos(0),
2725 m_startAccessReq(0)
2726{
2727 m_linksToEnableEmlsrOn = {0, 1, 2};
2728 m_nEmlsrStations = 1;
2730 m_establishBaDl = {};
2731 m_establishBaUl = {0};
2732 m_mainPhyId = 1;
2733 m_duration = Seconds(1.0);
2734}
2735
2736void
2738{
2739 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(m_transitionDelay.at(0)));
2740
2742
2743 m_apMac->GetQosTxop(AC_BE)->SetTxopLimits(
2744 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
2745
2746 auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma",
2747 BooleanValue(true),
2748 "EnableBsrp",
2750 m_apMac->AggregateObject(muScheduler);
2751}
2752
2753void
2755 uint8_t phyId,
2756 WifiConstPsduMap psduMap,
2757 WifiTxVector txVector,
2758 double txPowerW)
2759{
2760 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2761 auto linkId = m_txPsdus.back().linkId;
2762
2763 auto psdu = psduMap.begin()->second;
2764
2765 switch (psdu->GetHeader(0).GetType())
2766 {
2769 {
2770 // this is the first Trigger Frame sent after the AP requested channel access
2771 // through the Multi-user scheduler and it is an ICF for the EMLSR client
2772 m_txPsdusPos = m_txPsdus.size() - 1;
2773 auto txDuration = WifiPhy::CalculateTxDuration(psduMap,
2774 txVector,
2775 mac->GetWifiPhy(linkId)->GetPhyBand());
2776 NS_LOG_INFO("This is the first Trigger Frame\n");
2777 // once the Trigger Frame is received by the EMLSR client, make the client application
2778 // on the EMLSR client generate two packets. These packets will be sent via UL OFDMA
2779 // because the EMLSR client has blocked transmissions on other links when receiving
2780 // this Trigger Frame, hence it will not try to get access on other links via EDCA
2782 txDuration + MicroSeconds(1), // to account for propagation delay
2783 [=, this]() {
2784 for (const auto id : m_staMacs[0]->GetLinkIds())
2785 {
2787 m_staMacs[0]->GetFrameExchangeManager(id));
2789 ehtFem->UsingOtherEmlsrLink(),
2790 (id != linkId),
2791 "Link " << +id << " was" << (id == linkId ? " not" : "")
2792 << " expected to be blocked on EMLSR client at time "
2793 << Simulator::Now().As(Time::NS));
2794 }
2795 NS_LOG_INFO("Generate two packets\n");
2796 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2797 GetApplication(UPLINK, 0, 2, 100));
2798 });
2799 }
2800 break;
2801
2804 {
2805 CtrlBAckResponseHeader blockAck;
2806 psdu->GetPayload(0)->PeekHeader(blockAck);
2807 if (blockAck.IsMultiSta())
2808 {
2809 auto txDuration =
2811 txVector,
2812 mac->GetWifiPhy(linkId)->GetPhyBand());
2813 Simulator::Stop(txDuration + MicroSeconds(1));
2814 }
2815 }
2816 break;
2817
2818 default:;
2819 }
2820
2821 if (psdu->GetHeader(0).IsCfEnd())
2822 {
2823 // we do not check CF-End frames
2824 m_txPsdus.pop_back();
2825 }
2826}
2827
2828void
2838
2839void
2841{
2842 auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
2843 NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "No MU scheduler installed on AP MLD");
2844
2845 NS_LOG_INFO("Setting Access Request interval");
2846
2847 const auto interval = MilliSeconds(50);
2848 muScheduler->SetAccessReqInterval(interval);
2849 m_startAccessReq = Simulator::Now() + interval;
2850}
2851
2852void
2854{
2855 /**
2856 * Sending BSRP TF disabled.
2857 *
2858 * The figure assumes that link 0 is used to send the first Trigger Frame after that the
2859 * AP MLD requests channel access through the Multi-user scheduler. The first Trigger Frame
2860 * is MU-RTS because EMLSR client needs an ICF; the other Trigger Frames are Basic TFs and
2861 * do not solicit the EMLSR client.
2862 * ┌─────┐ ┌─────┐ ┌──────┐
2863 * │ MU │ │Basic│ │Multi-│
2864 * [link 0] │ RTS │ │ TF │ │STA BA│
2865 * ───────────┴─────┴┬───┬┴─────┴┬────────┬─┴──────┴───────────────
2866 * │CTS│ │QoS Null│
2867 * ├───┤ ├────────┤
2868 * │CTS│ │QoS Data│
2869 * └───┘ └────────┘
2870 *
2871 * ┌─────┐
2872 * │Basic│
2873 * [link 1] │ TF │
2874 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2875 * │QoS │
2876 * │Null│
2877 * └────┘
2878 *
2879 * ┌─────┐
2880 * │Basic│
2881 * [link 2] │ TF │
2882 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2883 * │QoS │
2884 * │Null│
2885 * └────┘
2886 *
2887 * Sending BSRP TF enabled.
2888 *
2889 * The figure assumes that link 0 is used to send the first Trigger Frame after that the
2890 * AP MLD requests channel access through the Multi-user scheduler. The first Trigger Frames
2891 * are all BSRP Trigger Frames, but only the first one solicits the EMLSR client, too.
2892 * ┌─────┐ ┌─────┐ ┌──────┐
2893 * │BSRP │ │Basic│ │Multi-│
2894 * [link 0] │ TF │ │ TF │ │STA BA│
2895 * ───────────┴─────┴┬────────┬┴─────┴┬────────┬─┴──────┴──────────
2896 * │QoS Null│ │QoS Data│
2897 * ├────────┤ └────────┘
2898 * │QoS Null│
2899 * └────────┘
2900 *
2901 * ┌─────┐
2902 * │BSRP │
2903 * [link 1] │ TF │
2904 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2905 * │QoS │
2906 * │Null│
2907 * └────┘
2908 *
2909 * ┌─────┐
2910 * │BSRP │
2911 * [link 2] │ TF │
2912 * ─────────────┴─────┴┬────┬──────────────────────────────────────
2913 * │QoS │
2914 * │Null│
2915 * └────┘
2916 */
2917
2918 NS_TEST_ASSERT_MSG_GT(m_txPsdusPos, 0, "First Trigger Frame not detected");
2919
2920 // Check the Trigger Frames (one per link) after requesting channel access
2921 auto index = m_txPsdusPos;
2922 const auto firstLinkId = m_txPsdus[m_txPsdusPos].linkId;
2923 for (; index < m_txPsdusPos + 3; ++index)
2924 {
2925 NS_TEST_ASSERT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsTrigger(),
2926 true,
2927 "Expected a Trigger Frame");
2928 CtrlTriggerHeader trigger;
2929 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
2930
2931 TriggerFrameType triggerType =
2932 m_enableBsrp ? TriggerFrameType::BSRP_TRIGGER
2933 : (index == m_txPsdusPos ? TriggerFrameType::MU_RTS_TRIGGER
2934 : TriggerFrameType::BASIC_TRIGGER);
2935 NS_TEST_EXPECT_MSG_EQ(+static_cast<uint8_t>(trigger.GetType()),
2936 +static_cast<uint8_t>(triggerType),
2937 "Unexpected Trigger Frame type on link " << +m_txPsdus[index].linkId);
2938
2939 // only the first TF solicits the EMLSR client and the non-AP MLD
2941 trigger.GetNUserInfoFields(),
2942 (index == m_txPsdusPos ? 2 : 1),
2943 "Unexpected number of User Info fields for Trigger Frame, index=" << index);
2944 }
2945
2946 auto startIndex = index;
2947 std::size_t ctsCount = 0;
2948 std::size_t qosNullCount = 0;
2949 // Check responses to Trigger Frames
2950 for (; index < startIndex + 4; ++index)
2951 {
2952 const auto& hdr = m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
2953
2954 if (hdr.IsCts())
2955 {
2956 ++ctsCount;
2957 continue;
2958 }
2959
2960 if (hdr.IsQosData() && !hdr.HasData())
2961 {
2962 ++qosNullCount;
2963 // if BSRP is enabled, the QoS Null frame sent by the EMLSR client in response to the
2964 // first BSRP TF reports a non-null buffer status
2965 if (m_enableBsrp &&
2966 hdr.GetAddr2() == m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress())
2967 {
2968 NS_TEST_EXPECT_MSG_GT(+hdr.GetQosQueueSize(), 0, "Unexpected buffer size");
2969 }
2970 else
2971 {
2972 NS_TEST_EXPECT_MSG_EQ(+hdr.GetQosQueueSize(), 0, "Unexpected buffer size");
2973 }
2974 continue;
2975 }
2976 }
2977 NS_TEST_EXPECT_MSG_EQ(ctsCount, (m_enableBsrp ? 0 : 2), "Unexpected number of CTS frames");
2978 NS_TEST_EXPECT_MSG_EQ(qosNullCount,
2979 (m_enableBsrp ? 4 : 2),
2980 "Unexpected number of QoS Null frames");
2981
2982 // we expect only one Basic Trigger Frame (sent on the same link as the first Trigger Frame),
2983 // because the buffer status reported on the other links by the non-EMLSR client is zero
2984 NS_TEST_ASSERT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsTrigger(),
2985 true,
2986 "Expected a Trigger Frame");
2987 NS_TEST_EXPECT_MSG_EQ(+m_txPsdus[index].linkId,
2988 +firstLinkId,
2989 "Unexpected link ID for Basic TF");
2990 CtrlTriggerHeader trigger;
2991 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
2992
2993 NS_TEST_EXPECT_MSG_EQ(+static_cast<uint8_t>(trigger.GetType()),
2994 +static_cast<uint8_t>(TriggerFrameType::BASIC_TRIGGER),
2995 "Unexpected Trigger Frame type");
2996
2997 // when BSRP TF is enabled, the non-EMLSR client has already communicated a buffer status of
2998 // zero, so it is not solicited by the AP through the Basic Trigger Frame. Otherwise, it is
2999 // solicited because buffer status was not known when the BSRP TF was prepared (before sending
3000 // MU-RTS)
3002 (m_enableBsrp ? 1 : 2),
3003 "Unexpected number of User Info fields for Basic Trigger Frame");
3004
3005 // Response(s) to the Basic Trigger Frame
3006 startIndex = ++index;
3007 for (; index < startIndex + (m_enableBsrp ? 1 : 2); ++index)
3008 {
3009 const auto& hdr = m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
3010
3011 NS_TEST_EXPECT_MSG_EQ(hdr.IsQosData(), true, "Expected a QoS frame");
3012
3013 // EMLSR client sends a QoS Data frame, non-EMLSR client sends a QoS Null frame
3015 hdr.HasData(),
3016 (hdr.GetAddr2() == m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress()),
3017 "Unexpected type of QoS data frame");
3018
3019 if (hdr.HasData())
3020 {
3021 NS_TEST_EXPECT_MSG_EQ(m_txPsdus[index].txVector.IsUlMu(),
3022 true,
3023 "QoS Data frame should be sent in a TB PPDU");
3024 }
3025 }
3026
3027 // Finally, the AP MLD sends a Multi-STA BlockAck
3028 NS_TEST_EXPECT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
3029 true,
3030 "Expected a BlockAck frame");
3031 CtrlBAckResponseHeader blockAck;
3032 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(blockAck);
3033 NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA BlockAck");
3034}
3035
3037 : TestSuite("wifi-emlsr-basic-exchanges", Type::UNIT)
3038{
3039 for (const auto& emlsrLinks :
3040 {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
3041 {
3043 0,
3044 emlsrLinks,
3045 {MicroSeconds(32)},
3046 {MicroSeconds(32)},
3047 MicroSeconds(512),
3048 true /* putAuxPhyToSleep */}),
3049 TestCase::Duration::QUICK);
3051 1,
3052 emlsrLinks,
3053 {MicroSeconds(64)},
3054 {MicroSeconds(64)},
3055 MicroSeconds(512),
3056 false /* putAuxPhyToSleep */}),
3057 TestCase::Duration::QUICK);
3059 2,
3060 emlsrLinks,
3061 {MicroSeconds(128), MicroSeconds(256)},
3062 {MicroSeconds(128), MicroSeconds(256)},
3063 MicroSeconds(512),
3064 true /* putAuxPhyToSleep */}),
3065 TestCase::Duration::QUICK);
3066 }
3067
3068 for (auto genBackoffIfTxopWithoutTx : {true, false})
3069 {
3070 AddTestCase(new EmlsrUlTxopTest({{0, 1, 2},
3071 MHz_u{40},
3072 MHz_u{20},
3073 MicroSeconds(5504),
3074 3,
3075 genBackoffIfTxopWithoutTx,
3076 true, /* putAuxPhyToSleep */
3077 false /* switchMainPhyBackDelayTimeout */}),
3078 TestCase::Duration::QUICK);
3079 AddTestCase(new EmlsrUlTxopTest({{0, 1},
3080 MHz_u{40},
3081 MHz_u{20},
3082 MicroSeconds(5504),
3083 1,
3084 genBackoffIfTxopWithoutTx,
3085 false, /* putAuxPhyToSleep */
3086 true /* switchMainPhyBackDelayTimeout */}),
3087 TestCase::Duration::QUICK);
3088 }
3089
3090 AddTestCase(new EmlsrUlOfdmaTest(false), TestCase::Duration::QUICK);
3091 AddTestCase(new EmlsrUlOfdmaTest(true), TestCase::Duration::QUICK);
3092}
3093
#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.
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:136
@ 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:53
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition wifi-mode.h:24
static constexpr uint8_t MAX_PROPAGATION_DELAY_USEC
maximum propagation delay
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