获取安卓设备的GNSS测量数据方法
2016 年 5 月,Google 在 I/O 开发者会议上宣布,将为 Android Nougat 操作系统中的应用程序提供原始 GNSS 观测数据。
API参考(Google中文官网)DEMO参考(GNSSLogger)
1. 用到的类
类 | 注释 |
---|---|
LocationManager | 用于注册GNSSmeasurement |
GnssMeasurementsEvent.Callback | 用于从GNSS引擎接收GNSS卫星测量值 |
GnssMeasurementsEvent | 一个包含测量值数据的容器 |
GnssClock | 包含GPS时钟时间戳记的类 |
Gnssmeasurement | 代表GNSS卫星测量的类,其中包含原始信息和计算信息 |
2.设置权限
必须先在Manifest文件中设置相应的权限,否则将无法获取测量的回调数据
<!-- 所需权限-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.WRITE_GSERVICES"/>
<!-- 所需权限-->
3.注册GNSSmeasurement
private LocationManager mLocationManager;
private GnssMeasurementsEvent.Callback gnssMeasurementEventListener =
new GnssMeasurementsEvent.Callback()
//这里先声明,重写方法将在下一步添加
public void RegisterMeasurements(){
@SuppressLint("MissingPermission")
boolean is_register_success=
mLocationManager.registerGnssMeasurementsCallback(gnssMeasurementEventListener);
//注册GNSSmeasurement回调监听,返回是否注册成功的信息
//测量信息将在GnssMeasurementsEvent.Callback中接收
}
//顺便把注销的方法也写了
public void unRegisterMeasurements(){
mLocationManager.unregisterGnssMeasurementsCallback(gnssMeasurementEventListener);
}
4.重写GnssMeasurementsEvent.Callback里的两个方法
private GnssMeasurementsEvent.Callback gnssMeasurementEventListener =
new GnssMeasurementsEvent.Callback() {
@Override
public void onGnssMeasurementsReceived(GnssMeasurementsEvent eventArgs) {
super.onGnssMeasurementsReceived(eventArgs);
//这里我们获取到了回调的测量数据容器:GnssMeasurementsEvent eventArgs
//TODO:
}
@Override
public void onStatusChanged(int status) {
super.onStatusChanged(status);
}
};
5.将测量数据转换为字符串
- 把GnssMeasurementsEvent拆分为GnssClock和Gnssmeasurement
//写一个接收测量信息的方法
public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
StringBuilder builder=new StringBuilder("GNSS测量数据:\n\n");
//这里的toStringClock和toStringMeasurement将写在下一步里
builder.append(toStringClock(event.getClock()));//写入gnss时钟的数据
builder.append("\n");
for (GnssMeasurement measurement : event.getMeasurements()) {
builder.append(toStringMeasurement(measurement));//写入gnss测量数据
builder.append("\n");
}
//这里可以写runOnUiThread(...),将builder打印在屏幕上
//也可以修改返回值
}
- 分别把GnssClock和Gnssmeasurement转化为字符串
private String toStringClock(GnssClock gnssClock){
//将GPS接收器时钟的值转换为字符串
final String format = " %-4s = %s\n";//定义数据显示格式,“%-4”表示左对齐、不足四位补足四位
StringBuilder builder=new StringBuilder("GNSS时钟:\n");
DecimalFormat numberFormat = new DecimalFormat("#0.000");//定义格式化数字
if (gnssClock.hasLeapSecond()) {
//如果闰秒存在则显示闰秒
builder.append(String.format(format, "闰秒(LeapSecond)", gnssClock.getLeapSecond()));
}
builder.append(String.format(format, "硬件时钟(TimeNanos)", gnssClock.getTimeNanos()));//获取以毫秒为单位的GNSS接收器内部硬件时钟值
if (gnssClock.hasTimeUncertaintyNanos()) {
//获取硬件时钟的误差估计(不确定度)
builder.append(String.format(format, "时钟误差估计(TimeUncertaintyNanos)", gnssClock.getTimeUncertaintyNanos()));
}
if (gnssClock.hasFullBiasNanos()) {
//如果存在接收机本地时钟总偏差,则显示
builder.append(String.format(format, "总时钟偏差(FullBiasNanos)", gnssClock.getFullBiasNanos()));
}
if (gnssClock.hasBiasNanos()) {
//亚纳秒偏差
builder.append(String.format(format, "亚偏差(BiasNanos)", gnssClock.getBiasNanos()));
}
if (gnssClock.hasBiasUncertaintyNanos()) {
//FullBiasNanos和BiasNanos的误差估计
builder.append(String.format(format, "时钟偏差估计(BiasUncertaintyNanos)", numberFormat.format(gnssClock.getBiasUncertaintyNanos())));
}
/**
* 注意:以上五个数据用于计算GPS时钟
* 具体计算方法为:local estimate of GPS time = TimeNanos - (FullBiasNanos + BiasNanos)
* 世界标准时:UtcTimeNanos = TimeNanos - (FullBiasNanos + BiasNanos) - LeapSecond * 1,000,000,000
*/
if (gnssClock.hasDriftNanosPerSecond()) {
//以每秒纳秒为单位获取时钟的漂移
builder.append(String.format(format, "时钟漂移(DriftNanosPerSecond)", numberFormat.format(gnssClock.getDriftNanosPerSecond())));
}
if (gnssClock.hasDriftUncertaintyNanosPerSecond()) {
//时钟偏差的估计
builder.append(String.format(format, "时钟漂移估计(DriftUncertaintyNanosPerSecond)", numberFormat.format(gnssClock.getDriftUncertaintyNanosPerSecond())));
}
//获取硬件时钟不连续的计数,即:每当gnssclock中断时,该值+1
builder.append(String.format(format, "中断计数(HardwareClockDiscontinuityCount)", gnssClock.getHardwareClockDiscontinuityCount()));
return builder.toString();
}
private String toStringMeasurement(GnssMeasurement measurement){
//将GNSS测量结果转换为字符串
//定义显示格式
final String format = " %-4s = %s\n";
StringBuilder builder = new StringBuilder("GNSS测量结果:\n");
DecimalFormat numberFormat = new DecimalFormat("#0.000");
DecimalFormat numberFormat1 = new DecimalFormat("#0.000E00");
//获取卫星ID
/**
* 取决于卫星类型
* GPS:1-32
* SBAS:120-151、183-192
* GLONASS:OSN或FCN + 100之一
* 1-24作为轨道槽号(OSN)(首选,如果知道)
* 93-106作为频道号(FCN)(-7至+6)加100。即将-7的FCN编码为93,0编码为100,+ 6编码为106
* QZSS:193-200
* 伽利略:1-36
* 北斗:1-37
*/
builder.append(String.format(format, "卫星ID", measurement.getSvid()));
//获取卫星类型
/**
* 1:CONSTELLATION_GPS 使用GPS定位
* 2:CONSTELLATION_SBAS 使用SBAS定位
* 3:CONSTELLATION_GLONASS 使用格洛纳斯定位
* 4:CONSTELLATION_QZSS 使用QZSS定位
* 5:CONSTELLATION_BEIDOU 使用北斗定位 (^-^)!
* 6:CONSTELLATION_GALILEO 使用伽利略定位
* 7:CONSTELLATION_IRNSS 使用印度区域卫星定位
*/
builder.append(String.format(format, "卫星类型", measurement.getConstellationType()));
//获取进行测量的时间偏移量(以纳秒为单位)
builder.append(String.format(format, "测量时间偏移量", measurement.getTimeOffsetNanos()));
//获取每个卫星的同步状态
//具体数值含义请查表
builder.append(String.format(format, "同步状态", measurement.getState()));
//获取时间戳的伪距速率,以m/s为单位
builder.append(
String.format(
format,
"伪距速率",
numberFormat.format(measurement.getPseudorangeRateMetersPerSecond())));
//获取伪距的速率不确定性(1-Sigma),以m/s为单位
builder.append(
String.format(
format,
"伪距速率不确定度",
numberFormat.format(measurement.getPseudorangeRateUncertaintyMetersPerSecond())));
//
if (measurement.getAccumulatedDeltaRangeState() != 0) {
// 获取“累积增量范围”状态
// 返回:MULTIPATH_INDICATOR_UNKNOWN(指示器不可用)=0
// notice 即:指示器可用时,收集数据
builder.append(
String.format(
format, "累积增量范围状态", measurement.getAccumulatedDeltaRangeState()));
//获取自上次重置通道以来的累积增量范围,以米为单位.
//该值仅在上面的state值为“可用”时有效
//notice 累积增量范围= -k * 载波相位(其中k为常数)
builder.append(
String.format(
format,
"累积增量范围",
numberFormat.format(measurement.getAccumulatedDeltaRangeMeters())));
//获取以米为单位的累积增量范围的不确定性(1-Sigma)
builder.append(
String.format(
format,
"累积增量范围不确定度",
numberFormat1.format(measurement.getAccumulatedDeltaRangeUncertaintyMeters())));
}
if (measurement.hasCarrierFrequencyHz()) {
//获取被跟踪信号的载波频率
builder.append(
String.format(format, "信号载波频率", measurement.getCarrierFrequencyHz()));
}
if (measurement.hasCarrierCycles()) {
//卫星和接收器之间的完整载波周期数
builder.append(String.format(format, "载波周期数", measurement.getCarrierCycles()));
}
if (measurement.hasCarrierPhase()) {
//获取接收器检测到的RF相位
builder.append(String.format(format, "RF相位", measurement.getCarrierPhase()));
}
if (measurement.hasCarrierPhaseUncertainty()) {
//误差估计
builder.append(
String.format(
format, "RF相位不确定度", measurement.getCarrierPhaseUncertainty()));
}
//获取一个值,该值指示事件的“多路径”状态,返回0或1或2
//MULTIPATH_INDICATOR_DETECTED = 1 测量显示有“多路径效应”迹象
// MULTIPATH_INDICATOR_NOT_DETECTED = 2 测量结果显示没有“多路径效应”迹象
builder.append(String.format(format, "多路经效应指示器", measurement.getMultipathIndicator()));
//
if (measurement.hasSnrInDb()) {
//获取信噪比(SNR),以dB为单位
builder.append(String.format(format, "信噪比", measurement.getSnrInDb()));
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
if (measurement.hasAutomaticGainControlLevelDb()) {
//获取以dB为单位的自动增益控制级别
builder.append(String.format(format, "自动增益控制级别", measurement.getAutomaticGainControlLevelDb()));
}
if (measurement.hasCarrierFrequencyHz()) {
builder.append(String.format(format, "载波频率", measurement.getCarrierFrequencyHz()));
}
}
return builder.toString();
}
最后将这些方法整合起来即可,UI显示的部分就不再赘述了。
关于伪距如何计算见下一篇博客:通过GNSS原始数据计算伪距