Android和蓝牙GPS结合的方法
这篇未完成的文章写于2016年初,两年后翻到了,贴出来。。。
1.1. Android LMS的原理
Android系统设计了一个以LocationManagerService为核心的位置管理架构提供相关的位置服务[1],以下基于Android4.4的代码展开。
图1 LMS原理
1. LocationManagerService和其客户端LocationManager。
LocationManagerService和Android Java Framework中其他Service一样由SystemServer创建并运行在system_process进程中。LocationManagerService内部将统一管理Android平台中能提供位置服务的相关模块,而LocationManager为那些需要使用位置服务的应用程序服务。LocationManager和LocationManagerService之间通过Binder进行交互
2. Android平台中能提供位置服务的相关模块统称为Location Provider(位置提供者,LP)。位置提供者必须实现LocationProviderInterface接口。这些接口对应的对象实例由LMS来创建和管理。在所有这些位置提供者中,Android Framework实现了其中的PassiveProvider和GpsLocationProvider。这两个LP由LMS创建并运行在system_process进程中。
3. 除了使用GPS定位外,系统还支持网络定位(NetworkLocation)方法来获取位置信息。这种方法大致的工作原理是,某地区的移动通信基站(Cell Tower)或无线网络AP的位置信息都已事先获取并保存在相关服务提供商的服务器上。当手机使用网络定位时,它首先向服务器查询自己所连接或搜索到的基站位置或AP的位置,然后根据信号的强度推算自己的大致位置。相比GPS定位而言,网络定位速度快,耗电少,适用于室内和室外,但精度较GPS差。Android原生代码并不提供Network Location Provider相关的功能,它一般由第三方应用厂商提供,例如Google的GMS(Google Mobile Service)包中有一个NetworkLocation.apk就提供了该功能,而国内上市的手机则使用百度公司提供的NetworkLocation_Baidu.apk。它们运行在应用程序所在的进程中。
4. FusedLocationProvider是一个比较特殊的LP。它本身不能提供位置信息,其内部将综合GpsLP和NetworkLP的位置信息,然后向使用者提供最符合使用者需求的数据。即它能根据使用者对电源消耗、精度两方面的要求以选择GpsLP或/和NetworkLP
作为真实的LP。同时,FusedLP能选择GpsLP或NetworkLP提供的位置信息中最好的那一个返回给使用者。简单点说,FusedLP出现之前,一个比较完善的LP客户端需要同时操作和管理GpsLP和NetworkLP,而有了FusedLP后,客户端只需要使用它即可,其余事情由FusedLP内部来管理。注意,FusedLP也由应用程序提供,它运行在FusedLocationProvider.apk所在的进程中。
5. GeoCodeProvider: 除了提供位置信息外,系统(借助第三方应用提供)还支持位置信息和地址信息相互转换,即得到某个地址(如国家、市区、街道名等)的位置信息(如经纬度信息),或者根据位置信息得到其对应的地址信息。由于地址和位置信息的映射关系一般也由第三方应用提供,所以LMS 和第三方应用中实现GeocodeProvider的对象交互。
6. libgps.so 包含了对GPS NMEA协议信息的解析, 其源代码主要位于
hardware/libhardware_legacy/gps/gps_qemu.c
NMEA是(National Marine Electronics Association )为海用电子设备制定的标准格式,是GPS接收机最通用的数据输出格式。
1.2. 通用架构设计
1. 设计原则: 低耦合,可替换
从上一节的AndroidLocation原理图上可以看出,GPS接入的处理在硬件抽象层(HAL)的libgps.so 对于普通的应用层App,是无法对此作出修改的。
如果直接修改HAL的代码,固然简单,但是需要拿到整个系统的源码,这种情况只能适用于特殊的机型,不属于本文通用框架的考虑范畴。
本文提出低耦合的,可以替换各种GPS来源的框架,既有利于架构分层明晰,也有利于对不同的GPS设备做对比的评测。
Android 处理之前提到的各种LocationProvider以外,还提供了一种模拟Location的Provider。借助这个特性,可以对LocationManager设置模拟的位置信息,从而达到不改变App层的代码,就能使用各种来源GPS的效果。
该方案的性能虽然略弱于直接修改HAL,但是贵在维护性好,扩展性好,能够满足大部分定位要求。
2. 设计原则:可记录,可回放
记录信号和事后回放是LBSApp的常用调试手段,对于不同的GPS来源,使用LocationManager的位置输出,作为统一的位置记录格式。
各个GPS Agent 可以按实际情况记录原始的位置信息。
3. 通用设计架构图
图2 通用架构图
LBS App 就是我们主要的基于位置的App。用于记录位置信息,使用位置信息。
MockLocationProvider是一个单独的App,用于提供伪装的位置服务,该位置信息则由蓝牙,COM口或者网络连接获取的GPS数据来解析得到。GPS位置信息提取之后,构造MockLocation传给LocationManager。LocationManager将该MockLocation视同真正的位置信息,提供给所有App使用。
以上两个App使我们的主要制品。尤以MockLocationProvider(GPSAgent) 最为重要,因为LBS App可以是其他的 通用App,如Baidu Map,AutoMap之类。
1.3. 蓝牙GPS Agent的设计
图3 蓝牙GPS Agent 序列图
上图实现基于AndroidSDK 由Java实现。
首先操作从UI层发起,选择已经配对的蓝牙GPS的名称。然后启动新的线程来连接蓝牙,之所以要启动新的线程,是因为UI线程不能被阻塞,Android建议界面的刷新频率在60fps,即每帧处理时间应在1000/ 60 = 16ms 之内,对于连接外设这种无法预期的操作,自然要放在UI线程之外了。
连接蓝牙设备成功之后,就要读取蓝牙GPS发过来的数据了,一般来说是启用新的线程来读取,至于为什么不直接在连接线程就读取呢,我觉得这样也是可行的,而且线程少也容易管理。不过Android SDK给出的示例代码是启用新的线程[2],这样逻辑更清晰些,但是程序复杂度会增加,使用者可以自行斟酌,本项目采用新的读取线程来处理。
从蓝牙GPS读取的数据是NMEA数据,需要进行解析,然后填充MockLocation发送给LocationManager。
1.4. NMEA的解析方案设计
NMEA-0183协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有$GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL等。下面给出这些常用NMEA-0183语句的字段定义解释
图4 NMEA语句结构
NMEA Frame 以$开头 以"\r\n"结尾
<Address>字段前两个字符表示GNSS的TalkID
NMEA Talk IDs
配置后的GNSS |
Talker ID |
GPS, SBAS, QZSS |
GP |
GLONASS |
GL |
Galileo |
GA |
Beidou |
GB |
其他兼容GNSS |
GN |
表1 GNSS TalkerID
一般GPS都写成GP,如有其它卫星的数据则写成GN,有些GPS接收机可以被用户配置指定TalkerID,本文都以GP为例。
<Address>字段后3个字符表示NMEA语句的类型格式
常用语句如下:
类型 |
全称 |
信息 |
GGA |
Global positioning system fix data |
GPS定位数据时间、经度、纬度、所用到的卫星数和高度 |
RMC |
Recommended Minimum Specific GPS/TRANSIT Data |
推荐的最少GNSS数据:时间、经度、纬度、高度、系统状态、速度、线路、日期 |
GLL |
Geographic Position |
地理位置-经度、纬度、时间和卫星运行状态 |
GSA |
GPS DOP and Active Satellites |
GNSS DOP,活动卫星编号,测量模式2D或3D |
VTG |
Track Made Good and Ground Speed |
地面速度信息 |
表2 常用NMEA语句类型
<Value> 字段由各个语句定义,均由逗号分隔
<Address> + <Value> 计算出校验和,放在<checksum>段,以该段*号开头,由两个16进制数表示,校验和算法为按字符做异或运算
GGA示例
$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,12.2,M,19.7,M,,0000*1F
字段0:$GPGGA,语句ID,表明该语句为Global Positioning System Fix Data(GGA)GPS定位信息
字段1:UTC 时间,hhmmss.sss,时分秒格式
字段2:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
字段3:纬度N(北纬)或S(南纬)
字段4:经度dddmm.mmmm,度分格式(前导位数不足则补0)
字段5:经度E(东经)或W(西经)
字段6:GPS状态,0=不可用(FIX NOTvalid),1=单点定位(GPS FIX),2=差分定位(DGPS),3=无效PPS,4=实时差分定位(RTK FIX),5=RTK FLOAT,6=正在估算
字段7:正在使用的卫星数量(00 - 12)(前导位数不足则补0)
字段8:HDOP水平精度因子(0.5 - 99.9)
字段9:海拔高度(-9999.9 - 99999.9)
字段10:单位:M(米)
字段11:地球椭球面相对大地水准面的高度 WGS84水准面划分
字段12:WGS84水准面划分单位:M(米)
字段13:差分时间(从接收到差分信号开始的秒数,如果不是差分定位将为空)
字段14:差分站ID号0000 - 1023(前导位数不足则补0,如果不是差分定位将为空)
字段15:校验值($与*之间的数异或后的值)
RMC示例
$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
字段0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
字段1:UTC时间,hhmmss.sss格式
字段2:状态,A=定位,V=未定位
字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
字段4:纬度N(北纬)或S(南纬)
字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)
字段6:经度E(东经)或W(西经)
字段7:速度,节,Knots
字段8:方位角,度
字段9:UTC日期,DDMMYY格式
字段10:磁偏角,(000 - 180)度(前导位数不足则补0)
字段11:磁偏角方向,E=东W=西
字段12:模式,A=自动,D=差分,E=估测,N=数据无效(3.0协议内容)
字段13:校验值($与*之间的数异或后的值)
MockLocation 的字段来源[3]
android.location.Location |
API含义 |
NMEA 语句 |
float getAccuracy() |
Returns the accuracy of the fix in meters. 定位精度 |
GGA Horizontal dilution of position 水平方向的精度因子 hdop* 10 作为近似的精度 |
double getAltitude() |
Returns the altitude of this fix. 海拔 |
GGA 的海平面 |
float getBearing() |
Returns the direction of travel in degrees East of true North 方向角,正北偏东度数 |
RMC 的 Track angle in degrees True |
Bundle getExtras() |
Returns additional provider-specific information about the location fix as a Bundle 可自定义,暂不处理 |
- |
double getLatitude() |
Returns the latitude of this fix. 定位时的纬度 |
GGA 或者 RMC的纬度 |
double getLongitude() |
Returns the longitude of this fix. 定位时的精度 |
GGA 或者 RMC的精度 |
String getProvider() |
Returns the name of the provider that generated this fix, or null if it is not associated with a provider. |
默认 GPSProvider |
float getSpeed() |
Returns the speed of the device over ground in meters/second. 地面速度 m/s |
RMC Speed over the ground in knots 单位需转成m/s |
long getTime() |
Returns the UTC time of this fix, in milliseconds since January 1, 1970. 自1970-1-1以来的UTC时间,单位ms |
RMC UTC Date and Time GGA 只有时间没有日期,不能用 |
表3 Android Locaton和NMEA语句对应关系
根据上表的总结
从RMC获取经纬度,UTC时间,速度,方向角
从GGA获取海拔和精度信息
只需要解析2句NMEA即可完成填充MockLocation的任务
1.5. DGPS Agent 原理
DGPS(Differential Global Positioning System,差分GPS),用于提高GPS的定位精度
图5 DGPS原理
为了减少(或者修正)定位计算的误差,人们事先把GPS接收机放在位置已精确测定的点上,这些点叫基站。基站的接收机通过接收GPS卫星信号,测得并计算出它们到卫星的伪距,将伪距和已知的精确距离相比较,求得该点在GPS系统中的伪距测量误差。然后这些基站再将这些误差作为修正值以标准数据格式通过播发台向周围空间播发。
在基站附近的DGPS用户一方面接收GPS卫星信号进行测距,同时它接收来自基站的误差修正信息,并以此来修正定位结果,从而提高定位精度。
DGPS用户离基站多远才算附近呢?下面有两个参考距离。
如果使用伪距差分定位(CodeDifferential Positioning)技术,则DGPS和基站的距离最好在200千米以内。
如果使用载波相位差分定位(Carrier-PhaseDifferential Positioning)技术,则DGPS和基站的距离最好在20千米内。
当修正数据用卫星发送时,这种系统就叫SBAS。
Satellite Based Augmentation System (SBAS)
– 利用地球静止轨道卫星建立的地区性广域差分增强系统
当然,SBAS的功能远不止简单地播发修正数据,它还能监测GPS或其他GNSS卫星的情况以加强信号的可靠性和安全性。展示了目前几个已投入使用或在建的SBAS系统以及它们的覆盖范围。
图6 全球主要SBAS示意图
几个主要的SBAS从左至右分别如下。
·WAAS:美国建造的广域增强系统(Wide Area AugmentationSystem)。
·EGNOS:欧盟建造的欧洲静地导航覆盖服务(EuropeanGeostationary Navigation Overlay Service)。
·GAGAN:印度建造的地球同步轨道增强导航系统(GPS And GEOAugmented Navigation)。
·Beidou:中国建造的北斗导航系统。
·MSAS:日本建造的多功能卫星增强系统(MultifunctionalSatellite Augmentation System)。
虽然每个单独的SBAS只能覆盖一定的范围,但通过RTCA DO-229C协议,SBAS之间的数据能够保证兼容性。
1.6. Android 串口数据读写的实现
DGPS一般都是通过串口读写,但是Android SDK并未直接提供读写串口的API。需要通过一些特殊的方式实现
方案1: 使用NDK的Linux C函数,在取得设备权限的情况下,读写串口设备文件,方法和Linux类似。
NDK全称是Native Development Kit, 是Google官方的Native开发包,支持 C/C++
串口读写相关API如下
Linux C API |
作用 |
open |
打开或创建文件(串口视同文件描述符) |
cfsetospeed |
设置输出波特率 |
cfsetispeed |
设置输入波特率 |
tcgetattr |
获取与终端相关的参数 |
cfmakeraw |
制作新的终端控制属性 |
tcsetattr |
设置终端参数 |
read |
读文件 |
write |
写文件 |
close |
关闭文件描述符 |
表4 Linux下串口操作API
方案2:如果一定要用Java来读写串口,具体做法是通过C的open函数获取到串口的文件描述符,间接地得到串口的句柄,然后传给Java层处理。
Java没有FileDescriptor 的公开的构造方法,需要在C中通过反射的方式构造。
// JNI public native static FileDescriptor openSerialPort(String path, int baudrate, int flags); jobject JNICALL Java_com_hehe_netserial_SerialPort_openSerialPort (JNIEnv * env, jclass thiz, jstring jstrPath, jint baudrate, jint flags) { int fd; speed_t speed; jobject mFileDescriptor; // 前面和C一样打开串口设备, 之后用JNI调用了 FileDescriptor 的构造方法 /* Create a corresponding file descriptor */ { // SDK公开方法中找不到这个构造函数... jclass cFileDescriptor = env->FindClass("java/io/FileDescriptor"); jmethodID iFileDescriptor = env->GetMethodID(cFileDescriptor, "<init>", "()V"); jfieldID descriptorID = env->GetFieldID(cFileDescriptor, "descriptor", "I"); mFileDescriptor = env->NewObject(cFileDescriptor, iFileDescriptor); env->SetIntField(mFileDescriptor, descriptorID, (jint)fd); } return mFileDescriptor; } |
Java 代码中可以用Stream来读写串口了
mFd = openSerialPort(device.getAbsolutePath(), baudrate, flags); if (mFd == null) { Log.e(TAG, "native open returns null"); throw new IOException(); } mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); |
2. 存在的问题和解决办法
2.1. WiFi Agent 的可行性还未验证
WiFi Agent主要依赖于LCM库实现数据的实时分发,虽然有LCM在Android上运行的例子,但是还未有用在Location上实际的DEMO。
解决方法:继续对WiFiAgent 实作DEMO,验证可行性
2.2. MockLocation 在Android 6.0的兼容性
Android 6.0对App的权限管理作出了调整,需要在【开发者选项】中设置进行MockLocation的App,即使通用架构设计中的GPSAgent App。
作出以上设置后,还需要判断是否启用模拟位置,判断方法也与Android之前的版本不同,
// Android 6.0 以下:是否开启【允许模拟位置】 boolean bMockPosition = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0 |
Android 6.0 不再使用Settings.Secure.ALLOW_MOCK_LOCATION[4],导致很多以前的位置模拟工具在Android6.0上无法运行,今后的项目需要为此作出调整。
3. 下一步主要研究任务
3.1. 实现WiFi Agent
WiFi Agent 的重要性不如 Bluetooth GPS Agent 和 DGPS Agent, 现阶段也未有实质的应用场景,所以其实现放在了最后,但是这是唯一一个可以向多个用户广播的系统,从万物互联的未来远景来看,和现有的LCM已经可用的情况下,这个方案也是值得研究的。
3.2. 做后续的对比评测及分析
现有的3种Agent中,Bluetooth GPS Agent 已经实用,使用的蓝牙GPS型号是
Holux RCV-3000 Bluetooth[5] .
图7 Holux RCV-3000 Bluetooth GPS
RCV-3000 的机型采用联发科技 (MTK) GPS 解决方案-MT3329 的低耗能设计
主要功能:
1) 内建 MTK MT3329 低耗能 GPS 芯片组。
2) 66个平行卫星搜寻频道以快速取得并重新取得讯号。
3) 优异敏感度高至 -165 dBm
4) 内建 WAAS/ EGNOS 解调器,无需另外装设硬件。
5) 与蓝牙串行埠规范 (SPP) 兼容。
6) 低耗电量。内建可更换的充电式锂电池,充电后可持续运作最长可达 28 小时。
7) 可选配 USB 端子连接线,可连接无蓝牙装置系统。
8) 支持 NMEA0183 V 3.01 数据协议。
通过这个蓝牙GPS和本项目的Bluetooth GPS Agent配合,已经成功地使Google Map,Baidu Map精确定位,尤其是在和手机自带的GPS定位缓慢时,蓝牙GPS定位快速尤其明显。
DGPS Agent 由于笔者未参与实车走行,没有获取第一手的测试结果。所以只能以看起始点的位置作为测试结果。
3.3. 形成文字的结论
本文主要是总结近年来Android和GPS结合的应用方法,现在技术飞速发展,希望这些方法能够对以后的位置信息应用起到参考作用。
参考文献
[1] 邓凡平. 深入理解Android:WiFi模块 NFC和GPS卷[M].
机械工业出版社, 2014:826-828.
[2] Google. Develop > API Guides > Connectivity-Bluetooth[EB/OL].
https://developer.android.com/guide/topics/connectivity/bluetooth.html, 2016.
[3] Google. android.location.Location [EB/OL].
https://developer.android.com/reference/android/location/Location.html, 2016.
[4] doris_d.Android 使用模拟位置(支持Android6.0)[EB/OL].
http://blog.csdn.net/doris_d/article/details/51384285 , 2016.
[5]Holux. Products > GPS Receiver/DataLogger > Wireless GPS Receiver / Data Logger > RCV-3000[EB/OL].http://www.holux.com/JCore/en/products/products_spec.jsp?pno=440, 2016.
Android和蓝牙GPS结合的方法相关推荐
- Android通过手机GPS获取经纬度方法
android 调用gps获取经纬度的方法大同小异,实则差不了多少.但是使用起来,有的方法看起来很冗杂,而且很不好用.下面为大家介绍中很简单的方法,而且是实时监听位置的变化. 首先定义: privat ...
- Android打开蓝牙的两种方法
1.隐式打开蓝牙 /*隐式打开蓝牙*/if (!mBluetoothAdapter.isEnabled()) {mBluetoothAdapter.enable();} 2.弹出对话框供用户选择是否打 ...
- Android Studio 基础 之 获取蓝牙Bluetooth 的状态,设置的蓝牙Bluetooth 的开关状态,并监听蓝牙Bluetooth 的状态变化方法整理
Android Studio 基础 之 获取蓝牙Bluetooth 的状态,设置的蓝牙Bluetooth 的开关状态,并监听蓝牙Bluetooth 的状态变化方法整理 目录 Android Studi ...
- android蓝牙配对 自动联接,Android系统下蓝牙自动配对连接方法
Android系统下蓝牙自动配对连接方法 [专利摘要]本发明涉及一种Android系统下蓝牙自动配对连接方法,其包括如下步骤:步骤1.在Android设备端内存储上次进行蓝牙连接蓝牙外设的蓝牙地址,并 ...
- android 定位蓝牙,蓝牙如何定位,简易蓝牙定位系统的实现方法
蓝牙如何定位,简易蓝牙定位系统的实现方法 1.准备设备 所需硬件设备: (1)低功率蓝牙定位器若干(如:10个),网上有卖(单价从几十到几百都有) (2)android设备一台,系统版本4.2以上(S ...
- Android BLE蓝牙踩坑总结
简介 自从Android-BLE库开源了一段时间以来,越来越多的小伙伴问到了各种各样的关于BLE的奇怪问题,在这里我想跟大家分享一下本人对于Android BLE蓝牙的一些看法和解决方式,避免刚接触的 ...
- Android Ble蓝牙开发
BLE Android 应用 开发 1.权限设置 2.获取蓝牙设备管理器 3.设备搜索 3.1 停止搜索 4.设备连接 5.设备的重连 6.设备的断开与服务关闭 7.通知的注册与接收 8.数据的主动读 ...
- Android BLE蓝牙开发知识总结
Android BLE蓝牙开发知识总结 1.蓝牙介绍 1.1什么是蓝牙? 蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据交换(使用2 ...
- Android 低功耗蓝牙开发
初识低功耗蓝牙 Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的 API, 应用程序通过这些 API 扫 ...
最新文章
- vc 添加打开文件对话框并读取文件
- Linux怎么处理binray文件,Linux下如何反汇编arm raw binary文件
- 《集体智慧编程》笔记(2 / 12):提供推荐
- 新手进阶:巧用 macOS 帮助菜单?
- iBATIS的自定义类型处理器TypeHandlerCallback解决乱码
- EasyUI DataGrid 实用例子(2015-05-22)
- #美化ggplot2生成的原始图片至清爽的感觉
- 使用EasyExcel上传下载excel
- I3D【Inflated 3D ConvNet】——膨胀卷积网络用于行为识别
- QSV格式转换MP4应该使用哪个视频格式转换器
- SQL SERVER 实用教程(第四版) 实验 1-10 非标准答案
- Visual Studio 2017 智能提示英文怎么切换成中文?
- 配置无状态IPv6地址自动配置基础实验
- 这是昨天的内容,就这样慢慢整吧,然后,荒废了好多时间啊!!
- 9+11个无版权、高清、免费图片素材网站给你!免费无版权可商用图标、图片素材,需要图片的时候可以上去看看
- 备战2020年大学生电子设计大赛
- 苹果承认iOS源代码泄露,对iOS 11.2.5的有没有影响
- 【2023软考】信息系统监理师与系统集成项目管理工程师哪个更好考?
- C#练习题答案: 英雄的根【难度:1级】--景越C#经典编程题库,1000道C#基础练习题等你来挑战
- 魅蓝note6救砖_魅蓝Note6线刷刷机教程 魅蓝Note6线刷包救砖刷机包下载