A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
eht-ppdu.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2021 DERONNE SOFTWARE ENGINEERING
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Sébastien Deronne <sebastien.deronne@gmail.com>
7 */
8
9#include "eht-ppdu.h"
10
11#include "eht-phy.h"
12
13#include "ns3/log.h"
14#include "ns3/wifi-phy-operating-channel.h"
15#include "ns3/wifi-psdu.h"
16
17#include <algorithm>
18#include <numeric>
19
20namespace ns3
21{
22
24
26 const WifiTxVector& txVector,
27 const WifiPhyOperatingChannel& channel,
28 Time ppduDuration,
29 uint64_t uid,
30 TxPsdFlag flag)
31 : HePpdu(psdus, txVector, channel, ppduDuration, uid, flag, false)
32{
33 NS_LOG_FUNCTION(this << psdus << txVector << channel << ppduDuration << uid << flag);
34 SetPhyHeaders(txVector, ppduDuration);
35}
36
37void
38EhtPpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
39{
40 NS_LOG_FUNCTION(this << txVector << ppduDuration);
41 SetLSigHeader(ppduDuration);
42 SetEhtPhyHeader(txVector);
43}
44
45void
47{
48 const auto bssColor = txVector.GetBssColor();
49 NS_ASSERT(bssColor < 64);
51 {
52 const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(MHz_u{20});
55 .m_bssColor = bssColor,
56 .m_ppduType = txVector.GetEhtPpduType(),
57 // TODO: EHT PPDU should store U-SIG per 20 MHz band, assume it is the lowest 20 MHz
58 // band for now
59 .m_puncturedChannelInfo =
61 txVector.GetEhtPpduType(),
62 (txVector.IsDlMu() && (txVector.GetChannelWidth() > MHz_u{80}))
63 ? std::optional{true}
64 : std::nullopt),
65 .m_ehtSigMcs = txVector.GetSigBMode().GetMcsValue(),
66 .m_giLtfSize = GetGuardIntervalAndNltfEncoding(txVector.GetGuardInterval(),
67 2 /*NLTF currently unused*/),
68 /* See section 36.3.12.8.2 of IEEE 802.11be D3.0 (EHT-SIG content channels):
69 * In non-OFDMA transmission, the Common field of the EHT-SIG content channel does not
70 * contain the RU Allocation subfield. For non-OFDMA transmission except for EHT
71 * sounding NDP, the Common field of the EHT-SIG content channel is encoded together
72 * with the first User field and this encoding block contains a CRC and Tail, referred
73 * to as a common encoding block. */
74 .m_ruAllocationA = txVector.IsMu() && !txVector.IsSigBCompression()
75 ? std::optional{txVector.GetRuAllocation(p20Index)}
76 : std::nullopt,
77 // TODO: RU Allocation-B not supported yet
78 .m_contentChannels = GetEhtSigContentChannels(txVector, p20Index)});
79 }
80 else if (ns3::IsUlMu(m_preamble))
81 {
84 .m_bssColor = bssColor,
85 .m_ppduType = txVector.GetEhtPpduType()});
86 }
87}
88
91{
92 if (m_psdus.contains(SU_STA_ID))
93 {
94 return WIFI_PPDU_TYPE_SU;
95 }
96 switch (m_preamble)
97 {
102 default:
103 NS_ASSERT_MSG(false, "invalid preamble " << m_preamble);
104 return WIFI_PPDU_TYPE_SU;
105 }
106}
107
108bool
110{
111 return (m_preamble == WIFI_PREAMBLE_EHT_MU) && !m_psdus.contains(SU_STA_ID);
112}
113
114bool
116{
117 return (m_preamble == WIFI_PREAMBLE_EHT_TB) && !m_psdus.contains(SU_STA_ID);
118}
119
120void
122{
123 txVector.SetLength(m_lSig.GetLength());
124 txVector.SetAggregation(m_psdus.size() > 1 || m_psdus.begin()->second->IsAggregate());
126 {
127 auto ehtPhyHeader = std::get_if<EhtMuPhyHeader>(&m_ehtPhyHeader);
128 NS_ASSERT(ehtPhyHeader);
129 const auto bw = GetChannelWidthMhzFromEncoding(ehtPhyHeader->m_bandwidth);
130 txVector.SetChannelWidth(bw);
131 txVector.SetBssColor(ehtPhyHeader->m_bssColor);
132 txVector.SetEhtPpduType(ehtPhyHeader->m_ppduType);
133 if (bw > MHz_u{80})
134 {
135 // TODO: use punctured channel information
136 }
137 txVector.SetSigBMode(EhtPhy::GetVhtMcs(ehtPhyHeader->m_ehtSigMcs));
138 txVector.SetGuardInterval(GetGuardIntervalFromEncoding(ehtPhyHeader->m_giLtfSize));
139 const auto ruAllocation = ehtPhyHeader->m_ruAllocationA; // RU Allocation-B not supported
140 // yet
141 if (const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(MHz_u{20});
142 ruAllocation.has_value())
143 {
144 txVector.SetRuAllocation(ruAllocation.value(), p20Index);
145 const auto isMuMimo = (ehtPhyHeader->m_ppduType == 2);
146 const auto muMimoUsers =
147 isMuMimo
148 ? std::accumulate(ehtPhyHeader->m_contentChannels.cbegin(),
149 ehtPhyHeader->m_contentChannels.cend(),
150 0,
151 [](uint8_t prev, const auto& cc) { return prev + cc.size(); })
152 : 0;
153 SetHeMuUserInfos(txVector,
154 ruAllocation.value(),
155 std::nullopt,
156 ehtPhyHeader->m_contentChannels,
157 ehtPhyHeader->m_ppduType == 2,
158 muMimoUsers);
159 }
160 else if (ehtPhyHeader->m_ppduType == 1) // EHT SU
161 {
162 NS_ASSERT(ehtPhyHeader->m_contentChannels.size() == 1 &&
163 ehtPhyHeader->m_contentChannels.front().size() == 1);
164 txVector.SetMode(
165 EhtPhy::GetEhtMcs(ehtPhyHeader->m_contentChannels.front().front().mcs));
166 txVector.SetNss(ehtPhyHeader->m_contentChannels.front().front().nss);
167 }
168 else
169 {
170 const auto fullBwRu{HeRu::RuSpec(WifiRu::GetRuType(bw), 1, true)};
171 txVector.SetHeMuUserInfo(ehtPhyHeader->m_contentChannels.front().front().staId,
172 {fullBwRu,
173 ehtPhyHeader->m_contentChannels.front().front().mcs,
174 ehtPhyHeader->m_contentChannels.front().front().nss});
175 }
176 }
177 else if (ns3::IsUlMu(m_preamble))
178 {
179 auto ehtPhyHeader = std::get_if<EhtTbPhyHeader>(&m_ehtPhyHeader);
180 NS_ASSERT(ehtPhyHeader);
181 txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(ehtPhyHeader->m_bandwidth));
182 txVector.SetBssColor(ehtPhyHeader->m_bssColor);
183 txVector.SetEhtPpduType(ehtPhyHeader->m_ppduType);
184 }
185}
186
187std::pair<std::size_t, std::size_t>
189 uint8_t ehtPpduType,
190 const RuAllocation& ruAllocation,
191 bool compression,
192 std::size_t numMuMimoUsers)
193{
194 if (ehtPpduType == 1)
195 {
196 return {1, 0};
197 }
199 ruAllocation,
200 std::nullopt,
201 compression,
202 numMuMimoUsers);
203}
204
206EhtPpdu::GetEhtSigContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
207{
208 if (txVector.GetEhtPpduType() == 1)
209 {
210 // according to spec the TXVECTOR shall have a correct STA-ID even for SU transmission,
211 // but this is not set by the MAC for simplification, so set to 0 for now.
212 return HeSigBContentChannels{{{0, txVector.GetNss(), txVector.GetMode().GetMcsValue()}}};
213 }
214 return HePpdu::GetHeSigBContentChannels(txVector, p20Index);
215}
216
219 const RuAllocation& ruAllocation,
220 uint8_t ehtPpduType,
221 bool compression,
222 std::size_t numMuMimoUsers)
223{
224 // FIXME: EHT-SIG is not implemented yet, hence this is a copy of HE-SIG-B
225 uint32_t commonFieldSize = 0;
226 if (!compression)
227 {
228 commonFieldSize = 4 /* CRC */ + 6 /* tail */;
229 if (channelWidth <= MHz_u{40})
230 {
231 commonFieldSize += 8; // only one allocation subfield
232 }
233 else
234 {
235 commonFieldSize +=
236 8 * (channelWidth / MHz_u{40}) /* one allocation field per 40 MHz */ +
237 1 /* center RU */;
238 }
239 }
240
241 auto numRusPerContentChannel = GetNumRusPerEhtSigBContentChannel(channelWidth,
242 ehtPpduType,
243 ruAllocation,
244 compression,
245 numMuMimoUsers);
246 auto maxNumRusPerContentChannel =
247 std::max(numRusPerContentChannel.first, numRusPerContentChannel.second);
248 auto maxNumUserBlockFields = maxNumRusPerContentChannel /
249 2; // handle last user block with single user, if any, further down
250 std::size_t userSpecificFieldSize =
251 maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */);
252 if (maxNumRusPerContentChannel % 2 != 0)
253 {
254 userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */;
255 }
256
257 return commonFieldSize + userSpecificFieldSize;
258}
259
260uint8_t
261EhtPpdu::GetPuncturedInfo(const std::vector<bool>& inactiveSubchannels,
262 uint8_t ehtPpduType,
263 std::optional<bool> isLow80MHz)
264{
265 if (inactiveSubchannels.size() < 4)
266 {
267 // no puncturing if less than 80 MHz
268 return 0;
269 }
270 NS_ASSERT_MSG(inactiveSubchannels.size() <= 8,
271 "Puncturing over more than 160 MHz is not supported");
272 if (ehtPpduType == 0)
273 {
274 // IEEE 802.11be D5.0 Table 36-28
275 NS_ASSERT(inactiveSubchannels.size() <= 4 || isLow80MHz.has_value());
276 const auto startIndex = (inactiveSubchannels.size() <= 4) ? 0 : (*isLow80MHz ? 0 : 4);
277 const auto stopIndex =
278 (inactiveSubchannels.size() <= 4) ? inactiveSubchannels.size() : (*isLow80MHz ? 4 : 8);
279 uint8_t puncturedInfoField = 0;
280 for (std::size_t i = startIndex; i < stopIndex; ++i)
281 {
282 if (!inactiveSubchannels.at(i))
283 {
284 puncturedInfoField |= 1 << (i / 4);
285 }
286 }
287 return puncturedInfoField;
288 }
289 // IEEE 802.11be D5.0 Table 36-30
290 const auto numPunctured = std::count_if(inactiveSubchannels.cbegin(),
291 inactiveSubchannels.cend(),
292 [](bool punctured) { return punctured; });
293 if (numPunctured == 0)
294 {
295 // no puncturing
296 return 0;
297 }
298 const auto firstPunctured = std::find_if(inactiveSubchannels.cbegin(),
299 inactiveSubchannels.cend(),
300 [](bool punctured) { return punctured; });
301 const auto firstIndex = std::distance(inactiveSubchannels.cbegin(), firstPunctured);
302 switch (numPunctured)
303 {
304 case 1:
305 return firstIndex + 1;
306 case 2:
307 NS_ASSERT_MSG(((firstIndex % 2) == 0) && inactiveSubchannels.at(firstIndex + 1),
308 "invalid 40 MHz puncturing pattern");
309 return 9 + (firstIndex / 2);
310 default:
311 break;
312 }
313 NS_ASSERT_MSG(false, "invalid puncturing pattern");
314 return 0;
315}
316
318EhtPpdu::GetPsdu(uint8_t bssColor, uint16_t staId /* = SU_STA_ID */) const
319{
320 if (m_psdus.contains(SU_STA_ID))
321 {
322 NS_ASSERT(m_psdus.size() == 1);
323 return m_psdus.at(SU_STA_ID);
324 }
325
326 if (IsUlMu())
327 {
328 auto ehtPhyHeader = std::get_if<EhtTbPhyHeader>(&m_ehtPhyHeader);
329 NS_ASSERT(ehtPhyHeader);
330 NS_ASSERT(m_psdus.size() == 1);
331 if ((bssColor == 0) || (ehtPhyHeader->m_bssColor == 0) ||
332 (bssColor == ehtPhyHeader->m_bssColor))
333 {
334 return m_psdus.cbegin()->second;
335 }
336 }
337 else if (IsDlMu())
338 {
339 auto ehtPhyHeader = std::get_if<EhtMuPhyHeader>(&m_ehtPhyHeader);
340 NS_ASSERT(ehtPhyHeader);
341 if ((bssColor == 0) || (ehtPhyHeader->m_bssColor == 0) ||
342 (bssColor == ehtPhyHeader->m_bssColor))
343 {
344 const auto it = m_psdus.find(staId);
345 if (it != m_psdus.cend())
346 {
347 return it->second;
348 }
349 }
350 }
351
352 return nullptr;
353}
354
357{
358 return Ptr<WifiPpdu>(new EhtPpdu(*this), false);
359}
360
361} // namespace ns3
static WifiMode GetEhtMcs(uint8_t index)
Return the EHT MCS corresponding to the provided index.
Definition eht-phy.cc:244
static uint32_t GetEhtSigFieldSize(MHz_u channelWidth, const RuAllocation &ruAllocation, uint8_t ehtPpduType, bool compression, std::size_t numMuMimoUsers)
Get variable length EHT-SIG field size.
Definition eht-ppdu.cc:218
static HeSigBContentChannels GetEhtSigContentChannels(const WifiTxVector &txVector, uint8_t p20Index)
Get the EHT-SIG content channels for a given PPDU IEEE 802.11be-D3.1 36.3.12.8.2 EHT-SIG content chan...
Definition eht-ppdu.cc:206
bool IsDlMu() const override
Return true if the PPDU is a DL MU PPDU.
Definition eht-ppdu.cc:109
void SetPhyHeaders(const WifiTxVector &txVector, Time ppduDuration) override
Fill in the PHY headers.
Definition eht-ppdu.cc:38
bool IsUlMu() const override
Return true if the PPDU is an UL MU PPDU.
Definition eht-ppdu.cc:115
void SetEhtPhyHeader(const WifiTxVector &txVector)
Fill in the EHT PHY header.
Definition eht-ppdu.cc:46
static std::pair< std::size_t, std::size_t > GetNumRusPerEhtSigBContentChannel(MHz_u channelWidth, uint8_t ehtPpduType, const RuAllocation &ruAllocation, bool compression, std::size_t numMuMimoUsers)
Get the number of RUs per EHT-SIG-B content channel.
Definition eht-ppdu.cc:188
Ptr< WifiPpdu > Copy() const override
Copy this instance.
Definition eht-ppdu.cc:356
void SetTxVectorFromPhyHeaders(WifiTxVector &txVector) const override
Fill in the TXVECTOR from PHY headers.
Definition eht-ppdu.cc:121
WifiPpduType GetType() const override
Return the PPDU type (.
Definition eht-ppdu.cc:90
static uint8_t GetPuncturedInfo(const std::vector< bool > &inactiveSubchannels, uint8_t ehtPpduType, std::optional< bool > isLow80MHz)
Get the Punctured Channel Information field in the U-SIG.
Definition eht-ppdu.cc:261
EhtPhyHeader m_ehtPhyHeader
the EHT PHY header
Definition eht-ppdu.h:174
EhtPpdu(const WifiConstPsduMap &psdus, const WifiTxVector &txVector, const WifiPhyOperatingChannel &channel, Time ppduDuration, uint64_t uid, TxPsdFlag flag)
Create an EHT PPDU, storing a map of PSDUs.
Definition eht-ppdu.cc:25
HE PPDU (11ax)
Definition he-ppdu.h:39
TxPsdFlag
The transmit power spectral density flag, namely used to correctly build PSDs for pre-HE and HE porti...
Definition he-ppdu.h:104
void SetHeMuUserInfos(WifiTxVector &txVector, 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:259
static Time GetGuardIntervalFromEncoding(uint8_t giAndNltfSize)
Convert guard interval from its encoding in HE-SIG-A.
Definition he-ppdu.cc:951
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:665
std::vector< std::vector< HeSigBUserSpecificField > > HeSigBContentChannels
HE SIG-B Content Channels.
Definition he-ppdu.h:50
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:908
static std::pair< std::size_t, std::size_t > GetNumRusPerHeSigBContentChannel(MHz_u channelWidth, const RuAllocation &ruAllocation, std::optional< Center26ToneRuIndication > center26ToneRuIndication, bool sigBCompression, uint8_t numMuMimoUsers)
Get the number of STAs per HE-SIG-B content channel.
Definition he-ppdu.cc:554
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:887
void SetLSigHeader(Time ppduDuration)
Fill in the L-SIG header.
Definition he-ppdu.cc:102
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:929
RU Specification.
Definition he-ru.h:37
uint16_t GetLength() const
Return the LENGTH field of L-SIG (in bytes).
Definition ofdm-ppdu.cc:199
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
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:151
Class that keeps track of all information about the current PHY operating channel.
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...
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
Ptr< const WifiPsdu > GetPsdu() const
Get the payload of the PPDU.
Definition wifi-ppdu.cc:142
WifiConstPsduMap m_psdus
the PSDUs contained in this PPDU
Definition wifi-ppdu.h:194
static RuType GetRuType(RuSpec ru)
Get the type of a given RU.
Definition wifi-ru.cc:45
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void SetRuAllocation(const RuAllocation &ruAlloc, uint8_t p20Index)
Set RU_ALLOCATION field.
void SetEhtPpduType(uint8_t type)
Set the EHT_PPDU_TYPE parameter.
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)
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set 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.
uint8_t GetEhtPpduType() const
Get the EHT_PPDU_TYPE parameter.
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.
const std::vector< bool > & GetInactiveSubchannels() const
Get the 20 MHz subchannels that are punctured.
Declaration of ns3::EhtPhy class.
Declaration of ns3::EhtPpdu class.
#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_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 ",...
WifiPpduType
The type of PPDU (SU, DL MU, or UL MU)
@ WIFI_PREAMBLE_EHT_TB
@ WIFI_PREAMBLE_EHT_MU
@ WIFI_PPDU_TYPE_DL_MU
@ WIFI_PPDU_TYPE_UL_MU
@ WIFI_PPDU_TYPE_SU
Every class exported by the ns3 library is enclosed in the ns3 namespace.
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
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition wifi-mode.h:24
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
PHY header for EHT MU PPDUs.
Definition eht-ppdu.h:50
uint8_t m_bandwidth
Bandwidth field.
Definition eht-ppdu.h:53
PHY header for EHT TB PPDUs.
Definition eht-ppdu.h:38
uint8_t m_bandwidth
Bandwidth field.
Definition eht-ppdu.h:41
uint32_t prev