diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-12-23 02:49:50 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-12-28 10:47:40 -0500 |
commit | 96cfe329a612a8da5878f7bc20f2d645b30553a3 (patch) | |
tree | 61527d8c279719d94246fc37971d8bab6148ff8c | |
parent | 90dd2f84a9174c38dbfb07d582ec6ee7697b1939 (diff) | |
download | rockbox-96cfe329a612a8da5878f7bc20f2d645b30553a3.tar.gz rockbox-96cfe329a612a8da5878f7bc20f2d645b30553a3.zip |
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
-rw-r--r-- | firmware/export/powermgmt.h | 4 | ||||
-rw-r--r-- | firmware/powermgmt.c | 62 |
2 files changed, 50 insertions, 16 deletions
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h index 77177e61af..3095d2c97b 100644 --- a/firmware/export/powermgmt.h +++ b/firmware/export/powermgmt.h | |||
@@ -95,8 +95,8 @@ void powermgmt_init(void) INIT_ATTR; | |||
95 | #endif | 95 | #endif |
96 | 96 | ||
97 | #ifndef BATT_CURRENT_AVE_SAMPLES | 97 | #ifndef BATT_CURRENT_AVE_SAMPLES |
98 | /* TODO may need tweaking */ | 98 | /* semi arbitrary but needs to be 'large' for the time estimation algorithm */ |
99 | #define BATT_CURRENT_AVE_SAMPLES 16 | 99 | #define BATT_CURRENT_AVE_SAMPLES 128 |
100 | #endif | 100 | #endif |
101 | 101 | ||
102 | #ifndef POWER_THREAD_STEP_TICKS | 102 | #ifndef POWER_THREAD_STEP_TICKS |
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index 55da2a5a44..0f5b104d7c 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c | |||
@@ -102,6 +102,13 @@ static char power_stack[DEFAULT_STACK_SIZE/2]; | |||
102 | #endif | 102 | #endif |
103 | static const char power_thread_name[] = "power"; | 103 | static const char power_thread_name[] = "power"; |
104 | 104 | ||
105 | /* Time estimation requires 64 bit math so don't use it in the bootloader. | ||
106 | * Also we need to be able to measure current, and not have a better time | ||
107 | * estimate source available. */ | ||
108 | #define HAVE_TIME_ESTIMATION \ | ||
109 | (!defined(BOOTLOADER) && !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) && \ | ||
110 | (defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE))) | ||
111 | |||
105 | #if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) | 112 | #if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) |
106 | int _battery_level(void) { return -1; } | 113 | int _battery_level(void) { return -1; } |
107 | #endif | 114 | #endif |
@@ -109,12 +116,16 @@ static int percent_now; /* Cached to avoid polling too often */ | |||
109 | 116 | ||
110 | #if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) | 117 | #if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) |
111 | int _battery_time(void) { return -1; } | 118 | int _battery_time(void) { return -1; } |
112 | #endif | 119 | #else |
113 | #if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || \ | ||
114 | defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE) | ||
115 | static int time_now; /* Cached to avoid polling too often */ | 120 | static int time_now; /* Cached to avoid polling too often */ |
116 | #endif | 121 | #endif |
117 | 122 | ||
123 | #if HAVE_TIME_ESTIMATION | ||
124 | static int time_now; /* reported time in minutes */ | ||
125 | static int64_t time_cnt; /* reported time in seconds */ | ||
126 | static int64_t time_err; /* error... it's complicated */ | ||
127 | #endif | ||
128 | |||
118 | #if !(CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE) | 129 | #if !(CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE) |
119 | int _battery_voltage(void) { return -1; } | 130 | int _battery_voltage(void) { return -1; } |
120 | #else | 131 | #else |
@@ -149,8 +160,7 @@ int battery_level(void) | |||
149 | * on the battery level and the actual current usage. */ | 160 | * on the battery level and the actual current usage. */ |
150 | int battery_time(void) | 161 | int battery_time(void) |
151 | { | 162 | { |
152 | #if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || \ | 163 | #if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || HAVE_TIME_ESTIMATION |
153 | defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE) | ||
154 | return time_now; | 164 | return time_now; |
155 | #else | 165 | #else |
156 | return -1; | 166 | return -1; |
@@ -373,19 +383,43 @@ static void battery_status_update(void) | |||
373 | 383 | ||
374 | #if CONFIG_BATTERY_MEASURE & TIME_MEASURE | 384 | #if CONFIG_BATTERY_MEASURE & TIME_MEASURE |
375 | time_now = _battery_time(); | 385 | time_now = _battery_time(); |
376 | #elif defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE) | 386 | #elif HAVE_TIME_ESTIMATION |
387 | /* TODO: This is essentially a bad version of coloumb counting, | ||
388 | * so in theory using coloumb counters when they are available | ||
389 | * should provide a more accurate result. Also note that this | ||
390 | * is hard-coded with a HZ/2 update rate to simplify arithmetic. */ | ||
391 | |||
377 | int current = battery_current(); | 392 | int current = battery_current(); |
378 | if(level >= 0 && current > 0 && battery_capacity > 0) { | 393 | int resolution = battery_capacity * 36; |
394 | |||
395 | int time_est; | ||
379 | #if CONFIG_CHARGING >= CHARGING_MONITOR | 396 | #if CONFIG_CHARGING >= CHARGING_MONITOR |
380 | if (charging_state()) | 397 | if (charging_state()) |
381 | time_now = (100 - level) * battery_capacity * 60 / 100 / current; | 398 | time_est = (100 - level) * battery_capacity * 36 / current; |
382 | else | 399 | else |
383 | #endif | 400 | #endif |
384 | time_now = (level + percent_now) * battery_capacity * 60 / 200 / current; | 401 | time_est = level * battery_capacity * 36 / current; |
385 | } else { | 402 | |
386 | /* not enough information to calculate time remaining */ | 403 | /* The first term nudges the counter toward the estimate. |
387 | time_now = -1; | 404 | * The second term decrements the counter due to elapsed time. */ |
405 | time_err += current * (time_est - time_cnt); | ||
406 | time_err -= resolution; | ||
407 | |||
408 | /* Arbitrary cutoff to ensure we don't get too far out | ||
409 | * of sync. Seems to work well on synthetic tests. */ | ||
410 | if(time_err > resolution * 12 || | ||
411 | time_err < -resolution * 13) { | ||
412 | time_cnt = time_est; | ||
413 | time_err = 0; | ||
388 | } | 414 | } |
415 | |||
416 | /* Convert the error into a time and adjust the counter. */ | ||
417 | int64_t adjustment = time_err / (2 * resolution); | ||
418 | time_cnt += adjustment; | ||
419 | time_err -= adjustment * (2 * resolution); | ||
420 | |||
421 | /* Update the reported time based on the counter. */ | ||
422 | time_now = (time_cnt + 30) / 60; | ||
389 | #endif | 423 | #endif |
390 | 424 | ||
391 | percent_now = level; | 425 | percent_now = level; |