主要: 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<CR><LF>
例如:$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);
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
如有什麼不對的地方還請指點,我好及時糾正