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
|
||||
|
||||
#ifndef BATT_CURRENT_AVE_SAMPLES
|
||||
/* TODO may need tweaking */
|
||||
#define BATT_CURRENT_AVE_SAMPLES 16
|
||||
/* semi arbitrary but needs to be 'large' for the time estimation algorithm */
|
||||
#define BATT_CURRENT_AVE_SAMPLES 128
|
||||
#endif
|
||||
|
||||
#ifndef POWER_THREAD_STEP_TICKS
|
||||
|
|
|
@ -102,6 +102,13 @@ static char power_stack[DEFAULT_STACK_SIZE/2];
|
|||
#endif
|
||||
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)
|
||||
int _battery_level(void) { return -1; }
|
||||
#endif
|
||||
|
@ -109,12 +116,16 @@ static int percent_now; /* Cached to avoid polling too often */
|
|||
|
||||
#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
|
||||
int _battery_time(void) { return -1; }
|
||||
#endif
|
||||
#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || \
|
||||
defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
|
||||
#else
|
||||
static int time_now; /* Cached to avoid polling too often */
|
||||
#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)
|
||||
int _battery_voltage(void) { return -1; }
|
||||
#else
|
||||
|
@ -149,8 +160,7 @@ int battery_level(void)
|
|||
* on the battery level and the actual current usage. */
|
||||
int battery_time(void)
|
||||
{
|
||||
#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || \
|
||||
defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
|
||||
#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || HAVE_TIME_ESTIMATION
|
||||
return time_now;
|
||||
#else
|
||||
return -1;
|
||||
|
@ -373,19 +383,43 @@ static void battery_status_update(void)
|
|||
|
||||
#if CONFIG_BATTERY_MEASURE & TIME_MEASURE
|
||||
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();
|
||||
if(level >= 0 && current > 0 && battery_capacity > 0) {
|
||||
int resolution = battery_capacity * 36;
|
||||
|
||||
int time_est;
|
||||
#if CONFIG_CHARGING >= CHARGING_MONITOR
|
||||
if (charging_state())
|
||||
time_now = (100 - level) * battery_capacity * 60 / 100 / current;
|
||||
time_est = (100 - level) * battery_capacity * 36 / current;
|
||||
else
|
||||
#endif
|
||||
time_now = (level + percent_now) * battery_capacity * 60 / 200 / current;
|
||||
} else {
|
||||
/* not enough information to calculate time remaining */
|
||||
time_now = -1;
|
||||
time_est = level * battery_capacity * 36 / current;
|
||||
|
||||
/* The first term nudges the counter toward the estimate.
|
||||
* 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
|
||||
|
||||
percent_now = level;
|
||||
|
|
Loading…
Reference in a new issue