在官方文档ns-3-manual.pdf中,整个框架图:
v 随机变量
伪随机生成器pseudo-random number generator (PRNG)。
默认情况下,ns-3仿真程序使用固定种子和运行数字,它们分别存在全局变量g_rngSeed 和 g_rngRun中,在ns-3.14版本以后,ns3::RandomVariableStream
代替ns3::RandomVariable,但是底层不变。
可用ns3::RngSeedManager类来控制种子的属性。在开始时ns3::RngSeedManager::SetSeed()设置种子。在程序开始时,可以用ns3::RngSeedManager::SetRun()运行随机数。
RngSeedManager::SetSeed (3); // Changes seed from default of 1 to 3
RngSeedManager::SetRun (7);// Changes run number from default of 1 to 7
例子:/ns-3.19/src/core/examples/sample-random-variable-stream.cc
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2012 University of Washington
*
* 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
*/
#include "ns3/simulator.h"
#include "ns3/nstime.h"
#include "ns3/command-line.h"
#include "ns3/random-variable-stream.h"
#include <iostream>
using namespace ns3;
/*
* This program can be run from waf such as "./waf --run sample-random-variable-stream"
*
* This program is about as simple as possible to display the use of ns-3
* to generate random numbers. By default, the uniform random variate that
* will be outputted is 0.816532. Because ns-3 uses a fixed seed for the
* pseudo-random number generator, this program should always output the
* same number. Likewise, ns-3 simulations using random variables will
* behave deterministically unless the user changes the RunNumber or the
* Seed.
*
* There are three primary mechanisms to change the seed or run numbers
* from their default integer value of 1
* 1) Through explicit call of SeedManager::SetSeed () and
* SeedManager::SetRun () (commented out below)
* 2) Through the passing of command line arguments such as:
* "./waf --command-template="%s --RngRun=<value>" --run program-name"
* 3) Through the use of the NS_GLOBAL_VALUE environment variable, such as:
* "NS_GLOBAL_VALUE="RngRun=<value>" ./waf --run program-name"
*
* For instance, setting the run number to 3 will change the program output to
* 0.775417
*
* Consult the ns-3 manual for more information about the use of the
* random number generator
*/
int main (int argc, char *argv[])
{
CommandLine cmd;
cmd.Parse (argc, argv);
// SeedManager::SetRun (3);
Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable> ();
std::cout << uv->GetValue () << std::endl;
}
如果一直运行,那么它的结果不变。
可以改变参数来运行:
$ NS_GLOBAL_VALUE="RngRun=3" ./waf --run program-name
$ ./waf --command-template="%s --RngRun=3" --run program-name
$ ./build/optimized/scratch/program-name --RngRun=3
但是貌似这个运行不了。
随机变量:基类是RandomVariableStream。派生类
• class UniformRandomVariable
• class ConstantRandomVariable
• class SequentialRandomVariable
• class ExponentialRandomVariable
• class ParetoRandomVariable
• class WeibullRandomVariable
• class NormalRandomVariable
• class LogNormalRandomVariable
• class GammaRandomVariable
• class ErlangRandomVariable
• class TriangularRandomVariable
• class ZipfRandomVariable
• class ZetaRandomVariable
• class DeterministicRandomVariable
• class EmpiricalRandomVariable
v 回调
解除依赖性,利用指针,让一个模块调用另外一个模块的函数。
区分是全局,还是类的函数。
如果是全局函数
static double
CbOne (double a, double b)
{
std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
return a;
}
int main (int argc, char *argv[])
{
// return type: double
// first arg type: double
// second arg type: double
Callback<double, double, double> one;
one = MakeCallback (&CbOne);
NS_ASSERT (!one.IsNull ());
// invoke cbOne function through callback instance
double retOne;
retOne = one (10.0, 20.0);
}
如果是类的函数:
class MyCb {
public:
int CbTwo (double a) {
std::cout << "invoke cbTwo a=" << a << std::endl;
return -5;
}
};
int main ()
{
...
// return type: int
// first arg type: double
Callback<int, double> two;
MyCb cb;
// build callback instance which points to MyCb::cbTwo
two = MakeCallback (&MyCb::CbTwo, &cb);
...
}
调用空的回调
two = MakeNullCallback<int, double> ();
NS_ASSERT (two.IsNull ());
v 对象模型
l 面向对象编程
l 对象基类:Object、ObjectBase、SimpleRefCount。属性系统,对象聚合系统,智能指针和引用计数系统。
l 内存管理与引用计数指针(Ptr)
引用计数指针Ptr,智能指针为每个基本类型提供一对函数Ref()和Unref()来增加和减少对象实例的引用计数。记得要用CreateObject模板函数创建类对象。
l 聚合,ns-3使用查询接口设计模式来使用聚合。
例子
static void
AddIpv4Stack(Ptr<Node> node)
{
//创建一个IPv4协议的指针对象。
Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();
//把IPv4协议聚合到节点中。这样Node就不需要被编辑来使用户使用指向基类Node的指针来访问IPv4接口。用户可以在程序运行时向节点请求指向该节点IPv4接口的指针。
ipv4->SetNode (node);
node->AggregateObject (ipv4);
Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> ();
ipv4Impl->SetIpv4 (ipv4);
node->AggregateObject (ipv4Impl);
}
如果有一个节点指针m_node,该指针指向一个节点对象,且先前已经將IPv4的实现聚合到该节点。用户想要配置一个路由协议,为了实现这点,必须访问已经聚合到该节点的一个对象,且该用户具有IP配置接口。
Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4> ();
如果没有IPv4的对象被聚合到该节点上,该方法返回一个NULL。
v 属性系统
仿真时,要设置:对使用的网络构件进行连接组织和模拟拓扑;对网络构件中实例化的模型所使用的参数值进行设置。
基本上类都是继承与ns3::Object 或者 ns3::ObjectBase.ns3类如果从ns3::Object 派生,那么它就有一个元数据metadata类,叫做TypeId,该类记录了关于类的元数据,用来对象聚合和组件管理系统。它包括:一个唯一的字串来标记该类,包括元数据系统的子类,在子类中一系列的可访问的构造函数。例子:
class Node : public Object
{
public:
static TypeId GetTypeId (void);
...
Node.cc文件的定义如下:
TypeId
Node::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::Node")
.SetParent<Object> ()
.AddConstructor<Node> ()
.AddAttribute ("DeviceList", "The list of devices associated to this Node.",
ObjectVectorValue (),
MakeObjectVectorAccessor (&Node::m_devices),
MakeObjectVectorChecker<NetDevice> ())
.AddAttribute ("ApplicationList", "The list of applications associated to this Node.",
ObjectVectorValue (),
MakeObjectVectorAccessor (&Node::m_applications),
MakeObjectVectorChecker<Application> ())
.AddAttribute ("Id", "The id (unique integer) of this Node.",
TypeId::ATTR_GET, // allow only getting it.
UintegerValue (0),
MakeUintegerAccessor (&Node::m_id),
MakeUintegerChecker<uint32_t> ())
;
return tid;
}
SetParent<Object>()在声明中调用,结合对象聚合机制使用,方便在使用GetObject()函数时安全的进行向上或向下类型转换。
.AddConstructor<Node> ()结合抽象对象工厂机制来使用,允许我们不必要知道类的内部细节而构建对象。
.AddAttribute()调用目的是把一个给定的唯一字符床和类的成员变量相关联。参数从左到右分别是:要绑定的字符串,解释说明,属性的映射变量,把类成员变量转化为第3个参数的类型(其实就是检查)。
有两种初始化方式:
Ptr<Node> n = CreateObject<Node> ();
和工厂机制初始化
ObjectFactory factory;
const std::string typeId = "ns3::Node’’;
factory.SetTypeId (typeId);
Ptr<Object> node = factory.Create <Object> ();
关于获取和修改属性值:
考虑到有类的声明:
class DropTailQueue : public Queue {
public:
static TypeId GetTypeId (void);
...
private:
std::queue<Ptr<Packet> > m_packets;
uint32_t m_maxPackets;
};
cc文件的描述:
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
TypeId DropTailQueue::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::DropTailQueue")
.SetParent<Queue> ()
.AddConstructor<DropTailQueue> ()
.AddAttribute ("MaxPackets",
"The maximum number of packets accepted by this DropTailQueue.",
UintegerValue (100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
MakeUintegerChecker<uint32_t> ())
;
return tid;
}
在属性设置中:建立m_maxPackets 到字符串“MaxPackets”的映射,并且默认值为100,提供了关于定义值的帮助文本,提供了checker,可以用来设置所允许的范围。
要注意宏定义NS_OBJECT_ENSURE_REGISTERED (DropTailQueue)是一定要调用的,否则属性系统涉及的属性都不能被初始化。
使用例子在src/point-to-point/examples/main-attribute-value.ccz中
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2008 University of Washington
*
* 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: Tom Henderson <[email protected]>
*/
#include "ns3/log.h"
#include "ns3/command-line.h"
#include "ns3/ptr.h"
#include "ns3/config.h"
#include "ns3/uinteger.h"
#include "ns3/string.h"
#include "ns3/pointer.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/queue.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/point-to-point-net-device.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("AttributeValueSample");
//
// This is a basic example of how to use the attribute system to
// set and get a value in the underlying system; namely, an unsigned
// integer of the maximum number of packets in a queue
//
int
main (int argc, char *argv[])
{
LogComponentEnable ("AttributeValueSample", LOG_LEVEL_INFO);
// By default, the MaxPackets attribute has a value of 100 packets
// (this default can be observed in the function DropTailQueue::GetTypeId)
// 设置默认属性,可以用字符串也可以用整数
// Here, we set it to 80 packets. We could use one of two value types:
// a string-based value or a UintegerValue value
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// The below function call is redundant
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
// Allow the user to override any of the defaults and the above
// SetDefaults() at run-time, via command-line arguments
CommandLine cmd;
cmd.Parse (argc, argv);
// Now, we will create a few objects using the low-level API
Ptr<Node> n0 = CreateObject<Node> ();
Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
n0->AddDevice (net0);
Ptr<Queue> q = CreateObject<DropTailQueue> ();
net0->SetQueue (q);
// At this point, we have created a single node (Node 0) and a
// single PointToPointNetDevice (NetDevice 0) and added a
// DropTailQueue to it.
// Now, we can manipulate the MaxPackets value of the already
// instantiated DropTailQueue. Here are various ways to do that.
// We assume that a smart pointer (Ptr) to a relevant network device
// is in hand; here, it is the net0 pointer.
// 1. Pointer-based access
//第一种方法,基于指针的修改属性,可以向下转型也可以不用向下转型。
// One way to change the value is to access a pointer to the
// underlying queue and modify its attribute.
//
// First, we observe that we can get a pointer to the (base class)
// queue via the PointToPointNetDevice attributes, where it is called
// TxQueue
PointerValue ptr;
net0->GetAttribute ("TxQueue", ptr);
Ptr<Queue> txQueue = ptr.Get<Queue> ();
// Using the GetObject function, we can perform a safe downcast
// to a DropTailQueue, where MaxPackets is a member
Ptr<DropTailQueue> dtq = txQueue->GetObject <DropTailQueue> ();
NS_ASSERT (dtq);
// Next, we can get the value of an attribute on this queue
// We have introduced wrapper "Value" classes for the underlying
// data types, similar to Java wrappers around these types, since
// the attribute system stores values and not disparate types.
// Here, the attribute value is assigned to a Uinteger, and
// the Get() method on this value produces the (unwrapped) uint32_t.
UintegerValue limit;
dtq->GetAttribute ("MaxPackets", limit);
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " packets");
// Note that the above downcast is not really needed; we could have
// done the same using the Ptr<Queue> even though the attribute
// is a member of the subclass
txQueue->GetAttribute ("MaxPackets", limit);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " packets");
// Now, let‘s set it to another value (60 packets)
txQueue->SetAttribute ("MaxPackets", UintegerValue (60));
txQueue->GetAttribute ("MaxPackets", limit);
NS_LOG_INFO ("3. txQueue limit changed: " << limit.Get () << " packets");
// 2. Namespace-based access
//通过命名空间来修改
// An alternative way to get at the attribute is to use the configuration
// namespace. Here, this attribute resides on a known path in this
// namespace; this approach is useful if one doesn‘t have access to
// the underlying pointers and would like to configure a specific
// attribute with a single statement.
Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", UintegerValue (25));
txQueue->GetAttribute ("MaxPackets", limit);
NS_LOG_INFO ("4. txQueue limit changed through namespace: " <<
limit.Get () << " packets");
// we could have also used wildcards to set this value for all nodes
// and all net devices (which in this simple example has the same
// effect as the previous Set())
Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", UintegerValue (15));
txQueue->GetAttribute ("MaxPackets", limit);
NS_LOG_INFO ("5. txQueue limit changed through wildcarded namespace: " <<
limit.Get () << " packets");
Simulator::Destroy ();
}
运行结果:
还可以使用对象名基于服务访问:
Names::Add ("server", serverNode);
Names::Add ("server/eth0", serverDevice);
...
Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
可以通过构造函数帮助类设置属性:
Ptr<Object> p = CreateObject<MyNewObject> ("n1", v1, "n2", v2, ...);
或者高层帮助APIs来设置:
mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
"MinX", DoubleValue (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", DoubleValue (5.0),
"DeltaY", DoubleValue (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));
ConfigStore是属性值和默认值的特殊数据库,它分布在不同的模块src/config-store/ directory中。可以看一个添加例子src/config-store/examples/config-store-save.cc。
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
#include "ns3/core-module.h"
#include "ns3/config-store-module.h"
#include <iostream>
using namespace ns3;
class ConfigExample : public Object
{
public:
static TypeId GetTypeId (void) {
static TypeId tid = TypeId ("ns3::ConfigExample")
.SetParent<Object> ()
.AddAttribute ("TestInt16", "help text",
IntegerValue (-2),
MakeIntegerAccessor (&ConfigExample::m_int16),
MakeIntegerChecker<int16_t> ())
;
return tid;
}
int16_t m_int16;
};
NS_OBJECT_ENSURE_REGISTERED (ConfigExample)
;
// Assign a new default value to A::TestInt16 (-5)
// Configure a TestInt16 value for a special instance of A (to -3)
// View the output from the config store
//
int main (int argc, char *argv[])
{
Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));
Ptr<ConfigExample> a_obj = CreateObject<ConfigExample> ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5, "Cannot set ConfigExample‘s integer attribute via Config::SetDefault");
Ptr<ConfigExample> b_obj = CreateObject<ConfigExample> ();
b_obj->SetAttribute ("TestInt16", IntegerValue (-3));
IntegerValue iv;
b_obj->GetAttribute ("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv.Get () == -3, "Cannot set ConfigExample‘s integer attribute via SetAttribute");
// These test objects are not rooted in any ns-3 configuration namespace.
// This is usually done automatically for ns3 nodes and channels, but
// we can establish a new root and anchor one of them there (note; we
// can‘t use two objects of the same type as roots). Rooting one of these
// is necessary for it to show up in the config namespace so that
// ConfigureAttributes() will work below.
Config::RegisterRootNamespaceObject (b_obj);
#ifdef HAVE_LIBXML2
// Output config store to XML format
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore outputConfig;
outputConfig.ConfigureDefaults ();
outputConfig.ConfigureAttributes ();
#endif /* HAVE_LIBXML2 */
// Output config store to txt format
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore outputConfig2;
outputConfig2.ConfigureDefaults ();
outputConfig2.ConfigureAttributes ();
Simulator::Run ();
Simulator::Destroy ();
}
v Tracing系统
要跟踪程序运行信息:
l 用回调,回调C++的输出
如
#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}
l 用logging系统,可以有控制级别
如:
NS_LOG_LOGIC ("For me (interface broadcast address)");
l 用tracing系统
这是在NS3中最重要的部分。它依赖于ns3的回调和属性机制。示例在ns-allinone-3.19/ns-3.19/examples/tutorial/fourth.cc:
#include "ns3/object.h"
#include "ns3/uinteger.h“
//这个头文件引入要跟踪的数据
#include "ns3/traced-value.h"
//这个头文件包含了本程序要使用的能把自定义数据转化为Trace Source的函数
#include "ns3/trace-source-accessor.h"
#include <iostream>
using namespace ns3;
class MyObject : public Object
{
public:
static TypeId GetTypeId (void)
{
static TypeId tid = TypeId ("MyObject")
.SetParent (Object::GetTypeId ())
.AddConstructor<MyObject> ()
.AddTraceSource ("MyInteger",
"An integer value to trace.",
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
return tid;
}
MyObject () {}
TracedValue<int32_t> m_myInt;
};
void
IntTrace (int32_t oldValue, int32_t newValue)
{
std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}
int
main (int argc, char *argv[])
{
Ptr<MyObject> myObject = CreateObject<MyObject> ();
//一般很少用,通常使用”config path”的子系统
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback (&IntTrace));
//
myObject->m_myInt = 1234;
}
运行结果:
使用config,在examples/tcp-large-transfer.cc中的部分片段:
void CwndTracer (uint32_t oldval, uint32_t newval) {}
...
Config::ConnectWithoutContext (
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
MakeCallback (&CwndTracer));
对参数说明:”/”代表后面紧跟命名空间,如果确定Configpath?只要进入API文档输入需要的类,会发现Config Paths标题,进去之后就知道了。
确定Trace Source:展开”Modules”->”C++ Construct Used by All Modules”,可以看到”the list of all trace source”找到可以使用的Trace Source。
确定Trace Sink:它其实是个函数,主要考虑返回值和参数。如果是Config::ConnectWithoutContext,那么函数可以只有一个变量:
Void courseChangeCallback(Ptr<const MobilityModel> model)
如果是Config::Connect,增加一个字符串参数:
Void courseChangeCallback(std::string path,Ptr<const MobilityModel> model)
9、ns-3内核