android compass seems unreliable

13,297

Solution 1

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);

Solution 2

Even though the author answered his own question I have to chime in here just to reinforce the almost utter uselessness of any kind of compass functionality on the Android platform.

I have come to the conclusion that anyone that depends on an Android compass application is asking for trouble, none of them work reliably and it is not the fault of the developer.

Google and the mfg's simply have not provided a way to get reliable accuracy from these devices, or even to determine if the accuracy is reliable which is even worse because sometimes they are and many times they are not and if someone trusts these devices for real orienteering God help them.

The reason the author probably thinks he is getting better results is that they use a pretty good noise filter on the deprecated orientation sensor ( why they could not do this on the newer method is beyond me ) and in limited testing on a single device after calibration this will appear to work, but in the field using many devices I have found that for the most part the reliability is always in question.

First the noise generated from the magnetic and orientation sensors are horrible, yes this can be overcome with proper DSP techniques, and with 2.3 and gyro enabled phones it will get better overall, but shame on Google and the Mfg's for wasting so much developer time with shoddy implementations of hardware and software outputs.

Second, I have tested at least 18 phones with proper DSP filtering in place and while that cleans up the noise it does not help with accuracy, even the same model of phones have different outputs (though some models seem better than others)

Third you have little in the way of determining if the sensors are calibrated, even doing the spastic figure 8 motion may or may not calibrate the phone and the user never really knows if it's working or not, unless you have a compass to verify, which kind of defeats the point doesn't it?

NOTE: you can multiply and sum the magnetic sensors with each other and take the square root of that sqrt(x*x+y*y+z*z) and make sure it's between 25 and 65 or so, this is one indicator you can use to detect anomalous fields but it's not completely reliable, better than nothing I guess.

Fourth, many phones are completely unreliable, calibrated or not, which is not limited to model types but possibly shoddy QA on the part of the mfg, I really don't know why but I can say that 3 HTC ARIA's produced wildly different results (one 30 degrees off, the other 50, and a third almost spot on) same thing with incredible, nexus, etc.

I tested 18 phones and many were pretty close to accurate IF you could calibrate properly, but many of those took 2-10 try's ( we verified after each calibration attempt with a high precision compass ) and more than a few times they would simply NOT calibrate at all.

NOTE: you do have to account for declination for a true north offset, which you can do with the API in android if you have access to the current GPS coordinates, altitude, time of day etc. the problem was NOT declination and if you are comparing to a compass that's not an issue anyway since it will also be effected by local magnetic fields.

Fith, cold starts always require the calibration step on every phone we tested, which includes the X, the incredible, the Aria, the Nexus, and the Thunderbolt. In other words the first time you start sensor listening 95% of the time it will require a calibration step (even a broken clock is right twice a day) so if you insist on adding this functionality I would just tell your user to do it at the start of each listener event.

If you leave the senors running (bad for battery) then you may or may not have to re-calibrate depending on the fields it's encountered) the above method works ok for that.

The bottom line is that when they work they seem cool, but you can NEVER currently be sure of the accuracy of the azimuth, which makes them pretty unreliable and useless for any real work.

Personally I would use a GPS bearing while moving and then a rotation vector method if possible, it might not be perfect but it would be a heck of a lot better than the craptacular implementation that you have in the current line of phones for azimuth.

Sorry about the long winded reply but I have wasted almost a month on trying to get this to work with the help of an expert DSP engineer and we have pretty much written of the android platform as useful in this respect.

A "Sometimes this works, other times it won't, you can never be sure unless you have a real compass" disclaimer should be put in every compass application in my opinion.

Solution 3

Your code looks fine to me, if there would be an error in your code I am pretty sure both devices would suffer from that. I think it's the hardware in the devices that's causing the differences. Have you "calibrated" both compasses by moving the phone in figure eight shapes? Many compass apps suggest that, including the map software that comes with symbian devices. That could work

Share:
13,297
ocross
Author by

ocross

Updated on June 05, 2022

Comments

  • ocross
    ocross about 2 years

    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.

  • ocross
    ocross about 13 years
    Thanks for the response, and you do make a great point about calibrating that can make a huge difference and make the compasses unreliable. Unfortunately this was a step i forgot to mention that i did before the tests(i edited it in now). I will give it another go and calibrate even longer this time too see if i notice any better results.
  • ocross
    ocross about 13 years
    Yes I agree with you. I came to the conclusion that you must realize that there will be around a +-30 degree margin of error with the readings it seems like on most my devices (only tested with 6) it sticks between a range of around 10. As for why i said the orientation sensor was the fix was because the droid X was not working at all. The reading wasn't anywhere near correct when the others were close with an error margin and switching to the orientation made the X work similar to the others. The noise reduction was nice but i was doing my own low pass filter already after getting values.
  • Idistic
    Idistic almost 13 years
    @ocross What I did find was that if the sensors cfg file was deleted the offset problem, and the re-calibration problem went away. The problem is that requires root access, and it's only temporary. Also the sticking problem seems to be related to manual calibration on the phones I tests, the lazy-8 wrist motion fixed that at least. For the other I had to include and offset function that could be "synched" to the gps bearing (speed accuracy must be good) or manually by the user. Problem is that drift happens over time
  • ravemir
    ravemir about 11 years
    This post kind is both a blessing and a curse: I was going to rely on accelerometers to determine the heading (another not-so-reliable method), but was wondering if switching off to magnetometers for heading determination wouldn't have been a better idea from the start. Since I am going to need a step-by-step accurate reading of heading, accelerometers don't work and compasses don't seem to work, what is left then? Is GPS reliable enough? Even with skyline interference?
  • Idistic
    Idistic about 11 years
    @ravemir Keep in mind that it's getting better. The real issue is that it's not universal (some phones are great others suck) that's the real problem. GPS is ok if your moving faster than a few mph/kph and and you have good signal, but only if your thinking about the forward direction (turning to the left while moving to the right won't work of course unless you can get rotational vectors etc) You need decent sensors and sensor fusion to make this all work reliably and it's coming, but you can't depend on it being their quite yet.
  • ravemir
    ravemir about 11 years
    The direction is indeed the forward direction, but I am not sure if running constitutes as "faster than a few mph". If indeed it does, I sure have wasted alot of time on nothing worth it xD
  • GVillani82
    GVillani82 over 10 years
    I have experimented the following compass sensor error: stackoverflow.com/questions/21016569/…
  • nsandersen
    nsandersen about 7 years
    I'm impressed your margins of error are so small - navigating around walking or driving I usually ignore the phone heading marker, which is typically 40-80 degrees from actual course, sometimes more.