Last version of TinyGPS library
Dependents: Nucleo_gps mbed_2018 mbed_2019_rx3 mbed_2019 ... more
TinyGPS.cpp
00001 /* 00002 TinyGPS - a small GPS library for Arduino providing basic NMEA parsing 00003 Based on work by and "distance_to" and "course_to" courtesy of Maarten Lamers. 00004 Suggestion to add satellites(), course_to(), and cardinal(), by Matt Monson. 00005 Precision improvements suggested by Wayne Holder. 00006 Copyright (C) 2008-2013 Mikal Hart 00007 All rights reserved. 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Lesser General Public 00011 License as published by the Free Software Foundation; either 00012 version 2.1 of the License, or (at your option) any later version. 00013 00014 This library is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 Lesser General Public License for more details. 00018 00019 You should have received a copy of the GNU Lesser General Public 00020 License along with this library; if not, write to the Free Software 00021 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00022 00023 MBED port by Sergey Drobyshevskiy (dROb) 00024 */ 00025 00026 #include "TinyGPS.h" 00027 00028 #define _GPRMC_TERM "GPRMC" 00029 #define _GPGGA_TERM "GPGGA" 00030 00031 TinyGPS::TinyGPS() 00032 : _time(GPS_INVALID_TIME) 00033 , _date(GPS_INVALID_DATE) 00034 , _latitude(GPS_INVALID_ANGLE) 00035 , _longitude(GPS_INVALID_ANGLE) 00036 , _altitude(GPS_INVALID_ALTITUDE) 00037 , _speed(GPS_INVALID_SPEED) 00038 , _course(GPS_INVALID_ANGLE) 00039 , _hdop(GPS_INVALID_HDOP) 00040 , _numsats(GPS_INVALID_SATELLITES) 00041 , _last_time_fix(GPS_INVALID_FIX_TIME) 00042 , _last_position_fix(GPS_INVALID_FIX_TIME) 00043 , _parity(0) 00044 , _is_checksum_term(false) 00045 , _sentence_type(_GPS_SENTENCE_OTHER) 00046 , _term_number(0) 00047 , _term_offset(0) 00048 , _gps_data_good(false) 00049 #ifndef _GPS_NO_STATS 00050 , _encoded_characters(0) 00051 , _good_sentences(0) 00052 , _failed_checksum(0) 00053 #endif 00054 { 00055 _term[0] = '\0'; 00056 } 00057 00058 // 00059 // public methods 00060 // 00061 00062 bool TinyGPS::encode(char c) 00063 { 00064 bool valid_sentence = false; 00065 00066 #ifndef _GPS_NO_STATS 00067 ++_encoded_characters; 00068 #endif 00069 switch(c) 00070 { 00071 case ',': // term terminators 00072 _parity ^= c; 00073 case '\r': 00074 case '\n': 00075 case '*': 00076 if (_term_offset < sizeof(_term)) 00077 { 00078 _term[_term_offset] = 0; 00079 valid_sentence = term_complete(); 00080 } 00081 ++_term_number; 00082 _term_offset = 0; 00083 _is_checksum_term = c == '*'; 00084 return valid_sentence; 00085 00086 case '$': // sentence begin 00087 _term_number = _term_offset = 0; 00088 _parity = 0; 00089 _sentence_type = _GPS_SENTENCE_OTHER; 00090 _is_checksum_term = false; 00091 _gps_data_good = false; 00092 return valid_sentence; 00093 } 00094 00095 // ordinary characters 00096 if (_term_offset < sizeof(_term) - 1) 00097 _term[_term_offset++] = c; 00098 if (!_is_checksum_term) 00099 _parity ^= c; 00100 00101 return valid_sentence; 00102 } 00103 00104 #ifndef _GPS_NO_STATS 00105 void TinyGPS::stats(unsigned long *chars, unsigned short *sentences, unsigned short *failed_cs) 00106 { 00107 if (chars) *chars = _encoded_characters; 00108 if (sentences) *sentences = _good_sentences; 00109 if (failed_cs) *failed_cs = _failed_checksum; 00110 } 00111 #endif 00112 00113 // 00114 // internal utilities 00115 // 00116 int TinyGPS::from_hex(char a) 00117 { 00118 if (a >= 'A' && a <= 'F') 00119 return a - 'A' + 10; 00120 else if (a >= 'a' && a <= 'f') 00121 return a - 'a' + 10; 00122 else 00123 return a - '0'; 00124 } 00125 00126 unsigned long TinyGPS::parse_decimal() 00127 { 00128 char *p = _term; 00129 bool isneg = *p == '-'; 00130 if (isneg) ++p; 00131 unsigned long ret = 100UL * gpsatol(p); 00132 while (gpsisdigit(*p)) ++p; 00133 if (*p == '.') 00134 { 00135 if (gpsisdigit(p[1])) 00136 { 00137 ret += 10 * (p[1] - '0'); 00138 if (gpsisdigit(p[2])) 00139 ret += p[2] - '0'; 00140 } 00141 } 00142 return isneg ? -ret : ret; 00143 } 00144 00145 // Parse a string in the form ddmm.mmmmmmm... 00146 unsigned long TinyGPS::parse_degrees() 00147 { 00148 char *p; 00149 unsigned long left_of_decimal = gpsatol(_term); 00150 unsigned long hundred1000ths_of_minute = (left_of_decimal % 100UL) * 100000UL; 00151 for (p=_term; gpsisdigit(*p); ++p); 00152 if (*p == '.') 00153 { 00154 unsigned long mult = 10000; 00155 while (gpsisdigit(*++p)) 00156 { 00157 hundred1000ths_of_minute += mult * (*p - '0'); 00158 mult /= 10; 00159 } 00160 } 00161 return (left_of_decimal / 100) * 1000000 + (hundred1000ths_of_minute + 3) / 6; 00162 } 00163 00164 #define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number) 00165 00166 // Processes a just-completed term 00167 // Returns true if new sentence has just passed checksum test and is validated 00168 bool TinyGPS::term_complete() 00169 { 00170 if (_is_checksum_term) 00171 { 00172 byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]); 00173 if (checksum == _parity) 00174 { 00175 if (_gps_data_good) 00176 { 00177 #ifndef _GPS_NO_STATS 00178 ++_good_sentences; 00179 #endif 00180 _last_time_fix = _new_time_fix; 00181 _last_position_fix = _new_position_fix; 00182 00183 switch(_sentence_type) 00184 { 00185 case _GPS_SENTENCE_GPRMC: 00186 _time = _new_time; 00187 _date = _new_date; 00188 _latitude = _new_latitude; 00189 _longitude = _new_longitude; 00190 _speed = _new_speed; 00191 _course = _new_course; 00192 break; 00193 case _GPS_SENTENCE_GPGGA: 00194 _altitude = _new_altitude; 00195 _time = _new_time; 00196 _latitude = _new_latitude; 00197 _longitude = _new_longitude; 00198 _numsats = _new_numsats; 00199 _hdop = _new_hdop; 00200 break; 00201 } 00202 00203 return true; 00204 } 00205 } 00206 00207 #ifndef _GPS_NO_STATS 00208 else 00209 ++_failed_checksum; 00210 #endif 00211 return false; 00212 } 00213 00214 // the first term determines the sentence type 00215 if (_term_number == 0) 00216 { 00217 if (!gpsstrcmp(_term, _GPRMC_TERM)) 00218 _sentence_type = _GPS_SENTENCE_GPRMC; 00219 else if (!gpsstrcmp(_term, _GPGGA_TERM)) 00220 _sentence_type = _GPS_SENTENCE_GPGGA; 00221 else 00222 _sentence_type = _GPS_SENTENCE_OTHER; 00223 return false; 00224 } 00225 00226 if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0]) 00227 switch(COMBINE(_sentence_type, _term_number)) 00228 { 00229 case COMBINE(_GPS_SENTENCE_GPRMC, 1): // Time in both sentences 00230 case COMBINE(_GPS_SENTENCE_GPGGA, 1): 00231 _new_time = parse_decimal(); 00232 _new_time_fix = millis(); 00233 break; 00234 case COMBINE(_GPS_SENTENCE_GPRMC, 2): // GPRMC validity 00235 _gps_data_good = _term[0] == 'A'; 00236 break; 00237 case COMBINE(_GPS_SENTENCE_GPRMC, 3): // Latitude 00238 case COMBINE(_GPS_SENTENCE_GPGGA, 2): 00239 _new_latitude = parse_degrees(); 00240 _new_position_fix = millis(); 00241 break; 00242 case COMBINE(_GPS_SENTENCE_GPRMC, 4): // N/S 00243 case COMBINE(_GPS_SENTENCE_GPGGA, 3): 00244 if (_term[0] == 'S') 00245 _new_latitude = -_new_latitude; 00246 break; 00247 case COMBINE(_GPS_SENTENCE_GPRMC, 5): // Longitude 00248 case COMBINE(_GPS_SENTENCE_GPGGA, 4): 00249 _new_longitude = parse_degrees(); 00250 break; 00251 case COMBINE(_GPS_SENTENCE_GPRMC, 6): // E/W 00252 case COMBINE(_GPS_SENTENCE_GPGGA, 5): 00253 if (_term[0] == 'W') 00254 _new_longitude = -_new_longitude; 00255 break; 00256 case COMBINE(_GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC) 00257 _new_speed = parse_decimal(); 00258 break; 00259 case COMBINE(_GPS_SENTENCE_GPRMC, 8): // Course (GPRMC) 00260 _new_course = parse_decimal(); 00261 break; 00262 case COMBINE(_GPS_SENTENCE_GPRMC, 9): // Date (GPRMC) 00263 _new_date = gpsatol(_term); 00264 break; 00265 case COMBINE(_GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA) 00266 _gps_data_good = _term[0] > '0'; 00267 break; 00268 case COMBINE(_GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA) 00269 _new_numsats = (unsigned char)atoi(_term); 00270 break; 00271 case COMBINE(_GPS_SENTENCE_GPGGA, 8): // HDOP 00272 _new_hdop = parse_decimal(); 00273 break; 00274 case COMBINE(_GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA) 00275 _new_altitude = parse_decimal(); 00276 break; 00277 } 00278 00279 return false; 00280 } 00281 00282 long TinyGPS::gpsatol(const char *str) 00283 { 00284 long ret = 0; 00285 while (gpsisdigit(*str)) 00286 ret = 10 * ret + *str++ - '0'; 00287 return ret; 00288 } 00289 00290 int TinyGPS::gpsstrcmp(const char *str1, const char *str2) 00291 { 00292 while (*str1 && *str1 == *str2) 00293 ++str1, ++str2; 00294 return *str1; 00295 } 00296 00297 /* static */ 00298 float TinyGPS::distance_between (float lat1, float long1, float lat2, float long2) 00299 { 00300 // returns distance in meters between two positions, both specified 00301 // as signed decimal-degrees latitude and longitude. Uses great-circle 00302 // distance computation for hypothetical sphere of radius 6372795 meters. 00303 // Because Earth is no exact sphere, rounding errors may be up to 0.5%. 00304 // Courtesy of Maarten Lamers 00305 float delta = toRadians(long1-long2); 00306 float sdlong = sin(delta); 00307 float cdlong = cos(delta); 00308 lat1 = toRadians(lat1); 00309 lat2 = toRadians(lat2); 00310 float slat1 = sin(lat1); 00311 float clat1 = cos(lat1); 00312 float slat2 = sin(lat2); 00313 float clat2 = cos(lat2); 00314 delta = (clat1 * slat2) - (slat1 * clat2 * cdlong); 00315 delta = delta * delta;//delta = sq(delta); 00316 delta += (clat2 * sdlong) * (clat2 * sdlong);//delta += sq(clat2 * sdlong); 00317 delta = sqrt(delta); 00318 float denom = (slat1 * slat2) + (clat1 * clat2 * cdlong); 00319 delta = atan2(delta, denom); 00320 return delta * 6372795; 00321 } 00322 00323 float TinyGPS::course_to (float lat1, float long1, float lat2, float long2) 00324 { 00325 // returns course in degrees (North=0, West=270) from position 1 to position 2, 00326 // both specified as signed decimal-degrees latitude and longitude. 00327 // Because Earth is no exact sphere, calculated course may be off by a tiny fraction. 00328 // Courtesy of Maarten Lamers 00329 float dlon = toRadians(long2-long1); 00330 lat1 = toRadians(lat1); 00331 lat2 = toRadians(lat2); 00332 float a1 = sin(dlon) * cos(lat2); 00333 float a2 = sin(lat1) * cos(lat2) * cos(dlon); 00334 a2 = cos(lat1) * sin(lat2) - a2; 00335 a2 = atan2(a1, a2); 00336 if (a2 < 0.0) 00337 { 00338 a2 += TWO_PI; 00339 } 00340 return toDegrees(a2); 00341 } 00342 00343 const char *TinyGPS::cardinal (float course) 00344 { 00345 static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; 00346 00347 int direction = (int)((course + 11.25f) / 22.5f); 00348 return directions[direction % 16]; 00349 } 00350 00351 // lat/long in MILLIONTHs of a degree and age of fix in milliseconds 00352 // (note: versions 12 and earlier gave this value in 100,000ths of a degree. 00353 void TinyGPS::get_position(long *latitude, long *longitude, unsigned long *fix_age) 00354 { 00355 if (latitude) *latitude = _latitude; 00356 if (longitude) *longitude = _longitude; 00357 if (fix_age) *fix_age = _last_position_fix == GPS_INVALID_FIX_TIME ? 00358 GPS_INVALID_AGE : millis() - _last_position_fix; 00359 } 00360 00361 // date as ddmmyy, time as hhmmsscc, and age in milliseconds 00362 void TinyGPS::get_datetime(unsigned long *date, unsigned long *time, unsigned long *age) 00363 { 00364 if (date) *date = _date; 00365 if (time) *time = _time; 00366 if (age) *age = _last_time_fix == GPS_INVALID_FIX_TIME ? 00367 GPS_INVALID_AGE : millis() - _last_time_fix; 00368 } 00369 00370 void TinyGPS::f_get_position(float *latitude, float *longitude, unsigned long *fix_age) 00371 { 00372 long lat, lon; 00373 get_position(&lat, &lon, fix_age); 00374 *latitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lat / 1000000.0); 00375 *longitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lon / 1000000.0); 00376 } 00377 00378 void TinyGPS::crack_datetime(int *year, byte *month, byte *day, 00379 byte *hour, byte *minute, byte *second, byte *hundredths, unsigned long *age) 00380 { 00381 unsigned long date, time; 00382 get_datetime(&date, &time, age); 00383 if (year) 00384 { 00385 *year = date % 100; 00386 *year += *year > 80 ? 1900 : 2000; 00387 } 00388 if (month) *month = (date / 100) % 100; 00389 if (day) *day = date / 10000; 00390 if (hour) *hour = time / 1000000; 00391 if (minute) *minute = (time / 10000) % 100; 00392 if (second) *second = (time / 100) % 100; 00393 if (hundredths) *hundredths = time % 100; 00394 } 00395 00396 float TinyGPS::f_altitude() 00397 { 00398 return _altitude == GPS_INVALID_ALTITUDE ? GPS_INVALID_F_ALTITUDE : _altitude / 100.0; 00399 } 00400 00401 float TinyGPS::f_course() 00402 { 00403 return _course == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : _course / 100.0; 00404 } 00405 00406 float TinyGPS::f_speed_knots() 00407 { 00408 return _speed == GPS_INVALID_SPEED ? GPS_INVALID_F_SPEED : _speed / 100.0; 00409 } 00410 00411 float TinyGPS::f_speed_mph() 00412 { 00413 float sk = f_speed_knots(); 00414 return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPH_PER_KNOT * sk; 00415 } 00416 00417 float TinyGPS::f_speed_mps() 00418 { 00419 float sk = f_speed_knots(); 00420 return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPS_PER_KNOT * sk; 00421 } 00422 00423 float TinyGPS::f_speed_kmph() 00424 { 00425 float sk = f_speed_knots(); 00426 return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_KMPH_PER_KNOT * sk; 00427 } 00428 00429 const float TinyGPS::GPS_INVALID_F_ANGLE = 1000.0; 00430 const float TinyGPS::GPS_INVALID_F_ALTITUDE = 1000000.0; 00431 const float TinyGPS::GPS_INVALID_F_SPEED = -1.0;
Generated on Thu Jul 21 2022 10:30:43 by 1.7.2