DfAC 8 years ago
parent
commit
c6633cdfb4
  1. 9
      GNSSLogger/.gitignore
  2. 19
      GNSSLogger/GNSSLogger.iml
  3. 1
      GNSSLogger/app/.gitignore
  4. 148
      GNSSLogger/app/app.iml
  5. 31
      GNSSLogger/app/build.gradle
  6. 17
      GNSSLogger/app/proguard-rules.pro
  7. 23
      GNSSLogger/app/src/main/AndroidManifest.xml
  8. 401
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/FileLogger.java
  9. 289
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssContainer.java
  10. 58
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssListener.java
  11. 83
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/HelpDialog.java
  12. 163
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/LoggerFragment.java
  13. 172
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/MainActivity.java
  14. 219
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/SettingsFragment.java
  15. 220
      GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/UiLogger.java
  16. BIN
      GNSSLogger/app/src/main/res/drawable/ic_launcher.png
  17. 19
      GNSSLogger/app/src/main/res/layout/activity_main.xml
  18. 67
      GNSSLogger/app/src/main/res/layout/fragment_log.xml
  19. 155
      GNSSLogger/app/src/main/res/layout/fragment_main.xml
  20. 13
      GNSSLogger/app/src/main/res/layout/help.xml
  21. BIN
      GNSSLogger/app/src/main/res/mipmap-hdpi/ic_launcher.png
  22. BIN
      GNSSLogger/app/src/main/res/mipmap-mdpi/ic_launcher.png
  23. BIN
      GNSSLogger/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  24. BIN
      GNSSLogger/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  25. BIN
      GNSSLogger/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  26. 20
      GNSSLogger/app/src/main/res/raw/help_contents.txt
  27. 6
      GNSSLogger/app/src/main/res/values-w820dp/dimens.xml
  28. 6
      GNSSLogger/app/src/main/res/values/colors.xml
  29. 6
      GNSSLogger/app/src/main/res/values/dimens.xml
  30. 19
      GNSSLogger/app/src/main/res/values/strings.xml
  31. 5
      GNSSLogger/app/src/main/res/values/styles.xml
  32. 23
      GNSSLogger/build.gradle
  33. 17
      GNSSLogger/gradle.properties
  34. 160
      GNSSLogger/gradlew
  35. 90
      GNSSLogger/gradlew.bat
  36. 10
      GNSSLogger/local.properties
  37. 1
      GNSSLogger/settings.gradle
  38. 7
      opensource/PlotPseudorangeRates.m
  39. 9
      opensource/PlotPvt.m
  40. 2
      opensource/ProcessGnssMeasScript.m
  41. 20
      opensource/README.md
  42. 21
      opensource/ReadGnssLogger.m
  43. 13
      opensource/SetDataFilter.m

9
GNSSLogger/.gitignore vendored

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

19
GNSSLogger/GNSSLogger.iml

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="GNSSLogger" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

1
GNSSLogger/app/.gitignore vendored

@ -0,0 +1 @@
/build

148
GNSSLogger/app/app.iml

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test.espresso/espresso-core/2.2.2/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test.espresso/espresso-idling-resource/2.2.2/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test/exposed-instrumentation-api-publish/0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test/rules/0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test/runner/0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-compat/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-ui/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-utils/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-fragment/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-media-compat/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v13/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 24 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="support-v4-24.2.1" level="project" />
<orderEntry type="library" exported="" name="support-compat-24.2.1" level="project" />
<orderEntry type="library" exported="" name="animated-vector-drawable-24.2.1" level="project" />
<orderEntry type="library" exported="" name="support-fragment-24.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="runner-0.5" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="espresso-idling-resource-2.2.2" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-library-1.3" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-integration-1.3" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="jsr305-2.0.1" level="project" />
<orderEntry type="library" exported="" name="design-24.2.1" level="project" />
<orderEntry type="library" exported="" name="support-media-compat-24.2.1" level="project" />
<orderEntry type="library" exported="" name="support-v13-24.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="espresso-core-2.2.2" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="exposed-instrumentation-api-publish-0.5" level="project" />
<orderEntry type="library" exported="" name="support-core-ui-24.2.1" level="project" />
<orderEntry type="library" exported="" name="recyclerview-v7-24.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="rules-0.5" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-24.2.1" level="project" />
<orderEntry type="library" exported="" name="support-vector-drawable-24.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="javax.annotation-api-1.2" level="project" />
<orderEntry type="library" exported="" name="support-core-utils-24.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="javax.inject-1" level="project" />
<orderEntry type="library" exported="" name="support-annotations-24.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="javawriter-2.1.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
</component>
</module>

31
GNSSLogger/app/build.gradle

@ -0,0 +1,31 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.google.android.apps.location.gps.gnsslogger"
minSdkVersion 24
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
testCompile 'junit:junit:4.12'
compile 'com.android.support:design:24.2.1'
compile 'com.android.support:support-v13:24.2.1'
}

17
GNSSLogger/app/proguard-rules.pro vendored

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /media/build/master/prebuilts/fullsdk/linux/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

23
GNSSLogger/app/src/main/AndroidManifest.xml

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.apps.location.gps.gnsslogger" >
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.google.android.apps.location.gps.gnsslogger.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

401
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/FileLogger.java

