2013-01-05 07:11:42 +00:00
|
|
|
/* planner.c
|
|
|
|
*
|
|
|
|
* code that allows us to plan future dives
|
|
|
|
*
|
|
|
|
* (c) Dirk Hohndel 2013
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "dive.h"
|
|
|
|
#include "divelist.h"
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
int stoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 21000, 30000, 42000, 60000, 90000 };
|
2013-01-05 07:11:42 +00:00
|
|
|
|
|
|
|
/* returns the tissue tolerance at the end of this (partial) dive */
|
2013-01-06 19:13:46 +00:00
|
|
|
double tissue_at_end(struct dive *dive, char **cached_datap)
|
2013-01-05 07:11:42 +00:00
|
|
|
{
|
|
|
|
struct divecomputer *dc;
|
|
|
|
struct sample *sample, *psample;
|
|
|
|
int i, j, t0, t1;
|
|
|
|
double tissue_tolerance;
|
|
|
|
|
|
|
|
if (!dive)
|
|
|
|
return 0.0;
|
2013-01-06 19:13:46 +00:00
|
|
|
if (*cached_datap) {
|
|
|
|
tissue_tolerance = restore_deco_state(*cached_datap);
|
|
|
|
} else {
|
|
|
|
tissue_tolerance = init_decompression(dive);
|
|
|
|
cache_deco_state(tissue_tolerance, cached_datap);
|
|
|
|
}
|
2013-01-05 07:11:42 +00:00
|
|
|
dc = &dive->dc;
|
|
|
|
if (!dc->samples)
|
2013-01-06 19:13:46 +00:00
|
|
|
return tissue_tolerance;
|
2013-01-05 07:11:42 +00:00
|
|
|
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? */
|
2013-01-06 19:13:46 +00:00
|
|
|
int time_at_last_depth(struct dive *dive, int next_stop, char **cached_data_p)
|
2013-01-05 07:11:42 +00:00
|
|
|
{
|
|
|
|
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;
|
2013-01-06 19:13:46 +00:00
|
|
|
tissue_tolerance = tissue_at_end(dive, cached_data_p);
|
2013-01-05 07:11:42 +00:00
|
|
|
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;
|
2013-01-07 06:09:12 +00:00
|
|
|
while (dp && dp->time) {
|
2013-01-05 07:11:42 +00:00
|
|
|
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++;
|
|
|
|
}
|
2013-01-07 06:09:12 +00:00
|
|
|
if (dc->samples == 0) {
|
|
|
|
/* not enough there yet to create a dive - most likely the first time is missing */
|
|
|
|
free(dive);
|
|
|
|
dive = NULL;
|
|
|
|
}
|
2013-01-05 07:11:42 +00:00
|
|
|
return dive;
|
|
|
|
}
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
void free_dps(struct divedatapoint *dp)
|
|
|
|
{
|
|
|
|
while (dp) {
|
|
|
|
struct divedatapoint *ndp = dp->next;
|
|
|
|
free(dp);
|
|
|
|
dp = ndp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-05 07:11:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-01-07 16:13:23 +00:00
|
|
|
void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel)
|
2013-01-07 06:09:12 +00:00
|
|
|
{
|
2013-01-07 16:13:23 +00:00
|
|
|
struct divedatapoint *pdp, *dp = get_nth_dp(diveplan, idx);
|
|
|
|
if (idx > 0 && is_rel) {
|
|
|
|
pdp = get_nth_dp(diveplan, idx - 1);
|
|
|
|
duration += pdp->time;
|
|
|
|
}
|
2013-01-07 06:09:12 +00:00
|
|
|
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;
|
|
|
|
}
|
2013-01-05 07:11:42 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep)
|
2013-01-05 07:11:42 +00:00
|
|
|
{
|
|
|
|
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;
|
2013-01-07 06:09:12 +00:00
|
|
|
if (*divep)
|
|
|
|
delete_single_dive(dive_table.nr - 1);
|
|
|
|
*divep = dive = create_dive_from_plan(diveplan);
|
2013-01-05 20:56:45 +00:00
|
|
|
if (!dive)
|
|
|
|
return;
|
2013-01-05 07:11:42 +00:00
|
|
|
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;
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
tissue_tolerance = tissue_at_end(dive, cached_datap);
|
2013-01-05 07:11:42 +00:00
|
|
|
ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1);
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
for (stopidx = 1; stopidx < sizeof(stoplevels) / sizeof(int); stopidx++)
|
2013-01-05 07:11:42 +00:00
|
|
|
if (stoplevels[stopidx] >= ceiling)
|
|
|
|
break;
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
while (stopidx > 0) {
|
2013-01-05 07:11:42 +00:00
|
|
|
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);
|
2013-01-07 06:09:12 +00:00
|
|
|
*divep = dive = create_dive_from_plan(diveplan);
|
2013-01-05 07:11:42 +00:00
|
|
|
record_dive(dive);
|
|
|
|
}
|
2013-01-07 06:09:12 +00:00
|
|
|
wait_time = time_at_last_depth(dive, stoplevels[stopidx - 1], cached_datap);
|
2013-01-05 07:11:42 +00:00
|
|
|
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);
|
2013-01-07 06:09:12 +00:00
|
|
|
*divep = dive = create_dive_from_plan(diveplan);
|
2013-01-05 07:11:42 +00:00
|
|
|
record_dive(dive);
|
|
|
|
stopidx--;
|
|
|
|
}
|
|
|
|
/* now make the dive visible as last dive of the dive list */
|
|
|
|
report_dives(FALSE, FALSE);
|
2013-01-07 06:09:12 +00:00
|
|
|
select_last_dive();
|
2013-01-05 07:11:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void test_planner()
|
|
|
|
{
|
2013-01-07 06:09:12 +00:00
|
|
|
struct dive *dive = NULL;
|
2013-01-05 07:11:42 +00:00
|
|
|
struct diveplan diveplan = {};
|
2013-01-07 06:09:12 +00:00
|
|
|
char *cache_data;
|
2013-01-05 07:11:42 +00:00
|
|
|
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);
|
|
|
|
|
2013-01-07 06:09:12 +00:00
|
|
|
plan(&diveplan, &cache_data, &dive);
|
|
|
|
/* we are not rerunning the plan, so free the cache right away */
|
|
|
|
free(cache_data);
|
2013-01-05 07:11:42 +00:00
|
|
|
}
|