Interface to the MTK3339 GPS module
Dependents: app_gps lpc812_exp_solution_exp-port-gps-lib
Revision 0:bd0fe2412980, committed 2013-11-07
- Comitter:
- embeddedartists
- Date:
- Thu Nov 07 11:46:53 2013 +0000
- Commit message:
- First release
Changed in this revision
MTK3339.cpp | Show annotated file Show diff for this revision Revisions of this file |
MTK3339.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r bd0fe2412980 MTK3339.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MTK3339.cpp Thu Nov 07 11:46:53 2013 +0000 @@ -0,0 +1,249 @@ + +#include "mbed.h" +#include "MTK3339.h" + + +MTK3339::MTK3339(PinName tx, PinName rx) : _serial(tx, rx) { + _serial.baud(9600); + + _state = StateStart; + _sentenceMask = 0; + _availDataType = NmeaInvalid; + memset(&gga, 0, sizeof(GgaType)); + memset(&vtg, 0, sizeof(VtgType)); +} + +void MTK3339::start(void (*fptr)(void), int mask) { + if (fptr && mask) { + _dataCallback.attach(fptr); + _sentenceMask = mask; + _serial.attach(this, &MTK3339::uartIrq, Serial::RxIrq); + } +} + +void MTK3339::stop() { + _dataCallback.attach(NULL); + _sentenceMask = 0; + _serial.attach(NULL); +} + +MTK3339::NmeaSentence MTK3339::getAvailableDataType() { + return _availDataType; +} + +double MTK3339::getLatitudeAsDegrees() { + if(gga.fix == 0 || gga.nsIndicator == 0) return 0; + + double l = gga.latitude; + char ns = gga.nsIndicator; + + // convert from ddmm.mmmm to degrees only + // 60 minutes is 1 degree + + int deg = (int)(l / 100); + l = (l - deg*100.0) / 60.0; + l = deg + l; + if (ns == 'S') l = -l; + + return l; +} + +double MTK3339::getLongitudeAsDegrees() { + if(gga.fix == 0 || gga.ewIndicator == 0) return 0; + + double l = gga.longitude; + char ew = gga.ewIndicator; + + // convert from ddmm.mmmm to degrees only + // 60 minutes is 1 degree + + int deg = (int)(l / 100); + l = (l - deg*100) / 60; + l = deg + l; + if (ew == 'W') l = -l; + + return l; +} + +void MTK3339::parseGGA(char* data, int dataLen) { + //http://aprs.gids.nl/nmea/#gga + + double tm = 0; + + memset(&gga, 0, sizeof(GgaType)); + + char* p = data; + int pos = 0; + + p = strchr(p, ','); + while (p != NULL && *p != 0) { + p++; + + switch(pos) { + case 0: // time: hhmmss.sss + tm = strtod(p, NULL); + gga.hours = (int)(tm / 10000); + gga.minutes = ((int)tm % 10000) / 100; + gga.seconds = ((int)tm % 100); + gga.milliseconds = (int)(tm * 1000) % 1000; + break; + case 1: // latitude: ddmm.mmmm + gga.latitude = strtod(p, NULL); + break; + case 2: // N/S indicator (north or south) + if (*p == 'N' || *p == 'S') { + gga.nsIndicator = *p; + } + break; + case 3: // longitude: dddmm.mmmm + gga.longitude = strtod(p, NULL); + break; + case 4: // E/W indicator (east or west) + if (*p == 'E' || *p == 'W') { + gga.ewIndicator = *p; + } + break; + case 5: // position indicator (1=no fix, 2=GPS fix, 3=Differential) + gga.fix = strtol(p, NULL, 10); + break; + case 6: // num satellites + gga.satellites = strtol(p, NULL, 10); + break; + case 7: // hdop + gga.hdop = strtod(p, NULL); + break; + case 8: // altitude + gga.altitude = strtod(p, NULL); + break; + case 9: // units + // ignore units + break; + case 10: // geoidal separation + gga.geoidal = strtod(p, NULL); + break; + } + pos++; + + p = strchr(p, ','); + } + +} + +void MTK3339::parseVTG(char* data, int dataLen) { + + + char* p = data; + int pos = 0; + + memset(&vtg, 0, sizeof(VtgType)); + + p = strchr(p, ','); + while (p != NULL && *p != 0) { + p++; + + switch(pos) { + case 0: // course in degrees + vtg.course = strtod(p, NULL); + break; + case 1: // Reference (T) + break; + case 2: // course magnetic (need customization) + break; + case 3: // reference (M) + break; + case 4: // speed in knots + vtg.speedKnots = strtod(p, NULL); + break; + case 5: // units (N) + break; + case 6: // speed in Km/h + vtg.speedKmHour = strtod(p, NULL); + break; + case 7: // units (K) + break; + case 8: // mode + if (*p == 'A' || *p == 'D' || *p == 'E') { + vtg.mode = *p; + } + + break; + + } + + pos++; + + p = strchr(p, ','); + } +} + + +void MTK3339::parseData(char* data, int len) { + do { + + // verify checksum + if (len < 3 || (len > 3 && data[len-3] != '*')) { + // invalid data + break; + } + int sum = strtol(&data[len-2], NULL, 16); + for(int i = 1; i < len-3; i++) { + sum ^= data[i]; + } + if (sum != 0) { + // invalid checksum + break; + } + + + if (strncmp("$GPGGA", data, 6) == 0 && (_sentenceMask & NmeaGga) != 0) { + parseGGA(data, len); + _availDataType = NmeaGga; + _dataCallback.call(); + _availDataType = NmeaInvalid; + } + else if (strncmp("$GPVTG", data, 6) == 0 && (_sentenceMask & NmeaVtg) != 0) { + parseVTG(data, len); + _availDataType = NmeaVtg; + _dataCallback.call(); + _availDataType = NmeaInvalid; + } + + + } while(0); +} + +void MTK3339::uartIrq() { + char d = 0; + + while(_serial.readable()) { + d = _serial.getc(); + + switch(_state) { + case StateStart: + if (d == '$') { + _buf[0] = '$'; + _bufPos = 1; + _state = StateData; + } + break; + case StateData: + if (_bufPos >= MTK3339_BUF_SZ) { + // error + _state = StateStart; + } + else if (d == '\r') { + + _buf[_bufPos] = 0; + + parseData(_buf, _bufPos); + + _state = StateStart; + } + else { + _buf[_bufPos++] = d; + } + + break; + } + } +}
diff -r 000000000000 -r bd0fe2412980 MTK3339.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MTK3339.h Thu Nov 07 11:46:53 2013 +0000 @@ -0,0 +1,172 @@ +#ifndef MTK3339_H +#define MTK3339_H + +/** + * An interface to the MTK3339 GPS module. + */ +class MTK3339 { +public: + + + enum NmeaSentence { + NmeaInvalid = 0, + NmeaGga = 0x01, +// NmeaGsa = 0x02, +// NmeaGsv = 0x04, +// NmeaRmc = 0x08, + NmeaVtg = 0x10 + }; + + struct GgaType { + /** UTC time - hours */ + int hours; + /** UTC time - minutes */ + int minutes; + /** UTC time - seconds */ + int seconds; + /** UTC time - milliseconds */ + int milliseconds; + + /** The latitude in ddmm.mmmm format (d = degrees, m = minutes) */ + double latitude; + /** The longitude in dddmm.mmmm format */ + double longitude; + /** North / South indicator */ + char nsIndicator; + /** East / West indicator */ + char ewIndicator; + + /** + * Position indicator: + * 0 = Fix not available + * 1 = GPS fix + * 2 = Differential GPS fix + */ + int fix; + + /** Number of used satellites */ + int satellites; + /** Horizontal Dilution of Precision */ + double hdop; + /** antenna altitude above/below mean sea-level */ + double altitude; + /** geoidal separation */ + double geoidal; + }; + + struct VtgType { + /** heading in degrees */ + double course; + /** speed in Knots */ + double speedKnots; + /** Speed in kilometer per hour */ + double speedKmHour; + /** + * Mode + * A = Autonomous mode + * D = Differential mode + * E = Estimated mode + */ + char mode; + }; + + /** + * Create an interface to the MTK3339 GPS module + * + * @param tx UART TX line pin + * @param rx UART RX line pin + */ + MTK3339(PinName tx, PinName rx); + + /** + * Start to read data from the GPS module. + * + * @param fptr A pointer to a void function that will be called when there + * is data available. + * @param mask specifies which sentence types (NmeaSentence) that are of + * interest. The callback function will only be called for messages + * specified in this mask. + */ + void start(void (*fptr)(void), int mask); + + /** + * Start to read data from the GPS module. + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + * @param mask specifies which sentence types (NmeaSentence) that are of + * interest. The member function will only be called for messages + * specified in this mask. + */ + template<typename T> + void start(T* tptr, void (T::*mptr)(void), int mask) { + if((mptr != NULL) && (tptr != NULL) && mask) { + _dataCallback.attach(tptr, mptr); + _sentenceMask = mask; + _serial.attach(this, &MTK3339::uartIrq, Serial::RxIrq); + } + } + + /** + * Stop to read data from GPS module + */ + void stop(); + + /** + * Get the type of the data reported in available data callback. + * This method will only return a valid type when called within the + * callback. + */ + NmeaSentence getAvailableDataType(); + + /** + * Get latitude in degrees (decimal format) + */ + double getLatitudeAsDegrees(); + /** + * Get longitude in degrees (decimal format) + */ + double getLongitudeAsDegrees(); + + /** + * Time, position and fix related data + */ + GgaType gga; + + /** + * Course and speed information relative to ground + */ + VtgType vtg; + + +private: + + enum PrivConstants { + MTK3339_BUF_SZ = 255 + }; + + enum DataState { + StateStart = 0, + StateData + }; + + FunctionPointer _dataCallback; + char _buf[MTK3339_BUF_SZ]; + int _bufPos; + DataState _state; + int _sentenceMask; + NmeaSentence _availDataType; + + Serial _serial; + + + void parseGGA(char* data, int dataLen); + void parseVTG(char* data, int dataLen); + void parseData(char* data, int len); + void uartIrq(); + + +}; + +#endif +