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