Saturday, 29th August, 2009
Applying the Calculus
The Android phone was hailed (some time ago) as The Killer Phone, due to it’s open-ness and connection with Google. It is a good phone, and it has got a nice interface. Trouble is, it still isn’t The Killer Phone. It’s a niche. The iPhone still seems to be The Killer Phone.
Anyways, for my story. Some time ago, I got a HTC Magic with Android 1.5 Cupcake. Those of you that know me in real life know that I’ve been wanting to write an application for it for some time. It’s not like I’ve been unable to code on it. I’ve been known to be without internets or laptop and still solve Project Euler problems using the Android Scripting Environment.
But these are severely limited. As far as I can tell, no real interface building is available, and the access to the APIs is there, but also quite limited.
So, I had to do it, I had to bite the bullet and suffer more Java.
I decided it would be cool to calculate one’s speed based on your acceleration since the application was started.
package dan.speedle;
import java.util.Date;
import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;
import android.hardware.SensorManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
public class Speedle extends Activity implements SensorEventListener{
private TextView tv_output;
private long yspeed;
private long last_time;
private long last_y;
private SensorManager sensor_m;
private Date timer;
private TextView out;
private TextView debug;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.out = (TextView)findViewById(R.id.output);
this.debug = (TextView)findViewById(R.id.debugView);
out.setText("Initializing...");
this.last_y = 0;
timer = new Date();
this.last_time = timer.getTime();
sensor_m = (SensorManager) getSystemService(SENSOR_SERVICE);
sensor_m.registerListener(this, sensor_m.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) ,
SensorManager.SENSOR_DELAY_UI);
}
public void onSensorChanged(SensorEvent se) {
// if (se.sensor.getType() == SensorManager.SENSOR_ACCELEROMETER) {
long current_y = (long)se.values[1];
long current_time = se.timestamp/1000000000;
long height = current_time - this.last_time; // Height of the new trapezium, delta-time
yspeed += (0.5)*(current_y + this.last_y)*height; // Area of the new delta-time trapezium
this.last_time = current_time; // Update the previous values for next time
this.last_y = current_y;
debug.setText("Delta Time: "+height);
//}
out.setText(Long.toString(yspeed));
}
public void onAccuracyChanged(Sensor s, int y) {
}
}
the onSensorChanged() bit is the part that does the hard work. It simply runs the trapezium rule on the point since the last sensor poll, and adds the resulting area to the speed. Effective? probably not? Accurate? LOL, no. Working? Vaguely :-D. It current has an output accuracy of about 1ms^-1, and two outputs, a debug message that tells us the change in time since the previous poll, and a line saying what the current “speed” is. Neat.
Issues… It does have issues, E.g. gravity is a force, so unless it’s flat, it reckon’s it’s falling, and hence accelerating, and increments the “speed” nicely. Also it also only works in 1D… Along the portrait edge of the screen.
I need to do some mathsing to work out the “speed” in each direction, then the resultant speed, etc. etc.
At a push, I reckon this could do all kinds of cool stuff, if I manage to get rid of gravity from the equation.
Done! :-D