GPS distance measurement between two coordinates using Arduino

I knew the dimension of the LCD (Nokia 5110) as 43mm x 43mm but it looked smaller than I thought.  That’s good because I want to put it on top of the GPS (Holux M-1000).

In my previous post, I explained how to get coordinates, date & time, speed, and bearing data from GPS, Holux M-1000.  Now that I have the LCD, it’s time to add two parts to the Arduino sketch: 1) LCD driver/display, 2) distance calculation between two locations.

1. LCD Driver

First of all, I searched for a simple and small library for the LCD, Nokia 5110. There were a few different libraries for this LCD: Adafruit’s, Sparkfun’s, and Henning Karlsen’s.  Among these libraries, I chose Henning Karlsen’s because I needed only simple text display with a couple of different font sizes.  Henning Karlsen has separate library for graphics as well.  I would like to thank Henning for his sharing his nice work on the library.  Henning’s library supports 3 different font sizes: SmallFont (text and number, 6×8), MediumNumber (number only, 12×16), and BigNumber (number only, 14x 24).  Only downside of this library is that the Medium and Big fonts do not support texts but only numbers. However I would need only numbers to display with bigger fonts, this limitation was no problem with me.

2. Distance calculation between two locations

There are number of websites showing how to calculate distance between two locations from latitudes and longitudes. Movable Type Scripts shows various  calculations of distance, bearing and other useful conversions using Haversine formula and BlueMM posted the Excel formula to calculate distance which is basically the same way as Haversine.

The calculation is quite straightforward but I found there was a problem: Arduino (Atmega328p) cannot handle over 6-7th decimal digits which is very important in trigonometric calculation for short distance.

Arduino reference page says “Floats have only 6-7 decimal digits of precision. That means the total number of digits, not the number to the right of the decimal point. Unlike other platforms, where you can get more precision by using a double (e.g. up to 15 digits), on the Arduino, double is the same size as float.”

Let me give you an example.  Suppose we started from a position A (lat: 40.00, long: 80) to a position B (lat: 40.01, long: 80.00). That is, we moved 0.01 degrees in latitude only. If you calculate the distance using Haversine formula on your PC, you will get about 1,111.9m. However, Arduino calculates it as 3,110.8m. Big error!  More interesting thing is that even if you reduce the latitude difference to 0.001 or 0.0001 degrees, you get the same results, 3,110.8m. So I investigate further what exactly cause this error. Of course I know the culprit is the float precision limitation as said above. But I wanted to know which part of the calculation by Arduino cause this big error. In the Haversine formula, there are COS, SIN and ACOS functions used.  I tested a few different calculations using these functions and found the calculation of COS and SIN functions affect minimal but the problem was the ACOS.  If you calculate the formula on your PC only inside of ACOS bracket, you will get 0.9999999848. See my point? The decimal places below 6th in ACOS function is actually important to calculate the angular difference for small distance, but unfortunately Arduino cannot handle this.  Not only for small distance but for even relatively long distance (say over 1 degree for instance) there is error between the results on the PC and Arduino.

Well, so I started thinking about how to avoid trigonometric function calculation when over 6th decimal places are important. And I found a solution! Instead of calculating angular difference between two positions and THEN calculating the distance by multiplying the mean earth radius, calculating a ratio of angle between two positions (latitude and longitude separately) over 360 degrees and divide the circumference of the earth by this ratio. In other words, keep the numbers big while calculation. Arduino’s float type has a limitation on the small decimal places, but can handle relatively big numbers!

Here is my formula:

The mean circumference of the earth is 2 x 6,371,000m x π = 40,030,170m

Δd (lat) = 40,030,170 x ΔΘ (lat) / 360 (assuming ΔΘ is small)

Δd(long) = 40,030,170 x ΔΘ(long) x cosΘm / 360 (Θm: mean latitude between two positions)

Now, the distance is √[Δd (lat)^2 + Δd (long)^2]

Below is the test Arduino sketch to test my formula.  The result is 11.029m while Haversine formula for the same coordinates gives 11.119m.  This is close enough considering the accuracy of the most GPS is bigger than one meter.

float gpsLat0 = 40.0;
float gpsLat = 40.0001;
float gpsLong0 = 80.0;
float gpsLong = 80.0;

void setup()
{
Serial.begin(38400);
float delLat = abs(gpsLat0-gpsLat)*111194.9;
float delLong = 111194.9*abs(gpsLong0-gpsLong)*cos(radians((gpsLat0+gpsLat)/2));
float distance = sqrt(pow(delLat,2)+pow(delLong,2));
Serial.println(distance,3);
}

void loop()
{
}

To be continued….

Advertisements

GPS (M-1000) with LCD (Nokia 5110)

Finally, I got the Nokia 5110 LCD that was ordered on eBay a few weeks ago. It took me a couple of days to find the best library for the LCD and quickly updated my Arduino program to display current location (latitude and longitude), date/time, speed, and bearing. There is 2.8V regulated power in the GPS that powers the JeonLab mini and LCD. I will upload my sketch and full detail later.

 photo GPSwithLCD5110_currentlocation.jpg