SMAC源码分析

先调用SMAC的构造函数,里面有mhGene_.sched(t),经过t秒后超时,超时后调用handleGeneTimer()函数。

setMySched(): 把MAC state设为0,numSched_设为1,把调度表的txSync设为1,说明要发送同步帧。如果没有收到同步帧,就要自己建立调度表,调度表的状态加1,并且把调度表的同步节点设置为当前节点。经过listenTime_+index*10的时间后CounterTimer超时,调用handleCounterTimer()函数。

handleCounterTimer():首先要保证该调度表的节点数大于0,如果改CounterTimer的value是sleepTime_的话,说明现在睡眠已经结束了。如果现在radioState不能发送或接收的话,给CounterTimer设置ListenTime_的时间,超时后调用handleCounterTimer()函数。如果MACstate可以发送并且该调度表的txSync_为1的话,把syncSched_设置为该调度表的id,同时开始竞争退避,当计数器减到0的时候调用handleCSTimer()函数。

adaptiveListen():如果节点处于睡眠状态,就把节点唤醒,总之要保证节点是醒着的。然后判断节点是否有数据要发送,如果有数据发送的话调用checkToSend()这个函数发送数据。同时设置了adapTime,节点在adapTime这段时间后定时器超时,调用handleAdaptiveListenTimer()函数。如果是收到同步帧的话,经过同步帧中的sleepTime来设置CounterTimer,并且经过sleepTime后调用handleCounterTimer()函数。然后把我的同步节点设为收到的同步帧中指示的同步节点。把0号调度表的节点数增加1,0号调度表的同步节点设置为收到的同步帧中指示的同步节点,并且调度表的状态数加1.然后把发同步帧的节点加到我的同步列表中。0号邻居列表的nodeId等于发同步帧的节点地址,schedId设为是0号调度表。active设为1,state设为同步帧的state。把numNeighb_设为1.

handleAdaptiveListenTimer():如果MAC层状态是idle并且不是处于发送间隔等待状态(sifs)并且调度表现在是处于睡眠状态,那么节点开始进入睡眠。

checkToSend():要保证txRequest等于1或者syncFlag_等于1,否则直接返回0.如果radiostate不处于睡眠和idle的状态,无法发送数据,直接返回0.如果MAC state不在SLEEP,IDLE,或WAIT_DATA状态,也无法发送数据,返回0.如果NavTimer和NeighNavTimer不忙(停止计时了)并且MACstate是SLEEP或者IDLE的话,把MACstate设为CR_SENSE,开始进行竞争退避。当dis+cw_时间后CSTimer将超时,将调用handleCsTimer()这个函数。

handleCsTimer():首先判断howToSend的类型,如果是BCASTSYNC,则调用sendSYNC()这个函数,并设MACstate为IDLE。如果是BCASTDATA,则调用startBcast()这个函数,如果是UNICAST,则调用startUcast()这个函数。

sendSYNC():首先进行一系列包的设置,包括syncNode,sleepTime等。sleepTime为这个调度表的TimeToSleep减去同步帧发送需要的时间。然后调用checkRadio()这个函数,如果radio state是IDLE或者SLEEP的话,则调用transmit()函数。

transmit():把radiostate设置为RADIO_TX,向物理层发送这个包,downtarget->recv(p->copy(),this).并且在包的传送时间后sendTimer超时,调用handleSendTimer()这个函数。物理层收到这个包时,调用phy.cc中的recv()函数,如果packet的direction是DOWN的话,调用wireless-phy.cc中的sendDown()函数,在这个函数中调用channel->recv()函数,把数据包传到信道上去。

startBcast():首先调用checkRadio()这个函数,然后调用transmit()这个函数,广播数据。

startUcast():首先进行初始化,如sendAddr进行接收节点的地址,然后调用sendRTS()这个函数,如果调用成功,MAC state设置为WAIT_CTS.

sendRTS():首先是RTS包的设置,如srcAddr,dstAddr,duration等。duration为接收节点收到RTS后,到整个数据发完还需要的时间。然后调用checkRadio()函数,成功的话调用transmit()函数。

