天天看點

android關于GPS hal層的分析

主要: Samsung   Cortex  ARM A8  smdkc110  1G

Gps:    Ublox-G6010

系統:  android 2.3

以下篇幅都是本人的一些建議以及做法

在android裡關于普通GPS子產品(俗稱硬GPS)相對來說是比較簡單的,因為android都幫你封裝好了,我們要做的稍後做詳解。

1、  首先拿到一個GPS子產品我們先判斷是硬GPS、還是半軟半硬的GPS,隻要不用于某個行業或者對于定位精度很高的話一般來說都會用硬GPS,關于半軟半硬的GPS詳解到時請參考部落格高精度GPS

2、  分析硬體原理圖,不過可能我個人在硬體原理圖知識很欠缺,是以對于GPS我隻關注三個方面電源控制、晶振和序列槽,這裡我司産用的是外部26M晶振和主要的UART3

3、  以上準備工作都好了話、下面就切入主題了,在Android系統中,關于GPS的實作位于:

Hardware/gps/    這裡隻分析hal層

    Framework: 

framework\base\services\java\com\android\server\systemServer.java

framework\services\java\com\android\server\LocationManagerService.java

frameworks\base\services\java\com\android\server\location\GpsLocationProvider.java

JNI:   /framework/base/services/jni/com_android_server_location_GpsLocationProvider.cpp

4、 

5、  這樣下來就可以吐出NMEA資料了,對于資料的NMEA格式這裡隻做稍微的講解

$資訊類型,xxx,xxx,xxx,xxx,xxx,xxx,xxx,

每行開頭的字元都是$,接着是資訊類型,後面是資料,用逗号隔開

資訊類型為:

GPGSV:可見衛星資訊

GPGLL:地理定位資訊

GPRMC:推薦最小定位資訊

GPVTG:地面速度資訊

GPGGA:GPS定位資訊

GPGSA:目前衛星資訊

這裡我們隻解析GPRMC和GPGGA的資訊

GPRMC資料詳解:

$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh

<1> UTC時間,hhmmss(時分秒)格式

<2> 定位狀态,A=有效定位,V=無效定位

<3> 緯度ddmm.mmmm(度分)格式(前面的0也将被傳輸)

<4> 緯度半球N(北半球)或S(南半球)

<5> 經度dddmm.mmmm(度分)格式(前面的0也将被傳輸)

<6> 經度半球E(東經)或W(西經)

<7> 地面速率(000.0~999.9節,前面的0也将被傳輸)

<8> 地面航向(000.0~359.9度,以真北為參考基準,前面的0也将被傳輸)

<9> UTC日期,ddmmyy(日月年)格式

<10> 磁偏角(000.0~180.0度,前面的0也将被傳輸)

<11> 磁偏角方向,E(東)或W(西)

<12> 模式訓示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)

解析内容:

$GPRMC,030254.00,A,2232.79596,N,11355.90127,E,0.028,,120313,,

1. 時間,這個是格林威治時間,是世界時間(UTC),我們需要把它轉換成中原標準時間(BTC),BTC和UTC差了8個小時,要在這個時間基礎上加8個小時。

2. 定位狀态,在接收到有效資料前,這個位是‘V’,後面的資料都為空,接到有效資料後,這個位是‘A’,後面才開始有資料。

3. 緯度,我們需要把它轉換成度分秒的格式,計算方法:

如接收到的緯度是:2232.79596

2232.79596 / 100  =  22.3279596   可以直接讀出22度

2232.79596–22 * 100 = 32.79596   可以直接讀出32分

32.79596–32 = 0.79596 * 60 = 47.7576  讀出47秒

是以緯度是:22度32分47秒。

4. 南北緯,這個位有兩種值‘N’(北緯)和‘S’(南緯)

5. 經度的計算方法和緯度的計算方法一樣

6. 東西經,這個位有兩種值‘E’(東經)和‘W’(西經)

7. 速率,這個速率值是 海裡/時,機關是節,要把它轉換成千米/時,根據:1海裡 = 1.85公裡,把得到的速率乘以1.85。

8. 航向,指的是偏離正北的角度

9. 日期,這個日期是準确的,不需要轉換

GPGGA資料詳解:

$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*xx<CR><LF>

例如:$GPGGA,030254.00,2232.79596,N,11355.90127,E,1,09,0.86,89.4,M,-2.7,M,,*7D$GPGGA:起始引導符及語句格式說明(本句為GPS定位資料);

<1>  UTC時間,格式為hhmmss.sss;

<2>  緯度,格式為ddmm.mmmm(第一位是零也将傳送);

<3>  緯度半球,N或S(北緯或南緯)

<4>  經度,格式為dddmm.mmmm(第一位零也将傳送);

<5>  經度半球,E或W(東經或西經)

<6>  定位品質訓示,0=定位無效,1=定位有效;

<7>  使用衛星數量,從00到12(第一個零也将傳送)

<8>  水準精确度,0.5到99.9

<9>  天線離海平面的高度,-9999.9到9999.9米  M  指機關米

<10> 大地水準面高度,-9999.9到9999.9米  M    指機關米

<11> 差分GPS資料期限(RTCM SC-104),最後設立RTCM傳送的秒數量

<12>  差分參考基站标号,從0000到1023(首位0也将傳送)。

首先看幾個重要的結構體

點選(此處)折疊或打開

  1. a)    結構體資訊
  2. typedef struct {
  3.     size_t size;
  4.     uint16_t flags;//标志位
  5.     double latitude;//緯度
  6.     double longitude; //經度
  7.     double altitude; //高度資訊
  8.     float speed; //速度
  9.     float bearing; //方向
  10.     float accuracy; //精确度
  11.     GpsUtcTime timestamp; //時間戳
  12. } GpsLocation;//表示GPS定位資訊
  13. typedef struct {
  14.     size_t size;
  15.     GpsStatusValue status;
  16. } GpsStatus;//狀态資訊,有以下幾種狀态
  17. typedef uint16_t GpsStatusValue;
  18. #define GPS_STATUS_NONE 0//未知狀态
  19. #define GPS_STATUS_SESSION_BEGIN 1 //已經開始導航
  20. #define GPS_STATUS_SESSION_END 2//停止導航
  21. #define GPS_STATUS_ENGINE_ON 3//已經通電但沒有導航
  22. #define GPS_STATUS_ENGINE_OFF 4//沒有通電狀态
  23. typedef struct {
  24.     size_t size;
  25.     int (*init)( GpsCallbacks* callbacks );//初始化GPS,設定回調函數GpsCallbacks
  26.     int (*start)( void ); //開始導航
  27.     int (*stop)( void ); //停止導航
  28.     void (*cleanup)( void ); //關閉接口
  29.     int (*inject_time)(GpsUtcTime time, int64_t timeReference, //置入目前時間
  30.                          int uncertainty);
  31.     int (*inject_location)(double latitude, double longitude, float accuracy); //置入未知資訊
  32.     void (*delete_aiding_data)(GpsAidingData flags); //删除幫助資料
  33.     int (*set_position_mode)(GpsPositionMode mode, int fix_frequency); //位置模式
  34.     const void* (*get_extension)(const char* name);
  35. } GpsInterface; //很重要
  36. typedef struct {
  37.     size_t size;
  38.     gps_location_callback location_cb;//位置資訊回調
  39.     gps_status_callback status_cb; //狀态資訊回調
  40.     gps_sv_status_callback sv_status_cb; //SV資訊狀态回調
  41.     gps_nmea_callback nmea_cb; //NMEA資料回調
  42.     gps_set_capabilities set_capabilities_cb;
  43.     gps_acquire_wakelock acquire_wakelock_cb;//申請鎖回調
  44.     gps_release_wakelock release_wakelock_cb;//釋放鎖回調
  45.     gps_create_thread create_thread_cb;//建立線程回調
  46. } GpsCallbacks;
  47. typedef struct {
  48.     int init;
  49.     int fd;
  50.     GpsCallbacks callbacks;
  51.     pthread_t thread;
  52.     int control[2];
  53. } GpsState;
  54. typedef struct {
  55.     int pos;
  56.     int overflow;
  57.     int utc_year;
  58.     int utc_mon;
  59.     int utc_day;
  60.     int utc_diff;
  61.     GpsLocation fix;
  62.     GpsSvStatus     sat_status;
  63.     GpsCallbacks callback;
  64.     char in[ NMEA_MAX_SIZE+1 ];
  65. } NmeaReader; 

以一個定位的應用來說明函數執行流程:一般情況下GPS預設是關閉的,是以要先

android關于GPS hal層的分析

