examples/wireless/multirate.cc
author Dean Armstrong <deanarm@gmail.com>
Thu, 02 Dec 2010 07:51:34 +0000
changeset 6673 ec22aa763e2d
parent 6648 d1785ce489c5
child 6674 52f8688d6d01
permissions -rw-r--r--
Bug 978: Consolidate Wi-Fi MAC high functionality This change reorganises the Wi-Fi MAC high classes in attempt to reduce duplication of functionality that is required for more than one of the MAC high models. A new class called RegularWifiMac has been created. This derives from the abstract WifiMac, and is parent of AdhocWifiMac, StaWifiMac, ApWifiMac, and MeshWifiInterfaceMac. The QoS and non-QoS class variants are no longer, with a RegularWifiMac attribute "QosSupported" allowing selection between these two modes of operation. QosWifiMacHelper and NqosWifiMacHelper continue to work as previously. Updates to some regression traces are necessary because the reorganisation has led to random number streams being initialised in slightly different orders and thus over-the-air timing changing.

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * 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: Duy Nguyen <duy@soe.ucsc.edu>
 */

/**
 * Scenarios: 100 nodes, multiple simultaneous flows, multi-hop ad hoc, routing,
 * and mobility
 *
 * QUICK INSTRUCTIONS:
 *
 * To optimize build: 
 * ./waf -d optimized configure
 * ./waf
 *
 * To compile:
 * ./waf --run multirate
 *
 * To compile with command line(useful for varying parameters):
 * ./waf --run "multirate --totalTime=0.3s --rateManager=ns3::MinstrelWifiManager"
 *
 * To turn on NS_LOG:
 * export NS_LOG=multirate=level_all
 * (can only view log if built with ./waf -d debug configure)
 *
 * To debug:
 * ./waf --shell
 * gdb ./build/debug/examples/wireless/multirate
 *
 * To view pcap files:
 * tcpdump -nn -tt -r filename.pcap
 *
 * To monitor the files:
 * tail -f filename.pcap
 *
 */

#include "ns3/core-module.h"
#include "ns3/common-module.h"
#include "ns3/node-module.h"
#include "ns3/helper-module.h"
#include "ns3/mobility-module.h"
#include "ns3/contrib-module.h"
#include "ns3/random-variable.h"
#include "ns3/wifi-module.h"
#include "ns3/flow-monitor-helper.h"
#include "ns3/olsr-helper.h"
#include "ns3/ipv4-static-routing-helper.h"
#include "ns3/ipv4-list-routing-helper.h"

#include <iostream>
#include <fstream>

NS_LOG_COMPONENT_DEFINE ("multirate");

using namespace ns3;

class Experiment
{
public:

  Experiment ();
  Experiment (std::string name);
  Gnuplot2dDataset Run (const WifiHelper &wifi, const YansWifiPhyHelper &wifiPhy,
                        const NqosWifiMacHelper &wifiMac, const YansWifiChannelHelper &wifiChannel, const MobilityHelper &mobility);

  bool CommandSetup (int argc, char **argv);
  bool IsRouting () { return (enableRouting == 1) ? 1:0; }
  bool IsMobility () { return (enableMobility == 1) ? 1:0; }

  uint32_t GetScenario () {return scenario; }

  std::string GetRtsThreshold () { return rtsThreshold; }
  std::string GetOutputFileName () { return outputFileName; }
  std::string GetRateManager () { return rateManager; }
  
private:

  Vector GetPosition (Ptr<Node> node);
  Ptr<Socket> SetupPacketReceive (Ptr<Node> node);
  NodeContainer GenerateNeighbors(NodeContainer c, uint32_t senderId);

  void ApplicationSetup (Ptr<Node> client, Ptr<Node> server, double start, double stop);
  void AssignNeighbors (NodeContainer c);
  void SelectSrcDest (NodeContainer c);
  void ReceivePacket (Ptr<Socket> socket);
  void CheckThroughput ();
  void SendMultiDestinations (Ptr<Node> sender, NodeContainer c);

  Gnuplot2dDataset m_output;

  double totalTime; 
  double expMean;

  uint32_t bytesTotal;
  uint32_t packetSize;
  uint32_t gridSize; 
  uint32_t nodeDistance;
  uint32_t port;
  uint32_t scenario;

  bool enablePcap;
  bool enableTracing;
  bool enableFlowMon;
  bool enableRouting;
  bool enableMobility;

  NodeContainer containerA, containerB, containerC, containerD; 
  std::string rtsThreshold, rateManager, outputFileName;
};

Experiment::Experiment ()
{}

Experiment::Experiment (std::string name) : 
  m_output (name),
  totalTime (0.3), 
  expMean (0.1), //flows being exponentially distributed
  bytesTotal(0),
  packetSize (2000),
  gridSize (10), //10x10 grid  for a total of 100 nodes
  nodeDistance (30),
  port (5000),
  scenario (4), 
  enablePcap (false), 
  enableTracing (true),
  enableFlowMon (false),
  enableRouting (false),
  enableMobility (false),
  rtsThreshold ("2200"), //0 for enabling rts/cts
  rateManager ("ns3::MinstrelWifiManager"),
  outputFileName ("minstrel")
{
  m_output.SetStyle (Gnuplot2dDataset::LINES);
}

Ptr<Socket>
Experiment::SetupPacketReceive (Ptr<Node> node)
{
  TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
  Ptr<Socket> sink = Socket::CreateSocket (node, tid);
  InetSocketAddress local = InetSocketAddress (Ipv4Address::GetAny (), port);
  sink->Bind (local);
  sink->SetRecvCallback (MakeCallback (&Experiment::ReceivePacket, this));

  return sink;
}

void
Experiment::ReceivePacket (Ptr<Socket> socket)
{
  Ptr<Packet> packet;
  while (packet = socket->Recv ())
  {
    bytesTotal += packet->GetSize();
  }
}

void
Experiment::CheckThroughput()
{
  double mbs = ((bytesTotal * 8.0) /1000000);
  bytesTotal = 0;
  m_output.Add ((Simulator::Now ()).GetSeconds (), mbs);

  //check throughput every 1/10 of a second 
  Simulator::Schedule (Seconds (0.1), &Experiment::CheckThroughput, this);
}

Vector
Experiment::GetPosition (Ptr<Node> node)
{
  Ptr<MobilityModel> mobility = node->GetObject<MobilityModel> ();
  return mobility->GetPosition ();
}

/**
 *
 * Take the grid map, divide it into 4 quadrants
 * Assign all nodes from each quadrant to a specific container 
 * 
 */
void
Experiment::AssignNeighbors (NodeContainer c)
{
  uint32_t totalNodes = c.GetN ();
  for (uint32_t i=0; i< totalNodes; i++)
    {
      if ( (i % gridSize) <= (gridSize/2 - 1))
        {
          //lower left quadrant
	  if ( i < totalNodes/2 )
	    {
	      containerA.Add(c.Get(i));
            }
      
          //upper left quadrant
          if ( i >= (uint32_t)(4*totalNodes)/10 )
	    {
	      containerC.Add(c.Get(i));  
            }
	}
      if ( (i % gridSize) >= (gridSize/2 - 1))
        {
          //lower right quadrant
	  if ( i < totalNodes/2 )
	    {
	      containerB.Add(c.Get(i));  
            }

          //upper right quadrant
          if ( i >= (uint32_t)(4*totalNodes)/10  )
	    {
	      containerD.Add(c.Get(i));  
            }
	}
    }
}

/**
 * Generate 1-hop and 2-hop neighbors of a node in grid topology
 *
 */
NodeContainer
Experiment::GenerateNeighbors (NodeContainer c, uint32_t senderId)
{
  NodeContainer nc;
  uint32_t limit = senderId + 2;  
  for (uint32_t i= senderId - 2; i <= limit; i++)
    {
      //must ensure the boundaries for other topologies
      nc.Add(c.Get(i));
      nc.Add(c.Get(i + 10));
      nc.Add(c.Get(i + 20));
      nc.Add(c.Get(i - 10));
      nc.Add(c.Get(i - 20));
    }
  return nc;
}

