wifi: Add a method to compute the indices of the 20MHz channel covering an RU (with unit test)

This commit is contained in:
Stefano Avallone
2023-01-18 17:23:41 +01:00
parent 514cfd612f
commit 9a812baf28
4 changed files with 822 additions and 0 deletions

View File

@@ -341,6 +341,7 @@ build_lib(
test/wifi-phy-reception-test.cc
test/wifi-phy-thresholds-test.cc
test/wifi-primary-channels-test.cc
test/wifi-ru-allocation-test.cc
test/wifi-channel-switching-test.cc
test/wifi-test.cc
test/wifi-transmit-mask-test.cc

View File

@@ -580,4 +580,117 @@ WifiPhyOperatingChannel::GetAll20MHzChannelIndicesInSecondary(
return secondaryIndices;
}
std::set<uint8_t>
WifiPhyOperatingChannel::Get20MHzIndicesCoveringRu(HeRu::RuSpec ru, uint16_t width) const
{
auto ruType = ru.GetRuType();
NS_ASSERT_MSG(HeRu::GetBandwidth(ruType) <= width,
"No RU of type " << ruType << " is contained in a " << width << " MHz channel");
NS_ASSERT_MSG(width <= GetWidth(),
"The given width (" << width << " MHz) exceeds the operational width ("
<< GetWidth() << " MHz)");
// trivial case: 2x996-tone RU
if (ruType == HeRu::RU_2x996_TONE)
{
return {0, 1, 2, 3, 4, 5, 6, 7};
}
// handle first the special case of center 26-tone RUs
if (ruType == HeRu::RU_26_TONE && ru.GetIndex() == 19)
{
NS_ASSERT_MSG(width >= 80,
"26-tone RU with index 19 is only present in channels of at least 80 MHz");
// the center 26-tone RU in an 80 MHz channel is not fully covered by
// any 20 MHz channel, but by the two central 20 MHz channels in the 80 MHz channel
auto indices = ru.GetPrimary80MHz() ? GetAll20MHzChannelIndicesInPrimary(80)
: GetAll20MHzChannelIndicesInSecondary(80);
indices.erase(indices.begin());
indices.erase(std::prev(indices.end()));
return indices;
}
auto ruIndex = ru.GetIndex();
if (ruType == HeRu::RU_26_TONE && ruIndex > 19)
{
// "ignore" the center 26-tone RU in an 80 MHz channel
ruIndex--;
}
// if the RU refers to a 160 MHz channel, we have to update the RU index (which
// refers to an 80 MHz channel) if the RU is not in the lower 80 MHz channel
if (width == 160)
{
bool primary80IsLower80 = (m_primary20Index < 4);
if (primary80IsLower80 != ru.GetPrimary80MHz())
{
auto nRusIn80MHz = HeRu::GetNRus(80, ruType);
// "ignore" the center 26-tone RU in an 80 MHz channel
if (ruType == HeRu::RU_26_TONE)
{
nRusIn80MHz--;
}
ruIndex += nRusIn80MHz;
}
}
uint8_t n20MHzChannels; // number of 20 MHz channels in the channel covering the RU
switch (ruType)
{
case HeRu::RU_26_TONE:
case HeRu::RU_52_TONE:
case HeRu::RU_106_TONE:
case HeRu::RU_242_TONE:
n20MHzChannels = 1;
break;
case HeRu::RU_484_TONE:
n20MHzChannels = 2;
break;
case HeRu::RU_996_TONE:
n20MHzChannels = 4;
break;
default:
NS_ABORT_MSG("Unhandled RU type: " << ruType);
}
auto nRusInCoveringChannel = HeRu::GetNRus(n20MHzChannels * 20, ruType);
// compute the index (starting at 0) of the covering channel within the given width
std::size_t indexOfCoveringChannelInGivenWidth = (ruIndex - 1) / nRusInCoveringChannel;
// expand the index of the covering channel in the indices of its constituent
// 20 MHz channels (within the given width)
NS_ASSERT(indexOfCoveringChannelInGivenWidth < 8); // max number of 20 MHz channels
std::set<uint8_t> indices({static_cast<uint8_t>(indexOfCoveringChannelInGivenWidth)});
while (n20MHzChannels > 1)
{
std::set<uint8_t> updatedIndices;
for (const auto& idx : indices)
{
updatedIndices.insert(idx * 2);
updatedIndices.insert(idx * 2 + 1);
}
indices.swap(updatedIndices);
n20MHzChannels /= 2;
}
// finally, add the appropriate offset if width is less than the operational channel width
auto offset = GetPrimaryChannelIndex(width) * width / 20;
if (offset > 0)
{
std::set<uint8_t> updatedIndices;
for (const auto& idx : indices)
{
updatedIndices.insert(idx + offset);
}
indices.swap(updatedIndices);
}
return indices;
}
} // namespace ns3