handleSendTimer():先把radiostate设为IDLE,tx_active设为0.然后判断数据包的类型,如果是RTS就调用sentRTS()这个函数,如果是CTS就调用sentCTS()函数,如果是DATA调用sentDATA()函数,如果是ACK就调用sentACK()函数,如果是SYNC就调用sentSYNC()函数。

sentSYNC():把自己同步的那个簇里的调度表的txSync设为0,numPeriods设为SYNCPERIOD。这里要说明一下,每个簇都维护一张调度表,内容如下:
struct SmacSchedTable { 
  int txSync;  // flag indicating need to send sync 
  int txData;  // flag indicating need to send data 
  int numPeriods; // count for number of periods 
  int numNodes;  // number of nodes on this schedule
  int syncNode;  // the node who initialized this schedule
  int chkSched; // flag indicating need to check numNodes
}; 
sentRTS():设置CTS timeout时间,超时了就调用handleGeneTimer()这个函数,同时把这个包free。

handleGeneTimer():首先判断numSched_是否为0,如果为0则说明目前没有调度表,节点就建立一个调度表。
如果MAC state是WAIT_CTS的话,则说明CTStimeout。这时重传次数加1,并设置MAC state为IDLE。此时要判断自己簇是否进入睡眠了,如果进入睡眠了,自己也先睡,等到时候醒来再发送。如果重发次数超过了限定,则设置MAC state为IDLE,并调用txMsgDone()函数告诉上层发送已经完毕,要求上层再把数据传下来。
如果MAC state是WAIT_ACK的话,说明发送了DATA但是没有收到ACK,此时要延长发送时间和次数,并调用updateNeighNav()这个函数,告诉邻居节点neighNav_长度要增加。如果此时neighNAV_已经很小了,则发送失败。调用txMsgDone()函数,并设置MAC state为IDLE。否则调用sendDATA()函数。
如果状态是DATA_SENSE1的话,说明节点收到了发给其他节点的RTS,继续等待waitCtrl的时间,进入状态DATA_SENSE2
如果状态是DATA_SENSE2的话,设置状态为IDLE,同时判断自己的簇是否处于睡眠了,如果是,自己也就睡了。

sentDATA():先判断howToSend的类型,如果是广播的话,把MAC state设为IDLE,把本簇调度表的txData置0,把txRequest置0,并调用txMsgDone()函数。并判断本簇是否进入睡眠了,如果是,自己也睡了。如果是单播的话,调用updateNeighNav()函数,并设置ACKtimeout时间,如果超时则调用handleGeneTimer()函数。

sentACK():调用updateNeighNav()函数。

recv(DOWN):MAC是从类connect继承而来,应用层产生的数据通过网络层,然后在MAC层通过recv()函数把数据发送到物理层去。当一个节点的物理层接收到数据时,MAC层的recv()函数同样也被调用到了。所以recv()函数检查包头的direction段,如果direction是Down的话,说明数据包来自上层,然后这个packet就被送到发送函数sendMsg()被发送出去。
recv(UP):如果数据包是从低层传来的(network interface),如果radiostate是RADIO_TX的话,此时不能接收数据包,把这个packet的error flag置1.经过一段时间后调用handleRecvTimer()这个函数。如果MACstate是CR_SENSE的话,取消CsTimer的计时。如果radiostate是RADIO_RX的话,说明已经在接收数据了,判断新接收到的包的功率和旧的包的比值,如果比值小于捕获门限的话,新接收到的包就被ignored,调用capture()函数。如果这两个包的能量很相近,就调用collision()函数。
如果radioState是RADIO_SLEEP的话,把radioState设为RADIO─RX,并且经过txtime(p)的时间后RecvTimer超时,调用handleRecvTimer()函数。例如节点A要给节点B发送RTS,节点B的物理层收到数据后,调用phy.cc中的recv()函数,这个函数调用了wireless-phy.cc中的sendUp()函数,并用uptarget_->recv(p,(Handler*) 0);来调用MAC层的recv()函数。至于downtarget_和uptarget_是如何逐层绑定的,可以参看mobilenode.tcl文件。

sendMsg():设置callback_,同时判段发送的方式,如果是广播的话,调用bcastMsg()函数,如果是单播的话,调用unicastMsg()函数。

