TinyGPS.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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. const float GPS_INVALID_F_ANGLE = 1000.0;
  24. const float GPS_INVALID_F_ALTITUDE = 1000000.0;
  25. const float GPS_INVALID_F_SPEED = -1.0;
  26. // properties
  27. unsigned long _time, _new_time;
  28. unsigned long _date, _new_date;
  29. long _latitude, _new_latitude;
  30. long _longitude, _new_longitude;
  31. long _altitude, _new_altitude;
  32. unsigned long _speed, _new_speed;
  33. unsigned long _course, _new_course;
  34. unsigned long _hdop, _new_hdop;
  35. unsigned short _numsats, _new_numsats;
  36. unsigned long _last_time_fix, _new_time_fix;
  37. unsigned long _last_position_fix, _new_position_fix;
  38. // parsing state variables
  39. uint8_t _parity;
  40. bool _is_checksum_term;
  41. char _term[15];
  42. uint8_t _sentence_type;
  43. uint8_t _term_number;
  44. uint8_t _term_offset;
  45. bool _gps_data_good;
  46. #ifndef _GPS_NO_STATS
  47. // statistics
  48. unsigned long _encoded_characters;
  49. unsigned short _good_sentences;
  50. unsigned short _failed_checksum;
  51. unsigned short _passed_checksum;
  52. #endif
  53. void setupTinyGps() {
  54. _term[0] = '\0';
  55. }
  56. int from_hex(char a);
  57. unsigned long parse_decimal();
  58. unsigned long parse_degrees();
  59. bool term_complete();
  60. bool gpsisdigit(char c) { return c >= '0' && c <= '9'; }
  61. long gpsatol(const char *str);
  62. int gpsstrcmp(const char *str1, const char *str2);
  63. // signed altitude in centimeters (from GPGGA sentence)
  64. long altitude() { return _altitude; }
  65. // course in last full GPRMC sentence in 100th of a degree
  66. unsigned long course() { return _course; }
  67. // speed in last full GPRMC sentence in 100ths of a knot
  68. unsigned long speed() { return _speed; }
  69. // satellites used in last full GPGGA sentence
  70. unsigned short satellites() { return _numsats; }
  71. // horizontal dilution of precision in 100ths
  72. unsigned long hdop() { return _hdop; }
  73. //
  74. // public methods
  75. //
  76. bool encode(char c)
  77. {
  78. bool valid_sentence = false;
  79. #ifndef _GPS_NO_STATS
  80. ++_encoded_characters;
  81. #endif
  82. switch(c)
  83. {
  84. case ',': // term terminators
  85. _parity ^= c;
  86. case '\r':
  87. case '\n':
  88. case '*':
  89. if (_term_offset < sizeof(_term))
  90. {
  91. _term[_term_offset] = 0;
  92. valid_sentence = term_complete();
  93. }
  94. ++_term_number;
  95. _term_offset = 0;
  96. _is_checksum_term = c == '*';
  97. return valid_sentence;
  98. case '$': // sentence begin
  99. _term_number = _term_offset = 0;
  100. _parity = 0;
  101. _sentence_type = _GPS_SENTENCE_OTHER;
  102. _is_checksum_term = false;
  103. _gps_data_good = false;
  104. return valid_sentence;
  105. }
  106. // ordinary characters
  107. if (_term_offset < sizeof(_term) - 1)
  108. _term[_term_offset++] = c;
  109. if (!_is_checksum_term)
  110. _parity ^= c;
  111. return valid_sentence;
  112. }
  113. #ifndef _GPS_NO_STATS
  114. void stats(unsigned long *chars, unsigned short *sentences, unsigned short *failed_cs)
  115. {
  116. if (chars) *chars = _encoded_characters;
  117. if (sentences) *sentences = _good_sentences;
  118. if (failed_cs) *failed_cs = _failed_checksum;
  119. }
  120. #endif
  121. //
  122. // internal utilities
  123. //
  124. int from_hex(char a)
  125. {
  126. if (a >= 'A' && a <= 'F')
  127. return a - 'A' + 10;
  128. else if (a >= 'a' && a <= 'f')
  129. return a - 'a' + 10;
  130. else
  131. return a - '0';
  132. }
  133. unsigned long parse_decimal()
  134. {
  135. char *p = _term;
  136. bool isneg = *p == '-';
  137. if (isneg) ++p;
  138. unsigned long ret = 100UL * gpsatol(p);
  139. while (gpsisdigit(*p)) ++p;
  140. if (*p == '.')
  141. {
  142. if (gpsisdigit(p[1]))
  143. {
  144. ret += 10 * (p[1] - '0');
  145. if (gpsisdigit(p[2]))
  146. ret += p[2] - '0';
  147. }
  148. }
  149. return isneg ? -ret : ret;
  150. }
  151. // Parse a string in the form ddmm.mmmmmmm...
  152. unsigned long parse_degrees()
  153. {
  154. char *p;
  155. unsigned long left_of_decimal = gpsatol(_term);
  156. unsigned long hundred1000ths_of_minute = (left_of_decimal % 100UL) * 100000UL;
  157. for (p=_term; gpsisdigit(*p); ++p);
  158. if (*p == '.')
  159. {
  160. unsigned long mult = 10000;
  161. while (gpsisdigit(*++p))
  162. {
  163. hundred1000ths_of_minute += mult * (*p - '0');
  164. mult /= 10;
  165. }
  166. }
  167. return (left_of_decimal / 100) * 1000000 + (hundred1000ths_of_minute + 3) / 6;
  168. }
  169. #define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number)
  170. // Processes a just-completed term
  171. // Returns true if new sentence has just passed checksum test and is validated
  172. bool term_complete()
  173. {
  174. if (_is_checksum_term)
  175. {
  176. uint8_t checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]);
  177. if (checksum == _parity)
  178. {
  179. if (_gps_data_good)
  180. {
  181. #ifndef _GPS_NO_STATS
  182. ++_good_sentences;
  183. #endif
  184. _last_time_fix = _new_time_fix;
  185. _last_position_fix = _new_position_fix;
  186. switch(_sentence_type)
  187. {
  188. case _GPS_SENTENCE_GPRMC:
  189. _time = _new_time;
  190. _date = _new_date;
  191. _latitude = _new_latitude;
  192. _longitude = _new_longitude;
  193. _speed = _new_speed;
  194. _course = _new_course;
  195. break;
  196. case _GPS_SENTENCE_GPGGA:
  197. _altitude = _new_altitude;
  198. _time = _new_time;
  199. _latitude = _new_latitude;
  200. _longitude = _new_longitude;
  201. _numsats = _new_numsats;
  202. _hdop = _new_hdop;
  203. break;
  204. }
  205. return true;
  206. }
  207. }
  208. #ifndef _GPS_NO_STATS
  209. else
  210. ++_failed_checksum;
  211. #endif
  212. return false;
  213. }
  214. // the first term determines the sentence type
  215. if (_term_number == 0)
  216. {
  217. if (!gpsstrcmp(_term, _GPRMC_TERM))
  218. _sentence_type = _GPS_SENTENCE_GPRMC;
  219. else if (!gpsstrcmp(_term, _GPGGA_TERM))
  220. _sentence_type = _GPS_SENTENCE_GPGGA;
  221. else
  222. _sentence_type = _GPS_SENTENCE_OTHER;
  223. return false;
  224. }
  225. if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0])
  226. switch(COMBINE(_sentence_type, _term_number))
  227. {
  228. case COMBINE(_GPS_SENTENCE_GPRMC, 1): // Time in both sentences
  229. case COMBINE(_GPS_SENTENCE_GPGGA, 1):
  230. _new_time = parse_decimal();
  231. _new_time_fix = millis();
  232. break;
  233. case COMBINE(_GPS_SENTENCE_GPRMC, 2): // GPRMC validity
  234. _gps_data_good = _term[0] == 'A';
  235. break;
  236. case COMBINE(_GPS_SENTENCE_GPRMC, 3): // Latitude
  237. case COMBINE(_GPS_SENTENCE_GPGGA, 2):
  238. _new_latitude = parse_degrees();
  239. _new_position_fix = millis();
  240. break;
  241. case COMBINE(_GPS_SENTENCE_GPRMC, 4): // N/S
  242. case COMBINE(_GPS_SENTENCE_GPGGA, 3):
  243. if (_term[0] == 'S')
  244. _new_latitude = -_new_latitude;
  245. break;
  246. case COMBINE(_GPS_SENTENCE_GPRMC, 5): // Longitude
  247. case COMBINE(_GPS_SENTENCE_GPGGA, 4):
  248. _new_longitude = parse_degrees();
  249. break;
  250. case COMBINE(_GPS_SENTENCE_GPRMC, 6): // E/W
  251. case COMBINE(_GPS_SENTENCE_GPGGA, 5):
  252. if (_term[0] == 'W')
  253. _new_longitude = -_new_longitude;
  254. break;
  255. case COMBINE(_GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC)
  256. _new_speed = parse_decimal();
  257. break;
  258. case COMBINE(_GPS_SENTENCE_GPRMC, 8): // Course (GPRMC)
  259. _new_course = parse_decimal();
  260. break;
  261. case COMBINE(_GPS_SENTENCE_GPRMC, 9): // Date (GPRMC)
  262. _new_date = gpsatol(_term);
  263. break;
  264. case COMBINE(_GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA)
  265. _gps_data_good = _term[0] > '0';
  266. break;
  267. case COMBINE(_GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA)
  268. _new_numsats = (unsigned char)atoi(_term);
  269. break;
  270. case COMBINE(_GPS_SENTENCE_GPGGA, 8): // HDOP
  271. _new_hdop = parse_decimal();
  272. break;
  273. case COMBINE(_GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA)
  274. _new_altitude = parse_decimal();
  275. break;
  276. }
  277. return false;
  278. }
  279. long gpsatol(const char *str)
  280. {
  281. long ret = 0;
  282. while (gpsisdigit(*str))
  283. ret = 10 * ret + *str++ - '0';
  284. return ret;
  285. }
  286. int gpsstrcmp(const char *str1, const char *str2)
  287. {
  288. while (*str1 && *str1 == *str2)
  289. ++str1, ++str2;
  290. return *str1;
  291. }
  292. /* static */
  293. const char *cardinal (float course)
  294. {
  295. static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
  296. int direction = (int)((course + 11.25f) / 22.5f);
  297. return directions[direction % 16];
  298. }
  299. // lat/long in MILLIONTHs of a degree and age of fix in milliseconds
  300. // (note: versions 12 and earlier gave this value in 100,000ths of a degree.
  301. void get_position(long *latitude, long *longitude, unsigned long *fix_age)
  302. {
  303. if (latitude) *latitude = _latitude;
  304. if (longitude) *longitude = _longitude;
  305. if (fix_age) *fix_age = _last_position_fix == GPS_INVALID_FIX_TIME ?
  306. GPS_INVALID_AGE : millis() - _last_position_fix;
  307. }
  308. // date as ddmmyy, time as hhmmsscc, and age in milliseconds
  309. void get_datetime(unsigned long *date, unsigned long *time, unsigned long *age)
  310. {
  311. if (date) *date = _date;
  312. if (time) *time = _time;
  313. if (age) *age = _last_time_fix == GPS_INVALID_FIX_TIME ?
  314. GPS_INVALID_AGE : millis() - _last_time_fix;
  315. }
  316. void f_get_position(float *latitude, float *longitude, unsigned long *fix_age)
  317. {
  318. long lat, lon;
  319. get_position(&lat, &lon, fix_age);
  320. *latitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lat / 1000000.0);
  321. *longitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lon / 1000000.0);
  322. }
  323. void crack_datetime(int *year, uint8_t *month, uint8_t *day,
  324. uint8_t *hour, uint8_t *minute, uint8_t *second, uint8_t *hundredths, unsigned long *age)
  325. {
  326. unsigned long date, time;
  327. get_datetime(&date, &time, age);
  328. if (year)
  329. {
  330. *year = date % 100;
  331. *year += *year > 80 ? 1900 : 2000;
  332. }
  333. if (month) *month = (date / 100) % 100;
  334. if (day) *day = date / 10000;
  335. if (hour) *hour = time / 1000000;
  336. if (minute) *minute = (time / 10000) % 100;
  337. if (second) *second = (time / 100) % 100;
  338. if (hundredths) *hundredths = time % 100;
  339. }
  340. float f_altitude()
  341. {
  342. return _altitude == GPS_INVALID_ALTITUDE ? GPS_INVALID_F_ALTITUDE : _altitude / 100.0;
  343. }
  344. float f_course()
  345. {
  346. return _course == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : _course / 100.0;
  347. }
  348. float f_speed_knots()
  349. {
  350. return _speed == GPS_INVALID_SPEED ? GPS_INVALID_F_SPEED : _speed / 100.0;
  351. }
  352. float f_speed_mph()
  353. {
  354. float sk = f_speed_knots();
  355. return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPH_PER_KNOT * sk;
  356. }
  357. float f_speed_mps()
  358. {
  359. float sk = f_speed_knots();
  360. return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPS_PER_KNOT * sk;
  361. }
  362. float f_speed_kmph()
  363. {
  364. float sk = f_speed_knots();
  365. return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_KMPH_PER_KNOT * sk;
  366. }