@ -0,0 +1,401 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import android.content.Context;
import android.content.Intent;
import android.location.GnssClock;
import android.location.GnssMeasurement;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Toast;
import com.google.android.apps.location.gps.gnsslogger.LoggerFragment.UIFragmentComponent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* A GNSS logger to store information to a file.
*/
public class FileLogger implements GnssListener {
private static final String TAG = "FileLogger";
private static final String FILE_PREFIX = "pseudoranges";
private static final String ERROR_WRITING_FILE = "Problem writing to file.";
private static final String COMMENT_START = "# ";
private static final char RECORD_DELIMITER = ',';
private static final String VERSION_TAG = "Version: ";
private static final String FILE_VERSION = "1.4.0.0, Platform: N";
private static final int MAX_FILES_STORED = 100;
private static final int MINIMUM_USABLE_FILE_SIZE_BYTES = 1000;
private final Context mContext;
private final Object mFileLock = new Object();
private BufferedWriter mFileWriter;
private File mFile;
private UIFragmentComponent mUiComponent;
public synchronized UIFragmentComponent getUiComponent() {
return mUiComponent;
}
public synchronized void setUiComponent(UIFragmentComponent value) {
mUiComponent = value;
}
public FileLogger(Context context) {
this.mContext = context;
}
/**
* Start a new file logging process.
*/
public void startNewLog() {
synchronized (mFileLock) {
File baseDirectory;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
baseDirectory = new File(Environment.getExternalStorageDirectory(), FILE_PREFIX);
baseDirectory.mkdirs();
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
logError("Cannot write to external storage.");
return;
} else {
logError("Cannot read external storage.");
return;
}
SimpleDateFormat formatter = new SimpleDateFormat("yyy_MM_dd_HH_mm_ss");
Date now = new Date();
String fileName = String.format("%s_log_%s.txt", FILE_PREFIX, formatter.format(now));
File currentFile = new File(baseDirectory, fileName);
String currentFilePath = currentFile.getAbsolutePath();
BufferedWriter currentFileWriter;
try {
currentFileWriter = new BufferedWriter(new FileWriter(currentFile));
} catch (IOException e) {
logException("Could not open file: " + currentFilePath, e);
return;
}
// initialize the contents of the file
try {
currentFileWriter.write(COMMENT_START);
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.write("Header Description:");
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.write(VERSION_TAG);
currentFileWriter.write(FILE_VERSION);
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.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");
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.write(
"Fix,Provider,Latitude,Longitude,Altitude,Speed,Accuracy,(UTC)TimeInMs");
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.write("Nav,Svid,Type,Status,MessageId,Sub-messageId,Data(Bytes)");
currentFileWriter.newLine();
currentFileWriter.write(COMMENT_START);
currentFileWriter.newLine();
} catch (IOException e) {
logException("Count not initialize file: " + currentFilePath, e);
return;
}
if (mFileWriter != null) {
try {
mFileWriter.close();
} catch (IOException e) {
logException("Unable to close all file streams.", e);
return;
}
}
mFile = currentFile;
mFileWriter = currentFileWriter;
Toast.makeText(mContext, "File opened: " + currentFilePath, Toast.LENGTH_SHORT).show();
// To make sure that files do not fill up the external storage:
// - Remove all empty files
FileFilter filter = new FileToDeleteFilter(mFile);
for (File existingFile : baseDirectory.listFiles(filter)) {
existingFile.delete();
}
// - Trim the number of files with data
File[] existingFiles = baseDirectory.listFiles();
int filesToDeleteCount = existingFiles.length - MAX_FILES_STORED;
if (filesToDeleteCount > 0) {
Arrays.sort(existingFiles);
for (int i = 0; i < filesToDeleteCount; ++i) {
existingFiles[i].delete();
}
}
}
}
/**
* Send the current log via email or other options selected from a pop menu shown to the user. A
* new log is started when calling this function.
*/
public void send() {
if (mFile == null) {
return;
}
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("*/*");
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "SensorLog");
emailIntent.putExtra(Intent.EXTRA_TEXT, "");
// attach the file
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(mFile));
getUiComponent().startActivity(Intent.createChooser(emailIntent, "Send log.."));
if (mFileWriter != null) {
try {
mFileWriter.close();
mFileWriter = null;
} catch (IOException e) {
logException("Unable to close all file streams.", e);
return;
}
}
}
@Override
public void onProviderEnabled(String provider) {}
@Override
public void onProviderDisabled(String provider) {}
@Override
public void onLocationChanged(Location location) {
if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
synchronized (mFileLock) {
if (mFileWriter == 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());
try {
mFileWriter.write(locationStream);
mFileWriter.newLine();
} catch (IOException e) {
logException(ERROR_WRITING_FILE, e);
}
}
}
}
@Override
public void onLocationStatusChanged(String provider, int status, Bundle extras) {}
@Override
public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
synchronized (mFileLock) {
if (mFileWriter == null) {
return;
}
GnssClock gnssClock = event.getClock();
for (GnssMeasurement measurement : event.getMeasurements()) {
try {
writeGnssMeasurementToFile(gnssClock, measurement);
} catch (IOException e) {
logException(ERROR_WRITING_FILE, e);
}
}
}
}
@Override
public void onGnssMeasurementsStatusChanged(int status) {}
@Override
public void onGnssNavigationMessageReceived(GnssNavigationMessage navigationMessage) {
synchronized (mFileLock) {
if (mFileWriter == null) {
return;
}
StringBuilder builder = new StringBuilder("Nav");
builder.append(RECORD_DELIMITER);
builder.append(navigationMessage.getSvid());
builder.append(RECORD_DELIMITER);
builder.append(navigationMessage.getType());
builder.append(RECORD_DELIMITER);
int status = navigationMessage.getStatus();
builder.append(status);
builder.append(RECORD_DELIMITER);
builder.append(navigationMessage.getMessageId());
builder.append(RECORD_DELIMITER);
builder.append(navigationMessage.getSubmessageId());
byte[] data = navigationMessage.getData();
for (byte word : data) {
builder.append(RECORD_DELIMITER);
builder.append(word);
}
try {
mFileWriter.write(builder.toString());
mFileWriter.newLine();
} catch (IOException e) {
logException(ERROR_WRITING_FILE, e);
}
}
}
@Override
public void onGnssNavigationMessageStatusChanged(int status) {}
@Override
public void onGnssStatusChanged(GnssStatus gnssStatus) {}
@Override
public void onNmeaReceived(long timestamp, String s) {}
@Override
public void onListenerRegistration(String listener, boolean result) {}
private void writeGnssMeasurementToFile(GnssClock clock, GnssMeasurement measurement)
throws IOException {
String clockStream =
String.format(
"Raw,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
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() + ",");
mFileWriter.write(clockStream);
String measurementStream =
String.format(
"%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());
mFileWriter.write(measurementStream);
mFileWriter.newLine();
}
private void logException(String errorMessage, Exception e) {
Log.e(GnssContainer.TAG + TAG, errorMessage, e);
Toast.makeText(mContext, errorMessage, Toast.LENGTH_LONG).show();
}
private void logError(String errorMessage) {
Log.e(GnssContainer.TAG + TAG, errorMessage);
Toast.makeText(mContext, errorMessage, Toast.LENGTH_LONG).show();
}
/**
* Implements a {@link FileFilter} to delete files that are not in the
* {@link FileToDeleteFilter#mRetainedFiles}.
*/
private static class FileToDeleteFilter implements FileFilter {
private final List<File> mRetainedFiles;
public FileToDeleteFilter(File... retainedFiles) {
this.mRetainedFiles = Arrays.asList(retainedFiles);
}
/**
* Returns {@code true} to delete the file, and {@code false} to keep the file.
*
* <p>Files are deleted if they are not in the {@link FileToDeleteFilter#mRetainedFiles} list.
*/
@Override
public boolean accept(File pathname) {
if (pathname == null || !pathname.exists()) {
return false;
}
if (mRetainedFiles.contains(pathname)) {
return false;
}
return pathname.length() < MINIMUM_USABLE_FILE_SIZE_BYTES;
}
}
}

289
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssContainer.java

@ -0,0 +1,289 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import android.content.Context;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.OnNmeaMessageListener;
import android.os.Bundle;
import android.os.SystemClock;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* A container for GPS related API calls, it binds the {@link LocationManager} with {@link UiLogger}
*/
public class GnssContainer {
public static final String TAG = "GnssLogger";
private static final long LOCATION_RATE_GPS_MS = TimeUnit.SECONDS.toMillis(1L);
private static final long LOCATION_RATE_NETWORK_MS = TimeUnit.SECONDS.toMillis(60L);
private boolean mLogLocations = true;
private boolean mLogNavigationMessages = true;
private boolean mLogMeasurements = true;
private boolean mLogStatuses = true;
private boolean mLogNmeas = true;
private final List<GnssListener> mLoggers;
private final LocationManager mLocationManager;
private final LocationListener mLocationListener =
new LocationListener() {
@Override
public void onProviderEnabled(String provider) {
if (mLogLocations) {
for (GnssListener logger : mLoggers) {
logger.onProviderEnabled(provider);
}
}
}
@Override
public void onProviderDisabled(String provider) {
if (mLogLocations) {
for (GnssListener logger : mLoggers) {
logger.onProviderDisabled(provider);
}
}
}
@Override
public void onLocationChanged(Location location) {
if (mLogLocations) {
for (GnssListener logger : mLoggers) {
logger.onLocationChanged(location);
}
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (mLogLocations) {
for (GnssListener logger : mLoggers) {
logger.onLocationStatusChanged(provider, status, extras);
}
}
}
};
private final GnssMeasurementsEvent.Callback gnssMeasurementsEventListener =
new GnssMeasurementsEvent.Callback() {
@Override
public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
if (mLogMeasurements) {
for (GnssListener logger : mLoggers) {
logger.onGnssMeasurementsReceived(event);
}
}
}
@Override
public void onStatusChanged(int status) {
if (mLogMeasurements) {
for (GnssListener logger : mLoggers) {
logger.onGnssMeasurementsStatusChanged(status);
}
}
}
};
private final GnssNavigationMessage.Callback gnssNavigationMessageListener =
new GnssNavigationMessage.Callback() {
@Override
public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
if (mLogNavigationMessages) {
for (GnssListener logger : mLoggers) {
logger.onGnssNavigationMessageReceived(event);
}
}
}
@Override
public void onStatusChanged(int status) {
if (mLogNavigationMessages) {
for (GnssListener logger : mLoggers) {
logger.onGnssNavigationMessageStatusChanged(status);
}
}
}
};
private final GnssStatus.Callback gnssStatusListener =
new GnssStatus.Callback() {
@Override
public void onStarted() {}
@Override
public void onStopped() {}
@Override
public void onSatelliteStatusChanged(GnssStatus status) {
for (GnssListener logger : mLoggers) {
logger.onGnssStatusChanged(status);
}
}
};
private final OnNmeaMessageListener nmeaListener =
new OnNmeaMessageListener() {
@Override
public void onNmeaMessage(String s, long l) {
if (mLogNmeas) {
for (GnssListener logger : mLoggers) {
logger.onNmeaReceived(l, s);
}
}
}
};
public GnssContainer(Context context, GnssListener... loggers) {
this.mLoggers = Arrays.asList(loggers);
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
public LocationManager getLocationManager() {
return mLocationManager;
}
public void setLogLocations(boolean value) {
mLogLocations = value;
}
public boolean canLogLocations() {
return mLogLocations;
}
public void setLogNavigationMessages(boolean value) {
mLogNavigationMessages = value;
}
public boolean canLogNavigationMessages() {
return mLogNavigationMessages;
}
public void setLogMeasurements(boolean value) {
mLogMeasurements = value;
}
public boolean canLogMeasurements() {
return mLogMeasurements;
}
public void setLogStatuses(boolean value) {
mLogStatuses = value;
}
public boolean canLogStatuses() {
return mLogStatuses;
}
public void setLogNmeas(boolean value) {
mLogNmeas = value;
}
public boolean canLogNmeas() {
return mLogNmeas;
}
public void registerLocation() {
boolean isGpsProviderEnabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (isGpsProviderEnabled) {
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
LOCATION_RATE_NETWORK_MS,
0.0f /* minDistance */,
mLocationListener);
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
LOCATION_RATE_GPS_MS,
0.0f /* minDistance */,
mLocationListener);
}
logRegistration("LocationUpdates", isGpsProviderEnabled);
}
public void unregisterLocation() {
mLocationManager.removeUpdates(mLocationListener);
}
public void registerMeasurements() {
logRegistration(
"GnssMeasurements",
mLocationManager.registerGnssMeasurementsCallback(gnssMeasurementsEventListener));
}
public void unregisterMeasurements() {
mLocationManager.unregisterGnssMeasurementsCallback(gnssMeasurementsEventListener);
}
public void registerNavigation() {
logRegistration(
"GpsNavigationMessage",
mLocationManager.registerGnssNavigationMessageCallback(gnssNavigationMessageListener));
}
public void unregisterNavigation() {
mLocationManager.unregisterGnssNavigationMessageCallback(gnssNavigationMessageListener);
}
public void registerGnssStatus() {
logRegistration("GnssStatus", mLocationManager.registerGnssStatusCallback(gnssStatusListener));
}
public void unregisterGpsStatus() {
mLocationManager.unregisterGnssStatusCallback(gnssStatusListener);
}
public void registerNmea() {
logRegistration("Nmea", mLocationManager.addNmeaListener(nmeaListener));
}
public void unregisterNmea() {
mLocationManager.removeNmeaListener(nmeaListener);
}
public void registerAll() {
registerLocation();
registerMeasurements();
registerNavigation();
registerGnssStatus();
registerNmea();
}
public void unregisterAll() {
unregisterLocation();
unregisterMeasurements();
unregisterNavigation();
unregisterGpsStatus();
unregisterNmea();
}
private void logRegistration(String listener, boolean result) {
for (GnssListener logger : mLoggers) {
logger.onListenerRegistration(listener, result);
}
}
}

58
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssListener.java

@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.OnNmeaMessageListener;
import android.os.Bundle;
/** A class representing an interface for logging GPS information. */
public interface GnssListener {
/** @see LocationListener#onProviderEnabled(String) */
void onProviderEnabled(String provider);
/** @see LocationListener#onProviderDisabled(String) */
void onProviderDisabled(String provider);
/** @see LocationListener#onLocationChanged(Location) */
void onLocationChanged(Location location);
/** @see LocationListener#onStatusChanged(String, int, Bundle) */
void onLocationStatusChanged(String provider, int status, Bundle extras);
/**
* @see android.location.GnssMeasurementsEvent.Callback#
* onGnssMeasurementsReceived(GnssMeasurementsEvent)
*/
void onGnssMeasurementsReceived(GnssMeasurementsEvent event);
/** @see GnssMeasurementsEvent.Callback#onStatusChanged(int) */
void onGnssMeasurementsStatusChanged(int status);
/**
* @see GnssNavigationMessage.Callback#
* onGnssNavigationMessageReceived(GnssNavigationMessage)
*/
void onGnssNavigationMessageReceived(GnssNavigationMessage event);
/** @see GnssNavigationMessage.Callback#onStatusChanged(int) */
void onGnssNavigationMessageStatusChanged(int status);
/** @see GnssStatus.Callback#onSatelliteStatusChanged(GnssStatus) */
void onGnssStatusChanged(GnssStatus gnssStatus);
/** Called when the listener is registered to listen to GNSS events */
void onListenerRegistration(String listener, boolean result);
/** @see OnNmeaMessageListener#onNmeaMessage(String, long) */
void onNmeaReceived(long l, String s);
}

83
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/HelpDialog.java

@ -0,0 +1,83 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.net.MailTo;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class HelpDialog extends Dialog {
private static Context mContext = null;
public HelpDialog(Context context) {
super(context);
mContext = context;
}
@Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.help);
WebView help = (WebView)findViewById(R.id.helpView);
help.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.startsWith("mailto:")){
MailTo mt = MailTo.parse(url);
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("*/*");
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "GNSSLogger Feedback");
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { mt.getTo()});
emailIntent.putExtra(Intent.EXTRA_TEXT, "");
mContext.startActivity(Intent.createChooser(emailIntent, "Send Feedback..."));
return true;
}
else{
view.loadUrl(url);
}
return true;
}
});
String helpText = readRawTextFile(R.raw.help_contents);
help.loadData(helpText, "text/html; charset=utf-8", "utf-8");
}
private String readRawTextFile(int id) {
InputStream inputStream = mContext.getResources().openRawResource(id);
InputStreamReader in = new InputStreamReader(inputStream);
BufferedReader buf = new BufferedReader(in);
String line;
StringBuilder text = new StringBuilder();
try {
while (( line = buf.readLine()) != null)
text.append(line);
} catch (IOException e) {
return null;
}
return text.toString();
}
}

163
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/LoggerFragment.java

@ -0,0 +1,163 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
/** The UI fragment that hosts a logging view. */
public class LoggerFragment extends Fragment {
private TextView mLogView;
private ScrollView mScrollView;
private FileLogger mFileLogger;
private UiLogger mUiLogger;
private final UIFragmentComponent mUiComponent = new UIFragmentComponent();
public void setUILogger(UiLogger value) {
mUiLogger = value;
}
public void setFileLogger(FileLogger value) {
mFileLogger = value;
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View newView = inflater.inflate(R.layout.fragment_log, container, false /* attachToRoot */);
mLogView = (TextView) newView.findViewById(R.id.log_view);
mScrollView = (ScrollView) newView.findViewById(R.id.log_scroll);
UiLogger currentUiLogger = mUiLogger;
if (currentUiLogger != null) {
currentUiLogger.setUiFragmentComponent(mUiComponent);
}
FileLogger currentFileLogger = mFileLogger;
if (currentFileLogger != null) {
currentFileLogger.setUiComponent(mUiComponent);
}
Button start = (Button) newView.findViewById(R.id.start_log);
start.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View view) {
mScrollView.fullScroll(View.FOCUS_UP);
}
});
Button end = (Button) newView.findViewById(R.id.end_log);
end.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View view) {
mScrollView.fullScroll(View.FOCUS_DOWN);
}
});
Button clear = (Button) newView.findViewById(R.id.clear_log);
clear.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View view) {
mLogView.setText("");
}
});
final Button startLog = (Button) newView.findViewById(R.id.start_logs);
final Button sendFile = (Button) newView.findViewById(R.id.send_file);
startLog.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View view) {
startLog.setEnabled(false);
sendFile.setEnabled(true);
Toast.makeText(getContext(), "Starting log...", Toast.LENGTH_LONG).show();
mFileLogger.startNewLog();
}
});
sendFile.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View view) {
startLog.setEnabled(true);
sendFile.setEnabled(false);
Toast.makeText(getContext(), "Sending file...", Toast.LENGTH_LONG).show();
mFileLogger.send();
}
});
return newView;
}
/**
* A facade for UI and Activity related operations that are required for {@link GnssListener}s.
*/
public class UIFragmentComponent {
private static final int MAX_LENGTH = 12000;
private static final int LOWER_THRESHOLD = (int) (MAX_LENGTH * 0.5);
public synchronized void logTextFragment(final String tag, final String text, int color) {
final SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(tag).append(" | ").append(text).append("\n");
builder.setSpan(
new ForegroundColorSpan(color),
0 /* start */,
builder.length(),
SpannableStringBuilder.SPAN_INCLUSIVE_EXCLUSIVE);
Activity activity = getActivity();
if (activity == null) {
return;
}
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
mLogView.append(builder);
Editable editable = mLogView.getEditableText();
int length = editable.length();
if (length > MAX_LENGTH) {
editable.delete(0, length - LOWER_THRESHOLD);
}
}
});
}
public void startActivity(Intent intent) {
getActivity().startActivity(intent);
}
}
}

172
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/MainActivity.java

