前言
事实上ns3的官方手册很全,相关书籍也是有的,官网先贴在这里:
ns-3.35_wifi-he-network.cc_ns-3网络仿真工具wifi脚本解析_wifi脚本网络拓扑_ns-3wifi6吞吐脚本关键注释_吞吐部分_基础ns-3_ns3.35-优快云博客
ns-3-model-library wifi 浅析_ns-3wifi部分解析_ns-3网络模拟器wifi部分文档分析_Part1_ns3 wifiphy物理层冲突-优快云博客
ns-3-model-library wifi 浅析_ns-3wifi部分解析_ns-3网络模拟器wifi部分文档分析_Part2_yansphy-优快云博客
不过现有的要么版本老旧,要么过于现象化,不够本质,正好我最近分析了下官方手册,在这里分享给大家——需要更加完整的内容,可以直接去官网。
Tracing跟踪
跟踪子系统是 ns-3 中需要了解的最重要的机制之一。在大多数情况下,ns-3 用户会对一些新的和改进的网络功能有一个绝妙的主意。为了验证这个想法是否有效,研究人员将对现有系统进行更改,然后通过收集捕获特征行为的统计数据来运行实验来了解新功能的行为方式。换句话说,运行模拟的全部意义在于生成输出以供进一步研究。在 ns-3 中,使研究人员能够执行此作的子系统是跟踪子系统。
Tracing动机
有很多方法可以从程序中获取信息。最直接的方法是直接将信息打印到标准输出,例如,
#include <iostream>
...
int main()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}
这在小型环境中是可行的,但随着模拟变得越来越复杂,您最终会得到越来越多的打印,并且解析和执行输出计算的任务开始变得越来越困难。要考虑的另一件事是,每次需要新的花絮时,都必须编辑软件核心并引入另一个版本。没有标准化的方法来控制所有这些输出,因此输出量往往会无限增长。最终,仅输出此信息所需的带宽开始限制仿真的运行时间。输出文件会增长到很大的大小,解析它们会成为一个问题。ns-3 提供了一种简单的机制,用于记录并通过 Log Components 提供对输出的一些控制,但控制级别根本不是非常细粒度的。logging 模块是一个相对生硬的工具。最好有一个工具,允许人们进入核心系统,并且只获得所需的信息,而不必更改和重新编译核心系统。更好的是,当感兴趣的项目发生变化或发生有趣的事件时通知用户的系统。ns-3 跟踪系统旨在沿着这些路线工作,并与 Attribute 和 Config 子词干完美集成,从而允许相对简单的使用场景。
概述
跟踪源 tracing source;跟踪接收器 tracing sink,sink负责记录数据,sink注册自己的记录数据函数为source的回调,source检测到时间就调用sink的数据记录callback函数。
跟踪子系统在很大程度上依赖于 ns-3 回调Callback 和属性Attribute 机制。在尝试了解跟踪系统之前,您应该阅读并理解手册的相应部分。ns-3 跟踪系统建立在独立跟踪源和跟踪接收器的概念之上;以及将 Source 连接到 Sink 的统一机制。跟踪源是可以向模拟中发生的事件发出信号并提供对有趣基础数据的访问的实体。例如,跟踪源可以指示网络设备何时接收到数据包,并为感兴趣的跟踪接收器提供对数据包内容的访问。跟踪源还可以指示模型中何时发生有趣的状态更改。例如,TCP 模型的拥塞窗口是跟踪源的主要候选对象。跟踪源本身没有用;它们必须连接到其他代码段,这些代码段实际上对源提供的信息执行一些有用的作。使用跟踪信息的实体称为跟踪接收器。跟踪源是事件的生成器,而跟踪接收器是使用者。这种显式划分允许将大量跟踪源分散在系统各处,这些位置是模型作者认为可能有用的。除非用户将跟踪接收器连接到这些源之一,否则不会输出任何内容。这种安排允许相对简单的用户将新型 sink 附加到现有的 tracing 源,而无需编辑和重新编译模拟器的核心或模型。跟踪源生成的跟踪事件可以有零个或多个使用者。可以将跟踪源视为一种点对多点信息链接。此概念性点对多点链接的“传输协议”是 ns-3回调。回想一下 Callback 部分,callback facility 是一种允许系统中的两个模块通过函数调用进行通信,同时将调用函数与被调用的类完全解耦的方法。这与上面概述的跟踪系统要求相同。基本上,跟踪源是可以向其注册多个函数的回调。当跟踪接收器表示有兴趣接收跟踪事件时,它会将回调添加到跟踪源持有的回调列表中。当发生有趣的事件时,跟踪源会调用其 operator() ,提供零个或多个参数。这会告诉源遍历其回调列表,依次调用每个回调。通过这种方式,参数被传送到跟踪接收器,它们只是函数。
最简单的例子
#include "ns3/object.h"
#include "ns3/uinteger.h"
#include "ns3/traced-value.h""
#include "ns3/trace-source-accessor.h"
#include <iostream>
using namespace ns3;
首先要做的是包含所需的文件。如上所述,跟踪系统大量使用了 Object 和 Attribute 系统。前两个包括引入这些系统的声明。文件 traced-value.h 引入了跟踪遵循值语义的数据所需的声明。通常,值语义仅意味着您可以传递对象,而不是地址。为了完全使用值语义,您必须有一个具有关联复制构造函数和赋值运算符的对象可用。我们扩展了这些要求,以讨论为纯旧数据 (POD) 类型预定义的运算符集。Operator=、operator++、operator–、operator+、operator== 等。这一切都意味着您将能够跟踪使用这些运算符对对象所做的更改。
class MyObject : public Object
{
public:
static TypeId GetTypeId()
{
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<uint32_t> m_myInt;
};
由于跟踪系统与 Attributes 集成,而 Attributes 与 Object 一起使用,因此跟踪源必须存在 ns-3Object。两行重要的代码是 .AddTraceSource 和 TracedValue 声明。的 .AddTraceSource 提供用于将跟踪源连接到外部世界的 “钩子”。TracedValue 声明提供了重载上述运算符并驱动回调过程的基础结构。
void
IntTrace(Int oldValue, Int newValue)
{
std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}
这是跟踪接收器的定义。它直接对应于回调函数。每当执行 TracedValue 的其中一个运算符时,都会调用此函数。
int
main(int argc, char *argv[])
{
Ptr<MyObject> myObject = CreateObject<MyObject>();
myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace));
myObject->m_myInt = 1234;
}
在此代码段中,需要做的第一件事是创建跟踪源所在的对象。下一步 TraceConnectWithoutContext 形成跟踪源和跟踪接收器之间的连接。请注意 MakeCallback 模板函数。回想一下 Callback 部分,这会创建一个专门的 functor,负责提供用于 “触发” 回调的重载 operator() 。重载的运算符 (++, – 等) 将使用此 operator() 来实际调用回调。TraceConnectWithoutContext 采用一个字符串参数,该参数提供分配给跟踪源的 Attribute 的名称。让我们暂时忽略关于 context 的部分,因为它还不重要。最后,
myObject->m_myInt = 1234;
应解释为对成员变量 operator= 的调用 m_myInt 整数作为参数传递。事实证明,此运算符(由 TracedValue)定义为执行返回 void 的回调,并将两个整数值作为参数 – 相关整数的一个旧值和一个新值。这正是我们提供的回调函数 IntTrace 的函数签名。总而言之,跟踪源本质上是一个包含回调列表的变量。跟踪接收器是用作回调目标的函数。Attribute 和 object type 信息系统用于提供一种将跟踪源连接到跟踪接收器的方法。“命中”跟踪源的作是在跟踪源上执行触发回调的运算符。这会导致跟踪接收器回调注册对使用 source 提供的参数调用的 source 的兴趣。
使用 Config 子系统连接到跟踪源
Config调用callback绑定的时候,/nodej/0就是某一个节点,$ns3:TCPL4protocol,是告诉config系统调用get object函数,获得node0上面已经聚合的协议TCPL4protocol,也就是获得了node0上面TCPL4protocol的指针,这样才能继续往后看,
上面简单示例中所示的 TraceConnectWithoutContext 调用实际上在系统中很少使用。更常见的是,Config 子系统用于允许使用所谓的 config path 在系统中选择跟踪源。例如,您可能会在系统中找到如下所示的内容(摘自 examples/tcp-large-transfer.cc ):
void CwndTracer(uint32_t oldval, uint32_t newval) {}
...
Config::ConnectWithoutContext(
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
MakeCallback(&CwndTracer));
这应该看起来非常熟悉。它与前面的示例相同,只是调用的是类 Config 的静态成员函数,而不是 Object 上的方法;并且提供的不是 Attribute name (属性名称),而是路径。首先要做的是向后读取路径。路径的最后一段必须是 Object 的 Attribute。事实上,如果你有一个指向 Object 的指针,该指针手头上有 “CongestionWindow” 属性(称为 theObject),你可以像前面的例子一样编写它:
void CwndTracer(uint32_t oldval, uint32_t newval) {}
...
theObject->TraceConnectWithoutContext("CongestionWindow", MakeCallback(&CwndTracer));
事实证明,Config::ConnectWithoutContext 的代码正是这样做的。此函数采用表示 Object 指针链的路径,并跟踪它们,直到到达路径的末尾,并将最后一段解释为最后一个对象上的 Attribute。让我们来看看发生了什么。路径中的前导 “/” 字符是指所谓的命名空间。配置系统中的预定义命名空间之一是 “NodeList”,它是模拟中所有节点的列表。列表中的项由列表中的索引引用,因此 “/NodeList/0” 是指模拟创建的节点列表中的第 0 个节点。这个节点实际上是一个 Ptr<Node>,因此是 ns3::Object 的子类。如对象模型部分所述,ns-3 支持对象聚合模型。下一个路径段以“$”字符开头,这表示应进行 GetObject 调用,以查找后面的类型。当 InternetStackHelper 初始化节点时,许多接口将聚合到该节点。其中之一是 TCP 四级协议。执行此协议对象的 ns3::TcpL4Protocol''.
When
the
``GetObject 运行时类型时,它将返回指向此类型对象的指针。TcpL4Protocol 类定义了一个名为 “SocketList” 的 Attribute,它是一个套接字列表。每个套接字实际上是一个具有自己的 Attributes 的 ns3::Object。套接字列表中的项目由 index 引用,就像在 NodeList 中一样,因此“SocketList/0”是指 NodeList 中第 0 个节点(在模拟中构造的第一个节点)上的套接字列表中的第 0 个套接字。这个套接字的类型是 ns3::TcpSocketImpl,它定义了一个名为“CongestionWindow”的属性,它是一个 TracedValue<uint32_t>。Config::ConnectWithoutContext 现在执行 a,:
object->TraceConnectWithoutContext("CongestionWindow", MakeCallback(&CwndTracer));
使用来自 “SocketList/0” 的对象指针,该指针在套接字中定义的跟踪源与回调 – CwndTracer 之间建立连接。现在,每当对 TCP 套接字中表示拥塞窗口的 TracedValue<uint32_t> 进行更改时,将执行已注册的回调,并调用函数 CwndTracer 打印出 TCP 拥塞窗口的旧值和新值。最后,Config::Connect...() 函数将引发错误,如果目标 TraceSource 在给定的路径中不存在。还有“故障安全”版本,Config::Connect...FailSafe()进行 TraceSource。如果至少可以建立一个连接,则故障安全版本返回 true。
使用 Tracing API
与跟踪系统的交互分为三个级别:
- 初级用户可以轻松控制哪些对象正在参与跟踪;
- 中级用户可以扩展跟踪系统以修改生成的输出格式或以不同的方式使用现有的跟踪源,而无需修改模拟器的核心;
- 高级用户可以修改模拟器核心以添加新的跟踪源和接收器。
使用 Trace Helpers
ns-3 跟踪帮助程序为配置和选择不同的跟踪事件以及将它们写入文件提供了丰富的环境。在前面的部分中,主要是 “构建拓扑”,我们已经看到了几种设计用于其他 (设备) 帮助程序的 trace helper 方法。也许您会记得看到过其中一些变化:
pointToPoint.EnablePcapAll("second");
pointToPoint.EnablePcap("second", p2pNodes.Get(0)->GetId(), 0);
csma.EnablePcap("third", csmaDevices.Get(0), true);
pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));
但是,可能并不明显的是,对于系统中找到的所有与跟踪相关的方法,都有一个一致的模型。我们现在将花一点时间来看看 “大局”。 目前,ns-3 中的跟踪帮助程序有两个主要用例:设备帮助程序和协议帮助程序。Device Helpers 着眼于指定应通过 node、device pair 启用哪些跟踪的问题。例如,您可能希望指定应在特定节点上的特定设备上启用 pcap 跟踪。这遵循 ns-3 设备概念模型,以及各种设备帮助程序的概念模型。自然而然地,创建的文件遵循 -- <prefix>-<protocol>-<interface>命名约定。Protocol Helpers 着眼于指定应通过协议和接口对启用哪些跟踪的问题。这遵循 ns-3 协议栈概念模型以及 Internet 堆栈帮助程序的概念模型。当然,跟踪文件应遵循 --<prefix>-<protocol>-<interface> 命名约定。我们使用一种称为 mixin 的方法将跟踪功能添加到我们的帮助程序类中。mixin 是一个提供子类继承的功能的类。从 mixin 继承不被视为一种专业化形式,但实际上是一种收集功能的方法。让我们快速看一下所有这四种情况以及它们各自的 mixin。
Pcap Tracing Device Helpers
EnablePcap和EnablePcapInternal是给devices用的ipv4不是device,属于协议的一部分,所以pcap tracing函数不是EnablePcapInternal;
这些帮助程序的目标是使向 ns-3 设备添加一致的 pcap 跟踪工具变得容易。我们希望所有不同风格的 pcap 跟踪在所有设备上都以相同的方式工作,因此这些帮助程序的方法由设备帮助程序继承。看看 src/network/helper/trace-helper.h 你是否想在查看真实代码的同时关注讨论。类 PcapHelperForDevice 是一个 mixin,它为在 ns-3 设备中使用 pcap 跟踪提供了高级功能。每个设备都必须实现一个从此类继承的虚拟方法。
virtual void EnablePcapInternal(std::string prefix, Ptr<NetDevice> nd, bool promiscuous) = 0;
此方法的签名反映了此级别以设备为中心的情况视图。从类 PcapUserHelperForDevice 继承的所有公共方法都简化为调用这个依赖于设备的实现方法。例如,最低级别的 pcap 方法:
void EnablePcap(std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
将直接调用 EnablePcapInternal 的设备实现。所有其他公共 pcap 跟踪方法都基于此实现构建,以提供其他用户级功能。这对用户意味着系统中的所有 device helpers 都将具有所有可用的 pcap 跟踪方法;如果设备正确实现 EnablePcapInternal,则这些方法将在设备上以相同的方式工作。
Pcap Tracing Device Helper Methods
Device helper创建的device,本身定义了EnablePcapInternal,而这些device helper都继承了public PcapHelperForDevice, public AsciiTraceHelperForDevice:class WifiPhyHelper : public PcapHelperForDevice, public AsciiTraceHelperForDevice 所以可以调用device. EnablePcap
promiscuous 这个对于phy来说似乎并不好用,关掉也不影响,都读的到
void EnablePcap(std::string prefix, Ptr<NetDevice> nd,
bool promiscuous = false, bool explicitFilename = false);
void EnablePcap(std::string prefix, std::string ndName,
bool promiscuous = false, bool explicitFilename = false);
void EnablePcap(std::string prefix, NetDeviceContainer d,
bool promiscuous = false);
void EnablePcap(std::string prefix, NodeContainer n,
bool promiscuous = false);
void EnablePcap(std::string prefix, uint32_t nodeid, uint32_t deviceid,
bool promiscuous = false);
void EnablePcapAll(std::string prefix, bool promiscuous = false);
在上面显示的每种方法中,都有一个名为 promiscuous 的默认参数,默认为 false。此参数指示不应在混杂模式下收集跟踪。如果您确实希望跟踪包含设备看到的所有流量(并且如果设备支持混杂模式),只需将 true 参数添加到上述任何调用中即可。例如:
Ptr<NetDevice> nd;
...
helper.EnablePcap("prefix", nd, true);
将在 nd 指定的 NetDevice 上启用混杂模式捕获。前两种方法还包括一个名为 explicitFilename 的默认参数,将在下面讨论。我们鼓励您仔细阅读 Doxygen for class PcapHelperForDevice 来查找这些方法的详细信息;但总结一下...... 您可以通过向 EnablePcap 方法提供 Ptr<NetDevice> 来在特定节点/网络设备对上启用 pcap 跟踪。Ptr<Node> 是隐式的,因为 net 设备必须只属于一个节点。例如:
Ptr<NetDevice> nd;
...
helper.EnablePcap("prefix", nd);
您可以通过向 EnablePcap 方法提供表示对象名称服务字符串的 std::string 来在特定节点/网络设备对上启用 pcap 跟踪。从名称字符串中查找 Ptr<NetDevice>。同样,<Node> 是隐式的,因为指定的 net 设备必须只属于一个 Node。例如:
Names::Add("server" ...);
Names::Add("server/eth0" ...);
...
helper.EnablePcap("prefix", "server/ath0");
您可以通过提供 NetDeviceContainer 在节点/网络设备对的集合上启用 pcap 跟踪。对于容器中的每个 NetDevice,都会检查类型。对于正确类型的每个设备(与设备帮助程序管理的类型相同),将启用跟踪。同样,<Node> 是隐含的,因为找到的网络设备必须恰好属于一个节点。例如:
NetDeviceContainer d = ...;
...
helper.EnablePcap("prefix", d);
您可以通过提供 NodeContainer 在节点/网络设备对的集合上启用 pcap 跟踪。对于 NodeContainer 中的每个 Node,将迭代其附加的 NetDevice。对于连接到容器中每个节点的每个 NetDevice,将检查该设备的类型。对于正确类型的每个设备(与设备帮助程序管理的类型相同),将启用跟踪。
NodeContainer n;
...
helper.EnablePcap("prefix", n);
您可以根据节点 ID 和设备 ID 以及显式 PTR 启用 pcap 跟踪。系统中的每个 Node 都有一个整数型 Node ID,连接到某个节点的每个设备都有一个整数型 Device ID。
helper.EnablePcap("prefix", 21, 1);
最后,您可以为系统中的所有设备启用 pcap 跟踪,其类型与设备帮助程序管理的类型相同。
helper.EnablePcapAll("prefix");
Pcap 跟踪设备帮助程序文件名选择
只是prefix和是否explicitFilename的区别,不太重要
上面的方法描述中隐含了 implementation 方法对完整文件名的构造。按照惯例,ns-3 系统中的 pcap 跟踪是以下格式 <prefix>-<node id>-<device id>.pcap 的
Ascii Tracing Device Helpers
Pcap 和 asci 的逻辑类似,保存东西不同而已,不再赘述。
ASCII 跟踪帮助程序 mixin 的行为与 pcap 版本基本相似。看看 src/network/helper/trace-helper.h 你是否想在查看真实代码的同时关注讨论。
Pcap Tracing Protocol Helpers
除了对于device 的 pcap和ascii以外,Protocol也有,也是类似的,ClickInternetStackHelper负责EnablePcapIpv4Internal,InternetTraceHelper负责EnablePcapIpv4,InternetStackHelper继承InternetTraceHelper,所以也可以EnablePcapIpv4,
这些 mixin 的目标是使向协议添加一致的 pcap 跟踪工具变得容易。我们希望所有不同风格的 pcap 跟踪在所有协议中都相同,因此这些帮助程序的方法由堆栈帮助程序继承。看看 src/network/helper/trace-helper.h 你是否想在查看真实代码的同时关注讨论。在本节中,我们将说明应用于协议 Ipv4 的方法。要在类似的协议中指定跟踪,只需替换适当的类型。例如,使用 Ptr<Ipv6> 而不是 Ptr<Ipv4>,并调用 EnablePcapIpv6 而不是 EnablePcapIpv4。类 PcapHelperForIpv4 提供了在 Ipv4 协议中使用 pcap 跟踪的高级功能。启用这些方法的每个协议帮助程序都必须实现从此类继承的单个 virtual method。例如,Ipv6 将有一个单独的实现,但唯一的区别在于方法名称和签名。需要不同的方法名称来区分类 Ipv4 和 Ipv6,它们都派生自类 Object,以及共享相同签名的方法。
virtual void EnablePcapIpv4Internal(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface) = 0;
此方法的签名反映了此级别情况的协议和以接口为中心的视图。从类 PcapHelperForIpv4 继承的所有公共方法都简化为调用这个依赖于设备的实现方法。例如,最低级别的 pcap 方法:
void EnablePcapIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
将直接调用 EnablePcapIpv4Internal 的设备实现。所有其他公共 pcap 跟踪方法都基于此实现构建,以提供其他用户级功能。这对用户意味着系统中的所有协议帮助程序都将具有所有可用的 pcap 跟踪方法;如果帮助程序正确实现 EnablePcapIpv4Internal,这些方法将跨协议以相同的方式工作。
Pcap Tracing Protocol Helper Methods
这些方法旨在与设备版本的以 Node 和 NetDevice 为中心的版本一一对应。我们使用protocol and interface约束,而不是 Node 和 NetDevice 对约束。请注意,就像在device 版本中一样,有六种方法:
void EnablePcapIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
void EnablePcapIpv4(std::string prefix, std::string ipv4Name, uint32_t interface);
void EnablePcapIpv4(std::string prefix, Ipv4InterfaceContainer c);
void EnablePcapIpv4(std::string prefix, NodeContainer n);
void EnablePcapIpv4(std::string prefix, uint32_t nodeid, uint32_t interface);
void EnablePcapIpv4All(std::string prefix);
我们鼓励您仔细阅读类 PcapHelperForIpv4 的 Doxygen 以查找这些方法的详细信息;但总结一下...... 您可以通过向 EnablePcap 方法提供 Ptr<Ipv4> 和接口来启用特定协议/接口对上的 pcap 跟踪。例如:
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
...
helper.EnablePcapIpv4("prefix", ipv4, 0);
您可以通过向 EnablePcap 方法提供表示对象名称服务字符串的 std::string 来在特定节点/网络设备对上启用 pcap 跟踪。从名称字符串中查找 Ptr<Ipv4>。例如:
Names::Add("serverIPv4" ...);
...
helper.EnablePcapIpv4("prefix", "serverIpv4", 1);
您可以通过提供 Ipv4InterfaceContainer 在协议/接口对的集合上启用 pcap 跟踪。对于容器中的每个 IPv4 / 接口对,将检查协议类型。对于适当类型的每个协议(与设备帮助程序管理的类型相同),将为相应的接口启用跟踪。例如:
NodeContainer nodes;
...
NetDeviceContainer devices = deviceHelper.Install(nodes);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer interfaces = ipv4.Assign(devices);
...
helper.EnablePcapIpv4("prefix", interfaces);
您可以通过提供 NodeContainer 在协议/接口对的集合上启用 pcap 跟踪。对于 NodeContainer 中的每个 Node,都会找到适当的协议。对于每个协议,将枚举其接口,并在结果对上启用跟踪。例如:
NodeContainer n;
...
helper.EnablePcapIpv4("prefix", n);
您也可以根据节点 ID 和接口启用 pcap 跟踪。在这种情况下,节点 ID 将转换为 Ptr<Node>,并在节点中查找相应的协议。生成的协议和接口用于指定生成的跟踪源。
helper.EnablePcapIpv4("prefix", 21, 1);
最后,您可以为系统中的所有接口启用 pcap 跟踪,其关联协议的类型与设备帮助程序管理的协议类型相同。
helper.EnablePcapIpv4All("prefix");
Pcap 跟踪协议帮助程序文件名选择
上述所有方法描述中都隐含了 implementation 方法对完整文件名的构造。按照惯例,对 ns-3 系统中的设备进行的 pcap 跟踪采用以下格式 <prefix>-<node id>-<device id>.pcap 。对于协议跟踪,协议和 Node 之间存在一对一的对应关系。这是因为协议对象被聚合到 Node 对象。由于系统中没有全局协议 ID,因此我们在文件命名中使用对应的节点 ID。因此,在自动选择的跟踪文件名中可能会出现文件名冲突。因此,协议跟踪的文件名约定已更改。
Ascii Tracing Protocol Helpers¶
与pcap类似,不再赘述
使用 Gnuplot 类制作绘图
有两种常用的方法来制作使用ns-3和gnuplot (http://www.gnuplot.info)的绘图:
1. 使用ns-3的gnuplot类创建一个gnuplot控制文件。
2. 使用ns-3生成的值创建gnuplot数据文件。
本节是关于方法1,也就是说,它是关于如何使用ns-3的Gnuplot类制作一个绘图。如果您对方法2感兴趣,请参阅ns-3教程中“跟踪”部分下的“真实示例”小节。
Creating Plots Using the Gnuplot Class
使用 gnuplot 类创建绘图
为了使用ns-3的Gnuplot类创建一个图表,必须执行以下步骤:
修改代码,使其使用Gnuplot类及其函数。
2. 运行代码,使其创建gnuplot控制文件。
3. 使用gnuplot控制文件的名称调用gnuplot。
4. 在您喜爱的图形查看器中查看生成的图形文件。有关步骤1的详细信息,请参阅下面讨论的示例图中的代码。
An Example Program that Uses the Gnuplot Class
使用 gnuplot 类的示例程序
使用ns-3的Gnuplot类的示例程序可以在这里找到:
src/stats/examples/gnuplot-example.cc
运行会输出plt,用gnuplot软件画图,生成png。
An Example 2-Dimensional Plot
二维图示例
下图使用下面的代码从gnuplot-example.cc创建:

std::string fileNameWithNoExtension = "plot-2d";
std::string graphicsFileName
= fileNameWithNoExtension + ".png";
std::string plotFileName
= fileNameWithNoExtension + ".plt";
std::string plotTitle
= "2-D Plot";
std::string dataTitle
= "2-D Data";
// Instantiate the plot and set its title.
Gnuplot plot(graphicsFileName);
plot.SetTitle(plotTitle);
// Make the graphics file, which the plot file will create when it
// is used with Gnuplot, be a PNG file.
plot.SetTerminal("png");
// Set the labels for each axis.
plot.SetLegend("X Values", "Y Values");
// Set the range for the x axis.
plot.AppendExtra("set xrange [-6:+6]");
// Instantiate the dataset, set its title, and make the points be
// plotted along with connecting lines.
Gnuplot2dDataset dataset;
dataset.SetTitle(dataTitle);
dataset.SetStyle(Gnuplot2dDataset::LINES_POINTS);
double x;
double y;
// Create the 2-D dataset.
for (x = -5.0; x <= +5.0; x += 1.0)
{
// Calculate the 2-D curve
//
//
2
// y = x .
//
y = x * x;
// Add this point.
dataset.Add(x, y);
}
// Add the dataset to the plot.
plot.AddDataset(dataset);
// Open the plot file.
std::ofstream plotFile(plotFileName.c_str());
// Write the plot file.
plot.GenerateOutput(plotFile);
// Close the plot file.
plotFile.close();
An Example 2-Dimensional Plot with Error Bars
带误差线的二维图示例
下面的二维图在x和y方向上有误差条,使用下面的代码从gnuplot-example.cc创建:
for (x = -5.0; x <= +5.0; x += 1.0)
{
// Calculate the 2-D curve
//
//
2
// y = x .
//
y = x * x;
// Make the uncertainty in the x direction be constant and make
// the uncertainty in the y direction be a constant fraction of
// y's value.
xErrorDelta = 0.25;
yErrorDelta = 0.1 * y;
// Add this point with uncertainties in both the x and y
// direction.
dataset.Add(x, y, xErrorDelta, yErrorDelta);
}
An Example 3-Dimensional Plot
3 维图 示例
下面的三维图是使用下面的代码从gnuplot-example.cc创建的:
// Create the 3-D dataset.
for (x = -5.0; x <= +5.0; x += 1.0)
{
for (y = -5.0; y <= +5.0; y += 1.0)
{
// Calculate the 3-D surface
//
//
2
2
// z = x * y .
//
z = x * x * y * y;
// Add this point.
dataset.Add(x, y, z);
}
// The blank line is necessary at the end of each x value's data
// points for the 3-D surface grid to work.
dataset.AddEmptyLine();
}
// Add the dataset to the plot.
plot.AddDataset(dataset);