A Discrete-Event Network Simulator
API
he-frame-exchange-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Stefano Avallone <stavallo@unina.it>
18 */
19
21
22#include "he-configuration.h"
23#include "he-phy.h"
25
26#include "ns3/abort.h"
27#include "ns3/ap-wifi-mac.h"
28#include "ns3/log.h"
29#include "ns3/recipient-block-ack-agreement.h"
30#include "ns3/snr-tag.h"
31#include "ns3/sta-wifi-mac.h"
32
33#include <algorithm>
34#include <functional>
35
36#undef NS_LOG_APPEND_CONTEXT
37#define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "][mac=" << m_self << "] "
38
39namespace ns3
40{
41
42NS_LOG_COMPONENT_DEFINE("HeFrameExchangeManager");
43
44NS_OBJECT_ENSURE_REGISTERED(HeFrameExchangeManager);
45
46TypeId
48{
49 static TypeId tid = TypeId("ns3::HeFrameExchangeManager")
51 .AddConstructor<HeFrameExchangeManager>()
52 .SetGroupName("Wifi");
53 return tid;
54}
55
57 : m_triggerFrameInAmpdu(false)
58{
59 NS_LOG_FUNCTION(this);
60}
61
63{
65}
66
67uint16_t
69{
70 NS_ASSERT(m_mac->GetHeConfiguration());
71 if (m_mac->GetHeConfiguration()->GetMpduBufferSize() > 64)
72 {
73 return 256;
74 }
75 return 64;
76}
77
78void
80{
81 m_apMac = DynamicCast<ApWifiMac>(mac);
82 m_staMac = DynamicCast<StaWifiMac>(mac);
84}
85
86void
88{
89 NS_LOG_FUNCTION(this);
90 m_apMac = nullptr;
91 m_staMac = nullptr;
92 m_psduMap.clear();
94 m_muScheduler = nullptr;
97}
98
99void
101{
103 NS_ABORT_MSG_IF(!m_apMac, "A Multi-User Scheduler can only be aggregated to an AP");
105 "A Multi-User Scheduler can only be aggregated to an HE AP");
106 m_muScheduler = muScheduler;
107}
108
109bool
110HeFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
111{
112 NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
113
116
117 /*
118 * We consult the Multi-user Scheduler (if available) to know the type of transmission to make
119 * if:
120 * - there is no pending BlockAckReq to transmit
121 * - either the AC queue is empty (the scheduler might select an UL MU transmission)
122 * or the next frame in the AC queue is a non-broadcast QoS data frame addressed to
123 * a receiver with which a BA agreement has been already established
124 */
125 if (m_muScheduler && !edca->GetBaManager()->GetBar(false) &&
126 (!(mpdu = edca->PeekNextMpdu(m_linkId)) ||
127 (mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().GetAddr1().IsGroup() &&
128 edca->GetBaAgreementEstablished(mpdu->GetHeader().GetAddr1(),
129 mpdu->GetHeader().GetQosTid()))))
130 {
131 txFormat =
132 m_muScheduler->NotifyAccessGranted(edca, availableTime, initialFrame, m_allowedWidth);
133 }
134
135 if (txFormat == MultiUserScheduler::SU_TX)
136 {
137 return VhtFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
138 }
139
140 if (txFormat == MultiUserScheduler::DL_MU_TX)
141 {
142 if (m_muScheduler->GetDlMuInfo().psduMap.empty())
143 {
145 "The Multi-user Scheduler returned DL_MU_TX with empty psduMap, do not transmit");
146 return false;
147 }
148
149 SendPsduMapWithProtection(m_muScheduler->GetDlMuInfo().psduMap,
150 m_muScheduler->GetDlMuInfo().txParams);
151 return true;
152 }
153
154 if (txFormat == MultiUserScheduler::UL_MU_TX)
155 {
156 auto packet = Create<Packet>();
157 packet->AddHeader(m_muScheduler->GetUlMuInfo().trigger);
158 auto trigger = Create<WifiMpdu>(packet, m_muScheduler->GetUlMuInfo().macHdr);
161 GetWifiPsdu(trigger, m_muScheduler->GetUlMuInfo().txParams.m_txVector)}},
162 m_muScheduler->GetUlMuInfo().txParams);
163 return true;
164 }
165
166 return false;
167}
168
169bool
171 Time availableTime,
172 bool initialFrame)
173{
174 NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
175
176 // First, check if there is a BAR to be transmitted
177 Ptr<const WifiMpdu> peekedItem = edca->GetBaManager()->GetBar(false);
178
179 if (!peekedItem)
180 {
181 NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
182 return false;
183 }
184
185 if (peekedItem->GetHeader().IsBlockAckReq())
186 {
187 // BlockAckReq are handled by the HT FEM
188 return HtFrameExchangeManager::SendMpduFromBaManager(edca, availableTime, initialFrame);
189 }
190
191 NS_ASSERT(peekedItem->GetHeader().IsTrigger());
192 m_triggerFrame = Copy(edca->GetBaManager()->GetBar());
193
194 SendPsduMap();
195 return true;
196}
197
198void
200{
201 NS_LOG_FUNCTION(this << &txParams);
202
203 m_psduMap = std::move(psduMap);
204 m_txParams = std::move(txParams);
205
206#ifdef NS3_BUILD_PROFILE_DEBUG
207 // If protection is required, the MPDUs must be stored in some queue because
208 // they are not put back in a queue if the MU-RTS/CTS exchange fails
210 {
211 for (const auto& psdu : psduMap)
212 {
213 for (const auto& mpdu : *PeekPointer(psdu.second))
214 {
215 NS_ASSERT(mpdu->GetHeader().IsCtl() || !mpdu->GetHeader().HasData() ||
216 mpdu->IsQueued());
217 }
218 }
219 }
220#endif
221
222 // Make sure that the acknowledgment time has been computed, so that SendMuRts()
223 // can reuse this value.
225
226 if (m_txParams.m_acknowledgment->acknowledgmentTime == Time::Min())
227 {
229 }
230
231 // Set QoS Ack policy
232 for (auto& psdu : m_psduMap)
233 {
235 }
236
238 {
239 NS_ABORT_MSG_IF(m_psduMap.size() > 1, "Cannot use RTS/CTS with MU PPDUs");
241 }
242 else if (m_txParams.m_protection->method == WifiProtection::NONE)
243 {
244 SendPsduMap();
245 }
246 else
247 {
248 NS_ABORT_MSG("Unknown or prohibited protection type: " << m_txParams.m_protection.get());
249 }
250}
251
254{
255 auto it = std::find_if(
256 psduMap.begin(),
257 psduMap.end(),
258 [&to](std::pair<uint16_t, Ptr<WifiPsdu>> psdu) { return psdu.second->GetAddr1() == to; });
259 if (it != psduMap.end())
260 {
261 return it->second;
262 }
263 return nullptr;
264}
265
266void
268{
269 NS_LOG_FUNCTION(this << *rts << txVector);
270
271 if (m_psduMap.empty())
272 {
273 // A CTS Timeout occurred when protecting a single PSDU that is not included
274 // in a DL MU PPDU is handled by the parent classes
276 return;
277 }
278
279 NS_ABORT_MSG_IF(m_psduMap.size() > 1, "RTS/CTS cannot be used to protect an MU PPDU");
280 DoCtsTimeout(m_psduMap.begin()->second);
281 m_psduMap.clear();
282}
283
284void
286{
287 NS_LOG_FUNCTION(this);
288
291
292 WifiTxTimer::Reason timerType = WifiTxTimer::NOT_RUNNING; // no timer
293 WifiTxVector* responseTxVector = nullptr;
294 Ptr<WifiMpdu> mpdu = nullptr;
295 Ptr<WifiPsdu> psdu = nullptr;
296 WifiTxVector txVector;
297
298 // Compute the type of TX timer to set depending on the acknowledgment method
299
300 /*
301 * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
302 */
304 {
305 WifiDlMuBarBaSequence* acknowledgment =
307
308 // schedule the transmission of required BlockAckReq frames
309 for (const auto& psdu : m_psduMap)
310 {
311 if (acknowledgment->stationsSendBlockAckReqTo.find(psdu.second->GetAddr1()) !=
312 acknowledgment->stationsSendBlockAckReqTo.end())
313 {
314 // the receiver of this PSDU will receive a BlockAckReq
315 std::set<uint8_t> tids = psdu.second->GetTids();
316 NS_ABORT_MSG_IF(tids.size() > 1,
317 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
318 uint8_t tid = *tids.begin();
319
322 m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid));
323 }
324 }
325
326 if (!acknowledgment->stationsReplyingWithNormalAck.empty())
327 {
328 // a station will reply immediately with a Normal Ack
330 responseTxVector =
331 &acknowledgment->stationsReplyingWithNormalAck.begin()->second.ackTxVector;
332 psdu =
333 GetPsduTo(acknowledgment->stationsReplyingWithNormalAck.begin()->first, m_psduMap);
334 NS_ASSERT(psdu->GetNMpdus() == 1);
335 mpdu = *psdu->begin();
336 }
337 else if (!acknowledgment->stationsReplyingWithBlockAck.empty())
338 {
339 // a station will reply immediately with a Block Ack
340 timerType = WifiTxTimer::WAIT_BLOCK_ACK;
341 responseTxVector =
342 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
343 psdu =
344 GetPsduTo(acknowledgment->stationsReplyingWithBlockAck.begin()->first, m_psduMap);
345 }
346 // else no station will reply immediately
347 }
348 /*
349 * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
350 */
352 {
353 WifiDlMuTfMuBar* acknowledgment =
354 static_cast<WifiDlMuTfMuBar*>(m_txParams.m_acknowledgment.get());
355
356 if (!m_triggerFrame)
357 {
358 // we are transmitting the DL MU PPDU and have to schedule the
359 // transmission of a MU-BAR Trigger Frame.
360 // Create a TRIGVECTOR by "merging" all the BlockAck TXVECTORs
361 std::map<uint16_t, CtrlBAckRequestHeader> recipients;
362
363 NS_ASSERT(!acknowledgment->stationsReplyingWithBlockAck.empty());
364 auto staIt = acknowledgment->stationsReplyingWithBlockAck.begin();
365 m_trigVector = staIt->second.blockAckTxVector;
366 while (staIt != acknowledgment->stationsReplyingWithBlockAck.end())
367 {
369 uint16_t staId = m_apMac->GetAssociationId(staIt->first, m_linkId);
370
372 staIt->second.blockAckTxVector.GetHeMuUserInfo(staId));
373 recipients.emplace(staId, staIt->second.barHeader);
374
375 staIt++;
376 }
377 // set the Length field of the response TXVECTOR, which is needed to correctly
378 // set the UL Length field of the MU-BAR Trigger Frame
379 m_trigVector.SetLength(acknowledgment->ulLength);
380
383 }
384 else
385 {
386 // we are transmitting the MU-BAR following the DL MU PPDU after a SIFS.
387 // m_psduMap and m_txParams are still the same as when the DL MU PPDU was sent.
388 // record the set of stations expected to send a BlockAck frame
389 m_staExpectTbPpduFrom.clear();
390 for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
391 {
392 m_staExpectTbPpduFrom.insert(station.first);
393 }
394
395 Ptr<WifiPsdu> triggerPsdu = GetWifiPsdu(m_triggerFrame, acknowledgment->muBarTxVector);
396 Time txDuration = m_phy->CalculateTxDuration(triggerPsdu->GetSize(),
397 acknowledgment->muBarTxVector,
398 m_phy->GetPhyBand());
399 // update acknowledgmentTime to correctly set the Duration/ID
400 acknowledgment->acknowledgmentTime -= (m_phy->GetSifs() + txDuration);
401 m_triggerFrame->GetHeader().SetDuration(GetPsduDurationId(txDuration, m_txParams));
402
403 responseTxVector =
404 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
405 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
407
409 timeout,
411 this,
412 &m_psduMap,
414 m_staExpectTbPpduFrom.size());
416
417 ForwardPsduDown(triggerPsdu, acknowledgment->muBarTxVector);
418
419 // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
420 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
421 hePhy->SetTrigVector(m_trigVector, timeout);
422
423 return;
424 }
425 }
426 /*
427 * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
428 */
430 {
431 WifiDlMuAggregateTf* acknowledgment =
433
434 // record the set of stations expected to send a BlockAck frame
435 m_staExpectTbPpduFrom.clear();
436
438 acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
439
440 for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
441 {
442 m_staExpectTbPpduFrom.insert(station.first);
443 // check that the station that is expected to send a BlockAck frame is
444 // actually the receiver of a PSDU
445 auto psduMapIt = std::find_if(m_psduMap.begin(),
446 m_psduMap.end(),
447 [&station](std::pair<uint16_t, Ptr<WifiPsdu>> psdu) {
448 return psdu.second->GetAddr1() == station.first;
449 });
450
451 NS_ASSERT(psduMapIt != m_psduMap.end());
452 // add a MU-BAR Trigger Frame to the PSDU
453 std::vector<Ptr<WifiMpdu>> mpduList(psduMapIt->second->begin(),
454 psduMapIt->second->end());
455 NS_ASSERT(mpduList.size() == psduMapIt->second->GetNMpdus());
456 // set the Length field of the response TXVECTOR, which is needed to correctly
457 // set the UL Length field of the MU-BAR Trigger Frame
458 station.second.blockAckTxVector.SetLength(acknowledgment->ulLength);
459 mpduList.push_back(PrepareMuBar(station.second.blockAckTxVector,
460 {{psduMapIt->first, station.second.barHeader}}));
461 psduMapIt->second = Create<WifiPsdu>(std::move(mpduList));
463 psduMapIt->first,
464 station.second.blockAckTxVector.GetHeMuUserInfo(psduMapIt->first));
465 }
466
468 responseTxVector =
469 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
470 m_trigVector.SetLength(acknowledgment->ulLength);
471 }
472 /*
473 * Basic Trigger Frame starting an UL MU transmission
474 */
475 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
476 {
477 // the PSDU map being sent must contain a (Basic) Trigger Frame
478 NS_ASSERT(m_psduMap.size() == 1 && m_psduMap.begin()->first == SU_STA_ID &&
479 (mpdu = *m_psduMap.begin()->second->begin())->GetHeader().IsTrigger());
480
481 WifiUlMuMultiStaBa* acknowledgment =
482 static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
483
484 // record the set of stations solicited by this Trigger Frame
485 m_staExpectTbPpduFrom.clear();
486
487 for (const auto& station : acknowledgment->stationsReceivingMultiStaBa)
488 {
489 m_staExpectTbPpduFrom.insert(station.first.first);
490 }
491
492 // Reset stationsReceivingMultiStaBa, which will be filled as soon as
493 // TB PPDUs are received
494 acknowledgment->stationsReceivingMultiStaBa.clear();
495 acknowledgment->baType.m_bitmapLen.clear();
496
497 // Add a SIFS and the TB PPDU duration to the acknowledgment time of the
498 // Trigger Frame, so that its Duration/ID is correctly computed
499 NS_ASSERT(m_muScheduler);
501 m_muScheduler->GetUlMuInfo().trigger.GetUlLength(),
502 acknowledgment->tbPpduTxVector,
503 m_phy->GetPhyBand());
504 acknowledgment->acknowledgmentTime += m_mac->GetWifiPhy()->GetSifs() + tbPpduDuration;
505
507 responseTxVector = &acknowledgment->tbPpduTxVector;
508 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo().trigger);
509 }
510 /*
511 * BSRP Trigger Frame
512 */
513 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE &&
514 !m_txParams.m_txVector.IsUlMu() && m_psduMap.size() == 1 &&
515 m_psduMap.begin()->first == SU_STA_ID &&
516 (*m_psduMap.begin()->second->begin())->GetHeader().IsTrigger())
517 {
518 CtrlTriggerHeader& trigger = m_muScheduler->GetUlMuInfo().trigger;
519 NS_ASSERT(trigger.IsBsrp());
520 NS_ASSERT(m_apMac);
521
522 // record the set of stations solicited by this Trigger Frame
523 m_staExpectTbPpduFrom.clear();
524
525 for (const auto& userInfo : trigger)
526 {
527 auto staIt = m_apMac->GetStaList().find(userInfo.GetAid12());
528 NS_ASSERT(staIt != m_apMac->GetStaList().end());
529 m_staExpectTbPpduFrom.insert(staIt->second);
530 }
531
532 // Add a SIFS and the TB PPDU duration to the acknowledgment time of the
533 // Trigger Frame, so that its Duration/ID is correctly computed
534 WifiNoAck* acknowledgment = static_cast<WifiNoAck*>(m_txParams.m_acknowledgment.get());
535 txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
536 acknowledgment->acknowledgmentTime +=
537 m_mac->GetWifiPhy()->GetSifs() +
538 HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
539 txVector,
540 m_phy->GetPhyBand());
541
543 responseTxVector = &txVector;
544 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo().trigger);
545 }
546 /*
547 * TB PPDU solicited by a Basic Trigger Frame
548 */
549 else if (m_txParams.m_txVector.IsUlMu() &&
550 m_txParams.m_acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
551 {
552 NS_ASSERT(m_psduMap.size() == 1);
554 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
555 txVector = GetWifiRemoteStationManager()->GetBlockAckTxVector(
556 m_psduMap.begin()->second->GetAddr1(),
557 m_txParams.m_txVector);
558 responseTxVector = &txVector;
559 }
560 /*
561 * QoS Null frames solicited by a BSRP Trigger Frame
562 */
563 else if (m_txParams.m_txVector.IsUlMu() &&
564 m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
565 {
566 // No response is expected, so do nothing.
567 }
568 else
569 {
570 NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
571 << m_txParams.m_acknowledgment.get() << ")");
572 }
573
574 // create a map of Ptr<const WifiPsdu>, as required by the PHY
575 WifiConstPsduMap psduMap;
576 for (const auto& psdu : m_psduMap)
577 {
578 psduMap.emplace(psdu.first, psdu.second);
579 }
580
581 Time txDuration;
582 if (m_txParams.m_txVector.IsUlMu())
583 {
584 txDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(m_txParams.m_txVector.GetLength(),
585 m_txParams.m_txVector,
586 m_phy->GetPhyBand());
587 }
588 else
589 {
590 txDuration =
591 m_phy->CalculateTxDuration(psduMap, m_txParams.m_txVector, m_phy->GetPhyBand());
592
593 // Set Duration/ID
594 Time durationId = GetPsduDurationId(txDuration, m_txParams);
595 for (auto& psdu : m_psduMap)
596 {
597 psdu.second->SetDuration(durationId);
598 }
599 }
600
601 if (timerType == WifiTxTimer::NOT_RUNNING)
602 {
603 if (!m_txParams.m_txVector.IsUlMu())
604 {
606 }
607 }
608 else
609 {
610 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
611 m_phy->CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
612 m_channelAccessManager->NotifyAckTimeoutStartNow(timeout);
613
614 // start timer
615 switch (timerType)
616 {
618 NS_ASSERT(mpdu);
619 m_txTimer.Set(timerType,
620 timeout,
622 this,
623 mpdu,
624 m_txParams.m_txVector);
625 break;
627 NS_ASSERT(psdu);
628 m_txTimer.Set(timerType,
629 timeout,
631 this,
632 psdu,
633 m_txParams.m_txVector);
634 break;
636 m_txTimer.Set(timerType,
637 timeout,
639 this,
640 &m_psduMap,
641 &m_staExpectTbPpduFrom,
642 m_staExpectTbPpduFrom.size());
643 break;
646 m_txTimer.Set(timerType,
647 timeout,
649 this,
650 &m_psduMap,
651 &m_staExpectTbPpduFrom,
652 m_staExpectTbPpduFrom.size());
653 break;
655 m_txTimer.Set(timerType,
656 timeout,
658 this,
659 m_psduMap.begin()->second,
660 m_txParams.m_txVector);
661 break;
662 default:
663 NS_ABORT_MSG("Unknown timer type: " << timerType);
664 break;
665 }
666 }
667
668 // transmit the map of PSDUs
669 ForwardPsduMapDown(psduMap, m_txParams.m_txVector);
670
674 {
675 // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
676 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
677 hePhy->SetTrigVector(m_trigVector, m_txTimer.GetDelayLeft());
678 }
679 else if (timerType == WifiTxTimer::NOT_RUNNING && m_txParams.m_txVector.IsUlMu())
680 {
681 // clear m_psduMap after sending QoS Null frames following a BSRP Trigger Frame
682 Simulator::Schedule(txDuration, &WifiPsduMap::clear, &m_psduMap);
683 }
684}
685
686void
687HeFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector)
688{
689 NS_LOG_FUNCTION(this << psduMap << txVector);
690
691 if (txVector.IsDlMu())
692 {
693 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
694 auto sigBMode = hePhy->GetSigBMode(txVector);
695 txVector.SetSigBMode(sigBMode);
696 }
697
698 for (const auto& psdu : psduMap)
699 {
700 NS_LOG_DEBUG("Transmitting: [STAID=" << psdu.first << ", " << *psdu.second << "]");
701 }
702 NS_LOG_DEBUG("TXVECTOR: " << txVector);
703 for (const auto& psdu : psduMap)
704 {
705 NotifyTxToEdca(psdu.second);
706 }
707 if (psduMap.size() > 1 || psduMap.begin()->second->IsAggregate() ||
708 psduMap.begin()->second->IsSingle())
709 {
710 txVector.SetAggregation(true);
711 }
712
713 m_phy->Send(psduMap, txVector);
714}
715
717HeFrameExchangeManager::PrepareMuBar(const WifiTxVector& responseTxVector,
718 std::map<uint16_t, CtrlBAckRequestHeader> recipients) const
719{
720 NS_LOG_FUNCTION(this << responseTxVector);
721 NS_ASSERT(responseTxVector.GetHeMuUserInfoMap().size() == recipients.size());
722 NS_ASSERT(!recipients.empty());
723
725 SetTargetRssi(muBar);
726 // Set the CS Required subfield to true, unless the UL Length subfield is less
727 // than or equal to 418 (see Section 26.5.2.5 of 802.11ax-2021)
728 muBar.SetCsRequired(muBar.GetUlLength() > 418);
729
730 // Add the Trigger Dependent User Info subfield to every User Info field
731 for (auto& userInfo : muBar)
732 {
733 auto recipientIt = recipients.find(userInfo.GetAid12());
734 NS_ASSERT(recipientIt != recipients.end());
735
736 // Store the BAR in the Trigger Dependent User Info subfield
737 userInfo.SetMuBarTriggerDepUserInfo(recipientIt->second);
738 }
739
740 Ptr<Packet> bar = Create<Packet>();
741 bar->AddHeader(muBar);
742 Mac48Address rxAddress;
743 // "If the Trigger frame has one User Info field and the AID12 subfield of the
744 // User Info contains the AID of a STA, then the RA field is set to the address
745 // of that STA". Otherwise, it is set to the broadcast address (Sec. 9.3.1.23 -
746 // 802.11ax amendment draft 3.0)
747 if (muBar.GetNUserInfoFields() > 1)
748 {
749 rxAddress = Mac48Address::GetBroadcast();
750 }
751 else
752 {
753 NS_ASSERT(m_apMac);
754 rxAddress = m_apMac->GetStaList().at(recipients.begin()->first);
755 }
756
757 WifiMacHeader hdr;
759 hdr.SetAddr1(rxAddress);
760 hdr.SetAddr2(m_self);
761 hdr.SetDsNotTo();
762 hdr.SetDsNotFrom();
763 hdr.SetNoRetry();
764 hdr.SetNoMoreFragments();
765
766 return Create<WifiMpdu>(bar, hdr);
767}
768
769void
770HeFrameExchangeManager::CalculateAcknowledgmentTime(WifiAcknowledgment* acknowledgment) const
771{
772 NS_LOG_FUNCTION(this << acknowledgment);
773 NS_ASSERT(acknowledgment);
774
775 /*
776 * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
777 */
778 if (acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
779 {
780 WifiDlMuBarBaSequence* dlMuBarBaAcknowledgment =
781 static_cast<WifiDlMuBarBaSequence*>(acknowledgment);
782
783 Time duration = Seconds(0);
784
785 // normal ack or implicit BAR policy can be used for (no more than) one receiver
786 NS_ABORT_IF(dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.size() +
787 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.size() >
788 1);
789
790 if (!dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.empty())
791 {
792 const auto& info =
793 dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.begin()->second;
794 duration +=
795 m_phy->GetSifs() +
796 m_phy->CalculateTxDuration(GetAckSize(), info.ackTxVector, m_phy->GetPhyBand());
797 }
798
799 if (!dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.empty())
800 {
801 const auto& info =
802 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.begin()->second;
803 duration += m_phy->GetSifs() + m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
804 info.blockAckTxVector,
805 m_phy->GetPhyBand());
806 }
807
808 for (const auto& stations : dlMuBarBaAcknowledgment->stationsSendBlockAckReqTo)
809 {
810 const auto& info = stations.second;
811 duration += m_phy->GetSifs() +
812 m_phy->CalculateTxDuration(GetBlockAckRequestSize(info.barType),
813 info.blockAckReqTxVector,
814 m_phy->GetPhyBand()) +
815 m_phy->GetSifs() +
816 m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
817 info.blockAckTxVector,
818 m_phy->GetPhyBand());
819 }
820
821 dlMuBarBaAcknowledgment->acknowledgmentTime = duration;
822 }
823 /*
824 * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
825 */
826 else if (acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
827 {
828 WifiDlMuTfMuBar* dlMuTfMuBarAcknowledgment = static_cast<WifiDlMuTfMuBar*>(acknowledgment);
829
830 Time duration = Seconds(0);
831
832 for (const auto& stations : dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck)
833 {
834 // compute the TX duration of the BlockAck response from this receiver.
835 const auto& info = stations.second;
836 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
837 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
838 Time currBlockAckDuration = m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
839 info.blockAckTxVector,
840 m_phy->GetPhyBand(),
841 staId);
842 // update the max duration among all the Block Ack responses
843 if (currBlockAckDuration > duration)
844 {
845 duration = currBlockAckDuration;
846 }
847 }
848
849 // The computed duration may not be coded exactly in the L-SIG length, hence determine
850 // the exact duration corresponding to the value that will be coded in this field.
851 WifiTxVector& txVector = dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck.begin()
852 ->second.blockAckTxVector;
853 std::tie(dlMuTfMuBarAcknowledgment->ulLength, duration) =
854 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
855
856 uint32_t muBarSize = GetMuBarSize(dlMuTfMuBarAcknowledgment->barTypes);
857 if (dlMuTfMuBarAcknowledgment->muBarTxVector.GetModulationClass() >= WIFI_MOD_CLASS_VHT)
858 {
859 // MU-BAR TF will be sent as an S-MPDU
860 muBarSize = MpduAggregator::GetSizeIfAggregated(muBarSize, 0);
861 }
862 dlMuTfMuBarAcknowledgment->acknowledgmentTime =
863 m_phy->GetSifs() +
864 m_phy->CalculateTxDuration(muBarSize,
865 dlMuTfMuBarAcknowledgment->muBarTxVector,
866 m_phy->GetPhyBand()) +
867 m_phy->GetSifs() + duration;
868 }
869 /*
870 * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
871 */
872 else if (acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
873 {
874 WifiDlMuAggregateTf* dlMuAggrTfAcknowledgment =
875 static_cast<WifiDlMuAggregateTf*>(acknowledgment);
876
877 Time duration = Seconds(0);
878
879 for (const auto& stations : dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck)
880 {
881 // compute the TX duration of the BlockAck response from this receiver.
882 const auto& info = stations.second;
883 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
884 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
885 Time currBlockAckDuration = m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
886 info.blockAckTxVector,
887 m_phy->GetPhyBand(),
888 staId);
889 // update the max duration among all the Block Ack responses
890 if (currBlockAckDuration > duration)
891 {
892 duration = currBlockAckDuration;
893 }
894 }
895
896 // The computed duration may not be coded exactly in the L-SIG length, hence determine
897 // the exact duration corresponding to the value that will be coded in this field.
898 WifiTxVector& txVector =
899 dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
900 std::tie(dlMuAggrTfAcknowledgment->ulLength, duration) =
901 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
902 dlMuAggrTfAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + duration;
903 }
904 /*
905 * Basic Trigger Frame starting an UL MU transmission
906 */
907 else if (acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
908 {
909 WifiUlMuMultiStaBa* ulMuMultiStaBa = static_cast<WifiUlMuMultiStaBa*>(acknowledgment);
910
911 Time duration = m_phy->CalculateTxDuration(GetBlockAckSize(ulMuMultiStaBa->baType),
912 ulMuMultiStaBa->multiStaBaTxVector,
913 m_phy->GetPhyBand());
914 ulMuMultiStaBa->acknowledgmentTime = m_phy->GetSifs() + duration;
915 }
916 /*
917 * TB PPDU solicired by a Basic or BSRP Trigger Frame
918 */
919 else if (acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
920 {
921 // The station solicited by the Trigger Frame does not have to account
922 // for the actual acknowledgment time since it is given the PPDU duration
923 // through the Trigger Frame
924 acknowledgment->acknowledgmentTime = Seconds(0);
925 }
926 else
927 {
928 VhtFrameExchangeManager::CalculateAcknowledgmentTime(acknowledgment);
929 }
930}
931
932Time
933HeFrameExchangeManager::GetTxDuration(uint32_t ppduPayloadSize,
934 Mac48Address receiver,
935 const WifiTxParameters& txParams) const
936{
937 if (!txParams.m_txVector.IsMu())
938 {
939 return VhtFrameExchangeManager::GetTxDuration(ppduPayloadSize, receiver, txParams);
940 }
941
942 NS_ASSERT_MSG(!txParams.m_txVector.IsDlMu() || m_apMac, "DL MU can be done by an AP");
943 NS_ASSERT_MSG(!txParams.m_txVector.IsUlMu() || m_staMac, "UL MU can be done by a STA");
944
945 if (txParams.m_acknowledgment &&
946 txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
947 {
948 // we need to account for the size of the aggregated MU-BAR Trigger Frame
949 WifiDlMuAggregateTf* acknowledgment =
950 static_cast<WifiDlMuAggregateTf*>(txParams.m_acknowledgment.get());
951
952 const auto& info = acknowledgment->stationsReplyingWithBlockAck.find(receiver);
953 NS_ASSERT(info != acknowledgment->stationsReplyingWithBlockAck.end());
954
955 ppduPayloadSize =
956 MpduAggregator::GetSizeIfAggregated(info->second.muBarSize, ppduPayloadSize);
957 }
958
959 uint16_t staId = (txParams.m_txVector.IsDlMu() ? m_apMac->GetAssociationId(receiver, m_linkId)
960 : m_staMac->GetAssociationId());
961 Time psduDuration = m_phy->CalculateTxDuration(ppduPayloadSize,
962 txParams.m_txVector,
963 m_phy->GetPhyBand(),
964 staId);
965
966 return std::max(psduDuration, txParams.m_txDuration);
967}
968
969void
970HeFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap,
971 const std::set<Mac48Address>* staMissedTbPpduFrom,
972 std::size_t nSolicitedStations)
973{
974 NS_LOG_FUNCTION(this << psduMap << staMissedTbPpduFrom->size() << nSolicitedStations);
975
976 NS_ASSERT(psduMap);
977 NS_ASSERT(psduMap->size() == 1 && psduMap->begin()->first == SU_STA_ID &&
978 psduMap->begin()->second->GetHeader(0).IsTrigger());
979
980 // This method is called if some station(s) did not send a TB PPDU
981 NS_ASSERT(!staMissedTbPpduFrom->empty());
982 NS_ASSERT(m_edca);
983
984 if (staMissedTbPpduFrom->size() == nSolicitedStations)
985 {
986 // no station replied, the transmission failed
987 m_edca->UpdateFailedCw(m_linkId);
988
989 TransmissionFailed();
990 }
991 else if (!m_multiStaBaEvent.IsRunning())
992 {
993 m_edca->ResetCw(m_linkId);
994 TransmissionSucceeded();
995 }
996
997 m_psduMap.clear();
998}
999
1000void
1001HeFrameExchangeManager::BlockAcksInTbPpduTimeout(
1002 WifiPsduMap* psduMap,
1003 const std::set<Mac48Address>* staMissedBlockAckFrom,
1004 std::size_t nSolicitedStations)
1005{
1006 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
1007
1008 NS_ASSERT(psduMap);
1009 NS_ASSERT(m_txParams.m_acknowledgment &&
1010 (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF ||
1011 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR));
1012
1013 // This method is called if some station(s) did not send a BlockAck frame in a TB PPDU
1014 NS_ASSERT(!staMissedBlockAckFrom->empty());
1015
1016 bool resetCw;
1017
1018 if (staMissedBlockAckFrom->size() == nSolicitedStations)
1019 {
1020 // no station replied, the transmission failed
1021 // call ReportDataFailed to increase SRC/LRC
1022 GetWifiRemoteStationManager()->ReportDataFailed(*psduMap->begin()->second->begin());
1023 resetCw = false;
1024 }
1025 else
1026 {
1027 // the transmission succeeded
1028 resetCw = true;
1029 }
1030
1031 m_triggerFrame = nullptr; // this is strictly needed for DL_MU_TF_MU_BAR only
1032
1033 for (const auto& sta : *staMissedBlockAckFrom)
1034 {
1035 Ptr<WifiPsdu> psdu = GetPsduTo(sta, *psduMap);
1036 NS_ASSERT(psdu);
1037 // If the QSRC[AC] or the QLRC[AC] has reached dot11ShortRetryLimit or dot11LongRetryLimit
1038 // respectively, CW[AC] shall be reset to CWmin[AC] (sec. 10.22.2.2 of 802.11-2016).
1039 // We should get that psduResetCw is the same for all PSDUs, but the handling of QSRC/QLRC
1040 // needs to be aligned to the specifications.
1041 bool psduResetCw;
1042 MissedBlockAck(psdu, m_txParams.m_txVector, psduResetCw);
1043 resetCw = resetCw || psduResetCw;
1044 }
1045
1046 NS_ASSERT(m_edca);
1047
1048 if (resetCw)
1049 {
1050 m_edca->ResetCw(m_linkId);
1051 }
1052 else
1053 {
1054 m_edca->UpdateFailedCw(m_linkId);
1055 }
1056
1057 if (staMissedBlockAckFrom->size() == nSolicitedStations)
1058 {
1059 // no station replied, the transmission failed
1060 TransmissionFailed();
1061 }
1062 else
1063 {
1064 TransmissionSucceeded();
1065 }
1066 m_psduMap.clear();
1067}
1068
1069void
1070HeFrameExchangeManager::BlockAckAfterTbPpduTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1071{
1072 NS_LOG_FUNCTION(this << *psdu << txVector);
1073
1074 bool resetCw;
1075
1076 // call ReportDataFailed to increase SRC/LRC
1077 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1078
1079 MissedBlockAck(psdu, m_txParams.m_txVector, resetCw);
1080
1081 // This is a PSDU sent in a TB PPDU. An HE STA resumes the EDCA backoff procedure
1082 // without modifying CW or the backoff counter for the associated EDCAF, after
1083 // transmission of an MPDU in a TB PPDU regardless of whether the STA has received
1084 // the corresponding acknowledgment frame in response to the MPDU sent in the TB PPDU
1085 // (Sec. 10.22.2.2 of 11ax Draft 3.0)
1086 m_psduMap.clear();
1087}
1088
1089void
1090HeFrameExchangeManager::NormalAckTimeout(Ptr<WifiMpdu> mpdu, const WifiTxVector& txVector)
1091{
1092 NS_LOG_FUNCTION(this << *mpdu << txVector);
1093
1094 VhtFrameExchangeManager::NormalAckTimeout(mpdu, txVector);
1095
1096 // If a Normal Ack is missed in response to a DL MU PPDU requiring acknowledgment
1097 // in SU format, we have to set the Retry flag for all transmitted MPDUs that have
1098 // not been acknowledged nor discarded and clear m_psduMap since the transmission failed.
1099 for (auto& psdu : m_psduMap)
1100 {
1101 for (auto& mpdu : *PeekPointer(psdu.second))
1102 {
1103 if (mpdu->IsQueued())
1104 {
1105 mpdu->GetHeader().SetRetry();
1106 }
1107 }
1108 }
1109 m_psduMap.clear();
1110}
1111
1112void
1113HeFrameExchangeManager::BlockAckTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1114{
1115 NS_LOG_FUNCTION(this << *psdu << txVector);
1116
1117 VhtFrameExchangeManager::BlockAckTimeout(psdu, txVector);
1118
1119 // If a Block Ack is missed in response to a DL MU PPDU requiring acknowledgment
1120 // in SU format, we have to set the Retry flag for all transmitted MPDUs that have
1121 // not been acknowledged nor discarded and clear m_psduMap since the transmission failed.
1122 for (auto& psdu : m_psduMap)
1123 {
1124 for (auto& mpdu : *PeekPointer(psdu.second))
1125 {
1126 if (mpdu->IsQueued())
1127 {
1128 mpdu->GetHeader().SetRetry();
1129 }
1130 }
1131 }
1132 m_psduMap.clear();
1133}
1134
1136HeFrameExchangeManager::GetTrigVector(const CtrlTriggerHeader& trigger) const
1137{
1138 WifiTxVector v;
1140 v.SetChannelWidth(trigger.GetUlBandwidth());
1142 v.SetLength(trigger.GetUlLength());
1143 for (const auto& userInfoField : trigger)
1144 {
1145 v.SetHeMuUserInfo(userInfoField.GetAid12(),
1146 {userInfoField.GetRuAllocation(),
1147 HePhy::GetHeMcs(userInfoField.GetUlMcs()),
1148 userInfoField.GetNss()});
1149 }
1150 return v;
1151}
1152
1154HeFrameExchangeManager::GetHeTbTxVector(CtrlTriggerHeader trigger, Mac48Address triggerSender) const
1155{
1156 NS_ASSERT(triggerSender !=
1157 m_self); // TxPower information is used only by STAs, it is useless for the sending AP
1158 // (which can directly use CtrlTriggerHeader::GetHeTbTxVector)
1159 NS_ASSERT(m_staMac);
1160 uint16_t staId = m_staMac->GetAssociationId();
1161 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
1162 NS_ASSERT(userInfoIt != trigger.end());
1163
1164 WifiTxVector v = trigger.GetHeTbTxVector(staId);
1165
1166 Ptr<HeConfiguration> heConfiguration = m_mac->GetHeConfiguration();
1167 NS_ASSERT_MSG(heConfiguration, "This STA has to be an HE station to send an HE TB PPDU");
1168 v.SetBssColor(heConfiguration->GetBssColor());
1169
1170 if (userInfoIt->IsUlTargetRssiMaxTxPower())
1171 {
1172 NS_LOG_LOGIC("AP requested using the max transmit power (" << m_phy->GetTxPowerEnd()
1173 << " dBm)");
1174 v.SetTxPowerLevel(m_phy->GetNTxPower());
1175 return v;
1176 }
1177
1178 uint8_t powerLevel = GetWifiRemoteStationManager()->GetDefaultTxPowerLevel();
1196 int8_t pathLossDb =
1197 trigger.GetApTxPower() -
1198 static_cast<int8_t>(GetWifiRemoteStationManager()->GetMostRecentRssi(
1199 triggerSender)); // cast RSSI to be on equal footing with AP Tx power information
1200 double reqTxPowerDbm = static_cast<double>(userInfoIt->GetUlTargetRssi() + pathLossDb);
1201
1202 // Convert the transmit power to a power level
1203 uint8_t numPowerLevels = m_phy->GetNTxPower();
1204 if (numPowerLevels > 1)
1205 {
1206 double stepDbm = (m_phy->GetTxPowerEnd() - m_phy->GetTxPowerStart()) / (numPowerLevels - 1);
1207 powerLevel = static_cast<uint8_t>(
1208 ceil((reqTxPowerDbm - m_phy->GetTxPowerStart()) /
1209 stepDbm)); // better be slightly above so as to satisfy target UL RSSI
1210 if (powerLevel > numPowerLevels)
1211 {
1212 powerLevel = numPowerLevels; // capping will trigger warning below
1213 }
1214 }
1215 if (reqTxPowerDbm > m_phy->GetPowerDbm(powerLevel))
1216 {
1217 NS_LOG_WARN("The requested power level ("
1218 << reqTxPowerDbm << "dBm) cannot be satisfied (max: " << m_phy->GetTxPowerEnd()
1219 << "dBm)");
1220 }
1221 v.SetTxPowerLevel(powerLevel);
1222 NS_LOG_LOGIC("UL power control: "
1223 << "input {pathLoss=" << pathLossDb << "dB, reqTxPower=" << reqTxPowerDbm << "dBm}"
1224 << " output {powerLevel=" << +powerLevel << " -> "
1225 << m_phy->GetPowerDbm(powerLevel) << "dBm}"
1226 << " PHY power capa {min=" << m_phy->GetTxPowerStart() << "dBm, max="
1227 << m_phy->GetTxPowerEnd() << "dBm, levels:" << +numPowerLevels << "}");
1228
1229 return v;
1230}
1231
1232void
1233HeFrameExchangeManager::SetTargetRssi(CtrlTriggerHeader& trigger) const
1234{
1235 NS_LOG_FUNCTION(this);
1236 NS_ASSERT(m_apMac);
1237
1238 trigger.SetApTxPower(static_cast<int8_t>(
1239 m_phy->GetPowerDbm(GetWifiRemoteStationManager()->GetDefaultTxPowerLevel())));
1240 for (auto& userInfo : trigger)
1241 {
1242 const auto staList = m_apMac->GetStaList();
1243 auto itAidAddr = staList.find(userInfo.GetAid12());
1244 NS_ASSERT(itAidAddr != staList.end());
1245 int8_t rssi = static_cast<int8_t>(
1246 GetWifiRemoteStationManager()->GetMostRecentRssi(itAidAddr->second));
1247 rssi = (rssi >= -20)
1248 ? -20
1249 : ((rssi <= -110) ? -110 : rssi); // cap so as to keep within [-110; -20] dBm
1250 userInfo.SetUlTargetRssi(rssi);
1251 }
1252}
1253
1254void
1255HeFrameExchangeManager::SendMultiStaBlockAck(const WifiTxParameters& txParams)
1256{
1257 NS_LOG_FUNCTION(this << &txParams);
1258
1259 NS_ASSERT(m_apMac);
1260 NS_ASSERT(txParams.m_acknowledgment &&
1261 txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1262 WifiUlMuMultiStaBa* acknowledgment =
1263 static_cast<WifiUlMuMultiStaBa*>(txParams.m_acknowledgment.get());
1264
1265 NS_ASSERT(!acknowledgment->stationsReceivingMultiStaBa.empty());
1266
1267 CtrlBAckResponseHeader blockAck;
1268 blockAck.SetType(acknowledgment->baType);
1269
1270 Mac48Address receiver;
1271
1272 for (const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa)
1273 {
1274 receiver = staInfo.first.first;
1275 uint8_t tid = staInfo.first.second;
1276 std::size_t index = staInfo.second;
1277
1278 blockAck.SetAid11(m_apMac->GetAssociationId(receiver, m_linkId), index);
1279 blockAck.SetTidInfo(tid, index);
1280
1281 if (tid == 14)
1282 {
1283 // All-ack context
1284 NS_LOG_DEBUG("Multi-STA Block Ack: Sending All-ack to=" << receiver);
1285 blockAck.SetAckType(true, index);
1286 continue;
1287 }
1288
1289 if (acknowledgment->baType.m_bitmapLen.at(index) == 0)
1290 {
1291 // Acknowledgment context
1292 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Ack to=" << receiver);
1293 blockAck.SetAckType(true, index);
1294 }
1295 else
1296 {
1297 // Block acknowledgment context
1298 blockAck.SetAckType(false, index);
1299
1300 auto addressTidPair = staInfo.first;
1301 auto agreementIt = m_agreements.find(addressTidPair);
1302 NS_ASSERT(agreementIt != m_agreements.end());
1303 agreementIt->second.FillBlockAckBitmap(&blockAck, index);
1304 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Block Ack with seq="
1305 << blockAck.GetStartingSequence(index) << " to=" << receiver
1306 << " tid=" << +tid);
1307 }
1308 }
1309
1310 WifiMacHeader hdr;
1312 hdr.SetAddr1(acknowledgment->stationsReceivingMultiStaBa.size() == 1
1313 ? receiver
1314 : Mac48Address::GetBroadcast());
1315 hdr.SetAddr2(m_self);
1316 hdr.SetDsNotFrom();
1317 hdr.SetDsNotTo();
1318
1319 Ptr<Packet> packet = Create<Packet>();
1320 packet->AddHeader(blockAck);
1321 Ptr<WifiPsdu> psdu =
1322 GetWifiPsdu(Create<WifiMpdu>(packet, hdr), acknowledgment->multiStaBaTxVector);
1323
1324 // The Duration/ID field in a BlockAck frame transmitted in response to a frame
1325 // carried in HE TB PPDU is set according to the multiple protection settings
1326 // (Sec. 9.2.5.7 of 802.11ax D3.0)
1327 Time txDuration = m_phy->CalculateTxDuration(GetBlockAckSize(acknowledgment->baType),
1328 acknowledgment->multiStaBaTxVector,
1329 m_phy->GetPhyBand());
1330 WifiTxParameters params;
1331 // if the TXOP limit is null, GetPsduDurationId returns the acknowledgment time,
1332 // hence we set an method with acknowledgment time equal to zero.
1333 params.m_acknowledgment = std::unique_ptr<WifiAcknowledgment>(new WifiNoAck);
1334 psdu->SetDuration(GetPsduDurationId(txDuration, params));
1335
1336 psdu->GetPayload(0)->AddPacketTag(m_muSnrTag);
1337
1338 ForwardPsduDown(psdu, acknowledgment->multiStaBaTxVector);
1339
1340 // continue with the TXOP if time remains
1341 m_psduMap.clear();
1342 m_edca->ResetCw(m_linkId);
1343 m_muSnrTag.Reset();
1344 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded, this);
1345}
1346
1347void
1348HeFrameExchangeManager::ReceiveBasicTrigger(const CtrlTriggerHeader& trigger,
1349 const WifiMacHeader& hdr)
1350{
1351 NS_LOG_FUNCTION(this << trigger << hdr);
1352 NS_ASSERT(trigger.IsBasic());
1353 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1354
1355 NS_LOG_DEBUG("Received a Trigger Frame (basic variant) soliciting a transmission");
1356
1357 if (trigger.GetCsRequired() && hdr.GetAddr2() != m_txopHolder && m_navEnd > Simulator::Now())
1358 {
1359 NS_LOG_DEBUG("Carrier Sensing required and channel busy, do nothing");
1360 return;
1361 }
1362
1363 // Starting from the Preferred AC indicated in the Trigger Frame, check if there
1364 // is either a pending BlockAckReq frame or a data frame that can be transmitted
1365 // in the allocated time and is addressed to a station with which a Block Ack
1366 // agreement has been established.
1367
1368 // create the sequence of TIDs to check
1369 std::vector<uint8_t> tids;
1370 uint16_t staId = m_staMac->GetAssociationId();
1371 AcIndex preferredAc = trigger.FindUserInfoWithAid(staId)->GetPreferredAc();
1372 auto acIt = wifiAcList.find(preferredAc);
1373 for (uint8_t i = 0; i < 4; i++)
1374 {
1375 NS_ASSERT(acIt != wifiAcList.end());
1376 tids.push_back(acIt->second.GetHighTid());
1377 tids.push_back(acIt->second.GetLowTid());
1378
1379 acIt++;
1380 if (acIt == wifiAcList.end())
1381 {
1382 acIt = wifiAcList.begin();
1383 }
1384 }
1385
1386 Ptr<WifiPsdu> psdu;
1387 WifiTxParameters txParams;
1388 WifiTxVector tbTxVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1389 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
1390 tbTxVector,
1391 m_phy->GetPhyBand());
1392
1393 for (const auto& tid : tids)
1394 {
1395 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1396
1397 if (!edca->GetBaAgreementEstablished(hdr.GetAddr2(), tid))
1398 {
1399 // no Block Ack agreement established for this TID
1400 continue;
1401 }
1402
1403 txParams.Clear();
1404 txParams.m_txVector = tbTxVector;
1405
1406 // first, check if there is a pending BlockAckReq frame
1407 if (Ptr<const WifiMpdu> mpdu;
1408 (mpdu = edca->GetBaManager()->GetBar(false, tid, hdr.GetAddr2())) &&
1409 TryAddMpdu(mpdu, txParams, ppduDuration))
1410 {
1411 NS_LOG_DEBUG("Sending a BAR within a TB PPDU");
1412 psdu = Create<WifiPsdu>(edca->GetBaManager()->GetBar(true, tid, hdr.GetAddr2()), true);
1413 break;
1414 }
1415
1416 // otherwise, check if a suitable data frame is available
1417 if (Ptr<WifiMpdu> mpdu; (mpdu = edca->PeekNextMpdu(m_linkId, tid, hdr.GetAddr2())))
1418 {
1419 Ptr<WifiMpdu> item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration, false);
1420
1421 if (item)
1422 {
1423 // try A-MPDU aggregation
1424 std::vector<Ptr<WifiMpdu>> mpduList =
1425 m_mpduAggregator->GetNextAmpdu(item, txParams, ppduDuration);
1426 psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1427 : Create<WifiPsdu>(item, true));
1428 break;
1429 }
1430 }
1431 }
1432
1433 if (psdu)
1434 {
1435 psdu->SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1436 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1437 }
1438 else
1439 {
1440 // send QoS Null frames
1441 SendQosNullFramesInTbPpdu(trigger, hdr);
1442 }
1443}
1444
1445void
1446HeFrameExchangeManager::SendQosNullFramesInTbPpdu(const CtrlTriggerHeader& trigger,
1447 const WifiMacHeader& hdr)
1448{
1449 NS_LOG_FUNCTION(this << trigger << hdr);
1450 NS_ASSERT(trigger.IsBasic() || trigger.IsBsrp());
1451 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1452
1453 NS_LOG_DEBUG("Requested to send QoS Null frames");
1454
1455 if (trigger.GetCsRequired() && hdr.GetAddr2() != m_txopHolder && m_navEnd > Simulator::Now())
1456 {
1457 NS_LOG_DEBUG("Carrier Sensing required and channel busy (TA="
1458 << hdr.GetAddr2() << ", TxopHolder=" << m_txopHolder
1459 << ", NAV end=" << m_navEnd.As(Time::S) << "), do nothing");
1460 return;
1461 }
1462
1463 WifiMacHeader header;
1465 header.SetAddr1(hdr.GetAddr2());
1466 header.SetAddr2(m_self);
1467 header.SetAddr3(hdr.GetAddr2());
1468 header.SetDsTo();
1469 header.SetDsNotFrom();
1470 // TR3: Sequence numbers for transmitted QoS (+)Null frames may be set
1471 // to any value. (Table 10-3 of 802.11-2016)
1472 header.SetSequenceNumber(0);
1473 // Set the EOSP bit so that NotifyTxToEdca will add the Queue Size
1474 header.SetQosEosp();
1475
1476 WifiTxParameters txParams;
1477 txParams.m_txVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1478 txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
1479 txParams.m_acknowledgment = std::unique_ptr<WifiAcknowledgment>(new WifiNoAck);
1480
1481 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
1482 txParams.m_txVector,
1483 m_phy->GetPhyBand());
1484 header.SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1485
1486 Ptr<WifiMpdu> mpdu;
1487 std::vector<Ptr<WifiMpdu>> mpduList;
1488 uint8_t tid = 0;
1489 header.SetQosTid(tid);
1490
1491 while (tid < 8 &&
1492 IsWithinSizeAndTimeLimits(
1493 txParams.GetSizeIfAddMpdu(mpdu = Create<WifiMpdu>(Create<Packet>(), header)),
1494 hdr.GetAddr2(),
1495 txParams,
1496 ppduDuration))
1497 {
1498 if (!m_mac->GetQosTxop(tid)->GetBaAgreementEstablished(hdr.GetAddr2(), tid))
1499 {
1500 NS_LOG_DEBUG("Skipping tid=" << +tid << " because no agreement established");
1501 header.SetQosTid(++tid);
1502 continue;
1503 }
1504
1505 NS_LOG_DEBUG("Aggregating a QoS Null frame with tid=" << +tid);
1506 // We could call TryAddMpdu instead of IsWithinSizeAndTimeLimits above in order to
1507 // get the TX parameters updated automatically. However, aggregating the QoS Null
1508 // frames might fail because MPDU aggregation is disabled by default for VO
1509 // and BK. Therefore, we skip the check on max A-MPDU size and only update the
1510 // TX parameters below.
1511 txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
1512 txParams.AddMpdu(mpdu);
1513 UpdateTxDuration(mpdu->GetHeader().GetAddr1(), txParams);
1514 mpduList.push_back(mpdu);
1515 header.SetQosTid(++tid);
1516 }
1517
1518 if (mpduList.empty())
1519 {
1520 NS_LOG_DEBUG("Not enough time to send a QoS Null frame");
1521 return;
1522 }
1523
1524 Ptr<WifiPsdu> psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1525 : Create<WifiPsdu>(mpduList.front(), true));
1526 uint16_t staId = m_staMac->GetAssociationId();
1527 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1528}
1529
1530void
1531HeFrameExchangeManager::SetTxopHolder(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
1532{
1533 NS_LOG_FUNCTION(this << psdu << txVector);
1534
1535 if (psdu->GetHeader(0).IsTrigger())
1536 {
1537 m_txopHolder = psdu->GetAddr2();
1538 }
1539 else if (!txVector.IsUlMu()) // the sender of a TB PPDU is not the TXOP holder
1540 {
1541 VhtFrameExchangeManager::SetTxopHolder(psdu, txVector);
1542 }
1543}
1544
1545void
1546HeFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
1547 RxSignalInfo rxSignalInfo,
1548 const WifiTxVector& txVector,
1549 bool inAmpdu)
1550{
1551 // The received MPDU is either broadcast or addressed to this station
1552 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1553
1554 const WifiMacHeader& hdr = mpdu->GetHeader();
1555
1556 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
1557 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
1558 {
1559 Mac48Address sender = hdr.GetAddr2();
1560 NS_ASSERT(m_txParams.m_acknowledgment &&
1561 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1562 WifiUlMuMultiStaBa* acknowledgment =
1563 static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
1564 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
1565
1566 if (m_staExpectTbPpduFrom.find(sender) == m_staExpectTbPpduFrom.end())
1567 {
1568 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
1569 return;
1570 }
1571
1572 if (hdr.IsBlockAckReq())
1573 {
1574 NS_LOG_DEBUG("Received a BlockAckReq in a TB PPDU from " << sender);
1575
1576 CtrlBAckRequestHeader blockAckReq;
1577 mpdu->GetPacket()->PeekHeader(blockAckReq);
1578 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1579 uint8_t tid = blockAckReq.GetTidInfo();
1580 auto agreementIt = m_agreements.find({sender, tid});
1581 NS_ASSERT(agreementIt != m_agreements.end());
1582 agreementIt->second.NotifyReceivedBar(blockAckReq.GetStartingSequence());
1583
1584 // Block Acknowledgment context
1585 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
1586 acknowledgment->baType.m_bitmapLen.push_back(
1587 GetBlockAckType(sender, tid).m_bitmapLen.at(0));
1588 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
1589 m_muSnrTag.Set(staId, rxSignalInfo.snr);
1590 }
1591 else if (hdr.IsQosData() && !inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
1592 {
1593 NS_LOG_DEBUG("Received an S-MPDU in a TB PPDU from " << sender << " (" << *mpdu << ")");
1594
1595 uint8_t tid = hdr.GetQosTid();
1596 auto agreementIt = m_agreements.find({sender, tid});
1597 NS_ASSERT(agreementIt != m_agreements.end());
1598 agreementIt->second.NotifyReceivedMpdu(mpdu);
1599
1600 // Acknowledgment context of Multi-STA Block Acks
1601 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
1602 acknowledgment->baType.m_bitmapLen.push_back(0);
1603 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
1604 m_muSnrTag.Set(staId, rxSignalInfo.snr);
1605 }
1606 else if (!(hdr.IsQosData() && !hdr.HasData() && !inAmpdu))
1607 {
1608 // The other case handled by this function is when we receive a QoS Null frame
1609 // that is not in an A-MPDU. For all other cases, the reception is handled by
1610 // parent classes. In particular, in case of a QoS data frame in A-MPDU, we
1611 // have to wait until the A-MPDU reception is completed, but we let the
1612 // parent classes notify the Block Ack agreement of the reception of this MPDU
1613 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1614 return;
1615 }
1616
1617 // Schedule the transmission of a Multi-STA BlockAck frame if needed
1618 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsRunning())
1619 {
1620 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
1621 &HeFrameExchangeManager::SendMultiStaBlockAck,
1622 this,
1623 std::cref(m_txParams));
1624 }
1625
1626 // remove the sender from the set of stations that are expected to send a TB PPDU
1627 m_staExpectTbPpduFrom.erase(sender);
1628
1629 if (m_staExpectTbPpduFrom.empty())
1630 {
1631 // we do not expect any other BlockAck frame
1632 m_txTimer.Cancel();
1633 m_channelAccessManager->NotifyAckTimeoutResetNow();
1634
1635 if (!m_multiStaBaEvent.IsRunning())
1636 {
1637 // all of the stations that replied with a TB PPDU sent QoS Null frames.
1638 NS_LOG_DEBUG("Continue the TXOP");
1639 m_psduMap.clear();
1640 m_edca->ResetCw(m_linkId);
1641 TransmissionSucceeded();
1642 }
1643 }
1644
1645 // the received TB PPDU has been processed
1646 return;
1647 }
1648
1649 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
1650 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF &&
1651 !inAmpdu) // if in A-MPDU, processing is done at the end of A-MPDU reception
1652 {
1653 Mac48Address sender = hdr.GetAddr2();
1654
1655 if (m_staExpectTbPpduFrom.find(sender) == m_staExpectTbPpduFrom.end())
1656 {
1657 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
1658 return;
1659 }
1660 if (!(hdr.IsQosData() && !hdr.HasData()))
1661 {
1662 NS_LOG_WARN("No QoS Null frame in the received MPDU");
1663 return;
1664 }
1665
1666 NS_LOG_DEBUG("Received a QoS Null frame in a TB PPDU from " << sender);
1667
1668 // remove the sender from the set of stations that are expected to send a TB PPDU
1669 m_staExpectTbPpduFrom.erase(sender);
1670
1671 if (m_staExpectTbPpduFrom.empty())
1672 {
1673 // we do not expect any other response
1674 m_txTimer.Cancel();
1675 m_channelAccessManager->NotifyAckTimeoutResetNow();
1676
1677 NS_ASSERT(m_edca);
1678 m_psduMap.clear();
1679 m_edca->ResetCw(m_linkId);
1680 TransmissionSucceeded();
1681 }
1682
1683 // the received TB PPDU has been processed
1684 return;
1685 }
1686
1687 if (hdr.IsCtl())
1688 {
1689 if (hdr.IsCts() && m_txTimer.IsRunning() &&
1690 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psduMap.size() == 1)
1691 {
1692 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
1693 NS_ASSERT(hdr.GetAddr1() == m_self);
1694
1695 Mac48Address sender = m_psduMap.begin()->second->GetAddr1();
1696 NS_LOG_DEBUG("Received CTS from=" << sender);
1697
1698 SnrTag tag;
1699 mpdu->GetPacket()->PeekPacketTag(tag);
1700 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1701 GetWifiRemoteStationManager()->ReportRtsOk(m_psduMap.begin()->second->GetHeader(0),
1702 rxSignalInfo.snr,
1703 txVector.GetMode(),
1704 tag.Get());
1705
1706 m_txTimer.Cancel();
1707 m_channelAccessManager->NotifyCtsTimeoutResetNow();
1708 Simulator::Schedule(m_phy->GetSifs(), &HeFrameExchangeManager::SendPsduMap, this);
1709 }
1710 else if (hdr.IsAck() && m_txTimer.IsRunning() &&
1711 m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
1712 {
1713 NS_ASSERT(hdr.GetAddr1() == m_self);
1714 NS_ASSERT(m_txParams.m_acknowledgment);
1715 NS_ASSERT(m_txParams.m_acknowledgment->method ==
1716 WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE);
1717
1718 WifiDlMuBarBaSequence* acknowledgment =
1719 static_cast<WifiDlMuBarBaSequence*>(m_txParams.m_acknowledgment.get());
1720 NS_ASSERT(acknowledgment->stationsReplyingWithNormalAck.size() == 1);
1721 NS_ASSERT(m_apMac);
1722 uint16_t staId = m_apMac->GetAssociationId(
1723 acknowledgment->stationsReplyingWithNormalAck.begin()->first,
1724 m_linkId);
1725 auto it = m_psduMap.find(staId);
1726 NS_ASSERT(it != m_psduMap.end());
1727 NS_ASSERT(it->second->GetAddr1() ==
1728 acknowledgment->stationsReplyingWithNormalAck.begin()->first);
1729 SnrTag tag;
1730 mpdu->GetPacket()->PeekPacketTag(tag);
1731 ReceivedNormalAck(*it->second->begin(),
1732 m_txParams.m_txVector,
1733 txVector,
1734 rxSignalInfo,
1735 tag.Get());
1736 m_psduMap.clear();
1737 }
1738 // TODO the PHY should not pass us a non-TB PPDU if we are waiting for a
1739 // TB PPDU. However, processing the PHY header is done by the PHY entity
1740 // corresponding to the modulation class of the PPDU being received, hence
1741 // it is not possible to check if a valid TRIGVECTOR is stored when receiving
1742 // PPDUs of older modulation classes. Therefore, we check here that we are
1743 // actually receiving a TB PPDU.
1744 else if (hdr.IsBlockAck() && txVector.IsUlMu() && m_txTimer.IsRunning() &&
1745 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU)
1746 {
1747 Mac48Address sender = hdr.GetAddr2();
1748 NS_LOG_DEBUG("Received BlockAck in TB PPDU from=" << sender);
1749
1750 SnrTag tag;
1751 mpdu->GetPacket()->PeekPacketTag(tag);
1752
1753 // notify the Block Ack Manager
1754 CtrlBAckResponseHeader blockAck;
1755 mpdu->GetPacket()->PeekHeader(blockAck);
1756 uint8_t tid = blockAck.GetTidInfo();
1757 std::pair<uint16_t, uint16_t> ret =
1758 GetBaManager(tid)->NotifyGotBlockAck(blockAck, hdr.GetAddr2(), {tid});
1759 GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
1760 ret.first,
1761 ret.second,
1762 rxSignalInfo.snr,
1763 tag.Get(),
1764 m_txParams.m_txVector);
1765
1766 // remove the sender from the set of stations that are expected to send a BlockAck
1767 if (m_staExpectTbPpduFrom.erase(sender) == 0)
1768 {
1769 NS_LOG_WARN("Received a BlockAck from an unexpected stations: " << sender);
1770 return;
1771 }
1772
1773 if (m_staExpectTbPpduFrom.empty())
1774 {
1775 // we do not expect any other BlockAck frame
1776 m_txTimer.Cancel();
1777 m_channelAccessManager->NotifyAckTimeoutResetNow();
1778 m_triggerFrame = nullptr; // this is strictly needed for DL_MU_TF_MU_BAR only
1779
1780 m_edca->ResetCw(m_linkId);
1781 m_psduMap.clear();
1782 TransmissionSucceeded();
1783 }
1784 }
1785 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1786 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
1787 {
1788 CtrlBAckResponseHeader blockAck;
1789 mpdu->GetPacket()->PeekHeader(blockAck);
1790
1791 NS_ABORT_MSG_IF(!blockAck.IsMultiSta(),
1792 "A Multi-STA BlockAck is expected after a TB PPDU");
1793 NS_LOG_DEBUG("Received a Multi-STA BlockAck from=" << hdr.GetAddr2());
1794
1795 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1796 if (hdr.GetAddr2() != m_bssid)
1797 {
1798 NS_LOG_DEBUG("The sender is not the AP we are associated with");
1799 return;
1800 }
1801
1802 uint16_t staId = m_staMac->GetAssociationId();
1803 std::vector<uint32_t> indices = blockAck.FindPerAidTidInfoWithAid(staId);
1804
1805 if (indices.empty())
1806 {
1807 NS_LOG_DEBUG("No Per AID TID Info subfield intended for me");
1808 return;
1809 }
1810
1811 MuSnrTag tag;
1812 mpdu->GetPacket()->PeekPacketTag(tag);
1813
1814 // notify the Block Ack Manager
1815 for (const auto& index : indices)
1816 {
1817 uint8_t tid = blockAck.GetTidInfo(index);
1818
1819 if (blockAck.GetAckType(index) && tid < 8)
1820 {
1821 // Acknowledgment context
1822 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
1823 GetBaManager(tid)->NotifyGotAck(*m_psduMap.at(staId)->begin());
1824 }
1825 else
1826 {
1827 // Block Acknowledgment or All-ack context
1828 if (blockAck.GetAckType(index) && tid == 14)
1829 {
1830 // All-ack context, we need to determine the actual TID(s) of the PSDU
1831 NS_ASSERT(indices.size() == 1);
1832 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
1833 std::set<uint8_t> tids = m_psduMap.at(staId)->GetTids();
1834 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not supported yet");
1835 tid = *tids.begin();
1836 }
1837
1838 std::pair<uint16_t, uint16_t> ret =
1839 GetBaManager(tid)->NotifyGotBlockAck(blockAck,
1840 hdr.GetAddr2(),
1841 {tid},
1842 index);
1843 GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
1844 ret.first,
1845 ret.second,
1846 rxSignalInfo.snr,
1847 tag.Get(staId),
1848 m_txParams.m_txVector);
1849 }
1850
1851 if (m_psduMap.at(staId)->GetHeader(0).IsQosData() &&
1852 (blockAck.GetAckType(index) // Ack or All-ack context
1853 || std::any_of(blockAck.GetBitmap(index).begin(),
1854 blockAck.GetBitmap(index).end(),
1855 [](uint8_t b) { return b != 0; })))
1856 {
1857 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).HasData());
1858 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).GetQosTid() == tid);
1859 // the station has received a response from the AP for the HE TB PPDU
1860 // transmitted in response to a Basic Trigger Frame and at least one
1861 // MPDU was acknowledged. Therefore, it needs to update the access
1862 // parameters if it received an MU EDCA Parameter Set element.
1863 m_mac->GetQosTxop(tid)->StartMuEdcaTimerNow(m_linkId);
1864 }
1865 }
1866
1867 // cancel the timer
1868 m_txTimer.Cancel();
1869 m_channelAccessManager->NotifyAckTimeoutResetNow();
1870 m_psduMap.clear();
1871 }
1872 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1873 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK)
1874 {
1875 // this BlockAck frame may have been sent in response to a DL MU PPDU with
1876 // acknowledgment in SU format or one of the consequent BlockAckReq frames.
1877 // We clear the PSDU map and let parent classes continue processing this frame.
1878 m_psduMap.clear();
1879 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1880 }
1881 else if (hdr.IsTrigger())
1882 {
1883 // Trigger Frames are only processed by STAs
1884 if (!m_staMac)
1885 {
1886 return;
1887 }
1888
1889 // A Trigger Frame in an A-MPDU is processed when the A-MPDU is fully received
1890 if (inAmpdu)
1891 {
1892 m_triggerFrameInAmpdu = true;
1893 return;
1894 }
1895
1896 CtrlTriggerHeader trigger;
1897 mpdu->GetPacket()->PeekHeader(trigger);
1898
1899 if (hdr.GetAddr1() != m_self &&
1900 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
1901 hdr.GetAddr2() != m_bssid // not sent by the AP this STA is associated with
1902 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
1903 {
1904 // not addressed to us
1905 return;
1906 }
1907
1908 uint16_t staId = m_staMac->GetAssociationId();
1909
1910 if (trigger.IsMuBar())
1911 {
1912 Mac48Address sender = hdr.GetAddr2();
1913 NS_LOG_DEBUG("Received MU-BAR Trigger Frame from=" << sender);
1914 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1915
1916 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
1917 NS_ASSERT(userInfoIt != trigger.end());
1918 CtrlBAckRequestHeader blockAckReq = userInfoIt->GetMuBarTriggerDepUserInfo();
1919 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1920 uint8_t tid = blockAckReq.GetTidInfo();
1921
1922 auto agreementIt = m_agreements.find({sender, tid});
1923
1924 if (agreementIt == m_agreements.end())
1925 {
1926 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1927 return;
1928 }
1929
1930 agreementIt->second.NotifyReceivedBar(blockAckReq.GetStartingSequence());
1931
1932 NS_LOG_DEBUG("Schedule Block Ack in TB PPDU");
1933 Simulator::Schedule(m_phy->GetSifs(),
1934 &HeFrameExchangeManager::SendBlockAck,
1935 this,
1936 agreementIt->second,
1937 hdr.GetDuration(),
1938 GetHeTbTxVector(trigger, hdr.GetAddr2()),
1939 rxSignalInfo.snr);
1940 }
1941 else if (trigger.IsBasic())
1942 {
1943 Simulator::Schedule(m_phy->GetSifs(),
1944 &HeFrameExchangeManager::ReceiveBasicTrigger,
1945 this,
1946 trigger,
1947 hdr);
1948 }
1949 else if (trigger.IsBsrp())
1950 {
1951 Simulator::Schedule(m_phy->GetSifs(),
1952 &HeFrameExchangeManager::SendQosNullFramesInTbPpdu,
1953 this,
1954 trigger,
1955 hdr);
1956 }
1957 }
1958 else
1959 {
1960 // the received control frame cannot be handled here
1961 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1962 }
1963
1964 // the received control frame has been processed
1965 return;
1966 }
1967
1968 // the received frame cannot be handled here
1969 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1970 ;
1971}
1972
1973void
1974HeFrameExchangeManager::EndReceiveAmpdu(Ptr<const WifiPsdu> psdu,
1975 const RxSignalInfo& rxSignalInfo,
1976 const WifiTxVector& txVector,
1977 const std::vector<bool>& perMpduStatus)
1978{
1979 std::set<uint8_t> tids = psdu->GetTids();
1980
1981 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
1982 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
1983 {
1984 Mac48Address sender = psdu->GetAddr2();
1985 NS_ASSERT(m_txParams.m_acknowledgment &&
1986 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1987 WifiUlMuMultiStaBa* acknowledgment =
1988 static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
1989 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
1990
1991 if (m_staExpectTbPpduFrom.find(sender) == m_staExpectTbPpduFrom.end())
1992 {
1993 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
1994 return;
1995 }
1996
1997 NS_LOG_DEBUG("Received an A-MPDU in a TB PPDU from " << sender << " (" << *psdu << ")");
1998
1999 if (std::any_of(tids.begin(), tids.end(), [&psdu](uint8_t tid) {
2000 return psdu->GetAckPolicyForTid(tid) == WifiMacHeader::NORMAL_ACK;
2001 }))
2002 {
2003 if (std::all_of(perMpduStatus.cbegin(), perMpduStatus.cend(), [](bool v) { return v; }))
2004 {
2005 // All-ack context
2006 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, 14),
2007 index);
2008 acknowledgment->baType.m_bitmapLen.push_back(0);
2009 }
2010 else
2011 {
2012 // Block Acknowledgment context
2013 std::size_t i = 0;
2014 for (const auto& tid : tids)
2015 {
2016 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid),
2017 index + i++);
2018 acknowledgment->baType.m_bitmapLen.push_back(
2019 GetBlockAckType(sender, tid).m_bitmapLen.at(0));
2020 }
2021 }
2022 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2023 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2024 }
2025
2026 // Schedule the transmission of a Multi-STA BlockAck frame if needed
2027 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsRunning())
2028 {
2029 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2030 &HeFrameExchangeManager::SendMultiStaBlockAck,
2031 this,
2032 std::cref(m_txParams));
2033 }
2034
2035 // remove the sender from the set of stations that are expected to send a TB PPDU
2036 m_staExpectTbPpduFrom.erase(sender);
2037
2038 if (m_staExpectTbPpduFrom.empty())
2039 {
2040 // we do not expect any other BlockAck frame
2041 m_txTimer.Cancel();
2042 m_channelAccessManager->NotifyAckTimeoutResetNow();
2043
2044 if (!m_multiStaBaEvent.IsRunning())
2045 {
2046 // all of the stations that replied with a TB PPDU sent QoS Null frames.
2047 NS_LOG_DEBUG("Continue the TXOP");
2048 m_psduMap.clear();
2049 m_edca->ResetCw(m_linkId);
2050 TransmissionSucceeded();
2051 }
2052 }
2053
2054 // the received TB PPDU has been processed
2055 return;
2056 }
2057
2058 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2059 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
2060 {
2061 Mac48Address sender = psdu->GetAddr2();
2062
2063 if (m_staExpectTbPpduFrom.find(sender) == m_staExpectTbPpduFrom.end())
2064 {
2065 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2066 return;
2067 }
2068 if (std::none_of(psdu->begin(), psdu->end(), [](Ptr<WifiMpdu> mpdu) {
2069 return mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().HasData();
2070 }))
2071 {
2072 NS_LOG_WARN("No QoS Null frame in the received PSDU");
2073 return;
2074 }
2075
2076 NS_LOG_DEBUG("Received QoS Null frames in a TB PPDU from " << sender);
2077
2078 // remove the sender from the set of stations that are expected to send a TB PPDU
2079 m_staExpectTbPpduFrom.erase(sender);
2080
2081 if (m_staExpectTbPpduFrom.empty())
2082 {
2083 // we do not expect any other response
2084 m_txTimer.Cancel();
2085 m_channelAccessManager->NotifyAckTimeoutResetNow();
2086
2087 NS_ASSERT(m_edca);
2088 m_psduMap.clear();
2089 m_edca->ResetCw(m_linkId);
2090 TransmissionSucceeded();
2091 }
2092
2093 // the received TB PPDU has been processed
2094 return;
2095 }
2096
2097 if (m_triggerFrameInAmpdu)
2098 {
2099 // the received A-MPDU contains a Trigger Frame. It is now time to handle it.
2100 auto psduIt = psdu->begin();
2101 while (psduIt != psdu->end())
2102 {
2103 if ((*psduIt)->GetHeader().IsTrigger())
2104 {
2105 ReceiveMpdu(*psduIt, rxSignalInfo, txVector, false);
2106 }
2107 psduIt++;
2108 }
2109
2110 m_triggerFrameInAmpdu = false;
2111 return;
2112 }
2113
2114 // the received frame cannot be handled here
2115 VhtFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
2116}
2117
2118} // namespace ns3
#define max(a, b)
Definition: 80211b.c:43
uint16_t GetAssociationId(Mac48Address addr, uint8_t linkId) const
void NotifyAckTimeoutStartNow(Time duration)
Notify that ack timer has started for the given duration.
Headers for BlockAckRequest.
Definition: ctrl-headers.h:51
uint16_t GetStartingSequence() const
Return the starting sequence number.
uint8_t GetTidInfo() const
Return the Traffic ID (TID).
bool IsMultiTid() const
Check if the current Ack Policy has Multi-TID Block Ack.
Headers for BlockAck response.
Definition: ctrl-headers.h:202
std::vector< uint32_t > FindPerAidTidInfoWithAid(uint16_t aid) const
For Multi-STA Block Acks, get the indices of the Per AID TID Info subfields carrying the given AID in...
uint16_t GetStartingSequence(std::size_t index=0) const
For Block Ack variants other than Multi-STA Block Ack, get the starting sequence number.
uint8_t GetTidInfo(std::size_t index=0) const
For Block Ack variants other than Multi-STA Block Ack, get the TID_INFO subfield of the BA Control fi...
const std::vector< uint8_t > & GetBitmap(std::size_t index=0) const
Return a const reference to the bitmap from the BlockAck response header.
void SetAckType(bool type, std::size_t index)
For Multi-STA Block Acks, set the Ack Type subfield of the Per AID TID Info subfield identified by th...
void SetTidInfo(uint8_t tid, std::size_t index=0)
For Block Ack variants other than Multi-STA Block Ack, set the TID_INFO subfield of the BA Control fi...
void SetType(BlockAckType type)
Set the block ack type.
void SetAid11(uint16_t aid, std::size_t index)
For Multi-STA Block Acks, set the AID11 subfield of the Per AID TID Info subfield identified by the g...
bool GetAckType(std::size_t index) const
For Multi-STA Block Acks, get the Ack Type subfield of the Per AID TID Info subfield identified by th...
bool IsMultiSta() const
Check if the BlockAck frame variant is Multi-STA Block Ack.
Headers for Trigger frames.
Definition: ctrl-headers.h:886
bool IsBasic() const
Check if this is a Basic Trigger frame.
void SetApTxPower(int8_t power)
Set the AP TX Power subfield of the Common Info field.
ConstIterator end() const
Get a const iterator indicating past-the-last User Info field in the list.
WifiTxVector GetHeTbTxVector(uint16_t staId) const
Get the TX vector that the station with the given STA-ID will use to send the HE TB PPDU solicited by...
uint16_t GetGuardInterval() const
Get the guard interval duration (in nanoseconds) of the solicited HE TB PPDU.
bool IsBsrp() const
Check if this is a Buffer Status Report Poll Trigger frame.
bool IsMuBar() const
Check if this is a MU-BAR Trigger frame.
std::size_t GetNUserInfoFields() const
Get the number of User Info fields in this Trigger Frame.
ConstIterator FindUserInfoWithAid(ConstIterator start, uint16_t aid12) const
Get a const iterator pointing to the first User Info field found (starting from the one pointed to by...
void SetCsRequired(bool cs)
Set the CS Required subfield of the Common Info field.
uint16_t GetUlLength() const
Get the UL Length subfield of the Common Info field.
uint16_t GetUlBandwidth() const
Get the bandwidth of the solicited HE TB PPDU.
bool GetCsRequired() const
Get the CS Required subfield of the Common Info field.
int8_t GetApTxPower() const
Get the power value (dBm) indicated by the AP TX Power subfield of the Common Info field.
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:55
void DoCtsTimeout(Ptr< WifiPsdu > psdu)
Take required actions when the CTS timer fired after sending an RTS to protect the given PSDU expires...
uint8_t m_linkId
the ID of the link this object is associated with
Ptr< WifiMac > m_mac
the MAC layer on this station
uint16_t m_allowedWidth
the allowed width in MHz for the current transmission
WifiTxTimer m_txTimer
the timer set upon frame transmission
void SendRts(const WifiTxParameters &txParams)
Send RTS to begin RTS-CTS-Data-Ack transaction.
Ptr< WifiPhy > m_phy
the PHY layer on this station
Ptr< ChannelAccessManager > m_channelAccessManager
the channel access manager
Ptr< ApWifiMac > m_apMac
MAC pointer (null if not an AP)
bool SendMpduFromBaManager(Ptr< QosTxop > edca, Time availableTime, bool initialFrame) override
If the Block Ack Manager associated with the given EDCA has a BlockAckReq frame to transmit (the dura...
void DoDispose() override
Destructor implementation.
Ptr< WifiMpdu > PrepareMuBar(const WifiTxVector &responseTxVector, std::map< uint16_t, CtrlBAckRequestHeader > recipients) const
Build a MU-BAR Trigger Frame starting from the TXVECTOR used to respond to the MU-BAR (in case of mul...
WifiTxParameters m_txParams
the TX parameters for the current PPDU
void BlockAckTimeout(Ptr< WifiPsdu > psdu, const WifiTxVector &txVector) override
Called when the BlockAck timeout expires.
uint16_t GetSupportedBaBufferSize() const override
Get the maximum supported buffer size for a Block Ack agreement.
void CalculateAcknowledgmentTime(WifiAcknowledgment *acknowledgment) const override
Calculate the time required to acknowledge a frame according to the given acknowledgment method.
Ptr< WifiMpdu > m_triggerFrame
Trigger Frame being sent.
void SetMultiUserScheduler(const Ptr< MultiUserScheduler > muScheduler)
Set the Multi-user Scheduler associated with this Frame Exchange Manager.
virtual void TbPpduTimeout(WifiPsduMap *psduMap, const std::set< Mac48Address > *staMissedTbPpduFrom, std::size_t nSolicitedStations)
Take the necessary actions after that some TB PPDUs are missing in response to Trigger Frame.
void SetWifiMac(const Ptr< WifiMac > mac) override
Set the MAC layer to use.
Ptr< MultiUserScheduler > m_muScheduler
Multi-user Scheduler (HE APs only)
WifiTxVector m_trigVector
the TRIGVECTOR
static Ptr< WifiPsdu > GetPsduTo(Mac48Address to, const WifiPsduMap &psduMap)
Get the PSDU in the given PSDU map that is addressed to the given MAC address, if any,...
Ptr< StaWifiMac > m_staMac
MAC pointer (null if not a STA)
void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector) override
Called when the CTS timeout expires.
bool StartFrameExchange(Ptr< QosTxop > edca, Time availableTime, bool initialFrame) override
Start a frame exchange (including protection frames and acknowledgment frames as needed) that fits wi...
std::set< Mac48Address > m_staExpectTbPpduFrom
set of stations expected to send a TB PPDU
void NormalAckTimeout(Ptr< WifiMpdu > mpdu, const WifiTxVector &txVector) override
Called when the Ack timeout expires.
virtual void BlockAckAfterTbPpduTimeout(Ptr< WifiPsdu > psdu, const WifiTxVector &txVector)
Take the necessary actions after that a Block Ack is missing after a TB PPDU solicited through a Trig...
void SendPsduMap()
Send the current PSDU map as a DL MU PPDU.
WifiPsduMap m_psduMap
the A-MPDU being transmitted
static TypeId GetTypeId()
Get the type ID.
virtual void BlockAcksInTbPpduTimeout(WifiPsduMap *psduMap, const std::set< Mac48Address > *staMissedBlockAckFrom, std::size_t nSolicitedStations)
Take the necessary actions after that some BlockAck frames are missing in response to a DL MU PPDU.
EventId m_multiStaBaEvent
Sending a Multi-STA BlockAck event.
void SendPsduMapWithProtection(WifiPsduMap psduMap, WifiTxParameters &txParams)
Send a map of PSDUs as a DL MU PPDU.
static Time ConvertLSigLengthToHeTbPpduDuration(uint16_t length, const WifiTxVector &txVector, WifiPhyBand band)
Definition: he-phy.cc:274
virtual bool SendMpduFromBaManager(Ptr< QosTxop > edca, Time availableTime, bool initialFrame)
If the Block Ack Manager associated with the given EDCA has a BlockAckReq frame to transmit (the dura...
void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector) override
Called when the CTS timeout expires.
virtual Time GetPsduDurationId(Time txDuration, const WifiTxParameters &txParams) const
Compute how to set the Duration/ID field of PSDUs that do not include fragments.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
void SetWifiMac(const Ptr< WifiMac > mac) override
Set the MAC layer to use.
void DoDispose() override
Destructor implementation.
bool StartFrameExchange(Ptr< QosTxop > edca, Time availableTime, bool initialFrame) override
Start a frame exchange (including protection frames and acknowledgment frames as needed) that fits wi...
virtual void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector)
Forward a PSDU down to the PHY layer.
an EUI-48 address
Definition: mac48-address.h:46
bool IsBroadcast() const
A tag to be attached to a response to a multi-user UL frame, that carries the SNR values with which t...
Definition: mu-snr-tag.h:37
double Get(uint16_t staId) const
Return the SNR value for the given sender.
Definition: mu-snr-tag.cc:64
TxFormat
Enumeration of the possible transmission formats.
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:268
void AddPacketTag(const Tag &tag) const
Add a packet tag.
Definition: packet.cc:979
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
Ptr< QosTxop > m_edca
the EDCAF that gained channel access
Ptr< BlockAckManager > GetBaManager()
Get the Block Ack Manager associated with this QosTxop.
Definition: qos-txop.cc:255
void ScheduleBar(Ptr< const WifiMpdu > bar, bool skipIfNoDataQueued=false)
Definition: qos-txop.cc:303
bool GetBaAgreementEstablished(Mac48Address address, uint8_t tid) const
Definition: qos-txop.cc:261
Ptr< WifiMpdu > PeekNextMpdu(uint8_t linkId, uint8_t tid=8, Mac48Address recipient=Mac48Address::GetBroadcast(), Ptr< WifiMpdu > item=nullptr)
Peek the next frame to transmit on the given link to the given receiver and of the given TID from the...
Definition: qos-txop.cc:368
Ptr< WifiMpdu > GetNextMpdu(uint8_t linkId, Ptr< WifiMpdu > peekedItem, WifiTxParameters &txParams, Time availableTime, bool initialFrame)
Prepare the frame to transmit on the given link starting from the MPDU that has been previously peeke...
Definition: qos-txop.cc:454
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:568
Introspection did not find any typical Config paths.
Definition: snr-tag.h:35
double Get() const
Return the SNR value.
Definition: snr-tag.cc:90
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:286
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
VhtFrameExchangeManager handles the frame exchange sequences for VHT stations.
Ptr< WifiPsdu > GetWifiPsdu(Ptr< WifiMpdu > mpdu, const WifiTxVector &txVector) const override
Get a PSDU containing the given MPDU.
static void SetQosAckPolicy(Ptr< WifiMpdu > item, const WifiAcknowledgment *acknowledgment)
Set the QoS Ack policy for the given MPDU, which must be a QoS data frame.
Implements the IEEE 802.11 MAC header.
uint8_t GetQosTid() const
Return the Traffic ID of a QoS header.
bool IsAck() const
Return true if the header is an Ack header.
bool IsBlockAckReq() const
Return true if the header is a BlockAckRequest header.
bool IsCts() const
Return true if the header is a CTS header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
bool IsTrigger() const
Return true if the header is a Trigger header.
void SetNoMoreFragments()
Un-set the More Fragment bit in the Frame Control Field.
bool IsCtl() const
Return true if the Type is Control.
Time GetDuration() const
Return the duration from the Duration/ID field (Time object).
void SetSequenceNumber(uint16_t seq)
Set the sequence number of the header.
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
bool IsBlockAck() const
Return true if the header is a BlockAck header.
void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Mac48Address GetAddr2() const
Return the address in the Address 2 field.
bool HasData() const
Return true if the header type is DATA and is not DATA_NULL.
void SetQosTid(uint8_t tid)
Set the TID for the QoS header.
QosAckPolicy GetQosAckPolicy() const
Return the QoS Ack policy in the QoS control field.
void SetDuration(Time duration)
Set the Duration/ID field with the given duration (Time object).
void SetDsTo()
Set the To DS bit in the Frame Control field.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
bool IsQosData() const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
void SetQosEosp()
Set the end of service period (EOSP) bit in the QoS control field.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
void SetDsNotTo()
Un-set the To DS bit in the Frame Control field.
void SetNoRetry()
Un-set the Retry bit in the Frame Control field.
Ptr< HeConfiguration > GetHeConfiguration() const
Definition: wifi-mac.cc:1250
Time GetSlot() const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:740
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:728
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1422
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:950
Ptr< PhyEntity > GetPhyEntity(WifiModulationClass modulation) const
Get the supported PHY entity corresponding to the modulation class.
Definition: wifi-phy.cc:685
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition: wifi-phy.cc:1415
std::set< uint8_t > GetTids() const
Get the set of TIDs of the QoS Data frames included in the PSDU.
Definition: wifi-psdu.cc:168
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition: wifi-psdu.cc:269
std::vector< Ptr< WifiMpdu > >::const_iterator end() const
Return a const iterator to past-the-last MPDU.
Definition: wifi-psdu.cc:335
std::vector< Ptr< WifiMpdu > >::const_iterator begin() const
Return a const iterator to the first MPDU.
Definition: wifi-psdu.cc:323
Mac48Address GetAddr2() const
Get the Transmitter Address (TA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:128
uint32_t GetSize() const
Return the size of the PSDU in bytes.
Definition: wifi-psdu.cc:263
Ptr< const Packet > GetPayload(std::size_t i) const
Get the payload of the i-th MPDU.
Definition: wifi-psdu.cc:281
Mac48Address GetAddr1() const
Get the Receiver Address (RA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:113
void SetDuration(Time duration)
Set the Duration/ID field on all the MPDUs.
Definition: wifi-psdu.cc:158
std::size_t GetNMpdus() const
Return the number of MPDUs constituting the PSDU.
Definition: wifi-psdu.cc:317
This class stores the TX parameters (TX vector, protection mechanism, acknowledgment mechanism,...
uint32_t GetSizeIfAddMpdu(Ptr< const WifiMpdu > mpdu) const
Get the size in bytes of the frame in case the given MPDU is added.
std::unique_ptr< WifiProtection > m_protection
protection method
std::unique_ptr< WifiAcknowledgment > m_acknowledgment
acknowledgment method
Time m_txDuration
TX duration of the frame.
WifiTxVector m_txVector
TXVECTOR of the frame being prepared.
void AddMpdu(Ptr< const WifiMpdu > mpdu)
Record that an MPDU is being added to the current frame.
void Clear()
Reset the TX parameters.
bool IsRunning() const
Return true if the timer is running.
void Set(Reason reason, const Time &delay, MEM mem_ptr, OBJ obj, Args... args)
This method is called when a frame soliciting a response is transmitted.
Reason
The reason why the timer was started.
Definition: wifi-tx-timer.h:56
@ WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU
Definition: wifi-tx-timer.h:61
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void SetTxPowerLevel(uint8_t powerlevel)
Sets the selected transmission power level.
void SetChannelWidth(uint16_t channelWidth)
Sets the selected channelWidth (in MHz)
void SetGuardInterval(uint16_t guardInterval)
Sets the guard interval duration (in nanoseconds)
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.
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set the HE MU user-specific transmission information for the given STA-ID.
void SetAggregation(bool aggregation)
Sets if PSDU contains A-MPDU.
bool IsDlMu() const
Return true if this TX vector is used for a downlink multi-user transmission.
const HeMuUserInfoMap & GetHeMuUserInfoMap() const
Get a const reference to the map HE MU user-specific transmission information indexed by STA-ID.
WifiModulationClass GetModulationClass() const
Get the modulation class specified by this TXVECTOR.
void SetLength(uint16_t length)
Set the LENGTH field of the L-SIG.
bool IsUlMu() const
Return true if this TX vector is used for an uplink multi-user transmission.
void SetSigBMode(const WifiMode &mode)
Set the MCS used for SIG-B.
bool IsMu() const
Return true if this TX vector is used for a multi-user transmission.
void SetBssColor(uint8_t color)
Set the BSS color.
void SetPreambleType(WifiPreamble preamble)
Sets the preamble type.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#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:86
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition: abort.h:76
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:296
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:74
@ WIFI_PREAMBLE_HE_TB
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
@ WIFI_MOD_CLASS_HE
HE (Clause 27)
@ MU_BAR_TRIGGER
Definition: ctrl-headers.h:562
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:850
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:488
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
uint32_t GetBlockAckRequestSize(BlockAckReqType type)
Return the total BlockAckRequest size (including FCS trailer).
Definition: wifi-utils.cc:76
uint32_t GetMuBarSize(std::list< BlockAckReqType > types)
Return the total MU-BAR size (including FCS trailer).
Definition: wifi-utils.cc:86
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Definition: qos-utils.cc:126
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_QOSDATA_NULL
Ptr< T > Copy(Ptr< T > object)
Return a deep copy of a Ptr.
Definition: ptr.h:649
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition: wifi-utils.cc:66
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition: wifi-utils.cc:58
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
mac
Definition: third.py:85
ns3::Time timeout
std::vector< uint8_t > m_bitmapLen
Length (bytes) of included bitmaps.
RxSignalInfo structure containing info on the received signal.
Definition: phy-entity.h:70
double snr
SNR in linear scale.
Definition: phy-entity.h:71
WifiAcknowledgment is an abstract base struct.
Time acknowledgmentTime
time required by the acknowledgment method
const Method method
acknowledgment method
WifiDlMuAggregateTf specifies that a DL MU PPDU made of PSDUs including each a MU-BAR Trigger Frame i...
uint16_t ulLength
the UL Length field of the MU-BAR Trigger Frames
std::map< Mac48Address, BlockAckInfo > stationsReplyingWithBlockAck
Set of stations replying with a BlockAck frame.
WifiDlMuBarBaSequence specifies that a DL MU PPDU is acknowledged through a sequence of BlockAckReq a...
std::map< Mac48Address, BlockAckReqInfo > stationsSendBlockAckReqTo
Set of stations receiving a BlockAckReq frame and replying with a BlockAck frame.
std::map< Mac48Address, BlockAckInfo > stationsReplyingWithBlockAck
Set of stations replying with a BlockAck frame (no more than one)
std::map< Mac48Address, AckInfo > stationsReplyingWithNormalAck
Set of stations replying with an Ack frame (no more than one)
WifiDlMuTfMuBar specifies that a DL MU PPDU is followed after a SIFS duration by a MU-BAR Trigger Fra...
std::list< BlockAckReqType > barTypes
BAR types.
std::map< Mac48Address, BlockAckInfo > stationsReplyingWithBlockAck
Set of stations replying with a BlockAck frame.
uint16_t ulLength
the UL Length field of the MU-BAR Trigger Frame
WifiTxVector muBarTxVector
TXVECTOR used to transmit the MU-BAR Trigger Frame.
WifiNoAck specifies that no acknowledgment is required.
WifiNoProtection specifies that no protection method is used.
WifiUlMuMultiStaBa specifies that a Basic Trigger Frame is being sent to solicit TB PPDUs that will b...
BlockAckType baType
BlockAck type.
WifiTxVector tbPpduTxVector
TXVECTOR for a TB PPDU.
WifiTxVector multiStaBaTxVector
TXVECTOR for the Multi-STA BlockAck.
std::map< std::pair< Mac48Address, uint8_t >, std::size_t > stationsReceivingMultiStaBa
Map (originator, tid) pairs to the their index in baType.
#define SU_STA_ID
Definition: wifi-mode.h:34