Files
unison/src/wifi/model/table-based-error-rate-model.cc

256 lines
8.1 KiB
C++

/*
* Copyright (c) 2020 University of Washington
*
* 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
*
* Authors: Rohan Patidar <rpatidar@uw.edu>
* Sébastien Deronne <sebastien.deronne@gmail.com>
*/
#include "table-based-error-rate-model.h"
#include "wifi-tx-vector.h"
#include "wifi-utils.h"
#include "yans-error-rate-model.h"
#include "ns3/dsss-error-rate-model.h"
#include "ns3/log.h"
#include "ns3/pointer.h"
#include "ns3/string.h"
#include "ns3/uinteger.h"
#include <algorithm>
#include <cmath>
namespace ns3
{
static const double SNR_PRECISION = 2; //!< precision for SNR
static const double TABLED_BASED_ERROR_MODEL_PRECISION = 1e-5; //!< precision for PER
NS_OBJECT_ENSURE_REGISTERED(TableBasedErrorRateModel);
NS_LOG_COMPONENT_DEFINE("TableBasedErrorRateModel");
TypeId
TableBasedErrorRateModel::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TableBasedErrorRateModel")
.SetParent<ErrorRateModel>()
.SetGroupName("Wifi")
.AddConstructor<TableBasedErrorRateModel>()
.AddAttribute("FallbackErrorRateModel",
"Ptr to the fallback error rate model to be used when no matching value "
"is found in a table",
PointerValue(CreateObject<YansErrorRateModel>()),
MakePointerAccessor(&TableBasedErrorRateModel::m_fallbackErrorModel),
MakePointerChecker<ErrorRateModel>())
.AddAttribute("SizeThreshold",
"Threshold in bytes over which the table for large size frames is used",
UintegerValue(400),
MakeUintegerAccessor(&TableBasedErrorRateModel::m_threshold),
MakeUintegerChecker<uint64_t>());
return tid;
}
TableBasedErrorRateModel::TableBasedErrorRateModel()
{
NS_LOG_FUNCTION(this);
}
TableBasedErrorRateModel::~TableBasedErrorRateModel()
{
NS_LOG_FUNCTION(this);
m_fallbackErrorModel = nullptr;
}
double
TableBasedErrorRateModel::RoundSnr(double snr, double precision) const
{
NS_LOG_FUNCTION(this << snr);
double multiplier = std::round(std::pow(10.0, precision));
return std::floor(snr * multiplier + 0.5) / multiplier;
}
std::optional<uint8_t>
TableBasedErrorRateModel::GetMcsForMode(WifiMode mode)
{
std::optional<uint8_t> mcs;
WifiModulationClass modulationClass = mode.GetModulationClass();
WifiCodeRate codeRate = mode.GetCodeRate();
uint16_t constellationSize = mode.GetConstellationSize();
if (modulationClass == WIFI_MOD_CLASS_OFDM || modulationClass == WIFI_MOD_CLASS_ERP_OFDM)
{
if (constellationSize == 2) // BPSK
{
if (codeRate == WIFI_CODE_RATE_1_2)
{
mcs = 0;
}
if (codeRate == WIFI_CODE_RATE_3_4)
{
// No MCS uses BPSK and a Coding Rate of 3/4
}
}
else if (constellationSize == 4) // QPSK
{
if (codeRate == WIFI_CODE_RATE_1_2)
{
mcs = 1;
}
else if (codeRate == WIFI_CODE_RATE_3_4)
{
mcs = 2;
}
}
else if (constellationSize == 16) // 16-QAM
{
if (codeRate == WIFI_CODE_RATE_1_2)
{
mcs = 3;
}
else if (codeRate == WIFI_CODE_RATE_3_4)
{
mcs = 4;
}
}
else if (constellationSize == 64) // 64-QAM
{
if (codeRate == WIFI_CODE_RATE_2_3)
{
mcs = 5;
}
else if (codeRate == WIFI_CODE_RATE_3_4)
{
mcs = 6;
}
}
}
else if (modulationClass >= WIFI_MOD_CLASS_HT)
{
mcs = mode.GetMcsValue();
}
return mcs;
}
double
TableBasedErrorRateModel::DoGetChunkSuccessRate(WifiMode mode,
const WifiTxVector& txVector,
double snr,
uint64_t nbits,
uint8_t numRxAntennas,
WifiPpduField field,
uint16_t staId) const
{
NS_LOG_FUNCTION(this << mode << txVector << snr << nbits << +numRxAntennas << field << staId);
uint64_t size = std::max<uint64_t>(1, (nbits / 8));
double roundedSnr = RoundSnr(RatioToDb(snr), SNR_PRECISION);
uint8_t mcs;
if (auto ret = GetMcsForMode(mode); ret.has_value())
{
mcs = ret.value();
}
else
{
NS_LOG_DEBUG("No MCS found for mode " << mode << ": use fallback error rate model");
return m_fallbackErrorModel
->GetChunkSuccessRate(mode, txVector, snr, nbits, numRxAntennas, field, staId);
}
bool ldpc = txVector.IsLdpc();
NS_LOG_FUNCTION(this << +mcs << roundedSnr << size << ldpc);
// HT: for MCS greater than 7, use 0 - 7 curves for data rate
if (mode.GetModulationClass() == WIFI_MOD_CLASS_HT)
{
mcs = mcs % 8;
}
if (mcs >= (ldpc ? ERROR_TABLE_LDPC_MAX_NUM_MCS : ERROR_TABLE_BCC_MAX_NUM_MCS))
{
NS_LOG_WARN("Table missing for MCS: "
<< +mcs << " in TableBasedErrorRateModel: use fallback error rate model");
return m_fallbackErrorModel
->GetChunkSuccessRate(mode, txVector, snr, nbits, numRxAntennas, field, staId);
}
auto errorTable = (ldpc ? AwgnErrorTableLdpc1458
: (size < m_threshold ? AwgnErrorTableBcc32 : AwgnErrorTableBcc1458));
const auto& itVector = errorTable[mcs];
auto itTable = std::find_if(itVector.cbegin(),
itVector.cend(),
[&roundedSnr](const std::pair<double, double>& element) {
return element.first == roundedSnr;
});
double minSnr = itVector.cbegin()->first;
double maxSnr = (--itVector.cend())->first;
double per;
if (itTable == itVector.cend())
{
if (roundedSnr < minSnr)
{
per = 1.0;
}
else if (roundedSnr > maxSnr)
{
per = 0.0;
}
else
{
double a = 0.0;
double b = 0.0;
double previousSnr = 0.0;
double nextSnr = 0.0;
for (auto i = itVector.cbegin(); i != itVector.cend(); ++i)
{
if (i->first < roundedSnr)
{
previousSnr = i->first;
a = i->second;
}
else
{
nextSnr = i->first;
b = i->second;
break;
}
}
per = a + (roundedSnr - previousSnr) * (b - a) / (nextSnr - previousSnr);
}
}
else
{
per = itTable->second;
}
uint16_t tableSize = (ldpc ? ERROR_TABLE_LDPC_FRAME_SIZE
: (size < m_threshold ? ERROR_TABLE_BCC_SMALL_FRAME_SIZE
: ERROR_TABLE_BCC_LARGE_FRAME_SIZE));
if (size != tableSize)
{
// From IEEE document 11-14/0803r1 (Packet Length for Box 0 Calibration)
per = (1.0 - std::pow((1 - per), (static_cast<double>(size) / tableSize)));
}
if (per < TABLED_BASED_ERROR_MODEL_PRECISION)
{
per = 0.0;
}
return 1.0 - per;
}
} // namespace ns3