2 * Copyright (C) 2014 Intel Corporation.
8 #include <hardware/sensors.h>
11 #include <utils/Log.h>
12 #include "calibration.h"
13 #include "matrix-ops.h"
14 #include "description.h"
17 #define MAX_RAW_DATA_COUNT 2000
18 #define RAW_DATA_FULL_PATH "/data/raw_compass_data_full_%d.txt"
19 #define RAW_DATA_SELECTED_PATH "/data/raw_compass_data_selected_%d.txt"
20 static FILE *raw_data = NULL;
21 static FILE *raw_data_selected = NULL;
22 static int raw_data_count = 0;
27 #define COMPASS_CALIBRATION_PATH "/data/compass.conf"
28 #define EPSILON 0.000000001
30 #define MAGNETIC_LOW 960 /* 31 micro tesla squared */
33 /* We'll have multiple calibration levels
34 * so that we can provide an estimation as fast as possible
36 static const float min_diffs[CAL_STEPS] = {0.2, 0.25, 0.4, 0.6, 1.0 };
37 static const float max_sqr_errs[CAL_STEPS] = {10.0, 10.0, 8.0, 5.0, 3.5 };
38 static const unsigned int lookback_counts[CAL_STEPS] = {2, 3, 4, 5, 6 };
40 /* reset calibration algorithm */
41 static void reset_sample (struct compass_cal* data)
44 data->sample_count = 0;
45 for (i = 0; i < MAGN_DS_SIZE; i++)
47 data->sample[i][j] = 0;
50 static double calc_square_err (struct compass_cal* data)
53 double raw[3][1], result[3][1], mat_diff[3][1];
56 for (i = 0; i < MAGN_DS_SIZE; i++) {
57 raw[0][0] = data->sample[i][0];
58 raw[1][0] = data->sample[i][1];
59 raw[2][0] = data->sample[i][2];
61 substract (3, 1, raw, data->offset, mat_diff);
62 multiply(3, 3, 1, data->w_invert, mat_diff, result);
64 double diff = sqrt(result[0][0] * result[0][0] + result[1][0] * result[1][0]
65 + result[2][0] * result[2][0]) - data->bfield;
73 // Given an real symmetric 3x3 matrix A, compute the eigenvalues
74 static void compute_eigenvalues(double mat[3][3], double* eig1, double* eig2, double* eig3)
76 double p = mat[0][1] * mat[0][1] + mat[0][2] * mat[0][2] + mat[1][2] * mat[1][2];
85 double q = (mat[0][0] + mat[1][1] + mat[2][2]) / 3;
86 double temp1 = mat[0][0] - q;
87 double temp2 = mat[1][1] - q;
88 double temp3 = mat[2][2] - q;
90 p = temp1 * temp1 + temp2 * temp2 + temp3 * temp3 + 2 * p;
94 assign(3, 3, mat, mat2);
98 multiply_scalar_inplace(3, 3, mat2, 1/p);
100 double r = (mat2[0][0] * mat2[1][1] * mat2[2][2] + mat2[0][1] * mat2[1][2] * mat2[2][0]
101 + mat2[0][2] * mat2[1][0] * mat2[2][1] - mat2[0][2] * mat2[1][1] * mat2[2][0]
102 - mat2[0][0] * mat2[1][2] * mat2[2][1] - mat2[0][1] * mat2[1][0] * mat2[2][2]) / 2;
112 *eig3 = q + 2 * p * cos(phi);
113 *eig1 = q + 2 * p * cos(phi + 2 * M_PI / 3);
114 *eig2 = 3 * q - *eig1 - *eig3;
117 static void calc_evector(double mat[3][3], double eig, double vec[3][1])
121 assign(3, 3, mat, h);
132 assign(2, 2, x_tmp, x);
134 double temp1 = x[0][0] * (-h[1][0]) + x[0][1] * (-h[2][0]);
135 double temp2 = x[1][0] * (-h[1][0]) + x[1][1] * (-h[2][0]);
136 double norm = sqrt(1 + temp1 * temp1 + temp2 * temp2);
138 vec[0][0] = 1.0 / norm;
139 vec[1][0] = temp1 / norm;
140 vec[2][0] = temp2 / norm;
143 static int ellipsoid_fit (mat_input_t m, double offset[3][1], double w_invert[3][3], double* bfield)
146 double h[MAGN_DS_SIZE][9];
147 double w[MAGN_DS_SIZE][1];
148 double h_trans[9][MAGN_DS_SIZE];
149 double p_temp1[9][9];
150 double p_temp2[9][MAGN_DS_SIZE];
151 double temp1[3][3], temp[3][3];
152 double temp1_inv[3][3];
156 double a[3][3], sqrt_evals[3][3], evecs[3][3], evecs_trans[3][3];
157 double evec1[3][1], evec2[3][1], evec3[3][1];
159 for (i = 0; i < MAGN_DS_SIZE; i++) {
160 w[i][0] = m[i][0] * m[i][0];
164 h[i][3] = -1 * m[i][0] * m[i][1];
165 h[i][4] = -1 * m[i][0] * m[i][2];
166 h[i][5] = -1 * m[i][1] * m[i][2];
167 h[i][6] = -1 * m[i][1] * m[i][1];
168 h[i][7] = -1 * m[i][2] * m[i][2];
171 transpose (MAGN_DS_SIZE, 9, h, h_trans);
172 multiply (9, MAGN_DS_SIZE, 9, h_trans, h, result);
173 invert (9, result, p_temp1);
174 multiply (9, 9, MAGN_DS_SIZE, p_temp1, h_trans, p_temp2);
175 multiply (9, MAGN_DS_SIZE, 1, p_temp2, w, p);
178 temp1[0][1] = p[3][0];
179 temp1[0][2] = p[4][0];
180 temp1[1][0] = p[3][0];
181 temp1[1][1] = 2 * p[6][0];
182 temp1[1][2] = p[5][0];
183 temp1[2][0] = p[4][0];
184 temp1[2][1] = p[5][0];
185 temp1[2][2] = 2 * p[7][0];
187 temp2[0][0] = p[0][0];
188 temp2[1][0] = p[1][0];
189 temp2[2][0] = p[2][0];
191 invert(3, temp1, temp1_inv);
192 multiply(3, 3, 1, temp1_inv, temp2, offset);
193 double off_x = offset[0][0];
194 double off_y = offset[1][0];
195 double off_z = offset[2][0];
198 a[0][0] = 1.0 / (p[8][0] + off_x * off_x + p[6][0] * off_y * off_y
199 + p[7][0] * off_z * off_z + p[3][0] * off_x * off_y
200 + p[4][0] * off_x * off_z + p[5][0] * off_y * off_z);
202 a[0][1] = p[3][0] * a[0][0] / 2;
203 a[0][2] = p[4][0] * a[0][0] / 2;
204 a[1][2] = p[5][0] * a[0][0] / 2;
205 a[1][1] = p[6][0] * a[0][0];
206 a[2][2] = p[7][0] * a[0][0];
211 double eig1 = 0, eig2 = 0, eig3 = 0;
212 compute_eigenvalues(a, &eig1, &eig2, &eig3);
214 sqrt_evals[0][0] = sqrt(eig1);
215 sqrt_evals[1][0] = 0;
216 sqrt_evals[2][0] = 0;
217 sqrt_evals[0][1] = 0;
218 sqrt_evals[1][1] = sqrt(eig2);
219 sqrt_evals[2][1] = 0;
220 sqrt_evals[0][2] = 0;
221 sqrt_evals[1][2] = 0;
222 sqrt_evals[2][2] = sqrt(eig3);
224 calc_evector(a, eig1, evec1);
225 calc_evector(a, eig2, evec2);
226 calc_evector(a, eig3, evec3);
228 evecs[0][0] = evec1[0][0];
229 evecs[1][0] = evec1[1][0];
230 evecs[2][0] = evec1[2][0];
231 evecs[0][1] = evec2[0][0];
232 evecs[1][1] = evec2[1][0];
233 evecs[2][1] = evec2[2][0];
234 evecs[0][2] = evec3[0][0];
235 evecs[1][2] = evec3[1][0];
236 evecs[2][2] = evec3[2][0];
238 multiply (3, 3, 3, evecs, sqrt_evals, temp1);
239 transpose(3, 3, evecs, evecs_trans);
240 multiply (3, 3, 3, temp1, evecs_trans, temp);
241 transpose (3, 3, temp, w_invert);
242 *bfield = pow(sqrt(1/eig1) * sqrt(1/eig2) * sqrt(1/eig3), 1.0/3.0);
247 multiply_scalar_inplace(3, 3, w_invert, *bfield);
252 static void compass_cal_init (FILE* data_file, struct sensor_info_t* info)
261 if (raw_data_selected) {
262 fclose(raw_data_selected);
263 raw_data_selected = NULL;
267 snprintf(path, 64, RAW_DATA_FULL_PATH, file_no);
268 raw_data = fopen(path,"w+");
269 snprintf(path, 64, RAW_DATA_SELECTED_PATH, file_no);
270 raw_data_selected = fopen(path,"w+");
275 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
276 int cal_steps = (info->max_cal_level && info->max_cal_level <= CAL_STEPS) ?
277 info->max_cal_level : CAL_STEPS;
278 if (cal_data == NULL)
282 reset_sample(cal_data);
284 if (!info->cal_level && data_file != NULL) {
285 int ret = fscanf(data_file, "%d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
286 &info->cal_level, &cal_data->offset[0][0], &cal_data->offset[1][0], &cal_data->offset[2][0],
287 &cal_data->w_invert[0][0], &cal_data->w_invert[0][1], &cal_data->w_invert[0][2],
288 &cal_data->w_invert[1][0], &cal_data->w_invert[1][1], &cal_data->w_invert[1][2],
289 &cal_data->w_invert[2][0], &cal_data->w_invert[2][1], &cal_data->w_invert[2][2],
292 if (ret != data_count || info->cal_level >= cal_steps) {
298 if (info->cal_level) {
299 ALOGV("CompassCalibration: load old data, caldata: %f %f %f %f %f %f %f %f %f %f %f %f %f",
300 cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0],
301 cal_data->w_invert[0][0], cal_data->w_invert[0][1], cal_data->w_invert[0][2], cal_data->w_invert[1][0],
302 cal_data->w_invert[1][1], cal_data->w_invert[1][2], cal_data->w_invert[2][0], cal_data->w_invert[2][1],
303 cal_data->w_invert[2][2], cal_data->bfield);
306 cal_data->offset[0][0] = 0;
307 cal_data->offset[1][0] = 0;
308 cal_data->offset[2][0] = 0;
310 cal_data->w_invert[0][0] = 1;
311 cal_data->w_invert[1][0] = 0;
312 cal_data->w_invert[2][0] = 0;
313 cal_data->w_invert[0][1] = 0;
314 cal_data->w_invert[1][1] = 1;
315 cal_data->w_invert[2][1] = 0;
316 cal_data->w_invert[0][2] = 0;
317 cal_data->w_invert[1][2] = 0;
318 cal_data->w_invert[2][2] = 1;
320 cal_data->bfield = 0;
325 static void compass_store_result(FILE* data_file, struct sensor_info_t* info)
327 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
329 if (data_file == NULL || cal_data == NULL)
332 int ret = fprintf(data_file, "%d %f %f %f %f %f %f %f %f %f %f %f %f %f\n",
333 info->cal_level, cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0],
334 cal_data->w_invert[0][0], cal_data->w_invert[0][1], cal_data->w_invert[0][2],
335 cal_data->w_invert[1][0], cal_data->w_invert[1][1], cal_data->w_invert[1][2],
336 cal_data->w_invert[2][0], cal_data->w_invert[2][1], cal_data->w_invert[2][2],
340 ALOGE ("compass calibration - store data failed!");
343 static int compass_collect (struct sensors_event_t* event, struct sensor_info_t* info)
345 float data[3] = {event->magnetic.x, event->magnetic.y, event->magnetic.z};
346 unsigned int index,j;
347 unsigned int lookback_count;
350 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
352 if (cal_data == NULL)
355 /* Discard the point if not valid */
356 if (data[0] == 0 || data[1] == 0 || data[2] == 0)
360 if (raw_data && raw_data_count < MAX_RAW_DATA_COUNT) {
361 fprintf(raw_data, "%f %f %f\n", (double)data[0], (double)data[1],
366 if (raw_data && raw_data_count >= MAX_RAW_DATA_COUNT) {
372 lookback_count = lookback_counts[info->cal_level];
373 min_diff = min_diffs[info->cal_level];
375 // For the current point to be accepted, each x/y/z value must be different enough
376 // to the last several collected points
377 if (cal_data->sample_count > 0 && cal_data->sample_count < MAGN_DS_SIZE) {
378 unsigned int lookback = lookback_count < cal_data->sample_count ? lookback_count :
379 cal_data->sample_count;
380 for (index = 0; index < lookback; index++){
381 for (j = 0; j < 3; j++) {
382 if (fabsf(data[j] - cal_data->sample[cal_data->sample_count-1-index][j]) < min_diff) {
383 ALOGV("CompassCalibration:point reject: [%f,%f,%f], selected_count=%d",
384 data[0], data[1], data[2], cal_data->sample_count);
391 if (cal_data->sample_count < MAGN_DS_SIZE) {
392 memcpy(cal_data->sample[cal_data->sample_count], data, sizeof(float) * 3);
393 cal_data->sample_count++;
394 ALOGV("CompassCalibration:point collected [%f,%f,%f], selected_count=%d",
395 (double)data[0], (double)data[1], (double)data[2], cal_data->sample_count);
397 if (raw_data_selected) {
398 fprintf(raw_data_selected, "%f %f %f\n", (double)data[0], (double)data[1], (double)data[2]);
405 static void scale_event (struct sensors_event_t* event)
408 float sanity_norm = 0;
411 sqr_norm = (event->magnetic.x * event->magnetic.x +
412 event->magnetic.y * event->magnetic.y +
413 event->magnetic.z * event->magnetic.z);
415 if (sqr_norm < MAGNETIC_LOW)
416 sanity_norm = MAGNETIC_LOW;
418 if (sanity_norm && sqr_norm) {
419 scale = sanity_norm / sqr_norm;
421 event->magnetic.x = event->magnetic.x * scale;
422 event->magnetic.y = event->magnetic.y * scale;
423 event->magnetic.z = event->magnetic.z * scale;
428 static void compass_compute_cal (struct sensors_event_t* event, struct sensor_info_t* info)
430 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
431 double result[3][1], raw[3][1], diff[3][1];
433 if (!info->cal_level || cal_data == NULL)
436 raw[0][0] = event->magnetic.x;
437 raw[1][0] = event->magnetic.y;
438 raw[2][0] = event->magnetic.z;
440 substract(3, 1, raw, cal_data->offset, diff);
441 multiply (3, 3, 1, cal_data->w_invert, diff, result);
443 event->magnetic.x = event->data[0] = result[0][0];
444 event->magnetic.y = event->data[1] = result[1][0];
445 event->magnetic.z = event->data[2] = result[2][0];
451 static int compass_ready (struct sensor_info_t* info)
457 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
460 * Some sensors take unrealistically long to calibrate at higher levels.
461 * We'll use a max_cal_level if we have such a property setup, or go with
462 * the default settings if not.
464 int cal_steps = (info->max_cal_level && info->max_cal_level <= CAL_STEPS) ?
465 info->max_cal_level : CAL_STEPS;
467 if (cal_data->sample_count < MAGN_DS_SIZE)
468 return info->cal_level;
470 max_sqr_err = max_sqr_errs[info->cal_level];
472 /* enough points have been collected, do the ellipsoid calibration */
473 for (i = 0; i < MAGN_DS_SIZE; i++) {
474 mat[i][0] = cal_data->sample[i][0];
475 mat[i][1] = cal_data->sample[i][1];
476 mat[i][2] = cal_data->sample[i][2];
479 /* check if result is good */
480 struct compass_cal new_cal_data;
481 /* the sample data must remain the same */
482 new_cal_data = *cal_data;
483 if (ellipsoid_fit(mat, new_cal_data.offset, new_cal_data.w_invert, &new_cal_data.bfield)) {
484 double new_err = calc_square_err (&new_cal_data);
485 ALOGI("new err is %f, max sqr err id %f", new_err,max_sqr_err);
486 if (new_err < max_sqr_err) {
487 double err = calc_square_err(cal_data);
489 /* new cal data is better, so we switch to the new */
490 memcpy(cal_data->offset, new_cal_data.offset, sizeof(cal_data->offset));
491 memcpy(cal_data->w_invert, new_cal_data.w_invert, sizeof(cal_data->w_invert));
492 cal_data->bfield = new_cal_data.bfield;
493 if (info->cal_level < (cal_steps - 1))
495 ALOGV("CompassCalibration: ready check success, caldata: %f %f %f %f %f %f %f %f %f %f %f %f %f, err %f",
496 cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0], cal_data->w_invert[0][0],
497 cal_data->w_invert[0][1], cal_data->w_invert[0][2], cal_data->w_invert[1][0], cal_data->w_invert[1][1],
498 cal_data->w_invert[1][2], cal_data->w_invert[2][0], cal_data->w_invert[2][1], cal_data->w_invert[2][2],
499 cal_data->bfield, new_err);
503 reset_sample(cal_data);
504 return info->cal_level;
508 void calibrate_compass (struct sensors_event_t* event, struct sensor_info_t* info)
512 /* Calibration is continuous */
513 compass_collect (event, info);
515 cal_level = compass_ready(info);
521 event->magnetic.status = SENSOR_STATUS_UNRELIABLE;
525 compass_compute_cal (event, info);
526 event->magnetic.status = SENSOR_STATUS_ACCURACY_LOW;
530 compass_compute_cal (event, info);
531 event->magnetic.status = SENSOR_STATUS_ACCURACY_MEDIUM;
535 compass_compute_cal (event, info);
536 event->magnetic.status = SENSOR_STATUS_ACCURACY_HIGH;
541 void compass_read_data (struct sensor_info_t* info)
543 FILE* data_file = fopen (COMPASS_CALIBRATION_PATH, "r");
545 compass_cal_init(data_file, info);
550 void compass_store_data (struct sensor_info_t* info)
552 FILE* data_file = fopen (COMPASS_CALIBRATION_PATH, "w");
554 compass_store_result(data_file, info);