最近刚好有机会移植一款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);
- if (err == 0) {
- hw_device_t* device;
- err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
- if (err == 0) {
-
- gps_device_t* gps_device = (gps_device_t *)device;
- interface = gps_device->get_gps_interface(gps_device);
对应的我们的libgps.so需要这么配合使用才能正确调用
- 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;
- <strong>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__);
- <strong>r->callback( &r->fix );</strong>
- 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;
-
- <u><strong>nmea_reader_set_callback( reader, state->callbacks.location_cb );
-
- state->init = STATE_START;
还有有点模糊么,好吧,我们把回调函数的接口放出(GPS.H)
-
- typedef struct {
-
- size_t size;
- <em><strong><u> gps_location_callback location_cb;</u></strong></em>
- 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中给我们提示
-
-
-
- typedef void (* gps_location_callback)(GpsLocation* location);
-
-
-
-
- typedef void (* gps_status_callback)(GpsStatus* status);
-
-
-
-
- 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来创建我们要的线程!
- if (state->callbacks.create_thread_cb( &state->thread, gps_state_thread, state )==0) {
- LOGE("could not create gps thread: %s", strerror(errno));
- goto Fail;
- XX_gps_init(GpsCallbacks* <strong>callbacks</strong>)
- {
- GpsState* s = _gps_state;
-
- s->callbacks = *callbacks
- gps_state_init(s);
- if (s->fd < 0)
- return -1;
-
关于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接口数据。其它部分参照源码自带的模拟器就可以完成。