Revised Embedded Artists' MTK3339 library to save all raw GPS messages and enable optional decoding of RMC message.

Fork of MTK3339 by EmbeddedArtists AB

MTK3339.cpp

Committer:
embeddedartists
Date:
2013-11-07
Revision:
0:bd0fe2412980
Child:
1:3057ad6a5d4b

File content as of revision 0:bd0fe2412980:


#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;
        }
    }
}