Rotation of a node based on Android Geomagnetic Rotation Vector doesn't work

lahellerlaheller USMember ✭✭

Hi!

I just created a simple UrhoSharp Android application where I want to rotate a box node based on Geomagnetic Rotation Vector (GRV) sensor (which is using accelerometer and magnetometer). Basically I want to rotate the box when my device's orientation is changed.
Using the SensorManager.GetQuaternionFromVector(Q, RV) method I am getting the quaternion for current rotation vector reading (filtered using a lowpass filter), and sending quaternion to BoxNode's Rotate( ) method. But unfortunately the box is rotating always, even if my device's orientation is not changed. Below is the full source code, while the interesting part is at the end. Any idea?

using Android.App;
using Android.Content.PM;
using Android.Hardware;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Support.V7.App;
using System.Linq;
using System.Threading;
using SysTrace = System.Diagnostics.Trace;
using Urho;
using Urho.Droid;
using Urho.Shapes;

namespace PlanetariumConcept3D {
    [Activity(Label = "@string/app_name", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, Icon = "@drawable/icon", Name = "hu.lheller.PlanetariumConcept3D", ConfigurationChanges = ConfigChanges.KeyboardHidden | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Portrait)]
    public class MainActivity : AppCompatActivity, ISensorEventListener {
        private const float ALPHA = 0.5f;
        private readonly float[] Q = new float[4];
        private SimpleApplication app = null;
        private SensorManager sm = null;
        private Sensor grv = null;
        private float[] grvReadings = null;
        private Node cameraNode = null;
        private Node boxNode = null;

        protected override void OnCreate(Bundle bundle) {
            base.OnCreate(bundle);

            sm = (SensorManager)GetSystemService(SensorService);
            grv = sm.GetDefaultSensor(SensorType.GeomagneticRotationVector);
            if (grv == null) {
                Toast.MakeText(this, "SensorType.GeomagneticRotationVector not present!", ToastLength.Long).Show();
                Finish();
                return;
            }

            SimpleApplication.UnhandledException += (s1, e1) => {
                SysTrace.WriteLine(e1.Exception.Message);
                e1.Handled = true;
            };

            var layout = new AbsoluteLayout(this);
            var surface = UrhoSurface.CreateSurface(this);
            var result = surface.Show<SimpleApplication>(null, true);

            ThreadPool.QueueUserWorkItem((arg1) => {
                while (!result.IsCompleted) { }
                app = result.Result;
                SysTrace.WriteLine("Platform: " + SimpleApplication.Platform);
                SimpleApplication.InvokeOnMain(() => {
                    app.LightNode.Position = new Vector3(0.0f, 3.0f, -3.0f);
                    app.LightNode.SetDirection(new Vector3(0, 0, 1));
                    app.Light.LightType = LightType.Spot;
                    app.Light.Color = Color.Black;

                    boxNode = app.Scene.CreateChild("boxNode");
                    boxNode.Position = new Vector3(0, 0, 5);
                    var box = boxNode.CreateComponent<Box>();
                    box.Color = Color.Yellow;

                    cameraNode = app.CameraNode;
                    cameraNode.Position = new Vector3(0, 0, 0);
                    cameraNode.SetDirection(new Vector3(0, 0, 1));
                });
            });

            layout.AddView(surface);
            SetContentView(layout);
        }

        protected override void OnResume() {
            UrhoSurface.OnResume();
            base.OnResume();
            sm.RegisterListener(this, grv, SensorDelay.Game);
        }

        protected override void OnPause() {
            UrhoSurface.OnPause();
            base.OnPause();
            sm.UnregisterListener(this);
        }

        public override void OnLowMemory() {
            UrhoSurface.OnLowMemory();
            base.OnLowMemory();
        }

        protected override void OnDestroy() {
            UrhoSurface.OnDestroy();
            base.OnDestroy();
        }

        public override bool DispatchKeyEvent(KeyEvent e) {
            if (!UrhoSurface.DispatchKeyEvent(e)) return false;
            return base.DispatchKeyEvent(e);
        }

        public override void OnWindowFocusChanged(bool hasFocus) {
            UrhoSurface.OnWindowFocusChanged(hasFocus);
            base.OnWindowFocusChanged(hasFocus);
        }

        public void OnAccuracyChanged(Sensor sensor, [GeneratedEnum] SensorStatus accuracy) {
            //throw new NotImplementedException();
        }

        private 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;
        }

        public void OnSensorChanged(SensorEvent e) {
            if (e.Sensor.Type == SensorType.GeomagneticRotationVector) {
                grvReadings = LowPass(e.Values.ToArray(), grvReadings);
                if (grvReadings != null && boxNode != null) {
                    SensorManager.GetQuaternionFromVector(Q, grvReadings);
                    // The quaternion Q is stored as [w, x, y, z]
                    // w = Q0, x = Q1, y = Q2, z = Q3
                    boxNode.Rotate(new Quaternion(Q[1], Q[2], Q[3], Q[0]));
                }
            }
        }
    }
}

Best Answer

  • lahellerlaheller US ✭✭
    edited April 2018 Accepted Answer

    After a bit of playing I solved it B)
    The OnSensorChanged( ) method should look like this:

                public void OnSensorChanged(SensorEvent e) {
                    if (e.Sensor.Type == SensorType.GeomagneticRotationVector) {
                        var rv = e.Values.ToArray();
                        grvReadings = LowPass(rv, grvReadings);
                        if (grvReadings != null && boxNode != null) {
                            var QV = new float[4];
                            SensorManager.GetQuaternionFromVector(QV, grvReadings);
                            boxNode.Rotation = new Quaternion(QV[1], QV[2], -QV[3], QV[0]);
                        }
                    }
                }
    

Answers

  • lahellerlaheller USMember ✭✭
    edited April 2018 Accepted Answer

    After a bit of playing I solved it B)
    The OnSensorChanged( ) method should look like this:

                public void OnSensorChanged(SensorEvent e) {
                    if (e.Sensor.Type == SensorType.GeomagneticRotationVector) {
                        var rv = e.Values.ToArray();
                        grvReadings = LowPass(rv, grvReadings);
                        if (grvReadings != null && boxNode != null) {
                            var QV = new float[4];
                            SensorManager.GetQuaternionFromVector(QV, grvReadings);
                            boxNode.Rotation = new Quaternion(QV[1], QV[2], -QV[3], QV[0]);
                        }
                    }
                }
    
Sign In or Register to comment.