As I mentioned before, Microsoft’s Coding 4 Fun GPS project contains a math error when calculating latitude and longitude. I’ll first provide a brief review of the NMEA protocol to better understand the error and how to correct it.
GPS receivers communicate using the NMEA protocol. Another device, such as a computer, receives the data in the form of NMEA sentences. There are several different types of NMEA sentences that allow for information on location (GPGGA), accuracy (GPGSA), satellite reception (GPGSV), etc. to be transmitted.
Here is an example of a NMEA sentence:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M, ,*47 |
The sentence type is indicated first ($GPGGA) so we know that this sentence is communicating the current position of the GPS device. To determine the location, we must look at the following portion of the sentence:
4807.038,N,01131.000,E |
The first value (4807.038) represents the latitude(Y) while the N represents north (of the equator). To find the actual latitude, the value must be separated after 2 digits to get 48 degrees and 7.038 minutes. Likewise, the longitude(X) value translates to 11 degrees and 31.000 minutes east.
It is important to note that GPGGA sentences communicate the location in degrees and decimal minutes. If we wanted to convert this to decimal degrees, the minutes value would have to be divided by 60 first since there are 60 minutes in a degree:
Decimal degrees = 48 + (7.038 / 60) = 48 + 0.1173 = 48.1173
Conversely, at first glance one might think to just move the decimal place over 2 positions to get decimal degrees (48.07038) but as we already know, this is incorrect. Any mapping programs using this formula will show your location offset in both latitude and longitude.
Now let’s take a look at the Coding 4 Fun formula. In the NMEAProtocol.cs file, find the method named ProcessGPGGA and see how latitude is calculated:
GPGGA.Latitude = Convert.ToDouble(fields[1])/100; |
The variable fields[1] contains the entire latitude value (i.e. 4807.038) from the NMEA sentence. But instead of first converting the minutes portion to decimal degrees, the code just divides everything by 100. This produces an incorrect latitude as I discussed above. The process is also repeated for longitude so the code’s location is different from the location in the GPS.
My adjusted code looks like this:
double degrees = Convert.ToDouble(fields[1].Substring(0, 2)); double minutes = Convert.ToDouble(fields[1].Substring(2, 7)); GPGGA.Latitude = Math.Round(degrees + (minutes / 60.0), 6); |
I first separate out degrees and minutes and then correctly combine them to produce decimal degrees. I repeated the process for the longitude calculation.
In reality, the math error only produces a small offset that might not even be noticeable unless you were looking at a finer-scaled map. I only discovered the error after using the original code in a mapping application that contained high-resolution aerial photography.