changeset 747:df0d43da1614

Added pressure compensation to CO2 detection: A pressure compensation is needed if the ExplorIR shall be used under extended pressure conditions. The procedure recommended by the application note has been integrated. To keep things simple the focus of the CO2 measurement is not the precision, as it is needed for the decompression calculation, but the indication of a critical increase of CO2 in the breathing loop. That's why only a lookup table with 1000ppm steps has been implemented instead of calculating the polynom for every measurement.
author Ideenmodellierer
date Sun, 05 Mar 2023 22:06:47 +0100
parents 7e84ae1513b6
children be25ab2d902c
files Discovery/Src/data_exchange_main.c Small_CPU/Src/externalInterface.c Small_CPU/Src/uart.c
diffstat 3 files changed, 58 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/Discovery/Src/data_exchange_main.c	Tue Feb 21 21:19:31 2023 +0100
+++ b/Discovery/Src/data_exchange_main.c	Sun Mar 05 22:06:47 2023 +0100
@@ -57,6 +57,7 @@
 /* Includes ------------------------------------------------------------------*/
 #include <stdlib.h>
 #include <string.h> // for memcpy
+#include <math.h>
 #include "stm32f4xx_hal.h"
 #include "stdio.h"
 #include "ostc.h"
@@ -409,6 +410,10 @@
 				break;
 			case SENSOR_CO2:	SensorActive[SENSOR_CO2] = 1;
 				break;
+#ifdef ENABLE_SENTINEL_MODE
+			case SENSOR_SENTINEL:	SensorActive[SENSOR_SENTINEL] = 1;
+				break;
+#endif
 			default:
 				break;
 		}
@@ -428,7 +433,7 @@
 	}
 
 #ifdef ENABLE_SENTINEL_MODE
-	if(settings->ppo2sensors_source == O2_SENSOR_SOURCE_SENTINEL)
+	if(SensorActive[SENSOR_SENTINEL])
 	{
 			externalInterface_Cmd |= EXT_INTERFACE_33V_ON | EXT_INTERFACE_UART_SENTINEL;
 			externalInterface_Cmd &= (~EXT_INTERFACE_ADC_ON);
@@ -829,9 +834,11 @@
 	uint8_t idx;
 	float meter = 0;
 	SSettings *pSettings;
-
+	
+#ifdef ENABLE_EXTERNAL_PRESSURE
+    float CO2Corr = 0.0;
+#endif
 
-	
 	// wireless - �ltere daten aufr�umen
 #if 0
 	for(int i=0;i<(2*NUM_GASES+1);i++)
@@ -1139,8 +1146,14 @@
 		pStateReal->sensorErrorsRTE = dataIn.sensorErrors;
 
 		/* data from CO2 sensor */
-		pStateReal->lifeData.CO2_data.CO2_ppm = dataIn.data[(dataIn.boolADCO2Data && DATA_BUFFER_CO2)].CO2_ppm;
+		pStateReal->lifeData.CO2_data.CO2_ppm = dataIn.data[(dataIn.boolADCO2Data && DATA_BUFFER_CO2)].CO2_ppm * 10;	/* Scale factor depends on sensor */
 		pStateReal->lifeData.CO2_data.signalStrength = dataIn.data[(dataIn.boolADCO2Data && DATA_BUFFER_CO2)].CO2_signalStrength;
+
+#ifdef ENABLE_EXTERNAL_PRESSURE
+		CO2Corr = 2.811*pow(10,-38)*pow(pStateReal->lifeData.CO2_data.CO2_ppm,6)- 9.817*pow(10,-32)*pow(pStateReal->lifeData.CO2_data.CO2_ppm,5)+1.304*pow(10,-25)*pow(pStateReal->lifeData.CO2_data.CO2_ppm,4)
+				 -8.216*pow(10,-20)*pow(pStateReal->lifeData.CO2_data.CO2_ppm,3)+2.311*pow(10,-14)*pow(pStateReal->lifeData.CO2_data.CO2_ppm,2) - 2.195*pow(10,-9)*pStateReal->lifeData.CO2_data.CO2_ppm - 1.471*pow(10,-3);
+		pStateReal->lifeData.CO2_data.CO2_ppm = pStateReal->lifeData.CO2_data.CO2_ppm / (1.0 + (CO2Corr * ((stateRealGetPointer()->lifeData.pressure_surface_bar * 1000) - ((stateRealGetPointer()->lifeData.ppO2Sensor_bar[2] *1000)))));
+#endif
 	}
 
 	/* apnea specials
--- a/Small_CPU/Src/externalInterface.c	Tue Feb 21 21:19:31 2023 +0100
+++ b/Small_CPU/Src/externalInterface.c	Sun Mar 05 22:06:47 2023 +0100
@@ -30,6 +30,7 @@
 #include "scheduler.h"
 #include "uart.h"
 #include "data_exchange.h"
+#include "pressure.h"
 
 extern SGlobal global;
 extern UART_HandleTypeDef huart1;
@@ -50,6 +51,9 @@
 
 #define ANSWER_CONFBYTE_INDEX		(4u)
 
+#define LOOKUP_CO2_CORR_TABLE_SCALE	(1000u)
+#define LOOKUP_CO2_CORR_TABLE_MAX	(30000u)
+
 static uint8_t activeChannel = 0;			/* channel which is in request */
 static uint8_t recBuf[ADC_ANSWER_LENGTH];
 static uint8_t timeoutCnt = 0;
