Getting an accurate location with CLLocationManager - iphone

I'm beginning to mess around with the CLLocationManager and I am trying to use the callback method below. I went outside to test it on my iPhone, and with the first few updates, I seem to be getting gibberish values, like all of a sudden my distance is 39meters even though I haven't moved anywhere. Or sometimes it will start at 5, and then jump to 20 meters, again without me moving anywhere. I went outside and walked, and the updates from the initial starting point seemed 'ok,' and then when I walked back, I got back to the original 39 meters starting point. I was wondering if what I am doing below is correct. I also included my viewDidLoad method where I initialize my CLLocationManager object.
Is there a way to ensure that my first values are accurate? Thanks in advance.
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if (startingPoint == nil) {
NSLog(#"NIL starting point");
self.startingPoint = newLocation;
}
CLLocationDistance distance = [newLocation distanceFromLocation:startingPoint];
NSString *distanceString = [[NSString alloc] initWithFormat:#"%g m", distance];
distanceLabel.text = distanceString;
[distanceString release];
numberOfUpdates++;
NSString *countString = [[NSString alloc] initWithFormat:#"%d", numberOfUpdates];
countLabel.text = countString;
[countString release];
}

GPS is an imperfect technology. Atmospheric conditions, sattelite availability (and position), sky visibility, signals bouncing off nearby objects (buildings) all contribute to it's inherent inaccuracy. Though there is even an "accuracy" value (which is usually pretty good) - even this is not completely reliable.
Airplanes are not allowed to use GPS for precision approaches - even their receivers aren't accurate enough, and they require other technologies (which have their own issues).
Try running the standard "Maps" application and use it as a comparison. I think your code is good - it's jut GPS.
Of course I am saying this because I am working on my own maritime navigation application, an running into all these issues myself.

Though this is an old question I still like to answer. The first call to "didUpdateToLocation" usually is some old value and you should always check the timestamp. From the Apple documentation:
NSDate* eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0)
{
// use the value
}

In your didUpdate method, you can test newLocation's .horizontalAccuracy property for an acceptable value and toss any values that are impossible (or even unlikely!).

Related

didUpdateToLocation called multiple times

I have an application in which user track his/her route when jogging or cycling, So i need perfect location, so user's routes will be perfect.
But, I have one problem in this,
locManager = [[CLLocationManager alloc] init];
[locManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[locManager setDelegate:self];
[locManager startUpdatingLocation];
In viewDidLoad. Using this didUpdateToLocation method called multiple times when I just dont move device a little and on map very strange route draw.
I just cant understand why this happen, if I am doing some wrong or missing something.
Thanks.......
I use locationManager.distanceFilter = 500; (or so) // meters
to prevent multiple calls from happening. just remember to call this BEFORE you start updating your location
You can set the distancefilter of the location manager hope this may help you
locationManager=[[CLLocationManager alloc] init];
locationManager.delegate=self;
locationManager.desiredAccuracy=kCLLocationAccuracyNearestTenMeters;
locationManager.distanceFilter=10.0;
[locationManager startUpdatingLocation];
When you first start location services, you'll generally see multiple location updates come whether you're moving or not. If you examine the horizontalAccuracy of the locations as they come in, you'll see that while it's "warming" up it will show a series of locations with greater and greater accuracy (i.e. smaller and smaller horizontalAccuracy values) until it reaches quiescence.
You could disregard those initial locations until horizontalAccuracy falls below a certain value. Or, better, during start up, you could disregard the previous location if (a) the distance between a new location and the old location is less than the horizontalAccuracy of the old location and (b) if the horizontalAccuracy of the new location is less than that of the prior location.
For example, let's assume you're maintaining an array of CLLocation objects, as well as a reference to the last drawn path:
#property (nonatomic, strong) NSMutableArray *locations;
#property (nonatomic, weak) id<MKOverlay> pathOverlay;
Furthermore, let's assume your location update routine is just adding to the array of locations and then indicating that the path should be redrawn:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"%s", __FUNCTION__);
CLLocation* location = [locations lastObject];
[self.locations addObject:location];
[self addPathToMapView:self.mapView];
}
Then the addPathToMapView can therefore remove the second from last location if it's less accurate than the last one and if the distance between them is less than the most recent location's accuracy.
- (void)addPathToMapView:(MKMapView *)mapView
{
NSInteger count = [self.locations count];
// let's see if we should remove the penultimate location
if (count > 2)
{
CLLocation *lastLocation = [self.locations lastObject];
CLLocation *previousLocation = self.locations[count - 2];
// if the very last location is more accurate than the previous one
// and if distance between the two of them is less than the accuracy,
// then remove that `previousLocation` (and update our count, appropriately)
if (lastLocation.horizontalAccuracy < previousLocation.horizontalAccuracy &&
[lastLocation distanceFromLocation:previousLocation] < lastLocation.horizontalAccuracy)
{
[self.locations removeObjectAtIndex:(count - 2)];
count--;
}
}
// now let's build our array of coordinates for our MKPolyline
CLLocationCoordinate2D coordinates[count];
NSInteger numberOfCoordinates = 0;
for (CLLocation *location in self.locations)
{
coordinates[numberOfCoordinates++] = location.coordinate;
}
// if there is a path to add to our map, do so
MKPolyline *polyLine = nil;
if (numberOfCoordinates > 1)
{
polyLine = [MKPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
[mapView addOverlay:polyLine];
}
// if there was a previous path drawn, remove it
if (self.pathOverlay)
[mapView removeOverlay:self.pathOverlay];
// save the current path
self.pathOverlay = polyLine;
}
Bottom line, just get rid of locations that are less accurate than the next one you have. You could get even more aggressive in the pruning process if you want, but there are tradeoffs there, but hopefully this illustrates the idea.
startUpdatingLocation
Will continuously update a user's location even when the location does not change. You just need to structure your app to handle these continuous updates according to your needs.
Try reading Apple's documentation on this subject. It is confusing at first but try anyway.
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW1
I think this is what you need.startMonitoringForRegion:desiredAccuracy
for Example see the following github link.
Try this Bread Crumb sample code provided by Apple..
http://developer.apple.com/library/ios/#samplecode/Breadcrumb/Introduction/Intro.html
Add this,
[locManager stopUpdatingLocation];
into your updateUserLocation delegate method.
Review the following code snippet:
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
[_locationManager stopUpdatingLocation];
}
locationManager.startUpdatingLocation() fetch location continuously and didUpdateLocations method calls several times,
Just set the value for locationManager.distanceFilter value before calling locationManager.startUpdatingLocation().
As I set 200 working fine
locationManager.distanceFilter = 200
locationManager.startUpdatingLocation()

CLLocationManager weird issues

I have a MKMapView whereby I drop an annotation everytime the view loads, or when showLocation custom class method is called.
I need the accuracy to be the best
-(void)viewDidLoad {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[locationManager startUpdatingLocation];
}
-(IBAction) showLocation:(id) sender{
[locationManager startUpdatingLocation];
}
- (void) locationManager:(CLLocationManager *) manager
didUpdateToLocation:(CLLocation *) newLocation
fromLocation:(CLLocation *) oldLocation {
// start geocoding with newLocation coordinate which will automatically set annotation.
SVGeocoder *geocodeRequest = [[SVGeocoder alloc]
initWithCoordinate:CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude)];
[geocodeRequest setDelegate:self];
[geocodeRequest startAsynchronous];
[geocodeRequest release];
[locationManager stopUpdatingLocation];
}
My question is when will the didUpdateToLocation method be called? Only after a new location is found when I perform [locationManager startUpdatingLocation]?
I am facing some weird issue when the user is travelling and on stationary.
Say the user is travelling from point A->B->C->D with 1 min intervals between points. When I call my method at point C, sometime it returns the coordinates of point A, sometimes point B and sometimes C. It's just random.
It's even more weird when I am on stationary. I get different coordinates when I calls showLocation method even though I am hooked up on my house WiFi.
I was thinking of implementing the didUpdateToLocation to get the best result it can within 5secs. If within the 5secs, it finds a particular location of an accuracy I had defined, then use the coordinate. If not, use the best it has found within the 5sec time frame. But as I am new I am not sure how to code something like that. I read up NSTimer and it seems like it might work.
Any advices guys?
Thanks a lot in advance!
One of the reasons you are receiving the location from point A is that CoreLocation is returning the last valid location it had first until it can obtain a more accurate location. When you call [locationManager startUpdatingLocation]; it will return the -didUpdateToLocation over and over until you are statisfied and finally call -stopUpdatingLocation.
I think you just need to allow a bit of time for it to get a better location fix before you stop updating your location. I would consider moving the stop updating location from your -didUpdateToLocation to a different method.
Remove [locationManager stopUpdatingLocation]; from your code and try .

