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 <cmath>
21#include <sstream>
22
23#undef NS_LOG_APPEND_CONTEXT
24#define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "] "
25
26namespace ns3
27{
28
29NS_LOG_COMPONENT_DEFINE("ChannelAccessManager");
30
31NS_OBJECT_ENSURE_REGISTERED(ChannelAccessManager);
32
34
35/**
36 * Listener for PHY events. Forwards to ChannelAccessManager.
37 * The ChannelAccessManager may handle multiple PHY listeners connected to distinct PHYs,
38 * but only one listener at a time can be active. Notifications from inactive listeners are
39 * ignored by the ChannelAccessManager, except for the channel switch notification.
40 * Inactive PHY listeners are typically configured by 11be EMLSR clients.
41 */
43{
44 public:
45 /**
46 * Create a PhyListener for the given ChannelAccessManager.
47 *
48 * @param cam the ChannelAccessManager
49 */
51 : m_cam(cam),
52 m_active(true)
53 {
54 }
55
56 ~PhyListener() override
57 {
58 }
59
60 /**
61 * Set this listener to be active or not.
62 *
63 * @param active whether this listener is active or not
64 */
65 void SetActive(bool active)
66 {
67 m_active = active;
68 }
69
70 /**
71 * @return whether this listener is active or not
72 */
73 bool IsActive() const
74 {
75 return m_active;
76 }
77
78 void NotifyRxStart(Time duration) override
79 {
80 if (m_active)
81 {
82 m_cam->NotifyRxStartNow(duration);
83 }
84 }
85
86 void NotifyRxEndOk() override
87 {
88 if (m_active)
89 {
90 m_cam->NotifyRxEndOkNow();
91 }
92 }
93
94 void NotifyRxEndError(const WifiTxVector& txVector) override
95 {
96 if (m_active)
97 {
98 m_cam->NotifyRxEndErrorNow(txVector);
99 }
100 }
101
102 void NotifyTxStart(Time duration, dBm_u txPower) override
103 {
104 if (m_active)
105 {
106 m_cam->NotifyTxStartNow(duration);
107 }
108 }
109
111 WifiChannelListType channelType,
112 const std::vector<Time>& per20MhzDurations) override
113 {
114 if (m_active)
115 {
116 m_cam->NotifyCcaBusyStartNow(duration, channelType, per20MhzDurations);
117 }
118 }
119
120 void NotifySwitchingStart(Time duration) override
121 {
122 m_cam->NotifySwitchingStartNow(this, duration);
123 }
124
125 void NotifySleep() override
126 {
127 if (m_active)
128 {
129 m_cam->NotifySleepNow();
130 }
131 }
132
133 void NotifyOff() override
134 {
135 if (m_active)
136 {
137 m_cam->NotifyOffNow();
138 }
139 }
140
141 void NotifyWakeup() override
142 {
143 if (m_active)
144 {
145 m_cam->NotifyWakeupNow();
146 }
147 }
148
149 void NotifyOn() override
150 {
151 if (m_active)
152 {
153 m_cam->NotifyOnNow();
154 }
155 }
156
157 private:
158 ns3::ChannelAccessManager* m_cam; //!< ChannelAccessManager to forward events to
159 bool m_active; //!< whether this PHY listener is active
160};
161
162/****************************************************************
163 * Implement the channel access manager of all Txop holders
164 ****************************************************************/
165
166TypeId
168{
169 static TypeId tid =
170 TypeId("ns3::ChannelAccessManager")
172 .SetGroupName("Wifi")
173 .AddConstructor<ChannelAccessManager>()
174 .AddAttribute("GenerateBackoffIfTxopWithoutTx",
175 "Specify whether the backoff should be invoked when the AC gains the "
176 "right to start a TXOP but it does not transmit any frame "
177 "(e.g., due to constraints associated with EMLSR operations), "
178 "provided that the queue is not actually empty.",
179 BooleanValue(false),
183 .AddAttribute("ProactiveBackoff",
184 "Specify whether a new backoff value is generated when a CCA busy "
185 "period starts, the backoff counter is zero and the station is not a "
186 "TXOP holder. This is useful to generate a new backoff value when, "
187 "e.g., the backoff counter reaches zero, the station does not transmit "
188 "and subsequently the medium becomes busy.",
189 BooleanValue(false),
192 .AddAttribute("ResetBackoffThreshold",
193 "If no PHY operates on this link, or the PHY operating on this link "
194 "stays in sleep mode or off mode, for a period greater than this "
195 "threshold, all the backoffs are reset.",
196 TimeValue(Time{0}),
199 .AddAttribute("NSlotsLeft",
200 "The NSlotsLeftAlert trace source is fired when the number of remaining "
201 "backoff slots for any AC is equal to or less than the value of this "
202 "attribute. Note that the trace source is fired only if the AC for which "
203 "the previous condition is met has requested channel access. Also, if "
204 "the value of this attribute is zero, the trace source is never fired.",
205 UintegerValue(0),
208 .AddAttribute("NSlotsLeftMinDelay",
209 "The minimum gap between the end of a medium busy event and the time "
210 "the NSlotsLeftAlert trace source can be fired.",
214 .AddTraceSource("NSlotsLeftAlert",
215 "The number of remaining backoff slots for the AC with the given index "
216 "reached the threshold set through the NSlotsLeft attribute.",
218 "ns3::ChannelAccessManager::NSlotsLeftCallback");
219 return tid;
220}
221
225 m_lastNavEnd(0),
227 m_lastRxReceivedOk(true),
228 m_lastTxEnd(0),
229 m_lastSwitchingEnd(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 m_txops.clear();
253 m_phy = nullptr;
254 m_feManager = nullptr;
255 m_phyListeners.clear();
256}
257
258std::shared_ptr<PhyListener>
260{
261 if (auto listenerIt = m_phyListeners.find(phy); listenerIt != m_phyListeners.end())
262 {
263 return listenerIt->second;
264 }
265 return nullptr;
266}
267
268void
270{
271 NS_LOG_FUNCTION(this << phy);
272
273 const auto now = Simulator::Now();
274 auto phyListener = GetPhyListener(phy);
275
276 if (phyListener)
277 {
278 // a PHY listener for the given PHY already exists, it must be inactive
279 NS_ASSERT_MSG(!phyListener->IsActive(),
280 "There is already an active listener registered for given PHY");
281 NS_ASSERT_MSG(!m_phy, "Cannot reactivate a listener if another PHY is active");
282 phyListener->SetActive(true);
283 // if a PHY listener already exists, the PHY was disconnected and now reconnected to the
284 // channel access manager; unregister the listener and register again (below) to get
285 // updated CCA busy information
286 phy->UnregisterListener(phyListener);
287 // we expect that the PHY is reconnected immediately after the other PHY left the link:
288 // reset the start of m_lastNoPhy so as to ignore this event
289 NS_ASSERT(m_lastNoPhy.start == now);
290 NS_ASSERT(m_lastNoPhy.end <= m_lastNoPhy.start);
291 m_lastNoPhy.start = m_lastNoPhy.end;
292 }
293 else
294 {
295 phyListener = std::make_shared<PhyListener>(this);
296 m_phyListeners.emplace(phy, phyListener);
297 if (m_phy)
298 {
300 }
301 else
302 {
303 // no PHY operating on this link and no previous PHY listener to reactivate
304 m_lastSwitchingEnd = now;
305 m_lastNoPhy.end = now;
306 if (now - m_lastNoPhy.start > m_resetBackoffThreshold)
307 {
309 }
310 }
311 }
312
313 m_phy = phy; // this is the new active PHY
315 phy->RegisterListener(phyListener);
316}
317
318void
320{
321 NS_LOG_FUNCTION(this << phy);
322 if (auto phyListener = GetPhyListener(phy))
323 {
324 phy->UnregisterListener(phyListener);
325 m_phyListeners.erase(phy);
326 // reset m_phy if we are removing listener registered for the active PHY
327 if (m_phy == phy)
328 {
331 m_phy = nullptr;
332 m_lastNoPhy.start = Simulator::Now();
333 }
334 }
335}
336
337void
339{
340 NS_LOG_FUNCTION(this << phy);
341 if (auto listener = GetPhyListener(phy))
342 {
343 listener->SetActive(false);
344 }
345}
346
347void
349 const WifiPhyOperatingChannel& channel,
350 uint8_t linkId)
351{
352 NS_LOG_FUNCTION(this << phy << channel << linkId);
354 "The given PHY is already expected to switch channel");
355 m_switchingEmlsrLinks.emplace(phy, EmlsrLinkSwitchInfo{channel, linkId});
356}
357
358void
360{
361 NS_LOG_FUNCTION(this << +linkId);
362 m_linkId = linkId;
363}
364
365void
367{
368 NS_LOG_FUNCTION(this << feManager);
369 m_feManager = feManager;
370 m_feManager->SetChannelAccessManager(this);
371}
372
373Time
375{
376 if (m_phy)
377 {
378 m_cachedSlot = m_phy->GetSlot();
379 }
380 return m_cachedSlot;
381}
382
383Time
385{
386 if (m_phy)
387 {
388 m_cachedSifs = m_phy->GetSifs();
389 }
390 return m_cachedSifs;
391}
392
393Time
398
399void
401{
402 NS_LOG_FUNCTION(this << txop);
403 m_txops.push_back(txop);
404}
405
406void
408{
409 NS_LOG_FUNCTION(this);
410 const auto now = Simulator::Now();
411
413 m_lastIdle.emplace(WIFI_CHANLIST_PRIMARY, Timespan{now, now});
414
415 const auto width = m_phy ? m_phy->GetChannelWidth() : MHz_u{0};
416 std::size_t size = (width > MHz_u{20} && m_phy->GetStandard() >= WIFI_STANDARD_80211ax)
417 ? Count20MHzSubchannels(width)
418 : 0;
419 m_lastPer20MHzBusyEnd.resize(size, now);
420
421 if (!m_phy || !m_phy->GetOperatingChannel().IsOfdm())
422 {
423 return;
424 }
425
426 if (width >= MHz_u{40})
427 {
429 m_lastIdle.emplace(WIFI_CHANLIST_SECONDARY, Timespan{now, now});
430 }
431 else
432 {
435 }
436
437 if (width >= MHz_u{80})
438 {
441 }
442 else
443 {
446 }
447
448 if (width >= MHz_u{160})
449 {
452 }
453 else
454 {
457 }
458
459 if (width >= MHz_u{320})
460 {
463 }
464 else
465 {
468 }
469
470 // TODO Add conditions for new channel widths as they get supported
471}
472
473void
475{
476 NS_LOG_FUNCTION(this);
477 Time now = Simulator::Now();
478
480
481 // reset all values
482 for (auto& [chType, time] : m_lastBusyEnd)
483 {
484 time = now;
485 }
486
487 for (auto& [chType, timeSpan] : m_lastIdle)
488 {
489 timeSpan = Timespan{now, now};
490 }
491
492 for (auto& time : m_lastPer20MHzBusyEnd)
493 {
494 time = now;
495 }
496}
497
498bool
500{
501 NS_LOG_FUNCTION(this);
502 Time now = Simulator::Now();
503 return (m_lastRx.end > now) // RX
504 || (m_lastTxEnd > now) // TX
505 || (m_lastNavEnd > now) // NAV busy
506 // an EDCA TXOP is obtained based solely on activity of the primary channel
507 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
508 || (m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now); // CCA busy
509}
510
511bool
513 bool hadFramesToTransmit,
514 bool checkMediumBusy)
515{
516 NS_LOG_FUNCTION(this << txop << hadFramesToTransmit << checkMediumBusy);
517
518 // No backoff needed if in sleep mode or off. Checking if m_phy is nullptr is a workaround
519 // needed for EMLSR and may be removed in the future
520 if (!m_phy || m_phy->IsStateSleep() || m_phy->IsStateOff())
521 {
522 return false;
523 }
524
525 // the Txop might have a stale value of remaining backoff slots
527
528 /*
529 * From section 10.3.4.2 "Basic access" of IEEE 802.11-2016:
530 *
531 * A STA may transmit an MPDU when it is operating under the DCF access
532 * method, either in the absence of a PC, or in the CP of the PCF access
533 * method, when the STA determines that the medium is idle when a frame is
534 * queued for transmission, and remains idle for a period of a DIFS, or an
535 * EIFS (10.3.2.3.7) from the end of the immediately preceding medium-busy
536 * event, whichever is the greater, and the backoff timer is zero. Otherwise
537 * the random backoff procedure described in 10.3.4.3 shall be followed.
538 *
539 * From section 10.22.2.2 "EDCA backoff procedure" of IEEE 802.11-2016:
540 *
541 * The backoff procedure shall be invoked by an EDCAF when any of the following
542 * events occurs:
543 * a) An MA-UNITDATA.request primitive is received that causes a frame with that AC
544 * to be queued for transmission such that one of the transmit queues associated
545 * with that AC has now become non-empty and any other transmit queues
546 * associated with that AC are empty; the medium is busy on the primary channel
547 */
548 if (!hadFramesToTransmit && txop->HasFramesToTransmit(m_linkId) &&
549 txop->GetAccessStatus(m_linkId) != Txop::GRANTED && txop->GetBackoffSlots(m_linkId) == 0)
550 {
551 if (checkMediumBusy && !IsBusy())
552 {
553 // medium idle. If this is a DCF, use immediate access (we can transmit
554 // in a DIFS if the medium remains idle). If this is an EDCAF, update
555 // the backoff start time kept by the EDCAF to the current time in order
556 // to correctly align the backoff start time at the next slot boundary
557 // (performed by the next call to ChannelAccessManager::RequestAccess())
558 Time delay =
559 (txop->IsQosTxop() ? Seconds(0) : GetSifs() + txop->GetAifsn(m_linkId) * GetSlot());
560 txop->UpdateBackoffSlotsNow(0, Simulator::Now() + delay, m_linkId);
561 }
562 else
563 {
564 // medium busy, backoff is needed
565 return true;
566 }
567 }
568 return false;
569}
570
571void
573{
574 NS_LOG_FUNCTION(this << txop);
575 if (m_phy && txop->HasFramesToTransmit(m_linkId))
576 {
577 m_phy->NotifyChannelAccessRequested();
578 }
579 // Deny access if in sleep mode or off. Checking if m_phy is nullptr is a workaround
580 // needed for EMLSR and may be removed in the future
581 if (!m_phy || m_phy->IsStateSleep() || m_phy->IsStateOff())
582 {
583 return;
584 }
585 /*
586 * EDCAF operations shall be performed at slot boundaries (Sec. 10.22.2.4 of 802.11-2016)
587 */
588 Time accessGrantStart = GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot());
589
590 if (const auto diff = txop->GetBackoffStart(m_linkId) - accessGrantStart;
591 txop->IsQosTxop() && diff.IsStrictlyPositive())
592 {
593 // The backoff start time reported by the EDCAF is more recent than the last time the medium
594 // was busy plus an AIFS, hence we need to align it to the next slot boundary.
595 const auto div = diff / GetSlot();
596 const uint32_t nIntSlots = div.GetHigh() + (div.GetLow() > 0 ? 1 : 0);
597 txop->UpdateBackoffSlotsNow(0, accessGrantStart + (nIntSlots * GetSlot()), m_linkId);
598 }
599
601 NS_ASSERT(txop->GetAccessStatus(m_linkId) != Txop::REQUESTED);
602 txop->NotifyAccessRequested(m_linkId);
605}
606
607void
609{
610 NS_LOG_FUNCTION(this);
611 uint32_t k = 0;
612 const auto now = Simulator::Now();
613 const auto accessGrantStart = GetAccessGrantStart();
614 if (accessGrantStart > now)
615 {
616 NS_LOG_DEBUG("access cannot be granted yet");
617 return;
618 }
619
620 for (auto i = m_txops.begin(); i != m_txops.end(); k++)
621 {
622 Ptr<Txop> txop = *i;
623 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
624 (!txop->IsQosTxop() || !StaticCast<QosTxop>(txop)->EdcaDisabled(m_linkId)) &&
625 GetBackoffEndFor(txop, accessGrantStart) <= now)
626 {
627 /**
628 * This is the first Txop we find with an expired backoff and which
629 * needs access to the medium. i.e., it has data to send.
630 */
631 NS_LOG_DEBUG("dcf " << k << " needs access. backoff expired. access granted. slots="
632 << txop->GetBackoffSlots(m_linkId));
633 i++; // go to the next item in the list.
634 k++;
635 std::vector<Ptr<Txop>> internalCollisionTxops;
636 for (auto j = i; j != m_txops.end(); j++, k++)
637 {
638 Ptr<Txop> otherTxop = *j;
639 if (otherTxop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
640 GetBackoffEndFor(otherTxop, accessGrantStart) <= now)
641 {
643 "dcf " << k << " needs access. backoff expired. internal collision. slots="
644 << otherTxop->GetBackoffSlots(m_linkId));
645 /**
646 * all other Txops with a lower priority whose backoff
647 * has expired and which needed access to the medium
648 * must be notified that we did get an internal collision.
649 */
650 internalCollisionTxops.push_back(otherTxop);
651 }
652 }
653
654 /**
655 * Now, we notify all of these changes in one go if the EDCAF winning
656 * the contention actually transmitted a frame. It is necessary to
657 * perform first the calculations of which Txops are colliding and then
658 * only apply the changes because applying the changes through notification
659 * could change the global state of the manager, and, thus, could change
660 * the result of the calculations.
661 */
663 // If we are operating on an OFDM channel wider than 20 MHz, find the largest
664 // idle primary channel and pass its width to the FrameExchangeManager, so that
665 // the latter can transmit PPDUs of the appropriate width (see Section 10.23.2.5
666 // of IEEE 802.11-2020).
667 auto interval = (m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
668 ? GetSifs() + 2 * GetSlot()
669 : m_phy->GetPifs();
670 auto width =
671 (m_phy->GetOperatingChannel().IsOfdm() && m_phy->GetChannelWidth() > MHz_u{20})
672 ? GetLargestIdlePrimaryChannel(interval, now)
673 : m_phy->GetChannelWidth();
674 if (m_feManager->StartTransmission(txop, width))
675 {
676 for (auto& collidingTxop : internalCollisionTxops)
677 {
678 m_feManager->NotifyInternalCollision(collidingTxop);
679 }
680 break;
681 }
682 else
683 {
684 // this TXOP did not transmit anything, make sure that backoff counter starts
685 // decreasing in a slot again
686 txop->UpdateBackoffSlotsNow(0, now, m_linkId);
687 // reset the current state to the EDCAF that won the contention
688 // but did not transmit anything
689 i--;
690 k = std::distance(m_txops.begin(), i);
691 }
692 }
693 i++;
694 }
695}
696
697void
699{
700 NS_LOG_FUNCTION(this);
701
702 const auto now = Simulator::Now();
703 const auto noPhyForTooLong = (!m_phy && now - m_lastNoPhy.start > m_resetBackoffThreshold);
704 const auto sleepForTooLong =
705 (m_phy && m_phy->IsStateSleep() && now - m_lastSleep.start > m_resetBackoffThreshold);
706 const auto offForTooLong =
707 (m_phy && m_phy->IsStateOff() && now - m_lastOff.start > m_resetBackoffThreshold);
708
709 if (noPhyForTooLong || sleepForTooLong || offForTooLong)
710 {
712 return;
713 }
714
718}
719
720std::multimap<Time, WifiExpectedAccessReason>
722{
723 NS_LOG_FUNCTION(this << ignoreNav);
724 const auto now = Simulator::Now();
725
726 std::multimap<Time, WifiExpectedAccessReason> ret;
727
728 // an EDCA TXOP is obtained based solely on activity of the primary channel
729 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
730 const auto busyAccessStart = m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY);
731 ret.emplace(busyAccessStart, WifiExpectedAccessReason::BUSY_END);
732
733 auto rxAccessStart = m_lastRx.end;
734 if ((m_lastRx.end <= now) && !m_lastRxReceivedOk)
735 {
736 rxAccessStart += GetEifsNoDifs();
737 }
738 ret.emplace(rxAccessStart, WifiExpectedAccessReason::RX_END);
739
741
742 const auto navAccessStart = ignoreNav ? Time{0} : m_lastNavEnd;
743 ret.emplace(navAccessStart, WifiExpectedAccessReason::NAV_END);
744
748
749 const auto noPhyStart = m_phy ? m_lastNoPhy.end : now;
750 ret.emplace(noPhyStart, WifiExpectedAccessReason::NO_PHY_END);
751
752 const auto lastSleepEnd = (m_lastSleep.start > m_lastSleep.end ? now : m_lastSleep.end);
753 ret.emplace(lastSleepEnd, WifiExpectedAccessReason::SLEEP_END);
754
755 const auto lastOffEnd = (m_lastOff.start > m_lastOff.end ? now : m_lastOff.end);
756 ret.emplace(lastOffEnd, WifiExpectedAccessReason::OFF_END);
757
758 NS_LOG_INFO("rx access start=" << rxAccessStart.As(Time::US)
759 << ", busy access start=" << busyAccessStart.As(Time::US)
760 << ", tx access start=" << m_lastTxEnd.As(Time::US)
761 << ", nav access start=" << navAccessStart.As(Time::US)
762 << ", switching access start=" << m_lastSwitchingEnd.As(Time::US)
763 << ", no PHY start=" << noPhyStart.As(Time::US)
764 << ", sleep access start=" << lastSleepEnd.As(Time::US)
765 << ", off access start=" << lastOffEnd.As(Time::US));
766 return ret;
767}
768
769Time
771{
772 NS_LOG_FUNCTION(this << ignoreNav);
773
774 auto timeReasonMap = DoGetAccessGrantStart(ignoreNav);
775 NS_ASSERT(!timeReasonMap.empty());
776 const auto accessGrantedStart = timeReasonMap.crbegin()->first;
777 NS_LOG_INFO("access grant start=" << accessGrantedStart.As(Time::US));
778
779 return accessGrantedStart + GetSifs();
780}
781
782Time
787
788Time
790{
791 NS_LOG_FUNCTION(this << txop << accessGrantStart.As(Time::S));
792 const auto mostRecentEvent =
793 std::max({txop->GetBackoffStart(m_linkId),
794 accessGrantStart + (txop->GetAifsn(m_linkId) * GetSlot())});
795 NS_LOG_DEBUG("Backoff start for " << txop->GetWifiMacQueue()->GetAc() << ": "
796 << mostRecentEvent.As(Time::US));
797
798 return mostRecentEvent;
799}
800
801Time
806
807Time
809{
810 NS_LOG_FUNCTION(this << txop);
811 Time backoffEnd =
812 GetBackoffStartFor(txop, accessGrantStart) + (txop->GetBackoffSlots(m_linkId) * GetSlot());
813 NS_LOG_DEBUG("Backoff end for " << txop->GetWifiMacQueue()->GetAc() << ": "
814 << backoffEnd.As(Time::US));
815
816 return backoffEnd;
817}
818
821{
822 NS_LOG_FUNCTION(this << delay.As(Time::US));
823
824 const auto now = Simulator::Now();
825 const auto deadline = now + delay;
826 const auto timeReasonMap = DoGetAccessGrantStart(false);
827 NS_ASSERT(!timeReasonMap.empty());
828 auto accessGrantStart = timeReasonMap.crbegin()->first;
829
830 if (accessGrantStart >= deadline)
831 {
832 // return the earliest reason for which access cannot be granted in time
833 for (const auto& [time, reason] : timeReasonMap)
834 {
835 if (time >= deadline)
836 {
841 NS_LOG_DEBUG("Access grant start (" << accessGrantStart.As(Time::US)
842 << ") too late for reason " << reason);
843 return reason;
844 }
845 }
846 NS_ABORT_MSG("No reason found that exceeds the deadline!");
847 }
848
849 accessGrantStart += GetSifs();
851
852 for (auto txop : m_txops)
853 {
854 if (txop->GetAccessStatus(m_linkId) != Txop::REQUESTED)
855 {
856 continue;
857 }
858
859 if (!txop->HasFramesToTransmit(m_linkId))
860 {
862 {
864 }
865 continue;
866 }
867
869 const auto backoffEnd = GetBackoffEndFor(txop, accessGrantStart);
870
871 if (backoffEnd >= now && backoffEnd <= deadline)
872 {
873 NS_LOG_DEBUG("Backoff end for " << txop->GetWifiMacQueue()->GetAc() << " on link "
874 << +m_linkId << ": " << backoffEnd.As(Time::US));
876 }
877 }
878
879 NS_LOG_DEBUG("Access grant not expected for reason: " << reason);
880 return reason;
881}
882
883Time
885{
886 return m_lastNavEnd;
887}
888
889void
891{
892 NS_LOG_FUNCTION(this);
893 uint32_t k = 0;
894 const auto accessGrantStart = GetAccessGrantStart();
895 for (auto txop : m_txops)
896 {
897 Time backoffStart = GetBackoffStartFor(txop, accessGrantStart);
898 if (backoffStart <= Simulator::Now())
899 {
900 uint32_t nIntSlots = ((Simulator::Now() - backoffStart) / GetSlot()).GetHigh();
901 /*
902 * EDCA behaves slightly different to DCA. For EDCA we
903 * decrement once at the slot boundary at the end of AIFS as
904 * well as once at the end of each clear slot
905 * thereafter. For DCA we only decrement at the end of each
906 * clear slot after DIFS. We account for the extra backoff
907 * by incrementing the slot count here in the case of
908 * EDCA. The if statement whose body we are in has confirmed
909 * that a minimum of AIFS has elapsed since last busy
910 * medium.
911 */
912 if (txop->IsQosTxop())
913 {
914 nIntSlots++;
915 }
916 uint32_t n = std::min(nIntSlots, txop->GetBackoffSlots(m_linkId));
917 NS_LOG_DEBUG("dcf " << k << " dec backoff slots=" << n);
918 Time backoffUpdateBound = backoffStart + (n * GetSlot());
919 txop->UpdateBackoffSlotsNow(n, backoffUpdateBound, m_linkId);
920 }
921 ++k;
922 }
923}
924
925void
927{
928 NS_LOG_FUNCTION(this);
929 /**
930 * Is there a Txop which needs to access the medium, and,
931 * if there is one, how many slots for AIFS+backoff does it require ?
932 */
933 Ptr<Txop> nextTxop;
934 auto expectedBackoffEnd = Simulator::GetMaximumSimulationTime();
935 const auto accessGrantStart = GetAccessGrantStart();
936 const auto now = Simulator::Now();
937 for (auto txop : m_txops)
938 {
939 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED)
940 {
941 if (auto backoffEnd = GetBackoffEndFor(txop, accessGrantStart);
942 backoffEnd > now && backoffEnd < expectedBackoffEnd)
943 {
944 expectedBackoffEnd = backoffEnd;
945 nextTxop = txop;
946 }
947 }
948 }
949 NS_LOG_DEBUG("Access timeout needed: " << (nextTxop != nullptr));
950 if (nextTxop)
951 {
952 const auto aci = nextTxop->GetWifiMacQueue()->GetAc();
953 NS_LOG_DEBUG("expected backoff end=" << expectedBackoffEnd << " by " << aci);
954 auto expectedBackoffDelay = expectedBackoffEnd - now;
955
956 if (m_nSlotsLeft > 0)
957 {
958 const auto expectedNotifyTime =
959 Max(expectedBackoffEnd - m_nSlotsLeft * GetSlot(),
960 accessGrantStart - GetSifs() + m_nSlotsLeftMinDelay);
961
962 if (expectedNotifyTime > now)
963 {
964 // make the timer expire when it's time to notify that the given slots are left
965 expectedBackoffDelay = expectedNotifyTime - now;
966 }
967 else
968 {
969 // notify that a number of slots less than or equal to the specified value are left
970 m_nSlotsLeftCallback(m_linkId, aci, expectedBackoffDelay);
971 }
972 }
973
974 if (m_accessTimeout.IsPending() &&
975 Simulator::GetDelayLeft(m_accessTimeout) > expectedBackoffDelay)
976 {
977 m_accessTimeout.Cancel();
978 }
979 if (m_accessTimeout.IsExpired())
980 {
981 m_accessTimeout = Simulator::Schedule(expectedBackoffDelay,
983 this);
984 }
985 }
986}
987
988MHz_u
990{
991 NS_LOG_FUNCTION(this << interval.As(Time::US) << end.As(Time::S));
992
993 // If the medium is busy or it just became idle, UpdateLastIdlePeriod does
994 // nothing. This allows us to call this method, e.g., at the end of a frame
995 // reception and check the busy/idle status of the channel before the start
996 // of the frame reception (last idle period was last updated at the start of
997 // the frame reception).
998 // If the medium has been idle for some time, UpdateLastIdlePeriod updates
999 // the last idle period. This is normally what we want because this method may
1000 // also be called before starting a TXOP gained through EDCA.
1002
1003 MHz_u width{0};
1004
1005 // we iterate over the different types of channels in the same order as they
1006 // are listed in WifiChannelListType
1007 for (const auto& lastIdle : m_lastIdle)
1008 {
1009 if (lastIdle.second.start <= end - interval && lastIdle.second.end >= end)
1010 {
1011 // channel idle, update width
1012 width = (width == MHz_u{0}) ? MHz_u{20} : (2 * width);
1013 }
1014 else
1015 {
1016 break;
1017 }
1018 }
1019 return width;
1020}
1021
1022bool
1023ChannelAccessManager::GetPer20MHzBusy(const std::set<uint8_t>& indices) const
1024{
1025 const auto now = Simulator::Now();
1026
1027 if (m_phy->GetChannelWidth() < MHz_u{40})
1028 {
1029 NS_ASSERT_MSG(indices.size() == 1 && *indices.cbegin() == 0,
1030 "Index 0 only can be specified if the channel width is less than 40 MHz");
1031 return m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now;
1032 }
1033
1034 for (const auto index : indices)
1035 {
1036 NS_ASSERT(index < m_lastPer20MHzBusyEnd.size());
1037 if (m_lastPer20MHzBusyEnd.at(index) > now)
1038 {
1039 NS_LOG_DEBUG("20 MHz channel with index " << +index << " is busy");
1040 return true;
1041 }
1042 }
1043 return false;
1044}
1045
1046void
1048{
1049 NS_LOG_FUNCTION(this << qosTxop << duration);
1050 NS_ASSERT(qosTxop->IsQosTxop());
1051 UpdateBackoff();
1052 Time resume = Simulator::Now() + duration;
1053 NS_LOG_DEBUG("Backoff will resume at time " << resume << " with "
1054 << qosTxop->GetBackoffSlots(m_linkId)
1055 << " remaining slot(s)");
1056 qosTxop->UpdateBackoffSlotsNow(0, resume, m_linkId);
1058}
1059
1060void
1062{
1063 NS_LOG_FUNCTION(this << enable);
1064 m_generateBackoffOnNoTx = enable;
1065}
1066
1067bool
1072
1073void
1075{
1076 NS_LOG_FUNCTION(this << duration);
1077 NS_LOG_DEBUG("rx start for=" << duration);
1078 UpdateBackoff();
1080 m_lastRx.start = Simulator::Now();
1081 m_lastRx.end = m_lastRx.start + duration;
1082 m_lastRxReceivedOk = true;
1083}
1084
1085void
1087{
1088 NS_LOG_FUNCTION(this);
1089 NS_LOG_DEBUG("rx end ok");
1090 m_lastRx.end = Simulator::Now();
1091 m_lastRxReceivedOk = true;
1092}
1093
1094void
1096{
1097 NS_LOG_FUNCTION(this);
1098 NS_LOG_DEBUG("rx end error");
1099 // we expect the PHY to notify us of the start of a CCA busy period, if needed
1100 m_lastRx.end = Simulator::Now();
1101 m_lastRxReceivedOk = false;
1102 m_eifsNoDifs = m_phy->GetSifs() + GetEstimatedAckTxTime(txVector);
1103}
1104
1105void
1107{
1108 NS_LOG_FUNCTION(this << duration);
1109 m_lastRxReceivedOk = true;
1110 Time now = Simulator::Now();
1111 if (m_lastRx.end > now)
1112 {
1113 // this may be caused only if PHY has started to receive a packet
1114 // inside SIFS, so, we check that lastRxStart was maximum a SIFS ago
1115 NS_ASSERT(now - m_lastRx.start <= GetSifs());
1116 m_lastRx.end = now;
1117 }
1118 else
1119 {
1121 }
1122 NS_LOG_DEBUG("tx start for " << duration);
1123 UpdateBackoff();
1124 m_lastTxEnd = now + duration;
1125}
1126
1127void
1129 WifiChannelListType channelType,
1130 const std::vector<Time>& per20MhzDurations)
1131{
1132 NS_LOG_FUNCTION(this << duration << channelType);
1133 UpdateBackoff();
1135 auto lastBusyEndIt = m_lastBusyEnd.find(channelType);
1136 NS_ASSERT(lastBusyEndIt != m_lastBusyEnd.end());
1137 Time now = Simulator::Now();
1138 lastBusyEndIt->second = now + duration;
1139 NS_ASSERT_MSG(per20MhzDurations.size() == m_lastPer20MHzBusyEnd.size(),
1140 "Size of received vector (" << per20MhzDurations.size()
1141 << ") differs from the expected size ("
1142 << m_lastPer20MHzBusyEnd.size() << ")");
1143 for (std::size_t chIdx = 0; chIdx < per20MhzDurations.size(); ++chIdx)
1144 {
1145 if (per20MhzDurations[chIdx].IsStrictlyPositive())
1146 {
1147 m_lastPer20MHzBusyEnd[chIdx] = now + per20MhzDurations[chIdx];
1148 }
1149 }
1150
1152 {
1153 // have all EDCAFs that are not carrying out a TXOP and have the backoff counter set to
1154 // zero proactively generate a new backoff value
1155 for (auto txop : m_txops)
1156 {
1157 if (txop->GetAccessStatus(m_linkId) != Txop::GRANTED &&
1158 txop->GetBackoffSlots(m_linkId) == 0)
1159 {
1160 NS_LOG_DEBUG("Generate backoff for " << txop->GetWifiMacQueue()->GetAc());
1161 txop->GenerateBackoff(m_linkId);
1162 }
1163 }
1164 }
1165}
1166
1167void
1169{
1170 NS_LOG_FUNCTION(this << phyListener << duration);
1171
1172 Time now = Simulator::Now();
1173 NS_ASSERT(m_lastTxEnd <= now);
1174
1175 if (phyListener) // to make tests happy
1176 {
1177 // check if the PHY switched channel to operate on another EMLSR link
1178
1179 for (const auto& [phyRef, listener] : m_phyListeners)
1180 {
1181 Ptr<WifiPhy> phy = phyRef;
1182 auto emlsrInfoIt = m_switchingEmlsrLinks.find(phy);
1183
1184 if (listener.get() == phyListener && emlsrInfoIt != m_switchingEmlsrLinks.cend() &&
1185 phy->GetOperatingChannel() == emlsrInfoIt->second.channel)
1186 {
1187 // the PHY associated with the given PHY listener switched channel to
1188 // operate on another EMLSR link as expected. We don't need this listener
1189 // anymore. The MAC will connect a new listener to the ChannelAccessManager
1190 // instance associated with the link the PHY is now operating on
1191 RemovePhyListener(phy);
1193 NS_ASSERT(ehtFem);
1194 ehtFem->NotifySwitchingEmlsrLink(phy, emlsrInfoIt->second.linkId, duration);
1195 m_switchingEmlsrLinks.erase(emlsrInfoIt);
1196 return;
1197 }
1198 }
1199 }
1200
1201 ResetState();
1202
1203 // Cancel timeout
1204 if (m_accessTimeout.IsPending())
1205 {
1206 m_accessTimeout.Cancel();
1207 }
1208
1209 // Reset backoffs
1210 for (const auto& txop : m_txops)
1211 {
1212 ResetBackoff(txop);
1213 }
1214
1215 // Notify the FEM, which will in turn notify the MAC
1216 m_feManager->NotifySwitchingStartNow(duration);
1217
1218 NS_LOG_DEBUG("switching start for " << duration);
1219 m_lastSwitchingEnd = now + duration;
1220}
1221
1222void
1224{
1225 NS_LOG_FUNCTION(this);
1226
1227 Time now = Simulator::Now();
1228 m_lastRxReceivedOk = true;
1230 m_lastRx.end = std::min(m_lastRx.end, now);
1231 m_lastNavEnd = std::min(m_lastNavEnd, now);
1234 m_lastNoPhy.end = std::min(m_lastNoPhy.end, now);
1235 m_lastSleep.end = std::min(m_lastSleep.end, now);
1236 m_lastOff.end = std::min(m_lastOff.end, now);
1237
1239}
1240
1241void
1243{
1244 NS_LOG_FUNCTION(this << txop);
1245
1246 uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
1247 if (remainingSlots > 0)
1248 {
1249 txop->UpdateBackoffSlotsNow(remainingSlots, Simulator::Now(), m_linkId);
1250 NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
1251 }
1252 txop->ResetCw(m_linkId);
1253 txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
1254}
1255
1256void
1258{
1259 NS_LOG_FUNCTION(this);
1260
1261 for (const auto& txop : m_txops)
1262 {
1263 ResetBackoff(txop);
1264 }
1265 m_accessTimeout.Cancel();
1266}
1267
1268void
1270{
1271 NS_LOG_FUNCTION(this);
1272 UpdateBackoff();
1274 m_lastSleep.start = Simulator::Now();
1275 m_feManager->NotifySleepNow();
1276 for (auto txop : m_txops)
1277 {
1278 txop->NotifySleep(m_linkId);
1279 }
1280}
1281
1282void
1284{
1285 NS_LOG_FUNCTION(this);
1286 UpdateBackoff();
1288 m_lastOff.start = Simulator::Now();
1289 m_feManager->NotifyOffNow();
1290 for (auto txop : m_txops)
1291 {
1292 txop->NotifyOff(m_linkId);
1293 }
1294}
1295
1296void
1298{
1299 NS_LOG_FUNCTION(this);
1300 const auto now = Simulator::Now();
1301 m_lastSleep.end = now;
1302 if (now - m_lastSleep.start > m_resetBackoffThreshold)
1303 {
1305 }
1306 for (auto txop : m_txops)
1307 {
1308 txop->NotifyWakeUp(m_linkId);
1309 }
1310}
1311
1312void
1314{
1315 NS_LOG_FUNCTION(this);
1316 const auto now = Simulator::Now();
1317 m_lastOff.end = now;
1318 if (now - m_lastOff.start > m_resetBackoffThreshold)
1319 {
1321 }
1322 for (auto txop : m_txops)
1323 {
1324 txop->NotifyOn();
1325 }
1326}
1327
1328void
1330{
1331 NS_LOG_FUNCTION(this << duration);
1332
1333 if (!m_phy)
1334 {
1335 NS_LOG_DEBUG("Do not reset NAV, CTS may have been missed due to the main PHY switching "
1336 "to another link to take over a TXOP while receiving the CTS");
1337 return;
1338 }
1339
1340 NS_LOG_DEBUG("nav reset for=" << duration);
1341 UpdateBackoff();
1342 m_lastNavEnd = Simulator::Now() + duration;
1343 /**
1344 * If the NAV reset indicates an end-of-NAV which is earlier
1345 * than the previous end-of-NAV, the expected end of backoff
1346 * might be later than previously thought so, we might need
1347 * to restart a new access timeout.
1348 */
1350}
1351
1352void
1354{
1355 NS_LOG_FUNCTION(this << duration);
1356 NS_LOG_DEBUG("nav start for=" << duration);
1357 UpdateBackoff();
1358 m_lastNavEnd = std::max(m_lastNavEnd, Simulator::Now() + duration);
1359}
1360
1361void
1368
1369void
1376
1377void
1379{
1380 NS_LOG_FUNCTION(this << duration);
1381 m_lastCtsTimeoutEnd = Simulator::Now() + duration;
1382}
1383
1384void
1391
1392void
1394{
1395 NS_LOG_FUNCTION(this);
1396 Time idleStart = std::max({m_lastTxEnd,
1397 m_lastRx.end,
1399 m_lastNoPhy.end,
1400 m_lastSleep.end,
1401 m_lastOff.end});
1402 Time now = Simulator::Now();
1403
1404 if (idleStart >= now)
1405 {
1406 // No new idle period
1407 return;
1408 }
1409
1410 for (const auto& busyEnd : m_lastBusyEnd)
1411 {
1412 if (busyEnd.second < now)
1413 {
1414 auto lastIdleIt = m_lastIdle.find(busyEnd.first);
1415 NS_ASSERT(lastIdleIt != m_lastIdle.end());
1416 lastIdleIt->second = {std::max(idleStart, busyEnd.second), now};
1417 NS_LOG_DEBUG("New idle period (" << lastIdleIt->second.start.As(Time::S) << ", "
1418 << lastIdleIt->second.end.As(Time::S)
1419 << ") on channel " << lastIdleIt->first);
1420 }
1421 }
1422}
1423
1424std::ostream&
1425operator<<(std::ostream& os, const WifiExpectedAccessReason& reason)
1426{
1427 switch (reason)
1428 {
1430 return (os << "ACCESS EXPECTED");
1432 return (os << "NOT_REQUESTED");
1434 return (os << "NOTHING_TO_TX");
1436 return (os << "RX_END");
1438 return (os << "BUSY_END");
1440 return (os << "TX_END");
1442 return (os << "NAV_END");
1444 return (os << "ACK_TIMER_END");
1446 return (os << "CTS_TIMER_END");
1448 return (os << "SWITCHING_END");
1450 return (os << "NO_PHY_END");
1452 return (os << "SLEEP_END");
1454 return (os << "OFF_END");
1456 return (os << "BACKOFF_END");
1457 default:
1458 NS_ABORT_MSG("Unknown expected access reason");
1459 return (os << "Unknown");
1460 }
1461}
1462
1463} // 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
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 unsuccessfully.
Timespan m_lastSleep
the last sleep start and end time
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.
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
Timespan m_lastOff
the last off start and end time
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,...
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.
Definition ptr.h:67
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:96
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:409
@ US
microsecond
Definition nstime.h:109
@ S
second
Definition nstime.h:107
AttributeValue implementation for Time.
Definition nstime.h:1456
@ 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
receive notifications about PHY events.
Class that keeps track of all information about the current PHY operating 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:114
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:1457
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1477
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:1393
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
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:585
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:145
double dBm_u
dBm weak type
Definition wifi-units.h:27
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:592
Structure defining start time and end time for a given state.