diff --git a/core/plannernotes.c b/core/plannernotes.c index 8a1f85009..4021806ea 100644 --- a/core/plannernotes.c +++ b/core/plannernotes.c @@ -30,16 +30,60 @@ int diveplan_duration(struct diveplan *diveplan) return (duration + 30) / 60; } +struct icd_data { // This structure provides communication between function isobaric_counterdiffusion() and the calling software. + int dN2; // The change in fraction (permille) of nitrogen during the change + int dHe; // The change in fraction (permille) of helium during the change +}; + +/* Perform isobaric counterdiffusion calculations for gas changes in trimix dives. + * Here we use the rule-of-fifths where, during a change involving trimix gas, the increase in nitrogen + * should not exceed one fifth of the decrease in helium. + * Parameters: 1) Pointer to the dive structure. + * 2) pointers to two gas mixes, the gas being switched from and the gas being switched to. + * 3) a pointer to an icd_data structure. + * Output: i) The icd_data stucture is filled with the delta_N2 and delta_He numbers (as permille). + * ii) Function returns a boolean indicating an exceeding of the rule-of-fifths. False = no icd problem. + */ +bool isobaric_counterdiffusion(struct gasmix *oldgasmix, struct gasmix *newgasmix, struct icd_data *results) +{ + results->dN2 = get_he(oldgasmix) + get_o2(oldgasmix) - get_he(newgasmix) - get_o2(newgasmix); + results->dHe = get_he(newgasmix) - get_he(oldgasmix); + return get_he(oldgasmix) && results->dN2 > 0 && 5 * results->dN2 > -results->dHe; +} + +/* Add the icd results of one trimix gas change to the dive plan html buffer. Two rows are added to the table, one + * indicating fractions of gas, the other indication partial pressures of gas. This function makes use of the + * icd_data structure that was filled with information by the function isobaric_counterdiffusion(). + * Parameters: 1) Pointer to the output buffer position at which writing should start. + * 2) The size of the part of the unused output buffer that remains unused. + * 3) The data structure containing icd calculation results: icdvalues. + * 4) The names of the gasses in the gas change: gas-from and gas-to. + * Returns: The size of the output buffer that has been used after the new results have been added. + */ +int add_icd_entry(char *icdbuffer, unsigned int maxsize, struct icd_data *icdvalues, int time_seconds, int ambientpressure_mbar, char *gasname_from, const char *gasname_to) +{ + return snprintf(icdbuffer, maxsize, + "%3d%s%s➙%s%+5.2f%%%+5.2f%%%+5.2f%% %+5.2f%s%+5.2f%s%+5.2f%s", + (time_seconds + 30) / 60, translate("gettextFromC", "min"), gasname_from, gasname_to, icdvalues->dHe / 10.0, + ((5 * icdvalues->dN2) > -icdvalues->dHe) ? "red" : "#383838", icdvalues->dN2 / 10.0 , 0.2 * (-icdvalues->dHe / 10), + ambientpressure_mbar * icdvalues->dHe / 1e6, translate("gettextFromC", "bar"), ((5 * icdvalues->dN2) > -icdvalues->dHe) ? "red" : "#383838", + ambientpressure_mbar * icdvalues->dN2 / 1e6, translate("gettextFromC", "bar"), + ambientpressure_mbar * -icdvalues->dHe / 5e6, translate("gettextFromC", "bar")); +} void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, int error) { const unsigned int sz_buffer = 2000000; const unsigned int sz_temp = 100000; + const unsigned int sz_icdbuf = 10000; char *buffer = (char *)malloc(sz_buffer); char *temp = (char *)malloc(sz_temp); + char *icdbuffer = (char *)malloc(sz_icdbuf); + char *old_gas_name = (char *)malloc(20); const char *deco, *segmentsymbol; static char buf[1000]; int len, lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0, lastprintsetpoint = -1; + int icyl, icdlen = 0; struct gasmix lastprintgasmix = {{ -1 }, { -1 }}; struct divedatapoint *dp = diveplan->dp; bool plan_verbatim = prefs.verbatim_plan; @@ -49,9 +93,11 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d bool gaschange_after = !plan_verbatim; bool gaschange_before; bool lastentered = true; + bool istrimix = false; + bool icdwarning = false; struct divedatapoint *nextdp = NULL; struct divedatapoint *lastbottomdp = NULL; - + struct icd_data icdvalues; if (decoMode() == VPMB) { deco = translate("gettextFromC", "VPM-B"); @@ -65,11 +111,8 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d "PLAN DIVES SIMPLY BASED ON THE RESULTS GIVEN HERE."), deco); disclaimer = buf; - if (!dp) { - free((void *)buffer); - free((void *)temp); - return; - } + if (!dp) + goto finished; if (error) { snprintf(temp, sz_temp, "%s", @@ -77,10 +120,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d snprintf(buffer, sz_buffer, "%s %s
", translate("gettextFromC", "Warning:"), temp); dive->notes = strdup(buffer); - - free((void *)buffer); - free((void *)temp); - return; + goto finished; } len = show_disclaimer ? snprintf(buffer, sz_buffer, "
%s
", disclaimer) : 0; @@ -121,7 +161,6 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d len += snprintf(buffer + len, sz_buffer - len, translate("gettextFromC", "Runtime: %dmin
"), diveplan_duration(diveplan)); - if (!plan_verbatim) { len += snprintf(buffer + len, sz_buffer - len, "", translate("gettextFromC", "depth")); @@ -135,6 +174,27 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d "", translate("gettextFromC", "gas")); } + for (icyl = 0; icyl < MAX_CYLINDERS; icyl++) { // If dive plan has an OC cylinder with helium, then initialise ICD table: + if ((dive->cylinder[icyl].cylinder_use == OC_GAS) && (get_he(&dive->cylinder[icyl].gasmix) > 0)) { + istrimix = true; + icdlen = 0; + icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, + "
%s%s
", + translate("gettextFromC","Isobaric counterdiffusion information")); + icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "", + translate("gettextFromC", "runtime")); + icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "", + translate("gettextFromC", "gaschange")); + icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "", + translate("gettextFromC", "ΔHe")); + icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "", + translate("gettextFromC", "ΔN₂")); + icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "", + translate("gettextFromC", "max ΔN₂")); + break; + } + } + do { struct gasmix gasmix, newgasmix = {}; const char *depth_unit; @@ -275,6 +335,12 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d } else { len += snprintf(buffer + len, sz_buffer - len, "", gasname(&newgasmix)); + if (isascent && (get_he(&lastprintgasmix) > 0)) { // For a trimix gas change on ascent, save ICD info if previous cylinder had helium + if (isobaric_counterdiffusion(&lastprintgasmix, &newgasmix, &icdvalues)) // Do icd calulations + icdwarning = true; + strcpy(old_gas_name, gasname(&lastprintgasmix)); // work-around because gasname() cannot be called more than once in one instruction. + icdlen += add_icd_entry(icdbuffer+icdlen, sz_icdbuf-icdlen, &icdvalues, dp->time, depth_to_mbar(dp->depth.mm, dive), old_gas_name, gasname(&newgasmix)); // Print calculations to buffer. + } } lastprintsetpoint = nextdp->setpoint; lastprintgasmix = newgasmix; @@ -288,6 +354,12 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d } else { len += snprintf(buffer + len, sz_buffer - len, "", gasname(&gasmix)); + if (get_he(&lastprintgasmix) > 0) { // For a trimix gas change, save ICD info if previous cylinder had helium + if (isobaric_counterdiffusion(&lastprintgasmix, &gasmix, &icdvalues)) // Do icd calculations + icdwarning = true; + strcpy(old_gas_name, gasname(&lastprintgasmix)); + icdlen += add_icd_entry(icdbuffer+icdlen, sz_icdbuf-icdlen, &icdvalues, dp->time, depth_to_mbar(dp->depth.mm, dive), old_gas_name, gasname(&gasmix)); // and print them to buffer. + } } // Set variables so subsequent iterations can test against the last gas printed lastprintsetpoint = dp->setpoint; @@ -302,15 +374,23 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d } } if (gaschange_after) { - // gas switch at this waypoint + // gas switch at this waypoint for verbatim if (plan_verbatim) { if (lastsetpoint >= 0) { - if (nextdp && nextdp->setpoint) + if (nextdp && nextdp->setpoint) { snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), gasname(&newgasmix), (double) nextdp->setpoint / 1000.0); - else + } else { snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s"), gasname(&newgasmix)); + if ((isascent) && (get_he(&lastprintgasmix) > 0)) { // For a trimix gas change on ascent: + if (isobaric_counterdiffusion(&lastprintgasmix, &newgasmix, &icdvalues)) // Do icd calculations + icdwarning = true; + strcpy(old_gas_name, gasname(&lastprintgasmix)); + icdlen += add_icd_entry(icdbuffer+icdlen, sz_icdbuf-icdlen, &icdvalues, dp->time, depth_to_mbar(dp->depth.mm, dive), old_gas_name, gasname(&newgasmix)); // and print them to buffer. + } + } len += snprintf(buffer + len, sz_buffer - len, "%s
", temp); } + lastprintgasmix = newgasmix; gaschange_after = false; gasmix = newgasmix; } @@ -458,17 +538,30 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d volume, unit, pressure, pressure_unit, gasname(&cyl->gasmix)); } } else { - if (lrint(volume) > 0) + if (lrint(volume) > 0) { snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s of %s (%.0f%s during planned ascent)"), volume, unit, gasname(&cyl->gasmix), deco_volume, unit); - else + } else { snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s of %s"), volume, unit, gasname(&cyl->gasmix)); + } } /* Gas consumption: Now finally print all strings to output */ len += snprintf(buffer + len, sz_buffer - len, "%s%s%s
", temp, warning, mingas); } + /* For trimix OC dives, add the ICD table here */ + if (istrimix) { + icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen,"
%s:
%s%s%s%s%s
%s%s
"); // End the ICD table + len += snprintf(buffer + len, sz_buffer - len, "%s", icdbuffer); // ..and add it to the html buffer + if (icdwarning) { // If necessary, add a warning message to html buffer + len += snprintf(buffer + len, sz_buffer - len, "%s %s", + translate("gettextFromC", "Warning:"), + translate("gettextFromC", "Isobaric counterdiffusion conditions exceeded")); + } + len += snprintf(buffer + len, sz_buffer - len, "
"); // ... and add a line break + } + /* Print warnings for pO2 */ dp = diveplan->dp; bool o2warning_exist = false; @@ -512,7 +605,10 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d snprintf(buffer + len, sz_buffer - len, ""); dive->notes = strdup(buffer); +finished: + free((void *)old_gas_name); free((void *)buffer); free((void *)temp); -} + free((void *)icdbuffer); +}