Describe exact power management state transitions in readme.
This commit is contained in:
parent
04d27ab984
commit
01a313a69c
5 changed files with 50 additions and 27 deletions
26
README.md
26
README.md
|
@ -19,11 +19,31 @@ The goal of this fork is to make 24/7 recording easier. I want to be able to run
|
||||||
|
|
||||||
## Power management
|
## Power management
|
||||||
|
|
||||||
When you are near a homepoint, trkpt will slow down the GPS polling frequency to reduce power consumption. When trkpt detects movement from the device's accelerometers, or when the GPS detects you are away from the homepoint, it will wake back up to full power.
|
trkpt has three states of power management. The states transition like this:
|
||||||
|
|
||||||
If the GPS is completely unable to receive a fix because you are indoors, underground, or trapped in a Faraday cage, trkpt will turn it off after a few minutes. Without any fix, we can't even tell if we're near a homepoint, and the GPS burns a lot of energy trying. Again, the motion sensor will wake it back up to full power.
|
1. **FULL POWER**: receives location updates as fast as Android provides them.
|
||||||
|
|
||||||
When you are away from a homepoint, and the GPS is not struggling, trkpt will always run the GPS at full power.
|
Stay near homepoint for a few minutes → Sleep
|
||||||
|
|
||||||
|
Unable to receive fix for several minutes and not charging → Dead
|
||||||
|
|
||||||
|
2. **SLEEPING**: receives location updates at a slower pace.
|
||||||
|
|
||||||
|
Motion sensors → Full power
|
||||||
|
|
||||||
|
Location leaves homepoint → Full power (presumably motion sensors will trigger, but just in case)
|
||||||
|
|
||||||
|
Unplugged from charger → Full power (maybe you are getting ready to depart)
|
||||||
|
|
||||||
|
Unable to receive fix for several minutes and not charging → Dead (time is doubled to accommodate slower sleeping pace)
|
||||||
|
|
||||||
|
3. **DEAD**: disables location updates.
|
||||||
|
|
||||||
|
Motion sensors → Full power
|
||||||
|
|
||||||
|
Plugged in to charger → Full power
|
||||||
|
|
||||||
|
Although saving battery power is important, capturing trackpoints is the #1 priority. I'd rather have a few too many wakeups than too few.
|
||||||
|
|
||||||
If your device doesn't support the motion sensors used here, then trkpt will always run at full power. It will not sleep or kill the GPS. Maybe we can find another solution to improve battery performance for devices in this scenario.
|
If your device doesn't support the motion sensors used here, then trkpt will always run at full power. It will not sleep or kill the GPS. Maybe we can find another solution to improve battery performance for devices in this scenario.
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ object Keys {
|
||||||
const val STATE_TRACKING_ACTIVE: Int = 1
|
const val STATE_TRACKING_ACTIVE: Int = 1
|
||||||
const val LOCATION_INTERVAL_FULL_POWER: Long = 0
|
const val LOCATION_INTERVAL_FULL_POWER: Long = 0
|
||||||
const val LOCATION_INTERVAL_SLEEP: Long = ONE_MINUTE_IN_MILLISECONDS
|
const val LOCATION_INTERVAL_SLEEP: Long = ONE_MINUTE_IN_MILLISECONDS
|
||||||
const val LOCATION_INTERVAL_GIVE_UP: Long = -1
|
const val LOCATION_INTERVAL_DEAD: Long = -1
|
||||||
const val STATE_THEME_FOLLOW_SYSTEM: String = "stateFollowSystem"
|
const val STATE_THEME_FOLLOW_SYSTEM: String = "stateFollowSystem"
|
||||||
const val STATE_THEME_LIGHT_MODE: String = "stateLightMode"
|
const val STATE_THEME_LIGHT_MODE: String = "stateLightMode"
|
||||||
const val STATE_THEME_DARK_MODE: String = "stateDarkMode"
|
const val STATE_THEME_DARK_MODE: String = "stateDarkMode"
|
||||||
|
|
|
@ -418,7 +418,7 @@ class MapFragment : Fragment()
|
||||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_skull_24dp)!!
|
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_skull_24dp)!!
|
||||||
description = "No location listeners are enabled"
|
description = "No location listeners are enabled"
|
||||||
}
|
}
|
||||||
else if (tracker.trackingState == Keys.STATE_TRACKING_ACTIVE && tracker.location_interval == Keys.LOCATION_INTERVAL_GIVE_UP)
|
else if (tracker.trackingState == Keys.STATE_TRACKING_ACTIVE && tracker.location_interval == Keys.LOCATION_INTERVAL_DEAD)
|
||||||
{
|
{
|
||||||
fillcolor = Color.argb(64, 0, 0, 0)
|
fillcolor = Color.argb(64, 0, 0, 0)
|
||||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_skull_24dp)!!
|
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_skull_24dp)!!
|
||||||
|
|
|
@ -59,7 +59,7 @@ class TrackerService: Service()
|
||||||
var arrived_at_home: Long = 0
|
var arrived_at_home: Long = 0
|
||||||
var location_interval: Long = 0
|
var location_interval: Long = 0
|
||||||
val TIME_UNTIL_SLEEP: Long = 2 * Keys.ONE_MINUTE_IN_MILLISECONDS
|
val TIME_UNTIL_SLEEP: Long = 2 * Keys.ONE_MINUTE_IN_MILLISECONDS
|
||||||
val TIME_UNTIL_GIVE_UP: Long = 3 * Keys.ONE_MINUTE_IN_MILLISECONDS
|
val TIME_UNTIL_DEAD: Long = 3 * Keys.ONE_MINUTE_IN_MILLISECONDS
|
||||||
val WATCHDOG_INTERVAL: Long = 30 * Keys.ONE_SECOND_IN_MILLISECONDS
|
val WATCHDOG_INTERVAL: Long = 30 * Keys.ONE_SECOND_IN_MILLISECONDS
|
||||||
private val RECENT_TRKPT_COUNT = 3600
|
private val RECENT_TRKPT_COUNT = 3600
|
||||||
private val DISPLACEMENT_LOCATION_COUNT = 5
|
private val DISPLACEMENT_LOCATION_COUNT = 5
|
||||||
|
@ -178,7 +178,7 @@ class TrackerService: Service()
|
||||||
location_interval = interval
|
location_interval = interval
|
||||||
var gps_added = false
|
var gps_added = false
|
||||||
var network_added = false
|
var network_added = false
|
||||||
if (use_gps_location && interval != Keys.LOCATION_INTERVAL_GIVE_UP)
|
if (use_gps_location && interval != Keys.LOCATION_INTERVAL_DEAD)
|
||||||
{
|
{
|
||||||
gps_added = addGpsLocationListener(interval)
|
gps_added = addGpsLocationListener(interval)
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ class TrackerService: Service()
|
||||||
{
|
{
|
||||||
removeGpsLocationListener()
|
removeGpsLocationListener()
|
||||||
}
|
}
|
||||||
if (use_network_location && interval != Keys.LOCATION_INTERVAL_GIVE_UP)
|
if (use_network_location && interval != Keys.LOCATION_INTERVAL_DEAD)
|
||||||
{
|
{
|
||||||
network_added = addNetworkLocationListener(interval)
|
network_added = addNetworkLocationListener(interval)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ class TrackerService: Service()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listeners_enabled_at = 0
|
listeners_enabled_at = 0
|
||||||
location_interval = Keys.LOCATION_INTERVAL_GIVE_UP
|
location_interval = Keys.LOCATION_INTERVAL_DEAD
|
||||||
}
|
}
|
||||||
displayNotification()
|
displayNotification()
|
||||||
}
|
}
|
||||||
|
@ -374,9 +374,9 @@ class TrackerService: Service()
|
||||||
notification_builder.setContentTitle("${timestamp} (sleeping)")
|
notification_builder.setContentTitle("${timestamp} (sleeping)")
|
||||||
notification_builder.setSmallIcon(R.drawable.ic_sleep_24dp)
|
notification_builder.setSmallIcon(R.drawable.ic_sleep_24dp)
|
||||||
}
|
}
|
||||||
else if (location_interval == Keys.LOCATION_INTERVAL_GIVE_UP)
|
else if (location_interval == Keys.LOCATION_INTERVAL_DEAD)
|
||||||
{
|
{
|
||||||
notification_builder.setContentTitle("${timestamp} (deadzone)")
|
notification_builder.setContentTitle("${timestamp} (dead)")
|
||||||
notification_builder.setSmallIcon(R.drawable.ic_skull_24dp)
|
notification_builder.setSmallIcon(R.drawable.ic_skull_24dp)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -449,7 +449,7 @@ class TrackerService: Service()
|
||||||
override fun onBind(p0: Intent?): IBinder
|
override fun onBind(p0: Intent?): IBinder
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackerService.onBind")
|
Log.i("VOUSSOIR", "TrackerService.onBind")
|
||||||
if (listeners_enabled_at == 0L && location_interval != Keys.LOCATION_INTERVAL_GIVE_UP)
|
if (listeners_enabled_at == 0L && location_interval != Keys.LOCATION_INTERVAL_DEAD)
|
||||||
{
|
{
|
||||||
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
}
|
}
|
||||||
|
@ -462,7 +462,7 @@ class TrackerService: Service()
|
||||||
override fun onRebind(intent: Intent?)
|
override fun onRebind(intent: Intent?)
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackerService.onRebind")
|
Log.i("VOUSSOIR", "TrackerService.onRebind")
|
||||||
if (listeners_enabled_at == 0L && location_interval != Keys.LOCATION_INTERVAL_GIVE_UP)
|
if (listeners_enabled_at == 0L && location_interval != Keys.LOCATION_INTERVAL_DEAD)
|
||||||
{
|
{
|
||||||
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,7 @@ class TrackerService: Service()
|
||||||
// stop receiving location updates - if not tracking
|
// stop receiving location updates - if not tracking
|
||||||
if (trackingState != Keys.STATE_TRACKING_ACTIVE)
|
if (trackingState != Keys.STATE_TRACKING_ACTIVE)
|
||||||
{
|
{
|
||||||
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_GIVE_UP)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_DEAD)
|
||||||
}
|
}
|
||||||
// ensures onRebind is called
|
// ensures onRebind is called
|
||||||
return true
|
return true
|
||||||
|
@ -530,7 +530,7 @@ class TrackerService: Service()
|
||||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE && location_interval != Keys.LOCATION_INTERVAL_FULL_POWER)
|
if (trackingState == Keys.STATE_TRACKING_ACTIVE && location_interval != Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
{
|
{
|
||||||
vibrator.vibrate(100)
|
vibrator.vibrate(100)
|
||||||
reset_location_listeners(Keys.LOCATION_INTERVAL_FULL_POWER)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
}
|
}
|
||||||
sensor_manager.requestTriggerSensor(this, significant_motion_sensor)
|
sensor_manager.requestTriggerSensor(this, significant_motion_sensor)
|
||||||
}
|
}
|
||||||
|
@ -551,7 +551,7 @@ class TrackerService: Service()
|
||||||
{
|
{
|
||||||
// beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
|
// beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
|
||||||
vibrator.vibrate(100)
|
vibrator.vibrate(100)
|
||||||
reset_location_listeners(Keys.LOCATION_INTERVAL_FULL_POWER)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,15 +571,21 @@ class TrackerService: Service()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Power connected")
|
Log.i("VOUSSOIR", "Power connected")
|
||||||
device_is_charging = true
|
device_is_charging = true
|
||||||
if (location_interval == Keys.LOCATION_INTERVAL_GIVE_UP)
|
if (location_interval == Keys.LOCATION_INTERVAL_DEAD)
|
||||||
{
|
{
|
||||||
reset_location_listeners(Keys.LOCATION_INTERVAL_FULL_POWER)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (intent.action == Intent.ACTION_POWER_DISCONNECTED)
|
else if (intent.action == Intent.ACTION_POWER_DISCONNECTED)
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "Power disconnected")
|
Log.i("VOUSSOIR", "Power disconnected")
|
||||||
device_is_charging = false
|
device_is_charging = false
|
||||||
|
// If the user has just unplugged their device, maybe they're getting ready
|
||||||
|
// to go out.
|
||||||
|
if (location_interval == Keys.LOCATION_INTERVAL_SLEEP)
|
||||||
|
{
|
||||||
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -630,7 +636,7 @@ class TrackerService: Service()
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12
|
notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12
|
||||||
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_GIVE_UP)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_DEAD)
|
||||||
handler.removeCallbacks(background_watchdog)
|
handler.removeCallbacks(background_watchdog)
|
||||||
unregisterReceiver(charging_broadcast_receiver)
|
unregisterReceiver(charging_broadcast_receiver)
|
||||||
}
|
}
|
||||||
|
@ -683,7 +689,7 @@ class TrackerService: Service()
|
||||||
allow_sleep = PreferencesHelper.loadAllowSleep()
|
allow_sleep = PreferencesHelper.loadAllowSleep()
|
||||||
if (! allow_sleep && location_interval != Keys.LOCATION_INTERVAL_FULL_POWER)
|
if (! allow_sleep && location_interval != Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
{
|
{
|
||||||
reset_location_listeners(Keys.LOCATION_INTERVAL_FULL_POWER)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keys.PREF_DEVICE_ID ->
|
Keys.PREF_DEVICE_ID ->
|
||||||
|
@ -715,19 +721,19 @@ class TrackerService: Service()
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
last_watchdog = now
|
last_watchdog = now
|
||||||
|
|
||||||
val struggletime = if (location_interval == Keys.LOCATION_INTERVAL_SLEEP) (TIME_UNTIL_GIVE_UP * 2) else TIME_UNTIL_GIVE_UP
|
val struggletime = if (location_interval == Keys.LOCATION_INTERVAL_SLEEP) (TIME_UNTIL_DEAD * 2) else TIME_UNTIL_DEAD
|
||||||
if (
|
if (
|
||||||
allow_sleep &&
|
allow_sleep &&
|
||||||
has_motion_sensor &&
|
has_motion_sensor &&
|
||||||
!device_is_charging &&
|
!device_is_charging &&
|
||||||
trackingState == Keys.STATE_TRACKING_ACTIVE &&
|
trackingState == Keys.STATE_TRACKING_ACTIVE &&
|
||||||
location_interval != Keys.LOCATION_INTERVAL_GIVE_UP &&
|
location_interval != Keys.LOCATION_INTERVAL_DEAD &&
|
||||||
(now - listeners_enabled_at) > struggletime &&
|
(now - listeners_enabled_at) > struggletime &&
|
||||||
(now - currentBestLocation.time) > struggletime &&
|
(now - currentBestLocation.time) > struggletime &&
|
||||||
(now - last_significant_motion) > struggletime
|
(now - last_significant_motion) > struggletime
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
reset_location_listeners(Keys.LOCATION_INTERVAL_GIVE_UP)
|
reset_location_listeners(interval=Keys.LOCATION_INTERVAL_DEAD)
|
||||||
gave_up_at = now
|
gave_up_at = now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,13 +75,10 @@ object PreferencesHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadTrackingState(): Int {
|
fun loadTrackingState(): Int {
|
||||||
val state = sharedPreferences.getInt(Keys.PREF_TRACKING_STATE, Keys.STATE_TRACKING_STOPPED)
|
return sharedPreferences.getInt(Keys.PREF_TRACKING_STATE, Keys.STATE_TRACKING_STOPPED)
|
||||||
Log.i("VOUSSOIR", "PreferencesHelper.loadTrackingState ${state}")
|
|
||||||
return state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveTrackingState(trackingState: Int) {
|
fun saveTrackingState(trackingState: Int) {
|
||||||
Log.i("VOUSSOIR", "PreferencesHelper.saveTrackingState ${trackingState}")
|
|
||||||
sharedPreferences.edit { putInt(Keys.PREF_TRACKING_STATE, trackingState) }
|
sharedPreferences.edit { putInt(Keys.PREF_TRACKING_STATE, trackingState) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue