A Discrete-Event Network Simulator
API
channel-access-manager.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2005,2006 INRIA
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
18  */
19 
20 #include "channel-access-manager.h"
21 
22 #include "frame-exchange-manager.h"
23 #include "txop.h"
24 #include "wifi-phy-listener.h"
25 #include "wifi-phy.h"
26 
27 #include "ns3/log.h"
28 #include "ns3/simulator.h"
29 
30 #undef NS_LOG_APPEND_CONTEXT
31 #define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "] "
32 
33 namespace ns3
34 {
35 
36 NS_LOG_COMPONENT_DEFINE("ChannelAccessManager");
37 
42 {
43  public:
50  : m_cam(cam)
51  {
52  }
53 
54  ~PhyListener() override
55  {
56  }
57 
58  void NotifyRxStart(Time duration) override
59  {
60  m_cam->NotifyRxStartNow(duration);
61  }
62 
63  void NotifyRxEndOk() override
64  {
66  }
67 
68  void NotifyRxEndError() override
69  {
71  }
72 
73  void NotifyTxStart(Time duration, double txPowerDbm) override
74  {
75  m_cam->NotifyTxStartNow(duration);
76  }
77 
78  void NotifyCcaBusyStart(Time duration,
79  WifiChannelListType channelType,
80  const std::vector<Time>& per20MhzDurations) override
81  {
82  m_cam->NotifyCcaBusyStartNow(duration, channelType, per20MhzDurations);
83  }
84 
85  void NotifySwitchingStart(Time duration) override
86  {
87  m_cam->NotifySwitchingStartNow(duration);
88  }
89 
90  void NotifySleep() override
91  {
93  }
94 
95  void NotifyOff() override
96  {
98  }
99 
100  void NotifyWakeup() override
101  {
103  }
104 
105  void NotifyOn() override
106  {
107  m_cam->NotifyOnNow();
108  }
109 
110  private:
112 };
113 
114 /****************************************************************
115  * Implement the channel access manager of all Txop holders
116  ****************************************************************/
117 
119  : m_lastAckTimeoutEnd(MicroSeconds(0)),
120  m_lastCtsTimeoutEnd(MicroSeconds(0)),
121  m_lastNavEnd(MicroSeconds(0)),
122  m_lastRx({MicroSeconds(0), MicroSeconds(0)}),
123  m_lastRxReceivedOk(true),
124  m_lastTxEnd(MicroSeconds(0)),
125  m_lastSwitchingEnd(MicroSeconds(0)),
126  m_sleeping(false),
127  m_off(false),
128  m_phyListener(nullptr),
129  m_linkId(0)
130 {
131  NS_LOG_FUNCTION(this);
132  InitLastBusyStructs();
133 }
134 
136 {
137  NS_LOG_FUNCTION(this);
138  delete m_phyListener;
139  m_phyListener = nullptr;
140 }
141 
142 void
144 {
145  NS_LOG_FUNCTION(this);
146  for (Ptr<Txop> i : m_txops)
147  {
148  i->Dispose();
149  i = nullptr;
150  }
151  m_phy = nullptr;
152  m_feManager = nullptr;
153 }
154 
155 void
157 {
158  NS_LOG_FUNCTION(this << phy);
159  NS_ASSERT(m_phyListener == nullptr);
160  m_phyListener = new PhyListener(this);
161  phy->RegisterListener(m_phyListener);
162  m_phy = phy;
164 }
165 
166 void
168 {
169  NS_LOG_FUNCTION(this << phy);
170  if (m_phyListener != nullptr)
171  {
172  phy->UnregisterListener(m_phyListener);
173  delete m_phyListener;
174  m_phyListener = nullptr;
175  m_phy = nullptr;
176  }
177 }
178 
179 void
181 {
182  NS_LOG_FUNCTION(this << +linkId);
183  m_linkId = linkId;
184 }
185 
186 void
188 {
189  NS_LOG_FUNCTION(this << feManager);
190  m_feManager = feManager;
191  m_feManager->SetChannelAccessManager(this);
192 }
193 
194 Time
196 {
197  return m_phy->GetSlot();
198 }
199 
200 Time
202 {
203  return m_phy->GetSifs();
204 }
205 
206 Time
208 {
209  return m_phy->GetSifs() + m_phy->GetAckTxTime();
210 }
211 
212 void
214 {
215  NS_LOG_FUNCTION(this << txop);
216  m_txops.push_back(txop);
217 }
218 
219 void
221 {
222  NS_LOG_FUNCTION(this);
223  Time now = Simulator::Now();
224  m_lastBusyEnd.clear();
225  m_lastPer20MHzBusyEnd.clear();
226  m_lastIdle.clear();
228  m_lastIdle[WIFI_CHANLIST_PRIMARY] = {now, now};
229 
230  if (!m_phy || !m_phy->GetOperatingChannel().IsOfdm())
231  {
232  return;
233  }
234 
235  uint16_t width = m_phy->GetChannelWidth();
236 
237  if (width >= 40)
238  {
240  m_lastIdle[WIFI_CHANLIST_SECONDARY] = {now, now};
241  }
242  if (width >= 80)
243  {
246  }
247  if (width >= 160)
248  {
251  }
252  // TODO Add conditions for new channel widths as they get supported
253 
254  if (m_phy->GetStandard() >= WIFI_STANDARD_80211ax && width > 20)
255  {
256  m_lastPer20MHzBusyEnd.assign(width / 20, now);
257  }
258 }
259 
260 bool
262 {
263  NS_LOG_FUNCTION(this);
264  Time now = Simulator::Now();
265  return (m_lastRx.end > now) // RX
266  || (m_lastTxEnd > now) // TX
267  || (m_lastNavEnd > now) // NAV busy
268  // an EDCA TXOP is obtained based solely on activity of the primary channel
269  // (Sec. 10.23.2.5 of IEEE 802.11-2020)
270  || (m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now); // CCA busy
271 }
272 
273 bool
275 {
276  NS_LOG_FUNCTION(this << txop);
277 
278  // No backoff needed if in sleep mode or off
279  if (m_sleeping || m_off)
280  {
281  return false;
282  }
283 
284  // the Txop might have a stale value of remaining backoff slots
285  UpdateBackoff();
286 
287  /*
288  * From section 10.3.4.2 "Basic access" of IEEE 802.11-2016:
289  *
290  * A STA may transmit an MPDU when it is operating under the DCF access
291  * method, either in the absence of a PC, or in the CP of the PCF access
292  * method, when the STA determines that the medium is idle when a frame is
293  * queued for transmission, and remains idle for a period of a DIFS, or an
294  * EIFS (10.3.2.3.7) from the end of the immediately preceding medium-busy
295  * event, whichever is the greater, and the backoff timer is zero. Otherwise
296  * the random backoff procedure described in 10.3.4.3 shall be followed.
297  *
298  * From section 10.22.2.2 "EDCA backoff procedure" of IEEE 802.11-2016:
299  *
300  * The backoff procedure shall be invoked by an EDCAF when any of the following
301  * events occurs:
302  * a) An MA-UNITDATA.request primitive is received that causes a frame with that AC
303  * to be queued for transmission such that one of the transmit queues associated
304  * with that AC has now become non-empty and any other transmit queues
305  * associated with that AC are empty; the medium is busy on the primary channel
306  */
308  txop->GetBackoffSlots(m_linkId) == 0)
309  {
310  if (!IsBusy())
311  {
312  // medium idle. If this is a DCF, use immediate access (we can transmit
313  // in a DIFS if the medium remains idle). If this is an EDCAF, update
314  // the backoff start time kept by the EDCAF to the current time in order
315  // to correctly align the backoff start time at the next slot boundary
316  // (performed by the next call to ChannelAccessManager::RequestAccess())
317  Time delay =
318  (txop->IsQosTxop() ? Seconds(0) : GetSifs() + txop->GetAifsn(m_linkId) * GetSlot());
319  txop->UpdateBackoffSlotsNow(0, Simulator::Now() + delay, m_linkId);
320  }
321  else
322  {
323  // medium busy, backoff is needed
324  return true;
325  }
326  }
327  return false;
328 }
329 
330 void
332 {
333  NS_LOG_FUNCTION(this << txop);
334  if (m_phy)
335  {
337  }
338  // Deny access if in sleep mode or off
339  if (m_sleeping || m_off)
340  {
341  return;
342  }
343  /*
344  * EDCAF operations shall be performed at slot boundaries (Sec. 10.22.2.4 of 802.11-2016)
345  */
346  Time accessGrantStart = GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot());
347 
348  if (txop->IsQosTxop() && txop->GetBackoffStart(m_linkId) > accessGrantStart)
349  {
350  // The backoff start time reported by the EDCAF is more recent than the last
351  // time the medium was busy plus an AIFS, hence we need to align it to the
352  // next slot boundary.
353  Time diff = txop->GetBackoffStart(m_linkId) - accessGrantStart;
354  uint32_t nIntSlots = (diff / GetSlot()).GetHigh() + 1;
355  txop->UpdateBackoffSlotsNow(0, accessGrantStart + (nIntSlots * GetSlot()), m_linkId);
356  }
357 
358  UpdateBackoff();
363 }
364 
365 void
367 {
368  NS_LOG_FUNCTION(this);
369  uint32_t k = 0;
370  Time now = Simulator::Now();
371  for (Txops::iterator i = m_txops.begin(); i != m_txops.end(); k++)
372  {
373  Ptr<Txop> txop = *i;
374  if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
375  (!txop->IsQosTxop() || !StaticCast<QosTxop>(txop)->EdcaDisabled(m_linkId)) &&
376  GetBackoffEndFor(txop) <= now)
377  {
382  NS_LOG_DEBUG("dcf " << k << " needs access. backoff expired. access granted. slots="
383  << txop->GetBackoffSlots(m_linkId));
384  i++; // go to the next item in the list.
385  k++;
386  std::vector<Ptr<Txop>> internalCollisionTxops;
387  for (Txops::iterator j = i; j != m_txops.end(); j++, k++)
388  {
389  Ptr<Txop> otherTxop = *j;
390  if (otherTxop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
391  GetBackoffEndFor(otherTxop) <= now)
392  {
393  NS_LOG_DEBUG(
394  "dcf " << k << " needs access. backoff expired. internal collision. slots="
395  << otherTxop->GetBackoffSlots(m_linkId));
401  internalCollisionTxops.push_back(otherTxop);
402  }
403  }
404 
414  // If we are operating on an OFDM channel wider than 20 MHz, find the largest
415  // idle primary channel and pass its width to the FrameExchangeManager, so that
416  // the latter can transmit PPDUs of the appropriate width (see Section 10.23.2.5
417  // of IEEE 802.11-2020).
418  auto interval = (m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
419  ? GetSifs() + 2 * GetSlot()
420  : m_phy->GetPifs();
421  auto width = (m_phy->GetOperatingChannel().IsOfdm() && m_phy->GetChannelWidth() > 20)
422  ? GetLargestIdlePrimaryChannel(interval, now)
423  : m_phy->GetChannelWidth();
424  if (m_feManager->StartTransmission(txop, width))
425  {
426  for (auto& collidingTxop : internalCollisionTxops)
427  {
428  m_feManager->NotifyInternalCollision(collidingTxop);
429  }
430  break;
431  }
432  else
433  {
434  // reset the current state to the EDCAF that won the contention
435  // but did not transmit anything
436  i--;
437  k = std::distance(m_txops.begin(), i);
438  }
439  }
440  i++;
441  }
442 }
443 
444 void
446 {
447  NS_LOG_FUNCTION(this);
448  UpdateBackoff();
451 }
452 
453 Time
455 {
456  NS_LOG_FUNCTION(this);
457  const Time& sifs = GetSifs();
458  Time rxAccessStart = m_lastRx.end + sifs;
460  {
461  rxAccessStart += GetEifsNoDifs();
462  }
463  // an EDCA TXOP is obtained based solely on activity of the primary channel
464  // (Sec. 10.23.2.5 of IEEE 802.11-2020)
465  Time busyAccessStart = m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) + sifs;
466  Time txAccessStart = m_lastTxEnd + sifs;
467  Time navAccessStart = m_lastNavEnd + sifs;
468  Time ackTimeoutAccessStart = m_lastAckTimeoutEnd + sifs;
469  Time ctsTimeoutAccessStart = m_lastCtsTimeoutEnd + sifs;
470  Time switchingAccessStart = m_lastSwitchingEnd + sifs;
471  Time accessGrantedStart;
472  if (ignoreNav)
473  {
474  accessGrantedStart = std::max({rxAccessStart,
475  busyAccessStart,
476  txAccessStart,
477  ackTimeoutAccessStart,
478  ctsTimeoutAccessStart,
479  switchingAccessStart});
480  }
481  else
482  {
483  accessGrantedStart = std::max({rxAccessStart,
484  busyAccessStart,
485  txAccessStart,
486  navAccessStart,
487  ackTimeoutAccessStart,
488  ctsTimeoutAccessStart,
489  switchingAccessStart});
490  }
491  NS_LOG_INFO("access grant start=" << accessGrantedStart << ", rx access start=" << rxAccessStart
492  << ", busy access start=" << busyAccessStart
493  << ", tx access start=" << txAccessStart
494  << ", nav access start=" << navAccessStart);
495  return accessGrantedStart;
496 }
497 
498 Time
500 {
501  NS_LOG_FUNCTION(this << txop);
502  Time mostRecentEvent =
504  GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot())});
505  NS_LOG_DEBUG("Backoff start: " << mostRecentEvent.As(Time::US));
506 
507  return mostRecentEvent;
508 }
509 
510 Time
512 {
513  NS_LOG_FUNCTION(this << txop);
514  Time backoffEnd = GetBackoffStartFor(txop) + (txop->GetBackoffSlots(m_linkId) * GetSlot());
515  NS_LOG_DEBUG("Backoff end: " << backoffEnd.As(Time::US));
516 
517  return backoffEnd;
518 }
519 
520 void
522 {
523  NS_LOG_FUNCTION(this);
524  uint32_t k = 0;
525  for (auto txop : m_txops)
526  {
527  Time backoffStart = GetBackoffStartFor(txop);
528  if (backoffStart <= Simulator::Now())
529  {
530  uint32_t nIntSlots = ((Simulator::Now() - backoffStart) / GetSlot()).GetHigh();
531  /*
532  * EDCA behaves slightly different to DCA. For EDCA we
533  * decrement once at the slot boundary at the end of AIFS as
534  * well as once at the end of each clear slot
535  * thereafter. For DCA we only decrement at the end of each
536  * clear slot after DIFS. We account for the extra backoff
537  * by incrementing the slot count here in the case of
538  * EDCA. The if statement whose body we are in has confirmed
539  * that a minimum of AIFS has elapsed since last busy
540  * medium.
541  */
542  if (txop->IsQosTxop())
543  {
544  nIntSlots++;
545  }
546  uint32_t n = std::min(nIntSlots, txop->GetBackoffSlots(m_linkId));
547  NS_LOG_DEBUG("dcf " << k << " dec backoff slots=" << n);
548  Time backoffUpdateBound = backoffStart + (n * GetSlot());
549  txop->UpdateBackoffSlotsNow(n, backoffUpdateBound, m_linkId);
550  }
551  ++k;
552  }
553 }
554 
555 void
557 {
558  NS_LOG_FUNCTION(this);
563  bool accessTimeoutNeeded = false;
564  Time expectedBackoffEnd = Simulator::GetMaximumSimulationTime();
565  for (auto txop : m_txops)
566  {
567  if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED)
568  {
569  Time tmp = GetBackoffEndFor(txop);
570  if (tmp > Simulator::Now())
571  {
572  accessTimeoutNeeded = true;
573  expectedBackoffEnd = std::min(expectedBackoffEnd, tmp);
574  }
575  }
576  }
577  NS_LOG_DEBUG("Access timeout needed: " << accessTimeoutNeeded);
578  if (accessTimeoutNeeded)
579  {
580  NS_LOG_DEBUG("expected backoff end=" << expectedBackoffEnd);
581  Time expectedBackoffDelay = expectedBackoffEnd - Simulator::Now();
582  if (m_accessTimeout.IsRunning() &&
583  Simulator::GetDelayLeft(m_accessTimeout) > expectedBackoffDelay)
584  {
586  }
588  {
589  m_accessTimeout = Simulator::Schedule(expectedBackoffDelay,
591  this);
592  }
593  }
594 }
595 
596 uint16_t
598 {
599  NS_LOG_FUNCTION(this << interval.As(Time::US) << end.As(Time::S));
600 
601  // If the medium is busy or it just became idle, UpdateLastIdlePeriod does
602  // nothing. This allows us to call this method, e.g., at the end of a frame
603  // reception and check the busy/idle status of the channel before the start
604  // of the frame reception (last idle period was last updated at the start of
605  // the frame reception).
606  // If the medium has been idle for some time, UpdateLastIdlePeriod updates
607  // the last idle period. This is normally what we want because this method may
608  // also be called before starting a TXOP gained through EDCA.
610 
611  uint16_t width = 0;
612 
613  // we iterate over the different types of channels in the same order as they
614  // are listed in WifiChannelListType
615  for (const auto& lastIdle : m_lastIdle)
616  {
617  if (lastIdle.second.start <= end - interval && lastIdle.second.end >= end)
618  {
619  // channel idle, update width
620  width = (width == 0) ? 20 : (2 * width);
621  }
622  else
623  {
624  break;
625  }
626  }
627  return width;
628 }
629 
630 bool
631 ChannelAccessManager::GetPer20MHzBusy(const std::set<uint8_t>& indices) const
632 {
633  const auto now = Simulator::Now();
634 
635  if (m_phy->GetChannelWidth() < 40)
636  {
637  NS_ASSERT_MSG(indices.size() == 1 && *indices.cbegin() == 0,
638  "Index 0 only can be specified if the channel width is less than 40 MHz");
639  return m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now;
640  }
641 
642  for (const auto index : indices)
643  {
644  NS_ASSERT(index < m_lastPer20MHzBusyEnd.size());
645  if (m_lastPer20MHzBusyEnd.at(index) > now)
646  {
647  NS_LOG_DEBUG("20 MHz channel with index " << +index << " is busy");
648  return true;
649  }
650  }
651  return false;
652 }
653 
654 void
656 {
657  NS_LOG_FUNCTION(this << qosTxop << duration);
658  NS_ASSERT(qosTxop->IsQosTxop());
659  UpdateBackoff();
660  Time resume = Simulator::Now() + duration;
661  NS_LOG_DEBUG("Backoff will resume at time " << resume << " with "
662  << qosTxop->GetBackoffSlots(m_linkId)
663  << " remaining slot(s)");
664  qosTxop->UpdateBackoffSlotsNow(0, resume, m_linkId);
666 }
667 
668 void
670 {
671  NS_LOG_FUNCTION(this << duration);
672  NS_LOG_DEBUG("rx start for=" << duration);
673  UpdateBackoff();
676  m_lastRx.end = m_lastRx.start + duration;
677  m_lastRxReceivedOk = true;
678 }
679 
680 void
682 {
683  NS_LOG_FUNCTION(this);
684  NS_LOG_DEBUG("rx end ok");
686  m_lastRxReceivedOk = true;
687 }
688 
689 void
691 {
692  NS_LOG_FUNCTION(this);
693  NS_LOG_DEBUG("rx end error");
694  // we expect the PHY to notify us of the start of a CCA busy period, if needed
696  m_lastRxReceivedOk = false;
697 }
698 
699 void
701 {
702  NS_LOG_FUNCTION(this << duration);
703  m_lastRxReceivedOk = true;
704  Time now = Simulator::Now();
705  if (m_lastRx.end > now)
706  {
707  // this may be caused only if PHY has started to receive a packet
708  // inside SIFS, so, we check that lastRxStart was maximum a SIFS ago
709  NS_ASSERT(now - m_lastRx.start <= GetSifs());
710  m_lastRx.end = now;
711  }
712  else
713  {
715  }
716  NS_LOG_DEBUG("tx start for " << duration);
717  UpdateBackoff();
718  m_lastTxEnd = now + duration;
719 }
720 
721 void
723  WifiChannelListType channelType,
724  const std::vector<Time>& per20MhzDurations)
725 {
726  NS_LOG_FUNCTION(this << duration << channelType);
727  UpdateBackoff();
729  auto lastBusyEndIt = m_lastBusyEnd.find(channelType);
730  NS_ASSERT(lastBusyEndIt != m_lastBusyEnd.end());
731  Time now = Simulator::Now();
732  lastBusyEndIt->second = now + duration;
733  NS_ASSERT_MSG(per20MhzDurations.size() == m_lastPer20MHzBusyEnd.size(),
734  "Size of received vector (" << per20MhzDurations.size()
735  << ") differs from the expected size ("
736  << m_lastPer20MHzBusyEnd.size() << ")");
737  for (std::size_t chIdx = 0; chIdx < per20MhzDurations.size(); ++chIdx)
738  {
739  if (per20MhzDurations[chIdx].IsStrictlyPositive())
740  {
741  m_lastPer20MHzBusyEnd[chIdx] = now + per20MhzDurations[chIdx];
742  }
743  }
744 }
745 
746 void
748 {
749  NS_LOG_FUNCTION(this << duration);
750  Time now = Simulator::Now();
751  NS_ASSERT(m_lastTxEnd <= now);
753 
754  m_lastRxReceivedOk = true;
756  m_lastRx.end = std::min(m_lastRx.end, now);
760 
761  // the new operating channel may have a different width than the previous one
763 
764  // Cancel timeout
766  {
768  }
769 
770  // Notify the FEM, which will in turn notify the MAC
771  m_feManager->NotifySwitchingStartNow(duration);
772 
773  // Reset backoffs
774  for (auto txop : m_txops)
775  {
776  uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
777  if (remainingSlots > 0)
778  {
779  txop->UpdateBackoffSlotsNow(remainingSlots, now, m_linkId);
780  NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
781  }
782  txop->ResetCw(m_linkId);
783  txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
784  }
785 
786  NS_LOG_DEBUG("switching start for " << duration);
787  m_lastSwitchingEnd = now + duration;
788 }
789 
790 void
792 {
793  NS_LOG_FUNCTION(this);
794  m_sleeping = true;
795  // Cancel timeout
797  {
799  }
800 
801  // Reset backoffs
802  for (auto txop : m_txops)
803  {
804  txop->NotifySleep(m_linkId);
805  }
806 }
807 
808 void
810 {
811  NS_LOG_FUNCTION(this);
812  m_off = true;
813  // Cancel timeout
815  {
817  }
818 
819  // Reset backoffs
820  for (auto txop : m_txops)
821  {
822  txop->NotifyOff();
823  }
824 }
825 
826 void
828 {
829  NS_LOG_FUNCTION(this);
830  m_sleeping = false;
831  for (auto txop : m_txops)
832  {
833  uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
834  if (remainingSlots > 0)
835  {
836  txop->UpdateBackoffSlotsNow(remainingSlots, Simulator::Now(), m_linkId);
837  NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
838  }
839  txop->ResetCw(m_linkId);
840  txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
841  txop->NotifyWakeUp(m_linkId);
842  }
843 }
844 
845 void
847 {
848  NS_LOG_FUNCTION(this);
849  m_off = false;
850  for (auto txop : m_txops)
851  {
852  uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
853  if (remainingSlots > 0)
854  {
855  txop->UpdateBackoffSlotsNow(remainingSlots, Simulator::Now(), m_linkId);
856  NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
857  }
858  txop->ResetCw(m_linkId);
859  txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
860  txop->NotifyOn();
861  }
862 }
863 
864 void
866 {
867  NS_LOG_FUNCTION(this << duration);
868  NS_LOG_DEBUG("nav reset for=" << duration);
869  UpdateBackoff();
870  m_lastNavEnd = Simulator::Now() + duration;
878 }
879 
880 void
882 {
883  NS_LOG_FUNCTION(this << duration);
884  NS_LOG_DEBUG("nav start for=" << duration);
885  UpdateBackoff();
887 }
888 
889 void
891 {
892  NS_LOG_FUNCTION(this << duration);
894  m_lastAckTimeoutEnd = Simulator::Now() + duration;
895 }
896 
897 void
899 {
900  NS_LOG_FUNCTION(this);
903 }
904 
905 void
907 {
908  NS_LOG_FUNCTION(this << duration);
909  m_lastCtsTimeoutEnd = Simulator::Now() + duration;
910 }
911 
912 void
914 {
915  NS_LOG_FUNCTION(this);
918 }
919 
920 void
922 {
923  NS_LOG_FUNCTION(this);
925  Time now = Simulator::Now();
926 
927  if (idleStart >= now)
928  {
929  // No new idle period
930  return;
931  }
932 
933  for (const auto& busyEnd : m_lastBusyEnd)
934  {
935  if (busyEnd.second < now)
936  {
937  auto lastIdleIt = m_lastIdle.find(busyEnd.first);
938  NS_ASSERT(lastIdleIt != m_lastIdle.end());
939  lastIdleIt->second = {std::max(idleStart, busyEnd.second), now};
940  NS_LOG_DEBUG("New idle period (" << lastIdleIt->second.start.As(Time::S) << ", "
941  << lastIdleIt->second.end.As(Time::S)
942  << ") on channel " << lastIdleIt->first);
943  }
944  }
945 }
946 
947 } // namespace ns3
#define min(a, b)
Definition: 80211b.c:42
#define max(a, b)
Definition: 80211b.c:43
Manage a set of ns3::Txop.
uint16_t GetLargestIdlePrimaryChannel(Time interval, Time end)
Return the width of the largest primary channel that has been idle for the given time interval before...
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 NotifyRxEndErrorNow()
Notify the Txop that a packet reception was just completed unsuccessfuly.
bool m_off
flag whether it is in off state
void NotifyRxStartNow(Time duration)
Time GetBackoffEndFor(Ptr< Txop > txop)
Return the time when the backoff procedure ended (or will ended) for the given Txop.
void NotifyWakeupNow()
Notify the Txop that the device has been resumed from sleep mode.
bool m_lastRxReceivedOk
the last receive OK
std::map< WifiChannelListType, Timespan > m_lastIdle
the last idle start and end time for each channel type
Ptr< WifiPhy > m_phy
pointer to the PHY
void NotifyAckTimeoutResetNow()
Notify that ack timer has reset.
void NotifyTxStartNow(Time duration)
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
void NotifyCcaBusyStartNow(Time duration, WifiChannelListType channelType, const std::vector< Time > &per20MhzDurations)
Time m_lastAckTimeoutEnd
the last Ack timeout end time
virtual Time GetSlot() const
Return the slot duration for this PHY.
void NotifyAckTimeoutStartNow(Time duration)
Notify that ack timer has started for the given duration.
void AccessTimeout()
Called when access timeout should occur (e.g.
void UpdateBackoff()
Update backoff slots for all Txops.
bool m_sleeping
flag whether it is in sleeping state
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.
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.
Time m_lastTxEnd
the last transmit end time
void SetupPhyListener(Ptr< WifiPhy > phy)
Set up listener for PHY events.
Time m_lastCtsTimeoutEnd
the last CTS timeout end time
void DoDispose() override
Destructor implementation.
void NotifySleepNow()
Notify the Txop that the device has been put in sleep mode.
Ptr< FrameExchangeManager > m_feManager
pointer to the Frame Exchange Manager
PhyListener * m_phyListener
the PHY listener
void UpdateLastIdlePeriod()
This method determines whether the medium has been idle during a period (of non-null duration) immedi...
void NotifySwitchingStartNow(Time duration)
void DisableEdcaFor(Ptr< Txop > qosTxop, Time duration)
Txops m_txops
the vector of managed Txops
bool GetPer20MHzBusy(const std::set< uint8_t > &indices) const
void DoGrantDcfAccess()
Grant access to Txop using DCF/EDCF contention rules.
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.
void NotifyNavResetNow(Time duration)
Time GetAccessGrantStart(bool ignoreNav=false) const
Access will never be granted to the medium before the time returned by this method.
bool NeedBackoffUponAccess(Ptr< Txop > txop)
Determine if a new backoff needs to be generated when a packet is queued for transmission.
void NotifyOnNow()
Notify the Txop that the device has been resumed from off mode.
Time GetBackoffStartFor(Ptr< Txop > txop)
Return the time when the backoff procedure started for the given Txop.
virtual Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
void NotifyNavStartNow(Time duration)
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:55
bool IsExpired() const
This method is syntactic sugar for the ns3::Simulator::IsExpired method.
Definition: event-id.cc:69
bool IsRunning() const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:76
Listener for PHY events.
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 NotifyTxStart(Time duration, double txPowerDbm) override
void NotifyRxStart(Time duration) override
void NotifyRxEndError() override
We have received the last bit of a packet for which NotifyRxStart was invoked first and,...
void NotifyOn() override
Notify listeners that we went to switch on.
void NotifySwitchingStart(Time duration) override
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
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:568
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:199
static Time GetMaximumSimulationTime()
Get the maximum representable simulation time.
Definition: simulator.cc:302
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition: simulator.cc:208
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition: time.cc:417
@ US
microsecond
Definition: nstime.h:118
@ S
second
Definition: nstime.h:116
virtual ChannelAccessStatus GetAccessStatus(uint8_t linkId) const
Definition: txop.cc:565
virtual bool HasFramesToTransmit(uint8_t linkId)
Check if the Txop has frames to transmit over the given link.
Definition: txop.cc:496
@ GRANTED
Definition: txop.h:102
@ NOT_REQUESTED
Definition: txop.h:100
@ REQUESTED
Definition: txop.h:101
virtual bool IsQosTxop() const
Check for QoS TXOP.
Definition: txop.cc:646
void UpdateBackoffSlotsNow(uint32_t nSlots, Time backoffUpdateBound, uint8_t linkId)
Update backoff slots for the given link that nSlots has passed.
Definition: txop.cc:324
Time GetBackoffStart(uint8_t linkId) const
Return the time when the backoff procedure started on the given link.
Definition: txop.cc:318
virtual void NotifyAccessRequested(uint8_t linkId)
Notify that access request has been received for the given link.
Definition: txop.cc:571
uint8_t GetAifsn() const
Return the number of slots that make up an AIFS.
Definition: txop.cc:450
uint32_t GetBackoffSlots(uint8_t linkId) const
Return the current number of backoff slots on the given link.
Definition: txop.cc:312
Time GetSlot() const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:786
uint16_t GetChannelWidth() const
Definition: wifi-phy.cc:1026
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:774
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:996
Time GetPifs() const
Return the PCF Interframe Space (PIFS) for this PHY.
Definition: wifi-phy.cc:798
WifiStandard GetStandard() const
Get the configured Wi-Fi standard.
Definition: wifi-phy.cc:1002
void NotifyChannelAccessRequested()
Notify the PHY that an access to the channel was requested.
Definition: wifi-phy.cc:1860
Time GetAckTxTime() const
Return the estimated Ack TX time for this PHY.
Definition: wifi-phy.cc:804
const WifiPhyOperatingChannel & GetOperatingChannel() const
Get a const reference to the operating channel.
Definition: wifi-phy.cc:1008
receive notifications about PHY events.
bool IsOfdm() const
Return whether the operating channel is an OFDM channel.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_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:275
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1360
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
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.
Definition: wifi-phy-band.h:35
@ WIFI_CHANLIST_PRIMARY
@ WIFI_CHANLIST_SECONDARY40
@ WIFI_CHANLIST_SECONDARY
@ WIFI_CHANLIST_SECONDARY80
Every class exported by the ns3 library is enclosed in the ns3 namespace.
phy
Definition: third.py:82