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 * 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
21
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
33namespace ns3
34{
35
36NS_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 {
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 {
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
142void
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
155void
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
166void
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
179void
181{
182 NS_LOG_FUNCTION(this << +linkId);
183 m_linkId = linkId;
184}
185
186void
188{
189 NS_LOG_FUNCTION(this << feManager);
190 m_feManager = feManager;
191 m_feManager->SetChannelAccessManager(this);
192}
193
194Time
196{
197 return m_phy->GetSlot();
198}
199
200Time
202{
203 return m_phy->GetSifs();
204}
205
206Time
208{
209 return m_phy->GetSifs() + m_phy->GetAckTxTime();
210}
211
212void
214{
215 NS_LOG_FUNCTION(this << txop);
216 m_txops.push_back(txop);
217}
218
219void
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
231 {
232 return;
233 }
234
235 uint16_t width = m_phy->GetChannelWidth();
236
237 if (width >= 40)
238 {
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
260bool
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
273bool
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
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 */
307 if (!txop->HasFramesToTransmit(m_linkId) && txop->GetAccessStatus(m_linkId) != Txop::GRANTED &&
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
330void
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
359 NS_ASSERT(txop->GetAccessStatus(m_linkId) != Txop::REQUESTED);
360 txop->NotifyAccessRequested(m_linkId);
363}
364
365void
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 {
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)
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
444void
446{
447 NS_LOG_FUNCTION(this);
451}
452
453Time
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
498Time
500{
501 NS_LOG_FUNCTION(this << txop);
502 Time mostRecentEvent =
503 std::max({txop->GetBackoffStart(m_linkId),
504 GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot())});
505 NS_LOG_DEBUG("Backoff start: " << mostRecentEvent.As(Time::US));
506
507 return mostRecentEvent;
508}
509
510Time
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
520void
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
555void
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();
583 Simulator::GetDelayLeft(m_accessTimeout) > expectedBackoffDelay)
584 {
586 }
588 {
589 m_accessTimeout = Simulator::Schedule(expectedBackoffDelay,
591 this);
592 }
593 }
594}
595
596uint16_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
630bool
631ChannelAccessManager::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
654void
656{
657 NS_LOG_FUNCTION(this << qosTxop << duration);
658 NS_ASSERT(qosTxop->IsQosTxop());
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
668void
670{
671 NS_LOG_FUNCTION(this << duration);
672 NS_LOG_DEBUG("rx start for=" << duration);
676 m_lastRx.end = m_lastRx.start + duration;
677 m_lastRxReceivedOk = true;
678}
679
680void
682{
683 NS_LOG_FUNCTION(this);
684 NS_LOG_DEBUG("rx end ok");
686 m_lastRxReceivedOk = true;
687}
688
689void
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
699void
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);
718 m_lastTxEnd = now + duration;
719}
720
721void
723 WifiChannelListType channelType,
724 const std::vector<Time>& per20MhzDurations)
725{
726 NS_LOG_FUNCTION(this << duration << channelType);
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
746void
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);
757 m_lastNavEnd = std::min(m_lastNavEnd, 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
790void
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
808void
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
826void
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
845void
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
864void
866{
867 NS_LOG_FUNCTION(this << duration);
868 NS_LOG_DEBUG("nav reset for=" << duration);
870 m_lastNavEnd = Simulator::Now() + duration;
878}
879
880void
882{
883 NS_LOG_FUNCTION(this << duration);
884 NS_LOG_DEBUG("nav start for=" << duration);
886 m_lastNavEnd = std::max(m_lastNavEnd, Simulator::Now() + duration);
887}
888
889void
891{
892 NS_LOG_FUNCTION(this << duration);
894 m_lastAckTimeoutEnd = Simulator::Now() + duration;
895}
896
897void
899{
900 NS_LOG_FUNCTION(this);
903}
904
905void
907{
908 NS_LOG_FUNCTION(this << duration);
909 m_lastCtsTimeoutEnd = Simulator::Now() + duration;
910}
911
912void
914{
915 NS_LOG_FUNCTION(this);
918}
919
920void
922{
923 NS_LOG_FUNCTION(this);
924 Time idleStart = std::max({m_lastTxEnd, m_lastRx.end, m_lastSwitchingEnd});
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
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
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
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
@ GRANTED
Definition: txop.h:102
@ NOT_REQUESTED
Definition: txop.h:100
@ REQUESTED
Definition: txop.h:101
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.