I'm trying to find out the force of which a button is pressed with the on-board accelerometer.
I figured i'll have to do a series of readings and then compare the values.
My attempt at it was something like this:
velButton.setOnTouchListener(new OnTouchListener()
{
public boolean onTouch(View view, MotionEvent event)
{
int velocity = 0;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
velocity = getVelocity();
velocityView.setText("This is the velocity: " + velocity);
break;
}
return false;
});
}
protected int getVelocity()
{
float accent = 0;
int velocity;
try
{
accent = getHighestValue();
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
velocity = (int) accent;
return velocity;
}
protected float getHighestValue() throws InterruptedException
{
float highest;
float[] value = new float[20];
String txt = "";
for(int i = 0; i < value.length; i++)
{
Thread.sleep(1);
value[i] = accelerometer_z;
txt += "Reading at: " + i + " ms returns: " + value[i] + "\n";
}
Arrays.sort(value);
highest = value[19];
textView2.setText(txt);
return highest;
}
The float accelerometer_z is initialized in my main activity and is updated continually through onSensorChanged.
My problem here is that I don't get a series of values, I only get One value from the for-loop in my getHighestValue method.
Cheers
/M
Added onSensorChanged:
#Override
public void onSensorChanged(SensorEvent event)
{
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
{
getAccelerometer(event);
}
}
private void getAccelerometer(SensorEvent event)
{
float[] value = event.values;
// Movement
accelerometer_x = value[0];
accelerometer_y = value[1];
accelerometer_z = value[2];
accelationSquareRoot = (accelerometer_x * accelerometer_x + accelerometer_y *
accelerometer_y + accelerometer_z * accelerometer_z)
/ (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH);
long actualTime = System.currentTimeMillis();
if (accelationSquareRoot >= 2)
{
if (actualTime - lastUpdate < 200)
{
return;
}
lastUpdate = actualTime;
}
}
In your for loop, you're forcing Thread to sleep with
...
Thread.sleep(1);
...
where the expected parameter(1 in your case) is in milliseconds.
I guess, your intention was not to force Thread to sleep 1 milliseconds because you want an actual value from sensor. So that should be something like:
...
Thread.sleep(x * 1000);
...
where x is defined with respect to sensitivity.
It seems that only one event handler can be run at the same time and that's the reason why I'm only getting a single value from the gethighestValue.
The onSensorChanged will never be allowed to make a new reading as long as the motionEvent in the onTouch is waiting for new input.
I solved this by letting the onSensorChanged implement a method like this:
private void getAccelerometer(SensorEvent event)
{
float[] value = event.values;
float accelerometer_x = value[0];
float accelerometer_y = value[1];
float accelerometer_z = value[2];
accReadingZ.add(accelerometer_z);
while(accReadingZ.size() > LIST_SIZE)
accReadingZ.removeFirst();
}
accReadingZ is here a class member with a size of 10 elements.
I can now use Collections.max(accReadingZ) to get the highest value in the series of readings.
Related
I really need help, I need to write ArrayList of Accelerometer Value into csv, but I got an error java lang indexoutofboundsexception invalid index 1 size is 1. Here is the code:
in here I change the sensor sampling rate within the certain time so it won't be that fast (because fast means a lot of noise):
public void SyncSensor(){
if((System.currentTimeMillis()-lastSaved) > ACCE_FILTER_DATA_MIN_TIME) {
lastSaved = System.currentTimeMillis();
//displayCleanValues();
// display the current x,y,z accelerometer values
//displayCurrentValues();
}
}
After that I want to save the value obtain into some arraylist and then write it into csv file, but this keep giving me an error:
public void onSensorChanged(SensorEvent event) {
if (isRunning) {
SyncSensor();
displayCleanValues();
displayCurrentValues();
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
//float[] values = event.values;
// get the change of the x,y,z values of the accelerometer
deltaX = Math.abs(lastX - event.values[0]);
deltaY = Math.abs(lastY - event.values[1]);
deltaZ = Math.abs(lastZ - event.values[2]);
//long timestamp = System.currentTimeMillis();
AccelData data = new AccelData( deltaX, deltaY, deltaZ);
sensorData.add(data);
lastSaved = System.currentTimeMillis();
// if the change is below 2, it is just plain noise
if (deltaX < 2)
deltaX = 0;
if (deltaY < 2)
deltaY = 0;
if ((deltaZ > vibrateThreshold) || (deltaY > vibrateThreshold) || (deltaZ > vibrateThreshold)) {
v.vibrate(50);
}
}
if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
//get the change of the x,y,z values of the accelerometer
gsx = event.values[0];
gsy = event.values[1];
gsz = event.values[2];
}
try {
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
for (int i = 0; i < sensorData.size(); i++) {
//long time = sensorData.get(i).getTimestamp();
float x = sensorData.get(i).getX(), y = sensorData.get(i).getY(), z = sensorData.get(i).getZ();
writer.write(String.format("%d; ACC; %f; %f; %f\n", event.timestamp, x, y, z));
break;
}
case Sensor.TYPE_GYROSCOPE:
writer.write(String.format("%d; GYRO; %f; %f; %f\n", event.timestamp, gsx, gsy, gsz));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
and here is the AccelData class:
public class AccelData {
//public long timestamp;
public float x;
public float y;
public float z;
public AccelData( float x, float y, float z){
this.x = x;
this.y = y;
this.z = z;
}
//public long getTimestamp(){
// return timestamp;
//}
public float getX(){
return x;
}
public float getY(){
return y;
}
public float getZ(){
return z;
}
}
Can someone help me?
Due to the power consuming GPS data, I would like to calculate the device speed with only the accelerometer x,y and z data. I have read a lot of questions about this topic and I tried many set-ups to find a satisfactory solution to calculate the speed when my device is in my car.
It seems so simple but nothing works, which drives me crazy.
Been trying the Sensor.TYPE_LINEAR_ACCELERATION and the Sensor.TYPE_ACCELEROMETER with removed gravity. Tried a Low Pass Filter on the Linear acceleration data. Unfortunately all with no succes.
Looks like the calculated speed is correct but testing in my car the calculated speed doesn't get higher then about 2 m/s.
below a code snip
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
public void onSensorChanged(SensorEvent event) {
if (event.sensor == mAccelerometer) {
if (timestamp != 0) {
final float dT = (event.timestamp - timestamp) * NS2S;
lax = event.values[0];
lay = event.values[1];
laz = event.values[2];
vx = vxo + lax * dT ;
vy = vyo + lay * dT ;
vz = vzo + laz * dT ;
speed = (float) (Math.sqrt(vx*vx + vy*vy + vz*vz)) ;
if (speed < 0.01) {speed = 0 ; }
tv_speed.setText(String.valueOf(speed));
}
timestamp = event.timestamp;
}
}
Hope someone can help, thanks a lot.
It's possible to compute distance and speed using only accelerometer, but with three conditions:
1. Linear movement - trajectory must be straight.
2. Slope of the road must be constant.
3. You must perform calibration procedure before start.
Where can you use this method with such restrictions - it's up to you... Now, how to do it:
We need something, implementing SensorEventListener interface. For the future use, let's add following abstract class:
public abstract class Accelerometer implements SensorEventListener {
protected float lastX;
protected float lastY;
protected float lastZ;
public abstract Point getPoint();
public void onAccuracyChanged(Sensor arg0, int arg1) {
}
}
and this will be our SensorEventListener:
public class XYZAccelerometer extends Accelerometer {
private static final int BUFFER_SIZE = 500;
// calibration
private float dX = 0;
private float dY = 0;
private float dZ = 0;
// buffer variables
private float X;
private float Y;
private float Z;
private int cnt = 0;
// returns last SenorEvent parameters
public Point getLastPoint(){
return new Point(lastX, lastY, lastZ, 1);
}
// returrns parameters, using buffer: average acceleration
// since last call of getPoint().
public Point getPoint(){
if (cnt == 0){
return new Point(lastX, lastY, lastZ, 1);
}
Point p = new Point(X, Y, Z, cnt);
reset();
return p;
}
// resets buffer
public void reset(){
cnt = 0;
X = 0;
Y = 0;
Z = 0;
}
public void onSensorChanged(SensorEvent se) {
float x = se.values[SensorManager.DATA_X] + dX;
float y = se.values[SensorManager.DATA_Y] + dY;
float z = se.values[SensorManager.DATA_Z] + dZ;
lastX = x;
lastY = y;
lastZ = z;
X+= x;
Y+= y;
Z+= z;
if (cnt < BUFFER_SIZE-1) {
cnt++;
} else
{
reset();
}
}
public int getCnt(){
return cnt;
}
public void setdX(float dX) {
this.dX = dX;
}
public void setdY(float dY) {
this.dY = dY;
}
public void setdZ(float dZ) {
this.dZ = dZ;
}
}
Calibrating accelerometer must be called before each experiment. Phone orientation must not be changed while measuring.
To calibrate accelerometer, i use this class:
public class Calibrator {
final static int UPDATE_INTERVAL = 400;
final static int ITERATIONS = 5;
Handler hRefresh;
XYZAccelerometer acc;
int eventNumber;
private LinkedList calData;
public Calibrator(Handler hRefresh, XYZAccelerometer acc, int eventNumber) {
this.hRefresh = hRefresh;
this.acc = acc;
this.eventNumber = eventNumber;
}
public void calibrate() {
final Timer calTimer = new Timer();
calData = new LinkedList();
acc.setdX(0);
acc.setdY(0);
acc.setdZ(0);
calTimer.scheduleAtFixedRate(
new TimerTask() {
public void run() {
addCalData(calData);
if (calData.size() > ITERATIONS) {
calTimer.cancel();
try {
calSensor(calData);
} catch (Exception ex) {
try {
throw ex;
} catch (Exception ex1) {
hRefresh.sendEmptyMessage(5);
}
}
hRefresh.sendEmptyMessage(eventNumber);
}
}
},
0,
UPDATE_INTERVAL);
}
private void addCalData(LinkedList cD) {
Point p = acc.getPoint();
cD.add(p);
acc.reset();
}
private void calSensor(LinkedList cD) throws Exception {
if (cD.size() < ITERATIONS-1) {
throw new Exception("not enough data to calibrate");
}
float x = 0;
float y = 0;
float z = 0;
// Don't use first measure
for (int i = 1; i < cD.size(); ++i) {
x += cD.get(i).getX();
y += cD.get(i).getY();
z += cD.get(i).getZ();
}
x = x / (cD.size() - 1);
y = y / (cD.size() - 1);
z = z / (cD.size() - 1);
acc.setdX(-x);
acc.setdY(-y);
acc.setdZ(-z);
}
}
maintenance class to keep data of one measure
public class Point {
private float x = 0;
private float y = 0;
private float z = 0;
private int cnt = 1;
public float getX() {
return x/(float)cnt;
}
public float getY() {
return y/(float)cnt;
}
public float getZ() {
return z/(float)cnt;
}
public Point(float x, float y, float z, int cnt) {
this.x = x;
this.y = y;
this.z = z;
this.cnt = cnt;
}
public float getForce(){
return getX()*getX()+getY()*getY()+getZ()*getZ();
}
}
And class to process data of measure
public class MeasurePoint {
private float x;
private float y;
private float z;
private float speedBefore;
private float speedAfter;
private float distance;
private float acceleration;
private long interval;
private Point averagePoint;
public MeasurePoint(float x, float y, float z, float speedBefore, long interval, Point averagePoint) {
this.x = x;
this.y = y;
this.z = z;
this.speedBefore = speedBefore;
this.interval = interval;
this.averagePoint = averagePoint;
speedAfter = 0;
calc();
}
private void calc(){
//Acceleration as projection of current vector on average
acceleration = this.x*averagePoint.getX() +
this.y*averagePoint.getY() +
this.z*averagePoint.getZ();
acceleration = acceleration / ((float)Math.sqrt(averagePoint.getForce()));
float t = ((float)interval / 1000f);
speedAfter = speedBefore + acceleration * t;
distance = speedBefore*t + acceleration*t*t/2;
}
public String getStoreString(){
String s = "write here whatever you want";
return s;
}
// add getters
}
This one - to store and save data array
public class MeasureData {
// points from accelerometr
private LinkedList accData;
private LinkedList data;
// timer interval of generating points
private long interval;
public MeasureData(long interval) {
this.interval = interval;
accData = new LinkedList ();
data = new LinkedList ();
}
public void addPoint(Point p){
accData.add(p);
}
public void process(){
for(int i = 0; i < accData.size(); ++i){
Point p = accData.get(i);
float speed = 0;
if(i > 0){
speed = data.get(i-1).getSpeedAfter();
}
data.add(new MeasurePoint(p.getX(), p.getY(), p.getZ(), speed, interval, getAveragePoint()));
}
}
public boolean saveExt(Context con, String fname) throws Throwable {
try {
File file = new File(con.getExternalFilesDir(null), fname);
FileOutputStream os = new FileOutputStream(file);
OutputStreamWriter out = new OutputStreamWriter(os);
for (int i = 0; i < data.size(); ++i) {
MeasurePoint m = data.get(i);
out.write(m.getStoreString());
}
out.close();
} catch (Throwable t) {
throw (t);
}
return true;
}
private Point getAveragePoint() {
float x = 0;
float y = 0;
float z = 0;
for(int i = 0; i < accData.size(); ++i){
Point p = accData.get(i);
x += p.getX();
y += p.getY();
z += p.getZ();
}
return new Point(x, y, z, 1);
}
public float getLastSpeed(){
return data.getLast().getSpeedAfter();
}
public float getLastSpeedKm(){
float ms = getLastSpeed();
return ms*3.6f;
}
}
And, finally, how to use all this in your activity(I cleaned it up a lot, sorry if it will not complie - fill free to write it in comments:
public class TestActivity extends Activity {
static final int TIMER_DONE = 2;
static final int START = 3;
static final int CAL_TIMER_DONE = 4;
static final int ERROR = 5;
private StartCatcher mStartListener;
private XYZAccelerometer xyzAcc;
private SensorManager mSensorManager;
private static final long UPDATE_INTERVAL = 500;
private static final long MEASURE_TIMES = 20;
private Timer timer;
private TextView tv;
private Button testBtn;
int counter;
private MeasureData mdXYZ;
/** handler for async events*/
Handler hRefresh = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TIMER_DONE:
onMeasureDone();
String es1 = Float.toString(Math.round(mdXYZ.getLastSpeedKm()*100)/100f);
tv.append(" END SPEED " + es1 + " " + es2 + " \n");
enableButtons();
break;
case START:
tv.append(" START");
timer = new Timer();
timer.scheduleAtFixedRate(
new TimerTask() {
public void run() {
dumpSensor();
}
},
0,
UPDATE_INTERVAL);
break;
case ERROR:
Toast.makeText(getApplicationContext(), "ERROR", Toast.LENGTH_SHORT).show();
break;
}
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv = (TextView) findViewById(R.id.txt);
testBtn = (Button) findViewById(R.id.btn);
}
#Override
protected void onResume() {
super.onResume();
tv.append("\n ..");
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
setAccelerometer();
setStartCatcher();
mSensorManager.registerListener(xyzAcc,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_GAME);
}
#Override
protected void onPause() {
mSensorManager.unregisterListener(xyzAcc);
super.onPause();
}
public void onButtonTest(View v) {
disableButtons();
mdXYZ = new MeasureData(UPDATE_INTERVAL);
counter = 0;
tv.setText("");
tv.append("Calibrating");
Calibrator cal = new Calibrator(hRefresh, xyzAcc, START);
cal.calibrate();
}
void dumpSensor() {
++counter;
mdXYZ.addPoint(xyzAcc.getPoint());
hRefresh.sendEmptyMessage(TICK);
if (counter > MEASURE_TIMES) {
timer.cancel();
hRefresh.sendEmptyMessage(TIMER_DONE);
}
}
private void enableButtons() {
testBtn.setEnabled(true);
}
private void setAccelerometer() {
xyzAcc = new XYZAccelerometer();
mSensorManager.registerListener(xyzAcc,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI);
}
private void disableButtons() {
testBtn.setEnabled(false);
}
private void onMeasureDone() {
try {
mdXYZ.process();
long now = System.currentTimeMillis();
mdXYZ.saveExt(this, Long.toString(now) + ".csv");
} catch (Throwable ex) {
Toast.makeText(this, ex.getMessage().toString(), Toast.LENGTH_SHORT);
}
}
}
<serviceLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<serviceButton
android:id="#+id/btn"
android:text="TEST"
android:layout_width="300px"
android:layout_height="200px"
android:onClick="onButtonTest" />
<serviceTextView
android:id = "#+id/txt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=":"
/>
<service/LinearLayout>
Although at the developer.android.com site it is stated clearly that
you could use the linear accelerometer to see how fast your car is going"
But I did not find anything (in testing or in examples or code) that showed this is really true.
Now, unfortunately, I am convinced that it is not possible to calculate the speed of a car with the linear accelerometer.
Please see the below code to get the velocity using accelerometer
public class SensorTestActivity extends Activity implements SensorEventListener {
double calibration = Double.NaN;
private SensorManager sensorManager;
private boolean color = false;
private TextView view;
private long lastUpdate;
float appliedAcceleration = 0;
float currentAcceleration = 0;
float velocity = 0;
Date lastUpdatedate;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
view = findViewById(R.id.textView);
// view.setBackgroundColor(Color.GREEN);
lastUpdatedate = new Date(System.currentTimeMillis());
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
lastUpdate = System.currentTimeMillis();
}
#Override
public void onSensorChanged(SensorEvent event) {
// if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// getAccelerometer(event);
// }
double x = event.values[0];
double y = event.values[1];
double z = event.values[2];
double a = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
if (calibration == Double.NaN)
calibration = a;
else {
updateVelocity();
currentAcceleration = (float)a;
}
}
private void getAccelerometer(SensorEvent event) {
float[] values = event.values;
// Movement
float x = values[0];
float y = values[1];
float z = values[2];
float accelationSquareRoot = (x * x + y * y + z * z)
/ (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH);
long actualTime = event.timestamp;
if (accelationSquareRoot >= 2) //
{
if (actualTime - lastUpdate < 200) {
return;
}
lastUpdate = actualTime;
// Toast.makeText(this, "Device was shuffed", Toast.LENGTH_SHORT)
// .show();
if (color) {
view.setBackgroundColor(Color.GREEN);
} else {
view.setBackgroundColor(Color.RED);
}
color = !color;
view.setText("SPEEDDDDD=== "+accelationSquareRoot);
// Log.i("SensorTestActivity","SPEEDDDDD=== "+accelationSquareRoot+" ");
}
}
private void updateVelocity() {
// Calculate how long this acceleration has been applied.
Date timeNow = new Date(System.currentTimeMillis());
long timeDelta = timeNow.getTime()-lastUpdatedate.getTime();
lastUpdatedate.setTime(timeNow.getTime());
// Calculate the change in velocity at the
// current acceleration since the last update.
float deltaVelocity = appliedAcceleration * (timeDelta/1000);
appliedAcceleration = currentAcceleration;
// Add the velocity change to the current velocity.
velocity += deltaVelocity;
final double mph = (Math.round(100*velocity / 1.6 * 3.6))/100;
Log.i("SensorTestActivity","SPEEDDDDD=== "+mph+" "+velocity);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
protected void onResume() {
super.onResume();
// register this class as a listener for the orientation and
// accelerometer sensors
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onPause() {
// unregister listener
super.onPause();
sensorManager.unregisterListener(this);
}
}
In my Android game I have implemented a custom code in the onTouchEvent method of a (custom) SurfaceView to emulate a ScrollView. I already tried an actual ScrollView, but due to performance and lack of customizability I preferred to override onTouchEvent myself.
It works perfectly, but I cannot properly emulate the inertia effect typical of scrollviews.
What I did right now is this:
int beginRawY = 0;
int beginScrollValue = 0;
Integer firstPointerId = null;
[...]
private boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
int rawY = (int) event.getRawY();
// This is meant to deal with multitouch: scroll only if I'm using "the same finger".
boolean rightPointer = firstPointerId != null && firstPointerId == pointerId;
switch (action) {
case MotionEvent.ACTION_DOWN:
initVelocityTracker(event);
if(firstPointerId == null) {
// Save initial scrollValue and touch position
beginRawY = rawY;
beginScrollValue = scrollValue;
firstPointerId = pointerId;
return true;
}
case MotionEvent.ACTION_MOVE:
if(rightPointer) {
updateVelocity(event);
// Updates scroll value to new scroll value
scrollValue = beginScrollValue -rawY + beginRawY;
return true;
}
case MotionEvent.ACTION_UP:
if(rightPointer) {
//Start the Dissipator runnable, passing to it the speed of the player's touch (number of pixels in 30 milliseconds)
dissipator.setVelocity(updateVelocity(event).pixelY());
dissipator.run();
firstPointerId = null;
return true;
}
}
return false;
}
private void initVelocityTracker(MotionEvent event) {
if(mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the velocity of a motion.
mVelocityTracker = VelocityTracker.obtain();
}
else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(event);
}
private PixelDot updateVelocity(MotionEvent event) {
int pointerId = event.getPointerId(event.getActionIndex());
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(30);
return new PixelDot(
VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId),
VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId));
}
[...]
private class DissipatorRunnable implements Runnable {
private float velocity = 0;
public void setVelocity(float velocity) {
this.velocity = velocity;
}
#Override
public void run() {
// Simply linear descreasing of the scroll speed
float vValue = velocity;
if(velocity > 0) {
while (vValue > 0 && scrollValue > 0) {
scrollValue = scrollValue - vValue;
vValue -= 5;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
while (vValue < 0 && scrollValue < height) {
scrollValue = scrollValue - vValue;
vValue += 5;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Code is simplier than it looks:
I use a ScrollValue to represent "how much the player scrolled". It is the number of pixels.
On Touch Down a save th eposition of the finger and the actual scrollvalue. I initialize a VelocityTracker too.
On Touch Move I update the velocity tracker and the scrollvalue.
On Touch Up I start a custom Runnable called Dissipator:
Every tot milliseconds I reduce (or increment) scrollvalue by the last tracked velocity, and then reduce the velocity value.
This kinda works, but the effect doesn't look like a scrollview with proper inertia, inertia is instead silly and too strong.
What should I do to emulate standard scrollviews inertia?
I'm writing Fall Detection application as a project for studies. I'm using accelerometer to detect impacts such as falls, jumps, and shakes, then I'm using barometer to check the height. If the height is bigger then 0.5 m between standing posture and lying posture after impact, it should detect it as fall.
I have a problem with saving barometer's pressure value before Impact from accelerometer and compare it with current pressure to see the difference.
Now I'm saving pressure value when Impact is detected and then later when currentstate is active or inactive. I need this to check the height and to see if the fall has really happend. Here is the code:
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
double ax = event.values[0];
double ay = event.values[1];
double az = event.values[2];
double acceleration = getMagnitude(ax, ay, az);
pushAcceleration(acceleration);
setKinematicState(accelerationFrame, ay);
/*
Checking Barometers Height in case of Falling
*/
if (currentState != previousState) {
if (currentState == KinematicState.FALL) {
PressuredataObject barometer;
barometer = pressuredataObjects.get(pressuredataObjects.size() - 1);
beforeFallAltitude = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, barometer.getAirPressure());
impactDetected = true;
}
if ((currentState == KinematicState.INACTIVE || currentState == KinematicState.ACTIVE) && impactDetected) {
PressuredataObject barometer;
barometer = pressuredataObjects.get(pressuredataObjects.size() - 1);
afterFallAltitude = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, barometer.getAirPressure());
float diffAltitude = beforeFallAltitude - afterFallAltitude;
float abs_diffAltitude = (diffAltitude < 0) ? -diffAltitude : diffAltitude;
if (abs_diffAltitude > 0.5) {
Log.d("abs_diffAltitude", "" + abs_diffAltitude);
state.setText("FALL DETECTED");
state.setTextColor(Color.parseColor("#FF0000"));
fallPlayer.start();
showDialog();
}
impactDetected = false;
}
systemState(currentState);
previousState = currentState;
}
}
if (event.sensor.getType() == Sensor.TYPE_PRESSURE) {
float[] values = event.values;
pressuredataObjects.add(new PressuredataObject(values[0], 0f, System.currentTimeMillis()));
if (pressuredataObjects.size() > BUFF_SIZE)
pressuredataObjects.remove(0);
PressuredataObject lastMeasure = pressuredataObjects.get(pressuredataObjects.size() - 1);
PressuredataObject medianValue = PressureUtilities.selectMedianValue(pressuredataObjects);
// Calculate speed and altitude
float speed = 0f;
float altitude = 0f;
if (pdoPrevious == null) {
medianValue.setSpeed(0);
} else {
speed = lastMeasure.getSpeed();
altitude = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, lastMeasure.getAirPressure());
}
pdoPrevious = medianValue;
}
}
And here you can see the functions for accelerometer
private void setKinematicState(List<Double> buffer, double accelerationY) {
int zrc = getMagnitudeValues(buffer);
if (zrc <= 3) {
currentState = KinematicState.INACTIVE;
} else if (zrc > 3 && zrc < 6) {
currentState = KinematicState.ACTIVE;
}
else if (zrc > 6) {
currentState = KinematicState.FALL;
}
}
private int getMagnitudeValues(List<Double> accelerationFrame) {
int count = 0;
for (int i = 1; i < accelerationFrame.size(); i++) {
if ((accelerationFrame.get(i - 1) - GRAVITY_ACC) < APPROXIMATION_ERROR
&& (accelerationFrame.get(i) - GRAVITY_ACC) > APPROXIMATION_ERROR) {
count++;
}
}
return count;
}
private double getMagnitude(double accelerationX, double accelerationY, double accelerationZ) {
return Math.sqrt(accelerationX * accelerationX
+ accelerationY * accelerationY + accelerationZ * accelerationZ);
}
private void pushAcceleration(double acceleration) {
accelerationFrame.add(acceleration);
if (accelerationFrame.size() > BUFF_SIZE)
accelerationFrame.remove(0);
}
Does anyone has any idea how to save previous pressure value just before the impact from accelerometer?
I need suggestion about how to detect the amount of movement of an android device. Suppose I have put the phone on a table or bed and then if somebody taps the table or sits or laydown on the bed then I want to detect the movement of the android device.
Actually I know that android has motion sensors APIs but I don't know which sensor to use and what sensor type is best for this type of movement detection.
I would be glad if someone can share some basic demo code.
Definitely work with the accelerometer:
// Start with some variables
private SensorManager sensorMan;
private Sensor accelerometer;
private float[] mGravity;
private float mAccel;
private float mAccelCurrent;
private float mAccelLast;
// In onCreate method
sensorMan = (SensorManager)getSystemService(SENSOR_SERVICE);
accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mAccel = 0.00f;
mAccelCurrent = SensorManager.GRAVITY_EARTH;
mAccelLast = SensorManager.GRAVITY_EARTH;
// And these:
#Override
public void onResume() {
super.onResume();
sensorMan.registerListener(this, accelerometer,
SensorManager.SENSOR_DELAY_UI);
}
#Override
protected void onPause() {
super.onPause();
sensorMan.unregisterListener(this);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
mGravity = event.values.clone();
// Shake detection
float x = mGravity[0];
float y = mGravity[1];
float z = mGravity[2];
mAccelLast = mAccelCurrent;
mAccelCurrent = FloatMath.sqrt(x*x + y*y + z*z);
float delta = mAccelCurrent - mAccelLast;
mAccel = mAccel * 0.9f + delta;
// Make this higher or lower according to how much
// motion you want to detect
if(mAccel > 3){
// do something
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// required method
}
I used the following class:
public class MovementDetector implements SensorEventListener {
protected final String TAG = getClass().getSimpleName();
private SensorManager sensorMan;
private Sensor accelerometer;
private MovementDetector() {
}
private static MovementDetector mInstance;
public static MovementDetector getInstance() {
if (mInstance == null) {
mInstance = new MovementDetector();
mInstance.init();
}
return mInstance;
}
//////////////////////
private HashSet<Listener> mListeners = new HashSet<MovementDetector.Listener>();
private void init() {
sensorMan = (SensorManager) GlobalData.getInstance().getContext().getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
}
public void start() {
sensorMan.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
public void stop() {
sensorMan.unregisterListener(this);
}
public void addListener(Listener listener) {
mListeners.add(listener);
}
/* (non-Javadoc)
* #see android.hardware.SensorEventListener#onSensorChanged(android.hardware.SensorEvent)
*/
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float diff = (float) Math.sqrt(x * x + y * y + z * z);
if (diff > 0.5) // 0.5 is a threshold, you can test it and change it
Log.d(TAG,"Device motion detected!!!!");
for (Listener listener : mListeners) {
listener.onMotionDetected(event, diff);
}
}
}
/* (non-Javadoc)
* #see android.hardware.SensorEventListener#onAccuracyChanged(android.hardware.Sensor, int)
*/
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
public interface Listener {
void onMotionDetected(SensorEvent event, float acceleration);
}
}
Usage:
On my activity onCrate():
MovementDetector.getInstance().addListener(new MovementDetector.Listener() {
#Override
public void onMotionDetected(SensorEvent event, float acceleration) {
mMotionDetectionTextView.setText("Acceleration: ["+String.format("%.3f",event.values[0])+","+String.format("%.3f",event.values[1])+","+String.format("%.3f",event.values[2])+"] "+String.format("%.3f", acceleration));
if (acceleration > SettingsHelper.getInstance().getMotionDetectionThreshold()){
mMotionDetectionTextView.setTextColor(Color.RED);
} else {
mMotionDetectionTextView.setTextColor(Color.WHITE);
}
}
});
On my activity onResume():
MovementDetector.getInstance().start();
On my activity onPause():
MovementDetector.getInstance().stop();
This code is for walking detection (Modified from #anthropomo code)
to get smoother value.
// initialize
private SensorManager sensorMan;
private Sensor accelerometer;
private float[] mGravity;
private double mAccel;
private double mAccelCurrent;
private double mAccelLast;
private boolean sensorRegistered = false;
// onCreate
sensorMan = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mAccel = 0.00f;
mAccelCurrent = SensorManager.GRAVITY_EARTH;
mAccelLast = SensorManager.GRAVITY_EARTH;
sensorMan.registerListener(this, accelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
sensorRegistered = true;
// onSensorChanged
private int hitCount = 0;
private double hitSum = 0;
private double hitResult = 0;
private final int SAMPLE_SIZE = 50; // change this sample size as you want, higher is more precise but slow measure.
private final double THRESHOLD = 0.2; // change this threshold as you want, higher is more spike movement
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity = event.values.clone();
// Shake detection
double x = mGravity[0];
double y = mGravity[1];
double z = mGravity[2];
mAccelLast = mAccelCurrent;
mAccelCurrent = Math.sqrt(x * x + y * y + z * z);
double delta = mAccelCurrent - mAccelLast;
mAccel = mAccel * 0.9f + delta;
if (hitCount <= SAMPLE_SIZE) {
hitCount++;
hitSum += Math.abs(mAccel);
} else {
hitResult = hitSum / SAMPLE_SIZE;
Log.d(TAG, String.valueOf(hitResult));
if (hitResult > THRESHOLD) {
Log.d(TAG, "Walking");
} else {
Log.d(TAG, "Stop Walking");
}
hitCount = 0;
hitSum = 0;
hitResult = 0;
}
}
}
I have been working with a similar idea to measure the displacement of the phone. I have found that the LINEAR ACCELERATION (and ACCELERATION) are not accurate enough to correctly measure the displacement.
This code should work a little better:
(ititialize)
private SensorManager sensorManager;
private Sensor accelerometer;
double[] maxAccelerations = new double[3];
double[] position = new double[3];
long[] times = new long[3];
// time combined with maxAcceleration can approximate the change in position,
// with the formula Δpos = (maxAcceleration * time ^ 2) / 6
long currentTime;
(onCreate)
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION) != null) {
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
sensorManager.registerListener(this, accelerometer, sensorManager.SENSOR_DELAY_FASTEST);
}
currentTime = System.currentTimeMillis();
for(int i=0;i<3;i++){
times[i]=currentTime;
}
else{
throw "Error";
//Which will throw an error, if not the error that is expected. 😉
}
(onSensorChanged and onAccuracyChanged)
#Override
public void onAccuracyChanged(Sensor ignore, int thisFunction) {
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
for(int i=0;i<3;i++){
if(Math.abs(event.values[i])<0.01){
// Note: this is to try to prevent accelerating time from being counted when the phone is stationary. 0.01 should be
// changed to an appropriate sensitivity level that can be calculated by finding an average noise level when the phone is stationary.
times[i]=System.currentTimeMillis();
}
if(event.values[i]>maxAccelerations[i] && maxAccelerations[i]>=0){
maxAccelerations[i]=event.values[i];
}
else if(event.values[i]<maxAccelerations[i] && maxAccelerations[i]<=0){
maxAccelerations[i]=event.values[i];
}
else if(event.values[i]>0 && maxAccelerations[i]<0){
currentTime = System.currentTimeMillis();
position[i]+=maxAccelerations[i] * (times[i]-currentTime)*(times[i]-currentTime) / 6;
times[i]=currentTime;
maxAccelerations[i]=event.values[i];
}
else if(event.values[i]<0 && maxAccelerations[i]>0){
currentTime = System.currentTimeMillis();
position[i]+=maxAccelerations[i] * (times[i]-currentTime)*(times[i]-currentTime) / 6;
times[i]=currentTime;
maxAccelerations[i]=event.values[i];
}
}
}
}
While I don't have demo code (since you aren't specific enough), a good start is here: http://developer.android.com/guide/topics/sensors/sensors_motion.html (and other items on the left).
if you are trying to find the displacement of your phone, you need to find the
Linear acceleration acting on your phone rather than the acceleration due to gravity
android has a built in converter to find the LINEAR ACCELERATION acting on your mobile phone
https://github.com/yuvaramsingh94/AndroidSensorTestCode/tree/master
this is a code where you can see how to get the raw value of LINEAR ACCELERATION