mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	cleanup: split out divecomputer functions from dive.c
Since dive.c is so huge, split out divecomputer-related functions into divecomputer.[c|h], sample.[c|h] and extradata.[c|h]. This does not give huge compile time improvements, since struct dive contains a struct divecomputer and therefore dive.h has to include divecomputer.h. However, it make things distinctly more clear. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
		
							parent
							
								
									4aa571d5a0
								
							
						
					
					
						commit
						0e196310f9
					
				
					 38 changed files with 777 additions and 680 deletions
				
			
		|  | @ -13,6 +13,7 @@ | |||
| #include "core/divefilter.h" | ||||
| #include "core/divesite.h" | ||||
| #include "core/picture.h" | ||||
| #include "core/sample.h" | ||||
| #include "exportfuncs.h" | ||||
| 
 | ||||
| #if !defined(SUBSURFACE_MOBILE) | ||||
|  |  | |||
|  | @ -64,6 +64,9 @@ set(SUBSURFACE_CORE_LIB_SRCS | |||
| 	display.h | ||||
| 	dive.c | ||||
| 	dive.h | ||||
| 	divecomputer.c | ||||
| 	divecomputer.h | ||||
| 	dive.h | ||||
| 	divefilter.cpp | ||||
| 	divefilter.h | ||||
| 	divelist.c | ||||
|  | @ -84,6 +87,7 @@ set(SUBSURFACE_CORE_LIB_SRCS | |||
| 	errorhelper.c | ||||
| 	exif.cpp | ||||
| 	exif.h | ||||
| 	extradata.h | ||||
| 	file.c | ||||
| 	file.h | ||||
| 	filterconstraint.cpp | ||||
|  | @ -144,6 +148,8 @@ set(SUBSURFACE_CORE_LIB_SRCS | |||
| 	qt-init.cpp | ||||
| 	qthelper.cpp | ||||
| 	qthelper.h | ||||
| 	sample.c | ||||
| 	sample.h | ||||
| 	save-git.c | ||||
| 	save-html.c | ||||
| 	save-html.h | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| 
 | ||||
| #include "dive.h" | ||||
| #include "file.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-time.h" | ||||
| #include "units.h" | ||||
| #include "sha1.h" | ||||
|  |  | |||
							
								
								
									
										184
									
								
								core/device.cpp
									
										
									
									
									
								
							
							
						
						
									
										184
									
								
								core/device.cpp
									
										
									
									
									
								
							|  | @ -8,190 +8,6 @@ | |||
| #include "core/settings/qPrefDiveComputer.h" | ||||
| #include <QString> // for QString::number
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Good fake dive profiles are hard. | ||||
|  * | ||||
|  * "depthtime" is the integral of the dive depth over | ||||
|  * time ("area" of the dive profile). We want that | ||||
|  * area to match the average depth (avg_d*max_t). | ||||
|  * | ||||
|  * To do that, we generate a 6-point profile: | ||||
|  * | ||||
|  *  (0, 0) | ||||
|  *  (t1, max_d) | ||||
|  *  (t2, max_d) | ||||
|  *  (t3, d) | ||||
|  *  (t4, d) | ||||
|  *  (max_t, 0) | ||||
|  * | ||||
|  * with the same ascent/descent rates between the | ||||
|  * different depths. | ||||
|  * | ||||
|  * NOTE: avg_d, max_d and max_t are given constants. | ||||
|  * The rest we can/should play around with to get a | ||||
|  * good-looking profile. | ||||
|  * | ||||
|  * That six-point profile gives a total area of: | ||||
|  * | ||||
|  *   (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) | ||||
|  * | ||||
|  * And the "same ascent/descent rates" requirement | ||||
|  * gives us (time per depth must be same): | ||||
|  * | ||||
|  *   t1 / max_d = (t3-t2) / (max_d-d) | ||||
|  *   t1 / max_d = (max_t-t4) / d | ||||
|  * | ||||
|  * We also obviously require: | ||||
|  * | ||||
|  *   0 <= t1 <= t2 <= t3 <= t4 <= max_t | ||||
|  * | ||||
|  * Let us call 'd_frac = d / max_d', and we get: | ||||
|  * | ||||
|  * Total area must match average depth-time: | ||||
|  * | ||||
|  *   (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t | ||||
|  *      max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t | ||||
|  *             max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d | ||||
|  *                   t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d) | ||||
|  * | ||||
|  * and descent slope must match ascent slopes: | ||||
|  * | ||||
|  *   t1 / max_d = (t3-t2) / (max_d*(1-d_frac)) | ||||
|  *           t1 = (t3-t2)/(1-d_frac) | ||||
|  * | ||||
|  * and | ||||
|  * | ||||
|  *   t1 / max_d = (max_t-t4) / (max_d*d_frac) | ||||
|  *           t1 = (max_t-t4)/d_frac | ||||
|  * | ||||
|  * In general, we have more free variables than we have constraints, | ||||
|  * but we can aim for certain basics, like a good ascent slope. | ||||
|  */ | ||||
| static int fill_samples(struct sample *s, int max_d, int avg_d, int max_t, double slope, double d_frac) | ||||
| { | ||||
| 	double t_frac = max_t * (1 - avg_d / (double)max_d); | ||||
| 	int t1 = lrint(max_d / slope); | ||||
| 	int t4 = lrint(max_t - t1 * d_frac); | ||||
| 	int t3 = lrint(t4 - (t_frac - t1) / (1 - d_frac)); | ||||
| 	int t2 = lrint(t3 - t1 * (1 - d_frac)); | ||||
| 
 | ||||
| 	if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	s[1].time.seconds = t1; | ||||
| 	s[1].depth.mm = max_d; | ||||
| 	s[2].time.seconds = t2; | ||||
| 	s[2].depth.mm = max_d; | ||||
| 	s[3].time.seconds = t3; | ||||
| 	s[3].depth.mm = lrint(max_d * d_frac); | ||||
| 	s[4].time.seconds = t4; | ||||
| 	s[4].depth.mm = lrint(max_d * d_frac); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| /* we have no average depth; instead of making up a random average depth
 | ||||
|  * we should assume either a PADI rectangular profile (for short and/or | ||||
|  * shallow dives) or more reasonably a six point profile with a 3 minute | ||||
|  * safety stop at 5m */ | ||||
| static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double slope) | ||||
| { | ||||
| 	// shallow or short dives are just trapecoids based on the given slope
 | ||||
| 	if (max_d < 10000 || max_t < 600) { | ||||
| 		s[1].time.seconds = lrint(max_d / slope); | ||||
| 		s[1].depth.mm = max_d; | ||||
| 		s[2].time.seconds = max_t - lrint(max_d / slope); | ||||
| 		s[2].depth.mm = max_d; | ||||
| 	} else { | ||||
| 		s[1].time.seconds = lrint(max_d / slope); | ||||
| 		s[1].depth.mm = max_d; | ||||
| 		s[2].time.seconds = max_t - lrint(max_d / slope) - 180; | ||||
| 		s[2].depth.mm = max_d; | ||||
| 		s[3].time.seconds = max_t - lrint(5000 / slope) - 180; | ||||
| 		s[3].depth.mm = 5000; | ||||
| 		s[4].time.seconds = max_t - lrint(5000 / slope); | ||||
| 		s[4].depth.mm = 5000; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| extern "C" void fake_dc(struct divecomputer *dc) | ||||
| { | ||||
| 	alloc_samples(dc, 6); | ||||
| 	struct sample *fake = dc->sample; | ||||
| 	int i; | ||||
| 
 | ||||
| 	dc->samples = 6; | ||||
| 
 | ||||
| 	/* The dive has no samples, so create a few fake ones */ | ||||
| 	int max_t = dc->duration.seconds; | ||||
| 	int max_d = dc->maxdepth.mm; | ||||
| 	int avg_d = dc->meandepth.mm; | ||||
| 
 | ||||
| 	memset(fake, 0, 6 * sizeof(struct sample)); | ||||
| 	fake[5].time.seconds = max_t; | ||||
| 	for (i = 0; i < 6; i++) { | ||||
| 		fake[i].bearing.degrees = -1; | ||||
| 		fake[i].ndl.seconds = -1; | ||||
| 	} | ||||
| 	if (!max_t || !max_d) { | ||||
| 		dc->samples = 0; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Set last manually entered time to the total dive length */ | ||||
| 	dc->last_manual_time = dc->duration; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We want to fake the profile so that the average | ||||
| 	 * depth ends up correct. However, in the absence of | ||||
| 	 * a reasonable average, let's just make something | ||||
| 	 * up. Note that 'avg_d == max_d' is _not_ a reasonable | ||||
| 	 * average. | ||||
| 	 * We explicitly treat avg_d == 0 differently */ | ||||
| 	if (avg_d == 0) { | ||||
| 		/* we try for a sane slope, but bow to the insanity of
 | ||||
| 		 * the user supplied data */ | ||||
| 		fill_samples_no_avg(fake, max_d, max_t, MAX(2.0 * max_d / max_t, (double)prefs.ascratelast6m)); | ||||
| 		if (fake[3].time.seconds == 0) { // just a 4 point profile
 | ||||
| 			dc->samples = 4; | ||||
| 			fake[3].time.seconds = max_t; | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	if (avg_d < max_d / 10 || avg_d >= max_d) { | ||||
| 		avg_d = (max_d + 10000) / 3; | ||||
| 		if (avg_d > max_d) | ||||
| 			avg_d = max_d * 2 / 3; | ||||
| 	} | ||||
| 	if (!avg_d) | ||||
| 		avg_d = 1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ok, first we try a basic profile with a specific ascent | ||||
| 	 * rate (5 meters per minute) and d_frac (1/3). | ||||
| 	 */ | ||||
| 	if (fill_samples(fake, max_d, avg_d, max_t, (double)prefs.ascratelast6m, 0.33)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ok, assume that didn't work because we cannot make the | ||||
| 	 * average come out right because it was a quick deep dive | ||||
| 	 * followed by a much shallower region | ||||
| 	 */ | ||||
| 	if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Uhhuh. That didn't work. We'd need to find a good combination that | ||||
| 	 * satisfies our constraints. Currently, we don't, we just give insane | ||||
| 	 * slopes. | ||||
| 	 */ | ||||
| 	if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Even that didn't work? Give up, there's something wrong */ | ||||
| } | ||||
| 
 | ||||
| struct device_table device_table; | ||||
| 
 | ||||
| bool device::operator==(const device &a) const | ||||
|  |  | |||
|  | @ -16,7 +16,6 @@ struct dive_table; | |||
| // global device table
 | ||||
| extern struct device_table device_table; | ||||
| 
 | ||||
| extern void fake_dc(struct divecomputer *dc); | ||||
| extern void set_dc_deviceid(struct divecomputer *dc, unsigned int deviceid, const struct device_table *table); | ||||
| 
 | ||||
| extern void add_devices_of_dive(const struct dive *dive, struct device_table *table); | ||||
|  |  | |||
							
								
								
									
										388
									
								
								core/dive.c
									
										
									
									
									
								
							
							
						
						
									
										388
									
								
								core/dive.c
									
										
									
									
									
								
							|  | @ -14,9 +14,11 @@ | |||
| #include "divesite.h" | ||||
| #include "errorhelper.h" | ||||
| #include "event.h" | ||||
| #include "extradata.h" | ||||
| #include "qthelper.h" | ||||
| #include "membuffer.h" | ||||
| #include "picture.h" | ||||
| #include "sample.h" | ||||
| #include "tag.h" | ||||
| #include "trip.h" | ||||
| #include "structured_list.h" | ||||
|  | @ -40,47 +42,6 @@ const char *divemode_text[] = {"OC", "CCR", "PSCR", "Freedive"}; | |||
| 
 | ||||
| static int calculate_depth_to_mbar(int depth, pressure_t surface_pressure, int salinity); | ||||
| 
 | ||||
| /*
 | ||||
|  * Adding a cylinder pressure sample field is not quite as trivial as it | ||||
|  * perhaps should be. | ||||
|  * | ||||
|  * We try to keep the same sensor index for the same sensor, so that even | ||||
|  * if the dive computer doesn't give pressure information for every sample, | ||||
|  * we don't move pressure information around between the different sensor | ||||
|  * indices. | ||||
|  * | ||||
|  * The "prepare_sample()" function will always copy the sensor indices | ||||
|  * from the previous sample, so the indices are pre-populated (but the | ||||
|  * pressures obviously are not) | ||||
|  */ | ||||
| void add_sample_pressure(struct sample *sample, int sensor, int mbar) | ||||
| { | ||||
| 	int idx; | ||||
| 
 | ||||
| 	if (!mbar) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Do we already have a slot for this sensor */ | ||||
| 	for (idx = 0; idx < MAX_SENSORS; idx++) { | ||||
| 		if (sensor != sample->sensor[idx]) | ||||
| 			continue; | ||||
| 		sample->pressure[idx].mbar = mbar; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Pick the first unused index if we couldn't reuse one */ | ||||
| 	for (idx = 0; idx < MAX_SENSORS; idx++) { | ||||
| 		if (sample->pressure[idx].mbar) | ||||
| 			continue; | ||||
| 		sample->sensor[idx] = sensor; | ||||
| 		sample->pressure[idx].mbar = mbar; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We do not have enough slots for the pressure samples. */ | ||||
| 	/* Should we warn the user about dropping pressure data? */ | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * The legacy format for sample pressures has a single pressure | ||||
|  * for each sample that can have any sensor, plus a possible | ||||
|  | @ -143,32 +104,6 @@ struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc | |||
| 	return ev; | ||||
| } | ||||
| 
 | ||||
| void add_event_to_dc(struct divecomputer *dc, struct event *ev) | ||||
| { | ||||
| 	struct event **p; | ||||
| 
 | ||||
| 	p = &dc->events; | ||||
| 
 | ||||
| 	/* insert in the sorted list of events */ | ||||
| 	while (*p && (*p)->time.seconds <= ev->time.seconds) | ||||
| 		p = &(*p)->next; | ||||
| 	ev->next = *p; | ||||
| 	*p = ev; | ||||
| } | ||||
| 
 | ||||
| struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name) | ||||
| { | ||||
| 	struct event *ev = create_event(time, type, flags, value, name); | ||||
| 
 | ||||
| 	if (!ev) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	add_event_to_dc(dc, ev); | ||||
| 
 | ||||
| 	remember_event(name); | ||||
| 	return ev; | ||||
| } | ||||
| 
 | ||||
| void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) | ||||
| { | ||||
| 	/* sanity check so we don't crash */ | ||||
|  | @ -183,31 +118,6 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second | |||
| 	add_event_to_dc(dc, ev); | ||||
| } | ||||
| 
 | ||||
| /* Substitutes an event in a divecomputer for another. No reordering is performed! */ | ||||
| void swap_event(struct divecomputer *dc, struct event *from, struct event *to) | ||||
| { | ||||
| 	for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { | ||||
| 		if (*ep == from) { | ||||
| 			to->next = from->next; | ||||
| 			*ep = to; | ||||
| 			from->next = NULL; // For good measure.
 | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Remove given event from dive computer. Does *not* free the event. */ | ||||
| void remove_event_from_dc(struct divecomputer *dc, struct event *event) | ||||
| { | ||||
| 	for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { | ||||
| 		if (*ep == event) { | ||||
| 			*ep = event->next; | ||||
| 			event->next = NULL; // For good measure.
 | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* since the name is an array as part of the structure (how silly is that?) we
 | ||||
|  * have to actually remove the existing event and replace it with a new one. | ||||
|  * WARNING, WARNING... this may end up freeing event in case that event is indeed | ||||
|  | @ -232,43 +142,6 @@ void update_event_name(struct dive *d, struct event *event, const char *name) | |||
| 	invalidate_dive_cache(d); | ||||
| } | ||||
| 
 | ||||
| void add_extra_data(struct divecomputer *dc, const char *key, const char *value) | ||||
| { | ||||
| 	struct extra_data **ed = &dc->extra_data; | ||||
| 
 | ||||
| 	while (*ed) | ||||
| 		ed = &(*ed)->next; | ||||
| 	*ed = malloc(sizeof(struct extra_data)); | ||||
| 	if (*ed) { | ||||
| 		(*ed)->key = strdup(key); | ||||
| 		(*ed)->value = strdup(value); | ||||
| 		(*ed)->next = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Find the divemode at time 'time' (in seconds) into the dive. Sequentially step through the divemode-change events,
 | ||||
|  * saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode | ||||
|  * is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration | ||||
|  * that calls this function, the search does not have to begin at the first event of the dive */ | ||||
| enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode) | ||||
| { | ||||
| 	const struct event *ev = *evp; | ||||
| 	if (dc) { | ||||
| 		if (*divemode == UNDEF_COMP_TYPE) { | ||||
| 			*divemode = dc->divemode; | ||||
| 			ev = get_next_event(dc->events, "modechange"); | ||||
| 		} | ||||
| 	} else { | ||||
| 		ev = NULL; | ||||
| 	} | ||||
| 	while (ev && ev->time.seconds < time) { | ||||
| 		*divemode = (enum divemode_t) ev->value; | ||||
| 		ev = get_next_event(ev->next, "modechange"); | ||||
| 	} | ||||
| 	*evp = ev; | ||||
| 	return *divemode; | ||||
| } | ||||
| 
 | ||||
| struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event *ev) | ||||
| { | ||||
| 	if (ev && event_is_gaschange(ev)) { | ||||
|  | @ -304,9 +177,6 @@ struct dive *alloc_dive(void) | |||
| 	return dive; | ||||
| } | ||||
| 
 | ||||
| static void free_dc(struct divecomputer *dc); | ||||
| static void free_dc_contents(struct divecomputer *dc); | ||||
| 
 | ||||
| /* copy an element in a list of dive computer extra data */ | ||||
| static void copy_extra_data(struct extra_data *sed, struct extra_data *ded) | ||||
| { | ||||
|  | @ -346,14 +216,6 @@ static void copy_dc_renumber(struct dive *d, const struct divecomputer *sdc, str | |||
| 	ddc->next = NULL; | ||||
| } | ||||
| 
 | ||||
| /* The first divecomputer is embedded in the dive structure. Free its data but not
 | ||||
|  * the structure itself. For all remainding dcs in the list, free data *and* structures. */ | ||||
| void free_dive_dcs(struct divecomputer *dc) | ||||
| { | ||||
| 	free_dc_contents(dc); | ||||
| 	STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc); | ||||
| } | ||||
| 
 | ||||
| static void free_dive_structures(struct dive *d) | ||||
| { | ||||
| 	if (!d) | ||||
|  | @ -476,24 +338,6 @@ void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_compo | |||
| } | ||||
| #undef CONDITIONAL_COPY_STRING | ||||
| 
 | ||||
| /* copies all events in this dive computer */ | ||||
| void copy_events(const struct divecomputer *s, struct divecomputer *d) | ||||
| { | ||||
| 	const struct event *ev; | ||||
| 	struct event **pev; | ||||
| 	if (!s || !d) | ||||
| 		return; | ||||
| 	ev = s->events; | ||||
| 	pev = &d->events; | ||||
| 	while (ev != NULL) { | ||||
| 		struct event *new_ev = clone_event(ev); | ||||
| 		*pev = new_ev; | ||||
| 		pev = &new_ev->next; | ||||
| 		ev = ev->next; | ||||
| 	} | ||||
| 	*pev = NULL; | ||||
| } | ||||
| 
 | ||||
| /* copies all events from all dive computers before a given time
 | ||||
|    this is used when editing a dive in the planner to preserve the events | ||||
|    of the old dive */ | ||||
|  | @ -542,81 +386,6 @@ void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void copy_samples(const struct divecomputer *s, struct divecomputer *d) | ||||
| { | ||||
| 	/* instead of carefully copying them one by one and calling add_sample
 | ||||
| 	 * over and over again, let's just copy the whole blob */ | ||||
| 	if (!s || !d) | ||||
| 		return; | ||||
| 	int nr = s->samples; | ||||
| 	d->samples = nr; | ||||
| 	d->alloc_samples = nr; | ||||
| 	// We expect to be able to read the memory in the other end of the pointer
 | ||||
| 	// if its a valid pointer, so don't expect malloc() to return NULL for
 | ||||
| 	// zero-sized malloc, do it ourselves.
 | ||||
| 	d->sample = NULL; | ||||
| 
 | ||||
| 	if(!nr) | ||||
| 		return; | ||||
| 
 | ||||
| 	d->sample = malloc(nr * sizeof(struct sample)); | ||||
| 	if (d->sample) | ||||
| 		memcpy(d->sample, s->sample, nr * sizeof(struct sample)); | ||||
| } | ||||
| 
 | ||||
| /* make room for num samples; if not enough space is available, the sample
 | ||||
|  * array is reallocated and the existing samples are copied. */ | ||||
| void alloc_samples(struct divecomputer *dc, int num) | ||||
| { | ||||
| 	if (num > dc->alloc_samples) { | ||||
| 		dc->alloc_samples = (num * 3) / 2 + 10; | ||||
| 		dc->sample = realloc(dc->sample, dc->alloc_samples * sizeof(struct sample)); | ||||
| 		if (!dc->sample) | ||||
| 			dc->samples = dc->alloc_samples = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void free_samples(struct divecomputer *dc) | ||||
| { | ||||
| 	if (dc) { | ||||
| 		free(dc->sample); | ||||
| 		dc->sample = 0; | ||||
| 		dc->samples = 0; | ||||
| 		dc->alloc_samples = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct sample *prepare_sample(struct divecomputer *dc) | ||||
| { | ||||
| 	if (dc) { | ||||
| 		int nr = dc->samples; | ||||
| 		struct sample *sample; | ||||
| 		alloc_samples(dc, nr + 1); | ||||
| 		if (!dc->sample) | ||||
| 			return NULL; | ||||
| 		sample = dc->sample + nr; | ||||
| 		memset(sample, 0, sizeof(*sample)); | ||||
| 
 | ||||
| 		// Copy the sensor numbers - but not the pressure values
 | ||||
| 		// from the previous sample if any.
 | ||||
| 		if (nr) { | ||||
| 			for (int idx = 0; idx < MAX_SENSORS; idx++) | ||||
| 				sample->sensor[idx] = sample[-1].sensor[idx]; | ||||
| 		} | ||||
| 		// Init some values with -1
 | ||||
| 		sample->bearing.degrees = -1; | ||||
| 		sample->ndl.seconds = -1; | ||||
| 
 | ||||
| 		return sample; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| void finish_sample(struct divecomputer *dc) | ||||
| { | ||||
| 	dc->samples++; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * So when we re-calculate maxdepth and meandepth, we will | ||||
|  * not override the old numbers if they are close to the | ||||
|  | @ -653,40 +422,6 @@ static void update_temperature(temperature_t *temperature, int new) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Calculate how long we were actually under water, and the average | ||||
|  * depth while under water. | ||||
|  * | ||||
|  * This ignores any surface time in the middle of the dive. | ||||
|  */ | ||||
| void fixup_dc_duration(struct divecomputer *dc) | ||||
| { | ||||
| 	int duration, i; | ||||
| 	int lasttime, lastdepth, depthtime; | ||||
| 
 | ||||
| 	duration = 0; | ||||
| 	lasttime = 0; | ||||
| 	lastdepth = 0; | ||||
| 	depthtime = 0; | ||||
| 	for (i = 0; i < dc->samples; i++) { | ||||
| 		struct sample *sample = dc->sample + i; | ||||
| 		int time = sample->time.seconds; | ||||
| 		int depth = sample->depth.mm; | ||||
| 
 | ||||
| 		/* We ignore segments at the surface */ | ||||
| 		if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) { | ||||
| 			duration += time - lasttime; | ||||
| 			depthtime += (time - lasttime) * (depth + lastdepth) / 2; | ||||
| 		} | ||||
| 		lastdepth = depth; | ||||
| 		lasttime = time; | ||||
| 	} | ||||
| 	if (duration) { | ||||
| 		dc->duration.seconds = duration; | ||||
| 		dc->meandepth.mm = (depthtime + duration / 2) / duration; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Which cylinders had gas used? */ | ||||
| #define SOME_GAS 5000 | ||||
| static bool cylinder_used(const cylinder_t *cyl) | ||||
|  | @ -1129,49 +864,12 @@ static void fixup_duration(struct dive *dive) | |||
| 	dive->duration.seconds = duration.seconds; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * What do the dive computers say the water temperature is? | ||||
|  * (not in the samples, but as dc property for dcs that support that) | ||||
|  */ | ||||
| unsigned int dc_watertemp(const struct divecomputer *dc) | ||||
| { | ||||
| 	int sum = 0, nr = 0; | ||||
| 
 | ||||
| 	do { | ||||
| 		if (dc->watertemp.mkelvin) { | ||||
| 			sum += dc->watertemp.mkelvin; | ||||
| 			nr++; | ||||
| 		} | ||||
| 	} while ((dc = dc->next) != NULL); | ||||
| 	if (!nr) | ||||
| 		return 0; | ||||
| 	return (sum + nr / 2) / nr; | ||||
| } | ||||
| 
 | ||||
| static void fixup_watertemp(struct dive *dive) | ||||
| { | ||||
| 	if (!dive->watertemp.mkelvin) | ||||
| 		dive->watertemp.mkelvin = dc_watertemp(&dive->dc); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * What do the dive computers say the air temperature is? | ||||
|  */ | ||||
| unsigned int dc_airtemp(const struct divecomputer *dc) | ||||
| { | ||||
| 	int sum = 0, nr = 0; | ||||
| 
 | ||||
| 	do { | ||||
| 		if (dc->airtemp.mkelvin) { | ||||
| 			sum += dc->airtemp.mkelvin; | ||||
| 			nr++; | ||||
| 		} | ||||
| 	} while ((dc = dc->next) != NULL); | ||||
| 	if (!nr) | ||||
| 		return 0; | ||||
| 	return (sum + nr / 2) / nr; | ||||
| } | ||||
| 
 | ||||
| static void fixup_airtemp(struct dive *dive) | ||||
| { | ||||
| 	if (!dive->airtemp.mkelvin) | ||||
|  | @ -1583,18 +1281,6 @@ struct dive *fixup_dive(struct dive *dive) | |||
| #define MERGE_TXT(res, a, b, n, sep) res->n = merge_text(a->n, b->n, sep) | ||||
| #define MERGE_NONZERO(res, a, b, n) res->n = a->n ? a->n : b->n | ||||
| 
 | ||||
| struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) | ||||
| { | ||||
| 	struct sample *p = prepare_sample(dc); | ||||
| 
 | ||||
| 	if (p) { | ||||
| 		*p = *sample; | ||||
| 		p->time.seconds = time; | ||||
| 		finish_sample(dc); | ||||
| 	} | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This is like add_sample(), but if the distance from the last sample | ||||
|  * is excessive, we add two surface samples in between. | ||||
|  | @ -2537,35 +2223,6 @@ static int similar(unsigned long a, unsigned long b, unsigned long expected) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Match two dive computer entries against each other, and | ||||
|  * tell if it's the same dive. Return 0 if "don't know", | ||||
|  * positive for "same dive" and negative for "definitely | ||||
|  * not the same dive" | ||||
|  */ | ||||
| int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) | ||||
| { | ||||
| 	/* Not same model? Don't know if matching.. */ | ||||
| 	if (!a->model || !b->model) | ||||
| 		return 0; | ||||
| 	if (strcasecmp(a->model, b->model)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Different device ID's? Don't know */ | ||||
| 	if (a->deviceid != b->deviceid) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Do we have dive IDs? */ | ||||
| 	if (!a->diveid || !b->diveid) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If they have different dive ID's on the same | ||||
| 	 * dive computer, that's a definite "same or not" | ||||
| 	 */ | ||||
| 	return a->diveid == b->diveid && a->when == b->when ? 1 : -1; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Match every dive computer against each other to see if | ||||
|  * we have a matching dive. | ||||
|  | @ -2680,28 +2337,6 @@ struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded | |||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| static void free_extra_data(struct extra_data *ed) | ||||
| { | ||||
| 	free((void *)ed->key); | ||||
| 	free((void *)ed->value); | ||||
| } | ||||
| 
 | ||||
| static void free_dc_contents(struct divecomputer *dc) | ||||
| { | ||||
| 	free(dc->sample); | ||||
| 	free((void *)dc->model); | ||||
| 	free((void *)dc->serial); | ||||
| 	free((void *)dc->fw_version); | ||||
| 	free_events(dc->events); | ||||
| 	STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data); | ||||
| } | ||||
| 
 | ||||
| static void free_dc(struct divecomputer *dc) | ||||
| { | ||||
| 	free_dc_contents(dc); | ||||
| 	free(dc); | ||||
| } | ||||
| 
 | ||||
| static int same_sample(struct sample *a, struct sample *b) | ||||
| { | ||||
| 	if (a->time.seconds != b->time.seconds) | ||||
|  | @ -2886,11 +2521,6 @@ static void join_dive_computers(struct dive *d, struct divecomputer *res, | |||
| 	remove_redundant_dc(res, prefer_downloaded); | ||||
| } | ||||
| 
 | ||||
| bool is_dc_planner(const struct divecomputer *dc) | ||||
| { | ||||
| 	return same_string(dc->model, "planned dive"); | ||||
| } | ||||
| 
 | ||||
| // Does this dive have a dive computer for which is_dc_planner has value planned
 | ||||
| bool has_planned(const struct dive *dive, bool planned) | ||||
| { | ||||
|  | @ -3486,20 +3116,6 @@ void split_divecomputer(const struct dive *src, int num, struct dive **out1, str | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* helper function to make it easier to work with our structures
 | ||||
|  * we don't interpolate here, just use the value from the last sample up to that time */ | ||||
| int get_depth_at_time(const struct divecomputer *dc, unsigned int time) | ||||
| { | ||||
| 	int depth = 0; | ||||
| 	if (dc && dc->sample) | ||||
| 		for (int i = 0; i < dc->samples; i++) { | ||||
| 			if (dc->sample[i].time.seconds > time) | ||||
| 				break; | ||||
| 			depth = dc->sample[i].depth.mm; | ||||
| 		} | ||||
| 	return depth; | ||||
| } | ||||
| 
 | ||||
| //Calculate O2 in best mix
 | ||||
| fraction_t best_o2(depth_t depth, const struct dive *dive) | ||||
| { | ||||
|  |  | |||
							
								
								
									
										102
									
								
								core/dive.h
									
										
									
									
									
								
							
							
						
						
									
										102
									
								
								core/dive.h
									
										
									
									
									
								
							|  | @ -13,6 +13,7 @@ | |||
| #include <stdio.h> | ||||
| 
 | ||||
| #include "divemode.h" | ||||
| #include "divecomputer.h" | ||||
| #include "equipment.h" | ||||
| #include "picture.h" | ||||
| 
 | ||||
|  | @ -20,8 +21,6 @@ | |||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| struct event; | ||||
| 
 | ||||
| extern int last_xml_version; | ||||
| 
 | ||||
| extern const char *cylinderuse_text[NUM_GAS_USE]; | ||||
|  | @ -39,73 +38,13 @@ static inline int interpolate(int a, int b, int part, int whole) | |||
| 	return (a+b)/2; | ||||
| } | ||||
| 
 | ||||
| #define MAX_SENSORS 2 | ||||
| struct sample                         // BASE TYPE BYTES  UNITS    RANGE               DESCRIPTION
 | ||||
| {                                     // --------- -----  -----    -----               -----------
 | ||||
| 	duration_t time;                  // int32_t    4  seconds  (0-34 yrs)             elapsed dive time up to this sample
 | ||||
| 	duration_t stoptime;              // int32_t    4  seconds  (0-34 yrs)             time duration of next deco stop
 | ||||
| 	duration_t ndl;                   // int32_t    4  seconds  (-1 no val, 0-34 yrs)  time duration before no-deco limit
 | ||||
| 	duration_t tts;                   // int32_t    4  seconds  (0-34 yrs)             time duration to reach the surface
 | ||||
| 	duration_t rbt;                   // int32_t    4  seconds  (0-34 yrs)             remaining bottom time
 | ||||
| 	depth_t depth;                    // int32_t    4    mm     (0-2000 km)            dive depth of this sample
 | ||||
| 	depth_t stopdepth;                // int32_t    4    mm     (0-2000 km)            depth of next deco stop
 | ||||
| 	temperature_t temperature;        // uint32_t   4    mK     (0-4 MK)               ambient temperature
 | ||||
| 	pressure_t pressure[MAX_SENSORS]; // int32_t    4    mbar   (0-2 Mbar)             cylinder pressures (main and CCR o2)
 | ||||
| 	o2pressure_t setpoint;            // uint16_t   2    mbar   (0-65 bar)             O2 partial pressure (will be setpoint)
 | ||||
| 	o2pressure_t o2sensor[3];         // uint16_t   6    mbar   (0-65 bar)             Up to 3 PO2 sensor values (rebreather)
 | ||||
| 	bearing_t bearing;                // int16_t    2  degrees  (-1 no val, 0-360 deg) compass bearing
 | ||||
| 	uint8_t sensor[MAX_SENSORS];      // uint8_t    1  sensorID (0-255)                ID of cylinder pressure sensor
 | ||||
| 	uint16_t cns;                     // uint16_t   1     %     (0-64k %)              cns% accumulated
 | ||||
| 	uint8_t heartbeat;                // uint8_t    1  beats/m  (0-255)                heart rate measurement
 | ||||
| 	volume_t sac;                     //            4  ml/min                          predefined SAC
 | ||||
| 	bool in_deco;                     // bool       1    y/n      y/n                  this sample is part of deco
 | ||||
| 	bool manually_entered;            // bool       1    y/n      y/n                  this sample was entered by the user,
 | ||||
| 					  //                                               not calculated when planning a dive
 | ||||
| };	                                  // Total size of structure: 57 bytes, excluding padding at end
 | ||||
| 
 | ||||
| struct extra_data { | ||||
| 	const char *key; | ||||
| 	const char *value; | ||||
| 	struct extra_data *next; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * NOTE! The deviceid and diveid are model-specific *hashes* of | ||||
|  * whatever device identification that model may have. Different | ||||
|  * dive computers will have different identifying data, it could | ||||
|  * be a firmware number or a serial ID (in either string or in | ||||
|  * numeric format), and we do not care. | ||||
|  * | ||||
|  * The only thing we care about is that subsurface will hash | ||||
|  * that information the same way. So then you can check the ID | ||||
|  * of a dive computer by comparing the hashes for equality. | ||||
|  * | ||||
|  * A deviceid or diveid of zero is assumed to be "no ID". | ||||
|  */ | ||||
| struct divecomputer { | ||||
| 	timestamp_t when; | ||||
| 	duration_t duration, surfacetime, last_manual_time; | ||||
| 	depth_t maxdepth, meandepth; | ||||
| 	temperature_t airtemp, watertemp; | ||||
| 	pressure_t surface_pressure; | ||||
| 	enum divemode_t divemode;	// dive computer type: OC(default) or CCR
 | ||||
| 	uint8_t no_o2sensors;		// rebreathers: number of O2 sensors used
 | ||||
| 	int salinity; 			// kg per 10000 l
 | ||||
| 	const char *model, *serial, *fw_version; | ||||
| 	uint32_t deviceid, diveid; | ||||
| 	int samples, alloc_samples; | ||||
| 	struct sample *sample; | ||||
| 	struct event *events; | ||||
| 	struct extra_data *extra_data; | ||||
| 	struct divecomputer *next; | ||||
| }; | ||||
| 
 | ||||
| struct dive_site; | ||||
| struct dive_site_table; | ||||
| struct dive_table; | ||||
| struct dive_trip; | ||||
| struct trip_table; | ||||
| struct full_text_cache; | ||||
| struct event; | ||||
| struct trip_table; | ||||
| struct dive { | ||||
| 	struct dive_trip *divetrip; | ||||
| 	timestamp_t when; | ||||
|  | @ -171,13 +110,8 @@ struct dive_components { | |||
| 	unsigned int weights : 1; | ||||
| }; | ||||
| 
 | ||||
| extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode); | ||||
| extern struct event *get_next_divemodechange(const struct event **evd, bool update_pointer); | ||||
| extern enum divemode_t get_divemode_at_time(const struct divecomputer *dc, int dtime, const struct event **ev_dmc); | ||||
| 
 | ||||
| extern bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx); | ||||
| extern int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *dc); | ||||
| extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); | ||||
| 
 | ||||
| extern fraction_t best_o2(depth_t depth, const struct dive *dive); | ||||
| extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); | ||||
|  | @ -191,8 +125,6 @@ extern int mbar_to_depth(int mbar, const struct dive *dive); | |||
| extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto); | ||||
| extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto); | ||||
| 
 | ||||
| #define SURFACE_THRESHOLD 750 /* somewhat arbitrary: only below 75cm is it really diving */ | ||||
| 
 | ||||
| extern bool autogroup; | ||||
| 
 | ||||
| struct dive *unregister_dive(int idx); | ||||
|  | @ -243,9 +175,6 @@ extern location_t dive_get_gps_location(const struct dive *d); | |||
| 
 | ||||
| extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset); | ||||
| 
 | ||||
| /* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */ | ||||
| extern int match_one_dc(const struct divecomputer *a, const struct divecomputer *b); | ||||
| 
 | ||||
| extern int save_dives(const char *filename); | ||||
| extern int save_dives_logic(const char *filename, bool select_only, bool anonymize); | ||||
| extern int save_dive(FILE *f, struct dive *dive, bool anonymize); | ||||
|  | @ -262,19 +191,12 @@ extern bool subsurface_user_is_root(void); | |||
| 
 | ||||
| extern struct dive *alloc_dive(void); | ||||
| extern void free_dive(struct dive *); | ||||
| extern void free_dive_dcs(struct divecomputer *dc); | ||||
| extern void record_dive_to_table(struct dive *dive, struct dive_table *table); | ||||
| extern void clear_dive(struct dive *dive); | ||||
| extern void copy_dive(const struct dive *s, struct dive *d); | ||||
| extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear); | ||||
| extern struct dive *move_dive(struct dive *s); | ||||
| 
 | ||||
| extern void alloc_samples(struct divecomputer *dc, int num); | ||||
| extern void free_samples(struct divecomputer *dc); | ||||
| extern struct sample *prepare_sample(struct divecomputer *dc); | ||||
| extern void finish_sample(struct divecomputer *dc); | ||||
| extern struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc); | ||||
| extern void add_sample_pressure(struct sample *sample, int sensor, int mbar); | ||||
| extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc); | ||||
| 
 | ||||
| extern bool dive_less_than(const struct dive *a, const struct dive *b); | ||||
|  | @ -284,28 +206,18 @@ extern struct dive *fixup_dive(struct dive *dive); | |||
| extern pressure_t calculate_surface_pressure(const struct dive *dive); | ||||
| extern pressure_t un_fixup_surface_pressure(const struct dive *d); | ||||
| extern int get_dive_salinity(const struct dive *dive); | ||||
| extern void fixup_dc_duration(struct divecomputer *dc); | ||||
| extern int dive_getUniqID(); | ||||
| extern unsigned int dc_airtemp(const struct divecomputer *dc); | ||||
| extern unsigned int dc_watertemp(const struct divecomputer *dc); | ||||
| extern int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2); | ||||
| extern int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2); | ||||
| extern struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site); | ||||
| extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded); | ||||
| extern void copy_events(const struct divecomputer *s, struct divecomputer *d); | ||||
| extern void copy_events_until(const struct dive *sd, struct dive *dd, int time); | ||||
| extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only); | ||||
| extern void copy_samples(const struct divecomputer *s, struct divecomputer *d); | ||||
| extern bool is_cylinder_used(const struct dive *dive, int idx); | ||||
| extern bool is_cylinder_prot(const struct dive *dive, int idx); | ||||
| extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx); | ||||
| extern struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx); | ||||
| extern void add_event_to_dc(struct divecomputer *dc, struct event *ev); | ||||
| extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to); | ||||
| extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name); | ||||
| extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); | ||||
| extern void update_event_name(struct dive *d, struct event *event, const char *name); | ||||
| extern void add_extra_data(struct divecomputer *dc, const char *key, const char *value); | ||||
| extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); | ||||
| extern int get_cylinder_index(const struct dive *dive, const struct event *ev); | ||||
| extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event *ev); | ||||
|  | @ -328,7 +240,6 @@ extern const char *existing_filename; | |||
| extern void subsurface_command_line_init(int *, char ***); | ||||
| extern void subsurface_command_line_exit(int *, char ***); | ||||
| 
 | ||||
| extern bool is_dc_planner(const struct divecomputer *dc); | ||||
| extern bool has_planned(const struct dive *dive, bool planned); | ||||
| 
 | ||||
| /* Get gasmixes at increasing timestamps.
 | ||||
|  | @ -340,13 +251,6 @@ extern struct gasmix get_gasmix(const struct dive *dive, const struct divecomput | |||
| /* Get gasmix at a given time */ | ||||
| extern struct gasmix get_gasmix_at_time(const struct dive *dive, const struct divecomputer *dc, duration_t time); | ||||
| 
 | ||||
| /* these structs holds the information that
 | ||||
|  * describes the cylinders / weight systems. | ||||
|  * they are global variables initialized in equipment.c | ||||
|  * used to fill the combobox in the add/edit cylinder | ||||
|  * dialog | ||||
|  */ | ||||
| 
 | ||||
| extern void set_informational_units(const char *units); | ||||
| extern void set_git_prefs(const char *prefs); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										543
									
								
								core/divecomputer.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										543
									
								
								core/divecomputer.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,543 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| 
 | ||||
| #include "divecomputer.h" | ||||
| #include "event.h" | ||||
| #include "extradata.h" | ||||
| #include "pref.h" | ||||
| #include "sample.h" | ||||
| #include "structured_list.h" | ||||
| #include "subsurface-string.h" | ||||
| 
 | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Good fake dive profiles are hard. | ||||
|  * | ||||
|  * "depthtime" is the integral of the dive depth over | ||||
|  * time ("area" of the dive profile). We want that | ||||
|  * area to match the average depth (avg_d*max_t). | ||||
|  * | ||||
|  * To do that, we generate a 6-point profile: | ||||
|  * | ||||
|  *  (0, 0) | ||||
|  *  (t1, max_d) | ||||
|  *  (t2, max_d) | ||||
|  *  (t3, d) | ||||
|  *  (t4, d) | ||||
|  *  (max_t, 0) | ||||
|  * | ||||
|  * with the same ascent/descent rates between the | ||||
|  * different depths. | ||||
|  * | ||||
|  * NOTE: avg_d, max_d and max_t are given constants. | ||||
|  * The rest we can/should play around with to get a | ||||
|  * good-looking profile. | ||||
|  * | ||||
|  * That six-point profile gives a total area of: | ||||
|  * | ||||
|  *   (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) | ||||
|  * | ||||
|  * And the "same ascent/descent rates" requirement | ||||
|  * gives us (time per depth must be same): | ||||
|  * | ||||
|  *   t1 / max_d = (t3-t2) / (max_d-d) | ||||
|  *   t1 / max_d = (max_t-t4) / d | ||||
|  * | ||||
|  * We also obviously require: | ||||
|  * | ||||
|  *   0 <= t1 <= t2 <= t3 <= t4 <= max_t | ||||
|  * | ||||
|  * Let us call 'd_frac = d / max_d', and we get: | ||||
|  * | ||||
|  * Total area must match average depth-time: | ||||
|  * | ||||
|  *   (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t | ||||
|  *      max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t | ||||
|  *             max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d | ||||
|  *                   t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d) | ||||
|  * | ||||
|  * and descent slope must match ascent slopes: | ||||
|  * | ||||
|  *   t1 / max_d = (t3-t2) / (max_d*(1-d_frac)) | ||||
|  *           t1 = (t3-t2)/(1-d_frac) | ||||
|  * | ||||
|  * and | ||||
|  * | ||||
|  *   t1 / max_d = (max_t-t4) / (max_d*d_frac) | ||||
|  *           t1 = (max_t-t4)/d_frac | ||||
|  * | ||||
|  * In general, we have more free variables than we have constraints, | ||||
|  * but we can aim for certain basics, like a good ascent slope. | ||||
|  */ | ||||
| static int fill_samples(struct sample *s, int max_d, int avg_d, int max_t, double slope, double d_frac) | ||||
| { | ||||
| 	double t_frac = max_t * (1 - avg_d / (double)max_d); | ||||
| 	int t1 = lrint(max_d / slope); | ||||
| 	int t4 = lrint(max_t - t1 * d_frac); | ||||
| 	int t3 = lrint(t4 - (t_frac - t1) / (1 - d_frac)); | ||||
| 	int t2 = lrint(t3 - t1 * (1 - d_frac)); | ||||
| 
 | ||||
| 	if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	s[1].time.seconds = t1; | ||||
| 	s[1].depth.mm = max_d; | ||||
| 	s[2].time.seconds = t2; | ||||
| 	s[2].depth.mm = max_d; | ||||
| 	s[3].time.seconds = t3; | ||||
| 	s[3].depth.mm = lrint(max_d * d_frac); | ||||
| 	s[4].time.seconds = t4; | ||||
| 	s[4].depth.mm = lrint(max_d * d_frac); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| /* we have no average depth; instead of making up a random average depth
 | ||||
|  * we should assume either a PADI rectangular profile (for short and/or | ||||
|  * shallow dives) or more reasonably a six point profile with a 3 minute | ||||
|  * safety stop at 5m */ | ||||
| static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double slope) | ||||
| { | ||||
| 	// shallow or short dives are just trapecoids based on the given slope
 | ||||
| 	if (max_d < 10000 || max_t < 600) { | ||||
| 		s[1].time.seconds = lrint(max_d / slope); | ||||
| 		s[1].depth.mm = max_d; | ||||
| 		s[2].time.seconds = max_t - lrint(max_d / slope); | ||||
| 		s[2].depth.mm = max_d; | ||||
| 	} else { | ||||
| 		s[1].time.seconds = lrint(max_d / slope); | ||||
| 		s[1].depth.mm = max_d; | ||||
| 		s[2].time.seconds = max_t - lrint(max_d / slope) - 180; | ||||
| 		s[2].depth.mm = max_d; | ||||
| 		s[3].time.seconds = max_t - lrint(5000 / slope) - 180; | ||||
| 		s[3].depth.mm = 5000; | ||||
| 		s[4].time.seconds = max_t - lrint(5000 / slope); | ||||
| 		s[4].depth.mm = 5000; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void fake_dc(struct divecomputer *dc) | ||||
| { | ||||
| 	alloc_samples(dc, 6); | ||||
| 	struct sample *fake = dc->sample; | ||||
| 	int i; | ||||
| 
 | ||||
| 	dc->samples = 6; | ||||
| 
 | ||||
| 	/* The dive has no samples, so create a few fake ones */ | ||||
| 	int max_t = dc->duration.seconds; | ||||
| 	int max_d = dc->maxdepth.mm; | ||||
| 	int avg_d = dc->meandepth.mm; | ||||
| 
 | ||||
| 	memset(fake, 0, 6 * sizeof(struct sample)); | ||||
| 	fake[5].time.seconds = max_t; | ||||
| 	for (i = 0; i < 6; i++) { | ||||
| 		fake[i].bearing.degrees = -1; | ||||
| 		fake[i].ndl.seconds = -1; | ||||
| 	} | ||||
| 	if (!max_t || !max_d) { | ||||
| 		dc->samples = 0; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Set last manually entered time to the total dive length */ | ||||
| 	dc->last_manual_time = dc->duration; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We want to fake the profile so that the average | ||||
| 	 * depth ends up correct. However, in the absence of | ||||
| 	 * a reasonable average, let's just make something | ||||
| 	 * up. Note that 'avg_d == max_d' is _not_ a reasonable | ||||
| 	 * average. | ||||
| 	 * We explicitly treat avg_d == 0 differently */ | ||||
| 	if (avg_d == 0) { | ||||
| 		/* we try for a sane slope, but bow to the insanity of
 | ||||
| 		 * the user supplied data */ | ||||
| 		fill_samples_no_avg(fake, max_d, max_t, MAX(2.0 * max_d / max_t, (double)prefs.ascratelast6m)); | ||||
| 		if (fake[3].time.seconds == 0) { // just a 4 point profile
 | ||||
| 			dc->samples = 4; | ||||
| 			fake[3].time.seconds = max_t; | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	if (avg_d < max_d / 10 || avg_d >= max_d) { | ||||
| 		avg_d = (max_d + 10000) / 3; | ||||
| 		if (avg_d > max_d) | ||||
| 			avg_d = max_d * 2 / 3; | ||||
| 	} | ||||
| 	if (!avg_d) | ||||
| 		avg_d = 1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ok, first we try a basic profile with a specific ascent | ||||
| 	 * rate (5 meters per minute) and d_frac (1/3). | ||||
| 	 */ | ||||
| 	if (fill_samples(fake, max_d, avg_d, max_t, (double)prefs.ascratelast6m, 0.33)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ok, assume that didn't work because we cannot make the | ||||
| 	 * average come out right because it was a quick deep dive | ||||
| 	 * followed by a much shallower region | ||||
| 	 */ | ||||
| 	if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Uhhuh. That didn't work. We'd need to find a good combination that | ||||
| 	 * satisfies our constraints. Currently, we don't, we just give insane | ||||
| 	 * slopes. | ||||
| 	 */ | ||||
| 	if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Even that didn't work? Give up, there's something wrong */ | ||||
| } | ||||
| 
 | ||||
| /* Find the divemode at time 'time' (in seconds) into the dive. Sequentially step through the divemode-change events,
 | ||||
|  * saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode | ||||
|  * is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration | ||||
|  * that calls this function, the search does not have to begin at the first event of the dive */ | ||||
| enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode) | ||||
| { | ||||
| 	const struct event *ev = *evp; | ||||
| 	if (dc) { | ||||
| 		if (*divemode == UNDEF_COMP_TYPE) { | ||||
| 			*divemode = dc->divemode; | ||||
| 			ev = get_next_event(dc->events, "modechange"); | ||||
| 		} | ||||
| 	} else { | ||||
| 		ev = NULL; | ||||
| 	} | ||||
| 	while (ev && ev->time.seconds < time) { | ||||
| 		*divemode = (enum divemode_t) ev->value; | ||||
| 		ev = get_next_event(ev->next, "modechange"); | ||||
| 	} | ||||
| 	*evp = ev; | ||||
| 	return *divemode; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* helper function to make it easier to work with our structures
 | ||||
|  * we don't interpolate here, just use the value from the last sample up to that time */ | ||||
| int get_depth_at_time(const struct divecomputer *dc, unsigned int time) | ||||
| { | ||||
| 	int depth = 0; | ||||
| 	if (dc && dc->sample) | ||||
| 		for (int i = 0; i < dc->samples; i++) { | ||||
| 			if (dc->sample[i].time.seconds > time) | ||||
| 				break; | ||||
| 			depth = dc->sample[i].depth.mm; | ||||
| 		} | ||||
| 	return depth; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* The first divecomputer is embedded in the dive structure. Free its data but not
 | ||||
|  * the structure itself. For all remainding dcs in the list, free data *and* structures. */ | ||||
| void free_dive_dcs(struct divecomputer *dc) | ||||
| { | ||||
| 	free_dc_contents(dc); | ||||
| 	STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc); | ||||
| } | ||||
| 
 | ||||
| /* make room for num samples; if not enough space is available, the sample
 | ||||
|  * array is reallocated and the existing samples are copied. */ | ||||
| void alloc_samples(struct divecomputer *dc, int num) | ||||
| { | ||||
| 	if (num > dc->alloc_samples) { | ||||
| 		dc->alloc_samples = (num * 3) / 2 + 10; | ||||
| 		dc->sample = realloc(dc->sample, dc->alloc_samples * sizeof(struct sample)); | ||||
| 		if (!dc->sample) | ||||
| 			dc->samples = dc->alloc_samples = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void free_samples(struct divecomputer *dc) | ||||
| { | ||||
| 	if (dc) { | ||||
| 		free(dc->sample); | ||||
| 		dc->sample = 0; | ||||
| 		dc->samples = 0; | ||||
| 		dc->alloc_samples = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct sample *prepare_sample(struct divecomputer *dc) | ||||
| { | ||||
| 	if (dc) { | ||||
| 		int nr = dc->samples; | ||||
| 		struct sample *sample; | ||||
| 		alloc_samples(dc, nr + 1); | ||||
| 		if (!dc->sample) | ||||
| 			return NULL; | ||||
| 		sample = dc->sample + nr; | ||||
| 		memset(sample, 0, sizeof(*sample)); | ||||
| 
 | ||||
| 		// Copy the sensor numbers - but not the pressure values
 | ||||
| 		// from the previous sample if any.
 | ||||
| 		if (nr) { | ||||
| 			for (int idx = 0; idx < MAX_SENSORS; idx++) | ||||
| 				sample->sensor[idx] = sample[-1].sensor[idx]; | ||||
| 		} | ||||
| 		// Init some values with -1
 | ||||
| 		sample->bearing.degrees = -1; | ||||
| 		sample->ndl.seconds = -1; | ||||
| 
 | ||||
| 		return sample; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void finish_sample(struct divecomputer *dc) | ||||
| { | ||||
| 	dc->samples++; | ||||
| } | ||||
| 
 | ||||
| struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) | ||||
| { | ||||
| 	struct sample *p = prepare_sample(dc); | ||||
| 
 | ||||
| 	if (p) { | ||||
| 		*p = *sample; | ||||
| 		p->time.seconds = time; | ||||
| 		finish_sample(dc); | ||||
| 	} | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Calculate how long we were actually under water, and the average | ||||
|  * depth while under water. | ||||
|  * | ||||
|  * This ignores any surface time in the middle of the dive. | ||||
|  */ | ||||
| void fixup_dc_duration(struct divecomputer *dc) | ||||
| { | ||||
| 	int duration, i; | ||||
| 	int lasttime, lastdepth, depthtime; | ||||
| 
 | ||||
| 	duration = 0; | ||||
| 	lasttime = 0; | ||||
| 	lastdepth = 0; | ||||
| 	depthtime = 0; | ||||
| 	for (i = 0; i < dc->samples; i++) { | ||||
| 		struct sample *sample = dc->sample + i; | ||||
| 		int time = sample->time.seconds; | ||||
| 		int depth = sample->depth.mm; | ||||
| 
 | ||||
| 		/* We ignore segments at the surface */ | ||||
| 		if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) { | ||||
| 			duration += time - lasttime; | ||||
| 			depthtime += (time - lasttime) * (depth + lastdepth) / 2; | ||||
| 		} | ||||
| 		lastdepth = depth; | ||||
| 		lasttime = time; | ||||
| 	} | ||||
| 	if (duration) { | ||||
| 		dc->duration.seconds = duration; | ||||
| 		dc->meandepth.mm = (depthtime + duration / 2) / duration; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * What do the dive computers say the water temperature is? | ||||
|  * (not in the samples, but as dc property for dcs that support that) | ||||
|  */ | ||||
| unsigned int dc_watertemp(const struct divecomputer *dc) | ||||
| { | ||||
| 	int sum = 0, nr = 0; | ||||
| 
 | ||||
| 	do { | ||||
| 		if (dc->watertemp.mkelvin) { | ||||
| 			sum += dc->watertemp.mkelvin; | ||||
| 			nr++; | ||||
| 		} | ||||
| 	} while ((dc = dc->next) != NULL); | ||||
| 	if (!nr) | ||||
| 		return 0; | ||||
| 	return (sum + nr / 2) / nr; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * What do the dive computers say the air temperature is? | ||||
|  */ | ||||
| unsigned int dc_airtemp(const struct divecomputer *dc) | ||||
| { | ||||
| 	int sum = 0, nr = 0; | ||||
| 
 | ||||
| 	do { | ||||
| 		if (dc->airtemp.mkelvin) { | ||||
| 			sum += dc->airtemp.mkelvin; | ||||
| 			nr++; | ||||
| 		} | ||||
| 	} while ((dc = dc->next) != NULL); | ||||
| 	if (!nr) | ||||
| 		return 0; | ||||
| 	return (sum + nr / 2) / nr; | ||||
| } | ||||
| 
 | ||||
| /* copies all events in this dive computer */ | ||||
| void copy_events(const struct divecomputer *s, struct divecomputer *d) | ||||
| { | ||||
| 	const struct event *ev; | ||||
| 	struct event **pev; | ||||
| 	if (!s || !d) | ||||
| 		return; | ||||
| 	ev = s->events; | ||||
| 	pev = &d->events; | ||||
| 	while (ev != NULL) { | ||||
| 		struct event *new_ev = clone_event(ev); | ||||
| 		*pev = new_ev; | ||||
| 		pev = &new_ev->next; | ||||
| 		ev = ev->next; | ||||
| 	} | ||||
| 	*pev = NULL; | ||||
| } | ||||
| 
 | ||||
| void copy_samples(const struct divecomputer *s, struct divecomputer *d) | ||||
| { | ||||
| 	/* instead of carefully copying them one by one and calling add_sample
 | ||||
| 	 * over and over again, let's just copy the whole blob */ | ||||
| 	if (!s || !d) | ||||
| 		return; | ||||
| 	int nr = s->samples; | ||||
| 	d->samples = nr; | ||||
| 	d->alloc_samples = nr; | ||||
| 	// We expect to be able to read the memory in the other end of the pointer
 | ||||
| 	// if its a valid pointer, so don't expect malloc() to return NULL for
 | ||||
| 	// zero-sized malloc, do it ourselves.
 | ||||
| 	d->sample = NULL; | ||||
| 
 | ||||
| 	if(!nr) | ||||
| 		return; | ||||
| 
 | ||||
| 	d->sample = malloc(nr * sizeof(struct sample)); | ||||
| 	if (d->sample) | ||||
| 		memcpy(d->sample, s->sample, nr * sizeof(struct sample)); | ||||
| } | ||||
| 
 | ||||
| void add_event_to_dc(struct divecomputer *dc, struct event *ev) | ||||
| { | ||||
| 	struct event **p; | ||||
| 
 | ||||
| 	p = &dc->events; | ||||
| 
 | ||||
| 	/* insert in the sorted list of events */ | ||||
| 	while (*p && (*p)->time.seconds <= ev->time.seconds) | ||||
| 		p = &(*p)->next; | ||||
| 	ev->next = *p; | ||||
| 	*p = ev; | ||||
| } | ||||
| 
 | ||||
| struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name) | ||||
| { | ||||
| 	struct event *ev = create_event(time, type, flags, value, name); | ||||
| 
 | ||||
| 	if (!ev) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	add_event_to_dc(dc, ev); | ||||
| 
 | ||||
| 	remember_event(name); | ||||
| 	return ev; | ||||
| } | ||||
| 
 | ||||
| /* Substitutes an event in a divecomputer for another. No reordering is performed! */ | ||||
| void swap_event(struct divecomputer *dc, struct event *from, struct event *to) | ||||
| { | ||||
| 	for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { | ||||
| 		if (*ep == from) { | ||||
| 			to->next = from->next; | ||||
| 			*ep = to; | ||||
| 			from->next = NULL; // For good measure.
 | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Remove given event from dive computer. Does *not* free the event. */ | ||||
| void remove_event_from_dc(struct divecomputer *dc, struct event *event) | ||||
| { | ||||
| 	for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { | ||||
| 		if (*ep == event) { | ||||
| 			*ep = event->next; | ||||
| 			event->next = NULL; // For good measure.
 | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void add_extra_data(struct divecomputer *dc, const char *key, const char *value) | ||||
| { | ||||
| 	struct extra_data **ed = &dc->extra_data; | ||||
| 
 | ||||
| 	while (*ed) | ||||
| 		ed = &(*ed)->next; | ||||
| 	*ed = malloc(sizeof(struct extra_data)); | ||||
| 	if (*ed) { | ||||
| 		(*ed)->key = strdup(key); | ||||
| 		(*ed)->value = strdup(value); | ||||
| 		(*ed)->next = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool is_dc_planner(const struct divecomputer *dc) | ||||
| { | ||||
| 	return same_string(dc->model, "planned dive"); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Match two dive computer entries against each other, and | ||||
|  * tell if it's the same dive. Return 0 if "don't know", | ||||
|  * positive for "same dive" and negative for "definitely | ||||
|  * not the same dive" | ||||
|  */ | ||||
| int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) | ||||
| { | ||||
| 	/* Not same model? Don't know if matching.. */ | ||||
| 	if (!a->model || !b->model) | ||||
| 		return 0; | ||||
| 	if (strcasecmp(a->model, b->model)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Different device ID's? Don't know */ | ||||
| 	if (a->deviceid != b->deviceid) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Do we have dive IDs? */ | ||||
| 	if (!a->diveid || !b->diveid) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If they have different dive ID's on the same | ||||
| 	 * dive computer, that's a definite "same or not" | ||||
| 	 */ | ||||
| 	return a->diveid == b->diveid && a->when == b->when ? 1 : -1; | ||||
| } | ||||
| 
 | ||||
| static void free_extra_data(struct extra_data *ed) | ||||
| { | ||||
| 	free((void *)ed->key); | ||||
| 	free((void *)ed->value); | ||||
| } | ||||
| 
 | ||||
| void free_dc_contents(struct divecomputer *dc) | ||||
| { | ||||
| 	free(dc->sample); | ||||
| 	free((void *)dc->model); | ||||
| 	free((void *)dc->serial); | ||||
| 	free((void *)dc->fw_version); | ||||
| 	free_events(dc->events); | ||||
| 	STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data); | ||||
| } | ||||
| 
 | ||||
| void free_dc(struct divecomputer *dc) | ||||
| { | ||||
| 	free_dc_contents(dc); | ||||
| 	free(dc); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										79
									
								
								core/divecomputer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								core/divecomputer.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| #ifndef DIVECOMPUTER_H | ||||
| #define DIVECOMPUTER_H | ||||
| 
 | ||||
| #include "divemode.h" | ||||
| #include "units.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| struct extra_data; | ||||
| struct sample; | ||||
| 
 | ||||
| /* Is this header the correct place? */ | ||||
| #define SURFACE_THRESHOLD 750 /* somewhat arbitrary: only below 75cm is it really diving */ | ||||
| 
 | ||||
| /*
 | ||||
|  * NOTE! The deviceid and diveid are model-specific *hashes* of | ||||
|  * whatever device identification that model may have. Different | ||||
|  * dive computers will have different identifying data, it could | ||||
|  * be a firmware number or a serial ID (in either string or in | ||||
|  * numeric format), and we do not care. | ||||
|  * | ||||
|  * The only thing we care about is that subsurface will hash | ||||
|  * that information the same way. So then you can check the ID | ||||
|  * of a dive computer by comparing the hashes for equality. | ||||
|  * | ||||
|  * A deviceid or diveid of zero is assumed to be "no ID". | ||||
|  */ | ||||
| struct divecomputer { | ||||
| 	timestamp_t when; | ||||
| 	duration_t duration, surfacetime, last_manual_time; | ||||
| 	depth_t maxdepth, meandepth; | ||||
| 	temperature_t airtemp, watertemp; | ||||
| 	pressure_t surface_pressure; | ||||
| 	enum divemode_t divemode;	// dive computer type: OC(default) or CCR
 | ||||
| 	uint8_t no_o2sensors;		// rebreathers: number of O2 sensors used
 | ||||
| 	int salinity; 			// kg per 10000 l
 | ||||
| 	const char *model, *serial, *fw_version; | ||||
| 	uint32_t deviceid, diveid; | ||||
| 	int samples, alloc_samples; | ||||
| 	struct sample *sample; | ||||
| 	struct event *events; | ||||
| 	struct extra_data *extra_data; | ||||
| 	struct divecomputer *next; | ||||
| }; | ||||
| 
 | ||||
| extern void fake_dc(struct divecomputer *dc); | ||||
| extern void free_dc(struct divecomputer *dc); | ||||
| extern void free_dc_contents(struct divecomputer *dc); | ||||
| extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode); | ||||
| extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); | ||||
| extern void free_dive_dcs(struct divecomputer *dc); | ||||
| extern void alloc_samples(struct divecomputer *dc, int num); | ||||
| extern void free_samples(struct divecomputer *dc); | ||||
| extern struct sample *prepare_sample(struct divecomputer *dc); | ||||
| extern void finish_sample(struct divecomputer *dc); | ||||
| extern struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc); | ||||
| extern void fixup_dc_duration(struct divecomputer *dc); | ||||
| extern unsigned int dc_airtemp(const struct divecomputer *dc); | ||||
| extern unsigned int dc_watertemp(const struct divecomputer *dc); | ||||
| extern void copy_events(const struct divecomputer *s, struct divecomputer *d); | ||||
| extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to); | ||||
| extern void copy_samples(const struct divecomputer *s, struct divecomputer *d); | ||||
| extern void add_event_to_dc(struct divecomputer *dc, struct event *ev); | ||||
| extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name); | ||||
| extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); | ||||
| extern void add_extra_data(struct divecomputer *dc, const char *key, const char *value); | ||||
| extern bool is_dc_planner(const struct divecomputer *dc); | ||||
| 
 | ||||
| /* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */ | ||||
| extern int match_one_dc(const struct divecomputer *a, const struct divecomputer *b); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  | @ -15,6 +15,7 @@ | |||
| #include "gettext.h" | ||||
| #include "git-access.h" | ||||
| #include "selection.h" | ||||
| #include "sample.h" | ||||
| #include "table.h" | ||||
| #include "trip.h" | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										11
									
								
								core/extradata.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								core/extradata.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| #ifndef EXTRADATA_H | ||||
| #define EXTRADATA_H | ||||
| 
 | ||||
| struct extra_data { | ||||
| 	const char *key; | ||||
| 	const char *value; | ||||
| 	struct extra_data *next; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -5,10 +5,12 @@ | |||
| #endif | ||||
| 
 | ||||
| #include "ssrf.h" | ||||
| #include "dive.h" | ||||
| #include "divesite.h" | ||||
| #include "gas.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "parse.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "divelist.h" | ||||
| #include "device.h" | ||||
| #include "membuffer.h" | ||||
|  |  | |||
|  | @ -2,13 +2,14 @@ | |||
| #include <errno.h> | ||||
| #include <libdivecomputer/parser.h> | ||||
| 
 | ||||
| #include "dive.h" | ||||
| #include "errorhelper.h" | ||||
| #include "ssrf.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "divelist.h" | ||||
| #include "file.h" | ||||
| #include "parse.h" | ||||
| #include "subsurface-time.h" | ||||
| #include "sample.h" | ||||
| #include "divelist.h" | ||||
| #include "gettext.h" | ||||
| #include "import-csv.h" | ||||
|  |  | |||
|  | @ -5,7 +5,9 @@ | |||
| #endif | ||||
| 
 | ||||
| #include "ssrf.h" | ||||
| #include "dive.h" | ||||
| #include "divesite.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "parse.h" | ||||
| #include "divelist.h" | ||||
|  |  | |||
|  | @ -7,8 +7,8 @@ | |||
| #include "qthelper.h" | ||||
| #include "ssrf.h" | ||||
| #include "dive.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "subsurface-time.h" | ||||
| #include "parse.h" | ||||
| #include "divelist.h" | ||||
| #include "device.h" | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| 
 | ||||
| #include "ssrf.h" | ||||
| #include "dive.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "parse.h" | ||||
| #include "divelist.h" | ||||
|  |  | |||
|  | @ -6,8 +6,9 @@ | |||
| 
 | ||||
| #include "ssrf.h" | ||||
| #include "dive.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "parse.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "divelist.h" | ||||
| #include "device.h" | ||||
| #include "membuffer.h" | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ | |||
| #include <fcntl.h> | ||||
| #include "gettext.h" | ||||
| #include "divesite.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "device.h" | ||||
| #include "dive.h" | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "divesite.h" | ||||
| #include "dive.h" | ||||
| #include "file.h" | ||||
| #include "sample.h" | ||||
| #include "strndup.h" | ||||
| 
 | ||||
| // Convert bytes into an INT
 | ||||
|  |  | |||
|  | @ -18,8 +18,9 @@ | |||
| #include "divesite.h" | ||||
| #include "event.h" | ||||
| #include "errorhelper.h" | ||||
| #include "trip.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "trip.h" | ||||
| #include "device.h" | ||||
| #include "membuffer.h" | ||||
| #include "git-access.h" | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include "subsurface-string.h" | ||||
| #include "gettext.h" | ||||
| #include "dive.h" | ||||
| #include "extradata.h" | ||||
| #include "file.h" | ||||
| #include "libdivecomputer.h" | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| 
 | ||||
| #include "gettext.h" | ||||
| 
 | ||||
| #include "dive.h" | ||||
| #include "divesite.h" | ||||
| #include "errorhelper.h" | ||||
| #include "subsurface-string.h" | ||||
|  | @ -31,6 +32,7 @@ | |||
| #include "membuffer.h" | ||||
| #include "picture.h" | ||||
| #include "qthelper.h" | ||||
| #include "sample.h" | ||||
| #include "tag.h" | ||||
| #include "xmlparams.h" | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,10 +7,12 @@ | |||
| #include <unistd.h> | ||||
| #include <libdivecomputer/parser.h> | ||||
| 
 | ||||
| #include "parse.h" | ||||
| #include "dive.h" | ||||
| #include "divesite.h" | ||||
| #include "errorhelper.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "parse.h" | ||||
| #include "picture.h" | ||||
| #include "trip.h" | ||||
| #include "device.h" | ||||
|  |  | |||
|  | @ -5,11 +5,13 @@ | |||
| #define MAX_EVENT_NAME 128 | ||||
| 
 | ||||
| #include "event.h" | ||||
| #include "picture.h" | ||||
| #include "dive.h" // for "struct extra_data" | ||||
| #include "equipment.h" // for cylinder_t | ||||
| #include "extradata.h" | ||||
| #include "filterpreset.h" | ||||
| #include "picture.h" | ||||
| 
 | ||||
| #include <sqlite3.h> | ||||
| #include <time.h> | ||||
| 
 | ||||
| struct xml_params; | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| #include "ssrf.h" | ||||
| #include "dive.h" | ||||
| #include "divelist.h" // for init_decompression() | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "deco.h" | ||||
| #include "errorhelper.h" | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| #include "display.h" | ||||
| #include "divelist.h" | ||||
| #include "event.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| 
 | ||||
| #include "profile.h" | ||||
|  |  | |||
							
								
								
									
										45
									
								
								core/sample.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								core/sample.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| 
 | ||||
| #include "sample.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * Adding a cylinder pressure sample field is not quite as trivial as it | ||||
|  * perhaps should be. | ||||
|  * | ||||
|  * We try to keep the same sensor index for the same sensor, so that even | ||||
|  * if the dive computer doesn't give pressure information for every sample, | ||||
|  * we don't move pressure information around between the different sensor | ||||
|  * indices. | ||||
|  * | ||||
|  * The "prepare_sample()" function will always copy the sensor indices | ||||
|  * from the previous sample, so the indices are pre-populated (but the | ||||
|  * pressures obviously are not) | ||||
|  */ | ||||
| void add_sample_pressure(struct sample *sample, int sensor, int mbar) | ||||
| { | ||||
| 	int idx; | ||||
| 
 | ||||
| 	if (!mbar) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Do we already have a slot for this sensor */ | ||||
| 	for (idx = 0; idx < MAX_SENSORS; idx++) { | ||||
| 		if (sensor != sample->sensor[idx]) | ||||
| 			continue; | ||||
| 		sample->pressure[idx].mbar = mbar; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Pick the first unused index if we couldn't reuse one */ | ||||
| 	for (idx = 0; idx < MAX_SENSORS; idx++) { | ||||
| 		if (sample->pressure[idx].mbar) | ||||
| 			continue; | ||||
| 		sample->sensor[idx] = sensor; | ||||
| 		sample->pressure[idx].mbar = mbar; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We do not have enough slots for the pressure samples. */ | ||||
| 	/* Should we warn the user about dropping pressure data? */ | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										41
									
								
								core/sample.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								core/sample.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| #ifndef SAMPLE_H | ||||
| #define SAMPLE_H | ||||
| 
 | ||||
| #include "units.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| #define MAX_SENSORS 2 | ||||
| struct sample                         // BASE TYPE BYTES  UNITS    RANGE               DESCRIPTION
 | ||||
| {                                     // --------- -----  -----    -----               -----------
 | ||||
| 	duration_t time;                  // int32_t    4  seconds  (0-34 yrs)             elapsed dive time up to this sample
 | ||||
| 	duration_t stoptime;              // int32_t    4  seconds  (0-34 yrs)             time duration of next deco stop
 | ||||
| 	duration_t ndl;                   // int32_t    4  seconds  (-1 no val, 0-34 yrs)  time duration before no-deco limit
 | ||||
| 	duration_t tts;                   // int32_t    4  seconds  (0-34 yrs)             time duration to reach the surface
 | ||||
| 	duration_t rbt;                   // int32_t    4  seconds  (0-34 yrs)             remaining bottom time
 | ||||
| 	depth_t depth;                    // int32_t    4    mm     (0-2000 km)            dive depth of this sample
 | ||||
| 	depth_t stopdepth;                // int32_t    4    mm     (0-2000 km)            depth of next deco stop
 | ||||
| 	temperature_t temperature;        // uint32_t   4    mK     (0-4 MK)               ambient temperature
 | ||||
| 	pressure_t pressure[MAX_SENSORS]; // int32_t    4    mbar   (0-2 Mbar)             cylinder pressures (main and CCR o2)
 | ||||
| 	o2pressure_t setpoint;            // uint16_t   2    mbar   (0-65 bar)             O2 partial pressure (will be setpoint)
 | ||||
| 	o2pressure_t o2sensor[3];         // uint16_t   6    mbar   (0-65 bar)             Up to 3 PO2 sensor values (rebreather)
 | ||||
| 	bearing_t bearing;                // int16_t    2  degrees  (-1 no val, 0-360 deg) compass bearing
 | ||||
| 	uint8_t sensor[MAX_SENSORS];      // uint8_t    1  sensorID (0-255)                ID of cylinder pressure sensor
 | ||||
| 	uint16_t cns;                     // uint16_t   1     %     (0-64k %)              cns% accumulated
 | ||||
| 	uint8_t heartbeat;                // uint8_t    1  beats/m  (0-255)                heart rate measurement
 | ||||
| 	volume_t sac;                     //            4  ml/min                          predefined SAC
 | ||||
| 	bool in_deco;                     // bool       1    y/n      y/n                  this sample is part of deco
 | ||||
| 	bool manually_entered;            // bool       1    y/n      y/n                  this sample was entered by the user,
 | ||||
| 					  //                                               not calculated when planning a dive
 | ||||
| };	                                  // Total size of structure: 57 bytes, excluding padding at end
 | ||||
| 
 | ||||
| extern void add_sample_pressure(struct sample *sample, int sensor, int mbar); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  | @ -20,11 +20,13 @@ | |||
| #include "divesite.h" | ||||
| #include "filterconstraint.h" | ||||
| #include "filterpreset.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "trip.h" | ||||
| #include "device.h" | ||||
| #include "errorhelper.h" | ||||
| #include "event.h" | ||||
| #include "extradata.h" | ||||
| #include "membuffer.h" | ||||
| #include "git-access.h" | ||||
| #include "version.h" | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "event.h" | ||||
| #include "file.h" | ||||
| #include "picture.h" | ||||
| #include "sample.h" | ||||
| #include "tag.h" | ||||
| #include "subsurface-time.h" | ||||
| #include "trip.h" | ||||
|  |  | |||
|  | @ -15,8 +15,10 @@ | |||
| #include "dive.h" | ||||
| #include "divesite.h" | ||||
| #include "errorhelper.h" | ||||
| #include "extradata.h" | ||||
| #include "filterconstraint.h" | ||||
| #include "filterpreset.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-string.h" | ||||
| #include "subsurface-time.h" | ||||
| #include "trip.h" | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "dive.h" | ||||
| #include "display.h" | ||||
| #include "event.h" | ||||
| #include "sample.h" | ||||
| #include "subsurface-time.h" | ||||
| #include "trip.h" | ||||
| #include "statistics.h" | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ | |||
| 
 | ||||
| #include "uemis.h" | ||||
| #include "divesite.h" | ||||
| #include "sample.h" | ||||
| #include <libdivecomputer/parser.h> | ||||
| #include <libdivecomputer/version.h> | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,6 +55,7 @@ SOURCES += ../../subsurface-mobile-main.cpp \ | |||
| 	../../core/profile.c \ | ||||
| 	../../core/device.cpp \ | ||||
| 	../../core/dive.c \ | ||||
| 	../../core/divecomputer.c \ | ||||
| 	../../core/divefilter.cpp \ | ||||
| 	../../core/event.c \ | ||||
| 	../../core/filterconstraint.cpp \ | ||||
|  | @ -69,6 +70,7 @@ SOURCES += ../../subsurface-mobile-main.cpp \ | |||
| 	../../core/parse.c \ | ||||
| 	../../core/picture.c \ | ||||
| 	../../core/pictureobj.cpp \ | ||||
| 	../../core/sample.c \ | ||||
| 	../../core/import-suunto.c \ | ||||
| 	../../core/import-shearwater.c \ | ||||
| 	../../core/import-seac.c \ | ||||
|  | @ -205,7 +207,9 @@ HEADERS += \ | |||
| 	../../core/device.h \ | ||||
| 	../../core/devicedetails.h \ | ||||
| 	../../core/dive.h \ | ||||
| 	../../core/divecomputer.h \ | ||||
| 	../../core/event.h \ | ||||
| 	../../core/extradata.h \ | ||||
| 	../../core/git-access.h \ | ||||
| 	../../core/gpslocation.h \ | ||||
| 	../../core/imagedownloader.h \ | ||||
|  | @ -242,6 +246,7 @@ HEADERS += \ | |||
| 	../../core/membuffer.h \ | ||||
| 	../../core/metrics.h \ | ||||
| 	../../core/qt-gui.h \ | ||||
| 	../../core/sample.h \ | ||||
| 	../../core/selection.h \ | ||||
| 	../../core/sha1.h \ | ||||
| 	../../core/strndup.h \ | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include "core/gettextfromc.h" | ||||
| #include "core/metrics.h" | ||||
| #include "core/membuffer.h" | ||||
| #include "core/sample.h" | ||||
| #include "core/subsurface-string.h" | ||||
| 
 | ||||
| #define DEPTH_NOT_FOUND (-2342) | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| #include "qt-models/divecomputerextradatamodel.h" | ||||
| #include "core/dive.h" | ||||
| #include "core/divecomputer.h" | ||||
| #include "core/extradata.h" | ||||
| #include "core/metrics.h" | ||||
| 
 | ||||
| 
 | ||||
| ExtraDataModel::ExtraDataModel(QObject *parent) : CleanerTableModel(parent) | ||||
| { | ||||
| 	//enum Column {KEY, VALUE};
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include "qt-models/models.h" | ||||
| #include "core/device.h" | ||||
| #include "core/qthelper.h" | ||||
| #include "core/sample.h" | ||||
| #include "core/settings/qPrefDivePlanner.h" | ||||
| #include "core/settings/qPrefUnit.h" | ||||
| #if !defined(SUBSURFACE_TESTING) | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| #include "testparse.h" | ||||
| #include "core/device.h" | ||||
| #include "core/dive.h" | ||||
| #include "core/divesite.h" | ||||
| #include "core/errorhelper.h" | ||||
| #include "core/trip.h" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue