Channel switching support for Wifi
authorRamon Bauza
Tue, 15 Sep 2009 10:47:02 +0200
changeset 5189 8fcdf87a790a
parent 5188 799fdd8fc54a
child 5191 ca171cb4ea82
Channel switching support for Wifi
AUTHORS
CHANGES.html
RELEASE_NOTES
examples/wifi-ap.cc
src/devices/wifi/dca-txop.cc
src/devices/wifi/dca-txop.h
src/devices/wifi/dcf-manager-test.cc
src/devices/wifi/dcf-manager.cc
src/devices/wifi/dcf-manager.h
src/devices/wifi/edca-txop-n.cc
src/devices/wifi/edca-txop-n.h
src/devices/wifi/interference-helper.cc
src/devices/wifi/interference-helper.h
src/devices/wifi/mac-low.cc
src/devices/wifi/mac-low.h
src/devices/wifi/wifi-phy-state-helper.cc
src/devices/wifi/wifi-phy-state-helper.h
src/devices/wifi/wifi-phy.h
src/devices/wifi/yans-wifi-phy.cc
src/devices/wifi/yans-wifi-phy.h
--- a/AUTHORS	Tue Sep 15 00:05:36 2009 -0700
+++ b/AUTHORS	Tue Sep 15 10:47:02 2009 +0200
@@ -1,6 +1,7 @@
 Kirill Andreev (andreev@iitp.ru)
 Nicola Baldo (nbaldo@cttc.es)
 Mirko Banchi (mk.banchi@gmail.com)
+Ramon Bauza (monbauza@gmail.com)
 Mehdi Benamor (mehdi.benamor@telecom-bretagne.eu)
 Raj Bhattacharjea (raj.b@gatech.edu)
 Timo Bingmann (timo.bingmann@student.kit.edu)
--- a/CHANGES.html	Tue Sep 15 00:05:36 2009 -0700
+++ b/CHANGES.html	Tue Sep 15 10:47:02 2009 +0200
@@ -107,6 +107,9 @@
 <ul>
 <li> 10MHz and 5MHz channel width supported by 802.11a model (Ramon Bauza and Kirill Andreev).
 </ul>
+<ul>
+<li> Channel switching support. YansWifiPhy can now switch among different channels (Ramon Bauza and Pavel Boyko).
+</ul>
 </p>
 </li>
 
--- a/RELEASE_NOTES	Tue Sep 15 00:05:36 2009 -0700
+++ b/RELEASE_NOTES	Tue Sep 15 10:47:02 2009 +0200
@@ -57,6 +57,8 @@
     
   e) 802.11 enhancements:
   	- 10MHz and 5MHz channel width supported by 802.11a model (Ramon Bauza and Kirill Andreev)
+	- Channel switching support. YansWifiPhy can now switch among different channels 
+          (Ramon Bauza and Pavel Boyko)
 
 API changes from ns-3.5
 -----------------------
--- a/examples/wifi-ap.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/examples/wifi-ap.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -62,6 +62,9 @@
 {
   std::cout << " state=";
   switch (state) {
+  case WifiPhy::SWITCHING: 
+    std::cout << "switchng";
+    break; 
   case WifiPhy::TX:
     std::cout << "tx      ";
     break;
--- a/src/devices/wifi/dca-txop.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/dca-txop.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -58,6 +58,9 @@
   virtual void DoNotifyCollision (void) {
     m_txop->NotifyCollision ();
   }
+  virtual void DoNotifyChannelSwitching (void) { 
+    m_txop->NotifyChannelSwitching ();
+  }
 
   DcaTxop *m_txop;
 };
@@ -447,6 +450,13 @@
 }
 
 void 
