diff options
Diffstat (limited to 'sensors')
-rw-r--r-- | sensors/Android.mk | 8 | ||||
-rw-r--r-- | sensors/sensors_stub.c | 60 | ||||
-rw-r--r-- | sensors/sensors_trout.c | 599 |
3 files changed, 667 insertions, 0 deletions
diff --git a/sensors/Android.mk b/sensors/Android.mk new file mode 100644 index 0000000..00fdbad --- /dev/null +++ b/sensors/Android.mk @@ -0,0 +1,8 @@ +# Copyright 2008 The Android Open Source Project + +ifeq ($(TARGET_PRODUCT),dream) +LOCAL_SRC_FILES += sensors/sensors_trout.c +else +LOCAL_SRC_FILES += sensors/sensors_stub.c +endif + diff --git a/sensors/sensors_stub.c b/sensors/sensors_stub.c new file mode 100644 index 0000000..c096cf2 --- /dev/null +++ b/sensors/sensors_stub.c @@ -0,0 +1,60 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hardware/sensors.h> +#include <unistd.h> + + +uint32_t sensors_control_init() { + return 0; +} + +/* returns a fd where to read the events from. the caller takes + * ownership of this fd */ +int sensors_control_open() { + return -1; +} + +/* returns a bitmask indicating which sensors are enabled */ +uint32_t sensors_control_activate(uint32_t sensors, uint32_t mask) { + return 0; +} + +/* set the delay between sensor events in ms */ +int sensors_control_delay(int32_t ms) { + return -1; +} + +/* initialize the sensor data read. doesn't take ownership of the fd */ +int sensors_data_open(int fd) { + return 0; +} + +/* done with sensor data */ +int sensors_data_close() { + return -1; +} + +/* returns a bitmask indicating which sensors have changed */ +int sensors_data_poll(sensors_data_t* data, uint32_t sensors_of_interest) { + usleep(60000000); + return 0; +} + +/* returns available sensors */ +uint32_t sensors_data_get_sensors() { + return 0; +} diff --git a/sensors/sensors_trout.c b/sensors/sensors_trout.c new file mode 100644 index 0000000..f3c0b3a --- /dev/null +++ b/sensors/sensors_trout.c @@ -0,0 +1,599 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Sensors" + +#include <hardware/sensors.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <math.h> +#include <poll.h> + +#include <linux/input.h> +#include <linux/akm8976.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> + +/*****************************************************************************/ + +#define AKM_DEVICE_NAME "/dev/akm8976_aot" + +#define SUPPORTED_SENSORS (SENSORS_ORIENTATION | \ + SENSORS_ACCELERATION | \ + SENSORS_MAGNETIC_FIELD | \ + SENSORS_ORIENTATION_RAW) + + +// sensor IDs must be a power of two and +// must match values in SensorManager.java +#define EVENT_TYPE_ACCEL_X ABS_X +#define EVENT_TYPE_ACCEL_Y ABS_Z +#define EVENT_TYPE_ACCEL_Z ABS_Y +#define EVENT_TYPE_ACCEL_STATUS ABS_WHEEL + +#define EVENT_TYPE_YAW ABS_RX +#define EVENT_TYPE_PITCH ABS_RY +#define EVENT_TYPE_ROLL ABS_RZ +#define EVENT_TYPE_ORIENT_STATUS ABS_RUDDER + +#define EVENT_TYPE_MAGV_X ABS_HAT0X +#define EVENT_TYPE_MAGV_Y ABS_HAT0Y +#define EVENT_TYPE_MAGV_Z ABS_BRAKE + +#define EVENT_TYPE_TEMPERATURE ABS_THROTTLE +#define EVENT_TYPE_STEP_COUNT ABS_GAS + +// 720 LSG = 1G +#define LSG (720.0f) + +// conversion of acceleration data to SI units (m/s^2) +#define CONVERT_A (GRAVITY_EARTH / LSG) +#define CONVERT_A_X (CONVERT_A) +#define CONVERT_A_Y (-CONVERT_A) +#define CONVERT_A_Z (CONVERT_A) + +// conversion of magnetic data to uT units +#define CONVERT_M (1.0f/16.0f) +#define CONVERT_M_X (CONVERT_M) +#define CONVERT_M_Y (CONVERT_M) +#define CONVERT_M_Z (CONVERT_M) + +#define SENSOR_STATE_MASK (0x7FFF) + +/*****************************************************************************/ + +static int sAkmFD = -1; +static uint32_t sActiveSensors = 0; + +/*****************************************************************************/ + +/* + * We use a Least Mean Squares filter to smooth out the output of the yaw + * sensor. + * + * The goal is to estimate the output of the sensor based on previous acquired + * samples. + * + * We approximate the input by a line with the equation: + * Z(t) = a * t + b + * + * We use the Least Mean Squares method to calculate a and b so that the + * distance between the line and the measured COUNT inputs Z(t) is minimal. + * + * In practice we only need to compute b, which is the value we're looking for + * (it's the estimated Z at t=0). However, to improve the latency a little bit, + * we're going to discard a certain number of samples that are too far from + * the estimated line and compute b again with the new (trimmed down) samples. + * + * notes: + * 'a' is the slope of the line, and physicaly represent how fast the input + * is changing. In our case, how fast the yaw is changing, that is, how fast the + * user is spinning the device (in degre / nanosecond). This value should be + * zero when the device is not moving. + * + * The minimum distance between the line and the samples (which we are not + * explicitely computing here), is an indication of how bad the samples are + * and gives an idea of the "quality" of the estimation (well, really of the + * sensor values). + * + */ + +/* sensor rate in me */ +#define SENSORS_RATE_MS 20 +/* timeout (constant value) in ms */ +#define SENSORS_TIMEOUT_MS 100 +/* # of samples to look at in the past for filtering */ +#define COUNT 24 +/* prediction ratio */ +#define PREDICTION_RATIO (1.0f/3.0f) +/* prediction time in seconds (>=0) */ +#define PREDICTION_TIME ((SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO) + +static float mV[COUNT*2]; +static float mT[COUNT*2]; +static int mIndex; + +static inline +float normalize(float x) +{ + x *= (1.0f / 360.0f); + if (fabsf(x) >= 0.5f) + x = x - ceilf(x + 0.5f) + 1.0f; + if (x < 0) + x += 1.0f; + x *= 360.0f; + return x; +} + +static void LMSInit(void) +{ + memset(mV, 0, sizeof(mV)); + memset(mT, 0, sizeof(mT)); + mIndex = COUNT; +} + +static float LMSFilter(int64_t time, int v) +{ + const float ns = 1.0f / 1000000000.0f; + const float t = time*ns; + float v1 = mV[mIndex]; + if ((v-v1) > 180) { + v -= 360; + } else if ((v1-v) > 180) { + v += 360; + } + /* Manage the circular buffer, we write the data twice spaced by COUNT + * values, so that we don't have to memcpy() the array when it's full */ + mIndex++; + if (mIndex >= COUNT*2) + mIndex = COUNT; + mV[mIndex] = v; + mT[mIndex] = t; + mV[mIndex-COUNT] = v; + mT[mIndex-COUNT] = t; + + float A, B, C, D, E; + float a, b; + int i; + + A = B = C = D = E = 0; + for (i=0 ; i<COUNT-1 ; i++) { + const int j = mIndex - 1 - i; + const float Z = mV[j]; + const float T = 0.5f*(mT[j] + mT[j+1]) - t; + float dT = mT[j] - mT[j+1]; + dT *= dT; + A += Z*dT; + B += T*(T*dT); + C += (T*dT); + D += Z*(T*dT); + E += dT; + } + b = (A*B + C*D) / (E*B + C*C); + a = (E*b - A) / C; + float f = b + PREDICTION_TIME*a; + + //LOGD("A=%f, B=%f, C=%f, D=%f, E=%f", A,B,C,D,E); + //LOGD("%lld %d %f %f", time, v, f, a); + + f = normalize(f); + return f; +} + +/*****************************************************************************/ + +static int open_input() +{ + /* scan all input drivers and look for "compass" */ + int fd = -1; + const char *dirname = "/dev/input"; + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + fd = open(devname, O_RDONLY); + if (fd>=0) { + char name[80]; + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + name[0] = '\0'; + } + if (!strcmp(name, "compass")) { + LOGD("using %s (name=%s)", devname, name); + break; + } + close(fd); + fd = -1; + } + } + closedir(dir); + + if (fd < 0) { + LOGE("Couldn't find or open 'compass' driver (%s)", strerror(errno)); + } + return fd; +} + +static int open_akm() +{ + if (sAkmFD <= 0) { + sAkmFD = open(AKM_DEVICE_NAME, O_RDONLY); + LOGD("%s, fd=%d", __PRETTY_FUNCTION__, sAkmFD); + LOGE_IF(sAkmFD<0, "Couldn't open %s (%s)", + AKM_DEVICE_NAME, strerror(errno)); + if (sAkmFD >= 0) { + sActiveSensors = 0; + } + } + return sAkmFD; +} + +static void close_akm() +{ + if (sAkmFD > 0) { + LOGD("%s, fd=%d", __PRETTY_FUNCTION__, sAkmFD); + close(sAkmFD); + sAkmFD = -1; + } +} + +static void enable_disable(int fd, uint32_t sensors, uint32_t mask) +{ + if (fd<0) return; + short flags; + + if (sensors & SENSORS_ORIENTATION_RAW) { + sensors |= SENSORS_ORIENTATION; + mask |= SENSORS_ORIENTATION; + } else if (mask & SENSORS_ORIENTATION_RAW) { + mask |= SENSORS_ORIENTATION; + } + + if (mask & SENSORS_ORIENTATION) { + flags = (sensors & SENSORS_ORIENTATION) ? 1 : 0; + if (ioctl(fd, ECS_IOCTL_APP_SET_MFLAG, &flags) < 0) { + LOGE("ECS_IOCTL_APP_SET_MFLAG error (%s)", strerror(errno)); + } + } + if (mask & SENSORS_ACCELERATION) { + flags = (sensors & SENSORS_ACCELERATION) ? 1 : 0; + if (ioctl(fd, ECS_IOCTL_APP_SET_AFLAG, &flags) < 0) { + LOGE("ECS_IOCTL_APP_SET_AFLAG error (%s)", strerror(errno)); + } + } + if (mask & SENSORS_TEMPERATURE) { + flags = (sensors & SENSORS_TEMPERATURE) ? 1 : 0; + if (ioctl(fd, ECS_IOCTL_APP_SET_TFLAG, &flags) < 0) { + LOGE("ECS_IOCTL_APP_SET_TFLAG error (%s)", strerror(errno)); + } + } +#ifdef ECS_IOCTL_APP_SET_MVFLAG + if (mask & SENSORS_MAGNETIC_FIELD) { + flags = (sensors & SENSORS_MAGNETIC_FIELD) ? 1 : 0; + if (ioctl(fd, ECS_IOCTL_APP_SET_MVFLAG, &flags) < 0) { + LOGE("ECS_IOCTL_APP_SET_MVFLAG error (%s)", strerror(errno)); + } + } +#endif +} + +static uint32_t read_sensors_state(int fd) +{ + if (fd<0) return 0; + short flags; + uint32_t sensors = 0; + // read the actual value of all sensors + if (!ioctl(fd, ECS_IOCTL_APP_GET_MFLAG, &flags)) { + if (flags) sensors |= SENSORS_ORIENTATION; + else sensors &= ~SENSORS_ORIENTATION; + } + if (!ioctl(fd, ECS_IOCTL_APP_GET_AFLAG, &flags)) { + if (flags) sensors |= SENSORS_ACCELERATION; + else sensors &= ~SENSORS_ACCELERATION; + } + if (!ioctl(fd, ECS_IOCTL_APP_GET_TFLAG, &flags)) { + if (flags) sensors |= SENSORS_TEMPERATURE; + else sensors &= ~SENSORS_TEMPERATURE; + } +#ifdef ECS_IOCTL_APP_SET_MVFLAG + if (!ioctl(fd, ECS_IOCTL_APP_GET_MVFLAG, &flags)) { + if (flags) sensors |= SENSORS_MAGNETIC_FIELD; + else sensors &= ~SENSORS_MAGNETIC_FIELD; + } +#endif + return sensors; +} + +/*****************************************************************************/ + +uint32_t sensors_control_init() +{ + return SUPPORTED_SENSORS; +} + +int sensors_control_open() +{ + return open_input(); +} + +uint32_t sensors_control_activate(uint32_t sensors, uint32_t mask) +{ + mask &= SUPPORTED_SENSORS; + uint32_t active = sActiveSensors; + uint32_t new_sensors = (active & ~mask) | (sensors & mask); + uint32_t changed = active ^ new_sensors; + if (changed) { + int fd = open_akm(); + if (fd < 0) return 0; + + if (!active && new_sensors) { + // force all sensors to be updated + changed = SUPPORTED_SENSORS; + } + + enable_disable(fd, new_sensors, changed); + + if (active && !new_sensors) { + // close the driver + close_akm(); + } + sActiveSensors = active = new_sensors; + LOGD("sensors=%08x, real=%08x", + sActiveSensors, read_sensors_state(fd)); + } + return active; +} + +int sensors_control_delay(int32_t ms) +{ +#ifdef ECS_IOCTL_APP_SET_DELAY + if (sAkmFD <= 0) { + return -1; + } + short delay = ms; + if (!ioctl(sAkmFD, ECS_IOCTL_APP_SET_DELAY, &delay)) { + return -errno; + } + return 0; +#else + return -1; +#endif +} + +/*****************************************************************************/ + +#define MAX_NUM_SENSORS 8 +static int sInputFD = -1; +static const int ID_O = 0; +static const int ID_A = 1; +static const int ID_T = 2; +static const int ID_M = 3; +static const int ID_OR = 7; // orientation raw +static sensors_data_t sSensors[MAX_NUM_SENSORS]; +static uint32_t sPendingSensors; + +int sensors_data_open(int fd) +{ + int i; + LMSInit(); + memset(&sSensors, 0, sizeof(sSensors)); + for (i=0 ; i<MAX_NUM_SENSORS ; i++) { + // by default all sensors have high accuracy + // (we do this because we don't get an update if the value doesn't + // change). + sSensors[i].vector.status = SENSOR_STATUS_ACCURACY_HIGH; + } + sPendingSensors = 0; + sInputFD = dup(fd); + LOGD("sensors_data_open: fd = %d", sInputFD); + return 0; +} + +int sensors_data_close() +{ + close(sInputFD); + sInputFD = -1; + return 0; +} + +static int pick_sensor(sensors_data_t* values) +{ + uint32_t mask = SENSORS_MASK; + while(mask) { + uint32_t i = 31 - __builtin_clz(mask); + mask &= ~(1<<i); + if (sPendingSensors & (1<<i)) { + sPendingSensors &= ~(1<<i); + *values = sSensors[i]; + values->sensor = (1<<i); + LOGD_IF(0, "%d [%f, %f, %f]", (1<<i), + values->vector.x, + values->vector.y, + values->vector.z); + return (1<<i); + } + } + LOGE("No sensor to return!!! sPendingSensors=%08x", sPendingSensors); + // we may end-up in a busy loop, slow things down, just in case. + usleep(100000); + return -1; +} + +int sensors_data_poll(sensors_data_t* values, uint32_t sensors_of_interest) +{ + struct input_event event; + int nread; + int64_t t; + + int fd = sInputFD; + if (fd <= 0) + return -1; + + // there are pending sensors, returns them now... + if (sPendingSensors) { + return pick_sensor(values); + } + + uint32_t new_sensors = 0; + struct pollfd fds; + fds.fd = fd; + fds.events = POLLIN; + fds.revents = 0; + + // wait until we get a complete event for an enabled sensor + while (1) { + nread = 0; + if (sensors_of_interest & SENSORS_ORIENTATION) { + /* We do some special processing if the orientation sensor is + * activated. In particular the yaw value is filtered with a + * LMS filter. Since the kernel only sends an event when the + * value changes, we need to wake up at regular intervals to + * generate an output value (the output value may not be + * constant when the input value is constant) + */ + int err = poll(&fds, 1, SENSORS_TIMEOUT_MS); + if (err == 0) { + struct timespec time; + time.tv_sec = time.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &time); + + /* generate an output value */ + t = time.tv_sec*1000000000LL+time.tv_nsec; + new_sensors |= SENSORS_ORIENTATION; + sSensors[ID_O].orientation.yaw = + LMSFilter(t, sSensors[ID_O].orientation.yaw); + + /* generate a fake sensors event */ + event.type = EV_SYN; + event.time.tv_sec = time.tv_sec; + event.time.tv_usec = time.tv_nsec/1000; + nread = sizeof(event); + } + } + if (nread == 0) { + /* read the next event */ + nread = read(fd, &event, sizeof(event)); + } + if (nread == sizeof(event)) { + uint32_t v; + if (event.type == EV_ABS) { + //LOGD("type: %d code: %d value: %-5d time: %ds", + // event.type, event.code, event.value, + // (int)event.time.tv_sec); + switch (event.code) { + + case EVENT_TYPE_ACCEL_X: + new_sensors |= SENSORS_ACCELERATION; + sSensors[ID_A].acceleration.x = event.value * CONVERT_A_X; + break; + case EVENT_TYPE_ACCEL_Y: + new_sensors |= SENSORS_ACCELERATION; + sSensors[ID_A].acceleration.y = event.value * CONVERT_A_Y; + break; + case EVENT_TYPE_ACCEL_Z: + new_sensors |= SENSORS_ACCELERATION; + sSensors[ID_A].acceleration.z = event.value * CONVERT_A_Z; + break; + + case EVENT_TYPE_MAGV_X: + new_sensors |= SENSORS_MAGNETIC_FIELD; + sSensors[ID_M].magnetic.x = event.value * CONVERT_M_X; + break; + case EVENT_TYPE_MAGV_Y: + new_sensors |= SENSORS_MAGNETIC_FIELD; + sSensors[ID_M].magnetic.y = event.value * CONVERT_M_Y; + break; + case EVENT_TYPE_MAGV_Z: + new_sensors |= SENSORS_MAGNETIC_FIELD; + sSensors[ID_M].magnetic.z = event.value * CONVERT_M_Z; + break; + + case EVENT_TYPE_YAW: + new_sensors |= SENSORS_ORIENTATION | SENSORS_ORIENTATION_RAW; + t = event.time.tv_sec*1000000000LL + + event.time.tv_usec*1000; + sSensors[ID_O].orientation.yaw = + (sensors_of_interest & SENSORS_ORIENTATION) ? + LMSFilter(t, event.value) : event.value; + sSensors[ID_OR].orientation.yaw = event.value; + break; + case EVENT_TYPE_PITCH: + new_sensors |= SENSORS_ORIENTATION | SENSORS_ORIENTATION_RAW; + sSensors[ID_O].orientation.pitch = event.value; + sSensors[ID_OR].orientation.pitch = event.value; + break; + case EVENT_TYPE_ROLL: + new_sensors |= SENSORS_ORIENTATION | SENSORS_ORIENTATION_RAW; + sSensors[ID_O].orientation.roll = event.value; + sSensors[ID_OR].orientation.roll = event.value; + break; + + case EVENT_TYPE_TEMPERATURE: + new_sensors |= SENSORS_TEMPERATURE; + sSensors[ID_T].temperature = event.value; + break; + + case EVENT_TYPE_STEP_COUNT: + // step count (only reported in MODE_FFD) + // we do nothing with it for now. + break; + case EVENT_TYPE_ACCEL_STATUS: + // accuracy of the calibration (never returned!) + //LOGD("G-Sensor status %d", event.value); + break; + case EVENT_TYPE_ORIENT_STATUS: + // accuracy of the calibration + v = (uint32_t)(event.value & SENSOR_STATE_MASK); + LOGD_IF(sSensors[ID_O].orientation.status != (uint8_t)v, + "M-Sensor status %d", v); + sSensors[ID_O].orientation.status = (uint8_t)v; + sSensors[ID_OR].orientation.status = (uint8_t)v; + break; + } + } else if (event.type == EV_SYN) { + if (new_sensors) { + sPendingSensors = new_sensors; + int64_t t = event.time.tv_sec*1000000000LL + + event.time.tv_usec*1000; + while (new_sensors) { + uint32_t i = 31 - __builtin_clz(new_sensors); + new_sensors &= ~(1<<i); + sSensors[i].time = t; + } + return pick_sensor(values); + } + } + } + } +} + +uint32_t sensors_data_get_sensors() { + return SUPPORTED_SENSORS; +} + |