why getSpeed() always return 0 on android
I need to get the speed and heading from the gps. However the only number i have from location.getSpeed()
is 0 or sometimes not available. my code:
String provider = initLocManager();
if (provider == null)
return false;
LocationListener locListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateWithNewLocation(location, interval, startId);
Log.i(getString(R.string.logging_tag), "speed =" + location.getSpeed());
}
public void onProviderDisabled(String provider){
updateWithNewLocation(null, interval, startId);
}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
_locManager.requestLocationUpdates(provider, interval, DEFAULT_GPS_MIN_DISTANCE, locListener)开发者_运维知识库;
private String initLocManager() {
String context = Context.LOCATION_SERVICE;
_locManager = (LocationManager) getSystemService(context);
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(true);
criteria.setSpeedRequired(true);
criteria.setCostAllowed(true);
//criteria.setPowerRequirement(Criteria.POWER_LOW);
String provider = _locManager.getBestProvider(criteria, true);
if (provider == null || provider.equals("")) {
displayGPSNotEnabledWarning(this);
return null;
}
return provider;
}
I tried to play the Criteria with but no success. Does anyone have an idea what is the problem?
location.getSpeed() only returns what was set with location.setSpeed(). This is a value that you can set for a location object.
To calculate the speed using GPS, you'll have to do a little math:
Speed = distance / time
So you would need to do:
(currentGPSPoint - lastGPSPoint) / (time between GPS points)
All converted to ft/sec, or however you want to show the speed. This is how I did it when I made a runner app.
More specifically, you'll need to calculate for absolute distances:
(sqrt((currentGPSPointX - lastGPSPointX)^2) + (currentGPSPointY - lastGPSPointY)^2)) / (time between GPS points)
It might help to make a new TrackPoint class or something, which keeps the GPS location and time it was taken inside.
Imbru's answer looks really good, but it is not very helpful if you are working with units.
Here's what I did to calculate the speed in meters per second (m/s).
object : LocationListener() {
var previousLocation: Location? = null
override fun onLocationChanged(location: Location) {
val speed = if (location.hasSpeed()) {
location.speed
} else {
previousLocation?.let { lastLocation ->
// Convert milliseconds to seconds
val elapsedTimeInSeconds = (location.time - lastLocation.time) / 1_000.
val distanceInMeters = lastLocation.distanceTo(location)
// Speed in m/s
distanceInMeters / elapsedTimeInSeconds
} ?: 0.0
}
previousLocation = location
/* There you have it, a speed value in m/s */
functionThatUsesSpeedInMeterPerSecond(speed)
. . .
}
. . .
}
There is my custom LocationListener used to get speed manually and by location object if has speed.
new LocationListener() {
private Location mLastLocation;
@Override
public void onLocationChanged(Location pCurrentLocation) {
//calcul manually speed
double speed = 0;
if (this.mLastLocation != null)
speed = Math.sqrt(
Math.pow(pCurrentLocation.getLongitude() - mLastLocation.getLongitude(), 2)
+ Math.pow(pCurrentLocation.getLatitude() - mLastLocation.getLatitude(), 2)
) / (pCurrentLocation.getTime() - this.mLastLocation.getTime());
//if there is speed from location
if (pCurrentLocation.hasSpeed())
//get location speed
speed = pCurrentLocation.getSpeed();
this.mLastLocation = pCurrentLocation;
////////////
//DO WHAT YOU WANT WITH speed VARIABLE
////////////
}
@Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
@Override
public void onProviderEnabled(String s) {
}
@Override
public void onProviderDisabled(String s) {
}
};
On a spherical planet distance should be calculated with these formulas :
private static Double distance(Location one, Location two) {
int R = 6371000;
Double dLat = toRad(two.getLatitude() - one.getLatitude());
Double dLon = toRad(two.getLongitude() - one.getLongitude());
Double lat1 = toRad(one.getLatitude());
Double lat2 = toRad(two.getLatitude());
Double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
Double d = R * c;
return d;
}
private static double toRad(Double d) {
return d * Math.PI / 180;
}
(1) I believe you can use the requestLocationUpdates()
method and then create a LocationListener class with an onLocationChange method set to display getSpeed()
. This is how i recently saw it done with Location.getLatitude and Location.getLongitude, so I believe you could just use getSpeed() the same way, correct?
(2) After just reading the eclipse description window, though, I see it says exactly what the previous person said: "if hasSpeed() is false, 0.0f is returned." But maybe this will help: http://www.ehow.com/how_5708473_convert-latitude-feet.html :)
getSpeed() method actually works fine but you have to use a high request interval like 1 second and you need high accuracy, first i was doing 3 second interval and PRIORITY_BALANCED_POWER_ACCURACY and i kept getting 0 values until i change it like i say. I am using fused location provider api.
public class Main3Activity extends AppCompatActivity {
private FusedLocationProviderClient mFusedLocationClient;
private int speed;
private double lat;
private double lng;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
}
@Override
protected void onResume() {
super.onResume();
if(!runtime_permissions()) {
requestLocations();
}
}
@Override
protected void onPause() {
super.onPause();
//stop location updates when Activity is no longer active
if (mFusedLocationClient != null) {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
}
}
@SuppressLint("MissingPermission")
private void requestLocations(){
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);;
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
}
LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
List<Location> locationList = locationResult.getLocations();
if (locationList.size() > 0) {
//The last location in the list is the newest
Location location = locationList.get(locationList.size() - 1);
lat = location.getLatitude();
lng = location.getLongitude();
//speed in km/h
speed = (int) ((location.getSpeed() * 3600) / 1000);
}
}
};
private boolean runtime_permissions() {
if(Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);
return true;
}
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == 100){
if( grantResults[0] == PackageManager.PERMISSION_GRANTED){
onResume();
}else{
runtime_permissions();
}
}
}
}
Hey I too was suffering from the same but now I have got it solved ! Just multiply the value by 18/5 , it gives almost the accurate value.
speed=location.getSpeed()*18/5
Also specify the interval
as 1000*2
and fastest interval
as 1000*1
for more accuracy
getspeed() works fine. You don't have to do the math using distance formula. It is already there in getspeed, As long as there are latitude and longitude, there will be a speed in getspeed.
I also encountered this problem before, I hope this can help.
It returns 0 because your device cannot get a lock on the GPS, or cannot connect to the GPS.
I tried to get the speed using an older lenovo device and it returns 0 because it cannot lock on a gps.
I tried using a samsung galaxy nexus and it returned my speed(has a better GPS sensor).
The GPS sensor in your phone might not be good or you are in an area that has a weak GPS signal such as inside a house or building.
I basicaslly calculate the instantaneous speed and then use the setSpeed() method to add it in the location. Its pretty accurate because I compared it inside a vehicle where I could check the tachymeter.
private double calculateInstantaneousSpeed(Location location) {
double insSpeed = 0;
if (y1 == null && x1 <= -1) {
//mark the location y1 at time x1
y1 = location;
x1 = duration.getDurationAsSeconds();
} else {
//mark the location y2 at time x2
y2 = location;
x2 = duration.getDurationAsSeconds();
//calculate the slope of the curve (instantaneous speed)
dy = y1.distanceTo(y2);
dx = x2 - x1;
insSpeed = dy / dx;
y1 = y2;
x1 = x2;
}
Singleton.getInstance().instantaneousSpeedSamples.add(insSpeed);
//System.out.println("Instantaneous Speed m/s: "+insSpeed);
return insSpeed;
}
Based on other suggestions here I put together this code with comments to why:
public static float getSpeedKmh(Location newLocation, Location previousLocation)
{
if(newLocation == null) throw new IllegalArgumentException("newLocation must not be null");
float speed = -1.0f;
if(newLocation.hasSpeed()) /* gps doppler speed at the current moment preferred */
{
speed = newLocation.getSpeed();
}else /* may be wifi or celltower based location: compute AVERAGE speed since last fix */
{
if(previousLocation == null) throw new IllegalArgumentException("Cannot compute speed without previousLocation (was null)");
if(previousLocation.getTime()==newLocation.getTime())
throw new IllegalArgumentException("Cannot compute speed from same time locations!"); /* diff by zero protection */
speed = newLocation.distanceTo(previousLocation) * 1000.0f /* time diff in millis so multiply by 1000 */
/ Math.abs(newLocation.getTime() - previousLocation.getTime()); /* Math abs: so you can even swap new and older location without effect */
}
return speed * 3.6f; /* m/s -> km/h */
}
精彩评论