Exemplo n.º 1
0
// copy the tanks from the current dive, or the default cylinder
// or an unknown cylinder
// setup the cylinder widget accordingly
void DivePlannerPointsModel::setupCylinders()
{
	int i;
	if (mode == PLAN && current_dive) {
		// take the displayed cylinders from the selected dive as starting point
		CylindersModel::instance()->copyFromDive(current_dive);
		copy_cylinders(current_dive, &displayed_dive, !prefs.display_unused_tanks);
		reset_cylinders(&displayed_dive, true);

		for (i = 0; i < MAX_CYLINDERS; i++)
			if (!cylinder_none(&(displayed_dive.cylinder[i])))
				return;		// We have at least one cylinder
	}
	if (!same_string(prefs.default_cylinder, "")) {
		fill_default_cylinder(&displayed_dive.cylinder[0]);
	}
	if (cylinder_none(&displayed_dive.cylinder[0])) {
		// roughly an AL80
		displayed_dive.cylinder[0].type.description = strdup(tr("unknown").toUtf8().constData());
		displayed_dive.cylinder[0].type.size.mliter = 11100;
		displayed_dive.cylinder[0].type.workingpressure.mbar = 207000;
	}
	reset_cylinders(&displayed_dive, false);
	CylindersModel::instance()->copyFromDive(&displayed_dive);
}
Exemplo n.º 2
0
bool is_cylinder_used(struct dive *dive, int idx)
{
	struct divecomputer *dc;
	bool firstGasExplicit = false;
	if (cylinder_none(&dive->cylinder[idx]))
		return false;

	if ((dive->cylinder[idx].start.mbar - dive->cylinder[idx].end.mbar) > SOME_GAS)
		return true;
	for_each_dc(dive, dc) {
		struct event *event = get_next_event(dc->events, "gaschange");
		while (event) {
			if (dc->sample && (event->time.seconds == 0 ||
					   (dc->samples && dc->sample[0].time.seconds == event->time.seconds)))
				firstGasExplicit = true;
			if (get_cylinder_index(dive, event) == idx)
				return true;
			event = get_next_event(event->next, "gaschange");
		}
		if (dc->divemode == CCR && (idx == dive->diluent_cylinder_index || idx == dive->oxygen_cylinder_index))
			return true;
	}
	if (idx == 0 && !firstGasExplicit)
		return true;
	return false;
}
Exemplo n.º 3
0
/*
 * Get "maximal" dive gas for a dive.
 * Rules:
 *  - Trimix trumps nitrox (highest He wins, O2 breaks ties)
 *  - Nitrox trumps air (even if hypoxic)
 * These are the same rules as the inter-dive sorting rules.
 */
static void get_dive_gas(struct dive *dive, int *o2, int *he, int *o2low)
{
	int i;
	int maxo2 = -1, maxhe = -1, mino2 = 1000;

	for (i = 0; i < MAX_CYLINDERS; i++) {
		cylinder_t *cyl = dive->cylinder + i;
		struct gasmix *mix = &cyl->gasmix;
		int o2 = mix->o2.permille;
		int he = mix->he.permille;

		if (cylinder_none(cyl))
			continue;
		if (!o2)
			o2 = AIR_PERMILLE;
		if (o2 < mino2)
			mino2 = o2;
		if (he > maxhe)
			goto newmax;
		if (he < maxhe)
			continue;
		if (o2 <= maxo2)
			continue;
newmax:
		maxhe = he;
		maxo2 = o2;
	}
	/* All air? Show/sort as "air"/zero */
	if (!maxhe && maxo2 == AIR_PERMILLE && mino2 == maxo2)
		maxo2 = mino2 = 0;
	*o2 = maxo2;
	*he = maxhe;
	*o2low = mino2;
}
Exemplo n.º 4
0
gboolean no_cylinders(cylinder_t *cyl)
{
	int i;

	for (i = 0; i < MAX_CYLINDERS; i++)
		if (!cylinder_none(cyl + i))
			return FALSE;
	return TRUE;
}
Exemplo n.º 5
0
void CylindersModel::copyFromDive(dive *d)
{
	if (!d)
		return;
	rows = 0;
	for (int i = 0; i < MAX_CYLINDERS; i++) {
		if (!cylinder_none(&d->cylinder[i]) && is_cylinder_used(d, i)) {
			rows = i + 1;
		}
	}
	if (rows > 0) {
		beginInsertRows(QModelIndex(), 0, rows - 1);
		endInsertRows();
	}
}
Exemplo n.º 6
0
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned
 * and that the pressures are reset to start = end = workingpressure */
void reset_cylinders(struct dive *dive)
{
	int i;
	pressure_t pO2 = {.mbar = 1400};

	for (i = 0; i < MAX_CYLINDERS; i++) {
		cylinder_t *cyl = &dive->cylinder[i];
		if (cylinder_none(cyl))
			continue;
		if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, assume conservative pO2 */
			cyl->depth = gas_mod(&cyl->gasmix, pO2, M_OR_FT(3,10));
		if (cyl->type.workingpressure.mbar)
			cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar;
		cyl->gas_used.mliter = 0;
	}
}
Exemplo n.º 7
0
void CylindersModel::updateDive()
{
	clear();
	rows = 0;
	for (int i = 0; i < MAX_CYLINDERS; i++) {
		if (!cylinder_none(&displayed_dive.cylinder[i]) &&
		    (prefs.display_unused_tanks ||
		     is_cylinder_used(&displayed_dive, i) ||
		     displayed_dive.cylinder[i].manually_added))
			rows = i + 1;
	}
	if (rows > 0) {
		beginInsertRows(QModelIndex(), 0, rows - 1);
		endInsertRows();
	}
}
Exemplo n.º 8
0
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned
 * and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */
void reset_cylinders(struct dive *dive, bool track_gas)
{
	int i;
	pressure_t decopo2 = {.mbar = prefs.decopo2};

	for (i = 0; i < MAX_CYLINDERS; i++) {
		cylinder_t *cyl = &dive->cylinder[i];
		if (cylinder_none(cyl))
			continue;
		if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */
			cyl->depth = gas_mod(&cyl->gasmix, decopo2, dive, M_OR_FT(3,10));
		if (track_gas)
			cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar;
		cyl->gas_used.mliter = 0;
		cyl->deco_gas_used.mliter = 0;
	}
}
Exemplo n.º 9
0
void CylindersModel::setDive(dive* d)
{
	if (current)
		clear();

	int amount = MAX_CYLINDERS;
	for(int i = 0; i < MAX_CYLINDERS; i++) {
		cylinder_t *cylinder = &d->cylinder[i];
		if (cylinder_none(cylinder)) {
			amount = i;
			break;
		}
	}

	beginInsertRows(QModelIndex(), 0, amount-1);
	rows = amount;
	current = d;
	endInsertRows();
}
Exemplo n.º 10
0
bool is_cylinder_used(struct dive *dive, int idx)
{
	struct divecomputer *dc;
	bool firstGasExplicit = false;
	if (cylinder_none(&dive->cylinder[idx]))
		return false;

	for_each_dc(dive, dc) {
		struct event *event = get_next_event(dc->events, "gaschange");
		while (event) {
			if (event->time.seconds < 30)
				firstGasExplicit = true;
			if (get_cylinder_index(dive, event) == idx)
				return true;
			event = get_next_event(event->next, "gaschange");
		}
	}
	if (idx == 0 && !firstGasExplicit)
		return true;
	return false;
}
Exemplo n.º 11
0
void CylindersModel::remove(const QModelIndex &index)
{
	if (index.column() != REMOVE) {
		return;
	}
	int same_gas = -1;
	cylinder_t *cyl = &displayed_dive.cylinder[index.row()];
	struct gasmix *mygas = &cyl->gasmix;
	for (int i = 0; i < MAX_CYLINDERS; i++) {
		if (i == index.row() || cylinder_none(&displayed_dive.cylinder[i]))
			continue;
		struct gasmix *gas2 = &displayed_dive.cylinder[i].gasmix;
		if (gasmix_distance(mygas, gas2) == 0)
			same_gas = i;
	}
	if (same_gas == -1 &&
	    ((DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING &&
	      DivePlannerPointsModel::instance()->tankInUse(cyl->gasmix)) ||
	     (DivePlannerPointsModel::instance()->currentMode() == DivePlannerPointsModel::NOTHING &&
	      (cyl->manually_added || is_cylinder_used(&displayed_dive, index.row()))))) {
		QMessageBox::warning(MainWindow::instance(), TITLE_OR_TEXT(
									tr("Cylinder cannot be removed"),
									tr("This gas is in use. Only cylinders that are not used in the dive can be removed.")),
				     QMessageBox::Ok);
		return;
	}
	beginRemoveRows(QModelIndex(), index.row(), index.row()); // yah, know, ugly.
	rows--;
	if (index.row() == 0) {
		// first gas - we need to make sure that the same gas ends up
		// as first gas
		memmove(cyl, &displayed_dive.cylinder[same_gas], sizeof(*cyl));
		remove_cylinder(&displayed_dive, same_gas);
	} else {
		remove_cylinder(&displayed_dive, index.row());
	}
	changed = true;
	endRemoveRows();
}
Exemplo n.º 12
0
bool is_gas_used(struct dive *dive, int idx)
{
	cylinder_t *cyl = &dive->cylinder[idx];
	int o2, he;
	struct divecomputer *dc;
	bool used = FALSE;
	bool firstGasExplicit = FALSE;
	if (cylinder_none(cyl))
		return FALSE;

	o2 = get_o2(&cyl->gasmix);
	he = get_he(&cyl->gasmix);
	dc = &dive->dc;
	while (dc) {
		struct event *event = dc->events;
		while (event) {
			if (event->value) {
				if (event->name && !strcmp(event->name, "gaschange")) {
					unsigned int event_he = event->value >> 16;
					unsigned int event_o2 = event->value & 0xffff;
					if (event->time.seconds < 30)
						firstGasExplicit = TRUE;
					if (is_air(o2, he)) {
						if (is_air(event_o2 * 10, event_he * 10))
							used = TRUE;
					} else if (he == event_he * 10 && o2 == event_o2 * 10) {
						used = TRUE;
					}
				}
			}
			if (used)
				break;
			event = event->next;
		}
		if (used)
			break;
		dc = dc->next;
	}
Exemplo n.º 13
0
static void show_single_dive_stats(struct dive *dive)
{
    char buf[80];
    double value;
    int decimals;
    const char *unit;
    int idx, offset, gas_used;
    struct dive *prev_dive;
    struct tm tm;

    process_all_dives(dive, &prev_dive);

    utc_mkdate(dive->when, &tm);
    snprintf(buf, sizeof(buf),
             /*++GETTEXT 80 chars: weekday, monthname, day, year, hour, min */
             _("%1$s, %2$s %3$d, %4$d %5$2d:%6$02d"),
             weekday(tm.tm_wday),
             monthname(tm.tm_mon),
             tm.tm_mday, tm.tm_year + 1900,
             tm.tm_hour, tm.tm_min);

    set_label(single_w.date, buf);
    set_label(single_w.dive_time, _("%d min"), (dive->duration.seconds + 30) / 60);
    if (prev_dive)
        set_label(single_w.surf_intv,
                  get_time_string(dive->when - (prev_dive->when + prev_dive->duration.seconds), 4));
    else
        set_label(single_w.surf_intv, _("unknown"));
    value = get_depth_units(dive->maxdepth.mm, &decimals, &unit);
    set_label(single_w.max_depth, "%.*f %s", decimals, value, unit);
    value = get_depth_units(dive->meandepth.mm, &decimals, &unit);
    set_label(single_w.avg_depth, "%.*f %s", decimals, value, unit);
    if (dive->watertemp.mkelvin) {
        value = get_temp_units(dive->watertemp.mkelvin, &unit);
        set_label(single_w.water_temp, "%.1f %s", value, unit);
    } else
        set_label(single_w.water_temp, "");
    value = get_volume_units(dive->sac, &decimals, &unit);
    if (value > 0) {
        set_label(single_w.sac, _("%.*f %s/min"), decimals, value, unit);
    } else
        set_label(single_w.sac, "");
    set_label(single_w.otu, "%d", dive->otu);
    offset = 0;
    gas_used = 0;
    buf[0] = '\0';
    /* for the O2/He readings just create a list of them */
    for (idx = 0; idx < MAX_CYLINDERS; idx++) {
        cylinder_t *cyl = &dive->cylinder[idx];
        unsigned int start, end;

        start = cyl->start.mbar ? : cyl->sample_start.mbar;
        end = cyl->end.mbar ? : cyl->sample_end.mbar;
        if (!cylinder_none(cyl)) {
            /* 0% O2 strangely means air, so 21% - I don't like that at all */
            int o2 = cyl->gasmix.o2.permille ? : AIR_PERMILLE;
            if (offset > 0) {
                snprintf(buf+offset, 80-offset, ", ");
                offset += 2;
            }
            snprintf(buf+offset, 80-offset, "%d/%d", (o2 + 5) / 10,
                     (cyl->gasmix.he.permille + 5) / 10);
            offset = strlen(buf);
        }
        /* and if we have size, start and end pressure, we can
         * calculate the total gas used */
        if (cyl->type.size.mliter && start && end)
            gas_used += cyl->type.size.mliter / 1000.0 * (start - end);
    }
Exemplo n.º 14
0
/*
 * Get "maximal" dive gas for a dive.
 * Rules:
 *  - Trimix trumps nitrox (highest He wins, O2 breaks ties)
 *  - Nitrox trumps air (even if hypoxic)
 * These are the same rules as the inter-dive sorting rules.
 */
void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p)
{
	int i;
	int maxo2 = -1, maxhe = -1, mino2 = 1000;


	for (i = 0; i < MAX_CYLINDERS; i++) {
		cylinder_t *cyl = dive->cylinder + i;
		struct gasmix *mix = &cyl->gasmix;
		int o2 = mix->o2.permille;
		int he = mix->he.permille;
		struct divecomputer *dc = &dive->dc;
		int used = 0;
		int first_gas_explicit = 0;

		while (dc) {
			struct event *event = dc->events;
			while (event) {
				if (event->value) {
					if (event->name && !strcmp(event->name, "gaschange")) {
						unsigned int event_he = event->value >> 16;
						unsigned int event_o2 = event->value & 0xffff;

						if (event->time.seconds < 30)
							first_gas_explicit = 1;
						if (is_air(o2, he)) {
							if (is_air(event_o2 * 10, event_he * 10))
								used = 1;
						}
						else {
							if (he == event_he*10 && o2 == event_o2*10)
								used = 1;
						}
					}
				}
				event = event->next;
			}
			dc = dc->next;
		}

		/* Unless explicity set, the first gas to use has index 0 */
		if (i == 0 && !first_gas_explicit)
			used = 1;

		if (!used)
			continue;

		if (cylinder_none(cyl))
			continue;
		if (!o2)
			o2 = O2_IN_AIR;
		if (o2 < mino2)
			mino2 = o2;
		if (he > maxhe)
			goto newmax;
		if (he < maxhe)
			continue;
		if (o2 <= maxo2)
			continue;
newmax:
		maxhe = he;
		maxo2 = o2;
	}
Exemplo n.º 15
0
/* we try to show the data from the currently selected divecomputer
 * right now for some values (e.g., surface pressure) we could fall back
 * to dive data, but for consistency we don't. */
static void show_single_dive_stats(struct dive *dive)
{
	char buf[256];
	double value;
	int decimals;
	const char *unit;
	int idx, offset, gas_used, mbar;
	struct dive *prev_dive;
	struct tm tm;
	struct divecomputer *dc;

	process_all_dives(dive, &prev_dive);
	if (!dive)
		return;
	dc = select_dc(&dive->dc);
	utc_mkdate(dive->when, &tm);
	snprintf(buf, sizeof(buf),
		/*++GETTEXT 80 chars: weekday, monthname, day, year, hour, min */
		_("%1$s, %2$s %3$d, %4$d %5$2d:%6$02d"),
		weekday(tm.tm_wday),
		monthname(tm.tm_mon),
		tm.tm_mday, tm.tm_year + 1900,
		tm.tm_hour, tm.tm_min);

	set_label(single_w.date, buf);
	set_label(single_w.dive_time, _("%d min"), (dive->duration.seconds + 30) / 60);
	if (prev_dive)
		set_label(single_w.surf_intv,
			get_time_string(dive->when - (prev_dive->when + prev_dive->duration.seconds), 4));
	else
		set_label(single_w.surf_intv, _("unknown"));
	value = get_depth_units(dc->maxdepth.mm, &decimals, &unit);
	set_label(single_w.max_depth, "%.*f %s", decimals, value, unit);
	value = get_depth_units(dc->meandepth.mm, &decimals, &unit);
	set_label(single_w.avg_depth, "%.*f %s", decimals, value, unit);
	set_label(single_w.viz, star_strings[dive->visibility]);
	if (dc->watertemp.mkelvin) {
		value = get_temp_units(dc->watertemp.mkelvin, &unit);
		set_label(single_w.water_temp, "%.1f %s", value, unit);
	} else {
		set_label(single_w.water_temp, "");
	}
	if (dc->airtemp.mkelvin) {
		value = get_temp_units(dc->airtemp.mkelvin, &unit);
		set_label(single_w.air_temp, "%.1f %s", value, unit);
	} else {
		if (dive->airtemp.mkelvin) {
			value = get_temp_units(dive->airtemp.mkelvin, &unit);
			set_label(single_w.air_temp, "%.1f %s", value, unit);
		} else {
				set_label(single_w.air_temp, "");
		}
	}
	mbar = dc->surface_pressure.mbar;
	/* it would be easy to get dive data here:
	 *	if (!mbar)
	 *		mbar = get_surface_pressure_in_mbar(dive, FALSE);
	 */
	if (mbar) {
		set_label(single_w.air_press, "%d mbar", mbar);
	} else {
		set_label(single_w.air_press, "");
	}
	value = get_volume_units(dive->sac, &decimals, &unit);
	if (value > 0)
		set_label(single_w.sac, _("%.*f %s/min"), decimals, value, unit);
	else
		set_label(single_w.sac, "");
	set_label(single_w.otu, "%d", dive->otu);
	offset = 0;
	gas_used = 0;
	buf[0] = '\0';
	/* for the O2/He readings just create a list of them */
	for (idx = 0; idx < MAX_CYLINDERS; idx++) {
		cylinder_t *cyl = &dive->cylinder[idx];
		pressure_t start, end;

		start = cyl->start.mbar ? cyl->start : cyl->sample_start;
		end = cyl->end.mbar ?cyl->sample_end : cyl->sample_end;
		if (!cylinder_none(cyl)) {
			/* 0% O2 strangely means air, so 21% - I don't like that at all */
			int o2 = get_o2(&cyl->gasmix);
			int he = get_he(&cyl->gasmix);
			if (offset > 0) {
				snprintf(buf+offset, 80-offset, ", ");
				offset += 2;
			}
			snprintf(buf+offset, 80-offset, "%d/%d", (o2 + 5) / 10, (he + 5) / 10);
			offset = strlen(buf);
		}
		/* and if we have size, start and end pressure, we can
		 * calculate the total gas used */
		if (start.mbar && end.mbar)
			gas_used += gas_volume(cyl, start) - gas_volume(cyl, end);
	}
	set_label(single_w.o2he, buf);
	if (gas_used) {
		value = get_volume_units(gas_used, &decimals, &unit);
		set_label(single_w.gas_used, "%.*f %s", decimals, value, unit);
	} else {
		set_label(single_w.gas_used, "");
	}
}
Exemplo n.º 16
0
static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer)
{
	char buffer[20000], temp[1000];
	int len, lastdepth = 0, lasttime = 0;
	struct divedatapoint *dp = diveplan->dp;
	bool gaschange = !plan_verbatim;
	struct divedatapoint *nextdp = NULL;

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

	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_runtime)
			len += snprintf(buffer + len, sizeof(buffer) - len, "<th style='padding-left: 10px;'>%s</th>",
					translate("gettextFromC", "runtime"));
		if (plan_display_duration)
			len += snprintf(buffer + len, sizeof(buffer) - len, "<th style='padding-left: 10px;'>%s</th>",
					translate("gettextFromC", "duration"));
		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;

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

		len = strlen(buffer);
		if (nextdp && gasmix_distance(&gasmix, &newgasmix))
			gaschange = true;
		if (plan_verbatim) {
			if (dp->depth != lastdepth) {
				if (plan_display_transitions || dp->entered || !dp->next || (gaschange && dp->next && dp->depth != nextdp->depth)) {
					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);
					lasttime = dp->time;
				}
			} else {
				if (dp->depth != nextdp->depth) {
					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);
					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_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 (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 (gaschange) {
					len += snprintf(buffer + len, sizeof(buffer) - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&newgasmix));
					gaschange = false;
				} else {
					len += snprintf(buffer + len, sizeof(buffer) - len, "<td>&nbsp;</td>");
				}
				len += snprintf(buffer + len, sizeof(buffer) - len, "</tr>");
				lasttime = dp->time;
			}
		}
		if (gaschange) {
			// gas switch at this waypoint
			if (plan_verbatim) {
				snprintf(temp, sizeof(temp), translate("gettextFromC", "Switch gas to %s"), gasname(&newgasmix));
				len += snprintf(buffer + len, sizeof(buffer) - len, "%s<br>", temp);
				gaschange = false;
			}
			gasmix = newgasmix;
		}
		lastdepth = dp->depth;
	} 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);

	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;
	while (dp) {
		if (dp->time != 0) {
			int pO2 = depth_to_atm(dp->depth, dive) * get_o2(&dp->gasmix);

			if (pO2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2)) {
				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"),
					 pO2 / 1000.0, 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);
}
Exemplo n.º 17
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);
}