The haverSine() function is the function to calculate the distance between two points on the Earth.
This function is discussed in several threads e.g. - Calculating distance between two points accurately - Syntax & Programs - Arduino Forum -
In another thread - low float precision - Science and Measurement - Arduino Forum - there was a need for a function to calculate the distance between close points (~50.. meters).
After some discussion I implemented an approximation function fastHaverSine()
This function is about 3.5 times faster than haverSine()
For distances smaller than 0.01 degree (~1km) it has an error of less than 0.01%
Find below a small sketch that compares FHA and HS for some values (not extensively)
As always comments and remarks are welcome.
//
// FILE: fastHaverSine.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: fastHaverSine is a faster function for relative short distances.
// DATE: 2015-07-18
// URL: http://forum.arduino.cc/index.php?topic=336729
//
// Released to the public domain
//
uint32_t start;
uint32_t stop;
void setup()
{
Serial.begin(115200);
Serial.println("Start ");
start = micros();
float d = HaverSine(50, 5, 50.01, 5.01);
stop = micros();
Serial.println("HAVERSINE");
Serial.print("TIME: ");
Serial.println(stop - start);
Serial.print("DIST: ");
Serial.println(d);
start = micros();
d = fastHaverSine(50, 5, 50.01, 5.01);
stop = micros();
Serial.println("\nFASTHAVERSINE");
Serial.print("TIME: ");
Serial.println(stop - start);
Serial.print("DIST: ");
Serial.println(d);
Serial.println("\n(50, 5, 51, 5)");
d = HaverSine(50, 5, 51, 5);
Serial.println(d);
d = fastHaverSine(50, 5, 51, 5);
Serial.println(d);
Serial.println("\n(50, 5, 50, 6)");
d = HaverSine(50, 5, 50, 6);
Serial.println(d);
d = fastHaverSine(50, 5, 50, 6);
Serial.println(d);
Serial.println("\n(50, 5, 50.1, 5)");
d = HaverSine(50, 5, 50.1, 5);
Serial.println(d);
d = fastHaverSine(50, 5, 50.1, 5);
Serial.println(d);
Serial.println("\n(50, 5, 50, 5.1)");
d = HaverSine(50, 5, 50, 5.1);
Serial.println(d);
d = fastHaverSine(50, 5, 50, 5.1);
Serial.println(d);
Serial.println("\n(50, 5, 50.1, 5.1)");
d = HaverSine(50, 5, 50.1, 5.1);
Serial.println(d);
d = fastHaverSine(50, 5, 50.1, 5.1);
Serial.println(d);
Serial.println("\n(50, 5, 50.01, 5.01)");
d = HaverSine(50, 5, 50.01, 5.01);
Serial.println(d, 3);
d = fastHaverSine(50, 5, 50.01, 5.01);
Serial.println(d, 3);
Serial.println("\n(50, 5, 50.001, 5.001)");
d = HaverSine(50, 5, 50.001, 5.001);
Serial.println(d, 4);
d = fastHaverSine(50, 5, 50.001, 5.001);
Serial.println(d, 4);
Serial.println("\n(50, 5, 50.0001, 5.0001)");
d = HaverSine(50, 5, 50.0001, 5.0001);
Serial.println(d, 5);
d = fastHaverSine(50, 5, 50.0001, 5.0001);
Serial.println(d, 5);
}
void loop()
{
}
double fastHaverSine(double lat1, double lon1, double lat2, double lon2)
{
// assume the sphere has circumference 1 on the equator
// to keep the number in same order of magnitude
// helps to keep precision
double dx = lat2 - lat1;
double dy = (lon2 - lon1) * cos(lat2 * (PI / 180.0));
double dist = sqrt(dx * dx + dy * dy);
return dist * 111194.93; // correct for earth sizes ;)
}
double HaverSine(double lat1, double lon1, double lat2, double lon2)
{
double ToRad = PI / 180.0;
double R = 6371000; // radius earth in meter
double dLat = (lat2 - lat1) * ToRad;
double dLon = (lon2 - lon1) * ToRad;
double a = sin(dLat / 2) * sin(dLat / 2) +
cos(lat1 * ToRad) * cos(lat2 * ToRad) *
sin(dLon / 2) * sin(dLon / 2);
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
double d = R * c;
return d;
}
output - distances in meters, timing in micros
Start
HAVERSINE
TIME: 868
DIST: 1321.66
FASTHAVERSINE
TIME: 248
DIST: 1321.62
Although just an indicative test, the FHS is about 3.5x faster than the HS.
(50, 5, 51, 5)
111194.93
111194.93
(50, 5, 50, 6)
71474.20
71474.72
(50, 5, 50.1, 5)
11119.32
11119.32
(50, 5, 50, 5.1)
7147.46
7147.47
(50, 5, 50.1, 5.1)
13214.36
13210.34
(50, 5, 50.01, 5.01)
1321.664
1321.624
(50, 5, 50.001, 5.001)
132.1307
132.1303
(50, 5, 50.0001, 5.0001)
13.14739
13.14739
It seems for distances <=0.1 degree the error of FHS is less than 0.1% of the HS
for distances of <1000 meter the error of the FHS seems less than 0.01%. (10 cm)
a more elaborate test needs to be done...