@ -0,0 +1,172 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import android.Manifest;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.design.widget.TabLayout.TabLayoutOnPageChangeListener;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import java.util.Locale;
/** The activity for the application. */
public class MainActivity extends AppCompatActivity {
private static final int LOCATION_REQUEST_ID = 1;
private static final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private static final int NUMBER_OF_FRAGMENTS = 2;
private static final int FRAGMENT_INDEX_SETTING = 0;
private static final int FRAGMENT_INDEX_LOGGER = 1;
private GnssContainer mGnssContainer;
private UiLogger mUiLogger;
private FileLogger mFileLogger;
private Fragment[] mFragments;
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermissionAndSetupFragments(this);
}
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to one of the
* sections/tabs/pages.
*/
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case FRAGMENT_INDEX_SETTING:
return mFragments[FRAGMENT_INDEX_SETTING];
case FRAGMENT_INDEX_LOGGER:
return mFragments[FRAGMENT_INDEX_LOGGER];
default:
throw new IllegalArgumentException("Invalid section: " + position);
}
}
@Override
public int getCount() {
// Show total pages.
return 2;
}
@Override
public CharSequence getPageTitle(int position) {
Locale locale = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_settings).toUpperCase(locale);
case 1:
return getString(R.string.title_log).toUpperCase(locale);
default:
return super.getPageTitle(position);
}
}
}
@Override
public void onRequestPermissionsResult(
int requestCode, String permissions[], int[] grantResults) {
if (requestCode == LOCATION_REQUEST_ID) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setupFragments();
}
}
}
private void setupFragments() {
mUiLogger = new UiLogger();
mFileLogger = new FileLogger(getApplicationContext());
mGnssContainer = new GnssContainer(getApplicationContext(), mUiLogger, mFileLogger);
mFragments = new Fragment[NUMBER_OF_FRAGMENTS];
SettingsFragment settingsFragment = new SettingsFragment();
settingsFragment.setGpsContainer(mGnssContainer);
mFragments[FRAGMENT_INDEX_SETTING] = settingsFragment;
LoggerFragment loggerFragment = new LoggerFragment();
loggerFragment.setUILogger(mUiLogger);
loggerFragment.setFileLogger(mFileLogger);
mFragments[FRAGMENT_INDEX_LOGGER] = loggerFragment;
// The viewpager that will host the section contents.
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setOffscreenPageLimit(2);
ViewPagerAdapter adapter = new ViewPagerAdapter(getFragmentManager());
viewPager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.setTabsFromPagerAdapter(adapter);
// Set a listener via setOnTabSelectedListener(OnTabSelectedListener) to be notified when any
// tab's selection state has been changed.
tabLayout.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager));
// Use a TabLayout.TabLayoutOnPageChangeListener to forward the scroll and selection changes to
// this layout
viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(tabLayout));
}
private boolean hasPermissions(Activity activity) {
if (Build.VERSION.SDK_INT < VERSION_CODES.M) {
// Permissions granted at install time.
return true;
}
for (String p : REQUIRED_PERMISSIONS) {
if (ContextCompat.checkSelfPermission(activity, p) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
private void requestPermissionAndSetupFragments(final Activity activity) {
if (hasPermissions(activity)) {
setupFragments();
} else {
ActivityCompat.requestPermissions(activity, REQUIRED_PERMISSIONS, LOCATION_REQUEST_ID);
}
}
}

219
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/SettingsFragment.java

@ -0,0 +1,219 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import android.app.Fragment;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.apps.location.gps.gnsslogger.GnssContainer;
import java.lang.reflect.InvocationTargetException;
import android.widget.Button;
/**
* The UI fragment showing a set of configurable settings for the client to request GPS data.
*/
public class SettingsFragment extends Fragment {
public static final String TAG = ":SettingsFragment";
private GnssContainer mGpsContainer;
private HelpDialog helpDialog;
public void setGpsContainer(GnssContainer value) {
mGpsContainer = value;
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false /* attachToRoot */);
final Switch registerLocation = (Switch) view.findViewById(R.id.register_location);
final TextView registerLocationLabel =
(TextView) view.findViewById(R.id.register_location_label);
//set the switch to OFF
registerLocation.setChecked(false);
registerLocationLabel.setText("Switch is OFF");
registerLocation.setOnCheckedChangeListener(
new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mGpsContainer.registerLocation();
registerLocationLabel.setText("Switch is ON");
} else {
mGpsContainer.unregisterLocation();
registerLocationLabel.setText("Switch is OFF");
}
}
});
final Switch registerMeasurements = (Switch) view.findViewById(R.id.register_measurements);
final TextView registerMeasurementsLabel =
(TextView) view.findViewById(R.id.register_measurement_label);
//set the switch to OFF
registerMeasurements.setChecked(false);
registerMeasurementsLabel.setText("Switch is OFF");
registerMeasurements.setOnCheckedChangeListener(
new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mGpsContainer.registerMeasurements();
registerMeasurementsLabel.setText("Switch is ON");
} else {
mGpsContainer.unregisterMeasurements();
registerMeasurementsLabel.setText("Switch is OFF");
}
}
});
final Switch registerNavigation = (Switch) view.findViewById(R.id.register_navigation);
final TextView registerNavigationLabel =
(TextView) view.findViewById(R.id.register_navigation_label);
//set the switch to OFF
registerNavigation.setChecked(false);
registerNavigationLabel.setText("Switch is OFF");
registerNavigation.setOnCheckedChangeListener(
new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mGpsContainer.registerNavigation();
registerNavigationLabel.setText("Switch is ON");
} else {
mGpsContainer.unregisterNavigation();
registerNavigationLabel.setText("Switch is OFF");
}
}
});
final Switch registerGpsStatus = (Switch) view.findViewById(R.id.register_status);
final TextView registerGpsStatusLabel =
(TextView) view.findViewById(R.id.register_status_label);
//set the switch to OFF
registerGpsStatus.setChecked(false);
registerGpsStatusLabel.setText("Switch is OFF");
registerGpsStatus.setOnCheckedChangeListener(
new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mGpsContainer.registerGnssStatus();
registerGpsStatusLabel.setText("Switch is ON");
} else {
mGpsContainer.unregisterGpsStatus();
registerGpsStatusLabel.setText("Switch is OFF");
}
}
});
final Switch registerNmea = (Switch) view.findViewById(R.id.register_nmea);
final TextView registerNmeaLabel = (TextView) view.findViewById(R.id.register_nmea_label);
//set the switch to OFF
registerNmea.setChecked(false);
registerNmeaLabel.setText("Switch is OFF");
registerNmea.setOnCheckedChangeListener(
new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mGpsContainer.registerNmea();
registerNmeaLabel.setText("Switch is ON");
} else {
mGpsContainer.unregisterNmea();
registerNmeaLabel.setText("Switch is OFF");
}
}
});
Button help = (Button) view.findViewById(R.id.help);
helpDialog = new HelpDialog(getContext());
helpDialog.setTitle("Help contents");
helpDialog.create();
help.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
helpDialog.show();
}
});
Button exit = (Button) view.findViewById(R.id.exit);
exit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getActivity().finishAffinity();
}
});
TextView swInfo = (TextView) view.findViewById(R.id.sw_info);
java.lang.reflect.Method method;
LocationManager locationManager = mGpsContainer.getLocationManager();
try {
method = locationManager.getClass().getMethod("getGnssYearOfHardware");
int hwYear = (int) method.invoke(locationManager);
if (hwYear == 0) {
swInfo.append("HW Year: " + "2015 or older \n");
} else {
swInfo.append("HW Year: " + hwYear + "\n");
}
} catch (NoSuchMethodException e) {
logException("No such method exception: ", e);
return null;
} catch (IllegalAccessException e) {
logException("Illegal Access exception: ", e);
return null;
} catch (InvocationTargetException e) {
logException("Invocation Target Exception: ", e);
return null;
}
String platfromVersionString = Build.VERSION.RELEASE;
swInfo.append("Platform: " + platfromVersionString + "\n");
int apiLivelInt = Build.VERSION.SDK_INT;
swInfo.append("Api Level: " + apiLivelInt);
return view;
}
private void logException(String errorMessage, Exception e) {
Log.e(GnssContainer.TAG + TAG, errorMessage, e);
Toast.makeText(getContext(), errorMessage, Toast.LENGTH_LONG).show();
}
}

