aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/CMakeLists.txt1
-rw-r--r--core/planner.c470
-rw-r--r--core/planner.h1
-rw-r--r--core/plannernotes.c479
-rw-r--r--core/qthelperfromc.h2
5 files changed, 483 insertions, 470 deletions
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 5c22e2306..fc6d20ba8 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -45,6 +45,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
ostctools.c
parse-xml.c
planner.c
+ plannernotes.c
profile.c
gaspressures.c
worldmap-save.c
diff --git a/core/planner.c b/core/planner.c
index 69ac24efa..30552a954 100644
--- a/core/planner.c
+++ b/core/planner.c
@@ -35,7 +35,6 @@ int decostoplevels_imperial[] = { 0, 3048, 6096, 9144, 12192, 15240, 18288, 2133
325120, 345440, 365760, 386080 };
double plangflow, plangfhigh;
-bool plan_verbatim, plan_display_runtime, plan_display_duration, plan_display_transitions;
extern double regressiona();
extern double regressionb();
@@ -69,9 +68,6 @@ void dump_plan(struct diveplan *diveplan)
}
#endif
-/* this is in qthelper.cpp, so including the .h file is a pain */
-extern const char *get_current_date();
-
bool diveplan_empty(struct diveplan *diveplan)
{
struct divedatapoint *dp;
@@ -525,472 +521,6 @@ static int *sort_stops(int *dstops, int dnr, struct gaschanges *gstops, int gnr)
return stoplevels;
}
-int diveplan_duration(struct diveplan *diveplan)
-{
- struct divedatapoint *dp = diveplan->dp;
- int duration = 0;
- while(dp) {
- if (dp->time > duration)
- duration = dp->time;
- dp = dp->next;
- }
- return duration / 60;
-}
-
-static 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;
- char *buffer = (char *)malloc(sz_buffer);
- char *temp = (char *)malloc(sz_temp);
- const char *deco, *segmentsymbol;
- static char buf[1000];
- int len, lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0, lastprintsetpoint = -1;
- struct gasmix lastprintgasmix = {{ -1 }, { -1 }};
- struct divedatapoint *dp = diveplan->dp;
- bool gaschange_after = !plan_verbatim;
- bool gaschange_before;
- bool lastentered = true;
- struct divedatapoint *nextdp = NULL;
- struct divedatapoint *lastbottomdp = NULL;
-
- plan_verbatim = prefs.verbatim_plan;
- plan_display_runtime = prefs.display_runtime;
- plan_display_duration = prefs.display_duration;
- plan_display_transitions = prefs.display_transitions;
-
- if (decoMode() == VPMB) {
- deco = translate("gettextFromC", "VPM-B");
- } else {
- deco = translate("gettextFromC", "BUHLMANN");
- }
-
- snprintf(buf, sizeof(buf), translate("gettextFromC", "DISCLAIMER / WARNING: THIS IS A NEW IMPLEMENTATION OF THE %s "
- "ALGORITHM AND A DIVE PLANNER IMPLEMENTATION BASED ON THAT WHICH HAS "
- "RECEIVED ONLY A LIMITED AMOUNT OF TESTING. WE STRONGLY RECOMMEND NOT TO "
- "PLAN DIVES SIMPLY BASED ON THE RESULTS GIVEN HERE."), deco);
- disclaimer = buf;
-
- if (!dp) {
- free((void *)buffer);
- free((void *)temp);
- return;
- }
-
- if (error) {
- snprintf(temp, sz_temp, "%s",
- translate("gettextFromC", "Decompression calculation aborted due to excessive time"));
- 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;
- }
-
- len = show_disclaimer ? snprintf(buffer, sz_buffer, "<div><b>%s</b><br></div>", disclaimer) : 0;
-
- if (diveplan->surface_interval > 60) {
- len += snprintf(buffer + len, sz_buffer - len, "<div><b>%s (%s) %s %d:%02d) %s %s<br>",
- translate("gettextFromC", "Subsurface"),
- subsurface_canonical_version(),
- translate("gettextFromC", "dive plan</b> (surface interval "),
- FRACTION(diveplan->surface_interval / 60, 60),
- translate("gettextFromC", "created on"),
- get_current_date());
- } else {
- len += snprintf(buffer + len, sz_buffer - len, "<div><b>%s (%s) %s %s</b><br>",
- translate("gettextFromC", "Subsurface"),
- subsurface_canonical_version(),
- translate("gettextFromC", "dive plan</b> created on"),
- get_current_date());
- }
-
- 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"));
- if (plan_display_duration)
- len += snprintf(buffer + len, sz_buffer - len, "<th style='padding-left: 10px;'>%s</th>",
- translate("gettextFromC", "duration"));
- if (plan_display_runtime)
- len += snprintf(buffer + len, sz_buffer - len, "<th style='padding-left: 10px;'>%s</th>",
- translate("gettextFromC", "runtime"));
- len += snprintf(buffer + len, sz_buffer - len,
- "<th style='padding-left: 10px; float: left;'>%s</th></tr></thead><tbody style='float: left;'>",
- translate("gettextFromC", "gas"));
- }
- do {
- struct gasmix gasmix, newgasmix = {};
- const char *depth_unit;
- double depthvalue;
- int decimals;
- bool isascent = (dp->depth.mm < lastdepth);
-
- nextdp = dp->next;
- if (dp->time == 0)
- continue;
- gasmix = dive->cylinder[dp->cylinderid].gasmix;
- depthvalue = get_depth_units(dp->depth.mm, &decimals, &depth_unit);
- /* analyze the dive points ahead */
- while (nextdp && nextdp->time == 0)
- nextdp = nextdp->next;
- if (nextdp)
- newgasmix = dive->cylinder[nextdp->cylinderid].gasmix;
- gaschange_after = (nextdp && (gasmix_distance(&gasmix, &newgasmix) || dp->setpoint != nextdp->setpoint));
- gaschange_before = (gasmix_distance(&lastprintgasmix, &gasmix) || lastprintsetpoint != dp->setpoint);
- /* do we want to skip this leg as it is devoid of anything useful? */
- if (!dp->entered &&
- nextdp &&
- dp->depth.mm != lastdepth &&
- nextdp->depth.mm != dp->depth.mm &&
- !gaschange_before &&
- !gaschange_after)
- continue;
- if (dp->time - lasttime < 10 && !(gaschange_after && dp->next && dp->depth.mm != dp->next->depth.mm))
- continue;
-
- /* Store pointer to last entered datapoint for minimum gas calculation */
- /* Do this only if depth is larger than last/2nd last deco stop at ~6m */
- int secondlastdecostop = 0;
- if (prefs.units.length == METERS ) {
- secondlastdecostop = decostoplevels_metric[2];
- } else {
- secondlastdecostop = decostoplevels_imperial[2];
- }
- if (dp->entered && !nextdp->entered && dp->depth.mm > secondlastdecostop)
- lastbottomdp = dp;
-
- len = strlen(buffer);
- if (plan_verbatim) {
- /* When displaying a verbatim plan, we output a waypoint for every gas change.
- * Therefore, we do not need to test for difficult cases that mean we need to
- * print a segment just so we don't miss a gas change. This makes the logic
- * to determine whether or not to print a segment much simpler than with the
- * non-verbatim plan.
- */
- if (dp->depth.mm != lastprintdepth) {
- if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth.mm != nextdp->depth.mm)) {
- if (dp->setpoint)
- snprintf(temp, sz_temp, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix),
- (double) dp->setpoint / 1000.0);
-
- else
- snprintf(temp, sz_temp, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix));
-
- len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
- }
- newdepth = dp->depth.mm;
- lasttime = dp->time;
- } else {
- if ((nextdp && dp->depth.mm != nextdp->depth.mm) || gaschange_after) {
- if (dp->setpoint)
- snprintf(temp, sz_temp, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix),
- (double) dp->setpoint / 1000.0);
- else
- snprintf(temp, sz_temp, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix));
-
- len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
- newdepth = dp->depth.mm;
- lasttime = dp->time;
- }
- }
- } else {
- /* When not displaying the verbatim dive plan, we typically ignore ascents between deco stops,
- * unless the display transitions option has been selected. We output a segment if any of the
- * following conditions are met.
- * 1) Display transitions is selected
- * 2) The segment was manually entered
- * 3) It is the last segment of the dive
- * 4) The segment is not an ascent, there was a gas change at the start of the segment and the next segment
- * is a change in depth (typical deco stop)
- * 5) There is a gas change at the end of the segment and the last segment was entered (first calculated
- * segment if it ends in a gas change)
- * 6) There is a gaschange after but no ascent. This should only occur when backgas breaks option is selected
- * 7) It is an ascent ending with a gas change, but is not followed by a stop. As case 5 already matches
- * the first calculated ascent if it ends with a gas change, this should only occur if a travel gas is
- * used for a calculated ascent, there is a subsequent gas change before the first deco stop, and zero
- * time has been allowed for a gas switch.
- */
- if (plan_display_transitions || dp->entered || !dp->next ||
- (nextdp && dp->depth.mm != nextdp->depth.mm) ||
- (!isascent && gaschange_before && nextdp && dp->depth.mm != nextdp->depth.mm) ||
- (gaschange_after && lastentered) || (gaschange_after && !isascent) ||
- (isascent && gaschange_after && nextdp && dp->depth.mm != nextdp->depth.mm )) {
- // Print a symbol to indicate whether segment is an ascent, descent, constant depth (user entered) or deco stop
- if (isascent)
- segmentsymbol = "&#10138;"; // up-right arrow for ascent
- else if (dp->depth.mm > lastdepth)
- segmentsymbol = "&#10136;"; // down-right arrow for descent
- else if (dp->entered)
- segmentsymbol = "&#10137;"; // right arrow for entered entered segment at constant depth
- else
- segmentsymbol = "-"; // minus sign (a.k.a. horizontal line) for deco stop
-
- len += snprintf(buffer + len, sz_buffer - len, "<tr><td style='padding-left: 10px; float: right;'>%s</td>", segmentsymbol);
-
- snprintf(temp, sz_temp, translate("gettextFromC", "%3.0f%s"), depthvalue, depth_unit);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
- if (plan_display_duration) {
- snprintf(temp, sz_temp, translate("gettextFromC", "%3dmin"), (dp->time - lasttime + 30) / 60);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
- }
- if (plan_display_runtime) {
- snprintf(temp, sz_temp, translate("gettextFromC", "%3dmin"), (dp->time + 30) / 60);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
- }
-
- /* Normally a gas change is displayed on the stopping segment, so only display a gas change at the end of
- * an ascent segment if it is not followed by a stop
- */
- if ((isascent || dp->entered) && gaschange_after && dp->next && nextdp && (dp->depth.mm != nextdp->depth.mm || nextdp->entered)) {
- if (dp->setpoint) {
- snprintf(temp, sz_temp, translate("gettextFromC", "(SP = %.1fbar)"), (double) nextdp->setpoint / 1000.0);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&newgasmix),
- temp);
- } else {
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&newgasmix));
- }
- lastprintsetpoint = nextdp->setpoint;
- lastprintgasmix = newgasmix;
- gaschange_after = false;
- } else if (gaschange_before) {
- // If a new gas has been used for this segment, now is the time to show it
- if (dp->setpoint) {
- snprintf(temp, sz_temp, translate("gettextFromC", "(SP = %.1fbar)"), (double) dp->setpoint / 1000.0);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&gasmix),
- temp);
- } else {
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&gasmix));
- }
- // Set variables so subsequent iterations can test against the last gas printed
- lastprintsetpoint = dp->setpoint;
- lastprintgasmix = gasmix;
- gaschange_after = false;
- } else {
- len += snprintf(buffer + len, sz_buffer - len, "<td>&nbsp;</td>");
- }
- len += snprintf(buffer + len, sz_buffer - len, "</tr>");
- newdepth = dp->depth.mm;
- lasttime = dp->time;
- }
- }
- if (gaschange_after) {
- // gas switch at this waypoint
- if (plan_verbatim) {
- if (lastsetpoint >= 0) {
- if (nextdp && nextdp->setpoint)
- snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), gasname(&newgasmix), (double) nextdp->setpoint / 1000.0);
- else
- snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s"), gasname(&newgasmix));
-
- len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
- }
- gaschange_after = false;
- gasmix = newgasmix;
- }
- }
- lastprintdepth = newdepth;
- lastdepth = dp->depth.mm;
- lastsetpoint = dp->setpoint;
- lastentered = dp->entered;
- } while ((dp = nextdp) != NULL);
- if (!plan_verbatim)
- len += snprintf(buffer + len, sz_buffer - len, "</tbody></table><br>");
-
- /* Print the CNS and OTU next.*/
- dive->cns = 0;
- dive->maxcns = 0;
- update_cylinder_related_info(dive);
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "CNS"));
- len += snprintf(buffer + len, sz_buffer - len, "<div>%s: %i%%", temp, dive->cns);
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "OTU"));
- len += snprintf(buffer + len, sz_buffer - len, "<br>%s: %i<br></div>", temp, dive->otu);
-
- /* Print the settings for the diveplan next. */
- if (decoMode() == BUEHLMANN){
- snprintf(temp, sz_temp, translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"),
- diveplan->gflow, diveplan->gfhigh);
- } else if (decoMode() == VPMB){
- int temp_len;
- if (diveplan->vpmb_conservatism == 0)
- temp_len = snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Deco model: VPM-B at nominal conservatism"));
- else
- temp_len = snprintf(temp, sz_temp, translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), diveplan->vpmb_conservatism);
- if (diveplan->eff_gflow)
- temp_len += snprintf(temp + temp_len, sz_temp - temp_len, translate("gettextFromC", ", effective GF=%d/%d"), diveplan->eff_gflow
- , diveplan->eff_gfhigh);
-
- } else if (decoMode() == RECREATIONAL){
- snprintf(temp, sz_temp, translate("gettextFromC", "Deco model: Recreational mode based on Bühlmann ZHL-16B with GFLow = %d%% and GFHigh = %d%%"),
- diveplan->gflow, diveplan->gfhigh);
- }
- len += snprintf(buffer + len, sz_buffer - len, "<div>%s<br>",temp);
-
- const char *depth_unit;
- int altitude = (int) get_depth_units((int) (log(1013.0 / diveplan->surface_pressure) * 7800000), NULL, &depth_unit);
-
- len += snprintf(buffer + len, sz_buffer - len, translate("gettextFromC", "ATM pressure: %dmbar (%d%s)<br></div>"),
- diveplan->surface_pressure,
- altitude,
- depth_unit);
-
- /* Get SAC values and units for printing it in gas consumption */
- double bottomsacvalue, decosacvalue;
- int sacdecimals;
- const char* sacunit;
-
- bottomsacvalue = get_volume_units(prefs.bottomsac, &sacdecimals, &sacunit);
- decosacvalue = get_volume_units(prefs.decosac, NULL, NULL);
-
- /* Reduce number of decimals from 1 to 0 for bar/min, keep 2 for cuft/min */
- if (sacdecimals==1) sacdecimals--;
-
- /* Print the gas consumption next.*/
- if (dive->dc.divemode == CCR)
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Gas consumption (CCR legs excluded):"));
- else
- snprintf(temp, sz_temp, "%s %.*f|%.*f%s/min):", translate("gettextFromC", "Gas consumption (based on SAC"),
- sacdecimals, bottomsacvalue, sacdecimals, decosacvalue, sacunit);
- len += snprintf(buffer + len, sz_buffer - len, "<div>%s<br>", temp);
-
- /* Print gas consumption: This loop covers all cylinders */
- for (int gasidx = 0; gasidx < MAX_CYLINDERS; gasidx++) {
- double volume, pressure, deco_volume, deco_pressure, mingas_volume, mingas_pressure, mingas_depth;
- const char *unit, *pressure_unit, *depth_unit;
- char warning[1000] = "";
- char mingas[1000] = "";
- cylinder_t *cyl = &dive->cylinder[gasidx];
- if (cylinder_none(cyl))
- break;
-
- volume = get_volume_units(cyl->gas_used.mliter, NULL, &unit);
- deco_volume = get_volume_units(cyl->deco_gas_used.mliter, NULL, &unit);
- if (cyl->type.size.mliter) {
- int remaining_gas = lrint((double)cyl->end.mbar * cyl->type.size.mliter / 1000.0 / gas_compressibility_factor(&cyl->gasmix, cyl->end.mbar / 1000.0));
- double deco_pressure_bar = isothermal_pressure(&cyl->gasmix, 1.0, remaining_gas + cyl->deco_gas_used.mliter, cyl->type.size.mliter)
- - cyl->end.mbar / 1000.0;
- deco_pressure = get_pressure_units(lrint(1000.0 * deco_pressure_bar), &pressure_unit);
- pressure = get_pressure_units(cyl->start.mbar - cyl->end.mbar, &pressure_unit);
- /* Warn if the plan uses more gas than is available in a cylinder
- * This only works if we have working pressure for the cylinder
- * 10bar is a made up number - but it seemed silly to pretend you could breathe cylinder down to 0 */
- if (cyl->end.mbar < 10000)
- snprintf(warning, sizeof(warning), "<br>&nbsp;&mdash; <span style='color: red;'>%s </span> %s",
- translate("gettextFromC", "Warning:"),
- translate("gettextFromC", "this is more gas than available in the specified cylinder!"));
- else
- if ((float) cyl->end.mbar * cyl->type.size.mliter / 1000.0 / gas_compressibility_factor(&cyl->gasmix, cyl->end.mbar / 1000.0)
- < (float) cyl->deco_gas_used.mliter)
- snprintf(warning, sizeof(warning), "<br>&nbsp;&mdash; <span style='color: red;'>%s </span> %s",
- translate("gettextFromC", "Warning:"),
- translate("gettextFromC", "not enough reserve for gas sharing on ascent!"));
-
- /* Do and print minimum gas calculation for last bottom gas, but only for OC mode, */
- /* not for recreational mode and if no other warning was set before. */
- else
- if (lastbottomdp && gasidx == lastbottomdp->cylinderid
- && dive->dc.divemode == OC && decoMode() != RECREATIONAL) {
- /* Calculate minimum gas volume. */
- volume_t mingasv;
- mingasv.mliter = lrint(prefs.sacfactor / 100.0 * prefs.problemsolvingtime * prefs.bottomsac
- * depth_to_bar(lastbottomdp->depth.mm, dive)
- + prefs.sacfactor / 100.0 * cyl->deco_gas_used.mliter);
- /* Calculate minimum gas pressure for cyclinder. */
- lastbottomdp->minimum_gas.mbar = lrint(isothermal_pressure(&cyl->gasmix, 1.0,
- mingasv.mliter, cyl->type.size.mliter) * 1000);
- /* Translate all results into correct units */
- mingas_volume = get_volume_units(mingasv.mliter, NULL, &unit);
- mingas_pressure = get_pressure_units(lastbottomdp->minimum_gas.mbar, &pressure_unit);
- mingas_depth = get_depth_units(lastbottomdp->depth.mm, NULL, &depth_unit);
- /* Print it to results */
- if (cyl->start.mbar > lastbottomdp->minimum_gas.mbar) snprintf(mingas, sizeof(mingas),
- translate("gettextFromC", "<br>&nbsp;&mdash; <span style='color: green;'>Minimum gas</span> (based on %.1fxSAC/+%dmin@%.0f%s): %.0f%s/%.0f%s"),
- prefs.sacfactor / 100.0, prefs.problemsolvingtime,
- mingas_depth, depth_unit,
- mingas_volume, unit,
- mingas_pressure, pressure_unit);
- else snprintf(warning, sizeof(warning), "<br>&nbsp;&mdash; <span style='color: red;'>%s </span> %s",
- translate("gettextFromC", "Warning:"),
- translate("gettextFromC", "required minimum gas for ascent already exceeding start pressure of cylinder!"));
- }
- /* Print the gas consumption for every cylinder here to temp buffer. */
- snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s/%.0f%s of <span style='color: red;'><b>%s</b></span> (%.0f%s/%.0f%s in planned ascent)"), volume, unit, pressure, pressure_unit, gasname(&cyl->gasmix), deco_volume, unit, deco_pressure, pressure_unit);
-
- } else {
- snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s (%.0f%s during planned ascent) of <span style='color: red;'><b>%s</b></span>"),
- volume, unit, deco_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);
- }
-
- /* Print warnings for pO2 */
- dp = diveplan->dp;
- bool o2warning_exist = false;
- if (dive->dc.divemode != CCR) {
- while (dp) {
- if (dp->time != 0) {
- struct gas_pressures pressures;
- struct gasmix *gasmix = &dive->cylinder[dp->cylinderid].gasmix;
- fill_pressures(&pressures, depth_to_atm(dp->depth.mm, dive), gasmix, 0.0, dive->dc.divemode);
-
- if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) {
- const char *depth_unit;
- int decimals;
- double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit);
- len = strlen(buffer);
- if (!o2warning_exist) len += snprintf(buffer + len, sz_buffer - len, "<br>");
- o2warning_exist = true;
- snprintf(temp, sz_temp,
- translate("gettextFromC", "high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
- pressures.o2, FRACTION(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit);
- len += snprintf(buffer + len, sz_buffer - len, "<span style='color: red;'>%s </span> %s<br>",
- translate("gettextFromC", "Warning:"), temp);
- } else if (pressures.o2 < 0.16) {
- const char *depth_unit;
- int decimals;
- double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit);
- len = strlen(buffer);
- if (!o2warning_exist) len += snprintf(buffer + len, sz_buffer - len, "<br>");
- o2warning_exist = true;
- snprintf(temp, sz_temp,
- translate("gettextFromC", "low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
- pressures.o2, FRACTION(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit);
- len += snprintf(buffer + len, sz_buffer - len, "<span style='color: red;'>%s </span> %s<br>",
- translate("gettextFromC", "Warning:"), temp);
-
- }
- }
- dp = dp->next;
- }
- }
- snprintf(buffer + len, sz_buffer - len, "</div>");
- dive->notes = strdup(buffer);
-
- free((void *)buffer);
- free((void *)temp);
-}
-
int ascent_velocity(int depth, int avg_depth, int bottom_time)
{
(void) bottom_time;
diff --git a/core/planner.h b/core/planner.h
index 3cae87cac..741f83c02 100644
--- a/core/planner.h
+++ b/core/planner.h
@@ -21,6 +21,7 @@ extern void get_gas_at_time(struct dive *dive, struct divecomputer *dc, duration
extern int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time);
extern int get_gasidx(struct dive *dive, struct gasmix *mix);
extern bool diveplan_empty(struct diveplan *diveplan);
+extern void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, int error);
extern void free_dps(struct diveplan *diveplan);
extern struct dive *planned_dive;
diff --git a/core/plannernotes.c b/core/plannernotes.c
new file mode 100644
index 000000000..ea4955925
--- /dev/null
+++ b/core/plannernotes.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: GPL-2.0
+/* planner.c
+ *
+ * code that allows us to plan future dives
+ *
+ * (c) Dirk Hohndel 2013
+ */
+#include <assert.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include "dive.h"
+#include "deco.h"
+#include "divelist.h"
+#include "planner.h"
+#include "gettext.h"
+#include "libdivecomputer/parser.h"
+#include "qthelperfromc.h"
+#include "version.h"
+
+int diveplan_duration(struct diveplan *diveplan)
+{
+ struct divedatapoint *dp = diveplan->dp;
+ int duration = 0;
+ while(dp) {
+ if (dp->time > duration)
+ duration = dp->time;
+ dp = dp->next;
+ }
+ return duration / 60;
+}
+
+
+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;
+ char *buffer = (char *)malloc(sz_buffer);
+ char *temp = (char *)malloc(sz_temp);
+ const char *deco, *segmentsymbol;
+ static char buf[1000];
+ int len, lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0, lastprintsetpoint = -1;
+ struct gasmix lastprintgasmix = {{ -1 }, { -1 }};
+ struct divedatapoint *dp = diveplan->dp;
+ bool plan_verbatim = prefs.verbatim_plan;
+ bool plan_display_runtime = prefs.display_runtime;
+ bool plan_display_duration = prefs.display_duration;
+ bool plan_display_transitions = prefs.display_transitions;
+ bool gaschange_after = !plan_verbatim;
+ bool gaschange_before;
+ bool lastentered = true;
+ struct divedatapoint *nextdp = NULL;
+ struct divedatapoint *lastbottomdp = NULL;
+
+
+ if (decoMode() == VPMB) {
+ deco = translate("gettextFromC", "VPM-B");
+ } else {
+ deco = translate("gettextFromC", "BUHLMANN");
+ }
+
+ snprintf(buf, sizeof(buf), translate("gettextFromC", "DISCLAIMER / WARNING: THIS IS A NEW IMPLEMENTATION OF THE %s "
+ "ALGORITHM AND A DIVE PLANNER IMPLEMENTATION BASED ON THAT WHICH HAS "
+ "RECEIVED ONLY A LIMITED AMOUNT OF TESTING. WE STRONGLY RECOMMEND NOT TO "
+ "PLAN DIVES SIMPLY BASED ON THE RESULTS GIVEN HERE."), deco);
+ disclaimer = buf;
+
+ if (!dp) {
+ free((void *)buffer);
+ free((void *)temp);
+ return;
+ }
+
+ if (error) {
+ snprintf(temp, sz_temp, "%s",
+ translate("gettextFromC", "Decompression calculation aborted due to excessive time"));
+ 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;
+ }
+
+ len = show_disclaimer ? snprintf(buffer, sz_buffer, "<div><b>%s</b><br></div>", disclaimer) : 0;
+
+ if (diveplan->surface_interval > 60) {
+ len += snprintf(buffer + len, sz_buffer - len, "<div><b>%s (%s) %s %d:%02d) %s %s<br>",
+ translate("gettextFromC", "Subsurface"),
+ subsurface_canonical_version(),
+ translate("gettextFromC", "dive plan</b> (surface interval "),
+ FRACTION(diveplan->surface_interval / 60, 60),
+ translate("gettextFromC", "created on"),
+ get_current_date());
+ } else {
+ len += snprintf(buffer + len, sz_buffer - len, "<div><b>%s (%s) %s %s</b><br>",
+ translate("gettextFromC", "Subsurface"),
+ subsurface_canonical_version(),
+ translate("gettextFromC", "dive plan</b> created on"),
+ get_current_date());
+ }
+
+ 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"));
+ if (plan_display_duration)
+ len += snprintf(buffer + len, sz_buffer - len, "<th style='padding-left: 10px;'>%s</th>",
+ translate("gettextFromC", "duration"));
+ if (plan_display_runtime)
+ len += snprintf(buffer + len, sz_buffer - len, "<th style='padding-left: 10px;'>%s</th>",
+ translate("gettextFromC", "runtime"));
+ len += snprintf(buffer + len, sz_buffer - len,
+ "<th style='padding-left: 10px; float: left;'>%s</th></tr></thead><tbody style='float: left;'>",
+ translate("gettextFromC", "gas"));
+ }
+ do {
+ struct gasmix gasmix, newgasmix = {};
+ const char *depth_unit;
+ double depthvalue;
+ int decimals;
+ bool isascent = (dp->depth.mm < lastdepth);
+
+ nextdp = dp->next;
+ if (dp->time == 0)
+ continue;
+ gasmix = dive->cylinder[dp->cylinderid].gasmix;
+ depthvalue = get_depth_units(dp->depth.mm, &decimals, &depth_unit);
+ /* analyze the dive points ahead */
+ while (nextdp && nextdp->time == 0)
+ nextdp = nextdp->next;
+ if (nextdp)
+ newgasmix = dive->cylinder[nextdp->cylinderid].gasmix;
+ gaschange_after = (nextdp && (gasmix_distance(&gasmix, &newgasmix) || dp->setpoint != nextdp->setpoint));
+ gaschange_before = (gasmix_distance(&lastprintgasmix, &gasmix) || lastprintsetpoint != dp->setpoint);
+ /* do we want to skip this leg as it is devoid of anything useful? */
+ if (!dp->entered &&
+ nextdp &&
+ dp->depth.mm != lastdepth &&
+ nextdp->depth.mm != dp->depth.mm &&
+ !gaschange_before &&
+ !gaschange_after)
+ continue;
+ if (dp->time - lasttime < 10 && !(gaschange_after && dp->next && dp->depth.mm != dp->next->depth.mm))
+ continue;
+
+ /* Store pointer to last entered datapoint for minimum gas calculation */
+ if (dp->entered && !nextdp->entered)
+ lastbottomdp = dp;
+
+ len = strlen(buffer);
+ if (plan_verbatim) {
+ /* When displaying a verbatim plan, we output a waypoint for every gas change.
+ * Therefore, we do not need to test for difficult cases that mean we need to
+ * print a segment just so we don't miss a gas change. This makes the logic
+ * to determine whether or not to print a segment much simpler than with the
+ * non-verbatim plan.
+ */
+ if (dp->depth.mm != lastprintdepth) {
+ if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth.mm != nextdp->depth.mm)) {
+ if (dp->setpoint)
+ snprintf(temp, sz_temp, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"),
+ decimals, depthvalue, depth_unit,
+ FRACTION(dp->time - lasttime, 60),
+ FRACTION(dp->time, 60),
+ gasname(&gasmix),
+ (double) dp->setpoint / 1000.0);
+
+ else
+ snprintf(temp, sz_temp, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s"),
+ decimals, depthvalue, depth_unit,
+ FRACTION(dp->time - lasttime, 60),
+ FRACTION(dp->time, 60),
+ gasname(&gasmix));
+
+ len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
+ }
+ newdepth = dp->depth.mm;
+ lasttime = dp->time;
+ } else {
+ if ((nextdp && dp->depth.mm != nextdp->depth.mm) || gaschange_after) {
+ if (dp->setpoint)
+ snprintf(temp, sz_temp, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"),
+ decimals, depthvalue, depth_unit,
+ FRACTION(dp->time - lasttime, 60),
+ FRACTION(dp->time, 60),
+ gasname(&gasmix),
+ (double) dp->setpoint / 1000.0);
+ else
+ snprintf(temp, sz_temp, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s"),
+ decimals, depthvalue, depth_unit,
+ FRACTION(dp->time - lasttime, 60),
+ FRACTION(dp->time, 60),
+ gasname(&gasmix));
+
+ len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
+ newdepth = dp->depth.mm;
+ lasttime = dp->time;
+ }
+ }
+ } else {
+ /* When not displaying the verbatim dive plan, we typically ignore ascents between deco stops,
+ * unless the display transitions option has been selected. We output a segment if any of the
+ * following conditions are met.
+ * 1) Display transitions is selected
+ * 2) The segment was manually entered
+ * 3) It is the last segment of the dive
+ * 4) The segment is not an ascent, there was a gas change at the start of the segment and the next segment
+ * is a change in depth (typical deco stop)
+ * 5) There is a gas change at the end of the segment and the last segment was entered (first calculated
+ * segment if it ends in a gas change)
+ * 6) There is a gaschange after but no ascent. This should only occur when backgas breaks option is selected
+ * 7) It is an ascent ending with a gas change, but is not followed by a stop. As case 5 already matches
+ * the first calculated ascent if it ends with a gas change, this should only occur if a travel gas is
+ * used for a calculated ascent, there is a subsequent gas change before the first deco stop, and zero
+ * time has been allowed for a gas switch.
+ */
+ if (plan_display_transitions || dp->entered || !dp->next ||
+ (nextdp && dp->depth.mm != nextdp->depth.mm) ||
+ (!isascent && gaschange_before && nextdp && dp->depth.mm != nextdp->depth.mm) ||
+ (gaschange_after && lastentered) || (gaschange_after && !isascent) ||
+ (isascent && gaschange_after && nextdp && dp->depth.mm != nextdp->depth.mm )) {
+ // Print a symbol to indicate whether segment is an ascent, descent, constant depth (user entered) or deco stop
+ if (isascent)
+ segmentsymbol = "&#10138;"; // up-right arrow for ascent
+ else if (dp->depth.mm > lastdepth)
+ segmentsymbol = "&#10136;"; // down-right arrow for descent
+ else if (dp->entered)
+ segmentsymbol = "&#10137;"; // right arrow for entered entered segment at constant depth
+ else
+ segmentsymbol = "-"; // minus sign (a.k.a. horizontal line) for deco stop
+
+ len += snprintf(buffer + len, sz_buffer - len, "<tr><td style='padding-left: 10px; float: right;'>%s</td>", segmentsymbol);
+
+ snprintf(temp, sz_temp, translate("gettextFromC", "%3.0f%s"), depthvalue, depth_unit);
+ len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
+ if (plan_display_duration) {
+ snprintf(temp, sz_temp, translate("gettextFromC", "%3dmin"), (dp->time - lasttime + 30) / 60);
+ len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
+ }
+ if (plan_display_runtime) {
+ snprintf(temp, sz_temp, translate("gettextFromC", "%3dmin"), (dp->time + 30) / 60);
+ len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
+ }
+
+ /* Normally a gas change is displayed on the stopping segment, so only display a gas change at the end of
+ * an ascent segment if it is not followed by a stop
+ */
+ if ((isascent || dp->entered) && gaschange_after && dp->next && nextdp && (dp->depth.mm != nextdp->depth.mm || nextdp->entered)) {
+ if (dp->setpoint) {
+ snprintf(temp, sz_temp, translate("gettextFromC", "(SP = %.1fbar)"), (double) nextdp->setpoint / 1000.0);
+ len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&newgasmix),
+ temp);
+ } else {
+ len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&newgasmix));
+ }
+ lastprintsetpoint = nextdp->setpoint;
+ lastprintgasmix = newgasmix;
+ gaschange_after = false;
+ } else if (gaschange_before) {
+ // If a new gas has been used for this segment, now is the time to show it
+ if (dp->setpoint) {
+ snprintf(temp, sz_temp, translate("gettextFromC", "(SP = %.1fbar)"), (double) dp->setpoint / 1000.0);
+ len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&gasmix),
+ temp);
+ } else {
+ len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&gasmix));
+ }
+ // Set variables so subsequent iterations can test against the last gas printed
+ lastprintsetpoint = dp->setpoint;
+ lastprintgasmix = gasmix;
+ gaschange_after = false;
+ } else {
+ len += snprintf(buffer + len, sz_buffer - len, "<td>&nbsp;</td>");
+ }
+ len += snprintf(buffer + len, sz_buffer - len, "</tr>");
+ newdepth = dp->depth.mm;
+ lasttime = dp->time;
+ }
+ }
+ if (gaschange_after) {
+ // gas switch at this waypoint
+ if (plan_verbatim) {
+ if (lastsetpoint >= 0) {
+ if (nextdp && nextdp->setpoint)
+ snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), gasname(&newgasmix), (double) nextdp->setpoint / 1000.0);
+ else
+ snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s"), gasname(&newgasmix));
+
+ len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
+ }
+ gaschange_after = false;
+ gasmix = newgasmix;
+ }
+ }
+ lastprintdepth = newdepth;
+ lastdepth = dp->depth.mm;
+ lastsetpoint = dp->setpoint;
+ lastentered = dp->entered;
+ } while ((dp = nextdp) != NULL);
+ if (!plan_verbatim)
+ len += snprintf(buffer + len, sz_buffer - len, "</tbody></table><br>");
+
+ /* Print the CNS and OTU next.*/
+ dive->cns = 0;
+ dive->maxcns = 0;
+ update_cylinder_related_info(dive);
+ snprintf(temp, sz_temp, "%s", translate("gettextFromC", "CNS"));
+ len += snprintf(buffer + len, sz_buffer - len, "<div>%s: %i%%", temp, dive->cns);
+ snprintf(temp, sz_temp, "%s", translate("gettextFromC", "OTU"));
+ len += snprintf(buffer + len, sz_buffer - len, "<br>%s: %i<br></div>", temp, dive->otu);
+
+ /* Print the settings for the diveplan next. */
+ if (decoMode() == BUEHLMANN){
+ snprintf(temp, sz_temp, translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"),
+ diveplan->gflow, diveplan->gfhigh);
+ } else if (decoMode() == VPMB){
+ int temp_len;
+ if (diveplan->vpmb_conservatism == 0)
+ temp_len = snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Deco model: VPM-B at nominal conservatism"));
+ else
+ temp_len = snprintf(temp, sz_temp, translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), diveplan->vpmb_conservatism);
+ if (diveplan->eff_gflow)
+ temp_len += snprintf(temp + temp_len, sz_temp - temp_len, translate("gettextFromC", ", effective GF=%d/%d"), diveplan->eff_gflow
+ , diveplan->eff_gfhigh);
+
+ } else if (decoMode() == RECREATIONAL){
+ snprintf(temp, sz_temp, translate("gettextFromC", "Deco model: Recreational mode based on Bühlmann ZHL-16B with GFLow = %d%% and GFHigh = %d%%"),
+ diveplan->gflow, diveplan->gfhigh);
+ }
+ len += snprintf(buffer + len, sz_buffer - len, "<div>%s<br>",temp);
+
+ const char *depth_unit;
+ int altitude = (int) get_depth_units((int) (log(1013.0 / diveplan->surface_pressure) * 7800000), NULL, &depth_unit);
+
+ len += snprintf(buffer + len, sz_buffer - len, translate("gettextFromC", "ATM pressure: %dmbar (%d%s)<br></div>"),
+ diveplan->surface_pressure,
+ altitude,
+ depth_unit);
+
+ /* Get SAC values and units for printing it in gas consumption */
+ double bottomsacvalue, decosacvalue;
+ int sacdecimals;
+ const char* sacunit;
+
+ bottomsacvalue = get_volume_units(prefs.bottomsac, &sacdecimals, &sacunit);
+ decosacvalue = get_volume_units(prefs.decosac, NULL, NULL);
+
+ /* Reduce number of decimals from 1 to 0 for bar/min, keep 2 for cuft/min */
+ if (sacdecimals==1) sacdecimals--;
+
+ /* Print the gas consumption next.*/
+ if (dive->dc.divemode == CCR)
+ snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Gas consumption (CCR legs excluded):"));
+ else
+ snprintf(temp, sz_temp, "%s %.*f|%.*f%s/min):", translate("gettextFromC", "Gas consumption (based on SAC"),
+ sacdecimals, bottomsacvalue, sacdecimals, decosacvalue, sacunit);
+ len += snprintf(buffer + len, sz_buffer - len, "<div>%s<br>", temp);
+
+ /* Print gas consumption: This loop covers all cylinders */
+ for (int gasidx = 0; gasidx < MAX_CYLINDERS; gasidx++) {
+ double volume, pressure, deco_volume, deco_pressure, mingas_volume, mingas_pressure, mingas_depth;
+ const char *unit, *pressure_unit, *depth_unit;
+ char warning[1000] = "";
+ char mingas[1000] = "";
+ cylinder_t *cyl = &dive->cylinder[gasidx];
+ if (cylinder_none(cyl))
+ break;
+
+ volume = get_volume_units(cyl->gas_used.mliter, NULL, &unit);
+ deco_volume = get_volume_units(cyl->deco_gas_used.mliter, NULL, &unit);
+ if (cyl->type.size.mliter) {
+ int remaining_gas = lrint((double)cyl->end.mbar * cyl->type.size.mliter / 1000.0 / gas_compressibility_factor(&cyl->gasmix, cyl->end.mbar / 1000.0));
+ double deco_pressure_bar = isothermal_pressure(&cyl->gasmix, 1.0, remaining_gas + cyl->deco_gas_used.mliter, cyl->type.size.mliter)
+ - cyl->end.mbar / 1000.0;
+ deco_pressure = get_pressure_units(lrint(1000.0 * deco_pressure_bar), &pressure_unit);
+ pressure = get_pressure_units(cyl->start.mbar - cyl->end.mbar, &pressure_unit);
+ /* Warn if the plan uses more gas than is available in a cylinder
+ * This only works if we have working pressure for the cylinder
+ * 10bar is a made up number - but it seemed silly to pretend you could breathe cylinder down to 0 */
+ if (cyl->end.mbar < 10000)
+ snprintf(warning, sizeof(warning), "<br>&nbsp;&mdash; <span style='color: red;'>%s </span> %s",
+ translate("gettextFromC", "Warning:"),
+ translate("gettextFromC", "this is more gas than available in the specified cylinder!"));
+ else
+ if ((float) cyl->end.mbar * cyl->type.size.mliter / 1000.0 / gas_compressibility_factor(&cyl->gasmix, cyl->end.mbar / 1000.0)
+ < (float) cyl->deco_gas_used.mliter)
+ snprintf(warning, sizeof(warning), "<br>&nbsp;&mdash; <span style='color: red;'>%s </span> %s",
+ translate("gettextFromC", "Warning:"),
+ translate("gettextFromC", "not enough reserve for gas sharing on ascent!"));
+
+ /* Do and print minimum gas calculation for last bottom gas, but only for OC mode, */
+ /* not for recreational mode and if no other warning was set before. */
+ else
+ if (lastbottomdp && gasidx == lastbottomdp->cylinderid
+ && dive->dc.divemode == OC && decoMode() != RECREATIONAL) {
+ /* Calculate minimum gas volume. */
+ volume_t mingasv;
+ mingasv.mliter = lrint(prefs.sacfactor / 100.0 * prefs.problemsolvingtime * prefs.bottomsac
+ * depth_to_bar(lastbottomdp->depth.mm, dive)
+ + prefs.sacfactor / 100.0 * cyl->deco_gas_used.mliter);
+ /* Calculate minimum gas pressure for cyclinder. */
+ lastbottomdp->minimum_gas.mbar = lrint(isothermal_pressure(&cyl->gasmix, 1.0,
+ mingasv.mliter, cyl->type.size.mliter) * 1000);
+ /* Translate all results into correct units */
+ mingas_volume = get_volume_units(mingasv.mliter, NULL, &unit);
+ mingas_pressure = get_pressure_units(lastbottomdp->minimum_gas.mbar, &pressure_unit);
+ mingas_depth = get_depth_units(lastbottomdp->depth.mm, NULL, &depth_unit);
+ /* Print it to results */
+ if (cyl->start.mbar > lastbottomdp->minimum_gas.mbar) snprintf(mingas, sizeof(mingas),
+ translate("gettextFromC", "<br>&nbsp;&mdash; <span style='color: green;'>Minimum gas</span> (based on %.1fxSAC/+%dmin@%.0f%s): %.0f%s/%.0f%s"),
+ prefs.sacfactor / 100.0, prefs.problemsolvingtime,
+ mingas_depth, depth_unit,
+ mingas_volume, unit,
+ mingas_pressure, pressure_unit);
+ else snprintf(warning, sizeof(warning), "<br>&nbsp;&mdash; <span style='color: red;'>%s </span> %s",
+ translate("gettextFromC", "Warning:"),
+ translate("gettextFromC", "required minimum gas for ascent already exceeding start pressure of cylinder!"));
+ }
+ /* Print the gas consumption for every cylinder here to temp buffer. */
+ snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s/%.0f%s of <span style='color: red;'><b>%s</b></span> (%.0f%s/%.0f%s in planned ascent)"), volume, unit, pressure, pressure_unit, gasname(&cyl->gasmix), deco_volume, unit, deco_pressure, pressure_unit);
+
+ } else {
+ snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s (%.0f%s during planned ascent) of <span style='color: red;'><b>%s</b></span>"),
+ volume, unit, deco_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);
+ }
+
+ /* Print warnings for pO2 */
+ dp = diveplan->dp;
+ bool o2warning_exist = false;
+ if (dive->dc.divemode != CCR) {
+ while (dp) {
+ if (dp->time != 0) {
+ struct gas_pressures pressures;
+ struct gasmix *gasmix = &dive->cylinder[dp->cylinderid].gasmix;
+ fill_pressures(&pressures, depth_to_atm(dp->depth.mm, dive), gasmix, 0.0, dive->dc.divemode);
+
+ if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) {
+ const char *depth_unit;
+ int decimals;
+ double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit);
+ len = strlen(buffer);
+ if (!o2warning_exist) len += snprintf(buffer + len, sz_buffer - len, "<br>");
+ o2warning_exist = true;
+ snprintf(temp, sz_temp,
+ translate("gettextFromC", "high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
+ pressures.o2, FRACTION(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit);
+ len += snprintf(buffer + len, sz_buffer - len, "<span style='color: red;'>%s </span> %s<br>",
+ translate("gettextFromC", "Warning:"), temp);
+ } else if (pressures.o2 < 0.16) {
+ const char *depth_unit;
+ int decimals;
+ double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit);
+ len = strlen(buffer);
+ if (!o2warning_exist) len += snprintf(buffer + len, sz_buffer - len, "<br>");
+ o2warning_exist = true;
+ snprintf(temp, sz_temp,
+ translate("gettextFromC", "low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
+ pressures.o2, FRACTION(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit);
+ len += snprintf(buffer + len, sz_buffer - len, "<span style='color: red;'>%s </span> %s<br>",
+ translate("gettextFromC", "Warning:"), temp);
+
+ }
+ }
+ dp = dp->next;
+ }
+ }
+ snprintf(buffer + len, sz_buffer - len, "</div>");
+ dive->notes = strdup(buffer);
+
+ free((void *)buffer);
+ free((void *)temp);
+}
diff --git a/core/qthelperfromc.h b/core/qthelperfromc.h
index 91fbde415..3ea18e7cf 100644
--- a/core/qthelperfromc.h
+++ b/core/qthelperfromc.h
@@ -21,5 +21,7 @@ char *picturedir_string();
const char *subsurface_user_agent();
enum deco_mode decoMode();
int parse_seabear_header(const char *filename, char **params, int pnr);
+extern const char *get_current_date();
+
#endif // QTHELPERFROMC_H