Files
unison/src/wifi/model/phy-entity.cc
2022-09-25 14:17:15 +00:00

1181 lines
44 KiB
C++

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 Orange Labs
*
* 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: Rediet <getachew.redieteab@orange.com>
* Sébastien Deronne <sebastien.deronne@gmail.com> (for logic ported from wifi-phy and spectrum-wifi-phy)
* Mathieu Lacage <mathieu.lacage@sophia.inria.fr> (for logic ported from wifi-phy)
*/
#include "phy-entity.h"
#include "spectrum-wifi-phy.h"
#include "wifi-psdu.h"
#include "preamble-detection-model.h"
#include "frame-capture-model.h"
#include "wifi-utils.h"
#include "wifi-spectrum-signal-parameters.h"
#include "interference-helper.h"
#include "ns3/packet.h"
#include "ns3/simulator.h"
#include "ns3/log.h"
#include "ns3/assert.h"
#include <algorithm>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("PhyEntity");
std::ostream & operator << (std::ostream &os, const PhyEntity::PhyRxFailureAction &action)
{
switch (action)
{
case PhyEntity::DROP:
return (os << "DROP");
case PhyEntity::ABORT:
return (os << "ABORT");
case PhyEntity::IGNORE:
return (os << "IGNORE");
default:
NS_FATAL_ERROR ("Unknown action");
return (os << "unknown");
}
}
std::ostream & operator << (std::ostream &os, const PhyEntity::PhyFieldRxStatus &status)
{
if (status.isSuccess)
{
return os << "success";
}
else
{
return os << "failure (" << status.reason << "/" << status.actionIfFailure << ")";
}
}
/*******************************************************
* Abstract base class for PHY entities
*******************************************************/
uint64_t PhyEntity::m_globalPpduUid = 0;
PhyEntity::~PhyEntity ()
{
NS_LOG_FUNCTION (this);
m_modeList.clear ();
CancelAllEvents ();
}
void
PhyEntity::SetOwner (Ptr<WifiPhy> wifiPhy)
{
NS_LOG_FUNCTION (this << wifiPhy);
m_wifiPhy = wifiPhy;
m_state = m_wifiPhy->m_state;
}
bool
PhyEntity::IsModeSupported (WifiMode mode) const
{
for (const auto & m : m_modeList)
{
if (m == mode)
{
return true;
}
}
return false;
}
uint8_t
PhyEntity::GetNumModes (void) const
{
return m_modeList.size ();
}
WifiMode
PhyEntity::GetMcs (uint8_t /* index */) const
{
NS_ABORT_MSG ("This method should be used only for HtPhy and child classes. Use GetMode instead.");
return WifiMode ();
}
bool
PhyEntity::IsMcsSupported (uint8_t /* index */) const
{
NS_ABORT_MSG ("This method should be used only for HtPhy and child classes. Use IsModeSupported instead.");
return false;
}
bool
PhyEntity::HandlesMcsModes (void) const
{
return false;
}
std::list<WifiMode>::const_iterator
PhyEntity::begin (void) const
{
return m_modeList.begin ();
}
std::list<WifiMode>::const_iterator
PhyEntity::end (void) const
{
return m_modeList.end ();
}
WifiMode
PhyEntity::GetSigMode (WifiPpduField field, const WifiTxVector& txVector) const
{
NS_FATAL_ERROR ("PPDU field is not a SIG field (no sense in retrieving the signaled mode) or is unsupported: " << field);
return WifiMode (); //should be overloaded
}
WifiPpduField
PhyEntity::GetNextField (WifiPpduField currentField, WifiPreamble preamble) const
{
auto ppduFormats = GetPpduFormats ();
const auto itPpdu = ppduFormats.find (preamble);
if (itPpdu != ppduFormats.end ())
{
const auto itField = std::find (itPpdu->second.begin (), itPpdu->second.end (), currentField);
if (itField != itPpdu->second.end ())
{
const auto itNextField = std::next (itField, 1);
if (itNextField != itPpdu->second.end ())
{
return *(itNextField);
}
NS_FATAL_ERROR ("No field after " << currentField << " for " << preamble << " for the provided PPDU formats");
}
else
{
NS_FATAL_ERROR ("Unsupported PPDU field " << currentField << " for " << preamble << " for the provided PPDU formats");
}
}
else
{
NS_FATAL_ERROR ("Unsupported preamble " << preamble << " for the provided PPDU formats");
}
return WifiPpduField::WIFI_PPDU_FIELD_PREAMBLE; // Silence compiler warning
}
Time
PhyEntity::GetDuration (WifiPpduField field, const WifiTxVector& txVector) const
{
if (field > WIFI_PPDU_FIELD_EHT_SIG)
{
NS_FATAL_ERROR ("Unsupported PPDU field");
}
return MicroSeconds (0); //should be overloaded
}
Time
PhyEntity::CalculatePhyPreambleAndHeaderDuration (const WifiTxVector& txVector) const
{
Time duration = MicroSeconds (0);
for (uint8_t field = WIFI_PPDU_FIELD_PREAMBLE; field < WIFI_PPDU_FIELD_DATA; ++field)
{
duration += GetDuration (static_cast<WifiPpduField> (field), txVector);
}
return duration;
}
WifiConstPsduMap
PhyEntity::GetWifiConstPsduMap (Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) const
{
return WifiConstPsduMap ({std::make_pair (SU_STA_ID, psdu)});
}
Ptr<const WifiPsdu>
PhyEntity::GetAddressedPsduInPpdu (Ptr<const WifiPpdu> ppdu) const
{
return ppdu->GetPsdu ();
}
PhyEntity::PhyHeaderSections
PhyEntity::GetPhyHeaderSections (const WifiTxVector& txVector, Time ppduStart) const
{
PhyHeaderSections map;
WifiPpduField field = WIFI_PPDU_FIELD_PREAMBLE; //preamble always present
Time start = ppduStart;
while (field != WIFI_PPDU_FIELD_DATA)
{
Time duration = GetDuration (field, txVector);
map[field] = std::make_pair (std::make_pair (start, start + duration),
GetSigMode (field, txVector));
//Move to next field
start += duration;
field = GetNextField (field, txVector.GetPreambleType ());
}
return map;
}
Ptr<WifiPpdu>
PhyEntity::BuildPpdu (const WifiConstPsduMap & psdus, const WifiTxVector& txVector, Time /* ppduDuration */)
{
NS_LOG_FUNCTION (this << psdus << txVector);
NS_FATAL_ERROR ("This method is unsupported for the base PhyEntity class. Use the overloaded version in the amendment-specific subclasses instead!");
return Create<WifiPpdu> (psdus.begin ()->second, txVector,
m_wifiPhy->GetOperatingChannel ().GetPrimaryChannelCenterFrequency (txVector.GetChannelWidth ())); //should be overloaded
}
Time
PhyEntity::GetDurationUpToField (WifiPpduField field, const WifiTxVector& txVector) const
{
if (field == WIFI_PPDU_FIELD_DATA) //this field is not in the map returned by GetPhyHeaderSections
{
return CalculatePhyPreambleAndHeaderDuration (txVector);
}
const auto & sections = GetPhyHeaderSections (txVector, NanoSeconds (0));
auto it = sections.find (field);
NS_ASSERT (it != sections.end ());
const auto & startStopTimes = it->second.first;
return startStopTimes.first; //return the start time of field relatively to the beginning of the PPDU
}
PhyEntity::SnrPer
PhyEntity::GetPhyHeaderSnrPer (WifiPpduField field, Ptr<Event> event) const
{
uint16_t measurementChannelWidth = GetMeasurementChannelWidth (event->GetPpdu ());
return m_wifiPhy->m_interference->CalculatePhyHeaderSnrPer (event, measurementChannelWidth, GetPrimaryBand (measurementChannelWidth),
field);
}
void
PhyEntity::StartReceiveField (WifiPpduField field, Ptr<Event> event)
{
NS_LOG_FUNCTION (this << field << *event);
NS_ASSERT (m_wifiPhy); //no sense if no owner WifiPhy instance
NS_ASSERT (m_wifiPhy->m_endPhyRxEvent.IsExpired ());
NS_ABORT_MSG_IF (field == WIFI_PPDU_FIELD_PREAMBLE, "Use the StartReceivePreamble method for preamble reception");
//Handle special cases of data reception
if (field == WIFI_PPDU_FIELD_DATA)
{
StartReceivePayload (event);
return;
}
bool supported = DoStartReceiveField (field, event);
NS_ABORT_MSG_IF (!supported, "Unknown field " << field << " for this PHY entity"); //TODO see what to do if not supported
Time duration = GetDuration (field, event->GetTxVector ());
m_wifiPhy->m_endPhyRxEvent = Simulator::Schedule (duration, &PhyEntity::EndReceiveField, this, field, event);
m_wifiPhy->NotifyCcaBusy (event->GetPpdu (), duration); //keep in CCA busy state up to reception of Data (will then switch to RX)
}
void
PhyEntity::EndReceiveField (WifiPpduField field, Ptr<Event> event)
{
NS_LOG_FUNCTION (this << field << *event);
NS_ASSERT (m_wifiPhy); //no sense if no owner WifiPhy instance
NS_ASSERT (m_wifiPhy->m_endPhyRxEvent.IsExpired ());
PhyFieldRxStatus status = DoEndReceiveField (field, event);
WifiTxVector txVector = event->GetTxVector ();
if (status.isSuccess) //move to next field if reception succeeded
{
StartReceiveField (GetNextField (field, txVector.GetPreambleType ()), event);
}
else
{
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
switch (status.actionIfFailure)
{
case ABORT:
//Abort reception, but consider medium as busy
AbortCurrentReception (status.reason);
if (event->GetEndTime () > (Simulator::Now () + m_state->GetDelayUntilIdle ()))
{
m_wifiPhy->SwitchMaybeToCcaBusy (ppdu);
}
break;
case DROP:
//Notify drop, keep in CCA busy, and perform same processing as IGNORE case
if (status.reason == FILTERED)
{
//PHY-RXSTART is immediately followed by PHY-RXEND (Filtered)
m_wifiPhy->m_phyRxPayloadBeginTrace (txVector, NanoSeconds (0)); //this callback (equivalent to PHY-RXSTART primitive) is also triggered for filtered PPDUs
}
m_wifiPhy->NotifyRxDrop (GetAddressedPsduInPpdu (ppdu), status.reason);
m_wifiPhy->NotifyCcaBusy (ppdu, GetRemainingDurationAfterField (ppdu, field));
//no break
case IGNORE:
//Keep in Rx state and reset at end
m_endRxPayloadEvents.push_back (Simulator::Schedule (GetRemainingDurationAfterField (ppdu, field),
&PhyEntity::ResetReceive, this, event));
break;
default:
NS_FATAL_ERROR ("Unknown action in case of failure");
}
}
}
Time
PhyEntity::GetRemainingDurationAfterField (Ptr<const WifiPpdu> ppdu, WifiPpduField field) const
{
const WifiTxVector& txVector = ppdu->GetTxVector ();
return ppdu->GetTxDuration () - (GetDurationUpToField (field, txVector) + GetDuration (field, txVector));
}
bool
PhyEntity::DoStartReceiveField (WifiPpduField field, Ptr<Event> event)
{
NS_LOG_FUNCTION (this << field << *event);
NS_ASSERT (field != WIFI_PPDU_FIELD_PREAMBLE && field != WIFI_PPDU_FIELD_DATA); //handled apart for the time being
auto ppduFormats = GetPpduFormats ();
auto itFormat = ppduFormats.find (event->GetPpdu ()->GetPreamble ());
if (itFormat != ppduFormats.end ())
{
auto itField = std::find (itFormat->second.begin (), itFormat->second.end (), field);
if (itField != itFormat->second.end ())
{
return true; //supported field so we can start receiving
}
}
return false; //unsupported otherwise
}
PhyEntity::PhyFieldRxStatus
PhyEntity::DoEndReceiveField (WifiPpduField field, Ptr<Event> event)
{
NS_LOG_FUNCTION (this << field << *event);
NS_ASSERT (field != WIFI_PPDU_FIELD_DATA); //handled apart for the time being
if (field == WIFI_PPDU_FIELD_PREAMBLE)
{
return DoEndReceivePreamble (event);
}
return PhyFieldRxStatus (false); //failed reception by default
}
void
PhyEntity::StartReceivePreamble (Ptr<const WifiPpdu> ppdu, RxPowerWattPerChannelBand& rxPowersW,
Time rxDuration)
{
//The total RX power corresponds to the maximum over all the bands
auto it = std::max_element (rxPowersW.begin (), rxPowersW.end (),
[] (const std::pair<WifiSpectrumBand, double> &p1, const std::pair<WifiSpectrumBand, double> &p2) {
return p1.second < p2.second;
});
NS_LOG_FUNCTION (this << ppdu << it->second);
Ptr<Event> event = DoGetEvent (ppdu, rxPowersW);
if (!event)
{
//PPDU should be simply considered as interference (once it has been accounted for in InterferenceHelper)
return;
}
Time endRx = Simulator::Now () + rxDuration;
if (m_state->GetState () == WifiPhyState::OFF)
{
NS_LOG_DEBUG ("Cannot start RX because device is OFF");
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ()))
{
m_wifiPhy->SwitchMaybeToCcaBusy (nullptr);
}
DropPreambleEvent (ppdu, WifiPhyRxfailureReason::POWERED_OFF, endRx);
return;
}
if (ppdu->IsTruncatedTx ())
{
NS_LOG_DEBUG ("Packet reception stopped because transmitter has been switched off");
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ()))
{
m_wifiPhy->SwitchMaybeToCcaBusy (ppdu);
}
DropPreambleEvent (ppdu, WifiPhyRxfailureReason::TRUNCATED_TX, endRx);
return;
}
switch (m_state->GetState ())
{
case WifiPhyState::SWITCHING:
NS_LOG_DEBUG ("Drop packet because of channel switching");
/*
* Packets received on the upcoming channel are added to the event list
* during the switching state. This way the medium can be correctly sensed
* when the device listens to the channel for the first time after the
* switching e.g. after channel switching, the channel may be sensed as
* busy due to other devices' transmissions started before the end of
* the switching.
*/
DropPreambleEvent (ppdu, CHANNEL_SWITCHING, endRx);
break;
case WifiPhyState::RX:
if (m_wifiPhy->m_frameCaptureModel
&& m_wifiPhy->m_frameCaptureModel->IsInCaptureWindow (m_wifiPhy->m_timeLastPreambleDetected)
&& m_wifiPhy->m_frameCaptureModel->CaptureNewFrame (m_wifiPhy->m_currentEvent, event))
{
AbortCurrentReception (FRAME_CAPTURE_PACKET_SWITCH);
NS_LOG_DEBUG ("Switch to new packet");
StartPreambleDetectionPeriod (event);
}
else
{
NS_LOG_DEBUG ("Drop packet because already in Rx");
DropPreambleEvent (ppdu, RXING, endRx);
if (!m_wifiPhy->m_currentEvent)
{
/*
* We are here because the non-legacy PHY header has not been successfully received.
* The PHY is kept in RX state for the duration of the PPDU, but EndReceive function is
* not called when the reception of the PPDU is finished, which is responsible to clear
* m_currentPreambleEvents. As a result, m_currentPreambleEvents should be cleared here.
*/
m_wifiPhy->m_currentPreambleEvents.clear ();
}
}
break;
case WifiPhyState::TX:
NS_LOG_DEBUG ("Drop packet because already in Tx");
DropPreambleEvent (ppdu, TXING, endRx);
break;
case WifiPhyState::CCA_BUSY:
if (m_wifiPhy->m_currentEvent)
{
if (m_wifiPhy->m_frameCaptureModel
&& m_wifiPhy->m_frameCaptureModel->IsInCaptureWindow (m_wifiPhy->m_timeLastPreambleDetected)
&& m_wifiPhy->m_frameCaptureModel->CaptureNewFrame (m_wifiPhy->m_currentEvent, event))
{
AbortCurrentReception (FRAME_CAPTURE_PACKET_SWITCH);
NS_LOG_DEBUG ("Switch to new packet");
StartPreambleDetectionPeriod (event);
}
else
{
NS_LOG_DEBUG ("Drop packet because already decoding preamble");
DropPreambleEvent (ppdu, BUSY_DECODING_PREAMBLE, endRx);
}
}
else
{
StartPreambleDetectionPeriod (event);
}
break;
case WifiPhyState::IDLE:
NS_ASSERT (!m_wifiPhy->m_currentEvent);
StartPreambleDetectionPeriod (event);
break;
case WifiPhyState::SLEEP:
NS_LOG_DEBUG ("Drop packet because in sleep mode");
DropPreambleEvent (ppdu, SLEEPING, endRx);
break;
default:
NS_FATAL_ERROR ("Invalid WifiPhy state.");
break;
}
}
void
PhyEntity::DropPreambleEvent (Ptr<const WifiPpdu> ppdu, WifiPhyRxfailureReason reason, Time endRx)
{
NS_LOG_FUNCTION (this << ppdu << reason << endRx);
m_wifiPhy->NotifyRxDrop (GetAddressedPsduInPpdu (ppdu), reason);
auto it = m_wifiPhy->m_currentPreambleEvents.find (std::make_pair (ppdu->GetUid (), ppdu->GetPreamble ()));
if (it != m_wifiPhy->m_currentPreambleEvents.end ())
{
m_wifiPhy->m_currentPreambleEvents.erase (it);
}
if (!m_wifiPhy->IsStateSleep () && !m_wifiPhy->IsStateOff () && (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())))
{
//that PPDU will be noise _after_ the end of the current event.
m_wifiPhy->SwitchMaybeToCcaBusy (ppdu);
}
}
void
PhyEntity::ErasePreambleEvent (Ptr<const WifiPpdu> ppdu, Time rxDuration)
{
NS_LOG_FUNCTION (this << ppdu << rxDuration);
auto it = m_wifiPhy->m_currentPreambleEvents.find (std::make_pair (ppdu->GetUid (), ppdu->GetPreamble ()));
if (it != m_wifiPhy->m_currentPreambleEvents.end ())
{
m_wifiPhy->m_currentPreambleEvents.erase (it);
}
if (m_wifiPhy->m_currentPreambleEvents.empty ())
{
m_wifiPhy->Reset ();
}
if (rxDuration > m_state->GetDelayUntilIdle ())
{
//this PPDU will be noise _after_ the completion of the current event
m_wifiPhy->SwitchMaybeToCcaBusy (ppdu);
}
}
uint16_t
PhyEntity::GetStaId (const Ptr<const WifiPpdu> /* ppdu */) const
{
return SU_STA_ID;
}
void
PhyEntity::StartReceivePayload (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
NS_ASSERT (m_wifiPhy->m_endPhyRxEvent.IsExpired ());
Time payloadDuration = DoStartReceivePayload (event);
m_state->SwitchToRx (payloadDuration);
}
Time
PhyEntity::DoStartReceivePayload (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
NS_LOG_DEBUG ("Receiving PSDU");
uint16_t staId = GetStaId (ppdu);
m_signalNoiseMap.insert ({std::make_pair (ppdu->GetUid (), staId), SignalNoiseDbm ()});
m_statusPerMpduMap.insert ({std::make_pair (ppdu->GetUid (), staId), std::vector<bool> ()});
ScheduleEndOfMpdus (event);
const WifiTxVector& txVector = event->GetTxVector ();
Time payloadDuration = ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (txVector);
m_wifiPhy->m_phyRxPayloadBeginTrace (txVector, payloadDuration); //this callback (equivalent to PHY-RXSTART primitive) is triggered only if headers have been correctly decoded and that the mode within is supported
m_endRxPayloadEvents.push_back (Simulator::Schedule (payloadDuration,
&PhyEntity::EndReceivePayload, this, event));
return payloadDuration;
}
void
PhyEntity::ScheduleEndOfMpdus (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
Ptr<const WifiPsdu> psdu = GetAddressedPsduInPpdu (ppdu);
const WifiTxVector& txVector = event->GetTxVector ();
uint16_t staId = GetStaId (ppdu);
Time endOfMpduDuration = NanoSeconds (0);
Time relativeStart = NanoSeconds (0);
Time psduDuration = ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (txVector);
Time remainingAmpduDuration = psduDuration;
size_t nMpdus = psdu->GetNMpdus ();
MpduType mpduType = (nMpdus > 1) ? FIRST_MPDU_IN_AGGREGATE : (psdu->IsSingle () ? SINGLE_MPDU : NORMAL_MPDU);
uint32_t totalAmpduSize = 0;
double totalAmpduNumSymbols = 0.0;
auto mpdu = psdu->begin ();
for (size_t i = 0; i < nMpdus && mpdu != psdu->end (); ++mpdu)
{
uint32_t size = (mpduType == NORMAL_MPDU) ? psdu->GetSize () : psdu->GetAmpduSubframeSize (i);
Time mpduDuration = m_wifiPhy->GetPayloadDuration (size, txVector,
m_wifiPhy->GetPhyBand (), mpduType, true, totalAmpduSize,
totalAmpduNumSymbols, staId);
remainingAmpduDuration -= mpduDuration;
if (i == (nMpdus - 1) && !remainingAmpduDuration.IsZero ()) //no more MPDUs coming
{
if (remainingAmpduDuration < NanoSeconds (txVector.GetGuardInterval ())) //enables to ignore padding
{
mpduDuration += remainingAmpduDuration; //apply a correction just in case rounding had induced slight shift
}
}
endOfMpduDuration += mpduDuration;
NS_LOG_INFO ("Schedule end of MPDU #" << i << " in " << endOfMpduDuration.As (Time::NS) <<
" (relativeStart=" << relativeStart.As (Time::NS) << ", mpduDuration=" << mpduDuration.As (Time::NS) <<
", remainingAmdpuDuration=" << remainingAmpduDuration.As (Time::NS) << ")");
m_endOfMpduEvents.push_back (Simulator::Schedule (endOfMpduDuration, &PhyEntity::EndOfMpdu, this, event, Create<WifiPsdu> (*mpdu, false), i, relativeStart, mpduDuration));
//Prepare next iteration
++i;
relativeStart += mpduDuration;
mpduType = (i == (nMpdus - 1)) ? LAST_MPDU_IN_AGGREGATE : MIDDLE_MPDU_IN_AGGREGATE;
}
}
void
PhyEntity::EndOfMpdu (Ptr<Event> event, Ptr<const WifiPsdu> psdu, size_t mpduIndex, Time relativeStart, Time mpduDuration)
{
NS_LOG_FUNCTION (this << *event << mpduIndex << relativeStart << mpduDuration);
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
WifiTxVector txVector = event->GetTxVector ();
uint16_t staId = GetStaId (ppdu);
std::pair<bool, SignalNoiseDbm> rxInfo = GetReceptionStatus (psdu, event, staId, relativeStart, mpduDuration);
NS_LOG_DEBUG ("Extracted MPDU #" << mpduIndex << ": duration: " << mpduDuration.As (Time::NS) <<
", correct reception: " << rxInfo.first << ", Signal/Noise: " << rxInfo.second.signal << "/" << rxInfo.second.noise << "dBm");
auto signalNoiseIt = m_signalNoiseMap.find (std::make_pair (ppdu->GetUid (), staId));
NS_ASSERT (signalNoiseIt != m_signalNoiseMap.end ());
signalNoiseIt->second = rxInfo.second;
RxSignalInfo rxSignalInfo;
rxSignalInfo.snr = rxInfo.second.signal / rxInfo.second.noise;
rxSignalInfo.rssi = rxInfo.second.signal;
auto statusPerMpduIt = m_statusPerMpduMap.find (std::make_pair (ppdu->GetUid (), staId));
NS_ASSERT (statusPerMpduIt != m_statusPerMpduMap.end ());
statusPerMpduIt->second.push_back (rxInfo.first);
if (rxInfo.first && GetAddressedPsduInPpdu (ppdu)->GetNMpdus () > 1)
{
//only done for correct MPDU that is part of an A-MPDU
m_state->NotifyRxMpdu (psdu, rxSignalInfo, txVector);
}
}
void
PhyEntity::EndReceivePayload (Ptr<Event> event)
{
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
WifiTxVector txVector = event->GetTxVector ();
Time psduDuration = ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (txVector);
NS_LOG_FUNCTION (this << *event << psduDuration);
NS_ASSERT (event->GetEndTime () == Simulator::Now ());
uint16_t staId = GetStaId (ppdu);
const auto & channelWidthAndBand = GetChannelWidthAndBand (event->GetTxVector (), staId);
double snr = m_wifiPhy->m_interference->CalculateSnr (event, channelWidthAndBand.first, txVector.GetNss (staId), channelWidthAndBand.second);
Ptr<const WifiPsdu> psdu = GetAddressedPsduInPpdu (ppdu);
m_wifiPhy->NotifyRxEnd (psdu);
auto signalNoiseIt = m_signalNoiseMap.find (std::make_pair (ppdu->GetUid (), staId));
NS_ASSERT (signalNoiseIt != m_signalNoiseMap.end ());
auto statusPerMpduIt = m_statusPerMpduMap.find (std::make_pair (ppdu->GetUid (), staId));
NS_ASSERT (statusPerMpduIt != m_statusPerMpduMap.end ());
if (std::count (statusPerMpduIt->second.begin (), statusPerMpduIt->second.end (), true))
{
//At least one MPDU has been successfully received
m_wifiPhy->NotifyMonitorSniffRx (psdu, m_wifiPhy->GetFrequency (), txVector, signalNoiseIt->second, statusPerMpduIt->second, staId);
RxSignalInfo rxSignalInfo;
rxSignalInfo.snr = snr;
rxSignalInfo.rssi = signalNoiseIt->second.signal; //same information for all MPDUs
RxPayloadSucceeded (psdu, rxSignalInfo, txVector, staId, statusPerMpduIt->second);
m_wifiPhy->m_previouslyRxPpduUid = ppdu->GetUid (); //store UID only if reception is successful (because otherwise trigger won't be read by MAC layer)
}
else
{
RxPayloadFailed (psdu, snr, txVector);
}
DoEndReceivePayload (ppdu);
m_wifiPhy->SwitchMaybeToCcaBusy (ppdu);
}
void
PhyEntity::RxPayloadSucceeded (Ptr<const WifiPsdu> psdu, RxSignalInfo rxSignalInfo,
const WifiTxVector& txVector, uint16_t staId,
const std::vector<bool>& statusPerMpdu)
{
NS_LOG_FUNCTION (this << *psdu << txVector);
m_state->NotifyRxPsduSucceeded (psdu, rxSignalInfo, txVector, staId, statusPerMpdu);
m_state->SwitchFromRxEndOk ();
}
void
PhyEntity::RxPayloadFailed (Ptr<const WifiPsdu> psdu, double snr, const WifiTxVector& txVector)
{
NS_LOG_FUNCTION (this << *psdu << txVector << snr);
m_state->NotifyRxPsduFailed (psdu, snr);
m_state->SwitchFromRxEndError ();
}
void
PhyEntity::DoEndReceivePayload (Ptr<const WifiPpdu> ppdu)
{
NS_LOG_FUNCTION (this << ppdu);
NS_ASSERT (m_wifiPhy->GetLastRxEndTime () == Simulator::Now ());
NotifyInterferenceRxEndAndClear (false); //don't reset WifiPhy
m_wifiPhy->m_currentEvent = 0;
m_wifiPhy->m_currentPreambleEvents.clear ();
m_endRxPayloadEvents.clear ();
}
std::pair<bool, SignalNoiseDbm>
PhyEntity::GetReceptionStatus (Ptr<const WifiPsdu> psdu, Ptr<Event> event, uint16_t staId,
Time relativeMpduStart, Time mpduDuration)
{
NS_LOG_FUNCTION (this << *psdu << *event << staId << relativeMpduStart << mpduDuration);
const auto & channelWidthAndBand = GetChannelWidthAndBand (event->GetTxVector (), staId);
SnrPer snrPer = m_wifiPhy->m_interference->CalculatePayloadSnrPer (event, channelWidthAndBand.first, channelWidthAndBand.second, staId,
std::make_pair (relativeMpduStart, relativeMpduStart + mpduDuration));
WifiMode mode = event->GetTxVector ().GetMode (staId);
NS_LOG_DEBUG ("rate=" << (mode.GetDataRate (event->GetTxVector (), staId)) <<
", SNR(dB)=" << RatioToDb (snrPer.snr) << ", PER=" << snrPer.per << ", size=" << psdu->GetSize () <<
", relativeStart = " << relativeMpduStart.As (Time::NS) << ", duration = " << mpduDuration.As (Time::NS));
// There are two error checks: PER and receive error model check.
// PER check models is typical for Wi-Fi and is based on signal modulation;
// Receive error model is optional, if we have an error model and
// it indicates that the packet is corrupt, drop the packet.
SignalNoiseDbm signalNoise;
signalNoise.signal = WToDbm (event->GetRxPowerW (channelWidthAndBand.second));
signalNoise.noise = WToDbm (event->GetRxPowerW (channelWidthAndBand.second) / snrPer.snr);
if (GetRandomValue () > snrPer.per
&& !(m_wifiPhy->m_postReceptionErrorModel && m_wifiPhy->m_postReceptionErrorModel->IsCorrupt (psdu->GetPacket ()->Copy ())))
{
NS_LOG_DEBUG ("Reception succeeded: " << psdu);
return std::make_pair (true, signalNoise);
}
else
{
NS_LOG_DEBUG ("Reception failed: " << psdu);
return std::make_pair (false, signalNoise);
}
}
std::pair<uint16_t, WifiSpectrumBand>
PhyEntity::GetChannelWidthAndBand (const WifiTxVector& txVector, uint16_t /* staId */) const
{
uint16_t channelWidth = GetRxChannelWidth (txVector);
return std::make_pair (channelWidth, GetPrimaryBand (channelWidth));
}
const std::map <std::pair<uint64_t, WifiPreamble>, Ptr<Event> > &
PhyEntity::GetCurrentPreambleEvents (void) const
{
return m_wifiPhy->m_currentPreambleEvents;
}
void
PhyEntity::AddPreambleEvent (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
m_wifiPhy->m_currentPreambleEvents.insert ({std::make_pair (ppdu->GetUid (), ppdu->GetPreamble ()), event});
}
Ptr<Event>
PhyEntity::DoGetEvent (Ptr<const WifiPpdu> ppdu, RxPowerWattPerChannelBand& rxPowersW)
{
Ptr<Event> event = CreateInterferenceEvent (ppdu, ppdu->GetTxVector (), ppdu->GetTxDuration (), rxPowersW);
//We store all incoming preamble events, and a decision is made at the end of the preamble detection window.
auto uidPreamblePair = std::make_pair (ppdu->GetUid (), ppdu->GetPreamble ());
NS_ASSERT (m_wifiPhy->m_currentPreambleEvents.find (uidPreamblePair) == m_wifiPhy->m_currentPreambleEvents.end ());
m_wifiPhy->m_currentPreambleEvents.insert ({uidPreamblePair, event});
return event;
}
Ptr<Event>
PhyEntity::CreateInterferenceEvent (Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector, Time duration, RxPowerWattPerChannelBand& rxPower, bool isStartOfdmaRxing /* = false */)
{
return m_wifiPhy->m_interference->Add (ppdu, txVector, duration, rxPower, isStartOfdmaRxing);
}
void
PhyEntity::UpdateInterferenceEvent (Ptr<Event> event, const RxPowerWattPerChannelBand& rxPower)
{
m_wifiPhy->m_interference->UpdateEvent (event, rxPower);
}
void
PhyEntity::NotifyInterferenceRxEndAndClear (bool reset)
{
m_wifiPhy->m_interference->NotifyRxEnd (Simulator::Now ());
m_signalNoiseMap.clear ();
m_statusPerMpduMap.clear ();
for (const auto & endOfMpduEvent : m_endOfMpduEvents)
{
NS_ASSERT (endOfMpduEvent.IsExpired ());
}
m_endOfMpduEvents.clear ();
if (reset)
{
m_wifiPhy->Reset ();
}
}
PhyEntity::PhyFieldRxStatus
PhyEntity::DoEndReceivePreamble (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
NS_ASSERT (m_wifiPhy->m_currentPreambleEvents.size () == 1); //Synched on one after detection period
return PhyFieldRxStatus (true); //always consider that preamble has been correctly received if preamble detection was OK
}
void
PhyEntity::StartPreambleDetectionPeriod (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
NS_LOG_DEBUG ("Sync to signal (power=" << WToDbm (GetRxPowerWForPpdu (event)) << "dBm)");
m_wifiPhy->m_interference->NotifyRxStart (); //We need to notify it now so that it starts recording events
m_endPreambleDetectionEvents.push_back (Simulator::Schedule (m_wifiPhy->GetPreambleDetectionDuration (), &PhyEntity::EndPreambleDetectionPeriod, this, event));
}
void
PhyEntity::EndPreambleDetectionPeriod (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
NS_ASSERT (!m_wifiPhy->IsStateRx ());
NS_ASSERT (m_wifiPhy->m_endPhyRxEvent.IsExpired ()); //since end of preamble reception is scheduled by this method upon success
//calculate PER on the measurement channel for PHY headers
uint16_t measurementChannelWidth = GetMeasurementChannelWidth (event->GetPpdu ());
auto measurementBand = GetPrimaryBand (measurementChannelWidth);
double maxRxPowerW = -1; //in case current event may not be sent on measurement channel (rxPowerW would be equal to 0)
Ptr<Event> maxEvent;
NS_ASSERT (!m_wifiPhy->m_currentPreambleEvents.empty ());
for (auto preambleEvent : m_wifiPhy->m_currentPreambleEvents)
{
double rxPowerW = preambleEvent.second->GetRxPowerW (measurementBand);
if (rxPowerW > maxRxPowerW)
{
maxRxPowerW = rxPowerW;
maxEvent = preambleEvent.second;
}
}
NS_ASSERT (maxEvent);
if (maxEvent != event)
{
NS_LOG_DEBUG ("Receiver got a stronger packet with UID " << maxEvent->GetPpdu ()->GetUid () << " during preamble detection: drop packet with UID " << event->GetPpdu ()->GetUid ());
m_wifiPhy->NotifyRxDrop (GetAddressedPsduInPpdu (event->GetPpdu ()), BUSY_DECODING_PREAMBLE);
auto it = m_wifiPhy->m_currentPreambleEvents.find (std::make_pair (event->GetPpdu ()->GetUid (), event->GetPpdu ()->GetPreamble ()));
m_wifiPhy->m_currentPreambleEvents.erase (it);
//This is needed to cleanup the m_firstPowerPerBand so that the first power corresponds to the power at the start of the PPDU
m_wifiPhy->m_interference->NotifyRxEnd (maxEvent->GetStartTime ());
//Make sure InterferenceHelper keeps recording events
m_wifiPhy->m_interference->NotifyRxStart ();
return;
}
m_wifiPhy->m_currentEvent = event;
double snr = m_wifiPhy->m_interference->CalculateSnr (m_wifiPhy->m_currentEvent, measurementChannelWidth, 1, measurementBand);
NS_LOG_DEBUG ("SNR(dB)=" << RatioToDb (snr) << " at end of preamble detection period");
if ((!m_wifiPhy->m_preambleDetectionModel && maxRxPowerW > 0.0)
|| (m_wifiPhy->m_preambleDetectionModel && m_wifiPhy->m_preambleDetectionModel->IsPreambleDetected (m_wifiPhy->m_currentEvent->GetRxPowerW (measurementBand), snr, measurementChannelWidth)))
{
//A bit convoluted but it enables to sync all PHYs
for (auto & it : m_wifiPhy->m_phyEntities)
{
it.second->CancelRunningEndPreambleDetectionEvents (true);
}
for (auto it = m_wifiPhy->m_currentPreambleEvents.begin (); it != m_wifiPhy->m_currentPreambleEvents.end (); )
{
if (it->second != m_wifiPhy->m_currentEvent)
{
NS_LOG_DEBUG ("Drop packet with UID " << it->first.first << " and preamble " << it->first.second << " arrived at time " << it->second->GetStartTime ());
WifiPhyRxfailureReason reason;
if (m_wifiPhy->m_currentEvent->GetPpdu ()->GetUid () > it->first.first)
{
reason = PREAMBLE_DETECTION_PACKET_SWITCH;
//This is needed to cleanup the m_firstPowerPerBand so that the first power corresponds to the power at the start of the PPDU
m_wifiPhy->m_interference->NotifyRxEnd (m_wifiPhy->m_currentEvent->GetStartTime ());
}
else
{
reason = BUSY_DECODING_PREAMBLE;
}
m_wifiPhy->NotifyRxDrop (GetAddressedPsduInPpdu (it->second->GetPpdu ()), reason);
it = m_wifiPhy->m_currentPreambleEvents.erase (it);
}
else
{
++it;
}
}
//Make sure InterferenceHelper keeps recording events
m_wifiPhy->m_interference->NotifyRxStart ();
m_wifiPhy->NotifyRxBegin (GetAddressedPsduInPpdu (m_wifiPhy->m_currentEvent->GetPpdu ()), m_wifiPhy->m_currentEvent->GetRxPowerWPerBand ());
m_wifiPhy->m_timeLastPreambleDetected = Simulator::Now ();
//Continue receiving preamble
Time durationTillEnd = GetDuration (WIFI_PPDU_FIELD_PREAMBLE, event->GetTxVector ()) - m_wifiPhy->GetPreambleDetectionDuration ();
m_wifiPhy->NotifyCcaBusy (event->GetPpdu (), durationTillEnd); //will be prolonged by next field
m_wifiPhy->m_endPhyRxEvent = Simulator::Schedule (durationTillEnd, &PhyEntity::EndReceiveField, this, WIFI_PPDU_FIELD_PREAMBLE, event);
}
else
{
NS_LOG_DEBUG ("Drop packet because PHY preamble detection failed");
// Like CCA-SD, CCA-ED is governed by the 4 us CCA window to flag CCA-BUSY
// for any received signal greater than the CCA-ED threshold.
DropPreambleEvent (m_wifiPhy->m_currentEvent->GetPpdu (), PREAMBLE_DETECT_FAILURE, m_wifiPhy->m_currentEvent->GetEndTime ());
if (m_wifiPhy->m_currentPreambleEvents.empty ())
{
//Do not erase events if there are still pending preamble events to be processed
m_wifiPhy->m_interference->NotifyRxEnd (Simulator::Now ());
}
m_wifiPhy->m_currentEvent = 0;
//Cancel preamble reception
m_wifiPhy->m_endPhyRxEvent.Cancel ();
}
}
bool
PhyEntity::IsConfigSupported (Ptr<const WifiPpdu> ppdu) const
{
WifiMode txMode = ppdu->GetTxVector ().GetMode ();
if (!IsModeSupported (txMode))
{
NS_LOG_DEBUG ("Drop packet because it was sent using an unsupported mode (" << txMode << ")");
return false;
}
return true;
}
void
PhyEntity::CancelAllEvents (void)
{
NS_LOG_FUNCTION (this);
for (auto & endPreambleDetectionEvent : m_endPreambleDetectionEvents)
{
endPreambleDetectionEvent.Cancel ();
}
m_endPreambleDetectionEvents.clear ();
for (auto & endRxPayloadEvent : m_endRxPayloadEvents)
{
endRxPayloadEvent.Cancel ();
}
m_endRxPayloadEvents.clear ();
for (auto & endMpduEvent : m_endOfMpduEvents)
{
endMpduEvent.Cancel ();
}
m_endOfMpduEvents.clear ();
}
bool
PhyEntity::NoEndPreambleDetectionEvents (void) const
{
return m_endPreambleDetectionEvents.empty ();
}
void
PhyEntity::CancelRunningEndPreambleDetectionEvents (bool clear /* = false */)
{
NS_LOG_FUNCTION (this << clear);
for (auto & endPreambleDetectionEvent : m_endPreambleDetectionEvents)
{
if (endPreambleDetectionEvent.IsRunning ())
{
endPreambleDetectionEvent.Cancel ();
}
}
if (clear)
{
m_endPreambleDetectionEvents.clear ();
}
}
void
PhyEntity::AbortCurrentReception (WifiPhyRxfailureReason reason)
{
NS_LOG_FUNCTION (this << reason);
DoAbortCurrentReception (reason);
m_wifiPhy->AbortCurrentReception (reason);
}
void
PhyEntity::DoAbortCurrentReception (WifiPhyRxfailureReason reason)
{
NS_LOG_FUNCTION (this << reason);
if (m_wifiPhy->m_currentEvent) //Otherwise abort has already been called just before
{
for (auto & endMpduEvent : m_endOfMpduEvents)
{
endMpduEvent.Cancel ();
}
m_endOfMpduEvents.clear ();
}
}
void
PhyEntity::ResetReceive (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
DoResetReceive (event);
NS_ASSERT (!m_wifiPhy->IsStateRx ());
m_wifiPhy->m_interference->NotifyRxEnd (Simulator::Now ());
NS_ASSERT (m_endRxPayloadEvents.size () == 1 && m_endRxPayloadEvents.front ().IsExpired ());
m_endRxPayloadEvents.clear ();
m_wifiPhy->m_currentEvent = 0;
m_wifiPhy->m_currentPreambleEvents.clear ();
m_wifiPhy->SwitchMaybeToCcaBusy (event->GetPpdu ());
}
void
PhyEntity::DoResetReceive (Ptr<Event> event)
{
NS_LOG_FUNCTION (this << *event);
NS_ASSERT (event->GetEndTime () == Simulator::Now ());
}
double
PhyEntity::GetRandomValue (void) const
{
return m_wifiPhy->m_random->GetValue ();
}
double
PhyEntity::GetRxPowerWForPpdu (Ptr<Event> event) const
{
return event->GetRxPowerW (GetPrimaryBand (GetMeasurementChannelWidth (event->GetPpdu ())));
}
Ptr<const Event>
PhyEntity::GetCurrentEvent (void) const
{
return m_wifiPhy->m_currentEvent;
}
WifiSpectrumBand
PhyEntity::GetPrimaryBand (uint16_t bandWidth) const
{
if (m_wifiPhy->GetChannelWidth () % 20 != 0)
{
return m_wifiPhy->GetBand (bandWidth);
}
return m_wifiPhy->GetBand (bandWidth, m_wifiPhy->m_operatingChannel.GetPrimaryChannelIndex (bandWidth));
}
WifiSpectrumBand
PhyEntity::GetSecondaryBand (uint16_t bandWidth) const
{
NS_ASSERT (m_wifiPhy->GetChannelWidth () >= 40);
return m_wifiPhy->GetBand (bandWidth, m_wifiPhy->m_operatingChannel.GetSecondaryChannelIndex (bandWidth));
}
uint16_t
PhyEntity::GetRxChannelWidth (const WifiTxVector& txVector) const
{
return std::min (m_wifiPhy->GetChannelWidth (), txVector.GetChannelWidth ());
}
double
PhyEntity::GetCcaThreshold (const Ptr<const WifiPpdu> ppdu, WifiChannelListType /*channelType*/) const
{
return (!ppdu) ? m_wifiPhy->GetCcaEdThreshold () : m_wifiPhy->GetCcaSensitivityThreshold ();
}
Time
PhyEntity::GetDelayUntilCcaEnd (double thresholdDbm, WifiSpectrumBand band)
{
return m_wifiPhy->m_interference->GetEnergyDuration (DbmToW (thresholdDbm), band);
}
void
PhyEntity::SwitchMaybeToCcaBusy (const Ptr<const WifiPpdu> ppdu)
{
//We are here because we have received the first bit of a packet and we are
//not going to be able to synchronize on it
//In this model, CCA becomes busy when the aggregation of all signals as
//tracked by the InterferenceHelper class is higher than the CcaBusyThreshold
const auto ccaIndication = GetCcaIndication (ppdu);
if (ccaIndication.has_value ())
{
NS_LOG_DEBUG ("CCA busy for " << ccaIndication.value ().second << " during " << ccaIndication.value ().first.As (Time::S));
m_state->SwitchMaybeToCcaBusy (ccaIndication.value ().first, ccaIndication.value ().second, {});
return;
}
if (ppdu)
{
SwitchMaybeToCcaBusy (nullptr);
}
}
PhyEntity::CcaIndication
PhyEntity::GetCcaIndication (const Ptr<const WifiPpdu> ppdu)
{
const uint16_t channelWidth = GetMeasurementChannelWidth (ppdu);
NS_LOG_FUNCTION (this << channelWidth);
const double ccaThresholdDbm = GetCcaThreshold (ppdu, WIFI_CHANLIST_PRIMARY);
const Time delayUntilCcaEnd = GetDelayUntilCcaEnd (ccaThresholdDbm, GetPrimaryBand (channelWidth));
if (delayUntilCcaEnd.IsStrictlyPositive ())
{
return std::make_pair (delayUntilCcaEnd, WIFI_CHANLIST_PRIMARY);
}
return std::nullopt;
}
void
PhyEntity::NotifyCcaBusy (const Ptr<const WifiPpdu> /*ppdu*/, Time duration, WifiChannelListType channelType)
{
NS_LOG_FUNCTION (this << duration << channelType);
NS_LOG_DEBUG ("CCA busy for " << channelType << " during " << duration.As (Time::S));
m_state->SwitchMaybeToCcaBusy (duration, channelType, {});
}
uint64_t
PhyEntity::ObtainNextUid (const WifiTxVector& /* txVector */)
{
NS_LOG_FUNCTION (this);
return m_globalPpduUid++;
}
uint16_t
PhyEntity::GetCenterFrequencyForChannelWidth (const WifiTxVector& txVector) const
{
NS_LOG_FUNCTION (this << txVector);
return m_wifiPhy->GetOperatingChannel ().GetPrimaryChannelCenterFrequency (txVector.GetChannelWidth ());
}
void
PhyEntity::NotifyPayloadBegin (const WifiTxVector& txVector, const Time& payloadDuration)
{
m_wifiPhy->m_phyRxPayloadBeginTrace (txVector, payloadDuration);
}
void
PhyEntity::StartTx (Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector)
{
NS_LOG_FUNCTION (this << ppdu << txVector);
Transmit (ppdu->GetTxDuration (), ppdu, txVector, "transmission");
}
void
PhyEntity::Transmit (Time txDuration, Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector, std::string type)
{
NS_LOG_FUNCTION (this << txDuration << ppdu << txVector << type);
double txPowerWatts = DbmToW (m_wifiPhy->GetTxPowerForTransmission (ppdu) + m_wifiPhy->GetTxGain ());
NS_LOG_DEBUG ("Start " << type << ": signal power before antenna gain=" << WToDbm (txPowerWatts) << "dBm");
Ptr<SpectrumValue> txPowerSpectrum = GetTxPowerSpectralDensity (txPowerWatts, ppdu, txVector);
Ptr<WifiSpectrumSignalParameters> txParams = Create<WifiSpectrumSignalParameters> ();
txParams->duration = txDuration;
txParams->psd = txPowerSpectrum;
txParams->ppdu = ppdu;
txParams->txCenterFreq = GetCenterFrequencyForChannelWidth (txVector);
NS_LOG_DEBUG ("Starting " << type << " with power " << WToDbm (txPowerWatts) << " dBm on channel " << +m_wifiPhy->GetChannelNumber () << " for " << txParams->duration.As (Time::MS));
NS_LOG_DEBUG ("Starting " << type << " with integrated spectrum power " << WToDbm (Integral (*txPowerSpectrum)) << " dBm; spectrum model Uid: " << txPowerSpectrum->GetSpectrumModel ()->GetUid ());
auto spectrumWifiPhy = DynamicCast<SpectrumWifiPhy> (m_wifiPhy);
NS_ASSERT (spectrumWifiPhy);
spectrumWifiPhy->Transmit (txParams);
}
uint16_t
PhyEntity::GetGuardBandwidth (uint16_t currentChannelWidth) const
{
return m_wifiPhy->GetGuardBandwidth (currentChannelWidth);
}
std::tuple<double, double, double>
PhyEntity::GetTxMaskRejectionParams (void) const
{
return m_wifiPhy->GetTxMaskRejectionParams ();
}
Time
PhyEntity::CalculateTxDuration (WifiConstPsduMap psduMap, const WifiTxVector& txVector, WifiPhyBand band) const
{
NS_ASSERT (psduMap.size () == 1);
const auto & it = psduMap.begin ();
return WifiPhy::CalculateTxDuration (it->second->GetSize (), txVector, band, it->first);
}
} //namespace ns3