package org.schmivits.airball.airdata; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import org.schmivits.airball.util.ValueModel; public class AccelerometerFlightData implements FlightData { private static final float MAX_ACCELERATION = 10f; private final UpdateSourceHelper mUpdateSourceHelper = new UpdateSourceHelper(); private abstract class SimpleValueModel implements ValueModel<Float> { @Override public boolean isValid() { return true; } } private final ValueModel<Float> mAirspeedValueModel = new SimpleValueModel() { @Override public Float getValue() { return mAirspeed; } }; private final ValueModel<Float> mAlphaValueModel = new SimpleValueModel() { @Override public Float getValue() { return mAlpha; } }; private final ValueModel<Float> mBetaValueModel = new SimpleValueModel() { @Override public Float getValue() { return mBeta; } }; private final ValueModel<Float> mAltitudeValueModel = new SimpleValueModel() { @Override public Float getValue() { return mAltitude; } }; private final ValueModel<Float> mClimbRateValueModel = new SimpleValueModel() { @Override public Float getValue() { return mClimbRate; } }; private float getLateral(SensorEvent e) { return mIsLandscape ? -e.values[1] : e.values[0]; } private float getLongitudinal(SensorEvent e) { return e.values[2]; } private final Airdata mAirdata = new Airdata() { @Override public ValueModel<Float> getAirspeed() { return mAirspeedValueModel; } @Override public ValueModel<Float> getAlpha() { return mAlphaValueModel; } @Override public ValueModel<Float> getBeta() { return mBetaValueModel; } @Override public ValueModel<Float> getAltitude() { return mAltitudeValueModel; } @Override public ValueModel<Float> getClimbRate() { return mClimbRateValueModel; } }; private final ValueModel<Aircraft> mAircraftValueModel = new ValueModel<Aircraft>() { @Override public boolean isValid() { return true; } @Override public Aircraft getValue() { return mAircraft; } }; private final SensorEventListener mSensorAdapter = new SensorEventListener() { @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { mSensorEvents.add(event); } }; private final Runnable mUpdateCycle = new Runnable() { @Override public void run() { while (true) { try { updateValues(mSensorEvents.take()); } catch (InterruptedException e) { // Ignore and continue } } } }; private final Aircraft mAircraft; private final BlockingQueue<SensorEvent> mSensorEvents = new LinkedBlockingDeque<SensorEvent>(); private final SensorManager mSensorManager; private final boolean mIsLandscape; private long mTimestamp = -1L; private float mAirspeed = 0f; private float mAlpha = 0f; private float mBeta = 0f; private float mClimbRate = 0f; private float mAltitude = 5000f; public AccelerometerFlightData( Aircraft aircraft, SensorManager sensorManager, boolean isLandscape) { mAircraft = aircraft; mIsLandscape = isLandscape; mSensorManager = sensorManager; sensorManager.registerListener( mSensorAdapter, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST); new Thread(mUpdateCycle).start(); } private void updateValues(SensorEvent e) { mAirspeed = scaledRange(getLongitudinal(e), 25f, 130f); mAlpha = scaledRange(getLongitudinal(e), 15f, 2f); mBeta = scaledSymmetric(-getLateral(e), 4f); if (mTimestamp != -1L) { mAltitude += mClimbRate * (float) (e.timestamp - mTimestamp) / 1000f / 1000f / 1000f / 60f; } mClimbRate = scaledRange(getLongitudinal(e), 2000f, -2000f); mTimestamp = e.timestamp; mUpdateSourceHelper.fire(); } @Override public void addUpdateListener(Runnable r) { synchronized (mUpdateSourceHelper) { mUpdateSourceHelper.addUpdateListener(r); } } @Override public void removeUpdateListener(Runnable r) { synchronized (mUpdateSourceHelper) { mUpdateSourceHelper.removeUpdateListener(r); } } @Override public ValueModel<Aircraft> getAircraft() { return mAircraftValueModel; } @Override public Airdata getAirdata() { return mAirdata; } @Override public String getConnectionStatus() { return null; } public void destroy() { mSensorManager.unregisterListener(mSensorAdapter); } private static float scaledSymmetric(float acceleration, float maxAbs) { return acceleration / MAX_ACCELERATION * maxAbs; } private static float scaledRange(float acceleration, float min, float max) { float ratio = ((acceleration / MAX_ACCELERATION + 1) / 2); return min + ratio * (max - min); } }