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