bcastMsg():把txRequest_设为1,同时把每个节点数大于0的簇的调度表中的txData设为1.有几个簇就要广播几次。
unicastMsg():首先在自己的邻居列表中寻找这个节点,如果找到的话,把dataSched_设为这个接收节点邻居列表中的schedId.
struct NeighbList { 
  int nodeId; 
  int schedId;
#ifdef JOURNAL_PAPER
  int active; //flag indicating the node is active recently
  int state; // flag indicating the node has changed schedule
#endif 
}; 
并把接收节点所在簇的调度表的txData设为1.把txRequest设为1。

handleRecvTimer():如果macstate是SLEEP的话,先要判断是不是发生了冲突,如果是的话,把这个包丢弃。并把冲突标志清0。并调用updateNav()函数,增加eifs的时间。如果macstate是CR_SENSE的话,现在因为冲突了,所以先睡眠。不是CR_SENSE的话就把radioState_设为IDLE。如果没有发生冲突的话,把包丢弃(因为睡眠时不能接收)。把radioState_设为RADIO_SLEEP.如果radioState是RADIO_TX的话,直接把包丢弃。如果不是在RADIO_SLEEP和RADIO_TX状态,如果发生了冲突,还是一样处理。如果收到的包有错误,把包丢弃,同时调用updateNav()函数,增加eifs时间。如果MACstate是CR_SENSE的话,进入睡眠,否则设radioState_为RADIO_IDLE。如果上述情况都没有发生,说明包是正确接收了,所以设置radioState_为RADIO_IDLE,同时判段收到的包的类型。如果是DATA,就调用handleDATA()函数,如果是RTS,就调用handleRTS()函数,如果是CTS就调用handleCTS()函数,如果是SYNC就调用handleSYNC函数。无论包是否正确接收,最后总是把pktRx_清0.

handleSYNC():这个函数是smac.cc的难点,主要讲了调度表是如何形成和更新的。如果numSched_为0,说明目前没有簇,则调用setMySched()函数。如果numSched_为0和globalSchedule为1的话,如果0号调度表的syncNode比同步帧的syncNode要大,并且接收节点的sendSYNC为0,调用setMysched()函数。(这里我不是很懂)。如果本节点的邻居节点数为0的话,调用setMySched()函数,follow这个同步帧的调度表。然后设置MAC state为IDLE。然后检查发送者是不是在我的邻居列表上,如果在邻居列表中有这个发送节点并且节点的state 已经发生了变化,记录下这个节点的id,放在nodeId中,并记录下这个节点的簇的标号,放在schedId中。此时判断邻居列表中该节点的state是否等于收到的同步帧的state,如果相等,更新调度表,把CounterTimer的时间设为同步帧的sleepTime,并把邻居列表该节点的active置1。globalSchedule_在初始化时设为0,说明不用globalschedule形式,所以在这里globalSchedule_等于1才能执行的代码就不解释了。如果邻居列表中该发送节点的state和收到的同步帧的state不一样的话,如果改发送节点所在的簇中numNodes等于1并且txRequest等于1的话,把该调度表的chkSched置1.否则把该节点调度表的numNodes减1。所以现在是一个新节点或者一个老节点转到一个新的调度表上了。当然也有可能是一个节点转到一个已知的调度表上去。所以要检查这个schedule是不是已知的。如果第i个调度表的numNodes大于0的话,如果调度表的timeToSleep和收到的同步帧的sleepTime误差在一定范围内,就采用同步帧的sleepTime。并且把第i个调度表的numNode加1,把scheId设为i。如果这是一个不知道的schedule的话,先找找看有没有已知的schedule其实已经空了。如果第i个schedule是空了(numNodes等于0),把这个新的调度表放到i号调度表中。把i号调度表的CounterTimer设为同步帧的sleepTime,syncNode设为同步帧的syncNode,因为变了调度表要向别的节点广播这个消息,所以把txSync置1,把numNodes置1,因为只有一个节点。并且把numSched加1.如果发送同步帧的节点不在我的邻居列表中,说明这是一个新的节点,如果不能在调度表中找到空的入口,说明已经达到了最大调度表数量,就不让这个节点加入。如果还有空的入口,就把这个节点加入到自己的邻居列表中。如果第i个邻居列表的state等于0,说明是空的,就把这个节点设为neighbList[i]。这里举个例子,如果节点A原来有B和C两个邻居节点,在A中的邻居列表集合中,B的信息在neighbList[0]中,C的信息在neighbList[1]中,并且这两个neighbList的state由0变为1.现在有一个新的节点D加入,则D的信息放在neighbList[2]。如果在邻居列表中没有找到空的入口,就drop这个节点。
如果是老的节点转到新的调度表的话,先看看有没有空的入口,如果调度表没有空的话,就把这个老的节点删掉。把这个节点所占有的neighbList中的state清0,同时把numNeighb_减1.如果找到空的入口的话,老的节点的neighbList的状态设置为收到的同步帧的状态。
有可能老的节点是从0号调度表上转过来的。先检查我是否是0号调度表的唯一成员了,如果是,调用checkMySched()函数。
下面一种情况是老的节点转到老的调度表中去。把nodeId的neighbList设为收到的同步帧的情况。

