powermgmt: Better time estimation
This method, while far from perfect, is able to make use of real-time battery usage information and updates frequently in fine-grained increments. This should make time estimates a lot more useful than they previously were. Change-Id: I66c6daba88210f60a27e239fbbcc56869be3b878
This commit is contained in:
parent
90dd2f84a9
commit
96cfe329a6
2 changed files with 50 additions and 16 deletions
|
@ -95,8 +95,8 @@ void powermgmt_init(void) INIT_ATTR;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BATT_CURRENT_AVE_SAMPLES
|
#ifndef BATT_CURRENT_AVE_SAMPLES
|
||||||
/* TODO may need tweaking */
|
/* semi arbitrary but needs to be 'large' for the time estimation algorithm */
|
||||||
#define BATT_CURRENT_AVE_SAMPLES 16
|
#define BATT_CURRENT_AVE_SAMPLES 128
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef POWER_THREAD_STEP_TICKS
|
#ifndef POWER_THREAD_STEP_TICKS
|
||||||
|
|
|
@ -102,6 +102,13 @@ static char power_stack[DEFAULT_STACK_SIZE/2];
|
||||||
#endif
|
#endif
|
||||||
static const char power_thread_name[] = "power";
|
static const char power_thread_name[] = "power";
|
||||||
|
|
||||||
|
/* Time estimation requires 64 bit math so don't use it in the bootloader.
|
||||||
|
* Also we need to be able to measure current, and not have a better time
|
||||||
|
* estimate source available. */
|
||||||
|
#define HAVE_TIME_ESTIMATION \
|
||||||
|
(!defined(BOOTLOADER) && !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) && \
|
||||||
|
(defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)))
|
||||||
|
|
||||||
#if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
|
#if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
|
||||||
int _battery_level(void) { return -1; }
|
int _battery_level(void) { return -1; }
|
||||||
#endif
|
#endif
|
||||||
|
@ -109,12 +116,16 @@ static int percent_now; /* Cached to avoid polling too often */
|
||||||
|
|
||||||
#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
|
#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
|
||||||
int _battery_time(void) { return -1; }
|
int _battery_time(void) { return -1; }
|
||||||
#endif
|
#else
|
||||||
#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || \
|
|
||||||
defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
|
|
||||||
static int time_now; /* Cached to avoid polling too often */
|
static int time_now; /* Cached to avoid polling too often */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_TIME_ESTIMATION
|
||||||
|
static int time_now; /* reported time in minutes */
|
||||||
|
static int64_t time_cnt; /* reported time in seconds */
|
||||||
|
static int64_t time_err; /* error... it's complicated */
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !(CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
|
#if !(CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
|
||||||
int _battery_voltage(void) { return -1; }
|
int _battery_voltage(void) { return -1; }
|
||||||
#else
|
#else
|
||||||
|
@ -149,8 +160,7 @@ int battery_level(void)
|
||||||
* on the battery level and the actual current usage. */
|
* on the battery level and the actual current usage. */
|
||||||
int battery_time(void)
|
int battery_time(void)
|
||||||
{
|
{
|
||||||
#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || \
|
#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || HAVE_TIME_ESTIMATION
|
||||||
defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
|
|
||||||
return time_now;
|
return time_now;
|
||||||
#else
|
#else
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -373,19 +383,43 @@ static void battery_status_update(void)
|
||||||
|
|
||||||
#if CONFIG_BATTERY_MEASURE & TIME_MEASURE
|
#if CONFIG_BATTERY_MEASURE & TIME_MEASURE
|
||||||
time_now = _battery_time();
|
time_now = _battery_time();
|
||||||
#elif defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
|
#elif HAVE_TIME_ESTIMATION
|
||||||
|
/* TODO: This is essentially a bad version of coloumb counting,
|
||||||
|
* so in theory using coloumb counters when they are available
|
||||||
|
* should provide a more accurate result. Also note that this
|
||||||
|
* is hard-coded with a HZ/2 update rate to simplify arithmetic. */
|
||||||
|
|
||||||
int current = battery_current();
|
int current = battery_current();
|
||||||
if(level >= 0 && current > 0 && battery_capacity > 0) {
|
int resolution = battery_capacity * 36;
|
||||||
|
|
||||||
|
int time_est;
|
||||||
#if CONFIG_CHARGING >= CHARGING_MONITOR
|
#if CONFIG_CHARGING >= CHARGING_MONITOR
|
||||||
if (charging_state())
|
if (charging_state())
|
||||||
time_now = (100 - level) * battery_capacity * 60 / 100 / current;
|
time_est = (100 - level) * battery_capacity * 36 / current;
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
time_now = (level + percent_now) * battery_capacity * 60 / 200 / current;
|
time_est = level * battery_capacity * 36 / current;
|
||||||
} else {
|
|
||||||
/* not enough information to calculate time remaining */
|
/* The first term nudges the counter toward the estimate.
|
||||||
time_now = -1;
|
* The second term decrements the counter due to elapsed time. */
|
||||||
|
time_err += current * (time_est - time_cnt);
|
||||||
|
time_err -= resolution;
|
||||||
|
|
||||||
|
/* Arbitrary cutoff to ensure we don't get too far out
|
||||||
|
* of sync. Seems to work well on synthetic tests. */
|
||||||
|
if(time_err > resolution * 12 ||
|
||||||
|
time_err < -resolution * 13) {
|
||||||
|
time_cnt = time_est;
|
||||||
|
time_err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert the error into a time and adjust the counter. */
|
||||||
|
int64_t adjustment = time_err / (2 * resolution);
|
||||||
|
time_cnt += adjustment;
|
||||||
|
time_err -= adjustment * (2 * resolution);
|
||||||
|
|
||||||
|
/* Update the reported time based on the counter. */
|
||||||
|
time_now = (time_cnt + 30) / 60;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
percent_now = level;
|
percent_now = level;
|
||||||
|
|
Loading…
Reference in a new issue