GPS-nmealib学习
生活随笔
收集整理的这篇文章主要介绍了
GPS-nmealib学习
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
GPS nmealib学习笔记
标签: GPSnmealibCSTACKHEAP 2013-10-17 11:12 4269人阅读 评论(7) 收藏 举报
本文章已收录于:
分类: 嵌入式ARM(24) 作者同类文章X nmealib简介 目录介绍 示例代码 若干注意点 1 重置解析载体缓冲区 2 注意nmeaINFO结构体大小 总结
分析NMEA语句并把结果保存在合适的C语言结构体中。
除了解析NMEA语句之外,还可以产生NMEA语句。
支持多种NMEA语句,包括GPGGA, GPGSA, GPGSV, GPRMC, GPVTG。 解析算法层次严谨。
附加地理学相关功能,可支持导航等数据工作。
更多介绍请参考以下链接
include\nmealib文件夹中存在nmealib相关的头文件
src文件夹存放nmealib相关源文件,该文件夹中的内容和include\nmealib文件夹相对应 samples文件夹存放若干例子,一些简单易懂的例子。
#include <nmea/nmea.h> #include <string.h> #include <stdio.h> int main() { // 被测试的GPS模块输出数据,仅有GPRMC格式 char gps_str[] = "$GPRMC,013257.00,A,3129.51829,N,12022.10562,E,0.093,,270813,,,A*7A\r\n"; nmeaINFO info; // nmea协议解析结果结构体 nmeaPARSER parser; // nmea协议解析载体 nmea_zero_INFO(&info); // 填入默认的解析结果 nmea_parser_init(&parser); // 为解析载体分配内存空间 // 调用函数完成GPS信息解析,最终结果保留于info数组中 if( (nmea_parse(&parser, gps_str, (int)strlen(gps_str), &info)) > 0 ) { printf("longitude %.5f\r\n",info.lon); printf("latitude %.5f\r\n",info.lat); printf("speed %.2f\r\n",info.speed); } nmea_parser_destroy(&parser); // 释放解析载体的内存空间 return 0; } #include <nmea/nmea.h>
#include <string.h>
#include <stdio.h>int main()
{// 被测试的GPS模块输出数据,仅有GPRMC格式char gps_str[] = "$GPRMC,013257.00,A,3129.51829,N,12022.10562,E,0.093,,270813,,,A*7A\r\n";nmeaINFO info; // nmea协议解析结果结构体nmeaPARSER parser; // nmea协议解析载体nmea_zero_INFO(&info); // 填入默认的解析结果nmea_parser_init(&parser); // 为解析载体分配内存空间// 调用函数完成GPS信息解析,最终结果保留于info数组中if( (nmea_parse(&parser, gps_str, (int)strlen(gps_str), &info)) > 0 ){printf("longitude %.5f\r\n",info.lon);printf("latitude %.5f\r\n",info.lat);printf("speed %.2f\r\n",info.speed);}nmea_parser_destroy(&parser); // 释放解析载体的内存空间return 0;
}
图1 程序输出结果 利用nmealib解析GPS模块的输出结果大致可以分为三步,第一步定义和初始化GPS信息结构体和解析载体结构体,第二步调用nmea_parse函数完成解析工作,第三步释放解析载体所占用的内存空间。如果仔细查看nmea_parser_init部分的代码,便会发现函数中使用了C标准库的malloc函数,该函数会在RAM中的heap空间开辟一个空间,这就需要使用完该载体之后立刻释放,所以nmea_parser_init和nmea_parser_destroy需要成对出现。
[cpp] view plaincopyprint?typedef struct _nmeaINFO { int smask; /**< Mask specifying types of packages from which data have been obtained */ nmeaTIME utc; /**< UTC of position */ int sig; /**< GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive) */ int fix; /**< Operating mode, used for navigation (1 = Fix not available; 2 = 2D; 3 = 3D) */ double PDOP; /**< Position Dilution Of Precision */ double HDOP; /**< Horizontal Dilution Of Precision */ double VDOP; /**< Vertical Dilution Of Precision */ double lat; /**< Latitude in NDEG - +/-[degree][min].[sec/60] */ double lon; /**< Longitude in NDEG - +/-[degree][min].[sec/60] */ double elv; /**< Antenna altitude above/below mean sea level (geoid) in meters */ double speed; /**< Speed over the ground in kilometers/hour */ double direction; /**< Track angle in degrees True */ double declination; /**< Magnetic variation degrees (Easterly var. subtracts from true course) */ nmeaSATINFO satinfo; /**< Satellites information */ } nmeaINFO; typedef struct _nmeaINFO
{int smask; /**< Mask specifying types of packages from which data have been obtained */nmeaTIME utc; /**< UTC of position */int sig; /**< GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive) */int fix; /**< Operating mode, used for navigation (1 = Fix not available; 2 = 2D; 3 = 3D) */double PDOP; /**< Position Dilution Of Precision */double HDOP; /**< Horizontal Dilution Of Precision */double VDOP; /**< Vertical Dilution Of Precision */double lat; /**< Latitude in NDEG - +/-[degree][min].[sec/60] */double lon; /**< Longitude in NDEG - +/-[degree][min].[sec/60] */double elv; /**< Antenna altitude above/below mean sea level (geoid) in meters */double speed; /**< Speed over the ground in kilometers/hour */double direction; /**< Track angle in degrees True */double declination; /**< Magnetic variation degrees (Easterly var. subtracts from true course) */nmeaSATINFO satinfo; /**< Satellites information */} nmeaINFO;
nmeaINFO是一个很关键的结构体,该结构体中保存了nmea语句解析的结果。例如lat代表纬度,lon代表精度,speed代表速度。需要注意的是lat和lon的数值格式和百度地图的格式是有区别,而速度的单位为KM/H,相对于“节”这个单位,公里每小时要好理解的多。
GPS模块可以输出的内容很多,但是最基本的信息可通过GPRMC获得。GPRMC的具体格式如下内容所示: $GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh <1> UTC时间,hhmmss.sss(时分秒.毫秒)格式 <2> 定位状态,A=有效定位,V=无效定位 <3> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输) <4> 纬度半球N(北半球)或S(南半球) <5> 经度dddmm.mmmm(度分)格式(前面的0也将被传输) <6> 经度半球E(东经)或W(西经) <7> 地面速率(000.0~999.9节,前面的0也将被传输) <8> 地面航向(000.0~359.9度,以正北为参考基准,前面的0也将被传输) <9> UTC日期,ddmmyy(日月年)格式 <10> 磁偏角(000.0~180.0度,前面的0也将被传输) <11> 磁偏角方向,E(东)或W(西) <12> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效) 在不同的情况下测试GPS模块,可以获得以下三种不同形式的输出内容: 1) $GPRMC,013257.00,A,3129.51829,N,12022.10562,E,0.093,,270813,,,A*7A\r\n 2) $GPRMC,022649.00,V,,,,,,,020913,,,N*7F\r\n 3) $GPRMC,,V,,,,,,,,,,N*53\r\n 【第一种】GPS定位成功,输出正确的GPS位置信息和对地速度信息。 【第二种】GPS定位异常,只有UTC时间信息,其中V代表定位错误。 【第三种】GPS定位异常,甚至没有UTC时间信息,其中V代表定位错误。 通过测试,nmealib处理第一种情况没有任何问题,但是连续处理第二种和第三种情况会产生问题,产生问题的主要原因是动态开辟的空间没有被释放。解决该问题需要修改nmealib的源代码。如果不想修改nmealib源代码,可以在串口接收GPS输出内容时直接过滤带有V的字符串,这种方法简单有效,同样可以获得准确的GPS坐标信息。
#define NMEA_DEF_PARSEBUFF (1024) #define NMEA_MIN_PARSEBUFF (256) 示例代码的分析中提到,解析载体parser通过动态内存分配的方法开辟空间,该空间位于RAM空间中的heap部分,如果heap部分设置的太小,将会导致MCU进入不可预知的状态或者直接掉入断言中。 在编译器设置中,option->linker,cstack和heap的大小设置如下图所示,此时heap的大小仅有512个字节,而解析载体却需要1024个字节,那么分配内存时势必会产生问题,而工程的编译却不会有任何问题。
图2 不合适的HEAP大小设置
[cpp] view plaincopyprint?if(0 == (parser->buffer = malloc(buff_size))) nmea_error("Insufficient memory!"); // 此时会发生内存分配错误 else { parser->buff_size = buff_size; resv = 1; } if(0 == (parser->buffer = malloc(buff_size)))nmea_error("Insufficient memory!"); // 此时会发生内存分配错误else{parser->buff_size = buff_size;resv = 1;}
在这种情况下可以适当减少缓冲区的大小,例如修改如下:
#define NMEA_DEF_PARSEBUFF (256) #define NMEA_MIN_PARSEBUFF (128) 同时可以把HEAP空间大小的调整一下。
图3 合适的HEAP大小设置 当然缓冲区的大小也是视情况而定,若GPS模块仅输出GPRMC语句,那么256字节的缓冲区已经足够了。但是如果GPS模块输出GPGGA, GPGSA, GPGSV, GPRMC, GPVTG等多种数据,那么256字节的缓冲区也有可能不够使用。
图4 查看CSTACK空间占用情况 从上图可以看出,CSTACK的大小为2048字节(2K),此时被占用752字节,占用率为36%,完全在可以接受的范围内。
示例工程链接:【bitbucket】 与50位技术专家面对面20年技术见证,附赠技术全景图
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
0.nmealib简介
nmealib是一个基于C语言的用于nmea协议的开源库。虽然nmea体积小巧,但是却具备了不少功能。分析NMEA语句并把结果保存在合适的C语言结构体中。
除了解析NMEA语句之外,还可以产生NMEA语句。
支持多种NMEA语句,包括GPGGA, GPGSA, GPGSV, GPRMC, GPVTG。 解析算法层次严谨。
附加地理学相关功能,可支持导航等数据工作。
更多介绍请参考以下链接
1.目录介绍
nmealib的目录还是非常清晰的,下面简单介绍一下。include\nmealib文件夹中存在nmealib相关的头文件
src文件夹存放nmealib相关源文件,该文件夹中的内容和include\nmealib文件夹相对应 samples文件夹存放若干例子,一些简单易懂的例子。
2.示例代码
[cpp] view plaincopyprint?图1 程序输出结果 利用nmealib解析GPS模块的输出结果大致可以分为三步,第一步定义和初始化GPS信息结构体和解析载体结构体,第二步调用nmea_parse函数完成解析工作,第三步释放解析载体所占用的内存空间。如果仔细查看nmea_parser_init部分的代码,便会发现函数中使用了C标准库的malloc函数,该函数会在RAM中的heap空间开辟一个空间,这就需要使用完该载体之后立刻释放,所以nmea_parser_init和nmea_parser_destroy需要成对出现。
[cpp] view plaincopyprint?
nmeaINFO是一个很关键的结构体,该结构体中保存了nmea语句解析的结果。例如lat代表纬度,lon代表精度,speed代表速度。需要注意的是lat和lon的数值格式和百度地图的格式是有区别,而速度的单位为KM/H,相对于“节”这个单位,公里每小时要好理解的多。
GPS模块可以输出的内容很多,但是最基本的信息可通过GPRMC获得。GPRMC的具体格式如下内容所示: $GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh <1> UTC时间,hhmmss.sss(时分秒.毫秒)格式 <2> 定位状态,A=有效定位,V=无效定位 <3> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输) <4> 纬度半球N(北半球)或S(南半球) <5> 经度dddmm.mmmm(度分)格式(前面的0也将被传输) <6> 经度半球E(东经)或W(西经) <7> 地面速率(000.0~999.9节,前面的0也将被传输) <8> 地面航向(000.0~359.9度,以正北为参考基准,前面的0也将被传输) <9> UTC日期,ddmmyy(日月年)格式 <10> 磁偏角(000.0~180.0度,前面的0也将被传输) <11> 磁偏角方向,E(东)或W(西) <12> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效) 在不同的情况下测试GPS模块,可以获得以下三种不同形式的输出内容: 1) $GPRMC,013257.00,A,3129.51829,N,12022.10562,E,0.093,,270813,,,A*7A\r\n 2) $GPRMC,022649.00,V,,,,,,,020913,,,N*7F\r\n 3) $GPRMC,,V,,,,,,,,,,N*53\r\n 【第一种】GPS定位成功,输出正确的GPS位置信息和对地速度信息。 【第二种】GPS定位异常,只有UTC时间信息,其中V代表定位错误。 【第三种】GPS定位异常,甚至没有UTC时间信息,其中V代表定位错误。 通过测试,nmealib处理第一种情况没有任何问题,但是连续处理第二种和第三种情况会产生问题,产生问题的主要原因是动态开辟的空间没有被释放。解决该问题需要修改nmealib的源代码。如果不想修改nmealib源代码,可以在串口接收GPS输出内容时直接过滤带有V的字符串,这种方法简单有效,同样可以获得准确的GPS坐标信息。
3.若干注意点
3.1 重置解析载体缓冲区
由于nmealib虽然使用C语言,但是相关测试环境均基于PC环境,所以在嵌入式环境下需要对相关参数(宏定义)进行修改。例如context.h文件中,定义了待处理的nmea缓冲区的大小,相关宏定义如下#define NMEA_DEF_PARSEBUFF (1024) #define NMEA_MIN_PARSEBUFF (256) 示例代码的分析中提到,解析载体parser通过动态内存分配的方法开辟空间,该空间位于RAM空间中的heap部分,如果heap部分设置的太小,将会导致MCU进入不可预知的状态或者直接掉入断言中。 在编译器设置中,option->linker,cstack和heap的大小设置如下图所示,此时heap的大小仅有512个字节,而解析载体却需要1024个字节,那么分配内存时势必会产生问题,而工程的编译却不会有任何问题。
图2 不合适的HEAP大小设置
[cpp] view plaincopyprint?
在这种情况下可以适当减少缓冲区的大小,例如修改如下:
#define NMEA_DEF_PARSEBUFF (256) #define NMEA_MIN_PARSEBUFF (128) 同时可以把HEAP空间大小的调整一下。
图3 合适的HEAP大小设置 当然缓冲区的大小也是视情况而定,若GPS模块仅输出GPRMC语句,那么256字节的缓冲区已经足够了。但是如果GPS模块输出GPGGA, GPGSA, GPGSV, GPRMC, GPVTG等多种数据,那么256字节的缓冲区也有可能不够使用。
3.2 注意nmeaINFO结构体大小
nmeaINFO结构体左右360字节,若该结构体类型的变量势必会占用较多的CSTACK(全局变量的情况除外),在嵌入式编程中像nmeaINFO类型的变量也可算的上是中型“人物”了。所以在调试的过程中可以打开IDE的stack查看功能(进入debug模式,view->stack),观察当前函数的CSTACK使用情况,尤其要注意CSTACK越界的情况。图4 查看CSTACK空间占用情况 从上图可以看出,CSTACK的大小为2048字节(2K),此时被占用752字节,占用率为36%,完全在可以接受的范围内。
4.总结
nmealib的使用可以缩短GPS的开发周期,这里只是简单的举例了nmealib的基本功能,更多的功能可以查看samples文件夹中的例子,并在实践中不断熟悉提高。示例工程链接:【bitbucket】 与50位技术专家面对面20年技术见证,附赠技术全景图
总结
以上是生活随笔为你收集整理的GPS-nmealib学习的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: __attribute__函数的作用
- 下一篇: 单片机自动调光C语言,51单片机触控调光