您的位置:首页 > 移动开发 > Android开发

android关于GPS hal层的分析

2015-10-09 16:49 435 查看
主控: 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

例如:$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也将传送)。

首先看几个重要的结构体

点击(此处)折叠或打开

a)    结构体信息

typedef struct {

    size_t size;

    uint16_t flags;//标志位

    double latitude;//纬度

    double longitude; //经度

    double altitude; //高度信息

    float speed; //速度

    float bearing; //方向

    float accuracy; //精确度

    GpsUtcTime timestamp; //时间戳

} GpsLocation;//表示GPS定位信息

typedef struct {

    size_t size;

    GpsStatusValue status;

} GpsStatus;//状态信息,有以下几种状态

typedef uint16_t GpsStatusValue;

#define GPS_STATUS_NONE 0//未知状态

#define GPS_STATUS_SESSION_BEGIN 1 //已经开始导航

#define GPS_STATUS_SESSION_END 2//停止导航

#define GPS_STATUS_ENGINE_ON 3//已经通电但没有导航

#define GPS_STATUS_ENGINE_OFF 4//没有通电状态

typedef struct {

    size_t size;

    int (*init)( GpsCallbacks* callbacks );//初始化GPS,设置回调函数GpsCallbacks

    int (*start)( void ); //开始导航

    int (*stop)( void ); //停止导航

    void (*cleanup)( void ); //关闭接口

    int (*inject_time)(GpsUtcTime time, int64_t
timeReference, //置入当前时间

                         int uncertainty);

    int (*inject_location)(double
latitude, double longitude, float accuracy); //置入未知信息

    void (*delete_aiding_data)(GpsAidingData
flags); //删除帮助数据

    int (*set_position_mode)(GpsPositionMode
mode, int fix_frequency); //位置模式

    const void* (*get_extension)(const char* name);

} GpsInterface; //很重要

typedef struct {

    size_t size;

    gps_location_callback location_cb;//位置信息回调

    gps_status_callback status_cb; //状态信息回调

    gps_sv_status_callback sv_status_cb; //SV信息状态回调

    gps_nmea_callback nmea_cb; //NMEA数据回调

    gps_set_capabilities set_capabilities_cb;

    gps_acquire_wakelock acquire_wakelock_cb;//申请锁回调

    gps_release_wakelock release_wakelock_cb;//释放锁回调

    gps_create_thread create_thread_cb;//创建线程回调

} GpsCallbacks;

typedef struct {

    int init;

    int fd;

    GpsCallbacks callbacks;

    pthread_t thread;

    int control[2];

} GpsState;

typedef struct {

    int pos;

    int overflow;

    int utc_year;

    int utc_mon;

    int utc_day;

    int utc_diff;

    GpsLocation fix;

    GpsSvStatus     sat_status;

    GpsCallbacks callback;

    char in[ NMEA_MAX_SIZE+1 ];

} NmeaReader; 

以一个定位的应用来说明函数执行流程:一般情况下GPS默认是关闭的,所以要先



点击(此处)折叠或打开

以下看具体函数的实现:

static int qemu_gps_init(GpsCallbacks* callbacks)

{

    GpsState* s = _gps_state;

    s->callbacks = *callbacks; //注册回调函数,JNI传下来的回调函数

    g_status.status=GPS_STATUS_ENGINE_ON;//设置状态
通电但还没开始导航

    s->callbacks.status_cb(&g_status);

    if (!s->init)

        gps_state_init(s);

    if (s->fd < 0)

        return -1;

     return 0;

}

static void gps_state_init( GpsState* state )

{

    state->init = 1;

    state->control[0] = -1;

    state->control[1] = -1;

    state->fd = -1;

    int ret=-1;

       GPS_PowerON(); //通过文件操作到驱动层控制电源开关

     state->fd= open("/dev/s3c2410_serial3",O_RDWR|O_NOCTTY|O_NDELAY);//这里用的是UART3

     if( state->fd < 0){

            LOGE("open port /dev/s3c2410_serial3 ERROR..state->fd=%d\n",state->fd); 

            exit(0);

        }else

            LOGE("open port:/dev/s3c2410_serial3 succceed..state->fd=%d\n",state->fd);

        if(fcntl( state->fd,F_SETFL,0)<0)

            LOGE("fcntl F_SETFL\n");

        {

    LOGI(">>>> Port setup..\n");

    int err;

      tcflush(state->fd, TCIOFLUSH);//以下是配置串口的参数

    if ((err = tcgetattr(state->fd,&termios)) != 0)

    {

        LOGI("tcgetattr(%d) = %d,errno %d\r\n",state->fd,err,errno);

        close(state->fd);

    }

      termios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);

      termios.c_oflag &= ~OPOST;

      termios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);

      termios.c_cflag &= ~(CSIZE|PARENB);

      termios.c_cflag |= CS8;

      termios.c_cflag &= ~CRTSCTS;//no
flow control

     

    tcsetattr(state->fd, TCSANOW, &termios);

    tcflush(state->fd, TCIOFLUSH);

    tcsetattr(state->fd, TCSANOW, &termios);

    tcflush(state->fd, TCIOFLUSH);

    tcflush(state->fd, TCIOFLUSH);

    if (cfsetispeed(&termios,B9600))//具体的参考GPS文档

       {

        LOGE("cfsetispeed.. errno..\r\n");

              close(state->fd);

              //return(-1);

       }

       // Set the output baud rates in the
termios.

      if (cfsetospeed(&termios,B9600))

       {

        LOGE("cfsetispeed.. errno..\r\n");

              close(state->fd);

              //return(-1);

       }

    tcsetattr(state->fd,TCSANOW,&termios);

    LOGE("Port setup finished..\n");

}

if (state->fd < 0) {

        LOGD("no gps emulation detected");

        return;

    }

    if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {

        LOGE("could not create thread control socket pair: %s", strerror(errno));

        goto Fail;

    }

 //创建 gps服务线程

state->thread=state->callbacks.create_thread_cb("gps_state_thread",gps_state_thread,state);

}

 

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);

         /*********add by jhuang for call back NMEA***************/

         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

   如有什么不对的地方还请指点,我好及时纠正
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: