2018 revision to classic DataBus AVC code.

Dependencies:   LSM303DLM Servo SerialGraphicLCD L3G4200D IncrementalEncoder SimpleShell

Files at this revision

API Documentation at this revision

Comitter:
shimniok
Date:
Mon Jan 07 16:47:33 2019 +0000
Parent:
43:9a285515f33a
Commit message:
Rewrote TinyGPS -> NMEA, GPS::read() now returns struct

Changed in this revision

GPS/GPS.h Show annotated file Show diff for this revision Revisions of this file
GPS/NMEA.cpp Show annotated file Show diff for this revision Revisions of this file
GPS/NMEA.h Show annotated file Show diff for this revision Revisions of this file
GPS/TinyGPS.cpp Show diff for this revision Revisions of this file
GPS/TinyGPS.h Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/GPS/GPS.h	Thu Jan 03 19:07:20 2019 +0000
+++ b/GPS/GPS.h	Mon Jan 07 16:47:33 2019 +0000
@@ -1,48 +1,19 @@
 #ifndef __GPS_H
 #define __GPS_H
 
-/** uBlox GPS UBX Protocol Reader
- * Parses uBlox GPS binary protocol
- * 
- * @author Wayne Holder; Ported to mbed by Michael Shimniok
+/** GPS interface class for character-at-a-time parsing of GPS protocols
+ * @author Michael Shimniok
  */
-#include "mbed.h"
-
 class GPS {
 public:
-    GPS(): _callback(0) {}
-
-    /**
-     * Parse one character of protocol
-     * @param c is the character to parse
-     * @return 1 when entire packet of data obtained, 0 otherwise
-     */    
-    virtual int parse(char c) = 0;
-
-    void subscribe(Callback<void()> cb) {
-        _callback = cb;
-    }
-
-    /** Read the latest data from GPS
-     * @param lat latitude in degrees
-     * @param lon longitude in degrees
-     * @param course heading/course in degrees
-     * @param speed speed m/s
-     * @param hdop horizontal dilution of precision
-     * @param svcount is the number of sattelites being tracked
-     * @return all parameters
-     */
-    void read(double& lat, double& lon, float& course, float& speed, float& hdop, int& svcount) {
-        lat = latest.lat;
-        lon = latest.lon;
-        course = latest.course;
-        speed = latest.speed;
-        hdop = latest.hdop;
-        svcount = latest.svcount;
-    }   
-
-protected:
+    /// struct containing year, month, date, hour, minute, second, lat, lon, course, speed, hdop, svcount
     typedef struct {
+        int year;
+        int month;
+        int date;
+        int hour;
+        int minute;
+        int second;
         double lat;
         double lon;
         float course;
@@ -51,10 +22,38 @@
         int svcount;
     } gps_data_t;
 
+#ifdef __MBED__
+    GPS(): _callback(0) {}
+#else
+    GPS() {}
+#endif
+
+    /** Parse one character of protocol
+     * @param c is the character to parse
+     * @return 1 when entire packet of data obtained, 0 otherwise
+     */
+    virtual int parse(char c) = 0;
+
+#ifdef __MBED__
+    void subscribe(Callback<void()> cb) {
+        _callback = cb;
+    }
+#endif
+
+    /** Read the latest data from GPS
+     * @returns gps_data_t struct with date, time, position, course, speed, hdop, and svcount
+     */
+    gps_data_t read() {
+        return latest;
+    }
+
+protected:
     gps_data_t latest;
     gps_data_t tmp;
 
+#ifdef __MBED__
     Callback<void()> _callback;
+#endif
 };
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GPS/NMEA.cpp	Mon Jan 07 16:47:33 2019 +0000
@@ -0,0 +1,285 @@
+/** NMEA - a small NMEA-parsing library based on TinyGPS
+ * @Author Michael Shimniok
+ * www.bot-thoughts.com
+
+ * TinyGPS - a small GPS library for Arduino providing basic NMEA parsing
+ * Copyright (C) 2008-9 Mikal Hart
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "NMEA.h"
+#include <cstdlib>
+#include <stdio.h>
+#include <string.h>
+
+#define _GPRMC_TERM   "GPRMC"
+#define _GPGGA_TERM   "GPGGA"
+
+NMEA::NMEA()
+:  _new_time(GPS_INVALID_TIME)
+,  _new_date(GPS_INVALID_DATE)
+,  _new_latitude(GPS_INVALID_ANGLE)
+,  _new_longitude(GPS_INVALID_ANGLE)
+,  _new_altitude(GPS_INVALID_ALTITUDE)
+,  _new_speed(GPS_INVALID_SPEED)
+,  _new_course(GPS_INVALID_ANGLE)
+,  _new_hdop(0)
+,  _new_sat_count(0)
+,  _last_time_fix(GPS_INVALID_FIX_TIME)
+,  _new_time_fix(GPS_INVALID_FIX_TIME)
+,  _last_position_fix(GPS_INVALID_FIX_TIME)
+,  _new_position_fix(GPS_INVALID_FIX_TIME)
+,  _parity(0)
+,  _is_checksum_term(false)
+,  _sentence_type(_GPS_SENTENCE_OTHER)
+,  _term_number(0)
+,  _term_offset(0)
+,  _gps_data_good(false)
+,  _rmc_ready(false)
+,  _gga_ready(false)
+{
+  _term[0] = '\0';
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// Public member functions
+//
+
+// Parse NMEA once character at a time
+// @return 1 if GGA and RMC sentences are valid and ready
+int NMEA::parse(char c)
+{
+    int valid_sentence = 0;
+
+    switch(c) {
+    case ',': // term terminators
+        _parity ^= c;
+        // no break
+    case '\r':
+    case '\n':
+    case '*':
+        if (_term_offset < sizeof(_term)) {
+            _term[_term_offset] = 0;
+            valid_sentence = term_complete();
+        }
+        ++_term_number;
+        _term_offset = 0;
+        _is_checksum_term = c == '*';
+#ifdef __MBED__
+        if (_callback) _callback();
+#endif
+        break;
+
+    case '$': // sentence begin
+        _term_number = _term_offset = 0;
+        _parity = 0;
+        _sentence_type = _GPS_SENTENCE_OTHER;
+        _is_checksum_term = false;
+        _gps_data_good = false;
+        break;
+
+    default:
+        // ordinary characters
+        if (_term_offset < sizeof(_term) - 1)
+            _term[_term_offset++] = c;
+        if (!_is_checksum_term)
+            _parity ^= c;
+        }
+
+    return valid_sentence & ready();
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Private member functions
+
+// Convert hex char representation to int
+int NMEA::from_hex(char a)
+{
+    if (a >= 'A' && a <= 'F')
+        return a - 'A' + 10;
+    else if (a >= 'a' && a <= 'f')
+        return a - 'a' + 10;
+    else
+        return a - '0';
+}
+
+
+// Convert string to integer
+int NMEA::parse_int()
+{
+    char *p = _term;
+    bool isneg = *p == '-';
+    if (isneg) ++p;
+    int ret = atoi(p);
+    return isneg ? -ret : ret;
+}
+
+
+// Convert string to double
+double NMEA::parse_decimal()
+{
+  char *p = _term;
+  double ret = atof(p);
+  return ret;
+}
+
+
+// Convert NMEA lat/lon degrees/minutes to double degrees
+double NMEA::parse_degrees()
+{
+  double result;
+  int16_t degrees = atoi(_term) / 100;
+  char *minutes = strchr(_term, '.') - 2;
+  //printf("term=<%s> %d %f\n", _term, degrees, atof(minutes));
+  result = degrees + atof(minutes) / 60.0;
+  return result;
+}
+
+
+// Processes a just-completed term
+// @return true if new sentence has just passed checksum test and is validated
+bool NMEA::term_complete()
+{
+    if (_is_checksum_term) {
+        byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]);
+        if (checksum == _parity) {
+            if (_gps_data_good) {
+                _last_time_fix = _new_time_fix;
+                _last_position_fix = _new_position_fix;
+                switch(_sentence_type) {
+                    case _GPS_SENTENCE_GPRMC:
+                        latest.date   = _new_date;
+                        latest.month  = _new_month;
+                        latest.year   = _new_year;
+                        latest.hour   = _new_hour;
+                        latest.minute = _new_minute;
+                        latest.second = _new_second;
+                        latest.lat    = _new_latitude;
+                        latest.lon    = _new_longitude;
+                        latest.speed  = _new_speed;
+                        latest.course = _new_course;
+                        printf("--- RMC_READY\n");
+                        _rmc_ready = true;
+                        break;
+                    case _GPS_SENTENCE_GPGGA:
+                        _altitude      = _new_altitude;
+                        latest.lat     = _new_latitude;
+                        latest.lon     = _new_longitude;
+                        latest.hdop    = _new_hdop;
+                        latest.svcount = _new_sat_count;
+                        printf("--- GGA_READY\n");
+                        _gga_ready = true;
+                        break;
+                }//switch _sentence_type
+                return true;
+//            } else {
+//                printf("bad data\n");
+            }//if _gps_data_good
+        } else {
+            printf("bad checksum 0x%x expected 0x%x\n", checksum, _parity);
+        }//if checksum
+
+        return false;
+    }//if _is_checksum_term
+
+    // the first term determines the sentence type
+    if (_term_number == 0) {
+        if (!strcmp(_term, _GPRMC_TERM))
+            _sentence_type = _GPS_SENTENCE_GPRMC;
+        else if (!strcmp(_term, _GPGGA_TERM))
+            _sentence_type = _GPS_SENTENCE_GPGGA;
+        else
+            _sentence_type = _GPS_SENTENCE_OTHER;
+        return false;
+    }
+
+    if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0]) {
+        printf("\tterm:<%s>\n", _term);
+        switch (_sentence_type | _term_number) {
+        case _GPS_SENTENCE_GPRMC|1: // Time in both sentences
+            //case _GPS_SENTENCE_GPGGA|1:
+            {
+                char *s = _term+4;
+                _new_second = atoi(s);
+                *s = 0;
+                s -= 2;
+                _new_minute = atoi(s);
+                *s = 0;
+                s -= 2;
+                _new_hour = atoi(s);
+            }
+            break;
+        case _GPS_SENTENCE_GPRMC|2: // GPRMC validity
+          _gps_data_good = _term[0] == 'A';
+          break;
+        case _GPS_SENTENCE_GPRMC|3: // Latitude
+        case _GPS_SENTENCE_GPGGA|2:
+          _new_latitude = parse_degrees();
+          break;
+        case _GPS_SENTENCE_GPRMC|4: // N/S
+        case _GPS_SENTENCE_GPGGA|3:
+          if (_term[0] == 'S')
+            _new_latitude = -_new_latitude;
+          break;
+        case _GPS_SENTENCE_GPRMC|5: // Longitude
+        case _GPS_SENTENCE_GPGGA|4:
+          _new_longitude = parse_degrees();
+          break;
+        case _GPS_SENTENCE_GPRMC|6: // E/W
+        case _GPS_SENTENCE_GPGGA|5:
+          if (_term[0] == 'W')
+            _new_longitude = -_new_longitude;
+          break;
+        case _GPS_SENTENCE_GPRMC|7: // Speed (GPRMC)
+          _new_speed = parse_decimal();
+          break;
+        case _GPS_SENTENCE_GPRMC|8: // Course (GPRMC)
+          _new_course = parse_decimal();
+          break;
+        case _GPS_SENTENCE_GPRMC|9: // Date (GPRMC)
+            {
+                char *s = _term+4;
+                _new_year = atoi(s);
+                *s = 0;
+                s -= 2;
+                _new_month = atoi(s);
+                *s = 0;
+                s -= 2;
+                _new_date = atoi(s);
+                //printf("%02d:%02d:%02d %02d/%02d/%02d\n", _new_hour, _new_minute, _new_second, _new_month, _new_date, _new_year);
+            }
+            break;
+        case _GPS_SENTENCE_GPGGA|6: // Fix data (GPGGA)
+          _gps_data_good = _term[0] > '0';
+          break;
+        case _GPS_SENTENCE_GPGGA|7: // Number of satelites tracked (GPGGA)
+          _new_sat_count = parse_int();
+          break;
+        case _GPS_SENTENCE_GPGGA|8: // Horizontal Dilution of Position (GPGGA)
+          _new_hdop = parse_decimal();
+          break;
+        case _GPS_SENTENCE_GPGGA|9: // Altitude (GPGGA)
+          _new_altitude = parse_decimal();
+          break;
+        default :
+          break;
+        } /* switch */
+    }//if
+
+  return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GPS/NMEA.h	Mon Jan 07 16:47:33 2019 +0000
@@ -0,0 +1,125 @@
+/* NMEA - a small NMEA-parsing library based on TinyGPS
+
+  TinyGPS - a small GPS library for Arduino providing basic NMEA parsing
+  Copyright (C) 2008-9 Mikal Hart
+  All rights reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+  Ported to mbed by Michael Shimniok
+*/
+
+#ifdef __MBED__
+#include "mbed.h"
+#endif
+
+#include <stdint.h>
+#include <ctype.h>
+#include "GPS.h"
+
+#ifndef __NMEA_h
+#define __NMEA_h
+
+#define _GPS_VERSION 9 // software version of this library
+#define _GPS_MPH_PER_KNOT 1.15077945
+#define _GPS_MPS_PER_KNOT 0.51444444
+#define _GPS_KMPH_PER_KNOT 1.852
+#define _GPS_MILES_PER_METER 0.00062137112
+#define _GPS_KM_PER_METER 0.001
+
+/** An library parsing for parsing select NMEA-0183 sentences
+ * @author Michael Shimniok
+ */
+class NMEA: public GPS {
+public:
+    typedef uint8_t byte;
+
+    /** Create a new GPS parsing object for parsing NMEA sentences
+     */
+    NMEA();
+
+    /** Parse a single character received from GPS
+     *
+     * @param c is the character received from the GPS
+     * @returns 1 when all sentences received
+     */
+    virtual int parse(char c);
+
+#ifndef _GPS_NO_STATS
+    void stats(unsigned long *chars, unsigned short *good_sentences, unsigned short *failed_cs);
+#endif
+
+    /** determine if all sentences parsed
+     *
+     */
+    inline bool ready() {
+        if (_rmc_ready && _gga_ready) {
+            _rmc_ready = _gga_ready = false;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /** Reset the ready flags for all the parsed sentences
+     */
+    inline void reset_ready() { _rmc_ready = _gga_ready = false; }
+
+    enum {GPS_INVALID_AGE = 0xFFFFFFFF, GPS_INVALID_ANGLE = 999999999, GPS_INVALID_ALTITUDE = 999999999, GPS_INVALID_DATE = 0,
+      GPS_INVALID_TIME = 0xFFFFFFFF, GPS_INVALID_SPEED = 999999999, GPS_INVALID_FIX_TIME = 0xFFFFFFFF};
+
+private:
+    enum {_GPS_SENTENCE_GPGGA=0x10, _GPS_SENTENCE_GPRMC=0x20, _GPS_SENTENCE_GPGSV=0x40, _GPS_SENTENCE_OTHER=0};
+
+    // properties
+    int _new_time;
+    int _new_hour;
+    int _new_minute;
+    int _new_second;
+    int _new_date;
+    int _new_month;
+    int _new_year;
+    double _new_latitude;
+    double _new_longitude;
+    long _altitude;
+    float _new_altitude;
+    float _new_speed;
+    float _new_course;
+    float _new_hdop;
+    unsigned int _new_sat_count;
+    unsigned long _last_time_fix, _new_time_fix;
+    unsigned long _last_position_fix, _new_position_fix;
+
+    // parsing state variables
+    byte _parity;
+    bool _is_checksum_term;
+    char _term[15];
+    byte _sentence_type;
+    byte _term_number;
+    uint8_t  _term_offset;
+    bool _gps_data_good;
+    bool _rmc_ready;
+    bool _gga_ready;
+
+    double parse_degrees();
+    int from_hex(char a);
+    int parse_int();
+    double parse_decimal();
+    bool term_complete();
+    bool gpsisdigit(char c) { return c >= '0' && c <= '9'; }
+    int gpsstrcmp(const char *str1, const char *str2);
+};
+
+#endif
--- a/GPS/TinyGPS.cpp	Thu Jan 03 19:07:20 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,354 +0,0 @@
-/*
-  TinyGPS - a small GPS library for Arduino providing basic NMEA parsing
-  Copyright (C) 2008-9 Mikal Hart
-  All rights reserved.
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-  
-  Ported to mbed by Michael Shimniok http://www.bot-thoughts.com/
-*/
-
-#include "TinyGPS.h"
-
-#define _GPRMC_TERM   "GPRMC"
-#define _GPGGA_TERM   "GPGGA"
-#define _GPGSV_TERM   "GPGSV"
-
-TinyGPS::TinyGPS()
-:  _time(GPS_INVALID_TIME)
-,  _new_time(GPS_INVALID_TIME)
-,  _date(GPS_INVALID_DATE)
-,  _new_date(GPS_INVALID_DATE)
-,  _latitude(GPS_INVALID_ANGLE)
-,  _new_latitude(GPS_INVALID_ANGLE)
-,  _longitude(GPS_INVALID_ANGLE)
-,  _new_longitude(GPS_INVALID_ANGLE)
-,  _altitude(GPS_INVALID_ALTITUDE)
-,  _new_altitude(GPS_INVALID_ALTITUDE)
-,  _speed(GPS_INVALID_SPEED)
-,  _new_speed(0)
-,  _course(GPS_INVALID_ANGLE)
-,  _new_course(GPS_INVALID_ANGLE)
-,  _hdop(0)
-,  _new_hdop(0)
-,  _sat_count(0)
-,  _new_sat_count(0)
-,  _last_time_fix(GPS_INVALID_FIX_TIME)
-,  _new_time_fix(GPS_INVALID_FIX_TIME)
-,  _last_position_fix(GPS_INVALID_FIX_TIME)
-,  _new_position_fix(GPS_INVALID_FIX_TIME)
-,  _parity(0)
-,  _is_checksum_term(false)
-,  _sentence_type(_GPS_SENTENCE_OTHER)
-,  _term_number(0)
-,  _term_offset(0)
-,  _gps_data_good(false)
-,  _rmc_ready(false)
-,  _gga_ready(false)
-,  _gsv_ready(false)
-#ifndef _GPS_NO_STATS
-,  _encoded_characters(0)
-,  _good_sentences(0)
-,  _failed_checksum(0)
-,  _passed_checksum(0)
-#endif
-{
-  _term[0] = '\0';
-}
-
-//
-// public methods
-//
-
-int TinyGPS::parse(char c)
-{
-    int valid_sentence = 0;
-
-    ++_encoded_characters;
-    switch(c) {
-    case ',': // term terminators
-    _parity ^= c;
-    // no break
-    case '\r':
-    case '\n':
-    case '*':
-        if (_term_offset < sizeof(_term)) {
-            _term[_term_offset] = 0;
-            valid_sentence = term_complete();
-        }
-        ++_term_number;
-        _term_offset = 0;
-        _is_checksum_term = c == '*';
-        if (_callback) _callback();
-        return valid_sentence;
-        
-    case '$': // sentence begin
-        _term_number = _term_offset = 0;
-        _parity = 0;
-        _sentence_type = _GPS_SENTENCE_OTHER;
-        _is_checksum_term = false;
-        _gps_data_good = false;
-        return valid_sentence;
-    }
-    
-    // ordinary characters
-    if (_term_offset < sizeof(_term) - 1)
-        _term[_term_offset++] = c;
-    if (!_is_checksum_term)
-        _parity ^= c;
-    
-    return valid_sentence;
-}
-
-#ifndef _GPS_NO_STATS
-void TinyGPS::stats(unsigned long *chars, unsigned short *sentences, unsigned short *failed_cs)
-{
-  if (chars) *chars = _encoded_characters;
-  if (sentences) *sentences = _good_sentences;
-  if (failed_cs) *failed_cs = _failed_checksum;
-}
-#endif
-
-//
-// internal utilities
-//
-int TinyGPS::from_hex(char a) 
-{
-  if (a >= 'A' && a <= 'F')
-    return a - 'A' + 10;
-  else if (a >= 'a' && a <= 'f')
-    return a - 'a' + 10;
-  else
-    return a - '0';
-}
-
-int TinyGPS::parse_int()
-{
-    char *p = _term;
-    bool isneg = *p == '-';
-    if (isneg) ++p;
-    while (*p == '0') ++p;
-    int ret = gpsatol(p);
-    return isneg ? -ret : ret;
-}
-
-long TinyGPS::parse_decimal()
-{
-  char *p = _term;
-  bool isneg = *p == '-';
-  if (isneg) ++p;
-  unsigned long ret = 100UL * gpsatol(p);
-  while (gpsisdigit(*p)) ++p;
-  if (*p == '.')
-  {
-    if (gpsisdigit(p[1]))
-    {
-      ret += 10 * (p[1] - '0');
-      if (gpsisdigit(p[2]))
-        ret += p[2] - '0';
-    }
-  }
-  return isneg ? -ret : ret;
-}
-
-// mes 04/27/12 increased fractional precision to 7 digits, was 5
-unsigned long TinyGPS::parse_degrees()
-{
-  char *p;
-  unsigned long left = gpsatol(_term);
-  unsigned long tenk_minutes = (left % 100UL) * 1000000UL;
-  for (p=_term; gpsisdigit(*p); ++p);
-  if (*p == '.')
-  {
-    unsigned long mult = 100000;
-    while (gpsisdigit(*++p))
-    {
-      tenk_minutes += mult * (*p - '0');
-      mult /= 10;
-    }
-  }
-  return (left / 100) * 10000000 + tenk_minutes / 6;
-}
-
-// Processes a just-completed term
-// Returns true if new sentence has just passed checksum test and is validated
-bool TinyGPS::term_complete()
-{
-    if (_is_checksum_term) {
-    
-        byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]);
-        if (checksum == _parity) {
-        
-            if (_gps_data_good) {
-            
-#ifndef _GPS_NO_STATS
-            ++_good_sentences;
-#endif
-            _last_time_fix = _new_time_fix;
-            _last_position_fix = _new_position_fix;
-
-            switch(_sentence_type) {
-            case _GPS_SENTENCE_GPRMC:
-                _time         = _new_time;
-                _date         = _new_date;
-                latest.lat    = _new_latitude;
-                latest.lon    = _new_longitude;
-                latest.speed  = _new_speed;
-                latest.course = _new_course;
-                _rmc_ready = true;
-                break;
-            case _GPS_SENTENCE_GPGGA:
-                _altitude      = _new_altitude;
-                _time          = _new_time;
-                latest.lat     = _new_latitude;
-                latest.lon     = _new_longitude;
-                latest.hdop    = _new_hdop;
-                latest.svcount = _new_sat_count;
-                _gga_ready = true;
-                break;
-            case _GPS_SENTENCE_GPGSV:
-                _gsv_ready     = true;
-                break;
-            }
-
-            return true;
-        }
-    }
-
-#ifndef _GPS_NO_STATS
-    else
-        ++_failed_checksum;
-#endif
-    return false;
-    }
-
-    // the first term determines the sentence type
-    if (_term_number == 0) {
-        if (!gpsstrcmp(_term, _GPRMC_TERM))
-            _sentence_type = _GPS_SENTENCE_GPRMC;
-        else if (!gpsstrcmp(_term, _GPGGA_TERM))
-            _sentence_type = _GPS_SENTENCE_GPGGA;
-        else if (!gpsstrcmp(_term, _GPGSV_TERM))
-            _sentence_type = _GPS_SENTENCE_GPGSV;
-        else
-            _sentence_type = _GPS_SENTENCE_OTHER;
-        return false;
-    }
-
-    if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0])
-    /*
-    if (_sentence_type == _GPS_SENTENCE_GPGSV) {
-        // $GPGSV,3,1,12,05,54,069,45,12,44,061,44,21,07,184,46,22,78,289,47*72<CR><LF>
-        // TODO 4 need to maintain a list of 12 structs with sat info and update it each time
-        switch (_term_number) {
-        case 0 : // number of messages
-            break;
-        case 1 : // sequence number
-            break;
-        case 2 : // satellites in view 
-            break;
-        case 3 : // sat ID
-        case 8 :
-        case 12 :
-        case 16 :
-            break;
-        case 4 : // elevation
-        case 9 :
-        case 13 :
-        case 17 :
-            break;
-        case 5 : // azimuth
-        case 10 :
-        case 14 :
-        case 18 :
-            break;
-        case 6 : // SNR
-        case 11 :
-        case 15 :
-        case 19 :
-            break;
-        }
-    } else {*/
-        switch (((_sentence_type == _GPS_SENTENCE_GPGGA) ? 200 : 100) + _term_number) {
-        case 101: // Time in both sentences
-        case 201:
-          _new_time = parse_decimal();
-          //_new_time_fix = millis();
-          break;
-        case 102: // GPRMC validity
-          _gps_data_good = _term[0] == 'A';
-          break;
-        case 103: // Latitude
-        case 202:
-          _new_latitude = parse_degrees();
-          //_new_position_fix = millis();
-          break;
-        case 104: // N/S
-        case 203:
-          if (_term[0] == 'S')
-            _new_latitude = -_new_latitude;
-          break;
-        case 105: // Longitude
-        case 204:
-          _new_longitude = parse_degrees();
-          break;
-        case 106: // E/W
-        case 205:
-          if (_term[0] == 'W')
-            _new_longitude = -_new_longitude;
-          break;
-        case 107: // Speed (GPRMC)
-          _new_speed = parse_decimal();
-          break;
-        case 108: // Course (GPRMC)
-          _new_course = parse_decimal();
-          break;
-        case 109: // Date (GPRMC)
-          _new_date = gpsatol(_term);
-          break;
-        case 206: // Fix data (GPGGA)
-          _gps_data_good = _term[0] > '0';
-          break;
-        case 207: // Number of satelites tracked (GPGGA)
-          _new_sat_count = parse_int();
-          break;
-        case 208: // Horizontal Dilution of Position (GPGGA)
-          _new_hdop = parse_decimal();
-          break;
-        case 209: // Altitude (GPGGA)
-          _new_altitude = parse_decimal();
-          break;
-        default :
-          break;
-        } /* switch */
-    //}
-
-  return false;
-}
-
-long TinyGPS::gpsatol(const char *str)
-{
-  long ret = 0;
-  while (gpsisdigit(*str))
-    ret = 10 * ret + *str++ - '0';
-  return ret;
-}
-
-int TinyGPS::gpsstrcmp(const char *str1, const char *str2)
-{
-  while (*str1 && *str1 == *str2)
-    ++str1, ++str2;
-  return *str1;
-}
--- a/GPS/TinyGPS.h	Thu Jan 03 19:07:20 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,234 +0,0 @@
-/*
-  TinyGPS - a small GPS library for Arduino providing basic NMEA parsing
-  Copyright (C) 2008-9 Mikal Hart
-  All rights reserved.
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-  
-  Ported to mbed by Michael Shimniok
-*/
-
-#include "mbed.h"
-#include "GPS.h"
-
-#ifndef TinyGPS_h
-#define TinyGPS_h
-
-#define _GPS_VERSION 9 // software version of this library
-#define _GPS_MPH_PER_KNOT 1.15077945
-#define _GPS_MPS_PER_KNOT 0.51444444
-#define _GPS_KMPH_PER_KNOT 1.852
-#define _GPS_MILES_PER_METER 0.00062137112
-#define _GPS_KM_PER_METER 0.001
-//#define _GPS_NO_STATS
-
-/** TinyGPS - a small GPS library for Arduino providing basic NMEA parsing Copyright (C) 2008-9 Mikal Hart
- * All rights reserved. Modified by Michael Shimniok 
- */
-
-class TinyGPS: public GPS {
-public:
-    typedef uint8_t byte;
-  
-    /** Create a new GPS parsing object for parsing NMEA sentences
-     */
-    TinyGPS();
-    
-    /** Parse a single character received from GPS
-     *
-     * @param c is the character received from the GPS
-     * @returns 1 when all sentences received
-     */
-    virtual int parse(char c);
-    
-    /** Return the date and time from the parsed NMEA sentences
-     *
-     * @returns date as an integer value
-     * @returns time as an integer value
-     * @returns fix_age in milliseconds if available
-     */
-    inline void get_datetime(unsigned long *date, unsigned long *time)
-    {
-      if (date) *date = _date;
-      if (time) *time = _time;
-    }
-
-    /** signed altitude in centimeters (from GPGGA sentence)
-     * @returns altitude in centimeters, integer
-     */
-    inline long altitude() { return _altitude; }
-
-    /** course in last full GPRMC sentence in 100th of a degree
-     * @returns course as an integer, 100ths of a degree
-     */
-    inline unsigned long course() { return _course; }
-    
-    /** speed in last full GPRMC sentence in 100ths of a knot
-     * @returns speed in 100ths of a knot
-     */
-    unsigned long speed() { return _speed; }
-
-    /* horizontal dilution of position in last full GPGGA sentence in 100ths
-     * @returns hdop in 100ths
-     */
-    unsigned long hdop() { return _hdop; }
-
-    /** number of satellites tracked in last full GPGGA sentence
-     * @returns number of satellites tracked 
-     */
-    unsigned long sat_count() { return _sat_count; }
-
-#ifndef _GPS_NO_STATS
-    void stats(unsigned long *chars, unsigned short *good_sentences, unsigned short *failed_cs);
-#endif
-
-    /** Convert date and time of last parsed sentence to integers
-     *
-     * @returns year
-     * @returns month
-     * @returns day of month
-     * @returns hour
-     * @returns minute
-     * @returns second
-     * @returns hundreths
-     * @returns fix_age in milliseconds if available
-     */
-    inline void crack_datetime(int *year, byte *month, byte *day, 
-      byte *hour, byte *minute, byte *second, byte *hundredths = 0, unsigned long *fix_age = 0)
-    {
-      unsigned long date, time;
-      get_datetime(&date, &time);
-      if (year) 
-      {
-        *year = date % 100;
-        *year += *year > 80 ? 1900 : 2000;
-      }
-      if (month) *month = (date / 100) % 100;
-      if (day) *day = date / 10000;
-      if (hour) *hour = time / 1000000;
-      if (minute) *minute = (time / 10000) % 100;
-      if (second) *second = (time / 100) % 100;
-      if (hundredths) *hundredths = time % 100;
-    }
-
-    /** returns altitude as a float
-    */
-    inline double f_altitude()    { return altitude() / 100.0; }
-    
-    /** returns course as a float
-    */
-    inline double f_course()      { return course() / 100.0; }
-    
-    /** returns speed in knots as a float
-    */
-    inline double f_speed_knots() { return speed() / 100.0; }
-    
-    /** returns speed in mph as a float 
-    */
-    inline double f_speed_mph()   { return _GPS_MPH_PER_KNOT * f_speed_knots(); }
-    
-    /** returns speed in meters per second as a float
-    */
-    inline double f_speed_mps()   { return _GPS_MPS_PER_KNOT * f_speed_knots(); }
-    
-    /** returns speed in km per hour as a float
-    */
-    inline double f_speed_kmph()  { return _GPS_KMPH_PER_KNOT * f_speed_knots(); }
-    
-    /** returns hdop as a float
-    */
-    inline double f_hdop()      { return hdop() / 100.0; }
-
-    /** @returns library version
-    */
-    static int library_version() { return _GPS_VERSION; }
-
-    /** determine if all sentences parsed
-     *
-     */
-    inline bool ready() { return (_rmc_ready && _gga_ready); }
-
-    /** determine if GSV sentence parsed since last reset_ready()
-     */
-    inline bool gsv_ready() { return _gsv_ready; }
-
-    inline bool gga_ready() { return _gga_ready; }
-    
-    inline bool rmc_ready() { return _rmc_ready; }
-   
-    /** Reset the ready flags for all the parsed sentences
-     */
-    inline void reset_ready() { _gsv_ready = _rmc_ready = _gga_ready = false; }
-
-    enum {GPS_INVALID_AGE = 0xFFFFFFFF, GPS_INVALID_ANGLE = 999999999, GPS_INVALID_ALTITUDE = 999999999, GPS_INVALID_DATE = 0,
-      GPS_INVALID_TIME = 0xFFFFFFFF, GPS_INVALID_SPEED = 999999999, GPS_INVALID_FIX_TIME = 0xFFFFFFFF};
-
-private:
-    enum {_GPS_SENTENCE_GPGGA, _GPS_SENTENCE_GPRMC, _GPS_SENTENCE_GPGSV, _GPS_SENTENCE_OTHER};
-    
-    // properties
-    unsigned long _time, _new_time;
-    unsigned long _date, _new_date;
-    long _latitude, _new_latitude;
-    long _longitude, _new_longitude;
-    long _altitude, _new_altitude;
-    unsigned long  _speed, _new_speed;
-    unsigned long  _course, _new_course;
-    unsigned long  _hdop, _new_hdop;
-    unsigned int _sat_count, _new_sat_count;
-    unsigned long _last_time_fix, _new_time_fix;
-    unsigned long _last_position_fix, _new_position_fix;
-
-    // parsing state variables
-    byte _parity;
-    bool _is_checksum_term;
-    char _term[15];
-    byte _sentence_type;
-    byte _term_number;
-    uint8_t  _term_offset;
-    bool _gps_data_good;
-    bool _rmc_ready;
-    bool _gga_ready;
-    bool _gsv_ready;
-
-#ifndef _GPS_NO_STATS
-    // statistics
-    unsigned long _encoded_characters;
-    unsigned short _good_sentences;
-    unsigned short _failed_checksum;
-    unsigned short _passed_checksum;
-#endif
-
-    // internal utilities
-    int from_hex(char a);
-    int parse_int();
-    long parse_decimal();
-    unsigned long parse_degrees();
-    bool term_complete();
-    bool gpsisdigit(char c) { return c >= '0' && c <= '9'; }
-    long gpsatol(const char *str);
-    int gpsstrcmp(const char *str1, const char *str2);
-};
-
-// Arduino 0012 workaround
-#undef int
-#undef char
-#undef long
-#undef byte
-#undef double
-#undef abs
-#undef round 
-
-#endif
--- a/main.cpp	Thu Jan 03 19:07:20 2019 +0000
+++ b/main.cpp	Mon Jan 07 16:47:33 2019 +0000
@@ -15,7 +15,7 @@
 #include "Config.h"
 #include "Updater.h"
 //#include "Ublox6.h"
