nmealib代码分析

来源:互联网 发布:中国空运交通运输网络 编辑:程序博客网 时间:2024/06/05 17:25
从之前的samples/parse/main.c开始。

以其中的一条GPGGA语句为例。

nmeaINFO结构汇总的是gps数据信息,里面包括utc时间、定位状态、质量因子、经纬度、速度、方向等信息,之所以说是汇总,那是因为这里是对所有的nmea语句进行解析,然后将相应的数据赋值到该结构中,而不仅仅是其中的一条nmea语句,因为一条nmea语句不可能包括所有的gps信息。

nmeaPARSER是解析nmea所需要的一个结构。

然后是nmea_zero_INFO。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void nmea_zero_INFO(nmeaINFO *info)  
  2. {  
  3.     memset(info, 0, sizeof(nmeaINFO));  
  4.     nmea_time_now(&info->utc);  
  5.     info->sig = NMEA_SIG_BAD;  
  6.     info->fix = NMEA_FIX_BAD;  
  7. }  

这里是对nmeaINFO这个结构中数据进行清零操作,使用nmea_time_now函数对其中utc时间赋一个初值,初值就是当前的系统时间,如果没有从nmea中解析出时间信息,那么最后的结果就是你当前的系统时间。而nmeaINFO中的sig、fix分别是定位状态和定位类型。

紧接着是nmea_parser_init。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_parser_init(nmeaPARSER *parser)  
  2. {  
  3.     int resv = 0;  
  4.     int buff_size = nmea_property()->parse_buff_size;  
  5.   
  6.     NMEA_ASSERT(parser);  
  7.   
  8.     if(buff_size < NMEA_MIN_PARSEBUFF)  
  9.         buff_size = NMEA_MIN_PARSEBUFF;  
  10.   
  11.     memset(parser, 0, sizeof(nmeaPARSER));  
  12.   
  13.     if(0 == (parser->buffer = malloc(buff_size)))  
  14.         nmea_error("Insufficient memory!");  
  15.     else  
  16.     {  
  17.         parser->buff_size = buff_size;  
  18.         resv = 1;  
  19.     }      
  20.   
  21.     return resv;  
  22. }  
这个函数自然是对nmeaPARSER结构做初始化,首先是buff_size,这里值为NMEA_DEF_PARSEBUFF,即1024。然后为buffer分配内存,这里自然是分配的1024字节大小。

最后调用nmea_parse函数对nmea语句进行解析。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_parse(      
  2.     nmeaPARSER *parser,  
  3.     const char *buff, int buff_sz,  
  4.     nmeaINFO *info  
  5.     )  
  6. {  
  7.     int ptype, nread = 0;  
  8.     void *pack = 0;  
  9.   
  10.     NMEA_ASSERT(parser && parser->buffer);  
  11.   
  12.     nmea_parser_push(parser, buff, buff_sz);  
  13.   
  14.     while(GPNON != (ptype = nmea_parser_pop(parser, &pack)))  
  15.     {  
  16.         nread++;  
  17.   
  18.         switch(ptype)  
  19.         {  
  20.         case GPGGA:  
  21.             nmea_GPGGA2info((nmeaGPGGA *)pack, info);  
  22.             break;  
  23.         case GPGSA:  
  24.             nmea_GPGSA2info((nmeaGPGSA *)pack, info);  
  25.             break;  
  26.         case GPGSV:  
  27.             nmea_GPGSV2info((nmeaGPGSV *)pack, info);  
  28.             break;  
  29.         case GPRMC:  
  30.             nmea_GPRMC2info((nmeaGPRMC *)pack, info);  
  31.             break;  
  32.         case GPVTG:  
  33.             nmea_GPVTG2info((nmeaGPVTG *)pack, info);  
  34.             break;  
  35.         };  
  36.   
  37.         free(pack);  
  38.     }  
  39.   
  40.     return nread;  
  41. }  
这个函数有四个参数,分别是nmeaPARSER指针,buff对应需要解析的nmea语句,buff_sz为nmea语句的长度,nmeaINFO指针。

调用nmea_parser_push函数。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_parser_push(nmeaPARSER *parser, const char *buff, int buff_sz)  
  2. {  
  3.     int nparse, nparsed = 0;  
  4.   
  5.     do  
  6.     {  
  7.         if(buff_sz > parser->buff_size)  
  8.             nparse = parser->buff_size;  
  9.         else  
  10.             nparse = buff_sz;  
  11.   
  12.         nparsed += nmea_parser_real_push(  
  13.             parser, buff, nparse);  
  14.   
  15.         buff_sz -= nparse;  
  16.   
  17.     } while(buff_sz);  
  18.   
  19.     return nparsed;  
  20. }  

