最近刚好有机会移植一款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接口数据。其它部分参照源码自带的模拟器就可以完成。
我们的硬件适配层就先到这里了。
--------------------------希望有错误,请指点,谢谢.