定位方式简介
Android
的定位系统是一套用于获取设备地理位置信息的软件和硬件组件,它允许应用程序和服务访问设备的位置信息数据。按照定位原理分类, 主要有以下的几种定位方式:
- GNSS定位
全球导航卫星系统(GNSS)是一种使用卫星信号来确定设备精确地理位置的技术,Android
设备设备通常配备了GNSS接收器,可以通过计算卫星信号来获取经度和纬度坐标。这种定位手段可以不需要接入网络,一般用于户外位置获取。GNSS 是美国的GPS、欧盟的GALILEO、俄罗斯的GLONASS和中国的北斗导航系统等的统称。 - 蜂窝网络定位
这种方法使用移动蜂窝网络基站的信息来估算设备的位置,通常可以提供较粗略的位置信息。基站数据量大,定位时需要将基站信息上传到服务器获取基站位置信息,然后计算出设备的位置。 - Wi-Fi定位
Android
设备可以扫描周围的Wi-Fi
网络,并根据已知Wi-Fi
热点的位置信息和信号强度来估算设备的位置。Wi-Fi
定位通常用于室内和城市环境中。 - 蓝牙定位
在一些特定的应用程序中还会使用蓝牙定位,通过扫描附近的特殊蓝牙基站设备来计算设备的位置。这种方法通常用于室内导航,会在室内布置一定数量的蓝牙基站。 - 传感器融合定位:
利用设备自带的陀螺仪、加速度计和磁力计等传感器,可以提高定位准确度。常见应用场景如行车导航,在卫星信号不良的情况下利用传感器进行惯性导航。
定位原理简介
基本原理简要概括:时间计算距离,三球测量单点。
不同的工作方式计算方式有所不同。
GNSS定位
GNSS 由三部分组成:
- 运行在太空的卫星星座天空中的卫星星座组成的信标广播系统,会不断向地球发送包含卫星空间坐标和时间信息的信号;
- 卫星地面控制部分处于地球的控制站能够控制天空中的卫星,控制卫星的飞行轨迹以及广播的星历、历书信息和时间信息,同时监控卫星飞行轨迹,不断修正卫星数据,保证卫星广播数据准确;
- 用户设备部分
用于接收卫星信号,通过解析广播中的卫星空间信息和时间信息,先计算出相对卫星的距离,然后通过至少三个卫星组成的球面计算出终端的空间坐标,带入地球模型,即可得到经纬度和高度信息;
星历:用来记录卫星的运行参数,通过星历,可以计算出任意时刻的导航卫星的位置和速度。星历表包含了非常详细的卫星轨道和位置信息,所以其数据量较大,传输时间较长,为了可以这个困难,人们设计了其简化集,即历书;
历书:包含卫星的位置等相关信息,精度较低;
时间:虽然卫星广播中附带了准确的时间,但由于电磁波在大气传播存在时延误差,所以一般使用更多的卫星来校准时间;
一次定位过程可以划分为三个阶段:
- 搜索卫星
- 下载星历/历书
- 计算位置
其中,搜索卫星和下载星历比较耗时,所以可以划分为三种情况:
- 冷启动:
手机第一次定位,没有保存星历和历书,则需要将上述流程完全走一遍,TTFF至少有12.5分钟; - 暖启动:
保存有星历和历书,只是比较老(历书不超过180天,旧时间信息不超过20S,旧位置信息不超过100Km),需要更新,更新后进行定位。TTFF至少为30S; - 热启动:
保存有效的星历,时间和位置信息,这种只需要测距定位就可以了,TTFF小于10S;
AGPS:
一些终端中会有一个AGPS
的功能,它的作用是通过互联网下载星历后注入给芯片,降低搜星和下载星历的耗时,实现缩减冷暖启动首次定位时间的效果。
蜂窝网络定位/Wi-Fi定位/蓝牙定位
这几类定位方式的原理基本一致,均是扫描已知地理位置的物体或设备信息,然后通过电信号计算出相对已知物体的距离,进而通过三个已知点的距离求解目标点位置。差异在于数据源:
- 基站:
运营商的基站地理位置信息为已知条件(运营商或三方系统授权持有),终端设备可以扫描基站得到基站列表,通过向基站发送应答包计算设备到基站的距离(已知电磁波传播速度),最后通过基站已知坐标和对应的距离信息,可以推算出终端的位置。基站信息量庞大,终端设备一般仅扫描基站,然后通过网络将基站信息发回服务器进行计算。
由于地面电磁波环境复杂容易发生衍射、多径传播等情况,会对定位精度造成较大干扰,所以此种方式定位精度较低,通常在几百米到几千米之间。 - WIFI/蓝牙:
WIFI和蓝牙原理相似,一般需要内建WIFI或蓝牙基站的位置信息数据库作为已知条件,终端设备扫描WIFI、蓝牙基站的信息和信号强度,再通过信号强度估算出终端和基站的距离(通过信号衰减程度计算),最后和数据库作对比计算终端的位置。
一句话总结:
- GNSS 定位和网络定位都是通过空间已知点和距离计算终端的位置,差别有三:
- GNSS 使用独立的GNSS接收机硬件实现定位,网络定位通常依赖传感器的数据使用软件计算位置;
- GNSS 使用卫星作为位置计算参考点,网络定位使用各种地面的信标(基站、WIFI、蓝牙等)作为位置计算参考点;
- GNSS 计算过程在终端芯片内可以无需网络参与,网络定位计算过程在软件层面(终端或服务器),一般情况需要通过网络上下传数据;
- GNSS 定位计算过程都在芯片内,基本无法干预; 网络定位计算过程一般在云端;
Android GNSS 系统结构
Android SDK
Android SDK 中提供了一组请求/停止请求定位信息的接口,请求定位的参数主要有进行定位的提供者(常用GNSS或NETWORK)、定位间隔参数(时间和距离)、接收定位结果的回调接口。
另外还有用于设置监听 GNSS 卫星状态的回调接口和监听 GNSS 芯片 NMEA 数据的回调接口,前者用于反馈当前的卫星数量、星座、以及型号质量等数据,后者用于展示、原始数据后处理、日志抓取等等。
请求定位的提供者常用的有GPS(GNSS)和NETWORK,另外还有 FUSED 和 PASSIVE:
- GPS_PROVIDER(GNSS)
期望系统启动卫星定位系统进行定位,这种方式会产生较高的功耗,精度较高; - NETWORK_PROVIDER
期望系统启动基于网络的方式进行定位,这种方式相比卫星定位会有较低的功耗,但精确度相对会比卫星定位低; - FUSED_PROVIDER
期望调用 GMS(谷歌移动服务) 进行定位,国内用不了; - PASSIVE_PROVIDER
期望系统不主动定位,通过“蹭”系统内别的 PROVIDER 的定位结果的定位方式,不常用;
SDK 层面操作的类主要为 android.location.LocationManager
,它暴露了请求定位,注册回调等方法。这个类包装了 ILocationManager
AIDL 接口,用于和 Framework
通信。
相关的接口如下:
public interface LocationListener {
void onLocationChanged(Location location);
@Deprecated
void onStatusChanged(String provider, int status, Bundle extras);
void onProviderEnabled(String provider);
void onProviderDisabled(String provider);
}
public void requestSingleUpdate(@NonNull String provider, @NonNull LocationListener listener, @Nullable Looper looper)
public void requestLocationUpdates(@NonNull String provider, long minTime, float minDistance, @NonNull LocationListener listener)
public void removeUpdates(@NonNull LocationListener listener)
public static abstract class Callback {
public void onStarted() {}
public void onStopped() {}
public void onFirstFix(int ttffMillis) {}
public void onSatelliteStatusChanged(GnssStatus status) {}
}
public boolean registerGnssStatusCallback(@NonNull GnssStatus.Callback callback)
public void unregisterGnssStatusCallback(@NonNull GnssStatus.Callback callback)
public interface OnNmeaMessageListener {
void onNmeaMessage(String message, long timestamp);
}
public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener)
public void removeNmeaListener(@NonNull OnNmeaMessageListener listener)
Android Framework
Framework 层面主要的类为 android.server.LocationManagerService
,它实现了 ILocationManager
AIDL 接口,一面透过 SDK 和应用打交道,一面透过各种 PROVIDER
和定位实现打交道:
- GPS_PROVIDER(GNSS)
通过 GNSS HAL 接口和芯片打交道。 - NETWORK_PROVIDER
依赖设备厂商的具体实现,Android 默认实现依赖 Google 提供的 GMS 实现国内无法使用;一般厂商会实现为通过基站/WIFI/蓝牙进行融合定位。 - FUSED_PROVIDER
调用 GMS(谷歌移动服务) 进行定位,国内用不了; - PASSIVE_PROVIDER
使用PassiveProvider
在系统位置更新时通知给应用,分享其他 PROVIDER 的定位结果;
LocationManagerService
在系统开机时由 SystemServer
的 startOtherServices
方法中初始化并调用 systemRunning
方法拉起,然后初始化各种 PROVIDER
,并且监听系统各种影响定位的状态(例如系统定位开关、低功耗模式、锁屏、用户切换等)。
LocationManagerService
的主要业务逻辑包括:
- 管理 Location Provider
- 应用定位权限管控
- 监听定位相关功能开关状态
- 监听系统状态(低功耗等特殊模式)变化,及时调整定位策略
- 分发定位信息(定位结果、卫星信息、NMEA数据)
LocationManagerService
和应用交互包括:
- 接收应用的定位请求
- 检查应用定位权限
- 回调应用的定位结果
- 回调 GNSS 卫星状态
- 回调 GNSS NMEA 数据
LocationManagerService
操作 GnssLocationProvider
和 GNSS HAL 交互包括:
- 控制芯片启动、停止定位
- 控制芯片的定位参数
- 向芯片注入辅助定位的特定数据
- 设置 HAL 解析定位信息的回调
- 接收并上报 HAL 解析的定位信息
HAL
HAL 层面是 Framework
和芯片的中间角色,对外屏蔽了不同芯片的差异。主要的职责包括:
- 控制定位芯片上下电
- 控制芯片启动、停止工作
- 处理
Framework
的定位请求 - 转发向芯片注入辅助定位的信息(AGPS、RTK等)
- 解析芯片输出的 NMEA 协议的报文数据
- 向
Framework
上报芯片输出的定位结果、卫星信息、NMEA数据等信息
驱动程序
GNSS 芯片上下电控制手段各不相同,有的使用GPIO,有的使用芯片平台的电源管理模块。
GNSS 芯片使能控制手段通常使用GPIO。
GNSS 芯片常见的通讯方式为串口,所以驱动程序以内核自带的串口驱动为主。
GNSS芯片
芯片厂商封装了 GNSS 射频单元、运算单元等功能的芯片。芯片能够接收卫星广播的星历、时间信息,内部进行计算后得到卫星信息和定位结果,通过串口对外输出使用 NMEA-0183 协议封装的报文。
网络定位
Android
初始化 LocationManagerService
时构造 LocationProviderProxy
实例,LocationProviderProxy
会寻找并绑定符合要求服务,作为网络定位的实现实例,然后应用请求网络定位时就会将定位请求发给绑定的服务实例处理。
被绑定的服务需要满足以下要求:
- 声明
ACTION
为com.android.location.service.v3.NetworkLocationProvider
以便被系统绑定 - 包名出现在
Framework
的config_locationProviderPackageNames
字段当中,系统会过滤不可信的定位服务实现 onBind
返回的实例需要继承自LocationProviderBase
,用以接收系统的定位请求和上报定位结果。