static dc_status_t reefnet_sensuspro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) { reefnet_sensuspro_parser_t *parser = (reefnet_sensuspro_parser_t *) abstract; if (abstract->size < 12) return DC_STATUS_DATAFORMAT; if (!parser->cached) { const unsigned char footer[2] = {0xFF, 0xFF}; const unsigned char *data = abstract->data; unsigned int size = abstract->size; unsigned int interval = array_uint16_le (data + 4); unsigned int maxdepth = 0; unsigned int nsamples = 0; unsigned int offset = 10; while (offset + sizeof (footer) <= size && memcmp (data + offset, footer, sizeof (footer)) != 0) { unsigned int value = array_uint16_le (data + offset); unsigned int depth = (value & 0x01FF); if (depth > maxdepth) maxdepth = depth; nsamples++; offset += 2; } parser->cached = 1; parser->divetime = nsamples * interval; parser->maxdepth = maxdepth; } if (value) { switch (type) { case DC_FIELD_DIVETIME: *((unsigned int *) value) = parser->divetime; break; case DC_FIELD_MAXDEPTH: *((double *) value) = (parser->maxdepth * FSW - parser->atmospheric) / parser->hydrostatic; break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = 0; break; case DC_FIELD_DIVEMODE: *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; break; default: return DC_STATUS_UNSUPPORTED; } } return DC_STATUS_SUCCESS; }
static dc_status_t hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) { hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; // Cache the header data. dc_status_t rc = hw_ostc_parser_cache (parser); if (rc != DC_STATUS_SUCCESS) return rc; unsigned int version = parser->version; const hw_ostc_layout_t *layout = parser->layout; unsigned int divetime = 0; if (version > 0x20) { // Use the dive time stored in the extended header, rounded down towards // the nearest minute, to match the value displayed by the ostc. divetime = (array_uint16_le (data + layout->duration) / 60) * 60; } else { // Use the normal dive time (excluding the shallow parts of the dive). divetime = array_uint16_le (data + layout->divetime) * 60 + data[layout->divetime + 2]; } const unsigned char *p = data + layout->datetime; dc_datetime_t dt; if (version == 0x23) { dt.year = p[0] + 2000; dt.month = p[1]; dt.day = p[2]; } else { dt.year = p[2] + 2000; dt.month = p[0]; dt.day = p[1]; } dt.hour = p[3]; dt.minute = p[4]; dt.second = 0; dc_ticks_t ticks = dc_datetime_mktime (&dt); if (ticks == (dc_ticks_t) -1) return DC_STATUS_DATAFORMAT; ticks -= divetime; if (!dc_datetime_localtime (datetime, ticks)) return DC_STATUS_DATAFORMAT; return DC_STATUS_SUCCESS; }
static dc_status_t reefnet_sensuspro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { reefnet_sensuspro_parser_t *parser = (reefnet_sensuspro_parser_t*) abstract; const unsigned char header[4] = {0x00, 0x00, 0x00, 0x00}; const unsigned char footer[2] = {0xFF, 0xFF}; const unsigned char *data = abstract->data; unsigned int size = abstract->size; unsigned int offset = 0; while (offset + sizeof (header) <= size) { if (memcmp (data + offset, header, sizeof (header)) == 0) { if (offset + 10 > size) return DC_STATUS_DATAFORMAT; unsigned int time = 0; unsigned int interval = array_uint16_le (data + offset + 4); offset += 10; while (offset + sizeof (footer) <= size && memcmp (data + offset, footer, sizeof (footer)) != 0) { unsigned int value = array_uint16_le (data + offset); unsigned int depth = (value & 0x01FF); unsigned int temperature = (value & 0xFE00) >> 9; dc_sample_value_t sample = {0}; // Time (seconds) time += interval; sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Temperature (°F) sample.temperature = (temperature - 32.0) * (5.0 / 9.0); if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); // Depth (absolute pressure in fsw) sample.depth = (depth * FSW - parser->atmospheric) / parser->hydrostatic; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); offset += 2; } break; } else { offset++; } }
static unsigned int get_profile_first (const unsigned char data[], const oceanic_common_layout_t *layout) { unsigned int value; if (layout->pt_mode_logbook == 0) { value = array_uint16_le (data + 5); } else { value = array_uint16_le (data + 4); } if (layout->memsize > 0x10000) return (value & 0x1FFF) * PAGESIZE; else return (value & 0x0FFF) * PAGESIZE; }
static dc_status_t hw_ostc_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { dc_buffer_t *buffer = dc_buffer_new (0); if (buffer == NULL) return DC_STATUS_NOMEMORY; dc_status_t rc = hw_ostc_device_dump (abstract, buffer); if (rc != DC_STATUS_SUCCESS) { dc_buffer_free (buffer); return rc; } // Emit a device info event. unsigned char *data = dc_buffer_get_data (buffer); dc_event_devinfo_t devinfo; devinfo.firmware = array_uint16_be (data + 264); devinfo.serial = array_uint16_le (data + 6); if (devinfo.serial > 7000) devinfo.model = 3; // OSTC 2C else if (devinfo.serial > 2048) devinfo.model = 2; // OSTC 2N else if (devinfo.serial > 300) devinfo.model = 1; // OSTC Mk2 else devinfo.model = 0; // OSTC device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); rc = hw_ostc_extract_dives (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); dc_buffer_free (buffer); return rc; }
dc_status_t atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsigned int size) { atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract; if (!ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS; if (size < SZ_VERSION) return DC_STATUS_INVALIDARGS; #ifdef HAVE_LIBUSB // Send the command to the dive computer. uint8_t bRequest = 0x01; int rc = libusb_control_transfer (device->handle, LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, bRequest, 0, 0, NULL, 0, TIMEOUT); if (rc != LIBUSB_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE(rc); } HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1); // Receive the answer from the dive computer. int length = 0; unsigned char packet[SZ_VERSION + 2] = {0}; rc = libusb_bulk_transfer (device->handle, 0x82, packet, sizeof (packet), &length, TIMEOUT); if (rc != LIBUSB_SUCCESS || length != sizeof (packet)) { ERROR (abstract->context, "Failed to receive the answer."); return EXITCODE(rc); } HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length); // Verify the checksum of the packet. unsigned short crc = array_uint16_le (packet + SZ_VERSION); unsigned short ccrc = checksum_add_uint16 (packet, SZ_VERSION, 0x0); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; } memcpy (data, packet, SZ_VERSION); return DC_STATUS_SUCCESS; #else return DC_STATUS_UNSUPPORTED; #endif }
static dc_status_t hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; // Cache the parser data. dc_status_t rc = hw_ostc_parser_cache (parser); if (rc != DC_STATUS_SUCCESS) return rc; unsigned int version = parser->version; unsigned int header = parser->header; const hw_ostc_layout_t *layout = parser->layout; // Get the sample rate. unsigned int samplerate = 0; if (version == 0x23) samplerate = data[header + 3]; else samplerate = data[36]; // Get the salinity factor. unsigned int salinity = data[layout->salinity]; if (version == 0x23) salinity += 100; if (salinity < 100 || salinity > 104) salinity = 100; double hydrostatic = GRAVITY * salinity * 10.0; // Get the number of sample descriptors. unsigned int nconfig = 0; if (version == 0x23) nconfig = data[header + 4]; else nconfig = 6; if (nconfig > MAXCONFIG) { ERROR(abstract->context, "Too many sample descriptors."); return DC_STATUS_DATAFORMAT; } // Get the extended sample configuration. hw_ostc_sample_info_t info[MAXCONFIG] = {{0}}; for (unsigned int i = 0; i < nconfig; ++i) { if (version == 0x23) { info[i].type = data[header + 5 + 3 * i + 0]; info[i].size = data[header + 5 + 3 * i + 1]; info[i].divisor = data[header + 5 + 3 * i + 2]; } else { info[i].type = i; info[i].divisor = (data[37 + i] & 0x0F); info[i].size = (data[37 + i] & 0xF0) >> 4; } if (info[i].divisor) { switch (info[i].type) { case 0: // Temperature case 1: // Deco / NDL if (info[i].size != 2) { ERROR(abstract->context, "Unexpected sample size."); return DC_STATUS_DATAFORMAT; } break; case 5: // CNS if (info[i].size != 1 && info[i].size != 2) { ERROR(abstract->context, "Unexpected sample size."); return DC_STATUS_DATAFORMAT; } break; default: // Not yet used. break; } } } unsigned int time = 0; unsigned int nsamples = 0; unsigned int offset = header; if (version == 0x23) offset += 5 + 3 * nconfig; while (offset + 3 <= size) { dc_sample_value_t sample = {0}; nsamples++; // Time (seconds). time += samplerate; sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Initial gas mix. if (time == samplerate) { unsigned int idx = parser->initial; unsigned int o2 = parser->gasmix[idx].oxygen; unsigned int he = parser->gasmix[idx].helium; sample.event.type = SAMPLE_EVENT_GASCHANGE2; sample.event.time = 0; sample.event.flags = 0; sample.event.value = o2 | (he << 16); if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Depth (mbar). unsigned int depth = array_uint16_le (data + offset); sample.depth = (depth * BAR / 1000.0) / hydrostatic; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); offset += 2; // Extended sample info. unsigned int length = data[offset] & 0x7F; offset += 1; // Check for buffer overflows. if (offset + length > size) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } // Get the event byte(s). unsigned int nbits = 0; unsigned int events = 0; while (data[offset - 1] & 0x80) { if (nbits && version != 0x23) break; if (length < 1) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } events |= data[offset] << nbits; nbits += 8; offset++; length--; } // Alarms sample.event.type = 0; sample.event.time = 0; sample.event.flags = 0; sample.event.value = 0; switch (events & 0x0F) { case 0: // No Alarm break; case 1: // Slow sample.event.type = SAMPLE_EVENT_ASCENT; break; case 2: // Deco Stop missed sample.event.type = SAMPLE_EVENT_CEILING; break; case 3: // Deep Stop missed sample.event.type = SAMPLE_EVENT_CEILING; break; case 4: // ppO2 Low Warning sample.event.type = SAMPLE_EVENT_PO2; break; case 5: // ppO2 High Warning sample.event.type = SAMPLE_EVENT_PO2; break; case 6: // Manual Marker sample.event.type = SAMPLE_EVENT_BOOKMARK; break; case 7: // Low Battery break; } if (sample.event.type && callback) callback (DC_SAMPLE_EVENT, sample, userdata); // Manual Gas Set & Change if (events & 0x10) { if (length < 2) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } unsigned int o2 = data[offset]; unsigned int he = data[offset + 1]; unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, MANUAL); if (idx >= parser->ngasmixes) { if (idx >= NGASMIXES) { ERROR (abstract->context, "Maximum number of gas mixes reached."); return DC_STATUS_NOMEMORY; } parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].helium = he; parser->ngasmixes = idx + 1; } sample.event.type = SAMPLE_EVENT_GASCHANGE2; sample.event.time = 0; sample.event.flags = 0; sample.event.value = o2 | (he << 16); if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); offset += 2; length -= 2; } // Gas Change if (events & 0x20) { if (length < 1) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } unsigned int idx = data[offset]; if (idx < 1 || idx > parser->ngasmixes) { ERROR(abstract->context, "Invalid gas mix."); return DC_STATUS_DATAFORMAT; } idx--; /* Convert to a zero based index. */ unsigned int o2 = parser->gasmix[idx].oxygen; unsigned int he = parser->gasmix[idx].helium; sample.event.type = SAMPLE_EVENT_GASCHANGE2; sample.event.time = 0; sample.event.flags = 0; sample.event.value = o2 | (he << 16); if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); offset++; length--; } if (version == 0x23) { // SetPoint Change if (events & 0x40) { if (length < 1) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } sample.setpoint = data[offset] / 100.0; if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); offset++; length--; } // Bailout Event if (events & 0x0100) { if (length < 2) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } sample.event.type = SAMPLE_EVENT_GASCHANGE2; sample.event.time = 0; sample.event.flags = 0; sample.event.value = data[offset] | (data[offset + 1] << 16); if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); offset += 2; length -= 2; } } // Extended sample info. for (unsigned int i = 0; i < nconfig; ++i) { if (info[i].divisor && (nsamples % info[i].divisor) == 0) { if (length < info[i].size) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } unsigned int value = 0; switch (info[i].type) { case 0: // Temperature (0.1 °C). value = array_uint16_le (data + offset); sample.temperature = value / 10.0; if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); break; case 1: // Deco / NDL if (data[offset]) { sample.deco.type = DC_DECO_DECOSTOP; sample.deco.depth = data[offset]; } else { sample.deco.type = DC_DECO_NDL; sample.deco.depth = 0.0; } sample.deco.time = data[offset + 1] * 60; if (callback) callback (DC_SAMPLE_DECO, sample, userdata); break; case 5: // CNS if (info[i].size == 2) sample.cns = array_uint16_le (data + offset) / 100.0; else sample.cns = data[offset] / 100.0; if (callback) callback (DC_SAMPLE_CNS, sample, userdata); break; default: // Not yet used. break; } offset += info[i].size; length -= info[i].size; } } if (version != 0x23) { // SetPoint Change if (events & 0x40) { if (length < 1) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } sample.setpoint = data[offset] / 100.0; if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); offset++; length--; } // Bailout Event if (events & 0x80) { if (length < 2) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } sample.event.type = SAMPLE_EVENT_GASCHANGE2; sample.event.time = 0; sample.event.flags = 0; sample.event.value = data[offset] | (data[offset + 1] << 16); if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); offset += 2; length -= 2; } } // Skip remaining sample bytes (if any). if (length) { WARNING (abstract->context, "Remaining %u bytes skipped.", length); } offset += length; } if (offset + 2 > size || data[offset] != 0xFD || data[offset + 1] != 0xFD) { ERROR (abstract->context, "Invalid end marker found!"); return DC_STATUS_DATAFORMAT; } parser->cached = PROFILE; return DC_STATUS_SUCCESS; }
static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + SZ_MEMORY; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Download the version data. unsigned char id[SZ_VERSION] = {0}; dc_status_t rc = hw_ostc3_device_version (abstract, id, sizeof (id)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the version."); return rc; } // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = 0; devinfo.firmware = array_uint16_be (id + 2); devinfo.serial = array_uint16_le (id + 0); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate memory. unsigned char *header = (unsigned char *) malloc (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); if (header == NULL) { ERROR (abstract->context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } // Download the logbook headers. rc = hw_ostc3_transfer (device, &progress, HEADER, NULL, 0, header, RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the header."); free (header); return rc; } // Locate the most recent dive. // The device maintains an internal counter which is incremented for every // dive, and the current value at the time of the dive is stored in the // dive header. Thus the most recent dive will have the highest value. unsigned int count = 0; unsigned int latest = 0; unsigned int maximum = 0; for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { unsigned int offset = i * RB_LOGBOOK_SIZE; // Ignore uninitialized header entries. if (array_isequal (header + offset, RB_LOGBOOK_SIZE, 0xFF)) continue; // Get the internal dive number. unsigned int current = array_uint16_le (header + offset + 80); if (current > maximum) { maximum = current; latest = i; } count++; } // Calculate the total and maximum size. unsigned int ndives = 0; unsigned int size = 0; unsigned int maxsize = 0; for (unsigned int i = 0; i < count; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = idx * RB_LOGBOOK_SIZE; // Uninitialized header entries should no longer be present at this // stage, unless the dives are interleaved with empty entries. But // that's something we don't support at all. if (array_isequal (header + offset, RB_LOGBOOK_SIZE, 0xFF)) { WARNING (abstract->context, "Unexpected empty header found."); break; } // Get the firmware version. unsigned int firmware = array_uint16_be (header + offset + 0x30); // Calculate the profile length. unsigned int length = RB_LOGBOOK_SIZE + array_uint24_le (header + offset + 9) - 6; if (firmware >= 93) length += 3; // Check the fingerprint data. if (memcmp (header + offset + 12, device->fingerprint, sizeof (device->fingerprint)) == 0) break; if (length > maxsize) maxsize = length; size += length; ndives++; } // Update and emit a progress event. progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Finish immediately if there are no dives available. if (ndives == 0) { free (header); return DC_STATUS_SUCCESS; } // Allocate enough memory for the largest dive. unsigned char *profile = (unsigned char *) malloc (maxsize); if (profile == NULL) { ERROR (abstract->context, "Failed to allocate memory."); free (header); return DC_STATUS_NOMEMORY; } // Download the dives. for (unsigned int i = 0; i < ndives; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = idx * RB_LOGBOOK_SIZE; // Get the firmware version. unsigned int firmware = array_uint16_be (header + offset + 0x30); // Calculate the profile length. unsigned int length = RB_LOGBOOK_SIZE + array_uint24_le (header + offset + 9) - 6; if (firmware >= 93) length += 3; // Download the dive. unsigned char number[1] = {idx}; rc = hw_ostc3_transfer (device, &progress, DIVE, number, sizeof (number), profile, length); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the dive."); free (profile); free (header); return rc; } // Verify the header in the logbook and profile are identical. if (memcmp (profile, header + offset, RB_LOGBOOK_SIZE) != 0) { ERROR (abstract->context, "Unexpected profile header."); free (profile); free (header); return rc; } if (callback && !callback (profile, length, profile + 12, sizeof (device->fingerprint), userdata)) break; } free (profile); free (header); return DC_STATUS_SUCCESS; }
static dc_status_t mares_nemo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) { mares_nemo_parser_t *parser = (mares_nemo_parser_t *) abstract; if (abstract->size == 0) return DC_STATUS_DATAFORMAT; const unsigned char *data = abstract->data; const unsigned char *p = abstract->data + 2 + parser->sample_count * parser->sample_size; if (value) { if (parser->mode != parser->freedive) { dc_gasmix_t *gasmix = (dc_gasmix_t *) value; switch (type) { case DC_FIELD_DIVETIME: *((unsigned int *) value) = parser->sample_count * 20; break; case DC_FIELD_MAXDEPTH: *((double *) value) = array_uint16_le (p + 53 - 10) / 10.0; break; case DC_FIELD_GASMIX_COUNT: if (parser->mode == 0 || parser->mode == 1) *((unsigned int *) value) = 1; else *((unsigned int *) value) = 0; break; case DC_FIELD_GASMIX: switch (parser->mode) { case 0: // Air gasmix->oxygen = 0.21; break; case 1: // Nitrox gasmix->oxygen = p[53 - 43] / 100.0; break; default: return DC_STATUS_UNSUPPORTED; } gasmix->helium = 0.0; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; default: return DC_STATUS_UNSUPPORTED; } } else { unsigned int divetime = 0; switch (type) { case DC_FIELD_DIVETIME: for (unsigned int i = 0; i < parser->sample_count; ++i) { unsigned int idx = 2 + parser->sample_size * i; divetime += data[idx + 2] + data[idx + 3] * 60; } *((unsigned int *) value) = divetime; break; case DC_FIELD_MAXDEPTH: *((double *) value) = array_uint16_le (p + 28 - 10) / 10.0; break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = 0; break; default: return DC_STATUS_UNSUPPORTED; } } } return DC_STATUS_SUCCESS; }
static dc_status_t hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) { hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; // Cache the header data. dc_status_t rc = hw_ostc_parser_cache (parser); if (rc != DC_STATUS_SUCCESS) return rc; // Cache the profile data. if (parser->cached < PROFILE) { rc = hw_ostc_parser_samples_foreach (abstract, NULL, NULL); if (rc != DC_STATUS_SUCCESS) return rc; } unsigned int version = parser->version; const hw_ostc_layout_t *layout = parser->layout; dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; dc_field_string_t *string = (dc_field_string_t *) value; unsigned int salinity = data[layout->salinity]; if (version == 0x23) salinity += 100; char buf[BUFLEN]; if (value) { switch (type) { case DC_FIELD_DIVETIME: *((unsigned int *) value) = array_uint16_le (data + layout->divetime) * 60 + data[layout->divetime + 2]; break; case DC_FIELD_MAXDEPTH: *((double *) value) = array_uint16_le (data + layout->maxdepth) / 100.0; break; case DC_FIELD_AVGDEPTH: *((double *) value) = array_uint16_le (data + layout->avgdepth) / 100.0; break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = parser->ngasmixes; break; case DC_FIELD_GASMIX: gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; case DC_FIELD_SALINITY: if (salinity < 100 || salinity > 104) return DC_STATUS_UNSUPPORTED; if (salinity == 100) water->type = DC_WATER_FRESH; else water->type = DC_WATER_SALT; water->density = salinity * 10.0; break; case DC_FIELD_ATMOSPHERIC: *((double *) value) = array_uint16_le (data + layout->atmospheric) / 1000.0; break; case DC_FIELD_TEMPERATURE_MINIMUM: *((double *) value) = (signed short) array_uint16_le (data + layout->temperature) / 10.0; break; case DC_FIELD_DIVEMODE: if (version == 0x21) { switch (data[51]) { case OSTC_APNEA: *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; break; case OSTC_GAUGE: *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; break; case OSTC_ZHL16_OC: case OSTC_ZHL16_OC_GF: *((dc_divemode_t *) value) = DC_DIVEMODE_OC; break; case OSTC_ZHL16_CC: case OSTC_ZHL16_CC_GF: case OSTC_PSCR_GF: *((dc_divemode_t *) value) = DC_DIVEMODE_CC; break; default: return DC_STATUS_DATAFORMAT; } } else if (version == 0x22) { switch (data[51]) { case FROG_ZHL16: case FROG_ZHL16_GF: *((dc_divemode_t *) value) = DC_DIVEMODE_OC; break; case FROG_APNEA: *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; break; default: return DC_STATUS_DATAFORMAT; } } else if (version == 0x23) { switch (data[82]) { case OSTC3_OC: *((dc_divemode_t *) value) = DC_DIVEMODE_OC; break; case OSTC3_CC: *((dc_divemode_t *) value) = DC_DIVEMODE_CC; break; case OSTC3_GAUGE: *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; break; case OSTC3_APNEA: *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; break; default: return DC_STATUS_DATAFORMAT; } } else { return DC_STATUS_UNSUPPORTED; } break; case DC_FIELD_STRING: switch(flags) { case 0: /* battery */ string->desc = "Battery at end"; snprintf(buf, BUFLEN, "%.2f", array_uint16_le (data + layout->battery) / 1000.0); break; case 1: /* desat */ string->desc = "Desat time"; snprintf(buf, BUFLEN, "%0u:%02u", array_uint16_le (data + layout->desat) / 60, array_uint16_le (data + layout->desat) % 60); break; case 2: /* fw_version */ string->desc = "FW Version"; snprintf(buf, BUFLEN, "%0u.%02u", data[layout->fw_version], data[layout->fw_version + 1]); break; case 3: /* serial */ string->desc = "Serial"; snprintf(buf, BUFLEN, "%u", parser->serial); break; case 4: /* Deco model */ string->desc = "Deco model"; if ((version == 0x23 && data[layout->decomode] == OSTC3_ZHL16) || (version == 0x22 && data[layout->decomode] == FROG_ZHL16) || (version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC || data[layout->decomode] == OSTC_ZHL16_CC))) strncpy(buf, "ZH-L16", BUFLEN); if ((version == 0x23 && data[layout->decomode] == OSTC3_ZHL16_GF) || (version == 0x22 && data[layout->decomode] == FROG_ZHL16_GF) || (version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC_GF || data[layout->decomode] == OSTC_ZHL16_CC_GF))) strncpy(buf, "ZH-L16-GF", BUFLEN); else return DC_STATUS_DATAFORMAT; break; case 5: /* Deco model info */ string->desc = "Deco model info"; if ((version == 0x23 && data[layout->decomode] == OSTC3_ZHL16) || (version == 0x22 && data[layout->decomode] == FROG_ZHL16) || (version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC || data[layout->decomode] == OSTC_ZHL16_CC))) snprintf(buf, BUFLEN, "Saturation %u, Desaturation %u", layout->deco_info1, layout->deco_info2); if ((version == 0x23 && data[layout->decomode] == OSTC3_ZHL16_GF) || (version == 0x22 && data[layout->decomode] == FROG_ZHL16_GF) || (version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC_GF || data[layout->decomode] == OSTC_ZHL16_CC_GF))) snprintf(buf, BUFLEN, "GF %u/%u", data[layout->deco_info1], data[layout->deco_info2]); else return DC_STATUS_DATAFORMAT; break; default: return DC_STATUS_UNSUPPORTED; } string->value = strdup(buf); break; default: return DC_STATUS_UNSUPPORTED; } } return DC_STATUS_SUCCESS; }
static dc_status_t reefnet_sensus_handshake (reefnet_sensus_device_t *device) { dc_device_t *abstract = (dc_device_t *) device; // Send the command to the device. unsigned char command = 0x0A; int n = serial_write (device->port, &command, 1); if (n != 1) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE (n); } // Receive the answer from the device. unsigned char handshake[SZ_HANDSHAKE + 2] = {0}; n = serial_read (device->port, handshake, sizeof (handshake)); if (n != sizeof (handshake)) { ERROR (abstract->context, "Failed to receive the handshake."); return EXITCODE (n); } // Verify the header of the packet. if (handshake[0] != 'O' || handshake[1] != 'K') { ERROR (abstract->context, "Unexpected answer header."); return DC_STATUS_PROTOCOL; } // The device is now waiting for a data request. device->waiting = 1; // Store the clock calibration values. device->systime = dc_datetime_now (); device->devtime = array_uint32_le (handshake + 8); // Store the handshake packet. memcpy (device->handshake, handshake + 2, SZ_HANDSHAKE); // Emit a clock event. dc_event_clock_t clock; clock.systime = device->systime; clock.devtime = device->devtime; device_event_emit (&device->base, DC_EVENT_CLOCK, &clock); // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = handshake[2] - '0'; devinfo.firmware = handshake[3] - '0'; devinfo.serial = array_uint16_le (handshake + 6); device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. dc_event_vendor_t vendor; vendor.data = device->handshake; vendor.size = sizeof (device->handshake); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); // Wait at least 10 ms to ensures the data line is // clear before transmission from the host begins. serial_sleep (device->port, 10); return DC_STATUS_SUCCESS; }
static dc_status_t reefnet_sensus_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { reefnet_sensus_device_t *device = (reefnet_sensus_device_t*) abstract; // Erase the current contents of the buffer and // pre-allocate the required amount of memory. if (!dc_buffer_clear (buffer) || !dc_buffer_reserve (buffer, SZ_MEMORY)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = 4 + SZ_MEMORY + 2 + 3; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Wake-up the device. dc_status_t rc = reefnet_sensus_handshake (device); if (rc != DC_STATUS_SUCCESS) return rc; // Send the command to the device. unsigned char command = 0x40; int n = serial_write (device->port, &command, 1); if (n != 1) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE (n); } // The device leaves the waiting state. device->waiting = 0; // Receive the answer from the device. unsigned int nbytes = 0; unsigned char answer[4 + SZ_MEMORY + 2 + 3] = {0}; while (nbytes < sizeof (answer)) { unsigned int len = sizeof (answer) - nbytes; if (len > 128) len = 128; n = serial_read (device->port, answer + nbytes, len); if (n != len) { ERROR (abstract->context, "Failed to receive the answer."); return EXITCODE (n); } // Update and emit a progress event. progress.current += len; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); nbytes += len; } // Verify the headers of the package. if (memcmp (answer, "DATA", 4) != 0 || memcmp (answer + sizeof (answer) - 3, "END", 3) != 0) { ERROR (abstract->context, "Unexpected answer start or end byte(s)."); return DC_STATUS_PROTOCOL; } // Verify the checksum of the package. unsigned short crc = array_uint16_le (answer + 4 + SZ_MEMORY); unsigned short ccrc = checksum_add_uint16 (answer + 4, SZ_MEMORY, 0x00); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; } dc_buffer_append (buffer, answer + 4, SZ_MEMORY); return DC_STATUS_SUCCESS; }
static dc_status_t atomics_cobalt_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract; // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = SZ_MEMORY + 2; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Emit a vendor event. dc_event_vendor_t vendor; vendor.data = device->version; vendor.size = sizeof (device->version); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = array_uint16_le (device->version + 12); devinfo.firmware = (array_uint16_le (device->version + 8) << 16) + array_uint16_le (device->version + 10); devinfo.serial = 0; for (unsigned int i = 0; i < 8; ++i) { devinfo.serial *= 10; devinfo.serial += device->version[i] - '0'; } device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate a memory buffer. dc_buffer_t *buffer = dc_buffer_new (0); if (buffer == NULL) return DC_STATUS_NOMEMORY; unsigned int ndives = 0; dc_status_t rc = DC_STATUS_SUCCESS; while ((rc = atomics_cobalt_read_dive (abstract, buffer, (ndives == 0), &progress)) == DC_STATUS_SUCCESS) { unsigned char *data = dc_buffer_get_data (buffer); unsigned int size = dc_buffer_get_size (buffer); if (size == 0) { dc_buffer_free (buffer); return DC_STATUS_SUCCESS; } if (memcmp (data + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0) { dc_buffer_free (buffer); return DC_STATUS_SUCCESS; } if (callback && !callback (data, size, data + FP_OFFSET, sizeof (device->fingerprint), userdata)) { dc_buffer_free (buffer); return DC_STATUS_SUCCESS; } // Adjust the maximum value to take into account the two checksum bytes // for the next dive. Since we don't know the total number of dives in // advance, we can't calculate the total number of checksum bytes and // adjust the maximum on the fly. progress.maximum += 2; ndives++; } dc_buffer_free (buffer); return rc; }
static dc_status_t mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { mares_darwin_parser_t *parser = (mares_darwin_parser_t *) abstract; if (abstract->size < parser->headersize) return DC_STATUS_DATAFORMAT; unsigned int time = 0; unsigned int pressure = array_uint16_be (abstract->data + 0x17); unsigned int offset = parser->headersize; while (offset + parser->samplesize <= abstract->size) { dc_sample_value_t sample = {0}; unsigned int value = array_uint16_le (abstract->data + offset); unsigned int depth = value & 0x07FF; unsigned int ascent = (value & 0xE000) >> 13; unsigned int violation = (value & 0x1000) >> 12; unsigned int deco = (value & 0x0800) >> 11; // Surface Time (seconds). time += 20; sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Depth (1/10 m). sample.depth = depth / 10.0; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); // Ascent rate if (ascent) { sample.event.type = SAMPLE_EVENT_ASCENT; sample.event.time = 0; sample.event.flags = 0; sample.event.value = ascent; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Deco violation if (violation) { sample.event.type = SAMPLE_EVENT_CEILING; sample.event.time = 0; sample.event.flags = 0; sample.event.value = 0; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Deco stop if (deco) { sample.event.type = SAMPLE_EVENT_DECOSTOP; sample.event.time = 0; sample.event.flags = 0; sample.event.value = 0; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } if (parser->samplesize == 3) { unsigned int type = (time / 20 + 2) % 3; if (type == 0) { // Tank Pressure (bar) pressure -= abstract->data[offset + 2]; sample.pressure.tank = 0; sample.pressure.value = pressure; if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); } } offset += parser->samplesize; } return DC_STATUS_SUCCESS; }
static dc_status_t mares_nemo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) { mares_nemo_parser_t *parser = (mares_nemo_parser_t *) abstract; // Clear the previous state. parser->base.data = NULL; parser->base.size = 0; parser->mode = 0; parser->length = 0; parser->sample_count = 0; parser->sample_size = 0; parser->header = 0; parser->extra = 0; if (size == 0) return DC_STATUS_SUCCESS; if (size < 2 + 3) return DC_STATUS_DATAFORMAT; unsigned int length = array_uint16_le (data); if (length > size) return DC_STATUS_DATAFORMAT; unsigned int extra = 0; const unsigned char marker[3] = {0xAA, 0xBB, 0xCC}; if (memcmp (data + length - 3, marker, sizeof (marker)) == 0) { if (parser->model == PUCKAIR) extra = 7; else extra = 12; } if (length < 2 + extra + 3) return DC_STATUS_DATAFORMAT; unsigned int mode = data[length - extra - 1]; unsigned int header_size = 53; unsigned int sample_size = 2; if (extra) { if (parser->model == PUCKAIR) sample_size = 3; else sample_size = 5; } if (mode == parser->freedive) { header_size = 28; sample_size = 6; } unsigned int nsamples = array_uint16_le (data + length - extra - 3); unsigned int nbytes = 2 + nsamples * sample_size + header_size + extra; if (length != nbytes) return DC_STATUS_DATAFORMAT; // Store the new state. parser->base.data = data; parser->base.size = size; parser->mode = mode; parser->length = length; parser->sample_count = nsamples; parser->sample_size = sample_size; parser->header = header_size; parser->extra = extra; return DC_STATUS_SUCCESS; }
static dc_status_t mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { mares_nemo_parser_t *parser = (mares_nemo_parser_t *) abstract; if (abstract->size == 0) return DC_STATUS_DATAFORMAT; const unsigned char *data = abstract->data; unsigned int size = abstract->size; if (parser->mode != parser->freedive) { unsigned int time = 0; for (unsigned int i = 0; i < parser->sample_count; ++i) { dc_sample_value_t sample = {0}; unsigned int idx = 2 + parser->sample_size * i; unsigned int value = array_uint16_le (data + idx); unsigned int depth = value & 0x07FF; unsigned int ascent = (value & 0xC000) >> 14; unsigned int violation = (value & 0x2000) >> 13; unsigned int deco = (value & 0x1000) >> 12; // Time (seconds). time += 20; sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Depth (1/10 m). sample.depth = depth / 10.0; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); // Ascent rate if (ascent) { sample.event.type = SAMPLE_EVENT_ASCENT; sample.event.time = 0; sample.event.flags = 0; sample.event.value = ascent; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Deco violation if (violation) { sample.event.type = SAMPLE_EVENT_CEILING; sample.event.time = 0; sample.event.flags = 0; sample.event.value = 0; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Deco stop if (deco) { sample.event.type = SAMPLE_EVENT_DECOSTOP; sample.event.time = 0; sample.event.flags = 0; sample.event.value = 0; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Pressure (1 bar). if (parser->sample_size == 3) { sample.pressure.tank = 0; sample.pressure.value = data[idx + 2]; if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); } } } else {
device_status_t mares_common_extract_dives (mares_common_device_t *device, const mares_common_layout_t *layout, const unsigned char data[], dive_callback_t callback, void *userdata) { assert (layout != NULL); // Get the freedive mode for this model. unsigned int freedive = 2; if (data[1] == 1 || data[1] == 7) freedive = 3; // Get the end of the profile ring buffer. unsigned int eop = array_uint16_le (data + 0x6B); if (eop < layout->rb_profile_begin || eop >= layout->rb_profile_end) { WARNING ("Ringbuffer pointer out of range."); return DEVICE_STATUS_ERROR; } // Make the ringbuffer linear, to avoid having to deal // with the wrap point. The buffer has extra space to // store the profile data for the freedives. unsigned char *buffer = (unsigned char *) malloc ( layout->rb_profile_end - layout->rb_profile_begin + layout->rb_freedives_end - layout->rb_freedives_begin); if (buffer == NULL) { WARNING ("Out of memory."); return DEVICE_STATUS_MEMORY; } memcpy (buffer + 0, data + eop, layout->rb_profile_end - eop); memcpy (buffer + layout->rb_profile_end - eop, data + layout->rb_profile_begin, eop - layout->rb_profile_begin); // For a freedive session, the Mares Nemo stores all the freedives of // that session in a single logbook entry, and each sample is actually // a summary for each individual freedive in the session. The profile // data is stored in a separate memory area. Since only the most recent // recent freediving session can have profile data, we keep track of the // number of freedives. unsigned int nfreedives = 0; unsigned int offset = layout->rb_profile_end - layout->rb_profile_begin; while (offset >= 3) { // Check for the presence of extra header bytes, which can be detected // by means of a three byte marker sequence. unsigned int extra = 0; const unsigned char marker[3] = {0xAA, 0xBB, 0xCC}; if (memcmp (buffer + offset - 3, marker, sizeof (marker)) == 0) { extra = 12; } // Check for overflows due to incomplete dives. if (offset < extra + 3) break; // Check the dive mode of the logbook entry. Valid modes are // 0 (air), 1 (EANx), 2 (freedive) or 3 (bottom timer). // If the ringbuffer has never reached the wrap point before, // there will be "empty" memory (filled with 0xFF) and // processing should stop at this point. unsigned int mode = buffer[offset - extra - 1]; if (mode == 0xFF) break; // The header and sample size are dependant on the dive mode. Only // in freedive mode, the sizes are different from the other modes. unsigned int header_size = 53; unsigned int sample_size = (extra ? 5 : 2); if (mode == freedive) { header_size = 28; sample_size = 6; nfreedives++; } // Get the number of samples in the profile data. unsigned int nsamples = array_uint16_le (buffer + offset - extra - 3); // Calculate the total number of bytes for this dive. // If the buffer does not contain that much bytes, we reached the // end of the ringbuffer. The current dive is incomplete (partially // overwritten with newer data), and processing should stop. unsigned int nbytes = 2 + nsamples * sample_size + header_size + extra; if (offset < nbytes) break; // Move to the start of the dive. offset -= nbytes; // Verify that the length that is stored in the profile data // equals the calculated length. If both values are different, // something is wrong and an error is returned. unsigned int length = array_uint16_le (buffer + offset); if (length != nbytes) { WARNING ("Calculated and stored size are not equal."); free (buffer); return DEVICE_STATUS_ERROR; } // Process the profile data for the most recent freedive entry. // Since we are processing the entries backwards (newest to oldest), // this entry will always be the first one. if (mode == freedive && nfreedives == 1) { // Count the number of freedives in the profile data. unsigned int count = 0; unsigned int idx = layout->rb_freedives_begin; while (idx + 2 <= layout->rb_freedives_end && count != nsamples) { // Each freedive in the session ends with a zero sample. unsigned int sample = array_uint16_le (data + idx); if (sample == 0) count++; // Move to the next sample. idx += 2; } // Verify that the number of freedive entries in the session // equals the number of freedives in the profile data. If // both values are different, the profile data is incomplete. assert (count == nsamples); // Append the profile data to the main logbook entry. The // buffer is guaranteed to have enough space, and the dives // that will be overwritten have already been processed. memcpy (buffer + offset + nbytes, data + layout->rb_freedives_begin, idx - layout->rb_freedives_begin); nbytes += idx - layout->rb_freedives_begin; } unsigned int fp_offset = offset + length - extra - FP_OFFSET; if (device && memcmp (buffer + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) { free (buffer); return DEVICE_STATUS_SUCCESS; } if (callback && !callback (buffer + offset, nbytes, buffer + fp_offset, sizeof (device->fingerprint), userdata)) { free (buffer); return DEVICE_STATUS_SUCCESS; } } free (buffer); return DEVICE_STATUS_SUCCESS; }
dc_status_t cressi_leonardo_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) { cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract; dc_context_t *context = (abstract ? abstract->context : NULL); if (abstract && !ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS; if (size < SZ_MEMORY) return DC_STATUS_DATAFORMAT; // Locate the most recent dive. // The device maintains an internal counter which is incremented for every // dive, and the current value at the time of the dive is stored in the // dive header. Thus the most recent dive will have the highest value. unsigned int count = 0; unsigned int latest = 0; unsigned int maximum = 0; for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { unsigned int offset = RB_LOGBOOK_BEGIN + i * RB_LOGBOOK_SIZE; // Ignore uninitialized header entries. if (array_isequal (data + offset, RB_LOGBOOK_SIZE, 0xFF)) break; // Get the internal dive number. unsigned int current = array_uint16_le (data + offset); if (current > maximum) { maximum = current; latest = i; } count++; } unsigned char *buffer = (unsigned char *) malloc (RB_LOGBOOK_SIZE + RB_PROFILE_END - RB_PROFILE_BEGIN); if (buffer == NULL) { ERROR (context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } for (unsigned int i = 0; i < count; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = RB_LOGBOOK_BEGIN + idx * RB_LOGBOOK_SIZE; // Get the ringbuffer pointers. unsigned int header = array_uint16_le (data + offset + 2); unsigned int footer = array_uint16_le (data + offset + 4); if (header < RB_PROFILE_BEGIN || header + 2 > RB_PROFILE_END || footer < RB_PROFILE_BEGIN || footer + 2 > RB_PROFILE_END) { ERROR (abstract->context, "Invalid ringbuffer pointer detected."); free (buffer); return DC_STATUS_DATAFORMAT; } // Get the same pointers from the profile. unsigned int header2 = array_uint16_le (data + footer); unsigned int footer2 = array_uint16_le (data + header); if (header2 != header || footer2 != footer) { ERROR (abstract->context, "Invalid ringbuffer pointer detected."); free (buffer); return DC_STATUS_DATAFORMAT; } // Calculate the profile address and length. unsigned int address = header + 2; unsigned int length = RB_PROFILE_DISTANCE (header, footer) - 2; // Check the fingerprint data. if (device && memcmp (data + offset + 8, device->fingerprint, sizeof (device->fingerprint)) == 0) break; // Copy the logbook entry. memcpy (buffer, data + offset, RB_LOGBOOK_SIZE); // Copy the profile data. if (address + length > RB_PROFILE_END) { unsigned int len_a = RB_PROFILE_END - address; unsigned int len_b = length - len_a; memcpy (buffer + RB_LOGBOOK_SIZE, data + address, len_a); memcpy (buffer + RB_LOGBOOK_SIZE + len_a, data + RB_PROFILE_BEGIN, len_b); } else { memcpy (buffer + RB_LOGBOOK_SIZE, data + address, length); } if (callback && !callback (buffer, RB_LOGBOOK_SIZE + length, buffer + 8, sizeof (device->fingerprint), userdata)) { break; } } free (buffer); return DC_STATUS_SUCCESS; }
static void cochran_dive_event(struct divecomputer *dc, const unsigned char *s, unsigned int seconds, unsigned int *in_deco, unsigned int *deco_ceiling, unsigned int *deco_time) { switch (s[0]) { case 0xC5: // Deco obligation begins *in_deco = 1; add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "deco stop")); break; case 0xDB: // Deco obligation ends *in_deco = 0; add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "deco stop")); break; case 0xAD: // Raise deco ceiling 10 ft *deco_ceiling -= 10; // ft *deco_time = (array_uint16_le(s + 3) + 1) * 60; break; case 0xAB: // Lower deco ceiling 10 ft *deco_ceiling += 10; // ft *deco_time = (array_uint16_le(s + 3) + 1) * 60; break; case 0xA8: // Entered Post Dive interval mode (surfaced) break; case 0xA9: // Exited PDI mode (re-submierged) break; case 0xBD: // Switched to normal PO2 setting break; case 0xC0: // Switched to FO2 21% mode (generally upon surface) break; case 0xC1: // "Ascent rate alarm add_event(dc, seconds, SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "ascent")); break; case 0xC2: // Low battery warning #ifdef SAMPLE_EVENT_BATTERY add_event(dc, seconds, SAMPLE_EVENT_BATTERY, SAMPLE_FLAGS_NONE, 0, QT_TRANSLATE_NOOP("gettextFromC", "battery")); #endif break; case 0xC3: // CNS warning add_event(dc, seconds, SAMPLE_EVENT_OLF, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "OLF")); break; case 0xC4: // Depth alarm begin add_event(dc, seconds, SAMPLE_EVENT_MAXDEPTH, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "maxdepth")); break; case 0xC8: // PPO2 alarm begin add_event(dc, seconds, SAMPLE_EVENT_PO2, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "pO₂")); break; case 0xCC: // Low cylinder 1 pressure"; break; case 0xCD: // Switch to deco blend setting add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE, SAMPLE_FLAGS_NONE, 0, QT_TRANSLATE_NOOP("gettextFromC", "gaschange")); break; case 0xCE: // NDL alarm begin add_event(dc, seconds, SAMPLE_EVENT_RBT, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "rbt")); break; case 0xD0: // Breathing rate alarm begin break; case 0xD3: // Low gas 1 flow rate alarm begin"; break; case 0xD6: // Ceiling alarm begin add_event(dc, seconds, SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "ceiling")); break; case 0xD8: // End decompression mode *in_deco = 0; add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "deco stop")); break; case 0xE1: // Ascent alarm end add_event(dc, seconds, SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "ascent")); break; case 0xE2: // Low transmitter battery alarm add_event(dc, seconds, SAMPLE_EVENT_TRANSMITTER, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "transmitter")); break; case 0xE3: // Switch to FO2 mode break; case 0xE5: // Switched to PO2 mode break; case 0xE8: // PO2 too low alarm add_event(dc, seconds, SAMPLE_EVENT_PO2, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "pO₂")); break; case 0xEE: // NDL alarm end add_event(dc, seconds, SAMPLE_EVENT_RBT, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "rbt")); break; case 0xEF: // Switch to blend 2 add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE, SAMPLE_FLAGS_NONE, 0, QT_TRANSLATE_NOOP("gettextFromC", "gaschange")); break; case 0xF0: // Breathing rate alarm end break; case 0xF3: // Switch to blend 1 (often at dive start) add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE, SAMPLE_FLAGS_NONE, 0, QT_TRANSLATE_NOOP("gettextFromC", "gaschange")); break; case 0xF6: // Ceiling alarm end add_event(dc, seconds, SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "ceiling")); break; default: break; } }
device_status_t suunto_common2_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) { suunto_common2_device_t *device = (suunto_common2_device_t*) abstract; // Enable progress notifications. device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; progress.maximum = RB_PROFILE_END - RB_PROFILE_BEGIN + 8 + SZ_VERSION + (SZ_MINIMUM > 4 ? SZ_MINIMUM : 4); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Read the version info. unsigned char version[SZ_VERSION] = {0}; device_status_t rc = suunto_common2_device_version (abstract, version, sizeof (version)); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory header."); return rc; } // Update and emit a progress event. progress.current += sizeof (version); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Read the serial number. unsigned char serial[SZ_MINIMUM > 4 ? SZ_MINIMUM : 4] = {0}; rc = suunto_common2_device_read (abstract, 0x0023, serial, sizeof (serial)); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory header."); return rc; } // Update and emit a progress event. progress.current += sizeof (serial); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Emit a device info event. device_devinfo_t devinfo; devinfo.model = version[0]; devinfo.firmware = array_uint24_be (version + 1); devinfo.serial = array_uint32_be (serial); device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo); // Read the header bytes. unsigned char header[8] = {0}; rc = suunto_common2_device_read (abstract, 0x0190, header, sizeof (header)); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory header."); return rc; } // Obtain the pointers from the header. unsigned int last = array_uint16_le (header + 0); unsigned int count = array_uint16_le (header + 2); unsigned int end = array_uint16_le (header + 4); unsigned int begin = array_uint16_le (header + 6); // Memory buffer to store all the dives. unsigned char data[SZ_MINIMUM + RB_PROFILE_END - RB_PROFILE_BEGIN] = {0}; // Calculate the total amount of bytes. unsigned int remaining = RB_PROFILE_DISTANCE (begin, end, count != 0); // Update and emit a progress event. progress.maximum -= (RB_PROFILE_END - RB_PROFILE_BEGIN) - remaining; progress.current += sizeof (header); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // To reduce the number of read operations, we always try to read // packages with the largest possible size. As a consequence, the // last package of a dive can contain data from more than one dive. // Therefore, the remaining data of this package (and its size) // needs to be preserved for the next dive. unsigned int available = 0; // The ring buffer is traversed backwards to retrieve the most recent // dives first. This allows us to download only the new dives. unsigned int current = last; unsigned int previous = end; unsigned int address = previous; unsigned int offset = remaining + SZ_MINIMUM; while (remaining) { // Calculate the size of the current dive. unsigned int size = RB_PROFILE_DISTANCE (current, previous, 1); if (size < 4 || size > remaining) { WARNING ("Unexpected profile size."); return DEVICE_STATUS_ERROR; } unsigned int nbytes = available; while (nbytes < size) { // Handle the ringbuffer wrap point. if (address == RB_PROFILE_BEGIN) address = RB_PROFILE_END; // Calculate the package size. Try with the largest possible // size first, and adjust when the end of the ringbuffer or // the end of the profile data is reached. unsigned int len = SZ_PACKET; if (RB_PROFILE_BEGIN + len > address) len = address - RB_PROFILE_BEGIN; // End of ringbuffer. if (nbytes + len > remaining) len = remaining - nbytes; // End of profile. /*if (nbytes + len > size) len = size - nbytes;*/ // End of dive (for testing only). // Move to the begin of the current package. offset -= len; address -= len; // Always read at least the minimum amount of bytes, because // reading fewer bytes is unreliable. The memory buffer is // large enough to prevent buffer overflows, and the extra // bytes are automatically ignored (due to reading backwards). unsigned int extra = 0; if (len < SZ_MINIMUM) extra = SZ_MINIMUM - len; // Read the package. rc = suunto_common2_device_read (abstract, address - extra, data + offset - extra, len + extra); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory."); return rc; } // Update and emit a progress event. progress.current += len; device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Next package. nbytes += len; } // The last package of the current dive contains the previous and // next pointers (in a continuous memory area). It can also contain // a number of bytes from the next dive. remaining -= size; available = nbytes - size; unsigned char *p = data + offset + available; unsigned int prev = array_uint16_le (p + 0); unsigned int next = array_uint16_le (p + 2); if (next != previous) { WARNING ("Profiles are not continuous."); return DEVICE_STATUS_ERROR; } // Next dive. previous = current; current = prev; unsigned int fp_offset = FP_OFFSET; if (devinfo.model == 0x15) fp_offset += 6; // HelO2 if (memcmp (p + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) return DEVICE_STATUS_SUCCESS; if (callback && !callback (p + 4, size - 4, p + fp_offset, sizeof (device->fingerprint), userdata)) return DEVICE_STATUS_SUCCESS; } return DEVICE_STATUS_SUCCESS; }
static dc_status_t atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, dc_event_progress_t *progress) { #ifdef HAVE_LIBUSB atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract; if (device_is_cancelled (abstract)) return DC_STATUS_CANCELLED; // Erase the current contents of the buffer. if (!dc_buffer_clear (buffer)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } // Send the command to the dive computer. uint8_t bRequest = 0; if (device->simulation) bRequest = init ? 0x02 : 0x03; else bRequest = init ? 0x09 : 0x0A; int rc = libusb_control_transfer (device->handle, LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, bRequest, 0, 0, NULL, 0, TIMEOUT); if (rc != LIBUSB_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE(rc); } HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1); unsigned int nbytes = 0; while (1) { // Receive the answer from the dive computer. int length = 0; unsigned char packet[8 * 1024] = {0}; rc = libusb_bulk_transfer (device->handle, 0x82, packet, sizeof (packet), &length, TIMEOUT); if (rc != LIBUSB_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return EXITCODE(rc); } HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length); // Update and emit a progress event. if (progress) { progress->current += length; device_event_emit (abstract, DC_EVENT_PROGRESS, progress); } // Append the packet to the output buffer. dc_buffer_append (buffer, packet, length); nbytes += length; // If we received fewer bytes than requested, the transfer is finished. if (length < sizeof (packet)) break; } // Check for a buffer error. if (dc_buffer_get_size (buffer) != nbytes) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } // Check for the minimum length. if (nbytes < 2) { ERROR (abstract->context, "Data packet is too short."); return DC_STATUS_PROTOCOL; } // When only two 0xFF bytes are received, there are no more dives. unsigned char *data = dc_buffer_get_data (buffer); if (nbytes == 2 && data[0] == 0xFF && data[1] == 0xFF) { dc_buffer_clear (buffer); return DC_STATUS_SUCCESS; } // Verify the checksum of the packet. unsigned short crc = array_uint16_le (data + nbytes - 2); unsigned short ccrc = checksum_add_uint16 (data, nbytes - 2, 0x0); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; } // Remove the checksum bytes. dc_buffer_slice (buffer, 0, nbytes - 2); return DC_STATUS_SUCCESS; #else return DC_STATUS_UNSUPPORTED; #endif }