00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "TinyGPS++.h"
00025
00026 #include <string.h>
00027 #include <ctype.h>
00028 #include <stdlib.h>
00029
00030 #define _GPRMCterm "GPRMC"
00031 #define _GPGGAterm "GPGGA"
00032
00033 TinyGPSPlus::TinyGPSPlus()
00034 : parity(0)
00035 , isChecksumTerm(false)
00036 , curSentenceType(GPS_SENTENCE_OTHER)
00037 , curTermNumber(0)
00038 , curTermOffset(0)
00039 , sentenceHasFix(false)
00040 , customElts(0)
00041 , customCandidates(0)
00042 , encodedCharCount(0)
00043 , sentencesWithFixCount(0)
00044 , failedChecksumCount(0)
00045 , passedChecksumCount(0)
00046 {
00047 term[0] = '\0';
00048 }
00049
00050
00051
00052
00053
00054 bool TinyGPSPlus::encode(char c)
00055 {
00056 ++encodedCharCount;
00057
00058 switch(c)
00059 {
00060 case ',':
00061 parity ^= (uint8_t)c;
00062 case '\r':
00063 case '\n':
00064 case '*':
00065 {
00066 bool isValidSentence = false;
00067 if (curTermOffset < sizeof(term))
00068 {
00069 term[curTermOffset] = 0;
00070 isValidSentence = endOfTermHandler();
00071 }
00072 ++curTermNumber;
00073 curTermOffset = 0;
00074 isChecksumTerm = c == '*';
00075 return isValidSentence;
00076 }
00077 break;
00078
00079 case '$':
00080 curTermNumber = curTermOffset = 0;
00081 parity = 0;
00082 curSentenceType = GPS_SENTENCE_OTHER;
00083 isChecksumTerm = false;
00084 sentenceHasFix = false;
00085 return false;
00086
00087 default:
00088 if (curTermOffset < sizeof(term) - 1)
00089 term[curTermOffset++] = c;
00090 if (!isChecksumTerm)
00091 parity ^= c;
00092 return false;
00093 }
00094
00095 return false;
00096 }
00097
00098
00099
00100
00101 int TinyGPSPlus::fromHex(char a)
00102 {
00103 if (a >= 'A' && a <= 'F')
00104 return a - 'A' + 10;
00105 else if (a >= 'a' && a <= 'f')
00106 return a - 'a' + 10;
00107 else
00108 return a - '0';
00109 }
00110
00111
00112
00113 int32_t TinyGPSPlus::parseDecimal(const char *term)
00114 {
00115 bool negative = *term == '-';
00116 if (negative) ++term;
00117 int32_t ret = 100 * (int32_t)atol(term);
00118 while (isdigit(*term)) ++term;
00119 if (*term == '.' && isdigit(term[1]))
00120 {
00121 ret += 10 * (term[1] - '0');
00122 if (isdigit(term[2]))
00123 ret += term[2] - '0';
00124 }
00125 return negative ? -ret : ret;
00126 }
00127
00128
00129
00130 void TinyGPSPlus::parseDegrees(const char *term, RawDegrees °)
00131 {
00132 uint32_t leftOfDecimal = (uint32_t)atol(term);
00133 uint16_t minutes = (uint16_t)(leftOfDecimal % 100);
00134 uint32_t multiplier = 10000000UL;
00135 uint32_t tenMillionthsOfMinutes = minutes * multiplier;
00136
00137 deg.deg = (int16_t)(leftOfDecimal / 100);
00138
00139 while (isdigit(*term))
00140 ++term;
00141
00142 if (*term == '.')
00143 while (isdigit(*++term))
00144 {
00145 multiplier /= 10;
00146 tenMillionthsOfMinutes += (*term - '0') * multiplier;
00147 }
00148
00149 deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3;
00150 deg.negative = false;
00151 }
00152
00153 #define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number)
00154
00155
00156
00157 bool TinyGPSPlus::endOfTermHandler()
00158 {
00159
00160 if (isChecksumTerm)
00161 {
00162 byte checksum = 16 * fromHex(term[0]) + fromHex(term[1]);
00163 if (checksum == parity)
00164 {
00165 passedChecksumCount++;
00166 if (sentenceHasFix)
00167 ++sentencesWithFixCount;
00168
00169 switch(curSentenceType)
00170 {
00171 case GPS_SENTENCE_GPRMC:
00172 date.commit();
00173 time.commit();
00174 if (sentenceHasFix)
00175 {
00176 location.commit();
00177 speed.commit();
00178 course.commit();
00179 }
00180 break;
00181 case GPS_SENTENCE_GPGGA:
00182 time.commit();
00183 if (sentenceHasFix)
00184 {
00185 location.commit();
00186 altitude.commit();
00187 }
00188 satellites.commit();
00189 hdop.commit();
00190 break;
00191 }
00192
00193
00194 for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0; p = p->next)
00195 p->commit();
00196 return true;
00197 }
00198
00199 else
00200 {
00201 ++failedChecksumCount;
00202 }
00203
00204 return false;
00205 }
00206
00207
00208 if (curTermNumber == 0)
00209 {
00210 if (!strcmp(term, _GPRMCterm))
00211 curSentenceType = GPS_SENTENCE_GPRMC;
00212 else if (!strcmp(term, _GPGGAterm))
00213 curSentenceType = GPS_SENTENCE_GPGGA;
00214 else
00215 curSentenceType = GPS_SENTENCE_OTHER;
00216
00217
00218 for (customCandidates = customElts; customCandidates != NULL && strcmp(customCandidates->sentenceName, term) < 0; customCandidates = customCandidates->next);
00219 if (customCandidates != NULL && strcmp(customCandidates->sentenceName, term) > 0)
00220 customCandidates = NULL;
00221
00222 return false;
00223 }
00224
00225 if (curSentenceType != GPS_SENTENCE_OTHER && term[0])
00226 switch(COMBINE(curSentenceType, curTermNumber))
00227 {
00228 case COMBINE(GPS_SENTENCE_GPRMC, 1):
00229 case COMBINE(GPS_SENTENCE_GPGGA, 1):
00230 time.setTime(term);
00231 break;
00232 case COMBINE(GPS_SENTENCE_GPRMC, 2):
00233 sentenceHasFix = term[0] == 'A';
00234 break;
00235 case COMBINE(GPS_SENTENCE_GPRMC, 3):
00236 case COMBINE(GPS_SENTENCE_GPGGA, 2):
00237 location.setLatitude(term);
00238 break;
00239 case COMBINE(GPS_SENTENCE_GPRMC, 4):
00240 case COMBINE(GPS_SENTENCE_GPGGA, 3):
00241 location.rawNewLatData.negative = term[0] == 'S';
00242 break;
00243 case COMBINE(GPS_SENTENCE_GPRMC, 5):
00244 case COMBINE(GPS_SENTENCE_GPGGA, 4):
00245 location.setLongitude(term);
00246 break;
00247 case COMBINE(GPS_SENTENCE_GPRMC, 6):
00248 case COMBINE(GPS_SENTENCE_GPGGA, 5):
00249 location.rawNewLngData.negative = term[0] == 'W';
00250 break;
00251 case COMBINE(GPS_SENTENCE_GPRMC, 7):
00252 speed.set(term);
00253 break;
00254 case COMBINE(GPS_SENTENCE_GPRMC, 8):
00255 course.set(term);
00256 break;
00257 case COMBINE(GPS_SENTENCE_GPRMC, 9):
00258 date.setDate(term);
00259 break;
00260 case COMBINE(GPS_SENTENCE_GPGGA, 6):
00261 sentenceHasFix = term[0] > '0';
00262 break;
00263 case COMBINE(GPS_SENTENCE_GPGGA, 7):
00264 satellites.set(term);
00265 break;
00266 case COMBINE(GPS_SENTENCE_GPGGA, 8):
00267 hdop.set(term);
00268 break;
00269 case COMBINE(GPS_SENTENCE_GPGGA, 9):
00270 altitude.set(term);
00271 break;
00272 }
00273
00274
00275 for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; p = p->next)
00276 if (p->termNumber == curTermNumber)
00277 p->set(term);
00278
00279 return false;
00280 }
00281
00282
00283 double TinyGPSPlus::distanceBetween(double lat1, double long1, double lat2, double long2)
00284 {
00285
00286
00287
00288
00289
00290 double delta = radians(long1-long2);
00291 double sdlong = sin(delta);
00292 double cdlong = cos(delta);
00293 lat1 = radians(lat1);
00294 lat2 = radians(lat2);
00295 double slat1 = sin(lat1);
00296 double clat1 = cos(lat1);
00297 double slat2 = sin(lat2);
00298 double clat2 = cos(lat2);
00299 delta = (clat1 * slat2) - (slat1 * clat2 * cdlong);
00300 delta = sq(delta);
00301 delta += sq(clat2 * sdlong);
00302 delta = sqrt(delta);
00303 double denom = (slat1 * slat2) + (clat1 * clat2 * cdlong);
00304 delta = atan2(delta, denom);
00305 return delta * 6372795;
00306 }
00307
00308 double TinyGPSPlus::courseTo(double lat1, double long1, double lat2, double long2)
00309 {
00310
00311
00312
00313
00314 double dlon = radians(long2-long1);
00315 lat1 = radians(lat1);
00316 lat2 = radians(lat2);
00317 double a1 = sin(dlon) * cos(lat2);
00318 double a2 = sin(lat1) * cos(lat2) * cos(dlon);
00319 a2 = cos(lat1) * sin(lat2) - a2;
00320 a2 = atan2(a1, a2);
00321 if (a2 < 0.0)
00322 {
00323 a2 += TWO_PI;
00324 }
00325 return degrees(a2);
00326 }
00327
00328 const char *TinyGPSPlus::cardinal(double course)
00329 {
00330 static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
00331 int direction = (int)((course + 11.25f) / 22.5f);
00332 return directions[direction % 16];
00333 }
00334
00335 void TinyGPSLocation::commit()
00336 {
00337 rawLatData = rawNewLatData;
00338 rawLngData = rawNewLngData;
00339 lastCommitTime = millis();
00340 valid = updated = true;
00341 }
00342
00343 void TinyGPSLocation::setLatitude(const char *term)
00344 {
00345 TinyGPSPlus::parseDegrees(term, rawNewLatData);
00346 }
00347
00348 void TinyGPSLocation::setLongitude(const char *term)
00349 {
00350 TinyGPSPlus::parseDegrees(term, rawNewLngData);
00351 }
00352
00353 double TinyGPSLocation::lat()
00354 {
00355 updated = false;
00356 double ret = rawLatData.deg + rawLatData.billionths / 1000000000.0;
00357 return rawLatData.negative ? -ret : ret;
00358 }
00359
00360 double TinyGPSLocation::lng()
00361 {
00362 updated = false;
00363 double ret = rawLngData.deg + rawLngData.billionths / 1000000000.0;
00364 return rawLngData.negative ? -ret : ret;
00365 }
00366
00367 void TinyGPSDate::commit()
00368 {
00369 date = newDate;
00370 lastCommitTime = millis();
00371 valid = updated = true;
00372 }
00373
00374 void TinyGPSTime::commit()
00375 {
00376 time = newTime;
00377 lastCommitTime = millis();
00378 valid = updated = true;
00379 }
00380
00381 void TinyGPSTime::setTime(const char *term)
00382 {
00383 newTime = (uint32_t)TinyGPSPlus::parseDecimal(term);
00384 }
00385
00386 void TinyGPSDate::setDate(const char *term)
00387 {
00388 newDate = atol(term);
00389 }
00390
00391 uint16_t TinyGPSDate::year()
00392 {
00393 updated = false;
00394 uint16_t year = date % 100;
00395 return year + 2000;
00396 }
00397
00398 uint8_t TinyGPSDate::month()
00399 {
00400 updated = false;
00401 return (date / 100) % 100;
00402 }
00403
00404 uint8_t TinyGPSDate::day()
00405 {
00406 updated = false;
00407 return date / 10000;
00408 }
00409
00410 uint8_t TinyGPSTime::hour()
00411 {
00412 updated = false;
00413 return time / 1000000;
00414 }
00415
00416 uint8_t TinyGPSTime::minute()
00417 {
00418 updated = false;
00419 return (time / 10000) % 100;
00420 }
00421
00422 uint8_t TinyGPSTime::second()
00423 {
00424 updated = false;
00425 return (time / 100) % 100;
00426 }
00427
00428 uint8_t TinyGPSTime::centisecond()
00429 {
00430 updated = false;
00431 return time % 100;
00432 }
00433
00434 void TinyGPSDecimal::commit()
00435 {
00436 val = newval;
00437 lastCommitTime = millis();
00438 valid = updated = true;
00439 }
00440
00441 void TinyGPSDecimal::set(const char *term)
00442 {
00443 newval = TinyGPSPlus::parseDecimal(term);
00444 }
00445
00446 void TinyGPSInteger::commit()
00447 {
00448 val = newval;
00449 lastCommitTime = millis();
00450 valid = updated = true;
00451 }
00452
00453 void TinyGPSInteger::set(const char *term)
00454 {
00455 newval = atol(term);
00456 }
00457
00458 TinyGPSCustom::TinyGPSCustom(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber)
00459 {
00460 begin(gps, _sentenceName, _termNumber);
00461 }
00462
00463 void TinyGPSCustom::begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber)
00464 {
00465 lastCommitTime = 0;
00466 updated = valid = false;
00467 sentenceName = _sentenceName;
00468 termNumber = _termNumber;
00469 memset(stagingBuffer, '\0', sizeof(stagingBuffer));
00470 memset(buffer, '\0', sizeof(buffer));
00471
00472
00473 gps.insertCustom(this, _sentenceName, _termNumber);
00474 }
00475
00476 void TinyGPSCustom::commit()
00477 {
00478 strcpy(this->buffer, this->stagingBuffer);
00479 lastCommitTime = millis();
00480 valid = updated = true;
00481 }
00482
00483 void TinyGPSCustom::set(const char *term)
00484 {
00485 strncpy(this->stagingBuffer, term, sizeof(this->stagingBuffer));
00486 }
00487
00488 void TinyGPSPlus::insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int termNumber)
00489 {
00490 TinyGPSCustom **ppelt;
00491
00492 for (ppelt = &this->customElts; *ppelt != NULL; ppelt = &(*ppelt)->next)
00493 {
00494 int cmp = strcmp(sentenceName, (*ppelt)->sentenceName);
00495 if (cmp < 0 || (cmp == 0 && termNumber < (*ppelt)->termNumber))
00496 break;
00497 }
00498
00499 pElt->next = *ppelt;
00500 *ppelt = pElt;
00501 }