I was wondering for one of the apps I'm writing does the Location.getSpeed() account for Altitude change? i.e. if I am going down a slope does it return a speed as if I'm going straight or does it actual do the calculation based on the triangle formed due to the downward slope?
The Location class does NOT consider altitude when giving you the speed.
The source for getSpeed is
public float getSpeed() {
return mSpeed;
}
And mSpeed assignment is mSpeed = (Parcel)in.readFloat();. Ultimately, if I'm not mistaken, it looks like the Parcel comes from ILocationManager.aidl which is private so I don't know, but I would guess it pretends you're not on a slope when it decides what the speed is.
And a lot less fun, but the docs say about getSpeed():
Get the speed if it is available, in meters/second over ground
which sounds like it does not consider altitude changes.
If you really want to know, you could somehow test it or you could file a bug regarding the getSpeed() documentation.
Related
I wanted to use Location.distanceTo. The idea is that if the user hasn't moved, then don't make the next function call until they do:
if (oldLocation!!.distanceTo(location) == 0f) {
continue
}
Per the docs distanceTo:
Returns the approximate distance in meters between this location and
the given location. (Source)
The tutorial uses feet. So, I thought, there must be a bunch of code someplace that does the conversion and, in this case, is totally unnecessary and inefficient. Right?
Apparently not. This causes an error:
if (oldLocation!!.distanceTo(location) == 0m) {
continue
}
So then I just put 0 in without saying what the measurement is and expected distanceTo to process that 0 in meters, which is what the docs say, but it doesn't work either.
if (oldLocation!!.distanceTo(location) == 0) {
continue
}
So here is the end question:
It were not for the tutorial doing this in feet, I would never have figured it out by reading the docs. Never. I mean, when it expressly says meters, how are we supposed to know that the solution to making it work is to use feet?
Did I miss something?
0f means a float value of 0. 0m is invalid syntax. You can see in the docs distanceTo returns type float.
I am writing an app which uses the onLocationChanged(Location location) callback along with location.getSpeed() to get the speed at which the user is traveling. I am curious as to what actually occurs when getSpeed() is called. I note that location is just a parameter fed into the callback by Android, which leads me to wonder:
is getSpeed() simply pulling an already-calculated field from this object, or does calling getSpeed() calculate the value in some other way?
I'm also curious many times what is in the source code or how is it called. I tried to find the code of android.Location class and it seems I'm succesfull.
Try to check out this page: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/location/java/android/location/Location.java
The method 'getSpeed' is at the row number 627, but it tells only 'return mSpeed;', so you have to look to the other parts of the class
Android's getSpeed() will just return the value that was set in setSpeed().
The best way to get speed is to use simple physics:
Speed = Distance/Time
The thing about GPS' is that there is a lot of variability and thus accuracy isn't always the best in terms of speed. You should use a filter to help smooth the data (The Kalman Filter is pretty popular for navigation data).
Good luck!
I am making a compass for android and I have it working but it has one flaw. The reading on the compass is consistently changing by a couple of degrees even when the device is not moving. I know that this is due to the magnetic distortion messing with sensor but how can I make my app still responsive while the user moves yet not have so much variation displayed when the user is still. So how can I give the appearance of a more steady compass?
You have to smooth the signal. How about displaying the average of the last N values instead of displaying the raw last value ?
Have you tried implementing a low pass filter? http://blog.thomnichols.org/2011/08/smoothing-sensor-data-with-a-low-pass-filter
If you want to write a compass, you'll need to low-pass filter both acceleration and magnetic field.
Here's an example of a low-pass filter. Not very elegant, but just to show the principle:
float filteredValue;
void lowPassFilter(float rawValue) {
final float alpha = 0.8f;
float filteredValue = alpha * filteredValue + (1.0f - alpha) * rawValue;
return filteredValue;
}
Note that filteredValue is remembered after each call (like a member variable).
On the other hand, rawValue is the value from the sensor event, like event.values[0].
Notice how every 'fresh' new rawValue only affects 20% of the filtered value (that's given by alpha). This brings 'stability' to the reading, at the cost of a delay.
I wish to get the acceleration vector of an Android phone. The problem is, the accelerometer coordinates are relative to the phone's rotation. What I want is the "absolute" acceleration, i.e., it should return the same values whichever way the phone is facing. (I want to detect if a user that is skiing is sliding down a slope without using GPS. I also need to be able to differentiate sliding and going up the chairlift.)
I can probably get those values by combining the accelerometer with the gyroscope, but I have no idea how I could offset the accelerometer's values with the gyroscope's.
Is this possible, and if so, how?
What you describe can't be done, unless you redefine the problem a bit. To help you redefine it, I'll outline the main issues:
First, I'm guessing that what you mean by "absolute acceleration" is acceleration with respect to geographical reference. The can't be done with the accelerometer alone, since it has no idea about geographical references. If you move far enough for the gps, or use the compass, you might be able to get around this, but each of these has its own issues (though at least the problem is soluble).
The second issue is that gravity and acceleration are completely indistinguishable using an accelerometer alone (this is known as the "equivalence principle"). Therefore, any measured acceleration will always be the vector sum of gravity and the acceleration, but there are always multiple solutions to these equations, and in the usual cases where the acceleration is smaller than gravity, you really can't determine anything about the acceleration. Since gravity is somewhat constant though, there are ways around this too, using, say, a gyroscope, or maybe your user could hold the phone in a fixed orientation (e.g., by looking at external cues like the horizon), and either of these approaches might let you subtract the influence of gravity, but it's generally a non-trivial problem.
The final point to not is that you seem to be thinking in an earth-fixed coordinate system and the phone's accelerometer is only phone-fixed. That is the accelerometer's z-axis many not have anything to do with up and down on the earth -- and the relationship will depend on the orientation of the phone. Really, many people would prefer an earth-fixed system, but the phone just doesn't know that. You can use external cues (GPS, magnetic field, gyroscope, gravity, horizon, etc) to try to align them, but given only a single arbitrary reading form the accelerometer, the information just isn't there.
Definitions:
acceleration vector: this is the x, y, z reading from the accelerometer (and each reading will depend on the phones orientation), sometimes written as A=(ax, ay, az).
acceleration magnitude: this is a=sqrt(ax2 + ay2 + az2), and this should not depend on the phones orientation (if the different axes are calibrated to be the same). If the phone is stationary, this will basically just be a reading of gravity. Note also that a lot of the information in the acceleration vector is lost using this measure.
normalized acceleration: The acceleration direction, that has magniture 1, i.e., A/a
acceleration in earth coordinates: I think this is what you really want, there's just no easy way to get it, and really even if you could, I don't think it would be as useful as it might seem at first.
Skiing:
I think you have a good shot at determining when someone is skiing based on the measurements from the accelerometer. Things like bumps and turns should all be quite distinctive using the accelerometer. For these I'd use the full acceleration vector. For example, in turns, the acceleration magnitude would stay roughly constant and the direction would sweep. Also note that free-fall (i.e., basically whenever the skier doesn't have their skies/feet/butt/etc on the ground, whether they're going upward when launching off a bump/jump, or falling out of the chairlift), the acceleration magnitude will be zero in free-fall. For the chairlift, it seems that it will likely have a distinctive rhythmic sway mostly within a single plane.
All of these things could be figured out. I'd recommend, if you really want to solve this problem, is to record data from your accelerometer while skiing, and see if you can determine when you're skiing based on the characteristics of the data. (My guess is, that your major stumbling block with this will be math, because it might be a bit tricky to come up with an algorithm the can distinguish the signatures of skiing, so it seems that it would be a good idea to review vector math, and things like dot-products and cross-products, and also, I suspect that a little bit on another topic known as FFTs or Fourier transforms might be useful in sorting out the time and frequency signatures of skiing vs swinging in the chair lift.)
You could also fold in GPS measurements, which wouldn't be as reliable, or give good time resolution, but could at least be used to double-check your algorithm.
You can calculate acceleration regardless of the phone's orientation using:
a = sqrt(x*x + y*y + z*z)
Where a is the absolute acceleration and x, y and z are accelerometer values for each of the phone's 3 axes.
Some phones have a barometer (air pressure sensor) built in. After applying a moving average, I have found it to be write ready to determine if the user is going up or down -perhaps useful for your problem. On the galaxy s4 and 5 I get a resolution good enough to determine whether the device just moved from the table, to the floor.
Note, gradual changes in weather will affect your readings, so you must consider the Delta over a reasonable time interval, and ignore changes around some threshold.
Consider using the GPS. In a flight logging app I use the acceleration (albeit the absolute value, not the vector) to filter noisy GPS data (I remove locations where the acceleration needed for the change in speed is not plausible):
/**
* Remove noise from the measurement of the location.
* #param loc a location
* #return Answer <code>false</code> iff the location should not be used.
*/
private boolean filterNoise(final Location loc) {
if( ! loc.hasSpeed() )
return true;
if( this.recentSpeeds.isEmpty() ) { // rescentSpeeds is a queue of locations
this.recentSpeeds.add(loc);
return true;
}
final Location lastFix = this.recentSpeeds.getHead();
final long delta_t = (loc.getTime() - lastFix.getTime()) / 1000;
if( delta_t == 0 )
return false;
final float delta_v = loc.getSpeed() - lastFix.getSpeed();
final float a = delta_v / delta_t;
if( Math.abs(a) <= AccelThreshold ) {
this.recentSpeeds.add(loc);
return true;
}
return false;
}
If you compute the speed using the coordinates from the last fix and the current fix you get the acceleration as vector.
I am trying to filter out the noise from the orientation/compass sensor in my magic phone.
Some of the readings seem to be 90-180 degrees off and there is a lot of jiggle. I have tried different things with limited success, so I was wondering if anyone could recommend an algorithm to filter this sort of noise to get a stable output.
BR, Mads
You need Low Pass Filter. There are explanation and simple algorithm on wikipedia
This is really late but it might help people like me who came to this question.
Use Type Rotation vector sensor. No need to use a low pass filter or calculate an average value of the last x sensor values.
Here's some code:
private float[] mMatrixR = new float[9];
private float[] mMatrixValues = new float[3];
#Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ROTATION_VECTOR:
// Get rotation matrix
SensorManager.getRotationMatrixFromVector(mMatrixR, event.values);
SensorManager.getOrientation(mMatrixR, mMatrixValues);
// Use this value in degrees
mAzimuth = Math.toDegrees(mMatrixValues[0]);
}
I found the values very fast and smooth and use these in my app. I used accelerometer and magnetometer as a backup in case rotation type vector isn't present in the device, it's a software based sensor (Sensor fusion) which uses the magnetometer, accelerometer and gyro (if present).
I got rid of most of the noise by just using a slower update time. I'm not sure if Android has a built-in filter for these, but it seems to stabalize a lot. Something like:
mSensorManager.registerListener(
mSensorListener,
mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
// SENSOR_DELAY_UI, instead of SENDOR_DELAY_FASTEST (or similar)
// seems to iron out a lot of the jitter
SensorManager.SENSOR_DELAY_UI
);
SensorManager offers:
SENSOR_DELAY_FASTEST : get sensor data as fast as possible
SENSOR_DELAY_GAME : rate suitable for games
SENSOR_DELAY_NORMAL : rate (default) suitable for screen orientation changes
SENSOR_DELAY_UI : rate suitable for the user interface
What have you tried? How many readings do you get per second?
I would suggest something along the lines of an average of the last X number of readings to get rid of the "jiggles" and throw away any readings that are wildly different from the current direction to stop any crazy "jumping" of values.
Depending on how many readings you are getting, and how much averaging you are doing, your app may lose responsiveness.
The following link might be useful.
http://www.chem.uoa.gr/applets/appletsmooth/appl_smooth2.html
If you do get a significant number of completely-wrong values, you probably don't want to just average them. You could try applying a median filter first - take N samples, calculate the median, and throw out anything more than +- some threshold value. You can apply a smoothing filter after that.
If your readings are "90-180 degrees off", then either you need to calibrate your compass, or your sensor is faulty.
Certainly, the magnetic sensor has a lot of jiggle, but the "standard deviation" of such noise is about 4 degrees off. You can choose a variety of mathematical filters (low pass, Kalman) and algorithms (averaging, drop spurious readings) to apply to the measurements that can give you acceptable results.
Are you using:
List<Sensor> sens = mySensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
You might be registering 2 separate sensor handles which are both being directed to your onSensorChanged method. Now, on my onSensorChanged method, I'm sending the bearing value to either the primary or secondary method based on the Vendor name. So try this code out:
Sensor sen = e.sensor;
double bearing = 0;
if (sen.getType()==Sensor.TYPE_ORIENTATION) {
bearing = e.values[SensorManager.DATA_X];
}
if (sen.getVendor().equals(sensorVendors[0])) {
myCompassView.setBearing(bearing);
} else {
myCompassView.setSecondaryBearing(bearing);
}