Garmin Descent Mk2i: add support for parsing cylinder pressure data

Very early trial at parsing the pressure data from the Garmin tank pods.
It seems to work for the test-cases I have.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2020-10-30 12:37:19 -07:00
parent c4a3db4ac1
commit 89b1cc4f28

View File

@ -50,6 +50,14 @@ struct pos {
int lat, lon;
};
#define MAX_SENSORS 6
struct garmin_sensor {
unsigned int sensor_id;
const char *sensor_name;
unsigned char sensor_enabled, sensor_units, sensor_used_for_gas_rate;
unsigned int sensor_rated_pressure, sensor_reserve_pressure, sensor_volume;
};
#define MAXTYPE 16
#define MAXGASES 16
#define MAXSTRINGS 32
@ -77,6 +85,11 @@ struct record_data {
// RECORD_DECO_MODEL
unsigned char model, gf_low, gf_high;
// RECORD_SENSOR_PROFILE has no data, fills in dive.sensor[nr_sensor]
// RECORD_TANK_UPDATE
unsigned int sensor, pressure;
};
#define RECORD_GASMIX 1
@ -84,6 +97,8 @@ struct record_data {
#define RECORD_EVENT 4
#define RECORD_DEVICE_INFO 8
#define RECORD_DECO_MODEL 16
#define RECORD_SENSOR_PROFILE 32
#define RECORD_TANK_UPDATE 64
typedef struct garmin_parser_t {
dc_parser_t base;
@ -106,6 +121,8 @@ typedef struct garmin_parser_t {
unsigned int profile;
unsigned int time;
int utc_offset, time_offset;
unsigned int nr_sensor;
struct garmin_sensor sensor[MAX_SENSORS];
} dive;
// I count nine (!) different GPS fields Hmm.
@ -128,6 +145,20 @@ typedef struct garmin_parser_t {
typedef int (*garmin_data_cb_t)(unsigned char type, const unsigned char *data, int len, void *user);
static inline struct garmin_sensor *current_sensor(garmin_parser_t *garmin)
{
return garmin->dive.sensor + garmin->dive.nr_sensor;
}
static int find_tank_index(garmin_parser_t *garmin, unsigned int sensor_id)
{
for (int i = 0; i < garmin->dive.nr_sensor; i++) {
if (garmin->dive.sensor[i].sensor_id == sensor_id)
return i;
}
return 0;
}
/*
* Decode the event. Numbers from Wojtek's fit2subs python script
*/
@ -222,6 +253,23 @@ static void flush_pending_record(struct garmin_parser_t *garmin)
}
if (pending & RECORD_DECO_MODEL)
dc_field_add_string_fmt(&garmin->cache, "Deco model", "Buhlmann ZHL-16C %u/%u", record->gf_low, record->gf_high);
// End of sensor record just increments nr_sensor,
// so that the next sensor record will start
// filling in the next one.
//
// NOTE! This only happens for tank pods, other
// sensors will just overwrite each other.
//
// Also note that the last sensor is just for
// scratch use, so that the sensor record can
// always fill in dive.sensor[nr_sensor] with
// no checking.
if (pending & RECORD_SENSOR_PROFILE) {
if (garmin->dive.nr_sensor < MAX_SENSORS-1)
garmin->dive.nr_sensor++;
}
return;
}
@ -237,6 +285,14 @@ static void flush_pending_record(struct garmin_parser_t *garmin)
garmin_event(garmin, record->event_nr, record->event_type,
record->event_group, record->event_data, record->event_unknown);
}
if (pending & RECORD_TANK_UPDATE) {
dc_sample_value_t sample = {0};
sample.pressure.tank = find_tank_index(garmin, record->sensor);
sample.pressure.value = record->pressure / 100.0;
garmin->callback(DC_SAMPLE_PRESSURE, sample, garmin->userdata);
}
}
@ -587,17 +643,56 @@ DECLARE_FIELD(DIVE_SETTINGS, safety_stop_time, UINT16) { }
DECLARE_FIELD(DIVE_SETTINGS, heart_rate_source_type, ENUM) { }
DECLARE_FIELD(DIVE_SETTINGS, hear_rate_device_type, UINT8) { }
DECLARE_FIELD(SENSOR_PROFILE, ant_channel_id, UINT32Z) { }
DECLARE_FIELD(SENSOR_PROFILE, name, STRING) { }
DECLARE_FIELD(SENSOR_PROFILE, enabled, ENUM) { }
DECLARE_FIELD(SENSOR_PROFILE, pressure_units, ENUM) { } // 0 is PSI, 1 is KPA (unused), 2 is Bar
DECLARE_FIELD(SENSOR_PROFILE, rated_pressure, UINT16) { }
DECLARE_FIELD(SENSOR_PROFILE, reserve_pressure, UINT16) { }
DECLARE_FIELD(SENSOR_PROFILE, volume, UINT16) { } // CuFt * 10 (PSI) or L * 10
DECLARE_FIELD(SENSOR_PROFILE, used_for_gas_rate, ENUM) { } // was used for SAC calculations?
// SENSOR_PROFILE record for each ANT/BLE sensor.
// We only care about sensor type 28 - Garmin tank pod.
DECLARE_FIELD(SENSOR_PROFILE, ant_channel_id, UINT32Z)
{
current_sensor(garmin)->sensor_id = data;
}
DECLARE_FIELD(SENSOR_PROFILE, name, STRING) { } // We don't pass in string types correctly
DECLARE_FIELD(SENSOR_PROFILE, enabled, ENUM)
{
current_sensor(garmin)->sensor_enabled = data;
}
DECLARE_FIELD(SENSOR_PROFILE, sensor_type, UINT8)
{
// 28 is tank pod
// start filling in next sensor after this record
if (data == 28)
garmin->record_data.pending |= RECORD_SENSOR_PROFILE;
}
DECLARE_FIELD(SENSOR_PROFILE, pressure_units, ENUM)
{
// 0 is PSI, 1 is KPA (unused), 2 is Bar
current_sensor(garmin)->sensor_units = data;
}
DECLARE_FIELD(SENSOR_PROFILE, rated_pressure, UINT16)
{
current_sensor(garmin)->sensor_rated_pressure = data;
}
DECLARE_FIELD(SENSOR_PROFILE, reserve_pressure, UINT16)
{
current_sensor(garmin)->sensor_reserve_pressure = data;
}
DECLARE_FIELD(SENSOR_PROFILE, volume, UINT16)
{
current_sensor(garmin)->sensor_volume = data;
}
DECLARE_FIELD(SENSOR_PROFILE, used_for_gas_rate, ENUM)
{
current_sensor(garmin)->sensor_used_for_gas_rate = data;
}
DECLARE_FIELD(TANK_UPDATE, sensor, UINT32Z) { } // sensor ID
DECLARE_FIELD(TANK_UPDATE, pressure, UINT16) { } // pressure in Bar * 100
DECLARE_FIELD(TANK_UPDATE, sensor, UINT32Z)
{
garmin->record_data.sensor = data;
}
DECLARE_FIELD(TANK_UPDATE, pressure, UINT16)
{
garmin->record_data.pressure = data;
garmin->record_data.pending |= RECORD_TANK_UPDATE;
}
DECLARE_FIELD(TANK_SUMMARY, sensor, UINT32Z) { } // sensor ID
DECLARE_FIELD(TANK_SUMMARY, start_pressure, UINT16) { } // Bar * 100
@ -817,6 +912,7 @@ DECLARE_MESG(SENSOR_PROFILE) = {
SET_FIELD(SENSOR_PROFILE, 0, ant_channel_id, UINT32Z), // derived from the number engraved on the side
SET_FIELD(SENSOR_PROFILE, 2, name, STRING),
SET_FIELD(SENSOR_PROFILE, 3, enabled, ENUM),
SET_FIELD(SENSOR_PROFILE, 52, sensor_type, UINT8), // 28 is tank pod
SET_FIELD(SENSOR_PROFILE, 74, pressure_units, ENUM), // 0 is PSI, 1 is KPA (unused), 2 is Bar
SET_FIELD(SENSOR_PROFILE, 75, rated_pressure, UINT16),
SET_FIELD(SENSOR_PROFILE, 76, reserve_pressure, UINT16),
@ -1217,6 +1313,7 @@ traverse_data(struct garmin_parser_t *garmin)
if (garmin->record_data.pending)
flush_pending_record(garmin);
}
return DC_STATUS_SUCCESS;
}
@ -1287,6 +1384,11 @@ garmin_parser_is_dive (dc_parser_t *abstract, const unsigned char *data, unsigne
}
}
static void add_sensor_string(garmin_parser_t *garmin, const char *desc, const struct garmin_sensor *sensor)
{
dc_field_add_string_fmt(&garmin->cache, desc, "%x", sensor->sensor_id);
}
static dc_status_t
garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
@ -1300,6 +1402,7 @@ garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsign
memset(&garmin->cache, 0, sizeof(garmin->cache));
traverse_data(garmin);
// These seem to be the "real" GPS dive coordinates
add_gps_string(garmin, "GPS1", &garmin->gps.SESSION.entry);
add_gps_string(garmin, "GPS2", &garmin->gps.SESSION.exit);
@ -1314,6 +1417,25 @@ garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsign
add_gps_string(garmin, "Record GPS", &garmin->gps.RECORD);
// We have no idea about gas mixes vs tanks
for (int i = 0; i < garmin->dive.nr_sensor; i++) {
// DC_ASSIGN_IDX(garmin->cache, tankinfo, i, ..);
// DC_ASSIGN_IDX(garmin->cache, tanksize, i, ..);
// DC_ASSIGN_IDX(garmin->cache, tankworkingpressure, i, ..);
}
// Hate hate hate gasmix vs tank counts.
//
// 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)
DC_ASSIGN_FIELD(garmin->cache, GASMIX_COUNT, garmin->dive.nr_sensor);
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);
}
return DC_STATUS_SUCCESS;
}