2020-01-29 14:56:21 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@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.
|
|
|
|
|
|
2022-09-20 17:11:22 +01:00
|
|
|
@author Rick Sellens.
|
2020-01-29 14:56:21 +00:00
|
|
|
|
2022-09-20 17:11:22 +01:00
|
|
|
@copyright CCBY license
|
2020-01-29 14:56:21 +00:00
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
|
|
|
|
|
#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
|
2020-02-23 23:31:37 +00:00
|
|
|
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));
|
2020-01-29 14:56:21 +00:00
|
|
|
}
|
|
|
|
|
// weighting factor for smoothing depends on delta t / tau
|
2020-01-29 18:10:43 +00:00
|
|
|
nmea_float_t w =
|
2020-01-29 17:57:20 +00:00
|
|
|
min((nmea_float_t)1.0,
|
2020-02-20 18:03:13 +00:00
|
|
|
(nmea_float_t)(millis() - val[idx].lastUpdate) / val[idx].response);
|
2020-01-29 14:56:21 +00:00
|
|
|
// default smoothing
|
2020-02-23 23:31:37 +00:00
|
|
|
val[idx].smoothed = (1.0f - w) * val[idx].smoothed + w * v;
|
2020-01-29 14:56:21 +00:00
|
|
|
// 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
|
2020-01-30 22:09:35 +00:00
|
|
|
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.
|
2020-01-29 14:56:21 +00:00
|
|
|
@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.
|
2022-09-20 17:11:22 +01:00
|
|
|
@param historyN Set size of data buffer.
|
2020-01-29 14:56:21 +00:00
|
|
|
@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;
|
2020-02-23 23:31:37 +00:00
|
|
|
if (scale > 0.0f)
|
2020-01-29 14:56:21 +00:00
|
|
|
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: ");
|
2020-01-29 18:10:43 +00:00
|
|
|
if (idx < 10)
|
2020-01-29 17:57:20 +00:00
|
|
|
Serial.print(" ");
|
2020-01-29 14:56:21 +00:00
|
|
|
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]);
|
2020-01-29 17:57:20 +00:00
|
|
|
for (unsigned i = val[idx].hist->n - 2;
|
2020-01-29 18:10:43 +00:00
|
|
|
i >= max(val[idx].hist->n - n, (unsigned)0);
|
|
|
|
|
i--) { // most recent first
|
2020-01-29 14:56:21 +00:00
|
|
|
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: ");
|
2020-01-29 17:57:20 +00:00
|
|
|
Serial.print(latitudeDegrees, 8);
|
2020-01-29 14:56:21 +00:00
|
|
|
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: ");
|
2020-01-29 17:57:20 +00:00
|
|
|
Serial.print(longitudeDegrees, 8);
|
2020-01-29 14:56:21 +00:00
|
|
|
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) {
|
2020-02-23 23:52:46 +00:00
|
|
|
// put the sin angle in -90 to 90 range
|
|
|
|
|
nmea_float_t sAng = asin(s) * (nmea_float_t)RAD_TO_DEG;
|
2020-01-29 14:56:21 +00:00
|
|
|
while (sAng < -90)
|
2020-02-23 23:31:37 +00:00
|
|
|
sAng += 180.0f;
|
2020-01-29 14:56:21 +00:00
|
|
|
while (sAng > 90)
|
2020-02-23 23:31:37 +00:00
|
|
|
sAng -= 180.0f;
|
2020-02-23 23:52:46 +00:00
|
|
|
// put the cos angle in 0 to 180 range
|
|
|
|
|
nmea_float_t cAng = acos(c) * (nmea_float_t)RAD_TO_DEG;
|
2020-01-29 14:56:21 +00:00
|
|
|
while (cAng < 0)
|
2020-02-23 23:31:37 +00:00
|
|
|
cAng += 180.0f;
|
2020-01-29 14:56:21 +00:00
|
|
|
while (cAng > 180)
|
2020-02-23 23:31:37 +00:00
|
|
|
cAng -= 180.0f;
|
2020-01-29 14:56:21 +00:00
|
|
|
// 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)
|
2020-02-23 23:31:37 +00:00
|
|
|
ang += 360.0f; // round up
|
2020-01-29 14:56:21 +00:00
|
|
|
while (ang > 360)
|
2020-02-23 23:31:37 +00:00
|
|
|
ang -= 360.0f; // round down
|
2020-01-29 14:56:21 +00:00
|
|
|
}
|
|
|
|
|
return ang;
|
|
|
|
|
}
|
|
|
|
|
#endif // NMEA_EXTENSIONS
|