14

I'm currently working on a location tracking app and I have difficulties with inaccurate location updates from my CLLocationManager. This causes my app to track distance which is in fact only caused by inaccurate GPS readings.

Inaccurate location readings

I can even leave my iPhone on the table with my app turned on and in few minutes my app tracks hundreds of meters worth of distance just because of this flaw.

Here's my initialization code:

- (void)initializeTracking {
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    self.locationManager.distanceFilter = 5;

    [self.locationManager startUpdatingLocation];
}

Thanks in advance! :-)

Petr Mánek
  • 1,046
  • 1
  • 9
  • 24
  • i am facing same problem, could u update ur code how u fix that issue. it would be good for us – ChenSmile Nov 24 '16 at 08:14
  • 1
    @Imran I never really changed `- (void)initializeTracking`. I only changed `- (void)locationManaged:didUpdateLocation` according to suggestions in progrmr's and Max_Power89's answers. If I had to do the same thing now, I would not implement it myself again. There are some CocoaPods solving this problem, for instance `INTULocationManager`. – Petr Mánek Nov 24 '16 at 10:46
  • thanks for ur sweet reply. hope u could do that. Could u tell me what changes u made on (void)locationManaged:didUpdateLocation bro. it would be easy to fix my issue – ChenSmile Nov 24 '16 at 11:31

4 Answers4

25

One of the ways I solved this in a similar application is to discard location updates where the distance change is somewhat less than the horizontal accuracy reported in that location update.

Given a previousLocation, then for a newLocation, compute distance from the previousLocation. If that distance >= (horizontalAccuracy * 0.5) then we used that location and that location becomes our new previousLocation. If the distance is less then we discard that location update, don't change previousLocation and wait for the next location update.

That worked well for our purposes, you might try something like that. If you still find too many updates that are noise, increase the 0.5 factor, maybe try 0.66.

You may also want to guard against cases when you are just starting to get a fix, where you get a series of location updates that appear to move but really what is happening is that the accuracy is improving significantly.

I would avoid starting any location tracking or distance measuring with a horizontal accuracy > 70 meters. Those are poor quality positions for GNSS, although that may be all you get when in an urban canyon, under heavy tree canopy, or other poor signal conditions.

progrmr
  • 75,956
  • 16
  • 112
  • 147
  • Thank you, this approach works really well in combination with accuracy-based filtering described in the other post. :-) – Petr Mánek Jul 22 '14 at 10:36
  • would the `horiztonalAccuracy` ever be higher than the `desiredAccuracy`? Or CoreLocation itself filters those out? – mfaani Sep 05 '18 at 14:57
  • 1
    @Honey yes, `horizontalAccuracy` can be higher or lower than `desiredAccuracy`. You will get a location with the best accuracy available at the time. It is up to you to filter our the ones you don't want to use (higher value == poor accuracy). For Example, it is typical to get poor accuracy (1000m) at the beginning, then it gets better in a few seconds or more, it can get as good as 5m accuracy, all while `desiredAccuracy` is set to 50m. – progrmr Sep 13 '18 at 01:49
5

I've used this method to retrive the desired accuracy of the location (In SWIFT)

let TIMEOUT_INTERVAL = 3.0
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
    let newLocation = locations.last as! CLLocation
    println("didupdateLastLocation \(newLocation)")
    //The last location must not be capured more then 3 seconds ago
    if newLocation.timestamp.timeIntervalSinceNow > -3 &&
        newLocation.horizontalAccuracy > 0 {
            var distance = CLLocationDistance(DBL_MAX)
            if let location = self.lastLocation {
                distance = newLocation.distanceFromLocation(location)
            }
            if self.lastLocation == nil ||
                self.lastLocation!.horizontalAccuracy > newLocation.horizontalAccuracy {
                    self.lastLocation = newLocation
                    if newLocation.horizontalAccuracy <= self.locationManager.desiredAccuracy {
                        //Desired location Found
                        println("LOCATION FOUND")
                        self.stopLocationManager()
                    }
            } else if distance < 1 {
                let timerInterval = newLocation.timestamp.timeIntervalSinceDate(self.lastLocation!.timestamp)
                if timerInterval >= TIMEOUT_INTERVAL {
                    //Force Stop
                    stopLocationManager()
                }
            }
    }

Where:

