androidcompass-geolocationsensorsandroid-sensors

android compass seems unreliable


I have been working on a small compass app the past couple of days, and have gotten the code up and running, but it seems that the compass reading is not accurate. After calibrating both phones, my first test that i found this in what that i simply held the phone against and flat surface looked at the reading then flipped it horizontally and put it against the same flat surface (a 180* turn) and the value did not change 180* it was closer to 240*.

I then tested the reading vs a compass, at times the reading seemed to be close but at other points it was more than 50* off. I even tried to put my phone and compass on the floor to keep the compasses away from any magnetic interference with the same results (note i also keep the compass and phone apart keeping them in the same direction by lining up with edges of a book).

I then put the sample application on another phone (first was nexus S, second was Motorola droid 1). Between the two phones the difference ranges from being equal at some points but at most points being between 50 and 15 degrees off.

I have looked through the documentation and also looked through many different forum posts and I do not see anyone with the same results. There may be some small error in my code that is causing my reading to come out incorrectly, or some documented bug that I am not seeing.

Any insight or suggestions would be greatly appreciated!!

Heres my on sensor changed code in my SensorEventListener class

public void onSensorChanged(SensorEvent event)
    {
        // If the sensor data is unreliable return
        if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE)
        {
            Toast.makeText(main.this, "Sensor Status Unreliable",Toast.LENGTH_SHORT).show();
        }





        // Gets the value of the sensor that has been changed
        switch (event.sensor.getType())
        {
        case Sensor.TYPE_ACCELEROMETER:
            m_vfgravity = event.values.clone();
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            m_vfgeomag = event.values.clone();
            break;
        }

        if (m_vfgravity != null && m_vfgeomag != null)
        {
            if(SensorManager.getRotationMatrix(m_vfinR, m_vfI, m_vfgravity, m_vfgeomag))
            {
                SensorManager.getOrientation(m_vfinR, m_vforientVals);

                m_fCompBearing = (float) Math.round((Math.toDegrees(m_vforientVals[0])) *2)/2;

                //convert to 0-360 from -180-180
                if(m_fCompBearing < 0.0)
                {
                    m_fCompBearing = 360 + m_fCompBearing;
                }


                mCompHead.setText("" + (int)m_fCompBearing);
            }

            calcOffset();   
            rotateCmp();
        }
    }

And code in on create of my activity

    mSMngr = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    mSListener = new cSensorListener();

    mSMngr.registerListener(mSListener,
            mSMngr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
            SensorManager.SENSOR_DELAY_UI);
    mSMngr.registerListener(mSListener,
            mSMngr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
            SensorManager.SENSOR_DELAY_UI);

Thanks in advance!

Edit: also tried it with the droid X with the worst results yet... When the phone is rolled around 45 degrees (rotated around z axis a computer coordinate system) the compass heading returned can change over 180 degrees, in fact the heading value goes in the opposite direction of the other phones when spun in the same direction. This is the only phone that produces this result even after calibrating in the settings. Also their is a compass live wallpaper i test against that doesnt have the same issue. So I would assume there would be something i can do in software to avoid this.


Solution

  • Well after much testing, and debugging. I came to the conclusion that yes as some of you mentioned the differences my droid 1 and Nexus S were purely hardware difference and magnetic interference.

    However the Droid X was a different issue, whatever i tried i could not get the correct readings out of the recommended way with getRotationMatrix and getOrientation, even when added re-map coordinates function. So after some tinkering with no success i figured id give the Orientation sensor way a shot.

    Google says that this way is deprecated and they recommend doing it the way i started off with, however i tried all types of combinations with that way with no success. So I went ahead and ignored thier warning and used the orientation sensor ... and it worked. Why ? i have no clue, the droid x is newer os than my droid 1 so it shouldn't have to do with using legacy code. However it does make sense why compass apps wrote to target 1.6 would work while my app doing the "recommended way" was not working.

    If anyone has any better way to do this let me know, or if you know a way to make it work with getRotationMatrix and getOrientation also do tell.

    Otherwise for anyone else who hits this brick wall as hard as I did heres the code that ended up working for me.

    my on sensor changed

            switch (event.sensor.getType())
            {
            case Sensor.TYPE_ORIENTATION:
                m_vforientVals = event.values.clone();
                break;
            }
    
            if(m_vforientVals != null)
            {
                m_fCompBearing = m_vforientVals[0];             
    
    
                mCompHead.setText("" + (int)m_fCompBearing);
    
                calcOffset();   
                rotateCmp();
            }
    

    and initialize the sensor listener

        mSMngr.registerListener(mSListener,mSMngr.getDefaultSensor(Sensor.TYPE_ORIENTATION),
                                SensorManager.SENSOR_DELAY_NORMAL);