Home | History | Annotate | Download | only in algos
      1 /*
      2  * Copyright (C) 2016 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 // adapted from frameworks/native/services/sensorservice/Fusion.cpp
     18 
     19 #include <algos/fusion.h>
     20 #include <algos/mat.h>
     21 
     22 #include <errno.h>
     23 #include <nanohub_math.h>
     24 #include <stdio.h>
     25 
     26 #include <seos.h>
     27 
     28 #ifdef DEBUG_CH
     29 // change to 0 to disable fusion debugging output
     30 #define DEBUG_FUSION  0
     31 #endif
     32 
     33 #define ACC     1
     34 #define MAG     2
     35 #define GYRO    4
     36 
     37 #define DEFAULT_GYRO_VAR         1e-7f
     38 #define DEFAULT_GYRO_BIAS_VAR    1e-12f
     39 #define DEFAULT_ACC_STDEV        5e-2f
     40 #define DEFAULT_MAG_STDEV        5e-1f
     41 
     42 #define GEOMAG_GYRO_VAR          2e-4f
     43 #define GEOMAG_GYRO_BIAS_VAR     1e-4f
     44 #define GEOMAG_ACC_STDEV         0.02f
     45 #define GEOMAG_MAG_STDEV         0.02f
     46 
     47 #define SYMMETRY_TOLERANCE       1e-10f
     48 #define FAKE_MAG_INTERVAL        1.0f  //sec
     49 
     50 #define NOMINAL_GRAVITY          9.81f
     51 #define FREE_FALL_THRESHOLD      (0.1f * NOMINAL_GRAVITY)
     52 #define FREE_FALL_THRESHOLD_SQ   (FREE_FALL_THRESHOLD * FREE_FALL_THRESHOLD)
     53 
     54 #define MAX_VALID_MAGNETIC_FIELD    75.0f
     55 #define MAX_VALID_MAGNETIC_FIELD_SQ (MAX_VALID_MAGNETIC_FIELD * MAX_VALID_MAGNETIC_FIELD)
     56 
     57 #define MIN_VALID_MAGNETIC_FIELD    20.0f   //norminal mag field strength is 25uT in some area
     58 #define MIN_VALID_MAGNETIC_FIELD_SQ (MIN_VALID_MAGNETIC_FIELD * MIN_VALID_MAGNETIC_FIELD)
     59 
     60 #define MIN_VALID_CROSS_PRODUCT_MAG     1.0e-3
     61 #define MIN_VALID_CROSS_PRODUCT_MAG_SQ  (MIN_VALID_CROSS_PRODUCT_MAG * MIN_VALID_CROSS_PRODUCT_MAG)
     62 
     63 #define DELTA_TIME_MARGIN 1.0e-9f
     64 
     65 #define TRUST_DURATION_MANUAL_MAG_CAL      5.f  //unit: seconds
     66 
     67 void initFusion(struct Fusion *fusion, uint32_t flags) {
     68     fusion->flags = flags;
     69 
     70     if (flags & FUSION_USE_GYRO) {
     71         // normal fusion mode
     72         fusion->param.gyro_var = DEFAULT_GYRO_VAR;
     73         fusion->param.gyro_bias_var = DEFAULT_GYRO_BIAS_VAR;
     74         fusion->param.acc_stdev = DEFAULT_ACC_STDEV;
     75         fusion->param.mag_stdev = DEFAULT_MAG_STDEV;
     76     } else {
     77         // geo mag mode
     78         fusion->param.gyro_var = GEOMAG_GYRO_VAR;
     79         fusion->param.gyro_bias_var = GEOMAG_GYRO_BIAS_VAR;
     80         fusion->param.acc_stdev = GEOMAG_ACC_STDEV;
     81         fusion->param.mag_stdev = GEOMAG_MAG_STDEV;
     82     }
     83 
     84     if (flags & FUSION_REINITIALIZE)
     85     {
     86         initVec3(&fusion->Ba, 0.0f, 0.0f, 1.0f);
     87         initVec3(&fusion->Bm, 0.0f, 1.0f, 0.0f);
     88 
     89         initVec4(&fusion->x0, 0.0f, 0.0f, 0.0f, 0.0f);
     90         initVec3(&fusion->x1, 0.0f, 0.0f, 0.0f);
     91 
     92         fusion->mInitState = 0;
     93 
     94         fusion->mPredictDt = 0.0f;
     95         fusion->mCount[0] = fusion->mCount[1] = fusion->mCount[2] = 0;
     96 
     97         initVec3(&fusion->mData[0], 0.0f, 0.0f, 0.0f);
     98         initVec3(&fusion->mData[1], 0.0f, 0.0f, 0.0f);
     99         initVec3(&fusion->mData[2], 0.0f, 0.0f, 0.0f);
    100 
    101     } else  {
    102         // mask off disabled sensor bit
    103         fusion->mInitState &= (ACC
    104                                | ((fusion->flags & FUSION_USE_MAG) ? MAG : 0)
    105                                | ((fusion->flags & FUSION_USE_GYRO) ? GYRO : 0));
    106     }
    107 
    108     fusionSetMagTrust(fusion, NORMAL);
    109     fusion->lastMagInvalid = false;
    110 }
    111 
    112 int fusionHasEstimate(const struct Fusion *fusion) {
    113     // waive sensor init depends on the mode
    114     return fusion->mInitState == (ACC
    115                                   | ((fusion->flags & FUSION_USE_MAG) ? MAG : 0)
    116                                   | ((fusion->flags & FUSION_USE_GYRO) ? GYRO : 0));
    117 }
    118 
    119 static void updateDt(struct Fusion *fusion, float dT) {
    120     if (fabsf(fusion->mPredictDt - dT) > DELTA_TIME_MARGIN) {
    121         float dT2 = dT * dT;
    122         float dT3 = dT2 * dT;
    123 
    124         float q00 = fusion->param.gyro_var * dT +
    125                     0.33333f * fusion->param.gyro_bias_var * dT3;
    126         float q11 = fusion->param.gyro_bias_var * dT;
    127         float q10 = 0.5f * fusion->param.gyro_bias_var * dT2;
    128         float q01 = q10;
    129 
    130         initDiagonalMatrix(&fusion->GQGt[0][0], q00);
    131         initDiagonalMatrix(&fusion->GQGt[0][1], -q10);
    132         initDiagonalMatrix(&fusion->GQGt[1][0], -q01);
    133         initDiagonalMatrix(&fusion->GQGt[1][1], q11);
    134         fusion->mPredictDt = dT;
    135     }
    136 }
    137 
    138 static int fusion_init_complete(struct Fusion *fusion, int what, const struct Vec3 *d, float dT) {
    139     if (fusionHasEstimate(fusion)) {
    140         return 1;
    141     }
    142 
    143     switch (what) {
    144         case ACC:
    145         {
    146             if (!(fusion->flags & FUSION_USE_GYRO)) {
    147                 updateDt(fusion, dT);
    148             }
    149             struct Vec3 unityD = *d;
    150             vec3Normalize(&unityD);
    151 
    152             vec3Add(&fusion->mData[0], &unityD);
    153             ++fusion->mCount[0];
    154 
    155             if (fusion->mCount[0] == 8) {
    156                 fusion->mInitState |= ACC;
    157             }
    158             break;
    159         }
    160 
    161         case MAG:
    162         {
    163             struct Vec3 unityD = *d;
    164             vec3Normalize(&unityD);
    165 
    166             vec3Add(&fusion->mData[1], &unityD);
    167             ++fusion->mCount[1];
    168 
    169             fusion->mInitState |= MAG;
    170             break;
    171         }
    172 
    173         case GYRO:
    174         {
    175             updateDt(fusion, dT);
    176 
    177             struct Vec3 scaledD = *d;
    178             vec3ScalarMul(&scaledD, dT);
    179 
    180             vec3Add(&fusion->mData[2], &scaledD);
    181             ++fusion->mCount[2];
    182 
    183             fusion->mInitState |= GYRO;
    184             break;
    185         }
    186 
    187         default:
    188             // assert(!"should not be here");
    189             break;
    190     }
    191 
    192     if (fusionHasEstimate(fusion)) {
    193         vec3ScalarMul(&fusion->mData[0], 1.0f / fusion->mCount[0]);
    194 
    195         if (fusion->flags & FUSION_USE_MAG) {
    196             vec3ScalarMul(&fusion->mData[1], 1.0f / fusion->mCount[1]);
    197         } else {
    198             fusion->fake_mag_decimation = 0.f;
    199         }
    200 
    201         struct Vec3 up = fusion->mData[0];
    202 
    203         struct Vec3 east;
    204         if (fusion->flags & FUSION_USE_MAG) {
    205             vec3Cross(&east, &fusion->mData[1], &up);
    206             vec3Normalize(&east);
    207         } else {
    208             findOrthogonalVector(up.x, up.y, up.z, &east.x, &east.y, &east.z);
    209         }
    210 
    211         struct Vec3 north;
    212         vec3Cross(&north, &up, &east);
    213 
    214         struct Mat33 R;
    215         initMatrixColumns(&R, &east, &north, &up);
    216 
    217         //Quat q;
    218         //initQuat(&q, &R);
    219 
    220         initQuat(&fusion->x0, &R);
    221         initVec3(&fusion->x1, 0.0f, 0.0f, 0.0f);
    222 
    223         initZeroMatrix(&fusion->P[0][0]);
    224         initZeroMatrix(&fusion->P[0][1]);
    225         initZeroMatrix(&fusion->P[1][0]);
    226         initZeroMatrix(&fusion->P[1][1]);
    227 
    228         fusionSetMagTrust(fusion, INITIALIZATION);
    229     }
    230 
    231     return 0;
    232 }
    233 
    234 static void matrixCross(struct Mat33 *out, struct Vec3 *p, float diag) {
    235     out->elem[0][0] = diag;
    236     out->elem[1][1] = diag;
    237     out->elem[2][2] = diag;
    238     out->elem[1][0] = p->z;
    239     out->elem[0][1] = -p->z;
    240     out->elem[2][0] = -p->y;
    241     out->elem[0][2] = p->y;
    242     out->elem[2][1] = p->x;
    243     out->elem[1][2] = -p->x;
    244 }
    245 
    246 static void fusionCheckState(struct Fusion *fusion) {
    247 
    248     if (!mat33IsPositiveSemidefinite(&fusion->P[0][0], SYMMETRY_TOLERANCE)
    249             || !mat33IsPositiveSemidefinite(
    250                 &fusion->P[1][1], SYMMETRY_TOLERANCE)) {
    251 
    252         initZeroMatrix(&fusion->P[0][0]);
    253         initZeroMatrix(&fusion->P[0][1]);
    254         initZeroMatrix(&fusion->P[1][0]);
    255         initZeroMatrix(&fusion->P[1][1]);
    256     }
    257 }
    258 
    259 #define kEps 1.0E-4f
    260 
    261 UNROLLED
    262 static void fusionPredict(struct Fusion *fusion, const struct Vec3 *w) {
    263     const float dT = fusion->mPredictDt;
    264 
    265     Quat q = fusion->x0;
    266     struct Vec3 b = fusion->x1;
    267 
    268     struct Vec3 we = *w;
    269     vec3Sub(&we, &b);
    270 
    271     struct Mat33 I33;
    272     initDiagonalMatrix(&I33, 1.0f);
    273 
    274     struct Mat33 I33dT;
    275     initDiagonalMatrix(&I33dT, dT);
    276 
    277     struct Mat33 wx;
    278     matrixCross(&wx, &we, 0.0f);
    279 
    280     struct Mat33 wx2;
    281     mat33Multiply(&wx2, &wx, &wx);
    282 
    283     float norm_we = vec3Norm(&we);
    284 
    285     if (fabsf(norm_we) < kEps) {
    286         return;
    287     }
    288 
    289     float lwedT = norm_we * dT;
    290     float hlwedT = 0.5f * lwedT;
    291     float ilwe = 1.0f / norm_we;
    292     float k0 = (1.0f - cosf(lwedT)) * (ilwe * ilwe);
    293     float k1 = sinf(lwedT);
    294     float k2 = cosf(hlwedT);
    295 
    296     struct Vec3 psi = we;
    297     vec3ScalarMul(&psi, sinf(hlwedT) * ilwe);
    298 
    299     struct Vec3 negPsi = psi;
    300     vec3ScalarMul(&negPsi, -1.0f);
    301 
    302     struct Mat33 O33;
    303     matrixCross(&O33, &negPsi, k2);
    304 
    305     struct Mat44 O;
    306     uint32_t i;
    307     for (i = 0; i < 3; ++i) {
    308         uint32_t j;
    309         for (j = 0; j < 3; ++j) {
    310             O.elem[i][j] = O33.elem[i][j];
    311         }
    312     }
    313 
    314     O.elem[3][0] = -psi.x;
    315     O.elem[3][1] = -psi.y;
    316     O.elem[3][2] = -psi.z;
    317     O.elem[3][3] = k2;
    318 
    319     O.elem[0][3] = psi.x;
    320     O.elem[1][3] = psi.y;
    321     O.elem[2][3] = psi.z;
    322 
    323     struct Mat33 tmp = wx;
    324     mat33ScalarMul(&tmp, k1 * ilwe);
    325 
    326     fusion->Phi0[0] = I33;
    327     mat33Sub(&fusion->Phi0[0], &tmp);
    328 
    329     tmp = wx2;
    330     mat33ScalarMul(&tmp, k0);
    331 
    332     mat33Add(&fusion->Phi0[0], &tmp);
    333 
    334     tmp = wx;
    335     mat33ScalarMul(&tmp, k0);
    336     fusion->Phi0[1] = tmp;
    337 
    338     mat33Sub(&fusion->Phi0[1], &I33dT);
    339 
    340     tmp = wx2;
    341     mat33ScalarMul(&tmp, ilwe * ilwe * ilwe * (lwedT - k1));
    342 
    343     mat33Sub(&fusion->Phi0[1], &tmp);
    344 
    345     mat44Apply(&fusion->x0, &O, &q);
    346 
    347     if (fusion->x0.w < 0.0f) {
    348         fusion->x0.x = -fusion->x0.x;
    349         fusion->x0.y = -fusion->x0.y;
    350         fusion->x0.z = -fusion->x0.z;
    351         fusion->x0.w = -fusion->x0.w;
    352     }
    353 
    354     // Pnew = Phi * P
    355 
    356     struct Mat33 Pnew[2][2];
    357     mat33Multiply(&Pnew[0][0], &fusion->Phi0[0], &fusion->P[0][0]);
    358     mat33Multiply(&tmp, &fusion->Phi0[1], &fusion->P[1][0]);
    359     mat33Add(&Pnew[0][0], &tmp);
    360 
    361     mat33Multiply(&Pnew[0][1], &fusion->Phi0[0], &fusion->P[0][1]);
    362     mat33Multiply(&tmp, &fusion->Phi0[1], &fusion->P[1][1]);
    363     mat33Add(&Pnew[0][1], &tmp);
    364 
    365     Pnew[1][0] = fusion->P[1][0];
    366     Pnew[1][1] = fusion->P[1][1];
    367 
    368     // P = Pnew * Phi^T
    369 
    370     mat33MultiplyTransposed2(&fusion->P[0][0], &Pnew[0][0], &fusion->Phi0[0]);
    371     mat33MultiplyTransposed2(&tmp, &Pnew[0][1], &fusion->Phi0[1]);
    372     mat33Add(&fusion->P[0][0], &tmp);
    373 
    374     fusion->P[0][1] = Pnew[0][1];
    375 
    376     mat33MultiplyTransposed2(&fusion->P[1][0], &Pnew[1][0], &fusion->Phi0[0]);
    377     mat33MultiplyTransposed2(&tmp, &Pnew[1][1], &fusion->Phi0[1]);
    378     mat33Add(&fusion->P[1][0], &tmp);
    379 
    380     fusion->P[1][1] = Pnew[1][1];
    381 
    382     mat33Add(&fusion->P[0][0], &fusion->GQGt[0][0]);
    383     mat33Add(&fusion->P[0][1], &fusion->GQGt[0][1]);
    384     mat33Add(&fusion->P[1][0], &fusion->GQGt[1][0]);
    385     mat33Add(&fusion->P[1][1], &fusion->GQGt[1][1]);
    386 
    387     fusionCheckState(fusion);
    388 }
    389 
    390 void fusionHandleGyro(struct Fusion *fusion, const struct Vec3 *w, float dT) {
    391     if (!fusion_init_complete(fusion, GYRO, w, dT)) {
    392         return;
    393     }
    394 
    395     updateDt(fusion, dT);
    396 
    397     fusionPredict(fusion, w);
    398 }
    399 
    400 UNROLLED
    401 static void scaleCovariance(struct Mat33 *out, const struct Mat33 *A, const struct Mat33 *P) {
    402     uint32_t r;
    403     for (r = 0; r < 3; ++r) {
    404         uint32_t j;
    405         for (j = r; j < 3; ++j) {
    406             float apat = 0.0f;
    407             uint32_t c;
    408             for (c = 0; c < 3; ++c) {
    409                 float v = A->elem[c][r] * P->elem[c][c] * 0.5f;
    410                 uint32_t k;
    411                 for (k = c + 1; k < 3; ++k) {
    412                     v += A->elem[k][r] * P->elem[c][k];
    413                 }
    414 
    415                 apat += 2.0f * v * A->elem[c][j];
    416             }
    417 
    418             out->elem[r][j] = apat;
    419             out->elem[j][r] = apat;
    420         }
    421     }
    422 }
    423 
    424 static void getF(struct Vec4 F[3], const struct Vec4 *q) {
    425     F[0].x = q->w;      F[1].x = -q->z;         F[2].x = q->y;
    426     F[0].y = q->z;      F[1].y = q->w;          F[2].y = -q->x;
    427     F[0].z = -q->y;     F[1].z = q->x;          F[2].z = q->w;
    428     F[0].w = -q->x;     F[1].w = -q->y;         F[2].w = -q->z;
    429 }
    430 
    431 static void fusionUpdate(
    432         struct Fusion *fusion, const struct Vec3 *z, const struct Vec3 *Bi, float sigma) {
    433     struct Mat33 A;
    434     quatToMatrix(&A, &fusion->x0);
    435 
    436     struct Vec3 Bb;
    437     mat33Apply(&Bb, &A, Bi);
    438 
    439     struct Mat33 L;
    440     matrixCross(&L, &Bb, 0.0f);
    441 
    442     struct Mat33 R;
    443     initDiagonalMatrix(&R, sigma * sigma);
    444 
    445     struct Mat33 S;
    446     scaleCovariance(&S, &L, &fusion->P[0][0]);
    447 
    448     mat33Add(&S, &R);
    449 
    450     struct Mat33 Si;
    451     mat33Invert(&Si, &S);
    452 
    453     struct Mat33 LtSi;
    454     mat33MultiplyTransposed(&LtSi, &L, &Si);
    455 
    456     struct Mat33 K[2];
    457     mat33Multiply(&K[0], &fusion->P[0][0], &LtSi);
    458     mat33MultiplyTransposed(&K[1], &fusion->P[0][1], &LtSi);
    459 
    460     struct Mat33 K0L;
    461     mat33Multiply(&K0L, &K[0], &L);
    462 
    463     struct Mat33 K1L;
    464     mat33Multiply(&K1L, &K[1], &L);
    465 
    466     struct Mat33 tmp;
    467     mat33Multiply(&tmp, &K0L, &fusion->P[0][0]);
    468     mat33Sub(&fusion->P[0][0], &tmp);
    469 
    470     mat33Multiply(&tmp, &K1L, &fusion->P[0][1]);
    471     mat33Sub(&fusion->P[1][1], &tmp);
    472 
    473     mat33Multiply(&tmp, &K0L, &fusion->P[0][1]);
    474     mat33Sub(&fusion->P[0][1], &tmp);
    475 
    476     mat33Transpose(&fusion->P[1][0], &fusion->P[0][1]);
    477 
    478     struct Vec3 e = *z;
    479     vec3Sub(&e, &Bb);
    480 
    481     struct Vec3 dq;
    482     mat33Apply(&dq, &K[0], &e);
    483 
    484 
    485     struct Vec4 F[3];
    486     getF(F, &fusion->x0);
    487 
    488     // 4x3 * 3x1 => 4x1
    489 
    490     struct Vec4 q;
    491     q.x = fusion->x0.x + 0.5f * (F[0].x * dq.x + F[1].x * dq.y + F[2].x * dq.z);
    492     q.y = fusion->x0.y + 0.5f * (F[0].y * dq.x + F[1].y * dq.y + F[2].y * dq.z);
    493     q.z = fusion->x0.z + 0.5f * (F[0].z * dq.x + F[1].z * dq.y + F[2].z * dq.z);
    494     q.w = fusion->x0.w + 0.5f * (F[0].w * dq.x + F[1].w * dq.y + F[2].w * dq.z);
    495 
    496     fusion->x0 = q;
    497     quatNormalize(&fusion->x0);
    498 
    499     if (fusion->flags & FUSION_USE_MAG) {
    500         // accumulate gyro bias (causes self spin) only if not
    501         // game rotation vector
    502         struct Vec3 db;
    503         mat33Apply(&db, &K[1], &e);
    504         vec3Add(&fusion->x1, &db);
    505     }
    506 
    507     fusionCheckState(fusion);
    508 }
    509 
    510 #define ACC_TRUSTWORTHY(abs_norm_err)  ((abs_norm_err) < 1.f)
    511 #define ACC_COS_CONV_FACTOR  0.01f
    512 #define ACC_COS_CONV_LIMIT   3.f
    513 
    514 int fusionHandleAcc(struct Fusion *fusion, const struct Vec3 *a, float dT) {
    515     if (!fusion_init_complete(fusion, ACC, a,  dT)) {
    516         return -EINVAL;
    517     }
    518 
    519     float norm2 = vec3NormSquared(a);
    520 
    521     if (norm2 < FREE_FALL_THRESHOLD_SQ) {
    522         return -EINVAL;
    523     }
    524 
    525     float l = sqrtf(norm2);
    526     float l_inv = 1.0f / l;
    527 
    528     if (!(fusion->flags & FUSION_USE_GYRO)) {
    529         // geo mag mode
    530         // drive the Kalman filter with zero mean dummy gyro vector
    531         struct Vec3 w_dummy;
    532 
    533         // avoid (fabsf(norm_we) < kEps) in fusionPredict()
    534         initVec3(&w_dummy, fusion->x1.x + kEps, fusion->x1.y + kEps,
    535                  fusion->x1.z + kEps);
    536 
    537         updateDt(fusion, dT);
    538         fusionPredict(fusion, &w_dummy);
    539     }
    540 
    541     struct Mat33 R;
    542     fusionGetRotationMatrix(fusion, &R);
    543 
    544     if (!(fusion->flags & FUSION_USE_MAG) &&
    545         (fusion->fake_mag_decimation += dT) > FAKE_MAG_INTERVAL) {
    546         // game rotation mode, provide fake mag update to prevent
    547         // P to diverge over time
    548         struct Vec3 m;
    549         mat33Apply(&m, &R, &fusion->Bm);
    550 
    551         fusionUpdate(fusion, &m, &fusion->Bm,
    552                       fusion->param.mag_stdev);
    553         fusion->fake_mag_decimation = 0.f;
    554     }
    555 
    556     struct Vec3 unityA = *a;
    557     vec3ScalarMul(&unityA, l_inv);
    558 
    559     float d = fabsf(l - NOMINAL_GRAVITY);
    560     float p;
    561     if (fusion->flags & FUSION_USE_GYRO) {
    562         float fc = 0;
    563         // Enable faster convergence
    564         if (ACC_TRUSTWORTHY(d)) {
    565             struct Vec3 aa;
    566             mat33Apply(&aa, &R, &fusion->Ba);
    567             float cos_err = vec3Dot(&aa, &unityA);
    568             cos_err = cos_err < (1.f - ACC_COS_CONV_FACTOR) ?
    569                 (1.f - ACC_COS_CONV_FACTOR) : cos_err;
    570             fc = (1.f - cos_err) *
    571                     (1.0f / ACC_COS_CONV_FACTOR * ACC_COS_CONV_LIMIT);
    572         }
    573         p = fusion->param.acc_stdev * expf(3 * d - fc);
    574     } else {
    575         // Adaptive acc weighting (trust acc less as it deviates from nominal g
    576         // more), acc_stdev *= e(sqrt(| |acc| - g_nominal|))
    577         //
    578         // The weighting equation comes from heuristics.
    579         p = fusion->param.acc_stdev * expf(sqrtf(d));
    580     }
    581 
    582     fusionUpdate(fusion, &unityA, &fusion->Ba, p);
    583 
    584     return 0;
    585 }
    586 
    587 #define MAG_COS_CONV_FACTOR   0.02f
    588 #define MAG_COS_CONV_LIMIT    3.5f
    589 #define MAG_STDEV_REDUCTION   0.005f // lower stdev means more trust
    590 
    591 int fusionHandleMag(struct Fusion *fusion, const struct Vec3 *m, float dT) {
    592     if (!fusion_init_complete(fusion, MAG, m, 0.0f /* dT */)) {
    593         return -EINVAL;
    594     }
    595 
    596     float magFieldSq = vec3NormSquared(m);
    597 
    598     if (magFieldSq > MAX_VALID_MAGNETIC_FIELD_SQ
    599             || magFieldSq < MIN_VALID_MAGNETIC_FIELD_SQ) {
    600         fusionSetMagTrust(fusion, NORMAL);
    601         fusion->lastMagInvalid = true;
    602         return -EINVAL;
    603     }
    604 
    605     struct Mat33 R;
    606     fusionGetRotationMatrix(fusion, &R);
    607 
    608     struct Vec3 up;
    609     mat33Apply(&up, &R, &fusion->Ba);
    610 
    611     struct Vec3 east;
    612     vec3Cross(&east, m, &up);
    613 
    614     if (vec3NormSquared(&east) < MIN_VALID_CROSS_PRODUCT_MAG_SQ) {
    615         fusionSetMagTrust(fusion, NORMAL);
    616         fusion->lastMagInvalid = true;
    617         return -EINVAL;
    618     }
    619 
    620     if (fusion->lastMagInvalid) {
    621         fusion->lastMagInvalid = false;
    622         fusionSetMagTrust(fusion, BACK_TO_VALID);
    623     }
    624 
    625     struct Vec3 north;
    626     vec3Cross(&north, &up, &east);
    627 
    628     float invNorm = 1.0f / vec3Norm(&north);
    629     vec3ScalarMul(&north, invNorm);
    630 
    631     float p = fusion->param.mag_stdev;
    632 
    633     if (fusion->flags & FUSION_USE_GYRO) {
    634         struct Vec3 mm;
    635         mat33Apply(&mm, &R, &fusion->Bm);
    636         float cos_err = vec3Dot(&mm, &north);
    637 
    638         if (fusion->trustedMagDuration > 0) {
    639             // if the trust mag time period is not finished
    640             if (cos_err < (1.f - MAG_COS_CONV_FACTOR/4)) {
    641                 // if the mag direction and the fusion north has not converged, lower the
    642                 // standard deviation of mag to speed up convergence.
    643                 p *= MAG_STDEV_REDUCTION;
    644                 fusion->trustedMagDuration -= dT;
    645             } else {
    646                 // it has converged already, so no need to keep the trust period any longer
    647                 fusionSetMagTrust(fusion, NORMAL);
    648             }
    649         } else {
    650             cos_err = cos_err < (1.f - MAG_COS_CONV_FACTOR) ?
    651                 (1.f - MAG_COS_CONV_FACTOR) : cos_err;
    652 
    653             float fc;
    654             fc = (1.f - cos_err) * (1.0f / MAG_COS_CONV_FACTOR * MAG_COS_CONV_LIMIT);
    655             p *= expf(-fc);
    656         }
    657     }
    658 
    659     fusionUpdate(fusion, &north, &fusion->Bm, p);
    660 
    661     return 0;
    662 }
    663 
    664 void fusionSetMagTrust(struct Fusion *fusion, int mode) {
    665     switch(mode) {
    666         case NORMAL:
    667             fusion->trustedMagDuration = 0; // disable
    668             break;
    669         case INITIALIZATION:
    670         case BACK_TO_VALID:
    671             fusion->trustedMagDuration = 0; // no special treatment for these two
    672             break;
    673         case MANUAL_MAG_CAL:
    674             fusion->trustedMagDuration = TRUST_DURATION_MANUAL_MAG_CAL;
    675             break;
    676         default:
    677             fusion->trustedMagDuration = 0; // by default it is disable
    678             break;
    679     }
    680 }
    681 
    682 void fusionGetAttitude(const struct Fusion *fusion, struct Vec4 *attitude) {
    683     *attitude = fusion->x0;
    684 }
    685 
    686 void fusionGetBias(const struct Fusion *fusion, struct Vec3 *bias) {
    687     *bias = fusion->x1;
    688 }
    689 
    690 void fusionGetRotationMatrix(const struct Fusion *fusion, struct Mat33 *R) {
    691     quatToMatrix(R, &fusion->x0);
    692 }
    693