how to get the most accurate user speed on iOS - ios

I'm new to iOS developing. Can please anyone tell me the most accurate way to get the user speed in iOS. CLLocation speed doesn't seem to be correct.
Here is my code to get the speed
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var speed: CLLocationSpeed = CLLocationSpeed()
speed = (locationManager.location?.speed)!
print("speed - ", speed * 3.6)

Moving speed calculated using 'CLLocation' won't be accurate since you need the location to be updated continuously which would take a lot of resources. Hence, CLLocation updates the location based on intervals.
If, for whatever reason, you want the speed to be updated continuously, you need to look into Accelerometer and Gyroscope. By using the values from both and with some data manipulation, you can get a more reasonable value for the current speed.
NOTE
Keep in mind that doing so will take a toll on your device's resources and is not the best approach.

Related

CoreLocation capturing speed

I am capturing location data using CLLocationManager. I am less concerned about horizontal accuracy, but I want the speed to be as accurate as possible.
Anyone know if kCLLocationAccuracyThreeKilometers will be sufficient for my use case or do I have to use kCLLocationAccuracyBest to get the most accurate speed?
Thanks.
AFAIK kCLLocationAccuracyThreeKilometers‍ works using cell towers (and doesn't use GPS), which doesn't give you every second's information (that's why it's good for battery), nor it uses core Motion. Without GPS or coreMotion any speed calculated won't be considered accurate
So to answer your question, no it won't give you an accurate speed, but just to be on the safe side, just jump into a car and start logging. (Though remember to turn location services off for all other apps, because sometimes other apps with better accuracy could increase your app's accuracy)
I stuck in same issue in one of my Map Based application and i solved this way ( while traveling through Delhi Metro even i tested) and its working fine :
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//Get Current Location
let location = locations.last! as CLLocation
let userLocation:CLLocation = locations[0] as CLLocation
// For getting speed
let speed = location.speed * 2.23694
lblSpeed.text = (location.speed<0) ? "-- \n mph" : String(format: "%d \n mph", Int(speed))
locationManager.startUpdatingHeading()
}
Accuracy is perfect in kCLLocationAccuracyBest
locationManager.desiredAccuracy = kCLLocationAccuracyBest
Feel free to provide your feedback after using this code . Hope it solve your issue . Keep Coding
TLDR; kCLLocationAccuracyBest is the way to go.
For details, see https://stackoverflow.com/a/737968/190599

kCLLocationAccuracyBestForNavigation vs kCLLocationAccuracyBest

I am building a speedometer app that shows the current speed a user is traveling. I would like to display the most accurate speed possible and therefore I am trying to figure out if I should use kCLLocationAccuracyBestForNavigation or kCLLocationAccuracyBest.
According to Apple's documentation, kCLLocationAccuracyBestForNavigation uses the highest possible accuracy and combine it with additional sensor data while kCLLocationAccuracyBest uses the highest level of accuracy but has no mention of sensor data. Does anyone know what this additional sensor data is and if it is used for calculating a more accurate speed?
I'm getting the speed from a CLLocation object.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations {
let speed = location.speed
...
}
}
kCLLocationAccuracyBestForNavigation uses accelerometer, gyro, wifi and cell towers.
As you are aiming to get best precision of speed I suggest you to use this constant.
Remember that this is also the most battery consuming.
Your callback seems correct to what you're expecting.
I have a suspicion!
My GPS tracker is using kCLLocationAccuracyBestForNavigation (with CLActivityTypeOther). To me it seems like kCLLocationAccuracyBestForNavigation would cause the location manager to also look up map data. When I ride my bike, the location manager sometimes reports locations that match the road beside me. But I am a not riding on that road...
Next I'll try kCLLocationAccuracyBest and see, whether or not this constant stops that rubbish.

How to get accurate speed from accelerometer on iOS?

I would need very accurate speed for my app, even when the user is only walking. I tried using the GPS, it works well but not for speeds that low. So my question is, could I use the accelerometer to get some more accurate values? If yes, how would I do that?
I do not think you will get something from accelerometer. Accelerometer only returns you motion (x,y,z). I do not know how you could able to get speed out of that, unless you will do some linear modelling algorithm that requires heavy research, that could be a Ph.D topic I guess.
I propose the following implementation.
- (id) init
{
self = [super init];
if (self != nil) {
self.manager = [[CLLocationManager alloc] init];
self.manager.delegate = self;
[self.manager startUpdatingLocation];
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"Speed = %f", newLocation.speed);
}
or you could use directly CMPedometer class which I believe that class developed exactly for physical activity applications. I have recently used that class and I am quite satisfied.
import CoreMotion
let lengthFormatter = NSLengthFormatter()
let pedometer = CMPedometer()
pedometer.startPedometerUpdatesFromDate(NSDate(), withHandler: { data, error in
if !error {
println("Steps Taken: \(data.numberOfSteps)")
var distance = data.distance.doubleValue
println("Distance: \(lengthFormatter.stringFromMeters(distance))")
var time = data.endDate.timeIntervalSinceDate(data.startDate)
var speed = distance / time
println("Speed: \(lengthFormatter.stringFromMeters(speed)) / s")
}
})
could I use the accelerometer to get some more accurate values
No, the accelerometer cannot tell you anything about speed. Acceleration and speed are very different things. Indeed, during motion with a constant speed, there is no acceleration (except gravity and similar side effects). You could travel at the speed of light and experience no acceleration (if you could travel at the speed of light).
It really isn't possible to get a decent speed estimate from the Accelerometer. That even if you start with the device at rest and end with it at rest and alsoo using the gyroscope.
Averaging the GPS location is probably the best, possibly a moving average of some sort.
I already have answerded similar question many times.
The apporach with the accelerometer will not work well for more than some seconds. The cause is the dramatic accuracy loss due double integration.
This is well explained in a Goole Talk "Sensor Fusioning" video.
What you can do, for low GPS speed:
A bit simplified:
1. Take locations evry 5 seconds,
2. calculate distance between fix(i-5) and fix(i),
3. calcuate time difference,
and cacluate then the speed in m/s by distanceMeters / timeDeltaSeconds
An advanced solution would use a time window of let's say 5 seconds,
and this evaluation window will move 1 step further every second,
using the same 3 calculation steps as above

CoreLocation find distance travelled and use as variable iOS

I am making an alarm that shuts off only after the phone has been moved a certain distance(to force the user out of bed). I need to be able to find the distance travelled after the alarm has gone off, then take this distance and use it in a method to shut the alarm sound off if the minimum distance has been travelled. Any ideas?
I'm using the following to update the location. Any thoughts on how to incorporate this into disabling the alarm?
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if(!newLocation) {
NSLog(#"No movement");
}
if ((oldLocation.coordinate.latitude != newLocation.coordinate.latitude) &&
(oldLocation.coordinate.longitude != newLocation.coordinate.longitude))
{
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:oldLocation.coordinate.latitude longitude:oldLocation.coordinate.longitude];
CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
CLLocationDistance distance = [loc2 distanceFromLocation:loc1];
if (distance>=4) {
NSLog(#"Very good!");
}
}
NSLog(#"Location:%#", newLocation);
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"Could not find location: %#", error);
}
A couple of thoughts:
You're not considering horizontalAccuracy. First if this is negative for either coordinate, then you just can't do any comparison. And even if they're both non-negative, you probably want to back out the horizontal accuracies if you really want to determine if the user moved a certain distances.
Look at not only the latitude and longitude, but the horizontalAccuracy, too.
You're not considering that when you turn on location services, there are frequently a whole series of coordinates that come in, and the coordinates may bounce around as the horizontal accuracy gets better and better. You want to make sure you don't consider this slow triangulation as the movement necessary to qualify as having gotten out of bed.
Make sure you do your experimentation on a real device, in real world scenarios. Using the simulator is definitely not a good test. Nor is using a device plugged into your computer (because you you want to see your GPS in real world scenario, where someone has moved around before going to bed).
I'd suggest starting with a simple app that logs location events in a database (or other format) and perhaps show the log in a tableview, so you don't always have to go back to your computer to review the results. Fire up that app, walk around, and then look at the location events you get. That will help you get your arms around the patterns you see as people use their actual devices.
Note that if this app is destined for the app store, you'll want to engage lots of different people in different environments and different devices and different scenarios (poor GPS locations, with wifi, no wifi, etc.). Even when you do your own real-world GPS experience with a physical device, you must appreciate that everyone else's may experience different GPS results.
I must confess to some reservations whether the iPhone GPS is accurate enough for this sort of app as a generalized solution, and you'll probably want to warn the user to make sure to plug in their iPhone before they go to sleep so the GPS doesn't drain the battery. You might also want to play tricks like turning on location services after the user has demonstrated that they're up and about. You might also not even want to turn on location services until a few minutes before the alarm, thereby assuring that the GPS is as accurate as it can be when the alarm goes off, but not draining their battery as they sleep if they don't happen to have it plugged in and charging.
Update:
Probably easier than calculating distances yourself, use the location services distance filter (i.e. only generate events when the location changes by x meters). For example:
- (void)startLocationManager
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 5.0; // detect when I move 5 meters
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
}
When I do that, standing completely still, as soon as I turn on location services, I get a whole bunch of events before I even start moving. You can see the GPS warming up, and getting more and more accurate:
latitude longitude horizontalAccuracy
---------------- ------------------ ------------------
39.9482837548282 (82.4963494095571) 65
39.9482817585030 (82.4964543834170) 10
39.9482392622539 (82.4964914314290) 5
39.9481753502422 (82.4964918505242) 5
39.9481269028419 (82.4964797805836) 5
I wait for that to quite down (it took 15-20 seconds in my case), and then I started walking in a straight line, looking at the distance from the last location I got above.
latitude longitude horizontalAccuracy distance
---------------- ------------------ ------------------ --------
39.9481722908476 (82.4964962091138) 5 5.3
39.9482206544289 (82.4965029146363) 5 10.4
39.9482627315828 (82.4965282279839) 5 15.4
39.9483157471204 (82.4965248752227) 5 21.2
I must confess that while I didn't measure it, these distances didn't feel quite right, but possibly within the 5 meter tolerance I set up with my distanceFilter.
Anyway, as all of this evidences, the right process is probably going to be
turn on location services, setting the distanceFilter appropriate for your app;
wait for the location to settle down;
make sure the horizontalAccuracy is even plausible for this process to work at all; and
wait for the new location notification based upon the GPS determining that the distanceFilter has been exceeded.
This never will be perfect (e.g. I had to walk a good 6-10 meters before my "5 meter" distanceFilter kicked in, probably a combination of the GPS lagging a few seconds and the horizontalAccuracy), but it might be "good enough for government work."
(By the way, I've changed my coordinates in the above log, so don't look for me in this Ohio cornfield, but it gives you and idea of the sort of pattern you may see.)

Accuracy of "Region Monitoring" on iOS 6

I'm trying to make a location-based app that requires constant updating of the location after opening. I was looking for a way to keep the location in the background when the app is closed and I found that is not the best way because it would drain the battery very quickly from any iPhone.
To cut a long story short I found with the "Region Monitoring" as I have understood they are the same as Apple uses for their App "Reminders" that also works with location. Then I used that option to test my app and I realize that in the simulator "never" enters the method "didEnterRegion" despite it is correctly created:
-(void) locationManager: (CLLocationManager *) manager didEnterRegion: (CLRegion *) region
{
NSLog (# "entering the region");
CLLocation * location1 = manager.location;
CLLocation * location2 = [[CLLocation alloc] initWithLatitude: latitud.doubleValue longitude: longitud.doubleValue];
}
I finally realized that the method is called but only when I walked away much of the area and I was back to the area, indicating that it did not work because I was already in that location (all this in the simulator), the strange thing is that it was not, the location of the simulator was miles from the region to monitor! Which leads me to think it is problem of accuracy, despite creating the region with 50 or 100 meter radius was spreading the 3 or 4 km, so I was useless.
Has anyone experienced this knows if iOS 6 simulator has this error or if precision problem? I don't have a Developer Licence yet to test it directly on my iPhone and I do not want to buy it yet because my app is far from finished, I appreciate any help on this topic.
Thanks in advance.
I would suggest doing actual ground testing. Region monitoring accuracy varies a lot depending on the test location.
Second option is to use region monitoring to just wake up the app and start standard location service. To do this you need permission to run the app in background. Apple may not approve this. Our app got rejected using this approach.
DidEnterRegion and ExitRegion is not very accurate. Around +, - 150m accuracy.
But -(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
is very accurate. Use this to find out your regions have coordinates/current location
CLLocation *currentLocation = [locations lastObject];
if ([self.ARegionYouSetBeforeHand containsCoordinate:currentLocation.coordinate])
{
//you arrived at this region!!
}
Region monitoring works (generally) on the same basis as the "significant location change" location service. That is, it uses cell tower location; Apple suggests you can usually expect accuracy around 1km, but warns that actual accuracy will vary with the density of cell towers in your area. You can find more background on this topic in the WWDC videos on Core Location.
It might be feasible -- I haven't tried -- to use Region Monitoring to (wake up your app and) get a general guess at the user's location, then use a higher accuracy mode in CLLocationManager to get a more precise fix.
At finish I realize that the accuracy of Region Monitoring is good enought for the most purposes, but in the simulator can be tricky because it not have GPS or cellphone hardware, anyway when I used "region monitoring" in my device even in a simulation route the function works correctly.
And for more accuracy even, I invoked the method "startUpdatingLocation" of the CLLocationManager into the method "didEnterRegion", that works okey for me.
I have experienced the same issue with Region Monitoring on iPhone 5C with cellular tower mobile data, disabling the WiFi, I was used to get didEnterRegion called very soon, when I was 1.95 Km away from the monitoring point for which I kept region radius of 100.0 meters. Which is too early.
Apple has some Threshold value determined by the hardware and the location technologies that are currently available. So for me it might be around 1800 meters early value.
I suggest to keep radius minimum (say 25.0 for 500.0 region) OR if it is so precious notification, use didUpdateLocations delegate method and determine your region and notify the same.
Regards,
Kaushik
I found that the built in region monitoring wasn't accurate enough for my needs. If you need higher accuracy, first set your accuracy appropriately, and then you can use something like this:
(Uses the same method as #coolcool1994, but a little more in-depth and using Swift 2.1. Remember to set up CLLocationManager and its Delegate (where the functions below would reside), and start receiving location updates)
var activeRegions = Set<String>()
var monitoredRegions = [
CLCircularRegion(center: CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0),
radius: 300, identifier: "Region1"),
CLCircularRegion(center: CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0),
radius: 300, identifier: "Region2")
]
func manuallyUpdateActiveRegionsFromLocation(location: CLLocation){
// Optionally explicitly reject unwanted location updates
// Setting the location manager delegate's desiredAccuracy should be enough
let maxOldTime = 300 //5 minutes
let minAccuracy = 300 //300 metres
if abs(location.timestamp.timeIntervalSinceNow) > maxOldTime
|| location.horizontalAccuracy > minAccuracy {
return
}
for monitoredRegion in monitoredRegions {
if monitoredRegion.containsCoordinate(location.coordinate) {
activeRegions.insert(monitoredRegion.identifier)
} else {
activeRegions.remove(monitoredRegion.identifier)
}
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
manuallyUpdateActiveRegionsFromLocation(location)
}
}
Pre-Defined GPS Accuracy on MK MapView
kCLLocationAccuracyBestForNavigation – Uses the highest possible
level of accuracy augmented by additional sensor data.
kCLLocationAccuracyBest – The highest recommended level of
accuracy for devices running on battery power.
kCLLocationAccuracyNearestTenMeters - Accurate to within 10 meters.
CLLocationAccuracyHundredMeters – Accurate to within 100 meters.
kCLLocationAccuracyKilometer – Accurate to within one kilometer.
kCLLocationAccuracyThreeKilometers – Accurate to within three
kilometers.
Usage:
var locationManager: CLLocationManager = CLLocationManager()
//MapView Location
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
locationManager.startUpdatingHeading()

Resources