Improve Accuracy of Location Manager in iOS Programming - iphone

I am making an application which tracks the user. I have notice when the application goes in the background and then when you open the app it's shows the wrong current location for the user until about 5 seconds. Is there to fix that because that 5 seconds delay ruins the tracking results ( it's adds three extra miles for no reason ).
Edit: The issue wasn't actually a "bug". I have to set in my Info.plist that I want background pocessing and boom the application tracking is super Accurate. A little tutorial to do that:
Go to Info.plist
Add an New Row called "Required background modes"
Then add again a new row called "App registers for location updates"
We are Done :)

One thing you can do is check the horizontalAccuracy property on the CLLocation that you're being returned. If this is above a certain threshold then you could throw away the result and wait for a more accurate one. If it is a number of miles out then I would expect the accuracy figure to be quite large. It's most likely using a cell site to determine location rather than GPS, and the margin of error would be much greater.
In your CLLocationManagerDelegate's locationManager:didUpdateLocations: method you could do the following:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if ([locations count] > 0) {
CLLocation *lastUpdatedLocation = [locations lastObject];
CLLocationAccuracy desiredAccuracy = 1000; // 1km accuracy
if (lastUpdatedLocation.horizontalAccuracy > desiredAccuracy) {
// This location is inaccurate. Throw it away and wait for the next call to the delegate.
return;
}
// This is where you do something with your location that's accurate enough.
}
}

Related

Iphone GPS is not giving accurate latitude and longitude

Hello Friend i have seen many post regarding accuracy problem with gps but its not working all the time
-(void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSString *latstr = [NSString stringWithFormat:#"%f", newLocation.coordinate.latitude];
NSString *longstring=[NSStringstringWithFormat:#"%f",newLocation.coordinate.longitude];
if (abs(howRecent)>5.0)
{
[self.locationManager startUpdatingLocation];
return;
}
if(abs(newLocation.horizontalAccuracy)<0.0f)
{
[self.locationManager startUpdatingLocation];
return;
}
if(newLocation.horizontalAccuracy>65.0f)
{
[self.locationManager startUpdatingLocation];
return;
}
self.latstring = [latstr copy];
self.longstr = [longstring copy];
if((updateLocationFirst||loadFirstView))
{
[[NSUserDefaults standardUserDefaults]setObject:latstring forKey:#"Latitude"];
[[NSUserDefaults standardUserDefaults]setObject:longstr forKey:#"Longitude"];
[self displayParticularDaySpecial];
loadFirstView=FALSE;
updateLocationFirst=FALSE;
[self.locationManager stopUpdatingLocation];
}
}
Here the problem is I am sending the latitude and longitude to the google api with respect to some addresses if i am decreasing the accuracy value its taking lot of time to load and this value is having problem when you reach at destination with respect to destination with 0.6 miles difference.
You should refer to the Location Awareness Programming Guide which provides excellent programming examples for determining your location.
As you'll see in the demo in that document, when you start the standard location manager, you generally tell it what accuracy you require. And once you start the location manager, you don't have to start it again (like you appear to be doing in your code sample). The didUpdateLocations method will be called again and again, with increasing accuracy, all on its own. You don't need to start the location manager again ... it keeps going until you turn it off. By definition, if you're in didUpdateLocations, it means it's already on.
I also notice that you're using the old didUpdateToLocation. If you're programming for iOS 6, you really want to use didUpdateLocations, which is the current version of that CLLocationManagerDelegate method.
Finally, you mention it takes a long time to get your location. That's very possible. In fact, in certain areas, you will never get very close (or any location at all). Your app should assume that accurate locations will take a while, and you should gracefully handle if you never get a really accurate location, which is very possible.
Have you set desiredLocationAccuracy in your CLLocationManager? By default the range is somewhat wide.
It will take some time to acquire a very exact value.

how to change gps update time in ios

I'm looking for a way to get a background location update every n minutes in my iOS application.. now i am getting update in every second.. can i change one second to three or four second or every minute...
Here is my code:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if([self.delegate conformsToProtocol:#protocol(GPSLocationDelegate)]) {
// Check if the class assigning itself as the delegate conforms to our protocol. If not, the message will go nowhere. Not good.
[self.delegate locationUpdate:newLocation];
NSLog(#"location manager");
/// alert for check msg ////
////////////////////////////
}
}
You can not do it.
Alternatively you can achieve it by below way. When you get location updates, it will have the timestamp associated with it.
Cache the time stamp on first location update. When you receive the second location update, compare the difference and check. If it is greater than your threshold limit, call your delegate and update the cached time stamp with updated location's time stamp.
You could stop the location manager when you receive a new location and schedule a timer to start it again 60 seconds past. The only problem will be that you won't get the events triggered exactly after each 60 seconds, but it may be ok.
And also be careful when you stop your location manager, as I think that its first event can be invalid (giving you just a copy of your last location), so may be you will have to keep it running for 5-10 seconds before stopping it and scheduling the next trigger.
Good luck with that!
Location updates are asynchronous, you can't control the frequency in which they appear.

Force location update

I'm attempting to overcome a sometimes-failure of didUpdateToLocation when checking local data based on current position, closing the app, traveling a bit, and opening the app again. What happens is that the user visits one place, checks the list, goes to another place, and the list is updated but using the old location data.
I would like to make sure I have a fresh newLocation.
What's wrong with this code?
double aa,bb,cc;
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[manager stopUpdatingLocation]; //only stop if new reliable pos fetched.
//old data? retry.
aa=[newLocation.timestamp timeIntervalSinceReferenceDate];
bb=[NSDate timeIntervalSinceReferenceDate];
cc=bb-aa; //must be <60 - minute-fresh
if (cc>60) {
[manager stopUpdatingLocation]; //only stop if new reliable pos fetched.
[manager startUpdatingLocation]; //only stop if new reliable pos fetched.
return;
}
...handle position
}
The symptoms are that cc is about 1200 seconds on app start, then on each retry it increases a few seconds.
I've tried -[newLocation.timestamp timeIntervalSinceNow], and similarly it increases a few seconds at each retry. At one point the interval was ~2400.
I'm using FilterNone and AccuracyBest, if that could influence it.
I'm open to alternative code solutions to make sure you have a fresh position.
The answer was that you need to write the handling the way you want it to behave.
I've read up on this common problem. I should have clarified that "force" means to start unambiguous behavior that gives a reliable position under optimal conditions for the phone. Not to busy-wait until satisfied or to expect an immediate result/error code.
From a common sense viewpoint, what would be desirable by all developers would be at least an extra method to simply ask for a position within parameters, and callback only when it's valid and within accuracy, or canceled, or timed out. If the conditions prevent it you then don't need to test out esoteric behavior to be able to handle it with custom code.
Why I needed to ask about this at all was:
no existence of a method functioning as described above
using code from a book (More Iphone 3 Development)
when writing it from scratch (looking at LocateMe.xproj), discovering that:
simulator behavior differs from phone behavior (not talking about the position itself, which is obviously as good as ip lookup can make it, but the behavior of didUpdateToLocation). Recognizing the limitations of a simulator, the method should at least behave like they do on a device. But currently, correctly written location handling/checking code just times out in the simulator (as in the LocateMe example), while incorrect code (asking once and using newLocation on callback) works.
and a bug causing didUpdateToLocation being called twice after [locationManager stopUpdatingLocation];
If on top of this you (like I) load data based on your position, and the data is required by multiple views in your application, the behavior of Apple's location-fetching methods doesn't make it easy to handle the chain of update-load-calculate-present consistently and problem-free throughout the app. Especially if your stuck between user/boss perception or decision of how it should work (a rock) and how the system works (a hard place).
Here's my current code which works on a device and in a simulator:
//ask for a position, as fresh as possible.
-(void)updateMeMap {
lastloc.coordinate=homeloc.coordinate;
lm.delegate=self;
ctr=0;
[self performSelector:#selector(stopUpd) withObject:nil afterDelay:gpstimeout];
[lm startUpdatingLocation];
}
//called on each position update.
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
ctr++;
if (ctr<15) {
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
NSLog(#"%d",ctr);
if (locationAge<60) {
NSLog(#"Found");
ctr=40;
homeloc.coordinate=newLocation.coordinate;
[self stopUpd];
[self reload];
} else {
NSLog(#"Lastupd");
lastloc.coordinate=newLocation.coordinate;
}
} else {
//enough tries, if not canceled choose lastloc.
if (ctr<40) {
NSLog(#"Last");
ctr=40;
homeloc.coordinate=lastloc.coordinate;
[self stopUpd];
[self reload];
}
}
}
//force stop updates. ctr prevents extra calls after stopUpdatingLocation.
//called after the timeout delay, if position found, cancel the timeout.
-(void)stopUpd {
[lm stopUpdatingLocation];
lm.delegate=nil;
if (ctr<15) {
ctr=40; //2 extra calls after stopupda... otherwise, now do nothing.
NSLog(#"Timeout");
homeloc.coordinate=lastloc.coordinate; //#need "copy"?
[self reload];
} else {
ctr=40; //2 extra calls after stopupda... otherwise, now do nothing.
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(stopUpd) object:nil];
}
}
// "couldn't get userlocation" handler. I also cancel like this on connectionDidFailWithError.
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"Location failed: %#",[error localizedDescription]);
ctr=0;
[self stopUpd];
}
Where ctr is there to prevent the 2 extra calls, lastloc is last known reliable position, homeloc is the phone position used for (threaded) loading of location-specific data with [self reload].
The constants 15 and 60 are safe values from real-world testing; gpstimeout is set to 30. If you work a lot in a simulator you may want to set it to something short like 3 seconds, as all you will get is a stale but relatively usable position and no more until the timeout.
Edit: If you can best my code or point out an omission, I'll mark that as answer, I hate marking my own as answer.
I've come across a similar problem in one of my previous apps.
There's no way to force a location update as far as I'm concerned - (I searched for a long time and didn't find anything)
One thing that I found was using the UIBackgroundModes key in my info.plist.
Support for some types of background execution must be declared in advance by the application that uses them. An application declares this support by including the UIBackgroundModes key in its Info.plist file. Its value is an array that contains one or more strings with the following values:
audio. The application plays audible content to the user while in the
background. (This includes streaming audio or video content using
AirPlay.)
location. The application keeps users informed of their
location, even while running in the background.
voip. The application
provides the ability for the user to make phone calls using an
Internet connection.
Which is what you're looking for. See the docs for more info.
What if you put more of the code within the age check, like:
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge<60) {
[manager stopUpdatingLocation]; //only stop if new reliable pos fetched.
...handle position
}
}

issues with geting location coordinate time to time

time to time there is an issue with geting the location coordinate on my app.
I have been testing my app for location coordinate from the simulator and the iphone sitting at my home (it is not just my home, i tested it in different location (outdoor) as well with a very good network connectivity), and i see this wired behavior, i have the right co ordinate at the moment and then i send the app to the background and bring it back i get the right location co ordinate, and if i do it 8-10 times (i.e sending it to background and bringing it to foreground) once in a while after the app comes from the background i cannot get location co ordinate, the only way to get the location co ordinate at this moment is to kill the app and then start fresh. So i am sure some thing is going wrong but I am not sure what is it.
This is what I am doing
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
//if the time interval returned from core location is more than 30 seconds we ignore it because it might be from an old session
if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 30) {
if(newLocation.coordinate.latitude != previousLocation.coordinate.latitude && newLocation.coordinate.longitude != previousLocation.coordinate.longitude){
if(newLocation.horizontalAccuracy <= 100){
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
else{
[self.locationManager startUpdatingLocation];
}
So basically i take the newLocation only if it is not older then 30 sec, and it is not same as previous location that i have stored locally and the horizontal accuracy is less than 100 meter. When i run it in debugger what i am observing is i get to the first if condition 3-4 times and if it fails it doesn't come after that, which means didUpdateToLocation doesn't get called at all.
Once the co ordinate meet all my criteria I do stopupdatinglocation and do startMonitoringSignificantLocationChanges.
The reason i am doing startUpdatingLocation in my else block is.
For example if didUpdateToLocation got called due to the startMonitoringSignificantLocationChanges, i want to get the accurate location after that so i am doing startUpdatingLocation every time i don't get the right location that i am looking for as i believe doing multiple startUpdatingLocation doesn't harm anything.
Let me know if there is something wrong in my thought process or in the code logic.
The first thing I would check is your info.plist. You have to include the key Required Background Modes -> App registers for location updates. Without this you won't receive location updates (besides significant location changes and region monitoring) while in the background.
In the main else, you call startUpdatingLocation. While I don't believe this hurts anything, unless you are balancing it with a stopUpdatingLocation I don't believe it will do anything. Documentation says:
Calling this method several times in succession does not automatically result in new events being generated. Calling stopUpdatingLocation in between, however, does cause a new initial event to be sent the next time you call this met
But either way, I don't think you need to tell it to startUpdating again, it will continue on it's own when the distanceFilter property is exceeded or the hardware gathers a more accurate location reading.
I don't see a closing } for your second if.

Stop Returning Cached CLLocationManager Location

I am wondering if there is some way to make it so CLLocationManager doesn't automatically returned a cached location. I understand that the documents say "The location service returns an initial location as quickly as possible, returning cached information when available" but this cached location could be extremely far away from the user's current location and I would like it more precise if possible.
Thanks for any help!
You can't stop it from caching, but it's not hard to filter out cached data. Core Location includes a timestamp with its locations. Compare the timestamp of the location with a timestamp saved when your app started, and you'll be able to tell which locations are old (cached, found before your app stated) and which are new. Throw away the old ones.
The location timestamp is an NSDate, so just get the value of [NSDate date] when your app starts up and use that as your reference point when filtering locations. You could even throw away the reference value once you start getting new data and treat a nil reference date as implying that new locations should be trusted.
-(void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation *)oldLocation
{
if ([newLocation.timestamp timeIntervalSinceNow] > -10.0) // The value is not older than 10 sec.
{
// do something
}
}
You can use this property of CLLocationManager:
#property(readonly, NS_NONATOMIC_IPHONEONLY) CLLocation *location;
The value of this property is nil if no location data has ever been retrieved, otherwise, this is where CoreLocation caches its data. Therefore, if you always want to start from scratch, simply check if this property is nil or not. If it's nil, then you are ok; otherwise, you need to stop your location manager and start it again: this will force an update, which will result in the cached value being overwritten by the fresh one you have just triggered.