summaryrefslogtreecommitdiffstats
path: root/core/planner.c
diff options
context:
space:
mode:
authorGravatar Berthold Stoeger <bstoeger@mail.tuwien.ac.at>2019-08-04 18:44:57 +0200
committerGravatar Dirk Hohndel <dirk@hohndel.org>2019-11-09 19:19:04 +0100
commit7c9f46acd202121e67557bb634961ef17a9f6c1f (patch)
treef21b6ff42a930cc97c31db9f20c03f1613e70291 /core/planner.c
parentcd4f66014f7b5269dff9e088c9d7d38241c03156 (diff)
downloadsubsurface-7c9f46acd202121e67557bb634961ef17a9f6c1f.tar.gz
Core: remove MAX_CYLINDERS restriction
Instead of using fixed size arrays, use a new cylinder_table structure. The code copies the weightsystem code, but is significantly more complex because cylinders are such an integral part of the core. Two functions to access the cylinders were added: get_cylinder() and get_or_create_cylinder() The former does a simple array access and supposes that the cylinder exists. The latter is used by the parser(s) and if a cylinder with the given id does not exist, cylinders up to that id are generated. One point will make C programmers cringe: the cylinder structure is passed by value. This is due to the way the table-macros work. A refactoring of the table macros is planned. It has to be noted that the size of a cylinder_t is 64 bytes, i.e. 8 long words on a 64-bit architecture, so passing on the stack is probably not even significantly slower than passing as reference. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'core/planner.c')
-rw-r--r--core/planner.c80
1 files changed, 40 insertions, 40 deletions
diff --git a/core/planner.c b/core/planner.c
index d488413c8..4a480640d 100644
--- a/core/planner.c
+++ b/core/planner.c
@@ -89,7 +89,7 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_
int get_gasidx(struct dive *dive, struct gasmix mix)
{
- return find_best_gasmix_match(mix, dive->cylinder);
+ return find_best_gasmix_match(mix, &dive->cylinders);
}
static void interpolate_transition(struct deco_state *ds, struct dive *dive, duration_t t0, duration_t t1, depth_t d0, depth_t d1, struct gasmix gasmix, o2pressure_t po2, enum divemode_t divemode)
@@ -177,10 +177,9 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, struct deco_s
/* if a default cylinder is set, use that */
-void fill_default_cylinder(struct dive *dive, int idx)
+void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl)
{
const char *cyl_name = prefs.default_cylinder;
- cylinder_t *cyl = &dive->cylinder[idx];
struct tank_info_t *ti = tank_info;
pressure_t pO2 = {.mbar = 1600};
@@ -270,7 +269,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive,
/* Create first sample at time = 0, not based on dp because
* there is no real dp for time = 0, set first cylinder to 0
* O2 setpoint for this sample will be filled later from next dp */
- cyl = &dive->cylinder[0];
+ cyl = get_or_create_cylinder(dive, 0);
sample = prepare_sample(dc);
sample->sac.mliter = prefs.bottomsac;
if (track_gas && cyl->type.workingpressure.mbar)
@@ -304,7 +303,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive,
if (dp->cylinderid != lastcylid) {
/* need to insert a first sample for the new gas */
add_gas_switch_event(dive, dc, lasttime + 1, dp->cylinderid);
- cyl = &dive->cylinder[dp->cylinderid];
+ cyl = get_or_create_cylinder(dive, dp->cylinderid);
sample = prepare_sample(dc);
sample[-1].setpoint.mbar = po2;
sample->time.seconds = lasttime + 1;
@@ -410,7 +409,7 @@ static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive
int nr = 0;
struct gaschanges *gaschanges = NULL;
struct divedatapoint *dp = diveplan->dp;
- int best_depth = dive->cylinder[*asc_cylinder].depth.mm;
+ int best_depth = dive->cylinders.cylinders[*asc_cylinder].depth.mm;
bool total_time_zero = true;
while (dp) {
if (dp->time == 0 && total_time_zero) {
@@ -445,7 +444,7 @@ static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive
for (nr = 0; nr < *gaschangenr; nr++) {
int idx = gaschanges[nr].gasidx;
printf("gaschange nr %d: @ %5.2lfm gasidx %d (%s)\n", nr, gaschanges[nr].depth / 1000.0,
- idx, gasname(dive->cylinder[idx].gasmix));
+ idx, gasname(dive->cylinders.cylinders[idx].gasmix));
}
#endif
return gaschanges;
@@ -528,7 +527,7 @@ int ascent_velocity(int depth, int avg_depth, int bottom_time)
static void track_ascent_gas(int depth, struct dive *dive, int cylinder_id, int avg_depth, int bottom_time, bool safety_stop, enum divemode_t divemode)
{
- cylinder_t *cylinder = &dive->cylinder[cylinder_id];
+ cylinder_t *cylinder = &dive->cylinders.cylinders[cylinder_id];
while (depth > 0) {
int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP;
if (deltad > depth)
@@ -596,7 +595,10 @@ static bool trial_ascent(struct deco_state *ds, int wait_time, int trial_depth,
*/
static bool enough_gas(const struct dive *dive, int current_cylinder)
{
- const cylinder_t *cyl = &dive->cylinder[current_cylinder];
+ cylinder_t *cyl;
+ if (current_cylinder < 0 || current_cylinder >= dive->cylinders.nr)
+ return false;
+ cyl = &dive->cylinders.cylinders[current_cylinder];
if (!cyl->start.mbar)
return true;
@@ -730,7 +732,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
const struct event *ev = NULL;
divemode = UNDEF_COMP_TYPE;
divemode = get_current_divemode(&dive->dc, bottom_time, &ev, &divemode);
- gas = dive->cylinder[current_cylinder].gasmix;
+ gas = dive->cylinders.cylinders[current_cylinder].gasmix;
po2 = sample->setpoint.mbar;
depth = dive->dc.sample[dive->dc.samples - 1].depth.mm;
@@ -785,11 +787,11 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
// How long can we stay at the current depth and still directly ascent to the surface?
do {
add_segment(ds, depth_to_bar(depth, dive),
- dive->cylinder[current_cylinder].gasmix,
+ dive->cylinders.cylinders[current_cylinder].gasmix,
timestep, po2, divemode, prefs.bottomsac);
- update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, &dive->cylinder[current_cylinder], false, divemode);
+ update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, &dive->cylinders.cylinders[current_cylinder], false, divemode);
clock += timestep;
- } while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, dive->cylinder[current_cylinder].gasmix,
+ } while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, dive->cylinders.cylinders[current_cylinder].gasmix,
po2, diveplan->surface_pressure / 1000.0, dive, divemode) &&
enough_gas(dive, current_cylinder) && clock < 6 * 3600);
@@ -797,7 +799,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
// In the best of all worlds, we would roll back also the last add_segment in terms of caching deco state, but
// let's ignore that since for the eventual ascent in recreational mode, nobody looks at the ceiling anymore,
// so we don't really have to compute the deco state.
- update_cylinder_pressure(dive, depth, depth, -timestep, prefs.bottomsac, &dive->cylinder[current_cylinder], false, divemode);
+ update_cylinder_pressure(dive, depth, depth, -timestep, prefs.bottomsac, &dive->cylinders.cylinders[current_cylinder], false, divemode);
clock -= timestep;
plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, true, divemode);
previous_point_time = clock;
@@ -835,7 +837,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
if (best_first_ascend_cylinder != current_cylinder) {
current_cylinder = best_first_ascend_cylinder;
- gas = dive->cylinder[current_cylinder].gasmix;
+ gas = dive->cylinders.cylinders[current_cylinder].gasmix;
#if DEBUG_PLAN & 16
printf("switch to gas %d (%d/%d) @ %5.2lfm\n", best_first_ascend_cylinder,
@@ -849,7 +851,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
divemode = OC;
po2 = 0;
add_segment(ds, depth_to_bar(depth, dive),
- dive->cylinder[current_cylinder].gasmix,
+ dive->cylinders.cylinders[current_cylinder].gasmix,
prefs.min_switch_duration, po2, divemode, prefs.bottomsac);
plan_add_segment(diveplan, prefs.min_switch_duration, depth, current_cylinder, po2, false, divemode);
clock += prefs.min_switch_duration;
@@ -891,7 +893,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time);
/* Always prefer the best_first_ascend_cylinder if it has the right gasmix.
* Otherwise take first cylinder from list with rightgasmix */
- if (same_gasmix(gas, dive->cylinder[best_first_ascend_cylinder].gasmix))
+ if (same_gasmix(gas, dive->cylinders.cylinders[best_first_ascend_cylinder].gasmix))
current_cylinder = best_first_ascend_cylinder;
else
current_cylinder = get_gasidx(dive, gas);
@@ -916,7 +918,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
deltad = depth - stoplevels[stopidx];
add_segment(ds, depth_to_bar(depth, dive),
- dive->cylinder[current_cylinder].gasmix,
+ dive->cylinders.cylinders[current_cylinder].gasmix,
TIMESTEP, po2, divemode, prefs.decosac);
last_segment_min_switch = false;
clock += TIMESTEP;
@@ -941,21 +943,21 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
if (current_cylinder != gaschanges[gi].gasidx) {
if (!prefs.switch_at_req_stop ||
!trial_ascent(ds, 0, depth, stoplevels[stopidx - 1], avg_depth, bottom_time,
- dive->cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode) || get_o2(dive->cylinder[current_cylinder].gasmix) < 160) {
+ dive->cylinders.cylinders[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode) || get_o2(dive->cylinders.cylinders[current_cylinder].gasmix) < 160) {
if (is_final_plan)
plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false, divemode);
stopping = true;
previous_point_time = clock;
current_cylinder = gaschanges[gi].gasidx;
- gas = dive->cylinder[current_cylinder].gasmix;
+ gas = dive->cylinders.cylinders[current_cylinder].gasmix;
#if DEBUG_PLAN & 16
printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi].gasidx,
(get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi].depth / 1000.0);
#endif
/* Stop for the minimum duration to switch gas unless we switch to o2 */
- if (!last_segment_min_switch && get_o2(dive->cylinder[current_cylinder].gasmix) != 1000) {
+ if (!last_segment_min_switch && get_o2(dive->cylinders.cylinders[current_cylinder].gasmix) != 1000) {
add_segment(ds, depth_to_bar(depth, dive),
- dive->cylinder[current_cylinder].gasmix,
+ dive->cylinders.cylinders[current_cylinder].gasmix,
prefs.min_switch_duration, po2, divemode, prefs.decosac);
clock += prefs.min_switch_duration;
last_segment_min_switch = true;
@@ -975,7 +977,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
while (1) {
/* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */
if (trial_ascent(ds, 0, depth, stoplevels[stopidx], avg_depth, bottom_time,
- dive->cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode)) {
+ dive->cylinders.cylinders[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode)) {
decostoptable[decostopcounter].depth = depth;
decostoptable[decostopcounter].time = 0;
decostopcounter++;
@@ -1001,15 +1003,15 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
*/
if (pendinggaschange) {
current_cylinder = gaschanges[gi + 1].gasidx;
- gas = dive->cylinder[current_cylinder].gasmix;
+ gas = dive->cylinders.cylinders[current_cylinder].gasmix;
#if DEBUG_PLAN & 16
printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi + 1].gasidx,
(get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi + 1].depth / 1000.0);
#endif
/* Stop for the minimum duration to switch gas unless we switch to o2 */
- if (!last_segment_min_switch && get_o2(dive->cylinder[current_cylinder].gasmix) != 1000) {
+ if (!last_segment_min_switch && get_o2(dive->cylinders.cylinders[current_cylinder].gasmix) != 1000) {
add_segment(ds, depth_to_bar(depth, dive),
- dive->cylinder[current_cylinder].gasmix,
+ dive->cylinders.cylinders[current_cylinder].gasmix,
prefs.min_switch_duration, po2, divemode, prefs.decosac);
clock += prefs.min_switch_duration;
last_segment_min_switch = true;
@@ -1018,7 +1020,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
}
int new_clock = wait_until(ds, dive, clock, clock, laststoptime * 2 + 1, timestep, depth, stoplevels[stopidx], avg_depth,
- bottom_time, dive->cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, divemode);
+ bottom_time, dive->cylinders.cylinders[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, divemode);
laststoptime = new_clock - clock;
/* Finish infinite deco */
if (laststoptime >= 48 * 3600 && depth >= 6000) {
@@ -1033,12 +1035,12 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
* backgas. This could be customized if there were demand.
*/
if (break_cylinder == -1) {
- if (get_o2(dive->cylinder[best_first_ascend_cylinder].gasmix) <= 320)
+ if (get_o2(dive->cylinders.cylinders[best_first_ascend_cylinder].gasmix) <= 320)
break_cylinder = best_first_ascend_cylinder;
else
break_cylinder = 0;
}
- if (get_o2(dive->cylinder[current_cylinder].gasmix) == 1000) {
+ if (get_o2(dive->cylinders.cylinders[current_cylinder].gasmix) == 1000) {
if (laststoptime >= 12 * 60) {
laststoptime = 12 * 60;
new_clock = clock + laststoptime;
@@ -1049,7 +1051,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
plan_add_segment(diveplan, laststoptime, depth, current_cylinder, po2, false, divemode);
previous_point_time = clock + laststoptime;
current_cylinder = break_cylinder;
- gas = dive->cylinder[current_cylinder].gasmix;
+ gas = dive->cylinders.cylinders[current_cylinder].gasmix;
}
} else if (o2break_next) {
if (laststoptime >= 6 * 60) {
@@ -1061,11 +1063,11 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
plan_add_segment(diveplan, laststoptime, depth, current_cylinder, po2, false, divemode);
previous_point_time = clock + laststoptime;
current_cylinder = breakfrom_cylinder;
- gas = dive->cylinder[current_cylinder].gasmix;
+ gas = dive->cylinders.cylinders[current_cylinder].gasmix;
}
}
}
- add_segment(ds, depth_to_bar(depth, dive), dive->cylinder[stop_cylinder].gasmix,
+ add_segment(ds, depth_to_bar(depth, dive), dive->cylinders.cylinders[stop_cylinder].gasmix,
laststoptime, po2, divemode, prefs.decosac);
last_segment_min_switch = false;
decostoptable[decostopcounter].depth = depth;
@@ -1099,14 +1101,12 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
}
if (prefs.surface_segment != 0) {
- for (int i = 0; i < MAX_CYLINDERS; i++)
- if (cylinder_nodata(&dive->cylinder[i])) {
- // Switch to an empty air cylinder for breathing air at the surface
- // If no empty cylinder is found, keep using last deco gas
- current_cylinder = i;
- dive->cylinder[i].cylinder_use = NOT_USED;
- break;
- }
+ // Switch to an empty air cylinder for breathing air at the surface
+ // If no empty cylinder is found, keep using last deco gas
+ cylinder_t cyl = { 0 };
+ cyl.cylinder_use = NOT_USED;
+ add_to_cylinder_table(&dive->cylinders, dive->cylinders.nr, cyl);
+ current_cylinder = dive->cylinders.nr - 1;
plan_add_segment(diveplan, prefs.surface_segment, 0, current_cylinder, 0, false, OC);
}
create_dive_from_plan(diveplan, dive, is_planner);