wifi: Use EHT RUs for EHT PPDUs

This commit is contained in:
Sébastien Deronne
2024-11-03 14:23:24 +01:00
parent a98446d27a
commit 7d6d710604
13 changed files with 356 additions and 235 deletions

View File

@@ -13,8 +13,10 @@
#include "ns3/log.h"
#include "ns3/wifi-phy-operating-channel.h"
#include "ns3/wifi-psdu.h"
#include "ns3/wifi-utils.h"
#include <algorithm>
#include <initializer_list>
#include <numeric>
namespace ns3
@@ -187,6 +189,7 @@ EhtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector) const
[](uint8_t prev, const auto& cc) { return prev + cc.size(); })
: 0;
SetHeMuUserInfos(txVector,
WIFI_MOD_CLASS_EHT,
ruAllocation.value(),
std::nullopt,
ehtPhyHeader->m_contentChannels,
@@ -203,7 +206,7 @@ EhtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector) const
}
else
{
const auto fullBwRu{HeRu::RuSpec(WifiRu::GetRuType(bw), 1, true)};
const auto fullBwRu{EhtRu::RuSpec(WifiRu::GetRuType(bw), 1, true, true)};
txVector.SetHeMuUserInfo(ehtPhyHeader->m_contentChannels.front().front().staId,
{fullBwRu,
ehtPhyHeader->m_contentChannels.front().front().mcs,
@@ -232,6 +235,7 @@ EhtPpdu::GetNumRusPerEhtSigBContentChannel(MHz_u channelWidth,
return {1, 0};
}
return HePpdu::GetNumRusPerHeSigBContentChannel(channelWidth,
WIFI_MOD_CLASS_EHT,
ruAllocation,
std::nullopt,
compression,
@@ -388,6 +392,26 @@ EhtPpdu::GetPsdu(uint8_t bssColor, uint16_t staId /* = SU_STA_ID */) const
return nullptr;
}
WifiRu::RuSpec
EhtPpdu::GetRuSpec(std::size_t ruAllocIndex, MHz_u bw, RuType ruType, std::size_t phyIndex) const
{
if (ruType == RuType::RU_26_TONE)
{
for (const auto undefinedRu : std::initializer_list<std::size_t>{19, 56, 93, 130})
{
if (phyIndex >= undefinedRu)
{
++phyIndex;
}
}
}
const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(MHz_u{20});
const auto& [primary160, primary80OrLow80] =
EhtRu::GetPrimaryFlags(bw, ruType, phyIndex, p20Index);
const auto index = EhtRu::GetIndexIn80MHzSegment(bw, ruType, phyIndex);
return EhtRu::RuSpec{ruType, index, primary160, primary80OrLow80};
}
Ptr<WifiPpdu>
EhtPpdu::Copy() const
{

View File

@@ -151,6 +151,12 @@ class EhtPpdu : public HePpdu
uint8_t ehtPpduType,
std::optional<bool> isLow80MHz);
protected:
WifiRu::RuSpec GetRuSpec(std::size_t ruAllocIndex,
MHz_u bw,
RuType ruType,
std::size_t phyIndex) const override;
private:
bool IsDlMu() const override;
bool IsUlMu() const override;

View File

@@ -216,6 +216,7 @@ HePhy::GetSigBSize(const WifiTxVector& txVector) const
NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
return HePpdu::GetSigBFieldSize(
txVector.GetChannelWidth(),
txVector.GetModulationClass(),
txVector.GetRuAllocation(
m_wifiPhy ? m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(MHz_u{20}) : 0),
txVector.GetCenter26ToneRuIndication(),
@@ -983,22 +984,24 @@ HePhy::GetRuBandForTx(const WifiTxVector& txVector, uint16_t staId) const
auto ru = txVector.GetRu(staId);
const auto channelWidth = txVector.GetChannelWidth();
NS_ASSERT(channelWidth <= m_wifiPhy->GetChannelWidth());
const auto mc = txVector.GetModulationClass();
const auto group = WifiRu::GetSubcarrierGroup(
channelWidth,
WifiRu::GetRuType(ru),
WifiRu::GetPhyIndex(ru,
channelWidth,
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(MHz_u{20})),
WIFI_MOD_CLASS_HE);
mc);
// for a TX spectrum, the guard bandwidth is a function of the transmission channel width
// and the spectrum width equals the transmission channel width (hence bandIndex equals 0)
const auto indices = ConvertRuSubcarriers(channelWidth,
GetGuardBandwidth(channelWidth),
m_wifiPhy->GetOperatingChannel().GetFrequencies(),
m_wifiPhy->GetChannelWidth(),
m_wifiPhy->GetSubcarrierSpacing(),
{group.front().first, group.back().second},
0);
const auto indices = ConvertRuSubcarriers({channelWidth,
GetGuardBandwidth(channelWidth),
m_wifiPhy->GetOperatingChannel().GetFrequencies(),
m_wifiPhy->GetChannelWidth(),
m_wifiPhy->GetSubcarrierSpacing(),
mc,
{group.front().first, group.back().second},
0});
WifiSpectrumBandInfo ruBandForTx{};
for (const auto& indicesPerSegment : indices)
{
@@ -1016,23 +1019,25 @@ HePhy::GetRuBandForRx(const WifiTxVector& txVector, uint16_t staId) const
auto ru = txVector.GetRu(staId);
const auto channelWidth = txVector.GetChannelWidth();
NS_ASSERT(channelWidth <= m_wifiPhy->GetChannelWidth());
const auto mc = txVector.GetModulationClass();
const auto group = WifiRu::GetSubcarrierGroup(
channelWidth,
WifiRu::GetRuType(ru),
WifiRu::GetPhyIndex(ru,
channelWidth,
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(MHz_u{20})),
WIFI_MOD_CLASS_HE);
mc);
// for an RX spectrum, the guard bandwidth is a function of the operating channel width
// and the spectrum width equals the operating channel width
const auto indices =
ConvertRuSubcarriers(channelWidth,
GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
m_wifiPhy->GetOperatingChannel().GetFrequencies(),
m_wifiPhy->GetChannelWidth(),
m_wifiPhy->GetSubcarrierSpacing(),
{group.front().first, group.back().second},
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth));
const auto indices = ConvertRuSubcarriers(
{channelWidth,
GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
m_wifiPhy->GetOperatingChannel().GetFrequencies(),
m_wifiPhy->GetChannelWidth(),
m_wifiPhy->GetSubcarrierSpacing(),
mc,
{group.front().first, group.back().second},
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth)});
WifiSpectrumBandInfo ruBandForRx{};
for (const auto& indicesPerSegment : indices)
{
@@ -1821,56 +1826,51 @@ HePhy::GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu)
}
std::vector<WifiSpectrumBandIndices>
HePhy::ConvertRuSubcarriers(MHz_u bandWidth,
MHz_u guardBandwidth,
const std::vector<MHz_u>& centerFrequencies,
MHz_u totalWidth,
Hz_u subcarrierSpacing,
SubcarrierRange subcarrierRange,
uint8_t bandIndex)
HePhy::ConvertRuSubcarriers(const RuSubcarriersInfo& info)
{
NS_ASSERT_MSG(bandWidth <= totalWidth,
"Bandwidth (" << bandWidth << ") cannot exceed total operating channel width ("
<< totalWidth << ")");
NS_ASSERT_MSG(info.bandWidth <= info.totalWidth,
"Bandwidth (" << info.bandWidth
<< ") cannot exceed total operating channel width ("
<< info.totalWidth << ")");
std::vector<WifiSpectrumBandIndices> convertedSubcarriers{};
guardBandwidth /= centerFrequencies.size();
const auto guardBandwidth{info.guardBandwidth / info.centerFrequencies.size()};
const auto nGuardBands =
static_cast<uint32_t>(((2 * MHzToHz(guardBandwidth)) / subcarrierSpacing) + 0.5);
if (bandWidth > (totalWidth / centerFrequencies.size()))
static_cast<uint32_t>(((2 * MHzToHz(guardBandwidth)) / info.subcarrierSpacing) + 0.5);
auto bw{info.bandWidth};
if (bw > (info.totalWidth / info.centerFrequencies.size()))
{
NS_ASSERT(bandIndex == 0);
bandWidth /= centerFrequencies.size();
NS_ASSERT(info.bandIndex == 0);
bw /= info.centerFrequencies.size();
}
// Figures 27-5 to 27-7 of IEEE 802.11ax-2021: the number of guard subcarriers is 6 for 20 MHz
// MHz HE PPDUs and 12 for 40/80/160 MHz HE PPDUs
const uint32_t guardSubcarriers = (bandWidth < MHz_u{40}) ? 6 : 12;
const auto refRu = WifiRu::GetRuType(bandWidth);
const auto mc{WIFI_MOD_CLASS_HE};
const auto offset = WifiRu::GetSubcarrierGroup(bandWidth, refRu, 1, mc).back().second;
const uint32_t guardSubcarriers = (bw < MHz_u{40}) ? 6 : 12;
const auto refRu = WifiRu::GetRuType(bw);
const auto offset = WifiRu::GetSubcarrierGroup(bw, refRu, 1, info.mc).back().second;
uint32_t centerFrequencyIndex = (nGuardBands / 2) + guardSubcarriers + offset;
const auto numBandsInBand = static_cast<size_t>(MHzToHz(bandWidth) / subcarrierSpacing);
centerFrequencyIndex += numBandsInBand * bandIndex;
const auto numBandsInBand = static_cast<size_t>(MHzToHz(bw) / info.subcarrierSpacing);
centerFrequencyIndex += numBandsInBand * info.bandIndex;
// start and stop subcarriers might be in different frequency segments, hence define a low and a
// high center frequency
auto centerFrequencyIndexLow = centerFrequencyIndex;
auto centerFrequencyIndexHigh = centerFrequencyIndex;
if (centerFrequencies.size() > 1)
if (info.centerFrequencies.size() > 1)
{
const auto numBandsBetweenSegments =
SpectrumWifiPhy::GetNumBandsBetweenSegments(centerFrequencies,
totalWidth,
subcarrierSpacing);
if (subcarrierRange.first > 0)
SpectrumWifiPhy::GetNumBandsBetweenSegments(info.centerFrequencies,
info.totalWidth,
info.subcarrierSpacing);
if (info.subcarrierRange.first > 0)
{
centerFrequencyIndexLow += numBandsBetweenSegments;
}
if (subcarrierRange.second > 0)
if (info.subcarrierRange.second > 0)
{
centerFrequencyIndexHigh += numBandsBetweenSegments;
}
}
convertedSubcarriers.emplace_back(centerFrequencyIndexLow + subcarrierRange.first,
centerFrequencyIndexHigh + subcarrierRange.second);
convertedSubcarriers.emplace_back(centerFrequencyIndexLow + info.subcarrierRange.first,
centerFrequencyIndexHigh + info.subcarrierRange.second);
return convertedSubcarriers;
}

View File

@@ -427,29 +427,30 @@ class HePhy : public VhtPhy
*/
static Time GetSymbolDuration(Time guardInterval);
/// structure containing the information about the RU subcarriers to be able to converted to the
/// indices used by the Spectrum model
struct RuSubcarriersInfo
{
MHz_u bandWidth; ///< width of the band used for the OFDMA transmission. Must be a multiple
///< of 20 MHz
MHz_u guardBandwidth; ///< width of the guard band
const std::vector<MHz_u>& centerFrequencies; ///< center frequency of each segment
MHz_u totalWidth; ///< width of the operating channel
Hz_u subcarrierSpacing; ///< subcarrier spacing
WifiModulationClass mc; ///< modulation class used for the transmission
SubcarrierRange subcarrierRange; ///< subcarrier range of the RU
uint8_t bandIndex{0}; ///< index (starting at 0) of the band within the operating channel
};
/**
* @param bandWidth the width of the band used for the OFDMA transmission. Must be a multiple of
* 20 MHz
* @param guardBandwidth width of the guard band
* @param centerFrequencies the center frequency of each segment
* @param totalWidth the width of the operating channel
* @param subcarrierSpacing the subcarrier spacing
* @param subcarrierRange the subcarrier range of the RU
* @param bandIndex the index (starting at 0) of the band within the operating channel
* @return the converted subcarriers
*
* This is a helper function to convert RU subcarriers, which are relative to the center
* frequency subcarrier, to the indexes used by the Spectrum model. The size of the returned
* frequency subcarrier, to the indices used by the Spectrum model. The size of the returned
* vector corresponds to the number of segments covered by the RU.
*
* @param info the information about the RU subcarrier to convert
* @return the indices used by the Spectrum model for the provided RU subcarrier
*/
static std::vector<WifiSpectrumBandIndices> ConvertRuSubcarriers(
MHz_u bandWidth,
MHz_u guardBandwidth,
const std::vector<MHz_u>& centerFrequencies,
MHz_u totalWidth,
Hz_u subcarrierSpacing,
SubcarrierRange subcarrierRange,
uint8_t bandIndex = 0);
static std::vector<WifiSpectrumBandIndices> ConvertRuSubcarriers(const RuSubcarriersInfo& info);
protected:
PhyFieldRxStatus ProcessSig(Ptr<Event> event,

View File

@@ -202,6 +202,7 @@ HePpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector) const
txVector.SetGuardInterval(GetGuardIntervalFromEncoding(heSigHeader->m_giLtfSize));
txVector.SetBssColor(heSigHeader->m_bssColor);
SetHeMuUserInfos(txVector,
WIFI_MOD_CLASS_HE,
heSigHeader->m_ruAllocation,
heSigHeader->m_center26ToneRuIndication,
heSigHeader->m_contentChannels,
@@ -248,6 +249,7 @@ HePpdu::GetRuSpec(std::size_t ruAllocIndex, MHz_u bw, RuType ruType, std::size_t
void
HePpdu::SetHeMuUserInfos(WifiTxVector& txVector,
WifiModulationClass mc,
const RuAllocation& ruAllocation,
std::optional<Center26ToneRuIndication> center26ToneRuIndication,
const HeSigBContentChannels& contentChannels,
@@ -304,7 +306,6 @@ HePpdu::SetHeMuUserInfos(WifiTxVector& txVector,
}
}
NS_ASSERT(ruAllocIndex < ruAllocation.size());
const auto mc{WIFI_MOD_CLASS_HE};
auto ruSpecs = WifiRu::GetRuSpecs(ruAllocation.at(ruAllocIndex), mc);
while (ruSpecs.empty() && (ruAllocIndex < ruAllocation.size()))
{
@@ -550,6 +551,7 @@ HePpdu::UpdateTxVectorForUlMu(const std::optional<WifiTxVector>& trigVector) con
std::pair<std::size_t, std::size_t>
HePpdu::GetNumRusPerHeSigBContentChannel(
MHz_u channelWidth,
WifiModulationClass mc,
const RuAllocation& ruAllocation,
std::optional<Center26ToneRuIndication> center26ToneRuIndication,
bool sigBCompression,
@@ -581,7 +583,6 @@ HePpdu::GetNumRusPerHeSigBContentChannel(
NS_ASSERT_MSG(ruAllocation.size() == Count20MHzSubchannels(channelWidth),
"RU allocation is not consistent with packet bandwidth");
const auto mc{WIFI_MOD_CLASS_HE};
switch (static_cast<uint16_t>(channelWidth))
{
case 40:
@@ -717,7 +718,7 @@ HePpdu::GetHeSigBContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
continue;
}
const auto mc{WIFI_MOD_CLASS_HE};
const auto mc = txVector.GetModulationClass();
const auto numRus = WifiRu::GetNRus(MHz_u{20}, ruType, mc);
while (prevRuIndex < ruIndex - 1)
{
@@ -775,6 +776,11 @@ HePpdu::GetHeSigBContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
{
// "ignore" the center 26-tone RUs in 80 MHz channels
ruIdx--;
if (ruIdx > 37)
{
NS_ASSERT(!WifiRu::IsHe(ru));
ruIdx -= (ruIdx - 19) / 37;
}
}
ccIndex = (((ruIdx - 1) / numRus) % 2 == 0) ? 0 : 1;
}
@@ -798,6 +804,7 @@ HePpdu::GetHeSigBContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
// Add unassigned RUs
auto numNumRusPerHeSigBContentChannel = GetNumRusPerHeSigBContentChannel(
channelWidth,
txVector.GetModulationClass(),
txVector.GetRuAllocation(p20Index),
txVector.GetCenter26ToneRuIndication(),
isSigBCompression,
@@ -823,6 +830,7 @@ HePpdu::GetHeSigBContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
uint32_t
HePpdu::GetSigBFieldSize(MHz_u channelWidth,
WifiModulationClass mc,
const RuAllocation& ruAllocation,
std::optional<Center26ToneRuIndication> center26ToneRuIndication,
bool sigBCompression,
@@ -846,6 +854,7 @@ HePpdu::GetSigBFieldSize(MHz_u channelWidth,
}
auto numRusPerContentChannel = GetNumRusPerHeSigBContentChannel(channelWidth,
mc,
ruAllocation,
center26ToneRuIndication,
sigBCompression,

View File

@@ -189,6 +189,7 @@ class HePpdu : public OfdmPpdu
* See section 27.3.10.8.3 of IEEE 802.11ax draft 4.0.
*
* @param channelWidth the channel width occupied by the PPDU
* @param mc the modulation class used for the transmission of the PPDU
* @param ruAllocation 8 bit RU_ALLOCATION per 20 MHz
* @param center26ToneRuIndication the center 26 tone RU indication
* @param sigBCompression flag whether SIG-B compression is used by the PPDU
@@ -197,6 +198,7 @@ class HePpdu : public OfdmPpdu
*/
static std::pair<std::size_t, std::size_t> GetNumRusPerHeSigBContentChannel(
MHz_u channelWidth,
WifiModulationClass mc,
const RuAllocation& ruAllocation,
std::optional<Center26ToneRuIndication> center26ToneRuIndication,
bool sigBCompression,
@@ -216,6 +218,7 @@ class HePpdu : public OfdmPpdu
/**
* Get variable length HE SIG-B field size
* @param channelWidth the channel width occupied by the PPDU
* @param mc the modulation class used for the transmission of the PPDU
* @param ruAllocation 8 bit RU_ALLOCATION per 20 MHz
* @param center26ToneRuIndication the center 26 tone RU indication
* @param sigBCompression flag whether SIG-B compression is used by the PPDU
@@ -224,6 +227,7 @@ class HePpdu : public OfdmPpdu
*/
static uint32_t GetSigBFieldSize(
MHz_u channelWidth,
WifiModulationClass mc,
const RuAllocation& ruAllocation,
std::optional<Center26ToneRuIndication> center26ToneRuIndication,
bool sigBCompression,
@@ -248,6 +252,7 @@ class HePpdu : public OfdmPpdu
* Reconstruct HeMuUserInfoMap from HE-SIG-B header.
*
* @param txVector the TXVECTOR to set its HeMuUserInfoMap
* @param mc the modulation class used for the transmission of the PPDU
* @param ruAllocation the RU_ALLOCATION per 20 MHz
* @param center26ToneRuIndication the center 26 tone RU indication
* @param contentChannels the HE-SIG-B content channels
@@ -255,6 +260,7 @@ class HePpdu : public OfdmPpdu
* @param numMuMimoUsers the number of MU-MIMO users addressed by the PPDU
*/
void SetHeMuUserInfos(WifiTxVector& txVector,
WifiModulationClass mc,
const RuAllocation& ruAllocation,
std::optional<Center26ToneRuIndication> center26ToneRuIndication,
const HeSigBContentChannels& contentChannels,

View File

@@ -174,13 +174,32 @@ RrMultiUserScheduler::GetTxVectorForUlMu(std::function<bool(const MasterInfo&)>
{
NS_LOG_FUNCTION(this);
auto heConfiguration = m_apMac->GetHeConfiguration();
NS_ASSERT(heConfiguration);
WifiTxVector txVector;
txVector.SetChannelWidth(m_allowedWidth);
txVector.SetGuardInterval(heConfiguration->GetGuardInterval());
txVector.SetBssColor(heConfiguration->m_bssColor);
txVector.SetPreambleType(WIFI_PREAMBLE_HE_TB);
const auto firstCandidate =
std::find_if(m_staListUl.begin(), m_staListUl.end(), canBeSolicited);
if (firstCandidate == m_staListUl.cend())
{
NS_LOG_DEBUG("No candidate");
return txVector;
}
// determine RUs to allocate to stations
const auto isEht =
(m_apMac->GetEhtSupported() && m_apMac->GetEhtSupported(firstCandidate->address));
auto count = std::min<std::size_t>(m_nStations, m_staListUl.size());
std::size_t nCentral26TonesRus;
WifiRu::GetEqualSizedRusForStations(m_allowedWidth,
count,
nCentral26TonesRus,
WIFI_MOD_CLASS_HE);
isEht ? WIFI_MOD_CLASS_EHT : WIFI_MOD_CLASS_HE);
NS_ASSERT(count >= 1);
if (!m_useCentral26TonesRus)
@@ -188,17 +207,15 @@ RrMultiUserScheduler::GetTxVectorForUlMu(std::function<bool(const MasterInfo&)>
nCentral26TonesRus = 0;
}
Ptr<HeConfiguration> heConfiguration = m_apMac->GetHeConfiguration();
NS_ASSERT(heConfiguration);
WifiTxVector txVector;
txVector.SetPreambleType(WIFI_PREAMBLE_HE_TB);
txVector.SetChannelWidth(m_allowedWidth);
txVector.SetGuardInterval(heConfiguration->GetGuardInterval());
txVector.SetBssColor(heConfiguration->m_bssColor);
if (isEht)
{
txVector.SetPreambleType(WIFI_PREAMBLE_EHT_TB);
txVector.SetEhtPpduType(0);
}
// TODO otherwise make sure the TX width does not exceed 160 MHz
// iterate over the associated stations until an enough number of stations is identified
auto staIt = m_staListUl.begin();
auto staIt = firstCandidate;
m_candidates.clear();
while (staIt != m_staListUl.end() &&
@@ -207,51 +224,15 @@ RrMultiUserScheduler::GetTxVectorForUlMu(std::function<bool(const MasterInfo&)>
{
NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
if (!canBeSolicited(*staIt))
{
NS_LOG_DEBUG("Skipping station based on provided function object");
staIt++;
continue;
}
if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_TB &&
!m_apMac->GetEhtSupported(staIt->address))
{
NS_LOG_DEBUG(
"Skipping non-EHT STA because this Trigger Frame is only soliciting EHT STAs");
staIt++;
staIt = std::find_if(++staIt, m_staListUl.end(), canBeSolicited);
continue;
}
uint8_t tid = 0;
while (tid < 8)
{
// check that a BA agreement is established with the receiver for the
// considered TID, since ack sequences for UL MU require block ack
if (m_apMac->GetBaAgreementEstablishedAsRecipient(staIt->address, tid))
{
break;
}
++tid;
}
if (tid == 8)
{
NS_LOG_DEBUG("No Block Ack agreement established with " << staIt->address);
staIt++;
continue;
}
// if the first candidate STA is an EHT STA, we switch to soliciting EHT TB PPDUs
if (txVector.GetHeMuUserInfoMap().empty())
{
if (m_apMac->GetEhtSupported() && m_apMac->GetEhtSupported(staIt->address))
{
txVector.SetPreambleType(WIFI_PREAMBLE_EHT_TB);
txVector.SetEhtPpduType(0);
}
// TODO otherwise, make sure the TX width does not exceed 160 MHz
}
// prepare the MAC header of a frame that would be sent to the candidate station,
// just for the purpose of retrieving the TXVECTOR used to transmit to that station
WifiMacHeader hdr(WIFI_MAC_QOSDATA);
@@ -267,8 +248,8 @@ RrMultiUserScheduler::GetTxVectorForUlMu(std::function<bool(const MasterInfo&)>
suTxVector.GetNss()});
m_candidates.emplace_back(staIt, nullptr);
// move to the next station in the list
staIt++;
// move to the next candidate station in the list
staIt = std::find_if(++staIt, m_staListUl.end(), canBeSolicited);
}
if (txVector.GetHeMuUserInfoMap().empty())
@@ -287,7 +268,26 @@ RrMultiUserScheduler::CanSolicitStaInBsrpTf(const MasterInfo& info) const
// check if station has setup the current link
if (!m_apMac->GetStaList(m_linkId).contains(info.aid))
{
NS_LOG_INFO("STA with AID " << info.aid << " has not setup link " << +m_linkId);
NS_LOG_INFO("STA with AID " << info.aid << " has not setup link " << +m_linkId
<< ": skipping station");
return false;
}
uint8_t tid = 0;
while (tid < 8)
{
// check that a BA agreement is established with the receiver for the
// considered TID, since ack sequences for UL MU require block ack
if (m_apMac->GetBaAgreementEstablishedAsRecipient(info.address, tid))
{
break;
}
++tid;
}
if (tid == 8)
{
NS_LOG_DEBUG("No Block Ack agreement established with " << info.address
<< ": skipping station");
return false;
}
@@ -312,7 +312,8 @@ RrMultiUserScheduler::CanSolicitStaInBsrpTf(const MasterInfo& info) const
if (!mapped)
{
NS_LOG_DEBUG("MLD " << *mldAddr << " has not mapped any TID on link " << +m_linkId);
NS_LOG_DEBUG("MLD " << *mldAddr << " has not mapped any TID on link " << +m_linkId
<< ": skipping station");
return false;
}
@@ -327,7 +328,7 @@ RrMultiUserScheduler::CanSolicitStaInBsrpTf(const MasterInfo& info) const
m_linkId,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY)))
{
NS_LOG_INFO("EMLSR client " << *mldAddr << " is using another link");
NS_LOG_INFO("EMLSR client " << *mldAddr << " is using another link: skipping station");
return false;
}
@@ -752,17 +753,7 @@ RrMultiUserScheduler::TrySendingDlMuPpdu()
std::size_t count =
std::min(static_cast<std::size_t>(m_nStations), m_staListDl[primaryAc].size());
std::size_t nCentral26TonesRus;
const auto ruType = WifiRu::GetEqualSizedRusForStations(m_allowedWidth,
count,
nCentral26TonesRus,
WIFI_MOD_CLASS_HE);
NS_ASSERT(count >= 1);
if (!m_useCentral26TonesRus)
{
nCentral26TonesRus = 0;
}
std::size_t nCentral26TonesRus{0};
uint8_t currTid = wifiAcList.at(primaryAc).GetHighTid();
@@ -814,6 +805,7 @@ RrMultiUserScheduler::TrySendingDlMuPpdu()
ruAllocations.resize(numRuAllocs);
NS_ASSERT((m_candidates.size() % numRuAllocs) == 0);
RuType ruType{RuType::RU_TYPE_MAX};
while (staIt != m_staListDl[primaryAc].end() &&
m_candidates.size() <
std::min(static_cast<std::size_t>(m_nStations), count + nCentral26TonesRus))
@@ -828,8 +820,6 @@ RrMultiUserScheduler::TrySendingDlMuPpdu()
continue;
}
RuType currRuType = (m_candidates.size() < count ? ruType : RuType::RU_26_TONE);
// check if the AP has at least one frame to be sent to the current station
for (uint8_t tid : tids)
{
@@ -857,17 +847,37 @@ RrMultiUserScheduler::TrySendingDlMuPpdu()
WifiTxVector txVectorCopy = m_txParams.m_txVector;
// the first candidate STA determines the preamble type for the DL MU PPDU
if (m_candidates.empty() &&
suTxVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
if (m_candidates.empty())
{
m_txParams.m_txVector.SetPreambleType(WIFI_PREAMBLE_EHT_MU);
m_txParams.m_txVector.SetEhtPpduType(0); // indicates DL OFDMA transmission
WifiModulationClass mc = WIFI_MOD_CLASS_HE;
if (suTxVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
{
m_txParams.m_txVector.SetPreambleType(WIFI_PREAMBLE_EHT_MU);
m_txParams.m_txVector.SetEhtPpduType(
0); // indicates DL OFDMA transmission
mc = WIFI_MOD_CLASS_EHT;
}
ruType = WifiRu::GetEqualSizedRusForStations(m_allowedWidth,
count,
nCentral26TonesRus,
mc);
NS_ASSERT(count >= 1);
if (!m_useCentral26TonesRus)
{
nCentral26TonesRus = 0;
}
}
m_txParams.m_txVector.SetHeMuUserInfo(staIt->aid,
{HeRu::RuSpec{currRuType, 1, true},
suTxVector.GetMode().GetMcsValue(),
suTxVector.GetNss()});
NS_ASSERT(ruType != RuType::RU_TYPE_MAX);
const auto currRuType =
(m_candidates.size() < count ? ruType : RuType::RU_26_TONE);
const auto ru =
(m_txParams.m_txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
? WifiRu::RuSpec(EhtRu::RuSpec{currRuType, 1, true, true})
: WifiRu::RuSpec(HeRu::RuSpec{currRuType, 1, true});
m_txParams.m_txVector.SetHeMuUserInfo(
staIt->aid,
{ru, suTxVector.GetMode().GetMcsValue(), suTxVector.GetNss()});
if (!GetHeFem(m_linkId)->TryAddMpdu(mpdu, m_txParams, actualAvailableTime))
{
@@ -923,7 +933,7 @@ RrMultiUserScheduler::FinalizeTxVector(WifiTxVector& txVector)
const auto ruType = WifiRu::GetEqualSizedRusForStations(m_allowedWidth,
nRusAssigned,
nCentral26TonesRus,
WIFI_MOD_CLASS_HE);
txVector.GetModulationClass());
NS_LOG_DEBUG(nRusAssigned << " stations are being assigned a " << ruType << " RU");
@@ -941,11 +951,11 @@ RrMultiUserScheduler::FinalizeTxVector(WifiTxVector& txVector)
WifiTxVector::HeMuUserInfoMap heMuUserInfoMap;
std::swap(heMuUserInfoMap, txVector.GetHeMuUserInfoMap());
const auto mc = IsEht(txVector.GetPreambleType()) ? WIFI_MOD_CLASS_EHT : WIFI_MOD_CLASS_HE;
auto candidateIt = m_candidates.begin(); // iterator over the list of candidate receivers
auto ruSet = WifiRu::GetRusOfType(m_allowedWidth, ruType, WIFI_MOD_CLASS_HE);
auto ruSet = WifiRu::GetRusOfType(m_allowedWidth, ruType, mc);
auto ruSetIt = ruSet.begin();
auto central26TonesRus =
WifiRu::GetCentral26TonesRus(m_allowedWidth, ruType, WIFI_MOD_CLASS_HE);
auto central26TonesRus = WifiRu::GetCentral26TonesRus(m_allowedWidth, ruType, mc);
auto central26TonesRusIt = central26TonesRus.begin();
for (std::size_t i = 0; i < nRusAssigned + nCentral26TonesRus; i++)

View File

@@ -148,45 +148,75 @@ SpectrumWifiPhy::GetRuBands(Ptr<WifiSpectrumPhyInterface> spectrumPhyInterface,
RuBands ruBands{};
const auto channelWidth = spectrumPhyInterface->GetChannelWidth();
const auto p20Index = GetOperatingChannel().GetPrimaryChannelIndex(MHz_u{20});
for (auto bw = channelWidth; bw >= MHz_u{20}; bw = bw / 2)
std::vector<WifiModulationClass> modClasses{WIFI_MOD_CLASS_HE};
if (GetStandard() >= WIFI_STANDARD_80211be)
{
for (uint32_t i = 0; i < (channelWidth / bw); ++i)
modClasses.push_back(WIFI_MOD_CLASS_EHT);
}
for (const auto modClass : modClasses)
{
for (MHz_u bw = channelWidth; bw >= MHz_u{20}; bw = bw / 2)
{
for (uint32_t type = 0;
type <= static_cast<uint32_t>(WifiRu::GetMaxRuType(WIFI_MOD_CLASS_HE));
++type)
if (bw > GetMaximumChannelWidth(modClass))
{
auto ruType = static_cast<RuType>(type);
std::size_t nRus = WifiRu::GetNRus(bw, ruType, WIFI_MOD_CLASS_HE);
for (std::size_t phyIndex = 1; phyIndex <= nRus; phyIndex++)
continue;
}
for (uint8_t i = 0; i < (channelWidth / bw); ++i)
{
for (uint32_t type = 0;
type <= static_cast<uint32_t>(WifiRu::GetMaxRuType(modClass));
++type)
{
const auto group =
WifiRu::GetSubcarrierGroup(bw, ruType, phyIndex, WIFI_MOD_CLASS_HE);
SubcarrierRange subcarrierRange =
std::make_pair(group.front().first, group.back().second);
const auto bandIndices =
HePhy::ConvertRuSubcarriers(bw,
guardBandwidth,
spectrumPhyInterface->GetCenterFrequencies(),
spectrumPhyInterface->GetChannelWidth(),
GetSubcarrierSpacing(),
subcarrierRange,
i);
WifiSpectrumBandInfo band{};
for (const auto& indicesPerSegment : bandIndices)
const auto ruType = static_cast<RuType>(type);
const auto nRus = WifiRu::GetNRus(bw, ruType, modClass);
std::size_t undefRus{0};
for (std::size_t phyIndex = 1; phyIndex <= nRus + undefRus; ++phyIndex)
{
band.indices.emplace_back(indicesPerSegment);
band.frequencies.emplace_back(
ConvertIndicesToFrequenciesForInterface(spectrumPhyInterface,
indicesPerSegment));
const auto group =
WifiRu::GetSubcarrierGroup(bw, ruType, phyIndex, modClass);
if (group.empty())
{
++undefRus;
continue;
}
const auto subcarrierRange =
std::make_pair(group.front().first, group.back().second);
const auto bandIndices = HePhy::ConvertRuSubcarriers(
{bw,
guardBandwidth,
spectrumPhyInterface->GetCenterFrequencies(),
spectrumPhyInterface->GetChannelWidth(),
GetSubcarrierSpacing(),
modClass,
subcarrierRange,
i});
WifiSpectrumBandInfo band{};
for (const auto& indicesPerSegment : bandIndices)
{
band.indices.emplace_back(indicesPerSegment);
band.frequencies.emplace_back(
ConvertIndicesToFrequenciesForInterface(spectrumPhyInterface,
indicesPerSegment));
}
WifiRu::RuSpec ru;
if (modClass == WIFI_MOD_CLASS_HE)
{
const auto index = HeRu::GetIndexIn80MHzSegment(bw, ruType, phyIndex);
const auto primary80 =
HeRu::GetPrimary80MHzFlag(bw, ruType, phyIndex, p20Index);
ru = HeRu::RuSpec{ruType, index, primary80};
}
else
{
const auto index = EhtRu::GetIndexIn80MHzSegment(bw, ruType, phyIndex);
const auto& [primary160, primary80OrLow80] =
EhtRu::GetPrimaryFlags(bw, ruType, phyIndex, p20Index);
ru = EhtRu::RuSpec{ruType, index, primary160, primary80OrLow80};
}
NS_ABORT_IF(WifiRu::GetPhyIndex(ru, bw, p20Index) != phyIndex);
ruBands.insert({band, ru});
}
const auto index = HeRu::GetIndexIn80MHzSegment(bw, ruType, phyIndex);
const auto primary80 =
HeRu::GetPrimary80MHzFlag(bw, ruType, phyIndex, p20Index);
const auto ru = HeRu::RuSpec{ruType, index, primary80};
NS_ABORT_IF(WifiRu::GetPhyIndex(ru, bw, p20Index) != phyIndex);
ruBands.insert({band, ru});
}
}
}

View File

@@ -116,6 +116,9 @@ static constexpr uint16_t MAX_AID{2007};
/// STA_ID for a RU that is intended for no user (Section 26.11.1 802.11ax-2021)
static constexpr uint16_t NO_USER_STA_ID{2046};
/// Unassigned 242-tone RU identifier for EHT (Table Table 36-34 IEEE 802.11be D7.0)
static constexpr uint8_t UNASSIGNED_242_TONE_EHT_RU{27};
/// The maximum value for the association ID updated since 802.11be (Sec. 9.4.1.8 of 802.11be D7.0)
static constexpr uint16_t EHT_MAX_AID{2006};

View File

@@ -691,6 +691,7 @@ WifiTxVector::GetInactiveSubchannels() const
void
WifiTxVector::SetCenter26ToneRuIndication(Center26ToneRuIndication center26ToneRuIndication)
{
NS_ASSERT(GetModulationClass() == WIFI_MOD_CLASS_HE);
if (IsDlMu())
{
NS_ASSERT(center26ToneRuIndication == DeriveCenter26ToneRuIndication());
@@ -701,7 +702,7 @@ WifiTxVector::SetCenter26ToneRuIndication(Center26ToneRuIndication center26ToneR
std::optional<Center26ToneRuIndication>
WifiTxVector::GetCenter26ToneRuIndication() const
{
if (!IsDlMu() || (m_channelWidth < MHz_u{80}))
if ((GetModulationClass() != WIFI_MOD_CLASS_HE) || !IsDlMu() || (m_channelWidth < MHz_u{80}))
{
return std::nullopt;
}
@@ -801,7 +802,10 @@ WifiTxVector::GetUserInfoMapOrderedByRus(uint8_t p20Index) const
RuAllocation
WifiTxVector::DeriveRuAllocation(uint8_t p20Index) const
{
RuAllocation ruAllocations(Count20MHzSubchannels(m_channelWidth), EMPTY_242_TONE_HE_RU);
const auto mc = GetModulationClass();
const auto emptyRu =
(mc == WIFI_MOD_CLASS_HE) ? EMPTY_242_TONE_HE_RU : UNASSIGNED_242_TONE_EHT_RU;
RuAllocation ruAllocations(Count20MHzSubchannels(m_channelWidth), emptyRu);
std::vector<RuType> ruTypes{};
ruTypes.resize(ruAllocations.size());
const auto& orderedMap = GetUserInfoMapOrderedByRus(p20Index);
@@ -817,8 +821,6 @@ WifiTxVector::DeriveRuAllocation(uint8_t p20Index) const
continue;
}
const auto ruBw = WifiRu::GetBandwidth(ruType);
NS_ASSERT_MSG(WifiRu::IsHe(ru), "EHT RUs should not be used yet");
const auto mc{WIFI_MOD_CLASS_HE};
const auto rusPerSubchannel =
WifiRu::GetRusOfType(ruBw > MHz_u{20} ? ruBw : MHz_u{20}, ruType, mc);
if ((m_channelWidth >= MHz_u{80}) && (ruIndex > 19))
@@ -835,7 +837,7 @@ WifiTxVector::DeriveRuAllocation(uint8_t p20Index) const
: ((ruIndex - 1) * numSubchannelsForRu);
NS_ABORT_IF(index >= Count20MHzSubchannels(m_channelWidth));
auto ruAlloc = WifiRu::GetEqualizedRuAllocation(ruType, false, true, mc);
if (ruAllocations.at(index) != EMPTY_242_TONE_HE_RU)
if (ruAllocations.at(index) != emptyRu)
{
if (ruType == ruTypes.at(index))
{
@@ -873,7 +875,7 @@ WifiTxVector::DeriveRuAllocation(uint8_t p20Index) const
}
for (std::size_t i = 0; i < numSubchannelsForRu; ++i)
{
auto ruAllocNoUsers = HeRu::GetEqualizedRuAllocation(ruType, false, false);
auto ruAllocNoUsers = WifiRu::GetEqualizedRuAllocation(ruType, false, false, mc);
ruTypes.at(index + i) = ruType;
ruAllocations.at(index + i) =
(IsSigBCompression() || ((index + i) % 2) == ccIndex) ? ruAlloc : ruAllocNoUsers;

View File

@@ -270,7 +270,8 @@ TxDurationTest::CheckMuTxDuration(std::list<uint32_t> sizes,
staIds.push_back(staId++);
}
txVector.SetSigBMode(VhtPhy::GetVhtMcs0());
txVector.SetRuAllocation({192, 192}, 0);
const uint16_t ruAllocPer20 = IsEht(preamble) ? 64 : 192;
txVector.SetRuAllocation({ruAllocPer20, ruAllocPer20}, 0);
Ptr<YansWifiPhy> phy = CreateObject<YansWifiPhy>();
std::list<WifiPhyBand> testedBands;
@@ -1950,31 +1951,32 @@ TxDurationTest::DoRun()
NS_TEST_EXPECT_MSG_EQ(retval, true, "an 802.11be SU duration failed");
// 802.11be MU durations
retval = retval &&
CheckMuTxDuration(
std::list<uint32_t>{1536, 1536},
std::list<HeMuUserInfo>{{HeRu::RuSpec{RuType::RU_242_TONE, 1, true}, 0, 1},
{HeRu::RuSpec{RuType::RU_242_TONE, 2, true}, 0, 1}},
MHz_u{40},
NanoSeconds(800),
WIFI_PREAMBLE_EHT_MU,
NanoSeconds(1493600)) // equivalent to 802.11ax MU
&& CheckMuTxDuration(
std::list<uint32_t>{1536, 1536},
std::list<HeMuUserInfo>{{HeRu::RuSpec{RuType::RU_242_TONE, 1, true}, 1, 1},
{HeRu::RuSpec{RuType::RU_242_TONE, 2, true}, 0, 1}},
MHz_u{40},
NanoSeconds(800),
WIFI_PREAMBLE_EHT_MU,
NanoSeconds(1493600)) // shouldn't change if first PSDU is shorter
&& CheckMuTxDuration(
std::list<uint32_t>{1536, 76},
std::list<HeMuUserInfo>{{HeRu::RuSpec{RuType::RU_242_TONE, 1, true}, 0, 1},
{HeRu::RuSpec{RuType::RU_242_TONE, 2, true}, 0, 1}},
MHz_u{40},
NanoSeconds(800),
WIFI_PREAMBLE_EHT_MU,
NanoSeconds(1493600));
retval =
retval &&
CheckMuTxDuration(
std::list<uint32_t>{1536, 1536},
std::list<HeMuUserInfo>{{EhtRu::RuSpec{RuType::RU_242_TONE, 1, true, true}, 0, 1},
{EhtRu::RuSpec{RuType::RU_242_TONE, 2, true, true}, 0, 1}},
MHz_u{40},
NanoSeconds(800),
WIFI_PREAMBLE_EHT_MU,
NanoSeconds(1493600)) // equivalent to 802.11ax MU
&& CheckMuTxDuration(
std::list<uint32_t>{1536, 1536},
std::list<HeMuUserInfo>{{EhtRu::RuSpec{RuType::RU_242_TONE, 1, true, true}, 1, 1},
{EhtRu::RuSpec{RuType::RU_242_TONE, 2, true, true}, 0, 1}},
MHz_u{40},
NanoSeconds(800),
WIFI_PREAMBLE_EHT_MU,
NanoSeconds(1493600)) // shouldn't change if first PSDU is shorter
&& CheckMuTxDuration(
std::list<uint32_t>{1536, 76},
std::list<HeMuUserInfo>{{EhtRu::RuSpec{RuType::RU_242_TONE, 1, true, true}, 0, 1},
{EhtRu::RuSpec{RuType::RU_242_TONE, 2, true, true}, 0, 1}},
MHz_u{40},
NanoSeconds(800),
WIFI_PREAMBLE_EHT_MU,
NanoSeconds(1493600));
NS_TEST_EXPECT_MSG_EQ(retval, true, "an 802.11be MU duration failed");
@@ -2116,6 +2118,7 @@ HeSigBDurationTest::DoRun()
// Verify number of users for content channels 1 and 2
const auto& numUsersPerCc = HePpdu::GetNumRusPerHeSigBContentChannel(
txVector.GetChannelWidth(),
txVector.GetModulationClass(),
txVector.GetRuAllocation(m_p20Index),
txVector.GetCenter26ToneRuIndication(),
txVector.IsSigBCompression(),
@@ -2485,8 +2488,10 @@ PhyHeaderSectionsTest::DoRun()
txVector.SetNss(2); // EHT-LTF duration assumed to be always 8 us for the time being (see note
// in HePhy::GetTrainingDuration)
txVector.SetMode(EhtPhy::GetEhtMcs9());
userInfoMap = {{1, {HeRu::RuSpec{RuType::RU_106_TONE, 1, true}, 4, 2}},
{2, {HeRu::RuSpec{RuType::RU_106_TONE, 1, true}, 9, 1}}};
userInfoMap = {{1, {EhtRu::RuSpec{RuType::RU_52_TONE, 1, true, true}, 4, 2}},
{2, {EhtRu::RuSpec{RuType::RU_52_TONE, 2, true, true}, 9, 1}},
{3, {EhtRu::RuSpec{RuType::RU_52_TONE, 3, true, true}, 4, 2}},
{4, {EhtRu::RuSpec{RuType::RU_52_TONE, 4, true, true}, 9, 1}}};
WifiMode uSigMode = EhtPhy::GetVhtMcs0();
WifiMode ehtSigMode = EhtPhy::GetVhtMcs4(); // because of first user info map
@@ -2505,7 +2510,7 @@ PhyHeaderSectionsTest::DoRun()
// -> EHT MU format
txVector.SetPreambleType(WIFI_PREAMBLE_EHT_MU);
txVector.SetEhtPpduType(0); // EHT MU transmission
txVector.SetRuAllocation({96}, 0);
txVector.SetRuAllocation({24}, 0);
sections[WIFI_PPDU_FIELD_U_SIG] = {{ppduStart + MicroSeconds(24), ppduStart + MicroSeconds(32)},
uSigMode};
sections[WIFI_PPDU_FIELD_EHT_SIG] = {
@@ -2517,7 +2522,7 @@ PhyHeaderSectionsTest::DoRun()
ehtSigMode};
CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
txVector.SetChannelWidth(MHz_u{160}); // shouldn't have any impact
txVector.SetRuAllocation({96, 113, 113, 113, 113, 113, 113, 113}, 0);
txVector.SetRuAllocation({24, 27, 27, 27, 27, 27, 27, 27}, 0);
CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
}

View File

@@ -305,22 +305,38 @@ TestMultiUserScheduler::ComputeWifiTxVector()
RuType ruType;
switch (static_cast<uint16_t>(bw))
{
case 20:
case 20: {
ruType = RuType::RU_52_TONE;
m_txVector.SetRuAllocation({112}, 0);
const uint16_t ruAllocPer20 = IsEht(m_txVector.GetPreambleType()) ? 24 : 112;
m_txVector.SetRuAllocation({ruAllocPer20}, 0);
break;
case 40:
}
case 40: {
ruType = RuType::RU_106_TONE;
m_txVector.SetRuAllocation({96, 96}, 0);
const uint16_t ruAllocPer20 = IsEht(m_txVector.GetPreambleType()) ? 48 : 96;
m_txVector.SetRuAllocation({ruAllocPer20, ruAllocPer20}, 0);
break;
case 80:
}
case 80: {
ruType = RuType::RU_242_TONE;
m_txVector.SetRuAllocation({192, 192, 192, 192}, 0);
const uint16_t ruAllocPer20 = IsEht(m_txVector.GetPreambleType()) ? 64 : 112;
m_txVector.SetRuAllocation({ruAllocPer20, ruAllocPer20, ruAllocPer20, ruAllocPer20}, 0);
break;
case 160:
}
case 160: {
ruType = RuType::RU_484_TONE;
m_txVector.SetRuAllocation({200, 200, 200, 200, 200, 200, 200, 200}, 0);
const uint16_t ruAllocPer20 = IsEht(m_txVector.GetPreambleType()) ? 72 : 200;
m_txVector.SetRuAllocation({ruAllocPer20,
ruAllocPer20,
ruAllocPer20,
ruAllocPer20,
ruAllocPer20,
ruAllocPer20,
ruAllocPer20,
ruAllocPer20},
0);
break;
}
default:
NS_ABORT_MSG("Unsupported channel width");
}
@@ -330,12 +346,20 @@ TestMultiUserScheduler::ComputeWifiTxVector()
for (auto& sta : staList)
{
auto index{ruIndex};
if ((bw == MHz_u{160}) && (ruIndex == 3))
if (!IsEht(m_txVector.GetPreambleType()) && (bw == MHz_u{160}) && (ruIndex >= 3))
{
index = HeRu::GetIndexIn80MHzSegment(bw, ruType, ruIndex);
primary80 = HeRu::GetPrimary80MHzFlag(bw, ruType, ruIndex, 0);
index = ruIndex - 2;
primary80 = false;
}
m_txVector.SetHeMuUserInfo(sta.first, {HeRu::RuSpec{ruType, index, primary80}, 11, 1});
if (IsEht(m_txVector.GetPreambleType()) && (bw > MHz_u{80}))
{
index = EhtRu::GetIndexIn80MHzSegment(bw, ruType, ruIndex);
primary80 = EhtRu::GetPrimaryFlags(bw, ruType, ruIndex, 0).second;
}
const auto ru = IsEht(m_txVector.GetPreambleType())
? WifiRu::RuSpec(EhtRu::RuSpec{ruType, index, true, primary80})
: WifiRu::RuSpec(HeRu::RuSpec{ruType, index, primary80});
m_txVector.SetHeMuUserInfo(sta.first, {ru, 11, 1});
ruIndex++;
}
m_txVector.SetSigBMode(VhtPhy::GetVhtMcs5());

View File

@@ -148,14 +148,15 @@ OfdmaTestHePhy::GetNonOfdmaBand(const WifiTxVector& txVector, uint16_t staId) co
WifiRu::GetPhyIndex(nonOfdmaRu,
channelWidth,
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(MHz_u{20})));
const auto indices =
ConvertRuSubcarriers(channelWidth,
GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
m_wifiPhy->GetOperatingChannel().GetFrequencies(),
m_wifiPhy->GetChannelWidth(),
m_wifiPhy->GetSubcarrierSpacing(),
{groupPreamble.front().first, groupPreamble.back().second},
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth));
const auto indices = ConvertRuSubcarriers(
{channelWidth,
GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
m_wifiPhy->GetOperatingChannel().GetFrequencies(),
m_wifiPhy->GetChannelWidth(),
m_wifiPhy->GetSubcarrierSpacing(),
txVector.GetModulationClass(),
{groupPreamble.front().first, groupPreamble.back().second},
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth)});
WifiSpectrumBandInfo nonOfdmaBand{};
for (const auto& indicesPerSegment : indices)
{