From a5494ccee46e98434d7205becc8167a6b70a7546 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Sat, 10 May 2025 01:59:19 +0200 Subject: [PATCH 01/10] Initial commit - principle working --- lib/obp60task/BoatDataCalibration.cpp | 123 ++++++++++++++++++++++++++ lib/obp60task/BoatDataCalibration.h | 33 +++++++ lib/obp60task/PageFourValues.cpp | 5 ++ lib/obp60task/PageFourValues2.cpp | 5 ++ lib/obp60task/PageOneValue.cpp | 2 + lib/obp60task/PageThreeValues.cpp | 4 + lib/obp60task/PageTwoValues.cpp | 3 + lib/obp60task/config.json | 91 +++++++++++++++++++ lib/obp60task/obp60task.cpp | 5 ++ 9 files changed, 271 insertions(+) create mode 100644 lib/obp60task/BoatDataCalibration.cpp create mode 100644 lib/obp60task/BoatDataCalibration.h diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp new file mode 100644 index 0000000..379e7dd --- /dev/null +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -0,0 +1,123 @@ +#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 + +#include "BoatDataCalibration.h" + +CalibrationDataList calibrationData; + +void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) +// Initial load of calibration data into internal list +// This function is called once at init phase of to read the configuration values +{ + // Load user configuration values + String lengthFormat = config->getString(config->lengthFormat); // [m|ft] + String distanceFormat = config->getString(config->distanceFormat); // [m|km|nm] + String speedFormat = config->getString(config->speedFormat); // [m/s|km/h|kn] + String windspeedFormat = config->getString(config->windspeedFormat); // [m/s|km/h|kn|bft] + String tempFormat = config->getString(config->tempFormat); // [K|°C|°F] + + // Read calibration settings for DBT + calibrationData.list[0].instance = "DBT"; + calibrationData.list[0].offset = (config->getString(config->calOffsetDBT)).toFloat(); + if (lengthFormat == "ft") { // Convert DBT to SI standard meters + calibrationData.list[0].offset *= 0.3048; + } + calibrationData.list[0].slope = 1.0; // No slope for DBT + calibrationData.list[0].isCalibrated = false; + + // Read calibration settings for other data instances + for (int i = 1; i < maxCalibrationData; i++) { + String instance = "calInstance" + String(i); + String offset = "calOffset" + String(i); + String slope = "calSlope" + String(i); + +// calibrationData = new CalibrationDataList; + calibrationData.list[i].instance = config->getString(instance, ""); + calibrationData.list[i].offset = (config->getString(offset, "")).toFloat(); + calibrationData.list[i].slope = (config->getString(slope, "")).toFloat(); + if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "AWS") { + if (windspeedFormat == "m/s") { // Convert calibration values to SI standard m/s + // No conversion needed + } else if (windspeedFormat == "km/h") { + calibrationData.list[i].offset /= 3.6; // Convert km/h to m/s + calibrationData.list[i].slope /= 3.6; // Convert km/h to m/s + } else if (windspeedFormat == "kn") { + calibrationData.list[i].offset /= 1.94384; // Convert kn to m/s + calibrationData.list[i].slope /= 1.94384; // Convert kn to m/s + } else if (windspeedFormat == "bft") { + calibrationData.list[i].offset *= 0.5; // Convert Bft to m/s (approx) -> to be improved + calibrationData.list[i].slope *= 0.5; // Convert km/h to m/s + } + } else if (calibrationData.list[i].instance == "STW") { + if (speedFormat == "m/s") { + // No conversion needed + } else if (speedFormat == "km/h") { + calibrationData.list[i].offset /= 3.6; // Convert km/h to m/s + calibrationData.list[i].slope /= 3.6; // Convert km/h to m/s + } else if (speedFormat == "kn") { + calibrationData.list[i].offset /= 1.94384; // Convert kn to m/s + calibrationData.list[i].slope /= 1.94384; // Convert kn to m/s + } + } else if (calibrationData.list[i].instance == "WTemp") { + if (tempFormat == "K" || tempFormat == "C") { + // No conversion needed + } else if (tempFormat == "F") { + calibrationData.list[i].offset *= 5.0 / 9.0; // Convert °F to K + calibrationData.list[i].slope *= 5.0 / 9.0; // Convert °F to K + } + } + calibrationData.list[i].isCalibrated = false; + + LOG_DEBUG(GwLog::LOG, "stored calibration data: %s, offset: %f, slope: %f", calibrationData.list[i].instance.c_str(), calibrationData.list[i].offset, calibrationData.list[i].slope); + } +} + +int CalibrationDataList::getInstanceListNo(String instance) +// Function to get the index of the requested instance in the list +{ + + // Check if instance is in the list + for (int i = 0; i < maxCalibrationData; i++) { + if (calibrationData.list[i].instance == instance) { + return i; + } + } + return -1; // instance not found +} + +/* void CalibrationDataList::updateBoatDataValidity(String instance) +{ + for (int i = 0; i < maxCalibrationData; i++) { + if (calibrationData.list[i].instance == instance) { + // test for boat data value validity - to be implemented + calibrationData.list[i].isValid = true; + return; + } + } +} */ + +void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* boatDataValue, GwLog* logger) +// Function to calibrate the boat data value +{ + double offset = 0; + double slope = 1.0; + + int listNo = getInstanceListNo(instance); + if (listNo >= 0) { + offset = calibrationData.list[listNo].offset; + slope = calibrationData.list[listNo].slope; + + if (!boatDataValue->valid) { // no valid boat data value, so we don't want to apply calibration data + return; + } else { + boatDataValue->value = (boatDataValue->value * slope) + offset; + calibrationData.list[listNo].value = boatDataValue->value; + calibrationData.list[listNo].isCalibrated = true; + LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s: Offset: %f Slope: %f Result: %f", instance.c_str(), offset, slope, boatDataValue->value); + + } + } else { + LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s not found in calibration data list", instance.c_str()); + } +} + +#endif \ No newline at end of file diff --git a/lib/obp60task/BoatDataCalibration.h b/lib/obp60task/BoatDataCalibration.h new file mode 100644 index 0000000..61db4db --- /dev/null +++ b/lib/obp60task/BoatDataCalibration.h @@ -0,0 +1,33 @@ +// Functions lib for data instance calibration + +#ifndef _BOATDATACALIBRATION_H +#define _BOATDATACALIBRATION_H + +#include "Pagedata.h" +#include "WString.h" + +typedef struct { + String instance; // data type/instance to be calibrated + double offset; // calibration offset + double slope; // calibration slope + double value; // calibrated data value + bool isCalibrated; // is data instance value calibrated? +} CalibData; + +const int maxCalibrationData = 3; // maximum number of calibration data instances + +class CalibrationDataList { +public: + CalibData list[maxCalibrationData]; // list of calibration data instances + + static void readConfig(GwConfigHandler* config, GwLog* logger); + static int getInstanceListNo(String instance); + static void calibrateInstance(String instance, GwApi::BoatValue* boatDataValue, GwLog* logger); + +private: +// GwLog* logger; +}; + +extern CalibrationDataList calibrationData; // this list holds all calibration data + +#endif \ No newline at end of file diff --git a/lib/obp60task/PageFourValues.cpp b/lib/obp60task/PageFourValues.cpp index e8e1024..fa0d61b 100644 --- a/lib/obp60task/PageFourValues.cpp +++ b/lib/obp60task/PageFourValues.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageFourValues : public Page { @@ -47,6 +48,7 @@ class PageFourValues : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information + CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -56,6 +58,7 @@ class PageFourValues : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information + CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value @@ -65,6 +68,7 @@ class PageFourValues : public Page name3 = name3.substring(0, 6); // String length limit for value name double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information + CalibrationDataList::calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value @@ -74,6 +78,7 @@ class PageFourValues : public Page name4 = name4.substring(0, 6); // String length limit for value name double value4 = bvalue4->value; // Value as double in SI unit bool valid4 = bvalue4->valid; // Valid information + CalibrationDataList::calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value diff --git a/lib/obp60task/PageFourValues2.cpp b/lib/obp60task/PageFourValues2.cpp index 61a5f6f..3e84611 100644 --- a/lib/obp60task/PageFourValues2.cpp +++ b/lib/obp60task/PageFourValues2.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageFourValues2 : public Page { @@ -47,6 +48,7 @@ class PageFourValues2 : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information + CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -56,6 +58,7 @@ class PageFourValues2 : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information + CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value @@ -65,6 +68,7 @@ class PageFourValues2 : public Page name3 = name3.substring(0, 6); // String length limit for value name double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information + CalibrationDataList::calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value @@ -74,6 +78,7 @@ class PageFourValues2 : public Page name4 = name4.substring(0, 6); // String length limit for value name double value4 = bvalue4->value; // Value as double in SI unit bool valid4 = bvalue4->valid; // Valid information + CalibrationDataList::calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value diff --git a/lib/obp60task/PageOneValue.cpp b/lib/obp60task/PageOneValue.cpp index 0d2a879..e839e79 100644 --- a/lib/obp60task/PageOneValue.cpp +++ b/lib/obp60task/PageOneValue.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageOneValue : public Page { @@ -41,6 +42,7 @@ class PageOneValue : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information + CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value diff --git a/lib/obp60task/PageThreeValues.cpp b/lib/obp60task/PageThreeValues.cpp index 71e1df1..c4b9702 100644 --- a/lib/obp60task/PageThreeValues.cpp +++ b/lib/obp60task/PageThreeValues.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageThreeValues : public Page { @@ -45,6 +46,7 @@ class PageThreeValues : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information + CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -54,6 +56,7 @@ class PageThreeValues : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information + CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value @@ -63,6 +66,7 @@ class PageThreeValues : public Page name3 = name3.substring(0, 6); // String length limit for value name double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information + CalibrationDataList::calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value diff --git a/lib/obp60task/PageTwoValues.cpp b/lib/obp60task/PageTwoValues.cpp index 673ab8f..481b6ba 100644 --- a/lib/obp60task/PageTwoValues.cpp +++ b/lib/obp60task/PageTwoValues.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageTwoValues : public Page { @@ -43,6 +44,7 @@ class PageTwoValues : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information + CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -52,6 +54,7 @@ class PageTwoValues : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information + CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index a4233ae..60c627a 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -219,6 +219,17 @@ "obp60":"true" } }, + { + "name": "calOffsetDBT", + "label": "Offset DBT", + "type": "number", + "default": "0.00", + "description": "Offset for depth transducer; positive for depth from surface, negative for depth below keel", + "category": "OBP60 Settings", + "capabilities": { + "obp60":"true" + } + }, { "name": "lengthFormat", "label": "Length Format", @@ -687,6 +698,86 @@ } }, { + "name": "calInstance1", + "label": "Calibration Data Instance 1", + "type": "list", + "default": "", + "description": "Data instance for calibration", + "list": [ + "AWA", + "AWS", + "HDM", + "STW", + "WTemp" + ], + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "calOffset1", + "label": "Calibration Data Instance 1 Offset", + "type": "number", + "default": "0.00", + "description": "Offset for data instance 1", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "calSlope1", + "label": "Calibration Data Instance 1 Slope", + "type": "number", + "default": "1.00", + "description": "Slope for data instance 1", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "calInstance2", + "label": "Calibration Data Instance 2", + "type": "list", + "default": "", + "description": "Data instance for calibration", + "list": [ + "AWA", + "AWS", + "HDM", + "STW", + "WTemp" + ], + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "calOffset2", + "label": "Calibration Data Instance 2 Offset", + "type": "number", + "default": "0.00", + "description": "Offset for data instance 2", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "calSlope2", + "label": "Calibration Data Instance 2 Slope", + "type": "number", + "default": "1.00", + "description": "Slope for data instance 2", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, +{ "name": "display", "label": "Display Mode", "type": "list", diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index 6073f9f..a84f247 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -12,6 +12,7 @@ #include // GxEPD2 lib for b/w E-Ink displays #include "OBP60Extensions.h" // Functions lib for extension board #include "OBP60Keypad.h" // Functions for keypad +#include "BoatDataCalibration.h" // Functions lib for data instance calibration #ifdef BOARD_OBP40S3 #include "driver/rtc_io.h" // Needs for weakup from deep sleep @@ -385,6 +386,10 @@ void OBP60Task(GwApi *api){ commonData.logger=logger; commonData.config=config; + // Read all calibration data settings from config + CalibrationDataList::readConfig(config, logger); + LOG_DEBUG(GwLog::LOG,"Calibration data read from config"); + #ifdef HARDWARE_V21 // Keyboard coordinates for page footer initKeys(commonData); From 2fb59fb1187eb731822614cfb856a0d9c25ff24e Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Sun, 11 May 2025 21:57:22 +0200 Subject: [PATCH 02/10] Implemented for few std. types - DBT missing --- lib/obp60task/BoatDataCalibration.cpp | 99 +++++++++++++++++---------- lib/obp60task/BoatDataCalibration.h | 1 - lib/obp60task/PageFourValues.cpp | 8 +-- lib/obp60task/PageFourValues2.cpp | 8 +-- lib/obp60task/PageOneValue.cpp | 8 +-- lib/obp60task/PageSixValues.cpp | 2 + lib/obp60task/PageThreeValues.cpp | 6 +- lib/obp60task/PageTwoValues.cpp | 4 +- lib/obp60task/config.json | 54 ++++++++++++--- lib/obp60task/obp60task.cpp | 7 +- 10 files changed, 126 insertions(+), 71 deletions(-) diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp index 379e7dd..ff8ec5a 100644 --- a/lib/obp60task/BoatDataCalibration.cpp +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -1,80 +1,79 @@ #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #include "BoatDataCalibration.h" +#include +#include CalibrationDataList calibrationData; void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) // Initial load of calibration data into internal list -// This function is called once at init phase of to read the configuration values +// This method is called once at init phase of to read the configuration values { - // Load user configuration values + // Load user format configuration values String lengthFormat = config->getString(config->lengthFormat); // [m|ft] String distanceFormat = config->getString(config->distanceFormat); // [m|km|nm] String speedFormat = config->getString(config->speedFormat); // [m/s|km/h|kn] String windspeedFormat = config->getString(config->windspeedFormat); // [m/s|km/h|kn|bft] - String tempFormat = config->getString(config->tempFormat); // [K|°C|°F] + String tempFormat = config->getString(config->tempFormat); // [K|C|F] - // Read calibration settings for DBT - calibrationData.list[0].instance = "DBT"; - calibrationData.list[0].offset = (config->getString(config->calOffsetDBT)).toFloat(); - if (lengthFormat == "ft") { // Convert DBT to SI standard meters - calibrationData.list[0].offset *= 0.3048; - } - calibrationData.list[0].slope = 1.0; // No slope for DBT - calibrationData.list[0].isCalibrated = false; + // Read calibration settings for data instances + for (int i = 0; i < maxCalibrationData; i++) { + String instance = "calInstance" + String(i+1); + String offset = "calOffset" + String(i+1); + String slope = "calSlope" + String(i+1); + calibrationData.list[i] = { "", 0.0f, 1.0f, 0.0f, false }; - // Read calibration settings for other data instances - for (int i = 1; i < maxCalibrationData; i++) { - String instance = "calInstance" + String(i); - String offset = "calOffset" + String(i); - String slope = "calSlope" + String(i); - -// calibrationData = new CalibrationDataList; calibrationData.list[i].instance = config->getString(instance, ""); + if (calibrationData.list[i].instance == "") { + LOG_DEBUG(GwLog::LOG, "no calibration data for instance no. %d", i+1); + continue; + } + calibrationData.list[i].offset = (config->getString(offset, "")).toFloat(); calibrationData.list[i].slope = (config->getString(slope, "")).toFloat(); - if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "AWS") { - if (windspeedFormat == "m/s") { // Convert calibration values to SI standard m/s + + // Convert calibration values to internal standard formats + if (calibrationData.list[i].instance == "AWS") { + if (windspeedFormat == "m/s") { // No conversion needed } else if (windspeedFormat == "km/h") { calibrationData.list[i].offset /= 3.6; // Convert km/h to m/s - calibrationData.list[i].slope /= 3.6; // Convert km/h to m/s } else if (windspeedFormat == "kn") { calibrationData.list[i].offset /= 1.94384; // Convert kn to m/s - calibrationData.list[i].slope /= 1.94384; // Convert kn to m/s } else if (windspeedFormat == "bft") { calibrationData.list[i].offset *= 0.5; // Convert Bft to m/s (approx) -> to be improved - calibrationData.list[i].slope *= 0.5; // Convert km/h to m/s } + + } else if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "HDM") { + calibrationData.list[i].offset *= M_PI / 180; // Convert deg to rad + } else if (calibrationData.list[i].instance == "STW") { if (speedFormat == "m/s") { // No conversion needed } else if (speedFormat == "km/h") { calibrationData.list[i].offset /= 3.6; // Convert km/h to m/s - calibrationData.list[i].slope /= 3.6; // Convert km/h to m/s } else if (speedFormat == "kn") { calibrationData.list[i].offset /= 1.94384; // Convert kn to m/s - calibrationData.list[i].slope /= 1.94384; // Convert kn to m/s } + } else if (calibrationData.list[i].instance == "WTemp") { if (tempFormat == "K" || tempFormat == "C") { // No conversion needed } else if (tempFormat == "F") { - calibrationData.list[i].offset *= 5.0 / 9.0; // Convert °F to K - calibrationData.list[i].slope *= 5.0 / 9.0; // Convert °F to K + calibrationData.list[i].offset *= 9.0 / 5.0; // Convert °F to K + calibrationData.list[i].slope *= 9.0 / 5.0; // Convert °F to K } } calibrationData.list[i].isCalibrated = false; - LOG_DEBUG(GwLog::LOG, "stored calibration data: %s, offset: %f, slope: %f", calibrationData.list[i].instance.c_str(), calibrationData.list[i].offset, calibrationData.list[i].slope); } + LOG_DEBUG(GwLog::LOG, "all calibration data read"); } int CalibrationDataList::getInstanceListNo(String instance) -// Function to get the index of the requested instance in the list +// Method to get the index of the requested instance in the list { - // Check if instance is in the list for (int i = 0; i < maxCalibrationData; i++) { if (calibrationData.list[i].instance == instance) { @@ -96,27 +95,51 @@ int CalibrationDataList::getInstanceListNo(String instance) } */ void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* boatDataValue, GwLog* logger) -// Function to calibrate the boat data value +// Method to calibrate the boat data value { double offset = 0; double slope = 1.0; + double dataValue = 0; int listNo = getInstanceListNo(instance); - if (listNo >= 0) { + if (listNo < 0) { + LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s not found in calibration data list", instance.c_str()); + return; + } else { offset = calibrationData.list[listNo].offset; slope = calibrationData.list[listNo].slope; if (!boatDataValue->valid) { // no valid boat data value, so we don't want to apply calibration data return; } else { - boatDataValue->value = (boatDataValue->value * slope) + offset; - calibrationData.list[listNo].value = boatDataValue->value; - calibrationData.list[listNo].isCalibrated = true; - LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s: Offset: %f Slope: %f Result: %f", instance.c_str(), offset, slope, boatDataValue->value); + dataValue = boatDataValue->value; + LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: name: %s: value: %f format: %s", boatDataValue->getName().c_str(), boatDataValue->value, boatDataValue->getFormat().c_str()); + if (boatDataValue->getFormat() == "formatWind") { // instance is of type angle + dataValue = (dataValue * slope) + offset; + dataValue = fmod(dataValue, 2 * M_PI); + if (dataValue > (M_PI)) { + dataValue -= (2 * M_PI); + } else if (dataValue < (M_PI * -1)) { + dataValue += (2 * M_PI); + } + } else if (boatDataValue->getFormat() == "formatCourse") { // instance is of type direction + dataValue = (dataValue * slope) + offset; + dataValue = fmod(dataValue, 2 * M_PI); + if (dataValue < 0) { + dataValue += (2 * M_PI); + } + } else if (boatDataValue->getFormat() == "kelvinToC") { // instance is of type temperature + dataValue = ((dataValue - 273.15) * slope) + offset + 273.15; + } else { + dataValue = (dataValue * slope) + offset; + } + + calibrationData.list[listNo].value = dataValue; + calibrationData.list[listNo].isCalibrated = true; + boatDataValue->value = dataValue; + LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s: Offset: %f Slope: %f Result: %f", instance.c_str(), offset, slope, boatDataValue->value); } - } else { - LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s not found in calibration data list", instance.c_str()); } } diff --git a/lib/obp60task/BoatDataCalibration.h b/lib/obp60task/BoatDataCalibration.h index 61db4db..32fa7e9 100644 --- a/lib/obp60task/BoatDataCalibration.h +++ b/lib/obp60task/BoatDataCalibration.h @@ -25,7 +25,6 @@ public: static void calibrateInstance(String instance, GwApi::BoatValue* boatDataValue, GwLog* logger); private: -// GwLog* logger; }; extern CalibrationDataList calibrationData; // this list holds all calibration data diff --git a/lib/obp60task/PageFourValues.cpp b/lib/obp60task/PageFourValues.cpp index fa0d61b..5343de6 100644 --- a/lib/obp60task/PageFourValues.cpp +++ b/lib/obp60task/PageFourValues.cpp @@ -48,7 +48,7 @@ class PageFourValues : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information - CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -58,7 +58,7 @@ class PageFourValues : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information - CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value @@ -68,7 +68,7 @@ class PageFourValues : public Page name3 = name3.substring(0, 6); // String length limit for value name double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information - CalibrationDataList::calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value @@ -78,7 +78,7 @@ class PageFourValues : public Page name4 = name4.substring(0, 6); // String length limit for value name double value4 = bvalue4->value; // Value as double in SI unit bool valid4 = bvalue4->valid; // Valid information - CalibrationDataList::calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value diff --git a/lib/obp60task/PageFourValues2.cpp b/lib/obp60task/PageFourValues2.cpp index 3e84611..24b3fce 100644 --- a/lib/obp60task/PageFourValues2.cpp +++ b/lib/obp60task/PageFourValues2.cpp @@ -48,7 +48,7 @@ class PageFourValues2 : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information - CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -58,7 +58,7 @@ class PageFourValues2 : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information - CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value @@ -68,7 +68,7 @@ class PageFourValues2 : public Page name3 = name3.substring(0, 6); // String length limit for value name double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information - CalibrationDataList::calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value @@ -78,7 +78,7 @@ class PageFourValues2 : public Page name4 = name4.substring(0, 6); // String length limit for value name double value4 = bvalue4->value; // Value as double in SI unit bool valid4 = bvalue4->valid; // Valid information - CalibrationDataList::calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value diff --git a/lib/obp60task/PageOneValue.cpp b/lib/obp60task/PageOneValue.cpp index e839e79..a58bd04 100644 --- a/lib/obp60task/PageOneValue.cpp +++ b/lib/obp60task/PageOneValue.cpp @@ -40,9 +40,9 @@ class PageOneValue : public Page GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) String name1 = xdrDelete(bvalue1->getName()); // Value name name1 = name1.substring(0, 6); // String length limit for value name - double value1 = bvalue1->value; // Value as double in SI unit - bool valid1 = bvalue1->valid; // Valid information - CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated +// double value1 = bvalue1->value; // Value as double in SI unit + bool valid1 = bvalue1->valid; // Valid information + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -54,7 +54,7 @@ class PageOneValue : public Page // Logging boat values if (bvalue1 == NULL) return; - LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1); + LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), bvalue1); // Draw page //*********************************************************** diff --git a/lib/obp60task/PageSixValues.cpp b/lib/obp60task/PageSixValues.cpp index b840864..b3bdeee 100644 --- a/lib/obp60task/PageSixValues.cpp +++ b/lib/obp60task/PageSixValues.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" #include "DSEG7Classic-BoldItalic26pt7b.h" @@ -61,6 +62,7 @@ class PageSixValues : public Page DataName[i] = xdrDelete(bvalue->getName()); DataName[i] = DataName[i].substring(0, 6); // String length limit for value name DataValue[i] = bvalue->value; // Value as double in SI unit + calibrationData.calibrateInstance(DataName[i], bvalue, logger); // Check if boat data value is to be calibrated DataValid[i] = bvalue->valid; DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places DataUnits[i] = formatValue(bvalue, *commonData).unit; diff --git a/lib/obp60task/PageThreeValues.cpp b/lib/obp60task/PageThreeValues.cpp index c4b9702..a90d636 100644 --- a/lib/obp60task/PageThreeValues.cpp +++ b/lib/obp60task/PageThreeValues.cpp @@ -46,7 +46,7 @@ class PageThreeValues : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information - CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -56,7 +56,7 @@ class PageThreeValues : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information - CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value @@ -66,7 +66,7 @@ class PageThreeValues : public Page name3 = name3.substring(0, 6); // String length limit for value name double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information - CalibrationDataList::calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value diff --git a/lib/obp60task/PageTwoValues.cpp b/lib/obp60task/PageTwoValues.cpp index 481b6ba..68dcfa9 100644 --- a/lib/obp60task/PageTwoValues.cpp +++ b/lib/obp60task/PageTwoValues.cpp @@ -44,7 +44,7 @@ class PageTwoValues : public Page name1 = name1.substring(0, 6); // String length limit for value name double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information - CalibrationDataList::calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -54,7 +54,7 @@ class PageTwoValues : public Page name2 = name2.substring(0, 6); // String length limit for value name double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information - CalibrationDataList::calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated + calibrationData.calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index 60c627a..4f59854 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -219,17 +219,6 @@ "obp60":"true" } }, - { - "name": "calOffsetDBT", - "label": "Offset DBT", - "type": "number", - "default": "0.00", - "description": "Offset for depth transducer; positive for depth from surface, negative for depth below keel", - "category": "OBP60 Settings", - "capabilities": { - "obp60":"true" - } - }, { "name": "lengthFormat", "label": "Length Format", @@ -706,6 +695,7 @@ "list": [ "AWA", "AWS", + "DBT", "HDM", "STW", "WTemp" @@ -746,6 +736,7 @@ "list": [ "AWA", "AWS", + "DBT", "HDM", "STW", "WTemp" @@ -777,6 +768,47 @@ "obp60":"true" } }, + { + "name": "calInstance3", + "label": "Calibration Data Instance 3", + "type": "list", + "default": "", + "description": "Data instance for calibration", + "list": [ + "AWA", + "AWS", + "DBT", + "HDM", + "STW", + "WTemp" + ], + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "calOffset3", + "label": "Calibration Data Instance 3 Offset", + "type": "number", + "default": "0.00", + "description": "Offset for data instance 3", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "calSlope3", + "label": "Calibration Data Instance 3 Slope", + "type": "number", + "default": "1.00", + "description": "Slope for data instance 3", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, { "name": "display", "label": "Display Mode", diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index a84f247..bdc28e0 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -386,10 +386,6 @@ void OBP60Task(GwApi *api){ commonData.logger=logger; commonData.config=config; - // Read all calibration data settings from config - CalibrationDataList::readConfig(config, logger); - LOG_DEBUG(GwLog::LOG,"Calibration data read from config"); - #ifdef HARDWARE_V21 // Keyboard coordinates for page footer initKeys(commonData); @@ -529,6 +525,9 @@ void OBP60Task(GwApi *api){ // add out of band system page (always available) Page *syspage = allPages.pages[0]->creator(commonData); + // Read all calibration data settings from config + calibrationData.readConfig(config, logger); + // Display screenshot handler for HTTP request // http://192.168.15.1/api/user/OBP60Task/screenshot api->registerRequestHandler("screenshot", [api, &pageNumber, pages](AsyncWebServerRequest *request) { From 583fbd0db8d9c2eb231db567690b7770fdabc236 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Thu, 15 May 2025 23:13:16 +0200 Subject: [PATCH 03/10] DBT implemented --- lib/obp60task/BoatDataCalibration.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp index ff8ec5a..4ce1cfa 100644 --- a/lib/obp60task/BoatDataCalibration.cpp +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -48,6 +48,13 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) } else if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "HDM") { calibrationData.list[i].offset *= M_PI / 180; // Convert deg to rad + } else if (calibrationData.list[i].instance == "DBT") { + if (lengthFormat == "m") { + // No conversion needed + } else if (lengthFormat == "ft") { + calibrationData.list[i].offset /= 3.28084; // Convert ft to m + } + } else if (calibrationData.list[i].instance == "STW") { if (speedFormat == "m/s") { // No conversion needed From 7ced07d2d945f78285758594ea055dea53bb6662 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Thu, 15 May 2025 23:24:40 +0200 Subject: [PATCH 04/10] TWA, TWS, TWD + Pages Wind, WindRose, WindRoseFlex added --- lib/obp60task/BoatDataCalibration.cpp | 4 ++-- lib/obp60task/PageWind.cpp | 3 +++ lib/obp60task/PageWindRose.cpp | 7 +++++++ lib/obp60task/PageWindRoseFlex.cpp | 7 +++++++ lib/obp60task/config.json | 9 +++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp index 4ce1cfa..3a817c2 100644 --- a/lib/obp60task/BoatDataCalibration.cpp +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -34,7 +34,7 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) calibrationData.list[i].slope = (config->getString(slope, "")).toFloat(); // Convert calibration values to internal standard formats - if (calibrationData.list[i].instance == "AWS") { + if (calibrationData.list[i].instance == "AWS" || calibrationData.list[i].instance == "TWS") { if (windspeedFormat == "m/s") { // No conversion needed } else if (windspeedFormat == "km/h") { @@ -45,7 +45,7 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) calibrationData.list[i].offset *= 0.5; // Convert Bft to m/s (approx) -> to be improved } - } else if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "HDM") { + } else if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "TWA" ||calibrationData.list[i].instance == "TWD" || calibrationData.list[i].instance == "HDM") { calibrationData.list[i].offset *= M_PI / 180; // Convert deg to rad } else if (calibrationData.list[i].instance == "DBT") { diff --git a/lib/obp60task/PageWind.cpp b/lib/obp60task/PageWind.cpp index e629597..272478b 100644 --- a/lib/obp60task/PageWind.cpp +++ b/lib/obp60task/PageWind.cpp @@ -3,6 +3,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" #include "N2kMessages.h" +#include "BoatDataCalibration.h" #define front_width 120 #define front_height 162 @@ -323,6 +324,7 @@ public: } String name1 = bvalue1->getName().c_str(); // Value name name1 = name1.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated double value1 = bvalue1->value; // Value as double in SI unit // bool valid1 = bvalue1->valid; // Valid information String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -336,6 +338,7 @@ public: } String name2 = bvalue2->getName().c_str(); // Value name name2 = name2.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated double value2 = bvalue2->value; // Value as double in SI unit // bool valid2 = bvalue2->valid; // Valid information if (simulation) { diff --git a/lib/obp60task/PageWindRose.cpp b/lib/obp60task/PageWindRose.cpp index 49a7ba3..dba50d1 100644 --- a/lib/obp60task/PageWindRose.cpp +++ b/lib/obp60task/PageWindRose.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageWindRose : public Page { @@ -51,6 +52,7 @@ public: GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) String name1 = xdrDelete(bvalue1->getName()); // Value name name1 = name1.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer @@ -65,6 +67,7 @@ public: GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue) String name2 = xdrDelete(bvalue2->getName()); // Value name name2 = name2.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -78,6 +81,7 @@ public: GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) String name3 = xdrDelete(bvalue3->getName()); // Value name name3 = name3.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -91,6 +95,7 @@ public: GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) String name4 = xdrDelete(bvalue4->getName()); // Value name name4 = name4.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated double value4 = bvalue4->value; // Value as double in SI unit bool valid4 = bvalue4->valid; // Valid information String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -104,6 +109,7 @@ public: GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue) String name5 = xdrDelete(bvalue5->getName()); // Value name name5 = name5.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name5, bvalue5, logger); // Check if boat data value is to be calibrated double value5 = bvalue5->value; // Value as double in SI unit bool valid5 = bvalue5->valid; // Valid information String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -117,6 +123,7 @@ public: GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue) String name6 = xdrDelete(bvalue6->getName()); // Value name name6 = name6.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name6, bvalue6, logger); // Check if boat data value is to be calibrated double value6 = bvalue6->value; // Value as double in SI unit bool valid6 = bvalue6->valid; // Valid information String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places diff --git a/lib/obp60task/PageWindRoseFlex.cpp b/lib/obp60task/PageWindRoseFlex.cpp index 413f047..c8c453b 100644 --- a/lib/obp60task/PageWindRoseFlex.cpp +++ b/lib/obp60task/PageWindRoseFlex.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageWindRoseFlex : public Page { @@ -51,6 +52,7 @@ public: GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) String name1 = xdrDelete(bvalue1->getName()); // Value name name1 = name1.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated double value1 = bvalue1->value; // Value as double in SI unit bool valid1 = bvalue1->valid; // Valid information value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer @@ -65,6 +67,7 @@ public: GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue) String name2 = xdrDelete(bvalue2->getName()); // Value name name2 = name2.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name2, bvalue2, logger); // Check if boat data value is to be calibrated double value2 = bvalue2->value; // Value as double in SI unit bool valid2 = bvalue2->valid; // Valid information String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -78,6 +81,7 @@ public: GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) String name3 = xdrDelete(bvalue3->getName()); // Value name name3 = name3.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name3, bvalue3, logger); // Check if boat data value is to be calibrated double value3 = bvalue3->value; // Value as double in SI unit bool valid3 = bvalue3->valid; // Valid information String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -91,6 +95,7 @@ public: GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) String name4 = xdrDelete(bvalue4->getName()); // Value name name4 = name4.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name4, bvalue4, logger); // Check if boat data value is to be calibrated double value4 = bvalue4->value; // Value as double in SI unit bool valid4 = bvalue4->valid; // Valid information String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -104,6 +109,7 @@ public: GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue) String name5 = xdrDelete(bvalue5->getName()); // Value name name5 = name5.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name5, bvalue5, logger); // Check if boat data value is to be calibrated double value5 = bvalue5->value; // Value as double in SI unit bool valid5 = bvalue5->valid; // Valid information String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places @@ -117,6 +123,7 @@ public: GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue) String name6 = xdrDelete(bvalue6->getName()); // Value name name6 = name6.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name6, bvalue6, logger); // Check if boat data value is to be calibrated double value6 = bvalue6->value; // Value as double in SI unit bool valid6 = bvalue6->valid; // Valid information String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index 4f59854..5c1c298 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -698,6 +698,9 @@ "DBT", "HDM", "STW", + "TWA", + "TWS", + "TWD", "WTemp" ], "category": "OBP60 Calibrations", @@ -739,6 +742,9 @@ "DBT", "HDM", "STW", + "TWA", + "TWS", + "TWD", "WTemp" ], "category": "OBP60 Calibrations", @@ -780,6 +786,9 @@ "DBT", "HDM", "STW", + "TWA", + "TWS", + "TWD", "WTemp" ], "category": "OBP60 Calibrations", From 2c348ca7fb27bd64bdddf65962e73f96266bbdb4 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Fri, 16 May 2025 00:15:12 +0200 Subject: [PATCH 05/10] Revert changes in PageOneValue except of calibration. --- lib/obp60task/PageOneValue.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/obp60task/PageOneValue.cpp b/lib/obp60task/PageOneValue.cpp index a58bd04..eac6cf2 100644 --- a/lib/obp60task/PageOneValue.cpp +++ b/lib/obp60task/PageOneValue.cpp @@ -40,9 +40,9 @@ class PageOneValue : public Page GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) String name1 = xdrDelete(bvalue1->getName()); // Value name name1 = name1.substring(0, 6); // String length limit for value name -// double value1 = bvalue1->value; // Value as double in SI unit - bool valid1 = bvalue1->valid; // Valid information calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated + double value1 = bvalue1->value; // Value as double in SI unit + bool valid1 = bvalue1->valid; // Valid information String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value @@ -54,7 +54,7 @@ class PageOneValue : public Page // Logging boat values if (bvalue1 == NULL) return; - LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), bvalue1); + LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1); // Draw page //*********************************************************** From 3d31fcf4eda7aa039b96dad16cca1de3ad27fea7 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Fri, 16 May 2025 19:43:27 +0200 Subject: [PATCH 06/10] add empty type as default; add calib for types PRPOS,RPOS, and for PageRudderPosition --- lib/obp60task/BoatDataCalibration.cpp | 9 +++++---- lib/obp60task/PageRudderPosition.cpp | 2 ++ lib/obp60task/config.json | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp index 3a817c2..ca12163 100644 --- a/lib/obp60task/BoatDataCalibration.cpp +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -22,10 +22,10 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) String instance = "calInstance" + String(i+1); String offset = "calOffset" + String(i+1); String slope = "calSlope" + String(i+1); - calibrationData.list[i] = { "", 0.0f, 1.0f, 0.0f, false }; + calibrationData.list[i] = { "---", 0.0f, 1.0f, 0.0f, false }; - calibrationData.list[i].instance = config->getString(instance, ""); - if (calibrationData.list[i].instance == "") { + calibrationData.list[i].instance = config->getString(instance, "---"); + if (calibrationData.list[i].instance == "---") { LOG_DEBUG(GwLog::LOG, "no calibration data for instance no. %d", i+1); continue; } @@ -45,7 +45,8 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) calibrationData.list[i].offset *= 0.5; // Convert Bft to m/s (approx) -> to be improved } - } else if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "TWA" ||calibrationData.list[i].instance == "TWD" || calibrationData.list[i].instance == "HDM") { + } else if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "TWA" ||calibrationData.list[i].instance == "TWD" || calibrationData.list[i].instance == "HDM" || + calibrationData.list[i].instance == "PRPOS" || calibrationData.list[i].instance == "RPOS") { calibrationData.list[i].offset *= M_PI / 180; // Convert deg to rad } else if (calibrationData.list[i].instance == "DBT") { diff --git a/lib/obp60task/PageRudderPosition.cpp b/lib/obp60task/PageRudderPosition.cpp index 6883ca2..967f605 100644 --- a/lib/obp60task/PageRudderPosition.cpp +++ b/lib/obp60task/PageRudderPosition.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "BoatDataCalibration.h" class PageRudderPosition : public Page { @@ -40,6 +41,7 @@ public: GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list String name1 = bvalue1->getName().c_str(); // Value name name1 = name1.substring(0, 6); // String length limit for value name + calibrationData.calibrateInstance(name1, bvalue1, logger); // Check if boat data value is to be calibrated value1 = bvalue1->value; // Raw value without unit convertion bool valid1 = bvalue1->valid; // Valid information String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index 5c1c298..b5db57d 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -690,13 +690,16 @@ "name": "calInstance1", "label": "Calibration Data Instance 1", "type": "list", - "default": "", + "default": "---", "description": "Data instance for calibration", "list": [ + "---", "AWA", "AWS", "DBT", "HDM", + "PRPOS", + "RPOS", "STW", "TWA", "TWS", @@ -734,13 +737,16 @@ "name": "calInstance2", "label": "Calibration Data Instance 2", "type": "list", - "default": "", + "default": "---", "description": "Data instance for calibration", "list": [ + "---", "AWA", "AWS", "DBT", "HDM", + "PRPOS", + "RPOS", "STW", "TWA", "TWS", @@ -778,13 +784,16 @@ "name": "calInstance3", "label": "Calibration Data Instance 3", "type": "list", - "default": "", + "default": "---", "description": "Data instance for calibration", "list": [ + "---", "AWA", "AWS", "DBT", "HDM", + "PRPOS", + "RPOS", "STW", "TWA", "TWS", From fb89dca3bec3b5ba73c22e1830ad24373c58a21f Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Fri, 16 May 2025 19:45:58 +0200 Subject: [PATCH 07/10] Fix 'value1 = 0" in PageRudderPosition --- lib/obp60task/PageRudderPosition.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/obp60task/PageRudderPosition.cpp b/lib/obp60task/PageRudderPosition.cpp index 967f605..290a9a6 100644 --- a/lib/obp60task/PageRudderPosition.cpp +++ b/lib/obp60task/PageRudderPosition.cpp @@ -46,20 +46,20 @@ public: bool valid1 = bvalue1->valid; // Valid information String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value + if(valid1 == true){ value1old = value1; // Save old value unit1old = unit1; // Save old unit + } else { + if(simulation == true){ + value1 = (3 + float(random(0, 50)) / 10.0)/360*2*PI; + unit1 = "Deg"; + } + else{ + value1 = 0; + } } - if(simulation == true){ - value1 = (3 + float(random(0, 50)) / 10.0)/360*2*PI; - unit1 = "Deg"; - } - else{ - value1 = 0; - } - - // Optical warning by limit violation (unused) if(String(flashLED) == "Limit Violation"){ setBlinkingLED(false); From 6dbbd13eadde3eaedd52c8b1bf628eec696d2fa9 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Fri, 16 May 2025 20:04:20 +0200 Subject: [PATCH 08/10] Improve code readability --- lib/obp60task/BoatDataCalibration.cpp | 58 ++++++++++++++++----------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp index ca12163..33d4121 100644 --- a/lib/obp60task/BoatDataCalibration.cpp +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -10,6 +10,14 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) // Initial load of calibration data into internal list // This method is called once at init phase of to read the configuration values { + String instance; + double offset; + double slope; + + String calInstance = ""; + String calOffset = ""; + String calSlope = ""; + // Load user format configuration values String lengthFormat = config->getString(config->lengthFormat); // [m|ft] String distanceFormat = config->getString(config->distanceFormat); // [m|km|nm] @@ -19,60 +27,62 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) // Read calibration settings for data instances for (int i = 0; i < maxCalibrationData; i++) { - String instance = "calInstance" + String(i+1); - String offset = "calOffset" + String(i+1); - String slope = "calSlope" + String(i+1); + calInstance = "calInstance" + String(i+1); + calOffset = "calOffset" + String(i+1); + calSlope = "calSlope" + String(i+1); calibrationData.list[i] = { "---", 0.0f, 1.0f, 0.0f, false }; - calibrationData.list[i].instance = config->getString(instance, "---"); - if (calibrationData.list[i].instance == "---") { + instance = config->getString(calInstance, "---"); + if (instance == "---") { LOG_DEBUG(GwLog::LOG, "no calibration data for instance no. %d", i+1); continue; } - - calibrationData.list[i].offset = (config->getString(offset, "")).toFloat(); - calibrationData.list[i].slope = (config->getString(slope, "")).toFloat(); + offset = (config->getString(calOffset, "")).toFloat(); + slope = (config->getString(calSlope, "")).toFloat(); // Convert calibration values to internal standard formats - if (calibrationData.list[i].instance == "AWS" || calibrationData.list[i].instance == "TWS") { + if (instance == "AWS" || instance == "TWS") { if (windspeedFormat == "m/s") { // No conversion needed } else if (windspeedFormat == "km/h") { - calibrationData.list[i].offset /= 3.6; // Convert km/h to m/s + offset /= 3.6; // Convert km/h to m/s } else if (windspeedFormat == "kn") { - calibrationData.list[i].offset /= 1.94384; // Convert kn to m/s + offset /= 1.94384; // Convert kn to m/s } else if (windspeedFormat == "bft") { - calibrationData.list[i].offset *= 0.5; // Convert Bft to m/s (approx) -> to be improved + offset *= 0.5; // Convert Bft to m/s (approx) -> to be improved } - } else if (calibrationData.list[i].instance == "AWA" || calibrationData.list[i].instance == "TWA" ||calibrationData.list[i].instance == "TWD" || calibrationData.list[i].instance == "HDM" || - calibrationData.list[i].instance == "PRPOS" || calibrationData.list[i].instance == "RPOS") { - calibrationData.list[i].offset *= M_PI / 180; // Convert deg to rad + } else if (instance == "AWA" || instance == "TWA" ||instance == "TWD" || instance == "HDM" || + instance == "PRPOS" || instance == "RPOS") { + offset *= M_PI / 180; // Convert deg to rad - } else if (calibrationData.list[i].instance == "DBT") { + } else if (instance == "DBT") { if (lengthFormat == "m") { // No conversion needed } else if (lengthFormat == "ft") { - calibrationData.list[i].offset /= 3.28084; // Convert ft to m + offset /= 3.28084; // Convert ft to m } - } else if (calibrationData.list[i].instance == "STW") { + } else if (instance == "STW") { if (speedFormat == "m/s") { // No conversion needed } else if (speedFormat == "km/h") { - calibrationData.list[i].offset /= 3.6; // Convert km/h to m/s + offset /= 3.6; // Convert km/h to m/s } else if (speedFormat == "kn") { - calibrationData.list[i].offset /= 1.94384; // Convert kn to m/s + offset /= 1.94384; // Convert kn to m/s } - } else if (calibrationData.list[i].instance == "WTemp") { + } else if (instance == "WTemp") { if (tempFormat == "K" || tempFormat == "C") { // No conversion needed } else if (tempFormat == "F") { - calibrationData.list[i].offset *= 9.0 / 5.0; // Convert °F to K - calibrationData.list[i].slope *= 9.0 / 5.0; // Convert °F to K + offset *= 9.0 / 5.0; // Convert °F to K + slope *= 9.0 / 5.0; // Convert °F to K } } + calibrationData.list[i].instance = instance; + calibrationData.list[i].offset = offset; + calibrationData.list[i].slope = slope; calibrationData.list[i].isCalibrated = false; LOG_DEBUG(GwLog::LOG, "stored calibration data: %s, offset: %f, slope: %f", calibrationData.list[i].instance.c_str(), calibrationData.list[i].offset, calibrationData.list[i].slope); } @@ -121,7 +131,7 @@ void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* b return; } else { dataValue = boatDataValue->value; - LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: name: %s: value: %f format: %s", boatDataValue->getName().c_str(), boatDataValue->value, boatDataValue->getFormat().c_str()); + LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: name: %s: value: %f format: %s", boatDataValue->getName().c_str(), boatDataValue->value, boatDataValue->getFormat().c_str()); if (boatDataValue->getFormat() == "formatWind") { // instance is of type angle dataValue = (dataValue * slope) + offset; From 10951d7f1342d9267a34dc64eb5c8257f7ebdc55 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Sat, 17 May 2025 10:58:31 +0200 Subject: [PATCH 09/10] Basis working --- lib/obp60task/BoatDataCalibration.cpp | 49 ++++++++++++++++++----- lib/obp60task/BoatDataCalibration.h | 2 + lib/obp60task/config.json | 56 +++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp index 33d4121..80c3aa7 100644 --- a/lib/obp60task/BoatDataCalibration.cpp +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -3,6 +3,7 @@ #include "BoatDataCalibration.h" #include #include +#include CalibrationDataList calibrationData; @@ -13,10 +14,12 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) String instance; double offset; double slope; + double smooth; String calInstance = ""; String calOffset = ""; String calSlope = ""; + String calSmooth = ""; // Load user format configuration values String lengthFormat = config->getString(config->lengthFormat); // [m|ft] @@ -27,18 +30,20 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) // Read calibration settings for data instances for (int i = 0; i < maxCalibrationData; i++) { - calInstance = "calInstance" + String(i+1); - calOffset = "calOffset" + String(i+1); - calSlope = "calSlope" + String(i+1); - calibrationData.list[i] = { "---", 0.0f, 1.0f, 0.0f, false }; + calInstance = "calInstance" + String(i + 1); + calOffset = "calOffset" + String(i + 1); + calSlope = "calSlope" + String(i + 1); + calSmooth = "calSmooth" + String(i + 1); + calibrationData.list[i] = { "---", 0.0f, 1.0f, 1, 0.0f, false }; instance = config->getString(calInstance, "---"); if (instance == "---") { - LOG_DEBUG(GwLog::LOG, "no calibration data for instance no. %d", i+1); + LOG_DEBUG(GwLog::LOG, "no calibration data for instance no. %d", i + 1); continue; } offset = (config->getString(calOffset, "")).toFloat(); slope = (config->getString(calSlope, "")).toFloat(); + smooth = (config->getString(calSmooth, "")).toInt(); // Convert calibration values to internal standard formats if (instance == "AWS" || instance == "TWS") { @@ -52,8 +57,7 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) offset *= 0.5; // Convert Bft to m/s (approx) -> to be improved } - } else if (instance == "AWA" || instance == "TWA" ||instance == "TWD" || instance == "HDM" || - instance == "PRPOS" || instance == "RPOS") { + } else if (instance == "AWA" || instance == "TWA" || instance == "TWD" || instance == "HDM" || instance == "PRPOS" || instance == "RPOS") { offset *= M_PI / 180; // Convert deg to rad } else if (instance == "DBT") { @@ -62,7 +66,7 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) } else if (lengthFormat == "ft") { offset /= 3.28084; // Convert ft to m } - + } else if (instance == "STW") { if (speedFormat == "m/s") { // No conversion needed @@ -80,11 +84,18 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) slope *= 9.0 / 5.0; // Convert °F to K } } + if (smooth < 0) { + smooth = 0; + } else if (smooth > 9) { + smooth = 9; + } calibrationData.list[i].instance = instance; calibrationData.list[i].offset = offset; calibrationData.list[i].slope = slope; + calibrationData.list[i].smooth = 1 - (smooth / 10.0); // smooth factor is between 0 and 1 calibrationData.list[i].isCalibrated = false; - LOG_DEBUG(GwLog::LOG, "stored calibration data: %s, offset: %f, slope: %f", calibrationData.list[i].instance.c_str(), calibrationData.list[i].offset, calibrationData.list[i].slope); + LOG_DEBUG(GwLog::LOG, "stored calibration data: %s, offset: %f, slope: %f, smoothing: %f", calibrationData.list[i].instance.c_str(), + calibrationData.list[i].offset, calibrationData.list[i].slope, calibrationData.list[i].smooth); } LOG_DEBUG(GwLog::LOG, "all calibration data read"); } @@ -153,6 +164,7 @@ void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* b dataValue = (dataValue * slope) + offset; } + calibrationData.smoothInstance(instance, dataValue, logger); // smooth the boat data value calibrationData.list[listNo].value = dataValue; calibrationData.list[listNo].isCalibrated = true; boatDataValue->value = dataValue; @@ -161,4 +173,23 @@ void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* b } } +void CalibrationDataList::smoothInstance(String instance, double &dataValue, GwLog* logger) +// Method to smoothen the boat data value +{ + // array for last values of smoothed boat data values + static std::unordered_map lastValue; + + double oldValue = 0; + double smoothFactor = calibrationData.list[getInstanceListNo(instance)].smooth; + + if (lastValue.find(instance.c_str()) != lastValue.end()) { + oldValue = lastValue[instance.c_str()]; + + dataValue = oldValue + (smoothFactor * (dataValue - oldValue)); // exponential smoothing algorithm + } + lastValue[instance.c_str()] = dataValue; // store the new value for next cycle; first time, store only the current value and return + + LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s: Smoothed value: %f", instance.c_str(), dataValue); +} + #endif \ No newline at end of file diff --git a/lib/obp60task/BoatDataCalibration.h b/lib/obp60task/BoatDataCalibration.h index 32fa7e9..43a4623 100644 --- a/lib/obp60task/BoatDataCalibration.h +++ b/lib/obp60task/BoatDataCalibration.h @@ -10,6 +10,7 @@ typedef struct { String instance; // data type/instance to be calibrated double offset; // calibration offset double slope; // calibration slope + double smooth; // smoothing factor double value; // calibrated data value bool isCalibrated; // is data instance value calibrated? } CalibData; @@ -23,6 +24,7 @@ public: static void readConfig(GwConfigHandler* config, GwLog* logger); static int getInstanceListNo(String instance); static void calibrateInstance(String instance, GwApi::BoatValue* boatDataValue, GwLog* logger); + void smoothInstance(String instance, double &dataValue, GwLog* logger); private: }; diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index b5db57d..ebce2df 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -713,7 +713,7 @@ }, { "name": "calOffset1", - "label": "Calibration Data Instance 1 Offset", + "label": "Data Instance 1 Calibration Offset", "type": "number", "default": "0.00", "description": "Offset for data instance 1", @@ -724,7 +724,7 @@ }, { "name": "calSlope1", - "label": "Calibration Data Instance 1 Slope", + "label": "Data Instance 1 Calibration Slope", "type": "number", "default": "1.00", "description": "Slope for data instance 1", @@ -733,6 +733,20 @@ "obp60":"true" } }, + { + "name": "calSmooth1", + "label": "Data Instance 1 Smoothing", + "type": "number", + "default": "0", + "check": "checkMinMax", + "min": 0, + "max": 9, + "description": "Smoothing factor for data instance 1", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, { "name": "calInstance2", "label": "Calibration Data Instance 2", @@ -760,7 +774,7 @@ }, { "name": "calOffset2", - "label": "Calibration Data Instance 2 Offset", + "label": "Data Instance 2 Calibration Offset", "type": "number", "default": "0.00", "description": "Offset for data instance 2", @@ -771,7 +785,7 @@ }, { "name": "calSlope2", - "label": "Calibration Data Instance 2 Slope", + "label": "Data Instance 2 Calibration Slope", "type": "number", "default": "1.00", "description": "Slope for data instance 2", @@ -780,6 +794,20 @@ "obp60":"true" } }, + { + "name": "calSmooth2", + "label": "Data Instance 2 Smoothing", + "type": "number", + "default": "0", + "check": "checkMinMax", + "min": 0, + "max": 9, + "description": "Smoothing factor for data instance 2", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, { "name": "calInstance3", "label": "Calibration Data Instance 3", @@ -807,7 +835,7 @@ }, { "name": "calOffset3", - "label": "Calibration Data Instance 3 Offset", + "label": "Data Instance 3 Calibration Offset", "type": "number", "default": "0.00", "description": "Offset for data instance 3", @@ -818,7 +846,7 @@ }, { "name": "calSlope3", - "label": "Calibration Data Instance 3 Slope", + "label": "Data Instance 3 Calibration Slope", "type": "number", "default": "1.00", "description": "Slope for data instance 3", @@ -827,7 +855,21 @@ "obp60":"true" } }, -{ + { + "name": "calSmooth3", + "label": "Data Instance 3 Smoothing", + "type": "number", + "default": "0", + "check": "checkMinMax", + "min": 0, + "max": 9, + "description": "Smoothing factor for data instance 3", + "category": "OBP60 Calibrations", + "capabilities": { + "obp60":"true" + } + }, + { "name": "display", "label": "Display Mode", "type": "list", From fca7a477281ed2ff154d8329443da981eaf2c768 Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Sat, 17 May 2025 12:18:21 +0200 Subject: [PATCH 10/10] smoothing factor adjustments --- lib/obp60task/BoatDataCalibration.cpp | 26 +++++++++++++++++--------- lib/obp60task/config.json | 6 +++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/obp60task/BoatDataCalibration.cpp b/lib/obp60task/BoatDataCalibration.cpp index 80c3aa7..cb2c853 100644 --- a/lib/obp60task/BoatDataCalibration.cpp +++ b/lib/obp60task/BoatDataCalibration.cpp @@ -84,15 +84,23 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger) slope *= 9.0 / 5.0; // Convert °F to K } } - if (smooth < 0) { + + // transform smoothing factor from {0.01..10} to {0.3..0.95} and invert for exponential smoothing formula + if (smooth <= 0) { smooth = 0; - } else if (smooth > 9) { - smooth = 9; + } else { + if (smooth > 10) { + smooth = 10; + } + // calibrationData.list[i].smooth = 1 - (smooth / 10.0); // smooth factor is between 0 and 1 + smooth = 0.3 + ((smooth - 0.01) * (0.95 - 0.3) / (10 - 0.01)); } + smooth = 1 - smooth; + calibrationData.list[i].instance = instance; calibrationData.list[i].offset = offset; calibrationData.list[i].slope = slope; - calibrationData.list[i].smooth = 1 - (smooth / 10.0); // smooth factor is between 0 and 1 + calibrationData.list[i].smooth = smooth; calibrationData.list[i].isCalibrated = false; LOG_DEBUG(GwLog::LOG, "stored calibration data: %s, offset: %f, slope: %f, smoothing: %f", calibrationData.list[i].instance.c_str(), calibrationData.list[i].offset, calibrationData.list[i].slope, calibrationData.list[i].smooth); @@ -132,7 +140,7 @@ void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* b int listNo = getInstanceListNo(instance); if (listNo < 0) { - LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s not found in calibration data list", instance.c_str()); + LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s not found in calibration data list", instance.c_str()); return; } else { offset = calibrationData.list[listNo].offset; @@ -142,7 +150,7 @@ void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* b return; } else { dataValue = boatDataValue->value; - LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: name: %s: value: %f format: %s", boatDataValue->getName().c_str(), boatDataValue->value, boatDataValue->getFormat().c_str()); + LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: value: %f, format: %s", boatDataValue->getName().c_str(), boatDataValue->value, boatDataValue->getFormat().c_str()); if (boatDataValue->getFormat() == "formatWind") { // instance is of type angle dataValue = (dataValue * slope) + offset; @@ -168,12 +176,12 @@ void CalibrationDataList::calibrateInstance(String instance, GwApi::BoatValue* b calibrationData.list[listNo].value = dataValue; calibrationData.list[listNo].isCalibrated = true; boatDataValue->value = dataValue; - LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s: Offset: %f Slope: %f Result: %f", instance.c_str(), offset, slope, boatDataValue->value); + LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: Offset: %f, Slope: %f, Result: %f", instance.c_str(), offset, slope, boatDataValue->value); } } } -void CalibrationDataList::smoothInstance(String instance, double &dataValue, GwLog* logger) +void CalibrationDataList::smoothInstance(String instance, double& dataValue, GwLog* logger) // Method to smoothen the boat data value { // array for last values of smoothed boat data values @@ -189,7 +197,7 @@ void CalibrationDataList::smoothInstance(String instance, double &dataValue, GwL } lastValue[instance.c_str()] = dataValue; // store the new value for next cycle; first time, store only the current value and return - LOG_DEBUG(GwLog::LOG, "BoatDataCalibration: %s: Smoothed value: %f", instance.c_str(), dataValue); + LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: Smoothing factor: %f, Smoothed value: %f", instance.c_str(), smoothFactor, dataValue); } #endif \ No newline at end of file diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index ebce2df..c1801af 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -740,7 +740,7 @@ "default": "0", "check": "checkMinMax", "min": 0, - "max": 9, + "max": 10, "description": "Smoothing factor for data instance 1", "category": "OBP60 Calibrations", "capabilities": { @@ -801,7 +801,7 @@ "default": "0", "check": "checkMinMax", "min": 0, - "max": 9, + "max": 10, "description": "Smoothing factor for data instance 2", "category": "OBP60 Calibrations", "capabilities": { @@ -862,7 +862,7 @@ "default": "0", "check": "checkMinMax", "min": 0, - "max": 9, + "max": 10, "description": "Smoothing factor for data instance 3", "category": "OBP60 Calibrations", "capabilities": {