Why is this bearing calculation so inacurate? - java

Is it even that inaccurate? I re-implented the whole thing with Apfloat arbitrary precision and it made no difference which I should have known to start with!!
public static double bearing(LatLng latLng1, LatLng latLng2) {
double deltaLong = toRadians(latLng2.longitude - latLng1.longitude);
double lat1 = toRadians(latLng1.latitude);
double lat2 = toRadians(latLng2.latitude);
double y = sin(deltaLong) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(deltaLong);
double result = toDegrees(atan2(y, x));
return (result + 360.0) % 360.0;
}
#Test
public void testBearing() {
LatLng first = new LatLng(36.0, 174.0);
LatLng second = new LatLng(36.0, 175.0);
assertEquals(270.0, LatLng.bearing(second, first), 0.005);
assertEquals(90.0, LatLng.bearing(first, second), 0.005);
}
The first assertion in the test gives this:
java.lang.AssertionError:
expected:<270.0> but
was:<270.29389750911355>
0.29 seems to quite a long way off? Is this the formula i chose to implement?

If you've done what you seem to have done and done it correctly you have figured out the bearing of A from B along the shortest route from A to B which, on the surface of the spherical (ish) Earth is the arc of the great circle between A and B, NOT the arc of the line of latitude between A and B.
Mathematica's geodetic functions give the bearings, for your test positions, as 89.7061 and 270.294.
So, it looks as if (a) your calculation is correct but (b) your navigational skills need polishing up.

Are you sure this is due to numeric problems? I must admit, that I don't exactly know what you are trying to calculate, but when you dealing with angles on a sphere, small deviations from what you would expect in euclidian geometry.

java.lang.AssertionError: expected:<270.0> but was:<270.29389750911355>
This 0.29 absolute error represents a relative error of 0.1%. How is this "a long way off"?
Floats will give 7 significant digits; doubles are good for 16. Could be the trig functions or the degrees to radians conversion.
Formula looks right, if this source is to be believed.
If I plug your start and final values into that page, the result that they report is 089°42′22″. If I subtract your result from 360 and convert to degrees, minutes, and seconds your result is identical to theirs. Either you're both correct or you're both wrong.

Related

How do I calculate the sine and cosine of a double in Java?

I'm trying to convert the sine of an angle from radians to degrees and I keep getting inaccurate numbers. My code looks like this:
public class PhysicsSolverAttempt2 {
public static void main(String[] args) {
double[] numbers = {60, 30, 0};
double launchAngle = Double.parseDouble(numbers[0]);
double iV = Double.parseDouble(numbers[1]);
System.out.println(launchAngle);
double iVV = iV * Math.toDegrees(Math.sin(launchAngle));
System.out.println(Math.toDegrees(Math.sin(launchAngle)));
}
}
When I use Math.sin(launchAngle), it gives me a perfect output in radians. However, when I convert the radians to degrees using the Math.toDegrees() function at the end, it gives me -17.464362139918286, though performing the same calculation with a calculator yields the number 0.86602540378. Am I using Math.toDegrees() incorrectly, or do I need to perform extra steps to get an accurate result?
Math.sin() accepts only radians as it's input. It appears your trying to find the sin of launchAngle, which is in degrees.
Math.toDegrees(Math.sin(Math.toRadians(launchAngle)));
The sine function returns the y-coordinate of a point on the unit circle. For Math.sin, you input an angle in radians and it returns the corresponding y-coordinate. However, what you are doing here is using the result of Math.sin (a y-coordinate, not an angle) as the argument for Math.toDegrees, which is supposed to convert radians to degrees. I assume the intention was to find the sin of the launchAngle. If so, turn
Math.toDegrees(Math.sin(launchAngle))
into
Math.sin(Math.toRadians(launchAngle))
It seems like the easiest way to find an accurate measurement is to simply multiply the initial degree measurement by Math.PI/180 and to use that number for calculations.
From there, Math.sin(launchAngle) will give clean, workable results.

Android Linear Acceleration, calculating a trajectory

I'd like to use my accelerometer in my car and with the accelerometer values, draw a trajectory in excel or any other platform with the origin in the first position value, that is the beginning of the path.
How can I achieve this? Please give me details I don't have any physics notion.
Please help, thanks in advance.
PS: I already programmed the SensorListener...
I have this for instance:
#Override
public void onSensorChanged(SensorEvent event){
if(last_values != null){
float dt = (event.timestamp - last_timestamp) * NS2S;
for(int index = 0 ; index < 3 ; ++index){
acceleration[index] = event.values[index];
velocity[index] += (acceleration[index] + last_values[index])/2 * dt;
position[index] += velocity[index] * dt;
}
vxarr.add(velocity[0]);
vyarr.add(velocity[1]);
vzarr.add(velocity[2]);
axarr.add(acceleration[0]);
ayarr.add(acceleration[1]);
azarr.add(acceleration[2]);
}
else{
last_values = new float[3];
acceleration = new float[3];
velocity = new float[3];
position = new float[3];
velocity[0] = velocity[1] = velocity[2] = 0f;
position[0] = position[1] = position[2] = 0f;
}
xarr.add(position[0]);
yarr.add(position[1]);
zarr.add(position[2]);
tvX.setText(String.valueOf(acceleration[0]));
tvY.setText(String.valueOf(acceleration[1]));
tvZ.setText(String.valueOf(acceleration[2]));
last_timestamp = event.timestamp;
}
but when I draw a circle with my phone I got this:
Sometimes I have just only negative values and sometimes I have just positive values, I never have negative AND positive values in order to have circle.
Acceleration is the derivative of speed by time (in other words, rate of change of speed); speed is the derivative of position by time. Therefore, acceleration is the second derivative of position. Conversely, position is the second antiderivative of acceleration. You could take the accelerometer measurements and do the double intergration over time to obtain the positions for your trajectory, except for two problems:
1) It's an indefinite integral, i.e. there are infinitely many solutions (see e.g. https://en.wikipedia.org/wiki/Antiderivative). In this context, it means that your measurements tell you nothing about the initial speed. But you can get it from GPS (with limited accuracy) or from user input in some form (e.g. assume the speed is zero when the user hits some button to start calculating the trajectory).
2) Error accumulation. Suppose the accelerometer error in any given direction a = 0.01 m/s^2 (a rough guess based on my phone). Over t = 5 minutes, this gives you an error of a*t^2/2 = 450 meters.
So you can't get a very accurate trajectory, especially over a long period of time. If that doesn't matter to you, you may be able to use code from the other answer, or write your own etc. but first you need to realize the very serious limitations of this approach.
How to calculate the position of the device using the accelerometer values?
Physicists like to think of the position in space of an object at a given time as a mathematical function p(t) with values ( x(t), y(t), z(t) ). The velocity v(t) of that object turns out to be the first derivative of p(t), and the acceleration a(t) nicely fits in as the first derivative of v(t).
From now on, we will just look at one dimension, the other two can be treated in the same way.
In order to get the velocity from the acceleration, we have to "reverse" the operation using our known initial values (without them we would not obtain a unique solution).
Another problem we are facing is that we don't have the acceleration as a function. We just have sample values handed to us more or less frequently by the accelerometer sensor.
So, with a prayer to Einstein, Newton and Riemann, we take these values and think of the acceleration function as a lot of small lines glued together. If the sensor fires often, this will be a very good approximation.
Our problem now has become much simpler: the (indefinite) integral (= antiderivative) to a linear function
f(t) = m*t + b is F(t) = m/2 * t^2 + b*t + c, where c can be chosen to satisfy the initial condition (zero velocity in our case) .
Let's use the point-slope form to model our approximation (with time values t0 and t1 and corresponding acceleration values a0 and a1):
a(t) = a0 + (a1 – a0)/(t1 – t0) * (t – t0)
Then we get (first calculate v(t0) + "integral-between-t0-and-t-of-a", then use t1 in place of t)
v(t1) = v(t0) + (a1 + a0) * (t1 – t0) / 2
Using the same logic, we also get a formula for the position:
p(t1) = p(t0) + (v(t1) + v(t0)) * (t1 – t0) / 2
Translated into code, where last_values is used to store the old acceleration values:
float dt = (event.timestamp - last_timestamp) * NS2S;
for(int index = 0 ; index < 3 ; ++index){
acceleration[index] = event.values[index];
float last_velocity = velocity[index];
velocity[index] += (acceleration[index] + last_values[index])/2 * dt;
position[index] += (velocity[index] + last_velocity )/2 * dt;
last_values[index] = acceleration[index];
}
**EDIT: **
All of this is only useful for us as long as our device is aligned with the world's coordinate system. Which will almost never be the case. So before calculating our values like above, we first have to transform them to world coordinates by using something like the rotation matrix from SensorManager.getRotationMatrix().
There is a code snippet in this answer by Csaba Szugyiczki which shows how to get the rotation matrix.
But as the documentation on getRotationMatrix() states
If the device is accelerating, or placed into a strong magnetic field, the returned matrices may be inaccurate.
...so I'm a bit pessimistic about using it while driving a car.

