Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfb29465 authored by Victoria Lease's avatar Victoria Lease Committed by Android Git Automerger
Browse files

am 04bb190c: am f402fa46: Merge "Simplify fused location provider." into jb-mr1-dev

* commit '04bb190c':
  Simplify fused location provider.
parents df4bcd6a 04bb190c
Loading
Loading
Loading
Loading
+37 −114
Original line number Original line Diff line number Diff line
@@ -42,16 +42,7 @@ public class FusionEngine implements LocationListener {
    private static final String NETWORK = LocationManager.NETWORK_PROVIDER;
    private static final String NETWORK = LocationManager.NETWORK_PROVIDER;
    private static final String GPS = LocationManager.GPS_PROVIDER;
    private static final String GPS = LocationManager.GPS_PROVIDER;


    // threshold below which a location is considered stale enough
    public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000; // 11 seconds
    // that we shouldn't use its bearing, altitude, speed etc
    private static final double WEIGHT_THRESHOLD = 0.5;
    // accuracy in meters at which a Location's weight is halved (compared to 0 accuracy)
    private static final double ACCURACY_HALFLIFE_M = 20.0;
    // age in seconds at which a Location's weight is halved (compared to 0 age)
    private static final double AGE_HALFLIFE_S = 60.0;

    private static final double ACCURACY_DECAY_CONSTANT_M = Math.log(2) / ACCURACY_HALFLIFE_M;
    private static final double AGE_DECAY_CONSTANT_S = Math.log(2) / AGE_HALFLIFE_S;


    private final Context mContext;
    private final Context mContext;
    private final LocationManager mLocationManager;
    private final LocationManager mLocationManager;
@@ -62,8 +53,6 @@ public class FusionEngine implements LocationListener {
    private Location mFusedLocation;
    private Location mFusedLocation;
    private Location mGpsLocation;
    private Location mGpsLocation;
    private Location mNetworkLocation;
    private Location mNetworkLocation;
    private double mNetworkWeight;
    private double mGpsWeight;


    private boolean mEnabled;
    private boolean mEnabled;
    private ProviderRequestUnbundled mRequest;
    private ProviderRequestUnbundled mRequest;
@@ -102,10 +91,6 @@ public class FusionEngine implements LocationListener {
        Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
        Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
    }
    }


    private boolean isAvailable() {
        return mStats.get(GPS).available || mStats.get(NETWORK).available;
    }

    /** Called on mLooper thread */
    /** Called on mLooper thread */
    public void enable() {
    public void enable() {
        mEnabled = true;
        mEnabled = true;
@@ -130,7 +115,6 @@ public class FusionEngine implements LocationListener {
        public boolean requested;
        public boolean requested;
        public long requestTime;
        public long requestTime;
        public long minTime;
        public long minTime;
        public long lastRequestTtff;
        @Override
        @Override
        public String toString() {
        public String toString() {
            StringBuilder s = new StringBuilder();
            StringBuilder s = new StringBuilder();
@@ -171,9 +155,6 @@ public class FusionEngine implements LocationListener {
            return;
            return;
        }
        }


        ProviderStats gpsStats = mStats.get(GPS);
        ProviderStats networkStats = mStats.get(NETWORK);

        long networkInterval = Long.MAX_VALUE;
        long networkInterval = Long.MAX_VALUE;
        long gpsInterval = Long.MAX_VALUE;
        long gpsInterval = Long.MAX_VALUE;
        for (LocationRequest request : mRequest.getLocationRequests()) {
        for (LocationRequest request : mRequest.getLocationRequests()) {
@@ -209,104 +190,46 @@ public class FusionEngine implements LocationListener {
        }
        }
    }
    }


    private static double weighAccuracy(Location loc) {
    /**
        double accuracy = loc.getAccuracy();
     * Test whether one location (a) is better to use than another (b).
        return Math.exp(-accuracy * ACCURACY_DECAY_CONSTANT_M);
     */
    private static boolean isBetterThan(Location locationA, Location locationB) {
      if (locationA == null) {
        return false;
      }
      }

      if (locationB == null) {
    private static double weighAge(Location loc) {
        return true;
        long ageSeconds = SystemClock.elapsedRealtimeNanos() - loc.getElapsedRealtimeNanos();
        ageSeconds /= 1000000000L;
        if (ageSeconds < 0) ageSeconds = 0;
        return Math.exp(-ageSeconds * AGE_DECAY_CONSTANT_S);
      }
      }

      // A provider is better if the reading is sufficiently newer.  Heading
    private double weigh(double gps, double network) {
      // underground can cause GPS to stop reporting fixes.  In this case it's
        return (gps * mGpsWeight) + (network * mNetworkWeight);
      // appropriate to revert to cell, even when its accuracy is less.
      if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
        return true;
      }
      }


    private double weigh(double gps, double network, double wrapMin, double wrapMax) {
      // A provider is better if it has better accuracy.  Assuming both readings
        // apply aliasing
      // are fresh (and by that accurate), choose the one with the smaller
        double wrapWidth = wrapMax - wrapMin;
      // accuracy circle.
        if (gps - network > wrapWidth / 2) network += wrapWidth;
      if (!locationA.hasAccuracy()) {
        else if (network - gps > wrapWidth / 2) gps += wrapWidth;
        return false;

      }
        double result = weigh(gps, network);
      if (!locationB.hasAccuracy()) {

        return true;
        // remove aliasing
      }
        if (result > wrapMax) result -= wrapWidth;
      return locationA.getAccuracy() < locationB.getAccuracy();
        return result;
    }
    }


    private void updateFusedLocation() {
    private void updateFusedLocation() {
        // naive fusion
        // may the best location win!
        mNetworkWeight = weighAccuracy(mNetworkLocation) * weighAge(mNetworkLocation);
        if (isBetterThan(mGpsLocation, mNetworkLocation)) {
        mGpsWeight = weighAccuracy(mGpsLocation) * weighAge(mGpsLocation);
            mFusedLocation = new Location(mGpsLocation);
        // scale mNetworkWeight and mGpsWeight so that they add to 1
        } else {
        double totalWeight = mNetworkWeight + mGpsWeight;
            mFusedLocation = new Location(mNetworkLocation);
        mNetworkWeight /= totalWeight;
        mGpsWeight /= totalWeight;

        Location fused = new Location(LocationManager.FUSED_PROVIDER);
        // fuse lat/long
        // assumes the two locations are close enough that earth curvature doesn't matter
        fused.setLatitude(weigh(mGpsLocation.getLatitude(), mNetworkLocation.getLatitude()));
        fused.setLongitude(weigh(mGpsLocation.getLongitude(), mNetworkLocation.getLongitude(),
                -180.0, 180.0));

        // fused accuracy
        //TODO: use some real math instead of this crude fusion
        // one suggestion is to fuse in a quadratic manner, eg
        // sqrt(weigh(gpsAcc^2, netAcc^2)).
        // another direction to explore is to consider the difference in the 2
        // locations. If the component locations overlap, the fused accuracy is
        // better than the component accuracies. If they are far apart,
        // the fused accuracy is much worse.
        fused.setAccuracy((float)weigh(mGpsLocation.getAccuracy(), mNetworkLocation.getAccuracy()));

        // fused time - now
        fused.setTime(System.currentTimeMillis());
        fused.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());

        // fuse altitude
        if (mGpsLocation.hasAltitude() && !mNetworkLocation.hasAltitude() &&
                mGpsWeight > WEIGHT_THRESHOLD) {
            fused.setAltitude(mGpsLocation.getAltitude());   // use GPS
        } else if (!mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude() &&
                mNetworkWeight > WEIGHT_THRESHOLD) {
            fused.setAltitude(mNetworkLocation.getAltitude());   // use Network
        } else if (mGpsLocation.hasAltitude() && mNetworkLocation.hasAltitude()) {
            fused.setAltitude(weigh(mGpsLocation.getAltitude(), mNetworkLocation.getAltitude()));
        }

        // fuse speed
        if (mGpsLocation.hasSpeed() && !mNetworkLocation.hasSpeed() &&
                mGpsWeight > WEIGHT_THRESHOLD) {
            fused.setSpeed(mGpsLocation.getSpeed());   // use GPS if its not too old
        } else if (!mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed() &&
                mNetworkWeight > WEIGHT_THRESHOLD) {
            fused.setSpeed(mNetworkLocation.getSpeed());   // use Network
        } else if (mGpsLocation.hasSpeed() && mNetworkLocation.hasSpeed()) {
            fused.setSpeed((float)weigh(mGpsLocation.getSpeed(), mNetworkLocation.getSpeed()));
        }

        // fuse bearing
        if (mGpsLocation.hasBearing() && !mNetworkLocation.hasBearing() &&
                mGpsWeight > WEIGHT_THRESHOLD) {
            fused.setBearing(mGpsLocation.getBearing());   // use GPS if its not too old
        } else if (!mGpsLocation.hasBearing() && mNetworkLocation.hasBearing() &&
                mNetworkWeight > WEIGHT_THRESHOLD) {
            fused.setBearing(mNetworkLocation.getBearing());   // use Network
        } else if (mGpsLocation.hasBearing() && mNetworkLocation.hasBearing()) {
            fused.setBearing((float)weigh(mGpsLocation.getBearing(), mNetworkLocation.getBearing(),
                    0.0, 360.0));
        }
        }

        if (mNetworkLocation != null) {
        if (mNetworkLocation != null) {
            fused.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, mNetworkLocation);
            mFusedLocation.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, mNetworkLocation);
        }
        }

        mFusedLocation.setProvider(LocationManager.FUSED_PROVIDER);
        mFusedLocation = fused;


        mCallback.reportLocation(mFusedLocation);
        mCallback.reportLocation(mFusedLocation);
    }
    }
@@ -349,9 +272,9 @@ public class FusionEngine implements LocationListener {
        StringBuilder s = new StringBuilder();
        StringBuilder s = new StringBuilder();
        s.append("mEnabled=" + mEnabled).append(' ').append(mRequest).append('\n');
        s.append("mEnabled=" + mEnabled).append(' ').append(mRequest).append('\n');
        s.append("fused=").append(mFusedLocation).append('\n');
        s.append("fused=").append(mFusedLocation).append('\n');
        s.append(String.format("gps %.3f %s\n", mGpsWeight, mGpsLocation));
        s.append(String.format("gps %s\n", mGpsLocation));
        s.append("    ").append(mStats.get(GPS)).append('\n');
        s.append("    ").append(mStats.get(GPS)).append('\n');
        s.append(String.format("net %.3f %s\n", mNetworkWeight, mNetworkLocation));
        s.append(String.format("net %s\n", mNetworkLocation));
        s.append("    ").append(mStats.get(NETWORK)).append('\n');
        s.append("    ").append(mStats.get(NETWORK)).append('\n');
        pw.append(s);
        pw.append(s);
    }
    }