wifi: Align EDCA transmissions to slot boundaries

This commit is contained in:
Stefano Avallone
2020-01-07 22:51:00 +01:00
parent 5000638ddc
commit 4e184f3e9b
18 changed files with 136 additions and 48 deletions

View File

@@ -88,6 +88,12 @@ and <b> BuildingsHelper </b> classes, a building aware pathloss models, e.g.,
<b> HybridBuildingsPropagationLossModel </b> is now able to accurately compute
the pathloss for a node moving in and out of buildings in a simulation. See <a href=https://gitlab.com/nsnam/ns-3-dev/issues/80>issue 80</a>
for discussion.</li>
<li> The implementation of the wifi channel access functions has been improved
to make them more adherent to the IEEE 802.11-2016 standard. Concerning the DCF,
the backoff procedure is no longer invoked when a packet is queued for transmission
and the medium has not been idle for a DIFS, but it is invoked if the medium is busy
or does not remain idle for a DIFS after the packet has been queued. Concerning the
EDCAF, tranmissions are now correctly aligned at slot boundaries.</li>
</ul>
<hr>

View File

@@ -29,6 +29,8 @@ Bugs fixed
----------
- Issue #119 - Waf --lcov-report option was broken
- Issue #84 - Wi-Fi removing wrong header due to copy-paste error
- wifi: a zero value for the backoff timer might be discarded and a new value
might be generated by an erroneous call to NotifyCollision().
Known issues
------------

View File

@@ -578,9 +578,28 @@ where the backoff timer duration is lazily calculated whenever needed since it
is claimed to have much better performance than the simpler recurring timer
solution.
The backoff procedure of DCF is described in section 9.2.5.2 of [ieee80211]_.
The DCF basic access is described in section 10.3.4.2 of [ieee80211-2016]_.
*The backoff procedure shall be invoked for a STA to transfer a frame
*A STA may transmit an MPDU when it is operating under the DCF access method
[..] when the STA determines that the medium is idle when a frame is queued
for transmission, and remains idle for a period of a DIFS, or an EIFS
(10.3.2.3.7) from the end of the immediately preceding medium-busy event,
whichever is the greater, and the backoff timer is zero. Otherwise the random
backoff procedure described in 10.3.4.3 shall be followed."
Thus, a station is allowed not to invoke the backoff procedure if all of the
following conditions are met:
* the medium is idle when a frame is queued for transmission
* the medium remains idle until the most recent of these two events: a DIFS
from the time when the frame is queued for transmission; an EIFS from the
end of the immediately preceding medium-busy event (associated with the
reception of an erroneous frame)
* the backoff timer is zero
The backoff procedure of DCF is described in section 10.3.4.3 of [ieee80211-2016]_.
* “A STA shall invoke the backoff procedure to transfer a frame
when finding the medium busy as indicated by either the physical or
virtual CS mechanism.”
* “A backoff procedure shall be performed immediately after the end of
@@ -588,19 +607,43 @@ The backoff procedure of DCF is described in section 9.2.5.2 of [ieee80211]_.
type Data, Management, or Control with subtype PS-Poll, even if no
additional transmissions are currently queued.”
Thus, if the queue is empty, a newly arrived packet should be transmitted
immediately after channel is sensed idle for DIFS. If queue is not empty
and after a successful MPDU that has no more fragments, a node should
also start the backoff timer.
The EDCA backoff procedure is slightly different than the DCF backoff procedure
and is described in section 10.22.2.2 of [ieee80211-2016]_. The backoff procedure
shall be invoked by an EDCAF when any of the following events occur:
Some users have observed that the 802.11 MAC with an empty queue on an
idle channel will transmit the first frame arriving to the model
immediately without waiting for DIFS or backoff, and wonder whether this
is compliant. According to the standard, “The backoff procedure shall
be invoked for a STA to transfer a frame when finding the medium busy
as indicated by either the physical or virtual CS mechanism.” So in
this case, the medium is not found to be busy in recent past and the
station can transmit immediately.
* a frame is "queued for transmission such that one of the transmit queues
associated with that AC has now become non-empty and any other transmit queues
associated with that AC are empty; the medium is busy on the primary channel"
* "The transmission of the MPDU in the final PPDU transmitted by the TXOP holder
during the TXOP for that AC has completed and the TXNAV timer has expired, and
the AC was a primary AC"
* "The transmission of an MPDU in the initial PPDU of a TXOP fails [..] and the
AC was a primary AC"
* "The transmission attempt collides internally with another EDCAF of an AC that
has higher priority"
* (optionally) "The transmission by the TXOP holder of an MPDU in a non-initial
PPDU of a TXOP fails"
Additionally, section 10.22.2.4 of [ieee80211-2016]_ introduces the notion of
slot boundary, which basically occurs following SIFS + AIFSN * slotTime of idle
medium after the last busy medium that was the result of a reception of a frame
with a correct FCS or following EIFS - DIFS + AIFSN * slotTime + SIFS of idle
medium after the last indicated busy medium that was the result of a frame reception
that has resulted in FCS error, or following a slotTime of idle medium occurring
immediately after any of these conditions.
On these specific slot boundaries, each EDCAF shall make a determination to perform
one and only one of the following functions:
* Decrement the backoff timer.
* Initiate the transmission of a frame exchange sequence.
* Invoke the backoff procedure due to an internal collision.
* Do nothing.
Thus, if an EDCAF decrements its backoff timer on a given slot boundary and, as
a result, the backoff timer has a zero value, the EDCAF cannot immediately
transmit, but it has to wait for another slotTime of idle medium before transmission
can start.
The higher-level MAC functions are implemented in a set of other C++ classes and
deal with:

View File

@@ -7,6 +7,8 @@ References
.. [ieee80211] IEEE Std 802.11-2012, *Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications*
.. [ieee80211-2016] IEEE Std 802.11-2016, *Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications*
.. [pei80211b] \G. Pei and Tom Henderson, `Validation of ns-3 802.11b PHY model <http://www.nsnam.org/~pei/80211b.pdf>`__
.. [pei80211ofdm] \G. Pei and Tom Henderson, `Validation of OFDM error rate model in ns-3 <http://www.nsnam.org/~pei/80211ofdm.pdf>`__

View File

@@ -262,6 +262,21 @@ ChannelAccessManager::RequestAccess (Ptr<Txop> state, bool isCfPeriod)
m_accessTimeout = Simulator::Schedule (delay, &ChannelAccessManager::DoGrantPcfAccess, this, state);
return;
}
/*
* EDCAF operations shall be performed at slot boundaries (Sec. 10.22.2.4 of 802.11-2016)
*/
Time accessGrantStart = GetAccessGrantStart () + (state->GetAifsn () * m_slot);
if (state->IsQosTxop () && state->GetBackoffStart () > accessGrantStart)
{
// The backoff start time reported by the EDCAF is more recent than the last
// time the medium was busy plus an AIFS, hence we need to align it to the
// next slot boundary.
Time diff = state->GetBackoffStart () - accessGrantStart;
uint32_t nIntSlots = (diff / m_slot).GetHigh () + 1;
state->UpdateBackoffSlotsNow (0, accessGrantStart + (nIntSlots * m_slot));
}
UpdateBackoff ();
NS_ASSERT (!state->IsAccessRequested ());
state->NotifyAccessRequested ();

View File

@@ -341,7 +341,10 @@ Txop::GenerateBackoffUponAccessIfNeeded (void)
if (!m_channelAccessManager->IsBusy ())
{
// medium idle. If this is a DCF, use immediate access (we can transmit
// in a DIFS if the medium remains idle).
// in a DIFS if the medium remains idle). If this is an EDCAF, update
// the backoff start time kept by the EDCAF to the current time in order
// to correctly align the backoff start time at the next slot boundary
// (performed by the next call to ChannelAccessManager::RequestAccess())
Time delay = (IsQosTxop () ? Seconds (0) : m_low->GetSifs () + GetAifsn () * m_low->GetSlotTime ());
UpdateBackoffSlotsNow (0, Simulator::Now () + delay);
}

View File

@@ -868,14 +868,29 @@ BlockAckAggregationDisabledTest::DoRun (void)
packetSocket.Install (wifiStaNode);
packetSocket.Install (wifiApNode);
Ptr<PacketSocketClient> client = CreateObject<PacketSocketClient> ();
client->SetAttribute ("PacketSize", UintegerValue (1400));
client->SetAttribute ("MaxPackets", UintegerValue (14));
client->SetAttribute ("Interval", TimeValue (MicroSeconds (0)));
client->SetRemote (socket);
wifiStaNode.Get (0)->AddApplication (client);
client->SetStartTime (Seconds (1));
client->SetStopTime (Seconds (3.0));
// the first client application generates a single packet, which is sent
// with the normal ack policy because there are no other packets queued
Ptr<PacketSocketClient> client1 = CreateObject<PacketSocketClient> ();
client1->SetAttribute ("PacketSize", UintegerValue (1400));
client1->SetAttribute ("MaxPackets", UintegerValue (1));
client1->SetAttribute ("Interval", TimeValue (MicroSeconds (0)));
client1->SetRemote (socket);
wifiStaNode.Get (0)->AddApplication (client1);
client1->SetStartTime (Seconds (1));
client1->SetStopTime (Seconds (3.0));
// the second client application generates 13 packets. Even if when the first
// packet is queued the queue is empty, the first packet is not transmitted
// immediately, but the EDCAF waits for the next slot boundary. At that time,
// other packets have been queued, hence a BA agreement is established first.
Ptr<PacketSocketClient> client2 = CreateObject<PacketSocketClient> ();
client2->SetAttribute ("PacketSize", UintegerValue (1400));
client2->SetAttribute ("MaxPackets", UintegerValue (13));
client2->SetAttribute ("Interval", TimeValue (MicroSeconds (0)));
client2->SetRemote (socket);
wifiStaNode.Get (0)->AddApplication (client2);
client2->SetStartTime (Seconds (1.5));
client2->SetStopTime (Seconds (3.0));
Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer> ();
server->SetLocal (socket);
@@ -892,7 +907,7 @@ BlockAckAggregationDisabledTest::DoRun (void)
Simulator::Destroy ();
// The client application generates 14 packets, so we expect that the wifi PHY
// The client applications generate 14 packets, so we expect that the wifi PHY
// layer transmits 14 MPDUs, the server application receives 14 packets, and
// two BARs are transmitted.
NS_TEST_EXPECT_MSG_EQ (m_txTotal, 14, "Unexpected number of transmitted packets");