@@ -66,13 +70,17 @@
 static uint8_t sensorDataId = 0;
 static SSensorDataDiveO2 sensorDataDiveO2;
 static externalInterfaceAutoDetect_t externalAutoDetect = DETECTION_OFF;
-static externalInterfaceSensorType SensorMap[EXT_INTERFACE_SENSOR_CNT] ={ SENSOR_ANALOG, SENSOR_ANALOG, SENSOR_ANALOG, SENSOR_NONE, SENSOR_NONE};
+static externalInterfaceSensorType SensorMap[EXT_INTERFACE_SENSOR_CNT] ={ SENSOR_OPTIC, SENSOR_OPTIC, SENSOR_OPTIC, SENSOR_NONE, SENSOR_NONE};
 static externalInterfaceSensorType tmpSensorMap[EXT_INTERFACE_SENSOR_CNT];
 static externalInterfaceSensorType MasterSensorMap[EXT_INTERFACE_SENSOR_CNT];
 
+static float LookupCO2PressureCorrection[LOOKUP_CO2_CORR_TABLE_MAX / LOOKUP_CO2_CORR_TABLE_SCALE];		/* lookup table for pressure compensation values */
+
 
 void externalInterface_Init(void)
 {
+	uint16_t index;
+	uint16_t coeff;
 	activeChannel = 0;
 	timeoutCnt = 0;
 	externalInterfacePresent = 0;
@@ -83,6 +91,15 @@
 	}
 	global.deviceDataSendToMaster.hw_Info.checkADC = 1;
 
+/* Create a lookup table based on GSS application note AN001: PRESSURE COMPENSATION OF A CO2 SENSOR */
+/* The main purpose of the sensor in the dive application is to be a warning indicator */
+/* => no exact values necessary => a lookup table with 50 entries should be sufficient */
+	LookupCO2PressureCorrection [0] = -0.0014;
+	for(index = 1; index < (LOOKUP_CO2_CORR_TABLE_MAX / LOOKUP_CO2_CORR_TABLE_SCALE); index++)
+	{
+		coeff = index * LOOKUP_CO2_CORR_TABLE_SCALE;
+		LookupCO2PressureCorrection[index] = 2.811*pow(10,-38)*pow(coeff,6)- 9.817*pow(10,-32)*pow(coeff,5)+1.304*pow(10,-25)*pow(coeff,4)-8.216*pow(10,-20)*pow(coeff,3)+2.311*pow(10,-14)*pow(coeff,2) - 2.195*pow(10,-9)*coeff - 1.471*pow(10,-3);
+	}
 	externalInterface_InitDatastruct();
 }
 
@@ -339,7 +356,25 @@
 
 void externalInterface_SetCO2Value(uint16_t CO2_ppm)
 {
-	externalCO2Value = CO2_ppm;
+	float local_ppm = CO2_ppm * 10.0;		/* scalfactor */
+
+#ifndef ENABLE_EXTERNAL_PRESSURE
+	float local_corr = 0.0;
+
+	if (local_ppm >= LOOKUP_CO2_CORR_TABLE_MAX)
+	{
+		local_corr = -0.0014;
+	}
+	else
+	{
+		local_corr = LookupCO2PressureCorrection[((uint16_t) (local_ppm / LOOKUP_CO2_CORR_TABLE_SCALE))];
+	}
+	local_ppm = local_ppm / (1.0 + (local_corr * (get_surface_mbar() - get_pressure_mbar())));
+#else
+/* The external pressure value is passed via ADC channel2 and calibration is done at firmware => just forward sensor data */
+/* compensation is done at firmware side. This is for testing only. Take care the the same algorithm is taken as used for the lookup table */
+#endif
+	externalCO2Value = local_ppm / 10.0;
 }
 
 void externalInterface_SetCO2SignalStrength(uint16_t LED_qa)
--- a/Small_CPU/Src/uart.c	Tue Feb 21 21:19:31 2023 +0100
+++ b/Small_CPU/Src/uart.c	Sun Mar 05 22:06:47 2023 +0100
@@ -208,8 +208,8 @@
 void UART_HandleCO2Data(void)
 {
 	uint8_t localRX = rxReadIndex;
-	uint8_t dataType = 0;
-	uint32_t dataValue = 0;
+	static uint8_t dataType = 0;
+	static uint32_t dataValue = 0;
 	static receiveState_t rxState = RX_Ready;
 	static uint32_t lastReceiveTick = 0;
 
@@ -266,7 +266,8 @@
 						break;
 					case 'Z':			externalInterface_SetCO2Value(dataValue);
 						break;
-					default: break;
+					default:			rxState = RX_Ready;
+						break;
 				}
 			}
 			if(rxState != RX_Data0)	/* reset state machine because message in wrong format */