diff --git a/src/devices/wifi/dcf.cc b/src/devices/wifi/dcf.cc new file mode 100644 index 000000000..7d4cdaf64 --- /dev/null +++ b/src/devices/wifi/dcf.cc @@ -0,0 +1,841 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ + +#include +#include +#include + +#include "ns3/random-variable.h" +#include "ns3/simulator.h" + +#include "dcf.h" +#include "mac-parameters.h" + + + +#define noDCF_TRACE 1 + +#ifdef DCF_TRACE +# include +# define TRACE(x) \ + std::cout << "DCF " << x << std::endl; +#else /* DCF_TRACE */ +# define TRACE(x) +#endif /* DCF_TRACE */ + +namespace ns3 { + + +DcfAccessListener::DcfAccessListener () +{} +DcfAccessListener::~DcfAccessListener () +{} + + + +Dcf::Dcf () + : m_accessTimerEvent (), + m_backoffStart (MicroSeconds (0)), + m_backoffLeft (MicroSeconds (0)), + m_lastNavStart (MicroSeconds (0)), + m_lastNavDuration (MicroSeconds (0)), + m_lastRxStart (MicroSeconds (0)), + m_lastRxDuration (MicroSeconds (0)), + m_lastRxReceivedOk (true), + m_lastRxEnd (MicroSeconds (0)), + m_lastTxStart (MicroSeconds (0)), + m_lastTxDuration (MicroSeconds (0)), + m_lastBusyStart (MicroSeconds (0)), + m_lastBusyDuration (MicroSeconds (0)), + m_rxing (false) +{ + ResetCw (); + m_random = new UniformVariable (); +} + +Dcf::~Dcf () +{ + delete m_random; +} + +void +Dcf::ResetRng (uint32_t seed) +{ + // XXX + // m_random->Reset (seed); +} +void +Dcf::SetParameters (MacParameters const*parameters) +{ + m_parameters = parameters; +} + +void +Dcf::SetDifs (Time difs) +{ + m_difs = difs; +} +void +Dcf::SetEifs (Time eifs) +{ + m_eifs = eifs; +} +void +Dcf::SetCwBounds (uint32_t min, uint32_t max) +{ + m_cwMin = min; + m_cwMax = max; + m_cw = min; +} +void +Dcf::RegisterAccessListener (DcfAccessListener *listener) +{ + m_listener = listener; +} + + +/*************************************************************** + * public API. + ***************************************************************/ + +void +Dcf::RequestAccess (void) +{ + Time delayUntilAccessGranted = GetDelayUntilAccessGranted (Now ()); + if (m_listener->AccessingAndWillNotify ()) + { + /* don't do anything. We will start a backoff and maybe + * a timer when the txop notifies us of the end-of-access. + */ + TRACE ("accessing. will be notified."); + } + else if (m_accessTimerEvent.IsRunning ()) + { + /* we don't need to do anything because we have an access + * timer which will expire soon. + */ + TRACE ("access timer running. will be notified"); + } + else if (IsBackoffNotCompleted (Now ()) && m_accessTimerEvent.IsExpired ()) + { + /* start timer for ongoing backoff. + */ + TRACE ("request access X delayed for="<AccessGrantedNow (); + } +} + +/*************************************************************** + * Timeout method. Notifies when Access is Granted. + ***************************************************************/ + + +void +Dcf::AccessTimeout () +{ + UpdateBackoff (Now ()); + if (m_backoffLeft.IsZero ()) + { + TRACE ("timeout access granted"); + m_listener->AccessGrantedNow (); + } + else + { + Time delayUntilAccessGranted = GetDelayUntilAccessGranted (Now ()); + TRACE ("timeout access delayed for "<< delayUntilAccessGranted); + m_accessTimerEvent = Simulator::Schedule (delayUntilAccessGranted, + &Dcf::AccessTimeout, this); + } +} + + +/*************************************************************** + * Random trivial helper methods. + ***************************************************************/ + +Time +Dcf::PickBackoffDelay (void) +{ + // XXX + //uint32_t pickedCw = m_random->GetSingleValue (0, m_cw); + uint32_t pickedCw = 0; + TRACE ("cw="<AccessGrantedNow (); + } + } + else + { + if (m_accessTimerEvent.IsRunning ()) + { + TRACE ("no access needed because timer running."); + } + if (!m_listener->AccessNeeded ()) + { + TRACE ("no access needed."); + } + TRACE ("no access needed for now."); + } +} +Time +Dcf::GetAccessGrantedStart (void) const +{ + /* This method evaluates the time where access to the + * medium is allowed. The return value could be + * somewhere in the past or in the future. + */ + Time rxAccessStart; + if (m_lastRxEnd >= m_lastRxStart) + { + if (m_lastRxReceivedOk) + { + rxAccessStart = m_lastRxEnd + GetDifs (); + } + else + { + rxAccessStart = m_lastRxEnd + GetEifs (); + } + } + else + { + rxAccessStart = m_lastRxStart + m_lastRxDuration + GetDifs (); + } + Time busyAccessStart = m_lastBusyStart + m_lastBusyDuration + GetDifs (); + Time txAccessStart = m_lastTxStart + m_lastTxDuration + GetDifs (); + Time navAccessStart = m_lastNavStart + m_lastNavDuration + GetDifs (); + Time accessGrantedStart = MostRecent (rxAccessStart, + busyAccessStart, + txAccessStart, + navAccessStart); + return accessGrantedStart; +} + +bool +Dcf::IsBackoffNotCompleted (Time now) +{ + UpdateBackoff (now); + if (m_backoffLeft.IsStrictlyPositive ()) + { + return true; + } + else + { + return false; + } +} + + +Time +Dcf::GetDelayUntilAccessGranted (Time now) +{ + Time deltaTo = GetAccessGrantedStart () - now; + Time retval = Max (deltaTo, Seconds (0)); + UpdateBackoff (now); + retval += m_backoffLeft; + return retval; +} +void +Dcf::UpdateBackoff (Time time) +{ + if (m_backoffLeft.IsZero ()) + { + return; + } + + //TRACE ("time: %f, backoffstart: %f\n", time, m_backoffStart); + assert (time >= m_backoffStart); + + Time mostRecentEvent = MostRecent (m_backoffStart, + GetAccessGrantedStart ()); + if (mostRecentEvent < time) + { + Time newBackoffLeft = m_backoffLeft - (time - mostRecentEvent); + m_backoffLeft = Max (newBackoffLeft, Seconds (0)); + TRACE ("at="< + +namespace ns3 { + +class DcfTest : public Test { +public: + DcfTest (); + virtual bool RunTests (void); + + // callback from DcfListener + void AccessGrantedNow (void); + bool AccessNeeded (void); + bool AccessingAndWillNotify (void); +private: + + void AddRxOkEvt (uint64_t at, uint64_t duration); + void AddRxErrorEvt (uint64_t at, uint64_t duration); + void AddTxEvt (uint64_t at, uint64_t duration); + void AddNavReset (uint64_t at, uint64_t start, uint64_t duration); + void AddNavStart (uint64_t at, uint64_t start, uint64_t duration); + void AddNavContinue (uint64_t at, uint64_t start, uint64_t duration); + void AddAccessRequest (uint64_t time); + void AddAccessError (uint64_t time); + void AddAccessErrorButOk (uint64_t time); + void AddAccessOk (uint64_t time); + + void ExpectAccessGranted (uint64_t time); + + // callback to forward to DCF + void AccessError (uint64_t time); + void AccessErrorButOk (uint64_t time); + void AccessOk (uint64_t time); + + void StartTest (void); + void EndTest (void); + + Dcf *m_dcf; + MacParameters *m_parameters; + class TestAccessListener *m_listener; + std::list m_accessGrantedExpected; + bool m_failed; +}; + +class TestAccessListener : public DcfAccessListener { +public: + TestAccessListener (DcfTest *test) + : m_test (test) {} + virtual ~TestAccessListener () {} + virtual void AccessGrantedNow (void) { + m_test->AccessGrantedNow (); + } + virtual bool AccessNeeded (void) { + return m_test->AccessNeeded (); + } + virtual bool AccessingAndWillNotify (void) { + return m_test->AccessingAndWillNotify (); + } +private: + DcfTest *m_test; +}; + + + +DcfTest::DcfTest () + : Test ("Dcf") {} + +void +DcfTest::AccessGrantedNow (void) +{ + if (m_accessGrantedExpected.empty ()) + { + Failure () << "DCF " + << "Failure: unexpected access granted at="<UpdateFailedCw (); + m_dcf->StartBackoff (); +} +void +DcfTest::AccessErrorButOk (uint64_t time) +{ + m_dcf->ResetCw (); + m_dcf->StartBackoff (); +} +void +DcfTest::AccessOk (uint64_t time) +{ + m_dcf->ResetCw (); + m_dcf->StartBackoff (); +} + +void +DcfTest::ExpectAccessGranted (uint64_t time) +{ + m_accessGrantedExpected.push_back (time); +} + +void +DcfTest::StartTest (void) +{ + m_dcf = new Dcf (); + m_dcf->ResetRng (0); + m_parameters = new MacParameters (); + m_listener = new TestAccessListener (this); + m_dcf->SetParameters (m_parameters); + m_dcf->RegisterAccessListener (m_listener); + + m_parameters->SetSlotTime (MicroSeconds (1)); + m_dcf->SetDifs (MicroSeconds (3)); + m_dcf->SetEifs (MicroSeconds (4)); + m_dcf->SetCwBounds (8, 64); +} +void +DcfTest::EndTest (void) +{ + if (!m_accessGrantedExpected.empty ()) + { + Failure () << "DCF: access not granted as expected" + << std::endl; + } + m_accessGrantedExpected.erase (m_accessGrantedExpected.begin (), + m_accessGrantedExpected.end ()); + Simulator::Destroy (); + delete m_dcf; + delete m_parameters; + delete m_listener; +} + +bool +DcfTest::RunTests (void) +{ + m_failed = false; + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 2+8); + AddRxOkEvt (32, 5); + AddAccessRequest (15); + AddAccessRequest (16); + AddAccessRequest (20); + ExpectAccessGranted (51); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 2+5); + AddRxOkEvt (32, 7); + AddAccessRequest (15); + AddAccessRequest (16); + AddAccessRequest (20); + ExpectAccessGranted (50); + Simulator::Run (); + EndTest (); + + StartTest (); + AddAccessRequest (10); + ExpectAccessGranted (10); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 2+8); + AddRxOkEvt (32, 7); + AddAccessRequest (40); + ExpectAccessGranted (43); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 2+8); + AddRxOkEvt (32, 7); + AddAccessRequest (41); + ExpectAccessGranted (43); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 2+8); + AddRxOkEvt (32, 7); + AddAccessRequest (43); + ExpectAccessGranted (43); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxErrorEvt (10, 20); + AddRxOkEvt (31, 7); + AddAccessRequest (39); + ExpectAccessGranted (41); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxErrorEvt (10, 20); + AddRxErrorEvt (31, 7); + AddAccessRequest (39); + ExpectAccessGranted (42); + Simulator::Run (); + EndTest (); + + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 200); + AddRxOkEvt (35, 10); + AddNavReset (45, 45, 0); + AddAccessRequest (32); + ExpectAccessGranted (48); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 200); + AddRxOkEvt (35, 10); + AddNavReset (45, 45, 0); + Simulator::Run (); + EndTest (); + + StartTest (); + AddRxOkEvt (10, 20); + AddNavStart (30, 30, 200); + AddRxOkEvt (35, 10); + AddNavReset (45, 45, 0); + AddAccessRequest (49); + ExpectAccessGranted (49); + Simulator::Run (); + EndTest (); + + + return !m_failed; +} + +static DcfTest gDcfTest; + +} // namespace ns3 + + +#endif /* RUN_SELF_TESTS */ diff --git a/src/devices/wifi/dcf.h b/src/devices/wifi/dcf.h new file mode 100644 index 000000000..a096f64db --- /dev/null +++ b/src/devices/wifi/dcf.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ + +#ifndef DCF_H +#define DCF_H + +#include +#include "ns3/event-id.h" +#include "ns3/uv-trace-source.h" +#include "ns3/callback-trace-source.h" +#include "ns3/nstime.h" + +namespace ns3 { + +class UniformVariable; +class MacParameters; + +class DcfAccessListener { +public: + DcfAccessListener (); + virtual ~DcfAccessListener (); + + /* Tell the listener than it can start + * accessing the medium right now. + */ + virtual void AccessGrantedNow (void) = 0; + /* ask the listener if there are candidates + * who need access to the medium. + */ + virtual bool AccessNeeded (void) = 0; + /* ask the listener if it is currently + * performing an access which was granted + * earlier to him and if it will notify + * the Dcf when the access is complete. + */ + virtual bool AccessingAndWillNotify (void) = 0; +}; + +class Dcf +{ +public: + Dcf (); + ~Dcf (); + + void ResetRng (uint32_t seed); + + void SetParameters (const MacParameters *parameters); + void SetDifs (Time difs); + void SetEifs (Time eifs); + void SetCwBounds (uint32_t min, uint32_t max); + void RegisterAccessListener (DcfAccessListener *listener); + + void RequestAccess (void); + + void ResetCw (void); + void UpdateFailedCw (void); + void StartBackoff (void); + + /* notification methods. */ + void NotifyRxStartNow (Time duration); + void NotifyRxEndOkNow (void); + void NotifyRxEndErrorNow (void); + void NotifyTxStartNow (Time duration); + void NotifyCcaBusyStartNow (Time duration); + void NotifyNavReset (Time now, Time duration); + void NotifyNavStart (Time now, Time duration); + void NotifyNavContinue (Time now, Time duration); +private: + void AccessTimeout (void); + + /* trivial helpers */ + Time PickBackoffDelay (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; + Time GetDifs (void) const; + Time GetEifs (void) const; + uint32_t GetCwMin (void) const; + uint32_t GetCwMax (void) const; + + /* time calculation helpers */ + bool IsPhyBusy (void); + bool IsBackoffNotCompleted (Time now); + Time GetDelayUntilAccessGranted (Time now); + Time GetAccessGrantedStart (void) const; + void UpdateBackoff (Time time); + + EventId m_accessTimerEvent; + + UniformVariable *m_random; + const MacParameters *m_parameters; + DcfAccessListener *m_listener; + Time m_difs; + Time m_eifs; + uint32_t m_cwMin; + uint32_t m_cwMax; + + UVTraceSource m_cw; + Time m_backoffStart; + Time m_backoffLeft; + 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; + /* "80211-dcf-backoff" + * param1: backoff Duration + * reports the start of a backoff + */ + CallbackTraceSource