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 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:

// Use NewSoftSerial for greater reliability
#define RXPIN 3
#define TXPIN 2
NewSoftSerial 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.

ong 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 10-5 degrees, so instead of 90°30′00″, get_position() returns a longitude value of 9050000, 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, 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: TinyGPS9.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.

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.

All input is appreciated.

Mikal Hart

Page last updated on January 18, 2010 at 11:41 am
96 Responses → “TinyGPS”

  1. Pantelis Ar

    6 months ago

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


  2. JSavage

    5 months 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);
    }


  3. Philip

    5 months 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;


  4. Philip

    5 months 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;


  5. Chris

    5 months 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?


  6. Mikal

    5 months 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


  7. Mikal

    5 months ago

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

    Mikal Hart


  8. Mikal

    5 months 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


  9. Rik

    5 months 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


  10. Tom

    5 months 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.


  11. Mikal

    5 months ago

    Rik,

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

    Mikal


  12. Mikal

    5 months 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


  13. Jeff O'Brien

    5 months 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


  14. Mike

    5 months ago

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


  15. Mikal

    4 months 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


  16. Scott

    4 months 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


  17. gek

    4 months 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.


  18. Larry

    4 months ago

    Does tinyGPS capture GPVTG sentences?


  19. Mikal

    4 months 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


  20. Mikal

    4 months ago

    @Larry,

    Not currently.

    Mikal


  21. Mikal

    4 months 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


  22. Scott

    4 months 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


  23. Brett Hagman

    4 months 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


  24. Mikal

    3 months 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


  25. David Brunell

    3 months 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.”


  26. Mikal

    3 months 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


  27. David Brunell

    3 months 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;
    }


  28. Jed Savage

    2 months 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?


  29. Mikal

    2 months ago

    Jed,

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

    Mikal


  30. Adi

    2 months 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


  31. Mikal

    2 months ago

    Adi, can you give further details?

    Mikal


  32. Rusty

    4 weeks 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-


  33. Mikal

    4 weeks ago

    Great catch, Rusty, and thanks for sharing it!

    Mikal


  34. Rusty

    4 weeks 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-


  35. simone garino

    3 weeks 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?


  36. Michael H

    2 weeks 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


  37. Mikal

    2 weeks 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


  38. amrk

    1 week 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!


  39. Mikal

    1 week ago

    Sorry, amrk,

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

    Mikal

7 Trackbacks For This Post
  1. เริ่มต้นสร้าง GPS จอสีกับอาดูอี้โน่ | Ayarafun Factory

    [...] http://www.sundial.org/arduino/?page_id=3 [...]

  2. 10

    [...] won’t. I answer a few questions on the Arduino microcontroller forum and post an update to a library I [...]

  3. The Hired Gun » GPS project: show me something

    [...] this journey I also looked into using the TinyGPS library. While it does a nice job of handling the core GPS parsing, I still had the primary desire to log [...]

  4. GPS mit Arduino - Webmeister Blog

    [...] serielle Verbindung werden die  Daten ans Display geschickt. Verwendet werden die Bibliotheken TinyGPS und [...]

  5. The Frustromantic Box, Part 4: Software « New Bright Idea

    [...] Arduino developers for the great librarires, and to Mikal Hart in particular for his work on the TinyGPS and NewSoftSerial [...]

  6. 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 [...]

  7. 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 [...]

Leave a Reply