diff --git a/src/wifi/model/eht/eht-capabilities.cc b/src/wifi/model/eht/eht-capabilities.cc index 082688d8e..973cc25a1 100644 --- a/src/wifi/model/eht/eht-capabilities.cc +++ b/src/wifi/model/eht/eht-capabilities.cc @@ -19,6 +19,9 @@ #include "eht-capabilities.h" +#include +#include + namespace ns3 { @@ -266,10 +269,133 @@ EhtMcsAndNssSet::Deserialize(Buffer::Iterator start, return count; } +uint16_t +EhtPpeThresholds::GetSize() const +{ + const auto numBitsSet = std::bitset<5>(ruIndexBitmask).count(); + const uint64_t nBitsNoPadding = 4 + 5 + (6 * numBitsSet * (nssPe + 1)); + return std::ceil(static_cast(nBitsNoPadding) / 8.0); +} + +void +EhtPpeThresholds::Serialize(Buffer::Iterator& start) const +{ + uint64_t nBitsNoPadding = 0; + uint8_t val = nssPe | ((ruIndexBitmask & 0x0f) << 4); + start.WriteU8(val); + nBitsNoPadding += 8; + val = (ruIndexBitmask & 0x10) >> 4; + nBitsNoPadding += 1; + uint8_t bitsPerPpet = 3; + for (const auto& info : ppeThresholdsInfo) + { + uint8_t offset = nBitsNoPadding % 8; + uint8_t bitsLeft = (8 - offset); + uint8_t bitMask = (0x01 << bitsLeft) - 0x01; + val |= (info.ppetMax & bitMask) << offset; + nBitsNoPadding += std::min(bitsLeft, bitsPerPpet); + if (nBitsNoPadding % 8 == 0) + { + start.WriteU8(val); + if (bitsLeft < 3) + { + const uint8_t remainingBits = (3 - bitsLeft); + bitMask = (0x01 << remainingBits) - 0x01; + val = (info.ppetMax >> bitsLeft) & bitMask; + nBitsNoPadding += remainingBits; + } + else + { + val = 0; + } + } + offset = nBitsNoPadding % 8; + bitsLeft = (8 - offset); + bitMask = (0x01 << bitsLeft) - 0x01; + val |= (info.ppet8 & bitMask) << offset; + nBitsNoPadding += std::min(bitsLeft, bitsPerPpet); + if (nBitsNoPadding % 8 == 0) + { + start.WriteU8(val); + if (bitsLeft < 3) + { + const uint8_t remainingBits = (3 - bitsLeft); + bitMask = (0x01 << remainingBits) - 0x01; + val = (info.ppet8 >> bitsLeft) & bitMask; + nBitsNoPadding += remainingBits; + } + else + { + val = 0; + } + } + } + if (nBitsNoPadding % 8 > 0) + { + start.WriteU8(val); + } +} + +uint16_t +EhtPpeThresholds::Deserialize(Buffer::Iterator start) +{ + Buffer::Iterator i = start; + uint64_t nBitsNoPadding = 0; + uint8_t val = i.ReadU8(); + nssPe = val & 0x0f; + ruIndexBitmask = ((val >> 4) & 0x0f); + nBitsNoPadding += 8; + val = i.ReadU8(); + ruIndexBitmask |= ((val & 0x01) << 4); + nBitsNoPadding += 1; + const auto numBitsSet = std::bitset<5>(ruIndexBitmask).count(); + const uint64_t bitsToDeserialize = (4 + 5 + (6 * numBitsSet * (nssPe + 1))); + uint8_t bitsPerPpet = 3; + while (nBitsNoPadding < bitsToDeserialize) + { + EhtPpeThresholdsInfo info; + uint8_t offset = nBitsNoPadding % 8; + uint8_t bitsLeft = (8 - offset); + uint8_t bitMask = (1 << bitsLeft) - 1; + info.ppetMax = ((val >> offset) & bitMask); + nBitsNoPadding += std::min(bitsLeft, bitsPerPpet); + if (nBitsNoPadding % 8 == 0) + { + val = i.ReadU8(); + if (bitsLeft < 3) + { + const uint8_t remainingBits = (3 - bitsLeft); + bitMask = (1 << remainingBits) - 1; + info.ppetMax |= ((val & bitMask) << bitsLeft); + nBitsNoPadding += remainingBits; + } + } + offset = nBitsNoPadding % 8; + bitsLeft = (8 - offset); + bitMask = (1 << bitsLeft) - 1; + info.ppet8 = ((val >> offset) & bitMask); + nBitsNoPadding += std::min(bitsLeft, bitsPerPpet); + if (nBitsNoPadding % 8 == 0) + { + val = i.ReadU8(); + if (bitsLeft < 3) + { + const uint8_t remainingBits = (3 - bitsLeft); + bitMask = (1 << remainingBits) - 1; + info.ppet8 |= ((val & bitMask) << bitsLeft); + nBitsNoPadding += remainingBits; + } + } + ppeThresholdsInfo.push_back(info); + } + return std::ceil(static_cast(bitsToDeserialize) / 8.0); +} + EhtCapabilities::EhtCapabilities() : m_macCapabilities{}, m_phyCapabilities{}, m_supportedEhtMcsAndNssSet{}, + m_ppeThresholds{}, m_is2_4Ghz{false}, m_heCapabilities{std::nullopt} { @@ -279,6 +405,7 @@ EhtCapabilities::EhtCapabilities(bool is2_4Ghz, const std::optionalGetChannelWidthSet(), m_phyCapabilities.support320MhzIn6Ghz); + i.Next(nBytes); + count += nBytes; + + if (m_phyCapabilities.ppeThresholdsPresent) + { + count += m_ppeThresholds.Deserialize(i); + } return count; } diff --git a/src/wifi/model/eht/eht-capabilities.h b/src/wifi/model/eht/eht-capabilities.h index d8d8c21f2..f7de2175a 100644 --- a/src/wifi/model/eht/eht-capabilities.h +++ b/src/wifi/model/eht/eht-capabilities.h @@ -198,6 +198,46 @@ struct EhtMcsAndNssSet bool support320MhzIn6Ghz); }; +/** + * EHT PPE Thresholds subfield. + * See IEEE 802.11be D1.5 9.4.2.313.5 EHT PPE Thresholds subfield + */ +struct EhtPpeThresholds +{ + /** + * EHT PPE Thresholds Info + */ + struct EhtPpeThresholdsInfo + { + uint8_t ppetMax : 3; //!< PPETmax + uint8_t ppet8 : 3; //!< PPE8 + }; + + uint8_t nssPe : 4; //!< NSS_PE + uint8_t ruIndexBitmask : 5; //!< RU Index Bitmask + std::vector ppeThresholdsInfo; //!< PPE Thresholds Info + + /** + * Get the size of the serialized EHT PPE Thresholds subfield + * + * \return the size of the serialized EHT PPE Thresholds subfield + */ + uint16_t GetSize() const; + /** + * Serialize the EHT PPE Thresholds subfield + * + * \param start iterator pointing to where the EHT PPE Thresholds subfield should be written to + */ + void Serialize(Buffer::Iterator& start) const; + /** + * Deserialize the EHT PPE Thresholds subfield + * + * \param start iterator pointing to where the EHT PPE Thresholds subfield should be read from + * \return the number of bytes read + */ + uint16_t Deserialize(Buffer::Iterator start); +}; + /** * \ingroup wifi * @@ -273,6 +313,7 @@ class EhtCapabilities : public WifiInformationElement EhtMacCapabilities m_macCapabilities; //!< EHT MAC Capabilities Info subfield EhtPhyCapabilities m_phyCapabilities; //!< EHT PHY Capabilities Info subfield EhtMcsAndNssSet m_supportedEhtMcsAndNssSet; //!< Supported EHT-MCS And NSS Set subfield + EhtPpeThresholds m_ppeThresholds; //!< EHT PPE Threshold Info subfield private: uint16_t GetInformationFieldSize() const override;