GPS 简介
GPS 属于全球卫星导航系统(global navigation satellite system)的一种。
有源与无源定位,GPS 为无源定位,接收机不需要发出任何信号。
卫星信号里包含有卫星的精确位置以及卫星发射信号的精确时间信息,接收机最少要收到四个卫星的信号,相当于从四个已知精确位置的点收到信号;而且可以通过接收机时钟得到时间差,从而知道四个信号从卫星到接收机的不准确距离(含同一个误差值,由接收机时钟误差造成),用这四个不准确距离和四个卫星的准确位置构建四个方程,解方程组就得到接收机位置。
这里有个问题,为什么 Android GPS API 返回的卫星数据,是天顶角(Alt) 和方位角(Az),这是地平坐标系。这些位置信息如果是卫星返回的,不是应该返回绝对位置吗?比如说卫星在天球中的赤经和赤纬吗?如果说经程序转换,那么地平坐标系是基于观察者的坐标系,这时观察者的数据应该是未知。
精度
C/A-code receivers ~ 5 -10 m. P/Y-code receivers ~ 2 -9 m
DGPS(差分 GPS)
C/A-code DGPS receivers ~0.7 -3 m. P/Y-code DGPS receivers ~0.5 -2.0 m.
卫星的几何分布也会影响到定位精度。
大地测量系统(Geodetic Datum)
大地测量系统是指用于描述地球上一个地点位置的坐标系统,有多套标准,国内面对的一般有如下三种
- WGS-84
GPS全球定位系统使用而建立的坐标系统。在Android系统内取得的海拔高度预设是参照WGS84而不是当地平均海平面,使得用户在海边定位是可能发现自己的测量值位于海平面以下
- GCJ-02
又名火星坐标。
A marker with GCJ-02 coordinates will be displayed at the correct location on a GCJ-02 map. However, the offsets can result in a 100 - 700 meter error from the actual location if a WGS-84 marker (such as a GPS location) is placed on a GCJ-02 map, or vice versa.
谷歌地图上的路网图用的是 GCJ-02,卫星地图(同谷歌地球)用的 是 WGS-84,所以显示出来会有 100-700m 的偏移。
- BD-09
百度标准的经纬度坐标,基于 GCJ-02。
坐标转换
WGS-84 和 GCJ-02 的转换可参考:中国地图坐标(GCJ-02)偏移算法破解小史 – blog.genglinxiao.com
GPSspg,提供各个地图的经纬度查询。以北纬 N39°54′26.54″ 东经 E116°23′28.84″ 为例:
- 谷歌地图:39.9087677478,116.3975780499 (GCJ-02)
- 百度地图:39.9151190000,116.4039630000 (BD-09)
- 腾讯高德:39.9087311069,116.3975323161 (GCJ-02)
- 图吧地图:39.9081728469,116.3867845961
- 谷歌地球:39.9073728469,116.3913445961 (WGS-84)
具体算法可参考:
- Offline Navigation for Windows Phone 7 - Source Code
- android-demo/InternalGpsConverter.java at master · douo/android-demo
GPS 轨迹文件
GPS 轨迹可以理解为是一个基于时间的 GPS 坐标点集合。常用的有两种:
- GPX
GPX(GPS Exchange Format) 基于 XML,专门用来储存地理资讯。免费、开放、常用。规范见:GPX 1.1 Schema Documentation
- KML
KML 也是基于 XML,由谷歌旗下的 Keyhole 维护,常用于谷歌系的软件。
Android Location 框架
Location Callback
PendingIntent 可以通过这样的方式获取更新的 Location 对象
Location location = getIntent().getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED);
Gps 卫星
GpsSatellite 用的是地平坐标系 :
高度角(Elevation/Altitude),有时就称为高度或仰角,是天体和观测者所在地的地平线的夹角。 方位角(Azimuth),是沿着地平线测量的角度(由正北方为起点向东方测量)。
AGPS
GPS 接收器可能最多需要 12.5 分钟(从 GPS 卫星下载历书和星历的时间)来计算出当前位置。
辅助有两种类型:
-
基地台基本定位功能(Mobile Station Based)
通过网络服务器获取卫星的轨道和历书数据,避免从 GPS 卫星直接下载数据从而加快锁定卫星。 通过网络获取精准的时间(NTP)。
-
基地台协助(Mobile Station Assisted)
设备上传本身的 GPS 数据,由服务器来技术位置。
Android 可以通过 LocationManager#sendExtraCommand
操作 AGPS。以 5.1 为例,总共支持 3 条命令。
delete_aiding_data
,清除辅助定位数据,重置 GPS 状态到冷启动force_time_injection
,命令从一个配置服务器中下载A-GPS 数据,这些数据将被GPS位置提供者使用force_xtra_injection
,命令从配置的NTP 服务器检索当前时间并进行更新,用来进行GPS 计算。
delete_aiding_data
命令用于删除先前已下载的A-GPS 数据。这是唯一使用Bundle 参数的附加命令,Bundle 用于控制要删除的A-GPS 数据。Bundle 可以包含布尔型的键值对来指明要移除的数据。
具体代码见:GpsLocationProvider 或 grepcode(Android 5.1)。
- cold(冷启动):没有之前的位置信息,没有星历,没有时间的估算。这种情况发生在初次使用定位时或电池没电导致星历丢失时。关机状态下将接收机移动200公里以上距离。
- warm(温启动):有历书信息,大致的位置和时间可知,没有星历信息。这种情况发生在本次定位距离上次超过两个小时时。
- hot(热启动):有星历信息,大致的时间和位置可知,通常比温启动的时间和位置信息精确。这种情况发生在本次定位与上次定位的时间间隔小于两个小时时。
星历(Ephemeris)与历书(Almanac):
为了缩短卫星锁定时间,GPS接收机需利用历书、当地位置的时间来预报卫星运行状态。
历书与星历都是表示卫星运行的参数。历书包括全部卫星的大概位置,用于卫星预报;星历只是当前接收机观测到的卫星的精确位置,用于定位。
NTP 服务器和 Xtra 服务器的配置文件保存在 /etc/gps.conf
。
参考:
FYI:
- WirelessMoves: How SUPL Reveals My Identity And Location To Google When I Use GPS
- How Does Android Use SUPL Server? - Google 網上論壇
NMEA
NMEA 是美国国家海洋电子协会(National Marine Electronics Association)的缩写。这里的 NMEA 是指一套规格用于定义各种海洋电子设备之间通讯的接口。这套标准的规格文件是收费的,可到官网购买。不过网上也能找到这套规格的描述,比如 deal 的文章还有埃里克·雷蒙(Eric S. Raymond)的披露。埃里克·雷蒙现在是 gpsd 的维护者。
NMEA 的基本单元是一种称为句子(sentence)的数据行,句子有 ASCII 字符组成,由 $
符号开始,由换行符(carriage return/line)结束,句子最多有 80 个可见字符(+ 一个不可见的换行符)。句子有两种类型:
-
标准句子
标准句子由两个字符作为前缀,表示设备类型, 比如 GPS 接收器的前缀就是
GP
,接着的三个字符表示句子的内容,由五个字符表示句子的类型。 -
专有(proprietary)句子
专有句子是由独立公司自己定义的句子,以
P
作为前缀,后面跟着三个字符用来表示厂商,比如PGRM
是佳明(Garmin)的句子。
句子内的数据项用,
分隔。*
带上两位十六进制字符表示校验码。
$GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75
Where:
GSV Satellites in view
2 Number of sentences for full data
1 sentence 1 of 2
08 Number of satellites in view
01 Satellite PRN number
40 Elevation, degrees
083 Azimuth, degrees
46 SNR - higher is better
for up to 4 satellites per sentence
*75 the checksum data, always begins with *
大部分 GPS 接收器支持的标准是 NMEA 0183 Version 2。
用下面代码在真机上进行测试
mLocationManager.addNmeaListener(new OnNmeaMessageListener() {
long firstTimestamp = -1;
int count;
@Override
public void onNmeaMessage(String message, long timestamp) {
if (firstTimestamp == -1) {
firstTimestamp = timestamp;
}
count++;
Log.d(TAG, message);
if (timestamp != firstTimestamp) {
Log.d(TAG, "rate:" + (count * 1000 / (timestamp - firstTimestamp)) + "sen/s");
}
}
});
- Nexus 6p,NMEA 的接收频率大概是 10 句每秒,接收的设备类型前缀有
GP
(GPS)和GL
(GLONASS) - P9 plus 上是 18 句每秒,前缀比较多:
QZ
(QZSS)、GN
(GLONASS)、IM
、BD
(北斗)、GP
(GPS)