點選(此處)折疊或打開

  1. 以下看具體函數的實作:
  2. static int qemu_gps_init(GpsCallbacks* callbacks)
  3. {
  4.     GpsState* s = _gps_state;
  5.     s->callbacks = *callbacks; //注冊回調函數,JNI傳下來的回調函數
  6.     g_status.status=GPS_STATUS_ENGINE_ON;//設定狀态 通電但還沒開始導航
  7.     s->callbacks.status_cb(&g_status);
  8.     if (!s->init)
  9.         gps_state_init(s);
  10.     if (s->fd < 0)
  11.         return -1;
  12.      return 0;
  13. }
  14. static void gps_state_init( GpsState* state )
  15. {
  16.     state->init = 1;
  17.     state->control[0] = -1;
  18.     state->control[1] = -1;
  19.     state->fd = -1;
  20.     int ret=-1;
  21.        GPS_PowerON(); //通過檔案操作到驅動層控制電源開關
  22.      state->fd= open("/dev/s3c2410_serial3",O_RDWR|O_NOCTTY|O_NDELAY);//這裡用的是UART3
  23.      if( state->fd < 0){
  24.             LOGE("open port /dev/s3c2410_serial3 ERROR..state->fd=%d\n",state->fd); 
  25.             exit(0);
  26.         }else
  27.             LOGE("open port:/dev/s3c2410_serial3 succceed..state->fd=%d\n",state->fd);
  28.         if(fcntl( state->fd,F_SETFL,0)<0)
  29.             LOGE("fcntl F_SETFL\n");
  30.         {
  31.     LOGI(">>>> Port setup..\n");
  32.     int err;
  33.       tcflush(state->fd, TCIOFLUSH);//以下是配置序列槽的參數
  34.     if ((err = tcgetattr(state->fd,&termios)) != 0)
  35.     {
  36.         LOGI("tcgetattr(%d) = %d,errno %d\r\n",state->fd,err,errno);
  37.         close(state->fd);
  38.     }
  39.       termios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
  40.       termios.c_oflag &= ~OPOST;
  41.       termios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
  42.       termios.c_cflag &= ~(CSIZE|PARENB);
  43.       termios.c_cflag |= CS8;
  44.       termios.c_cflag &= ~CRTSCTS;//no flow control
  45.     tcsetattr(state->fd, TCSANOW, &termios);
  46.     tcflush(state->fd, TCIOFLUSH);
  47.     tcsetattr(state->fd, TCSANOW, &termios);
  48.     tcflush(state->fd, TCIOFLUSH);
  49.     tcflush(state->fd, TCIOFLUSH);
  50.     if (cfsetispeed(&termios,B9600))//具體的參考GPS文檔
  51.        {
  52.         LOGE("cfsetispeed.. errno..\r\n");
  53.               close(state->fd);
  54.               //return(-1);
  55.        }
  56.        // Set the output baud rates in the termios.
  57.       if (cfsetospeed(&termios,B9600))
  58.        {
  59.         LOGE("cfsetispeed.. errno..\r\n");
  60.               close(state->fd);
  61.               //return(-1);
  62.        }
  63.     tcsetattr(state->fd,TCSANOW,&termios);
  64.     LOGE("Port setup finished..\n");
  65. }
  66. if (state->fd < 0) {
  67.         LOGD("no gps emulation detected");
  68.         return;
  69.     }
  70.     if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
  71.         LOGE("could not create thread control socket pair: %s", strerror(errno));
  72.         goto Fail;
  73.     }
  74.  //建立 gps服務線程
  75. state->thread=state->callbacks.create_thread_cb("gps_state_thread",gps_state_thread,state);
  76. }
  77. static void* gps_state_thread( void*  arg )

    {

        GpsState*   state = (GpsState*) arg;

        NmeaReader  reader[1];

        int         epoll_fd   = epoll_create(2);

        int         started    = 0;

        int         gps_fd     = state->fd;

        int         control_fd = state->control[1];

        nmea_reader_init( reader );

        //注冊監控control_fd gps_fd

        epoll_register( epoll_fd, control_fd );

        epoll_register( epoll_fd, gps_fd );

        LOGD("gps thread running");

        // now loop

        for (;;) {

            struct epoll_event   events[2];

            int                  ne, nevents;

            //當control_fd或gps_fd 有資料寫入時程式往下走,否則阻塞。

                   //init 到此結束

            nevents = epoll_wait( epoll_fd, events, 2, -1 );

            if (nevents < 0) {

                if (errno != EINTR)

                    LOGE("epoll_wait() unexpected error: %s", strerror(errno));

                continue;

            }

          //  LOGD("gps thread received %d events", nevents);

            for (ne = 0; ne < nevents; ne++) {

                if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {

                    LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");

                    goto Exit;

                }

                if ((events[ne].events & EPOLLIN) != 0) {

                    int  fd = events[ne].data.fd;

                    if (fd == control_fd)

                    {

                        char  cmd = 255;

                        int   ret;

                       // LOGD("gps control fd event");

                        do {

                            ret = read( fd, &cmd, 1 );

                        } while (ret < 0 && errno == EINTR);

                        if (cmd == CMD_QUIT) {

                            LOGD("gps thread quitting on demand");

                            goto Exit;

                        }

                        else if (cmd == CMD_START) {

                            if (!started)

    {

                              started = 1;

                                                              g_status.status=GPS_STATUS_SESSION_BEGIN;//開始導航

                           state->callbacks.status_cb(&g_status); //上傳gps狀态

                               //傳遞回調函數,解碼時reader作為參數傳下去

                               nmea_reader_set_callback( reader, state->callbacks );

                            }

                        }

                        else if (cmd == CMD_STOP) {

                            if (started) {

                                started = 0;

                                                                     g_status.status=GPS_STATUS_SESSION_END; //停止導航

                             state->callbacks.status_cb(&g_status); //上傳gps狀态

                            }

                        }

                    }

                    else if (fd == gps_fd)

                    {

                        char  buff[32];

                        //LOGD("gps fd event");

                        for (;;) {

                            int  nn, ret;

                            ret = read( fd, buff, sizeof(buff) );

                            if (ret < 0) {

                                if (errno == EINTR)

                                    continue;

                                if (errno != EWOULDBLOCK)

                                    LOGE("error while reading from gps daemon socket: %s:", strerror(errno));

                                break;

                            }

                            //LOGD("received %d bytes: %.*s", ret, ret, buff);

                            for (nn = 0; nn < ret; nn++)

                                nmea_reader_addc( reader, buff[nn] );//解析重要的操作就是在這裡實作了

                        }

                        //LOGD("gps fd event end");

                    }

                    else

                    {

                        LOGE("epoll_wait() returned unkown fd %d ?", fd);

                    }

                }

            }

        }

    Exit:

             close(gps_fd);

             GPS_PowerDOWN();

        return NULL;

    }

    static void nmea_reader_addc( NmeaReader*  r, int  c )

    {

        if (r->overflow) {

            r->overflow = (c != '\n');

            return;

        }

        if (r->pos >= (int) sizeof(r->in)-1 ) {

            r->overflow = 1;

            r->pos      = 0;

            return;

        }

        r->in[r->pos] = (char)c;

        r->pos       += 1;

        if (c == '\n') {

            nmea_reader_parse( r );

            r->pos = 0;

        }

    }

    static void  nmea_reader_parse( NmeaReader*  r )

    {

        NmeaTokenizer  tzer[1];

        Token          tok;

        D("Received: '%.*s'", r->pos, r->in);

             if(r->callback.nmea_cb)

             {

             r->callback.nmea_cb(r->fix.timestamp,r->in,r->pos);

             }

        if (r->pos < 9) {

            //D("Too short. discarded.");

            return;

        }

        nmea_tokenizer_init(tzer, r->in, r->in + r->pos);//這裡不做詳細的描述了,這個函數根據逗号的标記來取出每個資料儲存

        tok = nmea_tokenizer_get(tzer, 0);

        if (tok.p + 5 > tok.end) {

               return;

        }

      //'$GPGGA,081945.00,2232.79556,N,11355.90154,E,1,09,0.88,94.8,M,-2.7,M,,*7A  為例

        // ignore first two characters.

        tok.p += 2;

        if ( !memcmp(tok.p, "GGA", 3) ) {

                       #if 1

            // GPS fix

            Token  tok_time          = nmea_tokenizer_get(tzer,1); //對應081945.00

            Token  tok_latitude      = nmea_tokenizer_get(tzer,2);

            Token  tok_latitudeHemi  = nmea_tokenizer_get(tzer,3);

            Token  tok_longitude     = nmea_tokenizer_get(tzer,4);

            Token  tok_longitudeHemi = nmea_tokenizer_get(tzer,5);

            Token  tok_altitude      = nmea_tokenizer_get(tzer,9);

            Token  tok_altitudeUnits = nmea_tokenizer_get(tzer,10);

            nmea_reader_update_time(r, tok_time);//更新時間

            nmea_reader_update_latlong(r, tok_latitude,

                                          tok_latitudeHemi.p[0],

                                          tok_longitude,

                                          tok_longitudeHemi.p[0]);

            nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);

                       if (r->fix.flags != 0) {

                     if (r->callback.location_cb ) {

                         r->callback.location_cb( &r->fix );//回調上傳資料

                         r->fix.flags = 0;

                     }

                       }

                       #endif

       如有什麼不對的地方還請指點,我好及時糾正