1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2026-08-18 18:32:30 +02:00

Merge pull request #234 from Scorgan01/TrueWind-opt

Fixes and new 'smoothing' feature for data charts
This commit is contained in:
Norbert Walter
2026-03-27 17:52:49 +01:00
committed by GitHub
18 changed files with 311 additions and 232 deletions
+109 -108
View File
@@ -1,5 +1,4 @@
#include "OBPDataOperations.h" #include "OBPDataOperations.h"
//#include "BoatDataCalibration.h" // Functions lib for data instance calibration
// --- Class CalibrationData --------------- // --- Class CalibrationData ---------------
CalibrationData::CalibrationData(GwLog* log) CalibrationData::CalibrationData(GwLog* log)
@@ -155,8 +154,7 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue)
if (format == "formatWind") { // instance is of type angle if (format == "formatWind") { // instance is of type angle
dataValue = (dataValue * slope) + offset; dataValue = (dataValue * slope) + offset;
// dataValue = WindUtils::toPI(dataValue); dataValue = WindUtils::to2PI(dataValue); // wind angle data is stored in [0..2PI] format
dataValue = WindUtils::to2PI(dataValue); // we should call <toPI> for format of [-180..180], but pages cannot display negative values properly yet
} else if (format == "formatCourse") { // instance is of type direction } else if (format == "formatCourse") { // instance is of type direction
dataValue = (dataValue * slope) + offset; dataValue = (dataValue * slope) + offset;
@@ -169,7 +167,6 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue)
dataValue = (dataValue * slope) + offset; dataValue = (dataValue * slope) + offset;
} }
boatDataValue->value = dataValue; // update boat data value with calibrated value boatDataValue->value = dataValue; // update boat data value with calibrated value
calibrationMap[instance].value = dataValue; // store the calibrated value in the list calibrationMap[instance].value = dataValue; // store the calibrated value in the list
calibrationMap[instance].isCalibrated = true; calibrationMap[instance].isCalibrated = true;
@@ -247,23 +244,15 @@ void HstryBuf::add(double value)
void HstryBuf::handle(bool useSimuData, CommonData& common) void HstryBuf::handle(bool useSimuData, CommonData& common)
{ {
// GwApi::BoatValue* tmpBVal;
std::unique_ptr<GwApi::BoatValue> tmpBVal; // Temp variable to get formatted and converted data value from OBP60Formatter std::unique_ptr<GwApi::BoatValue> tmpBVal; // Temp variable to get formatted and converted data value from OBP60Formatter
// create temporary boat value for calibration purposes and retrieval of simulation value
// tmpBVal = new GwApi::BoatValue(boatDataName.c_str());
tmpBVal = std::unique_ptr<GwApi::BoatValue>(new GwApi::BoatValue(boatDataName));
tmpBVal->setFormat(boatValue->getFormat());
tmpBVal->value = boatValue->value;
tmpBVal->valid = boatValue->valid;
if (boatValue->valid) { if (boatValue->valid) {
// Calibrate boat value before adding it to history buffer
//calibrationData.calibrateInstance(tmpBVal.get(), logger);
//add(tmpBVal->value);
add(boatValue->value); add(boatValue->value);
} else if (useSimuData) { // add simulated value to history buffer } else if (useSimuData) { // add simulated value to history buffer
tmpBVal = std::unique_ptr<GwApi::BoatValue>(new GwApi::BoatValue(boatDataName)); // create temporary boat value for retrieval of simulation value
tmpBVal->setFormat(boatValue->getFormat());
tmpBVal->value = boatValue->value;
tmpBVal->valid = boatValue->valid;
double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>; here: retreive SI value double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>; here: retreive SI value
add(simSIValue); add(simSIValue);
} else { } else {
@@ -276,20 +265,7 @@ void HstryBuf::handle(bool useSimuData, CommonData& common)
HstryBuffers::HstryBuffers(int size, BoatValueList* boatValues, GwLog* log) HstryBuffers::HstryBuffers(int size, BoatValueList* boatValues, GwLog* log)
: size(size) : size(size)
, boatValueList(boatValues) , boatValueList(boatValues)
, logger(log) , logger(log) { };
{
// collect boat values for true wind calculation
// should all have been already created at true wind object initialization
// potentially to be moved to history buffer handling
awaBVal = boatValueList->findValueOrCreate("AWA");
hdtBVal = boatValueList->findValueOrCreate("HDT");
hdmBVal = boatValueList->findValueOrCreate("HDM");
varBVal = boatValueList->findValueOrCreate("VAR");
cogBVal = boatValueList->findValueOrCreate("COG");
sogBVal = boatValueList->findValueOrCreate("SOG");
awdBVal = boatValueList->findValueOrCreate("AWD");
}
// Create history buffer for boat data type // Create history buffer for boat data type
void HstryBuffers::addBuffer(const String& name) void HstryBuffers::addBuffer(const String& name)
@@ -301,8 +277,6 @@ void HstryBuffers::addBuffer(const String& name)
return; return;
} }
hstryBuffers[name] = std::unique_ptr<HstryBuf>(new HstryBuf(name, size, boatValueList, logger));
// Initialize metadata for buffer // Initialize metadata for buffer
String valueFormat = bufferParams[name].format; // Data format of boat data type String valueFormat = bufferParams[name].format; // Data format of boat data type
// String valueFormat = boatValueList->findValueOrCreate(name)->getFormat().c_str(); // Unfortunately, format is not yet available during system initialization // String valueFormat = boatValueList->findValueOrCreate(name)->getFormat().c_str(); // Unfortunately, format is not yet available during system initialization
@@ -311,6 +285,7 @@ void HstryBuffers::addBuffer(const String& name)
double bufferMinVal = bufferParams[name].bufferMinVal; // Min value for this history buffer double bufferMinVal = bufferParams[name].bufferMinVal; // Min value for this history buffer
double bufferMaxVal = bufferParams[name].bufferMaxVal; // Max value for this history buffer double bufferMaxVal = bufferParams[name].bufferMaxVal; // Max value for this history buffer
hstryBuffers[name] = std::unique_ptr<HstryBuf>(new HstryBuf(name, size, boatValueList, logger));
hstryBuffers[name]->init(valueFormat, hstryUpdFreq, mltplr, bufferMinVal, bufferMaxVal); hstryBuffers[name]->init(valueFormat, hstryUpdFreq, mltplr, bufferMinVal, bufferMaxVal);
LOG_DEBUG(GwLog::DEBUG, "HstryBuffers: new buffer added: name: %s, format: %s, multiplier: %d, min value: %.2f, max value: %.2f", name, valueFormat, mltplr, bufferMinVal, bufferMaxVal); LOG_DEBUG(GwLog::DEBUG, "HstryBuffers: new buffer added: name: %s, format: %s, multiplier: %d, min value: %.2f, max value: %.2f", name, valueFormat, mltplr, bufferMinVal, bufferMaxVal);
} }
@@ -329,8 +304,9 @@ RingBuffer<uint16_t>* HstryBuffers::getBuffer(const String& name)
auto it = hstryBuffers.find(name); auto it = hstryBuffers.find(name);
if (it != hstryBuffers.end()) { if (it != hstryBuffers.end()) {
return &it->second->hstryBuf; return &it->second->hstryBuf;
} else {
return nullptr;
} }
return nullptr;
} }
// --- End Class HstryBuffers --------------- // --- End Class HstryBuffers ---------------
@@ -396,56 +372,75 @@ void WindUtils::addPolar(const double* phi1, const double* r1,
toPol(&x1, &y1, phi, r); toPol(&x1, &y1, phi, r);
} }
void WindUtils::calcTwdSA(const double* AWA, const double* AWS, void WindUtils::calcTwdSA(const double* AWA, const double* AWS, const double* AWD, const double* CTW, const double* STW, const double* HDT,
const double* CTW, const double* STW, const double* HDT, double* TWA, double* TWS, double* TWD)
double* TWD, double* TWS, double* TWA, double* AWD)
{ {
*AWD = *AWA + *HDT;
*AWD = to2PI(*AWD);
double stw = -*STW; double stw = -*STW;
addPolar(AWD, AWS, CTW, &stw, TWD, TWS); addPolar(AWD, AWS, CTW, &stw, TWD, TWS);
// Normalize TWD to [0..360°] (2PI) and TWA to [-180..180] (PI) // Normalize to [0..2PI]
*TWD = to2PI(*TWD); *TWD = to2PI(*TWD);
*TWA = toPI(*TWD - *HDT); *TWA = to2PI(*TWD - *HDT); // for internal storage we normalize wind angle to [0..2PI], as well
} }
double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal) // calculate HDT from either HDM + VAR, HDM only, or COG
bool WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal, double* hdtVal)
{ {
double hdt;
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
if (*hdmVal != DBL_MAX) {
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 = to2PI(hdt);
} 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
} else {
hdt = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
}
return hdt;
}
bool WindUtils::calcWinds(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal, double* awdVal)
{
double stw, hdt, ctw;
double twd, tws, twa, awd;
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
if (*hdtVal != DBL_MAX) { if (*hdtVal != DBL_MAX) {
hdt = *hdtVal; // Use HDT if available // HDT already available-> nothing to do
return true;
}
if (*hdmVal != DBL_MAX) {
*hdtVal = *hdmVal + (*varVal != DBL_MAX ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
*hdtVal = to2PI(*hdtVal);
} else if (*cogVal != DBL_MAX && *sogVal >= minSogVal) {
*hdtVal = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
} else { } else {
hdt = calcHDT(hdmVal, varVal, cogVal, sogVal); *hdtVal = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
return false;
}
// LOG_DEBUG(GwLog::DEBUG, "WindUtils:calcHDT: HDT: %.1f, HDM %.1f, VAR %.1f, COG %.1f, SOG %.1f", *hdtVal * RAD_TO_DEG, *hdmVal * RAD_TO_DEG, *varVal * RAD_TO_DEG,
// *cogVal * RAD_TO_DEG, *sogVal * 3.6 / 1.852);
return true;
}
// calculate wind direction (either TWD or AWD) from wind angle (TWA or AWA) and HDT
bool WindUtils::calcWD(const double* waVal, const double* hdtVal, double* wdVal)
{
if (*waVal != DBL_MAX && *hdtVal != DBL_MAX) {
*wdVal = *waVal + *hdtVal;
*wdVal = to2PI(*wdVal);
} else {
*wdVal = DBL_MAX; // Cannot calculate wind direction WD without valid wind angle WA and HDT
return false;
}
return true;
}
// calculate full set of true winds (TWA, TWS, TWD) apparent winds and more data
bool WindUtils::calcTrueWinds(const double* awaVal, const double* awsVal, const double* awd,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
double* twaVal, double* twsVal, double* twdVal)
{
double stw, ctw;
double twd, tws, twa;
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
// Cannot calculate true winds at all without valid AWA, AWS
return false;
} }
if (*cogVal != DBL_MAX && *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 = *hdtVal; // 2nd approximation for CTW; HDT must exist if we reach this part of code
} }
if (*stwVal != DBL_MAX) { if (*stwVal != DBL_MAX) {
@@ -456,28 +451,21 @@ bool WindUtils::calcWinds(const double* awaVal, const double* awsVal,
// If STW and SOG are not available, we cannot calculate true wind // If STW and SOG are not available, we cannot calculate true wind
return false; return false;
} }
// LOG_DEBUG(GwLog::DEBUG, "WindUtils:calcWinds: HDT: %.1f, CTW %.1f, STW %.1f", hdt, ctw, stw); // LOG_DEBUG(GwLog::DEBUG, "WindUtils:calcTrueWinds: HDT: %.1f, CTW %.1f, STW %.1f", *hdtVal * RAD_TO_DEG, ctw * RAD_TO_DEG, stw * 3.6 / 1.852);
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) { calcTwdSA(awaVal, awsVal, awd, &ctw, &stw, hdtVal, &twa, &tws, &twd);
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier *twaVal = twa;
return false; *twsVal = tws;
} else { *twdVal = twd;
calcTwdSA(awaVal, awsVal, &ctw, &stw, &hdt, &twd, &tws, &twa, &awd);
*twdVal = twd;
*twsVal = tws;
*twaVal = twa;
*awdVal = awd;
return true; return true;
}
} }
// Calculate true wind data and add to obp60task boat data list // Calculate true wind data and add to obp60task boat data list
bool WindUtils::addWinds() bool WindUtils::handleWinds(bool calcWinds)
{ {
double twd, tws, twa, awd, hdt; double twd, tws, twa, awd;
bool twCalculated = false; bool twCalculated = false;
bool awdCalculated = false;
double awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX; double awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
double awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX; double awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
@@ -487,48 +475,61 @@ bool WindUtils::addWinds()
double hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX; double hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
double hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX; double hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
double varVal = varBVal->valid ? varBVal->value : DBL_MAX; double varVal = varBVal->valid ? varBVal->value : DBL_MAX;
//LOG_DEBUG(GwLog::DEBUG, "WindUtils:addWinds: 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, double twaVal = twaBVal->valid ? twaBVal->value : DBL_MAX;
// 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); double twsVal = twsBVal->valid ? twsBVal->value : DBL_MAX;
// LOG_DEBUG(GwLog::DEBUG, "WindUtils:handleWinds: AWA %.1f, AWS %.1f, AWD %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaVal * RAD_TO_DEG, awsVal * 3.6 / 1.852,
// awd * RAD_TO_DEG, cogVal * RAD_TO_DEG, stwVal * 3.6 / 1.852, sogVal * 3.6 / 1.852, hdtVal * RAD_TO_DEG, hdmVal * RAD_TO_DEG, varVal * RAD_TO_DEG);
// Check if TWD can be calculated from TWA and HDT/HDM if (calcHDT(&hdmVal, &varVal, &cogVal, &sogVal, &hdtVal)) {
if (twaBVal->valid) { hdtBVal->value = hdtVal;
if (!twdBVal->valid) { hdtBVal->valid = true;
if (hdtVal != DBL_MAX) { } else {
hdt = hdtVal; // Use HDT if available // determination of HDT was not possible due to missing prerequisite data
} else { hdtBVal->valid = false;
hdt = calcHDT(&hdmVal, &varVal, &cogVal, &sogVal); return false;
} }
twd = twaBVal->value + hdt;
twd = to2PI(twd); // calculate AWD if not existing and if possible
if (!awdBVal->valid) {
if (calcWD(&awaVal, &hdtVal, &awd)) {
awdBVal->value = awd;
awdBVal->valid = true;
} else {
awdBVal->valid = false;
return false;
}
}
// calculate TWD if not existing and if possible
if (!twdBVal->valid) {
// calculate TWD if it does not exist yet and TWA is available
if (calcWD(&twaVal, &hdtVal, &twd)) {
twdBVal->value = twd; twdBVal->value = twd;
twdBVal->valid = true; twdBVal->valid = true;
} else {
twdBVal->valid = false;
} }
}
} else { if (calcWinds && (!twaBVal->valid || !twsBVal->valid || !twdBVal->valid)) {
// Calculate true winds and AWD; if true winds exist, use at least AWD calculation // calculate true winds if user setting and at least one of three true wind values does not exist
twCalculated = calcWinds(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa, &awd); twCalculated = calcTrueWinds(&awaVal, &awsVal, &awd, &cogVal, &stwVal, &sogVal, &hdtVal, &twa, &tws, &twd);
if (twCalculated) { // Replace values only, if successfully calculated and not already available if (twCalculated) { // Replace values only, if successfully calculated and not already available
if (!twdBVal->valid) { if (!twaBVal->valid) {
twdBVal->value = twd; twaBVal->value = twa;
twdBVal->valid = true; twaBVal->valid = true;
} }
if (!twsBVal->valid) { if (!twsBVal->valid) {
twsBVal->value = tws; twsBVal->value = tws;
twsBVal->valid = true; twsBVal->valid = true;
} }
if (!twaBVal->valid) { if (!twdBVal->valid) {
//twaBVal->value = twa; twdBVal->value = twd;
twaBVal->value = to2PI(twa); // convert to [0..360], because pages cannot display negative values properly yet twdBVal->valid = true;
twaBVal->valid = true;
}
if (!awdBVal->valid) {
awdBVal->value = awd;
awdBVal->valid = true;
} }
} }
} }
// LOG_DEBUG(GwLog::DEBUG, "WindUtils:addWinds: twCalculated %d, TWD %.1f, TWA %.1f, TWS %.2f kn, AWD: %.1f", twCalculated, twdBVal->value * RAD_TO_DEG, // LOG_DEBUG(GwLog::DEBUG, "WindUtils:handleWinds: twCalculated %d, TWD %.1f, TWA %.1f, TWS %.2f kn, AWD: %.1f", twCalculated, twdBVal->value * RAD_TO_DEG,
// twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852, awdBVal->value * RAD_TO_DEG); // twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852, awdBVal->value * RAD_TO_DEG);
return twCalculated; return twCalculated;
+12 -10
View File
@@ -19,7 +19,7 @@ private:
} tCalibrationData; } tCalibrationData;
std::unordered_map<std::string, tCalibrationData> calibrationMap; // list of calibration data instances std::unordered_map<std::string, tCalibrationData> calibrationMap; // list of calibration data instances
std::unordered_map<std::string, double> lastValue; // array for last smoothed values of boat data values std::unordered_map<std::string, double> lastValue; // array for last smoothed value of boat data values
GwLog* logger; GwLog* logger;
static constexpr int8_t MAX_CALIBRATION_DATA = 4; // maximum number of calibration data instances static constexpr int8_t MAX_CALIBRATION_DATA = 4; // maximum number of calibration data instances
@@ -32,6 +32,7 @@ public:
bool smoothInstance(GwApi::BoatValue* boatDataValue); // Smooth single boat data value bool smoothInstance(GwApi::BoatValue* boatDataValue); // Smooth single boat data value
}; };
// Class for a single history buffer of boat values
class HstryBuf { class HstryBuf {
private: private:
RingBuffer<uint16_t> hstryBuf; // Circular buffer to store history values RingBuffer<uint16_t> hstryBuf; // Circular buffer to store history values
@@ -50,13 +51,13 @@ public:
void handle(bool useSimuData, CommonData& common); void handle(bool useSimuData, CommonData& common);
}; };
// Manage list of history buffers for supported boat data; used by boat data charts
class HstryBuffers { class HstryBuffers {
private: private:
std::map<String, std::unique_ptr<HstryBuf>> hstryBuffers; std::map<String, std::unique_ptr<HstryBuf>> hstryBuffers;
int size; // size of all history buffers int size; // size of all history buffers
BoatValueList* boatValueList; BoatValueList* boatValueList;
GwLog* logger; GwLog* logger;
GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal, *awdBVal; // boat values for true wind calculation
struct HistoryParams { struct HistoryParams {
int hstryUpdFreq; // update frequency of history buffer (documentation only) int hstryUpdFreq; // update frequency of history buffer (documentation only)
@@ -97,7 +98,8 @@ public:
class WindUtils { class WindUtils {
private: private:
GwApi::BoatValue *twaBVal, *twsBVal, *twdBVal; GwApi::BoatValue *twaBVal, *twsBVal, *twdBVal;
GwApi::BoatValue *awaBVal, *awsBVal, *awdBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal; GwApi::BoatValue *awaBVal, *awsBVal, *awdBVal;
GwApi::BoatValue *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
static constexpr double DBL_MAX = std::numeric_limits<double>::max(); static constexpr double DBL_MAX = std::numeric_limits<double>::max();
GwLog* logger; GwLog* logger;
@@ -128,12 +130,12 @@ public:
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);
void calcTwdSA(const double* AWA, const double* AWS, void calcTwdSA(const double* AWA, const double* AWS, const double* AWD, const double* CTW, const double* STW, const double* HDT,
const double* CTW, const double* STW, const double* HDT, double* TWA, double* TWS, double* TWD);
double* TWD, double* TWS, double* TWA, double* AWD); bool calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal, double* hdtVal);
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal); bool calcWD(const double* waVal, const double* hdtVal, double* wdVal);
bool calcWinds(const double* awaVal, const double* awsVal, bool calcTrueWinds(const double* awaVal, const double* awsVal, const double* awd,
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, double* awdVal); double* twdVal, double* twsVal, double* twaVal);
bool addWinds(); bool handleWinds(bool calcWinds);
}; };
+2 -2
View File
@@ -66,8 +66,8 @@ void sensorTask(void *param){
const int avgsize = 300; const int avgsize = 300;
constexpr int arrayBatV{avgsize}; constexpr int arrayBatV{avgsize};
constexpr int arrayBatC{avgsize}; constexpr int arrayBatC{avgsize};
movingAvg batV(arrayBatV); movingAvg<int> batV(arrayBatV);
movingAvg batC(arrayBatC); movingAvg<int> batC(arrayBatC);
batV.begin(); batV.begin();
batC.begin(); batC.begin();
+49 -21
View File
@@ -6,9 +6,9 @@
std::map<String, ChartProps> Chart::dfltChrtDta = { std::map<String, ChartProps> Chart::dfltChrtDta = {
{ "formatWind", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees { "formatWind", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees
{ "formatCourse", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees { "formatCourse", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees
{ "formatKnots", { 7.71, 2.56 } }, // default speed range in m/s { "formatKnots", { 2.57, 2.57 } }, // default speed range in m/s
{ "formatDepth", { 15.0, 5.0 } }, // default depth range in m { "formatDepth", { 10.0, 5.0 } }, // default depth range in m
{ "kelvinToC", { 30.0, 5.0 } } // default temp range in °C/K { "kelvinToC", { 20.0, 5.0 } } // default temp range in °C/K
}; };
// --- Class Chart --------------- // --- Class Chart ---------------
@@ -42,6 +42,11 @@ Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common,
dbMAX_VAL = dataBuf.getMaxVal(); dbMAX_VAL = dataBuf.getMaxVal();
bufSize = dataBuf.getCapacity(); bufSize = dataBuf.getCapacity();
smoothCharts = common.config->getBool(common.config->smoothCharts);
if (smoothCharts) {
chrtAvg.begin();
}
// Initialize chart data format; shorter version of standard format indicator // Initialize chart data format; shorter version of standard format indicator
if (dbFormat == "formatCourse" || dbFormat == "formatWind" || dbFormat == "formatRot") { if (dbFormat == "formatCourse" || dbFormat == "formatWind" || dbFormat == "formatRot") {
chrtDataFmt = WIND; // Chart is showing data of course / wind <degree> format chrtDataFmt = WIND; // Chart is showing data of course / wind <degree> format
@@ -337,6 +342,23 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do
double chrtVal; // Current data value double chrtVal; // Current data value
Pos point, prevPoint; // current and previous chart point Pos point, prevPoint; // current and previous chart point
if (smoothCharts) {
// prime moving average filter to ensure the first plotted point is already averaged
chrtAvg.reset();
// Feed the filter the 10 values preceding bufStart
for (int p = 10; p > 0; p--) {
// Calculate index with wrapping: (start - offset + size) % size
int primeIdx = (bufStart - (p * chrtIntv));
double primeVal = dataBuf.get(primeIdx);
if (primeVal != dbMAX_VAL) {
chrtAvg.reading(primeVal);
}
}
}
for (int i = 0; i < (numBufVals / chrtIntv); i++) { for (int i = 0; i < (numBufVals / chrtIntv); i++) {
chrtVal = dataBuf.get(bufStart + (i * chrtIntv)); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer chrtVal = dataBuf.get(bufStart + (i * chrtIntv)); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer
@@ -345,6 +367,11 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do
chrtPrevVal = dbMAX_VAL; chrtPrevVal = dbMAX_VAL;
} else { } else {
if (smoothCharts) {
// if chart lines shall be smoothed, apply moving average filter of the last 10 values
chrtVal = chrtAvg.reading(chrtVal);
}
point = setCurrentChartPoint(i, direction, chrtVal, chrtScale); point = setCurrentChartPoint(i, direction, chrtVal, chrtScale);
// if (i >= (numBufVals / chrtIntv) - 5) // log chart data of 1 line (adjust for test purposes) // if (i >= (numBufVals / chrtIntv) - 5) // log chart data of 1 line (adjust for test purposes)
@@ -399,7 +426,7 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do
if (chrtDataFmt == WIND) { // degree of course or wind if (chrtDataFmt == WIND) { // degree of course or wind
recalcRngMid = true; recalcRngMid = true;
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: chart end: timAxis: %d, i: %d, bufStart: %d, numBufVals: %d, recalcRngCntr: %d", timAxis, i, bufStart, numBufVals, recalcRngMid); // LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: chart end: timAxis: %d, i: %d, bufStart: %d, numBufVals: %d, recalcRngCntr: %d", timAxis, i, bufStart, numBufVals, recalcRngMid);
} }
break; break;
} }
@@ -440,46 +467,47 @@ Pos Chart::setCurrentChartPoint(const int i, const char direction, const double
// chart time axis label + lines // chart time axis label + lines
void Chart::drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8_t chrtIntv) void Chart::drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8_t chrtIntv)
{ {
float axSlots, intv, i; int axSlots, intv, i, timeRng;
char sTime[6]; char sTime[6];
int timeRng = chrtIntv * 4; // chart time interval: [1] 4 min., [2] 8 min., [3] 12 min., [4] 16 min., [8] 32 min. int tOffset;
getdisplay().setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setTextColor(fgColor); getdisplay().setTextColor(fgColor);
axSlots = 5; // number of axis labels intv = 60; // print axis label each 60 pixels = seconds
intv = timAxis / (axSlots - 1); // minutes per chart axis interval (interval is 1 less than axSlots) axSlots = timAxis / intv; // number of axis labels; value will be truncated to full number
i = timeRng; // Chart axis label start at -32, -16, -12, ... minutes i = axSlots * chrtIntv * -1; // current minute label
if (chrtDir == HORIZONTAL) { if (chrtDir == HORIZONTAL) {
getdisplay().fillRect(0, cRoot.y, dWidth, 2, fgColor); getdisplay().fillRect(0, cRoot.y, dWidth, 2, fgColor);
for (float j = 0; j < timAxis - 1; j += intv) { // fill time axis with values but keep area free on right hand side for value label // LOG_DEBUG(GwLog::DEBUG, "Chart::drawChrtTimeAxis: intv: %d, axSlots: %d, timAxis: %d, chrtIntv: %d", intv, axSlots, timAxis, chrtIntv);
for (int j = intv; j < timAxis - 1; j += intv) { // fill time axis with values but keep area free on right hand side for value label
// draw text with appropriate offset // draw text with appropriate offset
int tOffset = j == 0 ? 13 : -4; tOffset = j == 0 ? 13 : -4;
snprintf(sTime, sizeof(sTime), "-%.0f", i); snprintf(sTime, sizeof(sTime), "%d", i);
drawTextCenter(cRoot.x + j + tOffset, cRoot.y - 8, sTime); drawTextCenter(cRoot.x + j + tOffset, cRoot.y - 8, sTime);
getdisplay().drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + 5, fgColor); // draw short vertical time mark getdisplay().drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + 5, fgColor); // draw short vertical time mark
i -= chrtIntv; i += chrtIntv;
} }
} else { // vertical chart } else { // vertical chart
for (float j = intv; j < timAxis - 1; j += intv) { // don't print time label at upper and lower end of time axis for (float j = intv; j < timAxis - 1; j += intv) { // don't print time label at upper and lower end of time axis
i -= chrtIntv; // we start not at top chart position snprintf(sTime, sizeof(sTime), "%d", i);
snprintf(sTime, sizeof(sTime), "-%.0f", i);
getdisplay().drawLine(cRoot.x, cRoot.y + j, cRoot.x + valAxis, cRoot.y + j, fgColor); // Grid line getdisplay().drawLine(cRoot.x, cRoot.y + j, cRoot.x + valAxis, cRoot.y + j, fgColor); // Grid line
if (chrtSz == FULL_SIZE) { // full size chart if (chrtSz == FULL_SIZE) { // full size chart
getdisplay().fillRect(0, cRoot.y + j - 9, 32, 15, bgColor); // clear small area to remove potential chart lines getdisplay().fillRect(0, cRoot.y + j - 9, 32, 15, bgColor); // clear small area to remove potential chart lines
getdisplay().setCursor((4 - strlen(sTime)) * 7, cRoot.y + j + 3); // time value; print left screen; value right-formated getdisplay().setCursor((4 - strlen(sTime)) * 7, cRoot.y + j + 3); // time value; print left screen; value right-formated
getdisplay().printf("%s", sTime); // Range value getdisplay().printf("%s", sTime); // time value
} else if (chrtSz == HALF_SIZE_RIGHT) { // half size chart; right side } else if (chrtSz == HALF_SIZE_RIGHT) { // half size chart; right side
drawTextCenter(dWidth / 2, cRoot.y + j, sTime); // time value; print mid screen drawTextCenter(dWidth / 2, cRoot.y + j, sTime); // time value; print mid screen
} }
i += chrtIntv;
} }
} }
} }
@@ -497,11 +525,11 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
if (chrtSz == FULL_SIZE) { if (chrtSz == FULL_SIZE) {
// print buffer data name on left hand side of time axis (max. size 5 characters)
font = &Ubuntu_Bold12pt8b; font = &Ubuntu_Bold12pt8b;
// print buffer data name on right hand side of time axis (max. size 5 characters)
getdisplay().setFont(font); getdisplay().setFont(font);
drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5)); getdisplay().fillRect(cRoot.x + timAxis - 57, cRoot.y + 2, 58, 20, bgColor); // clear small area to remove potential chart lines
drawTextRalign(cRoot.x + timAxis, cRoot.y + 19, dbName.substring(0, 5));
if (chrtDataFmt == WIND) { if (chrtDataFmt == WIND) {
prntHorizChartThreeValueAxisLabel(font); prntHorizChartThreeValueAxisLabel(font);
@@ -515,11 +543,11 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
} else { // half size chart -> just print edge values + middle chart line } else { // half size chart -> just print edge values + middle chart line
font = &Ubuntu_Bold10pt8b; font = &Ubuntu_Bold10pt8b;
if (prntName) { if (prntName) {
// print buffer data name on right hand side of time axis (max. size 5 characters) // print buffer data name on right hand side of time axis (max. size 5 characters)
getdisplay().setFont(font); getdisplay().setFont(font);
drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5)); getdisplay().fillRect(cRoot.x + timAxis - 57, cRoot.y + 2, 58, 20, bgColor); // clear small area to remove potential chart lines
drawTextRalign(cRoot.x + timAxis, cRoot.y + 16, dbName.substring(0, 5));
} }
prntHorizChartThreeValueAxisLabel(font); prntHorizChartThreeValueAxisLabel(font);
+3 -2
View File
@@ -2,6 +2,7 @@
#pragma once #pragma once
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "movingAvg.h"
struct Pos { struct Pos {
int x; int x;
@@ -44,12 +45,11 @@ protected:
static constexpr bool NO_SIMUDATA = true; // switch off simulation feature of <formatValue> function static constexpr bool NO_SIMUDATA = true; // switch off simulation feature of <formatValue> function
RingBuffer<uint16_t>& dataBuf; // Buffer to display RingBuffer<uint16_t>& dataBuf; // Buffer to display
//char chrtDir; // Chart timeline direction: 'H' = horizontal, 'V' = vertical
//int8_t chrtSz; // Chart size: [0] = full size, [1] = half size left/top, [2] half size right/bottom
double dfltRng; // Default range of chart, e.g. 30 = [0..30] double dfltRng; // Default range of chart, e.g. 30 = [0..30]
uint16_t fgColor; // color code for any screen writing uint16_t fgColor; // color code for any screen writing
uint16_t bgColor; // color code for screen background uint16_t bgColor; // color code for screen background
bool useSimuData; // flag to indicate if simulation data is active bool useSimuData; // flag to indicate if simulation data is active
bool smoothCharts; // flag to indicate if charts shall be printed with smoothed gradient
String tempFormat; // user defined format for temperature String tempFormat; // user defined format for temperature
double zeroValue; // "0" SI value for temperature double zeroValue; // "0" SI value for temperature
@@ -84,6 +84,7 @@ protected:
bool bufDataValid = false; // Flag to indicate if buffer data is valid bool bufDataValid = false; // Flag to indicate if buffer data is valid
int oldChrtIntv = 0; // remember recent user selection of data interval int oldChrtIntv = 0; // remember recent user selection of data interval
movingAvg<double> chrtAvg{7}; // Store average of the last 7 chart values if chart gradient shall be smoothed
double chrtPrevVal; // Last data value in chart area double chrtPrevVal; // Last data value in chart area
int x, y; // x and y coordinates for drawing int x, y; // x and y coordinates for drawing
int prevX, prevY; // Last x and y coordinates for drawing int prevX, prevY; // Last x and y coordinates for drawing
-1
View File
@@ -2,7 +2,6 @@
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "movingAvg.h" // Lib for moving average building
class PageBattery2 : public Page class PageBattery2 : public Page
{ {
-1
View File
@@ -2,7 +2,6 @@
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "movingAvg.h" // Lib for moving average building
class PageGenerator : public Page class PageGenerator : public Page
{ {
-2
View File
@@ -209,8 +209,6 @@ public:
dataIntv = 3; dataIntv = 3;
} else if (dataIntv == 3) { } else if (dataIntv == 3) {
dataIntv = 4; dataIntv = 4;
} else if (dataIntv == 4) {
dataIntv = 8;
} else { } else {
dataIntv = 1; dataIntv = 1;
} }
-1
View File
@@ -2,7 +2,6 @@
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "movingAvg.h" // Lib for moving average building
class PageSolar : public Page class PageSolar : public Page
{ {
-2
View File
@@ -211,8 +211,6 @@ public:
dataIntv = 3; dataIntv = 3;
} else if (dataIntv == 3) { } else if (dataIntv == 3) {
dataIntv = 4; dataIntv = 4;
} else if (dataIntv == 4) {
dataIntv = 8;
} else { } else {
dataIntv = 1; dataIntv = 1;
} }
-1
View File
@@ -2,7 +2,6 @@
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "movingAvg.h" // Lib for moving average building
class PageVoltage : public Page class PageVoltage : public Page
{ {
+4 -1
View File
@@ -37,7 +37,7 @@ private:
bool oldShowTruW = false; // remember recent user selection of wind data type bool oldShowTruW = false; // remember recent user selection of wind data type
int8_t dataIntv = 1; // Update interval for wind history chart: int8_t dataIntv = 1; // Update interval for wind history chart:
// (1)|(2)|(3)|(4)|(8) x 240 seconds for 4, 8, 12, 16, 32 min. history chart // (1)|(2)|(3)|(4)|(8) seconds for up to 32 min. history chart
bool useSimuData; bool useSimuData;
// bool holdValues; // bool holdValues;
String flashLED; String flashLED;
@@ -231,6 +231,9 @@ public:
} else if (chrtMode == SPEED) { } else if (chrtMode == SPEED) {
if (wsChart) { if (wsChart) {
if (dataIntv == 8) {
dataIntv = 1; // horizontal charts show max. 4 x 7 min. only; no factor 8 multiplier
}
wsChart->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *wsBVal); wsChart->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *wsBVal);
} }
+11
View File
@@ -230,6 +230,17 @@
"obp40": "true" "obp40": "true"
} }
}, },
{
"name": "smoothCharts",
"label": "Smooth chart lines",
"type": "boolean",
"default": "false",
"description": "Print chart lines with a smoothed gradient",
"category": "OBP40 Settings",
"capabilities": {
"obp40": "true"
}
},
{ {
"name": "lengthFormat", "name": "lengthFormat",
"label": "Length Format", "label": "Length Format",
+11
View File
@@ -230,6 +230,17 @@
"obp60": "true" "obp60": "true"
} }
}, },
{
"name": "smoothCharts",
"label": "Smooth chart lines",
"type": "boolean",
"default": "false",
"description": "Print chart lines with a smoothed gradient",
"category": "OBP60 Settings",
"capabilities": {
"obp40": "true"
}
},
{ {
"name": "lengthFormat", "name": "lengthFormat",
"label": "Length Format", "label": "Length Format",
-67
View File
@@ -1,67 +0,0 @@
// Arduino Moving Average Library
// https://github.com/JChristensen/movingAvg
// Copyright (C) 2018 by Jack Christensen and licensed under
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
#include <movingAvg.h>
// initialize - allocate the interval array
void movingAvg::begin()
{
m_readings = new int[m_interval];
}
// add a new reading and return the new moving average
int movingAvg::reading(int newReading)
{
// add each new data point to the sum until the m_readings array is filled
if (m_nbrReadings < m_interval) {
++m_nbrReadings;
m_sum += newReading;
}
// once the array is filled, subtract the oldest data point and add the new one
else {
m_sum = m_sum - m_readings[m_next] + newReading;
}
m_readings[m_next] = newReading;
if (++m_next >= m_interval) m_next = 0;
return (m_sum + m_nbrReadings / 2) / m_nbrReadings;
}
// just return the current moving average
int movingAvg::getAvg()
{
return (m_sum + m_nbrReadings / 2) / m_nbrReadings;
}
// return the average for a subset of the data, the most recent nPoints readings.
// for invalid values of nPoints, return zero.
int movingAvg::getAvg(int nPoints)
{
if (nPoints < 1 || nPoints > m_interval || nPoints > m_nbrReadings) {
return 0;
}
else {
long sum{0};
int i = m_next;
for (int n=0; n<nPoints; ++n) {
if (i == 0) {
i = m_interval - 1;
}
else {
--i;
}
sum += m_readings[i];
}
return (sum + nPoints / 2) / nPoints;
}
}
// start the moving average over again
void movingAvg::reset()
{
m_nbrReadings = 0;
m_sum = 0;
m_next = 0;
}
+18 -8
View File
@@ -3,27 +3,37 @@
// Copyright (C) 2018 by Jack Christensen and licensed under // Copyright (C) 2018 by Jack Christensen and licensed under
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html // GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
// Extended to template class for handling of multiple data types
#ifndef MOVINGAVG_H_INCLUDED #ifndef MOVINGAVG_H_INCLUDED
#define MOVINGAVG_H_INCLUDED #define MOVINGAVG_H_INCLUDED
template <typename T>
class movingAvg class movingAvg
{ {
public: public:
movingAvg(int interval) movingAvg(int interval)
: m_interval{interval}, m_nbrReadings{0}, m_sum{0}, m_next{0} {} : m_interval{interval}, m_nbrReadings{0}, m_sum{0}, m_next{0}, m_readings{nullptr} {}
~movingAvg() { delete[] m_readings; }
void begin(); void begin();
int reading(int newReading); T reading(T newReading);
int getAvg(); T getAvg();
int getAvg(int nPoints); T getAvg(int nPoints);
int getCount() {return m_nbrReadings;} int getCount() { return m_nbrReadings; }
void reset(); void reset();
int* getReadings() {return m_readings;} T* getReadings() { return m_readings; }
private: private:
int m_interval; // number of data points for the moving average int m_interval; // number of data points for the moving average
int m_nbrReadings; // number of readings int m_nbrReadings; // number of readings
long m_sum; // sum of the m_readings array // Sum type adapts to T: long for integers, T for floating point
using SumType = typename std::conditional<std::is_floating_point<T>::value, T, long>::type;
SumType m_sum;
int m_next; // index to the next reading int m_next; // index to the next reading
int* m_readings; // pointer to the dynamically allocated interval array T* m_readings; // pointer to the dynamically allocated interval array
}; };
// Include the implementation to satisfy template instantiation requirements
#include "movingAvg.tpp"
#endif #endif
+88
View File
@@ -0,0 +1,88 @@
// Arduino Moving Average Library
// https://github.com/JChristensen/movingAvg
// Copyright (C) 2018 by Jack Christensen and licensed under
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
// Extended to template class for handling of multiple data types
//template <typename T>
//movingAvg<T>::movingAvg(int interval)
// : m_interval{interval}, m_nbrReadings{0}, m_sum{0}, m_next{0}, m_readings{nullptr}
//{}
// initialize - allocate the interval array
template <typename T>
void movingAvg<T>::begin()
{
m_readings = new T[m_interval];
}
// add a new reading and return the new moving average
template <typename T>
T movingAvg<T>::reading(T newReading)
{
// add each new data point to the sum until the m_readings array is filled
if (m_nbrReadings < m_interval) {
++m_nbrReadings;
m_sum += newReading;
}
// once the array is filled, subtract the oldest data point and add the new one
else {
m_sum = m_sum - m_readings[m_next] + newReading;
}
m_readings[m_next] = newReading;
if (++m_next >= m_interval) m_next = 0;
return getAvg();
}
// just return the current moving average
template <typename T>
T movingAvg<T>::getAvg()
{
if (m_nbrReadings == 0) return 0;
// Apply rounding for integers only
if (std::is_floating_point<T>::value) {
return m_sum / m_nbrReadings;
} else {
return (m_sum + (SumType)m_nbrReadings / 2) / m_nbrReadings;
}
}
// return the average for a subset of the data, the most recent nPoints readings.
// for invalid values of nPoints, return zero.
template <typename T>
T movingAvg<T>::getAvg(int nPoints)
{
if (nPoints < 1 || nPoints > m_interval || nPoints > m_nbrReadings) {
return 0;
}
SumType sum{0};
int i = m_next;
for (int n=0; n<nPoints; ++n) {
if (i == 0) {
i = m_interval - 1;
}
else {
--i;
}
sum += m_readings[i];
}
if (std::is_floating_point<T>::value) {
return sum / nPoints;
} else {
return (sum + (SumType)nPoints / 2) / nPoints; //
}
}
// start the moving average over again
template <typename T>
void movingAvg<T>::reset()
{
m_nbrReadings = 0;
m_sum = 0;
m_next = 0;
}
+4 -5
View File
@@ -498,7 +498,8 @@ void OBP60Task(GwApi *api){
pages[i].parameters.values.push_back(value); pages[i].parameters.values.push_back(value);
} }
// Read the specified boat data types of relevant pages and create a history buffer for each type // Read the specified boat data types of relevant pages and create a history buffer for each type for later use in charts
// applies only for pages that uses charts
if (pages[i].parameters.pageName == "OneValue" || pages[i].parameters.pageName == "TwoValues" || pages[i].parameters.pageName == "WindPlot") { if (pages[i].parameters.pageName == "OneValue" || pages[i].parameters.pageName == "TwoValues" || pages[i].parameters.pageName == "WindPlot") {
for (auto pVal : pages[i].parameters.values) { for (auto pVal : pages[i].parameters.values) {
hstryBufferList.addBuffer(pVal->getName()); hstryBufferList.addBuffer(pVal->getName());
@@ -843,11 +844,9 @@ void OBP60Task(GwApi *api){
api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues); api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
api->getStatus(commonData.status); api->getStatus(commonData.status);
if (calcTrueWnds) { trueWind.handleWinds(calcTrueWnds); // calculate true wind data from apparent wind values
trueWind.addWinds(); // calculate true wind data from apparent wind values
}
calibrationDataList.handleCalibration(&boatValues); // Process calibration for all boat data in <calibrationDataList> calibrationDataList.handleCalibration(&boatValues); // Process calibration for all boat data in <calibrationDataList>
hstryBufferList.handleHstryBufs(useSimuData, commonData); // Handle history buffers for certain boat data for windplot page and other usage hstryBufferList.handleHstryBufs(useSimuData, commonData); // Handle history buffers for certain boat data for charts and other usage
// Clear display // Clear display
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor); // getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);