具体实现官方有很多, 主要是一些概念比较不太好懂
看博客: wsn simulator
omnet++:https://omnetpp.org/documentation/
C++: CPrimer 中文版
注: (原则上不需要下载任何东西, 都是包括在安装包中的, 包括Qt环境,编译器mingw等)
有关更多安装说明, 请访问omnet installGuide。
使用omnet仿真
主要有三步:
File->New-Project->Omnet++Project 来新建项目, 项目新建后, 我们可以看到src
, simulation
文件夹
simulation
: 存放项目配置文件
我们在配置文件中指定想模拟的网络, 网络中节点的位置等
src
: 存放所有源代码, 包括ned
文件, C++源文件等关于所有文件夹的作用看: https://doc.omnetpp.org/omnetpp/manual/#cha:ned-lang
使用omnet进行仿真, 我们首先需要去定义网络的拓扑结构
在src
目录下, 我们新建一个network , 选择one item , 此时我们就有了一个网络,当然现在里面什么也没有, 点击Design标签, 可以以GUI的形式查看ned文件当前的内容
逻辑上, omnet++中的module就相当于C++中的类,并且在模块中可以定义属性,我们用module实例化出来的模块对象, 又可以放到其他的模块中
代码上, omnet++中的module最终就是一个C++中的类,在定义网络的行为时, 我们需要用到他的属性,参数等等
简单模块(simple Module)
复合模块(compound module)
中一个节点, 很多个节点, 一块网络, 都可以是一个模块
network本质上就是一个复合模块
一个模块的定义分为3个步骤
cModule类
或cSimpleModule
类, 定义一个Module类定义一个复合模块的一般语法如下, 所有的sections都为可选的
module Host
{
types: //定义模块类型(在submodules中使用),信道类型(在connections中使用)等
...
parameters: //定义该模块的参数, 如传输速率,节点个数等
...
gates: // 定义该模块的输入和输出口及个数
...
submodules: // 定义子模块实例
...
connections: // 定义子模块间的链接方式
...
}
定义一个简单模块的语法如下:
simple Host
{
...
parameters: //定义该模块的参数, 如传输速率等
...
gates: // 定义该模块的输入和输出口及个数
...
}
对于简单模块我们继承cSimpleModule
类
对于复合模块我们继承cModule
类, 来定义一个C++ Module类
#include
class ExampleModule: public omnetpp::cSimpleModule {
public:
ExampleModule();
virtual ~ExampleModule();
};
Define_Module(ExampleModule);
在类定义的后面, 添加Define_Module(模块名);
, 将模块与指定的C++类联系起来
#include
class ExampleModule: public omnetpp::cSimpleModule {
public:
ExampleModule();
virtual ~ExampleModule();
};
Define_Module(ExampleModule);
NED properties are metadata annotations that can be added to modules, parameters, gates, connections, NED files, packages, and virtually anything in NED. @display, @class, @namespace, @unit, @prompt, @loose, @directIn are all properties that have been mentioned in previous sections, but those examples only scratch the surface of what properties are used for.
Using properties, one can attach extra information to NED elements. Some properties are interpreted by NED, by the simulation kernel; other properties may be read and used from within the simulation model, or provide hints for NED editing tools.
Properties are attached to the type, so one cannot have different properties defined per-instance. All instances of modules, connections, parameters, etc. created from any particular location in the NED files have identical properties.
下面是一个使用元数据注解的例子
@namespace(foo); // file property
module Example
{
parameters:
@node; // module property
@display("i=device/pc"); // module property
int a @unit(s) = default(1); // parameter property
gates:
output out @loose @labels(pk); // gate properties
submodules:
src: Source {
parameters:
@display("p=150,100"); // submodule property
count @prompt("Enter count:"); // adding a property to a parameter
gates:
out[] @loose; // adding a property to a gate
}
...
connections:
src.out++ --> { @display("ls=green,2"); } --> sink1.in; // connection prop.
src.out++ --> Channel { @display("ls=green,2"); } --> sink2.in;
}
在ned文件中, 我们可以定义信道, 定义的一般语法如下
channel 信道名 extends 要继承的信道 // requires a CustomChannel C++ class
{
...信道属性...
}
大多数情况下我们不用去定义信道, omnet++自带了三个信道
ned.IdealChannel,
ned.DelayChannel
和ned.DatarateChannel
详细请看:https://doc.omnetpp.org/omnetpp/manual/#sec:ned-lang:channels
我们也可以直接在connections
中直接写出信道的属性, 省去了定义信道的步骤
在定义不同
定义在parameters 中的属性, 在配置文件.ini
中指定
omnet++根目录的sample目录下官方提供了许多例子供我们参考, 这里挑选几个较为典型的例子
我们先来看一个模块, 这是我做RIMAC时定义的一个节点, SimpleNode
//Simple Node 模块
//定义普通节点拥有的参数
package RIMAC.simplenode;
simple SimpleNode
{
parameters:
double x @unit(m);
double y @unit(m);
double txRange @unit(m); //数据传输距离
double senRange @unit(m); //数据感知距离
double Twait @unit(s); //事件等待时间
// double bitRate; //传输速率
double animationHoldTimeOnCollision @unit(s); //碰撞时动画持续时间
volatile double frameTime = uniform(0.5s, 1.5s) @unit(s); //一帧的时间
double sleepTime @unit(s); //睡眠时间
double sendConsumption @unit(W); //发送能耗
double recvConsumption @unit(W); //接受能耗
double sleepConsumption @unit(W); //睡眠能耗
double idleConsumption @unit(W); //空闲能耗
double energy @unit(J); //初始能量
int packetSize @unit(B);
@display("p=$x,$y");
@class(RIMAC::simpleNode);
@signal[energyLeft](type="double");
@statistic[energyLeftStat](title="energyLeft"; source="energyLeft"; record=vector,stats);
gates:
input in @directIn;
}
Sink定义
package RIMAC;
import RIMAC.simplenode.SimpleNode;
simple Sink extends SimpleNode
{
parameters:
@class(RIMAC::Sink);
@display("p=$x,$y;i=device/terminal;r=$txRange");
@signal[e2etd](type="double");
@statistic[e2etdStat](title="e2etd"; source="e2etd"; record=vector,stats);
@signal[ae2etd](type="double");
@statistic[ae2etdStat](title="ae2etd"; source="ae2etd"; record=vector,stats);
}
离散事件系统(Discrete Event System)是指事件发生在时间线中离散的部分, 对于计算机网络而言正像是如此
离散事件模拟系统通过在称为FES (Future Event Set) or FEL (Future Event List) 的 数据结构中保存未来事件的集合来实现
启动仿真后, 事件执行的伪代码如下
初始化(initialize) -- 包括构建模型,添加初始化事件到FES中
while (FES不为空 && 仿真未结束)
{
从FES中取出事件
t := 该事件发生时间
执行事件
(事件执行过程中,可能往FES中添加事件, 也可能往FES中删除事件)
}
结束仿真 (写入统计数据, etc.)
omnet网络的整个过程如下
perform simulation run:
build network
(i.e. the system module and its submodules recursively)
insert starter messages for all submodules using activity()
do callInitialize() on system module
enter event loop // (described earlier)
if (event loop terminated normally) // i.e. no errors
do callFinish() on system module
clean up
callInitialize和callFinish的伪代码如下
callInitialize()
{
call to user-defined initialize() function
if (module is compound)
for (each submodule)
do callInitialize() on submodule
}
callFinish()
{
if (module is compound)
for (each submodule)
do callFinish() on submodule
call to user-defined finish() function
}
从上面的执行过程可知, 模块是有初始化事件的, 我们通过父类cSimpleModule
的initialize()
方法来进行初始化,
模块调试:
EV<<“initialize!!!”<<"("<
可以重写两个方法来进行多阶段的初始化,在numInitStages
中返回阶段的个数
一般来说,我们在第一个初始化阶段进行变量赋值等操作, 后面的阶段可以进行周期计算等
virtual void initialize(int stage);
virtual int numInitStages() const;
模块通过调用finish()
来进行仿真结束的工作
消息的发送由 sendXXX 系列函数完成, 消息的接收基本都由handleMessage完成
cMessage类是所有消息类的父类, 我们可以自定义一个消息类(当然必须继承cMessage类), 也可以直接使用这个类
cMessage (const char *name=nullptr, short kind=0)
cMessage API reference
Message sending.
virtual void send (cMessage *msg, int gateid)
virtual void send (cMessage *msg, const char *gatename, int gateindex=-1)
virtual void send (cMessage *msg, cGate *outputgate)
virtual void sendDelayed (cMessage *msg, simtime_t delay, int gateid)
virtual void sendDelayed (cMessage *msg, simtime_t delay, const char *gatename, int gateindex=-1)
virtual void sendDelayed (cMessage *msg, simtime_t delay, cGate *outputgate)
virtual void sendDirect (cMessage *msg, cModule *mod, const char *inputGateName, int gateIndex=-1)
virtual void sendDirect (cMessage *msg, cModule *mod, int inputGateId)
virtual void sendDirect (cMessage *msg, cGate *inputGate)
virtual void sendDirect (cMessage *msg, simtime_t propagationDelay, simtime_t duration, cModule *mod, const char *inputGateName, int gateIndex=-1)
virtual void sendDirect (cMessage *msg, simtime_t propagationDelay, simtime_t duration, cModule *mod, int inputGateId)
virtual void sendDirect (cMessage *msg, simtime_t propagationDelay, simtime_t duration, cGate *inputGate)
我们不知道一个函数的作用的时候, 绝大多数情况下, 都是去查cSimpleModulle
类
cSimpleModule
其次会去 SimulationManual 中查, 里面会有一些实际应用的例子