766 lines
29 KiB
C
766 lines
29 KiB
C
|
/*
|
||
|
* Copyright (C) 2016 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/accelerometer/accel_cal.h"
|
||
|
|
||
|
#include <inttypes.h>
|
||
|
#include <math.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#if defined(ACCEL_CAL_DBG_ENABLED) || defined(IMU_TEMP_DBG_ENABLED)
|
||
|
#include "calibration/util/cal_log.h"
|
||
|
#endif // ACCEL_CAL_DBG_ENABLED || IMU_TEMP_DBG_ENABLED
|
||
|
|
||
|
// clang-format off
|
||
|
#define KSCALE \
|
||
|
0.101936799f // Scaling from m/s^2 to g (0.101 = 1/(9.81 m/s^2)).
|
||
|
#define KSCALE2 9.81f // Scaling from g to m/s^2.
|
||
|
#define PHI 0.707f // = 1/sqrt(2) gives a 45 degree angle for sorting data.
|
||
|
#define PHIb -0.707f
|
||
|
#define PHIZ 0.866f // smaller Z sphere cap, opening angle is 30 degrees.
|
||
|
#define PHIZb -0.866f
|
||
|
#define G_NORM_MAX \
|
||
|
1.38f // Norm during stillness should be 1 g, checking from max min values.
|
||
|
#define G_NORM_MIN 0.68f
|
||
|
#define MAX_OFF 0.1f // Will not accept offsets that are larger than 100 mg.
|
||
|
#define MIN_TEMP 20.0f // No Data is collected below 20 degree C.
|
||
|
#define MAX_TEMP 45.0f // No Data is collected above 45 degree C.
|
||
|
#define TEMP_CUT \
|
||
|
((MAX_TEMP - MIN_TEMP) / \
|
||
|
ACCEL_CAL_NUM_TEMP_WINDOWS) // Separation window size for temperature buckets
|
||
|
// in degrees C.
|
||
|
#define EIGEN_RATIO 0.35f // EIGEN_RATIO (must be greater than 0.35).
|
||
|
#define EIGEN_MAG 0.97f // Eigen value magnitude (must be greater than 0.97).
|
||
|
#define ACCEL_NEW_BIAS_THRESHOLD (0.0f) // Bias update detection threshold.
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
#define TEMP_HIST_LOW \
|
||
|
16 // Putting all Temp counts in first bucket for temp < 16 degree C.
|
||
|
#define TEMP_HIST_HIGH \
|
||
|
62 // Putting all Temp counts in last bucket for temp > 62 degree C.
|
||
|
#define HIST_COUNT 9
|
||
|
#endif
|
||
|
#ifdef IMU_TEMP_DBG_ENABLED
|
||
|
#define IMU_TEMP_DELTA_TIME_NANOS \
|
||
|
5000000000 // Printing every 5 seconds IMU temp.
|
||
|
#endif
|
||
|
// clang-format on
|
||
|
|
||
|
/////////// Start Debug //////////////////////
|
||
|
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
// Total bucket Counter.
|
||
|
static void accelStatsCounter(struct AccelStillDet *asd,
|
||
|
struct AccelStatsMem *adf) {
|
||
|
// Sorting the data in the different buckets
|
||
|
// x bucket ntx.
|
||
|
if (PHI < asd->mean_x) {
|
||
|
adf->ntx += 1;
|
||
|
}
|
||
|
// Negative x bucket ntxb.
|
||
|
if (PHIb > asd->mean_x) {
|
||
|
adf->ntxb += 1;
|
||
|
}
|
||
|
// Y bucket nty.
|
||
|
if (PHI < asd->mean_y) {
|
||
|
adf->nty += 1;
|
||
|
}
|
||
|
// Negative y bucket ntyb.
|
||
|
if (PHIb > asd->mean_y) {
|
||
|
adf->ntyb += 1;
|
||
|
}
|
||
|
// Z bucket ntz.
|
||
|
if (PHIZ < asd->mean_z) {
|
||
|
adf->ntz += 1;
|
||
|
}
|
||
|
// Negative z bucket ntzb.
|
||
|
if (PHIZb > asd->mean_z) {
|
||
|
adf->ntzb += 1;
|
||
|
}
|
||
|
// The leftover bucket ntle.
|
||
|
if (PHI > asd->mean_x && PHIb < asd->mean_x && PHI > asd->mean_y &&
|
||
|
PHIb < asd->mean_y && PHIZ > asd->mean_z && PHIZb < asd->mean_z) {
|
||
|
adf->ntle += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Temp histogram generation.
|
||
|
static void accelTempHisto(struct AccelStatsMem *adf, float temp) {
|
||
|
int index = 0;
|
||
|
|
||
|
// Take temp at every stillness detection.
|
||
|
adf->start_time_nanos = 0;
|
||
|
if (temp <= TEMP_HIST_LOW) {
|
||
|
adf->t_hist[0] += 1;
|
||
|
return;
|
||
|
}
|
||
|
if (temp >= TEMP_HIST_HIGH) {
|
||
|
adf->t_hist[TEMP_HISTOGRAM - 1] += 1;
|
||
|
return;
|
||
|
}
|
||
|
index = (int)(((temp - TEMP_HIST_LOW) / 2) + 1);
|
||
|
adf->t_hist[index] += 1;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
///////// End Debug ////////////////////
|
||
|
|
||
|
// Stillness detector reset.
|
||
|
static void asdReset(struct AccelStillDet *asd) {
|
||
|
asd->nsamples = 0;
|
||
|
asd->start_time = 0;
|
||
|
asd->acc_x = asd->acc_y = asd->acc_z = 0.0f;
|
||
|
asd->acc_xx = asd->acc_yy = asd->acc_zz = 0.0f;
|
||
|
}
|
||
|
|
||
|
// Stillness detector init.
|
||
|
static void accelStillInit(struct AccelStillDet *asd, uint32_t t0, uint32_t n_s,
|
||
|
float th) {
|
||
|
memset(asd, 0, sizeof(struct AccelStillDet));
|
||
|
asd->var_th = th;
|
||
|
asd->min_batch_window = t0;
|
||
|
asd->max_batch_window = t0 + 100000000;
|
||
|
asd->min_batch_size = n_s;
|
||
|
asd->n_still = 0;
|
||
|
}
|
||
|
|
||
|
// Good data reset.
|
||
|
static void agdReset(struct AccelGoodData *agd) {
|
||
|
agd->nx = agd->nxb = 0;
|
||
|
agd->ny = agd->nyb = 0;
|
||
|
agd->nz = agd->nzb = 0;
|
||
|
agd->nle = 0;
|
||
|
agd->acc_t = agd->acc_tt = 0;
|
||
|
agd->e_x = agd->e_y = agd->e_z = 0;
|
||
|
}
|
||
|
|
||
|
// Good data init.
|
||
|
static void accelGoodDataInit(struct AccelGoodData *agd, uint32_t fx,
|
||
|
uint32_t fxb, uint32_t fy, uint32_t fyb,
|
||
|
uint32_t fz, uint32_t fzb, uint32_t fle) {
|
||
|
memset(agd, 0, sizeof(struct AccelGoodData));
|
||
|
agd->nfx = fx;
|
||
|
agd->nfxb = fxb;
|
||
|
agd->nfy = fy;
|
||
|
agd->nfyb = fyb;
|
||
|
agd->nfz = fz;
|
||
|
agd->nfzb = fzb;
|
||
|
agd->nfle = fle;
|
||
|
agd->var_t = 0;
|
||
|
agd->mean_t = 0;
|
||
|
}
|
||
|
|
||
|
// Accel cal algo init (ready for temp buckets).
|
||
|
static void accelCalAlgoInit(struct AccelCalAlgo *acc, uint32_t fx,
|
||
|
uint32_t fxb, uint32_t fy, uint32_t fyb,
|
||
|
uint32_t fz, uint32_t fzb, uint32_t fle) {
|
||
|
accelGoodDataInit(&acc->agd, fx, fxb, fy, fyb, fz, fzb, fle);
|
||
|
kasaInit(&acc->akf);
|
||
|
}
|
||
|
|
||
|
// Returns true when a new accel calibration is available.
|
||
|
bool accelCalNewBiasAvailable(struct AccelCal *acc) {
|
||
|
return fabsf(acc->x_bias - acc->x_bias_new) > ACCEL_NEW_BIAS_THRESHOLD ||
|
||
|
fabsf(acc->y_bias - acc->y_bias_new) > ACCEL_NEW_BIAS_THRESHOLD ||
|
||
|
fabsf(acc->z_bias - acc->z_bias_new) > ACCEL_NEW_BIAS_THRESHOLD;
|
||
|
}
|
||
|
|
||
|
// Accel cal init.
|
||
|
void accelCalInit(struct AccelCal *acc,
|
||
|
const struct AccelCalParameters *parameters) {
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < ACCEL_CAL_NUM_TEMP_WINDOWS; ++i) {
|
||
|
// Init core accel data.
|
||
|
accelCalAlgoInit(&acc->ac1[i], parameters->fx, parameters->fxb,
|
||
|
parameters->fy, parameters->fyb, parameters->fz,
|
||
|
parameters->fzb, parameters->fle);
|
||
|
}
|
||
|
|
||
|
// Stillness Reset.
|
||
|
accelStillInit(&acc->asd, parameters->t0, parameters->n_s, parameters->th);
|
||
|
|
||
|
// Debug data init.
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
memset(&acc->adf, 0, sizeof(struct AccelStatsMem));
|
||
|
#endif
|
||
|
|
||
|
acc->x_bias = acc->y_bias = acc->z_bias = 0;
|
||
|
acc->x_bias_new = acc->y_bias_new = acc->z_bias_new = 0;
|
||
|
acc->average_temperature_celsius = 0;
|
||
|
|
||
|
#ifdef IMU_TEMP_DBG_ENABLED
|
||
|
acc->temp_time_nanos = 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// Stillness time check.
|
||
|
static int stillnessBatchComplete(struct AccelStillDet *asd,
|
||
|
uint64_t sample_time_nanos) {
|
||
|
int complete = 0;
|
||
|
|
||
|
// Checking if enough data is accumulated to calc Mean and Var.
|
||
|
if ((sample_time_nanos - asd->start_time > asd->min_batch_window) &&
|
||
|
(asd->nsamples > asd->min_batch_size)) {
|
||
|
if (sample_time_nanos - asd->start_time < asd->max_batch_window) {
|
||
|
complete = 1;
|
||
|
} else {
|
||
|
// Checking for too long batch window, if yes reset and start over.
|
||
|
asdReset(asd);
|
||
|
return complete;
|
||
|
}
|
||
|
} else if (sample_time_nanos - asd->start_time > asd->min_batch_window &&
|
||
|
(asd->nsamples < asd->min_batch_size)) {
|
||
|
// Not enough samples collected in max_batch_window during sample window.
|
||
|
asdReset(asd);
|
||
|
}
|
||
|
return complete;
|
||
|
}
|
||
|
|
||
|
// Releasing Memory.
|
||
|
void accelCalDestroy(struct AccelCal *acc) { (void)acc; }
|
||
|
|
||
|
// Stillness Detection.
|
||
|
static int accelStillnessDetection(struct AccelStillDet *asd,
|
||
|
uint64_t sample_time_nanos, float x, float y,
|
||
|
float z) {
|
||
|
float inv = 0.0f;
|
||
|
int complete = 0.0f;
|
||
|
float g_norm = 0.0f;
|
||
|
|
||
|
// Accumulate for mean and VAR.
|
||
|
asd->acc_x += x;
|
||
|
asd->acc_xx += x * x;
|
||
|
asd->acc_y += y;
|
||
|
asd->acc_yy += y * y;
|
||
|
asd->acc_z += z;
|
||
|
asd->acc_zz += z * z;
|
||
|
|
||
|
// Setting a new start time and wait until T0 is reached.
|
||
|
if (++asd->nsamples == 1) {
|
||
|
asd->start_time = sample_time_nanos;
|
||
|
}
|
||
|
if (stillnessBatchComplete(asd, sample_time_nanos)) {
|
||
|
// Getting 1/#samples and checking asd->nsamples != 0.
|
||
|
if (0 < asd->nsamples) {
|
||
|
inv = 1.0f / asd->nsamples;
|
||
|
} else {
|
||
|
// Something went wrong resetting and start over.
|
||
|
asdReset(asd);
|
||
|
return complete;
|
||
|
}
|
||
|
// Calculating the VAR = sum(x^2)/n - sum(x)^2/n^2.
|
||
|
asd->var_x = (asd->acc_xx - (asd->acc_x * asd->acc_x) * inv) * inv;
|
||
|
asd->var_y = (asd->acc_yy - (asd->acc_y * asd->acc_y) * inv) * inv;
|
||
|
asd->var_z = (asd->acc_zz - (asd->acc_z * asd->acc_z) * inv) * inv;
|
||
|
// Checking if sensor is still.
|
||
|
if (asd->var_x < asd->var_th && asd->var_y < asd->var_th &&
|
||
|
asd->var_z < asd->var_th) {
|
||
|
// Calcluating the MEAN = sum(x) / n.
|
||
|
asd->mean_x = asd->acc_x * inv;
|
||
|
asd->mean_y = asd->acc_y * inv;
|
||
|
asd->mean_z = asd->acc_z * inv;
|
||
|
// Calculating g_norm^2.
|
||
|
g_norm = asd->mean_x * asd->mean_x + asd->mean_y * asd->mean_y +
|
||
|
asd->mean_z * asd->mean_z;
|
||
|
// Magnitude check, still passsing when we have worse case offset.
|
||
|
if (g_norm < G_NORM_MAX && g_norm > G_NORM_MIN) {
|
||
|
complete = 1;
|
||
|
asd->n_still += 1;
|
||
|
}
|
||
|
}
|
||
|
asdReset(asd);
|
||
|
}
|
||
|
return complete;
|
||
|
}
|
||
|
|
||
|
// Good data detection, sorting and accumulate the data for Kasa.
|
||
|
static int accelGoodData(struct AccelStillDet *asd, struct AccelCalAlgo *ac1,
|
||
|
float temp) {
|
||
|
int complete = 0;
|
||
|
float inv = 0.0f;
|
||
|
|
||
|
// Sorting the data in the different buckets and accum
|
||
|
// x bucket nx.
|
||
|
if (PHI < asd->mean_x && ac1->agd.nx < ac1->agd.nfx) {
|
||
|
ac1->agd.nx += 1;
|
||
|
ac1->agd.acc_t += temp;
|
||
|
ac1->agd.acc_tt += temp * temp;
|
||
|
kasaAccumulate(&ac1->akf, asd->mean_x, asd->mean_y, asd->mean_z);
|
||
|
}
|
||
|
// Negative x bucket nxb.
|
||
|
if (PHIb > asd->mean_x && ac1->agd.nxb < ac1->agd.nfxb) {
|
||
|
ac1->agd.nxb += 1;
|
||
|
ac1->agd.acc_t += temp;
|
||
|
ac1->agd.acc_tt += temp * temp;
|
||
|
kasaAccumulate(&ac1->akf, asd->mean_x, asd->mean_y, asd->mean_z);
|
||
|
}
|
||
|
// Y bucket ny.
|
||
|
if (PHI < asd->mean_y && ac1->agd.ny < ac1->agd.nfy) {
|
||
|
ac1->agd.ny += 1;
|
||
|
ac1->agd.acc_t += temp;
|
||
|
ac1->agd.acc_tt += temp * temp;
|
||
|
kasaAccumulate(&ac1->akf, asd->mean_x, asd->mean_y, asd->mean_z);
|
||
|
}
|
||
|
// Negative y bucket nyb.
|
||
|
if (PHIb > asd->mean_y && ac1->agd.nyb < ac1->agd.nfyb) {
|
||
|
ac1->agd.nyb += 1;
|
||
|
ac1->agd.acc_t += temp;
|
||
|
ac1->agd.acc_tt += temp * temp;
|
||
|
kasaAccumulate(&ac1->akf, asd->mean_x, asd->mean_y, asd->mean_z);
|
||
|
}
|
||
|
// Z bucket nz.
|
||
|
if (PHIZ < asd->mean_z && ac1->agd.nz < ac1->agd.nfz) {
|
||
|
ac1->agd.nz += 1;
|
||
|
ac1->agd.acc_t += temp;
|
||
|
ac1->agd.acc_tt += temp * temp;
|
||
|
kasaAccumulate(&ac1->akf, asd->mean_x, asd->mean_y, asd->mean_z);
|
||
|
}
|
||
|
// Negative z bucket nzb.
|
||
|
if (PHIZb > asd->mean_z && ac1->agd.nzb < ac1->agd.nfzb) {
|
||
|
ac1->agd.nzb += 1;
|
||
|
ac1->agd.acc_t += temp;
|
||
|
ac1->agd.acc_tt += temp * temp;
|
||
|
kasaAccumulate(&ac1->akf, asd->mean_x, asd->mean_y, asd->mean_z);
|
||
|
}
|
||
|
// The leftover bucket nle.
|
||
|
if (PHI > asd->mean_x && PHIb < asd->mean_x && PHI > asd->mean_y &&
|
||
|
PHIb < asd->mean_y && PHIZ > asd->mean_z && PHIZb < asd->mean_z &&
|
||
|
ac1->agd.nle < ac1->agd.nfle) {
|
||
|
ac1->agd.nle += 1;
|
||
|
ac1->agd.acc_t += temp;
|
||
|
ac1->agd.acc_tt += temp * temp;
|
||
|
kasaAccumulate(&ac1->akf, asd->mean_x, asd->mean_y, asd->mean_z);
|
||
|
}
|
||
|
// Checking if all buckets are full.
|
||
|
if (ac1->agd.nx == ac1->agd.nfx && ac1->agd.nxb == ac1->agd.nfxb &&
|
||
|
ac1->agd.ny == ac1->agd.nfy && ac1->agd.nyb == ac1->agd.nfyb &&
|
||
|
ac1->agd.nz == ac1->agd.nfz && ac1->agd.nzb == ac1->agd.nfzb) {
|
||
|
// Check if akf->nsamples is zero.
|
||
|
if (ac1->akf.nsamples == 0) {
|
||
|
agdReset(&ac1->agd);
|
||
|
kasaReset(&ac1->akf);
|
||
|
complete = 0;
|
||
|
return complete;
|
||
|
}
|
||
|
|
||
|
// Normalize the data to the sample numbers.
|
||
|
kasaNormalize(&ac1->akf);
|
||
|
|
||
|
// Calculate the temp VAR and MEAN.
|
||
|
inv = 1.0f / ac1->akf.nsamples;
|
||
|
ac1->agd.var_t =
|
||
|
(ac1->agd.acc_tt - (ac1->agd.acc_t * ac1->agd.acc_t) * inv) * inv;
|
||
|
ac1->agd.mean_t = ac1->agd.acc_t * inv;
|
||
|
complete = 1;
|
||
|
}
|
||
|
|
||
|
// If any of the buckets has a bigger number as specified, reset and start
|
||
|
// over.
|
||
|
if (ac1->agd.nx > ac1->agd.nfx || ac1->agd.nxb > ac1->agd.nfxb ||
|
||
|
ac1->agd.ny > ac1->agd.nfy || ac1->agd.nyb > ac1->agd.nfyb ||
|
||
|
ac1->agd.nz > ac1->agd.nfz || ac1->agd.nzb > ac1->agd.nfzb) {
|
||
|
agdReset(&ac1->agd);
|
||
|
kasaReset(&ac1->akf);
|
||
|
complete = 0;
|
||
|
return complete;
|
||
|
}
|
||
|
return complete;
|
||
|
}
|
||
|
|
||
|
// Eigen value magnitude and ratio test.
|
||
|
static int accEigenTest(struct KasaFit *akf, struct AccelGoodData *agd) {
|
||
|
// covariance matrix.
|
||
|
struct Mat33 S;
|
||
|
S.elem[0][0] = akf->acc_xx - akf->acc_x * akf->acc_x;
|
||
|
S.elem[0][1] = S.elem[1][0] = akf->acc_xy - akf->acc_x * akf->acc_y;
|
||
|
S.elem[0][2] = S.elem[2][0] = akf->acc_xz - akf->acc_x * akf->acc_z;
|
||
|
S.elem[1][1] = akf->acc_yy - akf->acc_y * akf->acc_y;
|
||
|
S.elem[1][2] = S.elem[2][1] = akf->acc_yz - akf->acc_y * akf->acc_z;
|
||
|
S.elem[2][2] = akf->acc_zz - akf->acc_z * akf->acc_z;
|
||
|
|
||
|
struct Vec3 eigenvals;
|
||
|
struct Mat33 eigenvecs;
|
||
|
mat33GetEigenbasis(&S, &eigenvals, &eigenvecs);
|
||
|
|
||
|
float evmax = (eigenvals.x > eigenvals.y) ? eigenvals.x : eigenvals.y;
|
||
|
evmax = (eigenvals.z > evmax) ? eigenvals.z : evmax;
|
||
|
|
||
|
float evmin = (eigenvals.x < eigenvals.y) ? eigenvals.x : eigenvals.y;
|
||
|
evmin = (eigenvals.z < evmin) ? eigenvals.z : evmin;
|
||
|
|
||
|
float eigenvals_sum = eigenvals.x + eigenvals.y + eigenvals.z;
|
||
|
|
||
|
// Testing for negative number.
|
||
|
float evmag = (eigenvals_sum > 0) ? sqrtf(eigenvals_sum) : 0;
|
||
|
|
||
|
// Passing when evmin/evmax> EIGEN_RATIO.
|
||
|
int eigen_pass = (evmin > evmax * EIGEN_RATIO) && (evmag > EIGEN_MAG);
|
||
|
|
||
|
agd->e_x = eigenvals.x;
|
||
|
agd->e_y = eigenvals.y;
|
||
|
agd->e_z = eigenvals.z;
|
||
|
|
||
|
return eigen_pass;
|
||
|
}
|
||
|
|
||
|
// Updating the new bias and save to pointers. Return true if the bias changed.
|
||
|
bool accelCalUpdateBias(struct AccelCal *acc, float *x, float *y, float *z) {
|
||
|
*x = acc->x_bias_new;
|
||
|
*y = acc->y_bias_new;
|
||
|
*z = acc->z_bias_new;
|
||
|
|
||
|
// Check to see if the bias changed since last call to accelCalUpdateBias.
|
||
|
// Compiler does not allow us to use "==" and "!=" when comparing floats, so
|
||
|
// just use "<" and ">".
|
||
|
if ((acc->x_bias < acc->x_bias_new) || (acc->x_bias > acc->x_bias_new) ||
|
||
|
(acc->y_bias < acc->y_bias_new) || (acc->y_bias > acc->y_bias_new) ||
|
||
|
(acc->z_bias < acc->z_bias_new) || (acc->z_bias > acc->z_bias_new)) {
|
||
|
acc->x_bias = acc->x_bias_new;
|
||
|
acc->y_bias = acc->y_bias_new;
|
||
|
acc->z_bias = acc->z_bias_new;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Set the (initial) bias.
|
||
|
void accelCalBiasSet(struct AccelCal *acc, float x, float y, float z) {
|
||
|
acc->x_bias = acc->x_bias_new = x;
|
||
|
acc->y_bias = acc->y_bias_new = y;
|
||
|
acc->z_bias = acc->z_bias_new = z;
|
||
|
}
|
||
|
|
||
|
// Removing the bias.
|
||
|
void accelCalBiasRemove(struct AccelCal *acc, float *x, float *y, float *z) {
|
||
|
*x = *x - acc->x_bias;
|
||
|
*y = *y - acc->y_bias;
|
||
|
*z = *z - acc->z_bias;
|
||
|
}
|
||
|
|
||
|
// Accel Cal Runner.
|
||
|
void accelCalRun(struct AccelCal *acc, uint64_t sample_time_nanos, float x,
|
||
|
float y, float z, float temp) {
|
||
|
// Scaling to 1g, better for the algorithm.
|
||
|
x *= KSCALE;
|
||
|
y *= KSCALE;
|
||
|
z *= KSCALE;
|
||
|
|
||
|
// DBG: IMU temp messages every 5s.
|
||
|
#ifdef IMU_TEMP_DBG_ENABLED
|
||
|
if ((sample_time_nanos - acc->temp_time_nanos) > IMU_TEMP_DELTA_TIME_NANOS) {
|
||
|
CAL_DEBUG_LOG("IMU Temp Data: ",
|
||
|
", " CAL_FORMAT_3DIGITS ", %" PRIu64
|
||
|
", " CAL_FORMAT_6DIGITS_TRIPLET " \n",
|
||
|
CAL_ENCODE_FLOAT(temp, 3),
|
||
|
sample_time_nanos,
|
||
|
CAL_ENCODE_FLOAT(acc->x_bias_new, 6),
|
||
|
CAL_ENCODE_FLOAT(acc->y_bias_new, 6),
|
||
|
CAL_ENCODE_FLOAT(acc->z_bias_new, 6));
|
||
|
acc->temp_time_nanos = sample_time_nanos;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int temp_gate = 0;
|
||
|
|
||
|
// Temp GATE.
|
||
|
if (temp < MAX_TEMP && temp > MIN_TEMP) {
|
||
|
// Checking if accel is still.
|
||
|
if (accelStillnessDetection(&acc->asd, sample_time_nanos, x, y, z)) {
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
// Creating temp hist data.
|
||
|
accelTempHisto(&acc->adf, temp);
|
||
|
#endif
|
||
|
|
||
|
temp_gate = (int) ((temp - MIN_TEMP) / TEMP_CUT);
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
accelStatsCounter(&acc->asd, &acc->adf);
|
||
|
#endif
|
||
|
// If still -> pass the averaged accel data (mean) to the
|
||
|
// sorting, counting and accum function.
|
||
|
if (accelGoodData(&acc->asd, &acc->ac1[temp_gate], temp)) {
|
||
|
// Running the Kasa fit.
|
||
|
struct Vec3 bias;
|
||
|
float radius;
|
||
|
|
||
|
// Grabbing the fit from the MAG cal.
|
||
|
kasaFit(&acc->ac1[temp_gate].akf, &bias, &radius, G_NORM_MAX,
|
||
|
G_NORM_MIN);
|
||
|
|
||
|
// If offset is too large don't take.
|
||
|
if (fabsf(bias.x) < MAX_OFF && fabsf(bias.y) < MAX_OFF &&
|
||
|
fabsf(bias.z) < MAX_OFF) {
|
||
|
// Eigen Ratio Test.
|
||
|
if (accEigenTest(&acc->ac1[temp_gate].akf,
|
||
|
&acc->ac1[temp_gate].agd)) {
|
||
|
// Storing the new offsets and average temperature.
|
||
|
acc->x_bias_new = bias.x * KSCALE2;
|
||
|
acc->y_bias_new = bias.y * KSCALE2;
|
||
|
acc->z_bias_new = bias.z * KSCALE2;
|
||
|
acc->average_temperature_celsius = acc->ac1[temp_gate].agd.mean_t;
|
||
|
}
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
//// Debug ///////
|
||
|
acc->adf.noff += 1;
|
||
|
// Resetting the counter for the offset history.
|
||
|
if (acc->adf.n_o > HIST_COUNT) {
|
||
|
acc->adf.n_o = 0;
|
||
|
}
|
||
|
|
||
|
// Storing the Debug data.
|
||
|
acc->adf.x_o[acc->adf.n_o] = bias.x;
|
||
|
acc->adf.y_o[acc->adf.n_o] = bias.y;
|
||
|
acc->adf.z_o[acc->adf.n_o] = bias.z;
|
||
|
acc->adf.e_x[acc->adf.n_o] = acc->ac1[temp_gate].agd.e_x;
|
||
|
acc->adf.e_y[acc->adf.n_o] = acc->ac1[temp_gate].agd.e_y;
|
||
|
acc->adf.e_z[acc->adf.n_o] = acc->ac1[temp_gate].agd.e_z;
|
||
|
acc->adf.var_t[acc->adf.n_o] = acc->ac1[temp_gate].agd.var_t;
|
||
|
acc->adf.mean_t[acc->adf.n_o] = acc->ac1[temp_gate].agd.mean_t;
|
||
|
acc->adf.cal_time[acc->adf.n_o] = sample_time_nanos;
|
||
|
acc->adf.rad[acc->adf.n_o] = radius;
|
||
|
acc->adf.n_o += 1;
|
||
|
#endif
|
||
|
} else {
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
acc->adf.noff_max += 1;
|
||
|
#endif
|
||
|
}
|
||
|
///////////////
|
||
|
|
||
|
// Resetting the structs for a new accel cal run.
|
||
|
agdReset(&acc->ac1[temp_gate].agd);
|
||
|
kasaReset(&acc->ac1[temp_gate].akf);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef ACCEL_CAL_DBG_ENABLED
|
||
|
|
||
|
// Local helper macro for printing log messages.
|
||
|
#ifdef CAL_NO_FLOAT_FORMAT_STRINGS
|
||
|
#define CAL_FORMAT_ACCEL_HISTORY \
|
||
|
"%s%d.%06d,%s%d.%06d,%s%d.%06d,%s%d.%06d,%s%d.%06d,%s%d.%06d,%s%d.%06d," \
|
||
|
"%s%d.%06d,%s%d.%06d,%s%d.%06d"
|
||
|
#else
|
||
|
#define CAL_FORMAT_ACCEL_HISTORY \
|
||
|
"%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f"
|
||
|
#endif // CAL_NO_FLOAT_FORMAT_STRINGS
|
||
|
|
||
|
// Debug Print Output
|
||
|
void accelCalDebPrint(struct AccelCal *acc, float temp) {
|
||
|
static int32_t kk = 0;
|
||
|
if (++kk == 1000) {
|
||
|
// X offset history last 10 values.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{11," CAL_FORMAT_ACCEL_HISTORY "}(x_off history)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.x_o[9], 6));
|
||
|
|
||
|
// Y offset history last 10 values.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{12," CAL_FORMAT_ACCEL_HISTORY "}(y_off history)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.y_o[9], 6));
|
||
|
|
||
|
// Z offset history last 10 values.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{13," CAL_FORMAT_ACCEL_HISTORY "}(z_off history)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.z_o[9], 6));
|
||
|
|
||
|
// Temp history variation VAR of offset.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{14," CAL_FORMAT_ACCEL_HISTORY "}(VAR temp history)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.var_t[9], 6));
|
||
|
|
||
|
// Temp mean history of offset.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{15," CAL_FORMAT_ACCEL_HISTORY "}(MEAN Temp history)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.mean_t[9], 6));
|
||
|
|
||
|
// KASA radius history.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]", "{16," CAL_FORMAT_ACCEL_HISTORY "}(radius)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.rad[9], 6));
|
||
|
kk = 0;
|
||
|
}
|
||
|
|
||
|
if (kk == 750) {
|
||
|
// Eigen Vector X.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]", "{ 7," CAL_FORMAT_ACCEL_HISTORY "}(eigen x)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_x[9], 6));
|
||
|
// Y.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]", "{ 8," CAL_FORMAT_ACCEL_HISTORY "}(eigen y)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_y[9], 6));
|
||
|
// Z.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]", "{ 9," CAL_FORMAT_ACCEL_HISTORY "}(eigen z)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[0], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[1], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[2], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[3], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[4], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[5], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[6], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[7], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[8], 6),
|
||
|
CAL_ENCODE_FLOAT(acc->adf.e_z[9], 6));
|
||
|
// Accel Time in ns.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{10,%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
|
||
|
",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
|
||
|
"}(timestamp ns)\n",
|
||
|
acc->adf.cal_time[0], acc->adf.cal_time[1],
|
||
|
acc->adf.cal_time[2], acc->adf.cal_time[3],
|
||
|
acc->adf.cal_time[4], acc->adf.cal_time[5],
|
||
|
acc->adf.cal_time[6], acc->adf.cal_time[7],
|
||
|
acc->adf.cal_time[8], acc->adf.cal_time[9]);
|
||
|
}
|
||
|
|
||
|
if (kk == 500) {
|
||
|
// Total bucket count.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{ 0,%2d, %2d, %2d, %2d, %2d, %2d, %2d}(Total Bucket #)\n",
|
||
|
(unsigned)acc->adf.ntx, (unsigned)acc->adf.ntxb,
|
||
|
(unsigned)acc->adf.nty, (unsigned)acc->adf.ntyb,
|
||
|
(unsigned)acc->adf.ntz, (unsigned)acc->adf.ntzb,
|
||
|
(unsigned)acc->adf.ntle);
|
||
|
// Live bucket count lower.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{ 1,%2d, %2d, %2d, %2d, %2d, %2d, %2d, %3d}(Bucket # "
|
||
|
"lower)\n",
|
||
|
(unsigned)acc->ac1[0].agd.nx, (unsigned)acc->ac1[0].agd.nxb,
|
||
|
(unsigned)acc->ac1[0].agd.ny, (unsigned)acc->ac1[0].agd.nyb,
|
||
|
(unsigned)acc->ac1[0].agd.nz, (unsigned)acc->ac1[0].agd.nzb,
|
||
|
(unsigned)acc->ac1[0].agd.nle,
|
||
|
(unsigned)acc->ac1[0].akf.nsamples);
|
||
|
// Live bucket count hogher.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{ 2,%2d, %2d, %2d, %2d, %2d, %2d, %2d, %3d}(Bucket # "
|
||
|
"higher)\n",
|
||
|
(unsigned)acc->ac1[1].agd.nx, (unsigned)acc->ac1[1].agd.nxb,
|
||
|
(unsigned)acc->ac1[1].agd.ny, (unsigned)acc->ac1[1].agd.nyb,
|
||
|
(unsigned)acc->ac1[1].agd.nz, (unsigned)acc->ac1[1].agd.nzb,
|
||
|
(unsigned)acc->ac1[1].agd.nle,
|
||
|
(unsigned)acc->ac1[1].akf.nsamples);
|
||
|
// Offset used.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{ 3,"CAL_FORMAT_6DIGITS_TRIPLET", %2d}(updated offset "
|
||
|
"x,y,z, total # of offsets)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->x_bias, 6),
|
||
|
CAL_ENCODE_FLOAT(acc->y_bias, 6),
|
||
|
CAL_ENCODE_FLOAT(acc->z_bias, 6), (unsigned)acc->adf.noff);
|
||
|
// Offset New.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{ 4," CAL_FORMAT_6DIGITS_TRIPLET ", " CAL_FORMAT_6DIGITS
|
||
|
"}(New offset x,y,z, live temp)\n",
|
||
|
CAL_ENCODE_FLOAT(acc->x_bias_new, 6),
|
||
|
CAL_ENCODE_FLOAT(acc->y_bias_new, 6),
|
||
|
CAL_ENCODE_FLOAT(acc->z_bias_new, 6),
|
||
|
CAL_ENCODE_FLOAT(temp, 6));
|
||
|
// Temp Histogram.
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{ 5,%7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, "
|
||
|
"%7d, %7d}(temp histo)\n",
|
||
|
(unsigned)acc->adf.t_hist[0], (unsigned)acc->adf.t_hist[1],
|
||
|
(unsigned)acc->adf.t_hist[2], (unsigned)acc->adf.t_hist[3],
|
||
|
(unsigned)acc->adf.t_hist[4], (unsigned)acc->adf.t_hist[5],
|
||
|
(unsigned)acc->adf.t_hist[6], (unsigned)acc->adf.t_hist[7],
|
||
|
(unsigned)acc->adf.t_hist[8], (unsigned)acc->adf.t_hist[9],
|
||
|
(unsigned)acc->adf.t_hist[10], (unsigned)acc->adf.t_hist[11],
|
||
|
(unsigned)acc->adf.t_hist[12]);
|
||
|
CAL_DEBUG_LOG("[ACCEL_CAL]",
|
||
|
"{ 6,%7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, %7d, "
|
||
|
"%7d}(temp histo)\n",
|
||
|
(unsigned)acc->adf.t_hist[13], (unsigned)acc->adf.t_hist[14],
|
||
|
(unsigned)acc->adf.t_hist[15], (unsigned)acc->adf.t_hist[16],
|
||
|
(unsigned)acc->adf.t_hist[17], (unsigned)acc->adf.t_hist[18],
|
||
|
(unsigned)acc->adf.t_hist[19], (unsigned)acc->adf.t_hist[20],
|
||
|
(unsigned)acc->adf.t_hist[21], (unsigned)acc->adf.t_hist[22],
|
||
|
(unsigned)acc->adf.t_hist[23], (unsigned)acc->adf.t_hist[24]);
|
||
|
}
|
||
|
}
|
||
|
#endif
|