diff options
50 files changed, 472 insertions, 217 deletions
diff --git a/Documentation/images/DivePlanner2_f20.jpg b/Documentation/images/DivePlanner2_f20.jpg Binary files differdeleted file mode 100644 index 9f2a5d149..000000000 --- a/Documentation/images/DivePlanner2_f20.jpg +++ /dev/null diff --git a/Documentation/images/PlannerWindow1_f20.jpg b/Documentation/images/PlannerWindow1_f20.jpg Binary files differindex 5e57a3107..f2035d04e 100644..100755 --- a/Documentation/images/PlannerWindow1_f20.jpg +++ b/Documentation/images/PlannerWindow1_f20.jpg diff --git a/Documentation/images/Planner_OC_deco_VPM.jpg b/Documentation/images/Planner_OC_deco_VPM.jpg Binary files differnew file mode 100755 index 000000000..567ba7840 --- /dev/null +++ b/Documentation/images/Planner_OC_deco_VPM.jpg diff --git a/Documentation/images/Planner_OC_rec.jpg b/Documentation/images/Planner_OC_rec.jpg Binary files differnew file mode 100755 index 000000000..cad936ea4 --- /dev/null +++ b/Documentation/images/Planner_OC_rec.jpg diff --git a/Documentation/images/rec_diveplan.jpg b/Documentation/images/rec_diveplan.jpg Binary files differdeleted file mode 100644 index fbafc0155..000000000 --- a/Documentation/images/rec_diveplan.jpg +++ /dev/null diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 6f73e7a55..66d23b124 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -4,7 +4,7 @@ // Linus Torvalds, Miika Turkia, Amit Chaudhuri, Jan Schubert, Willem // Ferguson, Salvador Cuñat, Pedro Neves // :revnumber: 4.6 -// :revdate: January 2017 +// :revdate: March 2017 :icons: :toc2: :toc-placement: manual @@ -16,9 +16,10 @@ image::images/Subsurface4Banner.jpg["Banner",align="center"] [big]#USER MANUAL# *Manual authors*: Willem Ferguson, Jacco van Koll, Dirk Hohndel, Reinout Hoornweg, -Linus Torvalds, Miika Turkia, Amit Chaudhuri, Jan Schubert, Salvador Cuñat, Pedro Neves +Linus Torvalds, Miika Turkia, Amit Chaudhuri, Jan Schubert, Salvador Cuñat, Pedro Neves, +Stefan Fuchs -[blue]#_Version 4.6, January 2017_# +[blue]#_Version 4.6, March 2017_# Welcome as a user of _Subsurface_, an advanced dive logging program with extensive infrastructure to describe, organize, interpret and print scuba @@ -3318,7 +3319,7 @@ image::images/Pref4_f23.jpg["FIGURE: Preferences Graph page",align="center"] respect to inert gas loading and the deeper the ceilings are. Gradient factors of 20/60 are considered conservative and values of 70/90 are considered harsh. - In addition decide whether to check the _GFLow at max depth_ box. If checked, GF_Low is used for the + In addition decide whether to check the _GFLow at max. depth_ box. If checked, GF_Low is used for the deepest dive depth and linearly increased up to the GF_High value at the surface. If unchecked, GF_Low is used between the deepest dive depth and the first deco stop, after which the gradient factor linearly increases up to the GF_High value at the surface. For more information see: @@ -3491,7 +3492,7 @@ image::images/PlannerWindow1_f20.jpg["FIGURE: Dive planner startup window",align * The most efficient way to create a dive profile is to enter the appropriate values into the table marked _Dive planner points_. The first line of the table represents the duration and the final depth of the descent from the surface. Subsequent segments describe the bottom phase of the dive. - The _CC set point_ column is only relevant for closed circuit divers. + The _CC setpoint_ column is only relevant for closed circuit divers. The ascent is usually not specified because this is what the planner is supposed to calculate. Add additional segments to the profile by selecting the "+" icon at the top right hand of the table. Segments entered into the _Dive planner points_ table automatically appear in the *Dive @@ -3562,7 +3563,7 @@ Below is an image of a dive plan for a recreational dive at 30 meters. Although minutes, the duration of the dive is limited by the amount of air in the cylinder. That is shown in the text box at the bottom right of the panel, requiring sufficient air for buddy-sharing during ascent. -image::images/rec_diveplan.jpg["FIGURE: A recreational dive plan: setup",align="center"] +image::images/Planner_OC_rec.jpg["FIGURE: A recreational dive plan: setup",align="center"] ==== Non-recreational open circuit dives, including decompression @@ -3606,7 +3607,7 @@ in the _Rates_ section of the dive setup. pressure for oxygen needs to be specified for the bottom part of the dive (_bottom po2_) as well as for the decompression part of the dive (_deco po2_). Commonly used values are 1.4 bar for the bottom part of the dive and 1.6 bar for any decompression -stages. Normally, a partial pressure of 1.6 bar is not exceeded. PO2 settings and the depth at which switching to a gas takes place can also be edited in the +stages. Normally, a partial pressure of 1.6 bar is not exceeded. pO₂ settings and the depth at which switching to a gas takes place can also be edited in the _Available Gases_ dialog. Normally the planner decides on switching to a new gas when, during ascent, the partial pressure of the new gas has decreased to 1.6 bar. @@ -3626,6 +3627,21 @@ if you exceeds the total amount of gas available. Good practice demands that div the limit of the gas supply but that an appropriate reserve is kept for unforeseen circumstances. For technical diving, this reserve can be up to 66% of the total available gas. +In addition to calculating the total gas consumption for every cylinder the planner provides one way +of calculating the recommended volume of bottom gas which is needed for safe asscent to the +first deco gas change depth or the surface. This procedure is called the "minimum gas" or "rock bottom" +consideration and it is used by various (but not all) +technical diving organisations. The calculation assumes that in worst case an out of gas (OoG) +situation could occur at the end of the planned bottom time at maximum depth. This OoG event forces +the buddy team the share the gas of one diver and to stay at maximum depth for an additional +time of n minutes (preferences option "problem solving time"). +At the same moment the combined SAC of both divers is increased by a estimated factor (preferences option +"SAC factor") compared to the SAC factor of a single diver under normal conditions. +The result of the minimum gas calculation for the bottom gas is printed to the planner output as an +additional information. No automatic checks are performed based on this result. +Please take care that the feature only gives valid results for simple, rectengular shaped single +level dive profiles. For multi level dives one would need to check every leg of the profile independantly. + Now you can start the detailed time-depth planning of the dive. _Subsurface_ offers an unique graphical interface for doing planning. The mechanics are @@ -3659,7 +3675,7 @@ the surface. These changes can be deleted by right-clicking the gas change and manually creating a gas change by right-clicking on the appropriate waypoint. -A non-zero value in the "CC set point" column of the table of dive planner points +A non-zero value in the "CC setpoint" column of the table of dive planner points indicates a valid setpoint for oxygen partial pressure and that the segment is dived using a closed circuit rebreather (CCR). If the last manually entered segment is a CCR segment, the decompression phase is computed assuming the diver @@ -3668,10 +3684,10 @@ short) is on open circuit (OC, indicated by a zero set-point) the decompression is computed in OC mode and the planner only considers gas changes in OC mode. -Below is an example of a dive plan to 45m using EAN26, followed by an ascent using EAN50 -and using the settings as described above. +Below is an example of a dive plan to 45m using Tx21/35, followed by an ascent using EAN50 +and oxygen and using the settings as described above. -image::images/DivePlanner2_f20.jpg["FIGURE: Planning a dive: setup",align="center"] +image::images/Planner_OC_deco_VPM.jpg["FIGURE: Planning a dive: setup",align="center"] Once the above steps have been completed, save by clicking the _Save_ button towards the top middle of the planner. The saved dive plan will appear @@ -4540,7 +4556,7 @@ according to which spreadsheet program is used. Organize the diving data in the spreadsheet, so the first row contains the names (or titles) of each column and the information for each dive is stored in a single row. _Subsurface_ supports many data items (Dive number, Date, -Time, Duration, Location, GPS, Max Depth, Mean Depth, Buddy, Notes, Weight and Tags). +Time, Duration, Location, GPS, Max. Depth, Mean Depth, Buddy, Notes, Weight and Tags). Organize dive data following a few simple rules: 1. Date: use one of the following formats: yyyy-mm-dd, dd.mm.yyyy, mm/dd/yyyy diff --git a/Documentation/user-manual_es.txt b/Documentation/user-manual_es.txt index 5e61fc5f6..de8551d45 100644 --- a/Documentation/user-manual_es.txt +++ b/Documentation/user-manual_es.txt @@ -3557,7 +3557,7 @@ gestión del oxígeno: ** CCR Options: Estas opciones determinan la gestión del oxigeno en inmersiones con CCRs -*** _Set point por defecto en el Planificador_: Especifica el setpoint de O~2~ +*** _Setpoint por defecto en el Planificador_: Especifica el setpoint de O~2~ de una planificación con CCR. Este dertermina la pO~2~ a mantener durante el buceo. Se pueden añadir cambios a lo largo de la inmersión desde el menú contextual. @@ -3806,7 +3806,7 @@ image::images/PlannerWindow1_f20.jpg["FIGURA: Ventana de inicio del planificador apropiados en la tabla llamada _Puntos del planificador de inmersiones_. La primera línea de la tabla representa la duración y profundidad final de un descenso desde superficie. Los segmentos siguientes describirán la - fase de fondo de la inmersión. La columna _CC set point_ solo es relevante + fase de fondo de la inmersión. La columna _CC setpoint_ solo es relevante para para buceadores en circuito cerrado. El ascenso, normalmente, no se especifica, ya que esto es lo que se supone que el planificador debe calcular. diff --git a/Documentation/user-manual_fr.txt b/Documentation/user-manual_fr.txt index 12d5bba5c..14adde3f0 100644 --- a/Documentation/user-manual_fr.txt +++ b/Documentation/user-manual_fr.txt @@ -3498,7 +3498,7 @@ image::images/Pref4_f23.jpg["FIGURE: Preferences Graph page", align="center"] respect to inert gas loading and the deeper the ceilings are. Gradient factors of 20/60 are considered conservative and values of 70/90 are considered harsh. - In addition decide whether to check the _GFLow at max depth_ box. If checked, GF_Low is used for the + In addition decide whether to check the _GFLow at max. depth_ box. If checked, GF_Low is used for the deepest dive depth and linearly increased up to the GF_High value at the surface. If unchecked, GF_Low is used between the deepest dive depth and the first deco stop, after which the gradient factor linearly increases up to the GF_High value at the surface. For more information see: @@ -3684,7 +3684,7 @@ image::images/PlannerWindow1_f20.jpg["FIGURE: Dive planner startup window", alig values into the table marked _Dive planner points_. The first line of the table represents the duration and the final depth of the descent from the surface. Subsequent segments describe the bottom phase of the dive. The _CC - set point_ column is only relevant for closed circuit divers. The ascent is + setpoint_ column is only relevant for closed circuit divers. The ascent is usually not specified because this is what the planner is supposed to calculate. Add additional segments to the profile by selecting the "+" icon at the top right hand of the table. Segments entered into the _Dive planner @@ -3819,7 +3819,7 @@ maximal descent rate specified in the _Rates_ section of the dive setup. pressure for oxygen needs to be specified for the bottom part of the dive (_bottom po2_) as well as for the decompression part of the dive (_deco po2_). Commonly used values are 1.4 bar for the bottom part of the dive and 1.6 bar for any decompression -stages. Normally, a partial pressure of 1.6 bar is not exceeded. PO2 settings and the depth at which switching to a gas takes place can also be edited in the +stages. Normally, a partial pressure of 1.6 bar is not exceeded. pO₂ settings and the depth at which switching to a gas takes place can also be edited in the _Available Gases_ dialog. Normally the planner decides on switching to a new gas when, during ascent, the partial pressure of the new gas has decreased to 1.6 bar. @@ -3875,7 +3875,7 @@ switches will be suggested during the ascent to the surface. These changes can be deleted by right-clicking the gas change and manually creating a gas change by right-clicking on the appropriate waypoint. -A non-zero value in the "CC set point" column of the table of dive planner +A non-zero value in the "CC setpoint" column of the table of dive planner points indicates a valid setpoint for oxygen partial pressure and that the segment is dived using a closed circuit rebreather (CCR). If the last manually entered segment is a CCR segment, the decompression phase is diff --git a/core/configuredivecomputer.cpp b/core/configuredivecomputer.cpp index d8aefaecc..1e1f9faaf 100644 --- a/core/configuredivecomputer.cpp +++ b/core/configuredivecomputer.cpp @@ -131,8 +131,8 @@ bool ConfigureDiveComputer::saveXMLBackup(QString fileName, DeviceDetails *detai writer.writeTextElement("Dil3", dil3); writer.writeTextElement("Dil4", dil4); writer.writeTextElement("Dil5", dil5); - // - //Add set point values + + //Add setpoint values QString sp1 = QString("%1,%2") .arg(QString::number(details->sp1.sp), QString::number(details->sp1.depth)); diff --git a/core/configuredivecomputerthreads.cpp b/core/configuredivecomputerthreads.cpp index 33edbdce0..9dd83a6ea 100644 --- a/core/configuredivecomputerthreads.cpp +++ b/core/configuredivecomputerthreads.cpp @@ -501,7 +501,7 @@ static dc_status_t read_ostc3_settings(dc_device_t *device, DeviceDetails *m_dev m_deviceDetails->dil4 = dil4; m_deviceDetails->dil5 = dil5; - //Read set point Values + //Read setpoint Values setpoint sp1; setpoint sp2; setpoint sp3; @@ -700,7 +700,7 @@ static dc_status_t write_ostc3_settings(dc_device_t *device, DeviceDetails *m_de return rc; EMIT_PROGRESS(); - //write set point values + //write setpoint values unsigned char sp1Data[2] = { m_deviceDetails->sp1.sp, m_deviceDetails->sp1.depth diff --git a/core/datatrak.c b/core/datatrak.c index d46167cc8..6c9b00764 100644 --- a/core/datatrak.c +++ b/core/datatrak.c @@ -150,7 +150,7 @@ static dtrakheader read_file_header(FILE *archivo) return fileheader; } if (two_bytes_to_int(lector[0], lector[1]) != 0xA100) { - report_error(translate("gettextFromC", "Error: the file does not appear to be a DATATRAK divelog")); + report_error(translate("gettextFromC", "Error: the file does not appear to be a DATATRAK dive log")); free(lector); return fileheader; } diff --git a/core/deco.c b/core/deco.c index 66ed219ee..88585c98d 100644 --- a/core/deco.c +++ b/core/deco.c @@ -43,7 +43,7 @@ struct buehlmann_config { double gf_high; //! gradient factor high (at surface). double gf_low; //! gradient factor low (at bottom/start of deco calculation). double gf_low_position_min; //! gf_low_position below surface_min_shallow. - bool gf_low_at_maxdepth; //! if true, gf_low applies at max depth instead of at deepest ceiling. + bool gf_low_at_maxdepth; //! if true, gf_low applies at max. depth instead of at deepest ceiling. }; struct buehlmann_config buehlmann_config = { diff --git a/core/dive.c b/core/dive.c index 6c438b1a3..3fc348520 100644 --- a/core/dive.c +++ b/core/dive.c @@ -248,11 +248,15 @@ int units_to_sac(double volume) return lrint(volume * 1000); } -unsigned int units_to_depth(double depth) +depth_t units_to_depth(double depth) { - if (get_units()->length == METERS) - return lrint(depth * 1000); - return feet_to_mm(depth); + depth_t internaldepth; + if (get_units()->length == METERS) { + internaldepth.mm = lrint(depth * 1000); + } else { + internaldepth.mm = feet_to_mm(depth); + } + return internaldepth; } double get_depth_units(int mm, int *frac, const char **units) @@ -659,7 +663,7 @@ void finish_sample(struct divecomputer *dc) * new ones. * * Why? Because a dive computer may well actually track the - * max depth and mean depth at finer granularity than the + * max. depth and mean depth at finer granularity than the * samples it stores. So it's possible that the max and mean * have been reported more correctly originally. * @@ -2436,7 +2440,7 @@ static int find_sample_offset(struct divecomputer *a, struct divecomputer *b) * difference? * * So for example, we'd expect different dive computers to give different - * max depth readings. You might have them on different arms, and they + * max. depth readings. You might have them on different arms, and they * have different pressure sensors and possibly different ideas about * water salinity etc. * @@ -3584,11 +3588,11 @@ void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_depth) while (dp) { if (dp->time) { /* Ignore gas indication samples */ - integral += (dp->depth + last_depth) * (dp->time - last_time) / 2; + integral += (dp->depth.mm + last_depth) * (dp->time - last_time) / 2; last_time = dp->time; - last_depth = dp->depth; - if (dp->depth > *max_depth) - *max_depth = dp->depth; + last_depth = dp->depth.mm; + if (dp->depth.mm > *max_depth) + *max_depth = dp->depth.mm; } dp = dp->next; } diff --git a/core/dive.h b/core/dive.h index 981cdc0b9..b8096d50c 100644 --- a/core/dive.h +++ b/core/dive.h @@ -131,7 +131,7 @@ extern double get_temp_units(unsigned int mk, const char **units); extern double get_weight_units(unsigned int grams, int *frac, const char **units); extern double get_vertical_speed_units(unsigned int mms, int *frac, const char **units); -extern unsigned int units_to_depth(double depth); +extern depth_t units_to_depth(double depth); extern int units_to_sac(double volume); /* Volume in mliter of a cylinder at pressure 'p' */ @@ -845,7 +845,7 @@ extern double tissue_tolerance_calc(const struct dive *dive, double pressure); /* this should be converted to use our types */ struct divedatapoint { int time; - int depth; + depth_t depth; int cylinderid; int setpoint; bool entered; diff --git a/core/libdivecomputer.c b/core/libdivecomputer.c index 799846fc2..8ffb49d43 100644 --- a/core/libdivecomputer.c +++ b/core/libdivecomputer.c @@ -709,7 +709,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, struct device_data_t dc_divemode_t divemode; rc = dc_parser_get_field(parser, DC_FIELD_DIVEMODE, 0, &divemode); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - dev_info(devdata, translate("gettextFromC", "Error obtaining divemode")); + dev_info(devdata, translate("gettextFromC", "Error obtaining dive mode")); return rc; } if (rc == DC_STATUS_SUCCESS) diff --git a/core/liquivision.c b/core/liquivision.c index e2e9ff75e..c42086403 100644 --- a/core/liquivision.c +++ b/core/liquivision.c @@ -54,7 +54,7 @@ static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_ break; case 0x0008: // 4 byte time - // 2 byte gas set point 2 + // 2 byte gas setpoint 2 skip = 6; break; case 0x000f: diff --git a/core/planner.c b/core/planner.c index 771ea6acc..eb7352739 100644 --- a/core/planner.c +++ b/core/planner.c @@ -262,7 +262,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas) cylinder_t *cyl; int oldpo2 = 0; int lasttime = 0; - int lastdepth = 0; + depth_t lastdepth = {.mm = 0}; int lastcylid = 0; enum dive_comp_type type = displayed_dive.dc.divemode; @@ -303,7 +303,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas) if (dp->setpoint) type = CCR; int time = dp->time; - int depth = dp->depth; + depth_t depth = dp->depth; if (time == 0) { /* special entries that just inform the algorithm about @@ -329,7 +329,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas) sample = prepare_sample(dc); sample[-1].setpoint.mbar = po2; sample->time.seconds = lasttime + 1; - sample->depth.mm = lastdepth; + sample->depth = lastdepth; sample->manually_entered = dp->entered; sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; if (track_gas && cyl->type.workingpressure.mbar) @@ -344,11 +344,11 @@ static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas) sample[-1].setpoint.mbar = po2; sample->setpoint.mbar = po2; sample->time.seconds = lasttime = time; - sample->depth.mm = lastdepth = depth; + sample->depth = lastdepth = depth; sample->manually_entered = dp->entered; sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; if (track_gas && !sample[-1].setpoint.mbar) { /* Don't track gas usage for CCR legs of dive */ - update_cylinder_pressure(&displayed_dive, sample[-1].depth.mm, depth, time - sample[-1].time.seconds, + update_cylinder_pressure(&displayed_dive, sample[-1].depth.mm, depth.mm, time - sample[-1].time.seconds, dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl, !dp->entered); if (cyl->type.workingpressure.mbar) sample->cylinderpressure.mbar = cyl->end.mbar; @@ -382,7 +382,7 @@ struct divedatapoint *create_dp(int time_incr, int depth, int cylinderid, int po dp = malloc(sizeof(struct divedatapoint)); dp->time = time_incr; - dp->depth = depth; + dp->depth.mm = depth; dp->cylinderid = cylinderid; dp->setpoint = po2; dp->entered = false; @@ -429,24 +429,24 @@ static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, int *gascha bool total_time_zero = true; while (dp) { if (dp->time == 0 && total_time_zero) { - if (dp->depth <= depth) { + if (dp->depth.mm <= depth) { int i = 0; nr++; gaschanges = realloc(gaschanges, nr * sizeof(struct gaschanges)); while (i < nr - 1) { - if (dp->depth < gaschanges[i].depth) { + if (dp->depth.mm < gaschanges[i].depth) { memmove(gaschanges + i + 1, gaschanges + i, (nr - i - 1) * sizeof(struct gaschanges)); break; } i++; } - gaschanges[i].depth = dp->depth; + gaschanges[i].depth = dp->depth.mm; gaschanges[i].gasidx = dp->cylinderid; assert(gaschanges[i].gasidx != -1); } else { /* is there a better mix to start deco? */ - if (dp->depth < best_depth) { - best_depth = dp->depth; + if (dp->depth.mm < best_depth) { + best_depth = dp->depth.mm; *asc_cylinder = dp->cylinderid; } } @@ -546,6 +546,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool bool gaschange_before; bool lastentered = true; struct divedatapoint *nextdp = NULL; + struct divedatapoint *lastbottomdp = NULL; plan_verbatim = prefs.verbatim_plan; plan_display_runtime = prefs.display_runtime; @@ -614,13 +615,13 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool const char *depth_unit; double depthvalue; int decimals; - bool isascent = (dp->depth < lastdepth); + bool isascent = (dp->depth.mm < lastdepth); nextdp = dp->next; if (dp->time == 0) continue; gasmix = dive->cylinder[dp->cylinderid].gasmix; - depthvalue = get_depth_units(dp->depth, &decimals, &depth_unit); + depthvalue = get_depth_units(dp->depth.mm, &decimals, &depth_unit); /* analyze the dive points ahead */ while (nextdp && nextdp->time == 0) nextdp = nextdp->next; @@ -631,14 +632,25 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool /* do we want to skip this leg as it is devoid of anything useful? */ if (!dp->entered && nextdp && - dp->depth != lastdepth && - nextdp->depth != dp->depth && + dp->depth.mm != lastdepth && + nextdp->depth.mm != dp->depth.mm && !gaschange_before && !gaschange_after) continue; - if (dp->time - lasttime < 10 && !(gaschange_after && dp->next && dp->depth != dp->next->depth)) + if (dp->time - lasttime < 10 && !(gaschange_after && dp->next && dp->depth.mm != dp->next->depth.mm)) continue; + /* Store pointer to last entered datapoint for minimum gas calculation */ + /* Do this only if depth is larger than last/2nd last deco stop at ~6m */ + int secondlastdecostop = 0; + if (prefs.units.length == METERS ) { + secondlastdecostop = decostoplevels_metric[2]; + } else { + secondlastdecostop = decostoplevels_imperial[2]; + } + if (dp->entered && !nextdp->entered && dp->depth.mm > secondlastdecostop) + lastbottomdp = dp; + len = strlen(buffer); if (plan_verbatim) { /* When displaying a verbatim plan, we output a waypoint for every gas change. @@ -647,8 +659,8 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool * to determine whether or not to print a segment much simpler than with the * non-verbatim plan. */ - if (dp->depth != lastprintdepth) { - if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth != nextdp->depth)) { + if (dp->depth.mm != lastprintdepth) { + if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth.mm != nextdp->depth.mm)) { if (dp->setpoint) snprintf(temp, sz_temp, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"), decimals, depthvalue, depth_unit, @@ -666,10 +678,10 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp); } - newdepth = dp->depth; + newdepth = dp->depth.mm; lasttime = dp->time; } else { - if ((nextdp && dp->depth != nextdp->depth) || gaschange_after) { + if ((nextdp && dp->depth.mm != nextdp->depth.mm) || gaschange_after) { if (dp->setpoint) snprintf(temp, sz_temp, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"), decimals, depthvalue, depth_unit, @@ -685,7 +697,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool gasname(&gasmix)); len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp); - newdepth = dp->depth; + newdepth = dp->depth.mm; lasttime = dp->time; } } @@ -707,14 +719,14 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool * time has been allowed for a gas switch. */ if (plan_display_transitions || dp->entered || !dp->next || - (nextdp && dp->depth != nextdp->depth) || - (!isascent && gaschange_before && nextdp && dp->depth != nextdp->depth) || + (nextdp && dp->depth.mm != nextdp->depth.mm) || + (!isascent && gaschange_before && nextdp && dp->depth.mm != nextdp->depth.mm) || (gaschange_after && lastentered) || (gaschange_after && !isascent) || - (isascent && gaschange_after && nextdp && dp->depth != nextdp->depth )) { + (isascent && gaschange_after && nextdp && dp->depth.mm != nextdp->depth.mm )) { // Print a symbol to indicate whether segment is an ascent, descent, constant depth (user entered) or deco stop if (isascent) segmentsymbol = "➚"; // up-right arrow for ascent - else if (dp->depth > lastdepth) + else if (dp->depth.mm > lastdepth) segmentsymbol = "➘"; // down-right arrow for descent else if (dp->entered) segmentsymbol = "➙"; // right arrow for entered entered segment at constant depth @@ -737,7 +749,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool /* Normally a gas change is displayed on the stopping segment, so only display a gas change at the end of * an ascent segment if it is not followed by a stop */ - if ((isascent || dp->entered) && gaschange_after && dp->next && nextdp && (dp->depth != nextdp->depth || nextdp->entered)) { + if ((isascent || dp->entered) && gaschange_after && dp->next && nextdp && (dp->depth.mm != nextdp->depth.mm || nextdp->entered)) { if (dp->setpoint) { snprintf(temp, sz_temp, translate("gettextFromC", "(SP = %.1fbar)"), (double) nextdp->setpoint / 1000.0); len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&newgasmix), @@ -765,7 +777,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool len += snprintf(buffer + len, sz_buffer - len, "<td> </td>"); } len += snprintf(buffer + len, sz_buffer - len, "</tr>"); - newdepth = dp->depth; + newdepth = dp->depth.mm; lasttime = dp->time; } } @@ -785,7 +797,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool } } lastprintdepth = newdepth; - lastdepth = dp->depth; + lastdepth = dp->depth.mm; lastsetpoint = dp->setpoint; lastentered = dp->entered; } while ((dp = nextdp) != NULL); @@ -847,10 +859,13 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool snprintf(temp, sz_temp, "%s %.*f|%.*f%s/min):", translate("gettextFromC", "Gas consumption (based on SAC"), sacdecimals, bottomsacvalue, sacdecimals, decosacvalue, sacunit); len += snprintf(buffer + len, sz_buffer - len, "<div>%s<br>", temp); + + /* Print gas consumption: This loop covers all cylinders */ for (int gasidx = 0; gasidx < MAX_CYLINDERS; gasidx++) { - double volume, pressure, deco_volume, deco_pressure; - const char *unit, *pressure_unit; + double volume, pressure, deco_volume, deco_pressure, mingas_volume, mingas_pressure, mingas_depth; + const char *unit, *pressure_unit, *depth_unit; char warning[1000] = ""; + char mingas[1000] = ""; cylinder_t *cyl = &dive->cylinder[gasidx]; if (cylinder_none(cyl)) break; @@ -867,35 +882,73 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool * This only works if we have working pressure for the cylinder * 10bar is a made up number - but it seemed silly to pretend you could breathe cylinder down to 0 */ if (cyl->end.mbar < 10000) - snprintf(warning, sizeof(warning), " — <span style='color: red;'>%s </span> %s", + snprintf(warning, sizeof(warning), "<br> — <span style='color: red;'>%s </span> %s", translate("gettextFromC", "Warning:"), translate("gettextFromC", "this is more gas than available in the specified cylinder!")); else if ((float) cyl->end.mbar * cyl->type.size.mliter / 1000.0 / gas_compressibility_factor(&cyl->gasmix, cyl->end.mbar / 1000.0) < (float) cyl->deco_gas_used.mliter) - snprintf(warning, sizeof(warning), " — <span style='color: red;'>%s </span> %s", + snprintf(warning, sizeof(warning), "<br> — <span style='color: red;'>%s </span> %s", translate("gettextFromC", "Warning:"), translate("gettextFromC", "not enough reserve for gas sharing on ascent!")); - snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s/%.0f%s of %s (%.0f%s/%.0f%s in planned ascent)"), volume, unit, pressure, pressure_unit, gasname(&cyl->gasmix), deco_volume, unit, deco_pressure, pressure_unit); + /* Do and print minimum gas calculation for last bottom gas, but only for OC mode, */ + /* not for recreational mode and if no other warning was set before. */ + else + if (lastbottomdp && gasidx == lastbottomdp->cylinderid + && dive->dc.divemode == OC && decoMode() != RECREATIONAL) { + /* Calculate minimum gas volume. */ + volume_t mingasv; + mingasv.mliter = prefs.problemsolvingtime * prefs.bottomsac * prefs.sacfactor / 100.0 + * depth_to_bar(lastbottomdp->depth.mm, dive) + + cyl->deco_gas_used.mliter * prefs.sacfactor / 100.0; + /* Calculate minimum gas pressure for cyclinder. */ + pressure_t mingasp; + mingasp.mbar = isothermal_pressure(&cyl->gasmix, 1.0, + mingasv.mliter, cyl->type.size.mliter) * 1000; + /* Translate all results into correct units */ + mingas_volume = get_volume_units(mingasv.mliter, NULL, &unit); + mingas_pressure = get_pressure_units(mingasp.mbar, &pressure_unit); + mingas_depth = get_depth_units(lastbottomdp->depth.mm, NULL, &depth_unit); + /* Print it to results */ + if (cyl->start.mbar > mingasp.mbar) snprintf(mingas, sizeof(mingas), + translate("gettextFromC", "<br> — <span style='color: green;'>Minimum gas</span> (based on %.1fxSAC/+%dmin@%.0f%s): %.0f%s/%.0f%s"), + prefs.sacfactor / 100.0, prefs.problemsolvingtime, + mingas_depth, depth_unit, + mingas_volume, unit, + mingas_pressure, pressure_unit); + else snprintf(warning, sizeof(warning), "<br> — <span style='color: red;'>%s </span> %s", + translate("gettextFromC", "Warning:"), + translate("gettextFromC", "required minimum gas for ascent already exceeding start pressure of cylinder!")); + } + /* Print the gas consumption for every cylinder here to temp buffer. */ + snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s/%.0f%s of <span style='color: red;'><b>%s</b></span> (%.0f%s/%.0f%s in planned ascent)"), volume, unit, pressure, pressure_unit, gasname(&cyl->gasmix), deco_volume, unit, deco_pressure, pressure_unit); + } else { - snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s (%.0f%s during planned ascent) of %s"), volume, unit, deco_volume, unit, gasname(&cyl->gasmix)); + snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s (%.0f%s during planned ascent) of <span style='color: red;'><b>%s</b></span>"), + volume, unit, deco_volume, unit, gasname(&cyl->gasmix)); } - len += snprintf(buffer + len, sz_buffer - len, "%s%s<br>", temp, warning); + /* Gas consumption: Now finally print all strings to output */ + len += snprintf(buffer + len, sz_buffer - len, "%s%s%s<br>", temp, warning, mingas); } + + /* Print warnings for pO2 */ dp = diveplan->dp; + bool o2warning_exist = false; if (dive->dc.divemode != CCR) { while (dp) { if (dp->time != 0) { struct gas_pressures pressures; struct gasmix *gasmix = &dive->cylinder[dp->cylinderid].gasmix; - fill_pressures(&pressures, depth_to_atm(dp->depth, dive), gasmix, 0.0, dive->dc.divemode); + fill_pressures(&pressures, depth_to_atm(dp->depth.mm, dive), gasmix, 0.0, dive->dc.divemode); if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { const char *depth_unit; int decimals; - double depth_value = get_depth_units(dp->depth, &decimals, &depth_unit); + double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit); len = strlen(buffer); + if (!o2warning_exist) len += snprintf(buffer + len, sz_buffer - len, "<br>"); + o2warning_exist = true; snprintf(temp, sz_temp, translate("gettextFromC", "high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), pressures.o2, FRACTION(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit); @@ -904,8 +957,10 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool } else if (pressures.o2 < 0.16) { const char *depth_unit; int decimals; - double depth_value = get_depth_units(dp->depth, &decimals, &depth_unit); + double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit); len = strlen(buffer); + if (!o2warning_exist) len += snprintf(buffer + len, sz_buffer - len, "<br>"); + o2warning_exist = true; snprintf(temp, sz_temp, translate("gettextFromC", "low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), pressures.o2, FRACTION(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit); diff --git a/core/pref.h b/core/pref.h index 1360fba14..9122800bb 100644 --- a/core/pref.h +++ b/core/pref.h @@ -105,6 +105,8 @@ struct preferences { int ascratestops; int ascratelast6m; int descrate; + int sacfactor; + int problemsolvingtime; int bottompo2; int decopo2; enum deco_mode display_deco_mode; diff --git a/core/profile.c b/core/profile.c index 1a50c7efd..233f67360 100644 --- a/core/profile.c +++ b/core/profile.c @@ -1350,7 +1350,7 @@ static void plot_string(struct plot_info *pi, struct plot_data *entry, struct me put_format(b, translate("gettextFromC", "Safetystop: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), depthvalue, depth_unit); else - put_format(b, translate("gettextFromC", "Safetystop: unkn time @ %.0f%s\n"), + put_format(b, translate("gettextFromC", "Safetystop: unknown time @ %.0f%s\n"), depthvalue, depth_unit); } else { /* actual deco stop */ @@ -1358,7 +1358,7 @@ static void plot_string(struct plot_info *pi, struct plot_data *entry, struct me put_format(b, translate("gettextFromC", "Deco: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), depthvalue, depth_unit); else - put_format(b, translate("gettextFromC", "Deco: unkn time @ %.0f%s\n"), + put_format(b, translate("gettextFromC", "Deco: unknown time @ %.0f%s\n"), depthvalue, depth_unit); } } else if (entry->in_deco) { @@ -1573,7 +1573,7 @@ void compare_samples(struct plot_data *e1, struct plot_data *e2, char *buf, int int sac = lrint(volume_used / atm * 60 / delta_time); memcpy(buf2, buf, bufsize); volume_value = get_volume_units(sac, &volume_precision, &volume_unit); - snprintf(buf, bufsize, translate("gettextFromC", "%s SAC:%.*f %s"), buf2, volume_precision, volume_value, volume_unit); + snprintf(buf, bufsize, translate("gettextFromC", "%s SAC: %.*f%s"), buf2, volume_precision, volume_value, volume_unit); } } diff --git a/core/save-html.c b/core/save-html.c index 66472fdfc..0b2d8e79a 100644 --- a/core/save-html.c +++ b/core/save-html.c @@ -528,7 +528,7 @@ void export_translation(const char *file_name) write_attribute(b, "Back_to_List", translate("gettextFromC", "Back to list"), ", "); //dive detailed view - write_attribute(b, "Dive_No", translate("gettextFromC", "Dive No."), ", "); + write_attribute(b, "Dive_No", translate("gettextFromC", "Dive #"), ", "); write_attribute(b, "Dive_profile", translate("gettextFromC", "Dive profile"), ", "); write_attribute(b, "Dive_information", translate("gettextFromC", "Dive information"), ", "); write_attribute(b, "Dive_equipment", translate("gettextFromC", "Dive equipment"), ", "); diff --git a/core/subsurface-qt/SettingsObjectWrapper.cpp b/core/subsurface-qt/SettingsObjectWrapper.cpp index dfb486065..f5d2e0f9a 100644 --- a/core/subsurface-qt/SettingsObjectWrapper.cpp +++ b/core/subsurface-qt/SettingsObjectWrapper.cpp @@ -1240,6 +1240,16 @@ int DivePlannerSettings::descrate() const return prefs.descrate; } +int DivePlannerSettings::sacfactor() const +{ + return prefs.sacfactor; +} + +int DivePlannerSettings::problemsolvingtime() const +{ + return prefs.problemsolvingtime; +} + int DivePlannerSettings::bottompo2() const { return prefs.bottompo2; @@ -1441,6 +1451,30 @@ void DivePlannerSettings::setDescrate(int value) emit descrateChanged(value); } +void DivePlannerSettings::setSacFactor(int value) +{ + if (value == prefs.sacfactor) + return; + + QSettings s; + s.beginGroup(group); + s.setValue("sacfactor", value); + prefs.sacfactor = value; + emit sacFactorChanged(value); +} + +void DivePlannerSettings::setProblemSolvingTime(int value) +{ + if (value == prefs.problemsolvingtime) + return; + + QSettings s; + s.beginGroup(group); + s.setValue("problemsolvingtime", value); + prefs.problemsolvingtime = value; + emit problemSolvingTimeChanged(value); +} + void DivePlannerSettings::setBottompo2(int value) { if (value == prefs.bottompo2) @@ -2279,6 +2313,8 @@ void SettingsObjectWrapper::load() GET_INT("ascratestops", ascratestops); GET_INT("ascratelast6m", ascratelast6m); GET_INT("descrate", descrate); + GET_INT("sacfactor", sacfactor); + GET_INT("problemsolvingtime", problemsolvingtime); GET_INT("bottompo2", bottompo2); GET_INT("decopo2", decopo2); GET_INT("bestmixend", bestmixend.mm); @@ -2331,6 +2367,8 @@ void SettingsObjectWrapper::sync() s.setValue("ascratestops", prefs.ascratestops); s.setValue("ascratelast6m", prefs.ascratelast6m); s.setValue("descrate", prefs.descrate); + s.setValue("sacfactor", prefs.sacfactor); + s.setValue("problemsolvingtime", prefs.problemsolvingtime); s.setValue("bottompo2", prefs.bottompo2); s.setValue("decopo2", prefs.decopo2); s.setValue("bestmixend", prefs.bestmixend.mm); diff --git a/core/subsurface-qt/SettingsObjectWrapper.h b/core/subsurface-qt/SettingsObjectWrapper.h index 7fdd10498..1d35dd5d5 100644 --- a/core/subsurface-qt/SettingsObjectWrapper.h +++ b/core/subsurface-qt/SettingsObjectWrapper.h @@ -402,6 +402,8 @@ class DivePlannerSettings : public QObject { Q_PROPERTY(int ascratestops READ ascratestops WRITE setAscratestops NOTIFY ascratestopsChanged) Q_PROPERTY(int ascratelast6m READ ascratelast6m WRITE setAscratelast6m NOTIFY ascratelast6mChanged) Q_PROPERTY(int descrate READ descrate WRITE setDescrate NOTIFY descrateChanged) + Q_PROPERTY(int sacfactor READ sacfactor WRITE setSacFactor NOTIFY sacFactorChanged) + Q_PROPERTY(int problemsolvingtime READ problemsolvingtime WRITE setProblemSolvingTime NOTIFY problemSolvingTimeChanged) Q_PROPERTY(int bottompo2 READ bottompo2 WRITE setBottompo2 NOTIFY bottompo2Changed) Q_PROPERTY(int decopo2 READ decopo2 WRITE setDecopo2 NOTIFY decopo2Changed) Q_PROPERTY(int bestmixend READ bestmixend WRITE setBestmixend NOTIFY bestmixendChanged) @@ -427,6 +429,8 @@ public: int ascratestops() const; int ascratelast6m() const; int descrate() const; + int sacfactor() const; + int problemsolvingtime() const; int bottompo2() const; int decopo2() const; int bestmixend() const; @@ -451,6 +455,8 @@ public slots: void setAscratestops(int value); void setAscratelast6m(int value); void setDescrate(int value); + void setSacFactor(int value); + void setProblemSolvingTime(int value); void setBottompo2(int value); void setDecopo2(int value); void setBestmixend(int value); @@ -475,6 +481,8 @@ signals: void ascratestopsChanged(int value); void ascratelast6mChanged(int value); void descrateChanged(int value); + void sacFactorChanged(int value); + void problemSolvingTimeChanged(int value); void bottompo2Changed(int value); void decopo2Changed(int value); void bestmixendChanged(int value); diff --git a/core/subsurfacestartup.c b/core/subsurfacestartup.c index 1f9518136..89ecbd2fb 100644 --- a/core/subsurfacestartup.c +++ b/core/subsurfacestartup.c @@ -50,6 +50,8 @@ struct preferences default_prefs = { .ascratestops = 6000 / 60, .ascratelast6m = 1000 / 60, .descrate = 18000 / 60, + .sacfactor = 400, + .problemsolvingtime = 4, .bottompo2 = 1400, .decopo2 = 1600, .bestmixend.mm = 30000, diff --git a/core/uemis-downloader.c b/core/uemis-downloader.c index 237a6863f..6bf2dcabf 100644 --- a/core/uemis-downloader.c +++ b/core/uemis-downloader.c @@ -492,9 +492,9 @@ static bool uemis_get_answer(const char *path, char *request, int n_param_in, answer_in_mbuf = true; str_append_with_delim(sb, ""); if (!strcmp(request, "getDivelogs")) - what = translate("gettextFromC", "divelog #"); + what = translate("gettextFromC", "dive log #"); else if (!strcmp(request, "getDivespot")) - what = translate("gettextFromC", "divespot #"); + what = translate("gettextFromC", "dive spot #"); else if (!strcmp(request, "getDive")) what = translate("gettextFromC", "details for #"); } @@ -773,17 +773,17 @@ static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid) return false; } -/* This function is called for both divelog and dive information that we get - * from the SDA (what an insane design, btw). The object_id in the divelog +/* This function is called for both dive log and dive information that we get + * from the SDA (what an insane design, btw). The object_id in the dive log * matches the logfilenr in the dive information (which has its own, often * different object_id) - we use this as the diveid. - * We create the dive when parsing the divelog and then later, when we parse + * We create the dive when parsing the dive log and then later, when we parse * the dive information we locate the already created dive via its diveid. * Most things just get parsed and converted into our internal data structures, * but the dive location API is even more crazy. We just get an id that is an * index into yet another data store that we read out later. In order to * correctly populate the location and gps data from that we need to remember - * the addresses of those fields for every dive that references the divespot. */ + * the addresses of those fields for every dive that references the dive spot. */ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char *inbuf, char **max_divenr, int *for_dive) { char *buf = strdup(inbuf); @@ -805,7 +805,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * bp = buf + 1; tp = next_token(&bp); if (strcmp(tp, "divelog") == 0) { - /* this is a divelog */ + /* this is a dive log */ is_log = true; tp = next_token(&bp); /* is it a valid entry or nothing ? */ @@ -1129,7 +1129,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, stru snprintf(log_file_no_to_find, sizeof(log_file_no_to_find), "logfilenr{int{%d", dive->dc.diveid); #if UEMIS_DEBUG & 2 - fprintf(debugfile, "Looking for dive details to go with divelog id %d\n", dive->dc.diveid); + fprintf(debugfile, "Looking for dive details to go with dive log id %d\n", dive->dc.diveid); #endif while (!found) { if (import_thread_cancelled) @@ -1142,15 +1142,15 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, stru #endif *uemis_mem_status = get_memory(data->download_table, UEMIS_CHECK_SINGLE_DIVE); if (*uemis_mem_status == UEMIS_MEM_OK) { - /* if the memory isn's completely full we can try to read more divelog vs. dive details + /* if the memory isn's completely full we can try to read more dive log vs. dive details * UEMIS_MEM_CRITICAL means not enough space for a full round but the dive details - * and the divespots should fit into the UEMIS memory + * and the dive spots should fit into the UEMIS memory * The match we do here is to map the object_id to the logfilenr, we do this - * by iterating through the last set of loaded divelogs and then find the corresponding + * by iterating through the last set of loaded dive logs and then find the corresponding * dive with the matching logfilenr */ if (mbuf) { if (strstr(mbuf, log_file_no_to_find)) { - /* we found the logfilenr that matches our object_id from the divelog we were looking for + /* we found the logfilenr that matches our object_id from the dive log we were looking for * we mark the search successful even if the dive has been deleted. */ found = true; if (strstr(mbuf, "deleted{bool{true") == NULL) { @@ -1160,7 +1160,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, stru * UEMIS unfortunately deletes dives by deleting the dive details and not the logs. */ #if UEMIS_DEBUG & 2 d_time = get_dive_date_c_string(dive->when); - fprintf(debugfile, "Matching divelog id %d from %s with dive details %d\n", dive->dc.diveid, d_time, dive_to_read); + fprintf(debugfile, "Matching dive log id %d from %s with dive details %d\n", dive->dc.diveid, d_time, dive_to_read); #endif int divespot_id = uemis_get_divespot_id_by_diveid(dive->dc.diveid); if (divespot_id >= 0) @@ -1170,7 +1170,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, stru /* in this case we found a deleted file, so let's increment */ #if UEMIS_DEBUG & 2 d_time = get_dive_date_c_string(dive->when); - fprintf(debugfile, "TRY matching divelog id %d from %s with dive details %d but details are deleted\n", dive->dc.diveid, d_time, dive_to_read); + fprintf(debugfile, "TRY matching dive log id %d from %s with dive details %d but details are deleted\n", dive->dc.diveid, d_time, dive_to_read); #endif deleted_files++; max_deleted_seen = dive_to_read; @@ -1200,7 +1200,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, stru break; dive_to_read++; } else { - /* At this point the memory of the UEMIS is full, let's cleanup all divelog files were + /* At this point the memory of the UEMIS is full, let's cleanup all dive log files were * we could not match the details to. */ do_delete_dives(data->download_table, idx); return false; @@ -1281,7 +1281,7 @@ const char *do_uemis_import(device_data_t *data) realmbuf = strchr(mbuf, '{'); if (success && realmbuf && uemis_mem_status != UEMIS_MEM_FULL) { #if UEMIS_DEBUG & 16 - do_dump_buffer_to_file(realmbuf, "Divelogs"); + do_dump_buffer_to_file(realmbuf, "Dive logs"); #endif /* process the buffer we have assembled */ if (!process_raw_buffer(data, deviceidnr, realmbuf, &newmax, NULL)) { @@ -1307,7 +1307,7 @@ const char *do_uemis_import(device_data_t *data) fprintf(debugfile, "d_u_i after download and parse start %d end %d newmax %s progress %4.2f\n", start, end, newmax, progress_bar_fraction); #endif /* The way this works is that I am reading the current dive from what has been loaded during the getDiveLogs call to the UEMIS. - * As the object_id of the divelog entry and the object_id of the dive details are not necessarily the same, the match needs + * As the object_id of the dive log entry and the object_id of the dive details are not necessarily the same, the match needs * to happen based on the logfilenr. * What the following part does is to optimize the mapping by using * dive_to_read = the dive details entry that need to be read using the object_id @@ -1352,9 +1352,9 @@ const char *do_uemis_import(device_data_t *data) } #endif } else { - /* some of the loading from the UEMIS failed at the divelog level - * if the memory status = full, we can't even load the divespots and/or buddies. - * The loaded block of divelogs is useless and all new loaded divelogs need to + /* some of the loading from the UEMIS failed at the dive log level + * if the memory status = full, we can't even load the dive spots and/or buddies. + * The loaded block of dive logs is useless and all new loaded dive logs need to * be deleted from the download_table. */ if (uemis_mem_status == UEMIS_MEM_FULL) diff --git a/core/units.h b/core/units.h index 30d52fbf8..f2557ed22 100644 --- a/core/units.h +++ b/core/units.h @@ -53,7 +53,7 @@ extern "C" { * Also strive to use units that can not possibly be mistaken for a * valid value in a "normal" system without conversion. If the max * depth of a dive is '20000', you probably didn't convert from mm on - * output, or if the max depth gets reported as "0.2ft" it was either + * output, or if the max. depth gets reported as "0.2ft" it was either * a really boring dive, or there was some missing input conversion, * and a 60-ft dive got recorded as 60mm. * diff --git a/desktop-widgets/configuredivecomputerdialog.cpp b/desktop-widgets/configuredivecomputerdialog.cpp index 8b3a9a5e9..4ee61bb99 100644 --- a/desktop-widgets/configuredivecomputerdialog.cpp +++ b/desktop-widgets/configuredivecomputerdialog.cpp @@ -576,7 +576,7 @@ void ConfigureDiveComputerDialog::populateDeviceDetailsOSTC3() deviceDetails->dil4 = dil4; deviceDetails->dil5 = dil5; - //set set point details + //set setpoint details setpoint sp1; setpoint sp2; setpoint sp3; @@ -709,7 +709,7 @@ void ConfigureDiveComputerDialog::populateDeviceDetailsOSTC() deviceDetails->dil4 = dil4; deviceDetails->dil5 = dil5; - //set set point details + //set setpoint details setpoint sp1; setpoint sp2; setpoint sp3; @@ -750,7 +750,7 @@ void ConfigureDiveComputerDialog::populateDeviceDetailsSuuntoVyper() deviceDetails->lightEnabled = ui.lightCheckBox->isChecked(); deviceDetails->light = ui.lightSpinBox->value(); deviceDetails->alarmDepthEnabled = ui.alarmDepthCheckBox->isChecked(); - deviceDetails->alarmDepth = units_to_depth(ui.alarmDepthDoubleSpinBox->value()); + deviceDetails->alarmDepth = units_to_depth(ui.alarmDepthDoubleSpinBox->value()).mm; deviceDetails->alarmTimeEnabled = ui.alarmTimeCheckBox->isChecked(); deviceDetails->alarmTime = ui.alarmTimeSpinBox->value(); } @@ -945,23 +945,23 @@ void ConfigureDiveComputerDialog::reloadValuesOSTC3() ui.ostc3DilTable->setItem(4, 3, new QTableWidgetItem(QString::number(deviceDetails->dil5.type))); ui.ostc3DilTable->setItem(4, 4, new QTableWidgetItem(QString::number(deviceDetails->dil5.depth))); - //load set point 1 values + //load setpoint 1 values ui.ostc3SetPointTable->setItem(0, 1, new QTableWidgetItem(QString::number(deviceDetails->sp1.sp))); ui.ostc3SetPointTable->setItem(0, 2, new QTableWidgetItem(QString::number(deviceDetails->sp1.depth))); - //load set point 2 values + //load setpoint 2 values ui.ostc3SetPointTable->setItem(1, 1, new QTableWidgetItem(QString::number(deviceDetails->sp2.sp))); ui.ostc3SetPointTable->setItem(1, 2, new QTableWidgetItem(QString::number(deviceDetails->sp2.depth))); - //load set point 3 values + //load setpoint 3 values ui.ostc3SetPointTable->setItem(2, 1, new QTableWidgetItem(QString::number(deviceDetails->sp3.sp))); ui.ostc3SetPointTable->setItem(2, 2, new QTableWidgetItem(QString::number(deviceDetails->sp3.depth))); - //load set point 4 values + //load setpoint 4 values ui.ostc3SetPointTable->setItem(3, 1, new QTableWidgetItem(QString::number(deviceDetails->sp4.sp))); ui.ostc3SetPointTable->setItem(3, 2, new QTableWidgetItem(QString::number(deviceDetails->sp4.depth))); - //load set point 5 values + //load setpoint 5 values ui.ostc3SetPointTable->setItem(4, 1, new QTableWidgetItem(QString::number(deviceDetails->sp5.sp))); ui.ostc3SetPointTable->setItem(4, 2, new QTableWidgetItem(QString::number(deviceDetails->sp5.depth))); } @@ -1072,23 +1072,23 @@ setNumberOfDives ui.ostcDilTable->setItem(4, 3, new QTableWidgetItem(QString::number(deviceDetails->dil5.type))); ui.ostcDilTable->setItem(4, 4, new QTableWidgetItem(QString::number(deviceDetails->dil5.depth))); - //load set point 1 values + //load setpoint 1 values ui.ostcSetPointTable->setItem(0, 1, new QTableWidgetItem(QString::number(deviceDetails->sp1.sp))); ui.ostcSetPointTable->setItem(0, 2, new QTableWidgetItem(QString::number(deviceDetails->sp1.depth))); - //load set point 2 values + //load setpoint 2 values ui.ostcSetPointTable->setItem(1, 1, new QTableWidgetItem(QString::number(deviceDetails->sp2.sp))); ui.ostcSetPointTable->setItem(1, 2, new QTableWidgetItem(QString::number(deviceDetails->sp2.depth))); - //load set point 3 values + //load setpoint 3 values ui.ostcSetPointTable->setItem(2, 1, new QTableWidgetItem(QString::number(deviceDetails->sp3.sp))); ui.ostcSetPointTable->setItem(2, 2, new QTableWidgetItem(QString::number(deviceDetails->sp3.depth))); - //load set point 4 values + //load setpoint 4 values ui.ostcSetPointTable->setItem(3, 1, new QTableWidgetItem(QString::number(deviceDetails->sp4.sp))); ui.ostcSetPointTable->setItem(3, 2, new QTableWidgetItem(QString::number(deviceDetails->sp4.depth))); - //load set point 5 values + //load setpoint 5 values ui.ostcSetPointTable->setItem(4, 1, new QTableWidgetItem(QString::number(deviceDetails->sp5.sp))); ui.ostcSetPointTable->setItem(4, 2, new QTableWidgetItem(QString::number(deviceDetails->sp5.depth))); } diff --git a/desktop-widgets/configuredivecomputerdialog.ui b/desktop-widgets/configuredivecomputerdialog.ui index 4a4c3521f..3b78387df 100644 --- a/desktop-widgets/configuredivecomputerdialog.ui +++ b/desktop-widgets/configuredivecomputerdialog.ui @@ -1693,7 +1693,7 @@ <item row="1" column="0"> <widget class="QLabel" name="label_18"> <property name="text"> - <string>Max depth</string> + <string>Max. depth</string> </property> </widget> </item> diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp index 2c87c1188..5e65c8944 100644 --- a/desktop-widgets/divelogimportdialog.cpp +++ b/desktop-widgets/divelogimportdialog.cpp @@ -480,9 +480,9 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy) QString units = "Metric"; dl7 = true; while ((firstLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) { - /* DL7 actually defines individual units (e.g. depth, temp, pressure, etc.) - * and there are quite a few other options as well, but let's use metric - * unless depth unit is clearly Imperial. */ + /* DL7 actually defines individual units (e.g. depth, temperature, + * pressure, etc.) and there are quite a few other options as well, + * but let's use metric unless depth unit is clearly Imperial. */ if (firstLine.contains("ThFt")) { units = "Imperial"; diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index e4ea71445..4fc38cd2a 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -113,6 +113,7 @@ DivePlannerWidget::DivePlannerWidget(QWidget *parent, Qt::WindowFlags f) : QWidg plannerModel->setRecalc(true); ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::GAS, new AirTypesDelegate(this)); ui.cylinderTableWidget->setTitle(tr("Available gases")); + ui.cylinderTableWidget->setBtnToolTip(tr("Add cylinder")); ui.cylinderTableWidget->setModel(CylindersModel::instance()); QTableView *view = ui.cylinderTableWidget->view(); view->setColumnHidden(CylindersModel::START, true); @@ -217,7 +218,7 @@ void DivePlannerWidget::atmPressureChanged(const int pressure) void DivePlannerWidget::heightChanged(const int height) { - int pressure = (int) (1013.0 * exp(- (double) units_to_depth((double) height) / 7800000.0)); + int pressure = (int) (1013.0 * exp(- (double) units_to_depth((double) height).mm / 7800000.0)); ui.ATMPressure->blockSignals(true); ui.ATMPressure->setValue(pressure); ui.ATMPressure->blockSignals(false); @@ -253,6 +254,14 @@ void PlannerSettingsWidget::disableDecoElements(int mode) ui.vpmb_conservatism->setDisabled(true); ui.switch_at_req_stop->setDisabled(true); ui.min_switch_duration->setDisabled(true); + ui.sacfactor->setDisabled(true); + ui.problemsolvingtime->setDisabled(true); + ui.sacfactor->blockSignals(true); + ui.problemsolvingtime->blockSignals(true); + ui.sacfactor->setValue(2.0); + ui.problemsolvingtime->setValue(0); + ui.sacfactor->blockSignals(false); + ui.problemsolvingtime->blockSignals(false); } else if (mode == VPMB) { ui.gflow->setDisabled(true); @@ -265,6 +274,10 @@ void PlannerSettingsWidget::disableDecoElements(int mode) ui.vpmb_conservatism->setDisabled(false); ui.switch_at_req_stop->setDisabled(false); ui.min_switch_duration->setDisabled(false); + ui.sacfactor->setDisabled(false); + ui.problemsolvingtime->setDisabled(false); + ui.sacfactor->setValue(prefs.sacfactor / 100.0); + ui.problemsolvingtime->setValue(prefs.problemsolvingtime); } else if (mode == BUEHLMANN) { ui.gflow->setDisabled(false); @@ -277,6 +290,10 @@ void PlannerSettingsWidget::disableDecoElements(int mode) ui.vpmb_conservatism->setDisabled(true); ui.switch_at_req_stop->setDisabled(false); ui.min_switch_duration->setDisabled(false); + ui.sacfactor->setDisabled(false); + ui.problemsolvingtime->setDisabled(false); + ui.sacfactor->setValue(prefs.sacfactor / 100.0); + ui.problemsolvingtime->setValue(prefs.problemsolvingtime); } } @@ -300,6 +317,8 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) ui.display_runtime->setChecked(prefs.display_runtime); ui.display_transitions->setChecked(prefs.display_transitions); ui.safetystop->setChecked(prefs.safetystop); + ui.sacfactor->setValue(prefs.sacfactor / 100.0); + ui.problemsolvingtime->setValue(prefs.problemsolvingtime); ui.bottompo2->setValue(prefs.bottompo2 / 1000.0); ui.decopo2->setValue(prefs.decopo2 / 1000.0); ui.backgasBreaks->setChecked(prefs.doo2breaks); @@ -362,6 +381,8 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) connect(ui.descRate, SIGNAL(valueChanged(int)), this, SLOT(setDescRate(int))); connect(ui.ascRateStops, SIGNAL(valueChanged(int)), this, SLOT(setAscRateStops(int))); connect(ui.ascRateLast6m, SIGNAL(valueChanged(int)), this, SLOT(setAscRateLast6m(int))); + connect(ui.sacfactor, SIGNAL(valueChanged(double)), this, SLOT(sacFactorChanged(double))); + connect(ui.problemsolvingtime, SIGNAL(valueChanged(int)), this, SLOT(problemSolvingTimeChanged(int))); connect(ui.bottompo2, SIGNAL(valueChanged(double)), this, SLOT(setBottomPo2(double))); connect(ui.decopo2, SIGNAL(valueChanged(double)), this, SLOT(setDecoPo2(double))); connect(ui.bestmixEND, SIGNAL(valueChanged(int)), this, SLOT(setBestmixEND(int))); @@ -480,6 +501,16 @@ void PlannerSettingsWidget::setDescRate(int rate) SettingsObjectWrapper::instance()->planner_settings->setDescrate(rate * UNIT_FACTOR); } +void PlannerSettingsWidget::sacFactorChanged(const double factor) +{ + plannerModel->setSacFactor(factor); +} + +void PlannerSettingsWidget::problemSolvingTimeChanged(const int minutes) +{ + plannerModel->setProblemSolvingTime(minutes); +} + void PlannerSettingsWidget::setBottomPo2(double po2) { SettingsObjectWrapper::instance()->planner_settings->setBottompo2((int) (po2 * 1000.0)); @@ -495,7 +526,7 @@ void PlannerSettingsWidget::setDecoPo2(double po2) void PlannerSettingsWidget::setBestmixEND(int depth) { - SettingsObjectWrapper::instance()->planner_settings->setBestmixend(units_to_depth(depth)); + SettingsObjectWrapper::instance()->planner_settings->setBestmixend(units_to_depth(depth).mm); } void PlannerSettingsWidget::setBackgasBreaks(bool dobreaks) diff --git a/desktop-widgets/diveplanner.h b/desktop-widgets/diveplanner.h index 91501ceb7..f0cae3e5b 100644 --- a/desktop-widgets/diveplanner.h +++ b/desktop-widgets/diveplanner.h @@ -78,6 +78,8 @@ slots: void setAscRateStops(int rate); void setAscRateLast6m(int rate); void setDescRate(int rate); + void sacFactorChanged(const double factor); + void problemSolvingTimeChanged(const int min); void setBottomPo2(double po2); void setDecoPo2(double po2); void setBestmixEND(int depth); diff --git a/desktop-widgets/maintab.cpp b/desktop-widgets/maintab.cpp index b5a156d68..7a9ac5d2b 100644 --- a/desktop-widgets/maintab.cpp +++ b/desktop-widgets/maintab.cpp @@ -737,7 +737,7 @@ void MainTab::updateDiveInfo(bool clear) if (he_tot.mliter && o2_tot.mliter) gasUsedString.append(tr(" and ")); if (o2_tot.mliter) - gasUsedString.append(QString("O2: %2\n").arg(get_volume_string(o2_tot, true))); + gasUsedString.append(QString("O₂: %2\n").arg(get_volume_string(o2_tot, true))); } ui.gasConsumption->setText(gasUsedString); if(ui.locationTags->text().isEmpty()) @@ -1086,7 +1086,7 @@ void MainTab::acceptChanges() } if (editMode == ADD || editMode == MANUALLY_ADDED_DIVE) { // we just added or edited the dive, let fixup_dive() make - // sure we get the max depth right + // sure we get the max. depth right current_dive->maxdepth.mm = current_dc->maxdepth.mm = 0; fixup_dive(current_dive); set_dive_nr_for_current_dive(); diff --git a/desktop-widgets/plannerSettings.ui b/desktop-widgets/plannerSettings.ui index c6e16e54a..90331bdf6 100644 --- a/desktop-widgets/plannerSettings.ui +++ b/desktop-widgets/plannerSettings.ui @@ -402,7 +402,7 @@ </item> <item row="19" column="1"> <widget class="QComboBox" name="rebreathermode"> - <property name="currentText" stdset="0"> + <property name="currentText"> <string/> </property> <property name="maxVisibleItems"> @@ -530,7 +530,40 @@ <property name="spacing"> <number>2</number> </property> - <item row="5" column="0"> + <item row="1" column="1"> + <widget class="QDoubleSpinBox" name="decoStopSAC"> + <property name="suffix"> + <string>ℓ/min</string> + </property> + <property name="decimals"> + <number>0</number> + </property> + <property name="maximum"> + <double>99.000000000000000</double> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="decosac"> + <property name="text"> + <string>Deco SAC</string> + </property> + </widget> + </item> + <item row="8" column="0"> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="7" column="0"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -543,7 +576,7 @@ </property> </spacer> </item> - <item row="2" column="1"> + <item row="4" column="1"> <widget class="QDoubleSpinBox" name="bottompo2"> <property name="suffix"> <string>bar</string> @@ -572,7 +605,7 @@ </property> </widget> </item> - <item row="3" column="1"> + <item row="5" column="1"> <widget class="QDoubleSpinBox" name="decopo2"> <property name="suffix"> <string>bar</string> @@ -588,8 +621,11 @@ </property> </widget> </item> - <item row="4" column="1"> + <item row="6" column="1"> <widget class="QSpinBox" name="bestmixEND"> + <property name="toolTip"> + <string>Used to calculate best mix. Select best mix depth in 'Available gases' table by entering gas depth, followed by "B" (best trimix mix) or "BN" (best nitrox mix)</string> + </property> <property name="suffix"> <string>m</string> </property> @@ -602,9 +638,6 @@ <property name="value"> <number>30</number> </property> - <property name="toolTip"> - <string>Used to calculate best mix. Select best mix depth in 'Available gases' table by entering gas depth, followed by "B" (best trimix mix) or "BN" (best nitrox mix)</string> - </property> </widget> </item> <item row="0" column="0"> @@ -614,21 +647,21 @@ </property> </widget> </item> - <item row="2" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="bottompO2"> <property name="text"> <string>Bottom pO₂</string> </property> </widget> </item> - <item row="4" column="0"> + <item row="6" column="0"> <widget class="QLabel" name="bestEND"> <property name="text"> <string>Best mix END</string> </property> </widget> </item> - <item row="6" column="0" colspan="2"> + <item row="8" column="0" colspan="2"> <widget class="QGroupBox" name="Notes"> <property name="title"> <string>Notes</string> @@ -692,45 +725,67 @@ </layout> </widget> </item> - <item row="1" column="1"> - <widget class="QDoubleSpinBox" name="decoStopSAC"> - <property name="suffix"> - <string>ℓ/min</string> + <item row="5" column="0"> + <widget class="QLabel" name="decopO2"> + <property name="text"> + <string>Deco pO₂</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="sacFactor"> + <property name="text"> + <string>SAC factor</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QDoubleSpinBox" name="sacfactor"> + <property name="toolTip"> + <string>Used to calculate minimum gas. Consider two divers with possibly increased SAC after OoG event.</string> </property> <property name="decimals"> - <number>0</number> + <number>1</number> + </property> + <property name="minimum"> + <double>2.000000000000000</double> </property> <property name="maximum"> - <double>99.000000000000000</double> + <double>10.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + <property name="value"> + <double>4.000000000000000</double> </property> </widget> </item> <item row="3" column="0"> - <widget class="QLabel" name="decopO2"> + <widget class="QLabel" name="problemSolvingTime"> <property name="text"> - <string>Deco pO₂</string> + <string>Problem solving time</string> </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="decosac"> - <property name="text"> - <string>Deco SAC</string> + <item row="3" column="1"> + <widget class="QSpinBox" name="problemsolvingtime"> + <property name="toolTip"> + <string>Used to calculate minimum gas. Additional time at max. depth after OoG event.</string> </property> - </widget> - </item> - <item row="6" column="0"> - <spacer name="verticalSpacer_4"> - <property name="orientation"> - <enum>Qt::Vertical</enum> + <property name="suffix"> + <string>min</string> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>20</height> - </size> + <property name="prefix"> + <string/> </property> - </spacer> + <property name="maximum"> + <number>10</number> + </property> + <property name="value"> + <number>4</number> + </property> + </widget> </item> </layout> </widget> @@ -755,6 +810,7 @@ <tabstop>gflow</tabstop> <tabstop>gfhigh</tabstop> <tabstop>vpmb_deco</tabstop> + <tabstop>vpmb_conservatism</tabstop> <tabstop>drop_stone_mode</tabstop> <tabstop>lastStop</tabstop> <tabstop>backgasBreaks</tabstop> @@ -763,6 +819,8 @@ <tabstop>rebreathermode</tabstop> <tabstop>bottomSAC</tabstop> <tabstop>decoStopSAC</tabstop> + <tabstop>sacfactor</tabstop> + <tabstop>problemsolvingtime</tabstop> <tabstop>bottompo2</tabstop> <tabstop>decopo2</tabstop> <tabstop>bestmixEND</tabstop> diff --git a/desktop-widgets/preferences/preferences_graph.ui b/desktop-widgets/preferences/preferences_graph.ui index 3e175b985..5deacc1fd 100644 --- a/desktop-widgets/preferences/preferences_graph.ui +++ b/desktop-widgets/preferences/preferences_graph.ui @@ -253,7 +253,7 @@ <item row="4" column="2" colspan="2"> <widget class="QCheckBox" name="gf_low_at_maxdepth"> <property name="text"> - <string>GFLow at max depth</string> + <string>GFLow at max. depth</string> </property> </widget> </item> diff --git a/packaging/windows/mxe-based-build.sh b/packaging/windows/mxe-based-build.sh index 8087c3b14..06db171ac 100755 --- a/packaging/windows/mxe-based-build.sh +++ b/packaging/windows/mxe-based-build.sh @@ -257,17 +257,33 @@ echo "Starting Subsurface Build" rm -rf subsurface # first copy the Qt plugins in place -mkdir -p subsurface/staging/plugins -cd subsurface/staging/plugins -cp -a "$BASEDIR"/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/iconengines . -cp -a "$BASEDIR"/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/imageformats . -cp -a "$BASEDIR"/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/platforms . -cp -a "$BASEDIR"/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/printsupport . +QT_PLUGIN_DIRECTORIES="$BASEDIR/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/iconengines \ +$BASEDIR/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/imageformats \ +$BASEDIR/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/platforms \ +$BASEDIR/mxe/usr/i686-w64-mingw32.shared/qt5/plugins/printsupport" # for some reason we aren't installing libssrfmarblewidget.dll and # Qt5Xml.dll # I need to figure out why and fix that, but for now just manually copy that as well -cp "$BASEDIR"/mxe/usr/i686-w64-mingw32.shared/lib/libssrfmarblewidget.dll "$BUILDDIR"/subsurface/staging -cp "$BASEDIR"/mxe/usr/i686-w64-mingw32.shared/qt5/bin/Qt5Xml.dll "$BUILDDIR"/subsurface/staging +EXTRA_MANUAL_DEPENDENCIES="$BASEDIR/mxe/usr/i686-w64-mingw32.shared/lib/libssrfmarblewidget.dll \ +$BASEDIR/mxe/usr/i686-w64-mingw32.shared/qt5/bin/Qt5Xml.dll" + +STAGING_DIR=$BUILDDIR/subsurface/staging +STAGING_TESTS_DIR=$BUILDDIR/subsurface/staging_tests + +mkdir -p $STAGING_DIR/plugins +mkdir -p $STAGING_TESTS_DIR + +for d in $QT_PLUGIN_DIRECTORIES +do + cp -a $d $STAGING_DIR/plugins + cp -a $d $STAGING_TESTS_DIR +done + +for f in $EXTRA_MANUAL_DEPENDENCIES +do + cp $f $STAGING_DIR + cp $f $STAGING_TESTS_DIR +done cd "$BUILDDIR"/subsurface diff --git a/printing_templates/Flowlayout.html b/printing_templates/Flowlayout.html index eaa7289f8..3409b04a9 100644 --- a/printing_templates/Flowlayout.html +++ b/printing_templates/Flowlayout.html @@ -129,7 +129,7 @@ </tr> <tr> <td class="fieldTitle"> - <h1> Max depth </h1> + <h1> Max. depth </h1> </td> <td class="fieldData"> <p> {{ dive.depth }} </p> @@ -147,7 +147,7 @@ <table class="table_class"> <tbody><tr> <td class="fieldTitle"> - <h1> Time. </h1> + <h1> Time </h1> </td> <td class="fieldData"> <p> {{ dive.time }} </p> diff --git a/printing_templates/One Dive.html b/printing_templates/One Dive.html index 8cfc12799..7f7945396 100644 --- a/printing_templates/One Dive.html +++ b/printing_templates/One Dive.html @@ -142,7 +142,7 @@ </tr> <tr> <td class="fieldTitle"> - <h1> Max depth </h1> + <h1> Max. depth </h1> </td> <td class="fieldData"> <p> {{ dive.depth }} </p> diff --git a/printing_templates/Six Dives.html b/printing_templates/Six Dives.html index 1d4bb5b56..0fe48a628 100644 --- a/printing_templates/Six Dives.html +++ b/printing_templates/Six Dives.html @@ -136,7 +136,7 @@ <tr> <td> <h1> Dive # {{ dive.number }} - {{ dive.date }} {{ dive.time }}</h1> - <p style="float:right;"> Max depth: {{ dive.depth }} </p> + <p style="float:right;"> Max. depth: {{ dive.depth }} </p> </td> </tr> <tr> diff --git a/printing_templates/Two Dives.html b/printing_templates/Two Dives.html index e82781758..02fb436c1 100644 --- a/printing_templates/Two Dives.html +++ b/printing_templates/Two Dives.html @@ -151,7 +151,7 @@ </tr> <tr> <td class="fieldTitle"> - <h1> Max depth </h1> + <h1> Max. depth </h1> </td> <td class="fieldData"> <p> {{ dive.depth }} </p> diff --git a/printing_templates/statistics/Default.html b/printing_templates/statistics/Default.html index 4ec02766a..28991c09a 100644 --- a/printing_templates/statistics/Default.html +++ b/printing_templates/statistics/Default.html @@ -59,17 +59,17 @@ <td>Year</td> <td>Dives</td> <td>Total Time</td> - <td>Avg Time</td> + <td>Avg. Time</td> <td>Shortest Time</td> <td>Longest Time</td> - <td>Avg Depth</td> - <td>Min Depth</td> - <td>Max Depth</td> - <td>Avg SAC</td> - <td>Min SAC</td> - <td>Max SAC</td> - <td>Min Temp</td> - <td>Max Temp</td> + <td>Avg. Depth</td> + <td>Min. Depth</td> + <td>Max. Depth</td> + <td>Avg. SAC</td> + <td>Min. SAC</td> + <td>Max. SAC</td> + <td>Min. Temp</td> + <td>Max. Temp</td> </tr> {% block main_rows %} {% for year in years %} diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index 5af188c2d..b5a532e7a 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -165,7 +165,7 @@ void DiveEventItem::setupToolTipString() } } else if (type == SAMPLE_EVENT_PO2 && same_string(internalEvent->name, "SP change")) { // this is a bad idea - we are abusing an existing event type that is supposed to - // warn of high or low pO₂ and are turning it into a set point change event + // warn of high or low pO₂ and are turning it into a setpoint change event name += ":\n" + tr("Manual switch to OC"); } else { name += internalEvent->flags & SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") : diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index d4899232b..5a48e3a5f 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -639,7 +639,7 @@ void ProfileWidget2::plotDive(struct dive *d, bool force) if (shouldCalculateMaxTime || newMaxtime > maxtime) maxtime = newMaxtime; - /* Only update the max depth if it's bigger than the current ones + /* Only update the max. depth if it's bigger than the current ones * when we are dragging the handler to plan / add dive. * otherwhise, update normally. */ @@ -1736,13 +1736,13 @@ void ProfileWidget2::repositionDiveHandlers() continue; DiveHandler *h = handles.at(i); h->setVisible(datapoint.entered); - h->setPos(timeAxis->posAtValue(datapoint.time), profileYAxis->posAtValue(datapoint.depth)); + h->setPos(timeAxis->posAtValue(datapoint.time), profileYAxis->posAtValue(datapoint.depth.mm)); QPointF p1; if (i == 0) { if (prefs.drop_stone_mode) // place the text on the straight line from the drop to stone position - p1 = QPointF(timeAxis->posAtValue(datapoint.depth / prefs.descrate), - profileYAxis->posAtValue(datapoint.depth)); + p1 = QPointF(timeAxis->posAtValue(datapoint.depth.mm / prefs.descrate), + profileYAxis->posAtValue(datapoint.depth.mm)); else // place the text on the straight line from the origin to the first position p1 = QPointF(timeAxis->posAtValue(0), profileYAxis->posAtValue(0)); @@ -1794,7 +1794,7 @@ void ProfileWidget2::recreatePlannedDive() return; divedatapoint data = plannerModel->at(index); - data.depth = lrint(profileYAxis->valueAt(activeHandler->pos()) / M_OR_FT(1, 1)) * M_OR_FT(1, 1); + data.depth.mm = lrint(profileYAxis->valueAt(activeHandler->pos()) / M_OR_FT(1, 1)) * M_OR_FT(1, 1); data.time = lrint(timeAxis->valueAt(activeHandler->pos())); plannerModel->editStop(index, data); @@ -1810,10 +1810,10 @@ void ProfileWidget2::keyDownAction() if (DiveHandler *handler = qgraphicsitem_cast<DiveHandler *>(i)) { int row = handles.indexOf(handler); divedatapoint dp = plannerModel->at(row); - if (dp.depth >= profileYAxis->maximum()) + if (dp.depth.mm >= profileYAxis->maximum()) continue; - dp.depth += M_OR_FT(1, 5); + dp.depth.mm += M_OR_FT(1, 5); plannerModel->editStop(row, dp); } } @@ -1830,10 +1830,10 @@ void ProfileWidget2::keyUpAction() int row = handles.indexOf(handler); divedatapoint dp = plannerModel->at(row); - if (dp.depth <= 0) + if (dp.depth.mm <= 0) continue; - dp.depth -= M_OR_FT(1, 5); + dp.depth.mm -= M_OR_FT(1, 5); plannerModel->editStop(row, dp); } } diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 59d993374..4ae98da5b 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -20,7 +20,7 @@ CylindersModel::CylindersModel(QObject *parent) : QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role == Qt::DisplayRole && in_planner() && section == WORKINGPRESS) + if (role == Qt::DisplayRole && orientation == Qt::Horizontal && in_planner() && section == WORKINGPRESS) return tr("Start press."); else return CleanerTableModel::headerData(section, orientation, role); @@ -143,7 +143,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const // seem implausible case START: case END: - if ((cyl->start.mbar && !cyl->end.mbar) || + if ((cyl->start.mbar && !cyl->end.mbar && !cyl->sample_end.mbar) || (cyl->end.mbar && cyl->start.mbar <= cyl->end.mbar)) ret = REDORANGE1_HIGH_TRANS; break; @@ -157,7 +157,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const font.setItalic(!cyl->start.mbar); break; case END: - font.setItalic(!cyl->end.mbar); + font.setItalic(!cyl->end.mbar && !cyl->sample_end.mbar); break; } ret = font; @@ -256,10 +256,10 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const ret = tr("Switch depth for deco gas. Calculated using Deco pO₂ preference, unless set manually."); break; case MOD: - ret = tr("Calculated using Bottom pO₂ preference. Setting MOD adjusts O₂%, set to '*' for best O₂% for max depth."); + ret = tr("Calculated using Bottom pO₂ preference. Setting MOD adjusts O₂%, set to '*' for best O₂% for max. depth."); break; case MND: - ret = tr("Calculated using Best Mix END preference. Setting MND adjusts He%, set to '*' for best He% for max depth."); + ret = tr("Calculated using Best Mix END preference. Setting MND adjusts He%, set to '*' for best He% for max. depth."); break; } break; @@ -383,7 +383,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (CHANGED()) { if (QString::compare(vString.toUtf8().data(), "*") == 0) { cyl->bestmix_o2 = true; - // Calculate fO2 for max depth + // Calculate fO2 for max. depth cyl->gasmix.o2 = best_o2(displayed_dive.maxdepth, &displayed_dive); } else { cyl->bestmix_o2 = false; @@ -400,7 +400,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (CHANGED()) { if (QString::compare(vString.toUtf8().data(), "*") == 0) { cyl->bestmix_he = true; - // Calculate fO2 for max depth + // Calculate fO2 for max. depth cyl->gasmix.he = best_he(displayed_dive.maxdepth, &displayed_dive); } else { cyl->bestmix_he = false; diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 46326c678..f4b821e6e 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -152,15 +152,15 @@ void DivePlannerPointsModel::setupCylinders() CylindersModel::instance()->copyFromDive(&displayed_dive); } -// Update the dive's maximum depth. Returns true if max depth changed +// Update the dive's maximum depth. Returns true if max. depth changed bool DivePlannerPointsModel::updateMaxDepth() { int prevMaxDepth = displayed_dive.maxdepth.mm; displayed_dive.maxdepth.mm = 0; for (int i = 0; i < rowCount(); i++) { divedatapoint p = at(i); - if (p.depth > displayed_dive.maxdepth.mm) - displayed_dive.maxdepth.mm = p.depth; + if (p.depth.mm > displayed_dive.maxdepth.mm) + displayed_dive.maxdepth.mm = p.depth.mm; } return (displayed_dive.maxdepth.mm != prevMaxDepth); } @@ -241,7 +241,7 @@ QVariant DivePlannerPointsModel::data(const QModelIndex &index, int role) const case CCSETPOINT: return (double)p.setpoint / 1000; case DEPTH: - return (int) lrint(get_depth_units(p.depth, NULL, NULL)); + return (int) lrint(get_depth_units(p.depth.mm, NULL, NULL)); case RUNTIME: return p.time / 60; case DURATION: @@ -401,6 +401,20 @@ void DivePlannerPointsModel::setDecoSac(double sac) emitDataChanged(); } +void DivePlannerPointsModel::setSacFactor(double factor) +{ + auto planner = SettingsObjectWrapper::instance()->planner_settings; + planner->setSacFactor((int) round(factor * 100)); + emitDataChanged(); +} + +void DivePlannerPointsModel::setProblemSolvingTime(int minutes) +{ + auto planner = SettingsObjectWrapper::instance()->planner_settings; + planner->setProblemSolvingTime(minutes); + emitDataChanged(); +} + void DivePlannerPointsModel::setGFHigh(const int gfhigh) { tempGFHigh = gfhigh; @@ -542,7 +556,7 @@ void DivePlannerPointsModel::setDropStoneMode(bool value) beginInsertRows(QModelIndex(), 0, 0); /* Copy the first current point */ divedatapoint p = divepoints.at(0); - p.time = p.depth / prefs.descrate; + p.time = p.depth.mm / prefs.descrate; divepoints.push_front(p); endInsertRows(); } @@ -609,7 +623,7 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_ 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; + milimeters = t.depth.mm; seconds = t.time + 600; // 10 minutes. cylinderid = t.cylinderid; ccpoint = t.setpoint; @@ -648,7 +662,7 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_ // add the new stop beginInsertRows(QModelIndex(), row, row); divedatapoint point; - point.depth = milimeters; + point.depth.mm = milimeters; point.time = seconds; point.cylinderid = cylinderid; point.setpoint = ccpoint; @@ -793,11 +807,11 @@ void DivePlannerPointsModel::createTemporaryPlan() lastIndex = i; if (i == 0 && mode == PLAN && prefs.drop_stone_mode) { /* Okay, we add a first segment where we go down to depth */ - plan_add_segment(&diveplan, p.depth / prefs.descrate, p.depth, p.cylinderid, p.setpoint, true); - deltaT -= p.depth / prefs.descrate; + plan_add_segment(&diveplan, p.depth.mm / prefs.descrate, p.depth.mm, p.cylinderid, p.setpoint, true); + deltaT -= p.depth.mm / prefs.descrate; } if (p.entered) - plan_add_segment(&diveplan, deltaT, p.depth, p.cylinderid, p.setpoint, true); + plan_add_segment(&diveplan, deltaT, p.depth.mm, p.cylinderid, p.setpoint, true); } // what does the cache do??? diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index e9227f671..bc3458ffd 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -91,6 +91,8 @@ slots: void setReserveGas(int reserve); void setSwitchAtReqStop(bool value); void setMinSwitchDuration(int duration); + void setSacFactor(double factor); + void setProblemSolvingTime(int minutes); signals: void planCreated(); diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 20bdc3666..435d72a0b 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -223,7 +223,7 @@ QVariant DiveItem::data(int column, int role) const retVal = tr("Duration"); break; case TEMPERATURE: - retVal = tr("Temp(%1%2)").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F"); + retVal = tr("Temp.(%1%2)").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F"); break; case TOTALWEIGHT: retVal = tr("Weight(%1)").arg((get_units()->weight == units::KG) ? tr("kg") : tr("lbs")); @@ -232,7 +232,7 @@ QVariant DiveItem::data(int column, int role) const retVal = tr("Suit"); break; case CYLINDER: - retVal = tr("Cyl"); + retVal = tr("Cylinder"); break; case GAS: retVal = tr("Gas"); @@ -246,7 +246,7 @@ QVariant DiveItem::data(int column, int role) const retVal = tr("OTU"); break; case MAXCNS: - retVal = tr("Max CNS"); + retVal = tr("Max. CNS"); break; case PHOTOS: retVal = tr("Photos before/during/after dive"); @@ -445,7 +445,7 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Duration"); break; case TEMPERATURE: - ret = tr("Temp"); + ret = tr("Temp."); break; case TOTALWEIGHT: ret = tr("Weight"); @@ -454,7 +454,7 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Suit"); break; case CYLINDER: - ret = tr("Cyl"); + ret = tr("Cylinder"); break; case GAS: ret = tr("Gas"); @@ -494,7 +494,7 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Duration"); break; case TEMPERATURE: - ret = tr("Temp(%1%2)").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F"); + ret = tr("Temp.(%1%2)").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F"); break; case TOTALWEIGHT: ret = tr("Weight(%1)").arg((get_units()->weight == units::KG) ? tr("kg") : tr("lbs")); @@ -503,7 +503,7 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Suit"); break; case CYLINDER: - ret = tr("Cyl"); + ret = tr("Cylinder"); break; case GAS: ret = tr("Gas"); @@ -80,7 +80,7 @@ CC Closed circuit - short for "rebreather diving" OC Open circuit - traditional scuba -CC Set Point Rebreathers often are run with a fixed PO₂ - the "set point" +CC Setpoint Rebreathers often are run with a fixed PO₂ - the "setpoint" Bailing out to OC in a rebreather dive the dive switches to their emergency diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3d8d19f7a..294f26d05 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -54,7 +54,7 @@ macro(TEST NAME FILE) if(CMAKE_SYSTEM_NAME STREQUAL "Windows") # Re-install dependencies in WINDOWS_STAGING_TESTS (and not in WINDOWSSTAGING) # to avoid packing testing related dlls in the installer - install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DSUBSURFACE_TARGET=${NAME} -DSUBSURFACE_SOURCE=${SUBSURFACE_SOURCE} -DSTAGING=${WINDOWS_STAGING_TESTS} -P ${CMAKE_SOURCE_DIR}/cmake/Modules/dlllist.cmake)") + install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DSUBSURFACE_TARGET=${NAME} -DSUBSURFACE_SOURCE=${SUBSURFACE_SOURCE} -DSTAGING=${WINDOWS_STAGING_TESTS} -P ${CMAKE_SOURCE_DIR}/cmake/Modules/dlllist.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})") # Run test using wine if(WINE_PROGRAM) diff --git a/tests/testpreferences.cpp b/tests/testpreferences.cpp index c3de709ea..c01e9ec14 100644 --- a/tests/testpreferences.cpp +++ b/tests/testpreferences.cpp @@ -11,6 +11,12 @@ pref->sync(); \ pref->load(); \ QCOMPARE(METHOD, VALUE); +void TestPreferences::initTestCase() +{ + QCoreApplication::setOrganizationName("Subsurface"); + QCoreApplication::setOrganizationDomain("subsurface.hohndel.org"); + QCoreApplication::setApplicationName("SubsurfaceTestPreferences"); +} void TestPreferences::testPreferences() { diff --git a/tests/testpreferences.h b/tests/testpreferences.h index 9b2c44ab3..c97efb4de 100644 --- a/tests/testpreferences.h +++ b/tests/testpreferences.h @@ -8,6 +8,7 @@ class TestPreferences : public QObject { Q_OBJECT private slots: + void initTestCase(); void testPreferences(); }; diff --git a/theme/dive_export.html b/theme/dive_export.html index 2620ce12a..64617b5a0 100644 --- a/theme/dive_export.html +++ b/theme/dive_export.html @@ -214,7 +214,7 @@ function changeAdvSearch(e){ <div id="time_header" onClick="change_sort_col('3')" class="item">Time</div> <div id="location_header" onClick="change_sort_col('6')" class="item_large">Location</div> <div id="duration_header" onClick="change_sort_col('4')" class="item">Duration</div> - <div id="maxdepth_header" onClick="change_sort_col('5')" class="item">Max Depth</div> + <div id="maxdepth_header" onClick="change_sort_col('5')" class="item">Max. Depth</div> </div> <div id="diveslist"> </div> |