Detecting tunnels with Core Location - ios

I am using Core Location for turn by turn based navigation and would like to show a "GPS lost" alert in tunnels.
The problem is that the following two scenarios look the same to the app:
The user drives into a tunnel. GPS updates cease because there is no way to know where the user is.
The user stops at an intersection. GPS updates cease because the user is no longer moving.
I need to set these two situations apart. Ideas?
I have tried looking at the horizontalAccuracy property, but sometimes the updates cease completely, so there is no new horizontalAccuracy information.

Normally with CLLocationManager set for best accuracy for navigation and no distance filter, you should get a location update once a second even if you are stopped at an intersection.
If you stop getting those updates while the motion coprocessor (using CMMotionActivityManager) says you are still driving then you can infer that you are in a tunnel (or underground car park or someplace with a bad GPS signal).
BTW, GPS updates should not stop when you are stopped at an intersection if you have set distanceFilter = 0 and desiredAccuracy = kCLLocationAccuracyBestForNavigation and activityType = CLActivityTypeAutomotiveNavigation, etc.
Another thing to watch out for, if the tunnel has cellular coverage, you may still get location update from cellular triangulation but with worse accuracy. If the CLLocation.horizontalAccuracy goes from less than 50m to over 300m then you have lost GPS/GLONASS coverage even though you are still getting location updates.

Look at locationManager:didFailWithError: method:
If the location service is unable to retrieve a location right away,
it reports a kCLErrorLocationUnknown error and keeps trying.
To determine a second situation (user stops) use locationManagerDidPauseLocationUpdates: method:
When the location manager detects that the device’s location is not
changing, it can pause the delivery of updates in order to shut down
the appropriate hardware and save power. When it does this, it calls
this method to let your app know that this has happened.

Related

how to get the location with the CLLocationManager every 10 secondes?

I want to get the different locations of the users in order to display him the trips he did. But in order to save my user's battery, I want to get his location just every 10 seconds with my CLLocation manager.
I first thought about not implementing the 10 seconds interval and get the user's location every time he move with the didUpdateLocations of the CLLocationManager, but when I simulate a drive I get new location every second and I think this is really bad for the battery, am I right ?
Do not try to second guess the location manager. Your job is to set its properties appropriately, such as distanceFilter, desiredAccuracy and activityType. Apple will use every trick in the book to keep battery usage reasonable given your settings. As the docs tell you:
Core Location manages power aggressively by turning off hardware when it is not needed. For example, setting the desired accuracy for location events to one kilometer gives the location manager the flexibility to turn off GPS hardware and rely solely on the WiFi or cell radios, which can lead to significant power savings.
If the goal is track location in the background, there are special modes for that, which save even more.
Check location every 10 second a lot frequent, it will be drain user's battery too fast.
If you would save battery, you should learn apple guide about location manager.
You need use distanceFilter and desiredAccuracy
Base guide CLLocationManager
Energy Efficiency Guide for iOS Apps
Location Awareness Programming Guide - Tips for Conserving Battery Power
Update
Also you can check how fast user moving CLLocation have speed and adjust activityType

What strategy to adopt to monitor the user location in background?

