本次做的实验是获取GPS模块的原始数据,并对数据进行规格化处理后再输出,采用单线程方式实现,停止时需要强制中止(按CTRL+C),使用的设备是:瑞泰2410实验箱和HOLUX GR-87的GPS模块,该模块接到箱上的扩展槽上(对应COM1),整个实验过程涉及的步骤是:设置串口1的波特率等参数、读取通过COM1收到的GPS模块返回的原始数据,再进行分离Message ID为$GPRMC的项,并获取该串数据,处理后暂存到相应的数据结构中并输出。实验环境是:ARM+Linux。
本程序用到的文件有main.c、gps.h、gps.c、Makefile,下面给出源码如下:
main.c:
#include
#include
#include
#include
#include
#include "gps.h"
#define BAUDRATE B9600 //串口波特率,即GPS通过COM1发来的数据传输率
#define COM1 "/dev/ttyS1" //定义与GPS连接的串口1的实际位置,此位置需根据2410箱上的实际来设置
GPS_INFO gps_info; //存放分离后的gps数据,是一个结构体,在gps.h中定义
char GPS_BUF[1024]; //存放从串口读到的GPS原始数据
static int baud=BAUDRATE;
volatile int fd; //打开串口后保存的设备描述符
void* receive() //当收到一串数据时(以回车符作为分界来识别一串字符),则原样输出,当字符中第6个是C时,则表示是我们需要的数据串(以$GPRMC开头),则对该串进行分离出时间及经纬度并输出来,否则只是原样输出。
{
int i=0;
char c;
char buf[1024];
printf("read gps\n");
while (1)
{
read(fd,&c,1); //从打开的串口COM1(因为GPS模块接到了COM1)读取1个字符
buf[i++] = c;
if(c == '\n'){
strncpy(GPS_BUF,buf,i); //从buf中复制i个字符到GPS_BUF中
i=0; //重置数据的起始位置
printf("%s",GPS_BUF); //原样输出
if(buf[5]=='C'){
gps_parse(GPS_BUF,&gps_info); //分离原始数据
show_gps(&gps_info); //显示分离后的数据
}
}
}
printf("exit from reading gps\n");
return NULL;
}
int main()
{
struct termios options; //串口设置的结构
fd=open(COM1,O_RDWR);
if (fd <0) {
perror(COM1);
exit(-1);
}
tcgetattr(fd,&options);
options.c_cflag = baud | CRTSCTS | CS8 | CLOCAL | CREAD;
options.c_iflag = IGNPAR; //忽略奇偶校验
options.c_oflag = 0; //Raw 模式输出 //不处理,就原始数据输出
options.c_lflag = 0; //不处理,原始数据输入
options.c_cc[VMIN]=1; // 阻塞,直到读取到一个字符
options.c_cc[VTIME]=0; // 不使用字符间的计时器
//清空数据线,启动新的串口设置
tcflush(fd, TCIFLUSH); //刷新输入队列
tcsetattr(fd,TCSANOW,&options); //新属性设置生效
if(receive()==NULL) exit(-1);调用接收函数收取GPS获到的数据
exit(0);
}
gps.h:
#ifndef _GPS_H
#define _GPS_H
typedef struct{
int year; //年
int month; //月
int day; //日
int hour; //时
int minute; //分
int second; //秒
}date_time; //时间结构体
typedef struct{
date_time D;//时间
double latitude; //纬度
double longitude; //经度
char NS; //南北极
char EW; //东西
}GPS_INFO;
void gps_parse(char *line,GPS_INFO *GPS);
void show_gps(GPS_INFO *GPS);
#endif
gps.c:
#include
#include
#include
#include
#include "gps.h"
static int GetComma(int num,char* str); //获得str字符指针对应字符串中第num个逗号的位置
static void UTC2BTC(date_time *GPS); //将GPS获得的UTC时间转换成北京时间
static double get_double_number(char *s); //获得两个逗号中间的浮点数,并转成double值返回
void show_gps(GPS_INFO *GPS)//输出GPS分离原始数据后的可用数据(包含时间及经纬度)
{
printf("DATE : %ld-%02d-%02d \n",GPS->D.year,GPS->D.month,GPS->D.day);
printf("TIME : %02d:%02d:%02d \n",GPS->D.hour,GPS->D.minute,GPS->D.second);
printf("Latitude : %10.4f %c\n",GPS->latitude,GPS->NS);
printf("Longitude: %10.4f %c\n",GPS->longitude,GPS->EW);
}
////////////////////////////////////////////////////////////////////////////////
//解释gps发出的数据
//0 7 0 4 6 0 6 8 0 90 0 3 0 9
//$GPRMC,091400,A,3958.9870,N,11620.3278,E,000.0,000.0,120302,005.6,W*62
void gps_parse(char *line,GPS_INFO *GPS)
{
int tmp;
char c;
char* buf=line;
c=buf[5];
if(c=='C'){//"GPRMC"
GPS->D.hour =(buf[ 7]-'0')*10+(buf[ 8]-'0'); //原始数据串中第7位(从0计起)为时钟的十位,而8为个位
GPS->D.minute =(buf[ 9]-'0')*10+(buf[10]-'0'); // 9 分钟 10
GPS->D.second =(buf[11]-'0')*10+(buf[12]-'0'); // 11 秒钟 12
tmp = GetComma(9,buf); //获得buf中第9个逗号的起始位置,即在数据buf中的下标
GPS->D.day =(buf[tmp+0]-'0')*10+(buf[tmp+1]-'0');
GPS->D.month =(buf[tmp+2]-'0')*10+(buf[tmp+3]-'0');
GPS->D.year =(buf[tmp+4]-'0')*10+(buf[tmp+5]-'0')+2000;
GPS->latitude =get_double_number(&buf[GetComma(3,buf)]); //纬度
GPS->NS =buf[GetComma(4,buf)]; //南s北n
GPS->longitude=get_double_number(&buf[GetComma(5,buf)]); //经度
GPS->EW =buf[GetComma(6,buf)]; //东e西w
UTC2BTC(&GPS->D);
}
}
//获得在字符串s中第一个逗号前的数值,并转换为浮点数返回
static double get_double_number(char *s)
{
char buf[128];
int i;
double rev;
i=GetComma(1,s);
strncpy(buf,s,i);
buf[i]=0;
rev=atof(buf);
return rev;
}
//返回str中第num个逗号的下一位置(从0起)
static int GetComma(int num,char *str)
{
int i,j=0;
int len=strlen(str);
for(i=0;i {
if(str[i]==',')j++;
if(j==num)return i+1;
}
return 0;
}
//将世界时转换为北京时
static void UTC2BTC(date_time *GPS)
{
//如果秒号先出,再出时间数据,则将时间数据+1秒
GPS->second++; //加一秒
if(GPS->second>59){
GPS->second=0;
GPS->minute++;
if(GPS->minute>59){
GPS->minute=0;
GPS->hour++;
}
}
GPS->hour+=8; //北京时间与世界时间相差8个时区,即相差8个钟
if(GPS->hour>23)
{
GPS->hour-=24;
GPS->day+=1;
if(GPS->month==2 || GPS->month==4 || GPS->month==6 || GPS->month==9 || GPS->month==11 ){
if(GPS->day>30){
GPS->day=1;
GPS->month++;
}
}
else{
if(GPS->day>31){
GPS->day=1;
GPS->month++;
}
}
if((GPS->year % 4 ==0) && (GPS->year % 400 == 0 || GPS->year % 100 != 0)){ //判断闰年
if(GPS->day > 29 && GPS->month ==2){ //闰年二月比平年二月多一天
GPS->day=1;
GPS->month++;
}
}
else{
if(GPS->day>28 &&GPS->month ==2){
GPS->day=1;
GPS->month++;
}