220
GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/UiLogger.java

@ -0,0 +1,220 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.google.android.apps.location.gps.gnsslogger;
import android.graphics.Color;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
import android.util.Log;
import com.google.android.apps.location.gps.gnsslogger.LoggerFragment.UIFragmentComponent;
import java.util.concurrent.TimeUnit;
/**
* A class representing a UI logger for the application. Its responsibility is show information in
* the UI.
*/
public class UiLogger implements GnssListener {
private static final long EARTH_RADIUS_METERS = 6371000;
private static final int USED_COLOR = Color.rgb(0x4a, 0x5f, 0x70);
public UiLogger() {}
private UIFragmentComponent mUiFragmentComponent;
public synchronized UIFragmentComponent getUiFragmentComponent() {
return mUiFragmentComponent;
}
public synchronized void setUiFragmentComponent(UIFragmentComponent value) {
mUiFragmentComponent = value;
}
@Override
public void onProviderEnabled(String provider) {
logLocationEvent("onProviderEnabled: " + provider);
}
@Override
public void onProviderDisabled(String provider) {
logLocationEvent("onProviderDisabled: " + provider);
}
@Override
public void onLocationChanged(Location location) {
logLocationEvent("onLocationChanged: " + location);
}
@Override
public void onLocationStatusChanged(String provider, int status, Bundle extras) {
String message =
String.format(
"onStatusChanged: provider=%s, status=%s, extras=%s",
provider, locationStatusToString(status), extras);
logLocationEvent(message);
}
@Override
public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
logMeasurementEvent("onGnsssMeasurementsReceived: " + event);
}
@Override
public void onGnssMeasurementsStatusChanged(int status) {
logMeasurementEvent("onStatusChanged: " + gnssMeasurementsStatusToString(status));
}
@Override
public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
logNavigationMessageEvent("onGnssNavigationMessageReceived: " + event);
}
@Override
public void onGnssNavigationMessageStatusChanged(int status) {
logNavigationMessageEvent("onStatusChanged: " + getGnssNavigationMessageStatus(status));
}
@Override
public void onGnssStatusChanged(GnssStatus gnssStatus) {
logStatusEvent("onGnssStatusChanged: " + gnssStatusToString(gnssStatus));
}
@Override
public void onNmeaReceived(long timestamp, String s) {
logNmeaEvent(String.format("onNmeaReceived: timestamp=%d, %s", timestamp, s));
}
@Override
public void onListenerRegistration(String listener, boolean result) {
logEvent("Registration", String.format("add%sListener: %b", listener, result), USED_COLOR);
}
private void logMeasurementEvent(String event) {
logEvent("Measurement", event, USED_COLOR);
}
private void logNavigationMessageEvent(String event) {
logEvent("NavigationMsg", event, USED_COLOR);
}
private void logStatusEvent(String event) {
logEvent("Status", event, USED_COLOR);
}
private void logNmeaEvent(String event) {
logEvent("Nmea", event, USED_COLOR);
}
private void logEvent(String tag, String message, int color) {
String composedTag = GnssContainer.TAG + tag;
Log.d(composedTag, message);
logText(tag, message, color);
}
private void logText(String tag, String text, int color) {
UIFragmentComponent component = getUiFragmentComponent();
if (component != null) {
component.logTextFragment(tag, text, color);
}
}
private String locationStatusToString(int status) {
switch (status) {
case LocationProvider.AVAILABLE:
return "AVAILABLE";
case LocationProvider.OUT_OF_SERVICE:
return "OUT_OF_SERVICE";
case LocationProvider.TEMPORARILY_UNAVAILABLE:
return "TEMPORARILY_UNAVAILABLE";
default:
return "<Unknown>";
}
}
private String gnssMeasurementsStatusToString(int status) {
switch (status) {
case GnssMeasurementsEvent.Callback.STATUS_NOT_SUPPORTED:
return "NOT_SUPPORTED";
case GnssMeasurementsEvent.Callback.STATUS_READY:
return "READY";
case GnssMeasurementsEvent.Callback.STATUS_LOCATION_DISABLED:
return "GNSS_LOCATION_DISABLED";
default:
return "<Unknown>";
}
}
private String getGnssNavigationMessageStatus(int status) {
switch (status) {
case GnssNavigationMessage.STATUS_UNKNOWN:
return "Status Unknown";
case GnssNavigationMessage.STATUS_PARITY_PASSED:
return "READY";
case GnssNavigationMessage.STATUS_PARITY_REBUILT:
return "Status Parity Rebuilt";
default:
return "<Unknown>";
}
}
private String gnssStatusToString(GnssStatus gnssStatus) {
StringBuilder builder = new StringBuilder("SATELLITE_STATUS | [Satellites:\n");
for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) {
builder
.append("Constellation = ")
.append(getConstellationName(gnssStatus.getConstellationType(i)))
.append(", ");
builder.append("Svid = ").append(gnssStatus.getSvid(i)).append(", ");
builder.append("Cn0DbHz = ").append(gnssStatus.getCn0DbHz(i)).append(", ");
builder.append("Elevation = ").append(gnssStatus.getElevationDegrees(i)).append(", ");
builder.append("Azimuth = ").append(gnssStatus.getAzimuthDegrees(i)).append(", ");
builder.append("hasEphemeris = ").append(gnssStatus.hasEphemerisData(i)).append(", ");
builder.append("hasAlmanac = ").append(gnssStatus.hasAlmanacData(i)).append(", ");
builder.append("usedInFix = ").append(gnssStatus.usedInFix(i)).append("\n");
}
builder.append("]");
return builder.toString();
}
private void logLocationEvent(String event) {
logEvent("Location", event, USED_COLOR);
}
private String getConstellationName(int id) {
switch (id) {
case 1:
return "GPS";
case 2:
return "SBAS";
case 3:
return "GLONASS";
case 4:
return "QZSS";
case 5:
return "BEIDOU";
case 6:
return "GALILEO";
default:
return "UNKNOWN";
}
}
}

BIN
GNSSLogger/app/src/main/res/drawable/ic_launcher.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

19
GNSSLogger/app/src/main/res/layout/activity_main.xml

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_below="@id/tab_layout"/>
</LinearLayout>

67
GNSSLogger/app/src/main/res/layout/fragment_log.xml

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/start_log"
android:text="\u2770 Start"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
<Button
android:id="@+id/clear_log"
android:text="Clear"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
<Button
android:id="@+id/end_log"
android:text="End \u2771"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
</LinearLayout>
<ScrollView
android:id="@+id/log_scroll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/log_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/start_logs"
android:text="Start Log"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/send_file"
android:text="Stop &amp; Send"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>

