Implement ICD calculations in planner output

This patch implements the calculation and printing of icd parameters
in the dive planner output. It does:
1) icd parameters are calculated following the rule-of-fifths for
   OC trimix dives. For each gas change on the ascent that involves
   helium, the change in gas composition as well as the change in
   partial pressures are shown. If rule-of-fifths is exceeded, a
   warning message is prinetd.
2) An independent function is provided that in principle enables
   calculation of icd paramaters outside of the context of the
   dive planner.

Further updates to icd calculations

These changes respond to #1047. Code style conventions have been
improved. The ICD table is now also printed when the planner is
set to verbatim mode. The code that created the html icd results
has been moved to a function. This does not make the code more
efficient (many parameters passed) but it makes the code more clean.

Free a new dynamic variable (old_gas_name)

Stylistic changes to new code in plannernotes.c

Changes in comments to explain some of the changes I made.
Preparation of comments and functioning of isobaric_counterdiffusion()
and its dependent data structure for possible transfer to dive.c
and dive.h after finalisation and merging of the present PR.
Smaller stylistic changes to conform to our coding rules.

The several comments and suggestions during github review
(@atdotde, @leolit123, @sfuchs79) are incorporated.

Fixed up mis-aligned comments and text descriptions by replacing
tab characters with space characters, thereby hard-forcing
alignment of indented lines of text.

Remove parameter *dive from function isobaric_counterdiffusion().

Improve a few constructs to conform to our style rules.

Signed-off-by: Willem Ferguson <willemferguson@zoology.up.ac.za>
This commit is contained in:
Willem Ferguson 2018-01-15 09:43:57 +02:00 committed by Lubomir I. Ivanov
parent a15d0ec525
commit f072f78c29

View file

@ -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,
"<tr><td rowspan='2' style= 'vertical-align:top;'>%3d%s</td><td rowspan=2 style= 'vertical-align:top;'>%s&#10137;%s</td><td style='padding-left: 10px;'>%+5.2f%%</td><td style= 'padding-left: 15px; color:%s;'>%+5.2f%%</td><td style='padding-left: 15px;'>%+5.2f%%</td></tr> <tr><td style='padding-left: 10px;'>%+5.2f%s</td><td style='padding-left: 15px; color:%s;'>%+5.2f%s</td><td style='padding-left: 15px;'>%+5.2f%s</td></tr>",
(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, "<span style='color: red;'>%s </span> %s<br>",
translate("gettextFromC", "Warning:"), temp);
dive->notes = strdup(buffer);
free((void *)buffer);
free((void *)temp);
return;
goto finished;
}
len = show_disclaimer ? snprintf(buffer, sz_buffer, "<div><b>%s</b><br></div>", 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<br></div>"),
diveplan_duration(diveplan));
if (!plan_verbatim) {
len += snprintf(buffer + len, sz_buffer - len, "<table><thead><tr><th></th><th>%s</th>",
translate("gettextFromC", "depth"));
@ -135,6 +174,27 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d
"<th style='padding-left: 10px; float: left;'>%s</th></tr></thead><tbody style='float: left;'>",
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,
"<table><tbody style='float: left;'><tr><td colspan=5>%s:</td></tr>",
translate("gettextFromC","Isobaric counterdiffusion information"));
icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "<tr><td align='left'><b>%s</b></td>",
translate("gettextFromC", "runtime"));
icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "<td align='center'><b>%s</b></td>",
translate("gettextFromC", "gaschange"));
icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "<td style='padding-left: 15px;'><b>%s</b></td>",
translate("gettextFromC", "&#916;He"));
icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "<td style='padding-left: 20px;'><b>%s</b></td>",
translate("gettextFromC", "&#916;N&#8322;"));
icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen, "<td style='padding-left: 10px;'><b>%s</b></td></tr>",
translate("gettextFromC", "max &#916;N&#8322;"));
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, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>",
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, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>",
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<br>", 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 <span style='color: red;'><b>%s</b></span> (%.0f%s during planned ascent)"),
volume, unit, gasname(&cyl->gasmix), deco_volume, unit);
else
} else {
snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s of <span style='color: red;'><b>%s</b></span>"),
volume, unit, gasname(&cyl->gasmix));
}
}
/* Gas consumption: Now finally print all strings to output */
len += snprintf(buffer + len, sz_buffer - len, "%s%s%s<br>", temp, warning, mingas);
}
/* For trimix OC dives, add the ICD table here */
if (istrimix) {
icdlen += snprintf(icdbuffer + icdlen, sz_icdbuf - icdlen,"</tbody></table>"); // 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, "<span style='color: red;'>%s</span> %s",
translate("gettextFromC", "Warning:"),
translate("gettextFromC", "Isobaric counterdiffusion conditions exceeded"));
}
len += snprintf(buffer + len, sz_buffer - len, "<br>"); // ... 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, "</div>");
dive->notes = strdup(buffer);
finished:
free((void *)old_gas_name);
free((void *)buffer);
free((void *)temp);
}
free((void *)icdbuffer);
}