Files
unison/src/wifi/model/interference-helper.cc
2023-02-02 17:41:31 +00:00

751 lines
25 KiB
C++

/*
* Copyright (c) 2005,2006 INRIA
*
* 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: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
* Sébastien Deronne <sebastien.deronne@gmail.com>
*/
#include "interference-helper.h"
#include "error-rate-model.h"
#include "wifi-phy.h"
#include "wifi-psdu.h"
#include "wifi-utils.h"
#include "ns3/log.h"
#include "ns3/packet.h"
#include "ns3/simulator.h"
#include <algorithm>
#include <numeric>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("InterferenceHelper");
NS_OBJECT_ENSURE_REGISTERED(InterferenceHelper);
/****************************************************************
* PHY event class
****************************************************************/
Event::Event(Ptr<const WifiPpdu> ppdu,
const WifiTxVector& txVector,
Time duration,
RxPowerWattPerChannelBand&& rxPower)
: m_ppdu(ppdu),
m_txVector(txVector),
m_startTime(Simulator::Now()),
m_endTime(m_startTime + duration),
m_rxPowerW(std::move(rxPower))
{
}
Event::~Event()
{
m_ppdu = nullptr;
m_rxPowerW.clear();
}
Ptr<const WifiPpdu>
Event::GetPpdu() const
{
return m_ppdu;
}
Time
Event::GetStartTime() const
{
return m_startTime;
}
Time
Event::GetEndTime() const
{
return m_endTime;
}
Time
Event::GetDuration() const
{
return m_endTime - m_startTime;
}
double
Event::GetRxPowerW() const
{
NS_ASSERT(!m_rxPowerW.empty());
// The total RX power corresponds to the maximum over all the bands
auto it = std::max_element(
m_rxPowerW.begin(),
m_rxPowerW.end(),
[](const std::pair<WifiSpectrumBand, double>& p1,
const std::pair<WifiSpectrumBand, double>& p2) { return p1.second < p2.second; });
return it->second;
}
double
Event::GetRxPowerW(WifiSpectrumBand band) const
{
auto it = m_rxPowerW.find(band);
NS_ASSERT(it != m_rxPowerW.end());
return it->second;
}
const RxPowerWattPerChannelBand&
Event::GetRxPowerWPerBand() const
{
return m_rxPowerW;
}
const WifiTxVector&
Event::GetTxVector() const
{
return m_txVector;
}
void
Event::UpdateRxPowerW(const RxPowerWattPerChannelBand& rxPower)
{
NS_ASSERT(rxPower.size() == m_rxPowerW.size());
// Update power band per band
for (auto& currentRxPowerW : m_rxPowerW)
{
auto band = currentRxPowerW.first;
auto it = rxPower.find(band);
if (it != rxPower.end())
{
currentRxPowerW.second += it->second;
}
}
}
std::ostream&
operator<<(std::ostream& os, const Event& event)
{
os << "start=" << event.GetStartTime() << ", end=" << event.GetEndTime()
<< ", TXVECTOR=" << event.GetTxVector() << ", power=" << event.GetRxPowerW() << "W"
<< ", PPDU=" << event.GetPpdu();
return os;
}
/****************************************************************
* Class which records SNIR change events for a
* short period of time.
****************************************************************/
InterferenceHelper::NiChange::NiChange(double power, Ptr<Event> event)
: m_power(power),
m_event(event)
{
}
InterferenceHelper::NiChange::~NiChange()
{
m_event = nullptr;
}
double
InterferenceHelper::NiChange::GetPower() const
{
return m_power;
}
void
InterferenceHelper::NiChange::AddPower(double power)
{
m_power += power;
}
Ptr<Event>
InterferenceHelper::NiChange::GetEvent() const
{
return m_event;
}
/****************************************************************
* The actual InterferenceHelper
****************************************************************/
InterferenceHelper::InterferenceHelper()
: m_errorRateModel(nullptr),
m_numRxAntennas(1),
m_rxing(false)
{
NS_LOG_FUNCTION(this);
}
InterferenceHelper::~InterferenceHelper()
{
NS_LOG_FUNCTION(this);
}
TypeId
InterferenceHelper::GetTypeId()
{
static TypeId tid = TypeId("ns3::InterferenceHelper")
.SetParent<ns3::Object>()
.SetGroupName("Wifi")
.AddConstructor<InterferenceHelper>();
return tid;
}
void
InterferenceHelper::DoDispose()
{
NS_LOG_FUNCTION(this);
RemoveBands();
m_errorRateModel = nullptr;
}
Ptr<Event>
InterferenceHelper::Add(Ptr<const WifiPpdu> ppdu,
const WifiTxVector& txVector,
Time duration,
RxPowerWattPerChannelBand& rxPowerW,
bool isStartOfdmaRxing)
{
Ptr<Event> event = Create<Event>(ppdu, txVector, duration, std::move(rxPowerW));
AppendEvent(event, isStartOfdmaRxing);
return event;
}
void
InterferenceHelper::AddForeignSignal(Time duration, RxPowerWattPerChannelBand& rxPowerW)
{
// Parameters other than duration and rxPowerW are unused for this type
// of signal, so we provide dummy versions
WifiMacHeader hdr;
hdr.SetType(WIFI_MAC_QOSDATA);
hdr.SetQosTid(0);
Ptr<WifiPpdu> fakePpdu =
Create<WifiPpdu>(Create<WifiPsdu>(Create<Packet>(0), hdr), WifiTxVector(), 0);
Add(fakePpdu, WifiTxVector(), duration, rxPowerW);
}
void
InterferenceHelper::RemoveBands()
{
NS_LOG_FUNCTION(this);
for (auto it : m_niChangesPerBand)
{
it.second.clear();
}
m_niChangesPerBand.clear();
m_firstPowerPerBand.clear();
}
void
InterferenceHelper::AddBand(WifiSpectrumBand band)
{
NS_LOG_FUNCTION(this << band.first << band.second);
NS_ASSERT(m_niChangesPerBand.find(band) == m_niChangesPerBand.end());
NiChanges niChanges;
auto result = m_niChangesPerBand.insert({band, niChanges});
NS_ASSERT(result.second);
// Always have a zero power noise event in the list
AddNiChangeEvent(Time(0), NiChange(0.0, nullptr), result.first);
m_firstPowerPerBand.insert({band, 0.0});
}
void
InterferenceHelper::SetNoiseFigure(double value)
{
m_noiseFigure = value;
}
void
InterferenceHelper::SetErrorRateModel(const Ptr<ErrorRateModel> rate)
{
m_errorRateModel = rate;
}
Ptr<ErrorRateModel>
InterferenceHelper::GetErrorRateModel() const
{
return m_errorRateModel;
}
void
InterferenceHelper::SetNumberOfReceiveAntennas(uint8_t rx)
{
m_numRxAntennas = rx;
}
Time
InterferenceHelper::GetEnergyDuration(double energyW, WifiSpectrumBand band)
{
Time now = Simulator::Now();
auto niIt = m_niChangesPerBand.find(band);
NS_ASSERT(niIt != m_niChangesPerBand.end());
auto i = GetPreviousPosition(now, niIt);
Time end = i->first;
for (; i != niIt->second.end(); ++i)
{
double noiseInterferenceW = i->second.GetPower();
end = i->first;
if (noiseInterferenceW < energyW)
{
break;
}
}
return end > now ? end - now : MicroSeconds(0);
}
void
InterferenceHelper::AppendEvent(Ptr<Event> event, bool isStartOfdmaRxing)
{
NS_LOG_FUNCTION(this << event << isStartOfdmaRxing);
for (const auto& it : event->GetRxPowerWPerBand())
{
WifiSpectrumBand band = it.first;
auto niIt = m_niChangesPerBand.find(band);
NS_ASSERT(niIt != m_niChangesPerBand.end());
double previousPowerStart = 0;
double previousPowerEnd = 0;
auto previousPowerPosition = GetPreviousPosition(event->GetStartTime(), niIt);
previousPowerStart = previousPowerPosition->second.GetPower();
previousPowerEnd = GetPreviousPosition(event->GetEndTime(), niIt)->second.GetPower();
if (!m_rxing)
{
m_firstPowerPerBand.find(band)->second = previousPowerStart;
// Always leave the first zero power noise event in the list
niIt->second.erase(++(niIt->second.begin()), ++previousPowerPosition);
}
else if (isStartOfdmaRxing)
{
// When the first UL-OFDMA payload is received, we need to set m_firstPowerPerBand
// so that it takes into account interferences that arrived between the start of the
// UL MU transmission and the start of UL-OFDMA payload.
m_firstPowerPerBand.find(band)->second = previousPowerStart;
}
auto first =
AddNiChangeEvent(event->GetStartTime(), NiChange(previousPowerStart, event), niIt);
auto last = AddNiChangeEvent(event->GetEndTime(), NiChange(previousPowerEnd, event), niIt);
for (auto i = first; i != last; ++i)
{
i->second.AddPower(it.second);
}
}
}
void
InterferenceHelper::UpdateEvent(Ptr<Event> event, const RxPowerWattPerChannelBand& rxPower)
{
NS_LOG_FUNCTION(this << event);
// This is called for UL MU events, in order to scale power as long as UL MU PPDUs arrive
for (const auto& it : rxPower)
{
WifiSpectrumBand band = it.first;
auto niIt = m_niChangesPerBand.find(band);
NS_ASSERT(niIt != m_niChangesPerBand.end());
auto first = GetPreviousPosition(event->GetStartTime(), niIt);
auto last = GetPreviousPosition(event->GetEndTime(), niIt);
for (auto i = first; i != last; ++i)
{
i->second.AddPower(it.second);
}
}
event->UpdateRxPowerW(rxPower);
}
double
InterferenceHelper::CalculateSnr(double signal,
double noiseInterference,
uint16_t channelWidth,
uint8_t nss) const
{
NS_LOG_FUNCTION(this << signal << noiseInterference << channelWidth << +nss);
// thermal noise at 290K in J/s = W
static const double BOLTZMANN = 1.3803e-23;
// Nt is the power of thermal noise in W
double Nt = BOLTZMANN * 290 * channelWidth * 1e6;
// receiver noise Floor (W) which accounts for thermal noise and non-idealities of the receiver
double noiseFloor = m_noiseFigure * Nt;
double noise = noiseFloor + noiseInterference;
double snr = signal / noise; // linear scale
NS_LOG_DEBUG("bandwidth(MHz)=" << channelWidth << ", signal(W)= " << signal << ", noise(W)="
<< noiseFloor << ", interference(W)=" << noiseInterference
<< ", snr=" << RatioToDb(snr) << "dB");
if (m_errorRateModel->IsAwgn())
{
double gain = 1;
if (m_numRxAntennas > nss)
{
gain = static_cast<double>(m_numRxAntennas) /
nss; // compute gain offered by diversity for AWGN
}
NS_LOG_DEBUG("SNR improvement thanks to diversity: " << 10 * std::log10(gain) << "dB");
snr *= gain;
}
return snr;
}
double
InterferenceHelper::CalculateNoiseInterferenceW(Ptr<Event> event,
NiChangesPerBand* nis,
WifiSpectrumBand band) const
{
NS_LOG_FUNCTION(this << band.first << band.second);
auto firstPower_it = m_firstPowerPerBand.find(band);
NS_ASSERT(firstPower_it != m_firstPowerPerBand.end());
double noiseInterferenceW = firstPower_it->second;
auto niIt = m_niChangesPerBand.find(band);
NS_ASSERT(niIt != m_niChangesPerBand.end());
auto it = niIt->second.find(event->GetStartTime());
for (; it != niIt->second.end() && it->first < Simulator::Now(); ++it)
{
noiseInterferenceW = it->second.GetPower() - event->GetRxPowerW(band);
}
it = niIt->second.find(event->GetStartTime());
NS_ASSERT(it != niIt->second.end());
for (; it != niIt->second.end() && it->second.GetEvent() != event; ++it)
{
;
}
NiChanges ni;
ni.emplace(event->GetStartTime(), NiChange(0, event));
while (++it != niIt->second.end() && it->second.GetEvent() != event)
{
ni.insert(*it);
}
ni.emplace(event->GetEndTime(), NiChange(0, event));
nis->insert({band, ni});
NS_ASSERT_MSG(noiseInterferenceW >= 0,
"CalculateNoiseInterferenceW returns negative value " << noiseInterferenceW);
return noiseInterferenceW;
}
double
InterferenceHelper::CalculateChunkSuccessRate(double snir,
Time duration,
WifiMode mode,
const WifiTxVector& txVector,
WifiPpduField field) const
{
if (duration.IsZero())
{
return 1.0;
}
uint64_t rate = mode.GetDataRate(txVector.GetChannelWidth());
uint64_t nbits = static_cast<uint64_t>(rate * duration.GetSeconds());
double csr =
m_errorRateModel->GetChunkSuccessRate(mode, txVector, snir, nbits, m_numRxAntennas, field);
return csr;
}
double
InterferenceHelper::CalculatePayloadChunkSuccessRate(double snir,
Time duration,
const WifiTxVector& txVector,
uint16_t staId) const
{
if (duration.IsZero())
{
return 1.0;
}
WifiMode mode = txVector.GetMode(staId);
uint64_t rate = mode.GetDataRate(txVector, staId);
uint64_t nbits = static_cast<uint64_t>(rate * duration.GetSeconds());
nbits /= txVector.GetNss(staId); // divide effective number of bits by NSS to achieve same chunk
// error rate as SISO for AWGN
double csr = m_errorRateModel->GetChunkSuccessRate(mode,
txVector,
snir,
nbits,
m_numRxAntennas,
WIFI_PPDU_FIELD_DATA,
staId);
return csr;
}
double
InterferenceHelper::CalculatePayloadPer(Ptr<const Event> event,
uint16_t channelWidth,
NiChangesPerBand* nis,
WifiSpectrumBand band,
uint16_t staId,
std::pair<Time, Time> window) const
{
NS_LOG_FUNCTION(this << channelWidth << band.first << band.second << staId << window.first
<< window.second);
double psr = 1.0; /* Packet Success Rate */
const auto& niIt = nis->find(band)->second;
auto j = niIt.cbegin();
Time previous = j->first;
WifiMode payloadMode = event->GetTxVector().GetMode(staId);
Time phyPayloadStart = j->first;
if (event->GetPpdu()->GetType() != WIFI_PPDU_TYPE_UL_MU &&
event->GetPpdu()->GetType() !=
WIFI_PPDU_TYPE_DL_MU) // j->first corresponds to the start of the OFDMA payload
{
phyPayloadStart =
j->first + WifiPhy::CalculatePhyPreambleAndHeaderDuration(event->GetTxVector());
}
Time windowStart = phyPayloadStart + window.first;
Time windowEnd = phyPayloadStart + window.second;
double noiseInterferenceW = m_firstPowerPerBand.find(band)->second;
double powerW = event->GetRxPowerW(band);
while (++j != niIt.cend())
{
Time current = j->first;
NS_LOG_DEBUG("previous= " << previous << ", current=" << current);
NS_ASSERT(current >= previous);
double snr = CalculateSnr(powerW,
noiseInterferenceW,
channelWidth,
event->GetTxVector().GetNss(staId));
// Case 1: Both previous and current point to the windowed payload
if (previous >= windowStart)
{
psr *= CalculatePayloadChunkSuccessRate(snr,
Min(windowEnd, current) - previous,
event->GetTxVector(),
staId);
NS_LOG_DEBUG("Both previous and current point to the windowed payload: mode="
<< payloadMode << ", psr=" << psr);
}
// Case 2: previous is before windowed payload and current is in the windowed payload
else if (current >= windowStart)
{
psr *= CalculatePayloadChunkSuccessRate(snr,
Min(windowEnd, current) - windowStart,
event->GetTxVector(),
staId);
NS_LOG_DEBUG(
"previous is before windowed payload and current is in the windowed payload: mode="
<< payloadMode << ", psr=" << psr);
}
noiseInterferenceW = j->second.GetPower() - powerW;
previous = j->first;
if (previous > windowEnd)
{
NS_LOG_DEBUG("Stop: new previous=" << previous
<< " after time window end=" << windowEnd);
break;
}
}
double per = 1 - psr;
return per;
}
double
InterferenceHelper::CalculatePhyHeaderSectionPsr(
Ptr<const Event> event,
NiChangesPerBand* nis,
uint16_t channelWidth,
WifiSpectrumBand band,
PhyEntity::PhyHeaderSections phyHeaderSections) const
{
NS_LOG_FUNCTION(this << band.first << band.second);
double psr = 1.0; /* Packet Success Rate */
auto niIt = nis->find(band)->second;
auto j = niIt.begin();
NS_ASSERT(!phyHeaderSections.empty());
Time stopLastSection = Seconds(0);
for (const auto& section : phyHeaderSections)
{
stopLastSection = Max(stopLastSection, section.second.first.second);
}
Time previous = j->first;
double noiseInterferenceW = m_firstPowerPerBand.find(band)->second;
double powerW = event->GetRxPowerW(band);
while (++j != niIt.end())
{
Time current = j->first;
NS_LOG_DEBUG("previous= " << previous << ", current=" << current);
NS_ASSERT(current >= previous);
double snr = CalculateSnr(powerW, noiseInterferenceW, channelWidth, 1);
for (const auto& section : phyHeaderSections)
{
Time start = section.second.first.first;
Time stop = section.second.first.second;
if (previous <= stop || current >= start)
{
Time duration = Min(stop, current) - Max(start, previous);
if (duration.IsStrictlyPositive())
{
psr *= CalculateChunkSuccessRate(snr,
duration,
section.second.second,
event->GetTxVector(),
section.first);
NS_LOG_DEBUG("Current NI change in "
<< section.first << " [" << start << ", " << stop << "] for "
<< duration.As(Time::NS) << ": mode=" << section.second.second
<< ", psr=" << psr);
}
}
}
noiseInterferenceW = j->second.GetPower() - powerW;
previous = j->first;
if (previous > stopLastSection)
{
NS_LOG_DEBUG("Stop: new previous=" << previous << " after stop of last section="
<< stopLastSection);
break;
}
}
return psr;
}
double
InterferenceHelper::CalculatePhyHeaderPer(Ptr<const Event> event,
NiChangesPerBand* nis,
uint16_t channelWidth,
WifiSpectrumBand band,
WifiPpduField header) const
{
NS_LOG_FUNCTION(this << band.first << band.second << header);
auto niIt = nis->find(band)->second;
auto phyEntity = WifiPhy::GetStaticPhyEntity(event->GetTxVector().GetModulationClass());
PhyEntity::PhyHeaderSections sections;
for (const auto& section :
phyEntity->GetPhyHeaderSections(event->GetTxVector(), niIt.begin()->first))
{
if (section.first == header)
{
sections[header] = section.second;
}
}
double psr = 1.0;
if (!sections.empty())
{
psr = CalculatePhyHeaderSectionPsr(event, nis, channelWidth, band, sections);
}
return 1 - psr;
}
struct PhyEntity::SnrPer
InterferenceHelper::CalculatePayloadSnrPer(Ptr<Event> event,
uint16_t channelWidth,
WifiSpectrumBand band,
uint16_t staId,
std::pair<Time, Time> relativeMpduStartStop) const
{
NS_LOG_FUNCTION(this << channelWidth << band.first << band.second << staId
<< relativeMpduStartStop.first << relativeMpduStartStop.second);
NiChangesPerBand ni;
double noiseInterferenceW = CalculateNoiseInterferenceW(event, &ni, band);
double snr = CalculateSnr(event->GetRxPowerW(band),
noiseInterferenceW,
channelWidth,
event->GetTxVector().GetNss(staId));
/* calculate the SNIR at the start of the MPDU (located through windowing) and accumulate
* all SNIR changes in the SNIR vector.
*/
double per = CalculatePayloadPer(event, channelWidth, &ni, band, staId, relativeMpduStartStop);
return PhyEntity::SnrPer(snr, per);
}
double
InterferenceHelper::CalculateSnr(Ptr<Event> event,
uint16_t channelWidth,
uint8_t nss,
WifiSpectrumBand band) const
{
NiChangesPerBand ni;
double noiseInterferenceW = CalculateNoiseInterferenceW(event, &ni, band);
double snr = CalculateSnr(event->GetRxPowerW(band), noiseInterferenceW, channelWidth, nss);
return snr;
}
struct PhyEntity::SnrPer
InterferenceHelper::CalculatePhyHeaderSnrPer(Ptr<Event> event,
uint16_t channelWidth,
WifiSpectrumBand band,
WifiPpduField header) const
{
NS_LOG_FUNCTION(this << band.first << band.second << header);
NiChangesPerBand ni;
double noiseInterferenceW = CalculateNoiseInterferenceW(event, &ni, band);
double snr = CalculateSnr(event->GetRxPowerW(band), noiseInterferenceW, channelWidth, 1);
/* calculate the SNIR at the start of the PHY header and accumulate
* all SNIR changes in the SNIR vector.
*/
double per = CalculatePhyHeaderPer(event, &ni, channelWidth, band, header);
return PhyEntity::SnrPer(snr, per);
}
void
InterferenceHelper::EraseEvents()
{
for (auto niIt = m_niChangesPerBand.begin(); niIt != m_niChangesPerBand.end(); ++niIt)
{
niIt->second.clear();
// Always have a zero power noise event in the list
AddNiChangeEvent(Time(0), NiChange(0.0, nullptr), niIt);
m_firstPowerPerBand.at(niIt->first) = 0.0;
}
m_rxing = false;
}
InterferenceHelper::NiChanges::iterator
InterferenceHelper::GetNextPosition(Time moment, NiChangesPerBand::iterator niIt)
{
return niIt->second.upper_bound(moment);
}
InterferenceHelper::NiChanges::iterator
InterferenceHelper::GetPreviousPosition(Time moment, NiChangesPerBand::iterator niIt)
{
auto it = GetNextPosition(moment, niIt);
// This is safe since there is always an NiChange at time 0,
// before moment.
--it;
return it;
}
InterferenceHelper::NiChanges::iterator
InterferenceHelper::AddNiChangeEvent(Time moment, NiChange change, NiChangesPerBand::iterator niIt)
{
return niIt->second.insert(GetNextPosition(moment, niIt), std::make_pair(moment, change));
}
void
InterferenceHelper::NotifyRxStart()
{
NS_LOG_FUNCTION(this);
m_rxing = true;
}
void
InterferenceHelper::NotifyRxEnd(Time endTime)
{
NS_LOG_FUNCTION(this << endTime);
m_rxing = false;
// Update m_firstPowerPerBand for frame capture
for (auto niIt = m_niChangesPerBand.begin(); niIt != m_niChangesPerBand.end(); ++niIt)
{
NS_ASSERT(niIt->second.size() > 1);
auto it = GetPreviousPosition(endTime, niIt);
it--;
m_firstPowerPerBand.find(niIt->first)->second = it->second.GetPower();
}
}
} // namespace ns3