TinyOS的链路质量估计器的实现主要在tos/lib/net/le这个文件夹中,下面首先看一下链路质量估计器的头文件
LinkEstimator.h:
#define NEIGHBOR_TABLE_SIZE 10
enum {
NUM_ENTRIES_FLAG = 15,
};
typedef nx_struct linkest_header {
nx_uint8_t flags;
nx_uint8_t seq;
} linkest_header_t;
typedef nx_struct neighbor_stat_entry {
nx_am_addr_t ll_addr;
nx_uint8_t inquality;
} neighbor_stat_entry_t;
在头文件一开始首先是规定了链路质量估计表的大小,这个大小决定了每个节点只能有10个链路质量相对较小的邻居节点,如果如果有新的节点进来那就需要从邻居表中按照一定要求剔除一个节点。
NUM_ENTRIES_FLAG这个枚举规定了LEEP帧帧头的格式(源码分析2),根据要求,LEEP头部除了seq还有描述entry个数的字段以及保留字段,在帧头部的第一个字节中,0-3位是描述entry个数的字段,4-7是保留字段(全是0),所以这个变量规定了LEEP帧的第一个字节的格式。在实际实现的时候所有的flag都要和NUM_ENTRIES_FLAG这个枚举做一个与操作。
linkest_header_t这个数据类型规定了数据帧的头部结构。
neighbor_stat_entry_t这个数据类型规定了LEEP帧尾部的其中一个entry的结构。
typedef nx_struct linkest_footer {
neighbor_stat_entry_t neighborList[1];
} linkest_footer_t;
这个数据类型定义了整个LEEP帧的尾部,LEEP帧的尾部实际上就是一个neighbor_stat_entry_t数组,这个数组一开始大小为1。
enum {
VALID_ENTRY = 0x1,
MATURE_ENTRY = 0x2,
INIT_ENTRY = 0x4,
PINNED_ENTRY = 0x8
};
这个是一系列枚举,主要应用在链路质量估计表中,来表示每一个条目的状态,值得一提的是状态是可以叠加的,可以通过或操作让状态叠加,通过非操作让状态取消,比如一开始状态是有效,那就flag |= VALID_ENTRY,然后这个时候要让状态即有效又绑定,那么flag |= PINNED_ENTRY,如果这个时候要让状态无效那就flag &= ^VALID_ENTRY,这样子就可以了。在链路质量估计表中每个条目的状态的作用这里不展开。
typedef struct neighbor_table_entry {
am_addr_t ll_addr;
uint8_t lastseq;
uint8_t rcvcnt;
uint8_t failcnt;
uint8_t flags;
uint8_t inage;
uint8_t outage;
uint8_t inquality;
uint8_t outquality;
uint16_t eetx;
uint8_t data_success;
uint8_t data_total;
} neighbor_table_entry_t;
这个地方是链路质量估计表每一个条目的结构,链路质量估计表其实也是一个节点的邻居表。am_addr_t也是一个自定义数据类型,本质是unit16_t,是一个邻居节点的地址。lastseq是从这个邻居节点最近一次接收到LEEP帧的时候LEEP帧的序号。rcvcnt是从这个邻居节点收到的所有LEEP帧的个数,每次收到一个LEEP帧的时候这个值都会自增。flags是这个这个条目的状态,使用上面的那几个枚举赋值。
inage和outage在实现部分看起来没有什么实质性作用,可能是为了防止一些低概率问题所设定的字段,这个后面会单独讨论。
inquality是和这个节点之间的链路上的入站质量。outquality是对应的出站质量。这两个值得取值范围为0-255,0代表连接质量很差,255代表连接质量很好。eetx以及两个data开头的字段下面展开介绍。
ETX---通向根节点的链路开销
说道eetx就不得不提ETX,实际上ETX才是CTP的主角,eetx=pathETX+1,为什么是这样其实就是和数学上的规定有关系。
ETX是一个节点直接或者间接通向根节点的开销,数字越大意味着开销越大。
ETX的计算需要两个值,一个是邻居节点与根节点的ETX,还有一个就是这个节点与邻居节点的ETX(也被称作pathETX),其中pathETX是通过和这个邻居节点的入站以及出站质量得到的,质量越好,ETX越小(怎么换算本篇不展开),其中根节点的ETX为0(因为就是它自身,不需要额外开销)。举个例子,现在有三个节点root,A,B,A是root的子节点,B是A的子节点,假设A与root之间的pathETX是20,那么A的ETX就是20;如果B和A之间的pathETX是10,那么B的ETX就是30;如果B还有子节点,那就自此类推。路由引擎在选择父节点的时候,会将把所有记录的邻居节点对应的ETX算出来(邻居节点的ETX和自己与邻居节点时间的pathETX相加),然后选出一个ETX最小的,把对应的邻居节点当成父节点。
基于数据包发送层的链路质量估计
LEEP帧是一种链路估计的方式,基于数据包的发送成功率也是另一种方式,前期靠LEEP,在拓扑结构稳定的时候主要依靠基于数据包发送成功率的链路质量估计。
要使用这种链路估计要求链路层支持ACK帧。转发引擎发送数据包之后转发引擎会看有没有ACK帧,如果有ACK帧,那就data_success自增。data_total会在每一次转发的时候都会自增。然后定时会算出一个基于数据包的链路质量估计值,并更新链路质量估计表。