diff --git a/src/wifi/model/eht/eht-phy.cc b/src/wifi/model/eht/eht-phy.cc index fe8a87765..52bc325bb 100644 --- a/src/wifi/model/eht/eht-phy.cc +++ b/src/wifi/model/eht/eht-phy.cc @@ -129,6 +129,18 @@ EhtPhy::GetDuration(WifiPpduField field, const WifiTxVector& txVector) const } } +uint32_t +EhtPhy::GetSigBSize(const WifiTxVector& txVector) const +{ + if (ns3::IsDlMu(txVector.GetPreambleType()) && ns3::IsEht(txVector.GetPreambleType())) + { + return EhtPpdu::GetEhtSigFieldSize(txVector.GetChannelWidth(), + txVector.GetRuAllocation(), + txVector.GetEhtPpduType()); + } + return HePhy::GetSigBSize(txVector); +} + Time EhtPhy::CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const { diff --git a/src/wifi/model/eht/eht-phy.h b/src/wifi/model/eht/eht-phy.h index bf83e3120..3450e71b1 100644 --- a/src/wifi/model/eht/eht-phy.h +++ b/src/wifi/model/eht/eht-phy.h @@ -262,6 +262,7 @@ class EhtPhy : public HePhy WifiPhyRxfailureReason GetFailureReason(WifiPpduField field) const override; Time CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const override; Time CalculateNonOfdmaDurationForHeMu(const WifiTxVector& txVector) const override; + uint32_t GetSigBSize(const WifiTxVector& txVector) const override; /** * Create and return the EHT MCS corresponding to diff --git a/src/wifi/model/eht/eht-ppdu.cc b/src/wifi/model/eht/eht-ppdu.cc index bef4bb27a..7e1d18848 100644 --- a/src/wifi/model/eht/eht-ppdu.cc +++ b/src/wifi/model/eht/eht-ppdu.cc @@ -52,6 +52,7 @@ EhtPpdu::EhtPpdu(const WifiConstPsduMap& psdus, m_ehtSuMcs = txVector.GetMode().GetMcsValue(); m_ehtSuNStreams = txVector.GetNss(); } + m_ehtPpduType = txVector.GetEhtPpduType(); } WifiPpduType @@ -97,10 +98,7 @@ EhtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector, txVector.SetBssColor(heSig.GetBssColor()); txVector.SetLength(lSig.GetLength()); txVector.SetAggregation(m_psdus.size() > 1 || m_psdus.begin()->second->IsAggregate()); - if (!m_muUserInfos.empty()) - { - txVector.SetEhtPpduType(0); // FIXME set to 2 for DL MU-MIMO (non-OFDMA) transmission - } + txVector.SetEhtPpduType(m_ehtPpduType); // FIXME: PPDU type should be read from U-SIG for (const auto& muUserInfo : m_muUserInfos) { txVector.SetHeMuUserInfo(muUserInfo.first, muUserInfo.second); @@ -112,6 +110,51 @@ EhtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector, } } +std::pair +EhtPpdu::GetNumRusPerEhtSigBContentChannel(uint16_t channelWidth, + uint8_t ehtPpduType, + const std::vector& ruAllocation) +{ + if (ehtPpduType == 1) + { + return {1, 0}; + } + return HePpdu::GetNumRusPerHeSigBContentChannel(channelWidth, ruAllocation); +} + +uint32_t +EhtPpdu::GetEhtSigFieldSize(uint16_t channelWidth, + const RuAllocation& ruAllocation, + uint8_t ehtPpduType) +{ + // FIXME: EHT-SIG is not implemented yet, hence this is a copy of HE-SIG-B + auto commonFieldSize = 4 /* CRC */ + 6 /* tail */; + if (channelWidth <= 40) + { + commonFieldSize += 8; // only one allocation subfield + } + else + { + commonFieldSize += + 8 * (channelWidth / 40) /* one allocation field per 40 MHz */ + 1 /* center RU */; + } + + auto numStaPerContentChannel = + GetNumRusPerEhtSigBContentChannel(channelWidth, ehtPpduType, ruAllocation); + auto maxNumStaPerContentChannel = + std::max(numStaPerContentChannel.first, numStaPerContentChannel.second); + auto maxNumUserBlockFields = maxNumStaPerContentChannel / + 2; // handle last user block with single user, if any, further down + std::size_t userSpecificFieldSize = + maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */); + if (maxNumStaPerContentChannel % 2 != 0) + { + userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */; + } + + return commonFieldSize + userSpecificFieldSize; +} + Ptr EhtPpdu::Copy() const { diff --git a/src/wifi/model/eht/eht-ppdu.h b/src/wifi/model/eht/eht-ppdu.h index d0f7f464d..e6b4dc107 100644 --- a/src/wifi/model/eht/eht-ppdu.h +++ b/src/wifi/model/eht/eht-ppdu.h @@ -62,6 +62,32 @@ class EhtPpdu : public HePpdu WifiPpduType GetType() const override; Ptr Copy() const override; + /** + * Get the number of RUs per EHT-SIG-B content channel. + * This function will be used once EHT PHY headers are implemented. + * + * \param channelWidth the channel width occupied by the PPDU (in MHz) + * \param ehtPpduType the EHT_PPDU_TYPE used by the PPDU + * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz + + * \return a pair containing the number of RUs in each EHT-SIG-B content channel (resp. 1 and 2) + */ + static std::pair GetNumRusPerEhtSigBContentChannel( + uint16_t channelWidth, + uint8_t ehtPpduType, + const std::vector& ruAllocation); + + /** + * Get variable length EHT-SIG field size + * \param channelWidth the channel width occupied by the PPDU (in MHz) + * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz + * \param ehtPpduType the EHT_PPDU_TYPE used by the PPDU + * \return field size in bytes + */ + static uint32_t GetEhtSigFieldSize(uint16_t channelWidth, + const std::vector& ruAllocation, + uint8_t ehtPpduType); + private: bool IsDlMu() const override; bool IsUlMu() const override; @@ -71,6 +97,8 @@ class EhtPpdu : public HePpdu uint8_t m_ehtSuMcs{0}; //!< EHT-MCS for EHT SU transmissions uint8_t m_ehtSuNStreams{1}; //!< Number of streams for EHT SU transmissions + uint8_t m_ehtPpduType{1}; /**< EHT_PPDU_TYPE per Table 36-1 IEEE 802.11be D2.3. + To be removed once EHT PHY headers are supported. */ }; // class EhtPpdu } // namespace ns3 diff --git a/src/wifi/model/he/he-phy.cc b/src/wifi/model/he/he-phy.cc index 5a2f7c917..cff1c577e 100644 --- a/src/wifi/model/he/he-phy.cc +++ b/src/wifi/model/he/he-phy.cc @@ -216,14 +216,22 @@ HePhy::GetSigADuration(WifiPreamble preamble) const : MicroSeconds(8); // HE-SIG-A (first and second symbol) } +uint32_t +HePhy::GetSigBSize(const WifiTxVector& txVector) const +{ + if (ns3::IsDlMu(txVector.GetPreambleType())) + { + NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE); + return HePpdu::GetSigBFieldSize(txVector.GetChannelWidth(), txVector.GetRuAllocation()); + } + return 0; +} + Time HePhy::GetSigBDuration(const WifiTxVector& txVector) const { - if (ns3::IsDlMu(txVector.GetPreambleType())) // See section 27.3.11.8 of IEEE 802.11ax-2021 + if (auto sigBSize = GetSigBSize(txVector); sigBSize > 0) { - NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE); - - auto sigBSize = GetSigBFieldSize(txVector); auto symbolDuration = MicroSeconds(4); // Number of data bits per symbol auto ndbps = @@ -1779,41 +1787,6 @@ HePhy::GetMaxPsduSize() const return 6500631; } -uint32_t -HePhy::GetSigBFieldSize(const WifiTxVector& txVector) -{ - NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE); - NS_ASSERT(ns3::IsDlMu(txVector.GetPreambleType())); - - // Compute the number of bits used by common field. - // Assume that compression bit in HE-SIG-A is not set (i.e. not - // full band MU-MIMO); the field is present. - auto bw = txVector.GetChannelWidth(); - auto commonFieldSize = 4 /* CRC */ + 6 /* tail */; - if (bw <= 40) - { - commonFieldSize += 8; // only one allocation subfield - } - else - { - commonFieldSize += 8 * (bw / 40) /* one allocation field per 40 MHz */ + 1 /* center RU */; - } - - auto numStaPerContentChannel = txVector.GetNumRusPerHeSigBContentChannel(); - auto maxNumStaPerContentChannel = - std::max(numStaPerContentChannel.first, numStaPerContentChannel.second); - auto maxNumUserBlockFields = maxNumStaPerContentChannel / - 2; // handle last user block with single user, if any, further down - std::size_t userSpecificFieldSize = - maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */); - if (maxNumStaPerContentChannel % 2 != 0) - { - userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */; - } - - return commonFieldSize + userSpecificFieldSize; -} - bool HePhy::CanStartRx(Ptr ppdu, uint16_t txChannelWidth) const { diff --git a/src/wifi/model/he/he-phy.h b/src/wifi/model/he/he-phy.h index 08d60b572..b75579c64 100644 --- a/src/wifi/model/he/he-phy.h +++ b/src/wifi/model/he/he-phy.h @@ -423,13 +423,6 @@ class HePhy : public VhtPhy */ static bool IsAllowed(const WifiTxVector& txVector); - /** - * Get variable length HE SIG-B field size based on TX Vector - * \param txVector WiFi TX Vector - * \return field size in bytes - */ - static uint32_t GetSigBFieldSize(const WifiTxVector& txVector); - protected: PhyFieldRxStatus ProcessSig(Ptr event, PhyFieldRxStatus status, @@ -479,6 +472,12 @@ class HePhy : public VhtPhy */ virtual PhyFieldRxStatus ProcessSigB(Ptr event, PhyFieldRxStatus status); + /** + * \param txVector the transmission parameters + * \return the number of bits of the HE-SIG-B + */ + virtual uint32_t GetSigBSize(const WifiTxVector& txVector) const; + /** * Start receiving the PSDU (i.e. the first symbol of the PSDU has arrived) of an OFDMA * transmission. This function is called upon the RX event corresponding to the OFDMA part of diff --git a/src/wifi/model/he/he-ppdu.cc b/src/wifi/model/he/he-ppdu.cc index be4d932d5..c52922ef7 100644 --- a/src/wifi/model/he/he-ppdu.cc +++ b/src/wifi/model/he/he-ppdu.cc @@ -425,19 +425,75 @@ HePpdu::UpdateTxVectorForUlMu(const std::optional& trigVector) con } } -bool -HePpdu::IsAllocated(uint16_t staId) const +std::pair +HePpdu::GetNumRusPerHeSigBContentChannel(uint16_t channelWidth, + const std::vector& ruAllocation) { - return (m_muUserInfos.find(staId) != m_muUserInfos.cend()); + // MU-MIMO is not handled for now, i.e. one station per RU + NS_ASSERT_MSG(!ruAllocation.empty(), "RU allocation is not set"); + NS_ASSERT_MSG(ruAllocation.size() == channelWidth / 20, + "RU allocation is not consistent with packet bandwidth"); + + std::pair + chSize{0, 0}; + + switch (channelWidth) + { + case 40: + chSize.second += HeRu::GetRuSpecs(ruAllocation[1]).size(); + [[fallthrough]]; + case 20: + chSize.first += HeRu::GetRuSpecs(ruAllocation[0]).size(); + break; + default: + for (auto n = 0; n < channelWidth / 20;) + { + chSize.first += HeRu::GetRuSpecs(ruAllocation[n]).size(); + chSize.second += HeRu::GetRuSpecs(ruAllocation[n + 1]).size(); + if (ruAllocation[n] >= 208) + { + // 996 tone RU occupies 80 MHz + n += 4; + continue; + } + n += 2; + } + break; + } + return chSize; } -bool -HePpdu::IsStaInContentChannel(uint16_t staId, std::size_t channelId) const +uint32_t +HePpdu::GetSigBFieldSize(uint16_t channelWidth, const RuAllocation& ruAllocation) { - NS_ASSERT_MSG(channelId < m_contentChannelAlloc.size(), - "Invalid content channel ID " << channelId); - const auto& channelAlloc = m_contentChannelAlloc.at(channelId); - return (std::find(channelAlloc.cbegin(), channelAlloc.cend(), staId) != channelAlloc.cend()); + // Compute the number of bits used by common field. + // Assume that compression bit in HE-SIG-A is not set (i.e. not + // full band MU-MIMO); the field is present. + auto commonFieldSize = 4 /* CRC */ + 6 /* tail */; + if (channelWidth <= 40) + { + commonFieldSize += 8; // only one allocation subfield + } + else + { + commonFieldSize += + 8 * (channelWidth / 40) /* one allocation field per 40 MHz */ + 1 /* center RU */; + } + + auto numStaPerContentChannel = GetNumRusPerHeSigBContentChannel(channelWidth, ruAllocation); + auto maxNumStaPerContentChannel = + std::max(numStaPerContentChannel.first, numStaPerContentChannel.second); + auto maxNumUserBlockFields = maxNumStaPerContentChannel / + 2; // handle last user block with single user, if any, further down + std::size_t userSpecificFieldSize = + maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */); + if (maxNumStaPerContentChannel % 2 != 0) + { + userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */; + } + + return commonFieldSize + userSpecificFieldSize; } std::string diff --git a/src/wifi/model/he/he-ppdu.h b/src/wifi/model/he/he-ppdu.h index c7253a6cb..4a437ea90 100644 --- a/src/wifi/model/he/he-ppdu.h +++ b/src/wifi/model/he/he-ppdu.h @@ -33,6 +33,9 @@ namespace ns3 { +/// HE SIG-B Content Channels +constexpr size_t WIFI_MAX_NUM_HE_SIGB_CONTENT_CHANNELS = 2; + class WifiPsdu; /** @@ -249,19 +252,27 @@ class HePpdu : public OfdmPpdu void UpdateTxVectorForUlMu(const std::optional& trigVector) const; /** - * Check if STA ID is in HE SIG-B Content Channel ID - * \param staId STA ID - * \param channelId Content Channel ID - * \return true if STA ID in content channel ID, false otherwise + * Get the number of RUs per HE-SIG-B content channel. + * This is applicable only for MU. MU-MIMO (i.e. multiple stations + * per RU) is not supported yet. + * See section 27.3.10.8.3 of IEEE 802.11ax draft 4.0. + * + * \param channelWidth the channel width occupied by the PPDU (in MHz) + * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz + * \return a pair containing the number of RUs in each HE-SIG-B content channel (resp. 1 and 2) */ - bool IsStaInContentChannel(uint16_t staId, size_t channelId) const; + static std::pair GetNumRusPerHeSigBContentChannel( + uint16_t channelWidth, + const std::vector& ruAllocation); /** - * Check if STA ID is allocated - * \param staId STA ID - * \return true if allocated, false otherwise + * Get variable length HE SIG-B field size + * \param channelWidth the channel width occupied by the PPDU (in MHz) + * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz + * \return field size in bytes */ - bool IsAllocated(uint16_t staId) const; + static uint32_t GetSigBFieldSize(uint16_t channelWidth, + const std::vector& ruAllocation); protected: /** diff --git a/src/wifi/model/wifi-tx-vector.cc b/src/wifi/model/wifi-tx-vector.cc index 02931cbbf..e25c95b74 100644 --- a/src/wifi/model/wifi-tx-vector.cc +++ b/src/wifi/model/wifi-tx-vector.cc @@ -530,54 +530,6 @@ WifiTxVector::GetHeMuUserInfoMap() return m_muUserInfos; } -std::pair -WifiTxVector::GetNumRusPerHeSigBContentChannel() const -{ - if (m_preamble == WIFI_PREAMBLE_EHT_MU && m_ehtPpduType == 1) - { - return {1, 0}; - } - - // MU-MIMO is not handled for now, i.e. one station per RU - auto ruAllocation = GetRuAllocation(); - NS_ASSERT_MSG(!ruAllocation.empty(), "RU allocation is not set"); - if (ruAllocation.size() != m_channelWidth / 20) - { - ruAllocation = DeriveRuAllocation(); - } - NS_ASSERT_MSG(ruAllocation.size() == m_channelWidth / 20, - "RU allocation is not consistent with packet bandwidth"); - - std::pair - chSize{0, 0}; - - switch (GetChannelWidth()) - { - case 40: - chSize.second += HeRu::GetRuSpecs(ruAllocation[1]).size(); - [[fallthrough]]; - case 20: - chSize.first += HeRu::GetRuSpecs(ruAllocation[0]).size(); - break; - default: - for (auto n = 0; n < m_channelWidth / 20;) - { - chSize.first += HeRu::GetRuSpecs(ruAllocation[n]).size(); - chSize.second += HeRu::GetRuSpecs(ruAllocation[n + 1]).size(); - if (ruAllocation[n] >= 208) - { - // 996 tone RU occupies 80 MHz - n += 4; - continue; - } - n += 2; - } - break; - } - return chSize; -} - void WifiTxVector::SetInactiveSubchannels(const std::vector& inactiveSubchannels) { diff --git a/src/wifi/model/wifi-tx-vector.h b/src/wifi/model/wifi-tx-vector.h index c00eb8e99..58f4f0d4a 100644 --- a/src/wifi/model/wifi-tx-vector.h +++ b/src/wifi/model/wifi-tx-vector.h @@ -59,9 +59,6 @@ struct HeMuUserInfo /// A vector of subcarrier group using SubcarrierGroups = std::vector; -/// Maximum number of HE-SIG-B content channels -constexpr size_t WIFI_MAX_NUM_HE_SIGB_CONTENT_CHANNELS = 2; - /// HE SIG-B Content Channels STA ID Allocation using ContentChannelAllocation = std::vector>; @@ -422,15 +419,6 @@ class WifiTxVector * \return a reference to the map of HE MU user-specific information indexed by STA-ID */ HeMuUserInfoMap& GetHeMuUserInfoMap(); - /** - * Get the number of RUs per HE-SIG-B content channel. - * This is applicable only for MU. MU-MIMO (i.e. multiple stations - * per RU) is not supported yet. - * See section 27.3.10.8.3 of IEEE 802.11ax draft 4.0. - * - * \return a pair containing the number of RUs in each HE-SIG-B content channel (resp. 1 and 2) - */ - std::pair GetNumRusPerHeSigBContentChannel() const; /** * Set the 20 MHz subchannels that are punctured. diff --git a/src/wifi/test/tx-duration-test.cc b/src/wifi/test/tx-duration-test.cc index cb029e2d3..0f80f1280 100644 --- a/src/wifi/test/tx-duration-test.cc +++ b/src/wifi/test/tx-duration-test.cc @@ -19,7 +19,8 @@ */ #include "ns3/dsss-phy.h" -#include "ns3/eht-phy.h" //includes OFDM, HT, VHT and HE +#include "ns3/eht-phy.h" //includes OFDM, HT, VHT and HE +#include "ns3/eht-ppdu.h" //includes OFDM, HT, VHT and HE #include "ns3/erp-ofdm-phy.h" #include "ns3/he-ru.h" #include "ns3/log.h" @@ -1361,7 +1362,9 @@ HeSigBDurationTest::DoRun() NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector), VhtPhy::GetVhtMcs5(), "HE-SIG-B should be sent at MCS 5"); - std::pair numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel(); + std::pair numUsersPerCc = + HePpdu::GetNumRusPerHeSigBContentChannel(txVector.GetChannelWidth(), + txVector.GetRuAllocation()); NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first, 2, "Both users should be on HE-SIG-B content channel 1"); @@ -1383,7 +1386,8 @@ HeSigBDurationTest::DoRun() NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector), VhtPhy::GetVhtMcs4(), "HE-SIG-B should be sent at MCS 4"); - numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel(); + numUsersPerCc = HePpdu::GetNumRusPerHeSigBContentChannel(txVector.GetChannelWidth(), + txVector.GetRuAllocation()); NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first, 2, "Two users should be on HE-SIG-B content channel 1"); @@ -1402,7 +1406,8 @@ HeSigBDurationTest::DoRun() NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector), VhtPhy::GetVhtMcs3(), "HE-SIG-B should be sent at MCS 3"); - numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel(); + numUsersPerCc = HePpdu::GetNumRusPerHeSigBContentChannel(txVector.GetChannelWidth(), + txVector.GetRuAllocation()); NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first, 2, "Two users should be on HE-SIG-B content channel 1"); @@ -1422,7 +1427,8 @@ HeSigBDurationTest::DoRun() NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector), VhtPhy::GetVhtMcs1(), "HE-SIG-B should be sent at MCS 1"); - numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel(); + numUsersPerCc = HePpdu::GetNumRusPerHeSigBContentChannel(txVector.GetChannelWidth(), + txVector.GetRuAllocation()); NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first, 3, "Three users should be on HE-SIG-B content channel 1"); @@ -1441,7 +1447,8 @@ HeSigBDurationTest::DoRun() NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector), VhtPhy::GetVhtMcs1(), "HE-SIG-B should be sent at MCS 1"); - numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel(); + numUsersPerCc = HePpdu::GetNumRusPerHeSigBContentChannel(txVector.GetChannelWidth(), + txVector.GetRuAllocation()); NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first, 4, "Four users should be on HE-SIG-B content channel 1");