android/GlueIOIOPort: fix spurious errors after IOIO baud rate change
[xcsoar.git] / android / src / NonGPSSensors.java
blob98d556de2cc9989671055b2187be0cf838376795
1 /* Copyright_License {
3 XCSoar Glide Computer - http://www.xcsoar.org/
4 Copyright (C) 2000-2013 The XCSoar Project
5 A detailed list of copyright holders can be found in the file "AUTHORS".
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 // TODO items
24 // Wishlist:
25 // - Support more than one sensor of a particular type. Right now, only
26 // the default sensor of any given type is used. This is probably OK for
27 // all consumer devices.
28 // - Explore new and exotic sensors.
30 package org.xcsoar;
32 import android.content.Context;
33 import android.os.Handler;
34 import android.hardware.Sensor;
35 import android.hardware.SensorEvent;
36 import android.hardware.SensorEventListener;
37 import android.hardware.SensorManager;
38 import android.util.Log;
39 import android.os.SystemClock;
41 import java.util.HashMap;
42 import java.util.Map;
44 /**
45 * Code to support the growing suite of non-GPS sensors on Android platforms.
47 public class NonGPSSensors implements SensorEventListener, Runnable {
48 private static final String TAG = "XCSoar";
50 // Constant array saying whether we want to support certain sensors.
51 // If modifying this array, make certain that the largest ID value inside
52 // is still less than SENSOR_TYPE_ID_UPPER_BOUND, and don't forget to add
53 // corresponding constants to NonGPSSensors.(cpp|hpp).
54 private static final int[] SUPPORTED_SENSORS = {
55 Sensor.TYPE_ACCELEROMETER,
56 Sensor.TYPE_GYROSCOPE,
57 Sensor.TYPE_MAGNETIC_FIELD,
58 Sensor.TYPE_PRESSURE,
59 // TODO: Maybe add the following sensors if we can find a use for them.
60 // Sensor.TYPE_LIGHT,
61 // Sensor.TYPE_PROXIMITY,
62 // TODO: For Android 4.0 and up.
63 // Sensor.TYPE_AMBIENT_TEMPERATURE,
64 // Sensor.TYPE_RELATIVE_HUMIDITY,
65 // Remaining sensor types seem to be for "sensors" whose values are
66 // derived from the above sensors in software. We're probably better off
67 // taking care of that on our own.
70 // Noise variance constants for various types of Android pressure sensors.
71 private static final Map<String, Float> KF_PRESSURE_SENSOR_NOISE_VARIANCE = new HashMap<String, Float>() {{
72 // BMP180: seen in Galaxy Nexus.
73 /* The BMP180 noise variance is larger than the actual maximum
74 likelihood variance estimate based on data, since the noise
75 distribution appears to be more heavy-tailed than a normal
76 distribution. */
77 put("BMP180 Pressure sensor", 0.05f);
78 // Add more sensors using the syntax above.
79 }};
81 // A fallback pressure sensor noise variance constant for unfamiliar sensors.
82 private static final float KF_PRESSURE_SENSOR_NOISE_VARIANCE_FALLBACK = 0.05f;
84 // Non-inclusive upper bound on the largest sensor numerical type ID. As of
85 // API 14, the largest sensor numerical type ID appears to be 13. Used only
86 // for proportioning the following two arrays and for index checking.
87 private static final int SENSOR_TYPE_ID_UPPER_BOUND = 20;
89 // The set of sensors in SUPPORTED_SENSORS that are present on this device,
90 // i.e. that are retrieved by calling getDefaultSensor on sensor IDs present
91 // in that array. This array is indexed by sensor numerical type ID---if the
92 // corresponding sensor is absent or unsupported, the value will be null.
93 private Sensor[] default_sensors_;
95 // The set of sensors in SUPPORTED_SENSORS that are present on this device
96 // that we are actively listening to and passing into XCSoar. This array is
97 // indexed by sensor numerical type ID---if the corresponding sensor is
98 // absent or unsupported, the value will be null.
99 private boolean[] enabled_sensors_;
101 // Sensor manager.
102 private SensorManager sensor_manager_;
104 // Handler for non-GPS sensor reading.
105 private static Handler handler_;
107 // Noise variance we'll use for the pressure sensor in this device.
108 private float kf_sensor_noise_variance_;
110 private final SafeDestruct safeDestruct = new SafeDestruct();
113 * Index of this device in the global list. This value is extracted directly
114 * from this object by the C++ wrapper code.
116 private final int index;
119 * Global initialization of the class. Must be called from the main
120 * event thread, because the Handler object must be bound to that
121 * thread.
123 public static void Initialize() {
124 handler_ = new Handler();
127 NonGPSSensors(Context context, int _index) {
128 index = _index;
129 default_sensors_ = new Sensor[SENSOR_TYPE_ID_UPPER_BOUND];
130 enabled_sensors_ = new boolean[SENSOR_TYPE_ID_UPPER_BOUND];
132 // Obtain sensor manager.
133 sensor_manager_ = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
135 // Obtain the default Sensor objects for each of the desired sensors, if
136 // possible. Missing sensors will yield null values.
137 for (int id : SUPPORTED_SENSORS) {
138 default_sensors_[id] = sensor_manager_.getDefaultSensor(id);
141 /* Attempt to identify the type of pressure sensor in this device,
142 if one is present. */
143 kf_sensor_noise_variance_ = KF_PRESSURE_SENSOR_NOISE_VARIANCE_FALLBACK;
144 if (default_sensors_[Sensor.TYPE_PRESSURE] != null) {
145 final String sensor_name = default_sensors_[Sensor.TYPE_PRESSURE].getName();
146 final Float variance = KF_PRESSURE_SENSOR_NOISE_VARIANCE.get(sensor_name);
147 if (variance != null) {
148 Log.d(TAG, "Identified pressure sensor '" + sensor_name + "'; noise variance: " + variance);
149 kf_sensor_noise_variance_ = variance;
150 } else {
151 Log.d(TAG, "Unrecognized pressure sensor '" + sensor_name + "'; using default variance: " +
152 KF_PRESSURE_SENSOR_NOISE_VARIANCE_FALLBACK);
156 updateSensorSubscriptions();
160 * Trigger an update to this object's sensor subscriptions. This causes
161 * run() to be executed in the main thread. Should be called after each
162 * change to enabled_sensors_.
164 private void updateSensorSubscriptions() {
165 Log.d(TAG, "Triggering non-GPS sensor subscription update...");
166 handler_.removeCallbacks(this);
167 handler_.post(this);
171 * Retrieve an array of the type IDs of subscribable sensors. These are
172 * the sensors that are both supported by XCSoar and present in this device.
174 public int[] getSubscribableSensors() {
175 int[] subscribable = new int[SUPPORTED_SENSORS.length];
176 int s_ind = 0;
177 for (int id : SUPPORTED_SENSORS) {
178 if (default_sensors_[id] != null) subscribable[s_ind++] = id;
180 int[] result = new int[s_ind];
181 System.arraycopy(subscribable, 0, result, 0, s_ind);
182 return result;
186 * Enable the sensor designated by the furnished type ID. Returns true if
187 * the sensor is subscribable, regardless of whether it's already enabled;
188 * false otherwise. It's OK (albeit dumb) to call this on an already enabled
189 * sensor.
191 public boolean subscribeToSensor(int id) {
192 if (id < 0 || id >= SENSOR_TYPE_ID_UPPER_BOUND || default_sensors_[id] == null) {
193 return false;
195 enabled_sensors_[id] = true;
196 updateSensorSubscriptions();
197 return true;
201 * Disable the sensor designated by the furnished type ID. Returns true if
202 * the sensor is subscribable, regardless of whether it's already enabled;
203 * false otherwise. It's OK (albeit dumb) to call this on an already disabled
204 * sensor.
206 public boolean cancelSensorSubscription(int id) {
207 if (id < 0 || id >= SENSOR_TYPE_ID_UPPER_BOUND || default_sensors_[id] == null) {
208 return false;
210 enabled_sensors_[id] = false;
211 updateSensorSubscriptions();
212 return true;
216 * Return true iff we are subscribed to a particular sensor.
218 public boolean subscribedToSensor(int id) {
219 if (id < 0 || id >= SENSOR_TYPE_ID_UPPER_BOUND || default_sensors_[id] == null) {
220 return false;
222 return enabled_sensors_[id];
225 /** Cancel all sensor subscriptions. */
226 public void cancelAllSensorSubscriptions() {
227 safeDestruct.beginShutdown();
228 for (int id : SUPPORTED_SENSORS) enabled_sensors_[id] = false;
229 updateSensorSubscriptions();
230 safeDestruct.finishShutdown();
234 * from runnable; called by the #Handler and indirectly by
235 * updateSensorSubscriptions(). Updates the sensor subscriptions inside the
236 * main thread.
238 @Override
239 public void run() {
240 // Clear out all sensor listening subscriptions and subscribe (all over
241 // again) to all desired sensors.
242 Log.d(TAG, "Updating non-GPS sensor subscriptions...");
243 sensor_manager_.unregisterListener(this);
245 for (int id : SUPPORTED_SENSORS) {
246 if (enabled_sensors_[id] && default_sensors_[id] != null) {
247 Log.d(TAG, "Subscribing to sensor ID " + id + " (" + default_sensors_[id].getName() + ")");
248 sensor_manager_.registerListener(this, default_sensors_[id],
249 sensor_manager_.SENSOR_DELAY_NORMAL);
252 Log.d(TAG, "Done updating non-GPS sensor subscriptions...");
255 /** from SensorEventListener; currently does nothing. */
256 public void onAccuracyChanged(Sensor sensor, int accuracy) {
259 /** from SensorEventListener; report new sensor values to XCSoar. */
260 public void onSensorChanged(SensorEvent event) {
261 if (!safeDestruct.Increment())
262 return;
264 try {
265 switch (event.sensor.getType()) {
266 case Sensor.TYPE_ACCELEROMETER:
267 setAcceleration(event.values[0], event.values[1], event.values[2]);
268 break;
269 case Sensor.TYPE_GYROSCOPE:
270 setRotation(event.values[0], event.values[1], event.values[2]);
271 break;
272 case Sensor.TYPE_MAGNETIC_FIELD:
273 setMagneticField(event.values[0], event.values[1], event.values[2]);
274 break;
275 case Sensor.TYPE_PRESSURE:
276 setBarometricPressure(event.values[0], kf_sensor_noise_variance_);
277 break;
279 } finally {
280 safeDestruct.Decrement();
284 // Native methods for reporting sensor values to XCSoar's native C++ code.
285 private native void setAcceleration(float ddx, float ddy, float ddz);
286 private native void setRotation(float dtheta_x, float dtheta_y, float dtheta_z);
287 private native void setMagneticField(float h_x, float h_y, float h_z);
288 private native void setBarometricPressure(float pressure, float sensor_noise_variance);