Пример #1
0
static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, int error)
{
	char buffer[2000000], temp[100000];
	int len, lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0;
	struct divedatapoint *dp = diveplan->dp;
	bool gaschange = !plan_verbatim, postponed = plan_verbatim;
	struct divedatapoint *nextdp = NULL;

	disclaimer =  translate("gettextFromC", "DISCLAIMER / WARNING: THIS IS A NEW IMPLEMENTATION OF THE BUHLMANN "
				"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.");

	if (!dp)
		return;

	if (error) {
		snprintf(temp, sizeof(temp), "%s",
			 translate("gettextFromC", "Decompression calculation aborted due to excessive time"));
		snprintf(buffer, sizeof(buffer), "<span style='color: red;'>%s </span> %s<br>",
				translate("gettextFromC", "Warning:"), temp);
		dive->notes = strdup(buffer);
		return;
	}

	len = show_disclaimer ? snprintf(buffer, sizeof(buffer), "<div><b>%s<b></div><br>", disclaimer) : 0;
	snprintf(temp, sizeof(temp), translate("gettextFromC", "based on GFlow = %d and GFhigh = %d"),
		 diveplan->gflow, diveplan->gfhigh);
	len += snprintf(buffer + len, sizeof(buffer) - len, "<div><b>%s</b><br>%s</div><br>",
			translate("gettextFromC", "Subsurface dive plan"), temp);

	if (!plan_verbatim) {
		len += snprintf(buffer + len, sizeof(buffer) - len, "<div><table><thead><tr><th>%s</th>",
				translate("gettextFromC", "depth"));
		if (plan_display_duration)
			len += snprintf(buffer + len, sizeof(buffer) - len, "<th style='padding-left: 10px;'>%s</th>",
					translate("gettextFromC", "duration"));
		if (plan_display_runtime)
			len += snprintf(buffer + len, sizeof(buffer) - len, "<th style='padding-left: 10px;'>%s</th>",
					translate("gettextFromC", "runtime"));
		len += snprintf(buffer + len, sizeof(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;

		nextdp = dp->next;
		if (dp->time == 0)
			continue;
		gasmix = dp->gasmix;
		depthvalue = get_depth_units(dp->depth, &decimals, &depth_unit);
		/* analyze the dive points ahead */
		while (nextdp && nextdp->time == 0)
			nextdp = nextdp->next;
		if (nextdp)
			newgasmix = nextdp->gasmix;
		/* do we want to skip this leg as it is devoid of anything useful? */
		if (!dp->entered &&
		    gasmix_distance(&gasmix, &newgasmix) == 0 &&
		    nextdp &&
		    dp->setpoint == nextdp->setpoint &&
		    dp->depth != lastdepth &&
		    nextdp->depth != dp->depth
		    && !gaschange)
			continue;
		if (dp->time - lasttime < 10 && !(gaschange && dp->next && dp->depth != dp->next->depth))
			continue;

		len = strlen(buffer);
		if (nextdp && (gasmix_distance(&gasmix, &newgasmix) || dp->setpoint != nextdp->setpoint))
			gaschange = true;
		if (plan_verbatim) {
			if (dp->depth != lastprintdepth) {
				if (plan_display_transitions || dp->entered || !dp->next || (gaschange && dp->next && dp->depth != nextdp->depth)) {
					if (dp->setpoint)
						snprintf(temp, sizeof(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, sizeof(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, sizeof(buffer) - len, "%s<br>", temp);
				}
				newdepth = dp->depth;
				lasttime = dp->time;
			} else {
				if (dp->depth != nextdp->depth || gasmix_distance(&gasmix, &newgasmix) != 0 || dp->setpoint != nextdp->setpoint) {
					if (dp->setpoint)
						snprintf(temp, sizeof(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, sizeof(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, sizeof(buffer) - len, "%s<br>", temp);
					newdepth = dp->depth;
					lasttime = dp->time;
				}
			}
		} else {
			if ((dp->depth == lastdepth && dp->depth != nextdp->depth) || plan_display_transitions || dp->entered || !dp->next || (gaschange && dp->next && dp->depth != nextdp->depth)) {
				snprintf(temp, sizeof(temp), translate("gettextFromC", "%3.0f%s"), depthvalue, depth_unit);
				len += snprintf(buffer + len, sizeof(buffer) - len, "<tr><td style='padding-left: 10px; float: right;'>%s</td>", temp);
				if (plan_display_duration) {
					snprintf(temp, sizeof(temp), translate("gettextFromC", "%3dmin"), (dp->time - lasttime + 30) / 60);
					len += snprintf(buffer + len, sizeof(buffer) - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
				}
				if (plan_display_runtime) {
					snprintf(temp, sizeof(temp), translate("gettextFromC", "%3dmin"), (dp->time + 30) / 60);
					len += snprintf(buffer + len, sizeof(buffer) - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
				}

				if (gaschange) {
					if(dp->depth == lastdepth && !postponed) {
						postponed = true;
					} else {
						if (dp->setpoint) {
							snprintf(temp, sizeof(temp), translate("gettextFromC", "(SP = %.1fbar)"), (double) dp->setpoint / 1000.0);
							len += snprintf(buffer + len, sizeof(buffer) - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&newgasmix),
									temp);
						} else {
							len += snprintf(buffer + len, sizeof(buffer) - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&newgasmix));
						}
						gaschange = false;
						postponed = false;
					}
				} else {
					len += snprintf(buffer + len, sizeof(buffer) - len, "<td>&nbsp;</td>");
				}
				len += snprintf(buffer + len, sizeof(buffer) - len, "</tr>");
				newdepth = dp->depth;
				lasttime = dp->time;
			}
		}
		if (gaschange) {
			if(dp->depth == lastdepth && !postponed) {
				postponed = true;
			} else {
			// gas switch at this waypoint
			if (plan_verbatim) {
				if (lastsetpoint >= 0) {
					if (nextdp && nextdp->setpoint)
						snprintf(temp, sizeof(temp), translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), gasname(&newgasmix), (double) nextdp->setpoint / 1000.0);
					else
						snprintf(temp, sizeof(temp), translate("gettextFromC", "Switch gas to %s"), gasname(&newgasmix));

					len += snprintf(buffer + len, sizeof(buffer) - len, "%s<br>", temp);
				}
				gaschange = false;
				postponed = true;
			}
			gasmix = newgasmix;
			}
		}
		lastprintdepth = newdepth;
		lastdepth = dp->depth;
		lastsetpoint = dp->setpoint;
	} while ((dp = nextdp) != NULL);
	len += snprintf(buffer + len, sizeof(buffer) - len, "</tbody></table></div>");

	dive->cns = 0;
	dive->maxcns = 0;
	update_cylinder_related_info(dive);
	snprintf(temp, sizeof(temp), "%s", translate("gettextFromC", "CNS"));
	len += snprintf(buffer + len, sizeof(buffer) - len, "<div><br>%s: %i%%", temp, dive->cns);
	snprintf(temp, sizeof(temp), "%s", translate("gettextFromC", "OTU"));
	len += snprintf(buffer + len, sizeof(buffer) - len, "<br>%s: %i</div>", temp, dive->otu);

	if (dive->dc.divemode == CCR)
		snprintf(temp, sizeof(temp), "%s", translate("gettextFromC", "Gas consumption (CCR legs excluded):"));
	else
		snprintf(temp, sizeof(temp), "%s", translate("gettextFromC", "Gas consumption:"));
	len += snprintf(buffer + len, sizeof(buffer) - len, "<div><br>%s<br>", temp);
	for (int gasidx = 0; gasidx < MAX_CYLINDERS; gasidx++) {
		double volume, pressure, deco_volume, deco_pressure;
		const char *unit, *pressure_unit;
		char warning[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) {
			deco_pressure = get_pressure_units(1000.0 * cyl->deco_gas_used.mliter / cyl->type.size.mliter, &pressure_unit);
			pressure = get_pressure_units(1000.0 * cyl->gas_used.mliter / cyl->type.size.mliter, &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), " &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 < (float) cyl->deco_gas_used.mliter)
					snprintf(warning, sizeof(warning), " &mdash; <span style='color: red;'>%s </span> %s",
						translate("gettextFromC", "Warning:"),
						translate("gettextFromC", "not enough reserve for gas sharing on ascent!"));

			snprintf(temp, sizeof(temp), translate("gettextFromC", "%.0f%s/%.0f%s of %s (%.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, sizeof(temp), translate("gettextFromC", "%.0f%s (%.0f%s during planned ascent) of %s"), volume, unit, deco_volume, unit, gasname(&cyl->gasmix));
		}
		len += snprintf(buffer + len, sizeof(buffer) - len, "%s%s<br>", temp, warning);
	}
	dp = diveplan->dp;
	if (dive->dc.divemode != CCR) {
		while (dp) {
			if (dp->time != 0) {
				struct gas_pressures pressures;
				fill_pressures(&pressures, depth_to_atm(dp->depth, dive), &dp->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, &decimals, &depth_unit);
					len = strlen(buffer);
					snprintf(temp, sizeof(temp),
						 translate("gettextFromC", "high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
						 pressures.o2, FRACTION(dp->time, 60), gasname(&dp->gasmix), decimals, depth_value, depth_unit);
					len += snprintf(buffer + len, sizeof(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, &decimals, &depth_unit);
					len = strlen(buffer);
					snprintf(temp, sizeof(temp),
						 translate("gettextFromC", "low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
						 pressures.o2, FRACTION(dp->time, 60), gasname(&dp->gasmix), decimals, depth_value, depth_unit);
					len += snprintf(buffer + len, sizeof(buffer) - len, "<span style='color: red;'>%s </span> %s<br>",
							translate("gettextFromC", "Warning:"), temp);

				}
			}
			dp = dp->next;
		}
	}
	snprintf(buffer + len, sizeof(buffer) - len, "</div>");
	dive->notes = strdup(buffer);
}
Пример #2
0
static void calculate_gas_information_new(struct dive *dive, struct plot_info *pi)
{
	int i;
	double amb_pressure;

	for (i = 1; i < pi->nr; i++) {
		int fn2, fhe;
		struct plot_data *entry = pi->entry + i;
		int cylinderindex = entry->cylinderindex;

		amb_pressure = depth_to_bar(entry->depth, dive);

		fill_pressures(&entry->pressures, amb_pressure, &dive->cylinder[cylinderindex].gasmix, entry->o2pressure.mbar / 1000.0, dive->dc.divemode);
		fn2 = (int)(1000.0 * entry->pressures.n2 / amb_pressure);
		fhe = (int)(1000.0 * entry->pressures.he / amb_pressure);

		/* Calculate MOD, EAD, END and EADD based on partial pressures calculated before
		 * so there is no difference in calculating between OC and CC
		 * END takes O₂ + N₂ (air) into account ("Narcotic" for trimix dives)
		 * EAD just uses N₂ ("Air" for nitrox dives) */
		pressure_t modpO2 = { .mbar = (int)(prefs.modpO2 * 1000) };
		entry->mod = (double)gas_mod(&dive->cylinder[cylinderindex].gasmix, modpO2, dive, 1).mm;
		entry->end = (entry->depth + 10000) * (1000 - fhe) / 1000.0 - 10000;
		entry->ead = (entry->depth + 10000) * fn2 / (double)N2_IN_AIR - 10000;
		entry->eadd = (entry->depth + 10000) *
				      (entry->pressures.o2 / amb_pressure * O2_DENSITY +
				       entry->pressures.n2 / amb_pressure * N2_DENSITY +
				       entry->pressures.he / amb_pressure * HE_DENSITY) /
				      (O2_IN_AIR * O2_DENSITY + N2_IN_AIR * N2_DENSITY) * 1000 - 10000;
		if (entry->mod < 0)
			entry->mod = 0;
		if (entry->ead < 0)
			entry->ead = 0;
		if (entry->end < 0)
			entry->end = 0;
		if (entry->eadd < 0)
			entry->eadd = 0;
	}
}

void fill_o2_values(struct divecomputer *dc, struct plot_info *pi, struct dive *dive)
/* In the samples from each dive computer, there may be uninitialised oxygen
 * sensor or setpoint values, e.g. when events were inserted into the dive log
 * or if the dive computer does not report o2 values with every sample. But
 * for drawing the profile a complete series of valid o2 pressure values is
 * required. This function takes the oxygen sensor data and setpoint values
 * from the structures of plotinfo and replaces the zero values with their
 * last known values so that the oxygen sensor data are complete and ready
 * for plotting. This function called by: create_plot_info_new() */
{
	int i, j;
	pressure_t last_sensor[3], o2pressure;
	pressure_t amb_pressure;

	for (i = 0; i < pi->nr; i++) {
		struct plot_data *entry = pi->entry + i;

		if (dc->divemode == CCR) {
			if (i == 0) { // For 1st iteration, initialise the last_sensor values
				for (j = 0; j < dc->no_o2sensors; j++)
					last_sensor[j].mbar = pi->entry->o2sensor[j].mbar;
			} else { // Now re-insert the missing oxygen pressure values
				for (j = 0; j < dc->no_o2sensors; j++)
					if (entry->o2sensor[j].mbar)
						last_sensor[j].mbar = entry->o2sensor[j].mbar;
					else
						entry->o2sensor[j].mbar = last_sensor[j].mbar;
			} // having initialised the empty o2 sensor values for this point on the profile,
			amb_pressure.mbar = depth_to_mbar(entry->depth, dive);
			o2pressure.mbar = calculate_ccr_po2(entry, dc); // ...calculate the po2 based on the sensor data
			entry->o2pressure.mbar = MIN(o2pressure.mbar, amb_pressure.mbar);
		} else {
			entry->o2pressure.mbar = 0; // initialise po2 to zero for dctype = OC
		}
	}
}
Пример #3
0
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);
	char buf[1000], *deco;
	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;
	struct divedatapoint *nextdp = 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 (prefs.deco_mode == VPMB) {
		deco = "VPM-B";
	} else {
		deco = "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></div><br>", disclaimer) : 0;
	if (prefs.deco_mode == BUEHLMANN){
		snprintf(temp, sz_temp, translate("gettextFromC", "based on Buhlmann ZHL-16B with GFlow = %d and GFhigh = %d"),
			diveplan->gflow, diveplan->gfhigh);
	} else if (prefs.deco_mode == VPMB){
		snprintf(temp, sz_temp, "%s", translate("gettextFromC", "based on VPM-B"));
	} else if (prefs.deco_mode == RECREATIONAL){
		snprintf(temp, sz_temp, translate("gettextFromC", "recreational mode based on Buhlmann ZHL-16B with GFlow = %d and GFhigh = %d"),
			diveplan->gflow, diveplan->gfhigh);
	}
	len += snprintf(buffer + len, sz_buffer - len, "<div><b>%s</b><br>%s</div><br>",
			translate("gettextFromC", "Subsurface dive plan"), temp);

	if (!plan_verbatim) {
		len += snprintf(buffer + len, sz_buffer - len, "<div><table><thead><tr><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 < lastdepth);

		nextdp = dp->next;
		if (dp->time == 0)
			continue;
		gasmix = dp->gasmix;
		depthvalue = get_depth_units(dp->depth, &decimals, &depth_unit);
		/* analyze the dive points ahead */
		while (nextdp && nextdp->time == 0)
			nextdp = nextdp->next;
		if (nextdp)
			newgasmix = nextdp->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 != lastdepth &&
		    nextdp->depth != dp->depth &&
		    !gaschange_before &&
		    !gaschange_after)
			continue;
		if (dp->time - lasttime < 10 && !(gaschange_after && dp->next && dp->depth != dp->next->depth))
			continue;

		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 != lastprintdepth) {
				if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth != nextdp->depth)) {
					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;
				lasttime = dp->time;
			} else {
				if ((nextdp && dp->depth != nextdp->depth) || 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;
					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 != nextdp->depth) ||
			    (!isascent && gaschange_before && nextdp && dp->depth != nextdp->depth) ||
			    (gaschange_after && lastentered) || (gaschange_after && !isascent) ||
			    (isascent && gaschange_after && nextdp && dp->depth != nextdp->depth )) {
				snprintf(temp, sz_temp, translate("gettextFromC", "%3.0f%s"), depthvalue, depth_unit);
				len += snprintf(buffer + len, sz_buffer - len, "<tr><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 && gaschange_after && dp->next && nextdp && dp->depth != nextdp->depth) {
					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;
				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;
		lastsetpoint = dp->setpoint;
		lastentered = dp->entered;
	} while ((dp = nextdp) != NULL);
	len += snprintf(buffer + len, sz_buffer - len, "</tbody></table></div>");

	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><br>%s: %i%%", temp, dive->cns);
	snprintf(temp, sz_temp, "%s", translate("gettextFromC", "OTU"));
	len += snprintf(buffer + len, sz_buffer - len, "<br>%s: %i</div>", temp, dive->otu);

	if (dive->dc.divemode == CCR)
		snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Gas consumption (CCR legs excluded):"));
	else
		snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Gas consumption:"));
	len += snprintf(buffer + len, sz_buffer - len, "<div><br>%s<br>", temp);
	for (int gasidx = 0; gasidx < MAX_CYLINDERS; gasidx++) {
		double volume, pressure, deco_volume, deco_pressure;
		const char *unit, *pressure_unit;
		char warning[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) {
			deco_pressure = get_pressure_units(1000.0 * cyl->deco_gas_used.mliter / cyl->type.size.mliter, &pressure_unit);
			pressure = get_pressure_units(1000.0 * cyl->gas_used.mliter / cyl->type.size.mliter, &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), " &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 < (float) cyl->deco_gas_used.mliter)
					snprintf(warning, sizeof(warning), " &mdash; <span style='color: red;'>%s </span> %s",
						translate("gettextFromC", "Warning:"),
						translate("gettextFromC", "not enough reserve for gas sharing on ascent!"));

			snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s/%.0f%s of %s (%.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 %s"), volume, unit, deco_volume, unit, gasname(&cyl->gasmix));
		}
		len += snprintf(buffer + len, sz_buffer - len, "%s%s<br>", temp, warning);
	}
	dp = diveplan->dp;
	if (dive->dc.divemode != CCR) {
		while (dp) {
			if (dp->time != 0) {
				struct gas_pressures pressures;
				fill_pressures(&pressures, depth_to_atm(dp->depth, dive), &dp->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, &decimals, &depth_unit);
					len = strlen(buffer);
					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(&dp->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, &decimals, &depth_unit);
					len = strlen(buffer);
					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(&dp->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);
}