From 6e9278ff24705467937d90e9c8bdc4de8994a850 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 30 Apr 2017 09:49:15 +0200 Subject: [PATCH 01/12] Add support for the Mares Quad --- src/descriptor.c | 1 + src/mares_iconhd.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/descriptor.c b/src/descriptor.c index 6dd4cf9..53e862c 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -230,6 +230,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18}, {"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19}, {"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F}, + {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29}, /* Heinrichs Weikamp */ {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0}, {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1}, diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 9a2179c..3a73968 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -42,6 +42,7 @@ #define PUCKPRO 0x18 #define NEMOWIDE2 0x19 #define PUCK2 0x1F +#define QUAD 0x29 #define ACK 0xAA #define EOF 0xEA @@ -125,6 +126,7 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) {"Puck Pro", PUCKPRO}, {"Nemo Wide 2", NEMOWIDE2}, {"Puck 2", PUCK2}, + {"Quad", QUAD}, }; // Check the product name in the version packet against the list @@ -292,6 +294,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, const char * case NEMOWIDE2: case SMART: case SMARTAPNEA: + case QUAD: device->layout = &mares_nemowide2_layout; device->packetsize = 256; break; From 3daba977e0b93fa6fb3736dc42e9ff3c4bc3ff20 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 29 Apr 2017 12:21:54 +0200 Subject: [PATCH 02/12] Don't ignore handshaking errors --- src/uwatec_meridian.c | 6 +++++- src/uwatec_smart.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/uwatec_meridian.c b/src/uwatec_meridian.c index 077ec31..d59b855 100644 --- a/src/uwatec_meridian.c +++ b/src/uwatec_meridian.c @@ -228,7 +228,11 @@ uwatec_meridian_device_open (dc_device_t **out, dc_context_t *context, const cha dc_serial_purge (device->port, DC_DIRECTION_ALL); // Perform the handshaking. - uwatec_meridian_handshake (device); + status = uwatec_meridian_handshake (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to handshake with the device."); + goto error_close; + } *out = (dc_device_t*) device; diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index 2b94a3b..425e76e 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -191,7 +191,11 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) } // Perform the handshaking. - uwatec_smart_handshake (device); + status = uwatec_smart_handshake (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to handshake with the device."); + goto error_close; + } *out = (dc_device_t*) device; From 21c7c9c0091c23bbd1c6c0cabd85dbd6332a755e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 2 May 2017 19:27:26 +0200 Subject: [PATCH 03/12] Implement the read and write functions for the OSTC3 The existing infrastructure for upgrading the firmware can also be used to support reading and writing directly to the internal flash memory. --- src/hw_ostc3.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index b8693cb..e7b7d9e 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -125,6 +125,8 @@ static const unsigned char ostc3_key[16] = { }; static dc_status_t hw_ostc3_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t hw_ostc3_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); +static dc_status_t hw_ostc3_device_write (dc_device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size); static dc_status_t hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); static dc_status_t hw_ostc3_device_close (dc_device_t *abstract); @@ -133,8 +135,8 @@ static const dc_device_vtable_t hw_ostc3_device_vtable = { sizeof(hw_ostc3_device_t), DC_FAMILY_HW_OSTC3, hw_ostc3_device_set_fingerprint, /* set_fingerprint */ - NULL, /* read */ - NULL, /* write */ + hw_ostc3_device_read, /* read */ + hw_ostc3_device_write, /* write */ hw_ostc3_device_dump, /* dump */ hw_ostc3_device_foreach, /* foreach */ hw_ostc3_device_close /* close */ @@ -1181,7 +1183,7 @@ hw_ostc3_firmware_block_read (hw_ostc3_device_t *device, unsigned int addr, unsi } static dc_status_t -hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int addr, unsigned char block[], unsigned int block_size) +hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int addr, const unsigned char block[], unsigned int block_size) { unsigned char buffer[3 + SZ_FIRMWARE_BLOCK]; @@ -1446,6 +1448,87 @@ hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename) } } +static dc_status_t +hw_ostc3_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + if ((address % SZ_FIRMWARE_BLOCK != 0) || + (size % SZ_FIRMWARE_BLOCK != 0)) { + ERROR (abstract->context, "Address or size not aligned to the page size!"); + return DC_STATUS_INVALIDARGS; + } + + // Make sure the device is in service mode. + status = hw_ostc3_device_init (device, SERVICE); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + if (device->hardware == OSTC4) { + return DC_STATUS_UNSUPPORTED; + } + + unsigned int nbytes = 0; + while (nbytes < size) { + // Read a memory page. + status = hw_ostc3_firmware_block_read (device, address + nbytes, data + nbytes, SZ_FIRMWARE_BLOCK); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read block."); + return status; + } + + nbytes += SZ_FIRMWARE_BLOCK; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +hw_ostc3_device_write (dc_device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + if ((address % SZ_FIRMWARE_BLOCK != 0) || + (size % SZ_FIRMWARE_BLOCK != 0)) { + ERROR (abstract->context, "Address or size not aligned to the page size!"); + return DC_STATUS_INVALIDARGS; + } + + // Make sure the device is in service mode. + status = hw_ostc3_device_init (device, SERVICE); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + if (device->hardware == OSTC4) { + return DC_STATUS_UNSUPPORTED; + } + + // Erase the memory pages. + status = hw_ostc3_firmware_erase (device, address, size); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to erase blocks."); + return status; + } + + unsigned int nbytes = 0; + while (nbytes < size) { + // Write a memory page. + status = hw_ostc3_firmware_block_write (device, address + nbytes, data + nbytes, SZ_FIRMWARE_BLOCK); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to write block."); + return status; + } + + nbytes += SZ_FIRMWARE_BLOCK; + } + + return DC_STATUS_SUCCESS; +} + static dc_status_t hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { From 415d7f22144cf4187fcbcd16270c95300f74f53b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 9 May 2017 20:56:08 +0200 Subject: [PATCH 04/12] Fix the Sherwood Wisdom 3 memory layout The logbook ringbuffer starts at 0x03D0 instead of 0x0240. --- src/oceanic_atom2.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index cccfc4e..da19e7f 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -101,7 +101,6 @@ static const oceanic_common_version_t oceanic_atom2_version[] = { static const oceanic_common_version_t oceanic_atom2a_version[] = { {"MANTA R\0\0 512K"}, - {"WISDOM R\0\0 512K"}, {"INSIGHT2 \0\0 512K"}, {"OCEVEO30 \0\0 512K"}, {"ATMOSAI R\0\0 512K"}, @@ -132,6 +131,10 @@ static const oceanic_common_version_t oceanic_default_version[] = { {"COMPMASK \0\0 512K"}, }; +static const oceanic_common_version_t sherwood_wisdom_version[] = { + {"WISDOM R\0\0 512K"}, +}; + static const oceanic_common_version_t oceanic_proplus3_version[] = { {"PROPLUS3 \0\0 512K"}, }; @@ -288,6 +291,20 @@ static const oceanic_common_layout_t oceanic_atom2c_layout = { 0, /* pt_mode_serial */ }; +static const oceanic_common_layout_t sherwood_wisdom_layout = { + 0xFFF0, /* memsize */ + 0x0000, /* cf_devinfo */ + 0x0040, /* cf_pointers */ + 0x03D0, /* rb_logbook_begin */ + 0x0A40, /* rb_logbook_end */ + 8, /* rb_logbook_entry_size */ + 0x0A40, /* rb_profile_begin */ + 0xFE00, /* rb_profile_end */ + 0, /* pt_mode_global */ + 0, /* pt_mode_logbook */ + 0, /* pt_mode_serial */ +}; + static const oceanic_common_layout_t oceanic_proplus3_layout = { 0x10000, /* memsize */ 0x0000, /* cf_devinfo */ @@ -645,6 +662,8 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char device->base.layout = &oceanic_atom2b_layout; } else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_atom2c_version)) { device->base.layout = &oceanic_atom2c_layout; + } else if (OCEANIC_COMMON_MATCH (device->base.version, sherwood_wisdom_version)) { + device->base.layout = &sherwood_wisdom_layout; } else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_proplus3_version)) { device->base.layout = &oceanic_proplus3_layout; } else if (OCEANIC_COMMON_MATCH (device->base.version, tusa_zenair_version)) { From 588e7e7ab459b5df181d7600b891adc7458d9902 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 6 Feb 2015 09:56:21 -0800 Subject: [PATCH 05/12] Predator: don't report PPO2 unless in CC mode Sending this in OC mode is redundant and might confuse applications that assume they only get PPO2 data in CC mode. Signed-off-by: Dirk Hohndel --- src/shearwater_predator_parser.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 039926f..c71c9d7 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -422,11 +422,11 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal // Status flags. unsigned int status = data[offset + 11]; - // PPO2 - sample.ppo2 = data[offset + 6] / 100.0; - if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); - if ((status & OC) == 0) { + // PPO2 + sample.ppo2 = data[offset + 6] / 100.0; + if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); + // Setpoint if (parser->petrel) { sample.setpoint = data[offset + 18] / 100.0; From d3ca3e87bd92bce7a59818998825652e025e341f Mon Sep 17 00:00:00 2001 From: Anton Lundin Date: Wed, 14 Oct 2015 19:49:25 +0200 Subject: [PATCH 06/12] shearwater: Report individual sensor values This reads the reported mV values from the sensors, and based on the calibration values converts it into a ppo2 value to report. Signed-off-by: Anton Lundin --- src/shearwater_predator_parser.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index c71c9d7..7c36651 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -61,6 +61,7 @@ struct shearwater_predator_parser_t { unsigned int ngasmixes; unsigned int oxygen[NGASMIXES]; unsigned int helium[NGASMIXES]; + unsigned int calibration[3]; dc_divemode_t mode; }; @@ -281,6 +282,18 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) offset += parser->samplesize; } + // Cache sensor calibration for later use + parser->calibration[0] = array_uint16_be(data + 87); + parser->calibration[1] = array_uint16_be(data + 89); + parser->calibration[2] = array_uint16_be(data + 91); + // The Predator expects the mV output of the cells to be within 30mV + // to 70mV in 100% O2 at 1 atmosphere. + // If we add 1024 (1000?) to the calibration value, then the sensors + // lines up and matches the average. + parser->calibration[0] += 1024; + parser->calibration[1] += 1024; + parser->calibration[2] += 1024; + // Cache the data for later use. parser->headersize = headersize; parser->footersize = footersize; @@ -424,8 +437,19 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal if ((status & OC) == 0) { // PPO2 +#ifdef SENSOR_AVERAGE sample.ppo2 = data[offset + 6] / 100.0; if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); +#else + sample.ppo2 = data[offset + 12] * parser->calibration[0] / 100000.0; + if (callback && (data[86] & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata); + + sample.ppo2 = data[offset + 14] * parser->calibration[1] / 100000.0; + if (callback && (data[86] & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata); + + sample.ppo2 = data[offset + 15] * parser->calibration[2] / 100000.0; + if (callback && (data[86] & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata); +#endif // Setpoint if (parser->petrel) { From fe2d128b4453903ffb61feb0c10cbc69e9f3b81e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 13 Apr 2017 21:26:56 +0200 Subject: [PATCH 07/12] Apply the calibration correction only for the Predator The calibration values for the Petrel are typically in the range 1600 to 2400, while for Predator they are much smaller, with values in the range 800 to 1400. The consequence is that the calculated ppO2 values are too low for the Predator. Adding a constant offset of about 1000 changes the calibration value to be in approximately the same range as the Petrel, and hence more reasonable ppO2 values. But this correction should only be applied for the Predator, and not the Petrel. Reviewed-by: Anton Lundin --- src/parser.c | 4 ++-- src/shearwater_petrel.h | 2 +- src/shearwater_predator.h | 2 +- src/shearwater_predator_parser.c | 23 +++++++++++++++-------- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/parser.c b/src/parser.c index 98d2c89..9fdb1a9 100644 --- a/src/parser.c +++ b/src/parser.c @@ -149,10 +149,10 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa rc = atomics_cobalt_parser_create (&parser, context); break; case DC_FAMILY_SHEARWATER_PREDATOR: - rc = shearwater_predator_parser_create (&parser, context); + rc = shearwater_predator_parser_create (&parser, context, model); break; case DC_FAMILY_SHEARWATER_PETREL: - rc = shearwater_petrel_parser_create (&parser, context); + rc = shearwater_petrel_parser_create (&parser, context, model); break; case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_parser_create (&parser, context); diff --git a/src/shearwater_petrel.h b/src/shearwater_petrel.h index 8b927d6..c23bb74 100644 --- a/src/shearwater_petrel.h +++ b/src/shearwater_petrel.h @@ -34,7 +34,7 @@ dc_status_t shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, const char *name); dc_status_t -shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context); +shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); #ifdef __cplusplus } diff --git a/src/shearwater_predator.h b/src/shearwater_predator.h index f21d445..4665f80 100644 --- a/src/shearwater_predator.h +++ b/src/shearwater_predator.h @@ -34,7 +34,7 @@ dc_status_t shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, const char *name); dc_status_t -shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context); +shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); #ifdef __cplusplus } diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 7c36651..58d1704 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -48,10 +48,14 @@ #define NGASMIXES 10 +#define PREDATOR 2 +#define PETREL 3 + typedef struct shearwater_predator_parser_t shearwater_predator_parser_t; struct shearwater_predator_parser_t { dc_parser_t base; + unsigned int model; unsigned int petrel; unsigned int samplesize; // Cached fields. @@ -106,7 +110,7 @@ shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned static dc_status_t -shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int petrel) +shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int petrel) { shearwater_predator_parser_t *parser = NULL; const dc_parser_vtable_t *vtable = NULL; @@ -131,6 +135,7 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig } // Set the default values. + parser->model = model; parser->petrel = petrel; parser->samplesize = samplesize; parser->cached = 0; @@ -150,16 +155,16 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig dc_status_t -shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context) +shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) { - return shearwater_common_parser_create (out, context, 0); + return shearwater_common_parser_create (out, context, model, 0); } dc_status_t -shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context) +shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) { - return shearwater_common_parser_create (out, context, 1); + return shearwater_common_parser_create (out, context, model, 1); } @@ -290,9 +295,11 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // to 70mV in 100% O2 at 1 atmosphere. // If we add 1024 (1000?) to the calibration value, then the sensors // lines up and matches the average. - parser->calibration[0] += 1024; - parser->calibration[1] += 1024; - parser->calibration[2] += 1024; + if (parser->model == PREDATOR) { + parser->calibration[0] += 1024; + parser->calibration[1] += 1024; + parser->calibration[2] += 1024; + } // Cache the data for later use. parser->headersize = headersize; From 7e7cbd55b1c2b619a9f39a663bccc81c688b01b3 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 13 Apr 2017 21:26:56 +0200 Subject: [PATCH 08/12] Replace the constant offset with a scaling factor Correcting the Predator calibration value with a scaling factor produces even more reasonable ppO2 values compared to using a constant offset. The scaling factor of 2.2 is based on a linear regression between the average ppO2 reported by the dive computer, and the average ppO2 calculated over all (calibrated) sensors using the raw calibration value. Reviewed-by: Anton Lundin --- src/shearwater_predator_parser.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 58d1704..3e5fcbf 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -65,7 +65,7 @@ struct shearwater_predator_parser_t { unsigned int ngasmixes; unsigned int oxygen[NGASMIXES]; unsigned int helium[NGASMIXES]; - unsigned int calibration[3]; + double calibration[3]; dc_divemode_t mode; }; @@ -288,17 +288,17 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } // Cache sensor calibration for later use - parser->calibration[0] = array_uint16_be(data + 87); - parser->calibration[1] = array_uint16_be(data + 89); - parser->calibration[2] = array_uint16_be(data + 91); + parser->calibration[0] = array_uint16_be(data + 87) / 100000.0; + parser->calibration[1] = array_uint16_be(data + 89) / 100000.0; + parser->calibration[2] = array_uint16_be(data + 91) / 100000.0; // The Predator expects the mV output of the cells to be within 30mV // to 70mV in 100% O2 at 1 atmosphere. - // If we add 1024 (1000?) to the calibration value, then the sensors - // lines up and matches the average. + // If the calibration value is scaled with a factor 2.2, then the + // sensors lines up and matches the average. if (parser->model == PREDATOR) { - parser->calibration[0] += 1024; - parser->calibration[1] += 1024; - parser->calibration[2] += 1024; + for (size_t i = 0; i < 3; ++i) { + parser->calibration[i] *= 2.2; + } } // Cache the data for later use. @@ -448,14 +448,15 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal sample.ppo2 = data[offset + 6] / 100.0; if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); #else - sample.ppo2 = data[offset + 12] * parser->calibration[0] / 100000.0; + sample.ppo2 = data[offset + 12] * parser->calibration[0]; if (callback && (data[86] & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata); - sample.ppo2 = data[offset + 14] * parser->calibration[1] / 100000.0; + sample.ppo2 = data[offset + 14] * parser->calibration[1]; if (callback && (data[86] & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata); - sample.ppo2 = data[offset + 15] * parser->calibration[2] / 100000.0; + sample.ppo2 = data[offset + 15] * parser->calibration[2]; if (callback && (data[86] & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata); + } #endif // Setpoint From d6806ab494459b6ffc6897bddbf2622783743bec Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 8 May 2017 19:24:32 +0200 Subject: [PATCH 09/12] Report the ppO2 in external O2 sensor mode only The O2 sensor millivolt values are only valid if external O2 sensor monitoring is enabled. Note that the interpretation of the PPO2 status bit appears to be reversed (0=external and 1=internal). Reviewed-by: Anton Lundin --- src/shearwater_predator_parser.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 3e5fcbf..e922220 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -448,14 +448,15 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal sample.ppo2 = data[offset + 6] / 100.0; if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); #else - sample.ppo2 = data[offset + 12] * parser->calibration[0]; - if (callback && (data[86] & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata); + if ((status & PPO2_EXTERNAL) == 0) { + sample.ppo2 = data[offset + 12] * parser->calibration[0]; + if (callback && (data[86] & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata); - sample.ppo2 = data[offset + 14] * parser->calibration[1]; - if (callback && (data[86] & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata); + sample.ppo2 = data[offset + 14] * parser->calibration[1]; + if (callback && (data[86] & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata); - sample.ppo2 = data[offset + 15] * parser->calibration[2]; - if (callback && (data[86] & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata); + sample.ppo2 = data[offset + 15] * parser->calibration[2]; + if (callback && (data[86] & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata); } #endif From 2854453f264d6414f50f897a9f37b3a60907aa8f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 9 May 2017 21:16:39 +0200 Subject: [PATCH 10/12] Add the firmware version to the devinfo event --- src/divesystem_idive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index d8c02e8..b1913e6 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -372,7 +372,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = array_uint16_le (packet); - devinfo.firmware = 0; + devinfo.firmware = array_uint32_le (packet + 2); devinfo.serial = array_uint32_le (packet + 6); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); From 55b9307e193d989facad7383d989ec475b5a258e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 15 May 2017 22:14:47 +0200 Subject: [PATCH 11/12] Add support for the new iX3M APOS4 firmware The new APOS4 firmware changed the data format and communication protocol. The size of the samples changed from 54 to 64 bytes, and in order to speedup the download, a single data packet contains 3 samples at once. If the number of samples is not an exact multiple of three, the last packet appears to contain garbage data. For parsing, the firmware version is available in the dive header. Unfortunately it can't be used for dives that are downloaded from a device with the new firmware, but which have been recorded with an older firmware. Such dives store the old firmware version in the dive header, but they also use the new sample format. As a workaround, we inspect the size of the dive. --- src/divesystem_idive.c | 35 +++++++++++++++++++++++++++++++---- src/divesystem_idive_parser.c | 29 ++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index b1913e6..d5bf2e5 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -57,6 +57,7 @@ typedef struct divesystem_idive_commands_t { divesystem_idive_command_t range; divesystem_idive_command_t header; divesystem_idive_command_t sample; + unsigned int nsamples; } divesystem_idive_commands_t; typedef struct divesystem_idive_device_t { @@ -86,6 +87,7 @@ static const divesystem_idive_commands_t idive = { {0x98, 0x04}, {0xA0, 0x32}, {0xA8, 0x2A}, + 1, }; static const divesystem_idive_commands_t ix3m = { @@ -93,6 +95,15 @@ static const divesystem_idive_commands_t ix3m = { {0x78, 0x04}, {0x79, 0x36}, {0x7A, 0x36}, + 1, +}; + +static const divesystem_idive_commands_t ix3m_apos4 = { + {0x11, 0x1A}, + {0x78, 0x04}, + {0x79, 0x36}, + {0x7A, 0x40}, + 3, }; dc_status_t @@ -382,6 +393,14 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb vendor.size = commands->id.size; device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + if (device->model >= IX3M_EASY && device->model <= IX3M_REB) { + // Detect the APOS4 firmware. + unsigned int apos4 = (devinfo.firmware / 10000000) >= 4; + if (apos4) { + commands = &ix3m_apos4; + } + } + unsigned char cmd_range[] = {commands->range.cmd, 0x8D}; rc = divesystem_idive_transfer (device, cmd_range, sizeof(cmd_range), packet, commands->range.size); if (rc != DC_STATUS_SUCCESS) @@ -429,20 +448,28 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb dc_buffer_reserve(buffer, commands->header.size + commands->sample.size * nsamples); dc_buffer_append(buffer, packet, commands->header.size); - for (unsigned int j = 0; j < nsamples; ++j) { + for (unsigned int j = 0; j < nsamples; j += commands->nsamples) { unsigned int idx = j + 1; unsigned char cmd_sample[] = {commands->sample.cmd, (idx ) & 0xFF, (idx >> 8) & 0xFF}; - rc = divesystem_idive_transfer (device, cmd_sample, sizeof(cmd_sample), packet, commands->sample.size); + rc = divesystem_idive_transfer (device, cmd_sample, sizeof(cmd_sample), packet, commands->sample.size * commands->nsamples); if (rc != DC_STATUS_SUCCESS) return rc; + // If the number of samples is not an exact multiple of the + // number of samples per packet, then the last packet + // appears to contain garbage data. Ignore those samples. + unsigned int n = commands->nsamples; + if (j + n > nsamples) { + n = nsamples - j; + } + // Update and emit a progress event. - progress.current = i * NSTEPS + STEP(j + 2, nsamples + 1); + progress.current = i * NSTEPS + STEP(j + n + 1, nsamples + 1); device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - dc_buffer_append(buffer, packet, commands->sample.size); + dc_buffer_append(buffer, packet, commands->sample.size * n); } unsigned char *data = dc_buffer_get_data(buffer); diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index 968765c..3ff21b6 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -37,6 +37,7 @@ #define SZ_SAMPLE_IDIVE 0x2A #define SZ_HEADER_IX3M 0x36 #define SZ_SAMPLE_IX3M 0x36 +#define SZ_SAMPLE_IX3M_APOS4 0x40 #define NGASMIXES 8 @@ -53,8 +54,8 @@ typedef struct divesystem_idive_parser_t divesystem_idive_parser_t; struct divesystem_idive_parser_t { dc_parser_t base; + unsigned int model; unsigned int headersize; - unsigned int samplesize; // Cached fields. unsigned int cached; unsigned int divemode; @@ -97,12 +98,11 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign } // Set the default values. + parser->model = model; if (model >= IX3M_EASY && model <= IX3M_REB) { parser->headersize = SZ_HEADER_IX3M; - parser->samplesize = SZ_SAMPLE_IX3M; } else { parser->headersize = SZ_HEADER_IDIVE; - parser->samplesize = SZ_SAMPLE_IDIVE; } parser->cached = 0; parser->divemode = INVALID; @@ -241,8 +241,27 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba unsigned int mode_previous = INVALID; unsigned int divemode = INVALID; + unsigned int nsamples = array_uint16_le (data + 1); + unsigned int samplesize = SZ_SAMPLE_IDIVE; + if (parser->model >= IX3M_EASY && parser->model <= IX3M_REB) { + // Detect the APOS4 firmware. + unsigned int firmware = array_uint32_le(data + 0x2A); + unsigned int apos4 = (firmware / 10000000) >= 4; + if (apos4) { + // Dive downloaded and recorded with the APOS4 firmware. + samplesize = SZ_SAMPLE_IX3M_APOS4; + } else if (size == parser->headersize + nsamples * SZ_SAMPLE_IX3M_APOS4) { + // Dive downloaded with the APOS4 firmware, but recorded + // with an older firmware. + samplesize = SZ_SAMPLE_IX3M_APOS4; + } else { + // Dive downloaded and recorded with an older firmware. + samplesize = SZ_SAMPLE_IX3M; + } + } + unsigned int offset = parser->headersize; - while (offset + parser->samplesize <= size) { + while (offset + samplesize <= size) { dc_sample_value_t sample = {0}; // Time (seconds). @@ -328,7 +347,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba sample.cns = cns / 100.0; if (callback) callback (DC_SAMPLE_CNS, sample, userdata); - offset += parser->samplesize; + offset += samplesize; } // Cache the data for later use. From 28947876a2bd0d30ccea0d35a9264c5fe2fb0ac3 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 19 May 2017 20:02:33 +0200 Subject: [PATCH 12/12] Add support for the new models --- src/descriptor.c | 9 ++++++++- src/divesystem_idive.c | 4 ++-- src/divesystem_idive_parser.c | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 53e862c..70b51d4 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -290,8 +290,15 @@ static const dc_descriptor_t g_descriptors[] = { {"DiveSystem", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x0B}, {"DiveSystem", "iX3M Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x22}, {"DiveSystem", "iX3M Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x23}, - {"DiveSystem", "iX3M Tec", DC_FAMILY_DIVESYSTEM_IDIVE, 0x24}, + {"DiveSystem", "iX3M Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x24}, {"DiveSystem", "iX3M Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x25}, + {"DiveSystem", "iX3M Pro Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x32}, + {"DiveSystem", "iX3M Pro Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x34}, + {"DiveSystem", "iX3M Pro Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x35}, + {"DiveSystem", "iDive2 Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x40}, + {"DiveSystem", "iDive2 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42}, + {"DiveSystem", "iDive2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44}, + {"DiveSystem", "iDive2 Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45}, {"Cochran", "Commander", DC_FAMILY_COCHRAN_COMMANDER, 0}, {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 1}, {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 2}, diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index d5bf2e5..e2a5cda 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -367,7 +367,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb unsigned char packet[MAXPACKET - 2]; const divesystem_idive_commands_t *commands = &idive; - if (device->model >= IX3M_EASY && device->model <= IX3M_REB) { + if (device->model >= IX3M_EASY) { commands = &ix3m; } @@ -393,7 +393,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb vendor.size = commands->id.size; device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - if (device->model >= IX3M_EASY && device->model <= IX3M_REB) { + if (device->model >= IX3M_EASY) { // Detect the APOS4 firmware. unsigned int apos4 = (devinfo.firmware / 10000000) >= 4; if (apos4) { diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index 3ff21b6..473dc57 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -99,7 +99,7 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign // Set the default values. parser->model = model; - if (model >= IX3M_EASY && model <= IX3M_REB) { + if (model >= IX3M_EASY) { parser->headersize = SZ_HEADER_IX3M; } else { parser->headersize = SZ_HEADER_IDIVE; @@ -243,7 +243,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba unsigned int nsamples = array_uint16_le (data + 1); unsigned int samplesize = SZ_SAMPLE_IDIVE; - if (parser->model >= IX3M_EASY && parser->model <= IX3M_REB) { + if (parser->model >= IX3M_EASY) { // Detect the APOS4 firmware. unsigned int firmware = array_uint32_le(data + 0x2A); unsigned int apos4 = (firmware / 10000000) >= 4;