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"
17 #define MAX_RAW_DATA_COUNT 2000
18 static FILE *raw_data = NULL;
19 static FILE *raw_data_selected = NULL;
20 static int raw_data_count = 0;
25 /* reset calibration algorithm */
26 static void reset_sample (struct compass_cal* data)
29 data->sample_count = 0;
30 for (i = 0; i < DS_SIZE; i++)
32 data->sample[i][j] = 0;
35 static double calc_square_err (struct compass_cal* data)
38 double raw[3][1], result[3][1], mat_diff[3][1];
41 for (i = 0; i < DS_SIZE; i++) {
42 raw[0][0] = data->sample[i][0];
43 raw[1][0] = data->sample[i][1];
44 raw[2][0] = data->sample[i][2];
46 substract (3, 1, raw, data->offset, mat_diff);
47 multiply(3, 3, 1, data->w_invert, mat_diff, result);
49 double diff = sqrt(result[0][0] * result[0][0] + result[1][0] * result[1][0]
50 + result[2][0] * result[2][0]) - data->bfield;
58 // Given an real symmetric 3x3 matrix A, compute the eigenvalues
59 static void compute_eigenvalues(double mat[3][3], double* eig1, double* eig2, double* eig3)
61 double p = mat[0][1] * mat[0][1] + mat[0][2] * mat[0][2] + mat[1][2] * mat[1][2];
70 double q = (mat[0][0] + mat[1][1] + mat[2][2]) / 3;
71 double temp1 = mat[0][0] - q;
72 double temp2 = mat[1][1] - q;
73 double temp3 = mat[2][2] - q;
75 p = temp1 * temp1 + temp2 * temp2 + temp3 * temp3 + 2 * p;
79 assign(3, 3, mat, mat2);
83 multiply_scalar_inplace(3, 3, mat2, 1/p);
85 double r = (mat2[0][0] * mat2[1][1] * mat2[2][2] + mat2[0][1] * mat2[1][2] * mat2[2][0]
86 + mat2[0][2] * mat2[1][0] * mat2[2][1] - mat2[0][2] * mat2[1][1] * mat2[2][0]
87 - mat2[0][0] * mat2[1][2] * mat2[2][1] - mat2[0][1] * mat2[1][0] * mat2[2][2]) / 2;
97 *eig3 = q + 2 * p * cos(phi);
98 *eig1 = q + 2 * p * cos(phi + 2 * M_PI / 3);
99 *eig2 = 3 * q - *eig1 - *eig3;
102 static void calc_evector(double mat[3][3], double eig, double vec[3][1])
106 assign(3, 3, mat, h);
117 assign(2, 2, x_tmp, x);
119 double temp1 = x[0][0] * (-h[1][0]) + x[0][1] * (-h[2][0]);
120 double temp2 = x[1][0] * (-h[1][0]) + x[1][1] * (-h[2][0]);
121 double norm = sqrt(1 + temp1 * temp1 + temp2 * temp2);
123 vec[0][0] = 1.0 / norm;
124 vec[1][0] = temp1 / norm;
125 vec[2][0] = temp2 / norm;
128 static int ellipsoid_fit (mat_input_t m, double offset[3][1], double w_invert[3][3], double* bfield)
131 double h[DS_SIZE][9];
132 double w[DS_SIZE][1];
133 double h_trans[9][DS_SIZE];
134 double p_temp1[9][9];
135 double p_temp2[9][DS_SIZE];
136 double temp1[3][3], temp[3][3];
137 double temp1_inv[3][3];
141 double a[3][3], sqrt_evals[3][3], evecs[3][3], evecs_trans[3][3];
142 double evec1[3][1], evec2[3][1], evec3[3][1];
144 for (i = 0; i < DS_SIZE; i++) {
145 w[i][0] = m[i][0] * m[i][0];
149 h[i][3] = -1 * m[i][0] * m[i][1];
150 h[i][4] = -1 * m[i][0] * m[i][2];
151 h[i][5] = -1 * m[i][1] * m[i][2];
152 h[i][6] = -1 * m[i][1] * m[i][1];
153 h[i][7] = -1 * m[i][2] * m[i][2];
156 transpose (DS_SIZE, 9, h, h_trans);
157 multiply (9, DS_SIZE, 9, h_trans, h, result);
158 invert (9, result, p_temp1);
159 multiply (9, 9, DS_SIZE, p_temp1, h_trans, p_temp2);
160 multiply (9, DS_SIZE, 1, p_temp2, w, p);
163 temp1[0][1] = p[3][0];
164 temp1[0][2] = p[4][0];
165 temp1[1][0] = p[3][0];
166 temp1[1][1] = 2 * p[6][0];
167 temp1[1][2] = p[5][0];
168 temp1[2][0] = p[4][0];
169 temp1[2][1] = p[5][0];
170 temp1[2][2] = 2 * p[7][0];
172 temp2[0][0] = p[0][0];
173 temp2[1][0] = p[1][0];
174 temp2[2][0] = p[2][0];
176 invert(3, temp1, temp1_inv);
177 multiply(3, 3, 1, temp1_inv, temp2, offset);
178 double off_x = offset[0][0];
179 double off_y = offset[1][0];
180 double off_z = offset[2][0];
183 a[0][0] = 1.0 / (p[8][0] + off_x * off_x + p[6][0] * off_y * off_y
184 + p[7][0] * off_z * off_z + p[3][0] * off_x * off_y
185 + p[4][0] * off_x * off_z + p[5][0] * off_y * off_z);
187 a[0][1] = p[3][0] * a[0][0] / 2;
188 a[0][2] = p[4][0] * a[0][0] / 2;
189 a[1][2] = p[5][0] * a[0][0] / 2;
190 a[1][1] = p[6][0] * a[0][0];
191 a[2][2] = p[7][0] * a[0][0];
196 double eig1 = 0, eig2 = 0, eig3 = 0;
197 compute_eigenvalues(a, &eig1, &eig2, &eig3);
199 sqrt_evals[0][0] = sqrt(eig1);
200 sqrt_evals[1][0] = 0;
201 sqrt_evals[2][0] = 0;
202 sqrt_evals[0][1] = 0;
203 sqrt_evals[1][1] = sqrt(eig2);
204 sqrt_evals[2][1] = 0;
205 sqrt_evals[0][2] = 0;
206 sqrt_evals[1][2] = 0;
207 sqrt_evals[2][2] = sqrt(eig3);
209 calc_evector(a, eig1, evec1);
210 calc_evector(a, eig2, evec2);
211 calc_evector(a, eig3, evec3);
213 evecs[0][0] = evec1[0][0];
214 evecs[1][0] = evec1[1][0];
215 evecs[2][0] = evec1[2][0];
216 evecs[0][1] = evec2[0][0];
217 evecs[1][1] = evec2[1][0];
218 evecs[2][1] = evec2[2][0];
219 evecs[0][2] = evec3[0][0];
220 evecs[1][2] = evec3[1][0];
221 evecs[2][2] = evec3[2][0];
223 multiply (3, 3, 3, evecs, sqrt_evals, temp1);
224 transpose(3, 3, evecs, evecs_trans);
225 multiply (3, 3, 3, temp1, evecs_trans, temp);
226 transpose (3, 3, temp, w_invert);
227 *bfield = pow(sqrt(1/eig1) * sqrt(1/eig2) * sqrt(1/eig3), 1.0/3.0);
228 multiply_scalar_inplace(3, 3, w_invert, *bfield);
233 static void compass_cal_init (FILE* data_file, struct sensor_info_t* info)
242 if (raw_data_selected) {
243 fclose(raw_data_selected);
244 raw_data_selected = NULL;
248 snprintf(path, 64, RAW_DATA_FULL_PATH, file_no);
249 raw_data = fopen(path,"w+");
250 snprintf(path, 64, RAW_DATA_SELECTED_PATH, file_no);
251 raw_data_selected = fopen(path,"w+");
256 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
258 if (cal_data == NULL)
262 reset_sample(cal_data);
264 if (!info->calibrated && data_file != NULL) {
265 int ret = fscanf(data_file, "%d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
266 &info->calibrated, &cal_data->offset[0][0], &cal_data->offset[1][0], &cal_data->offset[2][0],
267 &cal_data->w_invert[0][0], &cal_data->w_invert[0][1], &cal_data->w_invert[0][2],
268 &cal_data->w_invert[1][0], &cal_data->w_invert[1][1], &cal_data->w_invert[1][2],
269 &cal_data->w_invert[2][0], &cal_data->w_invert[2][1], &cal_data->w_invert[2][2],
272 if (ret != data_count) {
273 info->calibrated = 0;
277 if (info->calibrated) {
278 ALOGV("CompassCalibration: load old data, caldata: %f %f %f %f %f %f %f %f %f %f %f %f %f",
279 cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0],
280 cal_data->w_invert[0][0], cal_data->w_invert[0][1], cal_data->w_invert[0][2], cal_data->w_invert[1][0],
281 cal_data->w_invert[1][1], cal_data->w_invert[1][2], cal_data->w_invert[2][0], cal_data->w_invert[2][1],
282 cal_data->w_invert[2][2], cal_data->bfield);
285 cal_data->offset[0][0] = 0;
286 cal_data->offset[1][0] = 0;
287 cal_data->offset[2][0] = 0;
289 cal_data->w_invert[0][0] = 1;
290 cal_data->w_invert[1][0] = 0;
291 cal_data->w_invert[2][0] = 0;
292 cal_data->w_invert[0][1] = 0;
293 cal_data->w_invert[1][1] = 1;
294 cal_data->w_invert[2][1] = 0;
295 cal_data->w_invert[0][2] = 0;
296 cal_data->w_invert[1][2] = 0;
297 cal_data->w_invert[2][2] = 1;
299 cal_data->bfield = 0;
304 static void compass_store_result(FILE* data_file, struct sensor_info_t* info)
306 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
308 if (data_file == NULL || cal_data == NULL)
311 int ret = fprintf(data_file, "%d %f %f %f %f %f %f %f %f %f %f %f %f %f\n",
312 info->calibrated, cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0],
313 cal_data->w_invert[0][0], cal_data->w_invert[0][1], cal_data->w_invert[0][2],
314 cal_data->w_invert[1][0], cal_data->w_invert[1][1], cal_data->w_invert[1][2],
315 cal_data->w_invert[2][0], cal_data->w_invert[2][1], cal_data->w_invert[2][2],
319 ALOGE ("compass calibration - store data failed!");
322 static int compass_collect (struct sensors_event_t* event, struct sensor_info_t* info, int64_t current_time)
324 float data[3] = {event->magnetic.x, event->magnetic.y, event->magnetic.z};
325 unsigned int index,j;
326 unsigned int lookback_count;
329 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
331 if (cal_data == NULL)
335 if (raw_data && raw_data_count < MAX_RAW_DATA_COUNT) {
336 fprintf(raw_data, "%f %f %f\n", (double)data[0], (double)data[1],
341 if (raw_data && raw_data_count >= MAX_RAW_DATA_COUNT) {
347 lookback_count = info->calibrated ? LOOKBACK_COUNT : FIRST_LOOKBACK_COUNT;
348 min_diff = info->calibrated ? MIN_DIFF : FIRST_MIN_DIFF;
350 // For the current point to be accepted, each x/y/z value must be different enough
351 // to the last several collected points
352 if (cal_data->sample_count > 0 && cal_data->sample_count < DS_SIZE) {
353 unsigned int lookback = lookback_count < cal_data->sample_count ? lookback_count :
354 cal_data->sample_count;
355 for (index = 0; index < lookback; index++){
356 for (j = 0; j < 3; j++) {
357 if (fabsf(data[j] - cal_data->sample[cal_data->sample_count-1-index][j]) < min_diff) {
358 ALOGV("CompassCalibration:point reject: [%f,%f,%f], selected_count=%d",
359 data[0], data[1], data[2], cal_data->sample_count);
366 if (cal_data->sample_count < DS_SIZE) {
367 memcpy(cal_data->sample[cal_data->sample_count], data, sizeof(float) * 3);
368 cal_data->sample_count++;
369 ALOGV("CompassCalibration:point collected [%f,%f,%f], selected_count=%d",
370 (double)data[0], (double)data[1], (double)data[2], cal_data->sample_count);
372 if (raw_data_selected) {
373 fprintf(raw_data_selected, "%f %f %f\n", (double)data[0], (double)data[1], (double)data[2]);
380 static void compass_compute_cal (struct sensors_event_t* event, struct sensor_info_t* info)
382 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
384 if (!info->calibrated || cal_data == NULL)
387 double result[3][1], raw[3][1], diff[3][1];
389 raw[0][0] = event->magnetic.x;
390 raw[1][0] = event->magnetic.y;
391 raw[2][0] = event->magnetic.z;
393 substract(3, 1, raw, cal_data->offset, diff);
394 multiply (3, 3, 1, cal_data->w_invert, diff, result);
396 event->magnetic.x = event->data[0] = result[0][0];
397 event->magnetic.y = event->data[1] = result[1][0];
398 event->magnetic.z = event->data[2] = result[2][0];
401 static int compass_ready (struct sensor_info_t* info)
407 struct compass_cal* cal_data = (struct compass_cal*) info->cal_data;
409 if (cal_data->sample_count < DS_SIZE)
410 return info->calibrated;
412 max_sqr_err = info->calibrated ? MAX_SQR_ERR : FIRST_MAX_SQR_ERR;
414 /* enough points have been collected, do the ellipsoid calibration */
415 for (i = 0; i < DS_SIZE; i++) {
416 mat[i][0] = cal_data->sample[i][0];
417 mat[i][1] = cal_data->sample[i][1];
418 mat[i][2] = cal_data->sample[i][2];
421 /* check if result is good */
422 struct compass_cal new_cal_data;
423 /* the sample data must remain the same */
424 new_cal_data = *cal_data;
425 if (ellipsoid_fit(mat, new_cal_data.offset, new_cal_data.w_invert, &new_cal_data.bfield)) {
426 double new_err = calc_square_err (&new_cal_data);
427 ALOGI("new err is %f, max sqr err id %f", new_err,max_sqr_err);
428 if (new_err < max_sqr_err) {
429 double err = calc_square_err(cal_data);
431 /* new cal data is better, so we switch to the new */
432 memcpy(cal_data->offset, new_cal_data.offset, sizeof(cal_data->offset));
433 memcpy(cal_data->w_invert, new_cal_data.w_invert, sizeof(cal_data->w_invert));
434 cal_data->bfield = new_cal_data.bfield;
435 info->calibrated = 1;
436 ALOGV("CompassCalibration: ready check success, caldata: %f %f %f %f %f %f %f %f %f %f %f %f %f, err %f",
437 cal_data->offset[0][0], cal_data->offset[1][0], cal_data->offset[2][0], cal_data->w_invert[0][0],
438 cal_data->w_invert[0][1], cal_data->w_invert[0][2], cal_data->w_invert[1][0], cal_data->w_invert[1][1],
439 cal_data->w_invert[1][2], cal_data->w_invert[2][0], cal_data->w_invert[2][1], cal_data->w_invert[2][2],
440 cal_data->bfield, new_err);
444 reset_sample(cal_data);
445 return info->calibrated;
448 void calibrate_compass (struct sensors_event_t* event, struct sensor_info_t* info, int64_t current_time)
450 long current_time_ms = current_time / 1000000;
451 compass_collect (event, info, current_time_ms);
452 if (compass_ready(info)) {
453 compass_compute_cal (event, info);
454 event->magnetic.status = SENSOR_STATUS_ACCURACY_HIGH;
456 event->magnetic.status = SENSOR_STATUS_ACCURACY_LOW;
460 void compass_read_data (struct sensor_info_t* info)
462 FILE* data_file = fopen (COMPASS_CALIBRATION_PATH, "r");
464 compass_cal_init(data_file, info);
469 void compass_store_data (struct sensor_info_t* info)
471 FILE* data_file = fopen (COMPASS_CALIBRATION_PATH, "w");
473 compass_store_result(data_file, info);