Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
498 views
in Technique[技术] by (71.8m points)

android - Compass readings on SGS III

My app needs to show the current bearing of the device using its compass. The code I'm using (below) works perfectly fine on my Galaxy Nexus and Galaxy One, but the compass is spinning around wildly on a Samsung Galaxy S III. I've tried doing a figure-8 to recalibrate the device, but that doesn't change anything. The weird thing is that other compass apps downloaded from Google Play work just fine on the SIII. What could be the issue here?

float[] mGravity;
float[] mGeomagnetic;

public void onSensorChanged( SensorEvent event ) {
    float azimuth = 0f;
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
        mGravity = event.values;
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
        mGeomagnetic = event.values;
    if (mGravity != null && mGeomagnetic != null) {
        float R[] = new float[9];
        float I[] = new float[9];
        boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
        if (success) {
            float orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);
            azimuth = orientation[0]; // orientation contains: azimut, pitch and roll
        }
     }

    //Discard 0.0-values
    if(azimuth == 0.0) { return; }

    //Convert the sensor value to degrees
    azimuth = (float) Math.toDegrees(azimuth); //same as azimuth = -azimuth*360/(2*3.14159f);


    //Smooth the sensor-output
    azimuth = smoothValues(azimuth);
}

//From http://stackoverflow.com/questions/4699417/android-compass-orientation-on-unreliable-low-pass-filter
//SmoothFactorCompass: The easing float that defines how smooth the movement will be (1 is no smoothing and 0 is never updating, my default is 0.5).
//SmoothThresholdCompass: The threshold in which the distance is big enough to turn immediately (0 is jump always, 360 is never jumping, my default is 30).
static final float SmoothFactorCompass = 0.5f;
static final float SmoothThresholdCompass = 30.0f;
float oldCompass = 0.0f;
private float smoothValues (float newCompass){
    if (Math.abs(newCompass - oldCompass) < 180) {
        if (Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
            oldCompass = newCompass;
        }
        else {
            oldCompass = oldCompass + SmoothFactorCompass * (newCompass - oldCompass);
        }
    }
    else {
        if (360.0 - Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
            oldCompass = newCompass;
        }
        else {
            if (oldCompass > newCompass) {
                oldCompass = (oldCompass + SmoothFactorCompass * ((360 + newCompass - oldCompass) % 360) + 360) % 360;
            } 
            else {
                oldCompass = (oldCompass - SmoothFactorCompass * ((360 - newCompass + oldCompass) % 360) + 360) % 360;
            }
        }
    }
    return oldCompass;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Currently I am investigating compass mechanism on Android and I would recommend to start with low-pass filter in your case. What you need to do - is to apply low-pass filter to both ACCELEROMETER and MAGNETIC_FIELD sensors data. Here is how I implemented that:

private float[] accel;
private float[] geomagnetic;
float R[] = new float[9];
float I[] = new float[9];
float orientation[] = new float[3];

@Override
public void onSensorChanged(SensorEvent event)
{
    synchronized (this)
    {
        float azimuth = -1f;

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
            accel = lowPass( event.values.clone(), accel );

        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
            geomagnetic = lowPass(event.values.clone(), geomagnetic);

        if (accel != null && geomagnetic != null)
        {

            boolean success = SensorManager.getRotationMatrix(R, I,
                    accel, geomagnetic);

            SensorManager.remapCoordinateSystem(R,
                    SensorManager.AXIS_X, SensorManager.AXIS_Z, R);

            if (success)
            {
                SensorManager.getOrientation(R, orientation);
                azimuth = orientation[0]; // orientation contains:
                                         // azimuth, pitch
                                         // and roll
                float newHeading = azimuth * 360 / (2 * 3.14159f);

                //do what you need to do with new heading
            } 
        }
    }
}


/*
 * time smoothing constant for low-pass filter 0 ≤ alpha ≤ 1 ; a smaller
 * value basically means more smoothing See:
 * http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
 */
static final float ALPHA = 0.15f;

/**
 * @see http
 *      ://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
 * @see http
 *      ://developer.android.com/reference/android/hardware/SensorEvent.html
 *      #values
 */

protected float[] lowPass(float[] input, float[] output)
{
    if (output == null)
        return input;

    for (int i = 0; i < input.length; i++)
    {
        output[i] = output[i] + ALPHA * (input[i] - output[i]);
    }
    return output;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...