Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of TinyGPS by
TinyGPS.cpp@2:af534d51739f, 2017-05-10 (annotated)
- Committer:
- sonson
- Date:
- Wed May 10 07:23:27 2017 +0000
- Revision:
- 2:af534d51739f
- Parent:
- 1:f522b8bdf987
GPS???????????????
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| shimniok | 0:8a347288f82c | 1 | /* |
| shimniok | 0:8a347288f82c | 2 | TinyGPS - a small GPS library for Arduino providing basic NMEA parsing |
| shimniok | 0:8a347288f82c | 3 | Copyright (C) 2008-9 Mikal Hart |
| shimniok | 0:8a347288f82c | 4 | All rights reserved. |
| shimniok | 0:8a347288f82c | 5 | |
| shimniok | 0:8a347288f82c | 6 | This library is free software; you can redistribute it and/or |
| shimniok | 0:8a347288f82c | 7 | modify it under the terms of the GNU Lesser General Public |
| shimniok | 0:8a347288f82c | 8 | License as published by the Free Software Foundation; either |
| shimniok | 0:8a347288f82c | 9 | version 2.1 of the License, or (at your option) any later version. |
| shimniok | 0:8a347288f82c | 10 | |
| shimniok | 0:8a347288f82c | 11 | This library is distributed in the hope that it will be useful, |
| shimniok | 0:8a347288f82c | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| shimniok | 0:8a347288f82c | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| shimniok | 0:8a347288f82c | 14 | Lesser General Public License for more details. |
| shimniok | 0:8a347288f82c | 15 | |
| shimniok | 0:8a347288f82c | 16 | You should have received a copy of the GNU Lesser General Public |
| shimniok | 0:8a347288f82c | 17 | License along with this library; if not, write to the Free Software |
| shimniok | 0:8a347288f82c | 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| shimniok | 0:8a347288f82c | 19 | |
| shimniok | 0:8a347288f82c | 20 | Ported to mbed by Michael Shimniok http://www.bot-thoughts.com/ |
| shimniok | 0:8a347288f82c | 21 | */ |
| shimniok | 0:8a347288f82c | 22 | |
| shimniok | 0:8a347288f82c | 23 | #include "TinyGPS.h" |
| shimniok | 0:8a347288f82c | 24 | |
| shimniok | 0:8a347288f82c | 25 | #define _GPRMC_TERM "GPRMC" |
| shimniok | 0:8a347288f82c | 26 | #define _GPGGA_TERM "GPGGA" |
| shimniok | 0:8a347288f82c | 27 | #define _GPGSV_TERM "GPGSV" |
| shimniok | 0:8a347288f82c | 28 | |
| shimniok | 0:8a347288f82c | 29 | TinyGPS::TinyGPS() |
| shimniok | 0:8a347288f82c | 30 | : _time(GPS_INVALID_TIME) |
| shimniok | 0:8a347288f82c | 31 | , _date(GPS_INVALID_DATE) |
| shimniok | 0:8a347288f82c | 32 | , _latitude(GPS_INVALID_ANGLE) |
| shimniok | 0:8a347288f82c | 33 | , _longitude(GPS_INVALID_ANGLE) |
| shimniok | 0:8a347288f82c | 34 | , _altitude(GPS_INVALID_ALTITUDE) |
| shimniok | 0:8a347288f82c | 35 | , _speed(GPS_INVALID_SPEED) |
| shimniok | 0:8a347288f82c | 36 | , _course(GPS_INVALID_ANGLE) |
| shimniok | 0:8a347288f82c | 37 | , _hdop(0) |
| shimniok | 0:8a347288f82c | 38 | , _sat_count(0) |
| shimniok | 0:8a347288f82c | 39 | , _last_time_fix(GPS_INVALID_FIX_TIME) |
| shimniok | 0:8a347288f82c | 40 | , _last_position_fix(GPS_INVALID_FIX_TIME) |
| shimniok | 0:8a347288f82c | 41 | , _parity(0) |
| shimniok | 0:8a347288f82c | 42 | , _is_checksum_term(false) |
| shimniok | 0:8a347288f82c | 43 | , _sentence_type(_GPS_SENTENCE_OTHER) |
| shimniok | 0:8a347288f82c | 44 | , _term_number(0) |
| shimniok | 0:8a347288f82c | 45 | , _term_offset(0) |
| shimniok | 0:8a347288f82c | 46 | , _gps_data_good(false) |
| shimniok | 0:8a347288f82c | 47 | , _rmc_ready(false) |
| shimniok | 0:8a347288f82c | 48 | , _gga_ready(false) |
| shimniok | 0:8a347288f82c | 49 | , _gsv_ready(false) |
| shimniok | 0:8a347288f82c | 50 | #ifndef _GPS_NO_STATS |
| shimniok | 0:8a347288f82c | 51 | , _encoded_characters(0) |
| shimniok | 0:8a347288f82c | 52 | , _good_sentences(0) |
| shimniok | 0:8a347288f82c | 53 | , _failed_checksum(0) |
| shimniok | 0:8a347288f82c | 54 | #endif |
| shimniok | 0:8a347288f82c | 55 | { |
| shimniok | 0:8a347288f82c | 56 | _term[0] = '\0'; |
| shimniok | 0:8a347288f82c | 57 | } |
| shimniok | 0:8a347288f82c | 58 | |
| shimniok | 0:8a347288f82c | 59 | // |
| shimniok | 0:8a347288f82c | 60 | // public methods |
| shimniok | 0:8a347288f82c | 61 | // |
| shimniok | 0:8a347288f82c | 62 | |
| shimniok | 0:8a347288f82c | 63 | bool TinyGPS::encode(char c) |
| shimniok | 0:8a347288f82c | 64 | { |
| shimniok | 0:8a347288f82c | 65 | bool valid_sentence = false; |
| shimniok | 0:8a347288f82c | 66 | |
| shimniok | 0:8a347288f82c | 67 | ++_encoded_characters; |
| shimniok | 0:8a347288f82c | 68 | switch(c) |
| shimniok | 0:8a347288f82c | 69 | { |
| shimniok | 0:8a347288f82c | 70 | case ',': // term terminators |
| shimniok | 0:8a347288f82c | 71 | _parity ^= c; |
| shimniok | 0:8a347288f82c | 72 | case '\r': |
| shimniok | 0:8a347288f82c | 73 | case '\n': |
| shimniok | 0:8a347288f82c | 74 | case '*': |
| shimniok | 0:8a347288f82c | 75 | if (_term_offset < sizeof(_term)) |
| shimniok | 0:8a347288f82c | 76 | { |
| shimniok | 0:8a347288f82c | 77 | _term[_term_offset] = 0; |
| shimniok | 0:8a347288f82c | 78 | valid_sentence = term_complete(); |
| shimniok | 0:8a347288f82c | 79 | } |
| shimniok | 0:8a347288f82c | 80 | ++_term_number; |
| shimniok | 0:8a347288f82c | 81 | _term_offset = 0; |
| shimniok | 0:8a347288f82c | 82 | _is_checksum_term = c == '*'; |
| shimniok | 0:8a347288f82c | 83 | return valid_sentence; |
| shimniok | 0:8a347288f82c | 84 | |
| shimniok | 0:8a347288f82c | 85 | case '$': // sentence begin |
| shimniok | 0:8a347288f82c | 86 | _term_number = _term_offset = 0; |
| shimniok | 0:8a347288f82c | 87 | _parity = 0; |
| shimniok | 0:8a347288f82c | 88 | _sentence_type = _GPS_SENTENCE_OTHER; |
| shimniok | 0:8a347288f82c | 89 | _is_checksum_term = false; |
| shimniok | 0:8a347288f82c | 90 | _gps_data_good = false; |
| shimniok | 0:8a347288f82c | 91 | return valid_sentence; |
| shimniok | 0:8a347288f82c | 92 | } |
| shimniok | 0:8a347288f82c | 93 | |
| shimniok | 0:8a347288f82c | 94 | // ordinary characters |
| shimniok | 0:8a347288f82c | 95 | if (_term_offset < sizeof(_term) - 1) |
| shimniok | 0:8a347288f82c | 96 | _term[_term_offset++] = c; |
| shimniok | 0:8a347288f82c | 97 | if (!_is_checksum_term) |
| shimniok | 0:8a347288f82c | 98 | _parity ^= c; |
| shimniok | 0:8a347288f82c | 99 | |
| shimniok | 0:8a347288f82c | 100 | return valid_sentence; |
| shimniok | 0:8a347288f82c | 101 | } |
| shimniok | 0:8a347288f82c | 102 | |
| shimniok | 0:8a347288f82c | 103 | #ifndef _GPS_NO_STATS |
| shimniok | 0:8a347288f82c | 104 | void TinyGPS::stats(unsigned long *chars, unsigned short *sentences, unsigned short *failed_cs) |
| shimniok | 0:8a347288f82c | 105 | { |
| shimniok | 0:8a347288f82c | 106 | if (chars) *chars = _encoded_characters; |
| shimniok | 0:8a347288f82c | 107 | if (sentences) *sentences = _good_sentences; |
| shimniok | 0:8a347288f82c | 108 | if (failed_cs) *failed_cs = _failed_checksum; |
| shimniok | 0:8a347288f82c | 109 | } |
| shimniok | 0:8a347288f82c | 110 | #endif |
| shimniok | 0:8a347288f82c | 111 | |
| shimniok | 0:8a347288f82c | 112 | // |
| shimniok | 0:8a347288f82c | 113 | // internal utilities |
| shimniok | 0:8a347288f82c | 114 | // |
| shimniok | 0:8a347288f82c | 115 | int TinyGPS::from_hex(char a) |
| shimniok | 0:8a347288f82c | 116 | { |
| shimniok | 0:8a347288f82c | 117 | if (a >= 'A' && a <= 'F') |
| shimniok | 0:8a347288f82c | 118 | return a - 'A' + 10; |
| shimniok | 0:8a347288f82c | 119 | else if (a >= 'a' && a <= 'f') |
| shimniok | 0:8a347288f82c | 120 | return a - 'a' + 10; |
| shimniok | 0:8a347288f82c | 121 | else |
| shimniok | 0:8a347288f82c | 122 | return a - '0'; |
| shimniok | 0:8a347288f82c | 123 | } |
| shimniok | 0:8a347288f82c | 124 | |
| shimniok | 0:8a347288f82c | 125 | unsigned long TinyGPS::parse_decimal() |
| shimniok | 0:8a347288f82c | 126 | { |
| shimniok | 0:8a347288f82c | 127 | char *p = _term; |
| shimniok | 0:8a347288f82c | 128 | bool isneg = *p == '-'; |
| shimniok | 0:8a347288f82c | 129 | if (isneg) ++p; |
| shimniok | 0:8a347288f82c | 130 | unsigned long ret = 100UL * gpsatol(p); |
| shimniok | 0:8a347288f82c | 131 | while (gpsisdigit(*p)) ++p; |
| shimniok | 0:8a347288f82c | 132 | if (*p == '.') |
| shimniok | 0:8a347288f82c | 133 | { |
| shimniok | 0:8a347288f82c | 134 | if (gpsisdigit(p[1])) |
| shimniok | 0:8a347288f82c | 135 | { |
| shimniok | 0:8a347288f82c | 136 | ret += 10 * (p[1] - '0'); |
| shimniok | 0:8a347288f82c | 137 | if (gpsisdigit(p[2])) |
| shimniok | 0:8a347288f82c | 138 | ret += p[2] - '0'; |
| shimniok | 0:8a347288f82c | 139 | } |
| shimniok | 0:8a347288f82c | 140 | } |
| shimniok | 0:8a347288f82c | 141 | return isneg ? -ret : ret; |
| shimniok | 0:8a347288f82c | 142 | } |
| shimniok | 0:8a347288f82c | 143 | |
| shimniok | 0:8a347288f82c | 144 | unsigned long TinyGPS::parse_degrees() |
| shimniok | 0:8a347288f82c | 145 | { |
| shimniok | 0:8a347288f82c | 146 | char *p; |
| shimniok | 0:8a347288f82c | 147 | unsigned long left = gpsatol(_term); |
| shimniok | 0:8a347288f82c | 148 | unsigned long tenk_minutes = (left % 100UL) * 10000UL; |
| shimniok | 0:8a347288f82c | 149 | for (p=_term; gpsisdigit(*p); ++p); |
| shimniok | 0:8a347288f82c | 150 | if (*p == '.') |
| shimniok | 0:8a347288f82c | 151 | { |
| shimniok | 0:8a347288f82c | 152 | unsigned long mult = 1000; |
| shimniok | 0:8a347288f82c | 153 | while (gpsisdigit(*++p)) |
| shimniok | 0:8a347288f82c | 154 | { |
| shimniok | 0:8a347288f82c | 155 | tenk_minutes += mult * (*p - '0'); |
| shimniok | 0:8a347288f82c | 156 | mult /= 10; |
| shimniok | 0:8a347288f82c | 157 | } |
| shimniok | 0:8a347288f82c | 158 | } |
| shimniok | 0:8a347288f82c | 159 | return (left / 100) * 100000 + tenk_minutes / 6; |
| shimniok | 0:8a347288f82c | 160 | } |
| shimniok | 0:8a347288f82c | 161 | |
| shimniok | 0:8a347288f82c | 162 | // Processes a just-completed term |
| shimniok | 0:8a347288f82c | 163 | // Returns true if new sentence has just passed checksum test and is validated |
| shimniok | 0:8a347288f82c | 164 | bool TinyGPS::term_complete() |
| shimniok | 0:8a347288f82c | 165 | { |
| shimniok | 0:8a347288f82c | 166 | if (_is_checksum_term) |
| shimniok | 0:8a347288f82c | 167 | { |
| shimniok | 0:8a347288f82c | 168 | byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]); |
| shimniok | 0:8a347288f82c | 169 | if (checksum == _parity) |
| shimniok | 0:8a347288f82c | 170 | { |
| shimniok | 1:f522b8bdf987 | 171 | if (_gps_data_good) |
| shimniok | 0:8a347288f82c | 172 | { |
| shimniok | 0:8a347288f82c | 173 | #ifndef _GPS_NO_STATS |
| shimniok | 0:8a347288f82c | 174 | ++_good_sentences; |
| shimniok | 0:8a347288f82c | 175 | #endif |
| shimniok | 0:8a347288f82c | 176 | _last_time_fix = _new_time_fix; |
| shimniok | 0:8a347288f82c | 177 | _last_position_fix = _new_position_fix; |
| shimniok | 0:8a347288f82c | 178 | |
| shimniok | 0:8a347288f82c | 179 | switch(_sentence_type) |
| shimniok | 0:8a347288f82c | 180 | { |
| shimniok | 0:8a347288f82c | 181 | case _GPS_SENTENCE_GPRMC: |
| shimniok | 0:8a347288f82c | 182 | _time = _new_time; |
| shimniok | 0:8a347288f82c | 183 | _date = _new_date; |
| shimniok | 0:8a347288f82c | 184 | _latitude = _new_latitude; |
| shimniok | 0:8a347288f82c | 185 | _longitude = _new_longitude; |
| shimniok | 0:8a347288f82c | 186 | _speed = _new_speed; |
| shimniok | 0:8a347288f82c | 187 | _course = _new_course; |
| shimniok | 0:8a347288f82c | 188 | _rmc_ready = true; |
| shimniok | 0:8a347288f82c | 189 | break; |
| shimniok | 0:8a347288f82c | 190 | case _GPS_SENTENCE_GPGGA: |
| shimniok | 0:8a347288f82c | 191 | _altitude = _new_altitude; |
| shimniok | 0:8a347288f82c | 192 | _time = _new_time; |
| shimniok | 0:8a347288f82c | 193 | _latitude = _new_latitude; |
| shimniok | 0:8a347288f82c | 194 | _longitude = _new_longitude; |
| shimniok | 0:8a347288f82c | 195 | _gga_ready = true; |
| shimniok | 0:8a347288f82c | 196 | _hdop = _new_hdop; |
| shimniok | 0:8a347288f82c | 197 | _sat_count = _new_sat_count; |
| shimniok | 0:8a347288f82c | 198 | case _GPS_SENTENCE_GPGSV: |
| shimniok | 0:8a347288f82c | 199 | _gsv_ready = true; |
| shimniok | 0:8a347288f82c | 200 | break; |
| shimniok | 0:8a347288f82c | 201 | } |
| shimniok | 0:8a347288f82c | 202 | |
| shimniok | 0:8a347288f82c | 203 | return true; |
| shimniok | 0:8a347288f82c | 204 | } |
| shimniok | 0:8a347288f82c | 205 | } |
| shimniok | 0:8a347288f82c | 206 | |
| shimniok | 0:8a347288f82c | 207 | #ifndef _GPS_NO_STATS |
| shimniok | 0:8a347288f82c | 208 | else |
| shimniok | 0:8a347288f82c | 209 | ++_failed_checksum; |
| shimniok | 0:8a347288f82c | 210 | #endif |
| shimniok | 0:8a347288f82c | 211 | return false; |
| shimniok | 0:8a347288f82c | 212 | } |
| shimniok | 0:8a347288f82c | 213 | |
| shimniok | 0:8a347288f82c | 214 | // the first term determines the sentence type |
| shimniok | 0:8a347288f82c | 215 | if (_term_number == 0) |
| shimniok | 0:8a347288f82c | 216 | { |
| shimniok | 0:8a347288f82c | 217 | if (!gpsstrcmp(_term, _GPRMC_TERM)) |
| shimniok | 0:8a347288f82c | 218 | _sentence_type = _GPS_SENTENCE_GPRMC; |
| shimniok | 0:8a347288f82c | 219 | else if (!gpsstrcmp(_term, _GPGGA_TERM)) |
| shimniok | 0:8a347288f82c | 220 | _sentence_type = _GPS_SENTENCE_GPGGA; |
| shimniok | 0:8a347288f82c | 221 | else if (!gpsstrcmp(_term, _GPGSV_TERM)) |
| shimniok | 0:8a347288f82c | 222 | _sentence_type = _GPS_SENTENCE_GPGSV; |
| shimniok | 0:8a347288f82c | 223 | else |
| shimniok | 0:8a347288f82c | 224 | _sentence_type = _GPS_SENTENCE_OTHER; |
| shimniok | 0:8a347288f82c | 225 | return false; |
| shimniok | 0:8a347288f82c | 226 | } |
| shimniok | 0:8a347288f82c | 227 | |
| shimniok | 0:8a347288f82c | 228 | if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0]) |
| shimniok | 0:8a347288f82c | 229 | switch((_sentence_type == _GPS_SENTENCE_GPGGA ? 200 : 100) + _term_number) |
| shimniok | 0:8a347288f82c | 230 | { |
| shimniok | 0:8a347288f82c | 231 | case 101: // Time in both sentences |
| shimniok | 0:8a347288f82c | 232 | case 201: |
| shimniok | 0:8a347288f82c | 233 | _new_time = parse_decimal(); |
| shimniok | 0:8a347288f82c | 234 | _new_time_fix = millis(); |
| shimniok | 0:8a347288f82c | 235 | break; |
| shimniok | 0:8a347288f82c | 236 | case 102: // GPRMC validity |
| shimniok | 0:8a347288f82c | 237 | _gps_data_good = _term[0] == 'A'; |
| shimniok | 0:8a347288f82c | 238 | break; |
| shimniok | 0:8a347288f82c | 239 | case 103: // Latitude |
| shimniok | 0:8a347288f82c | 240 | case 202: |
| shimniok | 0:8a347288f82c | 241 | _new_latitude = parse_degrees(); |
| shimniok | 0:8a347288f82c | 242 | _new_position_fix = millis(); |
| shimniok | 0:8a347288f82c | 243 | break; |
| shimniok | 0:8a347288f82c | 244 | case 104: // N/S |
| shimniok | 0:8a347288f82c | 245 | case 203: |
| shimniok | 0:8a347288f82c | 246 | if (_term[0] == 'S') |
| shimniok | 0:8a347288f82c | 247 | _new_latitude = -_new_latitude; |
| shimniok | 0:8a347288f82c | 248 | break; |
| shimniok | 0:8a347288f82c | 249 | case 105: // Longitude |
| shimniok | 0:8a347288f82c | 250 | case 204: |
| shimniok | 0:8a347288f82c | 251 | _new_longitude = parse_degrees(); |
| shimniok | 0:8a347288f82c | 252 | break; |
| shimniok | 0:8a347288f82c | 253 | case 106: // E/W |
| shimniok | 0:8a347288f82c | 254 | case 205: |
| shimniok | 0:8a347288f82c | 255 | if (_term[0] == 'W') |
| shimniok | 0:8a347288f82c | 256 | _new_longitude = -_new_longitude; |
| shimniok | 0:8a347288f82c | 257 | break; |
| shimniok | 0:8a347288f82c | 258 | case 107: // Speed (GPRMC) |
| shimniok | 0:8a347288f82c | 259 | _new_speed = parse_decimal(); |
| shimniok | 0:8a347288f82c | 260 | break; |
| shimniok | 0:8a347288f82c | 261 | case 108: // Course (GPRMC) |
| shimniok | 0:8a347288f82c | 262 | _new_course = parse_decimal(); |
| shimniok | 0:8a347288f82c | 263 | break; |
| shimniok | 0:8a347288f82c | 264 | case 109: // Date (GPRMC) |
| shimniok | 0:8a347288f82c | 265 | _new_date = gpsatol(_term); |
| shimniok | 0:8a347288f82c | 266 | break; |
| shimniok | 0:8a347288f82c | 267 | case 206: // Fix data (GPGGA) |
| shimniok | 0:8a347288f82c | 268 | _gps_data_good = _term[0] > '0'; |
| shimniok | 0:8a347288f82c | 269 | break; |
| shimniok | 0:8a347288f82c | 270 | case 207: // Number of satelites tracked (GPGGA) |
| shimniok | 0:8a347288f82c | 271 | _new_sat_count = parse_decimal(); |
| shimniok | 0:8a347288f82c | 272 | break; |
| shimniok | 0:8a347288f82c | 273 | case 208: // Horizontal Dilution of Position (GPGGA) |
| shimniok | 0:8a347288f82c | 274 | _new_hdop = parse_decimal(); |
| shimniok | 0:8a347288f82c | 275 | break; |
| shimniok | 0:8a347288f82c | 276 | case 209: // Altitude (GPGGA) |
| shimniok | 0:8a347288f82c | 277 | _new_altitude = parse_decimal(); |
| shimniok | 0:8a347288f82c | 278 | break; |
| shimniok | 0:8a347288f82c | 279 | } /* switch */ |
| shimniok | 0:8a347288f82c | 280 | |
| shimniok | 0:8a347288f82c | 281 | return false; |
| shimniok | 0:8a347288f82c | 282 | } |
| shimniok | 0:8a347288f82c | 283 | |
| shimniok | 0:8a347288f82c | 284 | long TinyGPS::gpsatol(const char *str) |
| shimniok | 0:8a347288f82c | 285 | { |
| shimniok | 0:8a347288f82c | 286 | long ret = 0; |
| shimniok | 0:8a347288f82c | 287 | while (gpsisdigit(*str)) |
| shimniok | 0:8a347288f82c | 288 | ret = 10 * ret + *str++ - '0'; |
| shimniok | 0:8a347288f82c | 289 | return ret; |
| shimniok | 0:8a347288f82c | 290 | } |
| shimniok | 0:8a347288f82c | 291 | |
| shimniok | 0:8a347288f82c | 292 | int TinyGPS::gpsstrcmp(const char *str1, const char *str2) |
| shimniok | 0:8a347288f82c | 293 | { |
| shimniok | 0:8a347288f82c | 294 | while (*str1 && *str1 == *str2) |
| shimniok | 0:8a347288f82c | 295 | ++str1, ++str2; |
| shimniok | 0:8a347288f82c | 296 | return *str1; |
| shimniok | 0:8a347288f82c | 297 | } |
