mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-09-11 19:55:14 +02:00
PageCurrent: added leeway coeff, changed boat value handling, fixed ROLL
This commit is contained in:
@@ -3,15 +3,26 @@
|
|||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "OBPDataOperations.h"
|
#include "OBPDataOperations.h"
|
||||||
// #include <cmath>
|
#include <unordered_map>
|
||||||
|
|
||||||
// Screen coordinates
|
// Screen coordinates for three boat values
|
||||||
struct Points {
|
struct Points {
|
||||||
int16_t x1, y1;
|
int16_t x1, y1;
|
||||||
int16_t x2, y2;
|
int16_t x2, y2;
|
||||||
int16_t x3, y3;
|
int16_t x3, y3;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// leeway K coefficient selection options from OBP configuration page
|
||||||
|
static const std::unordered_map<std::string, int> leeKMap = {
|
||||||
|
{ "---", 0 },
|
||||||
|
{ "Racer [2]", 2 },
|
||||||
|
{ "Multihull Draggerboard [4]", 4 },
|
||||||
|
{ "Performance Cruiser [6]", 6 },
|
||||||
|
{ "Family Cruiser [9]", 9 },
|
||||||
|
{ "Heavy Displacement [13]", 13 },
|
||||||
|
{ "Multihull Fixed Keel [16]", 16 }
|
||||||
|
};
|
||||||
|
|
||||||
// Screen coordinates for boat data values (top-left, bottom-left, top-right, bottom-right corners)
|
// Screen coordinates for boat data values (top-left, bottom-left, top-right, bottom-right corners)
|
||||||
static constexpr Points POS[] = {
|
static constexpr Points POS[] = {
|
||||||
{ 10, 65, 10, 95, 10, 115 }, // Position left top for value, name, unit
|
{ 10, 65, 10, 95, 10, 115 }, // Position left top for value, name, unit
|
||||||
@@ -61,7 +72,8 @@ private:
|
|||||||
bool holdValues;
|
bool holdValues;
|
||||||
String flashLED;
|
String flashLED;
|
||||||
String backlightMode;
|
String backlightMode;
|
||||||
uint8_t leeK;
|
String leeKStd;
|
||||||
|
double leeK;
|
||||||
|
|
||||||
static constexpr int NUMVALUES = 10; // no. of data values in this page
|
static constexpr int NUMVALUES = 10; // no. of data values in this page
|
||||||
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
|
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
|
||||||
@@ -120,11 +132,11 @@ private:
|
|||||||
|
|
||||||
ctw = calcCTW(awa, hdt, lay);
|
ctw = calcCTW(awa, hdt, lay);
|
||||||
|
|
||||||
// Ground velocity vector (East, North)
|
// Ground velocity vector (east, north)
|
||||||
double vg_x = sog * std::sin(cog);
|
double vg_x = sog * std::sin(cog);
|
||||||
double vg_y = sog * std::cos(cog);
|
double vg_y = sog * std::cos(cog);
|
||||||
|
|
||||||
// Water-relative velocity vector (East, North)
|
// Water-relative velocity vector (east, north)
|
||||||
double vw_x = stw * std::sin(ctw);
|
double vw_x = stw * std::sin(ctw);
|
||||||
double vw_y = stw * std::cos(ctw);
|
double vw_y = stw * std::cos(ctw);
|
||||||
|
|
||||||
@@ -137,8 +149,8 @@ private:
|
|||||||
crnt.set = std::atan2(vc_x, vc_y); // atan2(x, y) because 0° = North, clockwise positive
|
crnt.set = std::atan2(vc_x, vc_y); // atan2(x, y) because 0° = North, clockwise positive
|
||||||
crnt.set = WindUtils::to2PI(crnt.set); // internal respresentation of wind is [0..2PI]
|
crnt.set = WindUtils::to2PI(crnt.set); // internal respresentation of wind is [0..2PI]
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageCurrent-setDrift: sog: %.3f, ctw: %.3f, stw: %.3f, vc_x: %.3f, vc_y: %.3f, set: %.3f, drift: %.3f",
|
//LOG_DEBUG(GwLog::DEBUG, "PageCurrent-setDrift: sog: %.3f, ctw: %.3f, stw: %.3f, vc_x: %.3f, vc_y: %.3f, set: %.3f, drift: %.3f",
|
||||||
sog, ctw, stw, vc_x, vc_y, crnt.set, crnt.dft);
|
// sog, ctw, stw, vc_x, vc_y, crnt.set, crnt.dft);
|
||||||
|
|
||||||
return crnt;
|
return crnt;
|
||||||
};
|
};
|
||||||
@@ -254,16 +266,6 @@ private:
|
|||||||
getdisplay().fillTriangle(rx[0], ry[0], rx[2], ry[2], rx[6], ry[6], commonData->bgcolor);
|
getdisplay().fillTriangle(rx[0], ry[0], rx[2], ry[2], rx[6], ry[6], commonData->bgcolor);
|
||||||
getdisplay().fillTriangle(rx[5], ry[5], rx[4], ry[4], rx[3], ry[3], commonData->bgcolor);
|
getdisplay().fillTriangle(rx[5], ry[5], rx[4], ry[4], rx[3], ry[3], commonData->bgcolor);
|
||||||
|
|
||||||
/* // draw arrow frame (double for 2 px thickness)
|
|
||||||
for (int i = 0; i < 7; i++) {
|
|
||||||
int next = (i + 1) % 7;
|
|
||||||
// outer frame
|
|
||||||
getdisplay().drawLine(rx[i], ry[i], rx[next], ry[next], commonData->fgcolor);
|
|
||||||
// inner frame
|
|
||||||
getdisplay().drawLine(rxi[i], ryi[i], rxi[next], ryi[next], commonData->fgcolor);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// 5. Rahmen mit "Overdraw" für konstante Dicke
|
|
||||||
// draw arrow frame with "overdraw" for constant thickness
|
// draw arrow frame with "overdraw" for constant thickness
|
||||||
for (int i = 0; i < 7; i++) {
|
for (int i = 0; i < 7; i++) {
|
||||||
int next = (i + 1) % 7;
|
int next = (i + 1) % 7;
|
||||||
@@ -297,8 +299,15 @@ public:
|
|||||||
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
||||||
flashLED = commonData->config->getString(commonData->config->flashLED);
|
flashLED = commonData->config->getString(commonData->config->flashLED);
|
||||||
backlightMode = commonData->config->getString(commonData->config->backlight);
|
backlightMode = commonData->config->getString(commonData->config->backlight);
|
||||||
// leeK = commonData->config->getInt(commonData->config->leeK);
|
|
||||||
leeK = 9;
|
leeKStd = commonData->config->getString(commonData->config->leeKStd);
|
||||||
|
auto it = leeKMap.find(leeKStd.c_str());
|
||||||
|
if (it != leeKMap.end()) {
|
||||||
|
leeK = it->second;
|
||||||
|
} else if (leeKStd == "Individual value") {
|
||||||
|
leeK = (commonData->config->getString(commonData->config->leeK)).toDouble();
|
||||||
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: leeKStd: %s, leeK: %.3f", leeKStd.c_str(), leeK);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int handleKey(int key)
|
virtual int handleKey(int key)
|
||||||
@@ -347,43 +356,49 @@ public:
|
|||||||
// 0=AWA, 1=HDT, 2=SET, 3=DFT, 4=SOG, 5=COG, 6=STW, 7=HDM, 8=VAR, 9=ROLL
|
// 0=AWA, 1=HDT, 2=SET, 3=DFT, 4=SOG, 5=COG, 6=STW, 7=HDM, 8=VAR, 9=ROLL
|
||||||
std::vector<GwApi::BoatValue*> bValue(pageData.values.begin(), pageData.values.end());
|
std::vector<GwApi::BoatValue*> bValue(pageData.values.begin(), pageData.values.end());
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: printing #1: %s, %.3f, #2: %s, %.3f, #3: %s, %.3f, #4: %s, %.3f",
|
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: printing #1: %s, %.3f, valid: %d, #2: %s, %.3f, valid: %d, #3: %s, %.3f, valid: %d, #4: %s, %.3f, valid: %d",
|
||||||
bValue[AWA]->getName().c_str(), bValue[AWA]->value, bValue[HDT]->getName().c_str(), bValue[HDT]->value,
|
bValue[AWA]->getName().c_str(), bValue[AWA]->value, bValue[AWA]->valid, bValue[HDT]->getName().c_str(), bValue[HDT]->value, bValue[HDT]->valid,
|
||||||
bValue[SET]->getName().c_str(), bValue[SET]->value, bValue[DFT]->getName().c_str(), bValue[DFT]->value);
|
bValue[SET]->getName().c_str(), bValue[SET]->value, bValue[SET]->valid, bValue[DFT]->getName().c_str(), bValue[DFT]->value, bValue[DFT]->valid);
|
||||||
|
|
||||||
double awa = bValue[AWA]->valid ? bValue[AWA]->value : DBL_MAX;
|
|
||||||
double hdt = bValue[HDT]->valid ? bValue[HDT]->value : DBL_MAX;
|
|
||||||
current.set = bValue[SET]->valid ? bValue[SET]->value : DBL_MAX;
|
|
||||||
current.dft = bValue[DFT]->valid ? bValue[DFT]->value : DBL_MAX;
|
|
||||||
double sog = bValue[SOG]->valid ? bValue[SOG]->value : DBL_MAX;
|
|
||||||
double cog = bValue[COG]->valid ? bValue[COG]->value : DBL_MAX;
|
|
||||||
double stw = bValue[STW]->valid ? bValue[STW]->value : DBL_MAX;
|
|
||||||
double hdm = bValue[HDM]->valid ? bValue[HDM]->value : DBL_MAX;
|
|
||||||
double var = bValue[VAR]->valid ? bValue[VAR]->value : DBL_MAX;
|
|
||||||
double roll = bValue[ROLL]->valid ? bValue[ROLL]->value : DBL_MAX;
|
|
||||||
|
|
||||||
// Calculate current data
|
// Calculate current data
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: hdt 2nd: %.3f", hdt);
|
if (!bValue[SET]->valid || !bValue[DFT]->valid) { // If SET or DRIFT not available, try to calculate them
|
||||||
if (current.set == DBL_MAX || current.dft == DBL_MAX) { // If SET or DRIFT not available, try to calculate them
|
if (bValue[SOG]->valid && bValue[COG]->valid && bValue[STW]->valid && bValue[AWA]->valid) { // calculate current only if all required values are available
|
||||||
|
|
||||||
if (hdt == DBL_MAX) { // HDT not available
|
if (!bValue[HDT]->valid) { // HDT not available
|
||||||
if (hdm != DBL_MAX) {
|
if (bValue[HDM]->valid) {
|
||||||
hdt = hdm + (var != DBL_MAX ? var : 0.0); // Use corrected HDM if HDT is not available; just use HDM if VAR is not available
|
if (bValue[VAR]->valid) {
|
||||||
hdt = WindUtils::to2PI(hdt);
|
bValue[HDT]->value = bValue[HDM]->value + bValue[VAR]->value; // Use corrected HDM if HDT is not available
|
||||||
|
bValue[HDT]->value = WindUtils::to2PI(bValue[HDT]->value);
|
||||||
|
bValue[HDT]->valid = true;
|
||||||
|
} else {
|
||||||
|
// if HDT cannot be fully substituted by HDM+VAR, continue with HDM only
|
||||||
|
bValue[HDT] = bValue[HDM];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: hdt 3rd: %.3f", hdt);
|
|
||||||
|
|
||||||
leeK = 4;
|
|
||||||
roll = 10 * DEG_TO_RAD;
|
|
||||||
lay = calcLeeway(leeK, roll, stw);
|
|
||||||
current = calcSetAndDrift(lay, sog, cog, stw, hdt, awa);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: leeK: %d, lay: %.3f, roll: %.3f, stw: %.3f, awa: %.3f, hdt: %.3f, cog: %.3f, sog: %.3f, set: %.3f, dft: %.3f",
|
if (!bValue[ROLL]->valid) {
|
||||||
leeK, lay, roll, stw, awa, hdt, cog, sog, current.set, current.dft);
|
bValue[ROLL]->value = 0; // delete last value if roll value is not valid anymore; gateway keeps last value
|
||||||
|
}
|
||||||
|
lay = calcLeeway(leeK, bValue[ROLL]->value, bValue[STW]->value);
|
||||||
|
|
||||||
|
if (bValue[HDT]->valid) { // That's either HDT or HDM
|
||||||
|
current = calcSetAndDrift(lay, bValue[SOG]->value, bValue[COG]->value, bValue[STW]->value, bValue[HDT]->value, bValue[AWA]->value);
|
||||||
|
bValue[SET]->value = current.set;
|
||||||
|
bValue[SET]->setFormat("formatCourse");
|
||||||
|
bValue[SET]->valid = true;
|
||||||
|
bValue[DFT]->value = current.dft;
|
||||||
|
bValue[DFT]->setFormat("formatKnots");
|
||||||
|
bValue[DFT]->valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: leeKStd: %s, leeK: %.3f, lay: %.3f, roll: %.3f, stw: %.3f, awa: %.3f, hdt: %.3f, cog: %.3f, sog: %.3f, set: %.3f, dft: %.3f",
|
||||||
|
leeKStd.c_str(), leeK, lay, bValue[ROLL]->value, bValue[STW]->value, bValue[AWA]->value, bValue[HDT]->value, bValue[COG]->value, bValue[SOG]->value,
|
||||||
|
bValue[SET]->value, bValue[DFT]->value);
|
||||||
|
|
||||||
// Draw page
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
@@ -394,36 +409,10 @@ public:
|
|||||||
for (int i = 0; i < 4; i++) { // Display first 4 values
|
for (int i = 0; i < 4; i++) { // Display first 4 values
|
||||||
String name = xdrDelete(bValue[i]->getName()); // Value name
|
String name = xdrDelete(bValue[i]->getName()); // Value name
|
||||||
name = name.substring(0, 6); // String length limit for value name
|
name = name.substring(0, 6); // String length limit for value name
|
||||||
if (!bValue[i]->valid) {
|
|
||||||
switch (i) {
|
|
||||||
case AWA:
|
|
||||||
break; // we don't change AWA any time, so there is no adjustment option
|
|
||||||
case HDT:
|
|
||||||
bValue[i]->value = hdt;
|
|
||||||
bValue[i]->setFormat("formatCourse");
|
|
||||||
bValue[i]->valid = true; // Valid information
|
|
||||||
break;
|
|
||||||
case SET:
|
|
||||||
bValue[i]->value = current.set;
|
|
||||||
bValue[i]->setFormat("formatCourse");
|
|
||||||
bValue[i]->valid = true; // Valid information
|
|
||||||
break;
|
|
||||||
case DFT:
|
|
||||||
bValue[i]->value = current.dft;
|
|
||||||
bValue[i]->setFormat("formatKnots");
|
|
||||||
bValue[i]->valid = true; // Valid information
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String sValue = formatValue(bValue[i], *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
String sValue = formatValue(bValue[i], *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
String unit = formatValue(bValue[i], *commonData).unit; // Unit of value
|
String unit = formatValue(bValue[i], *commonData).unit; // Unit of value
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: name: %s, value: %s, format: %s, unit: %s",
|
// Show boat data value
|
||||||
name, sValue, bValue[i]->getFormat().c_str(), unit);
|
|
||||||
|
|
||||||
// Show bus data
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
||||||
getdisplay().setCursor(POS[i].x1, POS[i].y1);
|
getdisplay().setCursor(POS[i].x1, POS[i].y1);
|
||||||
if (!holdValues || useSimuData) {
|
if (!holdValues || useSimuData) {
|
||||||
@@ -458,9 +447,13 @@ public:
|
|||||||
// Horizontal separator right
|
// Horizontal separator right
|
||||||
getdisplay().fillRect(339, 149, 60, 2, commonData->fgcolor);
|
getdisplay().fillRect(339, 149, 60, 2, commonData->fgcolor);
|
||||||
|
|
||||||
drawCompassRose(hdt);
|
if (bValue[HDT]->valid) {
|
||||||
|
drawCompassRose(bValue[HDT]->value);
|
||||||
|
} else {
|
||||||
|
drawCompassRose(0.0);
|
||||||
|
};
|
||||||
getdisplay().drawBitmap((width - WAVE_W) / 2, (height - WAVE_H) / 2, wave_bitmap, WAVE_W, WAVE_H, commonData->fgcolor);
|
getdisplay().drawBitmap((width - WAVE_W) / 2, (height - WAVE_H) / 2, wave_bitmap, WAVE_W, WAVE_H, commonData->fgcolor);
|
||||||
drawRotatedArrow(current.dft, WindUtils::to2PI(current.set - hdt));
|
drawRotatedArrow(bValue[DFT]->value, WindUtils::to2PI(bValue[SET]->value - bValue[HDT]->value));
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
@@ -482,7 +475,7 @@ PageDescription registerPageCurrent(
|
|||||||
"Current", // Page name
|
"Current", // 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
|
||||||
{ "AWA", "HDT", "SET", "DFT", "SOG", "COG", "STW", "HDM", "VAR", "ROLL" }, // Bus values we need in the page
|
{ "AWA", "HDT", "SET", "DFT", "SOG", "COG", "STW", "HDM", "VAR", "xdrROLL" }, // Bus values we need in the page
|
||||||
true // Show display header on/off
|
true // Show display header on/off
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,44 @@
|
|||||||
"obp40": "true"
|
"obp40": "true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "leeKStd",
|
||||||
|
"label": "Leeway Coefficient",
|
||||||
|
"type": "list",
|
||||||
|
"default": "---",
|
||||||
|
"description": "Boat coefficient K for leeway",
|
||||||
|
"list": [
|
||||||
|
"---",
|
||||||
|
"Racer [2]",
|
||||||
|
"Multihull Draggerboard [4]",
|
||||||
|
"Performance Cruiser [6]",
|
||||||
|
"Family Cruiser [9]",
|
||||||
|
"Heavy Displacement [13]",
|
||||||
|
"Multihull Fixed Keel [16]",
|
||||||
|
"Individual value"
|
||||||
|
],
|
||||||
|
"category": "OBP40 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp40": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "leeK",
|
||||||
|
"label": "Leeway Coefficient",
|
||||||
|
"type": "number",
|
||||||
|
"default": "0.00",
|
||||||
|
"check": "checkMinMax",
|
||||||
|
"min": 0,
|
||||||
|
"max": 20,
|
||||||
|
"description": "Boat coefficient K for leeway - individual setting [0..20]",
|
||||||
|
"category": "OBP40 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp40":"true"
|
||||||
|
},
|
||||||
|
"condition": [
|
||||||
|
{ "leeKStd": ["Individual value"] }
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "fuelTank",
|
"name": "fuelTank",
|
||||||
"label": "Fuel Tank [l]",
|
"label": "Fuel Tank [l]",
|
||||||
|
|||||||
@@ -75,6 +75,44 @@
|
|||||||
"obp60":"true"
|
"obp60":"true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "leeKStd",
|
||||||
|
"label": "Leeway Coefficient",
|
||||||
|
"type": "list",
|
||||||
|
"default": "---",
|
||||||
|
"description": "Boat coefficient K for leeway",
|
||||||
|
"list": [
|
||||||
|
"---",
|
||||||
|
"Racer [2]",
|
||||||
|
"Multihull Draggerboard [4]",
|
||||||
|
"Performance Cruiser [6]",
|
||||||
|
"Family Cruiser [9]",
|
||||||
|
"Heavy Displacement [13]",
|
||||||
|
"Multihull Fixed Keel [16]",
|
||||||
|
"Individual value"
|
||||||
|
],
|
||||||
|
"category": "OBP60 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp60": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "leeK",
|
||||||
|
"label": "Leeway Coefficient",
|
||||||
|
"type": "number",
|
||||||
|
"default": "0.00",
|
||||||
|
"check": "checkMinMax",
|
||||||
|
"min": 0,
|
||||||
|
"max": 20,
|
||||||
|
"description": "Boat coefficient K for leeway - individual setting [0..20]",
|
||||||
|
"category": "OBP60 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp60":"true"
|
||||||
|
},
|
||||||
|
"condition": [
|
||||||
|
{ "leeKStd": ["Individual value"] }
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "fuelTank",
|
"name": "fuelTank",
|
||||||
"label": "Fuel Tank [l]",
|
"label": "Fuel Tank [l]",
|
||||||
|
|||||||
Reference in New Issue
Block a user