对GPSR代码的理解——gpsr_neighbor.cc

邻居列表类的实现

宏定义
#include "gpsr_neighbor.h"
#define PI 3.141593
#define MAX(a, b) (a>=b?a:b)
#define MIN(a, b) (a>=b?b:a)

初始化

初始化当前节点:my_id_、my_x_、my_y_分别代表当前节点的id标识以及地理位置信息,初始化阶段节点尚不存在。
初始化邻居列表:首、尾指针均指向空,并将邻居列表中表项数量归零。
关于构造函数参照此文章 click here
关于结构体指针参照此文章click here

GPSRNeighbors::GPSRNeighbors(){
  my_id_ = -1;
  my_x_ = 0.0;
  my_y_ = 0.0;

  head_ = tail_ = NULL;
  nbSize_ = 0;
}

对象使用完毕后,释放所有表项。

关于析构函数参照此文章click here

GPSRNeighbors::~GPSRNeighbors(){
  struct gpsr_neighbor *temp = head_;
  while(temp){
    temp = temp->next_;
    free(head_);
    head_ = temp;
  }
}

以下为对gpsr_neighbor.h文件中类GPSRNeighbors成员函数的定义


计算两节点(ax,ay)与(bx,by)之间的距离并返回计算结果result。

对GPSR代码的理解——gpsr_neighbor.cc_第1张图片

double
GPSRNeighbors::getdis(double ax, double ay, double bx, double by){
  double tempx = ax - bx;
  double tempy = ay - by;

  tempx = tempx * tempx;
  tempy = tempy * tempy;

  double result = sqrt(tempx + tempy);
  return result;
}

返回邻居节点数量nbSize_。
int
GPSRNeighbors::nbsize(){
  return nbSize_;
}

更新当前节点以及位置信息
void
GPSRNeighbors::myinfo(nsaddr_t mid, double mx, double my){
  my_id_ = mid;
  my_x_ = mx;
  my_y_ = my;
}

获取邻居

作用是根据所给出的id来返回对应的邻居节点。从邻居列表的第一项开始遍历邻居表,通过比较得到与所给id相符合的表项并返回,若表中不存在符合要求的表项,返回NULL。

struct gpsr_neighbor*
GPSRNeighbors::getnb(nsaddr_t nid){
  struct gpsr_neighbor *temp = head_;
  while(temp){
    if(temp->id_ == nid){
      if((GPSR_CURRENT - temp->ts_) < DEFAULT_GPSR_TIMEOUT)
		return temp;
      else {
		delnb(temp); //if this entry expire, delete it and return NULL
		return NULL;
      }
      return temp;
    }
    temp = temp->next_;  //遍历邻居表
  }
  return NULL;
}

向表中添加一个可能的邻居节点。

根据getnb()的返回结果判断邻居列表中是否已经存在id为 nid 的表项:

  • 若不存在,则代表该列表有了新的邻居,根据接收到的节点id、位置信息等来更新表项信息,并把它加入到邻居列表的最后(普通的链表插入程序)。
  • 若存在,表示该邻居列表中已存在对应表项,不做添加,但是对该邻居节点的位置信息、最近一次收到hello报文的时间进行更新。
void
GPSRNeighbors::newNB(nsaddr_t nid, double nx, double ny){
  struct gpsr_neighbor *temp = getnb(nid);  //temp指向id为nid的节点

  if(temp==NULL){ //it is a new neighbor
    temp=(struct gpsr_neighbor*)malloc(sizeof(struct gpsr_neighbor));
    temp->id_ = nid;
    temp->x_ = nx;
    temp->y_ = ny;
    temp->ts_ = GPSR_CURRENT;
    temp->next_ = temp->prev_ = NULL;

    if(tail_ == NULL){ //the list now is empty
      head_ = tail_  = temp;
      nbSize_ = 1;
    }
    else { //now the neighbors list is not empty
      tail_->next_ = temp;
      temp->prev_ = tail_;
      tail_ = temp;
      nbSize_++;
    }
  }
  else { //it is a already known neighbor
    temp->ts_ = GPSR_CURRENT;
    temp->x_ = nx; //the updating of location is allowed
    temp->y_ = ny;
  }
}

根据所提供id删除邻居列表中的记录。

