sim.android.hardware.SensorManager Maven / Gradle / Ivy
package sim.android.hardware;
/*
* Copyright (C) 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.
*/
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import sim.android.hardware.service.impl.SensorEventService;
/**
*
* SensorManager lets you access the device's {@link android.hardware.Sensor
* sensors}. Get an instance of this class by calling null {@link android.content.Context#getSystemService(java.lang.String)
* Context.getSystemService()} with the argument
* {@link android.content.Context#SENSOR_SERVICE}.
*
*
* Always make sure to disable sensors you don't need, especially when your
* activity is paused. Failing to do so can drain the battery in just a few
* hours. Note that the system will not disable sensors automatically
* when the screen turns off.
*
*
*
* public class SensorActivity extends Activity, implements SensorEventListener
* { private final SensorManager mSensorManager; private final Sensor
* mAccelerometer;
*
* public SensorActivity() { mSensorManager =
* (SensorManager)getSystemService(SENSOR_SERVICE); mAccelerometer =
* mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); }
*
* protected void onResume() { super.onResume();
* mSensorManager.registerListener(this, mAccelerometer,
* SensorManager.SENSOR_DELAY_NORMAL); }
*
* protected void onPause() { super.onPause();
* mSensorManager.unregisterListener(this); }
*
* public void onAccuracyChanged(Sensor sensor, int accuracy) { }
*
* public void onSensorChanged(SensorEvent event) { } }
*
*
* @see SensorEventListener
* @see SensorEvent
* @see Sensor
*
*/
public class SensorManager {
private static final String TAG = "SensorManager";
private static final float[] mTempMatrix = new float[16];
/* NOTE: sensor IDs must be a power of 2 */
/**
* A constant describing an orientation sensor. See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_ORIENTATION = 1 << 0;
/**
* A constant describing an accelerometer. See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_ACCELEROMETER = 1 << 1;
/**
* A constant describing a temperature sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_TEMPERATURE = 1 << 2;
/**
* A constant describing a magnetic sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_MAGNETIC_FIELD = 1 << 3;
/**
* A constant describing an ambient light sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_LIGHT = 1 << 4;
/**
* A constant describing a proximity sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_PROXIMITY = 1 << 5;
/**
* A constant describing a Tricorder See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_TRICORDER = 1 << 6;
/**
* A constant describing an orientation sensor. See
* {@link android.hardware.SensorListener SensorListener} for more details.
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_ORIENTATION_RAW = 1 << 7;
/**
* A constant that includes all sensors
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_ALL = 0x7F;
/**
* Smallest sensor ID
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_MIN = SENSOR_ORIENTATION;
/**
* Largest sensor ID
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int SENSOR_MAX = ((SENSOR_ALL + 1) >> 1);
/**
* Index of the X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int DATA_X = 0;
/**
* Index of the Y value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int DATA_Y = 1;
/**
* Index of the Z value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int DATA_Z = 2;
/**
* Offset to the untransformed values in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int RAW_DATA_INDEX = 3;
/**
* Index of the untransformed X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int RAW_DATA_X = 3;
/**
* Index of the untransformed Y value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int RAW_DATA_Y = 4;
/**
* Index of the untransformed Z value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
*
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
public static final int RAW_DATA_Z = 5;
/**
* Standard gravity (g) on Earth. This value is equivalent to 1G
*/
public static final float STANDARD_GRAVITY = 9.80665f;
/**
* Sun's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_SUN = 275.0f;
/**
* Mercury's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_MERCURY = 3.70f;
/**
* Venus' gravity in SI units (m/s^2)
*/
public static final float GRAVITY_VENUS = 8.87f;
/**
* Earth's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_EARTH = 9.80665f;
/**
* The Moon's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_MOON = 1.6f;
/**
* Mars' gravity in SI units (m/s^2)
*/
public static final float GRAVITY_MARS = 3.71f;
/**
* Jupiter's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_JUPITER = 23.12f;
/**
* Saturn's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_SATURN = 8.96f;
/**
* Uranus' gravity in SI units (m/s^2)
*/
public static final float GRAVITY_URANUS = 8.69f;
/**
* Neptune's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_NEPTUNE = 11.0f;
/**
* Pluto's gravity in SI units (m/s^2)
*/
public static final float GRAVITY_PLUTO = 0.6f;
/**
* Gravity (estimate) on the first Death Star in Empire units (m/s^2)
*/
public static final float GRAVITY_DEATH_STAR_I = 0.000000353036145f;
/**
* Gravity on the island
*/
public static final float GRAVITY_THE_ISLAND = 4.815162342f;
/**
* Maximum magnetic field on Earth's surface
*/
public static final float MAGNETIC_FIELD_EARTH_MAX = 60.0f;
/**
* Minimum magnetic field on Earth's surface
*/
public static final float MAGNETIC_FIELD_EARTH_MIN = 30.0f;
/**
* Standard atmosphere, or average sea-level pressure in hPa (millibar)
*/
public static final float PRESSURE_STANDARD_ATMOSPHERE = 1013.25f;
/**
* Maximum luminance of sunlight in lux
*/
public static final float LIGHT_SUNLIGHT_MAX = 120000.0f;
/**
* luminance of sunlight in lux
*/
public static final float LIGHT_SUNLIGHT = 110000.0f;
/**
* luminance in shade in lux
*/
public static final float LIGHT_SHADE = 20000.0f;
/**
* luminance under an overcast sky in lux
*/
public static final float LIGHT_OVERCAST = 10000.0f;
/**
* luminance at sunrise in lux
*/
public static final float LIGHT_SUNRISE = 400.0f;
/**
* luminance under a cloudy sky in lux
*/
public static final float LIGHT_CLOUDY = 100.0f;
/**
* luminance at night with full moon in lux
*/
public static final float LIGHT_FULLMOON = 0.25f;
/**
* luminance at night with no moon in lux
*/
public static final float LIGHT_NO_MOON = 0.001f;
/**
* get sensor data as fast as possible
*/
public static final int SENSOR_DELAY_FASTEST = 0;
/**
* rate suitable for games
*/
public static final int SENSOR_DELAY_GAME = 1;
/**
* rate suitable for the user interface
*/
public static final int SENSOR_DELAY_UI = 2;
/**
* rate (default) suitable for screen orientation changes
*/
public static final int SENSOR_DELAY_NORMAL = 3;
/**
* The values returned by this sensor cannot be trusted, calibration is
* needed or the environment doesn't allow readings
*/
public static final int SENSOR_STATUS_UNRELIABLE = 0;
/**
* This sensor is reporting data with low accuracy, calibration with the
* environment is needed
*/
public static final int SENSOR_STATUS_ACCURACY_LOW = 1;
/**
* This sensor is reporting data with an average level of accuracy,
* calibration with the environment may improve the readings
*/
public static final int SENSOR_STATUS_ACCURACY_MEDIUM = 2;
/**
* This sensor is reporting data with maximum accuracy
*/
public static final int SENSOR_STATUS_ACCURACY_HIGH = 3;
/**
* see {@link #remapCoordinateSystem}
*/
public static final int AXIS_X = 1;
/**
* see {@link #remapCoordinateSystem}
*/
public static final int AXIS_Y = 2;
/**
* see {@link #remapCoordinateSystem}
*/
public static final int AXIS_Z = 3;
/**
* see {@link #remapCoordinateSystem}
*/
public static final int AXIS_MINUS_X = AXIS_X | 0x80;
/**
* see {@link #remapCoordinateSystem}
*/
public static final int AXIS_MINUS_Y = AXIS_Y | 0x80;
/**
* see {@link #remapCoordinateSystem}
*/
public static final int AXIS_MINUS_Z = AXIS_Z | 0x80;
/*-----------------------------------------------------------------------*/
private static boolean sSensorModuleInitialized = false;
private SensorEventService sensorEventService;
private EventHandler mEventHandler;
// Used within this module from outside SensorManager, don't make private
static ArrayList mSensors = new ArrayList();
static final ArrayList mListeners = new ArrayList();
class FullListener {
SensorEventListener sensorEventListener;
int rate;
Handler handler;
Sensor sensor;
}
/**
*
*/
public SensorManager() {
if (!sSensorModuleInitialized) {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(looper);
} else {
mEventHandler = null;
}
sSensorModuleInitialized = true;
// initialize the sensor list
mSensors = new ArrayList();
addAccelerometerSensor();
addMagneticFieldSensor();
addOrientationSensor();
sensorEventService = new SensorEventService(this, mEventHandler);
sensorEventService.start();
}
}
public List getListeners() {
return mListeners;
}
private void addAccelerometerSensor() {
Sensor sensor = new Sensor();
sensor.setType(Sensor.TYPE_ACCELEROMETER);
synchronized (mSensors) {
mSensors.add(sensor);
}
}
private void addMagneticFieldSensor() {
Sensor sensor = new Sensor();
sensor.setType(Sensor.TYPE_MAGNETIC_FIELD);
synchronized (mSensors) {
mSensors.add(sensor);
}
}
private void addOrientationSensor() {
Sensor sensor = new Sensor();
sensor.setType(Sensor.TYPE_ORIENTATION);
sensor.setMaxRange(360.0f);
sensor.setRange(360.0f, 1.0f);
synchronized (mSensors) {
mSensors.add(sensor);
}
}
public class EventHandler extends Handler {
public static final int NEW_VALUE = 0;
public EventHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NEW_VALUE:
notifySensorChangedToListener((SensorEvent) msg.obj);
return;
default:
Log.e(TAG, "Unknown message type " + msg.what);
return;
}
}
}
public void notifySensorChangedToListener(SensorEvent se) {
System.out.println("notifySensorChangedToListener!!");
synchronized (mListeners) {
for (FullListener fl : mListeners) {
if (fl.sensor.getType() == se.sensor.getType()) {
fl.sensorEventListener.onSensorChanged(se);
}
}
}
}
/**
* @return available sensors.
* @deprecated This method is deprecated, use
* {@link SensorManager#getSensorList(int)} instead
*/
@Deprecated
public int getSensors() {
synchronized (mSensors) {
int result = 0;
final ArrayList fullList = mSensors;
for (Sensor i : fullList) {
switch (i.getType()) {
case Sensor.TYPE_ACCELEROMETER:
result |= SensorManager.SENSOR_ACCELEROMETER;
break;
case Sensor.TYPE_MAGNETIC_FIELD:
result |= SensorManager.SENSOR_MAGNETIC_FIELD;
break;
case Sensor.TYPE_ORIENTATION:
result |= SensorManager.SENSOR_ORIENTATION
| SensorManager.SENSOR_ORIENTATION_RAW;
break;
}
}
return result;
}
}
/**
* Use this method to get the list of available sensors of a certain type.
* Make multiple calls to get sensors of different types or use
* {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all the
* sensors.
*
* @param type of sensors requested
*
* @return a list of sensors matching the asked type.
*
* @see #getDefaultSensor(int)
* @see Sensor
*/
public List getSensorList(int type) {
// cache the returned lists the first time
List list = new ArrayList();
synchronized (mSensors) {
if (type == Sensor.TYPE_ALL) {
list.addAll(mSensors);
} else {
for (Sensor s : mSensors) {
if (s.getType() == type) {
list.add(s);
}
}
}
}
return list;
}
/**
* Use this method to get the default sensor for a given type. Note that the
* returned sensor could be a composite sensor, and its data could be
* averaged or filtered. If you need to access the raw sensors use
* {@link SensorManager#getSensorList(int) getSensorList}.
*
* @param type of sensors requested
*
* @return the default sensors matching the asked type.
*
* @see #getSensorList(int)
* @see Sensor
*/
public Sensor getDefaultSensor(int type) {
// TODO: need to be smarter, for now, just return the 1st sensor
List l = getSensorList(type);
return l.isEmpty() ? null : l.get(0);
}
/**
* Registers a listener for given sensors.
*
* @deprecated This method is deprecated, use
* {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
* instead.
*
* @param listener sensor listener object
*
* @param sensors a bit masks of the sensors to register to
*
* @return true
if the sensor is supported and successfully
* enabled
*/
@Deprecated
public boolean registerListener(SensorListener listener, int sensors) {
return registerListener(listener, sensors, SENSOR_DELAY_NORMAL);
}
/**
* Registers a SensorListener for given sensors.
*
* @deprecated This method is deprecated, use
* {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
* instead.
*
* @param listener sensor listener object
*
* @param sensors a bit masks of the sensors to register to
*
* @param rate rate of events. This is only a hint to the system. events may
* be received faster or slower than the specified rate. Usually events are
* received faster. The value must be one of null {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
*
* @return true
if the sensor is supported and successfully
* enabled
*/
@Deprecated
public boolean registerListener(SensorListener listener, int sensors,
int rate) {
if (listener == null) {
return false;
}
boolean result = false;
result = registerLegacyListener(SENSOR_ACCELEROMETER,
Sensor.TYPE_ACCELEROMETER, listener, sensors, rate) || result;
result = registerLegacyListener(SENSOR_MAGNETIC_FIELD,
Sensor.TYPE_MAGNETIC_FIELD, listener, sensors, rate) || result;
result = registerLegacyListener(SENSOR_ORIENTATION_RAW,
Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result;
result = registerLegacyListener(SENSOR_ORIENTATION,
Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result;
result = registerLegacyListener(SENSOR_TEMPERATURE,
Sensor.TYPE_TEMPERATURE, listener, sensors, rate) || result;
return result;
}
@SuppressWarnings("deprecation")
private boolean registerLegacyListener(int legacyType, int type,
SensorListener listener, int sensors, int rate) {
return true;
/*
* if (listener == null) { return false; } boolean result = false; //
* Are we activating this legacy sensor? if ((sensors & legacyType) !=
* 0) { // if so, find a suitable Sensor Sensor sensor =
* getDefaultSensor(type); if (sensor != null) { // If we don't already
* have one, create a LegacyListener // to wrap this listener and
* process the events as // they are expected by legacy apps.
* LegacyListener legacyListener = null; synchronized
* (mLegacyListenersMap) { legacyListener =
* mLegacyListenersMap.get(listener); if (legacyListener == null) { //
* we didn't find a LegacyListener for this client, // create one, and
* put it in our list. legacyListener = new LegacyListener(listener);
* mLegacyListenersMap.put(listener, legacyListener); } } // register
* this legacy sensor with this legacy listener
* legacyListener.registerSensor(legacyType); // and finally, register
* the legacy listener with the new apis result =
* registerListener(legacyListener, sensor, rate); } } return result;
*/
}
/**
* Unregisters a listener for the sensors with which it is registered.
*
* @deprecated This method is deprecated, use
* {@link SensorManager#unregisterListener(SensorEventListener, Sensor)}
* instead.
*
* @param listener a SensorListener object
*
* @param sensors a bit masks of the sensors to unregister from
*/
@Deprecated
public void unregisterListener(SensorListener listener, int sensors) {
unregisterLegacyListener(SENSOR_ACCELEROMETER,
Sensor.TYPE_ACCELEROMETER, listener, sensors);
unregisterLegacyListener(SENSOR_MAGNETIC_FIELD,
Sensor.TYPE_MAGNETIC_FIELD, listener, sensors);
unregisterLegacyListener(SENSOR_ORIENTATION_RAW,
Sensor.TYPE_ORIENTATION, listener, sensors);
unregisterLegacyListener(SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION,
listener, sensors);
unregisterLegacyListener(SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE,
listener, sensors);
}
@SuppressWarnings("deprecation")
private void unregisterLegacyListener(int legacyType, int type,
SensorListener listener, int sensors) {
/*
* if (listener == null) { return; } // do we know about this listener?
* LegacyListener legacyListener = null; synchronized
* (mLegacyListenersMap) { legacyListener =
* mLegacyListenersMap.get(listener); } if (legacyListener != null) { //
* Are we deactivating this legacy sensor? if ((sensors & legacyType) !=
* 0) { // if so, find the corresponding Sensor Sensor sensor =
* getDefaultSensor(type); if (sensor != null) { // unregister this
* legacy sensor and if we don't // need the corresponding Sensor,
* unregister it too if (legacyListener.unregisterSensor(legacyType)) {
* // corresponding sensor not needed, unregister
* unregisterListener(legacyListener, sensor); // finally check if we
* still need the legacyListener // in our mapping, if not, get rid of
* it too. synchronized (sListeners) { boolean found = false; for
* (ListenerDelegate i : sListeners) { if (i.getListener() ==
* legacyListener) { found = true; break; } } if (!found) { synchronized
* (mLegacyListenersMap) { mLegacyListenersMap.remove(listener); } } } }
* } } }
*/
}
/**
* Unregisters a listener for all sensors.
*
* @deprecated This method is deprecated, use
* {@link SensorManager#unregisterListener(SensorEventListener)} instead.
*
* @param listener a SensorListener object
*/
@Deprecated
public void unregisterListener(SensorListener listener) {
unregisterListener(listener, SENSOR_ALL | SENSOR_ORIENTATION_RAW);
}
/**
* Unregisters a listener for the sensors with which it is registered.
*
* @param listener a SensorEventListener object
*
* @param sensor the sensor to unregister from
*
* @see #unregisterListener(SensorEventListener)
* @see #registerListener(SensorEventListener, Sensor, int)
*
*/
public void unregisterListener(SensorEventListener listener, Sensor sensor) {
unregisterListener((Object) listener, sensor);
}
/**
* Unregisters a listener for all sensors.
*
* @param listener a SensorListener object
*
* @see #unregisterListener(SensorEventListener, Sensor)
* @see #registerListener(SensorEventListener, Sensor, int)
*
*/
public void unregisterListener(SensorEventListener listener) {
unregisterListener((Object) listener);
}
/**
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
*
* @param listener A {@link android.hardware.SensorEventListener
* SensorEventListener} object.
*
* @param sensor The {@link android.hardware.Sensor Sensor} to register to.
*
* @param rate The rate {@link android.hardware.SensorEvent sensor events}
* are delivered at. This is only a hint to the system. Events may be
* received faster or slower than the specified rate. Usually events are
* received faster. The value must be one of null {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST} or, the
* desired delay between events in microsecond.
*
* @return true
if the sensor is supported and successfully
* enabled.
*
* @see #registerListener(SensorEventListener, Sensor, int, Handler)
* @see #unregisterListener(SensorEventListener)
* @see #unregisterListener(SensorEventListener, Sensor)
*
*/
public boolean registerListener(SensorEventListener listener,
Sensor sensor, int rate) {
return registerListener(listener, sensor, rate, null);
}
/**
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
*
* @param listener A {@link android.hardware.SensorEventListener
* SensorEventListener} object.
*
* @param sensor The {@link android.hardware.Sensor Sensor} to register to.
*
* @param rate The rate {@link android.hardware.SensorEvent sensor events}
* are delivered at. This is only a hint to the system. Events may be
* received faster or slower than the specified rate. Usually events are
* received faster. The value must be one of null {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}. or, the
* desired delay between events in microsecond.
*
* @param handler The {@link android.os.Handler Handler} the
* {@link android.hardware.SensorEvent sensor events} will be delivered to.
*
* @return true if the sensor is supported and successfully enabled.
*
* @see #registerListener(SensorEventListener, Sensor, int)
* @see #unregisterListener(SensorEventListener)
* @see #unregisterListener(SensorEventListener, Sensor)
*
*/
public boolean registerListener(SensorEventListener listener,
Sensor sensor, int rate, Handler handler) {
if (listener == null || sensor == null) {
return false;
}
boolean result = true;
int delay = -1;
switch (rate) {
case SENSOR_DELAY_FASTEST:
delay = 0;
break;
case SENSOR_DELAY_GAME:
delay = 20000;
break;
case SENSOR_DELAY_UI:
delay = 60000;
break;
case SENSOR_DELAY_NORMAL:
delay = 200000;
break;
default:
delay = rate;
break;
}
synchronized (mListeners) {
for (FullListener fl : mListeners) {
if (fl.sensorEventListener == listener && fl.sensor == sensor) {
return true;
}
}
FullListener fl = new FullListener();
fl.handler = handler;
fl.rate = delay;
fl.sensorEventListener = listener;
fl.sensor = sensor;
mListeners.add(fl);
result = true;
}
/*
* // look for this listener in our list ListenerDelegate l = null; for
* (ListenerDelegate i : sListeners) { if (i.getListener() == listener)
* { l = i; break; } } // if we don't find it, add it to the list if (l
* == null) { l = new ListenerDelegate(listener, sensor, handler);
* sListeners.add(l); // if the list is not empty, start our main thread
* if (!sListeners.isEmpty()) { if (sSensorThread.startLocked()) { if
* (!enableSensorLocked(sensor, delay)) { // oops. there was an error
* sListeners.remove(l); result = false; } } else { // there was an
* error, remove the listener sListeners.remove(l); result = false; } }
* else { // weird, we couldn't add the listener result = false; } }
* else { l.addSensor(sensor); if (!enableSensorLocked(sensor, delay)) {
* // oops. there was an error l.removeSensor(sensor); result = false; }
* }
*/
return result;
}
private void unregisterListener(Object listener, Sensor sensor) {
if (listener == null || sensor == null) {
return;
}
synchronized (mListeners) {
final int size = mListeners.size();
for (int i = 0; i < size; i++) {
FullListener fl = mListeners.get(i);
if (fl.sensorEventListener == listener && fl.sensor == sensor) {
mListeners.remove(i);
break;
}
}
}
}
private void unregisterListener(Object listener) {
if (listener == null) {
return;
}
synchronized (mListeners) {
int size = mListeners.size();
for (int i = 0; i < size; i++) {
FullListener fl = mListeners.get(i);
if (fl.sensorEventListener == listener) {
mListeners.remove(i);
size = mListeners.size();
i = 0;
}
}
if(mListeners.isEmpty()) {
sensorEventService.stop();
sSensorModuleInitialized = false;
}
}
}
/**
*
* Computes the inclination matrix I as well as the rotation matrix
* R transforming a vector from the device coordinate system to the
* world's coordinate system which is defined as a direct orthonormal basis,
* where:
*
*
*
* - X is defined as the vector product Y.Z (It is tangential to
* the ground at the device's current location and roughly points
* East).
* - Y is tangential to the ground at the device's current location and
* points towards the magnetic North Pole.
* - Z points towards the sky and is perpendicular to the ground.
*
*
*
*
*
*
*
*
*
* By definition:
*
* [0 0 g] = R * gravity (g = magnitude of gravity)
*
* [0 m 0] = I * R * geomagnetic (m = magnitude of
* geomagnetic field)
*
* R is the identity matrix when the device is aligned with the
* world's coordinate system, that is, when the device's X axis points
* toward East, the Y axis points to the North Pole and the device is facing
* the sky.
*
*
* I is a rotation matrix transforming the geomagnetic vector into
* the same coordinate space as gravity (the world's coordinate space).
* I is a simple rotation around the X axis. The inclination angle in
* radians can be computed with {@link #getInclination}.
*
*
*
* Each matrix is returned either as a 3x3 or 4x4 row-major matrix depending
* on the length of the passed array:
*
* If the array length is 16:
*
*
* / M[ 0] M[ 1] M[ 2] M[ 3] \
* | M[ 4] M[ 5] M[ 6] M[ 7] |
* | M[ 8] M[ 9] M[10] M[11] |
* \ M[12] M[13] M[14] M[15] /
*
*
* This matrix is ready to be used by OpenGL ES's null {@link javax.microedition.khronos.opengles.GL10#glLoadMatrixf(float[], int)
* glLoadMatrixf(float[], int)}.
*
* Note that because OpenGL matrices are column-major matrices you must
* transpose the matrix before using it. However, since the matrix is a
* rotation matrix, its transpose is also its inverse, conveniently, it is
* often the inverse of the rotation that is needed for rendering; it can
* therefore be used with OpenGL ES directly.
*
* Also note that the returned matrices always have this form:
*
*
* / M[ 0] M[ 1] M[ 2] 0 \
* | M[ 4] M[ 5] M[ 6] 0 |
* | M[ 8] M[ 9] M[10] 0 |
* \ 0 0 0 1 /
*
*
*
* If the array length is 9:
*
*
* / M[ 0] M[ 1] M[ 2] \
* | M[ 3] M[ 4] M[ 5] |
* \ M[ 6] M[ 7] M[ 8] /
*
*
*
*
* The inverse of each matrix can be computed easily by taking its
* transpose.
*
*
* The matrices returned by this function are meaningful only when the
* device is not free-falling and it is not close to the magnetic north. If
* the device is accelerating, or placed into a strong magnetic field, the
* returned matrices may be inaccurate.
*
* @param R is an array of 9 floats holding the rotation matrix R
* when this function returns. R can be null.
*
*
* @param I is an array of 9 floats holding the rotation matrix I
* when this function returns. I can be null.
*
*
* @param gravity is an array of 3 floats containing the gravity vector
* expressed in the device's coordinate. You can simply use the
* {@link android.hardware.SensorEvent#values values} returned by a
* {@link android.hardware.SensorEvent SensorEvent} of a
* {@link android.hardware.Sensor Sensor} of type null {@link android.hardware.Sensor#TYPE_ACCELEROMETER
* TYPE_ACCELEROMETER}.
*
*
* @param geomagnetic is an array of 3 floats containing the geomagnetic
* vector expressed in the device's coordinate. You can simply use the
* {@link android.hardware.SensorEvent#values values} returned by a
* {@link android.hardware.SensorEvent SensorEvent} of a
* {@link android.hardware.Sensor Sensor} of type null {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD
* TYPE_MAGNETIC_FIELD}.
*
* @return true
on success, false
on failure (for
* instance, if the device is in free fall). On failure the output matrices
* are not modified.
*
* @see #getInclination(float[])
* @see #getOrientation(float[], float[])
* @see #remapCoordinateSystem(float[], int, int, float[])
*/
public static boolean getRotationMatrix(float[] R, float[] I,
float[] gravity, float[] geomagnetic) {
// TODO: move this to native code for efficiency
float Ax = gravity[0];
float Ay = gravity[1];
float Az = gravity[2];
final float Ex = geomagnetic[0];
final float Ey = geomagnetic[1];
final float Ez = geomagnetic[2];
float Hx = Ey * Az - Ez * Ay;
float Hy = Ez * Ax - Ex * Az;
float Hz = Ex * Ay - Ey * Ax;
final float normH = (float) Math.sqrt(Hx * Hx + Hy * Hy + Hz * Hz);
if (normH < 0.1f) {
// device is close to free fall (or in space?), or close to
// magnetic north pole. Typical values are > 100.
return false;
}
final float invH = 1.0f / normH;
Hx *= invH;
Hy *= invH;
Hz *= invH;
final float invA = 1.0f / (float) Math
.sqrt(Ax * Ax + Ay * Ay + Az * Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
final float Mx = Ay * Hz - Az * Hy;
final float My = Az * Hx - Ax * Hz;
final float Mz = Ax * Hy - Ay * Hx;
if (R != null) {
if (R.length == 9) {
R[0] = Hx;
R[1] = Hy;
R[2] = Hz;
R[3] = Mx;
R[4] = My;
R[5] = Mz;
R[6] = Ax;
R[7] = Ay;
R[8] = Az;
} else if (R.length == 16) {
R[0] = Hx;
R[1] = Hy;
R[2] = Hz;
R[3] = 0;
R[4] = Mx;
R[5] = My;
R[6] = Mz;
R[7] = 0;
R[8] = Ax;
R[9] = Ay;
R[10] = Az;
R[11] = 0;
R[12] = 0;
R[13] = 0;
R[14] = 0;
R[15] = 1;
}
}
if (I != null) {
// compute the inclination matrix by projecting the geomagnetic
// vector onto the Z (gravity) and X (horizontal component
// of geomagnetic vector) axes.
final float invE = 1.0f / (float) Math.sqrt(Ex * Ex + Ey * Ey + Ez
* Ez);
final float c = (Ex * Mx + Ey * My + Ez * Mz) * invE;
final float s = (Ex * Ax + Ey * Ay + Ez * Az) * invE;
if (I.length == 9) {
I[0] = 1;
I[1] = 0;
I[2] = 0;
I[3] = 0;
I[4] = c;
I[5] = s;
I[6] = 0;
I[7] = -s;
I[8] = c;
} else if (I.length == 16) {
I[0] = 1;
I[1] = 0;
I[2] = 0;
I[4] = 0;
I[5] = c;
I[6] = s;
I[8] = 0;
I[9] = -s;
I[10] = c;
I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0;
I[15] = 1;
}
}
return true;
}
/**
* Computes the geomagnetic inclination angle in radians from the
* inclination matrix I returned by {@link #getRotationMatrix}.
*
* @param I inclination matrix see {@link #getRotationMatrix}.
*
* @return The geomagnetic inclination angle in radians.
*
* @see #getRotationMatrix(float[], float[], float[], float[])
* @see #getOrientation(float[], float[])
* @see GeomagneticField
*
*/
public static float getInclination(float[] I) {
if (I.length == 9) {
return (float) Math.atan2(I[5], I[4]);
} else {
return (float) Math.atan2(I[6], I[5]);
}
}
/**
*
* Rotates the supplied rotation matrix so it is expressed in a different
* coordinate system. This is typically used when an application needs to
* compute the three orientation angles of the device (see
* {@link #getOrientation}) in a different coordinate system.
*
*
*
* When the rotation matrix is used for drawing (for instance with OpenGL
* ES), it usually doesn't need to be transformed by this function,
* unless the screen is physically rotated, in which case you can use
* {@link android.view.Display#getRotation() Display.getRotation()} to
* retrieve the current rotation of the screen. Note that because the user
* is generally free to rotate their screen, you often should consider the
* rotation in deciding the parameters to use here.
*
*
*
* Examples:
*
*
*
* - Using the camera (Y axis along the camera's axis) for an augmented
* reality application where the rotation angles are needed:
*
*
*
* remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);
*
*
*
* - Using the device as a mechanical compass when rotation is
* {@link android.view.Surface#ROTATION_90 Surface.ROTATION_90}:
*
*
*
* remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);
*
*
*
* Beware of the above example. This call is needed only to account for a
* rotation from its natural orientation when calculating the rotation
* angles (see {@link #getOrientation}). If the rotation matrix is also used
* for rendering, it may not need to be transformed, for instance if your
* {@link android.app.Activity Activity} is running in landscape mode.
*
*
*
* Since the resulting coordinate system is orthonormal, only two axes need
* to be specified.
*
* @param inR the rotation matrix to be transformed. Usually it is the
* matrix returned by {@link #getRotationMatrix}.
*
* @param X defines on which world axis and direction the X axis of the
* device is mapped.
*
* @param Y defines on which world axis and direction the Y axis of the
* device is mapped.
*
* @param outR the transformed rotation matrix. inR and outR can be the same
* array, but it is not recommended for performance reason.
*
* @return true
on success. false
if the input
* parameters are incorrect, for instance if X and Y define the same axis.
* Or if inR and outR don't have the same length.
*
* @see #getRotationMatrix(float[], float[], float[], float[])
*/
public static boolean remapCoordinateSystem(float[] inR, int X, int Y,
float[] outR) {
if (inR == outR) {
final float[] temp = mTempMatrix;
synchronized (temp) {
// we don't expect to have a lot of contention
if (remapCoordinateSystemImpl(inR, X, Y, temp)) {
final int size = outR.length;
for (int i = 0; i < size; i++) {
outR[i] = temp[i];
}
return true;
}
}
}
return remapCoordinateSystemImpl(inR, X, Y, outR);
}
private static boolean remapCoordinateSystemImpl(float[] inR, int X, int Y,
float[] outR) {
/*
* X and Y define a rotation matrix 'r':
*
* (X==1)?((X&0x80)?-1:1):0 (X==2)?((X&0x80)?-1:1):0
* (X==3)?((X&0x80)?-1:1):0 (Y==1)?((Y&0x80)?-1:1):0
* (Y==2)?((Y&0x80)?-1:1):0 (Y==3)?((X&0x80)?-1:1):0 r[0] ^ r[1]
*
* where the 3rd line is the vector product of the first 2 lines
*/
final int length = outR.length;
if (inR.length != length) {
return false; // invalid parameter
}
if ((X & 0x7C) != 0 || (Y & 0x7C) != 0) {
return false; // invalid parameter
}
if (((X & 0x3) == 0) || ((Y & 0x3) == 0)) {
return false; // no axis specified
}
if ((X & 0x3) == (Y & 0x3)) {
return false; // same axis specified
}
// Z is "the other" axis, its sign is either +/- sign(X)*sign(Y)
// this can be calculated by exclusive-or'ing X and Y; except for
// the sign inversion (+/-) which is calculated below.
int Z = X ^ Y;
// extract the axis (remove the sign), offset in the range 0 to 2.
final int x = (X & 0x3) - 1;
final int y = (Y & 0x3) - 1;
final int z = (Z & 0x3) - 1;
// compute the sign of Z (whether it needs to be inverted)
final int axis_y = (z + 1) % 3;
final int axis_z = (z + 2) % 3;
if (((x ^ axis_y) | (y ^ axis_z)) != 0) {
Z ^= 0x80;
}
final boolean sx = (X >= 0x80);
final boolean sy = (Y >= 0x80);
final boolean sz = (Z >= 0x80);
// Perform R * r, in avoiding actual muls and adds.
final int rowLength = ((length == 16) ? 4 : 3);
for (int j = 0; j < 3; j++) {
final int offset = j * rowLength;
for (int i = 0; i < 3; i++) {
if (x == i) {
outR[offset + i] = sx ? -inR[offset + 0] : inR[offset + 0];
}
if (y == i) {
outR[offset + i] = sy ? -inR[offset + 1] : inR[offset + 1];
}
if (z == i) {
outR[offset + i] = sz ? -inR[offset + 2] : inR[offset + 2];
}
}
}
if (length == 16) {
outR[3] = outR[7] = outR[11] = outR[12] = outR[13] = outR[14] = 0;
outR[15] = 1;
}
return true;
}
/**
* Computes the device's orientation based on the rotation matrix.
*
* When it returns, the array values is filled with the result:
*
* - values[0]: azimuth, rotation around the Z axis.
* - values[1]: pitch, rotation around the X axis.
* - values[2]: roll, rotation around the Y axis.
*
*
* The reference coordinate-system used is different from the world
* coordinate-system defined for the rotation matrix:
*
*
* - X is defined as the vector product Y.Z (It is tangential to
* the ground at the device's current location and roughly points
* West).
* - Y is tangential to the ground at the device's current location and
* points towards the magnetic North Pole.
* - Z points towards the center of the Earth and is perpendicular to the
* ground.
*
*
*
*
*
*
* All three angles above are in radians and positive in the
* counter-clockwise direction.
*
* @param R rotation matrix see {@link #getRotationMatrix}.
*
* @param values an array of 3 floats to hold the result.
*
* @return The array values passed as argument.
*
* @see #getRotationMatrix(float[], float[], float[], float[])
* @see GeomagneticField
*/
public static float[] getOrientation(float[] R, float values[]) {
/*
* 4x4 (length=16) case: / R[ 0] R[ 1] R[ 2] 0 \ | R[ 4] R[ 5] R[ 6] 0 |
* | R[ 8] R[ 9] R[10] 0 | \ 0 0 0 1 /
*
* 3x3 (length=9) case: / R[ 0] R[ 1] R[ 2] \ | R[ 3] R[ 4] R[ 5] | \ R[
* 6] R[ 7] R[ 8] /
*/
if (R.length == 9) {
values[0] = (float) Math.atan2(R[1], R[4]);
values[1] = (float) Math.asin(-R[7]);
values[2] = (float) Math.atan2(-R[6], R[8]);
} else {
values[0] = (float) Math.atan2(R[1], R[5]);
values[1] = (float) Math.asin(-R[9]);
values[2] = (float) Math.atan2(-R[8], R[10]);
}
return values;
}
/**
* Computes the Altitude in meters from the atmospheric pressure and the
* pressure at sea level.
*
* Typically the atmospheric pressure is read from a
* {@link Sensor#TYPE_PRESSURE} sensor. The pressure at sea level must be
* known, usually it can be retrieved from airport databases in the
* vicinity. If unknown, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE}
* as an approximation, but absolute altitudes won't be accurate.
*
*
* To calculate altitude differences, you must calculate the difference
* between the altitudes at both points. If you don't know the altitude as
* sea level, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE} instead,
* which will give good results considering the range of pressure typically
* involved.
*
*
*
* float altitude_difference =
* getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,
* pressure_at_point2) -
* getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,
* pressure_at_point1);
*
*
*
* @param p0 pressure at sea level
* @param p atmospheric pressure
* @return Altitude in meters
*/
public static float getAltitude(float p0, float p) {
final float coef = 1.0f / 5.255f;
return 44330.0f * (1.0f - (float) Math.pow(p / p0, coef));
}
class LmsFilter {
private static final int SENSORS_RATE_MS = 20;
private static final int COUNT = 12;
private static final float PREDICTION_RATIO = 1.0f / 3.0f;
private static final float PREDICTION_TIME = (SENSORS_RATE_MS * COUNT / 1000.0f)
* PREDICTION_RATIO;
private float mV[] = new float[COUNT * 2];
private float mT[] = new float[COUNT * 2];
private int mIndex;
public LmsFilter() {
mIndex = COUNT;
}
public float filter(long time, float in) {
float v = in;
final float ns = 1.0f / 1000000000.0f;
final 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 copy 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++) {
final int j = mIndex - 1 - i;
final float Z = mV[j];
final 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;
// Normalize
f *= (1.0f / 360.0f);
if (((f >= 0) ? f : -f) >= 0.5f) {
f = f - (float) Math.ceil(f + 0.5f) + 1.0f;
}
if (f < 0) {
f += 1.0f;
}
f *= 360.0f;
return f;
}
}
/**
* Helper function to compute the angle change between two rotation
* matrices. Given a current rotation matrix (R) and a previous rotation
* matrix (prevR) computes the rotation around the x,y, and z axes which
* transforms prevR to R. outputs a 3 element vector containing the x,y, and
* z angle change at indexes 0, 1, and 2 respectively.
*
* Each input matrix is either as a 3x3 or 4x4 row-major matrix depending on
* the length of the passed array:
*
* If the array length is 9, then the array elements represent this matrix
*
*
* / R[ 0] R[ 1] R[ 2] \
* | R[ 3] R[ 4] R[ 5] |
* \ R[ 6] R[ 7] R[ 8] /
*
*
* If the array length is 16, then the array elements represent this matrix
*
*
* / R[ 0] R[ 1] R[ 2] R[ 3] \
* | R[ 4] R[ 5] R[ 6] R[ 7] |
* | R[ 8] R[ 9] R[10] R[11] |
* \ R[12] R[13] R[14] R[15] /
*
*
* @param R current rotation matrix
* @param prevR previous rotation matrix
* @param angleChange an array of floats in which the angle change is stored
*/
public static void getAngleChange(float[] angleChange, float[] R,
float[] prevR) {
float rd1 = 0, rd4 = 0, rd6 = 0, rd7 = 0, rd8 = 0;
float ri0 = 0, ri1 = 0, ri2 = 0, ri3 = 0, ri4 = 0, ri5 = 0, ri6 = 0, ri7 = 0, ri8 = 0;
float pri0 = 0, pri1 = 0, pri2 = 0, pri3 = 0, pri4 = 0, pri5 = 0, pri6 = 0, pri7 = 0, pri8 = 0;
int i, j, k;
if (R.length == 9) {
ri0 = R[0];
ri1 = R[1];
ri2 = R[2];
ri3 = R[3];
ri4 = R[4];
ri5 = R[5];
ri6 = R[6];
ri7 = R[7];
ri8 = R[8];
} else if (R.length == 16) {
ri0 = R[0];
ri1 = R[1];
ri2 = R[2];
ri3 = R[4];
ri4 = R[5];
ri5 = R[6];
ri6 = R[8];
ri7 = R[9];
ri8 = R[10];
}
if (prevR.length == 9) {
pri0 = prevR[0];
pri1 = prevR[1];
pri2 = prevR[2];
pri3 = prevR[3];
pri4 = prevR[4];
pri5 = prevR[5];
pri6 = prevR[6];
pri7 = prevR[7];
pri8 = prevR[8];
} else if (prevR.length == 16) {
pri0 = prevR[0];
pri1 = prevR[1];
pri2 = prevR[2];
pri3 = prevR[4];
pri4 = prevR[5];
pri5 = prevR[6];
pri6 = prevR[8];
pri7 = prevR[9];
pri8 = prevR[10];
}
// calculate the parts of the rotation difference matrix we need
// rd[i][j] = pri[0][i] * ri[0][j] + pri[1][i] * ri[1][j] + pri[2][i] *
// ri[2][j];
rd1 = pri0 * ri1 + pri3 * ri4 + pri6 * ri7; // rd[0][1]
rd4 = pri1 * ri1 + pri4 * ri4 + pri7 * ri7; // rd[1][1]
rd6 = pri2 * ri0 + pri5 * ri3 + pri8 * ri6; // rd[2][0]
rd7 = pri2 * ri1 + pri5 * ri4 + pri8 * ri7; // rd[2][1]
rd8 = pri2 * ri2 + pri5 * ri5 + pri8 * ri8; // rd[2][2]
angleChange[0] = (float) Math.atan2(rd1, rd4);
angleChange[1] = (float) Math.asin(-rd7);
angleChange[2] = (float) Math.atan2(-rd6, rd8);
}
/**
* Helper function to convert a rotation vector to a rotation matrix. Given
* a rotation vector (presumably from a ROTATION_VECTOR sensor), returns a 9
* or 16 element rotation matrix in the array R. R must have length 9 or 16.
* If R.length == 9, the following matrix is returned:
*
*
* / R[ 0] R[ 1] R[ 2] \
* | R[ 3] R[ 4] R[ 5] |
* \ R[ 6] R[ 7] R[ 8] /
*
*
* If R.length == 16, the following matrix is returned:
*
*
* / R[ 0] R[ 1] R[ 2] 0 \
* | R[ 4] R[ 5] R[ 6] 0 |
* | R[ 8] R[ 9] R[10] 0 |
* \ 0 0 0 1 /
*
*
* @param rotationVector the rotation vector to convert
* @param R an array of floats in which to store the rotation matrix
*/
public static void getRotationMatrixFromVector(float[] R,
float[] rotationVector) {
float q0;
float q1 = rotationVector[0];
float q2 = rotationVector[1];
float q3 = rotationVector[2];
if (rotationVector.length == 4) {
q0 = rotationVector[3];
} else {
q0 = 1 - q1 * q1 - q2 * q2 - q3 * q3;
q0 = (q0 > 0) ? (float) Math.sqrt(q0) : 0;
}
float sq_q1 = 2 * q1 * q1;
float sq_q2 = 2 * q2 * q2;
float sq_q3 = 2 * q3 * q3;
float q1_q2 = 2 * q1 * q2;
float q3_q0 = 2 * q3 * q0;
float q1_q3 = 2 * q1 * q3;
float q2_q0 = 2 * q2 * q0;
float q2_q3 = 2 * q2 * q3;
float q1_q0 = 2 * q1 * q0;
if (R.length == 9) {
R[0] = 1 - sq_q2 - sq_q3;
R[1] = q1_q2 - q3_q0;
R[2] = q1_q3 + q2_q0;
R[3] = q1_q2 + q3_q0;
R[4] = 1 - sq_q1 - sq_q3;
R[5] = q2_q3 - q1_q0;
R[6] = q1_q3 - q2_q0;
R[7] = q2_q3 + q1_q0;
R[8] = 1 - sq_q1 - sq_q2;
} else if (R.length == 16) {
R[0] = 1 - sq_q2 - sq_q3;
R[1] = q1_q2 - q3_q0;
R[2] = q1_q3 + q2_q0;
R[3] = 0.0f;
R[4] = q1_q2 + q3_q0;
R[5] = 1 - sq_q1 - sq_q3;
R[6] = q2_q3 - q1_q0;
R[7] = 0.0f;
R[8] = q1_q3 - q2_q0;
R[9] = q2_q3 + q1_q0;
R[10] = 1 - sq_q1 - sq_q2;
R[11] = 0.0f;
R[12] = R[13] = R[14] = 0.0f;
R[15] = 1.0f;
}
}
/**
* Helper function to convert a rotation vector to a normalized quaternion.
* Given a rotation vector (presumably from a ROTATION_VECTOR sensor),
* returns a normalized quaternion in the array Q. The quaternion is stored
* as [w, x, y, z]
*
* @param rv the rotation vector to convert
* @param Q an array of floats in which to store the computed quaternion
*/
public static void getQuaternionFromVector(float[] Q, float[] rv) {
if (rv.length == 4) {
Q[0] = rv[3];
} else {
Q[0] = 1 - rv[0] * rv[0] - rv[1] * rv[1] - rv[2] * rv[2];
Q[0] = (Q[0] > 0) ? (float) Math.sqrt(Q[0]) : 0;
}
Q[1] = rv[0];
Q[2] = rv[1];
Q[3] = rv[2];
}
}