GPS模塊使用的是NMEA-0183 協議,NMEA-0183 是美國國家海洋電子協會(National MarineElectronics Association)所指定的標准規格,這一標准制訂所有航海電子儀器間的通訊標准,其中包含傳輸資料的格式以及傳輸資料的通訊協議。所以通常情況下,只需要通過串口讀取信息,通過字符串解析的方式把需要的數據分離出來就可以得到GPS數據。
具體的協議內容可以參見本文附錄的參考資料,在具體的的字符串解析中,實際只需要解析$GPGGA、$GPRMC兩個語句即可獲得我們所需要的全部內容,包括經緯度,時間,搜星狀態,衛星數量,高度,速度以及其他信號等,不同數據之間在獲取的字符串中是使用逗號隔開的,而相對位置固定,因此整體的思路就是讀取字符串,通過逗號位置判別對應數據,實現讀取分析。
作為數據讀取分析的基本,嵌入式linux的串口編程就是基礎中的基礎了,串口的設置主要是設置struct termios結構體的各成員值。termios是在POSIX規范中定義的標准接口,表示終端設備(包括虛擬終端、串口等)。口是一種終端設備,一般通過終端編程接口對其進行配置和控制。在具體講解串口相關編程之前,先了解一下終端相關知識。
終端有3種工作模式,分別為規范模式(canonical mode)、非規范模式(non-canonical mode)和原始模式(raw mode)。通過在termios結構的c_lflag中設置ICANNON標志來定義終端是以規范模式(設置ICANNON標志)還是以非規范模式(清除ICANNON標志)工作,默認情況為規范模式。
在規范模式下,所有的輸入是基於行進行處理。在用戶輸入一個行結束符(回車符、EOF等)之前,系統調用read()函數讀不到用戶輸入的任何字符。除了EOF之外的行結束符(回車符等)與普通字符一樣會被read()函數讀取到緩沖區之中。在規范模式中,行編輯是可行的,而且一次調用read()函數最多只能讀取一行數據。如果在read()函數中被請求讀取的數據字節數小於當前行可讀取的字節數,則read()函數只會讀取被請求的字節數,剩下的字節下次再被讀取。
在非規范模式下,所有的輸入是即時有效的,不需要用戶另外輸入行結束符,而且不可進行行編輯。在非規范模式下,對參數MIN(c_cc[VMIN])和TIME(c_cc[VTIME])的設置決定read()函數的調用方式。設置可以有4種不同的情況。
按照嚴格意義來講,原始模式是一種特殊的非規范模式。在原始模式下,所有的輸入數據以字節為單位被處理。在這個模式下,終端是不可回顯的,而且所有特定的終端輸入/輸出控制處理不可用。通過調用cfmakeraw()函數可以將終端設置為原始模式,而且該函數通過以下代碼可以得到實現。
實現環境Qt
這段程序為自行修改,實際測試通過,完成了串口配置和數據處理功能,可以直接解析獲得GPS相關參數。
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/signal.h> #include "string.h" #include "gps.h" #include <termios.h> #include <QtCore/QDebug> gps::gps() { baud=4800; GpsName=NULL; Gpsfd=0; for(int i=0;i<1024;i++) { GPS_BUF[i]=0; } GPS = new GPS_INFO; memset(GPS,0,sizeof(GPS_INFO)); } gps::~gps() { delete GPS; closeGpsDev(); tcsetattr(Gpsfd,TCSANOW,&oldtio); /* restore old modem setings */ tcsetattr(0,TCSANOW,&oldstdtio); /* restore old tty setings */ } //////////////////////////////////////////////////////////////////////////////// //解釋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 //$GPGGA,091400,3958.9870,N,11620.3278,E,1,03,1.9,114.2,M,-8.3,M,,*5E void gps::gps_parse() //////////////////////////////////////////////////////////////////////////////// { int tmp; char c; c = GPS_BUF[5]; if(c=='C') { //"GPRMC" GPS->D.hour =(GPS_BUF[ 7]-'0')*10+(GPS_BUF[ 8]-'0'); GPS->D.minute =(GPS_BUF[ 9]-'0')*10+(GPS_BUF[10]-'0'); GPS->D.second =(GPS_BUF[11]-'0')*10+(GPS_BUF[12]-'0'); tmp = GetComma(9,GPS_BUF); GPS->D.day =(GPS_BUF[tmp+0]-'0')*10+(GPS_BUF[tmp+1]-'0'); GPS->D.month =(GPS_BUF[tmp+2]-'0')*10+(GPS_BUF[tmp+3]-'0'); GPS->D.year =(GPS_BUF[tmp+4]-'0')*10+(GPS_BUF[tmp+5]-'0')+2000; GPS->status = GPS_BUF[GetComma(2,GPS_BUF)]; GPS->latitude = get_locate(get_double_number(&GPS_BUF[GetComma(3,GPS_BUF)])); GPS->NS = GPS_BUF[GetComma(4,GPS_BUF)]; GPS->longitude= get_locate(get_double_number(&GPS_BUF[GetComma(5,GPS_BUF)])); GPS->EW = GPS_BUF[GetComma(6,GPS_BUF)]; GPS->speed = get_double_number(&GPS_BUF[GetComma(7,GPS_BUF)]); UTC2BTC(&GPS->D); } if(c=='A') { //"$GPGGA" GPS->high = get_double_number(&GPS_BUF[GetComma(9,GPS_BUF)]); } } //將獲取文本信息轉換為double型 double gps::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; } double gps::get_locate(double temp) { int m; double n; m=(int)temp/100; n=(temp-m*100)/60; n=n+m; return n; } //////////////////////////////////////////////////////////////////////////////// //得到指定序號的逗號位置 int gps::GetComma(int num,char *str) { int i,j=0; int len=strlen(str); for(i=0;i<len;i++) { if(str[i]==',') { j++; } if(j==num) return i+1; } return 0; } //////////////////////////////////////////////////////////////////////////////// //將世界時轉換為北京時 void gps::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; 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 ){// 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++; } } if(GPS->month>12){ GPS->month-=12; GPS->year++; } } } void gps::receive(void) { int i=0; char c; char buf[1024]; volatile bool t=true; for(int i=0;i<1024;i++) { GPS_BUF[i]='\0'; } while (t) { if(read(Gpsfd,&c,1) < 0 ) { qDebug()<<"read Gpsdev error!"; return ; } buf[i++] = c; if(c == '\n') { strncpy(GPS_BUF,buf,i); i=0; t=false; } } } int gps::openGpsDev() { Gpsfd = open("/dev/ttySAC1",O_RDWR); if (Gpsfd < 0) { qDebug()<<"Cannot open gps device"<<endl; close(Gpsfd); return -1; } qDebug()<<"open gps device OK!"<<endl; return Gpsfd; } int gps::closeGpsDev() { close(Gpsfd); qDebug()<<"close gps device OK!"<<endl; return 0; } int gps::initGpsDev() { set_opt(Gpsfd,4800,8,'N',1); return 0; } void gps::readGpsDev(void) { receive(); gps_parse(); int lat=(int)(GPS->latitude*10000); int lon=(int)(GPS->longitude*10000); // qDebug()<<"gps"<<"lat="<<lat<<endl; // qDebug()<<"gps"<<"lon="<<lon<<endl; } int gps::set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) { struct termios newtio,oldtio; if ( tcgetattr( fd,&oldtio) != 0) { perror("SetupSerial 1"); return -1; } bzero( &newtio, sizeof( newtio ) ); newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; switch( nBits ) { case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; } switch( nEvent ) { case 'O': //奇校驗 newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; newtio.c_iflag |= (INPCK | ISTRIP); break; case 'E': //偶校驗 newtio.c_iflag |= (INPCK | ISTRIP); newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD; break; case 'N': //無校驗 newtio.c_cflag &= ~PARENB; break; } switch( nSpeed ) { case 2400: cfsetispeed(&newtio, B2400); cfsetospeed(&newtio, B2400); break; case 4800: cfsetispeed(&newtio, B4800); cfsetospeed(&newtio, B4800); break; case 9600: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; case 115200: cfsetispeed(&newtio, B115200); cfsetospeed(&newtio, B115200); break; default: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; } if( nStop == 1 ) { newtio.c_cflag &= ~CSTOPB; } else if ( nStop == 2 ) { newtio.c_cflag |= CSTOPB; } newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 0; tcflush(fd,TCIFLUSH); if((tcsetattr(fd,TCSANOW,&newtio))!=0) { qDebug()<<"com set error"<<endl; return -1; } qDebug()<<"set done!"<<endl; return 0; }