checkMySched():这个函数检查我是否是0号调度表中的唯一成员,如果是,需要切换并且跟随下一个可用的调度表。如果第i个调度表的numNodes大于0,就把i号调度表放到0号调度表中来,并且删掉i号调度表。此时0号调度表的节点数为原来i号调度表的节点数加上1.然后更新邻居列表,把i号neighbList的schedId设为0.

在程序开始,首先要对SMAC类进行初始话,其中有一条语句mhUpdateNeighb_.sched(SMAC_UPDATE_NEIGHB_PERIOD);其中SMAC_UPDATE_NEIGH_PERIOD设为50,就是说经过50秒后就会超时,调用handleUpdateNeighbTimer()函数,先判断txRequest是否为0,如果是0,说明不在发送,调用update_myNeighbList()这个函数,如果txRequest为1,就把updateNeighbList设为1,等发送结束后在更新邻居列表。

update_myNeighbList():先调用check_schedFlag()函数,然后调用update_neighbList()函数,把updateNeighbList清0,并把0号调度表的chkSched清0,因为在update_neighbList()函数中已经调用了checkMySched()这个函数。同时把txRequest清0

check_schedFlag():如果第i号调度表的chkSched为1的话,就把第i号节点的numNodes减1。

update_neighbList():如果第i号neighbList的state大于0的话,如果第i号neighbList的active不等于1,说明这个节点已经失效了。把schedId设为第i号neighbList的schedId,并且把这个调度表的numNodes减1.把第i号neighbList的state清0。也许这个节点是从0号调度表中过来的,检查我是否是 0号调度表的唯一成员,调用函数checkMySched()。然后再把UpdataNeighbTimer设为50,就是说经过50秒后超时,再次调用handleUpdateNeighbTimer()这个函数。

handleRTS():首先检查RTS是不是发给自己的,如果是的话,检查MAC state是否是IDLE或CR_SENSE并且nav等于0,如果成立,调用updateNeighNav()函数,设置邻居节点的nav,这个并不是真正地给邻居节点设置nav,只是相当于保存邻居节点的nav,这样就能知道邻居节点什么时候能够醒来。然后调用sendCTS()函数,并且把MACstate设为WAIT_DATA。如果RTS是发给别人的,如果MACstate是CR_SENSE,现在收到RTS了,就把MACstate设为DATA_SENSE1,并且调用updateNav()函数,设置自己的NAV。同时把GeneTimer设为timeWaitCtrl,如果时间超过了就说明CTS超时,调用handleGeneTimer()函数。

sendCTS():调用updateNeighNav()函数。
handleCTS():首先要检查是不是发给自己的,如果是,判断MAC state是不是WAIT_CTS并且发送CTS的节点是不是收到我发的RTS的节点,是的话,取消CTStimer,并且调用sendDATA()函数,同时把MACstate设为WAIT_ACK.如果CTS是发给别人的,调用updateNav()函数。如果MACstate在DATA_SENSE1或者DATA_SENSE2的话,取消CTStimer。然后使节点进入睡眠。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值