Pages: [1]   Go Down
Author Topic: Patch for TinyGPS - fix type, sats used/in view  (Read 1655 times)
0 Members and 1 Guest are viewing this topic.
Toronto, ON
Offline Offline
Full Member
***
Karma: 10
Posts: 233
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hats off to mikalhart for all of the contributions.  TinyGPS is definitely a good one.

I wanted to add some other fixes to TinyGPS.  Currently, TinyGPS makes the assumption that only GPGGA and GPRMC are the only "good" sentences, and data will only be available if those sentences have been parsed within the object as providing active data.  I've changed it so that data can be retrieved before a fix has been made.

GPGSA, and GPGSV sentences provide a lot of information before a fix has been made.  I've added a couple of quick pieces of data (satsinview, and satsused) to let you know if you are getting data from satellites before a fix can be made.

Here are the patches:
TinyGPS.h

Code:
--- TinyGPS9-orig\TinyGPS.h      Thu Apr 02 23:09:36 2009
+++ TinyGPS\TinyGPS.h      Wed Jun 17 15:01:36 2009
@@ -65,6 +65,15 @@
     // speed in last full GPRMC sentence in 100ths of a knot
     unsigned long speed() { return _speed; }
 
+    // number of satellites in view (GPGSV sentence)
+    unsigned char satsinview() { return _satsinview; }
+
+    // number of satellites used for fix (GPGSA sentence)
+    unsigned char satsused() { return _satsused; }
+    
+    // get the fix type
+    unsigned char fixtype() { return _fixtype; }
+
 #ifndef _GPS_NO_STATS
     void stats(unsigned long *chars, unsigned short *good_sentences, unsigned short *failed_cs);
 #endif
@@ -106,9 +115,11 @@
 
     enum {GPS_INVALID_AGE = 0xFFFFFFFF, GPS_INVALID_ANGLE = 999999999, GPS_INVALID_ALTITUDE = 999999999, GPS_INVALID_DATE = 0,
       GPS_INVALID_TIME = 0xFFFFFFFF, GPS_INVALID_SPEED = 999999999, GPS_INVALID_FIX_TIME = 0xFFFFFFFF};
+    
+    enum {GPS_FIX_NO_FIX = 1, GPS_FIX_2D = 2, GPS_FIX_3D = 3};
 
 private:
-    enum {_GPS_SENTENCE_GPGGA, _GPS_SENTENCE_GPRMC, _GPS_SENTENCE_OTHER};
+    enum {_GPS_SENTENCE_GPGGA, _GPS_SENTENCE_GPRMC, _GPS_SENTENCE_GPGSV, _GPS_SENTENCE_GPGSA, _GPS_SENTENCE_OTHER};
    
     // properties
     unsigned long _time, _new_time;
@@ -118,6 +129,9 @@
     long _altitude, _new_altitude;
     unsigned long  _speed, _new_speed;
     unsigned long  _course, _new_course;
+    unsigned char  _satsinview, _new_satsinview;
+    unsigned char  _satsused, _new_satsused;
+    unsigned char  _fixtype, _new_fixtype;
 
     unsigned long _last_time_fix, _new_time_fix;
     unsigned long _last_position_fix, _new_position_fix;
@@ -129,7 +143,6 @@
     byte _sentence_type;
     byte _term_number;
     byte _term_offset;
-    bool _gps_data_good;
 
 #ifndef _GPS_NO_STATS
     // statistics

TinyGPS.cpp
Code:
--- TinyGPS9-orig\TinyGPS.cpp      Tue Mar 03 22:39:58 2009
+++ TinyGPS\TinyGPS.cpp      Wed Jun 17 15:06:40 2009
@@ -23,6 +23,8 @@
 
 #define _GPRMC_TERM   "GPRMC"
 #define _GPGGA_TERM   "GPGGA"
+#define _GPGSV_TERM   "GPGSV"
+#define _GPGSA_TERM   "GPGSA"
 
 TinyGPS::TinyGPS()
 :  _time(GPS_INVALID_TIME)
@@ -32,6 +34,9 @@
 ,  _altitude(GPS_INVALID_ALTITUDE)
 ,  _speed(GPS_INVALID_SPEED)
 ,  _course(GPS_INVALID_ANGLE)
+,  _satsinview(0)
+,  _satsused(0)
+,  _fixtype(GPS_FIX_NO_FIX)
 ,  _last_time_fix(GPS_INVALID_FIX_TIME)
 ,  _last_position_fix(GPS_INVALID_FIX_TIME)
 ,  _parity(0)
@@ -39,7 +44,6 @@
 ,  _sentence_type(_GPS_SENTENCE_OTHER)
 ,  _term_number(0)
 ,  _term_offset(0)
-,  _gps_data_good(false)
 #ifndef _GPS_NO_STATS
 ,  _encoded_characters(0)
 ,  _good_sentences(0)
@@ -80,7 +84,6 @@
     _parity = 0;
     _sentence_type = _GPS_SENTENCE_OTHER;
     _is_checksum_term = false;
-    _gps_data_good = false;
     return valid_sentence;
   }
 
@@ -161,31 +164,35 @@
     byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]);
     if (checksum == _parity)
     {
-      if (_gps_data_good)
-      {
 #ifndef _GPS_NO_STATS
-        ++_good_sentences;
+      ++_good_sentences;
 #endif
-        _last_time_fix = _new_time_fix;
-        _last_position_fix = _new_position_fix;
+      _last_time_fix = _new_time_fix;
+      _last_position_fix = _new_position_fix;
 
-        switch(_sentence_type)
-        {
-        case _GPS_SENTENCE_GPRMC:
-          _time      = _new_time;
-          _date      = _new_date;
-          _latitude  = _new_latitude;
-          _longitude = _new_longitude;
-          _speed     = _new_speed;
-          _course    = _new_course;
-          break;
-        case _GPS_SENTENCE_GPGGA:
-          _altitude  = _new_altitude;
-          _time      = _new_time;
-          _latitude  = _new_latitude;
-          _longitude = _new_longitude;
-          break;
-        }
+      switch(_sentence_type)
+      {
+      case _GPS_SENTENCE_GPRMC:
+        _time      = _new_time;
+        _date      = _new_date;
+        _latitude  = _new_latitude;
+        _longitude = _new_longitude;
+        _speed     = _new_speed;
+        _course    = _new_course;
+        break;
+      case _GPS_SENTENCE_GPGGA:
+        _altitude  = _new_altitude;
+        _time      = _new_time;
+        _latitude  = _new_latitude;
+        _longitude = _new_longitude;
+        break;
+      case _GPS_SENTENCE_GPGSV:
+            _satsinview = _new_satsinview;
+            break;
+      case _GPS_SENTENCE_GPGSA:
+            _satsused = _new_satsused;
+            _fixtype = _new_fixtype;
+            break;
 
         return true;
       }
@@ -205,12 +212,42 @@
       _sentence_type = _GPS_SENTENCE_GPRMC;
     else if (!gpsstrcmp(_term, _GPGGA_TERM))
       _sentence_type = _GPS_SENTENCE_GPGGA;
+    else if (!gpsstrcmp(_term, _GPGSV_TERM))
+    {
+          _sentence_type = _GPS_SENTENCE_GPGSV;
+    }
+    else if (!gpsstrcmp(_term, _GPGSA_TERM))
+    {
+          _sentence_type = _GPS_SENTENCE_GPGSA;
+      _new_satsused = 0;
+    }
     else
       _sentence_type = _GPS_SENTENCE_OTHER;
     return false;
   }
 
-  if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0])
+      if (_sentence_type == _GPS_SENTENCE_GPGSV && _term_number == 3 && _term[0])
+      {
+            // we've got our number of sats
+            // NOTE: we will more than likely hit this a few times in a row, because
+            // there are usually multiple GPGSV sentences to describe all of the sats
+            _new_satsinview = (unsigned char) gpsatol(_term);
+      }
+      else if (_sentence_type == _GPS_SENTENCE_GPGSA)
+      {
+            if (_term_number == 2 && _term[0])  // Fix type
+            {
+                  _new_fixtype = (unsigned char) gpsatol(_term);
+            }
+            else if (_term_number >= 3 && _term_number <= 14 && _term[0]) // Count our sats used
+            {
+                  _new_satsused++;
+            }
+//            if (_term_number == 15)  // PDOP
+//            if (_term_number == 16)  // HDOP
+//            if (_term_number == 17)  // VDOP
+      }       
+  else if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0])
   switch((_sentence_type == _GPS_SENTENCE_GPGGA ? 200 : 100) + _term_number)
   {
     case 101: // Time in both sentences
@@ -218,9 +255,6 @@
       _new_time = parse_decimal();
       _new_time_fix = millis();
       break;
-    case 102: // GPRMC validity
-      _gps_data_good = _term[0] == 'A';
-      break;
     case 103: // Latitude
     case 202:
       _new_latitude = parse_degrees();
@@ -248,9 +282,6 @@
       break;
     case 109: // Date (GPRMC)
       _new_date = gpsatol(_term);
-      break;
-    case 206: // Fix data (GPGGA)
-      _gps_data_good = _term[0] > '0';
       break;
     case 209: // Altitude (GPGGA)
       _new_altitude = parse_decimal();

Here is how you would now check if a fix has been made and data is valid:

Code:
...
// data feeding before here, of course
if(gps.fixtype() > 1)
{
  gpsdump();
}
else
{
  Serial.print("No Fix. Satellites in view: "); Serial.println(gps.satsinview());
}
...

Let me know what you think.

b

P.S. mikalhart, if you use the SerialComm abstract base class for NewSoftSerial, then you could turn the "gps.encode(c)" into "gps.process()" or such.
Logged


Austin, TX USA
Offline Offline
God Member
*****
Karma: 5
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nice work, b.  You clearly understand the TinyGPS architecture well.  I like your patch.

Just to clarify, I never really thought that GPGGA and GPRMC were the only "good" sentences, just that they were the "best" sentences.  I try to maintain a balance between keeping the code very compact and making it maximally useful.  That said, the additional six bytes (is it?) to support these seems worthwhile.  Perhaps I will add them with a conditional #ifdef so that people who really care just about, say, position, could stay small.

Thank you!

And I guess it shows how out of touch I've been to say that I haven't even heard of the SerialComm basecall, but yes, it seems natural that NewSoftSerial should inherit from it.  Many thanks for your notes and contributions!

Mikal
« Last Edit: July 10, 2009, 09:30:49 pm by mikalhart » Logged

Greece
Offline Offline
Jr. Member
**
Karma: 0
Posts: 75
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

where exactly should i add this code?


im trying to use tinyGPS with the FileLogger lib and another function that converts the long int variables that holds the latitude and longtitude, to string but i cant get it to work yet.. The 2 scetches work fine when i try them stand alone, but the combined program fails.. Has anyone used tinyGPS combined with another library successfully with an SD card for example?

heres my code:


Code:
#include <NewSoftSerial.h>
#include <TinyGPS.h>
#include "FileLogger.h"
#include "floatToString.h"

#define MEM_PW 8
#define MESSAGE "latitude/longtitude: "
#define ENDL "\r\n"

unsigned long length = sizeof(MESSAGE)-1;
unsigned long length2 = sizeof(ENDL)-1;

byte buffer[] = MESSAGE;
byte buffer2[] = ENDL;
char buff1[8];
char buff2[8];



TinyGPS gps;
NewSoftSerial nss(2, 3);

void gpsdump(TinyGPS &gps);
bool feedgps();
void PrintMSG(int);
void printFloat(double f, int digits = 2);

void setup()
{
  pinMode(MEM_PW,OUTPUT);
  digitalWrite(MEM_PW,HIGH);
  Serial.begin(115200);
  nss.begin(4800);
  
  Serial.print("Testing TinyGPS library v. ");    Serial.println(TinyGPS::library_version());
  Serial.println("by Mikal Hart");
  Serial.println();
  Serial.print("Sizeof(gpsobject) = "); Serial.println(sizeof(TinyGPS));
  Serial.println();
}

void loop()
{
  bool newdata = false;
  unsigned long start = millis();

  // Every 5 seconds we print an update
  while (millis() - start < 5000)
  {
    if (feedgps())
      newdata = true;
  }
  
  if (newdata)
  {
    Serial.println("Acquired Data");
    Serial.println("-------------");
    gpsdump(gps);
    Serial.println("-------------");
    Serial.println();
  }
}

void printFloat(double number, int digits)
{
  // Handle negative numbers
  if (number < 0.0)
  {
     Serial.print('-');
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
  
  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  Serial.print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    Serial.print(".");

  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    Serial.print(toPrint);
    remainder -= toPrint;
  }
}

void gpsdump(TinyGPS &gps)
{
  long lat, lon;
  float flat, flon;
  unsigned long age, date, time, chars;
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned short sentences, failed;
  
  int result=0;

  result = FileLogger::append("data.txt", buffer, length);
  PrintMSG(result);

  gps.get_position(&lat, &lon, &age);
  Serial.print("Lat/Long(10^-5 deg): "); Serial.print(lat); Serial.print(", "); Serial.print(lon);
  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");
  
  result = FileLogger::append("data.txt",reinterpret_cast<byte*>(floatToString(buff1, lat, 8)), 8);
  PrintMSG(result);
  result = FileLogger::append("data.txt",reinterpret_cast<byte*>(floatToString(buff2, lon, 8)), 8);
  PrintMSG(result);  
  result = FileLogger::append("data.txt", buffer2, length2);
  PrintMSG(result);  
  
  
  
  feedgps(); // If we don't feed the gps during this long routine, we may drop characters and get checksum errors

  gps.f_get_position(&flat, &flon, &age);
  Serial.print("Lat/Long(float): "); printFloat(flat, 5); Serial.print(", "); printFloat(flon, 5);
  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");

  feedgps();

  gps.get_datetime(&date, &time, &age);
  Serial.print("Date(ddmmyy): "); Serial.print(date); Serial.print(" Time(hhmmsscc): "); Serial.print(time);
  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");

  feedgps();

  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  Serial.print("Date: "); Serial.print(static_cast<int>(month)); Serial.print("/"); Serial.print(static_cast<int>(day)); Serial.print("/"); Serial.print(year);
  Serial.print("  Time: "); Serial.print(static_cast<int>(hour)); Serial.print(":"); Serial.print(static_cast<int>(minute)); Serial.print(":"); Serial.print(static_cast<int>(second)); Serial.print("."); Serial.print(static_cast<int>(hundredths));
  Serial.print("  Fix age: ");  Serial.print(age); Serial.println("ms.");
  
  feedgps();

  Serial.print("Alt(cm): "); Serial.print(gps.altitude()); Serial.print(" Course(10^-2 deg): "); Serial.print(gps.course()); Serial.print(" Speed(10^-2 knots): "); Serial.println(gps.speed());
  Serial.print("Alt(float): "); printFloat(gps.f_altitude()); Serial.print(" Course(float): "); printFloat(gps.f_course()); Serial.println();
  Serial.print("Speed(knots): "); printFloat(gps.f_speed_knots()); Serial.print(" (mph): ");  printFloat(gps.f_speed_mph());
  Serial.print(" (mps): "); printFloat(gps.f_speed_mps()); Serial.print(" (kmph): "); printFloat(gps.f_speed_kmph()); Serial.println();

  feedgps();

  gps.stats(&chars, &sentences, &failed);
  Serial.print("Stats: characters: "); Serial.print(chars); Serial.print(" sentences: "); Serial.print(sentences); Serial.print(" failed checksum: "); Serial.println(failed);
}
  
bool feedgps()
{
  while (nss.available())
  {
    if (gps.encode(nss.read()))
      return true;
  }
  return false;
}
void PrintMSG(int result)
{      
        Serial.print(" Result: ");
        if( result == 0)
        {
          Serial.println("OK");
        }
        else if( result == 1)
        {
          Serial.println("Fail initializing");
        }
        else if( result == 2)
        {
          Serial.println("Fail appending");
        }
}        
« Last Edit: July 23, 2009, 12:17:49 pm by blank » Logged

Toronto, ON
Offline Offline
Full Member
***
Karma: 10
Posts: 233
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, I do have a solution, but it requires getting some extra fixes (which I am hoping to have implemented in either 0017 or 0018) for the serial connections.

Specifically, NewSoftSerial needs to have a peek() method added to it to work.  Maybe I can convince Mikal to modify NewSoftSerial to have the peek() function (it won't impact anyone's existing code).  Nonetheless, I already have a modified version of NewSoftSerial with the peek() method (I've unimaginatively named it NewSoftSerial2).

I am using the uMMC from Rogue Robotics (my company).  I've finished a uMMC library for the Arduino, but I've been doing a few more tweaks to our latest beta firmware, which has a whole whack of new features.  The library will work with our existing firmware, although you will have better performance with the new firmware.

With all that said, here's the sketch that I have written using the uMMC library.  As I said above, there are a bunch of requirements:
  • Modified NewSoftSerial (with peek() method)
  • New version of TinyGPS (patches in opening post of this thread)
  • uMMC from Rogue Robotics and uMMC library for Arduino

You can use the existing "print" methods with which we are all familiar to write to the uMMC.  This means you can reduce your output to something like this:

Code:
ummc1.print(lat);

etc... you can see more in the example below.

Let me know if this is what you're looking for (this example code only logs the data stats from the GPS):

Code:
#include <NewSoftSerial2.h>
#include <TinyGPS.h>
#include <uMMC.h>

#define LED1PIN 8
#define LED2PIN 9
#define LEDOFF 0
#define LEDON  1

NewSoftSerial2 ummc_s(4, 5);
NewSoftSerial2 gps_in(6, 7);

static void printtoummc(char c);
static int readfromummc(void);
static int8_t availableummc(void);
static int peekummc(void);

// this ugliness will hopefully be fixed with SerialComm base class
uMMC ummc1(&availableummc, &peekummc, &readfromummc, &printtoummc);

TinyGPS gps;

char linebuf[20];

void feedgps();

// <begin ugliness>
static void printtoummc(uint8_t c)
{
  ummc_s.print((uint8_t) c);
}

static int readfromummc(void)
{
  return ummc_s.read();
}

static int8_t availableummc(void)
{
  return ummc_s.available();
}

static int peekummc(void)
{
  return ummc_s.peek();
}
// </end ugliness>


void setup()
{
  pinMode(LED1PIN, OUTPUT);
  pinMode(LED2PIN, OUTPUT);
  pinMode(13, OUTPUT);
  digitalWrite(13,1);
  digitalWrite(LED1PIN, LEDOFF);
  digitalWrite(LED2PIN, LEDOFF);
  
  Serial.begin(57600);

  gps_in.begin(4800);
  
  ummc_s.begin(9600);
}

void loop()
{
  int8_t resp, filehandle;
  unsigned short sentences, failed;
  unsigned long age, date, time, chars;
  unsigned long start;
  
  digitalWrite(LED1PIN, LEDOFF);
  digitalWrite(LED2PIN, LEDOFF);

  Serial.println("Initializing uMMC");

  ummc1.sync();
  
  Serial.print("uMMC Version: ");
  Serial.println(ummc1.version());

  // prepare file for writing
  
  filehandle = ummc1.open("/arduinotest1.txt", uMMC::UMMC_OPEN_APPEND);
  
  if(filehandle > 0)
  {
    Serial.print("Open for write: handle ");
    Serial.println(filehandle);
  }
  else
  {
    Serial.print("ERROR: ");
    Serial.println(ummc1.LastErrorCode, HEX);
  }
  
  while(1)
  {
    start = millis();
    
    while(millis() - start < 2000)
    {
      feedgps();
    }
    
    feedgps();
    if(gps.fixtype() > 1)
    {
      gps.get_datetime(&date, &time, &age);
      feedgps();
      Serial.print(date); Serial.print(' '); Serial.println(time);
    }

    feedgps();

    gps.stats(&chars, &sentences, &failed);

    feedgps();

    Serial.print(chars/1024); Serial.print("K "); Serial.print(sentences); Serial.print(' '); Serial.println(failed);

    if(filehandle > 0)
    {
      ummc1.writeln_prep(filehandle);
      ummc1.print(chars/1024); ummc1.print("K "); ummc1.print(sentences); ummc1.print(' '); ummc1.print(failed);
      ummc1.writeln_finish();

      // Just a simple way to close the file
      if(Serial.available())
      {
        if(Serial.read() == 'C')
        {
          ummc1.close(filehandle);
          filehandle = 0;
          Serial.println();
          Serial.println("***File CLOSED");
        }
      }
    }
  }
}



/*****************************************/
// GPS Routines

void feedgps()
{
//  char c;

  while (gps_in.available())
  {
    digitalWrite(LED1PIN, LEDON);
//    c = gps_in.read();
//    Serial.print(c);
    gps.encode(gps_in.read());
  }

  digitalWrite(LED1PIN, LEDOFF);
}

You can pick up a uMMC from one of our distributors if you are interested.

b
Logged


Greece
Offline Offline
Jr. Member
**
Karma: 0
Posts: 75
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Right, thats where I thought the problem lied, in the serial communication cause my program only printed some characters from void(setup) and didnt log anything to my microSD card.

I'm highly interested as the project i'm working on is my diploma project in uni and I have a deadline. I've already bought another microSD module(the libelium one i found in the playground), but if this is going to untie my hands, i'm willing to buy one.

Where can I download Newsoftserial2 and when do you think 0017 will be available?
Logged

Toronto, ON
Offline Offline
Full Member
***
Karma: 10
Posts: 233
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, I doubt SerialComm will get into 0017 since the first RC is out.

I renamed the version of NewSoftSerial2 to NewSoftSerial to avoid having any strange versions floating around.  Hopefully we can get Mikal to incorporate the changes into NewSoftSerial.  (hint, hint)

You can get everything from here:

http://mediafire.com/Arduino

NOTES:

  • you will have to add SerialComm.h and SerialComm.cpp to the arduino/hardware/cores/arduino directory.
  • you will need to use the modified version of NewSoftSerial (download from above location)
  • if you want to use HardwareSerial, you will need to download the modified version (from above) - it will go into the arduino/hardware/cores/arduino directory.

Documentation and more info here: http://code.google.com/p/ummc-library-arduino/

b
« Last Edit: July 26, 2009, 05:42:40 am by bhagman » Logged


Greece
Offline Offline
Jr. Member
**
Karma: 0
Posts: 75
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Im having trouble modifying the lib's test code loop, as i don't understand what gps.fixtype method really does.

Could you please help me?

Code:
void loop()
{
  bool newdata = false;
  unsigned long start = millis();

  // Every 5 seconds we print an update
  while (millis() - start < 5000)
  {
    if (feedgps())
      newdata = true;
  }
  
  if (newdata)
  {
    Serial.println("Acquired Data");
    Serial.println("-------------");
    gpsdump(gps);
    Serial.println("-------------");
    Serial.println();
  }
}

should I add the code inside if (newdata) ?

« Last Edit: March 10, 2010, 11:03:44 am by blank » Logged

Pages: [1]   Go Up
Jump to: