TinyGPS.cpp 12 KB

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