View File

@@ -231,11 +231,11 @@ TestInterBssConstantObssPdAlgo::SetupSimulation ()
// AP2 sends a packet 0.5s later.
Simulator::Schedule (Seconds (2.0), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device2, sta_device2, m_payloadSize2);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (1), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device2, WifiPhyState::TX);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (5), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device2, WifiPhyState::TX);
// All other PHYs should have stay idle until 4us (preamble detection time).
Simulator::Schedule (Seconds (2.0) + MicroSeconds (2), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device1, WifiPhyState::IDLE);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (2), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device2, WifiPhyState::IDLE);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (2), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device1, WifiPhyState::IDLE);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (6), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device1, WifiPhyState::IDLE);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (6), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device2, WifiPhyState::IDLE);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (6), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device1, WifiPhyState::IDLE);
// All PHYs should be receiving the PHY header if preamble has been detected (always the case in this test).
Simulator::Schedule (Seconds (2.0) + MicroSeconds (10), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device1, WifiPhyState::RX);
Simulator::Schedule (Seconds (2.0) + MicroSeconds (10), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device2, WifiPhyState::RX);
@@ -251,18 +251,18 @@ TestInterBssConstantObssPdAlgo::SetupSimulation ()
// AP2 sends another packet 0.1s later.
Simulator::Schedule (Seconds (2.1), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device2, sta_device2, m_payloadSize2);
// STA1 sends a packet 100us later. Even though AP2 is still transmitting, STA1 can transmit simultaneously if it's PHY was reset by OBSS_PD SR.
Simulator::Schedule (Seconds (2.1) + MicroSeconds (100), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, sta_device1, ap_device1, m_payloadSize1);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (90), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, sta_device1, ap_device1, m_payloadSize1);
if (expectPhyReset)
{
// In this case, we check the TX power is restricted (and set the expected value slightly before transmission should occur)
double expectedTxPower = std::min (m_txPowerDbm, 21 - (m_obssPdLevelDbm + 82));
Simulator::Schedule (Seconds (2.1) + MicroSeconds (99), &TestInterBssConstantObssPdAlgo::SetExpectedTxPower, this, expectedTxPower);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (89), &TestInterBssConstantObssPdAlgo::SetExpectedTxPower, this, expectedTxPower);
}
// Check simultaneous transmissions
Simulator::Schedule (Seconds (2.1) + MicroSeconds (105), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device1, expectPhyReset ? WifiPhyState::TX : WifiPhyState::RX);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (105), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device1, WifiPhyState::RX);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (105), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device2, WifiPhyState::RX);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (105), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device2, WifiPhyState::TX);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (100), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device1, expectPhyReset ? WifiPhyState::TX : WifiPhyState::RX);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (100), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device1, WifiPhyState::RX);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (100), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, sta_device2, WifiPhyState::RX);
Simulator::Schedule (Seconds (2.1) + MicroSeconds (100), &TestInterBssConstantObssPdAlgo::CheckPhyState, this, ap_device2, WifiPhyState::TX);
// Verify transmit power restrictions are not applied if access to the channel is requested after ignored OBSS transmissions.
@@ -280,14 +280,14 @@ TestInterBssConstantObssPdAlgo::SetupSimulation ()
// This test checks whether this sequence preserves transmit power restrictions if CCA resets occurred, since STA 1 has been deferring during ignored OBSS transmissions.
Simulator::Schedule (Seconds (2.4), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, sta_device2, ap_device2, m_payloadSize2 / 10);
Simulator::Schedule (Seconds (2.4) + MicroSeconds (5), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device2, sta_device2, m_payloadSize2 / 10);
Simulator::Schedule (Seconds (2.4) + MicroSeconds (65), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device1, sta_device1, m_payloadSize1 / 10);
Simulator::Schedule (Seconds (2.4) + MicroSeconds (105), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device3, sta_device3, m_payloadSize3 / 10);
Simulator::Schedule (Seconds (2.4) + MicroSeconds (15), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device2, sta_device2, m_payloadSize2 / 10);
Simulator::Schedule (Seconds (2.4) + MicroSeconds (270), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device1, sta_device1, m_payloadSize1 / 10);
Simulator::Schedule (Seconds (2.4) + MicroSeconds (300), &TestInterBssConstantObssPdAlgo::SendOnePacket, this, ap_device3, sta_device3, m_payloadSize3 / 10);
if (expectPhyReset)
{
// In this case, we check the TX power is restricted (and set the expected value slightly before transmission should occur)
double expectedTxPower = std::min (m_txPowerDbm, 21 - (m_obssPdLevelDbm + 82));
Simulator::Schedule (Seconds (2.4) + MicroSeconds (450), &TestInterBssConstantObssPdAlgo::SetExpectedTxPower, this, expectedTxPower);
Simulator::Schedule (Seconds (2.4) + MicroSeconds (400), &TestInterBssConstantObssPdAlgo::SetExpectedTxPower, this, expectedTxPower);
}
Simulator::Stop (Seconds (2.5));

