TinyGPS

A Compact Arduino GPS/NMEA Parser

TinyGPS is designed to provide most of the NMEA GPS functionality I imagine an Arduino user would want – position, date, time, altitude, speed and course – without the large size that seems to accompany similar bodies of code.  To keep resource consumption low, the library avoids any mandatory floating point dependency and ignores all but a few key GPS fields.

Usage

To use, simply create an instance of an object like this:

#include "TinyGPS.h"
TinyGPS gps;

Feed the object serial NMEA data one character at a time using the encode() method. (TinyGPS does not handle retrieving serial data from a GPS unit.) When encode() returns “true”, a valid sentence has just changed the TinyGPS object’s internal state. For example:

#define RXPIN 3
#define TXPIN 2
SoftwareSerial nss(RXPIN, TXPIN);
void loop()
{
  while (nss.available())
  {
    int c = nss.read();
    if (gps.encode(c))
    {
      // process new gps info here
    }
  }
}

You can then query the object to get various tidbits of data. To test whether the data returned is stale, examine the (optional) parameter “fix_age” which returns the number of milliseconds since the data was encoded.

long lat, lon;
unsigned long fix_age, time, date, speed, course;
unsigned long chars;
unsigned short sentences, failed_checksum;

// retrieves +/- lat/long in 100000ths of a degree
gps.get_position(&lat, &lon, &fix_age);

// time in hhmmsscc, date in ddmmyy
gps.get_datetime(&date, &time, &fix_age);

// returns speed in 100ths of a knot
speed = gps.speed();

// course in 100ths of a degree
course = gps.course();

Statistics

The stats method provides a clue whether you are getting good data or not. It provides statistics that help with troubleshooting.

// statistics
gps.stats(&chars, &sentences, &failed_checksum);
  • chars – the number of characters fed to the object
  • sentences – the number of valid $GPGGA and $GPRMC sentences processed
  • failed_checksum – the number of sentences that failed the checksum test

Integral values

Values returned by the core TinyGPS methods are integral. Angular latitude and longitude measurements, for example, are provided in units of millionths of a degree, so instead of 90°30’00”, get_position() returns a longitude value of 90,500,000, or 90.5 degrees. But…

Using Floating Point

…for applications which are not resource constrained, it may be more convenient to use floating-point numbers. For these, TinyGPS offers several inline functions that return more easily-managed data. Don’t use these unless you can afford to link the floating-point libraries. Doing so may add 2000 or more bytes to the size of your application.

float flat, flon;

// returns +/- latitude/longitude in degrees
gps.f_get_position(&flat, &flon, &fix_age);
float falt = gps.f_altitude(); // +/- altitude in meters
float fc = gps.f_course(); // course in degrees
float fk = gps.f_speed_knots(); // speed in knots
float fmph = gps.f_speed_mph(); // speed in miles/hr
float fmps = gps.f_speed_mps(); // speed in m/sec
float fkmph = gps.f_speed_kmph(); // speed in km/hr

Date/time cracking

For more convenient access to date/time use this:

int year;
byte month, day, hour, minutes, second, hundredths;
unsigned long fix_age;

gps.crack_datetime(&year, &month, &day,
  &hour, &minute, &second, &hundredths, &fix_age);

Establishing a fix

TinyGPS objects depend on an external source, i.e. its host program, to feed valid and up-to-date NMEA GPS data. This is the only way to make sure that TinyGPS’s notion of the “fix” is current. Three things must happen to get valid position and time/date:

  1. You must feed the object serial NMEA data.
  2. The NMEA sentences must pass the checksum test.
  3. The NMEA sentences must report valid data. If the $GPRMC sentence reports a validity of “V” (void) instead of “A” (active), or if the $GPGGA sentence reports fix type “0” (no fix) then those sentences are discarded.

To test whether the TinyGPS object contains valid fix data, pass the address of an unsigned long variable for the “fix_age” parameter in the methods that support it. If the returned value is TinyGPS::GPS_INVALID_AGE, then you know the object has never received a valid fix. If not, then fix_age is the number of milliseconds since the last valid fix. If you are “feeding” the object regularly, fix_age should probably never get much over 1000. If fix_age starts getting large, that may be a sign that you once had a fix, but have lost it.

float flat, flon;
unsigned long fix_age; // returns +- latitude/longitude in degrees
gps.f_get_position(&flat, &flon, &fix_age);
if (fix_age == TinyGPS::GPS_INVALID_AGE)
  Serial.println("No fix detected");
else if (fix_age > 5000)
  Serial.println("Warning: possible stale data!");
else
  Serial.println("Data is current.");

Interfacing with Serial GPS

To get valid and timely GPS fixes, you must provide a reliable NMEA sentence feed. If your NMEA data is coming from a serial GPS unit, connect it to Arduino’s hardware serial port, or, if using a “soft” serial port, make sure that you are using a reliable SoftSerial library. As of this writing (Arduino 0013), the SoftwareSerial library provided with the IDE is inadequate. It’s best to use my NewSoftSerial library, which builds upon the fine work ladyada did with the AFSoftSerial library.

Library Version

You can retrieve the version of the TinyGPS library by calling the static member library_version().

int ver = TinyGPS::library_version();

Resource Consumption

Linking the TinyGPS library to your application adds approximately 2500 bytes to its size, unless you are invoking any of the f_* methods. These require the floating point libraries, which might add another 600+ bytes.

Download

The latest version of TinyGPS is available here: TinyGPS13.zip

Change Log

  1. initial version
  2. << streaming, supports $GPGGA for altitude, floating point inline functions
  3. also extract lat/long/time from $GPGGA for compatibility with devices with no $GPRMC
  4. bug fixes
  5. API re-org, attach separate fix_age’s to date/time and position.
  6. Prefer encode() over operator<<. Encode() returns boolean indicating whether TinyGPS object has changed state.
  7. Changed examples to use NewSoftSerial in lieu of AFSoftSerial; rearranged the distribution package.
  8. Greater precision in latitude and longitude.  Angles measured in 10-5 degrees instead of 10-4 as previously.  Some constants redefined.
  9. Minor bug fix release: the fix_age parameter of get_datetime() was not being set correctly.
  10. Added Maarten Lamers’ distance_to() as a static function.
  11. Arduino 1.0 compatibility
  12. Added satellites(), hdop(), course_to(), and cardinal()
  13. Improved precision in latitude and longitude rendering. get_position() now returns angles in millionths of a degree.

Acknowledgements

Many thanks to Arduino forum users mem and Brad Burleson for outstanding help in alpha testing this code. Thanks also to Maarten Lamers, who wrote the wiring library that originally gave me the idea of how to organize TinyGPS.  Thanks also to Dan P. for suggesting that I increase the lat/long precision in version 8.  Thanks to many people who suggested new useful features for TinyGPS, especially Matt Monson, who wrote some nice sample code to do so.

All input is appreciated.

Mikal Hart

Page last updated on August 31, 2013 at 7:00 pm
702 Responses → “TinyGPS”

  1. Mikal

    16 years ago

    budiwan,

    You can print them very easily with

    float lat, lon;
    Serial.print(lat);
    Serial.print(" ");
    Serial.print(lon);

    Mikal


  2. Pantelis Ar

    16 years ago

    Mikal, I’m using your lib and the test code.
    I’ve changed the period of time in which I take an update from the GPS from 5 to 1 sec. Do you think this could affect the library’s performance?


  3. Pantelis Ar

    16 years ago

    I’m using the Parallax GPS module. The manufacturers manual says that the precision of it is +-5m. I take a point (lat,lon) from google earth and try to reach it using the lib and Andreas Gazis formula for distance calculation. While I stand above the point I’ve set on google earth, in my serial monitor I get values of distance above 10 meters.

    I experienced the same when I used just the code example of Mikals lib and entered the values (lat,lon) I got in arduino’s monitor to google earth. The
    distance difference must have been at least 10, maybe 15 meters. Google earth showed me a point across the street and onto my neighbours house.

    Has anyone had the same precision/acurracy problem?
    I’ve already read the previous posts and I know that every GPS module has different precision, but my GPS seems to have way less precision than the one i’ve read in the manufacturers manual.


  4. Mikal

    16 years ago

    Blake, thanks for the kind update. That has to be the gentlest reminder I’ve ever had. :) I do intend to update TinyGPS someday to support more fields, but I just haven’t gotten around to it…

    Mikal


  5. Mikal

    16 years ago

    Pantelis, as long as you are not dropping any characters from the serial GPS stream, you should be fine. I use 1 second intervals with 4800 baud all the time with no problem.

    Mikal


  6. Pantelis Ar

    16 years ago

    Theres isn’t a function that returns the satelites in view, is there?


  7. JSavage

    16 years ago

    I’m using tinygps with a pmb-248. In my code I am looping until I get a valid fix using your instructions on this page. However, the GPS coordinates that come across after it initially fixes are off by up to 2 km. The coordinates slowly correct over the next 5 seconds or so, but I need to get an accurate fix without the 5 seconds of erroneous fixes before I run the rest of the code (not programmed yet). Do you know what I am doing wrong? Here is my code:

    #include
    #include
    const byte ssRx = 2; //Communication pin for GPS (Receive)
    const byte ssTx = 3; //Communication pin for GPS (Transmit)
    const byte lcdRx = 4; //Communication pin for LCD (Receive)
    const byte lcdTx = 5; //Communication pin for LCD (Transmit)
    TinyGPS GPS;
    NewSoftSerial ss(ssRx,ssTx);
    NewSoftSerial lcd(lcdRx,lcdTx);
    float fLat, fLon, distToTarget;
    unsigned long fix_age;
    float targetLat = 37.67523;
    float targetLon = -113.05405;
    boolean fixed = false;

    void setup() {
    // Initialize serial communication with LCD, setup and clear display
    lcd.begin(9600);
    lcd.print(22, BYTE);
    delay(10);
    lcd.print(12, BYTE);
    delay(10);
    // Initialize serial communication with GPS module
    ss.begin(4800);
    }

    void loop() {
    aquireGPSPosition();
    if (fixed) {
    displayLocation();
    delay(100);
    }
    else {
    lcd.print(12, BYTE);
    delay(10);
    lcd.print(128, BYTE);
    lcd.print(“Finding Position”);
    lcd.print(148, BYTE);
    lcd.print(“Please Wait”);
    delay(50);
    }
    }

    boolean aquireGPSPosition() {
    for (int i = 0; i < 10000; i++) {
    if (ss.available()) {
    int c = ss.read();
    if (GPS.encode(c)) {
    GPS.f_get_position(&fLat, &fLon, &fix_age);
    if ((fix_age != TinyGPS::GPS_INVALID_AGE) && (fix_age < 1000)) {
    fixed = true;
    }
    else {
    fixed = false;
    }
    }
    }
    }
    }

    void displayLocation() {
    lcd.print(12, BYTE);
    delay(10);
    lcd.print(128, BYTE);
    lcd.print(“Lat:”);
    lcd.print(fLat, 5);
    lcd.print(148, BYTE);
    lcd.print(“Lon:”);
    lcd.print(fLon, 5);
    }


  8. Philip

    16 years ago

    Thanks for TinyGPS. :)

    In the example for date/time cracking there are references to both “minutes” and “minute”:

    int year, month, day, hour, minutes, second, hundredths;
    unsigned long fix_age;

    gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &fix_age);

    Also, in an earlier example there’s a missing “l” in:

    ong lat, lon;


  9. Philip

    16 years ago

    Also, have just discovered in the version 9 some of the types of the date/time crack arguments have changed:

    int year;
    byte month, day, hour, minutes, second, hundredths;
    unsigned long fix_age;


  10. Chris

    16 years ago

    Hello!

    I have your library running in my GPS Logger. The only problem is, that when I write float values on the SD card using SDFat, the float values has only 2 decimals. Thus I tried to convert the float to a string and write this to SD, but when I use sprintf(chlat,”%.7f”,flat) the value of chlat is a ? . Do you have any idea what went wrong?


  11. Mikal

    16 years ago

    I think this is a pretty common experience — that initial reads from a GPS are a little “off”. It might be time for me to incorporate “dilution of precision” into TinyGPS.

    Mikal


  12. Mikal

    16 years ago

    Thanks for the detailed scrutiny, Phil. It’s always good to hear from you!

    Mikal Hart


  13. Mikal

    16 years ago

    Hi Chris–

    What version of Arduino are you using? With version 0018, I think you can specify the number of decimal places to print with float values.

    Mikal


  14. Rik

    16 years ago

    Hey,
    thanks for your library, it’s working great!
    I’ve got one question for you:
    what exactly does the course value mean?
    Is it the difference in degrees measured from the north?

    thanks,
    Rik


  15. Tom

    16 years ago

    Mikal, great stuff with TinyGPS. Works super on my Arduino and micro-mini GPS module. Are you interested in a patch for support of the 1PPS mark to synchronize timing to the 1/10thousandths or better ?

    I’ve got some interrupt code that captures the 1PPS.

    There’s an issue with the NewSerial library that having taken the time to track down, but using the HW USART the timing is accurate to 116 to 120 microseconds every second. So, I think it scales down to about 4-5 microseconds of accuracy.


  16. Mikal

    16 years ago

    Rik,

    I believe that “course” refers to the direction of travel, as detected by the GPS.

    Mikal


  17. Mikal

    16 years ago

    Tom, I’m not sure I understand your comments about synchronizing with 1PPS and NewSoftSerial, but it sounds interesting. Sure, send me a note, or post some follow up.

    Cheers,

    Mikal


  18. Jeff O'Brien

    16 years ago

    Hi Mikal,
    Thanks for two wonderful libraries. i am using NewSoftSerial and TinyGPS.
    before i ask for any data i feed it for 2 seconds. usually it takes in 4 or 5 sentences. at least one or two of those 4 or 5 fails the checksum. is this common? also you should know. i am using a board that has a 12mhz clock. i put in a new table for 12mhz by finding the mean of the 8 and 16mhz tables values. it seems to be working fine. i always get data back but was a little concerned about the amount of failed checksums. can you tell what is normal.
    also i’m using a GARMIN GPS15 at 4800baud if it matters.
    Thanks again for two great librarys.
    Jeff


  19. Mike

    16 years ago

    Yes I am also interested to know why I am getting lots of failed sentences. I am RX/TX at 4800 baud.


  20. Mikal

    16 years ago

    Mike, it’s probably due to a buffer overflow. If you’re using NewSoftSerial, check to see if nss.overflow() returns true.

    Cheers,

    Mikal


  21. Scott

    16 years ago

    Hi, used tinyGPS with NewSoftSerial, works great. Is there a reason that the position data is only brought out to the 10^-5 degrees? In the NEMA messages I get DDD MM.mmmm which is about a factor of 6 more precise. I tried looking at the parse_degrees() function, but it is a little over my head. Any pointers to getting a little more precision?

    I do realize that GPS is not really accurate enough to matter. I am trying to set up a personal differential setup though, and I think the added precision would be helpful.

    Thanks,

    Scott


  22. gek

    16 years ago

    I have a data integrity question. I like the approach of collecting all the data from a new sentence and then updating the fields that are returned to the client. My concern is for some reason (fast baudrate / GPS update frequency etc) you could get say position information from one sentence and date time from a difference sentence. When calling get_position() followed immediately by get_datetime(). Have you seen this in testing with any combination of calls? Or have I gone into the deep end. I have not seen this. I was just looking at your code and thinking of approaches to prevent overlap.


  23. Larry

    16 years ago

    Does tinyGPS capture GPVTG sentences?


  24. Mikal

    16 years ago

    Scott, I haven’t played with a lot of different models, but all of them that I know of only do dddmm.mmm. Which one do you have?

    Perhaps:

    // untested
    // returns in units of 10^-6 degrees
    // change f_get_position accordingly!
    unsigned long TinyGPS::parse_degrees()
    {
      char *p;
      unsigned long left = gpsatol(_term);
      unsigned long tenk_minutes = (left % 100UL) * 100000UL;
      for (p=_term; gpsisdigit(*p); ++p);
      if (*p == '.')
      {
        unsigned long mult = 10000;
        while (gpsisdigit(*++p))
        {
          tenk_minutes += mult * (*p - '0');
          mult /= 10;
        }
      }
      return (left / 100) * 1000000 + tenk_minutes / 6;
    }

    Mikal


  25. Mikal

    16 years ago

    @Larry,

    Not currently.

    Mikal


  26. Mikal

    16 years ago

    @gek–

    TinyGPS makes no claim that the time returned by get_datetime() has anything to do with the position returned by get_position(). All it claims are that both are the most recent time/pos that it has successfully decoded. I’m not sure I understand what you mean by “overlap”?

    Mikal


  27. Scott

    16 years ago

    Hi Mikal,

    I have been using the 65 Channel SUP500F 10Hz GPS Receiver with Smart Antenna (http://goo.gl/YtBH). Thanks for the code sample, I will let you know when I get it all working.

    Scott


  28. Brett Hagman

    16 years ago

    I’ve been asked by a few people about the satellites in view mod that I provided a while back (on the Arduino forums).

    So I thought I would post my mods here, and either Mikal can incorporate the changes into the library, or people can just use the mods I’ve made.

    Essentially, the mod adds satsinview(), satsused(), and fixtype() to the library.

    Gross details can be found here:
    http://www.roguerobotics.com/wikidocs/
    code/arduino_tinygps_satellite_count_mod

    b


  29. Mikal

    16 years ago

    Hey, thanks Brett. Other people have asked about Dilution of Precision. Ultimately, I’d like to make TinyGPS support a core set of GPS statistics and then allow the user to define some additional fields that they’d like to watch. I’ll probably fold some of this in in the next release — when I get a bit of time.

    Nice work. Thanks.

    Mikal


  30. David Brunell

    16 years ago

    Mikal, many thanks for putting this code in the public domain. I have a SkyTraq ST22 module which reports in dddmm.mmmm, so your 10^-6 modification above is definitely useful. I have tested it, and it works fine. However, I don’t understand your comment “change f_get_position accordingly.”


  31. Mikal

    16 years ago

    David,

    f_get_position() assumes get_position() returns degrees in 10^-5 units. If you replace get_position with the code above, f_get_position() will report values that are a factor of 10 too big.

    Mikal


  32. David Brunell

    16 years ago

    Thanks Mikal. My problem was that I couldn’t find f_get_position because I didn’t realize it was an inline function in the header file. I’ve changed it as below, and everything works great.

    inline void f_get_position(float *latitude, float *longitude, unsigned long *fix_age = 0)
    {
    long lat, lon;
    get_position(&lat, &lon, fix_age);
    *latitude = lat / 1000000.0;
    *longitude = lon / 1000000.0;
    }


  33. Jed Savage

    16 years ago

    Any work arounds for the time being to get an accurate fix? I know you’re planning on incorporating a dilution of precision, but is there anything I can do now to get a more accurate fix when the gps is first turned on?


  34. Mikal

    16 years ago

    Jed,

    I don’t know a trick for actually increasing the initial accuracy of the data. Anyone else?

    Mikal


  35. Adi

    16 years ago

    Hi,

    TinyGPS is great!. But I’m facing a major problem I have a LS20031. When I read the raw data, the Lat and Lon is correct and accurate; in which it reports 9 digits. When using tinygps I get the degree correctly but the remaining is incorrect…

    Is there a fix? or help?

    Thanks


  36. Mikal

    16 years ago

    Adi, can you give further details?

    Mikal


  37. Rusty

    15 years ago

    Mikal,
    Sorry if this has been reported before but in TinyGPS.cpp (verison 9)
    around line 60 you need to surround the ++_encoded_characters; statement
    with #ifndef _GPS_NO_STATS / #endif . If that’s not there, the weirdo’s
    like me who turn off the stats, can’t get TinyGPS to compile. :-o

    -Rusty-


  38. Mikal

    15 years ago

    Great catch, Rusty, and thanks for sharing it!

    Mikal


  39. Rusty

    15 years ago

    I too would like to add my request for P,H, & VDOP.
    These numbers can be decoded from the GPGSA sentence
    which is part of the standard set of NMEA v2.2 sentences.

    Another useful piece of information that comes out of this
    sentence is what type of fix the GPS is maintaining, 2D or 3D.
    Waiting for a 3D fix may allow others to avoid that initial
    position error that folks are reporting.

    Just a thought… :-)

    -Rusty-


  40. simone garino

    15 years ago

    hello

    I need to receive the longitudne and latitude in sexagesimal format. How do I change the function parse_degrees () to get the result in this format?


  41. Michael H

    15 years ago

    Mikal,

    I have a couple of questions:

    1) Do you have a library reference document for the TinyGPS? If so where can I find it?

    2) I am wanting to get some more data from my GPS – I have found the forum on the Arduino site that explains how to get the number of satelites in view, but I am needing the magnetic variation, 2D/3D fix, fix quality and dilution info


  42. Mikal

    15 years ago

    Michael, I’m afraid you are reading the TinyGPS documentation right here. I do hope to someday write a more thorough document. While I’m at it, I hope to add some of the popular fields you are interested in. Thanks for the note.

    Mikal


  43. amrk

    15 years ago

    Some gps functionality is lost with many gps units at high altitudes. Is this a function of the module or the programming such as your application? My hope is that your app circumvents this problem. Thanks for your contibution to arduino!


  44. Mikal

    15 years ago

    Sorry, amrk,

    My library only extracts the data the GPS reports and presents it in a more programmer-accessible form.

    Mikal


  45. Sanchoooo

    15 years ago

    Greets,
    I think I may have found a bug in your latest release:
    $GPRMC,033112.000,A,3203.3161,N,11042.1898,W,0.02,62.92,090910,,,D*4E
    Acquired Data
    ————-
    Lat/Long(10^-5 deg): 3205526, -11070316 Fix age: 500ms.
    Lat/Long(float): 32.05526, -110.70317 Fix age: 506ms.
    Date(ddmmyy): 90910 Time(hhmmsscc): 3311200 Fix age: 538ms.
    Date: 9/9/2010 Time: 3:31:12.0 Fix age: 544ms.
    Alt(cm): 95560 Course(10^-2 deg): 6292 Speed(10^-2 knots): 2
    Alt(float): 955.60 Course(float): 62.92
    Speed(knots): 0.02 (mph): 0.02 (mps): 0.01 (kmph): 0.04
    Stats: characters: 1256 sentences: 10 failed checksum: 0
    ————-

    Everything is printing correctly except the time. GPRMC shows 033112 but your output shows 33h11m20s0?c.


  46. Sanchoooo

    15 years ago

    So my silly little fix to it was:
    Serial.print(” Time(hhmmsscc): “);if (time < 10000000) Serial.print("0");Serial.print(time);//No leading 0's printed when hh < 12 so print one


  47. Rusty

    15 years ago

    Two things:
    1) Shouldn’t TinyGPS::ParseDecimal be returning a SIGNED long?
    2) I have made additions to TinyGPS to parse the GPGSA sentence
    (one of the standard sentences for NMEA v2.2) which can give
    the type of fix (2d or 3d), list of satellites, PDOP, HDOP,
    and VDOP. I’ll do a little more cleanup and testing but the
    code appears to be working correctly. Everyone is welcome to
    these additions.

    -Rusty-

3 Trackbacks For This Post
  1. Transmission success - Projects

    […] and sending the bits of “Hello World” in 7N1 format.  I spent some time with the TinyGPS and NewSoftSerial libraries from Mikal Hart, and got the parsing working nicely and building an […]

  2. GPS testing with LCD Character Display

    […] can have the LCD output latitude, longitude, or whatever. You’ll need the TinyGPS library from Arduiniana downloaded and installed for it to work. They suggest using NewSoftSerial, but I couldn’t get […]

  3. Arduino GPS — not exactly pocket-sized, but cool! « Arduino Projects FTW

    […] EEPROM so they are persistent between power cycles. The sketch uses Mikal Hart's excellent TinyGPS library and includes code from the ArduPilot Projectfor […]

Leave a Reply