summaryrefslogtreecommitdiffstats
path: root/planner.c
diff options
context:
space:
mode:
Diffstat (limited to 'planner.c')
-rw-r--r--planner.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/planner.c b/planner.c
new file mode 100644
index 000000000..9d407a7c4
--- /dev/null
+++ b/planner.c
@@ -0,0 +1,242 @@
+/* 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 };
+
+struct divedatapoint {
+ int time;
+ int depth;
+ int o2;
+ int he;
+ struct divedatapoint *next;
+};
+
+struct diveplan {
+ timestamp_t when;
+ int surface_pressure;
+ struct divedatapoint *dp;
+};
+
+/* 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);
+ 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);
+}