Skip to content

Commit

Permalink
Support added for NMEA Data Protocol v3.x and GLONASS
Browse files Browse the repository at this point in the history
  • Loading branch information
florind committed Sep 1, 2015
1 parent db4ef9c commit 39b72ac
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 3 deletions.
73 changes: 73 additions & 0 deletions Readme.md
@@ -0,0 +1,73 @@
# TinyGPS refresh for NMEA Data Protocol v3.x and GLONASS

This update adds support for newer NMEA-capable GPS devices that implement the [v3.x GNSS spec](http://geostar-navigation.com/file/geos3/geos_nmea_protocol_v3_0_eng.pdf) as well as devices that support [GLONASS](https://en.wikipedia.org/wiki/GLONASS).

#### The following new sentences are now supported:

1. NMEA GPS sentence:
* GPS Satellites in view [GPGSV](http://aprs.gids.nl/nmea/#gsv)
2. GNSS sentences:
* GNRMC (same with GPRMC)
* GNGNS
3. GLONASS sentences:
* GLGSV

#### Tracking satellites in view for both GPS and GLONASS constellations.

This allows for building e.g. an advanced GUI that shows the satellites in view and their respective strength both when searching and when tracking.
The data is accessible via a new method: `uint32_t[] trackedSattelites()` that returns an array of uint32 that (to keep the additional memory footprint at a minimum) encodes the useful data as follows (check [GPGSV](http://aprs.gids.nl/nmea/#gsv) for a comprehensive explanation ):

* bit 0-7: sattelite ID
* bit 8-14: SNR (dB), max 99dB
* bit 15: this sattelite is used in solution (not implemented yet)

The uint32 array size is 24, that is, it tracks up to 12 satellites for each constellation, GPS and GLONASS. 12 is the maximum number of satellites in view per constellation at any given point on Earth.
```c
...
char buf[32];
uint32_t* satz = tinygps.trackedSatellites();
uint8_t sat_count = 0;
for(int i=0;i<24;i++)
{
if(satz[i] != 0) //exclude zero SNR sats
{
sat_count++;
sprintf(buf, "PRN %d: %ddB ", satz[i]>>8, (satz[i]&0xFF)>>1);
Serial.println(buf);
}
}

```
This code produces an output of this form: ```PRN 21: 31dB PRN 11: 25dB PRN 71: 24dB ...``` where satellite ID and the strength are displayed for each satellite in view.
Additional notes from NMEA protocol v3.0:
> GPS satellites are identified by their PRN numbers, which range from 1 to 32.
> The numbers 65-96 are reserved for GLONASS satellites. GLONASS satellites are identified by 64+satellite slot number. The slot numbers are 1 through 24 for the full constellation of 24 satellites, this gives a range of 65 through 88. The numbers 89 through 96 are available if slot numbers above 24 are allocated to on-orbit spares.
The array is grouped by constellations with GPS first.
#### GNS mode indicator
Fix data now includes a field that shows which constellations are used when tracking, Accessible via a new ```char[] constellations()``` method that returns a char array on the following spec:
> Mode Indicator:
A variable length valid character field type with the first two characters currently defined. The first character indicates the use of GPS satellites, the second character indicates the use of GLONASS satellites. If another satellite system is added to the standard, the mode indicator will be extended to three characters, new satellite systems shall always be added on the right, so the order of characters in the Mode Indicator is: GPS, GLONASS, other satellite systems in the future.
The characters shall take one of the following values:
* N = No fix
* A = Autonomous mode
* D = Differential mode
* P = Precise mode is used to compute position fix
* R = Real Time Kinematic
* F = Float RTK
* E = Estimated (dead reckoning) mode
* M = Manual input mode
* S = Simulator mode.
For example, a return of ```AA``` when calling ```constellations()``` means that both GPS and GLONASS are used when the device is tracking, ```AN``` means only GPS is used, etc.
#### Time and date available when the GPS is not tracking
Now the time and date are also updated even when the device is not tracking since a valid date and time is computed when enough satellites are in view. Use with caution as it may yield false date and time.
90 changes: 88 additions & 2 deletions TinyGPS.cpp
Expand Up @@ -25,6 +25,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#define _GPRMC_TERM "GPRMC"
#define _GPGGA_TERM "GPGGA"
#define _GPGSA_TERM "GPGSA"
#define _GNRMC_TERM "GNRMC"
#define _GNGNS_TERM "GNGNS"
#define _GNGSA_TERM "GNGSA"
#define _GPGSV_TERM "GPGSV"
#define _GLGSV_TERM "GLGSV"

TinyGPS::TinyGPS()
: _time(GPS_INVALID_TIME)
Expand Down Expand Up @@ -170,6 +176,11 @@ bool TinyGPS::term_complete()
byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]);
if (checksum == _parity)
{
if(_sentence_type == _GPS_SENTENCE_GPRMC) //set the time and date even if not tracking
{
_time = _new_time;
_date = _new_date;
}
if (_gps_data_good)
{
#ifndef _GPS_NO_STATS
Expand Down Expand Up @@ -212,10 +223,18 @@ bool TinyGPS::term_complete()
// the first term determines the sentence type
if (_term_number == 0)
{
if (!gpsstrcmp(_term, _GPRMC_TERM))
if (!gpsstrcmp(_term, _GPRMC_TERM) || !gpsstrcmp(_term, _GNRMC_TERM))
_sentence_type = _GPS_SENTENCE_GPRMC;
else if (!gpsstrcmp(_term, _GPGGA_TERM))
_sentence_type = _GPS_SENTENCE_GPGGA;
else if (!gpsstrcmp(_term, _GNGNS_TERM))
_sentence_type = _GPS_SENTENCE_GNGNS;
else if (!gpsstrcmp(_term, _GNGSA_TERM) || !gpsstrcmp(_term, _GPGSA_TERM))
_sentence_type = _GPS_SENTENCE_GNGSA;
else if (!gpsstrcmp(_term, _GPGSV_TERM))
_sentence_type = _GPS_SENTENCE_GPGSV;
else if (!gpsstrcmp(_term, _GLGSV_TERM))
_sentence_type = _GPS_SENTENCE_GLGSV;
else
_sentence_type = _GPS_SENTENCE_OTHER;
return false;
Expand All @@ -226,6 +245,7 @@ bool TinyGPS::term_complete()
{
case COMBINE(_GPS_SENTENCE_GPRMC, 1): // Time in both sentences
case COMBINE(_GPS_SENTENCE_GPGGA, 1):
case COMBINE(_GPS_SENTENCE_GNGNS, 1):
_new_time = parse_decimal();
_new_time_fix = millis();
break;
Expand All @@ -234,23 +254,30 @@ bool TinyGPS::term_complete()
break;
case COMBINE(_GPS_SENTENCE_GPRMC, 3): // Latitude
case COMBINE(_GPS_SENTENCE_GPGGA, 2):
case COMBINE(_GPS_SENTENCE_GNGNS, 2):
_new_latitude = parse_degrees();
_new_position_fix = millis();
break;
case COMBINE(_GPS_SENTENCE_GPRMC, 4): // N/S
case COMBINE(_GPS_SENTENCE_GPGGA, 3):
case COMBINE(_GPS_SENTENCE_GNGNS, 3):
if (_term[0] == 'S')
_new_latitude = -_new_latitude;
break;
case COMBINE(_GPS_SENTENCE_GPRMC, 5): // Longitude
case COMBINE(_GPS_SENTENCE_GPGGA, 4):
case COMBINE(_GPS_SENTENCE_GNGNS, 4):
_new_longitude = parse_degrees();
break;
case COMBINE(_GPS_SENTENCE_GPRMC, 6): // E/W
case COMBINE(_GPS_SENTENCE_GPGGA, 5):
case COMBINE(_GPS_SENTENCE_GNGNS, 5):
if (_term[0] == 'W')
_new_longitude = -_new_longitude;
break;
case COMBINE(_GPS_SENTENCE_GNGNS, 6):
strncpy(_constellations, _term, 5);
break;
case COMBINE(_GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC)
_new_speed = parse_decimal();
break;
Expand All @@ -263,7 +290,8 @@ bool TinyGPS::term_complete()
case COMBINE(_GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA)
_gps_data_good = _term[0] > '0';
break;
case COMBINE(_GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA)
case COMBINE(_GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA): GPS only
case COMBINE(_GPS_SENTENCE_GNGNS, 7): // GNGNS counts-in all constellations
_new_numsats = (unsigned char)atoi(_term);
break;
case COMBINE(_GPS_SENTENCE_GPGGA, 8): // HDOP
Expand All @@ -272,6 +300,64 @@ bool TinyGPS::term_complete()
case COMBINE(_GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA)
_new_altitude = parse_decimal();
break;
case COMBINE(_GPS_SENTENCE_GNGSA, 3): //satellites used in solution: 3 to 15
//_sats_used[
break;
case COMBINE(_GPS_SENTENCE_GPGSV, 2): //beginning of sequence
case COMBINE(_GPS_SENTENCE_GLGSV, 2): //beginning of sequence
{
uint8_t msgId = atoi(_term)-1; //start from 0
if(msgId == 0) {
//http://geostar-navigation.com/file/geos3/geos_nmea_protocol_v3_0_eng.pdf
if(_sentence_type == _GPS_SENTENCE_GPGSV) {
//reset GPS & WAAS trackedSatellites
for(uint8_t x=0;x<12;x++)
{
tracked_sat_rec[x] = 0;
}
} else {
//reset GLONASS trackedSatellites: range starts with 23
for(uint8_t x=12;x<24;x++)
{
tracked_sat_rec[x] = 0;
}
}
}
_sat_index = msgId*4; //4 sattelites/line
if(_sentence_type == _GPS_SENTENCE_GLGSV)
{
_sat_index = msgId*4 + 12; //Glonass offset by 12
}
break;
}
case COMBINE(_GPS_SENTENCE_GPGSV, 4): //satellite #
case COMBINE(_GPS_SENTENCE_GPGSV, 8):
case COMBINE(_GPS_SENTENCE_GPGSV, 12):
case COMBINE(_GPS_SENTENCE_GPGSV, 16):
case COMBINE(_GPS_SENTENCE_GLGSV, 4):
case COMBINE(_GPS_SENTENCE_GLGSV, 8):
case COMBINE(_GPS_SENTENCE_GLGSV, 12):
case COMBINE(_GPS_SENTENCE_GLGSV, 16):
_tracked_satellites_index = atoi(_term);
break;
case COMBINE(_GPS_SENTENCE_GPGSV, 7): //strength
case COMBINE(_GPS_SENTENCE_GPGSV, 11):
case COMBINE(_GPS_SENTENCE_GPGSV, 15):
case COMBINE(_GPS_SENTENCE_GPGSV, 19):
case COMBINE(_GPS_SENTENCE_GLGSV, 7): //strength
case COMBINE(_GPS_SENTENCE_GLGSV, 11):
case COMBINE(_GPS_SENTENCE_GLGSV, 15):
case COMBINE(_GPS_SENTENCE_GLGSV, 19):
uint8_t stren = (uint8_t)atoi(_term);
if(stren == 0) //remove the record, 0dB strength
{
tracked_sat_rec[_sat_index + (_term_number-7)/4] = 0;
}
else
{
tracked_sat_rec[_sat_index + (_term_number-7)/4] = _tracked_satellites_index<<8 | stren<<1;
}
break;
}

return false;
Expand Down
22 changes: 21 additions & 1 deletion TinyGPS.h
Expand Up @@ -79,6 +79,9 @@ class TinyGPS
// horizontal dilution of precision in 100ths
inline unsigned long hdop() { return _hdop; }

inline char* constellations() { return _constellations; }
inline uint32_t* trackedSatellites() { return tracked_sat_rec; }

void f_get_position(float *latitude, float *longitude, unsigned long *fix_age = 0);
void crack_datetime(int *year, byte *month, byte *day,
byte *hour, byte *minute, byte *second, byte *hundredths = 0, unsigned long *fix_age = 0);
Expand All @@ -100,7 +103,8 @@ class TinyGPS
#endif

private:
enum {_GPS_SENTENCE_GPGGA, _GPS_SENTENCE_GPRMC, _GPS_SENTENCE_OTHER};
enum {_GPS_SENTENCE_GPGGA, _GPS_SENTENCE_GPRMC, _GPS_SENTENCE_GNGNS, _GPS_SENTENCE_GNGSA,
_GPS_SENTENCE_GPGSV, _GPS_SENTENCE_GLGSV, _GPS_SENTENCE_OTHER};

// properties
unsigned long _time, _new_time;
Expand All @@ -124,6 +128,22 @@ class TinyGPS
byte _term_number;
byte _term_offset;
bool _gps_data_good;

struct TrackedSattelites {
uint8_t prn; //"pseudo-random noise" sequences, or Gold codes. GPS sats are listed here http://en.wikipedia.org/wiki/List_of_GPS_satellites
uint8_t strength; //in dB
};

char _constellations[5];
uint8_t _sat_used_count;

//format:
//bit 0-7: sat ID
//bit 8-14: snr (dB), max 99dB
//bit 15: used in solution (when tracking)
uint32_t tracked_sat_rec[24]; //TODO: externalize array size
int _tracked_satellites_index;
uint8_t _sat_index;

#ifndef _GPS_NO_STATS
// statistics
Expand Down

0 comments on commit 39b72ac

Please sign in to comment.