Mercurial > public > ostc4
view Discovery/Src/check_warning.c @ 891:a7f4451ba69e Evo_2_23
Added hysteresis for ascent speed coloring:
In the previous version acenting with a speed close to the color limits caused a fast changing of the color for depth visiualization. To avoid this "blinking" a hysteresis has been added to the speed evaluation function.
author | ideenmodellierer |
---|---|
date | Tue, 10 Sep 2024 21:16:32 +0200 |
parents | e373e90a48db |
children | 5f38cf765193 |
line wrap: on
line source
/** ****************************************************************************** * @file check_warning.c * @author heinrichs weikamp gmbh * @date 17-Nov-2014 * @version V0.0.1 * @since 17-Nov-2014 * @brief check and set warnings for warnings * @verbatim ============================================================================== ##### How to use ##### ============================================================================== OSTC3 Warnings: niedriger Batteriezustand ( zu hoher oder zu niedriger Sauerstoffpartialdruck (ppO2) 0.2 - 1.6 zu hoher CNS (Gefahr der Sauerstoffvergiftung) 90% zu hohe Gradientenfaktoren 90 - 90 Missachtung der Dekostopps (der �berschrittene Dekostopp wird rot angezeigt) 0 m zu hohe Aufstiegsgeschwindigkeit 30 m/min aGF-Warnung: die Berechnung der Dekompression wird �ber alternative GF-Werte durchgef�hrt Fallback-Warnung bei ausgefallenem Sensor @endverbatim ****************************************************************************** * @attention * * <h2><center>© COPYRIGHT(c) 2014 heinrichs weikamp</center></h2> * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include <stdbool.h> #include "data_exchange.h" #include "check_warning.h" #include "settings.h" #include "decom.h" #include "tCCR.h" #include "tHome.h" #define DEBOUNCE_FALLBACK_TIME_MS (5000u) /* set warning after 5 seconds of pending error condition */ #define SETPOINT_DECO_START_RANGE_M 3.0 #define SWITCH_DEPTH_LOW_MINIMUM_M 1.0 /* Private variables with access ----------------------------------------------*/ static uint8_t betterGasId = 0; static uint8_t betterBailoutGasId = 0; static uint8_t betterSetpointId = 1; static int8_t fallback = 0; static uint16_t debounceFallbackTimeMS = 0; /* Private function prototypes -----------------------------------------------*/ static int8_t check_fallback(SDiveState * pDiveState); static int8_t check_ppO2(SDiveState * pDiveState); static int8_t check_O2_sensors(SDiveState * pDiveState); static int8_t check_CNS(SDiveState * pDiveState); static int8_t check_Deco(SDiveState * pDiveState); static int8_t check_AscentRate(SDiveState * pDiveState); static int8_t check_aGF(SDiveState * pDiveState); static int8_t check_BetterGas(SDiveState * pDiveState); static int8_t check_BetterSetpoint(SDiveState * pDiveState); static int8_t check_Battery(SDiveState * pDiveState); #ifdef ENABLE_BOTTLE_SENSOR static int8_t check_pressureSensor(SDiveState * pDiveState); #endif #ifdef ENABLE_CO2_SUPPORT static int8_t check_co2(SDiveState * pDiveState); #endif static int8_t check_helper_same_oxygen_and_helium_content(SGasLine * gas1, SGasLine * gas2); #ifdef HAVE_DEBUG_WARNINGS static int8_t check_debug(SDiveState * pDiveState); #endif /* Exported functions --------------------------------------------------------*/ void check_warning(void) { check_warning2(stateUsedWrite); } void check_warning2(SDiveState * pDiveState) { pDiveState->warnings.numWarnings = 0; pDiveState->warnings.numWarnings += check_aGF(pDiveState); pDiveState->warnings.numWarnings += check_AscentRate(pDiveState); pDiveState->warnings.numWarnings += check_CNS(pDiveState); pDiveState->warnings.numWarnings += check_Deco(pDiveState); pDiveState->warnings.numWarnings += check_ppO2(pDiveState); pDiveState->warnings.numWarnings += check_O2_sensors(pDiveState); pDiveState->warnings.numWarnings += check_BetterGas(pDiveState); pDiveState->warnings.numWarnings += check_BetterSetpoint(pDiveState); pDiveState->warnings.numWarnings += check_Battery(pDiveState); pDiveState->warnings.numWarnings += check_fallback(pDiveState); #ifdef ENABLE_BOTTLE_SENSOR pDiveState->warnings.numWarnings += check_pressureSensor(pDiveState); #endif #ifdef ENABLE_CO2_SUPPORT pDiveState->warnings.numWarnings += check_co2(pDiveState); #endif #ifdef HAVE_DEBUG_WARNINGS pDiveState->warnings.numWarnings += check_debug(pDiveState); #endif } void set_warning_fallback(void) { fallback = 1; } void clear_warning_fallback(void) { fallback = 0; debounceFallbackTimeMS = 0; } uint8_t actualBetterGasId(void) { return betterGasId; } uint8_t actualBetterBailoutGasId(void) { return betterBailoutGasId; } uint8_t actualBetterSetpointId(void) { return betterSetpointId; } uint8_t actualLeftMaxDepth(const SDiveState * pDiveState) { if(pDiveState->lifeData.depth_meter > (pDiveState->lifeData.max_depth_meter - 3.0f)) return 0; else return 1; } /* Private functions ---------------------------------------------------------*/ static int8_t check_fallback(SDiveState * pDiveState) { if(fallback && ((pDiveState->mode != MODE_DIVE) || (!isLoopMode(pDiveState->diveSettings.diveMode)))) fallback = 0; pDiveState->warnings.fallback = fallback; return pDiveState->warnings.fallback; } static int8_t check_ppO2(SDiveState * pDiveState) { if((pDiveState->mode != MODE_DIVE) || (pDiveState->warnings.fallback)) { pDiveState->warnings.ppO2Low = 0; pDiveState->warnings.ppO2High = 0; return 0; } uint8_t localPPO2, testPPO2high; if(pDiveState->lifeData.ppO2 < 0) localPPO2 = 0; else if(pDiveState->lifeData.ppO2 >= 2.5f) localPPO2 = 255; else localPPO2 = (uint8_t)(pDiveState->lifeData.ppO2 * 100); if((localPPO2 + 1) <= settingsGetPointer()->ppO2_min) pDiveState->warnings.ppO2Low = 1; else pDiveState->warnings.ppO2Low = 0; if(actualLeftMaxDepth(pDiveState)) testPPO2high = settingsGetPointer()->ppO2_max_deco; else testPPO2high = settingsGetPointer()->ppO2_max_std; if(localPPO2 >= (testPPO2high + 1)) pDiveState->warnings.ppO2High = 1; else pDiveState->warnings.ppO2High = 0; return pDiveState->warnings.ppO2Low + pDiveState->warnings.ppO2High; } static int8_t check_O2_sensors(SDiveState * pDiveState) { pDiveState->warnings.sensorLinkLost = 0; pDiveState->warnings.sensorOutOfBounds[0] = 0; pDiveState->warnings.sensorOutOfBounds[1] = 0; pDiveState->warnings.sensorOutOfBounds[2] = 0; if(isLoopMode(pDiveState->diveSettings.diveMode) && (pDiveState->diveSettings.CCR_Mode == CCRMODE_Sensors)) if(settingsGetPointer()->ppo2sensors_source == O2_SENSOR_SOURCE_OPTIC) { { if(!get_HUD_battery_voltage_V()) pDiveState->warnings.sensorLinkLost = 1; } } test_O2_sensor_values_outOfBounds(&pDiveState->warnings.sensorOutOfBounds[0], &pDiveState->warnings.sensorOutOfBounds[1], &pDiveState->warnings.sensorOutOfBounds[2]); return pDiveState->warnings.sensorLinkLost + pDiveState->warnings.sensorOutOfBounds[0] + pDiveState->warnings.sensorOutOfBounds[1] + pDiveState->warnings.sensorOutOfBounds[2]; } static uint8_t getBetterGasId(bool getDiluent, uint8_t startingGasId, SDiveState *diveState) { SDiveSettings diveSettings = diveState->diveSettings; SGasLine localGas; uint8_t betterGasIdLocal = startingGasId; uint8_t bestGasDepth = 255; uint8_t i; uint8_t gasIdOffset; if (getDiluent) { gasIdOffset = NUM_OFFSET_DILUENT; } else { gasIdOffset = 0; } /* life data is float, gas data is uint8 */ if (actualLeftMaxDepth(diveState)) { /* deco gases */ for (i=1+gasIdOffset; i<= 5+gasIdOffset; i++) { memcpy(&localGas,&diveSettings.gas[i],sizeof(SGasLine)); if((localGas.note.ub.first) && (diveSettings.diveMode == DIVEMODE_PSCR)) /* handle first gas as if it would be a deco gas set to MOD */ { localGas.note.ub.active = 1; localGas.note.ub.deco = 1; localGas.depth_meter = calc_MOD(i); } if ((localGas.note.ub.active) && (localGas.note.ub.deco) && (localGas.depth_meter) && (localGas.depth_meter >= (diveState->lifeData.depth_meter - 0.01f )) && (localGas.depth_meter <= bestGasDepth)) { betterGasIdLocal = i; bestGasDepth = diveSettings.gas[i].depth_meter; } } } else { /* travel gases */ bestGasDepth = 0; //check for travalgas for (i = 1 + gasIdOffset; i <= 5 + gasIdOffset; i++) { if ((diveSettings.gas[i].note.ub.active) && (diveSettings.gas[i].note.ub.travel) && (diveSettings.gas[i].depth_meter_travel) && (diveSettings.gas[i].depth_meter_travel <= (diveState->lifeData.depth_meter + 0.01f )) && (diveSettings.gas[i].depth_meter_travel >= bestGasDepth)) { betterGasIdLocal = i; bestGasDepth = diveSettings.gas[i].depth_meter; } } } if((!getDiluent) && (betterGasIdLocal > NUM_OFFSET_DILUENT)) /* an OC gas was requested but Id is pointing to a diluent => return first OC */ { for (i = 1 ; i <= NUM_OFFSET_DILUENT; i++) { if(diveSettings.gas[i].note.ub.first) { betterGasIdLocal = i; break; } } } return betterGasIdLocal; } static int8_t check_BetterGas(SDiveState *diveState) { diveState->warnings.betterGas = 0; if (stateUsed->mode != MODE_DIVE) { betterGasId = 0; return 0; } SDiveSettings diveSettings = diveState->diveSettings; SLifeData lifeData = diveState->lifeData; if (isLoopMode(diveSettings.diveMode)) { betterGasId = getBetterGasId(true, lifeData.actualGas.GasIdInSettings, diveState); betterBailoutGasId = getBetterGasId(false, lifeData.lastDiluent_GasIdInSettings, diveState); } else { betterGasId = getBetterGasId(false, lifeData.actualGas.GasIdInSettings, diveState); } if (betterGasId != lifeData.actualGas.GasIdInSettings && !check_helper_same_oxygen_and_helium_content(&diveSettings.gas[betterGasId], &diveSettings.gas[lifeData.actualGas.GasIdInSettings])) { diveState->warnings.betterGas = 1; } return diveState->warnings.betterGas; } uint8_t getSetpointLowId(void) { SSettings *settings = settingsGetPointer(); if (settings->autoSetpoint) { return SETPOINT_INDEX_AUTO_LOW; } uint8_t setpointLowId = 0; uint8_t setpointLowDepthM = UINT8_MAX; for (unsigned i = 1; i <= NUM_GASES; i++) { if (stateUsed->diveSettings.setpoint[i].depth_meter && stateUsed->diveSettings.setpoint[i].depth_meter < setpointLowDepthM) { setpointLowId = i; setpointLowDepthM = stateUsed->diveSettings.setpoint[i].depth_meter; } } return setpointLowId; } uint8_t getSetpointHighId(void) { SSettings *settings = settingsGetPointer(); if (settings->autoSetpoint) { return SETPOINT_INDEX_AUTO_HIGH; } uint8_t setpointHighId = 0; uint8_t setpointHighDepthM = 0; for (unsigned i = 1; i <= NUM_GASES; i++) { if (stateUsed->diveSettings.setpoint[i].depth_meter && stateUsed->diveSettings.setpoint[i].depth_meter >= setpointHighDepthM) { setpointHighId = i; setpointHighDepthM = stateUsed->diveSettings.setpoint[i].depth_meter; } } return setpointHighId; } uint8_t getSetpointDecoId(void) { SSettings *settings = settingsGetPointer(); if (settings->autoSetpoint && stateUsed->diveSettings.setpoint[SETPOINT_INDEX_AUTO_DECO].note.ub.active) { return SETPOINT_INDEX_AUTO_DECO; } return 0; } /* check for better travel!!! setpoint hw 151210 */ static int8_t check_BetterSetpoint(SDiveState *diveState) { diveState->warnings.betterSetpoint = 0; if (stateUsed->mode != MODE_DIVE) { betterSetpointId = 1; return 0; } SSettings *settings = settingsGetPointer(); float currentDepthM = diveState->lifeData.depth_meter; float lastChangeDepthM = diveState->lifeData.lastSetpointChangeDepthM; if (settings->dive_mode == DIVEMODE_CCR && lastChangeDepthM != currentDepthM) { bool descending = currentDepthM > lastChangeDepthM; uint8_t betterSetpointIdLocal = 0; if (settings->autoSetpoint) { bool decoSetpointEnabled = diveState->diveSettings.setpoint[SETPOINT_INDEX_AUTO_DECO].note.ub.active; const SDecoinfo *decoInfo = getDecoInfo(); uint8_t nextDecoStopDepthM; uint16_t nextDecoStopTimeRemainingS; tHome_findNextStop(decoInfo->output_stop_length_seconds, &nextDecoStopDepthM, &nextDecoStopTimeRemainingS); if (decoSetpointEnabled && nextDecoStopDepthM && currentDepthM < nextDecoStopDepthM + SETPOINT_DECO_START_RANGE_M && !diveState->lifeData.setpointDecoActivated) { betterSetpointIdLocal = SETPOINT_INDEX_AUTO_DECO; diveState->lifeData.setpointDecoActivated = true; } if (descending) { uint8_t switchDepthHighM = diveState->diveSettings.setpoint[SETPOINT_INDEX_AUTO_HIGH].depth_meter; if (lastChangeDepthM < switchDepthHighM && switchDepthHighM < currentDepthM && !diveState->lifeData.setpointDecoActivated) { betterSetpointIdLocal = SETPOINT_INDEX_AUTO_HIGH; } } else { uint8_t switchDepthLowM = diveState->diveSettings.setpoint[SETPOINT_INDEX_AUTO_LOW].depth_meter; if (lastChangeDepthM > SWITCH_DEPTH_LOW_MINIMUM_M && SWITCH_DEPTH_LOW_MINIMUM_M > currentDepthM) { // Avoid draining the oxygen supply by surfacing with a setpoint >= 1.0 betterSetpointIdLocal = SETPOINT_INDEX_AUTO_LOW; diveState->lifeData.setpointLowDelayed = false; } else if (lastChangeDepthM > switchDepthLowM && switchDepthLowM > currentDepthM) { if (nextDecoStopDepthM && settings->delaySetpointLow) { diveState->lifeData.setpointLowDelayed = true; } else { betterSetpointIdLocal = SETPOINT_INDEX_AUTO_LOW; } } } if (!nextDecoStopDepthM) { // Update the state when the decompression obligation ends diveState->lifeData.setpointDecoActivated = false; if (diveState->lifeData.setpointLowDelayed) { betterSetpointIdLocal = SETPOINT_INDEX_AUTO_LOW; diveState->lifeData.setpointLowDelayed = false; } } } else { uint8_t setpointLowId = getSetpointLowId(); uint8_t setpointHighId = getSetpointHighId(); uint8_t betterSetpointSwitchDepthM = descending ? 0 : UINT8_MAX; for (unsigned i = 1; i <= NUM_GASES; i++) { uint8_t switchDepthM = diveState->diveSettings.setpoint[i].depth_meter; if (!switchDepthM || (descending && i == setpointLowId) || (!descending && i == setpointHighId)) { continue; } if ((descending && lastChangeDepthM < switchDepthM && switchDepthM < currentDepthM && switchDepthM > betterSetpointSwitchDepthM) || (!descending && lastChangeDepthM > switchDepthM && switchDepthM > currentDepthM && switchDepthM <= betterSetpointSwitchDepthM)) { betterSetpointIdLocal = i; betterSetpointSwitchDepthM = switchDepthM; } } } if (betterSetpointIdLocal) { betterSetpointId = betterSetpointIdLocal; if (diveState->diveSettings.diveMode == DIVEMODE_CCR && diveState->diveSettings.setpoint[betterSetpointIdLocal].setpoint_cbar != diveState->lifeData.actualGas.setPoint_cbar) { diveState->warnings.betterSetpoint = 1; } } } return diveState->warnings.betterSetpoint; } /* hw 151030 */ static int8_t check_helper_same_oxygen_and_helium_content(SGasLine * gas1, SGasLine * gas2) { if(gas1->helium_percentage != gas2->helium_percentage) return 0; else if(gas1->oxygen_percentage != gas2->oxygen_percentage) return 0; else return 1; } static int8_t check_CNS(SDiveState * pDiveState) { if(stateUsed->mode != MODE_DIVE) { pDiveState->warnings.cnsHigh = 0; return 0; } if(pDiveState->lifeData.cns >= (float)(settingsGetPointer()->CNS_max)) pDiveState->warnings.cnsHigh = 1; else pDiveState->warnings.cnsHigh = 0; return pDiveState->warnings.cnsHigh; } static int8_t check_Battery(SDiveState * pDiveState) { if((pDiveState->lifeData.battery_charge > 0) && (pDiveState->lifeData.battery_charge < 10)) pDiveState->warnings.lowBattery = 1; else pDiveState->warnings.lowBattery = 0; return pDiveState->warnings.lowBattery; } static int8_t check_Deco(SDiveState * pDiveState) { if(stateUsed->mode != MODE_DIVE) { pDiveState->warnings.decoMissed = 0; return 0; } uint8_t depthNext = decom_get_actual_deco_stop(pDiveState); if(!depthNext) pDiveState->warnings.decoMissed = 0; else if(pDiveState->lifeData.depth_meter + 0.1f < (float)depthNext) pDiveState->warnings.decoMissed = 1; else pDiveState->warnings.decoMissed = 0; return pDiveState->warnings.decoMissed; } static int8_t check_AscentRate(SDiveState * pDiveState) { if(stateUsed->mode != MODE_DIVE) { pDiveState->warnings.ascentRateHigh = 0; return 0; } float warnAscentRateFloat; warnAscentRateFloat = (float)(settingsGetPointer()->ascent_MeterPerMinute_max); if(pDiveState->lifeData.ascent_rate_meter_per_min >= warnAscentRateFloat) pDiveState->warnings.ascentRateHigh = 1; else pDiveState->warnings.ascentRateHigh = 0; return pDiveState->warnings.ascentRateHigh; } static int8_t check_aGF(SDiveState * pDiveState) { if(stateUsed->mode != MODE_DIVE) { pDiveState->warnings.aGf = 0; return 0; } pDiveState->warnings.aGf = 0; if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE) { if((pDiveState->diveSettings.gf_high != settingsGetPointer()->GF_high) || (pDiveState->diveSettings.gf_low != settingsGetPointer()->GF_low)) pDiveState->warnings.aGf = 1; } return pDiveState->warnings.aGf; } #ifdef ENABLE_BOTTLE_SENSOR static int8_t check_pressureSensor(SDiveState * pDiveState) { int8_t ret = 0; if(pDiveState->lifeData.bottle_bar_age_MilliSeconds[pDiveState->lifeData.actualGas.GasIdInSettings] < 50) /* we received a new value */ { pDiveState->warnings.newPressure = stateUsed->lifeData.bottle_bar[stateUsed->lifeData.actualGas.GasIdInSettings]; ret = 1; } else { pDiveState->warnings.newPressure = 0; } return ret; } #endif #ifdef ENABLE_CO2_SUPPORT static int8_t check_co2(SDiveState * pDiveState) { if((pDiveState->mode != MODE_DIVE) || (settingsGetPointer()->co2_sensor_active == 0)) { pDiveState->warnings.co2High = 0; } else { if(pDiveState->lifeData.CO2_data.CO2_ppm > CO2_ALARM_LEVEL_PPM) { pDiveState->warnings.co2High = 1; } else { pDiveState->warnings.co2High = 0; } } return pDiveState->warnings.co2High; } #endif #ifdef HAVE_DEBUG_WARNINGS static int8_t check_debug(SDiveState * pDiveState) { uint8_t index = 0; pDiveState->warnings.debug = 0; if((settingsGetPointer()->ppo2sensors_source == O2_SENSOR_SOURCE_DIGITAL) || (settingsGetPointer()->ppo2sensors_source == O2_SENSOR_SOURCE_ANADIG)) { for(index=0; index<3; index++) { if(((pDiveState->lifeData.extIf_sensor_map[index] == SENSOR_DIGO2M) && (((SSensorDataDiveO2*)(stateUsed->lifeData.extIf_sensor_data[index]))->status & DVO2_FATAL_ERROR))) { pDiveState->warnings.debug = 1; } } } return pDiveState->warnings.debug; } #endif uint8_t debounce_warning_fallback(uint16_t debounceStepms) { uint8_t retVal = 0; debounceFallbackTimeMS += debounceStepms; if(debounceFallbackTimeMS > DEBOUNCE_FALLBACK_TIME_MS) { debounceFallbackTimeMS = DEBOUNCE_FALLBACK_TIME_MS; retVal = 1; } return retVal; } void reset_debounce_warning_fallback() { debounceFallbackTimeMS = 0; } /************************ (C) COPYRIGHT heinrichs weikamp *****END OF FILE****/