VFD modular clock firmware
Dependencies: DipCortex-EEprom RTC flw mbed
gps.cpp
- Committer:
- Backstrom
- Date:
- 2015-02-20
- Revision:
- 9:4bb5c30a475f
- Parent:
- 0:f6e68b4ce169
File content as of revision 9:4bb5c30a475f:
/* * 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