WSN中LEACH協(xié)議源碼分析

WSN中LEACH協(xié)議源碼分析分析(一)首先對(duì)wireless.tcl進(jìn)行分析,先對(duì)默認(rèn)的腳本選項(xiàng)進(jìn)行初始化:set opt(chan)Channel/WirelessChannelset opt(prop) Propagation/TwoRayGroundset opt(netif)Phy/WirelessPhyset opt(mac) Mac/802_11set opt(ifq) Queue/DropTail/PriQueueset opt(ll) LLset opt(ant) Antenna/OmniAntennaset opt(x) 0 ;# X dimension of the topographyset opt(y) 0 ;# Y dimension of the topographyset opt(cp) ""set opt(sc) "../mobility/scene/scen-670x670-50-600-20-2" ;# scenario fileset opt(ifqlen) 50;# max packet in ifset opt(nn) 51 ;# number of nodesset opt(seed) 0.0set opt(stop) 10.0 ;# simulation timeset opt(tr) out.tr ;# trace fileset opt(rp) dsdv ;# routing protocol scriptset opt(lm) "on" ;# log movement在這個(gè)wireless.tcl中設(shè)置了一些全局變量:## Initialize Global Variables#set ns_ [new Simulator]set chan [new $opt(chan)]set prop [new $opt(prop)]set topo [new Topography]set tracefd [open $opt(tr) w]$topo load_flatgrid $opt(x) $opt(y)$prop topography $topo這些初始化將在后面的使用中用到,該文件最重要的是創(chuàng)建leach節(jié)點(diǎn):創(chuàng)建方法如下:} elseif { [string compare $opt(rp) "leach"] == 0} { for {set i 0} {$i < $opt(nn) } {incr i} {leach-create-mobile-node $i}如果路由協(xié)議是leach協(xié)議,則在Uamps.tcl中調(diào)用leach-create-mobile-node方法創(chuàng)建leach節(jié)點(diǎn)。
將在第二小節(jié)講如何創(chuàng)建leach節(jié)點(diǎn)for {set i 0} {$i < $opt(nn) } {incr i} { $ns_ at $opt(stop). "$node_($i) reset"; //完成后,重置節(jié)點(diǎn)的應(yīng)用}$ns_ at $opt(stop).00000001 "puts \"NS EXITING...\" ; $ns_ halt"if { $opt(sc) == "" } {puts "*** NOTE: no scenario file specified." set opt(sc) "none"} else {puts "Loading scenario file..."source $opt(sc)puts "Load complete..."}ns在什么時(shí)候結(jié)束simulation,并告訴ns加載sc場(chǎng)景文件最后$ns_ run則ns就開始運(yùn)行了分析(二)上節(jié)對(duì)wireless.tcl進(jìn)行了簡(jiǎn)要的分析,接下來(lái)對(duì)Uamps.tcl腳本進(jìn)行分析set opt(Efriss_amp) [expr [expr 1.1 * $opt(RXThresh) * 16 * $PI * $PI] / \ [expr $opt(bw) * $opt(Gt) * $opt(Gr) * $l * $l]]# Etwo_ray_amp = RXThresh / (Rb Gt Gr ht^2 hr^2)set opt(Etwo_ray_amp) [expr 1.1 * $opt(RXThresh) / \ [expr $opt(bw) * $opt(Gt) * $opt(Gr) * \ $opt(ht) * $opt(ht) * $opt(ht) * $opt(ht)]]set opt(EXcvr) 50e-9 ;# Energy for radio circuitryset opt(e_bf) 5e-9 ;# Beamforming energy (J/bit)set opt(Esense) 0 ;# Sensing energy (J/bit)set opt(thresh_energy) 0.00 ;# Threshold for power adaptationset opt(Pidle) 0 ;# Idle power (W)set opt(Psleep) 0 ;# Sleep power (W)set initialized 0set rng_ [new RNG]#用于產(chǎn)生隨機(jī)數(shù) 首先往opt數(shù)組里面添加一些變量,并對(duì)這些變量進(jìn)行初化。
opt(Psleep) ,opt(Pidle), opt(thresh_energy)在ns-leach.tcl中使用到,這個(gè)是計(jì)算單位時(shí)間空閑所消耗的能量和休眠所消耗的能量這個(gè)腳本主要是創(chuàng)建leach節(jié)點(diǎn):if {$initialized == 0} { #remove old trace sens_init set initialized 1 } # Remove old trace files. catch "eval exec rm [glob -nocomplain $opt(dirname)/TDMAschedule.*.txt]" catch "exec rm $opt(dirname)/$opt(filename).energy" catch "exec rm $opt(dirname)/$opt(filename).data" catch "exec rm $opt(dirname)/$opt(filename).alive" catch "exec rm $opt(dirname)/startup.energy" catch "exec rm $opt(dirname)/init.energy"如果沒(méi)有初始化過(guò),則將以前的跟蹤文件刪除,接著回到創(chuàng)建leach的函數(shù)中,創(chuàng)建節(jié)點(diǎn):if {$id != $opt(nn_)} { puts -nonewline "$id " #important set node_($id) [new MobileNode/ResourceAwareNode] } else { puts "($opt(nn_) == BS)" set node_($id) [new MobileNode/ResourceAwareNode $BS_NODE] }如果不是簇頭節(jié)點(diǎn)則將$opt(nn_)-1個(gè)節(jié)點(diǎn)設(shè)置為一般節(jié)點(diǎn),將$opt(nn_)設(shè)置為BS節(jié)點(diǎn)。
newMobileNode/ResourceAwareNode函數(shù)是在ns-ranode.tcl中分析完這個(gè)我們接下來(lái)分析newMobileNode/ResourceAwareNode這個(gè)函數(shù)set node $node_($id) if {$id != $opt(nn_)} { # Set initial node energy. if {$opt(eq_energy) == 1} { $node set-energy $opt(init_energy) $opt(thresh_energy) } else {由于eq-energy在leach-test中給定,將eq-energy=1;則每個(gè)節(jié)點(diǎn)都會(huì)對(duì)能量進(jìn)行初始化,設(shè)置節(jié)點(diǎn)的初始能量和門檻能量(個(gè)人理解,死亡能量)set high_e_nodes [list 97 19 12 87 8 22 83 55 34 72] if {[lsearch $high_e_nodes $id] == -1} { set E 2 } else { set E 200 } $node set-energy $E $opt(thresh_energy) set initf [open "$opt(dirname)/init.energy" a] puts $initf "$id\t$E" close $initf將不屬于list中的能量初始化能量設(shè)置為2,將屬于list中的能量設(shè)置為200.并將初始化能量寫到init.energy中,將節(jié)點(diǎn)id和節(jié)點(diǎn)初始能量寫進(jìn)去。
else { # Base station has an infinite amount of energy. $node set-energy 50000 $opt(thresh_energy) }節(jié)點(diǎn)為簇頭節(jié)點(diǎn),則將節(jié)點(diǎn)的初始化能量設(shè)置為50000,能量無(wú)限到此為止,創(chuàng)建節(jié)點(diǎn)完成并將每個(gè)節(jié)點(diǎn)的能量初始化完成下一節(jié)將分析ns-ranode.tcl腳本區(qū)分普通節(jié)點(diǎn)和簇頭節(jié)點(diǎn)的不同接下來(lái)是配置節(jié)點(diǎn)信道和跟蹤文件:$ns_ at 0.0 "$node_($id) start-app" ns在0的時(shí)候啟動(dòng)應(yīng)用,應(yīng)用在ns-ranode.tcl中分析分析(三)對(duì)前面兩個(gè)腳本進(jìn)行分析后,已經(jīng)創(chuàng)建好節(jié)點(diǎn)和設(shè)置好節(jié)點(diǎn)的初始能量,將opt(nn_)-1個(gè)節(jié)點(diǎn)設(shè)置為普通節(jié)點(diǎn),而將opt(nn_)設(shè)置為bs現(xiàn)在對(duì)ns-ranode.tcl進(jìn)行分析好了我們現(xiàn)在就開始分析這個(gè)腳本Class MobileNode/ResourceAwareNode -superclass Node/MobileNodeMobileNode/ResourceAwareNode instproc init argsset bs_node [lindex $args 0] eval $self next [lreplace $args 0 0] args由參數(shù)傳遞進(jìn)來(lái),若節(jié)點(diǎn)為簇頭節(jié)點(diǎn)則bs_node=1,調(diào)用父類初始化函數(shù)。
set ResourceManager_ [new ResourceManager] $ResourceManager_ Register [new Resource/NeighborResource] set energy [new Resource/Energy]$ResourceManager_ Register $energyResourceManager在ns-resouce-manager.tcl中定義的Resource/NeighborResource在ns-neighbor-resource中對(duì)能量進(jìn)行管理 # Create a new agent and attach it to the node if {$bs_node == 1} { set agent [new Agent/BSAgent] } else { set agent [new Agent/RCAgent] } set rca_agent_ $agent 普通節(jié)點(diǎn)的應(yīng)用為RCAgent,BS的應(yīng)用為BSAGgent。
并將應(yīng)用attch到node上 下面兩段看不明白,但是這兩段不影響看程序 set haslist [find_haslist [$self id]] if {$bs_node == 1} { set rca [new $opt(bsapp)] } else { set rca [new $opt(rcapp) $opt(mtype) $wantslist $haslist] } $ns_ attach-agent $self $agent $rca attach-agent $agent set rca_app_ $rca將bs節(jié)點(diǎn)的應(yīng)用設(shè)置為bsapp,普通節(jié)點(diǎn)的應(yīng)用設(shè)置為rcaapp,并將節(jié)點(diǎn)的服務(wù)設(shè)置為不同的服務(wù)bsapp在ns-app.tcl中opt(rcapp)定義在leach.tcl中,opt(mtype)定義在leach.tcl中set opt(rcapp) "Application/LEACH" ;# Application typeset opt(ra_adv) [TxTime [expr $opt(hdr_size) + 4]] ;# Total time (s) for CH ADVs ;# Assume max 4(nn*%) CHsset opt(ra_adv_total) [expr $opt(ra_adv)*($opt(num_clusters)*4 + 1)] ;# RA Time (s) for nodes' join reqsset opt(ra_join) [expr 0.01 * $opt(nn_)] ;# Buffer time for join req xmittalset opt(ra_delay) [TxTime [expr $opt(hdr_size) + 4]] ;# Maximum time required to transmit ;# a schedule (n nodes in 1 cluster)set opt(xmit_sch) [expr 0.005 + [TxTime [expr $opt(nn_)*4+$opt(hdr_size)]]] ;# Overhead time for cluster set-upset opt(start_xmit) [expr $opt(ra_adv_total) + $opt(ra_join) + $opt(xmit_sch)]一般節(jié)點(diǎn)的應(yīng)用為Application/LEACH,最終sink節(jié)點(diǎn)的應(yīng)用為Application/BSApp。
通過(guò)前3次的分析得出,在分析1中定義變量,在分析2中創(chuàng)建leach節(jié)點(diǎn),在分析3中將節(jié)點(diǎn)的應(yīng)用綁定在節(jié)點(diǎn)上下面將分析leach的主要協(xié)議ns-leach.tcl腳本分析(四)完成前面3個(gè)腳本的分析,最后進(jìn)行ns-leach.tcl腳本的分析.首先我們看下這個(gè)腳本要使用的有哪些功能.Application/LEACH instproc init args {} 對(duì)leach進(jìn)行初始化,即構(gòu)造函數(shù)下面是leach的一些方法Application/LEACH instproc start {} {}Application/LEACH instproc getRandomNumber {llim ulim} {} 得到隨機(jī)數(shù)Application/LEACH instproc node {} {}Application/LEACH instproc nodeID {} {}Application/LEACH instproc mac {} {}Application/LEACH instproc getX {} {}Application/LEACH instproc getY {} {}Application/LEACH instproc getER {} {}Application/LEACH instproc GoToSleep {} {} 節(jié)點(diǎn)進(jìn)行休眠Application/LEACH instproc WakeUp {} {}節(jié)點(diǎn)醒來(lái)Application/LEACH instproc setCode code {}{}Application/LEACH instproc checkAlive {} {} 節(jié)點(diǎn)是否存活A(yù)pplication/LEACH instproc isClusterHead? {} {} 判斷是否是簇頭節(jié)點(diǎn)Application/LEACH instproc hasbeenClusterHead? {} {} 判斷是否成為過(guò)簇頭節(jié)點(diǎn)Application/LEACH instproc hasnotbeenClusterHead {} {}hasbeench=0不是簇頭節(jié)點(diǎn)Application/LEACH instproc setClusterHead {} {} 設(shè)置為簇頭節(jié)點(diǎn)Application/LEACH instproc unsetClusterHead {} {} 設(shè)置為非簇頭節(jié)點(diǎn)Application/LEACH instproc decideClusterHead {} {} 決定簇頭節(jié)點(diǎn),非常重要Application/LEACH instproc advertiseClusterHead {} {} 廣播簇頭節(jié)點(diǎn)Application/LEACH instproc findBestCluster {} {} 找到最佳簇Application/LEACH instproc informClusterHead {} {} 通知簇頭節(jié)點(diǎn)Application/LEACH instproc createSchedule {} {} 創(chuàng)建TDMA調(diào)度接收功能:Application/LEACH instproc recv {args} {}Application/LEACH instproc recvADV_CH {msg} {}Application/LEACH instproc recvJOIN_REQ {nodeID} {}Application/LEACH instproc recvADV_SCH {order} {}Application/LEACH instproc recvDATA {msg} {}發(fā)送功能:Application/LEACH instproc sendData {} {}Application/LEACH instproc send {mac_dst link_dst type msgdata_size dist code} {}Application/LEACH instproc send_now {mac_dst link_dst type msg data_size dist code} {}Application/LEACH instproc SendDataToBS {} {}Application/LEACH instproc SendMyDataToBS {} {}分析(五)由leach 分析三可知,一般節(jié)點(diǎn)的應(yīng)用為Application/LEACH。
則現(xiàn)在就是如何選擇簇頭節(jié)點(diǎn)和設(shè)置門檻值我們接下來(lái)分析leach 分析4中紅色的方法在leach 分析3中創(chuàng)建一個(gè)Application/LEACH對(duì)象則就會(huì)自動(dòng)調(diào)用start方法Application/LEACH instproc start {} { [$self mac] set node_num_ [$self nodeID] $self decideClusterHead $self checkAlive }在這個(gè)方法中會(huì)調(diào)用decideClusterHead和checkAlive方法Application/LEACH instproc GoToSleep {} { global opt ns_ $self instvar begin_idle_ begin_sleep_ [[$self node] set netif_(0)] set sleep_ 1 # If node has been awake, remove idle energy (e.g., the amount of energy # dissipated while the node is in the idle state). Otherwise, the node # has been asleep and must remove sleep energy (e.g., the amount of # energy dissipated while the node is in the sleep state). if {$begin_idle_ > $begin_sleep_} { set idle_energy [expr $opt(Pidle) * [expr [$ns_ now] - $begin_idle_]] [$self getER] remove $idle_energy } else { set sleep_energy [expr $opt(Psleep) * [expr [$ns_ now] - $begin_sleep_]] [$self getER] remove $sleep_energy } set begin_sleep_ [$ns_ now] set begin_idle_ 0}分析(六)在start中調(diào)用下面這個(gè)方法。
Application/LEACH instproc decideClusterHead {} { global ns_ chan opt node_ $self instvar alive_ TDMAschedule_ $self instvar begin_idle_ begin_sleep_ # Check the alive status of the node. If the node has run out of # energy, it no longer functions in the network. set ISalive [[[$self node] set netif_(0)] set alive_] if {$alive_ == 1} { if {$ISalive == 0} { puts "Node [$self nodeID] is DEAD!!!!" $chan removeif [[$self node] set netif_(0)] set alive_ 0 set opt(nn_) [expr $opt(nn_) - 1] set ISalive [[[$self node] set netif_(0)] set alive_] #從網(wǎng)絡(luò)接口netif中查看當(dāng)前節(jié)點(diǎn)狀 況如果節(jié)點(diǎn)存活,但是節(jié)點(diǎn)能量耗光,則$chan removeif [[$self node] set netif_(0)]將節(jié)點(diǎn)信道中移出,并將節(jié)點(diǎn)設(shè)置為死亡。
節(jié)點(diǎn)的總數(shù)目減少一個(gè)if {$opt(eq_energy) == 1} { # # Pi(t) = k / (N - k mod(r,N/k)) # where k is the expected number of clusters per round # N is the total number of sensor nodes in the network # and r is the number of rounds that have already passed. # set nn $opt(nn_) if {[expr $nn - $opt(num_clusters) * $round_] < 1} { set thresh 1 } else { set thresh [expr double($opt(num_clusters)) / \ [expr $nn - $opt(num_clusters) * $round_]] # Whenever round_ is 0, all nodes are eligible to be cluster-head. if {$round_ == 0} { $self hasnotbeenClusterHead } } # If node has been cluster-head in this group of rounds, it will not # act as a cluster-head for this round. if {[$self hasbeenClusterHead?]} { set thresh 0 } } else { # # Pi(t) = Ei(t) / Etotal(t) * k # where k is the expected number of clusters per round, # Ei(t) is the node's current energy, and Etotal(t) is the total # energy from all nodes in the network. # set Etotal 0 # Note! In a real network, would need a routing protocol to get this # information. Alternatively, each node could estimate Etotal(t) from # the energy of nodes in its cluster. for {set id 0} {$id < [expr $opt(nn)-1]} {incr id} { set app [$node_($id) set rca_app_] set E [[$app getER] query] set Etotal [expr $Etotal + $E] } set E [[$self getER] query] set thresh [expr double([expr $E * $opt(num_clusters)]) / $Etotal] }上面是對(duì)thresh的計(jì)算,當(dāng)(N - k mod(r,N/k))<1,則將thresh設(shè)置為1,否則節(jié)點(diǎn)thresh=k / (N - k mod(r,N/k)),每個(gè)節(jié)點(diǎn)在一個(gè)1/p中都要成為簇頭節(jié)點(diǎn)一次。
p=簇頭節(jié)點(diǎn)占所有節(jié)點(diǎn)的比例,在r=0的時(shí)候每個(gè)節(jié)點(diǎn)都有機(jī)會(huì)吃呢更為簇頭節(jié)點(diǎn)如果節(jié)點(diǎn)成為過(guò)簇頭節(jié)點(diǎn)則thresh=0,則這個(gè)節(jié)點(diǎn)在1/p輪后才可以成為簇頭節(jié)點(diǎn) if {[$self getRandomNumber 0 1] < $thresh} { puts "$nodeID: *******************************************" puts "$nodeID: Is a cluster head at time [$ns_ now]" $self setClusterHead set random_access [$self getRandomNumber 0 $opt(ra_adv)] #opt(ra_adv) in leach.tcl $ns_ at [expr $now_ + $random_access] "$self advertiseClusterHead" } else { puts "$nodeID: *******************************************" $self unsetClusterHead }如果thresh>getRandomNumber,則節(jié)點(diǎn)成為簇頭節(jié)點(diǎn)。
然后調(diào)用advertiseClusterHead方法set next_change_time_ [expr $now_ + $opt(ch_change)] $ns_ at $next_change_time_ "$self decideClusterHead" $ns_ at [expr $now_ + $opt(ra_adv_total)] "$self findBestCluster"當(dāng)節(jié)點(diǎn)成為簇頭節(jié)點(diǎn),則節(jié)點(diǎn)調(diào)用 advertiseClusterHead方法 set chID [$self nodeID] set currentCH_ $chID pp "Cluster Head $currentCH_ broadcasting ADV at time [$ns_ now]" set mac_dst $MAC_BROADCAST set link_dst $LINK_BROADCAST set msg [list $currentCH_] set datasize [expr $BYTES_ID * [llength $msg]] # Send beacons opt(max_dist) meters so all nodes can hear. $self send $mac_dst $link_dst $ADV_CH $msg $datasize $opt(max_dist) $code_將該節(jié)點(diǎn)設(shè)置為簇頭節(jié)點(diǎn),設(shè)置當(dāng)前節(jié)點(diǎn)所處的簇號(hào)。
然后發(fā)送數(shù)據(jù),廣播該節(jié)點(diǎn)為簇頭信息到全局網(wǎng)絡(luò)在$ns_ at [expr $now_ + $opt(ra_adv_total)] "$self findBestCluster"調(diào)用findBestCluster方法分析(七)當(dāng)簇頭發(fā)出了一個(gè)ADV類型的包時(shí),其他的節(jié)點(diǎn)會(huì)接收這個(gè)包,并會(huì)將發(fā)送這個(gè)包的簇頭的節(jié)點(diǎn)號(hào)按順序先后記錄在clusterChoices_中,還會(huì)計(jì)算每個(gè)簇頭到接收節(jié)點(diǎn)的距離并記錄在clusterDist_中這樣可以方便每個(gè)節(jié)點(diǎn)選簇的時(shí)候進(jìn)行比較具體的實(shí)現(xiàn)在ns-leach.tcl中的recvADV_CH函數(shù)中findBestCluster if [$self isClusterHead?] { # If node is CH, determine code and create a TDMA schedule. set dist_ $opt(max_dist) set currentCH_ $nodeID set myADVnum_ [[$self mac] set myADVnum_] # There are opt(spreading) - 1 codes available b/c need 1 code # for communication with the base station. set numCodesAvail [expr 2 * $opt(spreading) - 1] set ClusterCode [expr int(fmod($myADVnum_, $numCodesAvail)) + 1] $ns_ at [expr $now_ + $opt(ra_adv_total) + $opt(ra_join)] \ "$self createSchedule" } 如果這個(gè)節(jié)點(diǎn)是簇頭節(jié)點(diǎn),設(shè)置為當(dāng)前的簇頭, set numCodesAvail [expr 2 * $opt(spreading) - 1] set ClusterCode [expr int(fmod($myADVnum_, $numCodesAvail)) + 1]這兩句沒(méi)看懂在一個(gè)隨機(jī)時(shí)間后調(diào)用createSchedule,節(jié)點(diǎn)根據(jù)自己收到的clusterChoices,選擇最近的簇頭作為簇頭節(jié)點(diǎn)。
選擇好簇頭節(jié)點(diǎn)就informClusterHead發(fā)送完數(shù)據(jù)就進(jìn)入休眠狀態(tài)Application/LEACH instproc createSchedule {} { global ns_ opt ADV_SCH MAC_BROADCAST BYTES_ID $self instvar clusterNodes_ TDMAschedule_ $self instvar dist_ code_ now_ beginningE_ set numNodes [llength $clusterNodes_] set chID [$self nodeID] if {$numNodes == 0} { set xmitOrder "" puts "Warning! There are no nodes in this cluster ($chID)!" $self SendMyDataToBS } else { # Set the TDMA schedule and send it to all nodes in the cluster. set xmitOrder $clusterNodes_ set msg [list $xmitOrder] set spreading_factor $opt(spreading) set datasize [expr $spreading_factor * $BYTES_ID * [llength $xmitOrder]] pp "$chID sending TDMA schedule: $xmitOrder at time [$ns_ now]" pp "Packet size is $datasize." set mac_dst $MAC_BROADCAST set link_dst $chID $self send $mac_dst $link_dst $ADV_SCH $msg $datasize $dist_ $code_ } set TDMAschedule_ $xmitOrder set outf [open $opt(dirname)/TDMAschedule.$now_.txt a] puts $outf "$chID\t$TDMAschedule_" close $outf set outf [open $opt(dirname)/startup.energy a] puts $outf "[$ns_ now]\t$chID\t[expr $beginningE_ - [[$self getER] query]] " close $outf}在recvADV_SCH給每個(gè)節(jié)點(diǎn)一個(gè)時(shí)間間隙發(fā)送數(shù)據(jù) set frame_time_ [expr [expr 5 + [llength [join $order]]] * $opt(ss_slot_time)] set xmitTime_ [expr $opt(ss_slot_time) * $ind] set end_frm_time_ [expr $frame_time_ - $xmitTime_] set xmitat [expr [$ns_ now] + $xmitTime_] pp "$nodeID scheduled to transmit at $xmitat. It is now [$ns_ now]." if {[expr $xmitat + $end_frm_time_] < \ [expr $next_change_time_ - 10 * $opt(ss_slot_time)]} { $ns_ at $xmitat "$self sendData"具體實(shí)現(xiàn)方法看不太懂。