/* * Copyright (c) 2020 Orange Labs * * SPDX-License-Identifier: GPL-2.0-only * * Authors: Rediet * Sébastien Deronne (for logic ported from wifi-phy and * spectrum-wifi-phy) * Mathieu Lacage (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 #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) { 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::const_iterator PhyEntity::begin() const { return m_modeList.begin(); } std::list::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(field), txVector); } return duration; } WifiConstPsduMap PhyEntity::GetWifiConstPsduMap(Ptr psdu, const WifiTxVector& txVector) const { return WifiConstPsduMap({{SU_STA_ID, psdu}}); } Ptr PhyEntity::GetAddressedPsduInPpdu(Ptr 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 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(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) 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) { 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) { 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 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 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) { 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) { 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 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 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 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 /* ppdu */) const { return SU_STA_ID; } void PhyEntity::StartReceivePayload(Ptr 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) { NS_LOG_FUNCTION(this << *event); Ptr 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()}); 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) { NS_LOG_FUNCTION(this << *event); Ptr ppdu = event->GetPpdu(); Ptr 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, Ptr 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 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(mpdu, false), rxSignalInfo, txVector); } } void PhyEntity::EndReceivePayload(Ptr 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 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 psdu, RxSignalInfo rxSignalInfo, const WifiTxVector& txVector, uint16_t staId, const std::vector& statusPerMpdu) { NS_LOG_FUNCTION(this << *psdu << txVector); m_state->SwitchFromRxEndOk(); } void PhyEntity::RxPayloadFailed(Ptr psdu, double snr, const WifiTxVector& txVector) { NS_LOG_FUNCTION(this << *psdu << txVector << snr); m_state->SwitchFromRxEndError(txVector); } void PhyEntity::DoEndReceivePayload(Ptr 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 PhyEntity::GetReceptionStatus(Ptr mpdu, Ptr 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