subsurface/planner.c
Linus Torvalds 92a8fde9c2 Do a minimal hook-up of the dive plan tree view to the
actual planning

Yes, you can actually enter your segments now.

No, it's not wonderfully user-friendly.  If you don't enter enough
segments to create a dive plan, it will just silently fail, for example.
And the <tab> key that should get you to the next editable segment
doesn't.  And so on.  But it kind of works.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2013-01-05 15:51:57 -08:00

231 lines
6.1 KiB
C

/* planner.c
*
* code that allows us to plan future dives
*
* (c) Dirk Hohndel 2013
*/
#include "dive.h"
#include "divelist.h"
int stoplevels[] = { 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)
{
struct divecomputer *dc;
struct sample *sample, *psample;
int i, j, t0, t1;
double tissue_tolerance;
if (!dive)
return 0.0;
tissue_tolerance = init_decompression(dive);
dc = &dive->dc;
if (!dc->samples)
return 0.0;
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)
{
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);
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) {
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++;
}
return dive;
}
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;
}
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)
{
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;
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);
ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1);
for (stopidx = 0; 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);
dive = create_dive_from_plan(diveplan);
record_dive(dive);
}
wait_time = time_at_last_depth(dive, stoplevels[stopidx - 1]);
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);
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);
}
void test_planner()
{
struct diveplan diveplan = {};
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);
}