dc_status_t uwatec_meridian_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) { if (abstract && !ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS; const unsigned char header[4] = {0xa5, 0xa5, 0x5a, 0x5a}; // Search the data stream for start markers. unsigned int previous = size; unsigned int current = (size >= 4 ? size - 4 : 0); while (current > 0) { current--; if (memcmp (data + current, header, sizeof (header)) == 0) { // Get the length of the profile data. unsigned int len = array_uint32_le (data + current + 4); // Check for a buffer overflow. if (current + len > previous) return DC_STATUS_DATAFORMAT; if (callback && !callback (data + current, len, data + current + 8, 4, userdata)) return DC_STATUS_SUCCESS; // Prepare for the next dive. previous = current; current = (current >= 4 ? current - 4 : 0); } } return DC_STATUS_SUCCESS; }
static dc_status_t shearwater_predator_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { dc_buffer_t *buffer = dc_buffer_new (SZ_MEMORY); if (buffer == NULL) return DC_STATUS_NOMEMORY; dc_status_t rc = shearwater_predator_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.model = data[0x2000D]; devinfo.firmware = data[0x2000A]; devinfo.serial = array_uint32_le (data + 0x20002); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); rc = shearwater_predator_extract_dives (abstract, data, SZ_MEMORY, callback, userdata); dc_buffer_free (buffer); return rc; }
static dc_status_t uwatec_meridian_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) { uwatec_meridian_device_t *device = (uwatec_meridian_device_t*) abstract; if (size && size != 4) return DC_STATUS_INVALIDARGS; if (size) device->timestamp = array_uint32_le (data); else device->timestamp = 0; return DC_STATUS_SUCCESS; }
static dc_status_t reefnet_sensus_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) { reefnet_sensus_parser_t *parser = (reefnet_sensus_parser_t *) abstract; if (abstract->size < 2 + 4) return DC_STATUS_DATAFORMAT; unsigned int timestamp = array_uint32_le (abstract->data + 2); dc_ticks_t ticks = parser->systime - (parser->devtime - timestamp); if (!dc_datetime_localtime (datetime, ticks)) return DC_STATUS_DATAFORMAT; return DC_STATUS_SUCCESS; }
static dc_status_t uwatec_meridian_transfer (uwatec_meridian_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) { dc_device_t *abstract = (dc_device_t *) device; assert (csize > 0 && csize <= 255); // Build the packet. unsigned char packet[255 + 12] = { 0xFF, 0xFF, 0xFF, 0xA6, 0x59, 0xBD, 0xC2, 0x00, /* length */ 0x00, 0x00, 0x00, 0x00}; /* data and checksum */ memcpy (packet + 11, command, csize); packet[7] = csize; packet[11 + csize] = checksum_xor_uint8 (packet + 7, csize + 4, 0x00); // Send the packet. int n = serial_write (device->port, packet, csize + 12); if (n != csize + 12) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE (n); } // Read the echo. unsigned char echo[sizeof(packet)]; n = serial_read (device->port, echo, csize + 12); if (n != csize + 12) { ERROR (abstract->context, "Failed to receive the echo."); return EXITCODE (n); } // Verify the echo. if (memcmp (echo, packet, csize + 12) != 0) { WARNING (abstract->context, "Unexpected echo."); return DC_STATUS_PROTOCOL; } // Read the header. unsigned char header[6]; n = serial_read (device->port, header, sizeof (header)); if (n != sizeof (header)) { ERROR (abstract->context, "Failed to receive the header."); return EXITCODE (n); } // Verify the header. if (header[0] != ACK || array_uint32_le (header + 1) != asize + 1 || header[5] != packet[11]) { WARNING (abstract->context, "Unexpected header."); return DC_STATUS_PROTOCOL; } // Read the packet. n = serial_read (device->port, answer, asize); if (n != asize) { ERROR (abstract->context, "Failed to receive the packet."); return EXITCODE (n); } // Read the checksum. unsigned char csum = 0x00; n = serial_read (device->port, &csum, sizeof (csum)); if (n != sizeof (csum)) { ERROR (abstract->context, "Failed to receive the checksum."); return EXITCODE (n); } // Verify the checksum. unsigned char ccsum = 0x00; ccsum = checksum_xor_uint8 (header + 1, sizeof (header) - 1, ccsum); ccsum = checksum_xor_uint8 (answer, asize, ccsum); if (csum != ccsum) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; } return DC_STATUS_SUCCESS; }
static dc_status_t uwatec_meridian_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { uwatec_meridian_device_t *device = (uwatec_meridian_device_t*) abstract; dc_status_t rc = DC_STATUS_SUCCESS; // Erase the current contents of the buffer. if (!dc_buffer_clear (buffer)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); // Command template. unsigned char command[9] = {0x00, (device->timestamp ) & 0xFF, (device->timestamp >> 8 ) & 0xFF, (device->timestamp >> 16) & 0xFF, (device->timestamp >> 24) & 0xFF, 0x10, 0x27, 0, 0}; // Read the model number. command[0] = 0x10; unsigned char model[1] = {0}; rc = uwatec_meridian_transfer (device, command, 1, model, sizeof (model)); if (rc != DC_STATUS_SUCCESS) return rc; // Read the serial number. command[0] = 0x14; unsigned char serial[4] = {0}; rc = uwatec_meridian_transfer (device, command, 1, serial, sizeof (serial)); if (rc != DC_STATUS_SUCCESS) return rc; // Read the device clock. command[0] = 0x1A; unsigned char devtime[4] = {0}; rc = uwatec_meridian_transfer (device, command, 1, devtime, sizeof (devtime)); if (rc != DC_STATUS_SUCCESS) return rc; // Store the clock calibration values. device->systime = dc_datetime_now (); device->devtime = array_uint32_le (devtime); // Update and emit a progress event. progress.current += 9; device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); // 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 = model[0]; devinfo.firmware = 0; devinfo.serial = array_uint32_le (serial); device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); // Data Length. command[0] = 0xC6; unsigned char answer[4] = {0}; rc = uwatec_meridian_transfer (device, command, sizeof (command), answer, sizeof (answer)); if (rc != DC_STATUS_SUCCESS) return rc; unsigned int length = array_uint32_le (answer); // Update and emit a progress event. progress.maximum = 4 + 9 + (length ? length + 4 : 0); progress.current += 4; device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); if (length == 0) return DC_STATUS_SUCCESS; // Allocate the required amount of memory. if (!dc_buffer_resize (buffer, length)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } unsigned char *data = dc_buffer_get_data (buffer); // Data. command[0] = 0xC4; rc = uwatec_meridian_transfer (device, command, sizeof (command), answer, sizeof (answer)); if (rc != DC_STATUS_SUCCESS) return rc; unsigned int total = array_uint32_le (answer); // Update and emit a progress event. progress.current += 4; device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); if (total != length + 4) { ERROR (abstract->context, "Received an unexpected size."); return DC_STATUS_PROTOCOL; } unsigned int nbytes = 0; while (nbytes < length) { // Read the header. unsigned char header[5]; int n = serial_read (device->port, header, sizeof (header)); if (n != sizeof (header)) { ERROR (abstract->context, "Failed to receive the header."); return EXITCODE (n); } // Get the packet size. unsigned int packetsize = array_uint32_le (header); if (packetsize < 1 || nbytes + packetsize - 1 > length) { WARNING (abstract->context, "Unexpected header."); return DC_STATUS_PROTOCOL; } // Read the packet data. n = serial_read (device->port, data + nbytes, packetsize - 1); if (n != packetsize - 1) { ERROR (abstract->context, "Failed to receive the packet."); return EXITCODE (n); } // Read the checksum. unsigned char csum = 0x00; n = serial_read (device->port, &csum, sizeof (csum)); if (n != sizeof (csum)) { ERROR (abstract->context, "Failed to receive the checksum."); return EXITCODE (n); } // Verify the checksum. unsigned char ccsum = 0x00; ccsum = checksum_xor_uint8 (header, sizeof (header), ccsum); ccsum = checksum_xor_uint8 (data + nbytes, packetsize - 1, ccsum); if (csum != ccsum) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; } // Update and emit a progress event. progress.current += packetsize - 1; device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); nbytes += packetsize - 1; } return DC_STATUS_SUCCESS; }
dc_status_t reefnet_sensus_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) { reefnet_sensus_device_t *device = (reefnet_sensus_device_t*) abstract; dc_context_t *context = (abstract ? abstract->context : NULL); if (abstract && !ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS; // Search the entire data stream for start markers. unsigned int previous = size; unsigned int current = (size >= 7 ? size - 7 : 0); while (current > 0) { current--; if (data[current] == 0xFF && data[current + 6] == 0xFE) { // Once a start marker is found, start searching // for the end of the dive. The search is now // limited to the start of the previous dive. int found = 0; unsigned int nsamples = 0, count = 0; unsigned int offset = current + 7; // Skip non-sample data. while (offset + 1 <= previous) { // Depth (adjusted feet of seawater). unsigned char depth = data[offset++]; // Temperature (degrees Fahrenheit) if ((nsamples % 6) == 0) { if (offset + 1 > previous) break; offset++; } // Current sample is complete. nsamples++; // The end of a dive is reached when 17 consecutive // depth samples of less than 3 feet have been found. if (depth < 13 + 3) { count++; if (count == 17) { found = 1; break; } } else { count = 0; } } // Report an error if no end of dive was found. if (!found) { ERROR (context, "No end of dive found."); return DC_STATUS_DATAFORMAT; } // Automatically abort when a dive is older than the provided timestamp. unsigned int timestamp = array_uint32_le (data + current + 2); if (device && timestamp <= device->timestamp) return DC_STATUS_SUCCESS; if (callback && !callback (data + current, offset - current, data + current + 2, 4, userdata)) return DC_STATUS_SUCCESS; // Prepare for the next dive. previous = current; current = (current >= 7 ? current - 7 : 0); } } 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 void cochran_parse_dive(const unsigned char *decode, unsigned mod, const unsigned char *in, unsigned size, struct dive_table *table) { unsigned char *buf = malloc(size); struct dive *dive; struct divecomputer *dc; struct tm tm = {0}; uint32_t csum[5]; double max_depth, avg_depth, min_temp; unsigned int duration = 0, corrupt_dive = 0; /* * The scrambling has odd boundaries. I think the boundaries * match some data structure size, but I don't know. They were * discovered the same way we dynamically discover the decode * size: automatically looking for least random output. * * The boundaries are also this confused "off-by-one" thing, * the same way the file size is off by one. It's as if the * cochran software forgot to write one byte at the beginning. */ partial_decode(0, 0x0fff, decode, 1, mod, in, size, buf); partial_decode(0x0fff, 0x1fff, decode, 0, mod, in, size, buf); partial_decode(0x1fff, 0x2fff, decode, 0, mod, in, size, buf); partial_decode(0x2fff, 0x48ff, decode, 0, mod, in, size, buf); /* * This is not all the descrambling you need - the above are just * what appears to be the fixed-size blocks. The rest is also * scrambled, but there seems to be size differences in the data, * so this just descrambles part of it: */ if (size < 0x4914 + config.logbook_size) { // Analyst calls this a "Corrupt Beginning Summary" free(buf); return; } // Decode log entry (512 bytes + random prefix) partial_decode(0x48ff, 0x4914 + config.logbook_size, decode, 0, mod, in, size, buf); unsigned int sample_size = size - 0x4914 - config.logbook_size; int g; unsigned int sample_pre_offset = 0, sample_end_offset = 0; // Decode sample data partial_decode(0x4914 + config.logbook_size, size, decode, 0, mod, in, size, buf); #ifdef COCHRAN_DEBUG // Display pre-logbook data puts("\nPre Logbook Data\n"); cochran_debug_write(buf, 0x4914); // Display log book puts("\nLogbook Data\n"); cochran_debug_write(buf + 0x4914, config.logbook_size + 0x400); // Display sample data puts("\nSample Data\n"); #endif dive = alloc_dive(); dc = &dive->dc; unsigned char *log = (buf + 0x4914); switch (config.type) { case TYPE_GEMINI: case TYPE_COMMANDER: if (config.type == TYPE_GEMINI) { dc->model = "Gemini"; dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no fill_default_cylinder(&dive->cylinder[0]); dive->cylinder[0].gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256 + log[CMD_O2_PERCENT + 1]) * 10; dive->cylinder[0].gasmix.he.permille = 0; } else { dc->model = "Commander"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 2; g++) { fill_default_cylinder(&dive->cylinder[g]); dive->cylinder[g].gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 + log[CMD_O2_PERCENT + g * 2 + 1]) * 10; dive->cylinder[g].gasmix.he.permille = 0; } } tm.tm_year = log[CMD_YEAR]; tm.tm_mon = log[CMD_MON] - 1; tm.tm_mday = log[CMD_DAY]; tm.tm_hour = log[CMD_HOUR]; tm.tm_min = log[CMD_MIN]; tm.tm_sec = log[CMD_SEC]; tm.tm_isdst = -1; dive->when = dc->when = utc_mktime(&tm); dive->number = log[CMD_NUMBER] + log[CMD_NUMBER + 1] * 256 + 1; dc->duration.seconds = (log[CMD_BT] + log[CMD_BT + 1] * 256) * 60; dc->surfacetime.seconds = (log[CMD_SIT] + log[CMD_SIT + 1] * 256) * 60; dc->maxdepth.mm = lrint((log[CMD_MAX_DEPTH] + log[CMD_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->meandepth.mm = lrint((log[CMD_AVG_DEPTH] + log[CMD_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->watertemp.mkelvin = C_to_mkelvin((log[CMD_MIN_TEMP] / 32) - 1.8); dc->surface_pressure.mbar = lrint(ATM / BAR * pow(1 - 0.0000225577 * (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000); dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY]; SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum); dc->diveid = csum[0]; if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff) corrupt_dive = 1; sample_pre_offset = array_uint32_le(log + CMD_PREDIVE_OFFSET); sample_end_offset = array_uint32_le(log + CMD_END_OFFSET); break; case TYPE_EMC: dc->model = "EMC"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 4; g++) { fill_default_cylinder(&dive->cylinder[g]); dive->cylinder[g].gasmix.o2.permille = (log[EMC_O2_PERCENT + g * 2] / 256 + log[EMC_O2_PERCENT + g * 2 + 1]) * 10; dive->cylinder[g].gasmix.he.permille = (log[EMC_HE_PERCENT + g * 2] / 256 + log[EMC_HE_PERCENT + g * 2 + 1]) * 10; } tm.tm_year = log[EMC_YEAR]; tm.tm_mon = log[EMC_MON] - 1; tm.tm_mday = log[EMC_DAY]; tm.tm_hour = log[EMC_HOUR]; tm.tm_min = log[EMC_MIN]; tm.tm_sec = log[EMC_SEC]; tm.tm_isdst = -1; dive->when = dc->when = utc_mktime(&tm); dive->number = log[EMC_NUMBER] + log[EMC_NUMBER + 1] * 256 + 1; dc->duration.seconds = (log[EMC_BT] + log[EMC_BT + 1] * 256) * 60; dc->surfacetime.seconds = (log[EMC_SIT] + log[EMC_SIT + 1] * 256) * 60; dc->maxdepth.mm = lrint((log[EMC_MAX_DEPTH] + log[EMC_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->meandepth.mm = lrint((log[EMC_AVG_DEPTH] + log[EMC_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->watertemp.mkelvin = C_to_mkelvin((log[EMC_MIN_TEMP] - 32) / 1.8); dc->surface_pressure.mbar = lrint(ATM / BAR * pow(1 - 0.0000225577 * (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000); dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3); SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum); dc->diveid = csum[0]; if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff) corrupt_dive = 1; sample_pre_offset = array_uint32_le(log + EMC_PREDIVE_OFFSET); sample_end_offset = array_uint32_le(log + EMC_END_OFFSET); break; } // Use the log information to determine actual profile sample size // Otherwise we will get surface time at end of dive. if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff) sample_size = sample_end_offset - sample_pre_offset; cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914 + config.logbook_size, sample_size, &duration, &max_depth, &avg_depth, &min_temp); // Check for corrupt dive if (corrupt_dive) { dc->maxdepth.mm = lrint(max_depth * FEET * 1000); dc->meandepth.mm = lrint(avg_depth * FEET * 1000); dc->watertemp.mkelvin = C_to_mkelvin((min_temp - 32) / 1.8); dc->duration.seconds = duration; } record_dive_to_table(dive, table); mark_divelist_changed(true); free(buf); }