void setupPlanVpmb100mTo70m30min(struct diveplan *dp) { dp->salinity = 10300; dp->surface_pressure = 1013; dp->bottomsac = 0; dp->decosac = 0; struct gasmix bottomgas = { {120}, {650} }; struct gasmix tx21_35 = { {210}, {350} }; struct gasmix ean50 = { {500}, {0} }; struct gasmix oxygen = { {1000}, {0} }; pressure_t po2 = { 1600 }; displayed_dive.cylinder[0].gasmix = bottomgas; displayed_dive.cylinder[1].gasmix = tx21_35; displayed_dive.cylinder[2].gasmix = ean50; displayed_dive.cylinder[3].gasmix = oxygen; displayed_dive.surface_pressure.mbar = 1013; reset_cylinders(&displayed_dive, true); free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(18, 60); plan_add_segment(dp, droptime, M_OR_FT(100, 330), bottomgas, 0, 1); plan_add_segment(dp, 20*60 - droptime, M_OR_FT(100, 330), bottomgas, 0, 1); plan_add_segment(dp, 3*60, M_OR_FT(70, 230), bottomgas, 0, 1); plan_add_segment(dp, (30 - 20 - 3) * 60, M_OR_FT(70, 230), bottomgas, 0, 1); plan_add_segment(dp, 0, gas_mod(&tx21_35, po2, &displayed_dive, M_OR_FT(3,10)).mm, tx21_35, 0, 1); plan_add_segment(dp, 0, gas_mod(&ean50, po2, &displayed_dive, M_OR_FT(3,10)).mm, ean50, 0, 1); plan_add_segment(dp, 0, gas_mod(&oxygen, po2, &displayed_dive, M_OR_FT(3,10)).mm, oxygen, 0, 1); }
bool DivePlannerPointsModel::addGas(struct gasmix mix) { sanitize_gasmix(&mix); for (int i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = &displayed_dive.cylinder[i]; if (cylinder_nodata(cyl)) { fill_default_cylinder(cyl); cyl->gasmix = mix; /* The depth to change to that gas is given by the depth where its pO₂ is 1.6 bar. * The user should be able to change this depth manually. */ pressure_t modpO2; modpO2.mbar = prefs.decopo2; cyl->depth = gas_mod(&mix, modpO2, M_OR_FT(3,10)); // FIXME -- need to get rid of stagingDIve // the following now uses displayed_dive !!!! CylindersModel::instance()->updateDive(); return true; } if (!gasmix_distance(&cyl->gasmix, &mix)) return true; } qDebug("too many gases"); return false; }
void setupPlanVpmb60m30minAir(struct diveplan *dp) { dp->salinity = 10300; dp->surface_pressure = 1013; dp->bottomsac = 0; dp->decosac = 0; struct gasmix bottomgas = { {210}, {0} }; pressure_t po2 = { 1600 }; displayed_dive.cylinder[0].gasmix = bottomgas; displayed_dive.surface_pressure.mbar = 1013; reset_cylinders(&displayed_dive, true); free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, droptime, M_OR_FT(60, 200), bottomgas, 0, 1); plan_add_segment(dp, 30*60 - droptime, M_OR_FT(60, 200), bottomgas, 0, 1); }
void DivePlannerGraphics::settingsChanged() { if (depthLine->unitSystem == prefs.units.length) return; depthLine->setTickInterval(M_OR_FT(10, 30)); depthLine->updateTicks(); depthLine->unitSystem = prefs.units.length; }
void setupPlanVpmb30m20min(struct diveplan *dp) { dp->salinity = 10300; dp->surface_pressure = 1013; dp->bottomsac = prefs.bottomsac; dp->decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; displayed_dive.cylinder[0].gasmix = bottomgas; displayed_dive.cylinder[0].type.size.mliter = 36000; displayed_dive.cylinder[0].type.workingpressure.mbar = 232000; displayed_dive.surface_pressure.mbar = 1013; reset_cylinders(&displayed_dive, true); free_dps(dp); int droptime = M_OR_FT(30, 100) * 60 / M_OR_FT(18, 60); plan_add_segment(dp, droptime, M_OR_FT(30, 100), 0, 0, 1, OC); plan_add_segment(dp, 20 * 60 - droptime, M_OR_FT(30, 100), 0, 0, 1, OC); }
Q_FOREACH(QGraphicsItem *i, scene()->selectedItems()) { if (DiveHandler *handler = qgraphicsitem_cast<DiveHandler*>(i)) { int row = handles.indexOf(handler); divedatapoint dp = plannerModel->at(row); if (dp.depth >= depthLine->maximum()) continue; dp.depth += M_OR_FT(1,5); plannerModel->editStop(row, dp); } }
void DivePlannerPointsModel::createSimpleDive() { // initialize the start time in the plan diveplan.when = displayed_dive.when; // Use gas from the first cylinder int cylinderid = 0; // If we're in drop_stone_mode, don't add a first point. // It will be added implicit. if (!prefs.drop_stone_mode) addStop(M_OR_FT(15, 45), 1 * 60, cylinderid, 0, true); addStop(M_OR_FT(15, 45), 20 * 60, 0, 0, true); if (!isPlanner()) { addStop(M_OR_FT(5, 15), 42 * 60, 0, cylinderid, true); addStop(M_OR_FT(5, 15), 45 * 60, 0, cylinderid, true); } updateMaxDepth(); }
void setupPlanVpmb60m10mTx(struct diveplan *dp) { dp->salinity = 10300; dp->surface_pressure = 1013; dp->gfhigh = 100; dp->gflow = 100; dp->bottomsac = prefs.bottomsac; dp->decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix tx50_15 = {{500}, {150}}; struct gasmix oxygen = {{1000}, {0}}; pressure_t po2 = {1600}; displayed_dive.cylinder[0].gasmix = bottomgas; displayed_dive.cylinder[0].type.size.mliter = 24000; displayed_dive.cylinder[0].type.workingpressure.mbar = 232000; displayed_dive.cylinder[1].gasmix = tx50_15; displayed_dive.cylinder[2].gasmix = oxygen; reset_cylinders(&displayed_dive, true); free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, 0, gas_mod(tx50_15, po2, &displayed_dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, gas_mod(oxygen, po2, &displayed_dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); }
void setupPlan(struct diveplan *dp) { dp->salinity = 10300; dp->surface_pressure = 1013; dp->gfhigh = 100; dp->gflow = 100; dp->bottomsac = 0; dp->decosac = 0; struct gasmix bottomgas = { {150}, {450} }; struct gasmix ean36 = { {360}, {0} }; struct gasmix oxygen = { {1000}, {0} }; pressure_t po2 = { 1600 }; displayed_dive.cylinder[0].gasmix = bottomgas; displayed_dive.cylinder[1].gasmix = ean36; displayed_dive.cylinder[2].gasmix = oxygen; reset_cylinders(&displayed_dive, true); free_dps(dp); int droptime = M_OR_FT(79, 260) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, droptime, M_OR_FT(79, 260), bottomgas, 0, 1); plan_add_segment(dp, 30*60 - droptime, M_OR_FT(79, 260), bottomgas, 0, 1); plan_add_segment(dp, 0, gas_mod(&ean36, po2, &displayed_dive, M_OR_FT(3,10)).mm, ean36, 0, 1); plan_add_segment(dp, 0, gas_mod(&oxygen, po2, &displayed_dive, M_OR_FT(3,10)).mm, oxygen, 0, 1); }
void setupPlanVpmb100m10min(struct diveplan *dp) { dp->salinity = 10300; dp->surface_pressure = 1013; dp->bottomsac = prefs.bottomsac; dp->decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix ean50 = {{500}, {0}}; struct gasmix oxygen = {{1000}, {0}}; pressure_t po2 = {1600}; displayed_dive.cylinder[0].gasmix = bottomgas; displayed_dive.cylinder[0].type.size.mliter = 60000; displayed_dive.cylinder[0].type.workingpressure.mbar = 232000; displayed_dive.cylinder[1].gasmix = ean50; displayed_dive.cylinder[2].gasmix = oxygen; displayed_dive.surface_pressure.mbar = 1013; reset_cylinders(&displayed_dive, true); free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, gas_mod(ean50, po2, &displayed_dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, gas_mod(oxygen, po2, &displayed_dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); }
void DivePlannerPointsModel::createSimpleDive() { struct gasmix gas = { 0 }; // initialize the start time in the plan diveplan.when = displayed_dive.when; if (isPlanner()) // let's use the gas from the first cylinder gas = displayed_dive.cylinder[0].gasmix; // If we're in drop_stone_mode, don't add a first point. // It will be added implicit. if (!prefs.drop_stone_mode) addStop(M_OR_FT(15, 45), 1 * 60, &gas, 0, true); addStop(M_OR_FT(15, 45), 20 * 60, &gas, 0, true); if (!isPlanner()) { addStop(M_OR_FT(5, 15), 42 * 60, &gas, 0, true); addStop(M_OR_FT(5, 15), 45 * 60, &gas, 0, true); } }
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned * and that the pressures are reset to start = end = workingpressure */ void reset_cylinders(struct dive *dive) { int i; pressure_t pO2 = {.mbar = 1400}; for (i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = &dive->cylinder[i]; if (cylinder_none(cyl)) continue; if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, assume conservative pO2 */ cyl->depth = gas_mod(&cyl->gasmix, pO2, M_OR_FT(3,10)); if (cyl->type.workingpressure.mbar) cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar; cyl->gas_used.mliter = 0; } }
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned * and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */ void reset_cylinders(struct dive *dive, bool track_gas) { int i; pressure_t decopo2 = {.mbar = prefs.decopo2}; for (i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = &dive->cylinder[i]; if (cylinder_none(cyl)) continue; if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */ cyl->depth = gas_mod(&cyl->gasmix, decopo2, dive, M_OR_FT(3,10)); if (track_gas) cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar; cyl->gas_used.mliter = 0; cyl->deco_gas_used.mliter = 0; } }
void setupPlanVpmb100m60min(struct diveplan *dp) { dp->salinity = 10300; dp->surface_pressure = 1013; dp->bottomsac = 0; dp->decosac = 0; struct gasmix bottomgas = { {180}, {450} }; struct gasmix ean50 = { {500}, {0} }; struct gasmix oxygen = { {1000}, {0} }; pressure_t po2 = { 1600 }; displayed_dive.cylinder[0].gasmix = bottomgas; displayed_dive.cylinder[1].gasmix = ean50; displayed_dive.cylinder[2].gasmix = oxygen; displayed_dive.surface_pressure.mbar = 1013; reset_cylinders(&displayed_dive, true); free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, droptime, M_OR_FT(100, 330), bottomgas, 0, 1); plan_add_segment(dp, 60*60 - droptime, M_OR_FT(100, 330), bottomgas, 0, 1); plan_add_segment(dp, 0, gas_mod(&ean50, po2, &displayed_dive, M_OR_FT(3,10)).mm, ean50, 0, 1); plan_add_segment(dp, 0, gas_mod(&oxygen, po2, &displayed_dive, M_OR_FT(3,10)).mm, oxygen, 0, 1); }
int DivePlannerPointsModel::addStop(int milimeters, int seconds, gasmix *gas_in, int ccpoint, bool entered) { struct gasmix air = { 0 }; struct gasmix gas = { 0 }; bool usePrevious = false; if (gas_in) gas = *gas_in; else usePrevious = true; if (recalcQ()) removeDeco(); int row = divepoints.count(); if (seconds == 0 && milimeters == 0 && row != 0) { /* this is only possible if the user clicked on the 'plus' sign on the DivePoints Table */ const divedatapoint t = divepoints.at(lastEnteredPoint()); milimeters = t.depth; seconds = t.time + 600; // 10 minutes. gas = t.gasmix; ccpoint = t.setpoint; } else if (seconds == 0 && milimeters == 0 && row == 0) { milimeters = M_OR_FT(5, 15); // 5m / 15ft seconds = 600; // 10 min //Default to the first defined gas, if we got one. cylinder_t *cyl = &displayed_dive.cylinder[0]; if (cyl) gas = cyl->gasmix; } if (!usePrevious) if (!addGas(gas)) qDebug("addGas failed"); // FIXME add error propagation // check if there's already a new stop before this one: for (int i = 0; i < row; i++) { const divedatapoint &dp = divepoints.at(i); if (dp.time == seconds) { row = i; beginRemoveRows(QModelIndex(), row, row); divepoints.remove(row); endRemoveRows(); break; } if (dp.time > seconds) { row = i; break; } } // Previous, actually means next as we are typically subdiving a segment and the gas for // the segment is determined by the waypoint at the end. if (usePrevious) { if (row < divepoints.count()) { gas = divepoints.at(row).gasmix; } else if (row > 0) { gas = divepoints.at(row - 1).gasmix; } else { if (!addGas(air)) qDebug("addGas failed"); // FIXME add error propagation } } // add the new stop beginInsertRows(QModelIndex(), row, row); divedatapoint point; point.depth = milimeters; point.time = seconds; point.gasmix = gas; point.setpoint = ccpoint; point.entered = entered; point.next = NULL; divepoints.append(point); std::sort(divepoints.begin(), divepoints.end(), divePointsLessThan); endInsertRows(); return row; }
int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_in, int ccpoint, bool entered) { int cylinderid = 0; bool usePrevious = false; if (cylinderid_in) cylinderid = cylinderid_in; else usePrevious = true; if (recalcQ()) removeDeco(); int row = divepoints.count(); if (seconds == 0 && milimeters == 0 && row != 0) { /* this is only possible if the user clicked on the 'plus' sign on the DivePoints Table */ const divedatapoint t = divepoints.at(lastEnteredPoint()); milimeters = t.depth; seconds = t.time + 600; // 10 minutes. cylinderid = t.cylinderid; ccpoint = t.setpoint; } else if (seconds == 0 && milimeters == 0 && row == 0) { milimeters = M_OR_FT(5, 15); // 5m / 15ft seconds = 600; // 10 min // Default to the first cylinder cylinderid = 0; } // check if there's already a new stop before this one: for (int i = 0; i < row; i++) { const divedatapoint &dp = divepoints.at(i); if (dp.time == seconds) { row = i; beginRemoveRows(QModelIndex(), row, row); divepoints.remove(row); endRemoveRows(); break; } if (dp.time > seconds) { row = i; break; } } // Previous, actually means next as we are typically subdiving a segment and the gas for // the segment is determined by the waypoint at the end. if (usePrevious) { if (row < divepoints.count()) { cylinderid = divepoints.at(row).cylinderid; } else if (row > 0) { cylinderid = divepoints.at(row - 1).cylinderid; } } // add the new stop beginInsertRows(QModelIndex(), row, row); divedatapoint point; point.depth = milimeters; point.time = seconds; point.cylinderid = cylinderid; point.setpoint = ccpoint; point.entered = entered; point.next = NULL; divepoints.append(point); std::sort(divepoints.begin(), divepoints.end(), divePointsLessThan); endInsertRows(); return row; }
bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool show_disclaimer) { int bottom_depth; int bottom_gi; int bottom_stopidx; bool is_final_plan = true; int deco_time; int previous_deco_time; char *bottom_cache = NULL; struct sample *sample; int po2; int transitiontime, gi; int current_cylinder; unsigned int stopidx; int depth; double tissue_tolerance = 0.0; struct gaschanges *gaschanges = NULL; int gaschangenr; int *decostoplevels; int decostoplevelcount; unsigned int *stoplevels = NULL; int vpmb_first_stop; bool stopping = false; bool pendinggaschange = false; bool clear_to_ascend; int clock, previous_point_time; int avg_depth, max_depth, bottom_time = 0; int last_ascend_rate; int best_first_ascend_cylinder; struct gasmix gas, bottom_gas; int o2time = 0; 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); // Do we want deco stop array in metres or feet? if (prefs.units.length == METERS ) { decostoplevels = decostoplevels_metric; decostoplevelcount = sizeof(decostoplevels_metric) / sizeof(int); } else { decostoplevels = decostoplevels_imperial; decostoplevelcount = sizeof(decostoplevels_imperial) / sizeof(int); } /* If the user has selected last stop to be at 6m/20', we need to get rid of the 3m/10' stop. * Otherwise reinstate the last stop 3m/10' stop. */ if (prefs.last_stop) *(decostoplevels + 1) = 0; else *(decostoplevels + 1) = M_OR_FT(3,10); /* Let's start at the last 'sample', i.e. the last manually entered waypoint. */ sample = &displayed_dive.dc.sample[displayed_dive.dc.samples - 1]; get_gas_at_time(&displayed_dive, &displayed_dive.dc, sample->time, &gas); po2 = sample->setpoint.mbar; if ((current_cylinder = get_gasidx(&displayed_dive, &gas)) == -1) { report_error(translate("gettextFromC", "Can't find gas %s"), gasname(&gas)); current_cylinder = 0; } depth = displayed_dive.dc.sample[displayed_dive.dc.samples - 1].depth.mm; 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(false); } calc_crushing_pressure(depth_to_mbar(depth, &displayed_dive) / 1000.0); nuclear_regeneration(clock); clear_deco(displayed_dive.surface_pressure.mbar / 1000.0); vpmb_start_gradient(); previous_deco_time = 100000000; deco_time = 10000000; tissue_tolerance = tissue_at_end(&displayed_dive, cached_datap); displayed_dive.surface_pressure.mbar = diveplan->surface_pressure; #if DEBUG_PLAN & 4 printf("gas %s\n", gasname(&gas)); printf("depth %5.2lfm \n", depth / 1000.0); #endif best_first_ascend_cylinder = current_cylinder; /* Find the gases available for deco */ if (po2) { // Don't change gas in CCR mode gaschanges = NULL; gaschangenr = 0; } else { gaschanges = analyze_gaslist(diveplan, &gaschangenr, depth, &best_first_ascend_cylinder); } /* Find the first potential decostopdepth above current depth */ for (stopidx = 0; stopidx < decostoplevelcount; stopidx++) if (*(decostoplevels + stopidx) >= depth) break; if (stopidx > 0) stopidx--; /* Stoplevels are either depths of gas changes or potential deco stop depths. */ stoplevels = sort_stops(decostoplevels, stopidx + 1, gaschanges, gaschangenr); stopidx += gaschangenr; /* 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.deco_mode == RECREATIONAL) { 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; current_cylinder = best_first_ascend_cylinder; gas = displayed_dive.cylinder[current_cylinder].gasmix; #if DEBUG_PLAN & 16 printf("switch to gas %d (%d/%d) @ %5.2lfm\n", best_first_ascend_cylinder, (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[best_first_ascend_cylinder].depth / 1000.0); #endif } // VPM-B or Buehlmann Deco nuclear_regeneration(clock); vpmb_start_gradient(); previous_deco_time = 100000000; deco_time = 10000000; cache_deco_state(tissue_tolerance, &bottom_cache); // Lets us make several iterations bottom_depth = depth; bottom_gi = gi; bottom_gas = gas; bottom_stopidx = stopidx; // Find first stop used for VPM-B Boyle's law compensation if (prefs.deco_mode == VPMB) { vpmb_first_stop = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000, &displayed_dive, 1); if (vpmb_first_stop > 0) { while (stoplevels[stopidx] > vpmb_first_stop) { stopidx--; } stopidx++; vpmb_first_stop = stoplevels[stopidx]; } first_stop_pressure.mbar = depth_to_mbar(vpmb_first_stop, &displayed_dive); } else { first_stop_pressure.mbar = 0; } //CVA do { is_final_plan = (prefs.deco_mode == BUEHLMANN) || (previous_deco_time - deco_time < 10); // CVA time converges if (deco_time != 10000000) vpmb_next_gradient(deco_time, diveplan->surface_pressure / 1000.0); previous_deco_time = deco_time; tissue_tolerance = restore_deco_state(bottom_cache); depth = bottom_depth; gi = bottom_gi; clock = previous_point_time = bottom_time; gas = bottom_gas; stopping = false; decodive = false; stopidx = bottom_stopidx; breaktime = -1; breakcylinder = 0; o2time = 0; last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); if ((current_cylinder = get_gasidx(&displayed_dive, &gas)) == -1) { report_error(translate("gettextFromC", "Can't find gas %s"), gasname(&gas)); current_cylinder = 0; } while (1) { /* We will break out when we hit the surface */ do { /* Ascend to next stop depth */ int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; stopping = false; last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); } if (depth - deltad < stoplevels[stopidx]) deltad = depth - stoplevels[stopidx]; 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; } while (depth > 0 && depth > stoplevels[stopidx]); if (depth <= 0) break; /* We are at the surface */ if (gi >= 0 && stoplevels[stopidx] <= gaschanges[gi].depth) { /* We have reached a gas change. * Record this in the dive plan */ if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; stopping = true; // Boyles Law compensation boyles_law(depth_to_mbar(stoplevels[stopidx], &displayed_dive) / 1000.0); /* Check we need to change cylinder. * We might not if the cylinder was chosen by the user * or user has selected only to switch only at required stops. * If current gas is hypoxic, we want to switch asap */ if (current_cylinder != gaschanges[gi].gasidx) { if (!prefs.switch_at_req_stop || !trial_ascent(depth, stoplevels[stopidx - 1], avg_depth, bottom_time, tissue_tolerance, &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0) || get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) < 160) { current_cylinder = gaschanges[gi].gasidx; gas = displayed_dive.cylinder[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 */ tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, &displayed_dive.cylinder[current_cylinder].gasmix, prefs.min_switch_duration, po2, &displayed_dive, prefs.decosac); clock += prefs.min_switch_duration; if (prefs.doo2breaks && get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000) o2time += prefs.min_switch_duration; } else { /* The user has selected the option to switch gas only at required stops. * Remember that we are waiting to switch gas */ pendinggaschange = true; } } gi--; } --stopidx; /* Save the current state and try to ascend to the next stopdepth */ 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(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 */ if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; stopping = true; // Boyles Law compensation boyles_law(depth_to_mbar(stoplevels[stopidx], &displayed_dive) / 1000.0); } /* Are we waiting to switch gas? * Occurs when the user has selected the option to switch only at required stops */ if (pendinggaschange) { current_cylinder = gaschanges[gi + 1].gasidx; gas = displayed_dive.cylinder[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 */ tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, &displayed_dive.cylinder[current_cylinder].gasmix, prefs.min_switch_duration, po2, &displayed_dive, prefs.decosac); clock += prefs.min_switch_duration; if (prefs.doo2breaks && get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000) o2time += prefs.min_switch_duration; pendinggaschange = false; } /* Deco stop should end when runtime is at a whole minute */ int this_decotimestep; this_decotimestep = DECOTIMESTEP - clock % DECOTIMESTEP; tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, &displayed_dive.cylinder[current_cylinder].gasmix, this_decotimestep, po2, &displayed_dive, prefs.decosac); clock += this_decotimestep; /* Finish infinite deco */ if(clock >= 48 * 3600 && depth >= 6000) { error = LONGDECO; break; } if (prefs.doo2breaks) { /* The backgas breaks option limits time on oxygen to 12 minutes, followed by 6 minutes on * backgas (first defined gas). This could be customized if there were demand. */ if (get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000) { o2time += DECOTIMESTEP; if (o2time >= 12 * 60) { breaktime = 0; breakcylinder = current_cylinder; if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; current_cylinder = 0; gas = displayed_dive.cylinder[current_cylinder].gasmix; } } else { if (breaktime >= 0) { breaktime += DECOTIMESTEP; if (breaktime >= 6 * 60) { o2time = 0; if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; current_cylinder = breakcylinder; gas = displayed_dive.cylinder[current_cylinder].gasmix; breaktime = -1; } } } } } if (stopping) { /* Next we will ascend again. Add a waypoint if we have spend deco time */ if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; stopping = false; } } deco_time = clock - bottom_time; } while (!is_final_plan); 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 decodive; }
DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent), activeDraggedHandler(0) { fill_profile_color(); setBackgroundBrush(profile_color[BACKGROUND].at(0)); setMouseTracking(true); setScene(new QGraphicsScene()); scene()->setSceneRect(0,0,1920,1080); verticalLine = new QGraphicsLineItem( fromPercent(0, Qt::Horizontal), fromPercent(0, Qt::Vertical), fromPercent(0, Qt::Horizontal), fromPercent(100, Qt::Vertical) ); verticalLine->setPen(QPen(Qt::DotLine)); scene()->addItem(verticalLine); horizontalLine = new QGraphicsLineItem( fromPercent(0, Qt::Horizontal), fromPercent(0, Qt::Vertical), fromPercent(100, Qt::Horizontal), fromPercent(0, Qt::Vertical) ); horizontalLine->setPen(QPen(Qt::DotLine)); scene()->addItem(horizontalLine); timeLine = new Ruler(); timeLine->setMinimum(0); timeLine->setMaximum(TIME_INITIAL_MAX); timeLine->setTickInterval(10); timeLine->setColor(getColor(TIME_GRID)); timeLine->setLine( fromPercent(10, Qt::Horizontal), fromPercent(90, Qt::Vertical), fromPercent(90, Qt::Horizontal), fromPercent(90, Qt::Vertical) ); timeLine->setOrientation(Qt::Horizontal); timeLine->setTickSize(fromPercent(1, Qt::Vertical)); timeLine->setTextColor(getColor(TIME_TEXT)); timeLine->updateTicks(); scene()->addItem(timeLine); depthLine = new Ruler(); depthLine->setMinimum(0); depthLine->setMaximum(M_OR_FT(40,120)); depthLine->setTickInterval(M_OR_FT(10,30)); depthLine->setLine( fromPercent(10, Qt::Horizontal), fromPercent(10, Qt::Vertical), fromPercent(10, Qt::Horizontal), fromPercent(90, Qt::Vertical) ); depthLine->setOrientation(Qt::Vertical); depthLine->setTickSize(fromPercent(1, Qt::Horizontal)); depthLine->setColor(getColor(DEPTH_GRID)); depthLine->setTextColor(getColor(SAMPLE_DEEP)); depthLine->updateTicks(); scene()->addItem(depthLine); timeString = new QGraphicsSimpleTextItem(); timeString->setFlag(QGraphicsItem::ItemIgnoresTransformations); timeString->setBrush(profile_color[TIME_TEXT].at(0)); scene()->addItem(timeString); depthString = new QGraphicsSimpleTextItem(); depthString->setFlag(QGraphicsItem::ItemIgnoresTransformations); depthString->setBrush(profile_color[SAMPLE_DEEP].at(0)); scene()->addItem(depthString); diveBg = new QGraphicsPolygonItem(); diveBg->setPen(QPen(QBrush(),0)); scene()->addItem(diveBg); #define ADDBTN(obj, icon, text, horizontal, vertical, tooltip, value, slot) \ obj = new Button(); \ obj->setPixmap(QPixmap(icon)); \ obj->setPos(fromPercent(horizontal, Qt::Horizontal), fromPercent(vertical, Qt::Vertical)); \ obj->setToolTip(QString(tooltip.arg(value))); \ scene()->addItem(obj); \ connect(obj, SIGNAL(clicked()), this, SLOT(slot)); QString incrText; if (prefs.units.length == units::METERS) incrText = tr("10m"); else incrText = tr("30ft"); ADDBTN(plusDepth, ":plus", "" , 5, 5, tr("Increase maximum depth by %1"), incrText, increaseDepth()); ADDBTN(lessDepth, ":minimum","" , 2, 5, tr("Decreases maximum depth by %1"), incrText, decreaseDepth()); ADDBTN(plusTime, ":plus", "" , 95, 95, tr("Increase minimum time by %1"), tr("10min"), increaseTime()); ADDBTN(lessTime, ":minimum","" , 92, 95, tr("Decreases minimum time by %1"), tr("10min"), decreaseTime()); #undef ADDBTN minMinutes = TIME_INITIAL_MAX; QAction *action = NULL; #define ADD_ACTION( SHORTCUT, Slot ) \ action = new QAction(this); \ action->setShortcut( SHORTCUT ); \ action->setShortcutContext(Qt::WindowShortcut); \ addAction(action); \ connect(action, SIGNAL(triggered(bool)), this, SLOT( Slot )) ADD_ACTION(Qt::Key_Escape, keyEscAction()); ADD_ACTION(Qt::Key_Delete, keyDeleteAction()); ADD_ACTION(Qt::Key_Up, keyUpAction()); ADD_ACTION(Qt::Key_Down, keyDownAction()); ADD_ACTION(Qt::Key_Left, keyLeftAction()); ADD_ACTION(Qt::Key_Right, keyRightAction()); #undef ADD_ACTION // Prepare the stuff for the gas-choices. gasListView = new QListView(); gasListView->setWindowFlags(Qt::Popup); gasListView->setModel(GasSelectionModel::instance()); gasListView->hide(); gasListView->installEventFilter(this); connect(gasListView, SIGNAL(activated(QModelIndex)), this, SLOT(selectGas(QModelIndex))); connect(plannerModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(drawProfile())); connect(plannerModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(pointInserted(const QModelIndex&, int, int))); connect(plannerModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(pointsRemoved(const QModelIndex&, int, int))); setRenderHint(QPainter::Antialiasing); }
DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent), activeDraggedHandler(0) { fill_profile_color(); setBackgroundBrush(profile_color[BACKGROUND].at(0)); setMouseTracking(true); setScene(new QGraphicsScene()); scene()->setSceneRect(0,0,1920,1080); verticalLine = new QGraphicsLineItem( fromPercent(0, Qt::Horizontal), fromPercent(0, Qt::Vertical), fromPercent(0, Qt::Horizontal), fromPercent(100, Qt::Vertical) ); verticalLine->setPen(QPen(Qt::DotLine)); scene()->addItem(verticalLine); horizontalLine = new QGraphicsLineItem( fromPercent(0, Qt::Horizontal), fromPercent(0, Qt::Vertical), fromPercent(100, Qt::Horizontal), fromPercent(0, Qt::Vertical) ); horizontalLine->setPen(QPen(Qt::DotLine)); scene()->addItem(horizontalLine); timeLine = new Ruler(); timeLine->setMinimum(0); timeLine->setMaximum(TIME_INITIAL_MAX); timeLine->setTickInterval(10); timeLine->setColor(getColor(TIME_GRID)); timeLine->setLine( fromPercent(10, Qt::Horizontal), fromPercent(85, Qt::Vertical), fromPercent(90, Qt::Horizontal), fromPercent(85, Qt::Vertical) ); timeLine->setOrientation(Qt::Horizontal); timeLine->setTickSize(fromPercent(1, Qt::Vertical)); timeLine->setTextColor(getColor(TIME_TEXT)); timeLine->updateTicks(); scene()->addItem(timeLine); depthLine = new Ruler(); depthLine->setMinimum(0); depthLine->setMaximum(M_OR_FT(40,120)); depthLine->setTickInterval(M_OR_FT(10,30)); depthLine->setLine( fromPercent(10, Qt::Horizontal), fromPercent(10, Qt::Vertical), fromPercent(10, Qt::Horizontal), fromPercent(85, Qt::Vertical) ); depthLine->setOrientation(Qt::Vertical); depthLine->setTickSize(fromPercent(1, Qt::Horizontal)); depthLine->setColor(getColor(DEPTH_GRID)); depthLine->setTextColor(getColor(SAMPLE_DEEP)); depthLine->updateTicks(); scene()->addItem(depthLine); timeString = new QGraphicsSimpleTextItem(); timeString->setFlag(QGraphicsItem::ItemIgnoresTransformations); timeString->setBrush(profile_color[TIME_TEXT].at(0)); scene()->addItem(timeString); depthString = new QGraphicsSimpleTextItem(); depthString->setFlag(QGraphicsItem::ItemIgnoresTransformations); depthString->setBrush(profile_color[SAMPLE_DEEP].at(0)); scene()->addItem(depthString); diveBg = new QGraphicsPolygonItem(); diveBg->setPen(QPen(QBrush(),0)); scene()->addItem(diveBg); QString incrText; if (prefs.units.length == units::METERS) incrText = tr("10m"); else incrText = tr("30ft"); timeHandler = new ExpanderGraphics(); timeHandler->increaseBtn->setPixmap(QString(":plan_plus")); timeHandler->decreaseBtn->setPixmap(QString(":plan_minus")); timeHandler->icon->setPixmap(QString(":icon_time")); connect(timeHandler->increaseBtn, SIGNAL(clicked()), this, SLOT(increaseTime())); connect(timeHandler->decreaseBtn, SIGNAL(clicked()), this, SLOT(decreaseTime())); timeHandler->setPos(fromPercent(83, Qt::Horizontal), fromPercent(100, Qt::Vertical)); timeHandler->setZValue(-2); scene()->addItem(timeHandler); depthHandler = new ExpanderGraphics(); depthHandler->increaseBtn->setPixmap(QString(":arrow_up")); depthHandler->decreaseBtn->setPixmap(QString(":arrow_down")); depthHandler->icon->setPixmap(QString(":icon_depth")); // Inverted here in the slots because the 'up' graphi should increase the depness, // and the down should decrease. connect(depthHandler->increaseBtn, SIGNAL(clicked()), this, SLOT(decreaseDepth())); connect(depthHandler->decreaseBtn, SIGNAL(clicked()), this, SLOT(increaseDepth())); depthHandler->setPos(fromPercent(0, Qt::Horizontal), fromPercent(100, Qt::Vertical)); depthHandler->setZValue(-2); scene()->addItem(depthHandler); minMinutes = TIME_INITIAL_MAX; QAction *action = NULL; #define ADD_ACTION( SHORTCUT, Slot ) \ action = new QAction(this); \ action->setShortcut( SHORTCUT ); \ action->setShortcutContext(Qt::WindowShortcut); \ addAction(action); \ connect(action, SIGNAL(triggered(bool)), this, SLOT( Slot )) ADD_ACTION(Qt::Key_Escape, keyEscAction()); ADD_ACTION(Qt::Key_Delete, keyDeleteAction()); ADD_ACTION(Qt::Key_Up, keyUpAction()); ADD_ACTION(Qt::Key_Down, keyDownAction()); ADD_ACTION(Qt::Key_Left, keyLeftAction()); ADD_ACTION(Qt::Key_Right, keyRightAction()); #undef ADD_ACTION connect(plannerModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(drawProfile())); connect(plannerModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(pointInserted(const QModelIndex&, int, int))); connect(plannerModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(pointsRemoved(const QModelIndex&, int, int))); setRenderHint(QPainter::Antialiasing); }
bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role) { QString vString; bool addDiveMode = DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING; if (addDiveMode) DivePlannerPointsModel::instance()->rememberTanks(); cylinder_t *cyl = cylinderAt(index); switch (index.column()) { case TYPE: if (!value.isNull()) { QByteArray ba = value.toByteArray(); const char *text = ba.constData(); if (!cyl->type.description || strcmp(cyl->type.description, text)) { cyl->type.description = strdup(text); changed = true; } } break; case SIZE: if (CHANGED()) { TankInfoModel *tanks = TankInfoModel::instance(); QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl->type.description); cyl->type.size = string_to_volume(vString.toUtf8().data(), cyl->type.workingpressure); mark_divelist_changed(true); if (!matches.isEmpty()) tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter); changed = true; } break; case WORKINGPRESS: if (CHANGED()) { TankInfoModel *tanks = TankInfoModel::instance(); QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl->type.description); cyl->type.workingpressure = string_to_pressure(vString.toUtf8().data()); if (!matches.isEmpty()) tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl->type.workingpressure.mbar / 1000.0); changed = true; } break; case START: if (CHANGED()) { cyl->start = string_to_pressure(vString.toUtf8().data()); changed = true; } break; case END: if (CHANGED()) { //&& (!cyl->start.mbar || string_to_pressure(vString.toUtf8().data()).mbar <= cyl->start.mbar)) { cyl->end = string_to_pressure(vString.toUtf8().data()); changed = true; } break; case O2: if (CHANGED()) { cyl->gasmix.o2 = string_to_fraction(vString.toUtf8().data()); pressure_t modpO2; modpO2.mbar = prefs.decopo2; cyl->depth = gas_mod(&cyl->gasmix, modpO2, M_OR_FT(3,10)); changed = true; } break; case HE: if (CHANGED()) { cyl->gasmix.he = string_to_fraction(vString.toUtf8().data()); changed = true; } break; case DEPTH: if (CHANGED()) { cyl->depth = string_to_depth(vString.toUtf8().data()); changed = true; } } if (addDiveMode) DivePlannerPointsModel::instance()->tanksUpdated(); dataChanged(index, index); return true; }