From ae1e9b11a537eb0ba9b649cb876dae4cc4628045 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Oct 2015 04:47:39 +0900 Subject: [PATCH 1/2] Fix the momentary SAC calculation for the profile coloring There was tons wrong with the code. You can't just average the SAC rate over different plot entries, since they may not be the same duration. And it got the beginning and end conditions wrong etc etc. This should at least get much closer. And it's structured to be a lot more understandable, I hope, even if the math is a bit more involved (ie doing the proper time-pressure integration etc). It may still have bugs, but at least it now gets the right coloration at the beginning of a dive, and just eye-balling the slope vs color it seems to do the right thing. Signed-off-by: Linus Torvalds Signed-off-by: Robert C. Helling --- profile.c | 120 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 100 insertions(+), 20 deletions(-) diff --git a/profile.c b/profile.c index ee001d728..d39133c21 100644 --- a/profile.c +++ b/profile.c @@ -705,31 +705,111 @@ static void populate_cylinder_pressure_data(int idx, int start, int end, struct } } +/* + * Calculate the sac rate between the two plot entries 'first' and 'last'. + * + * Everything in between has a cylinder pressure, and it's all the same + * cylinder. + */ +static int sac_between(struct dive *dive, struct plot_data *first, struct plot_data *last) +{ + int airuse; + double pressuretime; + pressure_t a, b; + cylinder_t *cyl; + int duration; + + if (first == last) + return 0; + + /* Calculate air use - trivial */ + a.mbar = GET_PRESSURE(first); + b.mbar = GET_PRESSURE(last); + cyl = dive->cylinder + first->cylinderindex; + airuse = gas_volume(cyl, a) - gas_volume(cyl, b); + if (airuse <= 0) + return 0; + + /* Calculate depthpressure integrated over time */ + pressuretime = 0.0; + do { + int depth = (first[0].depth + first[1].depth) / 2; + int time = first[1].sec - first[0].sec; + double atm = depth_to_atm(depth, dive); + + pressuretime += atm * time; + } while (++first < last); + + /* Turn "atmseconds" into "atmminutes" */ + pressuretime /= 60; + + /* SAC = mliter per minute */ + return rint(airuse / pressuretime); +} + +/* + * Try to do the momentary sac rate for this entry, averaging over one + * minute. + */ +static void fill_sac(struct dive *dive, struct plot_info *pi, int idx) +{ + struct plot_data *entry = pi->entry + idx; + struct plot_data *first, *last; + int time; + + if (entry->sac) + return; + + if (!GET_PRESSURE(entry)) + return; + + /* + * Try to go back 30 seconds to get 'first'. + * Stop if the sensor changed, or if we went back too far. + */ + first = entry; + time = entry->sec - 30; + while (idx > 0) { + struct plot_data *prev = first-1; + if (prev->cylinderindex != first->cylinderindex) + break; + if (prev->depth < SURFACE_THRESHOLD && first->depth < SURFACE_THRESHOLD) + break; + if (prev->sec < time) + break; + if (!GET_PRESSURE(prev)) + break; + idx--; + first = prev; + } + + /* Now find an entry a minute after the first one */ + last = first; + time = first->sec + 60; + while (++idx < pi->nr) { + struct plot_data *next = last+1; + if (next->cylinderindex != last->cylinderindex) + break; + if (next->depth < SURFACE_THRESHOLD && last->depth < SURFACE_THRESHOLD) + break; + if (next->sec > time) + break; + if (!GET_PRESSURE(next)) + break; + last = next; + } + + /* Ok, now calculate the SAC between 'first' and 'last' */ + entry->sac = sac_between(dive, first, last); +} + static void calculate_sac(struct dive *dive, struct plot_info *pi) { int i = 0, last = 0; struct plot_data *last_entry = NULL; - for (i = 0; i < pi->nr; i++) { - struct plot_data *entry = pi->entry + i; - if (entry->sac) - continue; - if (!last_entry || last_entry->cylinderindex != entry->cylinderindex) { - last = i; - last_entry = entry; - entry->sac = get_local_sac(entry, pi->entry + i + 1, dive); - } else { - int j; - entry->sac = 0; - for (j = last; j < i; j++) - entry->sac += get_local_sac(pi->entry + j, pi->entry + j + 1, dive); - entry->sac /= (i - last); - if (entry->sec - last_entry->sec >= SAC_WINDOW) { - last++; - last_entry = pi->entry + last; - } - } - } + for (i = 0; i < pi->nr; i++) + fill_sac(dive, pi, i); } static void populate_secondary_sensor_data(struct divecomputer *dc, struct plot_info *pi) From 31d1d1f4217cab43c1fa6ca5612c2cc3ccc656eb Mon Sep 17 00:00:00 2001 From: "Robert C. Helling" Date: Fri, 23 Oct 2015 22:11:00 +0200 Subject: [PATCH 2/2] Don't change pen color when for zero SAC When for individual plot entries the SAC is zero this comes from gas changes and the SAC calculation needing a bit more pressure data and not from the diver switching to freediving mode. So we shold not change the pen color on the pressure line for that but pretend we are still breathing at the previous SAC. Signed-off-by: Robert C. Helling --- qt-ui/profile/diveprofileitem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qt-ui/profile/diveprofileitem.cpp b/qt-ui/profile/diveprofileitem.cpp index 986a0eac0..cff2b1f43 100644 --- a/qt-ui/profile/diveprofileitem.cpp +++ b/qt-ui/profile/diveprofileitem.cpp @@ -780,7 +780,8 @@ void DiveGasPressureItem::paint(QPainter *painter, const QStyleOptionGraphicsIte Q_FOREACH (const QPolygonF &poly, polygons) { entry = dataModel->data().entry; for (int i = 1, count = poly.count(); i < count; i++, entry++) { - pen.setBrush(getSacColor(entry->sac, displayed_dive.sac)); + if (entry->sac) + pen.setBrush(getSacColor(entry->sac, displayed_dive.sac)); painter->setPen(pen); painter->drawLine(poly[i - 1], poly[i]); }