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#include <numeric>
23
24namespace ns3
25{
26
28
29std::ostream&
30operator<<(std::ostream& os, const HePpdu::TxPsdFlag& flag)
31{
32 switch (flag)
33 {
35 return (os << "PSD_NON_HE_PORTION");
37 return (os << "PSD_HE_PORTION");
38 default:
39 NS_FATAL_ERROR("Invalid PSD flag");
40 return (os << "INVALID");
41 }
42}
43
45 const WifiTxVector& txVector,
46 const WifiPhyOperatingChannel& channel,
47 Time ppduDuration,
48 uint64_t uid,
49 TxPsdFlag flag,
50 bool instantiateHeaders /* = true */)
51 : OfdmPpdu(psdus.begin()->second,
52 txVector,
53 channel,
54 uid,
55 false), // don't instantiate LSigHeader of OfdmPpdu
56 m_txPsdFlag(flag)
57{
58 NS_LOG_FUNCTION(this << psdus << txVector << channel << ppduDuration << uid << flag
59 << instantiateHeaders);
60
61 // overwrite with map (since only first element used by OfdmPpdu)
62 m_psdus.begin()->second = nullptr;
63 m_psdus.clear();
64 m_psdus = psdus;
65 if (instantiateHeaders)
66 {
67 SetPhyHeaders(txVector, ppduDuration);
68 }
69}
70
72 const WifiTxVector& txVector,
73 const WifiPhyOperatingChannel& channel,
74 Time ppduDuration,
75 uint64_t uid,
76 bool instantiateHeaders /* = true */)
77 : OfdmPpdu(psdu,
78 txVector,
79 channel,
80 uid,
81 false), // don't instantiate LSigHeader of OfdmPpdu
82 m_txPsdFlag(PSD_NON_HE_PORTION)
83{
84 NS_LOG_FUNCTION(this << psdu << txVector << channel << ppduDuration << uid
85 << instantiateHeaders);
86 NS_ASSERT(!IsMu());
87 if (instantiateHeaders)
88 {
89 SetPhyHeaders(txVector, ppduDuration);
90 }
91}
92
93void
94HePpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
95{
96 NS_LOG_FUNCTION(this << txVector << ppduDuration);
97 SetLSigHeader(ppduDuration);
98 SetHeSigHeader(txVector);
99}
100
101void
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
120void
122{
123 const auto bssColor = txVector.GetBssColor();
124 NS_ASSERT(bssColor < 64);
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(MHz_u{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() >= MHz_u{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);
172 return txVector;
173}
174
175void
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,
206 heSigHeader->m_ruAllocation,
207 heSigHeader->m_center26ToneRuIndication,
208 heSigHeader->m_contentChannels,
209 heSigHeader->m_sigBCompression,
210 GetMuMimoUsersFromEncoding(heSigHeader->m_muMimoUsers));
211 txVector.SetSigBMode(HePhy::GetVhtMcs(heSigHeader->m_sigBMcs));
212 const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(MHz_u{20});
213 txVector.SetRuAllocation(heSigHeader->m_ruAllocation, p20Index);
214 if (heSigHeader->m_center26ToneRuIndication.has_value())
215 {
216 txVector.SetCenter26ToneRuIndication(heSigHeader->m_center26ToneRuIndication.value());
217 }
218 if (heSigHeader->m_sigBCompression)
219 {
220 NS_ASSERT(GetMuMimoUsersFromEncoding(heSigHeader->m_muMimoUsers) ==
221 txVector.GetHeMuUserInfoMap().size());
222 }
223 }
224}
225
227HePpdu::GetRuSpec(std::size_t ruAllocIndex, MHz_u bw, RuType ruType, std::size_t phyIndex) const
228{
229 auto isPrimary80{true};
230 auto index{phyIndex};
231 if (bw > MHz_u{80})
232 {
233 const auto isLow80 = ruAllocIndex < 4;
234 const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(MHz_u{20});
235 const auto primary80IsLower80 = (p20Index < bw / MHz_u{40});
236 if (!isLow80)
237 {
238 const auto numRusP80 = HeRu::GetRusOfType(MHz_u{80}, ruType).size();
239 index -= (ruType == RuType::RU_26_TONE) ? (numRusP80 - 1) : numRusP80;
240 }
241 isPrimary80 = ((primary80IsLower80 && isLow80) || (!primary80IsLower80 && !isLow80));
242 }
243 if ((ruType == RuType::RU_26_TONE) && (ruAllocIndex >= 2) && (index >= 19))
244 {
245 index++;
246 }
247 return HeRu::RuSpec{ruType, index, isPrimary80};
248}
249
250void
253 const RuAllocation& ruAllocation,
254 std::optional<Center26ToneRuIndication> center26ToneRuIndication,
255 const HeSigBContentChannels& contentChannels,
256 bool sigBcompression,
257 uint8_t numMuMimoUsers) const
258{
259 NS_ASSERT(ruAllocation.size() == Count20MHzSubchannels(txVector.GetChannelWidth()));
260 std::vector<uint8_t> remainingRuAllocIndices(ruAllocation.size());
261 std::iota(remainingRuAllocIndices.begin(), remainingRuAllocIndices.end(), 0);
262 std::size_t contentChannelIndex = 0;
263 std::size_t ruAllocIndex = 0;
264 for (const auto& contentChannel : contentChannels)
265 {
266 std::size_t numRusLeft = 0;
267 std::size_t numUsersLeft = 0;
268 ruAllocIndex = remainingRuAllocIndices.front();
269 std::size_t numUsersLeftInCc = contentChannel.size();
270 if (contentChannel.empty())
271 {
272 const auto pos = std::find(remainingRuAllocIndices.cbegin(),
273 remainingRuAllocIndices.cend(),
274 ruAllocIndex);
275 remainingRuAllocIndices.erase(pos);
276 ++contentChannelIndex;
277 continue;
278 }
279 for (const auto& userInfo : contentChannel)
280 {
281 if (center26ToneRuIndication && (numUsersLeftInCc == 1))
282 {
283 // handle central 26 tones
284 if ((contentChannelIndex == 0) &&
285 ((*center26ToneRuIndication ==
287 (*center26ToneRuIndication ==
289 {
290 txVector.SetHeMuUserInfo(
291 userInfo.staId,
292 {HeRu::RuSpec{RuType::RU_26_TONE, 19, true}, userInfo.mcs, userInfo.nss});
293 continue;
294 }
295 else if ((contentChannelIndex == 1) &&
296 ((*center26ToneRuIndication ==
298 (*center26ToneRuIndication ==
301 {
302 txVector.SetHeMuUserInfo(
303 userInfo.staId,
304 {HeRu::RuSpec{RuType::RU_26_TONE, 19, false}, userInfo.mcs, userInfo.nss});
305 continue;
306 }
307 }
308 NS_ASSERT(ruAllocIndex < ruAllocation.size());
309 auto ruSpecs = WifiRu::GetRuSpecs(ruAllocation.at(ruAllocIndex), mc);
310 while (ruSpecs.empty() && (ruAllocIndex < ruAllocation.size()))
311 {
312 const auto pos = std::find(remainingRuAllocIndices.cbegin(),
313 remainingRuAllocIndices.cend(),
314 ruAllocIndex);
315 remainingRuAllocIndices.erase(pos);
316 ruAllocIndex += 2;
317 NS_ASSERT(ruAllocIndex < ruAllocation.size());
318 ruSpecs = WifiRu::GetRuSpecs(ruAllocation.at(ruAllocIndex), mc);
319 }
320 if (numRusLeft == 0)
321 {
322 numRusLeft = ruSpecs.size();
323 }
324 if (numUsersLeft == 0)
325 {
326 if (sigBcompression)
327 {
328 numUsersLeft = numMuMimoUsers;
329 }
330 else
331 {
332 // not MU-MIMO
333 numUsersLeft = 1;
334 }
335 }
336 auto ruIndex = (ruSpecs.size() - numRusLeft);
337 const auto ruSpec = ruSpecs.at(ruIndex);
338 auto ruType = WifiRu::GetRuType(ruSpec);
339 if (sigBcompression)
340 {
341 ruType = WifiRu::GetRuType(ruAllocation.size() * MHz_u{20});
342 }
343 const auto ruBw = WifiRu::GetBandwidth(ruType);
344 const auto num20MhzSubchannelsInRu =
345 (ruBw < MHz_u{20}) ? 1 : Count20MHzSubchannels(ruBw);
346 if (userInfo.staId != NO_USER_STA_ID)
347 {
348 const std::size_t numRus =
349 (ruBw > MHz_u{20}) ? 1 : HeRu::GetNRus(MHz_u{20}, ruType);
350 const std::size_t ruIndexOffset = (ruBw < MHz_u{20})
351 ? (numRus * ruAllocIndex)
352 : (ruAllocIndex / num20MhzSubchannelsInRu);
353 std::size_t phyIndex = WifiRu::GetIndex(ruSpecs.at(ruIndex)) + ruIndexOffset;
354 const auto ru{
355 GetRuSpec(ruAllocIndex, txVector.GetChannelWidth(), ruType, phyIndex)};
356 txVector.SetHeMuUserInfo(userInfo.staId, {ru, userInfo.mcs, userInfo.nss});
357 }
358 numRusLeft--;
359 numUsersLeft--;
360 numUsersLeftInCc--;
361 if (numRusLeft == 0 && numUsersLeft == 0)
362 {
363 const auto pos = std::find(remainingRuAllocIndices.cbegin(),
364 remainingRuAllocIndices.cend(),
365 ruAllocIndex);
366 remainingRuAllocIndices.erase(pos);
367 ruAllocIndex += num20MhzSubchannelsInRu;
368 if (ruAllocIndex % 2 != contentChannelIndex)
369 {
370 ++ruAllocIndex;
371 }
372 }
373 }
374 contentChannelIndex++;
375 }
376}
377
378Time
379HePpdu::GetTxDuration() const
380{
381 Time ppduDuration;
382 const auto& txVector = GetTxVector();
383 const auto length = m_lSig.GetLength();
384 const auto tSymbol = HePhy::GetSymbolDuration(txVector.GetGuardInterval());
385 const auto preambleDuration = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
386 NS_ASSERT(m_operatingChannel.IsSet());
387 uint8_t sigExtension = (m_operatingChannel.GetPhyBand() == WIFI_PHY_BAND_2_4GHZ) ? 6 : 0;
388 uint8_t m = IsDlMu() ? 1 : 2;
389 // Equation 27-11 of IEEE P802.11ax/D4.0
390 const auto calculatedDuration =
391 MicroSeconds(((ceil(static_cast<double>(length + 3 + m) / 3)) * 4) + 20 + sigExtension);
392 NS_ASSERT(calculatedDuration > preambleDuration);
393 uint32_t nSymbols =
394 floor(static_cast<double>((calculatedDuration - preambleDuration).GetNanoSeconds() -
395 (sigExtension * 1000)) /
396 tSymbol.GetNanoSeconds());
397 return (preambleDuration + (nSymbols * tSymbol) + MicroSeconds(sigExtension));
398}
399
401HePpdu::Copy() const
402{
403 return Ptr<WifiPpdu>(new HePpdu(*this), false);
404}
405
407HePpdu::GetType() const
408{
409 switch (m_preamble)
410 {
415 default:
416 return WIFI_PPDU_TYPE_SU;
417 }
418}
419
420bool
421HePpdu::IsMu() const
422{
423 return (IsDlMu() || IsUlMu());
424}
425
426bool
427HePpdu::IsDlMu() const
428{
429 return (m_preamble == WIFI_PREAMBLE_HE_MU);
430}
431
432bool
433HePpdu::IsUlMu() const
434{
435 return (m_preamble == WIFI_PREAMBLE_HE_TB);
436}
437
439HePpdu::GetPsdu(uint8_t bssColor, uint16_t staId /* = SU_STA_ID */) const
440{
441 if (!IsMu())
442 {
443 NS_ASSERT(m_psdus.size() == 1);
444 return m_psdus.at(SU_STA_ID);
445 }
446
447 if (IsUlMu())
448 {
449 auto heSigHeader = std::get_if<HeTbSigHeader>(&m_heSig);
450 NS_ASSERT(heSigHeader);
451 NS_ASSERT(m_psdus.size() == 1);
452 if ((bssColor == 0) || (heSigHeader->m_bssColor == 0) ||
453 (bssColor == heSigHeader->m_bssColor))
454 {
455 return m_psdus.cbegin()->second;
456 }
457 }
458 else
459 {
460 auto heSigHeader = std::get_if<HeMuSigHeader>(&m_heSig);
461 NS_ASSERT(heSigHeader);
462 if ((bssColor == 0) || (heSigHeader->m_bssColor == 0) ||
463 (bssColor == heSigHeader->m_bssColor))
464 {
465 const auto it = m_psdus.find(staId);
466 if (it != m_psdus.cend())
467 {
468 return it->second;
469 }
470 }
471 }
472 return nullptr;
473}
474
475uint16_t
476HePpdu::GetStaId() const
477{
478 NS_ASSERT(IsUlMu());
479 return m_psdus.begin()->first;
480}
481
482MHz_u
483HePpdu::GetTxChannelWidth() const
484{
485 if (const auto& txVector = GetTxVector();
486 txVector.IsValid() && txVector.IsUlMu() && GetStaId() != SU_STA_ID)
487 {
488 TxPsdFlag flag = GetTxPsdFlag();
489 const auto ruWidth = WifiRu::GetBandwidth(WifiRu::GetRuType(txVector.GetRu(GetStaId())));
490 MHz_u channelWidth =
491 (flag == PSD_NON_HE_PORTION && ruWidth < MHz_u{20}) ? MHz_u{20} : ruWidth;
492 NS_LOG_INFO("Use " << channelWidth << " MHz for TB PPDU from " << GetStaId() << " for "
493 << flag);
494 return channelWidth;
495 }
496 else
497 {
498 return OfdmPpdu::GetTxChannelWidth();
499 }
500}
501
503HePpdu::GetTxPsdFlag() const
504{
505 return m_txPsdFlag;
506}
507
508void
509HePpdu::SetTxPsdFlag(TxPsdFlag flag) const
510{
511 NS_LOG_FUNCTION(this << flag);
512 m_txPsdFlag = flag;
513}
514
515void
516HePpdu::UpdateTxVectorForUlMu(const std::optional<WifiTxVector>& trigVector) const
517{
518 if (trigVector.has_value())
519 {
520 NS_LOG_FUNCTION(this << trigVector.value());
521 }
522 else
523 {
524 NS_LOG_FUNCTION(this);
525 }
526 if (!m_txVector.has_value())
527 {
528 m_txVector = GetTxVector();
529 }
530 NS_ASSERT(GetModulation() >= WIFI_MOD_CLASS_HE);
531 NS_ASSERT(GetType() == WIFI_PPDU_TYPE_UL_MU);
532 // HE TB PPDU reception needs information from the TRIGVECTOR to be able to receive the PPDU
533 const auto staId = GetStaId();
534 if (trigVector.has_value() && trigVector->IsUlMu() &&
535 (trigVector->GetHeMuUserInfoMap().contains(staId)))
536 {
537 // These information are not carried in HE-SIG-A for a HE TB PPDU,
538 // but they are carried in the Trigger frame soliciting the HE TB PPDU
539 m_txVector->SetGuardInterval(trigVector->GetGuardInterval());
540 m_txVector->SetHeMuUserInfo(staId, trigVector->GetHeMuUserInfo(staId));
541 }
542 else
543 {
544 // Set dummy user info, PPDU will be dropped later after decoding PHY headers.
545 m_txVector->SetHeMuUserInfo(
546 staId,
547 {HeRu::RuSpec{(WifiRu::GetRuType(m_txVector->GetChannelWidth())), 1, true}, 0, 1});
548 }
549}
550
551std::pair<std::size_t, std::size_t>
552HePpdu::GetNumRusPerHeSigBContentChannel(
553 MHz_u channelWidth,
555 const RuAllocation& ruAllocation,
556 std::optional<Center26ToneRuIndication> center26ToneRuIndication,
557 bool sigBCompression,
558 uint8_t numMuMimoUsers)
559{
560 std::pair<std::size_t /* number of RUs in content channel 1 */,
561 std::size_t /* number of RUs in content channel 2 */>
562 chSize{0, 0};
563
564 if (sigBCompression)
565 {
566 // If the HE-SIG-B Compression field in the HE-SIG-A field of an HE MU PPDU is 1,
567 // for bandwidths larger than 20 MHz, the AP performs an equitable split of
568 // the User fields between two HE-SIG-B content channels
569 if (channelWidth == MHz_u{20})
570 {
571 return {numMuMimoUsers, 0};
572 }
573 chSize.first = numMuMimoUsers / 2;
574 chSize.second = numMuMimoUsers / 2;
575 if (numMuMimoUsers != (chSize.first + chSize.second))
576 {
577 chSize.first++;
578 }
579 return chSize;
580 }
581
582 NS_ASSERT_MSG(!ruAllocation.empty(), "RU allocation is not set");
583 NS_ASSERT_MSG(ruAllocation.size() == Count20MHzSubchannels(channelWidth),
584 "RU allocation is not consistent with packet bandwidth");
585
586 switch (static_cast<uint16_t>(channelWidth))
587 {
588 case 40:
589 chSize.second += WifiRu::GetRuSpecs(ruAllocation[1], mc).size();
590 [[fallthrough]];
591 case 20:
592 chSize.first += WifiRu::GetRuSpecs(ruAllocation[0], mc).size();
593 break;
594 default:
595 for (std::size_t n = 0; n < Count20MHzSubchannels(channelWidth);)
596 {
597 std::size_t ccIndex;
598 const auto ruAlloc = ruAllocation.at(n);
599 std::size_t num20MHz{1};
600 const auto ruSpecs = WifiRu::GetRuSpecs(ruAlloc, mc);
601 const auto nRuSpecs = ruSpecs.size();
602 if (nRuSpecs == 1)
603 {
604 const auto ruBw = WifiRu::GetBandwidth(WifiRu::GetRuType(ruSpecs.front()));
605 num20MHz = Count20MHzSubchannels(ruBw);
606 }
607 if (nRuSpecs == 0)
608 {
609 ++n;
610 continue;
611 }
612 if (num20MHz > 1)
613 {
614 ccIndex = (chSize.first <= chSize.second) ? 0 : 1;
615 }
616 else
617 {
618 ccIndex = (n % 2 == 0) ? 0 : 1;
619 }
620 if (ccIndex == 0)
621 {
622 chSize.first += nRuSpecs;
623 }
624 else
625 {
626 chSize.second += nRuSpecs;
627 }
628 if (num20MHz > 1)
629 {
630 const auto skipNumIndices = (ccIndex == 0) ? num20MHz : num20MHz - 1;
631 n += skipNumIndices;
632 }
633 else
634 {
635 ++n;
636 }
637 }
638 break;
639 }
640 if (center26ToneRuIndication)
641 {
642 switch (*center26ToneRuIndication)
643 {
644 case Center26ToneRuIndication::CENTER_26_TONE_RU_LOW_80_MHZ_ALLOCATED:
645 chSize.first++;
646 break;
647 case Center26ToneRuIndication::CENTER_26_TONE_RU_HIGH_80_MHZ_ALLOCATED:
648 chSize.second++;
649 break;
650 case Center26ToneRuIndication::CENTER_26_TONE_RU_LOW_AND_HIGH_80_MHZ_ALLOCATED:
651 chSize.first++;
652 chSize.second++;
653 break;
654 case Center26ToneRuIndication::CENTER_26_TONE_RU_UNALLOCATED:
655 default:
656 break;
657 }
658 }
659 return chSize;
660}
661
663HePpdu::GetHeSigBContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
664{
665 HeSigBContentChannels contentChannels{{}};
666
667 const auto channelWidth = txVector.GetChannelWidth();
668 if (channelWidth > MHz_u{20})
669 {
670 contentChannels.emplace_back();
671 }
672
673 std::optional<HeSigBUserSpecificField> cc1Central26ToneRu;
674 std::optional<HeSigBUserSpecificField> cc2Central26ToneRu;
675
676 const auto& orderedMap = txVector.GetUserInfoMapOrderedByRus(p20Index);
677 RuType prevRuType{RuType::RU_TYPE_MAX};
678 std::size_t prevRuIndex{0};
679 std::size_t prevCcIndex{0};
680 for (const auto& [ru, staIds] : orderedMap)
681 {
682 const auto ruType = WifiRu::GetRuType(ru);
683 auto ruIdx = WifiRu::GetIndex(ru);
684 if ((ruType == RuType::RU_26_TONE) && (ruIdx == 19))
685 {
686 NS_ASSERT(WifiRu::IsHe(ru));
687 const auto staId = *staIds.cbegin();
688 const auto& userInfo = txVector.GetHeMuUserInfo(staId);
689 if (std::get<HeRu::RuSpec>(ru).GetPrimary80MHz())
690 {
691 NS_ASSERT(!cc1Central26ToneRu);
692 cc1Central26ToneRu = HeSigBUserSpecificField{staId, userInfo.nss, userInfo.mcs};
693 }
694 else
695 {
696 NS_ASSERT(!cc2Central26ToneRu);
697 cc2Central26ToneRu = HeSigBUserSpecificField{staId, userInfo.nss, userInfo.mcs};
698 }
699 continue;
700 }
701
702 const auto ruIndex = WifiRu::GetPhyIndex(ru, channelWidth, p20Index);
703 if ((prevRuType < RuType::RU_TYPE_MAX) && (prevRuType != ruType))
704 {
705 prevRuIndex *= WifiRu::GetBandwidth(prevRuType) / WifiRu::GetBandwidth(ruType);
706 }
707 if (ruType >= RuType::RU_484_TONE)
708 {
709 for (auto staId : staIds)
710 {
711 // equal split
712 const auto ccIndex =
713 (contentChannels.at(0).size() <= contentChannels.at(1).size()) ? 0 : 1;
714 const auto& userInfo = txVector.GetHeMuUserInfo(staId);
715 NS_ASSERT(ru == userInfo.ru);
716 contentChannels[ccIndex].push_back({staId, userInfo.nss, userInfo.mcs});
717 }
718 continue;
719 }
720
721 const auto mc = txVector.GetModulationClass();
722 const auto numRus = WifiRu::GetNRus(MHz_u{20}, ruType, mc);
723 while (prevRuIndex < ruIndex - 1)
724 {
725 std::size_t ccIndex{0};
726 if (channelWidth < MHz_u{40})
727 {
728 // only one content channel
729 ccIndex = 0;
730 }
731 else if (txVector.IsSigBCompression())
732 {
733 // equal split
734 ccIndex = (contentChannels.at(0).size() <= contentChannels.at(1).size()) ? 0 : 1;
735 }
736 else
737 {
738 ccIndex = ((prevRuIndex / numRus) % 2 == 0) ? 0 : 1;
739 }
740 const auto central26TonesRus =
741 WifiRu::GetCentral26TonesRus(channelWidth, prevRuType, mc);
742 const auto isCentral26ToneRu = std::none_of(
743 central26TonesRus.cbegin(),
744 central26TonesRus.cend(),
745 [ruIndex, channelWidth, p20Index](const auto& ruSpec) {
746 return WifiRu::GetPhyIndex(ruSpec, channelWidth, p20Index) == ruIndex;
747 });
748 if (ruType < RuType::RU_242_TONE && prevCcIndex == ccIndex &&
749 (ruType != RuType::RU_26_TONE || isCentral26ToneRu))
750 {
751 contentChannels[ccIndex].push_back({NO_USER_STA_ID, 0, 0});
752 }
753 ++prevRuIndex;
754 prevCcIndex = ccIndex;
755 }
756 prevRuIndex = ruIndex;
757 prevRuType = ruType;
758 for (auto staId : staIds)
759 {
760 const auto& userInfo = txVector.GetHeMuUserInfo(staId);
761 NS_ASSERT(ru == userInfo.ru);
762 std::size_t ccIndex{0};
763 if (channelWidth < MHz_u{40})
764 {
765 // only one content channel
766 ccIndex = 0;
767 }
768 else if (txVector.IsSigBCompression())
769 {
770 // MU-MIMO: equal split
771 ccIndex = (contentChannels.at(0).size() <= contentChannels.at(1).size()) ? 0 : 1;
772 }
773 else
774 {
775 if (ruType == RuType::RU_26_TONE && ruIdx > 19)
776 {
777 // "ignore" the center 26-tone RUs in 80 MHz channels
778 ruIdx--;
779 if (ruIdx > 37)
780 {
781 NS_ASSERT(!WifiRu::IsHe(ru));
782 ruIdx -= (ruIdx - 19) / 37;
783 }
784 }
785 ccIndex = (((ruIdx - 1) / numRus) % 2 == 0) ? 0 : 1;
786 }
787 contentChannels.at(ccIndex).push_back({staId, userInfo.nss, userInfo.mcs});
788 prevCcIndex = ccIndex;
789 }
790 }
791
792 if (cc1Central26ToneRu)
793 {
794 contentChannels.at(0).push_back(*cc1Central26ToneRu);
795 }
796 if (cc2Central26ToneRu)
797 {
798 contentChannels.at(1).push_back(*cc2Central26ToneRu);
799 }
800
801 const auto isSigBCompression = txVector.IsSigBCompression();
802 if (!isSigBCompression)
803 {
804 // Add unassigned RUs
805 auto numNumRusPerHeSigBContentChannel = GetNumRusPerHeSigBContentChannel(
806 channelWidth,
807 txVector.GetModulationClass(),
808 txVector.GetRuAllocation(p20Index),
810 isSigBCompression,
811 isSigBCompression ? txVector.GetHeMuUserInfoMap().size() : 0);
812 std::size_t contentChannelIndex = 1;
813 for (auto& contentChannel : contentChannels)
814 {
815 const auto totalUsersInContentChannel = (contentChannelIndex == 1)
816 ? numNumRusPerHeSigBContentChannel.first
817 : numNumRusPerHeSigBContentChannel.second;
818 NS_ASSERT(contentChannel.size() <= totalUsersInContentChannel);
819 std::size_t unallocatedRus = totalUsersInContentChannel - contentChannel.size();
820 for (std::size_t i = 0; i < unallocatedRus; i++)
821 {
822 contentChannel.push_back({NO_USER_STA_ID, 0, 0});
823 }
824 contentChannelIndex++;
825 }
826 }
827
828 return contentChannels;
829}
830
832HePpdu::GetSigBFieldSize(MHz_u channelWidth,
834 const RuAllocation& ruAllocation,
835 std::optional<Center26ToneRuIndication> center26ToneRuIndication,
836 bool sigBCompression,
837 std::size_t numMuMimoUsers)
838{
839 // Compute the number of bits used by common field.
840 uint32_t commonFieldSize = 0;
841 if (!sigBCompression)
842 {
843 commonFieldSize = 4 /* CRC */ + 6 /* tail */;
844 if (channelWidth <= MHz_u{40})
845 {
846 commonFieldSize += 8; // only one allocation subfield
847 }
848 else
849 {
850 commonFieldSize +=
851 8 * (channelWidth / MHz_u{40}) /* one allocation field per 40 MHz */ +
852 1 /* center RU */;
853 }
854 }
855
856 auto numRusPerContentChannel = GetNumRusPerHeSigBContentChannel(channelWidth,
857 mc,
858 ruAllocation,
859 center26ToneRuIndication,
860 sigBCompression,
861 numMuMimoUsers);
862 auto maxNumRusPerContentChannel =
863 std::max(numRusPerContentChannel.first, numRusPerContentChannel.second);
864 auto maxNumUserBlockFields = maxNumRusPerContentChannel /
865 2; // handle last user block with single user, if any, further down
866 std::size_t userSpecificFieldSize =
867 maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */);
868 if (maxNumRusPerContentChannel % 2 != 0)
869 {
870 userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */;
871 }
872
873 return commonFieldSize + userSpecificFieldSize;
874}
875
876std::string
877HePpdu::PrintPayload() const
878{
879 std::ostringstream ss;
880 if (IsMu())
881 {
882 ss << m_psdus;
883 ss << ", " << m_txPsdFlag;
884 }
885 else
886 {
887 ss << "PSDU=" << m_psdus.at(SU_STA_ID) << " ";
888 }
889 return ss.str();
890}
891
892uint8_t
893HePpdu::GetChannelWidthEncodingFromMhz(MHz_u channelWidth)
894{
895 if (channelWidth == MHz_u{160})
896 {
897 return 3;
898 }
899 else if (channelWidth == MHz_u{80})
900 {
901 return 2;
902 }
903 else if (channelWidth == MHz_u{40})
904 {
905 return 1;
906 }
907 else
908 {
909 return 0;
910 }
911}
912
913MHz_u
914HePpdu::GetChannelWidthMhzFromEncoding(uint8_t bandwidth)
915{
916 if (bandwidth == 3)
917 {
918 return MHz_u{160};
919 }
920 else if (bandwidth == 2)
921 {
922 return MHz_u{80};
923 }
924 else if (bandwidth == 1)
925 {
926 return MHz_u{40};
927 }
928 else
929 {
930 return MHz_u{20};
931 }
932}
933
934uint8_t
935HePpdu::GetGuardIntervalAndNltfEncoding(Time guardInterval, uint8_t nltf)
936{
937 const auto gi = guardInterval.GetNanoSeconds();
938 if ((gi == 800) && (nltf == 1))
939 {
940 return 0;
941 }
942 else if ((gi == 800) && (nltf == 2))
943 {
944 return 1;
945 }
946 else if ((gi == 1600) && (nltf == 2))
947 {
948 return 2;
949 }
950 else
951 {
952 return 3;
953 }
954}
955
956Time
957HePpdu::GetGuardIntervalFromEncoding(uint8_t giAndNltfSize)
958{
959 if (giAndNltfSize == 3)
960 {
961 // we currently do not consider DCM nor STBC fields
962 return NanoSeconds(3200);
963 }
964 else if (giAndNltfSize == 2)
965 {
966 return NanoSeconds(1600);
967 }
968 else
969 {
970 return NanoSeconds(800);
971 }
972}
973
974uint8_t
975HePpdu::GetNstsEncodingFromNss(uint8_t nss)
976{
977 NS_ASSERT(nss <= 8);
978 return nss - 1;
979}
980
981uint8_t
982HePpdu::GetNssFromNstsEncoding(uint8_t nsts)
983{
984 return nsts + 1;
985}
986
987uint8_t
988HePpdu::GetMuMimoUsersEncoding(uint8_t nUsers)
989{
990 NS_ASSERT(nUsers <= 8);
991 return (nUsers - 1);
992}
993
994uint8_t
995HePpdu::GetMuMimoUsersFromEncoding(uint8_t encoding)
996{
997 return (encoding + 1);
998}
999
1000} // namespace ns3
static WifiMode GetHeMcs(uint8_t index)
Return the HE MCS corresponding to the provided index.
Definition he-phy.cc:1555
HE PPDU (11ax)
Definition he-ppdu.h:39
HeSigHeader m_heSig
the HE-SIG PHY header
Definition he-ppdu.h:388
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
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:427
virtual bool IsUlMu() const
Return true if the PPDU is an UL MU PPDU.
Definition he-ppdu.cc:433
static uint8_t GetNstsEncodingFromNss(uint8_t nss)
Convert number of spatial streams to NSTS field encoding in HE-SIG-A.
Definition he-ppdu.cc:975
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:982
static Time GetGuardIntervalFromEncoding(uint8_t giAndNltfSize)
Convert guard interval from its encoding in HE-SIG-A.
Definition he-ppdu.cc:957
virtual 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:663
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:988
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:914
virtual bool IsMu() const
Return true if the PPDU is a MU PPDU.
Definition he-ppdu.cc:421
HePpdu(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector, const WifiPhyOperatingChannel &channel, Time ppduDuration, uint64_t uid, bool instantiateHeaders=true)
Create an SU HE PPDU, storing a PSDU.
Definition he-ppdu.cc:71
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:893
void SetLSigHeader(Time ppduDuration)
Fill in the L-SIG header.
Definition he-ppdu.cc:102
void SetHeMuUserInfos(WifiTxVector &txVector, WifiModulationClass mc, const RuAllocation &ruAllocation, std::optional< Center26ToneRuIndication > center26ToneRuIndication, const HeSigBContentChannels &contentChannels, bool sigBCompression, uint8_t numMuMimoUsers) const
Reconstruct HeMuUserInfoMap from HE-SIG-B header.
Definition he-ppdu.cc:251
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:935
static uint8_t GetMuMimoUsersFromEncoding(uint8_t encoding)
Convert number of MU-MIMO users from its encoding in HE-SIG-A.
Definition he-ppdu.cc:995
virtual WifiRu::RuSpec GetRuSpec(std::size_t ruAllocIndex, MHz_u bw, RuType ruType, std::size_t phyIndex) const
Get the RU specification that has been assigned a given user.
Definition he-ppdu.cc:227
RU Specification.
Definition he-ru.h:37
static std::vector< RuSpec > GetRusOfType(MHz_u bw, 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
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:101
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:324
uint8_t GetMcsValue() const
Definition wifi-mode.cc:151
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
std::variant< HeRu::RuSpec, EhtRu::RuSpec > RuSpec
variant of the RU specification
Definition wifi-ru.h:27
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.
WifiModulationClass GetModulationClass() const
Get the modulation class specified by this TXVECTOR.
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.
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:1369
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1381
Center26ToneRuIndication
Enum for the different values for CENTER_26_TONE_RU.
WifiPpduType
The type of PPDU (SU, DL MU, or UL MU)
WifiModulationClass
This enumeration defines the modulation classes per (Table 10-6 "Modulation classes"; IEEE 802....
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PREAMBLE_HE_MU
@ CENTER_26_TONE_RU_LOW_AND_HIGH_80_MHZ_ALLOCATED
@ CENTER_26_TONE_RU_HIGH_80_MHZ_ALLOCATED
@ CENTER_26_TONE_RU_LOW_80_MHZ_ALLOCATED
@ 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.
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition nstime.h:865
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
RuType
The different Resource Unit (RU) types.
Definition wifi-types.h:99
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
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:138
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
User Specific Fields in HE-SIG-Bs.
Definition he-ppdu.h:43
uint8_t nss
number of spatial streams
Definition he-ppdu.h:45
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