A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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/erp-ofdm-phy.h"
29#include "ns3/log.h"
30#include "ns3/recipient-block-ack-agreement.h"
31#include "ns3/snr-tag.h"
32#include "ns3/sta-wifi-mac.h"
33#include "ns3/wifi-mac-queue.h"
34#include "ns3/wifi-mac-trailer.h"
35
36#include <algorithm>
37#include <functional>
38
39#undef NS_LOG_APPEND_CONTEXT
40#define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "][mac=" << m_self << "] "
41
42namespace ns3
43{
44
45NS_LOG_COMPONENT_DEFINE("HeFrameExchangeManager");
46
47NS_OBJECT_ENSURE_REGISTERED(HeFrameExchangeManager);
48
49bool
50IsTrigger(const WifiPsduMap& psduMap)
51{
52 return psduMap.size() == 1 && psduMap.cbegin()->first == SU_STA_ID &&
53 psduMap.cbegin()->second->GetNMpdus() == 1 &&
54 psduMap.cbegin()->second->GetHeader(0).IsTrigger();
55}
56
57bool
59{
60 return psduMap.size() == 1 && psduMap.cbegin()->first == SU_STA_ID &&
61 psduMap.cbegin()->second->GetNMpdus() == 1 &&
62 psduMap.cbegin()->second->GetHeader(0).IsTrigger();
63}
64
65TypeId
67{
68 static TypeId tid = TypeId("ns3::HeFrameExchangeManager")
70 .AddConstructor<HeFrameExchangeManager>()
71 .SetGroupName("Wifi");
72 return tid;
73}
74
76 : m_intraBssNavEnd(0),
77 m_triggerFrameInAmpdu(false)
78{
79 NS_LOG_FUNCTION(this);
80}
81
83{
85}
86
87void
89{
90 NS_LOG_FUNCTION(this);
92 {
94 }
97}
98
99uint16_t
101{
103 if (m_mac->GetHeConfiguration()->GetMpduBufferSize() > 64)
104 {
105 return 256;
106 }
107 return 64;
108}
109
110void
112{
113 m_apMac = DynamicCast<ApWifiMac>(mac);
114 m_staMac = DynamicCast<StaWifiMac>(mac);
116}
117
118void
120{
121 NS_LOG_FUNCTION(this << txVector << psduDuration.As(Time::MS));
122 VhtFrameExchangeManager::RxStartIndication(txVector, psduDuration);
123 // Cancel intra-BSS NAV reset timer when receiving a frame from the PHY
125}
126
127void
129{
130 NS_LOG_FUNCTION(this);
131 m_apMac = nullptr;
132 m_staMac = nullptr;
133 m_psduMap.clear();
135 m_muScheduler = nullptr;
138}
139
140void
142{
144 NS_ABORT_MSG_IF(!m_apMac, "A Multi-User Scheduler can only be aggregated to an AP");
146 "A Multi-User Scheduler can only be aggregated to an HE AP");
147 m_muScheduler = muScheduler;
148}
149
150bool
151HeFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
152{
153 NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
154
157
158 /*
159 * We consult the Multi-user Scheduler (if available) to know the type of transmission to make
160 * if:
161 * - there is no pending BlockAckReq to transmit
162 * - either the AC queue is empty (the scheduler might select an UL MU transmission)
163 * or the next frame in the AC queue is a non-broadcast QoS data frame addressed to
164 * a receiver with which a BA agreement has been already established
165 */
166 if (m_muScheduler && !GetBar(edca->GetAccessCategory()) &&
167 (!(mpdu = edca->PeekNextMpdu(m_linkId)) ||
168 (mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().GetAddr1().IsGroup() &&
169 m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(),
170 mpdu->GetHeader().GetQosTid()))))
171 {
172 txFormat = m_muScheduler->NotifyAccessGranted(edca,
173 availableTime,
174 initialFrame,
176 m_linkId);
177 }
178
179 if (txFormat == MultiUserScheduler::SU_TX)
180 {
181 return VhtFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
182 }
183
184 if (txFormat == MultiUserScheduler::DL_MU_TX)
185 {
186 if (m_muScheduler->GetDlMuInfo(m_linkId).psduMap.empty())
187 {
189 "The Multi-user Scheduler returned DL_MU_TX with empty psduMap, do not transmit");
190 return false;
191 }
192
194 m_muScheduler->GetDlMuInfo(m_linkId).txParams);
195 return true;
196 }
197
198 if (txFormat == MultiUserScheduler::UL_MU_TX)
199 {
200 auto packet = Create<Packet>();
201 packet->AddHeader(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
202 auto trigger = Create<WifiMpdu>(packet, m_muScheduler->GetUlMuInfo(m_linkId).macHdr);
205 {SU_STA_ID,
206 GetWifiPsdu(trigger, m_muScheduler->GetUlMuInfo(m_linkId).txParams.m_txVector)}},
207 m_muScheduler->GetUlMuInfo(m_linkId).txParams);
208 return true;
209 }
210
211 return false;
212}
213
214void
216{
217 NS_LOG_FUNCTION(this << &txParams);
218
219 m_psduMap = std::move(psduMap);
220 m_txParams = std::move(txParams);
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 // in case we are sending a Trigger Frame, update the acknowledgment time so that
232 // the Duration/ID of the MU-RTS is correctly computed
234 {
236 const auto& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
237 NS_ASSERT_MSG(!trigger.IsBasic() || m_txParams.m_acknowledgment->method ==
239 "Acknowledgment (" << m_txParams.m_acknowledgment.get()
240 << ") incompatible with Basic Trigger Frame");
241 NS_ASSERT_MSG(!trigger.IsBsrp() ||
243 "Acknowledgment (" << m_txParams.m_acknowledgment.get()
244 << ") incompatible with BSRP Trigger Frame");
245 // Add a SIFS and the TB PPDU duration to the acknowledgment time of the Trigger Frame
246 auto txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
247 m_txParams.m_acknowledgment->acknowledgmentTime +=
249 txVector,
250 m_phy->GetPhyBand());
251 }
252
253 // Set QoS Ack policy
254 for (auto& psdu : m_psduMap)
255 {
257 }
258
259 for (const auto& psdu : m_psduMap)
260 {
261 for (const auto& mpdu : *PeekPointer(psdu.second))
262 {
263 if (mpdu->IsQueued())
264 {
265 mpdu->SetInFlight(m_linkId);
266 }
267 }
268 }
269
271}
272
273void
275{
276 NS_LOG_FUNCTION(this << &txParams);
277
278 NS_ABORT_MSG_IF(m_psduMap.size() > 1 &&
279 txParams.m_protection->method == WifiProtection::RTS_CTS,
280 "Cannot use RTS/CTS with MU PPDUs");
281 if (txParams.m_protection->method == WifiProtection::MU_RTS_CTS)
282 {
283 RecordSentMuRtsTo(txParams);
284 SendMuRts(txParams);
285 }
286 else
287 {
289 }
290}
291
292void
294{
295 NS_LOG_FUNCTION(this << &txParams);
296
297 NS_ASSERT(txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS);
298 auto protection = static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
299
300 NS_ASSERT(protection->muRts.IsMuRts());
301 NS_ASSERT_MSG(m_apMac, "APs only can send MU-RTS TF");
302 const auto& aidAddrMap = m_apMac->GetStaList(m_linkId);
303 NS_ASSERT(m_sentRtsTo.empty());
304
305 for (const auto& userInfo : protection->muRts)
306 {
307 const auto addressIt = aidAddrMap.find(userInfo.GetAid12());
308 NS_ASSERT_MSG(addressIt != aidAddrMap.end(), "AID not found");
309 m_sentRtsTo.insert(addressIt->second);
310 }
311}
312
313void
315{
316 NS_LOG_FUNCTION(this);
317 if (!m_psduMap.empty())
318 {
320 m_sentRtsTo.clear();
321 SendPsduMap();
322 return;
323 }
325}
326
327Time
329 const WifiTxVector& muRtsTxVector,
330 Time txDuration,
331 Time response) const
332{
333 NS_LOG_FUNCTION(this << muRtsSize << muRtsTxVector << txDuration << response);
334
336 {
337 WifiTxVector txVector;
338 txVector.SetMode(GetCtsModeAfterMuRts());
339 return VhtFrameExchangeManager::GetRtsDurationId(txVector, txDuration, response);
340 }
341
342 // under multiple protection settings, if the TXOP limit is not null, Duration/ID
343 // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
344 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
345 // of 802.11-2016)
346 return std::max(m_edca->GetRemainingTxop(m_linkId) -
347 m_phy->CalculateTxDuration(muRtsSize, muRtsTxVector, m_phy->GetPhyBand()),
348 Seconds(0));
349}
350
351void
353{
354 NS_LOG_FUNCTION(this << &txParams);
355 WifiMacHeader hdr;
358 hdr.SetAddr2(m_self);
359 hdr.SetDsNotTo();
360 hdr.SetDsNotFrom();
361 hdr.SetNoRetry();
362 hdr.SetNoMoreFragments();
363
364 NS_ASSERT(txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS);
365 auto protection = static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
366
367 NS_ASSERT(protection->muRts.IsMuRts());
368 protection->muRts.SetCsRequired(true);
369 Ptr<Packet> payload = Create<Packet>();
370 payload->AddHeader(protection->muRts);
371
372 auto mpdu = Create<WifiMpdu>(payload, hdr);
373
374 NS_ASSERT(txParams.m_txDuration != Time::Min());
375 mpdu->GetHeader().SetDuration(
376 GetMuRtsDurationId(mpdu->GetSize(),
377 protection->muRtsTxVector,
378 txParams.m_txDuration,
379 txParams.m_acknowledgment->acknowledgmentTime));
380
381 // Get the TXVECTOR used by one station to send the CTS response. This is used
382 // to compute the preamble duration, so it does not matter which station we choose
383 WifiTxVector ctsTxVector =
384 GetCtsTxVectorAfterMuRts(protection->muRts, protection->muRts.begin()->GetAid12());
385
386 // After transmitting an MU-RTS frame, the STA shall wait for a CTSTimeout interval of
387 // aSIFSTime + aSlotTime + aRxPHYStartDelay (Sec. 27.2.5.2 of 802.11ax D3.0).
388 // aRxPHYStartDelay equals the time to transmit the PHY header.
389 Time timeout = m_phy->CalculateTxDuration(mpdu->GetSize(),
390 protection->muRtsTxVector,
391 m_phy->GetPhyBand()) +
392 m_phy->GetSifs() + m_phy->GetSlot() +
394
397 timeout,
400 this,
401 mpdu,
402 protection->muRtsTxVector);
404
405 ForwardMpduDown(mpdu, protection->muRtsTxVector);
406}
407
408void
410{
411 NS_LOG_FUNCTION(this << *muRts << txVector);
412
413 if (m_psduMap.empty())
414 {
415 // A CTS Timeout occurred when protecting a single PSDU that is not included
416 // in a DL MU PPDU is handled by the parent classes
418 return;
419 }
420
421 m_sentRtsTo.clear();
422 for (const auto& psdu : m_psduMap)
423 {
424 for (const auto& mpdu : *PeekPointer(psdu.second))
425 {
426 if (mpdu->IsQueued())
427 {
428 mpdu->ResetInFlight(m_linkId);
429 }
430 }
431 }
432
433 // NOTE Implementation of QSRC[AC] and QLRC[AC] should be improved...
434 const auto& hdr = m_psduMap.cbegin()->second->GetHeader(0);
435 if (!hdr.GetAddr1().IsGroup())
436 {
438 }
439
440 if (!hdr.GetAddr1().IsGroup() &&
441 !GetWifiRemoteStationManager()->NeedRetransmission(*m_psduMap.cbegin()->second->begin()))
442 {
443 NS_LOG_DEBUG("Missed CTS, discard MPDUs");
445 for (const auto& psdu : m_psduMap)
446 {
447 // Dequeue the MPDUs if they are stored in a queue
448 DequeuePsdu(psdu.second);
449 for (const auto& mpdu : *PeekPointer(psdu.second))
450 {
452 }
453 }
455 }
456 else
457 {
458 NS_LOG_DEBUG("Missed CTS, retransmit MPDUs");
460 }
461 // Make the sequence numbers of the MPDUs available again if the MPDUs have never
462 // been transmitted, both in case the MPDUs have been discarded and in case the
463 // MPDUs have to be transmitted (because a new sequence number is assigned to
464 // MPDUs that have never been transmitted and are selected for transmission)
465 for (const auto& [staId, psdu] : m_psduMap)
466 {
468 }
469 m_psduMap.clear();
471}
472
475{
476 auto it = std::find_if(
477 psduMap.begin(),
478 psduMap.end(),
479 [&to](std::pair<uint16_t, Ptr<WifiPsdu>> psdu) { return psdu.second->GetAddr1() == to; });
480 if (it != psduMap.end())
481 {
482 return it->second;
483 }
484 return nullptr;
485}
486
487void
489{
490 NS_LOG_FUNCTION(this << *rts << txVector);
491
492 if (m_psduMap.empty())
493 {
494 // A CTS Timeout occurred when protecting a single PSDU that is not included
495 // in a DL MU PPDU is handled by the parent classes
497 return;
498 }
499
500 NS_ABORT_MSG_IF(m_psduMap.size() > 1, "RTS/CTS cannot be used to protect an MU PPDU");
501 DoCtsTimeout(m_psduMap.begin()->second);
502 m_psduMap.clear();
503}
504
505void
507{
508 NS_LOG_FUNCTION(this);
509
510 // A multi-user transmission may succeed even if some stations did not respond.
511 // Remove such stations from the set of stations for which protection is not needed
512 // in the current TXOP.
513 for (const auto& address : m_txTimer.GetStasExpectedToRespond())
514 {
515 NS_LOG_DEBUG(address << " did not respond, hence it is no longer protected");
516 m_protectedStas.erase(address);
517 }
518
520}
521
522void
524{
525 NS_LOG_FUNCTION(this);
526
529
530 WifiTxTimer::Reason timerType = WifiTxTimer::NOT_RUNNING; // no timer
531 WifiTxVector* responseTxVector = nullptr;
532 Ptr<WifiMpdu> mpdu = nullptr;
533 Ptr<WifiPsdu> psdu = nullptr;
534 WifiTxVector txVector;
535 std::set<Mac48Address> staExpectResponseFrom;
536
537 // Compute the type of TX timer to set depending on the acknowledgment method
538
539 /*
540 * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
541 */
543 {
544 auto acknowledgment =
546
547 // schedule the transmission of required BlockAckReq frames
548 for (const auto& psdu : m_psduMap)
549 {
550 if (acknowledgment->stationsSendBlockAckReqTo.find(psdu.second->GetAddr1()) !=
551 acknowledgment->stationsSendBlockAckReqTo.end())
552 {
553 // the receiver of this PSDU will receive a BlockAckReq
554 std::set<uint8_t> tids = psdu.second->GetTids();
555 NS_ABORT_MSG_IF(tids.size() > 1,
556 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
557 uint8_t tid = *tids.begin();
558
560 auto [reqHdr, hdr] =
561 m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid);
562 m_edca->GetBaManager()->ScheduleBar(reqHdr, hdr);
563 }
564 }
565
566 if (!acknowledgment->stationsReplyingWithNormalAck.empty())
567 {
568 // a station will reply immediately with a Normal Ack
570 responseTxVector =
571 &acknowledgment->stationsReplyingWithNormalAck.begin()->second.ackTxVector;
572 auto from = acknowledgment->stationsReplyingWithNormalAck.begin()->first;
573 psdu = GetPsduTo(from, m_psduMap);
574 NS_ASSERT(psdu->GetNMpdus() == 1);
575 mpdu = *psdu->begin();
576 staExpectResponseFrom.insert(from);
577 }
578 else if (!acknowledgment->stationsReplyingWithBlockAck.empty())
579 {
580 // a station will reply immediately with a Block Ack
581 timerType = WifiTxTimer::WAIT_BLOCK_ACK;
582 responseTxVector =
583 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
584 auto from = acknowledgment->stationsReplyingWithBlockAck.begin()->first;
585 psdu = GetPsduTo(from, m_psduMap);
586 staExpectResponseFrom.insert(from);
587 }
588 // else no station will reply immediately
589 }
590 /*
591 * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
592 */
594 {
595 auto acknowledgment = static_cast<WifiDlMuTfMuBar*>(m_txParams.m_acknowledgment.get());
596
597 if (!m_triggerFrame)
598 {
599 // we are transmitting the DL MU PPDU and have to schedule the
600 // transmission of a MU-BAR Trigger Frame.
601 // Create a TRIGVECTOR by "merging" all the BlockAck TXVECTORs
602 std::map<uint16_t, CtrlBAckRequestHeader> recipients;
603
604 NS_ASSERT(!acknowledgment->stationsReplyingWithBlockAck.empty());
605 auto staIt = acknowledgment->stationsReplyingWithBlockAck.begin();
606 m_trigVector = staIt->second.blockAckTxVector;
607 while (staIt != acknowledgment->stationsReplyingWithBlockAck.end())
608 {
610 uint16_t staId = m_apMac->GetAssociationId(staIt->first, m_linkId);
611
613 staIt->second.blockAckTxVector.GetHeMuUserInfo(staId));
614 recipients.emplace(staId, staIt->second.barHeader);
615
616 staIt++;
617 }
618 // set the Length field of the response TXVECTOR, which is needed to correctly
619 // set the UL Length field of the MU-BAR Trigger Frame
620 m_trigVector.SetLength(acknowledgment->ulLength);
621
623 }
624 else
625 {
626 // we are transmitting the MU-BAR following the DL MU PPDU after a SIFS.
627 // m_psduMap and m_txParams are still the same as when the DL MU PPDU was sent.
628 // record the set of stations expected to send a BlockAck frame
629 for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
630 {
631 staExpectResponseFrom.insert(station.first);
632 }
633
634 Ptr<WifiPsdu> triggerPsdu = GetWifiPsdu(m_triggerFrame, acknowledgment->muBarTxVector);
635 Time txDuration = m_phy->CalculateTxDuration(triggerPsdu->GetSize(),
636 acknowledgment->muBarTxVector,
637 m_phy->GetPhyBand());
638 // update acknowledgmentTime to correctly set the Duration/ID
639 acknowledgment->acknowledgmentTime -= (m_phy->GetSifs() + txDuration);
640 m_triggerFrame->GetHeader().SetDuration(GetPsduDurationId(txDuration, m_txParams));
641
642 responseTxVector =
643 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
644 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
646
648 timeout,
649 staExpectResponseFrom,
651 this,
652 &m_psduMap,
653 staExpectResponseFrom.size());
655
656 ForwardPsduDown(triggerPsdu, acknowledgment->muBarTxVector);
657
658 // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
659 auto hePhy =
660 StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
661 hePhy->SetTrigVector(m_trigVector, timeout);
662
663 return;
664 }
665 }
666 /*
667 * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
668 */
670 {
671 auto acknowledgment = static_cast<WifiDlMuAggregateTf*>(m_txParams.m_acknowledgment.get());
672
674 acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
675
676 // record the set of stations expected to send a BlockAck frame
677 for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
678 {
679 staExpectResponseFrom.insert(station.first);
680 // check that the station that is expected to send a BlockAck frame is
681 // actually the receiver of a PSDU
682 auto psduMapIt = std::find_if(m_psduMap.begin(),
683 m_psduMap.end(),
684 [&station](std::pair<uint16_t, Ptr<WifiPsdu>> psdu) {
685 return psdu.second->GetAddr1() == station.first;
686 });
687
688 NS_ASSERT(psduMapIt != m_psduMap.end());
689 // add a MU-BAR Trigger Frame to the PSDU
690 std::vector<Ptr<WifiMpdu>> mpduList(psduMapIt->second->begin(),
691 psduMapIt->second->end());
692 NS_ASSERT(mpduList.size() == psduMapIt->second->GetNMpdus());
693 // set the Length field of the response TXVECTOR, which is needed to correctly
694 // set the UL Length field of the MU-BAR Trigger Frame
695 station.second.blockAckTxVector.SetLength(acknowledgment->ulLength);
696 mpduList.push_back(PrepareMuBar(station.second.blockAckTxVector,
697 {{psduMapIt->first, station.second.barHeader}}));
698 psduMapIt->second = Create<WifiPsdu>(std::move(mpduList));
700 psduMapIt->first,
701 station.second.blockAckTxVector.GetHeMuUserInfo(psduMapIt->first));
702 }
703
705 responseTxVector =
706 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
707 m_trigVector.SetLength(acknowledgment->ulLength);
708 }
709 /*
710 * Basic Trigger Frame starting an UL MU transmission
711 */
712 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
713 {
714 // the PSDU map being sent must contain a (Basic) Trigger Frame
715 NS_ASSERT(IsTrigger(m_psduMap));
716 mpdu = *m_psduMap.begin()->second->begin();
717
718 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
719
720 // record the set of stations solicited by this Trigger Frame
721 for (const auto& station : acknowledgment->stationsReceivingMultiStaBa)
722 {
723 staExpectResponseFrom.insert(station.first.first);
724 }
725
726 // Reset stationsReceivingMultiStaBa, which will be filled as soon as
727 // TB PPDUs are received
728 acknowledgment->stationsReceivingMultiStaBa.clear();
729 acknowledgment->baType.m_bitmapLen.clear();
730
732 responseTxVector = &acknowledgment->tbPpduTxVector;
733 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
734 }
735 /*
736 * BSRP Trigger Frame
737 */
738 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE &&
739 !m_txParams.m_txVector.IsUlMu() && IsTrigger(m_psduMap))
740 {
741 CtrlTriggerHeader& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
742 NS_ASSERT(trigger.IsBsrp());
743 NS_ASSERT(m_apMac);
744
745 // record the set of stations solicited by this Trigger Frame
746 for (const auto& userInfo : trigger)
747 {
748 auto staIt = m_apMac->GetStaList(m_linkId).find(userInfo.GetAid12());
749 NS_ASSERT(staIt != m_apMac->GetStaList(m_linkId).end());
750 staExpectResponseFrom.insert(staIt->second);
751 }
752
754 txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
755 responseTxVector = &txVector;
756 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
757 }
758 /*
759 * TB PPDU solicited by a Basic Trigger Frame
760 */
761 else if (m_txParams.m_txVector.IsUlMu() &&
762 m_txParams.m_acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
763 {
764 NS_ASSERT(m_psduMap.size() == 1);
766 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
767 auto recv = m_psduMap.begin()->second->GetAddr1();
768 txVector = GetWifiRemoteStationManager()->GetBlockAckTxVector(recv, m_txParams.m_txVector);
769 responseTxVector = &txVector;
770 staExpectResponseFrom.insert(recv);
771 }
772 /*
773 * QoS Null frames solicited by a BSRP Trigger Frame
774 */
775 else if (m_txParams.m_txVector.IsUlMu() &&
776 m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
777 {
778 // No response is expected, so do nothing.
779 }
780 else
781 {
782 NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
783 << m_txParams.m_acknowledgment.get() << ")");
784 }
785
786 // create a map of Ptr<const WifiPsdu>, as required by the PHY
787 WifiConstPsduMap psduMap;
788 for (const auto& psdu : m_psduMap)
789 {
790 psduMap.emplace(psdu.first, psdu.second);
791 }
792
793 Time txDuration;
794 if (m_txParams.m_txVector.IsUlMu())
795 {
796 txDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(m_txParams.m_txVector.GetLength(),
797 m_txParams.m_txVector,
798 m_phy->GetPhyBand());
799 }
800 else
801 {
802 txDuration =
803 m_phy->CalculateTxDuration(psduMap, m_txParams.m_txVector, m_phy->GetPhyBand());
804
805 // Set Duration/ID
806 Time durationId = GetPsduDurationId(txDuration, m_txParams);
807 for (auto& psdu : m_psduMap)
808 {
809 psdu.second->SetDuration(durationId);
810 }
811 }
812
813 if (timerType == WifiTxTimer::NOT_RUNNING)
814 {
815 if (m_triggerFrame)
816 {
817 NS_LOG_DEBUG("Scheduling MU-BAR " << *m_triggerFrame);
818 Simulator::Schedule(txDuration + m_phy->GetSifs(),
820 this);
821 }
822 else if (!m_txParams.m_txVector.IsUlMu())
823 {
825 }
826 }
827 else
828 {
829 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
830 m_phy->CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
831 m_channelAccessManager->NotifyAckTimeoutStartNow(timeout);
832
833 // start timer
834 switch (timerType)
835 {
837 NS_ASSERT(mpdu);
838 m_txTimer.Set(timerType,
839 timeout,
840 staExpectResponseFrom,
842 this,
843 mpdu,
844 m_txParams.m_txVector);
845 break;
847 NS_ASSERT(psdu);
848 m_txTimer.Set(timerType,
849 timeout,
850 staExpectResponseFrom,
852 this,
853 psdu,
854 m_txParams.m_txVector);
855 break;
857 m_txTimer.Set(timerType,
858 timeout,
859 staExpectResponseFrom,
861 this,
862 &m_psduMap,
863 staExpectResponseFrom.size());
864 break;
867 m_txTimer.Set(timerType,
868 timeout,
869 staExpectResponseFrom,
871 this,
872 &m_psduMap,
873 staExpectResponseFrom.size());
874 break;
876 m_txTimer.Set(timerType,
877 timeout,
878 staExpectResponseFrom,
880 this,
881 m_psduMap.begin()->second,
882 m_txParams.m_txVector);
883 break;
884 default:
885 NS_ABORT_MSG("Unknown timer type: " << timerType);
886 break;
887 }
888 }
889
890 // transmit the map of PSDUs
891 ForwardPsduMapDown(psduMap, m_txParams.m_txVector);
892
896 {
897 // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
898 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
899 hePhy->SetTrigVector(m_trigVector, m_txTimer.GetDelayLeft());
900 }
901 else if (timerType == WifiTxTimer::NOT_RUNNING && m_txParams.m_txVector.IsUlMu())
902 {
903 // clear m_psduMap after sending QoS Null frames following a BSRP Trigger Frame
904 Simulator::Schedule(txDuration, &WifiPsduMap::clear, &m_psduMap);
905 }
906}
907
908void
909HeFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector)
910{
911 NS_LOG_FUNCTION(this << psduMap << txVector);
912
913 if (ns3::IsDlMu(txVector.GetPreambleType()))
914 {
915 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(txVector.GetModulationClass()));
916 auto sigBMode = hePhy->GetSigBMode(txVector);
917 txVector.SetSigBMode(sigBMode);
918 }
919
920 for (const auto& psdu : psduMap)
921 {
922 NS_LOG_DEBUG("Transmitting: [STAID=" << psdu.first << ", " << *psdu.second << "]");
923 }
924 NS_LOG_DEBUG("TXVECTOR: " << txVector);
925 for (const auto& [staId, psdu] : psduMap)
926 {
927 FinalizeMacHeader(psdu);
928 NotifyTxToEdca(psdu);
929 }
930 m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
931
932 if (psduMap.size() > 1 || psduMap.begin()->second->IsAggregate() ||
933 psduMap.begin()->second->IsSingle())
934 {
935 txVector.SetAggregation(true);
936 }
937
938 m_phy->Send(psduMap, txVector);
939}
940
942HeFrameExchangeManager::PrepareMuBar(const WifiTxVector& responseTxVector,
943 std::map<uint16_t, CtrlBAckRequestHeader> recipients) const
944{
945 NS_LOG_FUNCTION(this << responseTxVector);
946 NS_ASSERT(responseTxVector.GetHeMuUserInfoMap().size() == recipients.size());
947 NS_ASSERT(!recipients.empty());
948
949 CtrlTriggerHeader muBar(TriggerFrameType::MU_BAR_TRIGGER, responseTxVector);
950 SetTargetRssi(muBar);
951 // Set the CS Required subfield to true, unless the UL Length subfield is less
952 // than or equal to 418 (see Section 26.5.2.5 of 802.11ax-2021)
953 muBar.SetCsRequired(muBar.GetUlLength() > 418);
954
955 // Add the Trigger Dependent User Info subfield to every User Info field
956 for (auto& userInfo : muBar)
957 {
958 auto recipientIt = recipients.find(userInfo.GetAid12());
959 NS_ASSERT(recipientIt != recipients.end());
960
961 // Store the BAR in the Trigger Dependent User Info subfield
962 userInfo.SetMuBarTriggerDepUserInfo(recipientIt->second);
963 }
964
965 Ptr<Packet> bar = Create<Packet>();
966 bar->AddHeader(muBar);
967 Mac48Address rxAddress;
968 // "If the Trigger frame has one User Info field and the AID12 subfield of the
969 // User Info contains the AID of a STA, then the RA field is set to the address
970 // of that STA". Otherwise, it is set to the broadcast address (Sec. 9.3.1.23 -
971 // 802.11ax amendment draft 3.0)
972 if (muBar.GetNUserInfoFields() > 1)
973 {
974 rxAddress = Mac48Address::GetBroadcast();
975 }
976 else
977 {
978 NS_ASSERT(m_apMac);
979 rxAddress = m_apMac->GetStaList(m_linkId).at(recipients.begin()->first);
980 }
981
982 WifiMacHeader hdr;
984 hdr.SetAddr1(rxAddress);
985 hdr.SetAddr2(m_self);
986 hdr.SetDsNotTo();
987 hdr.SetDsNotFrom();
988 hdr.SetNoRetry();
989 hdr.SetNoMoreFragments();
990
991 return Create<WifiMpdu>(bar, hdr);
992}
993
994void
995HeFrameExchangeManager::CalculateProtectionTime(WifiProtection* protection) const
996{
997 NS_LOG_FUNCTION(this << protection);
998 NS_ASSERT(protection != nullptr);
999
1000 if (protection->method == WifiProtection::MU_RTS_CTS)
1001 {
1002 auto muRtsCtsProtection = static_cast<WifiMuRtsCtsProtection*>(protection);
1003
1004 // Get the TXVECTOR used by one station to send the CTS response. This is used
1005 // to compute the TX duration, so it does not matter which station we choose
1006 WifiTxVector ctsTxVector =
1007 GetCtsTxVectorAfterMuRts(muRtsCtsProtection->muRts,
1008 muRtsCtsProtection->muRts.begin()->GetAid12());
1009
1011 muRtsCtsProtection->muRts.GetSerializedSize() + WIFI_MAC_FCS_LENGTH;
1012 muRtsCtsProtection->protectionTime =
1013 m_phy->CalculateTxDuration(muRtsSize,
1014 muRtsCtsProtection->muRtsTxVector,
1015 m_phy->GetPhyBand()) +
1016 m_phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
1017 2 * m_phy->GetSifs();
1018 }
1019 else
1020 {
1021 VhtFrameExchangeManager::CalculateProtectionTime(protection);
1022 }
1023}
1024
1025void
1026HeFrameExchangeManager::CalculateAcknowledgmentTime(WifiAcknowledgment* acknowledgment) const
1027{
1028 NS_LOG_FUNCTION(this << acknowledgment);
1029 NS_ASSERT(acknowledgment);
1030
1031 /*
1032 * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
1033 */
1034 if (acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
1035 {
1036 auto dlMuBarBaAcknowledgment = static_cast<WifiDlMuBarBaSequence*>(acknowledgment);
1037
1038 Time duration = Seconds(0);
1039
1040 // normal ack or implicit BAR policy can be used for (no more than) one receiver
1041 NS_ABORT_IF(dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.size() +
1042 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.size() >
1043 1);
1044
1045 if (!dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.empty())
1046 {
1047 const auto& info =
1048 dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.begin()->second;
1049 duration +=
1050 m_phy->GetSifs() +
1051 m_phy->CalculateTxDuration(GetAckSize(), info.ackTxVector, m_phy->GetPhyBand());
1052 }
1053
1054 if (!dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.empty())
1055 {
1056 const auto& info =
1057 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.begin()->second;
1058 duration += m_phy->GetSifs() + m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
1059 info.blockAckTxVector,
1060 m_phy->GetPhyBand());
1061 }
1062
1063 for (const auto& stations : dlMuBarBaAcknowledgment->stationsSendBlockAckReqTo)
1064 {
1065 const auto& info = stations.second;
1066 duration += m_phy->GetSifs() +
1067 m_phy->CalculateTxDuration(GetBlockAckRequestSize(info.barType),
1068 info.blockAckReqTxVector,
1069 m_phy->GetPhyBand()) +
1070 m_phy->GetSifs() +
1071 m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
1072 info.blockAckTxVector,
1073 m_phy->GetPhyBand());
1074 }
1075
1076 dlMuBarBaAcknowledgment->acknowledgmentTime = duration;
1077 }
1078 /*
1079 * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
1080 */
1081 else if (acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
1082 {
1083 auto dlMuTfMuBarAcknowledgment = static_cast<WifiDlMuTfMuBar*>(acknowledgment);
1084
1085 Time duration = Seconds(0);
1086
1087 for (const auto& stations : dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck)
1088 {
1089 // compute the TX duration of the BlockAck response from this receiver.
1090 const auto& info = stations.second;
1091 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1092 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1093 Time currBlockAckDuration = m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
1094 info.blockAckTxVector,
1095 m_phy->GetPhyBand(),
1096 staId);
1097 // update the max duration among all the Block Ack responses
1098 if (currBlockAckDuration > duration)
1099 {
1100 duration = currBlockAckDuration;
1101 }
1102 }
1103
1104 // The computed duration may not be coded exactly in the L-SIG length, hence determine
1105 // the exact duration corresponding to the value that will be coded in this field.
1106 WifiTxVector& txVector = dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck.begin()
1107 ->second.blockAckTxVector;
1108 std::tie(dlMuTfMuBarAcknowledgment->ulLength, duration) =
1109 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1110
1111 uint32_t muBarSize = GetMuBarSize(dlMuTfMuBarAcknowledgment->barTypes);
1112 if (dlMuTfMuBarAcknowledgment->muBarTxVector.GetModulationClass() >= WIFI_MOD_CLASS_VHT)
1113 {
1114 // MU-BAR TF will be sent as an S-MPDU
1115 muBarSize = MpduAggregator::GetSizeIfAggregated(muBarSize, 0);
1116 }
1117 dlMuTfMuBarAcknowledgment->acknowledgmentTime =
1118 m_phy->GetSifs() +
1119 m_phy->CalculateTxDuration(muBarSize,
1120 dlMuTfMuBarAcknowledgment->muBarTxVector,
1121 m_phy->GetPhyBand()) +
1122 m_phy->GetSifs() + duration;
1123 }
1124 /*
1125 * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
1126 */
1127 else if (acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1128 {
1129 auto dlMuAggrTfAcknowledgment = static_cast<WifiDlMuAggregateTf*>(acknowledgment);
1130
1131 Time duration = Seconds(0);
1132
1133 for (const auto& stations : dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck)
1134 {
1135 // compute the TX duration of the BlockAck response from this receiver.
1136 const auto& info = stations.second;
1137 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1138 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1139 Time currBlockAckDuration = m_phy->CalculateTxDuration(GetBlockAckSize(info.baType),
1140 info.blockAckTxVector,
1141 m_phy->GetPhyBand(),
1142 staId);
1143 // update the max duration among all the Block Ack responses
1144 if (currBlockAckDuration > duration)
1145 {
1146 duration = currBlockAckDuration;
1147 }
1148 }
1149
1150 // The computed duration may not be coded exactly in the L-SIG length, hence determine
1151 // the exact duration corresponding to the value that will be coded in this field.
1152 WifiTxVector& txVector =
1153 dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
1154 std::tie(dlMuAggrTfAcknowledgment->ulLength, duration) =
1155 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1156 dlMuAggrTfAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + duration;
1157 }
1158 /*
1159 * Basic Trigger Frame starting an UL MU transmission
1160 */
1161 else if (acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
1162 {
1163 auto ulMuMultiStaBa = static_cast<WifiUlMuMultiStaBa*>(acknowledgment);
1164
1165 Time duration = m_phy->CalculateTxDuration(GetBlockAckSize(ulMuMultiStaBa->baType),
1166 ulMuMultiStaBa->multiStaBaTxVector,
1167 m_phy->GetPhyBand());
1168 ulMuMultiStaBa->acknowledgmentTime = m_phy->GetSifs() + duration;
1169 }
1170 /*
1171 * TB PPDU solicired by a Basic or BSRP Trigger Frame
1172 */
1173 else if (acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
1174 {
1175 // The station solicited by the Trigger Frame does not have to account
1176 // for the actual acknowledgment time since it is given the PPDU duration
1177 // through the Trigger Frame
1178 acknowledgment->acknowledgmentTime = Seconds(0);
1179 }
1180 else
1181 {
1182 VhtFrameExchangeManager::CalculateAcknowledgmentTime(acknowledgment);
1183 }
1184}
1185
1187HeFrameExchangeManager::GetCtsModeAfterMuRts() const
1188{
1189 // The CTS frame sent in response to an MU-RTS Trigger frame shall be carried in a non-HT or
1190 // non-HT duplicate PPDU (see Clause 17) with a 6 Mb/s rate (Sec. 26.2.6.3 of 802.11ax-2021)
1191 return m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ ? ErpOfdmPhy::GetErpOfdmRate6Mbps()
1192 : OfdmPhy::GetOfdmRate6Mbps();
1193}
1194
1196HeFrameExchangeManager::GetCtsTxVectorAfterMuRts(const CtrlTriggerHeader& trigger,
1197 uint16_t staId) const
1198{
1199 NS_LOG_FUNCTION(this << trigger << staId);
1200
1201 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
1202 NS_ASSERT_MSG(userInfoIt != trigger.end(), "User Info field for AID=" << staId << " not found");
1203 uint16_t bw = 0;
1204
1205 if (uint8_t ru = userInfoIt->GetMuRtsRuAllocation(); ru < 65)
1206 {
1207 bw = 20;
1208 }
1209 else if (ru < 67)
1210 {
1211 bw = 40;
1212 }
1213 else if (ru == 67)
1214 {
1215 bw = 80;
1216 }
1217 else
1218 {
1219 NS_ASSERT(ru == 68);
1220 bw = 160;
1221 }
1222
1223 auto txVector = GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1224 // set the channel width of the CTS TXVECTOR according to the allocated RU
1225 txVector.SetChannelWidth(bw);
1226
1227 return txVector;
1228}
1229
1230Time
1231HeFrameExchangeManager::GetTxDuration(uint32_t ppduPayloadSize,
1232 Mac48Address receiver,
1233 const WifiTxParameters& txParams) const
1234{
1235 if (!txParams.m_txVector.IsMu())
1236 {
1237 return VhtFrameExchangeManager::GetTxDuration(ppduPayloadSize, receiver, txParams);
1238 }
1239
1240 NS_ASSERT_MSG(!txParams.m_txVector.IsDlMu() || m_apMac, "DL MU can be done by an AP");
1241 NS_ASSERT_MSG(!txParams.m_txVector.IsUlMu() || m_staMac, "UL MU can be done by a STA");
1242
1243 if (txParams.m_acknowledgment &&
1244 txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1245 {
1246 // we need to account for the size of the aggregated MU-BAR Trigger Frame
1247 auto acknowledgment = static_cast<WifiDlMuAggregateTf*>(txParams.m_acknowledgment.get());
1248
1249 const auto& info = acknowledgment->stationsReplyingWithBlockAck.find(receiver);
1250 NS_ASSERT(info != acknowledgment->stationsReplyingWithBlockAck.end());
1251
1252 ppduPayloadSize =
1253 MpduAggregator::GetSizeIfAggregated(info->second.muBarSize, ppduPayloadSize);
1254 }
1255
1256 uint16_t staId = (txParams.m_txVector.IsDlMu() ? m_apMac->GetAssociationId(receiver, m_linkId)
1257 : m_staMac->GetAssociationId());
1258 Time psduDuration = m_phy->CalculateTxDuration(ppduPayloadSize,
1259 txParams.m_txVector,
1260 m_phy->GetPhyBand(),
1261 staId);
1262
1263 return std::max(psduDuration, txParams.m_txDuration);
1264}
1265
1266void
1267HeFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations)
1268{
1269 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
1270 NS_LOG_FUNCTION(this << psduMap << staMissedTbPpduFrom.size() << nSolicitedStations);
1271
1272 NS_ASSERT(psduMap);
1273 NS_ASSERT(IsTrigger(*psduMap));
1274
1275 // This method is called if some station(s) did not send a TB PPDU
1276 NS_ASSERT(!staMissedTbPpduFrom.empty());
1277 NS_ASSERT(m_edca);
1278
1279 if (staMissedTbPpduFrom.size() == nSolicitedStations)
1280 {
1281 // no station replied, the transmission failed
1282 m_edca->UpdateFailedCw(m_linkId);
1283
1284 TransmissionFailed();
1285 }
1286 else if (!m_multiStaBaEvent.IsRunning())
1287 {
1288 m_edca->ResetCw(m_linkId);
1289 TransmissionSucceeded();
1290 }
1291
1292 m_psduMap.clear();
1293}
1294
1295void
1296HeFrameExchangeManager::BlockAcksInTbPpduTimeout(WifiPsduMap* psduMap,
1297 std::size_t nSolicitedStations)
1298{
1299 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
1300
1301 NS_ASSERT(psduMap);
1302 NS_ASSERT(m_txParams.m_acknowledgment &&
1303 (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF ||
1304 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR));
1305
1306 // This method is called if some station(s) did not send a BlockAck frame in a TB PPDU
1307 const auto& staMissedBlockAckFrom = m_txTimer.GetStasExpectedToRespond();
1308 NS_ASSERT(!staMissedBlockAckFrom.empty());
1309
1310 bool resetCw;
1311
1312 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1313 {
1314 // no station replied, the transmission failed
1315 // call ReportDataFailed to increase SRC/LRC
1316 GetWifiRemoteStationManager()->ReportDataFailed(*psduMap->begin()->second->begin());
1317 resetCw = false;
1318 }
1319 else
1320 {
1321 // the transmission succeeded
1322 resetCw = true;
1323 }
1324
1325 if (m_triggerFrame)
1326 {
1327 // this is strictly needed for DL_MU_TF_MU_BAR only
1328 m_triggerFrame = nullptr;
1329 }
1330
1331 for (const auto& sta : staMissedBlockAckFrom)
1332 {
1333 Ptr<WifiPsdu> psdu = GetPsduTo(sta, *psduMap);
1334 NS_ASSERT(psdu);
1335 // If the QSRC[AC] or the QLRC[AC] has reached dot11ShortRetryLimit or dot11LongRetryLimit
1336 // respectively, CW[AC] shall be reset to CWmin[AC] (sec. 10.22.2.2 of 802.11-2016).
1337 // We should get that psduResetCw is the same for all PSDUs, but the handling of QSRC/QLRC
1338 // needs to be aligned to the specifications.
1339 bool psduResetCw;
1340 MissedBlockAck(psdu, m_txParams.m_txVector, psduResetCw);
1341 resetCw = resetCw || psduResetCw;
1342 }
1343
1344 NS_ASSERT(m_edca);
1345
1346 if (resetCw)
1347 {
1348 m_edca->ResetCw(m_linkId);
1349 }
1350 else
1351 {
1352 m_edca->UpdateFailedCw(m_linkId);
1353 }
1354
1355 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1356 {
1357 // no station replied, the transmission failed
1358 TransmissionFailed();
1359 }
1360 else
1361 {
1362 TransmissionSucceeded();
1363 }
1364 m_psduMap.clear();
1365}
1366
1367void
1368HeFrameExchangeManager::BlockAckAfterTbPpduTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1369{
1370 NS_LOG_FUNCTION(this << *psdu << txVector);
1371
1372 bool resetCw;
1373
1374 // call ReportDataFailed to increase SRC/LRC
1375 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1376
1377 MissedBlockAck(psdu, m_txParams.m_txVector, resetCw);
1378
1379 // This is a PSDU sent in a TB PPDU. An HE STA resumes the EDCA backoff procedure
1380 // without modifying CW or the backoff counter for the associated EDCAF, after
1381 // transmission of an MPDU in a TB PPDU regardless of whether the STA has received
1382 // the corresponding acknowledgment frame in response to the MPDU sent in the TB PPDU
1383 // (Sec. 10.22.2.2 of 11ax Draft 3.0)
1384 m_psduMap.clear();
1385}
1386
1387void
1388HeFrameExchangeManager::NormalAckTimeout(Ptr<WifiMpdu> mpdu, const WifiTxVector& txVector)
1389{
1390 NS_LOG_FUNCTION(this << *mpdu << txVector);
1391
1392 VhtFrameExchangeManager::NormalAckTimeout(mpdu, txVector);
1393
1394 // If a Normal Ack is missed in response to a DL MU PPDU requiring acknowledgment
1395 // in SU format, we have to set the Retry flag for all transmitted MPDUs that have
1396 // not been acknowledged nor discarded and clear m_psduMap since the transmission failed.
1397 for (auto& psdu : m_psduMap)
1398 {
1399 for (auto& mpdu : *PeekPointer(psdu.second))
1400 {
1401 if (mpdu->IsQueued())
1402 {
1403 m_mac->GetTxopQueue(mpdu->GetQueueAc())->GetOriginal(mpdu)->GetHeader().SetRetry();
1404 mpdu->ResetInFlight(m_linkId);
1405 }
1406 }
1407 }
1408 m_psduMap.clear();
1409}
1410
1411void
1412HeFrameExchangeManager::BlockAckTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1413{
1414 NS_LOG_FUNCTION(this << *psdu << txVector);
1415
1416 VhtFrameExchangeManager::BlockAckTimeout(psdu, txVector);
1417
1418 // If a Block Ack is missed in response to a DL MU PPDU requiring acknowledgment
1419 // in SU format, we have to set the Retry flag for all transmitted MPDUs that have
1420 // not been acknowledged nor discarded and clear m_psduMap since the transmission failed.
1421 for (auto& psdu : m_psduMap)
1422 {
1423 for (auto& mpdu : *PeekPointer(psdu.second))
1424 {
1425 if (mpdu->IsQueued())
1426 {
1427 mpdu->GetHeader().SetRetry();
1428 }
1429 }
1430 }
1431 m_psduMap.clear();
1432}
1433
1435HeFrameExchangeManager::GetTrigVector(const CtrlTriggerHeader& trigger) const
1436{
1437 WifiTxVector v;
1438 v.SetPreambleType(trigger.GetVariant() == TriggerFrameVariant::HE ? WIFI_PREAMBLE_HE_TB
1440 v.SetChannelWidth(trigger.GetUlBandwidth());
1442 v.SetLength(trigger.GetUlLength());
1443 for (const auto& userInfoField : trigger)
1444 {
1446 userInfoField.GetAid12(),
1447 {userInfoField.GetRuAllocation(), userInfoField.GetUlMcs(), userInfoField.GetNss()});
1448 }
1449 return v;
1450}
1451
1453HeFrameExchangeManager::GetHeTbTxVector(CtrlTriggerHeader trigger, Mac48Address triggerSender) const
1454{
1455 NS_ASSERT(triggerSender !=
1456 m_self); // TxPower information is used only by STAs, it is useless for the sending AP
1457 // (which can directly use CtrlTriggerHeader::GetHeTbTxVector)
1458 NS_ASSERT(m_staMac);
1459 uint16_t staId = m_staMac->GetAssociationId();
1460 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
1461 NS_ASSERT(userInfoIt != trigger.end());
1462
1463 WifiTxVector v = trigger.GetHeTbTxVector(staId);
1464
1465 Ptr<HeConfiguration> heConfiguration = m_mac->GetHeConfiguration();
1466 NS_ASSERT_MSG(heConfiguration, "This STA has to be an HE station to send an HE TB PPDU");
1467 v.SetBssColor(heConfiguration->GetBssColor());
1468
1469 if (userInfoIt->IsUlTargetRssiMaxTxPower())
1470 {
1471 NS_LOG_LOGIC("AP requested using the max transmit power (" << m_phy->GetTxPowerEnd()
1472 << " dBm)");
1473 v.SetTxPowerLevel(m_phy->GetNTxPower());
1474 return v;
1475 }
1476
1477 uint8_t powerLevel = GetWifiRemoteStationManager()->GetDefaultTxPowerLevel();
1495 auto optRssi = GetMostRecentRssi(triggerSender);
1496 NS_ASSERT(optRssi);
1497 int8_t pathLossDb =
1498 trigger.GetApTxPower() -
1499 static_cast<int8_t>(
1500 *optRssi); // cast RSSI to be on equal footing with AP Tx power information
1501 auto reqTxPowerDbm = static_cast<double>(userInfoIt->GetUlTargetRssi() + pathLossDb);
1502
1503 // Convert the transmit power to a power level
1504 uint8_t numPowerLevels = m_phy->GetNTxPower();
1505 if (numPowerLevels > 1)
1506 {
1507 double stepDbm = (m_phy->GetTxPowerEnd() - m_phy->GetTxPowerStart()) / (numPowerLevels - 1);
1508 powerLevel = static_cast<uint8_t>(
1509 ceil((reqTxPowerDbm - m_phy->GetTxPowerStart()) /
1510 stepDbm)); // better be slightly above so as to satisfy target UL RSSI
1511 if (powerLevel > numPowerLevels)
1512 {
1513 powerLevel = numPowerLevels; // capping will trigger warning below
1514 }
1515 }
1516 if (reqTxPowerDbm > m_phy->GetPowerDbm(powerLevel))
1517 {
1518 NS_LOG_WARN("The requested power level ("
1519 << reqTxPowerDbm << "dBm) cannot be satisfied (max: " << m_phy->GetTxPowerEnd()
1520 << "dBm)");
1521 }
1522 v.SetTxPowerLevel(powerLevel);
1523 NS_LOG_LOGIC("UL power control: "
1524 << "input {pathLoss=" << pathLossDb << "dB, reqTxPower=" << reqTxPowerDbm << "dBm}"
1525 << " output {powerLevel=" << +powerLevel << " -> "
1526 << m_phy->GetPowerDbm(powerLevel) << "dBm}"
1527 << " PHY power capa {min=" << m_phy->GetTxPowerStart() << "dBm, max="
1528 << m_phy->GetTxPowerEnd() << "dBm, levels:" << +numPowerLevels << "}");
1529
1530 return v;
1531}
1532
1533std::optional<double>
1534HeFrameExchangeManager::GetMostRecentRssi(const Mac48Address& address) const
1535{
1536 return GetWifiRemoteStationManager()->GetMostRecentRssi(address);
1537}
1538
1539void
1540HeFrameExchangeManager::SetTargetRssi(CtrlTriggerHeader& trigger) const
1541{
1542 NS_LOG_FUNCTION(this);
1543 NS_ASSERT(m_apMac);
1544
1545 trigger.SetApTxPower(static_cast<int8_t>(
1546 m_phy->GetPowerDbm(GetWifiRemoteStationManager()->GetDefaultTxPowerLevel())));
1547 for (auto& userInfo : trigger)
1548 {
1549 const auto staList = m_apMac->GetStaList(m_linkId);
1550 auto itAidAddr = staList.find(userInfo.GetAid12());
1551 NS_ASSERT(itAidAddr != staList.end());
1552 auto optRssi = GetMostRecentRssi(itAidAddr->second);
1553 NS_ASSERT(optRssi);
1554 auto rssi = static_cast<int8_t>(*optRssi);
1555 rssi = (rssi >= -20)
1556 ? -20
1557 : ((rssi <= -110) ? -110 : rssi); // cap so as to keep within [-110; -20] dBm
1558 userInfo.SetUlTargetRssi(rssi);
1559 }
1560}
1561
1562void
1563HeFrameExchangeManager::PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
1564{
1565 NS_LOG_FUNCTION(this << psdu << txVector);
1566
1567 auto txVectorCopy = txVector;
1568
1569 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsTrigger())
1570 {
1571 CtrlTriggerHeader trigger;
1572 psdu->GetPayload(0)->PeekHeader(trigger);
1573 if (trigger.IsMuRts())
1574 {
1575 const WifiMacHeader& muRts = psdu->GetHeader(0);
1576 // A station receiving an MU-RTS behaves just like as if it received an RTS.
1577 // Determine whether the MU-RTS is addressed to this station or not and
1578 // prepare an "equivalent" RTS frame so that we can reuse the UpdateNav()
1579 // and SetTxopHolder() methods of the parent classes
1580 WifiMacHeader rts;
1582 rts.SetDsNotFrom();
1583 rts.SetDsNotTo();
1584 rts.SetDuration(muRts.GetDuration());
1585 rts.SetAddr2(muRts.GetAddr2());
1586 if (m_staMac != nullptr && m_staMac->IsAssociated() &&
1587 muRts.GetAddr2() == m_bssid // sent by the AP this STA is associated with
1588 && trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) != trigger.end())
1589 {
1590 // the MU-RTS is addressed to this station
1591 rts.SetAddr1(m_self);
1592 }
1593 else
1594 {
1595 rts.SetAddr1(muRts.GetAddr2()); // an address different from that of this station
1596 }
1597 psdu = Create<const WifiPsdu>(Create<Packet>(), rts);
1598 // The duration of the NAV reset timeout has to take into account that the CTS
1599 // response is sent using the 6 Mbps data rate
1600 txVectorCopy =
1601 GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1602 }
1603 }
1604 VhtFrameExchangeManager::PostProcessFrame(psdu, txVectorCopy);
1605}
1606
1607void
1608HeFrameExchangeManager::SendCtsAfterMuRts(const WifiMacHeader& muRtsHdr,
1609 const CtrlTriggerHeader& trigger,
1610 double muRtsSnr)
1611{
1612 NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
1613
1614 if (!UlMuCsMediumIdle(trigger))
1615 {
1616 NS_LOG_DEBUG("UL MU CS indicated medium busy, cannot send CTS");
1617 return;
1618 }
1619
1620 NS_ASSERT(m_staMac != nullptr && m_staMac->IsAssociated());
1621 WifiTxVector ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
1622 ctsTxVector.SetTriggerResponding(true);
1623
1624 DoSendCtsAfterRts(muRtsHdr, ctsTxVector, muRtsSnr);
1625}
1626
1627void
1628HeFrameExchangeManager::SendMultiStaBlockAck(const WifiTxParameters& txParams, Time durationId)
1629{
1630 NS_LOG_FUNCTION(this << &txParams << durationId.As(Time::US));
1631
1632 NS_ASSERT(m_apMac);
1633 NS_ASSERT(txParams.m_acknowledgment &&
1634 txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1635 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(txParams.m_acknowledgment.get());
1636
1637 NS_ASSERT(!acknowledgment->stationsReceivingMultiStaBa.empty());
1638
1639 CtrlBAckResponseHeader blockAck;
1640 blockAck.SetType(acknowledgment->baType);
1641
1642 Mac48Address receiver;
1643
1644 for (const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa)
1645 {
1646 receiver = staInfo.first.first;
1647 uint8_t tid = staInfo.first.second;
1648 std::size_t index = staInfo.second;
1649
1650 blockAck.SetAid11(m_apMac->GetAssociationId(receiver, m_linkId), index);
1651 blockAck.SetTidInfo(tid, index);
1652
1653 if (tid == 14)
1654 {
1655 // All-ack context
1656 NS_LOG_DEBUG("Multi-STA Block Ack: Sending All-ack to=" << receiver);
1657 blockAck.SetAckType(true, index);
1658 continue;
1659 }
1660
1661 if (acknowledgment->baType.m_bitmapLen.at(index) == 0)
1662 {
1663 // Acknowledgment context
1664 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Ack to=" << receiver);
1665 blockAck.SetAckType(true, index);
1666 }
1667 else
1668 {
1669 // Block acknowledgment context
1670 blockAck.SetAckType(false, index);
1671
1672 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(receiver, tid);
1673 NS_ASSERT(agreement);
1674 agreement->get().FillBlockAckBitmap(&blockAck, index);
1675 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Block Ack with seq="
1676 << blockAck.GetStartingSequence(index) << " to=" << receiver
1677 << " tid=" << +tid);
1678 }
1679 }
1680
1681 WifiMacHeader hdr;
1683 hdr.SetAddr1(acknowledgment->stationsReceivingMultiStaBa.size() == 1
1684 ? receiver
1685 : Mac48Address::GetBroadcast());
1686 hdr.SetAddr2(m_self);
1687 hdr.SetDsNotFrom();
1688 hdr.SetDsNotTo();
1689
1690 Ptr<Packet> packet = Create<Packet>();
1691 packet->AddHeader(blockAck);
1692 Ptr<WifiPsdu> psdu =
1693 GetWifiPsdu(Create<WifiMpdu>(packet, hdr), acknowledgment->multiStaBaTxVector);
1694
1695 Time txDuration = m_phy->CalculateTxDuration(GetBlockAckSize(acknowledgment->baType),
1696 acknowledgment->multiStaBaTxVector,
1697 m_phy->GetPhyBand());
1708 NS_ASSERT(m_edca);
1709 if (m_edca->GetTxopLimit(m_linkId).IsZero())
1710 {
1711 // single protection settings
1712 psdu->SetDuration(Max(durationId - m_phy->GetSifs() - txDuration, Seconds(0)));
1713 }
1714 else
1715 {
1716 // multiple protection settings
1717 psdu->SetDuration(Max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0)));
1718 }
1719
1720 psdu->GetPayload(0)->AddPacketTag(m_muSnrTag);
1721
1722 ForwardPsduDown(psdu, acknowledgment->multiStaBaTxVector);
1723
1724 // continue with the TXOP if time remains
1725 m_psduMap.clear();
1726 m_edca->ResetCw(m_linkId);
1727 m_muSnrTag.Reset();
1728 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded, this);
1729}
1730
1731void
1732HeFrameExchangeManager::ReceiveBasicTrigger(const CtrlTriggerHeader& trigger,
1733 const WifiMacHeader& hdr)
1734{
1735 NS_LOG_FUNCTION(this << trigger << hdr);
1736 NS_ASSERT(trigger.IsBasic());
1737 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1738
1739 NS_LOG_DEBUG("Received a Trigger Frame (basic variant) soliciting a transmission");
1740
1741 if (!UlMuCsMediumIdle(trigger))
1742 {
1743 return;
1744 }
1745
1746 // Starting from the Preferred AC indicated in the Trigger Frame, check if there
1747 // is either a pending BlockAckReq frame or a data frame that can be transmitted
1748 // in the allocated time and is addressed to a station with which a Block Ack
1749 // agreement has been established.
1750
1751 // create the sequence of TIDs to check
1752 std::vector<uint8_t> tids;
1753 uint16_t staId = m_staMac->GetAssociationId();
1754 AcIndex preferredAc = trigger.FindUserInfoWithAid(staId)->GetPreferredAc();
1755 auto acIt = wifiAcList.find(preferredAc);
1756 for (uint8_t i = 0; i < 4; i++)
1757 {
1758 NS_ASSERT(acIt != wifiAcList.end());
1759 tids.push_back(acIt->second.GetHighTid());
1760 tids.push_back(acIt->second.GetLowTid());
1761
1762 acIt++;
1763 if (acIt == wifiAcList.end())
1764 {
1765 acIt = wifiAcList.begin();
1766 }
1767 }
1768
1769 Ptr<WifiPsdu> psdu;
1770 WifiTxParameters txParams;
1771 WifiTxVector tbTxVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1772 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
1773 tbTxVector,
1774 m_phy->GetPhyBand());
1775
1776 for (const auto& tid : tids)
1777 {
1778 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1779
1780 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
1781 {
1782 // no Block Ack agreement established for this TID
1783 continue;
1784 }
1785
1786 txParams.Clear();
1787 txParams.m_txVector = tbTxVector;
1788
1789 // first, check if there is a pending BlockAckReq frame
1790 if (auto mpdu = GetBar(edca->GetAccessCategory(), tid, hdr.GetAddr2());
1791 mpdu && TryAddMpdu(mpdu, txParams, ppduDuration))
1792 {
1793 NS_LOG_DEBUG("Sending a BAR within a TB PPDU");
1794 psdu = Create<WifiPsdu>(mpdu, true);
1795 break;
1796 }
1797
1798 // otherwise, check if a suitable data frame is available
1799 auto receiver =
1800 GetWifiRemoteStationManager()->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2());
1801 if (auto mpdu = edca->PeekNextMpdu(m_linkId, tid, receiver))
1802 {
1803 mpdu = CreateAliasIfNeeded(mpdu);
1804 if (auto item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration, false))
1805 {
1806 // try A-MPDU aggregation
1807 std::vector<Ptr<WifiMpdu>> mpduList =
1808 m_mpduAggregator->GetNextAmpdu(item, txParams, ppduDuration);
1809 psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1810 : Create<WifiPsdu>(item, true));
1811 break;
1812 }
1813 }
1814 }
1815
1816 if (psdu)
1817 {
1818 psdu->SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1819 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1820 }
1821 else
1822 {
1823 // send QoS Null frames
1824 SendQosNullFramesInTbPpdu(trigger, hdr);
1825 }
1826}
1827
1828void
1829HeFrameExchangeManager::SendQosNullFramesInTbPpdu(const CtrlTriggerHeader& trigger,
1830 const WifiMacHeader& hdr)
1831{
1832 NS_LOG_FUNCTION(this << trigger << hdr);
1833 NS_ASSERT(trigger.IsBasic() || trigger.IsBsrp());
1834 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1835
1836 NS_LOG_DEBUG("Requested to send QoS Null frames");
1837
1838 if (!UlMuCsMediumIdle(trigger))
1839 {
1840 return;
1841 }
1842
1843 WifiMacHeader header;
1845 header.SetAddr1(hdr.GetAddr2());
1846 header.SetAddr2(m_self);
1847 header.SetAddr3(hdr.GetAddr2());
1848 header.SetDsTo();
1849 header.SetDsNotFrom();
1850 // TR3: Sequence numbers for transmitted QoS (+)Null frames may be set
1851 // to any value. (Table 10-3 of 802.11-2016)
1852 header.SetSequenceNumber(0);
1853 // Set the EOSP bit so that NotifyTxToEdca will add the Queue Size
1854 header.SetQosEosp();
1855
1856 WifiTxParameters txParams;
1857 txParams.m_txVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1858 txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
1859 txParams.m_acknowledgment = std::unique_ptr<WifiAcknowledgment>(new WifiNoAck);
1860
1861 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
1862 txParams.m_txVector,
1863 m_phy->GetPhyBand());
1864 header.SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1865
1866 Ptr<WifiMpdu> mpdu;
1867 std::vector<Ptr<WifiMpdu>> mpduList;
1868 uint8_t tid = 0;
1869 header.SetQosTid(tid);
1870
1871 while (tid < 8 &&
1872 IsWithinSizeAndTimeLimits(
1873 txParams.GetSizeIfAddMpdu(mpdu = Create<WifiMpdu>(Create<Packet>(), header)),
1874 hdr.GetAddr2(),
1875 txParams,
1876 ppduDuration))
1877 {
1878 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
1879 {
1880 NS_LOG_DEBUG("Skipping tid=" << +tid << " because no agreement established");
1881 header.SetQosTid(++tid);
1882 continue;
1883 }
1884
1885 NS_LOG_DEBUG("Aggregating a QoS Null frame with tid=" << +tid);
1886 // We could call TryAddMpdu instead of IsWithinSizeAndTimeLimits above in order to
1887 // get the TX parameters updated automatically. However, aggregating the QoS Null
1888 // frames might fail because MPDU aggregation is disabled by default for VO
1889 // and BK. Therefore, we skip the check on max A-MPDU size and only update the
1890 // TX parameters below.
1891 txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
1892 txParams.AddMpdu(mpdu);
1893 UpdateTxDuration(mpdu->GetHeader().GetAddr1(), txParams);
1894 mpduList.push_back(mpdu);
1895 header.SetQosTid(++tid);
1896 }
1897
1898 if (mpduList.empty())
1899 {
1900 NS_LOG_DEBUG("Not enough time to send a QoS Null frame");
1901 return;
1902 }
1903
1904 Ptr<WifiPsdu> psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1905 : Create<WifiPsdu>(mpduList.front(), true));
1906 uint16_t staId = m_staMac->GetAssociationId();
1907 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1908}
1909
1910void
1911HeFrameExchangeManager::ReceiveMuBarTrigger(const CtrlTriggerHeader& trigger,
1912 uint8_t tid,
1913 Time durationId,
1914 double snr)
1915{
1916 NS_LOG_FUNCTION(this << trigger << tid << durationId.As(Time::US) << snr);
1917
1918 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(m_bssid, tid);
1919
1920 if (!agreement)
1921 {
1922 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1923 return;
1924 }
1925
1926 if (!UlMuCsMediumIdle(trigger))
1927 {
1928 return;
1929 }
1930
1931 NS_LOG_DEBUG("Send Block Ack in TB PPDU");
1932 auto txVector = GetHeTbTxVector(trigger, m_bssid);
1933 SendBlockAck(*agreement, durationId, txVector, snr);
1934}
1935
1936bool
1937HeFrameExchangeManager::IsIntraBssPpdu(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) const
1938{
1939 NS_LOG_FUNCTION(this << psdu << txVector);
1940
1941 // "If, based on the MAC address information of a frame carried in a received PPDU, the
1942 // received PPDU satisfies both intra-BSS and inter-BSS conditions, then the received PPDU is
1943 // classified as an intra-BSS PPDU." (Sec. 26.2.2 of 802.11ax-2021)
1944 // Hence, check first if the intra-BSS conditions using MAC address information are satisfied:
1945 // 1. "The PPDU carries a frame that has an RA, TA, or BSSID field value that is equal to
1946 // the BSSID of the BSS in which the STA is associated"
1947 const auto ra = psdu->GetAddr1();
1948 const auto ta = psdu->GetAddr2();
1949 const auto bssid = psdu->GetHeader(0).GetAddr3();
1950 const auto empty = Mac48Address();
1951
1952 if (ra == m_bssid || ta == m_bssid || bssid == m_bssid)
1953 {
1954 return true;
1955 }
1956
1957 // 2. "The PPDU carries a Control frame that does not have a TA field and that has an
1958 // RA field value that matches the saved TXOP holder address of the BSS in which
1959 // the STA is associated"
1960 if (psdu->GetHeader(0).IsCtl() && ta == empty && ra == m_txopHolder)
1961 {
1962 return true;
1963 }
1964
1965 // If we get here, the intra-BSS conditions using MAC address information are not satisfied.
1966 // "If the received PPDU satisfies the intra-BSS conditions using the RXVECTOR parameter
1967 // BSS_COLOR and also satisfies the inter-BSS conditions using MAC address information of a
1968 // frame carried in the PPDU, then the classification made using the MAC address information
1969 // takes precedence."
1970 // Hence, if the inter-BSS conditions using MAC address information are satisfied, the frame
1971 // is classified as inter-BSS
1972 // 1. "The PPDU carries a frame that has a BSSID field, the value of which is not the BSSID
1973 // of the BSS in which the STA is associated"
1974 if (bssid != empty && bssid != m_bssid)
1975 {
1976 return false;
1977 }
1978
1979 // 2. The PPDU carries a frame that does not have a BSSID field but has both an RA field and
1980 // TA field, neither value of which is equal to the BSSID of the BSS in which the STA is
1981 // associated
1982 if (bssid == empty && ta != empty && ra != empty && ta != m_bssid && ra != m_bssid)
1983 {
1984 return false;
1985 }
1986
1987 // If we get here, both intra-BSS and inter-bss conditions using MAC address information
1988 // are not satisfied. Hence, the frame is classified as intra-BSS if the intra-BSS conditions
1989 // using the RXVECTOR parameters are satisfied:
1990 // 1. The RXVECTOR parameter BSS_COLOR of the PPDU carrying the frame is the BSS color of the
1991 // BSS of which the STA is a member
1992 // This condition is used if the BSS is not disabled ("If a STA determines that the BSS color
1993 // is disabled (see 26.17.3.3), then the RXVECTOR parameter BSS_COLOR of a PPDU shall not be
1994 // used to classify the PPDU")
1995 const auto bssColor = m_mac->GetHeConfiguration()->GetBssColor();
1996
1997 // the other two conditions using the RXVECTOR parameter PARTIAL_AID are not implemented
1998 return bssColor != 0 && bssColor == txVector.GetBssColor();
1999}
2000
2001void
2002HeFrameExchangeManager::UpdateNav(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
2003{
2004 NS_LOG_FUNCTION(this << psdu << txVector);
2005
2006 if (!psdu->HasNav())
2007 {
2008 return;
2009 }
2010
2011 if (psdu->GetAddr1() == m_self)
2012 {
2013 // When the received frame’s RA is equal to the STA’s own MAC address, the STA
2014 // shall not update its NAV (IEEE 802.11-2020, sec. 10.3.2.4)
2015 return;
2016 }
2017
2018 // The intra-BSS NAV is updated by an intra-BSS PPDU. The basic NAV is updated by an
2019 // inter-BSS PPDU or a PPDU that cannot be classified as intra-BSS or inter-BSS.
2020 // (Section 26.2.4 of 802.11ax-2021)
2021 if (!IsIntraBssPpdu(psdu, txVector))
2022 {
2023 NS_LOG_DEBUG("PPDU not classified as intra-BSS, update the basic NAV");
2024 VhtFrameExchangeManager::UpdateNav(psdu, txVector);
2025 return;
2026 }
2027
2028 NS_LOG_DEBUG("PPDU classified as intra-BSS, update the intra-BSS NAV");
2029 Time duration = psdu->GetDuration();
2030 NS_LOG_DEBUG("Duration/ID=" << duration);
2031
2032 if (psdu->GetHeader(0).IsCfEnd())
2033 {
2034 // An HE STA that maintains two NAVs (see 26.2.4) and receives a CF-End frame should reset
2035 // the basic NAV if the received CF-End frame is carried in an inter-BSS PPDU and reset the
2036 // intra-BSS NAV if the received CF-End frame is carried in an intra-BSS PPDU. (Sec. 26.2.5
2037 // of 802.11ax-2021)
2038 NS_LOG_DEBUG("Received CF-End, resetting the intra-BSS NAV");
2039 IntraBssNavResetTimeout();
2040 return;
2041 }
2042
2043 // For all other received frames the STA shall update its NAV when the received
2044 // Duration is greater than the STA’s current NAV value (IEEE 802.11-2020 sec. 10.3.2.4)
2045 auto intraBssNavEnd = Simulator::Now() + duration;
2046 if (intraBssNavEnd > m_intraBssNavEnd)
2047 {
2048 m_intraBssNavEnd = intraBssNavEnd;
2049 NS_LOG_DEBUG("Updated intra-BSS NAV=" << m_intraBssNavEnd);
2050
2051 // A STA that used information from an RTS frame as the most recent basis to update
2052 // its NAV setting is permitted to reset its NAV if no PHY-RXSTART.indication
2053 // primitive is received from the PHY during a NAVTimeout period starting when the
2054 // MAC receives a PHY-RXEND.indication primitive corresponding to the detection of
2055 // the RTS frame. NAVTimeout period is equal to:
2056 // (2 x aSIFSTime) + (CTS_Time) + aRxPHYStartDelay + (2 x aSlotTime)
2057 // The “CTS_Time” shall be calculated using the length of the CTS frame and the data
2058 // rate at which the RTS frame used for the most recent NAV update was received
2059 // (IEEE 802.11-2016 sec. 10.3.2.4)
2060 if (psdu->GetHeader(0).IsRts())
2061 {
2062 WifiTxVector ctsTxVector =
2063 GetWifiRemoteStationManager()->GetCtsTxVector(psdu->GetAddr2(), txVector.GetMode());
2064 auto navResetDelay =
2065 2 * m_phy->GetSifs() +
2066 WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
2067 m_phy->CalculatePhyPreambleAndHeaderDuration(ctsTxVector) + 2 * m_phy->GetSlot();
2068 m_intraBssNavResetEvent =
2069 Simulator::Schedule(navResetDelay,
2070 &HeFrameExchangeManager::IntraBssNavResetTimeout,
2071 this);
2072 }
2073 }
2074 NS_LOG_DEBUG("Current intra-BSS NAV=" << m_intraBssNavEnd);
2075
2076 m_channelAccessManager->NotifyNavStartNow(duration);
2077}
2078
2079void
2080HeFrameExchangeManager::ClearTxopHolderIfNeeded()
2081{
2082 NS_LOG_FUNCTION(this);
2083 if (m_intraBssNavEnd <= Simulator::Now())
2084 {
2085 m_txopHolder.reset();
2086 }
2087}
2088
2089void
2090HeFrameExchangeManager::NavResetTimeout()
2091{
2092 NS_LOG_FUNCTION(this);
2093 m_navEnd = Simulator::Now();
2094 // Do not reset the TXOP holder because the basic NAV is updated by inter-BSS frames
2095 // The NAV seen by the ChannelAccessManager is now the intra-BSS NAV only
2096 Time intraBssNav = Simulator::GetDelayLeft(m_intraBssNavResetEvent);
2097 m_channelAccessManager->NotifyNavResetNow(intraBssNav);
2098}
2099
2100void
2101HeFrameExchangeManager::IntraBssNavResetTimeout()
2102{
2103 NS_LOG_FUNCTION(this);
2104 m_intraBssNavEnd = Simulator::Now();
2105 ClearTxopHolderIfNeeded();
2106 // The NAV seen by the ChannelAccessManager is now the basic NAV only
2107 Time basicNav = Simulator::GetDelayLeft(m_navResetEvent);
2108 m_channelAccessManager->NotifyNavResetNow(basicNav);
2109}
2110
2111void
2112HeFrameExchangeManager::SetTxopHolder(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
2113{
2114 NS_LOG_FUNCTION(this << psdu << txVector);
2115
2116 if (psdu->GetHeader(0).IsTrigger() && psdu->GetAddr2() == m_bssid)
2117 {
2118 m_txopHolder = m_bssid;
2119 }
2120 else if (!txVector.IsUlMu()) // the sender of a TB PPDU is not the TXOP holder
2121 {
2122 VhtFrameExchangeManager::SetTxopHolder(psdu, txVector);
2123 }
2124}
2125
2126bool
2127HeFrameExchangeManager::VirtualCsMediumIdle() const
2128{
2129 // For an HE STA maintaining two NAVs, if both the NAV timers are 0, the virtual CS indication
2130 // is that the medium is idle; if at least one of the two NAV timers is nonzero, the virtual CS
2131 // indication is that the medium is busy. (Sec. 26.2.4 of 802.11ax-2021)
2132 return m_navEnd <= Simulator::Now() && m_intraBssNavEnd <= Simulator::Now();
2133}
2134
2135bool
2136HeFrameExchangeManager::UlMuCsMediumIdle(const CtrlTriggerHeader& trigger) const
2137{
2138 if (!trigger.GetCsRequired())
2139 {
2140 NS_LOG_DEBUG("CS not required");
2141 return true;
2142 }
2143
2144 // A non-AP STA does not consider the intra-BSS NAV in determining whether to respond to a
2145 // Trigger frame sent by the AP with which the non-AP STA is associated.
2146 // A non-AP STA considers the basic NAV in determining whether to respond to a Trigger frame
2147 // sent by the AP with which the non-AP STA is associated. (Sec. 26.5.2.5 of 802.11ax-2021)
2148 const Time now = Simulator::Now();
2149 if (m_navEnd > now)
2150 {
2151 NS_LOG_DEBUG("Basic NAV indicates medium busy");
2152 return false;
2153 }
2154
2155 NS_ASSERT_MSG(m_staMac, "UL MU CS is only performed by non-AP STAs");
2156 const auto userInfoIt = trigger.FindUserInfoWithAid(m_staMac->GetAssociationId());
2157 NS_ASSERT_MSG(userInfoIt != trigger.end(),
2158 "No User Info field for STA (" << m_self
2159 << ") AID=" << m_staMac->GetAssociationId());
2160
2161 std::set<uint8_t> indices;
2162
2163 if (trigger.IsMuRts())
2164 {
2165 auto ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
2166 auto bw = ctsTxVector.GetChannelWidth();
2167 indices = m_phy->GetOperatingChannel().GetAll20MHzChannelIndicesInPrimary(bw);
2168 }
2169 else
2170 {
2171 indices =
2172 m_phy->GetOperatingChannel().Get20MHzIndicesCoveringRu(userInfoIt->GetRuAllocation(),
2173 trigger.GetUlBandwidth());
2174 }
2175 return !m_channelAccessManager->GetPer20MHzBusy(indices);
2176}
2177
2178void
2179HeFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
2180 RxSignalInfo rxSignalInfo,
2181 const WifiTxVector& txVector,
2182 bool inAmpdu)
2183{
2184 // The received MPDU is either broadcast or addressed to this station
2185 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
2186
2187 const WifiMacHeader& hdr = mpdu->GetHeader();
2188
2189 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2190 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2191 {
2192 Mac48Address sender = hdr.GetAddr2();
2193 NS_ASSERT(m_txParams.m_acknowledgment &&
2194 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2195 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
2196 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
2197
2198 if (m_txTimer.GetStasExpectedToRespond().count(sender) == 0)
2199 {
2200 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2201 return;
2202 }
2203
2204 if (hdr.IsBlockAckReq())
2205 {
2206 NS_LOG_DEBUG("Received a BlockAckReq in a TB PPDU from " << sender);
2207
2208 CtrlBAckRequestHeader blockAckReq;
2209 mpdu->GetPacket()->PeekHeader(blockAckReq);
2210 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
2211 uint8_t tid = blockAckReq.GetTidInfo();
2212 GetBaManager(tid)->NotifyGotBlockAckRequest(
2213 m_mac->GetMldAddress(sender).value_or(sender),
2214 tid,
2215 blockAckReq.GetStartingSequence());
2216
2217 // Block Acknowledgment context
2218 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2219 acknowledgment->baType.m_bitmapLen.push_back(
2220 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2221 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2222 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2223 }
2224 else if (hdr.IsQosData() && !inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
2225 {
2226 NS_LOG_DEBUG("Received an S-MPDU in a TB PPDU from " << sender << " (" << *mpdu << ")");
2227
2228 uint8_t tid = hdr.GetQosTid();
2229 GetBaManager(tid)->NotifyGotMpdu(mpdu);
2230
2231 // Acknowledgment context of Multi-STA Block Acks
2232 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2233 acknowledgment->baType.m_bitmapLen.push_back(0);
2234 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2235 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2236 }
2237 else if (!(hdr.IsQosData() && !hdr.HasData() && !inAmpdu))
2238 {
2239 // The other case handled by this function is when we receive a QoS Null frame
2240 // that is not in an A-MPDU. For all other cases, the reception is handled by
2241 // parent classes. In particular, in case of a QoS data frame in A-MPDU, we
2242 // have to wait until the A-MPDU reception is completed, but we let the
2243 // parent classes notify the Block Ack agreement of the reception of this MPDU
2244 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2245 return;
2246 }
2247
2248 // Schedule the transmission of a Multi-STA BlockAck frame if needed
2249 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsRunning())
2250 {
2251 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2252 &HeFrameExchangeManager::SendMultiStaBlockAck,
2253 this,
2254 std::cref(m_txParams),
2255 mpdu->GetHeader().GetDuration());
2256 }
2257
2258 // remove the sender from the set of stations that are expected to send a TB PPDU
2259 m_txTimer.GotResponseFrom(sender);
2260
2261 if (m_txTimer.GetStasExpectedToRespond().empty())
2262 {
2263 // we do not expect any other BlockAck frame
2264 m_txTimer.Cancel();
2265 m_channelAccessManager->NotifyAckTimeoutResetNow();
2266
2267 if (!m_multiStaBaEvent.IsRunning())
2268 {
2269 // all of the stations that replied with a TB PPDU sent QoS Null frames.
2270 NS_LOG_DEBUG("Continue the TXOP");
2271 m_psduMap.clear();
2272 m_edca->ResetCw(m_linkId);
2273 TransmissionSucceeded();
2274 }
2275 }
2276
2277 // the received TB PPDU has been processed
2278 return;
2279 }
2280
2281 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2282 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF &&
2283 !inAmpdu) // if in A-MPDU, processing is done at the end of A-MPDU reception
2284 {
2285 const auto& sender = hdr.GetAddr2();
2286
2287 if (m_txTimer.GetStasExpectedToRespond().count(sender) == 0)
2288 {
2289 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2290 return;
2291 }
2292 if (!(hdr.IsQosData() && !hdr.HasData()))
2293 {
2294 NS_LOG_WARN("No QoS Null frame in the received MPDU");
2295 return;
2296 }
2297
2298 NS_LOG_DEBUG("Received a QoS Null frame in a TB PPDU from " << sender);
2299
2300 // remove the sender from the set of stations that are expected to send a TB PPDU
2301 m_txTimer.GotResponseFrom(sender);
2302
2303 if (m_txTimer.GetStasExpectedToRespond().empty())
2304 {
2305 // we do not expect any other response
2306 m_txTimer.Cancel();
2307 m_channelAccessManager->NotifyAckTimeoutResetNow();
2308
2309 NS_ASSERT(m_edca);
2310 m_psduMap.clear();
2311 m_edca->ResetCw(m_linkId);
2312 TransmissionSucceeded();
2313 }
2314
2315 // the received TB PPDU has been processed
2316 return;
2317 }
2318
2319 if (hdr.IsCtl())
2320 {
2321 if (hdr.IsCts() && m_txTimer.IsRunning() &&
2322 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psduMap.size() == 1)
2323 {
2324 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
2325 NS_ASSERT(hdr.GetAddr1() == m_self);
2326
2327 Mac48Address sender = m_psduMap.begin()->second->GetAddr1();
2328 NS_LOG_DEBUG("Received CTS from=" << sender);
2329
2330 SnrTag tag;
2331 mpdu->GetPacket()->PeekPacketTag(tag);
2332 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2333 GetWifiRemoteStationManager()->ReportRtsOk(m_psduMap.begin()->second->GetHeader(0),
2334 rxSignalInfo.snr,
2335 txVector.GetMode(),
2336 tag.Get());
2337
2338 m_txTimer.Cancel();
2339 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2340 Simulator::Schedule(m_phy->GetSifs(),
2341 &HeFrameExchangeManager::ProtectionCompleted,
2342 this);
2343 }
2344 else if (hdr.IsCts() && m_txTimer.IsRunning() &&
2345 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS_AFTER_MU_RTS)
2346 {
2347 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
2348 NS_ASSERT(hdr.GetAddr1() == m_self);
2349
2350 NS_LOG_DEBUG("Received a CTS frame in response to an MU-RTS");
2351
2352 m_txTimer.Cancel();
2353 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2354 Simulator::Schedule(m_phy->GetSifs(),
2355 &HeFrameExchangeManager::ProtectionCompleted,
2356 this);
2357 }
2358 else if (hdr.IsAck() && m_txTimer.IsRunning() &&
2359 m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
2360 {
2361 NS_ASSERT(hdr.GetAddr1() == m_self);
2362 NS_ASSERT(m_txParams.m_acknowledgment);
2363 NS_ASSERT(m_txParams.m_acknowledgment->method ==
2364 WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE);
2365
2366 auto acknowledgment =
2367 static_cast<WifiDlMuBarBaSequence*>(m_txParams.m_acknowledgment.get());
2368 NS_ASSERT(acknowledgment->stationsReplyingWithNormalAck.size() == 1);
2369 NS_ASSERT(m_apMac);
2370 uint16_t staId = m_apMac->GetAssociationId(
2371 acknowledgment->stationsReplyingWithNormalAck.begin()->first,
2372 m_linkId);
2373 auto it = m_psduMap.find(staId);
2374 NS_ASSERT(it != m_psduMap.end());
2375 NS_ASSERT(it->second->GetAddr1() ==
2376 acknowledgment->stationsReplyingWithNormalAck.begin()->first);
2377 SnrTag tag;
2378 mpdu->GetPacket()->PeekPacketTag(tag);
2379 ReceivedNormalAck(*it->second->begin(),
2380 m_txParams.m_txVector,
2381 txVector,
2382 rxSignalInfo,
2383 tag.Get());
2384 m_psduMap.clear();
2385 }
2386 // TODO the PHY should not pass us a non-TB PPDU if we are waiting for a
2387 // TB PPDU. However, processing the PHY header is done by the PHY entity
2388 // corresponding to the modulation class of the PPDU being received, hence
2389 // it is not possible to check if a valid TRIGVECTOR is stored when receiving
2390 // PPDUs of older modulation classes. Therefore, we check here that we are
2391 // actually receiving a TB PPDU.
2392 else if (hdr.IsBlockAck() && txVector.IsUlMu() && m_txTimer.IsRunning() &&
2393 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU)
2394 {
2395 Mac48Address sender = hdr.GetAddr2();
2396 NS_LOG_DEBUG("Received BlockAck in TB PPDU from=" << sender);
2397
2398 SnrTag tag;
2399 mpdu->GetPacket()->PeekPacketTag(tag);
2400
2401 // notify the Block Ack Manager
2402 CtrlBAckResponseHeader blockAck;
2403 mpdu->GetPacket()->PeekHeader(blockAck);
2404 uint8_t tid = blockAck.GetTidInfo();
2405 std::pair<uint16_t, uint16_t> ret =
2406 GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
2407 blockAck,
2408 m_mac->GetMldAddress(sender).value_or(sender),
2409 {tid});
2410 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
2411 ret.first,
2412 ret.second,
2413 rxSignalInfo.snr,
2414 tag.Get(),
2415 m_txParams.m_txVector);
2416
2417 // remove the sender from the set of stations that are expected to send a BlockAck
2418 if (m_txTimer.GetStasExpectedToRespond().count(sender) == 0)
2419 {
2420 NS_LOG_WARN("Received a BlockAck from an unexpected stations: " << sender);
2421 return;
2422 }
2423
2424 m_txTimer.GotResponseFrom(sender);
2425
2426 if (m_txTimer.GetStasExpectedToRespond().empty())
2427 {
2428 // we do not expect any other BlockAck frame
2429 m_txTimer.Cancel();
2430 m_channelAccessManager->NotifyAckTimeoutResetNow();
2431 if (m_triggerFrame)
2432 {
2433 // this is strictly needed for DL_MU_TF_MU_BAR only
2434 m_triggerFrame = nullptr;
2435 }
2436
2437 m_edca->ResetCw(m_linkId);
2438 m_psduMap.clear();
2439 TransmissionSucceeded();
2440 }
2441 }
2442 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
2443 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
2444 {
2445 CtrlBAckResponseHeader blockAck;
2446 mpdu->GetPacket()->PeekHeader(blockAck);
2447
2448 NS_ABORT_MSG_IF(!blockAck.IsMultiSta(),
2449 "A Multi-STA BlockAck is expected after a TB PPDU");
2450 NS_LOG_DEBUG("Received a Multi-STA BlockAck from=" << hdr.GetAddr2());
2451
2452 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
2453 if (hdr.GetAddr2() != m_bssid)
2454 {
2455 NS_LOG_DEBUG("The sender is not the AP we are associated with");
2456 return;
2457 }
2458
2459 uint16_t staId = m_staMac->GetAssociationId();
2460 std::vector<uint32_t> indices = blockAck.FindPerAidTidInfoWithAid(staId);
2461
2462 if (indices.empty())
2463 {
2464 NS_LOG_DEBUG("No Per AID TID Info subfield intended for me");
2465 return;
2466 }
2467
2468 MuSnrTag tag;
2469 mpdu->GetPacket()->PeekPacketTag(tag);
2470
2471 // notify the Block Ack Manager
2472 for (const auto& index : indices)
2473 {
2474 uint8_t tid = blockAck.GetTidInfo(index);
2475
2476 if (blockAck.GetAckType(index) && tid < 8)
2477 {
2478 // Acknowledgment context
2479 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2480 GetBaManager(tid)->NotifyGotAck(m_linkId, *m_psduMap.at(staId)->begin());
2481 }
2482 else
2483 {
2484 // Block Acknowledgment or All-ack context
2485 if (blockAck.GetAckType(index) && tid == 14)
2486 {
2487 // All-ack context, we need to determine the actual TID(s) of the PSDU
2488 NS_ASSERT(indices.size() == 1);
2489 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2490 std::set<uint8_t> tids = m_psduMap.at(staId)->GetTids();
2491 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not supported yet");
2492 tid = *tids.begin();
2493 }
2494
2495 std::pair<uint16_t, uint16_t> ret = GetBaManager(tid)->NotifyGotBlockAck(
2496 m_linkId,
2497 blockAck,
2498 m_mac->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2()),
2499 {tid},
2500 index);
2501 GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
2502 ret.first,
2503 ret.second,
2504 rxSignalInfo.snr,
2505 tag.Get(staId),
2506 m_txParams.m_txVector);
2507 }
2508
2509 if (m_psduMap.at(staId)->GetHeader(0).IsQosData() &&
2510 (blockAck.GetAckType(index) // Ack or All-ack context
2511 || std::any_of(blockAck.GetBitmap(index).begin(),
2512 blockAck.GetBitmap(index).end(),
2513 [](uint8_t b) { return b != 0; })))
2514 {
2515 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).HasData());
2516 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).GetQosTid() == tid);
2517 // the station has received a response from the AP for the HE TB PPDU
2518 // transmitted in response to a Basic Trigger Frame and at least one
2519 // MPDU was acknowledged. Therefore, it needs to update the access
2520 // parameters if it received an MU EDCA Parameter Set element.
2521 m_mac->GetQosTxop(tid)->StartMuEdcaTimerNow(m_linkId);
2522 }
2523 }
2524
2525 // cancel the timer
2526 m_txTimer.Cancel();
2527 m_channelAccessManager->NotifyAckTimeoutResetNow();
2528 // dequeue BlockAckReq frames included in acknowledged TB PPDUs (if any)
2529 for (const auto& [staId, psdu] : m_psduMap)
2530 {
2531 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
2532 {
2533 DequeuePsdu(psdu);
2534 }
2535 }
2536 m_psduMap.clear();
2537 }
2538 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
2539 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK)
2540 {
2541 // this BlockAck frame may have been sent in response to a DL MU PPDU with
2542 // acknowledgment in SU format or one of the consequent BlockAckReq frames.
2543 // We clear the PSDU map and let parent classes continue processing this frame.
2544 m_psduMap.clear();
2545 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2546 }
2547 else if (hdr.IsTrigger())
2548 {
2549 // Trigger Frames are only processed by STAs
2550 if (!m_staMac)
2551 {
2552 return;
2553 }
2554
2555 // A Trigger Frame in an A-MPDU is processed when the A-MPDU is fully received
2556 if (inAmpdu)
2557 {
2558 m_triggerFrameInAmpdu = true;
2559 return;
2560 }
2561
2562 CtrlTriggerHeader trigger;
2563 mpdu->GetPacket()->PeekHeader(trigger);
2564
2565 if (hdr.GetAddr1() != m_self &&
2566 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
2567 hdr.GetAddr2() != m_bssid // not sent by the AP this STA is associated with
2568 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
2569 {
2570 // not addressed to us
2571 return;
2572 }
2573
2574 uint16_t staId = m_staMac->GetAssociationId();
2575
2576 if (trigger.IsMuRts())
2577 {
2578 Mac48Address sender = hdr.GetAddr2();
2579 NS_LOG_DEBUG("Received MU-RTS Trigger Frame from=" << sender);
2580 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2581
2582 // If a non-AP STA receives an MU-RTS Trigger frame, the non-AP STA shall commence
2583 // the transmission of a CTS frame response at the SIFS time boundary after
2584 // the end of a received PPDU when all the following conditions are met:
2585 // - The MU-RTS Trigger frame has one of the User Info fields addressed to
2586 // the non-AP STA (this is guaranteed if we get here)
2587 // - The UL MU CS condition indicates that the medium is idle
2588 // (Sec. 26.2.6.3 of 802.11ax-2021)
2589 NS_LOG_DEBUG("Schedule CTS");
2590 Simulator::Schedule(m_phy->GetSifs(),
2591 &HeFrameExchangeManager::SendCtsAfterMuRts,
2592 this,
2593 hdr,
2594 trigger,
2595 rxSignalInfo.snr);
2596 }
2597 else if (trigger.IsMuBar())
2598 {
2599 Mac48Address sender = hdr.GetAddr2();
2600 NS_LOG_DEBUG("Received MU-BAR Trigger Frame from=" << sender);
2601 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2602
2603 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
2604 NS_ASSERT(userInfoIt != trigger.end());
2605 CtrlBAckRequestHeader blockAckReq = userInfoIt->GetMuBarTriggerDepUserInfo();
2606 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
2607 uint8_t tid = blockAckReq.GetTidInfo();
2608
2609 GetBaManager(tid)->NotifyGotBlockAckRequest(
2610 m_mac->GetMldAddress(sender).value_or(sender),
2611 tid,
2612 blockAckReq.GetStartingSequence());
2613
2614 Simulator::Schedule(m_phy->GetSifs(),
2615 &HeFrameExchangeManager::ReceiveMuBarTrigger,
2616 this,
2617 trigger,
2618 tid,
2619 hdr.GetDuration(),
2620 rxSignalInfo.snr);
2621 }
2622 else if (trigger.IsBasic())
2623 {
2624 Simulator::Schedule(m_phy->GetSifs(),
2625 &HeFrameExchangeManager::ReceiveBasicTrigger,
2626 this,
2627 trigger,
2628 hdr);
2629 }
2630 else if (trigger.IsBsrp())
2631 {
2632 Simulator::Schedule(m_phy->GetSifs(),
2633 &HeFrameExchangeManager::SendQosNullFramesInTbPpdu,
2634 this,
2635 trigger,
2636 hdr);
2637 }
2638 }
2639 else
2640 {
2641 // the received control frame cannot be handled here
2642 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2643 }
2644
2645 // the received control frame has been processed
2646 return;
2647 }
2648
2649 // the received frame cannot be handled here
2650 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2651 ;
2652}
2653
2654void
2655HeFrameExchangeManager::EndReceiveAmpdu(Ptr<const WifiPsdu> psdu,
2656 const RxSignalInfo& rxSignalInfo,
2657 const WifiTxVector& txVector,
2658 const std::vector<bool>& perMpduStatus)
2659{
2660 std::set<uint8_t> tids = psdu->GetTids();
2661
2662 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2663 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2664 {
2665 Mac48Address sender = psdu->GetAddr2();
2666 NS_ASSERT(m_txParams.m_acknowledgment &&
2667 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2668 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
2669 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
2670
2671 if (m_txTimer.GetStasExpectedToRespond().count(sender) == 0)
2672 {
2673 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2674 return;
2675 }
2676
2677 NS_LOG_DEBUG("Received an A-MPDU in a TB PPDU from " << sender << " (" << *psdu << ")");
2678
2679 if (std::any_of(tids.begin(), tids.end(), [&psdu](uint8_t tid) {
2680 return psdu->GetAckPolicyForTid(tid) == WifiMacHeader::NORMAL_ACK;
2681 }))
2682 {
2683 if (std::all_of(perMpduStatus.cbegin(), perMpduStatus.cend(), [](bool v) { return v; }))
2684 {
2685 // All-ack context
2686 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, 14),
2687 index);
2688 acknowledgment->baType.m_bitmapLen.push_back(0);
2689 }
2690 else
2691 {
2692 // Block Acknowledgment context
2693 std::size_t i = 0;
2694 for (const auto& tid : tids)
2695 {
2696 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid),
2697 index + i++);
2698 acknowledgment->baType.m_bitmapLen.push_back(
2699 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2700 }
2701 }
2702 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2703 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2704 }
2705
2706 // Schedule the transmission of a Multi-STA BlockAck frame if needed
2707 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsRunning())
2708 {
2709 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2710 &HeFrameExchangeManager::SendMultiStaBlockAck,
2711 this,
2712 std::cref(m_txParams),
2713 psdu->GetDuration());
2714 }
2715
2716 // remove the sender from the set of stations that are expected to send a TB PPDU
2717 m_txTimer.GotResponseFrom(sender);
2718
2719 if (m_txTimer.GetStasExpectedToRespond().empty())
2720 {
2721 // we do not expect any other BlockAck frame
2722 m_txTimer.Cancel();
2723 m_channelAccessManager->NotifyAckTimeoutResetNow();
2724
2725 if (!m_multiStaBaEvent.IsRunning())
2726 {
2727 // all of the stations that replied with a TB PPDU sent QoS Null frames.
2728 NS_LOG_DEBUG("Continue the TXOP");
2729 m_psduMap.clear();
2730 m_edca->ResetCw(m_linkId);
2731 TransmissionSucceeded();
2732 }
2733 }
2734
2735 // the received TB PPDU has been processed
2736 return;
2737 }
2738
2739 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2740 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
2741 {
2742 Mac48Address sender = psdu->GetAddr2();
2743
2744 if (m_txTimer.GetStasExpectedToRespond().count(sender) == 0)
2745 {
2746 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2747 return;
2748 }
2749 if (std::none_of(psdu->begin(), psdu->end(), [](Ptr<WifiMpdu> mpdu) {
2750 return mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().HasData();
2751 }))
2752 {
2753 NS_LOG_WARN("No QoS Null frame in the received PSDU");
2754 return;
2755 }
2756
2757 NS_LOG_DEBUG("Received QoS Null frames in a TB PPDU from " << sender);
2758
2759 // remove the sender from the set of stations that are expected to send a TB PPDU
2760 m_txTimer.GotResponseFrom(sender);
2761
2762 if (m_txTimer.GetStasExpectedToRespond().empty())
2763 {
2764 // we do not expect any other response
2765 m_txTimer.Cancel();
2766 m_channelAccessManager->NotifyAckTimeoutResetNow();
2767
2768 NS_ASSERT(m_edca);
2769 m_psduMap.clear();
2770 m_edca->ResetCw(m_linkId);
2771 TransmissionSucceeded();
2772 }
2773
2774 // the received TB PPDU has been processed
2775 return;
2776 }
2777
2778 if (m_triggerFrameInAmpdu)
2779 {
2780 // the received A-MPDU contains a Trigger Frame. It is now time to handle it.
2781 auto psduIt = psdu->begin();
2782 while (psduIt != psdu->end())
2783 {
2784 if ((*psduIt)->GetHeader().IsTrigger())
2785 {
2786 ReceiveMpdu(*psduIt, rxSignalInfo, txVector, false);
2787 }
2788 psduIt++;
2789 }
2790
2791 m_triggerFrameInAmpdu = false;
2792 return;
2793 }
2794
2795 // the received frame cannot be handled here
2796 VhtFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
2797}
2798
2799} // namespace ns3
#define Max(a, b)
uint16_t GetAssociationId(Mac48Address addr, uint8_t linkId) const
const std::map< uint16_t, Mac48Address > & GetStaList(uint8_t linkId) const
Get a const reference to the map of associated stations on the given link.
void NotifyAckTimeoutStartNow(Time duration)
Notify that ack timer has started for the given duration.
void NotifyCtsTimeoutStartNow(Time duration)
Notify that CTS timer has started for the given duration.
Headers for BlockAckRequest.
Definition: ctrl-headers.h:52
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:203
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:942
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...
bool IsMuRts() const
Check if this is a MU-RTS Trigger frame.
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.
TriggerFrameVariant GetVariant() const
Get the Common Info field variant.
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
bool IsRunning() const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:76
std::set< Mac48Address > m_sentRtsTo
the STA(s) which we sent an RTS to (waiting for CTS)
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
virtual void SetWifiMac(const Ptr< WifiMac > mac)
Set the MAC layer to use.
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager() const
virtual void Reset()
Reset this frame exchange manager.
Mac48Address m_self
the MAC address of this device
virtual void StartProtection(const WifiTxParameters &txParams)
Start the protection mechanism indicated by the given TX parameters.
uint16_t m_allowedWidth
the allowed width in MHz for the current transmission
WifiTxTimer m_txTimer
the timer set upon frame transmission
std::set< Mac48Address > m_protectedStas
STAs that have replied to an RTS in this TXOP.
virtual Time GetRtsDurationId(const WifiTxVector &rtsTxVector, Time txDuration, Time response) const
Compute how to set the Duration/ID field of an RTS frame to send to protect a frame transmitted with ...
virtual void ProtectionCompleted()
Transmit prepared frame upon successful protection mechanism.
virtual void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector)
Called when the CTS timeout expires.
virtual void TransmissionSucceeded()
Take necessary actions upon a transmission success.
Ptr< WifiPhy > m_phy
the PHY layer on this station
Ptr< ChannelAccessManager > m_channelAccessManager
the channel access manager
virtual void RxStartIndication(WifiTxVector txVector, Time psduDuration)
virtual Time GetMuRtsDurationId(uint32_t muRtsSize, const WifiTxVector &muRtsTxVector, Time txDuration, Time response) const
Compute how to set the Duration/ID field of an MU-RTS Trigger Frame to send to protect a frame transm...
Ptr< ApWifiMac > m_apMac
MAC pointer (null if not an AP)
void DoDispose() override
Destructor implementation.
void Reset() override
Reset this frame exchange manager.
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...
virtual void SendMuRts(const WifiTxParameters &txParams)
Send an MU-RTS to begin an MU-RTS/CTS frame exchange protecting an MU PPDU.
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.
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
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.
WifiTxVector GetCtsTxVectorAfterMuRts(const CtrlTriggerHeader &trigger, uint16_t staId) const
Get the TXVECTOR that the station having the given station ID has to use to send a CTS frame after re...
void SetWifiMac(const Ptr< WifiMac > mac) override
Set the MAC layer to use.
Ptr< MultiUserScheduler > m_muScheduler
Multi-user Scheduler (HE APs only)
virtual void CtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector)
Called when no CTS frame is received after an MU-RTS.
WifiTxVector m_trigVector
the TRIGVECTOR
void ProtectionCompleted() override
Transmit prepared frame upon successful protection mechanism.
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.
virtual void BlockAcksInTbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations)
Take the necessary actions after that some BlockAck frames are missing in response to a DL MU PPDU.
EventId m_intraBssNavResetEvent
the event to reset the intra-BSS NAV after an RTS
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...
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...
virtual void TbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations)
Take the necessary actions after that some TB PPDUs are missing in response to Trigger Frame.
void SendPsduMap()
Send the current PSDU map as a DL MU PPDU.
WifiPsduMap m_psduMap
the A-MPDU being transmitted
void RecordSentMuRtsTo(const WifiTxParameters &txParams)
Record the stations being solicited by an MU-RTS TF.
static TypeId GetTypeId()
Get the type ID.
Time m_intraBssNavEnd
intra-BSS NAV expiration time
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.
void StartProtection(const WifiTxParameters &txParams) override
Start the protection mechanism indicated by the given TX parameters.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
static Time ConvertLSigLengthToHeTbPpduDuration(uint16_t length, const WifiTxVector &txVector, WifiPhyBand band)
Definition: he-phy.cc:288
Ptr< WifiMpdu > GetBar(AcIndex ac, std::optional< uint8_t > optTid=std::nullopt, std::optional< Mac48Address > optAddress=std::nullopt)
Get the next BlockAckRequest or MU-BAR Trigger Frame to send, if any.
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 ForwardMpduDown(Ptr< WifiMpdu > mpdu, WifiTxVector &txVector) override
Forward an MPDU down to the PHY layer.
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...
void NotifyPacketDiscarded(Ptr< const WifiMpdu > mpdu) override
Pass the given MPDU, discarded because of the max retry limit was reached, to the MPDU dropped callba...
virtual void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector)
Forward a PSDU down to the PHY layer.
void DequeuePsdu(Ptr< const WifiPsdu > psdu)
Dequeue the MPDUs of the given PSDU from the queue in which they are stored.
void ReleaseSequenceNumbers(Ptr< const WifiPsdu > psdu) const override
Make the sequence numbers of MPDUs included in the given PSDU available again if the MPDUs have never...
an EUI-48 address
Definition: mac48-address.h:46
static Mac48Address GetBroadcast()
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.
virtual void DoDispose()
Destructor implementation.
Definition: object.cc:352
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
void TransmissionFailed() override
Take necessary actions upon a transmission failure.
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:273
virtual Time GetRemainingTxop(uint8_t linkId) const
Return the remaining duration in the current TXOP on the given link.
Definition: qos-txop.cc:599
std::pair< CtrlBAckRequestHeader, WifiMacHeader > PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const
Definition: qos-txop.cc:291
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:558
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:199
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
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition: time.cc:415
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:286
@ MS
millisecond
Definition: nstime.h:117
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:314
Time GetTxopLimit() const
Return the TXOP limit.
Definition: txop.cc:491
void UpdateFailedCw(uint8_t linkId)
Update the value of the CW variable for the given link to take into account a transmission failure.
Definition: txop.cc:313
void ResetCw(uint8_t linkId)
Update the value of the CW variable for the given link to take into account a transmission success or...
Definition: txop.cc:304
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:930
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.
virtual uint32_t GetSize() const
Return the size of the WifiMacHeader in octets.
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.
virtual 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:1751
OriginatorAgreementOptConstRef GetBaAgreementEstablishedAsOriginator(Mac48Address recipient, uint8_t tid) const
Definition: wifi-mac.cc:1681
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:499
represent a single transmission mode
Definition: wifi-mode.h:51
Time GetSlot() const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:795
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:783
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1496
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1005
Ptr< PhyEntity > GetPhyEntity(WifiModulationClass modulation) const
Get the supported PHY entity corresponding to the modulation class.
Definition: wifi-phy.cc:713
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition: wifi-phy.cc:1489
void ReportFinalRtsFailed(const WifiMacHeader &header)
Should be invoked after calling ReportRtsFailed if NeedRetransmission returns false.
void ReportRtsFailed(const WifiMacHeader &header)
Should be invoked whenever the RtsTimeout associated to a transmission attempt expires.
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.
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:62
const std::set< Mac48Address > & GetStasExpectedToRespond() const
void Set(Reason reason, const Time &delay, const std::set< Mac48Address > &from, MEM mem_ptr, OBJ obj, Args... args)
This method is called when a frame soliciting a response is transmitted.
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)
uint8_t GetBssColor() const
Get the BSS color.
void SetGuardInterval(uint16_t guardInterval)
Sets the guard interval duration (in nanoseconds)
void SetTriggerResponding(bool triggerResponding)
Set the Trigger Responding parameter to the given value.
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.
WifiPreamble GetPreambleType() const
void SetAggregation(bool aggregation)
Sets if PSDU contains A-MPDU.
bool IsDlMu() const
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
void SetSigBMode(const WifiMode &mode)
Set the MCS used for SIG-B.
void SetBssColor(uint8_t color)
Set the BSS color.
uint16_t GetChannelWidth() const
void SetMode(WifiMode mode)
Sets the selected payload transmission mode.
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:46
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1325
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:72
@ WIFI_PREAMBLE_EHT_TB
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
Definition: wifi-phy-band.h:35
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:838
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:484
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
bool IsTrigger(const WifiPsduMap &psduMap)
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_RTS
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_QOSDATA_NULL
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition: wifi-utils.cc:66
bool IsDlMu(WifiPreamble preamble)
Return true if a preamble corresponds to a downlink multi-user transmission.
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition: wifi-utils.cc:58
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition: wifi-mode.h:35
uint32_t GetCtsSize()
Return the total CTS size (including FCS trailer).
Definition: wifi-utils.cc:111
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
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:69
double snr
SNR in linear scale.
Definition: phy-entity.h:70
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...
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...
WifiDlMuTfMuBar specifies that a DL MU PPDU is followed after a SIFS duration by a MU-BAR Trigger Fra...
WifiMuRtsCtsProtection specifies that MU-RTS/CTS protection method is used.
WifiNoAck specifies that no acknowledgment is required.
WifiNoProtection specifies that no protection method is used.
WifiProtection is an abstract base struct.
const Method method
protection method
WifiUlMuMultiStaBa specifies that a Basic Trigger Frame is being sent to solicit TB PPDUs that will b...
BlockAckType baType
BlockAck type.
std::map< std::pair< Mac48Address, uint8_t >, std::size_t > stationsReceivingMultiStaBa
Map (originator, tid) pairs to the their index in baType.