diff Discovery/Src/buehlmann.c @ 38:5f11787b4f42

include in ostc4 repository
author heinrichsweikamp
date Sat, 28 Apr 2018 11:52:34 +0200
parents
children 8f8ea3a32e82
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Discovery/Src/buehlmann.c	Sat Apr 28 11:52:34 2018 +0200
@@ -0,0 +1,867 @@
+/*	getrennte Gase für die verschiedenen Modi
+		um Gaswechsel Einträge zu vereinfachen
+		das heisst:
+		oc == bailout in cc mode
+*/
+
+/* Konvention:
+float extExample_variable_can_be_used_with_extern;
+*/
+
+#include <string.h>
+//#include "arm_math.h"
+#include <math.h>
+#include <stdbool.h>
+#include "buehlmann.h"
+#include "decom.h"
+
+
+extern  const float helium_time_constant[16];
+extern  const float nitrogen_time_constant[16];
+
+extern const float buehlmann_N2_a[];
+extern const float buehlmann_N2_b[];
+extern const float buehlmann_He_a[];
+extern const float buehlmann_He_b[];
+
+
+/*
+typedef struct
+{
+	float *pointer_array_tissue_nitrogen_bar;
+	float *pointer_array_tissue_helium_bar;
+	char gf_value;
+
+	float output_ceiling_ambient_bar_or_input;
+	_Bool output_ceiling_tolerated_if_ceiling_used_as_input;
+}		tissue_test_tolerance_struct;
+*/
+typedef struct
+{
+	float depth;
+	int id;
+} SStop;
+
+#define DECO_STOPS_MAX_TTS_CALCULATON_IN_SECONDS 59940 // 999 minuten; before: 18000 // 5(h) * 60(min) * 60 sec = 18000 sec
+#define DECO_STOPS_MAX_TTS_FOR_EVERY_SECOND_CALC_IN_SECONDS 7200
+#define NINETY_NINE_MINUTES_IN_SECONDS 59940
+
+# define PRESSURE_TEN_METER 1.0f
+# define PRESSURE_THREE_METER 0.333334f
+# define PRESSURE_150_CM 0.15f
+# define PRESSURE_HALF_METER 0.05f
+/*
+# define PRESSURE_150_CM_MBAR 150
+# define PRESSURE_TWO_M_MBAR 200
+# define PRESSURE_FIVE_M_MBAR 500
+# define PRESSURE_TEN_M_MBAR 1000
+# define PRESSURE_120_METER 12.0
+*/
+/*
+_____________________________________________________________
+*/
+
+
+void buehlmann_backup_and_restore(_Bool backup_restore_otherwise);
+float tissue_tolerance(void);
+void ambient_bar_to_deco_stop_depth_bar(float ceiling);
+int ascend_with_all_gaschanges(float pressure_decrease);
+float next_stop_depth_input_is_actual_stop_id(int actual_id);
+float get_gf_at_pressure(float pressure);
+void buehlmann_calc_ndl(void);
+_Bool dive1_check_deco(void);
+uint8_t buehlmann_tissue_test_tolerance(float depth_in_bar_absolute);
+
+/*
+_____________________________________________________________
+*/
+
+SDecoinfo gDecotable;
+float gSurface_pressure_bar;
+
+float gPressure;
+int gGas_id;
+float gTTS;
+float gTissue_nitrogen_bar[16];
+float gTissue_helium_bar[16];
+float	gGF_value;
+float gCNS;
+//float gMax_ceiling_bar = 0;
+int gNDL;
+
+
+//SLifeData *pLifeData;
+SDiveSettings *pBuDiveSettings;
+SDecoinfo* pDecolistBuehlmann;
+//signed char gGaschange_decreasing_depth_gas_id[BUEHLMANN_STRUCT_MAX_GASES];
+float gGF_low_depth_bar;
+SStop gStop;
+
+void buehlmann_init(void)
+{
+  //gMax_ceiling_bar = 0;
+}
+
+void buehlmann_backup_and_restore(_Bool backup_restore_otherwise)
+{
+	static float pressure;
+	static float gas_id;
+	static float tts;
+	static float tissue_nitrogen_bar[16];
+	static float tissue_helium_bar[16];
+	static float gf_value;
+	static int ndl;
+	static float cns;
+
+	if(backup_restore_otherwise)
+	{
+		pressure = gPressure;
+		gas_id = gGas_id;
+		tts = gTTS;
+		gf_value = gGF_value;
+		ndl = gNDL;
+		cns = gCNS;
+		memcpy(tissue_nitrogen_bar, gTissue_nitrogen_bar, (4*16));
+		memcpy(tissue_helium_bar, gTissue_helium_bar, (4*16));
+	}
+	else
+	{
+		gPressure = pressure;
+		gGas_id = gas_id;
+		gTTS = tts;
+		gGF_value = gf_value;
+		gNDL = ndl;
+		gCNS = cns;
+		memcpy(gTissue_nitrogen_bar, tissue_nitrogen_bar, (4*16));
+		memcpy(gTissue_helium_bar, tissue_helium_bar, (4*16));
+	}
+
+}
+/*void buehlmann__test__saturate_tissues(SBuehlmann *pInput,  int seconds)
+{
+	pBuehlmann = pInput;
+	pInput->dive_time_seconds += seconds;
+	// internal copying
+	gSurface_pressure_bar = pBuehlmann->pressure_surface_bar;
+
+	gPressure = pBuehlmann->pressure_ambient_bar;
+	gGas_id = pBuehlmann->actual_gas_id;
+	memcpy(gTissue_nitrogen_bar, pBuehlmann->tissue_nitrogen_bar, (4*16));
+	memcpy(gTissue_helium_bar, pBuehlmann->tissue_helium_bar, (4*16));
+
+	tissues_exposure_at_gPressure_seconds(seconds);
+
+	memcpy(pBuehlmann->tissue_nitrogen_bar, gTissue_nitrogen_bar, (4*16));
+	memcpy(pBuehlmann->tissue_helium_bar, gTissue_helium_bar, (4*16));
+}*/
+
+float buehlmann_get_gCNS(void)
+{
+	return gCNS;
+}
+
+void buehlmann_calc_deco(SLifeData* pLifeData, SDiveSettings * pDiveSettings, SDecoinfo * pDecoInfo)
+{
+	float ceiling;
+	int ascend_time;
+	int tts_seconds;
+	float pressure_delta;
+	float next_depth;
+	_Bool deco_reached = false;
+//	tissue_test_tolerance_struct tolerance_data;
+	unsigned short *stoplist;
+	int i;
+	
+   // decom_CreateGasChangeList(pDiveSettings, pLifeData);
+
+	gCNS = 0;
+  pDecoInfo->output_time_to_surface_seconds = 0;
+	pDecoInfo->output_ndl_seconds = 0;
+	for(int i=0;i<DECOINFO_STRUCT_MAX_STOPS;i++)
+	{
+		pDecoInfo->output_stop_length_seconds[i] = 0;
+	}
+	/* make input available global*/
+	pBuDiveSettings = pDiveSettings;
+
+  pDecolistBuehlmann = pDecoInfo;
+	/* internal copying */
+	gSurface_pressure_bar = pLifeData->pressure_surface_bar;
+
+	gPressure = pLifeData->pressure_ambient_bar;
+	gGas_id = 0;
+	memcpy(gTissue_nitrogen_bar, pLifeData->tissue_nitrogen_bar, (4*16));
+	memcpy(gTissue_helium_bar, pLifeData->tissue_helium_bar, (4*16));
+	gGF_value = ((float)pBuDiveSettings->gf_low) / 100.0f;
+	
+	//
+	memcpy(&gDecotable, pDecolistBuehlmann, sizeof(SDecoinfo));
+	stoplist = gDecotable.output_stop_length_seconds;
+
+
+  if(pLifeData->dive_time_seconds < 60)
+    return;
+	/* coupling */
+
+	/* functions */
+
+	// clean stop list
+	for(i = 0; i < DECOINFO_STRUCT_MAX_STOPS; i++)
+		stoplist[i] = 0;
+	gTTS = 0;
+	gNDL = 0;
+
+  if(pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero >= (gPressure - PRESSURE_150_CM))
+  {
+    deco_reached = true;
+  }
+
+
+//ascend_with_all_gaschanges(gPressure - gSurface_pressure_bar);
+	gGF_value = ((float)pBuDiveSettings->gf_high) / 100.0f;
+	//iling = tissue_tolerance();
+	// includes backup for gGF_value
+	// NDL
+	buehlmann_backup_and_restore(true); // includes backup for gGF_value
+	if(!dive1_check_deco() )
+	{
+	  buehlmann_backup_and_restore(false);
+		// no deco
+		pDecolistBuehlmann->output_time_to_surface_seconds = 0;
+		for(i = 0; i < DECOINFO_STRUCT_MAX_STOPS; i++)
+			pDecolistBuehlmann->output_stop_length_seconds[i] = 0;
+		// calc NDL
+		buehlmann_calc_ndl();
+		pDecolistBuehlmann->output_ndl_seconds = gNDL;
+		return;
+	}
+	buehlmann_backup_and_restore(false);
+	pDecolistBuehlmann->output_ndl_seconds = 0;
+
+  gGF_value = get_gf_at_pressure(gPressure);
+	//current ceiling at actual position
+	ceiling = tissue_tolerance();
+	//if(ceiling < pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero)
+		//ambient_bar_to_deco_stop_depth_bar(pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero);
+	//else
+  ambient_bar_to_deco_stop_depth_bar(ceiling);
+
+	// set the base for all upcoming parameters
+	ceiling = gStop.depth + gSurface_pressure_bar;
+	tts_seconds = 0;
+
+	// modify parameters if there is ascend or parameter fine adjustment
+	if(ceiling < (gPressure - PRESSURE_150_CM)) // more than 1.5 meter below ceiling
+	{
+		// ascend within 10 mtr to GF_low // speed 12 mtr/min -> 50 sec / 10 mtr;  15 sec / 3 mtr.
+		if(ceiling < (gPressure - PRESSURE_TEN_METER) )
+		{ do {
+				ascend_time = ascend_with_all_gaschanges(PRESSURE_TEN_METER);
+				tts_seconds += ascend_time;
+				ceiling = tissue_tolerance();
+				if(tts_seconds > DECO_STOPS_MAX_TTS_CALCULATON_IN_SECONDS)
+			{
+				/* pInput == pBuehlmann */
+				pDecolistBuehlmann->output_time_to_surface_seconds = NINETY_NINE_MINUTES_IN_SECONDS;
+				return;// NINETY_NINE_MINUTES_IN_SECONDS;
+			}
+			} while ((ascend_time > 0 ) && ((gPressure - PRESSURE_TEN_METER ) > gSurface_pressure_bar) && (ceiling < (gPressure - PRESSURE_TEN_METER)));
+		}
+		do {
+			buehlmann_backup_and_restore(true);
+			ascend_time = ascend_with_all_gaschanges(PRESSURE_THREE_METER);
+			tts_seconds += ascend_time;
+			ceiling = tissue_tolerance();
+			if(tts_seconds > DECO_STOPS_MAX_TTS_CALCULATON_IN_SECONDS)
+			{
+				/* pInput == pBuehlmann */
+				pDecolistBuehlmann->output_time_to_surface_seconds = NINETY_NINE_MINUTES_IN_SECONDS;
+				return;// NINETY_NINE_MINUTES_IN_SECONDS;
+			}
+			ambient_bar_to_deco_stop_depth_bar(ceiling);
+		} while ((ascend_time > 0 ) &&  ((gStop.depth + gSurface_pressure_bar) < gPressure));
+
+		if(gStop.depth + gSurface_pressure_bar > gPressure)
+		{
+			gPressure += PRESSURE_THREE_METER;
+			buehlmann_backup_and_restore(false);
+			tts_seconds -= ascend_time;
+		}
+    // calculate first stop based on tissue saturation within 10 meters of stop
+    //ambient_bar_to_deco_stop_depth_bar(ceiling);
+	}
+	else
+	{
+		// initial values, upper code might not be executed (is within 150 cm)
+	}
+
+
+
+
+  if(ceiling > gSurface_pressure_bar)
+  {
+
+    ceiling = gStop.depth + gSurface_pressure_bar;
+    // ascend the last meters to first stop (especially consider any gas changes around)
+    pressure_delta = gPressure - ceiling;
+    ascend_time = (int)ceil(pressure_delta * 50.0f);
+    tts_seconds += ascend_with_all_gaschanges(pressure_delta);
+  }
+	// NDL check
+	if(ceiling <= gSurface_pressure_bar)
+	{
+		/* pInput == pBuehlmann  same pointer*/
+		// NDL with GF_low
+		pDecolistBuehlmann->output_time_to_surface_seconds = 0;
+		return;
+	}
+  if(ceiling >pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero)
+    pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero = ceiling;
+
+	// calc gf loop
+	if(deco_reached)
+		gGF_low_depth_bar = pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero - gSurface_pressure_bar;
+	else
+		gGF_low_depth_bar = ceiling - gSurface_pressure_bar;
+
+	while(gStop.depth > 0)
+	{
+		do
+		{
+			next_depth = next_stop_depth_input_is_actual_stop_id(gStop.id);
+      gGF_value = get_gf_at_pressure(next_depth + gSurface_pressure_bar);
+			buehlmann_backup_and_restore(true);
+			ascend_time = ascend_with_all_gaschanges(gStop.depth - next_depth);
+			ceiling = tissue_tolerance();
+			/* pre check actual limit */
+			if(gDecotable.output_stop_length_seconds[gStop.id] >= 999*60)
+			{
+				tts_seconds -= 999*60 - gDecotable.output_stop_length_seconds[gStop.id];
+				gDecotable.output_stop_length_seconds[gStop.id] = 999*60;
+			}
+			else
+			/* more deco on the actual depth */
+			if(ceiling > next_depth + gSurface_pressure_bar)
+			{
+				next_depth = -1;
+				buehlmann_backup_and_restore(false);
+				decom_tissues_exposure2(10, &pBuDiveSettings->decogaslist[gGas_id], gPressure,gTissue_nitrogen_bar,gTissue_helium_bar); // some seconds at least at each stop
+				decom_oxygen_calculate_cns_exposure(10, &pBuDiveSettings->decogaslist[gGas_id], gPressure, &gCNS);
+        gDecotable.output_stop_length_seconds[gStop.id] += 10;
+        tts_seconds += 10;
+			}
+		} while(next_depth == -1);
+    tts_seconds += ascend_time;
+		gStop.depth = next_depth;
+    for(i = gGas_id + 1; i < BUEHLMANN_STRUCT_MAX_GASES; i++)
+    {
+        if(pBuDiveSettings->decogaslist[i].change_during_ascent_depth_meter_otherwise_zero == 0)
+            break;
+        float pressureChange =  ((float)pBuDiveSettings->decogaslist[i].change_during_ascent_depth_meter_otherwise_zero) / 10;
+        if(gStop.depth <= pressureChange + 0.00001f)
+        {
+            gGas_id = i;
+        }
+        else
+        {
+            break;
+        }
+    }
+		gStop.id--;
+	}
+
+  gDecotable.output_time_to_surface_seconds = tts_seconds;
+	memcpy(pDecolistBuehlmann, &gDecotable, sizeof(SDecoinfo));
+}
+
+
+float tissue_tolerance(void)
+{
+	float tissue_inertgas_saturation;
+	float inertgas_a;
+	float inertgas_b;
+	float ceiling;
+	float global_ceiling;
+	int ci;
+
+	global_ceiling = -1;
+
+	for (ci = 0; ci < 16; ci++)
+	{
+		if(gTissue_helium_bar[ci] == 0)
+		{
+			tissue_inertgas_saturation = gTissue_nitrogen_bar[ci];
+			//
+			inertgas_a = buehlmann_N2_a[ci];
+			inertgas_b = buehlmann_N2_b[ci];
+		}
+		else
+		{
+			tissue_inertgas_saturation =  gTissue_nitrogen_bar[ci] + gTissue_helium_bar[ci];
+			//
+			inertgas_a = ( ( buehlmann_N2_a[ci] *  gTissue_nitrogen_bar[ci]) + ( buehlmann_He_a[ci] * gTissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
+			inertgas_b = ( ( buehlmann_N2_b[ci] *  gTissue_nitrogen_bar[ci]) + ( buehlmann_He_b[ci] * gTissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
+		}
+		//
+		ceiling = (inertgas_b * ( tissue_inertgas_saturation - gGF_value * inertgas_a ) ) / (gGF_value - (inertgas_b * gGF_value) + inertgas_b);
+		if(ceiling > global_ceiling)
+			global_ceiling = ceiling;
+	}
+	return global_ceiling;
+}
+
+// hw 161121 for relative gradient
+float tissue_tolerance_without_gf_correction(float *tissue_inertgas_saturation_output)
+{
+	float tissue_inertgas_saturation;
+	float inertgas_a;
+	float inertgas_b;
+	float ceiling;
+	float global_ceiling;
+	int ci;
+
+	global_ceiling = -1;
+
+	for (ci = 0; ci < 16; ci++)
+	{
+		if(gTissue_helium_bar[ci] == 0)
+		{
+			tissue_inertgas_saturation = gTissue_nitrogen_bar[ci];
+			//
+			inertgas_a = buehlmann_N2_a[ci];
+			inertgas_b = buehlmann_N2_b[ci];
+		}
+		else
+		{
+			tissue_inertgas_saturation =  gTissue_nitrogen_bar[ci] + gTissue_helium_bar[ci];
+			//
+			inertgas_a = ( ( buehlmann_N2_a[ci] *  gTissue_nitrogen_bar[ci]) + ( buehlmann_He_a[ci] * gTissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
+			inertgas_b = ( ( buehlmann_N2_b[ci] *  gTissue_nitrogen_bar[ci]) + ( buehlmann_He_b[ci] * gTissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
+		}
+		//
+		ceiling = inertgas_b * ( tissue_inertgas_saturation - inertgas_a );
+		if(ceiling > global_ceiling)
+		{
+			global_ceiling = ceiling;
+			if(tissue_inertgas_saturation_output)
+			{
+				*tissue_inertgas_saturation_output = tissue_inertgas_saturation;
+			}
+		}
+	}
+	return global_ceiling;
+}
+
+
+uint8_t buehlmann_tissue_test_tolerance(float depth_in_bar_absolute)
+{
+	float tissue_inertgas_saturation;
+	float inertgas_a;
+	float inertgas_b;
+	float inertgas_tolerance;
+	float gf_minus_1;
+
+	gf_minus_1 = gGF_value - 1.0f;
+
+	for (int ci = 0; ci < 16; ci++)
+	{
+		if(gTissue_helium_bar[ci] == 0)
+		{
+			tissue_inertgas_saturation = gTissue_nitrogen_bar[ci];
+			//
+			inertgas_a = buehlmann_N2_a[ci];
+			inertgas_b = buehlmann_N2_b[ci];
+		}
+		else
+		{
+			tissue_inertgas_saturation =  gTissue_nitrogen_bar[ci] + gTissue_helium_bar[ci];
+			//
+			inertgas_a = ( ( buehlmann_N2_a[ci] *  gTissue_nitrogen_bar[ci]) + ( buehlmann_He_a[ci] * gTissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
+			inertgas_b = ( ( buehlmann_N2_b[ci] *  gTissue_nitrogen_bar[ci]) + ( buehlmann_He_b[ci] * gTissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
+		}
+		//
+		inertgas_tolerance = ( (gGF_value / inertgas_b - gf_minus_1) * depth_in_bar_absolute ) + ( gGF_value * inertgas_a );
+		//
+		if(inertgas_tolerance < tissue_inertgas_saturation)
+			return 0;
+	}
+	return 1;
+}
+
+
+void ambient_bar_to_deco_stop_depth_bar(float ceiling)
+{
+	int i;
+
+	ceiling -= gSurface_pressure_bar;
+
+	if(ceiling <= 0)
+	{
+		gStop.depth = pBuDiveSettings->last_stop_depth_bar;
+		gStop.id = 0;
+		return;
+	}
+
+
+    //for(int i = 1; i < 10; i++)
+
+	if((ceiling -  pBuDiveSettings->last_stop_depth_bar) <= 0)
+	{
+		gStop.depth =  pBuDiveSettings->last_stop_depth_bar;
+		gStop.id = 0;
+		return;
+	}
+
+	gStop.depth = pBuDiveSettings->input_second_to_last_stop_depth_bar;
+	gStop.id = 1;
+	ceiling -= pBuDiveSettings->input_second_to_last_stop_depth_bar;
+
+	if(ceiling <= 0)
+		return;
+
+	for(i = 1; i < (DECOINFO_STRUCT_MAX_STOPS - 2); i++)
+	{
+		ceiling -= pBuDiveSettings->input_next_stop_increment_depth_bar;
+		if(ceiling <= 0)
+			break;
+	}
+	gStop.depth += i * pBuDiveSettings->input_next_stop_increment_depth_bar;
+	gStop.id += i;
+	return;
+}
+
+float next_stop_depth_input_is_actual_stop_id(int actual_id)
+{
+	if(actual_id == 0)
+		return 0;
+
+	if(actual_id == 1)
+		return pBuDiveSettings->last_stop_depth_bar;
+
+	actual_id -= 2;
+	return pBuDiveSettings->input_second_to_last_stop_depth_bar + (actual_id * pBuDiveSettings->input_next_stop_increment_depth_bar);
+}
+
+int ascend_with_all_gaschanges(float pressure_decrease)
+{
+	float pressureTop, pressureTop_tmp, pressureBottom, pressureChange, ascendrate_in_seconds_for_one_bar, pressure_difference;
+	int time_for_ascend;
+	int seconds;
+	int i;
+
+	ascendrate_in_seconds_for_one_bar = 60 * 10 / pBuDiveSettings->ascentRate_meterperminute;
+
+	if(fabsf(gPressure - gSurface_pressure_bar) < PRESSURE_HALF_METER)
+	{
+		gPressure = gSurface_pressure_bar;
+		return 0;
+	}
+
+	pressureTop = gPressure - pressure_decrease;
+	if( gSurface_pressure_bar > pressureTop)
+		pressureTop = gSurface_pressure_bar;
+	pressureBottom = gPressure;
+	seconds = 0;
+    do{
+        pressureTop_tmp = pressureTop;
+        for(i = gGas_id + 1; i < BUEHLMANN_STRUCT_MAX_GASES; i++)
+        {
+            if(pBuDiveSettings->decogaslist[i].change_during_ascent_depth_meter_otherwise_zero == 0)
+                break;
+            pressureChange = gSurface_pressure_bar + ((float)pBuDiveSettings->decogaslist[i].change_during_ascent_depth_meter_otherwise_zero) / 10;
+            if(pressureBottom <= pressureChange)
+            {
+                gGas_id = i;
+            }
+            else
+            {
+                 break;
+            }
+
+        }
+        for(i = gGas_id + 1; i < BUEHLMANN_STRUCT_MAX_GASES; i++)
+        {
+            if(pBuDiveSettings->decogaslist[i].change_during_ascent_depth_meter_otherwise_zero == 0)
+                break;
+            pressureChange = gSurface_pressure_bar + ((float)pBuDiveSettings->decogaslist[i].change_during_ascent_depth_meter_otherwise_zero)/ 10;
+            if((pressureChange < pressureBottom) && (pressureChange > pressureTop))
+            {
+                pressureTop_tmp = pressureChange;
+            }
+        }
+        pressure_difference = pressureBottom - pressureTop_tmp;
+        if(pressure_difference > 0.0001f)
+        {
+          time_for_ascend = (int)ceilf(pressure_difference * ascendrate_in_seconds_for_one_bar);
+          decom_tissues_exposure_stage_schreiner(time_for_ascend, &pBuDiveSettings->decogaslist[gGas_id],
+                                              pressureBottom, pressureTop_tmp, gTissue_nitrogen_bar, gTissue_helium_bar);
+					decom_oxygen_calculate_cns_stage_SchreinerStyle(time_for_ascend,&pBuDiveSettings->decogaslist[gGas_id],
+                                              pressureBottom, pressureTop_tmp, &gCNS);
+        }
+        pressureBottom = pressureTop_tmp;
+        seconds += time_for_ascend;
+    }while(pressureTop_tmp > pressureTop);
+    gPressure = pressureTop;
+	return seconds;
+}
+
+
+float get_gf_at_pressure(float pressure)
+{
+	float gfSteigung = 0.0f;
+
+	if(gGF_low_depth_bar < 0)
+			gGF_low_depth_bar = PRESSURE_THREE_METER; // just to prevent erratic behaviour if variable is not set
+
+    gfSteigung = ((float)(pBuDiveSettings->gf_high - pBuDiveSettings->gf_low))/ gGF_low_depth_bar;
+
+
+	if((pressure - gSurface_pressure_bar) <= PRESSURE_HALF_METER)
+		return ((float)pBuDiveSettings->gf_high) / 100.0f;
+
+	if(pressure >= gSurface_pressure_bar + gGF_low_depth_bar)
+		return ((float)pBuDiveSettings->gf_low) / 100.0f;
+
+	return (pBuDiveSettings->gf_high - gfSteigung * (pressure - gSurface_pressure_bar) )/ 100.0f;
+}
+
+
+void buehlmann_calc_ndl(void)
+{
+	float local_tissue_nitrogen_bar[16];
+	float local_tissue_helium_bar[16];
+	int i;
+
+	gNDL = 0;
+	//Check ndl always use gHigh
+  gGF_value = ((float)pBuDiveSettings->gf_high) / 100.0f;
+	//10 minutes steps
+	while(gNDL < (300 * 60))
+	{
+		memcpy(local_tissue_nitrogen_bar, gTissue_nitrogen_bar, (4*16));
+		memcpy(local_tissue_helium_bar, gTissue_helium_bar, (4*16));
+		//
+		gNDL += 600;
+    decom_tissues_exposure2(600, &pBuDiveSettings->decogaslist[gGas_id], gPressure,gTissue_nitrogen_bar,gTissue_helium_bar);
+		decom_oxygen_calculate_cns_exposure(600,&pBuDiveSettings->decogaslist[gGas_id],gPressure,&gCNS);
+		//tissues_exposure_at_gPressure_seconds(600);
+		buehlmann_backup_and_restore(true);
+		if(dive1_check_deco() == true)
+		{
+			buehlmann_backup_and_restore(false);
+			break;
+		}
+		buehlmann_backup_and_restore(false);
+	}
+
+	if(gNDL < (300 * 60))
+		gNDL -= 600;
+
+	if(gNDL > (150 * 60))
+		return;
+
+	// refine
+	memcpy(gTissue_nitrogen_bar, local_tissue_nitrogen_bar, (4*16));
+	memcpy(gTissue_helium_bar, local_tissue_helium_bar, (4*16));
+
+	//One minutes step
+	for(i = 0; i < 20; i++)
+	{
+		gNDL += 60;
+		//tissues_exposure_at_gPressure_seconds(60);
+		decom_tissues_exposure2(60, &pBuDiveSettings->decogaslist[gGas_id], gPressure,gTissue_nitrogen_bar,gTissue_helium_bar);
+		decom_oxygen_calculate_cns_exposure(60,&pBuDiveSettings->decogaslist[gGas_id],gPressure,&gCNS);
+		buehlmann_backup_and_restore(true);
+		if(dive1_check_deco() == true)
+			break;
+		buehlmann_backup_and_restore(false);
+	}
+	//gNDL -= 60;
+	return;
+}
+
+
+//  ===============================================================================
+//	dive1_check_deco
+/// @brief	for NDL calculations
+///					160614 using ceilingOther and not ceiling
+//  ===============================================================================
+_Bool dive1_check_deco(void)
+{
+	// gGF_value is set in call routine;
+	// internes Backup!
+
+	// calc like in deco
+	float ceiling;
+	float ceilingOther; // new hw 160614
+
+	ceiling = tissue_tolerance();
+	ambient_bar_to_deco_stop_depth_bar(ceiling); // this will set gStop.depth :-) (and gStop.id)
+
+	// set the base for all upcoming parameters
+	ceilingOther = gStop.depth + gSurface_pressure_bar;
+
+	// modify parameters if there is ascend or parameter fine adjustment
+	if(ceilingOther < (gPressure - PRESSURE_150_CM)) // more than 1.5 meter below ceiling
+	{
+		// ascend within 10 mtr to GF_low // speed 12 mtr/min -> 50 sec / 10 mtr;  15 sec / 3 mtr.
+		while(((gPressure - PRESSURE_TEN_METER ) > gSurface_pressure_bar) && (ceiling < (gPressure - PRESSURE_TEN_METER)))
+		{
+			ascend_with_all_gaschanges(PRESSURE_TEN_METER);
+			ceiling = tissue_tolerance();
+		}
+		while(((gPressure - PRESSURE_THREE_METER )> gSurface_pressure_bar) && (ceiling < gPressure))
+		{
+			ascend_with_all_gaschanges(PRESSURE_THREE_METER);
+			ceiling = tissue_tolerance();
+		}
+	}
+	if(ceiling <= gSurface_pressure_bar)
+		return false;
+	else
+		return true;
+}
+
+
+void buehlmann_ceiling_calculator(SLifeData* pLifeData, SDiveSettings * pDiveSettings, SDecoinfo * pDecoInfo)
+{
+	float gf_low;
+	float gf_high;
+	float gf_delta;
+	int dv_gf_low_stop_meter;
+	_Bool test_result;
+	float next_gf_value;
+	float next_pressure_absolute;
+	int depth_in_meter;
+	
+	gf_low = pDiveSettings->gf_low;
+	gf_high = pDiveSettings->gf_high;
+
+	//
+	dv_gf_low_stop_meter = (int)((pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero - pLifeData->pressure_surface_bar) * 10);
+	//
+	if(dv_gf_low_stop_meter < 1)
+	{
+		next_gf_value = gf_high; // fix hw 161024
+		gf_delta = 0;
+	}
+	else
+	{
+		next_gf_value = gf_high;
+		gf_delta = gf_high - gf_low;
+		gf_delta /= dv_gf_low_stop_meter; // gf_delta is delta for each meter now!!
+	}
+	//
+	depth_in_meter = 0;
+	next_pressure_absolute = pLifeData->pressure_surface_bar;
+
+	memcpy(gTissue_nitrogen_bar, pLifeData->tissue_nitrogen_bar, (4*16));
+	memcpy(gTissue_helium_bar, pLifeData->tissue_helium_bar, (4*16));
+	gGF_value = next_gf_value / 100.0f;
+	//
+	test_result = buehlmann_tissue_test_tolerance(next_pressure_absolute);
+	//
+	while(!test_result && depth_in_meter < 200)
+	{
+		depth_in_meter += 1;
+		next_gf_value = fmaxf(gf_low, next_gf_value - gf_delta);
+		gGF_value = next_gf_value / 100.0f;
+		next_pressure_absolute += 0.1f; // 1 meter down
+		test_result = buehlmann_tissue_test_tolerance(next_pressure_absolute);
+	}
+	//
+	if(test_result)
+	{
+		// old direct paste
+		pDecoInfo->output_ceiling_meter = depth_in_meter;
+		// new sub-meter hw 160331
+		if(depth_in_meter >= 1)
+		{
+			for(int i = 0; i < 10; i++)
+			{
+				next_gf_value += gf_delta/10.0f;
+				gGF_value = next_gf_value / 100.0f;
+				next_pressure_absolute -= 0.01f; // 0.1 meter up
+				if(!buehlmann_tissue_test_tolerance(next_pressure_absolute))
+				{
+					pDecoInfo->output_ceiling_meter -= ((float)i)/10.0f;
+					break;
+				}
+			}
+		}
+	}
+	else
+	{
+		pDecoInfo->output_ceiling_meter = 999;
+	}
+}
+
+
+void buehlmann_relative_gradient_calculator(SLifeData* pLifeData, SDiveSettings * pDiveSettings, SDecoinfo * pDecoInfo)
+{
+	float gf_low;
+	float gf_high;
+	float gf_delta;
+	int dv_gf_low_stop_meter;
+
+	float rgf; // relative gradient factor by hwOS p2_deco.c
+	float temp_tissue;
+	float limit;
+	float pres_respiration;
+	float gf;
+
+	gf_low = pDiveSettings->gf_low;
+	gf_high = pDiveSettings->gf_high;
+
+	dv_gf_low_stop_meter = (int)((pDiveSettings->internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero - pLifeData->pressure_surface_bar) * 10);
+
+	if(dv_gf_low_stop_meter < 1)
+	{
+		gf_delta = 0;
+	}
+	else
+	{
+		gf_delta = gf_high - gf_low;
+		gf_delta /= dv_gf_low_stop_meter; // gf_delta is delta for each meter now!!
+	}
+
+	
+	limit = tissue_tolerance_without_gf_correction(&temp_tissue);
+	pres_respiration = pLifeData->pressure_ambient_bar;
+	
+	if( temp_tissue <= pres_respiration )
+	{
+		gf = 0.0;
+	}
+	else
+	{
+		gf = (temp_tissue  - pres_respiration)
+			 / (temp_tissue  - limit)
+			 * 100.0f;
+	}
+	
+	if(dv_gf_low_stop_meter < 1)
+	{
+		rgf = gf_high;
+	}
+	else
+	{
+		float temp1 = dv_gf_low_stop_meter; 
+		float temp2 = pLifeData->depth_meter;
+
+		if (temp2 <= 0)
+				rgf = gf_high;
+		else if (temp2 >= temp1)
+				rgf = gf_low;
+		else
+				rgf = gf_low + (temp1 - temp2)*gf_delta;
+	}
+	
+	rgf = gf / rgf;
+	
+	// avoid discussions about values > 100 below next deco stop
+	if((rgf > 1.0f) && (pLifeData->depth_meter >= pDecoInfo->output_ceiling_meter))
+		rgf = 1.0f;
+	
+	pDecoInfo->output_relative_gradient = rgf;
+}