From 9d80e7bfe47726e1f27be323088eb7d36f14c854 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Dec 2012 20:08:29 -0800 Subject: Add CNS and pO2 tracking in the samples This adds the new members to the sample structure and fills them from supported dive computers (Uemis SDA and OSTC / Shearwater Predator, assuming you have libdivecomputer 0.3). Save relvant values of this to the XML file and load it back. Handle the new fields when merging dives. At this stage we don't DO anything with this, all we do is extract them from the dive computer, save them to the XML file and load them back. Signed-off-by: Dirk Hohndel --- dive.c | 4 ++++ dive.h | 2 ++ libdivecomputer.c | 12 ++++++++++++ parse-xml.c | 19 +++++++++++++++++-- save-xml.c | 4 ++++ uemis.c | 1 + 6 files changed, 40 insertions(+), 2 deletions(-) diff --git a/dive.c b/dive.c index d4e0217d7..97b688a6d 100644 --- a/dive.c +++ b/dive.c @@ -703,6 +703,10 @@ add_sample_b: sample.cylinderpressure = as->cylinderpressure; if (as->cylinderindex) sample.cylinderindex = as->cylinderindex; + if (as->cns) + sample.cns = as->cns; + if (as->po2) + sample.po2 = as->po2; merge_one_sample(&sample, at, res); diff --git a/dive.h b/dive.h index 4cfa6fa38..3054173ad 100644 --- a/dive.h +++ b/dive.h @@ -230,6 +230,8 @@ struct sample { duration_t ndl; duration_t stoptime; depth_t stopdepth; + int cns; + int po2; }; /* diff --git a/libdivecomputer.c b/libdivecomputer.c index ca91dc892..e62265b7a 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -185,6 +185,18 @@ sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata) printf("%02X", ((unsigned char *) value.vendor.data)[i]); printf("\n"); break; +#if DC_VERSION_CHECK(0, 3, 0) + case DC_SAMPLE_SETPOINT: + /* for us a setpoint means constant pO2 from here */ + sample->po2 = value.setpoint * 1000 + 0.5; + break; + case DC_SAMPLE_PPO2: + sample->po2 = value.ppo2 * 1000 + 0.5; + break; + case DC_SAMPLE_CNS: + sample->cns = value.cns * 100 + 0.5; + break; +#endif default: break; } diff --git a/parse-xml.c b/parse-xml.c index 35299257a..f88d4ef7f 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -168,7 +168,7 @@ static struct { } cur_event; static struct tm cur_tm; static int cur_cylinder_index, cur_ws_index; -static int lastndl, laststoptime, laststopdepth; +static int lastndl, laststoptime, laststopdepth, lastcns, lastpo2; static enum import_source { UNKNOWN, @@ -484,6 +484,13 @@ static void get_index(char *buffer, void *_i) free(buffer); } +static void double_to_permil(char *buffer, void *_i) +{ + int *i = _i; + *i = g_ascii_strtod(buffer, NULL) * 1000.0 + 0.5; + free(buffer); +} + static void hex_value(char *buffer, void *_i) { uint32_t *i = _i; @@ -675,6 +682,10 @@ static void try_to_fill_sample(struct sample *sample, const char *name, char *bu return; if (MATCH(".sample.stopdepth", depth, &sample->stopdepth)) return; + if (MATCH(".sample.cns", get_index, &sample->cns)) + return; + if (MATCH(".sample.po2", double_to_permil, &sample->po2)) + return; switch (import_source) { case DIVINGLOG: @@ -1085,6 +1096,8 @@ static void sample_start(void) cur_sample->ndl.seconds = lastndl; cur_sample->stoptime.seconds = laststoptime; cur_sample->stopdepth.mm = laststopdepth; + cur_sample->cns = lastcns; + cur_sample->po2 = lastpo2; } static void sample_end(void) @@ -1096,6 +1109,8 @@ static void sample_end(void) lastndl = cur_sample->ndl.seconds; laststoptime = cur_sample->stoptime.seconds; laststopdepth = cur_sample->stopdepth.mm; + lastcns = cur_sample->cns; + lastpo2 = cur_sample->po2; cur_sample = NULL; } @@ -1120,7 +1135,7 @@ static void divecomputer_start(void) /* .. this is the one we'll use */ cur_dc = dc; - lastndl = laststoptime = laststopdepth = 0; + lastcns = lastpo2 = lastndl = laststoptime = laststopdepth = 0; } static void divecomputer_end(void) diff --git a/save-xml.c b/save-xml.c index 1d38cc1b8..fb9eb413a 100644 --- a/save-xml.c +++ b/save-xml.c @@ -334,6 +334,10 @@ static void save_sample(FILE *f, struct sample *sample, const struct sample *pre fprintf(f, " stoptime='%u:%02u min'", FRACTION(sample->stoptime.seconds, 60)); if (sample->stopdepth.mm != prev->stopdepth.mm) show_milli(f, " stopdepth='", sample->stopdepth.mm, " m", "'"); + if (sample->cns != prev->cns) + fprintf(f, " cns='%u%%'", sample->cns); + if (sample->po2 != prev->po2) + fprintf(f, " po2='%u.%2u bar'", FRACTION(sample->po2, 1000)); fprintf(f, " />\n"); } diff --git a/uemis.c b/uemis.c index e4a4adb21..8d05a99f9 100644 --- a/uemis.c +++ b/uemis.c @@ -349,6 +349,7 @@ void uemis_parse_divelog_binary(char *base64, void *datap) { sample->cylinderindex = u_sample->active_tank; sample->cylinderpressure.mbar = (u_sample->tank_pressure_high * 256 + u_sample->tank_pressure_low) * 10; + sample->cns = u_sample->cns; uemis_event(dive, dc, sample, u_sample); finish_sample(dc); i += 0x25; -- cgit v1.2.3-70-g09d2 From 3b425b4c1305f821d1bdafca3ecca8a6f3846a96 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Dec 2012 21:05:21 -0800 Subject: Plot pO2 graph based on sample information, if available This should give closed circuit divers with a supported dive computer correct partial pressure plots. Signed-off-by: Dirk Hohndel --- profile.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/profile.c b/profile.c index 8940817b8..c869d70df 100644 --- a/profile.c +++ b/profile.c @@ -41,6 +41,7 @@ struct plot_data { int ndl; int stoptime; int stopdepth; + int cns; int smoothed; double po2, pn2, phe; velocity_t velocity; @@ -1704,14 +1705,14 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer { int cylinderindex = -1; int lastdepth, lastindex; - int i, pi_idx, nr, sec, cyl, stoptime, ndl, stopdepth; + int i, pi_idx, nr, sec, cyl, stoptime, ndl, stopdepth, cns; struct plot_info *pi; pr_track_t *track_pr[MAX_CYLINDERS] = {NULL, }; pr_track_t *pr_track, *current; gboolean missing_pr = FALSE; struct plot_data *entry = NULL; struct event *ev; - double amb_pressure; + double amb_pressure, po2; /* The plot-info is embedded in the graphics context */ pi = &gc->pi; @@ -1755,6 +1756,8 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer pi->has_ndl |= ndl; stopdepth = sample->stopdepth.mm; stoptime = sample->stoptime.seconds; + po2 = sample->po2 / 1000.0; + cns = sample->cns; while (ev && ev->time.seconds < sample->time.seconds) { /* insert two fake plot info structures for the end of * the old tank and the start of the new tank */ @@ -1775,9 +1778,13 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer entry->stopdepth = stopdepth; entry->stoptime = stoptime; entry->ndl = ndl; + entry->cns = cns; + entry->po2 = po2; (entry + 1)->stopdepth = stopdepth; (entry + 1)->stoptime = stoptime; (entry + 1)->ndl = ndl; + (entry + 1)->cns = cns; + (entry + 1)->po2 = po2; pi_idx += 2; entry = pi->entry + i + pi_idx; ev = get_next_event(ev->next, "gaschange"); @@ -1791,6 +1798,8 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer entry->stopdepth = stopdepth; entry->stoptime = stoptime; entry->ndl = ndl; + entry->cns = cns; + entry->po2 = po2; pi_idx++; entry = pi->entry + i + pi_idx; ev = get_next_event(ev->next, "gaschange"); @@ -1801,6 +1810,8 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer entry->stopdepth = stopdepth; entry->stoptime = stoptime; entry->ndl = ndl; + entry->cns = cns; + entry->po2 = po2; entry->cylinderindex = sample->cylinderindex; SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar; entry->temperature = sample->temperature.mkelvin; @@ -1858,10 +1869,18 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer if (!fo2) fo2 = AIR_PERMILLE; - entry->po2 = fo2 / 1000.0 * amb_pressure; - entry->phe = fhe / 1000.0 * amb_pressure; - entry->pn2 = (1000 - fo2 - fhe) / 1000.0 * amb_pressure; - + if (entry->po2) { + /* we have an O2 partial pressure in the sample - so this + * is likely a CC dive... use that instead of the value + * from the cylinder info */ + double ratio = (double)fhe / (1000.0 - fo2); + entry->phe = (amb_pressure - entry->po2) * ratio; + entry->pn2 = amb_pressure - entry->po2 - entry->phe; + } else { + entry->po2 = fo2 / 1000.0 * amb_pressure; + entry->phe = fhe / 1000.0 * amb_pressure; + entry->pn2 = (1000 - fo2 - fhe) / 1000.0 * amb_pressure; + } /* finally, do the discrete integration to get the SAC rate equivalent */ current->pressure_time += (entry->sec - (entry-1)->sec) * depth_to_mbar((entry->depth + (entry-1)->depth) / 2, dive) / 1000.0; -- cgit v1.2.3-70-g09d2 From 40d05ed768eca91ac06058a1419291939b4b2a06 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Dec 2012 13:56:46 -0800 Subject: Show two decimal digits in pO2, pN2 and pHe in the tooltip Tec divers seem to like a little bit more precision - and the dive computers certainly provide it (or we can calculate it). Signed-off-by: Dirk Hohndel --- profile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/profile.c b/profile.c index c869d70df..e6b2d5301 100644 --- a/profile.c +++ b/profile.c @@ -2122,15 +2122,15 @@ static void plot_string(struct plot_data *entry, char *buf, size_t bufsize, } if (partial_pressure_graphs.po2) { memcpy(buf2, buf, bufsize); - snprintf(buf, bufsize, "%s\npO" UTF8_SUBSCRIPT_2 ":%.1f", buf2, entry->po2); + snprintf(buf, bufsize, "%s\npO" UTF8_SUBSCRIPT_2 ":%.2f", buf2, entry->po2); } if (partial_pressure_graphs.pn2) { memcpy(buf2, buf, bufsize); - snprintf(buf, bufsize, "%s\npN" UTF8_SUBSCRIPT_2 ":%.1f", buf2, entry->pn2); + snprintf(buf, bufsize, "%s\npN" UTF8_SUBSCRIPT_2 ":%.2f", buf2, entry->pn2); } if (partial_pressure_graphs.phe) { memcpy(buf2, buf, bufsize); - snprintf(buf, bufsize, "%s\npHe:%.1f", buf2, entry->phe); + snprintf(buf, bufsize, "%s\npHe:%.2f", buf2, entry->phe); } free(buf2); } -- cgit v1.2.3-70-g09d2 From 81851595669becd5bd1ef73cfc26570fa16554e0 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Dec 2012 13:58:27 -0800 Subject: Make changes to some of the preference immediately visible This is just a small part of the solution to the bigger problem with the preferences. See ticket 21 Signed-off-by: Dirk Hohndel --- gtk-gui.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gtk-gui.c b/gtk-gui.c index 7a4f108ad..9edf408b9 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -471,6 +471,7 @@ static void name(GtkWidget *w, gpointer data) \ { \ GtkWidget **entry = data; \ option = GTK_TOGGLE_BUTTON(w)->active; \ + dive_list_update_dives(); \ if (entry) \ gtk_widget_set_sensitive(*entry, option);\ } -- cgit v1.2.3-70-g09d2 From ef3735eafbe6eb2bdd1a85b772e7c6dfc8945ac7 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Dec 2012 14:02:31 -0800 Subject: Make sure pO2 and cns are filled in all samples This allows things to work for dive computers like the OSTC that give us setpoint information in the sample, but not constant pO2 readings. Signed-off-by: Dirk Hohndel --- libdivecomputer.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libdivecomputer.c b/libdivecomputer.c index e62265b7a..99532b63f 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -22,7 +22,7 @@ static const char *progress_bar_text = ""; static double progress_bar_fraction = 0.0; -static int stoptime, stopdepth, ndl; +static int stoptime, stopdepth, ndl, po2, cns; static GError *error(const char *fmt, ...) { @@ -152,6 +152,8 @@ sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata) sample->ndl.seconds = ndl; sample->stoptime.seconds = stoptime; sample->stopdepth.mm = stopdepth; + sample->po2 = po2; + sample->cns = cns; } sample = prepare_sample(dc); sample->time.seconds = value.time; @@ -188,13 +190,13 @@ sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata) #if DC_VERSION_CHECK(0, 3, 0) case DC_SAMPLE_SETPOINT: /* for us a setpoint means constant pO2 from here */ - sample->po2 = value.setpoint * 1000 + 0.5; + sample->po2 = po2 = value.setpoint * 1000 + 0.5; break; case DC_SAMPLE_PPO2: - sample->po2 = value.ppo2 * 1000 + 0.5; + sample->po2 = po2 = value.ppo2 * 1000 + 0.5; break; case DC_SAMPLE_CNS: - sample->cns = value.cns * 100 + 0.5; + sample->cns = cns = value.cns * 100 + 0.5; break; #endif default: -- cgit v1.2.3-70-g09d2 From 9bcd21bf679081b64aaf10f0fc5a035c34ac1c7b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Dec 2012 14:07:10 -0800 Subject: Draw pO2 profile last This way it sits on top of the other partial pressure plots and is a bit easier to read. Signed-off-by: Dirk Hohndel --- profile.c | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/profile.c b/profile.c index e6b2d5301..9b0ddd700 100644 --- a/profile.c +++ b/profile.c @@ -815,31 +815,6 @@ static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *p setup_pp_limits(gc, pi); - if (partial_pressure_graphs.po2) { - set_source_rgba(gc, PO2); - entry = pi->entry; - move_to(gc, entry->sec, entry->po2); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->po2 < partial_pressure_graphs.po2_threshold) - line_to(gc, entry->sec, entry->po2); - else - move_to(gc, entry->sec, entry->po2); - } - cairo_stroke(gc->cr); - - set_source_rgba(gc, PO2_ALERT); - entry = pi->entry; - move_to(gc, entry->sec, entry->po2); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->po2 >= partial_pressure_graphs.po2_threshold) - line_to(gc, entry->sec, entry->po2); - else - move_to(gc, entry->sec, entry->po2); - } - cairo_stroke(gc->cr); - } if (partial_pressure_graphs.pn2) { set_source_rgba(gc, PN2); entry = pi->entry; @@ -890,6 +865,31 @@ static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *p } cairo_stroke(gc->cr); } + if (partial_pressure_graphs.po2) { + set_source_rgba(gc, PO2); + entry = pi->entry; + move_to(gc, entry->sec, entry->po2); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->po2 < partial_pressure_graphs.po2_threshold) + line_to(gc, entry->sec, entry->po2); + else + move_to(gc, entry->sec, entry->po2); + } + cairo_stroke(gc->cr); + + set_source_rgba(gc, PO2_ALERT); + entry = pi->entry; + move_to(gc, entry->sec, entry->po2); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->po2 >= partial_pressure_graphs.po2_threshold) + line_to(gc, entry->sec, entry->po2); + else + move_to(gc, entry->sec, entry->po2); + } + cairo_stroke(gc->cr); + } } static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) -- cgit v1.2.3-70-g09d2 From 02844c3a10f3e7785aa81080e1c91b64c4d5dfd8 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Dec 2012 14:16:13 -0800 Subject: Correctly parse the two different gas change events This now takes the He percentage into account when matching tanks. Given the encoding of fO2 and fHe in the GASCHANGE2 event, we can simply mask and shift as if we have a GASCHANGE2 event and things will automatically work correctly for the regular GASCHANGE event. Signed-off-by: Dirk Hohndel --- profile.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/profile.c b/profile.c index 9b0ddd700..e56a1459d 100644 --- a/profile.c +++ b/profile.c @@ -1627,14 +1627,20 @@ static int get_cylinder_index(struct dive *dive, struct event *ev) * * Crazy suunto gas change events. We really should do * this in libdivecomputer or something. + * + * There are two different gas change events that can get + * us here - GASCHANGE2 has the He value in the high 16 + * bits; looking at the possible values we can actually + * handle them with the same code since the high 16 bits + * will be 0 with the GASCHANGE event - and that means no He */ for (i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = dive->cylinder+i; int o2 = (cyl->gasmix.o2.permille + 5) / 10; - if (o2 == ev->value) + int he = (cyl->gasmix.he.permille + 5) / 10; + if (o2 == (ev->value & 0xFFFF) && he == (ev->value >> 16)) return i; } - return 0; } -- cgit v1.2.3-70-g09d2 From 07b74007f1731b6557ec05300d5734970fb9d29c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Dec 2012 21:14:00 -0800 Subject: Correct partial pressure calculations Regardless what the dive computer tells us, don't believe that pO2 was higher than the ambient pressure. This gives much more realistic values. Signed-off-by: Dirk Hohndel --- profile.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/profile.c b/profile.c index e56a1459d..290638d6a 100644 --- a/profile.c +++ b/profile.c @@ -1879,9 +1879,11 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer /* we have an O2 partial pressure in the sample - so this * is likely a CC dive... use that instead of the value * from the cylinder info */ + double po2 = entry->po2 > amb_pressure ? amb_pressure : entry->po2; double ratio = (double)fhe / (1000.0 - fo2); - entry->phe = (amb_pressure - entry->po2) * ratio; - entry->pn2 = amb_pressure - entry->po2 - entry->phe; + entry->phe = (amb_pressure - po2) * ratio; + entry->pn2 = amb_pressure - po2 - entry->phe; + entry->po2 = po2; } else { entry->po2 = fo2 / 1000.0 * amb_pressure; entry->phe = fhe / 1000.0 * amb_pressure; -- cgit v1.2.3-70-g09d2 From 8dfc4dccc5b74626100952cf78fec8b10751fa33 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Dec 2012 21:20:00 -0800 Subject: Don't print an initial gaschange event that just tells us the gas used Some dive computers appear to tell us the gas used in a gaschange event right at the beginning of the dive. We arbitrary have a cut-off that says "a gas change in the first 30 seconds shouldn't get a marker". Signed-off-by: Dirk Hohndel --- profile.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/profile.c b/profile.c index 290638d6a..7e42c6083 100644 --- a/profile.c +++ b/profile.c @@ -357,6 +357,11 @@ static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, st } } } + if (event->time.seconds < 30 && !strcmp(event->name, "gaschange")) + /* a gas change in the first 30 seconds is the way of some dive computers + * to tell us the gas that is used; let's not plot a marker for that */ + return; + for (i = 0; i < pi->nr; i++) { struct plot_data *data = pi->entry + i; if (event->time.seconds < data->sec) -- cgit v1.2.3-70-g09d2 From 9463e7895be21befc6fbcdb02572d73ed2c188b2 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 9 Dec 2012 14:34:41 -0800 Subject: Don't print partial pressure values in the profile Instead provide a scale on the right in a highly transparent grey and rely on the tooltip available with mouse-over to pinpoint the value at certain spots with much better accuracy. Fixes #30 Signed-off-by: Dirk Hohndel --- color.h | 1 + display.h | 1 + profile.c | 287 +++++--------------------------------------------------------- 3 files changed, 21 insertions(+), 268 deletions(-) diff --git a/color.h b/color.h index 38839ae7f..23065d306 100644 --- a/color.h +++ b/color.h @@ -31,6 +31,7 @@ // Monochromes #define BLACK1_LOW_TRANS { 0.0, 0.0, 0.0, 0.75 } +#define BLACK1_HIGH_TRANS { 0.0, 0.0, 0.0, 0.25 } #define TUNDORA1_MED_TRANS { 0.3, 0.3, 0.3, 0.5 } #define MERCURY1_MED_TRANS { 0.9, 0.9, 0.9, 0.5 } #define CONCRETE1_LOWER_TRANS { 0.95, 0.95, 0.95, 0.9 } diff --git a/display.h b/display.h index 6f9f6854c..7b1628866 100644 --- a/display.h +++ b/display.h @@ -19,6 +19,7 @@ struct plot_info { int endpressure, maxpressure; int mintemp, maxtemp, endtemp; double endtempcoord; + double maxpp; gboolean has_ndl; struct plot_data *entry; }; diff --git a/profile.c b/profile.c index 7e42c6083..514435d51 100644 --- a/profile.c +++ b/profile.c @@ -69,7 +69,7 @@ typedef enum { VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY, /* gas colors */ - PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, + PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES, /* Other colors */ TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW, @@ -109,6 +109,7 @@ static const color_t profile_color[] = { [PN2_ALERT] = {{RED1, BLACK1_LOW_TRANS}}, [PHE] = {{PEANUT, PEANUT_MED_TRANS}}, [PHE_ALERT] = {{RED1, PEANUT_MED_TRANS}}, + [PP_LINES] = {{BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS}}, [TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1}}, [ALERT_BG] = {{BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS}}, @@ -522,186 +523,6 @@ static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi) } } -/* ap points to an array of int with pi->nr + 1 elements that is - * ininitialized with just one -1 entry - * this adds entries (if they aren't too close to an existing one) - * and keeps things sorted - * we KNOW the array is big enough to hold all possible indices - * a2p is a secondary array - we insert value at the same relative - * positio as idx in ap */ -static void add_index(int idx, int margin, int **ap, int **a2p, int value) -{ - int j, i = 0; - int *a = *ap; - int *a2 = *a2p; - - while (a[i] != -1 && a[i] < idx) - i++; - if (a[i] == idx) - return; - /* already have a POI to the left with the same vertical positiom and too close */ - if ((i > 0 && a[i - 1] != -1 && a2[i - 1] == value && idx - a[i - 1] < margin)) - return; - /* already have a POI to the right with the same vertical positiom and too close */ - if (a[i] != -1 && a2[i] == value && a[i] - idx < margin) - return; - if (a[i] != -1 && a[i] - idx < margin) - return; - j = i; - while (a[j] != -1) - j++; - while (j >= i) { - a[j+1] = a[j]; - a2[j+1] = a2[j]; - j--; - } - a[i] = idx; - a2[i] = value; -} - -#define LI(_i,_j) MAX((_i)-(_j), 0) -#define RI(_i,_j) MIN((_i)+(_j), nr - 1) -#define SPIKE(_i,_s) if (fabs(_s) > fabs(spk_data[_i])) spk_data[_i] = (_s) -/* this is an attempt at a metric that finds spikes in a data series */ -static void calculate_spikyness(int nr, double *data, double *spk_data, int deltax, double deltay) -{ - int i, j; - double dminl, dminr, dmaxl, dmaxr; - -#if DEBUG_PROFILE > 2 - printf("Spike data: \n 0 "); -#endif - for (i = 0; i < nr; i++) { - dminl = dminr = dmaxl = dmaxr = data[i]; - spk_data[i] = 0.0; - for (j = 1; j < deltax; j++) { - if (data[LI(i,j)] < dminl) - dminl = data[LI(i,j)]; - if (data[LI(i,j)] > dmaxl) - dmaxl = data[LI(i,j)]; - - if (data[RI(i,j)] < dminr) - dminr = data[RI(i,j)]; - if (data[RI(i,j)] > dmaxr) - dmaxr = data[RI(i,j)]; - - /* don't do super narrow */ - if (j < deltax / 3) - continue; - /* falling edge on left */ - if (dmaxl == data[i] && dmaxr - data[i] < 0.1 * (data[i] - dminl)) - SPIKE(i,(data[i] - dminl) / j); - /* falling edge on right */ - if (dmaxr == data[i] && dmaxl - data[i] < 0.1 * (data[i] - dminr)) - SPIKE(i,(data[i] - dminr) / j); - - /* minima get a negative spike value */ - /* rising edge on left */ - if (dminl == data[i] && data[i] - dminr < 0.1 * (dmaxl - data[i])) - SPIKE(i,(data[i] - dmaxl) / j); - /* rising edge on right */ - if (dminr == data[i] && data[i] - dminl < 0.1 * (dmaxr - data[i])) - SPIKE(i,(data[i] - dmaxr) / j); - } -#if DEBUG_PROFILE > 2 - fprintf(debugfile, "%.4lf ", spk_data[i]); - if (i % 12 == 11) - fprintf(debugfile, "\n%2d ", (i + 1) / 12); -#endif - } -#if DEBUG_PROFILE > 2 - printf("\n"); -#endif -} - -/* only show one spike in a deltax wide region - pick the highest (and first if the same) */ -static gboolean higher_spike(double *spk_data, int idx, int nr, int deltax) -{ - int i; - double s = spk_data[idx]; - for (i = MAX(0, idx - deltax); i <= MIN(idx + deltax, nr - 1); i++) { - if (s > 0) { - if (spk_data[i] > s) - return TRUE; - } else { - if (spk_data[i] < s) - return TRUE; - } - if (spk_data[i] == s && i < idx) - return TRUE; - } - return FALSE; -} - -/* this figures out which time stamps provide "interesting" formations in the graphs; - * this includes local minima and maxima as well as long plateaus. - * pass in the function that returns the value at a certain point (as double), - * the delta in time (expressed as number of data points of "significant time") - * the delta at which the value is considered to have been "significantly changed" and - * the number of points to cover - * returns a list of indices that ends with a -1 of times that are "interesting" */ -static void find_points_of_interest(struct plot_info *pi, double (*value_func)(int, struct plot_info *), - int deltax, double deltay, int **poip, int **poip_vpos) -{ - int i, j, nr = pi->nr; - double *data, *data_max, *data_min, *spk_data; - double min, max; - int *pois; - - /* avoid all the function calls by creating a local array and - * have some helper arrays to make our lifes easier */ - - data = malloc(nr * sizeof(double)); - data_max = malloc(nr * sizeof(double)); - data_min = malloc(nr * sizeof(double)); - spk_data = malloc(nr * sizeof(double)); - - pois = *poip = malloc((nr + 1) * sizeof(int)); - *poip_vpos = malloc((nr + 1) * sizeof(int)); - pois[0] = -1; - pois[1] = -1; - - /* copy the data and get the absolute minimum and maximum while we do it */ - for (i = 0; i < nr; i++) { - data_max[i] = data_min[i] = data[i] = value_func(i, pi); - if (i == 0 || data[i] < min) - min = data[i]; - if (i == 0 || data[i] > max) - max = data[i]; - } - /* next find out if there are real spikes in the graph */ - calculate_spikyness(nr, data, spk_data, deltax, deltay); - - /* now process all data points */ - for (i = 0; i < nr; i++) { - /* get the local min/max */ - for (j = MAX(0, i - deltax); j < i + deltax && j < nr; j++) { - if (data[j] < data[i]) - data_min[i] = data[j]; - if (data[j] > data[i]) - data_max[i] = data[j]; - } - /* is i the overall minimum or maximum */ - if (data[i] == max && (i == 0 || data[i - 1] != max)) - add_index(i, deltax, poip, poip_vpos, BOTTOM); - if (data[i] == min && (i == 0 || data[i - 1] != min)) - add_index(i, deltax, poip, poip_vpos, TOP); - /* is i a spike? */ - if (fabs(spk_data[i]) > 0.01 && ! higher_spike(spk_data, i, nr, deltax)) { - if (spk_data[i] > 0.0) - add_index(i, deltax, poip, poip_vpos, BOTTOM); - if (spk_data[i] < 0.0) - add_index(i, deltax, poip, poip_vpos, TOP); - } - /* is i a significant local minimum or maximum? */ - if (data[i] == data_min[i] && data_max[i] - data[i] > deltay) - add_index(i, deltax, poip, poip_vpos, TOP); - if (data[i] == data_max[i] && data[i] - data_min[i] > deltay) - add_index(i, deltax, poip, poip_vpos, BOTTOM); - } - /* still need to search for plateaus */ -} - static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi) { int maxdepth; @@ -717,99 +538,23 @@ static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi) gc->bottomy = -gc->topy / 20; } -static void plot_single_pp_text(struct graphics_context *gc, int sec, double pp, - double vpos, color_indice_t color) -{ - text_render_options_t tro = {12, color, CENTER, vpos}; - plot_text(gc, &tro, sec, pp, "%.1lf", pp); -} - -#define MAXPP(_mpp, _pp) { _mpp = 0; \ - for(i = 0; i< pi->nr; i++) \ - if (pi->entry[i]._pp > _mpp) \ - _mpp = pi->entry[i]._pp; \ - } - -static double po2_value(int idx, struct plot_info *pi) -{ - return pi->entry[idx].po2; -} - -static double pn2_value(int idx, struct plot_info *pi) -{ - return pi->entry[idx].pn2; -} - -static double phe_value(int idx, struct plot_info *pi) -{ - return pi->entry[idx].phe; -} - -static double plot_single_gas_pp_text(struct graphics_context *gc, struct plot_info *pi, - double (*value_func)(int, struct plot_info *), - double value_threshold, int color) -{ - int *pois, *pois_vpos; - int i, two_minutes = 1; - double maxpp = 0.0; - - /* don't bother with local min/max if the dive is under two minutes */ - if (pi->entry[pi->nr - 1].sec > 120) { - int idx = 0; - while (pi->entry[idx].sec == 0) - idx++; - while (pi->entry[idx + two_minutes].sec < 120) - two_minutes++; - } else { - two_minutes = pi->nr; - } - find_points_of_interest(pi, value_func, two_minutes, value_threshold, &pois, &pois_vpos); - for (i = 0; pois[i] != -1; i++) { - struct plot_data *entry = pi->entry + pois[i]; - double value = value_func(pois[i], pi); - -#if DEBUG_PROFILE > 1 - fprintf(debugfile, "POI at %d sec value %lf\n", entry->sec, entry->po2); -#endif - plot_single_pp_text(gc, entry->sec, value, pois_vpos[i], color); - if (value > maxpp) - maxpp = value; - } - free(pois); - free(pois_vpos); - - return maxpp; -} - static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) { - double pp, dpp, m, maxpp = 0.0; + double pp, dpp, m; int hpos; - static const text_render_options_t tro = {11, PN2, LEFT, MIDDLE}; + static const text_render_options_t tro = {11, PP_LINES, LEFT, MIDDLE}; setup_pp_limits(gc, pi); - if (partial_pressure_graphs.po2) { - maxpp = plot_single_gas_pp_text(gc, pi, po2_value, 0.4, PO2); - } - if (partial_pressure_graphs.pn2) { - m = plot_single_gas_pp_text(gc, pi, pn2_value, 0.6, PN2); - if (m > maxpp) - maxpp = m; - } - if (partial_pressure_graphs.phe) { - m = plot_single_gas_pp_text(gc, pi, phe_value, 0.4, PHE); - if (m > maxpp) - maxpp = m; - } - /* while this is somewhat useful, I don't like the way it looks... - * for now I'll leave the code here, but disable it */ - if (0) { - pp = floor(maxpp * 10.0) / 10.0 + 0.2; - dpp = floor(2.0 * pp) / 10.0; - hpos = pi->entry[pi->nr - 1].sec + 30; - for (m = 0.0; m <= pp; m += dpp) - plot_text(gc, &tro, hpos, m, "%.1f", m); + pp = floor(pi->maxpp * 10.0) / 10.0 + 0.2; + dpp = floor(2.0 * pp) / 10.0; + hpos = pi->entry[pi->nr - 1].sec; + set_source_rgba(gc, PP_LINES); + for (m = 0.0; m <= pp; m += dpp) { + move_to(gc, 0, m); + line_to(gc, hpos, m); + cairo_stroke(gc->cr); + plot_text(gc, &tro, hpos + 30, m, "%.1f", m); } } @@ -1894,6 +1639,12 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer entry->phe = fhe / 1000.0 * amb_pressure; entry->pn2 = (1000 - fo2 - fhe) / 1000.0 * amb_pressure; } + if (entry->po2 > pi->maxpp) + pi->maxpp = entry->po2; + if (entry->phe > pi->maxpp) + pi->maxpp = entry->phe; + if (entry->pn2 > pi->maxpp) + pi->maxpp = entry->pn2; /* finally, do the discrete integration to get the SAC rate equivalent */ current->pressure_time += (entry->sec - (entry-1)->sec) * depth_to_mbar((entry->depth + (entry-1)->depth) / 2, dive) / 1000.0; -- cgit v1.2.3-70-g09d2 From 39f80a1e0f38720addc66315aa1235731ba03418 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 10 Dec 2012 21:18:48 -0800 Subject: Display maximum CNS in the divelist We either pick the CNS reported by the dive computer at the end of the dive, or the maximum of that and the CNS values in the samples, if any. As usual, this column in the dive list defaults to off and it is controlled by a setting in the tec page of the preferences. Signed-off-by: Dirk Hohndel --- display-gtk.h | 1 + dive.c | 3 +++ dive.h | 2 +- divelist.c | 31 ++++++++++++++++++++++++++++++- gtk-gui.c | 8 ++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/display-gtk.h b/display-gtk.h index 5dc616bf9..f52b75639 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -19,6 +19,7 @@ typedef struct { gboolean nitrox; gboolean sac; gboolean otu; + gboolean maxcns; } visible_cols_t; typedef struct { diff --git a/dive.c b/dive.c index 97b688a6d..88d780f54 100644 --- a/dive.c +++ b/dive.c @@ -414,6 +414,7 @@ struct dive *fixup_dive(struct dive *dive) add_location(dive->location); add_suit(dive->suit); sanitize_cylinder_info(dive); + dive->maxcns = dive->cns; dc = &dive->dc; for (i = 0; i < dc->samples; i++) { struct sample *sample = dc->sample + i; @@ -474,6 +475,8 @@ struct dive *fixup_dive(struct dive *dive) depthtime += (time - lasttime) * (lastdepth + depth) / 2; lastdepth = depth; lasttime = time; + if (sample->cns > dive->maxcns) + dive->maxcns = sample->cns; } dive->start = start; dive->end = end; diff --git a/dive.h b/dive.h index 3054173ad..d113e27f4 100644 --- a/dive.h +++ b/dive.h @@ -316,7 +316,7 @@ struct dive { cylinder_t cylinder[MAX_CYLINDERS]; weightsystem_t weightsystem[MAX_WEIGHTSYSTEMS]; char *suit; - int sac, otu; + int sac, otu, cns, maxcns; /* Eventually we'll do multiple dive computers */ struct divecomputer dc; diff --git a/divelist.c b/divelist.c index 7a2f4fa5b..d157849fd 100644 --- a/divelist.c +++ b/divelist.c @@ -28,7 +28,7 @@ struct DiveList { GtkWidget *container_widget; GtkTreeStore *model, *listmodel, *treemodel; GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; - GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu; + GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu, *maxcns; int changed; }; @@ -61,6 +61,7 @@ enum { DIVE_NITROX, /* int: dummy */ DIVE_SAC, /* int: in ml/min */ DIVE_OTU, /* int: in OTUs */ + DIVE_MAXCNS, /* int: in % */ DIVE_LOCATION, /* "2nd Cathedral, Lanai" */ DIVELIST_COLUMNS }; @@ -691,6 +692,26 @@ static void otu_data_func(GtkTreeViewColumn *col, g_object_set(renderer, "text", buffer, NULL); } +/* Render the CNS data (in full %) */ +static void cns_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int value, idx; + char buffer[16]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_MAXCNS, &value, -1); + + if (idx < 0 || !value) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d%%", value); + + g_object_set(renderer, "text", buffer, NULL); +} + /* calculate OTU for a dive */ static int calculate_otu(struct dive *dive, struct divecomputer *dc) { @@ -841,6 +862,7 @@ static void fill_one_dive(struct dive *dive, DIVE_RATING, dive->rating, DIVE_SAC, dive->sac, DIVE_OTU, dive->otu, + DIVE_MAXCNS, dive->maxcns, DIVE_TOTALWEIGHT, total_weight(dive), DIVE_SUIT, suit, -1); @@ -920,6 +942,7 @@ void update_dive_list_col_visibility(void) gtk_tree_view_column_set_visible(dive_list.nitrox, visible_cols.nitrox); gtk_tree_view_column_set_visible(dive_list.sac, visible_cols.sac); gtk_tree_view_column_set_visible(dive_list.otu, visible_cols.otu); + gtk_tree_view_column_set_visible(dive_list.maxcns, visible_cols.maxcns); return; } @@ -1273,6 +1296,7 @@ static struct divelist_column { [DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &visible_cols.nitrox }, [DIVE_SAC] = { N_("SAC"), sac_data_func, NULL, 0, &visible_cols.sac }, [DIVE_OTU] = { N_("OTU"), otu_data_func, NULL, 0, &visible_cols.otu }, + [DIVE_MAXCNS] = { N_("maxCNS"), cns_data_func, NULL, 0, &visible_cols.maxcns }, [DIVE_LOCATION] = { N_("Location"), NULL, NULL, ALIGN_LEFT }, }; @@ -1414,6 +1438,7 @@ static int copy_tree_node(GtkTreeIter *a, GtkTreeIter *b) DIVE_CYLINDER, &cylinder_text, DIVE_SAC, &store_dive.sac, DIVE_OTU, &store_dive.otu, + DIVE_MAXCNS, &store_dive.maxcns, DIVE_LOCATION, &store_dive.location, -1); gtk_tree_store_set(STORE(dive_list), b, @@ -1429,6 +1454,7 @@ static int copy_tree_node(GtkTreeIter *a, GtkTreeIter *b) DIVE_CYLINDER, cylinder_text, DIVE_SAC, store_dive.sac, DIVE_OTU, store_dive.otu, + DIVE_MAXCNS, store_dive.maxcns, DIVE_LOCATION, store_dive.location, -1); free(cylinder_text); @@ -2337,6 +2363,7 @@ GtkWidget *dive_list_create(void) G_TYPE_INT, /* Nitrox */ G_TYPE_INT, /* SAC */ G_TYPE_INT, /* OTU */ + G_TYPE_INT, /* MAXCNS */ G_TYPE_STRING /* Location */ ); dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS, @@ -2353,6 +2380,7 @@ GtkWidget *dive_list_create(void) G_TYPE_INT, /* Nitrox */ G_TYPE_INT, /* SAC */ G_TYPE_INT, /* OTU */ + G_TYPE_INT, /* MAXCNS */ G_TYPE_STRING /* Location */ ); dive_list.model = dive_list.treemodel; @@ -2380,6 +2408,7 @@ GtkWidget *dive_list_create(void) dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX); dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC); dive_list.otu = divelist_column(&dive_list, dl_column + DIVE_OTU); + dive_list.maxcns = divelist_column(&dive_list, dl_column + DIVE_MAXCNS); dive_list.location = divelist_column(&dive_list, dl_column + DIVE_LOCATION); fill_dive_list(); diff --git a/gtk-gui.c b/gtk-gui.c index 9edf408b9..769ddee6f 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -477,6 +477,7 @@ static void name(GtkWidget *w, gpointer data) \ } OPTIONCALLBACK(otu_toggle, visible_cols.otu) +OPTIONCALLBACK(maxcns_toggle, visible_cols.maxcns) OPTIONCALLBACK(sac_toggle, visible_cols.sac) OPTIONCALLBACK(nitrox_toggle, visible_cols.nitrox) OPTIONCALLBACK(temperature_toggle, visible_cols.temperature) @@ -683,6 +684,11 @@ static void preferences_dialog(GtkWidget *w, gpointer data) gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL); + button = gtk_check_button_new_with_label(_("maxCNS")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.maxcns); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(maxcns_toggle), NULL); + frame = gtk_frame_new(_("Profile Settings")); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); @@ -782,6 +788,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(visible_cols.nitrox)); subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac)); subsurface_set_conf("OTU", PREF_BOOL, BOOL_TO_PTR(visible_cols.otu)); + subsurface_set_conf("MAXCNS", PREF_BOOL, BOOL_TO_PTR(visible_cols.maxcns)); subsurface_set_conf("divelist_font", PREF_STRING, divelist_font); subsurface_set_conf("autogroup", PREF_BOOL, BOOL_TO_PTR(autogroup)); @@ -1139,6 +1146,7 @@ void init_ui(int *argcp, char ***argvp) visible_cols.suit = PTR_TO_BOOL(subsurface_get_conf("SUIT", PREF_BOOL)); visible_cols.nitrox = PTR_TO_BOOL(subsurface_get_conf("NITROX", PREF_BOOL)); visible_cols.otu = PTR_TO_BOOL(subsurface_get_conf("OTU", PREF_BOOL)); + visible_cols.maxcns = PTR_TO_BOOL(subsurface_get_conf("MAXCNS", PREF_BOOL)); visible_cols.sac = PTR_TO_BOOL(subsurface_get_conf("SAC", PREF_BOOL)); partial_pressure_graphs.po2 = PTR_TO_BOOL(subsurface_get_conf("po2graph", PREF_BOOL)); -- cgit v1.2.3-70-g09d2 From 563af1daa781a53fad308870320f99a1903d5058 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 11 Dec 2012 07:43:11 -0800 Subject: Update to the new deco / ndl code in libdivecomputer Early in the libdivecomputer 0.3 development cycle Jef and I implemented deco and ndl as events. That wasn't a wise design choice and we agreed to switch this to be instead new sample types which makes much more sense (and is much more aligned with the way we are handling them inside Subsurface). So this commit tracks the change in libdivecomputer. Since this happened during the development cycle there isn't a way to detect this at compile time - so you need to make sure you have a matching version of libdivecomputer when compiling Subsurface. To make this easier: this commit of Subsurface requires a libdivecomputer version that includes the libdivecomputer commit d5d44c1e0ffd "Convert decostop / ndl to samples". Signed-off-by: Dirk Hohndel --- libdivecomputer.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libdivecomputer.c b/libdivecomputer.c index 99532b63f..51c6a5775 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -97,10 +97,10 @@ static void handle_event(struct divecomputer *dc, struct sample *sample, dc_samp if (value.event.type == SAMPLE_EVENT_SURFACE) return; - /* libdivecomputer 0.3 provides us with deco / ndl information for at least - * the OSTC. Sadly, it does so through events - so we convert this into our - * preferred sample format here */ -#if DC_VERSION_CHECK(0, 3, 0) + /* an early development version of libdivecomputer 0.3 provided us with deco / ndl information for + * a couple of dive computers through events; this got fixed later in the release cycle but for a + * short while I'll keep the code around that converts the events into our preferred sample format here */ +#if 0 if (value.event.type == SAMPLE_EVENT_DECOSTOP) { /* packed value - time in seconds in high 16 bit * depth in m(!) in low 16 bits */ @@ -198,6 +198,14 @@ sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata) case DC_SAMPLE_CNS: sample->cns = cns = value.cns * 100 + 0.5; break; + case DC_SAMPLE_DECO: + if (value.deco.type == DC_DECO_NDL) { + ndl = value.deco.time; + } else if (value.deco.type == DC_DECO_DECOSTOP || + value.deco.type == DC_DECO_DEEPSTOP) { + stopdepth = value.deco.depth * 1000.0 + 0.5; + stoptime = value.deco.time; + } #endif default: break; -- cgit v1.2.3-70-g09d2