summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
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
103static const char power_thread_name[] = "power"; 103static 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)
106int _battery_level(void) { return -1; } 113int _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)
111int _battery_time(void) { return -1; } 118int _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)
115static int time_now; /* Cached to avoid polling too often */ 120static int time_now; /* Cached to avoid polling too often */
116#endif 121#endif
117 122
123#if HAVE_TIME_ESTIMATION
124static int time_now; /* reported time in minutes */
125static int64_t time_cnt; /* reported time in seconds */
126static 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)
119int _battery_voltage(void) { return -1; } 130int _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. */
150int battery_time(void) 161int 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;