歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 嵌入式Linux中GPS信息讀取與處理

嵌入式Linux中GPS信息讀取與處理

日期:2017/3/1 9:46:00   编辑:Linux編程

GPS協議概要


GPS模塊使用的是NMEA-0183 協議,NMEA-0183 是美國國家海洋電子協會(National MarineElectronics Association)所指定的標准規格,這一標准制訂所有航海電子儀器間的通訊標准,其中包含傳輸資料的格式以及傳輸資料的通訊協議。所以通常情況下,只需要通過串口讀取信息,通過字符串解析的方式把需要的數據分離出來就可以得到GPS數據。

具體的協議內容可以參見本文附錄的參考資料,在具體的的字符串解析中,實際只需要解析$GPGGA、$GPRMC兩個語句即可獲得我們所需要的全部內容,包括經緯度,時間,搜星狀態,衛星數量,高度,速度以及其他信號等,不同數據之間在獲取的字符串中是使用逗號隔開的,而相對位置固定,因此整體的思路就是讀取字符串,通過逗號位置判別對應數據,實現讀取分析。

嵌入式Linux串口編程


作為數據讀取分析的基本,嵌入式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種不同的情況

  1. MIN = 0和TIME = 0:read()函數立即返回。若有可讀數據,則讀取數據並返回被讀取的字節數,否則讀取失敗並返回0
  2. MIN > 0和TIME = 0:read()函數會被阻塞直到MIN個字節數據可被讀取
  3. MIN = 0和TIME > 0:只要有數據可讀或者經過TIME個十分之一秒的時間,read()函數則立即返回,返回值為被讀取的字節數如果超時並且未讀到數據,則read()函數返回0。
  4. MIN > 0和TIME > 0:當有MIN個字節可讀或者兩個輸入字符之間的時間間隔超過TIME個十分之一秒時,read()函數才返回因為在輸入第一個字符之後系統才會啟動定時器,所以在這種情況下,read()函數至少讀取一個字節之後才返回

按照嚴格意義來講,原始模式是一種特殊的非規范模式在原始模式下,所有的輸入數據以字節為單位被處理。在這個模式下,終端是不可回顯的,而且所有特定的終端輸入/輸出控制處理不可用通過調用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;
}
Copyright © Linux教程網 All Rights Reserved