-#include "TinyGPS.h"
+#include "NMEA.h"
 #include "Logger.h"
 #include "SystemState.h"
 #include "Display.h"
@@ -129,16 +129,23 @@
 ///////////////////////////////////////////////////////////////////////////////
 // GPS
 //Ublox6 ublox;
-TinyGPS gps;
+NMEA nmea;
 EventQueue gpsQueue(8 * EVENTS_EVENT_SIZE);
 
 // Callback for gps parse data ready
 void gps_callback() {
+    GPS::gps_data_t data;
     GpsData d;
     
     led2 = !led2;
     //ublox.read(d.latitude, d.longitude, d.course, d.speed, d.hdop, d.svcount);   
-    gps.read(d.latitude, d.longitude, d.course, d.speed, d.hdop, d.svcount);
+    data = nmea.read();
+    d.latitude = data.lat;
+    d.longitude = data.lon;
+    d.course = data.course;
+    d.speed = data.speed;
+    d.hdop = data.hdop;
+    d.svcount = data.svcount;
     d.timestamp = Kernel::get_ms_count();
     logQueue.call(&logger, &Logger::log_gps, d);
     lcdQueue.call(&display, &Display::gps, d);
@@ -149,7 +156,7 @@
     while (s.readable()) {
         char c = s.getc();
         //gpsQueue.call(&ublox, &Ublox6::parse, c);
-        gpsQueue.call(&gps, &TinyGPS::parse, c);
+        gpsQueue.call(&nmea, &NMEA::parse, c);
     }
 }
 
@@ -178,17 +185,12 @@
 
 void read_gps(int argc, char **argv)
 {
-    double lat=0;
-    double lon=0;
-    float course=0;
-    float speed=0;
-    float hdop=0.0;
-    int svcount=0;
+    GPS::gps_data_t d;
     
-    gps.read(lat, lon, course, speed, hdop, svcount);
-    printf("%3.7f %3.7f\n", lat, lon);
-    printf("hdg=%03.1f deg spd=%3.1f m/s\n", course, speed);
-    printf("%d %f\n", svcount, hdop);
+    d = nmea.read();
+    printf("%3.7f %3.7f\n", d.lat, d.lon);
+    printf("hdg=%03.1f deg spd=%3.1f m/s\n", d.course, d.speed);
+    printf("%d %f\n", d.svcount, d.hdop);
 }
 
 void read_imu(int argc, char **argv) 
@@ -334,7 +336,7 @@
     display.status("Starting gps");
     Thread gpsThread(osPriorityHigh, 2048, 0, "gps");
     gpsThread.start(callback(&gpsQueue, &EventQueue::dispatch_forever));
-    gps.subscribe(gps_callback);
+    nmea.subscribe(gps_callback);
     s.attach(gps_handler);
     
     printf("Starting logging...\n");