From 9a3363dc7dcde1685f4a3c1e98f4864d8ef9fd67 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Thu, 8 Dec 2022 14:19:31 +1300 Subject: [PATCH] Added detection for bailout to OC / switch back to CC for CCR dives. As a CCR diver I would like to see the ability to properly track the 'on loop' / 'bailed out to open circuit' status directly in Subsurface, because this is an important (or even the most important) bit of status information during a CCR dive. This should extend to the use of the correct ppO2 / gas mix in the deco ceiling / tissue model calculation. My idea for how to do this would be to track the 'type' ('diluent' / 'OC bailout') for every gasmix / tank that is reported by the dive computer. Most CCR capable dive computers that I am familiar with require the user to enter two different gas lists for diluent and bailout, so this should work with the existing libdivecomputer API for these. Unfortunately I think making this change in Subsurface will require a bit of work, as the libdivecomputer field capable of tracking the 'type' of a gas or tank (`cache->tankinfo[]`) does not seem to be consumed at all in Subsurface. So this just provides a prerequisite for the change in Subsurface by populating `tankinfo[]`. In addition to this it also triggers a message on every switch from CC to OC and back, at least giving a visual indication of these diver triggered events. The messages can probably be removed from libdivecomputer again once 'loop status' tracking has been added to Subsurface. Also included is a fix of the tab expansion mess that I created in commit 2129403 ("Added facility to detect and interpret manual setpoint switches for Garmin Descent computers"). Apologies for this, I've switched to using a custom `.vimrc` for this project now. [ Edited up the commit message a bit further - Linus ] Signed-off-by: Michael Keller Signed-off-by: Linus Torvalds --- src/garmin_parser.c | 81 +++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/src/garmin_parser.c b/src/garmin_parser.c index df73aea..de63097 100644 --- a/src/garmin_parser.c +++ b/src/garmin_parser.c @@ -71,7 +71,7 @@ struct record_data { double ceiling; // RECORD_GASMIX - int index, gas_status; + int index, gas_status, gas_type; dc_gasmix_t gasmix; // RECORD_EVENT @@ -127,7 +127,8 @@ typedef struct garmin_parser_t { struct garmin_sensor sensor[MAX_SENSORS]; unsigned int setpoint_low_cbar, setpoint_high_cbar; unsigned int setpoint_low_switch_depth_mm, setpoint_high_switch_depth_mm; - unsigned int setpoint_low_switch_mode, setpoint_high_switch_mode; + unsigned int setpoint_low_switch_mode, setpoint_high_switch_mode; + dc_tankinfo_t *current_tankinfo; } dive; // I count nine (!) different GPS fields Hmm. @@ -220,7 +221,7 @@ static void garmin_event(struct garmin_parser_t *garmin, sample.event.type = SAMPLE_EVENT_STRING; sample.event.name = event_desc[data].name; - sample.event.flags = event_desc[data].severity << SAMPLE_FLAGS_SEVERITY_SHIFT; + sample.event.flags = event_desc[data].severity << SAMPLE_FLAGS_SEVERITY_SHIFT; if (data == 24 || data == 25 || data == 26 || data == 27) { // Update the actual setpoint used during the dive and report it @@ -236,6 +237,23 @@ static void garmin_event(struct garmin_parser_t *garmin, case 57: sample.gasmix = data; garmin->callback(DC_SAMPLE_GASMIX, sample, garmin->userdata); + + dc_tankinfo_t *tankinfo = &garmin->cache.tankinfo[data]; + if (!garmin->dive.current_tankinfo || (*tankinfo & DC_TANKINFO_CC_DILUENT) != (*garmin->dive.current_tankinfo & DC_TANKINFO_CC_DILUENT)) { + dc_sample_value_t sample2 = {0}; + sample2.event.type = SAMPLE_EVENT_STRING; + if (*tankinfo & DC_TANKINFO_CC_DILUENT) { + sample2.event.name = "Switched to closed circuit"; + } else { + sample2.event.name = "Switched to open circuit bailout"; + } + sample2.event.flags = 2 << SAMPLE_FLAGS_SEVERITY_SHIFT; + + garmin->callback(DC_SAMPLE_EVENT, sample2, garmin->userdata); + + garmin->dive.current_tankinfo = tankinfo; + } + return; } } @@ -260,7 +278,17 @@ static void flush_pending_record(struct garmin_parser_t *garmin) int index = record->index; if (enabled && index < MAXGASES) { DC_ASSIGN_IDX(garmin->cache, GASMIX, index, record->gasmix); - DC_ASSIGN_FIELD(garmin->cache, GASMIX_COUNT, index+1); + if (index + 1 > garmin->cache.GASMIX_COUNT) { + DC_ASSIGN_FIELD(garmin->cache, GASMIX_COUNT, index + 1); + garmin->cache.initialized |= 1 << DC_FIELD_TANK_COUNT; + } + + dc_tankinfo_t tankinfo = DC_TANKINFO_METRIC; + if (record->gas_type == 1) { + tankinfo |= DC_TANKINFO_CC_DILUENT; + } + garmin->cache.tankinfo[index] = tankinfo; + garmin->cache.initialized |= 1 << DC_FIELD_TANK; } } if (pending & RECORD_DEVICE_INFO && record->device_index == 0) { @@ -637,6 +665,12 @@ DECLARE_FIELD(DIVE_GAS, status, ENUM) // 0 - disabled, 1 - enabled, 2 - backup garmin->record_data.gas_status = data; } +DECLARE_FIELD(DIVE_GAS, type, ENUM) +{ + // 0 - open circuit, 1 - CCR diluent + garmin->record_data.gas_type = data; + garmin->record_data.pending |= RECORD_GASMIX; +} // DIVE_SUMMARY DECLARE_FIELD(DIVE_SUMMARY, avg_depth, UINT32) { DC_ASSIGN_FIELD(garmin->cache, AVGDEPTH, data / 1000.0); } @@ -908,12 +942,13 @@ DECLARE_MESG(RECORD) = { }; DECLARE_MESG(DIVE_GAS) = { - .maxfield = 3, + .maxfield = 4, .field = { // This uses a "message index" field to set the gas index SET_FIELD(DIVE_GAS, 0, helium, UINT8), SET_FIELD(DIVE_GAS, 1, oxygen, UINT8), SET_FIELD(DIVE_GAS, 2, status, ENUM), + SET_FIELD(DIVE_GAS, 3, type, ENUM), } }; @@ -1511,31 +1546,33 @@ garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsign // // There's no way to match them up unless they are an identity // mapping, so having two different ones doesn't actually work. - if (garmin->dive.nr_sensor > garmin->cache.GASMIX_COUNT) + if (garmin->dive.nr_sensor > garmin->cache.GASMIX_COUNT) { DC_ASSIGN_FIELD(garmin->cache, GASMIX_COUNT, garmin->dive.nr_sensor); + garmin->cache.initialized |= 1 << DC_FIELD_TANK_COUNT; + } for (int i = 0; i < garmin->dive.nr_sensor; i++) { static const char *name[] = { "Sensor 1", "Sensor 2", "Sensor 3", "Sensor 4", "Sensor 5" }; add_sensor_string(garmin, name[i], garmin->dive.sensor+i); } - if (garmin->cache.DIVEMODE == DC_DIVEMODE_CCR) { - dc_field_add_string_fmt(&garmin->cache, "Setpoint low [bar]", "%u.%02u", - garmin->dive.setpoint_low_cbar / 100, (garmin->dive.setpoint_low_cbar % 100)); - dc_field_add_string(&garmin->cache, "Setpoint low mode", garmin->dive.setpoint_low_switch_mode ? "auto" : "manual"); - if (garmin->dive.setpoint_low_switch_mode) { - dc_field_add_string_fmt(&garmin->cache, "Setpoint low auto switch depth [m]", "%u.%01u", - garmin->dive.setpoint_low_switch_depth_mm / 1000, (garmin->dive.setpoint_low_switch_depth_mm % 1000) / 100); - } + if (garmin->cache.DIVEMODE == DC_DIVEMODE_CCR) { + dc_field_add_string_fmt(&garmin->cache, "Setpoint low [bar]", "%u.%02u", + garmin->dive.setpoint_low_cbar / 100, (garmin->dive.setpoint_low_cbar % 100)); + dc_field_add_string(&garmin->cache, "Setpoint low mode", garmin->dive.setpoint_low_switch_mode ? "auto" : "manual"); + if (garmin->dive.setpoint_low_switch_mode) { + dc_field_add_string_fmt(&garmin->cache, "Setpoint low auto switch depth [m]", "%u.%01u", + garmin->dive.setpoint_low_switch_depth_mm / 1000, (garmin->dive.setpoint_low_switch_depth_mm % 1000) / 100); + } - dc_field_add_string_fmt(&garmin->cache, "Setpoint high [bar]", "%u.%02u", - garmin->dive.setpoint_high_cbar / 100, (garmin->dive.setpoint_high_cbar % 100)); - dc_field_add_string(&garmin->cache, "Setpoint high mode", garmin->dive.setpoint_high_switch_mode ? "auto" : "manual"); - if (garmin->dive.setpoint_high_switch_mode) { - dc_field_add_string_fmt(&garmin->cache, "Setpoint high auto switch depth [m]", "%u.%01u", - garmin->dive.setpoint_high_switch_depth_mm / 1000, (garmin->dive.setpoint_high_switch_depth_mm % 1000) / 100); - } - } + dc_field_add_string_fmt(&garmin->cache, "Setpoint high [bar]", "%u.%02u", + garmin->dive.setpoint_high_cbar / 100, (garmin->dive.setpoint_high_cbar % 100)); + dc_field_add_string(&garmin->cache, "Setpoint high mode", garmin->dive.setpoint_high_switch_mode ? "auto" : "manual"); + if (garmin->dive.setpoint_high_switch_mode) { + dc_field_add_string_fmt(&garmin->cache, "Setpoint high auto switch depth [m]", "%u.%01u", + garmin->dive.setpoint_high_switch_depth_mm / 1000, (garmin->dive.setpoint_high_switch_depth_mm % 1000) / 100); + } + } return DC_STATUS_SUCCESS; }