a new (wholy untested) Dcf implementation
This commit is contained in:
386
src/devices/wifi/dcf-manager.cc
Normal file
386
src/devices/wifi/dcf-manager.cc
Normal file
@@ -0,0 +1,386 @@
|
||||
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
|
||||
|
||||
#include "ns3/assert.h"
|
||||
#include "ns3/log.h"
|
||||
#include "ns3/simulator.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "dcf-manager.h"
|
||||
#include "mac-parameters.h"
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE ("DcfManager");
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
/****************************************************************
|
||||
* Implement the DCF state holder
|
||||
****************************************************************/
|
||||
|
||||
void
|
||||
DcfState::SetAifsn (uint32_t aifsn)
|
||||
{
|
||||
m_aifsn = aifsn;
|
||||
}
|
||||
|
||||
void
|
||||
DcfState::SetCwBounds (uint32_t minCw, uint32_t maxCw)
|
||||
{
|
||||
m_cwMin = minCw;
|
||||
m_cwMax = maxCw;
|
||||
}
|
||||
|
||||
void
|
||||
DcfState::ResetCw (void)
|
||||
{
|
||||
m_cw = m_cwMin;
|
||||
}
|
||||
void
|
||||
DcfState::UpdateFailedCw (void)
|
||||
{
|
||||
uint32_t cw = m_cw;
|
||||
cw *= 2;
|
||||
cw = std::min (m_cwMax, cw);
|
||||
m_cw = cw;
|
||||
}
|
||||
void
|
||||
DcfState::UpdateBackoffSlotsNow (uint32_t nSlots)
|
||||
{
|
||||
uint32_t n = std::min (nSlots, m_backoffSlots);
|
||||
m_backoffSlots -= n;
|
||||
}
|
||||
|
||||
void
|
||||
DcfState::StartBackoffNow (uint32_t nSlots)
|
||||
{
|
||||
NS_ASSERT (m_backoffSlots == 0);
|
||||
m_backoffSlots = nSlots;
|
||||
m_backoffStart = Simulator::Now ();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DcfState::GetAifsn (void) const
|
||||
{
|
||||
return m_aifsn;
|
||||
}
|
||||
uint32_t
|
||||
DcfState::GetCw (void) const
|
||||
{
|
||||
return m_cw;
|
||||
}
|
||||
uint32_t
|
||||
DcfState::GetBackoffSlots (void) const
|
||||
{
|
||||
return m_backoffSlots;
|
||||
}
|
||||
Time
|
||||
DcfState::GetBackoffStart (void) const
|
||||
{
|
||||
return m_backoffStart;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Implement the DCF manager of all DCF state holders
|
||||
****************************************************************/
|
||||
|
||||
void
|
||||
DcfManager::Add (DcfState *dcf)
|
||||
{
|
||||
m_states.push_back (dcf);
|
||||
}
|
||||
|
||||
Time
|
||||
DcfManager::MostRecent (Time a, Time b) const
|
||||
{
|
||||
return Max (a, b);
|
||||
}
|
||||
Time
|
||||
DcfManager::MostRecent (Time a, Time b, Time c) const
|
||||
{
|
||||
Time retval;
|
||||
retval = Max (a, b);
|
||||
retval = Max (retval, c);
|
||||
return retval;
|
||||
}
|
||||
Time
|
||||
DcfManager::MostRecent (Time a, Time b, Time c, Time d) const
|
||||
{
|
||||
Time e = Max (a, b);
|
||||
Time f = Max (c, d);
|
||||
Time retval = Max (e, f);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
DcfManager::IsBusy (void) const
|
||||
{
|
||||
// PHY busy
|
||||
if (m_rxing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Time lastTxEnd = m_lastTxStart + m_lastTxDuration;
|
||||
if (lastTxEnd > Simulator::Now ())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// NAV busy
|
||||
Time lastNavEnd = m_lastNavStart + m_lastNavDuration;
|
||||
if (lastNavEnd > Simulator::Now ())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DcfManager::RequestAccess (DcfState *state)
|
||||
{
|
||||
UpdateBackoff ();
|
||||
if (m_accessTimeout.IsRunning ())
|
||||
{
|
||||
/* we don't need to do anything because we have an access
|
||||
* timer which will expire soon.
|
||||
*/
|
||||
NS_LOG_DEBUG ("access timer running. will be notified");
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Since no access timeout is running, and if we have no
|
||||
* backoff running for this DcfState, start a new backoff
|
||||
* if needed.
|
||||
*/
|
||||
if (state->GetBackoffSlots () == 0 &&
|
||||
IsBusy ())
|
||||
{
|
||||
/* someone else has accessed the medium.
|
||||
* generate a backoff.
|
||||
*/
|
||||
state->NotifyCollision ();
|
||||
}
|
||||
|
||||
DoGrantAccess ();
|
||||
DoRestartAccessTimeoutIfNeeded ();
|
||||
}
|
||||
|
||||
void
|
||||
DcfManager::DoGrantAccess (void)
|
||||
{
|
||||
for (States::const_iterator i = m_states.begin (); i != m_states.end (); )
|
||||
{
|
||||
DcfState *state = *i;
|
||||
if (state->GetBackoffSlots () == 0 && state->NeedsAccess ())
|
||||
{
|
||||
/**
|
||||
* This is the first dcf we find with an expired backoff and which
|
||||
* needs access to the medium. i.e., it has data to send.
|
||||
*/
|
||||
state->NotifyAccessGranted ();
|
||||
i++; // go to the next item in the list.
|
||||
for (States::const_iterator j = i; j != m_states.end (); j++)
|
||||
{
|
||||
DcfState *state = *j;
|
||||
if (state->GetBackoffSlots () == 0 && state->NeedsAccess ())
|
||||
{
|
||||
/**
|
||||
* all other dcfs with a lower priority whose backoff
|
||||
* has expired and which needed access to the medium
|
||||
* must be notified that we did get an internal collision.
|
||||
*/
|
||||
state->NotifyInternalCollision ();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DcfManager::AccessTimeout (void)
|
||||
{
|
||||
UpdateBackoff ();
|
||||
DoGrantAccess ();
|
||||
DoRestartAccessTimeoutIfNeeded ();
|
||||
}
|
||||
|
||||
Time
|
||||
DcfManager::GetAccessGrantStart (void) const
|
||||
{
|
||||
Time rxAccessStart;
|
||||
if (m_lastRxEnd >= m_lastRxStart)
|
||||
{
|
||||
rxAccessStart = m_lastRxEnd + m_parameters->GetSifs ();
|
||||
if (!m_lastRxReceivedOk)
|
||||
{
|
||||
rxAccessStart += m_ackTxTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rxAccessStart = m_lastRxStart + m_lastRxDuration + m_parameters->GetSifs ();
|
||||
}
|
||||
Time busyAccessStart = m_lastBusyStart + m_lastBusyDuration + m_parameters->GetSifs ();
|
||||
Time txAccessStart = m_lastTxStart + m_lastTxDuration + m_parameters->GetSifs ();
|
||||
Time navAccessStart = m_lastNavStart + m_lastNavDuration + m_parameters->GetSifs ();
|
||||
Time accessGrantedStart = MostRecent (rxAccessStart,
|
||||
busyAccessStart,
|
||||
txAccessStart,
|
||||
navAccessStart);
|
||||
NS_LOG_DEBUG ("access grant start=" << accessGrantedStart);
|
||||
return accessGrantedStart;
|
||||
}
|
||||
|
||||
void
|
||||
DcfManager::UpdateBackoff (void)
|
||||
{
|
||||
for (States::const_iterator i = m_states.begin (); i != m_states.end (); i++)
|
||||
{
|
||||
DcfState *state = *i;
|
||||
|
||||
Time mostRecentEvent = MostRecent (state->GetBackoffStart (),
|
||||
GetAccessGrantStart ());
|
||||
if (mostRecentEvent < Simulator::Now ())
|
||||
{
|
||||
Scalar nSlots = (Simulator::Now () - mostRecentEvent) / m_parameters->GetSlotTime ();
|
||||
uint32_t nIntSlots = lrint (nSlots.GetDouble ());
|
||||
/**
|
||||
* For each DcfState, calculate how many backoff slots elapsed since
|
||||
* the last time its backoff counter was updated. If the number of
|
||||
* slots is smaller than its AIFSN, the backoff did not start, so,
|
||||
* we do not update it.
|
||||
*/
|
||||
if (nIntSlots > state->GetAifsn ())
|
||||
{
|
||||
state->UpdateBackoffSlotsNow (nIntSlots - state->GetAifsn ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DcfManager::DoRestartAccessTimeoutIfNeeded (void)
|
||||
{
|
||||
/**
|
||||
* Is there a DcfState which needs to access the medium, and,
|
||||
* if there is one, how many slots for AIFS+backoff does it require ?
|
||||
*/
|
||||
bool accessTimeoutNeeded = false;
|
||||
uint32_t minNSlots = 0xffffffff;
|
||||
Time backoffStart;
|
||||
for (States::const_iterator i = m_states.begin (); i != m_states.end (); i++)
|
||||
{
|
||||
DcfState *state = *i;
|
||||
if (state->NeedsAccess ())
|
||||
{
|
||||
accessTimeoutNeeded = true;
|
||||
minNSlots = std::min (state->GetAifsn () + state->GetBackoffSlots (), minNSlots);
|
||||
}
|
||||
}
|
||||
if (accessTimeoutNeeded)
|
||||
{
|
||||
/**
|
||||
* If one of the DcfState needs access to the medium, calculate when its
|
||||
* backoff is expected to end.
|
||||
*/
|
||||
Time expectedBackoffEnd = GetAccessGrantStart () + Scalar (minNSlots) * m_parameters->GetSlotTime ();
|
||||
/**
|
||||
* It is not possible that the backoff was expected to end before now
|
||||
* because if it were possible, this would mean that we have missed
|
||||
* a backoff expiration ! And that would be a bug.
|
||||
*/
|
||||
NS_ASSERT (expectedBackoffEnd >= Simulator::Now ());
|
||||
if (expectedBackoffEnd > Simulator::Now ())
|
||||
{
|
||||
Time expectedBackoffDelay = expectedBackoffEnd - Simulator::Now ();
|
||||
if (m_accessTimeout.IsRunning () &&
|
||||
Simulator::GetDelayLeft (m_accessTimeout) > expectedBackoffDelay)
|
||||
{
|
||||
m_accessTimeout.Cancel ();
|
||||
}
|
||||
if (m_accessTimeout.IsExpired ())
|
||||
{
|
||||
m_accessTimeout = Simulator::Schedule (expectedBackoffDelay,
|
||||
&DcfManager::AccessTimeout, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DcfManager::NotifyRxStartNow (Time duration)
|
||||
{
|
||||
Time now = Simulator::Now ();
|
||||
NS_LOG_DEBUG ("rx start at="<<now<<", for="<<duration);
|
||||
UpdateBackoff ();
|
||||
m_lastRxStart = now;
|
||||
m_lastRxDuration = duration;
|
||||
m_rxing = true;
|
||||
}
|
||||
void
|
||||
DcfManager::NotifyRxEndOkNow (void)
|
||||
{
|
||||
Time now = Simulator::Now ();
|
||||
NS_LOG_DEBUG ("rx end ok at="<<now);
|
||||
m_lastRxEnd = now;
|
||||
m_lastRxReceivedOk = true;
|
||||
m_rxing = false;
|
||||
}
|
||||
void
|
||||
DcfManager::NotifyRxEndErrorNow (void)
|
||||
{
|
||||
Time now = Simulator::Now ();
|
||||
NS_LOG_DEBUG ("rx end error at=");
|
||||
m_lastRxEnd = now;
|
||||
m_lastRxReceivedOk = false;
|
||||
m_rxing = false;
|
||||
}
|
||||
void
|
||||
DcfManager::NotifyTxStartNow (Time duration)
|
||||
{
|
||||
Time now = Simulator::Now ();
|
||||
NS_LOG_DEBUG ("tx start at="<<now<<" for "<<duration);
|
||||
UpdateBackoff ();
|
||||
m_lastTxStart = now;
|
||||
m_lastTxDuration = duration;
|
||||
}
|
||||
void
|
||||
DcfManager::NotifyCcaBusyStartNow (Time duration)
|
||||
{
|
||||
Time now = Simulator::Now ();
|
||||
NS_LOG_DEBUG ("busy start at="<<now<<" for "<<duration);
|
||||
UpdateBackoff ();
|
||||
m_lastBusyStart = now;
|
||||
m_lastBusyDuration = duration;
|
||||
}
|
||||
void
|
||||
DcfManager::NotifyNavResetNow (Time duration)
|
||||
{
|
||||
Time now = Simulator::Now ();
|
||||
NS_LOG_DEBUG ("nav reset at="<<now<<", for="<<duration);
|
||||
UpdateBackoff ();
|
||||
m_lastNavStart = now;
|
||||
m_lastNavDuration = duration;
|
||||
UpdateBackoff ();
|
||||
/**
|
||||
* If the nav reset indicates an end-of-nav which is earlier
|
||||
* than the previous end-of-nav, the expected end of backoff
|
||||
* might be later than previously thought so, we might need
|
||||
* to restart a new access timeout.
|
||||
*/
|
||||
DoRestartAccessTimeoutIfNeeded ();
|
||||
}
|
||||
void
|
||||
DcfManager::NotifyNavStartNow (Time duration)
|
||||
{
|
||||
Time now = Simulator::Now ();
|
||||
NS_ASSERT (m_lastNavStart < now);
|
||||
NS_LOG_DEBUG ("nav start at="<<now<<", for="<<duration);
|
||||
UpdateBackoff ();
|
||||
// XXX handle
|
||||
m_lastNavStart = now;
|
||||
m_lastNavDuration = duration;
|
||||
}
|
||||
|
||||
} // namespace ns3
|
||||
139
src/devices/wifi/dcf-manager.h
Normal file
139
src/devices/wifi/dcf-manager.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
|
||||
|
||||
#include "ns3/nstime.h"
|
||||
#include "ns3/event-id.h"
|
||||
#include <vector>
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
class MacParameters;
|
||||
|
||||
class DcfState
|
||||
{
|
||||
public:
|
||||
virtual ~DcfState ();
|
||||
|
||||
void SetAifsn (uint32_t aifsn);
|
||||
void SetCwBounds (uint32_t minCw, uint32_t maxCw);
|
||||
void ResetCw (void);
|
||||
void UpdateFailedCw (void);
|
||||
void StartBackoffNow (uint32_t nSlots);
|
||||
|
||||
private:
|
||||
friend class DcfManager;
|
||||
|
||||
uint32_t GetAifsn (void) const;
|
||||
uint32_t GetCw (void) const;
|
||||
uint32_t GetBackoffSlots (void) const;
|
||||
Time GetBackoffStart (void) const;
|
||||
|
||||
void UpdateBackoffSlotsNow (uint32_t nSlots);
|
||||
|
||||
virtual bool NeedsAccess (void) const = 0;
|
||||
virtual void NotifyAccessGranted (void) = 0;
|
||||
virtual void NotifyInternalCollision (void) = 0;
|
||||
virtual void NotifyCollision (void) = 0;
|
||||
|
||||
uint32_t m_aifsn;
|
||||
uint32_t m_backoffSlots;
|
||||
Time m_backoffStart;
|
||||
uint32_t m_cwMin;
|
||||
uint32_t m_cwMax;
|
||||
uint32_t m_cw;
|
||||
};
|
||||
|
||||
class DcfManager
|
||||
{
|
||||
void SetParameters (const MacParameters *parameters);
|
||||
|
||||
// at the lowest mandatory rate.
|
||||
// used for EIFS calculation.
|
||||
void SetAckTxDuration (Time ackTxDuration);
|
||||
|
||||
void Add (DcfState *dcf);
|
||||
|
||||
void RequestAccess (DcfState *state);
|
||||
|
||||
/**
|
||||
* \param duration expected duration of reception
|
||||
*
|
||||
* Notify the DCF that a packet reception started
|
||||
* for the expected duration.
|
||||
*/
|
||||
void NotifyRxStartNow (Time duration);
|
||||
/**
|
||||
* Notify the DCF that a packet reception was just
|
||||
* completed successfully.
|
||||
*/
|
||||
void NotifyRxEndOkNow (void);
|
||||
/**
|
||||
* Notify the DCF that a packet reception was just
|
||||
* completed unsuccessfully.
|
||||
*/
|
||||
void NotifyRxEndErrorNow (void);
|
||||
/**
|
||||
* \param duration expected duration of transmission
|
||||
*
|
||||
* Notify the DCF that a packet transmission was
|
||||
* just started and is expected to last for the specified
|
||||
* duration.
|
||||
*/
|
||||
void NotifyTxStartNow (Time duration);
|
||||
/**
|
||||
* \param duration expected duration of cca busy period
|
||||
*
|
||||
* Notify the DCF that a CCA busy period has just started.
|
||||
*/
|
||||
void NotifyCcaBusyStartNow (Time duration);
|
||||
/**
|
||||
* \param duration the value of the received NAV.
|
||||
*
|
||||
* Called at end of rx
|
||||
*/
|
||||
void NotifyNavResetNow (Time duration);
|
||||
/**
|
||||
* \param duration the value of the received NAV.
|
||||
*
|
||||
* Called at end of rx
|
||||
*/
|
||||
void NotifyNavStartNow (Time duration);
|
||||
|
||||
private:
|
||||
void UpdateBackoff (void);
|
||||
Time MostRecent (Time a, Time b) const;
|
||||
Time MostRecent (Time a, Time b, Time c) const;
|
||||
Time MostRecent (Time a, Time b, Time c, Time d) const;
|
||||
/**
|
||||
* Access will never be granted to the medium _before_
|
||||
* the time returned by this method.
|
||||
*
|
||||
* \returns the absolute time at which access could start to
|
||||
* be granted
|
||||
*/
|
||||
Time GetAccessGrantStart (void) const;
|
||||
void DoRestartAccessTimeoutIfNeeded (void);
|
||||
void AccessTimeout (void);
|
||||
void DoGrantAccess (void);
|
||||
bool IsBusy (void) const;
|
||||
|
||||
typedef std::vector<DcfState *> States;
|
||||
|
||||
States m_states;
|
||||
Time m_lastNavStart;
|
||||
Time m_lastNavDuration;
|
||||
Time m_lastRxStart;
|
||||
Time m_lastRxDuration;
|
||||
bool m_lastRxReceivedOk;
|
||||
Time m_lastRxEnd;
|
||||
Time m_lastTxStart;
|
||||
Time m_lastTxDuration;
|
||||
Time m_lastBusyStart;
|
||||
Time m_lastBusyDuration;
|
||||
bool m_rxing;
|
||||
bool m_sleeping;
|
||||
Time m_ackTxTime;
|
||||
EventId m_accessTimeout;
|
||||
MacParameters *m_parameters;
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
@@ -32,7 +32,8 @@ def build(bld):
|
||||
'mac-high-nqsta.cc',
|
||||
'wifi-net-device.cc',
|
||||
'wifi-default-parameters.cc',
|
||||
'random-stream.cc'
|
||||
'random-stream.cc',
|
||||
'dcf-manager.cc',
|
||||
]
|
||||
headers = bld.create_obj('ns3header')
|
||||
headers.source = [
|
||||
|
||||
Reference in New Issue
Block a user