A Discrete-Event Network Simulator
API
he-ppdu.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Orange Labs
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Rediet <getachew.redieteab@orange.com>
18  * Muhammad Iqbal Rochman <muhiqbalcr@uchicago.edu>
19  * Sébastien Deronne <sebastien.deronne@gmail.com> (HeSigHeader)
20  */
21 
22 #include "he-ppdu.h"
23 
24 #include "he-phy.h"
25 
26 #include "ns3/log.h"
27 #include "ns3/wifi-phy-operating-channel.h"
28 #include "ns3/wifi-phy.h"
29 #include "ns3/wifi-psdu.h"
30 #include "ns3/wifi-utils.h"
31 
32 #include <algorithm>
33 
34 namespace ns3
35 {
36 
37 NS_LOG_COMPONENT_DEFINE("HePpdu");
38 
39 std::ostream&
40 operator<<(std::ostream& os, const HePpdu::TxPsdFlag& flag)
41 {
42  switch (flag)
43  {
45  return (os << "PSD_NON_HE_PORTION");
47  return (os << "PSD_HE_PORTION");
48  default:
49  NS_FATAL_ERROR("Invalid PSD flag");
50  return (os << "INVALID");
51  }
52 }
53 
55  const WifiTxVector& txVector,
57  Time ppduDuration,
58  uint64_t uid,
59  TxPsdFlag flag)
60  : OfdmPpdu(psdus.begin()->second,
61  txVector,
62  channel,
63  uid,
64  false), // don't instantiate LSigHeader of OfdmPpdu
65  m_txPsdFlag(flag)
66 {
67  NS_LOG_FUNCTION(this << psdus << txVector << channel << ppduDuration << uid << flag);
68 
69  // overwrite with map (since only first element used by OfdmPpdu)
70  m_psdus.begin()->second = nullptr;
71  m_psdus.clear();
72  m_psdus = psdus;
73  SetPhyHeaders(txVector, ppduDuration);
74 }
75 
77  const WifiTxVector& txVector,
79  Time ppduDuration,
80  uint64_t uid)
81  : OfdmPpdu(psdu,
82  txVector,
83  channel,
84  uid,
85  false), // don't instantiate LSigHeader of OfdmPpdu
86  m_txPsdFlag(PSD_NON_HE_PORTION)
87 {
88  NS_LOG_FUNCTION(this << psdu << txVector << channel << ppduDuration << uid);
89  NS_ASSERT(!IsMu());
90  SetPhyHeaders(txVector, ppduDuration);
91 }
92 
93 void
94 HePpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
95 {
96  NS_LOG_FUNCTION(this << txVector << ppduDuration);
97  SetLSigHeader(ppduDuration);
98  SetHeSigHeader(txVector);
99 }
100 
101 void
103 {
104  uint8_t sigExtension = 0;
107  {
108  sigExtension = 6;
109  }
110  uint8_t m = IsDlMu() ? 1 : 2;
111  uint16_t length = ((ceil((static_cast<double>(ppduDuration.GetNanoSeconds() - (20 * 1000) -
112  (sigExtension * 1000)) /
113  1000) /
114  4.0) *
115  3) -
116  3 - m);
117  m_lSig.SetLength(length);
118 }
119 
120 void
122 {
123  const auto bssColor = txVector.GetBssColor();
124  NS_ASSERT(bssColor < 64);
125  if (ns3::IsUlMu(m_preamble))
126  {
128  .m_bssColor = bssColor,
129  .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth())});
130  }
131  else if (ns3::IsDlMu(m_preamble))
132  {
133  const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(20);
134  const uint8_t noMuMimoUsers{0};
136  .m_bssColor = bssColor,
137  .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth()),
138  .m_sigBMcs = txVector.GetSigBMode().GetMcsValue(),
139  .m_muMimoUsers = (txVector.IsSigBCompression()
140  ? GetMuMimoUsersEncoding(txVector.GetHeMuUserInfoMap().size())
141  : noMuMimoUsers),
142  .m_sigBCompression = txVector.IsSigBCompression(),
143  .m_giLtfSize = GetGuardIntervalAndNltfEncoding(txVector.GetGuardInterval(),
144  2 /*NLTF currently unused*/),
145  .m_ruAllocation = txVector.GetRuAllocation(p20Index),
146  .m_contentChannels = GetHeSigBContentChannels(txVector, p20Index),
147  .m_center26ToneRuIndication =
148  (txVector.GetChannelWidth() >= 80)
149  ? std::optional{txVector.GetCenter26ToneRuIndication()}
150  : std::nullopt});
151  }
152  else
153  {
154  const auto mcs = txVector.GetMode().GetMcsValue();
155  NS_ASSERT(mcs <= 11);
157  .m_bssColor = bssColor,
158  .m_mcs = mcs,
159  .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth()),
160  .m_giLtfSize = GetGuardIntervalAndNltfEncoding(txVector.GetGuardInterval(),
161  2 /*NLTF currently unused*/),
162  .m_nsts = GetNstsEncodingFromNss(txVector.GetNss())});
163  }
164 }
165 
168 {
169  WifiTxVector txVector;
170  txVector.SetPreambleType(m_preamble);
171  SetTxVectorFromPhyHeaders(txVector);
172  return txVector;
173 }
174 
175 void
177 {
178  txVector.SetLength(m_lSig.GetLength());
179  txVector.SetAggregation(m_psdus.size() > 1 || m_psdus.begin()->second->IsAggregate());
180  if (!IsMu())
181  {
182  auto heSigHeader = std::get_if<HeSuSigHeader>(&m_heSig);
183  NS_ASSERT(heSigHeader && (heSigHeader->m_format == 1));
184  txVector.SetMode(HePhy::GetHeMcs(heSigHeader->m_mcs));
185  txVector.SetNss(GetNssFromNstsEncoding(heSigHeader->m_nsts));
186  txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(heSigHeader->m_bandwidth));
187  txVector.SetGuardInterval(GetGuardIntervalFromEncoding(heSigHeader->m_giLtfSize));
188  txVector.SetBssColor(heSigHeader->m_bssColor);
189  }
190  else if (IsUlMu())
191  {
192  auto heSigHeader = std::get_if<HeTbSigHeader>(&m_heSig);
193  NS_ASSERT(heSigHeader && (heSigHeader->m_format == 0));
194  txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(heSigHeader->m_bandwidth));
195  txVector.SetBssColor(heSigHeader->m_bssColor);
196  }
197  else if (IsDlMu())
198  {
199  auto heSigHeader = std::get_if<HeMuSigHeader>(&m_heSig);
200  NS_ASSERT(heSigHeader);
201  txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(heSigHeader->m_bandwidth));
202  txVector.SetGuardInterval(GetGuardIntervalFromEncoding(heSigHeader->m_giLtfSize));
203  txVector.SetBssColor(heSigHeader->m_bssColor);
204  SetHeMuUserInfos(txVector,
205  heSigHeader->m_ruAllocation,
206  heSigHeader->m_contentChannels,
207  heSigHeader->m_sigBCompression,
208  GetMuMimoUsersFromEncoding(heSigHeader->m_muMimoUsers));
209  txVector.SetSigBMode(HePhy::GetVhtMcs(heSigHeader->m_sigBMcs));
210  const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(20);
211  txVector.SetRuAllocation(heSigHeader->m_ruAllocation, p20Index);
212  if (heSigHeader->m_center26ToneRuIndication.has_value())
213  {
214  txVector.SetCenter26ToneRuIndication(heSigHeader->m_center26ToneRuIndication.value());
215  }
216  if (heSigHeader->m_sigBCompression)
217  {
218  NS_ASSERT(GetMuMimoUsersFromEncoding(heSigHeader->m_muMimoUsers) ==
219  txVector.GetHeMuUserInfoMap().size());
220  }
221  }
222 }
223 
224 void
226  const RuAllocation& ruAllocation,
227  const HeSigBContentChannels& contentChannels,
228  bool sigBcompression,
229  uint8_t numMuMimoUsers) const
230 {
231  std::size_t contentChannelIndex = 0;
232  for (const auto& contentChannel : contentChannels)
233  {
234  std::size_t numRusLeft = 0;
235  std::size_t numUsersLeft = 0;
236  std::size_t ruAllocIndex = contentChannelIndex;
237  for (const auto& userInfo : contentChannel)
238  {
239  if (userInfo.staId == NO_USER_STA_ID)
240  {
241  continue;
242  }
243  if (ruAllocIndex >= ruAllocation.size())
244  {
245  break;
246  }
247  auto ruSpecs = HeRu::GetRuSpecs(ruAllocation.at(ruAllocIndex));
248  if (ruSpecs.empty())
249  {
250  continue;
251  }
252  if (numRusLeft == 0)
253  {
254  numRusLeft = ruSpecs.size();
255  }
256  if (numUsersLeft == 0)
257  {
258  if (sigBcompression)
259  {
260  numUsersLeft = numMuMimoUsers;
261  }
262  else
263  {
264  // not MU-MIMO
265  numUsersLeft = 1;
266  }
267  }
268  auto ruIndex = (ruSpecs.size() - numRusLeft);
269  auto ruSpec = ruSpecs.at(ruIndex);
270  auto ruType = ruSpec.GetRuType();
271  if ((ruAllocation.size() == 8) && (ruType == HeRu::RU_996_TONE) &&
272  (((txVector.GetChannelWidth() == 160) && sigBcompression) ||
273  std::all_of(
274  contentChannel.cbegin(),
275  contentChannel.cend(),
276  [&userInfo](const auto& item) { return userInfo.staId == item.staId; })))
277  {
278  NS_ASSERT(txVector.GetChannelWidth() == 160);
279  ruType = HeRu::RU_2x996_TONE;
280  }
281  const auto ruBw = HeRu::GetBandwidth(ruType);
282  auto primary80 = ruAllocIndex < 4;
283  auto num20MhzSubchannelsInRu = (ruBw < 20) ? 1 : (ruBw / 20);
284  auto numRuAllocsInContentChannel = std::max(1, num20MhzSubchannelsInRu / 2);
285  auto ruIndexOffset = (ruBw < 20) ? (ruSpecs.size() * ruAllocIndex)
286  : (ruAllocIndex / num20MhzSubchannelsInRu);
287  if (!primary80)
288  {
289  ruIndexOffset -= HeRu::GetRusOfType(80, ruType).size();
290  }
291  if (!txVector.IsAllocated(userInfo.staId))
292  {
293  txVector.SetHeMuUserInfo(userInfo.staId,
294  {{ruType, ruSpec.GetIndex() + ruIndexOffset, primary80},
295  userInfo.mcs,
296  userInfo.nss});
297  }
298  if ((ruType == HeRu::RU_2x996_TONE) && !sigBcompression)
299  {
300  return;
301  }
302  numRusLeft--;
303  numUsersLeft--;
304  if (numRusLeft == 0 && numUsersLeft == 0)
305  {
306  ruAllocIndex += (2 * numRuAllocsInContentChannel);
307  }
308  }
309  contentChannelIndex++;
310  }
311 }
312 
313 Time
314 HePpdu::GetTxDuration() const
315 {
316  Time ppduDuration = Seconds(0);
317  const auto& txVector = GetTxVector();
318  const auto length = m_lSig.GetLength();
319  const auto tSymbol = NanoSeconds(12800 + txVector.GetGuardInterval());
320  const auto preambleDuration = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
321  NS_ASSERT(m_operatingChannel.IsSet());
322  uint8_t sigExtension = (m_operatingChannel.GetPhyBand() == WIFI_PHY_BAND_2_4GHZ) ? 6 : 0;
323  uint8_t m = IsDlMu() ? 1 : 2;
324  // Equation 27-11 of IEEE P802.11ax/D4.0
325  const auto calculatedDuration =
326  MicroSeconds(((ceil(static_cast<double>(length + 3 + m) / 3)) * 4) + 20 + sigExtension);
327  NS_ASSERT(calculatedDuration > preambleDuration);
328  uint32_t nSymbols =
329  floor(static_cast<double>((calculatedDuration - preambleDuration).GetNanoSeconds() -
330  (sigExtension * 1000)) /
331  tSymbol.GetNanoSeconds());
332  return (preambleDuration + (nSymbols * tSymbol) + MicroSeconds(sigExtension));
333 }
334 
337 {
338  return Ptr<WifiPpdu>(new HePpdu(*this), false);
339 }
340 
342 HePpdu::GetType() const
343 {
344  switch (m_preamble)
345  {
346  case WIFI_PREAMBLE_HE_MU:
347  return WIFI_PPDU_TYPE_DL_MU;
348  case WIFI_PREAMBLE_HE_TB:
349  return WIFI_PPDU_TYPE_UL_MU;
350  default:
351  return WIFI_PPDU_TYPE_SU;
352  }
353 }
354 
355 bool
357 {
358  return (IsDlMu() || IsUlMu());
359 }
360 
361 bool
363 {
364  return (m_preamble == WIFI_PREAMBLE_HE_MU);
365 }
366 
367 bool
369 {
370  return (m_preamble == WIFI_PREAMBLE_HE_TB);
371 }
372 
374 HePpdu::GetPsdu(uint8_t bssColor, uint16_t staId /* = SU_STA_ID */) const
375 {
376  if (!IsMu())
377  {
378  NS_ASSERT(m_psdus.size() == 1);
379  return m_psdus.at(SU_STA_ID);
380  }
381 
382  if (IsUlMu())
383  {
384  auto heSigHeader = std::get_if<HeTbSigHeader>(&m_heSig);
385  NS_ASSERT(heSigHeader);
386  NS_ASSERT(m_psdus.size() == 1);
387  if ((bssColor == 0) || (heSigHeader->m_bssColor == 0) ||
388  (bssColor == heSigHeader->m_bssColor))
389  {
390  return m_psdus.cbegin()->second;
391  }
392  }
393  else
394  {
395  auto heSigHeader = std::get_if<HeMuSigHeader>(&m_heSig);
396  NS_ASSERT(heSigHeader);
397  if ((bssColor == 0) || (heSigHeader->m_bssColor == 0) ||
398  (bssColor == heSigHeader->m_bssColor))
399  {
400  const auto it = m_psdus.find(staId);
401  if (it != m_psdus.cend())
402  {
403  return it->second;
404  }
405  }
406  }
407  return nullptr;
408 }
409 
410 uint16_t
411 HePpdu::GetStaId() const
412 {
413  NS_ASSERT(IsUlMu());
414  return m_psdus.begin()->first;
415 }
416 
417 uint16_t
418 HePpdu::GetTxChannelWidth() const
419 {
420  if (const auto& txVector = GetTxVector();
421  txVector.IsValid() && txVector.IsUlMu() && GetStaId() != SU_STA_ID)
422  {
423  TxPsdFlag flag = GetTxPsdFlag();
424  uint16_t ruWidth = HeRu::GetBandwidth(txVector.GetRu(GetStaId()).GetRuType());
425  uint16_t channelWidth = (flag == PSD_NON_HE_PORTION && ruWidth < 20) ? 20 : ruWidth;
426  NS_LOG_INFO("Use channelWidth=" << channelWidth << " MHz for HE TB from " << GetStaId()
427  << " for " << flag);
428  return channelWidth;
429  }
430  else
431  {
432  return OfdmPpdu::GetTxChannelWidth();
433  }
434 }
435 
437 HePpdu::GetTxPsdFlag() const
438 {
439  return m_txPsdFlag;
440 }
441 
442 void
443 HePpdu::SetTxPsdFlag(TxPsdFlag flag) const
444 {
445  NS_LOG_FUNCTION(this << flag);
446  m_txPsdFlag = flag;
447 }
448 
449 void
450 HePpdu::UpdateTxVectorForUlMu(const std::optional<WifiTxVector>& trigVector) const
451 {
452  if (trigVector.has_value())
453  {
454  NS_LOG_FUNCTION(this << trigVector.value());
455  }
456  else
457  {
458  NS_LOG_FUNCTION(this);
459  }
460  if (!m_txVector.has_value())
461  {
462  m_txVector = GetTxVector();
463  }
464  NS_ASSERT(GetModulation() >= WIFI_MOD_CLASS_HE);
465  NS_ASSERT(GetType() == WIFI_PPDU_TYPE_UL_MU);
466  // HE TB PPDU reception needs information from the TRIGVECTOR to be able to receive the PPDU
467  const auto staId = GetStaId();
468  if (trigVector.has_value() && trigVector->IsUlMu() &&
469  (trigVector->GetHeMuUserInfoMap().count(staId) > 0))
470  {
471  // These information are not carried in HE-SIG-A for a HE TB PPDU,
472  // but they are carried in the Trigger frame soliciting the HE TB PPDU
473  m_txVector->SetGuardInterval(trigVector->GetGuardInterval());
474  m_txVector->SetHeMuUserInfo(staId, trigVector->GetHeMuUserInfo(staId));
475  }
476  else
477  {
478  // Set dummy user info, PPDU will be dropped later after decoding PHY headers.
479  m_txVector->SetHeMuUserInfo(
480  staId,
481  {{HeRu::GetRuType(m_txVector->GetChannelWidth()), 1, true}, 0, 1});
482  }
483 }
484 
485 std::pair<std::size_t, std::size_t>
486 HePpdu::GetNumRusPerHeSigBContentChannel(uint16_t channelWidth,
487  const RuAllocation& ruAllocation,
488  bool sigBCompression,
489  uint8_t numMuMimoUsers)
490 {
491  std::pair<std::size_t /* number of RUs in content channel 1 */,
492  std::size_t /* number of RUs in content channel 2 */>
493  chSize{0, 0};
494 
495  if (sigBCompression)
496  {
497  // If the HE-SIG-B Compression field in the HE-SIG-A field of an HE MU PPDU is 1,
498  // for bandwidths larger than 20 MHz, the AP performs an equitable split of
499  // the User fields between two HE-SIG-B content channels
500  if (channelWidth == 20)
501  {
502  return {numMuMimoUsers, 0};
503  }
504  chSize.first = numMuMimoUsers / 2;
505  chSize.second = numMuMimoUsers / 2;
506  if (numMuMimoUsers != (chSize.first + chSize.second))
507  {
508  chSize.first++;
509  }
510  return chSize;
511  }
512 
513  NS_ASSERT_MSG(!ruAllocation.empty(), "RU allocation is not set");
514  NS_ASSERT_MSG(ruAllocation.size() == channelWidth / 20,
515  "RU allocation is not consistent with packet bandwidth");
516 
517  switch (channelWidth)
518  {
519  case 40:
520  chSize.second += HeRu::GetRuSpecs(ruAllocation[1]).size();
521  [[fallthrough]];
522  case 20:
523  chSize.first += HeRu::GetRuSpecs(ruAllocation[0]).size();
524  break;
525  default:
526  for (auto n = 0; n < channelWidth / 20;)
527  {
528  chSize.first += HeRu::GetRuSpecs(ruAllocation[n]).size();
529  if (ruAllocation[n] >= 208)
530  {
531  // 996 tone RU occupies 80 MHz
532  n += 4;
533  continue;
534  }
535  n += 2;
536  }
537  for (auto n = 0; n < channelWidth / 20;)
538  {
539  chSize.second += HeRu::GetRuSpecs(ruAllocation[n + 1]).size();
540  if (ruAllocation[n + 1] >= 208)
541  {
542  // 996 tone RU occupies 80 MHz
543  n += 4;
544  continue;
545  }
546  n += 2;
547  }
548  break;
549  }
550  return chSize;
551 }
552 
554 HePpdu::GetHeSigBContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
555 {
556  HeSigBContentChannels contentChannels{{}};
557 
558  const auto channelWidth = txVector.GetChannelWidth();
559  if (channelWidth > 20)
560  {
561  contentChannels.emplace_back();
562  }
563 
564  const auto& orderedMap = txVector.GetUserInfoMapOrderedByRus(p20Index);
565  for (const auto& [ru, staIds] : orderedMap)
566  {
567  const auto ruType = ru.GetRuType();
568  if ((ruType > HeRu::RU_242_TONE) && !txVector.IsSigBCompression())
569  {
570  for (auto i = 0; i < ((ruType == HeRu::RU_2x996_TONE) ? 2 : 1); ++i)
571  {
572  for (auto staId : staIds)
573  {
574  const auto& userInfo = txVector.GetHeMuUserInfo(staId);
575  NS_ASSERT(ru == userInfo.ru);
576  contentChannels[0].push_back({staId, userInfo.nss, userInfo.mcs});
577  contentChannels[1].push_back({staId, userInfo.nss, userInfo.mcs});
578  }
579  }
580  continue;
581  }
582 
583  std::size_t numRus = (ruType >= HeRu::RU_242_TONE)
584  ? 1
585  : HeRu::m_heRuSubcarrierGroups.at({20, ruType}).size();
586  const auto ruIdx = ru.GetIndex();
587  for (auto staId : staIds)
588  {
589  const auto& userInfo = txVector.GetHeMuUserInfo(staId);
590  NS_ASSERT(ru == userInfo.ru);
591  std::size_t ccIndex{0};
592  if (channelWidth < 40)
593  {
594  // only one content channel
595  ccIndex = 0;
596  }
597  else if (txVector.IsSigBCompression())
598  {
599  // equal split
600  ccIndex = (contentChannels.at(0).size() <= contentChannels.at(1).size()) ? 0 : 1;
601  }
602  else // MU-MIMO
603  {
604  ccIndex = (((ruIdx - 1) / numRus) % 2 == 0) ? 0 : 1;
605  }
606  contentChannels.at(ccIndex).push_back({staId, userInfo.nss, userInfo.mcs});
607  }
608  }
609 
610  const auto isSigBCompression = txVector.IsSigBCompression();
611  if (!isSigBCompression)
612  {
613  // Add unassigned RUs
614  auto numNumRusPerHeSigBContentChannel = GetNumRusPerHeSigBContentChannel(
615  channelWidth,
616  txVector.GetRuAllocation(p20Index),
617  isSigBCompression,
618  isSigBCompression ? txVector.GetHeMuUserInfoMap().size() : 0);
619  std::size_t contentChannelIndex = 1;
620  for (auto& contentChannel : contentChannels)
621  {
622  const auto totalUsersInContentChannel = (contentChannelIndex == 1)
623  ? numNumRusPerHeSigBContentChannel.first
624  : numNumRusPerHeSigBContentChannel.second;
625  NS_ASSERT(contentChannel.size() <= totalUsersInContentChannel);
626  std::size_t unallocatedRus = totalUsersInContentChannel - contentChannel.size();
627  for (std::size_t i = 0; i < unallocatedRus; i++)
628  {
629  contentChannel.push_back({NO_USER_STA_ID, 0, 0});
630  }
631  contentChannelIndex++;
632  }
633  }
634 
635  return contentChannels;
636 }
637 
638 uint32_t
639 HePpdu::GetSigBFieldSize(uint16_t channelWidth,
640  const RuAllocation& ruAllocation,
641  bool sigBCompression,
642  std::size_t numMuMimoUsers)
643 {
644  // Compute the number of bits used by common field.
645  uint32_t commonFieldSize = 0;
646  if (!sigBCompression)
647  {
648  commonFieldSize = 4 /* CRC */ + 6 /* tail */;
649  if (channelWidth <= 40)
650  {
651  commonFieldSize += 8; // only one allocation subfield
652  }
653  else
654  {
655  commonFieldSize +=
656  8 * (channelWidth / 40) /* one allocation field per 40 MHz */ + 1 /* center RU */;
657  }
658  }
659 
660  auto numRusPerContentChannel = GetNumRusPerHeSigBContentChannel(channelWidth,
661  ruAllocation,
662  sigBCompression,
663  numMuMimoUsers);
664  auto maxNumRusPerContentChannel =
665  std::max(numRusPerContentChannel.first, numRusPerContentChannel.second);
666  auto maxNumUserBlockFields = maxNumRusPerContentChannel /
667  2; // handle last user block with single user, if any, further down
668  std::size_t userSpecificFieldSize =
669  maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */);
670  if (maxNumRusPerContentChannel % 2 != 0)
671  {
672  userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */;
673  }
674 
675  return commonFieldSize + userSpecificFieldSize;
676 }
677 
678 std::string
679 HePpdu::PrintPayload() const
680 {
681  std::ostringstream ss;
682  if (IsMu())
683  {
684  ss << m_psdus;
685  ss << ", " << m_txPsdFlag;
686  }
687  else
688  {
689  ss << "PSDU=" << m_psdus.at(SU_STA_ID) << " ";
690  }
691  return ss.str();
692 }
693 
694 uint8_t
695 HePpdu::GetChannelWidthEncodingFromMhz(uint16_t channelWidth)
696 {
697  if (channelWidth == 160)
698  {
699  return 3;
700  }
701  else if (channelWidth == 80)
702  {
703  return 2;
704  }
705  else if (channelWidth == 40)
706  {
707  return 1;
708  }
709  else
710  {
711  return 0;
712  }
713 }
714 
715 uint16_t
716 HePpdu::GetChannelWidthMhzFromEncoding(uint8_t bandwidth)
717 {
718  if (bandwidth == 3)
719  {
720  return 160;
721  }
722  else if (bandwidth == 2)
723  {
724  return 80;
725  }
726  else if (bandwidth == 1)
727  {
728  return 40;
729  }
730  else
731  {
732  return 20;
733  }
734 }
735 
736 uint8_t
737 HePpdu::GetGuardIntervalAndNltfEncoding(uint16_t gi, uint8_t nltf)
738 {
739  if (gi == 800 && nltf == 1)
740  {
741  return 0;
742  }
743  else if (gi == 800 && nltf == 2)
744  {
745  return 1;
746  }
747  else if (gi == 1600 && nltf == 2)
748  {
749  return 2;
750  }
751  else
752  {
753  return 3;
754  }
755 }
756 
757 uint16_t
758 HePpdu::GetGuardIntervalFromEncoding(uint8_t giAndNltfSize)
759 {
760  if (giAndNltfSize == 3)
761  {
762  // we currently do not consider DCM nor STBC fields
763  return 3200;
764  }
765  else if (giAndNltfSize == 2)
766  {
767  return 1600;
768  }
769  else
770  {
771  return 800;
772  }
773 }
774 
775 uint8_t
776 HePpdu::GetNstsEncodingFromNss(uint8_t nss)
777 {
778  NS_ASSERT(nss <= 8);
779  return nss - 1;
780 }
781 
782 uint8_t
783 HePpdu::GetNssFromNstsEncoding(uint8_t nsts)
784 {
785  return nsts + 1;
786 }
787 
788 uint8_t
789 HePpdu::GetMuMimoUsersEncoding(uint8_t nUsers)
790 {
791  NS_ASSERT(nUsers <= 8);
792  return (nUsers - 1);
793 }
794 
795 uint8_t
796 HePpdu::GetMuMimoUsersFromEncoding(uint8_t encoding)
797 {
798  return (encoding + 1);
799 }
800 
801 } // namespace ns3
#define max(a, b)
Definition: 80211b.c:42
static WifiMode GetHeMcs(uint8_t index)
Return the HE MCS corresponding to the provided index.
Definition: he-phy.cc:1548
HE PPDU (11ax)
Definition: he-ppdu.h:50
std::vector< std::vector< HeSigBUserSpecificField > > HeSigBContentChannels
HE SIG-B Content Channels.
Definition: he-ppdu.h:61
HeSigHeader m_heSig
the HE-SIG PHY header
Definition: he-ppdu.h:366
WifiTxVector DoGetTxVector() const override
Get the TXVECTOR used to send the PPDU.
Definition: he-ppdu.cc:167
virtual void SetTxVectorFromPhyHeaders(WifiTxVector &txVector) const
Fill in the TXVECTOR from PHY headers.
Definition: he-ppdu.cc:176
static uint16_t GetGuardIntervalFromEncoding(uint8_t giAndNltfSize)
Convert guard interval (in ns) from its encoding in HE-SIG-A.
Definition: he-ppdu.cc:758
static uint16_t GetChannelWidthMhzFromEncoding(uint8_t bandwidth)
Convert channel width expressed in MHz from bandwidth field encoding in HE-SIG-A.
Definition: he-ppdu.cc:716
TxPsdFlag
The transmit power spectral density flag, namely used to correctly build PSDs for pre-HE and HE porti...
Definition: he-ppdu.h:115
@ PSD_HE_PORTION
HE portion of an HE PPDU.
Definition: he-ppdu.h:117
@ PSD_NON_HE_PORTION
Non-HE portion of an HE PPDU.
Definition: he-ppdu.h:116
virtual bool IsDlMu() const
Return true if the PPDU is a DL MU PPDU.
Definition: he-ppdu.cc:362
virtual bool IsUlMu() const
Return true if the PPDU is an UL MU PPDU.
Definition: he-ppdu.cc:368
static uint8_t GetNstsEncodingFromNss(uint8_t nss)
Convert number of spatial streams to NSTS field encoding in HE-SIG-A.
Definition: he-ppdu.cc:776
void SetHeSigHeader(const WifiTxVector &txVector)
Fill in the HE-SIG header.
Definition: he-ppdu.cc:121
static uint8_t GetNssFromNstsEncoding(uint8_t nsts)
Convert number of spatial streams from NSTS field encoding in HE-SIG-A.
Definition: he-ppdu.cc:783
void SetPhyHeaders(const WifiTxVector &txVector, Time ppduDuration)
Fill in the PHY headers.
Definition: he-ppdu.cc:94
static HeSigBContentChannels GetHeSigBContentChannels(const WifiTxVector &txVector, uint8_t p20Index)
Get the HE SIG-B content channels for a given PPDU IEEE 802.11ax-2021 27.3.11.8.2 HE-SIG-B content ch...
Definition: he-ppdu.cc:554
void SetHeMuUserInfos(WifiTxVector &txVector, const RuAllocation &ruAllocation, const HeSigBContentChannels &contentChannels, bool sigBCompression, uint8_t numMuMimoUsers) const
Reconstruct HeMuUserInfoMap from HE-SIG-B header.
Definition: he-ppdu.cc:225
static uint8_t GetMuMimoUsersEncoding(uint8_t nUsers)
Convert number of MU-MIMO users to its encoding in HE-SIG-A.
Definition: he-ppdu.cc:789
virtual bool IsMu() const
Return true if the PPDU is a MU PPDU.
Definition: he-ppdu.cc:356
static uint8_t GetGuardIntervalAndNltfEncoding(uint16_t gi, uint8_t nltf)
Convert guard interval (in ns) and NLTF to its encoding in HE-SIG-A.
Definition: he-ppdu.cc:737
void SetLSigHeader(Time ppduDuration)
Fill in the L-SIG header.
Definition: he-ppdu.cc:102
HePpdu(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector, const WifiPhyOperatingChannel &channel, Time ppduDuration, uint64_t uid)
Create an SU HE PPDU, storing a PSDU.
Definition: he-ppdu.cc:76
static uint8_t GetChannelWidthEncodingFromMhz(uint16_t channelWidth)
Convert channel width expressed in MHz to bandwidth field encoding in HE-SIG-A.
Definition: he-ppdu.cc:695
static uint8_t GetMuMimoUsersFromEncoding(uint8_t encoding)
Convert number of MU-MIMO users from its encoding in HE-SIG-A.
Definition: he-ppdu.cc:796
static std::vector< RuSpec > GetRuSpecs(uint8_t ruAllocation)
Get the RU specs based on RU_ALLOCATION.
Definition: he-ru.cc:393
static uint16_t GetBandwidth(RuType ruType)
Get the approximate bandwidth occupied by a RU.
Definition: he-ru.cc:763
static std::vector< HeRu::RuSpec > GetRusOfType(uint16_t bw, HeRu::RuType ruType)
Get the set of distinct RUs of the given type (number of tones) available in a HE PPDU of the given b...
Definition: he-ru.cc:511
@ RU_996_TONE
Definition: he-ru.h:47
@ RU_2x996_TONE
Definition: he-ru.h:48
uint16_t GetLength() const
Return the LENGTH field of L-SIG (in bytes).
Definition: ofdm-ppdu.cc:210
void SetLength(uint16_t length)
Fill the LENGTH field of L-SIG (in bytes).
Definition: ofdm-ppdu.cc:203
OFDM PPDU (11a)
Definition: ofdm-ppdu.h:47
LSigHeader m_lSig
the L-SIG PHY header
Definition: ofdm-ppdu.h:110
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
int64_t GetNanoSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:418
static WifiMode GetVhtMcs(uint8_t index)
Return the VHT MCS corresponding to the provided index.
Definition: vht-phy.cc:342
uint8_t GetMcsValue() const
Definition: wifi-mode.cc:163
Class that keeps track of all information about the current PHY operating channel.
bool IsSet() const
Return true if a valid channel has been set, false otherwise.
uint8_t GetPrimaryChannelIndex(uint16_t primaryChannelWidth) const
If the operating channel width is a multiple of 20 MHz, return the index of the primary channel of th...
WifiPhyBand GetPhyBand() const
Return the PHY band of the operating channel.
const WifiPhyOperatingChannel & m_operatingChannel
the operating channel of the PHY
Definition: wifi-ppdu.h:210
WifiPreamble m_preamble
the PHY preamble
Definition: wifi-ppdu.h:202
WifiConstPsduMap m_psdus
the PSDUs contained in this PPDU
Definition: wifi-ppdu.h:204
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void SetCenter26ToneRuIndication(Center26ToneRuIndication center26ToneRuIndication)
Set CENTER_26_TONE_RU field.
void SetRuAllocation(const RuAllocation &ruAlloc, uint8_t p20Index)
Set RU_ALLOCATION field.
UserInfoMapOrderedByRus GetUserInfoMapOrderedByRus(uint8_t p20Index) const
Get the map of specific user info parameters ordered per increasing frequency RUs.
uint16_t GetGuardInterval() const
bool IsSigBCompression() const
Indicate whether the Common field is present in the HE-SIG-B field.
void SetChannelWidth(uint16_t channelWidth)
Sets the selected channelWidth (in MHz)
uint8_t GetBssColor() const
Get the BSS color.
const RuAllocation & GetRuAllocation(uint8_t p20Index) const
Get RU_ALLOCATION field.
void SetGuardInterval(uint16_t guardInterval)
Sets the guard interval duration (in nanoseconds)
std::optional< Center26ToneRuIndication > GetCenter26ToneRuIndication() const
Get CENTER_26_TONE_RU field This field is present if format is HE_MU and when channel width is set to...
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.
RuAllocation m_ruAllocation
RU allocations that are going to be carried in SIG-B common field per Table 27-1 IEEE.
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set the HE MU user-specific transmission information for the given STA-ID.
HeMuUserInfo GetHeMuUserInfo(uint16_t staId) const
Get the HE MU user-specific transmission information for the given STA-ID.
void SetAggregation(bool aggregation)
Sets if PSDU contains A-MPDU.
const HeMuUserInfoMap & GetHeMuUserInfoMap() const
Get a const reference to the map HE MU user-specific transmission information indexed by STA-ID.
uint8_t GetNss(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the number of spatial streams.
void SetLength(uint16_t length)
Set the LENGTH field of the L-SIG.
void SetSigBMode(const WifiMode &mode)
Set the MCS used for SIG-B.
void SetBssColor(uint8_t color)
Set the BSS color.
bool IsAllocated(uint16_t staId) const
Check if STA ID is allocated.
uint16_t GetChannelWidth() const
void SetMode(WifiMode mode)
Sets the selected payload transmission mode.
WifiMode GetSigBMode() const
Get MCS used for SIG-B.
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_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1350
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1362
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
WifiPpduType
The type of PPDU (SU, DL MU, or UL MU)
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PREAMBLE_HE_MU
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
Definition: wifi-phy-band.h:35
@ WIFI_PPDU_TYPE_DL_MU
@ WIFI_PPDU_TYPE_UL_MU
@ WIFI_PPDU_TYPE_SU
@ WIFI_MOD_CLASS_HE
HE (Clause 27)
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
Declaration of ns3::HePpdu class.
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:839
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
bool IsMu(WifiPreamble preamble)
Return true if a preamble corresponds to a multi-user transmission.
static constexpr uint16_t NO_USER_STA_ID
STA_ID for a RU that is intended for no user (Section 26.11.1 802.11ax-2021)
std::vector< uint8_t > RuAllocation
8 bit RU_ALLOCATION per 20 MHz
bool IsDlMu(WifiPreamble preamble)
Return true if a preamble corresponds to a downlink multi-user transmission.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition: angles.cc:159
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition: wifi-mode.h:35
bool IsUlMu(WifiPreamble preamble)
Return true if a preamble corresponds to a uplink multi-user transmission.
Ptr< T > Copy(Ptr< T > object)
Return a deep copy of a Ptr.
Definition: ptr.h:610
Definition: second.py:1
channel
Definition: third.py:88
HE-SIG PHY header for HE MU PPDUs (HE-SIG-A1/A2/B)
Definition: he-ppdu.h:90
uint8_t m_bssColor
BSS color field.
Definition: he-ppdu.h:92
HE-SIG PHY header for HE SU PPDUs (HE-SIG-A1/A2)
Definition: he-ppdu.h:67
uint8_t m_bssColor
BSS color field.
Definition: he-ppdu.h:69
HE-SIG PHY header for HE TB PPDUs (HE-SIG-A1/A2)
Definition: he-ppdu.h:80
uint8_t m_bssColor
BSS color field.
Definition: he-ppdu.h:82