+DcaTxop::NotifyChannelSwitching (void)
+{
+  m_queue->Flush();
+  m_currentPacket = 0;
+}
+
+void 
 DcaTxop::GotCts (double snr, WifiMode txMode)
 {
   NS_LOG_FUNCTION (this << snr << txMode);
--- a/src/devices/wifi/dca-txop.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/dca-txop.h	Tue Sep 15 10:47:02 2009 +0200
@@ -127,6 +127,7 @@
   void NotifyAccessGranted (void);
   void NotifyInternalCollision (void);
   void NotifyCollision (void);
+  void NotifyChannelSwitching (void); 
   /* event handlers */
   void GotCts (double snr, WifiMode txMode);
   void MissedCts (void);
--- a/src/devices/wifi/dcf-manager-test.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/dcf-manager-test.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -38,6 +38,7 @@
   virtual void DoNotifyAccessGranted (void);
   virtual void DoNotifyInternalCollision (void);
   virtual void DoNotifyCollision (void);
+  virtual void DoNotifyChannelSwitching (void); 
 
   typedef std::pair<uint64_t,uint64_t> ExpectedGrant;
   typedef std::list<ExpectedGrant> ExpectedGrants;
@@ -65,6 +66,7 @@
   void NotifyAccessGranted (uint32_t i);
   void NotifyInternalCollision (uint32_t i);
   void NotifyCollision (uint32_t i);
+  void NotifyChannelSwitching (uint32_t i); 
 
 
 private:
@@ -88,6 +90,9 @@
   void AddAccessRequestWithSuccessfullAck (uint64_t at, uint64_t txTime, 
                                   uint64_t expectedGrantTime, uint32_t ackDelay, uint32_t from);
   void DoAccessRequest (uint64_t txTime, uint64_t expectedGrantTime, DcfStateTest *state);
+  void AddCcaBusyEvt (uint64_t at, uint64_t duration); 
+  void AddSwitchingEvt (uint64_t at, uint64_t duration); 
+  void AddRxStartEvt (uint64_t at, uint64_t duration); 
   
   typedef std::vector<DcfStateTest *> DcfStates;
 
@@ -122,7 +127,11 @@
 {
   m_test->NotifyCollision (m_i);
 }
-
+void 
+DcfStateTest::DoNotifyChannelSwitching (void)
+{
+  m_test->NotifyChannelSwitching (m_i);
+}
 
 
 DcfManagerTest::DcfManagerTest ()
@@ -182,7 +191,22 @@
       m_result = result;
     }
 }
-
+void 
+DcfManagerTest::NotifyChannelSwitching (uint32_t i)
+{
+  DcfStateTest *state = m_dcfStates[i];
+  bool result = true;
+  if (!state->m_expectedGrants.empty ())
+    {
+      std::pair<uint64_t, uint64_t> expected = state->m_expectedGrants.front ();
+      state->m_expectedGrants.pop_front ();
+      NS_TEST_ASSERT_EQUAL (Simulator::Now (), MicroSeconds (expected.second));
+    }
+  if (!result)
+    {
+      m_result = result;
+    }
+}
 
 void 
 DcfManagerTest::ExpectInternalCollision (uint64_t time, uint32_t nSlots, uint32_t from)
@@ -320,7 +344,27 @@
   state->QueueTx (txTime, expectedGrantTime);
   m_dcfManager->RequestAccess (state);
 }
-
+void 
+DcfManagerTest::AddCcaBusyEvt (uint64_t at, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &DcfManager::NotifyMaybeCcaBusyStartNow, m_dcfManager, 
+                       MicroSeconds (duration));
+}
+void
+DcfManagerTest::AddSwitchingEvt (uint64_t at, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &DcfManager::NotifySwitchingStartNow, m_dcfManager, 
+                       MicroSeconds (duration));
+}
+void
+DcfManagerTest::AddRxStartEvt (uint64_t at, uint64_t duration)
+{
+  Simulator::Schedule (MicroSeconds (at) - Now (), 
+                       &DcfManager::NotifyRxStartNow, m_dcfManager, 
+                       MicroSeconds (duration));
+}
 
 
 
@@ -559,7 +603,95 @@
   AddAccessRequest (30, 50, 108, 0);
   ExpectCollision (30, 3, 0); // backoff: 3 slots
   EndTest ();
- 
+
+
+  // Channel switching tests
+
+  //  0          20     23      24   25  
+  //  | switching | sifs | aifsn | tx |   
+  //                | 
+  //               21 access request. 
+  StartTest (1, 3, 10);
+  AddDcfState (1);
+  AddSwitchingEvt(0,20);
+  AddAccessRequest (21, 1, 24, 0);
+  EndTest ();
+
+  //  20          40       50     53      54   55
+  //   | switching |  busy  | sifs | aifsn | tx |   
+  //         |          |
+  //        30 busy.   45 access request.   
+  //
+  StartTest (1, 3, 10);
+  AddDcfState (1);
+  AddSwitchingEvt(20,20);
+  AddCcaBusyEvt(30,20);
+  AddAccessRequest (45, 1, 54, 0);
+  EndTest ();
+
+  //  20     30          50     53      54   55
+  //   |  rx  | switching | sifs | aifsn | tx |   
+  //                        |
+  //                       51 access request.   
+  //
+  StartTest (1, 3, 10);
+  AddDcfState (1);
+  AddRxStartEvt (20,40);
+  AddSwitchingEvt(30,20);
+  AddAccessRequest (51, 1, 54, 0);
+  EndTest ();
+
+  //  20     30          50     53      54   55
+  //   | busy | switching | sifs | aifsn | tx |   
+  //                        |
+  //                       51 access request.   
+  //
+  StartTest (1, 3, 10);
+  AddDcfState (1);
+  AddCcaBusyEvt (20,40);
+  AddSwitchingEvt(30,20);
+  AddAccessRequest (51, 1, 54, 0);
+  EndTest ();
+
+  //  20      30          50     53      54   55
+  //   |  nav  | switching | sifs | aifsn | tx |   
+  //                        |
+  //                       51 access request.   
+  //
+  StartTest (1, 3, 10);
+  AddDcfState (1);
+  AddNavStart (20,40);
+  AddSwitchingEvt(30,20);
+  AddAccessRequest (51, 1, 54, 0);
+  EndTest ();
+
+  //  20      40             50          55     58      59   60
+  //   |  tx   | ack timeout  | switching | sifs | aifsn | tx |   
+  //                  |                     |
+  //                 45 access request.    56 access request.   
+  //
+  StartTest (1, 3, 10);
+  AddDcfState (1);
+  AddAccessRequestWithAckTimeout (20, 20, 20, 0);
+  AddAccessRequest (45, 1, 50, 0);
+  AddSwitchingEvt(50,5);
+  AddAccessRequest (56, 1, 59, 0);
+  EndTest ();
+
+  //  20         60     66      70       74       78  80         100    106     110  112
+  //   |    rx    | sifs | aifsn | bslot0 | bslot1 |   | switching | sifs | aifsn | tx |   
+  //        |                                                        |
+  //       30 access request.                                      101 access request.   
+  //
+  StartTest (4, 6, 10);
+  AddDcfState (1);
+  AddRxOkEvt(20,40);
+  AddAccessRequest (30, 2, 80, 0); 
+  ExpectCollision(30, 4, 0); // backoff: 4 slots
+  AddSwitchingEvt(80,20);
+  AddAccessRequest (101, 2, 110, 0);
+  EndTest ();
+
 
   return m_result;
 }
