summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-12-23 02:49:50 +0000
committerAidan MacDonald <amachronic@protonmail.com>2021-12-28 10:47:40 -0500
commit96cfe329a612a8da5878f7bc20f2d645b30553a3 (patch)
tree61527d8c279719d94246fc37971d8bab6148ff8c /firmware
parent90dd2f84a9174c38dbfb07d582ec6ee7697b1939 (diff)
downloadrockbox-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
Diffstat (limited to 'firmware')
-rw-r--r--firmware/export/powermgmt.h4
-rw-r--r--firmware/powermgmt.c62
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;
#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
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];
#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;
- else
+ if (charging_state())
+ 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;