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"
 | 
					#include "OBPDataOperations.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WindUtils::to2PI(double* a)
 | 
					double WindUtils::to2PI(double a)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    while (*a < 0) {
 | 
					    a = fmod(a, 2 * M_PI);
 | 
				
			||||||
        *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;
 | 
					    a += M_PI;
 | 
				
			||||||
    to2PI(a);
 | 
					    a = to2PI(a);
 | 
				
			||||||
    *a -= M_PI;
 | 
					    a -= M_PI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return a;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WindUtils::to360(double* a)
 | 
					double WindUtils::to360(double a)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    while (*a < 0) {
 | 
					    a = fmod(a, 360);
 | 
				
			||||||
        *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;
 | 
					    a += 180;
 | 
				
			||||||
    to360(a);
 | 
					    a = to360(a);
 | 
				
			||||||
    *a -= 180;
 | 
					    a -= 180;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return a;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WindUtils::toCart(const double* phi, const double* r, double* x, double* y)
 | 
					void WindUtils::toCart(const double* phi, const double* r, double* x, double* y)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    *x = *r * sin(radians(*phi));
 | 
					    *x = *r * sin(*phi);
 | 
				
			||||||
    *y = *r * cos(radians(*phi));
 | 
					    *y = *r * cos(*phi);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WindUtils::toPol(const double* x, const double* y, double* phi, double* r)
 | 
					void WindUtils::toPol(const double* x, const double* y, double* phi, double* r)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    *phi = 90 - degrees(atan2(*y, *x));
 | 
					    *phi = (M_PI / 2) - atan2(*y, *x);
 | 
				
			||||||
    to360(phi);
 | 
					    *phi = to2PI(*phi);
 | 
				
			||||||
    *r = sqrt(*x * *x + *y * *y);
 | 
					    *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,
 | 
					void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
 | 
				
			||||||
    const double* CTW, const double* STW, const double* HDT,
 | 
					    const double* CTW, const double* STW, const double* HDT,
 | 
				
			||||||
    double* TWD, double* TWS)
 | 
					    double* TWD, double* TWS, double* TWA)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    double AWD = *AWA + *HDT;
 | 
					    double awd = *AWA + *HDT;
 | 
				
			||||||
 | 
					    awd = to2PI(awd);
 | 
				
			||||||
    double stw = -*STW;
 | 
					    double stw = -*STW;
 | 
				
			||||||
    Serial.println("calcTwdSA: AWA: " + String(*AWA) + ", AWS: " + String(*AWS) + ", CTW: " + String(*CTW) + ", STW: " + String(*STW) + ", HDT: " + String(*HDT));
 | 
					    //    Serial.println("\ncalcTwdSA: AWA: " + String(*AWA) + ", AWS: " + String(*AWS) + ", CTW: " + String(*CTW) + ", STW: " + String(*STW) + ", HDT: " + String(*HDT));
 | 
				
			||||||
    addPolar(&AWD, AWS, CTW, &stw, TWD, TWS);
 | 
					    addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Normalize TWD to 0-360°
 | 
					    // Normalize TWD and TWA to 0-360°
 | 
				
			||||||
    while (*TWD < 0)
 | 
					    *TWD = to2PI(*TWD);
 | 
				
			||||||
        *TWD += 360;
 | 
					    *TWA = toPI(*TWD - *HDT);
 | 
				
			||||||
    while (*TWD >= 360)
 | 
					    //    Serial.println("calcTwdSA: TWD: " + String(*TWD) + ", TWS: " + String(*TWS));
 | 
				
			||||||
        *TWD -= 360;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Serial.println("calcTwdSA: TWD: " + String(*TWD) + ", TWS: " + String(*TWS));
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
 | 
					bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
 | 
				
			||||||
    const double* cogVal, const double* stwVal, const double* hdtVal,
 | 
					    const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
 | 
				
			||||||
    const double* hdmVal, double* twdVal, double* twsVal)
 | 
					    const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    double hdt, ctw;
 | 
					    double stw, hdt, ctw;
 | 
				
			||||||
    double hdmVar = 3.0; // Magnetic declination, can be set from config if needed
 | 
					    double twd, tws, twa;
 | 
				
			||||||
    double twd, tws;
 | 
					    static const double DBL_MIN = std::numeric_limits<double>::lowest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (*hdtVal == __DBL_MIN__) {
 | 
					    if (*hdtVal != DBL_MIN) {
 | 
				
			||||||
        if (*hdmVal != __DBL_MIN__) {
 | 
					        hdt = *hdtVal; // Use HDT if available
 | 
				
			||||||
            hdt = *hdmVal + hdmVar; // Use corrected HDM if HDT is not 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 {
 | 
					        } else {
 | 
				
			||||||
            return false; // Cannot calculate without valid HDT or HDM
 | 
					            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
 | 
					        // Cannot calculate true wind without valid AWA, AWS, COG, or STW
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        calcTwdSA(awaVal, awsVal, cogVal, stwVal, hdtVal, &twd, &tws);
 | 
					        calcTwdSA(awaVal, awsVal, &ctw, stwVal, &hdt, &twd, &tws, &twa);
 | 
				
			||||||
        *twdVal = twd;
 | 
					        *twdVal = twd;
 | 
				
			||||||
        *twsVal = tws;
 | 
					        *twsVal = tws;
 | 
				
			||||||
 | 
					        *twaVal = twa;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					void HstryBuf::fillWndBufSimData(tBoatHstryData& hstryBufs)
 | 
				
			||||||
// make function available in Python for testing
 | 
					// Fill most part of TWD and TWS history buffer with simulated data
 | 
				
			||||||
static PyObject* true_wind(PyObject* self, PyObject* args) {
 | 
					{
 | 
				
			||||||
    double AWA,AWS,CTW,STW,HDT,TWS,TWD;
 | 
					    double value = 20.0;
 | 
				
			||||||
    if (!PyArg_ParseTuple(args, "ddddd", &AWA, &AWS, &CTW, &STW, &HDT)) {
 | 
					    int16_t value2 = 0;
 | 
				
			||||||
        return NULL;
 | 
					    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[] = {
 | 
					/* double genTwdSimDat()
 | 
				
			||||||
    {"true_wind", true_wind, METH_VARARGS, NULL},
 | 
					{
 | 
				
			||||||
    {NULL, NULL, 0, NULL}
 | 
					    simTwd += random(-20, 20);
 | 
				
			||||||
};
 | 
					    if (simTwd < 0.0)
 | 
				
			||||||
 | 
					        simTwd += 360.0;
 | 
				
			||||||
 | 
					    if (simTwd >= 360.0)
 | 
				
			||||||
 | 
					        simTwd -= 360.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct PyModuleDef module = {
 | 
					    int16_t z = static_cast<int16_t>(DegToRad(simTwd) * 1000.0);
 | 
				
			||||||
    PyModuleDef_HEAD_INIT,
 | 
					    pageData.boatHstry.twdHstry->add(z); // Fill the buffer with some test data
 | 
				
			||||||
    "truewind",    // Module name
 | 
					 | 
				
			||||||
    NULL,          // Optional docstring
 | 
					 | 
				
			||||||
    -1,
 | 
					 | 
				
			||||||
    methods
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyMODINIT_FUNC PyInit_truewind(void) {
 | 
					    simTws += random(-200, 150) / 10.0; // TWS value in knots
 | 
				
			||||||
    return PyModule_Create(&module);
 | 
					    simTws = constrain(simTws, 0.0f, 50.0f); // Ensure TWS is between 0 and 50 knots
 | 
				
			||||||
 | 
					    twsValue = simTws;
 | 
				
			||||||
}*/
 | 
					}*/
 | 
				
			||||||
| 
						 | 
					@ -1,48 +1,36 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
// #include <Python.h>
 | 
					 | 
				
			||||||
#include "GwApi.h"
 | 
					#include "GwApi.h"
 | 
				
			||||||
 | 
					#include "OBPRingBuffer.h"
 | 
				
			||||||
#include <Arduino.h>
 | 
					#include <Arduino.h>
 | 
				
			||||||
#include <math.h>
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// #define radians(a) (a*0.017453292519943295)
 | 
					typedef struct {
 | 
				
			||||||
// #define degrees(a) (a*57.29577951308232)
 | 
					    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 {
 | 
					class WindUtils {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    static void to360(double* a);
 | 
					    static double to2PI(double a);
 | 
				
			||||||
    static void to180(double* a);
 | 
					    static double toPI(double a);
 | 
				
			||||||
    static void to2PI(double* a);
 | 
					    static double to360(double a);
 | 
				
			||||||
    static void toPI(double* a);
 | 
					    static double to180(double a);
 | 
				
			||||||
    static void toCart(const double* phi, const double* r, double* x, double* y);
 | 
					    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 toPol(const double* x, const double* y, double* phi, double* r);
 | 
				
			||||||
    static void addPolar(const double* phi1, const double* r1,
 | 
					    static void addPolar(const double* phi1, const double* r1,
 | 
				
			||||||
        const double* phi2, const double* r2,
 | 
					        const double* phi2, const double* r2,
 | 
				
			||||||
        double* phi, double* r);
 | 
					        double* phi, double* r);
 | 
				
			||||||
    static 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,
 | 
					    static void calcTwdSA(const double* AWA, const double* AWS,
 | 
				
			||||||
        const double* CTW, const double* STW, const double* HDT,
 | 
					        const double* CTW, const double* STW, const double* HDT,
 | 
				
			||||||
        double* TWD, double* TWS);
 | 
					        double* 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);
 | 
					    RingBuffer(size_t size);
 | 
				
			||||||
    void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
 | 
					    void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
 | 
				
			||||||
    bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
 | 
					    bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
 | 
				
			||||||
 | 
					    String getName() const; // Get buffer name
 | 
				
			||||||
    void add(const T& value); // Add a new value to  buffer
 | 
					    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 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
 | 
					    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;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get buffer name
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					String RingBuffer<T>::getName() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return dataName;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Add a new value to buffer
 | 
					// Add a new value to buffer
 | 
				
			||||||
template <typename T>
 | 
					template <typename T>
 | 
				
			||||||
void RingBuffer<T>::add(const T& value)
 | 
					void RingBuffer<T>::add(const T& value)
 | 
				
			||||||
| 
						 | 
					@ -129,9 +136,9 @@ T RingBuffer<T>::getMin() const
 | 
				
			||||||
        return MIN_VAL;
 | 
					        return MIN_VAL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    T minVal = getFirst();
 | 
					    T minVal = MAX_VAL;
 | 
				
			||||||
    T value;
 | 
					    T value;
 | 
				
			||||||
    for (size_t i = 1; i < count; i++) {
 | 
					    for (size_t i = 0; i < count; i++) {
 | 
				
			||||||
        value = get(i);
 | 
					        value = get(i);
 | 
				
			||||||
        if (value < minVal && value != MIN_VAL) {
 | 
					        if (value < minVal && value != MIN_VAL) {
 | 
				
			||||||
            minVal = value;
 | 
					            minVal = value;
 | 
				
			||||||
| 
						 | 
					@ -150,7 +157,7 @@ T RingBuffer<T>::getMin(size_t amount) const
 | 
				
			||||||
    if (amount > count)
 | 
					    if (amount > count)
 | 
				
			||||||
        amount = count;
 | 
					        amount = count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    T minVal = getLast();
 | 
					    T minVal = MAX_VAL;
 | 
				
			||||||
    T value;
 | 
					    T value;
 | 
				
			||||||
    for (size_t i = 0; i < amount; i++) {
 | 
					    for (size_t i = 0; i < amount; i++) {
 | 
				
			||||||
        value = get(count - 1 - i);
 | 
					        value = get(count - 1 - i);
 | 
				
			||||||
| 
						 | 
					@ -169,9 +176,9 @@ T RingBuffer<T>::getMax() const
 | 
				
			||||||
        return MIN_VAL;
 | 
					        return MIN_VAL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    T maxVal = getFirst();
 | 
					    T maxVal = MIN_VAL;
 | 
				
			||||||
    T value;
 | 
					    T value;
 | 
				
			||||||
    for (size_t i = 1; i < count; i++) {
 | 
					    for (size_t i = 0; i < count; i++) {
 | 
				
			||||||
        value = get(i);
 | 
					        value = get(i);
 | 
				
			||||||
        if (value > maxVal && value != MIN_VAL) {
 | 
					        if (value > maxVal && value != MIN_VAL) {
 | 
				
			||||||
            maxVal = value;
 | 
					            maxVal = value;
 | 
				
			||||||
| 
						 | 
					@ -190,7 +197,7 @@ T RingBuffer<T>::getMax(size_t amount) const
 | 
				
			||||||
    if (amount > count)
 | 
					    if (amount > count)
 | 
				
			||||||
        amount = count;
 | 
					        amount = count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    T maxVal = getLast();
 | 
					    T maxVal = MIN_VAL;
 | 
				
			||||||
    T value;
 | 
					    T value;
 | 
				
			||||||
    for (size_t i = 0; i < amount; i++) {
 | 
					    for (size_t i = 0; i < amount; i++) {
 | 
				
			||||||
        value = get(count - 1 - i);
 | 
					        value = get(count - 1 - i);
 | 
				
			||||||
| 
						 | 
					@ -328,7 +335,7 @@ bool RingBuffer<T>::isFull() const
 | 
				
			||||||
    return is_Full;
 | 
					    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>
 | 
					template <typename T>
 | 
				
			||||||
T RingBuffer<T>::getMinVal() const
 | 
					T RingBuffer<T>::getMinVal() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,20 +4,17 @@
 | 
				
			||||||
#include "OBP60Extensions.h"
 | 
					#include "OBP60Extensions.h"
 | 
				
			||||||
#include "OBPRingBuffer.h"
 | 
					#include "OBPRingBuffer.h"
 | 
				
			||||||
#include "Pagedata.h"
 | 
					#include "Pagedata.h"
 | 
				
			||||||
#include <N2kMessages.h> // just for RadToDeg function
 | 
					 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
 | 
					static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
 | 
				
			||||||
// #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
 | 
					// 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 getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int minVal = windDirHstry.getMinVal();
 | 
					    int minVal = windDirHstry.getMinVal();
 | 
				
			||||||
    size_t count = windDirHstry.getCurrentSize();
 | 
					    size_t count = windDirHstry.getCurrentSize();
 | 
				
			||||||
//    size_t capacity = windDirHstry.getCapacity();
 | 
					    //    size_t capacity = windDirHstry.getCapacity();
 | 
				
			||||||
//    size_t last = windDirHstry.getLastIdx();
 | 
					    //    size_t last = windDirHstry.getLastIdx();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (windDirHstry.isEmpty() || amount <= 0) {
 | 
					    if (windDirHstry.isEmpty() || amount <= 0) {
 | 
				
			||||||
        return minVal;
 | 
					        return minVal;
 | 
				
			||||||
| 
						 | 
					@ -30,7 +27,7 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
 | 
				
			||||||
    int maxRng = minVal;
 | 
					    int maxRng = minVal;
 | 
				
			||||||
    // Start from the newest value (last) and go backwards x times
 | 
					    // Start from the newest value (last) and go backwards x times
 | 
				
			||||||
    for (size_t i = 0; i < amount; i++) {
 | 
					    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);
 | 
					        value = windDirHstry.get(count - 1 - i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (value == minVal) {
 | 
					        if (value == minVal) {
 | 
				
			||||||
| 
						 | 
					@ -49,43 +46,6 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
 | 
				
			||||||
    return maxRng;
 | 
					    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 {
 | 
					class PageWindPlot : public Page {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,7 +65,7 @@ public:
 | 
				
			||||||
    virtual void setupKeys()
 | 
					    virtual void setupKeys()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Page::setupKeys();
 | 
					        Page::setupKeys();
 | 
				
			||||||
        commonData->keydata[0].label = "MODE";
 | 
					        //        commonData->keydata[0].label = "MODE";
 | 
				
			||||||
        commonData->keydata[1].label = "INTV";
 | 
					        commonData->keydata[1].label = "INTV";
 | 
				
			||||||
        commonData->keydata[4].label = "TWS";
 | 
					        commonData->keydata[4].label = "TWS";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -113,7 +73,7 @@ public:
 | 
				
			||||||
    // Key functions
 | 
					    // Key functions
 | 
				
			||||||
    virtual int handleKey(int key)
 | 
					    virtual int handleKey(int key)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Set chart mode TWD | TWS
 | 
					        // Set chart mode TWD | TWS -> to be implemented
 | 
				
			||||||
        if (key == 1) {
 | 
					        if (key == 1) {
 | 
				
			||||||
            if (chrtMode == 'D') {
 | 
					            if (chrtMode == 'D') {
 | 
				
			||||||
                chrtMode = 'S';
 | 
					                chrtMode = 'S';
 | 
				
			||||||
| 
						 | 
					@ -159,9 +119,10 @@ public:
 | 
				
			||||||
        GwLog* logger = commonData->logger;
 | 
					        GwLog* logger = commonData->logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        float twsValue; // TWS value in chart area
 | 
					        float twsValue; // TWS value in chart area
 | 
				
			||||||
        String twdName, twdUnit; // TWD name and unit
 | 
					        static String twdName, twdUnit; // TWD name and unit
 | 
				
			||||||
        int updFreq; // Update frequency for TWD
 | 
					        static int updFreq; // Update frequency for TWD
 | 
				
			||||||
        int16_t twdLowest, twdHighest; // TWD range
 | 
					        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 isInitialized = false; // Flag to indicate that page is initialized
 | 
				
			||||||
        static bool wndDataValid = false; // Flag to indicate if wind data is valid
 | 
					        static bool wndDataValid = false; // Flag to indicate if wind data is valid
 | 
				
			||||||
| 
						 | 
					@ -191,6 +152,7 @@ public:
 | 
				
			||||||
        static int chrtRng; // Range of wind values from mid wind value to min/max wind value in degrees
 | 
					        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
 | 
					        int diffRng; // Difference between mid and current wind value
 | 
				
			||||||
        static const int dfltRng = 40; // Default range for chart
 | 
					        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 int simTwd; // Simulation value for TWD
 | 
				
			||||||
        static float simTws; // Simulation value for TWS
 | 
					        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
 | 
					        static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        LOG_DEBUG(GwLog::LOG, "Display page WindPlot");
 | 
					        LOG_DEBUG(GwLog::LOG, "Display page WindPlot");
 | 
				
			||||||
        unsigned long start = millis();
 | 
					        unsigned long WndPlotStart = millis();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get config data
 | 
					        // Get config data
 | 
				
			||||||
        simulation = config->getBool(config->useSimuData);
 | 
					        simulation = config->getBool(config->useSimuData);
 | 
				
			||||||
| 
						 | 
					@ -210,11 +172,6 @@ public:
 | 
				
			||||||
        String backlightMode = config->getString(config->backlight);
 | 
					        String backlightMode = config->getString(config->backlight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!isInitialized) {
 | 
					        if (!isInitialized) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (simulation) {
 | 
					 | 
				
			||||||
                fillSimData(pageData); // Fill the buffer with some test data
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            width = getdisplay().width();
 | 
					            width = getdisplay().width();
 | 
				
			||||||
            height = getdisplay().height();
 | 
					            height = getdisplay().height();
 | 
				
			||||||
            xCenter = width / 2;
 | 
					            xCenter = width / 2;
 | 
				
			||||||
| 
						 | 
					@ -230,8 +187,33 @@ public:
 | 
				
			||||||
            lastAddedIdx = pageData.boatHstry.twdHstry->getLastIdx();
 | 
					            lastAddedIdx = pageData.boatHstry.twdHstry->getLastIdx();
 | 
				
			||||||
            pageData.boatHstry.twdHstry->getMetaData(twdName, twdUnit, updFreq, twdLowest, twdHighest);
 | 
					            pageData.boatHstry.twdHstry->getMetaData(twdName, twdUnit, updFreq, twdLowest, twdHighest);
 | 
				
			||||||
            wndCenter = INT_MIN;
 | 
					            wndCenter = INT_MIN;
 | 
				
			||||||
 | 
					            midWndDir = 0;
 | 
				
			||||||
 | 
					            diffRng = dfltRng;
 | 
				
			||||||
 | 
					            chrtRng = dfltRng;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            isInitialized = true; // Set flag to indicate that page is now initialized
 | 
					            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)
 | 
					        // Optical warning by limit violation (unused)
 | 
				
			||||||
| 
						 | 
					@ -240,30 +222,11 @@ public:
 | 
				
			||||||
            setFlashLED(false);
 | 
					            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
 | 
					        // Identify buffer size and buffer start position for chart
 | 
				
			||||||
        count = pageData.boatHstry.twdHstry->getCurrentSize();
 | 
					        count = pageData.boatHstry.twdHstry->getCurrentSize();
 | 
				
			||||||
        currIdx = pageData.boatHstry.twdHstry->getLastIdx();
 | 
					        currIdx = pageData.boatHstry.twdHstry->getLastIdx();
 | 
				
			||||||
        numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
 | 
					        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
 | 
					            // new data interval selected by user
 | 
				
			||||||
            intvBufSize = cHeight * dataIntv;
 | 
					            intvBufSize = cHeight * dataIntv;
 | 
				
			||||||
            numWndVals = min(count, (cHeight - 60) * dataIntv);
 | 
					            numWndVals = min(count, (cHeight - 60) * dataIntv);
 | 
				
			||||||
| 
						 | 
					@ -277,16 +240,21 @@ public:
 | 
				
			||||||
                bufStart = max(0, bufStart - numAddedBufVals);
 | 
					                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",
 | 
					        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",
 | 
				
			||||||
            pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, pageData.boatHstry.dbtHstry->getLast() / 10.0,
 | 
					            count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, DataValid[0],
 | 
				
			||||||
            count, intvBufSize, numWndVals, bufStart, numAddedBufVals, pageData.boatHstry.twdHstry->getLastIdx());
 | 
					            intvBufSize, numWndVals, bufStart, numAddedBufVals, pageData.boatHstry.twdHstry->getLastIdx(), oldDataIntv, dataIntv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // initialize chart range values
 | 
					        // Set wndCenter from 1st real buffer value
 | 
				
			||||||
        if (wndCenter == INT_MIN) {
 | 
					        if (wndCenter == INT_MIN || (wndCenter == 0 && count == 1)) {
 | 
				
			||||||
            wndCenter = max(0, int(pageData.boatHstry.twdHstry->get(numWndVals - intvBufSize) / 1000.0 * radToDeg)); // get 1st value of current data interval
 | 
					            midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals);
 | 
				
			||||||
            wndCenter = (int((wndCenter + (wndCenter >= 0 ? 5 : -5)) / 10) * 10) % 360; // Set new center value; round to nearest 10 degree value; 360° -> 0°
 | 
					            if (midWndDir != INT16_MIN) {
 | 
				
			||||||
            diffRng = dfltRng;
 | 
					                midWndDir = midWndDir / 1000.0 * radToDeg;
 | 
				
			||||||
            chrtRng = dfltRng;
 | 
					                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 {
 | 
					        } else {
 | 
				
			||||||
            // check and adjust range between left, center, and right chart limit
 | 
					            // check and adjust range between left, center, and right chart limit
 | 
				
			||||||
            diffRng = getRng(*pageData.boatHstry.twdHstry, wndCenter, numWndVals);
 | 
					            diffRng = getRng(*pageData.boatHstry.twdHstry, wndCenter, numWndVals);
 | 
				
			||||||
| 
						 | 
					@ -304,8 +272,6 @@ public:
 | 
				
			||||||
        wndRight = (chrtRng < 180 ? wndCenter + chrtRng : wndCenter + chrtRng - 1);
 | 
					        wndRight = (chrtRng < 180 ? wndCenter + chrtRng : wndCenter + chrtRng - 1);
 | 
				
			||||||
        if (wndRight >= 360)
 | 
					        if (wndRight >= 360)
 | 
				
			||||||
            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
 | 
					        // Draw page
 | 
				
			||||||
        //***********************************************************************
 | 
					        //***********************************************************************
 | 
				
			||||||
| 
						 | 
					@ -322,11 +288,9 @@ public:
 | 
				
			||||||
        char sWndLbl[4]; // char buffer for Wind angle label
 | 
					        char sWndLbl[4]; // char buffer for Wind angle label
 | 
				
			||||||
        getdisplay().setFont(&Ubuntu_Bold12pt8b);
 | 
					        getdisplay().setFont(&Ubuntu_Bold12pt8b);
 | 
				
			||||||
        getdisplay().setCursor(xCenter - 88, yOffset - 3);
 | 
					        getdisplay().setCursor(xCenter - 88, yOffset - 3);
 | 
				
			||||||
        getdisplay().print("TWD"); // Wind name
 | 
					        getdisplay().print("TWD"); // Wind data name
 | 
				
			||||||
        // getdisplay().setCursor(xCenter - 20, yOffset - 3);
 | 
					 | 
				
			||||||
        snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
 | 
					        snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
 | 
				
			||||||
        drawTextCenter(xCenter, yOffset - 11, sWndLbl);
 | 
					        drawTextCenter(xCenter, yOffset - 11, sWndLbl);
 | 
				
			||||||
        // getdisplay().print(sWndLbl); // Wind center value
 | 
					 | 
				
			||||||
        getdisplay().drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
 | 
					        getdisplay().drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
 | 
				
			||||||
        getdisplay().drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
 | 
					        getdisplay().drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
 | 
				
			||||||
        getdisplay().setCursor(1, yOffset - 3);
 | 
					        getdisplay().setCursor(1, yOffset - 3);
 | 
				
			||||||
| 
						 | 
					@ -343,7 +307,19 @@ public:
 | 
				
			||||||
        if (pageData.boatHstry.twdHstry->getMax() == pageData.boatHstry.twdHstry->getMinVal()) {
 | 
					        if (pageData.boatHstry.twdHstry->getMax() == pageData.boatHstry.twdHstry->getMinVal()) {
 | 
				
			||||||
            // only <INT16_MIN> values in buffer -> no valid wind data available
 | 
					            // only <INT16_MIN> values in buffer -> no valid wind data available
 | 
				
			||||||
            wndDataValid = false;
 | 
					            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 {
 | 
					        } else {
 | 
				
			||||||
 | 
					            numNoData = 0; // reset data error counter
 | 
				
			||||||
            wndDataValid = true; // At least some wind data available
 | 
					            wndDataValid = true; // At least some wind data available
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // Draw wind values in chart
 | 
					        // Draw wind values in chart
 | 
				
			||||||
| 
						 | 
					@ -361,17 +337,17 @@ public:
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    if (numNoData > 4) {
 | 
					                    if (numNoData > 4) {
 | 
				
			||||||
                        // If more than 4 invalid values in a row, send message
 | 
					                        // 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
 | 
					                        getdisplay().fillRect(xCenter - 66, height / 2 - 20, 146, 24, commonData->bgcolor); // Clear area for TWS value
 | 
				
			||||||
                        drawTextCenter(xCenter, height / 2 - 10, "No sensor data");
 | 
					                        drawTextCenter(xCenter, height / 2 - 10, "No sensor data");
 | 
				
			||||||
                    } */
 | 
					                    } */
 | 
				
			||||||
                } else {
 | 
					                } 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;
 | 
					                    x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
 | 
				
			||||||
                    y = yOffset + cHeight - i; // Position in chart area
 | 
					                    y = yOffset + cHeight - i; // Position in chart area
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (i >= (numWndVals / dataIntv) - 10)
 | 
					                    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)) {
 | 
					                    if ((i == 0) || (chrtPrevVal == INT16_MIN)) {
 | 
				
			||||||
                        // just a dot for 1st chart point or after some invalid values
 | 
					                        // 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 minWndDir = pageData.boatHstry.twdHstry->getMin(numWndVals) / 1000.0 * radToDeg;
 | 
				
			||||||
                    int maxWndDir = pageData.boatHstry.twdHstry->getMax(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);
 | 
					                    LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter);
 | 
				
			||||||
                    if ((minWndDir > wndCenter) || (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
 | 
					                        // 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) {
 | 
					                        if (midWndDir != INT16_MIN) {
 | 
				
			||||||
                            wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
 | 
					                            wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    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;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -433,6 +409,8 @@ public:
 | 
				
			||||||
            int xPosTws;
 | 
					            int xPosTws;
 | 
				
			||||||
            static const int yPosTws = yOffset + 40;
 | 
					            static const int yPosTws = yOffset + 40;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            xPosTws = flipTws ? 20 : width - 138;
 | 
					            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
 | 
					            currentZone = (y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146) ? 1 : 0; // Define current zone for TWS value
 | 
				
			||||||
            if (currentZone != lastZone) {
 | 
					            if (currentZone != lastZone) {
 | 
				
			||||||
| 
						 | 
					@ -447,21 +425,24 @@ public:
 | 
				
			||||||
            getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
 | 
					            getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
 | 
				
			||||||
            getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
 | 
					            getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
 | 
				
			||||||
            getdisplay().setCursor(xPosTws, yPosTws);
 | 
					            getdisplay().setCursor(xPosTws, yPosTws);
 | 
				
			||||||
            if (twsValue < 0 || twsValue >= 100) {
 | 
					            if (!DataValid[1]) {
 | 
				
			||||||
                getdisplay().print("--.-");
 | 
					                getdisplay().print("--.-");
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if (twsValue < 10.0) {
 | 
					                if (DataValue[1] < 9.95) {
 | 
				
			||||||
                    getdisplay().printf("!%3.1f", twsValue); // Value
 | 
					                    getdisplay().printf("!%3.1f", DataValue[1] + 0.05); // Value, round to 1 decimal
 | 
				
			||||||
                } else {
 | 
					                } 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().setFont(&Ubuntu_Bold12pt8b);
 | 
				
			||||||
            getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
 | 
					            getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
 | 
				
			||||||
            getdisplay().print("TWS"); // Name
 | 
					//            getdisplay().print("TWS"); // Name
 | 
				
			||||||
 | 
					            getdisplay().print(DataName[1]); // Name
 | 
				
			||||||
            getdisplay().setFont(&Ubuntu_Bold8pt8b);
 | 
					            getdisplay().setFont(&Ubuntu_Bold8pt8b);
 | 
				
			||||||
            getdisplay().setCursor(xPosTws + 78, yPosTws + 1);
 | 
					//            getdisplay().setCursor(xPosTws + 78, yPosTws + 1);
 | 
				
			||||||
            getdisplay().printf(" kn"); // Unit
 | 
					            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
 | 
					        // 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++) {
 | 
					        for (int i = 1; i <= 3; i++) {
 | 
				
			||||||
            yPos = yOffset + (i * 60);
 | 
					            yPos = yOffset + (i * 60);
 | 
				
			||||||
            getdisplay().fillRect(0, yPos, width, 1, commonData->fgcolor);
 | 
					            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);
 | 
					            getdisplay().setCursor(1, yPos + 4);
 | 
				
			||||||
            if (count >= intvBufSize) {
 | 
					            if (count >= intvBufSize) {
 | 
				
			||||||
                // Calculate minute value for label
 | 
					                // Calculate minute value for label
 | 
				
			||||||
| 
						 | 
					@ -483,8 +464,8 @@ public:
 | 
				
			||||||
            getdisplay().printf("%3d", chrtLbl); // Wind value label
 | 
					            getdisplay().printf("%3d", chrtLbl); // Wind value label
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        unsigned long finish = millis() - start;
 | 
					        unsigned long finish = millis() - WndPlotStart;
 | 
				
			||||||
//        LOG_DEBUG(GwLog::ERROR, "PageWindPlot Time: %lu", finish);
 | 
					        LOG_DEBUG(GwLog::ERROR, "PageWindPlot Time: %lu", finish);
 | 
				
			||||||
        // Update display
 | 
					        // Update display
 | 
				
			||||||
        getdisplay().nextPage(); // Partial update (fast)
 | 
					        getdisplay().nextPage(); // Partial update (fast)
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					@ -505,8 +486,8 @@ PageDescription registerPageWindPlot(
 | 
				
			||||||
    "WindPlot", // Page name
 | 
					    "WindPlot", // Page name
 | 
				
			||||||
    createPage, // Action
 | 
					    createPage, // Action
 | 
				
			||||||
    0, // Number of bus values depends on selection in Web configuration
 | 
					    0, // Number of bus values depends on selection in Web configuration
 | 
				
			||||||
//    { "TWD", "TWA", "TWS", "HDM", "AWA", "AWS", "STW", "COG", "SOG" }, // Bus values we need in the page
 | 
					    { "TWD", "TWS" }, // Bus values we need in the page
 | 
				
			||||||
    { }, // Bus values we need in the page
 | 
					//    {}, // Bus values we need in the page
 | 
				
			||||||
    true // Show display header on/off
 | 
					    true // Show display header on/off
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,15 +5,10 @@
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include "LedSpiTask.h"
 | 
					#include "LedSpiTask.h"
 | 
				
			||||||
#include "OBPRingBuffer.h"
 | 
					#include "OBPRingBuffer.h"
 | 
				
			||||||
 | 
					#include "OBPDataOperations.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_PAGE_NUMBER 10    // Max number of pages for show data
 | 
					#define MAX_PAGE_NUMBER 10    // Max number of pages for show data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct{
 | 
					 | 
				
			||||||
  RingBuffer<int16_t>* twdHstry;
 | 
					 | 
				
			||||||
  RingBuffer<int16_t>* twsHstry;
 | 
					 | 
				
			||||||
  RingBuffer<int16_t>* dbtHstry;
 | 
					 | 
				
			||||||
} tBoatHstryData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef std::vector<GwApi::BoatValue *> ValueList;
 | 
					typedef std::vector<GwApi::BoatValue *> ValueList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct{
 | 
					typedef struct{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,6 +219,17 @@
 | 
				
			||||||
            "obp60":"true"
 | 
					            "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",
 | 
					        "name": "lengthFormat",
 | 
				
			||||||
        "label": "Length Format",
 | 
					        "label": "Length Format",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,6 +219,17 @@
 | 
				
			||||||
            "obp40": "true"
 | 
					            "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",
 | 
					        "name": "lengthFormat",
 | 
				
			||||||
        "label": "Length Format",
 | 
					        "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
 | 
					// OBP60 Task
 | 
				
			||||||
//####################################################################################
 | 
					//####################################################################################
 | 
				
			||||||
void OBP60Task(GwApi *api){
 | 
					void OBP60Task(GwApi *api){
 | 
				
			||||||
| 
						 | 
					@ -392,11 +498,6 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
    commonData.logger=logger;
 | 
					    commonData.logger=logger;
 | 
				
			||||||
    commonData.config=config;
 | 
					    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
 | 
					#ifdef HARDWARE_V21
 | 
				
			||||||
    // Keyboard coordinates for page footer
 | 
					    // Keyboard coordinates for page footer
 | 
				
			||||||
    initKeys(commonData);
 | 
					    initKeys(commonData);
 | 
				
			||||||
| 
						 | 
					@ -494,6 +595,11 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
    //commonData.distanceformat=config->getString(xxx);
 | 
					    //commonData.distanceformat=config->getString(xxx);
 | 
				
			||||||
    //add all necessary data to common data
 | 
					    //add all necessary data to common data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create ring buffers for history storage of some boat data
 | 
				
			||||||
 | 
					    RingBuffer<int16_t> twdHstry(960); // Circular buffer to store 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
 | 
					    //fill the page data from config
 | 
				
			||||||
    numPages=config->getInt(config->visiblePages,1);
 | 
					    numPages=config->getInt(config->visiblePages,1);
 | 
				
			||||||
    if (numPages < 1) numPages=1;
 | 
					    if (numPages < 1) numPages=1;
 | 
				
			||||||
| 
						 | 
					@ -532,8 +638,10 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
            LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
 | 
					            LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
 | 
				
			||||||
            pages[i].parameters.values.push_back(value); 
 | 
					            pages[i].parameters.values.push_back(value); 
 | 
				
			||||||
       }
 | 
					       }
 | 
				
			||||||
 | 
					        if (pages[i].description->pageName == "WindPlot") {
 | 
				
			||||||
            // Add boat history data to page parameters
 | 
					            // Add boat history data to page parameters
 | 
				
			||||||
        pages[i].parameters.boatHstry = {&twdHstry, &twsHstry, &dbtHstry};
 | 
					            pages[i].parameters.boatHstry = hstryBufList;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // add out of band system page (always available)
 | 
					    // add out of band system page (always available)
 | 
				
			||||||
    Page *syspage = allPages.pages[0]->creator(commonData);
 | 
					    Page *syspage = allPages.pages[0]->creator(commonData);
 | 
				
			||||||
| 
						 | 
					@ -541,32 +649,12 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
    // Read all calibration data settings from config
 | 
					    // Read all calibration data settings from config
 | 
				
			||||||
    calibrationData.readConfig(config, logger);
 | 
					    calibrationData.readConfig(config, logger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // List of boat values for history storage
 | 
					    // Check user setting for true wind calculation
 | 
				
			||||||
    GwApi::BoatValue *twdBVal=new GwApi::BoatValue(GwBoatData::_TWD);
 | 
					    bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
 | 
				
			||||||
    GwApi::BoatValue *twsBVal=new GwApi::BoatValue(GwBoatData::_TWS);
 | 
					    // bool simulation = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
 | 
				
			||||||
    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);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // List of boat values for true winds calculation
 | 
					    // Initialize history buffer for certain boat data
 | 
				
			||||||
    GwApi::BoatValue *awaBVal=new GwApi::BoatValue(GwBoatData::_AWA);
 | 
					    initHstryBuf(api, &boatValues, hstryBufList);
 | 
				
			||||||
    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};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Display screenshot handler for HTTP request
 | 
					    // Display screenshot handler for HTTP request
 | 
				
			||||||
    // http://192.168.15.1/api/user/OBP60Task/screenshot
 | 
					    // http://192.168.15.1/api/user/OBP60Task/screenshot
 | 
				
			||||||
| 
						 | 
					@ -623,18 +711,6 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
    GwApi::BoatValue *lon = boatValues.findValueOrCreate("LON");        // Load GpsLongitude
 | 
					    GwApi::BoatValue *lon = boatValues.findValueOrCreate("LON");        // Load GpsLongitude
 | 
				
			||||||
    GwApi::BoatValue *hdop = boatValues.findValueOrCreate("HDOP");       // Load GpsHDOP
 | 
					    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");
 | 
					    LOG_DEBUG(GwLog::LOG,"obp60task: start mainloop");
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    commonData.time = boatValues.findValueOrCreate("GPST");      // Load GpsTime
 | 
					    commonData.time = boatValues.findValueOrCreate("GPST");      // Load GpsTime
 | 
				
			||||||
| 
						 | 
					@ -648,7 +724,6 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
    long starttime3 = millis();     // Display update all 1s
 | 
					    long starttime3 = millis();     // Display update all 1s
 | 
				
			||||||
    long starttime4 = millis();     // Delayed display update after 4s when select a new page
 | 
					    long starttime4 = millis();     // Delayed display update after 4s when select a new page
 | 
				
			||||||
    long starttime5 = millis();     // Calculate sunrise and sunset all 1s
 | 
					    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
 | 
					    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
 | 
					            // Refresh display data, default all 1s
 | 
				
			||||||
            currentPage = pages[pageNumber].page;
 | 
					            currentPage = pages[pageNumber].page;
 | 
				
			||||||
            int pagetime = 1000;
 | 
					            int pagetime = 1000;
 | 
				
			||||||
| 
						 | 
					@ -907,6 +928,12 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
                api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
 | 
					                api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
 | 
				
			||||||
                api->getStatus(commonData.status);
 | 
					                api->getStatus(commonData.status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (calcTrueWnds) {
 | 
				
			||||||
 | 
					                    addTrueWind(api, &boatValues);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Handle history buffers for TWD, TWS for wind plot page and other usage
 | 
				
			||||||
 | 
					                 handleHstryBuf(api, &boatValues, hstryBufList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Clear display
 | 
					                // Clear display
 | 
				
			||||||
                // getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
 | 
					                // getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
 | 
				
			||||||
                getdisplay().fillScreen(commonData.bgcolor);  // Clear display
 | 
					                getdisplay().fillScreen(commonData.bgcolor);  // Clear display
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue