mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Profile: Improve Display of Bailout / Loop Events.
For dives in CCR mode, show 'bailout' and 'on loop' events whenever a gas switch from a diluent gas to a bailout gas and vice versa happens. Signed-off-by: Michael Keller <github@ike.ch>
This commit is contained in:
parent
de12d3a6ea
commit
07898f277c
16 changed files with 183 additions and 121 deletions
103
core/dive.cpp
103
core/dive.cpp
|
@ -56,7 +56,7 @@ dive::~dive() = default;
|
|||
* equals the appropriate enum value [oxygen, diluent, bailout] given by cylinder_use_type.
|
||||
* A negative number returned indicates that a match could not be found.
|
||||
* Call parameters: dive = the dive being processed
|
||||
* cylinder_use_type = an enum, one of {oxygen, diluent, bailout} */
|
||||
* cylinder_use_type = an enum, one of {oxygen, diluent, bailout} */
|
||||
static int get_cylinder_idx_by_use(const struct dive &dive, enum cylinderuse cylinder_use_type)
|
||||
{
|
||||
auto it = std::find_if(dive.cylinders.begin(), dive.cylinders.end(), [cylinder_use_type]
|
||||
|
@ -156,16 +156,18 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second
|
|||
add_event_to_dc(dc, std::move(ev));
|
||||
}
|
||||
|
||||
struct gasmix dive::get_gasmix_from_event(const struct event &ev) const
|
||||
std::pair<const struct gasmix, divemode_t> dive::get_gasmix_from_event(const struct event &ev, const struct divecomputer &dc) const
|
||||
{
|
||||
if (ev.is_gaschange()) {
|
||||
int index = ev.gas.index;
|
||||
// FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course.
|
||||
if (index >= 0 && static_cast<size_t>(index) < cylinders.size() + 1)
|
||||
return get_cylinder(index)->gasmix;
|
||||
return ev.gas.mix;
|
||||
if (index >= 0 && static_cast<size_t>(index) < cylinders.size() + 1) {
|
||||
const cylinder_t *cylinder = get_cylinder(index);
|
||||
return std::make_pair(cylinder->gasmix, get_effective_divemode(dc, *cylinder));
|
||||
}
|
||||
return std::make_pair(ev.gas.mix, dc.divemode);
|
||||
}
|
||||
return gasmix_air;
|
||||
return std::make_pair(gasmix_air, dc.divemode);
|
||||
}
|
||||
|
||||
// we need this to be uniq. oh, and it has no meaning whatsoever
|
||||
|
@ -307,7 +309,7 @@ static int get_cylinder_used(const struct dive *dive, bool used[])
|
|||
* Some dive computers give cylinder indices, some
|
||||
* give just the gas mix.
|
||||
*/
|
||||
int dive::get_cylinder_index(const struct event &ev) const
|
||||
int dive::get_cylinder_index(const struct event &ev, const struct divecomputer &dc) const
|
||||
{
|
||||
if (ev.gas.index >= 0)
|
||||
return ev.gas.index;
|
||||
|
@ -320,7 +322,7 @@ int dive::get_cylinder_index(const struct event &ev) const
|
|||
*/
|
||||
report_info("Still looking up cylinder based on gas mix in get_cylinder_index()!");
|
||||
|
||||
gasmix mix = get_gasmix_from_event(ev);
|
||||
gasmix mix = get_gasmix_from_event(ev, dc).first;
|
||||
int best = find_best_gasmix_match(mix, cylinders);
|
||||
return best < 0 ? 0 : best;
|
||||
}
|
||||
|
@ -1629,6 +1631,38 @@ bool is_cylinder_use_appropriate(const struct divecomputer &dc, const cylinder_t
|
|||
return true;
|
||||
}
|
||||
|
||||
divemode_t get_effective_divemode(const struct divecomputer &dc, const struct cylinder_t &cylinder)
|
||||
{
|
||||
divemode_t divemode = dc.divemode;
|
||||
if (divemode == CCR && cylinder.cylinder_use == OC_GAS)
|
||||
divemode = OC;
|
||||
|
||||
return divemode;
|
||||
}
|
||||
|
||||
std::tuple<divemode_t, int, const struct gasmix *> get_dive_status_at(const struct dive &dive, const struct divecomputer &dc, int seconds, divemode_loop *loop_mode, gasmix_loop *loop_gas)
|
||||
{
|
||||
if (!loop_mode)
|
||||
loop_mode = new divemode_loop(dc);
|
||||
if (!loop_gas)
|
||||
loop_gas = new gasmix_loop(dive, dc);
|
||||
auto [divemode, divemode_time] = loop_mode->at(seconds);
|
||||
auto [cylinder_index, gasmix_time] = loop_gas->cylinder_index_at(seconds);
|
||||
const struct gasmix *gasmix;
|
||||
if (cylinder_index == -1) {
|
||||
gasmix = &gasmix_air;
|
||||
if (gasmix_time >= divemode_time)
|
||||
divemode = OC;
|
||||
} else {
|
||||
const struct cylinder_t *cylinder = dive.get_cylinder(cylinder_index);
|
||||
gasmix = &cylinder->gasmix;
|
||||
if (gasmix_time >= divemode_time)
|
||||
divemode = get_effective_divemode(dc, *cylinder);
|
||||
}
|
||||
|
||||
return std::make_tuple(divemode, cylinder_index, gasmix);
|
||||
}
|
||||
|
||||
/*
|
||||
* Merging cylinder information is non-trivial, because the two dive computers
|
||||
* may have different ideas of what the different cylinder indexing is.
|
||||
|
@ -2573,25 +2607,27 @@ gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) :
|
|||
|
||||
std::pair<int, int> gasmix_loop::next_cylinder_index()
|
||||
{
|
||||
if (dive.cylinders.empty())
|
||||
return std::make_pair(-1, INT_MAX);
|
||||
|
||||
if (first_run) {
|
||||
next_event = loop.next();
|
||||
last_cylinder_index = 0; // default to first cylinder
|
||||
last_time = 0;
|
||||
if (next_event && ((!dc.samples.empty() && next_event->time.seconds == dc.samples[0].time.seconds) || next_event->time.seconds <= 1)) {
|
||||
last_cylinder_index = dive.get_cylinder_index(*next_event);
|
||||
last_time = next_event->time.seconds;
|
||||
next_event = loop.next();
|
||||
} else if (dc.divemode == CCR) {
|
||||
last_cylinder_index = std::max(get_cylinder_idx_by_use(dive, DILUENT), last_cylinder_index);
|
||||
}
|
||||
|
||||
first_run = false;
|
||||
|
||||
if (dive.cylinders.empty()) {
|
||||
last_cylinder_index = -1;
|
||||
last_time = 0;
|
||||
} else {
|
||||
last_cylinder_index = 0; // default to first cylinder
|
||||
last_time = 0;
|
||||
if (next_event && ((!dc.samples.empty() && next_event->time.seconds == dc.samples[0].time.seconds) || next_event->time.seconds <= 1)) {
|
||||
last_cylinder_index = dive.get_cylinder_index(*next_event, dc);
|
||||
last_time = next_event->time.seconds;
|
||||
next_event = loop.next();
|
||||
} else if (dc.divemode == CCR) {
|
||||
last_cylinder_index = std::max(get_cylinder_idx_by_use(dive, DILUENT), last_cylinder_index);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (next_event) {
|
||||
last_cylinder_index = dive.get_cylinder_index(*next_event);
|
||||
last_cylinder_index = dive.get_cylinder_index(*next_event, dc);
|
||||
last_time = next_event->time.seconds;
|
||||
next_event = loop.next();
|
||||
} else {
|
||||
|
@ -2605,16 +2641,9 @@ std::pair<int, int> gasmix_loop::next_cylinder_index()
|
|||
|
||||
std::pair<gasmix, int> gasmix_loop::next()
|
||||
{
|
||||
if (first_run && dive.cylinders.empty()) {
|
||||
first_run = false;
|
||||
|
||||
// return one cylinder of air if we don't have any cylinders
|
||||
return std::make_pair(gasmix_air, 0);
|
||||
}
|
||||
|
||||
next_cylinder_index();
|
||||
|
||||
return std::make_pair(last_cylinder_index < 0 ? gasmix_invalid : dive.get_cylinder(last_cylinder_index)->gasmix, last_time);
|
||||
return get_last_gasmix();
|
||||
}
|
||||
|
||||
std::pair<int, int> gasmix_loop::cylinder_index_at(int time)
|
||||
|
@ -2630,13 +2659,9 @@ std::pair<int, int> gasmix_loop::cylinder_index_at(int time)
|
|||
|
||||
std::pair<gasmix, int> gasmix_loop::at(int time)
|
||||
{
|
||||
if (dive.cylinders.empty())
|
||||
// return air if we don't have any cylinders
|
||||
return std::make_pair(gasmix_air, 0);
|
||||
|
||||
cylinder_index_at(time);
|
||||
|
||||
return std::make_pair(last_cylinder_index < 0 ? gasmix_invalid : dive.get_cylinder(last_cylinder_index)->gasmix, last_time);
|
||||
return get_last_gasmix();
|
||||
}
|
||||
|
||||
bool gasmix_loop::has_next() const
|
||||
|
@ -2644,6 +2669,14 @@ bool gasmix_loop::has_next() const
|
|||
return first_run || (!dive.cylinders.empty() && next_event);
|
||||
}
|
||||
|
||||
std::pair<gasmix, int> gasmix_loop::get_last_gasmix()
|
||||
{
|
||||
if (last_cylinder_index < 0 && last_time == 0)
|
||||
return std::make_pair(gasmix_air, 0);
|
||||
|
||||
return std::make_pair(last_cylinder_index < 0 ? gasmix_invalid : dive.get_cylinder(last_cylinder_index)->gasmix, last_time);
|
||||
}
|
||||
|
||||
/* get the gas at a certain time during the dive */
|
||||
/* If there is a gasswitch at that time, it returns the new gasmix */
|
||||
struct gasmix dive::get_gasmix_at_time(const struct divecomputer &dc, duration_t time) const
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "divemode.h"
|
||||
#include "divecomputer.h"
|
||||
#include "equipment.h"
|
||||
#include "event.h"
|
||||
#include "picture.h" // TODO: remove
|
||||
#include "tag.h"
|
||||
|
||||
|
@ -103,9 +104,9 @@ struct dive {
|
|||
bool likely_same(const struct dive &b) const;
|
||||
bool is_cylinder_used(int idx) const;
|
||||
bool is_cylinder_prot(int idx) const;
|
||||
int get_cylinder_index(const struct event &ev) const;
|
||||
int get_cylinder_index(const struct event &ev, const struct divecomputer &dc) const;
|
||||
bool has_gaschange_event(const struct divecomputer *dc, int idx) const;
|
||||
struct gasmix get_gasmix_from_event(const struct event &ev) const;
|
||||
std::pair<const struct gasmix, divemode_t> get_gasmix_from_event(const struct event &ev, const struct divecomputer &dc) const;
|
||||
struct gasmix get_gasmix_at_time(const struct divecomputer &dc, duration_t time) const;
|
||||
cylinder_t *get_cylinder(int idx);
|
||||
cylinder_t *get_or_create_cylinder(int idx);
|
||||
|
@ -152,6 +153,8 @@ struct dive_or_trip {
|
|||
extern void cylinder_renumber(struct dive &dive, int mapping[]);
|
||||
extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused);
|
||||
extern bool is_cylinder_use_appropriate(const struct divecomputer &dc, const cylinder_t &cyl, bool allowNonUsable);
|
||||
extern divemode_t get_effective_divemode(const struct divecomputer &dc, const struct cylinder_t &cylinder);
|
||||
extern std::tuple<divemode_t, int, const struct gasmix *> get_dive_status_at(const struct dive &dive, const struct divecomputer &dc, int seconds, divemode_loop *loop_mode = nullptr, gasmix_loop *loop_gas = nullptr);
|
||||
|
||||
/* Data stored when copying a dive */
|
||||
struct dive_paste_data {
|
||||
|
|
|
@ -200,19 +200,20 @@ void fake_dc(struct divecomputer *dc)
|
|||
}
|
||||
|
||||
divemode_loop::divemode_loop(const struct divecomputer &dc) :
|
||||
last(dc.divemode), loop("modechange", dc)
|
||||
last(dc.divemode), last_time(0), loop("modechange", dc)
|
||||
{
|
||||
/* on first invocation, get first event (if any) */
|
||||
ev = loop.next();
|
||||
}
|
||||
|
||||
divemode_t divemode_loop::at(int time)
|
||||
std::pair<divemode_t, int> divemode_loop::at(int time)
|
||||
{
|
||||
while (ev && ev->time.seconds <= time) {
|
||||
last = static_cast<divemode_t>(ev->value);
|
||||
last_time = ev->time.seconds;
|
||||
ev = loop.next();
|
||||
}
|
||||
return last;
|
||||
return std::make_pair(last, last_time);
|
||||
}
|
||||
|
||||
/* helper function to make it easier to work with our structures
|
||||
|
|
|
@ -424,8 +424,8 @@ static void add_dive_to_deco(struct deco_state *ds, const struct dive &dive, boo
|
|||
{
|
||||
const struct divecomputer *dc = &dive.dcs[0];
|
||||
|
||||
gasmix_loop loop(dive, dive.dcs[0]);
|
||||
divemode_loop loop_d(dive.dcs[0]);
|
||||
gasmix_loop loop_gas(dive, dive.dcs[0]);
|
||||
divemode_loop loop_mode(dive.dcs[0]);
|
||||
for (auto [psample, sample]: pairwise_range(dc->samples)) {
|
||||
int t0 = psample.time.seconds;
|
||||
int t1 = sample.time.seconds;
|
||||
|
@ -433,9 +433,9 @@ static void add_dive_to_deco(struct deco_state *ds, const struct dive &dive, boo
|
|||
|
||||
for (j = t0; j < t1; j++) {
|
||||
depth_t depth = interpolate(psample.depth, sample.depth, j - t0, t1 - t0);
|
||||
auto gasmix = loop.at(j).first;
|
||||
add_segment(ds, dive.depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar,
|
||||
loop_d.at(j), dive.sac,
|
||||
[[maybe_unused]] auto [divemode, _cylinder_index, gasmix] = get_dive_status_at(dive, dive.dcs[0], j);
|
||||
add_segment(ds, dive.depth_to_bar(depth), *gasmix, 1, sample.setpoint.mbar,
|
||||
divemode, dive.sac,
|
||||
in_planner);
|
||||
}
|
||||
}
|
||||
|
|
13
core/event.h
13
core/event.h
|
@ -73,19 +73,20 @@ class gasmix_loop {
|
|||
const struct event *next_event;
|
||||
int last_cylinder_index;
|
||||
int last_time;
|
||||
std::pair<gasmix, int> get_last_gasmix();
|
||||
public:
|
||||
gasmix_loop(const struct dive &dive, const struct divecomputer &dc);
|
||||
// Return the next cylinder index / gasmix from the list of gas switches
|
||||
// and the time in seconds when this gas switch happened
|
||||
// (including the potentially imaginary first gas switch to cylinder 0 / air)
|
||||
std::pair<int, int> next_cylinder_index(); // -1 -> end
|
||||
std::pair<gasmix, int> next(); // gasmix_invalid -> end
|
||||
std::pair<int, int> next_cylinder_index(); // <-1, 0> => implicit air cylinder, <-1, INT_MAX> => end
|
||||
std::pair<gasmix, int> next(); // <gasmix_invalid, INT_MAX> => end
|
||||
|
||||
// Return the cylinder index / gasmix at a given time during the dive
|
||||
// and the time in seconds when this switch to this gas happened
|
||||
// (including the potentially imaginary first gas switch to cylinder 0 / air)
|
||||
std::pair<int, int> cylinder_index_at(int time); // -1 -> end
|
||||
std::pair<gasmix, int> at(int time); // gasmix_invalid -> end
|
||||
std::pair<int, int> cylinder_index_at(int time); // <-1, 0> => implicit air cylinder
|
||||
std::pair<gasmix, int> at(int time);
|
||||
|
||||
bool has_next() const;
|
||||
};
|
||||
|
@ -93,12 +94,14 @@ public:
|
|||
/* Get divemodes at increasing timestamps. */
|
||||
class divemode_loop {
|
||||
divemode_t last;
|
||||
int last_time;
|
||||
event_loop loop;
|
||||
const struct event *ev;
|
||||
public:
|
||||
divemode_loop(const struct divecomputer &dc);
|
||||
// Return the divemode at a given time during the dive
|
||||
divemode_t at(int time);
|
||||
// and the time in seconds when the switch to this divemode has happened
|
||||
std::pair<divemode_t, int> at(int time);
|
||||
};
|
||||
|
||||
extern const struct event *get_first_event(const struct divecomputer &dc, const std::string &name);
|
||||
|
|
|
@ -198,7 +198,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
|
|||
dump_pr_track(cyl, track_pr);
|
||||
#endif
|
||||
|
||||
/* Transfer interpolated cylinder pressures from pr_track strucktures to plotdata
|
||||
/* Transfer interpolated cylinder pressures from pr_track structures to plotdata
|
||||
* Go down the list of tank pressures in plot_info. Align them with the start &
|
||||
* end times of each profile segment represented by a pr_track_t structure. Get
|
||||
* the accumulated pressure_depths from the pr_track_t structures and then
|
||||
|
@ -248,7 +248,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
|
|||
last_segment = it;
|
||||
}
|
||||
|
||||
if(dive->get_cylinder(cyl)->cylinder_use == OC_GAS) {
|
||||
if (dive->get_cylinder(cyl)->cylinder_use == OC_GAS) {
|
||||
|
||||
/* if this segment has pressure_time, then calculate a new interpolated pressure */
|
||||
if (interpolate.pressure_time) {
|
||||
|
@ -358,16 +358,15 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
|
|||
int pressure = get_plot_sensor_pressure(pi, i, sensor);
|
||||
int time = entry.sec;
|
||||
|
||||
[[maybe_unused]] auto [divemode, cylinder_index, _gasmix] = get_dive_status_at(*dive, *dc, time, &loop_mode, &loop_gas);
|
||||
if (has_gaschange) {
|
||||
cyl = loop_gas.cylinder_index_at(time).first;
|
||||
cyl = cylinder_index;
|
||||
if (cyl < 0)
|
||||
cyl = sensor;
|
||||
}
|
||||
|
||||
divemode_t dmode = loop_mode.at(time);
|
||||
|
||||
if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment.
|
||||
entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5);
|
||||
entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[divemode] + 0.5);
|
||||
track[current].pressure_time += entry.pressure_time;
|
||||
track[current].t_end = entry.sec;
|
||||
if (pressure)
|
||||
|
|
|
@ -83,7 +83,7 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_
|
|||
if (event.time.seconds > time.seconds)
|
||||
break;
|
||||
if (event.name == "gaschange")
|
||||
cylinder_idx = dive->get_cylinder_index(event);
|
||||
cylinder_idx = dive->get_cylinder_index(event, *dc);
|
||||
}
|
||||
return cylinder_idx;
|
||||
}
|
||||
|
@ -124,7 +124,8 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct
|
|||
return 0;
|
||||
|
||||
const struct sample *psample = nullptr;
|
||||
divemode_loop loop(*dc);
|
||||
gasmix_loop loop_gas(*dive, *dc);
|
||||
divemode_loop loop_mode(*dc);
|
||||
for (auto &sample: dc->samples) {
|
||||
o2pressure_t setpoint = psample ? psample->setpoint
|
||||
: sample.setpoint;
|
||||
|
@ -156,7 +157,8 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct
|
|||
ds->max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar;
|
||||
}
|
||||
|
||||
divemode_t divemode = loop.at(t0.seconds + 1);
|
||||
[[maybe_unused]] auto [divemode, _cylinder_index, _gasmix] = get_dive_status_at(*dive, *dc, t0.seconds + 1, &loop_mode, &loop_gas);
|
||||
|
||||
interpolate_transition(ds, dive, t0, t1, lastdepth, sample.depth, gas, setpoint, divemode);
|
||||
psample = &sample;
|
||||
t0 = t1;
|
||||
|
@ -606,7 +608,7 @@ std::vector<decostop> plan(struct deco_state *ds, struct diveplan &diveplan, str
|
|||
deco_state_cache bottom_cache;
|
||||
int po2;
|
||||
int transitiontime, gi;
|
||||
int current_cylinder, stop_cylinder;
|
||||
int stop_cylinder;
|
||||
size_t stopidx;
|
||||
bool stopping = false;
|
||||
bool pendinggaschange = false;
|
||||
|
@ -622,7 +624,6 @@ std::vector<decostop> plan(struct deco_state *ds, struct diveplan &diveplan, str
|
|||
int laststoptime = timestep;
|
||||
bool o2breaking = false;
|
||||
struct divecomputer *dc = dive->get_dc(dcNr);
|
||||
enum divemode_t divemode = dc->divemode;
|
||||
|
||||
set_gf(diveplan.gflow, diveplan.gfhigh);
|
||||
set_vpmb_conservatism(diveplan.vpmb_conservatism);
|
||||
|
@ -661,11 +662,9 @@ std::vector<decostop> plan(struct deco_state *ds, struct diveplan &diveplan, str
|
|||
/* Keep time during the ascend */
|
||||
bottom_time = clock = previous_point_time = sample.time.seconds;
|
||||
|
||||
current_cylinder = get_cylinderid_at_time(dive, dc, sample.time);
|
||||
// Find the divemode at the end of the dive
|
||||
divemode_loop loop(*dc);
|
||||
divemode = loop.at(bottom_time);
|
||||
gas = dive->get_cylinder(current_cylinder)->gasmix;
|
||||
[[maybe_unused]] auto [divemode, current_cylinder, gasmix] = get_dive_status_at(*dive, *dc, bottom_time);
|
||||
gas = *gasmix;
|
||||
|
||||
po2 = sample.setpoint.mbar;
|
||||
depth_t depth = sample.depth;
|
||||
|
|
|
@ -588,7 +588,7 @@ void diveplan::add_plan_to_notes(struct dive &dive, bool show_disclaimer, planne
|
|||
std::string temp;
|
||||
struct gasmix gasmix = dive.get_cylinder(dp.cylinderid)->gasmix;
|
||||
|
||||
divemode_t current_divemode = loop.at(dp.time);
|
||||
divemode_t current_divemode = loop.at(dp.time).first;
|
||||
amb = dive.depth_to_atm(dp.depth);
|
||||
gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode);
|
||||
|
||||
|
|
|
@ -865,8 +865,8 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_
|
|||
if (decoMode(in_planner) == VPMB)
|
||||
ds->first_ceiling_pressure.mbar = dive->depth_to_mbar(first_ceiling);
|
||||
|
||||
gasmix_loop loop(*dive, *dc);
|
||||
divemode_loop loop_d(*dc);
|
||||
gasmix_loop loop_gas(*dive, *dc);
|
||||
divemode_loop loop_mode(*dc);
|
||||
for (i = 1; i < pi.nr; i++) {
|
||||
struct plot_data &entry = pi.entry[i];
|
||||
struct plot_data &prev = pi.entry[i - 1];
|
||||
|
@ -874,8 +874,8 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_
|
|||
int time_stepsize = 20;
|
||||
depth_t max_ceiling;
|
||||
|
||||
divemode_t current_divemode = loop_d.at(entry.sec);
|
||||
struct gasmix gasmix = loop.at(t1).first;
|
||||
[[maybe_unused]] auto [current_divemode, _cylinder_index, gasmix] = get_dive_status_at(*dive, *dc, entry.sec, &loop_mode, &loop_gas);
|
||||
|
||||
entry.ambpressure = dive->depth_to_bar(entry.depth);
|
||||
entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE;
|
||||
if (t0 > t1) {
|
||||
|
@ -887,7 +887,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_
|
|||
for (int j = t0 + time_stepsize; j <= t1; j += time_stepsize) {
|
||||
depth_t new_depth = interpolate(prev.depth, entry.depth, j - t0, t1 - t0);
|
||||
add_segment(ds, dive->depth_to_bar(new_depth),
|
||||
gasmix, time_stepsize, entry.o2pressure.mbar, current_divemode, entry.sac, in_planner);
|
||||
*gasmix, time_stepsize, entry.o2pressure.mbar, current_divemode, entry.sac, in_planner);
|
||||
entry.icd_warning = ds->icd_warning;
|
||||
if ((t1 - j < time_stepsize) && (j < t1))
|
||||
time_stepsize = t1 - j;
|
||||
|
@ -987,7 +987,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_
|
|||
/* We are going to mess up deco state, so store it for later restore */
|
||||
deco_state_cache cache_data;
|
||||
cache_data.cache(ds);
|
||||
calculate_ndl_tts(ds, dive, entry, gasmix, surface_pressure, current_divemode, in_planner);
|
||||
calculate_ndl_tts(ds, dive, entry, *gasmix, surface_pressure, current_divemode, in_planner);
|
||||
if (decoMode(in_planner) == VPMB && !in_planner && i == pi.nr - 1)
|
||||
final_tts = entry.tts_calc;
|
||||
/* Restore "real" deco state for next real time step */
|
||||
|
@ -1104,21 +1104,19 @@ static void calculate_gas_information_new(const struct dive *dive, const struct
|
|||
int i;
|
||||
double amb_pressure;
|
||||
|
||||
gasmix_loop loop(*dive, *dc);
|
||||
divemode_loop loop_d(*dc);
|
||||
gasmix_loop loop_gas(*dive, *dc);
|
||||
divemode_loop loop_mode(*dc);
|
||||
for (i = 1; i < pi.nr; i++) {
|
||||
double fn2, fhe;
|
||||
struct plot_data &entry = pi.entry[i];
|
||||
|
||||
auto gasmix = loop.at(entry.sec).first;
|
||||
[[maybe_unused]] auto [current_divemode, _cylinder_index, gasmix] = get_dive_status_at(*dive, *dc, entry.sec, &loop_mode, &loop_gas);
|
||||
amb_pressure = dive->depth_to_bar(entry.depth);
|
||||
divemode_t current_divemode = loop_d.at(entry.sec);
|
||||
entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode);
|
||||
entry.pressures = fill_pressures(amb_pressure, *gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode);
|
||||
fn2 = 1000.0 * entry.pressures.n2 / amb_pressure;
|
||||
fhe = 1000.0 * entry.pressures.he / amb_pressure;
|
||||
if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring.
|
||||
struct gasmix gasmix2 = loop.at(entry.sec).first;
|
||||
entry.scr_OC_pO2.mbar = (int) dive->depth_to_mbar(entry.depth) * get_o2(gasmix2) / 1000;
|
||||
entry.scr_OC_pO2.mbar = (int) dive->depth_to_mbar(entry.depth) * get_o2(*gasmix) / 1000;
|
||||
}
|
||||
|
||||
/* Calculate MOD, EAD, END and EADD based on partial pressures calculated before
|
||||
|
@ -1126,7 +1124,7 @@ static void calculate_gas_information_new(const struct dive *dive, const struct
|
|||
* END takes O₂ + N₂ (air) into account ("Narcotic" for trimix dives)
|
||||
* EAD just uses N₂ ("Air" for nitrox dives) */
|
||||
pressure_t modpO2 = { .mbar = (int)(prefs.modpO2 * 1000) };
|
||||
entry.mod = dive->gas_mod(gasmix, modpO2, 1_mm);
|
||||
entry.mod = dive->gas_mod(*gasmix, modpO2, 1_mm);
|
||||
entry.end = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * (1000 - fhe) / 1000.0));
|
||||
entry.ead = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * fn2 / (double)N2_IN_AIR));
|
||||
entry.eadd = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) *
|
||||
|
|
|
@ -373,7 +373,7 @@ static void save_samples(struct membuffer *b, const struct dive &dive, const str
|
|||
save_sample(b, s, dummy, o2sensor);
|
||||
}
|
||||
|
||||
static void save_one_event(struct membuffer *b, const struct dive &dive, const struct event &ev)
|
||||
static void save_one_event(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc, const struct event &ev)
|
||||
{
|
||||
put_format(b, "event %d:%02d", FRACTION_TUPLE(ev.time.seconds, 60));
|
||||
show_index(b, ev.type, "type=", "");
|
||||
|
@ -385,7 +385,7 @@ static void save_one_event(struct membuffer *b, const struct dive &dive, const s
|
|||
show_index(b, ev.value, "value=", "");
|
||||
show_utf8(b, " name=", ev.name.c_str(), "");
|
||||
if (ev.is_gaschange()) {
|
||||
struct gasmix mix = dive.get_gasmix_from_event(ev);
|
||||
struct gasmix mix = dive.get_gasmix_from_event(ev, dc).first;
|
||||
if (ev.gas.index >= 0)
|
||||
show_integer(b, ev.gas.index, "cylinder=", "");
|
||||
put_gasmix(b, mix);
|
||||
|
@ -396,7 +396,7 @@ static void save_one_event(struct membuffer *b, const struct dive &dive, const s
|
|||
static void save_events(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc)
|
||||
{
|
||||
for (auto &ev: dc.events)
|
||||
save_one_event(b, dive, ev);
|
||||
save_one_event(b, dive, dc, ev);
|
||||
}
|
||||
|
||||
static void save_dc(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc)
|
||||
|
|
|
@ -342,7 +342,7 @@ static void save_sample(struct membuffer *b, const struct sample &sample, struct
|
|||
put_format(b, " />\n");
|
||||
}
|
||||
|
||||
static void save_one_event(struct membuffer *b, const struct dive &dive, const struct event &ev)
|
||||
static void save_one_event(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc, const struct event &ev)
|
||||
{
|
||||
put_format(b, " <event time='%d:%02d min'", FRACTION_TUPLE(ev.time.seconds, 60));
|
||||
show_index(b, ev.type, "type='", "'");
|
||||
|
@ -353,7 +353,7 @@ static void save_one_event(struct membuffer *b, const struct dive &dive, const s
|
|||
show_index(b, ev.value, "value='", "'");
|
||||
show_utf8(b, ev.name.c_str(), " name='", "'", 1);
|
||||
if (ev.is_gaschange()) {
|
||||
struct gasmix mix = dive.get_gasmix_from_event(ev);
|
||||
struct gasmix mix = dive.get_gasmix_from_event(ev, dc).first;
|
||||
if (ev.gas.index >= 0)
|
||||
show_integer(b, ev.gas.index, "cylinder='", "'");
|
||||
put_gasmix(b, mix);
|
||||
|
@ -365,7 +365,7 @@ static void save_one_event(struct membuffer *b, const struct dive &dive, const s
|
|||
static void save_events(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc)
|
||||
{
|
||||
for (auto &ev: dc.events)
|
||||
save_one_event(b, dive, ev);
|
||||
save_one_event(b, dive, dc, ev);
|
||||
}
|
||||
|
||||
static void save_tags(struct membuffer *b, const tag_list &tags)
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
static int depthAtTime(const plot_info &pi, duration_t time);
|
||||
|
||||
DiveEventItem::DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix,
|
||||
DiveEventItem::DiveEventItem(const struct dive *d, const struct divecomputer *dc, int idx, const struct event &ev, const struct gasmix lastgasmix, divemode_t lastdivemode,
|
||||
const plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis,
|
||||
int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent) : DivePixmapItem(parent),
|
||||
vAxis(vAxis),
|
||||
|
@ -28,8 +28,8 @@ DiveEventItem::DiveEventItem(const struct dive *d, int idx, const struct event &
|
|||
{
|
||||
setFlag(ItemIgnoresTransformations);
|
||||
|
||||
setupPixmap(lastgasmix, pixmaps);
|
||||
setupToolTipString(lastgasmix);
|
||||
setupPixmap(lastgasmix, lastdivemode, *dc, pixmaps);
|
||||
setupToolTipString(lastgasmix, lastdivemode, *dc);
|
||||
recalculatePos();
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ DiveEventItem::~DiveEventItem()
|
|||
{
|
||||
}
|
||||
|
||||
void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps)
|
||||
void DiveEventItem::setupPixmap(const struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc, const DivePixmaps &pixmaps)
|
||||
{
|
||||
event_severity severity = ev.get_severity();
|
||||
if (ev.name.empty()) {
|
||||
|
@ -51,10 +51,15 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
|
|||
setPixmap(pixmaps.bookmark);
|
||||
setOffset(QPointF(0.0, -pixmap().height()));
|
||||
} else if (ev.is_gaschange()) {
|
||||
struct gasmix mix = dive->get_gasmix_from_event(ev);
|
||||
auto [mix, divemode] = dive->get_gasmix_from_event(ev, dc);
|
||||
struct icd_data icd_data;
|
||||
bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data);
|
||||
if (mix.he.permille) {
|
||||
if (divemode != lastdivemode) {
|
||||
if (divemode == CCR)
|
||||
setPixmap(pixmaps.onCCRLoop);
|
||||
else
|
||||
setPixmap(pixmaps.bailout);
|
||||
} else if (mix.he.permille) {
|
||||
if (icd)
|
||||
setPixmap(pixmaps.gaschangeTrimixICD);
|
||||
else
|
||||
|
@ -111,7 +116,7 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
|
|||
}
|
||||
}
|
||||
|
||||
void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
|
||||
void DiveEventItem::setupToolTipString(const struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc)
|
||||
{
|
||||
// we display the event on screen - so translate
|
||||
QString name = gettextFromC::tr(ev.name.c_str());
|
||||
|
@ -120,7 +125,7 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
|
|||
|
||||
if (ev.is_gaschange()) {
|
||||
struct icd_data icd_data;
|
||||
struct gasmix mix = dive->get_gasmix_from_event(ev);
|
||||
auto [mix, divemode] = dive->get_gasmix_from_event(ev, dc);
|
||||
name += ": ";
|
||||
name += QString::fromStdString(mix.name());
|
||||
|
||||
|
@ -135,6 +140,9 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
|
|||
qPrintable(tr("ΔN₂")), icd_data.dN2 / 10.0,
|
||||
icd ? ">" : "<", lrint(-icd_data.dHe / 5.0) / 10.0);
|
||||
}
|
||||
if (divemode != lastdivemode)
|
||||
name += QString("\nmodechange: %1").arg(gettextFromC::tr(divemode_text_ui[divemode != OC]));
|
||||
|
||||
} else if (ev.name == "modechange") {
|
||||
name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev.value]));
|
||||
} else if (value) {
|
||||
|
|
|
@ -13,7 +13,7 @@ struct plot_info;
|
|||
class DiveEventItem : public DivePixmapItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix,
|
||||
DiveEventItem(const struct dive *d, const struct divecomputer *dc, int idx, const struct event &ev, const struct gasmix lastgasmix, divemode_t lastdivemode,
|
||||
const struct plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis,
|
||||
int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent = nullptr);
|
||||
~DiveEventItem();
|
||||
|
@ -25,8 +25,8 @@ public:
|
|||
int firstSecond, int lastSecond);
|
||||
|
||||
private:
|
||||
void setupToolTipString(struct gasmix lastgasmix);
|
||||
void setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps);
|
||||
void setupToolTipString(struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc);
|
||||
void setupPixmap(struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc, const DivePixmaps &pixmaps);
|
||||
void recalculatePos();
|
||||
DiveCartesianAxis *vAxis;
|
||||
DiveCartesianAxis *hAxis;
|
||||
|
|
|
@ -551,8 +551,17 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
|
|||
// while all other items are up there on the constructor.
|
||||
qDeleteAll(eventItems);
|
||||
eventItems.clear();
|
||||
struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, 1_sec);
|
||||
|
||||
const struct gasmix *lastgasmix;
|
||||
divemode_t lastdivemode;
|
||||
int cylinder_index = gasmix_loop(*d, *currentdc).next_cylinder_index().first;
|
||||
if (cylinder_index == -1) {
|
||||
lastgasmix = &gasmix_air;
|
||||
lastdivemode = OC;
|
||||
} else {
|
||||
const cylinder_t *cylinder = d->get_cylinder(cylinder_index);
|
||||
lastgasmix = &cylinder->gasmix;
|
||||
lastdivemode = get_effective_divemode(*currentdc, *cylinder);
|
||||
}
|
||||
for (auto [idx, event]: enumerated_range(currentdc->events)) {
|
||||
// if print mode is selected only draw headings, SP change, gas events or bookmark event
|
||||
if (printMode) {
|
||||
|
@ -560,18 +569,23 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
|
|||
!(event.name == "heading" ||
|
||||
(event.name == "SP change" && event.time.seconds == 0) ||
|
||||
event.is_gaschange() ||
|
||||
event.is_divemodechange() ||
|
||||
event.type == SAMPLE_EVENT_BOOKMARK))
|
||||
continue;
|
||||
}
|
||||
if (DiveEventItem::isInteresting(d, currentdc, event, plotInfo, firstSecond, lastSecond)) {
|
||||
auto item = new DiveEventItem(d, idx, event, lastgasmix, plotInfo,
|
||||
auto item = new DiveEventItem(d, currentdc, idx, event, *lastgasmix, lastdivemode, plotInfo,
|
||||
timeAxis, profileYAxis, animSpeed, *pixmaps);
|
||||
item->setZValue(2);
|
||||
addItem(item);
|
||||
eventItems.push_back(item);
|
||||
}
|
||||
if (event.is_gaschange())
|
||||
lastgasmix = d->get_gasmix_from_event(event);
|
||||
if (event.is_gaschange()) {
|
||||
auto [gasmix, divemode] = d->get_gasmix_from_event(event, *currentdc);
|
||||
lastgasmix = &gasmix;
|
||||
lastdivemode = divemode;
|
||||
} else if (event.is_divemodechange())
|
||||
lastdivemode = event.value ? CCR : OC;
|
||||
}
|
||||
|
||||
QString dcText = QString::fromStdString(get_dc_nickname(currentdc));
|
||||
|
|
|
@ -529,10 +529,11 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
|
|||
QMenu m;
|
||||
if (!d)
|
||||
return;
|
||||
|
||||
const struct divecomputer *currentdc = d->get_dc(dc);
|
||||
// figure out if we are ontop of the dive computer name in the profile
|
||||
QGraphicsItem *sceneItem = itemAt(mapFromGlobal(event->globalPos()));
|
||||
if (isDiveTextItem(sceneItem, profileScene->diveComputerText)) {
|
||||
const struct divecomputer *currentdc = d->get_dc(dc);
|
||||
if (!currentdc->deviceid && dc == 0 && d->number_of_computers() == 1)
|
||||
// nothing to do, can't rename, delete or reorder
|
||||
return;
|
||||
|
@ -561,7 +562,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
|
|||
addGasChangeMenu(m, tr("Edit gas change"), *d, dc, item->ev.time.seconds);
|
||||
} else if (d && d->cylinders.size() > 1) {
|
||||
// if we have more than one gas, offer to switch to another one
|
||||
const struct divecomputer *currentdc = d->get_dc(dc);
|
||||
if (seconds == 0 || (!currentdc->samples.empty() && seconds <= currentdc->samples[0].time.seconds))
|
||||
addGasChangeMenu(m, tr("Set initial gas"), *d, dc, 0);
|
||||
else
|
||||
|
@ -571,18 +571,21 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
|
|||
m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); });
|
||||
m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); });
|
||||
|
||||
divemode_loop loop(*d->get_dc(dc));
|
||||
divemode_t divemode = loop.at(seconds);
|
||||
QMenu *changeMode = m.addMenu(tr("Change divemode"));
|
||||
if (divemode != OC)
|
||||
changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]),
|
||||
[this, seconds](){ addDivemodeSwitch(seconds, OC); });
|
||||
if (divemode != CCR)
|
||||
changeMode->addAction(gettextFromC::tr(divemode_text_ui[CCR]),
|
||||
[this, seconds](){ addDivemodeSwitch(seconds, CCR); });
|
||||
if (divemode != PSCR)
|
||||
changeMode->addAction(gettextFromC::tr(divemode_text_ui[PSCR]),
|
||||
[this, seconds](){ addDivemodeSwitch(seconds, PSCR); });
|
||||
[[maybe_unused]] auto [divemode, cylinder_index, _gasmix] = get_dive_status_at(*d, *currentdc, seconds);
|
||||
if (currentdc->divemode == PSCR || (currentdc->divemode == CCR && prefs.allowOcGasAsDiluent && (cylinder_index == -1 || d->get_cylinder(cylinder_index)->cylinder_use == OC_GAS))) {
|
||||
QMenu *changeMode = m.addMenu(tr("Change divemode"));
|
||||
if (divemode != OC) {
|
||||
changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]),
|
||||
[this, seconds](){ addDivemodeSwitch(seconds, OC); });
|
||||
} else {
|
||||
if (currentdc->divemode == PSCR)
|
||||
changeMode->addAction(gettextFromC::tr(divemode_text_ui[PSCR]),
|
||||
[this, seconds](){ addDivemodeSwitch(seconds, PSCR); });
|
||||
else
|
||||
changeMode->addAction(gettextFromC::tr(divemode_text_ui[CCR]),
|
||||
[this, seconds](){ addDivemodeSwitch(seconds, CCR); });
|
||||
}
|
||||
}
|
||||
|
||||
if (DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem)) {
|
||||
m.addAction(tr("Remove event"), [this,item] { removeEvent(item); });
|
||||
|
@ -639,7 +642,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
|
|||
}
|
||||
m2->addAction(tr("All event types"), this, &ProfileWidget2::unhideEventTypes);
|
||||
}
|
||||
const struct divecomputer *currentdc = d->get_dc(dc);
|
||||
if (currentdc && std::any_of(currentdc->events.begin(), currentdc->events.end(),
|
||||
[] (auto &ev) { return ev.hidden; }))
|
||||
m.addAction(tr("Unhide individually hidden events of this dive"), this, &ProfileWidget2::unhideEvents);
|
||||
|
|
|
@ -150,7 +150,8 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
|
|||
int j = 0;
|
||||
int cylinderid = 0;
|
||||
|
||||
divemode_loop loop(*dc);
|
||||
gasmix_loop loop_gas(*d, *dc);
|
||||
divemode_loop loop_mode(*dc);
|
||||
for (int i = 0; i < plansamples - 1; i++) {
|
||||
if (dc->last_manual_time.seconds && dc->last_manual_time.seconds > 120 && lasttime.seconds >= dc->last_manual_time.seconds)
|
||||
break;
|
||||
|
@ -172,7 +173,8 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
|
|||
if (newtime.seconds - lastrecordedtime.seconds > 10 || cylinderid == get_cylinderid_at_time(d, dc, nexttime)) {
|
||||
if (newtime.seconds == lastrecordedtime.seconds)
|
||||
newtime.seconds += 10;
|
||||
divemode_t current_divemode = loop.at(newtime.seconds - 1);
|
||||
|
||||
[[maybe_unused]] auto [current_divemode, _cylinder_index, _gasmix] = get_dive_status_at(*d, *dc, newtime.seconds - 1, &loop_mode, &loop_gas);
|
||||
addStop(depthsum / samplecount, newtime.seconds, cylinderid, last_sp.mbar, true, current_divemode);
|
||||
lastrecordedtime = newtime;
|
||||
}
|
||||
|
@ -182,9 +184,9 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
|
|||
}
|
||||
}
|
||||
// make sure we get the last point right so the duration is correct
|
||||
divemode_t current_divemode = loop.at(dc->duration.seconds);
|
||||
[[maybe_unused]] auto [current_divemode, _cylinder_index, _gasmix] = get_dive_status_at(*d, *dc, dc->duration.seconds, &loop_mode, &loop_gas);
|
||||
if (!hasMarkedSamples && !dc->last_manual_time.seconds)
|
||||
addStop(0_m, dc->duration.seconds,cylinderid, last_sp.mbar, true, current_divemode);
|
||||
addStop(0_m, dc->duration.seconds, cylinderid, last_sp.mbar, true, current_divemode);
|
||||
preserved_until = d->duration;
|
||||
|
||||
updateDiveProfile();
|
||||
|
|
Loading…
Add table
Reference in a new issue