/**
 * Sources and destinations are randomly selected such that a node 
 * may be the source for multiple destinations and a node maybe a destination 
 * for multiple sources. 
 */
void
Experiment::SelectSrcDest (NodeContainer c)
{
  uint32_t totalNodes = c.GetN();
  UniformVariable uvSrc (0, totalNodes/2 -1);
  UniformVariable uvDest (totalNodes/2, totalNodes);

  for (uint32_t i=0; i < totalNodes/3; i++)
    {
      ApplicationSetup (c.Get(uvSrc.RandomVariable::GetInteger()), c.Get(uvDest.RandomVariable::GetInteger()) ,  0, totalTime);
    }
}

/**
 *
 * A sender node will  set up a flow to each of the its neighbors
 * in its quadrant randomly.  All the flows are exponentially distributed
 *
 */
void
Experiment::SendMultiDestinations(Ptr<Node> sender, NodeContainer c)
{

  // UniformVariable params: (Xrange, Yrange)
  UniformVariable uv(0, c.GetN ());

  // ExponentialVariable params: (mean, upperbound)
  ExponentialVariable ev(expMean, totalTime);

  double start=0.0, stop=totalTime;
  uint32_t destIndex; 

  for (uint32_t i=0; i < c.GetN (); i++)
    {
      stop = start + ev.GetValue();
      NS_LOG_DEBUG("Start=" << start << " Stop=" << stop);

      do {
          destIndex = (uint32_t) uv.GetValue();
      } while ( (c.Get(destIndex))->GetId () == sender->GetId ());
      
      ApplicationSetup (sender, c.Get(destIndex) ,  start, stop);

      start = stop;

      if(start > totalTime) 
        {
          break;
        }
    }
}

void
Experiment::ApplicationSetup (Ptr<Node> client, Ptr<Node> server, double start, double stop)
{

  Vector serverPos = GetPosition (server);
  Vector clientPos = GetPosition (client);

  Ptr<Ipv4> ipv4Server = server->GetObject<Ipv4>();
  Ptr<Ipv4> ipv4Client = client->GetObject<Ipv4>();

  Ipv4InterfaceAddress iaddrServer = ipv4Server->GetAddress(1,0);
  Ipv4InterfaceAddress iaddrClient = ipv4Client->GetAddress(1,0);

  Ipv4Address ipv4AddrServer = iaddrServer.GetLocal ();
  Ipv4Address ipv4AddrClient = iaddrClient.GetLocal ();

  NS_LOG_DEBUG("Set up Server Device " <<  (server->GetDevice(0))->GetAddress () 
            << " with ip " << ipv4AddrServer 
            << " position (" << serverPos.x << "," << serverPos.y << "," << serverPos.z << ")");

  NS_LOG_DEBUG("Set up Client Device " <<  (client->GetDevice(0))->GetAddress () 
            << " with ip " << ipv4AddrClient 
            << " position (" << clientPos.x << "," << clientPos.y << "," << clientPos.z << ")"
            << "\n");

   
  // Equipping the source  node with OnOff Application used for sending 
  OnOffHelper onoff ("ns3::UdpSocketFactory", Address(InetSocketAddress(Ipv4Address("10.0.0.1"), port)));
  onoff.SetAttribute ("OnTime", RandomVariableValue (ConstantVariable (1)));
  onoff.SetAttribute ("OffTime", RandomVariableValue (ConstantVariable (0)));
  onoff.SetAttribute ("DataRate", DataRateValue (DataRate (60000000)));
  onoff.SetAttribute ("PacketSize", UintegerValue (packetSize));
  onoff.SetAttribute ("Remote", AddressValue(InetSocketAddress (ipv4AddrServer, port)));

  ApplicationContainer apps = onoff.Install (client);
  apps.Start (Seconds (start));
  apps.Stop (Seconds (stop));

  Ptr<Socket> sink = SetupPacketReceive (server);

}