在do while里又调用了nmea_parser_real_push函数,这里nparse还是等于buff_sz大小。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_parser_real_push(nmeaPARSER *parser, const char *buff, int buff_sz)  
  2. {  
  3.     int nparsed = 0, crc, sen_sz, ptype;  
  4.     nmeaParserNODE *node = 0;  
  5.   
  6.     NMEA_ASSERT(parser && parser->buffer);  
  7.   
  8.     /* clear unuse buffer (for debug) */  
  9.     /* 
  10.     memset( 
  11.         parser->buffer + parser->buff_use, 0, 
  12.         parser->buff_size - parser->buff_use 
  13.         ); 
  14.         */  
  15.   
  16.     /* add */  
  17.     if(parser->buff_use + buff_sz >= parser->buff_size)  
  18.         nmea_parser_buff_clear(parser);  
  19.   
  20.     memcpy(parser->buffer + parser->buff_use, buff, buff_sz);  
  21.     parser->buff_use += buff_sz;  
  22.   
  23.     /* parse */  
  24.     for(;;node = 0)  
  25.     {  
  26.         sen_sz = nmea_find_tail(  
  27.             (const char *)parser->buffer + nparsed,  
  28.             (int)parser->buff_use - nparsed, &crc);  
  29.   
  30.         if(!sen_sz)  
  31.         {  
  32.             if(nparsed)  
  33.                 memcpy(  
  34.                 parser->buffer,  
  35.                 parser->buffer + nparsed,  
  36.                 parser->buff_use -= nparsed);  
  37.             break;  
  38.         }  
  39.         else if(crc >= 0)  
  40.         {  
  41.             ptype = nmea_pack_type(  
  42.                 (const char *)parser->buffer + nparsed + 1,  
  43.                 parser->buff_use - nparsed - 1);  
  44.   
  45.             if(0 == (node = malloc(sizeof(nmeaParserNODE))))  
  46.                 goto mem_fail;  
  47.   
  48.             node->pack = 0;  
  49.   
  50.             switch(ptype)  
  51.             {  
  52.             case GPGGA:  
  53.                 if(0 == (node->pack = malloc(sizeof(nmeaGPGGA))))  
  54.                     goto mem_fail;  
  55.                 node->packType = GPGGA;  
  56.                 if(!nmea_parse_GPGGA(  
  57.                     (const char *)parser->buffer + nparsed,  
  58.                     sen_sz, (nmeaGPGGA *)node->pack))  
  59.                 {  
  60.                     free(node);  
  61.                     node = 0;  
  62.                 }  
  63.                 break;  
  64.             case GPGSA:  
  65.                 if(0 == (node->pack = malloc(sizeof(nmeaGPGSA))))  
  66.                     goto mem_fail;  
  67.                 node->packType = GPGSA;  
  68.                 if(!nmea_parse_GPGSA(  
  69.                     (const char *)parser->buffer + nparsed,  
  70.                     sen_sz, (nmeaGPGSA *)node->pack))  
  71.                 {  
  72.                     free(node);  
  73.                     node = 0;  
  74.                 }  
  75.                 break;  
  76.             case GPGSV:  
  77.                 if(0 == (node->pack = malloc(sizeof(nmeaGPGSV))))  
  78.                     goto mem_fail;  
  79.                 node->packType = GPGSV;  
  80.                 if(!nmea_parse_GPGSV(  
  81.                     (const char *)parser->buffer + nparsed,  
  82.                     sen_sz, (nmeaGPGSV *)node->pack))  
  83.                 {  
  84.                     free(node);  
  85.                     node = 0;  
  86.                 }  
  87.                 break;  
  88.             case GPRMC:  
  89.                 if(0 == (node->pack = malloc(sizeof(nmeaGPRMC))))  
  90.                     goto mem_fail;  
  91.                 node->packType = GPRMC;  
  92.                 if(!nmea_parse_GPRMC(  
  93.                     (const char *)parser->buffer + nparsed,  
  94.                     sen_sz, (nmeaGPRMC *)node->pack))  
  95.                 {  
  96.                     free(node);  
  97.                     node = 0;  
  98.                 }  
  99.                 break;  
  100.             case GPVTG:  
  101.                 if(0 == (node->pack = malloc(sizeof(nmeaGPVTG))))  
  102.                     goto mem_fail;  
  103.                 node->packType = GPVTG;  
  104.                 if(!nmea_parse_GPVTG(  
  105.                     (const char *)parser->buffer + nparsed,  
  106.                     sen_sz, (nmeaGPVTG *)node->pack))  
  107.                 {  
  108.                     free(node);  
  109.                     node = 0;  
  110.                 }  
  111.                 break;  
  112.             default:  
  113.                 free(node);  
  114.                 node = 0;  
  115.                 break;  
  116.             };  
  117.   
  118.             if(node)  
  119.             {  
  120.                 if(parser->end_node)  
  121.                     ((nmeaParserNODE *)parser->end_node)->next_node = node;  
  122.                 parser->end_node = node;  
  123.                 if(!parser->top_node)  
  124.                     parser->top_node = node;  
  125.                 node->next_node = 0;  
  126.             }  
  127.         }  
  128.   
  129.         nparsed += sen_sz;  
  130.     }  
  131.   
  132.     return nparsed;  
  133.   
  134. mem_fail:  
  135.     if(node)  
  136.         free(node);  
  137.   
  138.     nmea_error("Insufficient memory!");  
  139.   
  140.     return -1;  
  141. }  
