/*
* This file is part of WhereYouGo.
*
* WhereYouGo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WhereYouGo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WhereYouGo. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2012 Menion <whereyougo@asamm.cz>
*/
package menion.android.whereyougo.hardware.sensors;
import java.util.ArrayList;
import java.util.Vector;
import locus.api.objects.extra.Location;
import menion.android.whereyougo.hardware.location.LocationEventListener;
import menion.android.whereyougo.hardware.location.LocationState;
import menion.android.whereyougo.hardware.location.SatellitePosition;
import menion.android.whereyougo.settings.SettingValues;
import menion.android.whereyougo.settings.Settings;
import menion.android.whereyougo.utils.A;
import menion.android.whereyougo.utils.Logger;
import android.content.Context;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.Surface;
/**
* @author menion
* @since 25.1.2010 2010
*/
public class Orientation implements SensorEventListener, LocationEventListener {
private static final String TAG = "Orientation";
private Vector<OrientationListener> listeners;
private SensorManager sensorManager;
private float mLastAziGps;
private float mLastAziSensor;
private float mLastPitch;
private float mLastRoll;
private static float orient;
private static float pitch;
private static float roll;
private static float aboveOrBelow = 0.0f;
public Orientation() {
this.listeners = new Vector<OrientationListener>();
}
public void addListener(OrientationListener listener) {
if (!listeners.contains(listener)) {
this.listeners.add(listener);
Logger.i(TAG, "addListener(" + listener + "), listeners.size():" + listeners.size());
manageSensors();
}
}
public void removeListener(OrientationListener listener) {
if (listeners.contains(listener)) {
this.listeners.remove(listener);
Logger.i(TAG, "removeListener(" + listener + "), listeners.size():" + listeners.size());
manageSensors();
}
}
public void removeAllListeners() {
listeners.clear();
manageSensors();
}
public void manageSensors() {
// stop sensor manager
if (sensorManager != null) {
mLastAziGps = 0.0f;
mLastAziSensor = 0.0f;
sendOrientation(0.0f, 0.0f);
sensorManager.unregisterListener(this);
sensorManager = null;
// remove location listener
LocationState.removeLocationChangeListener(this);
}
// start new manager
if (listeners.size() > 0) {
if (sensorManager == null) {
sensorManager = (SensorManager) A.getMain().getSystemService(Context.SENSOR_SERVICE);
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_GAME);
}
// get azimuth from GPS when enabled in settings or by auto-change
if (!SettingValues.SENSOR_HARDWARE_COMPASS ||
SettingValues.SENSOR_HARDWARE_COMPASS_AUTO_CHANGE) {
// register location listener
LocationState.addLocationChangeListener(this);
// set zero bearing, if previously was seted by sensor
mLastAziGps = 0.0f;
}
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_MAGNETIC_FIELD:
break;
case Sensor.TYPE_ACCELEROMETER:
float filter = getFilter();
aboveOrBelow =
(float) ((event.values[SensorManager.DATA_Z] * filter) +
(aboveOrBelow * (1.0 - filter)));
break;
case Sensor.TYPE_ORIENTATION:
float valueOr = event.values[SensorManager.DATA_X];
//Logger.d(TAG, "sensorOrientation:" + valueOr + ", " + event.values[SensorManager.DATA_Y] + ", " + event.values[SensorManager.DATA_Z] + ", " + getDeclination());
// fix to true bearing
if (SettingValues.SENSOR_BEARING_TRUE) {
valueOr += getDeclination();
}
orient = filterValue(valueOr, orient);
pitch = filterValue(event.values[SensorManager.DATA_Y], pitch);
roll = filterValue(event.values[SensorManager.DATA_Z], roll);
float rollDef;
if (aboveOrBelow < 0) {
if (roll < 0) {
rollDef = -180 - roll;
} else {
rollDef = 180 - roll;
}
} else {
rollDef = roll;
}
this.mLastAziSensor = orient;
// do some orientation change by settings
int rotation = A.getMain().getWindowManager().
getDefaultDisplay().getRotation();
switch (rotation) {
case Surface.ROTATION_0:
// no need for change
break;
case Surface.ROTATION_90:
mLastAziSensor += 90;
break;
case Surface.ROTATION_180:
mLastAziSensor -= 180;
break;
case Surface.ROTATION_270:
mLastAziSensor -= 90;
break;
}
sendOrientation(pitch, rollDef);
break;
}
}
private void sendOrientation(float pitch, float roll) {
float usedOrient;
if (!SettingValues.SENSOR_HARDWARE_COMPASS_AUTO_CHANGE ||
LocationState.getLocation().getSpeed() < SettingValues.SENSOR_HARDWARE_COMPASS_AUTO_CHANGE_VALUE) {
if (!SettingValues.SENSOR_HARDWARE_COMPASS)
usedOrient = mLastAziGps;
else
usedOrient = mLastAziSensor;
} else {
usedOrient = mLastAziGps;
}
this.mLastPitch = pitch;
this.mLastRoll = roll;
for (OrientationListener listener : listeners) {
listener.onOrientationChanged(usedOrient, mLastPitch, mLastRoll);
}
}
public void onStatusChanged(String provider, int state, Bundle extras) {}
public void onGpsStatusChanged(int event, ArrayList<SatellitePosition> sats) {}
public int getPriority() {
return LocationEventListener.PRIORITY_MEDIUM;
}
public void onLocationChanged(Location location) {
//Logger.d(TAG, "onLocationChanged(), bear:" + location.hasBearing() + ", " + location.getBearing());
if (location.getBearing() != 0.0f) {
this.mLastAziGps = location.getBearing();
sendOrientation(mLastPitch, mLastRoll);
}
}
@Override
public boolean isRequired() {
return false;
}
@Override
public String getName() {
return TAG;
}
private float filterValue(float valueActual, float valueLast) {
if (valueActual < valueLast - 180.0f) {
valueLast -= 360.0f;
} else if (valueActual > valueLast + 180.0f) {
valueLast += 360.0f;
}
//Logger.d(TAG, "filterValue(" + valueActual + ", " + valueLast + ")");
float filter = getFilter();
return (float) ((valueActual * filter) +
(valueLast * (1.0 - filter)));
}
private float getFilter() {
switch (SettingValues.SENSOR_ORIENT_FILTER) {
case Settings.VALUE_SENSORS_ORIENT_FILTER_LIGHT:
return 0.20f;
case Settings.VALUE_SENSORS_ORIENT_FILTER_MEDIUM:
return 0.06f;
case Settings.VALUE_SENSORS_ORIENT_FILTER_HEAVY:
return 0.03f;
}
return 1.0f;
}
private static GeomagneticField gmf;
private static long lastCompute;
public static float getDeclination() {
long actualTime = System.currentTimeMillis();
if (gmf == null || actualTime - lastCompute > 300000) { // once per five minutes
Location loc = LocationState.getLocation();
// compute this only if needed
gmf = new GeomagneticField(
(float) loc.getLatitude(),
(float) loc.getLongitude(),
(float) loc.getAltitude(),
actualTime);
lastCompute = actualTime;
Logger.w(TAG, "getDeclination() - dec:" + gmf.getDeclination());
}
return gmf.getDeclination();
}
}