转载自http://blog.chinaunix.net/uid-26215986-id-3743534.html 这篇文章基本分析了一下普通gps 的HAL的编写方法,以及电源控制情况,根据这篇文章就可以很快改出自己的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
例如:$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也将传送)。
首先看几个重要的结构体
点击(此处)折叠或打开
以一个定位的应用来说明函数执行流程:一般情况下GPS默认是关闭的,所以要先
点击(此处)折叠或打开
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
如有什么不对的地方还请指点,我好及时纠正