View File

@@ -1954,14 +1954,14 @@ private:
*/
void RunSubtest (PointerValue apErrorModel, PointerValue staErrorModel);
uint8_t m_receivedNormalMpduCount; ///< Count received normal MPDU packets on STA
uint8_t m_receivedAmpduCount; ///< Count received A-MPDU packets on STA
uint8_t m_droppedActionCount; ///< Count dropped ADDBA request/response
uint8_t m_addbaEstablishedCount; ///< Count number of times ADDBA state machine is in established state
uint8_t m_addbaPendingCount; ///< Count number of times ADDBA state machine is in pending state
uint8_t m_addbaRejectedCount; ///< Count number of times ADDBA state machine is in rejected state
uint8_t m_addbaNoReplyCount; ///< Count number of times ADDBA state machine is in no_reply state
uint8_t m_addbaResetCount; ///< Count number of times ADDBA state machine is in reset state
uint16_t m_receivedNormalMpduCount; ///< Count received normal MPDU packets on STA
uint16_t m_receivedAmpduCount; ///< Count received A-MPDU packets on STA
uint16_t m_droppedActionCount; ///< Count dropped ADDBA request/response
uint16_t m_addbaEstablishedCount; ///< Count number of times ADDBA state machine is in established state
uint16_t m_addbaPendingCount; ///< Count number of times ADDBA state machine is in pending state
uint16_t m_addbaRejectedCount; ///< Count number of times ADDBA state machine is in rejected state
uint16_t m_addbaNoReplyCount; ///< Count number of times ADDBA state machine is in no_reply state
uint16_t m_addbaResetCount; ///< Count number of times ADDBA state machine is in reset state
};
Bug2470TestCase::Bug2470TestCase ()
@@ -2096,8 +2096,10 @@ Bug2470TestCase::RunSubtest (PointerValue apErrorModel, PointerValue staErrorMod
Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/$ns3::WifiPhy/PhyRxDrop", MakeCallback (&Bug2470TestCase::RxDropCallback, this));
Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/$ns3::RegularWifiMac/BE_Txop/BlockAckManager/AgreementState", MakeCallback (&Bug2470TestCase::AddbaStateChangedCallback, this));
Simulator::Schedule (Seconds (0.5), &Bug2470TestCase::SendPacketBurst, this, 5, apDevice.Get (0), staDevice.Get (0)->GetAddress ());
Simulator::Schedule (Seconds (0.8), &Bug2470TestCase::SendPacketBurst, this, 5, apDevice.Get (0), staDevice.Get (0)->GetAddress ());
Simulator::Schedule (Seconds (0.5), &Bug2470TestCase::SendPacketBurst, this, 1, apDevice.Get (0), staDevice.Get (0)->GetAddress ());
Simulator::Schedule (Seconds (0.5) + MicroSeconds (5), &Bug2470TestCase::SendPacketBurst, this, 4, apDevice.Get (0), staDevice.Get (0)->GetAddress ());
Simulator::Schedule (Seconds (0.8), &Bug2470TestCase::SendPacketBurst, this, 1, apDevice.Get (0), staDevice.Get (0)->GetAddress ());
Simulator::Schedule (Seconds (0.8) + MicroSeconds (5), &Bug2470TestCase::SendPacketBurst, this, 4, apDevice.Get (0), staDevice.Get (0)->GetAddress ());
Simulator::Stop (Seconds (1.0));
Simulator::Run ();