Files
unison/src/wifi/model/ctrl-headers.cc

2631 lines
68 KiB
C++

/*
* Copyright (c) 2009 MIRKO BANCHI
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Author: Mirko Banchi <mk.banchi@gmail.com>
*/
#include "ctrl-headers.h"
#include "wifi-tx-vector.h"
#include "wifi-utils.h"
#include "ns3/address-utils.h"
#include "ns3/he-phy.h"
#include <algorithm>
namespace ns3
{
/***********************************
* Block ack request
***********************************/
NS_OBJECT_ENSURE_REGISTERED(CtrlBAckRequestHeader);
CtrlBAckRequestHeader::CtrlBAckRequestHeader()
: m_barAckPolicy(false),
m_barType(BlockAckReqType::BASIC)
{
}
CtrlBAckRequestHeader::~CtrlBAckRequestHeader()
{
}
TypeId
CtrlBAckRequestHeader::GetTypeId()
{
static TypeId tid = TypeId("ns3::CtrlBAckRequestHeader")
.SetParent<Header>()
.SetGroupName("Wifi")
.AddConstructor<CtrlBAckRequestHeader>();
return tid;
}
TypeId
CtrlBAckRequestHeader::GetInstanceTypeId() const
{
return GetTypeId();
}
void
CtrlBAckRequestHeader::Print(std::ostream& os) const
{
os << "TID_INFO=" << m_tidInfo << ", StartingSeq=" << std::hex << m_startingSeq << std::dec;
}
uint32_t
CtrlBAckRequestHeader::GetSerializedSize() const
{
uint32_t size = 0;
size += 2; // Bar control
switch (m_barType.m_variant)
{
case BlockAckReqType::BASIC:
case BlockAckReqType::COMPRESSED:
case BlockAckReqType::EXTENDED_COMPRESSED:
size += 2;
break;
case BlockAckReqType::MULTI_TID:
size += (2 + 2) * (m_tidInfo + 1);
break;
case BlockAckReqType::GCR:
size += (2 + 6); // SSC plus GCR Group Address
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
return size;
}
void
CtrlBAckRequestHeader::Serialize(Buffer::Iterator start) const
{
Buffer::Iterator i = start;
i.WriteHtolsbU16(GetBarControl());
switch (m_barType.m_variant)
{
case BlockAckReqType::BASIC:
case BlockAckReqType::COMPRESSED:
case BlockAckReqType::EXTENDED_COMPRESSED:
i.WriteHtolsbU16(GetStartingSequenceControl());
break;
case BlockAckReqType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
case BlockAckReqType::GCR:
i.WriteHtolsbU16(GetStartingSequenceControl());
WriteTo(i, m_gcrAddress);
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
}
uint32_t
CtrlBAckRequestHeader::Deserialize(Buffer::Iterator start)
{
Buffer::Iterator i = start;
SetBarControl(i.ReadLsbtohU16());
switch (m_barType.m_variant)
{
case BlockAckReqType::BASIC:
case BlockAckReqType::COMPRESSED:
case BlockAckReqType::EXTENDED_COMPRESSED:
SetStartingSequenceControl(i.ReadLsbtohU16());
break;
case BlockAckReqType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
case BlockAckReqType::GCR:
SetStartingSequenceControl(i.ReadLsbtohU16());
ReadFrom(i, m_gcrAddress);
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
return i.GetDistanceFrom(start);
}
uint16_t
CtrlBAckRequestHeader::GetBarControl() const
{
uint16_t res = 0;
switch (m_barType.m_variant)
{
case BlockAckReqType::BASIC:
break;
case BlockAckReqType::COMPRESSED:
res |= (0x02 << 1);
break;
case BlockAckReqType::EXTENDED_COMPRESSED:
res |= (0x01 << 1);
break;
case BlockAckReqType::MULTI_TID:
res |= (0x03 << 1);
break;
case BlockAckReqType::GCR:
res |= (0x06 << 1);
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
res |= (m_tidInfo << 12) & (0xf << 12);
return res;
}
void
CtrlBAckRequestHeader::SetBarControl(uint16_t bar)
{
m_barAckPolicy = ((bar & 0x01) == 1);
if (((bar >> 1) & 0x0f) == 0x06)
{
m_barType.m_variant = BlockAckReqType::GCR;
}
else if (((bar >> 1) & 0x0f) == 0x03)
{
m_barType.m_variant = BlockAckReqType::MULTI_TID;
}
else if (((bar >> 1) & 0x0f) == 0x01)
{
m_barType.m_variant = BlockAckReqType::EXTENDED_COMPRESSED;
}
else if (((bar >> 1) & 0x0f) == 0x02)
{
m_barType.m_variant = BlockAckReqType::COMPRESSED;
}
else
{
m_barType.m_variant = BlockAckReqType::BASIC;
}
m_tidInfo = (bar >> 12) & 0x0f;
}
uint16_t
CtrlBAckRequestHeader::GetStartingSequenceControl() const
{
return (m_startingSeq << 4) & 0xfff0;
}
void
CtrlBAckRequestHeader::SetStartingSequenceControl(uint16_t seqControl)
{
m_startingSeq = (seqControl >> 4) & 0x0fff;
}
void
CtrlBAckRequestHeader::SetHtImmediateAck(bool immediateAck)
{
m_barAckPolicy = immediateAck;
}
void
CtrlBAckRequestHeader::SetType(BlockAckReqType type)
{
m_barType = type;
}
BlockAckReqType
CtrlBAckRequestHeader::GetType() const
{
return m_barType;
}
void
CtrlBAckRequestHeader::SetTidInfo(uint8_t tid)
{
m_tidInfo = static_cast<uint16_t>(tid);
}
void
CtrlBAckRequestHeader::SetStartingSequence(uint16_t seq)
{
m_startingSeq = seq;
}
bool
CtrlBAckRequestHeader::MustSendHtImmediateAck() const
{
return m_barAckPolicy;
}
uint8_t
CtrlBAckRequestHeader::GetTidInfo() const
{
auto tid = static_cast<uint8_t>(m_tidInfo);
return tid;
}
uint16_t
CtrlBAckRequestHeader::GetStartingSequence() const
{
return m_startingSeq;
}
void
CtrlBAckRequestHeader::SetGcrGroupAddress(const Mac48Address& address)
{
NS_ASSERT(IsGcr());
m_gcrAddress = address;
}
Mac48Address
CtrlBAckRequestHeader::GetGcrGroupAddress() const
{
NS_ASSERT(IsGcr());
return m_gcrAddress;
}
bool
CtrlBAckRequestHeader::IsBasic() const
{
return m_barType.m_variant == BlockAckReqType::BASIC;
}
bool
CtrlBAckRequestHeader::IsCompressed() const
{
return m_barType.m_variant == BlockAckReqType::COMPRESSED;
}
bool
CtrlBAckRequestHeader::IsExtendedCompressed() const
{
return m_barType.m_variant == BlockAckReqType::EXTENDED_COMPRESSED;
}
bool
CtrlBAckRequestHeader::IsMultiTid() const
{
return m_barType.m_variant == BlockAckReqType::MULTI_TID;
}
bool
CtrlBAckRequestHeader::IsGcr() const
{
return m_barType.m_variant == BlockAckReqType::GCR;
}
/***********************************
* Block ack response
***********************************/
NS_OBJECT_ENSURE_REGISTERED(CtrlBAckResponseHeader);
CtrlBAckResponseHeader::CtrlBAckResponseHeader()
: m_baAckPolicy(false),
m_tidInfo(0)
{
SetType(BlockAckType::BASIC);
}
CtrlBAckResponseHeader::~CtrlBAckResponseHeader()
{
}
TypeId
CtrlBAckResponseHeader::GetTypeId()
{
static TypeId tid = TypeId("ns3::CtrlBAckResponseHeader")
.SetParent<Header>()
.SetGroupName("Wifi")
.AddConstructor<CtrlBAckResponseHeader>();
return tid;
}
TypeId
CtrlBAckResponseHeader::GetInstanceTypeId() const
{
return GetTypeId();
}
void
CtrlBAckResponseHeader::Print(std::ostream& os) const
{
if (m_baType.m_variant != BlockAckType::MULTI_STA)
{
os << "TID_INFO=" << m_tidInfo << ", StartingSeq=0x" << std::hex
<< m_baInfo[0].m_startingSeq << std::dec;
}
else
{
for (std::size_t i = 0; i < m_baInfo.size(); i++)
{
os << "{AID=" << GetAid11(i) << ", TID=" << GetTidInfo(i) << ", StartingSeq=0x"
<< std::hex << m_baInfo[i].m_startingSeq << std::dec << "}";
}
}
}
uint32_t
CtrlBAckResponseHeader::GetSerializedSize() const
{
// This method only makes use of the configured BA type, so that functions like
// GetBlockAckSize () can easily return the size of a Block Ack of a given type
uint32_t size = 0;
size += 2; // BA control
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
size += (2 + m_baType.m_bitmapLen[0]);
break;
case BlockAckType::MULTI_TID:
size += (2 + 2 + 8) * (m_tidInfo + 1); // Multi-TID block ack
break;
case BlockAckType::GCR:
size += (2 + 6 + m_baType.m_bitmapLen[0]);
break;
case BlockAckType::MULTI_STA:
for (auto& bitmapLen : m_baType.m_bitmapLen)
{
size += 2 /* AID TID Info */ + (bitmapLen > 0 ? 2 : 0) /* BA SSC */ + bitmapLen;
}
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
return size;
}
void
CtrlBAckResponseHeader::Serialize(Buffer::Iterator start) const
{
Buffer::Iterator i = start;
i.WriteHtolsbU16(GetBaControl());
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
i.WriteHtolsbU16(GetStartingSequenceControl());
i = SerializeBitmap(i);
break;
case BlockAckType::GCR:
i.WriteHtolsbU16(GetStartingSequenceControl());
WriteTo(i, m_baInfo[0].m_address);
i = SerializeBitmap(i);
break;
case BlockAckType::MULTI_STA:
for (std::size_t index = 0; index < m_baInfo.size(); index++)
{
i.WriteHtolsbU16(m_baInfo[index].m_aidTidInfo);
if (GetAid11(index) != 2045)
{
if (!m_baInfo[index].m_bitmap.empty())
{
i.WriteHtolsbU16(GetStartingSequenceControl(index));
i = SerializeBitmap(i, index);
}
}
else
{
uint32_t reserved = 0;
i.WriteHtolsbU32(reserved);
WriteTo(i, m_baInfo[index].m_address);
}
}
break;
case BlockAckType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
}
uint32_t
CtrlBAckResponseHeader::Deserialize(Buffer::Iterator start)
{
Buffer::Iterator i = start;
SetBaControl(i.ReadLsbtohU16());
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
SetStartingSequenceControl(i.ReadLsbtohU16());
i = DeserializeBitmap(i);
break;
case BlockAckType::GCR:
SetStartingSequenceControl(i.ReadLsbtohU16());
ReadFrom(i, m_baInfo[0].m_address);
i = DeserializeBitmap(i);
break;
case BlockAckType::MULTI_STA: {
std::size_t index = 0;
while (i.GetRemainingSize() > 0)
{
m_baInfo.emplace_back();
m_baType.m_bitmapLen.push_back(0); // updated by next call to SetStartingSequenceControl
m_baInfo.back().m_aidTidInfo = i.ReadLsbtohU16();
if (GetAid11(index) != 2045)
{
// the Block Ack Starting Sequence Control and Block Ack Bitmap subfields
// are only present in Block acknowledgement context, i.e., if the Ack Type
// subfield is set to 0 and the TID subfield is set to a value from 0 to 7.
if (!GetAckType(index) && GetTidInfo(index) < 8)
{
SetStartingSequenceControl(i.ReadLsbtohU16(), index);
i = DeserializeBitmap(i, index);
}
}
else
{
i.ReadLsbtohU32(); // next 4 bytes are reserved
ReadFrom(i, m_baInfo.back().m_address);
// the length of this Per AID TID Info subfield is 12, so set
// the bitmap length to 8 to simulate the correct size
m_baType.m_bitmapLen.back() = 8;
}
index++;
}
}
break;
case BlockAckType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
return i.GetDistanceFrom(start);
}
void
CtrlBAckResponseHeader::SetHtImmediateAck(bool immediateAck)
{
m_baAckPolicy = immediateAck;
}
void
CtrlBAckResponseHeader::SetType(BlockAckType type)
{
m_baType = type;
m_baInfo.clear();
for (auto& bitmapLen : m_baType.m_bitmapLen)
{
BaInfoInstance baInfoInstance{.m_aidTidInfo = 0,
.m_startingSeq = 0,
.m_bitmap = std::vector<uint8_t>(bitmapLen, 0),
.m_address = Mac48Address()};
m_baInfo.emplace_back(baInfoInstance);
}
}
BlockAckType
CtrlBAckResponseHeader::GetType() const
{
return m_baType;
}
void
CtrlBAckResponseHeader::SetTidInfo(uint8_t tid, std::size_t index)
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
if (m_baType.m_variant != BlockAckType::MULTI_STA)
{
m_tidInfo = static_cast<uint16_t>(tid);
}
else
{
m_baInfo[index].m_aidTidInfo |= ((static_cast<uint16_t>(tid) & 0x000f) << 12);
}
}
void
CtrlBAckResponseHeader::SetStartingSequence(uint16_t seq, std::size_t index)
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
m_baInfo[index].m_startingSeq = seq;
}
bool
CtrlBAckResponseHeader::MustSendHtImmediateAck() const
{
return m_baAckPolicy;
}
uint8_t
CtrlBAckResponseHeader::GetTidInfo(std::size_t index) const
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
uint8_t tid = 0;
if (m_baType.m_variant != BlockAckType::MULTI_STA)
{
tid = static_cast<uint8_t>(m_tidInfo);
}
else
{
tid = static_cast<uint8_t>((m_baInfo[index].m_aidTidInfo >> 12) & 0x000f);
}
return tid;
}
uint16_t
CtrlBAckResponseHeader::GetStartingSequence(std::size_t index) const
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
return m_baInfo[index].m_startingSeq;
}
bool
CtrlBAckResponseHeader::IsBasic() const
{
return m_baType.m_variant == BlockAckType::BASIC;
}
bool
CtrlBAckResponseHeader::IsCompressed() const
{
return m_baType.m_variant == BlockAckType::COMPRESSED;
}
bool
CtrlBAckResponseHeader::IsExtendedCompressed() const
{
return m_baType.m_variant == BlockAckType::EXTENDED_COMPRESSED;
}
bool
CtrlBAckResponseHeader::IsMultiTid() const
{
return m_baType.m_variant == BlockAckType::MULTI_TID;
}
bool
CtrlBAckResponseHeader::IsMultiSta() const
{
return m_baType.m_variant == BlockAckType::MULTI_STA;
}
bool
CtrlBAckResponseHeader::IsGcr() const
{
return m_baType.m_variant == BlockAckType::GCR;
}
void
CtrlBAckResponseHeader::SetAid11(uint16_t aid, std::size_t index)
{
NS_ASSERT(m_baType.m_variant == BlockAckType::MULTI_STA && index < m_baInfo.size());
m_baInfo[index].m_aidTidInfo |= (aid & 0x07ff);
}
uint16_t
CtrlBAckResponseHeader::GetAid11(std::size_t index) const
{
NS_ASSERT(m_baType.m_variant == BlockAckType::MULTI_STA && index < m_baInfo.size());
return m_baInfo[index].m_aidTidInfo & 0x07ff;
}
void
CtrlBAckResponseHeader::SetAckType(bool type, std::size_t index)
{
NS_ASSERT(m_baType.m_variant == BlockAckType::MULTI_STA && index < m_baInfo.size());
if (type)
{
m_baInfo[index].m_aidTidInfo |= (1 << 11);
}
}
bool
CtrlBAckResponseHeader::GetAckType(std::size_t index) const
{
NS_ASSERT(m_baType.m_variant == BlockAckType::MULTI_STA && index < m_baInfo.size());
return ((m_baInfo[index].m_aidTidInfo >> 11) & 0x0001) != 0;
}
void
CtrlBAckResponseHeader::SetUnassociatedStaAddress(const Mac48Address& ra, std::size_t index)
{
NS_ASSERT(GetAid11(index) == 2045);
m_baInfo[index].m_address = ra;
}
Mac48Address
CtrlBAckResponseHeader::GetUnassociatedStaAddress(std::size_t index) const
{
NS_ASSERT(GetAid11(index) == 2045);
return m_baInfo[index].m_address;
}
std::size_t
CtrlBAckResponseHeader::GetNPerAidTidInfoSubfields() const
{
NS_ASSERT(m_baType.m_variant == BlockAckType::MULTI_STA);
return m_baInfo.size();
}
std::vector<uint32_t>
CtrlBAckResponseHeader::FindPerAidTidInfoWithAid(uint16_t aid) const
{
NS_ASSERT(m_baType.m_variant == BlockAckType::MULTI_STA);
std::vector<uint32_t> ret;
ret.reserve(m_baInfo.size());
for (uint32_t i = 0; i < m_baInfo.size(); i++)
{
if (GetAid11(i) == aid)
{
ret.push_back(i);
}
}
return ret;
}
void
CtrlBAckResponseHeader::SetGcrGroupAddress(const Mac48Address& address)
{
NS_ASSERT(IsGcr());
m_baInfo[0].m_address = address;
}
Mac48Address
CtrlBAckResponseHeader::GetGcrGroupAddress() const
{
NS_ASSERT(IsGcr());
return m_baInfo[0].m_address;
}
uint16_t
CtrlBAckResponseHeader::GetBaControl() const
{
uint16_t res = 0;
if (m_baAckPolicy)
{
res |= 0x1;
}
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
break;
case BlockAckType::COMPRESSED:
res |= (0x02 << 1);
break;
case BlockAckType::EXTENDED_COMPRESSED:
res |= (0x01 << 1);
break;
case BlockAckType::MULTI_TID:
res |= (0x03 << 1);
break;
case BlockAckType::GCR:
res |= (0x06 << 1);
break;
case BlockAckType::MULTI_STA:
res |= (0x0b << 1);
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
if (m_baType.m_variant != BlockAckType::MULTI_STA)
{
res |= (m_tidInfo << 12) & (0xf << 12);
}
return res;
}
void
CtrlBAckResponseHeader::SetBaControl(uint16_t ba)
{
m_baAckPolicy = ((ba & 0x01) == 1);
if (((ba >> 1) & 0x0f) == 0x06)
{
SetType(BlockAckType::GCR);
}
else if (((ba >> 1) & 0x0f) == 0x03)
{
SetType(BlockAckType::MULTI_TID);
}
else if (((ba >> 1) & 0x0f) == 0x01)
{
SetType(BlockAckType::EXTENDED_COMPRESSED);
}
else if (((ba >> 1) & 0x0f) == 0x02)
{
SetType(BlockAckType::COMPRESSED);
}
else if (((ba >> 1) & 0x0f) == 0)
{
SetType(BlockAckType::BASIC);
}
else if (((ba >> 1) & 0x0f) == 0x0b)
{
SetType(BlockAckType::MULTI_STA);
}
else
{
NS_FATAL_ERROR("Invalid BA type");
}
if (m_baType.m_variant != BlockAckType::MULTI_STA)
{
m_tidInfo = (ba >> 12) & 0x0f;
}
}
uint16_t
CtrlBAckResponseHeader::GetStartingSequenceControl(std::size_t index) const
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
uint16_t ret = (m_baInfo[index].m_startingSeq << 4) & 0xfff0;
// The Fragment Number subfield encodes the length of the bitmap for Compressed and Multi-STA
// variants (see sections 9.3.1.8.2 and 9.3.1.8.7 of 802.11ax-2021 and 802.11be Draft 4.0).
// Note that Fragmentation Level 3 is not supported.
if (m_baType.m_variant == BlockAckType::COMPRESSED || m_baType.m_variant == BlockAckType::GCR)
{
switch (m_baType.m_bitmapLen[0])
{
case 8:
// do nothing
break;
case 32:
ret |= 0x0004;
break;
case 64:
ret |= 0x0008;
break;
case 128:
ret |= 0x000a;
break;
default:
NS_ABORT_MSG("Unsupported bitmap length: " << +m_baType.m_bitmapLen[0] << " bytes");
}
}
else if (m_baType.m_variant == BlockAckType::MULTI_STA)
{
NS_ASSERT(m_baInfo.size() == m_baType.m_bitmapLen.size());
NS_ASSERT_MSG(!m_baInfo[index].m_bitmap.empty(),
"This Per AID TID Info subfield has no Starting Sequence Control subfield");
switch (m_baType.m_bitmapLen[index])
{
case 8:
// do nothing
break;
case 16:
ret |= 0x0002;
break;
case 32:
ret |= 0x0004;
break;
case 4:
ret |= 0x0006;
break;
case 64:
ret |= 0x0008;
break;
case 128:
ret |= 0x000a;
break;
default:
NS_ABORT_MSG("Unsupported bitmap length: " << +m_baType.m_bitmapLen[index] << " bytes");
}
}
return ret;
}
void
CtrlBAckResponseHeader::SetStartingSequenceControl(uint16_t seqControl, std::size_t index)
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
// The Fragment Number subfield encodes the length of the bitmap for Compressed and Multi-STA
// variants (see sections 9.3.1.8.2 and 9.3.1.8.7 of 802.11ax-2021 and 802.11be Draft 4.0).
// Note that Fragmentation Level 3 is not supported.
if (m_baType.m_variant == BlockAckType::COMPRESSED || m_baType.m_variant == BlockAckType::GCR)
{
uint16_t fragNumber = seqControl & 0x000f;
if ((fragNumber & 0x0001) == 1)
{
NS_FATAL_ERROR("Fragmentation Level 3 unsupported");
}
switch (fragNumber)
{
case 0:
SetType({m_baType.m_variant, {8}});
break;
case 4:
SetType({m_baType.m_variant, {32}});
break;
case 8:
SetType({m_baType.m_variant, {64}});
break;
case 10:
SetType({m_baType.m_variant, {128}});
break;
default:
NS_ABORT_MSG("Unsupported fragment number: " << fragNumber);
}
}
else if (m_baType.m_variant == BlockAckType::MULTI_STA)
{
uint16_t fragNumber = seqControl & 0x000f;
if ((fragNumber & 0x0001) == 1)
{
NS_FATAL_ERROR("Fragmentation Level 3 unsupported");
}
uint8_t bitmapLen = 0;
switch (fragNumber)
{
case 0:
bitmapLen = 8;
break;
case 2:
bitmapLen = 16;
break;
case 4:
bitmapLen = 32;
break;
case 6:
bitmapLen = 4;
break;
case 8:
bitmapLen = 64;
break;
case 10:
bitmapLen = 128;
break;
default:
NS_ABORT_MSG("Unsupported fragment number: " << fragNumber);
}
m_baType.m_bitmapLen[index] = bitmapLen;
m_baInfo[index].m_bitmap.assign(bitmapLen, 0);
}
m_baInfo[index].m_startingSeq = (seqControl >> 4) & 0x0fff;
}
Buffer::Iterator
CtrlBAckResponseHeader::SerializeBitmap(Buffer::Iterator start, std::size_t index) const
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
Buffer::Iterator i = start;
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
case BlockAckType::GCR:
case BlockAckType::MULTI_STA:
for (const auto& byte : m_baInfo[index].m_bitmap)
{
i.WriteU8(byte);
}
break;
case BlockAckType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
return i;
}
Buffer::Iterator
CtrlBAckResponseHeader::DeserializeBitmap(Buffer::Iterator start, std::size_t index)
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
Buffer::Iterator i = start;
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
case BlockAckType::GCR:
case BlockAckType::MULTI_STA:
for (uint8_t j = 0; j < m_baType.m_bitmapLen[index]; j++)
{
m_baInfo[index].m_bitmap[j] = i.ReadU8();
}
break;
case BlockAckType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
return i;
}
void
CtrlBAckResponseHeader::SetReceivedPacket(uint16_t seq, std::size_t index)
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
if (!IsInBitmap(seq, index))
{
return;
}
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
/* To set correctly basic block ack bitmap we need fragment number too.
So if it's not specified, we consider packet not fragmented. */
m_baInfo[index].m_bitmap[IndexInBitmap(seq) * 2] |= 0x01;
break;
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
case BlockAckType::GCR:
case BlockAckType::MULTI_STA: {
uint16_t i = IndexInBitmap(seq, index);
m_baInfo[index].m_bitmap[i / 8] |= (uint8_t(0x01) << (i % 8));
break;
}
case BlockAckType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
}
void
CtrlBAckResponseHeader::SetReceivedFragment(uint16_t seq, uint8_t frag)
{
NS_ASSERT(frag < 16);
if (!IsInBitmap(seq))
{
return;
}
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
m_baInfo[0].m_bitmap[IndexInBitmap(seq) * 2 + frag / 8] |= (0x01 << (frag % 8));
break;
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
case BlockAckType::GCR:
case BlockAckType::MULTI_STA:
/* We can ignore this...compressed block ack doesn't support
acknowledgment of single fragments */
break;
case BlockAckType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
}
bool
CtrlBAckResponseHeader::IsPacketReceived(uint16_t seq, std::size_t index) const
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
if (m_baType.m_variant == BlockAckType::MULTI_STA && GetAckType(index) &&
GetTidInfo(index) == 14)
{
// All-ack context
return true;
}
if (!IsInBitmap(seq, index))
{
return false;
}
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
/*It's impossible to say if an entire packet was correctly received. */
return false;
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
case BlockAckType::GCR:
case BlockAckType::MULTI_STA: {
uint16_t i = IndexInBitmap(seq, index);
uint8_t mask = uint8_t(0x01) << (i % 8);
return (m_baInfo[index].m_bitmap[i / 8] & mask) != 0;
}
case BlockAckType::MULTI_TID:
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
default:
NS_FATAL_ERROR("Invalid BA type");
break;
}
return false;
}
bool
CtrlBAckResponseHeader::IsFragmentReceived(uint16_t seq, uint8_t frag) const
{
NS_ASSERT(frag < 16);
if (!IsInBitmap(seq))
{
return false;
}
switch (m_baType.m_variant)
{
case BlockAckType::BASIC:
return (m_baInfo[0].m_bitmap[IndexInBitmap(seq) * 2 + frag / 8] & (0x01 << (frag % 8))) !=
0;
case BlockAckType::COMPRESSED:
case BlockAckType::EXTENDED_COMPRESSED:
case BlockAckType::GCR:
case BlockAckType::MULTI_STA:
/* We can ignore this...compressed block ack doesn't support
acknowledgement of single fragments */
return false;
case BlockAckType::MULTI_TID: {
NS_FATAL_ERROR("Multi-tid block ack is not supported.");
break;
}
default: {
NS_FATAL_ERROR("Invalid BA type");
break;
}
}
return false;
}
uint16_t
CtrlBAckResponseHeader::IndexInBitmap(uint16_t seq, std::size_t index) const
{
uint16_t i;
if (seq >= m_baInfo[index].m_startingSeq)
{
i = seq - m_baInfo[index].m_startingSeq;
}
else
{
i = SEQNO_SPACE_SIZE - m_baInfo[index].m_startingSeq + seq;
}
uint16_t nAckedMpdus = m_baType.m_bitmapLen[index] * 8;
if (m_baType.m_variant == BlockAckType::BASIC)
{
nAckedMpdus = nAckedMpdus / 16;
}
NS_ASSERT(i < nAckedMpdus);
return i;
}
bool
CtrlBAckResponseHeader::IsInBitmap(uint16_t seq, std::size_t index) const
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baType.m_bitmapLen.size());
uint16_t nAckedMpdus = m_baType.m_bitmapLen[index] * 8;
if (m_baType.m_variant == BlockAckType::BASIC)
{
nAckedMpdus = nAckedMpdus / 16;
}
return (seq - m_baInfo[index].m_startingSeq + SEQNO_SPACE_SIZE) % SEQNO_SPACE_SIZE <
nAckedMpdus;
}
const std::vector<uint8_t>&
CtrlBAckResponseHeader::GetBitmap(std::size_t index) const
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
return m_baInfo[index].m_bitmap;
}
void
CtrlBAckResponseHeader::ResetBitmap(std::size_t index)
{
NS_ASSERT_MSG(m_baType.m_variant == BlockAckType::MULTI_STA || index == 0,
"index can only be non null for Multi-STA Block Ack");
NS_ASSERT(index < m_baInfo.size());
m_baInfo[index].m_bitmap.assign(m_baType.m_bitmapLen[index], 0);
}
/***********************************
* Trigger frame - User Info field
***********************************/
CtrlTriggerUserInfoField::CtrlTriggerUserInfoField(TriggerFrameType triggerType,
TriggerFrameVariant variant)
: m_variant(variant),
m_aid12(0),
m_ruAllocation(0),
m_ulFecCodingType(false),
m_ulMcs(0),
m_ulDcm(false),
m_ps160(false),
m_ulTargetRssi(0),
m_triggerType(triggerType),
m_basicTriggerDependentUserInfo(0)
{
memset(&m_bits26To31, 0, sizeof(m_bits26To31));
}
CtrlTriggerUserInfoField::~CtrlTriggerUserInfoField()
{
}
CtrlTriggerUserInfoField&
CtrlTriggerUserInfoField::operator=(const CtrlTriggerUserInfoField& userInfo)
{
NS_ABORT_MSG_IF(m_triggerType != userInfo.m_triggerType, "Trigger Frame type mismatch");
// check for self-assignment
if (&userInfo == this)
{
return *this;
}
m_variant = userInfo.m_variant;
m_aid12 = userInfo.m_aid12;
m_ruAllocation = userInfo.m_ruAllocation;
m_ulFecCodingType = userInfo.m_ulFecCodingType;
m_ulMcs = userInfo.m_ulMcs;
m_ulDcm = userInfo.m_ulDcm;
m_ps160 = userInfo.m_ps160;
m_bits26To31 = userInfo.m_bits26To31;
m_ulTargetRssi = userInfo.m_ulTargetRssi;
m_basicTriggerDependentUserInfo = userInfo.m_basicTriggerDependentUserInfo;
m_muBarTriggerDependentUserInfo = userInfo.m_muBarTriggerDependentUserInfo;
return *this;
}
void
CtrlTriggerUserInfoField::Print(std::ostream& os) const
{
os << ", USER_INFO " << (m_variant == TriggerFrameVariant::HE ? "HE" : "EHT")
<< " variant AID=" << m_aid12 << ", RU_Allocation=" << +m_ruAllocation
<< ", MCS=" << +m_ulMcs;
}
uint32_t
CtrlTriggerUserInfoField::GetSerializedSize() const
{
uint32_t size = 0;
size += 5; // User Info (excluding Trigger Dependent User Info)
switch (m_triggerType)
{
case TriggerFrameType::BASIC_TRIGGER:
case TriggerFrameType::BFRP_TRIGGER:
size += 1;
break;
case TriggerFrameType::MU_BAR_TRIGGER:
size +=
m_muBarTriggerDependentUserInfo.GetSerializedSize(); // BAR Control and BAR Information
break;
default:;
// The Trigger Dependent User Info subfield is not present in the other variants
}
return size;
}
Buffer::Iterator
CtrlTriggerUserInfoField::Serialize(Buffer::Iterator start) const
{
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
"BFRP Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
"GCR-MU-BAR Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
"NFRP Trigger frame is not supported");
Buffer::Iterator i = start;
uint32_t userInfo = 0; // User Info except the MSB
userInfo |= (m_aid12 & 0x0fff);
userInfo |= (m_ruAllocation << 12);
userInfo |= (m_ulFecCodingType ? 1 << 20 : 0);
userInfo |= (m_ulMcs & 0x0f) << 21;
if (m_variant == TriggerFrameVariant::HE)
{
userInfo |= (m_ulDcm ? 1 << 25 : 0);
}
if (m_aid12 != 0 && m_aid12 != 2045)
{
userInfo |= (m_bits26To31.ssAllocation.startingSs & 0x07) << 26;
userInfo |= (m_bits26To31.ssAllocation.nSs & 0x07) << 29;
}
else
{
userInfo |= (m_bits26To31.raRuInformation.nRaRu & 0x1f) << 26;
userInfo |= (m_bits26To31.raRuInformation.moreRaRu ? 1 << 31 : 0);
}
i.WriteHtolsbU32(userInfo);
// Here we need to write 8 bits covering the UL Target RSSI (7 bits) and B39, which is
// reserved in the HE variant and the PS160 subfield in the EHT variant.
uint8_t bit32To39 = m_ulTargetRssi;
if (m_variant == TriggerFrameVariant::EHT)
{
bit32To39 |= (m_ps160 ? 1 << 7 : 0);
}
i.WriteU8(bit32To39);
if (m_triggerType == TriggerFrameType::BASIC_TRIGGER)
{
i.WriteU8(m_basicTriggerDependentUserInfo);
}
else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER)
{
m_muBarTriggerDependentUserInfo.Serialize(i);
i.Next(m_muBarTriggerDependentUserInfo.GetSerializedSize());
}
return i;
}
Buffer::Iterator
CtrlTriggerUserInfoField::Deserialize(Buffer::Iterator start)
{
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
"BFRP Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
"GCR-MU-BAR Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
"NFRP Trigger frame is not supported");
Buffer::Iterator i = start;
uint32_t userInfo = i.ReadLsbtohU32();
m_aid12 = userInfo & 0x0fff;
NS_ABORT_MSG_IF(m_aid12 == 4095, "Cannot deserialize a Padding field");
m_ruAllocation = (userInfo >> 12) & 0xff;
m_ulFecCodingType = (userInfo >> 20) & 0x01;
m_ulMcs = (userInfo >> 21) & 0x0f;
if (m_variant == TriggerFrameVariant::HE)
{
m_ulDcm = (userInfo >> 25) & 0x01;
}
if (m_aid12 != 0 && m_aid12 != 2045)
{
m_bits26To31.ssAllocation.startingSs = (userInfo >> 26) & 0x07;
m_bits26To31.ssAllocation.nSs = (userInfo >> 29) & 0x07;
}
else
{
m_bits26To31.raRuInformation.nRaRu = (userInfo >> 26) & 0x1f;
m_bits26To31.raRuInformation.moreRaRu = (userInfo >> 31) & 0x01;
}
uint8_t bit32To39 = i.ReadU8();
m_ulTargetRssi = bit32To39 & 0x7f; // B39 is reserved in HE variant
if (m_variant == TriggerFrameVariant::EHT)
{
m_ps160 = (bit32To39 >> 7) == 1;
}
if (m_triggerType == TriggerFrameType::BASIC_TRIGGER)
{
m_basicTriggerDependentUserInfo = i.ReadU8();
}
else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER)
{
uint32_t len = m_muBarTriggerDependentUserInfo.Deserialize(i);
i.Next(len);
}
return i;
}
TriggerFrameType
CtrlTriggerUserInfoField::GetType() const
{
return m_triggerType;
}
WifiPreamble
CtrlTriggerUserInfoField::GetPreambleType() const
{
switch (m_variant)
{
case TriggerFrameVariant::HE:
return WIFI_PREAMBLE_HE_TB;
case TriggerFrameVariant::EHT:
return WIFI_PREAMBLE_EHT_TB;
default:
NS_ABORT_MSG("Unexpected variant: " << +static_cast<uint8_t>(m_variant));
}
return WIFI_PREAMBLE_LONG; // to silence warning
}
void
CtrlTriggerUserInfoField::SetAid12(uint16_t aid)
{
NS_ASSERT_MSG((m_variant == TriggerFrameVariant::HE) || (aid != AID_SPECIAL_USER),
std::to_string(AID_SPECIAL_USER)
<< " is reserved for Special User Info Field in EHT variant");
m_aid12 = aid & 0x0fff;
}
uint16_t
CtrlTriggerUserInfoField::GetAid12() const
{
return m_aid12;
}
bool
CtrlTriggerUserInfoField::HasRaRuForAssociatedSta() const
{
return (m_aid12 == 0);
}
bool
CtrlTriggerUserInfoField::HasRaRuForUnassociatedSta() const
{
return (m_aid12 == 2045);
}
void
CtrlTriggerUserInfoField::SetRuAllocation(WifiRu::RuSpec ru)
{
const auto ruIndex = WifiRu::GetIndex(ru);
const auto ruType = WifiRu::GetRuType(ru);
NS_ABORT_MSG_IF(ruIndex == 0, "Valid indices start at 1");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
"SetMuRtsRuAllocation() must be used for MU-RTS");
switch (ruType)
{
case RuType::RU_26_TONE:
m_ruAllocation = ruIndex - 1;
NS_ABORT_MSG_IF(!WifiRu::IsHe(ru) && (m_ruAllocation == 18), "Reserved value.");
NS_ASSERT(m_ruAllocation <= 36);
break;
case RuType::RU_52_TONE:
m_ruAllocation = ruIndex + 36;
NS_ASSERT(m_ruAllocation <= 52);
break;
case RuType::RU_106_TONE:
m_ruAllocation = ruIndex + 52;
NS_ASSERT(m_ruAllocation <= 60);
break;
case RuType::RU_242_TONE:
m_ruAllocation = ruIndex + 60;
NS_ASSERT(m_ruAllocation <= 64);
break;
case RuType::RU_484_TONE:
m_ruAllocation = ruIndex + 64;
NS_ASSERT(m_ruAllocation <= 67);
break;
case RuType::RU_996_TONE:
m_ruAllocation = 67;
break;
case RuType::RU_2x996_TONE:
m_ruAllocation = 68;
break;
case RuType::RU_4x996_TONE:
m_ruAllocation = 69;
break;
default:
NS_FATAL_ERROR("RU type unknown.");
break;
}
NS_ABORT_MSG_IF(m_ruAllocation > 69, "Reserved value.");
auto b0 = (WifiRu::IsHe(ru) && !std::get<HeRu::RuSpec>(ru).GetPrimary80MHz()) ||
(WifiRu::IsEht(ru) && !std::get<EhtRu::RuSpec>(ru).GetPrimary80MHzOrLower80MHz());
m_ps160 = (WifiRu::IsEht(ru) && !std::get<EhtRu::RuSpec>(ru).GetPrimary160MHz());
m_ruAllocation <<= 1;
if (b0)
{
m_ruAllocation++;
}
}
WifiRu::RuSpec
CtrlTriggerUserInfoField::GetRuAllocation() const
{
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
"GetMuRtsRuAllocation() must be used for MU-RTS");
RuType ruType;
std::size_t index;
const auto primaryOrLower80MHz = ((m_ruAllocation & 0x01) == 0);
uint8_t val = m_ruAllocation >> 1;
if (val < 37)
{
ruType = RuType::RU_26_TONE;
index = val + 1;
}
else if (val < 53)
{
ruType = RuType::RU_52_TONE;
index = val - 36;
}
else if (val < 61)
{
ruType = RuType::RU_106_TONE;
index = val - 52;
}
else if (val < 65)
{
ruType = RuType::RU_242_TONE;
index = val - 60;
}
else if (val < 67)
{
ruType = RuType::RU_484_TONE;
index = val - 64;
}
else if (val == 67)
{
ruType = RuType::RU_996_TONE;
index = 1;
}
else if (val == 68)
{
ruType = RuType::RU_2x996_TONE;
index = 1;
}
else if (val == 69)
{
NS_ASSERT(m_variant == TriggerFrameVariant::EHT);
ruType = RuType::RU_4x996_TONE;
index = 1;
}
else
{
NS_FATAL_ERROR("Reserved value.");
}
if (m_variant == TriggerFrameVariant::EHT)
{
return EhtRu::RuSpec{ruType, index, !m_ps160, primaryOrLower80MHz};
}
return HeRu::RuSpec{ruType, index, primaryOrLower80MHz};
}
void
CtrlTriggerUserInfoField::SetMuRtsRuAllocation(uint8_t value)
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_RTS_TRIGGER,
"SetMuRtsRuAllocation() can only be used for MU-RTS");
NS_ABORT_MSG_IF(
value < 61 || value > 69,
"Value "
<< +value
<< " is not admitted for B7-B1 of the RU Allocation subfield of MU-RTS Trigger Frames");
m_ruAllocation = (value << 1);
if (value >= 68)
{
// set B0 for 160 MHz, 80+80 MHz and 320 MHz indication
m_ruAllocation++;
}
if (value == 69)
{
// set 160 for 320 MHz indication
m_ps160 = true;
}
}
uint8_t
CtrlTriggerUserInfoField::GetMuRtsRuAllocation() const
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_RTS_TRIGGER,
"GetMuRtsRuAllocation() can only be used for MU-RTS");
uint8_t value = (m_ruAllocation >> 1);
NS_ABORT_MSG_IF(
value < 61 || value > 69,
"Value "
<< +value
<< " is not admitted for B7-B1 of the RU Allocation subfield of MU-RTS Trigger Frames");
return value;
}
void
CtrlTriggerUserInfoField::SetUlFecCodingType(bool ldpc)
{
m_ulFecCodingType = ldpc;
}
bool
CtrlTriggerUserInfoField::GetUlFecCodingType() const
{
return m_ulFecCodingType;
}
void
CtrlTriggerUserInfoField::SetUlMcs(uint8_t mcs)
{
uint8_t maxMcs = m_variant == TriggerFrameVariant::EHT ? 13 : 11;
NS_ABORT_MSG_IF(mcs > maxMcs, "Invalid MCS index");
m_ulMcs = mcs;
}
uint8_t
CtrlTriggerUserInfoField::GetUlMcs() const
{
return m_ulMcs;
}
void
CtrlTriggerUserInfoField::SetUlDcm(bool dcm)
{
NS_ASSERT_MSG(m_variant == TriggerFrameVariant::HE, "UL DCM flag only present in HE variant");
m_ulDcm = dcm;
}
bool
CtrlTriggerUserInfoField::GetUlDcm() const
{
NS_ASSERT_MSG(m_variant == TriggerFrameVariant::HE, "UL DCM flag only present in HE variant");
return m_ulDcm;
}
void
CtrlTriggerUserInfoField::SetSsAllocation(uint8_t startingSs, uint8_t nSs)
{
NS_ABORT_MSG_IF(m_aid12 == 0 || m_aid12 == 2045, "SS Allocation subfield not present");
NS_ABORT_MSG_IF(!startingSs || startingSs > 8, "Starting SS must be from 1 to 8");
NS_ABORT_MSG_IF(!nSs || nSs > 8, "Number of SS must be from 1 to 8");
m_bits26To31.ssAllocation.startingSs = startingSs - 1;
m_bits26To31.ssAllocation.nSs = nSs - 1;
}
uint8_t
CtrlTriggerUserInfoField::GetStartingSs() const
{
if (m_aid12 == 0 || m_aid12 == 2045)
{
return 1;
}
return m_bits26To31.ssAllocation.startingSs + 1;
}
uint8_t
CtrlTriggerUserInfoField::GetNss() const
{
if (m_aid12 == 0 || m_aid12 == 2045)
{
return 1;
}
return m_bits26To31.ssAllocation.nSs + 1;
}
void
CtrlTriggerUserInfoField::SetRaRuInformation(uint8_t nRaRu, bool moreRaRu)
{
NS_ABORT_MSG_IF(m_aid12 != 0 && m_aid12 != 2045, "RA-RU Information subfield not present");
NS_ABORT_MSG_IF(!nRaRu || nRaRu > 32, "Number of contiguous RA-RUs must be from 1 to 32");
m_bits26To31.raRuInformation.nRaRu = nRaRu - 1;
m_bits26To31.raRuInformation.moreRaRu = moreRaRu;
}
uint8_t
CtrlTriggerUserInfoField::GetNRaRus() const
{
NS_ABORT_MSG_IF(m_aid12 != 0 && m_aid12 != 2045, "RA-RU Information subfield not present");
return m_bits26To31.raRuInformation.nRaRu + 1;
}
bool
CtrlTriggerUserInfoField::GetMoreRaRu() const
{
NS_ABORT_MSG_IF(m_aid12 != 0 && m_aid12 != 2045, "RA-RU Information subfield not present");
return m_bits26To31.raRuInformation.moreRaRu;
}
void
CtrlTriggerUserInfoField::SetUlTargetRssiMaxTxPower()
{
m_ulTargetRssi = 127; // see Table 9-25i of 802.11ax amendment D3.0
}
void
CtrlTriggerUserInfoField::SetUlTargetRssi(int8_t dBm)
{
NS_ABORT_MSG_IF(dBm < -110 || dBm > -20, "Invalid values for signal power");
m_ulTargetRssi = static_cast<uint8_t>(110 + dBm);
}
bool
CtrlTriggerUserInfoField::IsUlTargetRssiMaxTxPower() const
{
return (m_ulTargetRssi == 127);
}
int8_t
CtrlTriggerUserInfoField::GetUlTargetRssi() const
{
NS_ABORT_MSG_IF(m_ulTargetRssi == 127, "STA must use its max TX power");
return static_cast<int8_t>(m_ulTargetRssi) - 110;
}
void
CtrlTriggerUserInfoField::SetBasicTriggerDepUserInfo(uint8_t spacingFactor,
uint8_t tidLimit,
AcIndex prefAc)
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
m_basicTriggerDependentUserInfo = (spacingFactor & 0x03) |
(tidLimit & 0x07) << 2
// B5 is reserved
| (prefAc & 0x03) << 6;
}
uint8_t
CtrlTriggerUserInfoField::GetMpduMuSpacingFactor() const
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
return m_basicTriggerDependentUserInfo & 0x03;
}
uint8_t
CtrlTriggerUserInfoField::GetTidAggregationLimit() const
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
return (m_basicTriggerDependentUserInfo & 0x1c) >> 2;
}
AcIndex
CtrlTriggerUserInfoField::GetPreferredAc() const
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
return AcIndex((m_basicTriggerDependentUserInfo & 0xc0) >> 6);
}
void
CtrlTriggerUserInfoField::SetMuBarTriggerDepUserInfo(const CtrlBAckRequestHeader& bar)
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER,
"Not a MU-BAR Trigger frame");
NS_ABORT_MSG_IF(bar.GetType().m_variant != BlockAckReqType::COMPRESSED &&
bar.GetType().m_variant != BlockAckReqType::MULTI_TID,
"BAR Control indicates it is neither the Compressed nor the Multi-TID variant");
m_muBarTriggerDependentUserInfo = bar;
}
const CtrlBAckRequestHeader&
CtrlTriggerUserInfoField::GetMuBarTriggerDepUserInfo() const
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER,
"Not a MU-BAR Trigger frame");
return m_muBarTriggerDependentUserInfo;
}
/*****************************************
* Trigger frame - Special User Info field
*****************************************/
CtrlTriggerSpecialUserInfoField::CtrlTriggerSpecialUserInfoField(TriggerFrameType triggerType)
: m_triggerType(triggerType)
{
}
CtrlTriggerSpecialUserInfoField&
CtrlTriggerSpecialUserInfoField::operator=(const CtrlTriggerSpecialUserInfoField& other)
{
// check for self-assignment
if (&other == this)
{
return *this;
}
m_triggerType = other.m_triggerType;
m_ulBwExt = other.m_ulBwExt;
m_muBarTriggerDependentUserInfo = other.m_muBarTriggerDependentUserInfo;
return *this;
}
uint32_t
CtrlTriggerSpecialUserInfoField::GetSerializedSize() const
{
uint32_t size = 0;
size += 5; // User Info (excluding Trigger Dependent User Info)
switch (m_triggerType)
{
case TriggerFrameType::BASIC_TRIGGER:
case TriggerFrameType::BFRP_TRIGGER:
size += 1;
break;
case TriggerFrameType::MU_BAR_TRIGGER:
size +=
m_muBarTriggerDependentUserInfo.GetSerializedSize(); // BAR Control and BAR Information
break;
default:;
// The Trigger Dependent User Info subfield is not present in the other variants
}
return size;
}
Buffer::Iterator
CtrlTriggerSpecialUserInfoField::Serialize(Buffer::Iterator start) const
{
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
"BFRP Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
"GCR-MU-BAR Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
"NFRP Trigger frame is not supported");
Buffer::Iterator i = start;
uint32_t userInfo = 0;
userInfo |= (AID_SPECIAL_USER & 0x0fff);
userInfo |= (static_cast<uint32_t>(m_ulBwExt) << 15);
i.WriteHtolsbU32(userInfo);
i.WriteU8(0);
// TODO: EHT Spatial Reuse and U-SIG Disregard And Validate
if (m_triggerType == TriggerFrameType::BASIC_TRIGGER)
{
// The length is one octet and all the subfields are reserved in a Basic Trigger frame and
// in a BFRP Trigger frame
i.WriteU8(0);
}
else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER)
{
m_muBarTriggerDependentUserInfo.Serialize(i);
i.Next(m_muBarTriggerDependentUserInfo.GetSerializedSize());
}
return i;
}
Buffer::Iterator
CtrlTriggerSpecialUserInfoField::Deserialize(Buffer::Iterator start)
{
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
"BFRP Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
"GCR-MU-BAR Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
"NFRP Trigger frame is not supported");
Buffer::Iterator i = start;
const auto userInfo = i.ReadLsbtohU32();
i.ReadU8();
// TODO: EHT Spatial Reuse and U-SIG Disregard And Validate
const uint16_t aid12 = userInfo & 0x0fff;
NS_ABORT_MSG_IF(aid12 != AID_SPECIAL_USER, "Failed to deserialize Special User Info field");
m_ulBwExt = (userInfo >> 15) & 0x03;
if (m_triggerType == TriggerFrameType::BASIC_TRIGGER)
{
i.ReadU8();
}
else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER)
{
const auto len = m_muBarTriggerDependentUserInfo.Deserialize(i);
i.Next(len);
}
return i;
}
TriggerFrameType
CtrlTriggerSpecialUserInfoField::GetType() const
{
return m_triggerType;
}
void
CtrlTriggerSpecialUserInfoField::SetUlBwExt(MHz_u bw)
{
switch (static_cast<uint16_t>(bw))
{
case 20:
case 40:
case 80:
m_ulBwExt = 0;
break;
case 160:
m_ulBwExt = 1;
break;
case 320:
m_ulBwExt = 2;
// TODO: differentiate channelization 1 from channelization 2
break;
default:
NS_FATAL_ERROR("Bandwidth value not allowed.");
break;
}
}
uint8_t
CtrlTriggerSpecialUserInfoField::GetUlBwExt() const
{
return m_ulBwExt;
}
void
CtrlTriggerSpecialUserInfoField::SetMuBarTriggerDepUserInfo(const CtrlBAckRequestHeader& bar)
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER,
"Not a MU-BAR Trigger frame");
NS_ABORT_MSG_IF(bar.GetType().m_variant != BlockAckReqType::COMPRESSED &&
bar.GetType().m_variant != BlockAckReqType::MULTI_TID,
"BAR Control indicates it is neither the Compressed nor the Multi-TID variant");
m_muBarTriggerDependentUserInfo = bar;
}
const CtrlBAckRequestHeader&
CtrlTriggerSpecialUserInfoField::GetMuBarTriggerDepUserInfo() const
{
NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER,
"Not a MU-BAR Trigger frame");
return m_muBarTriggerDependentUserInfo;
}
/***********************************
* Trigger frame
***********************************/
NS_OBJECT_ENSURE_REGISTERED(CtrlTriggerHeader);
CtrlTriggerHeader::CtrlTriggerHeader()
: m_variant(TriggerFrameVariant::HE),
m_triggerType(TriggerFrameType::BASIC_TRIGGER),
m_ulLength(0),
m_moreTF(false),
m_csRequired(false),
m_ulBandwidth(0),
m_giAndLtfType(0),
m_apTxPower(0),
m_ulSpatialReuse(0),
m_padding(0)
{
}
CtrlTriggerHeader::CtrlTriggerHeader(TriggerFrameType type, const WifiTxVector& txVector)
: CtrlTriggerHeader()
{
m_triggerType = type;
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
"This constructor cannot be used for MU-RTS");
switch (txVector.GetPreambleType())
{
case WIFI_PREAMBLE_HE_TB:
m_variant = TriggerFrameVariant::HE;
break;
case WIFI_PREAMBLE_EHT_TB:
m_variant = TriggerFrameVariant::EHT;
break;
default:
NS_ABORT_MSG("Cannot create a TF out of a TXVECTOR with preamble type: "
<< txVector.GetPreambleType());
}
// special user is always present if solicited TB PPDU format is EHT or later
if (txVector.GetModulationClass() >= WifiModulationClass::WIFI_MOD_CLASS_EHT)
{
NS_ASSERT(m_variant == TriggerFrameVariant::EHT);
m_specialUserInfoField.emplace(m_triggerType);
}
SetUlBandwidth(txVector.GetChannelWidth());
SetUlLength(txVector.GetLength());
const auto gi = txVector.GetGuardInterval().GetNanoSeconds();
if ((gi == 800) || (gi == 1600))
{
m_giAndLtfType = 1;
}
else
{
m_giAndLtfType = 2;
}
for (auto& userInfo : txVector.GetHeMuUserInfoMap())
{
CtrlTriggerUserInfoField& ui = AddUserInfoField();
ui.SetAid12(userInfo.first);
ui.SetRuAllocation(userInfo.second.ru);
ui.SetUlMcs(userInfo.second.mcs);
ui.SetSsAllocation(1, userInfo.second.nss); // MU-MIMO is not supported
}
}
CtrlTriggerHeader::~CtrlTriggerHeader()
{
}
CtrlTriggerHeader&
CtrlTriggerHeader::operator=(const CtrlTriggerHeader& trigger)
{
// check for self-assignment
if (&trigger == this)
{
return *this;
}
m_variant = trigger.m_variant;
m_triggerType = trigger.m_triggerType;
m_ulLength = trigger.m_ulLength;
m_moreTF = trigger.m_moreTF;
m_csRequired = trigger.m_csRequired;
m_ulBandwidth = trigger.m_ulBandwidth;
m_giAndLtfType = trigger.m_giAndLtfType;
m_apTxPower = trigger.m_apTxPower;
m_ulSpatialReuse = trigger.m_ulSpatialReuse;
m_padding = trigger.m_padding;
m_specialUserInfoField.reset();
m_specialUserInfoField = trigger.m_specialUserInfoField;
m_userInfoFields.clear();
m_userInfoFields = trigger.m_userInfoFields;
return *this;
}
TypeId
CtrlTriggerHeader::GetTypeId()
{
static TypeId tid = TypeId("ns3::CtrlTriggerHeader")
.SetParent<Header>()
.SetGroupName("Wifi")
.AddConstructor<CtrlTriggerHeader>();
return tid;
}
TypeId
CtrlTriggerHeader::GetInstanceTypeId() const
{
return GetTypeId();
}
void
CtrlTriggerHeader::Print(std::ostream& os) const
{
os << "TriggerType=" << GetTypeString() << ", Bandwidth=" << +GetUlBandwidth()
<< ", UL Length=" << m_ulLength;
for (auto& ui : m_userInfoFields)
{
ui.Print(os);
}
}
void
CtrlTriggerHeader::SetVariant(TriggerFrameVariant variant)
{
NS_ABORT_MSG_IF(!m_userInfoFields.empty(),
"Cannot change Common Info field variant if User Info fields are present");
m_variant = variant;
// special user is always present if User Info field variant is EHT or later
if (!m_specialUserInfoField && (m_variant >= TriggerFrameVariant::EHT))
{
m_specialUserInfoField.emplace(m_triggerType);
}
}
TriggerFrameVariant
CtrlTriggerHeader::GetVariant() const
{
return m_variant;
}
uint32_t
CtrlTriggerHeader::GetSerializedSize() const
{
uint32_t size = 0;
size += 8; // Common Info (excluding Trigger Dependent Common Info)
// Add the size of the Trigger Dependent Common Info subfield
if (m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER)
{
size += 4;
}
if (m_specialUserInfoField)
{
NS_ASSERT(m_variant == TriggerFrameVariant::EHT);
size += m_specialUserInfoField->GetSerializedSize();
}
for (auto& ui : m_userInfoFields)
{
size += ui.GetSerializedSize();
}
size += m_padding;
return size;
}
void
CtrlTriggerHeader::Serialize(Buffer::Iterator start) const
{
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
"BFRP Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
"GCR-MU-BAR Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
"NFRP Trigger frame is not supported");
Buffer::Iterator i = start;
uint64_t commonInfo = 0;
commonInfo |= (static_cast<uint8_t>(m_triggerType) & 0x0f);
commonInfo |= (m_ulLength & 0x0fff) << 4;
commonInfo |= (m_moreTF ? 1 << 16 : 0);
commonInfo |= (m_csRequired ? 1 << 17 : 0);
commonInfo |= (m_ulBandwidth & 0x03) << 18;
commonInfo |= (m_giAndLtfType & 0x03) << 20;
commonInfo |= static_cast<uint64_t>(m_apTxPower & 0x3f) << 28;
commonInfo |= static_cast<uint64_t>(m_ulSpatialReuse) << 37;
if (m_variant == TriggerFrameVariant::HE)
{
uint64_t ulHeSigA2 = 0x01ff; // nine bits equal to 1
commonInfo |= ulHeSigA2 << 54;
}
i.WriteHtolsbU64(commonInfo);
if (m_specialUserInfoField)
{
NS_ASSERT(m_variant == TriggerFrameVariant::EHT);
i = m_specialUserInfoField->Serialize(i);
}
for (auto& ui : m_userInfoFields)
{
i = ui.Serialize(i);
}
for (std::size_t count = 0; count < m_padding; count++)
{
i.WriteU8(0xff); // Padding field
}
}
uint32_t
CtrlTriggerHeader::Deserialize(Buffer::Iterator start)
{
Buffer::Iterator i = start;
uint64_t commonInfo = i.ReadLsbtohU64();
m_triggerType = static_cast<TriggerFrameType>(commonInfo & 0x0f);
m_ulLength = (commonInfo >> 4) & 0x0fff;
m_moreTF = (commonInfo >> 16) & 0x01;
m_csRequired = (commonInfo >> 17) & 0x01;
m_ulBandwidth = (commonInfo >> 18) & 0x03;
m_giAndLtfType = (commonInfo >> 20) & 0x03;
m_apTxPower = (commonInfo >> 28) & 0x3f;
m_ulSpatialReuse = (commonInfo >> 37) & 0xffff;
uint8_t bit54and55 = (commonInfo >> 54) & 0x03;
m_variant = bit54and55 == 3 ? TriggerFrameVariant::HE : TriggerFrameVariant::EHT;
m_userInfoFields.clear();
m_padding = 0;
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
"BFRP Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
"GCR-MU-BAR Trigger frame is not supported");
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
"NFRP Trigger frame is not supported");
if (m_variant == TriggerFrameVariant::EHT)
{
m_specialUserInfoField.reset();
const auto userInfo = i.ReadLsbtohU16();
i.Prev(2);
if (const auto aid12 = userInfo & 0x0fff; aid12 == AID_SPECIAL_USER)
{
m_specialUserInfoField.emplace(m_triggerType);
i = m_specialUserInfoField->Deserialize(i);
}
}
while (i.GetRemainingSize() >= 2)
{
// read the first 2 bytes to check if we encountered the Padding field
if (i.ReadU16() == 0xffff)
{
m_padding = i.GetRemainingSize() + 2;
}
else
{
// go back 2 bytes to deserialize the User Info field from the beginning
i.Prev(2);
CtrlTriggerUserInfoField& ui = AddUserInfoField();
i = ui.Deserialize(i);
}
}
return i.GetDistanceFrom(start);
}
void
CtrlTriggerHeader::SetType(TriggerFrameType type)
{
m_triggerType = type;
}
TriggerFrameType
CtrlTriggerHeader::GetType() const
{
return m_triggerType;
}
const char*
CtrlTriggerHeader::GetTypeString() const
{
return GetTypeString(GetType());
}
const char*
CtrlTriggerHeader::GetTypeString(TriggerFrameType type)
{
#define FOO(x) \
case TriggerFrameType::x: \
return #x;
switch (type)
{
FOO(BASIC_TRIGGER);
FOO(BFRP_TRIGGER);
FOO(MU_BAR_TRIGGER);
FOO(MU_RTS_TRIGGER);
FOO(BSRP_TRIGGER);
FOO(GCR_MU_BAR_TRIGGER);
FOO(BQRP_TRIGGER);
FOO(NFRP_TRIGGER);
default:
return "ERROR";
}
#undef FOO
}
bool
CtrlTriggerHeader::IsBasic() const
{
return (m_triggerType == TriggerFrameType::BASIC_TRIGGER);
}
bool
CtrlTriggerHeader::IsBfrp() const
{
return (m_triggerType == TriggerFrameType::BFRP_TRIGGER);
}
bool
CtrlTriggerHeader::IsMuBar() const
{
return (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER);
}
bool
CtrlTriggerHeader::IsMuRts() const
{
return (m_triggerType == TriggerFrameType::MU_RTS_TRIGGER);
}
bool
CtrlTriggerHeader::IsBsrp() const
{
return (m_triggerType == TriggerFrameType::BSRP_TRIGGER);
}
bool
CtrlTriggerHeader::IsGcrMuBar() const
{
return (m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER);
}
bool
CtrlTriggerHeader::IsBqrp() const
{
return (m_triggerType == TriggerFrameType::BQRP_TRIGGER);
}
bool
CtrlTriggerHeader::IsNfrp() const
{
return (m_triggerType == TriggerFrameType::NFRP_TRIGGER);
}
void
CtrlTriggerHeader::SetUlLength(uint16_t len)
{
m_ulLength = (len & 0x0fff);
}
uint16_t
CtrlTriggerHeader::GetUlLength() const
{
return m_ulLength;
}
WifiTxVector
CtrlTriggerHeader::GetHeTbTxVector(uint16_t staId) const
{
NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
"GetHeTbTxVector() cannot be used for MU-RTS");
auto userInfoIt = FindUserInfoWithAid(staId);
NS_ASSERT(userInfoIt != end());
WifiTxVector v;
v.SetPreambleType(userInfoIt->GetPreambleType());
v.SetChannelWidth(GetUlBandwidth());
v.SetGuardInterval(GetGuardInterval());
v.SetLength(GetUlLength());
v.SetHeMuUserInfo(
staId,
{userInfoIt->GetRuAllocation(), userInfoIt->GetUlMcs(), userInfoIt->GetNss()});
return v;
}
void
CtrlTriggerHeader::SetMoreTF(bool more)
{
m_moreTF = more;
}
bool
CtrlTriggerHeader::GetMoreTF() const
{
return m_moreTF;
}
void
CtrlTriggerHeader::SetCsRequired(bool cs)
{
m_csRequired = cs;
}
bool
CtrlTriggerHeader::GetCsRequired() const
{
return m_csRequired;
}
void
CtrlTriggerHeader::SetUlBandwidth(MHz_u bw)
{
switch (static_cast<uint16_t>(bw))
{
case 20:
m_ulBandwidth = 0;
break;
case 40:
m_ulBandwidth = 1;
break;
case 80:
m_ulBandwidth = 2;
break;
case 160:
case 320:
m_ulBandwidth = 3;
break;
default:
NS_FATAL_ERROR("Bandwidth value not allowed.");
break;
}
if (bw > MHz_u{160})
{
NS_ASSERT(m_specialUserInfoField);
}
if (m_specialUserInfoField)
{
NS_ASSERT(m_variant == TriggerFrameVariant::EHT);
m_specialUserInfoField->SetUlBwExt(bw);
}
}
MHz_u
CtrlTriggerHeader::GetUlBandwidth() const
{
if (m_specialUserInfoField)
{
NS_ASSERT(m_variant == TriggerFrameVariant::EHT);
if (m_specialUserInfoField->GetUlBwExt() > 1)
{
return MHz_u{320};
}
}
return (1 << m_ulBandwidth) * MHz_u{20};
}
void
CtrlTriggerHeader::SetGiAndLtfType(Time guardInterval, uint8_t ltfType)
{
const auto gi = guardInterval.GetNanoSeconds();
if ((ltfType == 1) && (gi == 1600))
{
m_giAndLtfType = 0;
}
else if ((ltfType == 2) && (gi == 1600))
{
m_giAndLtfType = 1;
}
else if ((ltfType == 4) && (gi == 3200))
{
m_giAndLtfType = 2;
}
else
{
NS_FATAL_ERROR("Invalid combination of GI and LTF type");
}
}
Time
CtrlTriggerHeader::GetGuardInterval() const
{
if (m_giAndLtfType == 0 || m_giAndLtfType == 1)
{
return NanoSeconds(1600);
}
else if (m_giAndLtfType == 2)
{
return NanoSeconds(3200);
}
else
{
NS_FATAL_ERROR("Invalid value for GI And LTF Type subfield");
}
}
uint8_t
CtrlTriggerHeader::GetLtfType() const
{
if (m_giAndLtfType == 0)
{
return 1;
}
else if (m_giAndLtfType == 1)
{
return 2;
}
else if (m_giAndLtfType == 2)
{
return 4;
}
else
{
NS_FATAL_ERROR("Invalid value for GI And LTF Type subfield");
}
}
void
CtrlTriggerHeader::SetApTxPower(int8_t power)
{
// see Table 9-25f "AP Tx Power subfield encoding" of 802.11ax amendment D3.0
NS_ABORT_MSG_IF(power < -20 || power > 40, "Out of range power values");
m_apTxPower = static_cast<uint8_t>(power + 20);
}
int8_t
CtrlTriggerHeader::GetApTxPower() const
{
// see Table 9-25f "AP Tx Power subfield encoding" of 802.11ax amendment D3.0
return static_cast<int8_t>(m_apTxPower) - 20;
}
void
CtrlTriggerHeader::SetUlSpatialReuse(uint16_t sr)
{
m_ulSpatialReuse = sr;
}
uint16_t
CtrlTriggerHeader::GetUlSpatialReuse() const
{
return m_ulSpatialReuse;
}
void
CtrlTriggerHeader::SetPaddingSize(std::size_t size)
{
NS_ABORT_MSG_IF(size == 1, "The Padding field, if present, shall be at least two octets");
m_padding = size;
}
std::size_t
CtrlTriggerHeader::GetPaddingSize() const
{
return m_padding;
}
CtrlTriggerHeader
CtrlTriggerHeader::GetCommonInfoField() const
{
// make a copy of this Trigger Frame and remove the User Info fields (including the Special User
// Info field) from the copy
CtrlTriggerHeader trigger(*this);
trigger.m_specialUserInfoField.reset();
trigger.m_userInfoFields.clear();
return trigger;
}
CtrlTriggerUserInfoField&
CtrlTriggerHeader::AddUserInfoField()
{
m_userInfoFields.emplace_back(m_triggerType, m_variant);
return m_userInfoFields.back();
}
CtrlTriggerUserInfoField&
CtrlTriggerHeader::AddUserInfoField(const CtrlTriggerUserInfoField& userInfo)
{
NS_ABORT_MSG_IF(
userInfo.GetType() != m_triggerType,
"Trying to add a User Info field of a type other than the type of the Trigger Frame");
m_userInfoFields.push_back(userInfo);
return m_userInfoFields.back();
}
CtrlTriggerHeader::Iterator
CtrlTriggerHeader::RemoveUserInfoField(ConstIterator userInfoIt)
{
return m_userInfoFields.erase(userInfoIt);
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::begin() const
{
return m_userInfoFields.begin();
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::end() const
{
return m_userInfoFields.end();
}
CtrlTriggerHeader::Iterator
CtrlTriggerHeader::begin()
{
return m_userInfoFields.begin();
}
CtrlTriggerHeader::Iterator
CtrlTriggerHeader::end()
{
return m_userInfoFields.end();
}
std::size_t
CtrlTriggerHeader::GetNUserInfoFields() const
{
return m_userInfoFields.size();
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::FindUserInfoWithAid(ConstIterator start, uint16_t aid12) const
{
// the lambda function returns true if a User Info field has the AID12 subfield
// equal to the given aid12 value
return std::find_if(start, end(), [aid12](const CtrlTriggerUserInfoField& ui) -> bool {
return (ui.GetAid12() == aid12);
});
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::FindUserInfoWithAid(uint16_t aid12) const
{
return FindUserInfoWithAid(m_userInfoFields.begin(), aid12);
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::FindUserInfoWithRaRuAssociated(ConstIterator start) const
{
return FindUserInfoWithAid(start, 0);
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::FindUserInfoWithRaRuAssociated() const
{
return FindUserInfoWithAid(0);
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::FindUserInfoWithRaRuUnassociated(ConstIterator start) const
{
return FindUserInfoWithAid(start, 2045);
}
CtrlTriggerHeader::ConstIterator
CtrlTriggerHeader::FindUserInfoWithRaRuUnassociated() const
{
return FindUserInfoWithAid(2045);
}
bool
CtrlTriggerHeader::IsValid() const
{
if (m_triggerType == TriggerFrameType::MU_RTS_TRIGGER)
{
return true;
}
// check that allocated RUs do not overlap
// TODO This is not a problem in case of UL MU-MIMO
std::vector<WifiRu::RuSpec> prevRus;
for (auto& ui : m_userInfoFields)
{
if (WifiRu::DoesOverlap(GetUlBandwidth(), ui.GetRuAllocation(), prevRus))
{
return false;
}
prevRus.push_back(ui.GetRuAllocation());
}
return true;
}
} // namespace ns3