From: Kichan Kwon Date: Fri, 25 Nov 2016 05:50:35 +0000 (+0900) Subject: heart : upgrade HEART-battery X-Git-Tag: accepted/tizen/3.0/ivi/20161205.235125^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=39ec9d7687f7419f646ad9d5f028ed3d72680121;p=platform%2Fcore%2Fsystem%2Fresourced.git heart : upgrade HEART-battery 1) Stability - Use enum for charger status instead of integer - Fix memory leak - Asynchronous (level)DB updating 2) New function - Discharge rate : HIGH(1.2x~), LOW(~0.8x), compare with predicted rate - New logic for battery remaining time estimation - Calculate SOE(State Of Energy) decreasing rate - SOE is considered to the integral of OCP(Open-Circuit Potential) - To use new logic, vendor have to support battery capacity and OCV coefficients Change-Id: Ic80073028d8fde16980a53d8cb550023b56cb8b8 Signed-off-by: Kichan Kwon --- diff --git a/src/common/heart-common.h b/src/common/heart-common.h index b915126..da6f06b 100644 --- a/src/common/heart-common.h +++ b/src/common/heart-common.h @@ -39,6 +39,20 @@ enum heart_data_period { DATA_1WEEK, }; +enum charger_status_type { + DISCHARGING = 0, + CHARGING = 1, + MAX_CHARGER_STATE = 2, +}; + +enum discharge_rate_level_type { + BATTERY_DISCHARGE_NONE = -1, + BATTERY_DISCHARGE_LOW = 0, + BATTERY_DISCHARGE_AVG = 1, + BATTERY_DISCHARGE_HIGH = 2, + BATTERY_DISCHARGE_MAX = 3, +}; + struct heart_cpu_data { char appid[MAX_APPID_LENGTH]; char pkgid[MAX_PKGNAME_LENGTH]; @@ -69,10 +83,14 @@ struct heart_battery_capacity { int diff_capacity; long used_time; long charging_time; - int charger_status; + enum charger_status_type charger_status; int reset_mark; }; -/* battery capacity history*/ + +/* battery */ +int heart_battery_get_capacity(void); +enum charger_status_type heart_battery_get_charger_status(void); +enum discharge_rate_level_type heart_battery_get_discharge_rate_level(void); int heart_battery_get_capacity_history_latest(GArray *arrays, int charge, int max_size); int heart_battery_get_capacity_history(GArray *arrays, enum heart_data_period period); diff --git a/src/heart/heart-battery.c b/src/heart/heart-battery.c index 2bc703e..6b7c388 100644 --- a/src/heart/heart-battery.c +++ b/src/heart/heart-battery.c @@ -39,8 +39,11 @@ #include "heart-common.h" #include "config-parser.h" #include "trace.h" +#include "vconf.h" #include "module.h" #include "macro.h" +#include "util.h" +#include "file-helper.h" #define TIZEN_SYSTEM_APPID "org.tizen.system" #define TIZEN_SYSTEM_BATTERY_APPID "org.tizen.system.battery.capacity" @@ -66,6 +69,8 @@ #define BATTERY_USAGE_LEARNING -1 #define CUMUL_WEIGHT (0.8) #define TREND_WEIGHT (1 - CUMUL_WEIGHT) +#define DISCHARGE_BASE_RATE (0.2) + /* * BATTERY_PREDICTION_LATEST_COUNT must be >= BATTERY_PREDICTION_DATA_MIN */ @@ -83,6 +88,36 @@ #define BATTERY_LEVEL_USAGE "BATTERY_LEVEL_USAGE" #define BATTERY_PREDICTION "BATTERY_PREDICTION" +#define INDEX_WINDOW_SIZE 10 /*data storing index*/ +#define BATTERY_LEVEL_GAP 5 /*previous 5 battery data*/ +#define LONG_TIME_WEIGHT (0.7) /*weightage given for longer time discharge*/ +#define SHORT_TIME_WEIGHT (1-LONG_TIME_WEIGHT) /*weightage given for short time discharge*/ +#define MIN_TIME_FOR_LVL_CHANGE 50 + +/* + * Below 6 values are the co-efficient of the polynomial. These are devired from + * OCV and and battery percentage level based on experiment + */ +#define OCV_SOC_POLY_COEF_1 (3402.664) +#define OCV_SOC_POLY_COEF_2 (42.031) +#define OCV_SOC_POLY_COEF_3 (-1.76745) +#define OCV_SOC_POLY_COEF_4 (0.034798) +#define OCV_SOC_POLY_COEF_5 (-0.00030516) +#define OCV_SOC_POLY_COEF_6 (0.0000010116) +#define UPS_FACTOR (2.88) +#define DOUBLE_ZERO (0.000000) +#define FULL_CAPACITY 100 +/* BATTERY_C_RATE is defined as the rate of which battery capacity is changing + * If total battery capacity is XmAh, then in one second battery capacity change + * will be considered as 90 + */ +#define BATTERY_C_RATE 90 +#define SEC_TO_MIN(x) ((x)/60) +#define CAL_MIN(x, y) ((x < y) ? x : y) +#define BATTERY_WINDOW_INDEX(x) (((x) + INDEX_WINDOW_SIZE) % INDEX_WINDOW_SIZE) +#define HEART_BATTERY_DATA_FILE HEART_FILE_PATH"/.battery_data.dat" +#define VCONFKEY_HEART_BATTERY_DEVICE_MODE "db/setting/psmode" + enum { TA = 0, /* prediction based on total data average */ PCB = 1, /* prediction with physiological behaviors */ @@ -99,12 +134,6 @@ enum { POWER_MODE_MAX = 3, }; -enum charging_goal { - DISCHARGING = 0, - CHARGING = 1, - MAX_CHARGER_STATE = 2, -}; - enum { BATTERY_LEVEL_LOW = 0, /* 15 ~ 0 */ BATTERY_LEVEL_MID = 1, /* 49 ~ 16 */ @@ -139,15 +168,35 @@ struct battery_prediction { struct battery_status { /* current battery status */ - int curr_charger_status; + enum charger_status_type curr_charger_status; int curr_capacity; + enum discharge_rate_level_type discharge_rate_level; /* current runtime statistics */ long curr_run_time_sec[MAX_CHARGER_STATE]; /* seconds since reset */ long curr_cap_counter[MAX_CHARGER_STATE]; /* capacity level changes */ + long last_wall_time[INDEX_WINDOW_SIZE]; /* previous battery level discharge time */ + /* previous battery level change time in charging mode */ + long last_wall_time_chg; + /* charging data index*/ + int index_chg; + /* store last 5 battery level change time diff */ + int last_wall_time_chg_diff[BATTERY_LEVEL_GAP]; + /* indicates whether do we have enough data to estimate the battery time */ + int data_available; + int curr_index; /* store current index value */ + int last_capacity; /* last battery capacity value */ + int last_capacity_chg; /* last battery capacity value in charge mode */ + int remaining_time; /* previous remaining time */ + int remaining_time_chg; /* previous remaining time in charging mode */ + int remaining_time_ups; /* previous remaining time in UPS */ + double last_volt_intg[INDEX_WINDOW_SIZE]; /* store previous calculated voltage integral(energy) value */ + double last_pwr_bchg; /* store the battery power available just before charging start */ + /* wall clock time stamp when last event happened in seconds */ time_t last_event_wall_time; + time_t last_clock_tick; /* * reset mark is set when battery is charged in over 90% and @@ -172,6 +221,12 @@ struct battery_status { struct battery_prediction prediction[MAX_CHARGER_STATE]; }; +struct thread_data { + char *key; + void *data; + int len; +}; + static int default_sec_per_cap[MAX_CHARGER_STATE][DEFAULT_VALUE_MAX] = { { 70, 670, 3600 }, /* DISCHARGING MIN: 70s, AVG: 670s, MAX: 1 hour */ { 30, 80, 3600 } /* CHARGING MIN: 30s, AVG: 80s, MAX: 1 hour */ @@ -196,6 +251,88 @@ static pthread_mutex_t heart_battery_mutex = PTHREAD_MUTEX_INITIALIZER; static time_t last_file_commit_time; static int battery_learning_mode; +static int ocv_degree; /* Degree of the OCV and SOC polynomial */ +static double *intg; /* Co-efficients of the OCV and SOC polynomial */ +static double pivot_nor; /* time pivot for normal mode */ +static double pivot_ups; /* time pivot for UPS mode */ +static double volt_intg_full; /* 100% battery voltage integral(engery) value */ +static bool data_avail_chg = false; /* 5 readings in charging mode */ + /* + * variable to hold the current device mode(NORMAL, PS & UPS) + */ +static int device_mode = POWER_NORMAL_MODE; +/* + * Given the 'SOC', remaining energy of the cell can be predicted. New battery + * remaining time estimation logic will calculating the remaining energy as + * 'curr_volt_intg'. + * + * Based on estimated average power(P), a time estimate can be calcluated. + * Power can be estimated based on constant SOC interval Ps and constant + * time interval Pt. + * + * At high usage Ps will be short term average power and Pt will be long term + * average power. + * + * At low usage Ps will be long term average power and Pt will be short term + * average power. + * + * The new logic will calculate the average power 'batt_pwr' from Ps and Pt. + * + * Under-relaxation is a popular and effective technique of stabilizing prediction. + * The predicted time is stabilized around a pivot, which is calculated from the + * high-usage time and low-usage time + * + * The new logic will calculated 'pivot_nor' and 'pivot_ups' for normal and UPS + * mode respectively + * + * Finally, using current available energy(curr_volt_intg), average power(batt_pwr) + * and pivot time (pivot_nor or pivot_ups) this new logic will estimate the Battery + * Remaining Time for normal mode and UPS mode + */ +/* + * new battery remaining time estimation logic + */ +static int logic_v2; +/* + * Total time taken in minutes when device is discharging at fastest rate + */ +static int discharge_fast; +/* + * Total time taken in minutes when device is discharging at slowest rate + */ +static int discharge_slow; +/* + * Avarage power consumed in UPS mode + */ +static double average_pwr; +/* + * Total battery capacity in minutes(mAM) + */ +static int total_battery_capacity; +/* + * This variable indicates first battery level update after charger insert + */ +static bool first_level_change = FALSE; +/* + * This variable indicates the battery header used in heart.conf file + */ +static char battery_header[BATTERY_LINE_MAX] = "BATTERY"; + +int heart_battery_get_capacity(void) +{ + return batt_stat.curr_capacity; +} + +enum charger_status_type heart_battery_get_charger_status(void) +{ + return batt_stat.curr_charger_status; +} + +enum discharge_rate_level_type heart_battery_get_discharge_rate_level(void) +{ + return batt_stat.discharge_rate_level; +} + inline void heart_battery_set_usage_reset_stime(int history, time_t start_time) { batt_stat.batt_reset_usage[history].start_time = start_time; @@ -206,18 +343,18 @@ inline time_t heart_battery_get_usage_reset_stime(int history) return batt_stat.batt_reset_usage[history].start_time; } -inline void heart_battery_set_usage_reset(int history, int status, long sec_per_cap, long cap_counter) +inline void heart_battery_set_usage_reset(int history, enum charger_status_type status, long sec_per_cap, long cap_counter) { batt_stat.batt_reset_usage[history].sec_per_cap[status] = sec_per_cap; batt_stat.batt_reset_usage[history].cap_counter[status] = cap_counter; } -inline long heart_battery_get_usage_reset_total_time(int history, int status) +inline long heart_battery_get_usage_reset_total_time(int history, enum charger_status_type status) { return batt_stat.batt_reset_usage[history].sec_per_cap[status] * batt_stat.batt_reset_usage[history].cap_counter[status]; } -inline long heart_battery_get_usage_reset_count(int history, int status) +inline long heart_battery_get_usage_reset_count(int history, enum charger_status_type status) { return batt_stat.batt_reset_usage[history].cap_counter[status]; } @@ -232,33 +369,33 @@ inline time_t heart_battery_get_usage_level_stime(int level) return batt_stat.batt_lvl_usage[level].start_time; } -inline void heart_battery_set_usage_level(int level, int status, long sec_per_cap, long cap_counter) +inline void heart_battery_set_usage_level(int level, enum charger_status_type status, long sec_per_cap, long cap_counter) { batt_stat.batt_lvl_usage[level].sec_per_cap[status] = sec_per_cap; batt_stat.batt_lvl_usage[level].cap_counter[status] = cap_counter; } -inline long heart_battery_get_usage_level_total_time(int level, int status) +inline long heart_battery_get_usage_level_total_time(int level, enum charger_status_type status) { return batt_stat.batt_lvl_usage[level].sec_per_cap[status] * batt_stat.batt_lvl_usage[level].cap_counter[status]; } -inline long heart_battery_get_usage_level_spc(int level, int status) +inline long heart_battery_get_usage_level_spc(int level, enum charger_status_type status) { return batt_stat.batt_lvl_usage[level].sec_per_cap[status]; } -inline long heart_battery_get_usage_level_count(int level, int status) +inline long heart_battery_get_usage_level_count(int level, enum charger_status_type status) { return batt_stat.batt_lvl_usage[level].cap_counter[status]; } -inline long heart_battery_get_usage_week_total_time(int day, int status) +inline long heart_battery_get_usage_week_total_time(int day, enum charger_status_type status) { return batt_stat.week_day_usage[day].sec_per_cap[status] * batt_stat.week_day_usage[day].cap_counter[status]; } -inline long heart_battery_get_usage_week_count(int day, int status) +inline long heart_battery_get_usage_week_count(int day, enum charger_status_type status) { return batt_stat.week_day_usage[day].cap_counter[status]; } @@ -277,6 +414,14 @@ inline int heart_battery_get_learning_mode(void) { int i, count = 0; + if (logic_v2) { /* new logic */ + /*wait till enough data gets collected to estimated battery remaining time */ + if (batt_stat.data_available == TRUE) { + return 1; + } + return 0; + } + /* old logic */ for (i = 0; i < BATTERY_HISTORY_DAY_MAX; i++) { if (heart_battery_get_usage_week_stime(i)) count++; @@ -286,20 +431,20 @@ inline int heart_battery_get_learning_mode(void) return 0; } -inline void heart_battery_set_usage_week(int day, int status, long sec_per_cap, long cap_counter) +inline void heart_battery_set_usage_week(int day, enum charger_status_type status, long sec_per_cap, long cap_counter) { batt_stat.week_day_usage[day].sec_per_cap[status] = sec_per_cap; batt_stat.week_day_usage[day].cap_counter[status] = cap_counter; } -inline void heart_battery_set_prediction(int strategy, int status, long sec_per_cap, long cap_counter, long pred_min) +inline void heart_battery_set_prediction(int strategy, enum charger_status_type status, long sec_per_cap, long cap_counter, long pred_min) { batt_stat.prediction[status].sec_per_cap[strategy] = sec_per_cap; batt_stat.prediction[status].cap_counter[strategy] = cap_counter; batt_stat.prediction[status].time_pred_min[strategy] = pred_min; } -inline long heart_battery_get_prediction_time(int strategy, int status) +inline long heart_battery_get_prediction_time(int strategy, enum charger_status_type status) { return batt_stat.prediction[status].time_pred_min[strategy]; } @@ -314,13 +459,98 @@ inline void heart_battery_set_file_commit_timestamp(time_t timestamp) last_file_commit_time = timestamp; } +static void *async_db_task(void *data) +{ + char *key; + struct battery_used *used; + struct battery_status *status; + struct thread_data *pdata = (struct thread_data *)data; + if (!pdata) { + _E("Invalid Argument"); + return NULL; + } + key = pdata->key; + + _D("Task Key : %s", key); + if (!strncmp(BATTERY_USED_TIME, key, sizeof(BATTERY_USED_TIME) + 1)) { + used = (struct battery_used *)pdata->data; + logging_leveldb_putv(key, strlen(key), "%d %d ", + used->used_time_sec, used->last_update_time); + } else if (!strncmp(BATTERY_STATUS, key, sizeof(BATTERY_STATUS) + 1)) { + status = (struct battery_status *)pdata->data; + logging_leveldb_putv(key, strlen(key), "%d %ld %ld %ld %ld %d %d ", + status->curr_capacity, + status->curr_run_time_sec[DISCHARGING], + status->curr_cap_counter[DISCHARGING], + status->curr_run_time_sec[CHARGING], + status->curr_cap_counter[CHARGING], + status->curr_charger_status, + status->reset_mark); + } else if (!strncmp(BATTERY_RESET_USAGE, key, sizeof(BATTERY_RESET_USAGE) + 1) || + !strncmp(BATTERY_WEEK_DAY_USAGE, key, sizeof(BATTERY_WEEK_DAY_USAGE) + 1) || + !strncmp(BATTERY_LEVEL_USAGE, key, sizeof(BATTERY_LEVEL_USAGE) + 1) || + !strncmp(BATTERY_PREDICTION, key, sizeof(BATTERY_PREDICTION) + 1)) { + logging_leveldb_put(key, strlen(key), (char *)pdata->data, pdata->len); + } + free(pdata); + return NULL; +} + +static void update_db(char *key, void *data, int len) +{ + pthread_t thread_id; + int ret; + pthread_attr_t attr; + + struct thread_data *pdata = (struct thread_data *)malloc(sizeof(struct thread_data)); + if (!pdata) { + _E("malloc failed"); + return; + } + pdata->key = key; + pdata->data = data; + pdata->len = len; + + ret = pthread_attr_init(&attr); + if (ret) + _E("pthread pthread_attr_init failed, %d", ret); + + ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + if (ret) + _E("pthread pthread_attr_setdetachstate failed, %d", ret); + + ret = pthread_create(&thread_id, &attr, (void *)async_db_task, (void *)pdata); + if (ret) { + _E("pthread creation for async_task failed, %d", ret); + free(pdata); + } +} + +static int heart_battery_calculate_discharge_rate_level(long spc) +{ + long gap, total_spc; + + total_spc = batt_stat.prediction[DISCHARGING].sec_per_cap[TA]; + if (!total_spc) + return BATTERY_DISCHARGE_NONE; + gap = (total_spc * DISCHARGE_BASE_RATE); + + _I("total: %ld, spc: %ld, gap: %ld", total_spc, spc, gap); + + if (total_spc + gap < spc) + return BATTERY_DISCHARGE_LOW; + else if (total_spc - gap > spc) + return BATTERY_DISCHARGE_HIGH; + + return BATTERY_DISCHARGE_AVG; +} + static int heart_battery_save_used_time(char *key, struct battery_used *used) { if (!key || !used) return RESOURCED_ERROR_FAIL; - logging_leveldb_putv(key, strlen(key), "%d %d ", - used->used_time_sec, used->last_update_time); + update_db(key, used, 0); return RESOURCED_ERROR_NONE; }; @@ -329,14 +559,7 @@ static int heart_battery_save_status(char *key, struct battery_status *status) if (!key || !status) return RESOURCED_ERROR_FAIL; - logging_leveldb_putv(key, strlen(key), "%d %ld %ld %ld %ld %d %d ", - status->curr_capacity, - status->curr_run_time_sec[DISCHARGING], - status->curr_cap_counter[DISCHARGING], - status->curr_run_time_sec[CHARGING], - status->curr_cap_counter[CHARGING], - status->curr_charger_status, - status->reset_mark); + update_db(key, status, 0); return RESOURCED_ERROR_NONE; }; @@ -357,7 +580,7 @@ static int heart_battery_save_usage(char *key, struct battery_usage *usage, int usage[i].sec_per_cap[CHARGING], usage[i].cap_counter[CHARGING]); } - logging_leveldb_put(key, strlen(key), buf, len); + update_db(key, buf, len); return RESOURCED_ERROR_NONE; }; @@ -378,7 +601,7 @@ static int heart_battery_save_prediction(char *key, struct battery_prediction *p prediction[CHARGING].cap_counter[i], prediction[CHARGING].time_pred_min[i]); } - logging_leveldb_put(key, strlen(key), buf, len); + update_db(key, buf, len); return RESOURCED_ERROR_NONE; }; @@ -485,12 +708,17 @@ static int heart_battery_load_usage(char *key, struct battery_usage *usage, int ret = logging_leveldb_read(key, strlen(key), buf, sizeof(buf)); if (ret != RESOURCED_ERROR_NONE) { - _E("Failed to read leveldb key: %s", key); + _E("Failed to read leveldb key"); return RESOURCED_ERROR_FAIL; } + + if (buf[0] == '\0') { + _D("There is no history about %s", key); + return RESOURCED_ERROR_NONE; + } + i = 0; num = total_size/sizeof(struct battery_usage); - token = strtok_r(buf, " ", &saveptr); if (!token) { _E("Failed to token value"); @@ -588,7 +816,7 @@ static int heart_battery_load_prediction(char *key, struct battery_prediction *p return RESOURCED_ERROR_NONE; }; -static void heart_battery_update_used_time(time_t now, int status) +static void heart_battery_update_used_time(time_t now, enum charger_status_type status) { if (batt_used.last_charger_status == DISCHARGING) batt_used.used_time_sec += @@ -627,7 +855,7 @@ static int heart_battery_get_capacity_history_size(void) static void heart_battery_insert_capacity(GSList **history_list, int capacity, int diff_capacity, time_t timestamp, long used_time, long charging_time, - int charger_status, int reset_mark, int clear) + enum charger_status_type charger_status, int reset_mark, int clear) { static int old_reset_mark = 0; GSList *iter, *next; @@ -846,6 +1074,480 @@ static void heart_battery_save_to_file(bool force) heart_battery_set_file_commit_timestamp(now); } +/* + * This function will open the '.battery_data.dat' file and write all battery + * related data into it. + */ +static void heart_battery_write_data_to_file(void) +{ + int i; + int len = 0; + char buff[BATTERY_DATA_MAX] = {0,0}; + _cleanup_fclose_ FILE *fp = NULL; + + fp = fopen(HEART_BATTERY_DATA_FILE, "w"); + if (fp == NULL) { + _E("Could not open file battery_data file\n"); + return; + } + + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + len += snprintf(buff + len, BATTERY_DATA_MAX - len, "%ld %lf\n", \ + batt_stat.last_wall_time[i], batt_stat.last_volt_intg[i]); + + if (BATTERY_DATA_MAX < len + BATTERY_LINE_MAX) { + fputs(buff, fp); + len = 0; + } + } + len += snprintf(buff + len, BATTERY_DATA_MAX - len, "%d %d %d %d %lf %d", \ + batt_stat.curr_index, batt_stat.last_capacity,batt_stat.remaining_time, \ + batt_stat.remaining_time_ups, batt_stat.last_pwr_bchg, batt_stat.data_available); + + fputs(buff, fp); +} + +/* + * This function will open the '.battery_data.dat' file and read all battery + * related data from it and store in variable 'batt_stat' + */ +static void heart_battery_read_data_from_file(void) +{ + int i; + int len = 0; + char buff[BATTERY_DATA_MAX] = {0,0}; + char *ch; + _cleanup_fclose_ FILE *fp = NULL; + + fp = fopen(HEART_BATTERY_DATA_FILE, "r"); + if (fp == NULL) { + _E("Could not open battery_data file\n"); + return; + } + + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + ch = fgets(buff, BATTERY_DATA_MAX, fp); + if (ch == NULL) + /*if file is empty then return from here */ + return; + + len += sscanf(buff, "%ld %lf", \ + &batt_stat.last_wall_time[i], &batt_stat.last_volt_intg[i]); + } + + ch = fgets(buff, BATTERY_DATA_MAX, fp); + if (ch != NULL) + len += sscanf(buff, "%d %d %d %d %lf %d", \ + &batt_stat.curr_index, &batt_stat.last_capacity, &batt_stat.remaining_time, \ + &batt_stat.remaining_time_ups, &batt_stat.last_pwr_bchg, &batt_stat.data_available); +} + +/* + * This function will reject all the battery related data which were stored earlier + */ +static void heart_battery_reject_data(void) +{ + int i; + + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + batt_stat.last_wall_time[i] = 0; + batt_stat.last_volt_intg[i] = 0.0; + } + + /* Do not reject this, once data available, do not change untill next binary flash */ + /*batt_stat.data_available = 0; */ + batt_stat.last_capacity = 0; + batt_stat.curr_index = 0; + batt_stat.remaining_time = 0; + batt_stat.remaining_time_ups = 0; + /* do not flush this value. When proper power value is not present, previously + calculated power only will be used. */ + /*batt_stat.last_pwr_bchg = 0.0; */ + + heart_battery_write_data_to_file(); +} + +/* + * This function will calculate the voltage integral(energy) at battery level 100. + * We will calculate Pivot using fast discharge and slow discharge rate for + * estimating the battery time in normal mode + */ +static void heart_battery_calculate_pivot(void) +{ + int i; + + for (i = 0; i < ocv_degree; i++) { + volt_intg_full += (intg[i] /(i+1)) * pow(100 , i+1); + } + volt_intg_full = volt_intg_full/100000.0; + + pivot_nor = ((sqrt(discharge_fast) + sqrt(discharge_slow))/2); + pivot_nor = pivot_nor * pivot_nor; // taking square of Tpivot + + pivot_ups = ((sqrt(discharge_fast*UPS_FACTOR) + sqrt(discharge_slow*UPS_FACTOR))/2); + pivot_ups = pivot_ups * pivot_ups; // taking square of Tpivot +} + +/* function to return the time(in seconds) since the Epoch*/ +static long heart_battery_logging_get_time_sec_new(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return (tv.tv_sec + tv.tv_usec / 1000000); +} + +/* + * This function will read the current time and will compare with previously stored time. + * If time difference is more then it is assumed that device was powered off and it will + * adjust all stored time values + */ +static void heart_battery_power_off_time_adjustment(void) +{ + int i; + int index; + int clock_tick_diff = 0; + long int curr_time; + long int time_diff = 0; + + curr_time = heart_battery_logging_get_time_sec_new(); + + index = ((batt_stat.curr_index - 1) + INDEX_WINDOW_SIZE) % INDEX_WINDOW_SIZE; + if (batt_stat.last_wall_time[index] == 0) + index = 0; + + clock_tick_diff = logging_get_time(CLOCK_BOOTTIME) - batt_stat.last_clock_tick; + + /* check if we have store last time value or not */ + if (batt_stat.last_wall_time[index] != 0) { + time_diff = curr_time - batt_stat.last_wall_time[index]; + time_diff -= clock_tick_diff; + } + + _I("All store time value will be scaled up by %ld secs\n", time_diff); + + /*scale up all stored time value */ + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + if (batt_stat.last_wall_time[i] == 0) { + /* we do not have stored time value from this point onwards, stop updating*/ + break; + } + else + batt_stat.last_wall_time[i] += time_diff; + } + + /* update same value in battery data file*/ + heart_battery_write_data_to_file(); + + /* + * during charge time + */ + time_diff = curr_time - batt_stat.last_wall_time_chg; + time_diff -= clock_tick_diff; + /* + * update last_wall_time_chg as per previous and new time diff + * it will take care of positive of negative difference + */ + batt_stat.last_wall_time_chg += time_diff; + _I("last_wall_time_chg got updated by %d\n", time_diff); + /* + * make first_level_change as TRUE so that charge remaining time + * should not vary much + */ + first_level_change = TRUE; +} + +/* + * In case of any time change in the device, this callback get called and it + * will do the time adjustment + */ +static void time_change_notify_cb(keynode_t *key, void *data) +{ + heart_battery_power_off_time_adjustment(); +} + +static int get_battery_remaining_time_new(void) +{ + return batt_stat.remaining_time; +} + +static int get_battery_remaining_time_ups() +{ + return batt_stat.remaining_time_ups; +} + +/* + * This function will return the average of stored time diff values + */ +static int heart_battery_get_time_diff_avg(int time_diff_avg) +{ + int temp_index; + int i; + + if (data_avail_chg == true) { + /* + * calculate the average of last 5(including current) time diff values + */ + temp_index = ((batt_stat.index_chg-1) + BATTERY_LEVEL_GAP) % BATTERY_LEVEL_GAP; + for (i = 1; i < BATTERY_LEVEL_GAP; i++) { + time_diff_avg += batt_stat.last_wall_time_chg_diff[temp_index]; + temp_index = ((temp_index-1) + BATTERY_LEVEL_GAP) % BATTERY_LEVEL_GAP; + } + time_diff_avg = time_diff_avg/BATTERY_LEVEL_GAP; + } else { + /* + * take average of only available data. Do not include 1st value so + * i stared from 1. + */ + for (i = 1; i < batt_stat.index_chg; i++) + time_diff_avg += batt_stat.last_wall_time_chg_diff[i]; + + time_diff_avg = time_diff_avg/i; + } + /* + * to minimize the time diff fluctuation + */ + time_diff_avg = (time_diff_avg+BATTERY_C_RATE)/2; + _I("time_diff after averaging= %d\n", time_diff_avg); + + return time_diff_avg; +} + +/* + * This function will return the time diff between last and current battery + * level change + */ +int heart_battery_get_time_diff(long curr_wall_time) +{ + if (batt_stat.last_wall_time_chg == 0) + /* + * First time estimation + * In this case charge rate will be considered as time diff + */ + return BATTERY_C_RATE; + + return (curr_wall_time - batt_stat.last_wall_time_chg); +} + +/* + * This function will calculate the battery charging remaining time + */ +static void heart_battery_cal_charging_rem_time(void) +{ + long curr_wall_time = 0; + int rem_time; + int time_diff; + int time_diff_avg; + + if (batt_stat.last_capacity_chg == batt_stat.curr_capacity) { + _I("Last capacity %d is same as current capacity %d\n", + batt_stat.last_capacity_chg, batt_stat.curr_capacity); + return; + } + + curr_wall_time = heart_battery_logging_get_time_sec_new(); + + time_diff = heart_battery_get_time_diff(curr_wall_time); + + /* + * just after inserting the charger, if time diff between two level + * is less than MIN_TIME_FOR_LVL_CHANGE value then consider time diff + * as BATTERY_C_RATE so that there will not be sudden fall in charge + * remaining time + */ + if (time_diff < MIN_TIME_FOR_LVL_CHANGE && first_level_change == TRUE) { + /* + * first_level_change will be set to TRUE in function + * heart_battery_charger_status() and set to FALSE in function + * heart_battery_capacity_status() + */ + _I("time diff %d is less than min value of lvl change %d\n",time_diff, MIN_TIME_FOR_LVL_CHANGE); + time_diff = BATTERY_C_RATE; + } + + _I("data_avail_chg = %d, index_chg = %d\n",data_avail_chg, batt_stat.index_chg); + _I("last_wall_time_chg = %ld, curr_wall_time = %ld\n", batt_stat.last_wall_time_chg, curr_wall_time); + _I("time diff before averaging = %d\n",time_diff); + + /* + * get the average of all time diff values stored including current + */ + time_diff_avg = heart_battery_get_time_diff_avg(time_diff); + + if (batt_stat.curr_capacity == FULL_CAPACITY) { + batt_stat.remaining_time_chg = 0; + } else { + /* + * calculate the remaining charge time based on constant current + * and constant votlage(CCCV) logic. 'time_diff_avg' is for CC and + * square(time_diff_avg/BATTERY_C_RATE) is for CV part. + */ + rem_time = (time_diff_avg + (time_diff_avg/BATTERY_C_RATE)*(time_diff_avg/BATTERY_C_RATE))*(FULL_CAPACITY- batt_stat.curr_capacity); + batt_stat.remaining_time_chg = SEC_TO_MIN(rem_time); + } + + batt_stat.last_wall_time_chg = curr_wall_time; + batt_stat.last_wall_time_chg_diff[batt_stat.index_chg] = time_diff; + batt_stat.index_chg = (batt_stat.index_chg+1)%BATTERY_LEVEL_GAP; + batt_stat.last_capacity_chg = batt_stat.curr_capacity; + batt_stat.last_clock_tick = logging_get_time(CLOCK_BOOTTIME); + + _I("Capacity = %d, charging remaining time = %d\n", batt_stat.curr_capacity, batt_stat.remaining_time_chg); + + if (batt_stat.index_chg+1 == BATTERY_LEVEL_GAP) + data_avail_chg = true; +} + + +/* + * This function will calculate the battery estimation time for every battery + * level change + */ +static void heart_battery_cal_discharge_rem_time(void) +{ + double curr_volt_intg = 0; + double volt_intg_diff = 0; + double ps, pt; + double batt_pwr; + int rem_time = 0; + int index = 0; + int temp_index = 0; + int index_count; + int i; + int update_time; + long curr_wall_time = 0; + long time_diff1, time_diff2; + + _I("last capacity = %d, current capacity = %d\n",batt_stat.last_capacity, batt_stat.curr_capacity); + + /* if battery level change happen then only calculate the new time*/ + if (batt_stat.last_capacity != batt_stat.curr_capacity) { + /* calculate the voltage integral(energy) for current capacity */ + for (i = 0; i < ocv_degree; i++) { + curr_volt_intg += (intg[i] /(i+1)) * pow(batt_stat.curr_capacity , i+1); + } + curr_volt_intg = curr_volt_intg /100000.0; + + /*current time*/ + curr_wall_time = heart_battery_logging_get_time_sec_new(); + + _I("curr_volt_intg = %lf, curr_wall_time = %ld\n", curr_volt_intg, curr_wall_time); + + if (batt_stat.last_capacity < batt_stat.curr_capacity) { + /* + *this indicates that device was put for charging so need to reject all the previous data except + *'batt_stat.data_available' and 'batt_stat.last_pwr_bchg'. + *Use last available battery power before connecting the charger as current average power. + */ + batt_pwr = batt_stat.last_pwr_bchg; + heart_battery_reject_data(); + } + else { + index = ((batt_stat.curr_index - BATTERY_LEVEL_GAP) + INDEX_WINDOW_SIZE)%INDEX_WINDOW_SIZE; + if (batt_stat.last_wall_time[index] == 0 && batt_stat.last_volt_intg[index] == 0) { + /* do not have enough previous data so take 1st stored data as reference */ + time_diff1 = curr_wall_time - batt_stat.last_wall_time[0]; + volt_intg_diff = batt_stat.last_volt_intg[0] - curr_volt_intg; + if (volt_intg_diff < DOUBLE_ZERO) + volt_intg_diff = volt_intg_full - curr_volt_intg; + } + else { + /* time differenc since last battery level change*/ + time_diff1 = curr_wall_time - batt_stat.last_wall_time[index]; + /* voltage integral(energy) change since last battery level change */ + volt_intg_diff = batt_stat.last_volt_intg[index] - curr_volt_intg; + } + _I("time_diff1 = %d, volt_intg_diff = %lf\n", time_diff1, volt_intg_diff); + + time_diff1 = SEC_TO_MIN(time_diff1); /*convert second to minute */ + + /* calculate short term average power component*/ + if (time_diff1 != 0) + ps = (total_battery_capacity *volt_intg_diff) / time_diff1; + else + ps = 0; + + _I("ps = %lf\n", ps); + + /* find the voltage integral(energy) of 1 hr back battery level. + 1. Place the index at proper location + 2. get the time stored at that index + 3. if difference between last time and current time is >= 60 then take that voltage integral + */ + index_count = INDEX_WINDOW_SIZE; + time_diff2 = 0; + temp_index = BATTERY_WINDOW_INDEX(batt_stat.curr_index - 1); + while ((index_count > 0) && (time_diff2 < 60)) { + if (batt_stat.last_wall_time[temp_index] == 0) + /* data not available so break the loop */ + break; + + time_diff2 = SEC_TO_MIN(curr_wall_time - batt_stat.last_wall_time[temp_index]); + temp_index = BATTERY_WINDOW_INDEX(temp_index - 1); + index_count--; + } + + /* calculate long term average power component*/ + if (time_diff2 < 60) { + pt = 0; + } else { + temp_index = BATTERY_WINDOW_INDEX(temp_index + 1); + volt_intg_diff = batt_stat.last_volt_intg[temp_index] - curr_volt_intg ; + if (volt_intg_diff < DOUBLE_ZERO) + pt = 0; + else + pt = (total_battery_capacity * volt_intg_diff) / time_diff2; + } + + _I("time_diff2 = %d, volt_intg_diff = %lf, pt = %lf\n", time_diff2, volt_intg_diff, pt); + + /* calculate the average power from the short component and long component*/ + if ((pt + ps) != 0) + batt_pwr = (((pt * pt) + (ps * ps)) / (pt + ps)); + else + batt_pwr = batt_stat.last_pwr_bchg; + } + + /* total remaining time based on current battery level*/ + rem_time = (total_battery_capacity * curr_volt_intg) / batt_pwr; + + _I("pivot_nor = %lf, curr_volt_intg = %lf , volt_intg_full %lf, batt_pwr = %lf", pivot_nor, curr_volt_intg,volt_intg_full, batt_pwr); + + /* calculate remaining time for normal mode */ + update_time = (pivot_nor * curr_volt_intg)/volt_intg_full; + batt_stat.remaining_time = (LONG_TIME_WEIGHT*update_time + SHORT_TIME_WEIGHT*CAL_MIN(update_time, rem_time)); + + /* calculate remaining time for UPS mode */ + update_time = (pivot_ups * curr_volt_intg)/volt_intg_full; + batt_stat.remaining_time_ups = (LONG_TIME_WEIGHT*update_time + SHORT_TIME_WEIGHT*CAL_MIN(update_time, rem_time)); + + _I("normal mode remaining time = %d, ups remaining time = %d\n", + batt_stat.remaining_time, batt_stat.remaining_time_ups); + + batt_stat.last_volt_intg[batt_stat.curr_index] = curr_volt_intg; + batt_stat.last_wall_time[batt_stat.curr_index] = curr_wall_time; + batt_stat.last_capacity = batt_stat.curr_capacity; + batt_stat.curr_index = (batt_stat.curr_index + 1) % INDEX_WINDOW_SIZE; + batt_stat.last_clock_tick = logging_get_time(CLOCK_BOOTTIME); + + if (batt_pwr > DOUBLE_ZERO) { + /*when battery percentage is 0 then available power will be 0 so do not store this value. + keep previous value only. */ + batt_stat.last_pwr_bchg = batt_pwr; + } + + if (batt_stat.curr_index > BATTERY_LEVEL_GAP) { + /* When enough data to estimate the available battery time are stored, no need to update the status + of batt_stat.data_available untill we are flashing new binary. */ + batt_stat.data_available = TRUE; + } + + /* save all calculated battery data to file*/ + heart_battery_write_data_to_file(); + } +} + void heart_battery_update(struct logging_table_form *data, void *user_data) { heart_battery_save_to_file(false); @@ -967,7 +1669,7 @@ static long heart_battery_compute_remaining_time_in_min(int capacity_count, long return time; } -static void heart_battery_calculate_prediction(enum charging_goal goal) +static void heart_battery_calculate_prediction(enum charger_status_type goal) { int i, capacity, level; long total_time, total_count, sec_per_cap, pred_min; @@ -1071,12 +1773,13 @@ static void heart_battery_calculate_prediction(enum charging_goal goal) _E("Failed to alloc array"); return; } - if (heart_battery_get_capacity_history_latest(arrays, goal, BATTERY_PREDICTION_LATEST_COUNT) != RESOURCED_ERROR_NONE) { + if (heart_battery_get_capacity_history_latest(arrays, goal, BATTERY_PREDICTION_LATEST_COUNT) + != RESOURCED_ERROR_NONE) { _E("Failed to get battery capacity history"); + g_array_free(arrays, TRUE); + arrays = NULL; return; } - if (!arrays->len) - _E("No battery capacity history data"); total_time = 0; total_count = 0; for (i = 0; i < arrays->len; i++) { @@ -1088,6 +1791,7 @@ static void heart_battery_calculate_prediction(enum charging_goal goal) total_time += lbc->charging_time; else total_time += lbc->used_time; + free(lbc); } if (total_time && total_count >= BATTERY_PREDICTION_DATA_MIN) { sec_per_cap = total_time / total_count; @@ -1099,6 +1803,12 @@ static void heart_battery_calculate_prediction(enum charging_goal goal) pred_min = heart_battery_compute_remaining_time_in_min(capacity, sec_per_cap); heart_battery_set_prediction(COUNT, goal, sec_per_cap, total_count, pred_min); + /* check current discharge rate */ + if (sec_per_cap && goal == DISCHARGING) { + batt_stat.discharge_rate_level = + heart_battery_calculate_discharge_rate_level(sec_per_cap); + _I("discharge rate level : %ld", batt_stat.discharge_rate_level); + } } else heart_battery_set_prediction(COUNT, goal, 0, 0, 0); g_array_free(arrays, TRUE); @@ -1113,10 +1823,10 @@ static void heart_battery_calculate_prediction(enum charging_goal goal) } if (heart_battery_get_capacity_history(arrays, BATTERY_PREDICTION_PERIOD) != RESOURCED_ERROR_NONE) { _E("Failed to get battery capacity history"); + g_array_free(arrays, TRUE); + arrays = NULL; return; } - if (!arrays->len) - _E("No battery capacity history data"); total_time = 0; total_count = 0; for (i = 0; i < arrays->len; i++) { @@ -1124,16 +1834,21 @@ static void heart_battery_calculate_prediction(enum charging_goal goal) if (!lbc) break; if (goal == CHARGING) { - if (lbc->charger_status != CHARGING) + if (lbc->charger_status != CHARGING) { + free(lbc); continue; + } total_time += lbc->charging_time; total_count += lbc->diff_capacity; } else { - if (lbc->charger_status != DISCHARGING) + if (lbc->charger_status != DISCHARGING) { + free(lbc); continue; + } total_time += lbc->used_time; total_count += lbc->diff_capacity; } + free(lbc); } g_array_free(arrays, TRUE); arrays = NULL; @@ -1284,7 +1999,7 @@ static int heart_battery_add_capacity(int capacity) /* ============================ DBUS -> DEVICED on demand ==================== */ -static int heart_battery_get_capacity(void) +static int heart_battery_direct_get_capacity(void) { int capacity, ret; DBusMessage *msg; @@ -1306,7 +2021,7 @@ static int heart_battery_get_capacity(void) return capacity; } -static int heart_battery_get_charger_status(void) +static enum charger_status_type heart_battery_direct_get_charger_status(void) { int status, ret; DBusMessage *msg; @@ -1360,6 +2075,20 @@ static void heart_battery_capacity_status(void *data, DBusMessage *msg) heart_battery_add_capacity(capacity); heart_battery_update_used_time(logging_get_time(CLOCK_BOOTTIME), batt_stat.curr_charger_status); + + if (logic_v2) { + /* get the device mode */ + if (vconf_get_int( VCONFKEY_HEART_BATTERY_DEVICE_MODE, &device_mode)) + _E("failed to get VCONFKEY_HEART_BATTERY_DEVICE_MODE\n"); + + /* for every battery level change, calculate the battery estimation time using new logic*/ + heart_battery_cal_discharge_rem_time(); + + if (batt_stat.curr_charger_status == CHARGING) { + heart_battery_cal_charging_rem_time(); + first_level_change = FALSE; + } + } } static void heart_battery_charger_status(void *data, DBusMessage *msg) @@ -1410,6 +2139,17 @@ static void heart_battery_charger_status(void *data, DBusMessage *msg) heart_battery_update_used_time(logging_get_time(CLOCK_BOOTTIME), batt_stat.curr_charger_status); heart_battery_calculate_prediction(batt_stat.curr_charger_status); + + if (logic_v2) { + /* If charger has been removed then update the battery estimation time */ + heart_battery_cal_discharge_rem_time(); + + if (charger_status == CHARGING) { + batt_stat.last_wall_time_chg = 0; + first_level_change = TRUE; + heart_battery_cal_charging_rem_time(); + } + } } /* ========================= DBUS -> DEVICED handler END ==================== */ @@ -1452,12 +2192,7 @@ int heart_battery_get_capacity_history_latest(GArray *arrays, int charge, int ma lbci = malloc(sizeof(struct heart_battery_capacity)); if (!lbci) { _E("malloc failed"); - ret = pthread_mutex_unlock(&heart_battery_mutex); - if (ret) { - _E("pthread_mutex_unlock() failed, %d", ret); - return RESOURCED_ERROR_FAIL; - } - return RESOURCED_ERROR_OUT_OF_MEMORY; + goto unlock_exit; } lbci->capacity = lbc->capacity; lbci->diff_capacity = lbc->diff_capacity; @@ -1468,6 +2203,8 @@ int heart_battery_get_capacity_history_latest(GArray *arrays, int charge, int ma lbci->charger_status = lbc->charger_status; g_array_prepend_val(arrays, lbci); } +unlock_exit: + g_slist_free(rlist); ret = pthread_mutex_unlock(&heart_battery_mutex); if (ret) { _E("pthread_mutex_unlock() failed, %d", ret); @@ -1597,6 +2334,7 @@ static DBusMessage *edbus_get_battery_capacity_history_latest(E_DBus_Object *obj dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->used_time); dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->charging_time); dbus_message_iter_close_container(&arr, &sub); + free(lbc); } dbus_message_iter_close_container(&d_iter, &arr); exit: @@ -1696,7 +2434,7 @@ static DBusMessage *edbus_get_battery_used_time(E_DBus_Object *obj, DBusMessage return reply; } -static int get_battery_remaining_time(int mode, int status) +static int get_battery_remaining_time(int mode, enum charger_status_type status) { int i, ret, count; long sum, time, cumul_average, trend_average; @@ -1742,10 +2480,33 @@ static int get_battery_remaining_time(int mode, int status) } else ret = ((cumul_average * CUMUL_WEIGHT) + (trend_average * TREND_WEIGHT)); - if (status == CHARGING) + if (status == CHARGING) { + if (logic_v2) /* new logic */ + return batt_stat.remaining_time_chg; + return ret; /* old logic */ + } + + if (logic_v2) { /* new logic */ + switch (mode) { + case POWER_SAVING_MODE: + /* Fall through */ + case ULTRA_SAVING_MODE: + ret = get_battery_remaining_time_ups(); + break; + case POWER_NORMAL_MODE: + ret = get_battery_remaining_time_new(); + break; + default: + break; + } return ret; + } - switch (mode) { + if (ret <= 0) + return BATTERY_USAGE_LEARNING; + + /* old logic */ + switch(mode){ case ULTRA_SAVING_MODE: /* Fall through */ case POWER_SAVING_MODE: @@ -1804,6 +2565,22 @@ static DBusMessage *edbus_get_battery_charging_time(E_DBus_Object *obj, DBusMess return reply; } +static DBusMessage *edbus_get_battery_discharge_rate_level(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessage *reply; + DBusMessageIter iter; + int ret; + + ret = batt_stat.discharge_rate_level; + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + _I("discharge_rate_level %d", ret); + + return reply; +} + static DBusMessage *edbus_battery_save_to_file(E_DBus_Object *obj, DBusMessage *msg) { int ret; @@ -1835,11 +2612,12 @@ static struct edbus_method edbus_methods[] = { { "GetBatteryUsedTime", NULL, "i", edbus_get_battery_used_time }, { "GetBatteryRemainingTime", "i", "i", edbus_get_battery_remaining_time }, { "GetBatteryChargingTime", NULL, "i", edbus_get_battery_charging_time }, + { "GetBatteryDischargeRateLevel", NULL, "i", edbus_get_battery_discharge_rate_level }, { "SaveBatteryData", NULL, "i", edbus_battery_save_to_file }, }; /* ========================= DBUS interface END ==================== */ -static void heart_battery_used_time_init(int status) +static void heart_battery_used_time_init(enum charger_status_type status) { batt_used.last_charger_status = status; batt_used.last_update_time = logging_get_time(CLOCK_BOOTTIME); @@ -1885,21 +2663,70 @@ static void heart_battery_status_init(void) if (ret < 0) _E("Failed to read battery status data"); + if (logic_v2) { + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + batt_stat.last_wall_time[i] = 0; + batt_stat.last_volt_intg[i] = 0.0; + } + for (i = 0; i < BATTERY_LEVEL_GAP; i++) + batt_stat.last_wall_time_chg_diff[i] = 0; + + batt_stat.data_available = 0; + batt_stat.last_capacity = 0; + first_level_change = FALSE; + batt_stat.curr_index = 0; + batt_stat.remaining_time = 0; + batt_stat.remaining_time_ups = 0; + batt_stat.last_pwr_bchg = average_pwr; + batt_stat.last_wall_time_chg = 0; + batt_stat.index_chg = 0; + batt_stat.remaining_time_chg = 0; + batt_stat.last_clock_tick = logging_get_time(CLOCK_BOOTTIME); + + /* During the device boot-up, calculate energy for 100% battery and calcuate + pivot(norma and UPS mode) for time remaining time estimation*/ + heart_battery_calculate_pivot(); + /*get all the previousely stored data from file to program variable*/ + heart_battery_read_data_from_file(); + /* + * check whether was device power off for long time. If yes then adjust + * our stored time values + */ + heart_battery_power_off_time_adjustment(); + /* + * register callback for any time change from user side. + * If any time change happen then adjust whole data as per new time + */ + vconf_notify_key_changed(VCONFKEY_SETAPPL_TIMEZONE_INT, time_change_notify_cb, NULL); + vconf_notify_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED, time_change_notify_cb, NULL); + vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, time_change_notify_cb, NULL); + } + battery_learning_mode = heart_battery_get_learning_mode(); ret = heart_battery_capacity_read_from_file(HEART_BATTERY_CAPACITY_DATA_FILE); if (ret < 0) _E("Failed to read battery capacity data"); - capacity = heart_battery_get_capacity(); + capacity = heart_battery_direct_get_capacity(); if (capacity > 0) batt_stat.curr_capacity = capacity; - status = heart_battery_get_charger_status(); + status = heart_battery_direct_get_charger_status(); if (status >= 0) batt_stat.curr_charger_status = status; heart_battery_used_time_init(batt_stat.curr_charger_status); heart_battery_calculate_prediction(batt_stat.curr_charger_status); batt_stat.last_event_wall_time = logging_get_time(CLOCK_BOOTTIME); + batt_stat.discharge_rate_level = BATTERY_DISCHARGE_NONE; + + if (logic_v2) { + heart_battery_cal_discharge_rem_time(); + + if (batt_stat.curr_charger_status == CHARGING) { + heart_battery_cal_charging_rem_time(); + first_level_change = TRUE; + } + } } static int low_battery_handler(void *data) @@ -1915,24 +2742,56 @@ static int heart_battery_config(struct parse_result *result, void *user_data) if (!result) return -EINVAL; - if (strncmp(result->section, HEART_BATTERY_CONF_SECTION, strlen(HEART_BATTERY_CONF_SECTION)+1)) - return RESOURCED_ERROR_NONE; - - if (!strncmp(result->name, "POWER_NORMAL_MODE", strlen("POWER_NORMAL_MODE")+1)) { - val = atoi(result->value); - if (val > 0) - default_mode_spc[POWER_NORMAL_MODE] = val; - _D("POWER_NORMAL_MODE SPC: %d", val); - } else if (!strncmp(result->name, "POWER_SAVING_MODE", strlen("POWER_SAVING_MODE")+1)) { - val = atoi(result->value); - if (val > 0) - default_mode_spc[POWER_SAVING_MODE] = val; - _D("POWER_SAVING_MODE SPC: %d", val); - } else if (!strncmp(result->name, "ULTRA_SAVING_MODE", strlen("ULTRA_SAVING_MODE")+1)) { - val = atoi(result->value); - if (val > 0) - default_mode_spc[ULTRA_SAVING_MODE] = val; - _D("ULTRA_POWER_SAVING_MODE SPC: %d", val); + if (!strncmp(result->section, HEART_BATTERY_CONF_SECTION, sizeof(HEART_BATTERY_CONF_SECTION) + 1)) { + if (!strncmp(result->name, "POWER_NORMAL_MODE", sizeof("POWER_NORMAL_MODE") + 1)) { + val = atoi(result->value); + if (val > 0) + default_mode_spc[POWER_NORMAL_MODE] = val; + _D("POWER_NORMAL_MODE SPC: %d", val); + } else if (!strncmp(result->name, "POWER_SAVING_MODE", sizeof("POWER_SAVING_MODE") + 1)) { + val = atoi(result->value); + if (val > 0) + default_mode_spc[POWER_SAVING_MODE] = val; + _D("POWER_SAVING_MODE SPC: %d", val); + } else if (!strncmp(result->name, "ULTRA_SAVING_MODE", sizeof("ULTRA_SAVING_MODE") + 1)) { + val = atoi(result->value); + if (val > 0) + default_mode_spc[ULTRA_SAVING_MODE] = val; + _D("ULTRA_POWER_SAVING_MODE SPC: %d", val); + } + } else if (!strncmp(result->section, battery_header, sizeof(battery_header) + 1)) { + if (!strncmp(result->name, "LOGIC_V2", sizeof("LOGIC_V2") + 1)) { + logic_v2 = atoi(result->value); + _I("logic_v2 : %d", logic_v2); + } else if (!strncmp(result->name, "DISCHARGE_FAST", sizeof("DISCHARGE_FAST") + 1)) { + discharge_fast = atoi(result->value); + _I("discharge_fast: %d", discharge_fast); + } else if (!strncmp(result->name, "DISCHARGE_SLOW", sizeof("DISCHARGE_SLOW") + 1)) { + discharge_slow = atoi(result->value); + _I("discharge_slow: %d", discharge_slow); + } else if (!strncmp(result->name, "AVERAGE_PWR", sizeof("AVERAGE_PWR") + 1)) { + average_pwr = atof(result->value); + _I("average_power: %lf", average_pwr); + } else if (!strncmp(result->name, "OCV_SOC_POLY_COEF", sizeof("OCV_SOC_POLY_COEF") + 1)) { + char *token, *saveptr; + int i; + token = strtok_r(result->value, " ", &saveptr); + if (token) { + ocv_degree = atoi(token); + intg = calloc(ocv_degree, sizeof(double)); + if (!intg) { + _E("Out of memory"); + return -ENOMEM; + } + _I("Degree of OCV_SOC_POLY = %d", ocv_degree); + + for (i = 1; i <= ocv_degree ; i++) { + token = strtok_r(NULL, " ", &saveptr); + intg[i] = atof(token); + _I("OCV_SOC_POLY_COEF_%d = %.9g", i, intg[i]); + } + } + } } return RESOURCED_ERROR_NONE; } @@ -1954,11 +2813,42 @@ static void heart_battery_mode_factor_init(void) _I("ULTRA_POWER_SAVING_MODE factor: %f", val); } +/* + * This function will read total battery capacity from sys interface + * example - 1500mAh or 2600mAh + */ +static void heart_read_battery_total_capacity(void) +{ + u_int32_t batt_capacity; + + /* + * read the battery total capacity from sys interface path + * "/sys/class/power_supply/battery/batt_capacity" + * which is provided by kernel + */ + if (fread_uint("/sys/class/power_supply/battery/batt_capacity", + &batt_capacity) != RESOURCED_ERROR_NONE) { + _D("This device doesn't support battery capacity information. Disable LOGIC_V2"); + return; + } + + snprintf(battery_header, sizeof(battery_header), "BATTERY_%d", + batt_capacity); + + /* + * convert battery capacity from mAh to mAm + */ + total_battery_capacity = batt_capacity * 60; + _I("battery capacity = %d, battery header = %s", + batt_capacity, battery_header); +} + static int heart_battery_init(void *data) { int ret; - ret = logging_module_init(BATTERY_NAME, ONE_DAY, TEN_MINUTE, heart_battery_update, HEART_BATTERY_UPDATE_INTERVAL, SYSTEM_DEFAULT); + ret = logging_module_init(BATTERY_NAME, ONE_DAY, TEN_MINUTE, heart_battery_update, + HEART_BATTERY_UPDATE_INTERVAL, SYSTEM_DEFAULT); if (ret != RESOURCED_ERROR_NONE) { _E("logging module init failed"); return RESOURCED_ERROR_FAIL; @@ -1982,6 +2872,8 @@ static int heart_battery_init(void *data) if (ret < 0) _E("Failed to add a charger status signal handler"); + heart_read_battery_total_capacity(); + config_parse(HEART_CONF_FILE_PATH, heart_battery_config, NULL); heart_battery_mode_factor_init(); diff --git a/src/heart/heart.conf b/src/heart/heart.conf index f1468fe..93a78e0 100644 --- a/src/heart/heart.conf +++ b/src/heart/heart.conf @@ -8,3 +8,25 @@ APPOPT=OFF POWER_NORMAL_MODE=676 POWER_SAVING_MODE=750 ULTRA_SAVING_MODE=1947 + +# OCV_SOC_POLY_COEF=(DEGREE) (COEF_1) ... (COEF_N) + +[BATTERY_2600] +LOGIC_V2=1 +DISCHARGE_FAST=700 +DISCHARGE_SLOW=12000 +AVERAGE_PWR=35.0 +OCV_SOC_POLY_COEF=6 3402.664 42.031 -1.76745 0.034798 -0.00030516 0.0000010116 + +[BATTERY_1500] +LOGIC_V2=1 +DISCHARGE_FAST=200 +DISCHARGE_SLOW=7000 +AVERAGE_PWR=35.0 +OCV_SOC_POLY_COEF=6 3402.664 42.031 -1.76745 0.034798 -0.00030516 0.0000010116 + +[BATTERY] +LOGIC_V2=0 +DISCHARGE_FAST=0 +DISCHARGE_SLOW=0 +AVERAGE_PWR=0.0