A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
channel-access-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2005,2006 INRIA
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
7 */
8
10
11#include "txop.h"
12#include "wifi-mac-queue.h"
13#include "wifi-phy-listener.h"
14#include "wifi-phy.h"
15
16#include "ns3/eht-frame-exchange-manager.h"
17#include "ns3/log.h"
18#include "ns3/simulator.h"
19
20#include <sstream>
21
22#undef NS_LOG_APPEND_CONTEXT
23#define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "] "
24
25namespace ns3
26{
27
28NS_LOG_COMPONENT_DEFINE("ChannelAccessManager");
29
30NS_OBJECT_ENSURE_REGISTERED(ChannelAccessManager);
31
33
34/**
35 * Listener for PHY events. Forwards to ChannelAccessManager.
36 * The ChannelAccessManager may handle multiple PHY listeners connected to distinct PHYs,
37 * but only one listener at a time can be active. Notifications from inactive listeners are
38 * ignored by the ChannelAccessManager, except for the channel switch notification.
39 * Inactive PHY listeners are typically configured by 11be EMLSR clients.
40 */
42{
43 public:
44 /**
45 * Create a PhyListener for the given ChannelAccessManager.
46 *
47 * @param cam the ChannelAccessManager
48 */
50 : m_cam(cam),
51 m_active(true)
52 {
53 }
54
55 ~PhyListener() override
56 {
57 }
58
59 /**
60 * Set this listener to be active or not.
61 *
62 * @param active whether this listener is active or not
63 */
64 void SetActive(bool active)
65 {
66 m_active = active;
67 }
68
69 /**
70 * @return whether this listener is active or not
71 */
72 bool IsActive() const
73 {
74 return m_active;
75 }
76
77 void NotifyRxStart(Time duration) override
78 {
79 if (m_active)
80 {
81 m_cam->NotifyRxStartNow(duration);
82 }
83 }
84
85 void NotifyRxEndOk() override
86 {
87 if (m_active)
88 {
90 }
91 }
92
93 void NotifyRxEndError(const WifiTxVector& txVector) override
94 {
95 if (m_active)
96 {
97 m_cam->NotifyRxEndErrorNow(txVector);
98 }
99 }
100
101 void NotifyTxStart(Time duration, dBm_u txPower) override
102 {
103 if (m_active)
104 {
105 m_cam->NotifyTxStartNow(duration);
106 }
107 }
108
110 WifiChannelListType channelType,
111 const std::vector<Time>& per20MhzDurations) override
112 {
113 if (m_active)
114 {
115 m_cam->NotifyCcaBusyStartNow(duration, channelType, per20MhzDurations);
116 }
117 }
118
119 void NotifySwitchingStart(Time duration) override
120 {
121 m_cam->NotifySwitchingStartNow(this, duration);
122 }
123
124 void NotifySleep() override
125 {
126 if (m_active)
127 {
129 }
130 }
131
132 void NotifyOff() override
133 {
134 if (m_active)
135 {
137 }
138 }
139
140 void NotifyWakeup() override
141 {
142 if (m_active)
143 {
145 }
146 }
147
148 void NotifyOn() override
149 {
150 if (m_active)
151 {
153 }
154 }
155
156 private:
157 ns3::ChannelAccessManager* m_cam; //!< ChannelAccessManager to forward events to
158 bool m_active; //!< whether this PHY listener is active
159};
160
161/****************************************************************
162 * Implement the channel access manager of all Txop holders
163 ****************************************************************/
164
165TypeId
167{
168 static TypeId tid =
169 TypeId("ns3::ChannelAccessManager")
171 .SetGroupName("Wifi")
172 .AddConstructor<ChannelAccessManager>()
173 .AddAttribute("GenerateBackoffIfTxopWithoutTx",
174 "Specify whether the backoff should be invoked when the AC gains the "
175 "right to start a TXOP but it does not transmit any frame "
176 "(e.g., due to constraints associated with EMLSR operations), "
177 "provided that the queue is not actually empty.",
178 BooleanValue(false),
182 .AddAttribute("ProactiveBackoff",
183 "Specify whether a new backoff value is generated when a CCA busy "
184 "period starts, the backoff counter is zero and the station is not a "
185 "TXOP holder. This is useful to generate a new backoff value when, "
186 "e.g., the backoff counter reaches zero, the station does not transmit "
187 "and subsequently the medium becomes busy.",
188 BooleanValue(false),
191 .AddAttribute("ResetBackoffThreshold",
192 "If no PHY operates on this link for a period greater than this "
193 "threshold, all the backoffs are reset.",
194 TimeValue(Time{0}),
197 .AddAttribute("NSlotsLeft",
198 "The NSlotsLeftAlert trace source is fired when the number of remaining "
199 "backoff slots for any AC is equal to or less than the value of this "
200 "attribute. Note that the trace source is fired only if the AC for which "
201 "the previous condition is met has requested channel access. Also, if "
202 "the value of this attribute is zero, the trace source is never fired.",
203 UintegerValue(0),
206 .AddAttribute("NSlotsLeftMinDelay",
207 "The minimum gap between the end of a medium busy event and the time "
208 "the NSlotsLeftAlert trace source can be fired.",
212 .AddTraceSource("NSlotsLeftAlert",
213 "The number of remaining backoff slots for the AC with the given index "
214 "reached the threshold set through the NSlotsLeft attribute.",
216 "ns3::ChannelAccessManager::NSlotsLeftCallback");
217 return tid;
218}
219
221 : m_lastAckTimeoutEnd(0),
222 m_lastCtsTimeoutEnd(0),
223 m_lastNavEnd(0),
224 m_lastRx({MicroSeconds(0), MicroSeconds(0)}),
225 m_lastRxReceivedOk(true),
226 m_lastTxEnd(0),
227 m_lastSwitchingEnd(0),
228 m_lastSleepEnd(0),
229 m_lastOffEnd(0),
230 m_linkId(0)
231{
232 NS_LOG_FUNCTION(this);
233 InitLastBusyStructs();
234}
235
240
241void
247
248void
250{
251 NS_LOG_FUNCTION(this);
252 for (Ptr<Txop> i : m_txops)
253 {
254 i->Dispose();
255 i = nullptr;
256 }
257 m_phy = nullptr;
258 m_feManager = nullptr;
259 m_phyListeners.clear();
260}
261
262std::shared_ptr<PhyListener>
264{
265 if (auto listenerIt = m_phyListeners.find(phy); listenerIt != m_phyListeners.end())
266 {
267 return listenerIt->second;
268 }
269 return nullptr;
270}
271
272void
274{
275 NS_LOG_FUNCTION(this << phy);
276
277 const auto now = Simulator::Now();
278 auto phyListener = GetPhyListener(phy);
279
280 if (phyListener)
281 {
282 // a PHY listener for the given PHY already exists, it must be inactive
283 NS_ASSERT_MSG(!phyListener->IsActive(),
284 "There is already an active listener registered for given PHY");
285 NS_ASSERT_MSG(!m_phy, "Cannot reactivate a listener if another PHY is active");
286 phyListener->SetActive(true);
287 // if a PHY listener already exists, the PHY was disconnected and now reconnected to the
288 // channel access manager; unregister the listener and register again (below) to get
289 // updated CCA busy information
290 phy->UnregisterListener(phyListener);
291 // we expect that the PHY is reconnected immediately after the other PHY left the link:
292 // reset the start of m_lastNoPhy so as to ignore this event
296 }
297 else
298 {
299 phyListener = std::make_shared<PhyListener>(this);
300 m_phyListeners.emplace(phy, phyListener);
301 if (m_phy)
302 {
304 }
305 else
306 {
307 // no PHY operating on this link and no previous PHY listener to reactivate
308 m_lastSwitchingEnd = now;
309 m_lastNoPhy.end = now;
311 {
313 }
314 }
315 }
316
317 m_phy = phy; // this is the new active PHY
319 phy->RegisterListener(phyListener);
320}
321
322void
324{
325 NS_LOG_FUNCTION(this << phy);
326 if (auto phyListener = GetPhyListener(phy))
327 {
328 phy->UnregisterListener(phyListener);
329 m_phyListeners.erase(phy);
330 // reset m_phy if we are removing listener registered for the active PHY
331 if (m_phy == phy)
332 {
335 m_phy = nullptr;
337 }
338 }
339}
340
341void
343{
344 NS_LOG_FUNCTION(this << phy);
345 if (auto listener = GetPhyListener(phy))
346 {
347 listener->SetActive(false);
348 }
349}
350
351void
353 const WifiPhyOperatingChannel& channel,
354 uint8_t linkId)
355{
356 NS_LOG_FUNCTION(this << phy << channel << linkId);
358 "The given PHY is already expected to switch channel");
359 m_switchingEmlsrLinks.emplace(phy, EmlsrLinkSwitchInfo{channel, linkId});
360}
361
362void
364{
365 NS_LOG_FUNCTION(this << +linkId);
366 m_linkId = linkId;
367}
368
369void
371{
372 NS_LOG_FUNCTION(this << feManager);
373 m_feManager = feManager;
374 m_feManager->SetChannelAccessManager(this);
375}
376
377Time
379{
380 if (m_phy)
381 {
383 }
384 return m_cachedSlot;
385}
386
387Time
389{
390 if (m_phy)
391 {
393 }
394 return m_cachedSifs;
395}
396
397Time
402
403void
405{
406 NS_LOG_FUNCTION(this << txop);
407 m_txops.push_back(txop);
408}
409
410void
412{
413 NS_LOG_FUNCTION(this);
414 const auto now = Simulator::Now();
415
417 m_lastIdle.emplace(WIFI_CHANLIST_PRIMARY, Timespan{now, now});
418
419 const auto width = m_phy ? m_phy->GetChannelWidth() : MHz_u{0};
420 std::size_t size = (width > MHz_u{20} && m_phy->GetStandard() >= WIFI_STANDARD_80211ax)
421 ? Count20MHzSubchannels(width)
422 : 0;
423 m_lastPer20MHzBusyEnd.resize(size, now);
424
426 {
427 return;
428 }
429
430 if (width >= MHz_u{40})
431 {
433 m_lastIdle.emplace(WIFI_CHANLIST_SECONDARY, Timespan{now, now});
434 }
435 else
436 {
439 }
440
441 if (width >= MHz_u{80})
442 {
445 }
446 else
447 {
450 }
451
452 if (width >= MHz_u{160})
453 {
456 }
457 else
458 {
461 }
462
463 if (width >= MHz_u{320})
464 {
467 }
468 else
469 {
472 }
473
474 // TODO Add conditions for new channel widths as they get supported
475}
476
477void
479{
480 NS_LOG_FUNCTION(this);
481 Time now = Simulator::Now();
482
484
485 // reset all values
486 for (auto& [chType, time] : m_lastBusyEnd)
487 {
488 time = now;
489 }
490
491 for (auto& [chType, timeSpan] : m_lastIdle)
492 {
493 timeSpan = Timespan{now, now};
494 }
495
496 for (auto& time : m_lastPer20MHzBusyEnd)
497 {
498 time = now;
499 }
500}
501
502bool
504{
505 NS_LOG_FUNCTION(this);
506 Time now = Simulator::Now();
507 return (m_lastRx.end > now) // RX
508 || (m_lastTxEnd > now) // TX
509 || (m_lastNavEnd > now) // NAV busy
510 // an EDCA TXOP is obtained based solely on activity of the primary channel
511 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
512 || (m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now); // CCA busy
513}
514
515bool
517 bool hadFramesToTransmit,
518 bool checkMediumBusy)
519{
520 NS_LOG_FUNCTION(this << txop << hadFramesToTransmit << checkMediumBusy);
521
522 // No backoff needed if in sleep mode or off. Checking if m_phy is nullptr is a workaround
523 // needed for EMLSR and may be removed in the future
524 if (!m_phy || m_phy->IsStateSleep() || m_phy->IsStateOff())
525 {
526 return false;
527 }
528
529 // the Txop might have a stale value of remaining backoff slots
531
532 /*
533 * From section 10.3.4.2 "Basic access" of IEEE 802.11-2016:
534 *
535 * A STA may transmit an MPDU when it is operating under the DCF access
536 * method, either in the absence of a PC, or in the CP of the PCF access
537 * method, when the STA determines that the medium is idle when a frame is
538 * queued for transmission, and remains idle for a period of a DIFS, or an
539 * EIFS (10.3.2.3.7) from the end of the immediately preceding medium-busy
540 * event, whichever is the greater, and the backoff timer is zero. Otherwise
541 * the random backoff procedure described in 10.3.4.3 shall be followed.
542 *
543 * From section 10.22.2.2 "EDCA backoff procedure" of IEEE 802.11-2016:
544 *
545 * The backoff procedure shall be invoked by an EDCAF when any of the following
546 * events occurs:
547 * a) An MA-UNITDATA.request primitive is received that causes a frame with that AC
548 * to be queued for transmission such that one of the transmit queues associated
549 * with that AC has now become non-empty and any other transmit queues
550 * associated with that AC are empty; the medium is busy on the primary channel
551 */
552 if (!hadFramesToTransmit && txop->HasFramesToTransmit(m_linkId) &&
553 txop->GetAccessStatus(m_linkId) != Txop::GRANTED && txop->GetBackoffSlots(m_linkId) == 0)
554 {
555 if (checkMediumBusy && !IsBusy())
556 {
557 // medium idle. If this is a DCF, use immediate access (we can transmit
558 // in a DIFS if the medium remains idle). If this is an EDCAF, update
559 // the backoff start time kept by the EDCAF to the current time in order
560 // to correctly align the backoff start time at the next slot boundary
561 // (performed by the next call to ChannelAccessManager::RequestAccess())
562 Time delay =
563 (txop->IsQosTxop() ? Seconds(0) : GetSifs() + txop->GetAifsn(m_linkId) * GetSlot());
564 txop->UpdateBackoffSlotsNow(0, Simulator::Now() + delay, m_linkId);
565 }
566 else
567 {
568 // medium busy, backoff is needed
569 return true;
570 }
571 }
572 return false;
573}
574
575void
577{
578 NS_LOG_FUNCTION(this << txop);
579 if (m_phy && txop->HasFramesToTransmit(m_linkId))
580 {
582 }
583 // Deny access if in sleep mode or off. Checking if m_phy is nullptr is a workaround
584 // needed for EMLSR and may be removed in the future
585 if (!m_phy || m_phy->IsStateSleep() || m_phy->IsStateOff())
586 {
587 return;
588 }
589 /*
590 * EDCAF operations shall be performed at slot boundaries (Sec. 10.22.2.4 of 802.11-2016)
591 */
592 Time accessGrantStart = GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot());
593
594 if (txop->IsQosTxop() && txop->GetBackoffStart(m_linkId) > accessGrantStart)
595 {
596 // The backoff start time reported by the EDCAF is more recent than the last
597 // time the medium was busy plus an AIFS, hence we need to align it to the
598 // next slot boundary.
599 Time diff = txop->GetBackoffStart(m_linkId) - accessGrantStart;
600 uint32_t nIntSlots = (diff / GetSlot()).GetHigh() + 1;
601 txop->UpdateBackoffSlotsNow(0, accessGrantStart + (nIntSlots * GetSlot()), m_linkId);
602 }
603
605 NS_ASSERT(txop->GetAccessStatus(m_linkId) != Txop::REQUESTED);
606 txop->NotifyAccessRequested(m_linkId);
609}
610
611void
613{
614 NS_LOG_FUNCTION(this);
615 uint32_t k = 0;
616 const auto now = Simulator::Now();
617 const auto accessGrantStart = GetAccessGrantStart();
618 for (auto i = m_txops.begin(); i != m_txops.end(); k++)
619 {
620 Ptr<Txop> txop = *i;
621 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
622 (!txop->IsQosTxop() || !StaticCast<QosTxop>(txop)->EdcaDisabled(m_linkId)) &&
623 GetBackoffEndFor(txop, accessGrantStart) <= now)
624 {
625 /**
626 * This is the first Txop we find with an expired backoff and which
627 * needs access to the medium. i.e., it has data to send.
628 */
629 NS_LOG_DEBUG("dcf " << k << " needs access. backoff expired. access granted. slots="
630 << txop->GetBackoffSlots(m_linkId));
631 i++; // go to the next item in the list.
632 k++;
633 std::vector<Ptr<Txop>> internalCollisionTxops;
634 for (auto j = i; j != m_txops.end(); j++, k++)
635 {
636 Ptr<Txop> otherTxop = *j;
637 if (otherTxop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
638 GetBackoffEndFor(otherTxop, accessGrantStart) <= now)
639 {
641 "dcf " << k << " needs access. backoff expired. internal collision. slots="
642 << otherTxop->GetBackoffSlots(m_linkId));
643 /**
644 * all other Txops with a lower priority whose backoff
645 * has expired and which needed access to the medium
646 * must be notified that we did get an internal collision.
647 */
648 internalCollisionTxops.push_back(otherTxop);
649 }
650 }
651
652 /**
653 * Now, we notify all of these changes in one go if the EDCAF winning
654 * the contention actually transmitted a frame. It is necessary to
655 * perform first the calculations of which Txops are colliding and then
656 * only apply the changes because applying the changes through notification
657 * could change the global state of the manager, and, thus, could change
658 * the result of the calculations.
659 */
661 // If we are operating on an OFDM channel wider than 20 MHz, find the largest
662 // idle primary channel and pass its width to the FrameExchangeManager, so that
663 // the latter can transmit PPDUs of the appropriate width (see Section 10.23.2.5
664 // of IEEE 802.11-2020).
665 auto interval = (m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
666 ? GetSifs() + 2 * GetSlot()
667 : m_phy->GetPifs();
668 auto width =
670 ? GetLargestIdlePrimaryChannel(interval, now)
672 if (m_feManager->StartTransmission(txop, width))
673 {
674 for (auto& collidingTxop : internalCollisionTxops)
675 {
676 m_feManager->NotifyInternalCollision(collidingTxop);
677 }
678 break;
679 }
680 else
681 {
682 // this TXOP did not transmit anything, make sure that backoff counter starts
683 // decreasing in a slot again
684 txop->UpdateBackoffSlotsNow(0, now, m_linkId);
685 // reset the current state to the EDCAF that won the contention
686 // but did not transmit anything
687 i--;
688 k = std::distance(m_txops.begin(), i);
689 }
690 }
691 i++;
692 }
693}
694
695void
710
711std::multimap<Time, WifiExpectedAccessReason>
713{
714 NS_LOG_FUNCTION(this << ignoreNav);
715 const auto now = Simulator::Now();
716
717 std::multimap<Time, WifiExpectedAccessReason> ret;
718
719 // an EDCA TXOP is obtained based solely on activity of the primary channel
720 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
721 const auto busyAccessStart = m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY);
722 ret.emplace(busyAccessStart, WifiExpectedAccessReason::BUSY_END);
723
724 auto rxAccessStart = m_lastRx.end;
725 if ((m_lastRx.end <= now) && !m_lastRxReceivedOk)
726 {
727 rxAccessStart += GetEifsNoDifs();
728 }
729 ret.emplace(rxAccessStart, WifiExpectedAccessReason::RX_END);
730
732
733 const auto navAccessStart = ignoreNav ? Time{0} : m_lastNavEnd;
734 ret.emplace(navAccessStart, WifiExpectedAccessReason::NAV_END);
735
739
740 const auto noPhyStart = m_phy ? m_lastNoPhy.end : now;
741 ret.emplace(noPhyStart, WifiExpectedAccessReason::NO_PHY_END);
742
745
746 NS_LOG_INFO("rx access start=" << rxAccessStart.As(Time::US)
747 << ", busy access start=" << busyAccessStart.As(Time::US)
748 << ", tx access start=" << m_lastTxEnd.As(Time::US)
749 << ", nav access start=" << navAccessStart.As(Time::US)
750 << ", switching access start=" << m_lastSwitchingEnd.As(Time::US)
751 << ", no PHY start=" << noPhyStart.As(Time::US)
752 << ", sleep access start=" << m_lastSleepEnd.As(Time::US)
753 << ", off access start=" << m_lastOffEnd.As(Time::US));
754 return ret;
755}
756
757Time
759{
760 NS_LOG_FUNCTION(this << ignoreNav);
761
762 auto timeReasonMap = DoGetAccessGrantStart(ignoreNav);
763 NS_ASSERT(!timeReasonMap.empty());
764 const auto accessGrantedStart = timeReasonMap.crbegin()->first;
765 NS_LOG_INFO("access grant start=" << accessGrantedStart.As(Time::US));
766
767 return accessGrantedStart + GetSifs();
768}
769
770Time
775
776Time
778{
779 NS_LOG_FUNCTION(this << txop << accessGrantStart.As(Time::S));
780 const auto mostRecentEvent =
781 std::max({txop->GetBackoffStart(m_linkId),
782 accessGrantStart + (txop->GetAifsn(m_linkId) * GetSlot())});
783 NS_LOG_DEBUG("Backoff start for " << txop->GetWifiMacQueue()->GetAc() << ": "
784 << mostRecentEvent.As(Time::US));
785
786 return mostRecentEvent;
787}
788
789Time
794
795Time
797{
798 NS_LOG_FUNCTION(this << txop);
799 Time backoffEnd =
800 GetBackoffStartFor(txop, accessGrantStart) + (txop->GetBackoffSlots(m_linkId) * GetSlot());
801 NS_LOG_DEBUG("Backoff end for " << txop->GetWifiMacQueue()->GetAc() << ": "
802 << backoffEnd.As(Time::US));
803
804 return backoffEnd;
805}
806
809{
810 NS_LOG_FUNCTION(this << delay.As(Time::US));
811
812 const auto now = Simulator::Now();
813 const auto deadline = now + delay;
814 const auto timeReasonMap = DoGetAccessGrantStart(false);
815 NS_ASSERT(!timeReasonMap.empty());
816 auto accessGrantStart = timeReasonMap.crbegin()->first;
817
818 if (accessGrantStart >= deadline)
819 {
820 // return the earliest reason for which access cannot be granted in time
821 for (const auto& [time, reason] : timeReasonMap)
822 {
823 if (time >= deadline)
824 {
829 NS_LOG_DEBUG("Access grant start (" << accessGrantStart.As(Time::US)
830 << ") too late for reason " << reason);
831 return reason;
832 }
833 }
834 NS_ABORT_MSG("No reason found that exceeds the deadline!");
835 }
836
837 accessGrantStart += GetSifs();
839
840 for (auto txop : m_txops)
841 {
842 if (txop->GetAccessStatus(m_linkId) != Txop::REQUESTED)
843 {
844 continue;
845 }
846
847 if (!txop->HasFramesToTransmit(m_linkId))
848 {
850 {
852 }
853 continue;
854 }
855
857 const auto backoffEnd = GetBackoffEndFor(txop, accessGrantStart);
858
859 if (backoffEnd >= now && backoffEnd <= deadline)
860 {
861 NS_LOG_DEBUG("Backoff end for " << txop->GetWifiMacQueue()->GetAc() << " on link "
862 << +m_linkId << ": " << backoffEnd.As(Time::US));
864 }
865 }
866
867 NS_LOG_DEBUG("Access grant not expected for reason: " << reason);
868 return reason;
869}
870
871Time
873{
874 return m_lastNavEnd;
875}
876
877void
879{
880 NS_LOG_FUNCTION(this);
881 uint32_t k = 0;
882 const auto accessGrantStart = GetAccessGrantStart();
883 for (auto txop : m_txops)
884 {
885 Time backoffStart = GetBackoffStartFor(txop, accessGrantStart);
886 if (backoffStart <= Simulator::Now())
887 {
888 uint32_t nIntSlots = ((Simulator::Now() - backoffStart) / GetSlot()).GetHigh();
889 /*
890 * EDCA behaves slightly different to DCA. For EDCA we
891 * decrement once at the slot boundary at the end of AIFS as
892 * well as once at the end of each clear slot
893 * thereafter. For DCA we only decrement at the end of each
894 * clear slot after DIFS. We account for the extra backoff
895 * by incrementing the slot count here in the case of
896 * EDCA. The if statement whose body we are in has confirmed
897 * that a minimum of AIFS has elapsed since last busy
898 * medium.
899 */
900 if (txop->IsQosTxop())
901 {
902 nIntSlots++;
903 }
904 uint32_t n = std::min(nIntSlots, txop->GetBackoffSlots(m_linkId));
905 NS_LOG_DEBUG("dcf " << k << " dec backoff slots=" << n);
906 Time backoffUpdateBound = backoffStart + (n * GetSlot());
907 txop->UpdateBackoffSlotsNow(n, backoffUpdateBound, m_linkId);
908 }
909 ++k;
910 }
911}
912
913void
915{
916 NS_LOG_FUNCTION(this);
917 /**
918 * Is there a Txop which needs to access the medium, and,
919 * if there is one, how many slots for AIFS+backoff does it require ?
920 */
921 Ptr<Txop> nextTxop;
922 auto expectedBackoffEnd = Simulator::GetMaximumSimulationTime();
923 const auto accessGrantStart = GetAccessGrantStart();
924 const auto now = Simulator::Now();
925 for (auto txop : m_txops)
926 {
927 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED)
928 {
929 if (auto backoffEnd = GetBackoffEndFor(txop, accessGrantStart);
930 backoffEnd > now && backoffEnd < expectedBackoffEnd)
931 {
932 expectedBackoffEnd = backoffEnd;
933 nextTxop = txop;
934 }
935 }
936 }
937 NS_LOG_DEBUG("Access timeout needed: " << (nextTxop != nullptr));
938 if (nextTxop)
939 {
940 const auto aci = nextTxop->GetWifiMacQueue()->GetAc();
941 NS_LOG_DEBUG("expected backoff end=" << expectedBackoffEnd << " by " << aci);
942 auto expectedBackoffDelay = expectedBackoffEnd - now;
943
944 if (m_nSlotsLeft > 0)
945 {
946 const auto expectedNotifyTime =
947 Max(expectedBackoffEnd - m_nSlotsLeft * GetSlot(),
948 accessGrantStart - GetSifs() + m_nSlotsLeftMinDelay);
949
950 if (expectedNotifyTime > now)
951 {
952 // make the timer expire when it's time to notify that the given slots are left
953 expectedBackoffDelay = expectedNotifyTime - now;
954 }
955 else
956 {
957 // notify that a number of slots less than or equal to the specified value are left
958 m_nSlotsLeftCallback(m_linkId, aci, expectedBackoffDelay);
959 }
960 }
961
963 Simulator::GetDelayLeft(m_accessTimeout) > expectedBackoffDelay)
964 {
966 }
968 {
969 m_accessTimeout = Simulator::Schedule(expectedBackoffDelay,
971 this);
972 }
973 }
974}
975
976MHz_u
978{
979 NS_LOG_FUNCTION(this << interval.As(Time::US) << end.As(Time::S));
980
981 // If the medium is busy or it just became idle, UpdateLastIdlePeriod does
982 // nothing. This allows us to call this method, e.g., at the end of a frame
983 // reception and check the busy/idle status of the channel before the start
984 // of the frame reception (last idle period was last updated at the start of
985 // the frame reception).
986 // If the medium has been idle for some time, UpdateLastIdlePeriod updates
987 // the last idle period. This is normally what we want because this method may
988 // also be called before starting a TXOP gained through EDCA.
990
991 MHz_u width{0};
992
993 // we iterate over the different types of channels in the same order as they
994 // are listed in WifiChannelListType
995 for (const auto& lastIdle : m_lastIdle)
996 {
997 if (lastIdle.second.start <= end - interval && lastIdle.second.end >= end)
998 {
999 // channel idle, update width
1000 width = (width == MHz_u{0}) ? MHz_u{20} : (2 * width);
1001 }
1002 else
1003 {
1004 break;
1005 }
1006 }
1007 return width;
1008}
1009
1010bool
1011ChannelAccessManager::GetPer20MHzBusy(const std::set<uint8_t>& indices) const
1012{
1013 const auto now = Simulator::Now();
1014
1015 if (m_phy->GetChannelWidth() < MHz_u{40})
1016 {
1017 NS_ASSERT_MSG(indices.size() == 1 && *indices.cbegin() == 0,
1018 "Index 0 only can be specified if the channel width is less than 40 MHz");
1019 return m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now;
1020 }
1021
1022 for (const auto index : indices)
1023 {
1024 NS_ASSERT(index < m_lastPer20MHzBusyEnd.size());
1025 if (m_lastPer20MHzBusyEnd.at(index) > now)
1026 {
1027 NS_LOG_DEBUG("20 MHz channel with index " << +index << " is busy");
1028 return true;
1029 }
1030 }
1031 return false;
1032}
1033
1034void
1036{
1037 NS_LOG_FUNCTION(this << qosTxop << duration);
1038 NS_ASSERT(qosTxop->IsQosTxop());
1039 UpdateBackoff();
1040 Time resume = Simulator::Now() + duration;
1041 NS_LOG_DEBUG("Backoff will resume at time " << resume << " with "
1042 << qosTxop->GetBackoffSlots(m_linkId)
1043 << " remaining slot(s)");
1044 qosTxop->UpdateBackoffSlotsNow(0, resume, m_linkId);
1046}
1047
1048void
1050{
1051 NS_LOG_FUNCTION(this << enable);
1052 m_generateBackoffOnNoTx = enable;
1053}
1054
1055bool
1060
1061void
1063{
1064 NS_LOG_FUNCTION(this << duration);
1065 NS_LOG_DEBUG("rx start for=" << duration);
1066 UpdateBackoff();
1069 m_lastRx.end = m_lastRx.start + duration;
1070 m_lastRxReceivedOk = true;
1071}
1072
1073void
1081
1082void
1084{
1085 NS_LOG_FUNCTION(this);
1086 NS_LOG_DEBUG("rx end error");
1087 // we expect the PHY to notify us of the start of a CCA busy period, if needed
1089 m_lastRxReceivedOk = false;
1091}
1092
1093void
1095{
1096 NS_LOG_FUNCTION(this << duration);
1097 m_lastRxReceivedOk = true;
1098 Time now = Simulator::Now();
1099 if (m_lastRx.end > now)
1100 {
1101 // this may be caused only if PHY has started to receive a packet
1102 // inside SIFS, so, we check that lastRxStart was maximum a SIFS ago
1103 NS_ASSERT(now - m_lastRx.start <= GetSifs());
1104 m_lastRx.end = now;
1105 }
1106 else
1107 {
1109 }
1110 NS_LOG_DEBUG("tx start for " << duration);
1111 UpdateBackoff();
1112 m_lastTxEnd = now + duration;
1113}
1114
1115void
1117 WifiChannelListType channelType,
1118 const std::vector<Time>& per20MhzDurations)
1119{
1120 NS_LOG_FUNCTION(this << duration << channelType);
1121 UpdateBackoff();
1123 auto lastBusyEndIt = m_lastBusyEnd.find(channelType);
1124 NS_ASSERT(lastBusyEndIt != m_lastBusyEnd.end());
1125 Time now = Simulator::Now();
1126 lastBusyEndIt->second = now + duration;
1127 NS_ASSERT_MSG(per20MhzDurations.size() == m_lastPer20MHzBusyEnd.size(),
1128 "Size of received vector (" << per20MhzDurations.size()
1129 << ") differs from the expected size ("
1130 << m_lastPer20MHzBusyEnd.size() << ")");
1131 for (std::size_t chIdx = 0; chIdx < per20MhzDurations.size(); ++chIdx)
1132 {
1133 if (per20MhzDurations[chIdx].IsStrictlyPositive())
1134 {
1135 m_lastPer20MHzBusyEnd[chIdx] = now + per20MhzDurations[chIdx];
1136 }
1137 }
1138
1140 {
1141 // have all EDCAFs that are not carrying out a TXOP and have the backoff counter set to
1142 // zero proactively generate a new backoff value
1143 for (auto txop : m_txops)
1144 {
1145 if (txop->GetAccessStatus(m_linkId) != Txop::GRANTED &&
1146 txop->GetBackoffSlots(m_linkId) == 0)
1147 {
1148 NS_LOG_DEBUG("Generate backoff for " << txop->GetWifiMacQueue()->GetAc());
1149 txop->GenerateBackoff(m_linkId);
1150 }
1151 }
1152 }
1153}
1154
1155void
1157{
1158 NS_LOG_FUNCTION(this << phyListener << duration);
1159
1160 Time now = Simulator::Now();
1161 NS_ASSERT(m_lastTxEnd <= now);
1162
1163 if (phyListener) // to make tests happy
1164 {
1165 // check if the PHY switched channel to operate on another EMLSR link
1166
1167 for (const auto& [phyRef, listener] : m_phyListeners)
1168 {
1169 Ptr<WifiPhy> phy = phyRef;
1170 auto emlsrInfoIt = m_switchingEmlsrLinks.find(phy);
1171
1172 if (listener.get() == phyListener && emlsrInfoIt != m_switchingEmlsrLinks.cend() &&
1173 phy->GetOperatingChannel() == emlsrInfoIt->second.channel)
1174 {
1175 // the PHY associated with the given PHY listener switched channel to
1176 // operate on another EMLSR link as expected. We don't need this listener
1177 // anymore. The MAC will connect a new listener to the ChannelAccessManager
1178 // instance associated with the link the PHY is now operating on
1179 RemovePhyListener(phy);
1181 NS_ASSERT(ehtFem);
1182 ehtFem->NotifySwitchingEmlsrLink(phy, emlsrInfoIt->second.linkId, duration);
1183 m_switchingEmlsrLinks.erase(emlsrInfoIt);
1184 return;
1185 }
1186 }
1187 }
1188
1189 ResetState();
1190
1191 // Cancel timeout
1193 {
1195 }
1196
1197 // Reset backoffs
1198 for (const auto& txop : m_txops)
1199 {
1200 ResetBackoff(txop);
1201 }
1202
1203 // Notify the FEM, which will in turn notify the MAC
1204 m_feManager->NotifySwitchingStartNow(duration);
1205
1206 NS_LOG_DEBUG("switching start for " << duration);
1207 m_lastSwitchingEnd = now + duration;
1208}
1209
1210void
1212{
1213 NS_LOG_FUNCTION(this);
1214
1215 Time now = Simulator::Now();
1216 m_lastRxReceivedOk = true;
1218 m_lastRx.end = std::min(m_lastRx.end, now);
1219 m_lastNavEnd = std::min(m_lastNavEnd, now);
1222 m_lastNoPhy.end = std::min(m_lastNoPhy.end, now);
1223
1225}
1226
1227void
1229{
1230 NS_LOG_FUNCTION(this << txop);
1231
1232 uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
1233 if (remainingSlots > 0)
1234 {
1235 txop->UpdateBackoffSlotsNow(remainingSlots, Simulator::Now(), m_linkId);
1236 NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
1237 }
1238 txop->ResetCw(m_linkId);
1239 txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
1240}
1241
1242void
1244{
1245 NS_LOG_FUNCTION(this);
1246
1247 for (const auto& txop : m_txops)
1248 {
1249 ResetBackoff(txop);
1250 }
1252}
1253
1254void
1256{
1257 NS_LOG_FUNCTION(this);
1258 // Reset backoffs
1260 m_feManager->NotifySleepNow();
1261 for (auto txop : m_txops)
1262 {
1263 txop->NotifySleep(m_linkId);
1264 }
1265}
1266
1267void
1269{
1270 NS_LOG_FUNCTION(this);
1271 // Cancel timeout
1273 {
1275 }
1276
1277 // Reset backoffs
1278 for (auto txop : m_txops)
1279 {
1280 txop->NotifyOff();
1281 }
1282}
1283
1284void
1286{
1287 NS_LOG_FUNCTION(this);
1289 for (auto txop : m_txops)
1290 {
1291 ResetBackoff(txop);
1292 txop->NotifyWakeUp(m_linkId);
1293 }
1294}
1295
1296void
1298{
1299 NS_LOG_FUNCTION(this);
1301 for (auto txop : m_txops)
1302 {
1303 ResetBackoff(txop);
1304 txop->NotifyOn();
1305 }
1306}
1307
1308void
1310{
1311 NS_LOG_FUNCTION(this << duration);
1312
1313 if (!m_phy)
1314 {
1315 NS_LOG_DEBUG("Do not reset NAV, CTS may have been missed due to the main PHY switching "
1316 "to another link to take over a TXOP while receiving the CTS");
1317 return;
1318 }
1319
1320 NS_LOG_DEBUG("nav reset for=" << duration);
1321 UpdateBackoff();
1322 m_lastNavEnd = Simulator::Now() + duration;
1323 /**
1324 * If the NAV reset indicates an end-of-NAV which is earlier
1325 * than the previous end-of-NAV, the expected end of backoff
1326 * might be later than previously thought so, we might need
1327 * to restart a new access timeout.
1328 */
1330}
1331
1332void
1334{
1335 NS_LOG_FUNCTION(this << duration);
1336 NS_LOG_DEBUG("nav start for=" << duration);
1337 UpdateBackoff();
1338 m_lastNavEnd = std::max(m_lastNavEnd, Simulator::Now() + duration);
1339}
1340
1341void
1348
1349void
1356
1357void
1359{
1360 NS_LOG_FUNCTION(this << duration);
1361 m_lastCtsTimeoutEnd = Simulator::Now() + duration;
1362}
1363
1364void
1371
1372void
1374{
1375 NS_LOG_FUNCTION(this);
1376 Time idleStart = std::max({m_lastTxEnd,
1377 m_lastRx.end,
1381 m_lastOffEnd});
1382 Time now = Simulator::Now();
1383
1384 if (idleStart >= now)
1385 {
1386 // No new idle period
1387 return;
1388 }
1389
1390 for (const auto& busyEnd : m_lastBusyEnd)
1391 {
1392 if (busyEnd.second < now)
1393 {
1394 auto lastIdleIt = m_lastIdle.find(busyEnd.first);
1395 NS_ASSERT(lastIdleIt != m_lastIdle.end());
1396 lastIdleIt->second = {std::max(idleStart, busyEnd.second), now};
1397 NS_LOG_DEBUG("New idle period (" << lastIdleIt->second.start.As(Time::S) << ", "
1398 << lastIdleIt->second.end.As(Time::S)
1399 << ") on channel " << lastIdleIt->first);
1400 }
1401 }
1402}
1403
1404std::ostream&
1405operator<<(std::ostream& os, const WifiExpectedAccessReason& reason)
1406{
1407 switch (reason)
1408 {
1410 return (os << "ACCESS EXPECTED");
1412 return (os << "NOT_REQUESTED");
1414 return (os << "NOTHING_TO_TX");
1416 return (os << "RX_END");
1418 return (os << "BUSY_END");
1420 return (os << "TX_END");
1422 return (os << "NAV_END");
1424 return (os << "ACK_TIMER_END");
1426 return (os << "CTS_TIMER_END");
1428 return (os << "SWITCHING_END");
1430 return (os << "NO_PHY_END");
1432 return (os << "SLEEP_END");
1434 return (os << "OFF_END");
1436 return (os << "BACKOFF_END");
1437 default:
1438 NS_ABORT_MSG("Unknown expected access reason");
1439 return (os << "Unknown");
1440 }
1441}
1442
1443} // namespace ns3
#define Max(a, b)
AttributeValue implementation for Boolean.
Definition boolean.h:26
Manage a set of ns3::Txop.
bool m_proactiveBackoff
whether a new backoff value is generated when a CCA busy period starts and the backoff counter is zer...
std::vector< Time > m_lastPer20MHzBusyEnd
the last busy end time per 20 MHz channel (HE stations and channel width > 20 MHz only)
bool IsBusy() const
Check if the device is busy sending or receiving, or NAV or CCA busy.
void ResetBackoff(Ptr< Txop > txop)
Reset the backoff for the given DCF/EDCAF.
void NotifySwitchingStartNow(PhyListener *phyListener, Time duration)
NSlotsLeftTracedCallback m_nSlotsLeftCallback
traced callback for NSlotsLeft alerts
Time GetBackoffStartFor(Ptr< Txop > txop) const
Return the time when the backoff procedure started for the given Txop.
void ResetState()
Reset the state variables of this channel access manager.
void NotifySwitchingEmlsrLink(Ptr< WifiPhy > phy, const WifiPhyOperatingChannel &channel, uint8_t linkId)
Notify that the given PHY is about to switch to the given operating channel, which is used by the giv...
void ResetAllBackoffs()
Reset the backoff for all the DCF/EDCAF.
void NotifyWakeupNow()
Notify the Txop that the device has been resumed from sleep mode.
bool m_lastRxReceivedOk
the last receive OK
std::unordered_map< Ptr< WifiPhy >, EmlsrLinkSwitchInfo > m_switchingEmlsrLinks
Store information about the PHY objects that are going to operate on another EMLSR link.
std::map< WifiChannelListType, Timespan > m_lastIdle
the last idle start and end time for each channel type
Ptr< WifiPhy > m_phy
pointer to the unique active PHY
void NotifyAckTimeoutResetNow()
Notify that ack timer has reset.
void SetGenerateBackoffOnNoTx(bool enable)
Set the member variable indicating whether the backoff should be invoked when an AC gains the right t...
void NotifyRxEndOkNow()
Notify the Txop that a packet reception was just completed successfully.
virtual Time GetEifsNoDifs() const
Return the EIFS duration minus a DIFS.
uint8_t m_linkId
the ID of the link this object is associated with
uint8_t m_nSlotsLeft
fire the NSlotsLeftAlert trace source when the backoff counter with the minimum value among all ACs r...
void NotifyCcaBusyStartNow(Time duration, WifiChannelListType channelType, const std::vector< Time > &per20MhzDurations)
Time m_lastAckTimeoutEnd
the last Ack timeout end time
Timespan m_lastNoPhy
the last start and end time no PHY was operating on the link
Time m_cachedSlot
cached value for slot, to be only used without a PHY
Time m_eifsNoDifs
EIFS no DIFS time.
virtual Time GetSlot() const
Return the slot duration for this PHY.
Time m_nSlotsLeftMinDelay
the minimum gap between the end of a medium busy event and the time the NSlotsLeftAlert trace source ...
void NotifyAckTimeoutStartNow(Time duration)
Notify that ack timer has started for the given duration.
void AccessTimeout()
Called when access timeout should occur (e.g.
Time GetBackoffEndFor(Ptr< Txop > txop) const
Return the time when the backoff procedure ended (or will end) for the given Txop.
void UpdateBackoff()
Update backoff slots for all Txops.
void DeactivatePhyListener(Ptr< WifiPhy > phy)
Deactivate current registered listener for PHY events on the given PHY.
void SetLinkId(uint8_t linkId)
Set the ID of the link this Channel Access Manager is associated with.
void SetupFrameExchangeManager(Ptr< FrameExchangeManager > feManager)
Set up the Frame Exchange Manager.
bool NeedBackoffUponAccess(Ptr< Txop > txop, bool hadFramesToTransmit, bool checkMediumBusy)
Determine if a new backoff needs to be generated as per letter a) of Section 10.23....
void NotifyCtsTimeoutStartNow(Time duration)
Notify that CTS timer has started for the given duration.
void RequestAccess(Ptr< Txop > txop)
Time m_lastSwitchingEnd
the last switching end time
Timespan m_lastRx
the last receive start and end time
Time m_lastSleepEnd
the last sleep end time
std::map< WifiChannelListType, Time > m_lastBusyEnd
the last busy end time for each channel type
void RemovePhyListener(Ptr< WifiPhy > phy)
Remove current registered listener for PHY events on the given PHY.
bool m_generateBackoffOnNoTx
whether the backoff should be invoked when the AC gains the right to start a TXOP but it does not tra...
Time m_lastTxEnd
the last transmit end time
void SetupPhyListener(Ptr< WifiPhy > phy)
Set up (or reactivate) listener for PHY events on the given PHY.
Time m_lastCtsTimeoutEnd
the last CTS timeout end time
MHz_u GetLargestIdlePrimaryChannel(Time interval, Time end)
Return the width of the largest primary channel that has been idle for the given time interval before...
void DoDispose() override
Destructor implementation.
WifiExpectedAccessReason GetExpectedAccessWithin(const Time &delay) const
Check whether channel access is expected to be granted within the given delay.
void NotifySleepNow()
Notify the Txop that the device has been put in sleep mode.
Ptr< FrameExchangeManager > m_feManager
pointer to the Frame Exchange Manager
void NotifyRxEndErrorNow(const WifiTxVector &txVector)
Notify the Txop that a packet reception was just completed unsuccessfuly.
void UpdateLastIdlePeriod()
This method determines whether the medium has been idle during a period (of non-null duration) immedi...
void DisableEdcaFor(Ptr< Txop > qosTxop, Time duration)
void DoInitialize() override
Initialize() implementation.
Time m_lastOffEnd
the last off end time
Txops m_txops
the vector of managed Txops
std::multimap< Time, WifiExpectedAccessReason > DoGetAccessGrantStart(bool ignoreNav) const
Return a map containing (Time, WifiExpectedAccessReason) pairs sorted in increasing order of times.
bool GetPer20MHzBusy(const std::set< uint8_t > &indices) const
static TypeId GetTypeId()
Get the type ID.
void DoGrantDcfAccess()
Grant access to Txop using DCF/EDCF contention rules.
void ResizeLastBusyStructs()
Resize the structures holding busy end times per channel type (primary, secondary,...
std::shared_ptr< PhyListener > GetPhyListener(Ptr< WifiPhy > phy) const
Get current registered listener for PHY events on the given PHY.
Time m_lastNavEnd
the last NAV end time
void NotifyCtsTimeoutResetNow()
Notify that CTS timer has reset.
void NotifyOffNow()
Notify the Txop that the device has been put in off mode.
Time GetAccessGrantStart(bool ignoreNav=false) const
Access will never be granted to the medium before the time returned by this method.
Time m_cachedSifs
cached value for SIFS, to be only used without a PHY
void NotifyOnNow()
Notify the Txop that the device has been resumed from off mode.
PhyListenerMap m_phyListeners
the PHY listeners
virtual Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
static const Time DEFAULT_N_SLOTS_LEFT_MIN_DELAY
default value for the NSlotsLeftMinDelay attribute, corresponds to a PIFS in 5GHz/6GHz bands
Time m_resetBackoffThreshold
if no PHY operates on a link for a period greater than this threshold, the backoff on that link is re...
EventId m_accessTimeout
the access timeout ID
void InitLastBusyStructs()
Initialize the structures holding busy end times per channel type (primary, secondary,...
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition event-id.cc:65
bool IsExpired() const
This method is syntactic sugar for the ns3::Simulator::IsExpired method.
Definition event-id.cc:58
A base class which provides memory management and object aggregation.
Definition object.h:78
Listener for PHY events.
bool m_active
whether this PHY listener is active
PhyListener(ns3::ChannelAccessManager *cam)
Create a PhyListener for the given ChannelAccessManager.
void NotifyOff() override
Notify listeners that we went to switch off.
void NotifySleep() override
Notify listeners that we went to sleep.
ns3::ChannelAccessManager * m_cam
ChannelAccessManager to forward events to.
void NotifyRxStart(Time duration) override
void NotifyOn() override
Notify listeners that we went to switch on.
void NotifySwitchingStart(Time duration) override
void SetActive(bool active)
Set this listener to be active or not.
void NotifyRxEndOk() override
We have received the last bit of a packet for which NotifyRxStart was invoked first and,...
void NotifyWakeup() override
Notify listeners that we woke up.
void NotifyCcaBusyStart(Time duration, WifiChannelListType channelType, const std::vector< Time > &per20MhzDurations) override
void NotifyRxEndError(const WifiTxVector &txVector) override
void NotifyTxStart(Time duration, dBm_u txPower) override
Smart pointer class similar to boost::intrusive_ptr.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static Time GetMaximumSimulationTime()
Get the maximum representable simulation time.
Definition simulator.cc:300
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition simulator.cc:206
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:403
@ US
microsecond
Definition nstime.h:107
@ S
second
Definition nstime.h:105
AttributeValue implementation for Time.
Definition nstime.h:1432
@ GRANTED
Definition txop.h:79
@ NOT_REQUESTED
Definition txop.h:77
@ REQUESTED
Definition txop.h:78
a unique identifier for an interface.
Definition type-id.h:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
Time GetSlot() const
Return the slot duration for this PHY.
Definition wifi-phy.cc:850
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition wifi-phy.cc:838
bool IsStateOff() const
Definition wifi-phy.cc:2189
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1057
Time GetPifs() const
Return the PCF Interframe Space (PIFS) for this PHY.
Definition wifi-phy.cc:862
MHz_u GetChannelWidth() const
Definition wifi-phy.cc:1087
bool IsStateSleep() const
Definition wifi-phy.cc:2183
WifiStandard GetStandard() const
Get the configured Wi-Fi standard.
Definition wifi-phy.cc:1063
void NotifyChannelAccessRequested()
Notify the PHY that an access to the channel was requested.
Definition wifi-phy.cc:2015
const WifiPhyOperatingChannel & GetOperatingChannel() const
Get a const reference to the operating channel.
Definition wifi-phy.cc:1069
receive notifications about PHY events.
Class that keeps track of all information about the current PHY operating channel.
bool IsOfdm() const
Return whether the operating channel is an OFDM channel.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition boolean.h:70
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition nstime.h:1433
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1453
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition uinteger.h:35
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1345
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
WifiExpectedAccessReason
Enumeration values for the outcome of the check whether channel access is expected to be gained withi...
WifiChannelListType
Enumeration of the possible channel-list parameter elements defined in Table 8-5 of IEEE 802....
@ WIFI_STANDARD_80211ax
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
@ WIFI_CHANLIST_PRIMARY
@ WIFI_CHANLIST_SECONDARY40
@ WIFI_CHANLIST_SECONDARY
@ WIFI_CHANLIST_SECONDARY160
@ WIFI_CHANLIST_SECONDARY80
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition angles.cc:148
double MHz_u
MHz weak type.
Definition wifi-units.h:31
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
Time GetEstimatedAckTxTime(const WifiTxVector &txVector)
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:138
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
Structure defining start time and end time for a given state.