Category Archives: Programming

Reading and Parsing GPS Sentences (a Linux Example)

This post shows how to read the GPS position from a GPS-enabled Multitech modem. This approach can be adapted to reading on other devices.

GPS Sentences

GPS devices communicate using “sentences”, which are single-line strings of comma-separated values. In the case of the Multitech modem, these sentences are streamed continuously from a COM port.

An excellent reference that summarizes GPS sentences can be found here. If you need to find more information, it’s best to search for "NMEA 0183", which is the specification that defines GPS communications.

The basic approach for getting a GPS position is:

  1. Open the port that’s emitting the GPS sentences.
  2. Read one sentence (meaning one line) at a time until you get a $GPGSA sentence, which indicates how reliable the reported position will be.
  3. Parse the $GPGSA sentence to see if you have a reliable position "fix".
  4. If the position will be reliable, read one sentence at a time until you get a sentence that reports the position. There’s more than one sentence type for this. I ended up using the $GPRMC sentence, which reports position as latitude and longitude. It will also report altitude (assuming you need the altitude and the modem has a reliable 3D "fix").
  5. Parse the $GPRMC sentence to get latitude, longitude, and, optionally, altitude.
    The Multitech modem I worked with used the Linux gnu operating system, so the easy choice was to write a Linux script to retrieve the information, but the general approach will work with any language that can read from a COM port. It’s rare to find a language that can’t, though some languages that operate in a security "sandbox" (for example JavaScript) will prohibit interaction with a port.

    Reading from COM Ports in Linux

    In Linux the COM port is just another file. On the particular modem I was using, the GPS port was /dev/ttyS3. To see the GPS sentences from the command line, I just had to type cat /dev/ttyS3 and Linux would spew out the sentences until I stopped it.

    To read from the port in a program, just to redirect it to a file descriptor. In the example below I use file descriptor #5 to redirect the modem’s sentence-reporting port:

    # "Open" the port. Specifically, redirect port
    # /dev/ttyS3 to file descriptor 5
    exec 5

    Jumping ahead a bit, it's good to "close" things when you're done. In this case, that means killing off the redirect:

    # "Close" the port. Specifically, stop redirecting 
    # the port to file descriptor 5
    exec 5<&-

    To read a line from the port, use the read command. This normally reads from user input into a variable, but in this case we're reading from the redirected COM port. I've also added a timeout of 10 seconds so the read can give up if the GPS port is unresponsive; that's the -t 10 below:

    # Read a line from file descriptor 5, which
    # is a redirect from the GPS port /dev/ttyS3
    read -t 10 RESPONSE <&5

    If the read command is successful, the next GPS sentence in the stream will be placed into the RESPONSE variable. If the command times out it will return 1, which you can check using the $? shell variable ($? holds the return value of the last executed command):

    read -t 10 RESPONSE <&5
    if [[ $? -eq 1 ]]
      # close the port and return failure to the user

    Parsing the $GPGSA ("Fix" Information) Sentence

    A typical $GPGSA sentence looks like this:


    In the comma-delimited list of values, the only ones I cared about were the first (which identifies the sentence) and the third (which indicates if the GPS reading will be reliable).

    The first field's value must of course be $GPGSA. The third field's value will be one of the following:

    • 1 means the GPS doesn't have a position fix.
    • 2 means the GPS has a 2-dimensional (latitude and longitude) fix. If you don't care about altitude (and I didn't), then a reliability of 2 is acceptable. If you do care about altitude you'll need a reliability of 3.
    • 3 means the GPS has a 3-dimensional (latitude, longitude, and altitude) fix. This value is also acceptable if you need only latitude and longitude because you can just ignore the altitude.

    Once you've determined that the position reading will be reliable, the next step is to find and parse a position sentence.

    Parsing the $GPRMC (Recommended Minimum GPS Data) Sentence

    A typical $GPRMC sentence looks like this:


    In this comma-delimited list of values, the important fields are:

    1. When this field's value is $GPRMC, it indicates that the sentence has position information.
    2. This field will be A for active or V for void. A is good; V means there's an unexpected problem with the position reading. If you encounter a V you should start the whole position-reading process over again, including the $GPGSA reading. Another option is to quit and report failure.
    3. This is the absolute value of the latitude, but it needs some conversion. The number in the example is 4807.038, and it means 48 degrees and 7.038 minutes (48° 7.038"). A "minute" is a 60th of a degree. The next section shows how to convert from this format to a decimal degree value.
    4. This will be N if the latitude (field 4, above) is North, or S if the latitude is South. For many mapping applications, North is represented by a positive number and South by a negative number; for example latitude 45°S is specified as –45°.
    5. This is the absolute value of the longitude, using the same format as the latitude in field 4. The next section shows how to convert from this format to a decimal degree value.
    6. This will be E if the longitude is East, or W if the longitude is West. East is a positive longitude; West is negative.

    Converting the $GPRMC Latitude and Longitude Values

    Controls such as the Google Maps API want latitude and longitude values as degree values with decimal places rather than minutes, and South/West values as negatives. Here's how to convert them, using the value 4827.563 West:

    1. Divide the integer part of the latitude by 100. 4827 ÷ 100 = 48
    2. Get the "minutes" value by taking the remainder of the value divided by 100. 4827.563 - 100 × 48 = 27.563
    3. The result of step 2 is the "minutes" part of the degrees. Since there are 60 minutes per degree, divide the result from step 3 by 60 to get the decimal degrees. 27.563 ÷ 60 ≈ 0.45938
    4. Add the values from step 2 and 4. 48 + 0.45938 = 48.45938
    5. Make the result from step 5 negative if this is a latitude and it's South, or if it's a longitude and it's West. 48.45938 West = –45.45938

    Doing math in Linux scripting is more challenging than in most other languages, but it's certainly not impossible. I settled on using the bc utility, but you should use the math package you're most comfortable with. Here's a function to take a GPS value and direction and convert it to a signed decimal latitude/longitude:

    # Pass the GPS value and direction (N/S/E/W) to get the
    # decimal latitude/longitude.
    function gpsDecimal() {
        gpsDir ="$2"
        # Integer part of the lat/long
        gpsInt=`echo "scale=0;$gpsVal/100" | bc`
        # Minutes part of the lat/long
        gpsMin=`echo "scale=3;$gpsVal-100*$gpsInt" | bc`
        # Convert minutes to a full decimal value
        gpsDec=`echo "scale=5;$gpsInt+$gpsMin/60" | bc`
        # South and West are negative
        if [[ $gpsDir -eq "W" || $gpsDir -eq "S" ]]
        echo $gpsDec

    Other Considerations

    A GPS module can fail to report location for reasons such as loss of signal, loss of power, or malfunction. Any program that reads GPS values should have a graceful way out if the module isn't responsive or is reporting garbage. My finished program uses a timeout value of 10 seconds to read each sentence, and it reports failure under any of the following scenarios:

    • Failed to read a sentence after ten seconds
    • GPS fix quality is insufficient
    • Location data is invalid
    • Processed 100 sentences but couldn't get a location

    The Finished Program

    First of all, a disclaimer: this program resides on a cell modem I used for testing, and the modem isn't active so I had to retype it rather than copying it. I tried to be careful, but there may be a typo or two.

    And here's the finished program:

    # !bin/sh
    # getloc - read GPS location and echo status, latitude and longitude
    #          separated by a space. Status 0 is success.
    function closePort() {
      exec 5<&-
    function openPort() {
      exec 5

Reading XML with Namespaces using LINQ


On a recent .NET project I had to hook up with a web service that used non-standard headers in the request. After struggling with the configuration settings and the sparse Windows Communication Foundation (WCF) documentation for creating custom headers, I finally decided to skip WCF altogether and create the request as straight XML and manually process the XML in the response. It turned out to be easy (and straightforward), but I had a little trouble processing the returned XML using LINQ. The problem? The return XML had namespaces, which is common enough in the real world, but the .NET documentation and various blog posts showing how to process XML with LINQ don’t give much attention to namespaces. Continue reading

Raw XML Web Service Calls in C#

This is a companion article to Reading XML with Namespaces using LINQ, which explains how to parse XML with namespaces. Both articles came about because I had to use a web service that had non-standard envelope headers, and after struggling with the configuration settings and the sparse Windows Communication Foundation (WCF) documentation for creating custom headers, I finally threw it in and called the service by brute force – assembling the HTTP and XML requests, calling the service, and parsing the returned XML response.

I want to stress that WCF is excellent for communicating with a web service that more or less uses SOAP standards. If that’s your situation, stop reading right now and use WCF. But if you run into trouble with non-standard services I think it’s easier to put WCF aside and work directly with the service. At the very least, it’s a lot more straightforward. Continue reading