Example #1
0
 void
 finish_sample(sep_sample_t &sample, const vec_mutation_t &fixations,
               const unsigned nsam, const bool removeFixed,
               const sugar::treat_neutral)
 {
     finish_sample(sample.first, fixations, nsam, removeFixed,
                   sugar::treat_neutral::NEUTRAL);
     finish_sample(sample.second, fixations, nsam, removeFixed,
                   sugar::treat_neutral::SELECTED);
 }
Example #2
0
static void sample_parser(char *line, struct divecomputer *dc)
{
	int m, s = 0;
	struct sample *sample = new_sample(dc);

	m = strtol(line, &line, 10);
	if (*line == ':')
		s = strtol(line+1, &line, 10);
	sample->time.seconds = m*60+s;

	for (;;) {
		char c;

		while (isspace(c = *line))
			line++;
		if (!c)
			break;
		/* Less common sample entries have a name */
		if (c >= 'a' && c <= 'z') {
			line = parse_keyvalue_entry(parse_sample_keyvalue, sample, line);
		} else {
			const char *end;
			double val = ascii_strtod(line, &end);
			if (end == line) {
				report_error("Odd sample data: %s", line);
				break;
			}
			line = (char *)end;
			line = parse_sample_unit(sample, val, line);
		}
	}
	finish_sample(dc);
}
Example #3
0
 std::vector<sample_t>
 sample_details(
     const poptype &p, const std::vector<integer_type> &individuals,
     const bool removeFixed,
     const std::vector<std::pair<double, double>> &locus_boundaries)
 {
     auto temp = fwdpp_internal::ms_sample_separate_mlocus(
         p.mutations, p.gametes, p.diploids, individuals,
         2 * individuals.size(), removeFixed);
     if (!removeFixed && temp.size() != locus_boundaries.size())
         {
             throw std::runtime_error(
                 "incorrect number of elements in locus_boundaries");
         }
     std::vector<sample_t> rv;
     std::size_t j = 0;
     for (auto &i : temp)
         {
             rv.emplace_back(std::move(i.first));
             std::move(i.second.begin(), i.second.end(),
                       std::back_inserter(rv[j]));
         }
     for (std::size_t i = 0; i < locus_boundaries.size(); ++i)
         {
             finish_sample(rv.at(i), p.fixations, 2 * individuals.size(),
                           removeFixed, sugar::treat_neutral::ALL,
                           locus_boundaries.at(i));
         }
     return rv;
 }
Example #4
0
 void
 finish_sample(
     std::vector<sep_sample_t> &sample, const vec_mutation_t &fixations,
     const unsigned nsam, const bool removeFixed,
     const sugar::treat_neutral,
     const std::vector<std::pair<double, double>> &locus_boundaries)
 {
     for (std::size_t i = 0; i < sample.size(); ++i)
         {
             finish_sample(sample[i].first, fixations, nsam, removeFixed,
                           sugar::treat_neutral::NEUTRAL,
                           locus_boundaries.at(i));
             finish_sample(sample[i].second, fixations, nsam, removeFixed,
                           sugar::treat_neutral::SELECTED,
                           locus_boundaries.at(i));
         }
 }
Example #5
0
int try_to_open_csv(struct memblock *mem, enum csv_format type)
{
	char *p = mem->buffer;
	char *header[8];
	int i, time;
	timestamp_t date;
	struct dive *dive;
	struct divecomputer *dc;

	for (i = 0; i < 8; i++) {
		header[i] = p;
		p = strchr(p, ',');
		if (!p)
			return 0;
		p++;
	}

	date = parse_date(header[2]);
	if (!date)
		return 0;

	dive = alloc_dive();
	dive->when = date;
	dive->number = atoi(header[1]);
	dc = &dive->dc;

	time = 0;
	for (;;) {
		char *end;
		double val;
		struct sample *sample;

		errno = 0;
		val = strtod(p, &end); // FIXME == localization issue
		if (end == p)
			break;
		if (errno)
			break;

		sample = prepare_sample(dc);
		sample->time.seconds = time;
		add_sample_data(sample, type, val);
		finish_sample(dc);

		time++;
		dc->duration.seconds = time;
		if (*end != ',')
			break;
		p = end + 1;
	}
	record_dive(dive);
	return 1;
}
Example #6
0
void
sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
{
	int i;
	struct dive **divep = userdata;
	struct dive *dive = *divep;
	struct sample *sample;

	/*
	 * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
	 * which creates a new one.
	 */
	sample = dive->samples ? dive->sample+dive->samples-1 : NULL;

	switch (type) {
	case SAMPLE_TYPE_TIME:
		sample = prepare_sample(divep);
		sample->time.seconds = value.time;
		finish_sample(*divep, sample);
		break;
	case SAMPLE_TYPE_DEPTH:
		sample->depth.mm = value.depth * 1000 + 0.5;
		break;
	case SAMPLE_TYPE_PRESSURE:
		sample->cylinderindex = value.pressure.tank;
		sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
		break;
	case SAMPLE_TYPE_TEMPERATURE:
		sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
		break;
	case SAMPLE_TYPE_EVENT:
		handle_event(dive, sample, value);
		break;
	case SAMPLE_TYPE_RBT:
		printf("   <rbt>%u</rbt>\n", value.rbt);
		break;
	case SAMPLE_TYPE_HEARTBEAT:
		printf("   <heartbeat>%u</heartbeat>\n", value.heartbeat);
		break;
	case SAMPLE_TYPE_BEARING:
		printf("   <bearing>%u</bearing>\n", value.bearing);
		break;
	case SAMPLE_TYPE_VENDOR:
		printf("   <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
		for (i = 0; i < value.vendor.size; ++i)
			printf("%02X", ((unsigned char *) value.vendor.data)[i]);
		printf("</vendor>\n");
		break;
	default:
		break;
	}
}
Example #7
0
static void sample_end(void)
{
	if (!cur_dive)
		return;

	finish_sample(get_dc());
	lastndl = cur_sample->ndl.seconds;
	lastindeco = cur_sample->in_deco;
	laststoptime = cur_sample->stoptime.seconds;
	laststopdepth = cur_sample->stopdepth.mm;
	lastcns = cur_sample->cns;
	lastpo2 = cur_sample->po2;
	cur_sample = NULL;
}
Example #8
0
 sample_t
 sample_details(const poptype &p,
                const std::vector<integer_type> &individuals,
                const bool removeFixed)
 {
     sep_sample_t temp = fwdpp_internal::ms_sample_separate_single_deme(
         p.mutations, p.gametes, p.diploids, individuals,
         2 * individuals.size(), removeFixed);
     auto rv = std::move(temp.first);
     std::move(temp.second.begin(), temp.second.end(),
               std::back_inserter(rv));
     finish_sample(rv, p.fixations, 2 * individuals.size(), removeFixed,
                   sugar::treat_neutral::ALL);
     return rv;
 }
Example #9
0
/* simply overwrite the data in the displayed_dive
 * return false if something goes wrong */
static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas)
{
	struct divedatapoint *dp;
	struct divecomputer *dc;
	struct sample *sample;
	struct gasmix oldgasmix;
	struct event *ev;
	cylinder_t *cyl;
	int oldpo2 = 0;
	int lasttime = 0;
	int lastdepth = 0;

	if (!diveplan || !diveplan->dp)
		return;
#if DEBUG_PLAN & 4
	printf("in create_dive_from_plan\n");
	dump_plan(diveplan);
#endif
	// reset the cylinders and clear out the samples and events of the
	// displayed dive so we can restart
	reset_cylinders(&displayed_dive, track_gas);
	dc = &displayed_dive.dc;
	free(dc->sample);
	dc->sample = NULL;
	dc->samples = 0;
	dc->alloc_samples = 0;
	while ((ev = dc->events)) {
		dc->events = dc->events->next;
		free(ev);
	}
	dp = diveplan->dp;
	cyl = &displayed_dive.cylinder[0];
	oldgasmix = cyl->gasmix;
	sample = prepare_sample(dc);
	sample->po2.mbar = dp->po2;
	if (track_gas && cyl->type.workingpressure.mbar)
		sample->cylinderpressure.mbar = cyl->end.mbar;
	finish_sample(dc);
	while (dp) {
		struct gasmix gasmix = dp->gasmix;
		int po2 = dp->po2;
		int time = dp->time;
		int depth = dp->depth;

		if (time == 0) {
			/* special entries that just inform the algorithm about
			 * additional gases that are available */
			if (verify_gas_exists(gasmix) < 0)
				goto gas_error_exit;
			dp = dp->next;
			continue;
		}

		/* Check for SetPoint change */
		if (oldpo2 != po2) {
			if (lasttime)
				/* this is a bad idea - we should get a different SAMPLE_EVENT type
				 * reserved for this in libdivecomputer... overloading SMAPLE_EVENT_PO2
				 * with a different meaning will only cause confusion elsewhere in the code */
				add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, "SP change");
			oldpo2 = po2;
		}

		/* Make sure we have the new gas, and create a gas change event */
		if (gasmix_distance(&gasmix, &oldgasmix) > 0) {
			int idx;
			if ((idx = verify_gas_exists(gasmix)) < 0)
				goto gas_error_exit;
			/* need to insert a first sample for the new gas */
			add_gas_switch_event(&displayed_dive, dc, lasttime + 1, idx);
			cyl = &displayed_dive.cylinder[idx];
			sample = prepare_sample(dc);
			sample[-1].po2.mbar = po2;
			sample->time.seconds = lasttime + 1;
			sample->depth.mm = lastdepth;
			if (track_gas && cyl->type.workingpressure.mbar)
				sample->cylinderpressure.mbar = cyl->sample_end.mbar;
			finish_sample(dc);
			oldgasmix = gasmix;
		}
		/* Create sample */
		sample = prepare_sample(dc);
		/* set po2 at beginning of this segment */
		/* and keep it valid for last sample - where it likely doesn't matter */
		sample[-1].po2.mbar = po2;
		sample->po2.mbar = po2;
		sample->time.seconds = lasttime = time;
		sample->depth.mm = lastdepth = depth;
		if (track_gas) {
			update_cylinder_pressure(&displayed_dive, sample[-1].depth.mm, depth, time - sample[-1].time.seconds,
					dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl, !dp->entered);
			if (cyl->type.workingpressure.mbar)
				sample->cylinderpressure.mbar = cyl->end.mbar;
		}
		finish_sample(dc);
		dp = dp->next;
	}
#if DEBUG_PLAN & 32
	save_dive(stdout, &displayed_dive);
#endif
	return;

gas_error_exit:
	report_error(translate("gettextFromC", "Too many gas mixes"));
	return;
}
Example #10
0
int parse_txt_file(const char *filename, const char *csv)
{
	struct memblock memtxt, memcsv;

	if (readfile(filename, &memtxt) < 0) {
		return report_error(translate("gettextFromC", "Failed to read '%s'"), filename);
	}

	/*
	 * MkVI stores some information in .txt file but the whole profile and events are stored in .csv file. First
	 * make sure the input .txt looks like proper MkVI file, then start parsing the .csv.
	 */
	if (MATCH(memtxt.buffer, "MkVI_Config") == 0) {
		int d, m, y, he;
		int hh = 0, mm = 0, ss = 0;
		int prev_depth = 0, cur_sampletime = 0, prev_setpoint = -1, prev_ndl = -1;
		bool has_depth = false, has_setpoint = false, has_ndl = false;
		char *lineptr, *key, *value;
		int cur_cylinder_index = 0;
		unsigned int prev_time = 0;

		struct dive *dive;
		struct divecomputer *dc;
		struct tm cur_tm;

		value = parse_mkvi_value(memtxt.buffer, "Dive started at");
		if (sscanf(value, "%d-%d-%d %d:%d:%d", &y, &m, &d, &hh, &mm, &ss) != 6) {
			free(value);
			return -1;
		}
		free(value);
		cur_tm.tm_year = y;
		cur_tm.tm_mon = m - 1;
		cur_tm.tm_mday = d;
		cur_tm.tm_hour = hh;
		cur_tm.tm_min = mm;
		cur_tm.tm_sec = ss;

		dive = alloc_dive();
		dive->when = utc_mktime(&cur_tm);;
		dive->dc.model = strdup("Poseidon MkVI Discovery");
		value = parse_mkvi_value(memtxt.buffer, "Rig Serial number");
		dive->dc.deviceid = atoi(value);
		free(value);
		dive->dc.divemode = CCR;
		dive->dc.no_o2sensors = 2;

		dive->cylinder[cur_cylinder_index].cylinder_use = OXYGEN;
		dive->cylinder[cur_cylinder_index].type.size.mliter = 3000;
		dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000;
		dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6");
		dive->cylinder[cur_cylinder_index].gasmix.o2.permille = 1000;
		cur_cylinder_index++;

		dive->cylinder[cur_cylinder_index].cylinder_use = DILUENT;
		dive->cylinder[cur_cylinder_index].type.size.mliter = 3000;
		dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000;
		dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6");
		value = parse_mkvi_value(memtxt.buffer, "Helium percentage");
		he = atoi(value);
		free(value);
		value = parse_mkvi_value(memtxt.buffer, "Nitrogen percentage");
		dive->cylinder[cur_cylinder_index].gasmix.o2.permille = (100 - atoi(value) - he) * 10;
		free(value);
		dive->cylinder[cur_cylinder_index].gasmix.he.permille = he * 10;
		cur_cylinder_index++;

		lineptr = strstr(memtxt.buffer, "Dive started at");
		while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) {
			++lineptr;	// Skip over '\n'
			key = next_mkvi_key(lineptr);
			if (!key)
				break;
			value = parse_mkvi_value(lineptr, key);
			if (!value) {
				free(key);
				break;
			}
			add_extra_data(&dive->dc, key, value);
			free(key);
			free(value);
		}
		dc = &dive->dc;

		/*
		 * Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has
		 * the following format:
		 *
		 * timestamp, type, value
		 *
		 * And following fields are of interest to us:
		 *
		 * 	6	sensor1
		 * 	7	sensor2
		 * 	8	depth
		 *	13	o2 tank pressure
		 *	14	diluent tank pressure
		 *	20	o2 setpoint
		 *	39	water temp
		 */

		if (readfile(csv, &memcsv) < 0) {
			free(dive);
			return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv);
		}
		lineptr = memcsv.buffer;
		for (;;) {
			struct sample *sample;
			int type;
			int value;
			int sampletime;
			int gaschange = 0;

			/* Collect all the information for one sample */
			sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value);

			has_depth = false;
			has_setpoint = false;
			has_ndl = false;
			sample = prepare_sample(dc);

			/*
			 * There was a bug in MKVI download tool that resulted in erroneous sample
			 * times. This fix should work similarly as the vendor's own.
			 */

			sample->time.seconds = cur_sampletime < 0xFFFF * 3 / 4 ? cur_sampletime : prev_time;
			prev_time = sample->time.seconds;

			do {
				int i = sscanf(lineptr, "%d,%d,%d", &sampletime, &type, &value);
				switch (i) {
				case 3:
					switch (type) {
					case 0:
						//Mouth piece position event: 0=OC, 1=CC, 2=UN, 3=NC
						switch (value) {
						case 0:
							add_event(dc, cur_sampletime, 0, 0, 0,
									QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position OC"));
							break;
						case 1:
							add_event(dc, cur_sampletime, 0, 0, 0,
									QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position CC"));
							break;
						case 2:
							add_event(dc, cur_sampletime, 0, 0, 0,
									QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position unknown"));
							break;
						case 3:
							add_event(dc, cur_sampletime, 0, 0, 0,
									QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position not connected"));
							break;
						}
						break;
					case 3:
						//Power Off event
						add_event(dc, cur_sampletime, 0, 0, 0,
								QT_TRANSLATE_NOOP("gettextFromC", "Power off"));
						break;
					case 4:
						//Battery State of Charge in %
#ifdef SAMPLE_EVENT_BATTERY
						add_event(dc, cur_sampletime, SAMPLE_EVENT_BATTERY, 0,
								value, QT_TRANSLATE_NOOP("gettextFromC", "battery"));
#endif
						break;
					case 6:
						//PO2 Cell 1 Average
						add_sample_data(sample, POSEIDON_SENSOR1, value);
						break;
					case 7:
						//PO2 Cell 2 Average
						add_sample_data(sample, POSEIDON_SENSOR2, value);
						break;
					case 8:
						//Depth * 2
						has_depth = true;
						prev_depth = value;
						add_sample_data(sample, POSEIDON_DEPTH, value);
						break;
						//9 Max Depth * 2
						//10 Ascent/Descent Rate * 2
					case 11:
						//Ascent Rate Alert >10 m/s
						add_event(dc, cur_sampletime, SAMPLE_EVENT_ASCENT, 0, 0,
								QT_TRANSLATE_NOOP("gettextFromC", "ascent"));
						break;
					case 13:
						//O2 Tank Pressure
						add_sample_pressure(sample, 0, lrint(value * 1000));
						break;
					case 14:
						//Diluent Tank Pressure
						add_sample_pressure(sample, 1, lrint(value * 1000));
						break;
						//16 Remaining dive time #1?
						//17 related to O2 injection
					case 20:
						//PO2 Setpoint
						has_setpoint = true;
						prev_setpoint = value;
						add_sample_data(sample, POSEIDON_SETPOINT, value);
						break;
					case 22:
						//End of O2 calibration Event: 0 = OK, 2 = Failed, rest of dive setpoint 1.0
						if (value == 2)
							add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0,
									QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration failed"));
						add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0,
								QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration"));
						break;
					case 25:
						//25 Max Ascent depth
						add_sample_data(sample, POSEIDON_CEILING, value);
						break;
					case 31:
						//Start of O2 calibration Event
						add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_BEGIN, 0,
								QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration"));
						break;
					case 37:
						//Remaining dive time #2?
						has_ndl = true;
						prev_ndl = value;
						add_sample_data(sample, POSEIDON_NDL, value);
						break;
					case 39:
						// Water Temperature in Celcius
						add_sample_data(sample, POSEIDON_TEMP, value);
						break;
					case 85:
						//He diluent part in %
						gaschange += value << 16;
						break;
					case 86:
						//O2 diluent part in %
						gaschange += value;
						break;
						//239 Unknown, maybe PO2 at sensor validation?
						//240 Unknown, maybe PO2 at sensor validation?
						//247 Unknown, maybe PO2 Cell 1 during pressure test
						//248 Unknown, maybe PO2 Cell 2 during pressure test
						//250 PO2 Cell 1
						//251 PO2 Cell 2
					default:
						break;
					} /* sample types */
					break;
				case EOF:
					break;
				default:
					printf("Unable to parse input: %s\n", lineptr);
					break;
				}

				lineptr = strchr(lineptr, '\n');
				if (!lineptr || !*lineptr)
					break;
				lineptr++;

				/* Grabbing next sample time */
				sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value);
			} while (sampletime == cur_sampletime);

			if (gaschange)
				add_event(dc, cur_sampletime, SAMPLE_EVENT_GASCHANGE2, 0, gaschange,
						QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
			if (!has_depth)
				add_sample_data(sample, POSEIDON_DEPTH, prev_depth);
			if (!has_setpoint && prev_setpoint >= 0)
				add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint);
			if (!has_ndl && prev_ndl >= 0)
				add_sample_data(sample, POSEIDON_NDL, prev_ndl);
			finish_sample(dc);

			if (!lineptr || !*lineptr)
				break;
		}
		record_dive(dive);
		return 1;
	} else {
		return 0;
	}

	return 0;
}
Example #11
0
/*
* Parse sample data, extract events and build a dive
*/
static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
				  const unsigned char *samples, unsigned int size,
				  unsigned int *duration, double *max_depth,
				  double *avg_depth, double *min_temp)
{
	const unsigned char *s;
	unsigned int offset = 0, profile_period = 1, sample_cnt = 0;
	double depth = 0, temp = 0, depth_sample = 0, psi = 0, sgc_rate = 0;
	int ascent_rate = 0;
	unsigned int ndl = 0;
	unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0;

	struct divecomputer *dc = &dive->dc;
	struct sample *sample;

	// Initialize stat variables
	*max_depth = 0, *avg_depth = 0, *min_temp = 0xFF;

	// Get starting depth and temp (tank PSI???)
	switch (config.type) {
	case TYPE_GEMINI:
		depth = (float) (log[CMD_START_DEPTH]
			+ log[CMD_START_DEPTH + 1] * 256) / 4;
		temp = log[CMD_START_TEMP];
		psi = log[CMD_START_PSI] + log[CMD_START_PSI + 1] * 256;
		sgc_rate = (float)(log[CMD_START_SGC]
			+ log[CMD_START_SGC + 1] * 256) / 2;
		profile_period = log[CMD_PROFILE_PERIOD];
		break;
	case TYPE_COMMANDER:
		depth = (float) (log[CMD_START_DEPTH]
			+ log[CMD_START_DEPTH + 1] * 256) / 4;
		temp = log[CMD_START_TEMP];
		profile_period = log[CMD_PROFILE_PERIOD];
		break;

	case TYPE_EMC:
		depth = (float) log [EMC_START_DEPTH] / 256
			+ log[EMC_START_DEPTH + 1];
		temp = log[EMC_START_TEMP];
		profile_period = log[EMC_PROFILE_PERIOD];
		break;
	}

	// Skip past pre-dive events
	unsigned int x = 0;
	unsigned int c;
	while (x < size && (samples[x] & 0x80) == 0 && samples[x] != 0x40) {
		c = cochran_predive_event_bytes(samples[x]) + 1;
#ifdef COCHRAN_DEBUG
		printf("Predive event: ");
		for (unsigned int y = 0; y < c && x + y < size; y++) printf("%02x ", samples[x + y]);
		putchar('\n');
#endif
			x += c;
	}

	// Now process samples
	offset = x;
	while (offset + config.sample_size < size) {
		s = samples + offset;

		// Start with an empty sample
		sample = prepare_sample(dc);
		sample->time.seconds = sample_cnt * profile_period;

		// Check for event
		if (s[0] & 0x80) {
			cochran_dive_event(dc, s, sample_cnt * profile_period, &in_deco, &deco_ceiling, &deco_time);
			offset += cochran_dive_event_bytes(s[0]) + 1;
			continue;
		}

		// Depth is in every sample
		depth_sample = (float)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
		depth += depth_sample;

#ifdef COCHRAN_DEBUG
		cochran_debug_sample(s, sample_cnt);
#endif

		switch (config.type) {
		case TYPE_COMMANDER:
			switch (sample_cnt % 2) {
			case 0:	// Ascent rate
				ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1: -1);
				break;
			case 1:	// Temperature
				temp = s[1] / 2 + 20;
				break;
			}
			break;
		case TYPE_GEMINI:
			// Gemini with tank pressure and SAC rate.
			switch (sample_cnt % 4) {
			case 0:	// Ascent rate
				ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1);
				break;
			case 2:	// PSI change
				psi -= (float)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 4;
				break;
			case 1:	// SGC rate
				sgc_rate -= (float)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 2;
				break;
			case 3:	// Temperature
				temp = (float)s[1] / 2 + 20;
				break;
			}
			break;
		case TYPE_EMC:
			switch (sample_cnt % 2) {
			case 0:	// Ascent rate
				ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1: -1);
				break;
			case 1:	// Temperature
				temp = (float)s[1] / 2 + 20;
				break;
			}
			// Get NDL and deco information
			switch (sample_cnt % 24) {
			case 20:
				if (offset + 5 < size) {
					if (in_deco) {
						// Fist stop time
						//first_deco_time = (s[2] + s[5] * 256 + 1) * 60; // seconds
						ndl = 0;
					} else {
						// NDL
						ndl = (s[2] + s[5] * 256 + 1) * 60; // seconds
						deco_time = 0;
					}
				}
				break;
			case 22:
				if (offset + 5 < size) {
					if (in_deco) {
						// Total stop time
						deco_time = (s[2] + s[5] * 256 + 1) * 60; // seconds
						ndl = 0;
					}
				}
				break;
			}
		}

		// Track dive stats
		if (depth > *max_depth) *max_depth = depth;
		if (temp < *min_temp) *min_temp = temp;
		*avg_depth = (*avg_depth * sample_cnt + depth) / (sample_cnt + 1);

		sample->depth.mm = lrint(depth * FEET * 1000);
		sample->ndl.seconds = ndl;
		sample->in_deco = in_deco;
		sample->stoptime.seconds = deco_time;
		sample->stopdepth.mm = lrint(deco_ceiling * FEET * 1000);
		sample->temperature.mkelvin = C_to_mkelvin((temp - 32) / 1.8);
		sample->sensor[0] = 0;
		sample->pressure[0].mbar = lrint(psi * PSI / 100);

		finish_sample(dc);

		offset += config.sample_size;
		sample_cnt++;
	}
	UNUSED(ascent_rate); // mark the variable as unused

	if (sample_cnt > 0)
		*duration = sample_cnt * profile_period - 1;
}