Home | History | Annotate | Download | only in orientation
      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 #include <stdlib.h>
     18 #include <string.h>
     19 #include <timer.h>
     20 #include <heap.h>
     21 #include <plat/rtc.h>
     22 #include <plat/syscfg.h>
     23 #include <hostIntf.h>
     24 #include <nanohubPacket.h>
     25 #include <floatRt.h>
     26 
     27 #include <seos.h>
     28 
     29 #include <nanohub_math.h>
     30 #include <algos/fusion.h>
     31 #include <sensors.h>
     32 #include <variant/sensType.h>
     33 #include <limits.h>
     34 #include <slab.h>
     35 
     36 #define ORIENTATION_APP_VERSION 1
     37 
     38 #define MAX_NUM_COMMS_EVENT_SAMPLES 15 // at most 15 samples can fit in one comms_event
     39 #define NUM_COMMS_EVENTS_IN_FIFO    2  // This controls how often the hub needs to wake up
     40                                        // in batching
     41 
     42 // needs to be greater than max raw sensor rate ratio
     43 #define FIFO_DEPTH                  (NUM_COMMS_EVENTS_IN_FIFO * MAX_NUM_COMMS_EVENT_SAMPLES)
     44 
     45 /*
     46  * FIFO_MARGIN: max raw sensor rate ratio is 8:1.
     47  * If 2 batchs of high rate data comes before 1 low rate data, there can be at max 15 samples left
     48  * in the FIFO
     49  */
     50 #define FIFO_MARGIN                 15
     51 #define MAX_NUM_SAMPLES             (FIFO_MARGIN + FIFO_DEPTH) // actual input sample fifo depth
     52 #define EVT_SENSOR_ACC_DATA_RDY     sensorGetMyEventType(SENS_TYPE_ACCEL)
     53 #define EVT_SENSOR_GYR_DATA_RDY     sensorGetMyEventType(SENS_TYPE_GYRO)
     54 #define EVT_SENSOR_MAG_DATA_RDY     sensorGetMyEventType(SENS_TYPE_MAG)
     55 #define EVT_SENSOR_MAG_BIAS         sensorGetMyEventType(SENS_TYPE_MAG_BIAS)
     56 
     57 #define kGravityEarth               9.80665f
     58 #define kRad2deg                    (180.0f / M_PI)
     59 #define MIN_GYRO_RATE_HZ            SENSOR_HZ(100.0f)
     60 #define MAX_MAG_RATE_HZ             SENSOR_HZ(50.0f)
     61 
     62 enum
     63 {
     64     FUSION_FLAG_ENABLED             = 0x01,
     65     FUSION_FLAG_INITIALIZED         = 0x08,
     66     FUSION_FLAG_GAME_ENABLED        = 0x10,
     67     FUSION_FLAG_GAME_INITIALIZED    = 0x20
     68 };
     69 
     70 enum RawSensorType
     71 {
     72     ACC,
     73     GYR,
     74     MAG,
     75     NUM_OF_RAW_SENSOR
     76 };
     77 
     78 enum FusionSensorType
     79 {
     80     ORIENT,
     81     GRAVITY,
     82     GEOMAG,
     83     LINEAR,
     84     GAME,
     85     ROTAT,
     86     NUM_OF_FUSION_SENSOR
     87 };
     88 
     89 
     90 struct FusionSensorSample {
     91     uint64_t time;
     92     float x, y, z;
     93 };
     94 
     95 struct FusionSensor {
     96     uint32_t handle;
     97     struct TripleAxisDataEvent *ev;
     98     uint64_t prev_time;
     99     uint64_t latency;
    100     uint32_t rate;
    101     bool active;
    102     bool use_gyro_data;
    103     bool use_mag_data;
    104     uint8_t idx;
    105 };
    106 
    107 struct FusionTask {
    108     uint32_t tid;
    109     uint32_t accelHandle;
    110     uint32_t gyroHandle;
    111     uint32_t magHandle;
    112 
    113     struct Fusion fusion;
    114     struct Fusion game;
    115 
    116     struct FusionSensor sensors[NUM_OF_FUSION_SENSOR];
    117     struct FusionSensorSample samples[NUM_OF_RAW_SENSOR][MAX_NUM_SAMPLES];
    118     size_t sample_indices[NUM_OF_RAW_SENSOR];
    119     size_t sample_counts[NUM_OF_RAW_SENSOR];
    120     uint32_t counters[NUM_OF_RAW_SENSOR];
    121     uint64_t ResamplePeriodNs[NUM_OF_RAW_SENSOR];
    122     uint64_t last_time[NUM_OF_RAW_SENSOR];
    123     struct TripleAxisDataPoint last_sample[NUM_OF_RAW_SENSOR];
    124 
    125     uint32_t flags;
    126 
    127     uint32_t raw_sensor_rate[NUM_OF_RAW_SENSOR];
    128     uint64_t raw_sensor_latency;
    129 
    130     uint8_t accel_client_cnt;
    131     uint8_t gyro_client_cnt;
    132     uint8_t mag_client_cnt;
    133 };
    134 
    135 static uint32_t FusionRates[] = {
    136     SENSOR_HZ(12.5f),
    137     SENSOR_HZ(25.0f),
    138     SENSOR_HZ(50.0f),
    139     SENSOR_HZ(100.0f),
    140     SENSOR_HZ(200.0f),
    141     0,
    142 };
    143 
    144 //should match "supported rates in length" and be the timer length for that rate in nanosecs
    145 static const uint64_t rateTimerVals[] = {
    146     1000000000ULL / 12.5f,
    147     1000000000ULL / 25,
    148     1000000000ULL / 50,
    149     1000000000ULL / 100,
    150     1000000000ULL / 200,
    151 };
    152 
    153 static struct FusionTask mTask;
    154 
    155 #define DEC_INFO_RATE(name, rates, type, axis, inter, samples) \
    156     .sensorName = name, \
    157     .supportedRates = rates, \
    158     .sensorType = type, \
    159     .numAxis = axis, \
    160     .interrupt = inter, \
    161     .minSamples = samples
    162 
    163 static const struct SensorInfo mSi[NUM_OF_FUSION_SENSOR] =
    164 {
    165     { DEC_INFO_RATE("Orientation", FusionRates, SENS_TYPE_ORIENTATION, NUM_AXIS_THREE,
    166             NANOHUB_INT_NONWAKEUP, 20) },
    167     { DEC_INFO_RATE("Gravity", FusionRates, SENS_TYPE_GRAVITY, NUM_AXIS_THREE,
    168             NANOHUB_INT_NONWAKEUP, 20) },
    169     { DEC_INFO_RATE("Geomagnetic Rotation Vector", FusionRates, SENS_TYPE_GEO_MAG_ROT_VEC,
    170             NUM_AXIS_THREE, NANOHUB_INT_NONWAKEUP, 20) },
    171     { DEC_INFO_RATE("Linear Acceleration", FusionRates, SENS_TYPE_LINEAR_ACCEL, NUM_AXIS_THREE,
    172             NANOHUB_INT_NONWAKEUP, 20) },
    173     { DEC_INFO_RATE("Game Rotation Vector", FusionRates, SENS_TYPE_GAME_ROT_VECTOR, NUM_AXIS_THREE,
    174             NANOHUB_INT_NONWAKEUP, 300) },
    175     { DEC_INFO_RATE("Rotation Vector", FusionRates, SENS_TYPE_ROTATION_VECTOR, NUM_AXIS_THREE,
    176             NANOHUB_INT_NONWAKEUP, 20) },
    177 };
    178 
    179 static struct SlabAllocator *mDataSlab;
    180 
    181 static void dataEvtFree(void *ptr)
    182 {
    183     slabAllocatorFree(mDataSlab, ptr);
    184 }
    185 
    186 static void fillSamples(struct TripleAxisDataEvent *ev, enum RawSensorType index)
    187 {
    188     bool bad_timestamp;
    189     size_t i, w, n, num_samples;
    190     struct TripleAxisDataPoint *curr_sample, *next_sample;
    191     uint32_t counter;
    192     uint64_t ResamplePeriodNs, curr_time, next_time;
    193     uint64_t sample_spacing_ns;
    194     float weight_next;
    195 
    196     if (index == GYR && mTask.gyro_client_cnt == 0) {
    197         return;
    198     }
    199     if (index == MAG && mTask.mag_client_cnt == 0) {
    200         return;
    201     }
    202 
    203     n = mTask.sample_counts[index];
    204     i = mTask.sample_indices[index];
    205     counter = mTask.counters[index];
    206     ResamplePeriodNs = mTask.ResamplePeriodNs[index];
    207     w = (mTask.sample_indices[index] + n) % MAX_NUM_SAMPLES;
    208 
    209     // check if this sensor was used before
    210     if (mTask.last_time[index] == ULONG_LONG_MAX) {
    211         curr_sample = ev->samples;
    212         next_sample = curr_sample + 1;
    213         num_samples = ev->samples[0].firstSample.numSamples;
    214         curr_time = ev->referenceTime;
    215     } else {
    216         curr_sample = &mTask.last_sample[index];
    217         next_sample = ev->samples;
    218         num_samples = ev->samples[0].firstSample.numSamples + 1;
    219         curr_time = mTask.last_time[index];
    220     }
    221 
    222     while (num_samples > 1) {
    223 
    224         if (next_sample == ev->samples)
    225             next_time = ev->referenceTime;
    226         else
    227             next_time = curr_time + next_sample->deltaTime;
    228 
    229         // error handling for non-chronological accel timestamps
    230         sample_spacing_ns = (next_time > curr_time) ?  (next_time - curr_time) : 0;
    231 
    232         // This can happen during sensor config changes
    233         bad_timestamp = (sample_spacing_ns > 10 * ResamplePeriodNs);
    234 
    235         // Check to see if we need to move the interpolation window or
    236         // interpolate
    237         if ((counter >= sample_spacing_ns) || bad_timestamp) {
    238             num_samples--;
    239             counter -= (bad_timestamp ? counter : sample_spacing_ns);
    240             curr_sample = next_sample;
    241             next_sample++;
    242 
    243             curr_time = next_time;
    244         } else {
    245             weight_next = (float)counter / floatFromUint64(sample_spacing_ns);
    246 
    247             mTask.samples[index][w].x = curr_sample->x + weight_next *
    248                 (next_sample->x - curr_sample->x);
    249             mTask.samples[index][w].y = curr_sample->y + weight_next *
    250                 (next_sample->y - curr_sample->y);
    251             mTask.samples[index][w].z = curr_sample->z + weight_next *
    252                 (next_sample->z - curr_sample->z);
    253             mTask.samples[index][w].time = curr_time + counter;
    254 
    255             // Move the read index when buffer is full
    256             if (++n > MAX_NUM_SAMPLES) {
    257                 n = MAX_NUM_SAMPLES;
    258 
    259                 if (++i == MAX_NUM_SAMPLES) {
    260                     i = 0;
    261                 }
    262             }
    263 
    264             // Reset the write index
    265             if (++w == MAX_NUM_SAMPLES) {
    266                 w = 0;
    267             }
    268 
    269             // Move to the next resample
    270             counter += ResamplePeriodNs;
    271         }
    272     }
    273 
    274     mTask.sample_counts[index] = n;
    275     mTask.sample_indices[index] = i;
    276     mTask.counters[index] = counter;
    277     mTask.last_sample[index] = *curr_sample;
    278     mTask.last_time[index] = curr_time;
    279 }
    280 
    281 static bool allocateDataEvt(struct FusionSensor *mSensor, uint64_t time)
    282 {
    283     mSensor->ev = slabAllocatorAlloc(mDataSlab);
    284     if (mSensor->ev == NULL) {
    285         // slab allocation failed, need to stop draining raw samples for now.
    286         osLog(LOG_INFO, "ORIENTATION: slabAllocatorAlloc() Failed\n");
    287         return false;
    288     }
    289 
    290     // delta time for the first sample is sample count
    291     memset(&mSensor->ev->samples[0].firstSample, 0x00, sizeof(struct SensorFirstSample));
    292     mSensor->ev->referenceTime = time;
    293     mSensor->prev_time = time;
    294 
    295     return true;
    296 }
    297 
    298 // returns false if addSample() fails
    299 static bool addSample(struct FusionSensor *mSensor, uint64_t time, float x, float y, float z)
    300 {
    301     struct TripleAxisDataPoint *sample;
    302 
    303     // Bypass processing this accel sample.
    304     // This is needed after recovering from a slab shortage.
    305     if (mSensor->prev_time == time) {
    306         osLog(LOG_INFO, "Accel sample has been processed by fusion sensor %d\n",
    307               mSensor->idx);
    308         return true;
    309     }
    310 
    311     if (mSensor->ev == NULL) {
    312         if (!allocateDataEvt(mSensor, time))
    313             return false;
    314     }
    315 
    316     if (mSensor->ev->samples[0].firstSample.numSamples >= MAX_NUM_COMMS_EVENT_SAMPLES) {
    317         osLog(LOG_ERROR, "ORIENTATION: BAD_INDEX\n");
    318         return false;
    319     }
    320 
    321     sample = &mSensor->ev->samples[mSensor->ev->samples[0].firstSample.numSamples++];
    322 
    323     if (mSensor->ev->samples[0].firstSample.numSamples > 1) {
    324         sample->deltaTime = time > mSensor->prev_time ? (time - mSensor->prev_time) : 0;
    325         mSensor->prev_time = time;
    326     }
    327 
    328     sample->x = x;
    329     sample->y = y;
    330     sample->z = z;
    331 
    332     if (mSensor->ev->samples[0].firstSample.numSamples == MAX_NUM_COMMS_EVENT_SAMPLES) {
    333         osEnqueueEvtOrFree(
    334                 EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(mSi[mSensor->idx].sensorType),
    335                 mSensor->ev, dataEvtFree);
    336         mSensor->ev = NULL;
    337     }
    338     return true;
    339 }
    340 
    341 // returns false if addSample fails for any fusion sensor
    342 // (most likely due to slab allocation failure)
    343 static bool updateOutput(ssize_t last_accel_sample_index, uint64_t last_sensor_time)
    344 {
    345     struct Vec4 attitude;
    346     struct Vec3 g, a;
    347     struct Mat33 R;  // direction-cosine/rotation matrix, inertial -> device
    348     bool   rInited;  // indicates if matrix R has been initialzed. for avoiding repeated computation
    349     bool ret = true;
    350 
    351     if (fusionHasEstimate(&mTask.game)) {
    352         rInited = false;
    353         if (mTask.sensors[GAME].active) {
    354             fusionGetAttitude(&mTask.game, &attitude);
    355             if (!addSample(&mTask.sensors[GAME],
    356                     last_sensor_time,
    357                     attitude.x,
    358                     attitude.y,
    359                     attitude.z)) {
    360                 ret = false;
    361             }
    362         }
    363 
    364         if (mTask.sensors[GRAVITY].active) {
    365             fusionGetRotationMatrix(&mTask.game, &R);
    366             rInited = true;
    367             initVec3(&g, R.elem[0][2], R.elem[1][2], R.elem[2][2]);
    368             vec3ScalarMul(&g, kGravityEarth);
    369             if (!addSample(&mTask.sensors[GRAVITY],
    370                     last_sensor_time,
    371                     g.x,
    372                     g.y,
    373                     g.z)) {
    374                 ret = false;
    375             }
    376         }
    377 
    378         if (last_accel_sample_index >= 0
    379                 && mTask.sensors[LINEAR].active) {
    380             if (!rInited) {
    381                 fusionGetRotationMatrix(&mTask.game, &R);
    382             }
    383             initVec3(&g, R.elem[0][2], R.elem[1][2], R.elem[2][2]);
    384             vec3ScalarMul(&g, kGravityEarth);
    385             initVec3(&a,
    386                     mTask.samples[0][last_accel_sample_index].x,
    387                     mTask.samples[0][last_accel_sample_index].y,
    388                     mTask.samples[0][last_accel_sample_index].z);
    389 
    390             if (!addSample(&mTask.sensors[LINEAR],
    391                     mTask.samples[0][last_accel_sample_index].time,
    392                     a.x - g.x,
    393                     a.y - g.y,
    394                     a.z - g.z)) {
    395                 ret = false;
    396             }
    397         }
    398     }
    399 
    400     if (fusionHasEstimate(&mTask.fusion)) {
    401         fusionGetAttitude(&mTask.fusion, &attitude);
    402 
    403         if (mTask.sensors[ORIENT].active) {
    404             fusionGetRotationMatrix(&mTask.fusion, &R);
    405             // x, y, z = yaw, pitch, roll
    406             float x = atan2f(-R.elem[0][1], R.elem[0][0]) * kRad2deg;
    407             float y = atan2f(-R.elem[1][2], R.elem[2][2]) * kRad2deg;
    408             float z = asinf(R.elem[0][2]) * kRad2deg;
    409 
    410             if (x < 0.0f) {
    411                 x += 360.0f;
    412             }
    413 
    414             if (!addSample(&mTask.sensors[ORIENT],
    415                     last_sensor_time,
    416                     x,
    417                     y,
    418                     z)) {
    419                 ret = false;
    420             }
    421         }
    422 
    423         if (mTask.sensors[GEOMAG].active) {
    424             if (!addSample(&mTask.sensors[GEOMAG],
    425                     last_sensor_time,
    426                     attitude.x,
    427                     attitude.y,
    428                     attitude.z)) {
    429                 ret = false;
    430             }
    431         }
    432 
    433         if (mTask.sensors[ROTAT].active) {
    434             if (!addSample(&mTask.sensors[ROTAT],
    435                     last_sensor_time,
    436                     attitude.x,
    437                     attitude.y,
    438                     attitude.z)) {
    439                 ret = false;
    440             }
    441         }
    442 
    443     }
    444     return ret;
    445 }
    446 
    447 static void drainSamples()
    448 {
    449     struct Vec3 a, w, m;
    450     uint64_t a_time, g_time, m_time;
    451     size_t i = mTask.sample_indices[ACC];
    452     size_t j = 0;
    453     size_t k = 0;
    454     size_t which;
    455     float dT;
    456     bool success = true;
    457 
    458     if (mTask.gyro_client_cnt > 0)
    459         j = mTask.sample_indices[GYR];
    460 
    461     if (mTask.mag_client_cnt > 0)
    462         k = mTask.sample_indices[MAG];
    463 
    464     // Keep draining raw samples and producing fusion samples only if
    465     // 1) all raw sensors needed are present (to compare timestamp) and
    466     // 2) updateOutput() succeeded (no slab shortage)
    467     // Otherwise, wait till next raw sample event.
    468     while (mTask.sample_counts[ACC] > 0
    469             && (!(mTask.gyro_client_cnt > 0) || mTask.sample_counts[GYR] > 0)
    470             && (!(mTask.mag_client_cnt > 0) || mTask.sample_counts[MAG] > 0)
    471             && success) {
    472         a_time = mTask.samples[ACC][i].time;
    473         g_time = mTask.gyro_client_cnt > 0 ? mTask.samples[GYR][j].time
    474                             : ULONG_LONG_MAX;
    475         m_time = mTask.mag_client_cnt > 0 ? mTask.samples[MAG][k].time
    476                             : ULONG_LONG_MAX;
    477 
    478         // priority with same timestamp: gyro > acc > mag
    479         if (g_time <= a_time && g_time <= m_time) {
    480             which = GYR;
    481         } else if (a_time <= m_time) {
    482             which = ACC;
    483         } else {
    484             which = MAG;
    485         }
    486 
    487         dT = floatFromUint64(mTask.ResamplePeriodNs[which]) * 1e-9f;
    488         switch (which) {
    489         case ACC:
    490             initVec3(&a, mTask.samples[ACC][i].x, mTask.samples[ACC][i].y, mTask.samples[ACC][i].z);
    491 
    492             if (mTask.flags & FUSION_FLAG_ENABLED)
    493                 fusionHandleAcc(&mTask.fusion, &a, dT);
    494 
    495             if (mTask.flags & FUSION_FLAG_GAME_ENABLED)
    496                 fusionHandleAcc(&mTask.game, &a, dT);
    497 
    498             success = updateOutput(i, mTask.samples[ACC][i].time);
    499 
    500             // Do not remove the accel sample until all active fusion sesnsors
    501             // successfully updated the output.
    502             // Fusion sensors that have processed this accel sample will bypass
    503             // it in addSample().
    504             if (success) {
    505                 --mTask.sample_counts[ACC];
    506                 if (++i == MAX_NUM_SAMPLES) {
    507                     i = 0;
    508                 }
    509             }
    510             break;
    511         case GYR:
    512             initVec3(&w, mTask.samples[GYR][j].x, mTask.samples[GYR][j].y, mTask.samples[GYR][j].z);
    513 
    514             if (mTask.flags & FUSION_FLAG_ENABLED)
    515                 fusionHandleGyro(&mTask.fusion, &w, dT);
    516 
    517             if (mTask.flags & FUSION_FLAG_GAME_ENABLED)
    518                 fusionHandleGyro(&mTask.game, &w, dT);
    519 
    520             --mTask.sample_counts[GYR];
    521             if (++j == MAX_NUM_SAMPLES)
    522                 j = 0;
    523             break;
    524         case MAG:
    525             initVec3(&m, mTask.samples[MAG][k].x, mTask.samples[MAG][k].y, mTask.samples[MAG][k].z);
    526 
    527             fusionHandleMag(&mTask.fusion, &m, dT);
    528 
    529             --mTask.sample_counts[MAG];
    530             if (++k == MAX_NUM_SAMPLES)
    531                 k = 0;
    532             break;
    533         }
    534     }
    535 
    536     mTask.sample_indices[ACC] = i;
    537 
    538     if (mTask.gyro_client_cnt > 0)
    539         mTask.sample_indices[GYR] = j;
    540 
    541     if (mTask.mag_client_cnt > 0)
    542         mTask.sample_indices[MAG] = k;
    543 
    544     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    545         if (mTask.sensors[i].ev != NULL) {
    546             osEnqueueEvtOrFree(EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(mSi[i].sensorType),
    547                                mTask.sensors[i].ev, dataEvtFree);
    548             mTask.sensors[i].ev = NULL;
    549         }
    550     }
    551 }
    552 
    553 static void configureFusion()
    554 {
    555     if (mTask.sensors[ORIENT].active
    556             || mTask.sensors[ROTAT].active
    557             || mTask.sensors[GEOMAG].active) {
    558         mTask.flags |= FUSION_FLAG_ENABLED;
    559         initFusion(&mTask.fusion,
    560                 (mTask.mag_client_cnt > 0 ? FUSION_USE_MAG : 0) |
    561                 (mTask.gyro_client_cnt > 0 ? FUSION_USE_GYRO : 0) |
    562                 ((mTask.flags & FUSION_FLAG_INITIALIZED) ? 0 : FUSION_REINITIALIZE));
    563         mTask.flags |= FUSION_FLAG_INITIALIZED;
    564     } else {
    565         mTask.flags &= ~FUSION_FLAG_ENABLED;
    566         mTask.flags &= ~FUSION_FLAG_INITIALIZED;
    567     }
    568 }
    569 
    570 static void configureGame()
    571 {
    572     if (mTask.sensors[GAME].active || mTask.sensors[GRAVITY].active ||
    573             mTask.sensors[LINEAR].active) {
    574         mTask.flags |= FUSION_FLAG_GAME_ENABLED;
    575         initFusion(&mTask.game, FUSION_USE_GYRO |
    576                 ((mTask.flags & FUSION_FLAG_INITIALIZED) ? 0 : FUSION_REINITIALIZE));
    577         mTask.flags |= FUSION_FLAG_GAME_INITIALIZED;
    578     } else {
    579         mTask.flags &= ~FUSION_FLAG_GAME_ENABLED;
    580         mTask.flags &= ~FUSION_FLAG_GAME_INITIALIZED;
    581     }
    582 }
    583 
    584 static void fusionSetRateAcc(void)
    585 {
    586     int i;
    587     if  (mTask.accelHandle == 0) {
    588         mTask.sample_counts[ACC] = 0;
    589         mTask.sample_indices[ACC] = 0;
    590         mTask.counters[ACC] = 0;
    591         mTask.last_time[ACC] = ULONG_LONG_MAX;
    592         for (i = 0; sensorFind(SENS_TYPE_ACCEL, i, &mTask.accelHandle) != NULL; i++) {
    593             if (sensorRequest(mTask.tid, mTask.accelHandle, mTask.raw_sensor_rate[ACC],
    594                         mTask.raw_sensor_latency)) {
    595                 osEventSubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
    596                 break;
    597             }
    598         }
    599     } else {
    600         sensorRequestRateChange(mTask.tid, mTask.accelHandle, mTask.raw_sensor_rate[ACC],
    601                 mTask.raw_sensor_latency);
    602     }
    603 }
    604 
    605 static void fusionSetRateGyr(void)
    606 {
    607     int i;
    608     if (mTask.gyroHandle == 0) {
    609         mTask.sample_counts[GYR] = 0;
    610         mTask.sample_indices[GYR] = 0;
    611         mTask.counters[GYR] = 0;
    612         mTask.last_time[GYR] = ULONG_LONG_MAX;
    613         for (i = 0; sensorFind(SENS_TYPE_GYRO, i, &mTask.gyroHandle) != NULL; i++) {
    614             if (sensorRequest(mTask.tid, mTask.gyroHandle, mTask.raw_sensor_rate[GYR],
    615                         mTask.raw_sensor_latency)) {
    616                 osEventSubscribe(mTask.tid, EVT_SENSOR_GYR_DATA_RDY);
    617                 break;
    618             }
    619         }
    620     } else {
    621         sensorRequestRateChange(mTask.tid, mTask.gyroHandle, mTask.raw_sensor_rate[GYR],
    622                 mTask.raw_sensor_latency);
    623     }
    624 }
    625 
    626 static void fusionSetRateMag(void)
    627 {
    628     int i;
    629     if (mTask.magHandle == 0) {
    630         mTask.sample_counts[MAG] = 0;
    631         mTask.sample_indices[MAG] = 0;
    632         mTask.counters[MAG] = 0;
    633         mTask.last_time[MAG] = ULONG_LONG_MAX;
    634         for (i = 0; sensorFind(SENS_TYPE_MAG, i, &mTask.magHandle) != NULL; i++) {
    635             if (sensorRequest(mTask.tid, mTask.magHandle, mTask.raw_sensor_rate[MAG],
    636                         mTask.raw_sensor_latency)) {
    637                 osEventSubscribe(mTask.tid, EVT_SENSOR_MAG_DATA_RDY);
    638                 osEventSubscribe(mTask.tid, EVT_SENSOR_MAG_BIAS);
    639                 break;
    640             }
    641         }
    642     } else {
    643         sensorRequestRateChange(mTask.tid, mTask.magHandle, mTask.raw_sensor_rate[MAG],
    644                 mTask.raw_sensor_latency);
    645     }
    646 }
    647 
    648 static bool fusionSetRate(uint32_t rate, uint64_t latency, void *cookie)
    649 {
    650     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    651     int i;
    652     uint32_t max_rate = 0;
    653     uint32_t gyr_rate, mag_rate;
    654     uint64_t min_resample_period = ULONG_LONG_MAX;
    655 
    656     mSensor->rate = rate;
    657     mSensor->latency = latency;
    658 
    659     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    660         if (mTask.sensors[i].active) {
    661             max_rate = max_rate > mTask.sensors[i].rate ? max_rate : mTask.sensors[i].rate;
    662         }
    663     }
    664 
    665     if (mTask.accel_client_cnt > 0) {
    666         mTask.raw_sensor_rate[ACC] = max_rate;
    667         mTask.ResamplePeriodNs[ACC] = sensorTimerLookupCommon(FusionRates, rateTimerVals, max_rate);
    668         min_resample_period = mTask.ResamplePeriodNs[ACC] < min_resample_period ?
    669             mTask.ResamplePeriodNs[ACC] : min_resample_period;
    670     }
    671 
    672     if (mTask.gyro_client_cnt > 0) {
    673         gyr_rate = max_rate > MIN_GYRO_RATE_HZ ? max_rate : MIN_GYRO_RATE_HZ;
    674         mTask.raw_sensor_rate[GYR] = gyr_rate;
    675         mTask.ResamplePeriodNs[GYR] = sensorTimerLookupCommon(FusionRates, rateTimerVals, gyr_rate);
    676         min_resample_period = mTask.ResamplePeriodNs[GYR] < min_resample_period ?
    677             mTask.ResamplePeriodNs[GYR] : min_resample_period;
    678     }
    679 
    680     if (mTask.mag_client_cnt > 0) {
    681         mag_rate = max_rate < MAX_MAG_RATE_HZ ? max_rate : MAX_MAG_RATE_HZ;
    682         mTask.raw_sensor_rate[MAG] = mag_rate;
    683         mTask.ResamplePeriodNs[MAG] = sensorTimerLookupCommon(FusionRates, rateTimerVals, mag_rate);
    684         min_resample_period = mTask.ResamplePeriodNs[MAG] < min_resample_period ?
    685             mTask.ResamplePeriodNs[MAG] : min_resample_period;
    686     }
    687 
    688     // This guarantees that local raw sensor FIFOs won't overflow.
    689     mTask.raw_sensor_latency = min_resample_period * (FIFO_DEPTH - 1);
    690 
    691     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    692         if (mTask.sensors[i].active) {
    693             mTask.raw_sensor_latency = mTask.sensors[i].latency < mTask.raw_sensor_latency ?
    694                 mTask.sensors[i].latency : mTask.raw_sensor_latency;
    695         }
    696     }
    697 
    698     if (mTask.accel_client_cnt > 0)
    699         fusionSetRateAcc();
    700     if (mTask.gyro_client_cnt > 0)
    701         fusionSetRateGyr();
    702     if (mTask.mag_client_cnt > 0)
    703         fusionSetRateMag();
    704     if (mSensor->rate > 0)
    705         sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
    706 
    707     return true;
    708 }
    709 
    710 static bool fusionPower(bool on, void *cookie)
    711 {
    712     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    713     int idx;
    714 
    715     mSensor->active = on;
    716     if (on == false) {
    717         mTask.accel_client_cnt--;
    718         if (mSensor->use_gyro_data)
    719             mTask.gyro_client_cnt--;
    720         if (mSensor->use_mag_data)
    721             mTask.mag_client_cnt--;
    722 
    723         // if client_cnt == 0 and Handle == 0, nothing need to be done.
    724         // if client_cnt > 0 and Handle == 0, something else is turning it on, all will be done.
    725         if (mTask.accel_client_cnt == 0 && mTask.accelHandle != 0) {
    726             sensorRelease(mTask.tid, mTask.accelHandle);
    727             mTask.accelHandle = 0;
    728             osEventUnsubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
    729         }
    730 
    731         if (mTask.gyro_client_cnt == 0 && mTask.gyroHandle != 0) {
    732             sensorRelease(mTask.tid, mTask.gyroHandle);
    733             mTask.gyroHandle = 0;
    734             osEventUnsubscribe(mTask.tid, EVT_SENSOR_GYR_DATA_RDY);
    735         }
    736 
    737         if (mTask.mag_client_cnt == 0 && mTask.magHandle != 0) {
    738             sensorRelease(mTask.tid, mTask.magHandle);
    739             mTask.magHandle = 0;
    740             osEventUnsubscribe(mTask.tid, EVT_SENSOR_MAG_DATA_RDY);
    741         }
    742 
    743         idx = mSensor->idx;
    744         (void) fusionSetRate(0, ULONG_LONG_MAX, (void *)idx);
    745     } else {
    746         mTask.accel_client_cnt++;
    747         if (mSensor->use_gyro_data)
    748             mTask.gyro_client_cnt++;
    749         if (mSensor->use_mag_data)
    750             mTask.mag_client_cnt++;
    751     }
    752 
    753     configureFusion();
    754     configureGame();
    755     sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
    756 
    757     return true;
    758 }
    759 
    760 static bool fusionFirmwareUpload(void *cookie)
    761 {
    762     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    763 
    764     sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
    765     return true;
    766 }
    767 
    768 static bool fusionFlush(void *cookie)
    769 {
    770     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    771     uint32_t evtType = sensorGetMyEventType(mSi[mSensor->idx].sensorType);
    772 
    773     osEnqueueEvt(evtType, SENSOR_DATA_EVENT_FLUSH, NULL);
    774     return true;
    775 }
    776 
    777 static void fusionHandleEvent(uint32_t evtType, const void* evtData)
    778 {
    779     struct TripleAxisDataEvent *ev;
    780     int i;
    781 
    782     if (evtData == SENSOR_DATA_EVENT_FLUSH)
    783         return;
    784 
    785     switch (evtType) {
    786     case EVT_APP_START:
    787         // check for gyro and mag
    788         osEventUnsubscribe(mTask.tid, EVT_APP_START);
    789         if (!sensorFind(SENS_TYPE_GYRO, 0, &mTask.gyroHandle)) {
    790             for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++)
    791                 mTask.sensors[i].use_gyro_data = false;
    792         }
    793         mTask.gyroHandle = 0;
    794         if (!sensorFind(SENS_TYPE_MAG, 0, &mTask.magHandle)) {
    795             for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++)
    796                 mTask.sensors[i].use_mag_data = false;
    797         }
    798         mTask.magHandle = 0;
    799         break;
    800     case EVT_SENSOR_ACC_DATA_RDY:
    801         ev = (struct TripleAxisDataEvent *)evtData;
    802         fillSamples(ev, ACC);
    803         drainSamples();
    804         break;
    805     case EVT_SENSOR_GYR_DATA_RDY:
    806         ev = (struct TripleAxisDataEvent *)evtData;
    807         fillSamples(ev, GYR);
    808         drainSamples();
    809         break;
    810     case EVT_SENSOR_MAG_BIAS:
    811         ev = (struct TripleAxisDataEvent *)evtData;
    812         if (ev->samples[0].firstSample.biasPresent && mTask.flags & FUSION_FLAG_ENABLED) {
    813             //it is a user initiated mag cal event
    814             fusionSetMagTrust(&mTask.fusion, MANUAL_MAG_CAL);
    815         }
    816         break;
    817     case EVT_SENSOR_MAG_DATA_RDY:
    818         ev = (struct TripleAxisDataEvent *)evtData;
    819         fillSamples(ev, MAG);
    820         drainSamples();
    821         break;
    822     }
    823 }
    824 
    825 static const struct SensorOps mSops =
    826 {
    827     .sensorPower = fusionPower,
    828     .sensorFirmwareUpload = fusionFirmwareUpload,
    829     .sensorSetRate = fusionSetRate,
    830     .sensorFlush = fusionFlush,
    831 };
    832 
    833 static bool fusionStart(uint32_t tid)
    834 {
    835     size_t i, slabSize;
    836 
    837     mTask.tid = tid;
    838     mTask.flags = 0;
    839 
    840     for (i = 0; i < NUM_OF_RAW_SENSOR; i++) {
    841          mTask.sample_counts[i] = 0;
    842          mTask.sample_indices[i] = 0;
    843     }
    844 
    845     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    846         mTask.sensors[i].handle = sensorRegister(&mSi[i], &mSops, (void *)i, true);
    847         mTask.sensors[i].idx = i;
    848         mTask.sensors[i].use_gyro_data = true;
    849         mTask.sensors[i].use_mag_data = true;
    850     }
    851 
    852     mTask.sensors[GEOMAG].use_gyro_data = false;
    853     mTask.sensors[GAME].use_mag_data = false;
    854     mTask.sensors[GRAVITY].use_mag_data = false;
    855     mTask.sensors[LINEAR].use_mag_data = false;
    856 
    857     mTask.accel_client_cnt = 0;
    858     mTask.gyro_client_cnt = 0;
    859     mTask.mag_client_cnt = 0;
    860 
    861     slabSize = sizeof(struct TripleAxisDataEvent)
    862         + MAX_NUM_COMMS_EVENT_SAMPLES * sizeof(struct TripleAxisDataPoint);
    863 
    864     // worst case 6 output sensors * (N + 1) comms_events
    865     mDataSlab = slabAllocatorNew(slabSize, 4, 6 * (NUM_COMMS_EVENTS_IN_FIFO + 1));
    866     if (!mDataSlab) {
    867         osLog(LOG_ERROR, "ORIENTATION: slabAllocatorNew() FAILED\n");
    868         return false;
    869     }
    870 
    871     osEventSubscribe(mTask.tid, EVT_APP_START);
    872 
    873     return true;
    874 }
    875 
    876 static void fusionEnd()
    877 {
    878     mTask.flags &= ~FUSION_FLAG_INITIALIZED;
    879     mTask.flags &= ~FUSION_FLAG_GAME_INITIALIZED;
    880     slabAllocatorDestroy(mDataSlab);
    881 }
    882 
    883 INTERNAL_APP_INIT(
    884         APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 4),
    885         ORIENTATION_APP_VERSION,
    886         fusionStart,
    887         fusionEnd,
    888         fusionHandleEvent);
    889