initial commit

This commit is contained in:
2026-09-25 10:48:51 +10:00
commit b90beea8ae
1020 changed files with 131665 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
# Adafruit_GPS [![Build Status](https://travis-ci.com/adafruit/Adafruit_GPS.svg?branch=master)](https://travis-ci.com/adafruit/Adafruit_GPS) [![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_GPS/html/index.html)
> **Warning**
>
> This library assumes that only valid, supported NMEA sentences are parsed.
> Parsing invalid or unsupported NMEA sentences may result in memory corruption and/or unhandled faults & exceptions!
This is the Adafruit GPS library - the ultimate GPS library
for the ultimate GPS module!
Tested and works great with the Adafruit Ultimate GPS module
using MTK33x9 chipset
------> http://www.adafruit.com/products/746
These modules use TTL serial to communicate, 2 pins are required to
interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution
To install, use the Arduino Library Manager to search for 'Adafruit GPS' and install the library.
@@ -0,0 +1,127 @@
# Adafruit Community Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and leaders pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level or type of
experience, education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
## Our Standards
We are committed to providing a friendly, safe and welcoming environment for
all.
Examples of behavior that contributes to creating a positive environment
include:
* Be kind and courteous to others
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Collaborating with other community members
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and sexual attention or advances
* The use of inappropriate images, including in a community member's avatar
* The use of inappropriate language, including in a community member's nickname
* Any spamming, flaming, baiting or other attention-stealing behavior
* Excessive or unwelcome helping; answering outside the scope of the question
asked
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate
The goal of the standards and moderation guidelines outlined here is to build
and maintain a respectful community. We ask that you don’t just aim to be
"technically unimpeachable", but rather try to be your best self.
We value many things beyond technical expertise, including collaboration and
supporting others within our community. Providing a positive experience for
other community members can have a much more significant impact than simply
providing the correct answer.
## Our Responsibilities
Project leaders are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project leaders have the right and responsibility to remove, edit, or
reject messages, comments, commits, code, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any community member for other behaviors that they deem
inappropriate, threatening, offensive, or harmful.
## Moderation
Instances of behaviors that violate the Adafruit Community Code of Conduct
may be reported by any member of the community. Community members are
encouraged to report these situations, including situations they witness
involving other community members.
You may report in the following ways:
In any situation, you may send an email to <support@adafruit.com>.
On the Adafruit Discord, you may send an open message from any channel
to all Community Helpers by tagging @community helpers. You may also send an
open message from any channel, or a direct message to @kattni#1507,
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
@Andon#8175.
Email and direct message reports will be kept confidential.
In situations on Discord where the issue is particularly egregious, possibly
illegal, requires immediate action, or violates the Discord terms of service,
you should also report the message directly to Discord.
These are the steps for upholding our community’s standards of conduct.
1. Any member of the community may report any situation that violates the
Adafruit Community Code of Conduct. All reports will be reviewed and
investigated.
2. If the behavior is an egregious violation, the community member who
committed the violation may be banned immediately, without warning.
3. Otherwise, moderators will first respond to such behavior with a warning.
4. Moderators follow a soft "three strikes" policy - the community member may
be given another chance, if they are receptive to the warning and change their
behavior.
5. If the community member is unreceptive or unreasonable when warned by a
moderator, or the warning goes unheeded, they may be banned for a first or
second offense. Repeated offenses will result in the community member being
banned.
## Scope
This Code of Conduct and the enforcement policies listed above apply to all
Adafruit Community venues. This includes but is not limited to any community
spaces (both public and private), the entire Adafruit Discord server, and
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
interaction at a conference.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. As a community
member, you are representing our community, and are expected to behave
accordingly.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.4, available at
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
For other projects adopting the Adafruit Community Code of
Conduct, please contact the maintainers of those projects for enforcement.
If you wish to use this code of conduct for your own project, consider
explicitly mentioning your moderation policy or making a copy with your
own moderation policy so as to avoid confusion.
@@ -0,0 +1,43 @@
// Test code for Ultimate GPS Using Hardware Serial
// (e.g. GPS for Leonardo, Flora or FeatherWing)
//
// This code shows how to test a passthru between USB and hardware serial
//
// Tested and works great with the Adafruit GPS FeatherWing
// ------> https://www.adafruit.com/products/3133
// or Flora GPS
// ------> https://www.adafruit.com/products/1059
// but also works with the shield, breakout
// ------> https://www.adafruit.com/products/1272
// ------> https://www.adafruit.com/products/746
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
// what's the name of the hardware serial port?
#define GPSSerial Serial1
void setup() {
// make this baud rate fast enough to we aren't waiting on it
Serial.begin(115200);
// wait for hardware serial to appear
while (!Serial) delay(10);
// 9600 baud is the default rate for the Ultimate GPS
GPSSerial.begin(9600);
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
GPSSerial.write(c);
}
if (GPSSerial.available()) {
char c = GPSSerial.read();
Serial.write(c);
}
}
@@ -0,0 +1,66 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code turns on the LOCUS built-in datalogger. The datalogger
// turns off when power is lost, so you MUST turn it on every time
// you want to use it!
//
// Tested and works great with the Adafruit GPS FeatherWing
// ------> https://www.adafruit.com/products/3133
// or Flora GPS
// ------> https://www.adafruit.com/products/1059
// but also works with the shield, breakout
// ------> https://www.adafruit.com/products/1272
// ------> https://www.adafruit.com/products/746
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// what's the name of the hardware serial port?
#define GPSSerial Serial1
// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);
// 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
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
Serial.begin(115200);
delay(1000);
Serial.println("Adafruit GPS logging data dump!");
// 9600 NMEA is the default baud rate for MTK - some use 4800
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF);
while (GPSSerial.available())
GPSSerial.read();
delay(1000);
GPS.sendCommand("$PMTK622,1*29");
Serial.println("----------------------------------------------------");
}
uint32_t updateTime = 1000;
void loop() // run over and over again
{
if (Serial.available()) {
char c = Serial.read();
GPSSerial.write(c);
}
if (GPSSerial.available()) {
char c = GPSSerial.read();
Serial.write(c);
}
}//loop
@@ -0,0 +1,68 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code erases the LOCUS built-in datalogger storage
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
//This code is intended for use with Arduino Leonardo and other ATmega32U4-based Arduinos
#include <Adafruit_GPS.h>
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// If using software serial (sketch example default):
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7
// If using hardware serial:
// Connect the GPS TX (transmit) pin to Arduino RX1 (Digital 0)
// Connect the GPS RX (receive) pin to matching TX1 (Digital 1)
// If using software serial, keep these lines enabled
// (you can change the pin numbers to match your wiring):
// what's the name of the hardware serial port?
#define GPSSerial Serial1
// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);
// 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
void setup()
{
while (!Serial) ; //wait for serial port on Leonardo
// connect at 115200 so we can read the GPS fast enuf and
// also spit it out
Serial.begin(115200);
Serial.println("Adafruit GPS erase FLASH!");
// 9600 NMEA is the default baud rate for MTK
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF);
Serial.println("This code will ERASE the data log stored in the FLASH - Permanently!");
Serial.print("Are you sure you want to do this? [Y/N]: ");
while (Serial.read() != 'Y') delay(10);
Serial.println("\nERASING! UNPLUG YOUR ARDUINO WITHIN 5 SECONDS IF YOU DIDNT MEAN TO!");
delay(5000);
GPS.sendCommand(PMTK_LOCUS_ERASE_FLASH);
Serial.println("Erased");
}
void loop() // run over and over again
{
if (GPSSerial.available()) {
char c = GPSSerial.read();
if (GPSECHO)
Serial.write(c);
}
}
@@ -0,0 +1,71 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code turns on the LOCUS built-in datalogger. The datalogger
// turns off when power is lost, so you MUST turn it on every time
// you want to use it!
//
// Tested and works great with the Adafruit GPS FeatherWing
// ------> https://www.adafruit.com/products/3133
// or Flora GPS
// ------> https://www.adafruit.com/products/1059
// but also works with the shield, breakout
// ------> https://www.adafruit.com/products/1272
// ------> https://www.adafruit.com/products/746
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// what's the name of the hardware serial port?
#define GPSSerial Serial1
// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);
// 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
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
Serial.begin(115200);
delay(1000);
Serial.println("Adafruit GPS logging start test!");
// 9600 NMEA is the default baud rate for MTK - some use 4800
GPS.begin(9600);
// You can adjust which sentences to have the module emit, below
// Default is RMC + GGA
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Default is 1 Hz update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Ask for firmware version
GPS.sendCommand(PMTK_Q_RELEASE);
Serial.print("\nSTARTING LOGGING....");
if (GPS.LOCUS_StartLogger())
Serial.println(" STARTED!");
else
Serial.println(" no response :(");
}
void loop() // run over and over again
{
if (GPSSerial.available()) {
char c = GPSSerial.read();
Serial.write(c);
}
// Do other things here....
}
/******************************************************************/
@@ -0,0 +1,102 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code turns on the LOCUS built-in datalogger. The datalogger
// turns off when power is lost, so you MUST turn it on every time
// you want to use it!
//
// Tested and works great with the Adafruit GPS FeatherWing
// ------> https://www.adafruit.com/products/3133
// or Flora GPS
// ------> https://www.adafruit.com/products/1059
// but also works with the shield, breakout
// ------> https://www.adafruit.com/products/1272
// ------> https://www.adafruit.com/products/746
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// what's the name of the hardware serial port?
#define GPSSerial Serial1
// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);
// 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
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
Serial.begin(115200);
delay(1000);
Serial.println("Adafruit GPS logging start test!");
// 9600 NMEA is the default baud rate for MTK - some use 4800
GPS.begin(9600);
// You can adjust which sentences to have the module emit, below
// Default is RMC + GGA
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Default is 1 Hz update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Ask for firmware version
GPS.sendCommand(PMTK_Q_RELEASE);
Serial.print("\nSTARTING LOGGING....");
if (GPS.LOCUS_StartLogger())
Serial.println(" STARTED!");
else
Serial.println(" no response :(");
}
uint32_t timer = 0;
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 ((c) && (GPSECHO))
Serial.write(c);
if (millis() - timer > 1000)
{
timer = millis();
if (GPS.LOCUS_ReadStatus()) {
Serial.print("\n\nLog #");
Serial.print(GPS.LOCUS_serial, DEC);
if (GPS.LOCUS_type == LOCUS_OVERLAP)
Serial.print(", Overlap, ");
else if (GPS.LOCUS_type == LOCUS_FULLSTOP)
Serial.print(", Full Stop, Logging");
if (GPS.LOCUS_mode & 0x1) Serial.print(" AlwaysLocate");
if (GPS.LOCUS_mode & 0x2) Serial.print(" FixOnly");
if (GPS.LOCUS_mode & 0x4) Serial.print(" Normal");
if (GPS.LOCUS_mode & 0x8) Serial.print(" Interval");
if (GPS.LOCUS_mode & 0x10) Serial.print(" Distance");
if (GPS.LOCUS_mode & 0x20) Serial.print(" Speed");
Serial.print(", Content "); Serial.print((int)GPS.LOCUS_config);
Serial.print(", Interval "); Serial.print((int)GPS.LOCUS_interval);
Serial.print(" sec, Distance "); Serial.print((int)GPS.LOCUS_distance);
Serial.print(" m, Speed "); Serial.print((int)GPS.LOCUS_speed);
Serial.print(" m/s, Status ");
if (GPS.LOCUS_status)
Serial.print("LOGGING, ");
else
Serial.print("OFF, ");
Serial.print((int)GPS.LOCUS_records); Serial.print(" Records, ");
Serial.print((int)GPS.LOCUS_percent); Serial.print("% Used ");
}//if (GPS.LOCUS_ReadStatus())
}//if (millis() - timer > 1000)
}//loop
@@ -0,0 +1,114 @@
// Test code for Ultimate GPS Using Hardware Serial (e.g. GPS Flora or FeatherWing)
//
// 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
//
// Tested and works great with the Adafruit GPS FeatherWing
// ------> https://www.adafruit.com/products/3133
// or Flora GPS
// ------> https://www.adafruit.com/products/1059
// but also works with the shield, breakout
// ------> https://www.adafruit.com/products/1272
// ------> https://www.adafruit.com/products/746
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// what's the name of the hardware serial port?
#define GPSSerial Serial1
// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);
// 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 false
uint32_t timer = millis();
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
Serial.begin(115200);
Serial.println("Adafruit GPS library basic parsing 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
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
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// 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);
delay(1000);
// Ask for firmware version
GPSSerial.println(PMTK_Q_RELEASE);
}
void loop() // run over and over again
{
// read data from the GPS in the 'main loop'
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 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 trying to print out data
Serial.print(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
}
// approximately every 2 seconds or so, print out the current stats
if (millis() - timer > 2000) {
timer = millis(); // reset the timer
Serial.print("\nTime: ");
if (GPS.hour < 10) { Serial.print('0'); }
Serial.print(GPS.hour, DEC); Serial.print(':');
if (GPS.minute < 10) { Serial.print('0'); }
Serial.print(GPS.minute, DEC); Serial.print(':');
if (GPS.seconds < 10) { Serial.print('0'); }
Serial.print(GPS.seconds, DEC); Serial.print('.');
if (GPS.milliseconds < 10) {
Serial.print("00");
} else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) {
Serial.print("0");
}
Serial.println(GPS.milliseconds);
Serial.print("Date: ");
Serial.print(GPS.day, DEC); Serial.print('/');
Serial.print(GPS.month, DEC); Serial.print("/20");
Serial.println(GPS.year, DEC);
Serial.print("Fix: "); Serial.print((int)GPS.fix);
Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
if (GPS.fix) {
Serial.print("Location: ");
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("Antenna status: "); Serial.println((int)GPS.antenna);
}
}
}
@@ -0,0 +1,215 @@
// 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.
//
// 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
//
// Tested and works great with the Adafruit GPS FeatherWing
// ------> https://www.adafruit.com/products/3133
// or Flora GPS
// ------> https://www.adafruit.com/products/1059
// but also works with the shield, breakout
// ------> https://www.adafruit.com/products/1272
// ------> https://www.adafruit.com/products/746
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// what's the name of the hardware serial port?
#define GPSSerial Serial1
// 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 you don't need to call Boat.begin() in setup.
// We will build some fake sentences from the Boat data to feed to
// GPS for testing.
Adafruit_GPS Boat;
#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
// 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
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
// 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_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
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
delay(1000);
// Ask for firmware version
GPSSerial.println(PMTK_Q_RELEASE);
}
void loop() // run over and over again
{
// read data from the GPS in the 'main loop'
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 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
}
// 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
nextInterval = 1500 + random(1000);
// 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();
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");
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");
Serial.println(s, 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(", ");
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
@@ -0,0 +1,35 @@
// Test code for Adafruit GPS That Support Using I2C
//
// This code shows how to test a passthru between USB and I2C
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// Connect to the GPS on the hardware I2C port
Adafruit_GPS GPS(&Wire);
void setup() {
// wait for hardware serial to appear
while (!Serial);
// make this baud rate fast enough to we aren't waiting on it
Serial.begin(115200);
Serial.println("Adafruit GPS library basic I2C test!");
GPS.begin(0x10); // The I2C address to use is 0x10
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
GPS.write(c);
}
if (GPS.available()) {
char c = GPS.read();
Serial.write(c);
}
}
@@ -0,0 +1,35 @@
// Test code for Adafruit GPS That Support Using I2C
//
// This code shows how to test a passthru between USB and I2C
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// Connect to the GPS on the hardware I2C port
Adafruit_GPS GPS(&Wire);
void setup() {
// wait for hardware serial to appear
while (!Serial);
// make this baud rate fast enough to we aren't waiting on it
Serial.begin(115200);
Serial.println("Adafruit GPS library basic I2C test!");
GPS.begin(0x10); // The I2C address to use is 0x10
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
GPS.write(c);
}
if (GPS.available()) {
char c = GPS.read();
Serial.write(c);
}
}
@@ -0,0 +1,101 @@
// Test code for Adafruit GPS That Support Using I2C
//
// This code shows how to parse data from the I2C GPS
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// Connect to the GPS on the hardware I2C port
Adafruit_GPS GPS(&Wire);
// 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 false
uint32_t timer = millis();
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
Serial.begin(115200);
Serial.println("Adafruit I2C GPS library basic test!");
// 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
GPS.begin(0x10); // The I2C address to use is 0x10
// 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
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// 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);
delay(1000);
// Ask for firmware version
GPS.println(PMTK_Q_RELEASE);
}
void loop() // run over and over again
{
// read data from the GPS in the 'main loop'
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 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 trying 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
}
// approximately every 2 seconds or so, print out the current stats
if (millis() - timer > 2000) {
timer = millis(); // reset the timer
Serial.print("\nTime: ");
if (GPS.hour < 10) { Serial.print('0'); }
Serial.print(GPS.hour, DEC); Serial.print(':');
if (GPS.minute < 10) { Serial.print('0'); }
Serial.print(GPS.minute, DEC); Serial.print(':');
if (GPS.seconds < 10) { Serial.print('0'); }
Serial.print(GPS.seconds, DEC); Serial.print('.');
if (GPS.milliseconds < 10) {
Serial.print("00");
} else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) {
Serial.print("0");
}
Serial.println(GPS.milliseconds);
Serial.print("Date: ");
Serial.print(GPS.day, DEC); Serial.print('/');
Serial.print(GPS.month, DEC); Serial.print("/20");
Serial.println(GPS.year, DEC);
Serial.print("Fix: "); Serial.print((int)GPS.fix);
Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
if (GPS.fix) {
Serial.print("Location: ");
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);
}
}
}
@@ -0,0 +1,43 @@
// Test code for Adafruit GPS That Support Using SPI
//
// This code shows how to test a passthru between USB and SPI
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
// Connect to the GPS on the hardware SPI port
// with CS on pin #10
Adafruit_GPS GPS(&SPI, 10);
#define RESET_PIN 9
void setup() {
// wait for hardware serial to appear
while (!Serial);
// make this baud rate fast enough to we aren't waiting on it
Serial.begin(115200);
Serial.println("Adafruit GPS library basic SPI test!");
pinMode(RESET_PIN, OUTPUT);
digitalWrite(RESET_PIN, LOW);
delay(10);
digitalWrite(RESET_PIN, HIGH);
delay(100);
GPS.begin(100000); // use 100kHz for SPI data rate
GPS.println(PMTK_SET_NMEA_UPDATE_10HZ);
GPS.println(PMTK_SET_NMEA_OUTPUT_ALLDATA);
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
GPS.write(c);
}
if (GPS.available()) {
char c = GPS.read();
Serial.write(c);
}
}
@@ -0,0 +1,67 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code just echos whatever is coming from the GPS unit to the
// serial monitor, handy for debugging!
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7
// You can change the pin numbers to match your wiring:
SoftwareSerial mySerial(8, 7);
#define PMTK_SET_NMEA_UPDATE_1HZ "$PMTK220,1000*1F"
#define PMTK_SET_NMEA_UPDATE_5HZ "$PMTK220,200*2C"
#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F"
// turn on only the second sentence (GPRMC)
#define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"
// turn on GPRMC and GGA
#define PMTK_SET_NMEA_OUTPUT_RMCGGA "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// turn on ALL THE DATA
#define PMTK_SET_NMEA_OUTPUT_ALLDATA "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// turn off output
#define PMTK_SET_NMEA_OUTPUT_OFF "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
#define PMTK_Q_RELEASE "$PMTK605*31"
void setup() {
while (!Serial); // wait for Serial to be ready
Serial.begin(115200); // The serial port for the Arduino IDE port output
mySerial.begin(9600);
delay(2000);
Serial.println("Software Serial GPS Test Echo Test");
// you can send various commands to get it started
//mySerial.println(PMTK_SET_NMEA_OUTPUT_RMCGGA);
mySerial.println(PMTK_SET_NMEA_OUTPUT_ALLDATA);
mySerial.println(PMTK_SET_NMEA_UPDATE_1HZ);
Serial.println("Get version!");
mySerial.println(PMTK_Q_RELEASE);
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
Serial.write(c);
mySerial.write(c);
}
if (mySerial.available()) {
char c = mySerial.read();
Serial.write(c);
}
}
@@ -0,0 +1,60 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code turns on the LOCUS built-in datalogger. The datalogger
// turns off when power is lost, so you MUST turn it on every time
// you want to use it!
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7
// you can change the pin numbers to match your wiring:
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
void setup()
{
while (!Serial); // Leonardo will wait till serial connects
// connect at 115200 so we can read the GPS fast enuf and
// also spit it out
Serial.begin(115200);
Serial.println("Adafruit GPS logging start test!");
// 9600 NMEA is the default baud rate for MTK - some use 4800
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF);
while (mySerial.available())
mySerial.read();
delay(1000);
GPS.sendCommand("$PMTK622,1*29");
Serial.println("----------------------------------------------------");
}
void loop() // run over and over again
{
if (mySerial.available()) {
char c = mySerial.read();
if (c) {
#ifdef UDR0
UDR0 = c;
#else
Serial.print(c);
#endif
}
}
}
@@ -0,0 +1,100 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code erases the LOCUS built-in datalogger storage
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7
// you can change the pin numbers to match your wiring:
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
// 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 false
// this keeps track of whether we're using the interrupt
// off by default!
#ifndef ESP8266 // Sadly not on ESP8266
bool usingInterrupt = false;
#endif
void setup()
{
while (!Serial) ; //wait for serial port on Leonardo
// connect at 115200 so we can read the GPS fast enuf and
// also spit it out
Serial.begin(115200);
Serial.println("Adafruit GPS erase FLASH!");
// 9600 NMEA is the default baud rate for MTK
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF);
// the nice thing about this code is you can have a timer0 interrupt go off
// every 1 millisecond, and read data from the GPS for you. that makes the
// loop code a heck of a lot easier!
#ifndef ESP8266 // Not on ESP8266
useInterrupt(true);
#endif
Serial.println("This code will ERASE the data log stored in the FLASH - Permanently!");
Serial.print("Are you sure you want to do this? [Y/N]: ");
while (Serial.read() != 'Y') delay(10);
Serial.println("\nERASING! UNPLUG YOUR ARDUINO WITHIN 5 SECONDS IF YOU DIDNT MEAN TO!");
delay(5000);
GPS.sendCommand(PMTK_LOCUS_ERASE_FLASH);
Serial.println("Erased");
}
void loop() // run over and over again
{
if (mySerial.available()) {
Serial.write(mySerial.read());
}
}
/******************************************************************/
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
#ifndef ESP8266 // Not on ESP8266
ISR(TIMER0_COMPA_vect) {
char c = GPS.read();
// if you want to debug, this is a good time to do it!
if (GPSECHO && c) {
#ifdef UDR0
UDR0 = c;
// writing direct to UDR0 is much much faster than Serial.print
// but only one character can be written at a time.
#else
Serial.write(c);
#endif
}
}
void useInterrupt(bool v) {
if (v) {
// Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function above
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {
// do not call the interrupt function COMPA anymore
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
#endif // ESP8266
@@ -0,0 +1,105 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code turns on the LOCUS built-in datalogger. The datalogger
// turns off when power is lost, so you MUST turn it on every time
// you want to use it!
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7
// you can change the pin numbers to match your wiring:
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
// 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
// this keeps track of whether we're using the interrupt
// off by default!
#ifndef ESP8266 // Sadly not on ESP8266
bool usingInterrupt = false;
#endif
void setup()
{
// connect at 115200 so we can read the GPS fast enuf and
// also spit it out
Serial.begin(115200);
while (!Serial);
delay(1000);
Serial.println("Adafruit GPS logging start test!");
// 9600 NMEA is the default baud rate for MTK - some use 4800
GPS.begin(9600);
// You can adjust which sentences to have the module emit, below
// Default is RMC + GGA
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Default is 1 Hz update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
// the nice thing about this code is you can have a timer0 interrupt go off
// every 1 millisecond, and read data from the GPS for you. that makes the
// loop code a heck of a lot easier!
#ifndef ESP8266 // Not on ESP8266
useInterrupt(true);
#endif
delay(500);
Serial.print("\nSTARTING LOGGING....");
if (GPS.LOCUS_StartLogger())
Serial.println(" STARTED!");
else
Serial.println(" no response :(");
delay(1000);
}
void loop() // run over and over again
{
// do nothing! all reading and printing is done in the interrupt
}
/******************************************************************/
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
#ifndef ESP8266 // Not on ESP8266
ISR(TIMER0_COMPA_vect) {
char c = GPS.read();
// if you want to debug, this is a good time to do it!
if (GPSECHO && c) {
#ifdef UDR0
UDR0 = c;
// writing direct to UDR0 is much much faster than Serial.print
// but only one character can be written at a time.
#else
Serial.write(c);
#endif
}
}
void useInterrupt(bool v) {
if (v) {
// Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function above
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {
// do not call the interrupt function COMPA anymore
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
#endif // ESP8266
@@ -0,0 +1,143 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code turns on the LOCUS built-in datalogger. The datalogger
// turns off when power is lost, so you MUST turn it on every time
// you want to use it!
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7
// you can change the pin numbers to match your wiring:
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
// 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 false
// this keeps track of whether we're using the interrupt
// off by default!
#ifndef ESP8266 // Sadly not on ESP8266
bool usingInterrupt = false;
#endif
void setup()
{
while (!Serial); // the Leonardo will 'wait' until the USB plug is connected
// connect at 115200 so we can read the GPS fast enuf and
// also spit it out
Serial.begin(115200);
Serial.println("Adafruit GPS logging start test!");
// 9600 NMEA is the default baud rate for MTK - some use 4800
GPS.begin(9600);
// You can adjust which sentences to have the module emit, below
// Default is RMC + GGA
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Default is 1 Hz update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
// the nice thing about this code is you can have a timer0 interrupt go off
// every 1 millisecond, and read data from the GPS for you. that makes the
// loop code a heck of a lot easier!
#ifndef ESP8266 // Not on ESP8266
useInterrupt(true);
#endif
while (true) {
Serial.print("Starting logging....");
if (GPS.LOCUS_StartLogger()) {
Serial.println(" STARTED!");
break;
} else {
Serial.println(" no response :(");
}
}
}
uint32_t timer = 0;
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 ((c) && (GPSECHO))
Serial.write(c);
if (millis() - timer > 1000)
{
timer = millis();
if (GPS.LOCUS_ReadStatus()) {
Serial.print("\n\nLog #");
Serial.print(GPS.LOCUS_serial, DEC);
if (GPS.LOCUS_type == LOCUS_OVERLAP)
Serial.print(", Overlap, ");
else if (GPS.LOCUS_type == LOCUS_FULLSTOP)
Serial.print(", Full Stop, Logging");
if (GPS.LOCUS_mode & 0x1) Serial.print(" AlwaysLocate");
if (GPS.LOCUS_mode & 0x2) Serial.print(" FixOnly");
if (GPS.LOCUS_mode & 0x4) Serial.print(" Normal");
if (GPS.LOCUS_mode & 0x8) Serial.print(" Interval");
if (GPS.LOCUS_mode & 0x10) Serial.print(" Distance");
if (GPS.LOCUS_mode & 0x20) Serial.print(" Speed");
Serial.print(", Content "); Serial.print((int)GPS.LOCUS_config);
Serial.print(", Interval "); Serial.print((int)GPS.LOCUS_interval);
Serial.print(" sec, Distance "); Serial.print((int)GPS.LOCUS_distance);
Serial.print(" m, Speed "); Serial.print((int)GPS.LOCUS_speed);
Serial.print(" m/s, Status ");
if (GPS.LOCUS_status)
Serial.print("LOGGING, ");
else
Serial.print("OFF, ");
Serial.print((int)GPS.LOCUS_records); Serial.print(" Records, ");
Serial.print((int)GPS.LOCUS_percent); Serial.print("% Used ");
}//if (GPS.LOCUS_ReadStatus())
}//if (millis() - timer > 1000)
}//loop
/******************************************************************/
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
#ifndef ESP8266 // Not on ESP8266
ISR(TIMER0_COMPA_vect) {
char c = GPS.read();
// if you want to debug, this is a good time to do it!
#ifdef UDR0
// writing direct to UDR0 is much much faster than Serial.print
// but only one character can be written at a time.
UDR0 = c;
#else
Serial.print(c);
#endif
}
void useInterrupt(bool v) {
if (v) {
// Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function above
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {
// do not call the interrupt function COMPA anymore
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
#endif // ESP8266
@@ -0,0 +1,117 @@
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code shows how to listen to the GPS module in an interrupt
// which allows the program to have more 'freedom' - just parse
// when a new NMEA sentence is available! Then access data when
// desired.
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7
// you can change the pin numbers to match your wiring:
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
// 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
void setup()
{
// connect at 115200 so we can read the GPS fast enough and echo without dropping chars
// also spit it out
Serial.begin(115200);
delay(5000);
Serial.println("Adafruit GPS library basic parsing 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
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
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// 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);
delay(1000);
// Ask for firmware version
mySerial.println(PMTK_Q_RELEASE);
}
uint32_t timer = millis();
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 ((c) && (GPSECHO))
Serial.write(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
}
// approximately every 2 seconds or so, print out the current stats
if (millis() - timer > 2000) {
timer = millis(); // reset the timer
Serial.print("\nTime: ");
if (GPS.hour < 10) { Serial.print('0'); }
Serial.print(GPS.hour, DEC); Serial.print(':');
if (GPS.minute < 10) { Serial.print('0'); }
Serial.print(GPS.minute, DEC); Serial.print(':');
if (GPS.seconds < 10) { Serial.print('0'); }
Serial.print(GPS.seconds, DEC); Serial.print('.');
if (GPS.milliseconds < 10) {
Serial.print("00");
} else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) {
Serial.print("0");
}
Serial.println(GPS.milliseconds);
Serial.print("Date: ");
Serial.print(GPS.day, DEC); Serial.print('/');
Serial.print(GPS.month, DEC); Serial.print("/20");
Serial.println(GPS.year, DEC);
Serial.print("Fix: "); Serial.print((int)GPS.fix);
Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
if (GPS.fix) {
Serial.print("Location: ");
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("Antenna status: "); Serial.println((int)GPS.antenna);
}
}
}
@@ -0,0 +1,143 @@
/**************************************************************************/
/*!
@file NMEA_EXTENSIONS.ino
@section intro Introduction
An Arduino sketch for testing the NMEA_EXTENSIONS to the library. Does
not require any GPS hardware. Boat is a data only object we can use to
represent the actual data and build sentences from. GPS is a data only
object that parses the sentences and saves the results, the same way you
would with a communicating GPS object
Only some of the data values will have added history. Note that history
is stored as integers, scaled and offset from the float values to save
memory. The AWA (Apparent Wind Angle) is recorded as three components,
so that sin and cos parts can be accurately time averaged. onList() allows
testing sentences against a list to see if they should be passed on to
another listener, allowing your sketch to act as an NMEA multiplexer.
Although it will just barely compile for an UNO with the NMEA_EXTENSIONS,
defining two GPS objects pushes the limits of the UNO data space and
should probably be avoided.
@section author Author
Written by Rick Sellens.
@section license License
CCBY license
*/
/**************************************************************************/
#include "Adafruit_GPS.h"
Adafruit_GPS GPS; // The results obtained from the instruments -- no comms
Adafruit_GPS Boat; // The state of the boat used to create some simulated sentences
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 10000); // Wait for monitor to be ready.
Serial.print("\n\nNMEA_EXTENSIONS Example v 0.1.1\n\n");
#ifdef NMEA_EXTENSIONS
addHistory(&GPS);
#else
Serial.print("NMEA_EXTENSIONS not #defined, so there will be no action.\n");
#endif
}
char latestBoat[200] = "";
const char *senList[] = {"GGA", "GLL", "DBT", "HDM", "MWV", "ZZ"}; // sentence list
const char *passList[] = {"GGA", "DBT", "ZZ"}; // short list
void loop() {
static unsigned long lastPrint = 0;
updateBoat();
// stop keeping AWA history after 30 seconds, just as a demonstration
#ifdef NMEA_EXTENSIONS
if(millis() > 30000) GPS.removeHistory(NMEA_AWA);
#endif
if (millis() - lastPrint > 300 || lastPrint == 0) {
lastPrint = millis();
#ifdef NMEA_EXTENSIONS
Serial.print("\nSentences built from Boat and parsed by GPS ");
Serial.print("(Only a few get passed on to the ST network.):\n\n");
for (int i = 0;strncmp(senList[i],"ZZ",2);i++){
if (GPS.parse(Boat.build(latestBoat, "II", senList[i]))){
if (GPS.onList(latestBoat,passList))
Serial.print("Pass to ST: ");
else Serial.print(" No Pass: ");
Serial.print(latestBoat);
} else {
Serial.print("Couldn't build and parse a ");
Serial.print(senList[i]);
Serial.print(" sentence, maybe because sprintf() doesn't work with %f.");
}
}
Serial.print("\nSome of the resulting data stored in GPS:\n\n");
GPS.showDataValue(NMEA_LAT);
GPS.showDataValue(NMEA_LON);
GPS.showDataValue(NMEA_AWA, 20); // show more history values, if history on
GPS.showDataValue(NMEA_AWA_SIN);
GPS.showDataValue(NMEA_AWA_COS);
GPS.showDataValue(NMEA_AWS);
GPS.showDataValue(NMEA_HDG);
GPS.showDataValue(NMEA_DEPTH);
Serial.print("\nThe AWA is: ");
Serial.print(GPS.get(NMEA_AWA));
Serial.print(" while the smoothed value is: ");
Serial.println(GPS.getSmoothed(NMEA_AWA));
#endif // NMEA_Extensions
}
}
void updateBoat() { // Fill up the boat values with
// some test data to use in build()
nmea_float_t t = millis() / 1000.;
nmea_float_t theta = t / 100.; // slow
nmea_float_t gamma = theta * 10; // faster
// add some data to the old Adafruit_GPS variables
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;
#ifdef NMEA_EXTENSIONS
// add some data to the new NMEA data values
Boat.newDataValue(NMEA_AWS, 10 + cos(theta));
Boat.newDataValue(NMEA_AWA, 180 * sin(gamma));
Boat.newDataValue(NMEA_VTW, Boat.speed + cos(gamma) / 3);
Boat.newDataValue(NMEA_DEPTH, 10 + cos(gamma) * 5);
Boat.newDataValue(NMEA_HDG, 180 * sin(gamma) + 180);
Boat.newDataValue(NMEA_HDT, 180 * cos(gamma) + 180);
Boat.newDataValue(NMEA_VMG, sin(gamma) * 3);
Boat.newDataValue(NMEA_VMGWP, cos(gamma) * 5);
#endif // NMEA_EXTENSIONS
}
#ifdef NMEA_EXTENSIONS
void addHistory(Adafruit_GPS *nmea) {
// Record integer history for HDOP, scaled by 10.0, offset by 0.0,
// every 15 seconds for the most recent 20 values.
nmea->initHistory(NMEA_HDOP, 10.0, 0.0, 15, 20);
nmea->initHistory(NMEA_COG, 10.0, 0.0, 1);
nmea->initHistory(NMEA_AWA, 10.0, 0.0, 1);
nmea->initHistory(NMEA_HDG, 10.0, 0.0, 3);
// Record pressure every 10 minutes, in Pa relative to 1 bar
nmea->initHistory(NMEA_BAROMETER, 1.0, -100000.0, 600);
nmea->initHistory(NMEA_DEPTH, 10.0, 0.0, 3);
}
#endif // NMEA_EXTENSIONS
@@ -0,0 +1,11 @@
// this sketch will allow you to bypass the Atmega chip
// and connect the GPS sensor directly to the USB/Serial
// chip converter.
// Connect VIN to +5V
// Connect GND to Ground
// Connect GPS RX (data into GPS) to Digital 0
// Connect GPS TX (data out from GPS) to Digital 1
void setup() {}
void loop() {}
@@ -0,0 +1,210 @@
#include <SPI.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <SD.h>
#include <avr/sleep.h>
// Ladyada's logger modified by Bill Greiman to use the SdFat library
//
// This code shows how to listen to the GPS module in an interrupt
// which allows the program to have more 'freedom' - just parse
// when a new NMEA sentence is available! Then access data when
// desired.
//
// Tested and works great with the Adafruit Ultimate GPS Shield
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
// Fllybob added 10 sec logging option
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
// 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
/* set to true to only log to SD when GPS has a fix, for debugging, keep it false */
#define LOG_FIXONLY false
// this keeps track of whether we're using the interrupt
// off by default!
#ifndef ESP8266 // Sadly not on ESP8266
bool usingInterrupt = false;
#endif
// Set the pins used
#define chipSelect 10
#define ledPin 13
File logfile;
// read a Hex value and return the decimal equivalent
uint8_t parseHex(char c) {
if (c < '0')
return 0;
if (c <= '9')
return c - '0';
if (c < 'A')
return 0;
if (c <= 'F')
return (c - 'A')+10;
}
// blink out an error code
void error(uint8_t errno) {
/*
if (SD.errorCode()) {
putstring("SD error: ");
Serial.print(card.errorCode(), HEX);
Serial.print(',');
Serial.println(card.errorData(), HEX);
}
*/
while(1) {
uint8_t i;
for (i=0; i<errno; i++) {
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
delay(100);
}
for (i=errno; i<10; i++) {
delay(200);
}
}
}
void setup() {
// for Leonardos, if you want to debug SD issues, uncomment this line
// to see serial output
//while (!Serial);
// 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("\r\nUltimate GPSlogger Shield");
pinMode(ledPin, OUTPUT);
// make sure that the default chip select pin is set to
// output, even if you don't use it:
pinMode(10, OUTPUT);
if (!SD.begin(chipSelect)) {
Serial.println("Card init. failed!");
error(2);
}
char filename[15];
strcpy(filename, "GPSLOG00.TXT");
for (uint8_t i = 0; i < 100; i++) {
filename[6] = '0' + i/10;
filename[7] = '0' + i%10;
// create if does not exist, do not open existing, write, sync after write
if (! SD.exists(filename)) {
break;
}
}
logfile = SD.open(filename, FILE_WRITE);
if( ! logfile ) {
Serial.print("Couldnt create ");
Serial.println(filename);
error(3);
}
Serial.print("Writing to ");
Serial.println(filename);
// connect to the GPS at the desired rate
GPS.begin(9600);
// 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 logging data, we don't suggest using anything but either RMC only or RMC+GGA
// to keep the log files at a reasonable size
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 100 millihertz (once every 10 seconds), 1Hz or 5Hz update rate
// Turn off updates on antenna status, if the firmware permits it
GPS.sendCommand(PGCMD_NOANTENNA);
// the nice thing about this code is you can have a timer0 interrupt go off
// every 1 millisecond, and read data from the GPS for you. that makes the
// loop code a heck of a lot easier!
#ifndef ESP8266 // Not on ESP8266
useInterrupt(true);
#endif
Serial.println("Ready!");
}
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
#ifndef ESP8266 // Not on ESP8266
ISR(TIMER0_COMPA_vect) {
char c = GPS.read();
// if you want to debug, this is a good time to do it!
#ifdef UDR0
if (GPSECHO)
if (c) UDR0 = c;
// writing direct to UDR0 is much much faster than Serial.print
// but only one character can be written at a time.
#endif
}
void useInterrupt(bool v) {
if (v) {
// Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function above
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
}
else {
// do not call the interrupt function COMPA anymore
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
#endif // ESP8266
void loop() {
if (! usingInterrupt) {
// read data from the GPS in the 'main loop'
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 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 trying to print out data
// Don't call lastNMEA more than once between parse calls! Calling lastNMEA
// will clear the received flag and can cause very subtle race conditions if
// new data comes in before parse is called again.
char *stringptr = GPS.lastNMEA();
if (!GPS.parse(stringptr)) // 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
// Sentence parsed!
Serial.println("OK");
if (LOG_FIXONLY && !GPS.fix) {
Serial.print("No Fix");
return;
}
// Rad. lets log it!
Serial.println("Log");
uint8_t stringsize = strlen(stringptr);
if (stringsize != logfile.write((uint8_t *)stringptr, stringsize)) //write the string to the SD file
error(4);
if (strstr(stringptr, "RMC") || strstr(stringptr, "GGA")) logfile.flush();
Serial.println();
}
}
+216
View File
@@ -0,0 +1,216 @@
#######################################
# Syntax Coloring Map For RWS_NMEA
#######################################
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification
#######################################
# Datatypes (KEYWORD1)
#######################################
Adafruit_GPS KEYWORD1
nmea_float_t KEYWORD1
nmea_history_t KEYWORD1
nmea_datavalue_t KEYWORD1
nmea_index_t KEYWORD1
nmea_check_t KEYWORD1
nmea_value_type_t KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
lastNMEA KEYWORD2
newNMEAreceived KEYWORD2
common_init KEYWORD2
sendCommand KEYWORD2
pause KEYWORD2
parse KEYWORD2
parseHex KEYWORD2
check KEYWORD2
isEmpty KEYWORD2
addChecksum KEYWORD2
boatAngle KEYWORD2
compassAngle KEYWORD2
secondsSinceFix KEYWORD2
secondsSinceTime KEYWORD2
secondsSinceDate KEYWORD2
resetSentTime KEYWORD2
wakeup KEYWORD2
standby KEYWORD2
onList KEYWORD2
parseStr KEYWORD2
parseCoord KEYWORD2
newDataValue KEYWORD2
initDataValue KEYWORD2
initHistory KEYWORD2
removeHistory KEYWORD2
showDataValue KEYWORD2
get KEYWORD2
getSmoothed KEYWORD2
isCompoundAngle KEYWORD2
waitForSentence KEYWORD2
LOCUS_StartLogger KEYWORD2
LOCUS_StopLogger KEYWORD2
LOCUS_ReadStatus KEYWORD2
build KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
seconds KEYWORD2
milliseconds KEYWORD2
latitude KEYWORD2
longitude KEYWORD2
latitude_fixed KEYWORD2
longitude_fixed KEYWORD2
latitudeDegrees KEYWORD2
longitudeDegrees KEYWORD2
lat KEYWORD2
lon KEYWORD2
geoidheight KEYWORD2
altitude KEYWORD2
speed KEYWORD2
angle KEYWORD2
magvariation KEYWORD2
HDOP KEYWORD2
VDOP KEYWORD2
PDOP KEYWORD2
mag KEYWORD2
fix KEYWORD2
fixquality KEYWORD2
fixquality_3d KEYWORD2
satellites KEYWORD2
LOCUS_serial KEYWORD2
LOCUS_records KEYWORD2
LOCUS_type KEYWORD2
LOCUS_mode KEYWORD2
LOCUS_config KEYWORD2
LOCUS_interval KEYWORD2
LOCUS_distance KEYWORD2
LOCUS_speed KEYWORD2
LOCUS_status KEYWORD2
LOCUS_percent KEYWORD2
val KEYWORD2
depthToKeel KEYWORD2
depthToTransducer KEYWORD2
toid KEYWORD2
fromid KEYWORD2
txtTXT KEYWORD2
txtTot KEYWORD2
txtID KEYWORD2
txtN KEYWORD2
thisCheck KEYWORD2
thisSource KEYWORD2
thisSentence KEYWORD2
lastSource KEYWORD2
lastSentence KEYWORD2
data KEYWORD2
lastHistory KEYWORD2
historyInterval KEYWORD2
scale KEYWORD2
offset KEYWORD2
latest KEYWORD2
smoothed KEYWORD2
lastUpdate KEYWORD2
response KEYWORD2
type KEYWORD2
ockam KEYWORD2
hist KEYWORD2
label KEYWORD2
unit KEYWORD2
fmt KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
NMEA_EXTENSIONS LITERAL1
NMEA_EXTRAS LITERAL1
USE_SW_SERIAL LITERAL1
GPS_DEFAULT_I2C_ADDR LITERAL1
GPS_MAX_I2C_TRANSFER LITERAL1
GPS_MAX_SPI_TRANSFER LITERAL1
MAXLINELENGTH LITERAL1
NMEA_BAD LITERAL1
NMEA_HAS_DOLLAR LITERAL1
NMEA_HAS_CHECKSUM LITERAL1
NMEA_HAS_NAME LITERAL1
NMEA_HAS_SOURCE LITERAL1
NMEA_HAS_SENTENCE LITERAL1
NMEA_HAS_SENTENCE_P LITERAL1
NMEA_SIMPLE_FLOAT LITERAL1
NMEA_COMPASS_ANGLE LITERAL1
NMEA_BOAT_ANGLE LITERAL1
NMEA_COMPASS_ANGLE_SIN LITERAL1
NMEA_BOAT_ANGLE_SIN LITERAL1
NMEA_DDMM LITERAL1
NMEA_HHMMSS LITERAL1
NMEA_MAX_WP_ID LITERAL1
NMEA_MAX_SENTENCE_ID LITERAL1
NMEA_MAX_SOURCE_ID LITERAL1
NMEA_N_HIST LITERAL1
DEG_RAD LITERAL1
NMEA_HDOP LITERAL1
NMEA_LAT LITERAL1
NMEA_LON LITERAL1
NMEA_LATWP LITERAL1
NMEA_LONWP LITERAL1
NMEA_SOG LITERAL1
NMEA_COG LITERAL1
NMEA_COG_SIN LITERAL1
NMEA_COG_COS LITERAL1
NMEA_COGWP LITERAL1
NMEA_XTE LITERAL1
NMEA_DISTWP LITERAL1
NMEA_AWA LITERAL1
NMEA_AWA_SIN LITERAL1
NMEA_AWA_COS LITERAL1
NMEA_AWS LITERAL1
NMEA_TWA LITERAL1
NMEA_TWA_SIN LITERAL1
NMEA_TWA_COS LITERAL1
NMEA_TWD LITERAL1
NMEA_TWD_SIN LITERAL1
NMEA_TWD_COS LITERAL1
NMEA_TWS LITERAL1
NMEA_VMG LITERAL1
NMEA_VMGWP LITERAL1
NMEA_HEEL LITERAL1
NMEA_PITCH LITERAL1
NMEA_HDG LITERAL1
NMEA_HDG_SIN LITERAL1
NMEA_HDG_COS LITERAL1
NMEA_HDT LITERAL1
NMEA_HDT_SIN LITERAL1
NMEA_HDT_COS LITERAL1
NMEA_VTW LITERAL1
NMEA_LOG LITERAL1
NMEA_LOGR LITERAL1
NMEA_DEPTH LITERAL1
NMEA_RPM_M1 LITERAL1
NMEA_TEMPERATURE_M1 LITERAL1
NMEA_PRESSURE_M1 LITERAL1
NMEA_VOLTAGE_M1 LITERAL1
NMEA_CURRENT_M1 LITERAL1
NMEA_RPM_M2 LITERAL1
NMEA_TEMPERATURE_M2 LITERAL1
NMEA_PRESSURE_M2 LITERAL1
NMEA_VOLTAGE_M2 LITERAL1
NMEA_CURRENT_M2 LITERAL1
NMEA_TEMPERATURE_AIR LITERAL1
NMEA_TEMPERATURE_WATER LITERAL1
NMEA_HUMIDITY LITERAL1
NMEA_BAROMETER LITERAL1
NMEA_USR_00 LITERAL1
NMEA_USR_01 LITERAL1
NMEA_USR_02 LITERAL1
NMEA_USR_03 LITERAL1
NMEA_USR_04 LITERAL1
NMEA_USR_05 LITERAL1
NMEA_USR_06 LITERAL1
NMEA_USR_07 LITERAL1
NMEA_USR_08 LITERAL1
NMEA_USR_09 LITERAL1
NMEA_USR_10 LITERAL1
NMEA_USR_11 LITERAL1
NMEA_USR_12 LITERAL1
NMEA_MAX_INDEX LITERAL1
@@ -0,0 +1,10 @@
name=Adafruit GPS Library
version=1.7.5
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=An interrupt-based GPS library for no-parsing-required use
paragraph=An interrupt-based GPS library for no-parsing-required use
category=Sensors
url=https://github.com/adafruit/Adafruit_GPS
architectures=*
depends=SD
@@ -0,0 +1,26 @@
Software License Agreement (BSD License)
Copyright (c) 2012, Adafruit Industries
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,621 @@
/**************************************************************************/
/*!
@file Adafruit_GPS.cpp
@mainpage Adafruit Ultimate GPS Breakout
@section intro Introduction
This is the Adafruit GPS library - the ultimate GPS library
for the ultimate GPS module!
Tested and works great with the Adafruit Ultimate GPS module
using MTK33x9 chipset
------> http://www.adafruit.com/products/746
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
@section author Author
Written by Limor Fried/Ladyada for Adafruit Industries.
@section license License
BSD license, check license.txt for more information
All text above must be included in any redistribution
*/
/**************************************************************************/
#include <Adafruit_GPS.h>
static bool strStartsWith(const char *str, const char *prefix);
/**************************************************************************/
/*!
@brief Start the HW or SW serial port
@param baud_or_i2caddr Baud rate if using serial, I2C address if using I2C
@returns True on successful hardware init, False on failure
*/
/**************************************************************************/
bool Adafruit_GPS::begin(uint32_t baud_or_i2caddr) {
#if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \
!defined(NO_SW_SERIAL)))
if (gpsSwSerial) {
gpsSwSerial->begin(baud_or_i2caddr);
}
#endif
if (gpsHwSerial) {
gpsHwSerial->begin(baud_or_i2caddr);
}
if (gpsI2C) {
gpsI2C->begin();
if (baud_or_i2caddr > 0x7F) {
_i2caddr = GPS_DEFAULT_I2C_ADDR;
} else {
_i2caddr = baud_or_i2caddr;
}
// A basic scanner, see if it ACK's
gpsI2C->beginTransmission(_i2caddr);
return (gpsI2C->endTransmission() == 0);
}
if (gpsSPI) {
gpsSPI->begin();
gpsSPI_settings = SPISettings(baud_or_i2caddr, MSBFIRST, SPI_MODE0);
if (gpsSPI_cs >= 0) {
pinMode(gpsSPI_cs, OUTPUT);
digitalWrite(gpsSPI_cs, HIGH);
}
}
delay(10);
return true;
}
/**************************************************************************/
/*!
@brief Constructor when using SoftwareSerial
@param ser Pointer to SoftwareSerial device
*/
/**************************************************************************/
#if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \
!defined(NO_SW_SERIAL)))
Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser) {
common_init(); // Set everything to common state, then...
gpsSwSerial = ser; // ...override gpsSwSerial with value passed.
}
#endif
/**************************************************************************/
/*!
@brief Constructor when using HardwareSerial
@param ser Pointer to a HardwareSerial object
*/
/**************************************************************************/
Adafruit_GPS::Adafruit_GPS(HardwareSerial *ser) {
common_init(); // Set everything to common state, then...
gpsHwSerial = ser; // ...override gpsHwSerial with value passed.
}
/**************************************************************************/
/*!
@brief Constructor when using Stream
@param data Pointer to a Stream object
*/
/**************************************************************************/
Adafruit_GPS::Adafruit_GPS(Stream *data) {
common_init(); // Set everything to common state, then...
gpsStream = data; // ...override gpsStream with value passed.
}
/**************************************************************************/
/*!
@brief Constructor when using I2C
@param theWire Pointer to an I2C TwoWire object
*/
/**************************************************************************/
Adafruit_GPS::Adafruit_GPS(TwoWire *theWire) {
common_init(); // Set everything to common state, then...
gpsI2C = theWire; // ...override gpsI2C
}
/**************************************************************************/
/*!
@brief Constructor when using SPI
@param theSPI Pointer to an SPI device object
@param cspin The pin connected to the GPS CS, can be -1 if unused
*/
/**************************************************************************/
Adafruit_GPS::Adafruit_GPS(SPIClass *theSPI, int8_t cspin) {
common_init(); // Set everything to common state, then...
gpsSPI = theSPI; // ...override gpsSPI
gpsSPI_cs = cspin;
}
/**************************************************************************/
/*!
@brief Constructor when there are no communications attached
*/
/**************************************************************************/
Adafruit_GPS::Adafruit_GPS() {
common_init(); // Set everything to common state, then...
noComms = true;
}
/**************************************************************************/
/*!
@brief Initialization code used by all constructor types
*/
/**************************************************************************/
void Adafruit_GPS::common_init(void) {
#if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \
!defined(NO_SW_SERIAL)))
gpsSwSerial = NULL; // Set both to NULL, then override correct
#endif
gpsHwSerial = NULL; // port pointer in corresponding constructor
gpsStream = NULL; // port pointer in corresponding constructor
gpsI2C = NULL;
gpsSPI = NULL;
recvdflag = false;
paused = false;
lineidx = 0;
currentline = line1;
lastline = line2;
hour = minute = seconds = year = month = day = fixquality = fixquality_3d =
satellites = antenna = 0; // uint8_t
lat = lon = mag = 0; // char
fix = false; // bool
milliseconds = 0; // uint16_t
latitude = longitude = geoidheight = altitude = speed = angle = magvariation =
HDOP = VDOP = PDOP = 0.0; // nmea_float_t
#ifdef NMEA_EXTENSIONS
data_init();
#endif
}
/**************************************************************************/
/*!
@brief Destroy the object.
@return none
*/
/**************************************************************************/
Adafruit_GPS::~Adafruit_GPS() {
#ifdef NMEA_EXTENSIONS
for (int i = 0; i < (int)NMEA_MAX_INDEX; i++)
removeHistory((nmea_index_t)i); // to free any history mallocs
#endif
}
/**************************************************************************/
/*!
@brief How many bytes are available to read - part of 'Print'-class
functionality
@return Bytes available, 0 if none
*/
/**************************************************************************/
size_t Adafruit_GPS::available(void) {
if (paused)
return 0;
#if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \
!defined(NO_SW_SERIAL)))
if (gpsSwSerial) {
return gpsSwSerial->available();
}
#endif
if (gpsHwSerial) {
return gpsHwSerial->available();
}
if (gpsStream) {
return gpsStream->available();
}
if (gpsI2C || gpsSPI) {
return 1; // I2C/SPI doesnt have 'availability' so always has a byte at
// least to read!
}
return 0;
}
/**************************************************************************/
/*!
@brief Write a byte to the underlying transport - part of 'Print'-class
functionality
@param c A single byte to send
@return Bytes written - 1 on success, 0 on failure
*/
/**************************************************************************/
size_t Adafruit_GPS::write(uint8_t c) {
#if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \
!defined(NO_SW_SERIAL)))
if (gpsSwSerial) {
return gpsSwSerial->write(c);
}
#endif
if (gpsHwSerial) {
return gpsHwSerial->write(c);
}
if (gpsStream) {
return gpsStream->write(c);
}
if (gpsI2C) {
gpsI2C->beginTransmission(_i2caddr);
if (gpsI2C->write(c) != 1) {
return 0;
}
if (gpsI2C->endTransmission(true) == 0) {
return 1;
}
}
if (gpsSPI) {
gpsSPI->beginTransaction(gpsSPI_settings);
if (gpsSPI_cs >= 0) {
digitalWrite(gpsSPI_cs, LOW);
}
c = gpsSPI->transfer(c);
if (gpsSPI_cs >= 0) {
digitalWrite(gpsSPI_cs, HIGH);
}
gpsSPI->endTransaction();
return 1;
}
return 0;
}
/**************************************************************************/
/*!
@brief Read one character from the GPS device.
Call very frequently and multiple times per opportunity or the buffer
may overflow if there are frequent NMEA sentences. An 82 character NMEA
sentence 10 times per second will require 820 calls per second, and
once a loop() may not be enough. Check for newNMEAreceived() after at
least every 10 calls, or you may miss some short sentences.
@return The character that we received, or 0 if nothing was available
*/
/**************************************************************************/
char Adafruit_GPS::read(void) {
static uint32_t firstChar = 0; // first character received in current sentence
uint32_t tStart = millis(); // as close as we can get to time char was sent
char c = 0;
if (paused || noComms)
return c;
#if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \
!defined(NO_SW_SERIAL)))
if (gpsSwSerial) {
if (!gpsSwSerial->available())
return c;
c = gpsSwSerial->read();
}
#endif
if (gpsHwSerial) {
if (!gpsHwSerial->available())
return c;
c = gpsHwSerial->read();
}
if (gpsStream) {
if (!gpsStream->available())
return c;
c = gpsStream->read();
}
if (gpsI2C) {
if (_buff_idx <= _buff_max) {
c = _i2cbuffer[_buff_idx];
_buff_idx++;
} else {
// refill the buffer!
if (gpsI2C->requestFrom((uint8_t)0x10, (uint8_t)GPS_MAX_I2C_TRANSFER,
(uint8_t) true) == GPS_MAX_I2C_TRANSFER) {
// got data!
_buff_max = 0;
char curr_char = 0;
for (int i = 0; i < GPS_MAX_I2C_TRANSFER; i++) {
curr_char = gpsI2C->read();
if ((curr_char == 0x0A) && (last_char != 0x0D)) {
// skip duplicate 0x0A's - but keep as part of a CRLF
continue;
}
last_char = curr_char;
_i2cbuffer[_buff_max] = curr_char;
_buff_max++;
}
_buff_max--; // back up to the last valid slot
if ((_buff_max == 0) && (_i2cbuffer[0] == 0x0A)) {
_buff_max = -1; // ahh there was nothing to read after all
}
_buff_idx = 0;
}
return c;
}
}
if (gpsSPI) {
do {
gpsSPI->beginTransaction(gpsSPI_settings);
if (gpsSPI_cs >= 0) {
digitalWrite(gpsSPI_cs, LOW);
}
c = gpsSPI->transfer(0xFF);
if (gpsSPI_cs >= 0) {
digitalWrite(gpsSPI_cs, HIGH);
}
gpsSPI->endTransaction();
// skip duplicate 0x0A's - but keep as part of a CRLF
} while (((c == 0x0A) && (last_char != 0x0D)) ||
(!isprint(c) && !isspace(c)));
last_char = c;
}
// Serial.print(c);
currentline[lineidx] = c;
lineidx = lineidx + 1;
if (lineidx >= MAXLINELENGTH)
lineidx = MAXLINELENGTH -
1; // ensure there is someplace to put the next received character
if (c == '\n') {
currentline[lineidx] = 0;
if (currentline == line1) {
currentline = line2;
lastline = line1;
} else {
currentline = line1;
lastline = line2;
}
// Serial.println("----");
// Serial.println((char *)lastline);
// Serial.println("----");
lineidx = 0;
recvdflag = true;
recvdTime = millis(); // time we got the end of the string
sentTime = firstChar;
firstChar = 0; // there are no characters yet
return c; // wait until next character to set time
}
if (firstChar == 0)
firstChar = tStart;
return c;
}
/**************************************************************************/
/*!
@brief Send a command to the GPS device
@param str Pointer to a string holding the command to send
*/
/**************************************************************************/
void Adafruit_GPS::sendCommand(const char *str) { println(str); }
/**************************************************************************/
/*!
@brief Check to see if a new NMEA line has been received
@return True if received, false if not
*/
/**************************************************************************/
bool Adafruit_GPS::newNMEAreceived(void) { return recvdflag; }
/**************************************************************************/
/*!
@brief Pause/unpause receiving new data
@param p True = pause, false = unpause
*/
/**************************************************************************/
void Adafruit_GPS::pause(bool p) { paused = p; }
/**************************************************************************/
/*!
@brief Returns the last NMEA line received and unsets the received flag
@return Pointer to the last line string
*/
/**************************************************************************/
char *Adafruit_GPS::lastNMEA(void) {
recvdflag = false;
return (char *)lastline;
}
/**************************************************************************/
/*!
@brief Wait for a specified sentence from the device
@param wait4me Pointer to a string holding the desired response
@param max How long to wait, default is MAXWAITSENTENCE
@param usingInterrupts True if using interrupts to read from the GPS
(default is false)
@return True if we got what we wanted, false otherwise
*/
/**************************************************************************/
bool Adafruit_GPS::waitForSentence(const char *wait4me, uint8_t max,
bool usingInterrupts) {
uint8_t i = 0;
while (i < max) {
if (!usingInterrupts)
read();
if (newNMEAreceived()) {
char *nmea = lastNMEA();
i++;
if (strStartsWith(nmea, wait4me))
return true;
}
}
return false;
}
/**************************************************************************/
/*!
@brief Start the LOCUS logger
@return True on success, false if it failed
*/
/**************************************************************************/
bool Adafruit_GPS::LOCUS_StartLogger(void) {
sendCommand(PMTK_LOCUS_STARTLOG);
recvdflag = false;
return waitForSentence(PMTK_LOCUS_STARTSTOPACK);
}
/**************************************************************************/
/*!
@brief Stop the LOCUS logger
@return True on success, false if it failed
*/
/**************************************************************************/
bool Adafruit_GPS::LOCUS_StopLogger(void) {
sendCommand(PMTK_LOCUS_STOPLOG);
recvdflag = false;
return waitForSentence(PMTK_LOCUS_STARTSTOPACK);
}
/**************************************************************************/
/*!
@brief Read the logger status
@return True if we read the data, false if there was no response
*/
/**************************************************************************/
bool Adafruit_GPS::LOCUS_ReadStatus(void) {
sendCommand(PMTK_LOCUS_QUERY_STATUS);
if (!waitForSentence("$PMTKLOG"))
return false;
char *response = lastNMEA();
uint16_t parsed[10];
uint8_t i;
for (i = 0; i < 10; i++)
parsed[i] = -1;
response = strchr(response, ',');
for (i = 0; i < 10; i++) {
if (!response || (response[0] == 0) || (response[0] == '*'))
break;
response++;
parsed[i] = 0;
while ((response[0] != ',') && (response[0] != '*') && (response[0] != 0)) {
parsed[i] *= 10;
char c = response[0];
if (isDigit(c))
parsed[i] += c - '0';
else
parsed[i] = c;
response++;
}
}
LOCUS_serial = parsed[0];
LOCUS_type = parsed[1];
if (isAlpha(parsed[2])) {
parsed[2] = parsed[2] - 'a' + 10;
}
LOCUS_mode = parsed[2];
LOCUS_config = parsed[3];
LOCUS_interval = parsed[4];
LOCUS_distance = parsed[5];
LOCUS_speed = parsed[6];
LOCUS_status = !parsed[7];
LOCUS_records = parsed[8];
LOCUS_percent = parsed[9];
return true;
}
/**************************************************************************/
/*!
@brief Standby Mode Switches
@return False if already in standby, true if it entered standby
*/
/**************************************************************************/
bool Adafruit_GPS::standby(void) {
if (inStandbyMode) {
return false; // Returns false if already in standby mode, so that you do
// not wake it up by sending commands to GPS
} else {
inStandbyMode = true;
sendCommand(PMTK_STANDBY);
// return waitForSentence(PMTK_STANDBY_SUCCESS); // don't seem to be fast
// enough to catch the message, or something else just is not working
return true;
}
}
/**************************************************************************/
/*!
@brief Wake the sensor up
@return True if woken up, false if not in standby or failed to wake
*/
/**************************************************************************/
bool Adafruit_GPS::wakeup(void) {
if (inStandbyMode) {
inStandbyMode = false;
sendCommand(""); // send byte to wake it up
return waitForSentence(PMTK_AWAKE);
} else {
return false; // Returns false if not in standby mode, nothing to wakeup
}
}
/**************************************************************************/
/*!
@brief Time in seconds since the last position fix was obtained. The
time returned is limited to 2^32 milliseconds, which is about 49.7 days.
It will wrap around to zero if no position fix is received
for this long.
@return nmea_float_t value in seconds since last fix.
*/
/**************************************************************************/
nmea_float_t Adafruit_GPS::secondsSinceFix() {
return (millis() - lastFix) / 1000.;
}
/**************************************************************************/
/*!
@brief Time in seconds since the last GPS time was obtained. The time
returned is limited to 2^32 milliseconds, which is about 49.7 days. It
will wrap around to zero if no GPS time is received for this long.
@return nmea_float_t value in seconds since last GPS time.
*/
/**************************************************************************/
nmea_float_t Adafruit_GPS::secondsSinceTime() {
return (millis() - lastTime) / 1000.;
}
/**************************************************************************/
/*!
@brief Time in seconds since the last GPS date was obtained. The time
returned is limited to 2^32 milliseconds, which is about 49.7 days. It
will wrap around to zero if no GPS date is received for this long.
@return nmea_float_t value in seconds since last GPS date.
*/
/**************************************************************************/
nmea_float_t Adafruit_GPS::secondsSinceDate() {
return (millis() - lastDate) / 1000.;
}
/**************************************************************************/
/*!
@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 Checks whether a string starts with a specified prefix
@param str Pointer to a string
@param prefix Pointer to the prefix
@return True if str starts with prefix, false otherwise
*/
/**************************************************************************/
static bool strStartsWith(const char *str, const char *prefix) {
while (*prefix) {
if (*prefix++ != *str++)
return false;
}
return true;
}
@@ -0,0 +1,317 @@
/**************************************************************************/
/*!
@file Adafruit_GPS.h
This is the Adafruit GPS library - the ultimate GPS library
for the ultimate GPS module!
Tested and works great with the Adafruit Ultimate GPS module
using MTK33x9 chipset
------> http://www.adafruit.com/products/746
Pick one up today at the Adafruit electronics shop
and help support open source hardware & software! -ada
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution
*/
/**************************************************************************/
#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 NMEA_EXTRAS // inject on the compile command line to force extensions
#ifndef ARDUINO_ARCH_AVR
#define NMEA_EXTENSIONS ///< if defined will include more NMEA sentences
#endif
#else
#if (NMEA_EXTRAS > 0)
#define NMEA_EXTENSIONS ///< if defined will include more NMEA sentences
#endif
#endif
#if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \
!defined(NO_SW_SERIAL)))
#define USE_SW_SERIAL ///< insert line `#define NO_SW_SERIAL` before this header
///< if you don't want to include software serial in the
#endif ///< library
#define GPS_DEFAULT_I2C_ADDR \
0x10 ///< The default address for I2C transport of GPS data
#define GPS_MAX_I2C_TRANSFER \
32 ///< The max number of bytes we'll try to read at once
#define GPS_MAX_SPI_TRANSFER \
100 ///< The max number of bytes we'll try to read at once
#define MAXLINELENGTH 120 ///< how long are max NMEA lines to parse?
#define NMEA_MAX_SENTENCE_ID \
20 ///< maximum length of a sentence ID name, including terminating 0
#define NMEA_MAX_SOURCE_ID \
3 ///< maximum length of a source ID name, including terminating 0
#include "Arduino.h"
#ifdef USE_SW_SERIAL
#include <SoftwareSerial.h>
#endif
#include <Adafruit_PMTK.h>
#include <NMEA_data.h>
#include <SPI.h>
#include <Wire.h>
/// type for resulting code from running check()
typedef enum {
NMEA_BAD = 0, ///< passed none of the checks
NMEA_HAS_DOLLAR =
1, ///< has a dollar sign or exclamation mark in the first position
NMEA_HAS_CHECKSUM = 2, ///< has a valid checksum at the end
NMEA_HAS_NAME = 4, ///< there is a token after the $ followed by a comma
NMEA_HAS_SOURCE = 10, ///< has a recognized source ID
NMEA_HAS_SENTENCE = 20, ///< has a recognized sentence ID
NMEA_HAS_SENTENCE_P = 40 ///< has a recognized parseable sentence ID
} nmea_check_t;
/**************************************************************************/
/*!
@brief The GPS class
*/
class Adafruit_GPS : public Print {
public:
// Adafruit_GPS.cpp
bool begin(uint32_t baud_or_i2caddr);
#ifdef USE_SW_SERIAL
Adafruit_GPS(SoftwareSerial *ser); // Constructor when using SoftwareSerial
#endif
Adafruit_GPS(HardwareSerial *ser); // Constructor when using HardwareSerial
Adafruit_GPS(Stream *data); // Constructor when using Stream
Adafruit_GPS(TwoWire *theWire); // Constructor when using I2C
Adafruit_GPS(SPIClass *theSPI, int8_t cspin); // Constructor when using SPI
Adafruit_GPS(); // Constructor for no communications, just data storage
void common_init(void);
virtual ~Adafruit_GPS();
size_t available(void);
size_t write(uint8_t);
char read(void);
void sendCommand(const char *);
bool newNMEAreceived();
void pause(bool b);
char *lastNMEA(void);
bool waitForSentence(const char *wait, uint8_t max = MAXWAITSENTENCE,
bool usingInterrupts = false);
bool LOCUS_StartLogger(void);
bool LOCUS_StopLogger(void);
bool LOCUS_ReadStatus(void);
bool standby(void);
bool wakeup(void);
nmea_float_t secondsSinceFix();
nmea_float_t secondsSinceTime();
nmea_float_t secondsSinceDate();
void resetSentTime();
// NMEA_parse.cpp
bool parse(char *);
bool check(char *nmea);
bool onList(char *nmea, const char **list);
uint8_t parseHex(char c);
// NMEA_build.cpp
#ifdef NMEA_EXTENSIONS
char *build(char *nmea, const char *thisSource, const char *thisSentence,
char ref = 'R', bool noCRLF = false);
#endif
void addChecksum(char *buff);
// NMEA_data.cpp
void newDataValue(nmea_index_t tag, nmea_float_t v);
#ifdef NMEA_EXTENSIONS
nmea_float_t get(nmea_index_t idx);
nmea_float_t getSmoothed(nmea_index_t idx);
void initDataValue(nmea_index_t idx, char *label = NULL, char *fmt = NULL,
char *unit = NULL, unsigned long response = 0,
nmea_value_type_t type = NMEA_SIMPLE_FLOAT);
nmea_history_t *initHistory(nmea_index_t idx, nmea_float_t scale = 10.0,
nmea_float_t offset = 0.0,
unsigned historyInterval = 20,
unsigned historyN = 192);
void removeHistory(nmea_index_t idx);
void showDataValue(nmea_index_t idx, int n = 7);
bool isCompoundAngle(nmea_index_t idx);
#endif
nmea_float_t boatAngle(nmea_float_t s, nmea_float_t c);
nmea_float_t compassAngle(nmea_float_t s, nmea_float_t c);
int thisCheck = 0; ///< the results of the check on the current sentence
char thisSource[NMEA_MAX_SOURCE_ID] = {
0}; ///< the first two letters of the current sentence, e.g. WI, GP
char thisSentence[NMEA_MAX_SENTENCE_ID] = {
0}; ///< the next three letters of the current sentence, e.g. GLL, RMC
char lastSource[NMEA_MAX_SOURCE_ID] = {
0}; ///< the results of the check on the most recent successfully parsed
///< sentence
char lastSentence[NMEA_MAX_SENTENCE_ID] = {
0}; ///< the next three letters of the most recent successfully parsed
///< sentence, e.g. GLL, RMC
uint8_t hour; ///< GMT hours
uint8_t minute; ///< GMT minutes
uint8_t seconds; ///< GMT seconds
uint16_t milliseconds; ///< GMT milliseconds
uint8_t year; ///< GMT year
uint8_t month; ///< GMT month
uint8_t day; ///< GMT day
nmea_float_t latitude; ///< Floating point latitude value in degrees/minutes
///< as received from the GPS (DDMM.MMMM)
nmea_float_t longitude; ///< Floating point longitude value in degrees/minutes
///< as received from the GPS (DDDMM.MMMM)
/** Fixed point latitude and longitude value with degrees stored in units of
1/10000000 of a degree. See pull #13 for more details:
https://github.com/adafruit/Adafruit-GPS-Library/pull/13 */
int32_t latitude_fixed; ///< Fixed point latitude in decimal degrees.
///< Divide by 10000000.0 to get a double.
int32_t longitude_fixed; ///< Fixed point longitude in decimal degrees
///< Divide by 10000000.0 to get a double.
nmea_float_t latitudeDegrees; ///< Latitude in decimal degrees
nmea_float_t longitudeDegrees; ///< Longitude in decimal degrees
nmea_float_t geoidheight; ///< Diff between geoid height and WGS84 height
nmea_float_t altitude; ///< Altitude in meters above MSL
nmea_float_t speed; ///< Current speed over ground in knots
nmea_float_t angle; ///< Course in degrees from true north
nmea_float_t magvariation; ///< Magnetic variation in degrees (vs. true north)
nmea_float_t HDOP; ///< Horizontal Dilution of Precision - relative accuracy
///< of horizontal position
nmea_float_t VDOP; ///< Vertical Dilution of Precision - relative accuracy
///< of vertical position
nmea_float_t 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
bool 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
uint8_t antenna; ///< Antenna that is used (from PGTOP)
uint16_t LOCUS_serial; ///< Log serial number
uint16_t LOCUS_records; ///< Log number of data record
uint8_t LOCUS_type; ///< Log type, 0: Overlap, 1: FullStop
uint8_t LOCUS_mode; ///< Logging mode, 0x08 interval logger
uint8_t LOCUS_config; ///< Contents of configuration
uint8_t LOCUS_interval; ///< Interval setting
uint8_t LOCUS_distance; ///< Distance setting
uint8_t LOCUS_speed; ///< Speed setting
uint8_t LOCUS_status; ///< 0: Logging, 1: Stop logging
uint8_t LOCUS_percent; ///< Log life used percentage
#ifdef NMEA_EXTENSIONS
// NMEA additional public variables
nmea_datavalue_t
val[NMEA_MAX_INDEX]; ///< an array of data value structs, val[0] = most
///< recent HDOP so that ockam indexing works
nmea_float_t depthToKeel =
2.4; ///< depth from surface to bottom of keel in metres
nmea_float_t depthToTransducer =
0.0; ///< depth of transducer below the surface in metres
char toID[NMEA_MAX_WP_ID] = {
0}; ///< id of waypoint going to on this segment of the route
char fromID[NMEA_MAX_WP_ID] = {
0}; ///< id of waypoint coming from on this segment of the route
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:
// void parseLat(char *);
// bool parseLatDir(char *);
// void parseLon(char *);
// bool parseLonDir(char *);
// NMEA_data.cpp
void data_init();
// NMEA_parse.cpp
const char *tokenOnList(char *token, const char **list);
bool parseCoord(char *p, nmea_float_t *angleDegrees = NULL,
nmea_float_t *angle = NULL, int32_t *angle_fixed = NULL,
char *dir = NULL);
char *parseStr(char *buff, char *p, int n);
bool parseTime(char *);
bool parseFix(char *);
bool parseAntenna(char *);
bool isEmpty(char *pStart);
// used by check() for validity tests, room for future expansion
const char *sources[7] = {"II", "WI", "GP", "PG",
"GN", "P", "ZZZ"}; ///< valid source ids
#ifdef NMEA_EXTENSIONS
const char *sentences_parsed[21] = {"GGA", "GLL", "GSA", "RMC", "DBT", "HDM",
"HDT", "MDA", "MTW", "MWV", "RMB", "TOP",
"TXT", "VHW", "VLW", "VPW", "VWR", "WCV",
"XTE", "ZZZ"}; ///< parseable sentence ids
const char *sentences_known[15] = {
"APB", "DPT", "GSV", "HDG", "MWD", "ROT",
"RPM", "RSA", "VDR", "VTG", "ZDA", "ZZZ"}; ///< known, but not parseable
#else // make the lists short to save memory
const char *sentences_parsed[6] = {"GGA", "GLL", "GSA", "RMC",
"TOP", "ZZZ"}; ///< parseable sentence ids
const char *sentences_known[4] = {"DBT", "HDM", "HDT",
"ZZZ"}; ///< known, but not parseable
#endif
// Make all of these times far in the past by setting them near the middle of
// the millis() range. Timing assumes that sentences are parsed promptly.
uint32_t lastUpdate =
2000000000L; ///< millis() when last full sentence successfully parsed
uint32_t lastFix = 2000000000L; ///< millis() when last fix received
uint32_t lastTime = 2000000000L; ///< millis() when last time received
uint32_t lastDate = 2000000000L; ///< millis() when last date received
uint32_t recvdTime =
2000000000L; ///< millis() when last full sentence received
uint32_t sentTime = 2000000000L; ///< millis() when first character of last
///< full sentence received
bool paused;
uint8_t parseResponse(char *response);
#ifdef USE_SW_SERIAL
SoftwareSerial *gpsSwSerial;
#endif
bool noComms = false;
HardwareSerial *gpsHwSerial;
Stream *gpsStream;
TwoWire *gpsI2C;
SPIClass *gpsSPI;
int8_t gpsSPI_cs = -1;
SPISettings gpsSPI_settings =
SPISettings(1000000, MSBFIRST, SPI_MODE0); // default
char _spibuffer[GPS_MAX_SPI_TRANSFER]; // for when we write data, we need to
// read it too!
uint8_t _i2caddr;
char _i2cbuffer[GPS_MAX_I2C_TRANSFER];
int8_t _buff_max = -1, _buff_idx = 0;
char last_char = 0;
volatile char line1[MAXLINELENGTH]; ///< We double buffer: read one line in
///< and leave one for the main program
volatile char line2[MAXLINELENGTH]; ///< Second buffer
volatile uint8_t lineidx = 0; ///< our index into filling the current line
volatile char *currentline; ///< Pointer to current line buffer
volatile char *lastline; ///< Pointer to previous line buffer
volatile bool recvdflag; ///< Received flag
volatile bool inStandbyMode; ///< In standby flag
};
/**************************************************************************/
#endif
@@ -0,0 +1,116 @@
/**************************************************************************/
/*!
@file Adafruit_PMTK.h
This is the Adafruit GPS library - the ultimate GPS library
for the ultimate GPS module!
Tested and works great with the Adafruit Ultimate GPS module
using MTK33x9 chipset
------> http://www.adafruit.com/products/746
Pick one up today at the Adafruit electronics shop
and help support open source hardware & software! -ada
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution
*/
/**************************************************************************/
#ifndef _ADAFRUIT_PMTK_H
#define _ADAFRUIT_PMTK_H
/**************************************************************************/
/**
Different commands to set the update rate from once a second (1 Hz) to 10 times
a second (10Hz) Note that these only control the rate at which the position is
echoed, to actually speed up the position fix you must also send one of the
position fix rate commands below too. */
#define PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ \
"$PMTK220,10000*2F" ///< Once every 10 seconds, 100 millihertz.
#define PMTK_SET_NMEA_UPDATE_200_MILLIHERTZ \
"$PMTK220,5000*1B" ///< Once every 5 seconds, 200 millihertz.
#define PMTK_SET_NMEA_UPDATE_1HZ "$PMTK220,1000*1F" ///< 1 Hz
#define PMTK_SET_NMEA_UPDATE_2HZ "$PMTK220,500*2B" ///< 2 Hz
#define PMTK_SET_NMEA_UPDATE_5HZ "$PMTK220,200*2C" ///< 5 Hz
#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F" ///< 10 Hz
// Position fix update rate commands.
#define PMTK_API_SET_FIX_CTL_100_MILLIHERTZ \
"$PMTK300,10000,0,0,0,0*2C" ///< Once every 10 seconds, 100 millihertz.
#define PMTK_API_SET_FIX_CTL_200_MILLIHERTZ \
"$PMTK300,5000,0,0,0,0*18" ///< Once every 5 seconds, 200 millihertz.
#define PMTK_API_SET_FIX_CTL_1HZ "$PMTK300,1000,0,0,0,0*1C" ///< 1 Hz
#define PMTK_API_SET_FIX_CTL_5HZ "$PMTK300,200,0,0,0,0*2F" ///< 5 Hz
// Can't fix position faster than 5 times a second!
#define PMTK_SET_BAUD_115200 "$PMTK251,115200*1F" ///< 115200 bps
#define PMTK_SET_BAUD_57600 "$PMTK251,57600*2C" ///< 57600 bps
#define PMTK_SET_BAUD_9600 "$PMTK251,9600*17" ///< 9600 bps
#define PMTK_SET_NMEA_OUTPUT_GLLONLY \
"$PMTK314,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on only the
///< GPGLL sentence
#define PMTK_SET_NMEA_OUTPUT_RMCONLY \
"$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on only the
///< GPRMC sentence
#define PMTK_SET_NMEA_OUTPUT_VTGONLY \
"$PMTK314,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on only the
///< GPVTG
#define PMTK_SET_NMEA_OUTPUT_GGAONLY \
"$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on just the
///< GPGGA
#define PMTK_SET_NMEA_OUTPUT_GSAONLY \
"$PMTK314,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on just the
///< GPGSA
#define PMTK_SET_NMEA_OUTPUT_GSVONLY \
"$PMTK314,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on just the
///< GPGSV
#define PMTK_SET_NMEA_OUTPUT_RMCGGA \
"$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" ///< turn on GPRMC and
///< GPGGA
#define PMTK_SET_NMEA_OUTPUT_RMCGGAGSA \
"$PMTK314,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on GPRMC, GPGGA
///< and GPGSA
#define PMTK_SET_NMEA_OUTPUT_ALLDATA \
"$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28" ///< turn on ALL THE DATA
#define PMTK_SET_NMEA_OUTPUT_OFF \
"$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" ///< turn off output
// to generate your own sentences, check out the MTK command datasheet and use a
// checksum calculator such as the awesome
// http://www.hhhh.org/wiml/proj/nmeaxor.html
#define PMTK_LOCUS_STARTLOG "$PMTK185,0*22" ///< Start logging data
#define PMTK_LOCUS_STOPLOG "$PMTK185,1*23" ///< Stop logging data
#define PMTK_LOCUS_STARTSTOPACK \
"$PMTK001,185,3*3C" ///< Acknowledge the start or stop command
#define PMTK_LOCUS_QUERY_STATUS "$PMTK183*38" ///< Query the logging status
#define PMTK_LOCUS_ERASE_FLASH "$PMTK184,1*22" ///< Erase the log flash data
#define LOCUS_OVERLAP \
0 ///< If flash is full, log will overwrite old data with new logs
#define LOCUS_FULLSTOP 1 ///< If flash is full, logging will stop
#define PMTK_ENABLE_SBAS \
"$PMTK313,1*2E" ///< Enable search for SBAS satellite (only works with 1Hz
///< output rate)
#define PMTK_ENABLE_WAAS "$PMTK301,2*2E" ///< Use WAAS for DGPS correction data
#define PMTK_STANDBY \
"$PMTK161,0*28" ///< standby command & boot successful message
#define PMTK_STANDBY_SUCCESS "$PMTK001,161,3*36" ///< Not needed currently
#define PMTK_AWAKE "$PMTK010,002*2D" ///< Wake up
#define PMTK_Q_RELEASE "$PMTK605*31" ///< ask for the release and version
#define PGCMD_ANTENNA \
"$PGCMD,33,1*6C" ///< request for updates on antenna status
#define PGCMD_NOANTENNA "$PGCMD,33,0*6D" ///< don't show antenna status messages
#define MAXWAITSENTENCE \
10 ///< how long to wait when we're looking for a response
/**************************************************************************/
#endif
@@ -0,0 +1,619 @@
/**************************************************************************/
/*!
@file NMEA_build.cpp
This is the Adafruit GPS library - the ultimate GPS library
for the ultimate GPS module!
Tested and works great with the Adafruit Ultimate GPS module
using MTK33x9 chipset
------> http://www.adafruit.com/products/746
Pick one up today at the Adafruit electronics shop
and help support open source hardware & software! -ada
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
@author Limor Fried/Ladyada for Adafruit Industries.
@copyright BSD license, check license.txt for more information
All text above must be included in any redistribution
*/
/**************************************************************************/
#include <Adafruit_GPS.h>
#ifdef NMEA_EXTENSIONS
/**************************************************************************/
/*!
@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.
Floating point arguments to sprintf() are explicitly cast to double to
avoid warnings in some compilers.
build() adds Carriage Return and Line Feed to sentences to conform to
NMEA-183, so send your output with a print, not a println.
The resulting sentence may be corrupted if the input data is corrupt.
In particular, the sentence will be truncated if any of the character
data is 0, e.g. if lat is not set to 'N' or 'S'.
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)
@param noCRLF set true to disable adding CR/LF to comply with NMEA-183
@return Pointer to sentence if successful, NULL if fails
*/
/**************************************************************************/
char *Adafruit_GPS::build(char *nmea, const char *thisSource,
const char *thisSentence, char ref, bool noCRLF) {
sprintf(nmea, "%6.2f",
(double)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,,",
(double)hour * 10000L + minute * 100L + seconds +
milliseconds / 1000.,
(double)latitude, lat, (double)longitude, lon, fixquality,
satellites, (double)HDOP, (double)altitude, (double)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", (double)latitude, lat,
(double)longitude, lon,
(double)hour * 10000L + minute * 100L + seconds +
milliseconds / 1000.);
} else if (!strcmp(thisSentence, "GSA")) { //*****************************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",
(double)hour * 10000L + minute * 100L + seconds +
milliseconds / 1000.,
(double)latitude, lat, (double)longitude, lon, (double)speed,
(double)angle, day * 10000 + month * 100 + year,
(double)magvariation, mag);
} else if (!strcmp(thisSentence, "APB")) { //*****************************APB
// APB Autopilot Sentence "B"
// 13 15
// 1 2 3 4 5 6 7 8 9 10 11 12 | 14 |
// | | | | | | | | | | | | | | |
//$--APB,A,A,x.x,a,N,A,A,x.x,a,c--c,x.x,a,x.x,a*hh
// 1) Status
// V = LORAN-C Blink or SNR warning
// A = general warning flag or other navigation systems when a reliable
// fix is not available
// 2) Status
// V = Loran-C Cycle Lock warning flag
// A = OK or not used
// 3) Cross Track Error Magnitude
// 4) Direction to steer, L or R
// 5) Cross Track Units, N = Nautical Miles
// 6) Status
// A = Arrival Circle Entered
// 7) Status
// A = Perpendicular passed at waypoint
// 8) Bearing origin to destination
// 9) M = Magnetic, T = True
// 10) Destination Waypoint ID
// 11) Bearing, present position to Destination
// 12) M = Magnetic, T = True
// 13) Heading to steer to destination waypoint
// 14) M = Magnetic, T = True
// 15) Checksum
return NULL;
} else if (!strcmp(thisSentence, "DBK")) { //*****************************DBT
// DBK Depth Below Keel
// 1 2 3 4 5 6 7
// | | | | | | |
//$--DBK,x.x,f,x.x,M,x.x,F*hh
// 1) Depth, feet
// 2) f = feet
// 3) Depth, meters
// 4) M = meters
// 5) Depth, Fathoms
// 6) F = Fathoms
// 7) Checksum
return NULL;
} else if (!strcmp(thisSentence, "DBS")) { //*****************************DBT
// DBS Depth Below Surface
// 1 2 3 4 5 6 7
// | | | | | | |
//$--DBS,x.x,f,x.x,M,x.x,F*hh
// 1) Depth, feet
// 2) f = feet
// 3) Depth, meters
// 4) M = meters
// 5) Depth, Fathoms
// 6) F = Fathoms
// 7) Checksum
return NULL;
} else if (!strcmp(thisSentence, "DBT")) { //*****************************DBT
// DBT Depth Below Transducer
// 1 2 3 4 5 6 7
// | | | | | | |
//$--DBT,x.x,f,x.x,M,x.x,F*hh
// 1) Depth, feet
// 2) f = feet
// 3) Depth, meters
// 4) M = meters
// 5) Depth, Fathoms
// 6) F = Fathoms
// 7) Checksum
double d = val[NMEA_DEPTH].latest - depthToTransducer;
sprintf(p, "%f,f,%f,M,,,", d / 0.3048, d);
} else if (!strcmp(thisSentence, "DPT")) { //*****************************DPT
// DPT Heading – Deviation & Variation
// 1 2 3
// | | |
//$--DPT,x.x,x.x*hh
// 1) Depth, meters
// 2) Offset from transducer;
// positive means distance from transducer to water line,
// negative means distance from transducer to keel
// 3) Checksum
return NULL;
} else if (!strcmp(thisSentence, "GSV")) { //*****************************GSV
// GSV Satellites in view
// 1 2 3 4 5 6 7 n
// | | | | | | | |
//$--GSV,x,x,x,x,x,x,x,...*hh
// 1) total number of messages
// 2) message number
// 3) satellites in view
// 4) satellite number
// 5) elevation in degrees
// 6) azimuth in degrees to true
// 7) SNR in dB
// more satellite infos like 4)-7)
// n) Checksum
return NULL;
} else if (!strcmp(thisSentence, "HDG")) { //*****************************HDG
// HDG Heading – Deviation & Variation
// 1 2 3 4 5 6
// | | | | | |
//$--HDG,x.x,x.x,a,x.x,a*hh
// 1) Magnetic Sensor heading in degrees
// 2) Magnetic Deviation, degrees
// 3) Magnetic Deviation direction, E = Easterly, W = Westerly
// 4) Magnetic Variation degrees
// 5) Magnetic Variation direction, E = Easterly, W = Westerly
// 6) Checksum
return NULL;
} else if (!strcmp(thisSentence, "HDM")) { //*****************************HDM
// HDM Heading – Magnetic
// 1 2 3
// | | |
//$--HDM,x.x,M*hh
// 1) Heading Degrees, magnetic
// 2) M = magnetic
// 3) Checksum
sprintf(p, "%f,M", (double)val[NMEA_HDG].latest);
} else if (!strcmp(thisSentence, "HDT")) { //*****************************HDT
// HDT Heading – True
// 1 2 3
// | | |
//$--HDT,x.x,T*hh
// 1) Heading Degrees, true
// 2) T = True
// 3) Checksum
// starts with $II for integrated instrumentation
sprintf(p, "%f,T", (double)val[NMEA_HDT].latest);
} else if (!strcmp(thisSentence, "MDA")) { //*****************************MDA
// MDA Meteorological Composite
// 1 2 3 4 5 6 7 8 9 10 11 12
// | | | | | | | | | | | |
//$__MDA,x.x,I,x.x,B,x.x,C,x.x,C,x.x, ,x.x,C,,T,,M,,N,,M*hh
//$IIMDA,,I,,B,,C,21.8,C,,,,C,,T,,M,,N,,M*0F // sent by RayMarine i70s
// Speed/Depth/Wind
// 1) Barometric Pressure
// 2) inches of Hg
// 3) Barometric Pressure
// 4) bar
// 5) Atmospheric Temperature
// 6) C or F
// 7) Water Temperature
// 8) C or F
// 9) Relative Humidity
// 10)
// 11) Dew Point
// 12) C or F
return NULL;
} else if (!strcmp(thisSentence, "MTW")) { //*****************************MTW
// MTW Water Temperature
// 1 2 3
// | | |
//$IIMTW,x.x,C*hh
//$IIMTW,21.8,C*18 // sent by RayMarine i70s Speed/Depth/Wind
// 1) Degrees
// 2) Unit of Measurement, Celcius
// 3) Checksum
return NULL;
} else if (!strcmp(thisSentence, "MWD")) { //*****************************MWD
// MWD Wind Direction & Speed
// Format unknown
return NULL;
} else if (!strcmp(thisSentence, "MWV")) { //*****************************MWV
// MWV Wind Speed and Angle assuming values for True
// 1 2 3 4 5 6
// | | | | | |
//$IIMWV,x.x,a,x.x,a,a*hh
//$WIMWV,276.94,R,0,N,A*03 // sent by RayMarine i70s Speed/Depth/Wind
// 1) Wind Angle, 0 to 360 degrees
// 2) Reference, R = Relative, T = True
// 3) Wind Speed
// 4) Wind Speed Units, K/M/N kilometers/miles/knots
// 5) Status, A = Data Valid
// 6) Checksum
if (ref == 'R')
sprintf(p, "%f,%c,%f,N,A", (double)val[NMEA_AWA].latest, ref,
(double)val[NMEA_AWS].latest);
else
sprintf(p, "%f,%c,%f,N,A", (double)val[NMEA_TWA].latest, 'T',
(double)val[NMEA_TWS].latest);
} else if (!strcmp(thisSentence, "RMB")) { //*****************************RMB
// RMB Recommended Minimum Navigation Information
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14
// | | | | | | | | | | | | | |
//$--RMB,A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A*hh
// 1) Status, V = Navigation receiver warning
// 2) Cross Track error - nautical miles
// 3) Direction to Steer, Left or Right
// 4) TO Waypoint ID
// 5) FROM Waypoint ID
// 6) Destination Waypoint Latitude 7) N or S
// 8) Destination Waypoint Longitude 9) E or W
// 10) Range to destination in nautical miles
// 11) Bearing to destination in degrees True
// 12) Destination closing velocity in knots
// 13) Arrival Status, A = Arrival Circle Entered 14) Checksum
sprintf(p, ",,,,,,,,,,,%f,A", (double)val[NMEA_VMGWP].latest);
} else if (!strcmp(thisSentence, "ROT")) { //*****************************ROT
// ROT Rate Of Turn
// 1 2 3
// | | |
//$--ROT,x.x,A*hh
// 1) Rate Of Turn, degrees per minute, "-" means bow turns to port
// 2) Status, A means data is valid
// 3) Checksum
return NULL;
} else if (!strcmp(thisSentence, "RPM")) { //*****************************RPM
// RPM Revolutions
// 1 2 3 4 5 6
// | | | | | |
//$--RPM,a,x,x.x,x.x,A*hh
// 1) Source; S = Shaft, E = Engine
// 2) Engine or shaft number
// 3) Speed, Revolutions per minute
// 4) Propeller pitch, % of maximum, "-" means astern
// 5) Status, A means data is valid
// 6) Checksum
return NULL;
} else if (!strcmp(thisSentence, "RSA")) { //*****************************RSA
// RSA Rudder Sensor Angle
// 1 2 3 4 5
// | | | | |
//$--RSA,x.x,A,x.x,A*hh
// 1) Starboard (or single) rudder sensor, "-" means Turn To Port
// 2) Status, A means data is valid
// 3) Port rudder sensor
// 4) Status, A means data is valid
// 5) Checksum
return NULL;
} 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 if (!strcmp(thisSentence, "VDR")) { //*****************************VDR
// VDR Set and Drift
// 1 2 3 4 5 6 7
// | | | | | | |
//$--VDR,x.x,T,x.x,M,x.x,N*hh
// 1) Degress True
// 2) T = True
// 3) Degrees Magnetic
// 4) M = Magnetic
// 5) Knots (speed of current)
// 6) N = Knots
// 7) Checksum
return NULL;
} else if (!strcmp(thisSentence, "VHW")) { //*****************************VHW
// VHW Water Speed and Heading
// 1 2 3 4 5 6 7 8 9
// | | | | | | | | |
//$--VHW,x.x,T,x.x,M,x.x,N,x.x,K*hh
//$IIVHW,,T,,M,0,N,0,K*55 // sent by RayMarine i70s Speed/Depth/Wind
// 1) Degrees True
// 2) T = True
// 3) Degrees Magnetic
// 4) M = Magnetic
// 5) Knots (speed of vessel relative to the water) [66]
// 6) N = Knots
// 7) Kilometers (speed of vessel relative to the water)
// 8) K = Kilometres
// 9) Checksum
sprintf(p, "%f,T,%f,M,%f,N,%f,K", (double)val[NMEA_HDT].latest,
(double)val[NMEA_HDG].latest, (double)val[NMEA_VTW].latest,
(double)val[NMEA_VTW].latest * 1.829);
} else if (!strcmp(thisSentence, "VLW")) { //*****************************VLW
// VLW Distance Traveled through Water
// 1 2 3 4 5
// | | | | |
//$--VLW,x.x,N,x.x,N*hh
//$IIVLW,0,N,0,N,,N,,N*4D // sent by RayMarine i70s Speed/Depth/Wind
// not sure what the last two are?
// 1) Total cumulative distance
// 2) N = Nautical Miles
// 3) Distance since Reset
// 4) N = Nautical Miles
// 5) Checksum
return NULL;
} else if (!strcmp(thisSentence, "VPW")) { //*****************************VPW
// not supported by iNavX
// VPW Speed – Measured Parallel to Wind
// 1 2 3 4 5
// | | | | |
//$--VPW,x.x,N,x.x,M*hh
// 1) Speed, "-" means downwind
// 2) N = Knots
// 3) Speed, "-" means downwind
// 4) M = Meters per second
// 5) Checksum
sprintf(p, "%f,N,,", (double)val[NMEA_VMG].latest);
} else if (!strcmp(thisSentence, "VTG")) { //*****************************VTG
// VTG Track Made Good and Ground Speed
// 1 2 3 4 5 6 7 8 9
// | | | | | | | | |
//$--VTG,x.x,T,x.x,M,x.x,N,x.x,K*hh
// 1) Track Degrees 2) T = True
// 3) Track Degrees 4) M = Magnetic
// 5) Speed Knots 6) N = Knots
// 7) Speed Kilometers Per Hour 8) K = Kilometres Per Hour
// 9) Checksum
return NULL;
} else if (!strcmp(thisSentence, "VWR")) { //*****************************VWR
// VWR Relative Wind Speed and Angle
// 1 2 3 4 5 6 7 8 9
// | | | | | | | | |
//$--VWR,x.x,a,x.x,N,x.x,M,x.x,K*hh
//$WIVWR,83.1,L,0,N,0,M,0,K*6D // sent by RayMarine i70s
// Speed/Depth/Wind
// 1) Wind direction magnitude in degrees
// 2) Wind direction Left/Right of bow
// 3) Speed
// 4) N = Knots
// 5) Speed
// 6) M = Meters Per Second
// 7) Speed
// 8) K = Kilometers Per Hour
// 9) Checksum
return NULL;
} else if (!strcmp(thisSentence, "WCV")) { //*****************************WCV
// WCV Waypoint Closure Velocity
// 1 2 3 4
// | | | |
//$--WCV,x.x,N,c--c*hh
// 1) Velocity 2) N = knots 3) Waypoint ID 4) Checksum
sprintf(p, "%f,N,home", (double)val[NMEA_VMG].latest);
} else if (!strcmp(thisSentence, "XTE")) { //*****************************XTE
// XTE Cross-Track Error – Measured
// 1 2 3 4 5 6
// | | | | | |
//$--XTE,A,A,x.x,a,N,*hh
// 1) Status
// V = LORAN-C blink or SNR warning
// A = general warning flag or other navigation systems when a reliable
// fix is not available
// 2) Status
// V = Loran-C cycle lock warning flag
// A = OK or not used
// 3) Cross track error magnitude
// 4) Direction to steer, L or R
// 5) Cross track units. N = Nautical Miles
// 6) Checksum
return NULL;
} else if (!strcmp(thisSentence, "ZDA")) { //*****************************ZDA
// ZDA Time & Date – UTC, Day, Month, Year and Local Time Zone
// 1 2 3 4 5 6 7
// | | | | | | |
//$--ZDA,hhmmss.ss,xx,xx,xxxx,xx,xx*hh
// 1) Local zone minutes description, same sign as local hours
// 2) Local zone description, 00 to +/- 13 hours
// 3) Year
// 4) Month, 01 to 12
// 5) Day, 01 to 31
// 6) Time (UTC)
// 7) Checksum
return NULL;
} else {
return NULL; // didn't find a match for the build request
}
addChecksum(nmea); // Successful completion
if (!noCRLF) { // Add Carriage Return and Line Feed to comply with NMEA-183
size_t len = strlen(nmea);
char *nmeaWithCRLF =
(char *)malloc(len + 3); // +2 for \r\n, +1 for null terminator
if (nmeaWithCRLF) {
strcpy(nmeaWithCRLF, nmea); // Copy original string
strcat(nmeaWithCRLF, "\r\n"); // Append \r\n
strcpy(nmea, nmeaWithCRLF); // Copy back to original buffer
free(nmeaWithCRLF); // Free the allocated memory
}
}
return nmea; // return pointer to finished product
}
#endif // NMEA_EXTENSIONS
/**************************************************************************/
/*!
@brief Add *CS where CS is the two character hex checksum for all but
the first character in the string. The checksum is the result of an
exclusive or of all the characters in the string. Also useful if you
are creating new PMTK strings for controlling a GPS module and need a
checksum added.
@param buff Pointer to the string, which must be long enough
@return none
*/
/**************************************************************************/
void Adafruit_GPS::addChecksum(char *buff) {
char cs = 0;
int i = 1;
while (buff[i]) {
cs ^= buff[i];
i++;
}
// Calculate the needed buffer size: original length + 3 (*XX) + 1 (null
// terminator)
int neededSize = strlen(buff) + 4;
char *tempBuffer = (char *)malloc(neededSize);
if (tempBuffer != NULL) {
// Use snprintf to safely format the string with the checksum
snprintf(tempBuffer, neededSize, "%s*%02X", buff, cs);
// Copy the formatted string back to the original buffer
// Note: Make sure the original buffer is large enough to hold the new
// string.
strcpy(buff, tempBuffer);
// Free the allocated memory to avoid memory leaks
free(tempBuffer);
}
}
@@ -0,0 +1,566 @@
/**************************************************************************/
/*!
@file NMEA_data.cpp
Code for tracking values that change with time so that history can be
examined for recent trends in real time. This code will only generate the
stubs for newDataValue() and data_init(), adding essentially nothing to
the memory footprint unless NMEA_EXTENSIONS is defined.
This is code intended to complement the Adafruit GPS library and process
data for many additional NMEA sentences, mostly of interest to sailors.
The parse function can be a direct substitute for the Adafruit_GPS
function of the same name, updating the same variables within an NMEA
object. A simple use case would involve:
Define an Adafruit_GPS object and use it to collect and parse sentences
from a serial port. The GPS object will be updated and can be used exactly
as usual.
Define an NMEA object and use it to parse the same sentences. It will
succeed on more sentences than the GPS object and keep more detailed data
records. It updates all the same variables as the GPS object, so you could
skip the GPS parsing step.
@author Rick Sellens.
@copyright CCBY license
*/
/**************************************************************************/
#include "Adafruit_GPS.h"
/**************************************************************************/
/*!
@brief Update the value and history information with a new value. Call
whenever a new data value is received. The function does nothing if the
NMEA extensions are not enabled.
@param idx The data index for which a new value has been received
@param v The new value received
@return none
*/
/**************************************************************************/
void Adafruit_GPS::newDataValue(nmea_index_t idx, nmea_float_t v) {
#ifdef NMEA_EXTENSIONS
// Serial.println();Serial.print(idx);Serial.print(", "); Serial.println(v);
val[idx].latest = v; // update the value
// update the smoothed verion
if (isCompoundAngle(idx)) { // angle with sin/cos component recording
newDataValue((nmea_index_t)(idx + 1), sin(v / (nmea_float_t)RAD_TO_DEG));
newDataValue((nmea_index_t)(idx + 2), cos(v / (nmea_float_t)RAD_TO_DEG));
}
// weighting factor for smoothing depends on delta t / tau
nmea_float_t w =
min((nmea_float_t)1.0,
(nmea_float_t)(millis() - val[idx].lastUpdate) / val[idx].response);
// default smoothing
val[idx].smoothed = (1.0f - w) * val[idx].smoothed + w * v;
// special smoothing for some angle types
if (val[idx].type == NMEA_COMPASS_ANGLE_SIN)
val[idx].smoothed =
compassAngle(val[idx + 1].smoothed, val[idx + 2].smoothed);
if (val[idx].type == NMEA_BOAT_ANGLE_SIN)
val[idx].smoothed = boatAngle(val[idx + 1].smoothed, val[idx + 2].smoothed);
// some types just don't make sense to smooth -- use latest
if (val[idx].type == NMEA_BOAT_ANGLE)
val[idx].smoothed = val[idx].latest;
if (val[idx].type == NMEA_COMPASS_ANGLE)
val[idx].smoothed = val[idx].latest;
if (val[idx].type == NMEA_DDMM)
val[idx].smoothed = val[idx].latest;
if (val[idx].type == NMEA_HHMMSS)
val[idx].smoothed = val[idx].latest;
val[idx].lastUpdate = millis(); // take a time stamp
if (val[idx].hist) { // there's a history struct for this tag
unsigned long seconds = (millis() - val[idx].hist->lastHistory) / 1000;
// do an update if the time has come, or if this is the first time through
if (seconds >= val[idx].hist->historyInterval ||
val[idx].hist->lastHistory == 0) {
// move the old history back in time by one step
for (unsigned i = 0; i < (val[idx].hist->n - 1); i++)
val[idx].hist->data[i] = val[idx].hist->data[i + 1];
// Create the new entry, scaling and offsetting the value to fit into an
// integer, and based on the smoothed value.
val[idx].hist->data[val[idx].hist->n - 1] =
val[idx].hist->scale * (val[idx].smoothed - val[idx].hist->offset);
val[idx].hist->lastHistory = millis();
}
}
#endif // NMEA_EXTENSIONS
}
/**************************************************************************/
/*!
@brief Initialize the object. Build a val[] matrix of data values for
all of the enumerated values, including the extra values for the compound
angle types. The initializer shold probably leave it up to the user
sketch to decide which data values should carry the extra memory burden
of history.
@return none
*/
/**************************************************************************/
void Adafruit_GPS::data_init() {
#ifdef NMEA_EXTENSIONS
// fill all the data values with nothing
static char c[] = "NUL";
for (int i = 0; i < (int)NMEA_MAX_INDEX; i++) {
initDataValue((nmea_index_t)i, c, NULL, NULL, 0, (nmea_value_type_t)0);
}
// fill selected data values with the relevant information and pointers
static char BoatSpeedfmt[] = "%6.2f";
static char WindSpeedfmt[] = "%6.1f";
static char Speedunit[] = "knots";
static char Anglefmt[] = "%6.0f";
static char BoatAngleunit[] = "Degrees";
static char TrueAngleunit[] = "Deg True";
static char MagAngleunit[] = "Deg Mag";
static char HDOPlabel[] = "HDOP";
initDataValue(NMEA_HDOP, HDOPlabel);
static char LATlabel[] = "Lat";
static char LATfmt[] = "%9.4f";
static char LATunit[] = "DDD.dddd";
initDataValue(
NMEA_LAT, LATlabel, LATfmt, LATunit, 0,
NMEA_BOAT_ANGLE); // angle from -180 to 180, or actually -90 to 90 for lat
static char LONlabel[] = "Lon";
initDataValue(NMEA_LON, LONlabel, LATfmt, LATunit, 0,
NMEA_BOAT_ANGLE); // angle from -180 to 180
static char LATWPlabel[] = "WP Lat";
initDataValue(NMEA_LATWP, LATWPlabel, LATfmt, LATunit, 0, NMEA_BOAT_ANGLE);
static char LONWPlabel[] = "WP Lon";
initDataValue(NMEA_LONWP, LONWPlabel, LATfmt, LATunit, 0, NMEA_BOAT_ANGLE);
static char SOGlabel[] = "SOG";
initDataValue(NMEA_SOG, SOGlabel, BoatSpeedfmt, Speedunit);
static char COGlabel[] = "COG";
// types with sin/cos need two extra spots in the values matrix!
initDataValue(NMEA_COG, COGlabel, Anglefmt, TrueAngleunit, 0,
NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11
static char COGWPlabel[] = "WP COG";
initDataValue(NMEA_COGWP, COGWPlabel, Anglefmt, TrueAngleunit, 0,
NMEA_COMPASS_ANGLE); // type: angle 0-360 1
static char XTElabel[] = "XTE";
static char XTEfmt[] = "%6.2f";
static char XTEunit[] = "NM";
initDataValue(NMEA_XTE, XTElabel, XTEfmt, XTEunit);
static char DISTWPlabel[] = "WP Dist";
initDataValue(NMEA_DISTWP, DISTWPlabel, XTEfmt, XTEunit);
static char AWAlabel[] = "AWA";
initDataValue(NMEA_AWA, AWAlabel, Anglefmt, BoatAngleunit, 0,
NMEA_BOAT_ANGLE_SIN); // type: +-180 angle with sin/cos 12
static char AWSlabel[] = "AWS";
initDataValue(NMEA_AWS, AWSlabel, WindSpeedfmt, Speedunit);
static char TWAlabel[] = "TWA";
initDataValue(NMEA_TWA, TWAlabel, Anglefmt, BoatAngleunit, 0,
NMEA_BOAT_ANGLE_SIN); // type: +-180 angle with sin/cos 12
static char TWDlabel[] = "TWD";
initDataValue(NMEA_TWD, TWDlabel, Anglefmt, TrueAngleunit, 0,
NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11
static char TWSlabel[] = "TWS";
initDataValue(NMEA_TWS, TWSlabel, WindSpeedfmt, Speedunit);
static char VMGlabel[] = "VMG";
initDataValue(NMEA_VMG, VMGlabel, BoatSpeedfmt, Speedunit);
static char VMGWPlabel[] = "WP VMG";
initDataValue(NMEA_VMGWP, VMGWPlabel, BoatSpeedfmt, Speedunit);
static char HEELlabel[] = "Heel";
static char HEELunit[] = "Deg Stbd";
initDataValue(NMEA_HEEL, HEELlabel, Anglefmt, HEELunit, 0,
NMEA_BOAT_ANGLE); // type: angle +/-180 2
static char PITCHlabel[] = "Pitch";
static char PITCHunit[] = "Deg Bow Up";
initDataValue(NMEA_PITCH, PITCHlabel, Anglefmt, PITCHunit, 0,
NMEA_BOAT_ANGLE); // type: angle +/-180 2
static char HDGlabel[] = "HDG";
initDataValue(NMEA_HDG, HDGlabel, Anglefmt, MagAngleunit, 0,
NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11
static char HDTlabel[] = "HDG";
initDataValue(NMEA_HDT, HDTlabel, Anglefmt, TrueAngleunit, 0,
NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11
static char VTWlabel[] = "VTW";
initDataValue(NMEA_VTW, VTWlabel, BoatSpeedfmt, Speedunit);
static char LOGlabel[] = "Log";
static char LOGfmt[] = "%6.0f";
static char LOGunit[] = "NM";
initDataValue(NMEA_LOG, LOGlabel, LOGfmt, LOGunit);
static char LOGRlabel[] = "Trip";
static char LOGRfmt[] = "%6.2f";
initDataValue(NMEA_LOG, LOGRlabel, LOGRfmt, LOGunit);
static char DEPTHlabel[] = "Depth";
static char DEPTHfmt[] = "%6.1f";
static char DEPTHunit[] = "m";
initDataValue(NMEA_DEPTH, DEPTHlabel, DEPTHfmt, DEPTHunit);
static char RPM_M1label[] = "Motor 1";
static char RPM_M1fmt[] = "%6.0f";
static char RPM_M1unit[] = "RPM";
initDataValue(NMEA_RPM_M1, RPM_M1label, RPM_M1fmt, RPM_M1unit);
static char TEMPERATURE_M1label[] = "Temp 1";
static char TEMPERATURE_M1fmt[] = "%6.0f";
static char TEMPERATURE_M1unit[] = "Deg C";
initDataValue(NMEA_TEMPERATURE_M1, TEMPERATURE_M1label, TEMPERATURE_M1fmt,
TEMPERATURE_M1unit);
static char PRESSURE_M1label[] = "Oil 1";
static char PRESSURE_M1fmt[] = "%6.0f";
static char PRESSURE_M1unit[] = "kPa";
initDataValue(NMEA_PRESSURE_M1, PRESSURE_M1label, PRESSURE_M1fmt,
PRESSURE_M1unit);
static char VOLTAGE_M1label[] = "Motor 1";
static char VOLTAGE_M1fmt[] = "%6.2f";
static char VOLTAGE_M1unit[] = "Volts";
initDataValue(NMEA_VOLTAGE_M1, VOLTAGE_M1label, VOLTAGE_M1fmt,
VOLTAGE_M1unit);
static char CURRENT_M1label[] = "Motor 1";
static char CURRENT_M1fmt[] = "%6.1f";
static char CURRENT_M1unit[] = "Amps";
initDataValue(NMEA_CURRENT_M1, CURRENT_M1label, CURRENT_M1fmt,
CURRENT_M1unit);
static char RPM_M2label[] = "Motor 2";
initDataValue(NMEA_RPM_M2, RPM_M2label, RPM_M1fmt, RPM_M1unit);
static char TEMPERATURE_M2label[] = "Temp 2";
initDataValue(NMEA_TEMPERATURE_M2, TEMPERATURE_M2label, TEMPERATURE_M1fmt,
TEMPERATURE_M1unit);
static char PRESSURE_M2label[] = "Oil 2";
initDataValue(NMEA_PRESSURE_M2, PRESSURE_M2label, PRESSURE_M1fmt,
PRESSURE_M1unit);
static char VOLTAGE_M2label[] = "Motor 2";
initDataValue(NMEA_VOLTAGE_M2, VOLTAGE_M2label, VOLTAGE_M1fmt,
VOLTAGE_M1unit);
static char CURRENT_M2label[] = "Motor 2";
initDataValue(NMEA_CURRENT_M2, CURRENT_M2label, CURRENT_M1fmt,
CURRENT_M1unit);
static char TEMPERATURE_AIRlabel[] = "Air";
static char TEMPERATURE_AIRfmt[] = "%6.1f";
static char TEMPERATURE_AIRunit[] = "Deg C";
initDataValue(NMEA_TEMPERATURE_AIR, TEMPERATURE_AIRlabel, TEMPERATURE_AIRfmt,
TEMPERATURE_AIRunit);
static char TEMPERATURE_WATERlabel[] = "Water";
static char TEMPERATURE_WATERfmt[] = "%6.1f";
static char TEMPERATURE_WATERunit[] = "Deg C";
initDataValue(NMEA_TEMPERATURE_WATER, TEMPERATURE_WATERlabel,
TEMPERATURE_WATERfmt, TEMPERATURE_WATERunit);
static char HUMIDITYlabel[] = "Humidity";
static char HUMIDITYfmt[] = "%6.0f";
static char HUMIDITYunit[] = "% RH";
initDataValue(NMEA_HUMIDITY, HUMIDITYlabel, HUMIDITYfmt, HUMIDITYunit);
static char BAROMETERlabel[] = "Barometer";
static char BAROMETERfmt[] = "%6.0f";
static char BAROMETERunit[] = "Pa";
initDataValue(NMEA_BAROMETER, BAROMETERlabel, BAROMETERfmt, BAROMETERunit);
#endif // NMEA_EXTENSIONS
}
#ifdef NMEA_EXTENSIONS
/**************************************************************************/
/*!
@brief Clearer approach to retrieving NMEA values by allowing calls that
look like nmea.get(NMEA_TWA) instead of val[NMEA_TWA].latest.
Use newDataValue() to set the values.
@param idx the NMEA value's index
@return the latest NMEA value
*/
/**************************************************************************/
nmea_float_t Adafruit_GPS::get(nmea_index_t idx) {
if (idx >= NMEA_MAX_INDEX || idx < NMEA_HDOP)
return 0.0;
return val[idx].latest;
}
/**************************************************************************/
/*!
@brief Clearer approach to retrieving NMEA values
@param idx the NMEA value's index
@return the latest NMEA value, smoothed
*/
/**************************************************************************/
nmea_float_t Adafruit_GPS::getSmoothed(nmea_index_t idx) {
if (idx >= NMEA_MAX_INDEX || idx < NMEA_HDOP)
return 0.0;
return val[idx].smoothed;
}
/**************************************************************************/
/*!
@brief Initialize the contents of a data value table entry
@param idx The data index for the value to be initialized
@param label Pointer to a label string that describes the value
@param fmt Pointer to a sprintf format to use for the value, e.g. "%6.2f"
@param unit Pointer to a string for the units, e.g. "Deg Mag"
@param response Time constant for smoothing in ms. The longer the time
constant, the more slowly the smoothed value will move towards a new value.
@param type The type of data contained in the value. simple float 0,
angle 0-360 1, angle +/-180 2, angle with history centered +/- around
the latest angle 3, lat/lon DDMM.mm 10, time HHMMSS 20.
@return none
*/
/**************************************************************************/
void Adafruit_GPS::initDataValue(nmea_index_t idx, char *label, char *fmt,
char *unit, unsigned long response,
nmea_value_type_t type) {
if (idx < NMEA_MAX_INDEX) {
if (label)
val[idx].label = label;
if (fmt)
val[idx].fmt = fmt;
if (unit)
val[idx].unit = unit;
if (response)
val[idx].response = response;
val[idx].type = type;
if ((int)(val[idx].type / 10) ==
1) { // angle with sin/cos component recording
initDataValue(
(nmea_index_t)(idx +
1)); // initialize the next two data values as well
initDataValue((nmea_index_t)(idx + 2));
}
}
}
/**************************************************************************/
/*!
@brief Attempt to add history to a data value table entry. If it fails
to malloc the space, history will not be added. Test the pointer for a
check if needed. Select scale and offset values carefully so that
operations and results will fit inside 16 bit integer limits. For example
a scale of 1.0 and an offset of 100000.0 would be a good choice for
atmospheric pressure in Pa with values ranging ~ +/- 3500, while a scale
of 10.0 would be pushing the integer limits.
@param idx The data index for the value to have history recorded
@param scale Value for scaling the integer history list
@param offset Value for scaling the integer history list
@param historyInterval Approximate Time in seconds between historical
values.
@param historyN Set size of data buffer.
@return pointer to the history
*/
/**************************************************************************/
nmea_history_t *Adafruit_GPS::initHistory(nmea_index_t idx, nmea_float_t scale,
nmea_float_t offset,
unsigned historyInterval,
unsigned historyN) {
historyN = max((unsigned)10, historyN);
if (idx < NMEA_MAX_INDEX) {
// remove any existing history
if (val[idx].hist != NULL)
removeHistory(idx);
// space for the struct
val[idx].hist = (nmea_history_t *)malloc(sizeof(nmea_history_t));
if (val[idx].hist != NULL) {
// space for the data array of the appropriate size
val[idx].hist->data = (int16_t *)malloc(sizeof(int16_t) * historyN);
if (val[idx].hist->data != NULL) {
// initialize the data array
for (unsigned i = 0; i < historyN; i++)
val[idx].hist->data[i] = 0;
} else
free(val[idx].hist);
}
if (val[idx].hist != NULL) {
val[idx].hist->n = historyN;
if (scale > 0.0f)
val[idx].hist->scale = scale;
val[idx].hist->offset = offset;
if (historyInterval > 0)
val[idx].hist->historyInterval = historyInterval;
}
return val[idx].hist;
}
return NULL;
}
/**************************************************************************/
/*!
@brief Remove history from a data value table entry, if it has been added.
@param idx The data index for the value to have history removed
@return none
*/
/**************************************************************************/
void Adafruit_GPS::removeHistory(nmea_index_t idx) {
if (idx < NMEA_MAX_INDEX) {
if (val[idx].hist == NULL)
return;
free(val[idx].hist->data);
free(val[idx].hist);
val[idx].hist = NULL;
}
}
/**************************************************************************/
/*!
@brief Print out the current state of a data value. Primarily useful as
a debugging aid.
@param idx The index for the data value
@param n The number of history values to include
@return none
*/
/**************************************************************************/
void Adafruit_GPS::showDataValue(nmea_index_t idx, int n) {
Serial.print("idx: ");
if (idx < 10)
Serial.print(" ");
Serial.print(idx);
Serial.print(", ");
Serial.print(val[idx].label);
Serial.print(", ");
Serial.print(val[idx].latest, 4);
Serial.print(", ");
Serial.print(val[idx].smoothed, 4);
Serial.print(", at ");
Serial.print(val[idx].lastUpdate);
Serial.print(" ms, tau = ");
Serial.print(val[idx].response);
Serial.print(" ms, type:");
Serial.print(val[idx].type);
Serial.print(", ockam:");
Serial.print(val[idx].ockam);
if (val[idx].hist) {
Serial.print("\n History at ");
Serial.print(val[idx].hist->historyInterval);
Serial.print(" second intervals: ");
Serial.print(val[idx].hist->data[val[idx].hist->n - 1]);
for (unsigned i = val[idx].hist->n - 2;
i >= max(val[idx].hist->n - n, (unsigned)0);
i--) { // most recent first
Serial.print(", ");
Serial.print(val[idx].hist->data[i]);
}
}
Serial.print("\n");
if (idx == NMEA_LAT) {
Serial.print(" latitude (DDMM.mmmm): ");
Serial.print(latitude, 4);
Serial.print(", lat: ");
Serial.print(lat);
Serial.print(", latitudeDegrees: ");
Serial.print(latitudeDegrees, 8);
Serial.print(", latitude_fixed: ");
Serial.println(latitude_fixed);
}
if (idx == NMEA_LON) {
Serial.print(" longitude (DDMM.mmmm): ");
Serial.print(longitude, 4);
Serial.print(", lon: ");
Serial.print(lon);
Serial.print(", longitudeDegrees: ");
Serial.print(longitudeDegrees, 8);
Serial.print(", longitude_fixed: ");
Serial.println(longitude_fixed);
}
}
/**************************************************************************/
/*!
@brief Check if it is a compound angle
@param idx The index for the data value
@return true if a compound angle requiring 3 contiguos data values.
*/
/**************************************************************************/
bool Adafruit_GPS::isCompoundAngle(nmea_index_t idx) {
if ((int)(val[idx].type / 10) == 1) // angle with sin/cos component recording
return true;
return false;
}
/**************************************************************************/
/*!
@brief Estimate a direction in -180 to 180 degree range from the values
of the sine and cosine of the compound angle, which could be noisy.
@param s The sin of the angle
@param c The cosine of the angle
@return The angle in -180 to 180 degree range.
*/
/**************************************************************************/
nmea_float_t Adafruit_GPS::boatAngle(nmea_float_t s, nmea_float_t c) {
// put the sin angle in -90 to 90 range
nmea_float_t sAng = asin(s) * (nmea_float_t)RAD_TO_DEG;
while (sAng < -90)
sAng += 180.0f;
while (sAng > 90)
sAng -= 180.0f;
// put the cos angle in 0 to 180 range
nmea_float_t cAng = acos(c) * (nmea_float_t)RAD_TO_DEG;
while (cAng < 0)
cAng += 180.0f;
while (cAng > 180)
cAng -= 180.0f;
// Pick the most accurate representation and translate
if (cAng < 45)
return sAng; // Close hauled
else {
if (cAng > 135) { // Running
if (sAng > 0)
return 180 - sAng; // on starboard tack
else
return -180 - sAng; // on port tack
} else { // Reaching
if (sAng < 0)
return -cAng; // on port tack
else
return cAng; // on starboard tack
}
}
return 9999; // you can't get here, but there must be an explicit return
}
/**************************************************************************/
/*!
@brief Estimate a direction in 0 to 360 degree range from the values
of the sine and cosine of the compound angle, which could be noisy.
@param s The sin of the angle
@param c The cosine of the angle
@return The angle in 0 to 360 degree range.
*/
/**************************************************************************/
nmea_float_t Adafruit_GPS::compassAngle(nmea_float_t s, nmea_float_t c) {
nmea_float_t ang = boatAngle(s, c);
if (ang < 5000) { // if reasonable range
while (ang < 0)
ang += 360.0f; // round up
while (ang > 360)
ang -= 360.0f; // round down
}
return ang;
}
#endif // NMEA_EXTENSIONS
@@ -0,0 +1,194 @@
/**************************************************************************/
/*!
@file NMEA_data.h
*/
/**************************************************************************/
#ifndef _NMEA_DATA_H
#define _NMEA_DATA_H
#include "Arduino.h"
#define NMEA_MAX_WP_ID \
20 ///< maximum length of a waypoint ID name, including terminating 0
#define NMEA_MAX_SENTENCE_ID \
20 ///< maximum length of a sentence ID name, including terminating 0
#define NMEA_MAX_SOURCE_ID \
3 ///< maximum length of a source ID name, including terminating 0
/*************************************************************************
doubles and floats are identical on AVR processors like the UNO where space
is tight. doubles avoid the roundoff errors that led to the fixed point mods
in https://github.com/adafruit/Adafruit-GPS-Library/pull/13, provided the
processor supports actual doubles like the SAMD series with more storage. The
total penalty for going all double is under a few hundred bytes / instance or
0 bytes / instance on an UNO. This typedef allows a switch to lower precision
to save some storage if needed. A float carries 23 bits of fractional
resolution, giving a resolution of at least 9 significant digits, thus 6
significant digits in the decimal place of an angular value like latitude, and
thus a resolution on earth of at least 110 mm. That's closer than GPS will
hit, and closer than needed for navigation, so floats can be used to save a
little storage.
**************************************************************************/
#ifndef NMEA_FLOAT_T
#define NMEA_FLOAT_T float ///< let float be overidden on command line
#endif
typedef NMEA_FLOAT_T
nmea_float_t; ///< the type of variables to use for floating point
/**************************************************************************/
/*!
Struct to contain all the details associated with the history of an NMEA
data value as an optional extension to the data value struct. The history
is stored as scaled integers to save space while caputuring a reasonable
level of resolution. The integer is set equal to scale * (X - offset) and
can be converted back to an approximate float value with
X = I / scale + offset
Only some tags have history in order to save memory. Most of the memory
cost is directly in the array.
192 history values taken every 20 seconds covers just over an hour.
**************************************************************************/
typedef struct {
int16_t *data = NULL; ///< array of ints, oldest first
unsigned n = 0; ///< number of history array elements
uint32_t lastHistory = 0; ///< millis() when history was last updated
uint16_t historyInterval = 20; ///< seconds between history updates
nmea_float_t scale = 1.0; ///< history = (smoothed - offset) * scale
nmea_float_t offset = 0.0; ///< value = (float) history / scale + offset
} nmea_history_t;
/**************************************************************************/
/*!
Type to characterize the type of value stored in a data value struct.
The sine and cosine components of some angles allow
for smoothing of those angles by averaging of sine and cosine values
that are continuous, rather than angles that are discontinuous at
-180/180 or 359/0 transitions. Types 10-19 must have three contiguous
data value entries set up in the matrix to accommodate the extra sin
and cos values.
*/
/**************************************************************************/
typedef enum {
NMEA_SIMPLE_FLOAT = 0, ///< A simple floating point number
NMEA_COMPASS_ANGLE = 1, ///< A compass style angle from 0 to 360 degrees
NMEA_BOAT_ANGLE = 2, ///< An angle relative to the boat orientation
///< from -180 (port) to 180 degrees
NMEA_COMPASS_ANGLE_SIN =
11, ///< A compass style angle from 0 to 360 degrees, with sin and cos
///< elements stored for averaging, etc.
NMEA_BOAT_ANGLE_SIN =
12, ///< An angle relative to the boat orientation from -180 (port) to 180
///< degrees, with sin and cos elements stored for averaging, etc.
NMEA_DDMM = 20, ///< A latitude or longitude angle stored in DDMM.mmmm format
///< like it comes in from the GPS
NMEA_HHMMSS =
30 ///< A time stored in HHMMSS format like it comes in from the GPS
} nmea_value_type_t;
/**************************************************************************/
/*!
Struct to contain all the details associated with an NMEA data value that
can be tracked through time to see how it changes, carries a label, units,
and a format string to determine how it is displayed. Memory footprint
of about 32 bytes per data value, so not tenable in small memory spaces.
*/
/**************************************************************************/
typedef struct {
nmea_float_t latest = 0.0; ///< the most recently obtained value
nmea_float_t smoothed =
0.0; ///< smoothed value based on weight of dt/response
uint32_t lastUpdate = 0; ///< millis() when latest was last set
uint16_t response = 1000; ///< time constant in millis for smoothing
nmea_value_type_t type =
NMEA_SIMPLE_FLOAT; ///< type of float data value represented
byte ockam = 0; ///< the corresponding Ockam Instruments tag number, 0-128
nmea_history_t *hist = NULL; ///< pointer to history, if any
char *label = NULL; ///< pointer to quantity label, if any
char *unit = NULL; ///< pointer to units label, if any
char *fmt = NULL; ///< pointer to format string, if any
} nmea_datavalue_t;
/**************************************************************************/
/*!
Type to provide an index into the array of data values for different
NMEA quantities. The sine and cosine components of some angles allow
for smoothing of those angles by averaging of sine and cosine values
that are continuous, rather than angles that are discontinuous at
-180/180 or 359/0 transitions. Note that the enumerations are arranged
so that NMEA_XXX_SIN = NMEA_XXX + 1, and NMEA_XXX_COS = NMEA_XXX + 2.
*/
/**************************************************************************/
typedef enum {
NMEA_HDOP = 0, ///< Horizontal Dilution of Position
NMEA_LAT, ///< Latitude in signed decimal degrees -90 to 90
NMEA_LON, ///< Longitude in signed decimal degrees -180 to 180
NMEA_LATWP, ///< Waypoint Latitude in signed decimal degrees -90 to 90
NMEA_LONWP, ///< Waypoint Longitude in signed decimal degrees -180 to 180
NMEA_SOG, ///< Speed over Ground in knots
NMEA_COG, ///< Course over ground, 0 to 360 degrees true
NMEA_COG_SIN, ///< sine of Course over ground
NMEA_COG_COS, ///< cosine of Course over ground
NMEA_COGWP, ///< Course over ground to the waypoint, 0 to 360 degrees true
NMEA_XTE, ///< Cross track error for the current segment to the waypoint,
///< Nautical Miles -ve to the left
NMEA_DISTWP, ///< Distance to the waypoint in nautical miles
NMEA_AWA, ///< apparent wind angle relative to the boat -180 to 180 degrees
NMEA_AWA_SIN, ///< sine of apparent wind angle relative to the boat
NMEA_AWA_COS, ///< cosine of apparent wind angle relative to the boat
NMEA_AWS, ///< apparent wind speed, will be coerced to knots
NMEA_TWA, ///< true wind angle relative to the boat -180 to 180 degrees
NMEA_TWA_SIN, ///< sine of true wind angle relative to the boat
NMEA_TWA_COS, ///< cosine of true wind angle relative to the boat
NMEA_TWD, ///< true wind compass direction, magnetic 0 to 360 degrees magnetic
NMEA_TWD_SIN, ///< sine of true wind compass direction, magnetic
NMEA_TWD_COS, ///< cosine of true wind compass direction, magnetic
NMEA_TWS, ///< true wind speed in knots TWS
NMEA_VMG, ///< velocity made good relative to the wind -ve means downwind,
///< knots
NMEA_VMGWP, ///< velocity made good relative to the waypoint, knots
NMEA_HEEL, ///< boat heel angle, -180 to 180 degrees to starboard
NMEA_PITCH, ///< boat pitch angle, -180 to 180 degrees bow up
NMEA_HDG, ///< magnetic heading, 0 to 360 degrees magnetic
NMEA_HDG_SIN, ///< sine of magnetic heading
NMEA_HDG_COS, ///< cosine of magnetic heading
NMEA_HDT, ///< true heading, 0 to 360 degrees true
NMEA_HDT_SIN, ///< sine of true heading
NMEA_HDT_COS, ///< cosine of true heading
NMEA_VTW, ///< Boat speed through the water in knots
NMEA_LOG, ///< Distance logged through the water in nautical miles
NMEA_LOGR, ///< Distance logged through the water in nautical miles since
///< reset
NMEA_DEPTH, ///< depth of water below the surface in metres
NMEA_RPM_M1, ///< rpm of motor 1
NMEA_TEMPERATURE_M1, ///< temperature of motor 1 in C
NMEA_PRESSURE_M1, ///< pressure of motor 1 in kPa
NMEA_VOLTAGE_M1, ///< voltage of motor 1 in Volts
NMEA_CURRENT_M1, ///< current of motor 1 in Amps
NMEA_RPM_M2, ///< rpm of motor 2
NMEA_TEMPERATURE_M2, ///< temperature of motor 2 in C
NMEA_PRESSURE_M2, ///< pressure of motor 2 in kPa
NMEA_VOLTAGE_M2, ///< voltage of motor 2 in Volts
NMEA_CURRENT_M2, ///< current of motor 2 in Amps
NMEA_TEMPERATURE_AIR, ///< outside temperature in C
NMEA_TEMPERATURE_WATER, ///< sea water temperature in C
NMEA_HUMIDITY, ///< outside relative humidity in %
NMEA_BAROMETER, ///< barometric pressure in Pa absolute -- not altitude
///< corrected
NMEA_USR_00, ///< spaces for a user sketch to inject its own data
NMEA_USR_01, ///< spaces for a user sketch to inject its own data
NMEA_USR_02, ///< spaces for a user sketch to inject its own data
NMEA_USR_03,
NMEA_USR_04,
NMEA_USR_05,
NMEA_USR_06,
NMEA_USR_07,
NMEA_USR_08,
NMEA_USR_09,
NMEA_USR_10,
NMEA_USR_11,
NMEA_USR_12,
NMEA_MAX_INDEX ///< the largest number in the enum type -- not for data,
///< but does define size of data value array required.
} nmea_index_t; ///< Indices for data values expected to change often with time
#endif // _NMEA_DATA_H
@@ -0,0 +1,877 @@
/**************************************************************************/
/*!
@file NMEA_parse.cpp
This is the Adafruit GPS library - the ultimate GPS library
for the ultimate GPS module!
Tested and works great with the Adafruit Ultimate GPS module
using MTK33x9 chipset
------> http://www.adafruit.com/products/746
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
@author Limor Fried/Ladyada for Adafruit Industries.
@copyright BSD license, check license.txt for more information
All text above must be included in any redistribution
*/
/**************************************************************************/
#include <Adafruit_GPS.h>
/**************************************************************************/
/*!
@brief Parse a standard NMEA string and update the relevant variables.
Sentences start with a $, then a two character source identifier, then a
three character sentence identifier 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, including proprietary sentences
that start with P, like the $PMTK commands to the GPS modules. See the
build() function and http://fort21.ru/download/NMEAdescription.pdf for
sentence descriptions.
Encapsulated data sentences are supported by NMEA-183, and start with !
instead of $. https://gpsd.gitlab.io/gpsd/AIVDM.html provides details
about encapsulated data sentences used in AIS.
parse() permits, but does not require Carriage Return and Line Feed at the
end of sentences. The end of the sentence is recognized by the * for the
checksum. parse() will not recognize a sentence without a valid checksum.
NMEA_EXTENSIONS must be defined in order to parse more than basic
GPS module sentences.
@param nmea Pointer to the NMEA string
@return True if successfully parsed, false if fails check or parsing
*/
/**************************************************************************/
bool Adafruit_GPS::parse(char *nmea) {
if (!check(nmea))
return false;
// passed the check, so there's a valid source in thisSource and a valid
// sentence in thisSentence
char *p = nmea; // Pointer to move through the sentence -- good parsers are
// non-destructive
p = strchr(p, ',') + 1; // Skip to char after the next comma, then check.
// 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
// Adafruit from Actisense NGW-1 from SH CP150C
parseTime(p);
p = strchr(p, ',') + 1; // parse time with specialized function
// parse out both latitude and direction, then go to next field, or fail
if (parseCoord(p, &latitudeDegrees, &latitude, &latitude_fixed, &lat))
newDataValue(NMEA_LAT, latitudeDegrees);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
// parse out both longitude and direction, then go to next field, or fail
if (parseCoord(p, &longitudeDegrees, &longitude, &longitude_fixed, &lon))
newDataValue(NMEA_LON, longitudeDegrees);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p)) { // if it's a , (or a * at end of sentence) the value is
// not included
fixquality = atoi(p); // needs additional processing
if (fixquality > 0) {
fix = true;
lastFix = sentTime;
} else
fix = false;
}
p = strchr(p, ',') + 1; // then move on to the next
// Most can just be parsed with atoi() or atof(), then move on to the next.
if (!isEmpty(p))
satellites = atoi(p);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_HDOP, HDOP = atof(p));
p = strchr(p, ',') + 1;
if (!isEmpty(p))
altitude = atof(p);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1; // skip the units
if (!isEmpty(p))
geoidheight = atof(p); // skip the rest
} else if (!strcmp(thisSentence, "RMC")) { //*****************************RMC
// in Adafruit from Actisense NGW-1 from SH CP150C
parseTime(p);
p = strchr(p, ',') + 1;
parseFix(p);
p = strchr(p, ',') + 1;
// parse out both latitude and direction, then go to next field, or fail
if (parseCoord(p, &latitudeDegrees, &latitude, &latitude_fixed, &lat))
newDataValue(NMEA_LAT, latitudeDegrees);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
// parse out both longitude and direction, then go to next field, or fail
if (parseCoord(p, &longitudeDegrees, &longitude, &longitude_fixed, &lon))
newDataValue(NMEA_LON, longitudeDegrees);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_SOG, speed = atof(p));
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_COG, angle = atof(p));
p = strchr(p, ',') + 1;
if (!isEmpty(p)) {
uint32_t fulldate = atof(p);
day = fulldate / 10000;
month = (fulldate % 10000) / 100;
year = (fulldate % 100);
lastDate = sentTime;
} // skip the rest
} else if (!strcmp(thisSentence, "GLL")) { //*****************************GLL
// in Adafruit from Actisense NGW-1 from SH CP150C
// parse out both latitude and direction, then go to next field, or fail
if (parseCoord(p, &latitudeDegrees, &latitude, &latitude_fixed, &lat))
newDataValue(NMEA_LAT, latitudeDegrees);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
// parse out both longitude and direction, then go to next field, or fail
if (parseCoord(p, &longitudeDegrees, &longitude, &longitude_fixed, &lon))
newDataValue(NMEA_LON, longitudeDegrees);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
parseTime(p);
p = strchr(p, ',') + 1;
parseFix(p); // skip the rest
} else if (!strcmp(thisSentence, "GSA")) { //*****************************GSA
// in Adafruit from Actisense NGW-1
p = strchr(p, ',') + 1; // skip selection mode
if (!isEmpty(p))
fixquality_3d = atoi(p);
p = strchr(p, ',') + 1;
// skip 12 Satellite PDNs without interpreting them
for (int i = 0; i < 12; i++)
p = strchr(p, ',') + 1;
if (!isEmpty(p))
PDOP = atof(p);
p = strchr(p, ',') + 1;
// parse out HDOP, we also parse this from the GGA sentence. Chipset should
// report the same for both
if (!isEmpty(p))
newDataValue(NMEA_HDOP, HDOP = atof(p));
p = strchr(p, ',') + 1;
if (!isEmpty(p))
VDOP = atof(p); // last before checksum
} else if (!strcmp(thisSentence, "TOP")) { //*****************************TOP
// See:
// https://learn.adafruit.com/adafruit-ultimate-gps-featherwing/antenna-options
// There is an output sentence that will tell you the status of the
// antenna. $PGTOP,11,x where x is the status number. If x is 3 that means
// it is using the external antenna. If x is 2 it's using the internal
p = strchr(p, ',') + 1;
parseAntenna(p);
}
#ifdef NMEA_EXTENSIONS // Sentences not required for basic GPS functionality
else if (!strcmp(thisSentence, "APB")) { //*******************************APB
// from Actisense NGW-1 from SH CP150C
return false;
} else if (!strcmp(thisSentence, "DBT")) { //*****************************DBT
// from Actisense NGW-1
// feet, metres, fathoms below transducer coerced to water depth from
// surface in metres
if (!isEmpty(p))
newDataValue(NMEA_DEPTH,
(nmea_float_t)atof(p) * 0.3048f + depthToTransducer);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_DEPTH, (nmea_float_t)atof(p) + depthToTransducer);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_DEPTH,
(nmea_float_t)atof(p) * 6 * 0.3048f + depthToTransducer);
} else if (!strcmp(thisSentence, "DPT")) { //*****************************DPT
// from Actisense NGW-1
return false;
} else if (!strcmp(thisSentence, "GSV")) { //*****************************GSV
// from Actisense NGW-1
return false;
} else if (!strcmp(thisSentence, "HDG")) { //*****************************HDG
// from Actisense NGW-1 from SH CP150C
return false;
} else if (!strcmp(thisSentence, "HDM")) { //*****************************HDM
if (!isEmpty(p))
newDataValue(NMEA_HDG, atof(p)); // skip the rest
} else if (!strcmp(thisSentence, "HDT")) { //*****************************HDT
if (!isEmpty(p))
newDataValue(NMEA_HDT, atof(p)); // skip the rest
} else if (!strcmp(thisSentence, "MDA")) { //*****************************MDA
// from Actisense NGW-1
if (!isEmpty(p))
newDataValue(NMEA_BAROMETER, atof(p) * 3386.39);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_BAROMETER, atof(p) * 100000);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
nmea_float_t T = 100000.;
char u = 'C';
if (!isEmpty(p))
T = atof(p);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
u = *p;
p = strchr(p, ',') + 1;
if (u != 'C') {
T = (T - 32) / 1.8f;
u = 'C';
} // coerce to C
if (T < 1000)
newDataValue(NMEA_TEMPERATURE_AIR, T);
T = 100000.;
u = 'C';
if (!isEmpty(p))
T = atof(p);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
u = *p;
p = strchr(p, ',') + 1;
if (u != 'C') {
T = (T - 32) / 1.8f;
u = 'C';
}
if (T < 1000)
newDataValue(NMEA_TEMPERATURE_WATER, T);
if (!isEmpty(p))
newDataValue(NMEA_HUMIDITY, atof(p)); // skip the rest
} else if (!strcmp(thisSentence, "MTW")) { //*****************************MTW
nmea_float_t T = 100000.;
char u = 'C';
if (!isEmpty(p))
T = atof(p);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
u = *p; // last before checksum
if (u != 'C') {
T = (T - 32) / 1.8f;
u = 'C';
}
if (T < 1000)
newDataValue(NMEA_TEMPERATURE_WATER, T);
} else if (!strcmp(thisSentence, "MWD")) { //*****************************MWD
// from Actisense NGW-1
return false;
} else if (!strcmp(thisSentence, "MWV")) { //*****************************MWV
// from Actisense NGW-1
nmea_float_t ang = 100000.;
char ref = 'T';
if (!isEmpty(p))
ang = atof(p);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
ref = *p;
p = strchr(p, ',') + 1;
nmea_float_t spd = 100000.;
if (!isEmpty(p))
spd = atof(p);
p = strchr(p, ',') + 1;
char units = 'N';
if (!isEmpty(p))
units = *p;
p = strchr(p, ',') + 1;
char stat = 'A';
if (!isEmpty(p))
stat = *p; // last before checksum
if (units == 'K') {
spd /= 1.6f;
units = 'M';
}
if (units == 'M') {
spd *= 5280.0f / 6000.0f;
units = 'N';
}
if (ang > 180.0f)
ang -= 360.0f;
if (ref == 'R') {
if (ang < 1000.0f && stat == 'A')
newDataValue(NMEA_AWA, ang);
if (spd < 1000.0f && stat == 'A')
newDataValue(NMEA_AWS, spd);
} else {
if (ang < 1000.0f && stat == 'A')
newDataValue(NMEA_TWA, ang);
if (spd < 1000.0f && stat == 'A')
newDataValue(NMEA_TWS, spd);
}
} else if (!strcmp(thisSentence, "RMB")) { //*****************************RMB
// from Actisense NGW-1 from SH CP150C
// RMB Recommended Minimum Navigation Information
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14
// | | | | | | | | | | | | | |
//$--RMB,A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A*hh
// 1) Status, V = Navigation receiver warning
// 2) Cross Track error - nautical miles
// 3) Direction to Steer, Left or Right
// 4) TO Waypoint ID
// 5) FROM Waypoint ID
// 6) Destination Waypoint Latitude 7) N or S
// 8) Destination Waypoint Longitude 9) E or W
// 10) Range to destination in nautical miles
// 11) Bearing to destination in degrees True
// 12) Destination closing velocity in knots
// 13) Arrival Status, A = Arrival Circle Entered 14) Checksum
p = strchr(p, ',') + 1; // skip status
nmea_float_t xte = 100000.;
char xteDir = 'X';
if (!isEmpty(p))
xte = atof(p);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
xteDir = *p;
p = strchr(p, ',') + 1;
if (xte < 10000.0f && xteDir != 'X') {
if (xteDir == 'L')
xte *= -1.0f;
newDataValue(NMEA_XTE, xte);
}
if (!isEmpty(p))
parseStr(toID, p, NMEA_MAX_WP_ID);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
parseStr(fromID, p, NMEA_MAX_WP_ID);
p = strchr(p, ',') + 1;
nmea_float_t latitudeWP = 0;
nmea_float_t longitudeWP = 0;
int32_t latitude_fixedWP = 0;
int32_t longitude_fixedWP = 0;
nmea_float_t latitudeDegreesWP = 0;
nmea_float_t longitudeDegreesWP = 0;
char latWP = 'X';
char lonWP = 'X';
// parse out both latitude and direction for WayPoint, then go to next
// field, or fail
if (!isEmpty(p)) {
if (!parseCoord(p, &latitudeDegreesWP, &latitudeWP, &latitude_fixedWP,
&latWP))
return false;
else
newDataValue(NMEA_LATWP, latitudeDegreesWP);
}
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
// parse out both longitude and direction for WayPoint, then go to next
// field, or fail
if (!isEmpty(p)) {
if (!parseCoord(p, &longitudeDegreesWP, &longitudeWP, &longitude_fixedWP,
&lonWP))
return false;
else
newDataValue(NMEA_LONWP, longitudeDegreesWP);
}
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_DISTWP, atof(p));
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_COGWP, atof(p));
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_VMGWP, atof(p)); // skip arrival flag
} else if (!strcmp(thisSentence, "ROT")) { //*****************************ROT
return false;
} else if (!strcmp(thisSentence, "RPM")) { //*****************************RPM
return false;
} else if (!strcmp(thisSentence, "RSA")) { //*****************************RSA
// from Actisense NGW-1
return false;
} 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
} else if (!strcmp(thisSentence, "VDR")) { //*****************************VDR
// from Actisense NGW-1
return false;
} else if (!strcmp(thisSentence, "VHW")) { //*****************************VHW
// from Actisense NGW-1
if (!isEmpty(p))
newDataValue(NMEA_HDT, atof(p));
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_HDG, atof(p));
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_VTW, atof(p)); // skip the other units
} else if (!strcmp(thisSentence, "VLW")) { //*****************************VLW
// from Actisense NGW-1
if (!isEmpty(p))
newDataValue(NMEA_LOG, atof(p));
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
newDataValue(NMEA_LOGR, atof(p)); // skip the other units
} else if (!strcmp(thisSentence, "VPW")) { //*****************************VPW
// knots, metres/s coerced to knots
nmea_float_t vmg = 100000.;
if (!isEmpty(p))
vmg = atof(p);
p = strchr(p, ',') + 1;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
vmg = atof(p) * 0.3048 * 3600. / 6000.; // skip units
if (vmg < 1000.0f)
newDataValue(NMEA_VMG, vmg);
} else if (!strcmp(thisSentence, "VTG")) { //*****************************VTG
// from Actisense NGW-1 from SH CP150C
return false;
} else if (!strcmp(thisSentence, "VWR")) { //*****************************VWR
// from Actisense NGW-1
nmea_float_t ang = 1000.;
if (!isEmpty(p))
ang = atof(p);
p = strchr(p, ',') + 1;
char ref = ' ';
if (!isEmpty(p))
ref = *p;
p = strchr(p, ',') + 1;
if (ref == 'L')
ang *= -1;
if (ang < 1000.0f)
newDataValue(NMEA_AWA, ang);
nmea_float_t ws = 0.0;
char units = 'X';
if (!isEmpty(p))
ws = atof(p);
p = strchr(p, ',') + 1; // knots
if (!isEmpty(p))
units = *p;
p = strchr(p, ',') + 1;
if (!isEmpty(p))
ws = atof(p);
p = strchr(p, ',') + 1; // meters / second
if (!isEmpty(p))
units = *p;
p = strchr(p, ',') + 1; // M
if (!isEmpty(p))
ws = atof(p);
p = strchr(p, ',') + 1; // kilometers / hour can be converted back to knots
if (!isEmpty(p))
units = *p; // last before checksum
if (units == 'M') {
ws *= 3.6f;
units = 'K';
} // convert m/s to km/h
if (units == 'K') {
ws /= 1.6f;
units = 'M';
} // convert km/h to miles / h
if (units == 'M') {
ws *= 5280.0f / 6000.0f;
units = 'N';
} // convert miles / hr to knots
if (units == 'N')
newDataValue(NMEA_AWS, ws); // store the final result
} else if (!strcmp(thisSentence, "WCV")) { //*****************************WCV
// from SH CP150C
if (!isEmpty(p))
newDataValue(NMEA_VMGWP, atof(p)); // skip the rest
} else if (!strcmp(thisSentence, "XTE")) { //*****************************XTE
// from Actisense NGW-1 from SH CP150C
p = strchr(p, ',') + 1; // skip status 1
p = strchr(p, ',') + 1; // skip status 2
nmea_float_t xte = 100000.;
char xteDir = 'X';
if (!isEmpty(p))
xte = atof(p);
p = strchr(p, ',') + 1;
if (!isEmpty(p))
xteDir = *p;
p = strchr(p, ',') + 1;
if (xte < 10000.0f && xteDir != 'X') {
if (xteDir == 'L')
xte *= -1.0f;
newDataValue(NMEA_XTE, xte);
} // skip units
} else if (!strcmp(thisSentence, "ZDA")) { //*****************************ZDA
// from Actisense NGW-1
return false;
}
#endif // NMEA_EXTENSIONS
else {
return false; // didn't find the required sentence definition
}
// Record the successful parsing of where the last data came from and when
strcpy(lastSource, thisSource);
strcpy(lastSentence, thisSentence);
lastUpdate = millis();
return true;
}
/**************************************************************************/
/*!
@brief Check an NMEA string for basic format, valid source ID and valid
and valid sentence ID. Update the values of thisCheck, thisSource and
thisSentence.
@param nmea Pointer to the NMEA string
@return True if well formed, false if it has problems
*/
/**************************************************************************/
bool Adafruit_GPS::check(char *nmea) {
thisCheck = 0; // new check
*thisSentence = *thisSource = 0;
if (*nmea != '$' && *nmea != '!')
return false; // doesn't start with $ or !
else
thisCheck += NMEA_HAS_DOLLAR;
// do checksum check -- first look if we even have one -- ignore all but last
// *
char *ast = nmea; // not strchr(nmea,'*'); for first *
while (*ast)
ast++; // go to the end
while (*ast != '*' && ast > nmea)
ast--; // then back to * if it's there
if (*ast != '*')
return false; // there is no asterisk
else {
uint16_t sum = parseHex(*(ast + 1)) * 16; // extract checksum
sum += parseHex(*(ast + 2));
char *p = nmea; // check checksum
for (char *p1 = p + 1; p1 < ast; p1++)
sum ^= *p1;
if (sum != 0)
return false; // bad checksum :(
else
thisCheck += NMEA_HAS_CHECKSUM;
}
// extract source of variable length
char *p = nmea + 1;
const char *src = tokenOnList(p, sources);
if (src) {
strcpy(thisSource, src);
thisCheck += NMEA_HAS_SOURCE;
} else
return false;
p += strlen(src);
// extract sentence id and check if parsed
const char *snc = tokenOnList(p, sentences_parsed);
if (snc) {
strcpy(thisSentence, snc);
thisCheck += NMEA_HAS_SENTENCE_P + NMEA_HAS_SENTENCE;
} else { // check if known
snc = tokenOnList(p, sentences_known);
if (snc) {
strcpy(thisSentence, snc);
thisCheck += NMEA_HAS_SENTENCE;
return false; // known but not parsed
} else {
parseStr(thisSentence, p, NMEA_MAX_SENTENCE_ID);
return false; // unknown
}
}
return true; // passed all the tests
}
/**************************************************************************/
/*!
@brief Check if a token at the start of a string is on a list.
@param token Pointer to the string
@param list A list of strings, with the final entry starting "ZZ"
@return Pointer to the found token, or NULL if it fails
*/
/**************************************************************************/
const char *Adafruit_GPS::tokenOnList(char *token, const char **list) {
int i = 0; // index in the list
while (strncmp(list[i], "ZZ", 2) &&
i < 1000) { // stop at terminator and don't crash without it
// test for a match on the sentence name
if (!strncmp((const char *)list[i], (const char *)token, strlen(list[i])))
return list[i];
i++;
}
return NULL; // couldn't find a match
}
/**************************************************************************/
/*!
@brief Check if an NMEA string is valid and is on a list, perhaps to
decide if it should be passed to a particular NMEA device.
@param nmea Pointer to the NMEA string
@param list A list of strings, with the final entry "ZZ"
@return True if on the list, false if it fails check or is not on the list
*/
/**************************************************************************/
bool Adafruit_GPS::onList(char *nmea, const char **list) {
if (!check(nmea)) // sets thisSentence if valid
return false; // not a valid sentence
// stop at terminator with first two letters ZZ and don't crash without it
for (int i = 0; strncmp(list[i], "ZZ", 2) && i < 1000; i++) {
// test for a match on the sentence name
if (!strcmp((const char *)list[i], (const char *)thisSentence))
return true;
}
return false; // couldn't find a match
}
/**************************************************************************/
/*!
@brief Parse a part of an NMEA string for lat or lon angle and direction.
Works for either DDMM.mmmm,N (latitude) or DDDMM.mmmm,W (longitude) format.
Insensitive to number of decimal places present. Only fills the variables
if it succeeds and the variable pointer is not NULL. This allows calling
to fill only the variables of interest. Does rudimentary validation on
angle range.
Supersedes private functions parseLat(), parseLon(), parseLatDir(),
parseLonDir(), all previously called from parse().
@param pStart Pointer to the location of the token in the NMEA string
@param angle Pointer to the angle to fill with value in degrees/minutes as
received from the GPS (DDDMM.MMMM), unsigned
@param angle_fixed Pointer to the fix point version latitude in decimal
degrees * 10000000, signed
@param angleDegrees Pointer to the angle to fill with decimal degrees,
signed. As actual double on SAMD, etc. resolution is better than the
fixed point version.
@param dir Pointer to character to fill the direction N/S/E/W
@return true if successful, false if failed or no value
*/
/**************************************************************************/
bool Adafruit_GPS::parseCoord(char *pStart, nmea_float_t *angleDegrees,
nmea_float_t *angle, int32_t *angle_fixed,
char *dir) {
char *p = pStart;
if (!isEmpty(p)) {
// get the number in DDDMM.mmmm format and break into components
char degreebuff[10] = {0}; // Ensure string is terminated after strncpy
char *e = strchr(p, '.');
if (e == NULL || e - p > 6)
return false; // no decimal point in range
strncpy(degreebuff, p, e - p); // get DDDMM
long dddmm = atol(degreebuff);
long degrees = (dddmm / 100); // truncate the minutes
long minutes = dddmm - degrees * 100; // remove the degrees
p = e; // start from the decimal point
nmea_float_t decminutes = atof(e); // the fraction after the decimal point
p = strchr(p, ',') + 1; // go to the next field
// get the NSEW direction as a character
char nsew = 'X';
if (!isEmpty(p))
nsew = *p; // field is not empty
else
return false; // no direction provided
// set the various numerical formats to their values
long fixed = degrees * 10000000 + (minutes * 10000000) / 60 +
(decminutes * 10000000) / 60;
nmea_float_t ang = degrees * 100 + minutes + decminutes;
nmea_float_t deg = fixed / (nmea_float_t)10000000.;
if (nsew == 'S' ||
nsew == 'W') { // fixed and deg are signed, but DDDMM.mmmm is not
fixed = -fixed;
deg = -deg;
}
// reject directions that are not NSEW
if (nsew != 'N' && nsew != 'S' && nsew != 'E' && nsew != 'W')
return false;
// reject angles that are out of range
if (nsew == 'N' || nsew == 'S')
if (abs(deg) > 90)
return false;
if (abs(deg) > 180)
return false;
// store in locations passed as args
if (angle != NULL)
*angle = ang;
if (angle_fixed != NULL)
*angle_fixed = fixed;
if (angleDegrees != NULL)
*angleDegrees = deg;
if (dir != NULL)
*dir = nsew;
} else
return false; // no number
return true;
}
/**************************************************************************/
/*!
@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(int(e - p), n - 1);
strncpy(buff, p, len); // copy up to the comma
buff[len] = 0;
} else {
e = strchr(p, '*');
if (e) {
len = min(int(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 Parse a part of an NMEA string for time. Independent of number
of decimal places after the '.'
@param p Pointer to the location of the token in the NMEA string
@return true if successful, false otherwise
*/
/**************************************************************************/
bool Adafruit_GPS::parseTime(char *p) {
if (!isEmpty(p)) { // get time
uint32_t time = atol(p);
hour = time / 10000;
minute = (time % 10000) / 100;
seconds = (time % 100);
char *dec = strchr(p, '.');
char *comstar = min(strchr(p, ','), strchr(p, '*'));
if (dec != NULL && comstar != NULL && dec < comstar)
milliseconds = atof(dec) * 1000;
else
milliseconds = 0;
lastTime = sentTime;
return true;
}
return false;
}
/**************************************************************************/
/*!
@brief Parse a part of an NMEA string for whether there is a fix
@param p Pointer to the location of the token in the NMEA string
@return True if we parsed it, false if it has invalid data
*/
/**************************************************************************/
bool Adafruit_GPS::parseFix(char *p) {
if (!isEmpty(p)) {
if (p[0] == 'A') {
fix = true;
lastFix = sentTime;
} else if (p[0] == 'V')
fix = false;
else
return false;
return true;
}
return false;
}
/**************************************************************************/
/*!
@brief Parse a part of an NMEA string for antenna that is used
@param p Pointer to the location of the token in the NMEA string
@return 3=external 2=internal 1=there was an antenna short or problem
*/
/**************************************************************************/
bool Adafruit_GPS::parseAntenna(char *p) {
if (!isEmpty(p)) {
if (p[0] == '3') {
antenna = 3;
} else if (p[0] == '2') {
antenna = 2;
} else if (p[0] == '1') {
antenna = 1;
} else
return false;
return true;
}
return false;
}
/**************************************************************************/
/*!
@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 Parse a hex character and return the appropriate decimal value
@param c Hex character, e.g. '0' or 'B'
@return Integer value of the hex character. Returns 0 if c is not a proper
character
*/
/**************************************************************************/
// read a Hex value and return the decimal equivalent
uint8_t Adafruit_GPS::parseHex(char c) {
if (c < '0')
return 0;
if (c <= '9')
return c - '0';
if (c < 'A')
return 0;
if (c <= 'F')
return (c - 'A') + 10;
// if (c > 'F')
return 0;
}