1181 lines
44 KiB
C++
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
|