use smoothed altitude values to get more realistic elevation data (see #99) - v1 (needs testing)
This commit is contained in:
parent
9e8b127c70
commit
3fa589e21c
11 changed files with 175 additions and 51 deletions
|
@ -51,13 +51,13 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Kotlin
|
// Kotlin
|
||||||
def coroutinesVersion = "1.4.1"
|
def coroutinesVersion = "1.4.3"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
|
||||||
|
|
||||||
// AndroidX
|
// AndroidX
|
||||||
def navigationVersion = "2.3.3"
|
def navigationVersion = "2.3.5"
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.3.2'
|
||||||
|
|
|
@ -56,6 +56,8 @@ object Keys {
|
||||||
const val PREF_TRACKING_STATE: String = "prefTrackingState"
|
const val PREF_TRACKING_STATE: String = "prefTrackingState"
|
||||||
const val PREF_USE_IMPERIAL_UNITS: String = "prefUseImperialUnits"
|
const val PREF_USE_IMPERIAL_UNITS: String = "prefUseImperialUnits"
|
||||||
const val PREF_GPS_ONLY: String = "prefGpsOnly"
|
const val PREF_GPS_ONLY: String = "prefGpsOnly"
|
||||||
|
const val PREF_RECORDING_ACCURACY_HIGH: String = "prefRecordingAccuracyHigh"
|
||||||
|
const val PREF_ALTITUDE_SMOOTHING_VALUE: String = "prefAltitudeSmoothingValue"
|
||||||
const val PREF_LOCATION_ACCURACY_THRESHOLD: String = "prefLocationAccuracyThreshold"
|
const val PREF_LOCATION_ACCURACY_THRESHOLD: String = "prefLocationAccuracyThreshold"
|
||||||
const val PREF_LOCATION_AGE_THRESHOLD: String = "prefLocationAgeThreshold"
|
const val PREF_LOCATION_AGE_THRESHOLD: String = "prefLocationAgeThreshold"
|
||||||
|
|
||||||
|
@ -106,6 +108,7 @@ object Keys {
|
||||||
const val DEFAULT_ACCURACY: Float = 300f // in meters
|
const val DEFAULT_ACCURACY: Float = 300f // in meters
|
||||||
const val DEFAULT_ALTITUDE: Double = 0.0
|
const val DEFAULT_ALTITUDE: Double = 0.0
|
||||||
const val DEFAULT_TIME: Long = 0L
|
const val DEFAULT_TIME: Long = 0L
|
||||||
|
const val DEFAULT_ALTITUDE_SMOOTHING_VALUE: Int = 15
|
||||||
const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters
|
const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters
|
||||||
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 60000000000L // one minute in nanoseconds
|
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 60000000000L // one minute in nanoseconds
|
||||||
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters
|
||||||
|
|
|
@ -105,15 +105,26 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up "Accuracy Threshold" preference
|
// set up "Recording Accuracy" preference
|
||||||
val preferenceAccuracyThreshold: SeekBarPreference = SeekBarPreference(activity as Context)
|
val preferenceRecordingAccuracy: SwitchPreferenceCompat = SwitchPreferenceCompat(activity as Context)
|
||||||
preferenceAccuracyThreshold.title = getString(R.string.pref_accuracy_threshold_title)
|
preferenceRecordingAccuracy.title = getString(R.string.pref_recording_accuracy_title)
|
||||||
preferenceAccuracyThreshold.setIcon(R.drawable.ic_timeline_24dp)
|
preferenceRecordingAccuracy.setIcon(R.drawable.ic_timeline_24dp)
|
||||||
preferenceAccuracyThreshold.key = Keys.PREF_LOCATION_ACCURACY_THRESHOLD
|
preferenceRecordingAccuracy.key = Keys.PREF_RECORDING_ACCURACY_HIGH
|
||||||
preferenceAccuracyThreshold.summary = getString(R.string.pref_accuracy_threshold_summary)
|
preferenceRecordingAccuracy.summaryOn = getString(R.string.pref_recording_accuracy_summary_high)
|
||||||
preferenceAccuracyThreshold.showSeekBarValue = true
|
preferenceRecordingAccuracy.summaryOff = getString(R.string.pref_recording_accuracy_summary_default)
|
||||||
preferenceAccuracyThreshold.max = 50
|
preferenceRecordingAccuracy.setDefaultValue(false)
|
||||||
preferenceAccuracyThreshold.setDefaultValue(Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY)
|
|
||||||
|
// set up "Altitude Smoothing" preference
|
||||||
|
val preferenceAltitudeSmoothingValue: SeekBarPreference = SeekBarPreference(activity as Context)
|
||||||
|
preferenceAltitudeSmoothingValue.title = getString(R.string.pref_altitude_smoothing_value_title)
|
||||||
|
preferenceAltitudeSmoothingValue.setIcon(R.drawable.ic_bar_chart_24)
|
||||||
|
preferenceAltitudeSmoothingValue.key = Keys.PREF_ALTITUDE_SMOOTHING_VALUE
|
||||||
|
preferenceAltitudeSmoothingValue.summary = getString(R.string.pref_altitude_smoothing_value_summary)
|
||||||
|
preferenceAltitudeSmoothingValue.showSeekBarValue = true
|
||||||
|
preferenceAltitudeSmoothingValue.min = 5
|
||||||
|
preferenceAltitudeSmoothingValue.max = 20
|
||||||
|
preferenceAltitudeSmoothingValue.setDefaultValue(Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||||
|
|
||||||
|
|
||||||
// set up "Reset" preference
|
// set up "Reset" preference
|
||||||
val preferenceResetAdvanced: Preference = Preference(activity as Context)
|
val preferenceResetAdvanced: Preference = Preference(activity as Context)
|
||||||
|
@ -121,7 +132,9 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
preferenceResetAdvanced.setIcon(R.drawable.ic_undo_24dp)
|
preferenceResetAdvanced.setIcon(R.drawable.ic_undo_24dp)
|
||||||
preferenceResetAdvanced.summary = getString(R.string.pref_reset_advanced_summary)
|
preferenceResetAdvanced.summary = getString(R.string.pref_reset_advanced_summary)
|
||||||
preferenceResetAdvanced.setOnPreferenceClickListener{
|
preferenceResetAdvanced.setOnPreferenceClickListener{
|
||||||
preferenceAccuracyThreshold.value = Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY
|
// reset "Recording Accuracy" preference
|
||||||
|
preferenceRecordingAccuracy.isChecked = false
|
||||||
|
preferenceAltitudeSmoothingValue.value = Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +179,8 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
|
|
||||||
val preferenceCategoryAdvanced: PreferenceCategory = PreferenceCategory(activity as Context)
|
val preferenceCategoryAdvanced: PreferenceCategory = PreferenceCategory(activity as Context)
|
||||||
preferenceCategoryAdvanced.title = getString(R.string.pref_advanced_title)
|
preferenceCategoryAdvanced.title = getString(R.string.pref_advanced_title)
|
||||||
preferenceCategoryAdvanced.contains(preferenceAccuracyThreshold)
|
preferenceCategoryAdvanced.contains(preferenceRecordingAccuracy)
|
||||||
|
preferenceCategoryAdvanced.contains(preferenceAltitudeSmoothingValue)
|
||||||
preferenceCategoryAdvanced.contains(preferenceResetAdvanced)
|
preferenceCategoryAdvanced.contains(preferenceResetAdvanced)
|
||||||
|
|
||||||
val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context)
|
val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context)
|
||||||
|
@ -182,7 +196,8 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||||
screen.addPreference(preferenceCategoryMaintenance)
|
screen.addPreference(preferenceCategoryMaintenance)
|
||||||
screen.addPreference(preferenceDeleteNonStarred)
|
screen.addPreference(preferenceDeleteNonStarred)
|
||||||
screen.addPreference(preferenceCategoryAdvanced)
|
screen.addPreference(preferenceCategoryAdvanced)
|
||||||
screen.addPreference(preferenceAccuracyThreshold)
|
screen.addPreference(preferenceRecordingAccuracy)
|
||||||
|
screen.addPreference(preferenceAltitudeSmoothingValue)
|
||||||
screen.addPreference(preferenceResetAdvanced)
|
screen.addPreference(preferenceResetAdvanced)
|
||||||
screen.addPreference(preferenceCategoryAbout)
|
screen.addPreference(preferenceCategoryAbout)
|
||||||
screen.addPreference(preferenceAppVersion)
|
screen.addPreference(preferenceAppVersion)
|
||||||
|
|
|
@ -60,7 +60,8 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
var networkProviderActive: Boolean = false
|
var networkProviderActive: Boolean = false
|
||||||
var useImperial: Boolean = false
|
var useImperial: Boolean = false
|
||||||
var gpsOnly: Boolean = false
|
var gpsOnly: Boolean = false
|
||||||
var locationAccuracyThreshold: Int = Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY
|
var accuracyMultiplier: Int = 1
|
||||||
|
var altitudeSmoothingValue: Int = Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE
|
||||||
var currentBestLocation: Location = LocationHelper.getDefaultLocation()
|
var currentBestLocation: Location = LocationHelper.getDefaultLocation()
|
||||||
var lastSave: Date = Keys.DEFAULT_DATE
|
var lastSave: Date = Keys.DEFAULT_DATE
|
||||||
var stepCountOffset: Float = 0f
|
var stepCountOffset: Float = 0f
|
||||||
|
@ -89,7 +90,8 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
gpsOnly = PreferencesHelper.loadGpsOnly(this)
|
gpsOnly = PreferencesHelper.loadGpsOnly(this)
|
||||||
useImperial = PreferencesHelper.loadUseImperialUnits(this)
|
useImperial = PreferencesHelper.loadUseImperialUnits(this)
|
||||||
locationAccuracyThreshold = PreferencesHelper.loadAccuracyThreshold(this)
|
accuracyMultiplier = PreferencesHelper.loadAccuracyMultiplier(this)
|
||||||
|
altitudeSmoothingValue = PreferencesHelper.loadAltitudeSmoothingValue(this)
|
||||||
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
@ -429,9 +431,9 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
Keys.PREF_USE_IMPERIAL_UNITS -> {
|
Keys.PREF_USE_IMPERIAL_UNITS -> {
|
||||||
useImperial = PreferencesHelper.loadUseImperialUnits(this@TrackerService)
|
useImperial = PreferencesHelper.loadUseImperialUnits(this@TrackerService)
|
||||||
}
|
}
|
||||||
// preference "Accuracy Threshold"
|
// preference "Recording Accuracy"
|
||||||
Keys.PREF_LOCATION_ACCURACY_THRESHOLD -> {
|
Keys.PREF_RECORDING_ACCURACY_HIGH -> {
|
||||||
locationAccuracyThreshold = PreferencesHelper.loadAccuracyThreshold(this@TrackerService)
|
accuracyMultiplier = PreferencesHelper.loadAccuracyMultiplier(this@TrackerService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,7 +459,7 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
private val periodicTrackUpdate: Runnable = object : Runnable {
|
private val periodicTrackUpdate: Runnable = object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
// add waypoint to track - step count is continuously updated in onSensorChanged
|
// add waypoint to track - step count is continuously updated in onSensorChanged
|
||||||
val result: Pair<Track, Boolean> = TrackHelper.addWayPointToTrack(this@TrackerService, track, currentBestLocation, locationAccuracyThreshold, resumed)
|
val result: Pair<Track, Boolean> = TrackHelper.addWayPointToTrack(this@TrackerService, track, currentBestLocation, accuracyMultiplier, altitudeSmoothingValue, resumed)
|
||||||
// get track from result
|
// get track from result
|
||||||
track = result.first
|
track = result.first
|
||||||
// check if waypoint was successfully added (= result.second)
|
// check if waypoint was successfully added (= result.second)
|
||||||
|
@ -489,4 +491,4 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,11 @@ data class WayPoint(@Expose val provider: String,
|
||||||
@Expose var isStopOver: Boolean = false,
|
@Expose var isStopOver: Boolean = false,
|
||||||
@Expose var starred: Boolean = false): Parcelable {
|
@Expose var starred: Boolean = false): Parcelable {
|
||||||
|
|
||||||
|
/* Constructor using just Location */
|
||||||
|
constructor(location: Location) : this (location.provider, location.latitude, location.longitude, location. altitude, location.accuracy, location.time)
|
||||||
|
|
||||||
|
/* Constructor using Location plus distanceToStartingPoint and numberSatellites */
|
||||||
|
constructor(location: Location, distanceToStartingPoint: Float, numberSatellites: Int) : this (location.provider, location.latitude, location.longitude, location. altitude, location.accuracy, location.time, distanceToStartingPoint, numberSatellites)
|
||||||
|
|
||||||
/* Converts WayPoint into Location */
|
/* Converts WayPoint into Location */
|
||||||
fun toLocation(): Location {
|
fun toLocation(): Location {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import android.os.SystemClock
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import org.y20k.trackbook.Keys
|
import org.y20k.trackbook.Keys
|
||||||
import org.y20k.trackbook.core.Track
|
import org.y20k.trackbook.core.Track
|
||||||
|
import org.y20k.trackbook.core.WayPoint
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
@ -177,7 +178,7 @@ object LocationHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Checks if given location is different enough compared to previous location */
|
/* Checks if given location is different enough compared to previous location */
|
||||||
fun isDifferentEnough(previousLocation: Location?, location: Location): Boolean {
|
fun isDifferentEnough(previousLocation: Location?, location: Location, accuracyMultiplier: Int): Boolean {
|
||||||
// check if previous location is (not) available
|
// check if previous location is (not) available
|
||||||
if (previousLocation == null) return true
|
if (previousLocation == null) return true
|
||||||
|
|
||||||
|
@ -185,16 +186,15 @@ object LocationHelper {
|
||||||
// that the true position is within a circle of this radius.
|
// that the true position is within a circle of this radius.
|
||||||
// These formulas determine if the difference between the last point and
|
// These formulas determine if the difference between the last point and
|
||||||
// new point is statistically significant.
|
// new point is statistically significant.
|
||||||
val accuracy = if (location.accuracy != 0.0f) location.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE
|
val accuracy: Float = if (location.accuracy != 0.0f) location.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE
|
||||||
val previousAccuracy = if (previousLocation.accuracy != 0.0f) previousLocation.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE
|
val previousAccuracy: Float = if (previousLocation.accuracy != 0.0f) previousLocation.accuracy else Keys.DEFAULT_THRESHOLD_DISTANCE
|
||||||
val accuracyDelta = Math.sqrt((accuracy.pow(2) + previousAccuracy.pow(2)).toDouble())
|
val accuracyDelta: Double = Math.sqrt((accuracy.pow(2) + previousAccuracy.pow(2)).toDouble())
|
||||||
|
val distance: Float = calculateDistance(previousLocation, location)
|
||||||
val distance = calculateDistance(previousLocation, location)
|
|
||||||
|
|
||||||
// With 1*accuracyDelta we have 68% confidence that the points are
|
// With 1*accuracyDelta we have 68% confidence that the points are
|
||||||
// different. We can multiply this number to increase confidence but
|
// different. We can multiply this number to increase confidence but
|
||||||
// decrease point recording frequency if needed.
|
// decrease point recording frequency if needed.
|
||||||
return distance > accuracyDelta
|
return distance > accuracyDelta * accuracyMultiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ object LocationHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Calculate elevation differences */
|
/* Calculate elevation differences */
|
||||||
fun calculateElevationDifferences(previousLocation: Location?, location: Location, track: Track): Pair<Double, Double> {
|
fun calculateElevationDifferencesOld(previousLocation: Location?, location: Location, track: Track): Pair<Double, Double> {
|
||||||
// store current values
|
// store current values
|
||||||
var positiveElevation: Double = track.positiveElevation
|
var positiveElevation: Double = track.positiveElevation
|
||||||
var negativeElevation: Double = track.negativeElevation
|
var negativeElevation: Double = track.negativeElevation
|
||||||
|
@ -231,6 +231,27 @@ object LocationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Calculate elevation differences */
|
||||||
|
fun calculateElevationDifferences(previousLocation: Location?, location: Location, track: Track, altitudeSmoothingValue: Int): Pair<Double, Double> {
|
||||||
|
// store current values
|
||||||
|
var positiveElevation: Double = track.positiveElevation
|
||||||
|
var negativeElevation: Double = track.negativeElevation
|
||||||
|
if (previousLocation != null && location.altitude != Keys.DEFAULT_ALTITUDE) {
|
||||||
|
val locationAltitudeCorrected: Double = calculateCorrectedAltitude(location, track, altitudeSmoothingValue)
|
||||||
|
val previousLocationAltitudeCorrected: Double = calculateCorrectedAltitude(previousLocation, track, altitudeSmoothingValue)
|
||||||
|
// get elevation difference and sum it up
|
||||||
|
val altitudeDifference: Double = locationAltitudeCorrected - previousLocationAltitudeCorrected
|
||||||
|
if (altitudeDifference > 0) {
|
||||||
|
positiveElevation = track.positiveElevation + altitudeDifference // upwards movement
|
||||||
|
}
|
||||||
|
if (altitudeDifference < 0) {
|
||||||
|
negativeElevation = track.negativeElevation + altitudeDifference // downwards movement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair(positiveElevation, negativeElevation)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Checks if given location is a stop over */
|
/* Checks if given location is a stop over */
|
||||||
fun isStopOver(previousLocation: Location?, location: Location): Boolean {
|
fun isStopOver(previousLocation: Location?, location: Location): Boolean {
|
||||||
if (previousLocation == null) return false
|
if (previousLocation == null) return false
|
||||||
|
@ -239,4 +260,29 @@ object LocationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Calculate a moving average taking into account previously recorded altitude values */
|
||||||
|
private fun calculateCorrectedAltitude(location: Location, track: Track, altitudeSmoothingValue: Int): Double {
|
||||||
|
// add location to track
|
||||||
|
track.wayPoints.add(WayPoint(location))
|
||||||
|
// get size of track
|
||||||
|
val trackSize: Int = track.wayPoints.size
|
||||||
|
// skip calculation if less than two waypoints available
|
||||||
|
if (trackSize < 2) return location.altitude
|
||||||
|
// get number of locations to be used in calculating the moving average
|
||||||
|
val numberOfLocationsUsedForSmoothing: Int = if (trackSize < altitudeSmoothingValue) {
|
||||||
|
trackSize
|
||||||
|
} else {
|
||||||
|
altitudeSmoothingValue
|
||||||
|
}
|
||||||
|
// add altitude values in range and calculate average
|
||||||
|
val mostRecentWaypointIndex: Int = trackSize - 1
|
||||||
|
var altitudeSum: Double = 0.0
|
||||||
|
for (i in mostRecentWaypointIndex..(mostRecentWaypointIndex - numberOfLocationsUsedForSmoothing)) {
|
||||||
|
altitudeSum = altitudeSum + track.wayPoints[i].altitude
|
||||||
|
}
|
||||||
|
return altitudeSum / numberOfLocationsUsedForSmoothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,36 @@ object PreferencesHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Loads state of recording accuracy */
|
||||||
|
fun loadRecordingAccuracyHigh(context: Context): Boolean {
|
||||||
|
// get preferences
|
||||||
|
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
// load current setting
|
||||||
|
return settings.getBoolean(Keys.PREF_RECORDING_ACCURACY_HIGH, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Loads current accuracy multiplier */
|
||||||
|
fun loadAccuracyMultiplier(context: Context): Int {
|
||||||
|
// get preferences
|
||||||
|
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
// load current setting
|
||||||
|
val recordingAccuracyHigh: Boolean = settings.getBoolean(Keys.PREF_RECORDING_ACCURACY_HIGH, false)
|
||||||
|
// return multiplier based on state
|
||||||
|
return if (recordingAccuracyHigh) 2 else 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Load altitude smoothing value */
|
||||||
|
fun loadAltitudeSmoothingValue(context: Context): Int {
|
||||||
|
// get preferences
|
||||||
|
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
// load current setting
|
||||||
|
return settings.getInt(Keys.PREF_ALTITUDE_SMOOTHING_VALUE, Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Loads the state of a map */
|
/* Loads the state of a map */
|
||||||
fun loadCurrentBestLocation(context: Context): Location {
|
fun loadCurrentBestLocation(context: Context): Location {
|
||||||
// get preferences
|
// get preferences
|
||||||
|
@ -149,4 +179,4 @@ object PreferencesHelper {
|
||||||
settings.edit { putBoolean(Keys.PREF_ONE_TIME_HOUSEKEEPING_NECESSARY, state) }
|
settings.edit { putBoolean(Keys.PREF_ONE_TIME_HOUSEKEEPING_NECESSARY, state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.y20k.trackbook.helpers
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import org.y20k.trackbook.Keys
|
||||||
import org.y20k.trackbook.R
|
import org.y20k.trackbook.R
|
||||||
import org.y20k.trackbook.core.Track
|
import org.y20k.trackbook.core.Track
|
||||||
import org.y20k.trackbook.core.TracklistElement
|
import org.y20k.trackbook.core.TracklistElement
|
||||||
|
@ -48,7 +49,7 @@ object TrackHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Adds given locatiom as waypoint to track */
|
/* Adds given locatiom as waypoint to track */
|
||||||
fun addWayPointToTrack(context: Context, track: Track, location: Location, locationAccuracyThreshold: Int, resumed: Boolean): Pair<Track, Boolean> {
|
fun addWayPointToTrack(context: Context, track: Track, location: Location, accuracyMultiplier: Int, altitudeSmoothingValue: Int, resumed: Boolean): Pair<Track, Boolean> {
|
||||||
// get previous location
|
// get previous location
|
||||||
val previousLocation: Location?
|
val previousLocation: Location?
|
||||||
var numberOfWayPoints: Int = track.wayPoints.size
|
var numberOfWayPoints: Int = track.wayPoints.size
|
||||||
|
@ -76,8 +77,8 @@ object TrackHelper {
|
||||||
|
|
||||||
// add only if recent and accurate and different
|
// add only if recent and accurate and different
|
||||||
val shouldBeAdded: Boolean = (LocationHelper.isRecentEnough(location) &&
|
val shouldBeAdded: Boolean = (LocationHelper.isRecentEnough(location) &&
|
||||||
LocationHelper.isAccurateEnough(location, locationAccuracyThreshold) &&
|
LocationHelper.isAccurateEnough(location, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY) &&
|
||||||
LocationHelper.isDifferentEnough(previousLocation, location))
|
LocationHelper.isDifferentEnough(previousLocation, location, accuracyMultiplier))
|
||||||
|
|
||||||
// // Debugging for shouldBeAdded - remove for production
|
// // Debugging for shouldBeAdded - remove for production
|
||||||
// val recentEnough: Boolean = LocationHelper.isRecentEnough(location)
|
// val recentEnough: Boolean = LocationHelper.isRecentEnough(location)
|
||||||
|
@ -98,25 +99,34 @@ object TrackHelper {
|
||||||
track.length = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
track.length = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.altitude != 0.0) {
|
// update altitude values
|
||||||
// update altitude values
|
val altitude: Double = location.altitude
|
||||||
|
if (altitude != 0.0) {
|
||||||
|
|
||||||
|
// CASE: First location
|
||||||
if (numberOfWayPoints == 0) {
|
if (numberOfWayPoints == 0) {
|
||||||
track.maxAltitude = location.altitude
|
track.maxAltitude = altitude
|
||||||
track.minAltitude = location.altitude
|
track.minAltitude = altitude
|
||||||
} else {
|
}
|
||||||
// calculate elevation values (upwards / downwards movements)
|
|
||||||
val elevationDifferences: Pair<Double, Double> = LocationHelper.calculateElevationDifferences(previousLocation, location, track)
|
// CASE: Not first location
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Step 1: Update altitude values
|
||||||
|
if (altitude > track.maxAltitude) track.maxAltitude = altitude
|
||||||
|
if (altitude < track.minAltitude) track.minAltitude = altitude
|
||||||
|
|
||||||
|
// Step 2: Calculate and update elevation values (upwards / downwards movements)
|
||||||
|
val elevationDifferences: Pair<Double, Double> = LocationHelper.calculateElevationDifferences(previousLocation, location, track, altitudeSmoothingValue)
|
||||||
// check if any differences were calculated
|
// check if any differences were calculated
|
||||||
if (elevationDifferences != Pair(track.positiveElevation, track.negativeElevation)) {
|
if (elevationDifferences != Pair(track.positiveElevation, track.negativeElevation)) {
|
||||||
// update altitude values
|
|
||||||
if (location.altitude > track.maxAltitude) track.maxAltitude = location.altitude
|
|
||||||
if (location.altitude < track.minAltitude) track.minAltitude = location.altitude
|
|
||||||
// update elevation values (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
// update elevation values (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
||||||
if (!resumed) {
|
if (!resumed) {
|
||||||
track.positiveElevation = elevationDifferences.first
|
track.positiveElevation = elevationDifferences.first
|
||||||
track.negativeElevation = elevationDifferences.second
|
track.negativeElevation = elevationDifferences.second
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,12 +136,12 @@ object TrackHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save number of satellites
|
// save number of satellites
|
||||||
val numberOfSatellites: Int
|
val numberSatellites: Int
|
||||||
val extras = location.extras
|
val extras = location.extras
|
||||||
if (extras != null && extras.containsKey("satellites")) {
|
if (extras != null && extras.containsKey("satellites")) {
|
||||||
numberOfSatellites = extras.getInt("satellites", 0)
|
numberSatellites = extras.getInt("satellites", 0)
|
||||||
} else {
|
} else {
|
||||||
numberOfSatellites = 0
|
numberSatellites = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// add current location as point to center on for later display
|
// add current location as point to center on for later display
|
||||||
|
@ -139,7 +149,7 @@ object TrackHelper {
|
||||||
track.longitude = location.longitude
|
track.longitude = location.longitude
|
||||||
|
|
||||||
// add location as new waypoint
|
// add location as new waypoint
|
||||||
track.wayPoints.add(WayPoint(provider = location.provider, latitude = location.latitude, longitude = location.longitude, altitude = location.altitude, accuracy = location.accuracy, time = location.time, distanceToStartingPoint = track.length, numberSatellites = numberOfSatellites))
|
track.wayPoints.add(WayPoint(location, distanceToStartingPoint = track.length, numberSatellites = numberSatellites))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(track, shouldBeAdded)
|
return Pair(track, shouldBeAdded)
|
||||||
|
@ -147,8 +157,7 @@ object TrackHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Calculates time passed since last stop of recording */
|
/* Calculates time passed since last stop of recording */
|
||||||
fun calculateDurationOfPause(recordingStop: Date): Long =
|
fun calculateDurationOfPause(recordingStop: Date): Long = GregorianCalendar.getInstance().time.time - recordingStop.time
|
||||||
GregorianCalendar.getInstance().time.time - recordingStop.time
|
|
||||||
|
|
||||||
|
|
||||||
/* Creates GPX string for given track */
|
/* Creates GPX string for given track */
|
||||||
|
|
9
app/src/main/res/drawable/ic_bar_chart_24.xml
Normal file
9
app/src/main/res/drawable/ic_bar_chart_24.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/icon_default"
|
||||||
|
android:pathData="M5,9.2h3L8,19L5,19zM10.6,5h2.8v14h-2.8zM16.2,13L19,13v6h-2.8z"/>
|
||||||
|
</vector>
|
|
@ -78,6 +78,8 @@
|
||||||
<string name="pref_app_version_title">App Version</string>
|
<string name="pref_app_version_title">App Version</string>
|
||||||
<string name="pref_accuracy_threshold_summary">Discard location fixes with an accuracy larger than (meters):</string>
|
<string name="pref_accuracy_threshold_summary">Discard location fixes with an accuracy larger than (meters):</string>
|
||||||
<string name="pref_accuracy_threshold_title">Accuracy Threshold</string>
|
<string name="pref_accuracy_threshold_title">Accuracy Threshold</string>
|
||||||
|
<string name="pref_altitude_smoothing_value_summary">Number of waypoints used to smooth the elevation curve.</string>
|
||||||
|
<string name="pref_altitude_smoothing_value_title">Altitude Smoothing</string>
|
||||||
<string name="pref_advanced_title">Advanced</string>
|
<string name="pref_advanced_title">Advanced</string>
|
||||||
<string name="pref_delete_non_starred_summary">Delete all recordings in \"Tracks\" that are not starred.</string>
|
<string name="pref_delete_non_starred_summary">Delete all recordings in \"Tracks\" that are not starred.</string>
|
||||||
<string name="pref_delete_non_starred_title">Delete Non-Starred Recordings</string>
|
<string name="pref_delete_non_starred_title">Delete Non-Starred Recordings</string>
|
||||||
|
@ -89,6 +91,9 @@
|
||||||
<string name="pref_imperial_measurement_units_summary_metric">Currently using metric units (Kilometer, Meter).</string>
|
<string name="pref_imperial_measurement_units_summary_metric">Currently using metric units (Kilometer, Meter).</string>
|
||||||
<string name="pref_imperial_measurement_units_summary_imperial">Currently using imperial units (Miles, Feet).</string>
|
<string name="pref_imperial_measurement_units_summary_imperial">Currently using imperial units (Miles, Feet).</string>
|
||||||
<string name="pref_imperial_measurement_units_title">Use Imperial Measurements</string>
|
<string name="pref_imperial_measurement_units_title">Use Imperial Measurements</string>
|
||||||
|
<string name="pref_recording_accuracy_summary_high">High accuracy. Less waypoints are being recorded.</string>
|
||||||
|
<string name="pref_recording_accuracy_summary_default">Default accuracy.</string>
|
||||||
|
<string name="pref_recording_accuracy_title">Recording Accuracy</string>
|
||||||
<string name="pref_report_issue_summary">Report bugs and suggest improvements on GitHub.</string>
|
<string name="pref_report_issue_summary">Report bugs and suggest improvements on GitHub.</string>
|
||||||
<string name="pref_report_issue_title">Report Issue</string>
|
<string name="pref_report_issue_title">Report Issue</string>
|
||||||
<string name="pref_reset_advanced_summary">Reset advanced settings to defaults.</string>
|
<string name="pref_reset_advanced_summary">Reset advanced settings to defaults.</string>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
kotlin_version = '1.4.31'
|
kotlin_version = '1.4.32'
|
||||||
navigation_version = '2.3.3'
|
navigation_version = '2.3.3'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -10,7 +10,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
|
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue