U-BLOX GPS 模块及GPRMC指令解析
受朋友所托,调试一款GPS模块,该模块是UBLOX的NEO-6M GPS模组。想到用这款GPS的人较多,自己日后也有可能在用到这个模块,就写下这份笔记。
1. 介绍
基本信息如下:
1, 模块采用U-BLOX NEO-6M模组,体积小巧,性能优异。
2, 模块增加放大电路,有利于无缘陶瓷天线快速搜星。
3, 模块可通过串口进行各种参数设置,并可保存在EEPROM,使用方便。
4, 模块自带SMA接口,可以连接各种有源天线,适应能力强。
5, 模块兼容3.3V/5V电平,方便连接各种单片机系统。
6, 模块自带可充电后备电池,可以掉电保持星历数据。
看到这里,就可以知道,这个模块是高度集成的,有点类似于西门子华为等公司的GPRS模块,基本上就是一个小的系统,用户只需要用AT命令通过串口通信就可以完成所有工作。那么这款模块的使用,其实就是字符串的解析工作了。
2. 通信协议
GPS模块采用NMEA 0183协议,NMEA 0183是美国国家海洋电子协会(National Marine Electronics Association)为海用电子设备制定的标准格式。目前业已成了GPS导航设备统一的RTCM(Radio Technical Commission for Maritime services)标准协议。
NMEA-0183协议采用ASCII码来传递GPS定位信息,我们称之为帧。
帧格式形如:$aaccc,ddd,ddd,„,ddd*hh(CR)(LF)
1、“$”:帧命令起始位
2、aaccc:地址域,前两位为识别符(aa),后三位为语句名(ccc)
3、ddd„ddd:数据
4、“*”:校验和前缀(也可以作为语句数据结束的标志)
5、hh:校验和,$与*之间所有字符ASCII码的校验和(各字节做异或运算,得到
校验和后,再转换16进制格式的ASCII字符)
6、(CR)(LF):帧结束,回车和换行符
在一般的项目中,最常用的指令是第4个,即$GPRMC ,推荐定位信息,长度70字节。$GPRMC(推荐定位信息,Recommended Minimum Specific GPS/Transit Data),$GPRMC语句的基本格式如下:
$GPRMC,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)*hh(CR)(LF)
(1) UTC时间,hhmmss(时分秒)
(2) 定位状态,A=有效定位,V=无效定位
(3) 纬度ddmm.mmmmm(度分)
(4) 纬度半球N(北半球)或S(南半球)
(5) 经度dddmm.mmmmm(度分)
(6) 经度半球E(东经)或W(西经)
(7) 地面速率(000.0~999.9节)
(8) 地面航向(000.0~359.9度,以真北方为参考基准)
(9) UTC日期,ddmmyy(日月年)
(10)磁偏角(000.0~180.0度,前导位数不足则补0)
(11) 磁偏角方向,E(东)或W(西)
(12) 模式指示(A=自主定位,D=差分,E=估算,N=数据无效)
举例如下:
$GPRMC,023543.00,A,2308.28715,N,11322.09875,E,0.195,,240213,,,A*78
3. PC端显示数据
GPS模块有一个PC配置软件,叫做u-Center,可以对模块进行参数设置,然后保存到EEPROM,其实也可以通过单片机串口通信进行设置,但是PC端设置更加人性化,可以立刻看到结果。
打开GPS模块之后,接上u-Center软件,可以看到如下数据。
1 $GPGSV,,,,,,,,,,,,,,,,,,,*7D
$GPRMC,132043.00,V,,,,,,,,,,N*7F
$GPVTG,,,,,,,,,N*
$GPGSA,A,,,,,,,,,,,,,,99.99,99.99,99.99*
$GPGSV,,,,,,,,,,,,,,,,,,,*7B
$GPGSV,,,,,,,,,,,,,,,,,,,*7D
$GPRMC,132044.00,V,,,,,,,,,,N*
$GPVTG,,,,,,,,,N*
$GPGSA,A,,,,,,,,,,,,,,99.99,99.99,99.99*
$GPGSV,,,,,,,,,,,,,,,,,,,*7B
$GPGSV,,,,,,,,,,,,,,,,,,,*7A
$GPRMC,132045.00,V,,,,,,,,,,N*
$GPVTG,,,,,,,,,N*
$GPGSA,A,,,,,,,,,,,,,,99.99,99.99,99.99*
$GPGSV,,,,,,,,,,,,,,,,,,,*7B
$GPGSV,,,,,,,,,,,,,,,,,,,*7D
$GPRMC,132046.00,V,,,,,,,,,,N*7A
$GPVTG,,,,,,,,,N*
$GPGSV,,,,,,,,,,,,,,,,,,,*7B
$GPGSV,,,,,,,,,,,,,,,,,,,*
$GPRMC,132049.00,V,,,,,,,,,,N*
$GPRMC,133300.00,V,,,,,,,,,,N*7A
$GPRMC,133301.00,V,,,,,,,,,,N*7B
24 $GPRMC,133302.00,V,,,,,,,,,,N*
25 $GPRMC,133303.00,V,,,,,,,,,,N*
26 $GPRMC,133304.00,V,,,,,,,,,,N*7E
27 $GPRMC,133305.00,V,,,,,,,,,,N*7F
28 $GPRMC,133306.00,A,3949.63075,N,11616.48616,E,0.513,,,,,A*7A
29 $GPRMC,133307.00,A,3949.63025,N,11616.48614,E,1.053,,,,,A*7C
30 $GPRMC,133308.00,A,3949.63002,N,11616.48641,E,1.101,,,,,A*
…………
4. PC端软件配置
GPS有7种指令数据输出,如果我们只需要当前经纬度的话,可以屏蔽其他的数据。如果不屏蔽的话,MCU处理的时候,会不停的收到不需要的指令,降低MCU效率。
使用软件配置,[Config]-->[Configuration], 可以选择显示哪些项目,这里只保留GPRMC指令信息输出,然后保存。界面显示如下:
5. 数据解析
相对于MCU的处理速度,GPS定位数据更新几乎可以认为是缓慢变化的信号,每秒都会输出推荐位置信息,但是即使丢掉几个也不会影响定位准确性。因此可以使用中断循环buffer来接收GPS输出的串口数据,然后在需要的地方读取buffer,对数据实现一次检索,找到一个有用的GPS定位数据。
/*******************************************************************************
1.中断负责把GPS串口数据保存到GPS_Uart_Rcv_Buf,在合适的地方调用此函数开始解析。
2.调用本函数会自动关闭接收,然后处理,期间的GPS数据可以忽略。
3.在合适的地方打开接收。
*******************************************************************************/
uint8_t get_gps_useful_data(uint8_t *weidu, uint8_t *jingdu)
{
uint8_t *p_frame_start = NULL;
uint8_t *p_useful_frame_start = NULL;
uint8_t *p_useful_frame_end = NULL;
int16_t frame_len = ;
int16_t rcv_buf_data_len = ;
int16_t index = ;
int16_t frame_start_point = ;
int16_t remain_data_len = ;
uint8_t rtn =; gps_rcv_enable(FALSE);
rcv_buf_data_len = GPS_Rev_Buf_Size;
p_frame_start = &GPS_Uart_Rcv_Buf[]; /* data example:
$GPVTG,,,,,,,,,N*30
$GPRMC,132234.00,V,,,,,,,120116,,,N*7D
$GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79
*/
for(index = ; index < rcv_buf_data_len; )
{
p_frame_start=strstr(GPS_Uart_Rcv_Buf + index, "$GPRMC,"); //1. find start, "$GPRMC,"
//$GPRMC,132234.00,V,,,,,,,120116,,,N*7D $GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79
if(p_frame_start)
{
index = p_frame_start-GPS_Uart_Rcv_Buf;
if(index>)
{
memset(GPS_Uart_Rcv_Buf, , index);
} index = index + ;
p_useful_frame_start = strstr(GPS_Uart_Rcv_Buf + index, ",A,");//2 find useful data, ",A,"
frame_start_point = p_useful_frame_start - p_frame_start;
if((frame_start_point > )||(frame_start_point <= ))//not find useful data
{
continue;
}
frame_start_point = p_useful_frame_start-GPS_Uart_Rcv_Buf;
p_useful_frame_end = strstr(GPS_Uart_Rcv_Buf + frame_start_point, ",A*");//3 find useful data end, ",A*"
frame_len = p_useful_frame_end - p_frame_start + ;
if((frame_len > GPS_Frame_Buf_Size)||(frame_len <= )) // not found frame end
{
continue;
} //$GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79
frame_start_point = p_frame_start - GPS_Uart_Rcv_Buf + ;
memset(GPS_Frame_Buf, , GPS_Frame_Buf_Size);
memcpy(GPS_Frame_Buf, GPS_Uart_Rcv_Buf + frame_start_point , frame_len);
// if(check_frame_xor(GPS_Frame_Buf) != TRUE)
// {
// continue;
// }
//the frame is correct
rtn = read_gps_data(GPS_Frame_Buf, remain_data_len, weidu, jingdu);
if( == rtn)
{
break; //found weidu,jingdu data
}
else
{
continue;
}
}
else
{
break;
}
}
g_gps_data_cnt = ;
memset(GPS_Uart_Rcv_Buf, , GPS_Rev_Buf_Size);
return rtn;
}
上面处理的基本思想是:找到"$GPRMC,"帧头,然后找到代表定位信息有效的字符",A,",最后找到帧尾",A*"。
因为实际收到的数据有不完整的,或者无效的定位信息,也有串口接收循环buffer造成的覆盖数据,因此上面还校验了数据长度,以及一个标志字符串到另一个字符串直接的长度。
实际使用中,发现没有加上校验也可以很有效的工作,但是为了保证数据安全,最后的异或校验还有应该有的。
/*******************************************************************************
* Function Name :
* Description : 对GPRMC数据包进行解析,找到经纬度数据
* Input :
* Output :
* Return :
*******************************************************************************/
uint8_t read_gps_data(uint8_t *gps_buf, uint8_t frame_len, uint8_t *weidu, uint8_t *jindu)
{
uint8_t *weidu_s = NULL;
uint8_t *weidu_o = NULL;
uint8_t *jingdu_o = NULL;
uint8_t rtn =;
//GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79
weidu_s = strstr(GPS_Frame_Buf, ",A,");
weidu_o = strstr(GPS_Frame_Buf, ",N,");
jingdu_o = strstr(GPS_Frame_Buf, ",E,");
if((weidu_s == NULL) || (weidu_o == NULL) ||(jingdu_o == NULL) )
{
rtn = FALSE;
}
else
{
memset(weidu_buf,,sizeof(weidu_buf));
memset(jindu_buf,,sizeof(jindu_buf));
memcpy(weidu_buf, weidu_s+, (weidu_o-weidu_s-));
memcpy(jindu_buf, weidu_o+, (jingdu_o-weidu_o-));
printf("\r\n---------------------------------------");
printf("\r\nGet GPS Frame:\r\n%s\r\n", GPS_Frame_Buf);
printf("\r\n---------------------------------------");
rtn = TRUE;
}
return rtn;
}
6. 总结
本文总结了GPS模块的基本使用方法,可以得到经度和纬度信息。对于此类的模块产品,主要工作有两大部分:
1:模块的熟悉,包括配置和指令的使用,一般可以一边读文档一边跑下demo体验效果;
2:字符串的解析。
同样的经验可以应用于很多串口模块。
例如GPRS模块,蓝牙模块,zigbee模块,TCP模块,CAN模块,485模块。
这几种产品我都使用过,其基本思路是一样的,调试时候可以先用串口助手模拟MCU来发数据,其实大部分模块都有自己的PC端工具,可以很快的看到效果。
使用模块可以极大地提升项目效率,但是灵活性和成本上会有所牺牲。另外,由于模块的封闭性,一般都要写很多的异常保护处理来保证产品的正常工作。
U-BLOX GPS 模块及GPRMC指令解析的更多相关文章
- MSP430与ATK-NEO-6M GPS模块
近短时间在网上买了一个GPS模块,正好正在学习MSP430单片机,于是决心将GPS模块与MSP430结合起来,同时将代码贴出来,发现网上搜到好多资料都要注册才能下载,有些还要钱.自己动脑,才能自娱自乐 ...
- 树莓派连接GPS模块
一月份的时候觉得好玩买了树莓派,但是太懒没怎么研究,但最近当初买树莓派时的那个梦想又萦绕心头,决定抽空完成一下当年的计划~ GPS模块是其中很重要的一环,于是在某宝上搜索,找了一家相对便宜也很轻巧的G ...
- AngularJS 指令解析(二)
AngularJS 指令解析(二) 第一篇我们讲过了作用域(scope)这块内容,现在我们进入正题,讲AngularJS的指令. 什么是指令? 这里我们引用官方的一句话: Custom directi ...
- 时区之痒 - 从手机GPS模块获取的时间,真的是北京时间么?
去年互联网地图行业开始引入众包模式,国内比较大的地图商,比如四维图新.高德地图.百度地图纷纷开始推出UGC应用,众包给用户采集门址.公交站等信息,并按照工作量给与采集者一定的回报.我曾经玩过某德推出的 ...
- 检测android机器是否有GPS模块
public boolean hasGPSDevice(Context context) { final LocationManager mgr = (LocationManager)context. ...
- seajs模块标识命名和解析规则
模块标识采用路径形式,但要注意与路径的区别.require.require.async的第一个参数是模块标识.而seajs.use第一个参数为文件路径. use是全局的,require是局部的.模块标 ...
- # nodejs模块学习: express 解析
# nodejs模块学习: express 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子 ...
- 迅为IMX6开发板支持全网通4G模块丨GPS模块丨WIFI蓝牙丨千兆以太网
迅为i.MX6开发板丨迅为i.MX6Q开发板丨四核imx6开发板丨Cortec-A9开发板丨资料介绍: 特点: 处理器:Freescale Cortex-A9四核i.MX6Q主频1GHz 核心板配置: ...
- GPS模块输出的NMEA数据ddmm.mmmm转换成dd.ddddd并在google Earth Pro中描点
GPS模块输出的数据是NMEA格式,其中GPGGA字段包含我们需要的经纬度信息. 例:$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,12 ...
随机推荐
- 【C#】回调方法不通过object参数获得委托实例
回调方法中几乎都会存在获取委托实例的需求,进而通过委托实例调用EndInvoke以得到异步执行的返回值.在我看过的相关文章中,获取委托实例的方法几乎都是同一个,就是向BeginInvoke的最后一个参 ...
- C++宏和枚举
宏 我们的计算器程序,用1234对应加减乘除,对于人阅读很产生一点障碍.隔一个月后再看此代码可能想不起是0123还是1234了,还得去代码中查找,如果能为代表四则运算的四个数取个有意义的别名就好了,一 ...
- jQuery使用经验建议
在开发过很多 jQuery 插件以后,我慢慢的摸索出了一套开发jQuery插件比较标准的结构和模式.这样我就可以复制并粘贴大部分的代码结构,只要专注最主要的逻辑代码就行了. 使用相同的设计模式和架构也 ...
- 【C#】VS2015开发环境的安装和配置(一)2016-08-03更新
分类:C#.VS2015.WPF.ASP.NET MVC.Android.iOS.Unity3D: 更新日期:2016-08-03 一.简介 为了避免网上乱七八糟的过时介绍,避免误导初学者,这次把至2 ...
- MVC中几种常用ActionResult
一.定义 MVC中ActionResult是Action的返回结果.ActionResult 有多个派生类,每个子类功能均不同,并不是所有的子类都需要返回视图View,有些直接返回流,有些返回字符串等 ...
- 【Java每日一题】20161123
package Nov2016; import java.util.Scanner; public class Ques1123 { public static void main(String[] ...
- GC之详解CMS收集过程和日志分析
2016-08-23 关于GC的算法和垃圾收集器的种类就暂且不说了,网上有大把的资料供参考 话题引入 让我们先简单的看下整个堆年轻代和年老代的垃圾收集器组合(以下配合java8完美支持,其他版本可 ...
- PHP 单例模式继承的实现方式
/** * @author xiaojiang * 方式1 */ class father{ static $models = []; static public function getInstan ...
- jenkins 邮件配置
jenkins 自带一个邮件的通知的功能,但是自带的邮件通知功能,有点简单不能自定义信息,所以我们安装邮件插件Email-ext plugin.1.首先在系统管理-插件管理-安装Email-ext p ...
- Linux学习笔记16--Linux扩展权限
默认权限: 每一个终端都有一个umask属性,用于确定新建文件.新文件夹的默认权限. umask使用数字权限方式表示,如:022 一般来说,普通用户的默认umask是002,root用户的默认um ...