diff --git a/AUTHORS b/AUTHORS
index 87d19449f..3353e7652 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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)
diff --git a/CHANGES.html b/CHANGES.html
index 997b0e95f..d4350ab7d 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -107,6 +107,9 @@ router solicitation, DAD
- 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).
+
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 102e2bdb2..bf1181f79 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -30,8 +30,16 @@ http://www.nsnam.org/wiki/index.php/Installation
New user-visible features
-------------------------
- a) Add an implementation of the minstrel rate control algorithm
- (Duy Nguyen for gsoc)
+ a) 802.11 models:
+ - Add an implementation of the minstrel rate control algorithm
+ (Duy Nguyen for gsoc)
+ - AthstatsHelper: enables the wifi device to produce periodic
+ reports similar to the ones generated by madwifi's
+ athstats tool (Nicola Baldo)
+ - 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)
b) IPv6 models:
- IPv6 interface;
@@ -46,17 +54,10 @@ New user-visible features
- Examples (ping6, simple-routing-ping6, radvd, radvd-two-prefix,
icmpv6-redirect).
- c) added AthstatsHelper, which enables the wifi device to produce
- periodic reports similar to the ones generated by madwifi's
- athstats tool (Nicola Baldo)
-
- d) Wireless Mesh Networking models:
+ c) Wireless Mesh Networking models:
- General multi-interface mesh stack infrastructure (devices/mesh module).
- IEEE 802.11s (Draft 3.0) model including Peering Management Protocol and HWMP.
- Forwarding Layer for Meshing (FLAME) protocol.
-
- e) 802.11 enhancements:
- - 10MHz and 5MHz channel width supported by 802.11a model (Ramon Bauza and Kirill Andreev)
API changes from ns-3.5
-----------------------
diff --git a/doc/manual/callbacks.texi b/doc/manual/callbacks.texi
index 36dd2293e..1abcedddc 100644
--- a/doc/manual/callbacks.texi
+++ b/doc/manual/callbacks.texi
@@ -267,6 +267,10 @@ implement the specific operator() method,
};
@end verbatim
+@emph{N.B. The previous code is not real ns-3 code. It is simplistic example
+code used only to illustrate the concepts involved and to help you understand
+the system more. Do not expect to find this code anywhere in the ns-3 tree}
+
Notice that there are two variables defined in the class above. The m_p
variable is the object pointer and m_pmi is the variable containing the
address of the function to execute.
diff --git a/doc/manual/tracing.texi b/doc/manual/tracing.texi
index d445dfc12..a8b72797a 100644
--- a/doc/manual/tracing.texi
+++ b/doc/manual/tracing.texi
@@ -112,7 +112,7 @@ functions.
It will be useful to go walk a quick example just to reinforce what we've said.
-@verbose
+@verbatim
#include ``ns3/object.h''
#include ``ns3/uinteger.h''
#include ``ns3/traced-value.h''
@@ -121,7 +121,7 @@ It will be useful to go walk a quick example just to reinforce what we've said.
#include
using namespace ns3;
-@end verbose
+@end verbatim
The first thing to do is include the required files. As mentioned above, the
trace system makes heavy use of the Object and Attribute systems. The first
@@ -167,7 +167,7 @@ the @code{TracedValue} declaration.
The @code{.AddTraceSource} provides the ``hooks'' used for connecting the trace
source to the outside world. The @code{TracedValue} declaration provides the
-infrastructure that overloads the operators above and drives the callback
+infrastructure that overloads the operators mentioned above and drives the callback
process.
@verbatim
@@ -201,9 +201,11 @@ The next step, the @code{TraceConnectWithoutContext}, forms the connection
between the trace source and the trace sink. Notice the @code{MakeCallback}
template function. Recall from the Callback section that this creates the
specialized functor responsible for providing the overloaded @code{operator()}
-used to ``fire'' the callback. The @code{TraceConnectWithoutContext}, takes
-a string parameter that provides the name of the Attribute assigned to the
-trace source. Let's ignore the bit about context for now.
+used to ``fire'' the callback. The overloaded operators (++, --, etc.) will
+use this @code{operator()} to actually invoke the callback. The
+@code{TraceConnectWithoutContext}, takes a string parameter that provides
+the name of the Attribute assigned to the trace source. Let's ignore the bit
+about context for now since it is not important yet.
Finally, the line,
@@ -247,27 +249,27 @@ For example, one might find something that looks like the following in the syste
@end verbatim
This should look very familiar. It is the same thing as the previous example,
-except that a static member function of @code{Config} is being called instead of
-a method on @code{Object}, and instead of an @code{Attribute} name, a path is
+except that a static member function of class @code{Config} is being called instead
+of a method on @code{Object}; and instead of an @code{Attribute} name, a path is
being provided.
The first thing to do is to read the path backward. The last segment of the path
must be an @code{Attribute} of an @code{Object}. In fact, if you had a pointer to
-the @code{Object} that has the @code{Attribute} handy, you could write this just like
-the previous example:
+the @code{Object} that has the ``CongestionWindow'' @code{Attribute} handy (call it
+@code{theObject}), you could write this just like the previous example:
@verbatim
void CwndTracer (uint32_t oldval, uint32_t newval) {}
...
- object->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
+ theObject->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
@end verbatim
-And it turns out that @code{Config::ConnectWithoutContext} does exactly that. This
-function takes a path that represents a chain of @code{Object} pointers and follows
+It turns out that the code for @code{Config::ConnectWithoutContext} does exactly that.
+This function takes a path that represents a chain of @code{Object} pointers and follows
them until it gets to the end of the path and interprets the last segment as an
-@code{Attribute} on the last object.
+@code{Attribute} on the last object. Let's walk through what happens.
The leading ``/'' character in the path refers to a so-called namespace. One of the
predefined namespaces in the config system is ``NodeList'' which is a list of all of
diff --git a/examples/wifi-ap.cc b/examples/wifi-ap.cc
index f7f169ab8..f84395fa4 100644
--- a/examples/wifi-ap.cc
+++ b/examples/wifi-ap.cc
@@ -62,6 +62,9 @@ PhyStateTrace (std::string context, Time start, Time duration, enum WifiPhy::Sta
{
std::cout << " state=";
switch (state) {
+ case WifiPhy::SWITCHING:
+ std::cout << "switchng";
+ break;
case WifiPhy::TX:
std::cout << "tx ";
break;
diff --git a/samples/main-random-topology.cc b/samples/main-random-topology.cc
index 4c7e7b512..62e2d5fbb 100644
--- a/samples/main-random-topology.cc
+++ b/samples/main-random-topology.cc
@@ -32,7 +32,7 @@ int main (int argc, char *argv[])
mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
mobility.Install (c);
- Config::Connect ("/NodeList/*/$ns3::MobilityModelNotifier/CourseChange",
+ Config::Connect ("/NodeList/*/$ns3::MobilityModel/CourseChange",
MakeCallback (&CourseChange));
Simulator::Stop (Seconds (100.0));
diff --git a/src/devices/mesh/dot11s/dot11s-mac-header.cc b/src/devices/mesh/dot11s/dot11s-mac-header.cc
index 482dd95e1..215e2ecb4 100644
--- a/src/devices/mesh/dot11s/dot11s-mac-header.cc
+++ b/src/devices/mesh/dot11s/dot11s-mac-header.cc
@@ -252,6 +252,7 @@ WifiMeshActionHeader::GetAction ()
return retval;
default:
NS_FATAL_ERROR ("Unknown mesh peering management action code");
+ retval.peerLink = PEER_LINK_OPEN; /* quiet compiler */
return retval;
}
case MESH_PATH_SELECTION:
@@ -262,6 +263,7 @@ WifiMeshActionHeader::GetAction ()
return retval;
default:
NS_FATAL_ERROR ("Unknown mesh path selection action code");
+ retval.peerLink = PEER_LINK_OPEN; /* quiet compiler */
return retval;
}
case MESH_LINK_METRIC:
@@ -272,6 +274,7 @@ WifiMeshActionHeader::GetAction ()
// not yet supported
default:
NS_FATAL_ERROR ("Unsupported mesh action");
+ retval.peerLink = PEER_LINK_OPEN; /* quiet compiler */
return retval;
}
}
diff --git a/src/devices/mesh/dot11s/peer-management-protocol.cc b/src/devices/mesh/dot11s/peer-management-protocol.cc
index fe144f0e5..cff61506a 100644
--- a/src/devices/mesh/dot11s/peer-management-protocol.cc
+++ b/src/devices/mesh/dot11s/peer-management-protocol.cc
@@ -241,7 +241,7 @@ PeerManagementProtocol::ReceivePeerLinkFrame (uint32_t interface, Mac48Address p
Ptr peerLink = FindPeerLink (interface, peerAddress);
if (peerManagementElement.SubtypeIsOpen ())
{
- PmpReasonCode reasonCode;
+ PmpReasonCode reasonCode (REASON11S_RESERVED);
bool reject = !(ShouldAcceptOpen (interface, peerAddress, reasonCode));
if (peerLink == 0)
{
diff --git a/src/devices/wifi/dca-txop.cc b/src/devices/wifi/dca-txop.cc
index 6b4a64f43..e19363591 100644
--- a/src/devices/wifi/dca-txop.cc
+++ b/src/devices/wifi/dca-txop.cc
@@ -58,6 +58,9 @@ private:
virtual void DoNotifyCollision (void) {
m_txop->NotifyCollision ();
}
+ virtual void DoNotifyChannelSwitching (void) {
+ m_txop->NotifyChannelSwitching ();
+ }
DcaTxop *m_txop;
};
@@ -446,6 +449,13 @@ DcaTxop::NotifyCollision (void)
RestartAccessIfNeeded ();
}
+void
+DcaTxop::NotifyChannelSwitching (void)
+{
+ m_queue->Flush();
+ m_currentPacket = 0;
+}
+
void
DcaTxop::GotCts (double snr, WifiMode txMode)
{
diff --git a/src/devices/wifi/dca-txop.h b/src/devices/wifi/dca-txop.h
index 4fd697230..69b8a050f 100644
--- a/src/devices/wifi/dca-txop.h
+++ b/src/devices/wifi/dca-txop.h
@@ -127,6 +127,10 @@ private:
void NotifyAccessGranted (void);
void NotifyInternalCollision (void);
void NotifyCollision (void);
+ /**
+ * When a channel switching occurs, enqueued packets are removed.
+ */
+ void NotifyChannelSwitching (void);
/* event handlers */
void GotCts (double snr, WifiMode txMode);
void MissedCts (void);
diff --git a/src/devices/wifi/dcf-manager-test.cc b/src/devices/wifi/dcf-manager-test.cc
index b94f0ed3f..8972e527c 100644
--- a/src/devices/wifi/dcf-manager-test.cc
+++ b/src/devices/wifi/dcf-manager-test.cc
@@ -38,6 +38,7 @@ private:
virtual void DoNotifyAccessGranted (void);
virtual void DoNotifyInternalCollision (void);
virtual void DoNotifyCollision (void);
+ virtual void DoNotifyChannelSwitching (void);
typedef std::pair ExpectedGrant;
typedef std::list ExpectedGrants;
@@ -65,6 +66,7 @@ public:
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 @@ private:
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 DcfStates;
@@ -122,7 +127,11 @@ DcfStateTest::DoNotifyCollision (void)
{
m_test->NotifyCollision (m_i);
}
-
+void
+DcfStateTest::DoNotifyChannelSwitching (void)
+{
+ m_test->NotifyChannelSwitching (m_i);
+}
DcfManagerTest::DcfManagerTest ()
@@ -182,7 +191,22 @@ DcfManagerTest::NotifyCollision (uint32_t i)
m_result = result;
}
}
-
+void
+DcfManagerTest::NotifyChannelSwitching (uint32_t i)
+{
+ DcfStateTest *state = m_dcfStates[i];
+ bool result = true;
+ if (!state->m_expectedGrants.empty ())
+ {
+ std::pair 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 @@ DcfManagerTest::DoAccessRequest (uint64_t txTime, uint64_t expectedGrantTime, Dc
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 @@ DcfManagerTest::RunTests (void)
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;
}
diff --git a/src/devices/wifi/dcf-manager.cc b/src/devices/wifi/dcf-manager.cc
index 320bf0621..467caf77f 100644
--- a/src/devices/wifi/dcf-manager.cc
+++ b/src/devices/wifi/dcf-manager.cc
@@ -154,6 +154,11 @@ DcfState::NotifyInternalCollision (void)
{
DoNotifyInternalCollision ();
}
+void
+DcfState::NotifyChannelSwitching (void)
+{
+ DoNotifyChannelSwitching ();
+}
/***************************************************************
@@ -211,6 +216,9 @@ public:
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 @@ DcfManager::DcfManager ()
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 @@ DcfManager::MostRecent (Time a, Time b, Time c, Time d, Time e, Time f) const
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 @@ DcfManager::GetAccessGrantStart (void) const
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 @@ DcfManager::NotifyMaybeCcaBusyStartNow (Time duration)
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 "<NotifyCollision ();
}
+ virtual void DoNotifyChannelSwitching (void) {
+ m_txop->NotifyChannelSwitching ();
+ }
EdcaTxopN *m_txop;
};
@@ -427,6 +430,13 @@ EdcaTxopN::MissedCts (void)
RestartAccessIfNeeded ();
}
+void
+EdcaTxopN::NotifyChannelSwitching (void)
+{
+ m_queue->Flush();
+ m_currentPacket = 0;
+}
+
void
EdcaTxopN::Queue (Ptr packet, WifiMacHeader const &hdr)
{
diff --git a/src/devices/wifi/edca-txop-n.h b/src/devices/wifi/edca-txop-n.h
index 216c77bb1..eb80a4fc9 100644
--- a/src/devices/wifi/edca-txop-n.h
+++ b/src/devices/wifi/edca-txop-n.h
@@ -101,6 +101,10 @@ public:
void NotifyAccessGranted (void);
void NotifyInternalCollision (void);
void NotifyCollision (void);
+ /**
+ * When a channel switching occurs, enqueued packets are removed.
+ */
+ void NotifyChannelSwitching (void);
/*event handlers*/
void GotCts (double snr, WifiMode txMode);
diff --git a/src/devices/wifi/interference-helper.cc b/src/devices/wifi/interference-helper.cc
index c375d095e..4e0301ba0 100644
--- a/src/devices/wifi/interference-helper.cc
+++ b/src/devices/wifi/interference-helper.cc
@@ -603,6 +603,10 @@ InterferenceHelper::CalculateSnrPer (Ptr event)
return snrPer;
}
-
+void
+InterferenceHelper::EraseEvents (void)
+{
+ m_events.erase (m_events.begin (), m_events.end ());
+}
} // namespace ns3
diff --git a/src/devices/wifi/interference-helper.h b/src/devices/wifi/interference-helper.h
index 2140cb12a..0ae37b280 100644
--- a/src/devices/wifi/interference-helper.h
+++ b/src/devices/wifi/interference-helper.h
@@ -95,6 +95,7 @@ public:
Time duration, double rxPower);
struct InterferenceHelper::SnrPer CalculateSnrPer (Ptr event);
+ void EraseEvents (void);
private:
class NiChange {
public:
diff --git a/src/devices/wifi/mac-low.cc b/src/devices/wifi/mac-low.cc
index bc0ee2035..c18a7638f 100644
--- a/src/devices/wifi/mac-low.cc
+++ b/src/devices/wifi/mac-low.cc
@@ -244,6 +244,30 @@ std::ostream &operator << (std::ostream &os, const MacLowTransmissionParameters
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 (),
@@ -267,6 +291,14 @@ MacLow::~MacLow ()
NS_LOG_FUNCTION (this);
}
+void
+MacLow::SetupPhyMacLowListener (Ptr phy)
+{
+ m_phyMacLowListener = new PhyMacLowListener (this);
+ phy->RegisterListener (m_phyMacLowListener);
+}
+
+
void
MacLow::DoDispose (void)
{
@@ -274,6 +306,8 @@ MacLow::DoDispose (void)
CancelAllEvents ();
m_phy = 0;
m_stationManager = 0;
+ delete m_phyMacLowListener;
+ m_phyMacLowListener = 0;
}
void
@@ -339,6 +373,7 @@ MacLow::SetPhy (Ptr phy)
m_phy = phy;
m_phy->SetReceiveOkCallback (MakeCallback (&MacLow::ReceiveOk, this));
m_phy->SetReceiveErrorCallback (MakeCallback (&MacLow::ReceiveError, this));
+ SetupPhyMacLowListener(phy);
}
void
MacLow::SetWifiRemoteStationManager (Ptr manager)
@@ -488,6 +523,22 @@ MacLow::ReceiveError (Ptr packet, double rxSnr)
return;
}
+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, double rxSnr, WifiMode txMode, WifiPreamble preamble)
{
diff --git a/src/devices/wifi/mac-low.h b/src/devices/wifi/mac-low.h
index 7f1bca086..7b090fbd9 100644
--- a/src/devices/wifi/mac-low.h
+++ b/src/devices/wifi/mac-low.h
@@ -356,6 +356,14 @@ public:
* the MAC layer that a packet was unsuccessfully received.
*/
void ReceiveError (Ptr 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. When a channel switching
+ * occurs, pending MAC transmissions (RTS, CTS, DATA and ACK) are cancelled.
+ */
+ void NotifySwitchingStartNow (Time duration);
private:
void CancelAllEvents (void);
uint32_t GetAckSize (void) const;
@@ -402,6 +410,8 @@ private:
void StartDataTxTimers (void);
virtual void DoDispose (void);
+ void SetupPhyMacLowListener (Ptr phy);
+
Ptr m_phy;
Ptr m_stationManager;
MacLowRxCallback m_rxCallback;
@@ -434,6 +444,9 @@ private:
Time m_lastNavStart;
Time m_lastNavDuration;
+
+ // Listerner needed to monitor when a channel switching occurs.
+ class PhyMacLowListener *m_phyMacLowListener;
};
} // namespace ns3
diff --git a/src/devices/wifi/wifi-phy-state-helper.cc b/src/devices/wifi/wifi-phy-state-helper.cc
index 5626e4281..9a4d1cae8 100644
--- a/src/devices/wifi/wifi-phy-state-helper.cc
+++ b/src/devices/wifi/wifi-phy-state-helper.cc
@@ -54,9 +54,11 @@ WifiPhyStateHelper::WifiPhyStateHelper ()
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 @@ WifiPhyStateHelper::IsStateTx (void)
{
return (GetState () == WifiPhy::TX)?true:false;
}
+bool
+WifiPhyStateHelper::IsStateSwitching (void)
+{
+ return (GetState () == WifiPhy::SWITCHING)?true:false;
+}
@@ -128,6 +135,9 @@ WifiPhyStateHelper::GetDelayUntilIdle (void)
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 @@ WifiPhyStateHelper::StateToString (enum WifiPhy::State state)
case WifiPhy::SYNC:
return "SYNC";
break;
+ case WifiPhy::SWITCHING:
+ return "SWITCHING";
+ break;
default:
NS_ASSERT (false);
// quiet compiler
@@ -182,6 +195,10 @@ WifiPhyStateHelper::GetState (void)
{
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 @@ WifiPhyStateHelper::NotifyMaybeCcaBusyStart (Time duration)
(*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 @@ WifiPhyStateHelper::LogPreviousIdleAndCcaBusyStates (void)
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 @@ WifiPhyStateHelper::SwitchToTx (Time txDuration, Ptr packet, WifiM
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 @@ WifiPhyStateHelper::SwitchToSync (Time rxDuration)
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 @@ WifiPhyStateHelper::SwitchToSync (Time rxDuration)
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, double snr, WifiMode mode, enum WifiPreamble preamble)
{
@@ -348,6 +422,8 @@ WifiPhyStateHelper::SwitchMaybeToCcaBusy (Time duration)
NotifyMaybeCcaBusyStart (duration);
Time now = Simulator::Now ();
switch (GetState ()) {
+ case WifiPhy::SWITCHING:
+ break;
case WifiPhy::IDLE:
LogPreviousIdleAndCcaBusyStates ();
break;
diff --git a/src/devices/wifi/wifi-phy-state-helper.h b/src/devices/wifi/wifi-phy-state-helper.h
index b28561550..de8443b18 100644
--- a/src/devices/wifi/wifi-phy-state-helper.h
+++ b/src/devices/wifi/wifi-phy-state-helper.h
@@ -43,12 +43,14 @@ public:
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 packet, WifiMode txMode, WifiPreamble preamble, uint8_t txPower);
void SwitchToSync (Time syncDuration);
+ void SwitchToChannelSwitching (Time switchingDuration);
void SwitchFromSyncEndOk (Ptr packet, double snr, WifiMode mode, enum WifiPreamble preamble);
void SwitchFromSyncEndError (Ptr packet, double snr);
void SwitchMaybeToCcaBusy (Time duration);
@@ -66,15 +68,18 @@ private:
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;
diff --git a/src/devices/wifi/wifi-phy.h b/src/devices/wifi/wifi-phy.h
index d75ff70da..2e2092748 100644
--- a/src/devices/wifi/wifi-phy.h
+++ b/src/devices/wifi/wifi-phy.h
@@ -97,6 +97,14 @@ public:
* 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 @@ public:
/**
* The PHY layer is IDLE.
*/
- IDLE
+ IDLE,
+ /**
+ * The PHY layer is switching to other channel.
+ */
+ SWITCHING
};
/**
* arg1: packet received successfully
diff --git a/src/devices/wifi/yans-wifi-channel.h b/src/devices/wifi/yans-wifi-channel.h
index 6bd05c4e0..ab6a4d526 100644
--- a/src/devices/wifi/yans-wifi-channel.h
+++ b/src/devices/wifi/yans-wifi-channel.h
@@ -76,7 +76,9 @@ public:
* \param preamble the preamble associated to the packet
*
* This method should not be invoked by normal users. It is
- * currently invoked only from WifiPhy::Send.
+ * currently invoked only from WifiPhy::Send. YansWifiChannel
+ * delivers packets only between PHYs with the same m_channelNumber,
+ * e.g. PHYs that are operating on the same channel.
*/
void Send (Ptr sender, Ptr packet, double txPowerDbm,
WifiMode wifiMode, WifiPreamble preamble) const;
diff --git a/src/devices/wifi/yans-wifi-phy.cc b/src/devices/wifi/yans-wifi-phy.cc
index eead8afae..7efbf6739 100644
--- a/src/devices/wifi/yans-wifi-phy.cc
+++ b/src/devices/wifi/yans-wifi-phy.cc
@@ -302,27 +302,53 @@ YansWifiPhy::SetChannel (Ptr channel)
{
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 @@ YansWifiPhy::StartReceivePacket (Ptr packet,
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 @@ YansWifiPhy::SendPacket (Ptr packet, WifiMode txMode, WifiPreamble
* 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 @@ YansWifiPhy::IsStateTx (void)
{
return m_state->IsStateTx ();
}
+bool
+YansWifiPhy::IsStateSwitching (void)
+{
+ return m_state->IsStateSwitching ();
+}
Time
YansWifiPhy::GetStateDuration (void)
diff --git a/src/devices/wifi/yans-wifi-phy.h b/src/devices/wifi/yans-wifi-phy.h
index 34c3313e7..948d97bfb 100644
--- a/src/devices/wifi/yans-wifi-phy.h
+++ b/src/devices/wifi/yans-wifi-phy.h
@@ -77,6 +77,13 @@ public:
*
* where Starting channel frequency is standard-dependent, see SetStandard()
* as defined in IEEE 802.11-2007 17.3.8.3.2.
+ *
+ * YansWifiPhy can switch among different channels. Basically, YansWifiPhy
+ * has a private attribute m_channelNumber that identifies the channel the
+ * PHY operates on. Channel switching cannot interrupt an ongoing transmission.
+ * When PHY is in TX state, the channel switching is postponed until the end
+ * of the current transmission. When the PHY is in SYNC state, the channel
+ * switching causes the drop of the sync packet.
*/
void SetChannelNumber (uint16_t id);
/// Return current channel number, see SetChannelNumber()
@@ -124,6 +131,7 @@ public:
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 +160,6 @@ private:
double RatioToDb (double ratio) const;
double GetPowerDbm (uint8_t power) const;
void EndSync (Ptr packet, Ptr event);
- void DoSetChannelNumber(uint16_t id);
private:
double m_edThresholdW;
@@ -164,7 +171,7 @@ private:
uint32_t m_nTxPower;
Ptr m_channel;
- uint16_t m_channelId;
+ uint16_t m_channelNumber;
Ptr