Browse Source

Merge pull request #1 from google/master

update from original
pull/1/head
Lukasz K Bonenberg 8 years ago committed by GitHub
parent
commit
57be9f1174
  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. 19
      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 else
colors(i,:) = get(h,'Color'); colors(i,:) = get(h,'Color');
end end
text(ti,y(iFi(end)-1),int2str(gnssMeas.Svid(i)),'Color',colors(i,:)); iFi = find(isfinite(y));
meanDprM = mean(y(isfinite(y)));%store for analysing delta prr dpr 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; deltaMeanM(i) = meanPrrM - meanDprM;
end end
end end

9
opensource/PlotPvt.m

@ -25,17 +25,18 @@ iFi = isfinite(gpsPvt.allLlaDegDegM(:,1));%index into finite results
if ~any(iFi) if ~any(iFi)
return return
end 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 < 3, llaTrueDegDegM = []; end
if nargin < 4. titleString = 'PVT solution'; end if nargin < 4, titleString = 'PVT solution'; end
bGotLlaTrue = ~isempty(llaTrueDegDegM) && any(llaTrueDegDegM); bGotLlaTrue = ~isempty(llaTrueDegDegM) && any(llaTrueDegDegM);
%not empty and not all zeros %not empty and not all zeros
if bGotLlaTrue if bGotLlaTrue
llaRef = llaTrueDegDegM; llaRef = llaTrueDegDegM;
else else
llaRef = llaMed; 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 end
%% plot ne errors vs llaTrueDegDegM -------------------------------------------- %% 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 %param.llaTrueDegDegM = [45.5298979 -122.6619045 24.16] %workshop trial approx coords
%% Set the data filter and Read log file %% Set the data filter and Read log file
dataFilter = SetDataFilter; dataFilter = SetDataFilter;
[gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,prFileName,dataFilter,param); [gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,prFileName,dataFilter);
if isempty(gnssRaw), return, end if isempty(gnssRaw), return, end
%% Get online ephemeris from Nasa ftp, first compute UTC Time from gnssRaw: %% 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 https://github.com/google/gps-measurement-tools
# Initial setup: # Matlab
## Initial setup:
1. Extract the contents of the zip file to a directory 1. Extract the contents of the zip file to a directory
2. Open and run ProcessGnssMeasScript.m, when prompted by Matlab select `Change dir` 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 1. save the log file in a *./demoFiles* directory
2. edit ProcessGpsMeasScript.m, change `prFileName` to log filename 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, copy the file to the directory where your log file is,
and GetNasaHourlyEphemeris.m will read it from there. 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 See ~/gpstools/opensource/Contents.m or type 'help opensource' in matlab
command window. command window.
# Platform specific notes: ## Platform specific notes:
For Windows: use '\' (backslash), instead of '/' for directories. 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 If you uncompress the file 'by hand' and rerun GetNasaHourlyEphemeris.m, it will
read the uncompressed file. 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 Notice
Copyright 2016 Google Inc. Copyright 2016 Google Inc.

19
opensource/ReadGnssLogger.m

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

13
opensource/SetDataFilter.m

@ -8,13 +8,9 @@ function dataFilter = SetDataFilter
%Author: Frank van Diggelen %Author: Frank van Diggelen
%Open Source code for processing Android GNSS Measurements %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 %filter out FullBiasNanos == 0
dataFilter{end+1,1} = 'FullBiasNanos'; dataFilter{1,1} = 'FullBiasNanos';
dataFilter{end,2} = 'FullBiasNanos ~= 0'; dataFilter{1,2} = 'FullBiasNanos ~= 0';
%you can create other filters in the same way ... %you can create other filters in the same way ...
%for example, suppose you want to remove Svid 23: %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 % 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. % 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 %keep only Svid 2
% dataFilter{end+1,1} = 'Svid'; % dataFilter{end+1,1} = 'Svid';
% dataFilter{end,2} = 'Svid==2'; % dataFilter{end,2} = 'Svid==2';

Loading…
Cancel
Save