PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

516 lines
15KB

  1. /*
  2. TinyGPS - a small GPS library for Arduino providing basic NMEA parsing
  3. Based on work by and "distance_to" and "course_to" courtesy of Maarten Lamers.
  4. Suggestion to add satellites(), course_to(), and cardinal(), by Matt Monson.
  5. Precision improvements suggested by Wayne Holder.
  6. Copyright (C) 2008-2013 Mikal Hart
  7. All rights reserved.
  8. This library is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU Lesser General Public
  10. License as published by the Free Software Foundation; either
  11. version 2.1 of the License, or (at your option) any later version.
  12. This library is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. Lesser General Public License for more details.
  16. You should have received a copy of the GNU Lesser General Public
  17. License along with this library; if not, write to the Free Software
  18. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  19. */
  20. #include "TinyGPS.h"
  21. #define _GPRMC_TERM "GPRMC"
  22. #define _GPGGA_TERM "GPGGA"
  23. #define _GPGSA_TERM "GPGSA"
  24. #define _GNRMC_TERM "GNRMC"
  25. #define _GNGNS_TERM "GNGNS"
  26. #define _GNGSA_TERM "GNGSA"
  27. #define _GPGSV_TERM "GPGSV"
  28. #define _GLGSV_TERM "GLGSV"
  29. TinyGPS::TinyGPS()
  30. : _time(GPS_INVALID_TIME)
  31. , _date(GPS_INVALID_DATE)
  32. , _latitude(GPS_INVALID_ANGLE)
  33. , _longitude(GPS_INVALID_ANGLE)
  34. , _altitude(GPS_INVALID_ALTITUDE)
  35. , _speed(GPS_INVALID_SPEED)
  36. , _course(GPS_INVALID_ANGLE)
  37. , _hdop(GPS_INVALID_HDOP)
  38. , _numsats(GPS_INVALID_SATELLITES)
  39. , _last_time_fix(GPS_INVALID_FIX_TIME)
  40. , _last_position_fix(GPS_INVALID_FIX_TIME)
  41. , _parity(0)
  42. , _is_checksum_term(false)
  43. , _sentence_type(_GPS_SENTENCE_OTHER)
  44. , _term_number(0)
  45. , _term_offset(0)
  46. , _gps_data_good(false)
  47. #ifndef _GPS_NO_STATS
  48. , _encoded_characters(0)
  49. , _good_sentences(0)
  50. , _failed_checksum(0)
  51. #endif
  52. {
  53. _term[0] = '\0';
  54. }
  55. //
  56. // public methods
  57. //
  58. bool TinyGPS::encode(char c)
  59. {
  60. bool valid_sentence = false;
  61. #ifndef _GPS_NO_STATS
  62. ++_encoded_characters;
  63. #endif
  64. switch(c)
  65. {
  66. case ',': // term terminators
  67. _parity ^= c;
  68. case '\r':
  69. case '\n':
  70. case '*':
  71. if (_term_offset < sizeof(_term))
  72. {
  73. _term[_term_offset] = 0;
  74. valid_sentence = term_complete();
  75. }
  76. ++_term_number;
  77. _term_offset = 0;
  78. _is_checksum_term = c == '*';
  79. return valid_sentence;
  80. case '$': // sentence begin
  81. _term_number = _term_offset = 0;
  82. _parity = 0;
  83. _sentence_type = _GPS_SENTENCE_OTHER;
  84. _is_checksum_term = false;
  85. _gps_data_good = false;
  86. return valid_sentence;
  87. }
  88. // ordinary characters
  89. if (_term_offset < sizeof(_term) - 1)
  90. _term[_term_offset++] = c;
  91. if (!_is_checksum_term)
  92. _parity ^= c;
  93. return valid_sentence;
  94. }
  95. #ifndef _GPS_NO_STATS
  96. void TinyGPS::stats(unsigned long *chars, unsigned short *sentences, unsigned short *failed_cs)
  97. {
  98. if (chars) *chars = _encoded_characters;
  99. if (sentences) *sentences = _good_sentences;
  100. if (failed_cs) *failed_cs = _failed_checksum;
  101. }
  102. #endif
  103. //
  104. // internal utilities
  105. //
  106. int TinyGPS::from_hex(char a)
  107. {
  108. if (a >= 'A' && a <= 'F')
  109. return a - 'A' + 10;
  110. else if (a >= 'a' && a <= 'f')
  111. return a - 'a' + 10;
  112. else
  113. return a - '0';
  114. }
  115. unsigned long TinyGPS::parse_decimal()
  116. {
  117. char *p = _term;
  118. bool isneg = *p == '-';
  119. if (isneg) ++p;
  120. unsigned long ret = 100UL * gpsatol(p);
  121. while (gpsisdigit(*p)) ++p;
  122. if (*p == '.')
  123. {
  124. if (gpsisdigit(p[1]))
  125. {
  126. ret += 10 * (p[1] - '0');
  127. if (gpsisdigit(p[2]))
  128. ret += p[2] - '0';
  129. }
  130. }
  131. return isneg ? -ret : ret;
  132. }
  133. // Parse a string in the form ddmm.mmmmmmm...
  134. unsigned long TinyGPS::parse_degrees()
  135. {
  136. char *p;
  137. unsigned long left_of_decimal = gpsatol(_term);
  138. unsigned long hundred1000ths_of_minute = (left_of_decimal % 100UL) * 100000UL;
  139. for (p=_term; gpsisdigit(*p); ++p);
  140. if (*p == '.')
  141. {
  142. unsigned long mult = 10000;
  143. while (gpsisdigit(*++p))
  144. {
  145. hundred1000ths_of_minute += mult * (*p - '0');
  146. mult /= 10;
  147. }
  148. }
  149. return (left_of_decimal / 100) * 1000000 + (hundred1000ths_of_minute + 3) / 6;
  150. }
  151. #define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number)
  152. // Processes a just-completed term
  153. // Returns true if new sentence has just passed checksum test and is validated
  154. bool TinyGPS::term_complete()
  155. {
  156. if (_is_checksum_term)
  157. {
  158. byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]);
  159. if (checksum == _parity)
  160. {
  161. if(_sentence_type == _GPS_SENTENCE_GPRMC) //set the time and date even if not tracking
  162. {
  163. _time = _new_time;
  164. _date = _new_date;
  165. }
  166. if (_gps_data_good)
  167. {
  168. #ifndef _GPS_NO_STATS
  169. ++_good_sentences;
  170. #endif
  171. _last_time_fix = _new_time_fix;
  172. _last_position_fix = _new_position_fix;
  173. switch(_sentence_type)
  174. {
  175. case _GPS_SENTENCE_GPRMC:
  176. _time = _new_time;
  177. _date = _new_date;
  178. _latitude = _new_latitude;
  179. _longitude = _new_longitude;
  180. _speed = _new_speed;
  181. _course = _new_course;
  182. break;
  183. case _GPS_SENTENCE_GPGGA:
  184. _altitude = _new_altitude;
  185. _time = _new_time;
  186. _latitude = _new_latitude;
  187. _longitude = _new_longitude;
  188. _numsats = _new_numsats;
  189. _hdop = _new_hdop;
  190. break;
  191. }
  192. return true;
  193. }
  194. }
  195. #ifndef _GPS_NO_STATS
  196. else
  197. ++_failed_checksum;
  198. #endif
  199. return false;
  200. }
  201. // the first term determines the sentence type
  202. if (_term_number == 0)
  203. {
  204. if (!gpsstrcmp(_term, _GPRMC_TERM) || !gpsstrcmp(_term, _GNRMC_TERM))
  205. _sentence_type = _GPS_SENTENCE_GPRMC;
  206. else if (!gpsstrcmp(_term, _GPGGA_TERM))
  207. _sentence_type = _GPS_SENTENCE_GPGGA;
  208. else if (!gpsstrcmp(_term, _GNGNS_TERM))
  209. _sentence_type = _GPS_SENTENCE_GNGNS;
  210. else if (!gpsstrcmp(_term, _GNGSA_TERM) || !gpsstrcmp(_term, _GPGSA_TERM))
  211. _sentence_type = _GPS_SENTENCE_GNGSA;
  212. else if (!gpsstrcmp(_term, _GPGSV_TERM))
  213. _sentence_type = _GPS_SENTENCE_GPGSV;
  214. else if (!gpsstrcmp(_term, _GLGSV_TERM))
  215. _sentence_type = _GPS_SENTENCE_GLGSV;
  216. else
  217. _sentence_type = _GPS_SENTENCE_OTHER;
  218. return false;
  219. }
  220. if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0])
  221. switch(COMBINE(_sentence_type, _term_number))
  222. {
  223. case COMBINE(_GPS_SENTENCE_GPRMC, 1): // Time in both sentences
  224. case COMBINE(_GPS_SENTENCE_GPGGA, 1):
  225. case COMBINE(_GPS_SENTENCE_GNGNS, 1):
  226. _new_time = parse_decimal();
  227. _new_time_fix = millis();
  228. break;
  229. case COMBINE(_GPS_SENTENCE_GPRMC, 2): // GPRMC validity
  230. _gps_data_good = _term[0] == 'A';
  231. break;
  232. case COMBINE(_GPS_SENTENCE_GPRMC, 3): // Latitude
  233. case COMBINE(_GPS_SENTENCE_GPGGA, 2):
  234. case COMBINE(_GPS_SENTENCE_GNGNS, 2):
  235. _new_latitude = parse_degrees();
  236. _new_position_fix = millis();
  237. break;
  238. case COMBINE(_GPS_SENTENCE_GPRMC, 4): // N/S
  239. case COMBINE(_GPS_SENTENCE_GPGGA, 3):
  240. case COMBINE(_GPS_SENTENCE_GNGNS, 3):
  241. if (_term[0] == 'S')
  242. _new_latitude = -_new_latitude;
  243. break;
  244. case COMBINE(_GPS_SENTENCE_GPRMC, 5): // Longitude
  245. case COMBINE(_GPS_SENTENCE_GPGGA, 4):
  246. case COMBINE(_GPS_SENTENCE_GNGNS, 4):
  247. _new_longitude = parse_degrees();
  248. break;
  249. case COMBINE(_GPS_SENTENCE_GPRMC, 6): // E/W
  250. case COMBINE(_GPS_SENTENCE_GPGGA, 5):
  251. case COMBINE(_GPS_SENTENCE_GNGNS, 5):
  252. if (_term[0] == 'W')
  253. _new_longitude = -_new_longitude;
  254. break;
  255. case COMBINE(_GPS_SENTENCE_GNGNS, 6):
  256. strncpy(_constellations, _term, 5);
  257. break;
  258. case COMBINE(_GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC)
  259. _new_speed = parse_decimal();
  260. break;
  261. case COMBINE(_GPS_SENTENCE_GPRMC, 8): // Course (GPRMC)
  262. _new_course = parse_decimal();
  263. break;
  264. case COMBINE(_GPS_SENTENCE_GPRMC, 9): // Date (GPRMC)
  265. _new_date = gpsatol(_term);
  266. break;
  267. case COMBINE(_GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA)
  268. _gps_data_good = _term[0] > '0';
  269. break;
  270. case COMBINE(_GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA): GPS only
  271. case COMBINE(_GPS_SENTENCE_GNGNS, 7): // GNGNS counts-in all constellations
  272. _new_numsats = (unsigned char)atoi(_term);
  273. break;
  274. case COMBINE(_GPS_SENTENCE_GPGGA, 8): // HDOP
  275. _new_hdop = parse_decimal();
  276. break;
  277. case COMBINE(_GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA)
  278. _new_altitude = parse_decimal();
  279. break;
  280. case COMBINE(_GPS_SENTENCE_GNGSA, 3): //satellites used in solution: 3 to 15
  281. //_sats_used[
  282. break;
  283. case COMBINE(_GPS_SENTENCE_GPGSV, 2): //beginning of sequence
  284. case COMBINE(_GPS_SENTENCE_GLGSV, 2): //beginning of sequence
  285. {
  286. uint8_t msgId = atoi(_term)-1; //start from 0
  287. if(msgId == 0) {
  288. //http://geostar-navigation.com/file/geos3/geos_nmea_protocol_v3_0_eng.pdf
  289. if(_sentence_type == _GPS_SENTENCE_GPGSV) {
  290. //reset GPS & WAAS trackedSatellites
  291. for(uint8_t x=0;x<12;x++)
  292. {
  293. tracked_sat_rec[x] = 0;
  294. }
  295. } else {
  296. //reset GLONASS trackedSatellites: range starts with 23
  297. for(uint8_t x=12;x<24;x++)
  298. {
  299. tracked_sat_rec[x] = 0;
  300. }
  301. }
  302. }
  303. _sat_index = msgId*4; //4 sattelites/line
  304. if(_sentence_type == _GPS_SENTENCE_GLGSV)
  305. {
  306. _sat_index = msgId*4 + 12; //Glonass offset by 12
  307. }
  308. break;
  309. }
  310. case COMBINE(_GPS_SENTENCE_GPGSV, 4): //satellite #
  311. case COMBINE(_GPS_SENTENCE_GPGSV, 8):
  312. case COMBINE(_GPS_SENTENCE_GPGSV, 12):
  313. case COMBINE(_GPS_SENTENCE_GPGSV, 16):
  314. case COMBINE(_GPS_SENTENCE_GLGSV, 4):
  315. case COMBINE(_GPS_SENTENCE_GLGSV, 8):
  316. case COMBINE(_GPS_SENTENCE_GLGSV, 12):
  317. case COMBINE(_GPS_SENTENCE_GLGSV, 16):
  318. _tracked_satellites_index = atoi(_term);
  319. break;
  320. case COMBINE(_GPS_SENTENCE_GPGSV, 7): //strength
  321. case COMBINE(_GPS_SENTENCE_GPGSV, 11):
  322. case COMBINE(_GPS_SENTENCE_GPGSV, 15):
  323. case COMBINE(_GPS_SENTENCE_GPGSV, 19):
  324. case COMBINE(_GPS_SENTENCE_GLGSV, 7): //strength
  325. case COMBINE(_GPS_SENTENCE_GLGSV, 11):
  326. case COMBINE(_GPS_SENTENCE_GLGSV, 15):
  327. case COMBINE(_GPS_SENTENCE_GLGSV, 19):
  328. uint8_t stren = (uint8_t)atoi(_term);
  329. if(stren == 0) //remove the record, 0dB strength
  330. {
  331. tracked_sat_rec[_sat_index + (_term_number-7)/4] = 0;
  332. }
  333. else
  334. {
  335. tracked_sat_rec[_sat_index + (_term_number-7)/4] = _tracked_satellites_index<<8 | stren<<1;
  336. }
  337. break;
  338. }
  339. return false;
  340. }
  341. long TinyGPS::gpsatol(const char *str)
  342. {
  343. long ret = 0;
  344. while (gpsisdigit(*str))
  345. ret = 10 * ret + *str++ - '0';
  346. return ret;
  347. }
  348. int TinyGPS::gpsstrcmp(const char *str1, const char *str2)
  349. {
  350. while (*str1 && *str1 == *str2)
  351. ++str1, ++str2;
  352. return *str1;
  353. }
  354. /* static */
  355. float TinyGPS::distance_between (float lat1, float long1, float lat2, float long2)
  356. {
  357. // returns distance in meters between two positions, both specified
  358. // as signed decimal-degrees latitude and longitude. Uses great-circle
  359. // distance computation for hypothetical sphere of radius 6372795 meters.
  360. // Because Earth is no exact sphere, rounding errors may be up to 0.5%.
  361. // Courtesy of Maarten Lamers
  362. float delta = radians(long1-long2);
  363. float sdlong = sin(delta);
  364. float cdlong = cos(delta);
  365. lat1 = radians(lat1);
  366. lat2 = radians(lat2);
  367. float slat1 = sin(lat1);
  368. float clat1 = cos(lat1);
  369. float slat2 = sin(lat2);
  370. float clat2 = cos(lat2);
  371. delta = (clat1 * slat2) - (slat1 * clat2 * cdlong);
  372. delta = sq(delta);
  373. delta += sq(clat2 * sdlong);
  374. delta = sqrt(delta);
  375. float denom = (slat1 * slat2) + (clat1 * clat2 * cdlong);
  376. delta = atan2(delta, denom);
  377. return delta * 6372795;
  378. }
  379. float TinyGPS::course_to (float lat1, float long1, float lat2, float long2)
  380. {
  381. // returns course in degrees (North=0, West=270) from position 1 to position 2,
  382. // both specified as signed decimal-degrees latitude and longitude.
  383. // Because Earth is no exact sphere, calculated course may be off by a tiny fraction.
  384. // Courtesy of Maarten Lamers
  385. float dlon = radians(long2-long1);
  386. lat1 = radians(lat1);
  387. lat2 = radians(lat2);
  388. float a1 = sin(dlon) * cos(lat2);
  389. float a2 = sin(lat1) * cos(lat2) * cos(dlon);
  390. a2 = cos(lat1) * sin(lat2) - a2;
  391. a2 = atan2(a1, a2);
  392. if (a2 < 0.0)
  393. {
  394. a2 += TWO_PI;
  395. }
  396. return degrees(a2);
  397. }
  398. const char *TinyGPS::cardinal (float course)
  399. {
  400. static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
  401. int direction = (int)((course + 11.25f) / 22.5f);
  402. return directions[direction % 16];
  403. }
  404. // lat/long in MILLIONTHs of a degree and age of fix in milliseconds
  405. // (note: versions 12 and earlier gave this value in 100,000ths of a degree.
  406. void TinyGPS::get_position(long *latitude, long *longitude, unsigned long *fix_age)
  407. {
  408. if (latitude) *latitude = _latitude;
  409. if (longitude) *longitude = _longitude;
  410. if (fix_age) *fix_age = _last_position_fix == GPS_INVALID_FIX_TIME ?
  411. GPS_INVALID_AGE : millis() - _last_position_fix;
  412. }
  413. // date as ddmmyy, time as hhmmsscc, and age in milliseconds
  414. void TinyGPS::get_datetime(unsigned long *date, unsigned long *time, unsigned long *age)
  415. {
  416. if (date) *date = _date;
  417. if (time) *time = _time;
  418. if (age) *age = _last_time_fix == GPS_INVALID_FIX_TIME ?
  419. GPS_INVALID_AGE : millis() - _last_time_fix;
  420. }
  421. void TinyGPS::f_get_position(float *latitude, float *longitude, unsigned long *fix_age)
  422. {
  423. long lat, lon;
  424. get_position(&lat, &lon, fix_age);
  425. *latitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lat / 1000000.0);
  426. *longitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lon / 1000000.0);
  427. }
  428. void TinyGPS::crack_datetime(int *year, byte *month, byte *day,
  429. byte *hour, byte *minute, byte *second, byte *hundredths, unsigned long *age)
  430. {
  431. unsigned long date, time;
  432. get_datetime(&date, &time, age);
  433. if (year)
  434. {
  435. *year = date % 100;
  436. *year += *year > 80 ? 1900 : 2000;
  437. }
  438. if (month) *month = (date / 100) % 100;
  439. if (day) *day = date / 10000;
  440. if (hour) *hour = time / 1000000;
  441. if (minute) *minute = (time / 10000) % 100;
  442. if (second) *second = (time / 100) % 100;
  443. if (hundredths) *hundredths = time % 100;
  444. }
  445. float TinyGPS::f_altitude()
  446. {
  447. return _altitude == GPS_INVALID_ALTITUDE ? GPS_INVALID_F_ALTITUDE : _altitude / 100.0;
  448. }
  449. float TinyGPS::f_course()
  450. {
  451. return _course == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : _course / 100.0;
  452. }
  453. float TinyGPS::f_speed_knots()
  454. {
  455. return _speed == GPS_INVALID_SPEED ? GPS_INVALID_F_SPEED : _speed / 100.0;
  456. }
  457. float TinyGPS::f_speed_mph()
  458. {
  459. float sk = f_speed_knots();
  460. return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPH_PER_KNOT * sk;
  461. }
  462. float TinyGPS::f_speed_mps()
  463. {
  464. float sk = f_speed_knots();
  465. return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPS_PER_KNOT * sk;
  466. }
  467. float TinyGPS::f_speed_kmph()
  468. {
  469. float sk = f_speed_knots();
  470. return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_KMPH_PER_KNOT * sk;
  471. }
  472. const float TinyGPS::GPS_INVALID_F_ANGLE = 1000.0;
  473. const float TinyGPS::GPS_INVALID_F_ALTITUDE = 1000000.0;
  474. const float TinyGPS::GPS_INVALID_F_SPEED = -1.0;