linux下编程实现GPS数据获取与解析

*************************************************************************************************************************************************

开发板:fl2440

开发模块:A7(GPRS/GPS)

*************************************************************************************************************************************************

前言:我使用的开发模块是安信可公司研发的A7模块,同时具有GPRS与GPS功能,要注意的是它出的是TTL电平,所以使用USB转串口线,得支持这样的电平才行。

一,硬件连接打开GPS及测试

我使用的USB转串口线上的芯片是cp210的,所以在内核配置的时候,必须把这个支持选上,否则,开发板不识别。

[zoulei@CentOS linux-3.0]$ make menuconfig

make menuconfig
    Device Drivers->
        [*]USB support ->
            [*]USB Serial Converter supportUSB CP210x family of UART Bridge Controllers
make编译之后重新烧录到开发板,开发板即可识别USB转串口线。

插上USB转串口线后,开发板上/dev/目录下出现ttyUSB0设备,说明识别成功。


1.将串口AT指令控制发送端(U_TXD)和串口AT指令控制接收端(U_RXD)分别与USB转TTL转接头上的RXD和TXD相连,GND与GND相连。另一端接入

开发板的USB口即可。

2.网线连接PC与开发板,使用putty软件远程登录开发板,至于怎么连接我前面有篇博客有介绍,可以参考一下:点击打开链接

3.将A7连上开发板以后,在开发板上使用microcom命令监听相关串口,USB转串口芯片连接则监听串口/dev/ttyUSB0。然后把GPS功能打开:

>: microcom -s 115200 /dev/ttyUSB0 
AT   //检测模块是否正常
OK   //正常回显OK
AT+GPS=1   //打开GPS功能
OK
4. 此时将A7模块上的U_TXD连线切换到A7模块上的GPS_TXD引脚,其他连线不变。以波特率为9600重新打开监听串口,将会不断的收到GPS定位的数据。


这时候会发现GPS已经正常工作了。

二,GPS数据解析

在编程实现获取GPS数据之前,先了解一下上面的数据分别表示什么意思。

如:$GPRMC,131913.000,A,3029.64972,N,11423.62352,E,0.00,0.00,200617,,,A*67

字段0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息

字段1:UTC时间,hhmmss.sss格式(UTC这个是格林威治时间即世界时间

字段2:状态,A=定位,V=未定位

字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)

字段4:纬度N(北纬)或S(南纬)

字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)

字段6:经度E(东经)或W(西经)

字段7:速度,节,Knots

字段8:方位角,度

字段9:UTC日期,DDMMYY格式

字段10:磁偏角,(000 - 180)度(前导位数不足则补0)

字段11:磁偏角方向,E=东W=西

字段12:模式,A=自动,D=差分,E=估测,N=数据无效(3.0协议内容)

字段13:校验值($与*之间的数异或后的值)

因为本次编程要获取的就是$GPRMC......这一行的信息,所以只分析这一行的,其他的百度百科有介绍,就不一一解释了。

三,编程获取信息

1.串口配置

由于我们是通过串口来进行数据传输,也就是把GPS定位的信息,通过串口最后输出到我们的终端设备上。

/*********************************************************************************
 *      Copyright:  (C) 2017 zoulei
 *                  All rights reserved.
 *
 *       Filename:  uart1.c
 *    Description:  This file
 *
 *        Version:  1.0.0(2017年06月19日)
 *         Author:  zoulei 
 *      ChangeLog:  1, Release initial version on "2017年06月19日 16时41分59秒"
 *
 ********************************************************************************/
#include 
#include 
#include 
#include    /* 文件控制定义*/
#include  /* PPSIX 终端控制定义*/
#include 
#include 
#include 
#include 


int set_serial(int fd,int nSpeed,int nBits,char nEvent,int nStop)
{
    struct termios newttys1,oldttys1;

     /*保存原有串口配置*/
     if(tcgetattr(fd,&oldttys1)!=0)
     {
          perror("Setupserial 1");
          return -1;
     }
     memset(&newttys1,0,sizeof(newttys1));/* 先将新串口配置清0 */
     newttys1.c_cflag|=(CLOCAL|CREAD ); /* CREAD 开启串行数据接收,CLOCAL并打开本地连接模式 */

     newttys1.c_cflag &=~CSIZE;/* 设置数据位 */
     /* 数据位选择 */
     switch(nBits)
     {
         case 7:
             newttys1.c_cflag |=CS7;
             break;
         case 8:
             newttys1.c_cflag |=CS8;
             break;
     }
     /* 设置奇偶校验位 */
     switch( nEvent )
     {
         case '0':  /* 奇校验 */
             newttys1.c_cflag |= PARENB;/* 开启奇偶校验 */
             newttys1.c_iflag |= (INPCK | ISTRIP);/*INPCK打开输入奇偶校验;ISTRIP去除字符的第八个比特  */
             newttys1.c_cflag |= PARODD;/*启用奇校验(默认为偶校验)*/
             break;
         case 'E':/*偶校验*/
             newttys1.c_cflag |= PARENB; /*开启奇偶校验  */
             newttys1.c_iflag |= ( INPCK | ISTRIP);/*打开输入奇偶校验并去除字符第八个比特*/
             newttys1.c_cflag &= ~PARODD;/*启用偶校验*/
             break;
         case 'N': /*无奇偶校验*/
             newttys1.c_cflag &= ~PARENB;
             break;
     }
     /* 设置波特率 */
    switch( nSpeed )
    {
        case 2400:
            cfsetispeed(&newttys1, B2400);
            cfsetospeed(&newttys1, B2400);
            break;
        case 4800:
            cfsetispeed(&newttys1, B4800);
            cfsetospeed(&newttys1, B4800);
            break;
        case 9600:
            cfsetispeed(&newttys1, B9600);
            cfsetospeed(&newttys1, B9600);
            break;
        case 115200:
            cfsetispeed(&newttys1, B115200);
            cfsetospeed(&newttys1, B115200);
            break;
        default:
            cfsetispeed(&newttys1, B9600);
            cfsetospeed(&newttys1, B9600);
            break;
    }
     /*设置停止位*/
    if( nStop == 1)/* 设置停止位;若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB */
    {
        newttys1.c_cflag &= ~CSTOPB;/*默认为一位停止位; */
    }
    else if( nStop == 2)
    {
        newttys1.c_cflag |= CSTOPB;/* CSTOPB表示送两位停止位 */
    }

    /* 设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时*/
    newttys1.c_cc[VTIME] = 0;/* 非规范模式读取时的超时时间;*/
    newttys1.c_cc[VMIN]  = 0; /* 非规范模式读取时的最小字符数*/
    tcflush(fd ,TCIFLUSH);/* tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来 */

     /*激活配置使其生效*/
    if((tcsetattr( fd, TCSANOW,&newttys1))!=0)
    {
        perror("com set error");
        exit(1);
    }

    return 0;
}
******************************************************************************************************************************************************************************
这个代码段最重要的就是这个结构体

struct termios
      {
      tcflag_t  c_iflag;  //输入选项
      tcflag_t  c_oflag;  //输出选项
      tcflag_t  c_cflag;  //控制选项
      tcflag_t  c_lflag;  //行选项
      cc_t      c_cc[NCCS]; //控制字符
      }; 
其中我们更关注的是 c_cflag 控制选项。其中包含了波特率、数据位、校验位、停止位的设置。 
它可以支持很多常量名称其中设置数据传输率为相应的数据传输率前要加上“B”

这篇博客分析的不错:点击打开链接

*********************************************************************************************************************************

2.gps数据分析

gps_analyse.c:

/*********************************************************************************
 *      Copyright:  (C) 2017 zoulei
 *                  All rights reserved.
 *
 *       Filename:  gps_analyse.c
 *    Description:  This file
 *
 *        Version:  1.0.0(2017年06月19日)
 *         Author:  zoulei 
 *      ChangeLog:  1, Release initial version on "2017年06月19日 12时16分39秒"
 *
 ********************************************************************************/
#include 
#include 
#include 
#include "gps.h"

int gps_analyse (char *buff,GPRMC *gps_data)
{
    char *ptr=NULL;
     if(gps_data==NULL)
      {
         return -1;
      }
      if(strlen(buff)<10)
      {
         return -1;
      }
/* 如果buff字符串中包含字符"$GPRMC"则将$GPRMC的地址赋值给ptr */
      if(NULL==(ptr=strstr(buff,"$GPRMC")))
      {
         return -1;
      }
/* sscanf函数为从字符串输入,意思是将ptr内存单元的值作为输入分别输入到后面的结构体成员 */
      sscanf(ptr,"$GPRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));
      return 0;
}

int print_gps (GPRMC *gps_data)
{
    printf("                                                           \n");
    printf("                                                           \n");
    printf("===========================================================\n");
    printf("==                   全球GPS定位导航模块                 ==\n");
    printf("==              Author:zoulei                           ==\n");
    printf("==              Email:[email protected]               ==\n");
    printf("==              Platform:fl2440                         ==\n");
    printf("===========================================================\n");
    printf("                                                           \n");
    printf("===========================================================\n");
    printf("==   GPS state bit : %c  [A:有效状态 V:无效状态]              \n",gps_data->pos_state);
    printf("==   GPS mode  bit : %c  [A:自主定位 D:差分定位]               \n", gps_data->mode);
    printf("==   Date : 20%02d-%02d-%02d                                  \n",gps_data->date%100,(gps_data->date%10000)/100,gps_data->date/10000);
    printf("==   Time : %02d:%02d:%02d                                   \n",(gps_data->time/10000+8)%24,(gps_data->time%10000)/100,gps_data->time%100);
    printf("==   纬度 : 北纬:%d度%d分%d秒                              \n", ((int)gps_data->latitude) / 100, (int)(gps_data->latitude - ((int)gps_data->latitude / 100 * 100)), (int)(((gps_data->latitude - ((int)gps_data->latitude / 100 * 100)) - ((int)gps_data->latitude - ((int)gps_data->latitude / 100 * 100))) * 60.0));
    printf("==   经度 : 东经:%d度%d分%d秒                              \n", ((int)gps_data->longitude) / 100, (int)(gps_data->longitude - ((int)gps_data->longitude / 100 * 100)), (int)(((gps_data->longitude - ((int)gps_data->longitude / 100 * 100)) - ((int)gps_data->longitude - ((int)gps_data->longitude / 100 * 100))) * 60.0));
    printf("==   速度 : %.3f  m/s                                      \n",gps_data->speed);
    printf("==                                                       \n");
    printf("============================================================\n");

    return 0;
}
*************************************************************************************************************************

说明:1.由于我直接获取的是格林威治时间即世界时间(UTC),所以要把它转换成北京时间(BTC),也就是在这个时间

基础上加8个小时。

           2. 经纬度,GPRMC返回的纬度数据位ddmm.mmmm格式即度分格式,我们把它转换成常见的度分秒的格式,计算方法:如接收到的纬度是:3029.60430 
3029.60430/100=30.2960430可以直接读出30度, 3029.60430–30*100=29.60430, 可以直接读出29分 
(29.60430–29)*60 =0.60430*60=36.258读出36秒, 所以纬度是:30度29分36秒。
             3.GPRMC返回的速率值是海里/时,单位是节,把它转换成千米/时,换算为:1海里=1.85公里,把得到的速率乘以1.85。

           4.航向指的是偏离正北的角度 .

           5.GPRMC的日期格式为:ddmmyy,如:200617表示2017年06月20日,这个日期是准确的,不需要转换.

************************************************************************************************************************************

3.主函数打开串口设备读操作

main.c:

/*********************************************************************************
 *      Copyright:  (C) 2017 zoulei
 *                  All rights reserved.
 *
 *       Filename:  main.c
 *    Description:  This file
 *
 *        Version:  1.0.0(2017年06月19日)
 *         Author:  zoulei 
 *      ChangeLog:  1, Release initial version on "2017年06月19日 14时55分30秒"
 *
 ********************************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include "gps.h"
#define GPS_LEN 1024
int set_serial(int fd,int nSpeed, int nBits, char nEvent, int nStop);
int gps_analyse(char *buff,GPRMC *gps_data);
int print_gps(GPRMC *gps_data);

int main (int argc, char **argv)
{
    int fd=0;
    int n=0;
    GPRMC gprmc;
    char buff[GPS_LEN];
    char *dev_name="/dev/ttyUSB0";

    if((fd=open(dev_name,O_RDWR|O_NOCTTY|O_NDELAY))<0)
    {
            perror("Can't Open the ttyUSB0 Serial Port");
            return -1;
    }
    set_serial( fd,9600,8,'N',1);

    while(1)
    {
       sleep(2);
       if((n=read(fd,buff,sizeof(buff)))<0)
        {
           perror("read error");
           return -1;
        }
        printf("buff:%s\n",buff);
        memset(&gprmc, 0 , sizeof(gprmc));
        gps_analyse(buff,&gprmc);
        print_gps(&gprmc);

    }
    close(fd);
    return 0;
}
*************************************************************************************************************************

说明:这段代码:fd=open(dev_name,O_RDWR|O_NOCTTY|O_NDELAY。是通过open系统调用打开串口设备ttyUSB0,
O_NOCTTY:是为了告诉Linux这个程序不会成为这个端口上的“控制终端”.如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程。 
O_NDELAY:这个标志则是告诉Linux这个程序并不关心DCD信号线的状态,也就是不管串口是否有数据到来,都是非阻塞的,程序继续执行。

******************************************************************************************************************************

gps.h:

/********************************************************************************
 *      Copyright:  (C) 2017 zoulei
 *                  All rights reserved.
 *
 *       Filename:  gps.h
 *    Description:  This head file
 *
 *        Version:  1.0.0(2017年06月19日)
 *         Author:  zoulei 
 *      ChangeLog:  1, Release initial version on "2017年06月19日 12时25分59秒"
 *
 ********************************************************************************/

#ifndef __GPS_H__
#define __GPS_H__

typedef unsigned int UINT;
typedef int BYTE;

typedef struct __gprmc__
{
   UINT time;/* gps定位时间 */
   char pos_state;/*gps状态位*/
   float latitude;/*纬度 */
   float longitude;/* 经度 */
   float speed; /* 速度 */
   float direction;/*航向 */
   UINT date;  /*日期  */
   float declination; /* 磁偏角 */
   char dd;
   char mode;/* GPS模式位 */

}GPRMC;
extern int set_option(int fd,int nSpeed, int nBits, char nEvent, int nStop);
extern int print_gps(GPRMC *gps_date);
extern int gps_analyse(char *buff,GPRMC *gps_data);
#endif
4.编写Makefile

CC=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc

objs=uart1.o gps_analyse.o main.o
srcs=uart1.c gps_analyse.c main.c

gps_test: $(objs)
        $(CC) -o gps_test $(objs)
        @make clean

main.o: $(srcs) gps.h
        $(CC) -c  $(srcs)

uart1.o:  uart1.c
        $(CC) -c  uart1.c

analyse_gps.o: gps_analyse.c gps.h
        $(CC) -c  gps_analyse.c

clean:
        rm *.o
make编译之后,生成gps_test可执行文件,当然这个名字可根据自己的爱好在Makefile里设置成自己想要的名字,然后将gps_test文件下载到开发板给予777

权限,然后运行测试.


5.测试

测试结果如图:









你可能感兴趣的:(3G/GPRS/GPS模块)