A port of Mikal Hart's TinyGPS++ version 1.0.2 to mbed.

Dependents:   TinyGPSPlus-example PROJ515_GPS PROJ515_USS_GPS example-ttn-workshop

Committer:
aoba
Date:
Sat Feb 02 06:47:02 2019 +0000
Revision:
0:6d3813637f20
ported version 1.0.2 into Mbed

Who changed what in which revision?

UserRevisionLine numberNew contents of line
aoba 0:6d3813637f20 1 /*
aoba 0:6d3813637f20 2 TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing
aoba 0:6d3813637f20 3 Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers.
aoba 0:6d3813637f20 4 Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson.
aoba 0:6d3813637f20 5 Location precision improvements suggested by Wayne Holder.
aoba 0:6d3813637f20 6 Copyright (C) 2008-2013 Mikal Hart
aoba 0:6d3813637f20 7 All rights reserved.
aoba 0:6d3813637f20 8
aoba 0:6d3813637f20 9 This library is free software; you can redistribute it and/or
aoba 0:6d3813637f20 10 modify it under the terms of the GNU Lesser General Public
aoba 0:6d3813637f20 11 License as published by the Free Software Foundation; either
aoba 0:6d3813637f20 12 version 2.1 of the License, or (at your option) any later version.
aoba 0:6d3813637f20 13
aoba 0:6d3813637f20 14 This library is distributed in the hope that it will be useful,
aoba 0:6d3813637f20 15 but WITHOUT ANY WARRANTY; without even the implied warranty of
aoba 0:6d3813637f20 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
aoba 0:6d3813637f20 17 Lesser General Public License for more details.
aoba 0:6d3813637f20 18
aoba 0:6d3813637f20 19 You should have received a copy of the GNU Lesser General Public
aoba 0:6d3813637f20 20 License along with this library; if not, write to the Free Software
aoba 0:6d3813637f20 21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
aoba 0:6d3813637f20 22 */
aoba 0:6d3813637f20 23
aoba 0:6d3813637f20 24 #include "TinyGPSPlus.h"
aoba 0:6d3813637f20 25
aoba 0:6d3813637f20 26 // Arduino compatibility
aoba 0:6d3813637f20 27 #include <mbed.h>
aoba 0:6d3813637f20 28 unsigned long millis(void) {
aoba 0:6d3813637f20 29 return us_ticker_read() / 1000;
aoba 0:6d3813637f20 30 }
aoba 0:6d3813637f20 31
aoba 0:6d3813637f20 32 #include <math.h>
aoba 0:6d3813637f20 33 #define TWO_PI 6.2831855
aoba 0:6d3813637f20 34 double radians(double degrees) {
aoba 0:6d3813637f20 35 return (degrees * 71) / 4068;
aoba 0:6d3813637f20 36 }
aoba 0:6d3813637f20 37 double degrees(double radians) {
aoba 0:6d3813637f20 38 return radians * 57296 / 1000;
aoba 0:6d3813637f20 39 }
aoba 0:6d3813637f20 40 double sq(double n) {
aoba 0:6d3813637f20 41 return n*n;
aoba 0:6d3813637f20 42 }
aoba 0:6d3813637f20 43 // Arduino compatibility
aoba 0:6d3813637f20 44
aoba 0:6d3813637f20 45 #include <string.h>
aoba 0:6d3813637f20 46 #include <ctype.h>
aoba 0:6d3813637f20 47 #include <stdlib.h>
aoba 0:6d3813637f20 48
aoba 0:6d3813637f20 49 #define _GPRMCterm "GPRMC"
aoba 0:6d3813637f20 50 #define _GPGGAterm "GPGGA"
aoba 0:6d3813637f20 51 #define _GNRMCterm "GNRMC"
aoba 0:6d3813637f20 52 #define _GNGGAterm "GNGGA"
aoba 0:6d3813637f20 53
aoba 0:6d3813637f20 54 TinyGPSPlus::TinyGPSPlus()
aoba 0:6d3813637f20 55 : parity(0)
aoba 0:6d3813637f20 56 , isChecksumTerm(false)
aoba 0:6d3813637f20 57 , curSentenceType(GPS_SENTENCE_OTHER)
aoba 0:6d3813637f20 58 , curTermNumber(0)
aoba 0:6d3813637f20 59 , curTermOffset(0)
aoba 0:6d3813637f20 60 , sentenceHasFix(false)
aoba 0:6d3813637f20 61 , customElts(0)
aoba 0:6d3813637f20 62 , customCandidates(0)
aoba 0:6d3813637f20 63 , encodedCharCount(0)
aoba 0:6d3813637f20 64 , sentencesWithFixCount(0)
aoba 0:6d3813637f20 65 , failedChecksumCount(0)
aoba 0:6d3813637f20 66 , passedChecksumCount(0)
aoba 0:6d3813637f20 67 {
aoba 0:6d3813637f20 68 term[0] = '\0';
aoba 0:6d3813637f20 69 }
aoba 0:6d3813637f20 70
aoba 0:6d3813637f20 71 //
aoba 0:6d3813637f20 72 // public methods
aoba 0:6d3813637f20 73 //
aoba 0:6d3813637f20 74
aoba 0:6d3813637f20 75 bool TinyGPSPlus::encode(char c)
aoba 0:6d3813637f20 76 {
aoba 0:6d3813637f20 77 ++encodedCharCount;
aoba 0:6d3813637f20 78
aoba 0:6d3813637f20 79 switch(c)
aoba 0:6d3813637f20 80 {
aoba 0:6d3813637f20 81 case ',': // term terminators
aoba 0:6d3813637f20 82 parity ^= (uint8_t)c;
aoba 0:6d3813637f20 83 case '\r':
aoba 0:6d3813637f20 84 case '\n':
aoba 0:6d3813637f20 85 case '*':
aoba 0:6d3813637f20 86 {
aoba 0:6d3813637f20 87 bool isValidSentence = false;
aoba 0:6d3813637f20 88 if (curTermOffset < sizeof(term))
aoba 0:6d3813637f20 89 {
aoba 0:6d3813637f20 90 term[curTermOffset] = 0;
aoba 0:6d3813637f20 91 isValidSentence = endOfTermHandler();
aoba 0:6d3813637f20 92 }
aoba 0:6d3813637f20 93 ++curTermNumber;
aoba 0:6d3813637f20 94 curTermOffset = 0;
aoba 0:6d3813637f20 95 isChecksumTerm = c == '*';
aoba 0:6d3813637f20 96 return isValidSentence;
aoba 0:6d3813637f20 97 }
aoba 0:6d3813637f20 98 break;
aoba 0:6d3813637f20 99
aoba 0:6d3813637f20 100 case '$': // sentence begin
aoba 0:6d3813637f20 101 curTermNumber = curTermOffset = 0;
aoba 0:6d3813637f20 102 parity = 0;
aoba 0:6d3813637f20 103 curSentenceType = GPS_SENTENCE_OTHER;
aoba 0:6d3813637f20 104 isChecksumTerm = false;
aoba 0:6d3813637f20 105 sentenceHasFix = false;
aoba 0:6d3813637f20 106 return false;
aoba 0:6d3813637f20 107
aoba 0:6d3813637f20 108 default: // ordinary characters
aoba 0:6d3813637f20 109 if (curTermOffset < sizeof(term) - 1)
aoba 0:6d3813637f20 110 term[curTermOffset++] = c;
aoba 0:6d3813637f20 111 if (!isChecksumTerm)
aoba 0:6d3813637f20 112 parity ^= c;
aoba 0:6d3813637f20 113 return false;
aoba 0:6d3813637f20 114 }
aoba 0:6d3813637f20 115
aoba 0:6d3813637f20 116 return false;
aoba 0:6d3813637f20 117 }
aoba 0:6d3813637f20 118
aoba 0:6d3813637f20 119 //
aoba 0:6d3813637f20 120 // internal utilities
aoba 0:6d3813637f20 121 //
aoba 0:6d3813637f20 122 int TinyGPSPlus::fromHex(char a)
aoba 0:6d3813637f20 123 {
aoba 0:6d3813637f20 124 if (a >= 'A' && a <= 'F')
aoba 0:6d3813637f20 125 return a - 'A' + 10;
aoba 0:6d3813637f20 126 else if (a >= 'a' && a <= 'f')
aoba 0:6d3813637f20 127 return a - 'a' + 10;
aoba 0:6d3813637f20 128 else
aoba 0:6d3813637f20 129 return a - '0';
aoba 0:6d3813637f20 130 }
aoba 0:6d3813637f20 131
aoba 0:6d3813637f20 132 // static
aoba 0:6d3813637f20 133 // Parse a (potentially negative) number with up to 2 decimal digits -xxxx.yy
aoba 0:6d3813637f20 134 int32_t TinyGPSPlus::parseDecimal(const char *term)
aoba 0:6d3813637f20 135 {
aoba 0:6d3813637f20 136 bool negative = *term == '-';
aoba 0:6d3813637f20 137 if (negative) ++term;
aoba 0:6d3813637f20 138 int32_t ret = 100 * (int32_t)atol(term);
aoba 0:6d3813637f20 139 while (isdigit(*term)) ++term;
aoba 0:6d3813637f20 140 if (*term == '.' && isdigit(term[1]))
aoba 0:6d3813637f20 141 {
aoba 0:6d3813637f20 142 ret += 10 * (term[1] - '0');
aoba 0:6d3813637f20 143 if (isdigit(term[2]))
aoba 0:6d3813637f20 144 ret += term[2] - '0';
aoba 0:6d3813637f20 145 }
aoba 0:6d3813637f20 146 return negative ? -ret : ret;
aoba 0:6d3813637f20 147 }
aoba 0:6d3813637f20 148
aoba 0:6d3813637f20 149 // static
aoba 0:6d3813637f20 150 // Parse degrees in that funny NMEA format DDMM.MMMM
aoba 0:6d3813637f20 151 void TinyGPSPlus::parseDegrees(const char *term, RawDegrees &deg)
aoba 0:6d3813637f20 152 {
aoba 0:6d3813637f20 153 uint32_t leftOfDecimal = (uint32_t)atol(term);
aoba 0:6d3813637f20 154 uint16_t minutes = (uint16_t)(leftOfDecimal % 100);
aoba 0:6d3813637f20 155 uint32_t multiplier = 10000000UL;
aoba 0:6d3813637f20 156 uint32_t tenMillionthsOfMinutes = minutes * multiplier;
aoba 0:6d3813637f20 157
aoba 0:6d3813637f20 158 deg.deg = (int16_t)(leftOfDecimal / 100);
aoba 0:6d3813637f20 159
aoba 0:6d3813637f20 160 while (isdigit(*term))
aoba 0:6d3813637f20 161 ++term;
aoba 0:6d3813637f20 162
aoba 0:6d3813637f20 163 if (*term == '.')
aoba 0:6d3813637f20 164 while (isdigit(*++term))
aoba 0:6d3813637f20 165 {
aoba 0:6d3813637f20 166 multiplier /= 10;
aoba 0:6d3813637f20 167 tenMillionthsOfMinutes += (*term - '0') * multiplier;
aoba 0:6d3813637f20 168 }
aoba 0:6d3813637f20 169
aoba 0:6d3813637f20 170 deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3;
aoba 0:6d3813637f20 171 deg.negative = false;
aoba 0:6d3813637f20 172 }
aoba 0:6d3813637f20 173
aoba 0:6d3813637f20 174 #define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number)
aoba 0:6d3813637f20 175
aoba 0:6d3813637f20 176 // Processes a just-completed term
aoba 0:6d3813637f20 177 // Returns true if new sentence has just passed checksum test and is validated
aoba 0:6d3813637f20 178 bool TinyGPSPlus::endOfTermHandler()
aoba 0:6d3813637f20 179 {
aoba 0:6d3813637f20 180 // If it's the checksum term, and the checksum checks out, commit
aoba 0:6d3813637f20 181 if (isChecksumTerm)
aoba 0:6d3813637f20 182 {
aoba 0:6d3813637f20 183 uint8_t checksum = 16 * fromHex(term[0]) + fromHex(term[1]);
aoba 0:6d3813637f20 184 if (checksum == parity)
aoba 0:6d3813637f20 185 {
aoba 0:6d3813637f20 186 passedChecksumCount++;
aoba 0:6d3813637f20 187 if (sentenceHasFix)
aoba 0:6d3813637f20 188 ++sentencesWithFixCount;
aoba 0:6d3813637f20 189
aoba 0:6d3813637f20 190 switch(curSentenceType)
aoba 0:6d3813637f20 191 {
aoba 0:6d3813637f20 192 case GPS_SENTENCE_GPRMC:
aoba 0:6d3813637f20 193 date.commit();
aoba 0:6d3813637f20 194 time.commit();
aoba 0:6d3813637f20 195 if (sentenceHasFix)
aoba 0:6d3813637f20 196 {
aoba 0:6d3813637f20 197 location.commit();
aoba 0:6d3813637f20 198 speed.commit();
aoba 0:6d3813637f20 199 course.commit();
aoba 0:6d3813637f20 200 }
aoba 0:6d3813637f20 201 break;
aoba 0:6d3813637f20 202 case GPS_SENTENCE_GPGGA:
aoba 0:6d3813637f20 203 time.commit();
aoba 0:6d3813637f20 204 if (sentenceHasFix)
aoba 0:6d3813637f20 205 {
aoba 0:6d3813637f20 206 location.commit();
aoba 0:6d3813637f20 207 altitude.commit();
aoba 0:6d3813637f20 208 }
aoba 0:6d3813637f20 209 satellites.commit();
aoba 0:6d3813637f20 210 hdop.commit();
aoba 0:6d3813637f20 211 break;
aoba 0:6d3813637f20 212 }
aoba 0:6d3813637f20 213
aoba 0:6d3813637f20 214 // Commit all custom listeners of this sentence type
aoba 0:6d3813637f20 215 for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0; p = p->next)
aoba 0:6d3813637f20 216 p->commit();
aoba 0:6d3813637f20 217 return true;
aoba 0:6d3813637f20 218 }
aoba 0:6d3813637f20 219
aoba 0:6d3813637f20 220 else
aoba 0:6d3813637f20 221 {
aoba 0:6d3813637f20 222 ++failedChecksumCount;
aoba 0:6d3813637f20 223 }
aoba 0:6d3813637f20 224
aoba 0:6d3813637f20 225 return false;
aoba 0:6d3813637f20 226 }
aoba 0:6d3813637f20 227
aoba 0:6d3813637f20 228 // the first term determines the sentence type
aoba 0:6d3813637f20 229 if (curTermNumber == 0)
aoba 0:6d3813637f20 230 {
aoba 0:6d3813637f20 231 if (!strcmp(term, _GPRMCterm) || !strcmp(term, _GNRMCterm))
aoba 0:6d3813637f20 232 curSentenceType = GPS_SENTENCE_GPRMC;
aoba 0:6d3813637f20 233 else if (!strcmp(term, _GPGGAterm) || !strcmp(term, _GNGGAterm))
aoba 0:6d3813637f20 234 curSentenceType = GPS_SENTENCE_GPGGA;
aoba 0:6d3813637f20 235 else
aoba 0:6d3813637f20 236 curSentenceType = GPS_SENTENCE_OTHER;
aoba 0:6d3813637f20 237
aoba 0:6d3813637f20 238 // Any custom candidates of this sentence type?
aoba 0:6d3813637f20 239 for (customCandidates = customElts; customCandidates != NULL && strcmp(customCandidates->sentenceName, term) < 0; customCandidates = customCandidates->next);
aoba 0:6d3813637f20 240 if (customCandidates != NULL && strcmp(customCandidates->sentenceName, term) > 0)
aoba 0:6d3813637f20 241 customCandidates = NULL;
aoba 0:6d3813637f20 242
aoba 0:6d3813637f20 243 return false;
aoba 0:6d3813637f20 244 }
aoba 0:6d3813637f20 245
aoba 0:6d3813637f20 246 if (curSentenceType != GPS_SENTENCE_OTHER && term[0])
aoba 0:6d3813637f20 247 switch(COMBINE(curSentenceType, curTermNumber))
aoba 0:6d3813637f20 248 {
aoba 0:6d3813637f20 249 case COMBINE(GPS_SENTENCE_GPRMC, 1): // Time in both sentences
aoba 0:6d3813637f20 250 case COMBINE(GPS_SENTENCE_GPGGA, 1):
aoba 0:6d3813637f20 251 time.setTime(term);
aoba 0:6d3813637f20 252 break;
aoba 0:6d3813637f20 253 case COMBINE(GPS_SENTENCE_GPRMC, 2): // GPRMC validity
aoba 0:6d3813637f20 254 sentenceHasFix = term[0] == 'A';
aoba 0:6d3813637f20 255 break;
aoba 0:6d3813637f20 256 case COMBINE(GPS_SENTENCE_GPRMC, 3): // Latitude
aoba 0:6d3813637f20 257 case COMBINE(GPS_SENTENCE_GPGGA, 2):
aoba 0:6d3813637f20 258 location.setLatitude(term);
aoba 0:6d3813637f20 259 break;
aoba 0:6d3813637f20 260 case COMBINE(GPS_SENTENCE_GPRMC, 4): // N/S
aoba 0:6d3813637f20 261 case COMBINE(GPS_SENTENCE_GPGGA, 3):
aoba 0:6d3813637f20 262 location.rawNewLatData.negative = term[0] == 'S';
aoba 0:6d3813637f20 263 break;
aoba 0:6d3813637f20 264 case COMBINE(GPS_SENTENCE_GPRMC, 5): // Longitude
aoba 0:6d3813637f20 265 case COMBINE(GPS_SENTENCE_GPGGA, 4):
aoba 0:6d3813637f20 266 location.setLongitude(term);
aoba 0:6d3813637f20 267 break;
aoba 0:6d3813637f20 268 case COMBINE(GPS_SENTENCE_GPRMC, 6): // E/W
aoba 0:6d3813637f20 269 case COMBINE(GPS_SENTENCE_GPGGA, 5):
aoba 0:6d3813637f20 270 location.rawNewLngData.negative = term[0] == 'W';
aoba 0:6d3813637f20 271 break;
aoba 0:6d3813637f20 272 case COMBINE(GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC)
aoba 0:6d3813637f20 273 speed.set(term);
aoba 0:6d3813637f20 274 break;
aoba 0:6d3813637f20 275 case COMBINE(GPS_SENTENCE_GPRMC, 8): // Course (GPRMC)
aoba 0:6d3813637f20 276 course.set(term);
aoba 0:6d3813637f20 277 break;
aoba 0:6d3813637f20 278 case COMBINE(GPS_SENTENCE_GPRMC, 9): // Date (GPRMC)
aoba 0:6d3813637f20 279 date.setDate(term);
aoba 0:6d3813637f20 280 break;
aoba 0:6d3813637f20 281 case COMBINE(GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA)
aoba 0:6d3813637f20 282 sentenceHasFix = term[0] > '0';
aoba 0:6d3813637f20 283 break;
aoba 0:6d3813637f20 284 case COMBINE(GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA)
aoba 0:6d3813637f20 285 satellites.set(term);
aoba 0:6d3813637f20 286 break;
aoba 0:6d3813637f20 287 case COMBINE(GPS_SENTENCE_GPGGA, 8): // HDOP
aoba 0:6d3813637f20 288 hdop.set(term);
aoba 0:6d3813637f20 289 break;
aoba 0:6d3813637f20 290 case COMBINE(GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA)
aoba 0:6d3813637f20 291 altitude.set(term);
aoba 0:6d3813637f20 292 break;
aoba 0:6d3813637f20 293 }
aoba 0:6d3813637f20 294
aoba 0:6d3813637f20 295 // Set custom values as needed
aoba 0:6d3813637f20 296 for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; p = p->next)
aoba 0:6d3813637f20 297 if (p->termNumber == curTermNumber)
aoba 0:6d3813637f20 298 p->set(term);
aoba 0:6d3813637f20 299
aoba 0:6d3813637f20 300 return false;
aoba 0:6d3813637f20 301 }
aoba 0:6d3813637f20 302
aoba 0:6d3813637f20 303 /* static */
aoba 0:6d3813637f20 304 double TinyGPSPlus::distanceBetween(double lat1, double long1, double lat2, double long2)
aoba 0:6d3813637f20 305 {
aoba 0:6d3813637f20 306 // returns distance in meters between two positions, both specified
aoba 0:6d3813637f20 307 // as signed decimal-degrees latitude and longitude. Uses great-circle
aoba 0:6d3813637f20 308 // distance computation for hypothetical sphere of radius 6372795 meters.
aoba 0:6d3813637f20 309 // Because Earth is no exact sphere, rounding errors may be up to 0.5%.
aoba 0:6d3813637f20 310 // Courtesy of Maarten Lamers
aoba 0:6d3813637f20 311 double delta = radians(long1-long2);
aoba 0:6d3813637f20 312 double sdlong = sin(delta);
aoba 0:6d3813637f20 313 double cdlong = cos(delta);
aoba 0:6d3813637f20 314 lat1 = radians(lat1);
aoba 0:6d3813637f20 315 lat2 = radians(lat2);
aoba 0:6d3813637f20 316 double slat1 = sin(lat1);
aoba 0:6d3813637f20 317 double clat1 = cos(lat1);
aoba 0:6d3813637f20 318 double slat2 = sin(lat2);
aoba 0:6d3813637f20 319 double clat2 = cos(lat2);
aoba 0:6d3813637f20 320 delta = (clat1 * slat2) - (slat1 * clat2 * cdlong);
aoba 0:6d3813637f20 321 delta = sq(delta);
aoba 0:6d3813637f20 322 delta += sq(clat2 * sdlong);
aoba 0:6d3813637f20 323 delta = sqrt(delta);
aoba 0:6d3813637f20 324 double denom = (slat1 * slat2) + (clat1 * clat2 * cdlong);
aoba 0:6d3813637f20 325 delta = atan2(delta, denom);
aoba 0:6d3813637f20 326 return delta * 6372795;
aoba 0:6d3813637f20 327 }
aoba 0:6d3813637f20 328
aoba 0:6d3813637f20 329 double TinyGPSPlus::courseTo(double lat1, double long1, double lat2, double long2)
aoba 0:6d3813637f20 330 {
aoba 0:6d3813637f20 331 // returns course in degrees (North=0, West=270) from position 1 to position 2,
aoba 0:6d3813637f20 332 // both specified as signed decimal-degrees latitude and longitude.
aoba 0:6d3813637f20 333 // Because Earth is no exact sphere, calculated course may be off by a tiny fraction.
aoba 0:6d3813637f20 334 // Courtesy of Maarten Lamers
aoba 0:6d3813637f20 335 double dlon = radians(long2-long1);
aoba 0:6d3813637f20 336 lat1 = radians(lat1);
aoba 0:6d3813637f20 337 lat2 = radians(lat2);
aoba 0:6d3813637f20 338 double a1 = sin(dlon) * cos(lat2);
aoba 0:6d3813637f20 339 double a2 = sin(lat1) * cos(lat2) * cos(dlon);
aoba 0:6d3813637f20 340 a2 = cos(lat1) * sin(lat2) - a2;
aoba 0:6d3813637f20 341 a2 = atan2(a1, a2);
aoba 0:6d3813637f20 342 if (a2 < 0.0)
aoba 0:6d3813637f20 343 {
aoba 0:6d3813637f20 344 a2 += TWO_PI;
aoba 0:6d3813637f20 345 }
aoba 0:6d3813637f20 346 return degrees(a2);
aoba 0:6d3813637f20 347 }
aoba 0:6d3813637f20 348
aoba 0:6d3813637f20 349 const char *TinyGPSPlus::cardinal(double course)
aoba 0:6d3813637f20 350 {
aoba 0:6d3813637f20 351 static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
aoba 0:6d3813637f20 352 int direction = (int)((course + 11.25f) / 22.5f);
aoba 0:6d3813637f20 353 return directions[direction % 16];
aoba 0:6d3813637f20 354 }
aoba 0:6d3813637f20 355
aoba 0:6d3813637f20 356 void TinyGPSLocation::commit()
aoba 0:6d3813637f20 357 {
aoba 0:6d3813637f20 358 rawLatData = rawNewLatData;
aoba 0:6d3813637f20 359 rawLngData = rawNewLngData;
aoba 0:6d3813637f20 360 lastCommitTime = millis();
aoba 0:6d3813637f20 361 valid = updated = true;
aoba 0:6d3813637f20 362 }
aoba 0:6d3813637f20 363
aoba 0:6d3813637f20 364 void TinyGPSLocation::setLatitude(const char *term)
aoba 0:6d3813637f20 365 {
aoba 0:6d3813637f20 366 TinyGPSPlus::parseDegrees(term, rawNewLatData);
aoba 0:6d3813637f20 367 }
aoba 0:6d3813637f20 368
aoba 0:6d3813637f20 369 void TinyGPSLocation::setLongitude(const char *term)
aoba 0:6d3813637f20 370 {
aoba 0:6d3813637f20 371 TinyGPSPlus::parseDegrees(term, rawNewLngData);
aoba 0:6d3813637f20 372 }
aoba 0:6d3813637f20 373
aoba 0:6d3813637f20 374 double TinyGPSLocation::lat()
aoba 0:6d3813637f20 375 {
aoba 0:6d3813637f20 376 updated = false;
aoba 0:6d3813637f20 377 double ret = rawLatData.deg + rawLatData.billionths / 1000000000.0;
aoba 0:6d3813637f20 378 return rawLatData.negative ? -ret : ret;
aoba 0:6d3813637f20 379 }
aoba 0:6d3813637f20 380
aoba 0:6d3813637f20 381 double TinyGPSLocation::lng()
aoba 0:6d3813637f20 382 {
aoba 0:6d3813637f20 383 updated = false;
aoba 0:6d3813637f20 384 double ret = rawLngData.deg + rawLngData.billionths / 1000000000.0;
aoba 0:6d3813637f20 385 return rawLngData.negative ? -ret : ret;
aoba 0:6d3813637f20 386 }
aoba 0:6d3813637f20 387
aoba 0:6d3813637f20 388 void TinyGPSDate::commit()
aoba 0:6d3813637f20 389 {
aoba 0:6d3813637f20 390 date = newDate;
aoba 0:6d3813637f20 391 lastCommitTime = millis();
aoba 0:6d3813637f20 392 valid = updated = true;
aoba 0:6d3813637f20 393 }
aoba 0:6d3813637f20 394
aoba 0:6d3813637f20 395 void TinyGPSTime::commit()
aoba 0:6d3813637f20 396 {
aoba 0:6d3813637f20 397 time = newTime;
aoba 0:6d3813637f20 398 lastCommitTime = millis();
aoba 0:6d3813637f20 399 valid = updated = true;
aoba 0:6d3813637f20 400 }
aoba 0:6d3813637f20 401
aoba 0:6d3813637f20 402 void TinyGPSTime::setTime(const char *term)
aoba 0:6d3813637f20 403 {
aoba 0:6d3813637f20 404 newTime = (uint32_t)TinyGPSPlus::parseDecimal(term);
aoba 0:6d3813637f20 405 }
aoba 0:6d3813637f20 406
aoba 0:6d3813637f20 407 void TinyGPSDate::setDate(const char *term)
aoba 0:6d3813637f20 408 {
aoba 0:6d3813637f20 409 newDate = atol(term);
aoba 0:6d3813637f20 410 }
aoba 0:6d3813637f20 411
aoba 0:6d3813637f20 412 uint16_t TinyGPSDate::year()
aoba 0:6d3813637f20 413 {
aoba 0:6d3813637f20 414 updated = false;
aoba 0:6d3813637f20 415 uint16_t year = date % 100;
aoba 0:6d3813637f20 416 return year + 2000;
aoba 0:6d3813637f20 417 }
aoba 0:6d3813637f20 418
aoba 0:6d3813637f20 419 uint8_t TinyGPSDate::month()
aoba 0:6d3813637f20 420 {
aoba 0:6d3813637f20 421 updated = false;
aoba 0:6d3813637f20 422 return (date / 100) % 100;
aoba 0:6d3813637f20 423 }
aoba 0:6d3813637f20 424
aoba 0:6d3813637f20 425 uint8_t TinyGPSDate::day()
aoba 0:6d3813637f20 426 {
aoba 0:6d3813637f20 427 updated = false;
aoba 0:6d3813637f20 428 return date / 10000;
aoba 0:6d3813637f20 429 }
aoba 0:6d3813637f20 430
aoba 0:6d3813637f20 431 uint8_t TinyGPSTime::hour()
aoba 0:6d3813637f20 432 {
aoba 0:6d3813637f20 433 updated = false;
aoba 0:6d3813637f20 434 return time / 1000000;
aoba 0:6d3813637f20 435 }
aoba 0:6d3813637f20 436
aoba 0:6d3813637f20 437 uint8_t TinyGPSTime::minute()
aoba 0:6d3813637f20 438 {
aoba 0:6d3813637f20 439 updated = false;
aoba 0:6d3813637f20 440 return (time / 10000) % 100;
aoba 0:6d3813637f20 441 }
aoba 0:6d3813637f20 442
aoba 0:6d3813637f20 443 uint8_t TinyGPSTime::second()
aoba 0:6d3813637f20 444 {
aoba 0:6d3813637f20 445 updated = false;
aoba 0:6d3813637f20 446 return (time / 100) % 100;
aoba 0:6d3813637f20 447 }
aoba 0:6d3813637f20 448
aoba 0:6d3813637f20 449 uint8_t TinyGPSTime::centisecond()
aoba 0:6d3813637f20 450 {
aoba 0:6d3813637f20 451 updated = false;
aoba 0:6d3813637f20 452 return time % 100;
aoba 0:6d3813637f20 453 }
aoba 0:6d3813637f20 454
aoba 0:6d3813637f20 455 void TinyGPSDecimal::commit()
aoba 0:6d3813637f20 456 {
aoba 0:6d3813637f20 457 val = newval;
aoba 0:6d3813637f20 458 lastCommitTime = millis();
aoba 0:6d3813637f20 459 valid = updated = true;
aoba 0:6d3813637f20 460 }
aoba 0:6d3813637f20 461
aoba 0:6d3813637f20 462 void TinyGPSDecimal::set(const char *term)
aoba 0:6d3813637f20 463 {
aoba 0:6d3813637f20 464 newval = TinyGPSPlus::parseDecimal(term);
aoba 0:6d3813637f20 465 }
aoba 0:6d3813637f20 466
aoba 0:6d3813637f20 467 void TinyGPSInteger::commit()
aoba 0:6d3813637f20 468 {
aoba 0:6d3813637f20 469 val = newval;
aoba 0:6d3813637f20 470 lastCommitTime = millis();
aoba 0:6d3813637f20 471 valid = updated = true;
aoba 0:6d3813637f20 472 }
aoba 0:6d3813637f20 473
aoba 0:6d3813637f20 474 void TinyGPSInteger::set(const char *term)
aoba 0:6d3813637f20 475 {
aoba 0:6d3813637f20 476 newval = atol(term);
aoba 0:6d3813637f20 477 }
aoba 0:6d3813637f20 478
aoba 0:6d3813637f20 479 TinyGPSCustom::TinyGPSCustom(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber)
aoba 0:6d3813637f20 480 {
aoba 0:6d3813637f20 481 begin(gps, _sentenceName, _termNumber);
aoba 0:6d3813637f20 482 }
aoba 0:6d3813637f20 483
aoba 0:6d3813637f20 484 void TinyGPSCustom::begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber)
aoba 0:6d3813637f20 485 {
aoba 0:6d3813637f20 486 lastCommitTime = 0;
aoba 0:6d3813637f20 487 updated = valid = false;
aoba 0:6d3813637f20 488 sentenceName = _sentenceName;
aoba 0:6d3813637f20 489 termNumber = _termNumber;
aoba 0:6d3813637f20 490 memset(stagingBuffer, '\0', sizeof(stagingBuffer));
aoba 0:6d3813637f20 491 memset(buffer, '\0', sizeof(buffer));
aoba 0:6d3813637f20 492
aoba 0:6d3813637f20 493 // Insert this item into the GPS tree
aoba 0:6d3813637f20 494 gps.insertCustom(this, _sentenceName, _termNumber);
aoba 0:6d3813637f20 495 }
aoba 0:6d3813637f20 496
aoba 0:6d3813637f20 497 void TinyGPSCustom::commit()
aoba 0:6d3813637f20 498 {
aoba 0:6d3813637f20 499 strcpy(this->buffer, this->stagingBuffer);
aoba 0:6d3813637f20 500 lastCommitTime = millis();
aoba 0:6d3813637f20 501 valid = updated = true;
aoba 0:6d3813637f20 502 }
aoba 0:6d3813637f20 503
aoba 0:6d3813637f20 504 void TinyGPSCustom::set(const char *term)
aoba 0:6d3813637f20 505 {
aoba 0:6d3813637f20 506 strncpy(this->stagingBuffer, term, sizeof(this->stagingBuffer));
aoba 0:6d3813637f20 507 }
aoba 0:6d3813637f20 508
aoba 0:6d3813637f20 509 void TinyGPSPlus::insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int termNumber)
aoba 0:6d3813637f20 510 {
aoba 0:6d3813637f20 511 TinyGPSCustom **ppelt;
aoba 0:6d3813637f20 512
aoba 0:6d3813637f20 513 for (ppelt = &this->customElts; *ppelt != NULL; ppelt = &(*ppelt)->next)
aoba 0:6d3813637f20 514 {
aoba 0:6d3813637f20 515 int cmp = strcmp(sentenceName, (*ppelt)->sentenceName);
aoba 0:6d3813637f20 516 if (cmp < 0 || (cmp == 0 && termNumber < (*ppelt)->termNumber))
aoba 0:6d3813637f20 517 break;
aoba 0:6d3813637f20 518 }
aoba 0:6d3813637f20 519
aoba 0:6d3813637f20 520 pElt->next = *ppelt;
aoba 0:6d3813637f20 521 *ppelt = pElt;
aoba 0:6d3813637f20 522 }