--- a/src/devices/wifi/dcf-manager.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/dcf-manager.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -154,6 +154,11 @@
 {
   DoNotifyInternalCollision ();
 }
+void 
+DcfState::NotifyChannelSwitching (void) 
+{
+  DoNotifyChannelSwitching ();
+}
 
 
 /***************************************************************
@@ -211,6 +216,9 @@
   virtual void NotifyMaybeCcaBusyStart (Time duration) {
     m_dcf->NotifyMaybeCcaBusyStartNow (duration);
   }
+  virtual void NotifySwitchingStart (Time duration) { 
+    m_dcf->NotifySwitchingStartNow (duration);
+  }
 private:
   ns3::DcfManager *m_dcf;
 };
@@ -232,6 +240,8 @@
     m_lastTxDuration (MicroSeconds (0)),
     m_lastBusyStart (MicroSeconds (0)),
     m_lastBusyDuration (MicroSeconds (0)),
+    m_lastSwitchingStart (MicroSeconds (0)), 
+    m_lastSwitchingDuration (MicroSeconds (0)), 
     m_rxing (false),
     m_slotTime (Seconds (0.0)),
     m_sifs (Seconds (0.0)),
@@ -319,6 +329,18 @@
   return retval;
 }
 
+Time
+DcfManager::MostRecent (Time a, Time b, Time c, Time d, Time e, Time f, Time g) const
+{
+  Time h = Max (a, b);
+  Time i = Max (c, d);
+  Time j = Max (e, f);
+  Time k = Max (h, i);
+  Time l = Max (j, g);
+  Time retval = Max (k, l);
+  return retval;
+}
+
 bool 
 DcfManager::IsBusy (void) const
 {
@@ -454,12 +476,14 @@
   Time navAccessStart = m_lastNavStart + m_lastNavDuration + m_sifs;
   Time ackTimeoutAccessStart = m_lastAckTimeoutEnd + m_sifs;
   Time ctsTimeoutAccessStart = m_lastCtsTimeoutEnd + m_sifs;
+  Time switchingAccessStart = m_lastSwitchingStart + m_lastSwitchingDuration + m_sifs; 
   Time accessGrantedStart = MostRecent (rxAccessStart, 
                                         busyAccessStart,
                                         txAccessStart, 
                                         navAccessStart,
                                         ackTimeoutAccessStart,
-                                        ctsTimeoutAccessStart
+                                        ctsTimeoutAccessStart,
+                                        switchingAccessStart 
                                         );
   NS_LOG_INFO ("access grant start=" << accessGrantedStart <<
                ", rx access start=" << rxAccessStart <<
@@ -596,6 +620,67 @@
   m_lastBusyStart = Simulator::Now ();
   m_lastBusyDuration = duration;
 }
+
+
+void 
+DcfManager::NotifySwitchingStartNow (Time duration)
+{
+  Time now = Simulator::Now ();
+  NS_ASSERT (m_lastTxStart + m_lastTxDuration <= now);
+  NS_ASSERT (m_lastSwitchingStart + m_lastSwitchingDuration <= now);
+
+  if (m_rxing)
+    {
+      // channel switching during packet reception
+      m_lastRxEnd = Simulator::Now ();
+      m_lastRxDuration = m_lastRxEnd - m_lastRxStart;
+      m_lastRxReceivedOk = true;
+      m_rxing = false;
+    }
+  if (m_lastNavStart + m_lastNavDuration > now)
+    {
+      m_lastNavDuration = now - m_lastNavStart;
+    }
+  if (m_lastBusyStart + m_lastBusyDuration > now)
+    {
+      m_lastBusyDuration = now - m_lastBusyStart;
+    }
+  if (m_lastAckTimeoutEnd > now)
+    {
+      m_lastAckTimeoutEnd = now;
+    }
+  if (m_lastCtsTimeoutEnd > now)
+    {
+      m_lastCtsTimeoutEnd = now;
+    }
+
+  // Cancel timeout
+  if (m_accessTimeout.IsRunning ())
+    {
+      m_accessTimeout.Cancel ();
+    }
+
+  // Reset backoffs
+  for (States::iterator i = m_states.begin (); i != m_states.end (); i++)
+    {
+      DcfState *state = *i;
+      uint32_t remainingSlots = state->GetBackoffSlots ();
+      if (remainingSlots > 0) 
+        {
+          state->UpdateBackoffSlotsNow (remainingSlots, now);
+          NS_ASSERT(state->GetBackoffSlots()==0);
+        }
+      state->ResetCw();
+      state->m_accessRequested = false;
+      state->NotifyChannelSwitching();
+    } 
+ 
+  MY_DEBUG ("switching start for "<<duration);
+  m_lastSwitchingStart = Simulator::Now ();
+  m_lastSwitchingDuration = duration;
+
+}
+
 void 
 DcfManager::NotifyNavResetNow (Time duration)
 {
--- a/src/devices/wifi/dcf-manager.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/dcf-manager.h	Tue Sep 15 10:47:02 2009 +0200
@@ -100,6 +100,7 @@
   void NotifyAccessGranted (void);
   void NotifyCollision (void);
   void NotifyInternalCollision (void);
+  void NotifyChannelSwitching (void); 
 
 
   /**
@@ -130,6 +131,14 @@
    * is access is still needed.
    */
   virtual void DoNotifyCollision (void) = 0;
+   /**
+   * Called by DcfManager to notify a DcfState subclass
+   * that a channel switching occured.
+   *
+   * The subclass is expected to flush the queue of 
+   * packets.
+   */
+  virtual void DoNotifyChannelSwitching () = 0; 
 
   uint32_t m_aifsn;
   uint32_t m_backoffSlots;
@@ -248,6 +257,12 @@
    */
   void NotifyMaybeCcaBusyStartNow (Time duration);
   /**
+   * \param duration expected duration of channel switching period
+   *
+   * Notify the DCF that a channel switching period has just started.
+   */
+  void NotifySwitchingStartNow (Time duration); 
+  /**
    * \param duration the value of the received NAV.
    *
    * Called at end of rx
@@ -269,6 +284,7 @@
   Time MostRecent (Time a, Time b, Time c) const;
   Time MostRecent (Time a, Time b, Time c, Time d) const;
   Time MostRecent (Time a, Time b, Time c, Time d, Time e, Time f) const;
+  Time MostRecent (Time a, Time b, Time c, Time d, Time e, Time f, Time g) const; 
   /**
    * Access will never be granted to the medium _before_
    * the time returned by this method.
@@ -299,6 +315,8 @@
   Time m_lastTxDuration;
   Time m_lastBusyStart;
   Time m_lastBusyDuration;
+  Time m_lastSwitchingStart; 
+  Time m_lastSwitchingDuration; 
   bool m_rxing;
   bool m_sleeping;
   Time m_eifsNoDifs;
--- a/src/devices/wifi/edca-txop-n.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/edca-txop-n.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -55,6 +55,9 @@
   virtual void DoNotifyCollision (void) {
     m_txop->NotifyCollision ();
   }
+  virtual void DoNotifyChannelSwitching (void) { 
+    m_txop->NotifyChannelSwitching ();
+  }
   EdcaTxopN *m_txop;
 };
 
@@ -427,6 +430,13 @@
   RestartAccessIfNeeded ();
 }
 
+void 
+EdcaTxopN::NotifyChannelSwitching (void)
+{
+  m_queue->Flush();
+  m_currentPacket = 0;
+}
+
 void
 EdcaTxopN::Queue (Ptr<const Packet> packet, WifiMacHeader const &hdr)
 {
--- a/src/devices/wifi/edca-txop-n.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/edca-txop-n.h	Tue Sep 15 10:47:02 2009 +0200
@@ -101,6 +101,7 @@
   void NotifyAccessGranted (void);
   void NotifyInternalCollision (void);
   void NotifyCollision (void);
+  void NotifyChannelSwitching (void);
 
   /*event handlers*/
   void GotCts (double snr, WifiMode txMode);
--- a/src/devices/wifi/interference-helper.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/interference-helper.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -603,6 +603,10 @@
   return snrPer;
 }
 
-
+void
+InterferenceHelper::EraseEvents (void) 
+{  
+  m_events.erase (m_events.begin (), m_events.end ());
+}
 
 } // namespace ns3
--- a/src/devices/wifi/interference-helper.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/interference-helper.h	Tue Sep 15 10:47:02 2009 +0200
@@ -95,6 +95,7 @@
 				      Time duration, double rxPower);
 
   struct InterferenceHelper::SnrPer CalculateSnrPer (Ptr<InterferenceHelper::Event> event);
+  void EraseEvents (void); 
 private:
   class NiChange {
   public:
--- a/src/devices/wifi/mac-low.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/mac-low.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -244,6 +244,30 @@
   return os;
 }
 
+
+/***************************************************************
+ *         Listener for PHY events. Forwards to MacLow
+ ***************************************************************/
+
+
+class PhyMacLowListener : public ns3::WifiPhyListener {
+public:
+  PhyMacLowListener (ns3::MacLow *macLow)
+    : m_macLow (macLow) {}
+  virtual ~PhyMacLowListener () {}
+  virtual void NotifyRxStart (Time duration) {}
+  virtual void NotifyRxEndOk (void) {}
+  virtual void NotifyRxEndError (void) {}
+  virtual void NotifyTxStart (Time duration) {}
+  virtual void NotifyMaybeCcaBusyStart (Time duration) {}
+  virtual void NotifySwitchingStart (Time duration) { 
+    m_macLow->NotifySwitchingStartNow (duration);
+  }
+private:
+  ns3::MacLow *m_macLow;
+};
+
+
 MacLow::MacLow ()
   : m_normalAckTimeoutEvent (),
     m_fastAckTimeoutEvent (),
@@ -268,12 +292,22 @@
 }
 
 void 
+MacLow::SetupPhyMacLowListener (Ptr<WifiPhy> phy)
+{
+  m_phyMacLowListener = new PhyMacLowListener (this); 
+  phy->RegisterListener (m_phyMacLowListener);
+}
+
+
+void 
 MacLow::DoDispose (void)
 {
   NS_LOG_FUNCTION (this);
   CancelAllEvents ();
   m_phy = 0;
   m_stationManager = 0;
+  delete m_phyMacLowListener;
+  m_phyMacLowListener = 0;
 }
 
 void
@@ -339,6 +373,7 @@
   m_phy = phy;
   m_phy->SetReceiveOkCallback (MakeCallback (&MacLow::ReceiveOk, this));
   m_phy->SetReceiveErrorCallback (MakeCallback (&MacLow::ReceiveError, this));
+  SetupPhyMacLowListener(phy); 
 }
 void 
 MacLow::SetWifiRemoteStationManager (Ptr<WifiRemoteStationManager> manager)
@@ -489,6 +524,22 @@
 }
 
 void 
+MacLow::NotifySwitchingStartNow (Time duration)
+{
+  NS_LOG_DEBUG ("switching channel. Cancelling MAC pending events"); 
+  m_stationManager->Reset();
+  CancelAllEvents(); 
+  if (m_navCounterResetCtsMissed.IsRunning ())
+    {
+      m_navCounterResetCtsMissed.Cancel();
+    }
+  m_lastNavStart = Simulator::Now (); 
+  m_lastNavDuration = Seconds (0);
+  m_currentPacket = 0;
+  m_listener = 0;
+}
+
+void 
 MacLow::ReceiveOk (Ptr<Packet> packet, double rxSnr, WifiMode txMode, WifiPreamble preamble)
 {
   NS_LOG_FUNCTION (this << packet << rxSnr << txMode << preamble);
--- a/src/devices/wifi/mac-low.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/mac-low.h	Tue Sep 15 10:47:02 2009 +0200
@@ -356,6 +356,13 @@
    * the MAC layer that a packet was unsuccessfully received.
    */
   void ReceiveError (Ptr<const Packet> packet, double rxSnr);
+  /**
+   * \param duration switching delay duration.
+   *
+   * This method is typically invoked by the PhyMacLowListener to notify
+   * the MAC layer that a channel switching occured.
+   */
+  void NotifySwitchingStartNow (Time duration); 
 private:
   void CancelAllEvents (void);
   uint32_t GetAckSize (void) const;
@@ -402,6 +409,8 @@
   void StartDataTxTimers (void);
   virtual void DoDispose (void);
 
+  void SetupPhyMacLowListener (Ptr<WifiPhy> phy); 
+
   Ptr<WifiPhy> m_phy;
   Ptr<WifiRemoteStationManager> m_stationManager;
   MacLowRxCallback m_rxCallback;
@@ -434,6 +443,9 @@
 
   Time m_lastNavStart;
   Time m_lastNavDuration;
+
+  // Listerner needed to monitor when a channel switching occurs. 
+  class PhyMacLowListener *m_phyMacLowListener; 
 };
 
 } // namespace ns3
--- a/src/devices/wifi/wifi-phy-state-helper.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/wifi-phy-state-helper.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -54,9 +54,11 @@
     m_endTx (Seconds (0)),
     m_endSync (Seconds (0)),
     m_endCcaBusy (Seconds (0)),
+    m_endSwitching (Seconds (0)), 
     m_startTx (Seconds (0)),
     m_startSync (Seconds (0)),
     m_startCcaBusy (Seconds (0)),
+    m_startSwitching (Seconds (0)), 
     m_previousStateChangeTime (Seconds (0))
 {
   NS_LOG_FUNCTION (this);
@@ -104,6 +106,11 @@
 {
   return (GetState () == WifiPhy::TX)?true:false;
 }
+bool 
+WifiPhyStateHelper::IsStateSwitching (void)
+{
+  return (GetState () == WifiPhy::SWITCHING)?true:false;
+}
 
 
 
@@ -128,6 +135,9 @@
   case WifiPhy::CCA_BUSY:
     retval = m_endCcaBusy - Simulator::Now ();
     break;
+  case WifiPhy::SWITCHING: 
+    retval = m_endSwitching - Simulator::Now (); 
+    break;
   case WifiPhy::IDLE:
     retval = Seconds (0);
     break;
@@ -163,6 +173,9 @@
   case WifiPhy::SYNC:
     return "SYNC";
     break;
+  case WifiPhy::SWITCHING: 
+    return "SWITCHING";
+    break;
   default:
     NS_ASSERT (false);
     // quiet compiler
@@ -182,6 +195,10 @@
     {
       return WifiPhy::SYNC;
     } 
+  else if (m_endSwitching > Simulator::Now ())  
+    { 
+      return WifiPhy::SWITCHING;
+    }
   else if (m_endCcaBusy > Simulator::Now ()) 
     {
       return WifiPhy::CCA_BUSY;
@@ -228,6 +245,13 @@
     (*i)->NotifyMaybeCcaBusyStart (duration);
   }
 }
+void 
+WifiPhyStateHelper::NotifySwitchingStart (Time duration)
+{
+  for (Listeners::const_iterator i = m_listeners.begin (); i != m_listeners.end (); i++) {
+    (*i)->NotifySwitchingStart (duration);
+  }
+}
 
 void
 WifiPhyStateHelper::LogPreviousIdleAndCcaBusyStates (void)
@@ -235,11 +259,14 @@
   Time now = Simulator::Now ();
   Time idleStart = Max (m_endCcaBusy, m_endSync);
   idleStart = Max (idleStart, m_endTx);
+  idleStart = Max (idleStart, m_endSwitching); 
   NS_ASSERT (idleStart <= now);
   if (m_endCcaBusy > m_endSync && 
+      m_endCcaBusy > m_endSwitching &&  
       m_endCcaBusy > m_endTx) {
     Time ccaBusyStart = Max (m_endTx, m_endSync);
     ccaBusyStart = Max (ccaBusyStart, m_startCcaBusy);
+    ccaBusyStart = Max (ccaBusyStart, m_endSwitching); 
     m_stateLogger (ccaBusyStart, idleStart - ccaBusyStart, WifiPhy::CCA_BUSY);
   }
   m_stateLogger (idleStart, now - idleStart, WifiPhy::IDLE);
@@ -264,11 +291,13 @@
   case WifiPhy::CCA_BUSY: {
     Time ccaStart = Max (m_endSync, m_endTx);
     ccaStart = Max (ccaStart, m_startCcaBusy);
+    ccaStart = Max (ccaStart, m_endSwitching); 
     m_stateLogger (ccaStart, now - ccaStart, WifiPhy::CCA_BUSY);
   } break;
   case WifiPhy::IDLE:
     LogPreviousIdleAndCcaBusyStates ();
     break;
+  case WifiPhy::SWITCHING: 
   default:
     NS_ASSERT (false);
     break;
@@ -292,8 +321,10 @@
   case WifiPhy::CCA_BUSY: {
     Time ccaStart = Max (m_endSync, m_endTx);
     ccaStart = Max (ccaStart, m_startCcaBusy);
+    ccaStart = Max (ccaStart, m_endSwitching); 
     m_stateLogger (ccaStart, now - ccaStart, WifiPhy::CCA_BUSY);
   } break;
+  case WifiPhy::SWITCHING: 
   case WifiPhy::SYNC:
   case WifiPhy::TX:
     NS_ASSERT (false);
@@ -305,6 +336,49 @@
   m_endSync = now + rxDuration;
   NS_ASSERT (IsStateSync ());
 }
