> 文章列表 > Tossim 教程

Tossim 教程

Tossim 教程

系列文章目录

TinyOS 系列文章【一】:TinyOS 配置教程
TinyOS 系列文章【二】:Tossim 教程


文章目录

  • 系列文章目录
  • 前言
  • 1. Tossim 简介
  • 2. TOSSIM 仿真
    • 2.1. 编译 TOSSIM
    • 2.2. 基于 Python 的仿真
    • 2.3. 调试语句
    • 2.4. 网络配置
  • 总结

前言

本文主要用于记录在 WSN 课程中,配置大作业所需使用的 Tossim 仿真工具


1. Tossim 简介

TOSSIM (TinyOS simulator)TinyOS 自带的一个仿真工具,可以支持大规模的网络仿真。由于 TOSSIM 运行和传感器硬件相同的代码,所以仿真编译器能直接从 TinyOS 应用的组件表编译仿真程序。通过替换 TinyOS 下层部分硬件相关的组件,TOSSIM 把硬件中断换成离散仿真事件,由仿真器事件抛出的中断来驱动上层应用,其他的 TinyOS 组件尤其是上层的应用组件都无须更改。

Tossim 是一个库,你必须写程序配置仿真运行,TOSSIM 支持两种编程接口:PythonC++

  • Python 允许你动态的和仿真交互,就像一个强大的 debugger
  • 但是 Python 的解释器本 身就是一个性能瓶颈,执行效率没有 C++

这两种接口各有优缺点,面我们只介绍 Python 这种接口


2. TOSSIM 仿真

2.1. 编译 TOSSIM

TOSSIMTinyOS 系统的一个程序库,其核心代码位于 tos/lib/tossim

在每个 TinyOS 源代码目录里,都可能有一个对应的 sim 子目录,其中包含改代码的仿真实现

例如:tos/chips/atm128/timer/sim 含有 Atmega128 定时器的仿真组件

先进入 TinyOS 所在的根目录:

cd /opt/tinyos-2.1.2/

为了编译 TOSSIM,你应该在 make 命令后面加上可选的 sim 选项:

cd apps/Blink
make micaz sim

现在 TOSSIM 只支持 micaz 平台

如果 TOSSIM 编译通过,会得到下面的输出信息:

在这里插入图片描述

2.2. 基于 Python 的仿真

下面将以 RadioCountToLeds 应用程序为例,演示 TOSSIM 仿真的一般步骤

RadioCountToLeds 应用程序有一个 4Hz 的计数器,每当数值更新时,就把数值无线广播出去。当另一个 RadioCountToLeds 节点接收到技术值,就将其低 3 位显示在 LED 灯上

  1. 切换到 RadioCountToLeds(在 apps 目录下)程序所在目录:
cd tinyos-2.1.2/apps/RadioCountToLeds
make micaz sim

编译成功后,终端出现如下输出:

在这里插入图片描述

  1. 使用 Python 的交互模式,开启 Python 解释器:
python

终端出现类似下面的输出:

在这里插入图片描述

  1. 创建 TOSSIM 对象,导入 TOSSIM 仿真器,并创建一个 TOSSIM 对象,输入如下命令:
>>> from TOSSIM import *
>>> t = Tossim([])
  1. 运行 TOSSIM 仿真,利用 t.runNextEvent 命令可以运行一个仿真器事件,t 就是刚才创建的 TOSSIM 对象,runNextEventTOSSIM 对象的一个函数,输入函数格式如下:
>>> t.runNextEvent()
0

此时输入 t.runNextEvent,返回 0,表示没有事件需要运行,因为当前还没有启动任何节点

  1. 在 45654 时间点(仿真时间标记点,相当于仿真中的最小时间单位)启动 32 号节点,然后运行它的第一个事件,即 Boot.booted 事件

此时,执行 runNextEvent 返回 1, 因为它有一个启动事件并成功运行了该事件,代码如下:

>>> m = t.getNode(32)
>>> m.bootAtTime(45654)
>>> t.runNextEvent()
1
  1. 另外,可以调用 tickPerSecond 函数来表示一秒的仿真时间点总和,调用函数的代码如下:
>>> m = t.getNode(32)
>>> m.bootAtTime(4 * t.ticksPerSecond() + 242119)
>>> t.runNextEvent()
1
  1. 仿真运行中,一共有两种方法判断节点是否启动,其中一种方法就是利用节点对象中的 isOn 命令直接查询,代码如下:
>>> m.isOn()
1
>>> m.turnOff()
>>> m.isOn()
0
>>> m.bootAtTime(560000)
>>> t.runNextEvent()
0
>>> t.runNextEvent()
1

注意到,节点关闭并再重新开启后执行的第一个 runNextEvent 函数返回 0,这是因为当节点关掉时,队列里仍然有一个事件

但是,当轮到处理这个事件时,节点仍处于关闭状态, runNextEvent 就返回 0

第二个 runNextEvent 命令针对于 560000 时间点的启动事件,故返回 1

  1. TOSSIM 对象还有很多功能函数,在 Python 里,一般可用 dir 函数查看某对象的功能函数,如下:
>>> t = Tossim([])
>>> dir(t)
['__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__g
etattr__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new_
_',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str_
_',
'__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'addCh
annel',
'currentNode', 'getNode', 'init', 'mac', 'newPacket', 'radio', 'rem
oveChannel',
'runNextEvent', 'setCurrentNode', 'setTime', 'this', 'thisown', 'ti
cksPerSecond', 'time', 'timeStr']

常用函数有:

currentNode(): returns the ID of the current node.
getNode(id): returns an object representing a specific mote
runNextEvent(): run a simulation event
time(): return the current time in simulation ticks as a large inte
ger
timeStr(): return a string representation of the current time
init(): initialize TOSSIM
mac(): return the object representing the media access layer
radio(): return the object representing the radio model
addChannel(ch, output): add output as an output to channel ch
removeChannel(ch, output): remove output as an output to channel ch
ticksPerSecond(): return how many simulation ticks there are in a s
imulated second

2.3. 调试语句

TOSSIM 有一个称为 dbg 的调试系统

  1. dbg:打印调试信息,以节点 ID 开头

修改 RadioCountToLedsC.nc 里的 Boot.booted 事件,当启动时打印一条 debug 信息,如下所示:

event void Boot.booted() {call Leds.led0On();dbg("Boot", "Application booted.\\n");call AMControl.start();
}

每次修改完 RadioCountToLedsC.nc 文件都要重新执行 make micaz sim 这条命令才会使之生效

  1. dbg() 有两个或者更多的参数:第一个参数 Boot,定义了输出通道,输出通道是一个字符串,dbg 命令和 C++ 语言中的 sprintf 语句十分相似

例如,RadioCountToLedsC.nc 有如下调用:

event message_t* Receive.receive(message_t* bufPtr, void* payload,
uint8_t len) {dbg("RadioCountToLedsC", "Received packet of length %hhu.\\n", le
n);
... 
}

输出通道需要利用 TOSSIM 对象中的 addChannel 函数将它与具体输出设备绑定,为了这些, 我们需要导入 Python 的 sys 包,告诉 TOSSIMBoot 信息输出到指定地方

进行如下测试:

>>> from TOSSIM import *
>>> t = Tossim([])
>>> m = t.getNode(32)
>>> m.bootAtTime(45654)
>>> import sys
>>> t.addChannel("Boot", sys.stdout);
1

返回信息表示绑定成功,运行第一个仿真事件,节点启动:

>>> t.runNextEvent()
DEBUG (32): Application booted.
1

如果没有信息输出,你可能需要运行事件直到它输出:

>>> while not m.isOn():
>>> t.runNextEvent()
  1. 如果一条语句有多个通道并且这些通道共享输出,TOSSIM 只打印这些信息一次,例如, Boot 通道和 RadioCountTOLedsC 通道连接到标准输出,将只会打印出一条信息,例如:
event void Boot.booted() {call Leds.led0On();dbg("Boot,RadioCountToLedsC", "Application booted.\\n");dbg("RadioCountToLedsC", "Application booted again.\\n");dbg("Boot", "Application booted a third time.\\n");call AMControl.start();
}

在 Python 的交互模式里输入如下命令:

>>> import sys
>>> t.addChannel("Boot", sys.stdout)
>>> t.addChannel("RadioCountToLedsC", sys.stdout)

运行 runNextEvent 之后,将会打印出如下结果:

>>> t.runNextEvent()
DEBUG (32): Application booted.
DEBUG (32): Application booted again.
DEBUG (32): Application booted a third time.

2.4. 网络配置

仿真刚启动时,节点之间不能互相通信

只有配置好网络拓扑结构,TOSSIM 仿真才能模拟网络行为

TOSSIM 中默认的无线电模型是以信号强度为基础的,需要程序员通过脚本接口提供一组描述网络传播强度、底噪声和接收灵敏度的数据

TOSSIM 已经提供了一组基于 CC2420 芯片的数据

通过 Python 中的 radio 对象可以仿真无线电行为,利用 dir 命令还可以查看 radio 对象的具体函数,其代码如下:

>>> from TOSSIM import *
>>> t = Tossim([])
>>> r = t.radio()
>>> dir(r)
['__class__', '__del__', '__delattr__', '__dict__', '__doc__',
'__getattr__', '__getattribute__', '__hash__', '__init__',
'__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__str__', '__swig_getmethods__',
'__swig_setmethods__', '__weakref__', 'add', 'connected',
'gain', 'remove', 'setNoise', 'this', 'thisown',
]

TOSSIM 不但可以仿真无限电的传播模型,还可以利用就近匹配算法 CPM 来模拟射频噪 声、节点间的相互干扰以及外部信号源节点的干扰

CPM 算法利用噪声道文件产生一个统计模型,通过调用节点对象的 addNoiseTraceReading命令创建噪声模型

tos/lib/tossim/noise 目录下有几个简单的噪声文件

例如,meyer-­heavy.txt 文件保存的是斯坦福大学 Meyer 图书馆的噪声道数据,它的前十行如下:

-39
-98
-98
-98
-99
-98
-94
-98
-98
-98

需要将 tos/lib/tossim/noise 目录下的meyer-­heavy.txt 文件移动到 tinyos-2.1.2/apps/RadioCountToLeds 目录下

下面是一段根据噪声道数据分别为节点 0­7 创建噪声模型的代码:

noise = open("meyer-heavy.txt", "r")
lines = noise.readlines()
for line in lines:str1 = line.strip()if str1:val = int(str1)for i in range(7):t.getNode(i).addNoiseTraceReading(val)
for i in range(7):t.getNode(i).createNoiseModel()

因为无线电的链路信息可以存在文本文件里,所以 TOSSIM 允许创建一个描述拓扑结构的文 件,然后用 Python 脚本加载该文件,并把这些拓扑信息保存到 radio 对象里

在文本文件中,3 个数值指明一条链路:源节点、目标节点和增益,例如:

1 2 -54.0

表示节点 1 发送消息,节点 2 以­ 54dBm 的增益接收

创建一个拓扑文件 topo.txt

gedit topo.txt

其内容如下:

1  2 -54.0
2  1 -55.0
1  3 -60.0
3  1 -60.0
2  3 -64.0
3  2 -64.0

以下脚本代码会读取该文件,并把数据存到 radio 对象里:

>>> f = open("topo.txt", "r")
>>> for line in f:
...		s = line.split()
...		if s:
...			print " ", s[0], " ", s[1], " ", s[2];
...			r.add(int(s[0]), int(s[1]), float(s[2]))

此时,当一个节点发送数据包,其邻居节点就能接收到数据包

创建 test.py 文件:

gedit test.py

下面是 RadioCountToLedsC 实例的完整仿真脚本代码,保存在 test.py 中:

#! /usr/bin/python
from TOSSIM import *
import syst = Tossim([])
r = t.radio()
f = open("topo.txt", "r")for line in f:s = line.split()if s:print " ", s[0], " ", s[1], " ", s[2];r.add(int(s[0]), int(s[1]), float(s[2]))t.addChannel("RadioCountToLedsC", sys.stdout)
t.addChannel("Boot", sys.stdout)noise = open("meyer-heavy.txt", "r")
for line in noise:str1 = line.strip()if str1:val = int(str1)for i in range(1, 4):t.getNode(i).addNoiseTraceReading(val)
for i in range(1, 4):print "Creating noise model for ",i;t.getNode(i).createNoiseModel()t.getNode(1).bootAtTime(100001);
t.getNode(2).bootAtTime(800008);
t.getNode(3).bootAtTime(1800009);for i in range(100):t.runNextEvent()

运行该脚本,得到输出如下:

在这里插入图片描述

这些调试输出语句,都对应着 RadioCountToLedsC.nc 文件里的 dbg 调试语句,前面打印的网络拓扑语句是在 test.py 文件里 print " ", s[0], " ", s[1], " ", s[2]; 这条语句输出的


总结

一份简单的配置指南