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/inc/rtc.h>
     22 #include <plat/inc/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/inc/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
    286         osLog(LOG_ERROR, "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 static void addSample(struct FusionSensor *mSensor, uint64_t time, float x, float y, float z)
    299 {
    300     struct TripleAxisDataPoint *sample;
    301 
    302     if (mSensor->ev == NULL) {
    303         if (!allocateDataEvt(mSensor, time))
    304             return;
    305     }
    306 
    307     if (mSensor->ev->samples[0].firstSample.numSamples >= MAX_NUM_COMMS_EVENT_SAMPLES) {
    308         osLog(LOG_ERROR, "ORIENTATION: BAD_INDEX\n");
    309         return;
    310     }
    311 
    312     sample = &mSensor->ev->samples[mSensor->ev->samples[0].firstSample.numSamples++];
    313 
    314     if (mSensor->ev->samples[0].firstSample.numSamples > 1) {
    315         sample->deltaTime = time > mSensor->prev_time ? (time - mSensor->prev_time) : 0;
    316         mSensor->prev_time = time;
    317     }
    318 
    319     sample->x = x;
    320     sample->y = y;
    321     sample->z = z;
    322 
    323     if (mSensor->ev->samples[0].firstSample.numSamples == MAX_NUM_COMMS_EVENT_SAMPLES) {
    324         osEnqueueEvtOrFree(
    325                 EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(mSi[mSensor->idx].sensorType),
    326                 mSensor->ev, dataEvtFree);
    327         mSensor->ev = NULL;
    328     }
    329 }
    330 
    331 static void updateOutput(ssize_t last_accel_sample_index, uint64_t last_sensor_time)
    332 {
    333     struct Vec4 attitude;
    334     struct Vec3 g, a;
    335     struct Mat33 R;  // direction-cosine/rotation matrix, inertial -> device
    336     bool   rInited;  // indicates if matrix R has been initialzed. for avoiding repeated computation
    337 
    338     if (fusionHasEstimate(&mTask.game)) {
    339         rInited = false;
    340         if (mTask.sensors[GAME].active) {
    341             fusionGetAttitude(&mTask.game, &attitude);
    342             addSample(&mTask.sensors[GAME],
    343                     last_sensor_time,
    344                     attitude.x,
    345                     attitude.y,
    346                     attitude.z);
    347         }
    348 
    349         if (mTask.sensors[GRAVITY].active) {
    350             fusionGetRotationMatrix(&mTask.game, &R);
    351             rInited = true;
    352             initVec3(&g, R.elem[0][2], R.elem[1][2], R.elem[2][2]);
    353             vec3ScalarMul(&g, kGravityEarth);
    354             addSample(&mTask.sensors[GRAVITY],
    355                     last_sensor_time,
    356                     g.x, g.y, g.z);
    357         }
    358 
    359         if (last_accel_sample_index >= 0
    360                 && mTask.sensors[LINEAR].active) {
    361             if (!rInited) {
    362                 fusionGetRotationMatrix(&mTask.game, &R);
    363             }
    364             initVec3(&g, R.elem[0][2], R.elem[1][2], R.elem[2][2]);
    365             vec3ScalarMul(&g, kGravityEarth);
    366             initVec3(&a,
    367                     mTask.samples[0][last_accel_sample_index].x,
    368                     mTask.samples[0][last_accel_sample_index].y,
    369                     mTask.samples[0][last_accel_sample_index].z);
    370 
    371             addSample(&mTask.sensors[LINEAR],
    372                     mTask.samples[0][last_accel_sample_index].time,
    373                     a.x - g.x,
    374                     a.y - g.y,
    375                     a.z - g.z);
    376         }
    377     }
    378 
    379     if (fusionHasEstimate(&mTask.fusion)) {
    380         fusionGetAttitude(&mTask.fusion, &attitude);
    381 
    382         if (mTask.sensors[ORIENT].active) {
    383             fusionGetRotationMatrix(&mTask.fusion, &R);
    384             // x, y, z = yaw, pitch, roll
    385             float x = atan2f(-R.elem[0][1], R.elem[0][0]) * kRad2deg;
    386             float y = atan2f(-R.elem[1][2], R.elem[2][2]) * kRad2deg;
    387             float z = asinf(R.elem[0][2]) * kRad2deg;
    388 
    389             if (x < 0.0f) {
    390                 x += 360.0f;
    391             }
    392 
    393             addSample(&mTask.sensors[ORIENT],
    394                     last_sensor_time, x, y, z);
    395         }
    396 
    397         if (mTask.sensors[GEOMAG].active) {
    398             addSample(&mTask.sensors[GEOMAG],
    399                     last_sensor_time,
    400                     attitude.x,
    401                     attitude.y,
    402                     attitude.z);
    403         }
    404 
    405         if (mTask.sensors[ROTAT].active) {
    406             addSample(&mTask.sensors[ROTAT],
    407                     last_sensor_time,
    408                     attitude.x,
    409                     attitude.y,
    410                     attitude.z);
    411         }
    412 
    413     }
    414 }
    415 
    416 static void drainSamples()
    417 {
    418     size_t i = mTask.sample_indices[ACC];
    419     size_t j = 0;
    420     size_t k = 0;
    421     size_t which;
    422     struct Vec3 a, w, m;
    423     float dT;
    424     uint64_t a_time, g_time, m_time;
    425 
    426     if (mTask.gyro_client_cnt > 0)
    427         j = mTask.sample_indices[GYR];
    428 
    429     if (mTask.mag_client_cnt > 0)
    430         k = mTask.sample_indices[MAG];
    431 
    432     while (mTask.sample_counts[ACC] > 0
    433             && (!(mTask.gyro_client_cnt > 0) || mTask.sample_counts[GYR] > 0)
    434             && (!(mTask.mag_client_cnt > 0) || mTask.sample_counts[MAG] > 0)) {
    435         a_time = mTask.samples[ACC][i].time;
    436         g_time = mTask.gyro_client_cnt > 0 ? mTask.samples[GYR][j].time
    437                             : ULONG_LONG_MAX;
    438         m_time = mTask.mag_client_cnt > 0 ? mTask.samples[MAG][k].time
    439                             : ULONG_LONG_MAX;
    440 
    441         // priority with same timestamp: gyro > acc > mag
    442         if (g_time <= a_time && g_time <= m_time) {
    443             which = GYR;
    444         } else if (a_time <= m_time) {
    445             which = ACC;
    446         } else {
    447             which = MAG;
    448         }
    449 
    450         dT = floatFromUint64(mTask.ResamplePeriodNs[which]) * 1e-9f;
    451         switch (which) {
    452         case ACC:
    453             initVec3(&a, mTask.samples[ACC][i].x, mTask.samples[ACC][i].y, mTask.samples[ACC][i].z);
    454 
    455             if (mTask.flags & FUSION_FLAG_ENABLED)
    456                 fusionHandleAcc(&mTask.fusion, &a, dT);
    457 
    458             if (mTask.flags & FUSION_FLAG_GAME_ENABLED)
    459                 fusionHandleAcc(&mTask.game, &a, dT);
    460 
    461             updateOutput(i, mTask.samples[ACC][i].time);
    462 
    463             --mTask.sample_counts[ACC];
    464             if (++i == MAX_NUM_SAMPLES)
    465                 i = 0;
    466             break;
    467         case GYR:
    468             initVec3(&w, mTask.samples[GYR][j].x, mTask.samples[GYR][j].y, mTask.samples[GYR][j].z);
    469 
    470             if (mTask.flags & FUSION_FLAG_ENABLED)
    471                 fusionHandleGyro(&mTask.fusion, &w, dT);
    472 
    473             if (mTask.flags & FUSION_FLAG_GAME_ENABLED)
    474                 fusionHandleGyro(&mTask.game, &w, dT);
    475 
    476             --mTask.sample_counts[GYR];
    477             if (++j == MAX_NUM_SAMPLES)
    478                 j = 0;
    479             break;
    480         case MAG:
    481             initVec3(&m, mTask.samples[MAG][k].x, mTask.samples[MAG][k].y, mTask.samples[MAG][k].z);
    482 
    483             fusionHandleMag(&mTask.fusion, &m, dT);
    484 
    485             --mTask.sample_counts[MAG];
    486             if (++k == MAX_NUM_SAMPLES)
    487                 k = 0;
    488             break;
    489         }
    490     }
    491 
    492     mTask.sample_indices[ACC] = i;
    493 
    494     if (mTask.gyro_client_cnt > 0)
    495         mTask.sample_indices[GYR] = j;
    496 
    497     if (mTask.mag_client_cnt > 0)
    498         mTask.sample_indices[MAG] = k;
    499 
    500     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    501         if (mTask.sensors[i].ev != NULL) {
    502             osEnqueueEvtOrFree(EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(mSi[i].sensorType),
    503                                mTask.sensors[i].ev, dataEvtFree);
    504             mTask.sensors[i].ev = NULL;
    505         }
    506     }
    507 }
    508 
    509 static void configureFusion()
    510 {
    511     if (mTask.sensors[ORIENT].active
    512             || mTask.sensors[ROTAT].active
    513             || mTask.sensors[GEOMAG].active) {
    514         mTask.flags |= FUSION_FLAG_ENABLED;
    515         initFusion(&mTask.fusion,
    516                 (mTask.mag_client_cnt > 0 ? FUSION_USE_MAG : 0) |
    517                 (mTask.gyro_client_cnt > 0 ? FUSION_USE_GYRO : 0) |
    518                 ((mTask.flags & FUSION_FLAG_INITIALIZED) ? 0 : FUSION_REINITIALIZE));
    519         mTask.flags |= FUSION_FLAG_INITIALIZED;
    520     } else {
    521         mTask.flags &= ~FUSION_FLAG_ENABLED;
    522         mTask.flags &= ~FUSION_FLAG_INITIALIZED;
    523     }
    524 }
    525 
    526 static void configureGame()
    527 {
    528     if (mTask.sensors[GAME].active || mTask.sensors[GRAVITY].active ||
    529             mTask.sensors[LINEAR].active) {
    530         mTask.flags |= FUSION_FLAG_GAME_ENABLED;
    531         initFusion(&mTask.game, FUSION_USE_GYRO |
    532                 ((mTask.flags & FUSION_FLAG_INITIALIZED) ? 0 : FUSION_REINITIALIZE));
    533         mTask.flags |= FUSION_FLAG_GAME_INITIALIZED;
    534     } else {
    535         mTask.flags &= ~FUSION_FLAG_GAME_ENABLED;
    536         mTask.flags &= ~FUSION_FLAG_GAME_INITIALIZED;
    537     }
    538 }
    539 
    540 static void fusionSetRateAcc(void)
    541 {
    542     int i;
    543     if  (mTask.accelHandle == 0) {
    544         mTask.sample_counts[ACC] = 0;
    545         mTask.sample_indices[ACC] = 0;
    546         mTask.counters[ACC] = 0;
    547         mTask.last_time[ACC] = ULONG_LONG_MAX;
    548         for (i = 0; sensorFind(SENS_TYPE_ACCEL, i, &mTask.accelHandle) != NULL; i++) {
    549             if (sensorRequest(mTask.tid, mTask.accelHandle, mTask.raw_sensor_rate[ACC],
    550                         mTask.raw_sensor_latency)) {
    551                 osEventSubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
    552                 break;
    553             }
    554         }
    555     } else {
    556         sensorRequestRateChange(mTask.tid, mTask.accelHandle, mTask.raw_sensor_rate[ACC],
    557                 mTask.raw_sensor_latency);
    558     }
    559 }
    560 
    561 static void fusionSetRateGyr(void)
    562 {
    563     int i;
    564     if (mTask.gyroHandle == 0) {
    565         mTask.sample_counts[GYR] = 0;
    566         mTask.sample_indices[GYR] = 0;
    567         mTask.counters[GYR] = 0;
    568         mTask.last_time[GYR] = ULONG_LONG_MAX;
    569         for (i = 0; sensorFind(SENS_TYPE_GYRO, i, &mTask.gyroHandle) != NULL; i++) {
    570             if (sensorRequest(mTask.tid, mTask.gyroHandle, mTask.raw_sensor_rate[GYR],
    571                         mTask.raw_sensor_latency)) {
    572                 osEventSubscribe(mTask.tid, EVT_SENSOR_GYR_DATA_RDY);
    573                 break;
    574             }
    575         }
    576     } else {
    577         sensorRequestRateChange(mTask.tid, mTask.gyroHandle, mTask.raw_sensor_rate[GYR],
    578                 mTask.raw_sensor_latency);
    579     }
    580 }
    581 
    582 static void fusionSetRateMag(void)
    583 {
    584     int i;
    585     if (mTask.magHandle == 0) {
    586         mTask.sample_counts[MAG] = 0;
    587         mTask.sample_indices[MAG] = 0;
    588         mTask.counters[MAG] = 0;
    589         mTask.last_time[MAG] = ULONG_LONG_MAX;
    590         for (i = 0; sensorFind(SENS_TYPE_MAG, i, &mTask.magHandle) != NULL; i++) {
    591             if (sensorRequest(mTask.tid, mTask.magHandle, mTask.raw_sensor_rate[MAG],
    592                         mTask.raw_sensor_latency)) {
    593                 osEventSubscribe(mTask.tid, EVT_SENSOR_MAG_DATA_RDY);
    594                 osEventSubscribe(mTask.tid, EVT_SENSOR_MAG_BIAS);
    595                 break;
    596             }
    597         }
    598     } else {
    599         sensorRequestRateChange(mTask.tid, mTask.magHandle, mTask.raw_sensor_rate[MAG],
    600                 mTask.raw_sensor_latency);
    601     }
    602 }
    603 
    604 static bool fusionSetRate(uint32_t rate, uint64_t latency, void *cookie)
    605 {
    606     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    607     int i;
    608     uint32_t max_rate = 0;
    609     uint32_t gyr_rate, mag_rate;
    610     uint64_t min_resample_period = ULONG_LONG_MAX;
    611 
    612     mSensor->rate = rate;
    613     mSensor->latency = latency;
    614 
    615     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    616         if (mTask.sensors[i].active) {
    617             max_rate = max_rate > mTask.sensors[i].rate ? max_rate : mTask.sensors[i].rate;
    618         }
    619     }
    620 
    621     if (mTask.accel_client_cnt > 0) {
    622         mTask.raw_sensor_rate[ACC] = max_rate;
    623         mTask.ResamplePeriodNs[ACC] = sensorTimerLookupCommon(FusionRates, rateTimerVals, max_rate);
    624         min_resample_period = mTask.ResamplePeriodNs[ACC] < min_resample_period ?
    625             mTask.ResamplePeriodNs[ACC] : min_resample_period;
    626     }
    627 
    628     if (mTask.gyro_client_cnt > 0) {
    629         gyr_rate = max_rate > MIN_GYRO_RATE_HZ ? max_rate : MIN_GYRO_RATE_HZ;
    630         mTask.raw_sensor_rate[GYR] = gyr_rate;
    631         mTask.ResamplePeriodNs[GYR] = sensorTimerLookupCommon(FusionRates, rateTimerVals, gyr_rate);
    632         min_resample_period = mTask.ResamplePeriodNs[GYR] < min_resample_period ?
    633             mTask.ResamplePeriodNs[GYR] : min_resample_period;
    634     }
    635 
    636     if (mTask.mag_client_cnt > 0) {
    637         mag_rate = max_rate < MAX_MAG_RATE_HZ ? max_rate : MAX_MAG_RATE_HZ;
    638         mTask.raw_sensor_rate[MAG] = mag_rate;
    639         mTask.ResamplePeriodNs[MAG] = sensorTimerLookupCommon(FusionRates, rateTimerVals, mag_rate);
    640         min_resample_period = mTask.ResamplePeriodNs[MAG] < min_resample_period ?
    641             mTask.ResamplePeriodNs[MAG] : min_resample_period;
    642     }
    643 
    644     // This guarantees that local raw sensor FIFOs won't overflow.
    645     mTask.raw_sensor_latency = min_resample_period * (FIFO_DEPTH - 1);
    646 
    647     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    648         if (mTask.sensors[i].active) {
    649             mTask.raw_sensor_latency = mTask.sensors[i].latency < mTask.raw_sensor_latency ?
    650                 mTask.sensors[i].latency : mTask.raw_sensor_latency;
    651         }
    652     }
    653 
    654     if (mTask.accel_client_cnt > 0)
    655         fusionSetRateAcc();
    656     if (mTask.gyro_client_cnt > 0)
    657         fusionSetRateGyr();
    658     if (mTask.mag_client_cnt > 0)
    659         fusionSetRateMag();
    660     if (mSensor->rate > 0)
    661         sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
    662 
    663     return true;
    664 }
    665 
    666 static bool fusionPower(bool on, void *cookie)
    667 {
    668     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    669     int idx;
    670 
    671     mSensor->active = on;
    672     if (on == false) {
    673         mTask.accel_client_cnt--;
    674         if (mSensor->use_gyro_data)
    675             mTask.gyro_client_cnt--;
    676         if (mSensor->use_mag_data)
    677             mTask.mag_client_cnt--;
    678 
    679         // if client_cnt == 0 and Handle == 0, nothing need to be done.
    680         // if client_cnt > 0 and Handle == 0, something else is turning it on, all will be done.
    681         if (mTask.accel_client_cnt == 0 && mTask.accelHandle != 0) {
    682             sensorRelease(mTask.tid, mTask.accelHandle);
    683             mTask.accelHandle = 0;
    684             osEventUnsubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
    685         }
    686 
    687         if (mTask.gyro_client_cnt == 0 && mTask.gyroHandle != 0) {
    688             sensorRelease(mTask.tid, mTask.gyroHandle);
    689             mTask.gyroHandle = 0;
    690             osEventUnsubscribe(mTask.tid, EVT_SENSOR_GYR_DATA_RDY);
    691         }
    692 
    693         if (mTask.mag_client_cnt == 0 && mTask.magHandle != 0) {
    694             sensorRelease(mTask.tid, mTask.magHandle);
    695             mTask.magHandle = 0;
    696             osEventUnsubscribe(mTask.tid, EVT_SENSOR_MAG_DATA_RDY);
    697         }
    698 
    699         idx = mSensor->idx;
    700         (void) fusionSetRate(0, ULONG_LONG_MAX, (void *)idx);
    701     } else {
    702         mTask.accel_client_cnt++;
    703         if (mSensor->use_gyro_data)
    704             mTask.gyro_client_cnt++;
    705         if (mSensor->use_mag_data)
    706             mTask.mag_client_cnt++;
    707     }
    708 
    709     configureFusion();
    710     configureGame();
    711     sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
    712 
    713     return true;
    714 }
    715 
    716 static bool fusionFirmwareUpload(void *cookie)
    717 {
    718     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    719 
    720     sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
    721     return true;
    722 }
    723 
    724 static bool fusionFlush(void *cookie)
    725 {
    726     struct FusionSensor *mSensor = &mTask.sensors[(int)cookie];
    727     uint32_t evtType = sensorGetMyEventType(mSi[mSensor->idx].sensorType);
    728 
    729     osEnqueueEvt(evtType, SENSOR_DATA_EVENT_FLUSH, NULL);
    730     return true;
    731 }
    732 
    733 static void fusionHandleEvent(uint32_t evtType, const void* evtData)
    734 {
    735     struct TripleAxisDataEvent *ev;
    736     int i;
    737 
    738     if (evtData == SENSOR_DATA_EVENT_FLUSH)
    739         return;
    740 
    741     switch (evtType) {
    742     case EVT_APP_START:
    743         // check for gyro and mag
    744         osEventUnsubscribe(mTask.tid, EVT_APP_START);
    745         if (!sensorFind(SENS_TYPE_GYRO, 0, &mTask.gyroHandle)) {
    746             for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++)
    747                 mTask.sensors[i].use_gyro_data = false;
    748         }
    749         mTask.gyroHandle = 0;
    750         if (!sensorFind(SENS_TYPE_MAG, 0, &mTask.magHandle)) {
    751             for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++)
    752                 mTask.sensors[i].use_mag_data = false;
    753         }
    754         mTask.magHandle = 0;
    755         break;
    756     case EVT_SENSOR_ACC_DATA_RDY:
    757         ev = (struct TripleAxisDataEvent *)evtData;
    758         fillSamples(ev, ACC);
    759         drainSamples();
    760         break;
    761     case EVT_SENSOR_GYR_DATA_RDY:
    762         ev = (struct TripleAxisDataEvent *)evtData;
    763         fillSamples(ev, GYR);
    764         drainSamples();
    765         break;
    766     case EVT_SENSOR_MAG_BIAS:
    767         ev = (struct TripleAxisDataEvent *)evtData;
    768         if (ev->samples[0].firstSample.biasPresent && mTask.flags & FUSION_FLAG_ENABLED) {
    769             //it is a user initiated mag cal event
    770             fusionSetMagTrust(&mTask.fusion, MANUAL_MAG_CAL);
    771         }
    772         break;
    773     case EVT_SENSOR_MAG_DATA_RDY:
    774         ev = (struct TripleAxisDataEvent *)evtData;
    775         fillSamples(ev, MAG);
    776         drainSamples();
    777         break;
    778     }
    779 }
    780 
    781 static const struct SensorOps mSops =
    782 {
    783     .sensorPower = fusionPower,
    784     .sensorFirmwareUpload = fusionFirmwareUpload,
    785     .sensorSetRate = fusionSetRate,
    786     .sensorFlush = fusionFlush,
    787 };
    788 
    789 static bool fusionStart(uint32_t tid)
    790 {
    791     size_t i, slabSize;
    792 
    793     mTask.tid = tid;
    794     mTask.flags = 0;
    795 
    796     for (i = 0; i < NUM_OF_RAW_SENSOR; i++) {
    797          mTask.sample_counts[i] = 0;
    798          mTask.sample_indices[i] = 0;
    799     }
    800 
    801     for (i = ORIENT; i < NUM_OF_FUSION_SENSOR; i++) {
    802         mTask.sensors[i].handle = sensorRegister(&mSi[i], &mSops, (void *)i, true);
    803         mTask.sensors[i].idx = i;
    804         mTask.sensors[i].use_gyro_data = true;
    805         mTask.sensors[i].use_mag_data = true;
    806     }
    807 
    808     mTask.sensors[GEOMAG].use_gyro_data = false;
    809     mTask.sensors[GAME].use_mag_data = false;
    810     mTask.sensors[GRAVITY].use_mag_data = false;
    811     mTask.sensors[LINEAR].use_mag_data = false;
    812 
    813     mTask.accel_client_cnt = 0;
    814     mTask.gyro_client_cnt = 0;
    815     mTask.mag_client_cnt = 0;
    816 
    817     slabSize = sizeof(struct TripleAxisDataEvent)
    818         + MAX_NUM_COMMS_EVENT_SAMPLES * sizeof(struct TripleAxisDataPoint);
    819 
    820     // worst case 6 output sensors * (N + 1) comms_events
    821     mDataSlab = slabAllocatorNew(slabSize, 4, 6 * (NUM_COMMS_EVENTS_IN_FIFO + 1));
    822     if (!mDataSlab) {
    823         osLog(LOG_ERROR, "ORIENTATION: slabAllocatorNew() FAILED\n");
    824         return false;
    825     }
    826 
    827     osEventSubscribe(mTask.tid, EVT_APP_START);
    828 
    829     return true;
    830 }
    831 
    832 static void fusionEnd()
    833 {
    834     mTask.flags &= ~FUSION_FLAG_INITIALIZED;
    835     mTask.flags &= ~FUSION_FLAG_GAME_INITIALIZED;
    836     slabAllocatorDestroy(mDataSlab);
    837 }
    838 
    839 INTERNAL_APP_INIT(
    840         APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 4),
    841         ORIENTATION_APP_VERSION,
    842         fusionStart,
    843         fusionEnd,
    844         fusionHandleEvent);
    845