为什么80%的码农都做不了架构师?>>>
首先感谢各位兄弟姐妹们的耐心等待。根据今天得到的消息,本书预计在下周各大网店提供预订。注意,此处连载的是未经出版社编辑的原始稿件,所以样子会有些非专业。
注意,如下是本章目录,本文节选9.1~9.2.2 9.3.3.4~最后
为了方便读者深入学习,本系列连载都会将作者研究过
程中所学习的参考文献列出来
第九章 深入理解GPS
本章主要内容
- 介绍GPS基础知识;
-
介绍Android中GPS模块以及LocationManagerService。
9.1 概述
GPS,全称是Global Positioning System,中文译为全球定位系统。GPS源自美国军方的一个项目,其主要目的是为陆海空三大领域提供实时、全天候和全球性的导航服务。和GPS相对应的还有一个词,叫GNSS,它是Global Navigation Satellite System(全球导航卫星系统)的缩写。GPS是GNSS的一种具体实现形式。目前,世界上的GNSS除了美国的GPS外,还有欧盟的GALILEO、俄罗斯的GLONASS以及中国的北斗导航系统。
近几年来,随着iPhone和Android等新一代移动智能平台的普及,支持GPS及其它GNSS系统几乎是当下所有智能手机的标准功能,而在GPS或其他能提供位置信息的服务之上,人们更是构建了一个市场规模达数十亿美金的所谓的基于位置的服务(Location Based Service,简称LBS[1])。
随着位置信息获取技术的多样化,Android平台在这些技术之上抽象出了一套名为Location Manager(位置管理)的软件架构。当然,作为该框架中最重要的位置提供服务模块,GPS功能由Android系统直接提供。
和本书其他章节类似,本章也会从两个方面来介绍Android平台中LM相关的功能:
- 首先将介绍和GPS相关的一些基础知识。从原理上看,GPS和GLONASS或北斗等其它卫星导航系统类似,所以本章将仅围绕GPS开展相关的知识讲解。而读者在掌握GPS知识的基础上,能轻松将它们运用到其他GNSS系统中。
- 在了解GPS相关原理的基础上,本章将介绍Android平台中位置管理的软件架构及代码实现。
马上来看GPS基础知识介绍。
提示:作为《深入理解Android》系列丛书的惯例,笔者在每本书的最后一章都会留下一些内容请读者自行学习和研究。在此,笔者希望读者在本章基础上深入钻研GPS相关知识并能和其他读者分享自己的成果。
9.2 GPS基础知识介绍
与GPS相关的知识非常多,市面上也有很多专业的书籍来教授它们。不过,对于本书的读者来说,笔者将挑选并介绍一些比较实用的内容。笔者将这些知识归纳为如下三个部分:
- 卫星导航基本原理:这一节主要介绍卫星导航的一些基础知识。
- GPS工作原理:这一节集中介绍GPS的工作原理和相关的数据格式。
- OMA-SUPL协议:这一节集中介绍OMA-SUPL方面的知识。
注意:如何在大量专业的书籍中选择合适的知识点来向读者介绍是本书编写过程中一项非常重要及困难的工作。以GPS为例,其专业书籍涉及到较多的数学计算和公式推导。显然,这些内容对于当今业已成熟并高度集成化的GPS模块来说太过基础。就笔者总结的工作经验来说,对于一门陌生的技术和专业,初学者首先一定要掌握其基本原理和相关的概念。这些基本原理和概念将是这门技术或专业的主要框架和脉络。只有在掌握专业知识框架的基础上,才能开展更进一步的学习和研究。从这个角度出发,本章的基础知识将综合下文实际代码分析的需求,把主要内容集中在相关的GPS原理和概念上。
9.2.1 卫星导航基本原理
在这一节中,我们将介绍测距、参考坐标系、时间系统、卫星轨道等四个方面的基础知识。先来介绍测距原理。
1 测距原理介绍[2]
GPS(包括其他的GNSS系统)使用的测距原理非常简单。我们用一个图来说明它的工作过程,如图9-1所示。
图9-1 卫星测距原理示意
在图9-1所示的卫星测距原理示意图中:
- 卫星和地面接收器都各自有一个时钟。假设卫星和接收器的时钟能完美同步(注意这个假设,以后我们还会讲到它)。在0ms时刻,卫星向接收器发送了一串信号。
- 在67.3ms时,接收器收到了该信号。那么,卫星离接收器的距离就是信号传播速度乘以传播时间。
以数学公式来表示图9-1的卫星测距原理就是:
[公式一]
该公式中,c为光速,为信号传输时间。D为距离
有了公式一,我们可以计算接收器到任意一个卫星的距离。不过,距离(Range)和位置(Location)显然是两个不同的概念。那么,如何根据距离得到位置信息呢?
原来,位置需要放在某个坐标系中来考察。下一节将专门讨论坐标系。假设现在已经有一个坐标系了。那么,图9-2所示的内容就能回答刚才提出的问题。
图9-2 二维坐标系中接收器位置计算示意图
和图9-1的测距原理示意比起来,图9-2多了如下一些特点:
- 卫星和接收器的位置都置于一个统一的二维坐标系中来考察。
- 接收器离两个卫星的距离都由公式一计算得到,分别是D1和D2。
- 如果以卫星为圆心,以接收器到卫星的距离为半径,那么我们可以得到图9-2中的两个圆。这两个圆的相交点到卫星1的距离为D1,到卫星2的距离为D2。也就是说,这两个点就是接收器的可能位置。
- 如果接收器的Y坐标值不能高于卫星的Y坐标值,那么接收器的实际位置只能是图9-2中的(Xp,Yp)了。
掌握了二维坐标系中接收器的位置计算方法,只要再增加一颗卫星,我们就很容易推导出接收器在三维坐标系中的位置了。
从理想情况来说,定位(英文为Positioning)计算就这么简单,但现实情况却相当复杂。例如,在上述的讨论中还有两个重要的潜在问题没有解决,这两个问题是:
- 如何选择坐标系?
- 出于成本、便携性等各方面的考虑,接收器的时钟精度远不如卫星的时钟精度,所以在计算信号传输时间时会造成较大的偏差。由于信号传播速度是光速,所以哪怕这个时间偏差为0.1毫秒,距离偏差都会达到30公里。
那么这两个问题是如何解决的呢?下两节将分别介绍坐标系和时间系统。时间偏差的问题则通过引入第四颗GPS卫星参与定位计算来解决(详情见9.2.2中“定位计算相关知识”一节)。
2. 坐标系介绍
(1) ECI/ECEF/WGS-84介绍[2]
根据上一节的内容可知,坐标系对于位置计算非常重要。坐标系有很多个,甚至不同的国家都可能会建立更加符合本国实际情况的坐标系。但在GPS中,与它相关的坐标系主要有两个,它们分别是:
- 地心惯性坐标系(英文为Earth Centered Inertial,简称ECI):该坐标系用于描述GPS卫星的位置信息。在这种坐标系中,原点为地球的质心,卫星围绕质心运动,并遵守牛顿运动定律。
- 地心地球固连坐标系(英文为Earth Centered,Earth Fixed,简称ECEF):该坐标系用于描述地面接收器的位置信息。ECEF最大的特点是它会随着地球旋转而旋转。
提示:在GPS的定位计算过程中,我们需要先把卫星在ECI坐标系的位置转换成它在ECEF坐标系的位置。
图9-3展示了ECI和ECEF坐标系。
图9-3 ECI和ECEF坐标系示意图
图9-3中:
- 左图所示为ECI坐标系。该坐标系中,XY平面与地球赤道面重合。X轴指向天球(Celestial Sphere,一种假想的无限大的球,它和地球同心。所以ECI坐标系不受地球旋转的影响)的某个位置。Z轴与XY平面垂直并指向北极。ECI坐标系属于笛卡尔坐标系,故卫星的位置由(x,y,z)表示。
- 右图所示为ECEF坐标系。该坐标系的原点为地球中心(这就是Earth Centered一词的缘由)。XY平面也与地球赤道面重合。不过其X轴指向0经度方向,Y轴指向东经90度的方向。所以ECEF坐标系实际上是随着地球一起旋转的。ECEF坐标系也属于笛卡尔坐标系,故接收器的位置也由(x,y,z)表示。
ECEF是一个笛卡尔坐标系,而我们实际使用的位置信息却是由经纬度来表示的,那么如何将笛卡尔坐标系中的X,Y,Z值转换成经纬度呢?
该转换工作涉及到另外一个重要的概念,即标准地球模型。GPS参考的地球模型名为WGS-84(英文名为World Geodetic System 1984,由美国国防部建立)。WGS-84模型如图9-4所示:
图9-4 WGS-84模型介绍
图9-4所示的标准大地模型中:
- 地球被看做是一个椭球体。该椭球体的半长轴(英文为Semi Major Axis,实际长度为6378137.00米)为a,半短轴为b(英文为Semi Minor Axis,实际长度为6356752.31米)。根据a和b的值,该椭球体的偏心率[2](英文名为Eccentricity)可由公式 计算得到。
- 图中的Equatorial Plane为赤道面。赤道面和椭球体相交得到的椭圆为赤道(图中的Equator),它就是纬度为0的地方。图中的Greenwhich Meridian为格林尼治子午线,即经度为0的地方。椭球体的表面叫椭球面,即图中的Ellipsoid。
- 图中的P1点的位置采用了笛卡尔坐标系,其值为(x,y,z),而P点的位置则由椭球坐标系确定,其值为( )。注意,此处的h是P点与椭球面的高度,即GPS概念中的高度。
根据相关的公式[2],椭球坐标系和笛卡尔坐标系能相互转化。
(2) 高度计算
根据上节最后关于椭球坐标系中h坐标值的解释,GPS中的高度是指它和椭球面(Ellipsoid)的距离。但值得特别注意的是,这个高度和日常生活中所说的海拔高度不是同一个概念。日常生活中所说的海拔高度不是基于Ellipsoid,而是基于大地水准面(英文名为Geoid)的。那么,大地水准面是什么呢?
- 大地水准面是一个重力等位面。简单点说,静止海水在大地水准面上不会因为重力原因而流动。大地水准面和地球的质量分布等有重要关系。相比椭球面而言,大地水准面的数学模型非常复杂,很难用数学公式来描述它。
大地水准面和椭球面之间的区别影响了我们对高度的计算。图9-5所示为GPS高度与海拔高度的区别[3]:
图9-5 高度计算的区别
图9-5中:
- 地球真实的表面由大海和高山组成,这个表面叫地形(图中的Topography)。
- GPS测量的高度为h(也叫大地高,英文为Ellipsoidal Height),而日常所说的海拔高度为H(也叫正高,英文为Orthometric Height)。h和H之间的差为N(也叫大地水准面高,英文为Geoid Height)。
注意:对于高精度的测绘需求,我们往往需要把h值转换成H,不过一般情况下二者的差别不大。
了解了GPS的坐标系统,马上来看与GPS相关的另外一个非常重要的系统。
3. 时间系统[3]
和GPS相关的时间系统有四种之多,它们分别是国际原子时(英文为International Atomic Time,简写为IAT,注意,其对应的法语名为Temps Atomique International,所以其常用缩写也为TAI。笔者此处采用英文缩写IAT)、协调世界时间(Coordinated Universal Time,简写为UTC)、GPS时间(英文为GPS Time,简写为GPST)和本地时间(英文为Local Time)。笔者在此总结这四种时间系统的特点如下:
- IAT:1967年,人们利用铯原子振荡周期极为规律的特性研制出了高精度的原子钟,并将铯原子能级跃迁辐射9192631770周所经历的时间定为1秒。IAT起始时间从1958年1月1日0时0分0秒开始,其精度能达到每日数纳秒。细心的读者可能会问到,在原子钟出现之前,人们如何定义秒呢?原来,在原子钟出现之前,人们使用基于地球自转的天文测量得到的世界时(Universal Time,简称UT)作为时间计量单位。和原子时比起来,UT会由于地球自转的不稳定(由地球物质分布不均匀和其它星球的摄动力等引起的)而带来时间上的差异,该差异大概在3年内会增加到1秒左右。
- UTC(也叫世界统一时间、世界标准时间):TAI的精度为每日数纳秒,而UT的精度为每日数毫秒。对于这种情况,一种称为协调世界时的折衷时标于1972年面世。UTC以原子秒长为基础,在时刻上尽量接近UT。UT和UTC之间的间隔不能超过0.9秒,所以在有需要的情况下会在UTC内加上正或负闰秒(Leap second)。因此,协调世界时与国际原子时之间会出现若干整数秒的差别,而位于巴黎的国际地球自转事务中央局将决定何时加入闰秒以减少UTC和IAT之间的差别。UTC时间系统用途很广。目前几乎所有国家发播的时号都以UTC为基准。另外,互联网使用的网络时间协议(Network Time Protocol,简称NTP)获取的时间就是UTC。UTC的时间格式为:年(y)月(m)日(d)时(h)分(min)秒(s)。
- GPST:GPST也使用IAT中的原子秒为单位,其时间原点定于1980年1月6日UTC 0时。GPST比IAT慢19秒,而它和UTC时间的差异为整数秒,并且这个差值会随着时间的增加而积累(到2009年,GPST和UTC相差15秒)。GPST时间格式由从GPST原点开始的周数和周内秒数组成。例如2009年7月9号13点08分36秒(转成时分秒格式的GPST)用GPST表示就是第1539周392916秒。参考资料[5]介绍了GPST和UTC的转换方法。
- 本地时间[6]:本地时间基于UTC。它将全球分为24个时区,每一时区之中心为相隔15度经线,每一国家都处於一个或以上的时区内。第一时区的中心位於格林尼治子午线(或简称子午线)。该时区以西的地方慢一个小时或以上,而东面则较其快。本地时间表达方法遵循ISO 8601,其格式为“年月日T时分秒Z(或者时区标识)”。例如,20131030T093000Z,表示2013年10月30号09点30分0秒,Z表示是标准时间。如果表示北京时间,那么就是20131030T093000+08,其中“+08”表示东八区。
提示:以上是本书和时间系统相关的知识。这部分内容原本非常复杂,还涉及到较多天文方面的概念。在此,笔者建议读者先掌握本节所述内容。
4. 卫星轨道等知识介绍
本节将介绍卫星轨道等方面的知识。首先是卫星运行所遵循的开普勒三定律。
(1) 开普勒三定律介绍
卫星围绕地球运行时将遵循开普勒三定律。图9-6所示为开普勒第一和第二定律的示意图:
图9-6 开普勒第一和第二定律示意
图9-6中:
- 左图所示为开普勒第一定律示意。图中的Perigee为近地点,Apogee为远地点。根据开普勒第一定律[4],卫星将围绕地球做椭圆运动,地球为该椭圆两个焦点中的一个。
- 右图所示为开普勒第二定律示意。根据开普勒第二定律,在相同的时间内,卫星运行时所扫过的区域的面积相同。即如果图中的时间段Tv_1等于时间段Tv_2的话,面积A_1等于面积A_2。
而根据开普勒第三定律可知,围绕地球椭圆轨道运行的卫星,其椭圆轨道半长轴的立方与运行周期的平方之比为常量。第三定律可用公式表达,如下所示:
[公式二]
//开普勒第三定律,a为半长轴,T为卫星运行周期。
k为常量,取值为。其中,M为地球的质量,G为万有引力常数
开普勒三定律主要用来计算卫星运行位置等相关参数,例如第三定律常用来计算卫星的轨道高度。这部分内容请读者参考[7]。
(2) 卫星轨道及星历等知识介绍
卫星轨道虽然涉及到很多空间科学方面的知识,但对于本书的读者来说,我们只需掌握卫星运行轨道的几个重要参数和概念即可。图9-7展示了卫星运行轨道及相关参数。
图9-7 卫星运行轨道示意图
图9-7中:
- 左图中,Equatorial Plane为赤道平面,卫星轨道本身是一个椭圆轨道,它和赤道平面有一个夹角。这个夹角叫轨道倾角(图中的Inclination)。
- 右图中,假设观察者站在坐标原点观察左上角的卫星,则h代表仰角(Elevation angle),z代表天顶角(Zenith angle),而正北方向离卫星投影点的顺时针角度A为方位角(Azimuth angle)。
提示:上述参数是卫星运行轨道中几个非常重要的参数,不过,读者现在只需要记住它们的定义即可。
根据轨道倾角、运行周期等参数,人们将卫星轨道分为如图9-8所示的几大类:
图9-8 卫星轨道分类
- 地球同步轨道(Geosynchronous Earth Orbit,简写为GEO):GEO特点是其轨道高度距离地面大约35786公里,卫星运行周期等于地球自转周期(23小时56分4秒),卫星运行方向和地球自转方向一致。最后,轨道是圆形(即偏心率为0)。根据轨道倾角的不同,地球同步轨道还可细分为静止同步轨道、倾斜同步轨道和极地同步轨道。这三者的特点如下文所述:
1. 静止同步轨道:如果轨道面与地球赤道面重合(即轨道倾角为0),则这种轨道叫静止同步轨道(Geostationary Satellite Orbit,简称GSO)。该轨道的特点是:从地面观察者看到该轨道上的卫星始终位于某一位置,似乎保持静止不动。利用该轨道上的3颗卫星就可以实现除南北极很小一部分地区外的全球通信
2. 倾斜同步轨道(Inclined Geostationary Orbit,简称IGSO):如果轨道倾角大于零并小于90度,则这种轨道叫倾斜同步轨道。
3. 极地同步轨道(Polar Earth Orbit,简称PEO):如果轨道倾角等于90度,则称为极地同步轨道。运行在这种轨道上的卫星能到达南北极区上空,所以那些需要在全球范围内进行观测和应用的气象卫星等多采用这种轨道。
- 中地球轨道(Medium Earth Orbit,简称MEO):也叫中圆轨道,它距离地面10000公里,卫星运转周期在2至12小时之间。运行在该轨道上的卫星大部分是导航卫星,例如GPS导航卫星有一部分运行在该轨道上。
- 低地球轨道(Low Earth Orbit,检查LEO):也叫近地轨道或低地轨道,距离地面大约1000公里。由于近地轨道离地面较近,绝大多数对地观测卫星、测地卫星、空间站都采用近地轨道。
- 高椭圆轨道:高椭圆轨道是一种具有较低近地点和极高远地点的椭圆轨道,其远地点高度大于静止卫星的高度(36000千米)。根据开普勒定律,卫星在远地点附近区域的运行速度较慢,因此这种极度拉长的轨道的特点是卫星到达和离开远地点的过程很长,而经过近地点的过程极短。这使得卫星对远地点下方的地面区域的覆盖时间可以超过12小时。具有大倾斜角度的高椭圆轨道卫星可以覆盖地球的极地地区,所以对于像俄罗斯这样的高纬度国家而言,高椭圆轨道比同步轨道更有实际作用。
以上是卫星运行轨道的几个重要参数[5],除此之外,还有两个重要概念需要读者了解:
- 星历表(英文为Ephemeris):星历表本来是用来记录天体特定时刻的位置的。而在GNSS中,星历表则记录了卫星的一些运行参数,它使得我们通过星历表就可以计算出任意时刻的导航卫星的位置和速度。下文我们将见到在GPS中,星历表包含了非常详细的卫星轨道和位置信息,所以其数据量较大,传输时间较长。为了克服这个问题,人们设计了星历表的简化集,即历书。
- 历书(英文为Almanac):历书也包含了卫星的位置等相关信息,不过它是星历数据的简化集,其精度较低。所以,历书数据量较小,传输时间较短。
提示:星历和历书对于GPS定位计算来说至关重要。本章后文将介绍二者所包含的参数信息。
到此,本书所涉及到的与卫星导航原理相关的知识介绍就告一段落,这些内容对于讲解本章知识点来说已经足够。但本节所述内容仅仅是卫星导航全部知识的一小部分,那些有志从事卫星导航工作的读者还需要进一步花费时间来学习相关的专业知识。
[1]根据参考资料[1],LBS的催生源于几起悲剧事件,这其中就包括2001年美国的911事件。
[2]偏心率也叫扁率,对应的英文名为flattening。
[3]时间系统相关的知识非常复杂,参考资料[4]介绍得最为简练。
[4]开普勒三大定律本来描述的是行星在宇宙空间绕太阳公转所遵循的定律。不过导航卫星围绕地球运行也遵循此定律。所以笔者直接以导航卫星和地球为对象来介绍开普勒三大定律。
[5]卫星运行轨道的分类总结由笔者提炼并整理从网上搜索到的相关内容而来。
===========略略略
4 GPS JNI与HAL层介绍
在GpsLocationProvider初始化一节我们曾提到说GpsLocationProvider类有一个静态的初始化代码,在那段代码中,class_init_native函数将初始化JNI层相关模块。马上来看它。
(1) JNI与HAL层初始化
class_init_native函数对应的JNI函数如下所示:
[-->com_android_server_location_GpsLocationProvider.cpp::android_location_GpsLocationProvider_class_init_native]
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
int err;
hw_module_t* module;
//获取JNI回调函数集对应的Java MethodId。如果读者不熟悉JNI,请阅读《深入理解Android:卷1》
//第2章“深入理解JNI”
method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
.......//JNI回调函数集的其他函数,下文会详细介绍它们
method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
.......
//加载GPS HAL层模块,GPS_HARDWARE_MODULE_ID的值为“gps”
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
//打开GPS HAL层模块
err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
if (err == 0) {
gps_device_t* gps_device = (gps_device_t *)device;
//获取GPS HAL层最主要的交互接口GpsInterface,它是JNI层和HAL层的重要交互
//通道
sGpsInterface = gps_device->get_gps_interface(gps_device);
}
}
//GPS HAL模块对外还提供了几个重要交互接口。
if (sGpsInterface) {
sGpsXtraInterface = //用来和GPS HAL层中xtra模块交互的接口
(const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
sAGpsInterface = //用来和GPS HAL层AGPS模块交互的接口
(const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
sGpsNiInterface = //用来和GPS HAL层NI(Network Initiated)模块交互的接口
(const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
sGpsDebugInterface = //用来调试的接口
(const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
sAGpsRilInterface = //用来和GPS HAL层AGPS及RIL相关的接口
(const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
}
}
当GpsLP启用后,native_init函数将被调用,它对应的JNI函数代码如下所示:
[-->com_android_server_location_GpsLocationProvider.cpp::android_location_GpsLocationProvider_init]
static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
{
// this must be set before calling into the HAL library
if (!mCallbacksObj)
mCallbacksObj = env->NewGlobalRef(obj);
//向GPS HAL层设置回调函数接口。当GPS HAL层有情况需要通知JNI层时,这些回调函数
//将被调用
if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
return false;
//设置其他接口的回调函数
if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
sGpsXtraInterface = NULL;
if (sAGpsInterface) sAGpsInterface->init(&sAGpsCallbacks);
if (sGpsNiInterface) sGpsNiInterface->init(&sGpsNiCallbacks);
if (sAGpsRilInterface) sAGpsRilInterface->init(&sAGpsRilCallbacks);
return true;
}
通过上述代码可知,Android平台中通过定义多个交互接口实现了GPS Java层、JNI层以及HAL层的交互问题。显然,理解这些接口的作用将非常有助于我们学习Android平台中GPS模块的实现。下面将先介绍Java层与JNI层的交互接口。
(2) Java层与JNI层交互接口函数介绍
先来看GPS Java层调用的JNI函数,它们的作用如下所示:
[-->GpsLocationProvider.java]
//初始化JNI层相关的类型
native void class_init_native();
//判断系统是否支持GPS
native boolean native_is_supported();
//初始化GPS HAL层模块
native boolean native_init();
//清理GPS HAL层模块所分配的资源
native void native_cleanup();
/*
设置GPS模块工作模式,其各个参数取值含义如下:
mode:GPS工作模式,目前可取值有GPS_POSITION_MODE_STANDALONE(值为0,仅GPS工作),
GPS_POSITION_MODE_MS_BASED(值为1,AGPS MSB模式),GPS_POSITION_MODE_MS_ASSISTED
(值为2,AGPS MSA模式)
recurrence:位置更新模式,目前可取值有GPS_POSITION_RECURRENCE_PERIODIC(值为0,连续
定位)、GPS_POSITION_RECURRENCE_SINGLE(值为1,单次定位)
min_interval:最短位置更新时间,单位为毫秒
preferred_accurary: 期望的位置更新精度,单位为米
preferred_time:期望的TTFF时间,单位为毫秒
*/
native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
int preferred_accuracy, int preferred_time);
//下面这个两个函数用于启动和关闭导航
native boolean native_start();
native boolean native_stop();
//删掉AGPS辅助数据,其flags参数的取值请读者阅读GpsLocationProvider.java的deleteAidingData函数
native void native_delete_aiding_data(int flags);
//读取卫星信息,该函数在“reportStatus和reportSvStatus介绍”一节中已介绍过了
native int native_read_sv_status(int[] svs, float[] snrs,
float[] elevations, float[] azimuths, int[] masks);
//读取NMEA数据
native int native_read_nmea(byte[] buffer, int bufferSize);
//输入位置信息,在GpsLP中,该位置信息由NetworkLP提供
native void native_inject_location(double latitude, double longitude, float accuracy);
/*
输入NTP时间,其中:
time:为NtpTimeTrustedTime从网络中获取到的NTP时间
timeReference:为设备从开机到一次NTP请求处理成功后的所耗费的时间,由SystemClock的elapsedRealtime
函数返回
uncertainty:准确度。在Android系统中,该值为NTP请求发送和接收往返时间的一般。详情可参考
SnetpClient.java文件
*/
native void native_inject_time(long time, long timeReference, int uncertainty);
//GPS模块是否支持XTRA数据
native boolean native_supports_xtra();
//输入XTRA数据,即LTO数据
native void native_inject_xtra_data(byte[] data, int length);
//用于调试,获取GPS模块内部状态
native String native_get_internal_state();
//打开AGPS数据下载通道,参数apn指明了所要使用的APN
native void native_agps_data_conn_open(String apn);
//关闭AGPS数据下载通道
native void native_agps_data_conn_closed();
//GpsLP处理AGPS相关事宜失败时候调用下面这个函数
native void native_agps_data_conn_failed();
//将来自数据短信或WAP推送短信得到的信息传递给GPS模块。
native void native_agps_ni_message(byte [] msg, int length);
//设置AGPS服务端地址,其中type取值有两种:
//AGPS_TYPE_SUPL:值为1,代表SUPL服务器
//AGPS_TYPE_C2K:值为2,代表C2K服务器
native void native_set_agps_server(int type, String hostname, int port);
/*
当GPS模块需要使用APGS时,会调用reportNiNotification函数(详情见下文)以通知用户。
用户处理完后,将通过下面这个函数告知GPS模块处理结果。注意,这部分内容涉及到OMA-SUPL相关知识,请读者
阅读参考资料[28]。
该函数的参数如下:
notificationId:通知id,代表GPS HAL层的某一个处理请求
userResponse有三种取值,分别是GPS_NI_RESPONSE_ACCEPT(值为0,代表用户允许相关操作)、
GPS_NI_RESPONSE_DENY(值为1,用户拒绝相关操作)、GPS_NI_RESPONSE_NORESP
(值为2,代表用户无回应)。NI和GpsNetInitiatedHandler有关,读者可自行研究它
*/
native void native_send_ni_response(int notificationId, int userResponse);
/*
设置AGPS参考位置信息,其各参数解释如下:
type:取值可为AGPS_REF_LOCATION_TYPE_GSM_CELLID(值为1,代表GMS网络的cell id)、
AGPS_REF_LOCATION_TYPE_UMTS_CELLID(值为2,代表CDMA网络的cell id)、
AGPS_REG_LOCATION_TYPE_MAC(值为3,代表MAC地址)
mcc:Mobile Country Code(移动国家码),由3位数字组成,唯一地识别移动用户所属的国家,中国为460
mnc:Mobile Network Code(移动网络码),用于识别移动用户所归属的移动网络。中国移动TD系统使用00,
中国联通GSM系统使用01,中国移动GSM系统使用02,中国电信CDMA系统使用03
lac:Location Area Code(位置区码)。移动通信中,为了确定终端台的位置,每个移动网络的覆盖区都被划分
成许多位置区,位置区码(LAC)则用于标识不同的位置区。
cid:cell id(基站编号)
*/
native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
int lac, int cid);
/*
设置终端与移动网络相关的一些参数信息,其type参数决定了setid参数的取值。type可取值有:
type: AGPS_SETID_TYPE_NONE,值为0,无意义
AGPS_SETID_TYPE_IMSI,值为1,代表IMSI(international mobiles subscriber identity,
国际移动用户号码标识)。IMSI信息存储在SIM卡上。
AGPS_SETID_TYPE_MSISDN,值为2,代表Mobile Subscriber ISDN(用户号码),即手机号码
*/
native void native_agps_set_id(int type, String setid);
/*
通知GPS HAL层系统当前网络的状态,其各参数解释如下:
connected:网络是否连接
type:网络类型,可取值请参考ConnectivityManager中各网络类型的定义情况。常用的有TYPE_MOBILE、
TYPE_WIFI等
roaming:是否处于漫游状态
available:网络是否可用
extraInfo:附加信息
defaultAPN:默认APN
*/
native void native_update_network_state(boolean connected, int type,
boolean roaming, boolean available, String extraInfo, String defaultAPN);
现在来看看JNI回调Java层的函数,如下所示:
[-->GpsLocationProvider.java]
//GPS模块汇报位置信息
void reportLocation(int flags, double latitude, double longitude, double altitude,
float speed, float bearing, float accuracy, long timestamp):
//GPS模块通知GPS工作状态
void reportStatus(int status):
//GPS模块通知卫星状态
void reportSvStatus():
//GPS模块通知AGPS状态
void reportAGpsStatus(int type, int status, int ipaddr):
//GPS模块通知NMEA信息
void reportNmea(long timestamp):
//GPS模块通知GpsLP自己的能力
void setEngineCapabilities(int capabilities):
//GPS模块要求下载XTRA数据
void xtraDownloadRequest():
//GPS模块通知Network Initiated通知,其各参数解释如下:
void reportNiNotification(
int notificationId,//GPS HAL层分配的通知ID
int niType,//NI类型,可取值请参考gps.h的GpsNiType的定义
int notifyFlags,//标志信息,可取值请参考gps.h的GpsNiNotifyFlags定义
int timeout,//等待用户处理的超时时间
//当超时发生后,系统采用的默认处理结果。其取值和native_send_ni_respons中第二个参数一样
int defaultResponse,
String requestorId,//代表请求者的id
String text, //通知信息
int requestorIdEncoding,//requestorId的编码格式,参考gps.h GpsNiEncodingType的定义
int textEncoding,//text的编码格式
String extras //附加信息
):
//要求获取参考位置信息,flags参数目前没有使用
void requestRefLocation(int flags):
//要求设置移动网络相关信息,flags参数表示要获取什么样的信息,可取值同native_agps_set_id的type参数
//一致
void requestSetID(int flags):
//要求获取UTC时间
void requestUtcTime():
接下来看GPS JNI与HAL层的交互接口。我们主要介绍JNI调用HAL层的接口。
(3) GpsInterface及其他交互接口介绍
GpsInterface是GPS JNI与HAL层交互的主要接口。在Android平台中,该接口定义在一个同名的结构体中,其内容如下所示:
[-->gps.h::GpsInterface结构体]
typedef struct {
size_t size;//GpsInterface结构体的长度
//初始化GPS模块。其参数为GPS模块所需的回调函数
int (*init)( GpsCallbacks* callbacks );
//下面这两个函数用于开启或关闭导航
int (*start)( void );
int (*stop)( void );
//清理GPS模块所分配的资源
void (*cleanup)( void );
//输入UTC时间,参数解释同native_inject_time
int (*inject_time)(GpsUtcTime time, int64_t timeReference,int uncertainty);
//输入位置信息,参数解释同native_inject_location
int (*inject_location)(double latitude, double longitude, float accuracy);
//删除赋值信息,其参数解释同native_delete_aiding_data
void (*delete_aiding_data)(GpsAidingData flags);
//设置GPS模块的工作模式,其参数解释同native_set_position_mode
int (*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence,
uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time);
//获取GPS模块实现的扩展接口,AGPS扩展接口对应的name为“agps”、xtra扩展接口对应的name为“xtra”
const void* (*get_extension)(const char* name);
} GpsInterface;
比较GpsInterface和Java层定义的native函数,读者可发现二者结合非常紧密。实际上,JNI实现的那些native函数最终都会把请求通过HAL层接口交给GPS模块去处理。
再来看gps.h定义的GpsXtraInterface接口,相关内容封装在同名的结构体中,如下所示:
[-->gps.h::GpsXtraInterface结构体]
typedef struct {
size_t size;
//初始化GPS中的xtra相关模块
int (*init)( GpsXtraCallbacks* callbacks );
//输入xtra数据,其参数解释同native_inject_xtra_data
int (*inject_xtra_data)( char* data, int length );
} GpsXtraInterface;
接下来看AGpsInterface结构体,代码如下所示:
[-->gps.h::AGpsInterface]
typedef struct {
size_t size;
//初始化AGPS模块
void (*init)( AGpsCallbacks* callbacks );
//打开AGPS数据连接,其参数解释同native_data_conn_open
int (*data_conn_open)( const char* apn );
//关闭AGPS数据连接,其参数解释同native_data_conn_close
int (*data_conn_closed)();
//AGPS数据连接操作失败,同native_data_conn_fail
int (*data_conn_failed)();
//设置AGPS服务器地址等相关信息,参数解释同native_agps_set_server
int (*set_server)( AGpsType type, const char* hostname, int port );
} AGpsInterface;
最后来看GpsNiInterface和AGpsRilInterface接口。GpsNiInterface的代码如下所示:
[-->gps.h::GpsNiInterface]
typedef struct
{
size_t size;
//初始化NI模块
void (*init) (GpsNiCallbacks *callbacks);
//发送NI回复,请参数同native_send_ni_response
void (*respond) (int notif_id, GpsUserResponseType user_response);
} GpsNiInterface;
而AGpsRilInterface的代码如下所示:
[-->gps.h::AGpsRilInterface]
typedef struct {
size_t size;
//初始化AGPS Ril相关的处理模块
void (*init)( AGpsRilCallbacks* callbacks );
//设置参考位置信息,其第一个参数类型为AGpsRefLocation,该结构体的成员与
//native_agps_set_ref_location_cellid函数的参数一一对应
void (*set_ref_location) (const AGpsRefLocation *agps_reflocation, size_t sz_struct);
//设置AGPS移动网络id信息,其参数解释同native_agps_set_id
void (*set_set_id) (AGpsSetIDType type, const char* setid);
//设置NI消息,其参数解释同native_agps_ni_message
void (*ni_message) (uint8_t *msg, size_t len);
//注意,下面这两个函数的参数合起来就是native_update_network_state的参数。Java层调用
//一次native_update_network_state将触发下面这两个函数被调用
//更新移动网络状态
void (*update_network_state) (int connected, int type, int roaming, const char* extra_info);
//设置网络连接状态
void (*update_network_availability) (int avaiable, const char* apn);
} AGpsRilInterface;
9.3.4 Android中位置管理知识总结
本节对Android平台中LocationManagerService及相关模块进行了介绍,尤其对本章的核心GpsLocationProvider及GPS各层次及之间的交互接口进行了重点讲解。
和本书前面介绍的WifiService、WifiP2pService以及NfcService比起来,LMS一点也不复杂,这其中的几个主要原因包括:
- LMS提供的服务本身就比较简单,它的核心功能就是提供位置信息。
- GpsLP通过合理的分层接口设计使得GPS HAL层之上的代码能够不受底层硬件的影响。
另外,笔者希望读者在学习完本章后,对以下内容开展进一步的学习:
- 研究PassiveProvider和FusedLocationProvider的内容。掌握LMS如何与位于应用进程的LP进行交互。
- 学习LocationFudger的内容,掌握如何模糊位置信息。
- 学习GeofenceManager的内容。
- 最后,读者可尝试反编译NetworkLocation.apk,掌握NetworkLP以及Geocoder的实现原理[①]。
9.4 本章总结和参考资料说明
9.4.1 本章总结
本章内容分为两大块:
- 首先对本章所用到的GPS基础知识进行了介绍,这部分内容比较广,难度不大,读者可轻松掌握它们。
- 然后对Android平台中的位置管理模块进行了详细介绍。从代码上看,这些模块的难度都不大。
从整体情况来看,GPS是全书难度最大的一章,其中一个主要原因是GPS所涉及到的背景知识非常庞杂,而且历经几十年的发展,相关的知识更新速度也非常快。
目前,与GNSS相关的LBS还在高速发展中,谁能在如此激烈的竞争中拔得头筹呢?当然是那些有技术积淀,并能灵活快速应对市场需求的公司和组织了。在此,笔者希望读者不要满足于掌握本章甚至本书的内容,而应该把目标放得更长,更远一些,只有这样才能跟上技术发展的脚步,从而在激烈竞争中处于有利的位置。
9.4.2 参考资料说明
概述
[1] http://baike.baidu.com/subview/152851/5072513.htm?fromId=152851&from=rdtself
百度百科对LBS的介绍,读者可了解其“产生背景”一节的内容。
GPS基础知识
GPS基础知识这一节主要的参考资料是GPS Essentials of Satellite Navigation Compendium一书。它是笔者找到的对软件工程师而言关于GPS知识最全面和最通俗易懂的一本书。该书电子版可参考http://www.docin.com/p-67411894.html。
坐标系介绍
[2] GPS Essentials of Satellite Navigation Compendium第2章“Coordinate systems”
对GPS坐标系相关知识的介绍,读者可先略过投影那几节的内容。
[3] http://principles.ou.edu/earth_figure_gravity/geoid/
对GPS高度和海拔高度的解释。
时间系统
[4] 《GPS基本原理及其Matlab仿真》第3.2节“GPS时间系统”
时间系统是本章最难讲解的部分了,读者可在本章基础上再做深入学习。
[5] http://www.doc88.com/p-241652413475.htmlhttp://www.doc88.com/p-241652413475.html
GPS与民用时间的转换
[6] http://www.hko.gov.hk/gts/time/basicterms-localtimec.htm
香港天文台官方网站对本地时间的介绍
卫星轨道等知识
[7] GPS Essentials of Satellite Navigation Compendium第3章“Foundations of satellite technology”
GPS基础知识
[8] http://www.gps.gov/systems/gps/
GPS系统的美国官方网站,内容非常详细和生动,建议读者仔细阅读。
[9] http://www.gps.gov/systems/gps/space/
GPS官方网站对GPS空间段建设历史的描述。
[10] http://en.wikipedia.org/wiki/List_of_GPS_satellite_launches
维基百科对GPS卫星发射规划的介绍。
[11] GPS Essentials of Satellite Navigation Compendium第4章“GNSS technology: the GPS example”
GPS通信频段介绍
[12] http://www.gps.gov/systems/gps/modernization/civilsignals/#L2C
[13] http://www.gps.gov/systems/gps/modernization/civilsignals/#L5
GPS官方网站对L2C和L5频段的介绍。
[14] Interface Specification GPS 705C
该资料是GPS官方规范,用于定义空间段和地面控制段以及用户段通过L5频段交互的接口。如果不需要对L5做进一步了解的话,读者可略过它。
GPS官方规范的下载地址为http://www.gps.gov/technical/
GPS信号介绍
[15] Understanding the GPS:Introduction to the GPS第2部分“Basic Signal Structure And Error”
这本书是GPS Essentials of Satellite Navigation Compendium一书的升级参考书籍,讲解得非常透彻,覆盖面也比较广,建议读者深入阅读。
GPS导航电文介绍
[16] Interface Specification GPS 200G
该资料是GPS官方规范,用于定义空间段和地面控制段以及用户段通过L1及L2频段交互的接口。这应该是GPS的核心规范了,建议读者详细阅读。其中,附录II,III和IV详细介绍了GPS卫星发送导航电文的组成及相关参数。
定位计算相关知识
[17] GPS Essentials of Satellite Navigation Compendium第6章“Calculating position”
[18] http://www.doc88.com/p-797227641283.html
“GPS测速精度研究及应用”,一篇硕士学位论文,读者权当参考。
[19] http://wenku.baidu.com/view/3541a8a0b0717fd5360cdcc4.html
中国移动A-GPS终端技术规范。
[20] http://www.gpsinformation.org/dale/why12.htm
该资料对多channel功能可提升GPS接收器定位速度的原因进行了介绍。
NMEA-0183和GPX
[21] http://www.doc88.com/p-992198901288.html
NMEA Reference Manual. NMEA规范文档,感兴趣的读者可详细阅读它。
[22] http://www.topografix.com/gpx.asp
GPX官方网站。GPS格式比较简单,读者可轻松学会它。
[23] http://en.wikipedia.org/wiki/GPS_eXchange_Format
维基百科对GPX的介绍。
[24] https://developers.google.com/kml/documentation/
Google关于KML的介绍,上面有一些初学者入门指南资料。
GPS增强系统介绍
[25] GPS Essentials of Satellite Navigation Compendium第7章“Improved GPS: DGPS, SBAS, A-GPS and HSGPS”
[26] http://www.tutorialspoint.com/lte/lte_radio_protocol_architecture.htm
Control Plane和User Plane的区别
OMA-SUPL介绍
[27] OMA Secure User Plane Location Architecture 3.0版
OMA-SUPL官方技术文档,它对SUPL整个架构和相关功能进行了定义,读者务必认真阅读它。
[28] OMA User Plane Location Protocol 3.0版
ULP协议文档,建议读者认真阅读。
注意,OMA-SUPL相关协议的最高版本为3.0,读者可从官方网站http://technical.openmobilealliance.org/Technical/release_program/supl_V3_0.aspx下载。
[29] http://wenku.baidu.com/view/cc6b150703d8ce2f006623e2.html
“基于SUPL的移动定位系统的研究和设计”,一篇硕士学位论文,对移动定位技术介绍得非常全面,建议读者认真阅读。
Android中的位置管理
LocationManager应用示例
[30] https://developer.android.com/google/play-services/location.html
Android SDK中关于Google Play Service中Location API的官方介绍,建议那些对开发高级LBS相关应用程序感兴趣的读者阅读它。
NTP相关
[31] http://en.wikipedia.org/wiki/Network_Time_Protocol
维基百科关于NTP协议的介绍。
LTO相关
[32] http://www.broadcom.com/products/GPS/Location-Based-Services/LTO-AGPS
博通公司关于LTO的介绍,建议读者仔细阅读。
数据短信收发
[33] http://blog.fordemobile.com/2012/09/use-sms-to-send-and-receive-raw-data.html
国外一篇关于如何在Android平台上收发数据短信的文章。
[①]出于对版权的考虑,笔者不能在书中对NetworkLocation.apk反编译的结果开展详细讨论。如果时机合适,笔者将在博客上对它的实现进行介绍