Finding the distance between two points [Efficency]

Instead of typing it all out, here's a picture for you to see what I want. You know what they say, a picture is worth a thousand words.
What I have is p1, Θ, and d and speed, s of the projectile.
Speed:
From this I can deduce p2 using the equation p1.x * speed, p1.y * speed which are the co-ordenates for p2. From this I can calculate the distance using the equation |dx| + |dy|.
Distance
However, if I wasn't given speed, how would I be able to calculate the co-ordenates of p2 only using Θ, p1 and d?
Are there any other methods that would be the most efficent?
public double getDistanceTraveled() {
return Math.abs(x - oldX) + Math.abs(y - oldY);
} //use Manhattan aproach as it is more efficent than Euclidean
public double getSpeed() {
return getDistanceTraveled() / level.TICKS_PER_SECOND;
}
is what I am using at the moment. I realized that I had the variable time, so I was able to deduce p2 using this method:
d = |dx| + |dy|d = s / ts = t(|dx| + |dy|)
Not sure if this is that efficient though. Any suggestions to this problem, and just to repeat: I have the variables theta, p1, d and t and I have to find p2.
Efficency >>> Accuracy
If theta is the angle between line and y-axis.
Then you can calculate it with the following formulas.
Let P1 be the point (p1x, p1y). and P2 be the point (p2x, p2y)
p2x = p1x + d * sin(theta)
p2y = p1y + d * cos(theta)
If you use Math.sin(theta), then keep in mind that theta should be in radians. You can use Math.toRadian(degree) to get angle in radians.
You know what they say, a picture is worth a thousand words.
If you want efficiency, there's actually a much better approach than using the trig operations at all. Basically, since you already have d, the only thing you care about is the relative movement along x and y, which is defined by theta.
Knowing that, it's pretty trivial to just store unit vectors at whatever your required granularity is in a hashmap and multiply by d
After you've decided on your granularity, just pre-calculate a HashMap like so:
{0: [1, 0],
0.01: [0.99, 0.001], etc...
}
Then round the incoming number, see this question for details on making sure it matches your granularity. Then the calculation is just (in pseudo-code):
x = p1[0] + d * <HashMap>.getKey(angle)[0]
y = p2[1] + d * <HashMap>.getKey(angle)[1]
A HashMap lookup and a multiplication is WAY more efficient than even a single trig operation. Preliminary tests got me ~10x speedup but YMMV.

Finding angle between two points in Java behaving strange

