use smoothed altitude values to get more realistic elevation data (see #99) - v2 (needs testing)
parent
3fa589e21c
commit
bb00e18312
|
@ -108,7 +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_ALTITUDE_SMOOTHING_VALUE: Int = 10
|
||||||
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
|
||||||
|
|
|
@ -61,7 +61,6 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
var useImperial: Boolean = false
|
var useImperial: Boolean = false
|
||||||
var gpsOnly: Boolean = false
|
var gpsOnly: Boolean = false
|
||||||
var accuracyMultiplier: Int = 1
|
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
|
||||||
|
@ -72,6 +71,7 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
var bound: Boolean = false
|
var bound: Boolean = false
|
||||||
private val binder = LocalBinder()
|
private val binder = LocalBinder()
|
||||||
private val handler: Handler = Handler()
|
private val handler: Handler = Handler()
|
||||||
|
private var altitudeValues: SimpleMovingAverageQueue = SimpleMovingAverageQueue(Keys.DEFAULT_ALTITUDE_SMOOTHING_VALUE)
|
||||||
private lateinit var locationManager: LocationManager
|
private lateinit var locationManager: LocationManager
|
||||||
private lateinit var sensorManager: SensorManager
|
private lateinit var sensorManager: SensorManager
|
||||||
private lateinit var notificationManager: NotificationManager
|
private lateinit var notificationManager: NotificationManager
|
||||||
|
@ -91,7 +91,7 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
gpsOnly = PreferencesHelper.loadGpsOnly(this)
|
gpsOnly = PreferencesHelper.loadGpsOnly(this)
|
||||||
useImperial = PreferencesHelper.loadUseImperialUnits(this)
|
useImperial = PreferencesHelper.loadUseImperialUnits(this)
|
||||||
accuracyMultiplier = PreferencesHelper.loadAccuracyMultiplier(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
|
||||||
|
@ -104,7 +104,10 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
currentBestLocation = LocationHelper.getLastKnownLocation(this)
|
currentBestLocation = LocationHelper.getLastKnownLocation(this)
|
||||||
track = FileHelper.readTrack(this, FileHelper.getTempFileUri(this))
|
track = FileHelper.readTrack(this, FileHelper.getTempFileUri(this))
|
||||||
backgroundJob = Job()
|
backgroundJob = Job()
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
altitudeValues.capacity = PreferencesHelper.loadAltitudeSmoothingValue(this)
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(
|
||||||
|
sharedPreferenceChangeListener
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,7 +117,10 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
// SERVICE RESTART (via START_STICKY)
|
// SERVICE RESTART (via START_STICKY)
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE) {
|
if (trackingState == Keys.STATE_TRACKING_ACTIVE) {
|
||||||
LogHelper.w(TAG, "Trackbook has been killed by the operating system. Trying to resume recording.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Trackbook has been killed by the operating system. Trying to resume recording."
|
||||||
|
)
|
||||||
resumeTracking()
|
resumeTracking()
|
||||||
}
|
}
|
||||||
// ACTION STOP
|
// ACTION STOP
|
||||||
|
@ -175,7 +181,9 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
// remove notification
|
// remove notification
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
// stop listening for changes in shared preferences
|
// stop listening for changes in shared preferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(
|
||||||
|
sharedPreferenceChangeListener
|
||||||
|
)
|
||||||
// stop receiving location updates
|
// stop receiving location updates
|
||||||
removeGpsLocationListener()
|
removeGpsLocationListener()
|
||||||
removeNetworkLocationListener()
|
removeNetworkLocationListener()
|
||||||
|
@ -304,15 +312,25 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
override fun onProviderEnabled(provider: String) {
|
override fun onProviderEnabled(provider: String) {
|
||||||
LogHelper.v(TAG, "onProviderEnabled $provider")
|
LogHelper.v(TAG, "onProviderEnabled $provider")
|
||||||
when (provider) {
|
when (provider) {
|
||||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(
|
||||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
locationManager
|
||||||
|
)
|
||||||
|
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
||||||
|
LocationHelper.isNetworkEnabled(
|
||||||
|
locationManager
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onProviderDisabled(provider: String) {
|
override fun onProviderDisabled(provider: String) {
|
||||||
LogHelper.v(TAG, "onProviderDisabled $provider")
|
LogHelper.v(TAG, "onProviderDisabled $provider")
|
||||||
when (provider) {
|
when (provider) {
|
||||||
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(
|
||||||
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
locationManager
|
||||||
|
)
|
||||||
|
LocationManager.NETWORK_PROVIDER -> networkProviderActive =
|
||||||
|
LocationHelper.isNetworkEnabled(
|
||||||
|
locationManager
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
|
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
|
||||||
|
@ -330,13 +348,24 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
gpsProviderActive = LocationHelper.isGpsEnabled(locationManager)
|
||||||
if (gpsProviderActive) {
|
if (gpsProviderActive) {
|
||||||
// check for location permission
|
// check for location permission
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED) {
|
||||||
// adds GPS location listener
|
// adds GPS location listener
|
||||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f,gpsLocationListener)
|
locationManager.requestLocationUpdates(
|
||||||
|
LocationManager.GPS_PROVIDER,
|
||||||
|
0,
|
||||||
|
0f,
|
||||||
|
gpsLocationListener
|
||||||
|
)
|
||||||
gpsLocationListenerRegistered = true
|
gpsLocationListenerRegistered = true
|
||||||
LogHelper.v(TAG, "Added GPS location listener.")
|
LogHelper.v(TAG, "Added GPS location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add GPS location listener. Location permission is not granted.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to add GPS location listener. Location permission is not granted."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add GPS location listener.")
|
LogHelper.w(TAG, "Unable to add GPS location listener.")
|
||||||
|
@ -355,19 +384,33 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
networkProviderActive = LocationHelper.isNetworkEnabled(locationManager)
|
||||||
if (networkProviderActive && !gpsOnly) {
|
if (networkProviderActive && !gpsOnly) {
|
||||||
// check for location permission
|
// check for location permission
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED) {
|
||||||
// adds Network location listener
|
// adds Network location listener
|
||||||
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, networkLocationListener)
|
locationManager.requestLocationUpdates(
|
||||||
|
LocationManager.NETWORK_PROVIDER,
|
||||||
|
0,
|
||||||
|
0f,
|
||||||
|
networkLocationListener
|
||||||
|
)
|
||||||
networkLocationListenerRegistered = true
|
networkLocationListenerRegistered = true
|
||||||
LogHelper.v(TAG, "Added Network location listener.")
|
LogHelper.v(TAG, "Added Network location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add Network location listener. Location permission is not granted.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to add Network location listener. Location permission is not granted."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to add Network location listener.")
|
LogHelper.w(TAG, "Unable to add Network location listener.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogHelper.v(TAG, "Skipping registration. Network location listener has already been added.")
|
LogHelper.v(
|
||||||
|
TAG,
|
||||||
|
"Skipping registration. Network location listener has already been added."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +422,10 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
gpsLocationListenerRegistered = false
|
gpsLocationListenerRegistered = false
|
||||||
LogHelper.v(TAG, "Removed GPS location listener.")
|
LogHelper.v(TAG, "Removed GPS location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to remove GPS location listener. Location permission is needed.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to remove GPS location listener. Location permission is needed."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,14 +437,21 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
networkLocationListenerRegistered = false
|
networkLocationListenerRegistered = false
|
||||||
LogHelper.v(TAG, "Removed Network location listener.")
|
LogHelper.v(TAG, "Removed Network location listener.")
|
||||||
} else {
|
} else {
|
||||||
LogHelper.w(TAG, "Unable to remove Network location listener. Location permission is needed.")
|
LogHelper.w(
|
||||||
|
TAG,
|
||||||
|
"Unable to remove Network location listener. Location permission is needed."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Registers a step counter listener */
|
/* Registers a step counter listener */
|
||||||
private fun startStepCounter() {
|
private fun startStepCounter() {
|
||||||
val stepCounterAvailable = sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI)
|
val stepCounterAvailable = sensorManager.registerListener(
|
||||||
|
this, sensorManager.getDefaultSensor(
|
||||||
|
Sensor.TYPE_STEP_COUNTER
|
||||||
|
), SensorManager.SENSOR_DELAY_UI
|
||||||
|
)
|
||||||
if (!stepCounterAvailable) {
|
if (!stepCounterAvailable) {
|
||||||
LogHelper.w(TAG, "Pedometer sensor not available.")
|
LogHelper.w(TAG, "Pedometer sensor not available.")
|
||||||
track.stepCount = -1f
|
track.stepCount = -1f
|
||||||
|
@ -408,7 +461,12 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
|
|
||||||
/* Displays / updates notification */
|
/* Displays / updates notification */
|
||||||
private fun displayNotification(): Notification {
|
private fun displayNotification(): Notification {
|
||||||
val notification: Notification = notificationHelper.createNotification(trackingState, track.length, track.duration, useImperial)
|
val notification: Notification = notificationHelper.createNotification(
|
||||||
|
trackingState,
|
||||||
|
track.length,
|
||||||
|
track.duration,
|
||||||
|
useImperial
|
||||||
|
)
|
||||||
notificationManager.notify(Keys.TRACKER_SERVICE_NOTIFICATION_ID, notification)
|
notificationManager.notify(Keys.TRACKER_SERVICE_NOTIFICATION_ID, notification)
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -459,25 +517,34 @@ 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, accuracyMultiplier, altitudeSmoothingValue, resumed)
|
val result: Pair<Boolean, Track> = TrackHelper.addWayPointToTrack(track, currentBestLocation, accuracyMultiplier, resumed)
|
||||||
// get track from result
|
// get results
|
||||||
track = result.first
|
val successfullyAdded: Boolean = result.first
|
||||||
// check if waypoint was successfully added (= result.second)
|
track = result.second
|
||||||
if (resumed && result.second) {
|
|
||||||
// reset resumed flag, if necessary
|
|
||||||
resumed = false
|
|
||||||
}
|
|
||||||
// check, if waypoint was added
|
// check, if waypoint was added
|
||||||
if (result.second) {
|
if (successfullyAdded) {
|
||||||
// reset resumed flag, if necessary
|
// reset resumed flag, if necessary
|
||||||
if (resumed) {
|
if (resumed) {
|
||||||
resumed = false
|
resumed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store previous smoothed altitude
|
||||||
|
val previousAltitude: Double = altitudeValues.average
|
||||||
|
// put current altitude into queue
|
||||||
|
altitudeValues.add(currentBestLocation.altitude)
|
||||||
|
// get current smoothed altitude
|
||||||
|
val currentAltitude: Double = altitudeValues.average
|
||||||
|
// calculate and store elevation differences
|
||||||
|
track = LocationHelper.calculateElevationDifferences(currentAltitude, previousAltitude, track)
|
||||||
|
|
||||||
// save a temp track
|
// save a temp track
|
||||||
val now: Date = GregorianCalendar.getInstance().time
|
val now: Date = GregorianCalendar.getInstance().time
|
||||||
if (now.time - lastSave.time > Keys.SAVE_TEMP_TRACK_INTERVAL) {
|
if (now.time - lastSave.time > Keys.SAVE_TEMP_TRACK_INTERVAL) {
|
||||||
lastSave = now
|
lastSave = now
|
||||||
GlobalScope.launch { FileHelper.saveTempTrackSuspended(this@TrackerService, track) }
|
GlobalScope.launch { FileHelper.saveTempTrackSuspended(
|
||||||
|
this@TrackerService,
|
||||||
|
track
|
||||||
|
) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// update notification
|
// update notification
|
||||||
|
@ -491,4 +558,21 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Simple queue that evicts older elements and holds an average */
|
||||||
|
/* Credit: CircularQueue https://stackoverflow.com/a/51923797 */
|
||||||
|
class SimpleMovingAverageQueue(var capacity: Int) : LinkedList<Double>() {
|
||||||
|
private var sum: Double = 0.0
|
||||||
|
var average: Double = sum / capacity
|
||||||
|
override fun add(element: Double): Boolean {
|
||||||
|
if (this.size >= capacity) {
|
||||||
|
sum -= this.first
|
||||||
|
removeFirst()
|
||||||
|
} else {
|
||||||
|
sum += element
|
||||||
|
}
|
||||||
|
return super.add(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.os.Parcelable
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import com.google.gson.annotations.Expose
|
import com.google.gson.annotations.Expose
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import org.y20k.trackbook.helpers.LocationHelper
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -44,7 +45,7 @@ data class WayPoint(@Expose val provider: String,
|
||||||
constructor(location: Location) : this (location.provider, location.latitude, location.longitude, location. altitude, location.accuracy, location.time)
|
constructor(location: Location) : this (location.provider, location.latitude, location.longitude, location. altitude, location.accuracy, location.time)
|
||||||
|
|
||||||
/* Constructor using Location plus distanceToStartingPoint and numberSatellites */
|
/* 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)
|
constructor(location: Location, distanceToStartingPoint: Float) : this (location.provider, location.latitude, location.longitude, location. altitude, location.accuracy, location.time, distanceToStartingPoint, LocationHelper.getNumberOfSatellites(location))
|
||||||
|
|
||||||
/* Converts WayPoint into Location */
|
/* Converts WayPoint into Location */
|
||||||
fun toLocation(): Location {
|
fun toLocation(): Location {
|
||||||
|
|
|
@ -22,11 +22,11 @@ import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
|
import android.os.Bundle
|
||||||
import android.os.SystemClock
|
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
|
||||||
|
|
||||||
|
@ -232,23 +232,17 @@ object LocationHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Calculate elevation differences */
|
/* Calculate elevation differences */
|
||||||
fun calculateElevationDifferences(previousLocation: Location?, location: Location, track: Track, altitudeSmoothingValue: Int): Pair<Double, Double> {
|
fun calculateElevationDifferences(currentAltitude: Double, previousAltitude: Double, track: Track): Track {
|
||||||
// store current values
|
if (currentAltitude != Keys.DEFAULT_ALTITUDE || previousAltitude != Keys.DEFAULT_ALTITUDE) {
|
||||||
var positiveElevation: Double = track.positiveElevation
|
val altitudeDifference: Double = currentAltitude - previousAltitude
|
||||||
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) {
|
if (altitudeDifference > 0) {
|
||||||
positiveElevation = track.positiveElevation + altitudeDifference // upwards movement
|
track.positiveElevation += altitudeDifference // upwards movement
|
||||||
}
|
}
|
||||||
if (altitudeDifference < 0) {
|
if (altitudeDifference < 0) {
|
||||||
negativeElevation = track.negativeElevation + altitudeDifference // downwards movement
|
track.negativeElevation += altitudeDifference // downwards movement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pair(positiveElevation, negativeElevation)
|
return track
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -260,29 +254,17 @@ object LocationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Calculate a moving average taking into account previously recorded altitude values */
|
/* Get number of satellites from Location extras */
|
||||||
private fun calculateCorrectedAltitude(location: Location, track: Track, altitudeSmoothingValue: Int): Double {
|
fun getNumberOfSatellites(location: Location): Int {
|
||||||
// add location to track
|
val numberOfSatellites: Int
|
||||||
track.wayPoints.add(WayPoint(location))
|
val extras: Bundle? = location.extras
|
||||||
// get size of track
|
if (extras != null && extras.containsKey("satellites")) {
|
||||||
val trackSize: Int = track.wayPoints.size
|
numberOfSatellites = extras.getInt("satellites", 0)
|
||||||
// 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 {
|
} else {
|
||||||
altitudeSmoothingValue
|
numberOfSatellites = 0
|
||||||
}
|
}
|
||||||
// add altitude values in range and calculate average
|
return numberOfSatellites
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,8 @@ object TrackHelper {
|
||||||
|
|
||||||
|
|
||||||
/* Adds given locatiom as waypoint to track */
|
/* Adds given locatiom as waypoint to track */
|
||||||
fun addWayPointToTrack(context: Context, track: Track, location: Location, accuracyMultiplier: Int, altitudeSmoothingValue: Int, resumed: Boolean): Pair<Track, Boolean> {
|
fun addWayPointToTrack(track: Track, location: Location, accuracyMultiplier: Int, resumed: Boolean): Pair<Boolean, Track> {
|
||||||
// get previous location
|
// Step 1: Get previous location
|
||||||
val previousLocation: Location?
|
val previousLocation: Location?
|
||||||
var numberOfWayPoints: Int = track.wayPoints.size
|
var numberOfWayPoints: Int = track.wayPoints.size
|
||||||
|
|
||||||
|
@ -69,90 +69,47 @@ object TrackHelper {
|
||||||
previousLocation = track.wayPoints[numberOfWayPoints - 1].toLocation()
|
previousLocation = track.wayPoints[numberOfWayPoints - 1].toLocation()
|
||||||
}
|
}
|
||||||
|
|
||||||
// update duration
|
// Step 2: Update duration
|
||||||
val now: Date = GregorianCalendar.getInstance().time
|
val now: Date = GregorianCalendar.getInstance().time
|
||||||
val difference: Long = now.time - track.recordingStop.time
|
val difference: Long = now.time - track.recordingStop.time
|
||||||
track.duration = track.duration + difference
|
track.duration = track.duration + difference
|
||||||
track.recordingStop = now
|
track.recordingStop = now
|
||||||
|
|
||||||
// add only if recent and accurate and different
|
// Step 3: Add waypoint, ifrecent and accurate and different enough
|
||||||
val shouldBeAdded: Boolean = (LocationHelper.isRecentEnough(location) &&
|
val shouldBeAdded: Boolean = (LocationHelper.isRecentEnough(location) &&
|
||||||
LocationHelper.isAccurateEnough(location, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY) &&
|
LocationHelper.isAccurateEnough(location, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY) &&
|
||||||
LocationHelper.isDifferentEnough(previousLocation, location, accuracyMultiplier))
|
LocationHelper.isDifferentEnough(previousLocation, location, accuracyMultiplier))
|
||||||
|
|
||||||
// // Debugging for shouldBeAdded - remove for production
|
|
||||||
// val recentEnough: Boolean = LocationHelper.isRecentEnough(location)
|
|
||||||
// val accurateEnough: Boolean = LocationHelper.isAccurateEnough(location, locationAccuracyThreshold)
|
|
||||||
// val differentEnough: Boolean = LocationHelper.isDifferentEnough(previousLocation, location)
|
|
||||||
// val shouldBeAdded = recentEnough && accurateEnough && differentEnough
|
|
||||||
// if (!recentEnough && accurateEnough && differentEnough) { Toast.makeText(context, "Debug: Not recent enough", Toast.LENGTH_LONG).show() }
|
|
||||||
// else if (!accurateEnough && recentEnough && differentEnough) { Toast.makeText(context, "Debug: Not accurate enough", Toast.LENGTH_LONG).show() }
|
|
||||||
// else if (!differentEnough && recentEnough && accurateEnough) { Toast.makeText(context, "Debug: Not different enough", Toast.LENGTH_LONG).show() }
|
|
||||||
// else if (!recentEnough && !accurateEnough && differentEnough) { Toast.makeText(context, "Debug: Not recent and accurate enough", Toast.LENGTH_LONG).show() }
|
|
||||||
// else if (!recentEnough && !differentEnough && accurateEnough) { Toast.makeText(context, "Debug: Not recent and different enough", Toast.LENGTH_LONG).show() }
|
|
||||||
// else if (!accurateEnough && !differentEnough && recentEnough) { Toast.makeText(context, "Debug: Not accurate and different enough", Toast.LENGTH_LONG).show() }
|
|
||||||
// else { Toast.makeText(context, "Debug: bad location.", Toast.LENGTH_LONG).show() }
|
|
||||||
|
|
||||||
if (shouldBeAdded) {
|
if (shouldBeAdded) {
|
||||||
// update distance (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
// Step 3.1: Update distance (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
||||||
if (!resumed) {
|
if (!resumed) {
|
||||||
track.length = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
track.length = track.length + LocationHelper.calculateDistance(previousLocation, location)
|
||||||
}
|
}
|
||||||
|
// Step 3.2: Update altitude values
|
||||||
// update altitude values
|
|
||||||
val altitude: Double = location.altitude
|
val altitude: Double = location.altitude
|
||||||
if (altitude != 0.0) {
|
if (altitude != 0.0) {
|
||||||
|
|
||||||
// CASE: First location
|
|
||||||
if (numberOfWayPoints == 0) {
|
if (numberOfWayPoints == 0) {
|
||||||
track.maxAltitude = altitude
|
track.maxAltitude = altitude
|
||||||
track.minAltitude = altitude
|
track.minAltitude = altitude
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASE: Not first location
|
|
||||||
else {
|
else {
|
||||||
|
|
||||||
// Step 1: Update altitude values
|
|
||||||
if (altitude > track.maxAltitude) track.maxAltitude = altitude
|
if (altitude > track.maxAltitude) track.maxAltitude = altitude
|
||||||
if (altitude < track.minAltitude) track.minAltitude = 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
|
|
||||||
if (elevationDifferences != Pair(track.positiveElevation, track.negativeElevation)) {
|
|
||||||
// update elevation values (do not update if resumed -> we do not want to add values calculated during a recording pause)
|
|
||||||
if (!resumed) {
|
|
||||||
track.positiveElevation = elevationDifferences.first
|
|
||||||
track.negativeElevation = elevationDifferences.second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Step 3.3: Toggle stop over status, if necessary
|
||||||
// toggle stop over status, if necessary
|
|
||||||
if (track.wayPoints.size < 0) {
|
if (track.wayPoints.size < 0) {
|
||||||
track.wayPoints[track.wayPoints.size - 1].isStopOver = LocationHelper.isStopOver(previousLocation, location)
|
track.wayPoints[track.wayPoints.size - 1].isStopOver = LocationHelper.isStopOver(previousLocation, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
// save number of satellites
|
// Step 3.4: Add current location as point to center on for later display
|
||||||
val numberSatellites: Int
|
|
||||||
val extras = location.extras
|
|
||||||
if (extras != null && extras.containsKey("satellites")) {
|
|
||||||
numberSatellites = extras.getInt("satellites", 0)
|
|
||||||
} else {
|
|
||||||
numberSatellites = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// add current location as point to center on for later display
|
|
||||||
track.latitude = location.latitude
|
track.latitude = location.latitude
|
||||||
track.longitude = location.longitude
|
track.longitude = location.longitude
|
||||||
|
|
||||||
// add location as new waypoint
|
// Step 3.5: Add location as new waypoint
|
||||||
track.wayPoints.add(WayPoint(location, distanceToStartingPoint = track.length, numberSatellites = numberSatellites))
|
track.wayPoints.add(WayPoint(location = location, distanceToStartingPoint = track.length))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(track, shouldBeAdded)
|
return Pair(shouldBeAdded, track)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue