[SDL] Experimental adaptive algorithm for measuring gyroscope min/max values, removed the gyro calibration dialog

This commit is contained in:
pelya
2015-11-14 02:19:21 +02:00
parent d2c473ee51
commit 1e66878da4
8 changed files with 882 additions and 176 deletions

View File

@@ -36,6 +36,7 @@ import android.hardware.SensorEvent;
import android.util.Log;
import android.widget.TextView;
import android.os.Build;
import java.util.Arrays;
class AccelerometerReader implements SensorEventListener
@@ -105,33 +106,162 @@ class AccelerometerReader implements SensorEventListener
static class GyroscopeListener implements SensorEventListener
{
public float x1 = 0.0f, x2 = 0.0f, xc = 0.0f, y1 = 0.0f, y2 = 0.0f, yc = 0.0f, z1 = 0.0f, z2 = 0.0f, zc = 0.0f;
public boolean invertedOrientation = false;
final float noiseMin[] = new float[] { -1.0f, -1.0f, -1.0f }; // Large initial values, they will only decrease
final float noiseMax[] = new float[] { 1.0f, 1.0f, 1.0f };
float noiseData[][] = new float[200][3];
int noiseDataIdx = noiseData.length * 3 / 4; // Speed up first measurement, to converge to sane values faster
int noiseMovementDetected = 0;
float noiseMeasuredRange[] = null;
static int noiseCounter = 0;
public GyroscopeListener()
{
}
public void onSensorChanged(SensorEvent event)
void collectNoiseData(final float[] data)
{
if( event.values[0] < x1 || event.values[0] > x2 ||
event.values[1] < y1 || event.values[1] > y2 ||
event.values[2] < z1 || event.values[2] > z2 )
for( int i = 0; i < 3; i++ )
{
if( Globals.HorizontalOrientation )
if( data[i] < noiseMin[i] || data[i] > noiseMax[i] )
{
if( invertedOrientation )
nativeGyroscope(-(event.values[0] - xc), -(event.values[1] - yc), event.values[2] - zc);
else
nativeGyroscope(event.values[0] - xc, event.values[1] - yc, event.values[2] - zc);
// Movement detected, this can converge our min/max too early, so we're discarding last few values
if( noiseMovementDetected < 0 )
{
int discard = 10;
if( -noiseMovementDetected < discard )
discard = -noiseMovementDetected;
noiseDataIdx -= discard;
if( noiseDataIdx < 0 )
noiseDataIdx = 0;
}
noiseMovementDetected = 10;
return;
}
else
noiseData[noiseDataIdx][i] = data[i];
}
noiseMovementDetected--;
if( noiseMovementDetected >= 0 )
return; // Also discard several values after the movement stopped
noiseDataIdx++;
if( noiseDataIdx < noiseData.length )
return;
noiseCounter++;
Log.i( "SDL", "GYRO_NOISE: Measuring in progress... " + noiseCounter ); // DEBUG
if( noiseCounter > 15 )
{
Log.i( "SDL", "GYRO_NOISE: Measuring done! Max iteration reached " + noiseCounter ); // DEBUG
noiseData = null;
noiseMeasuredRange = null;
}
noiseDataIdx = 0;
boolean changed = false;
for( int i = 0; i < 3; i++ )
{
float min = 1.0f;
float max = -1.0f;
for( int ii = 0; ii < noiseData.length; ii++ )
{
if( invertedOrientation )
nativeGyroscope(-(event.values[1] - yc), event.values[0] - xc, event.values[2] - zc);
else
nativeGyroscope(event.values[1] - yc, -(event.values[0] - xc), event.values[2] - zc);
if( min > noiseData[ii][i] )
min = noiseData[ii][i];
if( max < noiseData[ii][i] )
max = noiseData[ii][i];
}
// Increase the range a bit, for conservative noise filtering
float middle = (min + max) / 2.0f;
min += (min - middle) * 0.2f;
max += (max - middle) * 0.2f;
// Check if range between min/max is less then the current range, as a safety measure,
// and min/max range is not jumping outside of previously measured range
if( max - min < noiseMax[i] - noiseMin[i] && min >= noiseMin[i] && max <= noiseMax[i] )
{
noiseMax[i] = (noiseMax[i] + max * 4.0f) / 5.0f;
noiseMin[i] = (noiseMin[i] + min * 4.0f) / 5.0f;
changed = true;
}
}
Log.i( "SDL", "GYRO_NOISE: MIN MAX: " + Arrays.toString(noiseMin) + " " + Arrays.toString(noiseMax) ); // DEBUG
if( !changed )
return;
// Determine when to stop measuring - check that the previous min/max range is close to the current one
float range[] = new float[3];
for( int i = 0; i < 3; i++ )
range[i] = noiseMax[i] - noiseMin[i];
Log.i( "SDL", "GYRO_NOISE: RANGE: " + Arrays.toString(range) + " " + Arrays.toString(noiseMeasuredRange) ); // DEBUG
if( noiseMeasuredRange == null )
{
noiseMeasuredRange = range;
return;
}
for( int i = 0; i < 3; i++ )
{
if( noiseMeasuredRange[i] / range[i] > 1.2f )
{
noiseMeasuredRange = range;
return;
}
}
// We converged to the final min/max, stop measuring
noiseData = null;
noiseMeasuredRange = null;
Log.i( "SDL", "GYRO_NOISE: Measuring done! Range converged on iteration " + noiseCounter ); // DEBUG
}
public void onSensorChanged(final SensorEvent event)
{
boolean filtered = true;
final float[] data = event.values;
if( noiseData != null )
collectNoiseData(data);
for( int i = 0; i < 3; i++ )
{
if( data[i] < noiseMin[i] )
{
filtered = false;
data[i] -= noiseMin[i];
}
else if( data[i] > noiseMax[i] )
{
filtered = false;
data[i] -= noiseMax[i];
}
}
if( filtered )
return;
if( Globals.HorizontalOrientation )
{
if( invertedOrientation )
nativeGyroscope(-data[0], -data[1], data[2]);
else
nativeGyroscope(data[0], data[1], data[2]);
}
else
{
if( invertedOrientation )
nativeGyroscope(-data[1], data[0], data[2]);
else
nativeGyroscope(data[1], -data[0], data[2]);
}
}
public void onAccuracyChanged(Sensor s, int a)
{
}

View File

@@ -163,15 +163,16 @@ class Settings
out.writeBoolean(false); // Unused
out.writeInt(Globals.TouchscreenKeyboardDrawSize);
out.writeInt(p.getApplicationVersion());
out.writeFloat(AccelerometerReader.gyro.x1);
out.writeFloat(AccelerometerReader.gyro.x2);
out.writeFloat(AccelerometerReader.gyro.xc);
out.writeFloat(AccelerometerReader.gyro.y1);
out.writeFloat(AccelerometerReader.gyro.y2);
out.writeFloat(AccelerometerReader.gyro.yc);
out.writeFloat(AccelerometerReader.gyro.z1);
out.writeFloat(AccelerometerReader.gyro.z2);
out.writeFloat(AccelerometerReader.gyro.zc);
// Gyroscope calibration data, now unused
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeFloat(0.0f);
out.writeBoolean(Globals.OuyaEmulation);
out.writeBoolean(Globals.HoverJitterFilter);
@@ -356,15 +357,16 @@ class Settings
settingsFile.readBoolean(); // Unused
Globals.TouchscreenKeyboardDrawSize = settingsFile.readInt();
int cfgVersion = settingsFile.readInt();
AccelerometerReader.gyro.x1 = settingsFile.readFloat();
AccelerometerReader.gyro.x2 = settingsFile.readFloat();
AccelerometerReader.gyro.xc = settingsFile.readFloat();
AccelerometerReader.gyro.y1 = settingsFile.readFloat();
AccelerometerReader.gyro.y2 = settingsFile.readFloat();
AccelerometerReader.gyro.yc = settingsFile.readFloat();
AccelerometerReader.gyro.z1 = settingsFile.readFloat();
AccelerometerReader.gyro.z2 = settingsFile.readFloat();
AccelerometerReader.gyro.zc = settingsFile.readFloat();
// Gyroscope calibration data, now unused
settingsFile.readFloat();
settingsFile.readFloat();
settingsFile.readFloat();
settingsFile.readFloat();
settingsFile.readFloat();
settingsFile.readFloat();
settingsFile.readFloat();
settingsFile.readFloat();
settingsFile.readFloat();
Globals.OuyaEmulation = settingsFile.readBoolean();
Globals.HoverJitterFilter = settingsFile.readBoolean();

View File

@@ -243,7 +243,6 @@ class SettingsMenu
new SettingsMenuMisc.OptionalDownloadConfig(false),
new SettingsMenuKeyboard.KeyboardConfigMainMenu(),
new SettingsMenuMouse.MouseConfigMainMenu(),
new SettingsMenuMisc.GyroscopeCalibration(),
new SettingsMenuMisc.AudioConfig(),
new SettingsMenuKeyboard.RemapHwKeysConfig(),
new SettingsMenuKeyboard.ScreenGesturesConfig(),

View File

@@ -589,157 +589,19 @@ class SettingsMenuMisc extends SettingsMenu
}
}
static class GyroscopeCalibration extends Menu implements SensorEventListener
static class GyroscopeCalibration extends Menu
{
String title(final MainActivity p)
{
return p.getResources().getString(R.string.calibrate_gyroscope);
return "";
}
boolean enabled()
{
return Globals.AppUsesGyroscope || Globals.MoveMouseWithGyroscope;
return false;
}
void run (final MainActivity p)
{
if( !(Globals.AppUsesGyroscope || Globals.MoveMouseWithGyroscope) || !AccelerometerReader.gyro.available(p) )
{
if( Globals.AppUsesGyroscope || Globals.MoveMouseWithGyroscope )
{
Toast toast = Toast.makeText(p, p.getResources().getString(R.string.calibrate_gyroscope_not_supported), Toast.LENGTH_LONG);
toast.show();
}
goBack(p);
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(p);
builder.setTitle(p.getResources().getString(R.string.calibrate_gyroscope));
builder.setMessage(p.getResources().getString(R.string.calibrate_gyroscope_text));
builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int item)
{
dialog.dismiss();
startCalibration(p);
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener()
{
public void onCancel(DialogInterface dialog)
{
goBack(p);
}
});
AlertDialog alert = builder.create();
alert.setOwnerActivity(p);
alert.show();
}
ImageView img;
Bitmap bmp;
int numEvents;
MainActivity p;
void startCalibration(final MainActivity _p)
{
p = _p;
img = new ImageView(p);
img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
img.setScaleType(ImageView.ScaleType.MATRIX);
bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate );
img.setImageBitmap(bmp);
Matrix m = new Matrix();
RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight());
RectF dst = new RectF( p.getVideoLayout().getWidth()/2 - 50, p.getVideoLayout().getHeight()/2 - 50,
p.getVideoLayout().getWidth()/2 + 50, p.getVideoLayout().getHeight()/2 + 50);
m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL);
img.setImageMatrix(m);
p.getVideoLayout().addView(img);
numEvents = -10;
AccelerometerReader.gyro.x1 = 100;
AccelerometerReader.gyro.x2 = -100;
AccelerometerReader.gyro.xc = 0;
AccelerometerReader.gyro.y1 = 100;
AccelerometerReader.gyro.y2 = -100;
AccelerometerReader.gyro.yc = 0;
AccelerometerReader.gyro.z1 = 100;
AccelerometerReader.gyro.z2 = -100;
AccelerometerReader.gyro.zc = 0;
AccelerometerReader.gyro.registerListener(p, this);
(new Thread(new Runnable()
{
public void run()
{
for(int count = 1; count < 10; count++)
{
p.setText("" + count * 10 + "% ...");
try {
Thread.sleep(300);
} catch( Exception e ) {}
}
finishCalibration(p);
}
}
)).start();
}
public void onSensorChanged(SensorEvent event)
{
gyroscopeEvent(event.values[0], event.values[1], event.values[2]);
}
public void onAccuracyChanged(Sensor s, int a)
{
}
void gyroscopeEvent(float x, float y, float z)
{
numEvents++;
if (numEvents <= 0)
return; // Skip few initial measurements, they may be incorrect
AccelerometerReader.gyro.xc += x;
AccelerometerReader.gyro.yc += y;
AccelerometerReader.gyro.zc += z;
AccelerometerReader.gyro.x1 = Math.min(AccelerometerReader.gyro.x1, x * 1.02f); // Small safety bound coefficient
AccelerometerReader.gyro.x2 = Math.max(AccelerometerReader.gyro.x2, x * 1.02f);
AccelerometerReader.gyro.y1 = Math.min(AccelerometerReader.gyro.y1, y * 1.02f);
AccelerometerReader.gyro.y2 = Math.max(AccelerometerReader.gyro.y2, y * 1.02f);
AccelerometerReader.gyro.z1 = Math.min(AccelerometerReader.gyro.z1, z * 1.02f);
AccelerometerReader.gyro.z2 = Math.max(AccelerometerReader.gyro.z2, z * 1.02f);
final Matrix m = new Matrix();
RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight());
RectF dst = new RectF( x * 5000 + p.getVideoLayout().getWidth()/2 - 50, y * 5000 + p.getVideoLayout().getHeight()/2 - 50,
x * 5000 + p.getVideoLayout().getWidth()/2 + 50, y * 5000 + p.getVideoLayout().getHeight()/2 + 50);
m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL);
p.runOnUiThread(new Runnable()
{
public void run()
{
img.setImageMatrix(m);
}
});
}
void finishCalibration(final MainActivity p)
{
AccelerometerReader.gyro.unregisterListener(p, this);
try {
Thread.sleep(200); // Just in case we have pending events
} catch( Exception e ) {}
if( numEvents > 10 )
{
AccelerometerReader.gyro.xc /= (float)numEvents;
AccelerometerReader.gyro.yc /= (float)numEvents;
AccelerometerReader.gyro.zc /= (float)numEvents;
Log.i("SDL", "libSDL: gyroscope calibration: " +
AccelerometerReader.gyro.x1 + " < " + AccelerometerReader.gyro.xc + " > " + AccelerometerReader.gyro.x2 + " : " +
AccelerometerReader.gyro.y1 + " < " + AccelerometerReader.gyro.yc + " > " + AccelerometerReader.gyro.y2 + " : " +
AccelerometerReader.gyro.z1 + " < " + AccelerometerReader.gyro.zc + " > " + AccelerometerReader.gyro.z2);
}
p.runOnUiThread(new Runnable()
{
public void run()
{
p.getVideoLayout().removeView(img);
goBack(p);
}
});
goBack(p);
}
}