added calibration to buffer; separated buffer and wind code in opb60task; prepared simulation; getMin/Max fix for ringbuffer for invalid data; fix for chart center; cleanup code
This commit is contained in:
parent
c48c6a2e48
commit
fe2223839f
|
@ -1,45 +1,51 @@
|
|||
#include "OBPDataOperations.h"
|
||||
|
||||
void WindUtils::to2PI(double* a)
|
||||
double WindUtils::to2PI(double a)
|
||||
{
|
||||
while (*a < 0) {
|
||||
*a += 2 * M_PI;
|
||||
a = fmod(a, 2 * M_PI);
|
||||
if (a < 0.0) {
|
||||
a += 2 * M_PI;
|
||||
}
|
||||
*a = fmod(*a, 2 * M_PI);
|
||||
return a;
|
||||
}
|
||||
|
||||
void WindUtils::toPI(double* a)
|
||||
double WindUtils::toPI(double a)
|
||||
{
|
||||
*a += M_PI;
|
||||
to2PI(a);
|
||||
*a -= M_PI;
|
||||
a += M_PI;
|
||||
a = to2PI(a);
|
||||
a -= M_PI;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void WindUtils::to360(double* a)
|
||||
double WindUtils::to360(double a)
|
||||
{
|
||||
while (*a < 0) {
|
||||
*a += 360;
|
||||
a = fmod(a, 360);
|
||||
if (a < 0.0) {
|
||||
a += 360;
|
||||
}
|
||||
*a = fmod(*a, 360);
|
||||
return a;
|
||||
}
|
||||
|
||||
void WindUtils::to180(double* a)
|
||||
double WindUtils::to180(double a)
|
||||
{
|
||||
*a += 180;
|
||||
to360(a);
|
||||
*a -= 180;
|
||||
a += 180;
|
||||
a = to360(a);
|
||||
a -= 180;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void WindUtils::toCart(const double* phi, const double* r, double* x, double* y)
|
||||
{
|
||||
*x = *r * sin(radians(*phi));
|
||||
*y = *r * cos(radians(*phi));
|
||||
*x = *r * sin(*phi);
|
||||
*y = *r * cos(*phi);
|
||||
}
|
||||
|
||||
void WindUtils::toPol(const double* x, const double* y, double* phi, double* r)
|
||||
{
|
||||
*phi = 90 - degrees(atan2(*y, *x));
|
||||
to360(phi);
|
||||
*phi = (M_PI / 2) - atan2(*y, *x);
|
||||
*phi = to2PI(*phi);
|
||||
*r = sqrt(*x * *x + *y * *y);
|
||||
}
|
||||
|
||||
|
@ -57,80 +63,96 @@ void WindUtils::addPolar(const double* phi1, const double* r1,
|
|||
|
||||
void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
|
||||
const double* CTW, const double* STW, const double* HDT,
|
||||
double* TWD, double* TWS)
|
||||
double* TWD, double* TWS, double* TWA)
|
||||
{
|
||||
double AWD = *AWA + *HDT;
|
||||
double awd = *AWA + *HDT;
|
||||
awd = to2PI(awd);
|
||||
double stw = -*STW;
|
||||
Serial.println("calcTwdSA: AWA: " + String(*AWA) + ", AWS: " + String(*AWS) + ", CTW: " + String(*CTW) + ", STW: " + String(*STW) + ", HDT: " + String(*HDT));
|
||||
addPolar(&AWD, AWS, CTW, &stw, TWD, TWS);
|
||||
// Serial.println("\ncalcTwdSA: AWA: " + String(*AWA) + ", AWS: " + String(*AWS) + ", CTW: " + String(*CTW) + ", STW: " + String(*STW) + ", HDT: " + String(*HDT));
|
||||
addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
|
||||
|
||||
// Normalize TWD to 0-360°
|
||||
while (*TWD < 0)
|
||||
*TWD += 360;
|
||||
while (*TWD >= 360)
|
||||
*TWD -= 360;
|
||||
|
||||
Serial.println("calcTwdSA: TWD: " + String(*TWD) + ", TWS: " + String(*TWS));
|
||||
// Normalize TWD and TWA to 0-360°
|
||||
*TWD = to2PI(*TWD);
|
||||
*TWA = toPI(*TWD - *HDT);
|
||||
// Serial.println("calcTwdSA: TWD: " + String(*TWD) + ", TWS: " + String(*TWS));
|
||||
}
|
||||
|
||||
bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
|
||||
const double* cogVal, const double* stwVal, const double* hdtVal,
|
||||
const double* hdmVal, double* twdVal, double* twsVal)
|
||||
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 hdt, ctw;
|
||||
double hdmVar = 3.0; // Magnetic declination, can be set from config if needed
|
||||
double twd, tws;
|
||||
double stw, hdt, ctw;
|
||||
double twd, tws, twa;
|
||||
static const double DBL_MIN = std::numeric_limits<double>::lowest();
|
||||
|
||||
if (*hdtVal == __DBL_MIN__) {
|
||||
if (*hdmVal != __DBL_MIN__) {
|
||||
hdt = *hdmVal + hdmVar; // Use corrected HDM if HDT is not available
|
||||
if (*hdtVal != DBL_MIN) {
|
||||
hdt = *hdtVal; // Use HDT if available
|
||||
} else {
|
||||
if (*hdmVal != DBL_MIN && *varVal != DBL_MIN) {
|
||||
hdt = *hdmVal + *varVal; // Use corrected HDM if HDT is not available
|
||||
hdt = to2PI(hdt);
|
||||
} else if (*cogVal != DBL_MIN) {
|
||||
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available
|
||||
} else {
|
||||
return false; // Cannot calculate without valid HDT or HDM
|
||||
}
|
||||
}
|
||||
ctw = *hdtVal + ((*cogVal - *hdtVal) / 2); // Estimate CTW from COG
|
||||
|
||||
if ((*awaVal == __DBL_MIN__) || (*awsVal == __DBL_MIN__) || (*cogVal == __DBL_MIN__) || (*stwVal == __DBL_MIN__)) {
|
||||
if (*cogVal != DBL_MIN) {
|
||||
ctw = *cogVal; // Use COG as CTW if available
|
||||
// ctw = *cogVal + ((*cogVal - hdt) / 2); // Estimate CTW from COG
|
||||
} else {
|
||||
ctw = hdt; // 2nd approximation for CTW;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*stwVal != DBL_MIN) {
|
||||
stw = *stwVal; // Use STW if available
|
||||
} else if (*sogVal != DBL_MIN) {
|
||||
stw = *sogVal;
|
||||
} else {
|
||||
// If STW and SOG are not available, we cannot calculate true wind
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((*awaVal == DBL_MIN) || (*awsVal == DBL_MIN) || (*cogVal == DBL_MIN) || (*stwVal == DBL_MIN)) {
|
||||
// Cannot calculate true wind without valid AWA, AWS, COG, or STW
|
||||
return false;
|
||||
} else {
|
||||
calcTwdSA(awaVal, awsVal, cogVal, stwVal, hdtVal, &twd, &tws);
|
||||
calcTwdSA(awaVal, awsVal, &ctw, stwVal, &hdt, &twd, &tws, &twa);
|
||||
*twdVal = twd;
|
||||
*twsVal = tws;
|
||||
*twaVal = twa;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// make function available in Python for testing
|
||||
static PyObject* true_wind(PyObject* self, PyObject* args) {
|
||||
double AWA,AWS,CTW,STW,HDT,TWS,TWD;
|
||||
if (!PyArg_ParseTuple(args, "ddddd", &AWA, &AWS, &CTW, &STW, &HDT)) {
|
||||
return NULL;
|
||||
void HstryBuf::fillWndBufSimData(tBoatHstryData& hstryBufs)
|
||||
// Fill most part of TWD and TWS history buffer with simulated data
|
||||
{
|
||||
double value = 20.0;
|
||||
int16_t value2 = 0;
|
||||
for (int i = 0; i < 900; i++) {
|
||||
value += random(-20, 20);
|
||||
value = WindUtils::to360(value);
|
||||
value2 = static_cast<int16_t>(value * DEG_TO_RAD * 1000);
|
||||
hstryBufs.twdHstry->add(value2);
|
||||
}
|
||||
|
||||
calc_true_wind(&AWA, &AWS, &CTW, &STW, &HDT, &TWD, &TWS);
|
||||
|
||||
PyObject* twd = PyFloat_FromDouble(TWD);
|
||||
PyObject* tws = PyFloat_FromDouble(TWS);
|
||||
PyObject* tw = PyTuple_Pack(2,twd,tws);
|
||||
return tw;
|
||||
}
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
{"true_wind", true_wind, METH_VARARGS, NULL},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
/* double genTwdSimDat()
|
||||
{
|
||||
simTwd += random(-20, 20);
|
||||
if (simTwd < 0.0)
|
||||
simTwd += 360.0;
|
||||
if (simTwd >= 360.0)
|
||||
simTwd -= 360.0;
|
||||
|
||||
static struct PyModuleDef module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"truewind", // Module name
|
||||
NULL, // Optional docstring
|
||||
-1,
|
||||
methods
|
||||
};
|
||||
int16_t z = static_cast<int16_t>(DegToRad(simTwd) * 1000.0);
|
||||
pageData.boatHstry.twdHstry->add(z); // Fill the buffer with some test data
|
||||
|
||||
PyMODINIT_FUNC PyInit_truewind(void) {
|
||||
return PyModule_Create(&module);
|
||||
simTws += random(-200, 150) / 10.0; // TWS value in knots
|
||||
simTws = constrain(simTws, 0.0f, 50.0f); // Ensure TWS is between 0 and 50 knots
|
||||
twsValue = simTws;
|
||||
}*/
|
|
@ -1,48 +1,36 @@
|
|||
#pragma once
|
||||
// #include <Python.h>
|
||||
#include "GwApi.h"
|
||||
#include "OBPRingBuffer.h"
|
||||
#include <Arduino.h>
|
||||
#include <math.h>
|
||||
|
||||
// #define radians(a) (a*0.017453292519943295)
|
||||
// #define degrees(a) (a*57.29577951308232)
|
||||
typedef struct {
|
||||
RingBuffer<int16_t>* twdHstry;
|
||||
RingBuffer<int16_t>* twsHstry;
|
||||
} tBoatHstryData; // Holds pointers to all history buffers for boat data
|
||||
|
||||
class HstryBuf {
|
||||
|
||||
public:
|
||||
void fillWndBufSimData(tBoatHstryData& hstryBufs); // Fill most part of the TWD and TWS history buffer with simulated data
|
||||
};
|
||||
|
||||
class WindUtils {
|
||||
|
||||
public:
|
||||
static void to360(double* a);
|
||||
static void to180(double* a);
|
||||
static void to2PI(double* a);
|
||||
static void toPI(double* a);
|
||||
static double to2PI(double a);
|
||||
static double toPI(double a);
|
||||
static double to360(double a);
|
||||
static double to180(double a);
|
||||
static void toCart(const double* phi, const double* r, double* x, double* y);
|
||||
static void toPol(const double* x, const double* y, double* phi, double* r);
|
||||
static void addPolar(const double* phi1, const double* r1,
|
||||
const double* phi2, const double* r2,
|
||||
double* phi, double* r);
|
||||
static bool calcTrueWind(const double* awaVal, const double* awsVal,
|
||||
const double* cogVal, const double* stwVal, const double* hdtVal,
|
||||
const double* hdmVal, double* twdVal, double* twsVal);
|
||||
static void calcTwdSA(const double* AWA, const double* AWS,
|
||||
const double* CTW, const double* STW, const double* HDT,
|
||||
double* TWD, double* TWS);
|
||||
double* TWD, double* TWS, double* TWA);
|
||||
static bool calcTrueWind(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);
|
||||
};
|
||||
|
||||
/*
|
||||
// make function available in Python for testing
|
||||
static PyObject* true_wind(PyObject* self, PyObject* args);
|
||||
static PyMethodDef methods[] = {
|
||||
{"true_wind", true_wind, METH_VARARGS, NULL},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
static struct PyModuleDef module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"truewind", // Module name
|
||||
NULL, // Optional docstring
|
||||
-1,
|
||||
methods
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC PyInit_truewind(void) {
|
||||
return PyModule_Create(&module);
|
||||
} */
|
|
@ -31,6 +31,7 @@ public:
|
|||
RingBuffer(size_t size);
|
||||
void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
|
||||
bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
|
||||
String getName() const; // Get buffer name
|
||||
void add(const T& value); // Add a new value to buffer
|
||||
T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest)
|
||||
T getFirst() const; // Get the first (oldest) value in buffer
|
||||
|
|
|
@ -57,6 +57,13 @@ bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequen
|
|||
return true;
|
||||
}
|
||||
|
||||
// Get buffer name
|
||||
template <typename T>
|
||||
String RingBuffer<T>::getName() const
|
||||
{
|
||||
return dataName;
|
||||
}
|
||||
|
||||
// Add a new value to buffer
|
||||
template <typename T>
|
||||
void RingBuffer<T>::add(const T& value)
|
||||
|
@ -129,9 +136,9 @@ T RingBuffer<T>::getMin() const
|
|||
return MIN_VAL;
|
||||
}
|
||||
|
||||
T minVal = getFirst();
|
||||
T minVal = MAX_VAL;
|
||||
T value;
|
||||
for (size_t i = 1; i < count; i++) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
value = get(i);
|
||||
if (value < minVal && value != MIN_VAL) {
|
||||
minVal = value;
|
||||
|
@ -150,7 +157,7 @@ T RingBuffer<T>::getMin(size_t amount) const
|
|||
if (amount > count)
|
||||
amount = count;
|
||||
|
||||
T minVal = getLast();
|
||||
T minVal = MAX_VAL;
|
||||
T value;
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
value = get(count - 1 - i);
|
||||
|
@ -169,9 +176,9 @@ T RingBuffer<T>::getMax() const
|
|||
return MIN_VAL;
|
||||
}
|
||||
|
||||
T maxVal = getFirst();
|
||||
T maxVal = MIN_VAL;
|
||||
T value;
|
||||
for (size_t i = 1; i < count; i++) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
value = get(i);
|
||||
if (value > maxVal && value != MIN_VAL) {
|
||||
maxVal = value;
|
||||
|
@ -190,7 +197,7 @@ T RingBuffer<T>::getMax(size_t amount) const
|
|||
if (amount > count)
|
||||
amount = count;
|
||||
|
||||
T maxVal = getLast();
|
||||
T maxVal = MIN_VAL;
|
||||
T value;
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
value = get(count - 1 - i);
|
||||
|
@ -328,7 +335,7 @@ bool RingBuffer<T>::isFull() const
|
|||
return is_Full;
|
||||
}
|
||||
|
||||
// Get lowest possible value for buffer; used for initialized buffer data
|
||||
// Get lowest possible value for buffer; used for non-set buffer data
|
||||
template <typename T>
|
||||
T RingBuffer<T>::getMinVal() const
|
||||
{
|
||||
|
|
|
@ -4,20 +4,17 @@
|
|||
#include "OBP60Extensions.h"
|
||||
#include "OBPRingBuffer.h"
|
||||
#include "Pagedata.h"
|
||||
#include <N2kMessages.h> // just for RadToDeg function
|
||||
#include <vector>
|
||||
|
||||
static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
|
||||
// #define radians(a) (a * 0.017453292519943295)
|
||||
// #define degrees(a) (a * 57.29577951308232)
|
||||
|
||||
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart
|
||||
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
|
||||
{
|
||||
int minVal = windDirHstry.getMinVal();
|
||||
size_t count = windDirHstry.getCurrentSize();
|
||||
// size_t capacity = windDirHstry.getCapacity();
|
||||
// size_t last = windDirHstry.getLastIdx();
|
||||
// size_t capacity = windDirHstry.getCapacity();
|
||||
// size_t last = windDirHstry.getLastIdx();
|
||||
|
||||
if (windDirHstry.isEmpty() || amount <= 0) {
|
||||
return minVal;
|
||||
|
@ -30,7 +27,7 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
|
|||
int maxRng = minVal;
|
||||
// Start from the newest value (last) and go backwards x times
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
// value = windDirHstry.get(((last - i) % capacity + capacity) % capacity);
|
||||
// value = windDirHstry.get(((last - i) % capacity + capacity) % capacity);
|
||||
value = windDirHstry.get(count - 1 - i);
|
||||
|
||||
if (value == minVal) {
|
||||
|
@ -49,43 +46,6 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
|
|||
return maxRng;
|
||||
}
|
||||
|
||||
void fillSimData(PageData& pageData)
|
||||
// Fill part of the TWD history buffer with simulated data
|
||||
{
|
||||
int value = 20;
|
||||
int16_t value2 = 0;
|
||||
for (int i = 0; i < 900; i++) {
|
||||
value += random(-20, 20);
|
||||
if (value < 0)
|
||||
value += 360;
|
||||
if (value >= 360)
|
||||
value -= 360;
|
||||
value2 = static_cast<int16_t>(DegToRad(value) * 1000.0);
|
||||
pageData.boatHstry.twdHstry->add(value2);
|
||||
}
|
||||
}
|
||||
|
||||
void fillTstBuffer(PageData& pageData)
|
||||
{
|
||||
float value = 0;
|
||||
int value2 = 0;
|
||||
for (int i = 0; i < 60; i++) {
|
||||
pageData.boatHstry.twdHstry->add(-10); // -> irregular data
|
||||
}
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 20; j++) {
|
||||
value += 10;
|
||||
value2 = static_cast<int>(DegToRad(value) * 1000.0);
|
||||
pageData.boatHstry.twdHstry->add(value2);
|
||||
}
|
||||
for (int j = 0; j < 20; j++) {
|
||||
value -= 10;
|
||||
value2 = static_cast<int>(DegToRad(value) * 1000.0);
|
||||
pageData.boatHstry.twdHstry->add(value2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
class PageWindPlot : public Page {
|
||||
|
||||
|
@ -105,7 +65,7 @@ public:
|
|||
virtual void setupKeys()
|
||||
{
|
||||
Page::setupKeys();
|
||||
commonData->keydata[0].label = "MODE";
|
||||
// commonData->keydata[0].label = "MODE";
|
||||
commonData->keydata[1].label = "INTV";
|
||||
commonData->keydata[4].label = "TWS";
|
||||
}
|
||||
|
@ -113,7 +73,7 @@ public:
|
|||
// Key functions
|
||||
virtual int handleKey(int key)
|
||||
{
|
||||
// Set chart mode TWD | TWS
|
||||
// Set chart mode TWD | TWS -> to be implemented
|
||||
if (key == 1) {
|
||||
if (chrtMode == 'D') {
|
||||
chrtMode = 'S';
|
||||
|
@ -159,9 +119,10 @@ public:
|
|||
GwLog* logger = commonData->logger;
|
||||
|
||||
float twsValue; // TWS value in chart area
|
||||
String twdName, twdUnit; // TWD name and unit
|
||||
int updFreq; // Update frequency for TWD
|
||||
int16_t twdLowest, twdHighest; // TWD range
|
||||
static String twdName, twdUnit; // TWD name and unit
|
||||
static int updFreq; // Update frequency for TWD
|
||||
static int16_t twdLowest, twdHighest; // TWD range
|
||||
// static int16_t twdBufMinVal; // lowest possible twd buffer value; used for non-set data
|
||||
|
||||
static bool isInitialized = false; // Flag to indicate that page is initialized
|
||||
static bool wndDataValid = false; // Flag to indicate if wind data is valid
|
||||
|
@ -191,6 +152,7 @@ public:
|
|||
static int chrtRng; // Range of wind values from mid wind value to min/max wind value in degrees
|
||||
int diffRng; // Difference between mid and current wind value
|
||||
static const int dfltRng = 40; // Default range for chart
|
||||
int midWndDir; // New value for wndCenter after chart start / shift
|
||||
static int simTwd; // Simulation value for TWD
|
||||
static float simTws; // Simulation value for TWS
|
||||
|
||||
|
@ -201,7 +163,7 @@ public:
|
|||
static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
|
||||
|
||||
LOG_DEBUG(GwLog::LOG, "Display page WindPlot");
|
||||
unsigned long start = millis();
|
||||
unsigned long WndPlotStart = millis();
|
||||
|
||||
// Get config data
|
||||
simulation = config->getBool(config->useSimuData);
|
||||
|
@ -210,11 +172,6 @@ public:
|
|||
String backlightMode = config->getString(config->backlight);
|
||||
|
||||
if (!isInitialized) {
|
||||
|
||||
if (simulation) {
|
||||
fillSimData(pageData); // Fill the buffer with some test data
|
||||
}
|
||||
|
||||
width = getdisplay().width();
|
||||
height = getdisplay().height();
|
||||
xCenter = width / 2;
|
||||
|
@ -230,8 +187,33 @@ public:
|
|||
lastAddedIdx = pageData.boatHstry.twdHstry->getLastIdx();
|
||||
pageData.boatHstry.twdHstry->getMetaData(twdName, twdUnit, updFreq, twdLowest, twdHighest);
|
||||
wndCenter = INT_MIN;
|
||||
midWndDir = 0;
|
||||
diffRng = dfltRng;
|
||||
chrtRng = dfltRng;
|
||||
|
||||
isInitialized = true; // Set flag to indicate that page is now initialized
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot Start1: lastAddedIdx: %d, simTwd: %.1f, isInitialized: %d, SimData: %d", lastAddedIdx, simTwd / 1000 + radToDeg, isInitialized, simulation);
|
||||
}
|
||||
|
||||
const int numBoatData = 2;
|
||||
GwApi::BoatValue* bvalue;
|
||||
String DataName[numBoatData];
|
||||
double DataValue[numBoatData];
|
||||
bool DataValid[numBoatData];
|
||||
String DataText[numBoatData];
|
||||
String DataUnit[numBoatData];
|
||||
String DataFormat[numBoatData];
|
||||
|
||||
// read boat data values; TWD only for validation test, TWS for display of current value
|
||||
for (int i = 0; i < numBoatData; i++) {
|
||||
bvalue = pageData.values[i];
|
||||
DataName[i] = xdrDelete(bvalue->getName());
|
||||
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
|
||||
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
|
||||
DataValue[i] = bvalue->value; // Value as double in SI unit
|
||||
DataValid[i] = bvalue->valid;
|
||||
DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||
DataUnit[i] = formatValue(bvalue, *commonData).unit;
|
||||
DataFormat[i] = bvalue->getFormat(); // Unit of value
|
||||
}
|
||||
|
||||
// Optical warning by limit violation (unused)
|
||||
|
@ -240,30 +222,11 @@ public:
|
|||
setFlashLED(false);
|
||||
}
|
||||
|
||||
if (simulation) {
|
||||
simTwd += random(-20, 20);
|
||||
if (simTwd < 0.0)
|
||||
simTwd += 360.0;
|
||||
if (simTwd >= 360.0)
|
||||
simTwd -= 360.0;
|
||||
|
||||
int16_t z = static_cast<int16_t>(DegToRad(simTwd) * 1000.0);
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot Simulation: getLast TWD: %.0f, lastIdx: %d", pageData.boatHstry.twdHstry->getLast() / 1000.0 * RAD_TO_DEG, pageData.boatHstry.twdHstry->getLastIdx());
|
||||
pageData.boatHstry.twdHstry->add(z); // Fill the buffer with some test data
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot Simulation: getAdded TWD: %.0f, lastIdx: %d", pageData.boatHstry.twdHstry->getLast() / 1000.0 * RAD_TO_DEG, pageData.boatHstry.twdHstry->getLastIdx());
|
||||
|
||||
simTws += random(-200, 150) / 10.0; // TWS value in knots
|
||||
simTws = constrain(simTws, 0.0f, 50.0f); // Ensure TWS is between 0 and 50 knots
|
||||
twsValue = simTws;
|
||||
} else {
|
||||
twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots
|
||||
}
|
||||
|
||||
// Identify buffer size and buffer start position for chart
|
||||
count = pageData.boatHstry.twdHstry->getCurrentSize();
|
||||
currIdx = pageData.boatHstry.twdHstry->getLastIdx();
|
||||
numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
|
||||
if (dataIntv != oldDataIntv) {
|
||||
if (dataIntv != oldDataIntv || count == 1) {
|
||||
// new data interval selected by user
|
||||
intvBufSize = cHeight * dataIntv;
|
||||
numWndVals = min(count, (cHeight - 60) * dataIntv);
|
||||
|
@ -277,16 +240,21 @@ public:
|
|||
bufStart = max(0, bufStart - numAddedBufVals);
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot Dataset: TWD: %.0f, TWS: %.1f, DBT: %.1f, count: %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d",
|
||||
pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, pageData.boatHstry.dbtHstry->getLast() / 10.0,
|
||||
count, intvBufSize, numWndVals, bufStart, numAddedBufVals, pageData.boatHstry.twdHstry->getLastIdx());
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot Dataset: count: %d, TWD: %.0f, TWS: %.1f, TWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, old: %d, act: %d",
|
||||
count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, DataValid[0],
|
||||
intvBufSize, numWndVals, bufStart, numAddedBufVals, pageData.boatHstry.twdHstry->getLastIdx(), oldDataIntv, dataIntv);
|
||||
|
||||
// initialize chart range values
|
||||
if (wndCenter == INT_MIN) {
|
||||
wndCenter = max(0, int(pageData.boatHstry.twdHstry->get(numWndVals - intvBufSize) / 1000.0 * radToDeg)); // get 1st value of current data interval
|
||||
wndCenter = (int((wndCenter + (wndCenter >= 0 ? 5 : -5)) / 10) * 10) % 360; // Set new center value; round to nearest 10 degree value; 360° -> 0°
|
||||
diffRng = dfltRng;
|
||||
chrtRng = dfltRng;
|
||||
// Set wndCenter from 1st real buffer value
|
||||
if (wndCenter == INT_MIN || (wndCenter == 0 && count == 1)) {
|
||||
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals);
|
||||
if (midWndDir != INT16_MIN) {
|
||||
midWndDir = midWndDir / 1000.0 * radToDeg;
|
||||
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
|
||||
} else {
|
||||
wndCenter = 0;
|
||||
}
|
||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, TWD: %.0f, wndCenter: %d, diffRng: %d, chrtRng: %d", count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg,
|
||||
wndCenter, diffRng, chrtRng);
|
||||
} else {
|
||||
// check and adjust range between left, center, and right chart limit
|
||||
diffRng = getRng(*pageData.boatHstry.twdHstry, wndCenter, numWndVals);
|
||||
|
@ -304,8 +272,6 @@ public:
|
|||
wndRight = (chrtRng < 180 ? wndCenter + chrtRng : wndCenter + chrtRng - 1);
|
||||
if (wndRight >= 360)
|
||||
wndRight -= 360;
|
||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FirstVal: %f, LastVal: %d, count: %d, diffRng: %d, chartRng: %d, Center: %d, scale: %f", pageData.boatHstry.twdHstry->getFirst() / 1000.0 * radToDeg,
|
||||
pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, count, diffRng, chrtRng, wndCenter, chrtScl);
|
||||
|
||||
// Draw page
|
||||
//***********************************************************************
|
||||
|
@ -322,11 +288,9 @@ public:
|
|||
char sWndLbl[4]; // char buffer for Wind angle label
|
||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||
getdisplay().setCursor(xCenter - 88, yOffset - 3);
|
||||
getdisplay().print("TWD"); // Wind name
|
||||
// getdisplay().setCursor(xCenter - 20, yOffset - 3);
|
||||
getdisplay().print("TWD"); // Wind data name
|
||||
snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
|
||||
drawTextCenter(xCenter, yOffset - 11, sWndLbl);
|
||||
// getdisplay().print(sWndLbl); // Wind center value
|
||||
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
|
||||
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
|
||||
getdisplay().setCursor(1, yOffset - 3);
|
||||
|
@ -343,7 +307,19 @@ public:
|
|||
if (pageData.boatHstry.twdHstry->getMax() == pageData.boatHstry.twdHstry->getMinVal()) {
|
||||
// only <INT16_MIN> values in buffer -> no valid wind data available
|
||||
wndDataValid = false;
|
||||
} else if (!DataValid[0]) {
|
||||
// currently no valid TWD data available
|
||||
numNoData++;
|
||||
wndDataValid = true;
|
||||
if (numNoData > 3) {
|
||||
// If more than 4 invalid values in a row, send message
|
||||
wndDataValid = false;
|
||||
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||
getdisplay().fillRect(xCenter - 66, height / 2 - 20, 146, 24, commonData->bgcolor); // Clear area for TWS value
|
||||
drawTextCenter(xCenter, height / 2 - 10, "No sensor data");
|
||||
}
|
||||
} else {
|
||||
numNoData = 0; // reset data error counter
|
||||
wndDataValid = true; // At least some wind data available
|
||||
}
|
||||
// Draw wind values in chart
|
||||
|
@ -361,17 +337,17 @@ public:
|
|||
}
|
||||
if (numNoData > 4) {
|
||||
// If more than 4 invalid values in a row, send message
|
||||
getdisplay().setFont(&Ubuntu_Bold10pt7b);
|
||||
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||
getdisplay().fillRect(xCenter - 66, height / 2 - 20, 146, 24, commonData->bgcolor); // Clear area for TWS value
|
||||
drawTextCenter(xCenter, height / 2 - 10, "No sensor data");
|
||||
} */
|
||||
} else {
|
||||
chrtVal = (chrtVal / 1000.0 * radToDeg) + 0.5; // Convert to degrees and round
|
||||
chrtVal = static_cast<int>((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round
|
||||
x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
|
||||
y = yOffset + cHeight - i; // Position in chart area
|
||||
|
||||
if (i >= (numWndVals / dataIntv) - 10)
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
|
||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
|
||||
|
||||
if ((i == 0) || (chrtPrevVal == INT16_MIN)) {
|
||||
// just a dot for 1st chart point or after some invalid values
|
||||
|
@ -404,15 +380,15 @@ public:
|
|||
|
||||
int minWndDir = pageData.boatHstry.twdHstry->getMin(numWndVals) / 1000.0 * radToDeg;
|
||||
int maxWndDir = pageData.boatHstry.twdHstry->getMax(numWndVals) / 1000.0 * radToDeg;
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter);
|
||||
if ((minWndDir > wndCenter) || (maxWndDir < wndCenter)) {
|
||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter);
|
||||
if ((minWndDir + 540 >= wndCenter + 540) || (maxWndDir + 540 <= wndCenter + 540)) {
|
||||
// Check if all wind value are left or right of center value -> optimize chart range
|
||||
int midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals) / 1000.0 * radToDeg;
|
||||
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals) / 1000.0 * radToDeg;
|
||||
if (midWndDir != INT16_MIN) {
|
||||
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot FreeTop: cHeight: %d, bufStart: %d, numWndVals: %d, wndCenter: %d", cHeight, bufStart, numWndVals, wndCenter);
|
||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: cHeight: %d, bufStart: %d, numWndVals: %d, wndCenter: %d", cHeight, bufStart, numWndVals, wndCenter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -433,6 +409,8 @@ public:
|
|||
int xPosTws;
|
||||
static const int yPosTws = yOffset + 40;
|
||||
|
||||
twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots
|
||||
|
||||
xPosTws = flipTws ? 20 : width - 138;
|
||||
currentZone = (y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146) ? 1 : 0; // Define current zone for TWS value
|
||||
if (currentZone != lastZone) {
|
||||
|
@ -447,21 +425,24 @@ public:
|
|||
getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
|
||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
||||
getdisplay().setCursor(xPosTws, yPosTws);
|
||||
if (twsValue < 0 || twsValue >= 100) {
|
||||
if (!DataValid[1]) {
|
||||
getdisplay().print("--.-");
|
||||
} else {
|
||||
if (twsValue < 10.0) {
|
||||
getdisplay().printf("!%3.1f", twsValue); // Value
|
||||
if (DataValue[1] < 9.95) {
|
||||
getdisplay().printf("!%3.1f", DataValue[1] + 0.05); // Value, round to 1 decimal
|
||||
} else {
|
||||
getdisplay().printf("%4.1f", twsValue); // Value}
|
||||
getdisplay().printf("%4.1f", DataValue[1] + 0.05); // Value, round to 1 decimal
|
||||
}
|
||||
}
|
||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||
getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
|
||||
getdisplay().print("TWS"); // Name
|
||||
// getdisplay().print("TWS"); // Name
|
||||
getdisplay().print(DataName[1]); // Name
|
||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||
getdisplay().setCursor(xPosTws + 78, yPosTws + 1);
|
||||
getdisplay().printf(" kn"); // Unit
|
||||
// getdisplay().setCursor(xPosTws + 78, yPosTws + 1);
|
||||
getdisplay().setCursor(xPosTws + 82, yPosTws + 1);
|
||||
// getdisplay().printf(" kn"); // Unit
|
||||
getdisplay().print(DataUnit[1]); // Unit
|
||||
}
|
||||
|
||||
// chart Y axis labels; print at last to overwrite potential chart lines in label area
|
||||
|
@ -471,7 +452,7 @@ public:
|
|||
for (int i = 1; i <= 3; i++) {
|
||||
yPos = yOffset + (i * 60);
|
||||
getdisplay().fillRect(0, yPos, width, 1, commonData->fgcolor);
|
||||
getdisplay().fillRect(0, yPos - 8, 26, 16, commonData->bgcolor); // Clear small area to remove potential chart lines
|
||||
getdisplay().fillRect(0, yPos - 8, 24, 16, commonData->bgcolor); // Clear small area to remove potential chart lines
|
||||
getdisplay().setCursor(1, yPos + 4);
|
||||
if (count >= intvBufSize) {
|
||||
// Calculate minute value for label
|
||||
|
@ -483,8 +464,8 @@ public:
|
|||
getdisplay().printf("%3d", chrtLbl); // Wind value label
|
||||
}
|
||||
|
||||
unsigned long finish = millis() - start;
|
||||
// LOG_DEBUG(GwLog::ERROR, "PageWindPlot Time: %lu", finish);
|
||||
unsigned long finish = millis() - WndPlotStart;
|
||||
LOG_DEBUG(GwLog::ERROR, "PageWindPlot Time: %lu", finish);
|
||||
// Update display
|
||||
getdisplay().nextPage(); // Partial update (fast)
|
||||
};
|
||||
|
@ -505,8 +486,8 @@ PageDescription registerPageWindPlot(
|
|||
"WindPlot", // Page name
|
||||
createPage, // Action
|
||||
0, // Number of bus values depends on selection in Web configuration
|
||||
// { "TWD", "TWA", "TWS", "HDM", "AWA", "AWS", "STW", "COG", "SOG" }, // Bus values we need in the page
|
||||
{ }, // Bus values we need in the page
|
||||
{ "TWD", "TWS" }, // Bus values we need in the page
|
||||
// {}, // Bus values we need in the page
|
||||
true // Show display header on/off
|
||||
);
|
||||
|
||||
|
|
|
@ -5,15 +5,10 @@
|
|||
#include <vector>
|
||||
#include "LedSpiTask.h"
|
||||
#include "OBPRingBuffer.h"
|
||||
#include "OBPDataOperations.h"
|
||||
|
||||
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
|
||||
|
||||
typedef struct{
|
||||
RingBuffer<int16_t>* twdHstry;
|
||||
RingBuffer<int16_t>* twsHstry;
|
||||
RingBuffer<int16_t>* dbtHstry;
|
||||
} tBoatHstryData;
|
||||
|
||||
typedef std::vector<GwApi::BoatValue *> ValueList;
|
||||
|
||||
typedef struct{
|
||||
|
|
|
@ -219,6 +219,17 @@
|
|||
"obp60":"true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "calcTrueWnds",
|
||||
"label": "Calculate True Wind",
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "If not available, calculate true wind data from appearant wind and other boat data",
|
||||
"category": "OBP60 Settings",
|
||||
"capabilities": {
|
||||
"obp60": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lengthFormat",
|
||||
"label": "Length Format",
|
||||
|
|
|
@ -219,6 +219,17 @@
|
|||
"obp40": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "calcTrueWnds",
|
||||
"label": "Calculate True Wind",
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "If not available, calculate true wind data from appearant wind and other boat data",
|
||||
"category": "OBP40 Settings",
|
||||
"capabilities": {
|
||||
"obp40": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lengthFormat",
|
||||
"label": "Length Format",
|
||||
|
|
|
@ -376,6 +376,112 @@ void underVoltageDetection(GwApi *api, CommonData &common){
|
|||
}
|
||||
}
|
||||
|
||||
//bool addTrueWind(GwApi* api, BoatValueList* boatValues, double *twd, double *tws, double *twa) {
|
||||
bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
|
||||
// Calculate true wind data and add to obp60task boat data list
|
||||
|
||||
double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
|
||||
bool isCalculated = false;
|
||||
const double DBL_MIN = std::numeric_limits<double>::lowest();
|
||||
|
||||
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate("TWD");
|
||||
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate("TWS");
|
||||
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
|
||||
GwApi::BoatValue *awaBVal = boatValues->findValueOrCreate("AWA");
|
||||
GwApi::BoatValue *awsBVal = boatValues->findValueOrCreate("AWS");
|
||||
GwApi::BoatValue *cogBVal = boatValues->findValueOrCreate("COG");
|
||||
GwApi::BoatValue *stwBVal = boatValues->findValueOrCreate("STW");
|
||||
GwApi::BoatValue *sogBVal = boatValues->findValueOrCreate("SOG");
|
||||
GwApi::BoatValue *hdtBVal = boatValues->findValueOrCreate("HDT");
|
||||
GwApi::BoatValue *hdmBVal = boatValues->findValueOrCreate("HDM");
|
||||
GwApi::BoatValue *varBVal = boatValues->findValueOrCreate("VAR");
|
||||
awaVal = awaBVal->valid ? awaBVal->value : DBL_MIN;
|
||||
awsVal = awsBVal->valid ? awsBVal->value : DBL_MIN;
|
||||
cogVal = cogBVal->valid ? cogBVal->value : DBL_MIN;
|
||||
stwVal = stwBVal->valid ? stwBVal->value : DBL_MIN;
|
||||
sogVal = sogBVal->valid ? sogBVal->value : DBL_MIN;
|
||||
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MIN;
|
||||
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MIN;
|
||||
varVal = varBVal->valid ? varBVal->value : DBL_MIN;
|
||||
api->getLogger()->logDebug(GwLog::ERROR,"obp60task addTrueWind: AWA: %.1f, AWS: %.1f, COG: %.1f, STW: %.1f, 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, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
|
||||
|
||||
isCalculated = WindUtils::calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twdBVal->value, &twsBVal->value, &twaBVal->value);
|
||||
twdBVal->valid = isCalculated;
|
||||
twsBVal->valid = isCalculated;
|
||||
twaBVal->valid = isCalculated;
|
||||
|
||||
api->getLogger()->logDebug(GwLog::ERROR,"obp60task calcTrueWind: TWD_Valid? %d, TWD=%.1f, TWS=%.1f, TWA=%.1f, isCalculated? %d", twdBVal->valid, twdBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852,
|
||||
twaBVal->value * RAD_TO_DEG, isCalculated);
|
||||
|
||||
return isCalculated;
|
||||
}
|
||||
|
||||
void initHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList) {
|
||||
// Init history buffers for TWD, TWS
|
||||
|
||||
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
|
||||
|
||||
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
|
||||
int hstryMinVal = 0; // Minimum value for these history buffers
|
||||
int twdHstryMax = 6283; // Max value for wind direction (TWD) in rad (0...2*PI), shifted by 1000 for 3 decimals
|
||||
int twsHstryMax = 1000; // Max value for wind speed (TWS) in m/s, shifted by 10 for 1 decimal
|
||||
// Initialize history buffers with meta data
|
||||
hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
|
||||
hstryBufList.twsHstry->setMetaData("TWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
|
||||
|
||||
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
|
||||
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
|
||||
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
|
||||
}
|
||||
|
||||
void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList) {
|
||||
// Handle history buffers for TWD, TWS
|
||||
|
||||
GwLog *logger = api->getLogger();
|
||||
|
||||
int16_t twdHstryMin = hstryBufList.twdHstry->getMinVal();
|
||||
int16_t twdHstryMax = hstryBufList.twdHstry->getMaxVal();
|
||||
int16_t twsHstryMin = hstryBufList.twsHstry->getMinVal();
|
||||
int16_t twsHstryMax = hstryBufList.twsHstry->getMaxVal();
|
||||
int16_t twdBuf, twsBuf;
|
||||
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
|
||||
|
||||
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
|
||||
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
|
||||
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
|
||||
|
||||
api->getLogger()->logDebug(GwLog::ERROR,"obp60task handleHstryBuf: twdBVal: %f, twsBVal: %f, twaBVal: %f, TWD_isValid? %d", twdBVal->value * RAD_TO_DEG,
|
||||
twsBVal->value * 3.6 / 1.852, twaBVal->value * RAD_TO_DEG, twdBVal->valid);
|
||||
calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values
|
||||
calBVal->setFormat(twdBVal->getFormat());
|
||||
if (twdBVal->valid) {
|
||||
calBVal->value = twdBVal->value;
|
||||
calBVal->valid = twdBVal->valid;
|
||||
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
||||
twdBuf = static_cast<int16_t>(std::round(calBVal->value * 1000));
|
||||
if (twdBuf >= twdHstryMin && twdBuf <= twdHstryMax) {
|
||||
hstryBufList.twdHstry->add(twdBuf);
|
||||
}
|
||||
}
|
||||
delete calBVal;
|
||||
calBVal = nullptr;
|
||||
|
||||
calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values
|
||||
calBVal->setFormat(twsBVal->getFormat());
|
||||
if (twsBVal->valid) {
|
||||
calBVal->value = twsBVal->value;
|
||||
calBVal->valid = twsBVal->valid;
|
||||
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
|
||||
twsBuf = static_cast<int16_t>(std::round(calBVal->value * 10));
|
||||
if (twsBuf >= twsHstryMin && twsBuf <= twsHstryMax) {
|
||||
hstryBufList.twsHstry->add(twsBuf);
|
||||
}
|
||||
}
|
||||
delete calBVal;
|
||||
calBVal = nullptr;
|
||||
}
|
||||
|
||||
// OBP60 Task
|
||||
//####################################################################################
|
||||
void OBP60Task(GwApi *api){
|
||||
|
@ -392,11 +498,6 @@ void OBP60Task(GwApi *api){
|
|||
commonData.logger=logger;
|
||||
commonData.config=config;
|
||||
|
||||
// Create ring buffers for history storage of some boat data
|
||||
RingBuffer<int16_t> twdHstry(960); // Circular buffer to store wind direction values; store 960 TWD values for 16 minutes history
|
||||
RingBuffer<int16_t> twsHstry(960); // Circular buffer to store wind speed values (TWS)
|
||||
RingBuffer<int16_t> dbtHstry(960); // Circular buffer to store water depth values (DBT)
|
||||
|
||||
#ifdef HARDWARE_V21
|
||||
// Keyboard coordinates for page footer
|
||||
initKeys(commonData);
|
||||
|
@ -494,6 +595,11 @@ void OBP60Task(GwApi *api){
|
|||
//commonData.distanceformat=config->getString(xxx);
|
||||
//add all necessary data to common data
|
||||
|
||||
// Create ring buffers for history storage of some boat data
|
||||
RingBuffer<int16_t> twdHstry(960); // Circular buffer to store wind direction values; store 960 TWD values for 16 minutes history
|
||||
RingBuffer<int16_t> twsHstry(960); // Circular buffer to store wind speed values (TWS)
|
||||
tBoatHstryData hstryBufList = {&twdHstry, &twsHstry};
|
||||
|
||||
//fill the page data from config
|
||||
numPages=config->getInt(config->visiblePages,1);
|
||||
if (numPages < 1) numPages=1;
|
||||
|
@ -532,8 +638,10 @@ void OBP60Task(GwApi *api){
|
|||
LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
|
||||
pages[i].parameters.values.push_back(value);
|
||||
}
|
||||
// Add boat history data to page parameters
|
||||
pages[i].parameters.boatHstry = {&twdHstry, &twsHstry, &dbtHstry};
|
||||
if (pages[i].description->pageName == "WindPlot") {
|
||||
// Add boat history data to page parameters
|
||||
pages[i].parameters.boatHstry = hstryBufList;
|
||||
}
|
||||
}
|
||||
// add out of band system page (always available)
|
||||
Page *syspage = allPages.pages[0]->creator(commonData);
|
||||
|
@ -541,32 +649,12 @@ void OBP60Task(GwApi *api){
|
|||
// Read all calibration data settings from config
|
||||
calibrationData.readConfig(config, logger);
|
||||
|
||||
// List of boat values for history storage
|
||||
GwApi::BoatValue *twdBVal=new GwApi::BoatValue(GwBoatData::_TWD);
|
||||
GwApi::BoatValue *twsBVal=new GwApi::BoatValue(GwBoatData::_TWS);
|
||||
GwApi::BoatValue *dbtBVal=new GwApi::BoatValue(GwBoatData::_DBT);
|
||||
GwApi::BoatValue *hstryValList[]={twdBVal, twsBVal, dbtBVal};
|
||||
api->getBoatDataValues(3, hstryValList);
|
||||
// Boat data history buffer initialization
|
||||
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
|
||||
int hstryMinVal = 0; // Minimum value for these history buffers
|
||||
int twdHstryMax = 6283; // Max value for wind direction (TWD) in rad (0...2*PI), shifted by 1000 for 3 decimals
|
||||
int twsHstryMax = 1000; // Max value for wind speed (TWS) in m/s, shifted by 10 for 1 decimal
|
||||
int dbtHstryMax = 3276; // Max value for depth in m (=327), shifted by 10 for 1 decimal
|
||||
// Initialize history buffers with meta data
|
||||
twdHstry.setMetaData(twdBVal->getName(), twdBVal->getFormat(), hstryUpdFreq, hstryMinVal, twdHstryMax);
|
||||
twsHstry.setMetaData(twsBVal->getName(), twsBVal->getFormat(), hstryUpdFreq, hstryMinVal, twsHstryMax);
|
||||
dbtHstry.setMetaData(dbtBVal->getName(), dbtBVal->getFormat(), hstryUpdFreq, hstryMinVal, dbtHstryMax);
|
||||
bool simulation = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
||||
// Check user setting for true wind calculation
|
||||
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
|
||||
// bool simulation = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
||||
|
||||
// List of boat values for true winds calculation
|
||||
GwApi::BoatValue *awaBVal=new GwApi::BoatValue(GwBoatData::_AWA);
|
||||
GwApi::BoatValue *awsBVal=new GwApi::BoatValue(GwBoatData::_AWS);
|
||||
GwApi::BoatValue *cogBVal=new GwApi::BoatValue(GwBoatData::_COG);
|
||||
GwApi::BoatValue *stwBVal=new GwApi::BoatValue(GwBoatData::_STW);
|
||||
GwApi::BoatValue *hdtBVal=new GwApi::BoatValue(GwBoatData::_HDT);
|
||||
GwApi::BoatValue *hdmBVal=new GwApi::BoatValue(GwBoatData::_HDM);
|
||||
GwApi::BoatValue *WndCalcValList[]={awaBVal, awsBVal, cogBVal, stwBVal, hdtBVal, hdmBVal};
|
||||
// Initialize history buffer for certain boat data
|
||||
initHstryBuf(api, &boatValues, hstryBufList);
|
||||
|
||||
// Display screenshot handler for HTTP request
|
||||
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||
|
@ -623,18 +711,6 @@ void OBP60Task(GwApi *api){
|
|||
GwApi::BoatValue *lon = boatValues.findValueOrCreate("LON"); // Load GpsLongitude
|
||||
GwApi::BoatValue *hdop = boatValues.findValueOrCreate("HDOP"); // Load GpsHDOP
|
||||
|
||||
/* // Boat values for wind conversion for main loop; will be calculated in case values are not available by sensors
|
||||
GwApi::BoatValue *twd = boatValues.findValueOrCreate("TWD"); // True Wind Direction
|
||||
GwApi::BoatValue *tws = boatValues.findValueOrCreate("TWS"); // True Wind Speed
|
||||
GwApi::BoatValue *twa = boatValues.findValueOrCreate("TWA"); // True Wind Angle
|
||||
GwApi::BoatValue *awaBVal = boatValues.findValueOrCreate("AWA"); // Apparent Wind Angle
|
||||
GwApi::BoatValue *awsBVal = boatValues.findValueOrCreate("AWS"); // Apparent Wind Speed
|
||||
GwApi::BoatValue *cogBVal = boatValues.findValueOrCreate("COG"); // Course Over Ground
|
||||
GwApi::BoatValue *stwBVal = boatValues.findValueOrCreate("STW"); // Speed Through Water
|
||||
GwApi::BoatValue *hdtBVal = boatValues.findValueOrCreate("HDT"); // Heading True
|
||||
GwApi::BoatValue *ctwBVal = boatValues.findValueOrCreate("CTW"); // Course Through Water
|
||||
GwApi::BoatValue *hdmBVal = boatValues.findValueOrCreate("HDM"); // Heading Magnetic
|
||||
*/
|
||||
LOG_DEBUG(GwLog::LOG,"obp60task: start mainloop");
|
||||
|
||||
commonData.time = boatValues.findValueOrCreate("GPST"); // Load GpsTime
|
||||
|
@ -648,7 +724,6 @@ void OBP60Task(GwApi *api){
|
|||
long starttime3 = millis(); // Display update all 1s
|
||||
long starttime4 = millis(); // Delayed display update after 4s when select a new page
|
||||
long starttime5 = millis(); // Calculate sunrise and sunset all 1s
|
||||
unsigned long starttime10 = millis(); // Get history TWD, TWS, DBT data and calculate true winds each 1s
|
||||
|
||||
pages[pageNumber].page->setupKeys(); // Initialize keys for first page
|
||||
|
||||
|
@ -838,60 +913,6 @@ void OBP60Task(GwApi *api){
|
|||
}
|
||||
}
|
||||
|
||||
// Read TWD, TWS, DBT data from boatData each 1000ms for history and windplot display
|
||||
if((millis() > starttime10 + 1000) && !simulation) {
|
||||
double twdVal, twsVal, dbtVal;
|
||||
double awaVal, awsVal, cogVal, stwVal, hdtVal, hdmVal;
|
||||
double DBL_MIN = std::numeric_limits<double>::lowest();
|
||||
|
||||
starttime10 = millis();
|
||||
LOG_DEBUG(GwLog::DEBUG,"History buffer write cycle");
|
||||
api->getBoatDataValues(3, hstryValList);
|
||||
|
||||
if (!twdBVal->valid || !twsBVal->valid) {
|
||||
api->getBoatDataValues(6, WndCalcValList); // Get all values for true wind calculation
|
||||
awaVal = awaBVal->valid ? awaBVal->value * RAD_TO_DEG : __DBL_MIN__;
|
||||
awsVal = awsBVal->valid ? awsBVal->value : __DBL_MIN__;
|
||||
cogVal = cogBVal->valid ? cogBVal->value * RAD_TO_DEG : __DBL_MIN__;
|
||||
stwVal = stwBVal->valid ? stwBVal->value : __DBL_MIN__;
|
||||
hdtVal = hdtBVal->valid ? hdtBVal->value * RAD_TO_DEG : __DBL_MIN__;
|
||||
hdmVal = hdmBVal->valid ? hdmBVal->value * RAD_TO_DEG : __DBL_MIN__;
|
||||
LOG_DEBUG(GwLog::ERROR,"obp60task - Read data: AWA: %f, AWS: %f, COG: %f, STW: %f, HDT: %f, TWD=%f, TWS=%f", awaBVal->value, awsBVal->value,
|
||||
cogBVal->value, stwBVal->value, hdtBVal->value, twdBVal->value, twsBVal->value);
|
||||
|
||||
bool isCalculated = WindUtils::calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &cogVal, &hdmVal, &twdVal, &twsVal); // Calculate true wind if TWD not available
|
||||
// bool isCalculated = WindUtils::calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &hdtVal, &hdmVal, &twdVal, &twsVal); // Calculate true wind if TWD not available
|
||||
LOG_DEBUG(GwLog::ERROR,"obp60task - calc Wind: AWA: %f, AWS: %f, COG: %f, STW: %f, HDT: %f, TWD=%f, TWS=%f, converted? %d", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
|
||||
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, twdVal, twsVal, isCalculated);
|
||||
}
|
||||
if (twdBVal->valid) {
|
||||
twdVal = std::round(twdBVal->value * 1000); // Shift value to store decimals in int16_t);
|
||||
if (twdVal < hstryMinVal || twdVal > twdHstryMax) {
|
||||
twdVal = INT16_MIN; // Add invalid value
|
||||
}
|
||||
}
|
||||
twdHstry.add(static_cast<int16_t>(twdVal));
|
||||
|
||||
if (twsBVal->valid) {
|
||||
twsVal = static_cast<int16_t>(twsBVal->value * 10); // Shift value to store decimals in int16_t
|
||||
if (twsVal < hstryMinVal || twsVal > twsHstryMax) {
|
||||
twsVal = INT16_MIN; // Add invalid value
|
||||
}
|
||||
}
|
||||
twsHstry.add(twsVal);
|
||||
|
||||
if (dbtBVal->valid) {
|
||||
dbtVal = dbtBVal->value * 10; // Shift value to store decimals in int16_t
|
||||
if (dbtVal < hstryMinVal || dbtVal > dbtHstryMax) {
|
||||
dbtVal = INT16_MIN; // Add invalid value
|
||||
}
|
||||
dbtHstry.add(dbtVal);
|
||||
}
|
||||
|
||||
int counttime = millis() - starttime10;
|
||||
LOG_DEBUG(GwLog::ERROR,"obp60task: History buffer write time: %d", counttime);
|
||||
}
|
||||
|
||||
// Refresh display data, default all 1s
|
||||
currentPage = pages[pageNumber].page;
|
||||
int pagetime = 1000;
|
||||
|
@ -907,6 +928,12 @@ void OBP60Task(GwApi *api){
|
|||
api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
|
||||
api->getStatus(commonData.status);
|
||||
|
||||
if (calcTrueWnds) {
|
||||
addTrueWind(api, &boatValues);
|
||||
}
|
||||
// Handle history buffers for TWD, TWS for wind plot page and other usage
|
||||
handleHstryBuf(api, &boatValues, hstryBufList);
|
||||
|
||||
// Clear display
|
||||
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
||||
getdisplay().fillScreen(commonData.bgcolor); // Clear display
|
||||
|
|
Loading…
Reference in New Issue