I have been looking for an answer for several hours now, and have turned up nothing. If this is a duplicate, I apologize but I have been unable to find a solution to my specific problem on StackOverflow.
I have a function that finds the angle between a point and the y-axis:
public static double getAngle(float x1,float y1) {
float y_x = 0;
float y_y = 1;
float p_x = x1;
float p_y = y1;
float theta = (float)Math.atan2((p_x-y_x),(p_y-y_y));
return (float)Math.toDegrees(theta)
}
Then when I call it, i get strange behavior:
getAngle(1,1); //returns 90.00000250447816
getAngle(5,5); //returns 51.34019265119512
getAngle(10,10); //returns 48.012787449847956
getAngle(100,100); //returns 45.287917631417216
getAngle(1000,1000); //returns 45.02866072599646
I know the answer is 45. It would appear the function `getAngle(x,x) is converging on 45 as the limit of x approaches infinity. The issue is I need this function to work for values between 0.01 and 10.0
Does anybody know why the function is behaving this way and how I can get the answer I am looking for?
P.S. I initially tried using the dot-product identity acos((ax*bx+ay*by)/|a||b|) and got a similar problem
You should simply use
float theta = (float) ((Math.PI/2) - Math.atan2(y1, x1));
The reason that your getAngle(x, x) approaches the correct angle when x approaches infinity is that you were effectively computing
Math.atan2(x, x-1)
which is equivalent to
Math.atan(x / (x-1))
and hence obviously approaches the correct
Math.atan(1)
when x approaches infinity.
It seems like your math is wrong. My approach would be to find the angle to the horizontal and do 90 minus that. For example:
double theta = Math.PI/2 - Math.atan2(y1,x1); //answer in radians
Why you are wrong is because you are subtracting the vectors from each one another so that you are getting the angle of a triangle defined by the new vector. By measuring it to the X axis and subtracting it from 90 it will be a lot easier :D

Java Strange Behavior with Sin and ToRadians

I have been given the task of using java to produce a Sin table, however I seem to get some very weird results for some values of the input. I am using the below
System.out.println("| sin(" + currentPoint + ") = " + Math.sin(Math.toRadians(currentPoint)));
Where (int) currentPoint is a value in degrees (eg 90)
These are results I find weird
| sin(360) = -2.4492935982947064E-16
| sin(180) = 1.2246467991473532E-16
| sin(150) = 0.49999999999999994
| sin(120) = 0.8660254037844387
Expecting
sin(360) = 0
sin(180) = 0
sin(150) = 0.5
sin(120) = 0.866025404
Am I missing something?
You're dealing with floating point numbers, looking for exact answers isn't going to work for all values. Take a look at
What Every Computer Scientist Should Know About Floating-Point Arithmetic. You want your tests to be equivalent to your expectations within some delta. Note that the answers you're getting are pretty close. It's expressing values in bits that's biting you.
From the link:
Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. Although there are infinitely many integers, in most programs the result of integer computations can be stored in 32 bits. In contrast, given any fixed number of bits, most calculations with real numbers will produce quantities that cannot be exactly represented using that many bits. Therefore the result of a floating-point calculation must often be rounded in order to fit back into its finite representation. This rounding error is the characteristic feature of floating-point computation.
If your code was System.out.println("| sin(" + currentPoint + ") = " + Math.sin(currentPoint)); you would expect this:
sin(360) = 0.958915723
sin(180) = -0.801152636
sin(150) = -0.71487643
sin(120) = 0.580611184
In other words, the sine of 360 radians is 0.9589, but the sine of 360 degrees is 0.
EDIT:
The reason you're seeing unexpected results is just due to lack of precision in the calculations. If you just format the results so they have fewer decimal places, the rounding will take care of it. Do something like this:
System.out.printf("| sin(%d) = %.7f\n", currentPoint, Math.sin(Math.toRadians(currentPoint)));
Then you will get results closer to what you expect.
Your results are correct ... for approximation try this...
result=Math.sin(Math.toRadians(value));
result=format(result);
private double format(double value) {
return (double)Math.round(value * 1000000) / 1000000; //you can change this to round up the value(for two position use 100...)
}
As mentioned above it is not an error, just the aproximation of computer's floating point arithmetic.
To get the expected answer, as sin() & cos() are between -1, 0 , +1, try to add 1 round it to the accurancy needed and substract 1.
x = round15(Math.sin(toRad(angle))+1)-1;
where round15 is defined
public double round15(double x){
DecimalFormat twoDForm = new DecimalFormat("0.##############E0");
String str = twoDForm.format(x);
return Double.valueOf(str);
}
It works for me, hope future readers like it.
The posters above are right. The correct values you are expecting are:
Sin(360 degrees) = 0
Sin(180 degrees) = 0
Sin(150 degrees) = .5
Sin(120 degrees) = .866
The code is returning the correct answers. They just need to be rounded. Try this:
System.out.printf("%s%.3f","| sin(" + currentPoint + ") = ", (Math.sin(Math.toRadians(currentPoint))));
You can change the .3f value to different numbers if you want to improve or reduce decimal precision.
For some reason it displays the sin of 360 to be -0.00. I am sure there is a more elegant solution, but this should work.
EDIT: Beaten by seconds. Use the code above mine, it is easier to read.
Also note that Math.PI, which is a double value, is not PI, but just an approximation of PI, and Math.sin(Math.PI) gives you the double value which is the closest to actual mathematical value of sin(Math.PI).
Below is the API description of the Math.sin method. Note the part in asterix.
I would bet that the difference between your expected results and the once you get are defects of floatpoint calculation or rounding problems.
sin
public static double sin(double a)
Returns the trigonometric sine of an angle. Special cases:
* If the argument is NaN or an infinity, then the result is NaN.
* If the argument is zero, then the result is a zero with the
same sign as the argument.
A result must be within 1 **ulp** of the correctly rounded result. Results
must be semi-monotonic.
Parameters:
a - an angle, in radians.
Returns:
the sine of the argument.
You must convert the angle into radians like this
Math.sin(Math.toRadians(90)) and the result must be 1

Categories

Resources