wifi: PHY logic changes to handle HE TB PPDUs reception
This commit is contained in:
@@ -181,10 +181,10 @@ InterferenceHelper::~InterferenceHelper ()
|
||||
}
|
||||
|
||||
Ptr<Event>
|
||||
InterferenceHelper::Add (Ptr<const WifiPpdu> ppdu, WifiTxVector txVector, Time duration, RxPowerWattPerChannelBand rxPowerW)
|
||||
InterferenceHelper::Add (Ptr<const WifiPpdu> ppdu, WifiTxVector txVector, Time duration, RxPowerWattPerChannelBand rxPowerW, bool isStartOfdmaRxing)
|
||||
{
|
||||
Ptr<Event> event = Create<Event> (ppdu, txVector, duration, rxPowerW);
|
||||
AppendEvent (event);
|
||||
AppendEvent (event, isStartOfdmaRxing);
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -265,9 +265,9 @@ InterferenceHelper::GetEnergyDuration (double energyW, WifiSpectrumBand band)
|
||||
}
|
||||
|
||||
void
|
||||
InterferenceHelper::AppendEvent (Ptr<Event> event)
|
||||
InterferenceHelper::AppendEvent (Ptr<Event> event, bool isStartOfdmaRxing)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << event);
|
||||
NS_LOG_FUNCTION (this << event << isStartOfdmaRxing);
|
||||
RxPowerWattPerChannelBand rxPowerWattPerChannelBand = event->GetRxPowerWPerBand ();
|
||||
for (auto const& it : rxPowerWattPerChannelBand)
|
||||
{
|
||||
@@ -284,6 +284,13 @@ InterferenceHelper::AppendEvent (Ptr<Event> event)
|
||||
// Always leave the first zero power noise event in the list
|
||||
ni_it->second.erase (++(ni_it->second.begin ()), GetNextPosition (event->GetStartTime (), band));
|
||||
}
|
||||
else if (isStartOfdmaRxing)
|
||||
{
|
||||
//When the first UL-OFDMA payload is received, we need to set m_firstPowerPerBand
|
||||
//so that it takes into account interferences that arrived between the start of the
|
||||
//UL MU transmission and the start of UL-OFDMA payload.
|
||||
m_firstPowerPerBand.find (band)->second = previousPowerStart;
|
||||
}
|
||||
auto first = AddNiChangeEvent (event->GetStartTime (), NiChange (previousPowerStart, event), band);
|
||||
auto last = AddNiChangeEvent (event->GetEndTime (), NiChange (previousPowerEnd, event), band);
|
||||
for (auto i = first; i != last; ++i)
|
||||
@@ -406,10 +413,19 @@ InterferenceHelper::CalculatePayloadPer (Ptr<const Event> event, uint16_t channe
|
||||
Time previous = j->first;
|
||||
WifiMode payloadMode = txVector.GetMode (staId);
|
||||
WifiPreamble preamble = txVector.GetPreambleType ();
|
||||
Time phyHeaderStart = j->first + WifiPhy::GetPhyPreambleDuration (txVector); //PPDU start time + preamble
|
||||
Time phyHtSigHeaderStart = phyHeaderStart + WifiPhy::GetPhyHeaderDuration (txVector); //PPDU start time + preamble + L-SIG
|
||||
Time phyTrainingSymbolsStart = phyHtSigHeaderStart + WifiPhy::GetPhyHtSigHeaderDuration (preamble) + WifiPhy::GetPhySigA1Duration (preamble) + WifiPhy::GetPhySigA2Duration (preamble); //PPDU start time + preamble + L-SIG + HT-SIG or SIG-A
|
||||
Time phyPayloadStart = phyTrainingSymbolsStart + WifiPhy::GetPhyTrainingSymbolDuration (txVector) + WifiPhy::GetPhySigBDuration (txVector); //PPDU start time + preamble + L-SIG + HT-SIG or SIG-A + Training + SIG-B
|
||||
Time phyPayloadStart;
|
||||
if (!event->GetPpdu ()->IsUlMu ())
|
||||
{
|
||||
Time phyHeaderStart = j->first + WifiPhy::GetPhyPreambleDuration (txVector); //PPDU start time + preamble
|
||||
Time phyHtSigHeaderStart = phyHeaderStart + WifiPhy::GetPhyHeaderDuration (txVector); //PPDU start time + preamble + L-SIG
|
||||
Time phyTrainingSymbolsStart = phyHtSigHeaderStart + WifiPhy::GetPhyHtSigHeaderDuration (preamble) + WifiPhy::GetPhySigA1Duration (preamble) + WifiPhy::GetPhySigA2Duration (preamble); //PPDU start time + preamble + L-SIG + HT-SIG or SIG-A
|
||||
phyPayloadStart = phyTrainingSymbolsStart + WifiPhy::GetPhyTrainingSymbolDuration (txVector) + WifiPhy::GetPhySigBDuration (txVector); //PPDU start time + preamble + L-SIG + HT-SIG or SIG-A + Training + SIG-B
|
||||
}
|
||||
else
|
||||
{
|
||||
//j->first corresponds to the start of the UL-OFDMA payload
|
||||
phyPayloadStart = j->first;
|
||||
}
|
||||
Time windowStart = phyPayloadStart + window.first;
|
||||
Time windowEnd = phyPayloadStart + window.second;
|
||||
double noiseInterferenceW = m_firstPowerPerBand.find (band)->second;
|
||||
|
||||
@@ -211,10 +211,11 @@ public:
|
||||
* \param txVector the TXVECTOR
|
||||
* \param duration the PPDU duration
|
||||
* \param rxPower received power per band (W)
|
||||
* \param isStartOfdmaRxing flag whether the event corresponds to the start of the OFDMA payload reception (only used for UL-OFDMA)
|
||||
*
|
||||
* \return Event
|
||||
*/
|
||||
Ptr<Event> Add (Ptr<const WifiPpdu> ppdu, WifiTxVector txVector, Time duration, RxPowerWattPerChannelBand rxPower);
|
||||
Ptr<Event> Add (Ptr<const WifiPpdu> ppdu, WifiTxVector txVector, Time duration, RxPowerWattPerChannelBand rxPower, bool isStartOfdmaRxing = false);
|
||||
|
||||
/**
|
||||
* Add a non-Wifi signal to interference helper.
|
||||
@@ -373,9 +374,10 @@ private:
|
||||
/**
|
||||
* Append the given Event.
|
||||
*
|
||||
* \param event
|
||||
* \param event the event to be appended
|
||||
* \param isStartOfdmaRxing flag whether event corresponds to the start of the OFDMA payload reception (only used for UL-OFDMA)
|
||||
*/
|
||||
void AppendEvent (Ptr<Event> event);
|
||||
void AppendEvent (Ptr<Event> event, bool isStartOfdmaRxing);
|
||||
|
||||
/**
|
||||
* Calculate noise and interference power in W.
|
||||
|
||||
@@ -34,8 +34,6 @@
|
||||
#include "wifi-utils.h"
|
||||
#include "wifi-ppdu.h"
|
||||
#include "wifi-psdu.h"
|
||||
#include "ap-wifi-mac.h"
|
||||
#include "wifi-net-device.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
@@ -360,19 +358,33 @@ SpectrumWifiPhy::StartRx (Ptr<SpectrumSignalParameters> rxParams)
|
||||
NS_LOG_INFO ("Received Wi-Fi signal");
|
||||
Ptr<WifiPpdu> ppdu = Copy (wifiRxParams->ppdu);
|
||||
if (ppdu->GetTxVector ().GetPreambleType () == WIFI_PREAMBLE_HE_TB
|
||||
&& wifiRxParams->txPsdFlag == PSD_HE_TB_OFDMA_PORTION
|
||||
&& m_currentHeTbPpduUid == ppdu->GetUid ())
|
||||
{
|
||||
Ptr<WifiNetDevice> device = DynamicCast<WifiNetDevice> (GetDevice ());
|
||||
bool isAp = (DynamicCast<ApWifiMac> (device->GetMac ()) != 0);
|
||||
if (isAp)
|
||||
&& wifiRxParams->txPsdFlag == PSD_HE_TB_OFDMA_PORTION)
|
||||
{
|
||||
if (m_currentHeTbPpduUid == ppdu->GetUid () && m_currentEvent != 0)
|
||||
{
|
||||
//AP and STA already received non-OFDMA part, handle OFDMA payload reception
|
||||
StartReceiveOfdmaPayload (ppdu, rxPowerW);
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_LOG_INFO ("Ignore UL-OFDMA since device is not AP");
|
||||
m_currentPreambleEvents.clear ();
|
||||
//PHY receives the OFDMA payload while having dropped the preamble
|
||||
NS_LOG_INFO ("Consider UL-OFDMA part of the HE TB PPDU as interference since device dropped the preamble");
|
||||
m_interference.Add (ppdu, ppdu->GetTxVector (), rxDuration, rxPowerW);
|
||||
auto it = m_currentPreambleEvents.find (std::make_pair(ppdu->GetUid (), ppdu->GetPreamble ()));
|
||||
if (it != m_currentPreambleEvents.end ())
|
||||
{
|
||||
m_currentPreambleEvents.erase (it);
|
||||
}
|
||||
if (m_currentPreambleEvents.empty ())
|
||||
{
|
||||
Reset ();
|
||||
}
|
||||
|
||||
if (rxDuration > m_state->GetDelayUntilIdle ())
|
||||
{
|
||||
//that packet will be noise _after_ the completion of the OFDMA part of the HE TB PPDUs
|
||||
SwitchMaybeToCcaBusy ();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -477,10 +477,11 @@ WifiPhyStateHelper::ContinueRxNextMpdu (Ptr<WifiPsdu> psdu, double snr, WifiTxVe
|
||||
void
|
||||
WifiPhyStateHelper::SwitchFromRxEndOk (Ptr<WifiPsdu> psdu, double snr, WifiTxVector txVector, uint16_t staId, std::vector<bool> statusPerMpdu)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << *psdu << snr << txVector << statusPerMpdu.size () <<
|
||||
NS_LOG_FUNCTION (this << *psdu << snr << txVector << staId << statusPerMpdu.size () <<
|
||||
std::all_of(statusPerMpdu.begin(), statusPerMpdu.end(), [](bool v) { return v; })); //returns true if all true
|
||||
NS_ASSERT (statusPerMpdu.size () != 0);
|
||||
NS_ASSERT (m_endRx == Simulator::Now ());
|
||||
NS_ASSERT (Abs (m_endRx - Simulator::Now ()) < MicroSeconds (1)); //1us corresponds to the maximum propagation delay (delay spread)
|
||||
//TODO: a better fix would be to call the function once all HE TB PPDUs are received
|
||||
m_rxOkTrace (psdu->GetPacket (), snr, txVector.GetMode (staId), txVector.GetPreambleType ());
|
||||
NotifyRxEndOk ();
|
||||
DoSwitchFromRx ();
|
||||
@@ -494,7 +495,8 @@ void
|
||||
WifiPhyStateHelper::SwitchFromRxEndError (Ptr<WifiPsdu> psdu, double snr)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << *psdu << snr);
|
||||
NS_ASSERT (m_endRx == Simulator::Now ());
|
||||
NS_ASSERT (Abs (m_endRx - Simulator::Now ()) < MicroSeconds (1)); //1us corresponds to the maximum propagation delay (delay spread)
|
||||
//TODO: a better fix would be to call the function once all HE TB PPDUs are received
|
||||
m_rxErrorTrace (psdu->GetPacket (), snr);
|
||||
NotifyRxEndError ();
|
||||
DoSwitchFromRx ();
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "he-configuration.h"
|
||||
#include "mpdu-aggregator.h"
|
||||
#include "wifi-psdu.h"
|
||||
#include "ap-wifi-mac.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
@@ -508,9 +509,9 @@ WifiPhy::GetTypeId (void)
|
||||
WifiPhy::WifiPhy ()
|
||||
: m_txMpduReferenceNumber (0xffffffff),
|
||||
m_rxMpduReferenceNumber (0xffffffff),
|
||||
m_endRxEvent (),
|
||||
m_endPhyRxEvent (),
|
||||
m_endTxEvent (),
|
||||
m_currentEvent (0),
|
||||
m_previouslyRxPpduUid (UINT64_MAX),
|
||||
m_currentHeTbPpduUid (UINT64_MAX),
|
||||
m_standard (WIFI_PHY_STANDARD_UNSPECIFIED),
|
||||
@@ -531,9 +532,9 @@ WifiPhy::WifiPhy ()
|
||||
m_rxSpatialStreams (0),
|
||||
m_channelNumber (0),
|
||||
m_initialChannelNumber (0),
|
||||
m_currentEvent (0),
|
||||
m_wifiRadioEnergyModel (0),
|
||||
m_timeLastPreambleDetected (Seconds (0))
|
||||
m_timeLastPreambleDetected (Seconds (0)),
|
||||
m_ofdmaStarted (false)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
m_random = CreateObject<UniformRandomVariable> ();
|
||||
@@ -550,8 +551,12 @@ WifiPhy::DoDispose (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
m_endTxEvent.Cancel ();
|
||||
m_endRxEvent.Cancel ();
|
||||
m_endPhyRxEvent.Cancel ();
|
||||
for (auto & endRxEvent : m_endRxEvents)
|
||||
{
|
||||
endRxEvent.Cancel ();
|
||||
}
|
||||
m_endRxEvents.clear ();
|
||||
for (auto & endPreambleDetectionEvent : m_endPreambleDetectionEvents)
|
||||
{
|
||||
endPreambleDetectionEvent.Cancel ();
|
||||
@@ -1697,7 +1702,11 @@ WifiPhy::DoChannelSwitch (uint8_t nch)
|
||||
case WifiPhyState::RX:
|
||||
NS_LOG_DEBUG ("drop packet because of channel switching while reception");
|
||||
m_endPhyRxEvent.Cancel ();
|
||||
m_endRxEvent.Cancel ();
|
||||
for (auto & endRxEvent : m_endRxEvents)
|
||||
{
|
||||
endRxEvent.Cancel ();
|
||||
}
|
||||
m_endRxEvents.clear ();
|
||||
for (auto & endPreambleDetectionEvent : m_endPreambleDetectionEvents)
|
||||
{
|
||||
endPreambleDetectionEvent.Cancel ();
|
||||
@@ -1717,7 +1726,11 @@ WifiPhy::DoChannelSwitch (uint8_t nch)
|
||||
endPreambleDetectionEvent.Cancel ();
|
||||
}
|
||||
m_endPreambleDetectionEvents.clear ();
|
||||
m_endRxEvent.Cancel ();
|
||||
for (auto & endRxEvent : m_endRxEvents)
|
||||
{
|
||||
endRxEvent.Cancel ();
|
||||
}
|
||||
m_endRxEvents.clear ();
|
||||
}
|
||||
goto switchChannel;
|
||||
break;
|
||||
@@ -1766,7 +1779,11 @@ WifiPhy::DoFrequencySwitch (uint16_t frequency)
|
||||
case WifiPhyState::RX:
|
||||
NS_LOG_DEBUG ("drop packet because of channel/frequency switching while reception");
|
||||
m_endPhyRxEvent.Cancel ();
|
||||
m_endRxEvent.Cancel ();
|
||||
for (auto & endRxEvent : m_endRxEvents)
|
||||
{
|
||||
endRxEvent.Cancel ();
|
||||
}
|
||||
m_endRxEvents.clear ();
|
||||
for (auto & endPreambleDetectionEvent : m_endPreambleDetectionEvents)
|
||||
{
|
||||
endPreambleDetectionEvent.Cancel ();
|
||||
@@ -1785,7 +1802,11 @@ WifiPhy::DoFrequencySwitch (uint16_t frequency)
|
||||
endPreambleDetectionEvent.Cancel ();
|
||||
}
|
||||
m_endPreambleDetectionEvents.clear ();
|
||||
m_endRxEvent.Cancel ();
|
||||
for (auto & endRxEvent : m_endRxEvents)
|
||||
{
|
||||
endRxEvent.Cancel ();
|
||||
}
|
||||
m_endRxEvents.clear ();
|
||||
goto switchFrequency;
|
||||
break;
|
||||
case WifiPhyState::SLEEP:
|
||||
@@ -1854,8 +1875,12 @@ WifiPhy::SetOffMode (void)
|
||||
m_powerRestricted = false;
|
||||
m_channelAccessRequested = false;
|
||||
m_endPhyRxEvent.Cancel ();
|
||||
m_endRxEvent.Cancel ();
|
||||
m_endTxEvent.Cancel ();
|
||||
for (auto & endRxEvent : m_endRxEvents)
|
||||
{
|
||||
endRxEvent.Cancel ();
|
||||
}
|
||||
m_endRxEvents.clear ();
|
||||
for (auto & endPreambleDetectionEvent : m_endPreambleDetectionEvents)
|
||||
{
|
||||
endPreambleDetectionEvent.Cancel ();
|
||||
@@ -2936,6 +2961,7 @@ WifiPhy::Reset (void)
|
||||
NS_LOG_FUNCTION (this);
|
||||
m_currentPreambleEvents.clear ();
|
||||
m_currentEvent = 0;
|
||||
m_ofdmaStarted = false;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2945,10 +2971,12 @@ WifiPhy::StartReceiveHeader (Ptr<Event> event)
|
||||
NS_ASSERT (!IsStateRx ());
|
||||
NS_ASSERT (m_endPhyRxEvent.IsExpired ());
|
||||
|
||||
//calculate PER on the 20 MHz primary channel for PHY headers
|
||||
uint16_t primaryChannelWidth = GetChannelWidth () >= 40 ? 20 : GetChannelWidth ();
|
||||
auto primaryBand = GetBand (primaryChannelWidth);
|
||||
double maxRxPowerW = 0;
|
||||
Ptr<Event> maxEvent;
|
||||
NS_ASSERT (!m_currentPreambleEvents.empty ());
|
||||
for (auto preambleEvent : m_currentPreambleEvents)
|
||||
{
|
||||
double rxPowerW = preambleEvent.second->GetRxPowerW (primaryBand);
|
||||
@@ -2974,24 +3002,13 @@ WifiPhy::StartReceiveHeader (Ptr<Event> event)
|
||||
}
|
||||
|
||||
m_currentEvent = event;
|
||||
|
||||
uint16_t channelWidth;
|
||||
if (m_currentEvent->GetTxVector ().GetChannelWidth () >= 40)
|
||||
{
|
||||
channelWidth = 20; //calculate PER on the 20 MHz primary channel for PHY headers
|
||||
}
|
||||
else
|
||||
{
|
||||
channelWidth = m_currentEvent->GetTxVector ().GetChannelWidth ();
|
||||
}
|
||||
auto band = GetBand (channelWidth);
|
||||
InterferenceHelper::SnrPer snrPer = m_interference.CalculateNonHtPhyHeaderSnrPer (m_currentEvent, band);
|
||||
double snr = snrPer.snr;
|
||||
NS_LOG_DEBUG ("snr(dB)=" << RatioToDb (snrPer.snr));
|
||||
|
||||
double snr = m_interference.CalculateSnr (m_currentEvent, primaryChannelWidth, 1, primaryBand);
|
||||
NS_LOG_DEBUG ("SNR(dB)=" << RatioToDb (snr) << " at start of legacy PHY header");
|
||||
|
||||
Time headerPayloadDuration = m_currentEvent->GetStartTime () + m_currentEvent->GetPpdu ()->GetTxDuration () - Simulator::Now ();
|
||||
|
||||
if (!m_preambleDetectionModel || (m_preambleDetectionModel->IsPreambleDetected (m_currentEvent->GetRxPowerW (band), snr, m_channelWidth)))
|
||||
if (!m_preambleDetectionModel || (m_preambleDetectionModel->IsPreambleDetected (m_currentEvent->GetRxPowerW (primaryBand), snr, primaryChannelWidth)))
|
||||
{
|
||||
for (auto & endPreambleDetectionEvent : m_endPreambleDetectionEvents)
|
||||
{
|
||||
@@ -3079,7 +3096,7 @@ WifiPhy::ContinueReceiveHeader (Ptr<Event> event)
|
||||
}
|
||||
InterferenceHelper::SnrPer snrPer = m_interference.CalculateNonHtPhyHeaderSnrPer (event, GetBand (channelWidth));
|
||||
|
||||
NS_LOG_DEBUG ("snr(dB)=" << RatioToDb (snrPer.snr) << ", per=" << snrPer.per);
|
||||
NS_LOG_DEBUG ("SNR(dB)=" << RatioToDb (snrPer.snr) << ", PER=" << snrPer.per);
|
||||
if (m_random->GetValue () > snrPer.per) //non-HT PHY header reception succeeded
|
||||
{
|
||||
NS_LOG_DEBUG ("Received non-HT PHY header");
|
||||
@@ -3303,13 +3320,28 @@ WifiPhy::StartReceiveOfdmaPayload (Ptr<WifiPpdu> ppdu, RxPowerWattPerChannelBand
|
||||
return p1.second < p2.second;
|
||||
});
|
||||
NS_LOG_FUNCTION (this << *ppdu << it->second);
|
||||
|
||||
WifiTxVector txVector = ppdu->GetTxVector ();
|
||||
Time payloadDuration = ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (txVector);
|
||||
Ptr<Event> event = m_interference.Add (ppdu, txVector, payloadDuration, rxPowersW);
|
||||
|
||||
NS_ASSERT (m_endRxEvent.IsExpired ());
|
||||
m_endRxEvent = Simulator::Schedule (payloadDuration, &WifiPhy::EndReceive, this, event);
|
||||
Ptr<Event> event = m_interference.Add (ppdu, txVector, payloadDuration, rxPowersW, !m_ofdmaStarted);
|
||||
m_ofdmaStarted = true;
|
||||
Ptr<WifiNetDevice> device = DynamicCast<WifiNetDevice> (GetDevice ());
|
||||
bool isAp = (DynamicCast<ApWifiMac> (device->GetMac ()) != 0);
|
||||
if (isAp)
|
||||
{
|
||||
Ptr<const WifiPsdu> psdu = GetAddressedPsduInPpdu (ppdu);
|
||||
if (psdu->GetNMpdus () > 1)
|
||||
{
|
||||
ScheduleEndOfMpdus (event);
|
||||
}
|
||||
m_endRxEvents.push_back (Simulator::Schedule (payloadDuration, &WifiPhy::EndReceive, 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<bool> ()});
|
||||
}
|
||||
else
|
||||
{
|
||||
//Don't do anything special for STAs since ResetReceive has already been scheduled by StartReceivePayload
|
||||
NS_ASSERT (m_endRxEvents.size () == 1 && m_endRxEvents.front ().IsRunning ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3317,17 +3349,17 @@ WifiPhy::StartReceivePayload (Ptr<Event> event)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << *event);
|
||||
NS_ASSERT (m_endPhyRxEvent.IsExpired ());
|
||||
NS_ASSERT (m_endRxEvent.IsExpired ());
|
||||
bool canReceivePayload = false;
|
||||
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
|
||||
WifiModulationClass modulation = ppdu->GetModulation ();
|
||||
//calculate PER on the primary 20 MHz channel for PHY headers
|
||||
uint16_t primaryChannelWidth = std::min (event->GetTxVector ().GetChannelWidth (), static_cast<uint16_t> (20));
|
||||
auto primaryBand = GetBand (primaryChannelWidth);
|
||||
|
||||
if (modulation >= WIFI_MOD_CLASS_HT)
|
||||
{
|
||||
InterferenceHelper::SnrPer snrPer = m_interference.CalculateHtPhyHeaderSnrPer (event, primaryBand);
|
||||
NS_LOG_DEBUG ("snr(dB)=" << RatioToDb (snrPer.snr) << ", per=" << snrPer.per);
|
||||
NS_LOG_DEBUG ("SNR(dB)=" << RatioToDb (snrPer.snr) << ", PER=" << snrPer.per);
|
||||
canReceivePayload = (m_random->GetValue () > snrPer.per);
|
||||
}
|
||||
else
|
||||
@@ -3357,6 +3389,7 @@ WifiPhy::StartReceivePayload (Ptr<Event> event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nss > GetMaxSupportedRxSpatialStreams ())
|
||||
{
|
||||
NS_LOG_DEBUG ("Packet reception could not be started because not enough RX antennas");
|
||||
@@ -3369,23 +3402,37 @@ WifiPhy::StartReceivePayload (Ptr<Event> event)
|
||||
}
|
||||
else if (IsModeSupported (txMode) || IsMcsSupported (txMode))
|
||||
{
|
||||
m_statusPerMpdu.clear();
|
||||
if (psdu->GetNMpdus () > 1)
|
||||
{
|
||||
ScheduleEndOfMpdus (event);
|
||||
}
|
||||
m_state->SwitchToRx (payloadDuration);
|
||||
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
|
||||
success = true;
|
||||
NS_LOG_DEBUG ("Receiving PSDU");
|
||||
if (txVector.GetPreambleType () == WIFI_PREAMBLE_HE_TB)
|
||||
|
||||
Ptr<WifiNetDevice> device = DynamicCast<WifiNetDevice> (GetDevice ());
|
||||
bool isAp = device != 0 && (DynamicCast<ApWifiMac> (device->GetMac ()) != 0);
|
||||
if (txVector.GetPreambleType () == WIFI_PREAMBLE_HE_TB && !isAp)
|
||||
{
|
||||
NS_LOG_DEBUG ("Ignore UL-OFDMA (OFDMA part of HE TB PPDU) received by STA but keep state in Rx");
|
||||
m_currentHeTbPpduUid = ppdu->GetUid ();
|
||||
//EndReceive is scheduled by StartReceiveOfdmaPayload
|
||||
//ResetReceive is scheduled below at end of PSDU
|
||||
}
|
||||
else
|
||||
{
|
||||
m_endRxEvent = Simulator::Schedule (payloadDuration, &WifiPhy::EndReceive, this, event);
|
||||
success = true;
|
||||
NS_LOG_DEBUG ("Receiving PSDU");
|
||||
m_signalNoiseMap.insert ({std::make_pair (ppdu->GetUid (), staId), SignalNoiseDbm ()});
|
||||
m_statusPerMpduMap.insert ({std::make_pair (ppdu->GetUid (), staId), std::vector<bool> ()});
|
||||
if (txVector.GetPreambleType () == WIFI_PREAMBLE_HE_TB)
|
||||
{
|
||||
//for HE TB PPDUs, ScheduleEndOfMpdus and EndReceive are scheduled by StartReceiveOfdmaPayload
|
||||
NS_ASSERT (isAp);
|
||||
m_currentHeTbPpduUid = ppdu->GetUid ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (psdu->GetNMpdus () > 1)
|
||||
{
|
||||
ScheduleEndOfMpdus (event);
|
||||
}
|
||||
m_endRxEvents.push_back (Simulator::Schedule (payloadDuration, &WifiPhy::EndReceive, this, event));
|
||||
}
|
||||
}
|
||||
}
|
||||
else //mode is not allowed
|
||||
@@ -3418,7 +3465,7 @@ WifiPhy::StartReceivePayload (Ptr<Event> event)
|
||||
{
|
||||
if (payloadDuration.IsStrictlyPositive ())
|
||||
{
|
||||
m_endRxEvent = Simulator::Schedule (payloadDuration, &WifiPhy::ResetReceive, this, event);
|
||||
m_endRxEvents.push_back (Simulator::Schedule (payloadDuration, &WifiPhy::ResetReceive, this, event));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3436,7 +3483,9 @@ WifiPhy::ScheduleEndOfMpdus (Ptr<Event> event)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << *event);
|
||||
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
|
||||
Ptr<const WifiPsdu> 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);
|
||||
@@ -3444,9 +3493,7 @@ WifiPhy::ScheduleEndOfMpdus (Ptr<Event> event)
|
||||
MpduType mpdutype = FIRST_MPDU_IN_AGGREGATE;
|
||||
uint32_t totalAmpduSize = 0;
|
||||
double totalAmpduNumSymbols = 0.0;
|
||||
Ptr<const WifiPsdu> psdu = GetAddressedPsduInPpdu (ppdu);
|
||||
size_t nMpdus = psdu->GetNMpdus ();
|
||||
uint16_t staId = SU_STA_ID;
|
||||
auto mpdu = psdu->begin ();
|
||||
for (size_t i = 0; i < nMpdus && mpdu != psdu->end (); ++mpdu)
|
||||
{
|
||||
@@ -3476,49 +3523,10 @@ WifiPhy::EndOfMpdu (Ptr<Event> event, Ptr<const WifiPsdu> psdu, size_t mpduIndex
|
||||
{
|
||||
NS_LOG_FUNCTION (this << *event << mpduIndex << relativeStart << mpduDuration);
|
||||
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
|
||||
uint16_t staId = GetStaId (ppdu);
|
||||
WifiTxVector txVector = event->GetTxVector ();
|
||||
uint16_t channelWidth = std::min (GetChannelWidth (), txVector.GetChannelWidth ());
|
||||
double snr = m_interference.CalculateSnr (event, channelWidth, txVector.GetNss (staId), GetBand (channelWidth));
|
||||
|
||||
std::pair<bool, SignalNoiseDbm> rxInfo = GetReceptionStatus (psdu, event, staId, relativeStart, mpduDuration);
|
||||
NS_LOG_DEBUG ("Extracted MPDU #" << mpduIndex << ": duration: " << mpduDuration.GetNanoSeconds () << "ns" <<
|
||||
", correct reception: " << rxInfo.first << ", Signal/Noise: " << rxInfo.second.signal << "/" << rxInfo.second.noise << "dBm");
|
||||
|
||||
m_signalNoise = rxInfo.second;
|
||||
m_statusPerMpdu.push_back (rxInfo.first);
|
||||
|
||||
if (rxInfo.first)
|
||||
{
|
||||
m_state->ContinueRxNextMpdu (Copy (psdu), snr, event->GetTxVector ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WifiPhy::EndReceive (Ptr<Event> event)
|
||||
{
|
||||
Time psduDuration = event->GetEndTime () - event->GetStartTime ();
|
||||
NS_LOG_FUNCTION (this << *event << psduDuration);
|
||||
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
|
||||
if (!ppdu->IsUlMu ())
|
||||
{
|
||||
NS_ASSERT (GetLastRxEndTime () == Simulator::Now ());
|
||||
}
|
||||
NS_ASSERT (event->GetEndTime () == Simulator::Now ());
|
||||
|
||||
uint16_t staId = GetStaId (ppdu);
|
||||
Ptr<const WifiPsdu> psdu = GetAddressedPsduInPpdu (ppdu);
|
||||
if (psdu->GetNMpdus () == 1)
|
||||
{
|
||||
//We do not enter here for A-MPDU since this is done in WifiPhy::EndOfMpdu
|
||||
std::pair<bool, SignalNoiseDbm> rxInfo = GetReceptionStatus (psdu, event, staId, NanoSeconds (0), psduDuration);
|
||||
m_signalNoise = rxInfo.second;
|
||||
m_statusPerMpdu.push_back (rxInfo.first);
|
||||
}
|
||||
|
||||
NotifyRxEnd (psdu);
|
||||
WifiTxVector txVector = event->GetTxVector ();
|
||||
uint16_t channelWidth = std::min (GetChannelWidth (), txVector.GetChannelWidth ());
|
||||
uint16_t channelWidth = std::min (GetChannelWidth (), event->GetTxVector ().GetChannelWidth ());
|
||||
WifiSpectrumBand band;
|
||||
if (txVector.IsMu ())
|
||||
{
|
||||
@@ -3530,12 +3538,72 @@ WifiPhy::EndReceive (Ptr<Event> event)
|
||||
band = GetBand (channelWidth);
|
||||
}
|
||||
double snr = m_interference.CalculateSnr (event, channelWidth, txVector.GetNss (staId), band);
|
||||
if (std::count (m_statusPerMpdu.begin (), m_statusPerMpdu.end (), true))
|
||||
|
||||
std::pair<bool, SignalNoiseDbm> rxInfo = GetReceptionStatus (psdu, event, staId, relativeStart, mpduDuration);
|
||||
NS_LOG_DEBUG ("Extracted MPDU #" << mpduIndex << ": duration: " << mpduDuration.GetNanoSeconds () << "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;
|
||||
|
||||
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)
|
||||
{
|
||||
m_state->ContinueRxNextMpdu (Copy (psdu), snr, txVector);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WifiPhy::EndReceive (Ptr<Event> event)
|
||||
{
|
||||
Ptr<const WifiPpdu> ppdu = event->GetPpdu ();
|
||||
WifiTxVector txVector = event->GetTxVector ();
|
||||
Time psduDuration = ppdu->GetTxDuration () - CalculatePhyPreambleAndHeaderDuration (txVector);
|
||||
NS_LOG_FUNCTION (this << *event << psduDuration);
|
||||
if (!ppdu->IsUlMu ())
|
||||
{
|
||||
NS_ASSERT (GetLastRxEndTime () == Simulator::Now ());
|
||||
}
|
||||
NS_ASSERT (event->GetEndTime () == Simulator::Now ());
|
||||
|
||||
uint16_t staId = GetStaId (ppdu);
|
||||
WifiSpectrumBand band;
|
||||
uint16_t channelWidth = std::min (GetChannelWidth (), txVector.GetChannelWidth ());
|
||||
if (txVector.IsMu ())
|
||||
{
|
||||
band = GetRuBand (txVector, staId);
|
||||
channelWidth = HeRu::GetBandwidth (txVector.GetRu (staId).ruType);
|
||||
}
|
||||
else
|
||||
{
|
||||
band = GetBand (channelWidth);
|
||||
}
|
||||
double snr = m_interference.CalculateSnr (event, channelWidth, txVector.GetNss (staId), band);
|
||||
|
||||
Ptr<const WifiPsdu> psdu = GetAddressedPsduInPpdu (ppdu);
|
||||
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 (psdu->GetNMpdus () == 1)
|
||||
{
|
||||
//We do not enter here for A-MPDU since this is done in WifiPhy::EndOfMpdu
|
||||
std::pair<bool, SignalNoiseDbm> rxInfo = GetReceptionStatus (psdu, event, staId, NanoSeconds (0), psduDuration);
|
||||
signalNoiseIt->second = rxInfo.second;
|
||||
statusPerMpduIt->second.push_back (rxInfo.first);
|
||||
}
|
||||
|
||||
if (std::count (statusPerMpduIt->second.begin (), statusPerMpduIt->second.end (), true))
|
||||
{
|
||||
//At least one MPDU has been successfully received
|
||||
WifiTxVector txVector = event->GetTxVector ();
|
||||
NotifyMonitorSniffRx (psdu, GetFrequency (), txVector, m_signalNoise, m_statusPerMpdu, staId);
|
||||
m_state->SwitchFromRxEndOk (Copy (psdu), snr, txVector, staId, m_statusPerMpdu);
|
||||
NotifyMonitorSniffRx (psdu, GetFrequency (), txVector, signalNoiseIt->second, statusPerMpduIt->second, staId);
|
||||
m_state->SwitchFromRxEndOk (Copy (psdu), snr, txVector, staId, statusPerMpduIt->second);
|
||||
m_previouslyRxPpduUid = event->GetPpdu ()->GetUid (); //store UID only if reception is successful (because otherwise trigger won't be read by MAC layer)
|
||||
}
|
||||
else
|
||||
@@ -3543,9 +3611,48 @@ WifiPhy::EndReceive (Ptr<Event> event)
|
||||
m_state->SwitchFromRxEndError (Copy (psdu), snr);
|
||||
}
|
||||
|
||||
m_interference.NotifyRxEnd (Simulator::Now ());
|
||||
m_currentEvent = 0;
|
||||
m_currentPreambleEvents.clear ();
|
||||
if (ppdu->IsUlMu ())
|
||||
{
|
||||
for (auto it = m_endRxEvents.begin (); it != m_endRxEvents.end (); )
|
||||
{
|
||||
if (it->IsExpired ())
|
||||
{
|
||||
it = m_endRxEvents.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
if (m_endRxEvents.empty ())
|
||||
{
|
||||
//We got the last PPDU of the UL-OFDMA transmission
|
||||
m_interference.NotifyRxEnd (Simulator::Now ());
|
||||
m_signalNoiseMap.clear ();
|
||||
m_statusPerMpduMap.clear ();
|
||||
for (const auto & endOfMpduEvent : m_endOfMpduEvents)
|
||||
{
|
||||
NS_ASSERT (endOfMpduEvent.IsExpired ());
|
||||
}
|
||||
m_endOfMpduEvents.clear ();
|
||||
Reset ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_interference.NotifyRxEnd (Simulator::Now ());
|
||||
MaybeCcaBusyDuration ();
|
||||
m_currentEvent = 0;
|
||||
m_currentPreambleEvents.clear ();
|
||||
m_endRxEvents.clear ();
|
||||
m_signalNoiseMap.clear ();
|
||||
m_statusPerMpduMap.clear ();
|
||||
for (const auto & endOfMpduEvent : m_endOfMpduEvents)
|
||||
{
|
||||
NS_ASSERT (endOfMpduEvent.IsExpired ());
|
||||
}
|
||||
m_endOfMpduEvents.clear ();
|
||||
}
|
||||
MaybeCcaBusyDuration ();
|
||||
}
|
||||
|
||||
@@ -3570,7 +3677,7 @@ WifiPhy::GetReceptionStatus (Ptr<const WifiPsdu> psdu, Ptr<Event> event, uint16_
|
||||
|
||||
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 () <<
|
||||
", 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.
|
||||
@@ -3635,6 +3742,8 @@ WifiPhy::ResetReceive (Ptr<Event> event)
|
||||
NS_ASSERT (event->GetEndTime () == Simulator::Now ());
|
||||
}
|
||||
NS_ASSERT (!IsStateRx ());
|
||||
NS_ASSERT (m_endRxEvents.size () == 1 && m_endRxEvents.front ().IsExpired ());
|
||||
m_endRxEvents.clear ();
|
||||
m_interference.NotifyRxEnd (Simulator::Now ());
|
||||
m_currentEvent = 0;
|
||||
m_currentPreambleEvents.clear ();
|
||||
@@ -4907,10 +5016,11 @@ WifiPhy::AbortCurrentReception (WifiPhyRxfailureReason reason)
|
||||
{
|
||||
m_endPhyRxEvent.Cancel ();
|
||||
}
|
||||
if (m_endRxEvent.IsRunning ())
|
||||
for (auto & endRxEvent : m_endRxEvents)
|
||||
{
|
||||
m_endRxEvent.Cancel ();
|
||||
endRxEvent.Cancel ();
|
||||
}
|
||||
m_endRxEvents.clear ();
|
||||
m_interference.NotifyRxEnd (Simulator::Now ());
|
||||
if (!m_currentEvent)
|
||||
{
|
||||
@@ -4979,11 +5089,27 @@ Ptr<const WifiPsdu>
|
||||
WifiPhy::GetAddressedPsduInPpdu (Ptr<const WifiPpdu> ppdu) const
|
||||
{
|
||||
Ptr<const WifiPsdu> psdu;
|
||||
if (!ppdu->IsDlMu ())
|
||||
if (!ppdu->IsMu ())
|
||||
{
|
||||
psdu = ppdu->GetPsdu ();
|
||||
}
|
||||
else
|
||||
else if (ppdu->IsUlMu ())
|
||||
{
|
||||
uint8_t bssColor = 0;
|
||||
Ptr<WifiNetDevice> device = DynamicCast<WifiNetDevice> (GetDevice ());
|
||||
if (device)
|
||||
{
|
||||
Ptr<HeConfiguration> heConfiguration = device->GetHeConfiguration ();
|
||||
if (heConfiguration)
|
||||
{
|
||||
UintegerValue bssColorAttribute;
|
||||
heConfiguration->GetAttribute ("BssColor", bssColorAttribute);
|
||||
bssColor = bssColorAttribute.Get ();
|
||||
}
|
||||
}
|
||||
psdu = ppdu->GetPsdu (bssColor);
|
||||
}
|
||||
else //DL MU
|
||||
{
|
||||
uint8_t bssColor = 0;
|
||||
Ptr<WifiNetDevice> device = DynamicCast<WifiNetDevice> (GetDevice ());
|
||||
|
||||
@@ -1864,11 +1864,12 @@ protected:
|
||||
uint32_t m_txMpduReferenceNumber; //!< A-MPDU reference number to identify all transmitted subframes belonging to the same received A-MPDU
|
||||
uint32_t m_rxMpduReferenceNumber; //!< A-MPDU reference number to identify all received subframes belonging to the same received A-MPDU
|
||||
|
||||
EventId m_endRxEvent; //!< the end of receive event
|
||||
EventId m_endPhyRxEvent; //!< the end of PHY receive event
|
||||
EventId m_endTxEvent; //!< the end of transmit event
|
||||
|
||||
std::vector <EventId> m_endRxEvents; //!< the end of receive events (only one unless UL MU reception)
|
||||
std::vector <EventId> m_endPreambleDetectionEvents; //!< the end of preamble detection events
|
||||
Ptr<Event> m_currentEvent; //!< Hold the current event
|
||||
std::map <std::pair<uint64_t /* UID*/, WifiPreamble>, Ptr<Event> > m_currentPreambleEvents; //!< store event associated to a PPDU (that has a unique ID and preamble combination) whose preamble is being received
|
||||
|
||||
uint64_t m_previouslyRxPpduUid; //!< UID of the previously received PPDU (reused by HE TB PPDUs), reset to UINT64_MAX upon transmission
|
||||
@@ -2236,17 +2237,25 @@ private:
|
||||
Ptr<NetDevice> m_device; //!< Pointer to the device
|
||||
Ptr<MobilityModel> m_mobility; //!< Pointer to the mobility model
|
||||
|
||||
Ptr<Event> m_currentEvent; //!< Hold the current event
|
||||
Ptr<FrameCaptureModel> m_frameCaptureModel; //!< Frame capture model
|
||||
Ptr<PreambleDetectionModel> m_preambleDetectionModel; //!< Preamble detection model
|
||||
Ptr<WifiRadioEnergyModel> m_wifiRadioEnergyModel; //!< Wifi radio energy model
|
||||
Ptr<ErrorModel> m_postReceptionErrorModel; //!< Error model for receive packet events
|
||||
Time m_timeLastPreambleDetected; //!< Record the time the last preamble was detected
|
||||
|
||||
std::vector<bool> m_statusPerMpdu; //!< 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
|
||||
SignalNoiseDbm m_signalNoise; //!< latest signal power and noise power in dBm (noise power includes the noise figure)
|
||||
std::vector <EventId> m_endOfMpduEvents; //!< the end of MPDU events (only used for A-MPDUs)
|
||||
|
||||
Callback<void> m_capabilitiesChangedCallback; //!< Callback when PHY capabilities changed
|
||||
/**
|
||||
* A pair of a UID and STA_ID
|
||||
*/
|
||||
typedef std::pair <uint64_t /* uid */, uint16_t /* staId */> UidStaIdPair;
|
||||
|
||||
std::map<UidStaIdPair, std::vector<bool> > 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<UidStaIdPair, SignalNoiseDbm> m_signalNoiseMap; //!< Map of the latest signal power and noise power in dBm (noise power includes the noise figure)
|
||||
|
||||
bool m_ofdmaStarted; //!< Flag whether the reception of the OFDMA part has started (only used for UL-OFDMA)
|
||||
|
||||
Callback<void> m_capabilitiesChangedCallback; //!< Callback when PHY capabilities changed
|
||||
};
|
||||
|
||||
} //namespace ns3
|
||||
|
||||
@@ -382,11 +382,14 @@ WifiPpdu::GetPsdu (uint8_t bssColor, uint16_t staId) const
|
||||
else if (IsUlMu ())
|
||||
{
|
||||
NS_ASSERT (m_psdus.size () == 1);
|
||||
return m_psdus.begin ()->second;
|
||||
if (bssColor == 0 || (bssColor == m_heSig.GetBssColor ()))
|
||||
{
|
||||
return m_psdus.begin ()->second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bssColor == m_heSig.GetBssColor ())
|
||||
if (bssColor == 0 || (bssColor == m_heSig.GetBssColor ()))
|
||||
{
|
||||
auto it = m_psdus.find (staId);
|
||||
if (it != m_psdus.end ())
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
* \param staId the staId of the PHY calling this function.
|
||||
* \return the PSDU
|
||||
*/
|
||||
Ptr<const WifiPsdu> GetPsdu (uint8_t bssColor = 64, uint16_t staId = SU_STA_ID) const;
|
||||
Ptr<const WifiPsdu> GetPsdu (uint8_t bssColor = 0, uint16_t staId = SU_STA_ID) const;
|
||||
|
||||
/**
|
||||
* Return true if the PPDU's transmission was aborted due to transmitter switch off
|
||||
|
||||
Reference in New Issue
Block a user