256 lines
8.1 KiB
C++
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
|