diff --git a/src/wifi/model/he-phy.cc b/src/wifi/model/he-phy.cc index 0da23d546..6457cf547 100644 --- a/src/wifi/model/he-phy.cc +++ b/src/wifi/model/he-phy.cc @@ -26,11 +26,13 @@ #include "he-configuration.h" #include "wifi-net-device.h" #include "sta-wifi-mac.h" +#include "ap-wifi-mac.h" #include "wifi-utils.h" #include "ns3/uinteger.h" #include "ns3/simulator.h" #include "ns3/log.h" #include "ns3/assert.h" +#include namespace ns3 { @@ -245,7 +247,7 @@ HePhy::GetSigBDuration (WifiTxVector txVector) const } uint16_t -HePhy::ConvertHeTbPpduDurationToLSigLength (Time ppduDuration, WifiPhyBand band) const +HePhy::ConvertHeTbPpduDurationToLSigLength (Time ppduDuration, WifiPhyBand band) { uint8_t sigExtension = 0; if (band == WIFI_PHY_BAND_2_4GHZ) @@ -258,11 +260,11 @@ HePhy::ConvertHeTbPpduDurationToLSigLength (Time ppduDuration, WifiPhyBand band) } Time -HePhy::ConvertLSigLengthToHeTbPpduDuration (uint16_t length, WifiTxVector txVector, WifiPhyBand band) const +HePhy::ConvertLSigLengthToHeTbPpduDuration (uint16_t length, WifiTxVector txVector, WifiPhyBand band) { NS_ABORT_IF (txVector.GetPreambleType () != WIFI_PREAMBLE_HE_TB); Time tSymbol = NanoSeconds (12800 + txVector.GetGuardInterval ()); - Time preambleDuration = CalculatePhyPreambleAndHeaderDuration (txVector); + Time preambleDuration = WifiPhy::GetStaticPhyEntity (WIFI_MOD_CLASS_HE)->CalculatePhyPreambleAndHeaderDuration (txVector); //this is quite convoluted but only way of keeping the method static uint8_t sigExtension = 0; if (band == WIFI_PHY_BAND_2_4GHZ) { @@ -307,6 +309,144 @@ HePhy::BuildPpdu (const WifiConstPsduMap & psdus, WifiTxVector txVector, return Create (psdus, txVector, ppduDuration, band, uid); } +void +HePhy::StartReceivePreamble (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW, + Time rxDuration, TxPsdFlag psdFlag) +{ + NS_LOG_FUNCTION (this << ppdu << rxDuration << psdFlag); + WifiTxVector txVector = ppdu->GetTxVector (); + if (txVector.GetPreambleType () == WIFI_PREAMBLE_HE_TB + && psdFlag == PSD_HE_TB_OFDMA_PORTION) + { + if (m_currentHeTbPpduUid == ppdu->GetUid () + && GetCurrentEvent () != 0) + { + //AP or STA has already received non-OFDMA part, switch to OFDMA part, and schedule reception of payload (will be canceled for STAs by StartPayload) + bool ofdmaStarted = !m_beginOfdmaPayloadRxEvents.empty (); + NS_LOG_INFO ("Switch to OFDMA part (already started? " << (ofdmaStarted ? "Y" : "N") << ") " << + "and schedule OFDMA payload reception in " << GetDuration (WIFI_PPDU_FIELD_TRAINING, txVector).As (Time::NS)); + Ptr event = CreateInterferenceEvent (ppdu, txVector, rxDuration, rxPowersW, !ofdmaStarted); + uint16_t staId = GetStaId (ppdu); + NS_ASSERT (m_beginOfdmaPayloadRxEvents.find (staId) == m_beginOfdmaPayloadRxEvents.end ()); + m_beginOfdmaPayloadRxEvents[staId] = Simulator::Schedule (GetDuration (WIFI_PPDU_FIELD_TRAINING, txVector), + &HePhy::StartReceiveOfdmaPayload, this, event); + } + else + { + //PHY receives the OFDMA payload while having dropped the preamble + NS_LOG_INFO ("Consider OFDMA part of the HE TB PPDU as interference since device dropped the preamble"); + CreateInterferenceEvent (ppdu, txVector, rxDuration, rxPowersW); + //the OFDMA part of the HE TB PPDUs will be noise _after_ the completion of the current event + ErasePreambleEvent (ppdu, rxDuration); + } + } + else + { + PhyEntity::StartReceivePreamble (ppdu, rxPowersW, rxDuration, psdFlag); + } +} + +void +HePhy::CancelAllEvents (void) +{ + NS_LOG_FUNCTION (this); + for (auto & beginOfdmaPayloadRxEvent : m_beginOfdmaPayloadRxEvents) + { + beginOfdmaPayloadRxEvent.second.Cancel (); + } + m_beginOfdmaPayloadRxEvents.clear (); + PhyEntity::CancelAllEvents (); +} + +void +HePhy::DoAbortCurrentReception (WifiPhyRxfailureReason reason) +{ + NS_LOG_FUNCTION (this << reason); + if (reason != OBSS_PD_CCA_RESET) + { + for (auto & endMpduEvent : m_endOfMpduEvents) + { + endMpduEvent.Cancel (); + } + m_endOfMpduEvents.clear (); + } + else + { + PhyEntity::DoAbortCurrentReception (reason); + } +} + +void +HePhy::DoResetReceive (Ptr event) +{ + NS_LOG_FUNCTION (this << *event); + if (event->GetPpdu ()->GetType () != WIFI_PPDU_TYPE_UL_MU) + { + NS_ASSERT (event->GetEndTime () == Simulator::Now ()); + } + for (auto & beginOfdmaPayloadRxEvent : m_beginOfdmaPayloadRxEvents) + { + beginOfdmaPayloadRxEvent.second.Cancel (); + } + m_beginOfdmaPayloadRxEvents.clear (); +} + +Ptr +HePhy::DoGetEvent (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW) +{ + Ptr event; + //We store all incoming preamble events, and a decision is made at the end of the preamble detection window. + //If a preamble is received after the preamble detection window, it is stored anyway because this is needed for HE TB PPDUs in + //order to properly update the received power in InterferenceHelper. The map is cleaned anyway at the end of the current reception. + if (ppdu->GetType () == WIFI_PPDU_TYPE_UL_MU) + { + auto uidPreamblePair = std::make_pair (ppdu->GetUid (), ppdu->GetPreamble ()); + WifiTxVector txVector = ppdu->GetTxVector (); + Time rxDuration = CalculateNonOfdmaDurationForHeTb (txVector); //the OFDMA part of the transmission will be added later on + const auto & currentPreambleEvents = GetCurrentPreambleEvents (); + auto it = currentPreambleEvents.find (uidPreamblePair); + if (it != currentPreambleEvents.end ()) + { + NS_LOG_DEBUG ("Received another HE TB PPDU for UID " << ppdu->GetUid () << " from STA-ID " << ppdu->GetStaId () << " and BSS color " << +txVector.GetBssColor ()); + event = it->second; + if (Simulator::Now () - event->GetStartTime () > NanoSeconds (400)) + { + //Section 27.3.14.3 from 802.11ax Draft 4.0: Pre-correction accuracy requirements. + //A STA that transmits an HE TB PPDU, non-HT PPDU, or non-HT duplicate PPDU in response to a triggering PPDU + //shall ensure that the transmission start time of the HE TB PPDU, non-HT PPDU, or non-HT duplicate PPDU is + //within ±0.4 µs + 16 µs from the end, at the STA’s antenna connector, of the last OFDM symbol of the triggering + //PPDU (if it contains no PE field) or of the PE field of the triggering PPDU (if the PE field is present). + //As a result, if an HE TB PPDU arrives later than 0.4 µs, it is added as an interference but PPDU is dropped. + event = CreateInterferenceEvent (ppdu, txVector, rxDuration, rxPowersW); + NS_LOG_DEBUG ("Drop packet because not received within the 400ns window"); + m_wifiPhy->NotifyRxDrop (GetAddressedPsduInPpdu (ppdu), HE_TB_PPDU_TOO_LATE); + } + else + { + //Update received power of the event associated to that UL MU transmission + UpdateInterferenceEvent (event, rxPowersW); + } + if ((GetCurrentEvent () != 0) && (GetCurrentEvent ()->GetPpdu ()->GetUid () != ppdu->GetUid ())) + { + NS_LOG_DEBUG ("Drop packet because already receiving another HE TB PPDU"); + m_wifiPhy->NotifyRxDrop (GetAddressedPsduInPpdu (ppdu), RXING); + } + return nullptr; + } + else + { + NS_LOG_DEBUG ("Received a new HE TB PPDU for UID " << ppdu->GetUid () << " from STA-ID " << ppdu->GetStaId () << " and BSS color " << +txVector.GetBssColor ()); + event = CreateInterferenceEvent (ppdu, txVector, rxDuration, rxPowersW); + AddPreambleEvent (event); + } + } + else + { + event = PhyEntity::DoGetEvent (ppdu, rxPowersW); + } + return event; +} + Ptr HePhy::GetAddressedPsduInPpdu (Ptr ppdu) const { @@ -340,8 +480,23 @@ HePhy::GetBssColor (void) const uint16_t HePhy::GetStaId (const Ptr ppdu) const { - //TODO Move HE-specific logic here - return m_wifiPhy->GetStaId (ppdu); + if (ppdu->GetType () == WIFI_PPDU_TYPE_UL_MU) + { + return ppdu->GetStaId (); + } + else if (ppdu->GetType () == WIFI_PPDU_TYPE_DL_MU) + { + Ptr device = DynamicCast (m_wifiPhy->GetDevice ()); + if (device) + { + Ptr mac = DynamicCast (device->GetMac ()); + if (mac && mac->IsAssociated ()) + { + return mac->GetAssociationId (); + } + } + } + return PhyEntity::GetStaId (ppdu); } PhyEntity::PhyFieldRxStatus @@ -434,6 +589,159 @@ HePhy::IsConfigSupported (Ptr ppdu) const return true; } +void +HePhy::DoStartReceivePayload (Ptr event) +{ + NS_LOG_FUNCTION (this << *event); + WifiTxVector txVector = event->GetTxVector (); + Ptr ppdu = event->GetPpdu (); + if (txVector.GetPreambleType () == WIFI_PREAMBLE_HE_TB) + { + Ptr device = DynamicCast (m_wifiPhy->GetDevice ()); + bool isAp = device != 0 && (DynamicCast (device->GetMac ()) != 0); + if (!isAp) + { + NS_LOG_DEBUG ("Ignore HE TB PPDU payload received by STA but keep state in Rx"); + m_endRxPayloadEvents.push_back (Simulator::Schedule (ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (txVector), + &PhyEntity::ResetReceive, this, event)); + //Cancel all scheduled events for OFDMA payload reception + NS_ASSERT (!m_beginOfdmaPayloadRxEvents.empty () && m_beginOfdmaPayloadRxEvents.begin ()->second.IsRunning ()); + for (auto & beginOfdmaPayloadRxEvent : m_beginOfdmaPayloadRxEvents) + { + beginOfdmaPayloadRxEvent.second.Cancel (); + } + m_beginOfdmaPayloadRxEvents.clear (); + } + else + { + NS_LOG_DEBUG ("Receiving PSDU in HE TB PPDU"); + 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 ()}); + if (txVector.GetPreambleType () == WIFI_PREAMBLE_HE_TB) + { + //for HE TB PPDUs, ScheduleEndOfMpdus and EndReceive are scheduled by StartReceiveOfdmaPayload + NS_ASSERT (isAp); + NS_ASSERT (!m_beginOfdmaPayloadRxEvents.empty ()); + for (auto & beginOfdmaPayloadRxEvent : m_beginOfdmaPayloadRxEvents) + { + NS_ASSERT (beginOfdmaPayloadRxEvent.second.IsRunning ()); + } + } + } + } + else + { + PhyEntity::DoStartReceivePayload (event); + } +} + +void +HePhy::DoEndReceivePayload (Ptr ppdu) +{ + NS_LOG_FUNCTION (this << ppdu); + if (ppdu->GetType () == WIFI_PPDU_TYPE_UL_MU) + { + for (auto it = m_endRxPayloadEvents.begin (); it != m_endRxPayloadEvents.end (); ) + { + if (it->IsExpired ()) + { + it = m_endRxPayloadEvents.erase (it); + } + else + { + it++; + } + } + if (m_endRxPayloadEvents.empty ()) + { + //We've got the last PPDU of the UL-OFDMA transmission + NotifyInterferenceRxEndAndClear (true); //reset WifiPhy + } + } + else + { + NS_ASSERT (m_wifiPhy->GetLastRxEndTime () == Simulator::Now ()); + PhyEntity::DoEndReceivePayload (ppdu); + } +} + +void +HePhy::StartReceiveOfdmaPayload (Ptr event) +{ + Ptr ppdu = event->GetPpdu (); + RxPowerWattPerChannelBand rxPowersW = event->GetRxPowerWPerBand (); + //The total RX power corresponds to the maximum over all the bands + auto it = std::max_element (rxPowersW.begin (), rxPowersW.end (), + [] (const std::pair &p1, const std::pair &p2) { + return p1.second < p2.second; + }); + NS_LOG_FUNCTION (this << *event << it->second); + NS_ASSERT (GetCurrentEvent () != 0); + auto itEvent = m_beginOfdmaPayloadRxEvents.find (GetStaId (ppdu)); + /** + * m_beginOfdmaPayloadRxEvents should still be running only for APs, since canceled in StartReceivePayload for STAs. + * This is because SpectrumWifiPhy does not have access to the device type and thus blindly schedules things, letting + * the parent WifiPhy class take into account device type. + */ + NS_ASSERT (itEvent != m_beginOfdmaPayloadRxEvents.end () && itEvent->second.IsExpired ()); + m_beginOfdmaPayloadRxEvents.erase (itEvent); + + Time payloadDuration = ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (ppdu->GetTxVector ()); + Ptr psdu = GetAddressedPsduInPpdu (ppdu); + ScheduleEndOfMpdus (event); + m_endRxPayloadEvents.push_back (Simulator::Schedule (payloadDuration, &PhyEntity::EndReceivePayload, this, event)); + m_signalNoiseMap.insert ({std::make_pair (ppdu->GetUid (), ppdu->GetStaId ()), SignalNoiseDbm ()}); + m_statusPerMpduMap.insert ({std::make_pair (ppdu->GetUid (), ppdu->GetStaId ()), std::vector ()}); +} + +std::pair +HePhy::GetChannelWidthAndBand (WifiTxVector txVector, uint16_t staId) const +{ + if (txVector.IsMu ()) + { + return std::make_pair (HeRu::GetBandwidth (txVector.GetRu (staId).ruType), + GetRuBand (txVector, staId)); + } + else + { + return PhyEntity::GetChannelWidthAndBand (txVector, staId); + } +} + +WifiSpectrumBand +HePhy::GetRuBand (WifiTxVector txVector, uint16_t staId) const +{ + NS_ASSERT (txVector.IsMu ()); + WifiSpectrumBand band; + HeRu::RuSpec ru = txVector.GetRu (staId); + uint16_t channelWidth = txVector.GetChannelWidth (); + NS_ASSERT (channelWidth <= m_wifiPhy->GetChannelWidth ()); + HeRu::SubcarrierGroup group = HeRu::GetSubcarrierGroup (channelWidth, ru.ruType, ru.index); + HeRu::SubcarrierRange range = std::make_pair (group.front ().first, group.back ().second); + band = m_wifiPhy->ConvertHeRuSubcarriers (channelWidth, range); + return band; +} + +WifiSpectrumBand +HePhy::GetNonOfdmaBand (WifiTxVector txVector, uint16_t staId) const +{ + NS_ASSERT (txVector.GetPreambleType () == WIFI_PREAMBLE_HE_TB); + uint16_t channelWidth = txVector.GetChannelWidth (); + NS_ASSERT (channelWidth <= m_wifiPhy->GetChannelWidth ()); + + HeRu::RuSpec ru = txVector.GetRu (staId); + uint16_t ruWidth = HeRu::GetBandwidth (ru.ruType); + uint16_t nonOfdmaWidth = ruWidth < 20 ? 20 : ruWidth; + + // Find the RU that encompasses the non-OFDMA part of the HE TB PPDU for the STA-ID + HeRu::RuSpec nonOfdmaRu = HeRu::FindOverlappingRu (channelWidth, ru, HeRu::GetRuType (nonOfdmaWidth)); + + HeRu::SubcarrierGroup groupPreamble = HeRu::GetSubcarrierGroup (channelWidth, nonOfdmaRu.ruType, nonOfdmaRu.index); + HeRu::SubcarrierRange range = std::make_pair (groupPreamble.front ().first, groupPreamble.back ().second); + return m_wifiPhy->ConvertHeRuSubcarriers (channelWidth, range); +} + uint64_t HePhy::GetCurrentHeTbPpduUid (void) const { diff --git a/src/wifi/model/he-phy.h b/src/wifi/model/he-phy.h index 23eb7d446..80f56559f 100644 --- a/src/wifi/model/he-phy.h +++ b/src/wifi/model/he-phy.h @@ -74,28 +74,23 @@ public: virtual Ptr BuildPpdu (const WifiConstPsduMap & psdus, WifiTxVector txVector, Time ppduDuration, WifiPhyBand band, uint64_t uid) const override; Ptr GetAddressedPsduInPpdu (Ptr ppdu) const override; + void StartReceivePreamble (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW, + Time rxDuration, TxPsdFlag psdFlag) override; + void CancelAllEvents (void) override; + virtual uint16_t GetStaId (const Ptr ppdu) const override; /** * \return the BSS color of this PHY. */ uint8_t GetBssColor (void) const; - /** - * Return the STA ID that has been assigned to the station this PHY belongs to. - * This is typically called for MU PPDUs, in order to pick the correct PSDU. - * - * \param ppdu the PPDU for which the STA ID is requested - * \return the STA ID - */ - uint16_t GetStaId (const Ptr ppdu) const; - /** * \param ppduDuration the duration of the HE TB PPDU * \param band the frequency band being used * * \return the L-SIG length value corresponding to that HE TB PPDU duration. */ - uint16_t ConvertHeTbPpduDurationToLSigLength (Time ppduDuration, WifiPhyBand band) const; + static uint16_t ConvertHeTbPpduDurationToLSigLength (Time ppduDuration, WifiPhyBand band); /** * \param length the L-SIG length value * \param txVector the TXVECTOR used for the transmission of this HE TB PPDU @@ -103,7 +98,7 @@ public: * * \return the duration of the HE TB PPDU corresponding to that L-SIG length value. */ - Time ConvertLSigLengthToHeTbPpduDuration (uint16_t length, WifiTxVector txVector, WifiPhyBand band) const; + static Time ConvertLSigLengthToHeTbPpduDuration (uint16_t length, WifiTxVector txVector, WifiPhyBand band); /** * \param txVector the transmission parameters used for the HE TB PPDU * @@ -111,6 +106,25 @@ public: */ Time CalculateNonOfdmaDurationForHeTb (WifiTxVector txVector) const; + /** + * Get the RU band used to transmit a PSDU to a given STA in a HE MU PPDU + * + * \param txVector the TXVECTOR used for the transmission + * \param staId the STA-ID of the recipient + * + * \return the RU band used to transmit a PSDU to a given STA in a HE MU PPDU + */ + WifiSpectrumBand GetRuBand (WifiTxVector txVector, uint16_t staId) const; + /** + * Get the band used to transmit the non-OFDMA part of an HE TB PPDU. + * + * \param txVector the TXVECTOR used for the transmission + * \param staId the STA-ID of the station taking part of the UL MU + * + * \return the spectrum band used to transmit the non-OFDMA part of an HE TB PPDU + */ + WifiSpectrumBand GetNonOfdmaBand (WifiTxVector txVector, uint16_t staId) const; + /** * \return the UID of the HE TB PPDU being received */ @@ -206,10 +220,26 @@ protected: // Inherited PhyFieldRxStatus ProcessSigA (Ptr event, PhyFieldRxStatus status) override; PhyFieldRxStatus ProcessSigB (Ptr event, PhyFieldRxStatus status) override; + Ptr DoGetEvent (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW) override; virtual bool IsConfigSupported (Ptr ppdu) const override; + virtual void DoStartReceivePayload (Ptr event) override; + std::pair GetChannelWidthAndBand (WifiTxVector txVector, uint16_t staId) const override; + void DoEndReceivePayload (Ptr ppdu) override; + void DoResetReceive (Ptr event) override; + void DoAbortCurrentReception (WifiPhyRxfailureReason reason) override; + + /** + * Start receiving the PSDU (i.e. the first symbol of the PSDU has arrived) of an UL-OFDMA transmission. + * This function is called upon the RX event corresponding to the OFDMA part of the UL MU PPDU. + * + * \param event the event holding incoming OFDMA part of the PPDU's information + */ + void StartReceiveOfdmaPayload (Ptr event); uint64_t m_currentHeTbPpduUid; //!< UID of the HE TB PPDU being received + std::map m_beginOfdmaPayloadRxEvents; //!< the beginning of the OFDMA payload reception events (indexed by STA-ID) + private: // Inherited virtual void BuildModeList (void) override; diff --git a/src/wifi/model/phy-entity.cc b/src/wifi/model/phy-entity.cc index e3ae05c19..45da19860 100644 --- a/src/wifi/model/phy-entity.cc +++ b/src/wifi/model/phy-entity.cc @@ -24,7 +24,9 @@ #include "wifi-phy.h" #include "wifi-psdu.h" #include "preamble-detection-model.h" +#include "frame-capture-model.h" #include "wifi-utils.h" +#include "ns3/packet.h" #include "ns3/simulator.h" #include "ns3/log.h" #include "ns3/assert.h" @@ -68,7 +70,9 @@ std::ostream & operator << (std::ostream &os, const PhyEntity::PhyFieldRxStatus PhyEntity::~PhyEntity () { + NS_LOG_FUNCTION (this); m_modeList.clear (); + CancelAllEvents (); } void @@ -246,20 +250,12 @@ 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 () - || m_wifiPhy->m_currentPreambleEvents.size () > 1); //TODO find a better way of handling multiple preambles until synching on one after detection period - - //Handle special cases of preamble and data reception (TODO improve this logic later on) - if (field == WIFI_PPDU_FIELD_PREAMBLE) - { - DoStartReceivePreamble (event); - return; - } + 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) { - //TODO improve this logic later on - //Hand over to WifiPhy for data processing - m_wifiPhy->StartReceivePayload (event); + StartReceivePayload (event); return; } @@ -304,10 +300,11 @@ PhyEntity::EndReceiveField (WifiPpduField field, Ptr event) //Notify drop, keep in CCA busy, and perform same processing as IGNORE case m_wifiPhy->NotifyRxDrop (GetAddressedPsduInPpdu (ppdu), status.reason); m_state->SwitchMaybeToCcaBusy (GetRemainingDurationAfterField (ppdu, field)); //keep in CCA busy state till the end + //no break case IGNORE: //Keep in Rx state and reset at end - m_wifiPhy->m_endRxEvents.push_back (Simulator::Schedule (GetRemainingDurationAfterField (ppdu, field), - &PhyEntity::ResetReceive, this, event)); + m_endRxPayloadEvents.push_back (Simulator::Schedule (GetRemainingDurationAfterField (ppdu, field), + &PhyEntity::ResetReceive, this, event)); break; default: NS_FATAL_ERROR ("Unknown action in case of failure"); @@ -352,13 +349,423 @@ PhyEntity::DoEndReceiveField (WifiPpduField field, Ptr event) return PhyFieldRxStatus (false); //failed reception by default } -bool -PhyEntity::DoStartReceivePreamble (Ptr event) +void +PhyEntity::StartReceivePreamble (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW, + Time /* rxDuration */, TxPsdFlag /* psdFlag */) +{ + //The total RX power corresponds to the maximum over all the bands + auto it = std::max_element (rxPowersW.begin (), rxPowersW.end (), + [] (const std::pair &p1, const std::pair &p2) { + return p1.second < p2.second; + }); + NS_LOG_FUNCTION (this << ppdu << it->second); + WifiTxVector txVector = ppdu->GetTxVector (); + Time rxDuration = ppdu->GetTxDuration (); //the actual duration of the PPDU should be considered + + Ptr event = DoGetEvent (ppdu, rxPowersW); + if (event == nullptr) + { + //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->MaybeCcaBusyDuration (m_wifiPhy->GetMeasurementChannelWidth (nullptr)); + } + 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->MaybeCcaBusyDuration (m_wifiPhy->GetMeasurementChannelWidth (ppdu)); + } + 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, m_wifiPhy->GetMeasurementChannelWidth (ppdu)); + break; + case WifiPhyState::RX: + if (m_wifiPhy->m_frameCaptureModel != 0 + && 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, m_wifiPhy->GetMeasurementChannelWidth (ppdu)); + if (m_wifiPhy->m_currentEvent == 0) + { + /* + * 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, m_wifiPhy->GetMeasurementChannelWidth (ppdu)); + break; + case WifiPhyState::CCA_BUSY: + if (m_wifiPhy->m_currentEvent != 0) + { + if (m_wifiPhy->m_frameCaptureModel != 0 + && 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, m_wifiPhy->GetMeasurementChannelWidth (ppdu)); + } + } + else + { + StartPreambleDetectionPeriod (event); + } + break; + case WifiPhyState::IDLE: + NS_ASSERT (m_wifiPhy->m_currentEvent == 0); + StartPreambleDetectionPeriod (event); + break; + case WifiPhyState::SLEEP: + NS_LOG_DEBUG ("Drop packet because in sleep mode"); + DropPreambleEvent (ppdu, SLEEPING, endRx, m_wifiPhy->GetMeasurementChannelWidth (nullptr)); + break; + default: + NS_FATAL_ERROR ("Invalid WifiPhy state."); + break; + } +} + +void +PhyEntity::DropPreambleEvent (Ptr ppdu, WifiPhyRxfailureReason reason, Time endRx, uint16_t measurementChannelWidth) +{ + NS_LOG_FUNCTION (this << ppdu << reason << endRx << measurementChannelWidth); + 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 (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) + { + //that PPDU will be noise _after_ the end of the current event. + m_wifiPhy->MaybeCcaBusyDuration (measurementChannelWidth); + } +} + +void +PhyEntity::ErasePreambleEvent (Ptr 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 (m_wifiPhy->GetMeasurementChannelWidth (ppdu)); + } +} + +uint16_t +PhyEntity::GetStaId (const Ptr /* ppdu */) const +{ + return SU_STA_ID; +} + +void +PhyEntity::StartReceivePayload (Ptr event) { NS_LOG_FUNCTION (this << *event); - //TODO handle special cases in WifiPhy::StartReceivePreamble - StartPreambleDetectionPeriod (event); - return true; + NS_ASSERT (m_wifiPhy->m_endPhyRxEvent.IsExpired ()); + WifiTxVector txVector = event->GetTxVector (); + Time payloadDuration = event->GetPpdu ()->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (txVector); + + //TODO: Add method in WifiPhy to clear all other PHYs (since this one is starting Rx) + m_state->SwitchToRx (payloadDuration); + 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 + + DoStartReceivePayload (event); +} + +void +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 ({std::make_pair (ppdu->GetUid (), staId), SignalNoiseDbm ()}); + m_statusPerMpduMap.insert ({std::make_pair (ppdu->GetUid (), staId), std::vector ()}); + ScheduleEndOfMpdus (event); + m_endRxPayloadEvents.push_back (Simulator::Schedule (ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (event->GetTxVector ()), + &PhyEntity::EndReceivePayload, this, event)); +} + +void +PhyEntity::ScheduleEndOfMpdus (Ptr event) +{ + NS_LOG_FUNCTION (this << *event); + Ptr ppdu = event->GetPpdu (); + Ptr psdu = GetAddressedPsduInPpdu (ppdu); + 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 (*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, Ptr psdu, size_t mpduIndex, Time relativeStart, Time mpduDuration) +{ + NS_LOG_FUNCTION (this << *event << mpduIndex << relativeStart << mpduDuration); + Ptr ppdu = event->GetPpdu (); + WifiTxVector txVector = event->GetTxVector (); + 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); + + std::pair 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 = snr; + 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->ContinueRxNextMpdu (Copy (psdu), rxSignalInfo, txVector); + } +} + +void +PhyEntity::EndReceivePayload (Ptr event) +{ + Ptr 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 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 + m_state->SwitchFromRxEndOk (Copy (psdu), rxSignalInfo, txVector, staId, statusPerMpduIt->second); + m_wifiPhy->m_previouslyRxPpduUid = event->GetPpdu ()->GetUid (); //store UID only if reception is successful (because otherwise trigger won't be read by MAC layer) + } + else + { + m_state->SwitchFromRxEndError (Copy (psdu), snr); + } + + DoEndReceivePayload (ppdu); + m_wifiPhy->MaybeCcaBusyDuration (m_wifiPhy->GetMeasurementChannelWidth (ppdu)); +} + +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 = 0; + m_wifiPhy->m_currentPreambleEvents.clear (); + m_endRxPayloadEvents.clear (); +} + +std::pair +PhyEntity::GetReceptionStatus (Ptr psdu, Ptr 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 +PhyEntity::GetChannelWidthAndBand (WifiTxVector txVector, uint16_t /* staId */) const +{ + uint16_t channelWidth = std::min (m_wifiPhy->GetChannelWidth (), txVector.GetChannelWidth ()); + return std::make_pair (channelWidth, m_wifiPhy->GetBand (channelWidth)); +} + +const std::map , Ptr > & +PhyEntity::GetCurrentPreambleEvents (void) const +{ + return m_wifiPhy->m_currentPreambleEvents; +} + +void +PhyEntity::AddPreambleEvent (Ptr event) +{ + NS_LOG_FUNCTION (this << *event); + Ptr ppdu = event->GetPpdu (); + m_wifiPhy->m_currentPreambleEvents.insert ({std::make_pair (ppdu->GetUid (), ppdu->GetPreamble ()), event}); +} + +Ptr +PhyEntity::DoGetEvent (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW) +{ + Ptr 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 +PhyEntity::CreateInterferenceEvent (Ptr ppdu, 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, 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 @@ -375,8 +782,7 @@ PhyEntity::StartPreambleDetectionPeriod (Ptr event) NS_LOG_FUNCTION (this << *event); NS_LOG_DEBUG ("Sync to signal (power=" << GetRxPowerWForPpdu (event) << "W)"); m_wifiPhy->m_interference.NotifyRxStart (); //We need to notify it now so that it starts recording events - //TODO see if preamble detection events cannot be ported here - m_wifiPhy->m_endPreambleDetectionEvents.push_back (Simulator::Schedule (m_wifiPhy->GetPreambleDetectionDuration (), &PhyEntity::EndPreambleDetectionPeriod, this, event)); + m_endPreambleDetectionEvents.push_back (Simulator::Schedule (m_wifiPhy->GetPreambleDetectionDuration (), &PhyEntity::EndPreambleDetectionPeriod, this, event)); } void @@ -424,11 +830,11 @@ PhyEntity::EndPreambleDetectionPeriod (Ptr event) 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))) { - for (auto & endPreambleDetectionEvent : m_wifiPhy->m_endPreambleDetectionEvents) + //A bit convoluted but it enables to sync all PHYs + for (auto & it : m_wifiPhy->m_phyEntities) { - endPreambleDetectionEvent.Cancel (); + it.second->CancelRunningEndPreambleDetectionEvents (true); } - m_wifiPhy->m_endPreambleDetectionEvents.clear (); for (auto it = m_wifiPhy->m_currentPreambleEvents.begin (); it != m_wifiPhy->m_currentPreambleEvents.end (); ) { @@ -471,7 +877,7 @@ PhyEntity::EndPreambleDetectionPeriod (Ptr event) 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. - m_wifiPhy->DropPreambleEvent (m_wifiPhy->m_currentEvent->GetPpdu (), PREAMBLE_DETECT_FAILURE, m_wifiPhy->m_currentEvent->GetEndTime (), m_wifiPhy->GetMeasurementChannelWidth (m_wifiPhy->m_currentEvent->GetPpdu ())); + DropPreambleEvent (m_wifiPhy->m_currentEvent->GetPpdu (), PREAMBLE_DETECT_FAILURE, m_wifiPhy->m_currentEvent->GetEndTime (), m_wifiPhy->GetMeasurementChannelWidth (m_wifiPhy->m_currentEvent->GetPpdu ())); if (m_wifiPhy->m_currentPreambleEvents.empty ()) { //Do not erase events if there are still pending preamble events to be processed @@ -495,22 +901,84 @@ PhyEntity::IsConfigSupported (Ptr ppdu) const 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 (); +} + +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); - //TODO cancel some events here later on + 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 with FILTERED reason + { + for (auto & endMpduEvent : m_endOfMpduEvents) + { + endMpduEvent.Cancel (); + } + m_endOfMpduEvents.clear (); + } +} + void PhyEntity::ResetReceive (Ptr event) { NS_LOG_FUNCTION (this << *event); - NS_ASSERT (event->GetEndTime () == Simulator::Now ()); //TODO: overload for UL MU + DoResetReceive (event); + NS_ASSERT (m_endRxPayloadEvents.size () == 1 && m_endRxPayloadEvents.front ().IsExpired ()); + m_endRxPayloadEvents.clear (); m_wifiPhy->ResetReceive (event); } +void +PhyEntity::DoResetReceive (Ptr event) +{ + NS_LOG_FUNCTION (this << *event); + NS_ASSERT (event->GetEndTime () == Simulator::Now ()); +} + double PhyEntity::GetRandomValue (void) const { @@ -523,4 +991,10 @@ PhyEntity::GetRxPowerWForPpdu (Ptr event) const return event->GetRxPowerW (m_wifiPhy->GetBand (m_wifiPhy->GetMeasurementChannelWidth (event->GetPpdu ()))); } +Ptr +PhyEntity::GetCurrentEvent (void) const +{ + return m_wifiPhy->m_currentEvent; +} + } //namespace ns3 diff --git a/src/wifi/model/phy-entity.h b/src/wifi/model/phy-entity.h index 4fb22d750..7a80a1288 100644 --- a/src/wifi/model/phy-entity.h +++ b/src/wifi/model/phy-entity.h @@ -339,6 +339,19 @@ public: */ virtual Ptr GetAddressedPsduInPpdu (Ptr ppdu) const; + /** + * Start receiving the PHY preamble of a PPDU (i.e. the first bit of the preamble has arrived). + * + * This method triggers the start of the preamble detection period (\see + * StartPreambleDetectionPeriod) if the PHY can process the PPDU. + * + * \param ppdu the arriving PPDU + * \param rxPowersW the receive power in W per band + * \param rxDuration the duration of the PPDU + * \param psdFlag the flag indicating the type of Tx PSD to build + */ + virtual void StartReceivePreamble (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW, + Time rxDuration, TxPsdFlag psdFlag); /** * Start receiving a given field. * @@ -367,6 +380,44 @@ public: */ void EndReceiveField (WifiPpduField field, Ptr event); + /** + * The last symbol of the PPDU has arrived. + * + * \param event the event holding incoming PPDU's information + */ + void EndReceivePayload (Ptr event); + + /** + * Reset PHY at the end of the PPDU under reception after it has failed the PHY header. + * + * \param event the event holding incoming PPDU's information + */ + void ResetReceive (Ptr event); + + /** + * Cancel and clear all running events. + */ + virtual void CancelAllEvents (void); + /** + * \return \c true if there is no end preamble detection event running, \c false otherwise + */ + bool NoEndPreambleDetectionEvents (void) const; + /** + * Cancel and eventually clear all end preamble detection events. + * + * \param clear whether to clear the end preamble detection events' list + */ + void CancelRunningEndPreambleDetectionEvents (bool clear = false); + + /** + * Return the STA ID that has been assigned to the station this PHY belongs to. + * This is typically called for MU PPDUs, in order to pick the correct PSDU. + * + * \param ppdu the PPDU for which the STA ID is requested + * \return the STA ID + */ + virtual uint16_t GetStaId (const Ptr ppdu) const; + protected: /** * A map of PPDU field elements per preamble type. @@ -404,16 +455,16 @@ protected: virtual PhyFieldRxStatus DoEndReceiveField (WifiPpduField field, Ptr event); /** - * Start receiving the preamble, perform amendment-specific actions, and - * signify if it is supported. + * Get the event corresponding to the incoming PPDU. * - * This method triggers the start of the preamble detection period (\see - * StartPreambleDetectionPeriod). + * We store all incoming preamble events, perform amendment-specific actions, + * and a decision is made at the end of the preamble detection window. * - * \param event the event holding incoming PPDU's information - * \return \c true if the preamble is supported, \c false otherwise + * \param ppdu the incoming PPDU + * \param rxPowersW the receive power in W per band + * \return the event holding the incoming PPDU's information */ - virtual bool DoStartReceivePreamble (Ptr event); + virtual Ptr DoGetEvent (Ptr ppdu, RxPowerWattPerChannelBand rxPowersW); /** * End receiving the preamble, perform amendment-specific actions, and * provide the status of the reception. @@ -439,6 +490,37 @@ protected: */ void EndPreambleDetectionPeriod (Ptr event); + /** + * Start receiving the PSDU (i.e. the first symbol of the PSDU has arrived). + * + * \param event the event holding incoming PPDU's information + */ + void StartReceivePayload (Ptr event); + + /** + * Start receiving the PSDU (i.e. the first symbol of the PSDU has arrived) + * and perform amendment-specific actions. + * + * \param event the event holding incoming PPDU's information + */ + virtual void DoStartReceivePayload (Ptr event); + + /** + * Perform amendment-specific actions before resetting PHY at + * the end of the PPDU under reception after it has failed the PHY header. + * + * \param event the event holding incoming PPDU's information + */ + virtual void DoResetReceive (Ptr event); + + /** + * Perform amendment-specific actions before aborting the + * current reception. + * + * \param reason the reason the reception is aborted + */ + virtual void DoAbortCurrentReception (WifiPhyRxfailureReason reason); + /** * Checks if the signaled configuration (excluding bandwidth) * is supported by the PHY. @@ -448,18 +530,82 @@ protected: */ virtual bool IsConfigSupported (Ptr ppdu) const; + /** + * Drop the PPDU and the corresponding preamble detection event, but keep CCA busy + * state after the completion of the currently processed event. + * + * \param ppdu the incoming PPDU + * \param reason the reason the PPDU is dropped + * \param endRx the end of the incoming PPDU's reception + * \param measurementChannelWidth the measurement width (in MHz) to consider for the PPDU + */ + void DropPreambleEvent (Ptr ppdu, WifiPhyRxfailureReason reason, Time endRx, uint16_t measurementChannelWidth); + + /** + * Erase the event corresponding to the PPDU from the list of preamble events, + * but consider it as noise after the completion of the current event. + * + * \param ppdu the incoming PPDU + * \param rxDuration the duration of the PPDU + */ + void ErasePreambleEvent (Ptr ppdu, Time rxDuration); + + /** + * Get the reception status for the provided MPDU and notify. + * + * \param psdu the arriving MPDU formatted as a PSDU + * \param event the event holding incoming PPDU's information + * \param staId the station ID of the PSDU (only used for MU) + * \param relativeMpduStart the relative start time of the MPDU within the A-MPDU. 0 for normal MPDUs + * \param mpduDuration the duration of the MPDU + * + * \return information on MPDU reception: status, signal power (dBm), and noise power (in dBm) + */ + std::pair GetReceptionStatus (Ptr psdu, + Ptr event, uint16_t staId, + Time relativeMpduStart, + Time mpduDuration); + /** + * The last symbol of an MPDU in an A-MPDU has arrived. + * + * \param event the event holding incoming PPDU's information + * \param psdu the arriving MPDU formatted as a PSDU containing a normal MPDU + * \param mpduIndex the index of the MPDU within the A-MPDU + * \param relativeStart the relative start time of the MPDU within the A-MPDU. + * \param mpduDuration the duration of the MPDU + */ + void EndOfMpdu (Ptr event, Ptr psdu, size_t mpduIndex, Time relativeStart, Time mpduDuration); + + /** + * Schedule end of MPDUs events. + * + * \param event the event holding incoming PPDU's information + */ + void ScheduleEndOfMpdus (Ptr event); + + /** + * Perform amendment-specific actions at the end of the reception of + * the payload. + * + * \param ppdu the incoming PPDU + */ + virtual void DoEndReceivePayload (Ptr ppdu); + + /** + * Get the channel width and band to use (will be overloaded by child classes). + * + * \param txVector the transmission parameters + * \param staId the station ID of the PSDU + * \return a pair of channel width (MHz) and band + */ + virtual std::pair GetChannelWidthAndBand (WifiTxVector txVector, uint16_t staId) const; + /** * Abort the current reception. * * \param reason the reason the reception is aborted */ void AbortCurrentReception (WifiPhyRxfailureReason reason); - /** - * Reset PHY at the end of the PPDU under reception after it has failed the PHY header. - * - * \param event the event holding incoming PPDU's information - */ - void ResetReceive (Ptr event); /** * Obtain a random value from the WifiPhy's generator. @@ -485,11 +631,68 @@ protected: * \return the received power (W) for the event over a given band */ double GetRxPowerWForPpdu (Ptr event) const; + /** + * Get the pointer to the current event (stored in WifiPhy). + * Wrapper used by child classes. + * + * \return the pointer to the current event + */ + Ptr GetCurrentEvent (void) const; + /** + * Get the map of current preamble events (stored in WifiPhy). + * Wrapper used by child classes. + * + * \return the reference to the map of current preamble events + */ + const std::map , Ptr > & GetCurrentPreambleEvents (void) const; + /** + * Add an entry to the map of current preamble events (stored in WifiPhy). + * Wrapper used by child classes. + * + * \param event the event holding incoming PPDU's information + */ + void AddPreambleEvent (Ptr event); + + /** + * Create an event using WifiPhy's InterferenceHelper class. + * Wrapper used by child classes. + * + * \copydoc InterferenceHelper::Add(Ptr, WifiTxVector, Time, RxPowerWattPerChannelBand, bool) + */ + Ptr CreateInterferenceEvent (Ptr ppdu, WifiTxVector txVector, Time duration, RxPowerWattPerChannelBand rxPower, bool isStartOfdmaRxing = false); + /** + * Update an event in WifiPhy's InterferenceHelper class. + * Wrapper used by child classes. + * + * \copydoc InterferenceHelper::UpdateEvent(Ptr, RxPowerWattPerChannelBand) + */ + void UpdateInterferenceEvent (Ptr event, RxPowerWattPerChannelBand rxPower); + /** + * Notify WifiPhy's InterferenceHelper of the end of the reception, + * clear maps and end of MPDU event, and eventually reset WifiPhy. + * + * \param reset whether to reset WifiPhy + */ + void NotifyInterferenceRxEndAndClear (bool reset); Ptr m_wifiPhy; //!< Pointer to the owning WifiPhy Ptr m_state; //!< Pointer to WifiPhyStateHelper of the WifiPhy (to make it reachable for child classes) std::list m_modeList; //!< the list of supported modes + + std::vector m_endPreambleDetectionEvents; //!< the end of preamble detection events + std::vector m_endOfMpduEvents; //!< the end of MPDU events (only used for A-MPDUs) + + std::vector m_endRxPayloadEvents; //!< the end of receive events (only one unless UL MU reception) + + /** + * A pair of a UID and STA_ID + */ + typedef std::pair UidStaIdPair; + + std::map > m_statusPerMpduMap; //!< Map of the current reception status per MPDU that is filled in as long as MPDUs are being processed by the PHY in case of an A-MPDU + std::map m_signalNoiseMap; //!< Map of the latest signal power and noise power in dBm (noise power includes the noise figure) + }; //class PhyEntity /** diff --git a/src/wifi/model/wifi-phy.h b/src/wifi/model/wifi-phy.h index 0bc3be832..e1753499d 100644 --- a/src/wifi/model/wifi-phy.h +++ b/src/wifi/model/wifi-phy.h @@ -1061,6 +1061,15 @@ public: */ WifiSpectrumBand GetNonOfdmaBand (WifiTxVector txVector, uint16_t staId) const; + /** + * \param channelWidth the total channel width (MHz) used for the OFDMA transmission + * \param range the subcarrier range of the HE RU + * \return the converted subcarriers + * + * This is a helper function to convert HE RU subcarriers, which are relative to the center frequency subcarrier, to the indexes used by the Spectrum model. + */ + virtual WifiSpectrumBand ConvertHeRuSubcarriers (uint16_t channelWidth, HeRu::SubcarrierRange range) const; + /** * Add the PHY entity to the map of __implemented__ PHY entities for the * given modulation class. @@ -1132,7 +1141,6 @@ protected: * \param channelWidth the channel width in MHz used for RSSI measurement */ void SwitchMaybeToCcaBusy (uint16_t channelWidth); -public: //TODO find a better way (robust enough for OfdmaSpectrumWifiPhy overload) /** * Return the STA ID that has been assigned to the station this PHY belongs to. * This is typically called for MU PPDUs, in order to pick the correct PSDU. @@ -1141,7 +1149,6 @@ public: //TODO find a better way (robust enough for OfdmaSpectrumWifiPhy overloa * \return the STA ID */ virtual uint16_t GetStaId (const Ptr ppdu) const; -protected: /** * Return the channel width used to measure the RSSI. * This corresponds to the primary channel unless it corresponds to the @@ -1162,15 +1169,6 @@ protected: */ virtual WifiSpectrumBand GetBand (uint16_t bandWidth, uint8_t bandIndex = 0); - /** - * \param channelWidth the total channel width (MHz) used for the OFDMA transmission - * \param range the subcarrier range of the HE RU - * \return the converted subcarriers - * - * This is a helper function to convert HE RU subcarriers, which are relative to the center frequency subcarrier, to the indexes used by the Spectrum model. - */ - virtual WifiSpectrumBand ConvertHeRuSubcarriers (uint16_t channelWidth, HeRu::SubcarrierRange range) const; - /** * Add the PHY entity to the map of supported PHY entities for the * given modulation class for the WifiPhy instance.