From 838717dbae6c7b2ffee15648be70f72992f5c604 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Thu, 8 Jun 2023 22:25:26 +1200 Subject: [PATCH] Add Time Synchronisation for the Shearwater Teric. Add time synchronisation support for the Shearwater Teric. Unlike the other Shearwater dive computers this model has basic support for time zones, so we need to set the time in UTC and configure the time zone offset. Signed-off-by: Michael Keller --- src/shearwater_common.c | 95 +++++++++++++++++++++++++--- src/shearwater_common.h | 7 ++- src/shearwater_petrel.c | 134 ++++++++++++++++++++-------------------- 3 files changed, 159 insertions(+), 77 deletions(-) diff --git a/src/shearwater_common.c b/src/shearwater_common.c index 8d2e035..bea0889 100644 --- a/src/shearwater_common.c +++ b/src/shearwater_common.c @@ -604,13 +604,6 @@ dc_status_t shearwater_common_device_timesync(dc_device_t *abstract, const dc_da return DC_STATUS_INVALIDARGS; } - char shearwater_timestamp[] = { - (unix_timestamp >> 24) & 0xFF, - (unix_timestamp >> 16) & 0xFF, - (unix_timestamp >> 8) & 0xFF, - unix_timestamp & 0xFF - }; - dc_buffer_t *buffer = dc_buffer_new(WDBI_TIME_PACKET_SIZE); if (buffer == NULL) { ERROR(abstract->context, "Insufficient buffer space available."); @@ -619,9 +612,15 @@ dc_status_t shearwater_common_device_timesync(dc_device_t *abstract, const dc_da return DC_STATUS_NOMEMORY; } + char shearwater_timestamp[] = { + (unix_timestamp >> 24) & 0xFF, + (unix_timestamp >> 16) & 0xFF, + (unix_timestamp >> 8) & 0xFF, + unix_timestamp & 0xFF, + }; dc_buffer_append(buffer, shearwater_timestamp, 4); - dc_status_t rc = shearwater_common_can_wdbi(device, buffer, ID_TIME); + dc_status_t rc = shearwater_common_can_wdbi(device, buffer, ID_LOCAL_TIME); if (rc != DC_STATUS_SUCCESS) { ERROR(abstract->context, "Failed to write the dive computer time."); } @@ -630,3 +629,83 @@ dc_status_t shearwater_common_device_timesync(dc_device_t *abstract, const dc_da return rc; } + +dc_status_t shearwater_common_read_model(shearwater_common_device_t *device, unsigned int *model) +{ + dc_buffer_t *buffer = dc_buffer_new(SZ_PACKET); + if (buffer == NULL) { + ERROR(device->base.context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + // Read the hardware type. + dc_status_t rc = shearwater_common_identifier(device, buffer, ID_HARDWARE); + if (rc != DC_STATUS_SUCCESS) { + ERROR(device->base.context, "Failed to read the hardware type."); + dc_buffer_free(buffer); + + return rc; + } + + // Convert and map to the model number. + unsigned int hardware = array_uint_be(dc_buffer_get_data(buffer), dc_buffer_get_size(buffer)); + switch (hardware) { + case 0x0101: + case 0x0202: + *model = PREDATOR; + break; + case 0x0606: + case 0x0A0A: // Nerd 1 + *model = NERD; + break; + case 0x7E2D: + case 0x0E0D: // Nerd 2 + *model = NERD2; + break; + case 0x0404: +case 0x0909: // Petrel 1 + *model = PETREL; + break; + case 0x0505: + case 0x0808: + case 0x0838: + case 0x08A5: + case 0x7828: + case 0x7B2C: + case 0x8838: // Petrel 2 + case 0x0B0B: // current docs (June 2023) imply this is a Petrel 2 + *model = PETREL; + break; + case 0xB407: // Petrel 3 + *model = PETREL3; + break; + case 0x0707: // Perdix + *model = PERDIX; + break; +case 0x0C0C: // current docs (June 2023) imply this is not a valid hardware ID + case 0x0C0D: // current docs (June 2023) show this as Perdix AI + case 0x0D0D: // current docs (June 2023) imply this is not a valid hardware ID + case 0x7C2D: // Perdix AI + case 0x8D6C: + *model = PERDIXAI; + break; + case 0xC407: // Perdix 2 + *model = PERDIX2; + break; + case 0x0F0F: + case 0x1F0A: + case 0x1F0F: + *model = TERIC; + break; +case 0x1512: + *model = PEREGRINE; + break; + default: + // return a model of 0 which is unknown + WARNING(device->base.context, "Unknown hardware type %04x. Assuming Petrel.", hardware); + } + + dc_buffer_free(buffer); + + return rc; +} diff --git a/src/shearwater_common.h b/src/shearwater_common.h index b15c9b8..397d950 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -34,7 +34,10 @@ extern "C" { #define ID_FIRMWARE 0x8011 #define ID_LOGUPLOAD 0x8021 #define ID_HARDWARE 0x8050 -#define ID_TIME 0x9030 +#define ID_LOCAL_TIME 0x9030 +#define ID_UTC_TIME 0x9031 +#define ID_LOCAL_TIME_OFFSET 0x9032 +#define ID_LOCAL_TIME_DST 0x9033 #define WDBI_TIME_PACKET_SIZE 7 @@ -74,6 +77,8 @@ dc_status_t shearwater_common_can_wdbi (shearwater_common_device_t *device, dc_b dc_status_t shearwater_common_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime); +dc_status_t shearwater_common_read_model(shearwater_common_device_t *device, unsigned int *model); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 7eaf71e..3b0a0b3 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -47,8 +47,71 @@ typedef struct shearwater_petrel_device_t { static dc_status_t shearwater_petrel_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime) { - // The Teric will need a different implementation - // to properly support time zones + shearwater_common_device_t *device = (shearwater_common_device_t *)abstract; + + unsigned int model = 0; + shearwater_common_read_model(device, &model); + if (model == TERIC) { + dc_ticks_t unix_timestamp = dc_datetime_mktime(datetime); + if (unix_timestamp == -1) { + ERROR(abstract->context, "Invalid date/time value specified."); + + return DC_STATUS_INVALIDARGS; + } + + dc_buffer_t *buffer = dc_buffer_new(WDBI_TIME_PACKET_SIZE); + if (buffer == NULL) { + ERROR(abstract->context, "Insufficient buffer space available."); + dc_buffer_free(buffer); + + return DC_STATUS_NOMEMORY; + } + + char shearwater_timestamp[] = { + (unix_timestamp >> 24) & 0xFF, + (unix_timestamp >> 16) & 0xFF, + (unix_timestamp >> 8) & 0xFF, + unix_timestamp & 0xFF, + }; + dc_buffer_append(buffer, shearwater_timestamp, 4); + + dc_status_t rc = shearwater_common_can_wdbi(device, buffer, ID_UTC_TIME); + if (rc != DC_STATUS_SUCCESS) { + ERROR(abstract->context, "Failed to write the dive computer UTC time."); + } + + dc_buffer_clear(buffer); + + int local_time_offset_minutes = datetime->timezone / 60; + char shearwater_local_time_offset[] = { + (local_time_offset_minutes >> 24) & 0xFF, + (local_time_offset_minutes >> 16) & 0xFF, + (local_time_offset_minutes >> 8) & 0xFF, + local_time_offset_minutes & 0xFF, + }; + dc_buffer_append(buffer, shearwater_local_time_offset, 4); + + rc = shearwater_common_can_wdbi(device, buffer, ID_LOCAL_TIME_OFFSET); + if (rc != DC_STATUS_SUCCESS) { + ERROR(abstract->context, "Failed to write the dive computer local time offset."); + } + + dc_buffer_clear(buffer); + + // We don't have a way to determine the daylight savings time setting, + // but the required offset is already factored into ID_LOCAL_TIME_OFFSET + char shearwater_local_time_dst[] = { 0, 0, 0, 0 }; + dc_buffer_append(buffer, shearwater_local_time_dst, 4); + + rc = shearwater_common_can_wdbi(device, buffer, ID_LOCAL_TIME_DST); + if (rc != DC_STATUS_SUCCESS) { + ERROR(abstract->context, "Failed to write the dive computer DST setting."); + } + + dc_buffer_free(buffer); + + return rc; + } return shearwater_common_device_timesync(abstract, datetime); } @@ -209,73 +272,8 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Convert to a number. unsigned int firmware = str2num (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), 1); - // Read the hardware type. - rc = shearwater_common_identifier (&device->base, buffer, ID_HARDWARE); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the hardware type."); - dc_buffer_free (buffer); - dc_buffer_free (manifests); - return rc; - } - - // Convert and map to the model number. - unsigned int hardware = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer)); unsigned int model = 0; - switch (hardware) { - case 0x0101: - case 0x0202: - model = PREDATOR; - break; - case 0x0606: - case 0x0A0A: // Nerd 1 - model = NERD; - break; - case 0x7E2D: - case 0x0E0D: // Nerd 2 - model = NERD2; - break; - case 0x0404: - case 0x0909: // Petrel 1 - model = PETREL; - break; - case 0x0505: - case 0x0808: - case 0x0838: - case 0x08A5: - case 0x7828: - case 0x7B2C: - case 0x8838: // Petrel 2 - case 0x0B0B: // current docs (June 2023) imply this is a Petrel 2 - model = PETREL; - break; - case 0xB407: // Petrel 3 - model = PETREL3; - break; - case 0x0707: // Perdix - model = PERDIX; - break; - case 0x0C0C: // current docs (June 2023) imply this is not a valid hardware ID - case 0x0C0D: // current docs (June 2023) show this as Perdix AI - case 0x0D0D: // current docs (June 2023) imply this is not a valid hardware ID - case 0x7C2D: // Perdix AI - case 0x8D6C: - model = PERDIXAI; - break; - case 0xC407: // Perdix 2 - model = PERDIX2; - break; - case 0x0F0F: - case 0x1F0A: - case 0x1F0F: - model = TERIC; - break; - case 0x1512: - model = PEREGRINE; - break; - default: - // return a model of 0 which is unknown - WARNING (abstract->context, "Unknown hardware type %04x. Assuming Petrel.", hardware); - } + shearwater_common_read_model((shearwater_common_device_t *)device, &model); // Emit a device info event. dc_event_devinfo_t devinfo;