首先将要解析的nmea字符串拷贝到nmeaPARSER的buffer指针处,注意这里最开始就分配好了1024字节大小的内存空间,然后对nmeaPARSER的buff_use做一个赋值操作,这里赋值为nmea语句的长度值。

到了for循环中,首先调用的是nmea_find_tail函数。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_find_tail(const char *buff, int buff_sz, int *res_crc)  
  2. {  
  3.     static const int tail_sz = 3 /* *[CRC] */ + 2 /* \r\n */;  
  4.   
  5.     const char *end_buff = buff + buff_sz;  
  6.     int nread = 0;  
  7.     int crc = 0;  
  8.   
  9.     NMEA_ASSERT(buff && res_crc);  
  10.   
  11.     *res_crc = -1;  
  12.   
  13.     for(;buff < end_buff; ++buff, ++nread)  
  14.     {  
  15.         if(('$' == *buff) && nread)  
  16.         {  
  17.             buff = 0;  
  18.             break;  
  19.         }  
  20.         else if('*' == *buff)  
  21.         {  
  22.             if(buff + tail_sz <= end_buff && '\r' == buff[3] && '\n' == buff[4])  
  23.             {  
  24.                 *res_crc = nmea_atoi(buff + 1, 2, 16);  
  25.                 nread = buff_sz - (int)(end_buff - (buff + tail_sz));  
  26.                 if(*res_crc != crc)  
  27.                 {  
  28.                     *res_crc = -1;  
  29.                     buff = 0;  
  30.                 }  
  31.             }  
  32.   
  33.             break;  
  34.         }  
  35.         else if(nread)  
  36.             crc ^= (int)*buff;  
  37.     }  
  38.   
  39.     if(*res_crc < 0 && buff)  
  40.         nread = 0;  
  41.   
  42.     return nread;  
  43. }  
这个函数主要干什么的呢,主要是找到nmea语句的结束符"\r\n",并判断其crc值是否正确,如果你私自改了nmea语句中的某个值,而又没有修改crc值,那么这里解析是不会成功的。

如果在其他地方发现了nmea语句的起始符"$",那么证明这条nmea语句是有问题的,直接退出。

那么边计算crc值,边找nmea语句的结束符,如果找到了一个符号"*",那么结束符就在后面的第3、第4个位置处。这里一并将nmea语句中的crc值取出来,并和前面计算的crc值做一个比较,如果不想等,说明这条nmea语句有问题,直接丢弃。最后返回的nread还是nmea语句的长度值。


返回到nmea_parser_real_push函数中,sen_sz不为0,那么自然走下面的else if流程。

然后调用nmea_pack_type函数判断nmea语句的包类型。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_pack_type(const char *buff, int buff_sz)  
  2. {  
  3.     static const char *pheads[] = {  
  4.         "GPGGA",  
  5.         "GPGSA",  
  6.         "GPGSV",  
  7.         "GPRMC",  
  8.         "GPVTG",  
  9.     };  
  10.   
  11.     NMEA_ASSERT(buff);  
  12.   
  13.     if(buff_sz < 5)  
  14.         return GPNON;  
  15.     else if(0 == memcmp(buff, pheads[0], 5))  
  16.         return GPGGA;  
  17.     else if(0 == memcmp(buff, pheads[1], 5))  
  18.         return GPGSA;  
  19.     else if(0 == memcmp(buff, pheads[2], 5))  
  20.         return GPGSV;  
  21.     else if(0 == memcmp(buff, pheads[3], 5))  
  22.         return GPRMC;  
  23.     else if(0 == memcmp(buff, pheads[4], 5))  
  24.         return GPVTG;  
  25.   
  26.     return GPNON;  
  27. }  
这里只支持5种类型的nmea语句,有GPGGA、GPGSA、GPGSV、GPRMC和GPVTG,这里只需要判断前5个字符就可以了,返回这个类型值。

