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.
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.
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
;
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
,
59 // TODO: Maybe add the following sensors if we can find a use for them.
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
77 put("BMP180 Pressure sensor", 0.05f
);
78 // Add more sensors using the syntax above.
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_
;
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
123 public static void Initialize() {
124 handler_
= new Handler();
127 NonGPSSensors(Context context
, int _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
;
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);
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
];
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
);
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
191 public boolean subscribeToSensor(int id
) {
192 if (id
< 0 || id
>= SENSOR_TYPE_ID_UPPER_BOUND
|| default_sensors_
[id
] == null) {
195 enabled_sensors_
[id
] = true;
196 updateSensorSubscriptions();
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
206 public boolean cancelSensorSubscription(int id
) {
207 if (id
< 0 || id
>= SENSOR_TYPE_ID_UPPER_BOUND
|| default_sensors_
[id
] == null) {
210 enabled_sensors_
[id
] = false;
211 updateSensorSubscriptions();
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) {
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
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())
265 switch (event
.sensor
.getType()) {
266 case Sensor
.TYPE_ACCELEROMETER
:
267 setAcceleration(event
.values
[0], event
.values
[1], event
.values
[2]);
269 case Sensor
.TYPE_GYROSCOPE
:
270 setRotation(event
.values
[0], event
.values
[1], event
.values
[2]);
272 case Sensor
.TYPE_MAGNETIC_FIELD
:
273 setMagneticField(event
.values
[0], event
.values
[1], event
.values
[2]);
275 case Sensor
.TYPE_PRESSURE
:
276 setBarometricPressure(event
.values
[0], kf_sensor_noise_variance_
);
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
);