I am developing a speedometer application in iPhone. I have used CLLocationManager(GPS based) to measure the speed of a vehicle. But i don't get accurate speed. I have tried all the filtering methods given in various stackoverflow discussions. But i don't get any improvement. At times, the speed reaches to a greater value(>400kmph).
I dont know what goes wrong. Please suggest me any code to get the accurate speed.
Thanks in advance,
I have followed the below mentioned stack over flow discussions
Measuring velocity via iPhone SDK
Optimizing CLLocationManager/CoreLocation to retrieve data points faster on the iPhone
CLLocation speed
Why is my CLLocation speed so inaccurate?
I have currently implemented the following code:
//locationManager declaration in ViewDidLoad
locManager=[[CLLocationManager alloc]init];
locManager.delegate =self;
[locManager startUpdatingLocation];
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSTimeInterval locationAge = abs([newLocation.timestamp timeIntervalSinceNow]);
if (locationAge > 5.0) return;
if (newLocation.speed < 0 || newLocation.horizontalAccuracy < 0) return;
if ((newLocation.horizontalAccuracy < (oldLocation.horizontalAccuracy - 10.0)) || (newLocation.horizontalAccuracy < 50.0)|| (newLocation.horizontalAccuracy <= 150.0))
{
if(oldLocation != nil)
{
CLLocationDistance distanceChange = [newLocation distanceFromLocation:oldLocation];
NSTimeInterval sinceLastUpdate = [newLocation.timestamp timeIntervalSinceDate:oldLocation.timestamp];
double calculatedSpeed;
calculatedSpeed = (distanceChange / sinceLastUpdate)*3.6; // to convert the speed into kmph.
}
}
}
Even if you don't like the built-in velocity stuff, it's simple to roll your own.
Speed is distance over time, right?
If you've got two CLLocations, you can do:
CLLocationDistance distance = [aLocation distanceFromLocation:bLocation];
distance is now a float of the distance between those two points, in meters. And those two CLLocations have timestamp properties too, right? Containing NSDate objects. Well...
NSTimeInterval timediff = [aDate timeIntervalSinceDate:bDate];
timediff is the difference in seconds.
So your average speed between those points in meters per second is distance over timediff. You can now calculate instantaneous speed as accurately as the frequency with which you're getting location updates.
I imagine this is all that's happening to get the speed property of the CLLocation object you're getting handed. But hey, you want to be closer to the math, go right ahead.
From there, you can convert to whatever units you like. There are 1609.344 meters in a mile. There are 3600 seconds in an hour.
NOW: This is as flawed as any pure GPS-based speed calculation, in that you're not going to get perfectly spaced tick-marks of location updates, and sometimes they're going to be way wrong. If you lose GPS signal and the device falls back to cell tower triangulation, it might put you WAY off the road somewhere, and it's going to look like you strapped on a rocket pack to get way over there that fast.
Related
I initialize the locationManager this way:
if (!self.locManager)
{
self.locManager = [[CLLocationManager alloc] init];
self.locManager.delegate = self;
[locManager startMonitoringSignificantLocationChanges];
}
my device is not moving and still "didUpdateToLocation" is being called every time.
What could be a problem?
Thanks
didUpdateToLocation may update for a number of reasons, a good strategy for handling this is to gradually filter results based on timestamp, then required accuracy.
Apple provide a good example in the LocateMe sample app:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// 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 (self.bestEffortAtLocation == nil || self.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")];
}
}
}
Do you check for location differences? CoreLocation also calls callbacks when other attributes such as accuracy, heading or speed change
startMonitoringSignificantLocationChangesshould give you an initial fix and after that you will get callbacks for "significant changes" (cell tower change etc.)
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to calculate speed of our car using iphone
How can I calculate my speed with an iPhone? For example, I might want to measure the speed of my car when driving. How can I implement this?
Try something like this:
- (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);
}
1>You can use GPS
Use the location services to get the latitude and longitude at a particular point of time and the you can get the same after a certain period of time say t.You can get the distance traveled from Latitude and longitude and divide it by the taken to get the speed
See the Apple GPS Doc
2>You can use Accelerometer
if you start from a halt, you should be able to calculate the distance you travel
based off the acceleration over time of the device.
See the Apple Accelerometer Doc
I am using the CoreLocation framework to get my speed and distance to calculate average speed.
On the first update that CoreLocation sends out, it shows negative values for both speed and distance traveled. How can I fix this?
Speed is locationController.locationManager.location.speed where locationController holds my CoreLocation delegate. Distance is calculated by taking the old location and the new location and calculating distance.
//Distance
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
// make sure the old and new coordinates are different
if ((oldLocation.coordinate.latitude != newLocation.coordinate.latitude) &&
(oldLocation.coordinate.longitude != newLocation.coordinate.longitude))
{
mDistance = [newLocation distanceFromLocation:oldLocation];
}
}
The data returned by Core Location may be invalid for a number of reasons. Before you use the data, run this method to see if it is valid.
// From http://troybrant.net/blog/2010/02/detecting-bad-corelocation-data/
- (BOOL)isValidLocation:(CLLocation *)newLocation
withOldLocation:(CLLocation *)oldLocation
{
// filter out nil locations
if (!newLocation){
return NO;
}
// filter out points by invalid accuracy
if (newLocation.horizontalAccuracy < 0){
return NO;
}
// filter out points that are out of order
NSTimeInterval secondsSinceLastPoint = [newLocation.timestamp
timeIntervalSinceDate:oldLocation.timestamp];
if (secondsSinceLastPoint < 0){
return NO;
}
// filter out points created before the manager was initialized
NSTimeInterval secondsSinceManagerStarted = [newLocation.timestamp
timeIntervalSinceDate:locationManagerStartDate];
if (secondsSinceManagerStarted < 0){
return NO;
}
// newLocation is good to use
return YES;
}
There's a note in the CLLocationManager Class Reference:
Because it can take several seconds to return an initial location, the location manager typically delivers the previously cached location data immediately and then delivers more up-to-date location data as it becomes available. Therefore it is always a good idea to check the timestamp of any location object before taking any actions.
You should check whether you're getting a cached value and ignore it, waiting for the first "real" update.
NSTimeInterval timeSinceLastUpdate = [[newLocation timestamp] timeIntervalSinceNow];
// If the information is older than a minute, or two minutes, or whatever
// you want based on expected average speed and desired accuracy,
// don't use it.
if( timeSinceLastUpdate < -60 ){
return;
}
If you are collecting the first point, it has no reference to calculate speed or distance. I believe it is defined to return -1 if it can't get you what you need.
To resolve this, simply check for the negative values.
if(location.speed > 0) {
return;
}
I am using the instance method distanceFromLocation in order to compare my new location with const defined locations
I am giving CLLocation *bonuslocation an instant value which is one of the gps points i am interested in and then i compare it to the new location
if the distance is less than 20m from the point an audio file is played.
While this is working perfectly for one location it does not work at all
if i add locally into the updatelocationmanager function more than one..
The code:
CLLocation *bonuslocation = [.....]
CLLocationDistance distancea = [bonuslocation distanceFromLocation newlocation]
if (distancea <= 20)
{
//play an audio
}
Can i have some advice on how to do it for 10 gps points????
Today is my Birthday can you see that as a birthday present??
Thank you..
Why not just put it in a loop? Or just run the comparison on 10 locations? What exactly is it that isn't working?
EDIT:
You never mentioned where you're getting your other locations, so let's assume you make them somehow and store them in an array...
NSArray *locationArray;
I gathered from your comment that you have different sounds for each location? A simple way would be to store the sounds in a second array...
NSArray *soundsArray;
Then you can do the following in your locationManager:didUpdateToLocation:fromLocation:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
for(int i=0; i<[locationArray count]; i++){
CLLocation *location = (CLLocation *)[locationArray objectAtIndex:i];
if([newLocation distanceFromLocation:location] < 20.0){
//perform some action e.g.
//play sound at [soundArray objectAtIndex:i]
}
}
}
Although I don't recommend simply playing a sound, as this simple logic will cause the sound to be played once for every location within the threshold all at the same time.
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.