/* Copyright (c) 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include "qg-profile-lib.h" #include "qg-defs.h" static int linear_interpolate(int y0, int x0, int y1, int x1, int x) { if (y0 == y1 || x == x0) return y0; if (x1 == x0 || x == x1) return y1; return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); } int interpolate_single_row_lut(struct profile_table_data *lut, int x, int scale) { int i, result; int cols = lut->cols; if (x < lut->col_entries[0] * scale) { pr_debug("x %d less than known range return y = %d lut = %s\n", x, lut->data[0][0], lut->name); return lut->data[0][0]; } if (x > lut->col_entries[cols-1] * scale) { pr_debug("x %d more than known range return y = %d lut = %s\n", x, lut->data[0][cols-1], lut->name); return lut->data[0][cols-1]; } for (i = 0; i < cols; i++) { if (x <= lut->col_entries[i] * scale) break; } if (x == lut->col_entries[i] * scale) { result = lut->data[0][i]; } else { result = linear_interpolate( lut->data[0][i-1], lut->col_entries[i-1] * scale, lut->data[0][i], lut->col_entries[i] * scale, x); } return result; } int interpolate_soc(struct profile_table_data *lut, int batt_temp, int ocv) { int i, j, soc_high, soc_low, soc; int rows = lut->rows; int cols = lut->cols; if (batt_temp < lut->col_entries[0] * DEGC_SCALE) { pr_debug("batt_temp %d < known temp range\n", batt_temp); batt_temp = lut->col_entries[0] * DEGC_SCALE; } if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE) { pr_debug("batt_temp %d > known temp range\n", batt_temp); batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE; } for (j = 0; j < cols; j++) if (batt_temp <= lut->col_entries[j] * DEGC_SCALE) break; if (batt_temp == lut->col_entries[j] * DEGC_SCALE) { /* found an exact match for temp in the table */ if (ocv >= lut->data[0][j]) return lut->row_entries[0]; if (ocv <= lut->data[rows - 1][j]) return lut->row_entries[rows - 1]; for (i = 0; i < rows; i++) { if (ocv >= lut->data[i][j]) { if (ocv == lut->data[i][j]) return lut->row_entries[i]; soc = linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i - 1], lut->data[i - 1][j], ocv); return soc; } } } /* batt_temp is within temperature for column j-1 and j */ if (ocv >= lut->data[0][j]) return lut->row_entries[0]; if (ocv <= lut->data[rows - 1][j - 1]) return lut->row_entries[rows - 1]; soc_low = soc_high = 0; for (i = 0; i < rows-1; i++) { if (soc_high == 0 && is_between(lut->data[i][j], lut->data[i+1][j], ocv)) { soc_high = linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i + 1], lut->data[i+1][j], ocv); } if (soc_low == 0 && is_between(lut->data[i][j-1], lut->data[i+1][j-1], ocv)) { soc_low = linear_interpolate( lut->row_entries[i], lut->data[i][j-1], lut->row_entries[i + 1], lut->data[i+1][j-1], ocv); } if (soc_high && soc_low) { soc = linear_interpolate( soc_low, lut->col_entries[j-1] * DEGC_SCALE, soc_high, lut->col_entries[j] * DEGC_SCALE, batt_temp); return soc; } } if (soc_high) return soc_high; if (soc_low) return soc_low; pr_debug("%d ocv wasn't found for temp %d in the LUT %s returning 100%%\n", ocv, batt_temp, lut->name); return 10000; } int interpolate_var(struct profile_table_data *lut, int batt_temp, int soc) { int i, var1, var2, var, rows, cols; int row1 = 0; int row2 = 0; rows = lut->rows; cols = lut->cols; if (soc > lut->row_entries[0]) { pr_debug("soc %d greater than known soc ranges for %s lut\n", soc, lut->name); row1 = 0; row2 = 0; } else if (soc < lut->row_entries[rows - 1]) { pr_debug("soc %d less than known soc ranges for %s lut\n", soc, lut->name); row1 = rows - 1; row2 = rows - 1; } else { for (i = 0; i < rows; i++) { if (soc == lut->row_entries[i]) { row1 = i; row2 = i; break; } if (soc > lut->row_entries[i]) { row1 = i - 1; row2 = i; break; } } } if (batt_temp < lut->col_entries[0] * DEGC_SCALE) batt_temp = lut->col_entries[0] * DEGC_SCALE; if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE) batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE; for (i = 0; i < cols; i++) if (batt_temp <= lut->col_entries[i] * DEGC_SCALE) break; if (batt_temp == lut->col_entries[i] * DEGC_SCALE) { var = linear_interpolate( lut->data[row1][i], lut->row_entries[row1], lut->data[row2][i], lut->row_entries[row2], soc); return var; } var1 = linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); var2 = linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); var = linear_interpolate( var1, lut->row_entries[row1], var2, lut->row_entries[row2], soc); return var; } int interpolate_slope(struct profile_table_data *lut, int batt_temp, int soc) { int i, ocvrow1, ocvrow2, rows, cols; int row1 = 0; int row2 = 0; int slope; rows = lut->rows; cols = lut->cols; if (soc >= lut->row_entries[0]) { pr_debug("soc %d >= max soc range - use the slope at soc=%d for lut %s\n", soc, lut->row_entries[0], lut->name); row1 = 0; row2 = 1; } else if (soc <= lut->row_entries[rows - 1]) { pr_debug("soc %d is <= min soc range - use the slope at soc=%d for lut %s\n", soc, lut->row_entries[rows - 1], lut->name); row1 = rows - 2; row2 = rows - 1; } else { for (i = 0; i < rows; i++) { if (soc >= lut->row_entries[i]) { row1 = i - 1; row2 = i; break; } } } if (batt_temp < lut->col_entries[0] * DEGC_SCALE) batt_temp = lut->col_entries[0] * DEGC_SCALE; if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE) batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE; for (i = 0; i < cols; i++) { if (batt_temp <= lut->col_entries[i] * DEGC_SCALE) break; } if (batt_temp == lut->col_entries[i] * DEGC_SCALE) { slope = (lut->data[row1][i] - lut->data[row2][i]); if (slope <= 0) { pr_warn_ratelimited("Slope=%d for soc=%d, using 1\n", slope, soc); slope = 1; } slope *= 10000; slope /= (lut->row_entries[row1] - lut->row_entries[row2]); return slope; } ocvrow1 = linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); ocvrow2 = linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); slope = (ocvrow1 - ocvrow2); if (slope <= 0) { pr_warn_ratelimited("Slope=%d for soc=%d, using 1\n", slope, soc); slope = 1; } slope *= 10000; slope /= (lut->row_entries[row1] - lut->row_entries[row2]); return slope; }