I can't get the gps coordinates for a persistent period of time

I have implemented the standard method of retrieving the coordinates from the gps using - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation.
The problem is that this function is called only on initialization and not throughout the life of the program. Is this normal?
In android you would implement a listener and you would get data instantly.
Is this not the wright way how i'm doing it? If it is, what could be the problem? (btw i've checked, i don't stopUpdatingLocation)
I have a CLLocationManager inherited class named testing and initialize it
testing* cllm = [[testing alloc] init];
cllm.delegate = self;
i later start the updating
[cllm startUpdatingLocation];
self.locationManagerDelegate = delegate;
and later is called
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
but after that it isn't called anymore. I need it to be called frequently so that i may calculate the distance to a certain point X from where i am.
Agreed with #Matt, without more code the best solution I can offer is this to tell it to update every time the device is moved with:
[self.locationManager setDistanceFiler:kCLDistanceFilterNone]
Update
I went through past projects and found the code I believe you are looking for assuming your location manager subclass is working properly
- (void)viewDidLoad {
[super viewDidLoad];
//Location
// create new location manager
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
// start location manager
[self.locationManager startUpdatingLocation];
}
-(void) distanceBetweenUserandPin {
CLLocation *currentUserLocation = [[CLLocation alloc] initWithLatitude:_currentLocation.latitude longitude:_currentLocation.longitude];
CLLocation *currentPinLocation = [[CLLocation alloc] initWithLatitude:_pinLocation.latitude longitude:_pinLocation.longitude];
CLLocationDistance distanceBetweenUserAndPinMeters = [currentUserLocation distanceFromLocation:currentPinLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
//This successfully saves Lat, Long Data to a point location
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude);
NSLog(#"%f, %f", location.latitude, location.longitude);
//This assigns the value of location to the ivar _currentLocation
_currentLocation = CLLocationCoordinate2DMake(location.latitude, location.longitude);
NSLog(#"%f, %f", _currentLocation.latitude, _currentLocation.longitude);
}
First, it seems strange to me that you would use a subclass of CLLocationManager, since I'm not sure what benefit that provides you. Assuming that's not the problem, however...
From the CLLocationManager documentation:
This method returns immediately. Calling this method causes the
location manager to obtain an initial location fix (which may take
several seconds) and notify your delegate by calling its
locationManager:didUpdateToLocation:fromLocation: method. After that,
the receiver generates update events primarily when the value in the
distanceFilter property is exceeded. Updates may be delivered in other
situations though. For example, the receiver may send another
notification if the hardware gathers a more accurate location reading.
What's happening is that it is being called once for the initial position fix, but it isn't calling again because other conditions haven't changed. If a user doesn't move anywhere, then new location data won't be provided since it will be the same as last time (with a few exceptions as mentioned in the docs).
When you're testing your app, make sure that you try moving around and changing your location to produce an update. If that doesn't work, try experimenting with the desiredAccuracy and distanceFilter properties:
You start standard location services by calling the
startUpdatingLocation method. This service is most appropriate for
applications that need more fine-grained control over the delivery of
location events. Specifically, it takes into account the values in the
desiredAccuracy and distanceFilter property to determine when to
deliver new events.
Other than that, I'd guess it might have to do with how you're subclassing CLLocationManager. Providing some of that code might help.

Corelocation framework does not produce accurate distances

I am developing an application that computes the distance travelled by the person. I am testing it on a iPad (Device-3.2). My iPad is using WiFi to get the current location. The results are highly inaccurate even though i have filtered the values. I don't know if GPS will give me accurate results. I am pasting the entire code below. Please verify the code and in case of errors please let me know. It would b very helpful if some one test the code on iPhone(3g) or iPad(3g). If not possible then just check the logic.....also i want to compute the calories burnt ..is there any formula to do so..? I have made simple view based project.....and used a distance label in nib file to set distance value but distance is updating at a very rapid rate....please correct it.
// iPacometerViewController.h
#interface iPacometerViewController : UIViewController {
CLLocationManager *locationManager;
CLLocation *oldLocat;
CLLocation *newLocat;
IBOutlet UILabel *distanceLabel;
}
#property(nonatomic,assign)IBOutlet UILabel *distanceLabel;
#property(nonatomic,retain)CLLocationManager *locationManager;
#property(nonatomic,retain)CLLocation *oldLocat;
#property(nonatomic,retain)CLLocation *newLocat;
-(void)computeDistanceFrom:(CLLocation *)oldL tO:(CLLocation *)newL;
#end
// iPacometerViewController.m
#import "iPacometerviewController.h"
#implementation iPacometerViewController
static double distance = 0.0;
#synthesize locationManager;
#synthesize oldLocat;
#synthesize newLocat;
#synthesize distanceLabel;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//initializing location manager
locationManager =[[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager.distanceFilter = 150.0f;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
oldLocat = [[CLLocation alloc]init];
newLocat = [[CLLocation alloc]init];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
if (newLocation.horizontalAccuracy 60.0) return; // data is too long ago, don't use it
NSLog(#"oldd %#",oldLocation);
self.oldLocat = oldLocation;
self.newLocat = newLocation;
if(oldLocat!=nil)
{
[self computeDistanceFrom:oldLocat tO:newLocat];
}
}
-(void)computeDistanceFrom:(CLLocation *)oldL tO:(CLLocation *)newL
{
NSLog(#"oldd %#",oldL);
NSLog(#"new %#",newL);
CLLocationDistance currentDistance = [oldL distanceFromLocation:newL];
NSLog(#"you have travel=%f",currentDistance);
distance = distance + currentDistance;
double distanceInKm = distance/1000;
NSString *distanceLabelValue = [NSString stringWithFormat:#"%1.2f Kms",distanceInKm];
distanceLabel.text = distanceLabelValue;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)dealloc {
//[mapView release];
[oldloct release];
[newLocat release];
[locationManager release];
[super dealloc];
}
#end
your logic is fine. Its the iPhone's logic that is bad.
There are two main issues that I see when writing this sort of code.
1) What I call poisoned points. These are cached points that the iPhone reports erroneously. You can check for them by dropping any points where the timestamp is the same as or earlier than the latest point. It may also be worth recording a frequency of visits to a point. The poisoned points tend to be visited time and time again (maybe 5 times) each time as a jump from your real track. If you can spot them, you can rule them out.
2) Accuracy - especially height changes. Look at the horizontal and vertical accuracy figures returned with each point. And look at the height. If they are inaccurate, then the velocity and therefore distance traveled will be too. One cause of bad distances is if one end of your pair has an altitude, and the other does not: the "not" gets classed as zero - so if you are 200m above the sea level at the time you have just travelled 200m without moving!
To improve accuracy, you may be better off writing your own great circle distance algorithm (or even simple Pythagoras given the distances are small) which ignores height, or just doing some better filtering on the points you use.
I'm struggling to find a reliable way of measuring location, distance & speed with the iPhone (3GS). and iPad or Touch is just going to be worse I guess.
If you can drop me a mail at andrew dot hawken at fiftyeggs dot co dot uk I will send you my raw iPhone logging software. It would be great if you can run it for a couple of trips and let me have the results - I'm trying to solve the same problem, but only have GPS datasets to work on.

iPhone Gps logging inaccurate

I'm logging gps points during a walk. Below it shows the function that the coordinates are saved each 5 seconds.
i Did several tests but i cannot get the right accuracy i want. (When testing the sky is clear also tests in google maps shows me that the gps signal is good).
here is the code:
-(void)viewDidAppear:(BOOL)animated{
if (self.locationManager == nil){
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
// only notify under 100 m accuracy
locationManager.distanceFilter = 100.0f;
locationManager.desiredAccuracy= kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
}
- start logging
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(getData) userInfo:nil repeats:YES];
</code>
<code>
-(void)getData{
int distance;
// re-use location.
if ([ [NSString stringWithFormat:#"%1.2f",previousLat] isEqualToString:#"0.00"]){
// if previous location is not available, do nothing
distance = 0;
}else{
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:previousLat longitude:previousLong];
CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:latGlobal longitude:longGlobal];
distance = [loc1 getDistanceFrom: loc2];
}
// overwrite latGlobal with new variable
previousLat = latGlobal;
previousLong = longGlobal;
// store location and save data to database
// this part goes ok
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// track the time to get a new gps result (for gps indicator orb)
lastPointTimestamp = [newLocation.timestamp copy];
// test that the horizontal accuracy does not indicate an invalid measurement
if (newLocation.horizontalAccuracy < 0) return;
// test the age of the location measurement to determine if the measurement is cached
// don't rely on cached measurements
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 5.0) return;
latGlobal = fabs(newLocation.coordinate.latitude);
longGlobal= fabs(newLocation.coordinate.longitude);
}
I have taken a screenshot of the plot results (the walk takes 30 minutes) and an example of what i'am trying to acomplish:
http://www.flickr.com/photos/21258341#N07/4623969014/
i really hope someone can put me in the right direction.
Looking at your plots - This is exactly what I see too in an app I am working on.
Looking at your code. Whenever location is updated, locationManager calls didUpdateToLocationFromLocation - polling at 5s intervals will give you a lot of points at the same location. (but not relevant to the question)
Also I think you are leaking a lastPointTimestamp (not relevant to the question)
Right - the jumping around - you can look at the accuracy of the points : you do this in locationManager anyway but only check for <0. Is there a pattern of what the horizontal accuracy is on the jumping points ? The accuracy figure may be much worse then for other points, or one specific value (caused by a switch to cell tower triangulation).
Finally, you may need to implement filtering. Kalman filters are the over-the-top way to go, but I am looking at using low-pass filters combined with some basic physics (max acceleration & speed for walking, cycling, running, driving etc) to improve results.
Happy to collaborate on this.