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