initial commit
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# Adafruit_GPS [](https://travis-ci.com/adafruit/Adafruit_GPS) [](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.
|
||||
+43
@@ -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);
|
||||
}
|
||||
}
|
||||
+66
@@ -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
|
||||
+68
@@ -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);
|
||||
}
|
||||
}
|
||||
+71
@@ -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....
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
+102
@@ -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
|
||||
+114
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+215
@@ -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);
|
||||
}
|
||||
}
|
||||
+67
@@ -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);
|
||||
}
|
||||
}
|
||||
+60
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -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
|
||||
+105
@@ -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
|
||||
+143
@@ -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
|
||||
+117
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user