feat: Enhance logging capabilities (#568)

Enhance logging in GPSTest

TODO:
- [x] Remove precision cap on decimal values (don't use `String.format()` - https://github.com/barbeau/gpstest/issues/553)
    - [x] Location
    - [x] Raw measurements
    - [x] NMEA
    - [x] Nav messages
    - [x] AntennaInfo
- [x] Add new GnssLogger info (described at https://www.kaggle.com/c/google-smartphone-decimeter-challenge/data# and https://github.com/barbeau/gpstest/issues/556)
    - [x] Location
    - [x] Raw measurements
    - [x] NMEA
    - [x] Nav messages
    - [x] GnssStatus
    - [x] Orientation
    - [x] GnssStatus - Settings UI
    - [x] Orientation - Settings UI
    - [x] Inject time on log - Settings UI
    - [x] Inject PSDS on log - Settings UI
- [x] Add location vertical accuracy (#557)
- [x] Archive logging README for v3 and older format
- [x] Update logging README with new v4 format
- [x] Update log header with new fields
    - [x] Location
    - [x] Raw measurements
    - [x] NMEA
    - [x] Nav messages
    - [x] GnssStatus
    - [x] Orientation

Closes https://github.com/barbeau/gpstest/issues/557
Closes https://github.com/barbeau/gpstest/issues/556
Closes https://github.com/barbeau/gpstest/issues/553
This commit is contained in:
Sean Barbeau
2021-11-18 18:30:01 -05:00
committed by GitHub
parent ab4a4ac9ce
commit c1ffd27b53
28 changed files with 1108 additions and 481 deletions

View File

@@ -86,7 +86,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
qzssL1.hasCarrierFrequency = true
qzssL1.carrierFrequencyHz = 1575420000.0f
qzssL1.carrierFrequencyHz = 1575420000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(qzssL1)
assertEquals("L1", label)
@@ -101,7 +101,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
qzssL2.hasCarrierFrequency = true
qzssL2.carrierFrequencyHz = 1227600000.0f
qzssL2.carrierFrequencyHz = 1227600000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(qzssL2)
assertEquals("L2", label)
@@ -116,7 +116,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
qzssL5.hasCarrierFrequency = true
qzssL5.carrierFrequencyHz = 1176450000.0f
qzssL5.carrierFrequencyHz = 1176450000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(qzssL5)
assertEquals("L5", label)
@@ -131,7 +131,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
qzssL6.hasCarrierFrequency = true
qzssL6.carrierFrequencyHz = 1278750000.0f
qzssL6.carrierFrequencyHz = 1278750000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(qzssL6)
assertEquals("L6", label)
@@ -160,7 +160,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
beidouB1.hasCarrierFrequency = true
beidouB1.carrierFrequencyHz = 1561098000.0f
beidouB1.carrierFrequencyHz = 1561098000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB1)
assertEquals("B1", label)
@@ -175,7 +175,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
beidouB1_2.hasCarrierFrequency = true
beidouB1_2.carrierFrequencyHz = 1589742000.0f
beidouB1_2.carrierFrequencyHz = 1589742000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB1_2)
assertEquals("B1-2", label)
@@ -190,7 +190,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
beidouB1c.hasCarrierFrequency = true
beidouB1c.carrierFrequencyHz = 1575420000.0f
beidouB1c.carrierFrequencyHz = 1575420000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB1c)
assertEquals("B1C", label)
@@ -205,7 +205,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
beidouB1c202.hasCarrierFrequency = true
beidouB1c202.carrierFrequencyHz = 1575450000.0f
beidouB1c202.carrierFrequencyHz = 1575450000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB1c202)
assertEquals("B1C", label)
@@ -220,7 +220,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
beidouB2.hasCarrierFrequency = true
beidouB2.carrierFrequencyHz = 1207140000.0f
beidouB2.carrierFrequencyHz = 1207140000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB2)
assertEquals("B2", label)
@@ -235,7 +235,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
beidouB2a.hasCarrierFrequency = true
beidouB2a.carrierFrequencyHz = 1176450000.0f
beidouB2a.carrierFrequencyHz = 1176450000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB2a)
assertEquals("B2a", label)
@@ -250,7 +250,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
beidouB3.hasCarrierFrequency = true
beidouB3.carrierFrequencyHz = 1268520000.0f
beidouB3.carrierFrequencyHz = 1268520000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(beidouB3)
assertEquals("B3", label)
@@ -267,7 +267,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
irnssL5.hasCarrierFrequency = true
irnssL5.carrierFrequencyHz = 1176450000.0f
irnssL5.carrierFrequencyHz = 1176450000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(irnssL5)
assertEquals("L5", label)
@@ -282,7 +282,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
irnssS.hasCarrierFrequency = true
irnssS.carrierFrequencyHz = 2492028000.0f
irnssS.carrierFrequencyHz = 2492028000.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(irnssS)
assertEquals("S", label)
@@ -297,7 +297,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
gagan.hasCarrierFrequency = true
gagan.carrierFrequencyHz = 1575420000.0f
gagan.carrierFrequencyHz = 1575420000.0
gagan.sbasType = SbasType.GAGAN
label = CarrierFreqUtils.getCarrierFrequencyLabel(gagan)
@@ -313,7 +313,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos120.hasCarrierFrequency = true
egnos120.carrierFrequencyHz = 1575420000.0f
egnos120.carrierFrequencyHz = 1575420000.0
egnos120.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos120)
@@ -329,7 +329,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos123.hasCarrierFrequency = true
egnos123.carrierFrequencyHz = 1575420000.0f
egnos123.carrierFrequencyHz = 1575420000.0
egnos123.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos123)
@@ -345,7 +345,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos126.hasCarrierFrequency = true
egnos126.carrierFrequencyHz = 1575420000.0f
egnos126.carrierFrequencyHz = 1575420000.0
egnos126.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos126)
@@ -361,7 +361,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos136.hasCarrierFrequency = true
egnos136.carrierFrequencyHz = 1575420000.0f
egnos136.carrierFrequencyHz = 1575420000.0
egnos136.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos136)
@@ -377,7 +377,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos120L5.hasCarrierFrequency = true
egnos120L5.carrierFrequencyHz = 1176450000.0f
egnos120L5.carrierFrequencyHz = 1176450000.0
egnos120L5.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos120L5)
@@ -393,7 +393,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos123L5.hasCarrierFrequency = true
egnos123L5.carrierFrequencyHz = 1176450000.0f
egnos123L5.carrierFrequencyHz = 1176450000.0
egnos123L5.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos123L5)
@@ -409,7 +409,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos126L5.hasCarrierFrequency = true
egnos126L5.carrierFrequencyHz = 1176450000.0f
egnos126L5.carrierFrequencyHz = 1176450000.0
egnos126L5.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos126L5)
@@ -425,7 +425,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos136L5.hasCarrierFrequency = true
egnos136L5.carrierFrequencyHz = 1176450000.0f
egnos136L5.carrierFrequencyHz = 1176450000.0
egnos136L5.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos136L5)
@@ -443,7 +443,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos133L1.hasCarrierFrequency = true
egnos133L1.carrierFrequencyHz = 1575420000.0f
egnos133L1.carrierFrequencyHz = 1575420000.0
egnos133L1.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos133L1)
@@ -459,7 +459,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
egnos133L5.hasCarrierFrequency = true
egnos133L5.carrierFrequencyHz = 1176450000.0f
egnos133L5.carrierFrequencyHz = 1176450000.0
egnos133L5.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(egnos133L5)
@@ -493,7 +493,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
anik15_138L1.hasCarrierFrequency = true
anik15_138L1.carrierFrequencyHz = 1575420000.0f
anik15_138L1.carrierFrequencyHz = 1575420000.0
anik15_138L1.sbasType = SbasType.WAAS
label = CarrierFreqUtils.getCarrierFrequencyLabel(anik15_138L1)
@@ -509,7 +509,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
anik15_138L5.hasCarrierFrequency = true
anik15_138L5.carrierFrequencyHz = 1176450000.0f
anik15_138L5.carrierFrequencyHz = 1176450000.0
anik15_138L5.sbasType = SbasType.WAAS
label = CarrierFreqUtils.getCarrierFrequencyLabel(anik15_138L5)
@@ -525,7 +525,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
ses5_136L1.hasCarrierFrequency = true
ses5_136L1.carrierFrequencyHz = 1575420000.0f
ses5_136L1.carrierFrequencyHz = 1575420000.0
ses5_136L1.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(ses5_136L1)
@@ -541,7 +541,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
ses5_136L5.hasCarrierFrequency = true
ses5_136L5.carrierFrequencyHz = 1176450000.0f
ses5_136L5.carrierFrequencyHz = 1176450000.0
ses5_136L5.sbasType = SbasType.EGNOS
label = CarrierFreqUtils.getCarrierFrequencyLabel(ses5_136L5)
@@ -559,7 +559,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
msas129L1.hasCarrierFrequency = true
msas129L1.carrierFrequencyHz = 1575420000.0f
msas129L1.carrierFrequencyHz = 1575420000.0
msas129L1.sbasType = SbasType.MSAS
label = CarrierFreqUtils.getCarrierFrequencyLabel(msas129L1)
@@ -575,7 +575,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
msas129L5.hasCarrierFrequency = true
msas129L5.carrierFrequencyHz = 1176450000.0f
msas129L5.carrierFrequencyHz = 1176450000.0
msas129L5.sbasType = SbasType.MSAS
label = CarrierFreqUtils.getCarrierFrequencyLabel(msas129L5)
@@ -591,7 +591,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
msas137L1.hasCarrierFrequency = true
msas137L1.carrierFrequencyHz = 1575420000.0f
msas137L1.carrierFrequencyHz = 1575420000.0
msas137L1.sbasType = SbasType.MSAS
label = CarrierFreqUtils.getCarrierFrequencyLabel(msas137L1)
@@ -607,7 +607,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
msas137L5.hasCarrierFrequency = true
msas137L5.carrierFrequencyHz = 1176450000.0f
msas137L5.carrierFrequencyHz = 1176450000.0
msas137L5.sbasType = SbasType.MSAS
label = CarrierFreqUtils.getCarrierFrequencyLabel(msas137L5)
@@ -623,7 +623,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
gpsL1variation.hasCarrierFrequency = true
gpsL1variation.carrierFrequencyHz = 1575420000.0000000f
gpsL1variation.carrierFrequencyHz = 1575420000.0000000
label = CarrierFreqUtils.getCarrierFrequencyLabel(gpsL1variation)
assertEquals("L1", label)
@@ -651,7 +651,7 @@ class CarrierFreqUtilsTest {
72f,
25f);
gpsL1badCf.hasCarrierFrequency = true
gpsL1badCf.carrierFrequencyHz = 12345.0f
gpsL1badCf.carrierFrequencyHz = 12345.0
label = CarrierFreqUtils.getCarrierFrequencyLabel(gpsL1badCf)
assertEquals(CF_UNKNOWN, label)

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2021 Sean J. Barbeau
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gpstest
import android.location.GnssAntennaInfo
import android.location.Location
import android.os.Build
import androidx.test.filters.SdkSuppress
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.android.gpstest.model.GnssType
import com.android.gpstest.model.Orientation
import com.android.gpstest.model.SatelliteStatus
import com.android.gpstest.util.FormatUtils.toLog
import com.android.gpstest.util.IOUtils
import com.android.gpstest.util.SatelliteUtil.isBearingAccuracySupported
import com.android.gpstest.util.SatelliteUtil.isSpeedAccuracySupported
import com.android.gpstest.util.SatelliteUtil.isVerticalAccuracySupported
import junit.framework.Assert
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class FormatUtilsTest {
@Test
fun locationToLog() {
val l = Location("test")
l.latitude = 45.34567899
l.longitude = 12.45678901
l.altitude = 56.2
l.speed = 19.2f
l.accuracy = 98.7f
l.bearing = 100.1f
l.time = 12345
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
l.speedAccuracyMetersPerSecond = 382.7f
l.bearingAccuracyDegrees = 284.1f
l.verticalAccuracyMeters = 583.4f
}
l.elapsedRealtimeNanos = 123456789
// Fix,Provider,LatitudeDegrees,LongitudeDegrees,AltitudeMeters,SpeedMps,AccuracyMeters,BearingDegrees,UnixTimeMillis,SpeedAccuracyMps,BearingAccuracyDegrees,elapsedRealtimeNanos,VerticalAccuracyMeters
if (l.isSpeedAccuracySupported() && l.isBearingAccuracySupported() && l.isVerticalAccuracySupported()) {
assertEquals(
"Fix,test,45.34567899,12.45678901,56.2,19.2,98.7,100.1,12345,382.7,284.1,123456789,583.4",
l.toLog()
)
} else {
assertEquals(
"Fix,test,45.34567899,12.45678901,56.2,19.2,98.7,100.1,12345,,,123456789,",
l.toLog()
)
}
}
/**
* Test writing GnssAntennaInfo to CSV format (only runs on Android R or higher)
*/
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
fun testSerializeGnssAntennaInfo() {
val builder = GnssAntennaInfo.Builder()
builder.setCarrierFrequencyMHz(1575.42)
builder.setPhaseCenterOffset(
GnssAntennaInfo.PhaseCenterOffset(
1.2,0.1,
3.4,0.2,
5.6,0.3))
builder.setPhaseCenterVariationCorrections(
GnssAntennaInfo.SphericalCorrections(
buildPhaseCenterVariationCorrectionsArray(),
buildPhaseCenterVariationCorrectionsUncertaintyArray()
)
)
builder.setSignalGainCorrections(
GnssAntennaInfo.SphericalCorrections(
buildSignalGainCorrectionsArray(),
buildSignalGainCorrectionsUncertaintyArray()
)
)
val expected = "GnssAntennaInfo,1575.42,1.2,0.1,3.4,0.2,5.6,0.3," +
"[11.22 33.44 55.66 77.88; 10.2 30.4 50.6 70.8; 12.2 34.4 56.6 78.8]," +
"[0.1 0.2 0.3 0.4; 1.1 1.2 1.3 1.4; 2.1 2.2 2.3 2.4],60.0,120.0," +
"[9.8 8.7 7.6 6.5; 5.4 4.3 3.2 2.1; 1.3 2.4 3.5 4.6]," +
"[0.11 0.22 0.33 0.44; 0.55 0.66 0.77 0.88; 0.91 0.92 0.93 0.94],60.0,120.0"
Assert.assertEquals(expected, builder.build().toLog())
}
/**
* Test writing array of doubles to String (for serializing GnssAntennaInfo)
*/
@Test
fun testSerializeDoubleArray() {
val data = buildPhaseCenterVariationCorrectionsArray()
val expected = "[11.22 33.44 55.66 77.88; 10.2 30.4 50.6 70.8; 12.2 34.4 56.6 78.8]"
Assert.assertEquals(expected, IOUtils.serialize(data))
}
private fun buildPhaseCenterVariationCorrectionsArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(11.22, 33.44, 55.66, 77.88)
val array2: DoubleArray = doubleArrayOf(10.2, 30.4, 50.6, 70.8)
val array3: DoubleArray = doubleArrayOf(12.2, 34.4, 56.6, 78.8)
return arrayOf(array1, array2, array3)
}
private fun buildPhaseCenterVariationCorrectionsUncertaintyArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(0.1, 0.2, 0.3, 0.4)
val array2: DoubleArray = doubleArrayOf(1.1, 1.2, 1.3, 1.4)
val array3: DoubleArray = doubleArrayOf(2.1, 2.2, 2.3, 2.4)
return arrayOf(array1, array2, array3)
}
private fun buildSignalGainCorrectionsArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(9.8, 8.7, 7.6, 6.5)
val array2: DoubleArray = doubleArrayOf(5.4, 4.3, 3.2, 2.1)
val array3: DoubleArray = doubleArrayOf(1.3, 2.4, 3.5, 4.6)
return arrayOf(array1, array2, array3)
}
private fun buildSignalGainCorrectionsUncertaintyArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(0.11, 0.22, 0.33, 0.44)
val array2: DoubleArray = doubleArrayOf(0.55, 0.66, 0.77, 0.88)
val array3: DoubleArray = doubleArrayOf(0.91, 0.92, 0.93, 0.94)
return arrayOf(array1, array2, array3)
}
@Test
fun testSatelliteToLog() {
val location = Location("test")
location.time = 0
val signalCount = 25
val signalIndex = 0
val gpsL1 = SatelliteStatus(
10,
GnssType.NAVSTAR,
35.00f,
hasAlmanac = true,
hasEphemeris = true,
usedInFix = true,
elevationDegrees = 57.00f,
azimuthDegrees = 136.00f
);
gpsL1.hasCarrierFrequency = true
gpsL1.carrierFrequencyHz = 1575420032.0
gpsL1.hasBasebandCn0DbHz = true
gpsL1.basebandCn0DbHz = 30.0f
assertEquals(
"Status,0,25,0,1,10,1575420032,35.0,136.0,57.0,1,1,1,30.0",
gpsL1.toLog(location.time, signalCount, signalIndex)
)
}
@Test
fun testOrientationToLog() {
val currentTime = 1234L
val timeAtBoot = 1000L
assertEquals(
"OrientationDeg,244,10000000,44444.44444,5555.5555,6666.66666",
Orientation(10000000, doubleArrayOf(44444.44444, 5555.5555, 6666.66666)).toLog(
currentTime,
timeAtBoot
)
)
}
}

