A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
minstrel-ht-wifi-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2009 Duy Nguyen
3 * Copyright (c) 2015 Ghada Badawy
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 * Authors: Duy Nguyen <duy@soe.ucsc.edu>
8 * Ghada Badawy <gbadawy@gmail.com>
9 * Matias Richart <mrichart@fing.edu.uy>
10 *
11 * Some Comments:
12 *
13 * 1) By default, Minstrel applies the multi-rate retry (the core of Minstrel
14 * algorithm). Otherwise, please use ConstantRateWifiManager instead.
15 *
16 * 2) Sampling is done differently from legacy Minstrel. Minstrel-HT tries
17 * to sample all rates in all groups at least once and to avoid many
18 * consecutive samplings.
19 *
20 * 3) Sample rate is tried only once, at first place of the MRR chain.
21 *
22 * reference: http://lwn.net/Articles/376765/
23 */
24
26
27#include "ns3/ht-configuration.h"
28#include "ns3/log.h"
29#include "ns3/packet.h"
30#include "ns3/random-variable-stream.h"
31#include "ns3/simulator.h"
32#include "ns3/wifi-mac.h"
33#include "ns3/wifi-net-device.h"
34#include "ns3/wifi-phy.h"
35#include "ns3/wifi-psdu.h"
36
37#include <algorithm>
38#include <iomanip>
39
40#define Min(a, b) ((a < b) ? a : b)
41#define Max(a, b) ((a > b) ? a : b)
42
43NS_LOG_COMPONENT_DEFINE("MinstrelHtWifiManager");
44
45namespace ns3
46{
47
48/**
49 * @brief standard information for each modulation class
50 */
51const std::map<WifiModulationClass, MinstrelHtWifiManager::StandardInfo> minstrelHtStandardInfos{
52 {
54 {
55 .groupType = WIFI_MINSTREL_GROUP_HT,
56 .maxMcs = 7,
57 .maxWidth = MHz_u{40},
58 .guardIntervals = {NanoSeconds(800), NanoSeconds(400)},
59 .maxStreams = 4,
60 },
61 },
62 {
64 {
65 .groupType = WIFI_MINSTREL_GROUP_VHT,
66 .maxMcs = 9,
67 .maxWidth = MHz_u{160},
68 .guardIntervals = {NanoSeconds(800), NanoSeconds(400)},
69 .maxStreams = 8,
70 },
71 },
72 {
74 {
75 .groupType = WIFI_MINSTREL_GROUP_HE,
76 .maxMcs = 11,
77 .maxWidth = MHz_u{160},
78 .guardIntervals = {NanoSeconds(3200), NanoSeconds(1600), NanoSeconds(800)},
79 .maxStreams = 8,
80 },
81 },
82};
83
84/// MinstrelHtWifiRemoteStation structure
86{
87 uint8_t m_sampleGroup; //!< The group that the sample rate belongs to.
88
89 uint32_t m_sampleWait; //!< How many transmission attempts to wait until a new sample.
90 uint32_t m_sampleTries; //!< Number of sample tries after waiting sampleWait.
91 uint32_t m_sampleCount; //!< Max number of samples per update interval.
92 uint32_t m_numSamplesSlow; //!< Number of times a slow rate was sampled.
93
94 uint32_t m_avgAmpduLen; //!< Average number of MPDUs in an A-MPDU.
95 uint32_t m_ampduLen; //!< Number of MPDUs in an A-MPDU.
96 uint32_t m_ampduPacketCount; //!< Number of A-MPDUs transmitted.
97
98 McsGroupData m_groupsTable; //!< Table of groups with stats.
99 bool m_isHt; //!< If the station is HT capable.
100
101 std::ofstream m_statsFile; //!< File where statistics table is written.
102};
103
105
106TypeId
108{
109 static TypeId tid =
110 TypeId("ns3::MinstrelHtWifiManager")
112 .AddConstructor<MinstrelHtWifiManager>()
113 .SetGroupName("Wifi")
114 .AddAttribute("UpdateStatistics",
115 "The interval between updating statistics table",
119 .AddAttribute("LegacyUpdateStatistics",
120 "The interval between updating statistics table (for legacy Minstrel)",
124 .AddAttribute("LookAroundRate",
125 "The percentage to try other rates (for legacy Minstrel)",
126 UintegerValue(10),
129 .AddAttribute("EWMA",
130 "EWMA level",
131 UintegerValue(75),
134 .AddAttribute("SampleColumn",
135 "The number of columns used for sampling",
136 UintegerValue(10),
139 .AddAttribute("PacketLength",
140 "The packet length used for calculating mode TxTime (bytes)",
141 UintegerValue(1200),
144 .AddAttribute("UseLatestAmendmentOnly",
145 "Use only the latest amendment when it is supported by both peers",
146 BooleanValue(true),
149 .AddAttribute("PrintStats",
150 "Control the printing of the statistics table",
151 BooleanValue(false),
154 .AddTraceSource("Rate",
155 "Traced value for rate changes (b/s)",
157 "ns3::TracedValueCallback::Uint64");
158 return tid;
159}
160
162 : m_numGroups(0),
163 m_numRates(0),
164 m_currentRate(0)
165{
166 NS_LOG_FUNCTION(this);
168 /**
169 * Create the legacy Minstrel manager in case HT is not supported by the device
170 * or non-HT stations want to associate.
171 */
173}
174
176{
177 NS_LOG_FUNCTION(this);
178 for (std::size_t i = 0; i < m_numGroups; i++)
179 {
180 m_minstrelGroups[i].ratesFirstMpduTxTimeTable.clear();
181 m_minstrelGroups[i].ratesTxTimeTable.clear();
182 }
183}
184
185int64_t
187{
188 NS_LOG_FUNCTION(this << stream);
189 int64_t numStreamsAssigned = 0;
190 m_uniformRandomVariable->SetStream(stream);
191 numStreamsAssigned++;
192 numStreamsAssigned += m_legacyManager->AssignStreams(stream);
193 return numStreamsAssigned;
194}
195
196void
198{
199 NS_LOG_FUNCTION(this << phy);
200 // Setup PHY for legacy manager.
201 m_legacyManager->SetupPhy(phy);
203}
204
205void
207{
208 NS_LOG_FUNCTION(this << mac);
209 m_legacyManager->SetupMac(mac);
211}
212
213void
215{
216 const auto& standardInfos = minstrelHtStandardInfos.at(mc);
217 for (MHz_u chWidth{20}; chWidth <= standardInfos.maxWidth; chWidth *= 2)
218 {
219 for (const auto& guardInterval : standardInfos.guardIntervals)
220 {
221 for (uint8_t streams = 1; streams <= standardInfos.maxStreams; ++streams)
222 {
223 const auto groupId =
224 GetGroupIdForType(standardInfos.groupType, streams, guardInterval, chWidth);
225
226 m_minstrelGroups[groupId].streams = streams;
227 m_minstrelGroups[groupId].gi = guardInterval;
228 m_minstrelGroups[groupId].chWidth = chWidth;
229 m_minstrelGroups[groupId].type = standardInfos.groupType;
230 m_minstrelGroups[groupId].isSupported = false;
231
232 // Check capabilities of the device
233 if ((((mc >= WIFI_MOD_CLASS_HE) &&
234 (GetGuardInterval() <=
235 guardInterval)) || /// Is GI supported by the transmitter?
236 ((mc < WIFI_MOD_CLASS_HE) &&
238 (guardInterval.GetNanoSeconds() ==
239 400)))) /// Is SGI supported by the transmitter?
240 && (GetPhy()->GetChannelWidth() >=
241 chWidth) /// Is channel width supported by the transmitter?
242 && (GetPhy()->GetMaxSupportedTxSpatialStreams() >=
243 streams)) /// Are streams supported by the transmitter?
244 {
245 m_minstrelGroups[groupId].isSupported = true;
246
247 // Calculate TX time for all rates of the group
248 WifiModeList mcsList = GetDeviceMcsList(mc);
249 for (uint8_t i = 0; i <= standardInfos.maxMcs; ++i)
250 {
251 const auto mcsIndex = (mc == WIFI_MOD_CLASS_HT)
252 ? (i + (m_minstrelGroups[groupId].streams - 1) *
253 (standardInfos.maxMcs + 1))
254 : i;
255 WifiMode mode = mcsList.at(mcsIndex);
256 // Check for invalid MCSs and do not add time to array.
257 if (IsValidMcs(streams, chWidth, mode))
258 {
259 AddFirstMpduTxTime(groupId,
260 mode,
262 guardInterval,
263 chWidth,
264 mode,
266 AddMpduTxTime(groupId,
267 mode,
269 guardInterval,
270 chWidth,
271 mode,
273 }
274 }
275 NS_LOG_DEBUG("Initialized group " << groupId << ": (" << +streams << ","
276 << guardInterval << "," << chWidth << ")");
277 }
278 }
279 }
280 }
281}
282
283void
285{
286 NS_LOG_FUNCTION(this);
287 /**
288 * Here we initialize m_minstrelGroups with all the possible groups.
289 * If a group is not supported by the device, then it is marked as not supported.
290 * Then, after all initializations are finished, we check actual support for each receiving
291 * station.
292 */
293
294 m_numGroups = 0;
295 if (GetHtSupported())
296 {
299 }
300 if (GetVhtSupported())
301 {
304 }
305 if (GetHeSupported())
306 {
309 }
310
311 /**
312 * Initialize the groups array.
313 * The HT groups come first, then the VHT ones, and finally the HE ones.
314 * Minstrel maintains different types of indexes:
315 * - A global continuous index, which identifies all rates within all groups, in [0,
316 * m_numGroups * m_numRates]
317 * - A groupId, which indexes a group in the array, in [0, m_numGroups]
318 * - A rateId, which identifies a rate within a group, in [0, m_numRates]
319 * - A deviceIndex, which indexes a MCS in the PHY MCS array.
320 * - A mcsIndex, which indexes a MCS in the wifi-remote-station-manager supported MCSs
321 * array.
322 */
323 NS_LOG_DEBUG("Initialize MCS Groups:");
325
326 if (GetHtSupported())
327 {
328 // Initialize all HT groups
330 }
331
332 if (GetVhtSupported())
333 {
334 // Initialize all VHT groups
336 }
337
338 if (GetHeSupported())
339 {
340 // Initialize all HE groups
342 }
343}
344
345bool
346MinstrelHtWifiManager::IsValidMcs(uint8_t streams, MHz_u chWidth, WifiMode mode)
347{
348 NS_LOG_FUNCTION(this << streams << chWidth << mode);
349 WifiTxVector txvector;
350 txvector.SetNss(streams);
351 txvector.SetChannelWidth(chWidth);
352 txvector.SetMode(mode);
353 return txvector.IsValid();
354}
355
356bool
358{
359 for (const auto& [mc, infos] : minstrelHtStandardInfos)
360 {
361 if ((m_minstrelGroups[groupId].type == infos.groupType) &&
362 (mode.GetModulationClass() == mc) &&
363 IsValidMcs(m_minstrelGroups[groupId].streams, m_minstrelGroups[groupId].chWidth, mode))
364 {
365 if (mc == WIFI_MOD_CLASS_HT)
366 {
367 /// Check if the HT MCS corresponds to groups number of streams.
368 return ((mode.GetMcsValue() < (m_minstrelGroups[groupId].streams * 8)) &&
369 (mode.GetMcsValue() >= ((m_minstrelGroups[groupId].streams - 1) * 8)));
370 }
371 return true;
372 }
373 }
374 return false;
375}
376
377Time
379 Time gi,
380 MHz_u chWidth,
381 WifiMode mode,
382 MpduType mpduType)
383{
384 NS_LOG_FUNCTION(this << streams << gi << chWidth << mode << mpduType);
385 WifiTxVector txvector{};
386 txvector.SetNss(streams);
387 txvector.SetGuardInterval(gi);
388 txvector.SetChannelWidth(chWidth);
389 txvector.SetMode(mode);
390 txvector.SetPreambleType(GetPreambleForTransmission(mode.GetModulationClass()));
392 WifiPhy::GetPayloadDuration(m_frameLength, txvector, GetPhy()->GetPhyBand(), mpduType);
393}
394
395Time
397{
398 NS_LOG_FUNCTION(this << groupId << mode);
399 const auto it = m_minstrelGroups[groupId].ratesFirstMpduTxTimeTable.find(mode);
400 NS_ASSERT(it != m_minstrelGroups[groupId].ratesFirstMpduTxTimeTable.cend());
401 return it->second;
402}
403
404void
406{
407 NS_LOG_FUNCTION(this << groupId << mode << t);
408 m_minstrelGroups[groupId].ratesFirstMpduTxTimeTable.insert(std::make_pair(mode, t));
409}
410
411Time
412MinstrelHtWifiManager::GetMpduTxTime(std::size_t groupId, WifiMode mode) const
413{
414 NS_LOG_FUNCTION(this << groupId << mode);
415 auto it = m_minstrelGroups[groupId].ratesTxTimeTable.find(mode);
416 NS_ASSERT(it != m_minstrelGroups[groupId].ratesTxTimeTable.end());
417 return it->second;
418}
419
420void
422{
423 NS_LOG_FUNCTION(this << groupId << mode << t);
424 m_minstrelGroups[groupId].ratesTxTimeTable.insert(std::make_pair(mode, t));
425}
426
429{
430 NS_LOG_FUNCTION(this);
431 auto station = new MinstrelHtWifiRemoteStation();
432
433 // Initialize variables common to both stations.
434 station->m_nextStatsUpdate = Simulator::Now() + m_updateStats;
435 station->m_col = 0;
436 station->m_index = 0;
437 station->m_maxTpRate = 0;
438 station->m_maxTpRate2 = 0;
439 station->m_maxProbRate = 0;
440 station->m_nModes = 0;
441 station->m_totalPacketsCount = 0;
442 station->m_samplePacketsCount = 0;
443 station->m_isSampling = false;
444 station->m_sampleRate = 0;
445 station->m_sampleDeferred = false;
446 station->m_shortRetry = 0;
447 station->m_longRetry = 0;
448 station->m_txrate = 0;
449 station->m_initialized = false;
450
451 // Variables specific to HT station
452 station->m_sampleGroup = 0;
453 station->m_numSamplesSlow = 0;
454 station->m_sampleCount = 16;
455 station->m_sampleWait = 0;
456 station->m_sampleTries = 4;
457
458 station->m_avgAmpduLen = 1;
459 station->m_ampduLen = 0;
460 station->m_ampduPacketCount = 0;
461
462 // Use the variable in the station to indicate whether the device supports HT.
463 // When correct information available it will be checked.
464 station->m_isHt = static_cast<bool>(GetPhy()->GetDevice()->GetHtConfiguration());
465
466 return station;
467}
468
469void
471{
472 NS_LOG_FUNCTION(this << station);
473 // Note: we appear to be doing late initialization of the table
474 // to make sure that the set of supported rates has been initialized
475 // before we perform our own initialization.
476 if (!station->m_initialized)
477 {
478 /**
479 * Check if the station supports HT.
480 * Assume that if the device do not supports HT then
481 * the station will not support HT either.
482 * We save from using another check and variable.
483 */
485 {
486 NS_LOG_INFO("non-HT station " << station);
487 station->m_isHt = false;
488 // We will use non-HT minstrel for this station. Initialize the manager.
489 m_legacyManager->SetAttribute("UpdateStatistics", TimeValue(m_legacyUpdateStats));
490 m_legacyManager->SetAttribute("LookAroundRate", UintegerValue(m_lookAroundRate));
491 m_legacyManager->SetAttribute("EWMA", UintegerValue(m_ewmaLevel));
492 m_legacyManager->SetAttribute("SampleColumn", UintegerValue(m_nSampleCol));
493 m_legacyManager->SetAttribute("PacketLength", UintegerValue(m_frameLength));
494 m_legacyManager->SetAttribute("PrintStats", BooleanValue(m_printStats));
495 m_legacyManager->CheckInit(station);
496 }
497 else
498 {
499 NS_LOG_DEBUG("HT station " << station);
500 station->m_isHt = true;
501 station->m_nModes = GetNMcsSupported(station);
502 station->m_minstrelTable = MinstrelRate(station->m_nModes);
503 station->m_sampleTable = SampleRate(m_numRates, std::vector<uint8_t>(m_nSampleCol));
504 InitSampleTable(station);
505 RateInit(station);
506 station->m_initialized = true;
507 }
508 }
509}
510
511void
513{
514 NS_LOG_FUNCTION(this << st);
516 "DoReportRxOk m_txrate=" << static_cast<MinstrelHtWifiRemoteStation*>(st)->m_txrate);
517}
518
519void
521{
522 NS_LOG_FUNCTION(this << st);
523 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
524 CheckInit(station);
525 if (!station->m_initialized)
526 {
527 return;
528 }
529 NS_LOG_DEBUG("DoReportRtsFailed m_txrate = " << station->m_txrate);
530 station->m_shortRetry++;
531}
532
533void
535 double ctsSnr,
536 WifiMode ctsMode,
537 double rtsSnr)
538{
539 NS_LOG_FUNCTION(this << st);
540}
541
542void
544{
545 NS_LOG_FUNCTION(this << st);
546 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
547 NS_LOG_DEBUG("Final RTS failed");
548 CheckInit(station);
549 if (!station->m_initialized)
550 {
551 return;
552 }
553 UpdateRetry(station);
554}
555
556void
558{
559 NS_LOG_FUNCTION(this << st);
560 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
561
562 CheckInit(station);
563 if (!station->m_initialized)
564 {
565 return;
566 }
567
568 NS_LOG_DEBUG("DoReportDataFailed " << station << "\t rate " << station->m_txrate
569 << "\tlongRetry \t" << station->m_longRetry);
570
571 if (!station->m_isHt)
572 {
573 m_legacyManager->UpdateRate(station);
574 }
575 else if (station->m_longRetry < CountRetries(station))
576 {
577 const auto rateId = GetRateId(station->m_txrate);
578 const auto groupId = GetGroupId(station->m_txrate);
579 station->m_groupsTable[groupId]
580 .m_ratesTable[rateId]
581 .numRateAttempt++; // Increment the attempts counter for the rate used.
582 UpdateRate(station);
583 }
584}
585
586void
588 double ackSnr,
589 WifiMode ackMode,
590 double dataSnr,
591 MHz_u dataChannelWidth,
592 uint8_t dataNss)
593{
594 NS_LOG_FUNCTION(this << st << ackSnr << ackMode << dataSnr << dataChannelWidth << dataNss);
595 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
596
597 CheckInit(station);
598 if (!station->m_initialized)
599 {
600 return;
601 }
602
603 if (!station->m_isHt)
604 {
605 NS_LOG_DEBUG("DoReportDataOk m_txrate = "
606 << station->m_txrate
607 << ", attempt = " << station->m_minstrelTable[station->m_txrate].numRateAttempt
608 << ", success = " << station->m_minstrelTable[station->m_txrate].numRateSuccess
609 << " (before update).");
610
611 station->m_minstrelTable[station->m_txrate].numRateSuccess++;
612 station->m_minstrelTable[station->m_txrate].numRateAttempt++;
613
614 m_legacyManager->UpdatePacketCounters(station);
615
616 NS_LOG_DEBUG("DoReportDataOk m_txrate = "
617 << station->m_txrate
618 << ", attempt = " << station->m_minstrelTable[station->m_txrate].numRateAttempt
619 << ", success = " << station->m_minstrelTable[station->m_txrate].numRateSuccess
620 << " (after update).");
621
622 UpdateRetry(station);
623 m_legacyManager->UpdateStats(station);
624
625 if (station->m_nModes >= 1)
626 {
627 station->m_txrate = m_legacyManager->FindRate(station);
628 }
629 }
630 else
631 {
632 const auto rateId = GetRateId(station->m_txrate);
633 const auto groupId = GetGroupId(station->m_txrate);
634
636 "DoReportDataOk m_txrate = "
637 << station->m_txrate
638 << ", attempt = " << station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt
639 << ", success = " << station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess
640 << " (before update).");
641
642 station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess++;
643 station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt++;
644
645 UpdatePacketCounters(station, 1, 0);
646
648 "DoReportDataOk m_txrate = "
649 << station->m_txrate
650 << ", attempt = " << station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt
651 << ", success = " << station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess
652 << " (after update).");
653
654 station->m_isSampling = false;
655 station->m_sampleDeferred = false;
656
657 UpdateRetry(station);
658 if (Simulator::Now() >= station->m_nextStatsUpdate)
659 {
660 UpdateStats(station);
661 }
662
663 if (station->m_nModes >= 1)
664 {
665 station->m_txrate = FindRate(station);
666 }
667 }
668
669 NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
670}
671
672void
674{
675 NS_LOG_FUNCTION(this << st);
676 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
677
678 CheckInit(station);
679 if (!station->m_initialized)
680 {
681 return;
682 }
683
684 NS_LOG_DEBUG("DoReportFinalDataFailed - TxRate=" << station->m_txrate);
685
686 if (!station->m_isHt)
687 {
688 m_legacyManager->UpdatePacketCounters(station);
689
690 UpdateRetry(station);
691
692 m_legacyManager->UpdateStats(station);
693 if (station->m_nModes >= 1)
694 {
695 station->m_txrate = m_legacyManager->FindRate(station);
696 }
697 }
698 else
699 {
700 UpdatePacketCounters(station, 0, 1);
701
702 station->m_isSampling = false;
703 station->m_sampleDeferred = false;
704
705 UpdateRetry(station);
706 if (Simulator::Now() >= station->m_nextStatsUpdate)
707 {
708 UpdateStats(station);
709 }
710
711 if (station->m_nModes >= 1)
712 {
713 station->m_txrate = FindRate(station);
714 }
715 }
716 NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
717}
718
719void
721 uint16_t nSuccessfulMpdus,
722 uint16_t nFailedMpdus,
723 double rxSnr,
724 double dataSnr,
725 MHz_u dataChannelWidth,
726 uint8_t dataNss)
727{
728 NS_LOG_FUNCTION(this << st << nSuccessfulMpdus << nFailedMpdus << rxSnr << dataSnr
729 << dataChannelWidth << dataNss);
730 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
731
732 CheckInit(station);
733 if (!station->m_initialized)
734 {
735 return;
736 }
737
738 NS_ASSERT_MSG(station->m_isHt, "A-MPDU Tx Status called but this is a non-HT STA.");
739
740 NS_LOG_DEBUG("DoReportAmpduTxStatus. TxRate=" << station->m_txrate
741 << " SuccMpdus=" << nSuccessfulMpdus
742 << " FailedMpdus=" << nFailedMpdus);
743
744 station->m_ampduPacketCount++;
745 station->m_ampduLen += nSuccessfulMpdus + nFailedMpdus;
746
747 UpdatePacketCounters(station, nSuccessfulMpdus, nFailedMpdus);
748
749 const auto rateId = GetRateId(station->m_txrate);
750 const auto groupId = GetGroupId(station->m_txrate);
751 station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess += nSuccessfulMpdus;
752 station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt +=
753 nSuccessfulMpdus + nFailedMpdus;
754
755 if (nSuccessfulMpdus == 0 && station->m_longRetry < CountRetries(station))
756 {
757 // We do not receive a BlockAck. The entire AMPDU fail.
758 UpdateRate(station);
759 }
760 else
761 {
762 station->m_isSampling = false;
763 station->m_sampleDeferred = false;
764
765 UpdateRetry(station);
766 if (Simulator::Now() >= station->m_nextStatsUpdate)
767 {
768 UpdateStats(station);
769 }
770
771 if (station->m_nModes >= 1)
772 {
773 station->m_txrate = FindRate(station);
774 }
775 NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
776 }
777}
778
779void
781{
782 NS_LOG_FUNCTION(this << station);
783
784 /**
785 * Retry Chain table is implemented here.
786 *
787 * FIXME
788 * Currently, NS3 does not retransmit an entire A-MPDU when BACK is missing
789 * but retransmits each MPDU until MPDUs lifetime expires (or a BACK is received).
790 * Then, there is no way to control A-MPDU retries (no call to NeedDataRetransmission).
791 * So, it is possible that the A-MPDU keeps retrying after longRetry reaches its limit.
792 *
793 *
794 * Try | LOOKAROUND RATE | NORMAL RATE
795 * -------------------------------------------------------
796 * 1 | Random rate | Best throughput
797 * 2 | Next best throughput | Next best throughput
798 * 3 | Best probability | Best probability
799 *
800 * Note: For clarity, multiple blocks of if's and else's are used
801 * Following implementation in Linux, in MinstrelHT lowest base rate is not used.
802 * Explanation can be found here: http://marc.info/?l=linux-wireless&m=144602778611966&w=2
803 */
804
805 CheckInit(station);
806 if (!station->m_initialized)
807 {
808 return;
809 }
810 station->m_longRetry++;
811
812 /**
813 * Get the IDs for all rates.
814 */
815 const auto maxTpRateId = GetRateId(station->m_maxTpRate);
816 const auto maxTpGroupId = GetGroupId(station->m_maxTpRate);
817 const auto maxTp2RateId = GetRateId(station->m_maxTpRate2);
818 const auto maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
819 const auto maxProbRateId = GetRateId(station->m_maxProbRate);
820 const auto maxProbGroupId = GetGroupId(station->m_maxProbRate);
821
822 /// For normal rate, we're not currently sampling random rates.
823 if (!station->m_isSampling)
824 {
825 /// Use best throughput rate.
826 if (station->m_longRetry <
827 station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount)
828 {
829 NS_LOG_DEBUG("Not Sampling; use the same rate again");
830 station->m_txrate = station->m_maxTpRate; //!< There are still a few retries.
831 }
832
833 /// Use second best throughput rate.
834 else if (station->m_longRetry <
835 (station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount +
836 station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].retryCount))
837 {
838 NS_LOG_DEBUG("Not Sampling; use the Max TP2");
839 station->m_txrate = station->m_maxTpRate2;
840 }
841
842 /// Use best probability rate.
843 else if (station->m_longRetry <=
844 (station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount +
845 station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].retryCount +
846 station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount))
847 {
848 NS_LOG_DEBUG("Not Sampling; use Max Prob");
849 station->m_txrate = station->m_maxProbRate;
850 }
851 else
852 {
853 NS_FATAL_ERROR("Max retries reached and m_longRetry not cleared properly. longRetry= "
854 << station->m_longRetry);
855 }
856 }
857
858 /// We're currently sampling random rates.
859 else
860 {
861 /// Sample rate is used only once
862 /// Use the best rate.
863 if (station->m_longRetry <
864 1 + station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTp2RateId].retryCount)
865 {
866 NS_LOG_DEBUG("Sampling use the MaxTP rate");
867 station->m_txrate = station->m_maxTpRate2;
868 }
869
870 /// Use the best probability rate.
871 else if (station->m_longRetry <=
872 1 + station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTp2RateId].retryCount +
873 station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount)
874 {
875 NS_LOG_DEBUG("Sampling use the MaxProb rate");
876 station->m_txrate = station->m_maxProbRate;
877 }
878 else
879 {
880 NS_FATAL_ERROR("Max retries reached and m_longRetry not cleared properly. longRetry= "
881 << station->m_longRetry);
882 }
883 }
884 NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
885}
886
887void
889{
890 NS_LOG_FUNCTION(this << station);
891 station->m_shortRetry = 0;
892 station->m_longRetry = 0;
893}
894
895void
897 uint16_t nSuccessfulMpdus,
898 uint16_t nFailedMpdus)
899{
900 NS_LOG_FUNCTION(this << station << nSuccessfulMpdus << nFailedMpdus);
901
902 station->m_totalPacketsCount += nSuccessfulMpdus + nFailedMpdus;
903 if (station->m_isSampling)
904 {
905 station->m_samplePacketsCount += nSuccessfulMpdus + nFailedMpdus;
906 }
907 if (station->m_totalPacketsCount == ~0)
908 {
909 station->m_samplePacketsCount = 0;
910 station->m_totalPacketsCount = 0;
911 }
912
913 if (!station->m_sampleWait && !station->m_sampleTries && station->m_sampleCount > 0)
914 {
915 station->m_sampleWait = 16 + 2 * station->m_avgAmpduLen;
916 station->m_sampleTries = 1;
917 station->m_sampleCount--;
918 }
919}
920
921uint16_t
923{
924 NS_LOG_FUNCTION(this << txRate << allowedWidth);
925
926 auto groupId = GetGroupId(txRate);
927 McsGroup group = m_minstrelGroups[groupId];
928
929 if (group.chWidth <= allowedWidth)
930 {
931 NS_LOG_DEBUG("Channel width is not greater than allowed width, nothing to do");
932 return txRate;
933 }
934
935 NS_ASSERT(GetPhy()->GetDevice()->GetHtConfiguration() != nullptr);
936 NS_ASSERT(static_cast<uint16_t>(group.chWidth) % 20 == 0);
937 // try halving the channel width and check if the group with the same number of
938 // streams and same GI is supported, until either a supported group is found or
939 // the width becomes lower than 20 MHz
940 auto width = group.chWidth / 2;
941
942 while (width >= MHz_u{20})
943 {
944 if (width > allowedWidth)
945 {
946 width /= 2;
947 continue;
948 }
949 groupId = GetGroupIdForType(group.type, group.streams, group.gi, width);
950 group = m_minstrelGroups[groupId];
951 if (group.isSupported)
952 {
953 break;
954 }
955 width /= 2;
956 }
957
958 NS_ABORT_MSG_IF(width < MHz_u{20}, "No rate compatible with the allowed width found");
959
960 return GetIndex(groupId, GetRateId(txRate));
961}
962
965{
966 NS_LOG_FUNCTION(this << st << allowedWidth);
967 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
968
969 if (!station->m_initialized)
970 {
971 CheckInit(station);
972 }
973
974 if (!station->m_isHt)
975 {
976 WifiTxVector vector = m_legacyManager->GetDataTxVector(station);
977 uint64_t dataRate = vector.GetMode().GetDataRate(vector);
978 if (m_currentRate != dataRate && !station->m_isSampling)
979 {
980 NS_LOG_DEBUG("New datarate: " << dataRate);
981 m_currentRate = dataRate;
982 }
983 return vector;
984 }
985
986 station->m_txrate = UpdateRateAfterAllowedWidth(station->m_txrate, allowedWidth);
987 NS_LOG_DEBUG("DoGetDataMode m_txrate= " << station->m_txrate);
988
989 const auto rateId = GetRateId(station->m_txrate);
990 const auto groupId = GetGroupId(station->m_txrate);
991 const auto mcsIndex = station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex;
992
993 NS_LOG_DEBUG("DoGetDataMode rateId= " << rateId << " groupId= " << groupId
994 << " mode= " << GetMcsSupported(station, mcsIndex));
995
996 McsGroup group = m_minstrelGroups[groupId];
997
998 // Check consistency of rate selected.
999 if (((group.type >= WIFI_MINSTREL_GROUP_HE) && (group.gi < GetGuardInterval(station))) ||
1000 ((group.type < WIFI_MINSTREL_GROUP_HE) && (group.gi.GetNanoSeconds() == 400) &&
1001 !GetShortGuardIntervalSupported(station)) ||
1002 (group.chWidth > GetChannelWidth(station)) ||
1003 (group.streams > GetNumberOfSupportedStreams(station)))
1004 {
1005 NS_FATAL_ERROR("Inconsistent group selected. Group: ("
1006 << +group.streams << "," << group.gi << "," << group.chWidth << ")"
1007 << " Station capabilities: (" << GetNumberOfSupportedStreams(station) << ","
1008 << ((group.type >= WIFI_MINSTREL_GROUP_HE)
1009 ? GetGuardInterval(station)
1010 : NanoSeconds(GetShortGuardIntervalSupported(station) ? 400 : 800))
1011 << "," << GetChannelWidth(station) << ")");
1012 }
1013 WifiMode mode = GetMcsSupported(station, mcsIndex);
1014 WifiTxVector txVector{
1015 mode,
1018 group.gi,
1020 group.streams,
1021 GetNess(station),
1022 GetPhy()->GetTxBandwidth(mode, group.chWidth),
1023 GetAggregation(station) && !station->m_isSampling};
1024 uint64_t dataRate = mode.GetDataRate(txVector);
1025 if (m_currentRate != dataRate && !station->m_isSampling)
1026 {
1027 NS_LOG_DEBUG("New datarate: " << dataRate);
1028 m_currentRate = dataRate;
1029 }
1030 return txVector;
1031}
1032
1035{
1036 NS_LOG_FUNCTION(this << st);
1037 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
1038
1039 if (!station->m_initialized)
1040 {
1041 CheckInit(station);
1042 }
1043
1044 if (!station->m_isHt)
1045 {
1046 return m_legacyManager->GetRtsTxVector(station);
1047 }
1048 else
1049 {
1050 NS_LOG_DEBUG("DoGetRtsMode m_txrate=" << station->m_txrate);
1051
1052 /* RTS is sent in a non-HT frame. RTS with HT is not supported yet in NS3.
1053 * When supported, decision of using HT has to follow rules in Section 9.7.6 from
1054 * 802.11-2012. From Sec. 9.7.6.5: "A frame other than a BlockAckReq or BlockAck that is
1055 * carried in a non-HT PPDU shall be transmitted by the STA using a rate no higher than the
1056 * highest rate in the BSSBasicRateSet parameter that is less than or equal to the rate or
1057 * non-HT reference rate (see 9.7.9) of the previously transmitted frame that was
1058 * directed to the same receiving STA. If no rate in the BSSBasicRateSet parameter meets
1059 * these conditions, the control frame shall be transmitted at a rate no higher than the
1060 * highest mandatory rate of the attached PHY that is less than or equal to the rate
1061 * or non-HT reference rate (see 9.7.9) of the previously transmitted frame that was
1062 * directed to the same receiving STA."
1063 */
1064
1065 // As we are in Minstrel HT, assume the last rate was an HT rate.
1066 const auto rateId = GetRateId(station->m_txrate);
1067 const auto groupId = GetGroupId(station->m_txrate);
1068 const auto mcsIndex = station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex;
1069
1070 const auto lastRate = GetMcsSupported(station, mcsIndex);
1071 const auto lastDataRate = lastRate.GetNonHtReferenceRate();
1072 const auto nBasicRates = GetNBasicModes();
1073
1074 WifiMode rtsRate;
1075 bool rateFound = false;
1076
1077 for (uint8_t i = 0; i < nBasicRates; i++)
1078 {
1079 const auto rate = GetBasicMode(i).GetDataRate(MHz_u{20});
1080 if (rate <= lastDataRate)
1081 {
1082 rtsRate = GetBasicMode(i);
1083 rateFound = true;
1084 }
1085 }
1086
1087 if (!rateFound)
1088 {
1089 for (const auto& mode : GetPhy()->GetModeList())
1090 {
1091 const auto rate = mode.GetDataRate(MHz_u{20});
1092 if (rate <= lastDataRate)
1093 {
1094 rtsRate = mode;
1095 rateFound = true;
1096 }
1097 }
1098 }
1099
1100 NS_ASSERT(rateFound);
1101
1102 return WifiTxVector(
1103 rtsRate,
1106 NanoSeconds(800),
1107 1,
1108 1,
1109 0,
1110 GetPhy()->GetTxBandwidth(rtsRate, GetChannelWidth(station)),
1111 GetAggregation(station));
1112 }
1113}
1114
1115std::list<Ptr<WifiMpdu>>
1117{
1118 NS_LOG_FUNCTION(this << *psdu);
1119
1120 std::list<Ptr<WifiMpdu>> mpdusToDrop;
1121
1122 for (const auto& mpdu : *PeekPointer(psdu))
1123 {
1124 if (!DoNeedRetransmission(station,
1125 mpdu->GetPacket(),
1126 (mpdu->GetRetryCount() < GetMac()->GetFrameRetryLimit())))
1127 {
1128 // this MPDU needs to be dropped
1129 mpdusToDrop.push_back(mpdu);
1130 }
1131 }
1132
1133 return mpdusToDrop;
1134}
1135
1136bool
1138 Ptr<const Packet> packet,
1139 bool normally)
1140{
1141 NS_LOG_FUNCTION(this << st << packet << normally);
1142
1143 auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
1144
1145 CheckInit(station);
1146 if (!station->m_initialized)
1147 {
1148 return normally;
1149 }
1150
1151 uint32_t maxRetries;
1152
1153 if (!station->m_isHt)
1154 {
1155 maxRetries = m_legacyManager->CountRetries(station);
1156 }
1157 else
1158 {
1159 maxRetries = CountRetries(station);
1160 }
1161
1162 if (station->m_longRetry >= maxRetries)
1163 {
1164 NS_LOG_DEBUG("No re-transmission allowed. Retries: " << station->m_longRetry
1165 << " Max retries: " << maxRetries);
1166 return false;
1167 }
1168 else
1169 {
1170 NS_LOG_DEBUG("Re-transmit. Retries: " << station->m_longRetry
1171 << " Max retries: " << maxRetries);
1172 return true;
1173 }
1174}
1175
1178{
1179 const auto maxProbRateId = GetRateId(station->m_maxProbRate);
1180 const auto maxProbGroupId = GetGroupId(station->m_maxProbRate);
1181 const auto maxTpRateId = GetRateId(station->m_maxTpRate);
1182 const auto maxTpGroupId = GetGroupId(station->m_maxTpRate);
1183 const auto maxTp2RateId = GetRateId(station->m_maxTpRate2);
1184 const auto maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
1185
1186 if (!station->m_isSampling)
1187 {
1188 return station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount +
1189 station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].retryCount +
1190 station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount;
1191 }
1192 else
1193 {
1194 return 1 + station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTp2RateId].retryCount +
1195 station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount;
1196 }
1197}
1198
1199uint16_t
1201{
1202 NS_LOG_FUNCTION(this << station);
1203 const auto sampleGroup = station->m_sampleGroup;
1204 const auto index = station->m_groupsTable[sampleGroup].m_index;
1205 const auto col = station->m_groupsTable[sampleGroup].m_col;
1206 const auto sampleIndex = station->m_sampleTable[index][col];
1207 const auto rateIndex = GetIndex(sampleGroup, sampleIndex);
1208 NS_LOG_DEBUG("Next Sample is " << rateIndex);
1209 SetNextSample(station); // Calculate the next sample rate.
1210 return rateIndex;
1211}
1212
1213void
1215{
1216 NS_LOG_FUNCTION(this << station);
1217 do
1218 {
1219 station->m_sampleGroup++;
1220 station->m_sampleGroup %= m_numGroups;
1221 } while (!station->m_groupsTable[station->m_sampleGroup].m_supported);
1222
1223 station->m_groupsTable[station->m_sampleGroup].m_index++;
1224
1225 const auto sampleGroup = station->m_sampleGroup;
1226 auto index = station->m_groupsTable[station->m_sampleGroup].m_index;
1227 auto col = station->m_groupsTable[sampleGroup].m_col;
1228
1229 if (index >= m_numRates)
1230 {
1231 station->m_groupsTable[station->m_sampleGroup].m_index = 0;
1232 station->m_groupsTable[station->m_sampleGroup].m_col++;
1233 if (station->m_groupsTable[station->m_sampleGroup].m_col >= m_nSampleCol)
1234 {
1235 station->m_groupsTable[station->m_sampleGroup].m_col = 0;
1236 }
1237 index = station->m_groupsTable[station->m_sampleGroup].m_index;
1238 col = station->m_groupsTable[sampleGroup].m_col;
1239 }
1240 NS_LOG_DEBUG("New sample set: group= " << +sampleGroup
1241 << " index= " << +station->m_sampleTable[index][col]);
1242}
1243
1244uint16_t
1246{
1247 NS_LOG_FUNCTION(this << station);
1248 NS_LOG_DEBUG("FindRate packet=" << station->m_totalPacketsCount);
1249
1250 if ((station->m_samplePacketsCount + station->m_totalPacketsCount) == 0)
1251 {
1252 return station->m_maxTpRate;
1253 }
1254
1255 // If we have waited enough, then sample.
1256 if (station->m_sampleWait == 0 && station->m_sampleTries != 0)
1257 {
1258 // SAMPLING
1259 NS_LOG_DEBUG("Obtaining a sampling rate");
1260 /// Now go through the table and find an index rate.
1261 const auto sampleIdx = GetNextSample(station);
1262 NS_LOG_DEBUG("Sampling rate = " << sampleIdx);
1263
1264 // Evaluate if the sampling rate selected should be used.
1265 const auto sampleGroupId = GetGroupId(sampleIdx);
1266 const auto sampleRateId = GetRateId(sampleIdx);
1267
1268 // If the rate selected is not supported, then don't sample.
1269 if (station->m_groupsTable[sampleGroupId].m_supported &&
1270 station->m_groupsTable[sampleGroupId].m_ratesTable[sampleRateId].supported)
1271 {
1272 /**
1273 * Sampling might add some overhead to the frame.
1274 * Hence, don't use sampling for the currently used rates.
1275 *
1276 * Also do not sample if the probability is already higher than 95%
1277 * to avoid wasting airtime.
1278 */
1279 const auto sampleRateInfo =
1280 station->m_groupsTable[sampleGroupId].m_ratesTable[sampleRateId];
1281
1282 NS_LOG_DEBUG("Use sample rate? MaxTpRate= "
1283 << station->m_maxTpRate << " CurrentRate= " << station->m_txrate
1284 << " SampleRate= " << sampleIdx
1285 << " SampleProb= " << sampleRateInfo.ewmaProb);
1286
1287 if (sampleIdx != station->m_maxTpRate && sampleIdx != station->m_maxTpRate2 &&
1288 sampleIdx != station->m_maxProbRate && sampleRateInfo.ewmaProb <= 95)
1289 {
1290 /**
1291 * Make sure that lower rates get sampled only occasionally,
1292 * if the link is working perfectly.
1293 */
1294
1295 const auto maxTpGroupId = GetGroupId(station->m_maxTpRate);
1296 const auto maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
1297 const auto maxTp2RateId = GetRateId(station->m_maxTpRate2);
1298 const auto maxProbGroupId = GetGroupId(station->m_maxProbRate);
1299 const auto maxProbRateId = GetRateId(station->m_maxProbRate);
1300
1301 const auto maxTpStreams = m_minstrelGroups[maxTpGroupId].streams;
1302 const auto sampleStreams = m_minstrelGroups[sampleGroupId].streams;
1303
1304 const auto sampleDuration = sampleRateInfo.perfectTxTime;
1305 const auto maxTp2Duration =
1306 station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].perfectTxTime;
1307 const auto maxProbDuration = station->m_groupsTable[maxProbGroupId]
1308 .m_ratesTable[maxProbRateId]
1309 .perfectTxTime;
1310
1311 NS_LOG_DEBUG("Use sample rate? SampleDuration= "
1312 << sampleDuration << " maxTp2Duration= " << maxTp2Duration
1313 << " maxProbDuration= " << maxProbDuration << " sampleStreams= "
1314 << +sampleStreams << " maxTpStreams= " << +maxTpStreams);
1315 if (sampleDuration < maxTp2Duration ||
1316 (sampleStreams < maxTpStreams && sampleDuration < maxProbDuration))
1317 {
1318 /// Set flag that we are currently sampling.
1319 station->m_isSampling = true;
1320
1321 /// set the rate that we're currently sampling
1322 station->m_sampleRate = sampleIdx;
1323
1324 NS_LOG_DEBUG("FindRate "
1325 << "sampleRate=" << sampleIdx);
1326 station->m_sampleTries--;
1327 return sampleIdx;
1328 }
1329 else
1330 {
1331 station->m_numSamplesSlow++;
1332 if (sampleRateInfo.numSamplesSkipped >= 20 && station->m_numSamplesSlow <= 2)
1333 {
1334 /// Set flag that we are currently sampling.
1335 station->m_isSampling = true;
1336
1337 /// set the rate that we're currently sampling
1338 station->m_sampleRate = sampleIdx;
1339
1340 NS_LOG_DEBUG("FindRate "
1341 << "sampleRate=" << sampleIdx);
1342 station->m_sampleTries--;
1343 return sampleIdx;
1344 }
1345 }
1346 }
1347 }
1348 }
1349 if (station->m_sampleWait > 0)
1350 {
1351 station->m_sampleWait--;
1352 }
1353
1354 /// Continue using the best rate.
1355
1356 NS_LOG_DEBUG("FindRate "
1357 << "maxTpRrate=" << station->m_maxTpRate);
1358 return station->m_maxTpRate;
1359}
1360
1361void
1363{
1364 NS_LOG_FUNCTION(this << station);
1365
1367
1368 station->m_numSamplesSlow = 0;
1369 station->m_sampleCount = 0;
1370
1371 double tempProb;
1372
1373 if (station->m_ampduPacketCount > 0)
1374 {
1375 uint32_t newLen = station->m_ampduLen / station->m_ampduPacketCount;
1376 station->m_avgAmpduLen =
1377 (newLen * (100 - m_ewmaLevel) + (station->m_avgAmpduLen * m_ewmaLevel)) / 100;
1378 station->m_ampduLen = 0;
1379 station->m_ampduPacketCount = 0;
1380 }
1381
1382 /* Initialize global rate indexes */
1383 station->m_maxTpRate = GetLowestIndex(station);
1384 station->m_maxTpRate2 = GetLowestIndex(station);
1385 station->m_maxProbRate = GetLowestIndex(station);
1386
1387 /// Update throughput and EWMA for each rate inside each group.
1388 for (std::size_t j = 0; j < m_numGroups; j++)
1389 {
1390 if (station->m_groupsTable[j].m_supported)
1391 {
1392 station->m_sampleCount++;
1393
1394 /* (re)Initialize group rate indexes */
1395 station->m_groupsTable[j].m_maxTpRate = GetLowestIndex(station, j);
1396 station->m_groupsTable[j].m_maxTpRate2 = GetLowestIndex(station, j);
1397 station->m_groupsTable[j].m_maxProbRate = GetLowestIndex(station, j);
1398
1399 for (uint8_t i = 0; i < m_numRates; i++)
1400 {
1401 if (station->m_groupsTable[j].m_ratesTable[i].supported)
1402 {
1403 station->m_groupsTable[j].m_ratesTable[i].retryUpdated = false;
1404
1406 +i << " "
1407 << GetMcsSupported(station,
1408 station->m_groupsTable[j].m_ratesTable[i].mcsIndex)
1409 << "\t attempt="
1410 << station->m_groupsTable[j].m_ratesTable[i].numRateAttempt
1411 << "\t success="
1412 << station->m_groupsTable[j].m_ratesTable[i].numRateSuccess);
1413
1414 /// If we've attempted something.
1415 if (station->m_groupsTable[j].m_ratesTable[i].numRateAttempt > 0)
1416 {
1417 station->m_groupsTable[j].m_ratesTable[i].numSamplesSkipped = 0;
1418 /**
1419 * Calculate the probability of success.
1420 * Assume probability scales from 0 to 100.
1421 */
1422 tempProb =
1423 (100 * station->m_groupsTable[j].m_ratesTable[i].numRateSuccess) /
1424 station->m_groupsTable[j].m_ratesTable[i].numRateAttempt;
1425
1426 /// Bookkeeping.
1427 station->m_groupsTable[j].m_ratesTable[i].prob = tempProb;
1428
1429 if (station->m_groupsTable[j].m_ratesTable[i].successHist == 0)
1430 {
1431 station->m_groupsTable[j].m_ratesTable[i].ewmaProb = tempProb;
1432 }
1433 else
1434 {
1435 station->m_groupsTable[j].m_ratesTable[i].ewmsdProb =
1436 CalculateEwmsd(station->m_groupsTable[j].m_ratesTable[i].ewmsdProb,
1437 tempProb,
1438 station->m_groupsTable[j].m_ratesTable[i].ewmaProb,
1439 m_ewmaLevel);
1440 /// EWMA probability
1441 tempProb =
1442 (tempProb * (100 - m_ewmaLevel) +
1443 station->m_groupsTable[j].m_ratesTable[i].ewmaProb * m_ewmaLevel) /
1444 100;
1445 station->m_groupsTable[j].m_ratesTable[i].ewmaProb = tempProb;
1446 }
1447
1448 station->m_groupsTable[j].m_ratesTable[i].throughput =
1449 CalculateThroughput(station, j, i, tempProb);
1450
1451 station->m_groupsTable[j].m_ratesTable[i].successHist +=
1452 station->m_groupsTable[j].m_ratesTable[i].numRateSuccess;
1453 station->m_groupsTable[j].m_ratesTable[i].attemptHist +=
1454 station->m_groupsTable[j].m_ratesTable[i].numRateAttempt;
1455 }
1456 else
1457 {
1458 station->m_groupsTable[j].m_ratesTable[i].numSamplesSkipped++;
1459 }
1460
1461 /// Bookkeeping.
1462 station->m_groupsTable[j].m_ratesTable[i].prevNumRateSuccess =
1463 station->m_groupsTable[j].m_ratesTable[i].numRateSuccess;
1464 station->m_groupsTable[j].m_ratesTable[i].prevNumRateAttempt =
1465 station->m_groupsTable[j].m_ratesTable[i].numRateAttempt;
1466 station->m_groupsTable[j].m_ratesTable[i].numRateSuccess = 0;
1467 station->m_groupsTable[j].m_ratesTable[i].numRateAttempt = 0;
1468
1469 if (station->m_groupsTable[j].m_ratesTable[i].throughput != 0)
1470 {
1471 SetBestStationThRates(station, GetIndex(j, i));
1472 SetBestProbabilityRate(station, GetIndex(j, i));
1473 }
1474 }
1475 }
1476 }
1477 }
1478
1479 // Try to sample all available rates during each interval.
1480 station->m_sampleCount *= 8;
1481
1482 // Recalculate retries for the rates selected.
1483 CalculateRetransmits(station, station->m_maxTpRate);
1484 CalculateRetransmits(station, station->m_maxTpRate2);
1485 CalculateRetransmits(station, station->m_maxProbRate);
1486
1487 NS_LOG_DEBUG("max tp=" << station->m_maxTpRate << "\nmax tp2=" << station->m_maxTpRate2
1488 << "\nmax prob=" << station->m_maxProbRate);
1489 if (m_printStats)
1490 {
1491 PrintTable(station);
1492 }
1493}
1494
1495double
1497 std::size_t groupId,
1498 uint8_t rateId,
1499 double ewmaProb)
1500{
1501 /**
1502 * Calculating throughput.
1503 * Do not account throughput if probability of success is below 10%
1504 * (as done in minstrel_ht linux implementation).
1505 */
1506 if (ewmaProb < 10)
1507 {
1508 return 0;
1509 }
1510 else
1511 {
1512 /**
1513 * For the throughput calculation, limit the probability value to 90% to
1514 * account for collision related packet error rate fluctuation.
1515 */
1516 const auto txTime = station->m_groupsTable[groupId].m_ratesTable[rateId].perfectTxTime;
1517 if (ewmaProb > 90)
1518 {
1519 return 90 / txTime.GetSeconds();
1520 }
1521 else
1522 {
1523 return ewmaProb / txTime.GetSeconds();
1524 }
1525 }
1526}
1527
1528void
1530{
1531 GroupInfo* group;
1532 MinstrelHtRateInfo rate;
1533 std::size_t tmpGroupId;
1534 uint8_t tmpRateId;
1535 double tmpTh;
1536 double tmpProb;
1537 std::size_t groupId;
1538 uint8_t rateId;
1539 double currentTh;
1540 // maximum group probability (GP) variables
1541 std::size_t maxGPGroupId;
1542 uint8_t maxGPRateId;
1543 double maxGPTh;
1544
1545 groupId = GetGroupId(index);
1546 rateId = GetRateId(index);
1547 group = &station->m_groupsTable[groupId];
1548 rate = group->m_ratesTable[rateId];
1549
1550 tmpGroupId = GetGroupId(station->m_maxProbRate);
1551 tmpRateId = GetRateId(station->m_maxProbRate);
1552 tmpProb = station->m_groupsTable[tmpGroupId].m_ratesTable[tmpRateId].ewmaProb;
1553 tmpTh = station->m_groupsTable[tmpGroupId].m_ratesTable[tmpRateId].throughput;
1554
1555 if (rate.ewmaProb > 75)
1556 {
1557 currentTh = station->m_groupsTable[groupId].m_ratesTable[rateId].throughput;
1558 if (currentTh > tmpTh)
1559 {
1560 station->m_maxProbRate = index;
1561 }
1562
1563 maxGPGroupId = GetGroupId(group->m_maxProbRate);
1564 maxGPRateId = GetRateId(group->m_maxProbRate);
1565 maxGPTh = station->m_groupsTable[maxGPGroupId].m_ratesTable[maxGPRateId].throughput;
1566
1567 if (currentTh > maxGPTh)
1568 {
1569 group->m_maxProbRate = index;
1570 }
1571 }
1572 else
1573 {
1574 if (rate.ewmaProb > tmpProb)
1575 {
1576 station->m_maxProbRate = index;
1577 }
1578 maxGPRateId = GetRateId(group->m_maxProbRate);
1579 if (rate.ewmaProb > group->m_ratesTable[maxGPRateId].ewmaProb)
1580 {
1581 group->m_maxProbRate = index;
1582 }
1583 }
1584}
1585
1586/*
1587 * Find & sort topmost throughput rates
1588 *
1589 * If multiple rates provide equal throughput the sorting is based on their
1590 * current success probability. Higher success probability is preferred among
1591 * MCS groups.
1592 */
1593void
1595{
1596 std::size_t groupId;
1597 uint8_t rateId;
1598 double th;
1599 double prob;
1600 std::size_t maxTpGroupId;
1601 uint8_t maxTpRateId;
1602 std::size_t maxTp2GroupId;
1603 uint8_t maxTp2RateId;
1604 double maxTpTh;
1605 double maxTpProb;
1606 double maxTp2Th;
1607 double maxTp2Prob;
1608
1609 groupId = GetGroupId(index);
1610 rateId = GetRateId(index);
1611 prob = station->m_groupsTable[groupId].m_ratesTable[rateId].ewmaProb;
1612 th = station->m_groupsTable[groupId].m_ratesTable[rateId].throughput;
1613
1614 maxTpGroupId = GetGroupId(station->m_maxTpRate);
1615 maxTpRateId = GetRateId(station->m_maxTpRate);
1616 maxTpProb = station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].ewmaProb;
1617 maxTpTh = station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].throughput;
1618
1619 maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
1620 maxTp2RateId = GetRateId(station->m_maxTpRate2);
1621 maxTp2Prob = station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].ewmaProb;
1622 maxTp2Th = station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].throughput;
1623
1624 if (th > maxTpTh || (th == maxTpTh && prob > maxTpProb))
1625 {
1626 station->m_maxTpRate2 = station->m_maxTpRate;
1627 station->m_maxTpRate = index;
1628 }
1629 else if (th > maxTp2Th || (th == maxTp2Th && prob > maxTp2Prob))
1630 {
1631 station->m_maxTpRate2 = index;
1632 }
1633
1634 // Find best rates per group
1635
1636 GroupInfo* group = &station->m_groupsTable[groupId];
1637 maxTpGroupId = GetGroupId(group->m_maxTpRate);
1638 maxTpRateId = GetRateId(group->m_maxTpRate);
1639 maxTpProb = group->m_ratesTable[maxTpRateId].ewmaProb;
1640 maxTpTh = station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].throughput;
1641
1642 maxTp2GroupId = GetGroupId(group->m_maxTpRate2);
1643 maxTp2RateId = GetRateId(group->m_maxTpRate2);
1644 maxTp2Prob = group->m_ratesTable[maxTp2RateId].ewmaProb;
1645 maxTp2Th = station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].throughput;
1646
1647 if (th > maxTpTh || (th == maxTpTh && prob > maxTpProb))
1648 {
1649 group->m_maxTpRate2 = group->m_maxTpRate;
1650 group->m_maxTpRate = index;
1651 }
1652 else if (th > maxTp2Th || (th == maxTp2Th && prob > maxTp2Prob))
1653 {
1654 group->m_maxTpRate2 = index;
1655 }
1656}
1657
1658void
1660{
1661 NS_LOG_FUNCTION(this << station);
1662
1664
1665 /**
1666 * Initialize groups supported by the receiver.
1667 */
1668 NS_LOG_DEBUG("Supported groups by station:");
1669 bool noSupportedGroupFound = true;
1670 for (std::size_t groupId = 0; groupId < m_numGroups; groupId++)
1671 {
1672 if (m_minstrelGroups[groupId].isSupported)
1673 {
1674 station->m_groupsTable[groupId].m_supported = false;
1675
1676 if ((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_HE) &&
1677 !GetHeSupported(station))
1678 {
1679 // It is a HE group but the receiver does not support HE: skip
1680 continue;
1681 }
1682 if ((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_VHT) &&
1683 !GetVhtSupported(station))
1684 {
1685 // It is a VHT group but the receiver does not support VHT: skip
1686 continue;
1687 }
1688 if ((m_minstrelGroups[groupId].type != WIFI_MINSTREL_GROUP_HE) &&
1690 {
1691 // It is not a HE group and the receiver supports HE: skip since
1692 // UseLatestAmendmentOnly attribute is enabled
1693 continue;
1694 }
1695 if (!GetHeSupported(station) &&
1696 (m_minstrelGroups[groupId].type != WIFI_MINSTREL_GROUP_VHT) &&
1698 {
1699 // It is not a VHT group and the receiver supports VHT (but not HE): skip since
1700 // UseLatestAmendmentOnly attribute is enabled
1701 continue;
1702 }
1703 if ((m_minstrelGroups[groupId].type < WIFI_MINSTREL_GROUP_HE) &&
1704 (m_minstrelGroups[groupId].gi.GetNanoSeconds() == 400) &&
1706 {
1707 // It is a SGI group but the receiver does not support SGI: skip
1708 continue;
1709 }
1710 if ((m_minstrelGroups[groupId].type >= WIFI_MINSTREL_GROUP_HE) &&
1711 (m_minstrelGroups[groupId].gi < GetGuardInterval(station)))
1712 {
1713 // The receiver does not support the GI: skip
1714 continue;
1715 }
1716 if (GetChannelWidth(station) < m_minstrelGroups[groupId].chWidth)
1717 {
1718 // The receiver does not support the channel width: skip
1719 continue;
1720 }
1721 if (GetNumberOfSupportedStreams(station) < m_minstrelGroups[groupId].streams)
1722 {
1723 // The receiver does not support the number of spatial streams: skip
1724 continue;
1725 }
1726
1727 NS_LOG_DEBUG("Group: " << groupId << " type: " << m_minstrelGroups[groupId].type
1728 << " streams: " << +m_minstrelGroups[groupId].streams
1729 << " GI: " << m_minstrelGroups[groupId].gi
1730 << " width: " << m_minstrelGroups[groupId].chWidth);
1731
1732 noSupportedGroupFound = false;
1733 station->m_groupsTable[groupId].m_supported = true;
1734 station->m_groupsTable[groupId].m_col = 0;
1735 station->m_groupsTable[groupId].m_index = 0;
1736
1737 station->m_groupsTable[groupId].m_ratesTable =
1738 MinstrelHtRate(m_numRates); /// Create the rate list for the group.
1739 for (uint8_t i = 0; i < m_numRates; i++)
1740 {
1741 station->m_groupsTable[groupId].m_ratesTable[i].supported = false;
1742 }
1743
1744 // Initialize all modes supported by the remote station that belong to the current
1745 // group.
1746 for (uint8_t i = 0; i < station->m_nModes; i++)
1747 {
1748 if (const auto mode = GetMcsSupported(station, i);
1749 ShouldAddMcsToGroup(mode, groupId))
1750 {
1751 NS_LOG_DEBUG("Mode " << +i << ": " << mode);
1752
1753 /// Use the McsValue as the index in the rate table.
1754 /// This way, MCSs not supported are not initialized.
1755 auto rateId = mode.GetMcsValue();
1756 if (mode.GetModulationClass() == WIFI_MOD_CLASS_HT)
1757 {
1758 rateId %= (minstrelHtStandardInfos.at(WIFI_MOD_CLASS_HT).maxMcs + 1);
1759 }
1760
1761 station->m_groupsTable[groupId].m_ratesTable[rateId].supported = true;
1762 station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex =
1763 i; /// Mapping between rateId and operationalMcsSet
1764 station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt = 0;
1765 station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess = 0;
1766 station->m_groupsTable[groupId].m_ratesTable[rateId].prob = 0;
1767 station->m_groupsTable[groupId].m_ratesTable[rateId].ewmaProb = 0;
1768 station->m_groupsTable[groupId].m_ratesTable[rateId].prevNumRateAttempt = 0;
1769 station->m_groupsTable[groupId].m_ratesTable[rateId].prevNumRateSuccess = 0;
1770 station->m_groupsTable[groupId].m_ratesTable[rateId].numSamplesSkipped = 0;
1771 station->m_groupsTable[groupId].m_ratesTable[rateId].successHist = 0;
1772 station->m_groupsTable[groupId].m_ratesTable[rateId].attemptHist = 0;
1773 station->m_groupsTable[groupId].m_ratesTable[rateId].throughput = 0;
1774 station->m_groupsTable[groupId].m_ratesTable[rateId].perfectTxTime =
1775 GetFirstMpduTxTime(groupId, GetMcsSupported(station, i));
1776 station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount = 0;
1777 station->m_groupsTable[groupId].m_ratesTable[rateId].adjustedRetryCount = 0;
1778 CalculateRetransmits(station, groupId, rateId);
1779 }
1780 }
1781 }
1782 }
1783 /// make sure at least one group is supported, otherwise we end up with an infinite loop in
1784 /// SetNextSample
1785 if (noSupportedGroupFound)
1786 {
1787 NS_FATAL_ERROR("No supported group has been found");
1788 }
1789 SetNextSample(station); /// Select the initial sample index.
1790 UpdateStats(station); /// Calculate the initial high throughput rates.
1791 station->m_txrate = FindRate(station); /// Select the rate to use.
1792}
1793
1794void
1796{
1797 NS_LOG_FUNCTION(this << station << index);
1798 const auto groupId = GetGroupId(index);
1799 const auto rateId = GetRateId(index);
1800 if (!station->m_groupsTable[groupId].m_ratesTable[rateId].retryUpdated)
1801 {
1802 CalculateRetransmits(station, groupId, rateId);
1803 }
1804}
1805
1806void
1808 std::size_t groupId,
1809 uint8_t rateId)
1810{
1811 NS_LOG_FUNCTION(this << station << groupId << rateId);
1812
1813 uint32_t cw = 15; // Is an approximation.
1814 uint32_t cwMax = 1023;
1815 Time cwTime;
1816 Time txTime;
1817 Time dataTxTime;
1818 const auto slotTime = GetPhy()->GetSlot();
1819 const auto ackTime = GetPhy()->GetSifs() + GetPhy()->GetBlockAckTxTime();
1820
1821 if (station->m_groupsTable[groupId].m_ratesTable[rateId].ewmaProb < 1)
1822 {
1823 station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount = 1;
1824 }
1825 else
1826 {
1827 station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount = 2;
1828 station->m_groupsTable[groupId].m_ratesTable[rateId].retryUpdated = true;
1829
1830 dataTxTime =
1832 groupId,
1833 GetMcsSupported(station,
1834 station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex)) +
1836 groupId,
1837 GetMcsSupported(station,
1838 station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex)) *
1839 (station->m_avgAmpduLen - 1);
1840
1841 /* Contention time for first 2 tries */
1842 cwTime = (cw / 2) * slotTime;
1843 cw = Min((cw + 1) * 2, cwMax);
1844 cwTime += (cw / 2) * slotTime;
1845 cw = Min((cw + 1) * 2, cwMax);
1846
1847 /* Total TX time for data and Contention after first 2 tries */
1848 txTime = cwTime + 2 * (dataTxTime + ackTime);
1849
1850 /* See how many more tries we can fit inside segment size */
1851 do
1852 {
1853 /* Contention time for this try */
1854 cwTime = (cw / 2) * slotTime;
1855 cw = Min((cw + 1) * 2, cwMax);
1856
1857 /* Total TX time after this try */
1858 txTime += cwTime + ackTime + dataTxTime;
1859 } while ((txTime < MilliSeconds(6)) &&
1860 (++station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount < 7));
1861 }
1862}
1863
1864double
1866 double currentProb,
1867 double ewmaProb,
1868 double weight)
1869{
1870 double diff;
1871 double incr;
1872 double tmp;
1873
1874 /* calculate exponential weighted moving variance */
1875 diff = currentProb - ewmaProb;
1876 incr = (100 - weight) * diff / 100;
1877 tmp = oldEwmsd * oldEwmsd;
1878 tmp = weight * (tmp + diff * incr) / 100;
1879
1880 /* return standard deviation */
1881 return sqrt(tmp);
1882}
1883
1884void
1886{
1887 NS_LOG_FUNCTION(this << station);
1888 station->m_col = station->m_index = 0;
1889
1890 // for off-setting to make rates fall between 0 and nModes
1891 auto numSampleRates = m_numRates;
1892
1893 uint16_t newIndex;
1894 for (uint8_t col = 0; col < m_nSampleCol; col++)
1895 {
1896 for (uint8_t i = 0; i < numSampleRates; i++)
1897 {
1898 /**
1899 * The next two lines basically tries to generate a random number
1900 * between 0 and the number of available rates
1901 */
1902 int uv = m_uniformRandomVariable->GetInteger(0, numSampleRates);
1903 newIndex = (i + uv) % numSampleRates;
1904
1905 // this loop is used for filling in other uninitialized places
1906 while (station->m_sampleTable[newIndex][col] != 0)
1907 {
1908 newIndex = (newIndex + 1) % m_numRates;
1909 }
1910 station->m_sampleTable[newIndex][col] = i;
1911 }
1912 }
1913}
1914
1915void
1917{
1918 if (!station->m_statsFile.is_open())
1919 {
1920 std::ostringstream tmp;
1921 tmp << "minstrel-ht-stats-" << station->m_state->m_address << ".txt";
1922 station->m_statsFile.open(tmp.str(), std::ios::out);
1923 }
1924
1925 station->m_statsFile
1926 << " best ____________rate__________ ________statistics________ "
1927 "________last_______ ______sum-of________\n"
1928 << " mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] "
1929 "[prob.|retry|suc|att] [#success | #attempts]\n";
1930 for (std::size_t i = 0; i < m_numGroups; i++)
1931 {
1932 StatsDump(station, i, station->m_statsFile);
1933 }
1934
1935 station->m_statsFile << "\nTotal packet count:: ideal "
1936 << Max(0, station->m_totalPacketsCount - station->m_samplePacketsCount)
1937 << " lookaround " << station->m_samplePacketsCount << "\n";
1938 station->m_statsFile << "Average # of aggregated frames per A-MPDU: " << station->m_avgAmpduLen
1939 << "\n\n";
1940
1941 station->m_statsFile.flush();
1942}
1943
1944void
1946 std::size_t groupId,
1947 std::ofstream& of)
1948{
1949 auto numRates = m_numRates;
1950 McsGroup group = m_minstrelGroups[groupId];
1951 Time txTime;
1952 for (uint8_t i = 0; i < numRates; i++)
1953 {
1954 if (station->m_groupsTable[groupId].m_supported &&
1955 station->m_groupsTable[groupId].m_ratesTable[i].supported)
1956 {
1957 of << group.type << " " << group.chWidth << " " << group.gi << " " << +group.streams
1958 << " ";
1959
1960 const auto maxTpRate = station->m_maxTpRate;
1961 const auto maxTpRate2 = station->m_maxTpRate2;
1962 const auto maxProbRate = station->m_maxProbRate;
1963
1964 const auto idx = GetIndex(groupId, i);
1965 if (idx == maxTpRate)
1966 {
1967 of << 'A';
1968 }
1969 else
1970 {
1971 of << ' ';
1972 }
1973 if (idx == maxTpRate2)
1974 {
1975 of << 'B';
1976 }
1977 else
1978 {
1979 of << ' ';
1980 }
1981 if (idx == maxProbRate)
1982 {
1983 of << 'P';
1984 }
1985 else
1986 {
1987 of << ' ';
1988 }
1989
1990 if (group.type == WIFI_MINSTREL_GROUP_HT)
1991 {
1992 of << std::setw(4) << " MCS"
1993 << (group.streams - 1) *
1995 i;
1996 }
1997 else
1998 {
1999 of << std::setw(7) << " MCS" << +i << "/" << static_cast<int>(group.streams);
2000 }
2001
2002 of << " " << std::setw(3) << idx << " ";
2003
2004 /* tx_time[rate(i)] in usec */
2005 txTime = GetFirstMpduTxTime(
2006 groupId,
2007 GetMcsSupported(station, station->m_groupsTable[groupId].m_ratesTable[i].mcsIndex));
2008 of << std::setw(6) << txTime.GetMicroSeconds() << " ";
2009
2010 of << std::setw(7) << CalculateThroughput(station, groupId, i, 100) / 100 << " "
2011 << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].throughput / 100
2012 << " " << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].ewmaProb
2013 << " " << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].ewmsdProb
2014 << " " << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].prob
2015 << " " << std::setw(2) << station->m_groupsTable[groupId].m_ratesTable[i].retryCount
2016 << " " << std::setw(3)
2017 << station->m_groupsTable[groupId].m_ratesTable[i].prevNumRateSuccess << " "
2018 << std::setw(3) << station->m_groupsTable[groupId].m_ratesTable[i].prevNumRateAttempt
2019 << " " << std::setw(9)
2020 << station->m_groupsTable[groupId].m_ratesTable[i].successHist << " "
2021 << std::setw(9) << station->m_groupsTable[groupId].m_ratesTable[i].attemptHist
2022 << "\n";
2023 }
2024 }
2025}
2026
2027uint16_t
2028MinstrelHtWifiManager::GetIndex(std::size_t groupId, uint8_t rateId)
2029{
2030 NS_LOG_FUNCTION(this << groupId << rateId);
2031 return groupId * m_numRates + rateId;
2032}
2033
2034uint8_t
2036{
2037 NS_LOG_FUNCTION(this << index);
2038 return index % m_numRates;
2039}
2040
2041std::size_t
2043{
2044 NS_LOG_FUNCTION(this << index);
2045 return index / m_numRates;
2046}
2047
2048std::size_t
2050 uint8_t streams,
2051 Time guardInterval,
2052 MHz_u chWidth)
2053{
2054 NS_LOG_FUNCTION(this << mc << streams << guardInterval << chWidth);
2055 const auto& standardInfos = minstrelHtStandardInfos.at(mc);
2056 const auto it = std::find(standardInfos.guardIntervals.cbegin(),
2057 standardInfos.guardIntervals.cend(),
2058 guardInterval);
2059 const auto giIndex = std::distance(standardInfos.guardIntervals.cbegin(), it);
2060 const auto widthIndex = std::log2(chWidth / MHz_u{20});
2061 return (standardInfos.maxStreams * standardInfos.guardIntervals.size() * widthIndex) +
2062 (standardInfos.maxStreams * giIndex) + streams - 1;
2063}
2064
2065std::size_t
2067{
2068 const auto& standardInfos = minstrelHtStandardInfos.at(mc);
2069 const auto numWidths = std::log2(standardInfos.maxWidth / MHz_u{20}) + 1;
2070 return numWidths * standardInfos.guardIntervals.size() * standardInfos.maxStreams;
2071}
2072
2073std::size_t
2074MinstrelHtWifiManager::GetHtGroupId(uint8_t streams, Time guardInterval, MHz_u chWidth)
2075{
2076 NS_LOG_FUNCTION(this << streams << guardInterval << chWidth);
2077 return GetIdInGroup(WIFI_MOD_CLASS_HT, streams, guardInterval, chWidth);
2078}
2079
2080std::size_t
2081MinstrelHtWifiManager::GetVhtGroupId(uint8_t streams, Time guardInterval, MHz_u chWidth)
2082{
2083 NS_LOG_FUNCTION(this << streams << guardInterval << chWidth);
2084 const auto allHtGroups = GetNumGroups(WIFI_MOD_CLASS_HT);
2085 const auto vhtGroupId = GetIdInGroup(WIFI_MOD_CLASS_VHT, streams, guardInterval, chWidth);
2086 return allHtGroups + vhtGroupId;
2087}
2088
2089std::size_t
2090MinstrelHtWifiManager::GetHeGroupId(uint8_t streams, Time guardInterval, MHz_u chWidth)
2091{
2092 NS_LOG_FUNCTION(this << streams << guardInterval << chWidth);
2093 // This check is needed since HT is not supported in 6 GHz band
2094 std::size_t allHtGroups = (GetHtSupported()) ? GetNumGroups(WIFI_MOD_CLASS_HT) : 0;
2095 // This check is needed since VHT is not supported in 2.4 and 6 GHz bands
2096 std::size_t allVhtGroups = (GetVhtSupported()) ? GetNumGroups(WIFI_MOD_CLASS_VHT) : 0;
2097 const auto heGroupId = GetIdInGroup(WIFI_MOD_CLASS_HE, streams, guardInterval, chWidth);
2098 return allHtGroups + allVhtGroups + heGroupId;
2099}
2100
2101std::size_t
2103 uint8_t streams,
2104 Time guardInterval,
2105 MHz_u chWidth)
2106{
2107 switch (type)
2108 {
2110 return GetHtGroupId(streams, guardInterval, chWidth);
2112 return GetVhtGroupId(streams, guardInterval, chWidth);
2114 return GetHeGroupId(streams, guardInterval, chWidth);
2115 default:
2116 NS_ABORT_MSG("Unknown group type: " << type);
2117 }
2118 return 0;
2119}
2120
2121uint16_t
2123{
2124 NS_LOG_FUNCTION(this << station);
2125
2126 std::size_t groupId = 0;
2127 uint8_t rateId = 0;
2128 while (groupId < m_numGroups && !station->m_groupsTable[groupId].m_supported)
2129 {
2130 groupId++;
2131 }
2132 while (rateId < m_numRates && !station->m_groupsTable[groupId].m_ratesTable[rateId].supported)
2133 {
2134 rateId++;
2135 }
2136 NS_ASSERT(station->m_groupsTable[groupId].m_supported &&
2137 station->m_groupsTable[groupId].m_ratesTable[rateId].supported);
2138 return GetIndex(groupId, rateId);
2139}
2140
2141uint16_t
2143{
2144 NS_LOG_FUNCTION(this << station << groupId);
2145
2146 uint8_t rateId = 0;
2147 while (rateId < m_numRates && !station->m_groupsTable[groupId].m_ratesTable[rateId].supported)
2148 {
2149 rateId++;
2150 }
2151 NS_ASSERT(station->m_groupsTable[groupId].m_supported &&
2152 station->m_groupsTable[groupId].m_ratesTable[rateId].supported);
2153 return GetIndex(groupId, rateId);
2154}
2155
2158{
2159 const auto& phyMcsList = GetPhy()->GetMcsList(mc);
2160 WifiModeList mcsList(phyMcsList.cbegin(), phyMcsList.cend());
2161 return mcsList;
2162}
2163
2164} // namespace ns3
#define Max(a, b)
#define Min(a, b)
Implementation of Minstrel-HT Rate Control Algorithm.
static TypeId GetTypeId()
Get the type ID.
uint32_t CountRetries(MinstrelHtWifiRemoteStation *station)
Count retries.
void StatsDump(MinstrelHtWifiRemoteStation *station, std::size_t groupId, std::ofstream &of)
Print group statistics.
uint32_t m_frameLength
Frame length used to calculate modes TxTime in bytes.
void InitSampleTable(MinstrelHtWifiRemoteStation *station)
Initialize Sample Table.
bool m_printStats
If statistics table should be printed.
int64_t AssignStreams(int64_t stream) override
Assign a fixed random variable stream number to the random variables used by this model.
void DoReportRxOk(WifiRemoteStation *station, double rxSnr, WifiMode txMode) override
This method is a pure virtual method that must be implemented by the sub-class.
void DoReportDataOk(WifiRemoteStation *station, double ackSnr, WifiMode ackMode, double dataSnr, MHz_u dataChannelWidth, uint8_t dataNss) override
This method is a pure virtual method that must be implemented by the sub-class.
WifiTxVector DoGetRtsTxVector(WifiRemoteStation *station) override
Time GetFirstMpduTxTime(std::size_t groupId, WifiMode mode) const
Obtain the TxTime saved in the group information.
MinstrelMcsGroups m_minstrelGroups
Global array for groups information.
void SetNextSample(MinstrelHtWifiRemoteStation *station)
Set the next sample from Sample Table.
uint8_t m_numRates
Number of rates per group Minstrel should consider.
uint16_t UpdateRateAfterAllowedWidth(uint16_t txRate, MHz_u allowedWidth)
Given the index of the current TX rate, check whether the channel width is not greater than the given...
uint8_t m_nSampleCol
Number of sample columns.
std::list< Ptr< WifiMpdu > > DoGetMpdusToDropOnTxFailure(WifiRemoteStation *station, Ptr< WifiPsdu > psdu) override
Find the MPDUs to drop (possibly based on their frame retry count) in the given PSDU,...
void RateInit(MinstrelHtWifiRemoteStation *station)
Initialize Minstrel Table.
void SetBestStationThRates(MinstrelHtWifiRemoteStation *station, uint16_t index)
Set index rate as maxTpRate or maxTp2Rate if is better than current values.
void AddMpduTxTime(std::size_t groupId, WifiMode mode, Time t)
Save a TxTime to the vector of groups.
void PrintTable(MinstrelHtWifiRemoteStation *station)
Printing Minstrel Table.
std::size_t GetHeGroupId(uint8_t streams, Time guardInterval, MHz_u chWidth)
Returns the groupId of an HE MCS with the given number of streams, GI and channel width used.
double CalculateEwmsd(double oldEwmsd, double currentProb, double ewmaProb, double weight)
Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation.
void DoReportDataFailed(WifiRemoteStation *station) override
This method is a pure virtual method that must be implemented by the sub-class.
void SetBestProbabilityRate(MinstrelHtWifiRemoteStation *station, uint16_t index)
Set index rate as maxProbRate if it is better than current value.
void AddFirstMpduTxTime(std::size_t groupId, WifiMode mode, Time t)
Save a TxTime to the vector of groups.
double CalculateThroughput(MinstrelHtWifiRemoteStation *station, std::size_t groupId, uint8_t rateId, double ewmaProb)
Return the average throughput of the MCS defined by groupId and rateId.
Time m_updateStats
How frequent do we calculate the stats.
Time GetMpduTxTime(std::size_t groupId, WifiMode mode) const
Obtain the TxTime saved in the group information.
TracedValue< uint64_t > m_currentRate
Trace rate changes.
uint16_t GetLowestIndex(MinstrelHtWifiRemoteStation *station)
Returns the lowest global index of the rates supported by the station.
void DoInitialize() override
Initialize() implementation.
bool ShouldAddMcsToGroup(WifiMode mode, std::size_t groupId)
Check whether a given MCS mode should be added to a given group.
std::size_t GetGroupId(uint16_t index)
Return the groupId from the global index.
std::size_t GetIdInGroup(WifiModulationClass mc, uint8_t streams, Time guardInterval, MHz_u chWidth)
Returns the Id of a MCS of a given modulation class with the given number of streams,...
WifiTxVector DoGetDataTxVector(WifiRemoteStation *station, MHz_u allowedWidth) override
void CheckInit(MinstrelHtWifiRemoteStation *station)
Check for initializations.
bool DoNeedRetransmission(WifiRemoteStation *st, Ptr< const Packet > packet, bool normally)
std::size_t GetNumGroups(WifiModulationClass mc)
Returns the number of groups for a given modulation class.
void UpdateRetry(MinstrelHtWifiRemoteStation *station)
Update the number of retries and reset accordingly.
void CalculateRetransmits(MinstrelHtWifiRemoteStation *station, uint16_t index)
Calculate the number of retransmissions to set for the index rate.
void SetupPhy(const Ptr< WifiPhy > phy) override
Set up PHY associated with this device since it is the object that knows the full set of transmit rat...
std::size_t GetVhtGroupId(uint8_t streams, Time guardInterval, MHz_u chWidth)
Returns the groupId of a VHT MCS with the given number of streams, GI and channel width used.
bool IsValidMcs(uint8_t streams, MHz_u chWidth, WifiMode mode)
Check the validity of a combination of number of streams, chWidth and mode.
void InitializeGroups(WifiModulationClass mc)
Initialize all groups belonging to a given modulation class.
uint8_t m_ewmaLevel
Exponential weighted moving average level (or coefficient).
uint16_t FindRate(MinstrelHtWifiRemoteStation *station)
Find a rate to use from Minstrel Table.
uint8_t m_lookAroundRate
The % to try other rates than our current rate.
std::size_t GetHtGroupId(uint8_t streams, Time guardInterval, MHz_u chWidth)
Returns the groupId of an HT MCS with the given number of streams, GI and channel width used.
std::size_t m_numGroups
Number of groups Minstrel should consider.
Time CalculateMpduTxDuration(uint8_t streams, Time gi, MHz_u chWidth, WifiMode mode, MpduType mpduType)
Estimates the TxTime of a frame with a given mode and group (stream, guard interval and channel width...
void UpdateRate(MinstrelHtWifiRemoteStation *station)
Update rate.
uint8_t GetRateId(uint16_t index)
Return the rateId inside a group, from the global index.
Time m_legacyUpdateStats
How frequent do we calculate the stats for legacy MinstrelWifiManager.
WifiModeList GetDeviceMcsList(WifiModulationClass mc) const
Returns a list of only the MCS supported by the device for a given modulation class.
void DoReportRtsOk(WifiRemoteStation *station, double ctsSnr, WifiMode ctsMode, double rtsSnr) override
This method is a pure virtual method that must be implemented by the sub-class.
Ptr< UniformRandomVariable > m_uniformRandomVariable
Provides uniform random variables.
uint16_t GetNextSample(MinstrelHtWifiRemoteStation *station)
Getting the next sample from Sample Table.
uint16_t GetIndex(std::size_t groupId, uint8_t rateId)
Returns the global index corresponding to the groupId and rateId.
void UpdateStats(MinstrelHtWifiRemoteStation *station)
Update the Minstrel Table.
WifiRemoteStation * DoCreateStation() const override
Ptr< MinstrelWifiManager > m_legacyManager
Pointer to an instance of MinstrelWifiManager.
void SetupMac(const Ptr< WifiMac > mac) override
Set up MAC associated with this device since it is the object that knows the full set of timing param...
void DoReportFinalDataFailed(WifiRemoteStation *station) override
This method is a pure virtual method that must be implemented by the sub-class.
void UpdatePacketCounters(MinstrelHtWifiRemoteStation *station, uint16_t nSuccessfulMpdus, uint16_t nFailedMpdus)
Update the number of sample count variables.
void DoReportFinalRtsFailed(WifiRemoteStation *station) override
This method is a pure virtual method that must be implemented by the sub-class.
std::size_t GetGroupIdForType(McsGroupType type, uint8_t streams, Time guardInterval, MHz_u chWidth)
Returns the group ID of an MCS of a given group type with the given number of streams,...
bool m_useLatestAmendmentOnly
Flag if only the latest supported amendment by both peers should be used.
void DoReportRtsFailed(WifiRemoteStation *station) override
This method is a pure virtual method that must be implemented by the sub-class.
void DoReportAmpduTxStatus(WifiRemoteStation *station, uint16_t nSuccessfulMpdus, uint16_t nFailedMpdus, double rxSnr, double dataSnr, MHz_u dataChannelWidth, uint8_t dataNss) override
Typically called per A-MPDU, either when a Block ACK was successfully received or when a BlockAckTime...
Smart pointer class similar to boost::intrusive_ptr.
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
int64_t GetNanoSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:407
int64_t GetMicroSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:402
a unique identifier for an interface.
Definition type-id.h:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
represent a single transmission mode
Definition wifi-mode.h:40
WifiModulationClass GetModulationClass() const
Definition wifi-mode.cc:174
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
Definition wifi-mode.cc:111
uint8_t GetMcsValue() const
Definition wifi-mode.cc:152
Time GetBlockAckTxTime() const
Return the estimated BlockAck TX time for this PHY.
Definition wifi-phy.cc:865
static Time GetPayloadDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, MpduType mpdutype=NORMAL_MPDU, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1540
Time GetSlot() const
Return the slot duration for this PHY.
Definition wifi-phy.cc:841
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition wifi-phy.cc:829
MHz_u GetTxBandwidth(WifiMode mode, MHz_u maxAllowedBandWidth=MHz_u{ std::numeric_limits< double >::max()}) const
Get the bandwidth for a transmission occurring on the current operating channel and using the given W...
Definition wifi-phy.cc:1123
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition wifi-phy.cc:647
std::list< WifiMode > GetMcsList() const
The WifiPhy::GetMcsList() method is used (e.g., by a WifiRemoteStationManager) to determine the set o...
Definition wifi-phy.cc:2131
std::list< WifiMode > GetModeList() const
The WifiPhy::GetModeList() method is used (e.g., by a WifiRemoteStationManager) to determine the set ...
Definition wifi-phy.cc:2082
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1580
hold a list of per-remote-station state.
uint8_t GetNumberOfSupportedStreams(Mac48Address address) const
Return the number of spatial streams supported by the station.
uint8_t GetNess(const WifiRemoteStation *station) const
uint8_t GetNBasicModes() const
Return the number of basic modes we support.
Time GetGuardInterval() const
Return the shortest supported HE guard interval duration.
Ptr< WifiPhy > GetPhy() const
Return the WifiPhy.
MHz_u GetChannelWidth(const WifiRemoteStation *station) const
Return the channel width supported by the station.
Ptr< const He6GhzBandCapabilities > GetStationHe6GhzCapabilities(const Mac48Address &from) const
Return the HE 6 GHz Band Capabilities sent by a remote station.
bool GetAggregation(const WifiRemoteStation *station) const
Return whether the given station supports A-MPDU.
bool GetHtSupported() const
Return whether the device has HT capability support enabled on the link this manager is associated wi...
Ptr< WifiMac > GetMac() const
Return the WifiMac.
uint8_t GetNMcsSupported(Mac48Address address) const
Return the number of MCS supported by the station.
WifiMode GetBasicMode(uint8_t i) const
Return a basic mode from the set of basic modes.
bool GetShortGuardIntervalSupported() const
Return whether the device has SGI support enabled.
virtual void SetupPhy(const Ptr< WifiPhy > phy)
Set up PHY associated with this device since it is the object that knows the full set of transmit rat...
WifiMode GetMcsSupported(const WifiRemoteStation *station, uint8_t i) const
Return the WifiMode supported by the specified station at the specified index.
bool GetVhtSupported() const
Return whether the device has VHT capability support enabled on the link this manager is associated w...
bool GetShortPreambleEnabled() const
Return whether the device uses short PHY preambles.
bool GetHeSupported() const
Return whether the device has HE capability support enabled.
virtual void SetupMac(const Ptr< WifiMac > mac)
Set up MAC associated with this device since it is the object that knows the full set of timing param...
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
bool IsValid(WifiPhyBand band=WIFI_PHY_BAND_UNSPECIFIED) const
The standard disallows certain combinations of WifiMode, number of spatial streams,...
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
void SetChannelWidth(MHz_u channelWidth)
Sets the selected channelWidth.
void SetMode(WifiMode mode)
Sets the selected payload transmission mode.
void SetNss(uint8_t nss)
Sets the number of Nss.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1380
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1356
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
WifiModulationClass
This enumeration defines the modulation classes per (Table 10-6 "Modulation classes"; IEEE 802....
MpduType
The type of an MPDU.
Definition wifi-types.h:41
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
@ WIFI_MOD_CLASS_HE
HE (Clause 27)
@ FIRST_MPDU_IN_AGGREGATE
The MPDU is the first aggregate in an A-MPDU with multiple MPDUs, but is not the last aggregate.
Definition wifi-types.h:48
@ MIDDLE_MPDU_IN_AGGREGATE
The MPDU is part of an A-MPDU with multiple MPDUs, but is neither the first nor the last aggregate.
Definition wifi-types.h:51
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
U * PeekPointer(const Ptr< U > &p)
Definition ptr.h:443
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
std::vector< RateInfo > MinstrelRate
Data structure for a Minstrel Rate table A vector of a struct RateInfo.
std::vector< McsGroup > MinstrelMcsGroups
Data structure for a table of group definitions.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition nstime.h:1432
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition uinteger.h:35
McsGroupType
Available MCS group types.
const std::map< WifiModulationClass, MinstrelHtWifiManager::StandardInfo > minstrelHtStandardInfos
standard information for each modulation class
double MHz_u
MHz weak type.
Definition wifi-units.h:31
std::vector< MinstrelHtRateInfo > MinstrelHtRate
Data structure for a Minstrel Rate table.
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition boolean.h:70
std::vector< WifiMode > WifiModeList
In various parts of the code, folk are interested in maintaining a list of transmission modes.
Definition wifi-mode.h:251
std::vector< std::vector< uint8_t > > SampleRate
Data structure for a Sample Rate table A vector of a vector uint8_t.
std::vector< GroupInfo > McsGroupData
Data structure for a table of groups.
WifiPreamble GetPreambleForTransmission(WifiModulationClass modulation, bool useShortPreamble)
Return the preamble to be used for the transmission.
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1452
A struct to contain information of a group.
MinstrelHtRate m_ratesTable
Information about rates of this group.
uint16_t m_maxTpRate2
The second max throughput rate of this group in bps.
uint16_t m_maxProbRate
The highest success probability rate of this group in bps.
uint16_t m_maxTpRate
The max throughput rate of this group in bps.
Data structure to contain the information that defines a group.
MHz_u chWidth
channel width
Time gi
guard interval duration
McsGroupType type
identifies the group,
bool isSupported
flag whether group is supported
uint8_t streams
number of spatial streams
A struct to contain all statistics information related to a data rate.
double ewmaProb
Exponential weighted moving average of probability.
MinstrelHtWifiRemoteStation structure.
McsGroupData m_groupsTable
Table of groups with stats.
uint32_t m_sampleCount
Max number of samples per update interval.
uint8_t m_sampleGroup
The group that the sample rate belongs to.
uint32_t m_ampduPacketCount
Number of A-MPDUs transmitted.
uint32_t m_numSamplesSlow
Number of times a slow rate was sampled.
uint32_t m_sampleTries
Number of sample tries after waiting sampleWait.
std::ofstream m_statsFile
File where statistics table is written.
uint32_t m_sampleWait
How many transmission attempts to wait until a new sample.
bool m_isHt
If the station is HT capable.
uint32_t m_avgAmpduLen
Average number of MPDUs in an A-MPDU.
uint32_t m_ampduLen
Number of MPDUs in an A-MPDU.
hold per-remote-station state for Minstrel Wifi manager.
uint16_t m_maxTpRate2
second highest throughput rate in bps
Time m_nextStatsUpdate
10 times every second
bool m_initialized
for initializing tables
uint16_t m_sampleRate
current sample rate in bps
uint16_t m_txrate
current transmit rate in bps
int m_totalPacketsCount
total number of packets as of now
bool m_isSampling
a flag to indicate we are currently sampling
MinstrelRate m_minstrelTable
minstrel table
uint32_t m_shortRetry
short retries such as control packets
uint16_t m_maxTpRate
the current throughput rate in bps
uint8_t m_nModes
number of modes supported
int m_samplePacketsCount
how many packets we have sample so far
uint8_t m_col
To keep track of the current position in the our random sample table going row by row from 1st column...
uint32_t m_longRetry
long retries such as data packets
uint16_t m_maxProbRate
rate with highest probability of success in bps
hold per-remote-station state.
WifiRemoteStationState * m_state
Remote station state.
Mac48Address m_address
Mac48Address of the remote station.
void CalculateThroughput()
Calculate the throughput.
Definition wifi-tcp.cc:51