diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2014-05-29 14:36:14 -0700 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2014-05-29 14:43:24 -0700 |
commit | d054e8c457c2113b39a7d8fe21215c99a86d1d9d (patch) | |
tree | e59c1a2a1948078e8970d157a619eaf3381cffc5 | |
parent | 56395b38946d4b7de9e485098e928e8e432ef920 (diff) | |
download | subsurface-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.h | 3 | ||||
-rw-r--r-- | equipment.c | 9 | ||||
-rw-r--r-- | planner.c | 82 | ||||
-rw-r--r-- | qt-ui/diveplanner.cpp | 60 |
4 files changed, 85 insertions, 69 deletions
@@ -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; + } +} @@ -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); |