diff options
Diffstat (limited to 'planner.c')
-rw-r--r-- | planner.c | 183 |
1 files changed, 142 insertions, 41 deletions
@@ -115,7 +115,7 @@ int get_gasidx(struct dive *dive, struct gasmix *mix) int gasidx = -1; while (++gasidx < MAX_CYLINDERS) - if (gasmix_distance(&dive->cylinder[gasidx].gasmix, mix) < 200) + if (gasmix_distance(&dive->cylinder[gasidx].gasmix, mix) < 100) return gasidx; return -1; } @@ -210,7 +210,7 @@ static int verify_gas_exists(struct gasmix mix_in) cyl = displayed_dive.cylinder + i; if (cylinder_nodata(cyl)) continue; - if (gasmix_distance(&cyl->gasmix, &mix_in) < 200) + if (gasmix_distance(&cyl->gasmix, &mix_in) < 100) return i; } fprintf(stderr, "this gas %s should have been on the cylinder list\nThings will fail now\n", gasname(&mix_in)); @@ -307,11 +307,10 @@ static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas) /* Check for SetPoint change */ if (oldpo2 != po2) { - if (lasttime) - /* this is a bad idea - we should get a different SAMPLE_EVENT type - * reserved for this in libdivecomputer... overloading SMAPLE_EVENT_PO2 - * with a different meaning will only cause confusion elsewhere in the code */ - add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, "SP change"); + /* this is a bad idea - we should get a different SAMPLE_EVENT type + * reserved for this in libdivecomputer... overloading SMAPLE_EVENT_PO2 + * with a different meaning will only cause confusion elsewhere in the code */ + add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, "SP change"); oldpo2 = po2; } @@ -521,7 +520,7 @@ static unsigned int *sort_stops(int *dstops, int dnr, struct gaschanges *gstops, static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, int error) { - char buffer[20000], temp[1000]; + char buffer[2000000], temp[100000]; int len, lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0; struct divedatapoint *dp = diveplan->dp; bool gaschange = !plan_verbatim, postponed = plan_verbatim; @@ -780,7 +779,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool dive->notes = strdup(buffer); } -int ascend_velocity(int depth, int avg_depth, int bottom_time) +int ascent_velocity(int depth, int avg_depth, int bottom_time) { /* We need to make this configurable */ @@ -801,7 +800,62 @@ int ascend_velocity(int depth, int avg_depth, int bottom_time) } } -int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool show_disclaimer) +void track_ascent_gas(int depth, cylinder_t *cylinder, int avg_depth, int bottom_time, bool safety_stop) +{ + while (depth > 0) { + int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; + if (deltad > depth) + deltad = depth; + update_cylinder_pressure(&displayed_dive, depth, depth - deltad, TIMESTEP, prefs.decosac, cylinder, true); + if (depth <= 5000 && depth >= (5000 - deltad) && safety_stop) { + update_cylinder_pressure(&displayed_dive, 5000, 5000, 180, prefs.decosac, cylinder, true); + safety_stop = false; + } + depth -= deltad; + } +} + +bool trial_ascent(int trial_depth, int stoplevel, int avg_depth, int bottom_time, double tissue_tolerance, struct gasmix *gasmix, int po2, double surface_pressure) +{ + + bool clear_to_ascend = true; + char *trial_cache = NULL; + + cache_deco_state(tissue_tolerance, &trial_cache); + while (trial_depth > stoplevel) { + int deltad = ascent_velocity(trial_depth, avg_depth, bottom_time) * TIMESTEP; + if (deltad > trial_depth) /* don't test against depth above surface */ + deltad = trial_depth; + tissue_tolerance = add_segment(depth_to_mbar(trial_depth, &displayed_dive) / 1000.0, + gasmix, + TIMESTEP, po2, &displayed_dive, prefs.decosac); + if (deco_allowed_depth(tissue_tolerance, surface_pressure, &displayed_dive, 1) > trial_depth - deltad) { + /* We should have stopped */ + clear_to_ascend = false; + break; + } + trial_depth -= deltad; + } + restore_deco_state(trial_cache); + return clear_to_ascend; +} + +bool enough_gas(int current_cylinder) +{ + cylinder_t *cyl; + cyl = &displayed_dive.cylinder[current_cylinder]; + + if (!cyl->start.mbar) + return true; + if (cyl->type.size.mliter) + return (float) (cyl->end.mbar - prefs.reserve_gas) * cyl->type.size.mliter / 1000.0 > (float) cyl->deco_gas_used.mliter; + else + return true; +} + +// Work out the stops. Return value is if there were any mandatory stops. + +bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool show_disclaimer) { struct sample *sample; int po2; @@ -813,11 +867,10 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s struct gaschanges *gaschanges = NULL; int gaschangenr; int *stoplevels = NULL; - char *trial_cache = NULL; bool stopping = false; bool clear_to_ascend; int clock, previous_point_time; - int avg_depth, bottom_time = 0; + int avg_depth, max_depth, bottom_time = 0; int last_ascend_rate; int best_first_ascend_cylinder; struct gasmix gas; @@ -825,12 +878,24 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s int breaktime = -1; int breakcylinder = 0; int error = 0; + bool decodive = false; set_gf(diveplan->gflow, diveplan->gfhigh, prefs.gf_low_at_maxdepth); if (!diveplan->surface_pressure) diveplan->surface_pressure = SURFACE_PRESSURE; create_dive_from_plan(diveplan, is_planner); + if (prefs.verbatim_plan) + plan_verbatim = true; + if (prefs.display_runtime) + plan_display_runtime = true; + if (prefs.display_duration) + plan_display_duration = true; + if (prefs.display_transitions) + plan_display_transitions = true; + if (prefs.last_stop) + decostoplevels[1] = 6000; + /* Let's start at the last 'sample', i.e. the last manually entered waypoint. */ sample = &displayed_dive.dc.sample[displayed_dive.dc.samples - 1]; @@ -842,15 +907,15 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s current_cylinder = 0; } depth = displayed_dive.dc.sample[displayed_dive.dc.samples - 1].depth.mm; - avg_depth = average_depth(diveplan); - last_ascend_rate = ascend_velocity(depth, avg_depth, bottom_time); + average_max_depth(diveplan, &avg_depth, &max_depth); + last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); /* if all we wanted was the dive just get us back to the surface */ if (!is_planner) { transitiontime = depth / 75; /* this still needs to be made configurable */ plan_add_segment(diveplan, transitiontime, 0, gas, po2, false); create_dive_from_plan(diveplan, is_planner); - return(error); + return(false); } tissue_tolerance = tissue_at_end(&displayed_dive, cached_datap); @@ -881,6 +946,57 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s /* Keep time during the ascend */ bottom_time = clock = previous_point_time = displayed_dive.dc.sample[displayed_dive.dc.samples - 1].time.seconds; gi = gaschangenr - 1; + if(prefs.recreational_mode) { + bool safety_stop = prefs.safetystop && max_depth >= 10000; + track_ascent_gas(depth, &displayed_dive.cylinder[current_cylinder], avg_depth, bottom_time, safety_stop); + // How long can we stay at the current depth and still directly ascent to the surface? + while (trial_ascent(depth, 0, avg_depth, bottom_time, tissue_tolerance, &displayed_dive.cylinder[current_cylinder].gasmix, + po2, diveplan->surface_pressure / 1000.0) && + enough_gas(current_cylinder)) { + tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, + &displayed_dive.cylinder[current_cylinder].gasmix, + DECOTIMESTEP, po2, &displayed_dive, prefs.bottomsac); + update_cylinder_pressure(&displayed_dive, depth, depth, DECOTIMESTEP, prefs.bottomsac, &displayed_dive.cylinder[current_cylinder], false); + clock += DECOTIMESTEP; + } + clock -= DECOTIMESTEP; + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, true); + previous_point_time = clock; + do { + /* Ascend to surface */ + int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; + if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); + previous_point_time = clock; + last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); + } + if (depth - deltad < 0) + deltad = depth; + + tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, + &displayed_dive.cylinder[current_cylinder].gasmix, + TIMESTEP, po2, &displayed_dive, prefs.decosac); + clock += TIMESTEP; + depth -= deltad; + if (depth <= 5000 && depth >= (5000 - deltad) && safety_stop) { + plan_add_segment(diveplan, clock - previous_point_time, 5000, gas, po2, false); + previous_point_time = clock; + clock += 180; + plan_add_segment(diveplan, clock - previous_point_time, 5000, gas, po2, false); + previous_point_time = clock; + safety_stop = false; + } + } while (depth > 0); + plan_add_segment(diveplan, clock - previous_point_time, 0, gas, po2, false); + create_dive_from_plan(diveplan, is_planner); + add_plan_to_notes(diveplan, &displayed_dive, show_disclaimer, error); + fixup_dc_duration(&displayed_dive.dc); + + free(stoplevels); + free(gaschanges); + + return(false); + } if (best_first_ascend_cylinder != current_cylinder) { stopping = true; @@ -897,12 +1013,12 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s /* We will break out when we hit the surface */ do { /* Ascend to next stop depth */ - int deltad = ascend_velocity(depth, avg_depth, bottom_time) * TIMESTEP; - if (ascend_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { + int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; + if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; stopping = false; - last_ascend_rate = ascend_velocity(depth, avg_depth, bottom_time); + last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); } if (depth - deltad < stoplevels[stopidx]) deltad = depth - stoplevels[stopidx]; @@ -936,31 +1052,14 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s --stopidx; /* Save the current state and try to ascend to the next stopdepth */ - int trial_depth = depth; - cache_deco_state(tissue_tolerance, &trial_cache); while (1) { /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ - clear_to_ascend = true; - while (trial_depth > stoplevels[stopidx]) { - int deltad = ascend_velocity(trial_depth, avg_depth, bottom_time) * TIMESTEP; - if (deltad > trial_depth) /* don't test against depth above surface */ - deltad = trial_depth; - tissue_tolerance = add_segment(depth_to_mbar(trial_depth, &displayed_dive) / 1000.0, - &displayed_dive.cylinder[current_cylinder].gasmix, - TIMESTEP, po2, &displayed_dive, prefs.decosac); - if (deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, &displayed_dive, 1) > trial_depth - deltad) { - /* We should have stopped */ - clear_to_ascend = false; - break; - } - trial_depth -= deltad; - } - restore_deco_state(trial_cache); - - if (clear_to_ascend) + if (trial_ascent(depth, stoplevels[stopidx], avg_depth, bottom_time, tissue_tolerance, + &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0)) break; /* We did not hit the ceiling */ /* Add a minute of deco time and then try again */ + decodive = true; if (!stopping) { /* The last segment was an ascend segment. * Add a waypoint for start of this deco stop */ @@ -971,7 +1070,6 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, &displayed_dive.cylinder[current_cylinder].gasmix, DECOTIMESTEP, po2, &displayed_dive, prefs.decosac); - cache_deco_state(tissue_tolerance, &trial_cache); clock += DECOTIMESTEP; /* Finish infinite deco */ if(clock >= 48 * 3600 && depth >= 6000) { @@ -1003,7 +1101,6 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s } } } - trial_depth = depth; } if (stopping) { /* Next we will ascend again. Add a waypoint if we have spend deco time */ @@ -1023,7 +1120,7 @@ int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool s free(stoplevels); free(gaschanges); - return error; + return decodive; } /* @@ -1082,6 +1179,10 @@ int validate_gas(const char *text, struct gasmix *gas) o2 = O2_IN_AIR; he = 0; text += strlen(translate("gettextFromC", "air")); + } else if (!strcasecmp(text, translate("gettextFromC", "oxygen"))) { + o2 = 1000; + he = 0; + text += strlen(translate("gettextFromC", "oxygen")); } else if (!strncasecmp(text, translate("gettextFromC", "ean"), 3)) { o2 = get_permille(text + 3, &text); he = 0; |