如果是GPGGA类型的nmea语句,那自然是调用nmea_parse_GPGGA这个函数对其进行解析了。在这之前首先为nmeaParserNODE和其中的pack申请了内存空间,那么自然这里的解析结果肯定是存储在pack这里了。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_parse_GPGGA(const char *buff, int buff_sz, nmeaGPGGA *pack)  
  2. {  
  3.     char time_buff[NMEA_TIMEPARSE_BUF];  
  4.   
  5.     NMEA_ASSERT(buff && pack);  
  6.   
  7.     memset(pack, 0, sizeof(nmeaGPGGA));  
  8.   
  9.     nmea_trace_buff(buff, buff_sz);  
  10.   
  11.     if(14 != nmea_scanf(buff, buff_sz,  
  12.         "$GPGGA,%s,%f,%C,%f,%C,%d,%d,%f,%f,%C,%f,%C,%f,%d*",  
  13.         &(time_buff[0]),  
  14.         &(pack->lat), &(pack->ns), &(pack->lon), &(pack->ew),  
  15.         &(pack->sig), &(pack->satinuse), &(pack->HDOP), &(pack->elv), &(pack->elv_units),  
  16.         &(pack->diff), &(pack->diff_units), &(pack->dgps_age), &(pack->dgps_sid)))  
  17.     {  
  18.         nmea_error("GPGGA parse error!");  
  19.         return 0;  
  20.     }  
  21.   
  22.     if(0 != _nmea_parse_time(&time_buff[0], (int)strlen(&time_buff[0]), &(pack->utc)))  
  23.     {  
  24.         nmea_error("GPGGA time parse error!");  
  25.         return 0;  
  26.     }  
  27.   
  28.     return 1;  
  29. }  
这里最重要的就是nmea_scanf函数了,这里才是真正的解析nmea语句的函数,这里这个函数的名字也很特别,带了个scanf。

回忆一下c语言中的scanf函数是怎么用的,例如scanf("%d", &val);,等待我们输入一个数字之后,那么最后的结果肯定是存在val中的。

这里的nmea_scanf也是类似的,只是这里的数据是在buff里,数据还是没有变化,还是那一条nmea语句。

nmea_scanf这个函数大家也可以去细看一下,反正最后的解析结果pack这里。


还是回到nmea_parser_real_push函数这里, 最后到了这里:
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. if(node)  
  2. {  
  3.     if(parser->end_node)  
  4.         ((nmeaParserNODE *)parser->end_node)->next_node = node;  
  5.     parser->end_node = node;  
  6.     if(!parser->top_node)  
  7.         parser->top_node = node;  
  8.     node->next_node = 0;  
  9. }  
node是找到了,初始时end_node、top_node都是都是为空的,那么都指向这里的node。


回到nmea_parse函数这里。nmea_parser_push函数执行完了,然后是调用nmea_parser_pop函数。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int nmea_parser_pop(nmeaPARSER *parser, void **pack_ptr)  
  2. {  
  3.     int retval = GPNON;  
  4.     nmeaParserNODE *node = (nmeaParserNODE *)parser->top_node;  
  5.   
  6.     NMEA_ASSERT(parser && parser->buffer);  
  7.   
  8.     if(node)  
  9.     {  
  10.         *pack_ptr = node->pack;  
  11.         retval = node->packType;  
  12.         parser->top_node = node->next_node;  
  13.         if(!parser->top_node)  
  14.             parser->end_node = 0;  
  15.         free(node);  
  16.     }  
  17.   
  18.     return retval;  
  19. }  
首先找到之前的pack,获取他的packType并返回。

如果packType是GPGGA,那么调用nmea_GPGGA2info,而这里的pack也强制转换为了nmeaGPGGA指针。
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void nmea_GPGGA2info(nmeaGPGGA *pack, nmeaINFO *info)  
  2. {  
  3.     NMEA_ASSERT(pack && info);  
  4.   
  5.     info->utc.hour = pack->utc.hour;  
  6.     info->utc.min = pack->utc.min;  
  7.     info->utc.sec = pack->utc.sec;  
  8.     info->utc.hsec = pack->utc.hsec;  
  9.     info->sig = pack->sig;  
  10.     info->HDOP = pack->HDOP;  
  11.     info->elv = pack->elv;  
  12.     info->lat = ((pack->ns == 'N')?pack->lat:-(pack->lat));  
  13.     info->lon = ((pack->ew == 'E')?pack->lon:-(pack->lon));  
  14.     info->smask |= GPGGA;  
  15. }  
而这个函数自然是对info中的某些数据做了一些赋值操作,包括经纬度、utc时间等。

最后解析结束。


ubox协议下载:http://download.csdn.net/detail/mcgrady_tracy/9407968

参考:http://www.gpsbaby.com/wz/yl.html
http://www.gpsbaby.com/wz/nmea.html
http://aprs.gids.nl/nmea/
0 0
原创粉丝点击