diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/Adafruit_GPS.cpp b/Adafruit_GPS.cpp old mode 100755 new mode 100644 index 38c8f27..6315222 --- a/Adafruit_GPS.cpp +++ b/Adafruit_GPS.cpp @@ -73,7 +73,7 @@ boolean Adafruit_GPS::parse(char *nmea) { return false; p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { fixquality = atoi(p); if (fixquality > 0) { fix = true; @@ -83,23 +83,23 @@ boolean Adafruit_GPS::parse(char *nmea) { } p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { satellites = atoi(p); } p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { HDOP = atof(p); } p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { altitude = atof(p); } p = strchr(p, ',') + 1; p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { geoidheight = atof(p); } } @@ -130,18 +130,18 @@ boolean Adafruit_GPS::parse(char *nmea) { // speed p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { speed = atof(p); } // angle p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { angle = atof(p); } p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { uint32_t fulldate = atof(p); day = fulldate / 10000; month = (fulldate % 10000) / 100; @@ -180,7 +180,7 @@ boolean Adafruit_GPS::parse(char *nmea) { // parse out Auto selection, but ignore them // parse out 3d fixquality p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { fixquality_3d = atoi(p); } // skip 12 Satellite PDNs without interpreting them @@ -189,22 +189,38 @@ boolean Adafruit_GPS::parse(char *nmea) { // parse out PDOP p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { PDOP = atof(p); } // parse out HDOP, we also parse this from the GGA sentence. Chipset should // report the same for both p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { HDOP = atof(p); } // parse out VDOP p = strchr(p, ',') + 1; - if (',' != *p) { + if (!isEmpty(p)) { VDOP = atof(p); } } +#ifdef NMEA_EXTENSIONS // Sentences not required for basic GPS functionality + else if (!strcmp(thisSentence, "TXT")) { //*******************************TXT + if (!isEmpty(p)) + txtTot = atoi(p); + p = strchr(p, ',') + 1; + if (!isEmpty(p)) + txtN = atoi(p); + p = strchr(p, ',') + 1; + if (!isEmpty(p)) + txtID = atoi(p); + p = strchr(p, ',') + 1; + if (!isEmpty(p)) + parseStr(txtTXT, p, 61); // copy the text to NMEA TXT max of 61 characters + } +#endif // NMEA_EXTENSIONS + // we dont parse the remaining, yet! else return false; @@ -296,6 +312,53 @@ const char *Adafruit_GPS::tokenOnList(char *token, const char **list) { return NULL; // couldn't find a match } +/**************************************************************************/ +/*! + @brief Parse a string token from pointer p to the next comma, asterisk + or end of string. + @param buff Pointer to the buffer to store the string in + @param p Pointer into a string + @param n Max permitted size of string including terminating 0 + @return Pointer to the string buffer +*/ +/**************************************************************************/ +char *Adafruit_GPS::parseStr(char *buff, char *p, int n) { + char *e = strchr(p, ','); + int len = 0; + if (e) { + len = min(e - p, n - 1); + strncpy(buff, p, len); // copy up to the comma + buff[len] = 0; + } else { + e = strchr(p, '*'); + if (e) { + len = min(e - p, n - 1); + strncpy(buff, p, len); // or up to the * + buff[e - p] = 0; + } else { + len = min((int)strlen(p), n - 1); + strncpy(buff, p, len); // or to the end or max capacity + } + } + return buff; +} + +/**************************************************************************/ +/*! + @brief Is the field empty, or should we try conversion? Won't work + for a text field that starts with an asterisk or a comma, but that + probably violates the NMEA-183 standard. + @param pStart Pointer to the location of the token in the NMEA string + @return true if empty field, false if something there +*/ +/**************************************************************************/ +bool Adafruit_GPS::isEmpty(char *pStart) { + if (',' != *pStart && '*' != *pStart && pStart != NULL) + return false; + else + return true; +} + /**************************************************************************/ /*! @brief Add *CS where CS is the two character hex checksum for all but @@ -345,7 +408,7 @@ void Adafruit_GPS::parseLat(char *p) { int32_t degree; long minutes; char degreebuff[10]; - if (',' != *p) { + if (!isEmpty(p)) { strncpy(degreebuff, p, 2); p += 2; degreebuff[2] = '\0'; @@ -394,7 +457,7 @@ void Adafruit_GPS::parseLon(char *p) { int32_t degree; long minutes; char degreebuff[10]; - if (',' != *p) { + if (!isEmpty(p)) { strncpy(degreebuff, p, 3); p += 3; degreebuff[3] = '\0'; @@ -419,7 +482,7 @@ void Adafruit_GPS::parseLon(char *p) { */ /**************************************************************************/ boolean Adafruit_GPS::parseLonDir(char *p) { - if (',' != *p) { + if (!isEmpty(p)) { if (p[0] == 'W') { lon = 'W'; longitudeDegrees *= -1.0; @@ -981,4 +1044,177 @@ static boolean strStartsWith(const char *str, const char *prefix) { return false; } return true; -} \ No newline at end of file +} + +#ifdef NMEA_EXTENSIONS +/**************************************************************************/ +/*! + @brief Fakes time of receipt of a sentence. Use between build() and parse() + to make the timing look like the sentence arrived from the GPS. +*/ +/**************************************************************************/ +void Adafruit_GPS::resetSentTime() { sentTime = millis(); } + +/**************************************************************************/ +/*! + @brief Build an NMEA sentence string based on the relevant variables. + Sentences start with a $, then a two character source identifier, then + a three character sentence name that defines the format, then a comma + and more comma separated fields defined by the sentence name. There are + many sentences listed that are not yet supported. Most of these sentence + definitions were found at http://fort21.ru/download/NMEAdescription.pdf + + build() will work with other lengths for source and sentence to allow + extension to building proprietary sentences like $PMTK220,100*2F. + + build() will not work properly in an environment that does not support + the %f floating point formatter in sprintf(), and will return NULL. + + build() adds Carriage Return and Line Feed to sentences to conform to + NMEA-183, so send your output with a print, not a println. + + Some of the data in these test sentences may be arbitrary, e.g. for the + TXT sentence which has a more complicated protocol for multiple lines + sent as a message set. Also, the data in the class variables are presumed + to be valid, so these sentences may contain values that are stale, or + the result of initialization rather than measurement. + + @param nmea Pointer to the NMEA string buffer. Must be big enough to + hold the sentence. No guarantee what will be in it if the + building of the sentence fails. + @param thisSource Pointer to the source name string (2 upper case) + @param thisSentence Pointer to the sentence name string (3 upper case) + @param ref Reference for the sentence, usually relative (R) or true (T) + @return Pointer to sentence if successful, NULL if fails +*/ +/**************************************************************************/ +char *Adafruit_GPS::build(char *nmea, const char *thisSource, + const char *thisSentence, char ref) { + sprintf(nmea, "%6.2f", 123.45); // fail if sprintf() doesn't handle floats + if (strcmp(nmea, "123.45")) + return NULL; + *nmea = '$'; + char *p = nmea + 1; // Pointer to move through the sentence + strncpy(p, thisSource, strlen(thisSource)); + p += strlen(thisSource); + strncpy(p, thisSentence, strlen(thisSentence)); + p += strlen(thisSentence); + *p = ','; + p += 1; // Now $XXSSS, and need to add argument fields + // This may look inefficient, but an M0 will get down the list in about 1 us / + // strcmp()! Put the GPS sentences from Adafruit_GPS at the top to make + // pruning excess code easier. Otherwise, keep them alphabetical for ease of + // reading. + + if (!strcmp(thisSentence, + "GGA")) { //********************************************GGA + // GGA Global Positioning System Fix Data. Time, Position and fix related + // data for a GPS receiver + // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // | | | | | | | | | | | | | | | + //$--GGA,hhmmss.ss,ddmm.mm,a,dddmm.mm,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh + // 1) Time (UTC) + // 2) Latitude + // 3) N or S (North or South) + // 4) Longitude + // 5) E or W (East or West) + // 6) GPS Quality Indicator, 0 - fix not available, 1 - GPS fix, 2 - + // Differential GPS fix 7) Number of satellites in view, 00 - 12 8) + // Horizontal Dilution of precision 9) Antenna Altitude above/below + // mean-sea-level (geoid) 10) Units of antenna altitude, meters 11) Geoidal + // separation, the difference between the WGS-84 earth + // ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level below + // ellipsoid + // 12) Units of geoidal separation, meters + // 13) Age of differential GPS data, time in seconds since last SC104 + // type 1 or 9 update, null field when DGPS is not used + // 14) Differential reference station ID, 0000-1023 + // 15) Checksum + sprintf(p, "%09.2f,%09.4f,%c,%010.4f,%c,%d,%02d,%f,%f,M,%f,M,,", + hour * 10000L + minute * 100L + seconds + milliseconds / 1000., + latitude, lat, longitude, lon, fixquality, satellites, HDOP, + altitude, geoidheight); + + } else if (!strcmp(thisSentence, + "GLL")) { //********************************************GLL + // GLL Geographic Position – Latitude/Longitude + // 1 2 3 4 5 6 7 + // | | | | | | | + //$--GLL,llll.ll,a,yyyyy.yy,a,hhmmss.ss,A*hh + // 1) Latitude ddmm.mm format + // 2) N or S (North or South) + // 3) Longitude dddmm.mm format + // 4) E or W (East or West) + // 5) Time (UTC) + // 6) Status A - Data Valid, V - Data Invalid + // 7) Checksum + sprintf(p, "%09.4f,%c,%010.4f,%c,%09.2f,A", latitude, lat, longitude, lon, + hour * 10000L + minute * 100L + seconds + milliseconds / 1000.); + + } else if (!strcmp(thisSentence, + "GSA")) { //******************************************** + // GSA GPS DOP and active satellites + // 1 2 3 14 15 16 17 18 + // | | | | | | | | + //$--GSA,a,a,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x.x,x.x,x.x*hh + // 1) Selection mode + // 2) Mode + // 3) ID of 1st satellite used for fix + // 4) ID of 2nd satellite used for fix + // ... + // 14) ID of 12th satellite used for fix + // 15) PDOP in meters + // 16) HDOP in meters + // 17) VDOP in meters + // 18) Checksum + return NULL; + + } else if (!strcmp(thisSentence, + "RMC")) { //********************************************RMC + // RMC Recommended Minimum Navigation Information + // 12 + // 1 2 3 4 5 6 7 8 9 10 11 | + // | | | | | | | | | | | | + //$--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxxxx,x.x,a*hh + // 1) Time (UTC) + // 2) Status, V = Navigation receiver warning + // 3) Latitude + // 4) N or S + // 5) Longitude + // 6) E or W + // 7) Speed over ground, knots + // 8) Track made good, degrees true + // 9) Date, ddmmyy + // 10) Magnetic Variation, degrees + // 11) E or W + // 12) Checksum + sprintf(p, "%09.2f,A,%09.4f,%c,%010.4f,%c,%f,%f,%06d,%f,%c", + hour * 10000L + minute * 100L + seconds + milliseconds / 1000., + latitude, lat, longitude, lon, speed, angle, + day * 10000 + month * 100 + year, magvariation, mag); + + } else if (!strcmp(thisSentence, + "TXT")) { //********************************************TXT + // as mentioned in https://github.com/adafruit/Adafruit_GPS/issues/95 + // TXT Text Transmission + // 1 2 3 4 5 + // | | | | | + //$--TXT,xx,xx,xx,c--c*hh + // 1) Total Number of Sentences 01-99 + // 2) Sentence Number 01-99 + // 3) Text Identifier 01-99 + // 4) Text String, max 61 characters + // 5) Checksum + sprintf(p, "01,01,23,This is the text of the sample message"); + + } else { + return NULL; // didn't find a match for the build request + } + + addChecksum(nmea); // Successful completion + sprintf(nmea, "%s\r\n", + nmea); // Add Carriage Return and Line Feed to comply with NMEA-183 + return nmea; // return pointer to finished product +} + +#endif // NMEA_EXTENSIONS diff --git a/Adafruit_GPS.h b/Adafruit_GPS.h index 2b4f4da..e752127 100644 --- a/Adafruit_GPS.h +++ b/Adafruit_GPS.h @@ -26,6 +26,15 @@ #ifndef _ADAFRUIT_GPS_H #define _ADAFRUIT_GPS_H +/**************************************************************************/ +/** + Comment out the definition of NMEA_EXTENSIONS to make the library use as + little memory as possible for GPS functionality only. The ARDUINO_ARCH_AVR + test should leave it out of any compilations for the UNO and similar. */ +#ifndef ARDUINO_ARCH_AVR +#define NMEA_EXTENSIONS ///< if defined will include more NMEA sentences +#endif + #define USE_SW_SERIAL ///< comment this out if you don't want to include ///< software serial in the library #define GPS_DEFAULT_I2C_ADDR \ @@ -226,16 +235,16 @@ public: float speed; ///< Current speed over ground in knots float angle; ///< Course in degrees from true north float magvariation; ///< Magnetic variation in degrees (vs. true north) - float HDOP; ///< Horizontal Dilution of Precision - relative accuracy of - ///< horizontal position - float VDOP; ///< Vertical Dilution of Precision - relative accuracy of - ///< vertical position - float PDOP; ///< Position Dilution of Precision - Complex maths derives a - ///< simple, single number for each kind of DOP - char lat; ///< N/S - char lon; ///< E/W - char mag; ///< Magnetic variation direction - boolean fix; ///< Have a fix? + float HDOP; ///< Horizontal Dilution of Precision - relative accuracy of + ///< horizontal position + float VDOP; ///< Vertical Dilution of Precision - relative accuracy of + ///< vertical position + float PDOP; ///< Position Dilution of Precision - Complex maths derives a + ///< simple, single number for each kind of DOP + char lat = 'X'; ///< N/S + char lon = 'X'; ///< E/W + char mag = 'X'; ///< Magnetic variation direction + boolean fix; ///< Have a fix? uint8_t fixquality; ///< Fix quality (0, 1, 2 = Invalid, GPS, DGPS) uint8_t fixquality_3d; ///< 3D fix quality (1, 3, 3 = Nofix, 2D fix, 3D fix) uint8_t satellites; ///< Number of satellites in use @@ -257,8 +266,23 @@ public: uint8_t LOCUS_status; ///< 0: Logging, 1: Stop logging uint8_t LOCUS_percent; ///< Log life used percentage +#ifdef NMEA_EXTENSIONS + // NMEA additional public functions + char *build(char *nmea, const char *thisSource, const char *thisSentence, + char ref = 'R'); + void resetSentTime(); + + // NMEA additional public variables + char txtTXT[63] = {0}; ///< text content from most recent TXT sentence + int txtTot = 0; ///< total TXT sentences in group + int txtID = 0; ///< id of the text message + int txtN = 0; ///< the TXT sentence number +#endif // NMEA_EXTENSIONS + private: const char *tokenOnList(char *token, const char **list); + char *parseStr(char *buff, char *p, int n); + bool isEmpty(char *pStart); void parseTime(char *); void parseLat(char *); boolean parseLatDir(char *); diff --git a/examples/GPS_HardwareSerial_Timing/GPS_HardwareSerial_Timing.ino b/examples/GPS_HardwareSerial_Timing/GPS_HardwareSerial_Timing.ino index d5d1493..41ab240 100644 --- a/examples/GPS_HardwareSerial_Timing/GPS_HardwareSerial_Timing.ino +++ b/examples/GPS_HardwareSerial_Timing/GPS_HardwareSerial_Timing.ino @@ -1,9 +1,14 @@ -// Test code for Ultimate GPS Using Hardware Serial (e.g. GPS Flora or FeatherWing) +// Test code for Ultimate GPS Using Hardware Serial (e.g. GPS Flora or +// FeatherWing) // -// This code is similar to GPS_HardwareSerial_Parsing, except for the additional -// elements to keep track of how long it has been since time and fix data have been -// received. This approach lets you keep an up to date clock based on GPS time at -// any time in between GPS fixes. +// This code is similar to GPS_HardwareSerial_Parsing, except for the additional +// elements to keep track of how long it has been since time and fix data have +// been received. This approach lets you keep an up to date clock based on GPS +// time at any time in between GPS fixes. +// +// It also shows how to take advantage of the build() function to generate test +// sentences. The additional code is within #ifdef NMEA_EXTENSIONS and #endif +// tags. // // This code shows how to listen to the GPS module via polling. Best used with // Feathers or Flora where you have hardware Serial and no interrupt @@ -27,36 +32,44 @@ // Connect to the GPS on the hardware port Adafruit_GPS GPS(&GPSSerial); +#ifdef NMEA_EXTENSIONS +// Create another GPS object to hold the state of the boat, with no +// communications, so don't call Boat.begin() in setup. We will build some fake +// sentences from the Boat data to feed to GPS for testing. +Adafruit_GPS Boat(&GPSSerial); +#endif + // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console // Set to 'true' if you want to debug and listen to the raw GPS sentences #define GPSECHO true uint32_t timer = millis(); +void setup() { + // while (!Serial); // uncomment to have the sketch wait until Serial is + // ready -void setup() -{ - //while (!Serial); // uncomment to have the sketch wait until Serial is ready - - // connect at 115200 so we can read the GPS fast enough and echo without dropping chars - // also spit it out + // connect at 115200 so we can read the GPS fast enough and echo without + // dropping chars also spit it out Serial.begin(115200); Serial.println("Adafruit GPS library basic test!"); // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 GPS.begin(9600); - // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude + // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) + // including altitude GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); // uncomment this line to turn on only the "minimum recommended" data - //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); - // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since - // the parser doesn't care about other sentences at this time + // GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); + // For parsing data, we don't suggest using anything but either RMC only or + // RMC+GGA since the parser doesn't care about other sentences at this time // Set the update rate (uncomment the one you want.) - //GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate - //GPS.sendCommand(PMTK_SET_NMEA_UPDATE_200_MILLIHERTZ); // 5 second update time + // GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate + // GPS.sendCommand(PMTK_SET_NMEA_UPDATE_200_MILLIHERTZ); // 5 second update + // time GPS.sendCommand(PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ); // 10 second update time - // For the parsing code to work nicely and have time to sort thru the data, and - // print it out we don't suggest using anything higher than 1 Hz + // For the parsing code to work nicely and have time to sort thru the data, + // and print it out we don't suggest using anything higher than 1 Hz // Request updates on antenna status, comment out to keep quiet GPS.sendCommand(PGCMD_ANTENNA); @@ -73,20 +86,26 @@ void loop() // run over and over again char c = GPS.read(); // if you want to debug, this is a good time to do it! if (GPSECHO) - if (c) Serial.print(c); + if (c) + Serial.print(c); // if a sentence is received, we can check the checksum, parse it... if (GPS.newNMEAreceived()) { // a tricky thing here is if we print the NMEA sentence, or data // we end up not listening and catching other sentences! // so be very wary if using OUTPUT_ALLDATA and trytng to print out data - //Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false - if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false - return; // we can fail to parse a sentence in which case we should just wait for another + // Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() + // flag to false + if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag + // to false + return; // we can fail to parse a sentence in which case we should just + // wait for another } // if millis() or timer wraps around, we'll just reset it - if (timer > millis()) timer = millis(); + if (timer > millis()) + timer = millis(); - // approximately every 2 seconds or so, random intervals, print out the current stats + // approximately every 2 seconds or so, random intervals, print out the + // current stats static unsigned nextInterval = 2000; if (millis() - timer > nextInterval) { timer = millis(); // reset the timer @@ -94,43 +113,105 @@ void loop() // run over and over again // Time in seconds keeps increasing after we get the NMEA sentence. // This estimate will lag real time due to transmission and parsing delays, // but the lag should be small and should also be consistent. - float s = GPS.seconds + GPS.milliseconds/1000. + GPS.secondsSinceTime(); + float s = GPS.seconds + GPS.milliseconds / 1000. + GPS.secondsSinceTime(); int m = GPS.minute; int h = GPS.hour; int d = GPS.day; // Adjust time and day forward to account for elapsed time. - // This will break at month boundaries!!! Humans will have to cope with April 31,32 etc. - while(s > 60){ s -= 60; m++; } - while(m > 60){ m -= 60; h++; } - while(h > 24){ h -= 24; d++; } - // ISO Standard Date Format, with leading zeros https://xkcd.com/1179/ - Serial.print("\nDate: "); - Serial.print(GPS.year+2000, DEC); Serial.print("-"); - if(GPS.month < 10) Serial.print("0"); - Serial.print(GPS.month, DEC); Serial.print("-"); - if(d < 10) Serial.print("0"); + // This will break at month boundaries!!! Humans will have to cope with + // April 31,32 etc. + while (s > 60) { + s -= 60; + m++; + } + while (m > 60) { + m -= 60; + h++; + } + while (h > 24) { + h -= 24; + d++; + } + // ISO Standard Date Format, with leading zeros https://xkcd.com/1179/ + Serial.print("\nDate: "); + Serial.print(GPS.year + 2000, DEC); + Serial.print("-"); + if (GPS.month < 10) + Serial.print("0"); + Serial.print(GPS.month, DEC); + Serial.print("-"); + if (d < 10) + Serial.print("0"); Serial.print(d, DEC); Serial.print(" Time: "); - if(h < 10) Serial.print("0"); - Serial.print(h, DEC); Serial.print(':'); - if(m < 10) Serial.print("0"); - Serial.print(m, DEC); Serial.print(':'); - if(s < 10) Serial.print("0"); + if (h < 10) + Serial.print("0"); + Serial.print(h, DEC); + Serial.print(':'); + if (m < 10) + Serial.print("0"); + Serial.print(m, DEC); + Serial.print(':'); + if (s < 10) + Serial.print("0"); Serial.println(s, 3); - Serial.print("Fix: "); Serial.print((int)GPS.fix); - Serial.print(" quality: "); Serial.println((int)GPS.fixquality); - Serial.print("Times [s] since last fix: "); Serial.print(GPS.secondsSinceFix(),3); - Serial.print(", GPS time: "); Serial.print(GPS.secondsSinceTime(),3); - Serial.print(", GPS date: "); Serial.println(GPS.secondsSinceDate(),3); + Serial.print("Fix: "); + Serial.print((int)GPS.fix); + Serial.print(" quality: "); + Serial.println((int)GPS.fixquality); + Serial.print("Time [s] since last fix: "); + Serial.println(GPS.secondsSinceFix(), 3); + Serial.print(" since last GPS time: "); + Serial.println(GPS.secondsSinceTime(), 3); + Serial.print(" since last GPS date: "); + Serial.println(GPS.secondsSinceDate(), 3); if (GPS.fix) { Serial.print("Location: "); - Serial.print(GPS.latitude, 4); Serial.print(GPS.lat); + Serial.print(GPS.latitude, 4); + Serial.print(GPS.lat); Serial.print(", "); - Serial.print(GPS.longitude, 4); Serial.println(GPS.lon); - Serial.print("Speed (knots): "); Serial.println(GPS.speed); - Serial.print("Angle: "); Serial.println(GPS.angle); - Serial.print("Altitude: "); Serial.println(GPS.altitude); - Serial.print("Satellites: "); Serial.println((int)GPS.satellites); + Serial.print(GPS.longitude, 4); + Serial.println(GPS.lon); + Serial.print("Speed (knots): "); + Serial.println(GPS.speed); + Serial.print("Angle: "); + Serial.println(GPS.angle); + Serial.print("Altitude: "); + Serial.println(GPS.altitude); + Serial.print("Satellites: "); + Serial.println((int)GPS.satellites); } +#ifdef NMEA_EXTENSIONS + char latestBoat[200] = ""; + updateBoat(); // create some test data in Boat + Boat.build(latestBoat, "GN", "RMC"); // make a sentence from Boat data + Serial.print("\nbuild() test output -->"); // + Serial.print(latestBoat); // + GPS.resetSentTime(); // make timing look like it came in on GPS + GPS.parse(latestBoat); // parse the test data and store in GPS +#endif } } + +#ifdef NMEA_EXTENSIONS +void updateBoat() { // fill up the boat values with some test data to use in + // build() + double t = millis() / 1000.; + double theta = t / 100.; // slow + double gamma = theta * 10; // faster + Boat.latitude = 4400 + sin(theta) * 60; + Boat.lat = 'N'; + Boat.longitude = 7600 + cos(theta) * 60; + Boat.lon = 'W'; + Boat.fixquality = 2; + Boat.speed = 3 + sin(gamma); + Boat.hour = abs(cos(theta)) * 24; + Boat.minute = 30 + sin(theta / 2) * 30; + Boat.seconds = 30 + sin(gamma) * 30; + Boat.milliseconds = 500 + sin(gamma) * 500; + Boat.year = 1 + abs(sin(theta)) * 25; + Boat.month = 1 + abs(sin(gamma)) * 11; + Boat.day = 1 + abs(sin(gamma)) * 26; + Boat.satellites = abs(cos(gamma)) * 10; +} +#endif