Calculate Accurate Speed Using GPS - iphone

I am using following code to update location after every 0.1 second -
- (void)viewDidLoad
{
[super viewDidLoad];
CLController = [[CoreLocationController alloc] init];
CLController.delegate = self;
// set auto update timer
currentTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:#selector(updatestart) userInfo:nil repeats:YES];
}
-(void)updatestart
{
[CLController.locMgr startUpdatingLocation];
}
And then I am trying to calculate speed using
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[locMgr stopUpdatingLocation];
NSLog(#"%f",newLocation.speed);
}
I am getting speed in meter/sec but after some time the process slows down and even when I stop traveling the speed is being calculated.
What I want is to get accurate speed and show alert if speed is above 12km/hr.
Is there any other approach which I can use to find the same?
Thanks..

Starting and stopping the location manager like that isn't the way to do it. You can't force it to give more location updates. Leave it running and average the location updates over time. To get the best accuracy, use kCLLocationAccuracyBestForNavigation for the location manager's desiredAccuracy.

Related

Any side effect of CLLocationManager with NSTimer using such approach?

I am working on an iPhone app which needs Location Updates with intervals specified by the user.
Here is the code sample, which I am using to do this:
#implementation TestLocation
- (void)viewDidLoad{
if ([Utils getDataWithKey:TIMER_INTERVAL] == nil) {
[Utils saveDataWithKey:TIMER_INTERVAL withValue:#"60.0"];
}
locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
}
- (void)startLocationManager:(NSTimer *)timer{
[locationManager startUpdatingLocation];
[timer invalidate];
timer = nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// Here is code to handle location updates...
[manager stopUpdatingLocation];
// Timer will start getting updated location.
NSTimeInterval timeInterval = [[Utils getDataWithKey:TIMER_INTERVAL] doubleValue];
[NSTimer scheduledTimerWithTimeInterval:timeInterval
target:self
selector:#selector(startLocationManager:)
userInfo:nil
repeats:NO];
}
// other implementations ...
#end
The code is working like a charm.
The question is:
I'm using CLLocationManager along with NSTimer, does this effect memory or battery consumption? I mean any negative effect on User Experience?
If so, any suggestion, helping links to do such task with optimization will be much appreciated.
Note: Utils is my class to store or retrieve data.
Yes this will have some side effects, you will not get the desired accuracy. Since it will call the locationManager:didUpdateToLocation:fromLocation: every time the GPS signal will
be come more accurate.
That isn't a good strategy because you can receive multiple asynchronous location events before the first call to [manager stopUpdatingLocation]. That will lead to exponential number of timers getting created.
Instead, just start the repeating timer after creating your location manager and still stop the location manager after each received event.

CoreLocation startMonitoringRegion not firing didenterregion /didexitregion delegates

i cant seem to get any of the didenterregion/didexitregion delegates firing , been stuck for 2 days on it
im using xcode4.2 with ios5 sdk and location simulation,
has anyone any luck getting this to work? is a 100m radius too small?(ive tried 1000 doesnt work too) or is there something wrong with my code
checked also with normal location updating and user location indeed enters region. could not find an answer anywhere as well
MainViewController(also CLLocationManagerDelegate)'s ViewDidLoad method:
mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
mapView.showsUserLocation = YES;
mapView.mapType = MKMapTypeStandard;
mapView.delegate = self;
[self.view addSubview:mapView];
MKCoordinateRegion region;// = {{0.0,0.0},{0.0,0.0}};
MKCoordinateSpan span;
span.latitudeDelta = 0.02;
span.longitudeDelta = 0.02;
CLLocationCoordinate2D coordinates =CLLocationCoordinate2DMake(1.34537, 103.84515);
region.span = span;
region.center = coordinates;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
if (locationManager==nil){
locationManager = [[CLLocationManager alloc] init];
}
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
self.locationManager.delegate =self;
if ([CLLocationManager regionMonitoringAvailable]){
NSLog(#"regionMonitoring available");
CLRegion* region3 = [[CLRegion alloc] initCircularRegionWithCenter:coordinates radius:100 identifier:#"region3"];
[self.locationManager startMonitoringForRegion:region3 desiredAccuracy:1.0];
NSLog(#"region = %#",region3);
NSLog(#"loc max = %f",self.locationManager.maximumRegionMonitoringDistance);
NSLog(#"location manger monitored regions = %#",self.locationManager.monitoredRegions);
also implemented all delegate methods neccessary
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didfailwitherror");
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"latitude %+.10f, longitude %+.10f\n",newLocation.coordinate.latitude,newLocation.coordinate.longitude);
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
NSLog(#"didenterregion");
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
NSLog(#"didexitregion");
}
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error
{NSLog(#"monitoringDidFailForRegion");}
thanks for any help
How have you been testing the location? The regions are a little tricky, but once they are set up, they usually work great. What I usually do to test the region is just use the custom location in the Simulator. Use the same coordinates you are adding your region. 1000M is pretty large. I've tested with regions as small as 15-20M. If you have the right coordinates, it should be fine. Try switching back and forth from Apple HQ and your custom coordinates. You should be able to see the didEnter and didExit methods trip almost instantly if you have them logged out.
The one area you may have issues with is that the callbacks might not be getting to your method. None of my regions worked until I set up my AppDelegate as the CLLocationManagerDelegate. The app delegate finally got the updates and processed them correctly. I would try adding these callbacks there first, then work your way back to your mainviewcontroller. Hope this helps.
UPDATE
Since answering this question, I have learned a few things more about region monitoring. Regions get added with a minimum region size despite what you add for default. I was told by an Apple engineer that the minimum region size is 100M. I still feel they are very accurate, but it might help to understand why it will check you in. If you need precision better than 100M, you may need to look at other options.

Show an UIAlert when delegate in LocationManager takes too long

I'm trying to create an alert when my LocationManager takes too long. In its delegate method, I check the newLocation's timestamp to make sure it's something recent:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 1.0) { // process time if time is less than 1.0 seconds old.
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateCount:) userInfo:nil repeats:YES];
return;
}
After some debugging, I realize that putting timer in that if block isn't right. What ends up happening is, the timer never invalidates, or rather my updateCount: method keeps running. I think what's happening is the delegate keeps getting called back with a bad time, then it keeps running more timers, so I get UIAlert after UIAlert.
I've used the same updateCount: method in a test project to create a 30 second timer and invalidate it and it works fine. Now however, I am stuck since I basically want to find the person's location, but if it takes too long (> 30 seconds), throw an alert. I'm not really sure where I should put this kind of code. It seemed to me to put it in the delegate where I'm making the check of the timestamp since that's the error condition I'm looking to keep track of. But that doesn't seem to be working well for me.
Is there a better place to put this kind of check? Or is there a better way to do this kind of task altogether? I'm pretty new to programming, so my knowledge is limited. TIA.
I think what you should be doing is- set the 30 second timer when you call [locationManager startUpdatingLocation]; Now, you can update your method to look something like-
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 1.0) { //good time found
//use this location
//either remove the 30 second timer or set a boolean
self.goodLocationFound = YES;
[locationManager stopUpdatingLocation];
}
In your timer method, you could do-
{
[locationManager stopUpdatingLocation];
if(self.goodLocationFound == NO)
{
//display alert
}
}

I can't get the gps coordinates for a persistent period of time

I have implemented the standard method of retrieving the coordinates from the gps using - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation.
The problem is that this function is called only on initialization and not throughout the life of the program. Is this normal?
In android you would implement a listener and you would get data instantly.
Is this not the wright way how i'm doing it? If it is, what could be the problem? (btw i've checked, i don't stopUpdatingLocation)
I have a CLLocationManager inherited class named testing and initialize it
testing* cllm = [[testing alloc] init];
cllm.delegate = self;
i later start the updating
[cllm startUpdatingLocation];
self.locationManagerDelegate = delegate;
and later is called
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
but after that it isn't called anymore. I need it to be called frequently so that i may calculate the distance to a certain point X from where i am.
Agreed with #Matt, without more code the best solution I can offer is this to tell it to update every time the device is moved with:
[self.locationManager setDistanceFiler:kCLDistanceFilterNone]
Update
I went through past projects and found the code I believe you are looking for assuming your location manager subclass is working properly
- (void)viewDidLoad {
[super viewDidLoad];
//Location
// create new location manager
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
// start location manager
[self.locationManager startUpdatingLocation];
}
-(void) distanceBetweenUserandPin {
CLLocation *currentUserLocation = [[CLLocation alloc] initWithLatitude:_currentLocation.latitude longitude:_currentLocation.longitude];
CLLocation *currentPinLocation = [[CLLocation alloc] initWithLatitude:_pinLocation.latitude longitude:_pinLocation.longitude];
CLLocationDistance distanceBetweenUserAndPinMeters = [currentUserLocation distanceFromLocation:currentPinLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
//This successfully saves Lat, Long Data to a point location
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude);
NSLog(#"%f, %f", location.latitude, location.longitude);
//This assigns the value of location to the ivar _currentLocation
_currentLocation = CLLocationCoordinate2DMake(location.latitude, location.longitude);
NSLog(#"%f, %f", _currentLocation.latitude, _currentLocation.longitude);
}
First, it seems strange to me that you would use a subclass of CLLocationManager, since I'm not sure what benefit that provides you. Assuming that's not the problem, however...
From the CLLocationManager documentation:
This method returns immediately. Calling this method causes the
location manager to obtain an initial location fix (which may take
several seconds) and notify your delegate by calling its
locationManager:didUpdateToLocation:fromLocation: method. After that,
the receiver generates update events primarily when the value in the
distanceFilter property is exceeded. Updates may be delivered in other
situations though. For example, the receiver may send another
notification if the hardware gathers a more accurate location reading.
What's happening is that it is being called once for the initial position fix, but it isn't calling again because other conditions haven't changed. If a user doesn't move anywhere, then new location data won't be provided since it will be the same as last time (with a few exceptions as mentioned in the docs).
When you're testing your app, make sure that you try moving around and changing your location to produce an update. If that doesn't work, try experimenting with the desiredAccuracy and distanceFilter properties:
You start standard location services by calling the
startUpdatingLocation method. This service is most appropriate for
applications that need more fine-grained control over the delivery of
location events. Specifically, it takes into account the values in the
desiredAccuracy and distanceFilter property to determine when to
deliver new events.
Other than that, I'd guess it might have to do with how you're subclassing CLLocationManager. Providing some of that code might help.

iPhone Gps logging inaccurate

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.