A Discrete-Event Network Simulator
QKDNetSim v2.0 (NS-3 v3.41) @ (+)
API
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Authors: Duy Nguyen <duy@soe.ucsc.edu>
19  * Ghada Badawy <gbadawy@gmail.com>
20  * Matias Richart <mrichart@fing.edu.uy>
21  *
22  * Some Comments:
23  *
24  * 1) By default, Minstrel applies the multi-rate retry (the core of Minstrel
25  * algorithm). Otherwise, please use ConstantRateWifiManager instead.
26  *
27  * 2) Sampling is done differently from legacy Minstrel. Minstrel-HT tries
28  * to sample all rates in all groups at least once and to avoid many
29  * consecutive samplings.
30  *
31  * 3) Sample rate is tried only once, at first place of the MRR chain.
32  *
33  * reference: http://lwn.net/Articles/376765/
34  */
35 
37 
38 #include "ns3/log.h"
39 #include "ns3/packet.h"
40 #include "ns3/random-variable-stream.h"
41 #include "ns3/simulator.h"
42 #include "ns3/wifi-mac.h"
43 #include "ns3/wifi-phy.h"
44 
45 #include <iomanip>
46 
47 #define Min(a, b) ((a < b) ? a : b)
48 #define Max(a, b) ((a > b) ? a : b)
49 
50 NS_LOG_COMPONENT_DEFINE("MinstrelHtWifiManager");
51 
52 namespace ns3
53 {
54 
57 {
58  uint8_t m_sampleGroup;
59 
60  uint32_t m_sampleWait;
61  uint32_t m_sampleTries;
62  uint32_t m_sampleCount;
63  uint32_t m_numSamplesSlow;
64 
65  uint32_t m_avgAmpduLen;
66  uint32_t m_ampduLen;
67  uint32_t m_ampduPacketCount;
68 
70  bool m_isHt;
71 
72  std::ofstream m_statsFile;
73 };
74 
76 
77 TypeId
79 {
80  static TypeId tid =
81  TypeId("ns3::MinstrelHtWifiManager")
83  .AddConstructor<MinstrelHtWifiManager>()
84  .SetGroupName("Wifi")
85  .AddAttribute("UpdateStatistics",
86  "The interval between updating statistics table",
90  .AddAttribute("LegacyUpdateStatistics",
91  "The interval between updating statistics table (for legacy Minstrel)",
92  TimeValue(MilliSeconds(100)),
95  .AddAttribute("LookAroundRate",
96  "The percentage to try other rates (for legacy Minstrel)",
97  UintegerValue(10),
99  MakeUintegerChecker<uint8_t>(0, 100))
100  .AddAttribute("EWMA",
101  "EWMA level",
102  UintegerValue(75),
104  MakeUintegerChecker<uint8_t>(0, 100))
105  .AddAttribute("SampleColumn",
106  "The number of columns used for sampling",
107  UintegerValue(10),
109  MakeUintegerChecker<uint8_t>())
110  .AddAttribute("PacketLength",
111  "The packet length used for calculating mode TxTime (bytes)",
112  UintegerValue(1200),
114  MakeUintegerChecker<uint32_t>())
115  .AddAttribute("UseLatestAmendmentOnly",
116  "Use only the latest amendment when it is supported by both peers",
117  BooleanValue(true),
120  .AddAttribute("PrintStats",
121  "Control the printing of the statistics table",
122  BooleanValue(false),
125  .AddTraceSource("Rate",
126  "Traced value for rate changes (b/s)",
128  "ns3::TracedValueCallback::Uint64");
129  return tid;
130 }
131 
133  : m_numGroups(0),
134  m_numRates(0),
135  m_currentRate(0)
136 {
137  NS_LOG_FUNCTION(this);
138  m_uniformRandomVariable = CreateObject<UniformRandomVariable>();
143  m_legacyManager = CreateObject<MinstrelWifiManager>();
144 }
145 
147 {
148  NS_LOG_FUNCTION(this);
149  for (uint8_t i = 0; i < m_numGroups; i++)
150  {
151  m_minstrelGroups[i].ratesFirstMpduTxTimeTable.clear();
152  m_minstrelGroups[i].ratesTxTimeTable.clear();
153  }
154 }
155 
156 int64_t
158 {
159  NS_LOG_FUNCTION(this << stream);
160  int64_t numStreamsAssigned = 0;
162  numStreamsAssigned++;
163  numStreamsAssigned += m_legacyManager->AssignStreams(stream);
164  return numStreamsAssigned;
165 }
166 
167 void
169 {
170  NS_LOG_FUNCTION(this << phy);
171  // Setup PHY for legacy manager.
172  m_legacyManager->SetupPhy(phy);
174 }
175 
176 void
178 {
179  NS_LOG_FUNCTION(this << mac);
180  m_legacyManager->SetupMac(mac);
182 }
183 
184 void
186 {
187  NS_LOG_FUNCTION(this);
195  if (GetHtSupported())
196  {
199  if (GetVhtSupported())
200  {
203  }
204  if (GetHeSupported())
205  {
208  }
209 
222  NS_LOG_DEBUG("Initialize MCS Groups:");
224 
225  // Initialize all HT groups
226  for (uint16_t chWidth = 20; chWidth <= MAX_HT_WIDTH; chWidth *= 2)
227  {
228  for (int gi = 800; gi >= 400;)
229  {
230  for (uint8_t streams = 1; streams <= MAX_HT_SUPPORTED_STREAMS; streams++)
231  {
232  uint8_t groupId = GetHtGroupId(streams, gi, chWidth);
233 
234  m_minstrelGroups[groupId].streams = streams;
235  m_minstrelGroups[groupId].gi = gi;
236  m_minstrelGroups[groupId].chWidth = chWidth;
238  m_minstrelGroups[groupId].isSupported = false;
239 
240  // Check capabilities of the device
242  (gi == 400))
243  && (GetPhy()->GetChannelWidth() >=
244  chWidth)
245  && (GetPhy()->GetMaxSupportedTxSpatialStreams() >=
246  streams))
247  {
248  m_minstrelGroups[groupId].isSupported = true;
249 
250  // Calculate TX time for all rates of the group
251  WifiModeList htMcsList = GetHtDeviceMcsList();
252  for (uint8_t i = 0; i < MAX_HT_GROUP_RATES; i++)
253  {
254  uint16_t deviceIndex = i + (m_minstrelGroups[groupId].streams - 1) * 8;
255  WifiMode mode = htMcsList[deviceIndex];
256  AddFirstMpduTxTime(groupId,
257  mode,
259  streams,
260  gi,
261  chWidth,
262  mode,
264  AddMpduTxTime(groupId,
265  mode,
267  streams,
268  gi,
269  chWidth,
270  mode,
272  }
273  NS_LOG_DEBUG("Initialized group " << +groupId << ": (" << +streams << ","
274  << gi << "," << chWidth << ")");
275  }
276  }
277  gi /= 2;
278  }
279  }
280 
281  if (GetVhtSupported())
282  {
283  // Initialize all VHT groups
284  for (uint16_t chWidth = 20; chWidth <= MAX_VHT_WIDTH; chWidth *= 2)
285  {
286  for (int gi = 800; gi >= 400;)
287  {
288  for (uint8_t streams = 1; streams <= MAX_VHT_SUPPORTED_STREAMS; streams++)
289  {
290  uint8_t groupId = GetVhtGroupId(streams, gi, chWidth);
291 
292  m_minstrelGroups[groupId].streams = streams;
293  m_minstrelGroups[groupId].gi = gi;
294  m_minstrelGroups[groupId].chWidth = chWidth;
296  m_minstrelGroups[groupId].isSupported = false;
297 
298  // Check capabilities of the device
300  (gi == 400))
301  && (GetPhy()->GetChannelWidth() >=
302  chWidth)
303  && (GetPhy()->GetMaxSupportedTxSpatialStreams() >=
304  streams))
305  {
306  m_minstrelGroups[groupId].isSupported = true;
307 
308  // Calculate TX time for all rates of the group
309  WifiModeList vhtMcsList = GetVhtDeviceMcsList();
310  for (uint8_t i = 0; i < MAX_VHT_GROUP_RATES; i++)
311  {
312  WifiMode mode = vhtMcsList[i];
313  // Check for invalid VHT MCSs and do not add time to array.
314  if (IsValidMcs(GetPhy(), streams, chWidth, mode))
315  {
317  groupId,
318  mode,
320  streams,
321  gi,
322  chWidth,
323  mode,
326  groupId,
327  mode,
329  streams,
330  gi,
331  chWidth,
332  mode,
334  }
335  }
336  NS_LOG_DEBUG("Initialized group " << +groupId << ": (" << +streams
337  << "," << gi << "," << chWidth
338  << ")");
339  }
340  }
341  gi /= 2;
342  }
343  }
344  }
345 
346  if (GetHeSupported())
347  {
348  // Initialize all HE groups
349  for (uint16_t chWidth = 20; chWidth <= MAX_HE_WIDTH; chWidth *= 2)
350  {
351  for (int gi = 3200; gi >= 800;)
352  {
353  for (uint8_t streams = 1; streams <= MAX_HE_SUPPORTED_STREAMS; streams++)
354  {
355  uint8_t groupId = GetHeGroupId(streams, gi, chWidth);
356 
357  m_minstrelGroups[groupId].streams = streams;
358  m_minstrelGroups[groupId].gi = gi;
359  m_minstrelGroups[groupId].chWidth = chWidth;
361  m_minstrelGroups[groupId].isSupported = false;
362 
363  // Check capabilities of the device
364  if ((GetGuardInterval() <= gi)
365  && (GetPhy()->GetChannelWidth() >=
366  chWidth)
367  && (GetPhy()->GetMaxSupportedTxSpatialStreams() >=
368  streams))
369  {
370  m_minstrelGroups[groupId].isSupported = true;
371 
372  // Calculate tx time for all rates of the group
373  WifiModeList heMcsList = GetHeDeviceMcsList();
374  for (uint8_t i = 0; i < MAX_HE_GROUP_RATES; i++)
375  {
376  WifiMode mode = heMcsList.at(i);
377  // Check for invalid HE MCSs and do not add time to array.
378  if (IsValidMcs(GetPhy(), streams, chWidth, mode))
379  {
381  groupId,
382  mode,
384  streams,
385  gi,
386  chWidth,
387  mode,
390  groupId,
391  mode,
393  streams,
394  gi,
395  chWidth,
396  mode,
398  }
399  }
400  NS_LOG_DEBUG("Initialized group " << +groupId << ": (" << +streams
401  << "," << gi << "," << chWidth
402  << ")");
403  }
404  }
405  gi /= 2;
406  }
407  }
408  }
409  }
410 }
411 
412 bool
414  uint8_t streams,
415  uint16_t chWidth,
416  WifiMode mode)
417 {
418  NS_LOG_FUNCTION(this << phy << +streams << chWidth << mode);
419  WifiTxVector txvector;
420  txvector.SetNss(streams);
421  txvector.SetChannelWidth(chWidth);
422  txvector.SetMode(mode);
423  return txvector.IsValid();
424 }
425 
426 Time
428  uint8_t streams,
429  uint16_t gi,
430  uint16_t chWidth,
431  WifiMode mode,
432  MpduType mpduType)
433 {
434  NS_LOG_FUNCTION(this << phy << +streams << gi << chWidth << mode << mpduType);
435  WifiTxVector txvector;
436  txvector.SetNss(streams);
437  txvector.SetGuardInterval(gi);
438  txvector.SetChannelWidth(chWidth);
439  txvector.SetNess(0);
440  txvector.SetStbc(false);
441  txvector.SetMode(mode);
444  WifiPhy::GetPayloadDuration(m_frameLength, txvector, phy->GetPhyBand(), mpduType);
445 }
446 
447 Time
449 {
450  NS_LOG_FUNCTION(this << +groupId << mode);
451  auto it = m_minstrelGroups[groupId].ratesFirstMpduTxTimeTable.find(mode);
452  NS_ASSERT(it != m_minstrelGroups[groupId].ratesFirstMpduTxTimeTable.end());
453  return it->second;
454 }
455 
456 void
458 {
459  NS_LOG_FUNCTION(this << +groupId << mode << t);
460  m_minstrelGroups[groupId].ratesFirstMpduTxTimeTable.insert(std::make_pair(mode, t));
461 }
462 
463 Time
464 MinstrelHtWifiManager::GetMpduTxTime(uint8_t groupId, WifiMode mode) const
465 {
466  NS_LOG_FUNCTION(this << +groupId << mode);
467  auto it = m_minstrelGroups[groupId].ratesTxTimeTable.find(mode);
468  NS_ASSERT(it != m_minstrelGroups[groupId].ratesTxTimeTable.end());
469  return it->second;
470 }
471 
472 void
474 {
475  NS_LOG_FUNCTION(this << +groupId << mode << t);
476  m_minstrelGroups[groupId].ratesTxTimeTable.insert(std::make_pair(mode, t));
477 }
478 
481 {
482  NS_LOG_FUNCTION(this);
483  auto station = new MinstrelHtWifiRemoteStation();
484 
485  // Initialize variables common to both stations.
486  station->m_nextStatsUpdate = Simulator::Now() + m_updateStats;
487  station->m_col = 0;
488  station->m_index = 0;
489  station->m_maxTpRate = 0;
490  station->m_maxTpRate2 = 0;
491  station->m_maxProbRate = 0;
492  station->m_nModes = 0;
493  station->m_totalPacketsCount = 0;
494  station->m_samplePacketsCount = 0;
495  station->m_isSampling = false;
496  station->m_sampleRate = 0;
497  station->m_sampleDeferred = false;
498  station->m_shortRetry = 0;
499  station->m_longRetry = 0;
500  station->m_txrate = 0;
501  station->m_initialized = false;
502 
503  // Variables specific to HT station
504  station->m_sampleGroup = 0;
505  station->m_numSamplesSlow = 0;
506  station->m_sampleCount = 16;
507  station->m_sampleWait = 0;
508  station->m_sampleTries = 4;
509 
510  station->m_avgAmpduLen = 1;
511  station->m_ampduLen = 0;
512  station->m_ampduPacketCount = 0;
513 
514  // Use the variable in the station to indicate whether the device supports HT.
515  // When correct information available it will be checked.
516  station->m_isHt = GetHtSupported();
517 
518  return station;
519 }
520 
521 void
523 {
524  NS_LOG_FUNCTION(this << station);
525  // Note: we appear to be doing late initialization of the table
526  // to make sure that the set of supported rates has been initialized
527  // before we perform our own initialization.
528  if (!station->m_initialized)
529  {
536  if (!GetHtSupported(station))
537  {
538  NS_LOG_INFO("non-HT station " << station);
539  station->m_isHt = false;
540  // We will use non-HT minstrel for this station. Initialize the manager.
541  m_legacyManager->SetAttribute("UpdateStatistics", TimeValue(m_legacyUpdateStats));
542  m_legacyManager->SetAttribute("LookAroundRate", UintegerValue(m_lookAroundRate));
543  m_legacyManager->SetAttribute("EWMA", UintegerValue(m_ewmaLevel));
544  m_legacyManager->SetAttribute("SampleColumn", UintegerValue(m_nSampleCol));
545  m_legacyManager->SetAttribute("PacketLength", UintegerValue(m_frameLength));
546  m_legacyManager->SetAttribute("PrintStats", BooleanValue(m_printStats));
547  m_legacyManager->CheckInit(station);
548  }
549  else
550  {
551  NS_LOG_DEBUG("HT station " << station);
552  station->m_isHt = true;
553  station->m_nModes = GetNMcsSupported(station);
554  station->m_minstrelTable = MinstrelRate(station->m_nModes);
555  station->m_sampleTable = SampleRate(m_numRates, std::vector<uint8_t>(m_nSampleCol));
556  InitSampleTable(station);
557  RateInit(station);
558  station->m_initialized = true;
559  }
560  }
561 }
562 
563 void
565 {
566  NS_LOG_FUNCTION(this << st);
567  NS_LOG_DEBUG(
568  "DoReportRxOk m_txrate=" << static_cast<MinstrelHtWifiRemoteStation*>(st)->m_txrate);
569 }
570 
571 void
573 {
574  NS_LOG_FUNCTION(this << st);
575  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
576  CheckInit(station);
577  if (!station->m_initialized)
578  {
579  return;
580  }
581  NS_LOG_DEBUG("DoReportRtsFailed m_txrate = " << station->m_txrate);
582  station->m_shortRetry++;
583 }
584 
585 void
587  double ctsSnr,
588  WifiMode ctsMode,
589  double rtsSnr)
590 {
591  NS_LOG_FUNCTION(this << st);
592 }
593 
594 void
596 {
597  NS_LOG_FUNCTION(this << st);
598  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
599  NS_LOG_DEBUG("Final RTS failed");
600  CheckInit(station);
601  if (!station->m_initialized)
602  {
603  return;
604  }
605  UpdateRetry(station);
606 }
607 
608 void
610 {
611  NS_LOG_FUNCTION(this << st);
612  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
613 
614  CheckInit(station);
615  if (!station->m_initialized)
616  {
617  return;
618  }
619 
620  NS_LOG_DEBUG("DoReportDataFailed " << station << "\t rate " << station->m_txrate
621  << "\tlongRetry \t" << station->m_longRetry);
622 
623  if (!station->m_isHt)
624  {
625  m_legacyManager->UpdateRate(station);
626  }
627  else if (station->m_longRetry < CountRetries(station))
628  {
629  uint8_t rateId = GetRateId(station->m_txrate);
630  uint8_t groupId = GetGroupId(station->m_txrate);
631  station->m_groupsTable[groupId]
632  .m_ratesTable[rateId]
633  .numRateAttempt++; // Increment the attempts counter for the rate used.
634  UpdateRate(station);
635  }
636 }
637 
638 void
640  double ackSnr,
641  WifiMode ackMode,
642  double dataSnr,
643  uint16_t dataChannelWidth,
644  uint8_t dataNss)
645 {
646  NS_LOG_FUNCTION(this << st << ackSnr << ackMode << dataSnr << dataChannelWidth << +dataNss);
647  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
648 
649  CheckInit(station);
650  if (!station->m_initialized)
651  {
652  return;
653  }
654 
655  NS_LOG_DEBUG("DoReportDataOk m_txrate = "
656  << station->m_txrate
657  << ", attempt = " << station->m_minstrelTable[station->m_txrate].numRateAttempt
658  << ", success = " << station->m_minstrelTable[station->m_txrate].numRateSuccess
659  << " (before update).");
660 
661  if (!station->m_isHt)
662  {
663  station->m_minstrelTable[station->m_txrate].numRateSuccess++;
664  station->m_minstrelTable[station->m_txrate].numRateAttempt++;
665 
666  m_legacyManager->UpdatePacketCounters(station);
667 
668  NS_LOG_DEBUG("DoReportDataOk m_txrate = "
669  << station->m_txrate
670  << ", attempt = " << station->m_minstrelTable[station->m_txrate].numRateAttempt
671  << ", success = " << station->m_minstrelTable[station->m_txrate].numRateSuccess
672  << " (after update).");
673 
674  UpdateRetry(station);
675  m_legacyManager->UpdateStats(station);
676 
677  if (station->m_nModes >= 1)
678  {
679  station->m_txrate = m_legacyManager->FindRate(station);
680  }
681  }
682  else
683  {
684  uint8_t rateId = GetRateId(station->m_txrate);
685  uint8_t groupId = GetGroupId(station->m_txrate);
686  station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess++;
687  station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt++;
688 
689  UpdatePacketCounters(station, 1, 0);
690 
691  NS_LOG_DEBUG("DoReportDataOk m_txrate = "
692  << station->m_txrate
693  << ", attempt = " << station->m_minstrelTable[station->m_txrate].numRateAttempt
694  << ", success = " << station->m_minstrelTable[station->m_txrate].numRateSuccess
695  << " (after update).");
696 
697  station->m_isSampling = false;
698  station->m_sampleDeferred = false;
699 
700  UpdateRetry(station);
701  if (Simulator::Now() >= station->m_nextStatsUpdate)
702  {
703  UpdateStats(station);
704  }
705 
706  if (station->m_nModes >= 1)
707  {
708  station->m_txrate = FindRate(station);
709  }
710  }
711 
712  NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
713 }
714 
715 void
717 {
718  NS_LOG_FUNCTION(this << st);
719  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
720 
721  CheckInit(station);
722  if (!station->m_initialized)
723  {
724  return;
725  }
726 
727  NS_LOG_DEBUG("DoReportFinalDataFailed - TxRate=" << station->m_txrate);
728 
729  if (!station->m_isHt)
730  {
731  m_legacyManager->UpdatePacketCounters(station);
732 
733  UpdateRetry(station);
734 
735  m_legacyManager->UpdateStats(station);
736  if (station->m_nModes >= 1)
737  {
738  station->m_txrate = m_legacyManager->FindRate(station);
739  }
740  }
741  else
742  {
743  UpdatePacketCounters(station, 0, 1);
744 
745  station->m_isSampling = false;
746  station->m_sampleDeferred = false;
747 
748  UpdateRetry(station);
749  if (Simulator::Now() >= station->m_nextStatsUpdate)
750  {
751  UpdateStats(station);
752  }
753 
754  if (station->m_nModes >= 1)
755  {
756  station->m_txrate = FindRate(station);
757  }
758  }
759  NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
760 }
761 
762 void
764  uint16_t nSuccessfulMpdus,
765  uint16_t nFailedMpdus,
766  double rxSnr,
767  double dataSnr,
768  uint16_t dataChannelWidth,
769  uint8_t dataNss)
770 {
771  NS_LOG_FUNCTION(this << st << nSuccessfulMpdus << nFailedMpdus << rxSnr << dataSnr
772  << dataChannelWidth << +dataNss);
773  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
774 
775  CheckInit(station);
776  if (!station->m_initialized)
777  {
778  return;
779  }
780 
781  NS_ASSERT_MSG(station->m_isHt, "A-MPDU Tx Status called but this is a non-HT STA.");
782 
783  NS_LOG_DEBUG("DoReportAmpduTxStatus. TxRate=" << station->m_txrate
784  << " SuccMpdus=" << nSuccessfulMpdus
785  << " FailedMpdus=" << nFailedMpdus);
786 
787  station->m_ampduPacketCount++;
788  station->m_ampduLen += nSuccessfulMpdus + nFailedMpdus;
789 
790  UpdatePacketCounters(station, nSuccessfulMpdus, nFailedMpdus);
791 
792  uint8_t rateId = GetRateId(station->m_txrate);
793  uint8_t groupId = GetGroupId(station->m_txrate);
794  station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess += nSuccessfulMpdus;
795  station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt +=
796  nSuccessfulMpdus + nFailedMpdus;
797 
798  if (nSuccessfulMpdus == 0 && station->m_longRetry < CountRetries(station))
799  {
800  // We do not receive a BlockAck. The entire AMPDU fail.
801  UpdateRate(station);
802  }
803  else
804  {
805  station->m_isSampling = false;
806  station->m_sampleDeferred = false;
807 
808  UpdateRetry(station);
809  if (Simulator::Now() >= station->m_nextStatsUpdate)
810  {
811  UpdateStats(station);
812  }
813 
814  if (station->m_nModes >= 1)
815  {
816  station->m_txrate = FindRate(station);
817  }
818  NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
819  }
820 }
821 
822 void
824 {
825  NS_LOG_FUNCTION(this << station);
826 
848  CheckInit(station);
849  if (!station->m_initialized)
850  {
851  return;
852  }
853  station->m_longRetry++;
854 
858  uint8_t maxTpRateId = GetRateId(station->m_maxTpRate);
859  uint8_t maxTpGroupId = GetGroupId(station->m_maxTpRate);
860  uint8_t maxTp2RateId = GetRateId(station->m_maxTpRate2);
861  uint8_t maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
862  uint8_t maxProbRateId = GetRateId(station->m_maxProbRate);
863  uint8_t maxProbGroupId = GetGroupId(station->m_maxProbRate);
864 
866  if (!station->m_isSampling)
867  {
869  if (station->m_longRetry <
870  station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount)
871  {
872  NS_LOG_DEBUG("Not Sampling; use the same rate again");
873  station->m_txrate = station->m_maxTpRate;
874  }
875 
877  else if (station->m_longRetry <
878  (station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount +
879  station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].retryCount))
880  {
881  NS_LOG_DEBUG("Not Sampling; use the Max TP2");
882  station->m_txrate = station->m_maxTpRate2;
883  }
884 
886  else if (station->m_longRetry <=
887  (station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount +
888  station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].retryCount +
889  station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount))
890  {
891  NS_LOG_DEBUG("Not Sampling; use Max Prob");
892  station->m_txrate = station->m_maxProbRate;
893  }
894  else
895  {
896  NS_FATAL_ERROR("Max retries reached and m_longRetry not cleared properly. longRetry= "
897  << station->m_longRetry);
898  }
899  }
900 
902  else
903  {
906  if (station->m_longRetry <
907  1 + station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTp2RateId].retryCount)
908  {
909  NS_LOG_DEBUG("Sampling use the MaxTP rate");
910  station->m_txrate = station->m_maxTpRate2;
911  }
912 
914  else if (station->m_longRetry <=
915  1 + station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTp2RateId].retryCount +
916  station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount)
917  {
918  NS_LOG_DEBUG("Sampling use the MaxProb rate");
919  station->m_txrate = station->m_maxProbRate;
920  }
921  else
922  {
923  NS_FATAL_ERROR("Max retries reached and m_longRetry not cleared properly. longRetry= "
924  << station->m_longRetry);
925  }
926  }
927  NS_LOG_DEBUG("Next rate to use TxRate = " << station->m_txrate);
928 }
929 
930 void
932 {
933  NS_LOG_FUNCTION(this << station);
934  station->m_shortRetry = 0;
935  station->m_longRetry = 0;
936 }
937 
938 void
940  uint16_t nSuccessfulMpdus,
941  uint16_t nFailedMpdus)
942 {
943  NS_LOG_FUNCTION(this << station << nSuccessfulMpdus << nFailedMpdus);
944 
945  station->m_totalPacketsCount += nSuccessfulMpdus + nFailedMpdus;
946  if (station->m_isSampling)
947  {
948  station->m_samplePacketsCount += nSuccessfulMpdus + nFailedMpdus;
949  }
950  if (station->m_totalPacketsCount == ~0)
951  {
952  station->m_samplePacketsCount = 0;
953  station->m_totalPacketsCount = 0;
954  }
955 
956  if (!station->m_sampleWait && !station->m_sampleTries && station->m_sampleCount > 0)
957  {
958  station->m_sampleWait = 16 + 2 * station->m_avgAmpduLen;
959  station->m_sampleTries = 1;
960  station->m_sampleCount--;
961  }
962 }
963 
964 uint16_t
965 MinstrelHtWifiManager::UpdateRateAfterAllowedWidth(uint16_t txRate, uint16_t allowedWidth)
966 {
967  NS_LOG_FUNCTION(this << txRate << allowedWidth);
968 
969  auto groupId = GetGroupId(txRate);
970  McsGroup group = m_minstrelGroups[groupId];
971 
972  if (group.chWidth <= allowedWidth)
973  {
974  NS_LOG_DEBUG("Channel width is not greater than allowed width, nothing to do");
975  return txRate;
976  }
977 
979  NS_ASSERT(group.chWidth % 20 == 0);
980  // try halving the channel width and check if the group with the same number of
981  // streams and same GI is supported, until either a supported group is found or
982  // the width becomes lower than 20 MHz
983  uint16_t width = group.chWidth / 2;
984 
985  while (width >= 20)
986  {
987  if (width > allowedWidth)
988  {
989  width /= 2;
990  continue;
991  }
992 
993  switch (group.type)
994  {
996  groupId = GetHtGroupId(group.streams, group.gi, width);
997  break;
999  groupId = GetVhtGroupId(group.streams, group.gi, width);
1000  break;
1002  groupId = GetHeGroupId(group.streams, group.gi, width);
1003  break;
1004  default:
1005  NS_ABORT_MSG("Unknown group type: " << group.type);
1006  }
1007 
1008  group = m_minstrelGroups[groupId];
1009  if (group.isSupported)
1010  {
1011  break;
1012  }
1013 
1014  width /= 2;
1015  }
1016 
1017  NS_ABORT_MSG_IF(width < 20, "No rate compatible with the allowed width found");
1018 
1019  return GetIndex(groupId, GetRateId(txRate));
1020 }
1021 
1024 {
1025  NS_LOG_FUNCTION(this << st << allowedWidth);
1026  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
1027 
1028  if (!station->m_initialized)
1029  {
1030  CheckInit(station);
1031  }
1032 
1033  if (!station->m_isHt)
1034  {
1035  WifiTxVector vector = m_legacyManager->GetDataTxVector(station);
1036  uint64_t dataRate = vector.GetMode().GetDataRate(vector);
1037  if (m_currentRate != dataRate && !station->m_isSampling)
1038  {
1039  NS_LOG_DEBUG("New datarate: " << dataRate);
1040  m_currentRate = dataRate;
1041  }
1042  return vector;
1043  }
1044  else
1045  {
1046  station->m_txrate = UpdateRateAfterAllowedWidth(station->m_txrate, allowedWidth);
1047  NS_LOG_DEBUG("DoGetDataMode m_txrate= " << station->m_txrate);
1048 
1049  uint8_t rateId = GetRateId(station->m_txrate);
1050  uint8_t groupId = GetGroupId(station->m_txrate);
1051  uint8_t mcsIndex = station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex;
1052 
1053  NS_LOG_DEBUG("DoGetDataMode rateId= " << +rateId << " groupId= " << +groupId
1054  << " mode= " << GetMcsSupported(station, mcsIndex));
1055 
1056  McsGroup group = m_minstrelGroups[groupId];
1057 
1058  // Check consistency of rate selected.
1059  if (((group.type == WIFI_MINSTREL_GROUP_HE) && (group.gi < GetGuardInterval(station))) ||
1060  (((group.type == WIFI_MINSTREL_GROUP_HT) || (group.type == WIFI_MINSTREL_GROUP_VHT)) &&
1061  (group.gi == 400) && !GetShortGuardIntervalSupported(station)) ||
1062  (group.chWidth > GetChannelWidth(station)) ||
1063  (group.streams > GetNumberOfSupportedStreams(station)))
1064  {
1065  NS_FATAL_ERROR("Inconsistent group selected. Group: ("
1066  << +group.streams << "," << group.gi << "," << group.chWidth << ")"
1067  << " Station capabilities: (" << GetNumberOfSupportedStreams(station)
1068  << ","
1069  << ((group.type == WIFI_MINSTREL_GROUP_HE)
1070  ? GetGuardInterval(station)
1071  : (GetShortGuardIntervalSupported(station) ? 400 : 800))
1072  << "," << GetChannelWidth(station) << ")");
1073  }
1074  WifiMode mode = GetMcsSupported(station, mcsIndex);
1075  WifiTxVector txVector{
1076  mode,
1079  group.gi,
1081  group.streams,
1082  GetNess(station),
1083  GetPhy()->GetTxBandwidth(mode, group.chWidth),
1084  GetAggregation(station) && !station->m_isSampling};
1085  uint64_t dataRate = mode.GetDataRate(txVector);
1086  if (m_currentRate != dataRate && !station->m_isSampling)
1087  {
1088  NS_LOG_DEBUG("New datarate: " << dataRate);
1089  m_currentRate = dataRate;
1090  }
1091  return txVector;
1092  }
1093 }
1094 
1097 {
1098  NS_LOG_FUNCTION(this << st);
1099  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
1100 
1101  if (!station->m_initialized)
1102  {
1103  CheckInit(station);
1104  }
1105 
1106  if (!station->m_isHt)
1107  {
1108  return m_legacyManager->GetRtsTxVector(station);
1109  }
1110  else
1111  {
1112  NS_LOG_DEBUG("DoGetRtsMode m_txrate=" << station->m_txrate);
1113 
1114  /* RTS is sent in a non-HT frame. RTS with HT is not supported yet in NS3.
1115  * When supported, decision of using HT has to follow rules in Section 9.7.6 from
1116  * 802.11-2012. From Sec. 9.7.6.5: "A frame other than a BlockAckReq or BlockAck that is
1117  * carried in a non-HT PPDU shall be transmitted by the STA using a rate no higher than the
1118  * highest rate in the BSSBasicRateSet parameter that is less than or equal to the rate or
1119  * non-HT reference rate (see 9.7.9) of the previously transmitted frame that was
1120  * directed to the same receiving STA. If no rate in the BSSBasicRateSet parameter meets
1121  * these conditions, the control frame shall be transmitted at a rate no higher than the
1122  * highest mandatory rate of the attached PHY that is less than or equal to the rate
1123  * or non-HT reference rate (see 9.7.9) of the previously transmitted frame that was
1124  * directed to the same receiving STA."
1125  */
1126 
1127  // As we are in Minstrel HT, assume the last rate was an HT rate.
1128  uint8_t rateId = GetRateId(station->m_txrate);
1129  uint8_t groupId = GetGroupId(station->m_txrate);
1130  uint8_t mcsIndex = station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex;
1131 
1132  WifiMode lastRate = GetMcsSupported(station, mcsIndex);
1133  uint64_t lastDataRate = lastRate.GetNonHtReferenceRate();
1134  uint8_t nBasicRates = GetNBasicModes();
1135 
1136  WifiMode rtsRate;
1137  bool rateFound = false;
1138 
1139  for (uint8_t i = 0; i < nBasicRates; i++)
1140  {
1141  uint64_t rate = GetBasicMode(i).GetDataRate(20);
1142  if (rate <= lastDataRate)
1143  {
1144  rtsRate = GetBasicMode(i);
1145  rateFound = true;
1146  }
1147  }
1148 
1149  if (!rateFound)
1150  {
1151  Ptr<WifiPhy> phy = GetPhy();
1152  for (const auto& mode : phy->GetModeList())
1153  {
1154  uint64_t rate = mode.GetDataRate(20);
1155  if (rate <= lastDataRate)
1156  {
1157  rtsRate = mode;
1158  rateFound = true;
1159  }
1160  }
1161  }
1162 
1163  NS_ASSERT(rateFound);
1164 
1165  return WifiTxVector(
1166  rtsRate,
1169  800,
1170  1,
1171  1,
1172  0,
1173  GetPhy()->GetTxBandwidth(rtsRate, GetChannelWidth(station)),
1174  GetAggregation(station));
1175  }
1176 }
1177 
1178 bool
1180  Ptr<const Packet> packet,
1181  bool normally)
1182 {
1183  NS_LOG_FUNCTION(this << st << packet << normally);
1184 
1185  auto station = static_cast<MinstrelHtWifiRemoteStation*>(st);
1186 
1187  CheckInit(station);
1188  if (!station->m_initialized)
1189  {
1190  return normally;
1191  }
1192 
1193  uint32_t maxRetries;
1194 
1195  if (!station->m_isHt)
1196  {
1197  maxRetries = m_legacyManager->CountRetries(station);
1198  }
1199  else
1200  {
1201  maxRetries = CountRetries(station);
1202  }
1203 
1204  if (station->m_longRetry >= maxRetries)
1205  {
1206  NS_LOG_DEBUG("No re-transmission allowed. Retries: " << station->m_longRetry
1207  << " Max retries: " << maxRetries);
1208  return false;
1209  }
1210  else
1211  {
1212  NS_LOG_DEBUG("Re-transmit. Retries: " << station->m_longRetry
1213  << " Max retries: " << maxRetries);
1214  return true;
1215  }
1216 }
1217 
1218 uint32_t
1220 {
1221  uint8_t maxProbRateId = GetRateId(station->m_maxProbRate);
1222  uint8_t maxProbGroupId = GetGroupId(station->m_maxProbRate);
1223  uint8_t maxTpRateId = GetRateId(station->m_maxTpRate);
1224  uint8_t maxTpGroupId = GetGroupId(station->m_maxTpRate);
1225  uint8_t maxTp2RateId = GetRateId(station->m_maxTpRate2);
1226  uint8_t maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
1227 
1228  if (!station->m_isSampling)
1229  {
1230  return station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].retryCount +
1231  station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].retryCount +
1232  station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount;
1233  }
1234  else
1235  {
1236  return 1 + station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTp2RateId].retryCount +
1237  station->m_groupsTable[maxProbGroupId].m_ratesTable[maxProbRateId].retryCount;
1238  }
1239 }
1240 
1241 uint16_t
1243 {
1244  NS_LOG_FUNCTION(this << station);
1245  uint8_t sampleGroup = station->m_sampleGroup;
1246  uint8_t index = station->m_groupsTable[sampleGroup].m_index;
1247  uint8_t col = station->m_groupsTable[sampleGroup].m_col;
1248  uint8_t sampleIndex = station->m_sampleTable[index][col];
1249  uint16_t rateIndex = GetIndex(sampleGroup, sampleIndex);
1250  NS_LOG_DEBUG("Next Sample is " << rateIndex);
1251  SetNextSample(station); // Calculate the next sample rate.
1252  return rateIndex;
1253 }
1254 
1255 void
1257 {
1258  NS_LOG_FUNCTION(this << station);
1259  do
1260  {
1261  station->m_sampleGroup++;
1262  station->m_sampleGroup %= m_numGroups;
1263  } while (!station->m_groupsTable[station->m_sampleGroup].m_supported);
1264 
1265  station->m_groupsTable[station->m_sampleGroup].m_index++;
1266 
1267  uint8_t sampleGroup = station->m_sampleGroup;
1268  uint8_t index = station->m_groupsTable[station->m_sampleGroup].m_index;
1269  uint8_t col = station->m_groupsTable[sampleGroup].m_col;
1270 
1271  if (index >= m_numRates)
1272  {
1273  station->m_groupsTable[station->m_sampleGroup].m_index = 0;
1274  station->m_groupsTable[station->m_sampleGroup].m_col++;
1275  if (station->m_groupsTable[station->m_sampleGroup].m_col >= m_nSampleCol)
1276  {
1277  station->m_groupsTable[station->m_sampleGroup].m_col = 0;
1278  }
1279  index = station->m_groupsTable[station->m_sampleGroup].m_index;
1280  col = station->m_groupsTable[sampleGroup].m_col;
1281  }
1282  NS_LOG_DEBUG("New sample set: group= " << +sampleGroup
1283  << " index= " << +station->m_sampleTable[index][col]);
1284 }
1285 
1286 uint16_t
1288 {
1289  NS_LOG_FUNCTION(this << station);
1290  NS_LOG_DEBUG("FindRate packet=" << station->m_totalPacketsCount);
1291 
1292  if ((station->m_samplePacketsCount + station->m_totalPacketsCount) == 0)
1293  {
1294  return station->m_maxTpRate;
1295  }
1296 
1297  // If we have waited enough, then sample.
1298  if (station->m_sampleWait == 0 && station->m_sampleTries != 0)
1299  {
1300  // SAMPLING
1301  NS_LOG_DEBUG("Obtaining a sampling rate");
1303  uint16_t sampleIdx = GetNextSample(station);
1304  NS_LOG_DEBUG("Sampling rate = " << sampleIdx);
1305 
1306  // Evaluate if the sampling rate selected should be used.
1307  uint8_t sampleGroupId = GetGroupId(sampleIdx);
1308  uint8_t sampleRateId = GetRateId(sampleIdx);
1309 
1310  // If the rate selected is not supported, then don't sample.
1311  if (station->m_groupsTable[sampleGroupId].m_supported &&
1312  station->m_groupsTable[sampleGroupId].m_ratesTable[sampleRateId].supported)
1313  {
1321  MinstrelHtRateInfo sampleRateInfo =
1322  station->m_groupsTable[sampleGroupId].m_ratesTable[sampleRateId];
1323 
1324  NS_LOG_DEBUG("Use sample rate? MaxTpRate= "
1325  << station->m_maxTpRate << " CurrentRate= " << station->m_txrate
1326  << " SampleRate= " << sampleIdx
1327  << " SampleProb= " << sampleRateInfo.ewmaProb);
1328 
1329  if (sampleIdx != station->m_maxTpRate && sampleIdx != station->m_maxTpRate2 &&
1330  sampleIdx != station->m_maxProbRate && sampleRateInfo.ewmaProb <= 95)
1331  {
1337  uint8_t maxTpGroupId = GetGroupId(station->m_maxTpRate);
1338  uint8_t maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
1339  uint8_t maxTp2RateId = GetRateId(station->m_maxTpRate2);
1340  uint8_t maxProbGroupId = GetGroupId(station->m_maxProbRate);
1341  uint8_t maxProbRateId = GetRateId(station->m_maxProbRate);
1342 
1343  uint8_t maxTpStreams = m_minstrelGroups[maxTpGroupId].streams;
1344  uint8_t sampleStreams = m_minstrelGroups[sampleGroupId].streams;
1345 
1346  Time sampleDuration = sampleRateInfo.perfectTxTime;
1347  Time maxTp2Duration =
1348  station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].perfectTxTime;
1349  Time maxProbDuration = station->m_groupsTable[maxProbGroupId]
1350  .m_ratesTable[maxProbRateId]
1351  .perfectTxTime;
1352 
1353  NS_LOG_DEBUG("Use sample rate? SampleDuration= "
1354  << sampleDuration << " maxTp2Duration= " << maxTp2Duration
1355  << " maxProbDuration= " << maxProbDuration << " sampleStreams= "
1356  << +sampleStreams << " maxTpStreams= " << +maxTpStreams);
1357  if (sampleDuration < maxTp2Duration ||
1358  (sampleStreams < maxTpStreams && sampleDuration < maxProbDuration))
1359  {
1361  station->m_isSampling = true;
1362 
1364  station->m_sampleRate = sampleIdx;
1365 
1366  NS_LOG_DEBUG("FindRate "
1367  << "sampleRate=" << sampleIdx);
1368  station->m_sampleTries--;
1369  return sampleIdx;
1370  }
1371  else
1372  {
1373  station->m_numSamplesSlow++;
1374  if (sampleRateInfo.numSamplesSkipped >= 20 && station->m_numSamplesSlow <= 2)
1375  {
1377  station->m_isSampling = true;
1378 
1380  station->m_sampleRate = sampleIdx;
1381 
1382  NS_LOG_DEBUG("FindRate "
1383  << "sampleRate=" << sampleIdx);
1384  station->m_sampleTries--;
1385  return sampleIdx;
1386  }
1387  }
1388  }
1389  }
1390  }
1391  if (station->m_sampleWait > 0)
1392  {
1393  station->m_sampleWait--;
1394  }
1395 
1397 
1398  NS_LOG_DEBUG("FindRate "
1399  << "maxTpRrate=" << station->m_maxTpRate);
1400  return station->m_maxTpRate;
1401 }
1402 
1403 void
1405 {
1406  NS_LOG_FUNCTION(this << station);
1407 
1409 
1410  station->m_numSamplesSlow = 0;
1411  station->m_sampleCount = 0;
1412 
1413  double tempProb;
1414 
1415  if (station->m_ampduPacketCount > 0)
1416  {
1417  uint32_t newLen = station->m_ampduLen / station->m_ampduPacketCount;
1418  station->m_avgAmpduLen =
1419  (newLen * (100 - m_ewmaLevel) + (station->m_avgAmpduLen * m_ewmaLevel)) / 100;
1420  station->m_ampduLen = 0;
1421  station->m_ampduPacketCount = 0;
1422  }
1423 
1424  /* Initialize global rate indexes */
1425  station->m_maxTpRate = GetLowestIndex(station);
1426  station->m_maxTpRate2 = GetLowestIndex(station);
1427  station->m_maxProbRate = GetLowestIndex(station);
1428 
1430  for (uint8_t j = 0; j < m_numGroups; j++)
1431  {
1432  if (station->m_groupsTable[j].m_supported)
1433  {
1434  station->m_sampleCount++;
1435 
1436  /* (re)Initialize group rate indexes */
1437  station->m_groupsTable[j].m_maxTpRate = GetLowestIndex(station, j);
1438  station->m_groupsTable[j].m_maxTpRate2 = GetLowestIndex(station, j);
1439  station->m_groupsTable[j].m_maxProbRate = GetLowestIndex(station, j);
1440 
1441  for (uint8_t i = 0; i < m_numRates; i++)
1442  {
1443  if (station->m_groupsTable[j].m_ratesTable[i].supported)
1444  {
1445  station->m_groupsTable[j].m_ratesTable[i].retryUpdated = false;
1446 
1447  NS_LOG_DEBUG(
1448  +i << " "
1449  << GetMcsSupported(station,
1450  station->m_groupsTable[j].m_ratesTable[i].mcsIndex)
1451  << "\t attempt="
1452  << station->m_groupsTable[j].m_ratesTable[i].numRateAttempt
1453  << "\t success="
1454  << station->m_groupsTable[j].m_ratesTable[i].numRateSuccess);
1455 
1457  if (station->m_groupsTable[j].m_ratesTable[i].numRateAttempt > 0)
1458  {
1459  station->m_groupsTable[j].m_ratesTable[i].numSamplesSkipped = 0;
1464  tempProb =
1465  (100 * station->m_groupsTable[j].m_ratesTable[i].numRateSuccess) /
1466  station->m_groupsTable[j].m_ratesTable[i].numRateAttempt;
1467 
1469  station->m_groupsTable[j].m_ratesTable[i].prob = tempProb;
1470 
1471  if (station->m_groupsTable[j].m_ratesTable[i].successHist == 0)
1472  {
1473  station->m_groupsTable[j].m_ratesTable[i].ewmaProb = tempProb;
1474  }
1475  else
1476  {
1477  station->m_groupsTable[j].m_ratesTable[i].ewmsdProb =
1478  CalculateEwmsd(station->m_groupsTable[j].m_ratesTable[i].ewmsdProb,
1479  tempProb,
1480  station->m_groupsTable[j].m_ratesTable[i].ewmaProb,
1481  m_ewmaLevel);
1483  tempProb =
1484  (tempProb * (100 - m_ewmaLevel) +
1485  station->m_groupsTable[j].m_ratesTable[i].ewmaProb * m_ewmaLevel) /
1486  100;
1487  station->m_groupsTable[j].m_ratesTable[i].ewmaProb = tempProb;
1488  }
1489 
1490  station->m_groupsTable[j].m_ratesTable[i].throughput =
1491  CalculateThroughput(station, j, i, tempProb);
1492 
1493  station->m_groupsTable[j].m_ratesTable[i].successHist +=
1494  station->m_groupsTable[j].m_ratesTable[i].numRateSuccess;
1495  station->m_groupsTable[j].m_ratesTable[i].attemptHist +=
1496  station->m_groupsTable[j].m_ratesTable[i].numRateAttempt;
1497  }
1498  else
1499  {
1500  station->m_groupsTable[j].m_ratesTable[i].numSamplesSkipped++;
1501  }
1502 
1504  station->m_groupsTable[j].m_ratesTable[i].prevNumRateSuccess =
1505  station->m_groupsTable[j].m_ratesTable[i].numRateSuccess;
1506  station->m_groupsTable[j].m_ratesTable[i].prevNumRateAttempt =
1507  station->m_groupsTable[j].m_ratesTable[i].numRateAttempt;
1508  station->m_groupsTable[j].m_ratesTable[i].numRateSuccess = 0;
1509  station->m_groupsTable[j].m_ratesTable[i].numRateAttempt = 0;
1510 
1511  if (station->m_groupsTable[j].m_ratesTable[i].throughput != 0)
1512  {
1513  SetBestStationThRates(station, GetIndex(j, i));
1514  SetBestProbabilityRate(station, GetIndex(j, i));
1515  }
1516  }
1517  }
1518  }
1519  }
1520 
1521  // Try to sample all available rates during each interval.
1522  station->m_sampleCount *= 8;
1523 
1524  // Recalculate retries for the rates selected.
1525  CalculateRetransmits(station, station->m_maxTpRate);
1526  CalculateRetransmits(station, station->m_maxTpRate2);
1527  CalculateRetransmits(station, station->m_maxProbRate);
1528 
1529  NS_LOG_DEBUG("max tp=" << station->m_maxTpRate << "\nmax tp2=" << station->m_maxTpRate2
1530  << "\nmax prob=" << station->m_maxProbRate);
1531  if (m_printStats)
1532  {
1533  PrintTable(station);
1534  }
1535 }
1536 
1537 double
1539  uint8_t groupId,
1540  uint8_t rateId,
1541  double ewmaProb)
1542 {
1548  if (ewmaProb < 10)
1549  {
1550  return 0;
1551  }
1552  else
1553  {
1558  Time txTime = station->m_groupsTable[groupId].m_ratesTable[rateId].perfectTxTime;
1559  if (ewmaProb > 90)
1560  {
1561  return 90 / txTime.GetSeconds();
1562  }
1563  else
1564  {
1565  return ewmaProb / txTime.GetSeconds();
1566  }
1567  }
1568 }
1569 
1570 void
1572 {
1573  GroupInfo* group;
1574  MinstrelHtRateInfo rate;
1575  uint8_t tmpGroupId;
1576  uint8_t tmpRateId;
1577  double tmpTh;
1578  double tmpProb;
1579  uint8_t groupId;
1580  uint8_t rateId;
1581  double currentTh;
1582  // maximum group probability (GP) variables
1583  uint8_t maxGPGroupId;
1584  uint8_t maxGPRateId;
1585  double maxGPTh;
1586 
1587  groupId = GetGroupId(index);
1588  rateId = GetRateId(index);
1589  group = &station->m_groupsTable[groupId];
1590  rate = group->m_ratesTable[rateId];
1591 
1592  tmpGroupId = GetGroupId(station->m_maxProbRate);
1593  tmpRateId = GetRateId(station->m_maxProbRate);
1594  tmpProb = station->m_groupsTable[tmpGroupId].m_ratesTable[tmpRateId].ewmaProb;
1595  tmpTh = station->m_groupsTable[tmpGroupId].m_ratesTable[tmpRateId].throughput;
1596 
1597  if (rate.ewmaProb > 75)
1598  {
1599  currentTh = station->m_groupsTable[groupId].m_ratesTable[rateId].throughput;
1600  if (currentTh > tmpTh)
1601  {
1602  station->m_maxProbRate = index;
1603  }
1604 
1605  maxGPGroupId = GetGroupId(group->m_maxProbRate);
1606  maxGPRateId = GetRateId(group->m_maxProbRate);
1607  maxGPTh = station->m_groupsTable[maxGPGroupId].m_ratesTable[maxGPRateId].throughput;
1608 
1609  if (currentTh > maxGPTh)
1610  {
1611  group->m_maxProbRate = index;
1612  }
1613  }
1614  else
1615  {
1616  if (rate.ewmaProb > tmpProb)
1617  {
1618  station->m_maxProbRate = index;
1619  }
1620  maxGPRateId = GetRateId(group->m_maxProbRate);
1621  if (rate.ewmaProb > group->m_ratesTable[maxGPRateId].ewmaProb)
1622  {
1623  group->m_maxProbRate = index;
1624  }
1625  }
1626 }
1627 
1628 /*
1629  * Find & sort topmost throughput rates
1630  *
1631  * If multiple rates provide equal throughput the sorting is based on their
1632  * current success probability. Higher success probability is preferred among
1633  * MCS groups.
1634  */
1635 void
1637 {
1638  uint8_t groupId;
1639  uint8_t rateId;
1640  double th;
1641  double prob;
1642  uint8_t maxTpGroupId;
1643  uint8_t maxTpRateId;
1644  uint8_t maxTp2GroupId;
1645  uint8_t maxTp2RateId;
1646  double maxTpTh;
1647  double maxTpProb;
1648  double maxTp2Th;
1649  double maxTp2Prob;
1650 
1651  groupId = GetGroupId(index);
1652  rateId = GetRateId(index);
1653  prob = station->m_groupsTable[groupId].m_ratesTable[rateId].ewmaProb;
1654  th = station->m_groupsTable[groupId].m_ratesTable[rateId].throughput;
1655 
1656  maxTpGroupId = GetGroupId(station->m_maxTpRate);
1657  maxTpRateId = GetRateId(station->m_maxTpRate);
1658  maxTpProb = station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].ewmaProb;
1659  maxTpTh = station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].throughput;
1660 
1661  maxTp2GroupId = GetGroupId(station->m_maxTpRate2);
1662  maxTp2RateId = GetRateId(station->m_maxTpRate2);
1663  maxTp2Prob = station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].ewmaProb;
1664  maxTp2Th = station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].throughput;
1665 
1666  if (th > maxTpTh || (th == maxTpTh && prob > maxTpProb))
1667  {
1668  station->m_maxTpRate2 = station->m_maxTpRate;
1669  station->m_maxTpRate = index;
1670  }
1671  else if (th > maxTp2Th || (th == maxTp2Th && prob > maxTp2Prob))
1672  {
1673  station->m_maxTpRate2 = index;
1674  }
1675 
1676  // Find best rates per group
1677 
1678  GroupInfo* group = &station->m_groupsTable[groupId];
1679  maxTpGroupId = GetGroupId(group->m_maxTpRate);
1680  maxTpRateId = GetRateId(group->m_maxTpRate);
1681  maxTpProb = group->m_ratesTable[maxTpRateId].ewmaProb;
1682  maxTpTh = station->m_groupsTable[maxTpGroupId].m_ratesTable[maxTpRateId].throughput;
1683 
1684  maxTp2GroupId = GetGroupId(group->m_maxTpRate2);
1685  maxTp2RateId = GetRateId(group->m_maxTpRate2);
1686  maxTp2Prob = group->m_ratesTable[maxTp2RateId].ewmaProb;
1687  maxTp2Th = station->m_groupsTable[maxTp2GroupId].m_ratesTable[maxTp2RateId].throughput;
1688 
1689  if (th > maxTpTh || (th == maxTpTh && prob > maxTpProb))
1690  {
1691  group->m_maxTpRate2 = group->m_maxTpRate;
1692  group->m_maxTpRate = index;
1693  }
1694  else if (th > maxTp2Th || (th == maxTp2Th && prob > maxTp2Prob))
1695  {
1696  group->m_maxTpRate2 = index;
1697  }
1698 }
1699 
1700 void
1702 {
1703  NS_LOG_FUNCTION(this << station);
1704 
1706 
1710  NS_LOG_DEBUG("Supported groups by station:");
1711  bool noSupportedGroupFound = true;
1712  for (uint8_t groupId = 0; groupId < m_numGroups; groupId++)
1713  {
1714  if (m_minstrelGroups[groupId].isSupported)
1715  {
1716  station->m_groupsTable[groupId].m_supported = false;
1717 
1718  if ((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_HE) &&
1719  !GetHeSupported(station))
1720  {
1721  // It is a HE group but the receiver does not support HE: skip
1722  continue;
1723  }
1724  if ((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_VHT) &&
1725  !GetVhtSupported(station))
1726  {
1727  // It is a VHT group but the receiver does not support VHT: skip
1728  continue;
1729  }
1730  if ((m_minstrelGroups[groupId].type != WIFI_MINSTREL_GROUP_HE) &&
1732  {
1733  // It is not a HE group and the receiver supports HE: skip since
1734  // UseLatestAmendmentOnly attribute is enabled
1735  continue;
1736  }
1737  if (!GetHeSupported(station) &&
1740  {
1741  // It is not a VHT group and the receiver supports VHT (but not HE): skip since
1742  // UseLatestAmendmentOnly attribute is enabled
1743  continue;
1744  }
1745  if (((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_HT) ||
1747  (m_minstrelGroups[groupId].gi == 400) && !GetShortGuardIntervalSupported(station))
1748  {
1749  // It is a SGI group but the receiver does not support SGI: skip
1750  continue;
1751  }
1752  if ((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_HE) &&
1753  (m_minstrelGroups[groupId].gi < GetGuardInterval(station)))
1754  {
1755  // The receiver does not support the GI: skip
1756  continue;
1757  }
1758  if (GetChannelWidth(station) < m_minstrelGroups[groupId].chWidth)
1759  {
1760  // The receiver does not support the channel width: skip
1761  continue;
1762  }
1763  if (GetNumberOfSupportedStreams(station) < m_minstrelGroups[groupId].streams)
1764  {
1765  // The receiver does not support the number of spatial streams: skip
1766  continue;
1767  }
1768 
1769  NS_LOG_DEBUG("Group: " << +groupId << " type: " << m_minstrelGroups[groupId].type
1770  << " streams: " << +m_minstrelGroups[groupId].streams
1771  << " GI: " << m_minstrelGroups[groupId].gi
1772  << " width: " << m_minstrelGroups[groupId].chWidth);
1773 
1774  noSupportedGroupFound = false;
1775  station->m_groupsTable[groupId].m_supported = true;
1776  station->m_groupsTable[groupId].m_col = 0;
1777  station->m_groupsTable[groupId].m_index = 0;
1778 
1779  station->m_groupsTable[groupId].m_ratesTable =
1781  for (uint8_t i = 0; i < m_numRates; i++)
1782  {
1783  station->m_groupsTable[groupId].m_ratesTable[i].supported = false;
1784  }
1785 
1786  // Initialize all modes supported by the remote station that belong to the current
1787  // group.
1788  for (uint8_t i = 0; i < station->m_nModes; i++)
1789  {
1790  WifiMode mode = GetMcsSupported(station, i);
1791 
1794  uint8_t rateId = mode.GetMcsValue();
1795  if (mode.GetModulationClass() == WIFI_MOD_CLASS_HT)
1796  {
1797  rateId %= MAX_HT_GROUP_RATES;
1798  }
1799 
1800  if (((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_HE) &&
1801  (mode.GetModulationClass() ==
1803  && IsValidMcs(GetPhy(),
1804  m_minstrelGroups[groupId].streams,
1805  m_minstrelGroups[groupId].chWidth,
1806  mode))
1807  || ((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_VHT) &&
1808  (mode.GetModulationClass() ==
1810  && IsValidMcs(GetPhy(),
1811  m_minstrelGroups[groupId].streams,
1812  m_minstrelGroups[groupId].chWidth,
1813  mode))
1814  || ((m_minstrelGroups[groupId].type == WIFI_MINSTREL_GROUP_HT) &&
1815  (mode.GetModulationClass() ==
1817  && (mode.GetMcsValue() <
1818  (m_minstrelGroups[groupId].streams *
1819  8))
1820  && (mode.GetMcsValue() >= ((m_minstrelGroups[groupId].streams - 1) * 8))))
1821  {
1822  NS_LOG_DEBUG("Mode " << +i << ": " << mode);
1823 
1824  station->m_groupsTable[groupId].m_ratesTable[rateId].supported = true;
1825  station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex =
1826  i;
1827  station->m_groupsTable[groupId].m_ratesTable[rateId].numRateAttempt = 0;
1828  station->m_groupsTable[groupId].m_ratesTable[rateId].numRateSuccess = 0;
1829  station->m_groupsTable[groupId].m_ratesTable[rateId].prob = 0;
1830  station->m_groupsTable[groupId].m_ratesTable[rateId].ewmaProb = 0;
1831  station->m_groupsTable[groupId].m_ratesTable[rateId].prevNumRateAttempt = 0;
1832  station->m_groupsTable[groupId].m_ratesTable[rateId].prevNumRateSuccess = 0;
1833  station->m_groupsTable[groupId].m_ratesTable[rateId].numSamplesSkipped = 0;
1834  station->m_groupsTable[groupId].m_ratesTable[rateId].successHist = 0;
1835  station->m_groupsTable[groupId].m_ratesTable[rateId].attemptHist = 0;
1836  station->m_groupsTable[groupId].m_ratesTable[rateId].throughput = 0;
1837  station->m_groupsTable[groupId].m_ratesTable[rateId].perfectTxTime =
1838  GetFirstMpduTxTime(groupId, GetMcsSupported(station, i));
1839  station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount = 0;
1840  station->m_groupsTable[groupId].m_ratesTable[rateId].adjustedRetryCount = 0;
1841  CalculateRetransmits(station, groupId, rateId);
1842  }
1843  }
1844  }
1845  }
1848  if (noSupportedGroupFound)
1849  {
1850  NS_FATAL_ERROR("No supported group has been found");
1851  }
1852  SetNextSample(station);
1853  UpdateStats(station);
1854  station->m_txrate = FindRate(station);
1855 }
1856 
1857 void
1859 {
1860  NS_LOG_FUNCTION(this << station << index);
1861  uint8_t groupId = GetGroupId(index);
1862  uint8_t rateId = GetRateId(index);
1863  if (!station->m_groupsTable[groupId].m_ratesTable[rateId].retryUpdated)
1864  {
1865  CalculateRetransmits(station, groupId, rateId);
1866  }
1867 }
1868 
1869 void
1871  uint8_t groupId,
1872  uint8_t rateId)
1873 {
1874  NS_LOG_FUNCTION(this << station << +groupId << +rateId);
1875 
1876  uint32_t cw = 15; // Is an approximation.
1877  uint32_t cwMax = 1023;
1878  Time cwTime;
1879  Time txTime;
1880  Time dataTxTime;
1881  Time slotTime = GetPhy()->GetSlot();
1882  Time ackTime = GetPhy()->GetSifs() + GetPhy()->GetBlockAckTxTime();
1883 
1884  if (station->m_groupsTable[groupId].m_ratesTable[rateId].ewmaProb < 1)
1885  {
1886  station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount = 1;
1887  }
1888  else
1889  {
1890  station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount = 2;
1891  station->m_groupsTable[groupId].m_ratesTable[rateId].retryUpdated = true;
1892 
1893  dataTxTime =
1895  groupId,
1896  GetMcsSupported(station,
1897  station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex)) +
1898  GetMpduTxTime(
1899  groupId,
1900  GetMcsSupported(station,
1901  station->m_groupsTable[groupId].m_ratesTable[rateId].mcsIndex)) *
1902  (station->m_avgAmpduLen - 1);
1903 
1904  /* Contention time for first 2 tries */
1905  cwTime = (cw / 2) * slotTime;
1906  cw = Min((cw + 1) * 2, cwMax);
1907  cwTime += (cw / 2) * slotTime;
1908  cw = Min((cw + 1) * 2, cwMax);
1909 
1910  /* Total TX time for data and Contention after first 2 tries */
1911  txTime = cwTime + 2 * (dataTxTime + ackTime);
1912 
1913  /* See how many more tries we can fit inside segment size */
1914  do
1915  {
1916  /* Contention time for this try */
1917  cwTime = (cw / 2) * slotTime;
1918  cw = Min((cw + 1) * 2, cwMax);
1919 
1920  /* Total TX time after this try */
1921  txTime += cwTime + ackTime + dataTxTime;
1922  } while ((txTime < MilliSeconds(6)) &&
1923  (++station->m_groupsTable[groupId].m_ratesTable[rateId].retryCount < 7));
1924  }
1925 }
1926 
1927 double
1929  double currentProb,
1930  double ewmaProb,
1931  double weight)
1932 {
1933  double diff;
1934  double incr;
1935  double tmp;
1936 
1937  /* calculate exponential weighted moving variance */
1938  diff = currentProb - ewmaProb;
1939  incr = (100 - weight) * diff / 100;
1940  tmp = oldEwmsd * oldEwmsd;
1941  tmp = weight * (tmp + diff * incr) / 100;
1942 
1943  /* return standard deviation */
1944  return sqrt(tmp);
1945 }
1946 
1947 void
1949 {
1950  NS_LOG_FUNCTION(this << station);
1951  station->m_col = station->m_index = 0;
1952 
1953  // for off-setting to make rates fall between 0 and nModes
1954  uint8_t numSampleRates = m_numRates;
1955 
1956  uint16_t newIndex;
1957  for (uint8_t col = 0; col < m_nSampleCol; col++)
1958  {
1959  for (uint8_t i = 0; i < numSampleRates; i++)
1960  {
1965  int uv = m_uniformRandomVariable->GetInteger(0, numSampleRates);
1966  newIndex = (i + uv) % numSampleRates;
1967 
1968  // this loop is used for filling in other uninitialized places
1969  while (station->m_sampleTable[newIndex][col] != 0)
1970  {
1971  newIndex = (newIndex + 1) % m_numRates;
1972  }
1973  station->m_sampleTable[newIndex][col] = i;
1974  }
1975  }
1976 }
1977 
1978 void
1980 {
1981  if (!station->m_statsFile.is_open())
1982  {
1983  std::ostringstream tmp;
1984  tmp << "minstrel-ht-stats-" << station->m_state->m_address << ".txt";
1985  station->m_statsFile.open(tmp.str(), std::ios::out);
1986  }
1987 
1988  station->m_statsFile
1989  << " best ____________rate__________ ________statistics________ "
1990  "________last_______ ______sum-of________\n"
1991  << " mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] "
1992  "[prob.|retry|suc|att] [#success | #attempts]\n";
1993  for (uint8_t i = 0; i < m_numGroups; i++)
1994  {
1995  StatsDump(station, i, station->m_statsFile);
1996  }
1997 
1998  station->m_statsFile << "\nTotal packet count:: ideal "
1999  << Max(0, station->m_totalPacketsCount - station->m_samplePacketsCount)
2000  << " lookaround " << station->m_samplePacketsCount << "\n";
2001  station->m_statsFile << "Average # of aggregated frames per A-MPDU: " << station->m_avgAmpduLen
2002  << "\n\n";
2003 
2004  station->m_statsFile.flush();
2005 }
2006 
2007 void
2009  uint8_t groupId,
2010  std::ofstream& of)
2011 {
2012  uint8_t numRates = m_numRates;
2013  McsGroup group = m_minstrelGroups[groupId];
2014  Time txTime;
2015  for (uint8_t i = 0; i < numRates; i++)
2016  {
2017  if (station->m_groupsTable[groupId].m_supported &&
2018  station->m_groupsTable[groupId].m_ratesTable[i].supported)
2019  {
2020  of << group.type << " " << group.chWidth << " " << group.gi << " " << +group.streams
2021  << " ";
2022 
2023  uint16_t maxTpRate = station->m_maxTpRate;
2024  uint16_t maxTpRate2 = station->m_maxTpRate2;
2025  uint16_t maxProbRate = station->m_maxProbRate;
2026 
2027  uint16_t idx = GetIndex(groupId, i);
2028  if (idx == maxTpRate)
2029  {
2030  of << 'A';
2031  }
2032  else
2033  {
2034  of << ' ';
2035  }
2036  if (idx == maxTpRate2)
2037  {
2038  of << 'B';
2039  }
2040  else
2041  {
2042  of << ' ';
2043  }
2044  if (idx == maxProbRate)
2045  {
2046  of << 'P';
2047  }
2048  else
2049  {
2050  of << ' ';
2051  }
2052 
2053  if (group.type == WIFI_MINSTREL_GROUP_HT)
2054  {
2055  of << std::setw(4) << " MCS" << (group.streams - 1) * 8 + i;
2056  }
2057  else
2058  {
2059  of << std::setw(7) << " MCS" << +i << "/" << static_cast<int>(group.streams);
2060  }
2061 
2062  of << " " << std::setw(3) << +idx << " ";
2063 
2064  /* tx_time[rate(i)] in usec */
2065  txTime = GetFirstMpduTxTime(
2066  groupId,
2067  GetMcsSupported(station, station->m_groupsTable[groupId].m_ratesTable[i].mcsIndex));
2068  of << std::setw(6) << txTime.GetMicroSeconds() << " ";
2069 
2070  of << std::setw(7) << CalculateThroughput(station, groupId, i, 100) / 100 << " "
2071  << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].throughput / 100
2072  << " " << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].ewmaProb
2073  << " " << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].ewmsdProb
2074  << " " << std::setw(7) << station->m_groupsTable[groupId].m_ratesTable[i].prob
2075  << " " << std::setw(2) << station->m_groupsTable[groupId].m_ratesTable[i].retryCount
2076  << " " << std::setw(3)
2077  << station->m_groupsTable[groupId].m_ratesTable[i].prevNumRateSuccess << " "
2078  << std::setw(3) << station->m_groupsTable[groupId].m_ratesTable[i].prevNumRateAttempt
2079  << " " << std::setw(9)
2080  << station->m_groupsTable[groupId].m_ratesTable[i].successHist << " "
2081  << std::setw(9) << station->m_groupsTable[groupId].m_ratesTable[i].attemptHist
2082  << "\n";
2083  }
2084  }
2085 }
2086 
2087 uint16_t
2088 MinstrelHtWifiManager::GetIndex(uint8_t groupId, uint8_t rateId)
2089 {
2090  NS_LOG_FUNCTION(this << +groupId << +rateId);
2091  uint16_t index;
2092  index = groupId * m_numRates + rateId;
2093  return index;
2094 }
2095 
2096 uint8_t
2098 {
2099  NS_LOG_FUNCTION(this << index);
2100  uint8_t id;
2101  id = index % m_numRates;
2102  return id;
2103 }
2104 
2105 uint8_t
2107 {
2108  NS_LOG_FUNCTION(this << index);
2109  return index / m_numRates;
2110 }
2111 
2112 uint8_t
2113 MinstrelHtWifiManager::GetHtGroupId(uint8_t txstreams, uint16_t gi, uint16_t chWidth)
2114 {
2115  NS_LOG_FUNCTION(this << +txstreams << gi << chWidth);
2116  uint8_t giIndex = (gi == 400) ? 1 : 0;
2117  uint8_t widthIndex = (chWidth == 40) ? 1 : 0;
2118  return (MAX_HT_SUPPORTED_STREAMS * 2 * widthIndex) + (MAX_HT_SUPPORTED_STREAMS * giIndex) +
2119  txstreams - 1;
2120 }
2121 
2122 uint8_t
2123 MinstrelHtWifiManager::GetVhtGroupId(uint8_t txstreams, uint16_t gi, uint16_t chWidth)
2124 {
2125  NS_LOG_FUNCTION(this << +txstreams << gi << chWidth);
2126  uint8_t giIndex = (gi == 400) ? 1 : 0;
2127  uint8_t widthIndex;
2128  if (chWidth == 160)
2129  {
2130  widthIndex = 3;
2131  }
2132  else if (chWidth == 80)
2133  {
2134  widthIndex = 2;
2135  }
2136  else if (chWidth == 40)
2137  {
2138  widthIndex = 1;
2139  }
2140  else // 20 MHz
2141  {
2142  widthIndex = 0;
2143  }
2144  uint8_t groupId = (MAX_HT_STREAM_GROUPS * MAX_HT_SUPPORTED_STREAMS);
2145  groupId += (MAX_VHT_SUPPORTED_STREAMS * 2 * widthIndex) +
2146  (MAX_VHT_SUPPORTED_STREAMS * giIndex) + txstreams - 1;
2147  return groupId;
2148 }
2149 
2150 uint8_t
2151 MinstrelHtWifiManager::GetHeGroupId(uint8_t txstreams, uint16_t gi, uint16_t chWidth)
2152 {
2153  NS_LOG_FUNCTION(this << +txstreams << gi << chWidth);
2154  uint8_t giIndex;
2155  if (gi == 800)
2156  {
2157  giIndex = 2;
2158  }
2159  else if (gi == 1600)
2160  {
2161  giIndex = 1;
2162  }
2163  else // 3200 ns
2164  {
2165  giIndex = 0;
2166  }
2167  uint8_t widthIndex;
2168  if (chWidth == 160)
2169  {
2170  widthIndex = 3;
2171  }
2172  else if (chWidth == 80)
2173  {
2174  widthIndex = 2;
2175  }
2176  else if (chWidth == 40)
2177  {
2178  widthIndex = 1;
2179  }
2180  else // 20 MHz
2181  {
2182  widthIndex = 0;
2183  }
2184  uint8_t groupId = (MAX_HT_STREAM_GROUPS * MAX_HT_SUPPORTED_STREAMS);
2185  if (GetVhtSupported())
2186  {
2188  }
2189  groupId += (MAX_HE_SUPPORTED_STREAMS * 3 * widthIndex) + (MAX_HE_SUPPORTED_STREAMS * giIndex) +
2190  txstreams - 1;
2191  return groupId;
2192 }
2193 
2194 uint16_t
2196 {
2197  NS_LOG_FUNCTION(this << station);
2198 
2199  uint8_t groupId = 0;
2200  uint8_t rateId = 0;
2201  while (groupId < m_numGroups && !station->m_groupsTable[groupId].m_supported)
2202  {
2203  groupId++;
2204  }
2205  while (rateId < m_numRates && !station->m_groupsTable[groupId].m_ratesTable[rateId].supported)
2206  {
2207  rateId++;
2208  }
2209  NS_ASSERT(station->m_groupsTable[groupId].m_supported &&
2210  station->m_groupsTable[groupId].m_ratesTable[rateId].supported);
2211  return GetIndex(groupId, rateId);
2212 }
2213 
2214 uint16_t
2216 {
2217  NS_LOG_FUNCTION(this << station << +groupId);
2218 
2219  uint8_t rateId = 0;
2220  while (rateId < m_numRates && !station->m_groupsTable[groupId].m_ratesTable[rateId].supported)
2221  {
2222  rateId++;
2223  }
2224  NS_ASSERT(station->m_groupsTable[groupId].m_supported &&
2225  station->m_groupsTable[groupId].m_ratesTable[rateId].supported);
2226  return GetIndex(groupId, rateId);
2227 }
2228 
2231 {
2232  const auto& mcsList = GetPhy()->GetMcsList(WIFI_MOD_CLASS_HE);
2233  WifiModeList heMcsList(mcsList.begin(), mcsList.end());
2234  return heMcsList;
2235 }
2236 
2239 {
2240  const auto& mcsList = GetPhy()->GetMcsList(WIFI_MOD_CLASS_VHT);
2241  WifiModeList vhtMcsList(mcsList.begin(), mcsList.end());
2242  return vhtMcsList;
2243 }
2244 
2247 {
2248  const auto& mcsList = GetPhy()->GetMcsList(WIFI_MOD_CLASS_HT);
2249  WifiModeList htMcsList(mcsList.begin(), mcsList.end());
2250  return htMcsList;
2251 }
2252 
2253 } // namespace ns3
Implementation of Minstrel-HT Rate Control Algorithm.
static TypeId GetTypeId()
Get the type ID.
uint32_t CountRetries(MinstrelHtWifiRemoteStation *station)
Count retries.
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.
bool DoNeedRetransmission(WifiRemoteStation *st, Ptr< const Packet > packet, bool normally) override
WifiTxVector DoGetDataTxVector(WifiRemoteStation *station, uint16_t allowedWidth) override
WifiTxVector DoGetRtsTxVector(WifiRemoteStation *station) override
MinstrelMcsGroups m_minstrelGroups
Global array for groups information.
void AddMpduTxTime(uint8_t groupId, WifiMode mode, Time t)
Save a TxTime to the vector of groups.
void SetNextSample(MinstrelHtWifiRemoteStation *station)
Set the next sample from Sample Table.
uint8_t m_numRates
Number of rates per group Minstrel should consider.
uint8_t m_nSampleCol
Number of sample columns.
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 PrintTable(MinstrelHtWifiRemoteStation *station)
Printing Minstrel Table.
void StatsDump(MinstrelHtWifiRemoteStation *station, uint8_t groupId, std::ofstream &of)
Print group statistics.
void DoReportAmpduTxStatus(WifiRemoteStation *station, uint16_t nSuccessfulMpdus, uint16_t nFailedMpdus, double rxSnr, double dataSnr, uint16_t dataChannelWidth, uint8_t dataNss) override
Typically called per A-MPDU, either when a Block ACK was successfully received or when a BlockAckTime...
double CalculateThroughput(MinstrelHtWifiRemoteStation *station, uint8_t groupId, uint8_t rateId, double ewmaProb)
Return the average throughput of the MCS defined by groupId and rateId.
void AddFirstMpduTxTime(uint8_t groupId, WifiMode mode, Time t)
Save a TxTime to the vector of groups.
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.
WifiModeList GetHeDeviceMcsList() const
Returns a list of only the HE MCS supported by the device.
Time m_updateStats
How frequent do we calculate the stats.
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.
WifiModeList GetVhtDeviceMcsList() const
Returns a list of only the VHT MCS supported by the device.
Time CalculateMpduTxDuration(Ptr< WifiPhy > phy, uint8_t streams, uint16_t gi, uint16_t chWidth, WifiMode mode, MpduType mpduType)
Estimates the TxTime of a frame with a given mode and group (stream, guard interval and channel width...
void CheckInit(MinstrelHtWifiRemoteStation *station)
Check for initializations.
WifiModeList GetHtDeviceMcsList() const
Returns a list of only the HT MCS supported by the device.
void UpdateRetry(MinstrelHtWifiRemoteStation *station)
Update the number of retries and reset accordingly.
Time GetFirstMpduTxTime(uint8_t groupId, WifiMode mode) const
Obtain the TxTime saved in the group information.
Time GetMpduTxTime(uint8_t groupId, WifiMode mode) const
Obtain the TxTime saved in the group information.
uint16_t UpdateRateAfterAllowedWidth(uint16_t txRate, uint16_t allowedWidth)
Given the index of the current TX rate, check whether the channel width is not greater than the given...
uint8_t m_numGroups
Number of groups Minstrel should consider.
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...
void DoReportDataOk(WifiRemoteStation *station, double ackSnr, WifiMode ackMode, double dataSnr, uint16_t dataChannelWidth, uint8_t dataNss) override
This method is a pure virtual method that must be implemented by the sub-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.
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.
uint8_t GetGroupId(uint16_t index)
Return the groupId from the global index.
uint8_t GetVhtGroupId(uint8_t txstreams, uint16_t gi, uint16_t chWidth)
Returns the groupId of a VHT MCS with the given number of streams, GI and channel width used.
uint8_t GetHtGroupId(uint8_t txstreams, uint16_t gi, uint16_t chWidth)
Returns the groupId of an HT MCS with the given number of streams, GI and channel width used.
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.
void UpdateStats(MinstrelHtWifiRemoteStation *station)
Update the Minstrel Table.
uint16_t GetIndex(uint8_t groupId, uint8_t rateId)
Returns the global index corresponding to the groupId and rateId.
WifiRemoteStation * DoCreateStation() const override
Ptr< MinstrelWifiManager > m_legacyManager
Pointer to an instance of MinstrelWifiManager.
uint8_t GetHeGroupId(uint8_t txstreams, uint16_t gi, uint16_t chWidth)
Returns the groupId of an HE MCS with the given number of streams, GI and channel width used.
bool IsValidMcs(Ptr< WifiPhy > phy, uint8_t streams, uint16_t chWidth, WifiMode mode)
Check the validity of a combination of number of streams, chWidth and mode.
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.
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 SetStream(int64_t stream)
Specifies the stream number for the RngStream.
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:403
int64_t GetMicroSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:413
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
Hold an unsigned integer type.
Definition: uinteger.h:45
uint32_t GetInteger(uint32_t min, uint32_t max)
Get the next random value drawn from the distribution.
represent a single transmission mode
Definition: wifi-mode.h:51
WifiModulationClass GetModulationClass() const
Definition: wifi-mode.cc:185
uint64_t GetNonHtReferenceRate() const
Definition: wifi-mode.cc:192
uint64_t GetDataRate(uint16_t channelWidth, uint16_t guardInterval, uint8_t nss) const
Definition: wifi-mode.cc:122
uint8_t GetMcsValue() const
Definition: wifi-mode.cc:163
Time GetBlockAckTxTime() const
Return the estimated BlockAck TX time for this PHY.
Definition: wifi-phy.cc:817
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:1460
Time GetSlot() const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:793
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:781
uint16_t GetTxBandwidth(WifiMode mode, uint16_t maxAllowedBandWidth=std::numeric_limits< uint16_t >::max()) const
Get the bandwidth for a transmission occurring on the current operating channel and using the given W...
Definition: wifi-phy.cc:1075
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:2003
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition: wifi-phy.cc:1500
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.
uint16_t GetChannelWidth(const WifiRemoteStation *station) const
Return the channel width supported by the station.
Ptr< WifiPhy > GetPhy() const
Return the WifiPhy.
uint16_t GetGuardInterval() const
Return the supported HE guard interval duration (in nanoseconds).
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.
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.
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...
void SetStbc(bool stbc)
Sets if STBC is being used.
void SetNess(uint8_t ness)
Sets the Ness number.
void SetChannelWidth(uint16_t channelWidth)
Sets the selected channelWidth (in MHz)
void SetGuardInterval(uint16_t guardInterval)
Sets the guard interval duration (in nanoseconds)
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 SetMode(WifiMode mode)
Sets the selected payload transmission mode.
void SetNss(uint8_t nss)
Sets the number of Nss.
void SetPreambleType(WifiPreamble preamble)
Sets the preamble type.
#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_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
int64x64_t Max(const int64x64_t &a, const int64x64_t &b)
Maximum.
Definition: int64x64.h:243
int64x64_t Min(const int64x64_t &a, const int64x64_t &b)
Minimum.
Definition: int64x64.h:229
#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
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
MpduType
The type of an MPDU.
@ WIFI_PREAMBLE_HT_MF
@ 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.
@ MIDDLE_MPDU_IN_AGGREGATE
The MPDU is part of an A-MPDU with multiple MPDUs, but is neither the first nor the last aggregate.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
static const uint8_t MAX_HT_GROUP_RATES
Number of rates (or MCS) per HT group.
static const uint8_t MAX_VHT_STREAM_GROUPS
Maximal number of groups per stream in VHT (4 possible channel widths and 2 possible GI configuration...
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1414
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.
static const uint8_t MAX_VHT_GROUP_RATES
Number of rates (or MCS) per VHT group.
static const uint8_t MAX_VHT_SUPPORTED_STREAMS
Maximal number of streams supported by the VHT PHY layer.
static const uint8_t MAX_HT_SUPPORTED_STREAMS
Constants for maximum values.
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:533
static const uint8_t MAX_HE_GROUP_RATES
Number of rates (or MCS) per HE group.
static const uint8_t MAX_HT_WIDTH
Maximal channel width in MHz.
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:86
static const uint8_t MAX_HT_STREAM_GROUPS
Maximal number of groups per stream in HT (2 possible channel widths and 2 possible GI configurations...
static const uint8_t MAX_HE_STREAM_GROUPS
Maximal number of groups per stream in HE (4 possible channel widths and 3 possible GI configurations...
WifiPreamble GetPreambleForTransmission(WifiModulationClass modulation, bool useShortPreamble)
Return the preamble to be used for the transmission.
std::vector< MinstrelHtRateInfo > MinstrelHtRate
Data structure for a Minstrel Rate table.
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
std::vector< WifiMode > WifiModeList
In various parts of the code, folk are interested in maintaining a list of transmission modes.
Definition: wifi-mode.h:262
std::vector< std::vector< uint8_t > > SampleRate
Data structure for a Sample Rate table A vector of a vector uint8_t.
static const uint8_t MAX_HE_WIDTH
Maximal channel width in MHz.
static const uint8_t MAX_VHT_WIDTH
Maximal channel width in MHz.
std::vector< GroupInfo > McsGroupData
Data structure for a table of groups.
static const uint8_t MAX_HE_SUPPORTED_STREAMS
Maximal number of streams supported by the HE PHY layer.
mac
Definition: third.py:92
phy
Definition: third.py:89
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.
uint16_t chWidth
channel width (MHz)
uint16_t gi
guard interval duration (nanoseconds)
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.
Time perfectTxTime
Perfect transmission time calculation, or frame calculation.
double ewmaProb
Exponential weighted moving average of probability.
uint32_t numSamplesSkipped
Number of times this rate statistics were not updated because no attempts have been made.
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
SampleRate m_sampleTable
sample table
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.