summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2014-05-29 14:36:14 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2014-05-29 14:43:24 -0700
commitd054e8c457c2113b39a7d8fe21215c99a86d1d9d (patch)
treee59c1a2a1948078e8970d157a619eaf3381cffc5
parent56395b38946d4b7de9e485098e928e8e432ef920 (diff)
downloadsubsurface-d054e8c457c2113b39a7d8fe21215c99a86d1d9d.tar.gz
Planner: track gas consumption in cylinders and samples
This commit is a little bigger than I usually prefer, but it's all somewhat interconnected. - we pass around the cylinders throughout the planning process and as we create the plan we calculate the gas consumption in every segment and track this both in the end pressure of the cylinder and over time in the samples - because of that we no longer try to calculate the gas consumption after being done planning; we just use what we calculated along the way - we also no longer add gases during the planning process - all gases have to come from the list of cylinders passed in (which makes sense as we should only use those gases that the user added in the UI or inherited from a the selected dive (when starting to plan with a dive already selected) With this patch I think we are close do being able to move all of the planning logic back into the planner.c code where it belongs. The one issue that still bothers me is that we are juggling so many dive structures and then keep copying content around. It seems like we should be able to reduce that. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--dive.h3
-rw-r--r--equipment.c9
-rw-r--r--planner.c82
-rw-r--r--qt-ui/diveplanner.cpp60
4 files changed, 85 insertions, 69 deletions
diff --git a/dive.h b/dive.h
index 03acea496..277573f5b 100644
--- a/dive.h
+++ b/dive.h
@@ -655,7 +655,7 @@ struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he, int po
#if DEBUG_PLAN
void dump_plan(struct diveplan *diveplan);
#endif
-void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, bool add_deco);
+void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, struct dive *master_dive, bool add_deco);
void delete_single_dive(int idx);
struct event *get_next_event(struct event *event, char *name);
@@ -687,6 +687,7 @@ extern bool no_weightsystems(weightsystem_t *ws);
extern bool weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2);
extern void remove_cylinder(struct dive *dive, int idx);
extern void remove_weightsystem(struct dive *dive, int idx);
+extern void reset_cylinders(struct dive *dive);
/*
* String handling.
diff --git a/equipment.c b/equipment.c
index 548f15b5e..fd0d6ee0c 100644
--- a/equipment.c
+++ b/equipment.c
@@ -181,3 +181,12 @@ void remove_weightsystem(struct dive *dive, int idx)
memmove(ws, ws + 1, nr * sizeof(*ws));
memset(ws + nr, 0, sizeof(*ws));
}
+
+void reset_cylinders(struct dive *dive)
+{
+ int i;
+ for (i = 0; i < MAX_CYLINDERS; i++) {
+ if (dive->cylinder[i].type.workingpressure.mbar)
+ dive->cylinder[i].start.mbar = dive->cylinder[i].end.mbar = dive->cylinder[i].type.workingpressure.mbar;
+ }
+}
diff --git a/planner.c b/planner.c
index 7bdb60e62..8c40f90d4 100644
--- a/planner.c
+++ b/planner.c
@@ -197,23 +197,33 @@ static int add_gas(struct dive *dive, int o2, int he)
if (gasmix_distance(mix, &mix_in) < 200)
return i;
}
- if (i == MAX_CYLINDERS) {
- return -1;
- }
- /* let's make it our default cylinder */
- fill_default_cylinder(cyl);
- mix->o2.permille = o2;
- mix->he.permille = he;
- sanitize_gasmix(mix);
- return i;
+ fprintf(stderr, "this gas (%d/%d) should have been on the cylinder list\nThings will fail now\n", o2, he);
+ return -1;
+}
+
+/* calculate the new end pressure of the cylinder, based on its current end pressure and the
+ * latest segment. */
+static void update_cylinder_pressure(struct dive *d, int old_depth, int new_depth, int duration, int sac, cylinder_t *cyl)
+{
+ volume_t gas_used;
+ pressure_t delta_p;
+ depth_t mean_depth;
+
+ if (!cyl || !cyl->type.size.mliter)
+ return;
+ mean_depth.mm = (old_depth + new_depth) / 2;
+ gas_used.mliter = depth_to_atm(mean_depth.mm, d) * sac / 60 * duration;
+ delta_p.mbar = gas_used.mliter * 1000.0 / cyl->type.size.mliter;
+ cyl->end.mbar -= delta_p.mbar;
}
-struct dive *create_dive_from_plan(struct diveplan *diveplan)
+static struct dive *create_dive_from_plan(struct diveplan *diveplan, struct dive *master_dive)
{
struct dive *dive;
struct divedatapoint *dp;
struct divecomputer *dc;
struct sample *sample;
+ cylinder_t *cyl;
int oldo2 = O2_IN_AIR, oldhe = 0;
int oldpo2 = 0;
int lasttime = 0;
@@ -230,12 +240,18 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan)
dc = &dive->dc;
dc->model = "planned dive"; /* do not translate here ! */
dp = diveplan->dp;
+ copy_cylinders(master_dive, dive);
+
+ /* reset the end pressure values and start with the first cylinder */
+ reset_cylinders(master_dive);
+ cyl = &master_dive->cylinder[0];
/* let's start with the gas given on the first segment */
if (dp->o2 || dp->he) {
oldo2 = dp->o2;
oldhe = dp->he;
}
+
sample = prepare_sample(dc);
sample->po2 = dp->po2;
finish_sample(dc);
@@ -280,6 +296,16 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan)
if ((idx = add_gas(dive, plano2, planhe)) < 0)
goto gas_error_exit;
add_gas_switch_event(dive, dc, lasttime, idx);
+ /* need to insert a last sample for the old gas */
+ sample = prepare_sample(dc);
+ sample[-1].po2 = po2;
+ sample->time.seconds = time - 1;
+ sample->depth.mm = depth;
+ update_cylinder_pressure(dive, sample[-1].depth.mm, depth, time - sample[-1].time.seconds,
+ dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl);
+ sample->cylinderpressure.mbar = cyl->end.mbar;
+ finish_sample(dc);
+ cyl = &dive->cylinder[idx];
oldo2 = o2;
oldhe = he;
}
@@ -291,6 +317,9 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan)
sample->po2 = po2;
sample->time.seconds = time;
sample->depth.mm = depth;
+ update_cylinder_pressure(dive, sample[-1].depth.mm, depth, time - sample[-1].time.seconds,
+ dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl);
+ sample->cylinderpressure.mbar = cyl->end.mbar;
finish_sample(dc);
lasttime = time;
dp = dp->next;
@@ -533,26 +562,18 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive)
get_gas_string(o2, he, gas, sizeof(gas));
gasidx = get_gasidx(dive, o2, he);
len = strlen(buffer);
- if (dp->depth != lastdepth) {
- used = diveplan->bottomsac / 1000.0 * depth_to_mbar((dp->depth + lastdepth) / 2, dive) *
- (dp->time - lasttime) / 60;
+ if (dp->depth != lastdepth)
snprintf(buffer + len, sizeof(buffer) - len, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s\n"),
decimals, depthvalue, depth_unit,
FRACTION(dp->time - lasttime, 60),
FRACTION(dp->time, 60),
gas);
- } else {
- /* we use deco SAC rate during the calculated deco stops, bottom SAC rate everywhere else */
- int sac = dp->entered ? diveplan->bottomsac : diveplan->decosac;
- used = sac / 1000.0 * depth_to_mbar(dp->depth, dive) * (dp->time - lasttime) / 60;
+ else
snprintf(buffer + len, sizeof(buffer) - len, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s\n"),
decimals, depthvalue, depth_unit,
FRACTION(dp->time - lasttime, 60),
FRACTION(dp->time, 60),
gas);
- }
- if (gasidx != -1)
- consumption[gasidx] += used;
get_gas_string(newo2, newhe, gas, sizeof(gas));
if (o2 != newo2 || he != newhe) {
len = strlen(buffer);
@@ -569,14 +590,13 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive)
double volume;
const char *unit;
char gas[64];
- if (dive->cylinder[gasidx].type.size.mliter)
- dive->cylinder[gasidx].end.mbar = dive->cylinder[gasidx].start.mbar - consumption[gasidx] / dive->cylinder[gasidx].type.size.mliter / 1000;
- if (consumption[gasidx] == 0)
- continue;
+ cylinder_t *cyl = &dive->cylinder[gasidx];
+ if (cylinder_none(cyl))
+ break;
+ int consumed = mbar_to_atm(cyl->start.mbar - cyl->end.mbar) * cyl->type.size.mliter;
len = strlen(buffer);
- volume = get_volume_units(consumption[gasidx], NULL, &unit);
- get_gas_string(get_o2(&dive->cylinder[gasidx].gasmix),
- get_he(&dive->cylinder[gasidx].gasmix), gas, sizeof(gas));
+ volume = get_volume_units(consumed, NULL, &unit);
+ get_gas_string(get_o2(&cyl->gasmix), get_he(&cyl->gasmix), gas, sizeof(gas));
snprintf(buffer + len, sizeof(buffer) - len, translate("gettextFromC", "%.0f%s of %s\n"), volume, unit, gas);
}
dive->notes = strdup(buffer);
@@ -598,7 +618,7 @@ int ascend_velocity(int depth, int avg_depth, int bottom_time)
return 6000 / 60;
}
-void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, bool add_deco)
+void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, struct dive *master_dive, bool add_deco)
{
struct dive *dive;
struct sample *sample;
@@ -623,7 +643,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b
diveplan->surface_pressure = SURFACE_PRESSURE;
if (*divep)
delete_single_dive(dive_table.nr - 1);
- *divep = dive = create_dive_from_plan(diveplan);
+ *divep = dive = create_dive_from_plan(diveplan, master_dive);
if (!dive)
return;
record_dive(dive);
@@ -649,7 +669,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b
plan_add_segment(diveplan, transitiontime, 0, o2, he, po2, false);
/* re-create the dive */
delete_single_dive(dive_table.nr - 1);
- *divep = dive = create_dive_from_plan(diveplan);
+ *divep = dive = create_dive_from_plan(diveplan, master_dive);
if (dive)
record_dive(dive);
return;
@@ -766,7 +786,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b
/* We made it to the surface */
plan_add_segment(diveplan, clock - previous_point_time, 0, o2, he, po2, false);
delete_single_dive(dive_table.nr - 1);
- *divep = dive = create_dive_from_plan(diveplan);
+ *divep = dive = create_dive_from_plan(diveplan, master_dive);
if (!dive)
goto error_exit;
add_plan_to_notes(diveplan, dive);
diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp
index 921fdf88b..8c3d45c63 100644
--- a/qt-ui/diveplanner.cpp
+++ b/qt-ui/diveplanner.cpp
@@ -116,22 +116,25 @@ void DivePlannerPointsModel::copyCylinders(dive *d)
// setup the cylinder widget accordingly
void DivePlannerPointsModel::setupCylinders()
{
- if (!stagingDive || stagingDive == current_dive)
+ if (!stagingDive)
return;
- if (current_dive) {
- copy_cylinders(current_dive, stagingDive);
- } else {
- if (!same_string(prefs.default_cylinder, "")) {
- fill_default_cylinder(&stagingDive->cylinder[0]);
+ if (stagingDive != current_dive) {
+ if (current_dive) {
+ copy_cylinders(current_dive, stagingDive);
} else {
- // roughly an AL80
- stagingDive->cylinder[0].type.description = strdup(tr("unknown").toUtf8().constData());
- stagingDive->cylinder[0].type.size.mliter = 11100;
- stagingDive->cylinder[0].type.workingpressure.mbar = 207000;
- stagingDive->cylinder[0].used = true;
+ if (!same_string(prefs.default_cylinder, "")) {
+ fill_default_cylinder(&stagingDive->cylinder[0]);
+ } else {
+ // roughly an AL80
+ stagingDive->cylinder[0].type.description = strdup(tr("unknown").toUtf8().constData());
+ stagingDive->cylinder[0].type.size.mliter = 11100;
+ stagingDive->cylinder[0].type.workingpressure.mbar = 207000;
+ stagingDive->cylinder[0].used = true;
+ }
}
}
+ reset_cylinders(stagingDive);
CylindersModel::instance()->copyFromDive(stagingDive);
}
@@ -804,12 +807,14 @@ void DivePlannerPointsModel::createTemporaryPlan()
#if DEBUG_PLAN
dump_plan(&diveplan);
#endif
- if (plannerModel->recalcQ())
- plan(&diveplan, &cache, &tempDive, isPlanner());
- if (mode == ADD || mode == PLAN) {
- // copy the samples and events, but don't overwrite the cylinders
- copy_samples(tempDive, current_dive);
- copy_events(tempDive, current_dive);
+ if (plannerModel->recalcQ()) {
+ plan(&diveplan, &cache, &tempDive, stagingDive, isPlanner());
+ if (mode == ADD || mode == PLAN) {
+ // copy the samples and events, but don't overwrite the cylinders
+ copy_samples(tempDive, current_dive);
+ copy_events(tempDive, current_dive);
+ copy_cylinders(tempDive, current_dive);
+ }
}
// throw away the cache
free(cache);
@@ -851,26 +856,7 @@ void DivePlannerPointsModel::createPlan()
plannerModel->setRecalc(oldRecalc);
//TODO: C-based function here?
- plan(&diveplan, &cache, &tempDive, isPlanner());
- copy_cylinders(stagingDive, tempDive);
- int mean[MAX_CYLINDERS], duration[MAX_CYLINDERS];
- per_cylinder_mean_depth(tempDive, select_dc(tempDive), mean, duration);
- for (int i = 0; i < MAX_CYLINDERS; i++) {
- cylinder_t *cyl = tempDive->cylinder + i;
- if (cylinder_none(cyl))
- continue;
- // FIXME: The epic assumption that all the cylinders after the first is deco
- int sac = i ? diveplan.decosac : diveplan.bottomsac;
- cyl->start.mbar = cyl->type.workingpressure.mbar;
- if (cyl->type.size.mliter) {
- int consumption = ((depth_to_mbar(mean[i], tempDive) * duration[i] / 60) * sac) / (cyl->type.size.mliter / 1000);
- cyl->end.mbar = cyl->start.mbar - consumption;
- } else {
- // Cylinders without a proper size are easily emptied.
- // Don't attempt to to calculate the infinite pressure drop.
- cyl->end.mbar = 0;
- }
- }
+ plan(&diveplan, &cache, &tempDive, stagingDive, isPlanner());
record_dive(tempDive);
mark_divelist_changed(true);