Merge pull request #203 from Scorgan01/PageWindPlot
History Buffer + Wind Calculation CleanUp
This commit is contained in:
commit
be946440d3
|
@ -3,7 +3,8 @@
|
||||||
#ifndef _BOATDATACALIBRATION_H
|
#ifndef _BOATDATACALIBRATION_H
|
||||||
#define _BOATDATACALIBRATION_H
|
#define _BOATDATACALIBRATION_H
|
||||||
|
|
||||||
#include "Pagedata.h"
|
// #include "Pagedata.h"
|
||||||
|
#include "GwApi.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,147 @@
|
||||||
#include "OBPDataOperations.h"
|
#include "OBPDataOperations.h"
|
||||||
|
|
||||||
|
// --- Class HstryBuf ---------------
|
||||||
|
// Init history buffers for selected boat data
|
||||||
|
void HstryBuf::init(BoatValueList* boatValues, GwLog *log) {
|
||||||
|
|
||||||
|
logger = log;
|
||||||
|
|
||||||
|
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
|
||||||
|
int hstryMinVal = 0; // Minimum value for these history buffers
|
||||||
|
twdHstryMax = 6283; // Max value for wind direction (TWD, AWD) in rad [0...2*PI], shifted by 1000 for 3 decimals
|
||||||
|
twsHstryMax = 65000; // Max value for wind speed (TWS, AWS) in m/s [0..65], shifted by 1000 for 3 decimals
|
||||||
|
awdHstryMax = twdHstryMax;
|
||||||
|
awsHstryMax = twsHstryMax;
|
||||||
|
twdHstryMin = hstryMinVal;
|
||||||
|
twsHstryMin = hstryMinVal;
|
||||||
|
awdHstryMin = hstryMinVal;
|
||||||
|
awsHstryMin = hstryMinVal;
|
||||||
|
const double DBL_MAX = std::numeric_limits<double>::max();
|
||||||
|
|
||||||
|
// Initialize history buffers with meta data
|
||||||
|
hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
|
||||||
|
hstryBufList.twsHstry->setMetaData("TWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
|
||||||
|
hstryBufList.awdHstry->setMetaData("AWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
|
||||||
|
hstryBufList.awsHstry->setMetaData("AWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
|
||||||
|
|
||||||
|
// create boat values for history data types, if they don't exist yet
|
||||||
|
twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
|
||||||
|
twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
|
||||||
|
twaBVal = boatValues->findValueOrCreate("TWA");
|
||||||
|
awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName());
|
||||||
|
awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName());
|
||||||
|
|
||||||
|
if (!awdBVal->valid) { // AWD usually does not exist
|
||||||
|
awdBVal->setFormat(hstryBufList.awdHstry->getFormat());
|
||||||
|
awdBVal->value = DBL_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect boat values for true wind calculation
|
||||||
|
awaBVal = boatValues->findValueOrCreate("AWA");
|
||||||
|
hdtBVal = boatValues->findValueOrCreate("HDT");
|
||||||
|
hdmBVal = boatValues->findValueOrCreate("HDM");
|
||||||
|
varBVal = boatValues->findValueOrCreate("VAR");
|
||||||
|
cogBVal = boatValues->findValueOrCreate("COG");
|
||||||
|
sogBVal = boatValues->findValueOrCreate("SOG");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle history buffers for TWD, TWS, AWD, AWS
|
||||||
|
//void HstryBuf::handleHstryBuf(GwApi* api, BoatValueList* boatValues, bool useSimuData) {
|
||||||
|
void HstryBuf::handleHstryBuf(bool useSimuData) {
|
||||||
|
|
||||||
|
static int16_t twd = 20; //initial value only relevant if we use simulation data
|
||||||
|
static uint16_t tws = 20; //initial value only relevant if we use simulation data
|
||||||
|
static double awd, aws, hdt = 20; //initial value only relevant if we use simulation data
|
||||||
|
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::DEBUG,"obp60task handleHstryBuf: TWD_isValid? %d, twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f", twdBVal->valid, twdBVal->value * RAD_TO_DEG,
|
||||||
|
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
|
||||||
|
|
||||||
|
if (twdBVal->valid) {
|
||||||
|
calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values
|
||||||
|
calBVal->setFormat(twdBVal->getFormat());
|
||||||
|
calBVal->value = twdBVal->value;
|
||||||
|
calBVal->valid = twdBVal->valid;
|
||||||
|
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
||||||
|
twd = static_cast<int16_t>(std::round(calBVal->value * 1000.0));
|
||||||
|
if (twd >= twdHstryMin && twd <= twdHstryMax) {
|
||||||
|
hstryBufList.twdHstry->add(twd);
|
||||||
|
}
|
||||||
|
delete calBVal;
|
||||||
|
calBVal = nullptr;
|
||||||
|
} else if (useSimuData) {
|
||||||
|
twd += random(-20, 20);
|
||||||
|
twd = WindUtils::to360(twd);
|
||||||
|
hstryBufList.twdHstry->add(static_cast<int16_t>(DegToRad(twd) * 1000.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twsBVal->valid) {
|
||||||
|
calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values
|
||||||
|
calBVal->setFormat(twsBVal->getFormat());
|
||||||
|
calBVal->value = twsBVal->value;
|
||||||
|
calBVal->valid = twsBVal->valid;
|
||||||
|
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
||||||
|
tws = static_cast<uint16_t>(std::round(calBVal->value * 1000));
|
||||||
|
if (tws >= twsHstryMin && tws <= twsHstryMax) {
|
||||||
|
hstryBufList.twsHstry->add(tws);
|
||||||
|
}
|
||||||
|
delete calBVal;
|
||||||
|
calBVal = nullptr;
|
||||||
|
} else if (useSimuData) {
|
||||||
|
tws += random(-5000, 5000); // TWS value in m/s; expands to 3 decimals
|
||||||
|
tws = constrain(tws, 0, 25000); // Limit TWS to [0..25] m/s
|
||||||
|
hstryBufList.twsHstry->add(tws);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (awaBVal->valid) {
|
||||||
|
if (hdtBVal->valid) {
|
||||||
|
hdt = hdtBVal->value; // Use HDT if available
|
||||||
|
} else {
|
||||||
|
hdt = WindUtils::calcHDT(&hdmBVal->value, &varBVal->value, &cogBVal->value, &sogBVal->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
awd = awaBVal->value + hdt;
|
||||||
|
awd = WindUtils::to2PI(awd);
|
||||||
|
calBVal = new GwApi::BoatValue("AWD"); // temporary solution for calibration of history buffer values
|
||||||
|
calBVal->value = awd;
|
||||||
|
calBVal->setFormat(awdBVal->getFormat());
|
||||||
|
calBVal->valid = true;
|
||||||
|
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
||||||
|
awdBVal->value = calBVal->value;
|
||||||
|
awdBVal->valid = true;
|
||||||
|
awd = std::round(calBVal->value * 1000.0);
|
||||||
|
if (awd >= awdHstryMin && awd <= awdHstryMax) {
|
||||||
|
hstryBufList.awdHstry->add(static_cast<int16_t>(awd));
|
||||||
|
}
|
||||||
|
delete calBVal;
|
||||||
|
calBVal = nullptr;
|
||||||
|
} else if (useSimuData) {
|
||||||
|
awd += random(-20, 20);
|
||||||
|
awd = WindUtils::to360(awd);
|
||||||
|
hstryBufList.awdHstry->add(static_cast<int16_t>(DegToRad(awd) * 1000.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (awsBVal->valid) {
|
||||||
|
calBVal = new GwApi::BoatValue("AWS"); // temporary solution for calibration of history buffer values
|
||||||
|
calBVal->setFormat(awsBVal->getFormat());
|
||||||
|
calBVal->value = awsBVal->value;
|
||||||
|
calBVal->valid = awsBVal->valid;
|
||||||
|
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
||||||
|
aws = std::round(calBVal->value * 1000);
|
||||||
|
if (aws >= awsHstryMin && aws <= awsHstryMax) {
|
||||||
|
hstryBufList.awsHstry->add(static_cast<uint16_t>(aws));
|
||||||
|
}
|
||||||
|
delete calBVal;
|
||||||
|
calBVal = nullptr;
|
||||||
|
} else if (useSimuData) {
|
||||||
|
aws += random(-5000, 5000); // TWS value in m/s; expands to 1 decimal
|
||||||
|
aws = constrain(aws, 0, 25000); // Limit TWS to [0..25] m/s
|
||||||
|
hstryBufList.awsHstry->add(aws);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- Class HstryBuf ---------------
|
||||||
|
|
||||||
|
// --- Class WindUtils --------------
|
||||||
double WindUtils::to2PI(double a)
|
double WindUtils::to2PI(double a)
|
||||||
{
|
{
|
||||||
a = fmod(a, 2 * M_PI);
|
a = fmod(a, 2 * M_PI);
|
||||||
|
@ -68,29 +210,25 @@ void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
|
||||||
double awd = *AWA + *HDT;
|
double awd = *AWA + *HDT;
|
||||||
awd = to2PI(awd);
|
awd = to2PI(awd);
|
||||||
double stw = -*STW;
|
double stw = -*STW;
|
||||||
// Serial.println("\ncalcTwdSA: AWA: " + String(*AWA) + ", AWS: " + String(*AWS) + ", CTW: " + String(*CTW) + ", STW: " + String(*STW) + ", HDT: " + String(*HDT));
|
|
||||||
addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
|
addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
|
||||||
|
|
||||||
// Normalize TWD and TWA to 0-360°
|
// Normalize TWD and TWA to 0-360°
|
||||||
*TWD = to2PI(*TWD);
|
*TWD = to2PI(*TWD);
|
||||||
*TWA = toPI(*TWD - *HDT);
|
*TWA = toPI(*TWD - *HDT);
|
||||||
// Serial.println("calcTwdSA: TWD: " + String(*TWD) + ", TWS: " + String(*TWS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal)
|
double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal)
|
||||||
{
|
{
|
||||||
double hdt;
|
double hdt;
|
||||||
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
||||||
static const double DBL_MIN = std::numeric_limits<double>::lowest();
|
|
||||||
|
|
||||||
// Serial.println("\ncalcTrueWind: HDT: " + String(*hdtVal) + ", HDM: " + String(*hdmVal) + ", VAR: " + String(*varVal) + ", SOG: " + String(*sogVal) + ", COG: " + String(*cogVal));
|
if (*hdmVal != DBL_MAX) {
|
||||||
if (*hdmVal != DBL_MIN) {
|
hdt = *hdmVal + (*varVal != DBL_MAX ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
|
||||||
hdt = *hdmVal + (*varVal != DBL_MIN ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
|
|
||||||
hdt = to2PI(hdt);
|
hdt = to2PI(hdt);
|
||||||
} else if (*cogVal != DBL_MIN && *sogVal >= minSogVal) {
|
} else if (*cogVal != DBL_MAX && *sogVal >= minSogVal) {
|
||||||
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
|
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
|
||||||
} else {
|
} else {
|
||||||
hdt = DBL_MIN; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
|
hdt = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
|
||||||
}
|
}
|
||||||
|
|
||||||
return hdt;
|
return hdt;
|
||||||
|
@ -103,37 +241,23 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
|
||||||
double stw, hdt, ctw;
|
double stw, hdt, ctw;
|
||||||
double twd, tws, twa;
|
double twd, tws, twa;
|
||||||
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
||||||
static const double DBL_MIN = std::numeric_limits<double>::lowest();
|
|
||||||
|
|
||||||
// Serial.println("\ncalcTrueWind: HDT: " + String(*hdtVal) + ", HDM: " + String(*hdmVal) + ", VAR: " + String(*varVal) + ", SOG: " + String(*sogVal) + ", COG: " + String(*cogVal));
|
if (*hdtVal != DBL_MAX) {
|
||||||
/* if (*hdtVal != DBL_MIN) {
|
|
||||||
hdt = *hdtVal; // Use HDT if available
|
|
||||||
} else {
|
|
||||||
if (*hdmVal != DBL_MIN) {
|
|
||||||
hdt = *hdmVal + (*varVal != DBL_MIN ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
|
|
||||||
hdt = to2PI(hdt);
|
|
||||||
} else if (*cogVal != DBL_MIN && *sogVal >= minSogVal) {
|
|
||||||
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
|
|
||||||
} else {
|
|
||||||
return false; // Cannot calculate without valid HDT or HDM+VAR or COG
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
if (*hdtVal != DBL_MIN) {
|
|
||||||
hdt = *hdtVal; // Use HDT if available
|
hdt = *hdtVal; // Use HDT if available
|
||||||
} else {
|
} else {
|
||||||
hdt = calcHDT(hdmVal, varVal, cogVal, sogVal);
|
hdt = calcHDT(hdmVal, varVal, cogVal, sogVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*cogVal != DBL_MIN && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG
|
if (*cogVal != DBL_MAX && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG
|
||||||
|
|
||||||
ctw = *cogVal; // Use COG for CTW if available
|
ctw = *cogVal; // Use COG for CTW if available
|
||||||
} else {
|
} else {
|
||||||
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
|
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*stwVal != DBL_MIN) {
|
if (*stwVal != DBL_MAX) {
|
||||||
stw = *stwVal; // Use STW if available
|
stw = *stwVal; // Use STW if available
|
||||||
} else if (*sogVal != DBL_MIN) {
|
} else if (*sogVal != DBL_MAX) {
|
||||||
stw = *sogVal;
|
stw = *sogVal;
|
||||||
} else {
|
} else {
|
||||||
// If STW and SOG are not available, we cannot calculate true wind
|
// If STW and SOG are not available, we cannot calculate true wind
|
||||||
|
@ -141,7 +265,7 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
|
||||||
}
|
}
|
||||||
// Serial.println("\ncalcTrueWind: HDT: " + String(hdt) + ", CTW: " + String(ctw) + ", STW: " + String(stw));
|
// Serial.println("\ncalcTrueWind: HDT: " + String(hdt) + ", CTW: " + String(ctw) + ", STW: " + String(stw));
|
||||||
|
|
||||||
if ((*awaVal == DBL_MIN) || (*awsVal == DBL_MIN)) {
|
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
|
||||||
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
|
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,3 +277,46 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate true wind data and add to obp60task boat data list
|
||||||
|
bool WindUtils::addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog* log) {
|
||||||
|
|
||||||
|
GwLog* logger = log;
|
||||||
|
|
||||||
|
double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
|
||||||
|
double twd, tws, twa;
|
||||||
|
bool isCalculated = false;
|
||||||
|
|
||||||
|
awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
|
||||||
|
awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
|
||||||
|
cogVal = cogBVal->valid ? cogBVal->value : DBL_MAX;
|
||||||
|
stwVal = stwBVal->valid ? stwBVal->value : DBL_MAX;
|
||||||
|
sogVal = sogBVal->valid ? sogBVal->value : DBL_MAX;
|
||||||
|
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
|
||||||
|
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
|
||||||
|
varVal = varBVal->valid ? varBVal->value : DBL_MAX;
|
||||||
|
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
|
||||||
|
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
|
||||||
|
|
||||||
|
isCalculated = calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa);
|
||||||
|
|
||||||
|
if (isCalculated) { // Replace values only, if successfully calculated and not already available
|
||||||
|
if (!twdBVal->valid) {
|
||||||
|
twdBVal->value = twd;
|
||||||
|
twdBVal->valid = true;
|
||||||
|
}
|
||||||
|
if (!twsBVal->valid) {
|
||||||
|
twsBVal->value = tws;
|
||||||
|
twsBVal->valid = true;
|
||||||
|
}
|
||||||
|
if (!twaBVal->valid) {
|
||||||
|
twaBVal->value = twa;
|
||||||
|
twaBVal->valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: isCalculated %d, TWD %.1f, TWA %.1f, TWS %.1f", isCalculated, twdBVal->value * RAD_TO_DEG,
|
||||||
|
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
|
||||||
|
|
||||||
|
return isCalculated;
|
||||||
|
}
|
||||||
|
// --- Class WindUtils --------------
|
||||||
|
|
|
@ -1,39 +1,90 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "GwApi.h"
|
#include <N2kMessages.h>
|
||||||
#include "OBPRingBuffer.h"
|
#include "OBPRingBuffer.h"
|
||||||
// #include <Arduino.h>
|
#include "BoatDataCalibration.h" // Functions lib for data instance calibration
|
||||||
|
#include "obp60task.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
RingBuffer<int16_t>* twdHstry;
|
RingBuffer<int16_t>* twdHstry;
|
||||||
RingBuffer<int16_t>* twsHstry;
|
RingBuffer<uint16_t>* twsHstry;
|
||||||
RingBuffer<int16_t>* awdHstry;
|
RingBuffer<int16_t>* awdHstry;
|
||||||
RingBuffer<int16_t>* awsHstry;
|
RingBuffer<uint16_t>* awsHstry;
|
||||||
} tBoatHstryData; // Holds pointers to all history buffers for boat data
|
} tBoatHstryData; // Holds pointers to all history buffers for boat data
|
||||||
|
|
||||||
class HstryBuf {
|
class HstryBuf {
|
||||||
|
private:
|
||||||
|
GwLog *logger;
|
||||||
|
|
||||||
|
RingBuffer<int16_t> twdHstry; // Circular buffer to store true wind direction values
|
||||||
|
RingBuffer<uint16_t> twsHstry; // Circular buffer to store true wind speed values (TWS)
|
||||||
|
RingBuffer<int16_t> awdHstry; // Circular buffer to store apparant wind direction values
|
||||||
|
RingBuffer<uint16_t> awsHstry; // Circular buffer to store apparant xwind speed values (AWS)
|
||||||
|
int16_t twdHstryMin; // Min value for wind direction (TWD) in history buffer
|
||||||
|
int16_t twdHstryMax; // Max value for wind direction (TWD) in history buffer
|
||||||
|
uint16_t twsHstryMin;
|
||||||
|
uint16_t twsHstryMax;
|
||||||
|
int16_t awdHstryMin;
|
||||||
|
int16_t awdHstryMax;
|
||||||
|
uint16_t awsHstryMin;
|
||||||
|
uint16_t awsHstryMax;
|
||||||
|
|
||||||
|
// boat values for buffers and for true wind calculation
|
||||||
|
GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal, *awdBVal, *awsBVal;
|
||||||
|
GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
tBoatHstryData hstryBufList;
|
||||||
|
|
||||||
|
HstryBuf(){
|
||||||
|
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry}; // Generate history buffers of zero size
|
||||||
|
};
|
||||||
|
HstryBuf(int size) {
|
||||||
|
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry};
|
||||||
|
hstryBufList.twdHstry->resize(960); // store 960 TWD values for 16 minutes history
|
||||||
|
hstryBufList.twsHstry->resize(960);
|
||||||
|
hstryBufList.awdHstry->resize(960);
|
||||||
|
hstryBufList.awsHstry->resize(960);
|
||||||
|
};
|
||||||
|
void init(BoatValueList* boatValues, GwLog *log);
|
||||||
|
void handleHstryBuf(bool useSimuData);
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindUtils {
|
class WindUtils {
|
||||||
|
private:
|
||||||
|
GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal;
|
||||||
|
GwApi::BoatValue *awaBVal, *awsBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
|
||||||
|
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
WindUtils(BoatValueList* boatValues){
|
||||||
|
twdBVal = boatValues->findValueOrCreate("TWD");
|
||||||
|
twsBVal = boatValues->findValueOrCreate("TWS");
|
||||||
|
twaBVal = boatValues->findValueOrCreate("TWA");
|
||||||
|
awaBVal = boatValues->findValueOrCreate("AWA");
|
||||||
|
awsBVal = boatValues->findValueOrCreate("AWS");
|
||||||
|
cogBVal = boatValues->findValueOrCreate("COG");
|
||||||
|
stwBVal = boatValues->findValueOrCreate("STW");
|
||||||
|
sogBVal = boatValues->findValueOrCreate("SOG");
|
||||||
|
hdtBVal = boatValues->findValueOrCreate("HDT");
|
||||||
|
hdmBVal = boatValues->findValueOrCreate("HDM");
|
||||||
|
varBVal = boatValues->findValueOrCreate("VAR");
|
||||||
|
};
|
||||||
static double to2PI(double a);
|
static double to2PI(double a);
|
||||||
static double toPI(double a);
|
static double toPI(double a);
|
||||||
static double to360(double a);
|
static double to360(double a);
|
||||||
static double to180(double a);
|
static double to180(double a);
|
||||||
static void toCart(const double* phi, const double* r, double* x, double* y);
|
void toCart(const double* phi, const double* r, double* x, double* y);
|
||||||
static void toPol(const double* x, const double* y, double* phi, double* r);
|
void toPol(const double* x, const double* y, double* phi, double* r);
|
||||||
static void addPolar(const double* phi1, const double* r1,
|
void addPolar(const double* phi1, const double* r1,
|
||||||
const double* phi2, const double* r2,
|
const double* phi2, const double* r2,
|
||||||
double* phi, double* r);
|
double* phi, double* r);
|
||||||
static void calcTwdSA(const double* AWA, const double* AWS,
|
void calcTwdSA(const double* AWA, const double* AWS,
|
||||||
const double* CTW, const double* STW, const double* HDT,
|
const double* CTW, const double* STW, const double* HDT,
|
||||||
double* TWD, double* TWS, double* TWA);
|
double* TWD, double* TWS, double* TWA);
|
||||||
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
|
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
|
||||||
static bool calcTrueWind(const double* awaVal, const double* awsVal,
|
bool calcTrueWind(const double* awaVal, const double* awsVal,
|
||||||
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
||||||
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal);
|
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal);
|
||||||
|
bool addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog *log);
|
||||||
};
|
};
|
|
@ -9,28 +9,32 @@
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class RingBuffer {
|
class RingBuffer {
|
||||||
private:
|
private:
|
||||||
mutable SemaphoreHandle_t bufLocker;
|
std::vector<T> buffer; // THE buffer vector
|
||||||
std::vector<T> buffer;
|
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
size_t head; // Points to the next insertion position
|
size_t head; // Points to the next insertion position
|
||||||
size_t first; // Points to the first (oldest) valid element
|
size_t first; // Points to the first (oldest) valid element
|
||||||
size_t last; // Points to the last (newest) valid element
|
size_t last; // Points to the last (newest) valid element
|
||||||
size_t count; // Number of valid elements currently in buffer
|
size_t count; // Number of valid elements currently in buffer
|
||||||
bool is_Full; // Indicates that all buffer elements are used and ringing is in use
|
bool is_Full; // Indicates that all buffer elements are used and ringing is in use
|
||||||
T MIN_VAL; // lowest possible value of buffer
|
T MIN_VAL; // lowest possible value of buffer of type <T>
|
||||||
T MAX_VAL; // highest possible value of buffer of type <T>
|
T MAX_VAL; // highest possible value of buffer of type <T> -> indicates invalid value in buffer
|
||||||
|
mutable SemaphoreHandle_t bufLocker;
|
||||||
|
|
||||||
// metadata for buffer
|
// metadata for buffer
|
||||||
String dataName; // Name of boat data in buffer
|
String dataName; // Name of boat data in buffer
|
||||||
String dataFmt; // Format of boat data in buffer
|
String dataFmt; // Format of boat data in buffer
|
||||||
int updFreq; // Update frequency in milliseconds
|
int updFreq; // Update frequency in milliseconds
|
||||||
T smallest; // Value range of buffer: smallest value
|
T smallest; // Value range of buffer: smallest value; needs to be => MIN_VAL
|
||||||
T largest; // Value range of buffer: biggest value
|
T largest; // Value range of buffer: biggest value; needs to be < MAX_VAL, since MAX_VAL indicates invalid entries
|
||||||
|
|
||||||
|
void initCommon();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
RingBuffer();
|
||||||
RingBuffer(size_t size);
|
RingBuffer(size_t size);
|
||||||
void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
|
void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
|
||||||
bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
|
bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
|
||||||
|
bool getMetaData(String& name, String& format);
|
||||||
String getName() const; // Get buffer name
|
String getName() const; // Get buffer name
|
||||||
String getFormat() const; // Get buffer data format
|
String getFormat() const; // Get buffer data format
|
||||||
void add(const T& value); // Add a new value to buffer
|
void add(const T& value); // Add a new value to buffer
|
||||||
|
@ -51,9 +55,10 @@ public:
|
||||||
size_t getLastIdx() const; // Get the index of newest value in buffer
|
size_t getLastIdx() const; // Get the index of newest value in buffer
|
||||||
bool isEmpty() const; // Check if buffer is empty
|
bool isEmpty() const; // Check if buffer is empty
|
||||||
bool isFull() const; // Check if buffer is full
|
bool isFull() const; // Check if buffer is full
|
||||||
T getMinVal() const; // Get lowest possible value for buffer; used for initialized buffer data
|
T getMinVal() const; // Get lowest possible value for buffer
|
||||||
T getMaxVal() const; // Get highest possible value for buffer
|
T getMaxVal() const; // Get highest possible value for buffer; used for unset/invalid buffer data
|
||||||
void clear(); // Clear buffer
|
void clear(); // Clear buffer
|
||||||
|
void resize(size_t size); // Delete buffer and set new size
|
||||||
T operator[](size_t index) const; // Operator[] for convenient access (same as get())
|
T operator[](size_t index) const; // Operator[] for convenient access (same as get())
|
||||||
std::vector<T> getAllValues() const; // Get all current values as a vector
|
std::vector<T> getAllValues() const; // Get all current values as a vector
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,30 @@
|
||||||
#include "OBPRingBuffer.h"
|
#include "OBPRingBuffer.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RingBuffer<T>::initCommon() {
|
||||||
|
MIN_VAL = std::numeric_limits<T>::lowest();
|
||||||
|
MAX_VAL = std::numeric_limits<T>::max();
|
||||||
|
dataName = "";
|
||||||
|
dataFmt = "";
|
||||||
|
updFreq = -1;
|
||||||
|
smallest = MIN_VAL;
|
||||||
|
largest = MAX_VAL;
|
||||||
|
bufLocker = xSemaphoreCreateMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
RingBuffer<T>::RingBuffer()
|
||||||
|
: capacity(0)
|
||||||
|
, head(0)
|
||||||
|
, first(0)
|
||||||
|
, last(0)
|
||||||
|
, count(0)
|
||||||
|
, is_Full(false)
|
||||||
|
{
|
||||||
|
initCommon();
|
||||||
|
// <buffer> stays empty
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
RingBuffer<T>::RingBuffer(size_t size)
|
RingBuffer<T>::RingBuffer(size_t size)
|
||||||
: capacity(size)
|
: capacity(size)
|
||||||
|
@ -9,23 +34,8 @@ RingBuffer<T>::RingBuffer(size_t size)
|
||||||
, count(0)
|
, count(0)
|
||||||
, is_Full(false)
|
, is_Full(false)
|
||||||
{
|
{
|
||||||
bufLocker = xSemaphoreCreateMutex();
|
initCommon();
|
||||||
|
buffer.resize(size, MAX_VAL); // MAX_VAL indicate invalid values
|
||||||
if (size == 0) {
|
|
||||||
// return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MIN_VAL = std::numeric_limits<T>::lowest();
|
|
||||||
MAX_VAL = std::numeric_limits<T>::max();
|
|
||||||
dataName = "";
|
|
||||||
dataFmt = "";
|
|
||||||
updFreq = -1;
|
|
||||||
smallest = MIN_VAL;
|
|
||||||
largest = MAX_VAL;
|
|
||||||
|
|
||||||
buffer.resize(size, MIN_VAL);
|
|
||||||
|
|
||||||
// return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specify meta data of buffer content
|
// Specify meta data of buffer content
|
||||||
|
@ -57,6 +67,20 @@ bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequen
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get meta data of buffer content
|
||||||
|
template <typename T>
|
||||||
|
bool RingBuffer<T>::getMetaData(String& name, String& format)
|
||||||
|
{
|
||||||
|
if (dataName == "" || dataFmt == "") {
|
||||||
|
return false; // Meta data not set
|
||||||
|
}
|
||||||
|
|
||||||
|
GWSYNCHRONIZED(&bufLocker);
|
||||||
|
name = dataName;
|
||||||
|
format = dataFmt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Get buffer name
|
// Get buffer name
|
||||||
template <typename T>
|
template <typename T>
|
||||||
String RingBuffer<T>::getName() const
|
String RingBuffer<T>::getName() const
|
||||||
|
@ -77,7 +101,7 @@ void RingBuffer<T>::add(const T& value)
|
||||||
{
|
{
|
||||||
GWSYNCHRONIZED(&bufLocker);
|
GWSYNCHRONIZED(&bufLocker);
|
||||||
if (value < smallest || value > largest) {
|
if (value < smallest || value > largest) {
|
||||||
buffer[head] = MIN_VAL; // Store MIN_VAL if value is out of range
|
buffer[head] = MAX_VAL; // Store MAX_VAL if value is out of range
|
||||||
} else {
|
} else {
|
||||||
buffer[head] = value;
|
buffer[head] = value;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +125,7 @@ T RingBuffer<T>::get(size_t index) const
|
||||||
{
|
{
|
||||||
GWSYNCHRONIZED(&bufLocker);
|
GWSYNCHRONIZED(&bufLocker);
|
||||||
if (isEmpty() || index < 0 || index >= count) {
|
if (isEmpty() || index < 0 || index >= count) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t realIndex = (first + index) % capacity;
|
size_t realIndex = (first + index) % capacity;
|
||||||
|
@ -120,7 +144,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getFirst() const
|
T RingBuffer<T>::getFirst() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
return get(0);
|
return get(0);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +154,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getLast() const
|
T RingBuffer<T>::getLast() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
return get(count - 1);
|
return get(count - 1);
|
||||||
}
|
}
|
||||||
|
@ -140,14 +164,14 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMin() const
|
T RingBuffer<T>::getMin() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
T minVal = MAX_VAL;
|
T minVal = MAX_VAL;
|
||||||
T value;
|
T value;
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
value = get(i);
|
value = get(i);
|
||||||
if (value < minVal && value != MIN_VAL) {
|
if (value < minVal && value != MAX_VAL) {
|
||||||
minVal = value;
|
minVal = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +183,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMin(size_t amount) const
|
T RingBuffer<T>::getMin(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
@ -168,7 +192,7 @@ T RingBuffer<T>::getMin(size_t amount) const
|
||||||
T value;
|
T value;
|
||||||
for (size_t i = 0; i < amount; i++) {
|
for (size_t i = 0; i < amount; i++) {
|
||||||
value = get(count - 1 - i);
|
value = get(count - 1 - i);
|
||||||
if (value < minVal && value != MIN_VAL) {
|
if (value < minVal && value != MAX_VAL) {
|
||||||
minVal = value;
|
minVal = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,14 +204,14 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMax() const
|
T RingBuffer<T>::getMax() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
T maxVal = MIN_VAL;
|
T maxVal = MIN_VAL;
|
||||||
T value;
|
T value;
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
value = get(i);
|
value = get(i);
|
||||||
if (value > maxVal && value != MIN_VAL) {
|
if (value > maxVal && value != MAX_VAL) {
|
||||||
maxVal = value;
|
maxVal = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +223,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMax(size_t amount) const
|
T RingBuffer<T>::getMax(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
@ -208,7 +232,7 @@ T RingBuffer<T>::getMax(size_t amount) const
|
||||||
T value;
|
T value;
|
||||||
for (size_t i = 0; i < amount; i++) {
|
for (size_t i = 0; i < amount; i++) {
|
||||||
value = get(count - 1 - i);
|
value = get(count - 1 - i);
|
||||||
if (value > maxVal && value != MIN_VAL) {
|
if (value > maxVal && value != MAX_VAL) {
|
||||||
maxVal = value;
|
maxVal = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +244,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMid() const
|
T RingBuffer<T>::getMid() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (getMin() + getMax()) / static_cast<T>(2);
|
return (getMin() + getMax()) / static_cast<T>(2);
|
||||||
|
@ -231,7 +255,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMid(size_t amount) const
|
T RingBuffer<T>::getMid(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
|
@ -245,7 +269,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMedian() const
|
T RingBuffer<T>::getMedian() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary vector with current valid elements
|
// Create a temporary vector with current valid elements
|
||||||
|
@ -274,7 +298,7 @@ template <typename T>
|
||||||
T RingBuffer<T>::getMedian(size_t amount) const
|
T RingBuffer<T>::getMedian(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return MIN_VAL;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
@ -342,14 +366,14 @@ bool RingBuffer<T>::isFull() const
|
||||||
return is_Full;
|
return is_Full;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get lowest possible value for buffer; used for non-set buffer data
|
// Get lowest possible value for buffer
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T RingBuffer<T>::getMinVal() const
|
T RingBuffer<T>::getMinVal() const
|
||||||
{
|
{
|
||||||
return MIN_VAL;
|
return MIN_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get highest possible value for buffer
|
// Get highest possible value for buffer; used for unset/invalid buffer data
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T RingBuffer<T>::getMaxVal() const
|
T RingBuffer<T>::getMaxVal() const
|
||||||
{
|
{
|
||||||
|
@ -368,6 +392,22 @@ void RingBuffer<T>::clear()
|
||||||
is_Full = false;
|
is_Full = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete buffer and set new size
|
||||||
|
template <typename T>
|
||||||
|
void RingBuffer<T>::resize(size_t newSize)
|
||||||
|
{
|
||||||
|
GWSYNCHRONIZED(&bufLocker);
|
||||||
|
capacity = newSize;
|
||||||
|
head = 0;
|
||||||
|
first = 0;
|
||||||
|
last = 0;
|
||||||
|
count = 0;
|
||||||
|
is_Full = false;
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
buffer.resize(newSize, MAX_VAL);
|
||||||
|
}
|
||||||
|
|
||||||
// Get all current values as a vector
|
// Get all current values as a vector
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::vector<T> RingBuffer<T>::getAllValues() const
|
std::vector<T> RingBuffer<T>::getAllValues() const
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
||||||
|
|
||||||
#include "BoatDataCalibration.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "OBPRingBuffer.h"
|
#include "OBPRingBuffer.h"
|
||||||
#include "Pagedata.h"
|
#include "OBPDataOperations.h"
|
||||||
|
#include "BoatDataCalibration.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
|
static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
|
||||||
|
@ -11,7 +12,7 @@ static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians t
|
||||||
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart; returns "0" if data is not valid
|
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart; returns "0" if data is not valid
|
||||||
int getCntr(const RingBuffer<int16_t>& windDirHstry, size_t amount)
|
int getCntr(const RingBuffer<int16_t>& windDirHstry, size_t amount)
|
||||||
{
|
{
|
||||||
int minVal = windDirHstry.getMinVal();
|
const int MAX_VAL = windDirHstry.getMaxVal();
|
||||||
size_t count = windDirHstry.getCurrentSize();
|
size_t count = windDirHstry.getCurrentSize();
|
||||||
|
|
||||||
if (windDirHstry.isEmpty() || amount <= 0) {
|
if (windDirHstry.isEmpty() || amount <= 0) {
|
||||||
|
@ -20,11 +21,11 @@ int getCntr(const RingBuffer<int16_t>& windDirHstry, size_t amount)
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
|
||||||
int16_t midWndDir, minWndDir, maxWndDir = 0;
|
uint16_t midWndDir, minWndDir, maxWndDir = 0;
|
||||||
int wndCenter = 0;
|
int wndCenter = 0;
|
||||||
|
|
||||||
midWndDir = windDirHstry.getMid(amount);
|
midWndDir = windDirHstry.getMid(amount);
|
||||||
if (midWndDir != INT16_MIN) {
|
if (midWndDir != MAX_VAL) {
|
||||||
midWndDir = midWndDir / 1000.0 * radToDeg;
|
midWndDir = midWndDir / 1000.0 * radToDeg;
|
||||||
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
|
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
|
||||||
minWndDir = windDirHstry.getMin(amount) / 1000.0 * radToDeg;
|
minWndDir = windDirHstry.getMin(amount) / 1000.0 * radToDeg;
|
||||||
|
@ -41,10 +42,11 @@ int getCntr(const RingBuffer<int16_t>& windDirHstry, size_t amount)
|
||||||
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
|
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
|
||||||
{
|
{
|
||||||
int minVal = windDirHstry.getMinVal();
|
int minVal = windDirHstry.getMinVal();
|
||||||
|
const int MAX_VAL = windDirHstry.getMaxVal();
|
||||||
size_t count = windDirHstry.getCurrentSize();
|
size_t count = windDirHstry.getCurrentSize();
|
||||||
|
|
||||||
if (windDirHstry.isEmpty() || amount <= 0) {
|
if (windDirHstry.isEmpty() || amount <= 0) {
|
||||||
return minVal;
|
return MAX_VAL;
|
||||||
}
|
}
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
@ -56,8 +58,8 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
|
||||||
for (size_t i = 0; i < amount; i++) {
|
for (size_t i = 0; i < amount; i++) {
|
||||||
value = windDirHstry.get(count - 1 - i);
|
value = windDirHstry.get(count - 1 - i);
|
||||||
|
|
||||||
if (value == minVal) {
|
if (value == MAX_VAL) {
|
||||||
continue;
|
continue; // ignore invalid values
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value / 1000.0 * radToDeg;
|
value = value / 1000.0 * radToDeg;
|
||||||
|
@ -69,7 +71,7 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
|
||||||
maxRng = 180;
|
maxRng = 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
return maxRng;
|
return (maxRng != minVal ? maxRng : MAX_VAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
|
@ -78,6 +80,8 @@ class PageWindPlot : public Page {
|
||||||
bool keylock = false; // Keylock
|
bool keylock = false; // Keylock
|
||||||
char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both
|
char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both
|
||||||
bool showTruW = true; // Show true wind or apparant wind in chart area
|
bool showTruW = true; // Show true wind or apparant wind in chart area
|
||||||
|
bool oldShowTruW = false; // remember recent user selection of wind data type
|
||||||
|
|
||||||
int dataIntv = 1; // Update interval for wind history chart:
|
int dataIntv = 1; // Update interval for wind history chart:
|
||||||
// (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart
|
// (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart
|
||||||
bool useSimuData;
|
bool useSimuData;
|
||||||
|
@ -101,7 +105,7 @@ public:
|
||||||
virtual void setupKeys()
|
virtual void setupKeys()
|
||||||
{
|
{
|
||||||
Page::setupKeys();
|
Page::setupKeys();
|
||||||
// commonData->keydata[0].label = "MODE";
|
// commonData->keydata[0].label = "MODE";
|
||||||
#if defined BOARD_OBP60S3
|
#if defined BOARD_OBP60S3
|
||||||
commonData->keydata[1].label = "SRC";
|
commonData->keydata[1].label = "SRC";
|
||||||
commonData->keydata[4].label = "INTV";
|
commonData->keydata[4].label = "INTV";
|
||||||
|
@ -169,34 +173,27 @@ public:
|
||||||
}
|
}
|
||||||
commonData->logger->logDebug(GwLog::LOG,"New PageWindPlot: wind source=%s", wndSrc);
|
commonData->logger->logDebug(GwLog::LOG,"New PageWindPlot: wind source=%s", wndSrc);
|
||||||
#endif
|
#endif
|
||||||
|
oldShowTruW = !showTruW; // makes wind source being initialized at initial page call
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int displayPage(PageData& pageData)
|
int displayPage(PageData& pageData)
|
||||||
{
|
{
|
||||||
GwConfigHandler* config = commonData->config;
|
GwConfigHandler* config = commonData->config;
|
||||||
GwLog* logger = commonData->logger;
|
GwLog* logger = commonData->logger;
|
||||||
|
|
||||||
static RingBuffer<int16_t>* wdHstry; // Wind direction data buffer
|
static RingBuffer<int16_t>* wdHstry; // Wind direction data buffer
|
||||||
static RingBuffer<int16_t>* wsHstry; // Wind speed data buffer
|
static RingBuffer<uint16_t>* wsHstry; // Wind speed data buffer
|
||||||
static String wdName, wdFormat; // Wind direction name and format
|
static String wdName, wdFormat; // Wind direction name and format
|
||||||
static String wsName, wsFormat; // Wind speed name and format
|
static String wsName, wsFormat; // Wind speed name and format
|
||||||
static int updFreq; // Update frequency for wind direction
|
static int16_t wdMAX_VAL; // Max. value of wd history buffer, indicating invalid values
|
||||||
static int16_t wdLowest, wdHighest; // Wind direction range
|
|
||||||
float wsValue; // Wind speed value in chart area
|
float wsValue; // Wind speed value in chart area
|
||||||
String wsUnit; // Wind speed unit in chart area
|
String wsUnit; // Wind speed unit in chart area
|
||||||
static GwApi::BoatValue* wsBVal = new GwApi::BoatValue("TWS"); // temp BoatValue for wind speed unit identification; required by OBP60Formater
|
static GwApi::BoatValue* wsBVal = new GwApi::BoatValue("TWS"); // temp BoatValue for wind speed unit identification; required by OBP60Formater
|
||||||
|
|
||||||
// current boat data values; TWD/AWD only for validation test, TWS/AWS for display of current value
|
// current boat data values; TWD/AWD only for validation test
|
||||||
const int numBoatData = 4;
|
const int numBoatData = 2;
|
||||||
GwApi::BoatValue* bvalue;
|
GwApi::BoatValue* bvalue;
|
||||||
String BDataName[numBoatData];
|
|
||||||
double BDataValue[numBoatData];
|
|
||||||
bool BDataValid[numBoatData];
|
bool BDataValid[numBoatData];
|
||||||
String BDataText[numBoatData];
|
|
||||||
String BDataUnit[numBoatData];
|
|
||||||
String BDataFormat[numBoatData];
|
|
||||||
|
|
||||||
static bool isInitialized = false; // Flag to indicate that page is initialized
|
static bool isInitialized = false; // Flag to indicate that page is initialized
|
||||||
static bool wndDataValid = false; // Flag to indicate if wind data is valid
|
static bool wndDataValid = false; // Flag to indicate if wind data is valid
|
||||||
|
@ -217,7 +214,6 @@ public:
|
||||||
static size_t lastIdx; // Last index of TWD history buffer
|
static size_t lastIdx; // Last index of TWD history buffer
|
||||||
static size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
|
static size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
|
||||||
static int oldDataIntv; // remember recent user selection of data interval
|
static int oldDataIntv; // remember recent user selection of data interval
|
||||||
static bool oldShowTruW; // remember recent user selection of wind data type
|
|
||||||
|
|
||||||
static int wndCenter; // chart wind center value position
|
static int wndCenter; // chart wind center value position
|
||||||
static int wndLeft; // chart wind left value position
|
static int wndLeft; // chart wind left value position
|
||||||
|
@ -234,13 +230,8 @@ public:
|
||||||
static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
|
static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::LOG, "Display PageWindPlot");
|
LOG_DEBUG(GwLog::LOG, "Display PageWindPlot");
|
||||||
|
ulong timer = millis();
|
||||||
|
|
||||||
/* // Get config data
|
|
||||||
bool useSimuData = config->getBool(config->useSimuData);
|
|
||||||
// holdValues = config->getBool(config->holdvalues);
|
|
||||||
String flashLED = config->getString(config->flashLED);
|
|
||||||
String backlightMode = config->getString(config->backlight);
|
|
||||||
*/
|
|
||||||
if (!isInitialized) {
|
if (!isInitialized) {
|
||||||
width = getdisplay().width();
|
width = getdisplay().width();
|
||||||
height = getdisplay().height();
|
height = getdisplay().height();
|
||||||
|
@ -249,18 +240,9 @@ public:
|
||||||
numNoData = 0;
|
numNoData = 0;
|
||||||
bufStart = 0;
|
bufStart = 0;
|
||||||
oldDataIntv = 0;
|
oldDataIntv = 0;
|
||||||
oldShowTruW = false; // we want to initialize wind buffers at 1st time routine runs
|
|
||||||
wdHstry = pageData.boatHstry.twdHstry;
|
|
||||||
bufSize = wdHstry->getCapacity();
|
|
||||||
wsHstry = pageData.boatHstry.twsHstry;
|
|
||||||
bufSize = wsHstry->getCapacity();
|
|
||||||
wdHstry->getMetaData(wdName, wdFormat, updFreq, wdLowest, wdHighest);
|
|
||||||
wsHstry->getMetaData(wsName, wsFormat, updFreq, wdLowest, wdHighest);
|
|
||||||
wsValue = 0;
|
wsValue = 0;
|
||||||
wsBVal->setFormat(wsHstry->getFormat());
|
|
||||||
numAddedBufVals, currIdx, lastIdx = 0;
|
numAddedBufVals, currIdx, lastIdx = 0;
|
||||||
lastAddedIdx = wdHstry->getLastIdx();
|
wndCenter = INT_MAX;
|
||||||
wndCenter = INT_MIN;
|
|
||||||
midWndDir = 0;
|
midWndDir = 0;
|
||||||
diffRng = dfltRng;
|
diffRng = dfltRng;
|
||||||
chrtRng = dfltRng;
|
chrtRng = dfltRng;
|
||||||
|
@ -271,14 +253,7 @@ public:
|
||||||
// read boat data values; TWD only for validation test, TWS for display of current value
|
// read boat data values; TWD only for validation test, TWS for display of current value
|
||||||
for (int i = 0; i < numBoatData; i++) {
|
for (int i = 0; i < numBoatData; i++) {
|
||||||
bvalue = pageData.values[i];
|
bvalue = pageData.values[i];
|
||||||
BDataName[i] = xdrDelete(bvalue->getName());
|
|
||||||
BDataName[i] = BDataName[i].substring(0, 6); // String length limit for value name
|
|
||||||
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
|
|
||||||
BDataValue[i] = bvalue->value; // Value as double in SI unit
|
|
||||||
BDataValid[i] = bvalue->valid;
|
BDataValid[i] = bvalue->valid;
|
||||||
BDataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
|
||||||
BDataUnit[i] = formatValue(bvalue, *commonData).unit;
|
|
||||||
BDataFormat[i] = bvalue->getFormat(); // Unit of value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optical warning by limit violation (unused)
|
// Optical warning by limit violation (unused)
|
||||||
|
@ -289,16 +264,18 @@ public:
|
||||||
|
|
||||||
if (showTruW != oldShowTruW) {
|
if (showTruW != oldShowTruW) {
|
||||||
if (showTruW) {
|
if (showTruW) {
|
||||||
wdHstry = pageData.boatHstry.twdHstry;
|
wdHstry = pageData.boatHstry->hstryBufList.twdHstry;
|
||||||
wsHstry = pageData.boatHstry.twsHstry;
|
wsHstry = pageData.boatHstry->hstryBufList.twsHstry;
|
||||||
} else {
|
} else {
|
||||||
wdHstry = pageData.boatHstry.awdHstry;
|
wdHstry = pageData.boatHstry->hstryBufList.awdHstry;
|
||||||
wsHstry = pageData.boatHstry.awsHstry;
|
wsHstry = pageData.boatHstry->hstryBufList.awsHstry;
|
||||||
}
|
}
|
||||||
wdHstry->getMetaData(wdName, wdFormat, updFreq, wdLowest, wdHighest);
|
wdHstry->getMetaData(wdName, wdFormat);
|
||||||
wsHstry->getMetaData(wsName, wsFormat, updFreq, wdLowest, wdHighest);
|
wsHstry->getMetaData(wsName, wsFormat);
|
||||||
|
wdMAX_VAL = wdHstry->getMaxVal();
|
||||||
bufSize = wdHstry->getCapacity();
|
bufSize = wdHstry->getCapacity();
|
||||||
wsBVal->setFormat(wsHstry->getFormat());
|
wsBVal->setFormat(wsHstry->getFormat());
|
||||||
|
lastAddedIdx = wdHstry->getLastIdx();
|
||||||
|
|
||||||
oldShowTruW = showTruW;
|
oldShowTruW = showTruW;
|
||||||
}
|
}
|
||||||
|
@ -321,19 +298,19 @@ public:
|
||||||
bufStart = max(0, bufStart - numAddedBufVals);
|
bufStart = max(0, bufStart - numAddedBufVals);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Dataset: count: %d, xWD: %.0f, xWS: %.1f, xWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, wind source: %s",
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Dataset: count: %d, xWD: %.1f, xWS: %.2f, xWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, wind source: %s",
|
||||||
count, wdHstry->getLast() / 1000.0 * radToDeg, wsHstry->getLast() / 10.0 * 1.94384, BDataValid[0], intvBufSize, numWndVals, bufStart, numAddedBufVals, wdHstry->getLastIdx(),
|
count, wdHstry->getLast() / 1000.0 * radToDeg, wsHstry->getLast() / 1000.0 * 1.94384, BDataValid[0], intvBufSize, numWndVals, bufStart, numAddedBufVals, wdHstry->getLastIdx(),
|
||||||
showTruW ? "True" : "App");
|
showTruW ? "True" : "App");
|
||||||
|
|
||||||
// Set wndCenter from 1st real buffer value
|
// Set wndCenter from 1st real buffer value
|
||||||
if (wndCenter == INT_MIN || (wndCenter == 0 && count == 1)) {
|
if (wndCenter == INT_MAX || (wndCenter == 0 && count == 1)) {
|
||||||
wndCenter = getCntr(*wdHstry, numWndVals);
|
wndCenter = getCntr(*wdHstry, numWndVals);
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, xWD: %.0f, wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", count, wdHstry->getLast() / 1000.0 * radToDeg,
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, xWD: %.1f, wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", count, wdHstry->getLast() / 1000.0 * radToDeg,
|
||||||
wndCenter, diffRng, chrtRng, wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg);
|
wndCenter, diffRng, chrtRng, wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg);
|
||||||
} else {
|
} else {
|
||||||
// check and adjust range between left, center, and right chart limit
|
// check and adjust range between left, center, and right chart limit
|
||||||
diffRng = getRng(*wdHstry, wndCenter, numWndVals);
|
diffRng = getRng(*wdHstry, wndCenter, numWndVals);
|
||||||
diffRng = (diffRng == INT16_MIN ? 0 : diffRng);
|
diffRng = (diffRng == wdMAX_VAL ? 0 : diffRng);
|
||||||
if (diffRng > chrtRng) {
|
if (diffRng > chrtRng) {
|
||||||
chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value
|
chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value
|
||||||
} else if (diffRng + 10 < chrtRng) { // Reduce chart range for higher resolution if possible
|
} else if (diffRng + 10 < chrtRng) { // Reduce chart range for higher resolution if possible
|
||||||
|
@ -365,7 +342,6 @@ public:
|
||||||
char sWndLbl[4]; // char buffer for Wind angle label
|
char sWndLbl[4]; // char buffer for Wind angle label
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(xCenter - 88, yOffset - 3);
|
getdisplay().setCursor(xCenter - 88, yOffset - 3);
|
||||||
// getdisplay().print("TWD"); // Wind data name
|
|
||||||
getdisplay().print(wdName); // Wind data name
|
getdisplay().print(wdName); // Wind data name
|
||||||
snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
|
snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
|
||||||
drawTextCenter(xCenter, yOffset - 11, sWndLbl);
|
drawTextCenter(xCenter, yOffset - 11, sWndLbl);
|
||||||
|
@ -382,11 +358,11 @@ public:
|
||||||
getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
|
getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
|
||||||
getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
|
getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
|
||||||
|
|
||||||
if (wdHstry->getMax() == wdHstry->getMinVal()) {
|
if (wdHstry->getMax() == wdMAX_VAL) {
|
||||||
// only <INT16_MIN> values in buffer -> no valid wind data available
|
// only <MAX_VAL> values in buffer -> no valid wind data available
|
||||||
wndDataValid = false;
|
wndDataValid = false;
|
||||||
} else if (!BDataValid[0] && !useSimuData) {
|
} else if (!BDataValid[0] && !useSimuData) {
|
||||||
// currently no valid TWD data available and no simulation mode
|
// currently no valid xWD data available and no simulation mode
|
||||||
numNoData++;
|
numNoData++;
|
||||||
wndDataValid = true;
|
wndDataValid = true;
|
||||||
if (numNoData > 3) {
|
if (numNoData > 3) {
|
||||||
|
@ -402,8 +378,8 @@ public:
|
||||||
if (wndDataValid) {
|
if (wndDataValid) {
|
||||||
for (int i = 0; i < (numWndVals / dataIntv); i++) {
|
for (int i = 0; i < (numWndVals / dataIntv); i++) {
|
||||||
chrtVal = static_cast<int>(wdHstry->get(bufStart + (i * dataIntv))); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer
|
chrtVal = static_cast<int>(wdHstry->get(bufStart + (i * dataIntv))); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer
|
||||||
if (chrtVal == INT16_MIN) {
|
if (chrtVal == wdMAX_VAL) {
|
||||||
chrtPrevVal = INT16_MIN;
|
chrtPrevVal = wdMAX_VAL;
|
||||||
} else {
|
} else {
|
||||||
chrtVal = static_cast<int>((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round
|
chrtVal = static_cast<int>((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round
|
||||||
x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
|
x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
|
||||||
|
@ -412,7 +388,7 @@ public:
|
||||||
if (i >= (numWndVals / dataIntv) - 1) // log chart data of 1 line (adjust for test purposes)
|
if (i >= (numWndVals / dataIntv) - 1) // log chart data of 1 line (adjust for test purposes)
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
|
||||||
|
|
||||||
if ((i == 0) || (chrtPrevVal == INT16_MIN)) {
|
if ((i == 0) || (chrtPrevVal == wdMAX_VAL)) {
|
||||||
// just a dot for 1st chart point or after some invalid values
|
// just a dot for 1st chart point or after some invalid values
|
||||||
prevX = x;
|
prevX = x;
|
||||||
prevY = y;
|
prevY = y;
|
||||||
|
@ -473,22 +449,24 @@ public:
|
||||||
lastZone = currentZone;
|
lastZone = currentZone;
|
||||||
|
|
||||||
wsValue = wsHstry->getLast();
|
wsValue = wsHstry->getLast();
|
||||||
wsBVal->value = wsValue; // temp variable to retreive data unit from OBP60Formater
|
wsBVal->value = wsValue / 1000.0; // temp variable to retreive data unit from OBP60Formater
|
||||||
wsBVal->valid = (static_cast<int16_t>(wsValue) != wsHstry->getMinVal());
|
wsBVal->valid = (static_cast<uint16_t>(wsValue) != wsHstry->getMinVal());
|
||||||
|
String swsValue = formatValue(wsBVal, *commonData).svalue; // value (string)
|
||||||
wsUnit = formatValue(wsBVal, *commonData).unit; // Unit of value
|
wsUnit = formatValue(wsBVal, *commonData).unit; // Unit of value
|
||||||
getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
|
getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
||||||
getdisplay().setCursor(xPosTws, yPosTws);
|
getdisplay().setCursor(xPosTws, yPosTws);
|
||||||
if (!wsBVal->valid) {
|
getdisplay().print(swsValue); // Value
|
||||||
|
/* if (!wsBVal->valid) {
|
||||||
getdisplay().print("--.-");
|
getdisplay().print("--.-");
|
||||||
} else {
|
} else {
|
||||||
wsValue = wsValue / 10.0 * 1.94384; // Wind speed value in knots
|
wsValue = wsValue / 1000.0 * 1.94384; // Wind speed value in knots
|
||||||
if (wsValue < 10.0) {
|
if (wsValue < 10.0) {
|
||||||
getdisplay().printf("!%3.1f", wsValue); // Value, round to 1 decimal
|
getdisplay().printf("!%3.1f", wsValue); // Value, round to 1 decimal
|
||||||
} else {
|
} else {
|
||||||
getdisplay().printf("%4.1f", wsValue); // Value, round to 1 decimal
|
getdisplay().printf("%4.1f", wsValue); // Value, round to 1 decimal
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
|
getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
|
||||||
getdisplay().print(wsName); // Name
|
getdisplay().print(wsName); // Name
|
||||||
|
@ -523,6 +501,7 @@ public:
|
||||||
getdisplay().printf("%3d", chrtLbl); // Wind value label
|
getdisplay().printf("%3d", chrtLbl); // Wind value label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot time: %ld", millis() - timer);
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -541,7 +520,7 @@ PageDescription registerPageWindPlot(
|
||||||
"WindPlot", // Page name
|
"WindPlot", // Page name
|
||||||
createPage, // Action
|
createPage, // Action
|
||||||
0, // Number of bus values depends on selection in Web configuration
|
0, // Number of bus values depends on selection in Web configuration
|
||||||
{ "TWD", "TWS", "AWD", "AWS" }, // Bus values we need in the page
|
{ "TWD", "AWD" }, // Bus values we need in the page
|
||||||
true // Show display header on/off
|
true // Show display header on/off
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "LedSpiTask.h"
|
#include "LedSpiTask.h"
|
||||||
#include "OBPRingBuffer.h"
|
|
||||||
#include "OBPDataOperations.h"
|
#include "OBPDataOperations.h"
|
||||||
|
|
||||||
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
|
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
|
||||||
|
@ -17,7 +16,7 @@ typedef struct{
|
||||||
uint8_t pageNumber; // page number in sequence of visible pages
|
uint8_t pageNumber; // page number in sequence of visible pages
|
||||||
//the values will always contain the user defined values first
|
//the values will always contain the user defined values first
|
||||||
ValueList values;
|
ValueList values;
|
||||||
tBoatHstryData boatHstry;
|
HstryBuf* boatHstry;
|
||||||
} PageData;
|
} PageData;
|
||||||
|
|
||||||
// Sensor data structure (only for extended sensors, not for NMEA bus sensors)
|
// Sensor data structure (only for extended sensors, not for NMEA bus sensors)
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "OBP60Extensions.h" // Functions lib for extension board
|
#include "OBP60Extensions.h" // Functions lib for extension board
|
||||||
#include "OBP60Keypad.h" // Functions for keypad
|
#include "OBP60Keypad.h" // Functions for keypad
|
||||||
#include "BoatDataCalibration.h" // Functions lib for data instance calibration
|
#include "BoatDataCalibration.h" // Functions lib for data instance calibration
|
||||||
#include "OBPRingBuffer.h" // Functions lib with ring buffer for history storage of some boat data
|
|
||||||
#include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation
|
#include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation
|
||||||
|
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
|
@ -148,39 +147,32 @@ void keyboardTask(void *param){
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
class BoatValueList{
|
// Scorgan: moved class declaration to header file <obp60task.h> to make class available to other functions
|
||||||
public:
|
// --- Class BoatValueList --------------
|
||||||
static const int MAXVALUES=100;
|
bool BoatValueList::addValueToList(GwApi::BoatValue *v){
|
||||||
//we create a list containing all our BoatValues
|
for (int i=0;i<numValues;i++){
|
||||||
//this is the list we later use to let the api fill all the values
|
if (allBoatValues[i] == v){
|
||||||
//additionally we put the necessary values into the paga data - see below
|
//already in list...
|
||||||
GwApi::BoatValue *allBoatValues[MAXVALUES];
|
return true;
|
||||||
int numValues=0;
|
|
||||||
|
|
||||||
bool addValueToList(GwApi::BoatValue *v){
|
|
||||||
for (int i=0;i<numValues;i++){
|
|
||||||
if (allBoatValues[i] == v){
|
|
||||||
//already in list...
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (numValues >= MAXVALUES) return false;
|
|
||||||
allBoatValues[numValues]=v;
|
|
||||||
numValues++;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
//helper to ensure that each BoatValue is only queried once
|
if (numValues >= MAXVALUES) return false;
|
||||||
GwApi::BoatValue *findValueOrCreate(String name){
|
allBoatValues[numValues]=v;
|
||||||
for (int i=0;i<numValues;i++){
|
numValues++;
|
||||||
if (allBoatValues[i]->getName() == name) {
|
return true;
|
||||||
return allBoatValues[i];
|
}
|
||||||
}
|
//helper to ensure that each BoatValue is only queried once
|
||||||
|
GwApi::BoatValue *BoatValueList::findValueOrCreate(String name){
|
||||||
|
for (int i=0;i<numValues;i++){
|
||||||
|
if (allBoatValues[i]->getName() == name) {
|
||||||
|
return allBoatValues[i];
|
||||||
}
|
}
|
||||||
GwApi::BoatValue *rt=new GwApi::BoatValue(name);
|
|
||||||
addValueToList(rt);
|
|
||||||
return rt;
|
|
||||||
}
|
}
|
||||||
};
|
GwApi::BoatValue *rt=new GwApi::BoatValue(name);
|
||||||
|
addValueToList(rt);
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
// --- Class BoatValueList --------------
|
||||||
|
|
||||||
//we want to have a list that has all our page definitions
|
//we want to have a list that has all our page definitions
|
||||||
//this way each page can easily be added here
|
//this way each page can easily be added here
|
||||||
|
@ -329,202 +321,6 @@ inline bool underVoltageDetection(float voffset, float vslope) {
|
||||||
return (calVoltage < minVoltage);
|
return (calVoltage < minVoltage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate true wind data and add to obp60task boat data list
|
|
||||||
bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
|
|
||||||
|
|
||||||
double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
|
|
||||||
double twd, tws, twa;
|
|
||||||
bool isCalculated = false;
|
|
||||||
const double DBL_MIN = std::numeric_limits<double>::lowest();
|
|
||||||
|
|
||||||
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate("TWD");
|
|
||||||
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate("TWS");
|
|
||||||
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
|
|
||||||
GwApi::BoatValue *awaBVal = boatValues->findValueOrCreate("AWA");
|
|
||||||
GwApi::BoatValue *awsBVal = boatValues->findValueOrCreate("AWS");
|
|
||||||
GwApi::BoatValue *cogBVal = boatValues->findValueOrCreate("COG");
|
|
||||||
GwApi::BoatValue *stwBVal = boatValues->findValueOrCreate("STW");
|
|
||||||
GwApi::BoatValue *sogBVal = boatValues->findValueOrCreate("SOG");
|
|
||||||
GwApi::BoatValue *hdtBVal = boatValues->findValueOrCreate("HDT");
|
|
||||||
GwApi::BoatValue *hdmBVal = boatValues->findValueOrCreate("HDM");
|
|
||||||
GwApi::BoatValue *varBVal = boatValues->findValueOrCreate("VAR");
|
|
||||||
awaVal = awaBVal->valid ? awaBVal->value : DBL_MIN;
|
|
||||||
awsVal = awsBVal->valid ? awsBVal->value : DBL_MIN;
|
|
||||||
cogVal = cogBVal->valid ? cogBVal->value : DBL_MIN;
|
|
||||||
stwVal = stwBVal->valid ? stwBVal->value : DBL_MIN;
|
|
||||||
sogVal = sogBVal->valid ? sogBVal->value : DBL_MIN;
|
|
||||||
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MIN;
|
|
||||||
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MIN;
|
|
||||||
varVal = varBVal->valid ? varBVal->value : DBL_MIN;
|
|
||||||
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
|
|
||||||
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
|
|
||||||
|
|
||||||
isCalculated = WindUtils::calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa);
|
|
||||||
|
|
||||||
if (isCalculated) { // Replace values only, if successfully calculated and not already available
|
|
||||||
if (!twdBVal->valid) {
|
|
||||||
twdBVal->value = twd;
|
|
||||||
twdBVal->valid = true;
|
|
||||||
}
|
|
||||||
if (!twsBVal->valid) {
|
|
||||||
twsBVal->value = tws;
|
|
||||||
twsBVal->valid = true;
|
|
||||||
}
|
|
||||||
if (!twaBVal->valid) {
|
|
||||||
twaBVal->value = twa;
|
|
||||||
twaBVal->valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task addTrueWind: isCalculated %d, TWD %.1f, TWA %.1f, TWS %.1f", isCalculated, twdBVal->value * RAD_TO_DEG,
|
|
||||||
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
|
|
||||||
|
|
||||||
return isCalculated;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init history buffers for selected boat data
|
|
||||||
void initHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList) {
|
|
||||||
|
|
||||||
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
|
|
||||||
const double DBL_MIN = std::numeric_limits<double>::lowest();
|
|
||||||
|
|
||||||
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
|
|
||||||
int hstryMinVal = 0; // Minimum value for these history buffers
|
|
||||||
int twdHstryMax = 6283; // Max value for wind direction (TWD, AWD) in rad (0...2*PI), shifted by 1000 for 3 decimals
|
|
||||||
int twsHstryMax = 1000; // Max value for wind speed (TWS, AWS) in m/s, shifted by 10 for 1 decimal
|
|
||||||
// Initialize history buffers with meta data
|
|
||||||
hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
|
|
||||||
hstryBufList.twsHstry->setMetaData("TWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
|
|
||||||
hstryBufList.awdHstry->setMetaData("AWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
|
|
||||||
hstryBufList.awsHstry->setMetaData("AWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
|
|
||||||
|
|
||||||
// create boat values for history data types, if they don't exist yet
|
|
||||||
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
|
|
||||||
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
|
|
||||||
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
|
|
||||||
GwApi::BoatValue *awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName());
|
|
||||||
GwApi::BoatValue *awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName());
|
|
||||||
|
|
||||||
if (!awdBVal->valid) { // AWD usually does not exist
|
|
||||||
awdBVal->setFormat(hstryBufList.awdHstry->getFormat());
|
|
||||||
awdBVal->value = DBL_MIN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList, bool useSimuData) {
|
|
||||||
// Handle history buffers for TWD, TWS
|
|
||||||
|
|
||||||
GwLog *logger = api->getLogger();
|
|
||||||
|
|
||||||
int16_t twdHstryMin = hstryBufList.twdHstry->getMinVal();
|
|
||||||
int16_t twdHstryMax = hstryBufList.twdHstry->getMaxVal();
|
|
||||||
int16_t twsHstryMin = hstryBufList.twsHstry->getMinVal();
|
|
||||||
int16_t twsHstryMax = hstryBufList.twsHstry->getMaxVal();
|
|
||||||
int16_t awdHstryMin = hstryBufList.awdHstry->getMinVal();
|
|
||||||
int16_t awdHstryMax = hstryBufList.awdHstry->getMaxVal();
|
|
||||||
int16_t awsHstryMin = hstryBufList.awsHstry->getMinVal();
|
|
||||||
int16_t awsHstryMax = hstryBufList.awsHstry->getMaxVal();
|
|
||||||
static int16_t twd, tws = 20; //initial value only relevant if we use simulation data
|
|
||||||
static double awd, aws, hdt = 20; //initial value only relevant if we use simulation data
|
|
||||||
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
|
|
||||||
|
|
||||||
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
|
|
||||||
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
|
|
||||||
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
|
|
||||||
GwApi::BoatValue *awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName());
|
|
||||||
GwApi::BoatValue *awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName());
|
|
||||||
GwApi::BoatValue *awaBVal = boatValues->findValueOrCreate("AWA");
|
|
||||||
GwApi::BoatValue *hdtBVal = boatValues->findValueOrCreate("HDT");
|
|
||||||
GwApi::BoatValue *hdmBVal = boatValues->findValueOrCreate("HDM");
|
|
||||||
GwApi::BoatValue *varBVal = boatValues->findValueOrCreate("VAR");
|
|
||||||
GwApi::BoatValue *cogBVal = boatValues->findValueOrCreate("COG");
|
|
||||||
GwApi::BoatValue *sogBVal = boatValues->findValueOrCreate("SOG");
|
|
||||||
|
|
||||||
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task handleHstryBuf: TWD_isValid? %d, twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f", twdBVal->valid, twdBVal->value * RAD_TO_DEG,
|
|
||||||
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
|
|
||||||
|
|
||||||
if (twdBVal->valid) {
|
|
||||||
calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values
|
|
||||||
calBVal->setFormat(twdBVal->getFormat());
|
|
||||||
calBVal->value = twdBVal->value;
|
|
||||||
calBVal->valid = twdBVal->valid;
|
|
||||||
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
|
||||||
twd = static_cast<int16_t>(std::round(calBVal->value * 1000));
|
|
||||||
if (twd >= twdHstryMin && twd <= twdHstryMax) {
|
|
||||||
hstryBufList.twdHstry->add(twd);
|
|
||||||
}
|
|
||||||
delete calBVal;
|
|
||||||
calBVal = nullptr;
|
|
||||||
} else if (useSimuData) {
|
|
||||||
twd += random(-20, 20);
|
|
||||||
twd = WindUtils::to360(twd);
|
|
||||||
hstryBufList.twdHstry->add(static_cast<int16_t>(DegToRad(twd) * 1000.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (twsBVal->valid) {
|
|
||||||
calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values
|
|
||||||
calBVal->setFormat(twsBVal->getFormat());
|
|
||||||
calBVal->value = twsBVal->value;
|
|
||||||
calBVal->valid = twsBVal->valid;
|
|
||||||
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
|
||||||
tws = static_cast<int16_t>(std::round(calBVal->value * 10));
|
|
||||||
if (tws >= twsHstryMin && tws <= twsHstryMax) {
|
|
||||||
hstryBufList.twsHstry->add(tws);
|
|
||||||
}
|
|
||||||
delete calBVal;
|
|
||||||
calBVal = nullptr;
|
|
||||||
} else if (useSimuData) {
|
|
||||||
tws += random(-50, 50); // TWS value in m/s; expands to 1 decimal
|
|
||||||
tws = constrain(tws, 0, 250); // Limit TWS to [0..25] m/s
|
|
||||||
hstryBufList.twsHstry->add(tws);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (awaBVal->valid) {
|
|
||||||
if (hdtBVal->valid) {
|
|
||||||
hdt = hdtBVal->value; // Use HDT if available
|
|
||||||
} else {
|
|
||||||
hdt = WindUtils::calcHDT(&hdmBVal->value, &varBVal->value, &cogBVal->value, &sogBVal->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
awd = awaBVal->value + hdt;
|
|
||||||
awd = WindUtils::to2PI(awd);
|
|
||||||
calBVal = new GwApi::BoatValue("AWD"); // temporary solution for calibration of history buffer values
|
|
||||||
calBVal->value = awd;
|
|
||||||
calBVal->setFormat(awdBVal->getFormat());
|
|
||||||
calBVal->valid = true;
|
|
||||||
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
|
||||||
awdBVal->value = calBVal->value;
|
|
||||||
awdBVal->valid = true;
|
|
||||||
awd = std::round(calBVal->value * 1000);
|
|
||||||
if (awd >= awdHstryMin && awd <= awdHstryMax) {
|
|
||||||
hstryBufList.awdHstry->add(static_cast<int16_t>(awd));
|
|
||||||
}
|
|
||||||
delete calBVal;
|
|
||||||
calBVal = nullptr;
|
|
||||||
} else if (useSimuData) {
|
|
||||||
awd += random(-20, 20);
|
|
||||||
awd = WindUtils::to360(awd);
|
|
||||||
hstryBufList.awdHstry->add(static_cast<int16_t>(DegToRad(awd) * 1000.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (awsBVal->valid) {
|
|
||||||
calBVal = new GwApi::BoatValue("AWS"); // temporary solution for calibration of history buffer values
|
|
||||||
calBVal->setFormat(awsBVal->getFormat());
|
|
||||||
calBVal->value = awsBVal->value;
|
|
||||||
calBVal->valid = awsBVal->valid;
|
|
||||||
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
|
||||||
aws = std::round(calBVal->value * 10);
|
|
||||||
if (aws >= awsHstryMin && aws <= awsHstryMax) {
|
|
||||||
hstryBufList.awsHstry->add(static_cast<int16_t>(aws));
|
|
||||||
}
|
|
||||||
delete calBVal;
|
|
||||||
calBVal = nullptr;
|
|
||||||
} else if (useSimuData) {
|
|
||||||
aws += random(-50, 50); // TWS value in m/s; expands to 1 decimal
|
|
||||||
aws = constrain(aws, 0, 250); // Limit TWS to [0..25] m/s
|
|
||||||
hstryBufList.awsHstry->add(aws);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OBP60 Task
|
// OBP60 Task
|
||||||
//####################################################################################
|
//####################################################################################
|
||||||
void OBP60Task(GwApi *api){
|
void OBP60Task(GwApi *api){
|
||||||
|
@ -635,16 +431,11 @@ void OBP60Task(GwApi *api){
|
||||||
int lastPage=pageNumber;
|
int lastPage=pageNumber;
|
||||||
|
|
||||||
BoatValueList boatValues; //all the boat values for the api query
|
BoatValueList boatValues; //all the boat values for the api query
|
||||||
|
HstryBuf hstryBufList(960); // Create ring buffers for history storage of some boat data
|
||||||
|
WindUtils trueWind(&boatValues); // Create helper object for true wind calculation
|
||||||
//commonData.distanceformat=config->getString(xxx);
|
//commonData.distanceformat=config->getString(xxx);
|
||||||
//add all necessary data to common data
|
//add all necessary data to common data
|
||||||
|
|
||||||
// Create ring buffers for history storage of some boat data
|
|
||||||
RingBuffer<int16_t> twdHstry(960); // Circular buffer to store true wind direction values; store 960 TWD values for 16 minutes history
|
|
||||||
RingBuffer<int16_t> twsHstry(960); // Circular buffer to store true wind speed values (TWS)
|
|
||||||
RingBuffer<int16_t> awdHstry(960); // Circular buffer to store appearant wind direction values; store 960 AWD values for 16 minutes history
|
|
||||||
RingBuffer<int16_t> awsHstry(960); // Circular buffer to store appearant xwind speed values (AWS)
|
|
||||||
tBoatHstryData hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry};
|
|
||||||
|
|
||||||
//fill the page data from config
|
//fill the page data from config
|
||||||
numPages=config->getInt(config->visiblePages,1);
|
numPages=config->getInt(config->visiblePages,1);
|
||||||
if (numPages < 1) numPages=1;
|
if (numPages < 1) numPages=1;
|
||||||
|
@ -684,10 +475,8 @@ void OBP60Task(GwApi *api){
|
||||||
LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
|
LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
|
||||||
pages[i].parameters.values.push_back(value);
|
pages[i].parameters.values.push_back(value);
|
||||||
}
|
}
|
||||||
if (pages[i].description->pageName == "WindPlot") {
|
// Add boat history data to page parameters
|
||||||
// Add boat history data to page parameters
|
pages[i].parameters.boatHstry = &hstryBufList;
|
||||||
pages[i].parameters.boatHstry = hstryBufList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// add out of band system page (always available)
|
// add out of band system page (always available)
|
||||||
Page *syspage = allPages.pages[0]->creator(commonData);
|
Page *syspage = allPages.pages[0]->creator(commonData);
|
||||||
|
@ -695,12 +484,12 @@ void OBP60Task(GwApi *api){
|
||||||
// Read all calibration data settings from config
|
// Read all calibration data settings from config
|
||||||
calibrationData.readConfig(config, logger);
|
calibrationData.readConfig(config, logger);
|
||||||
|
|
||||||
// Check user setting for true wind calculation
|
// Check user settings for true wind calculation
|
||||||
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
|
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
|
||||||
bool useSimuData = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
bool useSimuData = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
||||||
|
|
||||||
// Initialize history buffer for certain boat data
|
// Initialize history buffer for certain boat data
|
||||||
initHstryBuf(api, &boatValues, hstryBufList);
|
hstryBufList.init(&boatValues, logger);
|
||||||
|
|
||||||
// Display screenshot handler for HTTP request
|
// Display screenshot handler for HTTP request
|
||||||
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||||
|
@ -1015,10 +804,10 @@ void OBP60Task(GwApi *api){
|
||||||
api->getStatus(commonData.status);
|
api->getStatus(commonData.status);
|
||||||
|
|
||||||
if (calcTrueWnds) {
|
if (calcTrueWnds) {
|
||||||
addTrueWind(api, &boatValues);
|
trueWind.addTrueWind(api, &boatValues, logger);
|
||||||
}
|
}
|
||||||
// Handle history buffers for TWD, TWS for wind plot page and other usage
|
// Handle history buffers for TWD, TWS for wind plot page and other usage
|
||||||
handleHstryBuf(api, &boatValues, hstryBufList, useSimuData);
|
hstryBufList.handleHstryBuf(useSimuData);
|
||||||
|
|
||||||
// Clear display
|
// Clear display
|
||||||
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
||||||
|
|
|
@ -47,4 +47,18 @@
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp40-v1-docu.readthedocs.io/en/latest/"); // Link to help pages
|
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp40-v1-docu.readthedocs.io/en/latest/"); // Link to help pages
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class BoatValueList{
|
||||||
|
public:
|
||||||
|
static const int MAXVALUES=100;
|
||||||
|
//we create a list containing all our BoatValues
|
||||||
|
//this is the list we later use to let the api fill all the values
|
||||||
|
//additionally we put the necessary values into the paga data - see below
|
||||||
|
GwApi::BoatValue *allBoatValues[MAXVALUES];
|
||||||
|
int numValues=0;
|
||||||
|
|
||||||
|
bool addValueToList(GwApi::BoatValue *v);
|
||||||
|
//helper to ensure that each BoatValue is only queried once
|
||||||
|
GwApi::BoatValue *findValueOrCreate(String name);
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue