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