diff options
Diffstat (limited to 'src/schedule.c')
-rw-r--r-- | src/schedule.c | 144 |
1 files changed, 59 insertions, 85 deletions
diff --git a/src/schedule.c b/src/schedule.c index 5528ce3..1827ff3 100644 --- a/src/schedule.c +++ b/src/schedule.c @@ -10,6 +10,12 @@ int SWITCH_INTERMEDIATE = SWITCH_INTERMEDIATE_DEFAULT; +static void emit_waypoint(const decostate_t *ds, segtype_t type, const waypoint_callback_t *wp_cb) +{ + if (wp_cb && wp_cb->fn) + wp_cb->fn(ds, type, wp_cb->arg); +} + const gas_t *best_gas(double depth, const gas_t *gasses, int nof_gasses) { const gas_t *best = NULL; @@ -27,38 +33,33 @@ const gas_t *best_gas(double depth, const gas_t *gasses, int nof_gasses) return best; } -int direct_ascent(const decostate_t *ds, double depth, double time, const gas_t *gas) +int direct_ascent(const decostate_t *ds, double ascrate) { decostate_t ds_ = *ds; assert(ds_.firststop == -1); - add_segment_ascdec(&ds_, depth, abs_depth(0), time, gas); + add_segment_ascdec(&ds_, ds_.depth, abs_depth(0), gauge_depth(ds_.depth) / ascrate, ds_.gas); return gauge_depth(ceiling(&ds_, ds_.gfhi)) <= 0; } void simulate_dive(decostate_t *ds, const waypoint_t *waypoints, int nof_waypoints, const waypoint_callback_t *wp_cb) { - double depth = abs_depth(0); - for (int i = 0; i < nof_waypoints; i++) { double d = waypoints[i].depth; double t = waypoints[i].time; const gas_t *g = waypoints[i].gas; - if (d != depth) - add_segment_ascdec(ds, depth, d, t, g); + if (d != ds->depth) + add_segment_ascdec(ds, ds->depth, d, t, g); else add_segment_const(ds, d, t, g); - depth = d; - - if (wp_cb && wp_cb->fn) - wp_cb->fn(ds, (waypoint_t){.depth = d, .time = t, .gas = g}, SEG_DIVE, wp_cb->arg); + emit_waypoint(ds, SEG_DIVE, wp_cb); } } -double calc_ndl(decostate_t *ds, double depth, double ascrate, const gas_t *gas) +double calc_ndl(decostate_t *ds, double ascrate) { double ndl = 0; @@ -66,33 +67,33 @@ double calc_ndl(decostate_t *ds, double depth, double ascrate, const gas_t *gas) decostate_t ds_ = *ds; while (ndl < 360) { - double tmp = add_segment_const(&ds_, depth, STOPLEN_ROUGH, gas); + add_segment_const(&ds_, ds_.depth, STOPLEN_ROUGH, ds_.gas); - if (!direct_ascent(&ds_, depth, gauge_depth(depth) / ascrate, gas)) + if (!direct_ascent(&ds_, ascrate)) break; - ndl += tmp; + ndl = ds_.runtime - ds->runtime; } /* fine steps */ ds_ = *ds; if (ndl) - add_segment_const(&ds_, depth, ndl, gas); + add_segment_const(&ds_, ds_.depth, ndl, ds_.gas); while (ndl < 360) { - double tmp = add_segment_const(&ds_, depth, STOPLEN_FINE, gas); + add_segment_const(&ds_, ds_.depth, STOPLEN_FINE, ds_.gas); - if (!direct_ascent(&ds_, depth, gauge_depth(depth) / ascrate, gas)) + if (!direct_ascent(&ds_, ascrate)) break; - ndl += tmp; + ndl = ds_.runtime - ds->runtime; } return ndl; } -double deco_stop(decostate_t *ds, double depth, double next_stop, double current_gf, const gas_t *gas) +static void deco_stop(decostate_t *ds, double next_stop, double current_gf) { double stoplen = 0; @@ -100,86 +101,68 @@ double deco_stop(decostate_t *ds, double depth, double next_stop, double current decostate_t ds_ = *ds; for (;;) { - double tmp = add_segment_const(&ds_, depth, STOPLEN_ROUGH, gas); + add_segment_const(&ds_, ds_.depth, STOPLEN_ROUGH, ds_.gas); if (ceiling(&ds_, current_gf) <= next_stop) break; - stoplen += tmp; + stoplen = ds_.runtime - ds->runtime; } if (stoplen) - add_segment_const(ds, depth, stoplen, gas); + add_segment_const(ds, ds->depth, stoplen, ds->gas); /* fine steps */ while (ceiling(ds, current_gf) > next_stop) - stoplen += add_segment_const(ds, depth, STOPLEN_FINE, gas); - - return stoplen; + add_segment_const(ds, ds->depth, STOPLEN_FINE, ds->gas); } -static int surfaced(double depth) +static int surfaced(decostate_t *ds) { - return fabs(depth - SURFACE_PRESSURE) < 1E-2; + return ds->depth - SURFACE_PRESSURE < 1E-2; } -decoinfo_t calc_deco(decostate_t *ds, double start_depth, const gas_t *start_gas, const gas_t *deco_gasses, - int nof_gasses, const waypoint_callback_t *wp_cb) +decoinfo_t calc_deco(decostate_t *ds, const gas_t *deco_gasses, int nof_gasses, const waypoint_callback_t *wp_cb) { - decoinfo_t ret = {.tts = 0, .ndl = 0}; - - /* setup start parameters */ - double depth = start_depth; - const gas_t *gas = start_gas; - + const double runtime_start = ds->runtime; const double asc_per_min = msw_to_bar(9); + double next_stop; + double current_gf; + const gas_t *best; + /* check if direct ascent is possible */ - if (direct_ascent(ds, depth, gauge_depth(depth) / asc_per_min, gas)) { - ret.ndl = calc_ndl(ds, depth, asc_per_min, gas); - return ret; - } + if (direct_ascent(ds, asc_per_min)) + return (decoinfo_t){.tts = 0, .ndl = calc_ndl(ds, asc_per_min)}; - double next_stop = abs_depth(ds->ceil_multiple * (ceil(gauge_depth(depth) / ds->ceil_multiple) - 1)); + /* prepare for the first stop */ + next_stop = abs_depth(ds->ceil_multiple * (ceil(gauge_depth(ds->depth) / ds->ceil_multiple) - 1)); - if (next_stop == depth) + if (next_stop == ds->depth) next_stop -= ds->ceil_multiple; - double current_gf = get_gf(ds, next_stop); - - /* extra bookkeeping because waypoints and segments do not match 1:1 */ - double last_waypoint_depth = depth; - double waypoint_time; + current_gf = get_gf(ds, next_stop); + /* alternate between ascending and stopping until we surface */ for (;;) { - while (ceiling(ds, current_gf) < next_stop && !surfaced(depth)) { + /* ascend */ + while (ceiling(ds, current_gf) < next_stop && !surfaced(ds)) { /* switch to better gas if available */ - const gas_t *best = best_gas(depth, deco_gasses, nof_gasses); + best = best_gas(ds->depth, deco_gasses, nof_gasses); - if (SWITCH_INTERMEDIATE && best && best != gas) { - /* emit waypoint */ - waypoint_time = fabs(last_waypoint_depth - depth) / asc_per_min; - - if (wp_cb && wp_cb->fn) - wp_cb->fn(ds, (waypoint_t){.depth = depth, .time = waypoint_time, .gas = gas}, SEG_TRAVEL, - wp_cb->arg); - - last_waypoint_depth = depth; + if (SWITCH_INTERMEDIATE && best && best != ds->gas) { + /* emit waypoint because we're about to switch gas */ + emit_waypoint(ds, SEG_TRAVEL, wp_cb); /* switch gas */ - gas = best; - - ret.tts += add_segment_const(ds, depth, 1, gas); - - if (wp_cb && wp_cb->fn) - wp_cb->fn(ds, (waypoint_t){.depth = depth, .time = 1, .gas = gas}, SEG_GAS_SWITCH, wp_cb->arg); + add_segment_const(ds, ds->depth, 1, best); + emit_waypoint(ds, SEG_GAS_SWITCH, wp_cb); continue; } /* ascend to next stop */ - ret.tts += add_segment_ascdec(ds, depth, next_stop, fabs(depth - next_stop) / asc_per_min, gas); - depth = next_stop; + add_segment_ascdec(ds, ds->depth, next_stop, fabs(ds->depth - next_stop) / asc_per_min, ds->gas); /* make next stop shallower */ next_stop -= ds->ceil_multiple; @@ -192,7 +175,7 @@ decoinfo_t calc_deco(decostate_t *ds, double start_depth, const gas_t *start_gas } if (ds->firststop == -1) { - ds->firststop = depth; + ds->firststop = ds->depth; /* * if the first stop wasn't know yet during the previous call to @@ -205,31 +188,22 @@ decoinfo_t calc_deco(decostate_t *ds, double start_depth, const gas_t *start_gas continue; } - /* emit waypoint */ - waypoint_time = fabs(last_waypoint_depth - depth) / asc_per_min; - enum segtype_t segtype = surfaced(depth) ? SEG_SURFACE : SEG_TRAVEL; - - if (wp_cb && wp_cb->fn && waypoint_time) - wp_cb->fn(ds, (waypoint_t){.depth = depth, .time = waypoint_time, .gas = gas}, segtype, wp_cb->arg); - - last_waypoint_depth = depth; - /* terminate if we surfaced */ - if (surfaced(depth)) - return ret; + if (surfaced(ds)) { + emit_waypoint(ds, SEG_SURFACE, wp_cb); + return (decoinfo_t){.ndl = 0, .tts = ds->runtime - runtime_start}; + } + + emit_waypoint(ds, SEG_TRAVEL, wp_cb); /* switch to better gas if available */ - const gas_t *best = best_gas(depth, deco_gasses, nof_gasses); + best = best_gas(ds->depth, deco_gasses, nof_gasses); if (best) - gas = best; + ds->gas = best; /* stop until ceiling rises above next stop */ - double stoplen = deco_stop(ds, depth, next_stop, current_gf, gas); - - ret.tts += stoplen; - - if (wp_cb && wp_cb->fn) - wp_cb->fn(ds, (waypoint_t){.depth = depth, .time = stoplen, .gas = gas}, SEG_DECO_STOP, wp_cb->arg); + deco_stop(ds, next_stop, current_gf); + emit_waypoint(ds, SEG_DECO_STOP, wp_cb); } } |