Improve GPS frequency and accuracy - android

I am exploring the possibilities of using the GPS for VR gaming. Both frequency and accuracy are very low with my current test build.
In manifest I have
<uses-permission android:name="android.permission.LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
And I instance location manager like
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
0, 0, this);
I then use the distanceBetween function to get the bearing and distance
public void onLocationChanged(Location location) {
if(lastLocation != null) {
Location.distanceBetween(lastLocation.getLatitude(), lastLocation.getLongitude(), location.getLatitude(), location.getLongitude(), distanceBetween);
newGpsDataToSend = true;
if(debug)
debugListener.debugGPS(distanceBetween);
releaseSendThread();
}
lastLocation = location;
}
The frequency is below 1hz (5-10hz should be possible right?) and the bearing and distance is very jumpy.
Am I doing something wrong in the code? I do not care about battery consumption etc, I jsut want the fastest most reliably data I can get from the phone.
Complete source
https://github.com/AndersMalmgren/FreePIE/blob/VRWalk/Lib/Android/FreePIE%20Android%20IMU/src/com/freepie/android/imu/UdpSenderTask.java#L120

For hight sampling frequencies (counting 1 Hz as high for GPS sampling) you will get GPS readings where the distances between locations is less than the GPS accurracy. Therefore bearing an and distance are jumpy as observed.
To get a better bearing lower the sampling frequency or process the data by filtering or computing an average.
E.g., for a flight logging application I am sampling about every 2 to 5 seconds and I am computing the average of the last 5 altitude readings to compute vertical speed.

Related

Fused Location API gives Inaccurate Lat, Long when plotting in map it is slightly away from the road path even for low accuracy value

Google provides the fused location provider API to obtain location co-ordinates. According to the documentation, the API internally polls location data from different providers (GPS, Wifi, Cellular networks) and provides the best location. But, in High Accuracy mode, I have collected the below information. In this test GPS is always ON.
Latitude: 12.8560136
Longitude: 80.1997696
User Activity: IN VEHICLE
Speed: 21.810165 mph
Altitude: -83.0
Accuracy: 12.0
When I see this point in map, the points are not in the road. They are slightly away from the road.
Other points with the same accuracy are plotted in the road.When I full zoom and see, some of the points are slightly away from the traveled road path.
I want the accurate information. It points must be in the road path.
I have used the Fused Location API to get the location information.
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
Share your suggestion. If I use the Location manager will it solve my problem.
And also I need to consume less battery only. Fused API guarantees that it consumes only less power and more efficient.
And also Fused Location API has below issues,
Not able to get the satellite count.
Is always returning the FUSED provider. Not exactly gives which provider(GPS or Network) returns the Location information.
Your never notified when both the provider is unavailable . It will not return any value when none of the provider is available. To check the provider availability we need to separately register with Location manager. which consumes more power.
Please help me on this. Thanks in advance.
You won't beat the fused provider accuracy or power use with a straight up location provider.
The fused location provider reports speed as meters per second and accuracy in meters. The example point you have is accurate to 12 meters. A US lane is defined as 3.7m. With 12m of accuracy you could be off by 12/3.7=3.25 lanes in any direction. I can't say this enough. If you want accuracy you have to check the accuracy of the location.
How to get more accuracy by GPS_PROVIDER
LatLng and distance between returning incorrect values on newer phones
can't find the exact current location in android
Android Maps GPS outlier
Fused Location Provider unexpected behavior
Adjust your program to handle the accuracy of the point. For example assume the point is correct shade a circle around the point with a radius = the accuracy in the location.
Location data from GPS and other providers cannot be guaranteed to lie on the road. These sensors do not have the requisite mapping context to snap raw location signals to roads. To achieve this with raw data, you can use a snap to roads API. Mapping providers like Google Maps and OSM have ways to do this:
Google Maps snap to roads API
OSRM match service
If you are looking for an end-to-end solution that gives you location data on the road, you can also try the HyperTrack SDK for Android, which collects and processes location data to improve its accuracy. (Disclaimer: I work at HyperTrack.)
Also, to answer your other question, FusedLocationProviderApi does not expose the satellite count and provider information. To get the provider info, you can set up a GpsStatus.Listener class. This can be used in parallel to the fused provider in your app to check if the device has a GPS fix. You can use the following code snippet to set this up:
public class GPSStatusListener implements GpsStatus.Listener {
private LocationManager locationManager;
private boolean gpsFix;
public GPSStatusListener(LocationManager locationManager) {
this.locationManager = locationManager;
}
#Override
public void onGpsStatusChanged(int changeType) {
if (locationManager != null) {
try {
GpsStatus status = locationManager.getGpsStatus(null);
switch (changeType) {
case GpsStatus.GPS_EVENT_FIRST_FIX: // Received first fix
gpsFix = true;
break;
case GpsStatus.GPS_EVENT_SATELLITE_STATUS: // Check if satellites are in fix
for (GpsSatellite sat : status.getSatellites()) {
if (sat.usedInFix()) {
gpsFix = true;
break;
} else {
gpsFix = false;
}
}
break;
case GpsStatus.GPS_EVENT_STARTED: // GPS turned on
gpsFix = false;
break;
case GpsStatus.GPS_EVENT_STOPPED: // GPS turned off
gpsFix = false;
break;
default:
gpsFix = false;
return;
}
} catch (Exception e){
// Handle exception
}
}
}
public String getProvider() {
if (gpsFix) {
return "gps";
} else {
return "non_gps";
}
}
}

Able to fetch current location but its not accurate

This question may a repetition but i am not satisfied with others ,that is why asking again .i have created a simple app to show current location and displayed it on map.But its not accurate.I tested my app within a building and is fetching the nearby road as my current location,But other apps like Myteksi,Grab teksi is showing my company name as current location and its accurate.i dont know why its so.Please help.Code for fetching current location is giving below
protected void gotoCurrentLocation() {
Location currentLocation = mLocationClient.getLastLocation();
if (currentLocation == null) {
Log.d("currentLocation-->>>", "null");
Toast.makeText(this, "Current location isn't available",
Toast.LENGTH_SHORT).show();
} else {
LatLng ll = new LatLng(currentLocation.getLatitude(),
currentLocation.getLongitude());
Log.d("lattitude", currentLocation.getLatitude()+"");
Log.d("longitude", currentLocation.getLongitude()+"");
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(ll,
DEFAULTZOOM);
map.animateCamera(update);
/*String address= service.GetAddress(currentLocation.getLatitude(),
currentLocation.getLongitude());
Log.d("address", address);*/
}
}
please comment if any other codes are needed.
As you said you test the app in indoor location. And you know in indoor locations GPS sensor will not work, According to google's docs:
Although GPS is most accurate, it only works outdoors.
So your location might come from Network Provider using wi-fi or cell-id, which is not enough accurate.
Android's Network Location Provider determines user location using cell tower and Wi-Fi signals, providing location information in a way that works indoors and outdoors.
and you must be aware of that:(when using getLastLocation() )
To get the current location, create a location client, connect it to Location Services, and then call its getLastLocation() method. The return value is the best, most recent location, based on the permissions your app requested and the currently-enabled location sensors.
BUT:
The current location is only maintained while a location client is connected to Location Service. Assuming that no other apps are connected to Location Services, if you disconnect the client and then sometime later call getLastLocation(), the result may be out of date.
and also please take a look at this to learn more about Maintaining a current best estimate:
http://developer.android.com/guide/topics/location/strategies.html#BestEstimate
I hope this information helps. ;)
This is because fetching location is a very complicated task to do. And the native implantation of LocationClient might not be the most accurate.
Consider using one or all the 13 sensors(Table 1) in your device to improve that.
TYPE_ACCELEROMETER Hardware Measures the acceleration force in m/s2 that is applied to a device on all three physical axes (x, y, and z), including the force of gravity. Motion detection (shake, tilt, etc.).
TYPE_AMBIENT_TEMPERATURE Hardware Measures the ambient room temperature in degrees Celsius (°C). See note below. Monitoring air temperatures.
TYPE_GRAVITY Software or Hardware Measures the force of gravity in m/s2 that is applied to a device on all three physical axes (x, y, z). Motion detection (shake, tilt, etc.).
TYPE_GYROSCOPE Hardware Measures a device's rate of rotation in rad/s around each of the three physical axes (x, y, and z). Rotation detection (spin, turn, etc.).
TYPE_LIGHT Hardware Measures the ambient light level (illumination) in lx. Controlling screen brightness.
TYPE_LINEAR_ACCELERATION Software or Hardware Measures the acceleration force in m/s2 that is applied to a device on all three physical axes (x, y, and z), excluding the force of gravity. Monitoring acceleration along a single axis.
TYPE_MAGNETIC_FIELD Hardware Measures the ambient geomagnetic field for all three physical axes (x, y, z) in μT. Creating a compass.
TYPE_ORIENTATION Software Measures degrees of rotation that a device makes around all three physical axes (x, y, z). As of API level 3 you can obtain the inclination matrix and rotation matrix for a device by using the gravity sensor and the geomagnetic field sensor in conjunction with the getRotationMatrix() method. Determining device position.
TYPE_PRESSURE Hardware Measures the ambient air pressure in hPa or mbar. Monitoring air pressure changes.
TYPE_PROXIMITY Hardware Measures the proximity of an object in cm relative to the view screen of a device. This sensor is typically used to determine whether a handset is being held up to a person's ear. Phone position during a call.
TYPE_RELATIVE_HUMIDITY Hardware Measures the relative ambient humidity in percent (%). Monitoring dewpoint, absolute, and relative humidity.
TYPE_ROTATION_VECTOR Software or Hardware Measures the orientation of a device by providing the three elements of the device's rotation vector. Motion detection and rotation detection.
TYPE_TEMPERATURE Hardware Measures the temperature of the device in degrees Celsius (°C). This sensor implementation varies across devices and this sensor was replaced with the
TYPE_AMBIENT_TEMPERATURE sensor in API Level 14 Monitoring temperatures
You might be getting stale location data, as you're using LocationClient.getLastKnownLocation(), which returns the last cached location, not the current one. You could try requesting your own location updates using a LocationListener. To do this, you need to create your own LocationListener and override the default onLocationChanged(Location) behavior. For example:
final LocationListener ll = new LocationListener(){
int i=0;
#Override
public void onLocationChanged(Location loc) {
if(i < 5 && loc.getAccuracy() > 8){
i ++;
return;
}
double lat = loc.getLatitude();
double lon = loc.getLongitude();
double acc = loc.getAccuracy();
}
};
You then need to register the listener using an instance of LocationManager:
final LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, ll);
This example will register the listener for whenever the GPS location changes. The actual listener code above waits for at least 5 location changes to help ensure that the final location is accurate (the longer time waited, the more time it will have had to lock-on to satellites), then gets the latitude, longitude, and accuracy from the Location object. There are many other ways that you can setup your listener, but that's the way that I did it in one of my apps. If you Google something like "android location updates", you should find some other good tutorials for it. Good luck!

Location accuracy remains low using GPS

I have the following code in main activity:
LocationManager mlocMan = (LocationManager)getActivity().getSystemService(Context.LOCATION_SERVICE);
if (mlocMan.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
LocationListener mlocListener = new LocationManagerHelper(...);
mlocMan.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0,mlocListener);
}
In the location listener I have this: (theAccuracy was initialized to -1)
if (theAccuracy == -1 || theAccuracy > loc.getAccuracy()) {
theAccuracy = Math.round(loc.getAccuracy());
latitude = loc.getLatitude();
longitude = loc.getLongitude();
}
updateTimes++;
if (updateTimes == 3) {
mLocMan.removeUpdates(this);
updateTimes = 0;
//get address for location
theAccuracy = -1;
}
Meaning, after 3 location updates from the GPS, take the best accurate location and get its address. On the emulator I get fixed accuracy of 20m (I send long/lat using DDMS) but that's not real life so I tried with my device and while the very first time (3 requests) gave me the exact address (on the spot) with 40m accuracy, the next ones were sometimes more accurate but the address was nearby. The best accuracy I got was 29m (happened once) most of the times it's above 30. Is this a problem of my GPS (LG G3) or is there any other idea that can make things more accurate after 3-4-5 requests?
A few reasons:
Your emulator doesn't have actual gps hardware, so it's probably using your ip address, so that's why it seems fixed.
GPS hardware on your phone has to warm up a bit. Set the frequency of polling up for a bit (1 update a second, or half-second), and let it run for a few seconds, before taking measurements that count.
The location manager supports both hardware and network lookups (wifi / celltowers). Not sure how to set provider to just your phone's hardware, but if you use the googlePlayServices gps client (LocationClient), it's quite simple:
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationClient.requestLocationUpdates(mLocationRequest, (LocationListener) this);
You're probably testing at your computer in doors. Buildings definitely effect gps accuracy.

onlocationChanged is called even if I am sitting at the same place

I have used the code below and everything is working fine except that onLocationChanged is called even if I am sitting at the same location .
I thought it should be called only when I am moving right ?
I only want to get the location after I have moved a certain distance.
Please help me out.
Thanks in advance.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
locationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationMgr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0,
this);
}
#Override
public void onLocationChanged(Location location) {
Toast.makeText(this, "Working!", Toast.LENGTH_SHORT).show();
if (location != null) {
double lat = location.getLatitude();
double lng = location.getLongitude();
String Text = "Latitud = " + lat + "\nLongitud = " +
lng;
Toast.makeText(getBaseContext(),Text,Toast.LENGTH_SHORT).show();
}
}
You're requesting location updates at the shortest possible intervals/distances
locationMgr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0,
this);
This is what the documentation says about those parameters
" The location update interval can be controlled using the minTime parameter. The elapsed time between location updates will never be less than minTime, although it can be more depending on the Location Provider implementation and the update interval requested by other applications. "
The minDistance parameter can also be used to control the frequency of location updates. If it is greater than 0 then the location provider will only send your application an update when the location has changed by at least minDistance meters, AND at least minTime milliseconds have passed. However it is more difficult for location providers to save power using the minDistance parameter, so minTime should be the primary tool to conserving battery life.
I personally use a minTime of 10 seconds and 10 meters for my app
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000,
10, locationListener);
Network location is not as precise as you would think it is. Therefore the result returned by the sensors can fluctuate. This is even more true when you consider that GPS signal weakens if you don't have direct line of sight with the satellites, and the number of visible satellites also has effect on the precision. This gets even worse when you start using the network provider, where the position is calculated by triangulation of the signal strength of cell towers, and the number and SSIDs of visible wireless network. Since these can fluctuate quite a lot, the precision suffers greatly. There are bunch of averaging algorithms, and heuristics employed to minimize such fluctuations, but ultimately nothing can stabilize it to be as good as you expect it to be.
A simple averaging and variation filtering can help you. Adding a correction based on the device accelerometer can also help a lot, but it will make your code more complex.

Is Android's GPS altitude incorrect due to not including geoid height?

I'm testing an Android app that records the location (lat/long/alt). I'm running the app on a Samsung GTS5830 phone running Android 2.2.1
I read here and there that GPS altitude is often incorrect due to the earth not being perfectly spherical. At my location, for example, the geoid's height is 52 meters.
My understanding is that this height would be substracted from a "pure" GPS altitude. This would make sense for my location as:
- altitude from GPS phone: 535 m
- geoid altitude: 52 m
- altitude from phone's GPS minus geoid height: 482m
- correct atlitude: 478 m
482 is close enough to the real thing for me to track elevation when hiking
Is the above formula of the GPS height minus the geoid's height correct?
Am I correct to assume that android is not factoring in the geoid's height when returning the GPS altitude?
If the above is true, does it hold for all versions of Android?
Here is the code I use to obtain the GPS coordinates:
public class HelloAndroid extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d("main", "onCreate");
setupGps();
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
LocationListener locationListener;
LocationManager lm;
void setupGps() {
Log.d("gps", "Setting up GPS...");
locationListener = new MyLocationListener();
lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 20000, 5,
locationListener);
Log.d("gps",
"GPS supports altitude: "
+ lm.getProvider(LocationManager.GPS_PROVIDER)
.supportsAltitude());
Log.d("gps", "Finished setting up GPS.");
}
static class MyLocationListener implements LocationListener {
public void onLocationChanged(Location location) {
Log.d("gps", "long: " + location.getLongitude() + ", lat: "
+ location.getLatitude() + ", alt: "
+ location.getAltitude());
}
}
}
The answers to all three of your questions are yes.
The altitude you get from GPS is the height above the WGS84 ellipsoid in metres, which is an approximation of the earth's surface. I know that because I've been developing Android software to use it.
A correction has to be applied to convert the figure to height above mean sea level, or altitude as it is usually known. Note that this may differ from the altitude reported by an altimeter set to the current pressure at mean sea level because an altimeter actually measures air pressure, but air pressure is not just a function of height; it is also a function of air density and temperature, so an altimeter shows an approximation. This effect is not seen with GPS.
The Earth Gravitational Model 2008 (EGM2008) maps the difference in mean sea level with the ellipsoid as a function of longitude and latitude. Details are here:
http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008/egm08_wgs84.html
A less accurate model that uses less data called EGM96 is also available. Google both of these to understand these better.
It's not quite that simple -- the Android API has either changed or has bugs. I have two Android devices -- a 'generic' phone (Android 2.3.6) and a Nexus 7 (Android 4.x).
On the phone, getAltitude() gives an answer consistent with my actual altitude AMSL (i.e., corrected for geoid). On the Nexus 7, the altitude returned is uncorrected. The documentation for the API does not specify which is returned -- so in some sense both are 'correct'.
Both devices seem to have decent GPS modules -- the $GPGGA NMEA message shows both the correct altitute and the geoid correction. So it looks as though one has to parse the messages oneself to get the correct altitude, and the getAltitude() method is untrustworthy.
Errors in the altitude may well not be down to the GPS and geoid altitude separation.
The altitude value returned by any GPS receiver is always the least accurate value. I have often seen my office building report that it is moving in altitude between -200 and +750 metres. One fundamental reason is that it is impossible to get an even spread of satellites in the altitude direction; they are always going to be above you, and if there is any obscuration of the sky at low elevations, they will be located in a cone above you. In the X and Y directions there will always, assuming a good sky view, be satellites spread left and right; in front and behind the receiver. This spread enhances the accuracy of the position solution.

Categories

Resources