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
22#include "txop.h"
23#include "wifi-phy-listener.h"
24#include "wifi-phy.h"
25
26#include "ns3/eht-frame-exchange-manager.h"
27#include "ns3/log.h"
28#include "ns3/simulator.h"
29
30#include <sstream>
31
32#undef NS_LOG_APPEND_CONTEXT
33#define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "] "
34
35namespace ns3
36{
37
38NS_LOG_COMPONENT_DEFINE("ChannelAccessManager");
39
48{
49 public:
56 : m_cam(cam),
57 m_active(true)
58 {
59 }
60
61 ~PhyListener() override
62 {
63 }
64
70 void SetActive(bool active)
71 {
72 m_active = active;
73 }
74
78 bool IsActive() const
79 {
80 return m_active;
81 }
82
83 void NotifyRxStart(Time duration) override
84 {
85 if (m_active)
86 {
87 m_cam->NotifyRxStartNow(duration);
88 }
89 }
90
91 void NotifyRxEndOk() override
92 {
93 if (m_active)
94 {
96 }
97 }
98
99 void NotifyRxEndError() override
100 {
101 if (m_active)
102 {
104 }
105 }
106
107 void NotifyTxStart(Time duration, double txPowerDbm) override
108 {
109 if (m_active)
110 {
111 m_cam->NotifyTxStartNow(duration);
112 }
113 }
114
116 WifiChannelListType channelType,
117 const std::vector<Time>& per20MhzDurations) override
118 {
119 if (m_active)
120 {
121 m_cam->NotifyCcaBusyStartNow(duration, channelType, per20MhzDurations);
122 }
123 }
124
125 void NotifySwitchingStart(Time duration) override
126 {
127 m_cam->NotifySwitchingStartNow(this, duration);
128 }
129
130 void NotifySleep() override
131 {
132 if (m_active)
133 {
135 }
136 }
137
138 void NotifyOff() override
139 {
140 if (m_active)
141 {
143 }
144 }
145
146 void NotifyWakeup() override
147 {
148 if (m_active)
149 {
151 }
152 }
153
154 void NotifyOn() override
155 {
156 if (m_active)
157 {
159 }
160 }
161
162 private:
164 bool m_active;
165};
166
167/****************************************************************
168 * Implement the channel access manager of all Txop holders
169 ****************************************************************/
170
172 : m_lastAckTimeoutEnd(0),
173 m_lastCtsTimeoutEnd(0),
174 m_lastNavEnd(0),
175 m_lastRx({MicroSeconds(0), MicroSeconds(0)}),
176 m_lastRxReceivedOk(true),
177 m_lastTxEnd(0),
178 m_lastSwitchingEnd(0),
179 m_usingOtherEmlsrLink(false),
180 m_lastUsingOtherEmlsrLinkEnd(0),
181 m_sleeping(false),
182 m_off(false),
183 m_linkId(0)
184{
185 NS_LOG_FUNCTION(this);
186 InitLastBusyStructs();
187}
188
190{
191 NS_LOG_FUNCTION(this);
192}
193
194void
196{
197 NS_LOG_FUNCTION(this);
199}
200
201void
203{
204 NS_LOG_FUNCTION(this);
205 for (Ptr<Txop> i : m_txops)
206 {
207 i->Dispose();
208 i = nullptr;
209 }
210 m_phy = nullptr;
211 m_feManager = nullptr;
212 m_phyListeners.clear();
213}
214
217{
218 if (auto listenerIt = m_phyListeners.find(phy); listenerIt != m_phyListeners.end())
219 {
220 return listenerIt->second.get();
221 }
222 return nullptr;
223}
224
225void
227{
228 NS_LOG_FUNCTION(this << phy);
229
230 auto phyListener = GetPhyListener(phy);
231
232 if (phyListener)
233 {
234 // a PHY listener for the given PHY already exists, it must be inactive
235 NS_ASSERT_MSG(!phyListener->IsActive(),
236 "There is already an active listener registered for given PHY");
237 NS_ASSERT_MSG(!m_phy, "Cannot reactivate a listener if another PHY is active");
238 phyListener->SetActive(true);
239 }
240 else
241 {
242 phyListener = new PhyListener(this);
243 m_phyListeners.emplace(phy, phyListener);
244 phy->RegisterListener(phyListener);
245 }
246 if (m_phy)
247 {
249 }
250 m_phy = phy; // this is the new active PHY
252 if (phy->IsStateSwitching())
253 {
254 auto duration = phy->GetDelayUntilIdle();
255 NS_LOG_DEBUG("switching start for " << duration);
256 m_lastSwitchingEnd = Simulator::Now() + duration;
257 }
258}
259
260void
262{
263 NS_LOG_FUNCTION(this << phy);
264 if (auto phyListener = GetPhyListener(phy))
265 {
266 phy->UnregisterListener(phyListener);
267 m_phyListeners.erase(phy);
268 // reset m_phy if we are removing listener registered for the active PHY
269 if (m_phy == phy)
270 {
271 m_phy = nullptr;
272 }
273 }
274}
275
276void
278{
279 NS_LOG_FUNCTION(this << phy);
280 if (auto listener = GetPhyListener(phy))
281 {
282 listener->SetActive(false);
283 }
284 if (m_phy == phy)
285 {
286 m_phy = nullptr;
287 }
288}
289
290void
292 const WifiPhyOperatingChannel& channel,
293 uint8_t linkId)
294{
295 NS_LOG_FUNCTION(this << phy << channel << linkId);
296 NS_ASSERT_MSG(m_switchingEmlsrLinks.count(phy) == 0,
297 "The given PHY is already expected to switch channel");
298 m_switchingEmlsrLinks.emplace(phy, EmlsrLinkSwitchInfo{channel, linkId});
299}
300
301void
303{
304 NS_LOG_FUNCTION(this << +linkId);
305 m_linkId = linkId;
306}
307
308void
310{
311 NS_LOG_FUNCTION(this << feManager);
312 m_feManager = feManager;
313 m_feManager->SetChannelAccessManager(this);
314}
315
316Time
318{
319 return m_phy->GetSlot();
320}
321
322Time
324{
325 return m_phy->GetSifs();
326}
327
328Time
330{
331 return m_phy->GetSifs() + m_phy->GetAckTxTime();
332}
333
334void
336{
337 NS_LOG_FUNCTION(this << txop);
338 m_txops.push_back(txop);
339}
340
341void
343{
344 NS_LOG_FUNCTION(this);
345 Time now = Simulator::Now();
346 m_lastBusyEnd.clear();
347 m_lastPer20MHzBusyEnd.clear();
348 m_lastIdle.clear();
350 m_lastIdle[WIFI_CHANLIST_PRIMARY] = {now, now};
351
353 {
354 return;
355 }
356
357 uint16_t width = m_phy->GetChannelWidth();
358
359 if (width >= 40)
360 {
363 }
364 if (width >= 80)
365 {
368 }
369 if (width >= 160)
370 {
373 }
374 // TODO Add conditions for new channel widths as they get supported
375
376 if (m_phy->GetStandard() >= WIFI_STANDARD_80211ax && width > 20)
377 {
378 m_lastPer20MHzBusyEnd.assign(width / 20, now);
379 }
380}
381
382bool
384{
385 NS_LOG_FUNCTION(this);
386 Time now = Simulator::Now();
387 return (m_lastRx.end > now) // RX
388 || (m_lastTxEnd > now) // TX
389 || (m_lastNavEnd > now) // NAV busy
390 // an EDCA TXOP is obtained based solely on activity of the primary channel
391 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
392 || (m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now); // CCA busy
393}
394
395bool
397{
398 NS_LOG_FUNCTION(this << txop);
399
400 // No backoff needed if in sleep mode, off or when using another EMLSR link
402 {
403 return false;
404 }
405
406 // the Txop might have a stale value of remaining backoff slots
408
409 /*
410 * From section 10.3.4.2 "Basic access" of IEEE 802.11-2016:
411 *
412 * A STA may transmit an MPDU when it is operating under the DCF access
413 * method, either in the absence of a PC, or in the CP of the PCF access
414 * method, when the STA determines that the medium is idle when a frame is
415 * queued for transmission, and remains idle for a period of a DIFS, or an
416 * EIFS (10.3.2.3.7) from the end of the immediately preceding medium-busy
417 * event, whichever is the greater, and the backoff timer is zero. Otherwise
418 * the random backoff procedure described in 10.3.4.3 shall be followed.
419 *
420 * From section 10.22.2.2 "EDCA backoff procedure" of IEEE 802.11-2016:
421 *
422 * The backoff procedure shall be invoked by an EDCAF when any of the following
423 * events occurs:
424 * a) An MA-UNITDATA.request primitive is received that causes a frame with that AC
425 * to be queued for transmission such that one of the transmit queues associated
426 * with that AC has now become non-empty and any other transmit queues
427 * associated with that AC are empty; the medium is busy on the primary channel
428 */
429 if (!txop->HasFramesToTransmit(m_linkId) && txop->GetAccessStatus(m_linkId) != Txop::GRANTED &&
430 txop->GetBackoffSlots(m_linkId) == 0)
431 {
432 if (!IsBusy())
433 {
434 // medium idle. If this is a DCF, use immediate access (we can transmit
435 // in a DIFS if the medium remains idle). If this is an EDCAF, update
436 // the backoff start time kept by the EDCAF to the current time in order
437 // to correctly align the backoff start time at the next slot boundary
438 // (performed by the next call to ChannelAccessManager::RequestAccess())
439 Time delay =
440 (txop->IsQosTxop() ? Seconds(0) : GetSifs() + txop->GetAifsn(m_linkId) * GetSlot());
441 txop->UpdateBackoffSlotsNow(0, Simulator::Now() + delay, m_linkId);
442 }
443 else
444 {
445 // medium busy, backoff is needed
446 return true;
447 }
448 }
449 return false;
450}
451
452void
454{
455 NS_LOG_FUNCTION(this << txop);
456 if (m_phy)
457 {
459 }
461 {
462 NS_LOG_DEBUG("Channel access cannot be requested while using another EMLSR link");
463 return;
464 }
465 // Deny access if in sleep mode or off
466 if (m_sleeping || m_off)
467 {
468 return;
469 }
470 /*
471 * EDCAF operations shall be performed at slot boundaries (Sec. 10.22.2.4 of 802.11-2016)
472 */
473 Time accessGrantStart = GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot());
474
475 if (txop->IsQosTxop() && txop->GetBackoffStart(m_linkId) > accessGrantStart)
476 {
477 // The backoff start time reported by the EDCAF is more recent than the last
478 // time the medium was busy plus an AIFS, hence we need to align it to the
479 // next slot boundary.
480 Time diff = txop->GetBackoffStart(m_linkId) - accessGrantStart;
481 uint32_t nIntSlots = (diff / GetSlot()).GetHigh() + 1;
482 txop->UpdateBackoffSlotsNow(0, accessGrantStart + (nIntSlots * GetSlot()), m_linkId);
483 }
484
486 NS_ASSERT(txop->GetAccessStatus(m_linkId) != Txop::REQUESTED);
487 txop->NotifyAccessRequested(m_linkId);
490}
491
492void
494{
495 NS_LOG_FUNCTION(this);
496 uint32_t k = 0;
497 Time now = Simulator::Now();
498 for (auto i = m_txops.begin(); i != m_txops.end(); k++)
499 {
500 Ptr<Txop> txop = *i;
501 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
502 (!txop->IsQosTxop() || !StaticCast<QosTxop>(txop)->EdcaDisabled(m_linkId)) &&
503 GetBackoffEndFor(txop) <= now)
504 {
509 NS_LOG_DEBUG("dcf " << k << " needs access. backoff expired. access granted. slots="
510 << txop->GetBackoffSlots(m_linkId));
511 i++; // go to the next item in the list.
512 k++;
513 std::vector<Ptr<Txop>> internalCollisionTxops;
514 for (auto j = i; j != m_txops.end(); j++, k++)
515 {
516 Ptr<Txop> otherTxop = *j;
517 if (otherTxop->GetAccessStatus(m_linkId) == Txop::REQUESTED &&
518 GetBackoffEndFor(otherTxop) <= now)
519 {
521 "dcf " << k << " needs access. backoff expired. internal collision. slots="
522 << otherTxop->GetBackoffSlots(m_linkId));
528 internalCollisionTxops.push_back(otherTxop);
529 }
530 }
531
541 // If we are operating on an OFDM channel wider than 20 MHz, find the largest
542 // idle primary channel and pass its width to the FrameExchangeManager, so that
543 // the latter can transmit PPDUs of the appropriate width (see Section 10.23.2.5
544 // of IEEE 802.11-2020).
545 auto interval = (m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
546 ? GetSifs() + 2 * GetSlot()
547 : m_phy->GetPifs();
548 auto width = (m_phy->GetOperatingChannel().IsOfdm() && m_phy->GetChannelWidth() > 20)
549 ? GetLargestIdlePrimaryChannel(interval, now)
551 if (m_feManager->StartTransmission(txop, width))
552 {
553 for (auto& collidingTxop : internalCollisionTxops)
554 {
555 m_feManager->NotifyInternalCollision(collidingTxop);
556 }
557 break;
558 }
559 else
560 {
561 // reset the current state to the EDCAF that won the contention
562 // but did not transmit anything
563 i--;
564 k = std::distance(m_txops.begin(), i);
565 }
566 }
567 i++;
568 }
569}
570
571void
573{
574 NS_LOG_FUNCTION(this);
578}
579
580Time
582{
583 NS_LOG_FUNCTION(this);
584 const Time& sifs = GetSifs();
585 Time rxAccessStart = m_lastRx.end + sifs;
587 {
588 rxAccessStart += GetEifsNoDifs();
589 }
590 // an EDCA TXOP is obtained based solely on activity of the primary channel
591 // (Sec. 10.23.2.5 of IEEE 802.11-2020)
592 Time busyAccessStart = m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) + sifs;
593 Time txAccessStart = m_lastTxEnd + sifs;
594 Time navAccessStart = m_lastNavEnd + sifs;
595 Time ackTimeoutAccessStart = m_lastAckTimeoutEnd + sifs;
596 Time ctsTimeoutAccessStart = m_lastCtsTimeoutEnd + sifs;
597 Time switchingAccessStart = m_lastSwitchingEnd + sifs;
598 Time usingOtherEmlsrLinkAccessStart =
600 Time accessGrantedStart;
601 if (ignoreNav)
602 {
603 accessGrantedStart = std::max({rxAccessStart,
604 busyAccessStart,
605 txAccessStart,
606 ackTimeoutAccessStart,
607 ctsTimeoutAccessStart,
608 usingOtherEmlsrLinkAccessStart,
609 switchingAccessStart});
610 }
611 else
612 {
613 accessGrantedStart = std::max({rxAccessStart,
614 busyAccessStart,
615 txAccessStart,
616 navAccessStart,
617 ackTimeoutAccessStart,
618 ctsTimeoutAccessStart,
619 usingOtherEmlsrLinkAccessStart,
620 switchingAccessStart});
621 }
622 std::stringstream ss;
624 {
625 ss << ", using other EMLSR link access start=" << usingOtherEmlsrLinkAccessStart;
626 }
627 NS_LOG_INFO("access grant start=" << accessGrantedStart << ", rx access start=" << rxAccessStart
628 << ", busy access start=" << busyAccessStart
629 << ", tx access start=" << txAccessStart
630 << ", nav access start=" << navAccessStart << ss.str());
631 return accessGrantedStart;
632}
633
634Time
636{
637 NS_LOG_FUNCTION(this << txop);
638 Time mostRecentEvent =
639 std::max({txop->GetBackoffStart(m_linkId),
640 GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot())});
641 NS_LOG_DEBUG("Backoff start: " << mostRecentEvent.As(Time::US));
642
643 return mostRecentEvent;
644}
645
646Time
648{
649 NS_LOG_FUNCTION(this << txop);
650 Time backoffEnd = GetBackoffStartFor(txop) + (txop->GetBackoffSlots(m_linkId) * GetSlot());
651 NS_LOG_DEBUG("Backoff end: " << backoffEnd.As(Time::US));
652
653 return backoffEnd;
654}
655
656void
658{
659 NS_LOG_FUNCTION(this);
660 uint32_t k = 0;
661 for (auto txop : m_txops)
662 {
663 Time backoffStart = GetBackoffStartFor(txop);
664 if (backoffStart <= Simulator::Now())
665 {
666 uint32_t nIntSlots = ((Simulator::Now() - backoffStart) / GetSlot()).GetHigh();
667 /*
668 * EDCA behaves slightly different to DCA. For EDCA we
669 * decrement once at the slot boundary at the end of AIFS as
670 * well as once at the end of each clear slot
671 * thereafter. For DCA we only decrement at the end of each
672 * clear slot after DIFS. We account for the extra backoff
673 * by incrementing the slot count here in the case of
674 * EDCA. The if statement whose body we are in has confirmed
675 * that a minimum of AIFS has elapsed since last busy
676 * medium.
677 */
678 if (txop->IsQosTxop())
679 {
680 nIntSlots++;
681 }
682 uint32_t n = std::min(nIntSlots, txop->GetBackoffSlots(m_linkId));
683 NS_LOG_DEBUG("dcf " << k << " dec backoff slots=" << n);
684 Time backoffUpdateBound = backoffStart + (n * GetSlot());
685 txop->UpdateBackoffSlotsNow(n, backoffUpdateBound, m_linkId);
686 }
687 ++k;
688 }
689}
690
691void
693{
694 NS_LOG_FUNCTION(this);
699 bool accessTimeoutNeeded = false;
700 Time expectedBackoffEnd = Simulator::GetMaximumSimulationTime();
701 for (auto txop : m_txops)
702 {
703 if (txop->GetAccessStatus(m_linkId) == Txop::REQUESTED)
704 {
705 Time tmp = GetBackoffEndFor(txop);
706 if (tmp > Simulator::Now())
707 {
708 accessTimeoutNeeded = true;
709 expectedBackoffEnd = std::min(expectedBackoffEnd, tmp);
710 }
711 }
712 }
713 NS_LOG_DEBUG("Access timeout needed: " << accessTimeoutNeeded);
714 if (accessTimeoutNeeded)
715 {
716 NS_LOG_DEBUG("expected backoff end=" << expectedBackoffEnd);
717 Time expectedBackoffDelay = expectedBackoffEnd - Simulator::Now();
719 Simulator::GetDelayLeft(m_accessTimeout) > expectedBackoffDelay)
720 {
722 }
724 {
725 m_accessTimeout = Simulator::Schedule(expectedBackoffDelay,
727 this);
728 }
729 }
730}
731
732uint16_t
734{
735 NS_LOG_FUNCTION(this << interval.As(Time::US) << end.As(Time::S));
736
737 // If the medium is busy or it just became idle, UpdateLastIdlePeriod does
738 // nothing. This allows us to call this method, e.g., at the end of a frame
739 // reception and check the busy/idle status of the channel before the start
740 // of the frame reception (last idle period was last updated at the start of
741 // the frame reception).
742 // If the medium has been idle for some time, UpdateLastIdlePeriod updates
743 // the last idle period. This is normally what we want because this method may
744 // also be called before starting a TXOP gained through EDCA.
746
747 uint16_t width = 0;
748
749 // we iterate over the different types of channels in the same order as they
750 // are listed in WifiChannelListType
751 for (const auto& lastIdle : m_lastIdle)
752 {
753 if (lastIdle.second.start <= end - interval && lastIdle.second.end >= end)
754 {
755 // channel idle, update width
756 width = (width == 0) ? 20 : (2 * width);
757 }
758 else
759 {
760 break;
761 }
762 }
763 return width;
764}
765
766bool
767ChannelAccessManager::GetPer20MHzBusy(const std::set<uint8_t>& indices) const
768{
769 const auto now = Simulator::Now();
770
771 if (m_phy->GetChannelWidth() < 40)
772 {
773 NS_ASSERT_MSG(indices.size() == 1 && *indices.cbegin() == 0,
774 "Index 0 only can be specified if the channel width is less than 40 MHz");
775 return m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now;
776 }
777
778 for (const auto index : indices)
779 {
780 NS_ASSERT(index < m_lastPer20MHzBusyEnd.size());
781 if (m_lastPer20MHzBusyEnd.at(index) > now)
782 {
783 NS_LOG_DEBUG("20 MHz channel with index " << +index << " is busy");
784 return true;
785 }
786 }
787 return false;
788}
789
790void
792{
793 NS_LOG_FUNCTION(this << qosTxop << duration);
794 NS_ASSERT(qosTxop->IsQosTxop());
796 Time resume = Simulator::Now() + duration;
797 NS_LOG_DEBUG("Backoff will resume at time " << resume << " with "
798 << qosTxop->GetBackoffSlots(m_linkId)
799 << " remaining slot(s)");
800 qosTxop->UpdateBackoffSlotsNow(0, resume, m_linkId);
802}
803
804void
806{
807 NS_LOG_FUNCTION(this << duration);
808 NS_LOG_DEBUG("rx start for=" << duration);
812 m_lastRx.end = m_lastRx.start + duration;
813 m_lastRxReceivedOk = true;
814}
815
816void
818{
819 NS_LOG_FUNCTION(this);
820 NS_LOG_DEBUG("rx end ok");
822 m_lastRxReceivedOk = true;
823}
824
825void
827{
828 NS_LOG_FUNCTION(this);
829 NS_LOG_DEBUG("rx end error");
830 // we expect the PHY to notify us of the start of a CCA busy period, if needed
832 m_lastRxReceivedOk = false;
833}
834
835void
837{
838 NS_LOG_FUNCTION(this << duration);
839 m_lastRxReceivedOk = true;
840 Time now = Simulator::Now();
841 if (m_lastRx.end > now)
842 {
843 // this may be caused only if PHY has started to receive a packet
844 // inside SIFS, so, we check that lastRxStart was maximum a SIFS ago
845 NS_ASSERT(now - m_lastRx.start <= GetSifs());
846 m_lastRx.end = now;
847 }
848 else
849 {
851 }
852 NS_LOG_DEBUG("tx start for " << duration);
854 m_lastTxEnd = now + duration;
855}
856
857void
859 WifiChannelListType channelType,
860 const std::vector<Time>& per20MhzDurations)
861{
862 NS_LOG_FUNCTION(this << duration << channelType);
865 auto lastBusyEndIt = m_lastBusyEnd.find(channelType);
866 NS_ASSERT(lastBusyEndIt != m_lastBusyEnd.end());
867 Time now = Simulator::Now();
868 lastBusyEndIt->second = now + duration;
869 NS_ASSERT_MSG(per20MhzDurations.size() == m_lastPer20MHzBusyEnd.size(),
870 "Size of received vector (" << per20MhzDurations.size()
871 << ") differs from the expected size ("
872 << m_lastPer20MHzBusyEnd.size() << ")");
873 for (std::size_t chIdx = 0; chIdx < per20MhzDurations.size(); ++chIdx)
874 {
875 if (per20MhzDurations[chIdx].IsStrictlyPositive())
876 {
877 m_lastPer20MHzBusyEnd[chIdx] = now + per20MhzDurations[chIdx];
878 }
879 }
880}
881
882void
884{
885 NS_LOG_FUNCTION(this << phyListener << duration);
886
887 Time now = Simulator::Now();
888 NS_ASSERT(m_lastTxEnd <= now);
890
891 if (phyListener) // to make tests happy
892 {
893 // check if the PHY switched channel to operate on another EMLSR link
894
895 for (const auto& [phyRef, listener] : m_phyListeners)
896 {
897 Ptr<WifiPhy> phy = phyRef;
898 auto emlsrInfoIt = m_switchingEmlsrLinks.find(phy);
899
900 if (listener.get() == phyListener && emlsrInfoIt != m_switchingEmlsrLinks.cend() &&
901 phy->GetOperatingChannel() == emlsrInfoIt->second.channel)
902 {
903 // the PHY associated with the given PHY listener switched channel to
904 // operate on another EMLSR link as expected. We don't need this listener
905 // anymore. The MAC will connect a new listener to the ChannelAccessManager
906 // instance associated with the link the PHY is now operating on
908 auto ehtFem = DynamicCast<EhtFrameExchangeManager>(m_feManager);
909 NS_ASSERT(ehtFem);
910 ehtFem->NotifySwitchingEmlsrLink(phy, emlsrInfoIt->second.linkId, duration);
911 m_switchingEmlsrLinks.erase(emlsrInfoIt);
912 return;
913 }
914 }
915 }
916
917 ResetState();
918
919 // Reset backoffs
920 for (const auto& txop : m_txops)
921 {
922 ResetBackoff(txop);
923 }
924
925 // Notify the FEM, which will in turn notify the MAC
926 m_feManager->NotifySwitchingStartNow(duration);
927
928 NS_LOG_DEBUG("switching start for " << duration);
929 m_lastSwitchingEnd = now + duration;
930}
931
932void
934{
935 NS_LOG_FUNCTION(this);
936
937 Time now = Simulator::Now();
938 m_lastRxReceivedOk = true;
940 m_lastRx.end = std::min(m_lastRx.end, now);
941 m_lastNavEnd = std::min(m_lastNavEnd, now);
944
946
947 // Cancel timeout
949 {
951 }
952}
953
954void
956{
957 NS_LOG_FUNCTION(this << txop);
958
959 uint32_t remainingSlots = txop->GetBackoffSlots(m_linkId);
960 if (remainingSlots > 0)
961 {
962 txop->UpdateBackoffSlotsNow(remainingSlots, Simulator::Now(), m_linkId);
963 NS_ASSERT(txop->GetBackoffSlots(m_linkId) == 0);
964 }
965 txop->ResetCw(m_linkId);
966 txop->GetLink(m_linkId).access = Txop::NOT_REQUESTED;
967}
968
969void
971{
972 NS_LOG_FUNCTION(this);
973 m_sleeping = true;
974 // Cancel timeout
976 {
978 }
979
980 // Reset backoffs
981 for (auto txop : m_txops)
982 {
983 txop->NotifySleep(m_linkId);
984 }
985}
986
987void
989{
990 NS_LOG_FUNCTION(this);
991 m_off = true;
992 // Cancel timeout
994 {
996 }
997
998 // Reset backoffs
999 for (auto txop : m_txops)
1000 {
1001 txop->NotifyOff();
1002 }
1003}
1004
1005void
1007{
1008 NS_LOG_FUNCTION(this);
1009 m_sleeping = false;
1010 for (auto txop : m_txops)
1011 {
1012 ResetBackoff(txop);
1013 txop->NotifyWakeUp(m_linkId);
1014 }
1015}
1016
1017void
1019{
1020 NS_LOG_FUNCTION(this);
1021 m_off = false;
1022 for (auto txop : m_txops)
1023 {
1024 ResetBackoff(txop);
1025 txop->NotifyOn();
1026 }
1027}
1028
1029void
1031{
1032 NS_LOG_FUNCTION(this << duration);
1033 NS_LOG_DEBUG("nav reset for=" << duration);
1034 UpdateBackoff();
1035 m_lastNavEnd = Simulator::Now() + duration;
1043}
1044
1045void
1047{
1048 NS_LOG_FUNCTION(this << duration);
1049 NS_LOG_DEBUG("nav start for=" << duration);
1050 UpdateBackoff();
1051 m_lastNavEnd = std::max(m_lastNavEnd, Simulator::Now() + duration);
1052}
1053
1054void
1056{
1057 NS_LOG_FUNCTION(this << duration);
1059 m_lastAckTimeoutEnd = Simulator::Now() + duration;
1060}
1061
1062void
1064{
1065 NS_LOG_FUNCTION(this);
1068}
1069
1070void
1072{
1073 NS_LOG_FUNCTION(this << duration);
1074 m_lastCtsTimeoutEnd = Simulator::Now() + duration;
1075}
1076
1077void
1079{
1080 NS_LOG_FUNCTION(this);
1083}
1084
1085void
1087{
1088 NS_LOG_FUNCTION(this);
1089 // update backoff if a PHY is operating on this link
1090 if (m_phy)
1091 {
1092 UpdateBackoff();
1093 }
1094 // Cancel timeout
1096 {
1098 }
1099 m_usingOtherEmlsrLink = true;
1101}
1102
1103void
1105{
1106 NS_LOG_FUNCTION(this);
1107 m_usingOtherEmlsrLink = false;
1110}
1111
1112void
1114{
1115 NS_LOG_FUNCTION(this);
1116 Time idleStart =
1118 Time now = Simulator::Now();
1119
1120 if (idleStart >= now)
1121 {
1122 // No new idle period
1123 return;
1124 }
1125
1126 for (const auto& busyEnd : m_lastBusyEnd)
1127 {
1128 if (busyEnd.second < now)
1129 {
1130 auto lastIdleIt = m_lastIdle.find(busyEnd.first);
1131 NS_ASSERT(lastIdleIt != m_lastIdle.end());
1132 lastIdleIt->second = {std::max(idleStart, busyEnd.second), now};
1133 NS_LOG_DEBUG("New idle period (" << lastIdleIt->second.start.As(Time::S) << ", "
1134 << lastIdleIt->second.end.As(Time::S)
1135 << ") on channel " << lastIdleIt->first);
1136 }
1137 }
1138}
1139
1140} // 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 ResetBackoff(Ptr< Txop > txop)
Reset the backoff for the given DCF/EDCAF.
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)
void NotifySwitchingStartNow(PhyListener *phyListener, Time duration)
Time GetBackoffEndFor(Ptr< Txop > txop)
Return the time when the backoff procedure ended (or will ended) for the given Txop.
void ResetState()
Reset the state variables of this channel access manager.
void NotifySwitchingEmlsrLink(Ptr< WifiPhy > phy, const WifiPhyOperatingChannel &channel, uint8_t linkId)
Notify that the given PHY is about to switch to the given operating channel, which is used by the giv...
void NotifyStopUsingOtherEmlsrLink()
Notify that another EMLSR link is no longer being used, hence medium access can be resumed.
void NotifyWakeupNow()
Notify the Txop that the device has been resumed from sleep mode.
bool m_lastRxReceivedOk
the last receive OK
std::unordered_map< Ptr< WifiPhy >, EmlsrLinkSwitchInfo > m_switchingEmlsrLinks
Store information about the PHY objects that are going to operate on another EMLSR link.
std::map< WifiChannelListType, Timespan > m_lastIdle
the last idle start and end time for each channel type
Ptr< WifiPhy > m_phy
pointer to the unique active PHY
void NotifyAckTimeoutResetNow()
Notify that ack timer has reset.
void 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.
void DeactivatePhyListener(Ptr< WifiPhy > phy)
Deactivate current registered listener for PHY events on the given PHY.
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 on the given PHY.
Time m_lastTxEnd
the last transmit end time
void SetupPhyListener(Ptr< WifiPhy > phy)
Set up (or reactivate) listener for PHY events on the given PHY.
void NotifyStartUsingOtherEmlsrLink()
Notify that another EMLSR link is being used, hence medium access should be disabled.
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
bool m_usingOtherEmlsrLink
whether another EMLSR link is being used
void UpdateLastIdlePeriod()
This method determines whether the medium has been idle during a period (of non-null duration) immedi...
void DisableEdcaFor(Ptr< Txop > qosTxop, Time duration)
void DoInitialize() override
Initialize() implementation.
Txops m_txops
the vector of managed Txops
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.
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.
PhyListener * GetPhyListener(Ptr< WifiPhy > phy) const
Get current registered listener for PHY events on the given PHY.
Time m_lastUsingOtherEmlsrLinkEnd
the last time we were blocked because using another EMLSR link
PhyListenerMap m_phyListeners
the PHY listeners
virtual Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
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.
bool m_active
whether this PHY listener is active
PhyListener(ns3::ChannelAccessManager *cam)
Create a PhyListener for the given ChannelAccessManager.
void NotifyOff() override
Notify listeners that we went to switch off.
void NotifySleep() override
Notify listeners that we went to sleep.
ns3::ChannelAccessManager * m_cam
ChannelAccessManager to forward events to.
void 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 SetActive(bool active)
Set this listener to be active or not.
void NotifyRxEndOk() override
We have received the last bit of a packet for which NotifyRxStart was invoked first and,...
void NotifyWakeup() override
Notify listeners that we woke up.
void NotifyCcaBusyStart(Time duration, WifiChannelListType channelType, const std::vector< Time > &per20MhzDurations) override
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:558
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:415
@ US
microsecond
Definition: nstime.h:118
@ S
second
Definition: nstime.h:116
@ GRANTED
Definition: txop.h:103
@ NOT_REQUESTED
Definition: txop.h:101
@ REQUESTED
Definition: txop.h:102
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:1893
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.
Class that keeps track of all information about the current PHY operating channel.
bool IsOfdm() const
Return whether the operating channel is an OFDM channel.
#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:1349
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1325
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.