判断该id对应的表项在邻居列表中是否已存在,若不存在,则什么也不做;若存在,调用delnb(struct gpsr_neighbor *)给这个表项删掉。

void
GPSRNeighbors::delnb(nsaddr_t nid){
  struct gpsr_neighbor *temp = getnb(nid);
  if(temp==NULL) return;
  else delnb(temp);
}

直接删除记录。

首先是根据该链表结点O是否为头结点(它的前指针是否指向NULL)来判断其是否为链表的头结点。

  • 若是,就直接将头指针指向该结点的下一结点M。这个时候需要考虑到的是尾指针的问题,当链表中只有被删掉的这一结点O,则要将尾指针指向NULL,否则要记得将此时的头结点M的前向指针指向NULL。最后释放这一独立结点。
  • 若不是,就直接将被删除结点O的前一结点N的后向指针指向该结点的下一结点M。而在该被删除结点O为尾结点的情况下,需将该链表的尾指针指向结点N,若不是,记得将结点M的前向指针指向N(保证双向链表不断开)。
    [整这么多,无非就是链表操作- -!]
void
GPSRNeighbors::delnb(struct gpsr_neighbor *nb){
  struct gpsr_neighbor *preffix = nb->prev_;

  if(preffix == NULL){ 
    head_ = nb->next_;
    nb->next_ = NULL;
    if(head_ == NULL)
      	tail_ = NULL;
    else 
    	head_->prev_ = NULL;
    free(nb);
  }
  else {
    preffix->next_ = nb->next_;
    nb->prev_ = NULL;
    if(preffix->next_ == NULL)
     	 tail_ = preffix;
    else 
    	(preffix->next_)->prev_ = preffix;
    free(nb);
  }

  nbSize_--;

}

删除所有超时的记录。

比较邻居列表中节点的停留时间(当前时间与上次接收到hello消息的时间差)与生存时间,若已经超出规定的生存时间,则将该节点在邻居列表中删去(这里就不管前向指针了吗?不太清楚),若未超出,则继续检查下一节点。

void
GPSRNeighbors::delalltimeout(){
  struct gpsr_neighbor *temp = head_;
  struct gpsr_neighbor *dd;
  while(temp){
    if((GPSR_CURRENT - temp->ts_) >= DEFAULT_GPSR_TIMEOUT){
      dd = temp;
      temp = temp->next_;
      delnb(dd);
    }
    else temp = temp->next_;
  }

}

贪婪转发。

首先调用函数getdis()获取当前节点与目的节点的距离mindis,然后遍历邻居列表,依次比较列表中每个节点和目的节点的距离,并用更小的distance去更新mindis。直到最后便得到邻居列表中距离目的节点距离最小的节点。(这时候应该是若收到的返回值nexthop=-1,就代表要开始周边转发了?)

nsaddr_t
GPSRNeighbors::gf_nexthop(double dx, double dy){
  struct gpsr_neighbor *temp = head_;
  //initializing the minimal distance as my distance to sink
  double mindis =getdis(my_x_, my_y_, dx, dy);
  nsaddr_t nexthop = -1; //the nexthop result

  while(temp){
    double tempdis = getdis(temp->x_, temp->y_, dx, dy);
    if(tempdis < mindis){
      mindis = tempdis;
      nexthop = temp->id_;
    }
    temp = temp->next_;
  }

  return nexthop;
}

RNG

RNG平面图click here
对处在已知节点 O 的邻居列表中的所有节点 N ,都分别计算他们之间的距离dis(ON),然后对每一对节点O,N分别计算他们各自与邻居列表中其他节点 M 之间的距离dis(OM)dis(NM)。根据RNG平面图的定义,把所有满足dis(ON) <= max[dis(OM), dis(NM)]的节点N计入新链表。