155
GNSSLogger/app/src/main/res/layout/fragment_main.xml

@ -0,0 +1,155 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textStyle="bold"
android:layout_marginTop="15dp"
android:id="@+id/register_location_label" />
<Switch
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/register_location"
android:singleLine="true"
android:layout_marginTop="15dp"
android:text="@string/location_label" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textStyle="bold"
android:layout_marginTop="15dp"
android:id="@+id/register_measurement_label" />
<Switch
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/register_measurements"
android:singleLine="true"
android:layout_marginTop="15dp"
android:text="@string/measurements_label" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textStyle="bold"
android:layout_marginTop="15dp"
android:id="@+id/register_navigation_label" />
<Switch
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/register_navigation"
android:singleLine="true"
android:layout_marginTop="15dp"
android:text="@string/nav_msg_label" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textStyle="bold"
android:layout_marginTop="15dp"
android:id="@+id/register_status_label" />
<Switch
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/register_status"
android:singleLine="true"
android:layout_marginTop="15dp"
android:text="@string/gnss_status_label" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textStyle="bold"
android:layout_marginTop="15dp"
android:id="@+id/register_nmea_label" />
<Switch
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/register_nmea"
android:singleLine="true"
android:layout_marginTop="15dp"
android:text="@string/nmea_label" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/help"
android:layout_marginTop="150dp"
android:id="@+id/help"
android:singleLine="true" />
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/exit"
android:layout_marginTop="150dp"
android:id="@+id/exit"
android:singleLine="true" />
</LinearLayout>
<TextView
android:id="@+id/sw_info"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textStyle="bold"
android:gravity="bottom"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end|bottom"
android:textStyle="italic|bold"
android:text="@string/app_version"/>
</LinearLayout>

13
GNSSLogger/app/src/main/res/layout/help.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.webkit.WebView
android:id="@+id/helpView"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</android.webkit.WebView>
</LinearLayout>

BIN
GNSSLogger/app/src/main/res/mipmap-hdpi/ic_launcher.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
GNSSLogger/app/src/main/res/mipmap-mdpi/ic_launcher.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
GNSSLogger/app/src/main/res/mipmap-xhdpi/ic_launcher.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
GNSSLogger/app/src/main/res/mipmap-xxhdpi/ic_launcher.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
GNSSLogger/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

20
GNSSLogger/app/src/main/res/raw/help_contents.txt

@ -0,0 +1,20 @@
<a href="#about_apphelp">About AppHelp</a>
<br>
<a href="#feedback">Feedback</a>
<br>
<a name="about_apphelp" id="about_apphelp">
<h3>About AppHelp</h3>
<ul>
<li>Start Log button: Starts logging when pushed.</li>
<li>Stop & Send button: Stops logging and shows dialog to send the file via email or other options.</li>
</ul>
</a>
<br>
<a name="feedback" id="feedback">
<h3>Feedback</h3>
Any comments, feedback, suggestions on GNSSLogger please write to us using the link below.
<br>
<a href="mailto:pseudoranges-feedback@google.com">Contact support</a>
</a>
<br>

6
GNSSLogger/app/src/main/res/values-w820dp/dimens.xml

@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

6
GNSSLogger/app/src/main/res/values/colors.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

6
GNSSLogger/app/src/main/res/values/dimens.xml

@ -0,0 +1,6 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="button_text_size">12sp</dimen>
</resources>

19
GNSSLogger/app/src/main/res/values/strings.xml

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">GnssLogger</string>
<string name="app_version">v1.4.0.0</string>
<string name="title_settings">Settings</string>
<string name="title_log">Log</string>
<string name="title_device">Device</string>
<integer name="google_play_services_version">8487000</integer>
<string name="location_label">Location</string>
<string name="measurements_label">Measurements</string>
<string name="nav_msg_label">Navigation Messages</string>
<string name="gnss_status_label">GnssStatus</string>
<string name="nmea_label">Nmea</string>
<string name="help">HELP</string>
<string name="exit">Exit</string>
</resources>

5
GNSSLogger/app/src/main/res/values/styles.xml

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
</style>
</resources>

23
GNSSLogger/build.gradle

@ -0,0 +1,23 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

17
GNSSLogger/gradle.properties

@ -0,0 +1,17 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

160
GNSSLogger/gradlew vendored

@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
GNSSLogger/gradlew.bat vendored

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

10
GNSSLogger/local.properties

@ -0,0 +1,10 @@
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/media/build/master/prebuilts/fullsdk/linux

1
GNSSLogger/settings.gradle

@ -0,0 +1 @@
include ':app'

7
opensource/PlotPseudorangeRates.m

@ -60,8 +60,11 @@ for i=1:M
else
colors(i,:) = get(h,'Color');
end
text(ti,y(iFi(end)-1),int2str(gnssMeas.Svid(i)),'Color',colors(i,:));
meanDprM = mean(y(isfinite(y)));%store for analysing delta prr dpr
iFi = find(isfinite(y));
if any(iFi)
text(ti,y(iFi(end)),int2str(gnssMeas.Svid(i)),'Color',colors(i,:));
end
meanDprM = mean(y(iFi));%store for analysing delta prr dpr
deltaMeanM(i) = meanPrrM - meanDprM;
end
end

9
opensource/PlotPvt.m

@ -25,17 +25,18 @@ iFi = isfinite(gpsPvt.allLlaDegDegM(:,1));%index into finite results
if ~any(iFi)
return
end
llaMed = median(gpsPvt.allLlaDegDegM(iFi,:));%use median position as reference
llaMed = median(gpsPvt.allLlaDegDegM(iFi,:));%median position
%print median lla so user can use it as reference position if they want:
fprintf('Median llaDegDegM = [%.7f %.7f %.2f]\n',llaMed)
if nargin < 3, llaTrueDegDegM = []; end
if nargin < 4. titleString = 'PVT solution'; end
if nargin < 4, titleString = 'PVT solution'; end
bGotLlaTrue = ~isempty(llaTrueDegDegM) && any(llaTrueDegDegM);
%not empty and not all zeros
if bGotLlaTrue
llaRef = llaTrueDegDegM;
else
llaRef = llaMed;
%print median lla so user can use it as reference position if they want
fprintf('Median llaDegDegM = [%.7f %.7f %.2f]\n',llaMed)
end
%% plot ne errors vs llaTrueDegDegM --------------------------------------------

2
opensource/ProcessGnssMeasScript.m

@ -48,7 +48,7 @@ param.llaTrueDegDegM = [37.422578, -122.081678, -28];%Charleston Park Test Site
%param.llaTrueDegDegM = [45.5298979 -122.6619045 24.16] %workshop trial approx coords
%% Set the data filter and Read log file
dataFilter = SetDataFilter;
[gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,prFileName,dataFilter,param);
[gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,prFileName,dataFilter);
if isempty(gnssRaw), return, end
%% Get online ephemeris from Nasa ftp, first compute UTC Time from gnssRaw:

20
opensource/README.md

@ -13,12 +13,14 @@ This code is maintained on GitHub at the following link:
https://github.com/google/gps-measurement-tools
# Initial setup:
# Matlab
## Initial setup:
1. Extract the contents of the zip file to a directory
2. Open and run ProcessGnssMeasScript.m, when prompted by Matlab select `Change dir`
## To process a log file you collected from GnssLogger:
### To process a log file you collected from GnssLogger:
1. save the log file in a *./demoFiles* directory
2. edit ProcessGpsMeasScript.m, change `prFileName` to log filename
@ -34,12 +36,12 @@ site (GetNasaHourlyEphemeris.m will tell you the correct url and filename),
copy the file to the directory where your log file is,
and GetNasaHourlyEphemeris.m will read it from there.
## For a summary of the open source GNSS Measurements Tools
### For a summary of the open source GNSS Measurements Tools
See ~/gpstools/opensource/Contents.m or type 'help opensource' in matlab
command window.
# Platform specific notes:
## Platform specific notes:
For Windows: use '\' (backslash), instead of '/' for directories.
@ -59,6 +61,16 @@ edit the name of the unzip utility:
If you uncompress the file 'by hand' and rerun GetNasaHourlyEphemeris.m, it will
read the uncompressed file.
# GNSSLogger
Sample App that allows registering for various Android location related measurements
and log the measurements to the screen and optionally to a text file.
This source code is supplied as an Android Studio project that can be built and run
with [Android Studio](https://developer.android.com/studio/index.html).
The APK is also provided for convience.
# Copyright Notice
Copyright 2016 Google Inc.

21
opensource/ReadGnssLogger.m

@ -1,5 +1,5 @@
function [gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,fileName,dataFilter,gnssAnalysis)
%% [gnssRaw,gnssAnalysis]=ReadGnssLogger(dirName,fileName,dataFilter,gnssAnalysis);
%% [gnssRaw,gnssAnalysis]=ReadGnssLogger(dirName,fileName,[dataFilter],[gnssAnalysis]);
% Read the log file created by Gnss Logger App in Android
% Compatible with Android release N
%
@ -7,12 +7,14 @@ function [gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,fileName,dataFilter,gns
% dirName = string with directory of fileName,
% e.g. '~/Documents/MATLAB/Pseudoranges/2016-03-28'
% fileName = string with filename
% dataFilter = nx2 cell array of pairs of strings,
% optional inputs:
% [dataFilter], nx2 cell array of pairs of strings,
% dataFilter{i,1} is a string with one of 'Raw' header values from the
% GnssLogger log file e.g. 'ConstellationType'
% dataFilter{i,2} is a string with a valid matlab expression, containing
% the header value, e.g. 'ConstellationType==1'
% See SetDataFilter.m for full rules and examples of dataFilter.
% See SetDataFilter.m for full rules and examples of dataFilter.
% [gnssAnalysis] structure containing analysis, incl list of missing fields
%
% Output:
% gnssRaw, all GnssClock and GnssMeasurement fields from log file, including:
@ -45,6 +47,7 @@ function [gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,fileName,dataFilter,gns
% ReportMissingFields()
%% Initialize outputs and inputs
gnssRaw = [];
gnssAnalysis.GnssClockErrors = 'GnssClock Errors.';
gnssAnalysis.GnssMeasurementErrors = 'GnssMeasurement Errors.';
gnssAnalysis.ApiPassFail = '';
@ -63,7 +66,8 @@ rawCsvFile = MakeCsv(dirName,fileName);
%% apply dataFilter
[bOk] = CheckDataFilter(dataFilter,header);
if ~bOk, return, end
C = FilterData(C,dataFilter,header);
[bOk,C] = FilterData(C,dataFilter,header);
if ~bOk, return, end
%% pack data into gnssRaw structure
[gnssRaw,missing] = PackGnssRaw(C,header);
@ -130,7 +134,9 @@ end
if isempty(strfind(sPlatform,'N'))
%add || strfind(platform,'O') and so on for future platforms
fprintf('\nThis version of ReadGnssLogger supports Android N\n')
error('Found "%s" in log file, expected "Platform: N"',line)
fprintf('WARNING: did not find "Platform" type in log file, expected "Platform: N"\n')
fprintf('Please Update GnssLogger\n')
sPlatform = 'N';%assume version N
end
v1 = [1;4;0;0];
@ -237,9 +243,10 @@ fclose(fid);
end% of function ReadRawCsv
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function C = FilterData(C,dataFilter,header)
function [bOk,C] = FilterData(C,dataFilter,header)
%% filter C based on contents of dataFilter
bOk = true;
iS = ones(size(C{1})); %initialize index into rows of C
for i=1:size(dataFilter,1)
j=find(strcmp(header,dataFilter{i,1}));%j = index into header
@ -262,6 +269,8 @@ end
if ~any(iS) %if all zeros
fprintf('\nAll measurements removed. Specify dataFilter less strictly than this:, ')
dataFilter(:,2)
bOk=false;
C=[];
return
end

13
opensource/SetDataFilter.m

@ -8,13 +8,9 @@ function dataFilter = SetDataFilter
%Author: Frank van Diggelen
%Open Source code for processing Android GNSS Measurements
%filter for fine time measurements only <=> uncertainty < 10 ms = 1e7 ns
dataFilter{1,1} = 'BiasUncertaintyNanos';
dataFilter{1,2} = 'BiasUncertaintyNanos < 1e7';
%filter out FullBiasNanos == 0
dataFilter{end+1,1} = 'FullBiasNanos';
dataFilter{end,2} = 'FullBiasNanos ~= 0';
dataFilter{1,1} = 'FullBiasNanos';
dataFilter{1,2} = 'FullBiasNanos ~= 0';
%you can create other filters in the same way ...
%for example, suppose you want to remove Svid 23:
@ -26,6 +22,11 @@ dataFilter{end,2} = 'FullBiasNanos ~= 0';
% NOTE: you *cannot* use 'any(Svid)==[2,5,10,17]' because Svid refers to a
% vector variable and you must compare it to a scalar.
%filter for fine time measurements only <=> uncertainty < 10 ms = 1e7 ns
%For Nexus 5x and 6p this field is not filled, so comment out these next 2 lines
% dataFilter{end+1,1} = 'BiasUncertaintyNanos';
% dataFilter{end,2} = 'BiasUncertaintyNanos < 1e7';
%keep only Svid 2
% dataFilter{end+1,1} = 'Svid';
% dataFilter{end,2} = 'Svid==2';

Loading…
Cancel
Save