Имея проблемы с вычислением точного общего расстояния ходьбы/пробега с помощью CLLocationManager

Я пытаюсь создать приложение iOS, которое отображает общее расстояние, пройденное во время бега или ходьбы. Я прочитал и перечитал всю документацию, которую я могу найти, но у меня возникли проблемы с чем-то, что дает мне точное полное расстояние.

По сравнению с Nike + GPS или RunKeeper, мое приложение последовательно сообщает о более коротком расстоянии. Сначала они сообщают об этом, но по мере того, как я продолжаю двигаться, значения моего приложения и других запущенных приложений постепенно дрейфуют.

Например, если я прохожу на 0,3 километра (подтвержденный моим одометром автомобиля), Nike + GPS и RunKeeper каждый раз сообщают ~.3 километра каждый раз, но мое приложение сообщит ~.13 километров. newLocation.horizontalAccuracy постоянно 5.0 или 10.0.

Вот код, который я использую. Мне что-то не хватает? Любые мысли о том, как я мог бы улучшить это, чтобы получить более точное чтение?

#define kDistanceCalculationInterval 10 // the interval (seconds) at which we calculate the user distance
#define kNumLocationHistoriesToKeep 5 // the number of locations to store in history so that we can look back at them and determine which is most accurate
#define kValidLocationHistoryDeltaInterval 3 // the maximum valid age in seconds of a location stored in the location history
#define kMinLocationsNeededToUpdateDistance 3 // the number of locations needed in history before we will even update the current distance
#define kRequiredHorizontalAccuracy 40.0f // the required accuracy in meters for a location.  anything above this number will be discarded

- (id)init {
    if ((self = [super init])) {
        if ([CLLocationManager locationServicesEnabled]) {
            self.locationManager = [[CLLocationManager alloc] init];
            self.locationManager.delegate = self;
            self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
            self.locationManager.distanceFilter = 5; // specified in meters
        }

        self.locationHistory = [NSMutableArray arrayWithCapacity:kNumLocationHistoriesToKeep];
    }

    return self;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    // since the oldLocation might be from some previous use of core location, we need to make sure we're getting data from this run
    if (oldLocation == nil) return;
    BOOL isStaleLocation = [oldLocation.timestamp compare:self.startTimestamp] == NSOrderedAscending;

    [self.delegate locationManagerDebugText:[NSString stringWithFormat:@"accuracy: %.2f", newLocation.horizontalAccuracy]];

    if (!isStaleLocation && newLocation.horizontalAccuracy >= 0.0f && newLocation.horizontalAccuracy < kRequiredHorizontalAccuracy) {

        [self.locationHistory addObject:newLocation];
        if ([self.locationHistory count] > kNumLocationHistoriesToKeep) {
            [self.locationHistory removeObjectAtIndex:0];
        }

        BOOL canUpdateDistance = NO;
        if ([self.locationHistory count] >= kMinLocationsNeededToUpdateDistance) {
            canUpdateDistance = YES;
        }

        if ([NSDate timeIntervalSinceReferenceDate] - self.lastDistanceCalculation > kDistanceCalculationInterval) {
            self.lastDistanceCalculation = [NSDate timeIntervalSinceReferenceDate];

            CLLocation *lastLocation = (self.lastRecordedLocation != nil) ? self.lastRecordedLocation : oldLocation;

            CLLocation *bestLocation = nil;
            CGFloat bestAccuracy = kRequiredHorizontalAccuracy;
            for (CLLocation *location in self.locationHistory) {
                if ([NSDate timeIntervalSinceReferenceDate] - [location.timestamp timeIntervalSinceReferenceDate] <= kValidLocationHistoryDeltaInterval) {
                    if (location.horizontalAccuracy < bestAccuracy && location != lastLocation) {
                        bestAccuracy = location.horizontalAccuracy;
                        bestLocation = location;
                    }
                }
            }
            if (bestLocation == nil) bestLocation = newLocation;

            CLLocationDistance distance = [bestLocation distanceFromLocation:lastLocation];
            if (canUpdateDistance) self.totalDistance += distance;
            self.lastRecordedLocation = bestLocation;
        }
    }
}

Ответы

Ответ 1

Как оказалось, код, который я написал выше, отлично работает. Проблема оказалась в другой части моего приложения. Я случайно менял расстояние от метров до миль, а не от метров до километров. К сожалению,

В любом случае, надеюсь, мой пост по-прежнему будет иметь некоторые достоинства, так как я считаю это довольно солидным примером того, как отслеживать расстояние пользователя с Core Location.

Ответ 2

Вероятно, вы слишком низко задали kRequiredHorizontalAccuracy. Если в истории нет местоположения, которое имеет точность < kRequiredHorizontalAccuracy, тогда вы игнорируете все эти точки и добавляете 0 к расстоянию.