#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"
#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
*/
#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 */
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 */
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
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 */
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;
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];
}
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];
}
{
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++;
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];
}
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;
};
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;
};
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;
};
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;
};
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");
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 +=
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;
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);
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;
_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++) {
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;
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);
}
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++) {
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;
/* ============================ DBUS -> DEVICED on demand ==================== */
-static int heart_battery_get_capacity(void)
+static int heart_battery_direct_get_capacity(void)
{
int capacity, ret;
DBusMessage *msg;
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;
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)
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 ==================== */
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;
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);
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:
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;
} 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:
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;
{ "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);
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)
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;
}
_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;
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();