2

I am making an AR experience where I would like to use a phone's compass to direct a player toward true north, but I am having problems with smoothing the compass readings on Android devices. The code I have experimented with so far logs a certain number of readings into a queue, copies the queue into a list and then once the list is full it takes the average of all the readings. From here I would like to scale the reading between -1 & 1, where 0 represents South. Then I want this data to be used to rotate a compass image on the GUI layer.

The data is nowhere near as smooth as I'd like it to be, but here is the code I have so far:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CompassSlider : MonoBehaviour
{
    public float reading;
    public Queue<float> data;
    private float[] dataList;
    public int maxData = 100;
    public int count;
    public float sum, average;

    public float dampening = 0.1f;
    public float rotationToNorth;

    // Use this for initialization
    void Start()
    {
        data = new Queue<float>();
        dataList = new float[maxData];
    }

    // Update is called once per frame
    void Update()
    {
        Input.location.Start();
        Input.compass.enabled = true;

        count = data.Count;

        if (data.Count > 0)
        {
            data.CopyTo(dataList, 0);
        }

        if (data.Count == maxData)
        {
            for (int i = 0; i < data.Count; i++)
            {
                sum += dataList[i];
            }
            if (Mathf.Abs(dataList[maxData - 1]) > 0)
            {
                average = sum / maxData;
                sum = 0;
                data.Clear();
                dataList = new float[maxData];

            }
        }

        if (data.Count >= maxData)
        {
            data.Dequeue();
        }

        reading = Mathf.Round(Input.compass.trueHeading);
        data.Enqueue(reading);

        if (count == maxData) {
            rotationToNorth = average / 180;
            rotationToNorth = (Mathf.Round(rotationToNorth * 10)) / 10;
            rotationToNorth = rotationToNorth - 1;
        }
    }
}
  • You may want to read up on how to program a moving average, or a weighted average. With a weighted average, for example, you take the last N readings, but give more weight to the most recent readings. This allows your compass to be somewhat responsive to changes in direction, but still smooth things out. The way to do it is keep the last N readings in a collection, and then have N weighting factors (generally, that tail off somewhat exponentially). Then calculate the Sum(reading * weight) and divide by Sum(weight). – Flydog57 Nov 26 '18 at 23:45
  • I think what I actually want to do is figure out a way to find outliers in the data and remove them from the set completely. For example, I could have the phone still on a surface and the values would jitter around a center point, let's say 0.5. Sometimes I will get a random value that just doesn't make any sense (i.e. a -0.6 even though the phone hasn't been moved). Is there a way to check a previous and current value, and if the difference between these values is beyond a certain threshold, that the queue will just ignore the current value? – Danielle McPhatter Nov 28 '18 at 17:42
  • The problem is that compass data can have legitimate quick changes. Consider someone who turns 90 deg clockwise. The compass should swing from North to East pretty quickly. It's not like temperature where step changes generally don't happen quickly. The only way to really detect outliers would be to look at the sequence before and after the outlier (if you see N-N-N-E-N-N, then the East is probably an outlier) – Flydog57 Nov 28 '18 at 19:14

0 Answers0