EIFS depends on the modulation class of the PPDU whose reception failed rather than on the latest standard supported by a device
1464 lines
53 KiB
C++
1464 lines
53 KiB
C++
/*
|
|
* Copyright (c) 2020 Orange Labs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*
|
|
* 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 "frame-capture-model.h"
|
|
#include "interference-helper.h"
|
|
#include "preamble-detection-model.h"
|
|
#include "spectrum-wifi-phy.h"
|
|
#include "wifi-psdu.h"
|
|
#include "wifi-spectrum-signal-parameters.h"
|
|
#include "wifi-utils.h"
|
|
|
|
#include "ns3/assert.h"
|
|
#include "ns3/data-rate.h"
|
|
#include "ns3/log.h"
|
|
#include "ns3/packet.h"
|
|
#include "ns3/simulator.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#undef NS_LOG_APPEND_CONTEXT
|
|
#define NS_LOG_APPEND_CONTEXT WIFI_PHY_NS_LOG_APPEND_CONTEXT(m_wifiPhy)
|
|
|
|
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() 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() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::list<WifiMode>::const_iterator
|
|
PhyEntity::begin() const
|
|
{
|
|
return m_modeList.begin();
|
|
}
|
|
|
|
std::list<WifiMode>::const_iterator
|
|
PhyEntity::end() 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
|
|
{
|
|
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 Time(); // should be overloaded
|
|
}
|
|
|
|
Time
|
|
PhyEntity::CalculatePhyPreambleAndHeaderDuration(const WifiTxVector& txVector) const
|
|
{
|
|
Time duration;
|
|
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({{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] = {{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 << ppduDuration);
|
|
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()); // 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, Time());
|
|
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
|
|
{
|
|
const auto 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->GetPpdu()->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);
|
|
const auto& txVector = event->GetPpdu()->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->NotifyRxPpduDrop(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 auto& 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
|
|
const 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 auto& p1, const auto& p2) {
|
|
return p1.second < p2.second;
|
|
});
|
|
NS_LOG_FUNCTION(this << ppdu << it->second);
|
|
|
|
auto 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 (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;
|
|
case WifiPhyState::OFF:
|
|
NS_LOG_DEBUG("Drop packet because in switched off");
|
|
DropPreambleEvent(ppdu, WifiPhyRxfailureReason::POWERED_OFF, 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->NotifyRxPpduDrop(ppdu, reason);
|
|
auto it = m_wifiPhy->m_currentPreambleEvents.find({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({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({{ppdu->GetUid(), staId}, SignalNoiseDbm()});
|
|
m_statusPerMpduMap.insert({{ppdu->GetUid(), staId}, std::vector<bool>()});
|
|
ScheduleEndOfMpdus(event);
|
|
const auto& txVector = event->GetPpdu()->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 auto& txVector = event->GetPpdu()->GetTxVector();
|
|
uint16_t staId = GetStaId(ppdu);
|
|
Time endOfMpduDuration;
|
|
Time relativeStart;
|
|
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)
|
|
{
|
|
if (m_wifiPhy->m_notifyRxMacHeaderEnd)
|
|
{
|
|
// calculate MAC header size (including A-MPDU subframe header, if present)
|
|
auto macHdrSize =
|
|
(*mpdu)->GetHeader().GetSerializedSize() + (mpduType == NORMAL_MPDU ? 0 : 4);
|
|
// calculate the (approximate) duration of the MAC header TX
|
|
auto macHdrDuration = DataRate(txVector.GetMode(staId).GetDataRate(txVector, staId))
|
|
.CalculateBytesTxTime(macHdrSize);
|
|
const auto widthBand = GetChannelWidthAndBand(txVector, staId);
|
|
const auto snrPer = m_wifiPhy->m_interference->CalculatePayloadSnrPer(
|
|
event,
|
|
widthBand.first,
|
|
widthBand.second,
|
|
staId,
|
|
{relativeStart, relativeStart + macHdrDuration});
|
|
if (GetRandomValue() > snrPer.per)
|
|
{
|
|
// interference level should permit to correctly decode the MAC header
|
|
m_endOfMacHdrEvents[staId].push_back(
|
|
Simulator::Schedule(endOfMpduDuration + macHdrDuration, [=, this]() {
|
|
m_wifiPhy->m_phyRxMacHeaderEndTrace((*mpdu)->GetHeader(),
|
|
txVector,
|
|
remainingAmpduDuration -
|
|
macHdrDuration);
|
|
}));
|
|
}
|
|
}
|
|
|
|
uint32_t size = (mpduType == NORMAL_MPDU) ? psdu->GetSize() : psdu->GetAmpduSubframeSize(i);
|
|
Time mpduDuration = 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 < 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,
|
|
*mpdu,
|
|
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<WifiMpdu> mpdu,
|
|
size_t mpduIndex,
|
|
Time relativeStart,
|
|
Time mpduDuration)
|
|
{
|
|
NS_LOG_FUNCTION(this << *event << mpduIndex << relativeStart << mpduDuration);
|
|
const auto ppdu = event->GetPpdu();
|
|
const auto& txVector = ppdu->GetTxVector();
|
|
uint16_t staId = GetStaId(ppdu);
|
|
|
|
std::pair<bool, SignalNoiseDbm> rxInfo =
|
|
GetReceptionStatus(mpdu, 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({ppdu->GetUid(), staId});
|
|
NS_ASSERT(signalNoiseIt != m_signalNoiseMap.end());
|
|
signalNoiseIt->second = rxInfo.second;
|
|
|
|
RxSignalInfo rxSignalInfo;
|
|
rxSignalInfo.snr = DbToRatio(dB_u{rxInfo.second.signal - rxInfo.second.noise});
|
|
rxSignalInfo.rssi = rxInfo.second.signal;
|
|
|
|
auto statusPerMpduIt = m_statusPerMpduMap.find({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(Create<const WifiPsdu>(mpdu, false), rxSignalInfo, txVector);
|
|
}
|
|
}
|
|
|
|
void
|
|
PhyEntity::EndReceivePayload(Ptr<Event> event)
|
|
{
|
|
const auto ppdu = event->GetPpdu();
|
|
const auto& txVector = ppdu->GetTxVector();
|
|
NS_LOG_FUNCTION(
|
|
this << *event << ppdu->GetTxDuration() - CalculatePhyPreambleAndHeaderDuration(txVector));
|
|
NS_ASSERT(event->GetEndTime() == Simulator::Now());
|
|
const auto staId = GetStaId(ppdu);
|
|
const auto channelWidthAndBand = GetChannelWidthAndBand(txVector, staId);
|
|
const auto 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({ppdu->GetUid(), staId});
|
|
NS_ASSERT(signalNoiseIt != m_signalNoiseMap.end());
|
|
auto statusPerMpduIt = m_statusPerMpduMap.find({ppdu->GetUid(), staId});
|
|
NS_ASSERT(statusPerMpduIt != m_statusPerMpduMap.end());
|
|
// store per-MPDU status, which is cleared by the call to DoEndReceivePayload below
|
|
auto statusPerMpdu = statusPerMpduIt->second;
|
|
|
|
RxSignalInfo rxSignalInfo;
|
|
bool success;
|
|
|
|
if (std::count(statusPerMpdu.cbegin(), statusPerMpdu.cend(), true))
|
|
{
|
|
// At least one MPDU has been successfully received
|
|
m_wifiPhy->NotifyMonitorSniffRx(psdu,
|
|
m_wifiPhy->GetFrequency(),
|
|
txVector,
|
|
signalNoiseIt->second,
|
|
statusPerMpdu,
|
|
staId);
|
|
rxSignalInfo.snr = snr;
|
|
rxSignalInfo.rssi = signalNoiseIt->second.signal; // same information for all MPDUs
|
|
RxPayloadSucceeded(psdu, rxSignalInfo, txVector, staId, statusPerMpdu);
|
|
m_wifiPhy->m_previouslyRxPpduUid =
|
|
ppdu->GetUid(); // store UID only if reception is successful (because otherwise trigger
|
|
// won't be read by MAC layer)
|
|
success = true;
|
|
}
|
|
else
|
|
{
|
|
RxPayloadFailed(psdu, snr, txVector);
|
|
success = false;
|
|
}
|
|
|
|
m_state->NotifyRxPpduOutcome(ppdu, rxSignalInfo, txVector, staId, statusPerMpduIt->second);
|
|
DoEndReceivePayload(ppdu);
|
|
m_wifiPhy->SwitchMaybeToCcaBusy(ppdu);
|
|
|
|
// notify the MAC through the PHY state helper as the last action. Indeed, the notification
|
|
// of the RX end may lead the MAC to request a PHY state change (e.g., channel switch, sleep).
|
|
// Hence, all actions the PHY has to perform when RX ends should be completed before
|
|
// notifying the MAC.
|
|
success ? m_state->NotifyRxPsduSucceeded(psdu, rxSignalInfo, txVector, staId, statusPerMpdu)
|
|
: m_state->NotifyRxPsduFailed(psdu, snr);
|
|
}
|
|
|
|
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->SwitchFromRxEndOk();
|
|
}
|
|
|
|
void
|
|
PhyEntity::RxPayloadFailed(Ptr<const WifiPsdu> psdu, double snr, const WifiTxVector& txVector)
|
|
{
|
|
NS_LOG_FUNCTION(this << *psdu << txVector << snr);
|
|
m_state->SwitchFromRxEndError(txVector);
|
|
}
|
|
|
|
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 = nullptr;
|
|
m_wifiPhy->m_currentPreambleEvents.clear();
|
|
m_endRxPayloadEvents.clear();
|
|
}
|
|
|
|
std::pair<bool, SignalNoiseDbm>
|
|
PhyEntity::GetReceptionStatus(Ptr<WifiMpdu> mpdu,
|
|
Ptr<Event> event,
|
|
uint16_t staId,
|
|
Time relativeMpduStart,
|
|
Time mpduDuration)
|
|
{
|
|
NS_LOG_FUNCTION(this << *mpdu << *event << staId << relativeMpduStart << mpduDuration);
|
|
const auto channelWidthAndBand = GetChannelWidthAndBand(event->GetPpdu()->GetTxVector(), staId);
|
|
SnrPer snrPer = m_wifiPhy->m_interference->CalculatePayloadSnrPer(
|
|
event,
|
|
channelWidthAndBand.first,
|
|
channelWidthAndBand.second,
|
|
staId,
|
|
{relativeMpduStart, relativeMpduStart + mpduDuration});
|
|
|
|
WifiMode mode = event->GetPpdu()->GetTxVector().GetMode(staId);
|
|
NS_LOG_DEBUG("rate=" << (mode.GetDataRate(event->GetPpdu()->GetTxVector(), staId))
|
|
<< ", SNR(dB)=" << RatioToDb(snrPer.snr) << ", PER=" << snrPer.per
|
|
<< ", size=" << mpdu->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->GetRxPower(channelWidthAndBand.second));
|
|
signalNoise.noise = WToDbm(event->GetRxPower(channelWidthAndBand.second) / snrPer.snr);
|
|
if (GetRandomValue() > snrPer.per &&
|
|
!(m_wifiPhy->m_postReceptionErrorModel &&
|
|
m_wifiPhy->m_postReceptionErrorModel->IsCorrupt(mpdu->GetPacket()->Copy())))
|
|
{
|
|
NS_LOG_DEBUG("Reception succeeded: " << *mpdu);
|
|
return {true, signalNoise};
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_DEBUG("Reception failed: " << *mpdu);
|
|
return {false, signalNoise};
|
|
}
|
|
}
|
|
|
|
std::optional<Time>
|
|
PhyEntity::GetTimeToMacHdrEnd(uint16_t staId) const
|
|
{
|
|
const auto it = m_endOfMacHdrEvents.find(staId);
|
|
|
|
if (it == m_endOfMacHdrEvents.cend())
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
for (const auto& endOfMacHdrEvent : it->second)
|
|
{
|
|
if (endOfMacHdrEvent.IsPending())
|
|
{
|
|
return Simulator::GetDelayLeft(endOfMacHdrEvent);
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::pair<MHz_u, WifiSpectrumBandInfo>
|
|
PhyEntity::GetChannelWidthAndBand(const WifiTxVector& txVector, uint16_t /* staId */) const
|
|
{
|
|
const auto channelWidth = GetRxChannelWidth(txVector);
|
|
return {channelWidth, GetPrimaryBand(channelWidth)};
|
|
}
|
|
|
|
const std::map<std::pair<uint64_t, WifiPreamble>, Ptr<Event>>&
|
|
PhyEntity::GetCurrentPreambleEvents() 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({{ppdu->GetUid(), ppdu->GetPreamble()}, event});
|
|
}
|
|
|
|
Ptr<Event>
|
|
PhyEntity::DoGetEvent(Ptr<const WifiPpdu> ppdu, RxPowerWattPerChannelBand& rxPowersW)
|
|
{
|
|
// We store all incoming preamble events, and a decision is made at the end of the preamble
|
|
// detection window.
|
|
const auto& currentPreambleEvents = GetCurrentPreambleEvents();
|
|
const auto it = currentPreambleEvents.find({ppdu->GetUid(), ppdu->GetPreamble()});
|
|
if (it != currentPreambleEvents.cend())
|
|
{
|
|
// received another signal with the same content
|
|
NS_LOG_DEBUG("Received another PPDU for UID " << ppdu->GetUid());
|
|
const auto foundEvent = it->second;
|
|
HandleRxPpduWithSameContent(foundEvent, ppdu, rxPowersW);
|
|
return nullptr;
|
|
}
|
|
|
|
auto event = CreateInterferenceEvent(ppdu, ppdu->GetTxDuration(), rxPowersW);
|
|
AddPreambleEvent(event);
|
|
return event;
|
|
}
|
|
|
|
Ptr<Event>
|
|
PhyEntity::CreateInterferenceEvent(Ptr<const WifiPpdu> ppdu,
|
|
Time duration,
|
|
RxPowerWattPerChannelBand& rxPower,
|
|
bool isStartHePortionRxing /* = false */)
|
|
{
|
|
return m_wifiPhy->m_interference->Add(ppdu,
|
|
duration,
|
|
rxPower,
|
|
m_wifiPhy->GetCurrentFrequencyRange(),
|
|
isStartHePortionRxing);
|
|
}
|
|
|
|
void
|
|
PhyEntity::HandleRxPpduWithSameContent(Ptr<Event> event,
|
|
Ptr<const WifiPpdu> ppdu,
|
|
RxPowerWattPerChannelBand& rxPower)
|
|
{
|
|
if (const auto maxDelay =
|
|
m_wifiPhy->GetPhyEntityForPpdu(ppdu)->GetMaxDelayPpduSameUid(ppdu->GetTxVector());
|
|
Simulator::Now() - event->GetStartTime() > maxDelay)
|
|
{
|
|
// This PPDU arrived too late to be decoded properly. The PPDU is dropped and added as
|
|
// interference
|
|
event = CreateInterferenceEvent(ppdu, ppdu->GetTxDuration(), rxPower);
|
|
NS_LOG_DEBUG("Drop PPDU that arrived too late");
|
|
m_wifiPhy->NotifyRxPpduDrop(ppdu, PPDU_TOO_LATE);
|
|
return;
|
|
}
|
|
|
|
// Update received power and TXVECTOR of the event associated to that transmission upon
|
|
// reception of a signal adding up constructively (in case of a UL MU PPDU or non-HT duplicate
|
|
// PPDU)
|
|
m_wifiPhy->m_interference->UpdateEvent(event, rxPower);
|
|
const auto& txVector = ppdu->GetTxVector();
|
|
const auto& eventTxVector = event->GetPpdu()->GetTxVector();
|
|
auto updatedTxVector{eventTxVector};
|
|
updatedTxVector.SetChannelWidth(
|
|
std::max(eventTxVector.GetChannelWidth(), txVector.GetChannelWidth()));
|
|
if (updatedTxVector.GetChannelWidth() != eventTxVector.GetChannelWidth())
|
|
{
|
|
event->UpdatePpdu(ppdu);
|
|
}
|
|
}
|
|
|
|
void
|
|
PhyEntity::NotifyInterferenceRxEndAndClear(bool reset)
|
|
{
|
|
m_wifiPhy->m_interference->NotifyRxEnd(Simulator::Now(), m_wifiPhy->GetCurrentFrequencyRange());
|
|
m_signalNoiseMap.clear();
|
|
m_statusPerMpduMap.clear();
|
|
for (const auto& endOfMpduEvent : m_endOfMpduEvents)
|
|
{
|
|
NS_ASSERT(endOfMpduEvent.IsExpired());
|
|
}
|
|
m_endOfMpduEvents.clear();
|
|
for (const auto& [staId, endOfMacHdrEvents] : m_endOfMacHdrEvents)
|
|
{
|
|
for (const auto& endOfMacHdrEvent : endOfMacHdrEvents)
|
|
{
|
|
NS_ASSERT(endOfMacHdrEvent.IsExpired());
|
|
}
|
|
}
|
|
m_endOfMacHdrEvents.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);
|
|
const auto rxPower = GetRxPowerForPpdu(event);
|
|
NS_LOG_DEBUG("Sync to signal (power=" << (rxPower > Watt_u{0.0}
|
|
? std::to_string(WToDbm(rxPower)) + "dBm)"
|
|
: std::to_string(rxPower) + "W)"));
|
|
m_wifiPhy->m_interference->NotifyRxStart(
|
|
m_wifiPhy->GetCurrentFrequencyRange()); // We need to notify it now so that it starts
|
|
// recording events
|
|
m_endPreambleDetectionEvents.push_back(
|
|
Simulator::Schedule(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
|
|
const auto measurementChannelWidth = GetMeasurementChannelWidth(event->GetPpdu());
|
|
auto measurementBand = GetPrimaryBand(measurementChannelWidth);
|
|
std::optional<Watt_u>
|
|
maxRxPower; // in case current event may not be sent on measurement channel
|
|
Ptr<Event> maxEvent;
|
|
NS_ASSERT(!m_wifiPhy->m_currentPreambleEvents.empty());
|
|
for (auto preambleEvent : m_wifiPhy->m_currentPreambleEvents)
|
|
{
|
|
const auto rxPower = preambleEvent.second->GetRxPower(measurementBand);
|
|
if (!maxRxPower || (rxPower > *maxRxPower))
|
|
{
|
|
maxRxPower = rxPower;
|
|
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->NotifyRxPpduDrop(event->GetPpdu(), BUSY_DECODING_PREAMBLE);
|
|
|
|
auto it = m_wifiPhy->m_currentPreambleEvents.find(
|
|
{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(),
|
|
m_wifiPhy->GetCurrentFrequencyRange());
|
|
// Make sure InterferenceHelper keeps recording events
|
|
m_wifiPhy->m_interference->NotifyRxStart(m_wifiPhy->GetCurrentFrequencyRange());
|
|
return;
|
|
}
|
|
|
|
m_wifiPhy->m_currentEvent = event;
|
|
|
|
const auto 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 (const auto power = m_wifiPhy->m_currentEvent->GetRxPower(measurementBand);
|
|
(!m_wifiPhy->m_preambleDetectionModel && maxRxPower && (*maxRxPower > Watt_u{0.0})) ||
|
|
(m_wifiPhy->m_preambleDetectionModel && power > Watt_u{0.0} &&
|
|
m_wifiPhy->m_preambleDetectionModel->IsPreambleDetected(WToDbm(power),
|
|
snr,
|
|
measurementChannelWidth)))
|
|
{
|
|
// A bit convoluted but it enables to sync all PHYs
|
|
for (auto& it : m_wifiPhy->m_phyEntities)
|
|
{
|
|
it.second->CancelRunningEndPreambleDetectionEvents();
|
|
}
|
|
|
|
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(),
|
|
m_wifiPhy->GetCurrentFrequencyRange());
|
|
}
|
|
else
|
|
{
|
|
reason = BUSY_DECODING_PREAMBLE;
|
|
}
|
|
m_wifiPhy->NotifyRxPpduDrop(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->GetCurrentFrequencyRange());
|
|
|
|
m_wifiPhy->NotifyRxBegin(GetAddressedPsduInPpdu(m_wifiPhy->m_currentEvent->GetPpdu()),
|
|
m_wifiPhy->m_currentEvent->GetRxPowerPerBand());
|
|
m_wifiPhy->m_timeLastPreambleDetected = Simulator::Now();
|
|
|
|
// Continue receiving preamble
|
|
const auto durationTillEnd =
|
|
GetDuration(WIFI_PPDU_FIELD_PREAMBLE, event->GetPpdu()->GetTxVector()) -
|
|
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->GetCurrentFrequencyRange());
|
|
}
|
|
m_wifiPhy->m_currentEvent = nullptr;
|
|
// 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()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
CancelRunningEndPreambleDetectionEvents();
|
|
for (auto& endRxPayloadEvent : m_endRxPayloadEvents)
|
|
{
|
|
endRxPayloadEvent.Cancel();
|
|
}
|
|
m_endRxPayloadEvents.clear();
|
|
for (auto& endMpduEvent : m_endOfMpduEvents)
|
|
{
|
|
endMpduEvent.Cancel();
|
|
}
|
|
m_endOfMpduEvents.clear();
|
|
for (auto& [staId, endOfMacHdrEvents] : m_endOfMacHdrEvents)
|
|
{
|
|
for (auto& endMacHdrEvent : endOfMacHdrEvents)
|
|
{
|
|
endMacHdrEvent.Cancel();
|
|
}
|
|
}
|
|
m_endOfMacHdrEvents.clear();
|
|
}
|
|
|
|
bool
|
|
PhyEntity::NoEndPreambleDetectionEvents() const
|
|
{
|
|
return m_endPreambleDetectionEvents.empty();
|
|
}
|
|
|
|
void
|
|
PhyEntity::CancelRunningEndPreambleDetectionEvents()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
for (auto& endPreambleDetectionEvent : m_endPreambleDetectionEvents)
|
|
{
|
|
endPreambleDetectionEvent.Cancel();
|
|
}
|
|
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();
|
|
for (auto& [staId, endOfMacHdrEvents] : m_endOfMacHdrEvents)
|
|
{
|
|
for (auto& endMacHdrEvent : endOfMacHdrEvents)
|
|
{
|
|
endMacHdrEvent.Cancel();
|
|
}
|
|
}
|
|
m_endOfMacHdrEvents.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(), m_wifiPhy->GetCurrentFrequencyRange());
|
|
NS_ASSERT(m_endRxPayloadEvents.size() == 1 && m_endRxPayloadEvents.front().IsExpired());
|
|
m_endRxPayloadEvents.clear();
|
|
m_wifiPhy->m_currentEvent = nullptr;
|
|
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() const
|
|
{
|
|
return m_wifiPhy->m_random->GetValue();
|
|
}
|
|
|
|
Watt_u
|
|
PhyEntity::GetRxPowerForPpdu(Ptr<Event> event) const
|
|
{
|
|
return event->GetRxPower(GetPrimaryBand(GetMeasurementChannelWidth(event->GetPpdu())));
|
|
}
|
|
|
|
Ptr<const Event>
|
|
PhyEntity::GetCurrentEvent() const
|
|
{
|
|
return m_wifiPhy->m_currentEvent;
|
|
}
|
|
|
|
WifiSpectrumBandInfo
|
|
PhyEntity::GetPrimaryBand(MHz_u bandWidth) const
|
|
{
|
|
if (static_cast<uint16_t>(m_wifiPhy->GetChannelWidth()) % 20 != 0)
|
|
{
|
|
return m_wifiPhy->GetBand(bandWidth);
|
|
}
|
|
return m_wifiPhy->GetBand(bandWidth,
|
|
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(bandWidth));
|
|
}
|
|
|
|
WifiSpectrumBandInfo
|
|
PhyEntity::GetSecondaryBand(MHz_u bandWidth) const
|
|
{
|
|
NS_ASSERT(m_wifiPhy->GetChannelWidth() >= MHz_u{40});
|
|
return m_wifiPhy->GetBand(bandWidth,
|
|
m_wifiPhy->GetOperatingChannel().GetSecondaryChannelIndex(bandWidth));
|
|
}
|
|
|
|
MHz_u
|
|
PhyEntity::GetRxChannelWidth(const WifiTxVector& txVector) const
|
|
{
|
|
return std::min(m_wifiPhy->GetChannelWidth(), txVector.GetChannelWidth());
|
|
}
|
|
|
|
dBm_u
|
|
PhyEntity::GetCcaThreshold(const Ptr<const WifiPpdu> ppdu,
|
|
WifiChannelListType /*channelType*/) const
|
|
{
|
|
return (!ppdu) ? m_wifiPhy->GetCcaEdThreshold() : m_wifiPhy->GetCcaSensitivityThreshold();
|
|
}
|
|
|
|
Time
|
|
PhyEntity::GetDelayUntilCcaEnd(dBm_u threshold, const WifiSpectrumBandInfo& band)
|
|
{
|
|
return m_wifiPhy->m_interference->GetEnergyDuration(DbmToW(threshold), 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 auto channelWidth = GetMeasurementChannelWidth(ppdu);
|
|
NS_LOG_FUNCTION(this << channelWidth);
|
|
const auto ccaThreshold = GetCcaThreshold(ppdu, WIFI_CHANLIST_PRIMARY);
|
|
const Time delayUntilCcaEnd = GetDelayUntilCcaEnd(ccaThreshold, 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++;
|
|
}
|
|
|
|
Time
|
|
PhyEntity::GetMaxDelayPpduSameUid(const WifiTxVector& /*txVector*/)
|
|
{
|
|
return Seconds(0);
|
|
}
|
|
|
|
void
|
|
PhyEntity::NotifyPayloadBegin(const WifiTxVector& txVector, const Time& payloadDuration)
|
|
{
|
|
m_wifiPhy->m_phyRxPayloadBeginTrace(txVector, payloadDuration);
|
|
}
|
|
|
|
void
|
|
PhyEntity::StartTx(Ptr<const WifiPpdu> ppdu)
|
|
{
|
|
NS_LOG_FUNCTION(this << ppdu);
|
|
auto txPower = m_wifiPhy->GetTxPowerForTransmission(ppdu) + m_wifiPhy->GetTxGain();
|
|
auto txVector = ppdu->GetTxVector();
|
|
auto txPowerSpectrum = GetTxPowerSpectralDensity(DbmToW(txPower), ppdu);
|
|
Transmit(ppdu->GetTxDuration(), ppdu, txPower, txPowerSpectrum, "transmission");
|
|
}
|
|
|
|
void
|
|
PhyEntity::Transmit(Time txDuration,
|
|
Ptr<const WifiPpdu> ppdu,
|
|
dBm_u txPower,
|
|
Ptr<SpectrumValue> txPowerSpectrum,
|
|
const std::string& type)
|
|
{
|
|
NS_LOG_FUNCTION(this << txDuration << ppdu << txPower << type);
|
|
NS_LOG_DEBUG("Start " << type << ": signal power before antenna gain=" << txPower << "dBm");
|
|
auto txParams = Create<WifiSpectrumSignalParameters>();
|
|
txParams->duration = txDuration;
|
|
txParams->psd = txPowerSpectrum;
|
|
txParams->ppdu = ppdu;
|
|
NS_LOG_DEBUG("Starting " << type << " with power " << txPower << " 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);
|
|
}
|
|
|
|
MHz_u
|
|
PhyEntity::GetGuardBandwidth(MHz_u currentChannelWidth) const
|
|
{
|
|
return m_wifiPhy->GetGuardBandwidth(currentChannelWidth);
|
|
}
|
|
|
|
std::tuple<dBr_u, dBr_u, dBr_u>
|
|
PhyEntity::GetTxMaskRejectionParams() const
|
|
{
|
|
return m_wifiPhy->GetTxMaskRejectionParams();
|
|
}
|
|
|
|
Time
|
|
PhyEntity::CalculateTxDuration(const 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);
|
|
}
|
|
|
|
bool
|
|
PhyEntity::CanStartRx(Ptr<const WifiPpdu> ppdu) const
|
|
{
|
|
// The PHY shall not issue a PHY-RXSTART.indication primitive in response to a PPDU that does
|
|
// not overlap the primary channel
|
|
const auto channelWidth = m_wifiPhy->GetChannelWidth();
|
|
const auto primaryWidth = ((static_cast<uint16_t>(channelWidth) % 20 == 0)
|
|
? MHz_u{20}
|
|
: channelWidth); // if the channel width is a multiple of 20 MHz,
|
|
// then we consider the primary20 channel
|
|
const auto p20CenterFreq =
|
|
m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(primaryWidth);
|
|
const auto p20MinFreq = p20CenterFreq - (primaryWidth / 2);
|
|
const auto p20MaxFreq = p20CenterFreq + (primaryWidth / 2);
|
|
const auto txChannelWidth = (ppdu->GetTxChannelWidth() / ppdu->GetTxCenterFreqs().size());
|
|
for (auto txCenterFreq : ppdu->GetTxCenterFreqs())
|
|
{
|
|
const auto minTxFreq = txCenterFreq - txChannelWidth / 2;
|
|
const auto maxTxFreq = txCenterFreq + txChannelWidth / 2;
|
|
if ((p20MinFreq >= minTxFreq) && (p20MaxFreq <= maxTxFreq))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Ptr<const WifiPpdu>
|
|
PhyEntity::GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu)
|
|
{
|
|
return ppdu;
|
|
}
|
|
|
|
} // namespace ns3
|