版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、无线传感器网络的设计需要同时考虑能量效率、容错率、同步、服务质量、调度方法、系统拓扑等的影响。因此,ns-2等网络模拟器对于无线传感器网络的仿真是有局限的。本书将介绍另一种新的网络模拟器OMNeT+ ( Objective Modular Network Testbed in C+),并运用它进行无线传感器网络协议算法的仿真。OMNeT+是ObjectiveModularNetworkTestBedinC+的英文缩写,它是开源的基于组件的模块化的开放网络仿真平台,是近年来在科学和工业领域里逐渐流行的一种优秀的网络仿真平台。OMNeT+作为离散事件仿真器,具备强大完善的图形界面接口和可嵌入式仿真
2、内核,同NS2,OPNET和JavaSim等仿真平台相比,OMNeT+可运行于多个操作系统平台,可以简便定义网络拓扑结构,具备编程,调试和跟踪支持等功能。OMNeT+主要用于通信网络和分布式系统的仿真,目前最高版本为OMNeT4.4.1。用OMNeT+进行仿真的大致流程如下:1 一个OMNeT+模型是用通过交换信息来通讯的组件(模块)来构建的。模块可以嵌套,也就是说,几个模块可以组成一个复合模块。在创建模型时,你需要将系统映射到一个相互通讯的模块体系中。2 用NED语言定义模型的结构。你可以在OMNet+提供的IDE中以文本或图形化方式来编辑NED文件。3 模型的活动组件(简单模块)需要用C+
3、来编程,当中要使用仿真内核及类库。4 提供一个拥有配置和参数的omnetpp.ini文件给模型,一个配置文件可以用不同的参数来描述若干个仿真过程。5 构建仿真程序并运行它。你可以将代码链接到OMNet+的仿真内核及其提供的一个用户接口:命令行和交互式接口或图形化接口。6 仿真结果将写入输出向量和输出标量文件中。你可以使用IDE中提供的分析工具来进行可视化。结果文件是普通的文本,所以你能用R,Matlab或其它工具来进行绘图。2.1 OMNeT+框架 2.1.1 OMNeT+组成 OMNeT+主要由六个部分组成:仿真内核库(simulation kernel library,简称Sim),网络描
4、述语言的编译器(network description compiler, nedc),图形化的网络编辑器(graphical network description editor,GNED),仿真程序的图形化用户接口Tkenv,仿真程序的命令行用户接口Cmdenv,图形化的输出工具Plove和Scalar。 Sim是仿真内核和类库,用户编写的仿真程序要同Sim连接,Sim在OMNeT+中占据最为核心重要的地位。下面详细介绍的另外两重要组成部分。 (1)网络描述(NED)语言 NED是模块化的网络描述语言。网络描述包括大量的对组件的描述,如通道,简单和复合模块的类型。这些组件描述可用于各种不同
5、的网络描述中。NED语言用来定义模型中的网络拓扑结构,较为简单的网络拓扑可以使用GNED,但复杂网络的拓扑描述还应该用NED源文件方式书写。 (2)用户接口 OMNeT+的用户接口用于实现仿真程序的人机交互,OMNeT+允许模型内部机制对用户可视化,也允许用户启动和终止仿真,并更改模型内部的变量。OMNeT+中的图形化接口是一个用户工具,可方便用户了解模型内部的运行机制。 用户接口和仿真内核的交互是通过一个已定义的接口实现的。无需改变仿真内核,就可以实现不同类型的用户接口。同样无需更改模型文件,仿真模型可在不同接口下运行。用户以在强大图形化用户接口下测试和调试仿真程序,并最后可在简单快速的用户
6、接口中运行,而且该接口支持批处理。目前OMNeT+支持两种用户接口,即Tkenv和Cmdenv。对仿真进行的测试和调试可以在Tkenv接口下进行,Tkenv是一个简便易用的图形窗口化的用户接口,Tkenv支持跟踪,调试和执行仿真的功能。它在执行仿真过程中的任意时刻都能够提供详细的状态信息。Tkenv的主要特征有:各模块的文本输出有其独立的窗口,仿真过程中可以在Tkenv窗口中看到自传消息,支持仿真动画,标记断点,具有检查窗口,可以检查和改变模型中的变量,执行过程中仿真结果的图形化显示并且结果可以用柱状图和时间序列图显示,仿真可重新进行,快照文件用于显示模型的详细信息。 Cmdenv接口用于实际
7、的仿真实验,因为Cmdenv支持批处理。Cmdenv是一个简便的小型命令行接口,执行速度快。它可以在所有操作系统平台上运行。Cmdenv可以一次批处理配置文件中所有的仿真。2.1.2 OMNeT+结构 (1) OMNeT+具有模块化的结构,图1是OMNeT+仿真的高层体系结构。图1的箭头表示两组件之间的交互,图中共有5个箭头,表示了组件间的5种关系。 (1)执行模型和Sim:仿真内核管理将来的事件,当有事件发生时,仿真内核就调用执行模型中的模块。执行模型的模块存储在Sim的main对象中。执行模型依次调用仿真内核的函数并使用Sim库中的类。 (2)Sim和模型组件库:当仿真开始运行创建了仿真模
8、型的时候,仿真内核就实例化简单模块和其它的组件。当创建动态模块时,仿真内核也要引用组件库。实现在模型组件库中注册和查寻组件也是Sim的功能。 (3)执行模型和Envir:ev对象作为Envir的一部分,是面向执行模型的用户接口。仿真模型使用ev对象来记录调试信息。 (4)Sim和Envir:由Envir决定创建何种模型,Envir包含主要的仿真循环,并调用仿真内核以实现必须的功能。Envir捕捉并处理执行过程中发生在仿真内核和或类库中的错误和异常。 (5)Envir和Tkenv,Cmdenv:Envir定义了表示用户接口的TOmnetApp基类,Tkenv和Cmdenv都是TOmnetApp的
9、派生类。main()函数是Envir的一部分,为仿真决定选用合适的用户接口类,创建用户接口类的实例并执行。Sim和模型对ev对象的调用通过实例化TOmnetApp类进行。Envir通过TOmnetApp和其它类的方法实现Tkenv和Cmdenv的框架和基本功能。2.6.1 矢量描绘工具Plove Plove特征:Plove是描绘OMNET+输出矢量的一个便利的工具。每个矢量的线性跟轴边界,缩放比例,标题和标注一样被设置为最频繁的绘图选项。你可以点击一下将图保存到文件中去。在windows中,可以将图片复制到矢量格式的剪切板中,然后粘贴到其他的应用程序中。绘图之前先对结果进行过滤是可能的。过滤能
10、够平均化,切断极限值,通过计算柱状形可进行密度估计等等。启动时,Plove自动读你有效目录下的.ploverc文件。这个文件包含了一般的应用设置,包括你创建的滤波器。 Plove使用:首先,在左边方框中加载一个输出矢量(.vev)文件。可以通过点击中间的向右箭头将左方框内的矢量复制到右方框中。PLOT按钮将开始绘制右方框内所选矢量。在windows下是这样选定的:shift+左键拖选定一个区域,ctrl+左击选定/取消选定个别项。要调整图形风格,改变矢量的标题或增加一个滤波器,点击Options按钮。这也适应于一些选定的矢量。左方框作为你正在运行的矢量存储器。你可以加载许多矢量文件,删除你不想
11、处理的矢量文件,对它们重命名等等。这些改变不会影响矢量文件或磁盘。(Plove从不会自身修改输出矢量文件)在右方框内,如果你想要过滤矢量,你可以复制它们也可以保存最初的矢量文件。如果你为一个矢量设置了正确的选项,但是暂时还不想它留在右方框内,你可以把它送回左方框内存储起来。2.6.2 标量工具Scalar输出矢量捕获了仿真运行的瞬时行为。但是,为了比较多样的参数设置下的模型行为,输出标量更有用。输出标量格式,使用recordScalar()函数调用来记录标量结果,通常来自模块的finish()方法,代码如下:void EtherMAC:finish()double t = simTime();
12、if (t=0) return;recordScalar(simulated time, t);.相对应的输出标量文件(默认,omnetpp.sca)大致如下:run 1 lanscalar lan.hostA.mac simulated time 120.249243scalar .run 2 lanscalar lan.hostA.mac simulated time 235.678665.每次调用recordScalar()在文件中都产生一”scalar”行。另外,一些仿真运行可以将它们的结果记录到一个单个的文件中-进行比较,创建x-y绘图,等等。OMNeT+作为传感器网络的仿真平台具有
13、显著的优势,具体包括:l OMNeT+支持用户组件库,实现了模块类型的灵活重用。l OMNeT+的面向对象特性,允许仿真内核提供基类的灵活扩展。l OMNeT+提供了图形化的网络编辑器和网络、数据流查看工具。l OMNeT+提供仿真类库和用户界面,支持输入/输出、仿真数据的图形化显示、随机数生成器、消息结构等。通过用户界面,可以跟踪调试仿真过程。l 仿真环境采用C+语言开发,并采用自定义的配置文件omnetpp.ini进行配置定义。 OMNeT+通过NED语言来对网络系统进行描述。NED语言中包含着对信道、模块、节点和网络的完整描述,可以参见OMNeT+参考文档的第三章。在实际的网络仿真中,总
14、是会首先描述一些特定形状和特性的网络。一般说来,网络拓扑结构包括两种,一种是平面(flat)结构,另外一种是层次化(Hierarchy)结构。由于OMNeT+采用的是层次化的模块构建,所以层次化的网络拓扑可以通过平面的拓扑结构来生成。在这里,对常见的几种网络拓扑结构进行描述。更多的代码可以参见OMNeT+包中sample目录下的neddemo。在构建平面拓扑结构的时候,主要考虑的是各个节点之间的关系。对于规则的拓扑结构来说,节点之间的连线是有关系的。在下面的描述中,主要的是对这种关系进行梳理。(1) 二叉树结构一个二叉树结构中的节点包含有三个接口,分别对应父节点和左右子节点。这里用fromUp
15、per和downLeft、downRight来表示。对于高度为height的二叉树结构,其节点之间的关系可以表示如下。Ned代码 1 for i=0.2height-2, for j=0.2height-2 2 nodei.downLeft nodej.fromUpper if j=2*i+1; 3 nodei.downRight nodej.fromUpper if j=2*i+2; 4 这是OMNeT+中的另外一种表示方法(没有了if条件表达式)。Ned代码 5 for i=0.2(height-1)-2 6 nodei.downLeft node2*i+1.fromUpper; 7 no
16、dei.downRight node2*i+2.fromUpper; 8 生成的拓扑结构如下所示。从图中可以看到,最终生成了一个二叉树结构。这里的节点并没有显示成从上至下的结构,而是系统自动采用了一种合适的方法来表达。如果需要修改的话,则还需要对接点的位置进行约定。可以参见NED的描述文件。 (2) 链结构另外一种很常见的网络拓扑结构就是线性结构,就像一条链一样串起来。这种结构在OMNeT+中是很容易描述的。下面的代码这个实现。Ned代码 9 for i=0.n-2 10 nodei.right nodei+1.left; 11 生成的网络拓扑结构如下所示。 (3)完全图完全图是一个节点和网络
17、中的所有节点都有连接。拓扑结构描述如下所示。Ned代码 12 for i=0.(n-2), for j=(i+1).(n-1) 13 nodei.gj nodej.gi; 14 其中, node 表示节点,而这里的 g 表示门向量。这里通过二重循环将节点之间的所有接口都连接了起来。另外,这里采用了位置描述符来对网络进行描述,从而让整个网络成为一个环形。display(p=,ring); 生成的完全图如下所示。 (4)星形图这种网络拓扑结构是所有节点通过一个中心节点连出去。这种网络的创建并没有什么特殊的地方,一般构建两类节点:中间节点和终端节点,然后将所有的终端节点和中心节点相连即可。生成的拓扑
18、结构如下所示。 (5)网格网格拓扑结构这种网络拓扑结构也很常见。这种拓扑结构中的节点包含有四个接口,分别连接上下左右的节点。节点之间的连接关系如下所示。Ned代码 15 for i=0.height-1, for j=0.width-1 16 nodei*width+j.down node(i+1)*width+j.up if i!=height-1; 17 nodei*width+j.right node(i*width+j)+1.left if j!=width-1; 18 生成的网格图如下。 (6)蜂窝网络拓扑结构这是上面网格拓扑结构的一个变种,一般可以用于蜂窝网路中。这里的节点最多包含
19、有三个接口,其连接关系如下面的NED代码所示。Ned代码 19 for i=0.num-1 20 nodei.port+ nodei+1.port+ if inum-1 & i%(2*cols+2)!=2*cols; 21 nodei.port+ nodei+2*cols+1.port+ if inum-2*cols-1 & i%2=0; 22 (7)随机拓扑结构图有些时候需要随机生成网络拓扑结构,这在OMNeT+中是比较容易实现的。下面就是一种实现的方法。Ned代码 23 for i=0.n-1, for j=0.n-1 24 nodei.g+ nodej.g+ if i!=j & unif
20、orm(0,1)connectedness; 25 代码中connectedness用来控制节点的连接度,1表示连接所有的其他节点。减小此值将减少网络中出现的边数。一种随机图如下所示(connectedness=0.15)。 上面介绍的是比较常见的一些网络拓扑结构。在实际使用中,这些网络拓扑在针对特定问题的简化版本是可以的,但是当网络变得复杂后,这样的拓扑生成还是不行的。此时最好是采用专门的拓扑生成器,来生成网络拓扑。另外,这里介绍的都还是有线网络中的拓扑结构,还没有涉及到无线网络的拓扑生成。 现在的 OMNeT+4.0 已经将这里的结果集成了进去,推荐使用 EV 来输出日志信息。具体的定义见
21、include/cenvir.h 文件。这篇文章详细的讨论了如何设置 EV ,具有普遍的借鉴作用。 ev 语句可以用来打印信息从而了解到仿真模型正在做什么。这在进行调试和理解模型运行的时候是很有用的。现在的问题是,当模型需要运行很长时间的时候, ev 语句将会消耗大量的 CPU 周期。这个时候该怎么办呢? 本文将介绍如何高效率的进行记录,并且同时介绍如何创建 log channels 或者调试 channels 。 在 Cmdevn 环境下,当设置 express-mode=true 的时候,输出将会被丢弃而不会被打印,从而使得执行可以更快。但是实际上, ev 语句还是会带来一些开销。这种速度
22、的减小可能不会被注意到,但是影响却是很大的。这里的问题是,OMNeT+ 将只会丢弃也就是不打印已经转换为文本格式的字符串。举个例子,如下面的语句:Cpp代码 26 ev Average bit/sec is: totalBits/simTime() n; cpp view plaincopy27 ev Average bit/sec is: totalBits/simTime() n; 即使是在极速模式下, simTime() 将会被调用,浮点数除法也会被执行。结果将会被转换为字符串并存储在缓冲区中,而这里的缓冲区则会被丢弃。很遗憾的是, OMNeT+ 核心没有办法阻止这种事情发生,因为这是
23、C+ 的工作方式。这时候该怎么办呢?一种常见的方式是采用 #ifdef 。Cpp代码 28 #ifdef DEBUGGING 29 ev Average bit/sec is: totalBits/simTime() endl; 30 #endif cpp view plaincopy31 #ifdef DEBUGGING 32 ev Average bit/sec is: totalBits/simTime() endl; 33 #endif 这并不坏,但是却有一个很严重的问题:在切换打印输出的时候必须重新编译所有的文件。根据墨菲定律(有可能出错的事情,就会出错,Anything that
24、can go wrong will go wrong),当人们需要输出的时候往往看不到有输出。另外,代码中满含有 #ifdef 也不是一个好办法。随后想到的就是如何在编译的时候就将是否输出考虑到,下面是一个示例代码。Cpp代码 34 if (debugging) 35 ev Average bit/sec is: totalBits/simTime() endl; cpp view plaincopy36 if (debugging) 37 ev Average bit/sec is: totalBits/simTime() endl; 这比前面要好一些。 if 语句的开销是比较小的。这样,就
25、可以在初始化的时候通过 debugging 变量来决定是否输出信息。Cpp代码 38 debugging = par(debugging).boolValue(); cpp view plaincopy39 debugging = par(debugging).boolValue(); 这种做法还不是很方便,因为我们需要手工维护输出的状态。有些人会发现此值可以在调试中进行动态设置,但是这还不能令人满意。为什么不能让代码知道我们是否需要记录呢?实际上,我们可以回答这个问题。一般的, OMNeT+ 知道我们何时需要进行记录:不在极速模式下的时候。幸运的是, ev 对象可以知道这点,所以现在最新的代
26、码如下。Cpp代码 40 if (!ev.disabled() 41 ev Average bit/sec is: totalBits/simTime() endl; cpp view plaincopy42 if (!ev.disabled() 43 ev Average bit/sec is: totalBits/simTime() endl; 几乎就是这样了。现在的问题是需要为每个 ev 输出增加一个 if 语句。有经验的 C/C+ 程序员将会马上想到采用宏来产生精炼的代码。第一次尝试:Cpp代码 44 #define EV if (!ev.disabled() ev / * DANGE
27、ROUS!* 45 . 46 EV Average bit/sec is: totalBits/simTime() endl; cpp view plaincopy47 #define EV if (!ev.disabled() ev / * DANGEROUS!* 48 . 49 EV Average bit/sec is: totalBits/simTime() 1000) 51 EV Average bit/sec is: totalBits/simTime() n; 52 else 53 EV Not enough data yet 1000) 55 EV Average bit/se
28、c is: totalBits/simTime() n; 56 else 57 EV Not enough data yet 1000) 59 if (!ev.disabled() 60 ev Average bit/sec is: totalBits/simTime() endl; 61 else if (!ev.disabled() 62 ev Not enough data yet 1000) 64 if (!ev.disabled() 65 ev Average bit/sec is: totalBits/simTime() endl; 66 else if (!ev.disabled
29、() 67 ev Not enough data yet endl; 所以这里的代码将永远不会打印出“ Not enough data ”。 最好是忘记上面的 EV 定义,因为这很容易会使得你栽在上面。即使是在这个宏定义中加上一对括号也不能解决问题,因为打印参数将会在括号之外。看起来这个问题没法修复。尽管如此,让我们来看看下面这个版本:Cpp代码 68 #define EV ev.disabled() ? ev : ev 69 . 70 EV Average bit/sec is: totalBits/simTime() endl; cpp view plaincopy71 #define E
30、V ev.disabled() ? ev : ev 72 . 73 EV Average bit/sec is: totalBits/simTime() endl; 这看起来有点奇怪。这里的宏定义看起来没有区别(无论是为 true 或者是 false ),而且这和所有 C 语言教科书中所倡导的(宏如果需要扩展成表达式需要加上圆括号)相违背。但是确实这个宏是可以工作的。现在 EV 将会变成一个简单的表达式。 Cpp代码 74 ev.disabled() ? ev : ev Average bit/sec is: totalBits/simTime() endl; cpp view plainco
31、py75 ev.disabled() ? ev : ev Average bit/sec is: totalBits/simTime() endl; 这和下面的相同(注意符号的优先级) : Cpp代码 76 ev.disabled() ? ev : (ev Average bit/sec is: totalBits/simTime() endl); cpp view plaincopy77 ev.disabled() ? ev : (ev Average bit/sec is: totalBits/simTime() endl); 这时候,当 ev 被禁止的时候(条件为 true ),这只是简
32、单的一个 ev 对象的引用(这最终将会被编译器所优化,而不会产生任何的 CPU 指令);当 ev 启用的时候(条件为 false ),将会被还原成原始的 ev 语句。这正是我们所需要的。证明这里的 EV 定义在任何使用场景中都是可行的,这可以作为练习。无论是否有 if 语句,或者是还有一个 ?: 操作符,或者是其他的场景,这都是适用的。实际上,上面 EV 的 ?: 版本并不能在 VC+ 7.0 中通过编译(因为在 VC+ 7.0 中需要?:三元操作符的第二个和第三个参数的类型是一样的,而不会做默认的转换)。所以在 VC7 中的版本是这样的:Cpp代码 78 #define EV ev.disa
33、bled() ? (std:ostream&)ev : ev cpp view plaincopy79 #define EV ev.disabled() ? (std:ostream&)ev : ev (译者注:实际上,由于现在最新的 OMNeT+ 4.0 并不支持使用 VC 编译器进行编译,所以也没有采用这样的方式)。 如果你没有用过 log4j 或者是 C+ 中类似的工具( log4Cpp , libCWD 等),那你有可能错过调试管道或者说是日志管道。简单地说, channels 是针对快速滚动日志问题的答案(因为你几乎不可能在日志的海洋中找到有用的信息)。你的代码日志将会记录到多个管道
34、中,而在调试的时候可以只关注自己感兴趣的管道。有两个标准可以用来区分管道日志:topic 和调试级别(如 detail, info, warnings )。其中第三个标准是位置(模块位置),这已经被 OMNeT+ 内置了。可以通过查看模块的输出来得到你想要的信息。一个比较好的消息是通过上面的 EV 定义,可以用来简单的模拟日志管道。当书写一个 IP 模块的时候,检查下面的定义:Cpp代码 80 #define fwdingEV (ev.disabled()|!fwdingChannel) ? (std:ostream&)ev : ev 81 #define localEV (ev.disabled()|!localChann
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 政府采购质疑管理制度
- 采购补料制度模板范本
- 西安电影集团采购制度
- 学校采购管理制度
- 学校酒水采购管理制度
- 商务采购部规章制度
- 政府采购药物谈判制度
- 交通局物品采购管理制度
- 采购行业管理制度
- 数据中心采购制度
- 2026浙江温州市公安局招聘警务辅助人员42人笔试参考题库及答案解析
- 2026广东茂名市公安局招聘警务辅助人员67人考试参考题库及答案解析
- 2026年希望杯IHC全国赛二年级数学竞赛试卷(S卷)(含答案)
- 中国抗真菌药物临床应用指南(2025年版)
- 2025-2026 学年下学期八年级英语下册教学计划
- 幼儿园春季育儿知识分享:守护成长健康同行
- 2026年六安职业技术学院单招职业适应性考试题库附答案详解(预热题)
- 2025年安徽审计职业学院单招职业适应性测试试题及答案解析
- 2026年春节后复工复产“开工第一课”安全生产培训课件
- 2025年西南计算机有限责任公司招聘笔试真题
- SMT炉后检查作业指导书V1.0
评论
0/150
提交评论