I want to track the user location in background, in the purpose to show him an alert when he is close to one of his friend.
So i start with CLLocationManager. As far as i know their is only one reliable way to let the app know about the location update even if the user reboot the Iphone or kill the app: startMonitoringSignificantLocationChanges. But the problem is that even inside a city with many wifi, startMonitoringSignificantLocationChanges fire the DidUpdateLocations when the user move around 1km and that is really too much for my need
on the other way startUpdatingLocation is firing DidUpdateLocations at good interval (even too much because even when the user do not move it's fire quite often DidUpdateLocations). But startUpdatingLocation not survive to iphone reboot or app being killed by the user. Also I suspect that even with an accuracy of 100m, startUpdatingLocation use lot of battery consumption.
So the question: What strategy i can use in my app to track efficiently without draining too much the battery the user location at full time? I need an accuracy of around 100m and if possible an interval between 2.5 - 5 min for each track (i didn't find any option to specify a delay to wait before to catch a new location)
Actually i think to do something like this :
2 locationManager, 1 GPS and 1 Significant Changes
when app start I do with significantChangesLocationManager: startMonitoringSignificantLocationChanges and startMonitoringVisits
I also call GPSLocationManager startUpdatingLocation to retrieve the accurate user position. I set up PausesLocationUpdatesAutomatically(true) so that the GPSLocationManager will stop by himself soon or late
on DidUpdateLocations raise by the GPSLocationManager I start monitoring region enter/exit (100m radius around the obtained latitude/longiture) with significantChangesLocationManager
What do you think of such strategy ?
Even though you will receive more triggers than you need, as you already said, you can use startMonitoringSignificantLocationChanges. It is implemented in a very energy efficient way. It allows the app to be terminated and only be woken up again when iOS thinks the device has moved significantly. Another advantage would be that your app doesn't need the location background mode, which could raise questions during an app review.
The startUpdatingLocation let's the app continuously update the location of the device, even though you only receive a couple didUpdateLocations: events. Also, iOS cannot shutdown the app while updating is active, so it consumes a lot of battery.
You can also consider geofencing, with an exit geofence around the current location. However, significant location updates will be more reliable. Exit geofences won't trigger anymore once you're already out of a geofence, which could happen when the phone is turned off inside a geofence and turned back on outside. This solution has the same advantage of not needing a background mode.
As far as I understand your use case, startMonitoringSignificantLocationChanges sounds as the best option. You won't have control over the exact time interval and distance it triggers, but it is very energy efficient and easy to use.
As mentioned in comments, here are some approach I had to do in order to get the expected result in our app.
Significant Location Updates - is good to wake your app up once it gets finished by some reason that you can't control, so even if you lose some points, it will get back in some seconds/minutes.
This is not good if you need a good accuracy, as you cannot control the distance / time or whatever, iOS will update locations when mobile antenna is changed, wifi connection, turn air plane mode off and on but will not give a sequence of gps points.
Start Location Updates - Best accuracy but battery drainer. So you can't just turn it on let it go. You must implement some controls over it.
Background tasks - The only way I've found to keep my app alive.
The way you can combine is:
Have 2 location managers isolated, one for significant location change and one for start updating location;
Start your app with both turned on;
Inside your didUpdateLocations you can create your logic to start and stop your background tasks;
Create methods to start and pause your location manager and create your timers to control that;
inside your bg task you will start or pause your update locations, but never stop it, just if for example, your user logs out and you want to stop location;
keep significant location update location manager alive forever, if for some reason iOS decide to kill your app when in bg, it will ensure that in a given moment your app will come back to life;
For battery live, try do not use kCLLocationAccuracyBestForNavigation for accuracy as when it starts it eat a LOT of resources, most of time, kCLLocationAccuracyBest is more then enough and if you can use kCLLocationAccuracyNearestTenMeters;
Your didUpdateLocations will give you mostly a bunch of points, try to get only those with good horizontalAccuracy, around 20 meters for us was good enough, so let it work and once you have a point with good accuracy, you can pause it again.
Below you have some links that helped me a lot to implement our solution, none of those have a "as is" solution, as I said, I had to mix all of them and test a lot to understand its behaviour and make the necessary adjustments.
https://github.com/voyage11/Location
http://mobileoop.com/background-location-update-programming-for-ios-7
http://zaachi.com/2013/09/30/ios-locationmanager-location-update-in-my-own-interval-with-application-in-the-background.html
In addition to Mark's answer I would like you to explore pausesLocationUpdatesAutomatically which can help you save battery from draining when user has stopped Apple Documentation
Allowing the location manager to pause updates can improve battery life on the target device without sacrificing location data. When this property is set to true, the location manager pauses updates (and powers down the appropriate hardware) at times when the location data is unlikely to change. For example, if the user stops for food while using a navigation app, the location manager might pause updates for a period of time. You can help the determination of when to pause location updates by assigning a value to the activityType property.

How to get location by GPS without network in iOS?

I use CoreLocation Framework to get location, when in network it work normal , but when without the network, it can not locate always. it will not stop receiving location information after seconds. I want to know how to use GPS locating without network?
I think you are using your device in a house or building, where the device can't receive GPS satellite signal.
If that's the case, what you are seeing is the expected results. iOS internally keeps the last location (cache). When you start location updates, iOS passes that cashed location quickly to locationManager(:didUpdateLocations). When no network nor satellite signal, no location update will be sent thereafter.
If your device is receiving wifi w/o satellite signal, iOS determines the rough location by using the wifi, and sends location updates. You can't distinguish the source of location updates; they could be from wifi or from GPS satellite. (actually, you can guess by seeing the accuracy. when the source was wifi, accuracy was fixed to 50m w/ my iphone.)
One way to check if your location update is cached or not is to check the time stamp of location update.
The cashed location update should have older time stamp than when you started location update.
So, to your question, you are doing just right thing. Location manager is very easy to use. Try to receive satellite signal in open sky w/o network. For your tests, don't move. It may take 5-7 minutes at most. If you move, it may take longer. Hope this helps.

In ios, is it possible to know if the GPS location is not being updated?

I'm creating an app that allows the user to navigate with a map when offline (no internet connection or wifi available), and I want to let the user know if the gps location not being updated. I know I can get the last updated location timestamp and the accuracy, but is it possible to know if the gps has no reception?
No, there's no public interface for finding out about the state of specific location hardware such as the GPS receiver.
The Location Manager abstracts all that away, so that developers can get location and accuracy information without worrying about whether the device used GPS, Wifi, iBeacons, cell tower locations, Loran-C, celestial navigation, etc. to determine the location. This is generally a very good thing because it means that your apps work on all devices regardless of whether they have GPS, and will continue to work (and maybe even work better) on new devices that might use other technology. But it also means that you don't get to ask the question "is the device receiving a GPS signal right now?"
Is it possible to know if the GPS location is not being updated?
No. It doesn't seem possible given the available API.
There are a few things about GPS to consider, if you haven't already. It is technically possible to receive a location update from CLLocationManager when you're "offline" but it will depend on several things:
If your device actually has GPS hardware. Some don't.
If you have line of sight to GPS satellites. There's a difference between GPS and A-GPS
Those true GPS updates will come much less frequently because of the way GPS works, but it should work.
... but is it possible to know if the gps has no reception
Based on the way GPS works (as best I understand it) you either turn the radio on/off (-startUpdatingLocation/-stopUpdatingLocation) with the Location Services API and you either get locations more frequently with A-GPS or infrequently with pure GPS when you don't have a network signal. I don't think the Location Services API has a way of telling you "I don't have a GPS signal at all."

Determining iOS Location Services' mode of update

I'm building a little prototype app to test Location Services. My app uses both -startUpdatingLocation and -startMonitoringSignificantLocationChanges (for background processing).
In both cases, I'm trying to send a json object to a server with the location data I get, but I'd like to be able to also send some sort of identifier so I can determine which mode of location service was used to acquire the data (GPS, wifi, tower or at least if it was through one of the two services above.
I'm setting up my CLLocationManager in the AppDelegate if that makes any difference.
Thanks
The sources that CLLocationManager uses are not provided. However, you can determine whether GPS is allowed to turn off by setting the desiredAccuracy property. As the documentation notes, an accuracy of 1 kilometer may have the GPS turn off.
For example, setting the desired accuracy for location events to one kilometer gives the location manager the flexibility to turn off GPS hardware and rely solely on the WiFi or cell radios. Turning off GPS hardware can lead to significant power savings.
You don't exactly control the GPS because other apps may be requesting a greater accuracy in the background.
You can roughly guess what was used for determining your location by examining your CLLocation object. Each CLLocation object has a property called horizontalAccuracy. This value is measured in meters. If it is less than 1 kilometer then GPS was probably used.

Resources