View File

@@ -16,10 +16,7 @@
package com.android.gpstest
import android.content.Intent
import android.location.GnssAntennaInfo
import android.location.Location
import android.os.Build
import androidx.test.filters.SdkSuppress
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.android.gpstest.util.IOUtils
import junit.framework.Assert.*
@@ -294,77 +291,4 @@ class IOUtilsTest {
val input2 = "[GPS, GLONASS]"
assertEquals("[GPS, GLONASS]", IOUtils.replaceNavstar(input2))
}
/**
* Test writing array of doubles to String (for serializing GnssAntennaInfo)
*/
@Test
fun testSerializeDoubleArray() {
val data = buildPhaseCenterVariationCorrectionsArray()
val expected = "[11.22 33.44 55.66 77.88; 10.2 30.4 50.6 70.8; 12.2 34.4 56.6 78.8]"
assertEquals(expected, IOUtils.serialize(data))
}
/**
* Test writing GnssAntennaInfo to CSV format (only runs on Android R or higher)
*/
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
fun testSerializeGnssAntennaInfo() {
val builder = GnssAntennaInfo.Builder()
builder.setCarrierFrequencyMHz(1575.42)
builder.setPhaseCenterOffset(
GnssAntennaInfo.PhaseCenterOffset(
1.2,0.1,
3.4,0.2,
5.6,0.3))
builder.setPhaseCenterVariationCorrections(
GnssAntennaInfo.SphericalCorrections(
buildPhaseCenterVariationCorrectionsArray(),
buildPhaseCenterVariationCorrectionsUncertaintyArray()
)
)
builder.setSignalGainCorrections(
GnssAntennaInfo.SphericalCorrections(
buildSignalGainCorrectionsArray(),
buildSignalGainCorrectionsUncertaintyArray()
)
)
val expected = "GnssAntennaInfo,1575.42,1.2,0.1,3.4,0.2,5.6,0.3," +
"[11.22 33.44 55.66 77.88; 10.2 30.4 50.6 70.8; 12.2 34.4 56.6 78.8]," +
"[0.1 0.2 0.3 0.4; 1.1 1.2 1.3 1.4; 2.1 2.2 2.3 2.4],60.0,120.0," +
"[9.8 8.7 7.6 6.5; 5.4 4.3 3.2 2.1; 1.3 2.4 3.5 4.6]," +
"[0.11 0.22 0.33 0.44; 0.55 0.66 0.77 0.88; 0.91 0.92 0.93 0.94],60.0,120.0"
assertEquals(expected, IOUtils.serialize(builder.build()))
}
private fun buildPhaseCenterVariationCorrectionsArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(11.22, 33.44, 55.66, 77.88)
val array2: DoubleArray = doubleArrayOf(10.2, 30.4, 50.6, 70.8)
val array3: DoubleArray = doubleArrayOf(12.2, 34.4, 56.6, 78.8)
return arrayOf(array1, array2, array3)
}
private fun buildPhaseCenterVariationCorrectionsUncertaintyArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(0.1, 0.2, 0.3, 0.4)
val array2: DoubleArray = doubleArrayOf(1.1, 1.2, 1.3, 1.4)
val array3: DoubleArray = doubleArrayOf(2.1, 2.2, 2.3, 2.4)
return arrayOf(array1, array2, array3)
}
private fun buildSignalGainCorrectionsArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(9.8, 8.7, 7.6, 6.5)
val array2: DoubleArray = doubleArrayOf(5.4, 4.3, 3.2, 2.1)
val array3: DoubleArray = doubleArrayOf(1.3, 2.4, 3.5, 4.6)
return arrayOf(array1, array2, array3)
}
private fun buildSignalGainCorrectionsUncertaintyArray() : Array<DoubleArray> {
val array1: DoubleArray = doubleArrayOf(0.11, 0.22, 0.33, 0.44)
val array2: DoubleArray = doubleArrayOf(0.55, 0.66, 0.77, 0.88)
val array3: DoubleArray = doubleArrayOf(0.91, 0.92, 0.93, 0.94)
return arrayOf(array1, array2, array3)
}
}

View File

@@ -47,7 +47,7 @@ class SatelliteUtilsTest {
72f,
25f);
gpsL1.hasCarrierFrequency = true
gpsL1.carrierFrequencyHz = 1575420000.0f
gpsL1.carrierFrequencyHz = 1575420000.0
val gpsL1key = SatelliteUtils.createGnssSatelliteKey(gpsL1)
assertEquals("1 NAVSTAR", gpsL1key)
@@ -75,7 +75,7 @@ class SatelliteUtilsTest {
72f,
25f);
sbasWaasL1.hasCarrierFrequency = true
sbasWaasL1.carrierFrequencyHz = 1575420000.0f
sbasWaasL1.carrierFrequencyHz = 1575420000.0
sbasWaasL1.sbasType = SbasType.WAAS
val sbasWaasL1key = SatelliteUtils.createGnssSatelliteKey(sbasWaasL1)
@@ -119,7 +119,7 @@ class SatelliteUtilsTest {
72f,
25f);
sbasSdcm125L1WithCf.hasCarrierFrequency = true
sbasSdcm125L1WithCf.carrierFrequencyHz = 1575420000.0f
sbasSdcm125L1WithCf.carrierFrequencyHz = 1575420000.0
sbasSdcm125L1WithCf.sbasType = SbasType.SDCM
val sbasSdcm125L1WithCfkey = SatelliteUtils.createGnssSatelliteKey(sbasSdcm125L1WithCf)
@@ -141,7 +141,7 @@ class SatelliteUtilsTest {
72f,
25f);
gpsL1.hasCarrierFrequency = true
gpsL1.carrierFrequencyHz = 1575420000.0f
gpsL1.carrierFrequencyHz = 1575420000.0
val gpsL1key = SatelliteUtils.createGnssStatusKey(gpsL1)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -173,7 +173,7 @@ class SatelliteUtilsTest {
72f,
25f);
gpsBadCf.hasCarrierFrequency = true
gpsBadCf.carrierFrequencyHz = 9999999.0f
gpsBadCf.carrierFrequencyHz = 9999999.0
val gpsBadCfKey = SatelliteUtils.createGnssStatusKey(gpsBadCf)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

View File

@@ -33,7 +33,7 @@ fun gpsL1(id: Int, usedInFix: Boolean): SatelliteStatus {
72f,
25f);
gpsL1.hasCarrierFrequency = true
gpsL1.carrierFrequencyHz = 1575420000.0f
gpsL1.carrierFrequencyHz = 1575420000.0
return gpsL1
}
@@ -50,7 +50,7 @@ fun gpsL1NoSignal(id: Int): SatelliteStatus {
NO_DATA,
NO_DATA);
gpsL1.hasCarrierFrequency = true
gpsL1.carrierFrequencyHz = 1575420000.0f
gpsL1.carrierFrequencyHz = 1575420000.0
return gpsL1
}
@@ -67,7 +67,7 @@ fun gpsL2(): SatelliteStatus {
72f,
25f);
gpsL2.hasCarrierFrequency = true
gpsL2.carrierFrequencyHz = 1227600000.0f
gpsL2.carrierFrequencyHz = 1227600000.0
return gpsL2
}
@@ -84,7 +84,7 @@ fun gpsL3(): SatelliteStatus {
72f,
25f);
gpsL3.hasCarrierFrequency = true
gpsL3.carrierFrequencyHz = 1381050000.0f
gpsL3.carrierFrequencyHz = 1381050000.0
return gpsL3
}
@@ -101,7 +101,7 @@ fun gpsL4(): SatelliteStatus {
72f,
25f);
gpsL4.hasCarrierFrequency = true
gpsL4.carrierFrequencyHz = 1379913000.0f
gpsL4.carrierFrequencyHz = 1379913000.0
return gpsL4
}
@@ -118,7 +118,7 @@ fun gpsL5(id: Int, usedInFix: Boolean): SatelliteStatus {
72f,
25f);
gpsL5.hasCarrierFrequency = true
gpsL5.carrierFrequencyHz = 1176450000.0f
gpsL5.carrierFrequencyHz = 1176450000.0
return gpsL5
}
@@ -135,7 +135,7 @@ fun glonassL1variant1(): SatelliteStatus {
72f,
25f);
glonassL1variant1.hasCarrierFrequency = true
glonassL1variant1.carrierFrequencyHz = 1598062500.0f
glonassL1variant1.carrierFrequencyHz = 1598062500.0
return glonassL1variant1
}
@@ -152,7 +152,7 @@ fun glonassL1variant2(): SatelliteStatus {
72f,
25f);
glonassL1variant2.hasCarrierFrequency = true
glonassL1variant2.carrierFrequencyHz = 1605375000.0f
glonassL1variant2.carrierFrequencyHz = 1605375000.0
return glonassL1variant2
}
@@ -169,7 +169,7 @@ fun glonassL2variant1(): SatelliteStatus {
72f,
25f);
glonassL2.hasCarrierFrequency = true
glonassL2.carrierFrequencyHz = 1242937500.0f
glonassL2.carrierFrequencyHz = 1242937500.0
return glonassL2
}
@@ -186,7 +186,7 @@ fun glonassL2variant2(): SatelliteStatus {
72f,
25f);
glonassL2variant2.hasCarrierFrequency = true
glonassL2variant2.carrierFrequencyHz = 1248625000.0f
glonassL2variant2.carrierFrequencyHz = 1248625000.0
return glonassL2variant2
}
@@ -203,7 +203,7 @@ fun glonassL3(): SatelliteStatus {
72f,
25f);
glonassL3.hasCarrierFrequency = true
glonassL3.carrierFrequencyHz = 1207140000.0f
glonassL3.carrierFrequencyHz = 1207140000.0
return glonassL3
}
@@ -220,7 +220,7 @@ fun glonassL5(): SatelliteStatus {
72f,
25f);
glonassL5.hasCarrierFrequency = true
glonassL5.carrierFrequencyHz = 1176450000.0f
glonassL5.carrierFrequencyHz = 1176450000.0
return glonassL5
}
@@ -237,7 +237,7 @@ fun glonassL1Cdma(): SatelliteStatus {
72f,
25f);
glonassL1Cdma.hasCarrierFrequency = true
glonassL1Cdma.carrierFrequencyHz = 1575420000.0f
glonassL1Cdma.carrierFrequencyHz = 1575420000.0
return glonassL1Cdma
}
@@ -254,7 +254,7 @@ fun galileoE1(id: Int, usedInFix: Boolean): SatelliteStatus {
72f,
25f);
galileoE1.hasCarrierFrequency = true
galileoE1.carrierFrequencyHz = 1575420000.0f
galileoE1.carrierFrequencyHz = 1575420000.0
return galileoE1
}
@@ -271,7 +271,7 @@ fun galileoE5(id: Int, usedInFix: Boolean): SatelliteStatus {
72f,
25f);
galileoE5.hasCarrierFrequency = true
galileoE5.carrierFrequencyHz = 1191795000.0f
galileoE5.carrierFrequencyHz = 1191795000.0
return galileoE5
}
@@ -288,7 +288,7 @@ fun galileoE5a(id: Int, usedInFix: Boolean): SatelliteStatus {
72f,
25f);
galileoE5a.hasCarrierFrequency = true
galileoE5a.carrierFrequencyHz = 1176450000.0f
galileoE5a.carrierFrequencyHz = 1176450000.0
return galileoE5a
}
@@ -305,7 +305,7 @@ fun galileoE5b(id: Int, usedInFix: Boolean): SatelliteStatus {
72f,
25f);
galileoE5b.hasCarrierFrequency = true
galileoE5b.carrierFrequencyHz = 1207140000.0f
galileoE5b.carrierFrequencyHz = 1207140000.0
return galileoE5b
}
@@ -322,7 +322,7 @@ fun galileoE6(id: Int, usedInFix: Boolean): SatelliteStatus {
72f,
25f);
galileoE6.hasCarrierFrequency = true
galileoE6.carrierFrequencyHz = 1278750000.0f
galileoE6.carrierFrequencyHz = 1278750000.0
return galileoE6
}
@@ -339,7 +339,7 @@ fun waas_131L1(usedInFix: Boolean): SatelliteStatus {
72f,
25f);
waas_131L1.hasCarrierFrequency = true
waas_131L1.carrierFrequencyHz = 1575420000.0f
waas_131L1.carrierFrequencyHz = 1575420000.0
waas_131L1.sbasType = SbasType.WAAS
return waas_131L1
}
@@ -357,7 +357,7 @@ fun waas_131L5(usedInFix: Boolean): SatelliteStatus {
72f,
25f);
waas_131L5.hasCarrierFrequency = true
waas_131L5.carrierFrequencyHz = 1176450000.0f
waas_131L5.carrierFrequencyHz = 1176450000.0
waas_131L5.sbasType = SbasType.WAAS
return waas_131L5
}
@@ -375,7 +375,7 @@ fun waas_133L1(usedInFix: Boolean): SatelliteStatus {
72f,
25f);
waas_133L1.hasCarrierFrequency = true
waas_133L1.carrierFrequencyHz = 1575420000.0f
waas_133L1.carrierFrequencyHz = 1575420000.0
waas_133L1.sbasType = SbasType.WAAS
return waas_133L1
}
@@ -393,7 +393,7 @@ fun waas_133L5(usedInFix: Boolean): SatelliteStatus {
72f,
25f);
waas_133L5.hasCarrierFrequency = true
waas_133L5.carrierFrequencyHz = 1176450000.0f
waas_133L5.carrierFrequencyHz = 1176450000.0
waas_133L5.sbasType = SbasType.WAAS
return waas_133L5
}
@@ -411,7 +411,7 @@ fun galaxy15_135L1(usedInFix: Boolean): SatelliteStatus {
72f,
25f);
galaxy15_135L1.hasCarrierFrequency = true
galaxy15_135L1.carrierFrequencyHz = 1575420000.0f
galaxy15_135L1.carrierFrequencyHz = 1575420000.0
galaxy15_135L1.sbasType = SbasType.WAAS
return galaxy15_135L1
}
@@ -429,7 +429,7 @@ fun galaxy15_135L5(usedInFix: Boolean): SatelliteStatus {
72f,
25f);
galaxy15_135L5.hasCarrierFrequency = true
galaxy15_135L5.carrierFrequencyHz = 1176450000.0f
galaxy15_135L5.carrierFrequencyHz = 1176450000.0
galaxy15_135L5.sbasType = SbasType.WAAS
return galaxy15_135L5
}

View File

@@ -250,8 +250,8 @@ class MapFragment : SupportMapFragment(), View.OnClickListener, LocationSource,
sensorFlow = repository.getSensorUpdates()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach {
//Log.d(TAG, "Map sensor: orientation ${it.orientation}, tilt ${it.tilt}")
onOrientationChanged(it.orientation, it.tilt)
//Log.d(TAG, "Map sensor: orientation ${it.values[0]}, tilt ${it.values[1]}")
onOrientationChanged(it.values[0], it.values[1])
}
.launchIn(lifecycleScope)
}

