Skip to content

Instantly share code, notes, and snippets.

@bamapookie
Created June 23, 2019 22:36
Show Gist options
  • Save bamapookie/6cb23fd289c21d5ebafa86fc5efe6b12 to your computer and use it in GitHub Desktop.
Save bamapookie/6cb23fd289c21d5ebafa86fc5efe6b12 to your computer and use it in GitHub Desktop.
#ifndef GPS_FIX_CFG
#define GPS_FIX_CFG
// Copyright (C) 2014-2017, SlashDevin
//
// This file is part of NeoGPS
//
// NeoGPS 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 3 of the License, or
// (at your option) any later version.
//
// NeoGPS 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.
//
// You should have received a copy of the GNU General Public License
// along with NeoGPS. If not, see <http://www.gnu.org/licenses/>.
/**
* Enable/disable the storage for the members of a fix.
*
* Disabling a member prevents it from being parsed from a received message.
* The disabled member cannot be accessed or stored, and its validity flag
* would not be available. It will not be declared, and code that uses that
* member will not compile.
*
* DATE and TIME are somewhat coupled in that they share a single `time_t`,
* but they have separate validity flags.
*
* See also note regarding the DOP members, below.
*
*/
#define GPS_FIX_DATE
#define GPS_FIX_TIME
//#define GPS_FIX_LOCATION
//#define GPS_FIX_LOCATION_DMS
//#define GPS_FIX_ALTITUDE
//#define GPS_FIX_SPEED
//#define GPS_FIX_VELNED
//#define GPS_FIX_HEADING
//#define GPS_FIX_SATELLITES
//#define GPS_FIX_HDOP
//#define GPS_FIX_VDOP
//#define GPS_FIX_PDOP
//#define GPS_FIX_LAT_ERR
//#define GPS_FIX_LON_ERR
//#define GPS_FIX_ALT_ERR
//#define GPS_FIX_SPD_ERR
//#define GPS_FIX_HDG_ERR
//#define GPS_FIX_TIME_ERR
//#define GPS_FIX_GEOID_HEIGHT
#endif
#ifndef NMEAGPS_CFG_H
#define NMEAGPS_CFG_H
// Copyright (C) 2014-2017, SlashDevin
//
// This file is part of NeoGPS
//
// NeoGPS 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 3 of the License, or
// (at your option) any later version.
//
// NeoGPS 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.
//
// You should have received a copy of the GNU General Public License
// along with NeoGPS. If not, see <http://www.gnu.org/licenses/>.
#include "GPSfix_cfg.h"
//------------------------------------------------------
// Enable/disable the parsing of specific sentences.
//
// Configuring out a sentence prevents it from being recognized; it
// will be completely ignored. (See also NMEAGPS_RECOGNIZE_ALL, below)
//
// FYI: Only RMC and ZDA contain date information. Other
// sentences contain time information. Both date and time are
// required if you will be doing time_t-to-clock_t operations.
//#define NMEAGPS_PARSE_GGA
//#define NMEAGPS_PARSE_GLL
//#define NMEAGPS_PARSE_GSA
//#define NMEAGPS_PARSE_GSV
//#define NMEAGPS_PARSE_GST
//#define NMEAGPS_PARSE_RMC
//#define NMEAGPS_PARSE_VTG
#define NMEAGPS_PARSE_ZDA
//------------------------------------------------------
// Select which sentence is sent *last* by your GPS device
// in each update interval. This can be used by your sketch
// to determine when the GPS quiet time begins, and thus
// when you can perform "some" time-consuming operations.
#define LAST_SENTENCE_IN_INTERVAL NMEAGPS::NMEA_ZDA
// NOTE: For PUBX-only, PGRM and UBX configs, use
// (NMEAGPS::nmea_msg_t)(NMEAGPS::NMEA_LAST_MSG+1)
// Otherwise, use one of the standard NMEA messages:
// NMEAGPS::NMEA_RMC
//
// ==> CONFIRM THIS WITH NMEAorder.INO <==
//
// If the NMEA_LAST_SENTENCE_IN_INTERVAL is not chosen
// correctly, GPS data may be lost because the sketch
// takes too long elsewhere when this sentence is received.
// Also, fix members may contain information from different
// time intervals (i.e., they are not coherent).
//
// If you don't know which sentence is the last one,
// use NMEAorder.ino to list them. You do not have to select
// the last sentence the device sends if you have disabled
// it. Just select the last sentence that you have *enabled*.
//------------------------------------------------------
// Choose how multiple sentences are merged into a fix:
// 1) No merging
// Each sentence fills out its own fix; there could be
// multiple sentences per interval.
// 2) EXPLICIT_MERGING
// All sentences in an interval are *safely* merged into one fix.
// NMEAGPS_FIX_MAX must be >= 1.
// An interval is defined by NMEA_LAST_SENTENCE_IN_INTERVAL.
// 3) IMPLICIT_MERGING
// All sentences in an interval are merged into one fix, with
// possible data loss. If a received sentence is rejected for
// any reason (e.g., a checksum error), all the values are suspect.
// The fix will be cleared; no members will be valid until new
// sentences are received and accepted. This uses less RAM.
// An interval is defined by NMEA_LAST_SENTENCE_IN_INTERVAL.
// Uncomment zero or one:
#define NMEAGPS_EXPLICIT_MERGING
//#define NMEAGPS_IMPLICIT_MERGING
#ifdef NMEAGPS_IMPLICIT_MERGING
#define NMEAGPS_MERGING NMEAGPS::IMPLICIT_MERGING
// Nothing is done to the fix at the beginning of every sentence...
#define NMEAGPS_INIT_FIX(m)
// ...but we invalidate one part when it starts to get parsed. It *may* get
// validated when the parsing is finished.
#define NMEAGPS_INVALIDATE(m) m_fix.valid.m = false
#else
#ifdef NMEAGPS_EXPLICIT_MERGING
#define NMEAGPS_MERGING NMEAGPS::EXPLICIT_MERGING
#else
#define NMEAGPS_MERGING NMEAGPS::NO_MERGING
#define NMEAGPS_NO_MERGING
#endif
// When NOT accumulating (not IMPLICIT), invalidate the entire fix
// at the beginning of every sentence...
#define NMEAGPS_INIT_FIX(m) m.valid.init()
// ...so the individual parts do not need to be invalidated as they are parsed
#define NMEAGPS_INVALIDATE(m)
#endif
#if ( defined(NMEAGPS_NO_MERGING) + \
defined(NMEAGPS_IMPLICIT_MERGING) + \
defined(NMEAGPS_EXPLICIT_MERGING) ) > 1
#error Only one MERGING technique should be enabled in NMEAGPS_cfg.h!
#endif
//------------------------------------------------------
// Define the fix buffer size. The NMEAGPS object will hold on to
// this many fixes before an overrun occurs. This can be zero,
// but you have to be more careful about using gps.fix() structure,
// because it will be modified as characters are received.
#define NMEAGPS_FIX_MAX 1
#if defined(NMEAGPS_EXPLICIT_MERGING) && (NMEAGPS_FIX_MAX == 0)
#error You must define FIX_MAX >= 1 to allow EXPLICIT merging in NMEAGPS_cfg.h
#endif
//------------------------------------------------------
// Define how fixes are dropped when the FIFO is full.
// true = the oldest fix will be dropped, and the new fix will be saved.
// false = the new fix will be dropped, and all old fixes will be saved.
#define NMEAGPS_KEEP_NEWEST_FIXES true
//------------------------------------------------------
// Enable/Disable interrupt-style processing of GPS characters
// If you are using one of the NeoXXSerial libraries,
// to attachInterrupt, this must be defined.
// Otherwise, it must be commented out.
//#define NMEAGPS_INTERRUPT_PROCESSING
#ifdef NMEAGPS_INTERRUPT_PROCESSING
#define NMEAGPS_PROCESSING_STYLE NMEAGPS::PS_INTERRUPT
#else
#define NMEAGPS_PROCESSING_STYLE NMEAGPS::PS_POLLING
#endif
//------------------------------------------------------
// Enable/disable the talker ID, manufacturer ID and proprietary message processing.
//
// First, some background information. There are two kinds of NMEA sentences:
//
// 1. Standard NMEA sentences begin with "$ttccc", where
// "tt" is the talker ID, and
// "ccc" is the variable-length sentence type (i.e., command).
//
// For example, "$GPGLL,..." is a GLL sentence (Geographic Lat/Long)
// transmitted by talker "GP". This is the most common talker ID. Some
// devices may report "$GNGLL,..." when a mix of GPS and non-GPS
// satellites have been used to determine the GLL data.
//
// 2. Proprietary NMEA sentences (i.e., those unique to a particular
// manufacturer) begin with "$Pmmmccc", where
// "P" is the NMEA-defined prefix indicator for proprietary messages,
// "mmm" is the 3-character manufacturer ID, and
// "ccc" is the variable-length sentence type (it can be empty).
//
// No validation of manufacturer ID and talker ID is performed in this
// base class. For example, although "GP" is a common talker ID, it is not
// guaranteed to be transmitted by your particular device, and it IS NOT
// REQUIRED. If you need validation of these IDs, or you need to use the
// extra information provided by some devices, you have two independent
// options:
//
// 1. Enable SAVING the ID: When /decode/ returns DECODE_COMPLETED, the
// /talker_id/ and/or /mfr_id/ members will contain ID bytes. The entire
// sentence will be parsed, perhaps modifying members of /fix/. You should
// enable one or both IDs if you want the information in all sentences *and*
// you also want to know the ID bytes. This adds two bytes of RAM for the
// talker ID, and 3 bytes of RAM for the manufacturer ID.
//
// 2. Enable PARSING the ID: The virtual /parse_talker_id/ and
// /parse_mfr_id/ will receive each ID character as it is parsed. If it
// is not a valid ID, return /false/ to abort processing the rest of the
// sentence. No CPU time will be wasted on the invalid sentence, and no
// /fix/ members will be modified. You should enable this if you want to
// ignore some IDs. You must override /parse_talker_id/ and/or
// /parse_mfr_id/ in a derived class.
//
//#define NMEAGPS_SAVE_TALKER_ID
//#define NMEAGPS_PARSE_TALKER_ID
//#define NMEAGPS_PARSE_PROPRIETARY
#ifdef NMEAGPS_PARSE_PROPRIETARY
//#define NMEAGPS_SAVE_MFR_ID
#define NMEAGPS_PARSE_MFR_ID
#endif
//------------------------------------------------------
// Enable/disable tracking the current satellite array and,
// optionally, all the info for each satellite.
//
//#define NMEAGPS_PARSE_SATELLITES
//#define NMEAGPS_PARSE_SATELLITE_INFO
#ifdef NMEAGPS_PARSE_SATELLITES
#define NMEAGPS_MAX_SATELLITES (20)
#ifndef GPS_FIX_SATELLITES
#error GPS_FIX_SATELLITES must be defined in GPSfix.h!
#endif
#endif
#if defined(NMEAGPS_PARSE_SATELLITE_INFO) & \
!defined(NMEAGPS_PARSE_SATELLITES)
#error NMEAGPS_PARSE_SATELLITES must be defined!
#endif
//------------------------------------------------------
// Enable/disable gathering interface statistics:
// CRC errors and number of sentences received
#define NMEAGPS_STATS
//------------------------------------------------------
// Configuration item for allowing derived types of NMEAGPS.
// If you derive classes from NMEAGPS, you *must* define NMEAGPS_DERIVED_TYPES.
// If not defined, virtuals are not used, with a slight size (2 bytes) and
// execution time savings.
//#define NMEAGPS_DERIVED_TYPES
#ifdef NMEAGPS_DERIVED_TYPES
#define NMEAGPS_VIRTUAL virtual
#else
#define NMEAGPS_VIRTUAL
#endif
//-----------------------------------
// See if DERIVED_TYPES is required
#if (defined(NMEAGPS_PARSE_TALKER_ID) | defined(NMEAGPS_PARSE_MFR_ID)) & \
!defined(NMEAGPS_DERIVED_TYPES)
#error You must define NMEAGPS_DERIVED_TYPES in NMEAGPS.h in order to parse Talker and/or Mfr IDs!
#endif
//------------------------------------------------------
// Becase the NMEA checksum is not very good at error detection, you can
// choose to enable additional validity checks. This trades a little more
// code and execution time for more reliability.
//
// Validation at the character level is a syntactic check only. For
// example, integer fields must contain characters in the range 0..9,
// latitude hemisphere letters can be 'N' or 'S'. Characters that are not
// valid for a particular field will cause the entire sentence to be
// rejected as an error, *regardless* of whether the checksum would pass.
#define NMEAGPS_VALIDATE_CHARS false
// Validation at the field level is a semantic check. For
// example, latitude degrees must be in the range -90..+90.
// Values that are not valid for a particular field will cause the
// entire sentence to be rejected as an error, *regardless* of whether the
// checksum would pass.
#define NMEAGPS_VALIDATE_FIELDS false
//------------------------------------------------------
// Some devices may omit trailing commas at the end of some
// sentences. This may prevent the last field from being
// parsed correctly, because the parser for some types keep
// the value in an intermediate state until the complete
// field is received (e.g., parseDDDMM, parseFloat and
// parseZDA).
//
// Enabling this will inject a simulated comma when the end
// of a sentence is received and the last field parser
// indicated that it still needs one.
#define NMEAGPS_COMMA_NEEDED
//------------------------------------------------------
// Some applications may want to recognize a sentence type
// without actually parsing any of the fields. Uncommenting
// this define will allow the nmeaMessage member to be set
// when *any* standard message is seen, even though that
// message is not enabled by a NMEAGPS_PARSE_xxx define above.
// No valid flags will be true for those sentences.
#define NMEAGPS_RECOGNIZE_ALL
//------------------------------------------------------
// Sometimes, a little extra space is needed to parse an intermediate form.
// This config items enables extra space.
//#define NMEAGPS_PARSING_SCRATCHPAD
//------------------------------------------------------
// If you need to know the exact UTC time at *any* time,
// not just after a fix arrives, you must calculate the
// offset between the Arduino micros() clock and the UTC
// time in a received fix. There are two ways to do this:
//
// 1) When the GPS quiet time ends and the new update interval begins.
// The timestamp will be set when the first character (the '$') of
// the new batch of sentences arrives from the GPS device. This is fairly
// accurate, but it will be delayed from the PPS edge by the GPS device's
// fix calculation time (usually ~100us). There is very little variance
// in this calculation time (usually < 30us), so all timestamps are
// delayed by a nearly-constant amount.
//
// NOTE: At update rates higher than 1Hz, the updates may arrive with
// some increasing variance.
//#define NMEAGPS_TIMESTAMP_FROM_INTERVAL
// 2) From the PPS pin of the GPS module. It is up to the application
// developer to decide how to capture that event. For example, you could:
//
// a) simply poll for it in loop and call UTCsecondStart(micros());
// b) use attachInterrupt to call a Pin Change Interrupt ISR to save
// the micros() at the time of the interrupt (see NMEAGPS.h), or
// c) connect the PPS to an Input Capture pin. Set the
// associated TIMER frequency, calculate the elapsed time
// since the PPS edge, and add that to the current micros().
#define NMEAGPS_TIMESTAMP_FROM_PPS
#if defined( NMEAGPS_TIMESTAMP_FROM_INTERVAL ) & \
defined( NMEAGPS_TIMESTAMP_FROM_PPS )
#error You cannot enable both TIMESTAMP_FROM_INTERVAL and PPS in NMEAGPS_cfg.h!
#endif
#endif
#include <NMEAGPS.h>
//======================================================================
// Program: SyncTime.ino
//
// Description: This program shows how to update the sub-second
// part of a clock's seconds. You can adjust the clock update
// interval to be as small as 9ms. It will calculate the
// correct ms offset from each GPS second.
//
// Prerequisites:
// 1) NMEA.ino works with your device
// 2) GPS_FIX_TIME is enabled in GPSfix_cfg.h
// 3) NMEAGPS_PARSE_RMC is enabled in NMEAGPS_cfg.h. You could use
// any sentence that contains a time field. Be sure to change the
// "if" statement in GPSloop from RMC to your selected sentence.
//
// 'Serial' is for debug output to the Serial Monitor window.
//
// License:
// Copyright (C) 2014-2017, SlashDevin
//
// This file is part of NeoGPS
//
// NeoGPS 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 3 of the License, or
// (at your option) any later version.
//
// NeoGPS 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.
//
// You should have received a copy of the GNU General Public License
// along with NeoGPS. If not, see <http://www.gnu.org/licenses/>.
//
//======================================================================
#include <GPSport.h>
static NMEAGPS gps;
static gps_fix fix;
#if !defined(GPS_FIX_TIME)
#error You must define GPS_FIX_TIME in GPSfix_cfg.h!
#endif
#if !defined(NMEAGPS_PARSE_RMC) & \
!defined(NMEAGPS_PARSE_GLL) & \
!defined(NMEAGPS_PARSE_GGA) & \
!defined(NMEAGPS_PARSE_GST) & \
!defined(NMEAGPS_PARSE_ZDA)
#error You must define NMEAGPS_PARSE_RMC, GLL, GGA, GST, or ZDA in NMEAGPS_cfg.h!
#endif
#if !defined(NMEAGPS_TIMESTAMP_FROM_INTERVAL) & \
!defined(NMEAGPS_TIMESTAMP_FROM_PPS)
#error You must define NMEAGPS_TIMESTAMP_FROM_INTERVAL or PPS in NMEAGPS_cfg.h!
#endif
#if defined(NMEAGPS_TIMESTAMP_FROM_PPS)
#warning You must modify this sketch to call gps.UTCsecondStart at the PPS rising edge (see NMEAGPS.h comments).
#endif
#ifdef NMEAGPS_INTERRUPT_PROCESSING
#error You must *NOT* define NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h!
#endif
//----------------------------------------------------------------
static const uint32_t CLOCK_INTERVAL_MS = 10UL;
static uint32_t lastShowTime = CLOCK_INTERVAL_MS+1; // First time never matches
//----------------------------------------------------------------
static void showTime( uint16_t subs, uint16_t factor = 100 /* hundredths */ )
{
uint8_t showSeconds = fix.dateTime.seconds;
// Step by seconds until we're in the current UTC second
while (subs >= 1000UL) {
subs -= 1000UL;
if (showSeconds < 59)
showSeconds++;
else
showSeconds = 0;
//DEBUG_PORT.print( '+' );
}
DEBUG_PORT.print( showSeconds );
DEBUG_PORT.print( '.' );
// Leading zeroes
for (;;) {
factor /= 10;
if ((factor < 10) || (subs >= factor))
break;
DEBUG_PORT.print( '0' );
}
DEBUG_PORT.println( subs );
} // showTime
byte generateCrc(const char *message) {
byte crc = 0;
for (int i = 0; message[i] != 0; i++) crc ^= message[i];
return crc;
}
boolean pps = false;
void writeNmea(const char *message) {
char nmea[83];
sprintf(nmea, "$%s*%02X", message, generateCrc(message));
gpsPort.println(nmea);
}
void catchPps() {
gps.UTCsecondStart(micros());
pps = true;
}
//--------------------------
void setup()
{
attachInterrupt(digitalPinToInterrupt(4), catchPps, RISING);
DEBUG_PORT.begin(115200);
while (!DEBUG_PORT)
;
DEBUG_PORT.print( F("SyncTime.INO: started\n") );
DEBUG_PORT.println( F("Looking for GPS device on " GPS_PORT_NAME) );
DEBUG_PORT.println( F("Local time seconds.milliseconds") );
DEBUG_PORT.flush();
gpsPort.begin( 115200 );
writeNmea("PMTK300,1000,0,0,0,0");
writeNmea("PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0"); // Only output ZDA
writeNmea("PMTK220,100"); // NMEA Update rate
writeNmea("PMTK605"); // Firmware version
writeNmea("PMTK667"); // GNSS UTC Correction Data
}
//--------------------------
void loop()
{
if (pps) {
DEBUG_PORT.println("PPS");
pps = false;
}
while (gps.available( gpsPort )) {
fix = gps.read();
}
if (fix.valid.time) {
uint32_t UTCms = gps.UTCms();
if (((UTCms % CLOCK_INTERVAL_MS) == 0) && (UTCms != lastShowTime)) {
showTime( UTCms, 1000 );
lastShowTime = UTCms;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment