Reputation: 583
So, I've got a navigation app. It uses the onboard GPS (and magnetic compass and accelerometer) to construct a 3D Augmented Reality viewport similar to how Google Sky works. We're not using ARKit for a variety of reasons including that this device will be used in a moving vehicle and we're concerned that ARKit would get distracted by the apparently non-moving vehicle interior instead of paying attention to the invisibly-moving exterior reference frame outside the vehicle, which is the reference frame we want our AR navigation display to be aligned to.
The AR orientation part is not my main problem (though I am finding that the magcompass plus accelerometers really are fairly prone to error). My vexing issue is that no matter how hard I try, some iOS devices (mostly iPads) don't seem to be using the actual GPS hardware to determine the Core Location data. It seems to be using Wifi trilateration and therefore providing inferior position data (lacking Altitude) and therefore inferior velocity data calculated from the position data.
Here's some facts.
I initialize the Location services like this:
_locationManager = [[CLLocationManager alloc]init];
[_locationManager requestAlwaysAuthorization];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[_locationManager startUpdatingLocation];
On some devices, we definitely get a full 3D GPS location, including altitude.
However, certain test devices ("APPLE MPMF2LL/A iPad Pro with Wi-Fi + Cellular 512GB, 10.5", Silver", "Apple iPad Air 2 MH2V2LL/A (16GB, Wi-Fi + Cellular, Silver) 2014 Model", both with cellular capability and OS 11.2.6), we get Location data that reports a <0 value for Vertical Accuracy, meaning it's not valid, and the Lat/Lon Location value behaves like it's a Wifi trilateration position not a real GPS.
We've tried airplane mode on and off, made sure location services is enabled and permission granted to our app. We tried setting the Location permission to "Always". We tried factory resetting and restoring.
Simple compass apps from other app developers seem to work fine and show a correct altitude and Vertical Accuracy on these devices. Our app seems to work fine on other devices (iPhones of various flavors, full-size 5th gen iPad) and get full GPS Locations. So, it doesn't seem to be a hardware failure of the test devices, yet nor does it seem to be a complete failure of our code.
Is there some magic we aren't requesting or performing properly to ensure we're getting full GPS-sourced location data?
There is already a fallback in the code for devices or circumstances where we can't get GPS Altitude. But if the device HAS the capability, we want to make sure we're using it to the best ability. We also are working on the ability to use external GPSes, but it seems we ought to be able to use what the iOS devices definitely has if we ask for it.
Grateful for any insight. Doesn't seem like it should be this hard.
Upvotes: 0
Views: 162
Reputation: 583
So, after much pain and suffering, we identified the culprit, but there's still an unknown that is unsolved.
I'll start with the diff of the fix, because reference code trumps all, and then I'll explain what we believe was going wrong.
- CLGeocoder *geocoder = [[CLGeocoder alloc] init] ;
- [geocoder reverseGeocodeLocation:_currentLocation completionHandler:^(NSArray *placemarks, NSError *error)
+ if(_udpSocket == nil || timesincelastnetwork > _waitSecondsWithNoNetwork)
{
- if (!(error))
- {
- CLPlacemark *placemark = [placemarks objectAtIndex:0];
- // NSLog(@"\nCurrent Location Detected\n");
- //NSLog(@"placemark %@",placemark);
- NSString *locatedAt = [[placemark.addressDictionary valueForKey:@"FormattedAddressLines"] componentsJoinedByString:@", "];
- NSString *Address = [[NSString alloc]initWithString:locatedAt];
- NSString *Area = [[NSString alloc]initWithString:placemark.locality];
- NSString *Country = [[NSString alloc]initWithString:placemark.country];
- NSString *CountryArea = [NSString stringWithFormat:@"%@, %@", Area,Country];
- //NSLog(@"%@",CountryArea);
-
-
- CLLocation* l = placemark.location;
- CLLocationCoordinate2D coords = l.coordinate;
+ _currentLocation = [locations objectAtIndex:0];
+ //[_locationManager stopUpdatingLocation];
-
- float lati = coords.latitude; //39.8560963f; //
- float longi = coords.longitude; //-104.67373759999998f; //
-
- float alti = l.altitude;
So, what appears to have happened is that during the geocoding process, on SOME devices, the altitude was being lost but on some devices it was being preserved. Since we didn't really need the geocoding (we had left it in from the example code in case it would be useful), we just took it out and use the lat/lon/alt directly from the location service and it works properly on all devices now.
I'd consider it a bug or at least an undocumented condition that the geocoder is wiping out the altitude value in some unclear circumstances but not others, but fortunately, it's a bug we are able to work around.
Upvotes: 0