VFD modular clock firmware

Dependencies:   DipCortex-EEprom RTC flw mbed

Revision:
0:f6e68b4ce169
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gps.cpp	Mon Feb 09 13:40:46 2015 +0000
@@ -0,0 +1,261 @@
+/*
+ * GPS support for VFD Modular Clock
+ * (C) 2012 William B Phelps
+ *
+ * mbed Port (C) 2014 Akafugu Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "global.h"
+#include "gps.h"
+
+#ifdef HAVE_GPS
+
+//Serial gps(P1_13, P1_14);
+Serial* gps;
+
+bool g_gps_updating = false;
+int g_gps_cks_errors = 0;
+int g_gps_parse_errors = 0;
+int g_gps_time_errors = 0;
+
+unsigned long tGPSupdate;
+
+// we double buffer: read into one line and leave one for the main program
+//volatile char gpsBuffer1[GPSBUFFERSIZE];
+//volatile char gpsBuffer2[GPSBUFFERSIZE];
+volatile char* gpsBuffer1;
+volatile char* gpsBuffer2;
+
+// our index into filling the current line
+volatile uint8_t gpsBufferPtr;
+// pointers to the double buffers
+volatile char *gpsNextBuffer;
+volatile char *gpsLastBuffer;
+volatile uint8_t gpsDataReady_;
+
+time_t tLast = 0;  // for checking GPS messages
+
+void GPSread() 
+{
+    char c = gps->getc();
+    
+    if (c == '$') {
+        gpsNextBuffer[gpsBufferPtr] = 0;
+        gpsBufferPtr = 0;
+    }
+    if (c == '\n') {  // newline marks end of sentence
+        gpsNextBuffer[gpsBufferPtr] = 0;  // terminate string
+        if (gpsNextBuffer == gpsBuffer1) {  // switch buffers
+            gpsNextBuffer = gpsBuffer2;
+            gpsLastBuffer = gpsBuffer1;
+        } else {
+            gpsNextBuffer = gpsBuffer1;
+            gpsLastBuffer = gpsBuffer2;
+        }
+        gpsBufferPtr = 0;
+        gpsDataReady_ = true;  // signal data ready
+    }
+    
+    gpsNextBuffer[gpsBufferPtr++] = c;  // add char to current buffer, then increment index
+    if (gpsBufferPtr >= GPSBUFFERSIZE)  // if buffer full
+        gpsBufferPtr = GPSBUFFERSIZE-1;  // decrement index to make room (overrun)
+}
+
+uint8_t gpsDataReady(void) {
+    return gpsDataReady_;
+}
+
+char *gpsNMEA(void) {
+  gpsDataReady_ = false;
+  return (char *)gpsLastBuffer;
+}
+
+uint32_t parsedecimal(char *str, uint8_t len) {
+  uint32_t d = 0;
+    for (uint8_t i=0; i<len; i++) {
+   if ((str[i] > '9') || (str[0] < '0'))
+     return d;  // no more digits
+     d = (d*10) + (str[i] - '0');
+  }
+  return d;
+}
+
+int32_t _abs(int32_t a) {
+    if (a < 0) return -a;
+    return a;    
+}
+
+const char hex[17] = "0123456789ABCDEF";
+
+uint8_t atoh(char x) {
+  return (strchr(hex, x) - hex);
+}
+
+uint32_t hex2i(char *str, uint8_t len) {
+  uint32_t d = 0;
+    for (uint8_t i=0; i<len; i++) {
+     d = (d*10) + (strchr(hex, str[i]) - hex);
+    }
+    return d;
+}
+
+// find next token in GPS string - find next comma, then point to following char
+char * ntok ( char *ptr ) {
+    ptr = strchr(ptr, ',');  // Find the next comma
+    if (ptr == NULL) return NULL;
+    ptr++;  // point at next char after comma
+    return ptr;
+}
+
+//  225446       Time of fix 22:54:46 UTC
+//  A            Navigation receiver warning A = OK, V = warning
+//  4916.45,N    Latitude 49 deg. 16.45 min North
+//  12311.12,W   Longitude 123 deg. 11.12 min West
+//  000.5        Speed over ground, Knots
+//  054.7        Course Made Good, True
+//  191194       Date of fix  19 November 1994
+//  020.3,E      Magnetic variation 20.3 deg East
+//  *68          mandatory checksum
+
+//$GPRMC,225446.000,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68\r\n
+// 0         1         2         3         4         5         6         7
+// 0123456789012345678901234567890123456789012345678901234567890123456789012
+//    0     1       2    3    4     5    6   7     8      9     10  11 12
+time_t parseGPSdata(char *gpsBuffer, bool& error, bool& fix, int8_t tzh, uint8_t tzm) {
+    time_t tNow;
+    struct tm tm;
+    uint8_t gpsCheck1, gpsCheck2;  // checksums
+
+    char gpsFixStat;  // fix status
+//  char gpsLat[7];  // ddmm.ff  (with decimal point)
+//  char gpsLatH;  // hemisphere 
+//  char gpsLong[8];  // dddmm.ff  (with decimal point)
+//  char gpsLongH;  // hemisphere 
+//  char gpsSpeed[5];  // speed over ground
+//  char gpsCourse[5];  // Course
+//  char gpsDate[6];  // Date
+//  char gpsMagV[5];  // Magnetic variation 
+//  char gpsMagD;  // Mag var E/W
+//  char gpsCKS[2];  // Checksum without asterisk
+
+    error = false;
+    fix = false;
+
+    char *ptr;
+    uint32_t tmp;
+    if ( strncmp( gpsBuffer, "$GPRMC,", 7 ) == 0 ) {
+        //Calculate checksum from the received data
+        ptr = &gpsBuffer[1];  // start at the "G"
+        gpsCheck1 = 0;  // init collector
+
+        // Loop through entire string, XORing each character to the next
+        while (*ptr != '*') // count all the bytes up to the asterisk
+        {
+            gpsCheck1 ^= *ptr;
+            ptr++;
+            if (ptr>(gpsBuffer+GPSBUFFERSIZE)) goto GPSerrorP;  // extra sanity check, can't hurt...
+        }
+        // now get the checksum from the string itself, which is in hex
+        gpsCheck2 = atoh(*(ptr+1)) * 16 + atoh(*(ptr+2));
+    
+        if (gpsCheck1 == gpsCheck2) {  // if checksums match, process the data
+            ptr = &gpsBuffer[1];  // start at beginning of buffer
+            ptr = ntok(ptr);  // Find the time string
+            if (ptr == NULL) goto GPSerrorP;
+            char *p2 = strchr(ptr, ',');  // find comma after Time
+            if (p2 == NULL) goto GPSerrorP;
+            if (p2 < (ptr+6)) goto GPSerrorP;  // Time must be at least 6 chars
+            tmp = parsedecimal(ptr, 6);   // parse integer portion
+            tm.tm_hour = tmp / 10000;
+            tm.tm_min = (tmp / 100) % 100;
+            tm.tm_sec = tmp % 100;
+            ptr = ntok(ptr);  // Find the next token - Status
+            if (ptr == NULL) goto GPSerrorP;
+            gpsFixStat = ptr[0];
+            if (gpsFixStat == 'A') {  // if data valid, parse time & date
+                fix = true;
+            
+                for (uint8_t n=0; n<7; n++) { // skip 6 tokend, find date
+                    ptr = ntok(ptr);  // Find the next token
+                    if (ptr == NULL) goto GPSerrorP; // error if not found
+                }
+                p2 = strchr(ptr, ',');  // find comma after Date
+                if (p2 == NULL) goto GPSerrorP;
+                if (p2 != (ptr+6)) goto GPSerrorP;  // check date length
+                tmp = parsedecimal(ptr, 6); 
+                tm.tm_mday = tmp / 10000;
+                tm.tm_mon = ((tmp / 100) % 100) - 1; // zero offset for month
+                tm.tm_year = tmp % 100;
+                                
+                ptr = strchr(ptr, '*');  // Find Checksum
+                if (ptr == NULL) goto GPSerrorP;
+
+                tm.tm_year = y2kYearToTm(tm.tm_year);  // convert yy year to (yyyy-1900) (add 100)                
+                tNow = mktime(&tm);  // convert to time_t - seconds since 0/0/1970
+                
+                // If time jumps by more than a minute, complain about it. Either poor GPS signal or an error in the data
+                if ( (tLast>0) && (_abs(tNow - tLast)>60) )  // Beep if over 60 seconds since last GPRMC?
+                {
+                    goto GPSerrorT;  // it's probably an error
+                }
+                else {
+                    tLast = tNow;
+                    tNow = tNow + (long)(tzh) * SECS_PER_HOUR + (long)tzm;
+
+                }
+            } // if fix status is A
+        } // if checksums match
+        else  // checksums do not match
+            goto GPSerrorC;
+        
+        return tNow;
+
+GPSerrorC:
+        g_gps_cks_errors++;  // increment error count
+        goto GPSerror2a;
+GPSerrorP:
+        g_gps_parse_errors++;  // increment error count
+        goto GPSerror2a;
+GPSerrorT:
+#ifdef HAVE_SERIAL_DEBUG
+        tDelta = tNow - tGPSupdate;
+        Serial.print("tNow="); Serial.print(tNow); Serial.print(", tLast="); Serial.print(tLast); Serial.print(", diff="); Serial.print(tNow-tLast);
+        Serial.print(", tDelta="); Serial.print(tDelta);
+        Serial.println(" ");
+#endif
+        g_gps_time_errors++;  // increment error count
+        tLast = tNow;  // save new time
+
+GPSerror2a:
+        strcpy(gpsBuffer, "");  // wipe GPS buffer
+    }  // if "$GPRMC"
+    
+    error = true;
+    return 0;
+}
+
+void gps_init() {
+    gps = new Serial(P1_13, P1_14);
+    gps->attach(&GPSread);
+    
+    gpsBuffer1 = new char[GPSBUFFERSIZE];
+    gpsBuffer2 = new char[GPSBUFFERSIZE];
+            
+    tGPSupdate = 0;  // reset GPS last update time
+    gpsDataReady_ = false;
+    gpsBufferPtr = 0;
+    gpsNextBuffer = gpsBuffer1;
+    gpsLastBuffer = gpsBuffer2;
+}
+
+#endif // HAVE_GPS