static dc_status_t
shearwater_predator_extract_petrel (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata)
{
	shearwater_predator_device_t *device = (shearwater_predator_device_t*) abstract;
	dc_context_t *context = (abstract ? abstract->context : NULL);

	// Allocate memory for the profiles.
	unsigned char *buffer = (unsigned char *) malloc (RB_PROFILE_END - RB_PROFILE_BEGIN + SZ_BLOCK);
	if (buffer == NULL) {
		return DC_STATUS_NOMEMORY;
	}

	// Search the ringbuffer to locate matching header and footer
	// markers. Because the Petrel does reorder the internal ringbuffer
	// before sending the data, the most recent dive is always the first
	// one. Therefore, there is no need to search for it, as we have to
	// do for the Predator.
	unsigned int header = 0;
	unsigned int have_header = 0;
	unsigned int offset = RB_PROFILE_BEGIN;
	while (offset != RB_PROFILE_END) {
		if (array_isequal (data + offset, SZ_BLOCK, 0xFF)) {
			// Ignore empty blocks explicitly, because otherwise they are
			// incorrectly recognized as header markers.
			break;
		} else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFF) {
			// Remember the header marker.
			header = offset;
			have_header = 1;
		} else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFE && have_header) {
			// The dive number in the header and footer should be identical.
			if (memcmp (data + header + 2, data + offset + 2, 2) != 0) {
				ERROR (context, "Unexpected dive number.");
				free (buffer);
				return DC_STATUS_DATAFORMAT;
			}

			// Append the final block.
			unsigned int length = offset + SZ_BLOCK - header;
			memcpy (buffer, data + header, length);
			memcpy (buffer + length, data + SZ_MEMORY - SZ_BLOCK, SZ_BLOCK);

			// Check the fingerprint data.
			if (device && memcmp (buffer + 12, device->fingerprint, sizeof (device->fingerprint)) == 0)
				break;

			if (callback && !callback (buffer, length + SZ_BLOCK, buffer + 12, sizeof (device->fingerprint), userdata))
				break;

			// Reset the header marker.
			have_header = 0;
		}

		offset += SZ_BLOCK;
	}

	free (buffer);

	return DC_STATUS_SUCCESS;
}
static dc_status_t
shearwater_predator_extract_predator (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata)
{
	shearwater_predator_device_t *device = (shearwater_predator_device_t*) abstract;
	dc_context_t *context = (abstract ? abstract->context : NULL);

	// 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 maximum = 0;
	unsigned int eop = RB_PROFILE_END;

	// Search the ringbuffer backwards to locate matching header and
	// footer markers. Because the ringbuffer search algorithm starts at
	// some arbitrary position, which does not necessary corresponds
	// with a boundary between two dives, the begin position is adjusted
	// as soon as the first dive has been found. Without this step,
	// dives crossing the ringbuffer wrap point won't be detected when
	// searching backwards from the ringbuffer end offset.
	unsigned int footer = 0;
	unsigned int have_footer = 0;
	unsigned int begin = RB_PROFILE_BEGIN;
	unsigned int offset = RB_PROFILE_END;
	while (offset != begin) {
		// Handle the ringbuffer wrap point.
		if (offset == RB_PROFILE_BEGIN)
			offset = RB_PROFILE_END;

		// Move to the start of the block.
		offset -= SZ_BLOCK;

		if (array_isequal (data + offset, SZ_BLOCK, 0xFF)) {
			// Ignore empty blocks explicitly, because otherwise they are
			// incorrectly recognized as header markers.
		} else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFF && have_footer) {
			// If the first header marker is found, the begin offset is moved
			// after the corresponding footer marker. This is necessary to be
			// able to detect dives that cross the ringbuffer wrap point.
			if (begin == RB_PROFILE_BEGIN)
				begin = footer + SZ_BLOCK;

			// Get the internal dive number.
			unsigned int current = array_uint16_be (data + offset + 2);
			if (current > maximum) {
				maximum = current;
				eop = footer + SZ_BLOCK;
			}

			// The dive number in the header and footer should be identical.
			if (current != array_uint16_be (data + footer + 2)) {
				ERROR (context, "Unexpected dive number.");
				return DC_STATUS_DATAFORMAT;
			}

			// Reset the footer marker.
			have_footer = 0;
		} else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFE) {
			// Remember the footer marker.
			footer = offset;
			have_footer = 1;
		}
	}

	// Allocate memory for the profiles.
	unsigned char *buffer = (unsigned char *) malloc (RB_PROFILE_END - RB_PROFILE_BEGIN + SZ_BLOCK);
	if (buffer == NULL) {
		return DC_STATUS_NOMEMORY;
	}

	// Linearize the ringbuffer.
	memcpy (buffer + 0, data + eop, RB_PROFILE_END - eop);
	memcpy (buffer + RB_PROFILE_END - eop, data + RB_PROFILE_BEGIN, eop - RB_PROFILE_BEGIN);

	// Find the dives again in the linear buffer.
	footer = 0;
	have_footer = 0;
	offset = RB_PROFILE_END;
	while (offset != RB_PROFILE_BEGIN) {
		// Move to the start of the block.
		offset -= SZ_BLOCK;

		if (array_isequal (buffer + offset, SZ_BLOCK, 0xFF)) {
			break;
		} else if (buffer[offset + 0] == 0xFF && buffer[offset + 1] == 0xFF && have_footer) {
			// Append the final block.
			unsigned int length = footer + SZ_BLOCK - offset;
			memcpy (buffer + offset + length, data + SZ_MEMORY - SZ_BLOCK, SZ_BLOCK);

			// Check the fingerprint data.
			if (device && memcmp (buffer + offset + 12, device->fingerprint, sizeof (device->fingerprint)) == 0)
				break;

			if (callback && !callback (buffer + offset, length + SZ_BLOCK, buffer + offset + 12, sizeof (device->fingerprint), userdata))
				break;

			have_footer = 0;
		} else if (buffer[offset + 0] == 0xFF && buffer[offset + 1] == 0xFE) {
			footer = offset;
			have_footer = 1;
		}
	}

	free (buffer);

	return DC_STATUS_SUCCESS;
}
Пример #3
0
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
shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
	shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;

	const unsigned char *data = abstract->data;
	unsigned int size = abstract->size;

	if (size < 2 * SZ_BLOCK)
		return DC_STATUS_DATAFORMAT;

	// Get the offset to the footer record.
	unsigned int footer = size - SZ_BLOCK;
	if (parser->petrel || array_uint16_be (data + footer) == 0xFFFD) {
		if (size < 3 * SZ_BLOCK)
			return DC_STATUS_DATAFORMAT;

		footer -= SZ_BLOCK;
	}

	// Get the sample size.
	unsigned int samplesize = SZ_SAMPLE_PREDATOR;
	if (parser->petrel) {
		samplesize = SZ_SAMPLE_PETREL;
	}

	// Get the unit system.
	unsigned int units = data[8];

	// Previous gas mix.
	unsigned int o2_previous = 0, he_previous = 0;

	unsigned int time = 0;
	unsigned int offset = SZ_BLOCK;
	while (offset < footer) {
		dc_sample_value_t sample = {0};

		// Ignore empty samples.
		if (array_isequal (data + offset, samplesize, 0x00)) {
			offset += samplesize;
			continue;
		}

		// Time (seconds).
		time += 10;
		sample.time = time;
		if (callback) callback (DC_SAMPLE_TIME, sample, userdata);

		// Depth (1/10 m or ft).
		unsigned int depth = array_uint16_be (data + offset);
		if (units == IMPERIAL)
			sample.depth = depth * FEET / 10.0;
		else
			sample.depth = depth / 10.0;
		if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);

		// Temperature (°C or °F).
		unsigned int temperature = data[offset + 13];
		if (units == IMPERIAL)
			sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
		else
			sample.temperature = temperature;
		if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);

		// PPO2
		sample.ppo2 = data[offset + 6] / 100.0;
		if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);

		// Gaschange.
		unsigned int o2 = data[offset + 7];
		unsigned int he = data[offset + 8];
		if (o2 != o2_previous || he != he_previous) {
			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);
			o2_previous = o2;
			he_previous = he;
		}

		// Deco stop / NDL.
		unsigned int decostop = array_uint16_be (data + offset + 2);
		if (decostop) {
			sample.deco.type = DC_DECO_DECOSTOP;
			if (units == IMPERIAL)
				sample.deco.depth = decostop * FEET;
			else
				sample.deco.depth = decostop;
		} else {
			sample.deco.type = DC_DECO_NDL;
			sample.deco.depth = 0.0;
		}
		sample.deco.time = data[offset + 9] * 60;
		if (callback) callback (DC_SAMPLE_DECO, sample, userdata);

		offset += samplesize;
	}

	return DC_STATUS_SUCCESS;
}
dc_status_t
diverite_nitekq_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata)
{
	diverite_nitekq_device_t *device = (diverite_nitekq_device_t *) abstract;
	dc_context_t *context = (abstract ? abstract->context : NULL);

	if (abstract && !ISINSTANCE (abstract))
		return DC_STATUS_INVALIDARGS;

	if (size < SZ_PACKET + SZ_MEMORY)
		return DC_STATUS_DATAFORMAT;

	// Skip the first packet. We don't need it for anything. It also
	// makes the logic easier because all offsets in the data are
	// relative to the real start of the memory (e.g. excluding this
	// artificial first block).
	data += SZ_PACKET;

	// Allocate memory.
	unsigned char *buffer = (unsigned char *) malloc (RB_PROFILE_END - RB_PROFILE_BEGIN);
	if (buffer == NULL) {
		ERROR (context, "Failed to allocate memory.");
		return DC_STATUS_NOMEMORY;
	}

	// Get the end of profile pointer.
	unsigned int eop = array_uint16_be(data + EOP);
	if (eop < RB_PROFILE_BEGIN || eop >= RB_PROFILE_END) {
		ERROR (context, "Invalid ringbuffer pointer detected.");
		free (buffer);
		return DC_STATUS_DATAFORMAT;
	}

	// When a new dive is added, the device moves all existing logbook
	// and address entries towards the end, such that the most recent
	// one is always the first one. This is not the case for the profile
	// data, which is added at the end.
	unsigned int previous = eop;
	for (unsigned int i = 0; i < 10; ++i) {
		// Get the pointer to the logbook entry.
		const unsigned char *p = data + LOGBOOK + i * SZ_LOGBOOK;

		// Abort if an empty logbook is found.
		if (array_isequal (p, SZ_LOGBOOK, 0x00))
			break;

		// Get the address of the profile data.
		unsigned int address = array_uint16_be(data + ADDRESS + i * 2);
		if (address < RB_PROFILE_BEGIN || address >= RB_PROFILE_END) {
			ERROR (context, "Invalid ringbuffer pointer detected.");
			free (buffer);
			return DC_STATUS_DATAFORMAT;
		}

		// Check the fingerprint data.
		if (device && memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0)
			break;

		// Copy the logbook entry.
		memcpy (buffer, p, SZ_LOGBOOK);

		// Copy the profile data.
		unsigned int length = 0;
		if (previous > address) {
			length = previous - address;
			memcpy (buffer + SZ_LOGBOOK, data + address, length);
		} else {
			unsigned int len_a = RB_PROFILE_END - address;
			unsigned int len_b = previous - RB_PROFILE_BEGIN;
			length = len_a + len_b;
			memcpy (buffer + SZ_LOGBOOK, data + address, len_a);
			memcpy (buffer + SZ_LOGBOOK + len_a, data + RB_PROFILE_BEGIN, len_b);
		}

		if (callback && !callback (buffer, length + SZ_LOGBOOK, buffer, SZ_LOGBOOK, userdata)) {
			break;
		}

		previous = address;
	}

	free (buffer);

	return DC_STATUS_SUCCESS;
}
Пример #6
0
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;
}