View File

@@ -24,6 +24,8 @@
#include "wifi-phy-band.h"
#include "wifi-standards.h"
#include "ns3/he-ru.h"
#include <set>
#include <tuple>
@@ -263,6 +265,16 @@ class WifiPhyOperatingChannel
*/
uint8_t GetPrimaryChannelNumber(uint16_t primaryChannelWidth, WifiStandard standard) const;
/**
* Get the channel indices of the minimum subset of 20 MHz channels containing the given RU.
*
* \param ru the given RU
* \param width the width in MHz of the channel to which the given RU refers to; normally,
* it is the width in MHz of the PPDU for which the RU is allocated
* \return the channel indices of the minimum subset of 20 MHz channels containing the given RU
*/
std::set<uint8_t> Get20MHzIndicesCoveringRu(HeRu::RuSpec ru, uint16_t width) const;
private:
ConstIterator m_channelIt; //!< const iterator pointing to the configured frequency channel
uint8_t m_primary20Index; /**< index of the primary20 channel (0 indicates the 20 MHz

View File

@@ -0,0 +1,696 @@
/*
* Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Stefano Avallone <stavallo@unina.it>
*/
#include "ns3/test.h"
#include "ns3/wifi-phy-operating-channel.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("WifiRuAllocationTest");
/**
* \ingroup wifi-test
* \ingroup tests
*
* \brief Test the WifiPhyOperatingChannel::Get20MHzIndicesCoveringRu() method.
*/
class Wifi20MHzIndicesCoveringRuTest : public TestCase
{
public:
/**
* Constructor
*/
Wifi20MHzIndicesCoveringRuTest();
~Wifi20MHzIndicesCoveringRuTest() override = default;
/**
* Check that the indices of the 20 MHz channels covering the given RU as computed
* by WifiPhyOperatingChannel::Get20MHzIndicesCoveringRu() are correct.
*
* \param primary20 the index of the primary20 channel to configure
* \param ru the given RU
* \param width the width in MHz of the channel to which the given RU refers to; normally,
* it is the width in MHz of the PPDU for which the RU is allocated
* \param indices the expected indices
*/
void RunOne(uint8_t primary20,
HeRu::RuSpec ru,
uint16_t width,
const std::set<uint8_t>& indices);
private:
void DoRun() override;
WifiPhyOperatingChannel m_channel; //!< operating channel
};
Wifi20MHzIndicesCoveringRuTest::Wifi20MHzIndicesCoveringRuTest()
: TestCase("Check computation of the indices of the 20 MHz channels covering an RU")
{
}
void
Wifi20MHzIndicesCoveringRuTest::RunOne(uint8_t primary20,
HeRu::RuSpec ru,
uint16_t width,
const std::set<uint8_t>& indices)
{
auto printToStr = [](const std::set<uint8_t>& s) {
std::stringstream ss;
ss << "{";
for (const auto& index : s)
{
ss << +index << " ";
}
ss << "}";
return ss.str();
};
m_channel.SetPrimary20Index(primary20);
auto actualIndices = m_channel.Get20MHzIndicesCoveringRu(ru, width);
NS_TEST_ASSERT_MSG_EQ((actualIndices == indices),
true,
"Channel width=" << m_channel.GetWidth() << " MHz, PPDU width=" << width
<< " MHz, p20Index=" << +primary20 << " , RU=" << ru
<< ". Expected indices " << printToStr(indices)
<< " differs from actual " << printToStr(actualIndices));
}
void
Wifi20MHzIndicesCoveringRuTest::DoRun()
{
/******************
* 20 MHz channel *
******************/
m_channel.SetDefault(20, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
/* 20 MHz PPDU */
{
const uint16_t width = 20;
const uint8_t p20Index = 0;
// All the 9 26-tone RUs are covered by the unique 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
}
// All the 4 52-tone RUs are covered by the unique 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
}
// Both 106-tone RUs are covered by the unique 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
}
// The 242-tone RU is covered by the unique 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
}
/******************
* 40 MHz channel *
******************/
m_channel.SetDefault(40, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
/* 20 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 2; p20Index++)
{
const uint16_t width = 20;
// All the 9 26-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
}
// All the 4 52-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
}
// Both 106-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
}
// The 242-tone RU is covered by the primary 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
}
/* 40 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 2; p20Index++)
{
const uint16_t width = 40;
// The first 9 26-tone RUs are covered by the first 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {0});
}
// The second 9 26-tone RUs are covered by the second 20 MHz channel
for (std::size_t idx = 10; idx <= 18; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {1});
}
// The first 4 52-tone RUs are covered by the first 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {0});
}
// The second 4 52-tone RUs are covered by the second 20 MHz channel
for (std::size_t idx = 5; idx <= 8; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {1});
}
// The first 2 106-tone RUs are covered by the first 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {0});
}
// The second 2 106-tone RUs are covered by the second 20 MHz channel
for (std::size_t idx = 3; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {1});
}
// The first 242-tone RU is covered by the first 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {0});
// The second 242-tone RU is covered by the second 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {1});
// The 484-tone RU is covered by both 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {0, 1});
}
/******************
* 80 MHz channel *
******************/
m_channel.SetDefault(80, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
/* 20 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 4; p20Index++)
{
const uint16_t width = 20;
// All the 9 26-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
}
// All the 4 52-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
}
// Both 106-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
}
// The 242-tone RU is covered by the primary 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
}
/* 40 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 4; p20Index++)
{
const uint16_t width = 40;
// PPDU is transmitted on P40, which may be in the lower or higher 40 MHz
const uint8_t p40Index = p20Index / 2;
// RUs can be allocated in one (or both) of the two 20 MHz channels in P40
const uint8_t ch20Index0 = p40Index * 2;
const uint8_t ch20Index1 = p40Index * 2 + 1;
// The first 9 26-tone RUs are in the lower 20 MHz of the PPDU bandwidth
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index0});
}
// The second 9 26-tone RUs are in the higher 20 MHz of the PPDU bandwidth
for (std::size_t idx = 10; idx <= 18; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index1});
}
// The first 4 52-tone RUs are in the lower 20 MHz of the PPDU bandwidth
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index0});
}
// The second 4 52-tone RUs are in the higher 20 MHz of the PPDU bandwidth
for (std::size_t idx = 5; idx <= 8; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index1});
}
// The first 2 106-tone RUs are in the lower 20 MHz of the PPDU bandwidth
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index0});
}
// The second 2 106-tone RUs are in the higher 20 MHz of the PPDU bandwidth
for (std::size_t idx = 3; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index1});
}
// The first 242-tone RU is in the lower 20 MHz of the PPDU bandwidth
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {ch20Index0});
// The second 242-tone RU is in the higher 20 MHz of the PPDU bandwidth
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {ch20Index1});
// The 484-tone RU is covered by both 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {ch20Index0, ch20Index1});
}
/* 80 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 4; p20Index++)
{
const uint16_t width = 80;
// The first 9 26-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {0});
}
// The second 9 26-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 10; idx <= 18; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {1});
}
// The center 26-tone RU is covered by the central 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, 19, true), width, {1, 2});
// The following 9 26-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 20; idx <= 28; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {2});
}
// The last 9 26-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 29; idx <= 37; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {3});
}
// The first 4 52-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {0});
}
// The second 4 52-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 5; idx <= 8; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {1});
}
// The third 4 52-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 9; idx <= 12; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {2});
}
// The fourth 4 52-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 13; idx <= 16; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {3});
}
// The first 2 106-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {0});
}
// The second 2 106-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 3; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {1});
}
// The third 2 106-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 5; idx <= 6; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {2});
}
// The fourth 2 106-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 7; idx <= 8; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {3});
}
// The first 242-tone RU is in the first 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {0});
// The second 242-tone RU is in the second 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {1});
// The third 242-tone RU is in the third 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 3, true), width, {2});
// The fourth 242-tone RU is in the fourth 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 4, true), width, {3});
// The first 484-tone RU is covered by the first two 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {0, 1});
// The second 484-tone RU is covered by the last two 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 2, true), width, {2, 3});
// The 996-tone RU is covered by all the 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_996_TONE, 1, true), width, {0, 1, 2, 3});
}
/******************
* 160 MHz channel *
******************/
m_channel.SetDefault(160, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
/* 20 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
{
const uint16_t width = 20;
// All the 9 26-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
}
// All the 4 52-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
}
// Both 106-tone RUs are covered by the primary 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
}
// The 242-tone RU is covered by the primary 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
}
/* 40 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
{
const uint16_t width = 40;
// PPDU is transmitted on P40, which is one of the four 40 MHz channels
const uint8_t p40Index = p20Index / 2;
// RUs can be allocated in one (or both) of the two 20 MHz channels in P40
const uint8_t ch20Index0 = p40Index * 2;
const uint8_t ch20Index1 = p40Index * 2 + 1;
// The first 9 26-tone RUs are in the lower 20 MHz of the PPDU bandwidth
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index0});
}
// The second 9 26-tone RUs are in the higher 20 MHz of the PPDU bandwidth
for (std::size_t idx = 10; idx <= 18; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index1});
}
// The first 4 52-tone RUs are in the lower 20 MHz of the PPDU bandwidth
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index0});
}
// The second 4 52-tone RUs are in the higher 20 MHz of the PPDU bandwidth
for (std::size_t idx = 5; idx <= 8; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index1});
}
// The first 2 106-tone RUs are in the lower 20 MHz of the PPDU bandwidth
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index0});
}
// The second 2 106-tone RUs are in the higher 20 MHz of the PPDU bandwidth
for (std::size_t idx = 3; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index1});
}
// The first 242-tone RU is in the lower 20 MHz of the PPDU bandwidth
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {ch20Index0});
// The second 242-tone RU is in the higher 20 MHz of the PPDU bandwidth
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {ch20Index1});
// The 484-tone RU is covered by both 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {ch20Index0, ch20Index1});
}
/* 80 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
{
const uint16_t width = 80;
// PPDU is transmitted on P80, which is one of the two 80 MHz channels
const uint8_t p80Index = p20Index / 4;
// RUs can be allocated in one (or more) of the four 20 MHz channels in P80
const uint8_t ch20Index0 = p80Index * 4;
const uint8_t ch20Index1 = p80Index * 4 + 1;
const uint8_t ch20Index2 = p80Index * 4 + 2;
const uint8_t ch20Index3 = p80Index * 4 + 3;
// The first 9 26-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index0});
}
// The second 9 26-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 10; idx <= 18; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index1});
}
// The center 26-tone RU is covered by the central 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, 19, true), width, {ch20Index1, ch20Index2});
// The following 9 26-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 20; idx <= 28; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index2});
}
// The last 9 26-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 29; idx <= 37; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index3});
}
// The first 4 52-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index0});
}
// The second 4 52-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 5; idx <= 8; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index1});
}
// The third 4 52-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 9; idx <= 12; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index2});
}
// The fourth 4 52-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 13; idx <= 16; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index3});
}
// The first 2 106-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index0});
}
// The second 2 106-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 3; idx <= 4; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index1});
}
// The third 2 106-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 5; idx <= 6; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index2});
}
// The fourth 2 106-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 7; idx <= 8; idx++)
{
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index3});
}
// The first 242-tone RU is in the first 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {ch20Index0});
// The second 242-tone RU is in the second 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {ch20Index1});
// The third 242-tone RU is in the third 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 3, true), width, {ch20Index2});
// The fourth 242-tone RU is in the fourth 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 4, true), width, {ch20Index3});
// The first 484-tone RU is covered by the first two 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {ch20Index0, ch20Index1});
// The second 484-tone RU is covered by the last two 20 MHz channels
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 2, true), width, {ch20Index2, ch20Index3});
// The 996-tone RU is covered by all the 20 MHz channels
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_996_TONE, 1, true),
width,
{ch20Index0, ch20Index1, ch20Index2, ch20Index3});
}
/* 160 MHz PPDU */
for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
{
const uint16_t width = 160;
for (auto primary80MHz : {true, false})
{
// RUs can be allocated in one (or more) of the four 20 MHz channels in P80/S80
// (depending on the primary80MHz flag)
const uint8_t p80Index = (primary80MHz == (p20Index < 4)) ? 0 : 1;
const uint8_t ch20Index0 = p80Index * 4;
const uint8_t ch20Index1 = p80Index * 4 + 1;
const uint8_t ch20Index2 = p80Index * 4 + 2;
const uint8_t ch20Index3 = p80Index * 4 + 3;
// The first 9 26-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 9; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
width,
{ch20Index0});
}
// The second 9 26-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 10; idx <= 18; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
width,
{ch20Index1});
}
// The center 26-tone RU is covered by the central 20 MHz channels
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_26_TONE, 19, primary80MHz),
width,
{ch20Index1, ch20Index2});
// The following 9 26-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 20; idx <= 28; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
width,
{ch20Index2});
}
// The last 9 26-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 29; idx <= 37; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
width,
{ch20Index3});
}
// The first 4 52-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 4; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
width,
{ch20Index0});
}
// The second 4 52-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 5; idx <= 8; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
width,
{ch20Index1});
}
// The third 4 52-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 9; idx <= 12; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
width,
{ch20Index2});
}
// The fourth 4 52-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 13; idx <= 16; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
width,
{ch20Index3});
}
// The first 2 106-tone RUs are in the first 20 MHz channel
for (std::size_t idx = 1; idx <= 2; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
width,
{ch20Index0});
}
// The second 2 106-tone RUs are in the second 20 MHz channel
for (std::size_t idx = 3; idx <= 4; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
width,
{ch20Index1});
}
// The third 2 106-tone RUs are in the third 20 MHz channel
for (std::size_t idx = 5; idx <= 6; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
width,
{ch20Index2});
}
// The fourth 2 106-tone RUs are in the fourth 20 MHz channel
for (std::size_t idx = 7; idx <= 8; idx++)
{
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
width,
{ch20Index3});
}
// The first 242-tone RU is in the first 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, primary80MHz), width, {ch20Index0});
// The second 242-tone RU is in the second 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, primary80MHz), width, {ch20Index1});
// The third 242-tone RU is in the third 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 3, primary80MHz), width, {ch20Index2});
// The fourth 242-tone RU is in the fourth 20 MHz channel
RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 4, primary80MHz), width, {ch20Index3});
// The first 484-tone RU is covered by the first two 20 MHz channels
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_484_TONE, 1, primary80MHz),
width,
{ch20Index0, ch20Index1});
// The second 484-tone RU is covered by the last two 20 MHz channels
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_484_TONE, 2, primary80MHz),
width,
{ch20Index2, ch20Index3});
// The 996-tone RU is covered by all the 20 MHz channels
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_996_TONE, 1, primary80MHz),
width,
{ch20Index0, ch20Index1, ch20Index2, ch20Index3});
}
// The 2x996-tone RU is covered by all the eight 20 MHz channels
RunOne(p20Index,
HeRu::RuSpec(HeRu::RU_2x996_TONE, 1, true),
width,
{0, 1, 2, 3, 4, 5, 6, 7});
}
}
/**
* \ingroup wifi-test
* \ingroup tests
*
* \brief wifi primary channels test suite
*/
class WifiRuAllocationTestSuite : public TestSuite
{
public:
WifiRuAllocationTestSuite();
};
WifiRuAllocationTestSuite::WifiRuAllocationTestSuite()
: TestSuite("wifi-ru-allocation", UNIT)
{
AddTestCase(new Wifi20MHzIndicesCoveringRuTest(), TestCase::QUICK);
}
static WifiRuAllocationTestSuite g_wifiRuAllocationTestSuite; ///< the test suite