Vertical orientation degree - Android - android

Anyone knows how to get smooth vertical orientation degree in Android?
I already tried OrientationEventListener as shown below but it's very noisy. already tried all rates, Normal, Delay, Game and Fastest, all shown the same result.
myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int arg0) {
orientaion = arg0;
Log.i("orientaion", "orientaion:" + orientaion);
}
};

So there are two things going on that can affect what you need.
Sensor delay. Android provides four different sensor delay modes: SENSOR_DELAY_UI, SENSOR_DELAY_NORMAL, SENSOR_DELAY_GAME, and SENSOR_DELAY_FASTEST, where SENSOR_DELAY_UI has the longest interval between two data points and SENSOR_DELAY_FASTEST has the shortest. The shorter the interval the higher data sampling rate (number of samples per second). Higher sampling rate gives you more "responsive" data, but comes with greater noise, while lower sampling rate gives you more "laggy" data, but more smooth.
Noise filtering. With the above in mind, you need to decide which route you want to take. Does your application need fast response? If it does, you probably want to choose a higher sampling rate. Does your application need smooth data? I guess this is obviously YES given the context of the question, which means you need noise filtering. For sensor data, noise is mostly high frequency in nature (noise value oscillates very fast with time). So a low pass filter (LPF) is generally adequate.
A simple way to implement LPF is exponential smoothing. To integrate with your code:
int orientation = <init value>;
float update_rate = <value between 0 to 1>;
myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int arg0) {
orientation = (int)(orientation * (1f - update_rate) + arg0 * update_rate);
Log.i("orientation", "orientation:" + orientation);
}
};
Larger update_value means the resulting data is less smooth, which should be intuitive: if update_value == 1f, it falls back to your original code. Another note about update_value is it depends on the time interval between updates (related to sensor delay modes). You probably can tune this value to find one works for you, but if you want to know exactly how it works, check the alpha value definition under Electronic low-pass filters -> Discrete-time realization.

I had a similar problem showing an artificial horizon on my device. The low pass filter (LPF) solved this issue.
However you need to consider when you use the orientation angle in degrees and apply the LPF on it blindly, the result is faulty when the device is in portrait mode and turned from left to ride or opposite. The reason for this is the shift between 359 and 0 degree. Therefore I recommend to convert the degree into radians and apply the LPF on the sin and cos values of the orientation angle.
Further I recommend to use a dynamic alpha or update rate for the LPF. A static value for the alpha might be perfect on your device but not on any other.
The following class filters based on radians and uses a dynamic alpha as described above:
import static java.lang.Math.*;
Filter {
private static final float TIME_CONSTANT = .297f;
private static final float NANOS = 1000000000.0f;
private static final int MAX = 360;
private double alpha;
private float timestamp;
private float timestampOld;
private int count;
private int values[];
Filter() {
timestamp = System.nanoTime();
timestampOld = System.nanoTime();
values = new int[0];
}
int filter(int input) {
//there is no need to filter if we have only one
if(values.length == 0) {
values = new int[] {0, input};
return input;
}
//filter based on last element from array and input
int filtered = filter(values[1], input);
//new array based on previous result and filter
values = new int[] {values[1], filtered};
return filtered;
}
private int filter(int previous, int current) {
calculateAlpha();
//convert to radians
double radPrev = toRadians(previous);
double radCurrent = toRadians(current);
//filter based on sin & cos
double sumSin = filter(sin(radPrev), sin(radCurrent));
double sumCos = filter(cos(radPrev), cos(radCurrent));
//calculate result angle
double radRes = atan2(sumSin, sumCos);
//convert radians to degree, round it and normalize (modulo of 360)
long round = round(toDegrees(radRes));
return (int) ((MAX + round) % MAX);
}
//dynamic alpha
private void calculateAlpha() {
timestamp = System.nanoTime();
float diff = timestamp - timestampOld;
double dt = 1 / (count / (diff / NANOS));
count++;
alpha = dt/(TIME_CONSTANT + dt);
}
private double filter(double previous, double current) {
return (previous + alpha * (current - previous));
}
}
For further readings see this discussion.

Related

mpandroidchart - How can I avoid the repeated values in Y-Axis?

I want to avoid the repeated values and the -0 values in Y-Axis, avoiding the image situation.
I have these ideas to solve this, but any solution:
Limit the zoom before having repeated values in YAxis, therefore stop the infinite zoom-in on the chart
Get the YAxis values and remove the duplicate values.
Though this is an older question I'd like to add to it for future reference. Newer versions of the library have a little known feature that resolves the duplicated labels, called granularity. This is way simpler to use than the older solutions (though to be fair, this wasn't available at the time those were posted).
You can always check the latest AxisBase Javadocs (3.0.0-beta1) for a more detailed explanation. Here are the relevant methods:
setGranularity(float granularity):
Set a minimum interval for the axis when zooming in. The axis is not
allowed to go below that limit. This can be used to avoid label
duplicating when zooming in.
setGranularityEnabled(boolean enabled):
Enabled/disable granularity control on axis value intervals. If
enabled, the axis interval is not allowed to go below a certain
granularity.
So in your case you'd need to set the granularity to 0.1f since you have one decimal point. The following snippet of code should avoid the repeated values on the axis:
YAxis yAxis = mChart.getAxisLeft();
yAxis.setGranularityEnabled(true);
yAxis.setGranularity(0.1f);
tl;dr You can do this by changing the label count in onChartScale.
First, you want your listener set up:
chart.setOnChartGestureListener(this); // set a listener ;)
You try to get the top/bottom drawn values and check what gets drawn on the screen. Apply some basic calculations, and you're set.
The following code will draw 2 labels if the zoom level gets (too) high and up to 9 otherwise:
#Override
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
final YAxis yAxis = mChart.getAxisLeft();
final Transformer transformer = mChart.getTransformer(YAxis.AxisDependency.LEFT);
// ...minor dirty hack
final PointD top = transformer.getValuesByTouchPoint(0, 0);
final PointD bottom = transformer.getValuesByTouchPoint(0, mChart.getHeight());
final int diff = (int)(top.y - bottom.y);
// draw 2-9 axis labels
final int count = Math.min(9, Math.max(diff, 2));
Log.d("scale", String.format("scale %f: diff %d and count %d", scaleY, diff, count));
// "force" the count, for there are drawing issues where none get drawn on high zoom levels (just try it out)
yAxis.setLabelCount(count, true);
}
// todo implement other interface methods
The value formatter and everything else stays the same.
And some ugly screenshot to show that it works :D
code like this:
mchart.setAutoScaleMinMaxEnabled(false);
mchart.getAxisLeft().setAxisMaxValue(10);
mchart.getAxisLeft().setAxisMinValue(5);
mchart.getAxisRight().setAxisMaxValue(10);
mchart.getAxisRight().setAxisMinValue(5);
or:
boolean a = isReady;
mchart.getAxisLeft().setFormater(new format(float b){ return "" ;})
When u get data :
mchart.setAutoScaleMinMaxEnabled(true);
mchart.getAxisLeft().resetAxisMaxValue();
mchart.getAxisLeft().resetAxisMinValue();
mchart.getAxisRight().resetAxisMaxValue();
mchart.getAxisRight().resetAxisMinValue(5);
I have no code by my hand.
You can set granularity to your required value to prevent the repetition when zoomed.
mBarChart.getAxisLeft().setGranularity(1f); // y-axis scale will be 0,1,2,3,4....
mBarChart.getAxisLeft().setGranularityEnabled(true);
If you want to set granularity dynamically, then implement IAxisValueFormatter and compare the return value to get the difference and set Granularity to that difference.
private float yAxisScaleDifference = -1;
private boolean granularitySet = false;
//say the values returned by IAxisFormatter is in hundreds: 100, 200, 300...
mBarChart.getAxisLeft.setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float v, AxisBase axisBase) {
if(!granularitySet) {
if(yAxisScaleDifference == -1) {
yAxisScaleDifference = v; //10
}
else {
float diff = v - yAxisScaleDifference; //200 - 100 = 100
if(diff >= 1000) {
yAxisLeft.setGranularity(1000f);
}
else if(diff >= 100) {
yAxisLeft.setGranularity(100f); //set to 100
}
else if(diff >= 1f) {
yAxisLeft.setGranularity(1f);
}
granularitySet =true;
}
}
return val;
}
});
Another Example:
say Y-Axis returns [1200,3400,8000,9000....]
first time: 1200
second time: 3400 - 1200 = 2200
granularity set to 1000
If the difference is not uniform you have to use array to store the differences and take the average to get the right granularity.
If you are using IAxisValueFormatter the problem might be in the conversion of float to Int when trying to access the values array.
For me the solution was to
IAxisValueFormatter numAppointmentsXAxisFormatter = new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
int index = (int)Math.ceil(value);
if(index < 0){
index = 0;
}else if(index >= numAppointmentsLabels.size()){
index = numAppointmentsLabels.size()-1;
}
return numAppointmentsLabels.get(index);
}
};
I also added +1 to the label count
chart.getXAxis().setLabelCount(numValue+1, true);
Hope it helps
I have never used MPAndroidCharts before, but just a little search here and there got me this method which I think would be useful.
public void setLabelCount(int count, boolean force) {
if (count > 25)
count = 25;
if (count < 2)
count = 2;
mLabelCount = count;
mForceLabels = force;
}
The description says that "exact specified count of labels will be drawn and evenly distributed alongside the axis". If you can make it work in your favor, you might be able to limit the zoom.
Also, there is another method-
public int getLabelCount() {
return mLabelCount;
}
This returns the number of labels on the axis. Hope this helps.
https://github.com/PhilJay/MPAndroidChart/blob/5a15715b25991e3d61d27d552f9eba45975d65e7/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java

How to implement the most cost effective shake detector

I have an app that needs to detect shake possibly whenever the user has their phone's screen on. I've found plenty of examples of how to detect shake. The example below being the most intriguing so far with use of Google code and adding in the gravity component. My question is, can this code be improved? Shake detection is pretty solid and i'm getting no false positives. I'm mostly concerned with battery life improvement.
private static final int mMinimumForce = 5;
private static final int mShakeFrequency = 500;
private static final int mMovesRequired = 4;
private float[] mGravity = { 0.0f, 0.0f, 0.0f };
private float[] mAcceleration = { 0.0f, 0.0f, 0.0f };
private static final int mXAxis = 0;
private static final int mYAxis = 1;
private static final int mZAxis = 2;
private long mCurrentTime = 0;
private long mLastTime = 0;
private int mMoveCount = 0;
private final float mAlpha = 0.8f;
public void onSensorChanged(SensorEvent event)
{
if(event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
{
return;
}
// Set linear acceleration
// Gravity components of x, y, and z acceleration
mGravity[mXAxis] = mAlpha * mGravity[mXAxis] + (1 - mAlpha) * event.values[mXAxis];
mGravity[mYAxis] = mAlpha * mGravity[mYAxis] + (1 - mAlpha) * event.values[mYAxis];
mGravity[mZAxis] = mAlpha * mGravity[mZAxis] + (1 - mAlpha) * event.values[mZAxis];
// Linear acceleration of x, y, z with gravity effect removed
mAcceleration[mXAxis] = event.values[mXAxis] - mGravity[mXAxis];
mAcceleration[mYAxis] = event.values[mYAxis] - mGravity[mYAxis];
mAcceleration[mZAxis] = event.values[mZAxis] - mGravity[mZAxis];
// Set maximum linear acceleration amongst x, y, z
float maxAcceleration = mAcceleration[mXAxis];
if (mAcceleration[mYAxis] > maxAcceleration)
{
maxAcceleration = mAcceleration[mYAxis];
}
if (mAcceleration[mZAxis] > maxAcceleration)
{
maxAcceleration = mAcceleration[mZAxis];
}
// Process shake
if (maxAcceleration > mMinimumForce)
{
Log.d(TAG, "Shake detected");
mCurrentTime = System.currentTimeMillis();
if (mLastTime == 0)
{
mLastTime = mCurrentTime;
}
long elapsedTime = mCurrentTime - mLastTime;
if (elapsedTime > mShakeFrequency)
{
mLastTime = 0;
mMoveCount = 0;
}
else
{
mMoveCount++;
if (mMoveCount > mMovesRequired)
{
Log.d(TAG, "Shake moves detected: " + mMovesRequired);
// do some work here
mLastTime = 0;
mMoveCount = 0;
}
}
}
}
Google I/O docs have great information on all of your concerns. Here's one such document.
https://dl.google.com/io/2009/pres/W_0300_CodingforLife-BatteryLifeThatIs.pdf
Your point on floating point math is correct. While your code doesn't do much as far as calculations, the constant calling of it at high frequency could tax the CPU.
Your point on accelerometer using battery. While each device is different regarding power consumption and this device doesn't use close to what the gyroscope does, it will show a marked difference if used non-stop on a full day of active screen use.
I agree that coding to the highest standards and efficiency is good regardless of if you see a marked difference in battery usage. It's just being a good citizen. If I can get 30 more minutes out of my phone, give it to me!!!
My suggestions on your code, which are really just reflective of Google's recommendations and many you're already speaking about.
Register your listener with the lowest possible polling rate.
Supplement point one with a filter at the beginning of the code to immediately return based on that poll rate not being satisfied. (Current Time - Last Time) > POLL_RATE. This is important because Android may not adhere to the poll rate registered in the listener.
Favor integer math over floating point if you can.
Investigate whether you can use AlarmManager and/or other sensors first that are more cost effective before engaging the accelerometer. I don't know if this is possible in your case, but worth checking into.

How to detect left and right tilt of an android device mounted with an accelerometer?

Lets say you have the acceleration readings in all the 3 dimensions i.e X, Y and Z. How do you infer using the readings the phone was tilted left or right? The readings get generated every 20ms.
I actually want the logic of inferring the tilt from the readings. The tilt needs to be smooth.
A tilt can be detected in a sort of diferent ways. You can take into account 1 axis, 2 axis, or the 3 axis. Depending on how accurate you want it, and how much you feel like fighting with maths.
If you use only one axis, it is quite simple. Think the mobile is completely horizontal, and you move it like this:
using just one axis, lets say, axis x, will be enough, since you can detect accurately a change in that axis position, since even any small movement will do a change in the axis.
But, if your application is only reading that axis, and the user has the phone almost vertical, the difference in x axis will be really small even rotating the phone a big angle.
Anyways,for applications that only need coarse resolution, a single-axis can be used.
Referring to basic trigonometry, the projection of the gravity vector on the x-axis produces an output acceleration equal to the sine of the angle between the accelerometer x-axis and the horizon.
This means that having the values of an axis (those are acceleration values) you can calculate the angle in which the device is.
this means that the value given to you by the sensor, is = to 9,8 * sine of the angle, so doing the maths you can get the actual angle.
But don't worry, you don't even have to do this. Since the values are more or less proportional, as you can see in the table below, you can work directly with the value of the sensor, without taking much care of what angle represents, if you don't need it to be much accurate, since a change in that value means a proportional change in the angle, so with a few test, you will find out how big should be the change in order to be relevant to you.
So, if you take the value over the time, and compare to each other, you can figure out how big the rotation was. For this,
you consider just one axis. this will be axis X.
write a function to get the difference in the sensor value for that axis between one function call, and the next
Decide a maximum time and a minimum sensor difference, that you will consider a valid movement (e.g. a big rotation is good but only if it is fast enough, and a fast movement is good only if the difference in the angle is big enough)
if you detect two measurements that accomplish those conditions, you take note of half tilt done (in a boolean for instance), and start measuring again, but now, the new reference value is the value that was considered half tilt.
if the last difference was positive, now you need a negative difference, and if the last difference was negative, now you need a positive difference; this is, coming back. so start taking values comparing the new reference value with the new values coming from the sensor, and see if one accomplish what you decided in point 3.
if you find a valid value (accomplishing value difference and time conditions ), you have a tilt. But if you dont get a good value and the time is consumed, you reset everything: let your reference value be the last one, reset the timers, reset the half-tilt-done boolean to false, and keep measuring.
I hope this is good enough for you. For sure you can find some libraries or code snippets to help you out with this, but i think is good, as you say, to know the logic of inferring the tilt from the readings
The pictures was taken from this article, wich i recomend to read if you want to improve the accuracy and consider 2 o 3 axis for the tilt
The commonsware Sensor Monitor app does a pretty good job with this. It converts the sensor readouts to X, Y, Z values on each sensor reading, so it's pretty easy from there to determine which way the device is moving.
https://github.com/commonsguy/cw-omnibus/tree/master/Sensor/Monitor
Another item worth noting (from the Commonsware book):
There are four standard delay periods, defined as constants on the
SensorManager class:
SENSOR_DELAY_NORMAL, which is what most apps would use for broad changes, such as detecting a screen rotating from portrait to
landscape
SENSOR_DELAY_UI, for non-game cases where you want to update the UI continuously based upon sensor readings
SENSOR_DELAY_GAME, which is faster (less delay) than SENSOR_DELAY_UI, to try to drive a higher frame rate
SENSOR_DELAY_FASTEST, which is the “firehose” of sensor readings, without delay
You can use the accelerometer and magnetic field sensor to accomplish this. You can call this method in your OnSensorChanged method to detect if the phone was tilt upwards. This currently only works if the phone is held horizontally. Check the actual blog post for a more complete solution.
http://www.ahotbrew.com/how-to-detect-forward-and-backward-tilt/
public boolean isTiltUpward()
{
if (mGravity != null && mGeomagnetic != null)
{
float R[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
if (success)
{
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
/*
* If the roll is positive, you're in reverse landscape (landscape right), and if the roll is negative you're in landscape (landscape left)
*
* Similarly, you can use the pitch to differentiate between portrait and reverse portrait.
* If the pitch is positive, you're in reverse portrait, and if the pitch is negative you're in portrait.
*
* orientation -> azimut, pitch and roll
*
*
*/
pitch = orientation[1];
roll = orientation[2];
inclineGravity = mGravity.clone();
double norm_Of_g = Math.sqrt(inclineGravity[0] * inclineGravity[0] + inclineGravity[1] * inclineGravity[1] + inclineGravity[2] * inclineGravity[2]);
// Normalize the accelerometer vector
inclineGravity[0] = (float) (inclineGravity[0] / norm_Of_g);
inclineGravity[1] = (float) (inclineGravity[1] / norm_Of_g);
inclineGravity[2] = (float) (inclineGravity[2] / norm_Of_g);
//Checks if device is flat on ground or not
int inclination = (int) Math.round(Math.toDegrees(Math.acos(inclineGravity[2])));
/*
* Float obj1 = new Float("10.2");
* Float obj2 = new Float("10.20");
* int retval = obj1.compareTo(obj2);
*
* if(retval > 0) {
* System.out.println("obj1 is greater than obj2");
* }
* else if(retval < 0) {
* System.out.println("obj1 is less than obj2");
* }
* else {
* System.out.println("obj1 is equal to obj2");
* }
*/
Float objPitch = new Float(pitch);
Float objZero = new Float(0.0);
Float objZeroPointTwo = new Float(0.2);
Float objZeroPointTwoNegative = new Float(-0.2);
int objPitchZeroResult = objPitch.compareTo(objZero);
int objPitchZeroPointTwoResult = objZeroPointTwo.compareTo(objPitch);
int objPitchZeroPointTwoNegativeResult = objPitch.compareTo(objZeroPointTwoNegative);
if (roll < 0 && ((objPitchZeroResult > 0 && objPitchZeroPointTwoResult > 0) || (objPitchZeroResult < 0 && objPitchZeroPointTwoNegativeResult > 0)) && (inclination > 30 && inclination < 40))
{
return true;
}
else
{
return false;
}
}
}
return false;
}
Is this what you're looking for?
public class AccelerometerHandler implements SensorEventListener
{
float accelX;
float accelY;
float accelZ;
public AccelerometerHandler(Context paramContext)
{
SensorManager localSensorManager = (SensorManager)paramContext.getSystemService("sensor");
if (localSensorManager.getSensorList(1).size() != 0)
localSensorManager.registerListener(this, (Sensor)localSensorManager.getSensorList(1).get(0), 1);
}
public float getAccelX()
{
return this.accelX;
}
public float getAccelY()
{
return this.accelY;
}
public float getAccelZ()
{
return this.accelZ;
}
public void onAccuracyChanged(Sensor paramSensor, int paramInt)
{
}
public void onSensorChanged(SensorEvent paramSensorEvent)
{
this.accelX = paramSensorEvent.values[0];
this.accelY = paramSensorEvent.values[1];
this.accelZ = paramSensorEvent.values[2];
}
}

using LinearAcceleration and time passed to get distance traveled - android [duplicate]

This question already has answers here:
How to use Accelerometer to measure distance for Android Application Development
(2 answers)
Closed 9 years ago.
I know i am opening up a can of worms with trying to get the linear motion of a device using the accelerometer, but please just humor me.
I am trying to figure out the right formula to take the Sensor.TYPE_LINEAR_ACCELEROMETER (which i believe is normal accelerometer data minus gravity) and essentially say "this much time has passed and the i have accelerated x amount since last time, so i have traveled d amount.
should be something like distanceTraveledOnX = linearAccerationOfX * TimePassed;
easy enough in the real world right? if i have been going 1 mile a minute for 10minutes then i have traveled 10 miles.. speed * time = distance
problem is im not sure what the linearAcceleration is using for unit of measure. I know my timePassed is in NanoSeconds as i am saying (in my onSensorChanged)
currentTime = System.nanoTime();//var of type (double)
timePassed = currentTime - lastTime;
lastTime = currentTime;
can someone please help me figure out the formula for translating the linearAcceleration value to a nanoSecond measurement..
thanks
EDIT
here is the code im currently using but im always getting 0 :
public void onSensorChanged(SensorEvent evt) {
if (type == Sensor.TYPE_LINEAR_ACCELERATION) {
newTime = System.currentTimeMillis()/1000;
float oldVelocity = lastTime1-lastTime0;
float newVelocity = newTime- lastTime1;
if(oldVelocity<1)oldVelocity =1;
newX = lastX1 + ((lastX1 - lastX0)/oldVelocity)*newVelocity +(evt.values[0]/2)*(newVelocity*newVelocity);
lastX0 = lastX1;
lastX1 = newX;
lastTime0 = lastTime1;
lastTime1 = newTime;
Log.v("SENSOR MAN LINEAR", "new X:"+newX);
}
}
This stuff is high school physics, and if you don't know the difference between acceleration and velocity, you'll need to review it before you have any hope here.
I can tell you this much: the linear acceleration readings from a cell phone or tablet aren't remotely precise or accurate enough to do what you want without constant correction (via gps or other methods). There is an entire field of study trying to solve this problem. I've attended conferences on it.
That said, you also need to take into account that the orientation of your device will also change, unless this is some sort of special application, e.g. the device is trapped onto a sled which can only move in one direction.
Let's assume that case, and assume that the device is strapped to your sled with the right side of the device (+X axis) aligned in the direction of travel. Let's also assume that the initial position of the sled is known (call it X0) when the program starts, and that the initial velocity is zero.
Your code looks approximately like this:
double x0; // previous position, meters
double x; // current position
double v0; // previous velocity, meters/second
double v; // current velocity
long t0; // previous time, nanoseconds
long t; // current time
public void onStart() {
x0 = getInitialPosition();
x = x0;
v0 = 0;
v = v;
t0 = System.getCurrentTime() * 1000000;
// Enable sensors; left as an exercise for the reader
}
public void onSensorChanged(SensorEvent event) {
// Assume linear acceleration is the only active sensor
double accel = event.values[0]; // X axis is our axis of acceleration
t = event.timestamp;
double dt = (t - t0) * .000001;
v = v0 + accel * dt;
x = x0 + v * dt;
t0 = t;
v0 = v;
x0 = x;
}
This is by no means a complete solution. Doing this right involves differential equations which I'm not equipped to explain here (translation: I've forgotten everything I learned in college). However, if your acceleration value is accurate enough, and your time slice is short enough, this is viable.
If you need to solve this in more than one direction, it's only slightly more complicated provided that the device never changes orientation. If it does, then you also need to capture the rotation sensor and learn about quaternions and rotation matrices.
And even if you do everything right, errors will still accumulate, so now you want some sort of correction factor based on GPS, known geometry of the environment (e.g. if you're indoors and the software has a map of the building, it can make corrections when you turn a corner), and other environmental clues such as WiFi hotspots in known locations.
You might want to read up on Kalman filters at this point.
Executive summary: this is a HARD problem in the general case, and if you solve it, there's probably fame and fortune waiting for you.
Well, the correct form, known from school, is
finalXPosition = (linearAcceleration*timePassed^2)/2+ initialVelocity*timePassed+initialXPosition
finalVelocity = initialVelocity*timePassed
chaining these chunks you'll get your theoretical values.
In practice, best results are achieved by regular calibration of initialXPosition and initialVelocity through GPS.
simple example to receive calibrated horizontal acceleration in onSensorChanged:
class Integrator {
private float position = 0f;
private float velocity = 0f;
public void setGpsPosition (float gpsPosition) {
position = gpsPosition;
}
public void setGpsVelocity (float gpsVelocity) {
velocity = gpsVelocity;
}
public void onAccelerationChangeHandler(float acceleration, float timePassed) {
position += acceleration*timePassed*timePassed/2f + velocity*timePassed;
velocity += acceleration*timePassed;
}
public float getCurrentPosition() {
return position;
}
}
usage for x-acceleration:
long lastTime = 0;
public void onSensorChanged(SensorEvent evt) {
if (evt.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
long newTime = System.currentTimeMillis();
OnAccelerationChangeHandler(evt.values[0], (newTime-lastTime)/1000);
lastTime = newTime;
}
Please, note that outside a minute scale the error makes this all meaningless w/o gps correction. Understand, that if you are walking at constant speed the sensor won't give you anything at all.

jump method in android game

I'm new in Android Game Development and I started a simple Game in that a Droid can jump over incoming boxes.
I want to call my droid.jump() method in the surface view with an onTouchEvent (just by a simple tap on the screen)
I created a class called Droid:
public class Droid {
// Log Tag for Debugging
public static final String LOG_TAG = "_1Projekt";
private Bitmap bitmap; // the actual bitmap
private int x; // the X coordinate
private int y; // the Y coordinate
private boolean touched; // if droid is touched/picked up
private Speed speed; // the speed with its directions
private long mLastTime;
public Droid(Bitmap bitmap, int x, int y) {
this.bitmap = bitmap;
this.x = x;
this.y = y;
this.speed = new Speed();
}
.......
...
And the jump() method is my problem. I would like to have a smooth jump but I don’t know how to calculate this with the current System Time.
My idea was that the droid should update it Y position every –TimePeriod- and should start with a fast velocity and then decrease it to 0 to get a smooth jump.
But I don’t know how to calculate this in my while loop.
My current jump():
public void jump() {
Log.d(LOG_TAG, "Jumping");
long now = System.currentTimeMillis();
int elapsedTime = 100;
int jump = y-30;
while(y > jump)
{
if(System.currentTimeMillis() > now + elapsedTime)
{
now = now + elapsedTime;
elapsedTime -=3;
y = y-1;
}
}
}
Up to know I only implemented the "up" part of Jump.
Thank you for your answers! Greetings DroidDude
You may want to look here (the third post):
Before the mainloop, have
//Get the current time
timeStep = System.currentTimeMillis();
And then do your stuff. Then before the loop goes back to start, have
// Hold to lock at FPS
while(System.currentTimeMillis()-timeStep < 1000/60);
Where 60 is the frames per second to run.
This method also allows you to get the difference in times after the
while loop in order to find out how much time it took to render one
frame. Using this, you could have a variable that you can multiply all
your increments by to get frame-independent speed.
For example, if you divide the number of milliseconds it took to
handle the frame by 16.666, the number will equal 1 when the program
runs at about 60 FPS. When the frame rate is lower, it takes more
milliseconds to render the frame and that factor gets bigger.
Be sure to put it BEFORE repaint however. This is the way video games
are timed also. An example is shown in my video game base applet code
snippet under JAVA.

Categories

Resources