Gnuplot2dDataset
Experiment::Run (const WifiHelper &wifi, const YansWifiPhyHelper &wifiPhy,
                 const NqosWifiMacHelper &wifiMac, const YansWifiChannelHelper &wifiChannel, const MobilityHelper &mobility)
{


  uint32_t nodeSize = gridSize*gridSize;
  NodeContainer c;
  c.Create (nodeSize);

  YansWifiPhyHelper phy = wifiPhy;
  phy.SetChannel (wifiChannel.Create ());

  NqosWifiMacHelper mac = wifiMac;
  NetDeviceContainer devices = wifi.Install (phy, mac, c);


  OlsrHelper olsr;
  Ipv4StaticRoutingHelper staticRouting;

  Ipv4ListRoutingHelper list;
  
  if (enableRouting)
    {
      list.Add (staticRouting, 0);
      list.Add (olsr, 10);
    }

  InternetStackHelper internet;

  if (enableRouting)
    {
      internet.SetRoutingHelper(list);
    }
  internet.Install (c);


  Ipv4AddressHelper address;
  address.SetBase ("10.0.0.0", "255.255.255.0");

  Ipv4InterfaceContainer ipInterfaces;
  ipInterfaces = address.Assign(devices);
  
  MobilityHelper mobil= mobility;
  mobil.SetPositionAllocator ("ns3::GridPositionAllocator",
                                "MinX", DoubleValue (0.0),
                                "MinY", DoubleValue (0.0),
                                "DeltaX", DoubleValue (nodeDistance),
                                "DeltaY", DoubleValue (nodeDistance),
                                "GridWidth", UintegerValue (gridSize),
                                "LayoutType", StringValue ("RowFirst"));

  mobil.SetMobilityModel ("ns3::ConstantPositionMobilityModel");

  if (enableMobility && enableRouting)
    {
      //Rectangle (xMin, xMax, yMin, yMax)
      mobil.SetMobilityModel ("ns3::RandomDirection2dMobilityModel",
                              "Bounds", RectangleValue (Rectangle (0, 500, 0, 500)),
                              "Speed", RandomVariableValue (ConstantVariable (10)),
                              "Pause", RandomVariableValue (ConstantVariable (0.2)));
    }
  mobil.Install (c);


//    NS_LOG_INFO ("Enabling global routing on all nodes");
//    Ipv4GlobalRoutingHelper::PopulateRoutingTables ();

  if ( scenario == 1 && enableRouting)
    {
      SelectSrcDest(c);
    }
  else if ( scenario == 2)
    {
      //All flows begin at the same time
      for (uint32_t i = 0; i < nodeSize - 1; i = i+2)
        {
          ApplicationSetup (c.Get (i), c.Get (i+1),  0, totalTime);
        }
    }
  else if ( scenario == 3)
    {
      AssignNeighbors(c);
      //Note: these senders are hand-picked in order to ensure good coverage
      //for 10x10 grid, basically one sender for each quadrant
      //you might have to change these values for other grids 
      NS_LOG_DEBUG(">>>>>>>>>region A<<<<<<<<<");
      SendMultiDestinations(c.Get(22), containerA);

      NS_LOG_DEBUG(">>>>>>>>>region B<<<<<<<<<");
      SendMultiDestinations(c.Get(26), containerB);

      NS_LOG_DEBUG(">>>>>>>>>region C<<<<<<<<<");
      SendMultiDestinations(c.Get(72), containerC);

      NS_LOG_DEBUG(">>>>>>>>>region D<<<<<<<<<");
      SendMultiDestinations(c.Get(76), containerD);
    }
  else if ( scenario == 4)
    {
      //GenerateNeighbors(NodeContainer, uint32_t sender)
      //Note: these senders are hand-picked in order to ensure good coverage
      //you might have to change these values for other grids 
      NodeContainer c1, c2, c3, c4, c5, c6, c7, c8, c9;

      c1 = GenerateNeighbors(c, 22);
      c2 = GenerateNeighbors(c, 24);;
      c3 = GenerateNeighbors(c, 26);;
      c4 = GenerateNeighbors(c, 42);;
      c5 = GenerateNeighbors(c, 44);;
      c6 = GenerateNeighbors(c, 46);;
      c7 = GenerateNeighbors(c, 62);;
      c8 = GenerateNeighbors(c, 64);;
      c9 = GenerateNeighbors(c, 66);;

      SendMultiDestinations(c.Get(22), c1);
      SendMultiDestinations(c.Get(24), c2);
      SendMultiDestinations(c.Get(26), c3);
      SendMultiDestinations(c.Get(42), c4);
      SendMultiDestinations(c.Get(44), c5);
      SendMultiDestinations(c.Get(46), c6);
      SendMultiDestinations(c.Get(62), c7);
      SendMultiDestinations(c.Get(64), c8);
      SendMultiDestinations(c.Get(66), c9);
    }

  CheckThroughput ();

  if (enablePcap)
    {
      phy.EnablePcapAll(GetOutputFileName());
    }

  if (enableTracing)
    {
      AsciiTraceHelper ascii;
      phy.EnableAsciiAll (ascii.CreateFileStream (GetOutputFileName() + ".tr"));
    }

  Ptr<FlowMonitor> flowmon;

  if (enableFlowMon)
    {
      FlowMonitorHelper flowmonHelper;
      flowmon = flowmonHelper.InstallAll ();
    }

  Simulator::Stop (Seconds (totalTime));
  Simulator::Run ();

  if (enableFlowMon)
    {
      flowmon->SerializeToXmlFile ((GetOutputFileName() + ".flomon"), false, false);
    }

  Simulator::Destroy ();
  
  return m_output;
}

