diff --git a/bindings/python/wscript b/bindings/python/wscript index 41e3a6407..a555a47bc 100644 --- a/bindings/python/wscript +++ b/bindings/python/wscript @@ -15,7 +15,7 @@ import Build import Utils ## https://launchpad.net/pybindgen/ -REQUIRED_PYBINDGEN_VERSION = (0, 13, 0, 744) +REQUIRED_PYBINDGEN_VERSION = (0, 13, 0, 745) REQUIRED_PYGCCXML_VERSION = (0, 9, 5) diff --git a/doc/doxygen.conf b/doc/doxygen.conf index 8af9dc443..e16f5b0a3 100644 --- a/doc/doxygen.conf +++ b/doc/doxygen.conf @@ -496,6 +496,7 @@ WARN_LOGFILE = INPUT = doc/modules \ doc/main.h \ doc/introspected-doxygen.h \ + utils \ src # This tag can be used to specify the character encoding of the source files that diff --git a/doc/modules b/doc/modules index 36b640ee5..b7417e476 100644 --- a/doc/modules +++ b/doc/modules @@ -60,5 +60,9 @@ * @defgroup constants Constants * @brief Constants you can change * + * @defgroup utils Utils + * @brief The utils directory is for various programs and scripts related + * to code coverage, test suites, style checking, and benchmarking. + * * @defgroup contrib Contrib */ diff --git a/examples/wireless/wifi-blockack.cc b/examples/wireless/wifi-blockack.cc new file mode 100644 index 000000000..350cb3482 --- /dev/null +++ b/examples/wireless/wifi-blockack.cc @@ -0,0 +1,141 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ + +/** + * This is a simple example in order to show how 802.11n compressed block ack mechanism could be used. + * + * Network topology: + * + * Wifi 192.168.1.0 + * + * AP + * * * + * | | + * n1 n2 + * + * In this example a QoS sta sends UDP datagram packets to access point. On the access point + * there is no application installed so it replies to every packet with an ICMP frame. However + * our attention is on originator sta n1. We have set blockAckThreshold (mininum number of packets to use + * block ack) to 2 so if there are in the BestEffort queue more than 2 packets a block ack will be + * negotiated. We also set a timeout for block ack inactivity to 3 blocks of 1024 microseconds. This timer is + * reset when: + * - the originator receives a block ack frame. + * - the recipient receives a block ack request or a MPDU with ack policy Block Ack. + */ +#include "ns3/core-module.h" +#include "ns3/simulator-module.h" +#include "ns3/node-module.h" +#include "ns3/helper-module.h" +#include "ns3/global-routing-module.h" +#include "ns3/wifi-module.h" +#include "ns3/mobility-module.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("Test-block-ack"); + +int main (int argc, char const* argv[]) +{ + LogComponentEnable ("EdcaTxopN", LOG_LEVEL_DEBUG); + LogComponentEnable ("BlockAckManager", LOG_LEVEL_INFO); + + Ptr sta = CreateObject (); + Ptr ap = CreateObject (); + + YansWifiChannelHelper channel = YansWifiChannelHelper::Default (); + YansWifiPhyHelper phy = YansWifiPhyHelper::Default (); + phy.SetChannel (channel.Create ()); + + WifiHelper wifi = WifiHelper::Default (); + QosWifiMacHelper mac = QosWifiMacHelper::Default (); + /* disable fragmentation */ + wifi.SetRemoteStationManager ("ns3::AarfWifiManager", "FragmentationThreshold", UintegerValue (2500)); + + Ssid ssid ("My-network"); + + mac.SetType ("ns3::QstaWifiMac", "Ssid" , SsidValue (ssid), "ActiveProbing", BooleanValue (false)); + NetDeviceContainer staDevice = wifi.Install (phy, mac, sta); + + mac.SetType ("ns3::QapWifiMac", "Ssid", SsidValue (ssid), "BeaconGeneration", BooleanValue (true), + "BeaconInterval", TimeValue (Seconds (2.5))); + NetDeviceContainer apDevice = wifi.Install (phy, mac, ap); + + /* setting blockack threshold for sta's BE queue */ + Config::Set ("/NodeList/0/DeviceList/0/Mac/BE_EdcaTxopN/BlockAckThreshold", UintegerValue (2)); + /* setting block inactivity timeout to 3*1024 = 3072 microseconds */ + //Config::Set ("/NodeList/0/DeviceList/0/Mac/BE_EdcaTxopN/BlockAckInactivityTimeout", UintegerValue (3)); + + /* Setting mobility model */ + MobilityHelper mobility; + + mobility.SetPositionAllocator ("ns3::GridPositionAllocator", + "MinX", DoubleValue (0.0), + "MinY", DoubleValue (0.0), + "DeltaX", DoubleValue (5.0), + "DeltaY", DoubleValue (10.0), + "GridWidth", UintegerValue (3), + "LayoutType", StringValue ("RowFirst")); + + mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel", + "Bounds", RectangleValue (Rectangle (-50, 50, -50, 50))); + mobility.Install (sta); + + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ap); + + /* Internet stack*/ + InternetStackHelper stack; + stack.Install (sta); + stack.Install (ap); + + Ipv4AddressHelper address; + + address.SetBase ("192.168.1.0", "255.255.255.0"); + Ipv4InterfaceContainer staIf; + Ipv4InterfaceContainer apIf; + staIf = address.Assign (staDevice); + apIf = address.Assign (apDevice); + + /* Setting applications */ + + uint16_t port = 9; + + DataRate dataRate ("1Mb/s"); + OnOffHelper onOff ("ns3::UdpSocketFactory", Address (InetSocketAddress (apIf.GetAddress (0), port))); + onOff.SetAttribute ("DataRate", DataRateValue (dataRate)); + onOff.SetAttribute ("OnTime", RandomVariableValue (ConstantVariable (0.01))); + onOff.SetAttribute ("OffTime", RandomVariableValue (ConstantVariable (8))); + onOff.SetAttribute ("PacketSize", UintegerValue (50)); + + ApplicationContainer staApps = onOff.Install (sta); + + staApps.Start (Seconds (1.0)); + staApps.Stop (Seconds (10.0)); + + Ipv4GlobalRoutingHelper::PopulateRoutingTables (); + + Simulator::Stop (Seconds (10.0)); + + phy.EnablePcap ("test-blockack-2", ap->GetId (), 0); + Simulator::Run (); + Simulator::Destroy (); + + return 0; +} diff --git a/examples/wireless/wscript b/examples/wireless/wscript index bcb45f2ab..241eb1e22 100644 --- a/examples/wireless/wscript +++ b/examples/wireless/wscript @@ -34,3 +34,6 @@ def build(bld): obj = bld.create_ns3_program('wifi-simple-interference', ['core', 'simulator', 'mobility', 'wifi']) obj.source = 'wifi-simple-interference.cc' + + obj = bld.create_ns3_program('wifi-blockack', ['core', 'simulator', 'mobility', 'wifi']) + obj.source = 'wifi-blockack.cc' diff --git a/src/devices/wifi/jakes-propagation-loss-model.cc b/src/common/jakes-propagation-loss-model.cc similarity index 100% rename from src/devices/wifi/jakes-propagation-loss-model.cc rename to src/common/jakes-propagation-loss-model.cc diff --git a/src/devices/wifi/jakes-propagation-loss-model.h b/src/common/jakes-propagation-loss-model.h similarity index 100% rename from src/devices/wifi/jakes-propagation-loss-model.h rename to src/common/jakes-propagation-loss-model.h diff --git a/src/devices/wifi/propagation-delay-model.cc b/src/common/propagation-delay-model.cc similarity index 100% rename from src/devices/wifi/propagation-delay-model.cc rename to src/common/propagation-delay-model.cc diff --git a/src/devices/wifi/propagation-delay-model.h b/src/common/propagation-delay-model.h similarity index 100% rename from src/devices/wifi/propagation-delay-model.h rename to src/common/propagation-delay-model.h diff --git a/src/devices/wifi/propagation-loss-model-test-suite.cc b/src/common/propagation-loss-model-test-suite.cc similarity index 100% rename from src/devices/wifi/propagation-loss-model-test-suite.cc rename to src/common/propagation-loss-model-test-suite.cc diff --git a/src/devices/wifi/propagation-loss-model.cc b/src/common/propagation-loss-model.cc similarity index 100% rename from src/devices/wifi/propagation-loss-model.cc rename to src/common/propagation-loss-model.cc diff --git a/src/devices/wifi/propagation-loss-model.h b/src/common/propagation-loss-model.h similarity index 100% rename from src/devices/wifi/propagation-loss-model.h rename to src/common/propagation-loss-model.h diff --git a/src/common/wscript b/src/common/wscript index 10c956971..7e3c51fb7 100644 --- a/src/common/wscript +++ b/src/common/wscript @@ -23,6 +23,10 @@ def build(bld): 'pcap-file-test-suite.cc', 'pcap-file-object.cc', 'output-stream-object.cc', + 'propagation-delay-model.cc', + 'propagation-loss-model.cc', + 'propagation-loss-model-test-suite.cc', + 'jakes-propagation-loss-model.cc', ] headers = bld.new_task_gen('ns3header') @@ -47,4 +51,7 @@ def build(bld): 'pcap-file.h', 'pcap-file-object.h', 'output-stream-object.h', + 'propagation-delay-model.h', + 'propagation-loss-model.h', + 'jakes-propagation-loss-model.h', ] diff --git a/src/core/random-variable.cc b/src/core/random-variable.cc index 306b8885d..bdda79e79 100644 --- a/src/core/random-variable.cc +++ b/src/core/random-variable.cc @@ -23,12 +23,12 @@ #include #include -#include // for gettimeofday +#include // for gettimeofday #include #include #include #include -#include +#include #include #include @@ -42,100 +42,103 @@ using namespace std; -namespace ns3{ +namespace ns3 { -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // Seed Manager -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- -uint32_t SeedManager::GetSeed() +uint32_t SeedManager::GetSeed () { uint32_t s[6]; RngStream::GetPackageSeed (s); - NS_ASSERT( - s[0] == s[1] && - s[0] == s[2] && - s[0] == s[3] && - s[0] == s[4] && - s[0] == s[5] - ); + NS_ASSERT ( + s[0] == s[1] + && s[0] == s[2] + && s[0] == s[3] + && s[0] == s[4] + && s[0] == s[5] + ); return s[0]; } -void SeedManager::SetSeed(uint32_t seed) +void SeedManager::SetSeed (uint32_t seed) { - Config::SetGlobal("RngSeed", IntegerValue(seed)); + Config::SetGlobal ("RngSeed", IntegerValue (seed)); } -void SeedManager::SetRun(uint32_t run) +void SeedManager::SetRun (uint32_t run) { - Config::SetGlobal("RngRun", IntegerValue(run)); + Config::SetGlobal ("RngRun", IntegerValue (run)); } -uint32_t SeedManager::GetRun() +uint32_t SeedManager::GetRun () { return RngStream::GetPackageRun (); } bool SeedManager::CheckSeed (uint32_t seed) { - return RngStream::CheckSeed(seed); + return RngStream::CheckSeed (seed); } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // RandomVariableBase methods -class RandomVariableBase +class RandomVariableBase { public: RandomVariableBase (); RandomVariableBase (const RandomVariableBase &o); - virtual ~RandomVariableBase(); - virtual double GetValue() = 0; - virtual uint32_t GetInteger(); - virtual RandomVariableBase* Copy(void) const = 0; + virtual ~RandomVariableBase (); + virtual double GetValue () = 0; + virtual uint32_t GetInteger (); + virtual RandomVariableBase* Copy (void) const = 0; protected: - RngStream* m_generator; //underlying generator being wrapped + RngStream* m_generator; // underlying generator being wrapped }; -RandomVariableBase::RandomVariableBase() - : m_generator(NULL) +RandomVariableBase::RandomVariableBase () + : m_generator (NULL) { } -RandomVariableBase::RandomVariableBase(const RandomVariableBase& r) - :m_generator(0) +RandomVariableBase::RandomVariableBase (const RandomVariableBase& r) + : m_generator (0) { if (r.m_generator) { - m_generator = new RngStream(*r.m_generator); + m_generator = new RngStream (*r.m_generator); } } -RandomVariableBase::~RandomVariableBase() +RandomVariableBase::~RandomVariableBase () { delete m_generator; } -uint32_t RandomVariableBase::GetInteger() +uint32_t RandomVariableBase::GetInteger () { - return (uint32_t)GetValue(); + return (uint32_t)GetValue (); } -//------------------------------------------------------- +// ------------------------------------------------------- -RandomVariable::RandomVariable() +RandomVariable::RandomVariable () : m_variable (0) -{} -RandomVariable::RandomVariable(const RandomVariable&o) +{ +} +RandomVariable::RandomVariable (const RandomVariable&o) : m_variable (o.m_variable->Copy ()) -{} +{ +} RandomVariable::RandomVariable (const RandomVariableBase &variable) : m_variable (variable.Copy ()) -{} +{ +} RandomVariable & RandomVariable::operator = (const RandomVariable &o) { @@ -147,17 +150,17 @@ RandomVariable::operator = (const RandomVariable &o) m_variable = o.m_variable->Copy (); return *this; } -RandomVariable::~RandomVariable() +RandomVariable::~RandomVariable () { delete m_variable; } -double +double RandomVariable::GetValue (void) const { return m_variable->GetValue (); } -uint32_t +uint32_t RandomVariable::GetInteger (void) const { return m_variable->GetInteger (); @@ -173,197 +176,223 @@ RandomVariable::Peek (void) const ATTRIBUTE_VALUE_IMPLEMENT (RandomVariable); ATTRIBUTE_CHECKER_IMPLEMENT (RandomVariable); -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // UniformVariableImpl -class UniformVariableImpl : public RandomVariableBase { +class UniformVariableImpl : public RandomVariableBase +{ public: /** * Creates a uniform random number generator in the * range [0.0 .. 1.0). */ - UniformVariableImpl(); + UniformVariableImpl (); /** * Creates a uniform random number generator with the specified range * \param s Low end of the range * \param l High end of the range */ - UniformVariableImpl(double s, double l); + UniformVariableImpl (double s, double l); - UniformVariableImpl(const UniformVariableImpl& c); + UniformVariableImpl (const UniformVariableImpl& c); double GetMin (void) const; double GetMax (void) const; - + /** * \return A value between low and high values specified by the constructor */ - virtual double GetValue(); + virtual double GetValue (); /** * \return A value between low and high values specified by parameters */ - virtual double GetValue(double s, double l); - - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (double s, double l); + + virtual RandomVariableBase* Copy (void) const; private: double m_min; double m_max; }; -UniformVariableImpl::UniformVariableImpl() - : m_min(0), m_max(1.0) { } - -UniformVariableImpl::UniformVariableImpl(double s, double l) - : m_min(s), m_max(l) { } +UniformVariableImpl::UniformVariableImpl () + : m_min (0), + m_max (1.0) +{ +} -UniformVariableImpl::UniformVariableImpl(const UniformVariableImpl& c) - : RandomVariableBase(c), m_min(c.m_min), m_max(c.m_max) { } +UniformVariableImpl::UniformVariableImpl (double s, double l) + : m_min (s), + m_max (l) +{ +} -double +UniformVariableImpl::UniformVariableImpl (const UniformVariableImpl& c) + : RandomVariableBase (c), + m_min (c.m_min), + m_max (c.m_max) +{ +} + +double UniformVariableImpl::GetMin (void) const { return m_min; } -double +double UniformVariableImpl::GetMax (void) const { return m_max; } -double UniformVariableImpl::GetValue() +double UniformVariableImpl::GetValue () { - if(!m_generator) + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } - return m_min + m_generator->RandU01() * (m_max - m_min); + return m_min + m_generator->RandU01 () * (m_max - m_min); } -double UniformVariableImpl::GetValue(double s, double l) +double UniformVariableImpl::GetValue (double s, double l) { - if(!m_generator) + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } - return s + m_generator->RandU01() * (l-s); + return s + m_generator->RandU01 () * (l - s); } -RandomVariableBase* UniformVariableImpl::Copy() const +RandomVariableBase* UniformVariableImpl::Copy () const { - return new UniformVariableImpl(*this); + return new UniformVariableImpl (*this); } -UniformVariable::UniformVariable() +UniformVariable::UniformVariable () : RandomVariable (UniformVariableImpl ()) -{} -UniformVariable::UniformVariable(double s, double l) +{ +} +UniformVariable::UniformVariable (double s, double l) : RandomVariable (UniformVariableImpl (s, l)) -{} +{ +} -double UniformVariable::GetValue(void) const +double UniformVariable::GetValue (void) const { return this->RandomVariable::GetValue (); } -double UniformVariable::GetValue(double s, double l) +double UniformVariable::GetValue (double s, double l) { - return ((UniformVariableImpl*)Peek())->GetValue(s,l); + return ((UniformVariableImpl*)Peek ())->GetValue (s,l); } uint32_t UniformVariable::GetInteger (uint32_t s, uint32_t l) { - NS_ASSERT(s <= l); - return static_cast( GetValue(s, l+1) ); + NS_ASSERT (s <= l); + return static_cast ( GetValue (s, l + 1) ); } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // ConstantVariableImpl methods -class ConstantVariableImpl : public RandomVariableBase { +class ConstantVariableImpl : public RandomVariableBase +{ public: /** * Construct a ConstantVariableImpl RNG that returns zero every sample */ - ConstantVariableImpl(); - + ConstantVariableImpl (); + /** * Construct a ConstantVariableImpl RNG that returns the specified value * every sample. * \param c Unchanging value for this RNG. */ - ConstantVariableImpl(double c); + ConstantVariableImpl (double c); - ConstantVariableImpl(const ConstantVariableImpl& c) ; + ConstantVariableImpl (const ConstantVariableImpl& c); /** * \brief Specify a new constant RNG for this generator. * \param c New constant value for this RNG. */ - void NewConstant(double c); + void NewConstant (double c); /** * \return The constant value specified */ - virtual double GetValue(); - virtual uint32_t GetInteger(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual uint32_t GetInteger (); + virtual RandomVariableBase* Copy (void) const; private: double m_const; }; -ConstantVariableImpl::ConstantVariableImpl() - : m_const(0) { } +ConstantVariableImpl::ConstantVariableImpl () + : m_const (0) +{ +} -ConstantVariableImpl::ConstantVariableImpl(double c) - : m_const(c) { }; - -ConstantVariableImpl::ConstantVariableImpl(const ConstantVariableImpl& c) - : RandomVariableBase(c), m_const(c.m_const) { } +ConstantVariableImpl::ConstantVariableImpl (double c) + : m_const (c) +{ +} -void ConstantVariableImpl::NewConstant(double c) - { m_const = c;} - -double ConstantVariableImpl::GetValue() +ConstantVariableImpl::ConstantVariableImpl (const ConstantVariableImpl& c) + : RandomVariableBase (c), + m_const (c.m_const) +{ +} + +void ConstantVariableImpl::NewConstant (double c) +{ + m_const = c; +} + +double ConstantVariableImpl::GetValue () { return m_const; } -uint32_t ConstantVariableImpl::GetInteger() +uint32_t ConstantVariableImpl::GetInteger () { return (uint32_t)m_const; } -RandomVariableBase* ConstantVariableImpl::Copy() const +RandomVariableBase* ConstantVariableImpl::Copy () const { - return new ConstantVariableImpl(*this); + return new ConstantVariableImpl (*this); } -ConstantVariable::ConstantVariable() +ConstantVariable::ConstantVariable () : RandomVariable (ConstantVariableImpl ()) -{} -ConstantVariable::ConstantVariable(double c) +{ +} +ConstantVariable::ConstantVariable (double c) : RandomVariable (ConstantVariableImpl (c)) -{} -void -ConstantVariable::SetConstant(double c) +{ +} +void +ConstantVariable::SetConstant (double c) { *this = ConstantVariable (c); } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // SequentialVariableImpl methods -class SequentialVariableImpl : public RandomVariableBase { +class SequentialVariableImpl : public RandomVariableBase +{ public: /** @@ -377,7 +406,7 @@ public: * \param i Increment between sequence values * \param c Number of times each member of the sequence is repeated */ - SequentialVariableImpl(double f, double l, double i = 1, uint32_t c = 1); + SequentialVariableImpl (double f, double l, double i = 1, uint32_t c = 1); /** * \brief Constructor for the SequentialVariableImpl RNG. @@ -389,16 +418,16 @@ public: * \param i Reference to a RandomVariableBase for the sequence increment * \param c Number of times each member of the sequence is repeated */ - SequentialVariableImpl(double f, double l, const RandomVariable& i, uint32_t c = 1); + SequentialVariableImpl (double f, double l, const RandomVariable& i, uint32_t c = 1); - SequentialVariableImpl(const SequentialVariableImpl& c); - - ~SequentialVariableImpl(); + SequentialVariableImpl (const SequentialVariableImpl& c); + + ~SequentialVariableImpl (); /** * \return The next value in the Sequence */ - virtual double GetValue(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; private: double m_min; double m_max; @@ -408,152 +437,193 @@ private: uint32_t m_currentConsecutive; }; -SequentialVariableImpl::SequentialVariableImpl(double f, double l, double i, uint32_t c) - : m_min(f), m_max(l), m_increment(ConstantVariable(i)), m_consecutive(c), - m_current(f), m_currentConsecutive(0) -{} +SequentialVariableImpl::SequentialVariableImpl (double f, double l, double i, uint32_t c) + : m_min (f), + m_max (l), + m_increment (ConstantVariable (i)), + m_consecutive (c), + m_current (f), + m_currentConsecutive (0) +{ +} -SequentialVariableImpl::SequentialVariableImpl(double f, double l, const RandomVariable& i, uint32_t c) - : m_min(f), m_max(l), m_increment(i), m_consecutive(c), - m_current(f), m_currentConsecutive(0) -{} +SequentialVariableImpl::SequentialVariableImpl (double f, double l, const RandomVariable& i, uint32_t c) + : m_min (f), + m_max (l), + m_increment (i), + m_consecutive (c), + m_current (f), + m_currentConsecutive (0) +{ +} -SequentialVariableImpl::SequentialVariableImpl(const SequentialVariableImpl& c) - : RandomVariableBase(c), m_min(c.m_min), m_max(c.m_max), - m_increment(c.m_increment), m_consecutive(c.m_consecutive), - m_current(c.m_current), m_currentConsecutive(c.m_currentConsecutive) -{} +SequentialVariableImpl::SequentialVariableImpl (const SequentialVariableImpl& c) + : RandomVariableBase (c), + m_min (c.m_min), + m_max (c.m_max), + m_increment (c.m_increment), + m_consecutive (c.m_consecutive), + m_current (c.m_current), + m_currentConsecutive (c.m_currentConsecutive) +{ +} -SequentialVariableImpl::~SequentialVariableImpl() -{} +SequentialVariableImpl::~SequentialVariableImpl () +{ +} -double SequentialVariableImpl::GetValue() +double SequentialVariableImpl::GetValue () { // Return a sequential series of values double r = m_current; if (++m_currentConsecutive == m_consecutive) { // Time to advance to next m_currentConsecutive = 0; - m_current += m_increment.GetValue(); + m_current += m_increment.GetValue (); if (m_current >= m_max) - m_current = m_min + (m_current - m_max); + { + m_current = m_min + (m_current - m_max); + } } return r; } -RandomVariableBase* SequentialVariableImpl::Copy() const +RandomVariableBase* SequentialVariableImpl::Copy () const { - return new SequentialVariableImpl(*this); + return new SequentialVariableImpl (*this); } -SequentialVariable::SequentialVariable(double f, double l, double i, uint32_t c) +SequentialVariable::SequentialVariable (double f, double l, double i, uint32_t c) : RandomVariable (SequentialVariableImpl (f, l, i, c)) -{} -SequentialVariable::SequentialVariable(double f, double l, const RandomVariable& i, uint32_t c) +{ +} +SequentialVariable::SequentialVariable (double f, double l, const RandomVariable& i, uint32_t c) : RandomVariable (SequentialVariableImpl (f, l, i, c)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // ExponentialVariableImpl methods -class ExponentialVariableImpl : public RandomVariableBase { +class ExponentialVariableImpl : public RandomVariableBase +{ public: /** * Constructs an exponential random variable with a mean * value of 1.0. */ - ExponentialVariableImpl(); + ExponentialVariableImpl (); /** * \brief Constructs an exponential random variable with a specified mean * \param m Mean value for the random variable */ - explicit ExponentialVariableImpl(double m); + explicit ExponentialVariableImpl (double m); /** - * \brief Constructs an exponential random variable with spefified - * \brief mean and upper limit. + * \brief Constructs an exponential random variable with specified + * mean and upper limit. * * Since exponential distributions can theoretically return unbounded values, * it is sometimes useful to specify a fixed upper limit. Note however when - * the upper limit is specified, the true mean of the distribution is - * slightly smaller than the mean value specified. + * the upper limit is specified, the true mean of the distribution is + * slightly smaller than the mean value specified: \f$ m - b/(e^{b/m}-1) \f$. * \param m Mean value of the random variable * \param b Upper bound on returned values */ - ExponentialVariableImpl(double m, double b); + ExponentialVariableImpl (double m, double b); + + ExponentialVariableImpl (const ExponentialVariableImpl& c); - ExponentialVariableImpl(const ExponentialVariableImpl& c); - /** * \return A random value from this exponential distribution */ - virtual double GetValue(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; private: double m_mean; // Mean value of RV double m_bound; // Upper bound on value (if non-zero) }; -ExponentialVariableImpl::ExponentialVariableImpl() - : m_mean(1.0), m_bound(0) { } - -ExponentialVariableImpl::ExponentialVariableImpl(double m) - : m_mean(m), m_bound(0) { } - -ExponentialVariableImpl::ExponentialVariableImpl(double m, double b) - : m_mean(m), m_bound(b) { } - -ExponentialVariableImpl::ExponentialVariableImpl(const ExponentialVariableImpl& c) - : RandomVariableBase(c), m_mean(c.m_mean), m_bound(c.m_bound) { } - -double ExponentialVariableImpl::GetValue() +ExponentialVariableImpl::ExponentialVariableImpl () + : m_mean (1.0), + m_bound (0) { - if(!m_generator) +} + +ExponentialVariableImpl::ExponentialVariableImpl (double m) + : m_mean (m), + m_bound (0) +{ +} + +ExponentialVariableImpl::ExponentialVariableImpl (double m, double b) + : m_mean (m), + m_bound (b) +{ +} + +ExponentialVariableImpl::ExponentialVariableImpl (const ExponentialVariableImpl& c) + : RandomVariableBase (c), + m_mean (c.m_mean), + m_bound (c.m_bound) +{ +} + +double ExponentialVariableImpl::GetValue () +{ + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } - while(1) + while (1) { - double r = -m_mean*log(m_generator->RandU01()); - if (m_bound == 0 || r <= m_bound) return r; - //otherwise, try again + double r = -m_mean*log (m_generator->RandU01 ()); + if (m_bound == 0 || r <= m_bound) + { + return r; + } + // otherwise, try again } } -RandomVariableBase* ExponentialVariableImpl::Copy() const +RandomVariableBase* ExponentialVariableImpl::Copy () const { - return new ExponentialVariableImpl(*this); + return new ExponentialVariableImpl (*this); } -ExponentialVariable::ExponentialVariable() +ExponentialVariable::ExponentialVariable () : RandomVariable (ExponentialVariableImpl ()) -{} -ExponentialVariable::ExponentialVariable(double m) +{ +} +ExponentialVariable::ExponentialVariable (double m) : RandomVariable (ExponentialVariableImpl (m)) -{} -ExponentialVariable::ExponentialVariable(double m, double b) +{ +} +ExponentialVariable::ExponentialVariable (double m, double b) : RandomVariable (ExponentialVariableImpl (m, b)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // ParetoVariableImpl methods -class ParetoVariableImpl : public RandomVariableBase { +class ParetoVariableImpl : public RandomVariableBase +{ public: /** * Constructs a pareto random variable with a mean of 1 and a shape * parameter of 1.5 */ - ParetoVariableImpl(); + ParetoVariableImpl (); /** * Constructs a pareto random variable with specified mean and shape * parameter of 1.5 * \param m Mean value of the distribution */ - explicit ParetoVariableImpl(double m); + explicit ParetoVariableImpl (double m); /** * Constructs a pareto random variable with the specified mean value and @@ -561,7 +631,7 @@ public: * \param m Mean value of the distribution * \param s Shape parameter for the distribution */ - ParetoVariableImpl(double m, double s); + ParetoVariableImpl (double m, double s); /** * \brief Constructs a pareto random variable with the specified mean @@ -575,15 +645,15 @@ public: * \param s Shape parameter * \param b Upper limit on returned values */ - ParetoVariableImpl(double m, double s, double b); + ParetoVariableImpl (double m, double s, double b); + + ParetoVariableImpl (const ParetoVariableImpl& c); - ParetoVariableImpl(const ParetoVariableImpl& c); - /** * \return A random value from this Pareto distribution */ - virtual double GetValue(); - virtual RandomVariableBase* Copy() const; + virtual double GetValue (); + virtual RandomVariableBase* Copy () const; private: double m_mean; // Mean value of RV @@ -591,66 +661,94 @@ private: double m_bound; // Upper bound on value (if non-zero) }; -ParetoVariableImpl::ParetoVariableImpl() - : m_mean(1.0), m_shape(1.5), m_bound(0) { } - -ParetoVariableImpl::ParetoVariableImpl(double m) - : m_mean(m), m_shape(1.5), m_bound(0) { } - -ParetoVariableImpl::ParetoVariableImpl(double m, double s) - : m_mean(m), m_shape(s), m_bound(0) { } - -ParetoVariableImpl::ParetoVariableImpl(double m, double s, double b) - : m_mean(m), m_shape(s), m_bound(b) { } - -ParetoVariableImpl::ParetoVariableImpl(const ParetoVariableImpl& c) - : RandomVariableBase(c), m_mean(c.m_mean), m_shape(c.m_shape), - m_bound(c.m_bound) { } - -double ParetoVariableImpl::GetValue() +ParetoVariableImpl::ParetoVariableImpl () + : m_mean (1.0), + m_shape (1.5), + m_bound (0) { - if(!m_generator) +} + +ParetoVariableImpl::ParetoVariableImpl (double m) + : m_mean (m), + m_shape (1.5), + m_bound (0) +{ +} + +ParetoVariableImpl::ParetoVariableImpl (double m, double s) + : m_mean (m), + m_shape (s), + m_bound (0) +{ +} + +ParetoVariableImpl::ParetoVariableImpl (double m, double s, double b) + : m_mean (m), + m_shape (s), + m_bound (b) +{ +} + +ParetoVariableImpl::ParetoVariableImpl (const ParetoVariableImpl& c) + : RandomVariableBase (c), + m_mean (c.m_mean), + m_shape (c.m_shape), + m_bound (c.m_bound) +{ +} + +double ParetoVariableImpl::GetValue () +{ + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } double scale = m_mean * ( m_shape - 1.0) / m_shape; - while(1) + while (1) { - double r = (scale * ( 1.0 / pow(m_generator->RandU01(), 1.0 / m_shape))); - if (m_bound == 0 || r <= m_bound) return r; - //otherwise, try again + double r = (scale * ( 1.0 / pow (m_generator->RandU01 (), 1.0 / m_shape))); + if (m_bound == 0 || r <= m_bound) + { + return r; + } + // otherwise, try again } } -RandomVariableBase* ParetoVariableImpl::Copy() const +RandomVariableBase* ParetoVariableImpl::Copy () const { - return new ParetoVariableImpl(*this); + return new ParetoVariableImpl (*this); } ParetoVariable::ParetoVariable () : RandomVariable (ParetoVariableImpl ()) -{} -ParetoVariable::ParetoVariable(double m) +{ +} +ParetoVariable::ParetoVariable (double m) : RandomVariable (ParetoVariableImpl (m)) -{} -ParetoVariable::ParetoVariable(double m, double s) +{ +} +ParetoVariable::ParetoVariable (double m, double s) : RandomVariable (ParetoVariableImpl (m, s)) -{} -ParetoVariable::ParetoVariable(double m, double s, double b) +{ +} +ParetoVariable::ParetoVariable (double m, double s, double b) : RandomVariable (ParetoVariableImpl (m, s, b)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // WeibullVariableImpl methods -class WeibullVariableImpl : public RandomVariableBase { +class WeibullVariableImpl : public RandomVariableBase +{ public: /** * Constructs a weibull random variable with a mean * value of 1.0 and a shape (alpha) parameter of 1 */ - WeibullVariableImpl(); + WeibullVariableImpl (); /** @@ -658,7 +756,7 @@ public: * value and a shape (alpha) parameter of 1.5. * \param m mean value of the distribution */ - WeibullVariableImpl(double m) ; + WeibullVariableImpl (double m); /** * Constructs a weibull random variable with the specified mean @@ -666,28 +764,28 @@ public: * \param m Mean value for the distribution. * \param s Shape (alpha) parameter for the distribution. */ - WeibullVariableImpl(double m, double s); + WeibullVariableImpl (double m, double s); - /** - * \brief Constructs a weibull random variable with the specified mean - * \brief value, shape (alpha), and upper bound. - * Since WeibullVariableImpl distributions can theoretically return unbounded values, - * it is sometimes usefull to specify a fixed upper limit. Note however - * that when the upper limit is specified, the true mean of the distribution - * is slightly smaller than the mean value specified. - * \param m Mean value for the distribution. - * \param s Shape (alpha) parameter for the distribution. - * \param b Upper limit on returned values - */ - WeibullVariableImpl(double m, double s, double b); + /** + * \brief Constructs a weibull random variable with the specified mean + * \brief value, shape (alpha), and upper bound. + * Since WeibullVariableImpl distributions can theoretically return unbounded values, + * it is sometimes usefull to specify a fixed upper limit. Note however + * that when the upper limit is specified, the true mean of the distribution + * is slightly smaller than the mean value specified. + * \param m Mean value for the distribution. + * \param s Shape (alpha) parameter for the distribution. + * \param b Upper limit on returned values + */ + WeibullVariableImpl (double m, double s, double b); + + WeibullVariableImpl (const WeibullVariableImpl& c); - WeibullVariableImpl(const WeibullVariableImpl& c); - /** * \return A random value from this Weibull distribution */ - virtual double GetValue(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; private: double m_mean; // Mean value of RV @@ -695,79 +793,107 @@ private: double m_bound; // Upper bound on value (if non-zero) }; -WeibullVariableImpl::WeibullVariableImpl() : m_mean(1.0), m_alpha(1), m_bound(0) { } -WeibullVariableImpl::WeibullVariableImpl(double m) - : m_mean(m), m_alpha(1), m_bound(0) { } -WeibullVariableImpl::WeibullVariableImpl(double m, double s) - : m_mean(m), m_alpha(s), m_bound(0) { } -WeibullVariableImpl::WeibullVariableImpl(double m, double s, double b) - : m_mean(m), m_alpha(s), m_bound(b) { }; -WeibullVariableImpl::WeibullVariableImpl(const WeibullVariableImpl& c) - : RandomVariableBase(c), m_mean(c.m_mean), m_alpha(c.m_alpha), - m_bound(c.m_bound) { } - -double WeibullVariableImpl::GetValue() +WeibullVariableImpl::WeibullVariableImpl () : m_mean (1.0), + m_alpha (1), + m_bound (0) { - if(!m_generator) +} +WeibullVariableImpl::WeibullVariableImpl (double m) + : m_mean (m), + m_alpha (1), + m_bound (0) +{ +} +WeibullVariableImpl::WeibullVariableImpl (double m, double s) + : m_mean (m), + m_alpha (s), + m_bound (0) +{ +} +WeibullVariableImpl::WeibullVariableImpl (double m, double s, double b) + : m_mean (m), + m_alpha (s), + m_bound (b) +{ +} +WeibullVariableImpl::WeibullVariableImpl (const WeibullVariableImpl& c) + : RandomVariableBase (c), + m_mean (c.m_mean), + m_alpha (c.m_alpha), + m_bound (c.m_bound) +{ +} + +double WeibullVariableImpl::GetValue () +{ + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } double exponent = 1.0 / m_alpha; - while(1) + while (1) { - double r = m_mean * pow( -log(m_generator->RandU01()), exponent); - if (m_bound == 0 || r <= m_bound) return r; - //otherwise, try again + double r = m_mean * pow ( -log (m_generator->RandU01 ()), exponent); + if (m_bound == 0 || r <= m_bound) + { + return r; + } + // otherwise, try again } } -RandomVariableBase* WeibullVariableImpl::Copy() const +RandomVariableBase* WeibullVariableImpl::Copy () const { - return new WeibullVariableImpl(*this); + return new WeibullVariableImpl (*this); } -WeibullVariable::WeibullVariable() +WeibullVariable::WeibullVariable () : RandomVariable (WeibullVariableImpl ()) -{} -WeibullVariable::WeibullVariable(double m) +{ +} +WeibullVariable::WeibullVariable (double m) : RandomVariable (WeibullVariableImpl (m)) -{} -WeibullVariable::WeibullVariable(double m, double s) +{ +} +WeibullVariable::WeibullVariable (double m, double s) : RandomVariable (WeibullVariableImpl (m, s)) -{} -WeibullVariable::WeibullVariable(double m, double s, double b) +{ +} +WeibullVariable::WeibullVariable (double m, double s, double b) : RandomVariable (WeibullVariableImpl (m, s, b)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // NormalVariableImpl methods -class NormalVariableImpl : public RandomVariableBase { // Normally Distributed random var +class NormalVariableImpl : public RandomVariableBase // Normally Distributed random var +{ public: - static const double INFINITE_VALUE; + static const double INFINITE_VALUE; /** * Constructs an normal random variable with a mean * value of 0 and variance of 1. - */ - NormalVariableImpl(); + */ + NormalVariableImpl (); /** * \brief Construct a normal random variable with specified mean and variance * \param m Mean value * \param v Variance * \param b Bound. The NormalVariableImpl is bounded within +-bound of the mean. - */ - NormalVariableImpl(double m, double v, double b = INFINITE_VALUE); + */ + NormalVariableImpl (double m, double v, double b = INFINITE_VALUE); + + NormalVariableImpl (const NormalVariableImpl& c); - NormalVariableImpl(const NormalVariableImpl& c); - /** * \return A value from this normal distribution */ - virtual double GetValue(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; double GetMean (void) const; double GetVariance (void) const; @@ -783,62 +909,77 @@ private: const double NormalVariableImpl::INFINITE_VALUE = 1e307; -NormalVariableImpl::NormalVariableImpl() - : m_mean(0.0), m_variance(1.0), m_bound(INFINITE_VALUE), m_nextValid(false){} - -NormalVariableImpl::NormalVariableImpl(double m, double v, double b) - : m_mean(m), m_variance(v), m_bound(b), m_nextValid(false) { } - -NormalVariableImpl::NormalVariableImpl(const NormalVariableImpl& c) - : RandomVariableBase(c), m_mean(c.m_mean), m_variance(c.m_variance), - m_bound(c.m_bound), m_nextValid(false) { } - -double NormalVariableImpl::GetValue() +NormalVariableImpl::NormalVariableImpl () + : m_mean (0.0), + m_variance (1.0), + m_bound (INFINITE_VALUE), + m_nextValid (false) { - if(!m_generator) +} + +NormalVariableImpl::NormalVariableImpl (double m, double v, double b) + : m_mean (m), + m_variance (v), + m_bound (b), + m_nextValid (false) +{ +} + +NormalVariableImpl::NormalVariableImpl (const NormalVariableImpl& c) + : RandomVariableBase (c), + m_mean (c.m_mean), + m_variance (c.m_variance), + m_bound (c.m_bound), + m_nextValid (false) +{ +} + +double NormalVariableImpl::GetValue () +{ + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } if (m_nextValid) { // use previously generated m_nextValid = false; return m_next; } - while(1) + while (1) { // See Simulation Modeling and Analysis p. 466 (Averill Law) // for algorithm; basically a Box-Muller transform: // http://en.wikipedia.org/wiki/Box-Muller_transform - double u1 = m_generator->RandU01(); - double u2 = m_generator->RandU01();; + double u1 = m_generator->RandU01 (); + double u2 = m_generator->RandU01 (); double v1 = 2 * u1 - 1; double v2 = 2 * u2 - 1; double w = v1 * v1 + v2 * v2; if (w <= 1.0) { // Got good pair - double y = sqrt((-2 * log(w))/w); - m_next = m_mean + v2 * y * sqrt(m_variance); - //if next is in bounds, it is valid - m_nextValid = fabs(m_next-m_mean) <= m_bound; - double x1 = m_mean + v1 * y * sqrt(m_variance); - //if x1 is in bounds, return it - if (fabs(x1-m_mean) <= m_bound) + double y = sqrt ((-2 * log (w)) / w); + m_next = m_mean + v2 * y * sqrt (m_variance); + // if next is in bounds, it is valid + m_nextValid = fabs (m_next - m_mean) <= m_bound; + double x1 = m_mean + v1 * y * sqrt (m_variance); + // if x1 is in bounds, return it + if (fabs (x1 - m_mean) <= m_bound) { return x1; } - //otherwise try and return m_next if it is valid + // otherwise try and return m_next if it is valid else if (m_nextValid) - { - m_nextValid = false; - return m_next; - } - //otherwise, just run this loop again + { + m_nextValid = false; + return m_next; + } + // otherwise, just run this loop again } } } -RandomVariableBase* NormalVariableImpl::Copy() const +RandomVariableBase* NormalVariableImpl::Copy () const { - return new NormalVariableImpl(*this); + return new NormalVariableImpl (*this); } double @@ -859,109 +1000,131 @@ NormalVariableImpl::GetBound (void) const return m_bound; } -NormalVariable::NormalVariable() +NormalVariable::NormalVariable () : RandomVariable (NormalVariableImpl ()) -{} -NormalVariable::NormalVariable(double m, double v) +{ +} +NormalVariable::NormalVariable (double m, double v) : RandomVariable (NormalVariableImpl (m, v)) -{} -NormalVariable::NormalVariable(double m, double v, double b) +{ +} +NormalVariable::NormalVariable (double m, double v, double b) : RandomVariable (NormalVariableImpl (m, v, b)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -class EmpiricalVariableImpl : public RandomVariableBase { +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +class EmpiricalVariableImpl : public RandomVariableBase +{ public: /** * Constructor for the EmpiricalVariableImpl random variables. */ - explicit EmpiricalVariableImpl(); + explicit EmpiricalVariableImpl (); - virtual ~EmpiricalVariableImpl(); - EmpiricalVariableImpl(const EmpiricalVariableImpl& c); + virtual ~EmpiricalVariableImpl (); + EmpiricalVariableImpl (const EmpiricalVariableImpl& c); /** * \return A value from this empirical distribution */ - virtual double GetValue(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; /** * \brief Specifies a point in the empirical distribution * \param v The function value for this point * \param c Probability that the function is less than or equal to v */ - virtual void CDF(double v, double c); // Value, prob <= Value + virtual void CDF (double v, double c); // Value, prob <= Value private: - class ValueCDF { - public: - ValueCDF(); - ValueCDF(double v, double c); - ValueCDF(const ValueCDF& c); + class ValueCDF + { +public: + ValueCDF (); + ValueCDF (double v, double c); + ValueCDF (const ValueCDF& c); double value; double cdf; }; - virtual void Validate(); // Insure non-decreasing emiprical values - virtual double Interpolate(double, double, double, double, double); + virtual void Validate (); // Insure non-decreasing emiprical values + virtual double Interpolate (double, double, double, double, double); bool validated; // True if non-decreasing validated std::vector emp; // Empicical CDF }; // ValueCDF methods -EmpiricalVariableImpl::ValueCDF::ValueCDF() - : value(0.0), cdf(0.0){ } -EmpiricalVariableImpl::ValueCDF::ValueCDF(double v, double c) - : value(v), cdf(c) { } -EmpiricalVariableImpl::ValueCDF::ValueCDF(const ValueCDF& c) - : value(c.value), cdf(c.cdf) { } +EmpiricalVariableImpl::ValueCDF::ValueCDF () + : value (0.0), + cdf (0.0) +{ +} +EmpiricalVariableImpl::ValueCDF::ValueCDF (double v, double c) + : value (v), + cdf (c) +{ +} +EmpiricalVariableImpl::ValueCDF::ValueCDF (const ValueCDF& c) + : value (c.value), + cdf (c.cdf) +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // EmpiricalVariableImpl methods -EmpiricalVariableImpl::EmpiricalVariableImpl() - : validated(false) { } +EmpiricalVariableImpl::EmpiricalVariableImpl () + : validated (false) +{ +} -EmpiricalVariableImpl::EmpiricalVariableImpl(const EmpiricalVariableImpl& c) - : RandomVariableBase(c), validated(c.validated), emp(c.emp) { } +EmpiricalVariableImpl::EmpiricalVariableImpl (const EmpiricalVariableImpl& c) + : RandomVariableBase (c), + validated (c.validated), + emp (c.emp) +{ +} -EmpiricalVariableImpl::~EmpiricalVariableImpl() { } +EmpiricalVariableImpl::~EmpiricalVariableImpl () +{ +} -double EmpiricalVariableImpl::GetValue() +double EmpiricalVariableImpl::GetValue () { // Return a value from the empirical distribution // This code based (loosely) on code by Bruce Mah (Thanks Bruce!) - if(!m_generator) + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } - if (emp.size() == 0) + if (emp.size () == 0) { return 0.0; // HuH? No empirical data } - if (!validated) + if (!validated) { - Validate(); // Insure in non-decreasing + Validate (); // Insure in non-decreasing } - double r = m_generator->RandU01(); - if (r <= emp.front().cdf) + double r = m_generator->RandU01 (); + if (r <= emp.front ().cdf) { - return emp.front().value; // Less than first + return emp.front ().value; // Less than first } - if (r >= emp.back().cdf) - { - return emp.back().value; // Greater than last + if (r >= emp.back ().cdf) + { + return emp.back ().value; // Greater than last } // Binary search std::vector::size_type bottom = 0; - std::vector::size_type top = emp.size() - 1; - while(1) + std::vector::size_type top = emp.size () - 1; + while (1) { std::vector::size_type c = (top + bottom) / 2; - if (r >= emp[c].cdf && r < emp[c+1].cdf) + if (r >= emp[c].cdf && r < emp[c + 1].cdf) { // Found it - return Interpolate(emp[c].cdf, emp[c+1].cdf, - emp[c].value, emp[c+1].value, - r); + return Interpolate (emp[c].cdf, emp[c + 1].cdf, + emp[c].value, emp[c + 1].value, + r); } // Not here, adjust bounds if (r < emp[c].cdf) @@ -975,21 +1138,21 @@ double EmpiricalVariableImpl::GetValue() } } -RandomVariableBase* EmpiricalVariableImpl::Copy() const +RandomVariableBase* EmpiricalVariableImpl::Copy () const { - return new EmpiricalVariableImpl(*this); + return new EmpiricalVariableImpl (*this); } -void EmpiricalVariableImpl::CDF(double v, double c) +void EmpiricalVariableImpl::CDF (double v, double c) { // Add a new empirical datapoint to the empirical cdf // NOTE. These MUST be inserted in non-decreasing order - emp.push_back(ValueCDF(v, c)); + emp.push_back (ValueCDF (v, c)); } -void EmpiricalVariableImpl::Validate() +void EmpiricalVariableImpl::Validate () { ValueCDF prior; - for (std::vector::size_type i = 0; i < emp.size(); ++i) + for (std::vector::size_type i = 0; i < emp.size (); ++i) { ValueCDF& current = emp[i]; if (current.value < prior.value || current.cdf < prior.cdf) @@ -999,27 +1162,29 @@ void EmpiricalVariableImpl::Validate() << " prior value " << prior.value << " current cdf " << current.cdf << " prior cdf " << prior.cdf << endl; - NS_FATAL_ERROR("Empirical Dist error"); + NS_FATAL_ERROR ("Empirical Dist error"); } prior = current; } validated = true; } -double EmpiricalVariableImpl::Interpolate(double c1, double c2, - double v1, double v2, double r) +double EmpiricalVariableImpl::Interpolate (double c1, double c2, + double v1, double v2, double r) { // Interpolate random value in range [v1..v2) based on [c1 .. r .. c2) return (v1 + ((v2 - v1) / (c2 - c1)) * (r - c1)); } -EmpiricalVariable::EmpiricalVariable() +EmpiricalVariable::EmpiricalVariable () : RandomVariable (EmpiricalVariableImpl ()) -{} +{ +} EmpiricalVariable::EmpiricalVariable (const RandomVariableBase &variable) : RandomVariable (variable) -{} -void -EmpiricalVariable::CDF(double v, double c) +{ +} +void +EmpiricalVariable::CDF (double v, double c) { EmpiricalVariableImpl *impl = dynamic_cast (Peek ()); NS_ASSERT (impl); @@ -1027,50 +1192,53 @@ EmpiricalVariable::CDF(double v, double c) } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // IntegerValue EmpiricalVariableImpl methods -class IntEmpiricalVariableImpl : public EmpiricalVariableImpl { +class IntEmpiricalVariableImpl : public EmpiricalVariableImpl +{ public: + IntEmpiricalVariableImpl (); - IntEmpiricalVariableImpl(); - - virtual RandomVariableBase* Copy(void) const; + virtual RandomVariableBase* Copy (void) const; /** * \return An integer value from this empirical distribution */ - virtual uint32_t GetInteger(); + virtual uint32_t GetInteger (); private: - virtual double Interpolate(double, double, double, double, double); + virtual double Interpolate (double, double, double, double, double); }; -IntEmpiricalVariableImpl::IntEmpiricalVariableImpl() { } - -uint32_t IntEmpiricalVariableImpl::GetInteger() +IntEmpiricalVariableImpl::IntEmpiricalVariableImpl () { - return (uint32_t)GetValue(); } -RandomVariableBase* IntEmpiricalVariableImpl::Copy() const +uint32_t IntEmpiricalVariableImpl::GetInteger () { - return new IntEmpiricalVariableImpl(*this); + return (uint32_t)GetValue (); } -double IntEmpiricalVariableImpl::Interpolate(double c1, double c2, - double v1, double v2, double r) +RandomVariableBase* IntEmpiricalVariableImpl::Copy () const +{ + return new IntEmpiricalVariableImpl (*this); +} + +double IntEmpiricalVariableImpl::Interpolate (double c1, double c2, + double v1, double v2, double r) { // Interpolate random value in range [v1..v2) based on [c1 .. r .. c2) - return ceil(v1 + ((v2 - v1) / (c2 - c1)) * (r - c1)); + return ceil (v1 + ((v2 - v1) / (c2 - c1)) * (r - c1)); } -IntEmpiricalVariable::IntEmpiricalVariable() +IntEmpiricalVariable::IntEmpiricalVariable () : EmpiricalVariable (IntEmpiricalVariableImpl ()) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // DeterministicVariableImpl -class DeterministicVariableImpl : public RandomVariableBase +class DeterministicVariableImpl : public RandomVariableBase { public: @@ -1079,55 +1247,61 @@ public: * * Creates a generator that returns successive elements of the d array * on successive calls to ::Value(). Note that the d pointer is copied - * for use by the generator (shallow-copy), not its contents, so the - * contents of the array d points to have to remain unchanged for the use + * for use by the generator (shallow-copy), not its contents, so the + * contents of the array d points to have to remain unchanged for the use * of DeterministicVariableImpl to be meaningful. * \param d Pointer to array of random values to return in sequence * \param c Number of values in the array */ - explicit DeterministicVariableImpl(double* d, uint32_t c); + explicit DeterministicVariableImpl (double* d, uint32_t c); - virtual ~DeterministicVariableImpl(); + virtual ~DeterministicVariableImpl (); /** * \return The next value in the deterministic sequence */ - virtual double GetValue(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; private: uint32_t count; uint32_t next; double* data; }; -DeterministicVariableImpl::DeterministicVariableImpl(double* d, uint32_t c) - : count(c), next(c), data(d) +DeterministicVariableImpl::DeterministicVariableImpl (double* d, uint32_t c) + : count (c), + next (c), + data (d) { // Nothing else needed } -DeterministicVariableImpl::~DeterministicVariableImpl() { } - -double DeterministicVariableImpl::GetValue() +DeterministicVariableImpl::~DeterministicVariableImpl () { - if (next == count) +} + +double DeterministicVariableImpl::GetValue () +{ + if (next == count) { next = 0; } return data[next++]; } -RandomVariableBase* DeterministicVariableImpl::Copy() const +RandomVariableBase* DeterministicVariableImpl::Copy () const { - return new DeterministicVariableImpl(*this); + return new DeterministicVariableImpl (*this); } -DeterministicVariable::DeterministicVariable(double* d, uint32_t c) +DeterministicVariable::DeterministicVariable (double* d, uint32_t c) : RandomVariable (DeterministicVariableImpl (d, c)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // LogNormalVariableImpl -class LogNormalVariableImpl : public RandomVariableBase { +class LogNormalVariableImpl : public RandomVariableBase +{ public: /** * \param mu mu parameter of the lognormal distribution @@ -1139,7 +1313,7 @@ public: * \return A random value from this distribution */ virtual double GetValue (); - virtual RandomVariableBase* Copy(void) const; + virtual RandomVariableBase* Copy (void) const; private: double m_mu; @@ -1153,31 +1327,32 @@ RandomVariableBase* LogNormalVariableImpl::Copy () const } LogNormalVariableImpl::LogNormalVariableImpl (double mu, double sigma) - :m_mu(mu), m_sigma(sigma) + : m_mu (mu), + m_sigma (sigma) { } // The code from this function was adapted from the GNU Scientific // Library 1.8: /* randist/lognormal.c - * + * * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. - * + * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* The lognormal distribution has the form +/* The lognormal distribution has the form p(x) dx = 1/(x * sqrt(2 pi sigma^2)) exp(-(ln(x) - zeta)^2/2 sigma^2) dx @@ -1186,9 +1361,9 @@ LogNormalVariableImpl::LogNormalVariableImpl (double mu, double sigma) double LogNormalVariableImpl::GetValue () { - if(!m_generator) + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } double u, v, r2, normal, z; @@ -1213,10 +1388,11 @@ LogNormalVariableImpl::GetValue () LogNormalVariable::LogNormalVariable (double mu, double sigma) : RandomVariable (LogNormalVariableImpl (mu, sigma)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // GammaVariableImpl class GammaVariableImpl : public RandomVariableBase { @@ -1236,9 +1412,9 @@ public: * \return A random value from the gamma distribution with parameters alpha * and beta */ - double GetValue(double alpha, double beta); + double GetValue (double alpha, double beta); - virtual RandomVariableBase* Copy(void) const; + virtual RandomVariableBase* Copy (void) const; private: double m_alpha; @@ -1253,14 +1429,15 @@ RandomVariableBase* GammaVariableImpl::Copy () const } GammaVariableImpl::GammaVariableImpl (double alpha, double beta) - : m_alpha(alpha), m_beta(beta) + : m_alpha (alpha), + m_beta (beta) { } double GammaVariableImpl::GetValue () { - return GetValue(m_alpha, m_beta); + return GetValue (m_alpha, m_beta); } /* @@ -1271,10 +1448,10 @@ GammaVariableImpl::GetValue () G. Marsaglia, W. W. Tsang: A simple method for gereating Gamma variables ACM Transactions on mathematical software, Vol. 26, No. 3, Sept. 2000 - The Gamma distribution density function has the form + The Gamma distribution density function has the form - x^(alpha-1) * exp(-x/beta) - p(x; alpha, beta) = ---------------------------- + x^(alpha-1) * exp(-x/beta) + p(x; alpha, beta) = ---------------------------- beta^alpha * Gamma(alpha) for x > 0. @@ -1282,17 +1459,17 @@ GammaVariableImpl::GetValue () double GammaVariableImpl::GetValue (double alpha, double beta) { - if(!m_generator) + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } if (alpha < 1) { double u = m_generator->RandU01 (); - return GetValue(1.0 + alpha, beta) * pow (u, 1.0 / alpha); + return GetValue (1.0 + alpha, beta) * pow (u, 1.0 / alpha); } - + double x, v, u; double d = alpha - 1.0 / 3.0; double c = (1.0 / 3.0) / sqrt (d); @@ -1303,7 +1480,8 @@ GammaVariableImpl::GetValue (double alpha, double beta) { x = m_normal.GetValue (); v = 1.0 + c * x; - } while (v <= 0); + } + while (v <= 0); v = v * v * v; u = m_generator->RandU01 (); @@ -1330,18 +1508,18 @@ GammaVariable::GammaVariable (double alpha, double beta) { } -double GammaVariable::GetValue(void) const +double GammaVariable::GetValue (void) const { return this->RandomVariable::GetValue (); } -double GammaVariable::GetValue(double alpha, double beta) const +double GammaVariable::GetValue (double alpha, double beta) const { - return ((GammaVariableImpl*)Peek())->GetValue(alpha, beta); + return ((GammaVariableImpl*)Peek ())->GetValue (alpha, beta); } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // ErlangVariableImpl class ErlangVariableImpl : public RandomVariableBase @@ -1362,9 +1540,9 @@ public: * \return A random value from the Erlang distribution with parameters k and * lambda. */ - double GetValue(unsigned int k, double lambda); + double GetValue (unsigned int k, double lambda); - virtual RandomVariableBase* Copy(void) const; + virtual RandomVariableBase* Copy (void) const; private: unsigned int m_k; @@ -1378,24 +1556,25 @@ RandomVariableBase* ErlangVariableImpl::Copy () const } ErlangVariableImpl::ErlangVariableImpl (unsigned int k, double lambda) - : m_k(k), m_lambda(lambda) + : m_k (k), + m_lambda (lambda) { } double ErlangVariableImpl::GetValue () { - return GetValue(m_k, m_lambda); + return GetValue (m_k, m_lambda); } /* The code for the following generator functions was adapted from ns-2 tools/ranvar.cc - The Erlang distribution density function has the form + The Erlang distribution density function has the form - x^(k-1) * exp(-x/lambda) - p(x; k, lambda) = --------------------------- + x^(k-1) * exp(-x/lambda) + p(x; k, lambda) = --------------------------- lambda^k * (k-1)! for x > 0. @@ -1403,17 +1582,17 @@ ErlangVariableImpl::GetValue () double ErlangVariableImpl::GetValue (unsigned int k, double lambda) { - if(!m_generator) + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } - ExponentialVariable exponential(lambda); + ExponentialVariable exponential (lambda); double result = 0; for (unsigned int i = 0; i < k; ++i) { - result += exponential.GetValue(); + result += exponential.GetValue (); } return result; @@ -1429,26 +1608,27 @@ ErlangVariable::ErlangVariable (unsigned int k, double lambda) { } -double ErlangVariable::GetValue(void) const +double ErlangVariable::GetValue (void) const { return this->RandomVariable::GetValue (); } -double ErlangVariable::GetValue(unsigned int k, double lambda) const +double ErlangVariable::GetValue (unsigned int k, double lambda) const { - return ((ErlangVariableImpl*)Peek())->GetValue(k, lambda); + return ((ErlangVariableImpl*)Peek ())->GetValue (k, lambda); } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // TriangularVariableImpl methods -class TriangularVariableImpl : public RandomVariableBase { +class TriangularVariableImpl : public RandomVariableBase +{ public: /** * Creates a triangle distribution random number generator in the * range [0.0 .. 1.0), with mean of 0.5 */ - TriangularVariableImpl(); + TriangularVariableImpl (); /** * Creates a triangle distribution random number generator with the specified @@ -1457,65 +1637,81 @@ public: * \param l High end of the range * \param mean mean of the distribution */ - TriangularVariableImpl(double s, double l, double mean); + TriangularVariableImpl (double s, double l, double mean); + + TriangularVariableImpl (const TriangularVariableImpl& c); - TriangularVariableImpl(const TriangularVariableImpl& c); - /** * \return A value from this distribution */ - virtual double GetValue(); - virtual RandomVariableBase* Copy(void) const; + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; private: double m_min; double m_max; - double m_mode; //easier to work with the mode internally instead of the mean - //they are related by the simple: mean = (min+max+mode)/3 + double m_mode; // easier to work with the mode internally instead of the mean + // they are related by the simple: mean = (min+max+mode)/3 }; -TriangularVariableImpl::TriangularVariableImpl() - : m_min(0), m_max(1), m_mode(0.5) { } - -TriangularVariableImpl::TriangularVariableImpl(double s, double l, double mean) - : m_min(s), m_max(l), m_mode(3.0*mean-s-l) { } - -TriangularVariableImpl::TriangularVariableImpl(const TriangularVariableImpl& c) - : RandomVariableBase(c), m_min(c.m_min), m_max(c.m_max), m_mode(c.m_mode) { } - -double TriangularVariableImpl::GetValue() +TriangularVariableImpl::TriangularVariableImpl () + : m_min (0), + m_max (1), + m_mode (0.5) { - if(!m_generator) +} + +TriangularVariableImpl::TriangularVariableImpl (double s, double l, double mean) + : m_min (s), + m_max (l), + m_mode (3.0 * mean - s - l) +{ +} + +TriangularVariableImpl::TriangularVariableImpl (const TriangularVariableImpl& c) + : RandomVariableBase (c), + m_min (c.m_min), + m_max (c.m_max), + m_mode (c.m_mode) +{ +} + +double TriangularVariableImpl::GetValue () +{ + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } - double u = m_generator->RandU01(); - if(u <= (m_mode - m_min) / (m_max - m_min) ) + double u = m_generator->RandU01 (); + if (u <= (m_mode - m_min) / (m_max - m_min) ) { - return m_min + sqrt(u * (m_max - m_min) * (m_mode - m_min) ); + return m_min + sqrt (u * (m_max - m_min) * (m_mode - m_min) ); } else { - return m_max - sqrt( (1-u) * (m_max - m_min) * (m_max - m_mode) ); + return m_max - sqrt ( (1 - u) * (m_max - m_min) * (m_max - m_mode) ); } } -RandomVariableBase* TriangularVariableImpl::Copy() const +RandomVariableBase* TriangularVariableImpl::Copy () const { - return new TriangularVariableImpl(*this); + return new TriangularVariableImpl (*this); } -TriangularVariable::TriangularVariable() +TriangularVariable::TriangularVariable () : RandomVariable (TriangularVariableImpl ()) -{} -TriangularVariable::TriangularVariable(double s, double l, double mean) +{ +} +TriangularVariable::TriangularVariable (double s, double l, double mean) : RandomVariable (TriangularVariableImpl (s,l,mean)) -{} +{ +} -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // ZipfVariableImpl -class ZipfVariableImpl : public RandomVariableBase { +class ZipfVariableImpl : public RandomVariableBase +{ public: /** * \param n the number of possible items @@ -1532,12 +1728,12 @@ public: * \return A random value from this distribution */ virtual double GetValue (); - virtual RandomVariableBase* Copy(void) const; + virtual RandomVariableBase* Copy (void) const; private: long m_n; double m_alpha; - double m_c; //the normalization constant + double m_c; // the normalization constant }; @@ -1547,38 +1743,42 @@ RandomVariableBase* ZipfVariableImpl::Copy () const } ZipfVariableImpl::ZipfVariableImpl () - :m_n(1), m_alpha(0), m_c(1) + : m_n (1), + m_alpha (0), + m_c (1) { } ZipfVariableImpl::ZipfVariableImpl (long n, double alpha) - :m_n(n), m_alpha(alpha), m_c(0) + : m_n (n), + m_alpha (alpha), + m_c (0) { - //calculate the normalization constant c - for(int i=1;i<=n;i++) + // calculate the normalization constant c + for (int i = 1; i <= n; i++) { - m_c+=(1.0/pow((double)i,alpha)); + m_c += (1.0 / pow ((double)i,alpha)); } - m_c=1.0/m_c; + m_c = 1.0 / m_c; } double ZipfVariableImpl::GetValue () { - if(!m_generator) + if (!m_generator) { - m_generator = new RngStream(); + m_generator = new RngStream (); } - double u = m_generator->RandU01(); - double sum_prob=0,zipf_value=0; - for(int i=1;i<=m_n;i++) + double u = m_generator->RandU01 (); + double sum_prob = 0,zipf_value = 0; + for (int i = 1; i <= m_n; i++) { - sum_prob+=m_c/pow((double)i,m_alpha); - if(sum_prob>u) + sum_prob += m_c / pow ((double)i,m_alpha); + if (sum_prob > u) { - zipf_value=i; + zipf_value = i; break; } } @@ -1587,14 +1787,103 @@ ZipfVariableImpl::GetValue () ZipfVariable::ZipfVariable () : RandomVariable (ZipfVariableImpl ()) -{} +{ +} ZipfVariable::ZipfVariable (long n, double alpha) : RandomVariable (ZipfVariableImpl (n, alpha)) -{} +{ +} -std::ostream &operator << (std::ostream &os, const RandomVariable &var) +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ZetaVariableImpl +class ZetaVariableImpl : public RandomVariableBase +{ +public: + /** + * \param alpha the alpha parameter + */ + ZetaVariableImpl (double alpha); + + /** + * \A zipf variable with alpha=1.1 + */ + ZetaVariableImpl (); + + /** + * \return A random value from this distribution + */ + virtual double GetValue (); + virtual RandomVariableBase* Copy (void) const; + +private: + double m_alpha; + double m_b; // just for calculus simplifications +}; + + +RandomVariableBase* ZetaVariableImpl::Copy () const +{ + return new ZetaVariableImpl (m_alpha); +} + +ZetaVariableImpl::ZetaVariableImpl () + : m_alpha (3.14), + m_b (pow (2.0, 2.14)) +{ +} + + +ZetaVariableImpl::ZetaVariableImpl (double alpha) + : m_alpha (alpha), + m_b (pow (2.0, alpha - 1.0)) +{ +} + +/* + The code for the following generator functions is borrowed from: + L. Devroye: Non-Uniform Random Variate Generation, Springer-Verlag, New York, 1986. + page 551 + */ +double +ZetaVariableImpl::GetValue () +{ + if (!m_generator) + { + m_generator = new RngStream (); + } + + double u, v; + double X, T; + double test; + + do + { + u = m_generator->RandU01 (); + v = m_generator->RandU01 (); + X = floor (pow (u, -1.0 / (m_alpha - 1.0))); + T = pow (1.0 + 1.0 / X, m_alpha - 1.0); + test = v * X * (T - 1.0) / (m_b - 1.0); + } + while ( test > (T / m_b) ); + + return X; +} + +ZetaVariable::ZetaVariable () + : RandomVariable (ZetaVariableImpl ()) +{ +} + +ZetaVariable::ZetaVariable (double alpha) + : RandomVariable (ZetaVariableImpl (alpha)) +{ +} + + +std::ostream & operator << (std::ostream &os, const RandomVariable &var) { RandomVariableBase *base = var.Peek (); ConstantVariableImpl *constant = dynamic_cast (base); @@ -1624,7 +1913,7 @@ std::ostream &operator << (std::ostream &os, const RandomVariable &var) os.setstate (std::ios_base::badbit); return os; } -std::istream &operator >> (std::istream &is, RandomVariable &var) +std::istream & operator >> (std::istream &is, RandomVariable &var) { std::string value; is >> value; @@ -1715,7 +2004,9 @@ class BasicRandomNumberTestCase : public TestCase { public: BasicRandomNumberTestCase (); - virtual ~BasicRandomNumberTestCase () {} + virtual ~BasicRandomNumberTestCase () + { + } private: virtual bool DoRun (void); @@ -1744,7 +2035,7 @@ BasicRandomNumberTestCase::DoRun (void) vector samples; const int NSAMPLES = 10000; double sum = 0; - + // // Get and store a bunch of samples. As we go along sum them and then find // the mean value of the samples. @@ -1757,7 +2048,7 @@ BasicRandomNumberTestCase::DoRun (void) } double obtainedMean = sum / NSAMPLES; NS_TEST_EXPECT_MSG_EQ_TOL (obtainedMean, desiredMean, 0.1, "Got unexpected mean value from LogNormalVariable"); - + // // Wander back through the saved stamples and find their standard deviation // @@ -1777,7 +2068,9 @@ class RandomNumberSerializationTestCase : public TestCase { public: RandomNumberSerializationTestCase (); - virtual ~RandomNumberSerializationTestCase () {} + virtual ~RandomNumberSerializationTestCase () + { + } private: virtual bool DoRun (void); @@ -1794,7 +2087,7 @@ RandomNumberSerializationTestCase::DoRun (void) RandomVariableValue val; val.DeserializeFromString ("Uniform:0.1:0.2", MakeRandomVariableChecker ()); RandomVariable rng = val.Get (); - NS_TEST_ASSERT_MSG_EQ (val.SerializeToString (MakeRandomVariableChecker ()), "Uniform:0.1:0.2", + NS_TEST_ASSERT_MSG_EQ (val.SerializeToString (MakeRandomVariableChecker ()), "Uniform:0.1:0.2", "Deserialize and Serialize \"Uniform:0.1:0.2\" mismatch"); val.DeserializeFromString ("Normal:0.1:0.2", MakeRandomVariableChecker ()); @@ -1825,4 +2118,4 @@ BasicRandomNumberTestSuite::BasicRandomNumberTestSuite () BasicRandomNumberTestSuite BasicRandomNumberTestSuite; -}//namespace ns3 +} // namespace ns3 diff --git a/src/core/random-variable.h b/src/core/random-variable.h index 715309eff..189baef04 100644 --- a/src/core/random-variable.h +++ b/src/core/random-variable.h @@ -43,7 +43,6 @@ class RandomVariableBase; class SeedManager { public: - /** * \brief set the seed * it will duplicate the seed value 6 times @@ -57,45 +56,45 @@ public: * Note, while the underlying RNG takes six integer values as a seed; * it is sufficient to set these all to the same integer, so we provide * a simpler interface here that just takes one integer. - */ + */ static void SetSeed (uint32_t seed); - + /** * \brief Get the seed value * \return the seed value * * Note: returns the first of the six seed values used in the underlying RNG */ - static uint32_t GetSeed (); - - /** - * \brief Set the run number of simulation - * - * \code - * SeedManager::SetSeed(12); - * int N = atol(argv[1]); //read in run number from command line - * SeedManager::SetRun(N); - * UniformVariable x(0,10); - * ExponentialVariable y(2902); - * \endcode - * In this example, N could successivly be equal to 1,2,3, etc. and the user - * would continue to get independent runs out of the single simulation. For - * this simple example, the following might work: - * \code - * ./simulation 0 - * ...Results for run 0:... - * - * ./simulation 1 - * ...Results for run 1:... - * \endcode - */ + static uint32_t GetSeed (); + + /** + * \brief Set the run number of simulation + * + * \code + * SeedManager::SetSeed(12); + * int N = atol(argv[1]); //read in run number from command line + * SeedManager::SetRun(N); + * UniformVariable x(0,10); + * ExponentialVariable y(2902); + * \endcode + * In this example, N could successivly be equal to 1,2,3, etc. and the user + * would continue to get independent runs out of the single simulation. For + * this simple example, the following might work: + * \code + * ./simulation 0 + * ...Results for run 0:... + * + * ./simulation 1 + * ...Results for run 1:... + * \endcode + */ static void SetRun (uint32_t run); /** * \returns the current run number * @sa SetRun */ static uint32_t GetRun (void); - + /** * \brief Check if seed value is valid if wanted to be used as seed * \return true if valid and false if invalid @@ -111,7 +110,7 @@ public: * Note: The underlying random number generation method used * by NS-3 is the RngStream code by Pierre L'Ecuyer at * the University of Montreal. - * + * * NS-3 has a rich set of random number generators. * Class RandomVariable defines the base class functionalty * required for all random number generators. By default, the underlying @@ -119,13 +118,13 @@ public: * coming from the ns3::GlobalValue \ref GlobalValueRngSeed "RngSeed" and \ref GlobalValueRngRun "RngRun". */ class RandomVariable -{ +{ public: - RandomVariable(); - RandomVariable(const RandomVariable&o); + RandomVariable (); + RandomVariable (const RandomVariable&o); RandomVariable &operator = (const RandomVariable &o); - ~RandomVariable(); - + ~RandomVariable (); + /** * \brief Returns a random double from the underlying distribution * \return A floating point random value @@ -139,13 +138,13 @@ public: uint32_t GetInteger (void) const; private: - friend std::ostream &operator << (std::ostream &os, const RandomVariable &var); - friend std::istream &operator >> (std::istream &os, RandomVariable &var); + friend std::ostream & operator << (std::ostream &os, const RandomVariable &var); + friend std::istream & operator >> (std::istream &os, RandomVariable &var); RandomVariableBase *m_variable; protected: RandomVariable (const RandomVariableBase &variable); - RandomVariableBase *Peek (void) const; + RandomVariableBase * Peek (void) const; }; /** @@ -153,7 +152,7 @@ protected: * \ingroup randomvariable * * This class supports the creation of objects that return random numbers - * from a fixed uniform distribution. It also supports the generation of + * from a fixed uniform distribution. It also supports the generation of * single random numbers from various uniform distributions. * * The low end of the range is always included and the high end @@ -164,40 +163,40 @@ protected: * UniformVariable::GetSingleValue(100,1000); //returns a value [100,1000) * \endcode */ -class UniformVariable : public RandomVariable +class UniformVariable : public RandomVariable { public: /** * Creates a uniform random number generator in the * range [0.0 .. 1.0). */ - UniformVariable(); + UniformVariable (); /** * Creates a uniform random number generator with the specified range * \param s Low end of the range * \param l High end of the range */ - UniformVariable(double s, double l); + UniformVariable (double s, double l); /** * \brief call RandomVariable::GetValue * \return A floating point random value * - * Note: we have to re-implement this method here because the method is + * Note: we have to re-implement this method here because the method is * overloaded below for the two-argument variant and the c++ name resolution - * rules don't work well with overloads split between parent and child + * rules don't work well with overloads split between parent and child * classes. */ double GetValue (void) const; - + /** * \brief Returns a random double with the specified range * \param s Low end of the range * \param l High end of the range * \return A floating point random value */ - double GetValue(double s, double l); + double GetValue (double s, double l); /** * \brief Returns a random unsigned integer from the interval [s,l] including both ends. @@ -215,26 +214,27 @@ public: * Class ConstantVariable defines a random number generator that * returns the same value every sample. */ -class ConstantVariable : public RandomVariable { +class ConstantVariable : public RandomVariable +{ public: /** * Construct a ConstantVariable RNG that returns zero every sample */ - ConstantVariable(); - + ConstantVariable (); + /** * Construct a ConstantVariable RNG that returns the specified value * every sample. * \param c Unchanging value for this RNG. */ - ConstantVariable(double c); + ConstantVariable (double c); /** * \brief Specify a new constant RNG for this generator. * \param c New constant value for this RNG. */ - void SetConstant(double c); + void SetConstant (double c); }; @@ -244,10 +244,10 @@ public: * * Class SequentialVariable defines a random number generator that * returns a sequential sequence. The sequence monotonically - * increases for a period, then wraps around to the low value + * increases for a period, then wraps around to the low value * and begins monotonicaly increasing again. */ -class SequentialVariable : public RandomVariable +class SequentialVariable : public RandomVariable { public: /** @@ -261,7 +261,7 @@ public: * \param i Increment between sequence values * \param c Number of times each member of the sequence is repeated */ - SequentialVariable(double f, double l, double i = 1, uint32_t c = 1); + SequentialVariable (double f, double l, double i = 1, uint32_t c = 1); /** * \brief Constructor for the SequentialVariable RNG. @@ -273,7 +273,7 @@ public: * \param i Reference to a RandomVariable for the sequence increment * \param c Number of times each member of the sequence is repeated */ - SequentialVariable(double f, double l, const RandomVariable& i, uint32_t c = 1); + SequentialVariable (double f, double l, const RandomVariable& i, uint32_t c = 1); }; @@ -282,17 +282,18 @@ public: * \ingroup randomvariable * * This class supports the creation of objects that return random numbers - * from a fixed exponential distribution. It also supports the generation of + * from a fixed exponential distribution. It also supports the generation of * single random numbers from various exponential distributions. * - * The probability density function of an exponential variable + * The probability density function of an exponential variable * is defined over the interval [0, +inf) as: * \f$ \alpha e^{-\alpha x} \f$ - * where \f$ \alpha = \frac{1}{mean} \f$ + * where \f$ \alpha = \frac{1}{mean} \f$ + * + * The bounded version is defined over the interval [0,b] as: + * \f$ \alpha e^{-\alpha x} \quad x \in [0,b] \f$. + * Note that in this case the true mean is \f$ 1/\alpha - b/(e^{\alpha \, b}-1) \f$ * - * The bounded version is defined over the internal [0,+inf) as: - * \f$ \left\{ \begin{array}{cl} \alpha e^{-\alpha x} & x < bound \\ bound & x > bound \end{array}\right. \f$ - * * \code * ExponentialVariable x(3.14); * x.GetValue(); //will always return with mean 3.14 @@ -301,33 +302,33 @@ public: * \endcode * */ -class ExponentialVariable : public RandomVariable -{ +class ExponentialVariable : public RandomVariable +{ public: /** * Constructs an exponential random variable with a mean * value of 1.0. */ - ExponentialVariable(); + ExponentialVariable (); /** * \brief Constructs an exponential random variable with a specified mean * \param m Mean value for the random variable */ - explicit ExponentialVariable(double m); + explicit ExponentialVariable (double m); /** - * \brief Constructs an exponential random variable with spefified - * \brief mean and upper limit. + * \brief Constructs an exponential random variable with specified + * mean and upper limit. * * Since exponential distributions can theoretically return unbounded values, * it is sometimes useful to specify a fixed upper limit. Note however when - * the upper limit is specified, the true mean of the distribution is - * slightly smaller than the mean value specified. + * the upper limit is specified, the true mean of the distribution is + * slightly smaller than the mean value specified: \f$ m - b/(e^{b/m}-1) \f$. * \param m Mean value of the random variable * \param b Upper bound on returned values */ - ExponentialVariable(double m, double b); + ExponentialVariable (double m, double b); }; @@ -336,13 +337,13 @@ public: * \ingroup randomvariable * * This class supports the creation of objects that return random numbers - * from a fixed pareto distribution. It also supports the generation of + * from a fixed pareto distribution. It also supports the generation of * single random numbers from various pareto distributions. * * The probability density function is defined over the range [\f$x_m\f$,+inf) as: - * \f$ k \frac{x_m^k}{x^{k+1}}\f$ where \f$x_m > 0\f$ is called the location + * \f$ k \frac{x_m^k}{x^{k+1}}\f$ where \f$x_m > 0\f$ is called the location * parameter and \f$ k > 0\f$ is called the pareto index or shape. - * + * * The parameter \f$ x_m \f$ can be infered from the mean and the parameter \f$ k \f$ * with the equation \f$ x_m = mean \frac{k-1}{k}, k > 1\f$. * @@ -367,7 +368,7 @@ public: * parameter of 1.5 * \param m Mean value of the distribution */ - explicit ParetoVariable(double m); + explicit ParetoVariable (double m); /** * Constructs a pareto random variable with the specified mean value and @@ -375,7 +376,7 @@ public: * \param m Mean value of the distribution * \param s Shape parameter for the distribution */ - ParetoVariable(double m, double s); + ParetoVariable (double m, double s); /** * \brief Constructs a pareto random variable with the specified mean @@ -389,7 +390,7 @@ public: * \param s Shape parameter * \param b Upper limit on returned values */ - ParetoVariable(double m, double s, double b); + ParetoVariable (double m, double s, double b); }; @@ -398,7 +399,7 @@ public: * \ingroup randomvariable * * This class supports the creation of objects that return random numbers - * from a fixed weibull distribution. It also supports the generation of + * from a fixed weibull distribution. It also supports the generation of * single random numbers from various weibull distributions. * * The probability density function is defined over the interval [0, +inf] @@ -407,13 +408,14 @@ public: * specified mean is related to the scale and shape parameters by the following relation: * \f$ mean = \lambda\Gamma\left(1+\frac{1}{k}\right) \f$ where \f$ \Gamma \f$ is the Gamma function. */ -class WeibullVariable : public RandomVariable { +class WeibullVariable : public RandomVariable +{ public: /** * Constructs a weibull random variable with a mean * value of 1.0 and a shape (alpha) parameter of 1 */ - WeibullVariable(); + WeibullVariable (); /** @@ -421,7 +423,7 @@ public: * value and a shape (alpha) parameter of 1.5. * \param m mean value of the distribution */ - WeibullVariable(double m) ; + WeibullVariable (double m); /** * Constructs a weibull random variable with the specified mean @@ -429,20 +431,20 @@ public: * \param m Mean value for the distribution. * \param s Shape (alpha) parameter for the distribution. */ - WeibullVariable(double m, double s); + WeibullVariable (double m, double s); - /** - * \brief Constructs a weibull random variable with the specified mean - * \brief value, shape (alpha), and upper bound. - * Since WeibullVariable distributions can theoretically return unbounded values, - * it is sometimes usefull to specify a fixed upper limit. Note however - * that when the upper limit is specified, the true mean of the distribution - * is slightly smaller than the mean value specified. - * \param m Mean value for the distribution. - * \param s Shape (alpha) parameter for the distribution. - * \param b Upper limit on returned values - */ - WeibullVariable(double m, double s, double b); + /** + * \brief Constructs a weibull random variable with the specified mean + * \brief value, shape (alpha), and upper bound. + * Since WeibullVariable distributions can theoretically return unbounded values, + * it is sometimes usefull to specify a fixed upper limit. Note however + * that when the upper limit is specified, the true mean of the distribution + * is slightly smaller than the mean value specified. + * \param m Mean value for the distribution. + * \param s Shape (alpha) parameter for the distribution. + * \param b Upper limit on returned values + */ + WeibullVariable (double m, double s, double b); }; @@ -450,9 +452,9 @@ public: * \brief Class NormalVariable defines a random variable with a * normal (Gaussian) distribution. * \ingroup randomvariable - * + * * This class supports the creation of objects that return random numbers - * from a fixed normal distribution. It also supports the generation of + * from a fixed normal distribution. It also supports the generation of * single random numbers from various normal distributions. * * The density probability function is defined over the interval (-inf,+inf) @@ -466,15 +468,15 @@ public: /** * Constructs an normal random variable with a mean * value of 0 and variance of 1. - */ - NormalVariable(); + */ + NormalVariable (); /** * \brief Construct a normal random variable with specified mean and variance. * \param m Mean value * \param v Variance - */ - NormalVariable(double m, double v); + */ + NormalVariable (double m, double v); /** * \brief Construct a normal random variable with specified mean and variance @@ -482,15 +484,15 @@ public: * \param v Variance * \param b Bound. The NormalVariable is bounded symetrically about the mean * [mean-bound,mean+bound] - */ - NormalVariable(double m, double v, double b); + */ + NormalVariable (double m, double v, double b); }; /** * \brief EmpiricalVariable distribution random var * \ingroup randomvariable * - * Defines a random variable that has a specified, empirical + * Defines a random variable that has a specified, empirical * distribution. The distribution is specified by a * series of calls to the CDF member function, specifying a * value and the probability that the function value is less than @@ -501,19 +503,20 @@ public: * as inverse transform sampling: * (http://en.wikipedia.org/wiki/Inverse_transform_sampling). */ -class EmpiricalVariable : public RandomVariable { +class EmpiricalVariable : public RandomVariable +{ public: /** * Constructor for the EmpiricalVariable random variables. */ - explicit EmpiricalVariable(); + explicit EmpiricalVariable (); /** * \brief Specifies a point in the empirical distribution * \param v The function value for this point * \param c Probability that the function is less than or equal to v */ - void CDF(double v, double c); // Value, prob <= Value + void CDF (double v, double c); // Value, prob <= Value protected: EmpiricalVariable (const RandomVariableBase &variable); }; @@ -527,10 +530,10 @@ protected: * sampling interpolation described in the EmpiricalVariable documentation * is modified to only return integers. */ -class IntEmpiricalVariable : public EmpiricalVariable +class IntEmpiricalVariable : public EmpiricalVariable { public: - IntEmpiricalVariable(); + IntEmpiricalVariable (); }; /** @@ -550,13 +553,13 @@ public: * * Creates a generator that returns successive elements of the d array * on successive calls to ::Value(). Note that the d pointer is copied - * for use by the generator (shallow-copy), not its contents, so the - * contents of the array d points to have to remain unchanged for the use + * for use by the generator (shallow-copy), not its contents, so the + * contents of the array d points to have to remain unchanged for the use * of DeterministicVariable to be meaningful. * \param d Pointer to array of random values to return in sequence * \param c Number of values in the array */ - explicit DeterministicVariable(double* d, uint32_t c); + explicit DeterministicVariable (double* d, uint32_t c); }; /** @@ -573,7 +576,7 @@ public: * * The probability density function is defined over the interval [0,+inf) as: * \f$ \frac{1}{x\sigma\sqrt{2\pi}} e^{-\frac{(ln(x) - \mu)^2}{2\sigma^2}}\f$ - * where \f$ mean = e^{\mu+\frac{\sigma^2}{2}} \f$ and + * where \f$ mean = e^{\mu+\frac{\sigma^2}{2}} \f$ and * \f$ variance = (e^{\sigma^2}-1)e^{2\mu+\sigma^2}\f$ * * The \f$ \mu \f$ and \f$ \sigma \f$ parameters can be calculated from the mean @@ -581,7 +584,7 @@ public: * \f$ \mu = ln(mean) - \frac{1}{2}ln\left(1+\frac{stddev}{mean^2}\right)\f$, and, * \f$ \sigma = \sqrt{ln\left(1+\frac{stddev}{mean^2}\right)}\f$ */ -class LogNormalVariable : public RandomVariable +class LogNormalVariable : public RandomVariable { public: /** @@ -595,18 +598,18 @@ public: * \brief Gamma Distributed Random Variable * \ingroup randomvariable * - * GammaVariable defines a random variable with gamma distribution. + * GammaVariable defines a random variable with gamma distribution. * * This class supports the creation of objects that return random numbers - * from a fixed gamma distribution. It also supports the generation of + * from a fixed gamma distribution. It also supports the generation of * single random numbers from various gamma distributions. * * The probability density function is defined over the interval [0,+inf) as: * \f$ x^{\alpha-1} \frac{e^{-\frac{x}{\beta}}}{\beta^\alpha \Gamma(\alpha)}\f$ - * where \f$ mean = \alpha\beta \f$ and + * where \f$ mean = \alpha\beta \f$ and * \f$ variance = \alpha \beta^2\f$ */ -class GammaVariable : public RandomVariable +class GammaVariable : public RandomVariable { public: /** @@ -622,7 +625,7 @@ public: /** * \brief call RandomVariable::GetValue - * \return A floating point random value + * \return A floating point random value * * Note: we have to re-implement this method here because the method is * overloaded below for the two-argument variant and the c++ name resolution @@ -630,14 +633,14 @@ public: * classes. */ double GetValue (void) const; - + /** * \brief Returns a gamma random distributed double with parameters alpha and beta. * \param alpha alpha parameter of the gamma distribution * \param beta beta parameter of the gamma distribution * \return A floating point random value */ - double GetValue(double alpha, double beta) const; + double GetValue (double alpha, double beta) const; }; /** @@ -656,10 +659,10 @@ public: * * The probability density function is defined over the interval [0,+inf) as: * \f$ \frac{x^{k-1} e^{-\frac{x}{\lambda}}}{\lambda^k (k-1)!}\f$ - * where \f$ mean = k \lambda \f$ and + * where \f$ mean = k \lambda \f$ and * \f$ variance = k \lambda^2\f$ */ -class ErlangVariable : public RandomVariable +class ErlangVariable : public RandomVariable { public: /** @@ -675,7 +678,7 @@ public: /** * \brief call RandomVariable::GetValue - * \return A floating point random value + * \return A floating point random value * * Note: we have to re-implement this method here because the method is * overloaded below for the two-argument variant and the c++ name resolution @@ -683,50 +686,87 @@ public: * classes. */ double GetValue (void) const; - + /** * \brief Returns an Erlang random distributed double with parameters k and lambda. * \param k k parameter of the Erlang distribution. Must be a non-negative integer. * \param lambda lambda parameter of the Erlang distribution * \return A floating point random value */ - double GetValue(unsigned int k, double lambda) const; + double GetValue (unsigned int k, double lambda) const; }; /** - * \brief Zipf Distributed random var (between 1 and n included) + * \brief Zipf Distributed Random Variable * \ingroup randomvariable * + * ZipfVariable defines a discrete random variable with Zipf distribution. + * + * The Zipf's law states that given some corpus of natural language + * utterances, the frequency of any word is inversely proportional + * to its rank in the frequency table. + * + * Zipf's distribution have two parameters, alpha and N, where: + * \f$ \alpha > 0 \f$ (real) and \f$ N \in \{1,2,3 \dots\}\f$ (integer). + * Probability Mass Function is \f$ f(k; \alpha, N) = k^{-\alpha}/ H_{N,\alpha} \f$ + * where \f$ H_{N,\alpha} = \sum_{n=1}^N n^{-\alpha} \f$ */ -class ZipfVariable : public RandomVariable +class ZipfVariable : public RandomVariable { public: /** - * \param n the number of possible items - * \param alpha the alpha parameter + * \brief Returns a Zipf random variable with parameters N and alpha. + * \param N the number of possible items. Must be a positive integer. + * \param alpha the alpha parameter. Must be a strictly positive real. */ - ZipfVariable (long n, double alpha); + ZipfVariable (long N, double alpha); /** - * A zipf variable with N=1 and alpha=0 + * Constructs a Zipf random variable with N=1 and alpha=0. */ ZipfVariable (); }; +/** + * \brief Zeta Distributed Distributed Random Variable + * \ingroup randomvariable + * + * ZetaVariable defines a discrete random variable with Zeta distribution. + * + * The Zeta distribution is closely related to Zipf distribution when N goes to infinity. + * + * Zeta distribution has one parameter, alpha, \f$ \alpha > 1 \f$ (real). + * Probability Mass Function is \f$ f(k; \alpha) = k^{-\alpha}/\zeta(\alpha) \f$ + * where \f$ \zeta(\alpha) \f$ is the Riemann zeta function ( \f$ \sum_{n=1}^\infty n^{-\alpha} ) \f$ + */ +class ZetaVariable : public RandomVariable +{ +public: + /** + * \brief Returns a Zeta random variable with parameter alpha. + * \param alpha the alpha parameter. Must be a strictly greater than 1, real. + */ + ZetaVariable (double alpha); + /** + * Constructs a Zeta random variable with alpha=3.14 + */ + ZetaVariable (); +}; + /** * \brief Triangularly Distributed random var * \ingroup randomvariable - * + * * This distribution is a triangular distribution. The probablility density * is in the shape of a triangle. */ -class TriangularVariable : public RandomVariable +class TriangularVariable : public RandomVariable { public: /** * Creates a triangle distribution random number generator in the * range [0.0 .. 1.0), with mean of 0.5 */ - TriangularVariable(); + TriangularVariable (); /** * Creates a triangle distribution random number generator with the specified @@ -735,12 +775,12 @@ public: * \param l High end of the range * \param mean mean of the distribution */ - TriangularVariable(double s, double l, double mean); + TriangularVariable (double s, double l, double mean); }; -std::ostream &operator << (std::ostream &os, const RandomVariable &var); -std::istream &operator >> (std::istream &os, RandomVariable &var); +std::ostream & operator << (std::ostream &os, const RandomVariable &var); +std::istream & operator >> (std::istream &os, RandomVariable &var); /** * \class ns3::RandomVariableValue @@ -751,7 +791,7 @@ ATTRIBUTE_VALUE_DEFINE (RandomVariable); ATTRIBUTE_CHECKER_DEFINE (RandomVariable); ATTRIBUTE_ACCESSOR_DEFINE (RandomVariable); -}//namespace ns3 +} // namespace ns3 #endif diff --git a/src/devices/wifi/block-ack-agreement.cc b/src/devices/wifi/block-ack-agreement.cc new file mode 100644 index 000000000..2c2e408f9 --- /dev/null +++ b/src/devices/wifi/block-ack-agreement.cc @@ -0,0 +1,119 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#include "block-ack-agreement.h" + +namespace ns3 { + +BlockAckAgreement::BlockAckAgreement () + : m_amsduSupported (0), + m_blockAckPolicy (1), + m_inactivityEvent () +{} + +BlockAckAgreement::BlockAckAgreement (Mac48Address peer, uint8_t tid) + : m_amsduSupported (0), + m_blockAckPolicy (1), + m_inactivityEvent () +{ + m_tid = tid; + m_peer = peer; +} + +BlockAckAgreement::~BlockAckAgreement () +{ + m_inactivityEvent.Cancel (); +} + +void +BlockAckAgreement::SetBufferSize (uint16_t bufferSize) +{ + NS_ASSERT (bufferSize <= 1024); + m_bufferSize = bufferSize; +} +void +BlockAckAgreement::SetTimeout (uint16_t timeout) +{ + m_timeout = timeout; +} +void +BlockAckAgreement::SetStartingSequence (uint16_t seq) +{ + NS_ASSERT (seq < 4096); + m_startingSeq = seq; +} +void +BlockAckAgreement::SetImmediateBlockAck (void) +{ + m_blockAckPolicy = 1; +} +void +BlockAckAgreement::SetDelayedBlockAck (void) +{ + m_blockAckPolicy = 0; +} +void +BlockAckAgreement::SetAmsduSupport (bool supported) +{ + m_amsduSupported = supported; +} + +uint8_t +BlockAckAgreement::GetTid (void) const +{ + return m_tid; +} +Mac48Address +BlockAckAgreement::GetPeer (void) const +{ + return m_peer; +} +uint16_t +BlockAckAgreement::GetBufferSize (void) const +{ + return m_bufferSize; +} +uint16_t +BlockAckAgreement::GetTimeout (void) const +{ + return m_timeout; +} +uint16_t +BlockAckAgreement::GetStartingSequence (void) const +{ + return m_startingSeq; +} +uint16_t +BlockAckAgreement::GetStartingSequenceControl (void) const +{ + uint16_t seqControl = (m_startingSeq<<4) | 0xfff0; + return seqControl; +} +bool +BlockAckAgreement::IsImmediateBlockAck (void) const +{ + return (m_blockAckPolicy == 1); +} +bool +BlockAckAgreement::IsAmsduSupported (void) const +{ + return (m_amsduSupported == 1)?true:false; +} + +} //namespace ns3 diff --git a/src/devices/wifi/block-ack-agreement.h b/src/devices/wifi/block-ack-agreement.h new file mode 100644 index 000000000..34f4459e4 --- /dev/null +++ b/src/devices/wifi/block-ack-agreement.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#ifndef BLOCK_ACK_AGREEMENT_H +#define BLOCK_ACK_AGREEMENT_H + +#include "ns3/mac48-address.h" +#include "ns3/event-id.h" + +namespace ns3 { +/** + * \brief Maintains information for a block ack agreement. + */ +class BlockAckAgreement +{ + friend class MacLow; +public: + BlockAckAgreement (); + BlockAckAgreement (Mac48Address peer, uint8_t tid); + ~BlockAckAgreement (); + void SetBufferSize (uint16_t bufferSize); + void SetTimeout (uint16_t timeout); + void SetStartingSequence (uint16_t seq); + void SetImmediateBlockAck (void); + void SetDelayedBlockAck (void); + void SetAmsduSupport (bool supported); + + uint8_t GetTid (void) const; + Mac48Address GetPeer (void) const; + uint16_t GetBufferSize (void) const; + uint16_t GetTimeout (void) const; + uint16_t GetStartingSequence (void) const; + uint16_t GetStartingSequenceControl (void) const; + bool IsImmediateBlockAck (void) const; + bool IsAmsduSupported (void) const; + +protected: + + Mac48Address m_peer; + uint8_t m_amsduSupported; + uint8_t m_blockAckPolicy; /* represents type of block ack: immediate or delayed */ + uint8_t m_tid; + uint16_t m_bufferSize; + uint16_t m_timeout; + uint16_t m_startingSeq; + + EventId m_inactivityEvent; +}; + +} //namespace ns3 + +#endif /* BLOCK_ACK_AGREEMENT_H */ diff --git a/src/devices/wifi/block-ack-manager.cc b/src/devices/wifi/block-ack-manager.cc new file mode 100644 index 000000000..845d387ca --- /dev/null +++ b/src/devices/wifi/block-ack-manager.cc @@ -0,0 +1,617 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#include "ns3/log.h" +#include "ns3/assert.h" +#include "ns3/simulator.h" +#include "ns3/fatal-error.h" + +#include "block-ack-manager.h" +#include "mgt-headers.h" +#include "ctrl-headers.h" +#include "wifi-mac-header.h" +#include "edca-txop-n.h" +#include "mac-low.h" +#include "wifi-mac-queue.h" +#include "mac-tx-middle.h" + +NS_LOG_COMPONENT_DEFINE ("BlockAckManager"); + +namespace ns3 { + +BlockAckManager::Item::Item () +{} + +BlockAckManager::Item::Item (Ptr packet, const WifiMacHeader &hdr, Time tStamp) + : packet (packet), hdr (hdr), timestamp (tStamp) +{} + +Bar::Bar () +{} + +Bar::Bar (Ptr bar, Mac48Address recipient, uint8_t tid, bool immediate) + : bar (bar), recipient (recipient), tid (tid), immediate (immediate) +{} + +BlockAckManager::BlockAckManager () +{} + +BlockAckManager::~BlockAckManager () +{ + m_queue = 0; + m_agreements.clear (); + m_retryPackets.clear (); +} + +bool +BlockAckManager::ExistsAgreement (Mac48Address recipient, uint8_t tid) const +{ + return (m_agreements.find (std::make_pair (recipient, tid)) != m_agreements.end ()); +} + +bool +BlockAckManager::ExistsAgreementInState (Mac48Address recipient, uint8_t tid, + enum OriginatorBlockAckAgreement::State state) const +{ + AgreementsCI it; + it = m_agreements.find (std::make_pair (recipient, tid)); + if (it != m_agreements.end ()) + { + switch (state) { + case OriginatorBlockAckAgreement::INACTIVE: + return it->second.first.IsInactive (); + case OriginatorBlockAckAgreement::ESTABLISHED: + return it->second.first.IsEstablished (); + case OriginatorBlockAckAgreement::PENDING: + return it->second.first.IsPending (); + case OriginatorBlockAckAgreement::UNSUCCESSFUL: + return it->second.first.IsUnsuccessful (); + default: + NS_FATAL_ERROR ("Invalid state for block ack agreement"); + } + } + return false; +} + +void +BlockAckManager::CreateAgreement (const MgtAddBaRequestHeader *reqHdr, Mac48Address recipient) +{ + pair key (recipient, reqHdr->GetTid ()); + OriginatorBlockAckAgreement agreement (recipient, reqHdr->GetTid ()); + agreement.SetStartingSequence (reqHdr->GetStartingSequence ()); + /* for now we assume that originator doesn't use this field. Use of this field + is mandatory only for recipient */ + agreement.SetBufferSize (0); + agreement.SetTimeout (reqHdr->GetTimeout ()); + agreement.SetAmsduSupport (reqHdr->IsAmsduSupported ()); + if (reqHdr->IsImmediateBlockAck ()) + { + agreement.SetImmediateBlockAck (); + } + else + { + agreement.SetDelayedBlockAck (); + } + agreement.SetState (OriginatorBlockAckAgreement::PENDING); + PacketQueue queue(0); + pair value (agreement, queue); + m_agreements.insert (make_pair (key, value)); + m_blockPackets (recipient, reqHdr->GetTid ()); +} + +void +BlockAckManager::DestroyAgreement (Mac48Address recipient, uint8_t tid) +{ + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + if (it != m_agreements.end ()) + { + for (list::iterator i = m_retryPackets.begin (); i != m_retryPackets.end ();) + { + if ((*i)->hdr.GetAddr1 () == recipient && (*i)->hdr.GetQosTid () == tid) + { + i = m_retryPackets.erase (i); + } + else + { + i++; + } + } + m_agreements.erase (it); + //remove scheduled bar + for (list::iterator i = m_bars.begin (); i != m_bars.end ();) + { + if (i->recipient == recipient && i->tid == tid) + { + i = m_bars.erase (i); + } + else + { + i++; + } + } + } +} + +void +BlockAckManager::UpdateAgreement (const MgtAddBaResponseHeader *respHdr, Mac48Address recipient) +{ + uint8_t tid = respHdr->GetTid (); + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + if (it != m_agreements.end ()) + { + OriginatorBlockAckAgreement& agreement = it->second.first; + agreement.SetBufferSize (respHdr->GetBufferSize () + 1); + agreement.SetTimeout (respHdr->GetTimeout ()); + agreement.SetAmsduSupport (respHdr->IsAmsduSupported ()); + if (respHdr->IsImmediateBlockAck ()) + { + agreement.SetImmediateBlockAck (); + } + else + { + agreement.SetDelayedBlockAck (); + } + agreement.SetState (OriginatorBlockAckAgreement::ESTABLISHED); + if (agreement.GetTimeout () != 0) + { + Time timeout = MicroSeconds (1024 * agreement.GetTimeout ()); + agreement.m_inactivityEvent = Simulator::Schedule (timeout, + &BlockAckManager::InactivityTimeout, + this, + recipient, tid); + } + } + m_unblockPackets (recipient, tid); +} + +void +BlockAckManager::StorePacket (Ptr packet, const WifiMacHeader &hdr, Time tStamp) +{ + NS_LOG_FUNCTION (this); + NS_ASSERT (hdr.IsQosData ()); + + uint8_t tid = hdr.GetQosTid (); + Mac48Address recipient = hdr.GetAddr1 (); + + Item item (packet, hdr, tStamp); + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + NS_ASSERT (it != m_agreements.end ()); + it->second.second.push_back (item); +} + +Ptr +BlockAckManager::GetNextPacket (WifiMacHeader &hdr) +{ + NS_LOG_FUNCTION (this); + Ptr packet = 0; + if (m_retryPackets.size () > 0) + { + CleanupBuffers (); + PacketQueueI queueIt = m_retryPackets.front (); + m_retryPackets.pop_front (); + packet = queueIt->packet; + hdr = queueIt->hdr; + hdr.SetRetry (); + NS_LOG_INFO ("Retry packet seq="<second.second.erase (queueIt); + } + } + return packet; +} + +bool +BlockAckManager::HasBar (struct Bar &bar) +{ + if (m_bars.size () > 0) + { + bar = m_bars.front (); + m_bars.pop_front (); + return true; + } + return false; +} + +bool +BlockAckManager::HasPackets (void) const +{ + return (m_retryPackets.size () > 0 || m_bars.size () > 0); +} + +uint32_t +BlockAckManager::GetNBufferedPackets (Mac48Address recipient, uint8_t tid) const +{ + uint32_t nPackets = 0; + if (ExistsAgreement (recipient, tid)) + { + AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid)); + PacketQueueCI queueIt = (*it).second.second.begin (); + uint16_t currentSeq = 0; + while (queueIt != (*it).second.second.end ()) + { + currentSeq = (*queueIt).hdr.GetSequenceNumber (); + nPackets++; + /* a fragmented packet must be counted as one packet */ + while (queueIt != (*it).second.second.end () && (*queueIt).hdr.GetSequenceNumber () == currentSeq) + { + queueIt++; + } + } + return nPackets; + } + return 0; +} + +uint32_t +BlockAckManager::GetNRetryNeededPackets (Mac48Address recipient, uint8_t tid) const +{ + uint32_t nPackets = 0; + uint16_t currentSeq = 0; + if (ExistsAgreement (recipient, tid)) + { + list::const_iterator it = m_retryPackets.begin (); + while (it != m_retryPackets.end ()) + { + if ((*it)->hdr.GetAddr1 () == recipient && (*it)->hdr.GetQosTid () == tid) + { + currentSeq = (*it)->hdr.GetSequenceNumber (); + nPackets++; + /* a fragmented packet must be counted as one packet */ + while (it != m_retryPackets.end () && (*it)->hdr.GetSequenceNumber () == currentSeq) + { + it++; + } + } + } + } + return nPackets; +} + +void +BlockAckManager::SetBlockAckThreshold (uint8_t nPackets) +{ + m_blockAckThreshold = nPackets; +} + +void +BlockAckManager::NotifyGotBlockAck (const CtrlBAckResponseHeader *blockAck, Mac48Address recipient) +{ + NS_LOG_FUNCTION (this); + uint16_t sequenceFirstLost = 0; + if (!blockAck->IsMultiTid ()) + { + uint8_t tid = blockAck->GetTidInfo (); + if (ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED)) + { + bool foundFirstLost = false; + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + PacketQueueI queueEnd = it->second.second.end (); + + if (it->second.first.m_inactivityEvent.IsRunning ()) + { + /* Upon reception of a block ack frame, the inactivity timer at the + originator must be reset. + For more details see section 11.5.3 in IEEE802.11e standard */ + it->second.first.m_inactivityEvent.Cancel (); + Time timeout = MicroSeconds (1024 * it->second.first.GetTimeout ()); + it->second.first.m_inactivityEvent = Simulator::Schedule (timeout, + &BlockAckManager::InactivityTimeout, + this, + recipient, tid); + } + if (blockAck->IsBasic ()) + { + for (PacketQueueI queueIt = it->second.second.begin (); queueIt != queueEnd;) + { + if (blockAck->IsFragmentReceived ((*queueIt).hdr.GetSequenceNumber (), + (*queueIt).hdr.GetFragmentNumber ())) + { + queueIt = it->second.second.erase (queueIt); + } + else + { + if (!foundFirstLost) + { + foundFirstLost = true; + sequenceFirstLost = (*queueIt).hdr.GetSequenceNumber (); + (*it).second.first.SetStartingSequence (sequenceFirstLost); + } + m_retryPackets.push_back (queueIt); + queueIt++; + } + } + } + else if (blockAck->IsCompressed ()) + { + for (PacketQueueI queueIt = it->second.second.begin (); queueIt != queueEnd;) + { + if (blockAck->IsPacketReceived ((*queueIt).hdr.GetSequenceNumber ())) + { + uint16_t currentSeq = (*queueIt).hdr.GetSequenceNumber (); + while (queueIt != queueEnd && + (*queueIt).hdr.GetSequenceNumber () == currentSeq) + { + queueIt = it->second.second.erase (queueIt); + } + } + else + { + if (!foundFirstLost) + { + foundFirstLost = true; + sequenceFirstLost = (*queueIt).hdr.GetSequenceNumber (); + (*it).second.first.SetStartingSequence (sequenceFirstLost); + } + m_retryPackets.push_back (queueIt); + queueIt++; + } + } + } + uint16_t newSeq = m_txMiddle->GetNextSeqNumberByTidAndAddress (tid, recipient); + if ((foundFirstLost && !SwitchToBlockAckIfNeeded (recipient, tid, sequenceFirstLost)) || + (!foundFirstLost && !SwitchToBlockAckIfNeeded (recipient, tid, newSeq))) + { + it->second.first.SetState (OriginatorBlockAckAgreement::INACTIVE); + } + } + } + else + { + //NOT SUPPORTED FOR NOW + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } +} + +void +BlockAckManager::SetBlockAckType (enum BlockAckType bAckType) +{ + m_blockAckType = bAckType; +} + +Ptr +BlockAckManager::ScheduleBlockAckReqIfNeeded (Mac48Address recipient, uint8_t tid) +{ + /* This method checks if a BlockAckRequest frame should be send to the recipient station. + Number of packets under block ack is specified in OriginatorBlockAckAgreement object but sometimes + this number could be incorrect. In fact is possible that a block ack agreement exists for n + packets but some of these packets are dropped due to MSDU lifetime expiration. + */ + NS_LOG_FUNCTION (this); + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + NS_ASSERT (it != m_agreements.end ()); + + if ((*it).second.first.NeedBlockAckRequest () || + (GetNRetryNeededPackets (recipient, tid) == 0 && + m_queue->GetNPacketsByTidAndAddress (tid, WifiMacHeader::ADDR1, recipient) == 0)) + { + OriginatorBlockAckAgreement &agreement = (*it).second.first; + agreement.CompleteExchange (); + + CtrlBAckRequestHeader reqHdr; + if (m_blockAckType == BASIC_BLOCK_ACK || m_blockAckType == COMPRESSED_BLOCK_ACK) + { + reqHdr.SetType (m_blockAckType); + reqHdr.SetTidInfo (agreement.GetTid ()); + reqHdr.SetStartingSequence (agreement.GetStartingSequence ()); + } + else if (m_blockAckType == MULTI_TID_BLOCK_ACK) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Invalid block ack type."); + } + Ptr bar = Create (); + bar->AddHeader (reqHdr); + return bar; + } + return 0; +} + +void +BlockAckManager::InactivityTimeout (Mac48Address recipient, uint8_t tid) +{ + m_blockAckInactivityTimeout (recipient, tid, true); +} + +void +BlockAckManager::NotifyAgreementEstablished (Mac48Address recipient, uint8_t tid, uint16_t startingSeq) +{ + NS_LOG_FUNCTION (this); + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + NS_ASSERT (it != m_agreements.end ()); + + it->second.first.SetState (OriginatorBlockAckAgreement::ESTABLISHED); + it->second.first.SetStartingSequence (startingSeq); +} + +void +BlockAckManager::NotifyAgreementUnsuccessful (Mac48Address recipient, uint8_t tid) +{ + NS_LOG_FUNCTION (this); + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + NS_ASSERT (it != m_agreements.end ()); + if (it != m_agreements.end ()) + { + it->second.first.SetState (OriginatorBlockAckAgreement::UNSUCCESSFUL); + } +} + +void +BlockAckManager::NotifyMpduTransmission (Mac48Address recipient, uint8_t tid) +{ + NS_LOG_FUNCTION (this); + Ptr bar = 0; + AgreementsI it = m_agreements.find (std::make_pair (recipient, tid)); + NS_ASSERT (it != m_agreements.end ()); + it->second.first.NotifyMpduTransmission (); + bar = ScheduleBlockAckReqIfNeeded (recipient, tid); + if (bar != 0) + { + Bar request (bar, recipient, tid, it->second.first.IsImmediateBlockAck ()); + m_bars.push_back (request); + } +} + +void +BlockAckManager::SetQueue (Ptr queue) +{ + m_queue = queue; +} + +bool +BlockAckManager::SwitchToBlockAckIfNeeded (Mac48Address recipient, uint8_t tid, uint16_t startingSeq) +{ + NS_LOG_FUNCTION (this); + NS_ASSERT (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::PENDING)); + if (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::UNSUCCESSFUL) && ExistsAgreement (recipient, tid)) + { + uint32_t packets = m_queue->GetNPacketsByTidAndAddress (tid, WifiMacHeader::ADDR1, recipient) + + GetNBufferedPackets (recipient, tid); + if (packets >= m_blockAckThreshold) + { + NotifyAgreementEstablished (recipient, tid, startingSeq); + return true; + } + } + return false; +} + +void +BlockAckManager::TearDownBlockAck (Mac48Address recipient, uint8_t tid) +{ + NS_LOG_FUNCTION (this); + DestroyAgreement (recipient, tid); +} + +bool +BlockAckManager::HasOtherFragments (uint16_t sequenceNumber) const +{ + bool retVal = false; + if (m_retryPackets.size () > 0) + { + Item next = *(m_retryPackets.front ()); + if (next.hdr.GetSequenceNumber () == sequenceNumber) + { + retVal = true; + } + } + return retVal; +} + +uint32_t +BlockAckManager::GetNextPacketSize (void) const +{ + uint32_t size = 0; + if (m_retryPackets.size () > 0) + { + Item next = *(m_retryPackets.front ()); + size = next.packet->GetSize (); + } + return size; +} + +void +BlockAckManager::CleanupBuffers (void) +{ + for (AgreementsI j = m_agreements.begin(); j != m_agreements.end (); j++) + { + if (j->second.second.empty ()) + { + continue; + } + Time now = Simulator::Now (); + PacketQueueI end = j->second.second.begin (); + for (PacketQueueI i = j->second.second.begin (); i != j->second.second.end (); i++) + { + if (i->timestamp + m_maxDelay > now) + { + end = i; + break; + } + else + { + /* remove retry packet iterator if it's present in retry queue */ + for (list::iterator it = m_retryPackets.begin (); it != m_retryPackets.end (); it++) + { + if ((*it)->hdr.GetAddr1 () == j->second.first.GetPeer () && + (*it)->hdr.GetQosTid () == j->second.first.GetTid () && + (*it)->hdr.GetSequenceNumber () == i->hdr.GetSequenceNumber ()) + { + m_retryPackets.erase (it); + } + } + } + } + j->second.second.erase (j->second.second.begin (), end); + j->second.first.SetStartingSequence (end->hdr.GetSequenceNumber ()); + } +} + +void +BlockAckManager::SetMaxPacketDelay (Time maxDelay) +{ + NS_LOG_FUNCTION (this); + m_maxDelay = maxDelay; +} + +void +BlockAckManager::SetBlockAckInactivityCallback (Callback callback) +{ + m_blockAckInactivityTimeout = callback; +} + +void +BlockAckManager::SetBlockDestinationCallback (Callback callback) +{ + m_blockPackets = callback; +} + +void +BlockAckManager::SetUnblockDestinationCallback (Callback callback) +{ + m_unblockPackets = callback; +} + +void +BlockAckManager::SetTxMiddle (MacTxMiddle* txMiddle) +{ + m_txMiddle = txMiddle; +} + +} //namespace ns3 diff --git a/src/devices/wifi/block-ack-manager.h b/src/devices/wifi/block-ack-manager.h new file mode 100644 index 000000000..40859ab3c --- /dev/null +++ b/src/devices/wifi/block-ack-manager.h @@ -0,0 +1,297 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#ifndef BLOCK_ACK_MANAGER_H +#define BLOCK_ACK_MANAGER_H + +#include +#include +#include + +#include "ns3/packet.h" + +#include "wifi-mac-header.h" +#include "originator-block-ack-agreement.h" +#include "ctrl-headers.h" +#include "qos-utils.h" + +using namespace std; + +namespace ns3 { + +class MgtAddBaResponseHeader; +class MgtAddBaRequestHeader; +class MgtDelBaHeader; +class MacTxMiddle; +class WifiMacQueue; + +struct Bar { + Bar (); + Bar (Ptr packet, + Mac48Address recipient, + uint8_t tid, + bool immediate); + Ptr bar; + Mac48Address recipient; + uint8_t tid; + bool immediate; +}; + +/** + * \brief Manages all block ack agreements for an originator station. + */ +class BlockAckManager +{ +public: + BlockAckManager (); + ~BlockAckManager (); + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Traffic ID. + * + * Checks if a block ack agreement exists with station addressed by + * recipient for tid tid. + */ + bool ExistsAgreement (Mac48Address recipient, uint8_t tid) const; + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Traffic ID. + * \param state The state for block ack agreement + + * Checks if a block ack agreement with a state equals to state exists with + * station addressed by recipient for tid tid. + */ + bool ExistsAgreementInState (Mac48Address recipient, uint8_t tid, + enum OriginatorBlockAckAgreement::State state) const; + /** + * \param reqHdr Relative Add block ack request (action frame). + * \param recipient Address of peer station involved in block ack mechanism. + * + * Creates a new block ack agreement in pending state. When a ADDBA response + * with a successful status code is received, the relative agreement becomes established. + */ + void CreateAgreement (const MgtAddBaRequestHeader *reqHdr, Mac48Address recipient); + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Tid Traffic id of transmitted packet. + * + * Invoked when a recipient reject a block ack agreement or when a Delba frame + * is Received/Trasmitted. + */ + void DestroyAgreement (Mac48Address recipient, uint8_t tid); + /** + * \param respHdr Relative Add block ack response (action frame). + * \param recipient Address of peer station involved in block ack mechanism. + * + * Invoked upon receipt of a ADDBA response frame from recipient. + */ + void UpdateAgreement (const MgtAddBaResponseHeader *respHdr, Mac48Address recipient); + /** + * \param packet Packet to store. + * \param hdr 802.11 header for packet. + * + * Stores packet for a possible future retransmission. Retransmission occurs + * if the packet, in a block ack frame, is indicated by recipient as not received. + */ + void StorePacket (Ptr packet, const WifiMacHeader &hdr, Time tStamp); + /** + * \param hdr 802.11 header of returned packet (if exists). + * + * This methods returns a packet (if exists) indicated as not received in + * corresponding block ack bitmap. + */ + Ptr GetNextPacket (WifiMacHeader &hdr); + bool HasBar (struct Bar &bar); + /** + * Returns true if there are packets that need of retransmission or at least a + * BAR is sheduled. Returns false othewise. + */ + bool HasPackets (void) const; + /** + * \param blockAck The received block ack frame. + * \param recipient Sender of block ack frame. + * + * Invoked upon receipt of a block ack frame. Typically, this function, is called + * by ns3::EdcaTxopN object. Performs a check on which MPDUs, previously sent + * with ack policy set to Block Ack, were correctly received by the recipient. + * An acknowldeged MPDU is removed from the buffer, retransmitted otherwise. + */ + void NotifyGotBlockAck (const CtrlBAckResponseHeader *blockAck, Mac48Address recipient); + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Traffic ID. + * + * Returns number of packets buffered for a specified agreement. This methods doesn't return + * number of buffered MPDUs but number of buffered MSDUs. + */ + uint32_t GetNBufferedPackets (Mac48Address recipient, uint8_t tid) const; + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Traffic ID. + * + * Returns number of packets for a specific agreeemnt that need retransmission. + * This methods doesn't return number of MPDUs that need retransmission but number MSDUs. + */ + uint32_t GetNRetryNeededPackets (Mac48Address recipient, uint8_t tid) const; + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Traffic ID of transmitted packet. + * + * Puts corresponding agreement in established state and updates number of packets + * and starting sequence field. Invoked typically after a block ack refresh. + */ + void NotifyAgreementEstablished (Mac48Address recipient, uint8_t tid, uint16_t startingSeq); + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Traffic ID of transmitted packet. + * + * Marks an agreement as unsuccessful. This happens if recipient station reject block ack setup + * by an ADDBAResponse frame with a failure status code. FOr now we assume that every QoS station accepts + * a block ack setup. + */ + void NotifyAgreementUnsuccessful (Mac48Address recipient, uint8_t tid); + /** + * \param recipient Address of peer station involved in block ack mechanism. + * \param tid Traffic ID of transmitted packet. + * + * This methods is typically invoked by ns3::EdcaTxopN object every time that a MPDU + * with ack policy subfield in Qos Control field set to Block Ack is transmitted. + */ + void NotifyMpduTransmission (Mac48Address recipient, uint8_t tid); + /** + * \param nPackets Minimum number of packets for use of block ack. + * + * Upon receipt of a block ack frame, if total number of packets (packets in WifiMacQueue + * and buffered packets) is greater of nPackets, they are transmitted using block ack mechanism. + */ + void SetBlockAckThreshold (uint8_t nPackets); + /** + * \param queue The WifiMacQueue object. + */ + void SetQueue (Ptr queue); + void SetTxMiddle (MacTxMiddle* txMiddle); + /** + * \param bAckType Type of block ack + * + * See ctrl-headers.h for more details. + */ + void SetBlockAckType (enum BlockAckType bAckType); + /** + * \param recipient Address of station involved in block ack mechanism. + * \param tid Traffic ID. + * + * This method is invoked by EdcaTxopN object upon receipt of a DELBA frame + * from recipient. The relative block ack agreement is destroied. + */ + void TearDownBlockAck (Mac48Address recipient, uint8_t tid); + /** + * \param Sequence number of the packet which fragment is part of. + * + * Returns true if another fragment with sequence number sequenceNumber is scheduled + * for retransmission. + */ + bool HasOtherFragments (uint16_t sequenceNumber) const; + /** + * Returns size of the next packet that needs retransmission. + */ + uint32_t GetNextPacketSize (void) const; + /** + * \param maxDelay Max delay for a buffered packet. + * + * This method is always called by ns3::WifiMacQueue object and sets max delay equals + * to ns3:WifiMacQueue delay value. + */ + void SetMaxPacketDelay (Time maxDelay); + /** + */ + void SetBlockAckInactivityCallback (Callback callback); + void SetBlockDestinationCallback (Callback callback); + void SetUnblockDestinationCallback (Callback callback); + /** + * Checks if there are in the queue other packets that could be send under block ack. + * If yes adds these packets in current block ack exchange. + * However, number of packets exchanged in the current block ack, will not exceed + * the value of BufferSize in the corresponding OriginatorBlockAckAgreement object. + */ + bool SwitchToBlockAckIfNeeded (Mac48Address recipient, uint8_t tid, uint16_t startingSeq); +private: + /** + * Checks if all packets, for which a block ack agreement was established or refreshed, + * have been transmitted. If yes, adds a pair in m_bAckReqs to indicate that + * at next channel access a block ack request (for established agreement + * recipient,tid) is needed. + */ + Ptr ScheduleBlockAckReqIfNeeded (Mac48Address recipient, uint8_t tid); + /** + * This method removes packets whose lifetime was exceded. + */ + void CleanupBuffers (void); + void InactivityTimeout (Mac48Address, uint8_t); + + struct Item; + typedef std::list PacketQueue; + typedef std::list::iterator PacketQueueI; + typedef std::list::const_iterator PacketQueueCI; + + typedef std::map, + std::pair > Agreements; + typedef std::map, + std::pair >::iterator AgreementsI; + typedef std::map, + std::pair >::const_iterator AgreementsCI; + + struct Item { + Item (); + Item (Ptr packet, + const WifiMacHeader &hdr, + Time tStamp); + Ptr packet; + WifiMacHeader hdr; + Time timestamp; + }; + + /** + * This data structure contains, for each block ack agreement (recipient, tid), a set of packets + * for which an ack by block ack is requested. + * Every packet or fragment indicated as correctly received in block ack frame is + * erased from this data structure. Pushed back in retransmission queue otherwise. + */ + Agreements m_agreements; + /** + * This list contains all iterators to stored packets that need to be retransmitted. + * A packet needs retransmission if it's indicated as not correctly recevied in a block ack + * frame. + */ + std::list m_retryPackets; + std::list m_bars; + + uint8_t m_blockAckThreshold; + enum BlockAckType m_blockAckType; + Time m_maxDelay; + MacTxMiddle* m_txMiddle; + Mac48Address m_address; + Ptr m_queue; + Callback m_blockAckInactivityTimeout; + Callback m_blockPackets; + Callback m_unblockPackets; +}; + +} //namespace ns3 + +#endif /* BLOCK_ACK_MANAGER_H */ diff --git a/src/devices/wifi/block-ack-test-suite.cc b/src/devices/wifi/block-ack-test-suite.cc new file mode 100644 index 000000000..deb504abb --- /dev/null +++ b/src/devices/wifi/block-ack-test-suite.cc @@ -0,0 +1,214 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#include "ns3/test.h" +#include "ns3/log.h" +#include "qos-utils.h" +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("BlockAckTest"); +/** + * This simple test verifies the correctness of buffering for packets received + * under block ack. In order to completely understand this example is important to cite + * section 9.10.3 in IEEE802.11 standard: + * + * "[...] The sequence number space is considered divided into two parts, one of which + * is “old” and one of which is “new” by means of a boundary created by adding half the + * sequence number range to the current start of receive window (modulo 2^12)." + */ +//------------------------------------------------------------------------------------- + +/* ----- = old packets + * +++++ = new packets + * + * CASE A: startSeq < endSeq + * - - + + * initial buffer state: 0 16 56000 + * + * + * 0 4095 + * |------|++++++++++++++++|-----| + * ^ ^ + * | startSeq | endSeq = 4000 + * + * first received packet's sequence control = 64016 (seqNum = 4001, fragNum = 0) - + * second received packet's sequence control = 63984 (seqNum = 3999, fragNum = 0) + + * 4001 is older seq number so this packet should be inserted at the buffer's begin. + * 3999 is previous element of older of new packets: it should be inserted at the end of buffer. + * + * expected buffer state: 64016 0 16 56000 63984 + * + */ +class PacketBufferingCaseA : public TestCase +{ +public: + PacketBufferingCaseA (); + virtual ~PacketBufferingCaseA (); +private: + virtual bool DoRun (void); + std::list m_expectedBuffer; +}; + +PacketBufferingCaseA::PacketBufferingCaseA () + : TestCase ("Check correct order of buffering when startSequence < endSeq") +{ + m_expectedBuffer.push_back (64016); + m_expectedBuffer.push_back (0); + m_expectedBuffer.push_back (16); + m_expectedBuffer.push_back (56000); + m_expectedBuffer.push_back (63984); +} + +PacketBufferingCaseA::~PacketBufferingCaseA () +{} + +bool +PacketBufferingCaseA::DoRun (void) +{ + std::list m_buffer; + std::list::iterator i,j; + m_buffer.push_back (0); + m_buffer.push_back (16); + m_buffer.push_back (56000); + + uint16_t endSeq = 4000; + + uint16_t receivedSeq = 4001 * 16; + uint32_t mappedSeq = QosUtilsMapSeqControlToUniqueInteger (receivedSeq, endSeq); + for (i = m_buffer.begin (); i != m_buffer.end () && QosUtilsMapSeqControlToUniqueInteger ((*i), endSeq) < mappedSeq; i++); + { + m_buffer.insert (i, receivedSeq); + } + + receivedSeq = 3999 * 16; + mappedSeq = QosUtilsMapSeqControlToUniqueInteger (receivedSeq, endSeq); + for (i = m_buffer.begin (); i != m_buffer.end () && QosUtilsMapSeqControlToUniqueInteger ((*i), endSeq) < mappedSeq; i++); + { + m_buffer.insert (i, receivedSeq); + } + + for (i = m_buffer.begin (), j = m_expectedBuffer.begin (); i != m_buffer.end (); i++, j++) + { + NS_TEST_EXPECT_MSG_EQ (*i, *j, "error in buffer order"); + } + return GetErrorStatus (); +} + +/* ----- = old packets + * +++++ = new packets + * + * CASE B: startSeq > endSeq + * - + + + * initial buffer state: 256 64000 16 + * + * + * 0 4095 + * |++++++|----------------|++++++| + * ^ ^ + * | endSeq = 10 | startSeq + * + * first received packet's sequence control = 240 (seqNum = 15, fragNum = 0) - + * second received packet's sequence control = 241 (seqNum = 15, fragNum = 1) - + * third received packet's sequence control = 64800 (seqNum = 4050, fragNum = 0) + + * 240 is an old packet should be inserted at the buffer's begin. + * 241 is an old packet: second segment of the above packet. + * 4050 is a new packet: it should be inserted between 64000 and 16. + * + * expected buffer state: 240 241 256 64000 64800 16 + * + */ +class PacketBufferingCaseB : public TestCase +{ +public: + PacketBufferingCaseB (); + virtual ~PacketBufferingCaseB (); +private: + virtual bool DoRun (void); + std::list m_expectedBuffer; +}; + +PacketBufferingCaseB::PacketBufferingCaseB () + : TestCase ("Check correct order of buffering when startSequence > endSeq") +{ + m_expectedBuffer.push_back (240); + m_expectedBuffer.push_back (241); + m_expectedBuffer.push_back (256); + m_expectedBuffer.push_back (64000); + m_expectedBuffer.push_back (64800); + m_expectedBuffer.push_back (16); +} + +PacketBufferingCaseB::~PacketBufferingCaseB () +{} + +bool +PacketBufferingCaseB::DoRun (void) +{ + std::list m_buffer; + std::list::iterator i,j; + m_buffer.push_back (256); + m_buffer.push_back (64000); + m_buffer.push_back (16); + + uint16_t endSeq = 10; + + uint16_t receivedSeq = 15 * 16; + uint32_t mappedSeq = QosUtilsMapSeqControlToUniqueInteger (receivedSeq, endSeq); + for (i = m_buffer.begin (); i != m_buffer.end () && QosUtilsMapSeqControlToUniqueInteger ((*i), endSeq) < mappedSeq; i++); + { + m_buffer.insert (i, receivedSeq); + } + + receivedSeq = 15 * 16 + 1; + mappedSeq = QosUtilsMapSeqControlToUniqueInteger (receivedSeq, endSeq); + for (i = m_buffer.begin (); i != m_buffer.end () && QosUtilsMapSeqControlToUniqueInteger ((*i), endSeq) < mappedSeq; i++); + { + m_buffer.insert (i, receivedSeq); + } + + receivedSeq = 4050 * 16; + mappedSeq = QosUtilsMapSeqControlToUniqueInteger (receivedSeq, endSeq); + for (i = m_buffer.begin (); i != m_buffer.end () && QosUtilsMapSeqControlToUniqueInteger ((*i), endSeq) < mappedSeq; i++); + { + m_buffer.insert (i, receivedSeq); + } + + for (i = m_buffer.begin (), j = m_expectedBuffer.begin (); i != m_buffer.end (); i++, j++) + { + NS_TEST_EXPECT_MSG_EQ (*i, *j, "error in buffer order"); + } + return GetErrorStatus (); +} + +class BlockAckTestSuite : public TestSuite +{ +public: + BlockAckTestSuite (); +}; + +BlockAckTestSuite::BlockAckTestSuite () + : TestSuite ("wifi-block-ack", UNIT) +{ + AddTestCase (new PacketBufferingCaseA); + AddTestCase (new PacketBufferingCaseB); +} + +BlockAckTestSuite g_blockAckTestSuite; diff --git a/src/devices/wifi/ctrl-headers.cc b/src/devices/wifi/ctrl-headers.cc new file mode 100644 index 000000000..5e17893db --- /dev/null +++ b/src/devices/wifi/ctrl-headers.cc @@ -0,0 +1,681 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#include "ns3/fatal-error.h" + +#include "ctrl-headers.h" + +namespace ns3 { + +/*********************************** + * Block ack request + ***********************************/ + +NS_OBJECT_ENSURE_REGISTERED (CtrlBAckRequestHeader); + +CtrlBAckRequestHeader::CtrlBAckRequestHeader () + : m_barAckPolicy (false), + m_multiTid (false), + m_compressed (false) +{} + +CtrlBAckRequestHeader::~CtrlBAckRequestHeader () +{} + +TypeId +CtrlBAckRequestHeader::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::CtrlBAckRequestHeader") + .SetParent
() + .AddConstructor () + ; + return tid; +} + +TypeId +CtrlBAckRequestHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +void +CtrlBAckRequestHeader::Print (std::ostream &os) const +{ + os << "TID_INFO=" << m_tidInfo << ", StartingSeq=" << std::hex << m_startingSeq; +} + +uint32_t +CtrlBAckRequestHeader::GetSerializedSize () const +{ + uint32_t size = 0; + size += 2; //Bar control + if (!m_multiTid) + { + size += 2; //Starting sequence control + } + else + { + if (m_compressed) + { + size += (2 + 2) * (m_tidInfo + 1); //Multi-tid block ack + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return size; +} + +void +CtrlBAckRequestHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteHtolsbU16 (GetBarControl ()); + if (!m_multiTid) + { + i.WriteHtolsbU16 (GetStartingSequenceControl ()); + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } +} + +uint32_t +CtrlBAckRequestHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + SetBarControl (i.ReadLsbtohU16 ()); + if (!m_multiTid) + { + SetStartingSequenceControl (i.ReadLsbtohU16 ()); + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return i.GetDistanceFrom (start); +} + +uint16_t +CtrlBAckRequestHeader::GetBarControl (void) const +{ + uint16_t res = 0; + if (m_barAckPolicy) + { + res |= 0x1; + } + if (m_multiTid) + { + res |= (0x1<<1); + } + if (m_compressed) + { + res |= (0x1<<2); + } + res |= (m_tidInfo << 12) & (0xf<<12); + return res; +} + +void +CtrlBAckRequestHeader::SetBarControl (uint16_t bar) +{ + m_barAckPolicy = ((bar & 0x01) == 1)?true:false; + m_multiTid = (((bar >> 1) & 0x01) == 1)?true:false; + m_compressed = (((bar >> 2) & 0x01) == 1)?true:false; + m_tidInfo = (bar >> 12) & 0x0f; +} + +uint16_t +CtrlBAckRequestHeader::GetStartingSequenceControl (void) const +{ + return (m_startingSeq << 4) & 0xfff0; +} + +void +CtrlBAckRequestHeader::SetStartingSequenceControl (uint16_t seqControl) +{ + m_startingSeq = (seqControl >> 4) & 0x0fff; +} + +void +CtrlBAckRequestHeader::SetHtImmediateAck (bool immediateAck) +{ + m_barAckPolicy = immediateAck; +} + +void +CtrlBAckRequestHeader::SetType (enum BlockAckType type) +{ + switch (type) { + case BASIC_BLOCK_ACK: + m_multiTid = false; + m_compressed = false; + break; + case COMPRESSED_BLOCK_ACK: + m_multiTid = false; + m_compressed = true; + break; + case MULTI_TID_BLOCK_ACK: + m_multiTid = true; + m_compressed = true; + break; + default: + NS_FATAL_ERROR ("Invalid variant type"); + break; + } +} + +void +CtrlBAckRequestHeader::SetTidInfo (uint8_t tid) +{ + m_tidInfo = static_cast (tid); +} + +void +CtrlBAckRequestHeader::SetStartingSequence (uint16_t seq) +{ + m_startingSeq = seq; +} + +bool +CtrlBAckRequestHeader::MustSendHtImmediateAck (void) const +{ + return m_barAckPolicy; +} + +uint8_t +CtrlBAckRequestHeader::GetTidInfo (void) const +{ + uint8_t tid = static_cast (m_tidInfo); + return tid; +} + +uint16_t +CtrlBAckRequestHeader::GetStartingSequence (void) const +{ + return m_startingSeq; +} + +bool +CtrlBAckRequestHeader::IsBasic (void) const +{ + return (!m_multiTid && !m_compressed)?true:false; +} + +bool +CtrlBAckRequestHeader::IsCompressed (void) const +{ + return (!m_multiTid && m_compressed)?true:false; +} + +bool +CtrlBAckRequestHeader::IsMultiTid (void) const +{ + return (m_multiTid && m_compressed)?true:false; +} + +/*********************************** + * Block ack response + ***********************************/ + +NS_OBJECT_ENSURE_REGISTERED (CtrlBAckResponseHeader); + +CtrlBAckResponseHeader::CtrlBAckResponseHeader () + : m_baAckPolicy (false), + m_multiTid (false), + m_compressed (false) +{ + memset (&bitmap, 0, sizeof (bitmap)); +} + +CtrlBAckResponseHeader::~CtrlBAckResponseHeader () +{} + +TypeId +CtrlBAckResponseHeader::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::CtrlBAckResponseHeader") + .SetParent
() + .AddConstructor () + ; + return tid; +} + +TypeId +CtrlBAckResponseHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +void +CtrlBAckResponseHeader::Print (std::ostream &os) const +{ + os << "TID_INFO=" << m_tidInfo << ", StartingSeq=" << std::hex << m_startingSeq; +} + +uint32_t +CtrlBAckResponseHeader::GetSerializedSize (void) const +{ + uint32_t size = 0; + size += 2; //Bar control + if (!m_multiTid) + { + if (!m_compressed) + { + size += (2 + 128); //Basic block ack + } + else + { + size += (2 + 8); //Compressed block ack + } + } + else + { + if (m_compressed) + { + size += (2 + 2 + 8) * (m_tidInfo + 1); //Multi-tid block ack + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return size; +} + +void +CtrlBAckResponseHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteHtolsbU16 (GetBaControl ()); + if (!m_multiTid) + { + i.WriteHtolsbU16 (GetStartingSequenceControl ()); + i = SerializeBitmap (i); + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } +} + +uint32_t +CtrlBAckResponseHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + SetBaControl (i.ReadLsbtohU16 ()); + if (!m_multiTid) + { + SetStartingSequenceControl (i.ReadLsbtohU16 ()); + i = DeserializeBitmap (i); + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return i.GetDistanceFrom (start); +} + +void +CtrlBAckResponseHeader::SetHtImmediateAck (bool immediateAck) +{ + m_baAckPolicy = immediateAck; +} + +void +CtrlBAckResponseHeader::SetType (enum BlockAckType type) +{ + switch (type) { + case BASIC_BLOCK_ACK: + m_multiTid = false; + m_compressed = false; + break; + case COMPRESSED_BLOCK_ACK: + m_multiTid = false; + m_compressed = true; + break; + case MULTI_TID_BLOCK_ACK: + m_multiTid = true; + m_compressed = true; + break; + default: + NS_FATAL_ERROR ("Invalid variant type"); + break; + } +} + +void +CtrlBAckResponseHeader::SetTidInfo (uint8_t tid) +{ + m_tidInfo = static_cast (tid); +} + +void +CtrlBAckResponseHeader::SetStartingSequence (uint16_t seq) +{ + m_startingSeq = seq; +} + +bool +CtrlBAckResponseHeader::MustSendHtImmediateAck (void) const +{ + return (m_baAckPolicy)?true:false; +} + +uint8_t +CtrlBAckResponseHeader::GetTidInfo (void) const +{ + uint8_t tid = static_cast (m_tidInfo); + return tid; +} + +uint16_t +CtrlBAckResponseHeader::GetStartingSequence (void) const +{ + return m_startingSeq; +} + +bool +CtrlBAckResponseHeader::IsBasic (void) const +{ + return (!m_multiTid && !m_compressed)?true:false; +} + +bool +CtrlBAckResponseHeader::IsCompressed (void) const +{ + return (!m_multiTid && m_compressed)?true:false; +} + +bool +CtrlBAckResponseHeader::IsMultiTid (void) const +{ + return (m_multiTid && m_compressed)?true:false; +} + +uint16_t +CtrlBAckResponseHeader::GetBaControl (void) const +{ + uint16_t res = 0; + if (m_baAckPolicy) + { + res |= 0x1; + } + if (m_multiTid) + { + res |= (0x1<<1); + } + if (m_compressed) + { + res |= (0x1<<2); + } + res |= (m_tidInfo << 12) & (0xf<<12); + return res; +} + +void +CtrlBAckResponseHeader::SetBaControl (uint16_t ba) +{ + m_baAckPolicy = ((ba & 0x01) == 1)?true:false; + m_multiTid = (((ba >> 1) & 0x01) == 1)?true:false; + m_compressed = (((ba >> 2) & 0x01) == 1)?true:false; + m_tidInfo = (ba >> 12) & 0x0f; +} + +uint16_t +CtrlBAckResponseHeader::GetStartingSequenceControl (void) const +{ + return (m_startingSeq << 4) & 0xfff0; +} + +void +CtrlBAckResponseHeader::SetStartingSequenceControl (uint16_t seqControl) +{ + m_startingSeq = (seqControl >> 4) & 0x0fff; +} + +Buffer::Iterator +CtrlBAckResponseHeader::SerializeBitmap (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + if (!m_multiTid) + { + if (!m_compressed) + { + for (uint32_t j = 0; j < 64; j++) + { + i.WriteHtolsbU16 (bitmap.m_bitmap[j]); + } + } + else + { + i.WriteHtolsbU64 (bitmap.m_compressedBitmap); + } + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return i; +} + +Buffer::Iterator +CtrlBAckResponseHeader::DeserializeBitmap (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + if (!m_multiTid) + { + if (!m_compressed) + { + for (uint32_t j = 0; j < 64; j++) + { + bitmap.m_bitmap[j] = i.ReadLsbtohU16 (); + } + } + else + { + bitmap.m_compressedBitmap = i.ReadLsbtohU64 (); + } + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return i; +} + +void +CtrlBAckResponseHeader::SetReceivedPacket (uint16_t seq) +{ + if (!m_multiTid) + { + if (!m_compressed) + { + /* To set correctly basic block ack bitmap we need fragment number too. + So if it's not specified, we consider packet not fragmented. */ + bitmap.m_bitmap[IndexInBitmap (seq)] |= 0x0001; + } + else + { + bitmap.m_compressedBitmap |= (0x0000000000000001 << IndexInBitmap (seq)); + } + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } +} + +void +CtrlBAckResponseHeader::SetReceivedFragment (uint16_t seq, uint8_t frag) +{ + NS_ASSERT (frag < 16); + if (!m_multiTid) + { + if (!m_compressed) + { + bitmap.m_bitmap[IndexInBitmap (seq)] |= (0x0001<> IndexInBitmap (seq)) & mask) == 1)?true:false; + } + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return false; +} + +bool +CtrlBAckResponseHeader::IsFragmentReceived (uint16_t seq, uint8_t frag) const +{ + NS_ASSERT (frag < 16); + if (!m_multiTid) + { + if (!m_compressed) + { + return ((bitmap.m_bitmap[IndexInBitmap (seq)] & (0x0001<seq was correctly received, also all of its fragments + were correctly received. */ + uint64_t mask = 0x0000000000000001; + return (((bitmap.m_compressedBitmap >> IndexInBitmap (seq)) & mask) == 1)?true:false; + } + } + else + { + if (m_compressed) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + else + { + NS_FATAL_ERROR ("Reserved configuration."); + } + } + return false; +} + +uint8_t +CtrlBAckResponseHeader::IndexInBitmap (uint16_t seq) const +{ + if (seq >= m_startingSeq) + { + return (seq - m_startingSeq); + } + else + { + return (4096 - m_startingSeq + seq); + } +} + +} //namespace ns3 diff --git a/src/devices/wifi/ctrl-headers.h b/src/devices/wifi/ctrl-headers.h new file mode 100644 index 000000000..1510554a4 --- /dev/null +++ b/src/devices/wifi/ctrl-headers.h @@ -0,0 +1,159 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#ifndef CTRL_HEADERS_H +#define CTRL_HEADERS_H + +#include "ns3/header.h" + +namespace ns3 { + +/** Headers for Block ack request and response. + * 802.11n standard includes three types of block ack: + * - Basic block ack (unique type in 802.11e) + * - Compressed block ack + * - Multi-TID block ack + * For now only basic block ack and compressed block ack + * are supported. + * Basic block ack is also default variant. + */ +enum BlockAckType +{ + BASIC_BLOCK_ACK, + COMPRESSED_BLOCK_ACK, + MULTI_TID_BLOCK_ACK +}; + +class CtrlBAckRequestHeader : public Header { +public: + CtrlBAckRequestHeader (); + ~CtrlBAckRequestHeader (); + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + + void SetHtImmediateAck (bool immediateAck); + void SetType (enum BlockAckType type); + void SetTidInfo (uint8_t tid); + void SetStartingSequence (uint16_t seq); + + bool MustSendHtImmediateAck (void) const; + uint8_t GetTidInfo (void) const; + uint16_t GetStartingSequence (void) const; + bool IsBasic (void) const; + bool IsCompressed (void) const; + bool IsMultiTid (void) const; + + uint16_t GetStartingSequenceControl (void) const; + +private: + + void SetStartingSequenceControl (uint16_t seqControl); + uint16_t GetBarControl (void) const; + void SetBarControl (uint16_t bar); + + /** + * The lsb bit of the BAR control field is used only for the + * HT (High Throughput) delayed block ack configuration. + * For now only non HT immediate block ack is implemented so this field + * is here only for a future implementation of HT delayed variant. + */ + bool m_barAckPolicy; + bool m_multiTid; + bool m_compressed; + uint16_t m_tidInfo; + uint16_t m_startingSeq; +}; + +class CtrlBAckResponseHeader : public Header { +public: + CtrlBAckResponseHeader (); + ~CtrlBAckResponseHeader (); + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + + void SetHtImmediateAck (bool immeadiateAck); + void SetType (enum BlockAckType type); + void SetTidInfo (uint8_t tid); + void SetStartingSequence (uint16_t seq); + + bool MustSendHtImmediateAck (void) const; + uint8_t GetTidInfo (void) const; + uint16_t GetStartingSequence (void) const; + bool IsBasic (void) const; + bool IsCompressed (void) const; + bool IsMultiTid (void) const; + + void SetReceivedPacket (uint16_t seq); + void SetReceivedFragment (uint16_t seq, uint8_t frag); + bool IsPacketReceived (uint16_t seq) const; + bool IsFragmentReceived (uint16_t seq, uint8_t frag) const; + + uint16_t GetStartingSequenceControl (void) const; + void SetStartingSequenceControl (uint16_t seqControl); + +private: + + uint16_t GetBaControl (void) const; + void SetBaControl (uint16_t bar); + + Buffer::Iterator SerializeBitmap (Buffer::Iterator start) const; + Buffer::Iterator DeserializeBitmap (Buffer::Iterator start); + + /** + * This function is used to correctly index in both bitmap + * and compressed bitmap, one bit or one block of 16 bits respectly. + * If we are using basic block ack, return value represents index of + * block of 16 bits for packet having sequence number equals to seq. + * If we are using compressed block ack, return value represents bit + * to set to 1 in the compressed bitmap to indicate that packet having + * sequence number equals to seq was correctly received. + * + * for more details see 7.2.1.8 in IEEE 802.11n/D4.00 + */ + uint8_t IndexInBitmap (uint16_t seq) const; + + /** + * The lsb bit of the BA control field is used only for the + * HT (High Throughput) delayed block ack configuration. + * For now only non HT immediate block ack is implemented so this field + * is here only for a future implementation of HT delayed variant. + */ + bool m_baAckPolicy; + bool m_multiTid; + bool m_compressed; + uint16_t m_tidInfo; + uint16_t m_startingSeq; + + union { + uint16_t m_bitmap[64]; + uint64_t m_compressedBitmap; + } bitmap; +}; + +} //namespace ns3 + +#endif /* CTRL_HEADERS_H */ diff --git a/src/devices/wifi/edca-txop-n.cc b/src/devices/wifi/edca-txop-n.cc index 68b9f8db0..9473da398 100644 --- a/src/devices/wifi/edca-txop-n.cc +++ b/src/devices/wifi/edca-txop-n.cc @@ -31,6 +31,9 @@ #include "random-stream.h" #include "wifi-mac-queue.h" #include "msdu-aggregator.h" +#include "mgt-headers.h" +#include "qos-blocked-destinations.h" +#include "block-ack-manager.h" NS_LOG_COMPONENT_DEFINE ("EdcaTxopN"); @@ -82,6 +85,12 @@ public: virtual void MissedAck (void) { m_txop->MissedAck (); } + virtual void GotBlockAck (const CtrlBAckResponseHeader *blockAck, Mac48Address source) { + m_txop->GotBlockAck (blockAck, source); + } + virtual void MissedBlockAck (void) { + m_txop->MissedBlockAck (); + } virtual void StartNext (void) { m_txop->StartNext (); } @@ -93,6 +102,22 @@ private: EdcaTxopN *m_txop; }; +class EdcaTxopN::BlockAckEventListener : public MacLowBlockAckEventListener +{ +public: + BlockAckEventListener (EdcaTxopN *txop) + : MacLowBlockAckEventListener (), + m_txop (txop) {} + virtual ~BlockAckEventListener () {} + + virtual void BlockAckInactivityTimeout (Mac48Address address, uint8_t tid) { + m_txop->SendDelbaFrame (address, tid, false); + } + +private: + EdcaTxopN *m_txop; +}; + NS_OBJECT_ENSURE_REGISTERED (EdcaTxopN); TypeId @@ -101,6 +126,19 @@ EdcaTxopN::GetTypeId (void) static TypeId tid = TypeId ("ns3::EdcaTxopN") .SetParent () .AddConstructor () + .AddAttribute ("BlockAckThreshold", "If number of packets in this queue reaches this value,\ + block ack mechanism is used. If this value is 0, block ack is never used.", + UintegerValue(0), + MakeUintegerAccessor (&EdcaTxopN::SetBlockAckThreshold, + &EdcaTxopN::GetBlockAckThreshold), + MakeUintegerChecker (0, 64)) + .AddAttribute ("BlockAckInactivityTimeout", "Represents max time (blocks of 1024 micro seconds) allowed for block ack\ + inactivity. If this value isn't equal to 0 a timer start after that a\ + block ack setup is completed and will be reset every time that a block\ + ack frame is received. If this value is 0, block ack inactivity timeout won't be used.", + UintegerValue(0), + MakeUintegerAccessor (&EdcaTxopN::m_blockAckInactivityTimeout), + MakeUintegerChecker ()) ; return tid; } @@ -108,13 +146,22 @@ EdcaTxopN::GetTypeId (void) EdcaTxopN::EdcaTxopN () : m_manager (0), m_currentPacket(0), - m_aggregator (0) + m_aggregator (0), + m_blockAckType (COMPRESSED_BLOCK_ACK) { NS_LOG_FUNCTION (this); m_transmissionListener = new EdcaTxopN::TransmissionListener (this); + m_blockAckListener = new EdcaTxopN::BlockAckEventListener (this); m_dcf = new EdcaTxopN::Dcf (this); m_queue = CreateObject (); m_rng = new RealRandomStream (); + m_qosBlockedDestinations = new QosBlockedDestinations (); + m_baManager = new BlockAckManager (); + m_baManager->SetQueue (m_queue); + m_baManager->SetBlockAckType (m_blockAckType); + m_baManager->SetBlockDestinationCallback (MakeCallback (&QosBlockedDestinations::Block, m_qosBlockedDestinations)); + m_baManager->SetUnblockDestinationCallback (MakeCallback (&QosBlockedDestinations::Unblock, m_qosBlockedDestinations)); + m_baManager->SetMaxPacketDelay (m_queue->GetMaxDelay ()); } EdcaTxopN::~EdcaTxopN () @@ -132,9 +179,15 @@ EdcaTxopN::DoDispose (void) delete m_transmissionListener; delete m_dcf; delete m_rng; + delete m_qosBlockedDestinations; + delete m_baManager; + delete m_blockAckListener; m_transmissionListener = 0; m_dcf = 0; m_rng = 0; + m_qosBlockedDestinations = 0; + m_baManager = 0; + m_blockAckListener = 0; m_txMiddle = 0; m_aggregator = 0; } @@ -253,7 +306,7 @@ EdcaTxopN::SetLow(Ptr low) bool EdcaTxopN::NeedsAccess (void) const { - return !m_queue->IsEmpty () || m_currentPacket != 0; + return !m_queue->IsEmpty () || m_currentPacket != 0 || m_baManager->HasPackets (); } void @@ -262,23 +315,50 @@ EdcaTxopN::NotifyAccessGranted (void) NS_LOG_FUNCTION (this); if (m_currentPacket == 0) { - if (m_queue->IsEmpty ()) + if (m_queue->IsEmpty () && !m_baManager->HasPackets ()) { MY_DEBUG ("queue is empty"); return; } - m_currentPacket = m_queue->Dequeue (&m_currentHdr); - NS_ASSERT (m_currentPacket != 0); - - uint16_t sequence = m_txMiddle->GetNextSequenceNumberfor (&m_currentHdr); - m_currentHdr.SetSequenceNumber (sequence); - m_currentHdr.SetFragmentNumber (0); - m_currentHdr.SetNoMoreFragments (); - m_currentHdr.SetNoRetry (); - m_fragmentNumber = 0; - MY_DEBUG ("dequeued size="<GetSize ()<< - ", to="< 0 && + !m_baManager->ExistsAgreement (m_currentHdr.GetAddr1 (), m_currentHdr.GetQosTid ()) && + SetupBlockAckIfNeeded ()) + { + return; + } + m_currentPacket = m_queue->DequeueFirstAvailable (&m_currentHdr, m_currentPacketTimestamp, m_qosBlockedDestinations); + NS_ASSERT (m_currentPacket != 0); + + uint16_t sequence = m_txMiddle->GetNextSequenceNumberfor (&m_currentHdr); + m_currentHdr.SetSequenceNumber (sequence); + m_currentHdr.SetFragmentNumber (0); + m_currentHdr.SetNoMoreFragments (); + m_currentHdr.SetNoRetry (); + m_fragmentNumber = 0; + MY_DEBUG ("dequeued size="<GetSize ()<< + ", to="<UpdateFailedCw (); + + m_dcf->StartBackoffNow (m_rng->GetNext (0, m_dcf->GetCw ())); + RestartAccessIfNeeded (); +} + Ptr EdcaTxopN::GetMsduAggregator (void) const { @@ -513,7 +639,7 @@ EdcaTxopN::RestartAccessIfNeeded (void) { NS_LOG_FUNCTION (this); if ((m_currentPacket != 0 || - !m_queue->IsEmpty ()) && + !m_queue->IsEmpty () || m_baManager->HasPackets ()) && !m_dcf->IsAccessRequested ()) { m_manager->RequestAccess (m_dcf); @@ -525,7 +651,7 @@ EdcaTxopN::StartAccessIfNeeded (void) { NS_LOG_FUNCTION (this); if (m_currentPacket == 0 && - !m_queue->IsEmpty () && + (!m_queue->IsEmpty () || m_baManager->HasPackets ()) && !m_dcf->IsAccessRequested ()) { m_manager->RequestAccess (m_dcf); @@ -651,6 +777,12 @@ EdcaTxopN::GetFragmentPacket (WifiMacHeader *hdr) return fragment; } +void +EdcaTxopN::SetAccessClass (enum AccessClass ac) +{ + m_ac = ac; +} + Mac48Address EdcaTxopN::MapSrcAddressForAggregation (const WifiMacHeader &hdr) { @@ -687,4 +819,273 @@ EdcaTxopN::SetMsduAggregator (Ptr aggr) m_aggregator = aggr; } +void +EdcaTxopN::PushFront (Ptr packet, const WifiMacHeader &hdr) +{ + NS_LOG_FUNCTION (this << packet << &hdr); + WifiMacTrailer fcs; + uint32_t fullPacketSize = hdr.GetSerializedSize () + packet->GetSize () + fcs.GetSerializedSize (); + WifiRemoteStation *station = GetStation (hdr.GetAddr1 ()); + station->PrepareForQueue (packet, fullPacketSize); + m_queue->PushFront (packet, hdr); + StartAccessIfNeeded (); +} + +void +EdcaTxopN::GotAddBaResponse (const MgtAddBaResponseHeader *respHdr, Mac48Address recipient) +{ + NS_LOG_FUNCTION (this); + MY_DEBUG ("received ADDBA response from "<GetTid (); + if (m_baManager->ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::PENDING)) + { + if (respHdr->GetStatusCode ().IsSuccess ()) + { + MY_DEBUG ("block ack agreement established with "<UpdateAgreement (respHdr, recipient); + } + else + { + MY_DEBUG ("discard ADDBA response"<NotifyAgreementUnsuccessful (recipient, tid); + } + } + RestartAccessIfNeeded (); +} + +void +EdcaTxopN::GotDelBaFrame (const MgtDelBaHeader *delBaHdr, Mac48Address recipient) +{ + NS_LOG_FUNCTION (this); + MY_DEBUG ("received DELBA frame from="<TearDownBlockAck (recipient, delBaHdr->GetTid ()); +} + +void +EdcaTxopN::GotBlockAck (const CtrlBAckResponseHeader *blockAck, Mac48Address recipient) +{ + MY_DEBUG ("got block ack from="<NotifyGotBlockAck (blockAck, recipient); + m_currentPacket = 0; + m_dcf->ResetCw (); + m_dcf->StartBackoffNow (m_rng->GetNext (0, m_dcf->GetCw ())); + RestartAccessIfNeeded (); +} + +void +EdcaTxopN::VerifyBlockAck (void) +{ + NS_LOG_FUNCTION (this); + uint8_t tid = m_currentHdr.GetQosTid (); + Mac48Address recipient = m_currentHdr.GetAddr1 (); + uint16_t sequence = m_currentHdr.GetSequenceNumber (); + if (m_baManager->ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::INACTIVE)) + { + m_baManager->SwitchToBlockAckIfNeeded (recipient, tid, sequence); + } + if (m_baManager->ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED)) + { + m_currentHdr.SetQosAckPolicy (WifiMacHeader::BLOCK_ACK); + } +} + +void +EdcaTxopN::CompleteTx (void) +{ + if (m_currentHdr.IsQosData () && m_currentHdr.IsQosBlockAck ()) + { + if (!m_currentHdr.IsRetry ()) + { + m_baManager->StorePacket (m_currentPacket, m_currentHdr, m_currentPacketTimestamp); + } + m_baManager->NotifyMpduTransmission (m_currentHdr.GetAddr1 (), m_currentHdr.GetQosTid ()); + //we are not waiting for an ack: transmission is completed + m_currentPacket = 0; + m_dcf->ResetCw (); + m_dcf->StartBackoffNow (m_rng->GetNext (0, m_dcf->GetCw ())); + StartAccessIfNeeded (); + } +} + +bool +EdcaTxopN::SetupBlockAckIfNeeded () +{ + uint8_t tid = m_currentHdr.GetQosTid (); + Mac48Address recipient = m_currentHdr.GetAddr1 (); + + uint32_t packets = m_queue->GetNPacketsByTidAndAddress (tid, WifiMacHeader::ADDR1, recipient); + + if (packets >= m_blockAckThreshold) + { + /* Block ack setup */ + uint16_t startingSequence = m_txMiddle->GetNextSeqNumberByTidAndAddress (tid, recipient); + SendAddBaRequest (recipient, tid, startingSequence, m_blockAckInactivityTimeout, true); + return true; + } + return false; +} + +void +EdcaTxopN::SendBlockAckRequest (const struct Bar &bar) +{ + NS_LOG_FUNCTION (this); + WifiMacHeader hdr; + hdr.SetType (WIFI_MAC_CTL_BACKREQ); + hdr.SetAddr1 (bar.recipient); + hdr.SetAddr2 (m_low->GetAddress ()); + hdr.SetAddr3 (m_low->GetBssid ()); + hdr.SetDsNotTo (); + hdr.SetDsNotFrom (); + hdr.SetNoRetry (); + hdr.SetNoMoreFragments (); + + m_currentPacket = bar.bar; + m_currentHdr = hdr; + + MacLowTransmissionParameters params; + params.DisableRts (); + params.DisableNextData (); + params.DisableOverrideDurationId (); + if (bar.immediate) + { + if (m_blockAckType == BASIC_BLOCK_ACK) + { + params.EnableBasicBlockAck (); + } + else if (m_blockAckType == COMPRESSED_BLOCK_ACK) + { + params.EnableCompressedBlockAck (); + } + else if (m_blockAckType == MULTI_TID_BLOCK_ACK) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported"); + } + } + else + { + //Delayed block ack + params.EnableAck (); + } + m_low->StartTransmission (m_currentPacket, &m_currentHdr, params, m_transmissionListener); +} + +void +EdcaTxopN::CompleteConfig (void) +{ + NS_LOG_FUNCTION (this); + m_baManager->SetTxMiddle (m_txMiddle); + m_low->RegisterBlockAckListenerForAc (m_ac, m_blockAckListener); + m_baManager->SetBlockAckInactivityCallback (MakeCallback (&EdcaTxopN::SendDelbaFrame, this)); +} + +void +EdcaTxopN::SetBlockAckThreshold (uint8_t threshold) +{ + m_blockAckThreshold = threshold; + m_baManager->SetBlockAckThreshold (threshold); +} + +uint8_t +EdcaTxopN::GetBlockAckThreshold (void) const +{ + return m_blockAckThreshold; +} + +void +EdcaTxopN::SendAddBaRequest (Mac48Address dest, uint8_t tid, uint16_t startSeq, + uint16_t timeout, bool immediateBAck) +{ + NS_LOG_FUNCTION (this); + MY_DEBUG ("sent ADDBA request to "<GetAddress ()); + hdr.SetAddr3 (m_low->GetAddress ()); + hdr.SetDsNotTo (); + hdr.SetDsNotFrom (); + + WifiActionHeader actionHdr; + WifiActionHeader::ActionValue action; + action.blockAck = WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST; + actionHdr.SetAction (WifiActionHeader::BLOCK_ACK, action); + + Ptr packet = Create (); + /*Setting ADDBARequest header*/ + MgtAddBaRequestHeader reqHdr; + reqHdr.SetAmsduSupport (true); + if (immediateBAck) + { + reqHdr.SetImmediateBlockAck (); + } + else + { + reqHdr.SetDelayedBlockAck (); + } + reqHdr.SetTid (tid); + /* For now we don't use buffer size field in the ADDBA request frame. The recipient + * will choose how many packets it can receive under block ack. + */ + reqHdr.SetBufferSize (0); + reqHdr.SetTimeout (timeout); + reqHdr.SetStartingSequence (startSeq); + + m_baManager->CreateAgreement (&reqHdr, dest); + + packet->AddHeader (reqHdr); + packet->AddHeader (actionHdr); + + m_currentPacket = packet; + m_currentHdr = hdr; + + uint16_t sequence = m_txMiddle->GetNextSequenceNumberfor (&m_currentHdr); + m_currentHdr.SetSequenceNumber (sequence); + m_currentHdr.SetFragmentNumber (0); + m_currentHdr.SetNoMoreFragments (); + m_currentHdr.SetNoRetry (); + + MacLowTransmissionParameters params; + params.EnableAck (); + params.DisableRts (); + params.DisableNextData (); + params.DisableOverrideDurationId (); + + m_low->StartTransmission (m_currentPacket, &m_currentHdr, params, + m_transmissionListener); +} + +void +EdcaTxopN::SendDelbaFrame (Mac48Address addr, uint8_t tid, bool byOriginator) +{ + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetAddr1 (addr); + hdr.SetAddr2 (m_low->GetAddress ()); + hdr.SetAddr3 (m_low->GetAddress ()); + hdr.SetDsNotTo (); + hdr.SetDsNotFrom (); + + MgtDelBaHeader delbaHdr; + delbaHdr.SetTid (tid); + if (byOriginator) + { + delbaHdr.SetByOriginator (); + } + else + { + delbaHdr.SetByRecipient (); + } + + WifiActionHeader actionHdr; + WifiActionHeader::ActionValue action; + action.blockAck = WifiActionHeader::BLOCK_ACK_DELBA; + actionHdr.SetAction (WifiActionHeader::BLOCK_ACK, action); + + Ptr packet = Create (); + packet->AddHeader (delbaHdr); + packet->AddHeader (actionHdr); + + PushFront (packet, hdr); +} + } //namespace ns3 diff --git a/src/devices/wifi/edca-txop-n.h b/src/devices/wifi/edca-txop-n.h index 298eb2700..fc395e40a 100644 --- a/src/devices/wifi/edca-txop-n.h +++ b/src/devices/wifi/edca-txop-n.h @@ -31,6 +31,7 @@ #include "wifi-mac-header.h" #include "qos-utils.h" #include "dcf.h" +#include "ctrl-headers.h" #include #include @@ -44,7 +45,12 @@ class MacTxMiddle; class WifiMacParameters; class WifiMacQueue; class RandomStream; +class QosBlockedDestinations; class MsduAggregator; +class MgtAddBaResponseHeader; +class BlockAckManager; +class MgtDelBaHeader; + /* This queue contains packets for a particular access class. * possibles access classes are: @@ -110,6 +116,10 @@ public: void GotCts (double snr, WifiMode txMode); void MissedCts (void); void GotAck (double snr, WifiMode txMode); + void GotBlockAck (const CtrlBAckResponseHeader *blockAck, Mac48Address recipient); + void MissedBlockAck (void); + void GotAddBaResponse (const MgtAddBaResponseHeader *respHdr, Mac48Address recipient); + void GotDelBaFrame (const MgtDelBaHeader *delBaHdr, Mac48Address recipient); void MissedAck (void); void StartNext (void); void Cancel (void); @@ -128,8 +138,14 @@ public: void NextFragment (void); Ptr GetFragmentPacket (WifiMacHeader *hdr); + void SetAccessClass (enum AccessClass ac); void Queue (Ptr packet, const WifiMacHeader &hdr); void SetMsduAggregator (Ptr aggr); + void PushFront (Ptr packet, const WifiMacHeader &hdr); + void CompleteConfig (void); + void SetBlockAckThreshold (uint8_t threshold); + uint8_t GetBlockAckThreshold (void) const; + void SendDelbaFrame (Mac48Address addr, uint8_t tid, bool byOriginator); private: /** @@ -145,9 +161,33 @@ private: Mac48Address MapDestAddressForAggregation (const WifiMacHeader &hdr); EdcaTxopN &operator = (const EdcaTxopN &); EdcaTxopN (const EdcaTxopN &); + + /* If number of packets in the queue reaches m_blockAckThreshold value, an ADDBARequest frame + * is sent to destination in order to setup a block ack. + */ + bool SetupBlockAckIfNeeded (); + /* Sends an ADDBARequest to establish a block ack agreement with sta + * addressed by recipient for tid tid. + */ + void SendAddBaRequest (Mac48Address recipient, uint8_t tid, uint16_t startSeq, + uint16_t timeout, bool immediateBAck); + /* After that all packets, for which a block ack agreement was established, have been + * transmitted, we have to send a block ack request. + */ + void SendBlockAckRequest (const struct Bar &bar); + /* For now is typically invoked to complete transmission of a packets sent with ack policy + * Block Ack: the packet is buffered and dcf is reset. + */ + void CompleteTx (void); + /* Verifies if dequeued packet has to be transmitted with ack policy Block Ack. This happens + * if an established block ack agreement exists with the receiver. + */ + void VerifyBlockAck (void); + AccessClass m_ac; class Dcf; class TransmissionListener; + class BlockAckEventListener; friend class Dcf; friend class TransmissionListener; Dcf *m_dcf; @@ -158,6 +198,7 @@ private: Ptr m_low; MacTxMiddle *m_txMiddle; TransmissionListener *m_transmissionListener; + BlockAckEventListener *m_blockAckListener; RandomStream *m_rng; Ptr m_stationManager; uint8_t m_fragmentNumber; @@ -170,6 +211,15 @@ private: WifiMacHeader m_currentHdr; Ptr m_aggregator; TypeOfStation m_typeOfStation; + QosBlockedDestinations *m_qosBlockedDestinations; + BlockAckManager *m_baManager; + /* + * Represents the minimun number of packets for use of block ack. + */ + uint8_t m_blockAckThreshold; + enum BlockAckType m_blockAckType; + Time m_currentPacketTimestamp; + uint16_t m_blockAckInactivityTimeout; }; } //namespace ns3 diff --git a/src/devices/wifi/mac-low.cc b/src/devices/wifi/mac-low.cc index 116b679b6..fd95d87d0 100644 --- a/src/devices/wifi/mac-low.cc +++ b/src/devices/wifi/mac-low.cc @@ -1,6 +1,7 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2005,2006 INRIA + * Copyright (c) 2009 MIRKO BANCHI * * 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 @@ -16,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage + * Author: Mirko Banchi */ #include "ns3/assert.h" @@ -28,6 +30,8 @@ #include "mac-low.h" #include "wifi-phy.h" #include "wifi-mac-trailer.h" +#include "qos-utils.h" +#include "edca-txop-n.h" NS_LOG_COMPONENT_DEFINE ("MacLow"); @@ -110,11 +114,23 @@ MacLowTransmissionListener::MacLowTransmissionListener () {} MacLowTransmissionListener::~MacLowTransmissionListener () {} +void +MacLowTransmissionListener::GotBlockAck (const CtrlBAckResponseHeader *blockAck, + Mac48Address source) +{} +void +MacLowTransmissionListener::MissedBlockAck (void) +{} MacLowDcfListener::MacLowDcfListener () {} MacLowDcfListener::~MacLowDcfListener () {} +MacLowBlockAckEventListener::MacLowBlockAckEventListener () +{} +MacLowBlockAckEventListener::~MacLowBlockAckEventListener () +{} + MacLowTransmissionParameters::MacLowTransmissionParameters () : m_nextSize (0), m_waitAck (ACK_NONE), @@ -146,6 +162,21 @@ MacLowTransmissionParameters::EnableSuperFastAck (void) { m_waitAck = ACK_SUPER_FAST; } +void +MacLowTransmissionParameters::EnableBasicBlockAck (void) +{ + m_waitAck = BLOCK_ACK_BASIC; +} +void +MacLowTransmissionParameters::EnableCompressedBlockAck (void) +{ + m_waitAck = BLOCK_ACK_COMPRESSED; +} +void +MacLowTransmissionParameters::EnableMultiTidBlockAck (void) +{ + m_waitAck = BLOCK_ACK_MULTI_TID; +} void MacLowTransmissionParameters::EnableFastAck (void) { @@ -191,6 +222,21 @@ MacLowTransmissionParameters::MustWaitSuperFastAck (void) const { return (m_waitAck == ACK_SUPER_FAST); } +bool +MacLowTransmissionParameters::MustWaitBasicBlockAck (void) const +{ + return (m_waitAck == BLOCK_ACK_BASIC)?true:false; +} +bool +MacLowTransmissionParameters::MustWaitCompressedBlockAck (void) const +{ + return (m_waitAck == BLOCK_ACK_COMPRESSED)?true:false; +} +bool +MacLowTransmissionParameters::MustWaitMultiTidBlockAck (void) const +{ + return (m_waitAck == BLOCK_ACK_MULTI_TID)?true:false; +} bool MacLowTransmissionParameters::MustSendRts (void) const { @@ -239,6 +285,15 @@ std::ostream &operator << (std::ostream &os, const MacLowTransmissionParameters case MacLowTransmissionParameters::ACK_SUPER_FAST: os << "super-fast"; break; + case MacLowTransmissionParameters::BLOCK_ACK_BASIC: + os << "basic-block-ack"; + break; + case MacLowTransmissionParameters::BLOCK_ACK_COMPRESSED: + os << "compressed-block-ack"; + break; + case MacLowTransmissionParameters::BLOCK_ACK_MULTI_TID: + os << "multi-tid-block-ack"; + break; } os << "]"; return os; @@ -273,6 +328,7 @@ MacLow::MacLow () m_fastAckTimeoutEvent (), m_superFastAckTimeoutEvent (), m_fastAckFailedTimeoutEvent (), + m_blockAckTimeoutEvent (), m_ctsTimeoutEvent (), m_sendCtsEvent (), m_sendAckEvent (), @@ -307,6 +363,7 @@ MacLow::DoDispose (void) m_fastAckTimeoutEvent.Cancel (); m_superFastAckTimeoutEvent.Cancel (); m_fastAckFailedTimeoutEvent.Cancel (); + m_blockAckTimeoutEvent.Cancel (); m_ctsTimeoutEvent.Cancel (); m_sendCtsEvent.Cancel (); m_sendAckEvent.Cancel (); @@ -343,6 +400,11 @@ MacLow::CancelAllEvents (void) m_fastAckFailedTimeoutEvent.Cancel (); oneRunning = true; } + if (m_blockAckTimeoutEvent.IsRunning ()) + { + m_blockAckTimeoutEvent.Cancel (); + oneRunning = true; + } if (m_ctsTimeoutEvent.IsRunning ()) { m_ctsTimeoutEvent.Cancel (); @@ -399,6 +461,16 @@ MacLow::SetAckTimeout (Time ackTimeout) { m_ackTimeout = ackTimeout; } +void +MacLow::SetBasicBlockAckTimeout (Time blockAckTimeout) +{ + m_basicBlockAckTimeout = blockAckTimeout; +} +void +MacLow::SetCompressedBlockAckTimeout (Time blockAckTimeout) +{ + m_compressedBlockAckTimeout = blockAckTimeout; +} void MacLow::SetCtsTimeout (Time ctsTimeout) { @@ -434,6 +506,16 @@ MacLow::GetAckTimeout (void) const { return m_ackTimeout; } +Time +MacLow::GetBasicBlockAckTimeout () const +{ + return m_basicBlockAckTimeout; +} +Time +MacLow::GetCompressedBlockAckTimeout () const +{ + return m_compressedBlockAckTimeout; +} Time MacLow::GetCtsTimeout (void) const { @@ -649,6 +731,53 @@ MacLow::ReceiveOk (Ptr packet, double rxSnr, WifiMode txMode, WifiPreamb &MacLow::WaitSifsAfterEndTx, this); } } + else if (hdr.IsBlockAck () && hdr.GetAddr1 () == m_self && + (m_txParams.MustWaitBasicBlockAck () || m_txParams.MustWaitCompressedBlockAck ()) && + m_blockAckTimeoutEvent.IsRunning ()) + { + NS_LOG_DEBUG ("got block ack from "<RemoveHeader (blockAck); + m_blockAckTimeoutEvent.Cancel (); + m_listener->GotBlockAck (&blockAck, hdr.GetAddr2 ()); + } + else if (hdr.IsBlockAckReq () && hdr.GetAddr1 () == m_self) + { + CtrlBAckRequestHeader blockAckReq; + packet->RemoveHeader (blockAckReq); + if (!blockAckReq.IsMultiTid ()) + { + AgreementsI it = m_bAckAgreements.find (std::make_pair (hdr.GetAddr2 (), blockAckReq.GetTidInfo ())); + if (it != m_bAckAgreements.end ()) + { + NS_ASSERT (m_sendAckEvent.IsExpired ()); + /* See section 11.5.3 in IEEE802.11 for mean of this timer */ + ResetBlockAckInactivityTimerIfNeeded (it->second.first); + if ((*it).second.first.IsImmediateBlockAck ()) + { + NS_LOG_DEBUG ("rx blockAckRequest/sendImmediateBlockAck from="<< hdr.GetAddr2 ()); + m_sendAckEvent = Simulator::Schedule (GetSifs (), + &MacLow::SendBlockAckAfterBlockAckRequest, this, + blockAckReq, + hdr.GetAddr2 (), + hdr.GetDuration (), + txMode); + } + else + { + NS_FATAL_ERROR ("Delayed block ack not supported."); + } + } + else + { + NS_LOG_DEBUG ("There's not a valid agreement for this block ack request."); + } + } + else + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + } else if (hdr.IsCtl ()) { NS_LOG_DEBUG ("rx drop " << hdr.GetTypeString ()); @@ -658,7 +787,50 @@ MacLow::ReceiveOk (Ptr packet, double rxSnr, WifiMode txMode, WifiPreamb WifiRemoteStation *station = GetStation (hdr.GetAddr2 ()); station->ReportRxOk (rxSnr, txMode); - if (hdr.IsQosData () && hdr.IsQosNoAck ()) + if (hdr.IsQosData () && StoreMpduIfNeeded (packet, hdr)) + { + /* From section 9.10.4 in IEEE802.11: + Upon the receipt of a QoS data frame from the originator for which + the Block Ack agreement exists, the recipient shall buffer the MSDU + regardless of the value of the Ack Policy subfield within the + QoS Control field of the QoS data frame. */ + if (hdr.IsQosAck ()) + { + AgreementsI it = m_bAckAgreements.find (std::make_pair (hdr.GetAddr2 (), hdr.GetQosTid ())); + RxCompleteBufferedPacketsWithSmallerSequence (it->second.first.GetStartingSequence (), + hdr.GetAddr2 (), hdr.GetQosTid ()); + RxCompleteBufferedPackets (hdr.GetAddr2 (), hdr.GetQosTid ()); + NS_ASSERT (m_sendAckEvent.IsExpired ()); + m_sendAckEvent = Simulator::Schedule (GetSifs (), + &MacLow::SendAckAfterData, this, + hdr.GetAddr2 (), + hdr.GetDuration (), + txMode, + rxSnr); + } + else if (hdr.IsQosBlockAck ()) + { + AgreementsI it = m_bAckAgreements.find (std::make_pair (hdr.GetAddr2 (), hdr.GetQosTid ())); + /* See section 11.5.3 in IEEE802.11 for mean of this timer */ + ResetBlockAckInactivityTimerIfNeeded (it->second.first); + } + return; + } + else if (hdr.IsQosData () && hdr.IsQosBlockAck ()) + { + /* This happens if a packet with ack policy Block Ack is received and a block ack + agreement for that packet doesn't exist. + + From section 11.5.3 in IEEE802.11e: + When a recipient does not have an active Block ack for a TID, but receives + data MPDUs with the Ack Policy subfield set to Block Ack, it shall discard + them and shall send a DELBA frame using the normal access + mechanisms. */ + AccessClass ac = QosUtilsMapTidToAc (hdr.GetQosTid ()); + m_edcaListeners[ac]->BlockAckInactivityTimeout (hdr.GetAddr2 (), hdr.GetQosTid ()); + return; + } + else if (hdr.IsQosData () && hdr.IsQosNoAck ()) { NS_LOG_DEBUG ("rx unicast/noAck from="<CalculateTxDuration (GetAckSize (), ackMode, WIFI_PREAMBLE_LONG); } Time +MacLow::GetBlockAckDuration (Mac48Address to, WifiMode blockAckReqTxMode, enum BlockAckType type) const +{ + /* + * For immediate BlockAck we should transmit the frame with the same WifiMode + * as the BlockAckReq. + * + * from section 9.6 in IEEE802.11e: + * The BlockAck control frame shall be sent at the same rate and modulation class as + * the BlockAckReq frame if it is sent in response to a BlockAckReq frame. + */ + return m_phy->CalculateTxDuration (GetBlockAckSize (type), blockAckReqTxMode, WIFI_PREAMBLE_LONG); +} +Time MacLow::GetCtsDuration (Mac48Address to, WifiMode rtsTxMode) const { WifiMode ctsMode = GetCtsTxModeForRts (to, rtsTxMode); @@ -977,6 +1183,18 @@ MacLow::FastAckTimeout (void) } } void +MacLow::BlockAckTimeout (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG ("block ack timeout"); + + WifiRemoteStation *station = GetStation (m_currentHdr.GetAddr1 ()); + station->ReportDataFailed (); + MacLowTransmissionListener *listener = m_listener; + m_listener = 0; + listener->MissedBlockAck (); +} +void MacLow::SuperFastAckTimeout () { NS_LOG_FUNCTION (this); @@ -1069,7 +1287,19 @@ MacLow::StartDataTxTimers (void) NotifyAckTimeoutStartNow (timerDelay); m_superFastAckTimeoutEvent = Simulator::Schedule (timerDelay, &MacLow::SuperFastAckTimeout, this); - } + } + else if (m_txParams.MustWaitBasicBlockAck ()) + { + Time timerDelay = txDuration + GetBasicBlockAckTimeout (); + NS_ASSERT (m_blockAckTimeoutEvent.IsExpired ()); + m_blockAckTimeoutEvent = Simulator::Schedule (timerDelay, &MacLow::BlockAckTimeout, this); + } + else if (m_txParams.MustWaitCompressedBlockAck ()) + { + Time timerDelay = txDuration + GetCompressedBlockAckTimeout (); + NS_ASSERT (m_blockAckTimeoutEvent.IsExpired ()); + m_blockAckTimeoutEvent = Simulator::Schedule (timerDelay, &MacLow::BlockAckTimeout, this); + } else if (m_txParams.HasNextPacket ()) { Time delay = txDuration + GetSifs (); @@ -1098,7 +1328,17 @@ MacLow::SendDataPacket (void) } else { - if (m_txParams.MustWaitAck ()) + if (m_txParams.MustWaitBasicBlockAck ()) + { + duration += GetSifs (); + duration += GetBlockAckDuration (m_currentHdr.GetAddr1 (), dataTxMode, BASIC_BLOCK_ACK); + } + else if (m_txParams.MustWaitCompressedBlockAck ()) + { + duration += GetSifs (); + duration += GetBlockAckDuration (m_currentHdr.GetAddr1 (), dataTxMode, COMPRESSED_BLOCK_ACK); + } + else if (m_txParams.MustWaitAck ()) { duration += GetSifs (); duration += GetAckDuration (m_currentHdr.GetAddr1 (), dataTxMode); @@ -1255,4 +1495,365 @@ MacLow::SendAckAfterData (Mac48Address source, Time duration, WifiMode dataTxMod ForwardDown (packet, &ack, ackTxMode); } +bool +MacLow::StoreMpduIfNeeded (Ptr packet, WifiMacHeader hdr) +{ + AgreementsI it = m_bAckAgreements.find (std::make_pair (hdr.GetAddr2 (), hdr.GetQosTid ())); + if (it != m_bAckAgreements.end ()) + { + WifiMacTrailer fcs; + packet->RemoveTrailer (fcs); + BufferedPacket bufferedPacket (packet, hdr); + + uint16_t endSequence = ((*it).second.first.GetStartingSequence () + 2047) % 4096; + uint16_t mappedSeqControl = QosUtilsMapSeqControlToUniqueInteger (hdr.GetSequenceControl () ,endSequence); + + BufferedPacketI i = (*it).second.second.begin (); + for (; i != (*it).second.second.end () && + QosUtilsMapSeqControlToUniqueInteger ((*i).second.GetSequenceControl (), endSequence) < mappedSeqControl; i++); + (*it).second.second.insert (i, bufferedPacket); + return true; + } + return false; +} + +void +MacLow::CreateBlockAckAgreement (const MgtAddBaResponseHeader *respHdr, Mac48Address originator, + uint16_t startingSeq) +{ + uint8_t tid = respHdr->GetTid (); + BlockAckAgreement agreement (originator, tid); + if (respHdr->IsImmediateBlockAck ()) + { + agreement.SetImmediateBlockAck (); + } + else + { + agreement.SetDelayedBlockAck (); + } + agreement.SetAmsduSupport (respHdr->IsAmsduSupported ()); + agreement.SetBufferSize (respHdr->GetBufferSize ()); + agreement.SetTimeout (respHdr->GetTimeout ()); + agreement.SetStartingSequence (startingSeq); + + std::list buffer (0); + AgreementKey key (originator, respHdr->GetTid ()); + AgreementValue value (agreement, buffer); + m_bAckAgreements.insert (std::make_pair (key, value)); + + if (respHdr->GetTimeout () != 0) + { + AgreementsI it = m_bAckAgreements.find (std::make_pair (originator, respHdr->GetTid ())); + Time timeout = MicroSeconds (1024 * agreement.GetTimeout ()); + + AccessClass ac = QosUtilsMapTidToAc (agreement.GetTid ()); + + it->second.first.m_inactivityEvent = Simulator::Schedule (timeout, + &MacLowBlockAckEventListener::BlockAckInactivityTimeout, + m_edcaListeners[ac], + originator, tid); + } +} + +void +MacLow::DestroyBlockAckAgreement (Mac48Address originator, uint8_t tid) +{ + AgreementsI it = m_bAckAgreements.find (std::make_pair (originator, tid)); + if (it != m_bAckAgreements.end ()) + { + RxCompleteBufferedPacketsWithSmallerSequence (it->second.first.GetStartingSequence (), originator, tid); + RxCompleteBufferedPackets (originator, tid); + m_bAckAgreements.erase (it); + } +} + +void +MacLow::RxCompleteBufferedPacketsWithSmallerSequence (uint16_t seq, Mac48Address originator, uint8_t tid) +{ + AgreementsI it = m_bAckAgreements.find (std::make_pair (originator, tid)); + if (it != m_bAckAgreements.end ()) + { + BufferedPacketI i = (*it).second.second.begin (); + uint16_t endSequence = ((*it).second.first.GetStartingSequence () + 2047) % 4096; + uint16_t mappedStart = QosUtilsMapSeqControlToUniqueInteger (seq, endSequence); + uint16_t guard = (*it).second.first.GetStartingSequence (); + BufferedPacketI last = (*it).second.second.begin (); + + for (; i != (*it).second.second.end () && + QosUtilsMapSeqControlToUniqueInteger ((*i).second.GetSequenceNumber (), endSequence) < mappedStart;) + { + while (i != (*it).second.second.end () && guard == (*i).second.GetSequenceControl ()) + { + if (!(*i).second.IsMoreFragments ()) + { + while (last != i) + { + m_rxCallback ((*last).first, &(*last).second); + last++; + } + m_rxCallback ((*last).first, &(*last).second); + last++; + } + guard = (*i).second.IsMoreFragments () ? (guard + 1) : ((guard + 16) & 0xfff0); + } + /* go to next packet */ + while (i != (*it).second.second.end () && ((guard >> 4) & 0x0fff) == (*i).second.GetSequenceNumber ()) + { + i++; + } + if (i != (*it).second.second.end ()) + { + guard = (*i).second.GetSequenceControl () & 0xfff0; + last = i; + } + } + (*it).second.second.erase ((*it).second.second.begin (), i); + } +} + +void +MacLow::RxCompleteBufferedPackets (Mac48Address originator, uint8_t tid) +{ + AgreementsI it = m_bAckAgreements.find (std::make_pair (originator, tid)); + if (it != m_bAckAgreements.end ()) + { + uint16_t startingSeqCtrl = ((*it).second.first.GetStartingSequence ()<<4) & 0xfff0; + uint16_t guard = startingSeqCtrl; + + BufferedPacketI lastComplete = (*it).second.second.begin (); + BufferedPacketI i = (*it).second.second.begin (); + for (;i != (*it).second.second.end() && guard == (*i).second.GetSequenceControl (); i++) + { + if (!(*i).second.IsMoreFragments ()) + { + while (lastComplete != i) + { + m_rxCallback ((*lastComplete).first, &(*lastComplete).second); + lastComplete++; + } + m_rxCallback ((*lastComplete).first, &(*lastComplete).second); + lastComplete++; + } + guard = (*i).second.IsMoreFragments () ? (guard + 1) : ((guard + 16) & 0xfff0); + } + (*it).second.first.SetStartingSequence ((guard>>4)&0x0fff); + /* All packets already forwarded to WifiMac must be removed from buffer: + [begin (), lastComplete) */ + (*it).second.second.erase ((*it).second.second.begin (), lastComplete); + } +} + +void +MacLow::SendBlockAckResponse (const CtrlBAckResponseHeader* blockAck, Mac48Address originator, bool immediate, + Time duration, WifiMode blockAckReqTxMode) +{ + Ptr packet = Create (); + packet->AddHeader (*blockAck); + + WifiMacHeader hdr; + hdr.SetType (WIFI_MAC_CTL_BACKRESP); + hdr.SetAddr1 (originator); + hdr.SetAddr2 (GetAddress ()); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + hdr.SetNoRetry (); + hdr.SetNoMoreFragments (); + + m_currentPacket = packet; + m_currentHdr = hdr; + if (immediate) + { + m_txParams.DisableAck (); + duration -= GetSifs (); + if (blockAck->IsBasic ()) + { + duration -= GetBlockAckDuration (originator, blockAckReqTxMode, BASIC_BLOCK_ACK); + } + else if (blockAck->IsCompressed ()) + { + duration -= GetBlockAckDuration (originator, blockAckReqTxMode, COMPRESSED_BLOCK_ACK); + } + else if (blockAck->IsMultiTid ()) + { + NS_FATAL_ERROR ("Multi-tid block ack is not supported."); + } + } + else + { + m_txParams.EnableAck (); + duration += GetSifs (); + duration += GetAckDuration (originator, blockAckReqTxMode); + } + m_txParams.DisableNextData (); + + StartDataTxTimers (); + + NS_ASSERT (duration >= MicroSeconds (0)); + hdr.SetDuration (duration); + //here should be present a control about immediate or delayed block ack + //for now we assume immediate + packet->AddHeader (hdr); + WifiMacTrailer fcs; + packet->AddTrailer (fcs); + ForwardDown (packet, &hdr, blockAckReqTxMode); + m_currentPacket = 0; +} + +void +MacLow::SendBlockAckAfterBlockAckRequest (const CtrlBAckRequestHeader reqHdr, Mac48Address originator, + Time duration, WifiMode blockAckReqTxMode) +{ + NS_LOG_FUNCTION (this); + CtrlBAckResponseHeader blockAck; + uint8_t tid; + bool immediate = false; + if (!reqHdr.IsMultiTid ()) + { + blockAck.SetStartingSequence (reqHdr.GetStartingSequence ()); + blockAck.SetTidInfo (reqHdr.GetTidInfo ()); + + tid = reqHdr.GetTidInfo (); + AgreementsI it; + it = m_bAckAgreements.find (std::make_pair (originator, tid)); + if (it != m_bAckAgreements.end ()) + { + immediate = (*it).second.first.IsImmediateBlockAck (); + uint16_t startingSeqCtrl = reqHdr.GetStartingSequenceControl (); + + /* All packets with smaller sequence than starting sequence control must be passed up to Wifimac + * See 9.10.3 in IEEE8022.11e standard. + */ + RxCompleteBufferedPacketsWithSmallerSequence ((startingSeqCtrl>>4)&0xfff0, originator, tid); + + std::list::iterator i = (*it).second.second.begin (); + + /* For more details about next operations see section 9.10.4 of IEEE802.11e standard */ + if (reqHdr.IsBasic ()) + { + blockAck.SetType (BASIC_BLOCK_ACK); + uint16_t guard = startingSeqCtrl; + std::list::iterator lastComplete = (*it).second.second.begin (); + for (; i != (*it).second.second.end () && guard == (*i).second.GetSequenceControl (); i++) + { + blockAck.SetReceivedFragment ((*i).second.GetSequenceNumber (), + (*i).second.GetFragmentNumber ()); + /* Section 9.10.4 in IEEE802.11n: the recipient shall pass up to WifiMac the + * MSDUs and A-MSDUs starting with the starting sequence number + * sequentially until there is an incomplete MSDU or A-MSDU in the buffer */ + if (!(*i).second.IsMoreFragments ()) + { + while (lastComplete != i) + { + m_rxCallback ((*lastComplete).first, &(*lastComplete).second); + lastComplete++; + } + m_rxCallback ((*lastComplete).first, &(*lastComplete).second); + lastComplete++; + } + guard = (*i).second.IsMoreFragments () ? (guard + 1) : (guard + 16) & 0xfff0; + } + (*it).second.first.SetStartingSequence ((guard>>4)&0x0fff); + /* All packets already forwarded to WifiMac must be removed from buffer: + [begin (), lastComplete) */ + (*it).second.second.erase ((*it).second.second.begin (), lastComplete); + for (i = lastComplete; i != (*it).second.second.end (); i++) + { + blockAck.SetReceivedFragment ((*i).second.GetSequenceNumber (), + (*i).second.GetFragmentNumber ()); + } + } + else if (reqHdr.IsCompressed ()) + { + blockAck.SetType (COMPRESSED_BLOCK_ACK); + uint16_t guard = startingSeqCtrl; + std::list::iterator lastComplete = (*it).second.second.begin (); + for (; i != (*it).second.second.end () && guard == (*i).second.GetSequenceControl (); i++) + { + if (!(*i).second.IsMoreFragments ()) + { + blockAck.SetReceivedPacket ((*i).second.GetSequenceNumber ()); + while (lastComplete != i) + { + m_rxCallback ((*lastComplete).first, &(*lastComplete).second); + lastComplete++; + } + m_rxCallback ((*lastComplete).first, &(*lastComplete).second); + lastComplete++; + } + guard = (*i).second.IsMoreFragments () ? (guard + 1) : (guard + 16) & 0xfff0; + } + (*it).second.first.SetStartingSequence ((guard>>4)&0x0fff); + /* All packets already forwarded to WifiMac must be removed from buffer: + [begin (), lastcomplete) */ + (*it).second.second.erase ((*it).second.second.begin (), lastComplete); + i = lastComplete; + if (i != (*it).second.second.end ()) + { + guard = (*i).second.GetSequenceControl () & 0xfff0; + } + for (; i != (*it).second.second.end ();) + { + for (; i != (*it).second.second.end () && guard == (*i).second.GetSequenceControl (); i++) + { + if (!(*i).second.IsMoreFragments ()) + { + guard = (guard + 16) & 0xfff0; + blockAck.SetReceivedPacket ((*i).second.GetSequenceNumber ()); + } + else + { + guard += 1; + } + } + while (i != (*it).second.second.end () && ((guard >> 4) & 0x0fff) == (*i).second.GetSequenceNumber ()) + { + i++; + } + if (i != (*it).second.second.end ()) + { + guard = (*i).second.GetSequenceControl () & 0xfff0; + } + } + } + } + else + { + NS_LOG_DEBUG ("there's not a valid block ack agreement with "<::iterator it = m_edcaListeners.find (ac); + //NS_ASSERT (it != m_edcaListeners.end ()); + + agreement.m_inactivityEvent = Simulator::Schedule (timeout, + &MacLowBlockAckEventListener::BlockAckInactivityTimeout, + m_edcaListeners[ac], + agreement.GetPeer (), + agreement.GetTid ()); + } +} + +void +MacLow::RegisterBlockAckListenerForAc (enum AccessClass ac, MacLowBlockAckEventListener *listener) +{ + m_edcaListeners.insert (std::make_pair (ac, listener)); +} + } // namespace ns3 diff --git a/src/devices/wifi/mac-low.h b/src/devices/wifi/mac-low.h index c6104faf9..f72971d2a 100644 --- a/src/devices/wifi/mac-low.h +++ b/src/devices/wifi/mac-low.h @@ -1,6 +1,7 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2005, 2006 INRIA + * Copyright (c) 2009 MIRKO BANCHI * * 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 @@ -16,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage + * Author: Mirko Banchi */ #ifndef MAC_LOW_H #define MAC_LOW_H @@ -23,21 +25,27 @@ #include #include #include +#include #include "wifi-mac-header.h" #include "wifi-mode.h" #include "wifi-preamble.h" #include "wifi-remote-station-manager.h" +#include "ctrl-headers.h" +#include "mgt-headers.h" +#include "block-ack-agreement.h" #include "ns3/mac48-address.h" #include "ns3/callback.h" #include "ns3/event-id.h" #include "ns3/packet.h" #include "ns3/nstime.h" +#include "qos-utils.h" namespace ns3 { class WifiPhy; class WifiMac; +class EdcaTxopN; /** * \brief listen to events coming from ns3::MacLow. @@ -75,6 +83,29 @@ public: * AckTimeout. */ virtual void MissedAck (void) = 0; + /** + * \param blockAck Block ack response header + * \param source Address of block ack sender + * + * Invoked when ns3::MacLow receives a block ack frame. + * Block ack frame is received after a block ack request + * and contains information about the correct reception + * of a set of packet for which a normal ack wasn't send. + * Default implementation for this method is empty. Every + * queue that intends to be notified by MacLow of reception + * of a block ack must redefine this function. + */ + virtual void GotBlockAck (const CtrlBAckResponseHeader *blockAck, Mac48Address source); + /** + * ns3::MacLow did not receive an expected BLOCK_ACK within + * BlockAckTimeout. This method is used only for immediate + * block ack variant. With delayed block ack, the MissedAck method will be + * called instead: upon receipt of a block ack request, the rx station will + * reply with a normal ack frame. Later, when the rx station gets a txop, it + * will send the block ack back to the tx station which will reply with a + * normal ack to the rx station. + */ + virtual void MissedBlockAck (void); /** * Invoked when ns3::MacLow wants to start a new transmission * as configured by MacLowTransmissionParameters::EnableNextData. @@ -118,6 +149,25 @@ public: virtual void CtsTimeoutReset () = 0; }; +/** + * \brief listen for block ack events. + */ +class MacLowBlockAckEventListener { +public: + MacLowBlockAckEventListener (); + virtual ~MacLowBlockAckEventListener (); + /** + * Typically is called in order to notify EdcaTxopN that a block ack inactivity + * timeout occurs for the block ack agreement identified by the pair originator, tid. + * + * Rx station maintains an inactivity timer for each block ack + * agreement. Timer is reset when a frame with ack policy block ack + * or a block ack request are received. When this timer reaches zero + * this method is called and a delba frame is scheduled for transmission. + */ + virtual void BlockAckInactivityTimeout (Mac48Address originator, uint8_t tid) = 0; +}; + /** * \brief control how a packet is transmitted. * @@ -156,6 +206,18 @@ public: * MacLowTransmissionListener::MissedAck */ void EnableSuperFastAck (void); + /** + * Wait BASICBLOCKACKTimeout for a Basic Block Ack Response frame. + */ + void EnableBasicBlockAck (void); + /** + * Wait COMPRESSEDBLOCKACKTimeout for a Compressed Block Ack Response frame. + */ + void EnableCompressedBlockAck (void); + /** + * NOT IMPLEMENTED FOR NOW + */ + void EnableMultiTidBlockAck (void); /** * Send a RTS, and wait CTSTimeout for a CTS. If we get a * CTS on time, call MacLowTransmissionListener::GotCts @@ -232,6 +294,24 @@ public: * \sa EnableSuperFastAck */ bool MustWaitSuperFastAck (void) const; + /** + * \returns true if block ack mechanism is used, false otherwise. + * + * \sa EnableBlockAck + */ + bool MustWaitBasicBlockAck (void) const; + /** + * \returns true if compressed block ack mechanism is used, false otherwise. + * + * \sa EnableCompressedBlockAck + */ + bool MustWaitCompressedBlockAck (void) const; + /** + * \returns true if multi-tid block ack mechanism is used, false otherwise. + * + * \sa EnableMultiTidBlockAck + */ + bool MustWaitMultiTidBlockAck (void) const; /** * \returns true if RTS should be sent and CTS waited for before * sending data, false otherwise. @@ -262,7 +342,10 @@ private: ACK_NONE, ACK_NORMAL, ACK_FAST, - ACK_SUPER_FAST + ACK_SUPER_FAST, + BLOCK_ACK_BASIC, + BLOCK_ACK_COMPRESSED, + BLOCK_ACK_MULTI_TID } m_waitAck; bool m_sendRts; Time m_overrideDurationId; @@ -286,6 +369,8 @@ public: void SetAddress (Mac48Address ad); void SetAckTimeout (Time ackTimeout); + void SetBasicBlockAckTimeout (Time blockAckTimeout); + void SetCompressedBlockAckTimeout (Time blockAckTimeout); void SetCtsTimeout (Time ctsTimeout); void SetSifs (Time sifs); void SetSlotTime (Time slotTime); @@ -293,6 +378,8 @@ public: void SetBssid (Mac48Address ad); Mac48Address GetAddress (void) const; Time GetAckTimeout (void) const; + Time GetBasicBlockAckTimeout () const; + Time GetCompressedBlockAckTimeout () const; Time GetCtsTimeout (void) const; Time GetSifs (void) const; Time GetSlotTime (void) const; @@ -364,9 +451,39 @@ public: * occurs, pending MAC transmissions (RTS, CTS, DATA and ACK) are cancelled. */ void NotifySwitchingStartNow (Time duration); + /** + * \param respHdr Add block ack response from originator (action frame). + * \param originator Address of peer station involved in block ack mechanism. + * \param startingSeq Sequence number of the first MPDU of all packets for which block ack was negotiated. + * + * This function is typically invoked only by ns3::QapWifiMac and ns3::QstaWifiMac. + * If we are transmitting an Add block ack response, MacLow must allocate buffers to collect + * all correctly received packets belonging to category for which block ack was negotiated. + * It's needed in order to send a Block ack after corresponding originator's Block ack request. + */ + void CreateBlockAckAgreement (const MgtAddBaResponseHeader *respHdr, Mac48Address originator, + uint16_t startingSeq); + /** + * \param originator Address of peer partecipating in Block Ack mechanism. + * \param tid TID for which Block Ack was created. + * + * Checks if exists an established block ack agreement with originator + * for tid tid. If the agreement exists, tears down it. This function is typically + * invoked when a DELBA frame is received from originator. + */ + void DestroyBlockAckAgreement (Mac48Address originator, uint8_t tid); + /** + * \param ac Access class managed by the queue. + * \param listener The listener for the queue. + * + * The lifetime of the registered listener is typically equal to the lifetime of the queue + * associated to this AC. + */ + void RegisterBlockAckListenerForAc (enum AccessClass ac, MacLowBlockAckEventListener *listener); private: void CancelAllEvents (void); uint32_t GetAckSize (void) const; + uint32_t GetBlockAckSize (enum BlockAckType type) const; uint32_t GetRtsSize (void) const; uint32_t GetCtsSize (void) const; uint32_t GetSize (Ptr packet, const WifiMacHeader *hdr) const; @@ -381,8 +498,10 @@ private: WifiMode GetDataTxMode (Ptr packet, const WifiMacHeader *hdr) const; WifiMode GetCtsTxModeForRts (Mac48Address to, WifiMode rtsTxMode) const; WifiMode GetAckTxModeForData (Mac48Address to, WifiMode dataTxMode) const; + Time GetCtsDuration (Mac48Address to, WifiMode rtsTxMode) const; Time GetAckDuration (Mac48Address to, WifiMode dataTxMode) const; + Time GetBlockAckDuration (Mac48Address to, WifiMode blockAckReqTxMode, enum BlockAckType type) const; void NotifyNav (const WifiMacHeader &hdr, WifiMode txMode, WifiPreamble preamble); void DoNavResetNow (Time duration); bool DoNavStartNow (Time duration); @@ -398,6 +517,7 @@ private: void FastAckTimeout (void); void SuperFastAckTimeout (void); void FastAckFailedTimeout (void); + void BlockAckTimeout (void); void CtsTimeout (void); void SendCtsAfterRts (Mac48Address source, Time duration, WifiMode txMode, double rtsSnr); void SendAckAfterData (Mac48Address source, Time duration, WifiMode txMode, double rtsSnr); @@ -409,6 +529,53 @@ private: void SendCurrentTxPacket (void); void StartDataTxTimers (void); virtual void DoDispose (void); + /** + * \param originator Address of peer partecipating in Block Ack mechanism. + * \param tid TID for which Block Ack was created. + * \param seq Starting sequence + * + * This function forward up all completed "old" packets with sequence number + * smaller than seq. All comparison are performed circularly mod 4096. + */ + void RxCompleteBufferedPacketsWithSmallerSequence (uint16_t seq, Mac48Address originator, uint8_t tid); + /** + * \param originator Address of peer partecipating in Block Ack mechanism. + * \param tid TID for which Block Ack was created. + * + * This method is typically invoked when a MPDU with ack policy + * subfield set to Normal Ack is received and a block ack agreement + * for that packet exists. + * This happens when the originator of block ack has only few MPDUs to send. + * All completed MSDUs starting with starting sequence number of block ack + * agreement are forward up to WifiMac until there is an incomplete MSDU. + * See section 9.10.4 in IEEE802.11 standard for more details. + */ + void RxCompleteBufferedPackets (Mac48Address originator, uint8_t tid); + /* + * This method checks if exists a valid established block ack agreement. + * If there is, store the packet without pass it up to WifiMac. The packet is buffered + * in order of increasing sequence control field. All comparison are performed + * circularly modulo 2^12. + */ + bool StoreMpduIfNeeded (Ptr packet, WifiMacHeader hdr); + /* + * Invoked after that a block ack request has been received. Looks for corresponding + * block ack agreement and creates block ack bitmap on a received packets basis. + */ + void SendBlockAckAfterBlockAckRequest (const CtrlBAckRequestHeader reqHdr, Mac48Address originator, + Time duration, WifiMode blockAckReqTxMode); + /* + * This method creates block ack frame with header equals to blockAck and start its transmission. + */ + void SendBlockAckResponse (const CtrlBAckResponseHeader* blockAck, Mac48Address originator, bool immediate, + Time duration, WifiMode blockAckReqTxMode); + /* + * Every time that a block ack request or a packet with ack policy equals to block ack + * are received, if a relative block ack agreement exists and the value of inactivity timeout + * is not 0, the timer is reset. + * see section 11.5.3 in IEEE802.11e for more details. + */ + void ResetBlockAckInactivityTimerIfNeeded (BlockAckAgreement &agreement); void SetupPhyMacLowListener (Ptr phy); @@ -423,6 +590,7 @@ private: EventId m_fastAckTimeoutEvent; EventId m_superFastAckTimeoutEvent; EventId m_fastAckFailedTimeoutEvent; + EventId m_blockAckTimeoutEvent; EventId m_ctsTimeoutEvent; EventId m_sendCtsEvent; EventId m_sendAckEvent; @@ -437,6 +605,8 @@ private: Mac48Address m_self; Mac48Address m_bssid; Time m_ackTimeout; + Time m_basicBlockAckTimeout; + Time m_compressedBlockAckTimeout; Time m_ctsTimeout; Time m_sifs; Time m_slotTime; @@ -447,6 +617,23 @@ private: // Listerner needed to monitor when a channel switching occurs. class PhyMacLowListener *m_phyMacLowListener; + + /* + * BlockAck data structures. + */ + typedef std::pair, WifiMacHeader> BufferedPacket; + typedef std::list::iterator BufferedPacketI; + + typedef std::pair AgreementKey; + typedef std::pair > AgreementValue; + + typedef std::map Agreements; + typedef std::map::iterator AgreementsI; + + Agreements m_bAckAgreements; + + typedef std::map QueueListeners; + QueueListeners m_edcaListeners; }; } // namespace ns3 diff --git a/src/devices/wifi/mac-tx-middle.cc b/src/devices/wifi/mac-tx-middle.cc index 74a5b6207..2b84d7d91 100644 --- a/src/devices/wifi/mac-tx-middle.cc +++ b/src/devices/wifi/mac-tx-middle.cc @@ -77,4 +77,17 @@ MacTxMiddle::GetNextSequenceNumberfor (const WifiMacHeader *hdr) return retval; } +uint16_t +MacTxMiddle::GetNextSeqNumberByTidAndAddress (uint8_t tid, Mac48Address addr) const +{ + NS_ASSERT (tid < 16); + uint16_t seq = 0; + std::map ::const_iterator it = m_qosSequences.find (addr); + if (it != m_qosSequences.end ()) + { + return it->second[tid]; + } + return seq; +} + } // namespace ns3 diff --git a/src/devices/wifi/mac-tx-middle.h b/src/devices/wifi/mac-tx-middle.h index a701a93eb..9bccfc779 100644 --- a/src/devices/wifi/mac-tx-middle.h +++ b/src/devices/wifi/mac-tx-middle.h @@ -37,6 +37,7 @@ public: ~MacTxMiddle (); uint16_t GetNextSequenceNumberfor (const WifiMacHeader *hdr); + uint16_t GetNextSeqNumberByTidAndAddress (uint8_t tid, Mac48Address addr) const; private: std::map m_qosSequences; diff --git a/src/devices/wifi/mgt-headers.cc b/src/devices/wifi/mgt-headers.cc index dfcb2d0cc..cbf531dba 100644 --- a/src/devices/wifi/mgt-headers.cc +++ b/src/devices/wifi/mgt-headers.cc @@ -1,6 +1,7 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2006 INRIA + * Copyright (c) 2009 MIRKO BANCHI * * 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 @@ -16,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage + * Author: Mirko Banchi */ #include "mgt-headers.h" #include "ns3/simulator.h" @@ -405,6 +407,11 @@ WifiActionHeader::SetAction (WifiActionHeader::CategoryValue type, switch (type) { + case BLOCK_ACK: + { + m_actionValue = action.blockAck; + break; + } case MESH_PEERING_MGT: { m_actionValue = action.peerLink; @@ -427,6 +434,8 @@ WifiActionHeader::GetCategory () { switch (m_category) { + case BLOCK_ACK: + return BLOCK_ACK; case MESH_PEERING_MGT: return MESH_PEERING_MGT; case MESH_LINK_METRIC: @@ -451,6 +460,19 @@ WifiActionHeader::GetAction () retval.peerLink = PEER_LINK_OPEN; // Needs to be initialized to something to quiet valgrind in default cases switch (m_category) { + case BLOCK_ACK: + switch (m_actionValue) + { + case BLOCK_ACK_ADDBA_REQUEST: + retval.blockAck = BLOCK_ACK_ADDBA_REQUEST; + return retval; + case BLOCK_ACK_ADDBA_RESPONSE: + retval.blockAck = BLOCK_ACK_ADDBA_RESPONSE; + return retval; + case BLOCK_ACK_DELBA: + retval.blockAck = BLOCK_ACK_DELBA; + return retval; + } case MESH_PEERING_MGT: switch (m_actionValue) { @@ -528,4 +550,449 @@ WifiActionHeader::Deserialize (Buffer::Iterator start) return i.GetDistanceFrom (start); } +/*************************************************** +* ADDBARequest +****************************************************/ + +NS_OBJECT_ENSURE_REGISTERED (MgtAddBaRequestHeader); + +MgtAddBaRequestHeader::MgtAddBaRequestHeader () + : m_dialogToken (1), + m_amsduSupport (1), + m_bufferSize (0) +{} + +TypeId +MgtAddBaRequestHeader::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::MgtAddBaRequestHeader") + .SetParent
() + .AddConstructor (); + ; + return tid; +} + +TypeId +MgtAddBaRequestHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +void +MgtAddBaRequestHeader::Print (std::ostream &os) const +{} + +uint32_t +MgtAddBaRequestHeader::GetSerializedSize (void) const +{ + uint32_t size = 0; + size += 1; //Dialog token + size += 2; //Block ack parameter set + size += 2; //Block ack timeout value + size += 2; //Starting sequence control + return size; +} + +void +MgtAddBaRequestHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteU8 (m_dialogToken); + i.WriteHtolsbU16 (GetParameterSet ()); + i.WriteHtolsbU16 (m_timeoutValue); + i.WriteHtolsbU16 (GetStartingSequenceControl ()); +} + +uint32_t +MgtAddBaRequestHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + m_dialogToken = i.ReadU8 (); + SetParameterSet (i.ReadLsbtohU16 ()); + m_timeoutValue = i.ReadLsbtohU16 (); + SetStartingSequenceControl (i.ReadLsbtohU16 ()); + return i.GetDistanceFrom (start); +} + +void +MgtAddBaRequestHeader::SetDelayedBlockAck () +{ + m_policy = 0; +} + +void +MgtAddBaRequestHeader::SetImmediateBlockAck () +{ + m_policy = 1; +} + +void +MgtAddBaRequestHeader::SetTid (uint8_t tid) +{ + NS_ASSERT (tid < 16); + m_tid = tid; +} + +void +MgtAddBaRequestHeader::SetTimeout (uint16_t timeout) +{ + m_timeoutValue = timeout; +} + +void +MgtAddBaRequestHeader::SetBufferSize (uint16_t size) +{ + m_bufferSize = size; +} + +void +MgtAddBaRequestHeader::SetStartingSequence (uint16_t seq) +{ + m_startingSeq = seq; +} + +void +MgtAddBaRequestHeader::SetAmsduSupport (bool supported) +{ + m_amsduSupport = supported; +} + +uint8_t +MgtAddBaRequestHeader::GetTid (void) const +{ + return m_tid; +} + +bool +MgtAddBaRequestHeader::IsImmediateBlockAck (void) const +{ + return (m_policy == 1)?true:false; +} + +uint16_t +MgtAddBaRequestHeader::GetTimeout (void) const +{ + return m_timeoutValue; +} + +uint16_t +MgtAddBaRequestHeader::GetBufferSize (void) const +{ + return m_bufferSize; +} + +bool +MgtAddBaRequestHeader::IsAmsduSupported (void) const +{ + return (m_amsduSupport == 1)?true:false; +} + +uint16_t +MgtAddBaRequestHeader::GetStartingSequence (void) const +{ + return m_startingSeq; +} + +uint16_t +MgtAddBaRequestHeader::GetStartingSequenceControl (void) const +{ + return (m_startingSeq << 4) & 0xfff0; +} + +void +MgtAddBaRequestHeader::SetStartingSequenceControl (uint16_t seqControl) +{ + m_startingSeq = (seqControl >> 4) & 0x0fff; +} + +uint16_t +MgtAddBaRequestHeader::GetParameterSet (void) const +{ + uint16_t res = 0; + res |= m_amsduSupport; + res |= m_policy << 1; + res |= m_tid << 2; + res |= m_bufferSize << 6; + return res; +} + +void +MgtAddBaRequestHeader::SetParameterSet (uint16_t params) +{ + m_amsduSupport = (params) & 0x01; + m_policy = (params >> 1) & 0x01; + m_tid = (params >> 2) & 0x0f; + m_bufferSize = (params >> 6) & 0x03ff; +} + +/*************************************************** +* ADDBAResponse +****************************************************/ + +NS_OBJECT_ENSURE_REGISTERED (MgtAddBaResponseHeader); + +MgtAddBaResponseHeader::MgtAddBaResponseHeader () + : m_dialogToken (1), + m_amsduSupport (1), + m_bufferSize (0) +{} + +TypeId +MgtAddBaResponseHeader::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::MgtAddBaResponseHeader") + .SetParent
() + .AddConstructor () + ; + return tid; +} + +TypeId +MgtAddBaResponseHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +void +MgtAddBaResponseHeader::Print (std::ostream &os) const +{ + os <<"status code="<> 1) & 0x01; + m_tid = (params >> 2) & 0x0f; + m_bufferSize = (params >> 6) & 0x03ff; +} + +/*************************************************** +* DelBa +****************************************************/ + +NS_OBJECT_ENSURE_REGISTERED (MgtDelBaHeader); + +MgtDelBaHeader::MgtDelBaHeader () + : m_reasonCode (1) +{} + +TypeId +MgtDelBaHeader::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::MgtDelBaHeader") + .SetParent
() + .AddConstructor () + ; + return tid; +} + +TypeId +MgtDelBaHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +void +MgtDelBaHeader::Print (std::ostream &os) const +{} + +uint32_t +MgtDelBaHeader::GetSerializedSize (void) const +{ + uint32_t size = 0; + size += 2; //DelBa parameter set + size += 2; //Reason code + return size; +} + +void +MgtDelBaHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteHtolsbU16 (GetParameterSet ()); + i.WriteHtolsbU16 (m_reasonCode); +} + +uint32_t +MgtDelBaHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + SetParameterSet (i.ReadLsbtohU16 ()); + m_reasonCode = i.ReadLsbtohU16 (); + return i.GetDistanceFrom (start); +} + +bool +MgtDelBaHeader::IsByOriginator (void) const +{ + return (m_initiator == 1)?true:false; +} + +uint8_t +MgtDelBaHeader::GetTid (void) const +{ + NS_ASSERT (m_tid < 16); + uint8_t tid = static_cast (m_tid); + return tid; +} + +void +MgtDelBaHeader::SetByOriginator (void) +{ + m_initiator = 1; +} + +void +MgtDelBaHeader::SetByRecipient (void) +{ + m_initiator = 0; +} + +void +MgtDelBaHeader::SetTid (uint8_t tid) +{ + NS_ASSERT (tid < 16); + m_tid = static_cast (tid); +} + +uint16_t +MgtDelBaHeader::GetParameterSet (void) const +{ + uint16_t res = 0; + res |= m_initiator << 11; + res |= m_tid << 12; + return res; +} + +void +MgtDelBaHeader::SetParameterSet (uint16_t params) +{ + m_initiator = (params >> 11) & 0x01; + m_tid = (params >> 12) & 0x0f; +} + } // namespace ns3 diff --git a/src/devices/wifi/mgt-headers.h b/src/devices/wifi/mgt-headers.h index 6a04c8ffb..6d8a9cd4b 100644 --- a/src/devices/wifi/mgt-headers.h +++ b/src/devices/wifi/mgt-headers.h @@ -1,6 +1,7 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2006 INRIA + * Copyright (c) 2009 MIRKO BANCHI * * 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 @@ -16,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage + * Author: Mirko Banchi */ #ifndef MGT_HEADERS_H #define MGT_HEADERS_H @@ -134,6 +136,10 @@ private: class MgtBeaconHeader : public MgtProbeResponseHeader {}; +/**************************** +* Action frames +*****************************/ + /** * \brief See IEEE 802.11 chapter 7.3.1.11 * @@ -148,6 +154,7 @@ public: /* Compatible with open80211s implementation */ enum CategoryValue //table 7-24 staring from 4 { + BLOCK_ACK = 3, MESH_PEERING_MGT = 30, MESH_LINK_METRIC = 31, MESH_PATH_SELECTION = 32, @@ -189,6 +196,12 @@ public: TBTT_ADJUSTMENT_REQUEST, MESH_CHANNEL_SWITCH_ANNOUNCEMENT, }; + enum BlockAckActionValue + { + BLOCK_ACK_ADDBA_REQUEST = 0, + BLOCK_ACK_ADDBA_RESPONSE = 1, + BLOCK_ACK_DELBA = 2 + }; typedef union { enum PeerLinkMgtActionValue peerLink; @@ -196,6 +209,7 @@ public: enum PathSelectionActionValue pathSelection; enum InterworkActionValue interwork; enum ResourceCoordinationActionValue resourceCoordination; + enum BlockAckActionValue blockAck; } ActionValue; void SetAction (enum CategoryValue type,ActionValue action); @@ -212,6 +226,115 @@ private: uint8_t m_actionValue; }; +class MgtAddBaRequestHeader : public Header { +public: + + MgtAddBaRequestHeader (); + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + + void SetDelayedBlockAck (); + void SetImmediateBlockAck (); + void SetTid (uint8_t tid); + void SetTimeout (uint16_t timeout); + void SetBufferSize (uint16_t size); + void SetStartingSequence (uint16_t seq); + void SetAmsduSupport (bool supported); + + uint16_t GetStartingSequence (void) const; + uint8_t GetTid (void) const; + bool IsImmediateBlockAck (void) const; + uint16_t GetTimeout (void) const; + uint16_t GetBufferSize (void) const; + bool IsAmsduSupported (void) const; + +private: + uint16_t GetParameterSet (void) const; + void SetParameterSet (uint16_t params); + uint16_t GetStartingSequenceControl (void) const; + void SetStartingSequenceControl (uint16_t seqControl); + + uint8_t m_dialogToken; /* Not used for now */ + uint8_t m_amsduSupport; + uint8_t m_policy; + uint8_t m_tid; + uint16_t m_bufferSize; + uint16_t m_timeoutValue; + uint16_t m_startingSeq; +}; + +class MgtAddBaResponseHeader : public Header { +public: + + MgtAddBaResponseHeader (); + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + + void SetDelayedBlockAck (); + void SetImmediateBlockAck (); + void SetTid (uint8_t tid); + void SetTimeout (uint16_t timeout); + void SetBufferSize (uint16_t size); + void SetStatusCode (StatusCode code); + void SetAmsduSupport (bool supported); + + StatusCode GetStatusCode (void) const; + uint8_t GetTid (void) const; + bool IsImmediateBlockAck (void) const; + uint16_t GetTimeout (void) const; + uint16_t GetBufferSize (void) const; + bool IsAmsduSupported (void) const; + +private: + uint16_t GetParameterSet (void) const; + void SetParameterSet (uint16_t params); + + uint8_t m_dialogToken; /* Not used for now */ + StatusCode m_code; + uint8_t m_amsduSupport; + uint8_t m_policy; + uint8_t m_tid; + uint16_t m_bufferSize; + uint16_t m_timeoutValue; +}; + +class MgtDelBaHeader : public Header { +public: + MgtDelBaHeader (); + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + + bool IsByOriginator (void) const; + uint8_t GetTid (void) const; + void SetTid (uint8_t); + void SetByOriginator (void); + void SetByRecipient (void); + +private: + uint16_t GetParameterSet (void) const; + void SetParameterSet (uint16_t params); + + uint16_t m_initiator; + uint16_t m_tid; + /* Not used for now. + Always set to 1: "Unspecified reason" */ + uint16_t m_reasonCode; +}; } // namespace ns3 diff --git a/src/devices/wifi/originator-block-ack-agreement.cc b/src/devices/wifi/originator-block-ack-agreement.cc new file mode 100644 index 000000000..934c41dda --- /dev/null +++ b/src/devices/wifi/originator-block-ack-agreement.cc @@ -0,0 +1,82 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#include "originator-block-ack-agreement.h" + +namespace ns3 { + +OriginatorBlockAckAgreement::OriginatorBlockAckAgreement () + : BlockAckAgreement (), + m_state (PENDING), + m_sentMpdus (0) +{} +OriginatorBlockAckAgreement::OriginatorBlockAckAgreement (Mac48Address recipient, uint8_t tid) + : BlockAckAgreement (recipient, tid), + m_state (PENDING), + m_sentMpdus (0) +{} +OriginatorBlockAckAgreement::~OriginatorBlockAckAgreement () +{} +void +OriginatorBlockAckAgreement::SetState (enum State state) +{ + m_state = state; + if (state == INACTIVE) + { + m_sentMpdus = 0; + } +} +bool +OriginatorBlockAckAgreement::IsPending (void) const +{ + return (m_state == PENDING)?true:false; +} +bool +OriginatorBlockAckAgreement::IsEstablished (void) const +{ + return (m_state == ESTABLISHED)?true:false; +} +bool +OriginatorBlockAckAgreement::IsInactive (void) const +{ + return (m_state == INACTIVE)?true:false; +} +bool +OriginatorBlockAckAgreement::IsUnsuccessful (void) const +{ + return (m_state == UNSUCCESSFUL)?true:false; +} +void +OriginatorBlockAckAgreement::NotifyMpduTransmission (void) +{ + NS_ASSERT (m_sentMpdus < m_bufferSize); + m_sentMpdus++; +} +bool +OriginatorBlockAckAgreement::NeedBlockAckRequest (void) const +{ + return (m_sentMpdus == m_bufferSize/2)?true:false; +} +void +OriginatorBlockAckAgreement::CompleteExchange (void) +{ + m_sentMpdus = 0; +} + +} //namespace ns3 diff --git a/src/devices/wifi/originator-block-ack-agreement.h b/src/devices/wifi/originator-block-ack-agreement.h new file mode 100644 index 000000000..3bdf68198 --- /dev/null +++ b/src/devices/wifi/originator-block-ack-agreement.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#ifndef ORIGINATOR_BLOCK_ACK_AGREEMENT_H +#define ORIGINATOR_BLOCK_ACK_AGREEMENT_H + +#include "block-ack-agreement.h" + +namespace ns3 { + +/* \brief Maintains the state and information about trasmitted MPDUs with ack policy block ack + * for an originator station. + */ +class OriginatorBlockAckAgreement : public BlockAckAgreement +{ + friend class BlockAckManager; +public: + OriginatorBlockAckAgreement (); + OriginatorBlockAckAgreement (Mac48Address recipient, uint8_t tid); + ~OriginatorBlockAckAgreement (); + /* receive ADDBAResponse + * send ADDBARequest --------------- status code = success --------------- + * ----------------->| PENDING |------------------------>| ESTABLISHED |----- + * --------------- --------------- | + * | / ^ ^ | + * receive ADDBAResponse | receive BlockAck / | | | receive BlockAck + * status code = failure | retryPkts + queuePkts / | | | retryPkts + queuePkts + * v < / | | | >= + * --------------- blockAckThreshold / | | | blockAckThreshold + * | UNSUCCESSFUL | / | | | + * --------------- v | ----------| + * -------------- | + * | INACTIVE | | + * -------------- | + * send a MPDU (Normal Ack) | | + * retryPkts + queuePkts | | + * >= | | + * blockAckThreshold |---------------- + */ + /** + * Represents the state for this agreement. + * + * PENDING: + * If an agreement is in PENDING state it means that an ADDBARequest frame was sent to + * recipient in order to setup the block ack and the originator is waiting for the relative + * ADDBAResponse frame. + * + * ESTABLISHED: + * The block ack is active and all packets relative to this agreement are trasmitted + * with ack policy set to block ack. + * + * INACTIVE: + * In our implementation, block ack tear-down happens only if an inactivity timeout occurs + * so we could have an active block ack but a number of packets that doesn't reach the value of + * m_blockAckThreshold (see ns3::BlocAckManager). In these conditions the agreement becomes + * INACTIVE until that the number of packets reaches the value of m_blockAckThreshold again. + * + * UNSUCCESSFUL (not used for now): + * The agremeent's state becomes UNSUCCESSFUL if: + * + * - its previous state was PENDING and an ADDBAResponse frame wasn't received from + * recipient station within an interval of time defined by m_bAckSetupTimeout attribute + * in ns3::WifiMac. + * - an ADDBAResponse frame is received from recipient and the Status Code field is set + * to failure. + * + * In both cases for station addressed by BlockAckAgreement::m_peer and for + * TID BlockAckAgreement::m_tid block ack mechanism won't be used. + */ + enum State { + PENDING, + ESTABLISHED, + INACTIVE, + UNSUCCESSFUL + }; + void SetState (enum State state); + bool IsPending (void) const; + bool IsEstablished (void) const; + bool IsInactive (void) const; + bool IsUnsuccessful (void) const; + /** + * Notifies a packet's transmission with ack policy Block Ack. + */ + void NotifyMpduTransmission (void); + /** + * Returns true if all packets for which a block ack was negotiated have been transmitted so + * a block ack request is needed in order to acknowledge them. + */ + bool NeedBlockAckRequest (void) const; + void CompleteExchange (void); + +private: + enum State m_state; + uint8_t m_sentMpdus; +}; + +} //namespace ns3 + +#endif /* ORIGINATOR_BLOCK_ACK_AGREEMENT_H */ diff --git a/src/devices/wifi/qadhoc-wifi-mac.cc b/src/devices/wifi/qadhoc-wifi-mac.cc index a2bff3114..be98c41bf 100644 --- a/src/devices/wifi/qadhoc-wifi-mac.cc +++ b/src/devices/wifi/qadhoc-wifi-mac.cc @@ -147,6 +147,18 @@ QadhocWifiMac::SetAckTimeout (Time ackTimeout) m_low->SetAckTimeout (ackTimeout); } +void +QadhocWifiMac::SetBasicBlockAckTimeout (Time blockAckTimeout) +{ + m_low->SetBasicBlockAckTimeout (blockAckTimeout); +} + +void +QadhocWifiMac::SetCompressedBlockAckTimeout (Time blockAckTimeout) +{ + m_low->SetCompressedBlockAckTimeout (blockAckTimeout); +} + void QadhocWifiMac::SetCtsTimeout (Time ctsTimeout) { @@ -183,6 +195,18 @@ QadhocWifiMac::GetAckTimeout (void) const return m_low->GetAckTimeout (); } +Time +QadhocWifiMac::GetBasicBlockAckTimeout (void) const +{ + return m_low->GetBasicBlockAckTimeout (); +} + +Time +QadhocWifiMac::GetCompressedBlockAckTimeout (void) const +{ + return m_low->GetCompressedBlockAckTimeout (); +} + Time QadhocWifiMac::GetCtsTimeout (void) const { @@ -354,7 +378,42 @@ QadhocWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) } else if (hdr->IsMgt ()) { - //Handling action frames + if (hdr->IsAction ()) + { + WifiActionHeader actionHdr; + packet->RemoveHeader (actionHdr); + if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST) + { + MgtAddBaRequestHeader reqHdr; + packet->RemoveHeader (reqHdr); + SendAddBaResponse (&reqHdr, hdr->GetAddr2 ()); + } + else if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST) + { + MgtAddBaResponseHeader respHdr; + packet->RemoveHeader (respHdr); + m_queues[QosUtilsMapTidToAc (respHdr.GetTid ())]->GotAddBaResponse (&respHdr, hdr->GetAddr2 ()); + } + else if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_DELBA) + { + MgtDelBaHeader delBaHdr; + packet->RemoveHeader (delBaHdr); + if (delBaHdr.IsByOriginator ()) + { + /* Delba frame was sent by originator, this means that an ingoing established + agreement exists in MacLow */ + m_low->DestroyBlockAckAgreement (hdr->GetAddr2 (), delBaHdr.GetTid ()); } + else + { + /* We must notify correct queue tear down of agreement */ + AccessClass ac = QosUtilsMapTidToAc (delBaHdr.GetTid ()); + m_queues[ac]->GotDelBaFrame (&delBaHdr, hdr->GetAddr2 ()); + } + } + } } } @@ -402,6 +461,8 @@ QadhocWifiMac::SetQueue (enum AccessClass ac) edca->SetManager (m_dcfManager); edca->SetTypeOfStation (ADHOC_STA); edca->SetTxMiddle (m_txMiddle); + edca->SetAccessClass (ac); + edca->CompleteConfig (); m_queues.insert (std::make_pair(ac, edca)); } @@ -446,4 +507,59 @@ QadhocWifiMac::FinishConfigureStandard (enum WifiPhyStandard standard) } } + +void +QadhocWifiMac::SendAddBaResponse (const MgtAddBaRequestHeader *reqHdr, Mac48Address originator) +{ + NS_LOG_FUNCTION (this); + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetAddr1 (originator); + hdr.SetAddr2 (m_low->GetAddress ()); + hdr.SetAddr3 (m_low->GetAddress ()); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + + MgtAddBaResponseHeader respHdr; + StatusCode code; + code.SetSuccess (); + respHdr.SetStatusCode (code); + //Here a control about queues type? + respHdr.SetAmsduSupport (reqHdr->IsAmsduSupported ()); + + if (reqHdr->IsImmediateBlockAck ()) + { + respHdr.SetImmediateBlockAck (); + } + else + { + respHdr.SetDelayedBlockAck (); + } + respHdr.SetTid (reqHdr->GetTid ()); + /* For now there's not no control about limit of reception. + We assume that receiver has no limit on reception. + However we assume that a receiver sets a bufferSize in order to satisfy + next equation: + (bufferSize + 1) % 16 = 0 + So if a recipient is able to buffer a packet, it should be also able to buffer + all possible packet's fragments. + See section 7.3.1.14 in IEEE802.11e for more details. */ + respHdr.SetBufferSize (1023); + respHdr.SetTimeout (reqHdr->GetTimeout ()); + + WifiActionHeader actionHdr; + WifiActionHeader::ActionValue action; + action.blockAck = WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE; + actionHdr.SetAction (WifiActionHeader::BLOCK_ACK, action); + + Ptr packet = Create (); + packet->AddHeader (respHdr); + packet->AddHeader (actionHdr); + + /* ns3::MacLow have to buffer all correctly received packet for this block ack session */ + m_low->CreateBlockAckAgreement (&respHdr, originator, reqHdr->GetStartingSequence ()); + + //Better a management queue? + m_queues[QosUtilsMapTidToAc (reqHdr->GetTid ())]->PushFront (packet, hdr); +} } //namespace ns3 diff --git a/src/devices/wifi/qadhoc-wifi-mac.h b/src/devices/wifi/qadhoc-wifi-mac.h index bf65b0299..0ebad8a5b 100644 --- a/src/devices/wifi/qadhoc-wifi-mac.h +++ b/src/devices/wifi/qadhoc-wifi-mac.h @@ -38,6 +38,7 @@ class WifiPhy; class DcfManager; class MacLow; class MacRxMiddle; +class MgtAddBaRequestHeader; class QadhocWifiMac : public WifiMac { @@ -73,6 +74,10 @@ public: virtual void SetAddress (Mac48Address address); virtual void SetSsid (Ssid ssid); virtual Mac48Address GetBssid (void) const; + virtual void SetBasicBlockAckTimeout (Time blockAckTimeout); + virtual void SetCompressedBlockAckTimeout (Time blockAckTimeout); + virtual Time GetBasicBlockAckTimeout (void) const; + virtual Time GetCompressedBlockAckTimeout (void) const; private: @@ -82,7 +87,8 @@ private: void ForwardUp (Ptr packet, Mac48Address from, Mac48Address to); QadhocWifiMac &operator = (const QadhocWifiMac &); QadhocWifiMac (const QadhocWifiMac &); - + void SendAddBaResponse (const MgtAddBaRequestHeader *reqHdr, Mac48Address originator); + /** * When an A-MSDU is received, is deaggregated by this method and all extracted packets are * forwarded up. diff --git a/src/devices/wifi/qap-wifi-mac.cc b/src/devices/wifi/qap-wifi-mac.cc index ec9a8d6f1..fbdd69a8f 100644 --- a/src/devices/wifi/qap-wifi-mac.cc +++ b/src/devices/wifi/qap-wifi-mac.cc @@ -197,6 +197,18 @@ QapWifiMac::SetAckTimeout (Time ackTimeout) m_low->SetAckTimeout (ackTimeout); } +void +QapWifiMac::SetBasicBlockAckTimeout (Time blockAckTimeout) +{ + m_low->SetBasicBlockAckTimeout (blockAckTimeout); +} + +void +QapWifiMac::SetCompressedBlockAckTimeout (Time blockAckTimeout) +{ + m_low->SetCompressedBlockAckTimeout (blockAckTimeout); +} + void QapWifiMac::SetCtsTimeout (Time ctsTimeout) { @@ -233,6 +245,18 @@ QapWifiMac::GetAckTimeout (void) const return m_low->GetAckTimeout (); } +Time +QapWifiMac::GetBasicBlockAckTimeout () const +{ + return m_low->GetBasicBlockAckTimeout (); +} + +Time +QapWifiMac::GetCompressedBlockAckTimeout () const +{ + return m_low->GetCompressedBlockAckTimeout (); +} + Time QapWifiMac::GetCtsTimeout (void) const { @@ -673,7 +697,44 @@ QapWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) else if (hdr->IsReassocReq ()) { /* we don't support reassoc frames for now */ - } + } + else if (hdr->IsAction ()) + { + WifiActionHeader actionHdr; + packet->RemoveHeader (actionHdr); + if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST) + { + MgtAddBaRequestHeader reqHdr; + packet->RemoveHeader (reqHdr); + SendAddBaResponse (&reqHdr, hdr->GetAddr2 ()); + } + else if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE) + { + MgtAddBaResponseHeader respHdr; + packet->RemoveHeader (respHdr); + m_queues[QosUtilsMapTidToAc (respHdr.GetTid ())]->GotAddBaResponse (&respHdr, hdr->GetAddr2 ()); + } + else if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_DELBA) + { + MgtDelBaHeader delBaHdr; + packet->RemoveHeader (delBaHdr); + if (delBaHdr.IsByOriginator ()) + { + /* Delba frame was sent by originator, this means that an ingoing established + agreement exists in MacLow */ + m_low->DestroyBlockAckAgreement (hdr->GetAddr2 (), delBaHdr.GetTid ()); + } + else + { + /* We must notify correct queue tear down of agreement */ + AccessClass ac = QosUtilsMapTidToAc (delBaHdr.GetTid ()); + m_queues[ac]->GotDelBaFrame (&delBaHdr, hdr->GetAddr2 ()); + } + } + } else if (hdr->IsAuthentication () || hdr->IsDeauthentication ()) { @@ -744,6 +805,8 @@ QapWifiMac::SetQueue (enum AccessClass ac) edca->SetTxMiddle (m_txMiddle); edca->SetTxOkCallback (MakeCallback (&QapWifiMac::TxOk, this)); edca->SetTxFailedCallback (MakeCallback (&QapWifiMac::TxFailed, this)); + edca->SetAccessClass (ac); + edca->CompleteConfig (); m_queues.insert (std::make_pair(ac, edca)); } @@ -799,4 +862,59 @@ QapWifiMac::DoStart (void) WifiMac::DoStart (); } +void +QapWifiMac::SendAddBaResponse (const MgtAddBaRequestHeader *reqHdr, Mac48Address originator) +{ + NS_LOG_FUNCTION (this); + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetAddr1 (originator); + hdr.SetAddr2 (m_low->GetAddress ()); + hdr.SetAddr3 (m_low->GetAddress ()); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + + MgtAddBaResponseHeader respHdr; + StatusCode code; + code.SetSuccess (); + respHdr.SetStatusCode (code); + //Here a control about queues type? + respHdr.SetAmsduSupport (reqHdr->IsAmsduSupported ()); + + if (reqHdr->IsImmediateBlockAck ()) + { + respHdr.SetImmediateBlockAck (); + } + else + { + respHdr.SetDelayedBlockAck (); + } + respHdr.SetTid (reqHdr->GetTid ()); + /* For now there's not no control about limit of reception. + We assume that receiver has no limit on reception. + However we assume that a receiver sets a bufferSize in order to satisfy + next equation: + (bufferSize + 1) % 16 = 0 + So if a recipient is able to buffer a packet, it should be also able to buffer + all possible packet's fragments. + See section 7.3.1.14 in IEEE802.11e for more details. */ + respHdr.SetBufferSize (1023); + respHdr.SetTimeout (reqHdr->GetTimeout ()); + + WifiActionHeader actionHdr; + WifiActionHeader::ActionValue action; + action.blockAck = WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE; + actionHdr.SetAction (WifiActionHeader::BLOCK_ACK, action); + + Ptr packet = Create (); + packet->AddHeader (respHdr); + packet->AddHeader (actionHdr); + + /* ns3::MacLow have to buffer all correctly received packet for this block ack session */ + m_low->CreateBlockAckAgreement (&respHdr, originator, reqHdr->GetStartingSequence ()); + + //Better a management queue? + m_queues[QosUtilsMapTidToAc (reqHdr->GetTid ())]->PushFront (packet, hdr); +} + } //namespace ns3 diff --git a/src/devices/wifi/qap-wifi-mac.h b/src/devices/wifi/qap-wifi-mac.h index ec1de5e49..55824d0ad 100644 --- a/src/devices/wifi/qap-wifi-mac.h +++ b/src/devices/wifi/qap-wifi-mac.h @@ -47,6 +47,7 @@ class MacTxMiddle; class DcfManager; class AmsduSubframeHeader; class MsduAggregator; +class MgtAddBaRequestHeader; class QapWifiMac : public WifiMac { @@ -81,6 +82,10 @@ public: virtual void SetAddress (Mac48Address address); virtual void SetSsid (Ssid ssid); virtual Mac48Address GetBssid (void) const; + virtual void SetBasicBlockAckTimeout (Time blockAckTimeout); + virtual void SetCompressedBlockAckTimeout (Time blockAckTimeout); + virtual Time GetBasicBlockAckTimeout (void) const; + virtual Time GetCompressedBlockAckTimeout (void) const; void SetBeaconInterval (Time interval); Time GetBeaconInterval (void) const; @@ -103,6 +108,7 @@ private: void TxFailed (const WifiMacHeader& hdr); void SendProbeResp (Mac48Address to); void SendAssocResp (Mac48Address to, bool success); + void SendAddBaResponse (const MgtAddBaRequestHeader *reqHdr, Mac48Address originator); void SendOneBeacon (void); SupportedRates GetSupportedRates (void) const; void SetBeaconGeneration (bool enable); diff --git a/src/devices/wifi/qos-blocked-destinations.cc b/src/devices/wifi/qos-blocked-destinations.cc new file mode 100644 index 000000000..0bc06dac2 --- /dev/null +++ b/src/devices/wifi/qos-blocked-destinations.cc @@ -0,0 +1,66 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005, 2009 INRIA + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#include "qos-blocked-destinations.h" + +namespace ns3 { + +QosBlockedDestinations::QosBlockedDestinations () +{} + +QosBlockedDestinations::~QosBlockedDestinations () +{} + +bool +QosBlockedDestinations::IsBlocked (Mac48Address dest, uint8_t tid) const +{ + for (BlockedPacketsCI i = m_blockedQosPackets.begin (); i != m_blockedQosPackets.end (); i++) + { + if (i->first == dest && i->second == tid) + { + return true; + } + } + return false; +} + +void +QosBlockedDestinations::Block (Mac48Address dest, uint8_t tid) +{ + if (!IsBlocked (dest, tid)) + { + m_blockedQosPackets.push_back (std::make_pair (dest, tid)); + } +} + +void +QosBlockedDestinations::Unblock (Mac48Address dest, uint8_t tid) +{ + for (BlockedPacketsI i = m_blockedQosPackets.begin (); i != m_blockedQosPackets.end (); i++) + { + if (i->first == dest && i->second == tid) + { + m_blockedQosPackets.erase (i); + break; + } + } +} + +} //namespace ns3 diff --git a/src/devices/wifi/qos-blocked-destinations.h b/src/devices/wifi/qos-blocked-destinations.h new file mode 100644 index 000000000..35b2cf45f --- /dev/null +++ b/src/devices/wifi/qos-blocked-destinations.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005, 2009 INRIA + * Copyright (c) 2009 MIRKO BANCHI + * + * 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: Mirko Banchi + */ +#ifndef QOS_BLOCKED_DESTINATIONS_H +#define QOS_BLOCKED_DESTINATIONS_H + +#include +#include "ns3/mac48-address.h" + +namespace ns3 { + +class QosBlockedDestinations +{ +public: + QosBlockedDestinations (); + ~QosBlockedDestinations (); + + void Block (Mac48Address dest, uint8_t tid); + void Unblock (Mac48Address dest, uint8_t tid); + bool IsBlocked (Mac48Address dest, uint8_t tid) const; + +private: + typedef std::list > BlockedPackets; + typedef std::list >::iterator BlockedPacketsI; + typedef std::list >::const_iterator BlockedPacketsCI; + BlockedPackets m_blockedQosPackets; +}; + +} //namespace ns3 + +#endif /* QOS_BLOCKED_DESTINATIONS_H */ diff --git a/src/devices/wifi/qos-utils.cc b/src/devices/wifi/qos-utils.cc index bb79ff613..c806a95eb 100644 --- a/src/devices/wifi/qos-utils.cc +++ b/src/devices/wifi/qos-utils.cc @@ -16,6 +16,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mirko Banchi + * Author: Cecchi Niccolò */ #include "qos-utils.h" #include "qos-tag.h" @@ -69,4 +70,16 @@ QosUtilsGetTidForPacket (Ptr packet) return tid; } +uint32_t +QosUtilsMapSeqControlToUniqueInteger (uint16_t seqControl, uint16_t endSequence) +{ + uint32_t integer = 0; + uint16_t numberSeq = (seqControl>>4) & 0x0fff; + integer = (4096 - (endSequence + 1) + numberSeq) % 4096; + integer *= 16; + integer += (seqControl & 0x000f); + return integer; +} + + } //namespace ns3 diff --git a/src/devices/wifi/qos-utils.h b/src/devices/wifi/qos-utils.h index 184759ed4..197ef37bd 100644 --- a/src/devices/wifi/qos-utils.h +++ b/src/devices/wifi/qos-utils.h @@ -45,6 +45,13 @@ AccessClass QosUtilsMapTidToAc (uint8_t tid); */ uint8_t QosUtilsGetTidForPacket (Ptr packet); +/* + * Next function is useful to correctly sort buffered packets under block ack. + * When an BAR is received from originator station, completed "old" + * (see section 9.10.3 in IEEE802.11e) packets must be forwarded up before "new" packets. + */ +uint32_t QosUtilsMapSeqControlToUniqueInteger (uint16_t seqControl, uint16_t endSequence); + } //namespace ns3 #endif /* QOS_UTILS_H */ diff --git a/src/devices/wifi/qsta-wifi-mac.cc b/src/devices/wifi/qsta-wifi-mac.cc index 1df061ed5..24b2a22b5 100644 --- a/src/devices/wifi/qsta-wifi-mac.cc +++ b/src/devices/wifi/qsta-wifi-mac.cc @@ -169,6 +169,18 @@ QstaWifiMac::SetAckTimeout (Time ackTimeout) m_low->SetAckTimeout (ackTimeout); } +void +QstaWifiMac::SetBasicBlockAckTimeout (Time blockAckTimeout) +{ + m_low->SetBasicBlockAckTimeout (blockAckTimeout); +} + +void +QstaWifiMac::SetCompressedBlockAckTimeout (Time blockAckTimeout) +{ + m_low->SetCompressedBlockAckTimeout (blockAckTimeout); +} + void QstaWifiMac::SetCtsTimeout (Time ctsTimeout) { @@ -205,6 +217,18 @@ QstaWifiMac::GetAckTimeout (void) const return m_low->GetAckTimeout (); } +Time +QstaWifiMac::GetBasicBlockAckTimeout (void) const +{ + return m_low->GetBasicBlockAckTimeout (); +} + +Time +QstaWifiMac::GetCompressedBlockAckTimeout (void) const +{ + return m_low->GetCompressedBlockAckTimeout (); +} + Time QstaWifiMac::GetCtsTimeout (void) const { @@ -674,6 +698,43 @@ QstaWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) } } } + else if (hdr->IsAction ()) + { + WifiActionHeader actionHdr; + packet->RemoveHeader (actionHdr); + if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST) + { + MgtAddBaRequestHeader reqHdr; + packet->RemoveHeader (reqHdr); + SendAddBaResponse (&reqHdr, hdr->GetAddr2 ()); + } + else if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE) + { + MgtAddBaResponseHeader respHdr; + packet->RemoveHeader (respHdr); + m_queues[QosUtilsMapTidToAc (respHdr.GetTid ())]->GotAddBaResponse (&respHdr, hdr->GetAddr2 ()); + } + else if (actionHdr.GetCategory () == WifiActionHeader::BLOCK_ACK && + actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_DELBA) + { + MgtDelBaHeader delBaHdr; + packet->RemoveHeader (delBaHdr); + if (delBaHdr.IsByOriginator ()) + { + /* Delba frame was sent by originator, this means that an ingoing established + agreement exists in MacLow */ + m_low->DestroyBlockAckAgreement (hdr->GetAddr2 (), delBaHdr.GetTid ()); + } + else + { + /* We must notify correct queue tear down of agreement */ + AccessClass ac = QosUtilsMapTidToAc (delBaHdr.GetTid ()); + m_queues[ac]->GotDelBaFrame (&delBaHdr, hdr->GetAddr2 ()); + } + } + } } SupportedRates @@ -731,6 +792,8 @@ QstaWifiMac::SetQueue (enum AccessClass ac) edca->SetManager (m_dcfManager); edca->SetTypeOfStation (STA); edca->SetTxMiddle (m_txMiddle); + edca->SetAccessClass (ac); + edca->CompleteConfig (); m_queues.insert (std::make_pair(ac, edca)); } @@ -775,5 +838,59 @@ QstaWifiMac::FinishConfigureStandard (enum WifiPhyStandard standard) } } +void +QstaWifiMac::SendAddBaResponse (const MgtAddBaRequestHeader *reqHdr, Mac48Address originator) +{ + NS_LOG_FUNCTION (this); + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetAddr1 (originator); + hdr.SetAddr2 (m_low->GetAddress ()); + hdr.SetAddr3 (m_low->GetAddress ()); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + + MgtAddBaResponseHeader respHdr; + StatusCode code; + code.SetSuccess (); + respHdr.SetStatusCode (code); + //Here a control about queues type? + respHdr.SetAmsduSupport (reqHdr->IsAmsduSupported ()); + + if (reqHdr->IsImmediateBlockAck ()) + { + respHdr.SetImmediateBlockAck (); + } + else + { + respHdr.SetDelayedBlockAck (); + } + respHdr.SetTid (reqHdr->GetTid ()); + /* For now there's not no control about limit of reception. + We assume that receiver has no limit on reception. + However we assume that a receiver sets a bufferSize in order to satisfy + next equation: + (bufferSize + 1) % 16 = 0 + So if a recipient is able to buffer a packet, it should be also able to buffer + all possible packet's fragments. + See section 7.3.1.14 in IEEE802.11e for more details. */ + respHdr.SetBufferSize (1023); + respHdr.SetTimeout (reqHdr->GetTimeout ()); + + WifiActionHeader actionHdr; + WifiActionHeader::ActionValue action; + action.blockAck = WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE; + actionHdr.SetAction (WifiActionHeader::BLOCK_ACK, action); + + Ptr packet = Create (); + packet->AddHeader (respHdr); + packet->AddHeader (actionHdr); + + /* ns3::MacLow have to buffer all correctly received packet for this block ack session */ + m_low->CreateBlockAckAgreement (&respHdr, originator, reqHdr->GetStartingSequence ()); + + //Better a management queue? + m_queues[QosUtilsMapTidToAc (reqHdr->GetTid ())]->PushFront (packet, hdr); +} } //namespace ns3 diff --git a/src/devices/wifi/qsta-wifi-mac.h b/src/devices/wifi/qsta-wifi-mac.h index cc151f35b..4e30b4deb 100644 --- a/src/devices/wifi/qsta-wifi-mac.h +++ b/src/devices/wifi/qsta-wifi-mac.h @@ -45,6 +45,7 @@ class MacLow; class WifiMacHeader; class AmsduSubframeHeader; class MsduAggregator; +class MgtAddBaRequestHeader; class QstaWifiMac : public WifiMac { @@ -80,6 +81,10 @@ public: virtual void SetAddress (Mac48Address address); virtual void SetSsid (Ssid ssid); virtual Mac48Address GetBssid (void) const; + virtual void SetBasicBlockAckTimeout (Time blockAckTimeout); + virtual void SetCompressedBlockAckTimeout (Time blockAckTimeout); + virtual Time GetBasicBlockAckTimeout (void) const; + virtual Time GetCompressedBlockAckTimeout (void) const; void SetMaxMissedBeacons (uint32_t missed); void SetProbeRequestTimeout (Time timeout); @@ -100,6 +105,7 @@ private: void ProbeRequestTimeout (void); void SendAssociationRequest (void); void SendProbeRequest (void); + void SendAddBaResponse (const MgtAddBaRequestHeader *reqHdr, Mac48Address originator); void TryToEnsureAssociated (void); bool IsAssociated (void) const; bool IsWaitAssocResp (void) const; diff --git a/src/devices/wifi/wifi-channel.cc b/src/devices/wifi/wifi-channel.cc index a13149a79..cc78c3273 100644 --- a/src/devices/wifi/wifi-channel.cc +++ b/src/devices/wifi/wifi-channel.cc @@ -28,8 +28,8 @@ #include "wifi-channel.h" #include "wifi-net-device.h" #include "yans-wifi-phy.h" -#include "propagation-loss-model.h" -#include "propagation-delay-model.h" +#include "ns3/propagation-loss-model.h" +#include "ns3/propagation-delay-model.h" NS_LOG_COMPONENT_DEFINE ("WifiChannel"); diff --git a/src/devices/wifi/wifi-mac-header.cc b/src/devices/wifi/wifi-mac-header.cc index fa62be888..39a2a0363 100644 --- a/src/devices/wifi/wifi-mac-header.cc +++ b/src/devices/wifi/wifi-mac-header.cc @@ -122,6 +122,19 @@ WifiMacHeader::SetBeacon (void) m_ctrlType = TYPE_MGT; m_ctrlSubtype = 8; } +void +WifiMacHeader::SetBlockAckReq (void) +{ + m_ctrlType = TYPE_CTL; + m_ctrlSubtype = 8; +} +void +WifiMacHeader::SetBlockAck (void) +{ + m_ctrlType = TYPE_CTL; + m_ctrlSubtype = 9; +} + void WifiMacHeader::SetTypeData (void) { @@ -348,6 +361,21 @@ void WifiMacHeader::SetQosAckPolicy (enum QosAckPolicy policy) break; } } +void +WifiMacHeader::SetQosNormalAck () +{ + m_qosAckPolicy = 0; +} +void +WifiMacHeader::SetQosBlockAck () +{ + m_qosAckPolicy = 3; +} +void +WifiMacHeader::SetQosNoAck () +{ + m_qosAckPolicy = 1; +} void WifiMacHeader::SetQosAmsdu (void) { m_amsduPresent = 1; @@ -630,6 +658,16 @@ WifiMacHeader::IsMultihopAction (void) const { return (GetType () == WIFI_MAC_MGT_MULTIHOP_ACTION); } +bool +WifiMacHeader::IsBlockAckReq (void) const +{ + return (GetType () == WIFI_MAC_CTL_BACKREQ)?true:false; +} +bool +WifiMacHeader::IsBlockAck (void) const +{ + return (GetType () == WIFI_MAC_CTL_BACKRESP)?true:false; +} uint16_t @@ -808,8 +846,7 @@ WifiMacHeader::GetSize (void) const break; case SUBTYPE_CTL_BACKREQ: case SUBTYPE_CTL_BACKRESP: - // NOT IMPLEMENTED - NS_ASSERT (false); + size = 2+2+6+6; break; } break; @@ -1018,8 +1055,7 @@ WifiMacHeader::Serialize (Buffer::Iterator i) const break; case SUBTYPE_CTL_BACKREQ: case SUBTYPE_CTL_BACKRESP: - // NOT IMPLEMENTED - NS_ASSERT (false); + WriteTo (i, m_addr2); break; default: //NOTREACHED @@ -1068,8 +1104,7 @@ WifiMacHeader::Deserialize (Buffer::Iterator start) break; case SUBTYPE_CTL_BACKREQ: case SUBTYPE_CTL_BACKRESP: - // NOT IMPLEMENTED - NS_ASSERT (false); + ReadFrom (i, m_addr2); break; } break; diff --git a/src/devices/wifi/wifi-mac-header.h b/src/devices/wifi/wifi-mac-header.h index 5984e586b..37ad2cf2c 100644 --- a/src/devices/wifi/wifi-mac-header.h +++ b/src/devices/wifi/wifi-mac-header.h @@ -102,6 +102,8 @@ public: void SetBeacon (void); void SetTypeData (void); void SetAction (); + void SetBlockAckReq (void); + void SetBlockAck (void); void SetMultihopAction(); void SetDsFrom (void); void SetDsNotFrom (void); @@ -125,6 +127,9 @@ public: void SetQosEosp (); void SetQosNoEosp (); void SetQosAckPolicy (enum QosAckPolicy); + void SetQosNormalAck (void); + void SetQosBlockAck (void); + void SetQosNoAck (void); void SetQosAmsdu (void); void SetQosNoAmsdu (void); void SetQosTxopLimit (uint8_t txop); @@ -145,6 +150,8 @@ public: bool IsRts (void) const; bool IsCts (void) const; bool IsAck (void) const; + bool IsBlockAckReq (void) const; + bool IsBlockAck (void) const; bool IsAssocReq (void) const; bool IsAssocResp (void) const; bool IsReassocReq (void) const; diff --git a/src/devices/wifi/wifi-mac-queue.cc b/src/devices/wifi/wifi-mac-queue.cc index 731e17398..95073b103 100644 --- a/src/devices/wifi/wifi-mac-queue.cc +++ b/src/devices/wifi/wifi-mac-queue.cc @@ -24,6 +24,7 @@ #include "ns3/uinteger.h" #include "wifi-mac-queue.h" +#include "qos-blocked-destinations.h" using namespace std; @@ -111,18 +112,19 @@ WifiMacQueue::Cleanup (void) Time now = Simulator::Now (); uint32_t n = 0; - PacketQueueI end = m_queue.begin (); - for (PacketQueueI i = m_queue.begin (); i != m_queue.end (); i++) + for (PacketQueueI i = m_queue.begin (); i != m_queue.end ();) { if (i->tstamp + m_maxDelay > now) { - end = i; - break; + i++; + } + else + { + i = m_queue.erase (i); + n++; } - n++; } m_size -= n; - m_queue.erase (m_queue.begin (), end); } Ptr @@ -260,4 +262,81 @@ WifiMacQueue::Remove (Ptr packet) return false; } +void +WifiMacQueue::PushFront (Ptr packet, const WifiMacHeader &hdr) +{ + Cleanup (); + if (m_size == m_maxSize) + { + return; + } + Time now = Simulator::Now (); + m_queue.push_front (Item (packet, hdr, now)); + m_size++; +} + +uint32_t +WifiMacQueue::GetNPacketsByTidAndAddress (uint8_t tid, WifiMacHeader::AddressType type, + Mac48Address addr) +{ + Cleanup (); + uint32_t nPackets = 0; + if (!m_queue.empty ()) + { + PacketQueueI it; + NS_ASSERT (type <= 4); + for (it = m_queue.begin (); it != m_queue.end (); it++) + { + if (GetAddressForPacket (type, it) == addr) + { + if (it->hdr.IsQosData () && it->hdr.GetQosTid () == tid) + { + nPackets++; + } + } + } + } + return nPackets; +} + +Ptr +WifiMacQueue::DequeueFirstAvailable (WifiMacHeader *hdr, Time ×tamp, + const QosBlockedDestinations *blockedPackets) +{ + Cleanup (); + Ptr packet = 0; + for (PacketQueueI it = m_queue.begin (); it != m_queue.end (); it++) + { + if (!it->hdr.IsQosData () || + !blockedPackets->IsBlocked (it->hdr.GetAddr1 (), it->hdr.GetQosTid ())) + { + *hdr = it->hdr; + timestamp = it->tstamp; + packet = it->packet; + m_queue.erase (it); + m_size--; + return packet; + } + } + return packet; +} + +Ptr +WifiMacQueue::PeekFirstAvailable (WifiMacHeader *hdr, Time ×tamp, + const QosBlockedDestinations *blockedPackets) +{ + Cleanup (); + for (PacketQueueI it = m_queue.begin (); it != m_queue.end (); it++) + { + if (!it->hdr.IsQosData () || + !blockedPackets->IsBlocked (it->hdr.GetAddr1 (), it->hdr.GetQosTid ())) + { + *hdr = it->hdr; + timestamp = it->tstamp; + return it->packet; + } + } + return 0; +} + } // namespace ns3 diff --git a/src/devices/wifi/wifi-mac-queue.h b/src/devices/wifi/wifi-mac-queue.h index 10b3c1eeb..fb1cad8c6 100644 --- a/src/devices/wifi/wifi-mac-queue.h +++ b/src/devices/wifi/wifi-mac-queue.h @@ -32,6 +32,7 @@ namespace ns3 { class WifiMacParameters; +class QosBlockedDestinations; /** * \brief a 802.11e-specific queue. @@ -61,6 +62,7 @@ public: Time GetMaxDelay (void) const; void Enqueue (Ptr packet, const WifiMacHeader &hdr); + void PushFront (Ptr packet, const WifiMacHeader &hdr); Ptr Dequeue (WifiMacHeader *hdr); Ptr Peek (WifiMacHeader *hdr); /** @@ -91,12 +93,33 @@ public: * performed in linear time (O(n)). */ bool Remove (Ptr packet); - + /** + * Returns number of QoS packets having tid equals to tid and address + * specified by type equals to addr. + */ + uint32_t GetNPacketsByTidAndAddress (uint8_t tid, + WifiMacHeader::AddressType type, + Mac48Address addr); + /** + * Returns first available packet for transmission. A packet could be no available + * if it's a QoS packet with a tid and an address1 fields equal to tid and addr + * respectively that index a pending agreement in the BlockAckManager object. + * So that packet must not be transmitted until reception of an ADDBA response frame from station + * addressed by addr. This method removes the packet from queue. + */ + Ptr DequeueFirstAvailable (WifiMacHeader *hdr, + Time &tStamp, + const QosBlockedDestinations *blockedPackets); + /** + * Returns first available packet for transmission. The packet isn't removed from queue. + */ + Ptr PeekFirstAvailable (WifiMacHeader *hdr, + Time &tStamp, + const QosBlockedDestinations *blockedPackets); void Flush (void); bool IsEmpty (void); uint32_t GetSize (void); - private: struct Item; diff --git a/src/devices/wifi/wifi-mac.cc b/src/devices/wifi/wifi-mac.cc index 629fb5a6d..5b88f9eaa 100644 --- a/src/devices/wifi/wifi-mac.cc +++ b/src/devices/wifi/wifi-mac.cc @@ -69,6 +69,63 @@ WifiMac::GetDefaultCtsAckTimeout (void) return ctsTimeout; } +Time +WifiMac::GetDefaultBasicBlockAckDelay (void) +{ + // This value must be rivisited + return MicroSeconds (250); +} +Time +WifiMac::GetDefaultCompressedBlockAckDelay (void) +{ + // This value must be rivisited + return MicroSeconds (68); +} +Time +WifiMac::GetDefaultBasicBlockAckTimeout (void) +{ + Time blockAckTimeout = GetDefaultSifs (); + blockAckTimeout += GetDefaultBasicBlockAckDelay (); + blockAckTimeout += MicroSeconds (GetDefaultMaxPropagationDelay ().GetMicroSeconds () * 2); + blockAckTimeout += GetDefaultSlot (); + return blockAckTimeout; +} +Time +WifiMac::GetDefaultCompressedBlockAckTimeout (void) +{ + Time blockAckTimeout = GetDefaultSifs (); + blockAckTimeout += GetDefaultCompressedBlockAckDelay (); + blockAckTimeout += MicroSeconds (GetDefaultMaxPropagationDelay ().GetMicroSeconds () * 2); + blockAckTimeout += GetDefaultSlot (); + return blockAckTimeout; +} + +void +WifiMac::SetBasicBlockAckTimeout (Time blockAckTimeout) +{ + //this method must be implemented by QoS WifiMacs +} + +Time +WifiMac::GetBasicBlockAckTimeout (void) const +{ + //this method must be implemented by QoS WifiMacs + return MicroSeconds (0); +} + +void +WifiMac::SetCompressedBlockAckTimeout (Time blockAckTimeout) +{ + //this methos must be implemented by QoS WifiMacs +} + +Time +WifiMac::GetCompressedBlockAckTimeout (void) const +{ + //this method must be implemented by QoS WifiMacs + return MicroSeconds (0); +} + TypeId WifiMac::GetTypeId (void) { @@ -84,6 +141,16 @@ WifiMac::GetTypeId (void) MakeTimeAccessor (&WifiMac::GetAckTimeout, &WifiMac::SetAckTimeout), MakeTimeChecker ()) + .AddAttribute ("BasicBlockAckTimeout", "When this timeout expires, the BASIC_BLOCK_ACK_REQ/BASIC_BLOCK_ACK handshake has failed.", + TimeValue (GetDefaultBasicBlockAckTimeout ()), + MakeTimeAccessor (&WifiMac::GetBasicBlockAckTimeout, + &WifiMac::SetBasicBlockAckTimeout), + MakeTimeChecker ()) + .AddAttribute ("CompressedBlockAckTimeout", "When this timeout expires, the COMPRESSED_BLOCK_ACK_REQ/COMPRESSED_BLOCK_ACK handshake has failed.", + TimeValue (GetDefaultCompressedBlockAckTimeout ()), + MakeTimeAccessor (&WifiMac::GetCompressedBlockAckTimeout, + &WifiMac::SetCompressedBlockAckTimeout), + MakeTimeChecker ()) .AddAttribute ("Sifs", "The value of the SIFS constant.", TimeValue (GetDefaultSifs ()), MakeTimeAccessor (&WifiMac::SetSifs, diff --git a/src/devices/wifi/wifi-mac.h b/src/devices/wifi/wifi-mac.h index eb5f0841a..73d42dc86 100644 --- a/src/devices/wifi/wifi-mac.h +++ b/src/devices/wifi/wifi-mac.h @@ -179,6 +179,13 @@ public: * \param linkDown the callback to invoke when the link becomes down. */ virtual void SetLinkDownCallback (Callback linkDown) = 0; + /* Next functions are not pure vitual so non Qos WifiMacs are not + * forced to implement them. + */ + virtual void SetBasicBlockAckTimeout (Time blockAckTimeout); + virtual Time GetBasicBlockAckTimeout (void) const; + virtual void SetCompressedBlockAckTimeout (Time blockAckTimeout); + virtual Time GetCompressedBlockAckTimeout (void) const; /** * Public method used to fire a MacTx trace. Implemented for encapsulation @@ -224,6 +231,10 @@ private: static Time GetDefaultEifsNoDifs (void); static Time GetDefaultCtsAckDelay (void); static Time GetDefaultCtsAckTimeout (void); + static Time GetDefaultBasicBlockAckDelay (void); + static Time GetDefaultBasicBlockAckTimeout (void); + static Time GetDefaultCompressedBlockAckDelay (void); + static Time GetDefaultCompressedBlockAckTimeout (void); /** * \param standard the phy standard to be used * diff --git a/src/devices/wifi/wifi-phy-test.cc b/src/devices/wifi/wifi-phy-test.cc index 500879d85..25d580df7 100644 --- a/src/devices/wifi/wifi-phy-test.cc +++ b/src/devices/wifi/wifi-phy-test.cc @@ -20,8 +20,8 @@ #include "wifi-net-device.h" #include "yans-wifi-channel.h" #include "yans-wifi-phy.h" -#include "propagation-loss-model.h" -#include "propagation-delay-model.h" +#include "ns3/propagation-loss-model.h" +#include "ns3/propagation-delay-model.h" #include "error-rate-model.h" #include "yans-error-rate-model.h" #include "ns3/ptr.h" diff --git a/src/devices/wifi/wifi-test.cc b/src/devices/wifi/wifi-test.cc index df619e5cf..c60f72851 100644 --- a/src/devices/wifi/wifi-test.cc +++ b/src/devices/wifi/wifi-test.cc @@ -23,8 +23,8 @@ #include "adhoc-wifi-mac.h" #include "yans-wifi-phy.h" #include "arf-wifi-manager.h" -#include "propagation-delay-model.h" -#include "propagation-loss-model.h" +#include "ns3/propagation-delay-model.h" +#include "ns3/propagation-loss-model.h" #include "error-rate-model.h" #include "yans-error-rate-model.h" #include "ns3/constant-position-mobility-model.h" diff --git a/src/devices/wifi/wscript b/src/devices/wifi/wscript index 63385c236..5fac2d8eb 100644 --- a/src/devices/wifi/wscript +++ b/src/devices/wifi/wscript @@ -3,10 +3,6 @@ def build(bld): obj = bld.create_ns3_module('wifi', ['node']) obj.source = [ - 'propagation-delay-model.cc', - 'propagation-loss-model.cc', - 'propagation-loss-model-test-suite.cc', - 'jakes-propagation-loss-model.cc', 'wifi-channel.cc', 'wifi-mode.cc', 'ssid.cc', @@ -58,14 +54,17 @@ def build(bld): 'amsdu-subframe-header.cc', 'msdu-standard-aggregator.cc', 'minstrel-wifi-manager.cc', + 'originator-block-ack-agreement.cc', 'dcf.cc', + 'ctrl-headers.cc', + 'qos-blocked-destinations.cc', + 'block-ack-agreement.cc', + 'block-ack-manager.cc', + 'block-ack-test-suite.cc', ] headers = bld.new_task_gen('ns3header') headers.module = 'wifi' headers.source = [ - 'propagation-delay-model.h', - 'propagation-loss-model.h', - 'jakes-propagation-loss-model.h', 'wifi-net-device.h', 'wifi-channel.h', 'wifi-mode.h', @@ -109,7 +108,11 @@ def build(bld): 'mac-rx-middle.h', 'mac-low.h', 'minstrel-wifi-manager.h', + 'originator-block-ack-agreement.h', 'dcf.h', + 'ctrl-headers.h', + 'block-ack-agreement.h', + 'block-ack-manager.h', ] if bld.env['ENABLE_GSL']: diff --git a/src/devices/wifi/yans-wifi-channel.cc b/src/devices/wifi/yans-wifi-channel.cc index 05385410e..b4738037c 100644 --- a/src/devices/wifi/yans-wifi-channel.cc +++ b/src/devices/wifi/yans-wifi-channel.cc @@ -27,8 +27,8 @@ #include "ns3/object-factory.h" #include "yans-wifi-channel.h" #include "yans-wifi-phy.h" -#include "propagation-loss-model.h" -#include "propagation-delay-model.h" +#include "ns3/propagation-loss-model.h" +#include "ns3/propagation-delay-model.h" NS_LOG_COMPONENT_DEFINE ("YansWifiChannel"); diff --git a/src/routing/olsr/olsr-routing-protocol.cc b/src/routing/olsr/olsr-routing-protocol.cc index 45d9e91ce..8bc67e870 100644 --- a/src/routing/olsr/olsr-routing-protocol.cc +++ b/src/routing/olsr/olsr-routing-protocol.cc @@ -2610,8 +2610,6 @@ Ptr RoutingProtocol::RouteOutput (Ptr p, const Ipv4Header &header, Ptr oif, Socket::SocketErrno &sockerr) { NS_LOG_FUNCTION (this << " " << m_ipv4->GetObject ()->GetId() << " " << header.GetDestination () << " " << oif); - // TBD: oif is unused; can be used to restrict the outgoing interface - // of the found route if application bound to a source interface Ptr rtentry; RoutingTableEntry entry1, entry2; if (Lookup (header.GetDestination (), entry1) != 0) @@ -2621,9 +2619,22 @@ RoutingProtocol::RouteOutput (Ptr p, const Ipv4Header &header, PtrGetInterfaceForDevice (oif) != static_cast (interfaceIdx)) + { + // We do not attempt to perform a constrained routing search + // if the caller specifies the oif; we just enforce that + // that the found route matches the requested outbound interface + NS_LOG_DEBUG ("Olsr node " << m_mainAddress + << ": RouteOutput for dest=" << header.GetDestination () + << " Route interface " << interfaceIdx + << " does not match requested output interface " + << m_ipv4->GetInterfaceForDevice (oif)); + sockerr = Socket::ERROR_NOROUTETOHOST; + return rtentry; + } rtentry = Create (); rtentry->SetDestination (header.GetDestination ()); - uint32_t interfaceIdx = entry2.interface; // the source address is the interface address that matches // the destination address (when multiple are present on the // outgoing interface, one is selected via scoping rules) diff --git a/utils/utils.h b/utils/utils.h new file mode 100644 index 000000000..0831973ed --- /dev/null +++ b/utils/utils.h @@ -0,0 +1,80 @@ +/** + * \ingroup utils + * \defgroup CheckStyle check-style.py + * + * The check-style.py script will test and reformat code according to the + * ns-3 coding style posted at http://www.nsnam.org/codingstyle.html + * It requires that you install 'uncrustify' + * + * It has multiple levels of conformance: + * - level=0: the default: merely checks indentation + * - level=1: checks also for missing spaces before parentheses + * - level=2: checks also for missing newlines and braces around single-line statements + * - level=3: checks also for missing trailing whitespaces + * + * Examples: + * + * check a single file (level 0 by default): +\verbatim +./check-style.py -f src/core/object.h +\endverbatim + * + * fix the style of a single file: +\verbatim +./check-style.py --level=2 --in-place -f src/core/object.h +\endverbatim + * + * look at the changes needed for a single file: +\verbatim +./check-style.py --diff --level=1 -f src/core/object.h | less +\endverbatim + * + * look at the status of all files modified in your mercurial repository: +\verbatim +./check-style.py --check-hg +\endverbatim + * + * look at the changes needed for all modified files in your mercurial + * repository: +\verbatim +./check-style.py --check-hg --diff |less +\endverbatim + * + * Enable this script to run as a 'commit' hook in your repository and + * disallow commits which contain files with invalid style: + * +\verbatim +cat hgrc (can be appended to .hg/hgrc or ~/.hg/hgrc or /etc/hg/hgrc +[hooks] +# uncomment below line to enable: works only with mercurial >= 1.3 +#pretxncommit.indent = path-to-binary/check-indent.py --check-hg-hook +# uncomment below line to enable: works with all (?) versions +# of mercurial but requires that PYTHONPATH is defined to point to +# the directory which contains check-indent.py +#pretxncommit.indent = python:check-indent.run_as_hg_hook +\endverbatim + * + * Usage: +\verbatim +Usage: check-style.py [options] + +Options: + -h, --help show this help message and exit + --debug Output some debugging information + -l LEVEL, --level=LEVEL + Level of style conformance: higher levels include all + lower levels. level=0: re-indent only. level=1: add + extra spaces. level=2: insert extra newlines and extra + braces around single-line statements. level=3: remove + all trailing spaces + --check-hg-hook Get the list of files to check from mercurial's list + of modified and added files and assume that the script + runs as a pretxncommit mercurial hook + --check-hg Get the list of files to check from mercurial's list + of modified and added files + -f FILE, --check-file=FILE + Check a single file + --diff Generate a diff on stdout of the indented files + -i, --in-place Indent the input files in-place +\endverbatim + */