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:
+105
-104
@@ -1,5 +1,4 @@
|
||||
#include "OBPDataOperations.h"
|
||||
//#include "BoatDataCalibration.h" // Functions lib for data instance calibration
|
||||
|
||||
// --- Class CalibrationData ---------------
|
||||
CalibrationData::CalibrationData(GwLog* log)
|
||||
@@ -155,8 +154,7 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue)
|
||||
|
||||
if (format == "formatWind") { // instance is of type angle
|
||||
dataValue = (dataValue * slope) + offset;
|
||||
// dataValue = WindUtils::toPI(dataValue);
|
||||
dataValue = WindUtils::to2PI(dataValue); // we should call <toPI> for format of [-180..180], but pages cannot display negative values properly yet
|
||||
dataValue = WindUtils::to2PI(dataValue); // wind angle data is stored in [0..2PI] format
|
||||
|
||||
} else if (format == "formatCourse") { // instance is of type direction
|
||||
dataValue = (dataValue * slope) + offset;
|
||||
@@ -169,7 +167,6 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue)
|
||||
dataValue = (dataValue * slope) + offset;
|
||||
}
|
||||
|
||||
|
||||
boatDataValue->value = dataValue; // update boat data value with calibrated value
|
||||
calibrationMap[instance].value = dataValue; // store the calibrated value in the list
|
||||
calibrationMap[instance].isCalibrated = true;
|
||||
@@ -247,23 +244,15 @@ void HstryBuf::add(double value)
|
||||
|
||||
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
|
||||
|
||||
// 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));
|
||||
if (boatValue->valid) {
|
||||
add(boatValue->value);
|
||||
} 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;
|
||||
|
||||
if (boatValue->valid) {
|
||||
// Calibrate boat value before adding it to history buffer
|
||||
//calibrationData.calibrateInstance(tmpBVal.get(), logger);
|
||||
//add(tmpBVal->value);
|
||||
add(boatValue->value);
|
||||
|
||||
} else if (useSimuData) { // add simulated value to history buffer
|
||||
double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>; here: retreive SI value
|
||||
add(simSIValue);
|
||||
} else {
|
||||
@@ -276,20 +265,7 @@ void HstryBuf::handle(bool useSimuData, CommonData& common)
|
||||
HstryBuffers::HstryBuffers(int size, BoatValueList* boatValues, GwLog* log)
|
||||
: size(size)
|
||||
, boatValueList(boatValues)
|
||||
, 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");
|
||||
}
|
||||
, logger(log) { };
|
||||
|
||||
// Create history buffer for boat data type
|
||||
void HstryBuffers::addBuffer(const String& name)
|
||||
@@ -301,8 +277,6 @@ void HstryBuffers::addBuffer(const String& name)
|
||||
return;
|
||||
}
|
||||
|
||||
hstryBuffers[name] = std::unique_ptr<HstryBuf>(new HstryBuf(name, size, boatValueList, logger));
|
||||
|
||||
// Initialize metadata for buffer
|
||||
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
|
||||
@@ -311,6 +285,7 @@ void HstryBuffers::addBuffer(const String& name)
|
||||
double bufferMinVal = bufferParams[name].bufferMinVal; // Min 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);
|
||||
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);
|
||||
if (it != hstryBuffers.end()) {
|
||||
return &it->second->hstryBuf;
|
||||
}
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// --- End Class HstryBuffers ---------------
|
||||
|
||||
@@ -396,56 +372,75 @@ void WindUtils::addPolar(const double* phi1, const double* r1,
|
||||
toPol(&x1, &y1, phi, r);
|
||||
}
|
||||
|
||||
void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
|
||||
const double* CTW, const double* STW, const double* HDT,
|
||||
double* TWD, double* TWS, double* TWA, double* AWD)
|
||||
void WindUtils::calcTwdSA(const double* AWA, const double* AWS, const double* AWD, const double* CTW, const double* STW, const double* HDT,
|
||||
double* TWA, double* TWS, double* TWD)
|
||||
{
|
||||
*AWD = *AWA + *HDT;
|
||||
*AWD = to2PI(*AWD);
|
||||
double stw = -*STW;
|
||||
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);
|
||||
*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
|
||||
|
||||
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 {
|
||||
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
|
||||
|
||||
ctw = *cogVal; // Use COG for CTW if available
|
||||
} 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) {
|
||||
@@ -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
|
||||
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)) {
|
||||
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
|
||||
return false;
|
||||
} else {
|
||||
calcTwdSA(awaVal, awsVal, &ctw, &stw, &hdt, &twd, &tws, &twa, &awd);
|
||||
*twdVal = twd;
|
||||
*twsVal = tws;
|
||||
calcTwdSA(awaVal, awsVal, awd, &ctw, &stw, hdtVal, &twa, &tws, &twd);
|
||||
*twaVal = twa;
|
||||
*awdVal = awd;
|
||||
*twsVal = tws;
|
||||
*twdVal = twd;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 awdCalculated = false;
|
||||
|
||||
double awaVal = awaBVal->valid ? awaBVal->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 hdmVal = hdmBVal->valid ? hdmBVal->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,
|
||||
// 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 twaVal = twaBVal->valid ? twaBVal->value : DBL_MAX;
|
||||
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 (twaBVal->valid) {
|
||||
if (!twdBVal->valid) {
|
||||
if (hdtVal != DBL_MAX) {
|
||||
hdt = hdtVal; // Use HDT if available
|
||||
if (calcHDT(&hdmVal, &varVal, &cogVal, &sogVal, &hdtVal)) {
|
||||
hdtBVal->value = hdtVal;
|
||||
hdtBVal->valid = true;
|
||||
} else {
|
||||
hdt = calcHDT(&hdmVal, &varVal, &cogVal, &sogVal);
|
||||
// determination of HDT was not possible due to missing prerequisite data
|
||||
hdtBVal->valid = false;
|
||||
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->valid = true;
|
||||
} else {
|
||||
twdBVal->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Calculate true winds and AWD; if true winds exist, use at least AWD calculation
|
||||
twCalculated = calcWinds(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa, &awd);
|
||||
|
||||
if (calcWinds && (!twaBVal->valid || !twsBVal->valid || !twdBVal->valid)) {
|
||||
// calculate true winds if user setting and at least one of three true wind values does not exist
|
||||
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 (!twdBVal->valid) {
|
||||
twdBVal->value = twd;
|
||||
twdBVal->valid = true;
|
||||
if (!twaBVal->valid) {
|
||||
twaBVal->value = twa;
|
||||
twaBVal->valid = true;
|
||||
}
|
||||
if (!twsBVal->valid) {
|
||||
twsBVal->value = tws;
|
||||
twsBVal->valid = true;
|
||||
}
|
||||
if (!twaBVal->valid) {
|
||||
//twaBVal->value = twa;
|
||||
twaBVal->value = to2PI(twa); // convert to [0..360], because pages cannot display negative values properly yet
|
||||
twaBVal->valid = true;
|
||||
}
|
||||
if (!awdBVal->valid) {
|
||||
awdBVal->value = awd;
|
||||
awdBVal->valid = true;
|
||||
if (!twdBVal->valid) {
|
||||
twdBVal->value = twd;
|
||||
twdBVal->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);
|
||||
|
||||
return twCalculated;
|
||||
|
||||
@@ -19,7 +19,7 @@ private:
|
||||
} tCalibrationData;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// Class for a single history buffer of boat values
|
||||
class HstryBuf {
|
||||
private:
|
||||
RingBuffer<uint16_t> hstryBuf; // Circular buffer to store history values
|
||||
@@ -50,13 +51,13 @@ public:
|
||||
void handle(bool useSimuData, CommonData& common);
|
||||
};
|
||||
|
||||
// Manage list of history buffers for supported boat data; used by boat data charts
|
||||
class HstryBuffers {
|
||||
private:
|
||||
std::map<String, std::unique_ptr<HstryBuf>> hstryBuffers;
|
||||
int size; // size of all history buffers
|
||||
BoatValueList* boatValueList;
|
||||
GwLog* logger;
|
||||
GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal, *awdBVal; // boat values for true wind calculation
|
||||
|
||||
struct HistoryParams {
|
||||
int hstryUpdFreq; // update frequency of history buffer (documentation only)
|
||||
@@ -97,7 +98,8 @@ public:
|
||||
class WindUtils {
|
||||
private:
|
||||
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();
|
||||
GwLog* logger;
|
||||
|
||||
@@ -128,12 +130,12 @@ public:
|
||||
void addPolar(const double* phi1, const double* r1,
|
||||
const double* phi2, const double* r2,
|
||||
double* phi, double* r);
|
||||
void calcTwdSA(const double* AWA, const double* AWS,
|
||||
const double* CTW, const double* STW, const double* HDT,
|
||||
double* TWD, double* TWS, double* TWA, double* AWD);
|
||||
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
|
||||
bool calcWinds(const double* awaVal, const double* awsVal,
|
||||
void calcTwdSA(const double* AWA, const double* AWS, const double* AWD, const double* CTW, const double* STW, const double* HDT,
|
||||
double* TWA, double* TWS, double* TWD);
|
||||
bool calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal, double* hdtVal);
|
||||
bool calcWD(const double* waVal, const double* hdtVal, double* wdVal);
|
||||
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* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal, double* awdVal);
|
||||
bool addWinds();
|
||||
double* twdVal, double* twsVal, double* twaVal);
|
||||
bool handleWinds(bool calcWinds);
|
||||
};
|
||||
@@ -66,8 +66,8 @@ void sensorTask(void *param){
|
||||
const int avgsize = 300;
|
||||
constexpr int arrayBatV{avgsize};
|
||||
constexpr int arrayBatC{avgsize};
|
||||
movingAvg batV(arrayBatV);
|
||||
movingAvg batC(arrayBatC);
|
||||
movingAvg<int> batV(arrayBatV);
|
||||
movingAvg<int> batC(arrayBatC);
|
||||
batV.begin();
|
||||
batC.begin();
|
||||
|
||||
|
||||
+49
-21
@@ -6,9 +6,9 @@
|
||||
std::map<String, ChartProps> Chart::dfltChrtDta = {
|
||||
{ "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
|
||||
{ "formatKnots", { 7.71, 2.56 } }, // default speed range in m/s
|
||||
{ "formatDepth", { 15.0, 5.0 } }, // default depth range in m
|
||||
{ "kelvinToC", { 30.0, 5.0 } } // default temp range in °C/K
|
||||
{ "formatKnots", { 2.57, 2.57 } }, // default speed range in m/s
|
||||
{ "formatDepth", { 10.0, 5.0 } }, // default depth range in m
|
||||
{ "kelvinToC", { 20.0, 5.0 } } // default temp range in °C/K
|
||||
};
|
||||
|
||||
// --- Class Chart ---------------
|
||||
@@ -42,6 +42,11 @@ Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common,
|
||||
dbMAX_VAL = dataBuf.getMaxVal();
|
||||
bufSize = dataBuf.getCapacity();
|
||||
|
||||
smoothCharts = common.config->getBool(common.config->smoothCharts);
|
||||
if (smoothCharts) {
|
||||
chrtAvg.begin();
|
||||
}
|
||||
|
||||
// Initialize chart data format; shorter version of standard format indicator
|
||||
if (dbFormat == "formatCourse" || dbFormat == "formatWind" || dbFormat == "formatRot") {
|
||||
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
|
||||
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++) {
|
||||
|
||||
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;
|
||||
} 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);
|
||||
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
@@ -440,46 +467,47 @@ Pos Chart::setCurrentChartPoint(const int i, const char direction, const double
|
||||
// chart time axis label + lines
|
||||
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];
|
||||
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().setTextColor(fgColor);
|
||||
|
||||
axSlots = 5; // number of axis labels
|
||||
intv = timAxis / (axSlots - 1); // minutes per chart axis interval (interval is 1 less than axSlots)
|
||||
i = timeRng; // Chart axis label start at -32, -16, -12, ... minutes
|
||||
intv = 60; // print axis label each 60 pixels = seconds
|
||||
axSlots = timAxis / intv; // number of axis labels; value will be truncated to full number
|
||||
i = axSlots * chrtIntv * -1; // current minute label
|
||||
|
||||
if (chrtDir == HORIZONTAL) {
|
||||
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
|
||||
int tOffset = j == 0 ? 13 : -4;
|
||||
snprintf(sTime, sizeof(sTime), "-%.0f", i);
|
||||
tOffset = j == 0 ? 13 : -4;
|
||||
snprintf(sTime, sizeof(sTime), "%d", i);
|
||||
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
|
||||
|
||||
i -= chrtIntv;
|
||||
i += chrtIntv;
|
||||
}
|
||||
|
||||
} 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
|
||||
|
||||
i -= chrtIntv; // we start not at top chart position
|
||||
snprintf(sTime, sizeof(sTime), "-%.0f", i);
|
||||
snprintf(sTime, sizeof(sTime), "%d", i);
|
||||
getdisplay().drawLine(cRoot.x, cRoot.y + j, cRoot.x + valAxis, cRoot.y + j, fgColor); // Grid line
|
||||
|
||||
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().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
|
||||
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) {
|
||||
|
||||
// print buffer data name on left hand side of time axis (max. size 5 characters)
|
||||
font = &Ubuntu_Bold12pt8b;
|
||||
|
||||
// print buffer data name on right hand side of time axis (max. size 5 characters)
|
||||
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) {
|
||||
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
|
||||
|
||||
font = &Ubuntu_Bold10pt8b;
|
||||
|
||||
if (prntName) {
|
||||
// print buffer data name on right hand side of time axis (max. size 5 characters)
|
||||
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);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
#include "Pagedata.h"
|
||||
#include "OBP60Extensions.h"
|
||||
#include "movingAvg.h"
|
||||
|
||||
struct Pos {
|
||||
int x;
|
||||
@@ -44,12 +45,11 @@ protected:
|
||||
static constexpr bool NO_SIMUDATA = true; // switch off simulation feature of <formatValue> function
|
||||
|
||||
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]
|
||||
uint16_t fgColor; // color code for any screen writing
|
||||
uint16_t bgColor; // color code for screen background
|
||||
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
|
||||
double zeroValue; // "0" SI value for temperature
|
||||
|
||||
@@ -84,6 +84,7 @@ protected:
|
||||
bool bufDataValid = false; // Flag to indicate if buffer data is valid
|
||||
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
|
||||
int x, y; // x and y coordinates for drawing
|
||||
int prevX, prevY; // Last x and y coordinates for drawing
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "Pagedata.h"
|
||||
#include "OBP60Extensions.h"
|
||||
#include "movingAvg.h" // Lib for moving average building
|
||||
|
||||
class PageBattery2 : public Page
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "Pagedata.h"
|
||||
#include "OBP60Extensions.h"
|
||||
#include "movingAvg.h" // Lib for moving average building
|
||||
|
||||
class PageGenerator : public Page
|
||||
{
|
||||
|
||||
@@ -209,8 +209,6 @@ public:
|
||||
dataIntv = 3;
|
||||
} else if (dataIntv == 3) {
|
||||
dataIntv = 4;
|
||||
} else if (dataIntv == 4) {
|
||||
dataIntv = 8;
|
||||
} else {
|
||||
dataIntv = 1;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "Pagedata.h"
|
||||
#include "OBP60Extensions.h"
|
||||
#include "movingAvg.h" // Lib for moving average building
|
||||
|
||||
class PageSolar : public Page
|
||||
{
|
||||
|
||||
@@ -211,8 +211,6 @@ public:
|
||||
dataIntv = 3;
|
||||
} else if (dataIntv == 3) {
|
||||
dataIntv = 4;
|
||||
} else if (dataIntv == 4) {
|
||||
dataIntv = 8;
|
||||
} else {
|
||||
dataIntv = 1;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "Pagedata.h"
|
||||
#include "OBP60Extensions.h"
|
||||
#include "movingAvg.h" // Lib for moving average building
|
||||
|
||||
class PageVoltage : public Page
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ private:
|
||||
bool oldShowTruW = false; // remember recent user selection of wind data type
|
||||
|
||||
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 holdValues;
|
||||
String flashLED;
|
||||
@@ -231,6 +231,9 @@ public:
|
||||
|
||||
} else if (chrtMode == SPEED) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -230,6 +230,17 @@
|
||||
"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",
|
||||
"label": "Length Format",
|
||||
|
||||
@@ -230,6 +230,17 @@
|
||||
"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",
|
||||
"label": "Length Format",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -3,27 +3,37 @@
|
||||
// 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
|
||||
|
||||
#ifndef MOVINGAVG_H_INCLUDED
|
||||
#define MOVINGAVG_H_INCLUDED
|
||||
|
||||
template <typename T>
|
||||
class movingAvg
|
||||
{
|
||||
public:
|
||||
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();
|
||||
int reading(int newReading);
|
||||
int getAvg();
|
||||
int getAvg(int nPoints);
|
||||
int getCount() {return m_nbrReadings;}
|
||||
T reading(T newReading);
|
||||
T getAvg();
|
||||
T getAvg(int nPoints);
|
||||
int getCount() { return m_nbrReadings; }
|
||||
void reset();
|
||||
int* getReadings() {return m_readings;}
|
||||
T* getReadings() { return m_readings; }
|
||||
|
||||
private:
|
||||
int m_interval; // number of data points for the moving average
|
||||
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_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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -498,7 +498,8 @@ void OBP60Task(GwApi *api){
|
||||
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") {
|
||||
for (auto pVal : pages[i].parameters.values) {
|
||||
hstryBufferList.addBuffer(pVal->getName());
|
||||
@@ -843,11 +844,9 @@ void OBP60Task(GwApi *api){
|
||||
api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
|
||||
api->getStatus(commonData.status);
|
||||
|
||||
if (calcTrueWnds) {
|
||||
trueWind.addWinds(); // calculate true wind data from apparent wind values
|
||||
}
|
||||
trueWind.handleWinds(calcTrueWnds); // calculate true wind data from apparent wind values
|
||||
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
|
||||
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
||||
|
||||
Reference in New Issue
Block a user