bool
Experiment::CommandSetup (int argc, char **argv)
{
  // for commandline input
  CommandLine cmd;
  cmd.AddValue ("packetSize", "packet size", packetSize);
  cmd.AddValue ("totalTime", "simulation time", totalTime);
  cmd.AddValue ("rtsThreshold", "rts threshold", rtsThreshold);
  cmd.AddValue ("rateManager", "type of rate", rateManager);
  cmd.AddValue ("outputFileName", "output filename", outputFileName);
  cmd.AddValue ("enableRouting", "enable Routing", enableRouting);
  cmd.AddValue ("enableMobility", "enable Mobility", enableMobility);
  cmd.AddValue ("scenario", "scenario ", scenario);

  cmd.Parse (argc, argv);
  return true;
}

int main (int argc, char *argv[])
{

  Experiment experiment;
  experiment = Experiment ("multirate");

  //for commandline input
  experiment.CommandSetup(argc, argv);

  // set value to 0 for enabling fragmentation
  Config::SetDefault ("ns3::WifiRemoteStationManager::FragmentationThreshold", StringValue ("2200"));
  Config::SetDefault ("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue (experiment.GetRtsThreshold()));

  std::ofstream outfile ((experiment.GetOutputFileName()+ ".plt").c_str());

  MobilityHelper mobility;
  Gnuplot gnuplot;
  Gnuplot2dDataset dataset;

  WifiHelper wifi = WifiHelper::Default ();
  NqosWifiMacHelper wifiMac = NqosWifiMacHelper::Default ();
  YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default ();
  YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
  Ssid ssid = Ssid ("Testbed");
  
  wifiMac.SetType ("ns3::AdhocWifiMac",
                   "Ssid", SsidValue(ssid));
  wifi.SetStandard (WIFI_PHY_STANDARD_holland);
  wifi.SetRemoteStationManager (experiment.GetRateManager());

  NS_LOG_INFO ("Scenario: " << experiment.GetScenario ());
  NS_LOG_INFO ("Rts Threshold: " << experiment.GetRtsThreshold());
  NS_LOG_INFO ("Name:  " << experiment.GetOutputFileName());
  NS_LOG_INFO ("Rate:  " << experiment.GetRateManager());
  NS_LOG_INFO ("Routing: " << experiment.IsRouting());
  NS_LOG_INFO ("Mobility: " << experiment.IsMobility());

  dataset = experiment.Run (wifi, wifiPhy, wifiMac, wifiChannel, mobility);

  gnuplot.AddDataset (dataset);
  gnuplot.GenerateOutput (outfile);

  return 0;
}