From c634a07e38a4196bc64d05e5d58b183d32e84092 Mon Sep 17 00:00:00 2001 From: "Robert C. Helling" Date: Wed, 18 Aug 2021 23:38:15 +0200 Subject: [PATCH] Planner: Correctly compute CNS and OTU for bailout segments For dives with mixed divemode, one needs to check sample.setpoint to figure out if the segment is an OC segment and the po2 needs to be computed from the gasmix and ambient pressure. This fixes #3310 Signed-off-by: Robert C. Helling --- CHANGELOG.md | 1 + core/divelist.c | 74 +++++++++++++++++++++++++++---------------------- core/planner.c | 2 ++ 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc2e06a04..97309fe64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ - mobile: add ability to edit tags +- Correct OTU and CNS calculations for planned bailout - replace dive computer management with a new implementation, this fixes rare cases where Subsurface unnecessarily download already downloaded dives, but also means that some older style dive computer can no longer have nicknames diff --git a/core/divelist.c b/core/divelist.c index 9beea4d7c..1d856118c 100644 --- a/core/divelist.c +++ b/core/divelist.c @@ -85,6 +85,35 @@ static int active_o2(const struct dive *dive, const struct divecomputer *dc, dur return get_o2(gas); } +// Do not call on first sample as it acccesses the previous sample +static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, const struct sample *sample) +{ + int po2i, po2f, po2; + const struct sample *psample = sample - 1; + // Use sensor[0] if available + if ((dc->divemode == CCR || dc->divemode == PSCR) && sample->o2sensor[0].mbar) { + po2i = psample->o2sensor[0].mbar; + po2f = sample->o2sensor[0].mbar; // then use data from the first o2 sensor + po2 = (po2f + po2i) / 2; + } else if (sample->setpoint.mbar > 0) { + po2 = MIN((int) sample->setpoint.mbar, + depth_to_mbar(sample->depth.mm, dive)); + } else { + double amb_presure = depth_to_bar(sample->depth.mm, dive); + double pamb_pressure = depth_to_bar(psample->depth.mm , dive); + if (dc->divemode == PSCR) { + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample->time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample->time)); + } else { + int o2 = active_o2(dive, dc, psample->time); // ... calculate po2 from depth and FiO2. + po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment + po2f = lrint(o2 * amb_presure); // (final) po2 at end of segment + } + po2 = (po2i + po2f) / 2; + } + return po2; +} + /* Calculate OTU for a dive - this only takes the first divecomputer into account. Implement the protocol in Erik Baker's document "Oxygen Toxicity Calculations". This code implements a third-order continuous approximation of Baker's Eq. 2 and enables OTU @@ -104,15 +133,19 @@ static int calculate_otu(const struct dive *dive) struct sample *sample = dc->sample + i; struct sample *psample = sample - 1; t = sample->time.seconds - psample->time.seconds; - if ((dc->divemode == CCR || dc->divemode == PSCR) && sample->o2sensor[0].mbar) { // if dive computer has o2 sensor(s) (CCR & PSCR) .. + // if there is sensor data use sensor[0] + if ((dc->divemode == CCR || dc->divemode == PSCR) && sample->o2sensor[0].mbar) { po2i = psample->o2sensor[0].mbar; po2f = sample->o2sensor[0].mbar; // ... use data from the first o2 sensor } else { - if (dc->divemode == CCR) { - po2i = MIN((int) psample->setpoint.mbar, - depth_to_mbar(psample->depth.mm, dive)); // if CCR has no o2 sensors then use setpoint + if (sample->setpoint.mbar > 0) { po2f = MIN((int) sample->setpoint.mbar, depth_to_mbar(sample->depth.mm, dive)); + if (psample->setpoint.mbar > 0) + po2i = MIN((int) psample->setpoint.mbar, + depth_to_mbar(psample->depth.mm, dive)); + else + po2i = po2f; } else { // For OC and rebreather without o2 sensor/setpoint double amb_presure = depth_to_bar(sample->depth.mm, dive); double pamb_pressure = depth_to_bar(psample->depth.mm , dive); @@ -162,42 +195,17 @@ static double calculate_cns_dive(const struct dive *dive) /* Calculate the CNS for each sample in this dive and sum them */ for (n = 1; n < dc->samples; n++) { int t; - int po2i, po2f; - bool trueo2 = false; + int po2; struct sample *sample = dc->sample + n; struct sample *psample = sample - 1; t = sample->time.seconds - psample->time.seconds; - if ((dc->divemode == CCR || dc->divemode == PSCR) && sample->o2sensor[0].mbar) { // if dive computer has o2 sensor(s) (CCR & PSCR) - po2i = psample->o2sensor[0].mbar; - po2f = sample->o2sensor[0].mbar; // then use data from the first o2 sensor - trueo2 = true; - } - if ((dc->divemode == CCR) && (!trueo2)) { - po2i = MIN((int) psample->setpoint.mbar, - depth_to_mbar(psample->depth.mm, dive)); // if CCR has no o2 sensors then use setpoint - po2f = MIN((int) sample->setpoint.mbar, - depth_to_mbar(sample->depth.mm, dive)); - trueo2 = true; - } - if (!trueo2) { - double amb_presure = depth_to_bar(sample->depth.mm, dive); - double pamb_pressure = depth_to_bar(psample->depth.mm , dive); - if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample->time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample->time)); - } else { - int o2 = active_o2(dive, dc, psample->time); // ... calculate po2 from depth and FiO2. - po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment - po2f = lrint(o2 * amb_presure); // (final) po2 at end of segment - } - } - int po2avg = (po2i + po2f) / 2; // po2i now holds the mean po2 of initial and final po2 values of segment. + po2 = get_sample_o2(dive, dc, sample); /* Don't increase CNS when po2 below 500 matm */ - if (po2avg <= 500) + if (po2 <= 500) continue; // This formula is the result of fitting two lines to the Log of the NOAA CNS table - rate = po2i <= 1500 ? exp(-11.7853 + 0.00193873 * po2avg) : exp(-23.6349 + 0.00980829 * po2avg); + rate = po2 <= 1500 ? exp(-11.7853 + 0.00193873 * po2) : exp(-23.6349 + 0.00980829 * po2); cns += (double) t * rate * 100.0; } return cns; diff --git a/core/planner.c b/core/planner.c index 98c75df1e..598e568bf 100644 --- a/core/planner.c +++ b/core/planner.c @@ -284,6 +284,8 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, } sample = prepare_sample(dc); sample[-1].setpoint.mbar = po2; + if (po2) + sample[-1].o2sensor[0].mbar = po2; sample->time.seconds = lasttime + 1; sample->depth = lastdepth; sample->manually_entered = dp->entered;