Mercurial > public > ostc4
view Discovery/Src/tCCR.c @ 866:3a1c533f3840
Zusammenf?hren
author | heinrichsweikamp |
---|---|
date | Mon, 22 Jul 2024 16:40:14 +0200 |
parents | acf6614dc396 |
children | 4d98fb2a178e |
line wrap: on
line source
/////////////////////////////////////////////////////////////////////////////// /// -*- coding: UTF-8 -*- /// /// \file Discovery/Src/tCCR.c /// \brief HUD data via optical port /// \author Heinrichs Weikamp gmbh /// \date 18-Dec-2014 /// /// \details /// /// $Id$ /////////////////////////////////////////////////////////////////////////////// /// \par Copyright (c) 2014-2018 Heinrichs Weikamp gmbh /// /// This program is free software: you can redistribute it and/or modify /// it under the terms of the GNU General Public License as published by /// the Free Software Foundation, either version 3 of the License, or /// (at your option) any later version. /// /// This program is distributed in the hope that it will be useful, /// but WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU General Public License for more details. /// /// You should have received a copy of the GNU General Public License /// along with this program. If not, see <http://www.gnu.org/licenses/>. ////////////////////////////////////////////////////////////////////////////// /* Includes ------------------------------------------------------------------*/ #include <string.h> #include "tCCR.h" #include "ostc.h" #include "data_central.h" #include "data_exchange.h" #include "check_warning.h" #include "configuration.h" #include <math.h> /* Private types -------------------------------------------------------------*/ typedef struct { uint8_t hud_firmwareVersion; bit8_Type status_byte; uint16_t sensor_voltage_100uV[3]; uint8_t sensor_ppo2_cbar[3]; uint8_t temp1; uint16_t battery_voltage_mV; uint16_t checksum; } SIrLink; typedef enum { sensorOK = 0, sensorSuspect, SensorOutOfBounds } sensorTrustState_t; #define HUD_BABBLING_IDIOT (30u) /* 30 Bytes received without break */ #define HUD_RX_FRAME_LENGTH (15u) /* Length of a HUD data frame */ #define HUD_RX_FRAME_BREAK_MS (100u) /* Time used to detect a gap between two byte receptions => frame start */ #define HUD_RX_START_DELAY_MS (500u) /* Delay for start of RX function to avoid start of reception while a transmission is ongoing. */ /* Based on an assumed cycle time by the sensor of 1 second. Started at time of last RX */ #define BOTTLE_SENSOR_TIMEOUT (6000u) /* signal pressure budget as not received after 10 minutes (6000 * 100ms) */ #define MAX_SENSOR_COMPARE_DEVIATION (0.15f) /* max deviation between two sensors allowed before their results are rated as suspect */ #define MAX_SENSOR_VOLTAGE_MV (250u) /* max allowed voltage value for a sensor measurement */ #ifdef ENABLE_ALTERNATIVE_SENSORTYP #define MIN_SENSOR_VOLTAGE_MV (3u) /* min allowed voltage value for a sensor measurement (Inspiration, Submatix, Sentinel Typ) */ #else #define MIN_SENSOR_VOLTAGE_MV (8u) /* min allowed voltage value for a sensor measurement (legacy OSTC TYP) */ #endif /* Private variables ---------------------------------------------------------*/ static SIrLink receiveHUD[2]; static uint8_t boolHUDdata = 0; static uint8_t data_old__lost_connection_to_HUD = 1; static uint8_t receiveHUDraw[16]; static uint8_t StartListeningToUART_HUD = 0; static uint16_t HUDTimeoutCount = 0; static uint16_t ScrubberTimeoutCount = 0; static __IO ITStatus UartReadyHUD = RESET; static uint32_t LastReceivedTick_HUD = 0; /* Private variables with external access via get_xxx() function -------------*/ /* Private function prototypes -----------------------------------------------*/ static uint8_t tCCR_fallbackToFixedSetpoint(void); #ifndef USART_IR_HUD void tCCR_init(void) { } void tCCR_control(void) { } void tCCR_test(void) { } void tCCR_restart(void) { } float get_ppO2Sensor_bar(uint8_t sensor_id) { } float get_sensorVoltage_mV(uint8_t sensor_id) { } float get_HUD_battery_voltage_V(void) { } void tCCR_tick(void) { } #else /* Exported functions --------------------------------------------------------*/ float get_ppO2Sensor_bar(uint8_t sensor_id) { if((sensor_id > 2) || data_old__lost_connection_to_HUD) return 0; return (float)(receiveHUD[boolHUDdata].sensor_ppo2_cbar[sensor_id]) / 100.0f; } float get_sensorVoltage_mV(uint8_t sensor_id) { if((sensor_id > 2) || data_old__lost_connection_to_HUD) return 0; return (float)(receiveHUD[boolHUDdata].sensor_voltage_100uV[sensor_id]) / 10.0f; } float get_HUD_battery_voltage_V(void) { if(data_old__lost_connection_to_HUD) return 0; return (float)(receiveHUD[boolHUDdata].battery_voltage_mV) / 1000.0f; } void test_O2_sensor_values_outOfBounds(int8_t * outOfBouds1, int8_t * outOfBouds2, int8_t * outOfBouds3) { uint8_t sensorNotActiveBinary; uint8_t sensorActive[3]; sensorTrustState_t sensorState[3]; uint8_t index; // test1: user deactivation sensorNotActiveBinary = stateUsed->diveSettings.ppo2sensors_deactivated; for(int i=0;i<3;i++) sensorActive[i] = 1; if(sensorNotActiveBinary) { if(sensorNotActiveBinary & 1) sensorActive[0] = 0; if(sensorNotActiveBinary & 2) sensorActive[1] = 0; if(sensorNotActiveBinary & 4) sensorActive[2] = 0; } // test2: mV of remaining sensors for(index=0; index<3; index++) { sensorState[index] = sensorOK; if(sensorActive[index]) { if(((stateUsed->lifeData.extIf_sensor_map[index] == SENSOR_DIGO2M) && (((SSensorDataDiveO2*)(stateUsed->lifeData.extIf_sensor_data[index]))->status & DVO2_FATAL_ERROR)) || ((stateUsed->lifeData.extIf_sensor_map[index] != SENSOR_DIGO2M) && (((stateUsed->lifeData.sensorVoltage_mV[index] < MIN_SENSOR_VOLTAGE_MV) || (stateUsed->lifeData.sensorVoltage_mV[index] > MAX_SENSOR_VOLTAGE_MV))))) { sensorActive[index] = 0; switch(index) { case 0: sensorNotActiveBinary |= 1; break; case 1: sensorNotActiveBinary |= 2; break; case 2: sensorNotActiveBinary |= 4; break; } } } } *outOfBouds1 = 0; *outOfBouds2 = 0; *outOfBouds3 = 0; /* with two, one or no sensor, there is nothing to compare anymore */ if(sensorNotActiveBinary) { // set outOfBounds for both tests if(!sensorActive[0]) *outOfBouds1 = 1; if(!sensorActive[1]) *outOfBouds2 = 1; if(!sensorActive[2]) *outOfBouds3 = 1; } else { /* Check two or more of Three */ /* compare every sensor with each other. If there is only one mismatch the value might be OK. In case both comparisons fail the sensor is out of bounds */ if(fabsf(stateUsed->lifeData.ppO2Sensor_bar[0] - stateUsed->lifeData.ppO2Sensor_bar[1]) > MAX_SENSOR_COMPARE_DEVIATION) { sensorState[0]++; sensorState[1]++; } if(fabsf(stateUsed->lifeData.ppO2Sensor_bar[0] - stateUsed->lifeData.ppO2Sensor_bar[2]) > MAX_SENSOR_COMPARE_DEVIATION) { sensorState[0]++; sensorState[2]++; } if(fabsf(stateUsed->lifeData.ppO2Sensor_bar[1] - stateUsed->lifeData.ppO2Sensor_bar[2]) > MAX_SENSOR_COMPARE_DEVIATION) { sensorState[1]++; sensorState[2]++; } for(index = 0; index < 3; index++) { if(sensorState[index] == SensorOutOfBounds) { switch(index) { case 0: *outOfBouds1 = 1; break; case 1: *outOfBouds2 = 1; break; case 2: *outOfBouds3 = 1; break; default: break; } } } } } /* this function is called out of the 100ms callback => to be considered for debouncing */ uint8_t get_ppO2SensorWeightedResult_cbar(void) { static uint8_t lastValidValue = 0; int8_t sensorOutOfBound[3]; uint16_t result = 0; uint8_t count = 0; uint8_t retVal = 0; test_O2_sensor_values_outOfBounds(&sensorOutOfBound[0], &sensorOutOfBound[1], &sensorOutOfBound[2]); for(int i=0;i<3;i++) { if(!sensorOutOfBound[i]) { result += stateUsed->lifeData.ppO2Sensor_bar[i] * 100.0; /* convert centibar used by HUB */ count++; } } if(count == 0) /* all sensors out of bounds! => return last valid value as workaround till diver takes action */ { if(debounce_warning_fallback(100)) { set_warning_fallback(); retVal = tCCR_fallbackToFixedSetpoint(); /* this function only changes setpoint if option is enabled */ } if(retVal == 0) { retVal = lastValidValue; } } else { reset_debounce_warning_fallback(); retVal = (uint8_t)(result / count); lastValidValue = retVal; } return retVal; } void tCCR_init(void) { uint8_t loop; StartListeningToUART_HUD = 1; SDiveState* pDiveData = stateRealGetPointerWrite(); for(loop=0;loop<(2*NUM_GASES+1);loop++) { pDiveData->lifeData.bottle_bar_age_MilliSeconds[loop] = BOTTLE_SENSOR_TIMEOUT; } } /* after 3 seconds without update from HUD * data is considered old */ void tCCR_tick(void) { SSettings* pSettings = settingsGetPointer(); if(pSettings->ppo2sensors_source == O2_SENSOR_SOURCE_OPTIC) { if(HUDTimeoutCount < 3 * 10) HUDTimeoutCount++; else { data_old__lost_connection_to_HUD = 1; if(HUDTimeoutCount < 20 * 10) HUDTimeoutCount++; else tCCR_fallbackToFixedSetpoint(); } } /* decrease scrubber timer only in real dive mode, and if we are not bailed out */ if((pSettings->scrubTimerMode != SCRUB_TIMER_OFF) && (isLoopMode(pSettings->dive_mode)) && (stateUsed->mode == MODE_DIVE) && isLoopMode(stateUsed->diveSettings.diveMode)) // && (stateUsed == stateRealGetPointer())) { ScrubberTimeoutCount++; if(ScrubberTimeoutCount >= 600) /* resolution is minutes */ { ScrubberTimeoutCount = 0; if(pSettings->scrubberData[pSettings->scubberActiveId].TimerCur > MIN_SCRUBBER_TIME) { pSettings->scrubberData[pSettings->scubberActiveId].TimerCur--; } translateDate(stateUsed->lifeData.dateBinaryFormat, &pSettings->scrubberData[pSettings->scubberActiveId].lastDive); } } } void tCCR_SetRXIndication(void) { static uint8_t floatingRXCount = 0; if((UartIR_HUD_Handle.RxXferSize == HUD_RX_FRAME_LENGTH) || (UartIR_HUD_Handle.RxXferSize == HUD_RX_FRAME_LENGTH - 1)) /* we expected a complete frame */ { UartReadyHUD = SET; LastReceivedTick_HUD = HAL_GetTick(); floatingRXCount = 0; } else /* follow up of error handling */ { if(time_elapsed_ms(LastReceivedTick_HUD, HAL_GetTick()) > HUD_RX_FRAME_BREAK_MS) /* Reception took a while => frame start detected */ { HAL_UART_Receive_IT(&UartIR_HUD_Handle, &receiveHUDraw[1], 14); /* We have already the first byte => get the missing 14 */ } else { if(floatingRXCount++ < HUD_BABBLING_IDIOT) { HAL_UART_Receive_IT(&UartIR_HUD_Handle, receiveHUDraw, 1); /* Start polling of incoming bytes */ } else /* Significant amount of data comming in without break => disable input */ { /* by not reactivation HUD RX, no recovery fromthis state */ stateUsedWrite->diveSettings.ppo2sensors_deactivated = 0x07; /* Display deactivation */ } } } } void tCCR_restart(void) { HAL_UART_AbortReceive_IT(&UartIR_HUD_Handle); /* Called by the error handler. RX will be restarted by control function */ StartListeningToUART_HUD = 1; } void tCCR_control(void) { uint16_t checksum = 0; #ifdef ENABLE_BOTTLE_SENSOR SDiveState *pLivedata = stateRealGetPointerWrite(); #endif if((UartReadyHUD == RESET) && StartListeningToUART_HUD && (time_elapsed_ms(LastReceivedTick_HUD, HAL_GetTick()) > HUD_RX_START_DELAY_MS)) { StartListeningToUART_HUD = 0; HAL_UART_Receive_IT(&UartIR_HUD_Handle, receiveHUDraw, HUD_RX_FRAME_LENGTH); } if(UartReadyHUD == SET) { UartReadyHUD = RESET; StartListeningToUART_HUD = 1; /* check if received package is valid */ for(int i=0;i<13;i++) { checksum += receiveHUDraw[i]; } receiveHUD[!boolHUDdata].checksum = receiveHUDraw[13] + (256 * receiveHUDraw[14]); if(checksum == receiveHUD[!boolHUDdata].checksum) { #ifdef ENABLE_BOTTLE_SENSOR if(receiveHUDraw[0] == 0xA5) /* code for pressure sensor */ { pLivedata->lifeData.bottle_bar[pLivedata->lifeData.actualGas.GasIdInSettings] = receiveHUDraw[10]; pLivedata->lifeData.bottle_bar_age_MilliSeconds[pLivedata->lifeData.actualGas.GasIdInSettings] = 0; } else #endif /* handle O2 sensor data */ { memcpy(&receiveHUD[!boolHUDdata], receiveHUDraw, 11); receiveHUD[!boolHUDdata].battery_voltage_mV = receiveHUDraw[11] + (256 * receiveHUDraw[12]); } boolHUDdata = !boolHUDdata; HUDTimeoutCount = 0; data_old__lost_connection_to_HUD = 0; } else { if(data_old__lost_connection_to_HUD) /* we lost connection, maybe due to RX shift => start single byte read to resynchronize */ { HAL_UART_Receive_IT(&UartIR_HUD_Handle, receiveHUDraw, 1); StartListeningToUART_HUD = 0; } } memset(receiveHUDraw,0,sizeof(receiveHUDraw)); } } #endif /* Private functions ---------------------------------------------------------*/ static uint8_t tCCR_fallbackToFixedSetpoint(void) { uint8_t retVal = 0; uint8_t setpointCbar, actualGasID; if((stateUsed->mode == MODE_DIVE) && (stateUsed->diveSettings.CCR_Mode == CCRMODE_Sensors) && (stateUsed->diveSettings.fallbackOption)) { if(stateUsed->diveSettings.diveMode == DIVEMODE_CCR) { setpointCbar = stateUsed->diveSettings.setpoint[1].setpoint_cbar; stateUsedWrite->diveSettings.CCR_Mode = CCRMODE_FixedSetpoint; } else { setpointCbar = stateUsed->lifeData.ppo2Simulated_bar * 100; stateUsedWrite->diveSettings.CCR_Mode = CCRMODE_Simulation; } actualGasID = stateUsed->lifeData.actualGas.GasIdInSettings; setActualGas_DM(&stateUsedWrite->lifeData,actualGasID,setpointCbar); set_warning_fallback(); retVal = setpointCbar; } return retVal; }