I am developing an application that computes the distance travelled by the person. I am testing it on a iPad (Device-3.2). My iPad is using WiFi to get the current location. The results are highly inaccurate even though i have filtered the values. I don't know if GPS will give me accurate results. I am pasting the entire code below. Please verify the code and in case of errors please let me know. It would b very helpful if some one test the code on iPhone(3g) or iPad(3g). If not possible then just check the logic.....also i want to compute the calories burnt ..is there any formula to do so..? I have made simple view based project.....and used a distance label in nib file to set distance value but distance is updating at a very rapid rate....please correct it.
// iPacometerViewController.h
#interface iPacometerViewController : UIViewController {
CLLocationManager *locationManager;
CLLocation *oldLocat;
CLLocation *newLocat;
IBOutlet UILabel *distanceLabel;
}
#property(nonatomic,assign)IBOutlet UILabel *distanceLabel;
#property(nonatomic,retain)CLLocationManager *locationManager;
#property(nonatomic,retain)CLLocation *oldLocat;
#property(nonatomic,retain)CLLocation *newLocat;
-(void)computeDistanceFrom:(CLLocation *)oldL tO:(CLLocation *)newL;
#end
// iPacometerViewController.m
#import "iPacometerviewController.h"
#implementation iPacometerViewController
static double distance = 0.0;
#synthesize locationManager;
#synthesize oldLocat;
#synthesize newLocat;
#synthesize distanceLabel;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//initializing location manager
locationManager =[[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager.distanceFilter = 150.0f;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
oldLocat = [[CLLocation alloc]init];
newLocat = [[CLLocation alloc]init];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
if (newLocation.horizontalAccuracy 60.0) return; // data is too long ago, don't use it
NSLog(#"oldd %#",oldLocation);
self.oldLocat = oldLocation;
self.newLocat = newLocation;
if(oldLocat!=nil)
{
[self computeDistanceFrom:oldLocat tO:newLocat];
}
}
-(void)computeDistanceFrom:(CLLocation *)oldL tO:(CLLocation *)newL
{
NSLog(#"oldd %#",oldL);
NSLog(#"new %#",newL);
CLLocationDistance currentDistance = [oldL distanceFromLocation:newL];
NSLog(#"you have travel=%f",currentDistance);
distance = distance + currentDistance;
double distanceInKm = distance/1000;
NSString *distanceLabelValue = [NSString stringWithFormat:#"%1.2f Kms",distanceInKm];
distanceLabel.text = distanceLabelValue;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)dealloc {
//[mapView release];
[oldloct release];
[newLocat release];
[locationManager release];
[super dealloc];
}
#end
your logic is fine. Its the iPhone's logic that is bad.
There are two main issues that I see when writing this sort of code.
1) What I call poisoned points. These are cached points that the iPhone reports erroneously. You can check for them by dropping any points where the timestamp is the same as or earlier than the latest point. It may also be worth recording a frequency of visits to a point. The poisoned points tend to be visited time and time again (maybe 5 times) each time as a jump from your real track. If you can spot them, you can rule them out.
2) Accuracy - especially height changes. Look at the horizontal and vertical accuracy figures returned with each point. And look at the height. If they are inaccurate, then the velocity and therefore distance traveled will be too. One cause of bad distances is if one end of your pair has an altitude, and the other does not: the "not" gets classed as zero - so if you are 200m above the sea level at the time you have just travelled 200m without moving!
To improve accuracy, you may be better off writing your own great circle distance algorithm (or even simple Pythagoras given the distances are small) which ignores height, or just doing some better filtering on the points you use.
I'm struggling to find a reliable way of measuring location, distance & speed with the iPhone (3GS). and iPad or Touch is just going to be worse I guess.
If you can drop me a mail at andrew dot hawken at fiftyeggs dot co dot uk I will send you my raw iPhone logging software. It would be great if you can run it for a couple of trips and let me have the results - I'm trying to solve the same problem, but only have GPS datasets to work on.
Related
I have an application in which user track his/her route when jogging or cycling, So i need perfect location, so user's routes will be perfect.
But, I have one problem in this,
locManager = [[CLLocationManager alloc] init];
[locManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[locManager setDelegate:self];
[locManager startUpdatingLocation];
In viewDidLoad. Using this didUpdateToLocation method called multiple times when I just dont move device a little and on map very strange route draw.
I just cant understand why this happen, if I am doing some wrong or missing something.
Thanks.......
I use locationManager.distanceFilter = 500; (or so) // meters
to prevent multiple calls from happening. just remember to call this BEFORE you start updating your location
You can set the distancefilter of the location manager hope this may help you
locationManager=[[CLLocationManager alloc] init];
locationManager.delegate=self;
locationManager.desiredAccuracy=kCLLocationAccuracyNearestTenMeters;
locationManager.distanceFilter=10.0;
[locationManager startUpdatingLocation];
When you first start location services, you'll generally see multiple location updates come whether you're moving or not. If you examine the horizontalAccuracy of the locations as they come in, you'll see that while it's "warming" up it will show a series of locations with greater and greater accuracy (i.e. smaller and smaller horizontalAccuracy values) until it reaches quiescence.
You could disregard those initial locations until horizontalAccuracy falls below a certain value. Or, better, during start up, you could disregard the previous location if (a) the distance between a new location and the old location is less than the horizontalAccuracy of the old location and (b) if the horizontalAccuracy of the new location is less than that of the prior location.
For example, let's assume you're maintaining an array of CLLocation objects, as well as a reference to the last drawn path:
#property (nonatomic, strong) NSMutableArray *locations;
#property (nonatomic, weak) id<MKOverlay> pathOverlay;
Furthermore, let's assume your location update routine is just adding to the array of locations and then indicating that the path should be redrawn:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"%s", __FUNCTION__);
CLLocation* location = [locations lastObject];
[self.locations addObject:location];
[self addPathToMapView:self.mapView];
}
Then the addPathToMapView can therefore remove the second from last location if it's less accurate than the last one and if the distance between them is less than the most recent location's accuracy.
- (void)addPathToMapView:(MKMapView *)mapView
{
NSInteger count = [self.locations count];
// let's see if we should remove the penultimate location
if (count > 2)
{
CLLocation *lastLocation = [self.locations lastObject];
CLLocation *previousLocation = self.locations[count - 2];
// if the very last location is more accurate than the previous one
// and if distance between the two of them is less than the accuracy,
// then remove that `previousLocation` (and update our count, appropriately)
if (lastLocation.horizontalAccuracy < previousLocation.horizontalAccuracy &&
[lastLocation distanceFromLocation:previousLocation] < lastLocation.horizontalAccuracy)
{
[self.locations removeObjectAtIndex:(count - 2)];
count--;
}
}
// now let's build our array of coordinates for our MKPolyline
CLLocationCoordinate2D coordinates[count];
NSInteger numberOfCoordinates = 0;
for (CLLocation *location in self.locations)
{
coordinates[numberOfCoordinates++] = location.coordinate;
}
// if there is a path to add to our map, do so
MKPolyline *polyLine = nil;
if (numberOfCoordinates > 1)
{
polyLine = [MKPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
[mapView addOverlay:polyLine];
}
// if there was a previous path drawn, remove it
if (self.pathOverlay)
[mapView removeOverlay:self.pathOverlay];
// save the current path
self.pathOverlay = polyLine;
}
Bottom line, just get rid of locations that are less accurate than the next one you have. You could get even more aggressive in the pruning process if you want, but there are tradeoffs there, but hopefully this illustrates the idea.
startUpdatingLocation
Will continuously update a user's location even when the location does not change. You just need to structure your app to handle these continuous updates according to your needs.
Try reading Apple's documentation on this subject. It is confusing at first but try anyway.
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW1
I think this is what you need.startMonitoringForRegion:desiredAccuracy
for Example see the following github link.
Try this Bread Crumb sample code provided by Apple..
http://developer.apple.com/library/ios/#samplecode/Breadcrumb/Introduction/Intro.html
Add this,
[locManager stopUpdatingLocation];
into your updateUserLocation delegate method.
Review the following code snippet:
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
[_locationManager stopUpdatingLocation];
}
locationManager.startUpdatingLocation() fetch location continuously and didUpdateLocations method calls several times,
Just set the value for locationManager.distanceFilter value before calling locationManager.startUpdatingLocation().
As I set 200 working fine
locationManager.distanceFilter = 200
locationManager.startUpdatingLocation()
I'm beginning to mess around with the CLLocationManager and I am trying to use the callback method below. I went outside to test it on my iPhone, and with the first few updates, I seem to be getting gibberish values, like all of a sudden my distance is 39meters even though I haven't moved anywhere. Or sometimes it will start at 5, and then jump to 20 meters, again without me moving anywhere. I went outside and walked, and the updates from the initial starting point seemed 'ok,' and then when I walked back, I got back to the original 39 meters starting point. I was wondering if what I am doing below is correct. I also included my viewDidLoad method where I initialize my CLLocationManager object.
Is there a way to ensure that my first values are accurate? Thanks in advance.
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if (startingPoint == nil) {
NSLog(#"NIL starting point");
self.startingPoint = newLocation;
}
CLLocationDistance distance = [newLocation distanceFromLocation:startingPoint];
NSString *distanceString = [[NSString alloc] initWithFormat:#"%g m", distance];
distanceLabel.text = distanceString;
[distanceString release];
numberOfUpdates++;
NSString *countString = [[NSString alloc] initWithFormat:#"%d", numberOfUpdates];
countLabel.text = countString;
[countString release];
}
GPS is an imperfect technology. Atmospheric conditions, sattelite availability (and position), sky visibility, signals bouncing off nearby objects (buildings) all contribute to it's inherent inaccuracy. Though there is even an "accuracy" value (which is usually pretty good) - even this is not completely reliable.
Airplanes are not allowed to use GPS for precision approaches - even their receivers aren't accurate enough, and they require other technologies (which have their own issues).
Try running the standard "Maps" application and use it as a comparison. I think your code is good - it's jut GPS.
Of course I am saying this because I am working on my own maritime navigation application, an running into all these issues myself.
Though this is an old question I still like to answer. The first call to "didUpdateToLocation" usually is some old value and you should always check the timestamp. From the Apple documentation:
NSDate* eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0)
{
// use the value
}
In your didUpdate method, you can test newLocation's .horizontalAccuracy property for an acceptable value and toss any values that are impossible (or even unlikely!).
Has anyone nailed this down yet?
I've read a lot of forum postings and I still can't tell if this is a settled question...
Given that didUpdateToLocation() can return cached or inaccurate information how do you tell when you have an accurate fix?
When you set desired accuracy to kCLLocationAccuracyNearestTenMeters, HundredMeters, Kilometer, ThreeKilometers, it seems obvious that you can compare the horizontal accuracy of points returned against the desired accuracy to decide when to accept a point.
But the values of kCLLocationAccuracyBestForNavigation and kCLLocationAccuracyBest are -2 and -1 respectively so how do I know when I have the desired accuracy??
I currently check the age of the fix and reject any fix that is too "old" timewise.
I then check to see if the horizontal accuracy is negative and reject that if it is.
if I get a positive verticalAccuracy then I generally use that fix since the corresponding horizontal accuracy is also good.
But these checks were used when I has looking for accuracy in the 10-100s of meters...
I don't know what if anything to check when I'm running ...AccuracyBestForNavigation or ...AccuracyBest?? Am I suppose to level the locationManager running constantly and never expect to shut it off and don't filter any results??
Anyone wiser than I with an answer??
Thanks
It's not really a definitive, perfect algorithm (I doubt there is one for this task, because of external conditions, I mean, you can try get ur location on plain field or inside a tomb), it's an ad-hoc one, and it works for me.
I did a wrapper for LocationManager, like
#protocol LocationManagerWrapperDelegate <NSObject>
#required
- (void) locationUpdated: (CLLocation *) locationUpdate;
- (void) errorOccured: (NSError *) error;
#end
#interface LocationManagerWrapper : NSObject <CLLocationManagerDelegate>
{
CLLocationManager *locationManager;
id delegate;
CLLocation *mostAccurateLocation;
int updatesCounter;
BOOL m_acceptableTimePeriodElapsed;
}
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, retain) CLLocation *mostAccurateLocation;
#property (nonatomic, assign) id <LocationManagerWrapperDelegate> delegate;
- (void) startUpdatingLocation;
- (void) locationManager: (CLLocationManager *) manager
didUpdateToLocation: (CLLocation *) newLocation
fromLocation: (CLLocation *) oldLocation;
- (void) locationManager: (CLLocationManager *) manager
didFailWithError: (NSError *) error;
+ (LocationManagerWrapper *) sharedInstance;
#end
Implementation
#define NUMBER_OF_TRIES 4
#define ACCEPTABLE_TIME_PERIOD 15.0
- (void) startUpdatingLocation
{
NSAssert(self.delegate != nil, #"No delegate set to receive location update.");
updatesCounter = 0;
self.mostAccurateLocation = nil;
m_acceptableTimePeriodElapsed = NO;
[NSTimer scheduledTimerWithTimeInterval:ACCEPTABLE_TIME_PERIOD
target:self
selector:#selector(acceptableTimePeriodElapsed:)
userInfo:nil
repeats:NO];
[self.locationManager startUpdatingLocation];
}
- (void) acceptableTimePeriodElapsed: (NSTimer *) timer
{
#synchronized(self)
{
m_acceptableTimePeriodElapsed = YES;
// TODO: if period is set by user - check we have mostAccurateLocation at this point
[self.delegate locationUpdated:self.mostAccurateLocation];
[self.locationManager stopUpdatingLocation];
}
}
- (void) locationManager: (CLLocationManager *) manager
didUpdateToLocation: (CLLocation *) newLocation
fromLocation: (CLLocation *) oldLocation
{
#synchronized(self)
{
if (m_acceptableTimePeriodElapsed) return;
NSLog([NSString stringWithFormat:#"lat: %#, long: %#, acc: %#",
[ [NSNumber numberWithDouble:newLocation.coordinate.latitude] stringValue],
[ [NSNumber numberWithDouble:newLocation.coordinate.longitude] stringValue],
[ [NSNumber numberWithDouble:newLocation.horizontalAccuracy] stringValue] ] );
updatesCounter++;
// ignore first returned value
if (updatesCounter <= 1) return;
if (self.mostAccurateLocation == nil ||
self.mostAccurateLocation.horizontalAccuracy > newLocation.horizontalAccuracy)
{
self.mostAccurateLocation = newLocation;
}
if (updatesCounter >= NUMBER_OF_TRIES)
{
[self.delegate locationUpdated:self.mostAccurateLocation];
[self.locationManager stopUpdatingLocation];
}
}
}
The code is not excellent (neither formatting is), but the idea, I think, is simple and clear, get first location, throw it out, it's a cached one, do 3 tries max for a the most accurate location. It can take a long time, and if user is waiting (as in my case), define a time limit. One more time, it works for my app, but feel free to tweak it or take another approach.
I know this is an old thread I'm bringing back, but I came across this question because I was also looking for a most accurate solution, and after reading your solution fspirit, i like it a lot. Like morgman, I was also pretty much thinking something similar, but you just put our thoughts in code, lol. Also I like your forward-multi-threaded thinking ;)
But I actually have a question/suggestion, if you will.
When I try to get location data on the iphone, I get at least 3 sets of coordinates with the same horizontal accuracy right off the bat. So a good number of tries would have to be more than 4. But the question then, is how many tries is good?
So i suggest that instead of changing the number of tries, we can make a slight tweak to the code, so that the number of tries counter goes up Only if the horizontal accuracy is significantly less than the previous one, or less than the most accurate one, something like that. I'm just thinking out loud here. Because I took a look at all the location data I got from my iphone 4 (and I'm not sure how it works on other devices), and judging from what i saw, the location is getting updated pretty constantly, and the horizontal accuracy stays relatively the same for the first couple of updates, then after a while it suddenly jumps down from say, 400 to 100, and then after a second or two more, it jumps down again to about 30. All this is over the course of about 30-50 updates, so in this situation a max number of tries of 4 might not work, unless you're only counting the jumps. What do you guys think?
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.
I have a simple mapview that has the following viewdidload method and didupdate to location:
- (void)viewDidLoad {
NSLog(#"in view did load");
[super viewDidLoad];
self.mapView.showsUserLocation = YES;
self.put_url = nil;
locationManager = [[CLLocationManager alloc] init];
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[locationManager setDelegate:self];
noUpdates = 0;
[locationManager startUpdatingLocation];
self.availableParking = [[NSMutableArray alloc] init];
//self.availableParking = nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"in didupdatetolocatoin");
if([newLocation horizontalAccuracy] < 100 && [newLocation horizontalAccuracy] > 0) {
// Add annotation to map
DDAnnotation *annotation = [[DDAnnotation alloc] initWithCoordinate:newLocation.coordinate title:#"Park Here?"];
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.05;
span.longitudeDelta = 0.05;
region.span = span;
region.center = newLocation.coordinate;
self.mapView.region = region;
NSLog(#"in didupdatetolocation");
noUpdates++;
NSLog(#"IN UPDATELOCATION NOUPDATES = %d",noUpdates);
if (noUpdates == 1) {
[self.mapView addAnnotation:annotation];
NSLog(#"ADDED ANNOTATION IN didUpdateToLocation!!");
[locationManager stopUpdatingLocation];
[self.settingsViewController setState:self.mapView.userLocation.subtitle andCity:#"fuckface"];
NSLog(#"STOPPED UPDATING LOCATION");
UpdateLocation *updatedLocation = [[[UpdateLocation alloc] initWithUserid:#"fuckface" andCoordinate:newLocation.coordinate withMapViewController:self]
autorelease];
NSLog(#"Lat = %f, Long = %f",newLocation.coordinate.latitude,newLocation.coordinate.longitude);
//[NSThread detachNewThreadSelector:#selector(getLocations) toTarget:self withObject:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(sendUpdate)
name:#"NoteFromOne"
object:updatedLocation];
}
// We only update location once, and let users to do the rest of the changes by dragging annotation to place they want
} else {
NSLog(#"Accuracy not good enough %lf", [newLocation horizontalAccuracy]);
}
}
When Im connected to a wireless network it works flawlessly zooming in and dropping an annotation pin in my current location. Over the 3G network it never zooms or drops the pin. Any ideas as to why? Thanks in advance.
Here's a couple of screen shots:
With 3G:
alt text http://www.freeimagehosting.net/uploads/fe7fcbb2ea.jpg
With wifi:
alt text http://www.freeimagehosting.net/uploads/6b653e60a7.jpg
The GPS unit has nothing to do with the network connection (unless the GPS signal is not available, in which case the GPS may try to use the wifi hotspot or the cell your device is connected to in order to infer your location). It works independently of it if the GPS signal is available, and the network is only used to actually show the map. From the code snipped you posted, you only show the map when you reach an horizontal accuracy less than 100 meters, otherwise you let the GPS unit updating the location. However, if you try your code in the exact SAME place, the GPS unit on your device should always return the same updates. Therefore, I really do not understand the behaviour you are describing: this would be possible only if the GPS unit returned different updates for the same place when a different network connection is available because the GPS signal is not available. Are you seeing the same latitude/longitude in the two cases or not? Are you seeing the same accuracy? Be careful to measure this in exactly the same place in both cases.
If you obtain the same updates, then it may be possible that your 3G cellular connection is simply not powerful enough or is only apparently available, so that you did not get the map. Try testing the speed of your 3G network from the same place.
A related consideration. You should allow the GPS unit to work until either a specified amount of time elapses (say 20 seconds, use NSTimer for this) or until you reach a specified level of accuracy, whichever happens first. Otherwise you may end up consuming too much the battery.