Merge pull request #58 from mikeller/add_garmin_descent_mk3_support

Merging this - there is demand for this fix, so now is a good time to get it tested: https://groups.google.com/g/subsurface-divelog/c/7Uo7A9YTGhg/m/BlhlZ2NNBQAJ
This commit is contained in:
Michael Keller 2024-04-28 18:46:40 +12:00 committed by GitHub
commit ca55a11ed5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 22 deletions

View File

@ -471,10 +471,11 @@ static const dc_descriptor_t g_descriptors[] = {
// Not merged upstream yet
/* Garmin -- model numbers as defined in FIT format; USB product id is (0x4000 | model) */
/* for the Mk1 we are using the model of the global model - the APAC model is 2991 */
/* for the Mk2 we are using the model of the global model - the APAC model is 3702 */
/* for the Mk1 we are using the model of the global model */
/* for the Mk2/Mk3 we are using the model of the Mk2 global model */
/* see garmin_parser.c for a more comprehensive list of models */
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
{"Garmin", "Descent Mk2/Mk2i", DC_FAMILY_GARMIN, 3258, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
{"Garmin", "Descent Mk2(i)/Mk3(i)", DC_FAMILY_GARMIN, 3258, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
{"FIT", "File import", DC_FAMILY_GARMIN, 0, DC_TRANSPORT_USBSTORAGE, NULL },
};

View File

@ -101,7 +101,6 @@ garmin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *ios
// in order to have only one entry for the Mk2, we don't use the Mk2/APAC model number in our code
device->use_mtp = (model == (0x0FFF & DESCENT_MK2));
device->mtp_device = NULL;
DEBUG(context, "Found Garmin with model 0x%x which is a %s\n", model, (device->use_mtp ? "Mk2/Mk2i" : "Mk1"));
#endif
*out = (dc_device_t *) device;
@ -331,12 +330,16 @@ mtp_get_file_list(dc_device_t *abstract, struct file_list *files)
for (i = 0; i < numrawdevices; i++) {
LIBMTP_devicestorage_t *storage;
// we only want to read from a Garmin Descent Mk2 device at this point
if (rawdevices[i].device_entry.vendor_id != GARMIN_VENDOR ||
(rawdevices[i].device_entry.product_id != DESCENT_MK2 && rawdevices[i].device_entry.product_id != DESCENT_MK2_APAC)) {
if (rawdevices[i].device_entry.vendor_id != GARMIN_VENDOR) {
DEBUG(abstract->context, "Garmin/mtp: skipping raw device %04x/%04x",
rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id);
continue;
}
if (rawdevices[i].device_entry.product_id != DESCENT_MK2 && rawdevices[i].device_entry.product_id != DESCENT_MK2_APAC) {
DEBUG(abstract->context, "Garmin/mtp: skipping Garmin raw device %04x/%04x, as it is not a dive computer / does not support MTP",
rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id);
continue;
}
device->mtp_device = LIBMTP_Open_Raw_Device_Uncached(&rawdevices[i]);
if (device->mtp_device == NULL) {
DEBUG(abstract->context, "Garmin/mtp: unable to open raw device %d", i);

View File

@ -129,7 +129,7 @@ typedef struct garmin_parser_t {
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;
dc_gasmix_t *current_gasmix;
dc_usage_t current_gasmix_usage;
} dive;
// I count nine (!) different GPS fields Hmm.
@ -239,11 +239,11 @@ static void garmin_event(struct garmin_parser_t *garmin,
sample.gasmix = data;
garmin->callback(DC_SAMPLE_GASMIX, &sample, garmin->userdata);
dc_gasmix_t *gasmix = &garmin->cache.GASMIX[data];
if (!garmin->dive.current_gasmix || gasmix->usage != garmin->dive.current_gasmix->usage) {
dc_usage_t gasmix_usage = garmin->cache.GASMIX[data].usage;
if (gasmix_usage != garmin->dive.current_gasmix_usage) {
dc_sample_value_t sample2 = {0};
sample2.event.type = SAMPLE_EVENT_STRING;
if (gasmix->usage == DC_USAGE_DILUENT) {
if (gasmix_usage == DC_USAGE_DILUENT) {
sample2.event.name = "Switched to closed circuit";
} else {
sample2.event.name = "Switched to open circuit bailout";
@ -252,7 +252,7 @@ static void garmin_event(struct garmin_parser_t *garmin,
garmin->callback(DC_SAMPLE_EVENT, &sample2, garmin->userdata);
garmin->dive.current_gasmix = gasmix;
garmin->dive.current_gasmix_usage = gasmix_usage;
}
return;
@ -486,19 +486,22 @@ DECLARE_FIELD(ANY, timestamp, UINT32)
{
garmin->record_data.timestamp = data;
if (garmin->callback) {
dc_sample_value_t sample = {0};
// Turn the timestamp relative to the beginning of the dive
if (data < garmin->dive.time)
if (data < garmin->dive.time) {
DEBUG(garmin->base.context, "Timestamp before dive start: %d (dive start: %d)", data, garmin->dive.time);
return;
data -= garmin->dive.time;
}
data -= garmin->dive.time - 1;
// Did we already do this?
if (data < garmin->record_data.time)
if (data == garmin->record_data.time)
return;
garmin->record_data.time = data;
// Now we're ready to actually update the sample times
garmin->record_data.time = data+1;
dc_sample_value_t sample = {0};
sample.time = data * 1000;
garmin->callback(DC_SAMPLE_TIME, &sample, garmin->userdata);
}
@ -656,6 +659,7 @@ DECLARE_FIELD(ACTIVITY, event_group, UINT8) { }
// SPORT
DECLARE_FIELD(SPORT, sub_sport, ENUM) {
garmin->dive.sub_sport = (ENUM) data;
garmin->dive.current_gasmix_usage = DC_USAGE_OPEN_CIRCUIT;
dc_divemode_t val;
switch (data) {
case 55: val = DC_DIVEMODE_GAUGE;
@ -663,7 +667,10 @@ DECLARE_FIELD(SPORT, sub_sport, ENUM) {
case 56:
case 57: val = DC_DIVEMODE_FREEDIVE;
break;
case 63: val = DC_DIVEMODE_CCR;
case 63:
val = DC_DIVEMODE_CCR;
garmin->dive.current_gasmix_usage = DC_USAGE_DILUENT;
break;
default: val = DC_DIVEMODE_OC;
}
@ -797,7 +804,7 @@ DECLARE_FIELD(SENSOR_PROFILE, enabled, ENUM)
{
current_sensor(garmin)->sensor_enabled = data;
}
DECLARE_FIELD(SENSOR_PROFILE, sensor_type, UINT8)
DECLARE_FIELD(SENSOR_PROFILE, sensor_type, ENUM)
{
// 28 is tank pod
// start filling in next sensor after this record
@ -1090,7 +1097,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, 52, sensor_type, ENUM), // 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),
@ -1337,6 +1344,9 @@ static int traverse_regular(struct garmin_parser_t *garmin,
}
if (field_desc) {
if (field_nr == 253 && !msg_desc->maxfield)
DEBUG(garmin->base.context, "Ignoring timestamp field for undefined message.");
else
field_desc->parse(garmin, base_type, data);
} else {
unknown_field(garmin, data, msg_name, field_nr, base_type, len);
@ -1521,7 +1531,12 @@ traverse_data(struct garmin_parser_t *garmin)
// Compressed records are like normal records
// with that added relative timestamp
DEBUG(garmin->base.context, "Compressed record for type %d", type);
if (!(garmin->type_desc + type)->msg_desc->maxfield)
DEBUG(garmin->base.context, "Ignoring timestamp field for undefined message.");
else
parse_ANY_timestamp(garmin, time);
len = traverse_regular(garmin, data, datasize, type, &time);
} else if (record & 0x40) { // Definition record?
len = traverse_definition(garmin, data, datasize, record);
@ -1615,6 +1630,20 @@ static void add_sensor_string(garmin_parser_t *garmin, const char *desc, const s
static dc_status_t
garmin_parser_set_data (garmin_parser_t *garmin, const unsigned char *data, unsigned int size)
{
// This list is empirical and somewhat speculative
// will have to be confirmed with Garmin
static const struct {
int id;
const char *name;
} models[] = {
{ 2859, "Descent Mk1" },
{ 2991, "Descent Mk1 APAC" },
{ 3258, "Descent Mk2(i)" },
{ 3542, "Descent Mk2s" },
{ 3702, "Descent Mk2 APAC" },
{ 4223, "Descent Mk3" },
};
/* Walk the data once without a callback to set up the core fields */
garmin->callback = NULL;
garmin->userdata = NULL;
@ -1630,6 +1659,17 @@ garmin_parser_set_data (garmin_parser_t *garmin, const unsigned char *data, unsi
if (garmin->dive.firmware)
dc_field_add_string_fmt(&garmin->cache, "Firmware", "%u.%02u",
garmin->dive.firmware / 100, garmin->dive.firmware % 100);
if (garmin->dive.product) {
int i = 0;
for (i = 0; i < C_ARRAY_SIZE(models); i++)
if (models[i].id == garmin->dive.product)
break;
if (i < C_ARRAY_SIZE(models))
dc_field_add_string_fmt(&garmin->cache, "Model", "%s", models[i].name);
else
dc_field_add_string_fmt(&garmin->cache, "Model", "Unknown model ID: %u", garmin->dive.product);
}
// These seem to be the "real" GPS dive coordinates
add_gps_string(garmin, "GPS1", &garmin->gps.SESSION.entry);