Skip to content

Commit f402fa4

Browse files
Victoria LeaseAndroid (Google) Code Review
authored andcommitted
Merge "Simplify fused location provider." into jb-mr1-dev
2 parents f9307c5 + 0fdfa6b commit f402fa4

File tree

1 file changed

+37
-114
lines changed

1 file changed

+37
-114
lines changed

packages/FusedLocation/src/com/android/location/fused/FusionEngine.java

Lines changed: 37 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,7 @@ public interface Callback {
4242
private static final String NETWORK = LocationManager.NETWORK_PROVIDER;
4343
private static final String GPS = LocationManager.GPS_PROVIDER;
4444

45-
// threshold below which a location is considered stale enough
46-
// that we shouldn't use its bearing, altitude, speed etc
47-
private static final double WEIGHT_THRESHOLD = 0.5;
48-
// accuracy in meters at which a Location's weight is halved (compared to 0 accuracy)
49-
private static final double ACCURACY_HALFLIFE_M = 20.0;
50-
// age in seconds at which a Location's weight is halved (compared to 0 age)
51-
private static final double AGE_HALFLIFE_S = 60.0;
52-
53-
private static final double ACCURACY_DECAY_CONSTANT_M = Math.log(2) / ACCURACY_HALFLIFE_M;
54-
private static final double AGE_DECAY_CONSTANT_S = Math.log(2) / AGE_HALFLIFE_S;
45+
public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000; // 11 seconds
5546

5647
private final Context mContext;
5748
private final LocationManager mLocationManager;
@@ -62,8 +53,6 @@ public interface Callback {
6253
private Location mFusedLocation;
6354
private Location mGpsLocation;
6455
private Location mNetworkLocation;
65-
private double mNetworkWeight;
66-
private double mGpsWeight;
6756

6857
private boolean mEnabled;
6958
private ProviderRequestUnbundled mRequest;
@@ -102,10 +91,6 @@ public void deinit() {
10291
Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
10392
}
10493

105-
private boolean isAvailable() {
106-
return mStats.get(GPS).available || mStats.get(NETWORK).available;
107-
}
108-
10994
/** Called on mLooper thread */
11095
public void enable() {
11196
mEnabled = true;
@@ -130,7 +115,6 @@ private static class ProviderStats {
130115
public boolean requested;
131116
public long requestTime;
132117
public long minTime;
133-
public long lastRequestTtff;
134118
@Override
135119
public String toString() {
136120
StringBuilder s = new StringBuilder();
@@ -171,9 +155,6 @@ private void updateRequirements() {
171155
return;
172156
}
173157

174-
ProviderStats gpsStats = mStats.get(GPS);
175-
ProviderStats networkStats = mStats.get(NETWORK);
176-
177158
long networkInterval = Long.MAX_VALUE;
178159
long gpsInterval = Long.MAX_VALUE;
179160
for (LocationRequest request : mRequest.getLocationRequests()) {
@@ -209,104 +190,46 @@ private void updateRequirements() {
209190
}
210191
}
211192

212-
private static double weighAccuracy(Location loc) {
213-
double accuracy = loc.getAccuracy();
214-
return Math.exp(-accuracy * ACCURACY_DECAY_CONSTANT_M);
215-
}
216-
217-
private static double weighAge(Location loc) {
218-
long ageSeconds = SystemClock.elapsedRealtimeNanos() - loc.getElapsedRealtimeNanos();
219-
ageSeconds /= 1000000000L;
220-
if (ageSeconds < 0) ageSeconds = 0;
221-
return Math.exp(-ageSeconds * AGE_DECAY_CONSTANT_S);
222-
}
223-
224-
private double weigh(double gps, double network) {
225-
return (gps * mGpsWeight) + (network * mNetworkWeight);
226-
}
227-
228-
private double weigh(double gps, double network, double wrapMin, double wrapMax) {
229-
// apply aliasing
230-
double wrapWidth = wrapMax - wrapMin;
231-
if (gps - network > wrapWidth / 2) network += wrapWidth;
232-
else if (network - gps > wrapWidth / 2) gps += wrapWidth;
233-
234-
double result = weigh(gps, network);
235-
236-
// remove aliasing
237-
if (result > wrapMax) result -= wrapWidth;
238-
return result;
193+
/**
194+
* Test whether one location (a) is better to use than another (b).
195+
*/
196+
private static boolean isBetterThan(Location locationA, Location locationB) {
197+
if (locationA == null) {
198+
return false;
199+
}
200+
if (locationB == null) {
201+
return true;
202+
}
203+
// A provider is better if the reading is sufficiently newer. Heading
204+
// underground can cause GPS to stop reporting fixes. In this case it's
205+
// appropriate to revert to cell, even when its accuracy is less.
206+
if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
207+
return true;
208+
}
209+
210+
// A provider is better if it has better accuracy. Assuming both readings
211+
// are fresh (and by that accurate), choose the one with the smaller
212+
// accuracy circle.
213+
if (!locationA.hasAccuracy()) {
214+
return false;
215+
}
216+
if (!locationB.hasAccuracy()) {
217+
return true;
218+
}
219+
return locationA.getAccuracy() < locationB.getAccuracy();
239220
}
240221

241222
private void updateFusedLocation() {
242-
// naive fusion
243-
mNetworkWeight = weighAccuracy(mNetworkLocation) * weighAge(mNetworkLocation);
244-
mGpsWeight = weighAccuracy(mGpsLocation) * weighAge(mGpsLocation);
245-
// scale mNetworkWeight and mGpsWeight so that they add to 1
246-
double totalWeight = mNetworkWeight + mGpsWeight;
247-
mNetworkWeight /= totalWeight;
248-
mGpsWeight /= totalWeight;
249-
250-
Location fused = new Location(LocationManager.FUSED_PROVIDER);
251-
// fuse lat/long
252-
// assumes the two locations are close enough that earth curvature doesn't matter
253-
fused.setLatitude(weigh(mGpsLocation.getLatitude(), mNetworkLocation.getLatitude()));
254-
fused.setLongitude(weigh(mGpsLocation.getLongitude(), mNetworkLocation.getLongitude(),
255-
-180.0, 180.0));
256-
257-
// fused accuracy
258-
//TODO: use some real math instead of this crude fusion
259-
// one suggestion is to fuse in a quadratic manner, eg
260-
// sqrt(weigh(gpsAcc^2, netAcc^2)).
261-
// another direction to explore is to consider the difference in the 2
262-
// locations. If the component locations overlap, the fused accuracy is
263-
// better than the component accuracies. If they are far apart,
264-
// the fused accuracy is much worse.
265-
fused.setAccuracy((float)weigh(mGpsLocation.getAccuracy(), mNetworkLocation.getAccuracy()));
266-
267-
// fused time - now
268-
fused.setTime(System.currentTimeMillis());
269-
fused.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
270-
271-
// fuse altitude
272-
if (mGpsLocation.hasAltitude() && !mNetworkLocation.hasAltitude() &&
273-
mGpsWeight > WEIGHT_THRESHOLD) {
274-
fused.setAltitude(mGpsLocation.getAltitude()); // use GPS
275-
} else if (!mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude() &&
276-
mNetworkWeight > WEIGHT_THRESHOLD) {
277-
fused.setAltitude(mNetworkLocation.getAltitude()); // use Network
278-
} else if (mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude()) {
279-
fused.setAltitude(weigh(mGpsLocation.getAltitude(), mNetworkLocation.getAltitude()));
280-
}
281-
282-
// fuse speed
283-
if (mGpsLocation.hasSpeed() && !mNetworkLocation.hasSpeed() &&
284-
mGpsWeight > WEIGHT_THRESHOLD) {
285-
fused.setSpeed(mGpsLocation.getSpeed()); // use GPS if its not too old
286-
} else if (!mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed() &&
287-
mNetworkWeight > WEIGHT_THRESHOLD) {
288-
fused.setSpeed(mNetworkLocation.getSpeed()); // use Network
289-
} else if (mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed()) {
290-
fused.setSpeed((float)weigh(mGpsLocation.getSpeed(), mNetworkLocation.getSpeed()));
291-
}
292-
293-
// fuse bearing
294-
if (mGpsLocation.hasBearing() && !mNetworkLocation.hasBearing() &&
295-
mGpsWeight > WEIGHT_THRESHOLD) {
296-
fused.setBearing(mGpsLocation.getBearing()); // use GPS if its not too old
297-
} else if (!mGpsLocation.hasBearing() && mNetworkLocation.hasBearing() &&
298-
mNetworkWeight > WEIGHT_THRESHOLD) {
299-
fused.setBearing(mNetworkLocation.getBearing()); // use Network
300-
} else if (mGpsLocation.hasBearing() && mNetworkLocation.hasBearing()) {
301-
fused.setBearing((float)weigh(mGpsLocation.getBearing(), mNetworkLocation.getBearing(),
302-
0.0, 360.0));
223+
// may the best location win!
224+
if (isBetterThan(mGpsLocation, mNetworkLocation)) {
225+
mFusedLocation = new Location(mGpsLocation);
226+
} else {
227+
mFusedLocation = new Location(mNetworkLocation);
303228
}
304-
305229
if (mNetworkLocation != null) {
306-
fused.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, mNetworkLocation);
230+
mFusedLocation.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, mNetworkLocation);
307231
}
308-
309-
mFusedLocation = fused;
232+
mFusedLocation.setProvider(LocationManager.FUSED_PROVIDER);
310233

311234
mCallback.reportLocation(mFusedLocation);
312235
}
@@ -349,9 +272,9 @@ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
349272
StringBuilder s = new StringBuilder();
350273
s.append("mEnabled=" + mEnabled).append(' ').append(mRequest).append('\n');
351274
s.append("fused=").append(mFusedLocation).append('\n');
352-
s.append(String.format("gps %.3f %s\n", mGpsWeight, mGpsLocation));
275+
s.append(String.format("gps %s\n", mGpsLocation));
353276
s.append(" ").append(mStats.get(GPS)).append('\n');
354-
s.append(String.format("net %.3f %s\n", mNetworkWeight, mNetworkLocation));
277+
s.append(String.format("net %s\n", mNetworkLocation));
355278
s.append(" ").append(mStats.get(NETWORK)).append('\n');
356279
pw.append(s);
357280
}

0 commit comments

Comments
 (0)