OSDN Git Service

Revisit sampling rate arbitration
[android-x86/hardware-intel-libsensors.git] / compass-calibration.c
index 724ae43..85934be 100644 (file)
 
 #ifdef DBG_RAW_DATA
 #define MAX_RAW_DATA_COUNT 2000
+#define RAW_DATA_FULL_PATH "/data/raw_compass_data_full_%d.txt"
+#define RAW_DATA_SELECTED_PATH "/data/raw_compass_data_selected_%d.txt"
 static FILE *raw_data = NULL;
 static FILE *raw_data_selected = NULL;
 static int raw_data_count = 0;
 int file_no = 0;
 #endif
 
+/* compass defines */
+#define COMPASS_CALIBRATION_PATH "/data/compass.conf"
+#define EPSILON 0.000000001
+
+#define MAGNETIC_LOW 960 /* 31 micro tesla squared */
+#define CAL_STEPS 5
+
 /* We'll have multiple calibration levels
 *  so that we can provide an estimation as fast as possible
 */
-static const float min_diffs[CAL_STEPS] =  { 0.2, 0.4, 0.6, 1.0 };
-static const float max_sqr_errs[CAL_STEPS] = { 10.0, 8.0, 5.0, 3.5 };
-static const unsigned int lookback_counts[CAL_STEPS] = { 3, 4, 5, 6 };
+static const float min_diffs[CAL_STEPS] =  {0.2,  0.25, 0.4, 0.6, 1.0 };
+static const float max_sqr_errs[CAL_STEPS] = {10.0, 10.0, 8.0, 5.0, 3.5 };
+static const unsigned int lookback_counts[CAL_STEPS] = {2, 3, 4, 5, 6 };
 
 /* reset calibration algorithm */
-static void reset_sample (struct compass_cal* data)
+static void reset_sample (struct compass_cal_t* data)
 {
     int i,j;
     data->sample_count = 0;
-    for (i = 0; i < DS_SIZE; i++)
+    for (i = 0; i < MAGN_DS_SIZE; i++)
         for (j=0; j < 3; j++)
             data->sample[i][j] = 0;
+
+    data->average[0] = data->average[1] = data->average[2] = 0;
 }
 
-static double calc_square_err (struct compass_cal* data)
+static double calc_square_err (struct compass_cal_t* data)
 {
     double err = 0;
     double raw[3][1], result[3][1], mat_diff[3][1];
     int i;
+    float stdev[3] = {0,0,0};
 
-    for (i = 0; i < DS_SIZE; i++) {
+    for (i = 0; i < MAGN_DS_SIZE; i++) {
         raw[0][0] = data->sample[i][0];
         raw[1][0] = data->sample[i][1];
         raw[2][0] = data->sample[i][2];
 
+        stdev[0] += (raw[0][0] - data->average[0]) * (raw[0][0] - data->average[0]);
+        stdev[1] += (raw[1][0] - data->average[1]) * (raw[1][0] - data->average[1]);
+        stdev[2] += (raw[2][0] - data->average[2]) * (raw[2][0] - data->average[2]);
+
         substract (3, 1, raw, data->offset, mat_diff);
         multiply(3, 3, 1, data->w_invert, mat_diff, result);
 
@@ -57,11 +73,23 @@ static double calc_square_err (struct compass_cal* data)
 
         err += diff * diff;
     }
-    err /= DS_SIZE;
+
+    stdev[0] = sqrt(stdev[0] / MAGN_DS_SIZE);
+    stdev[1] = sqrt(stdev[1] / MAGN_DS_SIZE);
+    stdev[2] = sqrt(stdev[2] / MAGN_DS_SIZE);
+
+    /*
+     * A sanity check - if we have too little variation for an axis
+     * it's best to reject the calibration than risking a wrong calibration.
+     */
+    if (stdev[0] <= 1 || stdev[1] <= 1 || stdev[2] <= 1)
+        return max_sqr_errs[0];
+
+    err /= MAGN_DS_SIZE;
     return err;
 }
 
-// Given an real symmetric 3x3 matrix A, compute the eigenvalues
+/* Given an real symmetric 3x3 matrix A, compute the eigenvalues */
 static void compute_eigenvalues(double mat[3][3], double* eig1, double* eig2, double* eig3)
 {
     double p = mat[0][1] * mat[0][1] + mat[0][2] * mat[0][2] + mat[1][2] * mat[1][2];
@@ -134,11 +162,11 @@ static void calc_evector(double mat[3][3], double eig, double vec[3][1])
 static int ellipsoid_fit (mat_input_t m, double offset[3][1], double w_invert[3][3], double* bfield)
 {
     int i;
-    double h[DS_SIZE][9];
-    double w[DS_SIZE][1];
-    double h_trans[9][DS_SIZE];
+    double h[MAGN_DS_SIZE][9];
+    double w[MAGN_DS_SIZE][1];
+    double h_trans[9][MAGN_DS_SIZE];
     double p_temp1[9][9];
-    double p_temp2[9][DS_SIZE];
+    double p_temp2[9][MAGN_DS_SIZE];
     double temp1[3][3], temp[3][3];
     double temp1_inv[3][3];
     double temp2[3][1];
@@ -147,7 +175,7 @@ static int ellipsoid_fit (mat_input_t m, double offset[3][1], double w_invert[3]
     double a[3][3], sqrt_evals[3][3], evecs[3][3], evecs_trans[3][3];
     double evec1[3][1], evec2[3][1], evec3[3][1];
 
-    for (i = 0; i < DS_SIZE; i++) {
+    for (i = 0; i < MAGN_DS_SIZE; i++) {
         w[i][0] = m[i][0] * m[i][0];
         h[i][0] = m[i][0];
         h[i][1] = m[i][1];
@@ -159,11 +187,11 @@ static int ellipsoid_fit (mat_input_t m, double offset[3][1], double w_invert[3]
         h[i][7] = -1 * m[i][2] * m[i][2];
         h[i][8] = 1;
     }
-    transpose (DS_SIZE, 9, h, h_trans);
-    multiply (9, DS_SIZE, 9, h_trans, h, result);
+    transpose (MAGN_DS_SIZE, 9, h, h_trans);
+    multiply (9, MAGN_DS_SIZE, 9, h_trans, h, result);
     invert (9, result, p_temp1);
-    multiply (9, 9, DS_SIZE, p_temp1, h_trans, p_temp2);
-    multiply (9, DS_SIZE, 1, p_temp2, w, p);
+    multiply (9, 9, MAGN_DS_SIZE, p_temp1, h_trans, p_temp2);
+    multiply (9, MAGN_DS_SIZE, 1, p_temp2, w, p);
 
     temp1[0][0] = 2;
     temp1[0][1] = p[3][0];
@@ -202,6 +230,9 @@ static int ellipsoid_fit (mat_input_t m, double offset[3][1], double w_invert[3]
     double eig1 = 0, eig2 = 0, eig3 = 0;
     compute_eigenvalues(a, &eig1, &eig2, &eig3);
 
+    if (eig1 <=0 || eig2 <= 0 || eig3 <= 0)
+        return 0;
+
     sqrt_evals[0][0] = sqrt(eig1);
     sqrt_evals[1][0] = 0;
     sqrt_evals[2][0] = 0;
@@ -231,6 +262,10 @@ static int ellipsoid_fit (mat_input_t m, double offset[3][1], double w_invert[3]
     multiply (3, 3, 3, temp1, evecs_trans, temp);
     transpose (3, 3, temp, w_invert);
     *bfield = pow(sqrt(1/eig1) * sqrt(1/eig2) * sqrt(1/eig3), 1.0/3.0);
+
+    if (*bfield < 0)
+        return 0;
+
     multiply_scalar_inplace(3, 3, w_invert, *bfield);
 
     return 1;
@@ -259,8 +294,9 @@ static void compass_cal_init (FILE* data_file, struct sensor_info_t* info)
     raw_data_count = 0;
 #endif
 
-    struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
-
+    struct compass_cal_t* cal_data = (struct compass_cal_t*) info->cal_data;
+    int cal_steps = (info->max_cal_level && info->max_cal_level <= CAL_STEPS) ?
+        info->max_cal_level : CAL_STEPS;
     if (cal_data == NULL)
         return;
 
@@ -275,11 +311,12 @@ static void compass_cal_init (FILE* data_file, struct sensor_info_t* info)
             &cal_data->w_invert[2][0], &cal_data->w_invert[2][1], &cal_data->w_invert[2][2],
             &cal_data->bfield);
 
-        if (ret != data_count) {
+        if (ret != data_count || info->cal_level >= cal_steps) {
             info->cal_level = 0;
         }
     }
 
+
     if (info->cal_level) {
         ALOGV("CompassCalibration: load old data, caldata: %f %f %f %f %f %f %f %f %f %f %f %f %f",
             cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0],
@@ -309,7 +346,7 @@ static void compass_cal_init (FILE* data_file, struct sensor_info_t* info)
 
 static void compass_store_result(FILE* data_file, struct sensor_info_t* info)
 {
-    struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
+    struct compass_cal_t* cal_data = (struct compass_cal_t*) info->cal_data;
 
     if (data_file == NULL || cal_data == NULL)
         return;
@@ -325,20 +362,20 @@ static void compass_store_result(FILE* data_file, struct sensor_info_t* info)
         ALOGE ("compass calibration - store data failed!");
 }
 
-static int compass_collect (struct sensors_event_t* event, struct sensor_info_t* info, int64_t current_time)
+static int compass_collect (struct sensors_event_t* event, struct sensor_info_t* info)
 {
     float data[3] = {event->magnetic.x, event->magnetic.y, event->magnetic.z};
     unsigned int index,j;
     unsigned int lookback_count;
     float min_diff;
 
-    struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
+    struct compass_cal_t* cal_data = (struct compass_cal_t*) info->cal_data;
 
     if (cal_data == NULL)
         return -1;
 
     /* Discard the point if not valid */
-    if (data[0] == 0 && data[1] == 0 && data[2] == 0)
+    if (data[0] == 0 || data[1] == 0 || data[2] == 0)
         return -1;
 
 #ifdef DBG_RAW_DATA
@@ -357,9 +394,11 @@ static int compass_collect (struct sensors_event_t* event, struct sensor_info_t*
     lookback_count = lookback_counts[info->cal_level];
     min_diff = min_diffs[info->cal_level];
 
-    // For the current point to be accepted, each x/y/z value must be different enough
-    // to the last several collected points
-    if (cal_data->sample_count > 0 && cal_data->sample_count < DS_SIZE) {
+    /*
+     * For the current point to be accepted, each x/y/z value must be different
+     * enough to the last several collected points.
+     */
+    if (cal_data->sample_count > 0 && cal_data->sample_count < MAGN_DS_SIZE) {
         unsigned int lookback = lookback_count < cal_data->sample_count ? lookback_count :
                         cal_data->sample_count;
         for (index = 0; index < lookback; index++){
@@ -373,9 +412,12 @@ static int compass_collect (struct sensors_event_t* event, struct sensor_info_t*
         }
     }
 
-    if (cal_data->sample_count < DS_SIZE) {
+    if (cal_data->sample_count < MAGN_DS_SIZE) {
         memcpy(cal_data->sample[cal_data->sample_count], data, sizeof(float) * 3);
         cal_data->sample_count++;
+        cal_data->average[0] += data[0];
+        cal_data->average[1] += data[1];
+        cal_data->average[2] += data[2];
         ALOGV("CompassCalibration:point collected [%f,%f,%f], selected_count=%d",
             (double)data[0], (double)data[1], (double)data[2], cal_data->sample_count);
 #ifdef DBG_RAW_DATA
@@ -397,8 +439,8 @@ static void scale_event (struct sensors_event_t* event)
                 event->magnetic.y * event->magnetic.y +
                 event->magnetic.z * event->magnetic.z);
 
-    sanity_norm = (sqr_norm < MAGNETIC_LOW) ?  MAGNETIC_LOW : sanity_norm;
-    sanity_norm = (sqr_norm > MAGNETIC_HIGH) ? MAGNETIC_HIGH : sanity_norm;
+    if (sqr_norm < MAGNETIC_LOW)
+        sanity_norm = MAGNETIC_LOW;
 
     if (sanity_norm && sqr_norm) {
         scale = sanity_norm / sqr_norm;
@@ -412,7 +454,7 @@ static void scale_event (struct sensors_event_t* event)
 
 static void compass_compute_cal (struct sensors_event_t* event, struct sensor_info_t* info)
 {
-    struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
+    struct compass_cal_t* cal_data = (struct compass_cal_t*) info->cal_data;
     double result[3][1], raw[3][1], diff[3][1];
 
     if (!info->cal_level || cal_data == NULL)
@@ -439,24 +481,38 @@ static int compass_ready (struct sensor_info_t* info)
     int i;
     float max_sqr_err;
 
-    struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
+    struct compass_cal_t* cal_data = (struct compass_cal_t*) info->cal_data;
+    struct compass_cal_t new_cal_data;
+
+    /*
+     *  Some sensors take unrealistically long to calibrate at higher levels.
+     *  We'll use a max_cal_level if we have such a property setup, or go with
+     *  the default settings if not.
+     */
+    int cal_steps = (info->max_cal_level && info->max_cal_level <= CAL_STEPS) ?
+        info->max_cal_level : CAL_STEPS;
 
-    if (cal_data->sample_count < DS_SIZE)
+    if (cal_data->sample_count < MAGN_DS_SIZE)
         return info->cal_level;
 
     max_sqr_err = max_sqr_errs[info->cal_level];
 
-    /* enough points have been collected, do the ellipsoid calibration */
-    for (i = 0; i < DS_SIZE; i++) {
+    /* Enough points have been collected, do the ellipsoid calibration */
+
+    /* Compute average per axis */
+    cal_data->average[0] /= MAGN_DS_SIZE;
+    cal_data->average[1] /= MAGN_DS_SIZE;
+    cal_data->average[2] /= MAGN_DS_SIZE;
+
+    for (i = 0; i < MAGN_DS_SIZE; i++) {
        mat[i][0] = cal_data->sample[i][0];
        mat[i][1] = cal_data->sample[i][1];
        mat[i][2] = cal_data->sample[i][2];
     }
 
-    /* check if result is good */
-    struct compass_cal new_cal_data;
-    /* the sample data must remain the same */
+    /* Check if result is good. The sample data must remain the same */
     new_cal_data = *cal_data;
+
     if (ellipsoid_fit(mat, new_cal_data.offset, new_cal_data.w_invert, &new_cal_data.bfield)) {
         double new_err = calc_square_err (&new_cal_data);
         ALOGI("new err is %f, max sqr err id %f", new_err,max_sqr_err);
@@ -467,7 +523,7 @@ static int compass_ready (struct sensor_info_t* info)
                 memcpy(cal_data->offset, new_cal_data.offset, sizeof(cal_data->offset));
                 memcpy(cal_data->w_invert, new_cal_data.w_invert, sizeof(cal_data->w_invert));
                 cal_data->bfield = new_cal_data.bfield;
-                if (info->cal_level < (CAL_STEPS - 1))
+                if (info->cal_level < (cal_steps - 1))
                     info->cal_level++;
                 ALOGV("CompassCalibration: ready check success, caldata: %f %f %f %f %f %f %f %f %f %f %f %f %f, err %f",
                     cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0], cal_data->w_invert[0][0],
@@ -481,13 +537,13 @@ static int compass_ready (struct sensor_info_t* info)
     return info->cal_level;
 }
 
-void calibrate_compass (struct sensors_event_t* event, struct sensor_info_t* info, int64_t current_time)
+
+void calibrate_compass (struct sensors_event_t* event, struct sensor_info_t* info)
 {
-    long current_time_ms = current_time / 1000000;
     int cal_level;
 
     /* Calibration is continuous */
-    compass_collect (event, info, current_time_ms);
+    compass_collect (event, info);
 
     cal_level = compass_ready(info);