概述
最近在做室内外无缝定位的相关demo,室外定位中,GNSS定位方法具有精度高、成本低等优点,而且Android平台也提供了原生API用于定位,可以很方便地进行功能实现。GNSS定位方法理论上最少需要4颗卫星就能获取位置信息,因此,可以基于有效卫星数判断选择使用室内定位方法还是GNSS方法(即基于卫星数实现无缝切换功能)。
本文记录了使用Android原生API获取位置信息的方法、获取接收到的卫星信号个数的方法以及获取参与定位的卫星信号个数的方法。
权限申请及开启GPS
首先需要在AndroidManifest文件里面进行权限申请,代码如下:
<!-- rough location permission -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- fine location permission -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
从Android6.0开始,Google将权限分为正常权限与危险权限,危险权限需要在运行时再次向用户申请。位置信息属于危险权限,因此,需要在Activity里面对权限再次进行申请,具体代码如下:
// get location permission again
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 0);
return;
}
权限申请完成后,
注册一个LocationManager。LocationManager是Android官方提供的用于管理位置信息的一个类,该类提供对系统位置服务的访问。这些服务允许应用程序获得设备的地理位置的周期性更新,或者当设备接近一个给定的地理位置时得到通知。具体可以看官方文档。代码如下:
LocationManager mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
同时,确保移动设备开启GPS,方法代码如下:
void insureGPSisOpen(LocationManager locationManager) {
// judge whether the GPS is open
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(getApplicationContext(), "请开启GPS导航...", Toast.LENGTH_SHORT).show();
// trun to the GPS setting page
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(intent, 0);
return;
}
Log.d(TAG, "insureGPSisOpen");
return;
}
之后,在onCreate()方法里调用上述子方法。
调用原生API进行定位
requestLocationUpdates(provider: String, minTimeMs: Long, minDistanceM: Float, listener: LocationListener)是LocationManager类的一个方法,该方法能够从给定的Provider中获取位置信息,并在位置更新时调用LocationListener(上述方法的第四个参数)的onLocationChanged()方法。LocationListener用于接收通知,当LocationListener已经注册到LocationManager且设备位置发生变化时,将调用LocationListener里的相关方法。
首先编写一个LocationListener的子类并重写其中的onLocationChanged()方法,具体代码如下:
LocationListener locationListener = new LocationListener() {
@Override
// callback func when the location changed
public void onLocationChanged(@NonNull Location location) {
mTvLatitude.setText("纬度:" + location.getLatitude());
mTvLongitude.setText("经度:" + location.getLongitude());
// change the flag, to record cur status
Log.d(TAG, "onLocationChanged");
}
};
然后将自定义的LocationListener注册到LocationManager中,具体代码如下:
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000, 0,locationListener);
通过上述步骤就能获取位置信息。
获取设备收到的卫星信号个数
GnssStatus.Callback是用于接收GNSS事件发生时的通知的类,而registerGnssStatusCallback()方法用于将自定义的GnssStatus.Callback类注册到LocationManager类中。
首先自定义一个内部类继承自GnssStatus.Callback,并重写其中的onSatelliteStatusChanged()方法,具体代码如下:
@RequiresApi(api = Build.VERSION_CODES.N)
GnssStatus.Callback mGNSSCallback = new GnssStatus.Callback() {
@Override
public void onSatelliteStatusChanged(@NonNull GnssStatus status) {
super.onSatelliteStatusChanged(status);
// get satellite count
satelliteCount = status.getSatelliteCount();
mTvSatelliteCount.setText("共收到卫星信号:" + satelliteCount + "个");
}
};
然后将mGNSSCallback注册到LocationManager中,具体代码如下:
mLocationManager.registerGnssStatusCallback(locaCallback);
通过上述步骤就能实现设备收到的卫星信号个数的统计。
注意,因为现在的GNSS系统包括GPS、GLONASS、BEIDOU等,Android官方提供的API也可以判断卫星所属的GNSS系统,通过调用status.getConstellationType(i)方法实现。以实现北斗卫星统计为例,可以在onSatelliteStatusChanged()方法里添加如下代码:
if(satelliteCount > 0) {
for (int i = 0; i < satelliteCount; i++) {
// get satellite type
int type = status.getConstellationType(i);
if(GnssStatus.CONSTELLATION_BEIDOU == type) {
// increase if type == BEIDOU
BDSatelliteCount++;
}
}
Log.d(TAG, "BDS count:" + BDSatelliteCount);
}
获取用于定位的卫星信号个数
Android不一定将所有收到的卫星信号都用于定位算法中,会基于高度角、信噪比等剔除可信度不高的卫星信号。
Android官方提供封装方法status.usedInFix(i),该方法返回boolean值,可以直接用于判断该卫星信号是否用于定位,如增加判断语句:
if (status.usedInFix(i)) {
BDSInFix++;
}