struct gpsr_neighbor *
GPSRNeighbors::rng_planarize()
{
    struct gpsr_neighbor *temp, *result, *index;
    index = head_;
    result = NULL;

    while(index)   
    {
    	double mdis = getdis(my_x_, my_y_, index->x_, index->y_);     //当前节点和其邻居节点的距离
     	temp = head_;
      	while(temp)    //判断是否有节点,使得其到my和index的最大距离小于my和index的距离(有的话代表这个邻居节点不好使)
      	{
			if(temp->id_ != index->id_)
			{
	  			double tempdis1 = getdis(my_x_, my_y_, temp->x_, temp->y_);
	  			double tempdis2 = getdis(index->x_, index->y_, temp->x_, temp->y_);
	 	 		if(tempdis1 < mdis && tempdis2 < mdis) 
	 	 			break;
			}
			temp=temp->next_;
	 	 }
      	if(temp==NULL)   //将该邻居节点记录
     	{
   			temp = (struct gpsr_neighbor*)malloc(sizeof(struct gpsr_neighbor));
			temp->id_ = index->id_;
			temp->x_ = index->x_;
			temp->y_ = index->y_;
			temp->next_ = result;   //头插
			temp->prev_ = NULL;
			if(result)      //前向指针
				result->prev_ = temp;
			result = temp;
      	}
      	index=index->next_;   //遍历邻居表
    }
    return result;
}

GG

GG平面图 click here
对GPSR代码的理解——gpsr_neighbor.cc_第2张图片

struct gpsr_neighbor *
GPSRNeighbors::gg_planarize(){
    struct gpsr_neighbor *temp, *result, *index;
    index = head_;
    result = NULL;

    while(index)
    {
    	double midpx = my_x_ + (index->x_ - my_x_)/2.0;
    	double midpy = my_y_ + (index->y_ - my_y_)/2.0;
     	double mdis = getdis(my_x_, my_y_, midpx, midpy); //按图形来理解,为圆半径长度
     	temp = head_;
      	while(temp)      //判断邻居表中所有节点是否在圆的范围内
      	{
      		if(temp->id_ != index->id_)
      		{
	  			double tempdis = getdis(midpx, midpy, temp->x_, temp->y_);
	  			if(tempdis < mdis) 
	  				break;
			}
			temp=temp->next_;
		}
     	if(temp==NULL)  //在圆范围内不存在节点,则可以选择index点
     	{
			temp = (struct gpsr_neighbor*)malloc(sizeof(struct gpsr_neighbor));
			temp->id_ = index->id_;
			temp->x_ = index->x_;
			temp->y_ = index->y_;
			temp->next_ = result;     //前插
			temp->prev_ = NULL;
			if(result) 
				result->prev_ = temp;
			result = temp;
     	 }
     	 index=index->next_;
    }
    return result;
}

基于给定线相对角度计算绝对角度

首先计算两点R1(x1,y1)和R2(x2,y2)之间的距离,再计算出两点连线与坐标轴的正弦值和余弦值。这时候要判断sin的符号,若sin>0,代表R2在R1的y正方向,计算arccos(cos)即可求出角度。否则代表R2在R1的y负方向,用2π减去所求值采薇实际角度。

double
GPSRNeighbors::angle(double x1, double y1, double x2, double y2){
  double line_len = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));

  double sin_theta, cos_theta;
  double theta;

  if(line_len == 0.0){
    printf("2 nodes are the same\n");
    return -1.0;
  }

  sin_theta = (y2-y1)/line_len;
  cos_theta = (x2-x1)/line_len;

  theta = acos(cos_theta);

  if(sin_theta<0){
    theta = 2*PI - theta;
  }

  return theta;
}

//检查两条线是否在本地交叉

只搞懂了判断平行(abtan(x) == a*tan(x)*b),对交叉那部分还是不怎么懂。
对GPSR代码的理解——gpsr_neighbor.cc_第3张图片

/* To check the line from me to theother, and the line from source
 * and destination is intersecting each other or not
 * Note: 2 line segments intersects each other if they have a common
 *       point, BUT here, if the common point is the end point,
 *       we don't count it.
 */
int
GPSRNeighbors::intersect(nsaddr_t theother, double sx, double sy,
			 double dx, double dy){
  //line 1 (x1,y1)--(x2,y2) is the segment
  //line 2 (x3,y3)--(x4,y4) is the xD
  struct gpsr_neighbor *other = getnb(theother);   //根据id获取该邻居节点

  if(other==NULL){
    printf("Wrong the other node\n");
    exit(1);
  }

  double x1 = my_x_;
  double y1 = my_y_;
  double x2 = other->x_;
  double y2 = other->y_;
  double x3 = sx;
  double y3 = sy;
  double x4 = dx;
  double y4 = dy;

  double a1 = y2 - y1;
  double b1 = x1 - x2;
  double c1 = x2*y1 - x1*y2;

  double a2 = y4 - y3;
  double b2 = x3 - x4;
  double c2 = x4*y3 - x3*y4;

  double denom = a1*b2 - a2*b1;

  double x, y; //the result;

  if(denom == 0) {
    return 0; //parallel lines;
  }

  x = (b1*c2 - b2*c1)/denom;
  y = (a2*c1 - a1*c2)/denom;

  if(x > MIN(x1, x2) && x < MAX(x1, x2) &&
     x > MIN(x3, x4) && x < MAX(x3, x4))
    return 1;
  else return 0;
}

返回给定的邻居列表的大小
int
GPSRNeighbors::num_of_neighbors(struct gpsr_neighbor *nblist){
  struct gpsr_neighbor *temp = nblist;
  int counter = 0;
  while(temp){
    counter++;
    temp = temp->next_;
  }
  return counter;
}

释放给定的邻居列表
void
GPSRNeighbors::free_neighbors(struct gpsr_neighbor *nblist){
  struct gpsr_neighbor *temp, *head;
  head = nblist;
  while(head){
    temp = head;
    head = head->next_;
    free(temp);
  }
}

周边转发

首先根据type来确定是GG平面化或者RNG平面化,得到平面化后的邻居列表。

nsaddr_t
GPSRNeighbors::peri_nexthop(int type_, nsaddr_t last,
			    double sx, double sy,
			    double dx, double dy)
{
	struct gpsr_neighbor *planar_neighbors, *temp;
 	double alpha, minangle;
  	nsaddr_t nexthop=-1;
 	if(type_)  //GG 平面化
 	{
    	planar_neighbors = gg_planarize(); //得到GG平面化后的邻居列表
  	}
  	else   //RNG 平面化
  	{
    	planar_neighbors = rng_planarize();  //得到RNG平面化后的邻居列表
  	}
 	if(last>-1)
 	{
    	struct gpsr_neighbor *lastnb = getnb(last);
    	if(lastnb == NULL) 
    	{
    		printf("Wrong last nb %d->%d\n", last, my_id_);
      		exit(1);
   	 	}
   	 	alpha = angle(my_x_, my_y_, lastnb->x_, lastnb->y_); //计算当前节点与上一跳节点连线的夹角
   	 }
 	 else
    	alpha = angle(my_x_, my_y_, dx, dy); //计算当前节点与目的节点连线的夹角
 	 temp = planar_neighbors;
 	 while(temp)    //遍历平面化后的邻居列表
 	 {
 	 	if(temp->id_ != last)
 	 	{
      		double delta;
      		delta = angle(my_x_, my_y_, temp->x_, temp->y_); //计算当前节点与邻居节点连线的夹角
      		delta = delta - alpha;
      		//得到“当前节点与邻居节点连线” 与 “当前节点与目的节点连线”或“当前节点与上一跳节点连线”的夹角
      		if(delta < 0.0)  //这就规定了逆时针方向。
      		{
				delta = 2*PI + delta;
     	 	}
     	 	if(delta < minangle)
     	 	{
				minangle = delta;
				nexthop = temp->id_;
      		}
    	}
    	temp = temp->next_;
  	}
  	if(num_of_neighbors(planar_neighbors) > 1 && intersect(nexthop, sx, sy, dx, dy))
    {
    	free_neighbors(planar_neighbors); 
      	return peri_nexthop(type_, nexthop, sx, sy, dx, dy);
  	}
 	return nexthop;
}

转储邻居列表

将当前节点id写入到文件中,然后指针temp指向链表头部,遍历链表,获取每个节点的id并写入到文件中。从而完成邻居列表的转储工作。

void
GPSRNeighbors::dump(){
  delalltimeout();   //删除超时邻居节点

  FILE *fp = fopen(NB_TRACE_FILE, "a+");

  struct gpsr_neighbor *temp = head_;
  fprintf(fp, "%d:\t", my_id_);
  while(temp){        //遍历邻居表
    fprintf(fp, "%d ", temp->id_);
    temp = temp->next_;
  }
  fprintf(fp,"\n");
  fclose(fp);
}

你可能感兴趣的:(对GPSR代码的理解——gpsr_neighbor.cc)