Im creating an application where i need to position a ImageView depending on the Orientation of the device. I use the values from a MagneticField and Accelerometer Sensors to calculate the device orientation with
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues)
SensorManager.getOrientation(rotationMatrix, values);
double degrees = Math.toDegrees(values[0]);
My problem is that the positioning of the ImageView is very sensitive to changes in the orientation. Making the imageview constantly jumping around the screen. (because the degrees change)
I read that this can be because my device is close to things that can affect the magneticfield readings. But this is not the only reason it seems.
I tried downloading some applications and found that the "3D compass" and "Compass" remains extremely steady in its readings (when setting the noise filter up), i would like the same behavior in my application.
I read that i can tweak the "noise" of my readings by adding a "Low pass filter", but i have no idea how to implement this (because of my lack of Math).
Im hoping someone can help me creating a more steady reading on my device, Where a little movement to the device wont affect the current orientation. Right now i do a small
if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }
To filter abit of the noise. But its not working very well :)
Though I havn't used the compass on Android, the basic processing shown below (in JavaScript) will probably work for you.
It's based on the low pass filter on the accelerometer that's recommended by the Windows Phone team with modifications to suit a compass (the cyclic behavior every 360").
I assume the compass reading is in degrees, a float between 0-360, and the output should be similar.
You want to accomplish 2 things in the filter:
For that we will have 2 constants:
We have one variable saved across the calls, a float called oldCompass and it is the result of the algorithm.
So the variable defenition is:
and the function recieves newCompass, and returns oldCompass as the result.
I see that the issue was opened 5 months ago and probably isn't relevant anymore, but I'm sure other programmers might find it useful.
Oded Elyada.
A low pass filter (LPF) blocks fast changing signals and
allows only slow changes in the signals. This means any small
sudden changes will be ignored.
The standard way to implement this in software is to take a running average
of the last N samples and report that value. Start with N as small as 3 and
keep increasing N until you find sufficient smoothed out response in your app.
Do keep in mind that the higher you make N, slower the response of the system.
This lowpass filter works for angles in radians. Use the add function for each compass reading, then call average to get the average.
Use
Math.toDegrees()
orMath.toRadians()
to convert.See my answer to this related question: Smoothing data from a sensor
A software low pass filter is basically a modified version of that. Indeed, in that answer I even provided this link to another related question: Low pass filter software?
Keep in mind that, for example the average of 350 and 10 is not 180. My solution: