add Dcf to build
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Tue, 09 Oct 2007 12:17:02 +0200
changeset 1923 a8caecf6637f
parent 1922 dd18bc29fc3c
child 1924 0471fb5a3df8
add Dcf to build
src/devices/wifi/dcf.cc
src/devices/wifi/dcf.h
src/devices/wifi/wscript
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/wifi/dcf.cc	Tue Oct 09 12:17:02 2007 +0200
@@ -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 <mathieu.lacage@sophia.inria.fr>
+ */
+
+#include <iostream>
+#include <cassert>
+#include <math.h>
+
+#include "ns3/random-variable.h"
+#include "ns3/simulator.h"
+
+#include "dcf.h"
+#include "mac-parameters.h"
+
+
+
+#define noDCF_TRACE 1
+
+#ifdef DCF_TRACE
+#  include <iostream>
+#  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="<<delayUntilAccessGranted);
+      m_accessTimerEvent = Simulator::Schedule (delayUntilAccessGranted, 
+                                                &Dcf::AccessTimeout, this);
+    } 
+  else if (IsPhyBusy ()) 
+    {
+      /* someone else has accessed the medium.
+       * generate a backoff, start timer.
+       */
+      StartBackoff ();
+    } 
+  else if (delayUntilAccessGranted.IsStrictlyPositive ()) 
+    {
+      /* medium is IDLE, we have no backoff running but we 
+       * need to wait a bit before accessing the medium.
+       */
+      TRACE ("request access Y delayed for="<< delayUntilAccessGranted);
+      assert (m_accessTimerEvent.IsExpired ());
+      m_accessTimerEvent = Simulator::Schedule (delayUntilAccessGranted, 
+                                                &Dcf::AccessTimeout, this);
+    } 
+  else 
+    {
+      /* we can access the medium now.
+       */
+      TRACE ("access granted immediatly");
+      m_listener->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="<<GetCwMin ()<<
+         "<"<<m_cw<<"<"<<GetCwMax ()<<
+         ", picked="<<pickedCw); 
+  Time delay = Scalar (pickedCw) * m_parameters->GetSlotTime ();
+  return delay;
+}
+void
+Dcf::ResetCw (void)
+{
+  m_cw = GetCwMin ();
+}
+void
+Dcf::UpdateFailedCw (void)
+{
+  uint32_t cw = m_cw;
+  cw *= 2;
+  if (cw > GetCwMax ()) 
+    {
+      cw = GetCwMax ();
+    }
+  m_cw = cw;
+}
+
+Time
+Dcf::MostRecent (Time a, Time b) const
+{
+  return Max (a, b);
+}
+Time
+Dcf::MostRecent (Time a, Time b, Time c) const
+{
+  Time retval;
+  retval = Max (a, b);
+  retval = Max (retval, c);
+  return retval;
+}
+Time
+Dcf::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;
+}
+
+Time
+Dcf::GetDifs (void) const
+{
+  return m_difs;
+}
+Time
+Dcf::GetEifs (void) const
+{
+  return m_eifs;
+}
+uint32_t 
+Dcf::GetCwMin (void) const
+{
+  return m_cwMin;
+}
+uint32_t 
+Dcf::GetCwMax (void) const
+{
+  return m_cwMax;
+}
+
+/***************************************************************
+ *     Complicated timekeeping backoff methods.
+ ***************************************************************/ 
+
+bool 
+Dcf::IsPhyBusy (void)
+{
+  if (m_rxing) 
+    {
+      return true;
+    }
+  Time lastTxEnd = m_lastTxStart + m_lastTxDuration;
+  if (lastTxEnd > Simulator::Now ()) 
+    {
+      return true;
+    }
+  return false;
+}
+
+void
+Dcf::StartBackoff (void)
+{
+  Time backoffStart = Now ();
+  Time backoffDuration = PickBackoffDelay ();
+  m_backoffTrace (backoffDuration);
+  assert (m_backoffStart <= backoffStart);
+  m_backoffStart = backoffStart;
+  m_backoffLeft = backoffDuration;
+  if (m_listener->AccessNeeded () && m_accessTimerEvent.IsExpired ()) 
+    {
+      Time delayUntilAccessGranted  = GetDelayUntilAccessGranted (Now ());
+      if (delayUntilAccessGranted.IsStrictlyPositive ()) 
+        {
+          TRACE ("start at "<<backoffStart<<", for "<<backoffDuration);
+          m_accessTimerEvent = Simulator::Schedule (delayUntilAccessGranted,
+                                                    &Dcf::AccessTimeout, this);
+        } 
+      else 
+        {
+          TRACE ("access granted now");
+          m_listener->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="<<time<<", left="<< m_backoffLeft);
+      m_backoffStart = time;
+    }
+}
+
+/***************************************************************
+ *     Notification methods.
+ ***************************************************************/ 
+void
+Dcf::NotifyNavReset (Time navStart, Time duration)
+{
+  m_lastNavStart = navStart;
+  m_lastNavDuration = duration;
+  Time navEnd = navStart + duration;
+  Time newDelayUntilAccessGranted = GetDelayUntilAccessGranted (navEnd);
+  assert (newDelayUntilAccessGranted.IsStrictlyPositive ());
+  /* This is quite unfortunate but we need to cancel the access timer
+   * because this nav reset might have brought the time of
+   * possible access closer to us than expected.
+   */
+  if (m_accessTimerEvent.IsRunning ()) 
+    {
+      m_accessTimerEvent.Cancel ();
+      m_accessTimerEvent = Simulator::Schedule (newDelayUntilAccessGranted,
+                                                &Dcf::AccessTimeout, this);
+    }
+}
+void
+Dcf::NotifyNavStart (Time navStart, Time duration)
+{
+  assert (m_lastNavStart < navStart);
+  TRACE ("nav start at="<<navStart<<", for="<<duration);
+  UpdateBackoff (navStart);
+  m_lastNavStart = navStart;
+  m_lastNavDuration = duration;
+}
+void
+Dcf::NotifyNavContinue (Time navStart, Time duration)
+{
+  NotifyNavStart (navStart, duration);
+}
+
+void 
+Dcf::NotifyRxStartNow (Time duration)
+{
+  Time now = Now ();
+  TRACE ("rx start at="<<now<<", for="<<duration);
+  UpdateBackoff (now);
+  m_lastRxStart = now;
+  m_lastRxDuration = duration;
+  m_rxing = true;
+}
+void 
+Dcf::NotifyRxEndOkNow (void)
+{
+  Time now = Now ();
+  TRACE ("rx end ok at="<<now);
+  m_lastRxEnd = now;
+  m_lastRxReceivedOk = true;
+  m_rxing = false;
+}
+void 
+Dcf::NotifyRxEndErrorNow (void)
+{
+  Time now = Now ();
+  TRACE ("rx end error at=");
+  m_lastRxEnd = now;
+  m_lastRxReceivedOk = false;
+  m_rxing = false;
+}
+void 
+Dcf::NotifyTxStartNow (Time duration)
+{
+  Time now = Now ();
+  TRACE ("tx start at="<<now<<" for "<<duration);
+  UpdateBackoff (now);
+  m_lastTxStart = now;
+  m_lastTxDuration = duration;
+}
+
+void 
+Dcf::NotifyCcaBusyStartNow (Time duration)
+{
+  Time now = Now ();
+  TRACE ("busy start at="<<now<<" for "<<duration);
+  UpdateBackoff (now);
+  m_lastBusyStart = now;
+  m_lastBusyDuration = duration;
+}
+
+} // namespace ns3
+
+#ifdef RUN_SELF_TESTS
+#include "ns3/test.h"
+#include <list>
+
+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<uint64_t> 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="<<Simulator::Now ()
+                 << std::endl;
+      m_failed = true;
+      return;
+    }
+  uint64_t expected = m_accessGrantedExpected.front ();
+  uint64_t actual = Simulator::Now ().GetMicroSeconds ();
+  if (expected != actual) 
+    {
+      Failure () << "DCF "
+                 << "Failure: access granted at=" << Simulator::Now ()
+                 << ", expected at="<<expected<<"us"
+                 << std::endl;
+      m_failed = true;
+      return;
+    }
+  m_accessGrantedExpected.erase (m_accessGrantedExpected.begin ());
+}
+bool 
+DcfTest::AccessNeeded (void)
+{
+  return true;
+}
+bool 
+DcfTest::AccessingAndWillNotify (void)
+{
+  return false;
+}
+
+void 
+DcfTest::AddRxOkEvt (uint64_t at, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &Dcf::NotifyRxStartNow, m_dcf, 
+                       MicroSeconds (duration));
+  Simulator::Schedule (MicroSeconds (at+duration) - Now (), 
+                       &Dcf::NotifyRxEndOkNow, m_dcf);
+}
+void 
+DcfTest::AddRxErrorEvt (uint64_t at, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &Dcf::NotifyRxStartNow, m_dcf, 
+                       MicroSeconds (duration));
+  Simulator::Schedule (MicroSeconds (at+duration) - Now (), 
+                       &Dcf::NotifyRxEndErrorNow, m_dcf);
+}
+void 
+DcfTest::AddTxEvt (uint64_t at, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &Dcf::NotifyTxStartNow, m_dcf, 
+                       MicroSeconds (duration));
+}
+void 
+DcfTest::AddNavReset (uint64_t at, uint64_t start, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &Dcf::NotifyNavReset, m_dcf, 
+                       MicroSeconds (start), 
+                       MicroSeconds (duration));
+}
+void 
+DcfTest::AddNavStart (uint64_t at, uint64_t start, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &Dcf::NotifyNavStart, m_dcf, 
+                       MicroSeconds (start), MicroSeconds (duration));
+}
+void 
+DcfTest::AddNavContinue (uint64_t at, uint64_t start, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &Dcf::NotifyNavContinue, m_dcf, 
+                       MicroSeconds (start), 
+                       MicroSeconds (duration));
+}
+void 
+DcfTest::AddAccessRequest (uint64_t time)
+{
+  Simulator::Schedule (MicroSeconds (time) - Now (), 
+                       &Dcf::RequestAccess, m_dcf);
+}
+void 
+DcfTest::AddAccessError (uint64_t time)
+{
+  Simulator::Schedule (MicroSeconds (time) - Now (), 
+                       &DcfTest::AccessError, this, 
+                       time);
+}
+void 
+DcfTest::AddAccessErrorButOk (uint64_t time)
+{
+  Simulator::Schedule (MicroSeconds (time) - Now (), 
+                       &DcfTest::AccessErrorButOk, this, 
+                       time);
+}
+void 
+DcfTest::AddAccessOk (uint64_t time)
+{
+  Simulator::Schedule (MicroSeconds (time) - Now (), 
+                       &DcfTest::AccessOk, this, 
+                       time);
+}
+
+void 
+DcfTest::AccessError (uint64_t time)
+{
+  m_dcf->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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/wifi/dcf.h	Tue Oct 09 12:17:02 2007 +0200
@@ -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 <mathieu.lacage@sophia.inria.fr>
+ */
+
+#ifndef DCF_H
+#define DCF_H
+
+#include <stdint.h>
+#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<uint32_t> 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<Time> m_backoffTrace;
+};
+
+} // namespace ns3
+
+#endif /* DCF_H */
--- a/src/devices/wifi/wscript	Tue Oct 09 11:36:13 2007 +0200
+++ b/src/devices/wifi/wscript	Tue Oct 09 12:17:02 2007 +0200
@@ -17,6 +17,7 @@
         'wifi-mac-trailer.cc',
         'mac-parameters.cc',
         'mac-low.cc',
+        'dcf.cc',
         ]
     headers = bld.create_obj('ns3header')
     headers.source = [