Reputation: 13
I would like to use the game_rotation_vector sensor in an immersive card. This sensor is not available with the current version of Glass (XE17). So I would like to know how I can get the same values.
Actually I would like to get the yaw, pitch and azimuth values. I currently use the rotation_vector sensor to do it. I compute the difference between the beginning and the current value that gives me the current degree. But when the user shakes the horizontal values change and my app computes an bad degree.
Upvotes: 0
Views: 4345
Reputation: 11756
I wrote a helper class for managing orientation on Glass, since I was computing it several times as part of a Glass app showing real-time transit info.
/*
* Copyright (C) 2014 Sean J. Barbeau, University of South Florida
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.joulespersecond.oba.glass;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import java.util.ArrayList;
/**
* Implements a sensor-based orientation helper for Glass, which allows listeners to receive
* orientation updates
*/
public class OrientationHelper implements SensorEventListener {
public interface Listener {
/**
* Called every time there is an update to the orientation
*
* @param deltaHeading change in heading from last heading value
* @param deltaPitch change in pitch from last pitch value
*/
void onOrientationChanged(float heading, float pitch, float deltaHeading, float deltaPitch);
}
static final String TAG = "OrientationHelper";
Context mContext;
SensorManager mSensorManager;
private float[] mRotationMatrix = new float[16];
private float[] mOrientation = new float[9];
private float[] history = new float[2];
private float mHeading;
private float mPitch;
ArrayList<Listener> mListeners = new ArrayList<Listener>();
public OrientationHelper(Context context) {
mContext = context;
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
}
public synchronized void registerListener(Listener listener) {
if (!mListeners.contains(listener)) {
mListeners.add(listener);
}
// If this is the first listener, make sure we're monitoring the sensors to provide updates
if (mListeners.size() == 1) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR),
SensorManager.SENSOR_DELAY_UI);
}
}
public synchronized void unregisterListener(Listener listener) {
if (mListeners.contains(listener)) {
mListeners.remove(listener);
}
if (mListeners.size() == 0) {
mSensorManager.unregisterListener(this);
}
}
public synchronized void onResume() {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR),
SensorManager.SENSOR_DELAY_UI);
}
public synchronized void onPause() {
mSensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values);
SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_X,
SensorManager.AXIS_Z, mRotationMatrix);
SensorManager.getOrientation(mRotationMatrix, mOrientation);
mHeading = (float) Math.toDegrees(mOrientation[0]);
mPitch = (float) Math.toDegrees(mOrientation[1]);
float xDelta = history[0] - mHeading; // Currently unused
float yDelta = history[1] - mPitch;
history[0] = mHeading;
history[1] = mPitch;
// Use magnetic field to compute true (geographic) north, if data is available
// Note that if Glass doesn't have location info (e.g., it isn't paired and doesn't have a data connection), this should still work, you just can't correct for magnetic north
Float magneticDeclination = LocationHelper.getMagneticDeclination();
if (magneticDeclination != null) {
mHeading += magneticDeclination;
}
// Make sure value is between 0-360
mHeading = MathUtils.mod(mHeading, 360.0f);
for (Listener l : mListeners) {
l.onOrientationChanged(mHeading, mPitch, xDelta, yDelta);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Here's MathUtil.java:
package com.joulespersecond.oba.glass;
/**
* A utility class containing arithmetic and geometry helper methods.
*
* (from Glass Compass sample)
*/
public class MathUtils {
/**
* Calculates {@code a mod b} in a way that respects negative values (for example,
* {@code mod(-1, 5) == 4}, rather than {@code -1}).
*
* @param a the dividend
* @param b the divisor
* @return {@code a mod b}
*/
public static float mod(float a, float b) {
return (a % b + b) % b;
}
}
...and the LocationHelper
, used to correct for magnetic north, is shown here. If a location can't be determined (e.g., Glass isn't paired and doesn't have a data connection), this will still work, you just won't be able to correct for magnetic north (error will vary, depending on where you are in the world).
To use the orientation helper, you'd implement the listener interface, for example in a View
, where you would re-draw something based on orientation when the azimuth (heading) and pitch change:
public class MyView extends View implements OrientationHelper.Listener {
private float mHeading;
private float mPitch;
private float mXDelta;
private float mYDelta;
...
@Override
public void onOrientationChanged(float heading, float pitch, float xDelta, float yDelta) {
mHeading = heading;
mPitch = pitch;
mXDelta = xDelta;
mYDelta = yDelta;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
// Draw something based on orientation
...
}
}
..and register the listener when you create the view in your Service or Activity:
mOrientationHelper = new OrientationHelper(this);
mOrientationHelper.registerListener(mMyView);
And call mOrientationHelper.onResume();
in your Activity onResume()
, and mOrientationHelper.onPause();
in your Activity onPause()
to start/stop the sensors.
The Google Glass GDK Compass sample is another good example of how to use orientation on Glass.
All this works on general Android as well, with the exception that the coordinate re-mapping here is fixed to the orientation of Glass. To use this on devices that can change orientation, you'll need to re-map coordinate systems depending on orientation, which is discussed here.
Upvotes: 2