Mbed library for Quectel L80 GPS module

Files at this revision

API Documentation at this revision

Comitter:
biagiomkr
Date:
Fri Apr 12 17:00:42 2019 +0000
Commit message:
First release of Mbed library for Quectel L80

Changed in this revision

Quectel_L80_GPS.cpp Show annotated file Show diff for this revision Revisions of this file
Quectel_L80_GPS.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Quectel_L80_GPS.cpp	Fri Apr 12 17:00:42 2019 +0000
@@ -0,0 +1,652 @@
+/***********************************************************************
+This is the Quectel L80 GPS library.
+It is based on the Adafruit GPS library and has been adapted in order to
+work with Mbed OS.
+
+Initially written by Limor Fried/Ladyada for Adafruit Industries.
+Modified by Biagio Montaruli <biagio.hkr@gmail.com>
+BSD license, check license.txt for more information
+All text above must be included in any redistribution
+************************************************************************/
+
+/* Includes ------------------------------------------------------------------*/
+#include "mbed.h"
+#include <ctype.h>
+#include "Quectel_L80_GPS.h"
+
+/* Constructor */
+Quectel_L80_GPS::Quectel_L80_GPS(Serial *ser)
+{
+    gpsHwSerial = ser;
+
+    recvdflag   = false;
+    paused      = false;
+    lineidx     = 0;
+    currentline = line1;
+    lastline    = line2;
+    inFullOnMode = true;
+    inStandbyMode = false;
+    inAlwaysLocateMode = false;
+
+    hours = minutes = seconds = milliseconds = 0;
+    year = month = day = 0;
+    fixQuality = satellites = 0;
+    lat = lon = mag = 0;
+    fix = false;
+    latitude = longitude = altitude = 0.0;
+    geoidheight = speed = angle = magvariation = HDOP = 0.0;
+
+    LOCUS_serial = LOCUS_records = 0;
+    LOCUS_type = LOCUS_mode = LOCUS_config = LOCUS_interval = 0;
+    LOCUS_distance = LOCUS_speed = LOCUS_status = LOCUS_percent = 0;
+}
+
+bool Quectel_L80_GPS::parse(char *nmea)
+{
+    if (nmea[strlen(nmea) - 4] == '*') {
+        uint16_t sum = parseHex(nmea[strlen(nmea) - 3]) * 16;
+        sum += parseHex(nmea[strlen(nmea) - 2]);
+
+        /* compute checksum */
+        for (uint8_t i = 2; i < (strlen(nmea) - 4); i++) {
+            sum ^= nmea[i];
+        }
+        if (sum != 0) {
+            /* bad checksum */
+            return false;
+        }
+    }
+    int32_t degree;
+    long min;
+    char degreebuff[10];
+    /* look for a few common sentences */
+    if (strstr(nmea, "$GPGGA")) {
+        /* found GGA */
+        char *p = nmea;
+        /* get time */
+        p = strchr(p, ',') + 1;
+        double timef = atof(p);
+        uint32_t time = timef;
+        hours = time / 10000;
+        minutes = (time % 10000) / 100;
+        seconds = (time % 100);
+
+        milliseconds = fmod(timef, 1.0) * 1000;
+
+        /* parse out latitude */
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            strncpy(degreebuff, p, 2);
+            p += 2;
+            degreebuff[2] = '\0';
+            degree = atol(degreebuff) * 10000000;
+            strncpy(degreebuff, p, 2);
+            p += 3; /* skip decimal point */
+            strncpy(degreebuff + 2, p, 4);
+            degreebuff[6] = '\0';
+            min = 50 * atol(degreebuff) / 3;
+            latitude_fixed = degree + min;
+            latitude = degree / 100000 + min * 0.000006F;
+            latitudeDegrees = (latitude - 100 * int(latitude / 100)) / 60.0;
+            latitudeDegrees += int(latitude / 100);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            if (p[0] == 'S') latitudeDegrees *= -1.0;
+            if (p[0] == 'N') lat = 'N';
+            else if (p[0] == 'S') lat = 'S';
+            else if (p[0] == ',') lat = 0;
+            else return false;
+        }
+
+        /* parse out longitude */
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            strncpy(degreebuff, p, 3);
+            p += 3;
+            degreebuff[3] = '\0';
+            degree = atol(degreebuff) * 10000000;
+            strncpy(degreebuff, p, 2);
+            p += 3; /* skip decimal point */
+            strncpy(degreebuff + 2, p, 4);
+            degreebuff[6] = '\0';
+            min = 50 * atol(degreebuff) / 3;
+            longitude_fixed = degree + min;
+            longitude = degree / 100000 + min * 0.000006F;
+            longitudeDegrees = (longitude - 100 * int(longitude / 100)) / 60.0;
+            longitudeDegrees += int(longitude / 100);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            if (p[0] == 'W') longitudeDegrees *= -1.0;
+            if (p[0] == 'W') lon = 'W';
+            else if (p[0] == 'E') lon = 'E';
+            else if (p[0] == ',') lon = 0;
+            else return false;
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            fixQuality = atoi(p);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            satellites = atoi(p);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            HDOP = atof(p);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            altitude = atof(p);
+        }
+
+        p = strchr(p, ',') + 1;
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            geoidheight = atof(p);
+        }
+        return true;
+    }
+    if (strstr(nmea, "$GPRMC")) {
+        /* found RMC */
+        char *p = nmea;
+
+        /* get time */
+        p = strchr(p, ',') + 1;
+        double timef = atof(p);
+        uint32_t time = timef;
+        hours = time / 10000;
+        minutes = (time % 10000) / 100;
+        seconds = (time % 100);
+
+        milliseconds = fmod(timef, 1.0) * 1000;
+
+        p = strchr(p, ',') + 1;
+        if (p[0] == 'A')
+            fix = true;
+        else if (p[0] == 'V')
+            fix = false;
+        else
+            return false;
+
+        /* parse out latitude */
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            strncpy(degreebuff, p, 2);
+            p += 2;
+            degreebuff[2] = '\0';
+            long degree = atol(degreebuff) * 10000000;
+            strncpy(degreebuff, p, 2);
+            p += 3; /* skip decimal point */
+            strncpy(degreebuff + 2, p, 4);
+            degreebuff[6] = '\0';
+            min = 50 * atol(degreebuff) / 3;
+            latitude_fixed = degree + min;
+            latitude = degree / 100000 + min * 0.000006F;
+            latitudeDegrees = (latitude - 100 * int(latitude / 100)) / 60.0;
+            latitudeDegrees += int(latitude / 100);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            if (p[0] == 'S') latitudeDegrees *= -1.0;
+            if (p[0] == 'N') lat = 'N';
+            else if (p[0] == 'S') lat = 'S';
+            else if (p[0] == ',') lat = 0;
+            else return false;
+        }
+
+        /* parse out longitude */
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            strncpy(degreebuff, p, 3);
+            p += 3;
+            degreebuff[3] = '\0';
+            degree = atol(degreebuff) * 10000000;
+            strncpy(degreebuff, p, 2);
+            p += 3; /* skip decimal point */
+            strncpy(degreebuff + 2, p, 4);
+            degreebuff[6] = '\0';
+            min = 50 * atol(degreebuff) / 3;
+            longitude_fixed = degree + min;
+            longitude = degree / 100000 + minutes * 0.000006F;
+            longitudeDegrees = (longitude - 100 * int(longitude / 100)) / 60.0;
+            longitudeDegrees += int(longitude / 100);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            if (p[0] == 'W') longitudeDegrees *= -1.0;
+            if (p[0] == 'W') lon = 'W';
+            else if (p[0] == 'E') lon = 'E';
+            else if (p[0] == ',') lon = 0;
+            else return false;
+        }
+        /* speed */
+        p = strchr(p, ',')+1;
+        if (',' != *p) {
+            speed = atof(p);
+        }
+
+        /* angle */
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            angle = atof(p);
+        }
+
+        p = strchr(p, ',') + 1;
+        if (',' != *p) {
+            uint32_t fulldate = atof(p);
+            day = fulldate / 10000;
+            month = (fulldate % 10000) / 100;
+            year = (fulldate % 100);
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+char Quectel_L80_GPS::read(void)
+{
+    char c = 0;
+
+    if (paused) return c;
+
+    if(!gpsHwSerial->readable()) return c;
+
+    c = gpsHwSerial->getc();
+#ifdef SERIAL_DEBUG
+    pcSerial.print(c);
+#endif
+
+    if (c == '\n') {
+        currentline[lineidx] = 0;
+
+        if (currentline == line1) {
+            currentline = line2;
+            lastline = line1;
+        } else {
+            currentline = line1;
+            lastline = line2;
+        }
+
+#ifdef SERIAL_DEBUG
+        pcSerial.println("----");
+        pcSerial.println((char *)lastline);
+        pcSerial.println("----");
+#endif
+        lineidx = 0;
+        recvdflag = true;
+    }
+
+    currentline[lineidx++] = c;
+    if (lineidx >= MAXLINELENGTH)
+        lineidx = MAXLINELENGTH-1;
+
+    return c;
+}
+
+void Quectel_L80_GPS::sendCommand(const char *str)
+{
+    gpsHwSerial->printf("%s\n", str);
+}
+
+bool Quectel_L80_GPS::newNMEAreceived(void)
+{
+    return recvdflag;
+}
+
+void Quectel_L80_GPS::pause(bool p)
+{
+    paused = p;
+}
+
+char *Quectel_L80_GPS::lastNMEA(void)
+{
+    recvdflag = false;
+    return (char *)lastline;
+}
+
+/* read a Hex value and return the decimal equivalent */
+uint8_t Quectel_L80_GPS::parseHex(char c)
+{
+    if (c < '0')
+        return 0;
+    if (c <= '9')
+        return c - '0';
+    if (c < 'A')
+        return 0;
+    if (c <= 'F')
+        return (c - 'A') + 10;
+    // if (c > 'F')
+    return 0;
+}
+
+bool Quectel_L80_GPS::waitForSentence(const char *waitstr, uint8_t max)
+{
+    char str[20];
+
+    uint8_t i = 0;
+    while (i < max) {
+        read();
+
+        if (newNMEAreceived()) {
+            char *nmea = lastNMEA();
+            strncpy(str, nmea, 20);
+            str[19] = 0;
+            i++;
+
+            if (strstr(str, waitstr))
+                return true;
+        }
+    }
+
+    return false;
+}
+
+uint8_t Quectel_L80_GPS::getHour(void)
+{
+    return hours;
+}
+
+uint8_t Quectel_L80_GPS::getMinutes(void)
+{
+    return minutes;
+}
+
+uint8_t Quectel_L80_GPS::getSeconds(void)
+{
+    return seconds;
+}
+
+uint8_t Quectel_L80_GPS::getMilliseconds(void)
+{
+    return milliseconds;
+}
+
+uint8_t Quectel_L80_GPS::getDay(void)
+{
+    return day;
+}
+
+uint8_t Quectel_L80_GPS::getMonth(void)
+{
+    return month;
+}
+
+uint8_t Quectel_L80_GPS::getYear(void)
+{
+    return year;
+}
+
+float Quectel_L80_GPS::getLatitude(void)
+{
+    return latitude;
+}
+
+float Quectel_L80_GPS::getLongitude(void)
+{
+    return longitude;
+}
+
+int32_t Quectel_L80_GPS::getLatitudeFixed(void)
+{
+    return latitude_fixed;
+}
+
+int32_t Quectel_L80_GPS::getLongitudeFixed(void)
+{
+    return longitude_fixed;
+}
+
+float Quectel_L80_GPS::getLatitudeDegrees(void)
+{
+    return latitudeDegrees;
+}
+
+float Quectel_L80_GPS::getLongitudeDegrees(void)
+{
+    return longitudeDegrees;
+}
+
+float Quectel_L80_GPS::getAltitude(void)
+{
+    return altitude;
+}
+
+float Quectel_L80_GPS::getGeoidheight(void)
+{
+    return geoidheight;
+}
+
+float Quectel_L80_GPS::getSpeed(void)
+{
+    return speed;
+}
+
+float Quectel_L80_GPS::getAngle(void)
+{
+    return angle;
+}
+
+float Quectel_L80_GPS::getMagVariation(void)
+{
+    return magvariation;
+}
+
+float Quectel_L80_GPS::getHDOP(void)
+{
+    return HDOP;
+}
+
+char Quectel_L80_GPS::getLatCardinalDir(void)
+{
+    return lat;
+}
+
+char Quectel_L80_GPS::getLonCardinalDir(void)
+{
+    return lon;
+}
+
+char Quectel_L80_GPS::getMagCardinalDir(void)
+{
+    return mag;
+}
+
+bool Quectel_L80_GPS::isFixed(void)
+{
+    return fix;
+}
+
+uint8_t Quectel_L80_GPS::getQuality(void)
+{
+    return fixQuality;
+}
+
+uint8_t Quectel_L80_GPS::getSatellites(void)
+{
+    return satellites;
+}
+
+bool Quectel_L80_GPS::LOCUS_StartLogger(void)
+{
+    sendCommand(PMTK_LOCUS_STARTLOG);
+    recvdflag = false;
+    return waitForSentence(PMTK_LOCUS_STARTSTOPACK);
+}
+
+bool Quectel_L80_GPS::LOCUS_StopLogger(void)
+{
+    sendCommand(PMTK_LOCUS_STOPLOG);
+    recvdflag = false;
+    return waitForSentence(PMTK_LOCUS_STARTSTOPACK);
+}
+
+bool Quectel_L80_GPS::LOCUS_ReadStatus(void)
+{
+    sendCommand(PMTK_LOCUS_QUERY_STATUS);
+
+    if (!waitForSentence("$PMTKLOG"))
+        return false;
+
+    char *response = lastNMEA();
+    uint16_t parsed[10];
+    uint8_t i;
+
+    for (i = 0; i < 10; i++) {
+        parsed[i] = -1;
+    }
+
+    response = strchr(response, ',');
+    for (i = 0; i < 10; i++) {
+        if (!response || (response[0] == 0) || (response[0] == '*'))
+            break;
+        response++;
+        parsed[i] = 0;
+        while ((response[0] != ',') && (response[0] != '*') && (response[0] != 0)) {
+            parsed[i] *= 10;
+            char c = response[0];
+            if (isdigit(c))
+                parsed[i] += c - '0';
+            else
+                parsed[i] = c;
+            response++;
+        }
+    }
+    LOCUS_serial = parsed[0];
+    LOCUS_type = parsed[1];
+    if (isalpha(parsed[2])) {
+        parsed[2] = parsed[2] - 'a' + 10;
+    }
+    LOCUS_mode = parsed[2];
+    LOCUS_config = parsed[3];
+    LOCUS_interval = parsed[4];
+    LOCUS_distance = parsed[5];
+    LOCUS_speed = parsed[6];
+    LOCUS_status = !parsed[7];
+    LOCUS_records = parsed[8];
+    LOCUS_percent = parsed[9];
+
+    return true;
+}
+
+uint16_t Quectel_L80_GPS::LOCUS_GetSerial()
+{
+    return LOCUS_serial;
+}
+
+uint16_t Quectel_L80_GPS::LOCUS_GetRecords()
+{
+    return LOCUS_records;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetType()
+{
+    return LOCUS_type;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetMode()
+{
+    return LOCUS_mode;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetConfig()
+{
+    return LOCUS_config;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetInterval()
+{
+    return LOCUS_interval;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetDistance()
+{
+    return LOCUS_distance;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetSpeed()
+{
+    return LOCUS_speed;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetStatus()
+{
+    return LOCUS_status;
+}
+
+uint8_t Quectel_L80_GPS::LOCUS_GetPercent()
+{
+    return LOCUS_percent;
+}
+
+bool Quectel_L80_GPS::setStandbyMode(void)
+{
+    if (inStandbyMode) {
+        /* Returns false if already in standby mode */
+        return false;
+    } else {
+        inStandbyMode = true;
+        inFullOnMode = false;
+        inAlwaysLocateMode = false;
+        recvdflag = false;
+        sendCommand(PMTK_STANDBY);
+        return waitForSentence(PMTK_STANDBY_SUCCESS);
+    }
+}
+
+bool Quectel_L80_GPS::wakeupStandby(void)
+{
+    if (inStandbyMode) {
+        inStandbyMode = false;
+        inFullOnMode = true;
+        inAlwaysLocateMode = false;
+        recvdflag = false;
+        sendCommand("");  /* send byte to wake it up */
+        return waitForSentence(PMTK_AWAKE);
+    } else {
+        /* Returns false if not in standby mode, nothing to wakeup */
+        return false;
+    }
+}
+
+/* AlwaysLocate Mode APIs: */
+bool Quectel_L80_GPS::setAlwaysLocateMode(void)
+{
+    if (inAlwaysLocateMode) {
+        /* Returns false if already in AlwayLocate mode,
+         * so that you do not wake it up by sending commands to GPS */
+        return false;
+    } else {
+        inStandbyMode = false;
+        inFullOnMode = false;
+        inAlwaysLocateMode = true;
+        recvdflag = false;
+        sendCommand(PMTK_ALWAYSLOCATE_STANDBY);
+        return waitForSentence(PMTK_ALWAYSLOCATE_STANDBY_SUCCESS);
+    }
+}
+
+bool Quectel_L80_GPS::wakeupAlwaysLocate(void)
+{
+    if (inAlwaysLocateMode) {
+        inStandbyMode = false;
+        inFullOnMode = true;
+        inAlwaysLocateMode = false;
+        sendCommand(PMTK_ALWAYSLOCATE_EXIT);
+        return true;
+    } else {
+        /* Returns false if not in AlwaysLocate mode, nothing to wakeup */
+        return false;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Quectel_L80_GPS.h	Fri Apr 12 17:00:42 2019 +0000
@@ -0,0 +1,222 @@
+/***********************************************************************
+This is the Quectel L80 GPS library.
+It is based on the Adafruit GPS library and has been adapted in order to
+work with Mbed OS.
+
+Initially written by Limor Fried/Ladyada for Adafruit Industries.
+Modified by Biagio Montaruli <biagio.hkr@gmail.com>
+BSD license, check license.txt for more information
+All text above must be included in any redistribution
+************************************************************************/
+
+#ifndef __QUECTEL_L80_GPS_H
+#define __QUECTEL_L80_GPS_H
+
+/* *
+ * Commands to set the update rate from once a second (1 Hz) to
+ * 10 times a second (10Hz). Note that these only control the rate at which the
+ * position is echoed, to actually speed up the position fix you must also send
+ * one of the position fix rate commands below too.
+ */
+#define PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ  "$PMTK220,10000*2F"
+#define PMTK_SET_NMEA_UPDATE_200_MILLIHERTZ  "$PMTK220,5000*1B"
+#define PMTK_SET_NMEA_UPDATE_1HZ  "$PMTK220,1000*1F"
+#define PMTK_SET_NMEA_UPDATE_2HZ  "$PMTK220,500*2B"
+#define PMTK_SET_NMEA_UPDATE_5HZ  "$PMTK220,200*2C"
+#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F"
+/* Position fix update rate commands. */
+#define PMTK_API_SET_FIX_CTL_100_MILLIHERTZ  "$PMTK300,10000,0,0,0,0*2C"
+#define PMTK_API_SET_FIX_CTL_200_MILLIHERTZ  "$PMTK300,5000,0,0,0,0*18"
+#define PMTK_API_SET_FIX_CTL_1HZ  "$PMTK300,1000,0,0,0,0*1C"
+#define PMTK_API_SET_FIX_CTL_5HZ  "$PMTK300,200,0,0,0,0*2F"
+
+#define PMTK_SET_BAUD_57600 "$PMTK251,57600*2C"
+#define PMTK_SET_BAUD_9600 "$PMTK251,9600*17"
+
+/* turn on only the second sentence (GPRMC) */
+#define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"
+/* turn on GPRMC and GGA */
+#define PMTK_SET_NMEA_OUTPUT_RMCGGA "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
+/* turn on ALL THE DATA */
+#define PMTK_SET_NMEA_OUTPUT_ALLDATA "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
+/* turn off output */
+#define PMTK_SET_NMEA_OUTPUT_OFF "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
+
+/*
+ * NOTE: to generate your own sentences, check out the MTK command datasheet and
+ * use a checksum calculator
+ */
+
+#define PMTK_LOCUS_STARTLOG "$PMTK185,0*22"
+#define PMTK_LOCUS_STOPLOG  "$PMTK185,1*23"
+#define PMTK_LOCUS_STARTSTOPACK "$PMTK001,185,3*3C"
+#define PMTK_LOCUS_QUERY_STATUS "$PMTK183*38"
+#define PMTK_LOCUS_ERASE_FLASH  "$PMTK184,1*22"
+#define LOCUS_OVERLAP   0
+#define LOCUS_FULLSTOP  1
+
+#define PMTK_ENABLE_SBAS    "$PMTK313,1*2E"
+#define PMTK_ENABLE_WAAS    "$PMTK301,2*2E"
+
+/* Standby mode command & boot successful message */
+#define PMTK_STANDBY    "$PMTK161,0*28"
+#define PMTK_STANDBY_SUCCESS    "$PMTK001,161,3*36"
+#define PMTK_AWAKE  "$PMTK010,002*2D"
+
+/* *
+ * Backup mode commands:
+ * keeping GPS_EN signal low and sending PMTK command "$PMTK225,4*2F" will make
+ * L80 module enter into backup mode forever. To wake the GPS module up you must
+ * pull the GPS_EN signal high.
+ */
+#define PMTK_BACKUP "$PMTK225,4*2F"
+
+/* AlwaysLocate mode commands */
+/* AlwaysLocate standby commands: */
+#define PMTK_ALWAYSLOCATE_STANDBY   "$PMTK225,8*23"
+#define PMTK_ALWAYSLOCATE_STANDBY_SUCCESS   "$PMTK001,225,3*35"
+/* AlwaysLocate backup command: */
+#define PMTK_ALWAYSLOCATE_BACKUP    "$PMTK225,9*22"
+/* AlwaysLocate exit command: the gps module returns into full on mode */
+#define PMTK_ALWAYSLOCATE_EXIT  "$PMTK225,0*2B"
+
+/* EASY functions: */
+#define PMTK_EASY_ENABLE    "$PMTK869,1,1*35"
+#define PMTK_EASY_DISABLE   "$PMTK869,1,0*34"
+#define PMTK_EASY_CHECK_STATUS  "$PMTK869,0*29"
+#define PMTK_EASY_STATUS_ENABLED    "$PMTK869,2,1*36"
+#define PMTK_EASY_STATUS_DISABLED   "$PMTK869,2,0*37"
+
+/* Get the release and version: */
+#define PMTK_Q_RELEASE "$PMTK605*31"
+
+/* Request for updates on antenna status */
+#define PGCMD_ANTENNA "$PGCMD,33,1*6C"
+#define PGCMD_NOANTENNA "$PGCMD,33,0*6D"
+
+/* AIC function: */
+#define PMTK_AIC_ENABLE "$PMTK286,1*23"
+#define PMTK_AIC_DISABLE "$PMTK286,0*22"
+
+/* Maximum timeout for a response */
+#define MAXWAITSENTENCE 10
+
+/* Maximum length of NMEA senstences */
+#define MAXLINELENGTH 120
+
+#ifdef SERIAL_DEBUG
+Serial pcSerial(USBTX, USBRX);
+#endif
+
+class Quectel_L80_GPS
+{
+public:
+    Quectel_L80_GPS(Serial *ser);
+
+    char *lastNMEA(void);
+    bool newNMEAreceived();
+    void common_init(void);
+
+    void sendCommand(const char *);
+
+    void pause(bool b);
+
+    bool parseNMEA(char *response);
+    uint8_t parseHex(char c);
+
+    char read(void);
+    bool parse(char *);
+
+    uint8_t getHour(void);
+    uint8_t getMinutes(void);
+    uint8_t getSeconds(void);
+    uint8_t getMilliseconds(void);
+
+    uint8_t getYear(void);
+    uint8_t getMonth(void);
+    uint8_t getDay(void);
+
+    float getLatitude(void);
+    float getLongitude(void);
+    int32_t getLatitudeFixed(void);
+    int32_t getLongitudeFixed(void);
+    float getLatitudeDegrees(void);
+    float getLongitudeDegrees(void);
+    float getAltitude(void);
+    float getGeoidheight(void);
+    float getSpeed(void);
+    float getAngle(void);
+    float getMagVariation(void);
+    float getHDOP(void);
+
+    char getLatCardinalDir(void);
+    char getLonCardinalDir(void);
+    char getMagCardinalDir(void);
+
+    bool isFixed(void);
+    uint8_t getQuality(void);
+
+    uint8_t getSatellites(void);
+
+    bool wakeupStandby(void);
+    bool setStandbyMode(void);
+
+    bool setAlwaysLocateMode(void);
+    bool wakeupAlwaysLocate(void);
+
+    bool waitForSentence(const char *wait, uint8_t max = MAXWAITSENTENCE);
+    bool LOCUS_StartLogger(void);
+    bool LOCUS_StopLogger(void);
+    bool LOCUS_ReadStatus(void);
+
+    uint16_t LOCUS_GetSerial();
+    uint16_t LOCUS_GetRecords();
+    uint8_t LOCUS_GetType();
+    uint8_t LOCUS_GetMode();
+    uint8_t LOCUS_GetConfig();
+    uint8_t LOCUS_GetInterval();
+    uint8_t LOCUS_GetDistance();
+    uint8_t LOCUS_GetSpeed();
+    uint8_t LOCUS_GetStatus();
+    uint8_t LOCUS_GetPercent();
+
+private:
+    Serial *gpsHwSerial;
+    
+    bool paused;
+    bool inStandbyMode;
+    bool inFullOnMode;
+    bool inAlwaysLocateMode;
+
+    bool recvdflag;
+    /* use two buffer: read one line in and leave one for the main program */
+    char line1[MAXLINELENGTH];
+    char line2[MAXLINELENGTH];
+    /* index into the current line */
+    uint8_t lineidx;
+    /* pointers to the double buffers */
+    char *currentline;
+    char *lastline;
+
+    uint8_t hours, minutes, seconds, year, month, day;
+    uint16_t milliseconds;
+    /* floating point latitude and longitude value in degrees */
+    float latitude, longitude;
+    /* Fixed point latitude and longitude value with degrees stored in units of
+     * 1/100000 degrees, and minutes stored in units of 1/100000 degrees.
+     */
+    int32_t latitude_fixed, longitude_fixed;
+    float latitudeDegrees, longitudeDegrees;
+    float geoidheight, altitude;
+    float speed, angle, magvariation, HDOP;
+    char lat, lon, mag;
+    bool fix;
+    uint8_t fixQuality, satellites;
+
+    uint16_t LOCUS_serial, LOCUS_records;
+    uint8_t LOCUS_type, LOCUS_mode, LOCUS_config, LOCUS_interval, LOCUS_distance,
+            LOCUS_speed, LOCUS_status, LOCUS_percent;
+    uint8_t parseResponse(char *response);
+};
+
+#endif
\ No newline at end of file