0

I'm developing an app similar to a camera app, with a line in the center of the camera preview that shows the device tilt in regard to the horizon.

I'm using only the accelerometer, since my use case is a semi-stationary device (the user may move and tilt it and the line will update, but they should only expect a fair reading if the device is somewhat still).

This is what I've got so far:

public void onSensorChanged(SensorEvent event) {
    switch( event.sensor.getType() ){
        case Sensor.TYPE_ACCELEROMETER:
            float[] acc = event.values.clone(); // [accX, accY, accZ]

            double gravityNormalized = Math.sqrt(acc[0]*acc[0] + acc[1]*acc[1] + acc[2]*acc[2]);

            double[] accNormalized = new double[3];
            accNormalized[0] = acc[0]/gravityNormalized;
            accNormalized[1] = acc[1]/gravityNormalized;
            accNormalized[2] = acc[2]/gravityNormalized;

            double[] tiltDegrees = new double[3];
            tiltDegrees[0] = Math.toDegrees(Math.asin(accNormalized[0]));
            tiltDegrees[1] = Math.toDegrees(Math.asin(accNormalized[1]));
            tiltDegrees[2] = Math.toDegrees(Math.asin(accNormalized[2]));

            Log.d(TAG, "tiltDegrees: " + Arrays.toString(tiltDegrees) + ", accNormalized: " + Arrays.toString(accNormalized) + ", acc: " + Arrays.toString(acc) );

            ((ImageView) findViewById(R.id.levelLine)).setRotation((int)tiltDegrees[0]);
            break;
    }
}

It seems to be working fairly well, as long as the rotation is moderate. When I start nearing 90 degrees rotation of the device (portrait orientation vs horizon), the line stops rotating and is thus no longer in line with the horizon, but slightly tilted (I'd say about 5-10 degrees). Also, if I continue to rotate the device further than 90 degrees, the line becomes more and more tilted away from the horizon and becomes vertical when the device is rotated 135/-135 degrees.

Am I doing something wrong here? Is there a better way of getting the device tilt?

Further along in development I have the need to accurately get the tilt "forwards/backwards" (i.e. how many degrees the phone is tilted forwards/backwards while in portrait orientation). I'm thinking I could use the same approach I'm doing now but on tiltDegrees[1] instead of tiltDegrees[0]?

I somehow find it a bit strange that there is a TYPE_ROTATION_VECTOR that relates to the world-frame (north/south/east/west) but no device-frame TYPE_DEVICE_VECTOR or something similar to easily get the rotation of the device along its X, Y and Z axis (e.g. in relation to a perfect portrait mode).

Magnus
  • 17,157
  • 19
  • 104
  • 189
  • Is there is a reason why you didn't use existing framework? [Android Position Sensors - Computing the Device's Orientation](https://developer.android.com/guide/topics/sensors/sensors_position.html#sensors-pos-orient) and specifically `remapCoordinateSystem(..)` – Morrison Chang Feb 20 '18 at 03:40
  • @MorrisonChang I tried that, but I can't quite seem to get anything useful from it. The values jump around all over the place, and I'm not really sure which of the angles would correspond to the tilting I'm looking for (tilting the phone left/right while in portrait mode and facing the screen). I'm suspecting the values jump around due to magnetic fields from appliances nearby - is there a reason to involve the magnetometer? – Magnus Feb 20 '18 at 07:52
  • If you are having sensor noise issue, see: https://stackoverflow.com/a/24119793/295004 Orientation see: https://stackoverflow.com/a/15552017/295004 Nothing wrong with excluding magnetometer. – Morrison Chang Feb 20 '18 at 08:05
  • Also when I dealt with sensors, if I needed good fidelity (like a ball balance game), I would have a calibration step in the app as I found device to device variation too high. – Morrison Chang Feb 20 '18 at 08:14
  • @MorrisonChang If you wanna expand your comment about `remapCoordinateSystem` into an answer, I'll have a second look. But from my earlier tries I really couldn't figure out how to get the "standing sideways tilt" from those built-in methods. Good comment about calibration though. Would that need to be done in different orientations to account for different errors in different axis? I've seen some apps just say "lay the phone down on a level surface", but does that kind of calibration really work when the user then holds his phone in standing portrait orientation later? – Magnus Feb 20 '18 at 15:59
  • I realize that `remapCoordinateSystem` may complicate the matter. If you haven't locked orientation use `Display.getRotation()` to get standing facing user, lying left side facing user, lying right side, upside down orientation of the device. From there you can "massage/filter" the sensor readings to get what you need. You'll have to experiment with calibration with the understanding that the sensors are inherently noisy. – Morrison Chang Feb 20 '18 at 22:17

0 Answers0