If I have two GPS locations, say 51.507222, -0.1275 and 48.856667, 2.350833, what formula could I use to calculate the distance between the two? I've heard a lot about a haversine formula, but can't find any information about it, or how to apply it to C.
I've written the following code, however, it's very innacurate. Anybody know why? I can't figure it out. The problem comes from the function itself, but I don't know what it is.
float calcDistance(float A, float B, float C, float D)
{
float dLat;
float dLon;
dLat = (C - A);
dLon = (D - B);
dLat /= 57.29577951;
dLon /= 57.29577951;
float v_a;
float v_c;
float distance;
v_a = sin(dLat/2) * sin(dLat/2) + cos(A) * cos(C) * sin(dLon/2) * sin(dLon/2);
v_c = 2 * atan2(sqrt(v_a),sqrt(1-v_a));
distance = r * v_c;
return distance;
}
Your code is absolutely correct.
I would rename parameters A, B, C and D to lat1, lon1, lat2 and lon2.
It returns the distance in kilometers. You need to define r as 6371, roughly the radius of the earth.
What you're looking for is the great circle distance.
This page has a block of Javascript which I'm sure you could adapt easily enough in C:
var R = 6371e3; // metres
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2-lat1).toRadians();
var Δλ = (lon2-lon1).toRadians();
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
Maybee its too late, but this is the reason of the inaccurate calculation:
-> A and C (lat1 and lat2) must also be converted from degree to radians
dLat /= 57.295779513082325;
dLon /= 57.295779513082325;
lat1 /= 57.295779513082325;
lat2 /= 57.295779513082325;
regards,
omeier
Related
I'm trying to do a calculation and for some reason when I'm using float I'm getting -nan(ind) but when I'm changing the variables (x,y) to double I'm getting to right answer maybe you guys have any idea why its happening ?
Thank you
#include <stdio.h>
#include <math.h>
#define pi 3.1416
#define L1 0.5
#define L2 0.5
void main()
{
float q1[12], q2[12], q1_Degrees[12], q2_Degrees[12];
float x = 0.8;
float y = 0.6;
q2[0] = acos((pow(x, 2) + pow(y, 2) - pow(L1, 2) - pow(L2, 2)) / (2 * L1*L2));
q1[0] = atan(y / x) - atan((L2*sin(q2[0])) / (L1 + L2 * cos(q2[0])));
q1_Degrees[0] = (q1[0] * 180) / pi;
q2_Degrees[0] = (q2[0] * 180) / pi;
printf_s("q1 is = %.1f q2 is = %.1f\n\n", q1_Degrees[0], q2_Degrees[0]);
}
2 concerns
acos()
The x in acos(x) needs to be in the range [-1...1]. Outside that, the result may be NaN.
(pow(x, 2) + pow(y, 2) - pow(L1, 2) - pow(L2, 2)) / (2 * L1*L2) is prone to slight effects of computation that result in a value just outside [-1...1] even if mathematically the result should be in range.
A quick work-around:
double z = (pow(x, 2) + pow(y, 2) - pow(L1, 2) - pow(L2, 2)) / (2 * L1*L2);
if (z < -1.0) z = -1.0;
else if (z > 1.0) z = 1.0;
q2[0] = acos(z);
The issue applies to double, float, long double. The fact it "worked" with one type is no reason to believe code is robust with other values.
Note that code is calling double functions like acos(), pow() and not their float counterparts acosf(), powf(). I recommend to use double throughout unless you have a compelling reason otherwise.
atan
atan() provides a [-π/2... +π/2] radians (aka [-90...90] degrees) result.
A whole circle result of [-π... +π] radians (aka [-180...180] degrees) is available with atan2(y,x)
atan((L2*sin(q2[0])) / (L1 + L2 * cos(q2[0])))
// or
atan2(L2*sin(q2[0]), L1 + L2 * cos(q2[0]))
A better solution is to use a different form of trig manipulation that does not depend on the edge of acos(). Easiest to do if OP also posted the higher level goal of the exercise.
Some basic debugging:
First, you can narrow down your code to:
#include <stdio.h>
#include <math.h>
void main()
{
float x = 0.8;
float y = 0.6;
double q = acos((pow(x, 2) + pow(y, 2) - 0.5) * 2);
printf("q = %lf\n", q);
}
Then, it becomes obvious that either pow(x, 2) or pow(y, 2) yield slightly different results for float and double.
At this point, let's investigate the actual differences:
Between the value of (float)0.8 and the value of (double)0.8
Between the value of (float)0.6 and the value of (double)0.6
#include <stdio.h>
void main()
{
printf("(float)0.8 = %.10f\n", (float)0.8);
printf("(double)0.8 = %.10lf\n", (double)0.8);
printf("(float)0.6 = %.10f\n", (float)0.6);
printf("(double)0.6 = %.10lf\n", (double)0.6);
}
The printout is:
(float)0.8 = 0.8000000119
(double)0.8 = 0.8000000000
(float)0.6 = 0.6000000238
(double)0.6 = 0.6000000000
Does that answer your question?
You're getting accumulated roundoff which runs a bit past the domain of acos().
Simplifying your example to a minimum that shows the issue:
#include <stdio.h>
#include <math.h>
#define L1 0.5
#define L2 0.5
int main()
{
float x = 0.8;
float y = 0.6;
float acos_param = (pow(x, 2) + pow(y, 2) - pow(L1, 2) - pow(L2, 2)) / (2 * L1*L2);
float q2 = acos(acos_param);
printf("acos_param = %.9f; q2 = %.9f\n", acos_param, q2);
return 0;
}
And running this - with floats - we see:
acos_param = 1.000000119; q2 = nan
Aha: greater than 1.0 is out of range of acos so you get NaN (not a number).
Changing all the float to double we get:
acos_param = 1.000000000; q2 = 0.000000000
which is more in line with expectations.
EDIT - Expanding on comments in the comments, variadic functions in C always pass floating-point values as double, and the misleadingly-named format %f really means double, not float.
Even if you attempt to cast "down" to a float, it will get promoted again back to double before it's called, but will truncate the precision.
Try this:
#include <stdio.h>
int main()
{
double d1 = 0.8;
double d2 = (float)0.8;
printf("d1=%.9f; d2=%.9f\n", d1, d2);
return 0;
}
Which on my compiler produces:
d1=0.800000000; d2=0.800000012
Here, d1 is the full real-deal double, while d2 is the float-truncated version promoted back to double.
And in no case is the l format specifier need; %f and %lf are the same thing.
Use case is to generate a sine wave for digital synthesis, so, we need to compute all values of sin(d t) where:
t is an integer number, representing the sample number. This is variable. Range is from 0 to 158,760,000 for one hour sound of CD quality.
d is double, representing the delta of the angle. This is constant. And the range is: greater than 0 , less than pi.
Goal is to achieve high accuracy with traditional int and double data types. Performance is not important.
Naive implementation is:
double next()
{
t++;
return sin( ((double) t) * (d) );
}
But, the problem is when t increases, accuracy gets reduced because big numbers provided to "sin" function.
An improved version is the following:
double next()
{
d_sum += d;
if (d_sum >= (M_PI*2)) d_sum -= (M_PI*2);
return sin(d_sum);
}
Here, I make sure to provide numbers in range from 0 to 2*pi to the "sin" function.
But, now, the problem is when d is small, there are many small additions which decreases the accuracy every time.
The question here is how to improve the accuracy.
Appendix 1
"accuracy gets reduced because big numbers provided to "sin" function":
#include <stdio.h>
#include <math.h>
#define TEST (300000006.7846112)
#define TEST_MOD (0.0463259891528704262050786960234519968548937998410258872449766)
#define SIN_TEST (0.0463094209176730795999323058165987662490610492247070175523420)
int main()
{
double a = sin(TEST);
double b = sin(TEST_MOD);
printf("a=%0.20f \n" , a);
printf("diff=%0.20f \n" , a - SIN_TEST);
printf("b=%0.20f \n" , b);
printf("diff=%0.20f \n" , b - SIN_TEST);
return 0;
}
Output:
a=0.04630944601888796475
diff=0.00000002510121488442
b=0.04630942091767308033
diff=0.00000000000000000000
You can try an approach that is used is some implementations of fast Fourier transformation. Values of trigonometric function are calculated based on previous values and delta.
Sin(A + d) = Sin(A) * Cos(d) + Cos(A) * Sin(d)
Here we have to store and update cosine value too and store constant (for given delta) factors Cos(d) and Sin(d).
Now about precision: cosine(d) for small d is very close to 1, so there is risk of precision loss (there are only few significant digits in numbers like 0.99999987). To overcome this issue, we can store constant factors as
dc = Cos(d) - 1 = - 2 * Sin(d/2)^2
ds = Sin(d)
using another formulas to update current value
(here sa = Sin(A) for current value, ca = Cos(A) for current value)
ts = sa //remember last values
tc = ca
sa = sa * dc + ca * ds
ca = ca * dc - ts * ds
sa = sa + ts
ca = ca + tc
P.S. Some FFT implementations periodically (every K steps) renew sa and ca values through trig. functions to avoid error accumulation.
Example result. Calculations in doubles.
d=0.000125
800000000 iterations
finish angle 100000 radians
cos sin
described method -0.99936080743598 0.03574879796994
Cos,Sin(100000) -0.99936080743821 0.03574879797202
windows Calc -0.9993608074382124518911354141448
0.03574879797201650931647050069581
sin(x) = sin(x + 2N∙π), so the problem can be boiled down to accurately finding a small number which is equal to a large number x modulo 2π.
For example, –1.61059759 ≅ 256 mod 2π, and you can calculate sin(-1.61059759) with more precision than sin(256)
So let's choose some integer number to work with, 256. First find small numbers which are equal to powers of 256, modulo 2π:
// to be calculated once for a given frequency
// approximate hard-coded numbers for d = 1 below:
double modB = -1.61059759; // = 256 mod (2π / d)
double modC = 2.37724612; // = 256² mod (2π / d)
double modD = -0.89396887; // = 256³ mod (2π / d)
and then split your index as a number in base 256:
// split into a base 256 representation
int a = i & 0xff;
int b = (i >> 8) & 0xff;
int c = (i >> 16) & 0xff;
int d = (i >> 24) & 0xff;
You can now find a much smaller number x which is equal to i modulo 2π/d
// use our smaller constants instead of the powers of 256
double x = a + modB * b + modC * c + modD * d;
double the_answer = sin(d * x);
For different values of d you'll have to calculate different values modB, modC and modD, which are equal to those powers of 256, but modulo (2π / d). You could use a high precision library for these couple of calculations.
Scale up the period to 2^64, and do the multiplication using integer arithmetic:
// constants:
double uint64Max = pow(2.0, 64.0);
double sinFactor = 2 * M_PI / (uint64Max);
// scale the period of the waveform up to 2^64
uint64_t multiplier = (uint64_t) floor(0.5 + uint64Max * d / (2.0 * M_PI));
// multiplication with index (implicitly modulo 2^64)
uint64_t x = i * multiplier;
// scale 2^64 down to 2π
double value = sin((double)x * sinFactor);
As long as your period is not billions of samples, the precision of multiplier will be good enough.
The following code keeps the input to the sin() function within a small range, while somewhat reducing the number of small additions or subtractions due to a potentially very tiny phase increment.
double next() {
t0 += 1.0;
d_sum = t0 * d;
if ( d_sum > 2.0 * M_PI ) {
t0 -= (( 2.0 * M_PI ) / d );
}
return (sin(d_sum));
}
For hyper accuracy, OP has 2 problems:
multiplying d by n and maintaining more precision than double. That is answered in the first part below.
Performing a mod of the period. The simple solution is to use degrees and then mod 360, easy enough to do exactly. To do 2*π of large angles is tricky as it needs a value of 2*π with about 27 more bits of accuracy than (double) 2.0 * M_PI
Use 2 doubles to represent d.
Let us assume 32-bit int and binary64 double. So double has 53-bits of accuracy.
0 <= n <= 158,760,000 which is about 227.2. Since double can handle 53-bit unsigned integers continuously and exactly, 53-28 --> 25, any double with only 25 significant bits can be multiplied by n and still be exact.
Segment d into 2 doubles dmsb,dlsb, the 25-most significant digits and the 28- least.
int exp;
double dmsb = frexp(d, &exp); // exact result
dmsb = floor(dmsb * POW2_25); // exact result
dmsb /= POW2_25; // exact result
dmsb *= pow(2, exp); // exact result
double dlsb = d - dmsb; // exact result
Then each multiplication (or successive addition) of dmsb*n will be exact. (this is the important part.) dlsb*n will only error in its least few bits.
double next()
{
d_sum_msb += dmsb; // exact
d_sum_lsb += dlsb;
double angle = fmod(d_sum_msb, M_PI*2); // exact
angle += fmod(d_sum_lsb, M_PI*2);
return sin(angle);
}
Note: fmod(x,y) results are expected to be exact give exact x,y.
#include <stdio.h>
#include <math.h>
#define AS_n 158760000
double AS_d = 300000006.7846112 / AS_n;
double AS_d_sum_msb = 0.0;
double AS_d_sum_lsb = 0.0;
double AS_dmsb = 0.0;
double AS_dlsb = 0.0;
double next() {
AS_d_sum_msb += AS_dmsb; // exact
AS_d_sum_lsb += AS_dlsb;
double angle = fmod(AS_d_sum_msb, M_PI * 2); // exact
angle += fmod(AS_d_sum_lsb, M_PI * 2);
return sin(angle);
}
#define POW2_25 (1U << 25)
int main(void) {
int exp;
AS_dmsb = frexp(AS_d, &exp); // exact result
AS_dmsb = floor(AS_dmsb * POW2_25); // exact result
AS_dmsb /= POW2_25; // exact result
AS_dmsb *= pow(2, exp); // exact result
AS_dlsb = AS_d - AS_dmsb; // exact result
double y;
for (long i = 0; i < AS_n; i++)
y = next();
printf("%.20f\n", y);
}
Output
0.04630942695385031893
Use degrees
Recommend using degrees as 360 degrees is the exact period and M_PI*2 radians is an approximation. C cannot represent π exactly.
If OP still wants to use radians, for further insight on performing the mod of π, see Good to the Last Bit
I am writing a function in C that returns the radius of an ellipse at a given angle with a given length and width; Basically writing this calculation in C:
Unfortunately the platform does not support math.h however there are sin and cos functions built in that I can use.
How do I write this calculation in C and store it in an int?
I have tried:
int theta = 90;
int a = 164;
int b = 144;
float aa = (((a^2) * ((sin_lookup(DEG_TO_TRIGANGLE(theta)))^2)) +
((b^2) * ((cos_lookup(DEG_TO_TRIGANGLE(theta)))^2))) /
(TRIG_MAX_ANGLE^2);
float result = (a * b) / (my_sqrt(aa));
int value = (int)result;
Easy enough
int getRadius(double a, double b, double theta)
{
double s = sin(theta),
c = cos(theta);
return (a * b) / sqrt((a*a)*(s*s)+(b*b)*(c*c))
}
Though I'm not sure why you want to return an int. You'll loose a lot of precision.
The ^ operator is not the way to do powers. It's actually a bitwise XOR. This is a common mistake new (C) programmers make. math.h has a function pow() for calculating powers, but you said you can't use math.h. These values are only raised to the second power, so it's pretty easy to just multiply it manually.
I'm working on writing a C program to perform division of 2 complex numbers in Fixed Point.
I'm not able to get the fractional part from it. Below is more details on it.
I have 2 complex numbers:
N = a + ib
M = c + jd
I need to do N/M in fixed point (not using floating point)
Example values for the above complex numbers can be:
a = 1.55, b = 1.44, c = 1.24, d = 0.55
N = 1.55 + i(1.44)
M = 1.24 + j(0.55)
For converting to Fixed Point, I multiply these a, b, c and d with 2^14.
After that they become:
a = 0x6333, b = 0x5c28, c = 0x4f5c and d = 0x2333
Then to perform the N/M operation I do:
N/M = (a + ib)/(c + jd) = ((a + ib) * (c - jd)) / ((c + jd) * (c - jd))
Then for the real part alone:
(ac + bd) / (c^2 + d^2)
and so on..
The issue I'm facing is that I'm not understanding how to get the fractional part from the division.
I'm getting only the decimal part which is mostly either 1 or 0.
What is the correct way to get the fractional part? In the above example, the real part should be something like 1.47490. But I'm only able to get 1.
Can anyone please help me with the right way in doing the complex division for Fixed Point?
Thank you very much.
In fixed point division and multiplication one must notice that the result value must have also scaling factor K.
in addition / subtraction:
a * K + b * K = K * ( a + b)
in multiplication:
(a * K) * (b * K) = K^2 * (a * b) --> must compensate with 1/K
proper form: (aK * bK) / K
in division:
(a * K) / (b * K) = a / b --> must pre-multiply with K
proper form: (aK * K) / (bK)
For two complex X=a+jb, and Y=c+jd, where j=sqrt(-1), and a, b, c, d are real numbers, the division X/Y is given by
(ac+bd)/(c^2+d^2) + j(bc-ad)/(c^2+d^2)
Let K = 2^14, the binary digit for saving the fraction, as you mentioned.
Let A is the fixed number representation of a, i.e. A = a * K, and similarly let B = b*K, C=c*K, and lastly D=d*K. Assume your number is not big enough to overflow the integer of your computer, in the following calculations:
You should first calculate U = A * C + B * D, V = C * C + D * D, W = B * C - A * D
Then, you should calculate V' = V >> 14. Then, the real part of X/Y is U/V', and the imaginary part of X/Y is W/V', with both parts represented in your fixed point form.
I'm trying to convert spherical coordinates (namely latitude and longitude from a GPS device) into Cartesian coordinates. I'm following this simple conversion derived from the polar coordinates conversion equations.
Then I'm calculating the distance between the two point applying the euclidean distance but the value I'm finding is not always the same as the distance I can calculate using the haversine formula. In particular I'm noticing that given different longitudes but same latitudes leads to the same distances computed by the two algorithms, whereas having the same longitude and changing the latitude carries different values.
Here is the C code I am using:
double ComputeDistance(double lat1,double lon1, double lat2, double lon2)
{
double dlon, dlat, a, c;
dlon = lon2- lon1;
dlat = lat2 - lat1;
a = pow(sin(dlat/2),2) + cos(lat1) * cos(lat2) * pow(sin(dlon/2),2);
c = 2 * atan2(sqrt(a), sqrt(1-a));
return 6378140 * c; /* 6378140 is the radius of the Earth in meters*/
}
int main (int argc, const char * argv[]) {
double lat1 = 41.788251028649575;
double lat2 = 41.788251028649575;
double long1 = -118.1457209154;
double long2 = -118.1407209154;//just ~10 meters distant
lat1 = DEGREES_TO_RADIANS(lat1);
lat2 = DEGREES_TO_RADIANS(lat2);
long1 = DEGREES_TO_RADIANS(long1);
long2 = DEGREES_TO_RADIANS(long2);
//transform in cartesian coordinates
double x = 6378140 * cos(lat1) * cos(long1);
double y = 6378140 * cos(lat1) * sin(long1);
double x2 = 6378140 * cos(lat2) * cos(long2);
double y2 = 6378140 * cos(lat2) * sin(long2);
double dist = sqrt(pow(x2 - x, 2) + pow(y2 - y, 2));
printf("DIST %lf\n", dist);
printf("NDIST %lf\n", ComputeDistance(lat1, long1, lat2, long2));
return 0;
}
Am I doing something incorrect or there is some math behind it I am not seeing (and maybe ask this on Mathoverflow boards?). UPDATE It is not necessary to cross boards, as someone correctly pointed this conversion is not meaningful for computing the exact distance between two points (the distance between the two poles is zero). So I am reformulating it as: why at small deltas (0.0001 which corresponds to 10 mts more or less) of latitudes the distance appear to be so different from the haversine formula (20-25%)?
UPDATE 2:
As Oli Charlesworth pointed out, not considering the z axis makes this conversion a projection that does not mind the north-south difference. This is also the cause of the difference in deltas I was pointing out. In fact, in the correct transform the z is related to the latitude and if you consider it , then compute the euclidean distance between the two points (in a 3d space now), both latitude and longitude will lead to a good approximation for small deltas.
For Example for a degree of latitude the error is ~ 1.41 meters.
From this, there is no 2D map projection where distance is preserved. Calculating the distance from the 2D projection of points is useless.