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);
147}
148
149void
151{
152 NS_LOG_FUNCTION(this);
153 for (Ptr<Txop> i : m_txops)
154 {
155 i->Dispose();
156 i = nullptr;
157 }
158 m_phy = nullptr;
159 m_feManager = nullptr;
160}
161
162void
164{
165 NS_LOG_FUNCTION(this << phy);
166 NS_ASSERT(m_phyListener == nullptr);
167 m_phyListener = new PhyListener(this);
168 phy->RegisterListener(m_phyListener);
169 m_phy = phy;
171}
172
173void
175{
176 NS_LOG_FUNCTION(this << phy);
177 if (m_phyListener != nullptr)
178 {
179 phy->UnregisterListener(m_phyListener);
180 delete m_phyListener;
181 m_phyListener = nullptr;
182 m_phy = nullptr;
183 }
184}
185
186void
188{
189 NS_LOG_FUNCTION(this << +linkId);
190 m_linkId = linkId;
191}
192
193void
195{
196 NS_LOG_FUNCTION(this << feManager);
197 m_feManager = feManager;
198 m_feManager->SetChannelAccessManager(this);
199}
200
201Time
203{
204 return m_phy->GetSlot();
205}
206
207Time
209{
210 return m_phy->GetSifs();
211}
212
213Time
215{
216 return m_phy->GetSifs() + m_phy->GetAckTxTime();
217}
218
219void
221{
222 NS_LOG_FUNCTION(this << txop);
223 m_txops.push_back(txop);
224}
225
226void
228{
229 NS_LOG_FUNCTION(this);
230 Time now = Simulator::Now();
231 m_lastBusyEnd.clear();
232 m_lastPer20MHzBusyEnd.clear();
233 m_lastIdle.clear();
235 m_lastIdle[WIFI_CHANLIST_PRIMARY] = {now, now};
236
238 {
239 return;
240 }
241
242 uint16_t width = m_phy->GetChannelWidth();
243
244 if (width >= 40)
245 {
248 }
249 if (width >= 80)
250 {
253 }
254 if (width >= 160)
255 {
258 }
259 // TODO Add conditions for new channel widths as they get supported
260
261 if (m_phy->GetStandard() >= WIFI_STANDARD_80211ax && width > 20)
262 {
263 m_lastPer20MHzBusyEnd.assign(width / 20, now);
264 }
265}
266
267bool
269{
270 NS_LOG_FUNCTION(this);
271 Time now = Simulator::Now();
272 return (m_lastRx.end > now) // RX
273 || (m_lastTxEnd > now) // TX
274 || (m_lastNavEnd > now) // NAV busy
275 // an EDCA TXOP is obtained based solely on activity of the primary channel
276 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
277 || (m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now); // CCA busy
278}
279
280bool
282{
283 NS_LOG_FUNCTION(this << txop);
284
285 // No backoff needed if in sleep mode or off
286 if (m_sleeping || m_off)
287 {
288 return false;
289 }
290
291 // the Txop might have a stale value of remaining backoff slots
293
294 /*
295 * From section 10.3.4.2 "Basic access" of IEEE 802.11-2016:
296 *
297 * A STA may transmit an MPDU when it is operating under the DCF access
298 * method, either in the absence of a PC, or in the CP of the PCF access
299 * method, when the STA determines that the medium is idle when a frame is
300 * queued for transmission, and remains idle for a period of a DIFS, or an
301 * EIFS (10.3.2.3.7) from the end of the immediately preceding medium-busy
302 * event, whichever is the greater, and the backoff timer is zero. Otherwise
303 * the random backoff procedure described in 10.3.4.3 shall be followed.
304 *
305 * From section 10.22.2.2 "EDCA backoff procedure" of IEEE 802.11-2016:
306 *
307 * The backoff procedure shall be invoked by an EDCAF when any of the following
308 * events occurs:
309 * a) An MA-UNITDATA.request primitive is received that causes a frame with that AC
310 * to be queued for transmission such that one of the transmit queues associated
311 * with that AC has now become non-empty and any other transmit queues
312 * associated with that AC are empty; the medium is busy on the primary channel
313 */
314 if (!txop->HasFramesToTransmit(m_linkId) && txop->GetAccessStatus(m_linkId) != Txop::GRANTED &&
315 txop->GetBackoffSlots(m_linkId) == 0)
316 {
317 if (!IsBusy())
318 {
319 // medium idle. If this is a DCF, use immediate access (we can transmit
320 // in a DIFS if the medium remains idle). If this is an EDCAF, update
321 // the backoff start time kept by the EDCAF to the current time in order
322 // to correctly align the backoff start time at the next slot boundary
323 // (performed by the next call to ChannelAccessManager::RequestAccess())
324 Time delay =
325 (txop->IsQosTxop() ? Seconds(0) : GetSifs() + txop->GetAifsn(m_linkId) * GetSlot());
326 txop->UpdateBackoffSlotsNow(0, Simulator::Now() + delay, m_linkId);
327 }
328 else
329 {
330 // medium busy, backoff is needed
331 return true;
332 }
333 }
334 return false;
335}
336
337void
339{
340 NS_LOG_FUNCTION(this << txop);
341 if (m_phy)
342 {
344 }
345 // Deny access if in sleep mode or off
346 if (m_sleeping || m_off)
347 {
348 return;
349 }
350 /*
351 * EDCAF operations shall be performed at slot boundaries (Sec. 10.22.2.4 of 802.11-2016)
352 */
353 Time accessGrantStart = GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot());
354
355 if (txop->IsQosTxop() && txop->GetBackoffStart(m_linkId) > accessGrantStart)
356 {
357 // The backoff start time reported by the EDCAF is more recent than the last
358 // time the medium was busy plus an AIFS, hence we need to align it to the
359 // next slot boundary.
360 Time diff = txop->GetBackoffStart(m_linkId) - accessGrantStart;
361 uint32_t nIntSlots = (diff / GetSlot()).GetHigh() + 1;
362 txop->UpdateBackoffSlotsNow(0, accessGrantStart + (nIntSlots * GetSlot()), m_linkId);
363 }
364
366 NS_ASSERT(txop->GetAccessStatus(m_linkId) != Txop::REQUESTED);
367 txop->NotifyAccessRequested(m_linkId);
370}
371
372void
374{
375 NS_LOG_FUNCTION(this);
376 uint32_t k = 0;
377 Time now = Simulator::Now();
378 for (Txops::iterator i = m_txops.begin(); i != m_txops.end(); k++)
379 {
380 Ptr<Txop> txop = *i;
381 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
382 (!txop->IsQosTxop() || !StaticCast<QosTxop>(txop)->EdcaDisabled(m_linkId)) &&
383 GetBackoffEndFor(txop) <= now)
384 {
389 NS_LOG_DEBUG("dcf " << k << " needs access. backoff expired. access granted. slots="
390 << txop->GetBackoffSlots(m_linkId));
391 i++; // go to the next item in the list.
392 k++;
393 std::vector<Ptr<Txop>> internalCollisionTxops;
394 for (Txops::iterator j = i; j != m_txops.end(); j++, k++)
395 {
396 Ptr<Txop> otherTxop = *j;
397 if (otherTxop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
398 GetBackoffEndFor(otherTxop) <= now)
399 {
401 "dcf " << k << " needs access. backoff expired. internal collision. slots="
402 << otherTxop->GetBackoffSlots(m_linkId));
408 internalCollisionTxops.push_back(otherTxop);
409 }
410 }
411
421 // If we are operating on an OFDM channel wider than 20 MHz, find the largest
422 // idle primary channel and pass its width to the FrameExchangeManager, so that
423 // the latter can transmit PPDUs of the appropriate width (see Section 10.23.2.5
424 // of IEEE 802.11-2020).
425 auto interval = (m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
426 ? GetSifs() + 2 * GetSlot()
427 : m_phy->GetPifs();
428 auto width = (m_phy->GetOperatingChannel().IsOfdm() && m_phy->GetChannelWidth() > 20)
429 ? GetLargestIdlePrimaryChannel(interval, now)
431 if (m_feManager->StartTransmission(txop, width))
432 {
433 for (auto& collidingTxop : internalCollisionTxops)
434 {
435 m_feManager->NotifyInternalCollision(collidingTxop);
436 }
437 break;
438 }
439 else
440 {
441 // reset the current state to the EDCAF that won the contention
442 // but did not transmit anything
443 i--;
444 k = std::distance(m_txops.begin(), i);
445 }
446 }
447 i++;
448 }
449}
450
451void
453{
454 NS_LOG_FUNCTION(this);
458}
459
460Time
462{
463 NS_LOG_FUNCTION(this);
464 const Time& sifs = GetSifs();
465 Time rxAccessStart = m_lastRx.end + sifs;
467 {
468 rxAccessStart += GetEifsNoDifs();
469 }
470 // an EDCA TXOP is obtained based solely on activity of the primary channel
471 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
472 Time busyAccessStart = m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) + sifs;
473 Time txAccessStart = m_lastTxEnd + sifs;
474 Time navAccessStart = m_lastNavEnd + sifs;
475 Time ackTimeoutAccessStart = m_lastAckTimeoutEnd + sifs;
476 Time ctsTimeoutAccessStart = m_lastCtsTimeoutEnd + sifs;
477 Time switchingAccessStart = m_lastSwitchingEnd + sifs;
478 Time accessGrantedStart;
479 if (ignoreNav)
480 {
481 accessGrantedStart = std::max({rxAccessStart,
482 busyAccessStart,
483 txAccessStart,
484 ackTimeoutAccessStart,
485 ctsTimeoutAccessStart,
486 switchingAccessStart});
487 }
488 else
489 {
490 accessGrantedStart = std::max({rxAccessStart,
491 busyAccessStart,
492 txAccessStart,
493 navAccessStart,
494 ackTimeoutAccessStart,
495 ctsTimeoutAccessStart,
496 switchingAccessStart});
497 }
498 NS_LOG_INFO("access grant start=" << accessGrantedStart << ", rx access start=" << rxAccessStart
499 << ", busy access start=" << busyAccessStart
500 << ", tx access start=" << txAccessStart
501 << ", nav access start=" << navAccessStart);
502 return accessGrantedStart;
503}
504
505Time
507{
508 NS_LOG_FUNCTION(this << txop);
509 Time mostRecentEvent =
510 std::max({txop->GetBackoffStart(m_linkId),
511 GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot())});
512 NS_LOG_DEBUG("Backoff start: " << mostRecentEvent.As(Time::US));
513
514 return mostRecentEvent;
515}
516
517Time
519{
520 NS_LOG_FUNCTION(this << txop);
521 Time backoffEnd = GetBackoffStartFor(txop) + (txop->GetBackoffSlots(m_linkId) * GetSlot());
522 NS_LOG_DEBUG("Backoff end: " << backoffEnd.As(Time::US));
523
524 return backoffEnd;
525}
526
527void
529{
530 NS_LOG_FUNCTION(this);
531 uint32_t k = 0;
532 for (auto txop : m_txops)
533 {
534 Time backoffStart = GetBackoffStartFor(txop);
535 if (backoffStart <= Simulator::Now())
536 {
537 uint32_t nIntSlots = ((Simulator::Now() - backoffStart) / GetSlot()).GetHigh();
538 /*
539 * EDCA behaves slightly different to DCA. For EDCA we
540 * decrement once at the slot boundary at the end of AIFS as
541 * well as once at the end of each clear slot
542 * thereafter. For DCA we only decrement at the end of each
543 * clear slot after DIFS. We account for the extra backoff
544 * by incrementing the slot count here in the case of
545 * EDCA. The if statement whose body we are in has confirmed
546 * that a minimum of AIFS has elapsed since last busy
547 * medium.
548 */
549 if (txop->IsQosTxop())
550 {
551 nIntSlots++;
552 }
553 uint32_t n = std::min(nIntSlots, txop->GetBackoffSlots(m_linkId));
554 NS_LOG_DEBUG("dcf " << k << " dec backoff slots=" << n);
555 Time backoffUpdateBound = backoffStart + (n * GetSlot());
556 txop->UpdateBackoffSlotsNow(n, backoffUpdateBound, m_linkId);
557 }
558 ++k;
559 }
560}
561
562void
564{
565 NS_LOG_FUNCTION(this);
570 bool accessTimeoutNeeded = false;
571 Time expectedBackoffEnd = Simulator::GetMaximumSimulationTime();
572 for (auto txop : m_txops)
573 {
574 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED)
575 {
576 Time tmp = GetBackoffEndFor(txop);
577 if (tmp > Simulator::Now())
578 {
579 accessTimeoutNeeded = true;
580 expectedBackoffEnd = std::min(expectedBackoffEnd, tmp);
581 }
582 }
583 }
584 NS_LOG_DEBUG("Access timeout needed: " << accessTimeoutNeeded);
585 if (accessTimeoutNeeded)
586 {
587 NS_LOG_DEBUG("expected backoff end=" << expectedBackoffEnd);
588 Time expectedBackoffDelay = expectedBackoffEnd - Simulator::Now();
590 Simulator::GetDelayLeft(m_accessTimeout) > expectedBackoffDelay)
591 {
593 }
595 {
596 m_accessTimeout = Simulator::Schedule(expectedBackoffDelay,
598 this);
599 }
600 }
601}
602
603uint16_t
605{
606 NS_LOG_FUNCTION(this << interval.As(Time::US) << end.As(Time::S));
607
608 // If the medium is busy or it just became idle, UpdateLastIdlePeriod does
609 // nothing. This allows us to call this method, e.g., at the end of a frame
610 // reception and check the busy/idle status of the channel before the start
611 // of the frame reception (last idle period was last updated at the start of
612 // the frame reception).
613 // If the medium has been idle for some time, UpdateLastIdlePeriod updates
614 // the last idle period. This is normally what we want because this method may
615 // also be called before starting a TXOP gained through EDCA.
617
618 uint16_t width = 0;
619
620 // we iterate over the different types of channels in the same order as they
621 // are listed in WifiChannelListType
622 for (const auto& lastIdle : m_lastIdle)
623 {
624 if (lastIdle.second.start <= end - interval && lastIdle.second.end >= end)
625 {
626 // channel idle, update width
627 width = (width == 0) ? 20 : (2 * width);
628 }
629 else
630 {
631 break;
632 }
633 }
634 return width;
635}
636
637bool
638ChannelAccessManager::GetPer20MHzBusy(const std::set<uint8_t>& indices) const
639{
640 const auto now = Simulator::Now();
641
642 if (m_phy->GetChannelWidth() < 40)
643 {
644 NS_ASSERT_MSG(indices.size() == 1 && *indices.cbegin() == 0,
645 "Index 0 only can be specified if the channel width is less than 40 MHz");
646 return m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now;
647 }
648
649 for (const auto index : indices)
650 {
651 NS_ASSERT(index < m_lastPer20MHzBusyEnd.size());
652 if (m_lastPer20MHzBusyEnd.at(index) > now)
653 {
654 NS_LOG_DEBUG("20 MHz channel with index " << +index << " is busy");
655 return true;
656 }
657 }
658 return false;
659}
660
661void
663{
664 NS_LOG_FUNCTION(this << qosTxop << duration);
665 NS_ASSERT(qosTxop->IsQosTxop());
667 Time resume = Simulator::Now() + duration;
668 NS_LOG_DEBUG("Backoff will resume at time " << resume << " with "
669 << qosTxop->GetBackoffSlots(m_linkId)
670 << " remaining slot(s)");
671 qosTxop->UpdateBackoffSlotsNow(0, resume, m_linkId);
673}
674
675void
677{
678 NS_LOG_FUNCTION(this << duration);
679 NS_LOG_DEBUG("rx start for=" << duration);
683 m_lastRx.end = m_lastRx.start + duration;
684 m_lastRxReceivedOk = true;
685}
686
687void
689{
690 NS_LOG_FUNCTION(this);
691 NS_LOG_DEBUG("rx end ok");
693 m_lastRxReceivedOk = true;
694}
695
696void
698{
699 NS_LOG_FUNCTION(this);
700 NS_LOG_DEBUG("rx end error");
701 // we expect the PHY to notify us of the start of a CCA busy period, if needed
703 m_lastRxReceivedOk = false;
704}
705
706void
708{
709 NS_LOG_FUNCTION(this << duration);
710 m_lastRxReceivedOk = true;
711 Time now = Simulator::Now();
712 if (m_lastRx.end > now)
713 {
714 // this may be caused only if PHY has started to receive a packet
715 // inside SIFS, so, we check that lastRxStart was maximum a SIFS ago
716 NS_ASSERT(now - m_lastRx.start <= GetSifs());
717 m_lastRx.end = now;
718 }
719 else
720 {
722 }
723 NS_LOG_DEBUG("tx start for " << duration);
725 m_lastTxEnd = now + duration;
726}
727
728void
730 WifiChannelListType channelType,
731 const std::vector<Time>& per20MhzDurations)
732{
733 NS_LOG_FUNCTION(this << duration << channelType);
736 auto lastBusyEndIt = m_lastBusyEnd.find(channelType);
737 NS_ASSERT(lastBusyEndIt != m_lastBusyEnd.end());
738 Time now = Simulator::Now();
739 lastBusyEndIt->second = now + duration;
740 NS_ASSERT_MSG(per20MhzDurations.size() == m_lastPer20MHzBusyEnd.size(),
741 "Size of received vector (" << per20MhzDurations.size()
742 << ") differs from the expected size ("
743 << m_lastPer20MHzBusyEnd.size() << ")");
744 for (std::size_t chIdx = 0; chIdx < per20MhzDurations.size(); ++chIdx)
745 {
746 if (per20MhzDurations[chIdx].IsStrictlyPositive())
747 {
748 m_lastPer20MHzBusyEnd[chIdx] = now + per20MhzDurations[chIdx];
749 }
750 }
751}
752
753void
755{
756 NS_LOG_FUNCTION(this << duration);
757 Time now = Simulator::Now();
758 NS_ASSERT(m_lastTxEnd <= now);
760
761 m_lastRxReceivedOk = true;
763 m_lastRx.end = std::min(m_lastRx.end, now);
764 m_lastNavEnd = std::min(m_lastNavEnd, now);
767
768 // the new operating channel may have a different width than the previous one
770
771 // Cancel timeout
773 {
775 }
776
777 // Notify the FEM, which will in turn notify the MAC
778 m_feManager->NotifySwitchingStartNow(duration);
779
780 // Reset backoffs
781 for (auto txop : m_txops)
782 {
783 uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
784 if (remainingSlots > 0)
785 {
786 txop->UpdateBackoffSlotsNow(remainingSlots, now, m_linkId);
787 NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
788 }
789 txop->ResetCw(m_linkId);
790 txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
791 }
792
793 NS_LOG_DEBUG("switching start for " << duration);
794 m_lastSwitchingEnd = now + duration;
795}
796
797void
799{
800 NS_LOG_FUNCTION(this);
801 m_sleeping = true;
802 // Cancel timeout
804 {
806 }
807
808 // Reset backoffs
809 for (auto txop : m_txops)
810 {
811 txop->NotifySleep(m_linkId);
812 }
813}
814
815void
817{
818 NS_LOG_FUNCTION(this);
819 m_off = true;
820 // Cancel timeout
822 {
824 }
825
826 // Reset backoffs
827 for (auto txop : m_txops)
828 {
829 txop->NotifyOff();
830 }
831}
832
833void
835{
836 NS_LOG_FUNCTION(this);
837 m_sleeping = false;
838 for (auto txop : m_txops)
839 {
840 uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
841 if (remainingSlots > 0)
842 {
843 txop->UpdateBackoffSlotsNow(remainingSlots, Simulator::Now(), m_linkId);
844 NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
845 }
846 txop->ResetCw(m_linkId);
847 txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
848 txop->NotifyWakeUp(m_linkId);
849 }
850}
851
852void
854{
855 NS_LOG_FUNCTION(this);
856 m_off = false;
857 for (auto txop : m_txops)
858 {
859 uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
860 if (remainingSlots > 0)
861 {
862 txop->UpdateBackoffSlotsNow(remainingSlots, Simulator::Now(), m_linkId);
863 NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
864 }
865 txop->ResetCw(m_linkId);
866 txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
867 txop->NotifyOn();
868 }
869}
870
871void
873{
874 NS_LOG_FUNCTION(this << duration);
875 NS_LOG_DEBUG("nav reset for=" << duration);
877 m_lastNavEnd = Simulator::Now() + duration;
885}
886
887void
889{
890 NS_LOG_FUNCTION(this << duration);
891 NS_LOG_DEBUG("nav start for=" << duration);
893 m_lastNavEnd = std::max(m_lastNavEnd, Simulator::Now() + duration);
894}
895
896void
898{
899 NS_LOG_FUNCTION(this << duration);
901 m_lastAckTimeoutEnd = Simulator::Now() + duration;
902}
903
904void
906{
907 NS_LOG_FUNCTION(this);
910}
911
912void
914{
915 NS_LOG_FUNCTION(this << duration);
916 m_lastCtsTimeoutEnd = Simulator::Now() + duration;
917}
918
919void
921{
922 NS_LOG_FUNCTION(this);
925}
926
927void
929{
930 NS_LOG_FUNCTION(this);
931 Time idleStart = std::max({m_lastTxEnd, m_lastRx.end, m_lastSwitchingEnd});
932 Time now = Simulator::Now();
933
934 if (idleStart >= now)
935 {
936 // No new idle period
937 return;
938 }
939
940 for (const auto& busyEnd : m_lastBusyEnd)
941 {
942 if (busyEnd.second < now)
943 {
944 auto lastIdleIt = m_lastIdle.find(busyEnd.first);
945 NS_ASSERT(lastIdleIt != m_lastIdle.end());
946 lastIdleIt->second = {std::max(idleStart, busyEnd.second), now};
947 NS_LOG_DEBUG("New idle period (" << lastIdleIt->second.start.As(Time::S) << ", "
948 << lastIdleIt->second.end.As(Time::S)
949 << ") on channel " << lastIdleIt->first);
950 }
951 }
952}
953
954} // 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)
void DoInitialize() override
Initialize() implementation.
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:795
uint16_t GetChannelWidth() const
Definition: wifi-phy.cc:1035
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:783
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1005
Time GetPifs() const
Return the PCF Interframe Space (PIFS) for this PHY.
Definition: wifi-phy.cc:807
WifiStandard GetStandard() const
Get the configured Wi-Fi standard.
Definition: wifi-phy.cc:1011
void NotifyChannelAccessRequested()
Notify the PHY that an access to the channel was requested.
Definition: wifi-phy.cc:1865
Time GetAckTxTime() const
Return the estimated Ack TX time for this PHY.
Definition: wifi-phy.cc:813
const WifiPhyOperatingChannel & GetOperatingChannel() const
Get a const reference to the operating channel.
Definition: wifi-phy.cc:1017
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.