+
+void 
+WifiPhyStateHelper::SwitchToChannelSwitching (Time switchingDuration)
+{ 
+  NotifySwitchingStart (switchingDuration); 
+  Time now = Simulator::Now ();
+  switch (GetState ()) {
+  case WifiPhy::SYNC:
+    /* The packet which is being received as well
+     * as its endSync event are cancelled by the caller.
+     */
+    m_syncing = false;
+    m_stateLogger (m_startSync, now - m_startSync, WifiPhy::SYNC);
+    m_endSync = now;
+    break;
+  case WifiPhy::CCA_BUSY: {
+    Time ccaStart = Max (m_endSync, m_endTx);
+    ccaStart = Max (ccaStart, m_startCcaBusy);
+    ccaStart = Max (ccaStart, m_endSwitching);  
+    m_stateLogger (ccaStart, now - ccaStart, WifiPhy::CCA_BUSY);
+  } break;
+  case WifiPhy::IDLE:
+    LogPreviousIdleAndCcaBusyStates (); 
+    break;
+  case WifiPhy::TX:
+  case WifiPhy::SWITCHING:
+  default:
+    NS_ASSERT (false);
+    break;
+  }
+  
+  if (now < m_endCcaBusy)
+    {
+      m_endCcaBusy = now; 
+    }
+
+  m_stateLogger (now, switchingDuration, WifiPhy::SWITCHING);
+  m_previousStateChangeTime = now;
+  m_startSwitching = now;
+  m_endSwitching = now + switchingDuration;
+  NS_ASSERT (IsStateSwitching ());
+}
+
 void 
 WifiPhyStateHelper::SwitchFromSyncEndOk (Ptr<Packet> packet, double snr, WifiMode mode, enum WifiPreamble preamble)
 {
@@ -348,6 +422,8 @@
   NotifyMaybeCcaBusyStart (duration);
   Time now = Simulator::Now ();
   switch (GetState ()) {
+  case WifiPhy::SWITCHING: 
+    break;
   case WifiPhy::IDLE:
     LogPreviousIdleAndCcaBusyStates ();
   break;
--- a/src/devices/wifi/wifi-phy-state-helper.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/wifi-phy-state-helper.h	Tue Sep 15 10:47:02 2009 +0200
@@ -43,12 +43,14 @@
   bool IsStateBusy (void);
   bool IsStateSync (void);
   bool IsStateTx (void);
+  bool IsStateSwitching (void);
   Time GetStateDuration (void);
   Time GetDelayUntilIdle (void);
   Time GetLastRxStartTime (void) const;
 
   void SwitchToTx (Time txDuration, Ptr<const Packet> packet, WifiMode txMode, WifiPreamble preamble, uint8_t txPower);
   void SwitchToSync (Time syncDuration);
+  void SwitchToChannelSwitching (Time switchingDuration); 
   void SwitchFromSyncEndOk (Ptr<Packet> packet, double snr, WifiMode mode, enum WifiPreamble preamble);
   void SwitchFromSyncEndError (Ptr<const Packet> packet, double snr);
   void SwitchMaybeToCcaBusy (Time duration);
@@ -66,15 +68,18 @@
   void NotifySyncEndOk (void);
   void NotifySyncEndError (void);
   void NotifyMaybeCcaBusyStart (Time duration);
+  void NotifySwitchingStart (Time duration); 
   void DoSwitchFromSync (void);
 
   bool m_syncing;
   Time m_endTx;
   Time m_endSync;
   Time m_endCcaBusy;
+  Time m_endSwitching; 
   Time m_startTx;
   Time m_startSync;
   Time m_startCcaBusy;
+  Time m_startSwitching; 
   Time m_previousStateChangeTime;
 
   Listeners m_listeners;
--- a/src/devices/wifi/wifi-phy.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/wifi-phy.h	Tue Sep 15 10:47:02 2009 +0200
@@ -97,6 +97,14 @@
    * what duration it reported.
    */
   virtual void NotifyMaybeCcaBusyStart (Time duration) = 0;
+  /**
+   * \param duration the expected channel switching duration.
+   *
+   * We do not send any event to notify the end of 
+   * channel switching. Listeners should assume that the
+   * channel implicitely reverts to the idle or busy states.
+   */
+  virtual void NotifySwitchingStart (Time duration) = 0; 
 };
 
 
@@ -127,7 +135,11 @@
     /**
      * The PHY layer is IDLE.
      */
-    IDLE
+    IDLE,
+    /**
+     * The PHY layer is switching to other channel.
+     */
+    SWITCHING
   };
   /**
    * arg1: packet received successfully
--- a/src/devices/wifi/yans-wifi-phy.cc	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/yans-wifi-phy.cc	Tue Sep 15 10:47:02 2009 +0200
@@ -302,27 +302,53 @@
 {
   m_channel = channel;
   m_channel->Add (this);
-  m_channelId = 1;      // always start on channel starting frequency (channel 1)
+  m_channelNumber = 1;      // always start on channel starting frequency (channel 1)
 }
 
 void 
 YansWifiPhy::SetChannelNumber (uint16_t nch)
 {
-  // TODO implement channel switching state machine here
-  DoSetChannelNumber (nch);
-}
+  NS_ASSERT(!IsStateSwitching()); 
+  switch (m_state->GetState ()) {
+  case YansWifiPhy::SYNC:
+    NS_LOG_DEBUG ("drop packet because of channel switching while reception");
+    m_endSyncEvent.Cancel();
+    goto switchChannel;
+    break;
+  case YansWifiPhy::TX:
+      NS_LOG_DEBUG ("channel switching postponed until end of current transmission");
+      Simulator::Schedule (GetDelayUntilIdle(), &YansWifiPhy::SetChannelNumber, this, nch);
+    break;
+  case YansWifiPhy::CCA_BUSY:
+  case YansWifiPhy::IDLE:
+    goto switchChannel;
+    break;
+  default:
+    NS_ASSERT (false);
+    break;
+  }
 
-void
-YansWifiPhy::DoSetChannelNumber (uint16_t nch)
-{
-  NS_LOG_DEBUG("switching channel " << m_channelId << " -> " << nch);
-  m_channelId = nch;
+  return;
+
+  switchChannel: 
+
+  NS_LOG_DEBUG("switching channel " << m_channelNumber << " -> " << nch);
+  m_state->SwitchToChannelSwitching(m_channelSwitchDelay); 
+  m_interference.EraseEvents(); 
+  /*
+   * Needed here to be able to correctly sensed the medium for the first
+   * time after the switching. The actual switching is not performed until
+   * after m_channelSwitchDelay. Packets received during the switching
+   * state are added to the event list and are employed later to figure
+   * out the state of the medium after the switching.
+   */
+  m_channelNumber = nch;
 }
 
 uint16_t 
 YansWifiPhy::GetChannelNumber() const
 {
-  return m_channelId;
+  return m_channelNumber;
 }
 
 double
@@ -361,6 +387,24 @@
                               rxPowerW);
 
   switch (m_state->GetState ()) {
+  case YansWifiPhy::SWITCHING: 
+    NS_LOG_DEBUG ("drop packet because of channel switching");
+    NotifyRxDrop (packet);
+    /*
+     * Packets received on the upcoming channel are added to the event list
+     * during the switching state. This way the medium can be correctly sensed
+     * when the device listens to the channel for the first time after the
+     * switching e.g. after channel switching, the channel may be sensed as
+     * busy due to other devices' tramissions started before the end of
+     * the switching.
+     */
+    if (endRx > Simulator::Now () + m_state->GetDelayUntilIdle ()) 
+      {
+        // that packet will be noise _after_ the completion of the
+        // channel switching.
+        goto maybeCcaBusy;
+      }
+    break;
   case YansWifiPhy::SYNC:
     NS_LOG_DEBUG ("drop packet because already in Sync (power="<<
                   rxPowerW<<"W)");
@@ -431,7 +475,7 @@
    *    prevent it.
    *  - we are idle
    */
-  NS_ASSERT (!m_state->IsStateTx ());
+  NS_ASSERT (!m_state->IsStateTx () && !m_state->IsStateSwitching ());
 
   Time txDuration = CalculateTxDuration (packet->GetSize (), txMode, preamble);
   if (m_state->IsStateSync ())
@@ -563,6 +607,11 @@
 {
   return m_state->IsStateTx ();
 }
+bool 
+YansWifiPhy::IsStateSwitching (void)
+{
+  return m_state->IsStateSwitching ();
+}
 
 Time
 YansWifiPhy::GetStateDuration (void)
--- a/src/devices/wifi/yans-wifi-phy.h	Tue Sep 15 00:05:36 2009 -0700
+++ b/src/devices/wifi/yans-wifi-phy.h	Tue Sep 15 10:47:02 2009 +0200
@@ -124,6 +124,7 @@
   virtual bool IsStateBusy (void);
   virtual bool IsStateSync (void);
   virtual bool IsStateTx (void);
+  virtual bool IsStateSwitching (void); 
   virtual Time GetStateDuration (void);
   virtual Time GetDelayUntilIdle (void);
   virtual Time GetLastRxStartTime (void) const;
@@ -152,7 +153,6 @@
   double RatioToDb (double ratio) const;
   double GetPowerDbm (uint8_t power) const;
   void EndSync (Ptr<Packet> packet, Ptr<InterferenceHelper::Event> event);
-  void DoSetChannelNumber(uint16_t id);
 
 private:
   double   m_edThresholdW;
@@ -164,7 +164,7 @@
   uint32_t m_nTxPower;
 
   Ptr<YansWifiChannel> m_channel;
-  uint16_t m_channelId;
+  uint16_t m_channelNumber;
   Ptr<Object> m_device;
   Ptr<Object> m_mobility;
   Modes m_modes;