最近刚好有机会移植一款GPS到我们的产品上,就GPS模块移植本身而言,是很简单的。做过WINCE 或PC开发GPS的朋友肯定很清楚了,无非就是把GPS的标准数据从串口读出来,然后解析,应用程序获取其中经纬度,定位时间等信息,根据自己的需求或转化成地图上具体地点,或做其它使用。
先来说说一般的硬GPS,其优点不言而喻,相对“基站定位”,其精度要高很多,特别是在基站信号差的地方。缺点是往往第1次冷启动的时间慢的蛋疼!,还有天线的问题,我记得很明显,在室外我测试GPS,下午的时候信号特别好(估计卫星刚好在我头上),晚上就特别差,真郁闷- -!以前我们同事也做过GPS,但是效果都不好。射频部分的电路处理也非常不专业。这也是一般硬GPS的我认为最大的缺点了。
现有出现了一种AGPS的方案,说实话,我还没实现这个功能。android2.3GPS接口部分,可以参见gps.h。AGPS的作用一是能有效解决第1次冷启动慢的问题(一般通过网络预先下载星率表)。2是在室内等穿透力很弱的地方,AGPS也能过利用基站辅助定位。
好了,本文介绍的GPS的移植过程,AGPS我还等待大家帮忙了。
我的使用环境是android2.3,GPS与2.2类似,但也有比较大的区别,特别是JNI和SO硬件适配层。这里我记录下,在写动态库的时候遇到的两个主要问题。
1,JNI层调用我们的动态库,这里我们可以参见代码自己的模拟器gps_qemu.c文件
主要注意的是:
com_android_server_location_GpsLocationProvider.cpp中,当我们通过在开机启动gps服务的时候,我们最开始是需要通过JNI调用GPS交互口的
static const GpsInterface* get_gps_interface() { int err; hw_module_t* module; const GpsInterface* interface = NULL; err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);//frankBIBI获取模块 if (err == 0) {//frankBIBI hw_device_t* device; err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);//获取设备 if (err == 0) {///frankBIBI //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, gps_device_t* gps_device = (gps_device_t *)device; interface = gps_device->get_gps_interface(gps_device);//--frankBIBI 获取交互接口
// interface = gps_get_interface(); } } return interface; }
对应的我们的libgps.so需要这么配合使用才能正确调用
//frankBIBI
static int open_XXgps(const struct hw_module_t* module, char const* name, struct hw_device_t** device) { struct gps_device_t *dev = malloc(sizeof(struct gps_device_t)); memset(dev, 0, sizeof(*dev)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (struct hw_module_t*)module; dev->common.close = (int (*)(struct hw_device_t*))close_lights; dev->get_gps_interface = gps_get_hardware_interface;//交互
*device = (struct hw_device_t*)dev; return 0; } static struct hw_module_methods_t gps_module_methods = { .open = open_XXgps }; const struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = GPS_HARDWARE_MODULE_ID, .name = "XX GPS Module", .author = "The Android Open Source Project", .methods = &gps_module_methods, }; //.......................................................................
重点是这个dev->get_gps_interface = gps_get_hardware_interface;//交互
这样应用就明白了吧,很类似流接口吧
static const GpsInterface XXGpsInterface = { XX_gps_init, XX_gps_start, XX_gps_stop, XX_gps_set_fix_frequency, XX_gps_cleanup, XX_gps_inject_time, XX_gps_delete_aiding_data, XX_gps_set_position_mode, XX_gps_get_extension, }; const GpsInterface* gps_get_hardware_interface() { return &XXGpsInterface; }
这样交互就OK了。接下来,主要就是初始化,GPS启动,数据采集,数据解析,数据返回到JNI了。
2,这里讲一下,遇到的第2个问题。就是初始化完后,数据返回到JNI层,出现了libgps.so崩溃的问题。我相信,第1次在android2.3下面调试gps的同学很多会遇到。
这是为什么呢?
具体错误我是再这个函数中遇到的
static void nmea_reader_set_callback( NmeaReader* r, gps_location_callback cb ) { r->callback = cb; if (cb != NULL && r->fix.flags != 0) { D("%s: sending latest fix to new callback", __FUNCTION__); r->callback( &r->fix ); r->fix.flags = 0; } }
调用它的函数是gps_state_thread线程函数,作用是检测通道是否有控制命令或数据,然后做相应的控制。
else if (cmd == CMD_START) { if (!started) { D("gps thread starting location_cb=%p", state->callbacks.location_cb); started = 1; nmea_reader_set_callback( reader, state->callbacks.location_cb );//frankBIBI state->init = STATE_START;
还有有点模糊么,好吧,我们把回调函数的接口放出(GPS.H)
/** GPS callback structure. */ typedef struct { /** set to sizeof(GpsCallbacks) */ size_t size; gps_location_callback location_cb; gps_status_callback status_cb; gps_sv_status_callback sv_status_cb; gps_nmea_callback nmea_cb; 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;
好了r->callback( &r->fix );往JNI返回数据了。这里就出现了LIBGPS.SO崩溃了...崩溃的画面类似我前一篇文章“3G开关”那个画面类似。
为什么呢?
GPS.h中给我们提示
/** Callback with location information. * Can only be called from a thread created by create_thread_cb. */ typedef void (* gps_location_callback)(GpsLocation* location); /** Callback with status information. * Can only be called from a thread created by create_thread_cb. */ typedef void (* gps_status_callback)(GpsStatus* status); /** Callback with SV status information. * Can only be called from a thread created by create_thread_cb. */ typedef void (* gps_sv_status_callback)(GpsSvStatus* sv_info);
在来看我们是怎么创建我们的gps_state_thread,在gps_state_init函数中创建线程。
pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0
很明显,其无法与JNI层的反馈函数想联系。OK ,我们就利用JNI中注册的create_thread_cb来创建我们要的线程!
//frankBIBI
if (state->callbacks.create_thread_cb( &state->thread, gps_state_thread, state )==0) { LOGE("could not create gps thread: %s", strerror(errno)); goto Fail;
//相应的初始化的地方要调整s->callbacks = *callbacks要提前
XX_gps_init(GpsCallbacks* callbacks)//
{ GpsState* s = _gps_state; //frankBIBI s->callbacks = *callbacks
if (!s->init)//
gps_state_init(s); if (s->fd < 0) return -1; // s->callbacks = *callbacks
return 0; }
关于GpsCallbacks* callbacks......callback 解释如下:
当我们在做GPS初始化的时候,com_android_server_location_GpsLocationProvider.cpp中的static const GpsInterface* GetGpsInterface(JNIEnv* env, jobject obj)函数中“ if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) ”将sGpsCallbacks这个反馈函数的结构体传入到我们的LIBGPS.SO初始化注册!!
GpsCallbacks sGpsCallbacks = { sizeof(GpsCallbacks), location_callback, status_callback, sv_status_callback, nmea_callback, set_capabilities_callback, acquire_wakelock_callback, release_wakelock_callback, create_thread_callback, };
好了,libgps.so主要遇到就这两个问题。这样你就可以通过
GpsCallbacks sGpsCallbacks = { sizeof(GpsCallbacks), location_callback, status_callback, sv_status_callback, nmea_callback, set_capabilities_callback, acquire_wakelock_callback, release_wakelock_callback, create_thread_callback, };
利用这些回调函数,数据和信息都可以通过这些返回给JNI,来填充GPS接口数据。其它部分参照源码自带的模拟器就可以完成。
我们的硬件适配层就先到这里了。
--------------------------希望有错误,请指点,谢谢.