subsurface/planner.c
Dirk Hohndel be9be189f7 Display dive profile of the dive we plan, as we plan it
As the user enters data into the entry fields, that data is validated and
as soon as there is enough data we start constructing a dive profile,
including the final ascent to the surface, including required deco stops,
etc.

This commit still has some serious issues.

- when data is input that doesn't validate, we just print a warning to
  stdout - instead we need to change the backgroundcolor of the input
  field or something.
- when we switch to the last dive in order to show the profile we don't
  actually search for the last dive - we just show the first one in the
  tree. This works for the default sort order but is of course wrong
  otherwise

I'm sure there are many other bugs, but I want to push it out where it is
right now for others to be able to take a look.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2013-01-06 22:15:03 -08:00

290 lines
7.7 KiB
C

/* planner.c
*
* code that allows us to plan future dives
*
* (c) Dirk Hohndel 2013
*/
#include "dive.h"
#include "divelist.h"
int stoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 21000, 30000, 42000, 60000, 90000 };
/* returns the tissue tolerance at the end of this (partial) dive */
double tissue_at_end(struct dive *dive, char **cached_datap)
{
struct divecomputer *dc;
struct sample *sample, *psample;
int i, j, t0, t1;
double tissue_tolerance;
if (!dive)
return 0.0;
if (*cached_datap) {
tissue_tolerance = restore_deco_state(*cached_datap);
} else {
tissue_tolerance = init_decompression(dive);
cache_deco_state(tissue_tolerance, cached_datap);
}
dc = &dive->dc;
if (!dc->samples)
return tissue_tolerance;
psample = sample = dc->sample;
t0 = 0;
for (i = 0; i < dc->samples; i++, sample++) {
t1 = sample->time.seconds;
for (j = t0; j < t1; j++) {
int depth = psample->depth.mm + (j - t0) * (sample->depth.mm - psample->depth.mm) / (t1 - t0);
tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0,
&dive->cylinder[sample->sensor].gasmix, 1, sample->po2);
}
psample = sample;
t0 = t1;
}
return tissue_tolerance;
}
/* how many seconds until we can ascend to the next stop? */
int time_at_last_depth(struct dive *dive, int next_stop, char **cached_data_p)
{
int depth;
double surface_pressure, tissue_tolerance;
int wait = 0;
struct sample *sample;
if (!dive)
return 0;
surface_pressure = dive->surface_pressure.mbar / 1000.0;
tissue_tolerance = tissue_at_end(dive, cached_data_p);
sample = &dive->dc.sample[dive->dc.samples - 1];
depth = sample->depth.mm;
while (deco_allowed_depth(tissue_tolerance, surface_pressure, dive, 1) > next_stop) {
wait++;
tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0,
&dive->cylinder[sample->sensor].gasmix, 1, sample->po2);
}
return wait;
}
int add_gas(struct dive *dive, int o2, int he)
{
int i;
struct gasmix *mix;
cylinder_t *cyl;
for (i = 0; i < MAX_CYLINDERS; i++) {
cyl = dive->cylinder + i;
if (cylinder_nodata(cyl))
break;
mix = &cyl->gasmix;
if (o2 == mix->o2.permille && he == mix->he.permille)
return i;
}
if (i == MAX_CYLINDERS) {
printf("too many cylinders\n");
return -1;
}
mix = &cyl->gasmix;
mix->o2.permille = o2;
mix->he.permille = he;
return i;
}
struct dive *create_dive_from_plan(struct diveplan *diveplan)
{
struct dive *dive;
struct divedatapoint *dp;
struct divecomputer *dc;
struct sample *sample;
int gasused = 0;
int t = 0;
int lastdepth = 0;
if (!diveplan || !diveplan->dp)
return NULL;
dive = alloc_dive();
dive->when = diveplan->when;
dive->surface_pressure.mbar = diveplan->surface_pressure;
dc = &dive->dc;
dc->model = "Simulated Dive";
dp = diveplan->dp;
while (dp && dp->time) {
int i, depth;
if (dp->o2 != dive->cylinder[gasused].gasmix.o2.permille ||
dp->he != dive->cylinder[gasused].gasmix.he.permille)
gasused = add_gas(dive, dp->o2, dp->he);
for (i = t; i < dp->time; i += 10) {
depth = lastdepth + (i - t) * (dp->depth - lastdepth) / (dp->time - t);
sample = prepare_sample(dc);
sample->time.seconds = i;
sample->depth.mm = depth;
sample->sensor = gasused;
dc->samples++;
}
sample = prepare_sample(dc);
sample->time.seconds = dp->time;
sample->depth.mm = dp->depth;
sample->sensor = gasused;
lastdepth = dp->depth;
t = dp->time;
dp = dp->next;
dc->samples++;
}
if (dc->samples == 0) {
/* not enough there yet to create a dive - most likely the first time is missing */
free(dive);
dive = NULL;
}
return dive;
}
void free_dps(struct divedatapoint *dp)
{
while (dp) {
struct divedatapoint *ndp = dp->next;
free(dp);
dp = ndp;
}
}
struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he)
{
struct divedatapoint *dp;
dp = malloc(sizeof(struct divedatapoint));
dp->time = time_incr;
dp->depth = depth;
dp->o2 = o2;
dp->he = he;
dp->next = NULL;
return dp;
}
struct divedatapoint *get_nth_dp(struct diveplan *diveplan, int idx)
{
struct divedatapoint **ldpp, *dp = diveplan->dp;
int i = 0;
ldpp = &diveplan->dp;
while (dp && i++ < idx) {
ldpp = &dp->next;
dp = dp->next;
}
while (i++ <= idx) {
*ldpp = dp = create_dp(0, 0, 0, 0);
ldpp = &((*ldpp)->next);
}
return dp;
}
void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration)
{
struct divedatapoint *dp = get_nth_dp(diveplan, idx);
dp->time = duration;
}
void add_depth_to_nth_dp(struct diveplan *diveplan, int idx, int depth)
{
struct divedatapoint *dp = get_nth_dp(diveplan, idx);
dp->depth = depth;
}
void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he)
{
struct divedatapoint *dp = get_nth_dp(diveplan, idx);
dp->o2 = o2;
dp->he = he;
}
void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp)
{
struct divedatapoint **lastdp = &diveplan->dp;
struct divedatapoint *ldp = *lastdp;
while(*lastdp) {
ldp = *lastdp;
lastdp = &(*lastdp)->next;
}
*lastdp = dp;
if (ldp)
dp->time += ldp->time;
}
void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he)
{
struct divedatapoint *dp = create_dp(duration, depth, o2, he);
add_to_end_of_diveplan(diveplan, dp);
}
void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep)
{
struct dive *dive;
struct sample *sample;
int wait_time, o2, he;
int ceiling, depth, transitiontime;
int stopidx;
double tissue_tolerance;
if (!diveplan->surface_pressure)
diveplan->surface_pressure = 1013;
if (*divep)
delete_single_dive(dive_table.nr - 1);
*divep = dive = create_dive_from_plan(diveplan);
if (!dive)
return;
record_dive(dive);
sample = &dive->dc.sample[dive->dc.samples - 1];
o2 = dive->cylinder[sample->sensor].gasmix.o2.permille;
he = dive->cylinder[sample->sensor].gasmix.he.permille;
tissue_tolerance = tissue_at_end(dive, cached_datap);
ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1);
for (stopidx = 1; stopidx < sizeof(stoplevels) / sizeof(int); stopidx++)
if (stoplevels[stopidx] >= ceiling)
break;
while (stopidx > 0) {
depth = dive->dc.sample[dive->dc.samples - 1].depth.mm;
if (depth > stoplevels[stopidx]) {
transitiontime = (depth - stoplevels[stopidx]) / 150;
plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he);
/* re-create the dive */
delete_single_dive(dive_table.nr - 1);
*divep = dive = create_dive_from_plan(diveplan);
record_dive(dive);
}
wait_time = time_at_last_depth(dive, stoplevels[stopidx - 1], cached_datap);
if (wait_time)
plan_add_segment(diveplan, wait_time, stoplevels[stopidx], o2, he);
transitiontime = (stoplevels[stopidx] - stoplevels[stopidx - 1]) / 150;
plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he);
/* re-create the dive */
delete_single_dive(dive_table.nr - 1);
*divep = dive = create_dive_from_plan(diveplan);
record_dive(dive);
stopidx--;
}
/* now make the dive visible as last dive of the dive list */
report_dives(FALSE, FALSE);
select_last_dive();
}
void test_planner()
{
struct dive *dive = NULL;
struct diveplan diveplan = {};
char *cache_data;
int end_of_last_dive = dive_table.dives[dive_table.nr - 1]->when + dive_table.dives[dive_table.nr -1]->duration.seconds;
diveplan.when = end_of_last_dive + 50 * 3600; /* don't take previous dives into account for deco calculation */
diveplan.surface_pressure = 1013;
plan_add_segment(&diveplan, 120, 36000, 209, 0);
plan_add_segment(&diveplan, 1800, 36000, 209, 0);
plan_add_segment(&diveplan, 59, 27000, 209, 0);
plan_add_segment(&diveplan, 1, 27000, 400, 0);
plan(&diveplan, &cache_data, &dive);
/* we are not rerunning the plan, so free the cache right away */
free(cache_data);
}