Home | History | Annotate | Download | only in mag_sphere_fit_cal
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "calibration/magnetometer/mag_sphere_fit_cal/mag_sphere_fit.h"
     18 
     19 #include <errno.h>
     20 #include <string.h>
     21 
     22 #define MAX_ITERATIONS 30
     23 #define INITIAL_U_SCALE 1.0e-4f
     24 #define GRADIENT_THRESHOLD 1.0e-16f
     25 #define RELATIVE_STEP_THRESHOLD 1.0e-7f
     26 #define FROM_MICRO_SEC_TO_SEC 1.0e-6f
     27 
     28 void magCalSphereReset(struct MagCalSphere *mocs) {
     29   mocs->number_of_data_samples = 0;
     30   mocs->sample_counter = 0;
     31   memset(&mocs->sphere_data, 0, sizeof(mocs->sphere_data));
     32 }
     33 
     34 void initMagCalSphere(
     35     struct MagCalSphere *mocs,
     36     const struct MagCalParameters *mag_cal_parameters,
     37     const struct DiversityCheckerParameters *diverse_parameters,
     38     float default_odr_in_hz) {
     39   initMagCal(&mocs->moc, mag_cal_parameters, diverse_parameters);
     40   mocs->inv_data_size = 1.0f / (float)NUM_SPHERE_FIT_DATA;
     41   mocs->batch_time_in_sec =
     42       (float)(mag_cal_parameters->min_batch_window_in_micros) *
     43       FROM_MICRO_SEC_TO_SEC;
     44   // Initialize to take every sample, default setting.
     45   mocs->sample_drop = 0;
     46   magCalSphereReset(mocs);
     47 
     48   // Setting lm params.
     49   mocs->sphere_fit.params.max_iterations = MAX_ITERATIONS;
     50   mocs->sphere_fit.params.initial_u_scale = INITIAL_U_SCALE;
     51   mocs->sphere_fit.params.gradient_threshold = GRADIENT_THRESHOLD;
     52   mocs->sphere_fit.params.relative_step_threshold = RELATIVE_STEP_THRESHOLD;
     53   sphereFitInit(&mocs->sphere_fit.sphere_cal, &mocs->sphere_fit.params,
     54                 MIN_NUM_SPHERE_FIT_POINTS);
     55   sphereFitSetSolverData(&mocs->sphere_fit.sphere_cal,
     56                          &mocs->sphere_fit.lm_data);
     57   calDataReset(&mocs->sphere_fit.sphere_param);
     58 
     59   // Initializes the starting output data rate estimate.
     60   magCalSphereOdrUpdate(mocs, default_odr_in_hz);
     61 }
     62 
     63 void magCalSphereDestroy(struct MagCalSphere *mocs) { (void)mocs; }
     64 
     65 void magCalSphereOdrUpdate(struct MagCalSphere *mocs, float odr_in_hz) {
     66   // Calculate the numbers of samples to be dropped, in order to fill up
     67   // the data set.
     68   float sample_drop = odr_in_hz * mocs->batch_time_in_sec * mocs->inv_data_size;
     69   mocs->sample_drop = (uint32_t)floorf(sample_drop);
     70 }
     71 
     72 // Updates the sphere fit data set, by calculating the numbers
     73 // of samples to be dropped, based on odr_in_hz, to fill up the available memory
     74 // in the given batch size window.
     75 void magCalSphereDataUpdate(struct MagCalSphere *mocs, float x, float y,
     76                             float z) {
     77   // build a vector.
     78   const float vec[3] = {x, y, z};
     79 
     80   // sample_counter for the down sampling.
     81   mocs->sample_counter++;
     82 
     83   // checking if sample_count >= sample_drop, if yes we store the mag sample in
     84   // the data set.
     85   if (mocs->sample_counter >= mocs->sample_drop) {
     86     if (mocs->number_of_data_samples < NUM_SPHERE_FIT_DATA) {
     87       memcpy(&mocs->sphere_data[mocs->number_of_data_samples *
     88                                 THREE_AXIS_DATA_DIM],
     89              vec, sizeof(float) * THREE_AXIS_DATA_DIM);
     90       // counting the numbers of samples in the data set.
     91       mocs->number_of_data_samples++;
     92     }
     93     // resetting the sample_counter.
     94     mocs->sample_counter = 0;
     95   }
     96 }
     97 
     98 // Runs the Sphere Fit.
     99 enum MagUpdate magCalSphereFit(struct MagCalSphere *mocs,
    100                                uint64_t sample_time_us) {
    101   // Setting up sphere fit data.
    102   struct SphereFitData data = {&mocs->sphere_data[0], NULL,
    103                                mocs->number_of_data_samples, mocs->moc.radius};
    104   float initial_bias[3] = {mocs->moc.x_bias, mocs->moc.y_bias,
    105                            mocs->moc.z_bias};
    106 
    107   // Setting initial bias values based on the KASA fit.
    108   sphereFitSetInitialBias(&mocs->sphere_fit.sphere_cal, initial_bias);
    109 
    110   // Running the sphere fit and checking if successful.
    111   if (sphereFitRunCal(&mocs->sphere_fit.sphere_cal, &data, sample_time_us)) {
    112     // Updating sphere parameters.
    113     sphereFitGetLatestCal(&mocs->sphere_fit.sphere_cal,
    114                           &mocs->sphere_fit.sphere_param);
    115 
    116     // Updating that a full sphere fit is available.
    117     return UPDATE_SPHERE_FIT;
    118   }
    119   return NO_UPDATE;
    120 }
    121 
    122 enum MagUpdate magCalSphereUpdate(struct MagCalSphere *mocs,
    123                                   uint64_t sample_time_us, float x, float y,
    124                                   float z) {
    125   enum MagUpdate new_cal = NO_UPDATE;
    126 
    127   // Saving data for sphere fit.
    128   magCalSphereDataUpdate(mocs, x, y, z);
    129 
    130   // Checking if KASA found a bias, if yes can run the sphere fit.
    131   if (UPDATE_BIAS == magCalUpdate(&mocs->moc, sample_time_us, x, y, z)) {
    132     // Running the sphere fit algo.
    133     new_cal = magCalSphereFit(mocs, sample_time_us);
    134 
    135     // Resetting.
    136     sphereFitReset(&mocs->sphere_fit.sphere_cal);
    137     magCalSphereReset(mocs);
    138 
    139     // If moc.kasa_batching is false, ran into a time out, hence the sphere
    140     // algo has to be reset as well.
    141   } else if (!mocs->moc.kasa_batching) {
    142     magCalSphereReset(mocs);
    143   }
    144 
    145   // Return which update has happened.
    146   return new_cal;
    147 }
    148