blob: eb23b876287f40495d98bfae74de9dfcfb7f2df8 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "calibration/magnetometer/mag_sphere_fit.h"
#include <errno.h>
#include <string.h>
#include "calibration/util/cal_log.h"
#define MAX_ITERATIONS 30
#define INITIAL_U_SCALE 1.0e-4f
#define GRADIENT_THRESHOLD 1.0e-16f
#define RELATIVE_STEP_THRESHOLD 1.0e-7f
#define FROM_MICRO_SEC_TO_SEC 1.0e-6f
void magCalSphereReset(struct MagCalSphere *mocs) {
mocs->number_of_data_samples = 0;
mocs->sample_counter = 0;
memset(&mocs->sphere_data, 0, sizeof(mocs->sphere_data));
}
void initMagCalSphere(struct MagCalSphere *mocs, float x_bias, float y_bias,
float z_bias, float c00, float c01, float c02, float c10,
float c11, float c12, float c20, float c21, float c22,
uint32_t min_batch_window_in_micros,
size_t min_num_diverse_vectors,
size_t max_num_max_distance, float var_threshold,
float max_min_threshold, float local_field,
float threshold_tuning_param,
float max_distance_tuning_param) {
initMagCal(&mocs->moc, x_bias, y_bias, z_bias, c00, c01, c02, c10, c11, c12,
c20, c21, c22, min_batch_window_in_micros, min_num_diverse_vectors,
max_num_max_distance, var_threshold, max_min_threshold,
local_field, threshold_tuning_param, max_distance_tuning_param);
mocs->inv_data_size = 1.0f / (float)NUM_SPHERE_FIT_DATA;
mocs->batch_time_in_sec =
(float)(min_batch_window_in_micros) * FROM_MICRO_SEC_TO_SEC;
// Initialize to take every sample, default setting.
mocs->sample_drop = 0;
magCalSphereReset(mocs);
// Setting lm params.
mocs->sphere_fit.params.max_iterations = MAX_ITERATIONS;
mocs->sphere_fit.params.initial_u_scale = INITIAL_U_SCALE;
mocs->sphere_fit.params.gradient_threshold = GRADIENT_THRESHOLD;
mocs->sphere_fit.params.relative_step_threshold = RELATIVE_STEP_THRESHOLD;
sphereFitInit(&mocs->sphere_fit.sphere_cal, &mocs->sphere_fit.params,
MIN_NUM_SPHERE_FIT_POINTS);
sphereFitSetSolverData(&mocs->sphere_fit.sphere_cal,
&mocs->sphere_fit.lm_data);
calDataReset(&mocs->sphere_fit.sphere_param);
}
void magCalSphereDestroy(struct MagCalSphere *mocs) { (void)mocs; }
void magCalSphereOdrUpdate(struct MagCalSphere *mocs, float odr_in_hz) {
// Calculate the numbers of samples to be dropped, in order to fill up
// the data set.
float sample_drop = odr_in_hz * mocs->batch_time_in_sec * mocs->inv_data_size;
mocs->sample_drop = (uint32_t)floorf(sample_drop);
}
// Updates the sphere fit data set, by calculating the numbers
// of samples to be dropped, based on odr_in_hz, to fill up the available memory
// in the given batch size window.
void magCalSphereDataUpdate(struct MagCalSphere *mocs, float x, float y,
float z) {
// build a vector.
const float vec[3] = {x, y, z};
// sample_counter for the down sampling.
mocs->sample_counter++;
// checking if sample_count >= sample_drop, if yes we store the mag sample in
// the data set.
if (mocs->sample_counter >= mocs->sample_drop) {
if (mocs->number_of_data_samples < NUM_SPHERE_FIT_DATA) {
memcpy(&mocs->sphere_data[mocs->number_of_data_samples *
THREE_AXIS_DATA_DIM],
vec, sizeof(float) * 3);
// counting the numbers of samples in the data set.
mocs->number_of_data_samples++;
}
// resetting the sample_counter.
mocs->sample_counter = 0;
}
}
// Runs the Sphere Fit.
enum MagUpdate magCalSphereFit(struct MagCalSphere *mocs,
uint64_t sample_time_us) {
// Setting up sphere fit data.
struct SphereFitData data = {&mocs->sphere_data[0], NULL,
mocs->number_of_data_samples, mocs->moc.radius};
float initial_bias[3] = {mocs->moc.x_bias, mocs->moc.y_bias,
mocs->moc.z_bias};
// Setting initial bias values based on the KASA fit.
sphereFitSetInitialBias(&mocs->sphere_fit.sphere_cal, initial_bias);
// Running the sphere fit and checking if successful.
if (sphereFitRunCal(&mocs->sphere_fit.sphere_cal, &data, sample_time_us)) {
// Updating Sphere parameters. Can use "calDataCorrectData" function to
// correct data.
sphereFitGetLatestCal(&mocs->sphere_fit.sphere_cal,
&mocs->sphere_fit.sphere_param);
// Updating that a full sphere fit is available.
return UPDATE_SPHERE_FIT;
}
return NO_UPDATE;
}
enum MagUpdate magCalSphereUpdate(struct MagCalSphere *mocs,
uint64_t sample_time_us, float x, float y,
float z) {
enum MagUpdate new_cal = NO_UPDATE;
// Saving data for sphere fit.
magCalSphereDataUpdate(mocs, x, y, z);
// Checking if KASA found a bias, if yes can run the sphere fit.
if (UPDATE_BIAS == magCalUpdate(&mocs->moc, sample_time_us, x, y, z)) {
// Running the sphere fit algo.
new_cal = magCalSphereFit(mocs, sample_time_us);
// Resetting.
sphereFitReset(&mocs->sphere_fit.sphere_cal);
magCalSphereReset(mocs);
// If moc.kasa_batching is false, ran into a time out, hence the sphere
// algo has to be reset as well.
} else if (!mocs->moc.kasa_batching) {
magCalSphereReset(mocs);
}
// Return which update has happened.
return new_cal;
}