View File

@@ -32,6 +32,7 @@ import android.location.LocationManager
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.SystemClock
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
@@ -50,6 +51,8 @@ import com.android.gpstest.ui.MainActivity
import com.android.gpstest.util.FormatUtils.toNotificationTitle
import com.android.gpstest.util.IOUtils.*
import com.android.gpstest.util.PreferenceUtil
import com.android.gpstest.util.PreferenceUtil.injectPsdsWhenLogging
import com.android.gpstest.util.PreferenceUtil.injectTimeWhenLogging
import com.android.gpstest.util.PreferenceUtil.isCsvLoggingEnabled
import com.android.gpstest.util.PreferenceUtil.isJsonLoggingEnabled
import com.android.gpstest.util.PreferenceUtil.writeAntennaInfoToFileCsv
@@ -62,6 +65,8 @@ import com.android.gpstest.util.PreferenceUtil.writeNavMessageToLogcat
import com.android.gpstest.util.PreferenceUtil.writeNmeaTimestampToLogcat
import com.android.gpstest.util.PreferenceUtil.writeNmeaToAndroidMonitor
import com.android.gpstest.util.PreferenceUtil.writeNmeaToFile
import com.android.gpstest.util.PreferenceUtil.writeOrientationToFile
import com.android.gpstest.util.PreferenceUtil.writeStatusToFile
import com.android.gpstest.util.PreferenceUtils
import com.android.gpstest.util.SatelliteUtil.toSatelliteGroup
import com.android.gpstest.util.SatelliteUtil.toSatelliteStatus
@@ -113,6 +118,7 @@ class ForegroundOnlyLocationService : LifecycleService() {
private var measurementFlow: Job? = null
private var antennaFlow: Job? = null
private var gnssFlow: Job? = null
private var sensorFlow: Job? = null
lateinit var csvFileLogger: CsvFileLogger
lateinit var jsonFileLogger: JsonFileLogger
@@ -258,6 +264,7 @@ class ForegroundOnlyLocationService : LifecycleService() {
observeNmeaFlow()
observeNavMessageFlow()
observeMeasurementsFlow()
observeSensorFlow()
if (SatelliteUtils.isGnssAntennaInfoSupported(getSystemService(Context.LOCATION_SERVICE) as LocationManager)) {
observeAntennaFlow()
}
@@ -270,6 +277,7 @@ class ForegroundOnlyLocationService : LifecycleService() {
navMessageFlow?.cancel()
measurementFlow?.cancel()
antennaFlow?.cancel()
sensorFlow?.cancel()
}
@ExperimentalCoroutinesApi
@@ -312,17 +320,27 @@ class ForegroundOnlyLocationService : LifecycleService() {
// Observe locations via Flow as they are generated by the repository
gnssFlow = repository.getGnssStatus()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.map { it.toSatelliteStatus().toSatelliteGroup() }
.map { it.toSatelliteStatus() }
.onEach {
Log.d(TAG, "Service gnssStatus: $it")
//Log.d(TAG, "Service SatelliteStatus: $it")
// Note - this Flow needs to be active so the Activity/Fragments get TTFF
// when it's created while the service is running in the background
currentSatellites = it
currentSatellites = it.toSatelliteGroup()
// Show location in notification
notificationManager.notify(
NOTIFICATION_ID,
buildNotification(currentLocation, it)
buildNotification(currentLocation, currentSatellites)
)
// Log Status
GlobalScope.launch(Dispatchers.IO) {
if (writeStatusToFile() &&
applicationContext.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
) {
initLogging()
csvFileLogger.onGnssStatusChanged(it, currentLocation)
}
}
}
.launchIn(lifecycleScope)
}
@@ -440,6 +458,27 @@ class ForegroundOnlyLocationService : LifecycleService() {
.launchIn(lifecycleScope)
}
@ExperimentalCoroutinesApi
private fun observeSensorFlow() {
if (sensorFlow?.isActive == true) {
// If we're already observing updates, don't register again
return
}
// Observe locations via Flow as they are generated by the repository
sensorFlow = repository.getSensorUpdates()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach {
//Log.d(TAG, "Service sensor: orientation ${it.values[0]}, tilt ${it.values[1]}")
if (writeOrientationToFile() &&
applicationContext.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
) {
initLogging()
csvFileLogger.onOrientationChanged(it, System.currentTimeMillis(), SystemClock.elapsedRealtime())
}
}
.launchIn(lifecycleScope)
}
private fun goForegroundOrStopSelf() {
lifecycleScope.launch {
// We may have been restarted by the system - check if we're still monitoring data
@@ -564,6 +603,16 @@ class ForegroundOnlyLocationService : LifecycleService() {
if (!applicationContext.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
return
}
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
// Inject time and/or PSDS to make sure timestamps and assistance are as updated as possible
if (injectTimeWhenLogging()) {
forceTimeInjection(locationManager)
}
if (injectPsdsWhenLogging()) {
forcePsdsInjection(locationManager)
}
val date = Date()
if (!csvFileLogger.isStarted && isCsvLoggingEnabled()) {
// User has granted permissions and has chosen to log at least one data type

View File

@@ -26,7 +26,7 @@ import android.view.Display
import android.view.Surface
import com.android.gpstest.Application
import com.android.gpstest.R
import com.android.gpstest.model.OrientationAndTilt
import com.android.gpstest.model.Orientation
import com.android.gpstest.util.MathUtils
import com.android.gpstest.util.SatelliteUtils
import com.android.gpstest.util.hasPermission
@@ -76,8 +76,9 @@ class SharedSensorManager constructor(
val callback: SensorEventListener =
object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
var orientation: Double
var tilt = Double.NaN
var orientationX: Double
var tiltY = Double.NaN
var yawZ = Double.NaN
when (event.sensor.type) {
Sensor.TYPE_ROTATION_VECTOR -> {
@@ -86,12 +87,13 @@ class SharedSensorManager constructor(
val display = getDisplay()
if (display != null) handleRotation(display.rotation)
orientation = Math.toDegrees(values[0].toDouble()) // azimuth
tilt = Math.toDegrees(values[1].toDouble())
orientationX = Math.toDegrees(values[0].toDouble()) // azimuth
tiltY = Math.toDegrees(values[1].toDouble())
yawZ = Math.toDegrees(values[2].toDouble())
}
Sensor.TYPE_ORIENTATION ->
// Legacy orientation sensors
orientation = event.values[0].toDouble()
orientationX = event.values[0].toDouble()
else ->
// A sensor we're not using, so return
return
@@ -103,14 +105,14 @@ class SharedSensorManager constructor(
true
)
) {
orientation += geomagneticField.declination.toDouble()
orientationX += geomagneticField.declination.toDouble()
// Make sure value is between 0-360
orientation = MathUtils.mod(orientation.toFloat(), 360.0f).toDouble()
orientationX = MathUtils.mod(orientationX, 360.0)
}
//Log.d(TAG, "New sensor: $orientation and $tilt")
//Log.d(TAG, "New sensor: $orientationX and $tiltY")
// Send the new sensors to the Flow observers
trySend(OrientationAndTilt(orientation, tilt))
trySend(Orientation(event.timestamp, doubleArrayOf(orientationX, tiltY, yawZ)))
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
@@ -262,8 +264,11 @@ class SharedSensorManager constructor(
}
}
/**
* A flow of sensor orientations
*/
@ExperimentalCoroutinesApi
fun sensorFlow(): Flow<OrientationAndTilt> {
fun sensorFlow(): Flow<Orientation> {
return _sensorUpdates
}
}

View File

@@ -37,12 +37,14 @@ import androidx.core.content.ContextCompat;
import com.android.gpstest.Application;
import com.android.gpstest.BuildConfig;
import com.android.gpstest.R;
import com.android.gpstest.model.Orientation;
import com.android.gpstest.model.SatelliteStatus;
import com.android.gpstest.util.FormatUtils;
import com.android.gpstest.util.IOUtils;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
/**
* A GNSS logger to store information to a CSV file. Originally from https://github.com/google/gps-measurement-tools/tree/master/GNSSLogger,
@@ -84,6 +86,7 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
*/
@Override
void writeFileHeader(BufferedWriter writer, String filePath) {
// TODO - update header to new field formats
try {
writer.write(COMMENT_START);
writer.newLine();
@@ -134,15 +137,7 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
writer.newLine();
writer.write(COMMENT_START);
writer.write(
" Raw,ElapsedRealtimeMillis,TimeNanos,LeapSecond,TimeUncertaintyNanos,FullBiasNanos,"
+ "BiasNanos,BiasUncertaintyNanos,DriftNanosPerSecond,DriftUncertaintyNanosPerSecond,"
+ "HardwareClockDiscontinuityCount,Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,"
+ "ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,"
+ "PseudorangeRateUncertaintyMetersPerSecond,"
+ "AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,"
+ "AccumulatedDeltaRangeUncertaintyMeters,CarrierFrequencyHz,CarrierCycles,"
+ "CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,"
+ "ConstellationType,AgcDb,CarrierFrequencyHz");
" Raw,utcTimeMillis,TimeNanos,LeapSecond,TimeUncertaintyNanos,FullBiasNanos,BiasNanos,BiasUncertaintyNanos,DriftNanosPerSecond,DriftUncertaintyNanosPerSecond,HardwareClockDiscontinuityCount,Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,PseudorangeRateUncertaintyMetersPerSecond,AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,AccumulatedDeltaRangeUncertaintyMeters,CarrierFrequencyHz,CarrierCycles,CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,ConstellationType,AgcDb,BasebandCn0DbHz,FullInterSignalBiasNanos,FullInterSignalBiasUncertaintyNanos,SatelliteInterSignalBiasNanos,SatelliteInterSignalBiasUncertaintyNanos,CodeType,ChipsetElapsedRealtimeNanos");
writer.newLine();
writer.write(COMMENT_START);
writer.newLine();
@@ -151,7 +146,7 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
writer.newLine();
writer.write(COMMENT_START);
writer.write(
" Fix,Provider,Latitude,Longitude,Altitude,Speed,Accuracy,(UTC)TimeInMs");
" Fix,Provider,LatitudeDegrees,LongitudeDegrees,AltitudeMeters,SpeedMps,AccuracyMeters,BearingDegrees,UnixTimeMillis,SpeedAccuracyMps,BearingAccuracyDegrees,elapsedRealtimeNanos,VerticalAccuracyMeters");
writer.newLine();
writer.write(COMMENT_START);
writer.newLine();
@@ -177,6 +172,20 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
writer.write(COMMENT_START);
writer.write(" GnssAntennaInfo,CarrierFrequencyMHz,PhaseCenterOffsetXOffsetMm,PhaseCenterOffsetXOffsetUncertaintyMm,PhaseCenterOffsetYOffsetMm,PhaseCenterOffsetYOffsetUncertaintyMm,PhaseCenterOffsetZOffsetMm,PhaseCenterOffsetZOffsetUncertaintyMm,PhaseCenterVariationCorrectionsArray,PhaseCenterVariationCorrectionUncertaintiesArray,PhaseCenterVariationCorrectionsDeltaPhi,PhaseCenterVariationCorrectionsDeltaTheta,SignalGainCorrectionsArray,SignalGainCorrectionUncertaintiesArray,SignalGainCorrectionsDeltaPhi,SignalGainCorrectionsDeltaTheta");
writer.newLine();
writer.write(COMMENT_START);
writer.newLine();
writer.write(COMMENT_START);
writer.write("GnssStatus format (https://developer.android.com/reference/android/location/GnssStatus):");
writer.newLine();
writer.write(COMMENT_START);
writer.write(" Status,UnixTimeMillis,SignalCount,SignalIndex,ConstellationType,Svid,CarrierFrequencyHz,Cn0DbHz,AzimuthDegrees,ElevationDegrees,UsedInFix,HasAlmanacData,HasEphemerisData,BasebandCn0DbHz");
writer.newLine();
writer.write(COMMENT_START);
writer.write("Orientation sensor format (https://developer.android.com/reference/android/hardware/SensorEvent#values):");
writer.newLine();
writer.write(COMMENT_START);
writer.write(" OrientationDeg,utcTimeMillis,elapsedRealtimeNanos,yawDeg,rollDeg,pitchDeg");
writer.newLine();
} catch (IOException e) {
logException(Application.Companion.getApp().getString(R.string.could_not_initialize_file, filePath), e);
return;
@@ -188,17 +197,7 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
if (fileWriter == null) {
return;
}
String locationStream =
String.format(
Locale.US,
"Fix,%s,%f,%f,%f,%f,%f,%d",
location.getProvider(),
location.getLatitude(),
location.getLongitude(),
location.getAltitude(),
location.getSpeed(),
location.getAccuracy(),
location.getTime());
String locationStream = FormatUtils.toLog(location);
try {
fileWriter.write(locationStream);
fileWriter.newLine();
@@ -208,7 +207,33 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
/**
* Called to log GnssStatus information
* @param statuses GnssStatus information converted to a list of SatelliteStatus
* @param location the most recently calculated location, or null if one hasn't been calculated yet
*/
public synchronized void onGnssStatusChanged(List<SatelliteStatus> statuses, Location location) {
if (fileWriter == null) {
return;
}
int i = 0;
for (SatelliteStatus s : statuses) {
try {
writeStatusToFile(s, location != null ? location.getTime() : 0, i, statuses.size());
} catch (IOException e) {
logException(Application.Companion.getApp().getString(R.string.error_writing_file), e);
}
i++;
}
}
private synchronized void writeStatusToFile(SatelliteStatus status, long unixTimeMillis, int signalCount, int signalIndex) throws IOException {
fileWriter.write(
FormatUtils.toLog(status, unixTimeMillis, signalCount, signalIndex)
);
fileWriter.newLine();
}
public synchronized void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
if (fileWriter == null) {
return;
@@ -223,7 +248,6 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public synchronized void onGnssNavigationMessageReceived(GnssNavigationMessage navigationMessage) {
if (fileWriter == null) {
return;
@@ -267,55 +291,15 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void writeGnssMeasurementToFile(GnssClock clock, GnssMeasurement measurement)
throws IOException {
String clockStream =
String.format(
"Raw,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
fileWriter.write(
FormatUtils.toLog(
SystemClock.elapsedRealtime(),
clock.getTimeNanos(),
clock.hasLeapSecond() ? clock.getLeapSecond() : "",
clock.hasTimeUncertaintyNanos() ? clock.getTimeUncertaintyNanos() : "",
clock.getFullBiasNanos(),
clock.hasBiasNanos() ? clock.getBiasNanos() : "",
clock.hasBiasUncertaintyNanos() ? clock.getBiasUncertaintyNanos() : "",
clock.hasDriftNanosPerSecond() ? clock.getDriftNanosPerSecond() : "",
clock.hasDriftUncertaintyNanosPerSecond()
? clock.getDriftUncertaintyNanosPerSecond()
: "",
clock.getHardwareClockDiscontinuityCount() + ",");
fileWriter.write(clockStream);
String measurementStream =
String.format(
"%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
measurement.getSvid(),
measurement.getTimeOffsetNanos(),
measurement.getState(),
measurement.getReceivedSvTimeNanos(),
measurement.getReceivedSvTimeUncertaintyNanos(),
measurement.getCn0DbHz(),
measurement.getPseudorangeRateMetersPerSecond(),
measurement.getPseudorangeRateUncertaintyMetersPerSecond(),
measurement.getAccumulatedDeltaRangeState(),
measurement.getAccumulatedDeltaRangeMeters(),
measurement.getAccumulatedDeltaRangeUncertaintyMeters(),
measurement.hasCarrierFrequencyHz() ? measurement.getCarrierFrequencyHz() : "",
measurement.hasCarrierCycles() ? measurement.getCarrierCycles() : "",
measurement.hasCarrierPhase() ? measurement.getCarrierPhase() : "",
measurement.hasCarrierPhaseUncertainty()
? measurement.getCarrierPhaseUncertainty()
: "",
measurement.getMultipathIndicator(),
measurement.hasSnrInDb() ? measurement.getSnrInDb() : "",
measurement.getConstellationType(),
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& measurement.hasAutomaticGainControlLevelDb()
? measurement.getAutomaticGainControlLevelDb()
: "",
measurement.hasCarrierFrequencyHz() ? measurement.getCarrierFrequencyHz() : "");
fileWriter.write(measurementStream);
SystemClock.elapsedRealtimeNanos(),
clock,
measurement)
);
fileWriter.newLine();
}
@@ -323,7 +307,7 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
public synchronized void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> list) {
try {
for (GnssAntennaInfo info : list) {
fileWriter.write(IOUtils.serialize(info));
fileWriter.write(FormatUtils.toLog(info));
fileWriter.newLine();
}
fileWriter.newLine();
@@ -331,4 +315,16 @@ public class CsvFileLogger extends BaseFileLogger implements FileLogger {
logException("Unable to write antenna info to CSV", e);
}
}
public synchronized void onOrientationChanged(Orientation orientation, long currentTimeMs, long millisSinceBootMs) {
if (fileWriter == null) {
return;
}
try {
fileWriter.write(FormatUtils.toLog(orientation, currentTimeMs, millisSinceBootMs));
fileWriter.newLine();
} catch (IOException e) {
logException(Application.Companion.getApp().getString(R.string.error_writing_file), e);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2021 Sean Barbeau
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gpstest.model
/**
* Container class holding rotation sensor timestamp, and [values], where the first index is the
* orientation (X) for display (which has magnetic correction applied if available as well as
* rotation correction), the second is the tilt (Y), and the third is the yaw (Z)
*/
data class Orientation(val elapsedRealtimeNanos: Long, val values: DoubleArray) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Orientation
if (elapsedRealtimeNanos != other.elapsedRealtimeNanos) return false
if (!values.contentEquals(other.values)) return false
return true
}
override fun hashCode(): Int {
var result = elapsedRealtimeNanos.hashCode()
result = 31 * result + values.contentHashCode()
return result
}
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright 2021 Sean Barbeau
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gpstest.model
/**
* Container class holding rotation sensor [orientation] and [tilt]
*/
data class OrientationAndTilt(val orientation: Double, val tilt: Double)

View File

@@ -30,7 +30,9 @@ data class SatelliteStatus (
var azimuthDegrees: Float) {
var sbasType: SbasType = SbasType.UNKNOWN
var hasCarrierFrequency: Boolean = false
var carrierFrequencyHz: Float = NO_DATA
var carrierFrequencyHz: Double = 0.0
var hasBasebandCn0DbHz: Boolean = false
var basebandCn0DbHz: Float = NO_DATA
companion object {
const val NO_DATA = 0.0f

View File

@@ -69,6 +69,7 @@ import kotlin.math.abs
@AndroidEntryPoint
class SkyFragment : Fragment() {
@OptIn(ExperimentalCoroutinesApi::class)
private val viewModel: SignalInfoViewModel by activityViewModels()
// Binding variables
@@ -197,8 +198,8 @@ class SkyFragment : Fragment() {
sensorFlow = repository.getSensorUpdates()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach {
//Log.d(TAG, "Sky sensor: orientation ${it.orientation}, tilt ${it.tilt}")
onOrientationChanged(it.orientation, it.tilt)
//Log.d(TAG, "Sky sensor: orientation ${it[0]}, tilt ${it[1]}")
onOrientationChanged(it.values[0], it.values[1])
}
.launchIn(lifecycleScope)
}

View File

@@ -61,6 +61,7 @@ import com.android.gpstest.util.FormatUtils.formatBearingAccuracy
import com.android.gpstest.util.PreferenceUtil.coordinateFormat
import com.android.gpstest.util.PreferenceUtil.shareIncludeAltitude
import com.android.gpstest.util.PreferenceUtils.gnssFilter
import com.android.gpstest.util.SatelliteUtil.isVerticalAccuracySupported
import java.text.SimpleDateFormat
@Preview
@@ -91,9 +92,10 @@ fun previewLocation(): Location {
altitude = 13.5
speed = 21.5f
bearing = 240f
if (SatelliteUtils.isSpeedAndBearingAccuracySupported()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
bearingAccuracyDegrees = 5.6f
speedAccuracyMetersPerSecond = 6.1f
verticalAccuracyMeters = 92.5f
}
}
return l
@@ -365,7 +367,7 @@ fun LabelColumn2(location: Location) {
) {
LocationLabel(R.string.fix_time_label)
LocationLabel(R.string.ttff_label)
LocationLabel(if (SatelliteUtils.isVerticalAccuracySupported(location)) R.string.hor_and_vert_accuracy_label else R.string.accuracy_label)
LocationLabel(if (location.isVerticalAccuracySupported()) R.string.hor_and_vert_accuracy_label else R.string.accuracy_label)
LocationLabel(R.string.num_sats_label)
LocationLabel(R.string.bearing_label)
LocationLabel(R.string.bearing_acc_label)

View File

@@ -33,7 +33,7 @@ public class CarrierFreqUtils {
*/
public static String CF_UNSUPPORTED = "unsupported";
static final float CF_TOLERANCE_MHZ = 1f;
static final double CF_TOLERANCE_MHZ = 1d;
/**
* Returns the label that should be displayed for a given GNSS constellation, svid, and carrier
@@ -47,7 +47,7 @@ public class CarrierFreqUtils {
if (!SatelliteUtils.isCfSupported() || !status.getHasCarrierFrequency()) {
return CF_UNSUPPORTED;
}
float cfMhz = MathUtils.toMhz(status.getCarrierFrequencyHz());
double cfMhz = MathUtils.toMhz(status.getCarrierFrequencyHz());
int svid = status.getSvid();
switch (status.getGnssType()) {
@@ -79,16 +79,16 @@ public class CarrierFreqUtils {
* @param carrierFrequencyMhz carrier frequency in MHz
* @return carrier frequency label
*/
public static String getNavstarCF(float carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
public static String getNavstarCF(double carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1227.6f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1227.6, CF_TOLERANCE_MHZ)) {
return "L2";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1381.05f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1381.05, CF_TOLERANCE_MHZ)) {
return "L3";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1379.913f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1379.913, CF_TOLERANCE_MHZ)) {
return "L4";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
} else {
return CF_UNKNOWN;
@@ -100,19 +100,19 @@ public class CarrierFreqUtils {
* @param carrierFrequencyMhz carrier frequency in MHz
* @return carrier frequency label
*/
public static String getGlonassCf(float carrierFrequencyMhz) {
if (carrierFrequencyMhz >= 1598.0f && carrierFrequencyMhz <= 1606.0f) {
public static String getGlonassCf(double carrierFrequencyMhz) {
if (carrierFrequencyMhz >= 1598.0 && carrierFrequencyMhz <= 1606.0) {
// Actual range is 1598.0625 MHz to 1605.375, but allow padding for float comparisons - #103
return "L1";
} else if (carrierFrequencyMhz >= 1242.0f && carrierFrequencyMhz <= 1249.0f) {
} else if (carrierFrequencyMhz >= 1242.0 && carrierFrequencyMhz <= 1249.0) {
// Actual range is 1242.9375 MHz to 1248.625, but allow padding for float comparisons - #103
return "L2";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14, CF_TOLERANCE_MHZ)) {
// Exact range is unclear - appears to be 1202.025 - 1207.14 - #103
return "L3";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1-C";
} else {
return CF_UNKNOWN;
@@ -124,18 +124,18 @@ public class CarrierFreqUtils {
* @param carrierFrequencyMhz carrier frequency in MHz
* @return carrier frequency label
*/
public static String getBeidoucCf(float carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1561.098f, CF_TOLERANCE_MHZ)) {
public static String getBeidoucCf(double carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1561.098, CF_TOLERANCE_MHZ)) {
return "B1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1589.742f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1589.742, CF_TOLERANCE_MHZ)) {
return "B1-2";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "B1C";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14, CF_TOLERANCE_MHZ)) {
return "B2";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "B2a";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1268.52f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1268.52, CF_TOLERANCE_MHZ)) {
return "B3";
} else {
return CF_UNKNOWN;
@@ -147,14 +147,14 @@ public class CarrierFreqUtils {
* @param carrierFrequencyMhz carrier frequency in MHz
* @return carrier frequency label
*/
public static String getQzssCf(float carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
public static String getQzssCf(double carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1227.6f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1227.6, CF_TOLERANCE_MHZ)) {
return "L2";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1278.75f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1278.75, CF_TOLERANCE_MHZ)) {
return "L6";
} else {
return CF_UNKNOWN;
@@ -166,16 +166,16 @@ public class CarrierFreqUtils {
* @param carrierFrequencyMhz carrier frequency in MHz
* @return carrier frequency label
*/
public static String getGalileoCf(float carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
public static String getGalileoCf(double carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "E1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1191.795f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1191.795, CF_TOLERANCE_MHZ)) {
return "E5";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "E5a";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1207.14, CF_TOLERANCE_MHZ)) {
return "E5b";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1278.75f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1278.75, CF_TOLERANCE_MHZ)) {
return "E6";
} else {
return CF_UNKNOWN;
@@ -187,10 +187,10 @@ public class CarrierFreqUtils {
* @param carrierFrequencyMhz carrier frequency in MHz
* @return carrier frequency label
*/
public static String getIrnssCf(float carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
public static String getIrnssCf(double carrierFrequencyMhz) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 2492.028f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 2492.028, CF_TOLERANCE_MHZ)) {
return "S";
} else {
return CF_UNKNOWN;
@@ -203,31 +203,31 @@ public class CarrierFreqUtils {
* @param carrierFrequencyMhz carrier frequency in MHz
* @return carrier frequency label
*/
public static String getSbasCf(int svid, float carrierFrequencyMhz) {
public static String getSbasCf(int svid, double carrierFrequencyMhz) {
if (svid == 120 || svid == 123 || svid == 126 || svid == 136) {
// EGNOS - https://gssc.esa.int/navipedia/index.php/EGNOS_Space_Segment
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
}
} else if (svid == 129 || svid == 137) {
// MSAS (Japan) - https://gssc.esa.int/navipedia/index.php/MSAS_Space_Segment
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
}
} else if (svid == 127 || svid == 128 || svid == 139) {
// GAGAN (India)
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
}
} else if (svid == 131 || svid == 133 || svid == 135 || svid == 138) {
// WAAS (US)
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42f, CF_TOLERANCE_MHZ)) {
if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1575.42, CF_TOLERANCE_MHZ)) {
return "L1";
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45f, CF_TOLERANCE_MHZ)) {
} else if (MathUtils.fuzzyEquals(carrierFrequencyMhz, 1176.45, CF_TOLERANCE_MHZ)) {
return "L5";
}
}
@@ -244,7 +244,7 @@ public class CarrierFreqUtils {
@RequiresApi(api = Build.VERSION_CODES.R)
public static String getCarrierFrequencyLabel(GnssAntennaInfo gnssAntennaInfo) {
String label;
float cfMHz = (float) gnssAntennaInfo.getCarrierFrequencyMHz();
double cfMHz = gnssAntennaInfo.getCarrierFrequencyMHz();
// Try each GNSS until we find a valid label
label = getNavstarCF(cfMHz);
if (label.equals(CF_UNKNOWN)) {

View File

@@ -1,11 +1,21 @@
package com.android.gpstest.util
import android.location.GnssAntennaInfo
import android.location.GnssClock
import android.location.GnssMeasurement
import android.location.Location
import com.android.gpstest.Application
import android.os.Build
import androidx.annotation.RequiresApi
import com.android.gpstest.Application.Companion.app
import com.android.gpstest.R
import com.android.gpstest.model.CoordinateType
import com.android.gpstest.model.Orientation
import com.android.gpstest.model.SatelliteGroup
import com.android.gpstest.model.SatelliteStatus
import com.android.gpstest.util.SatelliteUtil.isBearingAccuracySupported
import com.android.gpstest.util.SatelliteUtil.isSpeedAccuracySupported
import com.android.gpstest.util.SatelliteUtil.isVerticalAccuracySupported
import com.android.gpstest.util.SatelliteUtil.toGnssStatusConstellationType
import java.util.concurrent.TimeUnit
/**
@@ -18,12 +28,12 @@ internal object FormatUtils {
when (PreferenceUtil.coordinateFormat()) {
"dd" -> {
// Decimal degrees
return Application.app.getString(R.string.lat_or_lon, latOrLong)
return app.getString(R.string.lat_or_lon, latOrLong)
}
"dms" -> {
// Degrees minutes seconds
return UIUtils.getDMSFromLocation(
Application.app,
app,
latOrLong,
coordinateType
)
@@ -31,14 +41,14 @@ internal object FormatUtils {
"ddm" -> {
// Degrees decimal minutes
return UIUtils.getDDMFromLocation(
Application.app,
app,
latOrLong,
coordinateType
)
}
else -> {
// Decimal degrees
return Application.app.getString(R.string.lat_or_lon, latOrLong)
return app.getString(R.string.lat_or_lon, latOrLong)
}
}
}
@@ -47,11 +57,11 @@ internal object FormatUtils {
if (location.hasAltitude()) {
val text = when {
PreferenceUtil.distanceUnits().equals(PreferenceUtil.METERS, ignoreCase = true) -> {
Application.app.getString(R.string.gps_altitude_value_meters, location.altitude)
app.getString(R.string.gps_altitude_value_meters, location.altitude)
}
else -> {
// Feet
Application.app.getString(
app.getString(
R.string.gps_altitude_value_feet,
UIUtils.toFeet(location.altitude)
)
@@ -68,18 +78,18 @@ internal object FormatUtils {
val text = when {
PreferenceUtil.speedUnits()
.equals(PreferenceUtil.METERS_PER_SECOND, ignoreCase = true) -> {
Application.app.getString(R.string.gps_speed_value_meters_sec, location.speed)
app.getString(R.string.gps_speed_value_meters_sec, location.speed)
}
PreferenceUtil.speedUnits()
.equals(PreferenceUtil.KILOMETERS_PER_HOUR, ignoreCase = true) -> {
Application.app.getString(
app.getString(
R.string.gps_speed_value_kilometers_hour,
UIUtils.toKilometersPerHour(location.speed)
)
}
else -> {
// Miles per hour
Application.app.getString(
app.getString(
R.string.gps_speed_value_miles_hour,
UIUtils.toMilesPerHour(location.speed)
)
@@ -106,16 +116,16 @@ internal object FormatUtils {
}
fun formatAccuracy(location: Location): String {
if (SatelliteUtils.isVerticalAccuracySupported(location)) {
if (location.isVerticalAccuracySupported()) {
if (PreferenceUtil.distanceUnits().equals(PreferenceUtil.METERS, ignoreCase = true)) {
return Application.app.getString(
return app.getString(
R.string.gps_hor_and_vert_accuracy_value_meters,
location.accuracy,
location.verticalAccuracyMeters
)
} else {
// Feet
return Application.app.getString(
return app.getString(
R.string.gps_hor_and_vert_accuracy_value_feet,
UIUtils.toFeet(location.accuracy.toDouble()),
UIUtils.toFeet(
@@ -127,12 +137,12 @@ internal object FormatUtils {
if (location.hasAccuracy()) {
return if (PreferenceUtil.distanceUnits()
.equals(PreferenceUtil.METERS, ignoreCase = true)) {
Application.app.getString(
app.getString(
R.string.gps_accuracy_value_meters, location.accuracy
)
} else {
// Feet
Application.app.getString(
app.getString(
R.string.gps_accuracy_value_feet,
UIUtils.toFeet(location.accuracy.toDouble())
)
@@ -147,11 +157,11 @@ internal object FormatUtils {
return if (PreferenceUtil.distanceUnits()
.equals(PreferenceUtil.METERS, ignoreCase = true)) {
Application.app.getString(
app.getString(
R.string.gps_altitude_msl_value_meters,
altitudeMsl)
} else {
Application.app.getString(
app.getString(
R.string.gps_altitude_msl_value_feet,
UIUtils.toFeet(altitudeMsl)
)
@@ -159,25 +169,25 @@ internal object FormatUtils {
}
fun formatSpeedAccuracy(location: Location): String {
if (SatelliteUtils.isSpeedAndBearingAccuracySupported() && location.hasSpeedAccuracy()) {
if (location.isSpeedAccuracySupported()) {
when {
PreferenceUtil.speedUnits()
.equals(PreferenceUtil.METERS_PER_SECOND, ignoreCase = true) -> {
return Application.app.getString(
return app.getString(
R.string.gps_speed_acc_value_meters_sec,
location.speedAccuracyMetersPerSecond
)
}
PreferenceUtil.speedUnits()
.equals(PreferenceUtil.KILOMETERS_PER_HOUR, ignoreCase = true) -> {
return Application.app.getString(
return app.getString(
R.string.gps_speed_acc_value_km_hour,
UIUtils.toKilometersPerHour(location.speedAccuracyMetersPerSecond)
)
}
else -> {
// Miles per hour
return Application.app.getString(
return app.getString(
R.string.gps_speed_acc_value_miles_hour,
UIUtils.toMilesPerHour(location.speedAccuracyMetersPerSecond)
)
@@ -192,7 +202,7 @@ internal object FormatUtils {
}
fun formatBearingAccuracy(location: Location): String {
return if (SatelliteUtils.isSpeedAndBearingAccuracySupported() && location.hasBearingAccuracy()) {
return if (location.isBearingAccuracySupported()) {
app.getString(
R.string.gps_bearing_acc_value,
location.bearingAccuracyDegrees
@@ -219,4 +229,139 @@ internal object FormatUtils {
" (" + IOUtils.trimEnds(meta.supportedGnssCfs.sorted().toString()) + ")"
else ""
}
/**
* Returns location data formatted to CSV for logging in the following format:
* Fix,Provider,LatitudeDegrees,LongitudeDegrees,AltitudeMeters,SpeedMps,AccuracyMeters,BearingDegrees,UnixTimeMillis,SpeedAccuracyMps,BearingAccuracyDegrees,elapsedRealtimeNanos,VerticalAccuracyMeters
*/
@JvmStatic
fun Location.toLog(): String {
return "Fix,$provider,$latitude,$longitude,$altitude,$speed,$accuracy,$bearing,$time," +
"${if (isSpeedAccuracySupported()) speedAccuracyMetersPerSecond else ""}," +
"${if (isBearingAccuracySupported()) bearingAccuracyDegrees else ""}," +
"$elapsedRealtimeNanos," +
"${if (isVerticalAccuracySupported()) verticalAccuracyMeters else ""}"
}
/**
* Returns status data formatted to CSV for logging in the following format
* ([unixTimeMillis] comes from the last location fix from GPS provider, or 0 if location hasn't been obtained yet):
* Status,UnixTimeMillis,SignalCount,SignalIndex,ConstellationType,Svid,CarrierFrequencyHz,Cn0DbHz,AzimuthDegrees,ElevationDegrees,UsedInFix,HasAlmanacData,HasEphemerisData,BasebandCn0DbHz
*
* Sample data:
* Status,0,25,0,1,10,1575420032,35.0,136.0,57.0,1,1,1,30.0
*/
@JvmStatic
fun SatelliteStatus.toLog(unixTimeMillis: Long, signalCount: Int, signalIndex: Int): String {
return "Status," +
"$unixTimeMillis," +
"$signalCount,$signalIndex,${gnssType.toGnssStatusConstellationType()},$svid," +
"${carrierFrequencyHz.toBigDecimal().toPlainString()},$cn0DbHz,$azimuthDegrees,$elevationDegrees," +
"${if (usedInFix) "1" else "0"}," +
"${if (hasAlmanac) "1" else "0"}," +
"${if (hasEphemeris) "1" else "0"}," +
"${if (hasBasebandCn0DbHz) basebandCn0DbHz else ""}"
}
/**
* Converts the provided SystemClock.elapsedRealtime() as [elapsedRealtime] and
* [elapsedRealtimeNanos] from SystemClock.elapsedRealtimeNanos(), [clock] and
* [measurement] to a CSV format:
* Raw,utcTimeMillis,TimeNanos,LeapSecond,TimeUncertaintyNanos,FullBiasNanos,BiasNanos,BiasUncertaintyNanos,DriftNanosPerSecond,DriftUncertaintyNanosPerSecond,HardwareClockDiscontinuityCount,Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,PseudorangeRateUncertaintyMetersPerSecond,AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,AccumulatedDeltaRangeUncertaintyMeters,CarrierFrequencyHz,CarrierCycles,CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,ConstellationType,AgcDb,BasebandCn0DbHz,FullInterSignalBiasNanos,FullInterSignalBiasUncertaintyNanos,SatelliteInterSignalBiasNanos,SatelliteInterSignalBiasUncertaintyNanos,CodeType,ChipsetElapsedRealtimeNanos
*/
@JvmStatic
fun toLog(elapsedRealtime: Long, elapsedRealtimeNanos: Long, clock: GnssClock, measurement: GnssMeasurement): String {
return clock.toLog(elapsedRealtime) + "," + measurement.toLog(elapsedRealtimeNanos)
}
/**
* Returns the following format:
* Raw,utcTimeMillis,TimeNanos,LeapSecond,TimeUncertaintyNanos,FullBiasNanos,BiasNanos,BiasUncertaintyNanos,DriftNanosPerSecond,DriftUncertaintyNanosPerSecond,HardwareClockDiscontinuityCount
*/
@JvmStatic
private fun GnssClock.toLog(elapsedRealtime: Long): String {
return "Raw,$elapsedRealtime,$timeNanos," +
"${if (hasLeapSecond()) leapSecond else ""}," +
"${if (hasTimeUncertaintyNanos()) timeUncertaintyNanos else ""}," +
"$fullBiasNanos" +
"${if (hasBiasNanos()) biasNanos else ""}," +
"${if (hasBiasUncertaintyNanos()) biasUncertaintyNanos else ""}," +
"${if (hasDriftNanosPerSecond()) driftNanosPerSecond else ""}," +
"${if (hasDriftUncertaintyNanosPerSecond()) driftUncertaintyNanosPerSecond else ""}," +
"$hardwareClockDiscontinuityCount"
}
/**
* Returns the following format:
* Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,PseudorangeRateUncertaintyMetersPerSecond,AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,AccumulatedDeltaRangeUncertaintyMeters,CarrierFrequencyHz,CarrierCycles,CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,ConstellationType,AgcDb,BasebandCn0DbHz,FullInterSignalBiasNanos,FullInterSignalBiasUncertaintyNanos,SatelliteInterSignalBiasNanos,SatelliteInterSignalBiasUncertaintyNanos,CodeType,ChipsetElapsedRealtimeNanos
*/
@JvmStatic
private fun GnssMeasurement.toLog(elapsedRealtimeNanos: Long): String {
// Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,PseudorangeRateUncertaintyMetersPerSecond,AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,AccumulatedDeltaRangeUncertaintyMeters,
return "$svid,$timeOffsetNanos,$state,$receivedSvTimeNanos,$receivedSvTimeUncertaintyNanos," +
"$cn0DbHz,$pseudorangeRateMetersPerSecond,$pseudorangeRateUncertaintyMetersPerSecond," +
"$accumulatedDeltaRangeState,$accumulatedDeltaRangeMeters," +
"$accumulatedDeltaRangeUncertaintyMeters," +
// CarrierFrequencyHz,CarrierCycles,CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,ConstellationType,AgcDb,
"${if (hasCarrierFrequencyHz()) carrierFrequencyHz.toBigDecimal().toPlainString() else ""}," +
"${if (hasCarrierCycles()) carrierCycles else ""}," +
"${if (hasCarrierPhase()) carrierPhase else ""}," +
"${if (hasCarrierPhaseUncertainty()) carrierPhaseUncertainty else ""}," +
"$multipathIndicator," +
"${if (hasSnrInDb()) snrInDb else ""}," +
"$constellationType," +
"${if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasAutomaticGainControlLevelDb()) automaticGainControlLevelDb else ""}," +
// BasebandCn0DbHz,FullInterSignalBiasNanos,FullInterSignalBiasUncertaintyNanos,SatelliteInterSignalBiasNanos,SatelliteInterSignalBiasUncertaintyNanos,CodeType,ChipsetElapsedRealtimeNanos
"${if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasBasebandCn0DbHz()) basebandCn0DbHz else ""}," +
"${if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasFullInterSignalBiasNanos()) fullInterSignalBiasNanos else ""}," +
"${if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasFullInterSignalBiasUncertaintyNanos()) fullInterSignalBiasUncertaintyNanos else ""}," +
"${if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasSatelliteInterSignalBiasNanos()) satelliteInterSignalBiasNanos else ""}," +
"${if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasSatelliteInterSignalBiasUncertaintyNanos()) satelliteInterSignalBiasUncertaintyNanos else ""}," +
"${if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && hasCodeType()) codeType else ""}," +
"$elapsedRealtimeNanos"
}
/**
* Returns a CSV representation of the data as:
* GnssAntennaInfo,CarrierFrequencyMHz,PhaseCenterOffsetXOffsetMm,PhaseCenterOffsetXOffsetUncertaintyMm,PhaseCenterOffsetYOffsetMm,PhaseCenterOffsetYOffsetUncertaintyMm,PhaseCenterOffsetZOffsetMm,PhaseCenterOffsetZOffsetUncertaintyMm,PhaseCenterVariationCorrectionsArray,PhaseCenterVariationCorrectionUncertaintiesArray,PhaseCenterVariationCorrectionsDeltaPhi,PhaseCenterVariationCorrectionsDeltaTheta,SignalGainCorrectionsArray,SignalGainCorrectionUncertaintiesArray,SignalGainCorrectionsDeltaPhi,SignalGainCorrectionsDeltaTheta
*/
@RequiresApi(api = Build.VERSION_CODES.R)
@JvmStatic
fun GnssAntennaInfo.toLog(): String {
return "GnssAntennaInfo,$carrierFrequencyMHz,${phaseCenterOffset.xOffsetMm}," +
"${phaseCenterOffset.xOffsetUncertaintyMm},${phaseCenterOffset.yOffsetMm}," +
"${phaseCenterOffset.yOffsetUncertaintyMm},${phaseCenterOffset.zOffsetMm}," +
"${phaseCenterOffset.zOffsetUncertaintyMm},${
IOUtils.serialize(
phaseCenterVariationCorrections!!.correctionsArray
)
},${IOUtils.serialize(phaseCenterVariationCorrections!!.correctionUncertaintiesArray)}," +
"${phaseCenterVariationCorrections!!.deltaPhi},${phaseCenterVariationCorrections!!.deltaTheta},${
IOUtils.serialize(
signalGainCorrections!!.correctionsArray
)
},${IOUtils.serialize(signalGainCorrections!!.correctionUncertaintiesArray)}," +
"${signalGainCorrections!!.deltaPhi},${signalGainCorrections!!.deltaTheta}"
}
/**
* Formats orientations as follows:
* OrientationDeg,1637087900313,1131752852726298,200,0,0
*
* given [currentTimeMs] as System.currentTimeMillis(), and [millisSinceBootMs] as SystemClock.elapsedRealtime()
*
* where:
* utcTimeMillis - The sum of elapsedRealtimeNanos below and the estimated device boot time at UTC, after a recent NTP (Network Time Protocol) sync.
* elapsedRealtimeNanos - The time in nanoseconds at which the event happened.
* yawDeg - If the screen is in portrait mode, this value equals the Azimuth degree (modulus to 0°~360°). If the screen is in landscape mode, it equals the sum (modulus to 0°~360°) of the screen rotation angle (either 90° or 270°) and the Azimuth degree. Azimuth, refers to the angle of rotation about the -z axis. This value represents the angle between the device's y axis and the magnetic north pole.
* rollDeg - Roll, angle of rotation about the y axis. This value represents the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground.
* pitchDeg - Pitch, angle of rotation about the x axis. This value represents the angle between a plane parallel to the device's screen and a plane parallel to the ground.
*/
@JvmStatic
fun Orientation.toLog(currentTimeMs: Long, millisSinceBootMs: Long): String {
val timeAtBootMs = currentTimeMs - millisSinceBootMs
return "OrientationDeg,${TimeUnit.NANOSECONDS.toMillis(elapsedRealtimeNanos) + timeAtBootMs}," +
"$elapsedRealtimeNanos," +
"${values[0]},${values[1]},${values[2]}"
}
}

View File

@@ -24,7 +24,6 @@ import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.location.GnssAntennaInfo;
import android.location.GnssMeasurement;
import android.location.GnssNavigationMessage;
import android.location.Location;
@@ -480,32 +479,6 @@ public class IOUtils {
return input.replace("NAVSTAR", "GPS");
}
/**
* Serializes GnssAntennaInfo to a CSV format
* @param info
* @return GnssAntennaInfo in a CSV format
*/
@RequiresApi(api = Build.VERSION_CODES.R)
public static String serialize(@NonNull GnssAntennaInfo info) {
return String.format(
"GnssAntennaInfo,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
info.getCarrierFrequencyMHz(),
info.getPhaseCenterOffset().getXOffsetMm(),
info.getPhaseCenterOffset().getXOffsetUncertaintyMm(),
info.getPhaseCenterOffset().getYOffsetMm(),
info.getPhaseCenterOffset().getYOffsetUncertaintyMm(),
info.getPhaseCenterOffset().getZOffsetMm(),
info.getPhaseCenterOffset().getZOffsetUncertaintyMm(),
serialize(info.getPhaseCenterVariationCorrections().getCorrectionsArray()),
serialize(info.getPhaseCenterVariationCorrections().getCorrectionUncertaintiesArray()),
info.getPhaseCenterVariationCorrections().getDeltaPhi(),
info.getPhaseCenterVariationCorrections().getDeltaTheta(),
serialize(info.getSignalGainCorrections().getCorrectionsArray()),
serialize(info.getSignalGainCorrections().getCorrectionUncertaintiesArray()),
info.getSignalGainCorrections().getDeltaPhi(),
info.getSignalGainCorrections().getDeltaTheta());
}
/**
* Serializes the provided two-dimensional array of doubles to a String
* (for example, for logging GnssAntennaInfo to CSV files). Example:

View File

@@ -38,7 +38,7 @@ public class MathUtils {
* @param b the divisor
* @return {@code a mod b}
*/
public static float mod(float a, float b) {
public static double mod(double a, double b) {
return (a % b + b) % b;
}
@@ -47,8 +47,8 @@ public class MathUtils {
* @param hertz value to be converted
* @return value converted to MHz
*/
public static float toMhz(float hertz) {
return hertz / 1000000.00f;
public static double toMhz(double hertz) {
return hertz / 1000000.00;
}
/**

View File

@@ -82,12 +82,28 @@ internal object PreferenceUtil {
return Application.prefs.getBoolean(Application.app.getString(R.string.pref_key_file_navigation_message_output), false);
}
fun writeStatusToFile(): Boolean {
return Application.prefs.getBoolean(Application.app.getString(R.string.pref_key_file_gnss_status_output), false);
}
fun writeOrientationToFile(): Boolean {
return Application.prefs.getBoolean(Application.app.getString(R.string.pref_key_file_orientation_output), false);
}
fun injectTimeWhenLogging(): Boolean {
return Application.prefs.getBoolean(Application.app.getString(R.string.pref_key_inject_time_when_logging), true);
}
fun injectPsdsWhenLogging(): Boolean {
return Application.prefs.getBoolean(Application.app.getString(R.string.pref_key_inject_psds_when_logging), true);
}
fun isFileLoggingEnabled(): Boolean {
return isCsvLoggingEnabled() || isJsonLoggingEnabled()
}
fun isCsvLoggingEnabled(): Boolean {
return writeNmeaToFile() || writeMeasurementsToFile() || writeNavMessageToFile() || writeLocationToFile() || writeAntennaInfoToFileCsv()
return writeNmeaToFile() || writeMeasurementsToFile() || writeNavMessageToFile() || writeLocationToFile() || writeAntennaInfoToFileCsv() || writeStatusToFile() || writeOrientationToFile()
}
fun isJsonLoggingEnabled(): Boolean {

View File

@@ -15,7 +15,10 @@
*/
package com.android.gpstest.util
import android.annotation.SuppressLint
import android.location.GnssStatus
import android.location.Location
import android.os.Build
import com.android.gpstest.model.*
import com.android.gpstest.util.CarrierFreqUtils.*
import com.android.gpstest.util.SatelliteUtils.createGnssSatelliteKey
@@ -25,13 +28,14 @@ internal object SatelliteUtil {
/**
* Tranforms the Android [GnssStatus] object to a list of our [SatelliteStatus] model objects
*/
@JvmStatic
fun GnssStatus.toSatelliteStatus() : List<SatelliteStatus> {
val satStatuses: MutableList<SatelliteStatus> = ArrayList()
for (i in 0 until this.satelliteCount) {
val satStatus = SatelliteStatus(
this.getSvid(i),
SatelliteUtils.getGnssConstellationType(this.getConstellationType(i)),
this.getConstellationType(i).toGnssType(),
this.getCn0DbHz(i),
this.hasAlmanacData(i),
this.hasEphemerisData(i),
@@ -41,10 +45,14 @@ internal object SatelliteUtil {
)
if (SatelliteUtils.isCfSupported() && this.hasCarrierFrequencyHz(i)) {
satStatus.hasCarrierFrequency = true
satStatus.carrierFrequencyHz = this.getCarrierFrequencyHz(i)
satStatus.carrierFrequencyHz = this.getCarrierFrequencyHz(i).toDouble()
}
if (isBasebandCn0DbHzSupported(i)) {
satStatus.hasBasebandCn0DbHz = true
satStatus.basebandCn0DbHz = this.getBasebandCn0DbHz(i)
}
if (satStatus.gnssType == GnssType.SBAS) {
satStatus.sbasType = SatelliteUtils.getSbasConstellationType(satStatus.svid)
satStatus.sbasType = satStatus.svid.toSbasType()
}
satStatuses.add(satStatus)
}
@@ -193,4 +201,108 @@ internal object SatelliteUtil {
)
)
}
/**
* Returns true if the speed accuracy is supported for this location, false if it does not
*
* @return true if the speed accuracy is supported for this location, false if it does not
*/
fun Location.isSpeedAccuracySupported(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasSpeedAccuracy()
}
/**
* Returns true if the bearing accuracy is supported for this location, false if it does not
*
* @return true if the bearing accuracy is supported for this location, false if it does not
*/
fun Location.isBearingAccuracySupported(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasBearingAccuracy()
}
/**
* Returns true if the platform supports providing vertical accuracy values and this location
* has vertical accuracy information, false if it does not
*
* @return true if the platform supports providing vertical accuracy values and this location
* has vertical accuracy information, false if it does not
*/
fun Location.isVerticalAccuracySupported(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasVerticalAccuracy()
}
/**
* Returns true if the platform supports providing basebandCn0DbHz values and the status at the
* provided [index] has basebandCn0DbHz information, false if it does not
*
* @return true if the platform supports providing basebandCn0DbHz values and the status at the
* provided [index] has basebandCn0DbHz information, false if it does not
*/
fun GnssStatus.isBasebandCn0DbHzSupported(index: Int): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasBasebandCn0DbHz(index)
}
/**
* Returns the Global Navigation Satellite System (GNSS) for a satellite given the GnssStatus
* constellation type. Note that getSbasConstellationType() should be used to get the particular
* SBAS constellation type
*
* @return GnssType for the given GnssStatus constellation type
*/
private fun Int.toGnssType(): GnssType {
return when (this) {
GnssStatus.CONSTELLATION_GPS -> GnssType.NAVSTAR
GnssStatus.CONSTELLATION_GLONASS -> GnssType.GLONASS
GnssStatus.CONSTELLATION_BEIDOU -> GnssType.BEIDOU
GnssStatus.CONSTELLATION_QZSS -> GnssType.QZSS
GnssStatus.CONSTELLATION_GALILEO -> GnssType.GALILEO
GnssStatus.CONSTELLATION_IRNSS -> GnssType.IRNSS
GnssStatus.CONSTELLATION_SBAS -> GnssType.SBAS
GnssStatus.CONSTELLATION_UNKNOWN -> GnssType.UNKNOWN
else -> GnssType.UNKNOWN
}
}
/**
* Returns the Android system GnssStatus.getConstellationType() for our GnssType enumeration
* @return GnssStatus.getConstellationType() for a given GnssType
*/
@SuppressLint("InlinedApi")
fun GnssType.toGnssStatusConstellationType(): Int {
return when (this) {
GnssType.NAVSTAR -> GnssStatus.CONSTELLATION_GPS
GnssType.GLONASS -> GnssStatus.CONSTELLATION_GLONASS
GnssType.BEIDOU -> GnssStatus.CONSTELLATION_BEIDOU
GnssType.QZSS -> GnssStatus.CONSTELLATION_QZSS
GnssType.GALILEO -> GnssStatus.CONSTELLATION_GALILEO
GnssType.IRNSS -> GnssStatus.CONSTELLATION_IRNSS
GnssType.SBAS -> GnssStatus.CONSTELLATION_SBAS
GnssType.UNKNOWN -> GnssStatus.CONSTELLATION_UNKNOWN
}
}
/**
* Returns the SBAS constellation type for a GnssStatus.CONSTELLATION_SBAS satellite given the GnssStatus
* svid. For Android 7.0 and higher.
*
* [this] is the identification number provided by the GnssStatus.getSvid() method
* @return SbasType for the given GnssStatus svid for GnssStatus.CONSTELLATION_SBAS satellites
*/
fun Int.toSbasType(): SbasType {
if (this == 120 || this == 123 || this == 126 || this == 136) {
return SbasType.EGNOS
} else if (this == 125 || this == 140 || this == 141) {
return SbasType.SDCM
} else if (this == 130 || this == 143 || this == 144) {
// Also referred to as BDSBAS
return SbasType.SNAS
} else if (this == 131 || this == 133 || this == 135 || this == 138) {
return SbasType.WAAS
} else if (this == 127 || this == 128 || this == 139) {
return SbasType.GAGAN
} else if (this == 129 || this == 137) {
return SbasType.MSAS
}
return SbasType.UNKNOWN
}
}

View File

@@ -16,21 +16,12 @@
package com.android.gpstest.util;
import static com.android.gpstest.model.GnssType.BEIDOU;
import static com.android.gpstest.model.GnssType.GALILEO;
import static com.android.gpstest.model.GnssType.GLONASS;
import static com.android.gpstest.model.GnssType.IRNSS;
import static com.android.gpstest.model.GnssType.NAVSTAR;
import static com.android.gpstest.model.GnssType.QZSS;
import static com.android.gpstest.model.GnssType.SBAS;
import static com.android.gpstest.model.GnssType.UNKNOWN;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.location.GnssMeasurement;
import android.location.GnssStatus;
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
@@ -39,7 +30,6 @@ import androidx.annotation.RequiresApi;
import com.android.gpstest.model.GnssType;
import com.android.gpstest.model.SatelliteName;
import com.android.gpstest.model.SatelliteStatus;
import com.android.gpstest.model.SbasType;
/**
* Utilities to manage GNSS signal and satellite information
@@ -48,64 +38,6 @@ public class SatelliteUtils {
private static final String TAG = "SatelliteUtils";
/**
* Returns the Global Navigation Satellite System (GNSS) for a satellite given the GnssStatus
* constellation type. For Android 7.0 and higher. This is basically a translation to our
* own GnssType enumeration that we use for Android 6.0.1 and lower. Note that
* getSbasConstellationType() should be used to get the particular SBAS constellation type
*
* @param gnssConstellationType constellation type provided by the GnssStatus.getConstellationType()
* method
* @return GnssType for the given GnssStatus constellation type
*/
public static GnssType getGnssConstellationType(int gnssConstellationType) {
switch (gnssConstellationType) {
case GnssStatus.CONSTELLATION_GPS:
return NAVSTAR;
case GnssStatus.CONSTELLATION_GLONASS:
return GLONASS;
case GnssStatus.CONSTELLATION_BEIDOU:
return BEIDOU;
case GnssStatus.CONSTELLATION_QZSS:
return QZSS;
case GnssStatus.CONSTELLATION_GALILEO:
return GALILEO;
case GnssStatus.CONSTELLATION_IRNSS:
return IRNSS;
case GnssStatus.CONSTELLATION_SBAS:
return SBAS;
case GnssStatus.CONSTELLATION_UNKNOWN:
return UNKNOWN;
default:
return UNKNOWN;
}
}
/**
* Returns the SBAS constellation type for a GnssStatus.CONSTELLATION_SBAS satellite given the GnssStatus
* svid. For Android 7.0 and higher.
*
* @param svid identification number provided by the GnssStatus.getSvid() method
* @return SbasType for the given GnssStatus svid for GnssStatus.CONSTELLATION_SBAS satellites
*/
public static SbasType getSbasConstellationType(int svid) {
if (svid == 120 || svid == 123 || svid == 126 || svid == 136) {
return SbasType.EGNOS;
} else if (svid == 125 || svid == 140 || svid == 141) {
return SbasType.SDCM;
} else if (svid == 130 || svid == 143 || svid == 144) {
// Also referred to as BDSBAS
return SbasType.SNAS;
} else if (svid == 131 || svid == 133 || svid == 135 || svid == 138) {
return SbasType.WAAS;
} else if (svid == 127 || svid == 128 || svid == 139) {
return SbasType.GAGAN;
} else if (svid == 129 || svid == 137) {
return SbasType.MSAS;
}
return SbasType.UNKNOWN;
}
/**
* Returns the satellite name for a satellite given the constellation type and svid. For
* Android 7.0 and higher.
@@ -177,26 +109,6 @@ public class SatelliteUtils {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
/**
* Returns true if the platform supports providing vertical accuracy values and this location
* has vertical accuracy information, false if it does not
*
* @return true if the platform supports providing vertical accuracy values and this location
* has vertical accuracy information, false if it does not
*/
public static boolean isVerticalAccuracySupported(Location location) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && location.hasVerticalAccuracy();
}
/**
* Returns true if the platform supports providing speed and bearing accuracy values, false if it does not
*
* @return true if the platform supports providing speed and bearing accuracy values, false if it does not
*/
public static boolean isSpeedAndBearingAccuracySupported() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
/**
* Returns true if automatic gain control is supported for this GNSS measurement, false if it is not
* @param gnssMeasurement

View File

@@ -91,8 +91,6 @@
<string name="pref_key_gps_min_distance">gps_min_distance</string>
<string name="pref_key_gnss_background">gnss_background</string>
pref_key_run_gnss_in_background
<string name="pref_key_display_category">display_category</string>
<string name="pref_key_keep_screen_on">keep_screen_on</string>
<string name="pref_key_true_north">true_north</string>
@@ -104,12 +102,17 @@
<string name="pref_key_as_navigation_message_output">navigation_message_output</string>
<string name="pref_key_file_output_category">file_output_category</string>
<string name="pref_key_inject_assist_data_when_logging_category">inject_assist_data_when_logging_category</string>
<string name="pref_key_file_nmea_output">file_nmea_output</string>
<string name="pref_key_file_measurement_output">file_measurement_output</string>
<string name="pref_key_file_navigation_message_output">file_navigation_message_output</string>
<string name="pref_key_file_antenna_output_json">file_antenna_output_json</string>
<string name="pref_key_file_antenna_output_csv">file_antenna_output_csv</string>
<string name="pref_key_file_location_output">file_location_output</string>
<string name="pref_key_file_gnss_status_output">file_gnss_status_output</string>
<string name="pref_key_file_orientation_output">file_orientation_output</string>
<string name="pref_key_inject_time_when_logging">inject_time_when_logging</string>
<string name="pref_key_inject_psds_when_logging">inject_psds_when_logging</string>
<string name="pref_key_about_category">about_category</string>
<string name="pref_key_showed_v2_tutorial">showed_v2_tutorial</string>

View File

@@ -329,6 +329,13 @@
</string>
<!-- File Output -->
<string name="pref_inject_assist_data_when_logging_category_title">Auto-inject assist data</string>
<string name="pref_inject_time_when_logging_title">Auto-inject time</string>
<string name="pref_inject_time_when_logging_summary">Automatically injects time data from a Network Time Protocol server when file logging starts</string>
<string name="pref_inject_psds_when_logging_title">Auto-inject PSDS</string>
<string name="pref_inject_psds_when_logging_summary">Automatically injects Predicted Satellite Data Service (e.g., XTRA) data when file logging starts</string>
<string name="pref_file_output_category_title">File Output</string>
<string name="pref_file_nmea_output_summary">Logs NMEA sentences to the CSV file</string>
<string name="pref_file_measurement_output_summary">Logs GNSS measurements to the CSV file (Android
@@ -342,7 +349,10 @@
<string name="pref_file_antenna_output_csv_summary">Logs GNSS antenna details to the CSV file. (Android 11 and up, and only available on some devices)</string>
<string name="pref_file_antenna_output_json_title">Antenna Info (JSON)</string>
<string name="pref_file_antenna_output_json_summary">Logs GNSS antenna details to the JSON file. (Android 11 and up, and only available on some devices)</string>
<string name="pref_file_gnss_status_output_title">GnssStatus</string>
<string name="pref_file_gnss_status_output_summary">Logs GnssStatus data per signal to the CSV file</string>
<string name="pref_file_orientation_output_title">Orientation</string>
<string name="pref_file_orientation_output_summary">Logs orientation sensor data to the CSV file</string>
<string name="logging_to_new_file">Logging to new file: %1$s</string>
<string name="unable_to_close_all_file_streams">Unable to close all file streams.</string>
<string name="could_not_initialize_file">Count not initialize file: %1$s</string>

View File

@@ -94,21 +94,31 @@
android:title="@string/pref_nmea_output_title"
android:summary="@string/pref_file_nmea_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_file_navigation_message_output"
android:title="@string/pref_navigation_message_output_title"
android:summary="@string/pref_file_navigation_message_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_file_measurement_output"
android:title="@string/pref_measurement_output_title"
android:summary="@string/pref_file_measurement_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_file_gnss_status_output"
android:title="@string/pref_file_gnss_status_output_title"
android:summary="@string/pref_file_gnss_status_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_file_orientation_output"
android:title="@string/pref_file_orientation_output_title"
android:summary="@string/pref_file_orientation_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_file_location_output"
android:title="@string/pref_file_location_output_title"
android:summary="@string/pref_file_location_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_file_navigation_message_output"
android:title="@string/pref_navigation_message_output_title"
android:summary="@string/pref_file_navigation_message_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_file_antenna_output_csv"
android:title="@string/pref_file_antenna_output_csv_title"
@@ -121,6 +131,21 @@
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/pref_inject_assist_data_when_logging_category_title"
android:key="@string/pref_key_inject_assist_data_when_logging_category">
<CheckBoxPreference
android:key="@string/pref_key_inject_time_when_logging"
android:title="@string/pref_inject_time_when_logging_title"
android:summary="@string/pref_inject_time_when_logging_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="@string/pref_key_inject_psds_when_logging"
android:title="@string/pref_inject_psds_when_logging_title"
android:summary="@string/pref_inject_psds_when_logging_summary"
android:defaultValue="true" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/pref_as_output_category_title"
android:key="@string/pref_key_as_android_monitor_category">
@@ -134,16 +159,16 @@
android:title="@string/pref_nmea_timestamp_output_title"
android:summary="@string/pref_nmea_timestamp_output_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="@string/pref_key_as_navigation_message_output"
android:title="@string/pref_navigation_message_output_title"
android:summary="@string/pref_as_navigation_message_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_as_measurement_output"
android:title="@string/pref_measurement_output_title"
android:summary="@string/pref_as_measurement_output_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/pref_key_as_navigation_message_output"
android:title="@string/pref_navigation_message_output_title"
android:summary="@string/pref_as_navigation_message_output_summary"
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>

View File

@@ -297,8 +297,8 @@ class MapFragment : Fragment(), MapInterface {
sensorFlow = repository.getSensorUpdates()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach {
//Log.d(TAG, "Map sensor: orientation ${it.orientation}, tilt ${it.tilt}")
onOrientationChanged(it.orientation, it.tilt)
//Log.d(TAG, "Map sensor: orientation ${it.values[0]}, tilt ${it.values[1]}")
onOrientationChanged(it.values[0], it.values[1])
}
.launchIn(lifecycleScope)
}

View File

@@ -15,12 +15,12 @@
*/
package com.android.gpstest.util;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import org.junit.Test;
public class MathUtilTest {
/**
@@ -28,8 +28,8 @@ public class MathUtilTest {
*/
@Test
public void testToMhz() {
float mhz = MathUtils.toMhz(1000000.0f);
assertEquals(1.0f, mhz);
double mhz = MathUtils.toMhz(1000000.0);
assertEquals(1.0, mhz);
}
/**

165
LOGGING-v3.md Normal file
View File

@@ -0,0 +1,165 @@
# Data Output and Logging - v3 and lower
**NOTE** This page contains logging information for older versions of GPSTest (v3 and lower).
***Please see [the main Logging documentation](/LOGGING.md) for the current version of GPSTest.***
GPSTest allows you to output raw data about GNSS/GPS to files as well as the Android system log, which can be used to visualize the data using tools like Google's [GPS Measurement Tools project](https://github.com/google/gps-measurement-tools) shown below. The following sections discuss how to access these logs, and details about the data that is output.
![image](https://user-images.githubusercontent.com/928045/64804800-844fae00-d55d-11e9-8212-b0ef65885dc7.png)
## Logging data to files
You can enable file logging in GPSTest via the Settings. By default file logging is turned off for all data to avoid unnecessarily filling up storage space.
Steps to log and view data:
1. In the GPSTest app, go to "Settings", scroll down, tap on "Logging and Output" and under "File Output" make sure the box is checked for each data output type you'd like to see (see next sections).
1. You can share CSV and JSON log files via the "Share" button in the top action bar of the app, or by copying them off internal memory or SD card over USB.
*Note: The file output from GPSTest is buffered, so if you manually copy the file off the device while the app is running you can end up with a partial file if the buffer hasn't been fully flushed. You can force the flush of the buffer by killing the app (e.g., via the Back button), and then if you refresh your file view you should get the complete file with all your data.*
#### Data output - CSV
Here are the details of file logging:
* CSV format - Same file format as the Google [GPS Measurement Tools (GNSS Logger) project](https://github.com/google/gps-measurement-tools) so you can use MATLAB tools under that project to [analyze the data](https://github.com/google/gps-measurement-tools#to-process-a-log-file-you-collected-from-gnsslogger).
* Files are saved to your Android device storage under the `gnss_log` directory.
* After you've enabled logging via Settings, each time you start the app a new file will be created named with the date and time (e.g., `gnss_log_2019_09_11_13_09_50.txt`). If you end the app (e.g., hit back button) and restart it, another file gets created.
* Each row of the file is prefixed with a string designating the data type:
* `Raw` - Raw GNSS measurements
* `Fix` - Location fix information
* `Nav` - Navigation message
* `NMEA` - NMEA sentences
* `GnssAntennaInfo` - [Antenna characteristics](https://developer.android.com/reference/android/location/GnssAntennaInfo) for the device model. Available on supported devices (e.g., Pixel 5) with Android 11 and higher.
The header of the CSV file explains the format for each data type:
~~~
# Raw GNSS measurements format (Android 7.0 and higher on supported devices):
# Raw,ElapsedRealtimeMillis,TimeNanos,LeapSecond,TimeUncertaintyNanos,FullBiasNanos,BiasNanos,BiasUncertaintyNanos,DriftNanosPerSecond,DriftUncertaintyNanosPerSecond,HardwareClockDiscontinuityCount,Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,PseudorangeRateUncertaintyMetersPerSecond,AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,AccumulatedDeltaRangeUncertaintyMeters,CarrierFrequencyHz,CarrierCycles,CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,ConstellationType,AgcDb,CarrierFrequencyHz
#
# Location fix format (all devices):
# Fix,Provider,Latitude,Longitude,Altitude,Speed,Accuracy,(UTC)TimeInMs
#
# Navigation message format (Android 7.0 and higher on supported devices):
# Nav,Svid,Type,Status,MessageId,Sub-messageId,Data(Bytes)
#
# NMEA format (Android 2.3 and higher) (for [NMEA sentence] format see https://www.gpsinformation.org/dale/nmea.htm):
# NMEA,[NMEA sentence],timestamp
#
# GnssAntennaInfo format (https://developer.android.com/reference/android/location/GnssAntennaInfo):
# GnssAntennaInfo,CarrierFrequencyMHz,PhaseCenterOffsetXOffsetMm,PhaseCenterOffsetXOffsetUncertaintyMm,PhaseCenterOffsetYOffsetMm,PhaseCenterOffsetYOffsetUncertaintyMm,PhaseCenterOffsetZOffsetMm,PhaseCenterOffsetZOffsetUncertaintyMm,PhaseCenterVariationCorrectionsArray,PhaseCenterVariationCorrectionUncertaintiesArray,PhaseCenterVariationCorrectionsDeltaPhi,PhaseCenterVariationCorrectionsDeltaTheta,SignalGainCorrectionsArray,SignalGainCorrectionUncertaintiesArray,SignalGainCorrectionsDeltaPhi,SignalGainCorrectionsDeltaTheta
~~~
Sample data looks like:
~~~
NMEA,$GNGSA,A,2,66,81,87,,,,,,,,,,1.3,1.0,0.9,2*3F,1568222348217
NMEA,$GNVTG,,T,,M,0.0,N,0.0,K,A*3D,1568222348218
NMEA,$GNGGA,171859.00,2804.281311,N,08225.605044,W,1,07,1.0,39.1,M,-24.8,M,,*75,1568222348220
NMEA,$GNRMC,171859.00,A,2804.281311,N,08225.605044,W,0.0,,110919,3.1,W,A,V*77,1568222348220
Raw,1257164406,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,2,0.0,207,73139369397323,271,22.8,-556.3143920898438,10.200000762939453,0,0.0,0.0,,,,,0,,3,,
Raw,1257164408,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,23,0.0,207,73139369842860,1523,19.3,182.0135955810547,10.510000228881836,0,0.0,0.0,,,,,0,,3,,
Raw,1257164411,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,17,0.0,207,73139361386059,1192,21.2,-592.7639770507812,10.278000831604004,0,0.0,0.0,,,,,0,,3,,
Raw,1257164412,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,5,0.0,15,321557370216944,1041,20.3,208.6379852294922,9.641500473022461,0,0.0,0.0,,,,,0,,1,,
Raw,1257164413,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,6,0.0,15,321557356784894,926,21.9,394.1838073730469,9.560500144958496,0,0.0,0.0,,,,,0,,1,,
Raw,1257164413,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,12,0.0,15,321557361694004,825,22.2,557.084716796875,9.458000183105469,0,0.0,0.0,,,,,0,,1,,
Raw,1257164414,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,13,0.0,15,321557363597446,1504,17.2,-508.64544677734375,9.940999984741211,0,0.0,0.0,,,,,0,,1,,
Raw,1257164415,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,15,0.0,15,321557358793297,826,22.0,-689.0819702148438,9.483500480651855,0,0.0,0.0,,,,,0,,1,,
Fix,gps,28.071355,-82.426751,14.320496,0.000000,38.592003,1568222340000
GnssAntennaInfo,1575.42,1.2,0.1,3.4,0.2,5.6,0.3,[11.22 33.44 55.66 77.88; 10.2 30.4 50.6 70.8; 12.2 34.4 56.6 78.8],[0.1 0.2 0.3 0.4; 1.1 1.2 1.3 1.4; 2.1 2.2 2.3 2.4],60.0,120.0,[9.8 8.7 7.6 6.5; 5.4 4.3 3.2 2.1; 1.3 2.4 3.5 4.6],[0.11 0.22 0.33 0.44; 0.55 0.66 0.77 0.88; 0.91 0.92 0.93 0.94],60.0,120.0
GnssAntennaInfo,1227.6,3.4,0.2,5.6,0.3,1.2,0.1,[55.66 77.88; 11.22 33.44; 56.6 78.8; 12.2 34.4],[0.3 0.4; 1.1 1.2; 2.1 2.2; 0.1 0.2],180.0,90.0,[7.6 6.5; 5.4 4.3; 1.3 2.4; 9.8 8.7],[0.91 0.92; 0.55 0.66; 0.11 0.22; 0.93 0.94],180.0,90.0
~~~
#### Data output - JSON
[GnssAntennaInfo](https://developer.android.com/reference/android/location/GnssAntennaInfo) logging is available on supported devices (e.g., Pixel 5) with Android 11 and is also logged in the JSON format. GNSS antenna(s) characteristics, such as phase center offset (PCO) coordinates, phase center variation (PCV) corrections, and signal gain corrections can be applied to the raw measurements to improve accuracy.
Logging works similar to the CSV file process, with a file name like `gnss_log_2019_09_11_13_09_50.json`. Here's example data from a Pixel 5:
~~~
[
{
"carrierFrequencyMHz":1575.42,
"phaseCenterOffset":{
"xoffsetMm":1.2,
"xoffsetUncertaintyMm":0.1,
"yoffsetMm":3.4,
"yoffsetUncertaintyMm":0.2,
"zoffsetMm":5.6,
"zoffsetUncertaintyMm":0.3
},
"phaseCenterVariationCorrections":{
"correctionUncertaintiesArray":[
[ 0.1, 0.2, 0.3, 0.4 ],
[ 1.1, 1.2, 1.3, 1.4 ],
[ 2.1, 2.2, 2.3, 2.4 ]
],
"correctionsArray":[
[ 11.22, 33.44, 55.66, 77.88 ],
[ 10.2, 30.4, 50.6, 70.8 ],
[ 12.2, 34.4, 56.6, 78.8 ]
],
"deltaPhi":60.0,
"deltaTheta":120.0
},
"signalGainCorrections":{
"correctionUncertaintiesArray":[
[ 0.11, 0.22, 0.33, 0.44 ],
[ 0.55, 0.66, 0.77, 0.88 ],
[ 0.91, 0.92, 0.93, 0.94 ]
],
"correctionsArray":[
[ 9.8, 8.7, 7.6, 6.5 ],
[ 5.4, 4.3, 3.2, 2.1 ],
[ 1.3, 2.4, 3.5, 4.6 ]
],
"deltaPhi":60.0,
"deltaTheta":120.0
}
}
...
]
~~~
#### Data output - RINEX
Are you interested in data in the RINEX format? GPSTest doesn't natively support RINEX, but you can use the below tool to convert from the above CSV format to RINEX:
https://github.com/rokubun/android_rinex
#### Data Analysis
Use the CSV file output from GPSTest along with the Google [GPS Measurement Tools project](https://github.com/google/gps-measurement-tools) to analyze the data (as of late October 2020 supported for [GnssAntennaInfo](https://developer.android.com/reference/android/location/GnssAntennaInfo) doesn't seem to exist).
For details see the Android documentation:
* ["GNSS Measurements - Analyzing raw measurements"](https://developer.android.com/guide/topics/sensors/gnss#analyze)
Here's a screenshot from the GPS Measurement Tools desktop software:
![image](https://user-images.githubusercontent.com/928045/64804800-844fae00-d55d-11e9-8212-b0ef65885dc7.png)
## Accessing the system log via Android Studio
You can view the data output from GPSTest by using Android Monitor, which is included with Android Studio.
Steps to view data:
1. Install [Android Studio](https://developer.android.com/studio/index.html)
1. [Enable USB debugging](https://developer.android.com/studio/run/device.html#developer-device-options) on your device
1. In [Android Monitor](https://developer.android.com/studio/profile/android-monitor.html), in the drop down box on far right side, select [`No Filters`](https://developer.android.com/studio/debug/am-logcat.html#filtering).
1. In Android Monitor, in the [search box](https://developer.android.com/studio/debug/am-logcat.html#searching) with magnifying glass, enter `GpsOutput` to filter out all other system output.
1. In the GPSTest app, go to "Settings", scroll down, tap on "Logging and Output" and under "Android Monitor Output" make sure the box is checked for each data output type you'd like to see (see next section).
#### Data output
Android 2.3 and higher:
* **NMEA output** - NMEA strings. Enabled by default for Android Studio. To show only this output in Android Monitor, use the search box text `GpsOutputNmea`.
Android 7.0 and higher:
* **GNSS Measurements** - Raw GNSS satellite measurements observed by the GNSS subsystem. Disabled by default for Android Studio. To show only this output in Android Monitor, use the search box text `GpsOutputMeasure`.
* **GNSS Navigation Message** - Navigation messages observed by the GNSS subsystem. Disabled by default for Android Studio. To show only this output in Android Monitor, use the search box text `GpsOutputNav`.
## What devices support pseudorange measurements and navigation messages?
Check out the [FAQ](https://github.com/barbeau/gpstest/blob/master/FAQ.md#what-android-70-devices-support-logging-raw-pseudorange-and-navigation-messages) for known device information.

View File

@@ -1,4 +1,4 @@
# Data Output and Logging
# Data Output and Logging (GPSTest v4 and higher)
GPSTest allows you to output raw data about GNSS/GPS to files as well as the Android system log, which can be used to visualize the data using tools like Google's [GPS Measurement Tools project](https://github.com/google/gps-measurement-tools) shown below. The following sections discuss how to access these logs, and details about the data that is output.
@@ -26,24 +26,31 @@ Here are the details of file logging:
* `Nav` - Navigation message
* `NMEA` - NMEA sentences
* `GnssAntennaInfo` - [Antenna characteristics](https://developer.android.com/reference/android/location/GnssAntennaInfo) for the device model. Available on supported devices (e.g., Pixel 5) with Android 11 and higher.
* `Status` - [GnssStatus](https://developer.android.com/reference/android/location/GnssStatus) per signal
* `OrientationDeg` - [Orientation sensor](https://developer.android.com/reference/android/hardware/SensorEvent#values) data
The header of the CSV file explains the format for each data type:
~~~
# Raw GNSS measurements format (Android 7.0 and higher on supported devices):
# Raw,ElapsedRealtimeMillis,TimeNanos,LeapSecond,TimeUncertaintyNanos,FullBiasNanos,BiasNanos,BiasUncertaintyNanos,DriftNanosPerSecond,DriftUncertaintyNanosPerSecond,HardwareClockDiscontinuityCount,Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,PseudorangeRateUncertaintyMetersPerSecond,AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,AccumulatedDeltaRangeUncertaintyMeters,CarrierFrequencyHz,CarrierCycles,CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,ConstellationType,AgcDb,CarrierFrequencyHz
#
# Location fix format (all devices):
# Fix,Provider,Latitude,Longitude,Altitude,Speed,Accuracy,(UTC)TimeInMs
#
# Navigation message format (Android 7.0 and higher on supported devices):
# Raw GNSS measurements format:
# Raw,utcTimeMillis,TimeNanos,LeapSecond,TimeUncertaintyNanos,FullBiasNanos,BiasNanos,BiasUncertaintyNanos,DriftNanosPerSecond,DriftUncertaintyNanosPerSecond,HardwareClockDiscontinuityCount,Svid,TimeOffsetNanos,State,ReceivedSvTimeNanos,ReceivedSvTimeUncertaintyNanos,Cn0DbHz,PseudorangeRateMetersPerSecond,PseudorangeRateUncertaintyMetersPerSecond,AccumulatedDeltaRangeState,AccumulatedDeltaRangeMeters,AccumulatedDeltaRangeUncertaintyMeters,CarrierFrequencyHz,CarrierCycles,CarrierPhase,CarrierPhaseUncertainty,MultipathIndicator,SnrInDb,ConstellationType,AgcDb,BasebandCn0DbHz,FullInterSignalBiasNanos,FullInterSignalBiasUncertaintyNanos,SatelliteInterSignalBiasNanos,SatelliteInterSignalBiasUncertaintyNanos,CodeType,ChipsetElapsedRealtimeNanos
#
# Location fix format:
# Fix,Provider,LatitudeDegrees,LongitudeDegrees,AltitudeMeters,SpeedMps,AccuracyMeters,BearingDegrees,UnixTimeMillis,SpeedAccuracyMps,BearingAccuracyDegrees,elapsedRealtimeNanos,VerticalAccuracyMeters
#
# Navigation message format:
# Nav,Svid,Type,Status,MessageId,Sub-messageId,Data(Bytes)
#
# NMEA format (Android 2.3 and higher) (for [NMEA sentence] format see https://www.gpsinformation.org/dale/nmea.htm):
# NMEA,[NMEA sentence],timestamp
#
# NMEA format (for [NMEA sentence] format see https://en.wikipedia.org/wiki/NMEA_0183):
# NMEA,[NMEA sentence],(UTC)TimeInMs
#
# GnssAntennaInfo format (https://developer.android.com/reference/android/location/GnssAntennaInfo):
# GnssAntennaInfo,CarrierFrequencyMHz,PhaseCenterOffsetXOffsetMm,PhaseCenterOffsetXOffsetUncertaintyMm,PhaseCenterOffsetYOffsetMm,PhaseCenterOffsetYOffsetUncertaintyMm,PhaseCenterOffsetZOffsetMm,PhaseCenterOffsetZOffsetUncertaintyMm,PhaseCenterVariationCorrectionsArray,PhaseCenterVariationCorrectionUncertaintiesArray,PhaseCenterVariationCorrectionsDeltaPhi,PhaseCenterVariationCorrectionsDeltaTheta,SignalGainCorrectionsArray,SignalGainCorrectionUncertaintiesArray,SignalGainCorrectionsDeltaPhi,SignalGainCorrectionsDeltaTheta
#
# GnssStatus format (https://developer.android.com/reference/android/location/GnssStatus):
# Status,UnixTimeMillis,SignalCount,SignalIndex,ConstellationType,Svid,CarrierFrequencyHz,Cn0DbHz,AzimuthDegrees,ElevationDegrees,UsedInFix,HasAlmanacData,HasEphemerisData,BasebandCn0DbHz
# Orientation sensor format (https://developer.android.com/reference/android/hardware/SensorEvent#values):
# OrientationDeg,utcTimeMillis,elapsedRealtimeNanos,yawDeg,rollDeg,pitchDeg
~~~
Sample data looks like:
@@ -53,19 +60,90 @@ NMEA,$GNGSA,A,2,66,81,87,,,,,,,,,,1.3,1.0,0.9,2*3F,1568222348217
NMEA,$GNVTG,,T,,M,0.0,N,0.0,K,A*3D,1568222348218
NMEA,$GNGGA,171859.00,2804.281311,N,08225.605044,W,1,07,1.0,39.1,M,-24.8,M,,*75,1568222348220
NMEA,$GNRMC,171859.00,A,2804.281311,N,08225.605044,W,0.0,,110919,3.1,W,A,V*77,1568222348220
Raw,1257164406,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,2,0.0,207,73139369397323,271,22.8,-556.3143920898438,10.200000762939453,0,0.0,0.0,,,,,0,,3,,
Raw,1257164408,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,23,0.0,207,73139369842860,1523,19.3,182.0135955810547,10.510000228881836,0,0.0,0.0,,,,,0,,3,,
Raw,1257164411,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,17,0.0,207,73139361386059,1192,21.2,-592.7639770507812,10.278000831604004,0,0.0,0.0,,,,,0,,3,,
Raw,1257164412,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,5,0.0,15,321557370216944,1041,20.3,208.6379852294922,9.641500473022461,0,0.0,0.0,,,,,0,,1,,
Raw,1257164413,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,6,0.0,15,321557356784894,926,21.9,394.1838073730469,9.560500144958496,0,0.0,0.0,,,,,0,,1,,
Raw,1257164413,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,12,0.0,15,321557361694004,825,22.2,557.084716796875,9.458000183105469,0,0.0,0.0,,,,,0,,1,,
Raw,1257164414,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,13,0.0,15,321557363597446,1504,17.2,-508.64544677734375,9.940999984741211,0,0.0,0.0,,,,,0,,1,,
Raw,1257164415,126692640000000,,,-1252130864797923510,0.9759163856506348,571.2438141927123,74.13293543922987,35.96258578603258,729,15,0.0,15,321557358793297,826,22.0,-689.0819702148438,9.483500480651855,0,0.0,0.0,,,,,0,,1,,
Fix,gps,28.071355,-82.426751,14.320496,0.000000,38.592003,1568222340000
Raw,1308593941,1258975580000000,18,,-1320040982834296519-0.42849159240722656,249.35539113357663,-16.601702122755693,22.49701461923959,1417,1,0.0,16399,416758333755552,489,29.8,255.40264892578125,1.5410001277923584,16,0.0,0.0,1575420030,,,,0,,1,0.26,24.8,0.0,0.0,,,C,1308593941103684
Raw,1308593941,1258975580000000,18,,-1320040982834296519-0.42849159240722656,249.35539113357663,-16.601702122755693,22.49701461923959,1417,16,0.0,16399,416758334210563,425,30.0,-752.9444580078125,1.4825000762939453,16,0.0,0.0,1575420030,,,,0,,1,0.26,25.0,0.0,0.0,,,C,1308593941407851
Raw,1308593941,1258975580000000,18,,-1320040982834296519-0.42849159240722656,249.35539113357663,-16.601702122755693,22.49701461923959,1417,22,0.0,16399,416758339876604,278,33.3,-357.5998840332031,1.2899999618530273,16,0.0,0.0,1575420030,,,,0,,1,0.26,28.299999999999997,0.0,0.0,,,C,1308593941522799
Raw,1308593941,1258975580000000,18,,-1320040982834296519-0.42849159240722656,249.35539113357663,-16.601702122755693,22.49701461923959,1417,25,0.0,16399,416758336060464,368,31.7,577.1130981445312,1.4255000352859497,16,0.0,0.0,1575420030,,,,0,,1,0.26,26.7,0.0,0.0,,,C,1308593941624153
Raw,1308593941,1258975580000000,18,,-1320040982834296519-0.42849159240722656,249.35539113357663,-16.601702122755693,22.49701461923959,1417,32,0.0,16399,416758342536604,12,42.5,167.77867126464844,0.047997571527957916,16,0.0,0.0,1575420030,,,,0,,1,0.26,37.5,0.0,0.0,,,C,1308593941724570
Raw,1308593941,1258975580000000,18,,-1320040982834296519-0.42849159240722656,249.35539113357663,-16.601702122755693,22.49701461923959,1417,9,0.0,49359,81940349009787,564,28.4,-236.4537811279297,1.725000023841858,16,0.0,0.0,1600875010,,,,0,,3,-0.68,24.4,3825.078857421875,17.50518035888672,,,C,1308593941811497
GnssAntennaInfo,1575.42,1.2,0.1,3.4,0.2,5.6,0.3,[11.22 33.44 55.66 77.88; 10.2 30.4 50.6 70.8; 12.2 34.4 56.6 78.8],[0.1 0.2 0.3 0.4; 1.1 1.2 1.3 1.4; 2.1 2.2 2.3 2.4],60.0,120.0,[9.8 8.7 7.6 6.5; 5.4 4.3 3.2 2.1; 1.3 2.4 3.5 4.6],[0.11 0.22 0.33 0.44; 0.55 0.66 0.77 0.88; 0.91 0.92 0.93 0.94],60.0,120.0
GnssAntennaInfo,1227.6,3.4,0.2,5.6,0.3,1.2,0.1,[55.66 77.88; 11.22 33.44; 56.6 78.8; 12.2 34.4],[0.3 0.4; 1.1 1.2; 2.1 2.2; 0.1 0.2],180.0,90.0,[7.6 6.5; 5.4 4.3; 1.3 2.4; 9.8 8.7],[0.91 0.92; 0.55 0.66; 0.11 0.22; 0.93 0.94],180.0,90.0
OrientationDeg,1637264740657,1308593196466709,123.92464891404495,0.3803144045376981,-0.3826964863715929
OrientationDeg,1637264740662,1308593201310095,123.9245396310182,0.38081343523393213,-0.383448687634367
OrientationDeg,1637264740667,1308593206153428,123.92448498950489,0.3812450711729652,-0.3836270729655807
Status,0,0,13,1,1,1575420032,25.5,273.0,14.0,0,1,0,20.5
Status,0,1,13,1,16,1575420032,24.7,193.0,17.0,0,1,0,19.7
Status,0,2,13,1,22,1575420032,27.5,304.0,40.0,0,1,0,22.5
Status,0,3,13,1,25,1575420032,28.3,42.0,19.0,0,1,0,23.3
Status,0,4,13,1,26,1575420032,22.6,174.0,55.0,0,1,0,17.6
Status,0,5,13,1,32,1575420032,38.1,74.0,47.0,0,1,1,33.1
Fix,gps,28.07124449,-82.42663169,-16.32806396484375,0.0,21.196445,0.0,1637264742000,1.9549425,,1308594915408632,13.918213
~~~
Google's documentation for the [Smartphone Decimeter Challenge](https://www.kaggle.com/c/google-smartphone-decimeter-challenge/data#) has additional information.
Here are a few more additional pieces of information on the following formats, which is copied from the above page.
`Raw` - The raw GNSS measurements of one GNSS signal (each satellite may have 1-2 signals for L5-enabled smartphones), collected from the Android API GnssMeasurement:
* utcTimeMillis - Milliseconds since UTC epoch (1970/1/1), converted from GnssClock
* TimeNanos - The GNSS receiver internal hardware clock value in nanoseconds.
* LeapSecond - The leap second associated with the clock's time.
* TimeUncertaintyNanos - The clock's time uncertainty (1-sigma) in nanoseconds.
* FullBiasNanos - The difference between hardware clock getTimeNanos() inside GPS receiver and the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
* BiasNanos - The clock's sub-nanosecond bias.
* BiasUncertaintyNanos - The clock's bias uncertainty (1-sigma) in nanoseconds.
* DriftNanosPerSecond - The clock's drift in nanoseconds per second.
* DriftUncertaintyNanosPerSecond - The clock's drift uncertainty (1-sigma) in nanoseconds per second.
* HardwareClockDiscontinuityCount - Count of hardware clock discontinuities.
* Svid - The satellite ID. More info can be found here.
* TimeOffsetNanos - The time offset at which the measurement was taken in nanoseconds.
* State - Integer signifying sync state of the satellite. Each bit in the integer attributes to a particular state information of the measurement. See the metadata/raw_state_bit_map.json file for the mapping between bits and states.
* ReceivedSvTimeNanos - The received GNSS satellite time, at the measurement time, in nanoseconds.
* ReceivedSvTimeUncertaintyNanos - The error estimate (1-sigma) for the received GNSS time, in nanoseconds.
* Cn0DbHz - The carrier-to-noise density in dB-Hz.
* PseudorangeRateMetersPerSecond - The pseudorange rate at the timestamp in m/s.
* PseudorangeRateUncertaintyMetersPerSecond - The pseudorange's rate uncertainty (1-sigma) in m/s.
* AccumulatedDeltaRangeState - This indicates the state of the 'Accumulated Delta Range' measurement. Each bit in the integer attributes to state of the measurement. See the metadata/accumulated_delta_range_state_bit_map.json file for the mapping between bits and states.
* AccumulatedDeltaRangeMeters - The accumulated delta range since the last channel reset, in meters.
* AccumulatedDeltaRangeUncertaintyMeters - The accumulated delta range's uncertainty (1-sigma) in meters.
* CarrierFrequencyHz - The carrier frequency of the tracked signal.
* CarrierCycles - The number of full carrier cycles between the satellite and the receiver. Null in these datasets.
* CarrierPhase - The RF phase detected by the receiver. Null in these datasets.
* CarrierPhaseUncertainty - The carrier-phase's uncertainty (1-sigma). Null in these datasets.
* MultipathIndicator - A value indicating the 'multipath' state of the event.
* SnrInDb - The (post-correlation & integration) Signal-to-Noise ratio (SNR) in dB.
* ConstellationType - GNSS constellation type. It's an integer number, whose mapping to string value is provided in the constellation_type_mapping.csv file.
* AgcDb - The Automatic Gain Control level in dB.
* BasebandCn0DbHz - The baseband carrier-to-noise density in dB-Hz. Only available in Android 11.
* FullInterSignalBiasNanos - The GNSS measurement's inter-signal bias in nanoseconds with sub-nanosecond accuracy. Only available in Android 11.
* FullInterSignalBiasUncertaintyNanos - The GNSS measurement's inter-signal bias uncertainty (1 sigma) in nanoseconds with sub-nanosecond accuracy. Only available in Android 11.
* SatelliteInterSignalBiasNanos - The GNSS measurement's satellite inter-signal bias in nanoseconds with sub-nanosecond accuracy. Only available in Android 11.
* SatelliteInterSignalBiasUncertaintyNanos - The GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in nanoseconds with sub-nanosecond accuracy. Only available in Android 11.
* CodeType - The GNSS measurement's code type. Only available in recent logs.
* ChipsetElapsedRealtimeNanos - The elapsed real-time of this clock since system boot, in nanoseconds. Only available in recent logs.
`Status` - The status of a GNSS signal, as collected from the Android API GnssStatus.
* UnixTimeMillis - Milliseconds since UTC epoch (1970/1/1), reported from the last location changed by GPS provider.
* SignalCount - The total number of satellites in the satellite list.
* SignalIndex - The index of current signal.
* ConstellationType: The constellation type of the satellite at the specified index.
* Svid: The satellite ID.
* CarrierFrequencyHz: The carrier frequency of the signal tracked.
* Cn0DbHz: The carrier-to-noise density at the antenna of the satellite at the specified index in dB-Hz.
* AzimuthDegrees: The azimuth the satellite at the specified index.
* ElevationDegrees: The elevation of the satellite at the specified index.
* UsedInFix: Whether the satellite at the specified index was used in the calculation of the most recent position fix (`0` or `1`).
* HasAlmanacData: Whether the satellite at the specified index has almanac data (`0` or `1`).
* HasEphemerisData: Whether the satellite at the specified index has ephemeris data (`0` or `1`).
* BasebandCn0DbHz: The baseband carrier-to-noise density of the satellite at the specified index in dB-Hz.
`OrientationDeg` - Each row represents an estimated device orientation, collected from Android API SensorManager#getOrientation:
* utcTimeMillis - The sum of elapsedRealtimeNanos below and the estimated device boot time at UTC, after a recent NTP (Network Time Protocol) sync.
* elapsedRealtimeNanos - The time in nanoseconds at which the event happened.
* yawDeg - If the screen is in portrait mode, this value equals the Azimuth degree (modulus to 0° and 360°). If the screen is in landscape mode, it equals the sum (modulus to 0° and 360°) of the screen rotation angle (either 90° or 270°) and the Azimuth degree. Azimuth, refers to the angle of rotation about the -z axis. This value represents the angle between the device's y axis and the magnetic north pole.
* rollDeg - Roll, angle of rotation about the y axis. This value represents the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground.
* pitchDeg - Pitch, angle of rotation about the x axis. This value represents the angle between a plane parallel to the device's screen and a plane parallel to the ground.
#### Data output - JSON
[GnssAntennaInfo](https://developer.android.com/reference/android/location/GnssAntennaInfo) logging is available on supported devices (e.g., Pixel 5) with Android 11 and is also logged in the JSON format. GNSS antenna(s) characteristics, such as phase center offset (PCO) coordinates, phase center variation (PCV) corrections, and signal gain corrections can be applied to the raw measurements to improve accuracy.
@@ -158,4 +236,8 @@ Android 7.0 and higher:
## What devices support pseudorange measurements and navigation messages?
Check out the [FAQ](https://github.com/barbeau/gpstest/blob/master/FAQ.md#what-android-70-devices-support-logging-raw-pseudorange-and-navigation-messages) for known device information.
Check out the [FAQ](https://github.com/barbeau/gpstest/blob/master/FAQ.md#what-android-70-devices-support-logging-raw-pseudorange-and-navigation-messages) for known device information.
## Where can I find logging documentation for older versions of GPSTest (v3 and lower)?
The documentation on this page is for GPSTest v4 and higher. Check out [this page](/LOGGING-v3.md).