14481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown/* 24481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * Copyright (C) 2012 The Android Open Source Project 34481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * 44481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 54481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * you may not use this file except in compliance with the License. 64481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * You may obtain a copy of the License at 74481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * 84481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * http://www.apache.org/licenses/LICENSE-2.0 94481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * 104481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * Unless required by applicable law or agreed to in writing, software 114481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 124481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * See the License for the specific language governing permissions and 144481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * limitations under the License. 154481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown */ 164481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 174481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownpackage android.hardware; 184481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 194481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownimport android.os.RemoteException; 204481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownimport android.os.ServiceManager; 214481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownimport android.view.IRotationWatcher; 224481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownimport android.view.IWindowManager; 234481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownimport android.view.Surface; 244481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 254481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownimport java.util.HashMap; 264481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownimport java.util.List; 274481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 284481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown/** 294481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * Helper class for implementing the legacy sensor manager API. 304481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * @hide 314481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown */ 324481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown@SuppressWarnings("deprecation") 334481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brownfinal class LegacySensorManager { 344481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static boolean sInitialized; 354481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static IWindowManager sWindowManager; 364481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static int sRotation = Surface.ROTATION_0; 374481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 384481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private final SensorManager mSensorManager; 394481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 404481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // List of legacy listeners. Guarded by mLegacyListenersMap. 414481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private final HashMap<SensorListener, LegacyListener> mLegacyListenersMap = 424481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown new HashMap<SensorListener, LegacyListener>(); 434481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 444481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public LegacySensorManager(SensorManager sensorManager) { 454481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mSensorManager = sensorManager; 464481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 474481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown synchronized (SensorManager.class) { 484481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (!sInitialized) { 494481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown sWindowManager = IWindowManager.Stub.asInterface( 504481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown ServiceManager.getService("window")); 514481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (sWindowManager != null) { 524481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // if it's null we're running in the system process 534481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // which won't get the rotated values 544481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown try { 554481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown sRotation = sWindowManager.watchRotation( 564481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown new IRotationWatcher.Stub() { 574481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public void onRotationChanged(int rotation) { 584481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown LegacySensorManager.onRotationChanged(rotation); 594481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 604481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 614481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown ); 624481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } catch (RemoteException e) { 634481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 644481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 654481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 664481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 674481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 684481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 694481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public int getSensors() { 704481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown int result = 0; 714481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown final List<Sensor> fullList = mSensorManager.getFullSensorList(); 724481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown for (Sensor i : fullList) { 734481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown switch (i.getType()) { 744481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case Sensor.TYPE_ACCELEROMETER: 754481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result |= SensorManager.SENSOR_ACCELEROMETER; 764481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 774481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case Sensor.TYPE_MAGNETIC_FIELD: 784481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result |= SensorManager.SENSOR_MAGNETIC_FIELD; 794481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 804481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case Sensor.TYPE_ORIENTATION: 814481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result |= SensorManager.SENSOR_ORIENTATION 824481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown | SensorManager.SENSOR_ORIENTATION_RAW; 834481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 844481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 854481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 864481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return result; 874481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 884481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 894481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public boolean registerListener(SensorListener listener, int sensors, int rate) { 904481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (listener == null) { 914481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return false; 924481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 934481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown boolean result = false; 944481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result = registerLegacyListener(SensorManager.SENSOR_ACCELEROMETER, 954481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown Sensor.TYPE_ACCELEROMETER, listener, sensors, rate) || result; 964481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result = registerLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD, 974481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown Sensor.TYPE_MAGNETIC_FIELD, listener, sensors, rate) || result; 984481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW, 994481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result; 1004481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION, 1014481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result; 1024481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result = registerLegacyListener(SensorManager.SENSOR_TEMPERATURE, 1034481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown Sensor.TYPE_TEMPERATURE, listener, sensors, rate) || result; 1044481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return result; 1054481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1064481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 1074481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private boolean registerLegacyListener(int legacyType, int type, 1084481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown SensorListener listener, int sensors, int rate) { 1094481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown boolean result = false; 1104481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // Are we activating this legacy sensor? 1114481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((sensors & legacyType) != 0) { 1124481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // if so, find a suitable Sensor 1134481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown Sensor sensor = mSensorManager.getDefaultSensor(type); 1144481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (sensor != null) { 1154481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // We do all of this work holding the legacy listener lock to ensure 1164481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // that the invariants around listeners are maintained. This is safe 1174481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // because neither registerLegacyListener nor unregisterLegacyListener 1184481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // are called reentrantly while sensors are being registered or unregistered. 1194481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown synchronized (mLegacyListenersMap) { 1204481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // If we don't already have one, create a LegacyListener 1214481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // to wrap this listener and process the events as 1224481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // they are expected by legacy apps. 1234481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown LegacyListener legacyListener = mLegacyListenersMap.get(listener); 1244481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (legacyListener == null) { 1254481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // we didn't find a LegacyListener for this client, 1264481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // create one, and put it in our list. 1274481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown legacyListener = new LegacyListener(listener); 1284481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mLegacyListenersMap.put(listener, legacyListener); 1294481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1304481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 1314481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // register this legacy sensor with this legacy listener 1324481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (legacyListener.registerSensor(legacyType)) { 1334481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // and finally, register the legacy listener with the new apis 1344481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result = mSensorManager.registerListener(legacyListener, sensor, rate); 1354481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } else { 1364481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown result = true; // sensor already enabled 1374481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1384481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1394481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1404481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1414481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return result; 1424481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1434481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 1444481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public void unregisterListener(SensorListener listener, int sensors) { 1454481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (listener == null) { 1464481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return; 1474481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1484481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown unregisterLegacyListener(SensorManager.SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER, 1494481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown listener, sensors); 1504481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown unregisterLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD, 1514481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown listener, sensors); 1524481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW, Sensor.TYPE_ORIENTATION, 1534481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown listener, sensors); 1544481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION, 1554481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown listener, sensors); 1564481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown unregisterLegacyListener(SensorManager.SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE, 1574481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown listener, sensors); 1584481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1594481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 1604481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private void unregisterLegacyListener(int legacyType, int type, 1614481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown SensorListener listener, int sensors) { 1624481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // Are we deactivating this legacy sensor? 1634481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((sensors & legacyType) != 0) { 1644481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // if so, find the corresponding Sensor 1654481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown Sensor sensor = mSensorManager.getDefaultSensor(type); 1664481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (sensor != null) { 1674481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // We do all of this work holding the legacy listener lock to ensure 1684481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // that the invariants around listeners are maintained. This is safe 1694481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // because neither registerLegacyListener nor unregisterLegacyListener 1704481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // are called re-entrantly while sensors are being registered or unregistered. 1714481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown synchronized (mLegacyListenersMap) { 1724481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // do we know about this listener? 1734481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown LegacyListener legacyListener = mLegacyListenersMap.get(listener); 1744481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (legacyListener != null) { 1754481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // unregister this legacy sensor and if we don't 1764481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // need the corresponding Sensor, unregister it too 1774481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (legacyListener.unregisterSensor(legacyType)) { 1784481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // corresponding sensor not needed, unregister 1794481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mSensorManager.unregisterListener(legacyListener, sensor); 1804481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 1814481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // finally check if we still need the legacyListener 1824481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // in our mapping, if not, get rid of it too. 1834481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (!legacyListener.hasSensors()) { 1844481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mLegacyListenersMap.remove(listener); 1854481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1864481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1874481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1884481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1894481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1904481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1914481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1924481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 1934481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown static void onRotationChanged(int rotation) { 1944481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown synchronized (SensorManager.class) { 1954481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown sRotation = rotation; 1964481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1974481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 1984481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 1994481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown static int getRotation() { 2004481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown synchronized (SensorManager.class) { 2014481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return sRotation; 2024481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2034481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2044481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2054481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static final class LegacyListener implements SensorEventListener { 2064481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private float mValues[] = new float[6]; 2074481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private SensorListener mTarget; 2084481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private int mSensors; 2094481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private final LmsFilter mYawfilter = new LmsFilter(); 2104481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2114481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown LegacyListener(SensorListener target) { 2124481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mTarget = target; 2134481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mSensors = 0; 2144481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2154481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2164481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown boolean registerSensor(int legacyType) { 2174481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((mSensors & legacyType) != 0) { 2184481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return false; 2194481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2204481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown boolean alreadyHasOrientationSensor = hasOrientationSensor(mSensors); 2214481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mSensors |= legacyType; 2224481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (alreadyHasOrientationSensor && hasOrientationSensor(legacyType)) { 2234481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return false; // don't need to re-register the orientation sensor 2244481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2254481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return true; 2264481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2274481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2284481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown boolean unregisterSensor(int legacyType) { 2294481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((mSensors & legacyType) == 0) { 2304481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return false; 2314481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2324481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mSensors &= ~legacyType; 2334481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (hasOrientationSensor(legacyType) && hasOrientationSensor(mSensors)) { 2344481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return false; // can't unregister the orientation sensor just yet 2354481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2364481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return true; 2374481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2384481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2394481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown boolean hasSensors() { 2404481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return mSensors != 0; 2414481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2424481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2434481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static boolean hasOrientationSensor(int sensors) { 2444481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return (sensors & (SensorManager.SENSOR_ORIENTATION 2454481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown | SensorManager.SENSOR_ORIENTATION_RAW)) != 0; 2464481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2474481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2484481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public void onAccuracyChanged(Sensor sensor, int accuracy) { 2494481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown try { 2504481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mTarget.onAccuracyChanged(getLegacySensorType(sensor.getType()), accuracy); 2514481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } catch (AbstractMethodError e) { 2524481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // old app that doesn't implement this method 2534481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // just ignore it. 2544481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2554481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2564481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2574481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public void onSensorChanged(SensorEvent event) { 2584481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown final float v[] = mValues; 2594481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown v[0] = event.values[0]; 2604481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown v[1] = event.values[1]; 2614481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown v[2] = event.values[2]; 2624481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown int type = event.sensor.getType(); 2634481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown int legacyType = getLegacySensorType(type); 2644481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mapSensorDataToWindow(legacyType, v, LegacySensorManager.getRotation()); 2654481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (type == Sensor.TYPE_ORIENTATION) { 2664481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((mSensors & SensorManager.SENSOR_ORIENTATION_RAW)!=0) { 2674481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION_RAW, v); 2684481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2694481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((mSensors & SensorManager.SENSOR_ORIENTATION)!=0) { 2704481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown v[0] = mYawfilter.filter(event.timestamp, v[0]); 2714481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION, v); 2724481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2734481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } else { 2744481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mTarget.onSensorChanged(legacyType, v); 2754481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2764481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 2774481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2784481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown /* 2794481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * Helper function to convert the specified sensor's data to the windows's 2804481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * coordinate space from the device's coordinate space. 2814481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * 2824481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * output: 3,4,5: values in the old API format 2834481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * 0,1,2: transformed values in the old API format 2844481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * 2854481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown */ 2864481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private void mapSensorDataToWindow(int sensor, 2874481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float[] values, int orientation) { 2884481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float x = values[0]; 2894481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float y = values[1]; 2904481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float z = values[2]; 2914481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 2924481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown switch (sensor) { 2934481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ORIENTATION: 2944481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ORIENTATION_RAW: 2954481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown z = -z; 2964481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 2974481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ACCELEROMETER: 2984481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown x = -x; 2994481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown y = -y; 3004481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown z = -z; 3014481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 3024481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_MAGNETIC_FIELD: 3034481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown x = -x; 3044481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown y = -y; 3054481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 3064481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3074481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[0] = x; 3084481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[1] = y; 3094481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[2] = z; 3104481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[3] = x; 3114481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[4] = y; 3124481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[5] = z; 3134481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 3144481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((orientation & Surface.ROTATION_90) != 0) { 3154481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // handles 90 and 270 rotation 3164481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown switch (sensor) { 3174481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ACCELEROMETER: 3184481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_MAGNETIC_FIELD: 3194481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[0] =-y; 3204481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[1] = x; 3214481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[2] = z; 3224481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 3234481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ORIENTATION: 3244481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ORIENTATION_RAW: 3254481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[0] = x + ((x < 270) ? 90 : -270); 3264481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[1] = z; 3274481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[2] = y; 3284481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 3294481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3304481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3314481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((orientation & Surface.ROTATION_180) != 0) { 3324481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown x = values[0]; 3334481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown y = values[1]; 3344481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown z = values[2]; 3354481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // handles 180 (flip) and 270 (flip + 90) rotation 3364481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown switch (sensor) { 3374481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ACCELEROMETER: 3384481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_MAGNETIC_FIELD: 3394481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[0] =-x; 3404481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[1] =-y; 3414481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[2] = z; 3424481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 3434481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ORIENTATION: 3444481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case SensorManager.SENSOR_ORIENTATION_RAW: 3454481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[0] = (x >= 180) ? (x - 180) : (x + 180); 3464481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[1] =-y; 3474481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown values[2] =-z; 3484481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown break; 3494481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3504481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3514481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3524481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 3534481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static int getLegacySensorType(int type) { 3544481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown switch (type) { 3554481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case Sensor.TYPE_ACCELEROMETER: 3564481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return SensorManager.SENSOR_ACCELEROMETER; 3574481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case Sensor.TYPE_MAGNETIC_FIELD: 3584481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return SensorManager.SENSOR_MAGNETIC_FIELD; 3594481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case Sensor.TYPE_ORIENTATION: 3604481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return SensorManager.SENSOR_ORIENTATION_RAW; 3614481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown case Sensor.TYPE_TEMPERATURE: 3624481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return SensorManager.SENSOR_TEMPERATURE; 3634481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3644481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return 0; 3654481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3664481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3674481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 3684481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static final class LmsFilter { 3694481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static final int SENSORS_RATE_MS = 20; 3704481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static final int COUNT = 12; 3714481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static final float PREDICTION_RATIO = 1.0f/3.0f; 3724481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO; 3734481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private float mV[] = new float[COUNT*2]; 374278a966e80dec29219dfa1356bf4cef07096c913Mathias Agopian private long mT[] = new long[COUNT*2]; 3754481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown private int mIndex; 3764481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 3774481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public LmsFilter() { 3784481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mIndex = COUNT; 3794481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3804481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 3814481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown public float filter(long time, float in) { 3824481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float v = in; 3834481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown final float ns = 1.0f / 1000000000.0f; 3844481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float v1 = mV[mIndex]; 3854481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if ((v-v1) > 180) { 3864481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown v -= 360; 3874481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } else if ((v1-v) > 180) { 3884481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown v += 360; 3894481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 3904481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown /* Manage the circular buffer, we write the data twice spaced 3914481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * by COUNT values, so that we don't have to copy the array 3924481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown * when it's full 3934481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown */ 3944481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mIndex++; 3954481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (mIndex >= COUNT*2) 3964481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mIndex = COUNT; 3974481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mV[mIndex] = v; 398278a966e80dec29219dfa1356bf4cef07096c913Mathias Agopian mT[mIndex] = time; 3994481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown mV[mIndex-COUNT] = v; 400278a966e80dec29219dfa1356bf4cef07096c913Mathias Agopian mT[mIndex-COUNT] = time; 4014481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 4024481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float A, B, C, D, E; 4034481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float a, b; 4044481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown int i; 4054481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 4064481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown A = B = C = D = E = 0; 4074481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown for (i=0 ; i<COUNT-1 ; i++) { 4084481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown final int j = mIndex - 1 - i; 4094481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown final float Z = mV[j]; 410278a966e80dec29219dfa1356bf4cef07096c913Mathias Agopian final float T = (mT[j]/2 + mT[j+1]/2 - time)*ns; 411278a966e80dec29219dfa1356bf4cef07096c913Mathias Agopian float dT = (mT[j] - mT[j+1])*ns; 4124481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown dT *= dT; 4134481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown A += Z*dT; 4144481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown B += T*(T*dT); 4154481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown C += (T*dT); 4164481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown D += Z*(T*dT); 4174481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown E += dT; 4184481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 4194481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown b = (A*B + C*D) / (E*B + C*C); 4204481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown a = (E*b - A) / C; 4214481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown float f = b + PREDICTION_TIME*a; 4224481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown 4234481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown // Normalize 4244481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown f *= (1.0f / 360.0f); 4254481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (((f>=0)?f:-f) >= 0.5f) 4264481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown f = f - (float)Math.ceil(f + 0.5f) + 1.0f; 4274481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown if (f < 0) 4284481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown f += 1.0f; 4294481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown f *= 360.0f; 4304481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown return f; 4314481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 4324481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown } 4334481d9c10ceaf3b886fb5cab1d20941932af5b0fJeff Brown} 434