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 * 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
34namespace ns3
35{
36
38
39std::ostream&
40operator<<(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,
56 const WifiPhyOperatingChannel& channel,
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,
78 const WifiPhyOperatingChannel& channel,
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
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(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);
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,
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
224void
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
313Time
314HePpdu::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
336HePpdu::Copy() const
337{
338 return Ptr<WifiPpdu>(new HePpdu(*this), false);
339}
340
342HePpdu::GetType() const
343{
344 switch (m_preamble)
345 {
350 default:
351 return WIFI_PPDU_TYPE_SU;
352 }
353}
354
355bool
356HePpdu::IsMu() const
357{
358 return (IsDlMu() || IsUlMu());
359}
360
361bool
362HePpdu::IsDlMu() const
363{
364 return (m_preamble == WIFI_PREAMBLE_HE_MU);
365}
366
367bool
368HePpdu::IsUlMu() const
369{
370 return (m_preamble == WIFI_PREAMBLE_HE_TB);
371}
372
374HePpdu::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
410uint16_t
411HePpdu::GetStaId() const
412{
413 NS_ASSERT(IsUlMu());
414 return m_psdus.begin()->first;
415}
416
417uint16_t
418HePpdu::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
437HePpdu::GetTxPsdFlag() const
438{
439 return m_txPsdFlag;
440}
441
442void
443HePpdu::SetTxPsdFlag(TxPsdFlag flag) const
444{
445 NS_LOG_FUNCTION(this << flag);
446 m_txPsdFlag = flag;
447}
448
449void
450HePpdu::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().contains(staId)))
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
485std::pair<std::size_t, std::size_t>
486HePpdu::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
554HePpdu::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
639HePpdu::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
678std::string
679HePpdu::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
694uint8_t
695HePpdu::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
715uint16_t
716HePpdu::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
736uint8_t
737HePpdu::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
757uint16_t
758HePpdu::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
775uint8_t
776HePpdu::GetNstsEncodingFromNss(uint8_t nss)
777{
778 NS_ASSERT(nss <= 8);
779 return nss - 1;
780}
781
782uint8_t
783HePpdu::GetNssFromNstsEncoding(uint8_t nsts)
784{
785 return nsts + 1;
786}
787
788uint8_t
789HePpdu::GetMuMimoUsersEncoding(uint8_t nUsers)
790{
791 NS_ASSERT(nUsers <= 8);
792 return (nUsers - 1);
793}
794
795uint8_t
796HePpdu::GetMuMimoUsersFromEncoding(uint8_t encoding)
797{
798 return (encoding + 1);
799}
800
801} // namespace ns3
static WifiMode GetHeMcs(uint8_t index)
Return the HE MCS corresponding to the provided index.
Definition: he-phy.cc:1549
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:767
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:515
@ 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
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
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:345
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:1343
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1355
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1319
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.
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.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition: angles.cc:159
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.
bool IsUlMu(WifiPreamble preamble)
Return true if a preamble corresponds to a uplink multi-user transmission.
Definition: second.py:1
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