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, " | %s | ",
translate("gettextFromC", "depth"));
@@ -135,6 +174,27 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d
"%s |
",
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: |
",
+ translate("gettextFromC","Isobaric counterdiffusion information"));
+ icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "%s | ",
+ translate("gettextFromC", "runtime"));
+ icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "%s | ",
+ translate("gettextFromC", "gaschange"));
+ icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "%s | ",
+ translate("gettextFromC", "ΔHe"));
+ icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "%s | ",
+ translate("gettextFromC", "ΔN₂"));
+ icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "%s |
",
+ 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, "%s | ",
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, "%s | ",
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,"
"); // 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);
+}