if newLocation.timestamp.timeIntervalSinceNow > -3 &&
        newLocation.horizontalAccuracy > 0 {

The last location retrieved must not be captured more then 3 seconds ago and the last location must have a valid horizontal accuracy (if less then 1 means that it's not a valid location).

Then we're going to set a distance with a default value:

            var distance = CLLocationDistance(DBL_MAX)

Calculate the distance from the last location retrieved to the new location:

if let location = self.lastLocation {
                distance = newLocation.distanceFromLocation(location)
            }

If our local last location hasn't been setted yet or if the new location horizontally accuracy it's better then the actual one, then we are going to set our local location to the new location:

if self.lastLocation == nil ||
        self.lastLocation!.horizontalAccuracy > newLocation.horizontalAccuracy {
            self.lastLocation = newLocation

The next step it's to check whether the accuracy from the location retrieved it's good enough. To do that we check if the horizontalDistance of the location retrieved it lest then the desiredAccurancy. If this is case we can stop our manager:

if newLocation.horizontalAccuracy <= self.locationManager.desiredAccuracy {
                        //Desired location Found
                        self.stopLocationManager()
                    }

With the last if we're going to check if the distance from the last location retrieved and the new location it's less the one (that means that the 2 locations are very close). If this it's the case then we're going to get the time interval from the last location retrieved and the new location retrieved, and check if the interval it's more then 3 seconds. If this is the case, this mean that it's more then 3 seconds that we're not receiving a location which is more accurate of the our local location, and so we can stop the location services:

else if distance < 1 {
                let timerInterval = newLocation.timestamp.timeIntervalSinceDate(self.lastLocation!.timestamp)
                if timerInterval >= TIMEOUT_INTERVAL {
                    //Force Stop
                    println("Stop location timeout")
                    stopLocationManager()
                }
            }
Max_Power89
  • 1,710
  • 1
  • 21
  • 38
  • 1
    if u could convert this code in objective c, it would be much easier to get ur point ..thanks – ChenSmile Nov 24 '16 at 08:46
  • 1
    I'm using kCLLocationAccuracyBest for desiredAccuracy and it's -1. If I'm not mistaken, horizontalAccuracy < 0 is invalid value, that we should throw away. So this line of code "if newLocation.horizontalAccuracy <= self.locationManager.desiredAccuracy", it's only rarely get executed. – KMC Dec 17 '16 at 19:03
2

This is always a problem with satellite locations. It is an estimate and estimates can vary. Each new report is a new estimate. What you need is a position clamp that ignores values when there is no movement.

You might try to use sensors to know if the device is actually moving. Look at accelerometer data, if it isn't changing then the device isn't moving even though GPS says it is. Of course, there is noise on the accelerometer data so you have to filter that out also.

This is a tricky problem to solve.

user2246302
  • 386
  • 4
  • 11
0

There's really not a whole lot more you can do to improve what the operating system and your current reception gives to you. On first look it doesn't look like there's anything wrong with your code - when it comes to iOS location updates you're really at the mercy of the OS and your service.

What you CAN do is control what locations you pay attention to. If I were you in my didUpdateLocations function when you get callbacks from the OS with new locations - you could ignore any locations with horizontal accuracies greater than some predefined threshold, maybe 25m? You would end up with less location updates to use but you'd have less noise.

Mike
  • 9,765
  • 5
  • 34
  • 59
  • 1
    There is a lot he can do, you might look at other Apps that do not increase distance counting while sitting on his desk. – AlexWien Jul 21 '14 at 14:46
  • What does "increase distance counting" mean? He's looking for ways to get more accurate location updates from the OS - feel free to share a better solution, I'd be interested to know if there was one. – Mike Jul 21 '14 at 14:47
  • The graphics shows that he is standing still at that road or sitting at his (office) desk inside the building nearby. In both cases the distance accumulation should not be raising, read his question again: his goal is: "distance tracking" – AlexWien Jul 21 '14 at 14:52
  • I was afraid of this, I'm going to try out your suggestion now. I've also changed the _desiredAccuracy_ to _kCLLocationAccuracyNearestTenMeters_ and _distanceFilter_ to _20_. Do you have any preferred configuration of these values? I wonder how other apps cope with this noise, for instance **Nike+ running**. It runs on the same device as my app but always ends up with the correct location data, how is this possible? – Petr Mánek Jul 21 '14 at 14:54
  • My aim is to track distance as accurately as possible and make the line in the picture as smooth as possible (i.e. minimize the noise). – Petr Mánek Jul 21 '14 at 14:59
  • 1
    It *could* be possible if they ignore all location updates with accuracies worse than 5m say, but of course I don't know how they do it. As far as I know there's no way to limit the noise - you don't control what you get from the OS - only what you DO with the info from the OS. – Mike Jul 21 '14 at 15:02
  • I hope there's some trick to do this effectively. This is my first location tracking app and I'd be greatly discouraged if issues like this cannot be solved in a better way... – Petr Mánek Jul 21 '14 at 15:09
  • So far I've changed the settings of my _locationManager_ and added the accuracy threshold filter. My horizontal accuracy threshold is currently set to 30 meters and I've added a mechanism which ignores the threshold if there is no location update for too long. Gonna test this setup now, if you have any other suggestions, feel free to comment. – Petr Mánek Jul 21 '14 at 15:13
  • Do you have any logic to ignore inaccurate updates? Simply setting the accuracy threshold doesn't prevent the OS from giving you bad locations. I have mine set to BEST and I've gotten accuracies > 100m. – Mike Jul 21 '14 at 15:14
  • Yes, apart from the new configuration of my locationManager I've added an explicit condition into _locationManager:didUpdateLocations:_ to exclude accuracies > 30m. This condition is suspended when there was no update in the last 10 mins. – Petr Mánek Jul 21 '14 at 15:21