Location.HorizontalAccuracy too bad when getting location from background - iphone

I'm writing an app that monitors the user's location. I have a CLLocationManager object that uses startMonitoringSignificantLocationChanges, so I can get locations updates from the background when the app is not running. I have setup my application didFinishLaunchingWithOptions so if I get a location key I fire up my manager to get the user's location. Everything works fine but the problem is that every time I get a location from the background, the Horizontal Accuracy of this location is very bad. In most of the cases it is 1414 m.
Does anybody know why the horizontal accuracy is so bad when the location comes from the background?
Is there anything I can do to get locations with better accuracy in the background?
When the app is running all the locations I get are very accurate, this only happens when the location comes from the background. Does that have anything to do with the number of cell towers I have in my city? I was thinking maybe the device doesn't use gps of wifi nodes to get locations in the background.
Anyways, Any help here is appreciated.
Please share your thoughts.
Thanks!

The accuracy of locations returned by CLLocationManager is determined by the desiredAccuracy, which is by default, kCLLocationAccuracyBest, and by the available accuracy of the device. For instance, you may get less accurate locations if the device's battery is low, or you may get more accurate locations if they are still cached from another app.
However, getting you incredibly accurate coordinates drains a significant amount of power from the battery and will drain the device. Applications in the background are probably limited to a much lower resolution of accuracy to improve battery performance.
Accurate locations require a lot of power to use the GPS radio while less accurate locations can rely on nearby wifi hotspots and the cell towers within range of the phone.
As your application resumes from the background, the system will try to improve the accuracy of the results you get. It's a tricky concept, but take a look at the Maps application on your phone. At first, the circle representing your location is very large; as the system gets a more accurate sense of your location, the circle becomes smaller. This visualization represents the phone using more power to get a more precise location.
You'll see a similar phenomenon with CLLocationManager as your app resumes from the background: you'll get an inaccurate location and receive subsequent, more accurate updates.
It's a compromise between convenience and battery life that Apple had to make when designing their APIs. The first update to a user's location probably won't be that accurate unless they were just using the Maps application and the location is cached.
The best advice I can give you is to listen for subsequent updates from the location manager and update your UI accordingly. Good luck!

As the name states: The startMonitoringSignificantLocationChanges notification is only there to let you know that a the the user's location significantly different from the last one checked. It's your job when you receive that Notification to update your location according to the desired accuracy you want.The notification won't do that for you. It is only going to let you know the location has change so you can deal with the situation accordingly. If you don't know how to get a better accuracy you may want to check out the Apple sample code for LocateMe.Here's a snippet that stores the accuracy(bestEffortAtLocation) then tests the accuracy each time the delegate is called until a better result comes in or a time-out occurs.:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// store all of the measurements, just so we can see what kind of data we might receive
[locationMeasurements addObject:newLocation];
// test the age of the location measurement to determine if the measurement is cached
// in most cases you will not want to rely on cached measurements
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 5.0) return;
// test that the horizontal accuracy does not indicate an invalid measurement
if (newLocation.horizontalAccuracy < 0) return;
// test the measurement to see if it is more accurate than the previous measurement
if (bestEffortAtLocation == nil || bestEffortAtLocation.horizontalAccuracy > newLocation.horizontalAccuracy) {
// store the location as the "best effort"
self.bestEffortAtLocation = newLocation;
// test the measurement to see if it meets the desired accuracy
//
// IMPORTANT!!! kCLLocationAccuracyBest should not be used for comparison with location coordinate or altitidue
// accuracy because it is a negative value. Instead, compare against some predetermined "real" measure of
// acceptable accuracy, or depend on the timeout to stop updating. This sample depends on the timeout.
//
if (newLocation.horizontalAccuracy <= locationManager.desiredAccuracy) {
// we have a measurement that meets our requirements, so we can stop updating the location
//
// IMPORTANT!!! Minimize power usage by stopping the location manager as soon as possible.
//
[self stopUpdatingLocation:NSLocalizedString(#"Acquired Location", #"Acquired Location")];
// we can also cancel our previous performSelector:withObject:afterDelay: - it's no longer necessary
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(stopUpdatingLocation:) object:nil];
}
}
// update the display with the new location data
}
The credit goes to Apple because this is a snippet straight from their sample code LocateMe:
http://developer.apple.com/library/ios/#samplecode/LocateMe/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007801-Intro-DontLinkElementID_2
So when you get the notification and need to get a better result you'll need to update the accuracy and see if that gives you a better result.

Related

How to detect the accuracy margin of error using Core Location

I have an app that tracks user location using the following:
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
According to Apple's documentation, Core Location will try to obtain the best possible reading until I tell it to stop. However, I realize the reading can be impaired by many uncontrollable things i.e. weather, device in a building, etc.
For the purposes of my app, I would like to store how accurate the reading actually was. For example, if I am in a field, I may get a reading that is accurate up to 10 meters, but if I were in that same field during a thunderstorm, I may get a reading that is accurate up to 100 meters.
Is there a way to detect how accurate my reading actually is?
CLLocation (the object that gets returned from the CLLocationManager) contains two properties, horizontalAccuracy and verticalAccuracy.
This is a value measured in metres that tells you how many metres radius around the given location you could be.
You can read more about CLLocation here... CLLocation Class Reference
I believe this is what Apple uses in its maps app to place the circle around the location to represent the area you could possibly be in.

(iOS) Multiple notifications via locationManager:didExitRegion: when exiting a region

I'm working on a location-based app that makes use of the CLLocationManager region monitoring.
I'm using a single CLLocationManager and single delegate (which are set up in the main app delegate at startup), and I'm noticing that I often get a burst of multiple calls to my delegate (on locationManager:didExitRegion:) when exiting a monitored region -- usually two calls, but sometimes more. Has anyone else experienced this, or have any ideas what can be going wrong?
I'm instantiating the CLLocationManager as follows, in a class that is instantiated in the app delegate:
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
_locationManager.delegate = self;
I'm setting up region monitoring like this:
// The region instance has a radius of 300 meters
[_locationManager startMonitoringForRegion:region desiredAccuracy:1000];
As I understand from the documentation, providing the desired accuracy of 1000 means that locationManager:didExitRegion: should only be called once we're 1000 meters outside of the region.
On additional point -- as far as I've seen, I only get a burst multiple notifications if I'm in the car (and therefore travelling quite quickly). It doesn't seem to happen if I'm on a bike or on foot. Any pointers as to what I'm doing wrong (or if this is an issue that others have already encountered) are appreciated.
tl;dr: It's really quite simple - you're getting as much information as Apple can give you about the fact that you're crossing cell tower boundaries - meaning, not really good data at all.
Now the real story:
Generally, there are only a couple of ways that CoreLocation can determine one's position:
By GPS. This is highly accurate (tens of meters), but takes lots of power to use.
By WiFi. This is generally accurate, though wifi base stations can and do change, which makes it fuzzy. This method cross-references the wifi stations in the area vs some known accurate locations - so it knows when it sees "FooWifiStation" that it's associated with a particular area, measured by precise instruments, or perhaps even other phones with GPS turned on (which is controversial, we may never know if Apple uses this method)
By cell tower locations. These don't move, so it knows you're within a big fuzzy dot of coverage when you're associated with a tower. This is the least accurate, but least power-consuming, because your phone is already doing the work to stay in contact.
You can even see this if you go into the maps application "cold": you start out immediately with a big fuzzy blue dot, (at least 1km where I am), then it shrinks as it gets a wifi location fix, then it more or less disappears when GPS gets its fix. It's doing [cell tower] => [wifi] => [gps] accuracies in real time.
From my experience, the "significant location change" clause means "We'll let you know whenever you move in a big way, as long as we don't have to do any more work or expend any more power to get you that data. This means de facto that the very best you can rely on is using transitions between cell towers. Apple deliberately left this vague, because if another app uses something that has better resolution - say, you open Maps.app, and it gets a GPS fix - it is possible that you will suddenly get a great fix on location, but you can't depend on that always being the case. You've asked for a "weak reference" to location.
Now think about what happens when you are wandering about in your car: your cell phone has to manage that transition. It gets handed off, talks to multiple towers at once, that sort of thing, to manage a seamless transition which must be viable while you are having a conversation. Just that is a pretty neat feat for any little box to do. Necessarily, this will cause you to see a bit of bounce in location updates, because, to the phone, you're vibrating between cell towers at this time as it negotiates the transition.
So what the radius really means is that you're interested in data of roughly that accuracy - but the API will not guarantee that you get updates within that radius. You can think of it as Apple saying, "We're going to bucket your accuracy into one of 3 groups - but we didn't give you an enumeration, because we want to reserve the right to develop better methods of fixing your location without you having to change your code". Of course, in reality, any real app will change if they change their method of getting location, because they are annoyingly vague about this location stuff.
In other words, you will get a location fix at the cell tower with some totally made up guess as to how good that accuracy is; when you move to the next tower, you will instantly jump to its location, with a similarly fuzzy fix - does that make sense? CoreLocation is telling you you're at the cell tower with accuracy of however far the cell tower's signal reaches; as you move to another tower, you will probably get the bounciness of the handoff.
So when it comes to it, to really do a good job, you have to assume that the data is one of "great", "ok", or "bad" and use other methods - like Kalmann filters - if you really need to have a better guess at where the user is. As a zeroth-order approximation, I would just debounce the callbacks based on the time of the update, which is given, and assume that the user isn't really leaping kilometers back and forth within a few seconds, but rather, travelling in the direction of the first new update.
I think you'd be better off, using a desiredAccuracy that is smaller than the radius of 300m, i.e. kCLLocationAccuracyHundredMeters. Have you tried that?
There is no documentation about the underlying logic, but I'd assume, that desiredAccuracy can be regarded as the minimium distance you'd have to travel that the movement counts as a "border crossing".
Region monitoring is based on "significant location events", not GPS – otherwise the battery wouldn't last half a day.
If you use such a high desiredAccuracy, the system might get more than one significant location event (these seem to be generated about every 500m – also depending how many wifi networks you have in the area.
In that case, the system might compare the current location resulting from the significant change with the distance to all sides of your region.
If you're just outside 1000m from opposite side of your region, it might notify again and only stop notifying once your outside the 1000m from each side of your region.
The reason for the accuracy parameter is rather to avoid border crossings, if you are too close to the border, so you're not seeing inside-outside-inside-outside etc while traveling just outside the border...
In my apps, I tried many many different combinations of radius and accuracy and settled on 500m/100m and 100m/10m – those work very well in my real work within-city szenario.
(see also the excellent article series http://longweekendmobile.com/2010/07/22/iphone-background-gps-accurate-to-500-meters-not-enough-for-foot-traffic/)
I'll bet this is your issue, locationManager:didExitRegion: gets called for EVERY region that every location manager in every application on your iPhone has registered. You need to compare the region's identifier string sent as a parameter with the region's identifier string of the region you want your app to currently do something with.
When you send [_locationManager startMonitoringForRegion:region desiredAccuracy:1000]; you're creating a region, if it doesn't already exists to be monitored for. This is NOT the same as adding an observer to NSNotification Center. Apple's region object blindly sends the notification to EVERY CLLocationManager which then sends the message to the delegate.
First, the documentation does not indicate that desiredAccuracy alone determines how far out of a region you need to be, before didExitRegion event is generated. If you want to fine-tune how often events are fired, you need to use the distanceFilter as well, which determines the horizontal movement you need to have moved before an event will be fired.
If use of the distanceFilter does not work, then I would recommend the following:
If you are using NSNotificationCenter, then ensure that you have removed other classes from notification, via [[NSNotificationCenter defaultCenter] removeObserver:self]. You can optionally specify a name and object to this call.
1000 Km is a large radius, which has a high chance of intersecting with many regions in the vicinity. I would try a lower number for this parameter to see if that doesn't decrease the number of exit notifications that you are receiving. The only thing that indicates that this may not be the solution is that you did not say you are receiving blasts of didEnterRegion as well.
Lastly, I would check the identifier of the CLRegion being passed in the didExitRegionEvent, to see if you can't set up an NSDictionary of regions yourself. You'll have to set a region to the dictionary on didEnterRegion, and remove it on didExitRegion. So, on didExitRegion, all you have to do is ensure that you are interested in the region by checking that you have the region already. I would guess that CLRegion is already equipped with isEqual: and hash to allow it to be stored in a collection.
Good luck!
I found that in order to save battery you have to use monitorForSignificantLocationChange.
My solution is to filter out multiple alerts coming within 60 seconds for the same region:
-(BOOL)checkForMultipleNotifications:(GeoFenceModel*)fence
{
GeoFenceModel *tempFence = [fenceAlertsTimeStamps objectForKey:fence.geoFenceId];
if(tempFence == nil){
fence.lastAlertDate = [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]];
[fenceAlertsTimeStamps setObject:fence forKey:fence.geoFenceId];
NSLog(#"checkForMultipleNotifications : no Notifications found for Region : %#, Adding to Dictionary with timestamp %.1f",fence.geoFenceId,fence.lastAlertDate.doubleValue);
}
else if(([[NSDate date] timeIntervalSince1970] - fence.lastAlertDate.doubleValue) <= 60.0){
NSLog(#"checkForMultipleNotifications : Multiple region break notifications within 60 seconds, skipping this alert");
return NO;
}
return YES;
}

difference between desiredAccuracy and distanceFilter

Sorry for being a noob here. I am not able to clearly differentiate between CLLocationManager's properties distanceFilter and desiredAccuracy.
If I want my application to give different coordinates for even small distances (say 100-200 metres) what values should i set for these properties.
Help would be greatly appreciated.
According to developer.apple.com
distanceFilter
The minimum distance (measured in meters) a device must move laterally
before an update event is generated.
That means, based on previous location event, another location update will only be received after exceeding distanceFilter value distance.
desiredAccuracy refers to how accurate your location data should be.
For example if you wish to see the exact street you're on you a high accuracy value for this parameter. (kCLLocationAccuracyBest)
If you only wish to see the approximate area (such as in which neighbourhood you're in) you'd set a lower accuracy value for this param. (kCLLocationAccuracyThreeKilometers)
Choose this to suit your needs, however be aware that the more precise you wish to be and the more often you request updates, the more power it will drain from your device.
Hope this helps,
Vlad
distanceFilter - this is minimal distance which device should pass from previous location which was passed to delegate with ...didUpdateToLocation:... method. And as soon as distance reached location service will invoke ...didUpdateToLocation... again and so on.
desiredAccuracy - tells to location service how accurate coordinate you want and this is minimal location error radius. If value is very low (ex. 5) radio will try to use GPS hardware and will keep powering it up hardly to make it give most accurate location. If value is large,than system may decide to use data which was retrieved from WiFi hotspots location triangulation.

altitude property in location manager returning zero on Iphone when reading GPS Location

I am writing an app, that uses GPS. I can get successfully the latitude, longitude and other properties, but altitude seems to always return "0.00" i have the following code to test it in the most simplest way and still get 0.00. Code below:
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
// Stop updating location if renewed in less than 60 seconds
if ([self timeBetweenLocationandNow] < 60)
{
[locationManager stopUpdatingLocation];
NSLog(#"GPS Stopped");
}
NSLog(#"Altitude:%.2f m",newLocation.altitude);
}
Any ideas on what could be wrong ? also on an init method i have the following:
- (id)init
{
self = [super init];
if (self != nil)
{
// Create location manager Object
locationManager = [[CLLocationManager alloc] init];
// Set the delegate to this object
[locationManager setDelegate:self];
// Set distance filter and accuracy
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager startUpdatingLocation];
}
return self;
}
Will appreciate any insight. thank you
I'm no expert but this is how I understand it: The iPhone is quite different from other gps systems because of the multiple ways in which it goes about figuring out your location. The iPhone figures out the location in three ways: local wifi signals, cell towers, and/or gps chip. Using a gps satellite to find a position can be extremely slow and reception can be pretty much non-existent in populated cities. Therefore, iPhone generally looks to the first 2 options first.
Wifi positioning data comes from people driving around on the streets logging hotspots which obviously isn't going to give much of a hint of an altitude. I'm not sure if cell tower triangulation gives any altitude data but my guess is no. These two methods are extremely fast compared to gps but will yield an altitude of 0 as you have seen. If you are in a place where there is a decent gps satellite reception the iPhone may give you the altitude, but even then, it might not even find it till it has received the satellite signal a few times and called the location delegate multiple times, and even then altitude data for gps can be notoriously inaccurate.
Long story short, it's a bad idea to count on accurate altitude data from the iPhone.
i found the problem. As Deepmist suggested the delegate has to be called several times. I was stopping the GPS after the first fix. Which was giving me accurate data for coordinates but altitude remained # 0.00 until like 3 or so delegate calls. Now i get the altitude data, what is a good way to check for this ? so i can stop the location manager after i get the data ? i can check that altitude is a non-zero value, but was wondering if there are other properties that should be used instead to make sure that you have the highest accuracy possible making all of your location data valid (i.e non-zero on some properties). thank you

Why is my CLLocation speed so inaccurate?

I´m playing a bit with the iPhone SDK and I want to show the current speed in my application. There are so many apps that can do that really precise, especially for low speeds like running or biking. The best I've seen is RunKeeper.
But, in my application, the speed is absolutely inaccurate. In low speeds it is always null, and only at higher speeds it shows some values but they are rarely updated and not really useful.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
if (newLocation.timestamp > oldLocation.timestamp &&
newLocation.verticalAccuracy > 0.0f && // GPS is active
newLocation.horizontalAccuracy < 500.0f && // GPS is active
//newLocation.horizontalAccuracy < kCLLocationAccuracyHundredMeters && // good quality GPS signal
//newLocation.speed > 1.0f && // enough movment for accurate speed and course measurement
oldLocation != nil) // oldLocation is nil on the first reading
{
double speed = (newLocation.speed * 3.6);
[self updateDisplayWithSpeed:speed];
//double direction = newLocation.course;
}
}
Does anyone have working code for that? Or can you tell me what´s wrong with mine?
I would try the following.
Remember that you should set the distanceFilter and desiredAccuracy as needed by your scenario: walking is not the same as traveling by car etc. Moreover, when you request high accuracy from the GPS, you must always discard the very first location provided by the GPS and use the second one as the starting location. Quoting the Apple documentation:
You should assign a value to this property that is appropriate for your usage scenario. In other words, if you need only the current location within a few kilometers, you should not specify kCLLocationAccuracyBest for the accuracy. Determining a location with greater accuracy requires more time and more power.
When requesting high accuracy location data, the initial event delivered by the location service may not have the accuracy you requested. The location service delivers the initial event as quickly as possible. It then continues to determine the location with the accuracy you requested and delivers additional events, as necessary, when that data is available.
Here is my suggestion.
First, update the location using the GPS using an interval of at least one second. Below this interval the speed value is useless because it is measured in meters per second and owing to the previous discussion, you are not likely to get a valid update in less than a second with high accuracy.
Second, only use meaningful values for the location coordinates: you should discard values with negative horizontalAccuracy. This is what I am referring to when speaking of good locations.
Third, you can compute the distance by yourself: compute the distance between the last good location and the previous good location using getDistanceFrom() and then divide by the number of seconds elapsed between the location update. This will give you the distance in meters per second. I will try to do this and compare the result with the Apple provided speed.
Please try following in your code :
speedLabel.text = [NSString stringWithFormat:#"SPEED(Km/Hr): %f", [location speed]*3.6];
latitudeLabel.text = [NSString stringWithFormat:#"LATITUDE: %f", location.coordinate.latitude];
longitudeLabel.text = [NSString stringWithFormat:#"LONGITUDE: %f", location.coordinate.longitude];
CLLocationDistance meters = [location distanceFromLocation:orginalLoc];
distanceLabel.text = [NSString stringWithFormat:#"DISTANCE(M): %f", meters];