因为课题中涉及到了802.11无线通信,而bianchi于2000年发布的关于802.11的论文也很经典,故做了编程和仿真。其中的仿真基于DCF,参考了下面作者的一部分见解。该博客是为了自己以后方便查看而整理,请多多包涵!
原文出处:https://blog.csdn.net/rs_network/article/details/53667980
序言
前面我们已经讨论了802.11的性能,以及其理论模型,为了验证理论模型,我们大致做一个仿真验证。本文主要介绍笔者在研究过程中写的一个简化版的仿真模型,至于NS2,NS3,OPNET这些,由于整体代码较为繁杂,所以我们不加以扩充。
求解Bianchi模型的理论性能
参考Bianchi模型中我们的叙述:802.11协议精读13:理论协议性能(Bianchi模型)。在之前的叙述中,我们已经说明,只要能够计算出发送概率,就可以一步一步推导出理论性能。而发送概率和冲突概率是一个fix-point问题,如下式:
以下我们简单介绍如何用Matlab的fsolve函数对此进行求解。
fslove函数是Matlab用来做方程求解的,与solve函数不同,前者是采用迭代法进行数值求解,后者是符号求解(即可以写出具体的求解表达式)。我们前面的方程实际上是不好直接写出表达式的,所以我们采用fslove函数进行求解。
x = fsolve(fun,x0,options)
fsolve的调用格式如上,其中fun是以句柄的形式写入的,x0是初值,options是一些具体的参数。比如:x = fsolve(@(x) sin(3*x),[1],optimoptions('fsolve','Display','iter')),其中句柄就是(@(x) sin(3*x)),初值是[1],求解的过程按照迭代的形式进行显示。
在具体求解Bianchi模型中的发送概率和冲突概率的时候,我们采用单独新建一个函数的形式来做函数句柄(一者这样表述方便,二者是中含有一个求和式,所以不好用一个函数形式进行表达)。
代码如下(在开源项目中都已经包含了这些文件),其中还可以看到声明了三个global参数,分别是CW窗口大小,回退次数,节点数目,这些在求解中实际上是以参数的形式传入的,不过由于这里参数的函数句柄用来做fsolve求解,所以就采用globe的形式进行传参。
具体调用如下(注:调用之前需要用global参数设置,,三个参数):
开源代码中我应该是用的alpha和beta的表述,实际意义是和,相同的。然后按照之前所列的公式,一步一步计算即可。
DCF仿真(FSM思路)
这一节我们讨论下协议仿真的思路,大部分802.11协议的仿真,或者说网络的仿真都是基于离散随机事件的,换言之是基于事件触发的仿真机制。为了一些特殊的仿真需要,即在时间轴上,具体执行某一个事件,仿真在这个事件执行过程中是否有别的事情发生,所以我们设置了一个基于时间触发的仿真机制,并用此机制来做802.11协议的仿真。
在具体的仿真中,我们采用有限状态机(Finite State Machine、FSM)的思路,即用时间触发来驱动每一个节点中的FSM。基于OOP的思想,我们将每一个节点封装成一个类。类中用一些成员变量来记录节点的一些参数和状态,比如SIFS,DIFS之类。并且同一个时间,节点只能处于一个发送状态。
如上图,总的是有一个时间轴(比如从到),然后有一个公共信道channel,信道有两种状态:idle和busy。
节点上还另外有一个计时器,当时间轴上的时间大于节点的定时的时候,那么节点执行一次状态机的切换(在节点类中,进行状态切换)。每一个单元时间(比如说),节点都做一次与时间轴同步时间的工作,比如说上图,第的时候,节点执行一次状态机切换(即IDLE_STATE往Backoff_STATE切换),然后执行DIFS和Backoff counter初始化的动作(具体见代码)。然后节点在第到DIFS时间内,节点只做时间同步的工作,不做状态切换。当第DIFS时间到达后,节点再将状态从Backoff STATE切换到SEND_STATE。
此时由于节点(Node)处于发送状态,那么就将信道至忙。其他的节点在执行过程中,会检测信道的状态,如果发现信道忙,那么其他的节点就什么都不做,仅仅空置这一个单位时间即可。其中每一个节点的FSM状态机设置如下:
IDLE_STATE:初始状态。若节点没有选择Backoff counter,那么随机选择一个。若有,就保持不变。并且每一次Backoff前执行(包含frozen后的Backoff)等待DIFS的动作。
BACKOFF_STATE:在DIFS时间后,节点转移到BACKOFF_STATE,在该状态下,节点每经过一个Slot,倒数一次Backoff counter。若在该Slot过程中,如果发现信道忙,则延迟该Slot的计时器,直到信道idle时,再继续倒数。当Backoff counter倒数至0时,转移到SEND_STATE。
SEND_STATE:当转移到该状态后,节点发送数据帧。即设置计时器为数据帧传输时间(包含ACK),并且将信道设置为busy状态。当传输完成后,即转移至RECEIVE_STATE。
RECEIVE_STATE:如果传输过程中,主时间轴没有发现冲突发生,并且这一次传输没有信道影响(通过概率判断),即代表本次传输成功。那么节点在RECEIVE_STATE状态内,记录发送成功一个数据帧,并将CW窗口大小至为(0,CWmin)。成功接收后,信道置为idle,节点状态转移至IDLE_STATE。
COLLISION_STATE:当SEND_STATE后,我们通过主时间轴上,判断是否有两个或者两个以上节点同时进入SEND_STATE。如果有的话,那么就意味着冲突发生,此时这些冲突节点都进入了COLLISION_STATE,并将其CW窗口大小翻倍(这个是在主时间轴上做的)。由于节点可能发送的数据包长度不同(虽然仿真默认是相同的),当其中最长的数据帧发送完之后,转入ACKTIMEOUT_STATE,并将系信道释放,即置为idle。ACKTIMEOUT_STATE:该状态由于没有成功发送数据包,所以不进行数据包个数上的记录,然后直接转移回IDLE_STATE。
以上就是一个仿真的基本思路,通过这种方法能够较为直观理解下,仿真DCF的随机接入机制。不过基于时间驱动的仿真缺点就在于耗时,笔者之前的一个版本是用matlab写的,就是因为运行时间原因,所以修改到C++上运行,以上仿真思路仅供参考,如有错误,还请见谅。