Revised Embedded Artists' MTK3339 library to save all raw GPS messages and enable optional decoding of RMC message.
Fork of MTK3339 by
MTK3339.cpp
00001 // Original author: Embedded Artists 00002 // Revised by T. Bronez, 2015-12-16 00003 00004 #include "mbed.h" 00005 #include "MTK3339.h" 00006 #define min(a,b) a>b?a:b 00007 00008 /* 00009 // For DEBUG, can flash LED2 on arrival of RMC message 00010 DigitalOut led2(LED2); 00011 Timeout timeout2; 00012 void led2Off() { 00013 led2 = 1; 00014 } 00015 void flashLED2() { 00016 led2 = 0; 00017 timeout2.attach(&led2Off, 0.1); 00018 } 00019 */ 00020 00021 MTK3339::MTK3339(PinName tx, PinName rx) : _serial(tx, rx) { 00022 _serial.baud(9600); 00023 _state = StateStart; 00024 _sentenceMask = 0; 00025 _availDataType = NMEA_NONE; 00026 memset(&rmc, 0, sizeof(RmcType)); 00027 00028 memset(&ggaMsg, 0, MSG_BUF_SZ+1); 00029 memset(&gsaMsg, 0, MSG_BUF_SZ+1); 00030 memset(&gsvMsg, 0, MSG_BUF_SZ+1); 00031 memset(&rmcMsg, 0, MSG_BUF_SZ+1); 00032 memset(&vtgMsg, 0, MSG_BUF_SZ+1); 00033 } 00034 00035 void MTK3339::start(void (*fptr)(void), int mask) { 00036 if (fptr && mask) { 00037 _dataCallback.attach(fptr); 00038 _sentenceMask = mask; 00039 _serial.attach(this, &MTK3339::uartIrq, Serial::RxIrq); 00040 } 00041 00042 // DEBUG: Query antenna status 00043 //_serial.printf("$PGTOP,11,3*6F\r\n"); 00044 //_serial.printf("$PGCMD,33,1*6C\r\n"); 00045 } 00046 00047 void MTK3339::stop() { 00048 _dataCallback.attach(NULL); 00049 _sentenceMask = 0; 00050 _serial.attach(NULL); 00051 } 00052 00053 MTK3339::NmeaSentence MTK3339::getAvailableDataType() { 00054 return _availDataType; 00055 } 00056 00057 void MTK3339::decodeRMC () { 00058 // See http://aprs.gids.nl/nmea/#rmc for RMC sentence definition 00059 const double sixtieth = 1.0/60.0; 00060 int n = 0; // scratch int for conversions 00061 double d = 0; // scratch double for conversions 00062 char* q = NULL; // scratch pointer for conversions 00063 00064 memset(&rmc, 0, sizeof(RmcType)); 00065 char* p = rmcMsg; 00066 00067 /** 00068 * Find and decode field 0, UTC time, as hhmmss.sss 00069 */ 00070 p = strchr(p, ','); // First comma 00071 if (p == NULL || *p == 0) return; // Unexpected 00072 p++; // Start of field 00073 00074 q = p; 00075 // Decode time hours 00076 n = ((*q++) - 48)*10; // tens 00077 n += ((*q++) - 48); // ones 00078 rmc.gps_tm.tm_hour = n; 00079 // Decode time minutes 00080 n = ((*q++) - 48)*10; // tens 00081 n += ((*q++) - 48); // ones 00082 rmc.gps_tm.tm_min = n; 00083 // Decode time seconds 00084 n = ((*q++) - 48)*10; // tens 00085 n += ((*q++) - 48); // ones 00086 rmc.gps_tm.tm_sec = n; 00087 // Decode time milliseconds 00088 q++; // decimal point 00089 n = ((*q++) - 48)*100; // hundreds of msec = tenths of second 00090 n += ((*q++) - 48)*10; // tens of msec = hundredths of second 00091 n += ((*q++) - 48); // ones of msec = thousandths of second 00092 rmc.msec = n; 00093 00094 /** 00095 * Find and decode field 1, GPS status, as single 00096 * character 'A' for valid or 'V' for invalid 00097 */ 00098 p = strchr(p, ','); // Next comma 00099 if (p == NULL || *p == 0) return; // Unexpected 00100 p++; // Start of field 00101 00102 if (*p == 'A' || *p == 'V') 00103 rmc.status = *p; 00104 else 00105 rmc.status = ' '; 00106 00107 /** 00108 * Find and decode field 2, latitude, formatted as ddmm.mmmm 00109 */ 00110 p = strchr(p, ','); // Next comma 00111 if (p == NULL || *p == 0) return; // Unexpected 00112 p++; // Start of field 00113 00114 q = p; 00115 d = ((*q++) - 48)*10; // tens 00116 d += ((*q++) - 48); // ones 00117 rmc.lat_dd = d; // whole degrees 00118 00119 d = ((*q++) - 48)*10; // tens 00120 d += ((*q++) - 48); // ones 00121 q++; // decimal point 00122 d += ((*q++) - 48)*0.1000; // tenths 00123 d += ((*q++) - 48)*0.0100; // hundredths 00124 d += ((*q++) - 48)*0.0010; // thousandths 00125 d += ((*q++) - 48)*0.0001; // ten-thousandths 00126 if (*q == ',') 00127 rmc.lat_dd += sixtieth*d; // decimal degrees 00128 else 00129 rmc.lat_dd = NAN; // Unexpected 00130 00131 /** 00132 * Find and decode field 3, latitude sense, as single 00133 * character 'N' for north or 'S' for south 00134 */ 00135 p = strchr(p, ','); // Next comma 00136 if (p == NULL || *p == 0) return; // Unexpected 00137 p++; // Start of field 00138 00139 if (*p == 'S') rmc.lat_dd = -rmc.lat_dd; 00140 00141 /** 00142 * Find and decode field 4, longitude, formatted as dddmm.mmmm 00143 */ 00144 p = strchr(p, ','); // Next comma 00145 if (p == NULL || *p == 0) return; // Unexpected 00146 p++; // Start of field 00147 00148 q = p; 00149 d = ((*q++) - 48)*100; // hundreds 00150 d += ((*q++) - 48)*10; // tens 00151 d += ((*q++) - 48); // ones 00152 rmc.lon_dd = d; // whole degrees 00153 00154 d = ((*q++) - 48)*10; // tens 00155 d += ((*q++) - 48); // ones 00156 q++; // decimal point 00157 d += ((*q++) - 48)*0.1000; // tenths 00158 d += ((*q++) - 48)*0.0100; // hundredths 00159 d += ((*q++) - 48)*0.0010; // thousandths 00160 d += ((*q++) - 48)*0.0001; // ten-thousandths 00161 if (*q == ',') 00162 rmc.lon_dd += sixtieth*d; // decimal degrees 00163 else 00164 rmc.lon_dd = NAN; // Unexpected 00165 00166 /** 00167 * Find and decode field 5, longitude sense, as single 00168 * character 'E' for east or 'W' for west 00169 */ 00170 p = strchr(p, ','); // Next comma 00171 if (p == NULL || *p == 0) return; // Unexpected 00172 p++; // Start of field 00173 00174 if (*p == 'W') rmc.lon_dd = -rmc.lon_dd; 00175 00176 /** 00177 * Find and skip field 6, speed in knots 00178 */ 00179 p = strchr(p, ','); // Next comma 00180 if (p == NULL || *p == 0) return; // Unexpected 00181 p++; // Start of field 00182 00183 /** 00184 * Find and skip field 7, course in degrees 00185 */ 00186 p = strchr(p, ','); // Next comma 00187 if (p == NULL || *p == 0) return; // Unexpected 00188 p++; // Start of field 00189 00190 /** 00191 * Find and decode field 8, UTC date, formatted as ddmmyy 00192 * where 1<=dd<=31, 1<=mm<=12, and yy is offset from 2000 00193 */ 00194 p = strchr(p, ','); // Next comma 00195 if (p == NULL || *p == 0) return; // Unexpected 00196 p++; // Start of field 00197 00198 q = p; 00199 // Decode date day 00200 n = ((*q++) - 48)*10; // tens 00201 n += ((*q++) - 48); // ones 00202 rmc.gps_tm.tm_mday = n; 00203 // Decode date month 00204 n = ((*q++) - 48)*10; // tens 00205 n += ((*q++) - 48); // ones 00206 rmc.gps_tm.tm_mon = n-1; // tm_mon runs 0-11, not 1-12 00207 // Decode date year 00208 n = ((*q++) - 48)*10; // tens 00209 n += ((*q++) - 48); // ones 00210 rmc.gps_tm.tm_year = n+100; // tm_year is offset from year 1900, not 2000 00211 00212 /** 00213 * Find and skip field 9, magnetic variation 00214 * (not implemented in GPS firmware) 00215 */ 00216 p = strchr(p, ','); // Next comma 00217 if (p == NULL || *p == 0) return; // Unexpected 00218 p++; // Start of field 00219 00220 /** 00221 * Find and skip field 10, magnetic variation direction 00222 * (not implemented in GPS firmware) 00223 */ 00224 p = strchr(p, ','); // Next comma 00225 if (p == NULL || *p == 0) return; // Unexpected 00226 p++; // Start of field 00227 } 00228 00229 void MTK3339::saveGGA(char* data, int dataLen) { 00230 // See http://aprs.gids.nl/nmea/#gga for GGA sentence definition 00231 memcpy (ggaMsg, data, min(dataLen, MSG_BUF_SZ)); 00232 } 00233 00234 void MTK3339::saveGSA(char* data, int dataLen) { 00235 // See http://aprs.gids.nl/nmea/#gsa for GSA sentence definition 00236 memcpy (gsaMsg, data, min(dataLen, MSG_BUF_SZ)); 00237 } 00238 00239 void MTK3339::saveGSV(char* data, int dataLen) { 00240 // See http://aprs.gids.nl/nmea/#gsv for GSV sentence definition 00241 memcpy (gsvMsg, data, min(dataLen, MSG_BUF_SZ)); 00242 } 00243 00244 void MTK3339::saveRMC(char* data, int dataLen) { 00245 // See http://aprs.gids.nl/nmea/#rmc for RMC sentence definition 00246 memcpy (rmcMsg, data, min(dataLen, MSG_BUF_SZ)); 00247 } 00248 00249 void MTK3339::saveVTG(char* data, int dataLen) { 00250 // See http://aprs.gids.nl/nmea/#vtg for VTG sentence definition 00251 memcpy (vtgMsg, data, min(dataLen, MSG_BUF_SZ)); 00252 } 00253 00254 void MTK3339::saveData(char* data, int len) { 00255 do { 00256 // Verify checksum 00257 if (len < 3 || (len > 3 && data[len-3] != '*')) { 00258 // invalid data 00259 break; 00260 } 00261 int sum = strtol(&data[len-2], NULL, 16); 00262 for(int i = 1; i < len-3; i++) { 00263 sum ^= data[i]; 00264 } 00265 if (sum != 0) { 00266 // invalid checksum 00267 break; 00268 } 00269 00270 //printf("msg: %s\n", data); // DEBUG 00271 00272 // Save appropriate sentence for later decoding 00273 if (strncmp("$GPRMC", data, 6) == 0 && (_sentenceMask & NMEA_RMC) != 0) { 00274 //flashLED2(); // for DEBUG 00275 saveRMC(data, len); 00276 _availDataType = NMEA_RMC; 00277 _dataCallback.call(); 00278 _availDataType = NMEA_NONE; 00279 } 00280 else if (strncmp("$GPGGA", data, 6) == 0 && (_sentenceMask & NMEA_GGA) != 0) { 00281 saveGGA(data, len); 00282 _availDataType = NMEA_GGA; 00283 _dataCallback.call(); 00284 _availDataType = NMEA_NONE; 00285 } 00286 else if (strncmp("$GPGSA", data, 6) == 0 && (_sentenceMask & NMEA_GSA) != 0) { 00287 saveGSA(data, len); 00288 _availDataType = NMEA_GSA; 00289 _dataCallback.call(); 00290 _availDataType = NMEA_NONE; 00291 } 00292 else if (strncmp("$GPGSV", data, 6) == 0 && (_sentenceMask & NMEA_GSV) != 0) { 00293 saveGSV(data, len); 00294 _availDataType = NMEA_GSV; 00295 _dataCallback.call(); 00296 _availDataType = NMEA_NONE; 00297 } 00298 else if (strncmp("$GPVTG", data, 6) == 0 && (_sentenceMask & NMEA_VTG) != 0) { 00299 saveVTG(data, len); 00300 _availDataType = NMEA_VTG; 00301 _dataCallback.call(); 00302 _availDataType = NMEA_NONE; 00303 } 00304 } while(0); 00305 } 00306 00307 void MTK3339::uartIrq() { 00308 char d = 0; 00309 while(_serial.readable()) { 00310 d = _serial.getc(); 00311 switch(_state) { 00312 case StateStart: 00313 if (d == '$') { 00314 _buf[0] = '$'; 00315 _bufPos = 1; 00316 _state = StateData; 00317 } 00318 break; 00319 case StateData: 00320 if (_bufPos >= MTK3339_BUF_SZ) { 00321 // error 00322 _state = StateStart; 00323 } 00324 else if (d == '\r') { 00325 _buf[_bufPos] = 0; 00326 saveData(_buf, _bufPos); 00327 _state = StateStart; 00328 } 00329 else { 00330 _buf[_bufPos++] = d; 00331 } 00332 break; 00333 } 00334 } 00335 } 00336 00337 /* 00338 From http://forum.trenz-electronic.de/index.php?topic=273.0 00339 (untested), other MTK3339 commands and responses: 00340 00341 To disable antenna status/advisor messages, 00342 $PGCMD,33,0*6D<CR><LF> 00343 00344 MTK3339 should acknowledge with 00345 $PGACK,33,0*6E<CR><LF> 00346 00347 To enable antenna status/advisor messages, 00348 $PGCMD,33,1*6C<CR><LF> 00349 00350 MTK3339 should acknowledge with 00351 $PGACK,33,1*6F<CR><LF> 00352 00353 To query antenna status, 00354 $PGTOP,11,3*6F<CR><LF> 00355 00356 MTK3339 should respond with one of the following: 00357 $PGTOP,11,1*6D active antenna shorted 00358 $PGTOP,11,2*6E using internal antenna 00359 $PGTOP,11,3*6F using active antenna 00360 */
Generated on Fri Jul 22 2022 20:10:17 by 1.7.2