Start sanitizing gaschange event information

Decode the gasmix data into a sane format when creating the event, and
add the (currently unused) ability to specify a gas change to a
particular cylinder rather than (or in addition to) the gasmix.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Linus Torvalds 2014-08-17 12:26:21 -06:00 committed by Dirk Hohndel
parent b47e0658cf
commit df4e26c875
8 changed files with 234 additions and 97 deletions

68
dive.c
View file

@ -26,7 +26,31 @@ static const char *default_tags[] = {
QT_TRANSLATE_NOOP("gettextFromC", "deco")
};
void add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name)
int event_is_gaschange(struct event *ev)
{
return ev->type == SAMPLE_EVENT_GASCHANGE ||
ev->type == SAMPLE_EVENT_GASCHANGE2;
}
/*
* Does the gas mix data match the legacy
* libdivecomputer event format? If so,
* we can skip saving it, in order to maintain
* the old save formats. We'll re-generate the
* gas mix when loading.
*/
int event_gasmix_redundant(struct event *ev)
{
int value = ev->value;
int o2, he;
o2 = (value & 0xffff) * 10;
he = (value >> 16) * 10;
return o2 == ev->gas.mix.o2.permille &&
he == ev->gas.mix.he.permille;
}
struct event *add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name)
{
struct event *ev, **p;
unsigned int size, len = strlen(name);
@ -34,7 +58,7 @@ void add_event(struct divecomputer *dc, int time, int type, int flags, int value
size = sizeof(*ev) + len + 1;
ev = malloc(size);
if (!ev)
return;
return NULL;
memset(ev, 0, size);
memcpy(ev->name, name, len);
ev->time.seconds = time;
@ -42,6 +66,22 @@ void add_event(struct divecomputer *dc, int time, int type, int flags, int value
ev->flags = flags;
ev->value = value;
/*
* Expand the events into a sane format. Currently
* just gas switches
*/
switch (type) {
case SAMPLE_EVENT_GASCHANGE2:
/* High 16 bits are He percentage */
ev->gas.mix.he.permille = (value >> 16) * 10;
/* Fallthrough */
case SAMPLE_EVENT_GASCHANGE:
/* Low 16 bits are O2 percentage */
ev->gas.mix.o2.permille = (value & 0xffff) * 10;
ev->gas.index = -1;
break;
}
p = &dc->events;
/* insert in the sorted list of events */
@ -50,6 +90,7 @@ void add_event(struct divecomputer *dc, int time, int type, int flags, int value
ev->next = *p;
*p = ev;
remember_event(name);
return ev;
}
static int same_event(struct event *a, struct event *b)
@ -107,14 +148,11 @@ void update_event_name(struct dive *d, struct event *event, char *name)
/* this returns a pointer to static variable - so use it right away after calling */
struct gasmix *get_gasmix_from_event(struct event *ev)
{
static struct gasmix g;
g.o2.permille = g.he.permille = 0;
if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE || ev->type == SAMPLE_EVENT_GASCHANGE2)) {
g.o2.permille = 10 * ev->value & 0xffff;
if (ev->type == SAMPLE_EVENT_GASCHANGE2)
g.he.permille = 10 * (ev->value >> 16);
}
return &g;
static struct gasmix dummy;
if (ev && event_is_gaschange(ev))
return &ev->gas.mix;
return &dummy;
}
int get_pressure_units(int mb, const char **units)
@ -1543,6 +1581,7 @@ static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc)
static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, int mapping[])
{
int i;
struct event *ev;
/* Did the first gas get remapped? Add gas switch event */
if (mapping[0] > 0)
@ -1559,6 +1598,15 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, int
if (sensor >= 0)
s->sensor = sensor;
}
/* Remap the gas change indexes */
for (ev = dc->events; ev; ev = ev->next) {
if (!event_is_gaschange(ev))
continue;
if (ev->gas.index < 0)
continue;
ev->gas.index = mapping[ev->gas.index];
}
}
/*

22
dive.h
View file

@ -85,11 +85,29 @@ typedef struct
struct event {
struct event *next;
duration_t time;
int type, flags, value;
int type;
/* This is the annoying libdivecomputer format. */
int flags, value;
/* .. and this is our "extended" data for some event types */
union {
/*
* Currently only for gas switch events.
*
* NOTE! The index may be -1, which means "unknown". In that
* case, the get_cylinder_index() function will give the best
* match with the cylinders in the dive based on gasmix.
*/
struct {
int index;
struct gasmix mix;
} gas;
};
bool deleted;
char name[];
};
extern int event_is_gaschange(struct event *ev);
extern int event_gasmix_redundant(struct event *ev);
extern int get_pressure_units(int mb, const char **units);
extern double get_depth_units(int mm, int *frac, const char **units);
@ -651,7 +669,7 @@ extern void copy_samples(struct divecomputer *s, struct divecomputer *d);
extern bool is_cylinder_used(struct dive *dive, int idx);
extern void fill_default_cylinder(cylinder_t *cyl);
extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx);
extern void add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name);
extern struct event *add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name);
extern void remove_event(struct event *event);
extern void update_event_name(struct dive *d, struct event* event, char *name);
extern void per_cylinder_mean_depth(struct dive *dive, struct divecomputer *dc, int *mean, int *duration);

View file

@ -529,6 +529,13 @@ static void parse_event_keyvalue(void *_event, const char *key, const char *valu
event->value = val;
} else if (!strcmp(key, "name")) {
/* We get the name from the string handling */
} else if (!strcmp(key, "cylinder")) {
/* NOTE! We add one here as a marker that "yes, we got a cylinder index" */
event->gas.index = 1+get_index(value);
} else if (!strcmp(key, "o2")) {
event->gas.mix.o2 = get_fraction(value);
} else if (!strcmp(key, "he")) {
event->gas.mix.he = get_fraction(value);
} else
report_error("Unexpected event key/value pair (%s/%s)", key, value);
}
@ -538,7 +545,7 @@ static void parse_dc_event(char *line, struct membuffer *str, void *_dc)
int m, s = 0;
const char *name;
struct divecomputer *dc = _dc;
struct event event = { 0 };
struct event event = { 0 }, *ev;
m = strtol(line, &line, 10);
if (*line == ':')
@ -557,7 +564,17 @@ static void parse_dc_event(char *line, struct membuffer *str, void *_dc)
name = "";
if (str->len)
name = mb_cstring(str);
add_event(dc, event.time.seconds, event.type, event.flags, event.value, name);
ev = add_event(dc, event.time.seconds, event.type, event.flags, event.value, name);
if (ev && event_is_gaschange(ev)) {
/*
* We subtract one here because "0" is "no index",
* and the parsing will add one for actual cylinder
* index data (see parse_event_keyvalue)
*/
ev->gas.index = event.gas.index-1;
if (event.gas.mix.o2.permille || event.gas.mix.he.permille)
ev->gas.mix = event.gas.mix;
}
}
static void parse_trip_date(char *line, struct membuffer *str, void *_trip)

View file

@ -95,17 +95,17 @@ const struct units IMPERIAL_units = IMPERIAL_UNITS;
/*
* Dive info as it is being built up..
*/
#define MAX_EVENT_NAME 128
static struct divecomputer *cur_dc;
static struct dive *cur_dive;
static dive_trip_t *cur_trip = NULL;
static struct sample *cur_sample;
static struct picture *cur_picture;
static struct {
int active;
duration_t time;
int type, flags, value;
const char *name;
} cur_event;
static union {
struct event event;
char allocation[sizeof(struct event)+MAX_EVENT_NAME];
} event_allocation = { .event.deleted = 1 };
#define cur_event event_allocation.event
static struct {
struct {
const char *model;
@ -527,6 +527,15 @@ static void utf8_string(char *buffer, void *_res)
*res = strdup(buffer);
}
static void event_name(char *buffer, char *name)
{
int size = trimspace(buffer);
if (size >= MAX_EVENT_NAME)
size = MAX_EVENT_NAME-1;
memcpy(name, buffer, size);
name[size] = 0;
}
/* Extract the dive computer type from the xml text buffer */
static void get_dc_type(char *buffer, enum dive_comp_type *i)
{
@ -704,16 +713,22 @@ static void try_to_match_autogroup(const char *name, char *buf)
void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx)
{
/* The gas switch event format is insane. It will be fixed, I think */
int o2 = get_o2(&dive->cylinder[idx].gasmix);
int he = get_he(&dive->cylinder[idx].gasmix);
/* The gas switch event format is insane for historical reasons */
struct gasmix *mix = &dive->cylinder[idx].gasmix;
int o2 = get_o2(mix);
int he = get_he(mix);
struct event *ev;
int value;
o2 = (o2 + 5) / 10;
he = (he + 5) / 10;
value = o2 + (he << 16);
add_event(dc, seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange");
ev = add_event(dc, seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange");
if (ev) {
ev->gas.index = idx;
ev->gas.mix = *mix;
}
}
static void get_cylinderindex(char *buffer, uint8_t *i)
@ -751,9 +766,9 @@ static void try_to_fill_dc_settings(const char *name, char *buf)
static void try_to_fill_event(const char *name, char *buf)
{
start_match("event", name, buf);
if (MATCH("event", utf8_string, &cur_event.name))
if (MATCH("event", event_name, cur_event.name))
return;
if (MATCH("name", utf8_string, &cur_event.name))
if (MATCH("name", event_name, cur_event.name))
return;
if (MATCH("time", eventtime, &cur_event.time))
return;
@ -763,6 +778,15 @@ static void try_to_fill_event(const char *name, char *buf)
return;
if (MATCH("value", get_index, &cur_event.value))
return;
if (MATCH("cylinder", get_index, &cur_event.gas.index)) {
/* We add one to indicate that we got an actual cylinder index value */
cur_event.gas.index++;
return;
}
if (MATCH("o2", percent, &cur_event.gas.mix.o2))
return;
if (MATCH("he", percent, &cur_event.gas.mix.he))
return;
nonmatch("event", name, buf);
}
@ -1332,15 +1356,13 @@ static void trip_end(void)
static void event_start(void)
{
memset(&cur_event, 0, sizeof(cur_event));
cur_event.active = 1;
cur_event.deleted = 0; /* Active */
}
static void event_end(void)
{
struct divecomputer *dc = get_dc();
if (cur_event.name) {
if (strcmp(cur_event.name, "surface") != 0) {
/* 123 is a magic event that we used for a while to encode images in dives */
if (strcmp(cur_event.name, "surface") != 0) { /* 123 is a magic event that we used for a while to encode images in dives */
if (cur_event.type == 123) {
struct picture *pic = alloc_picture();
pic->filename = strdup(cur_event.name);
@ -1348,20 +1370,24 @@ static void event_end(void)
pic->offset.seconds = cur_event.time.seconds;
dive_add_picture(cur_dive, pic);
} else {
struct event *ev;
/* At some point gas change events did not have any type. Thus we need to add
* one on import, if we encounter the type one missing.
*/
if (cur_event.type == 0 && strcmp(cur_event.name, "gaschange") == 0)
cur_event.type = cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
add_event(dc, cur_event.time.seconds,
ev = add_event(dc, cur_event.time.seconds,
cur_event.type, cur_event.flags,
cur_event.value, cur_event.name);
if (ev && event_is_gaschange(ev)) {
/* See try_to_fill_event() on why the filled-in index is one too big */
ev->gas.index = cur_event.gas.index-1;
if (cur_event.gas.mix.o2.permille || cur_event.gas.mix.he.permille)
ev->gas.mix = cur_event.gas.mix;
}
}
free((void *)cur_event.name);
}
cur_event.active = 0;
cur_event.deleted = 1; /* No longer active */
}
static void picture_start(void)
@ -1472,7 +1498,7 @@ static void entry(const char *name, char *buf)
try_to_match_autogroup(name, buf);
return;
}
if (cur_event.active) {
if (!cur_event.deleted) {
try_to_fill_event(name, buf);
return;
}
@ -1722,71 +1748,71 @@ extern int dm4_events(void *handle, int columns, char **data, char **column)
switch (atoi(data[2])) {
case 1:
/* 1 Mandatory Safety Stop */
cur_event.name = strdup("safety stop (mandatory)");
strcpy(cur_event.name, "safety stop (mandatory)");
break;
case 3:
/* 3 Deco */
/* What is Subsurface's term for going to
* deco? */
cur_event.name = strdup("deco");
strcpy(cur_event.name, "deco");
break;
case 4:
/* 4 Ascent warning */
cur_event.name = strdup("ascent");
strcpy(cur_event.name, "ascent");
break;
case 5:
/* 5 Ceiling broken */
cur_event.name = strdup("violation");
strcpy(cur_event.name, "violation");
break;
case 6:
/* 6 Mandatory safety stop ceiling error */
cur_event.name = strdup("violation");
strcpy(cur_event.name, "violation");
break;
case 7:
/* 7 Below deco floor */
cur_event.name = strdup("below floor");
strcpy(cur_event.name, "below floor");
break;
case 8:
/* 8 Dive time alarm */
cur_event.name = strdup("divetime");
strcpy(cur_event.name, "divetime");
break;
case 9:
/* 9 Depth alarm */
cur_event.name = strdup("maxdepth");
strcpy(cur_event.name, "maxdepth");
break;
case 10:
/* 10 OLF 80% */
case 11:
/* 11 OLF 100% */
cur_event.name = strdup("OLF");
strcpy(cur_event.name, "OLF");
break;
case 12:
/* 12 High pO₂ */
cur_event.name = strdup("PO2");
strcpy(cur_event.name, "PO2");
break;
case 13:
/* 13 Air time */
cur_event.name = strdup("airtime");
strcpy(cur_event.name, "airtime");
break;
case 17:
/* 17 Ascent warning */
cur_event.name = strdup("ascent");
strcpy(cur_event.name, "ascent");
break;
case 18:
/* 18 Ceiling error */
cur_event.name = strdup("ceiling");
strcpy(cur_event.name, "ceiling");
break;
case 19:
/* 19 Surfaced */
cur_event.name = strdup("surface");
strcpy(cur_event.name, "surface");
break;
case 20:
/* 20 Deco */
cur_event.name = strdup("deco");
strcpy(cur_event.name, "deco");
break;
case 22:
/* 22 Mandatory safety stop violation */
cur_event.name = strdup("violation");
strcpy(cur_event.name, "violation");
break;
case 257:
/* 257 Dive active */
@ -1796,14 +1822,14 @@ extern int dm4_events(void *handle, int columns, char **data, char **column)
case 258:
/* 258 Bookmark */
if (data[3]) {
cur_event.name = strdup("heading");
strcpy(cur_event.name, "heading");
cur_event.value = atoi(data[3]);
} else {
cur_event.name = strdup("bookmark");
strcpy(cur_event.name, "bookmark");
}
break;
default:
cur_event.name = strdup("unknown");
strcpy(cur_event.name, "unknown");
cur_event.value = atoi(data[2]);
break;
}
@ -1986,7 +2012,7 @@ extern int shearwater_changes(void *handle, int columns, char **data, char **col
if (data[0])
cur_event.time.seconds = atoi(data[0]);
if (data[1]) {
cur_event.name = strdup("gaschange");
strcpy(cur_event.name, "gaschange");
cur_event.value = atof(data[1]) * 100;
}
event_end();

View file

@ -289,6 +289,14 @@ struct plot_info *analyze_plot_info(struct plot_info *pi)
return pi;
}
/*
* If the event has an explicit cylinder index,
* we return that. If it doesn't, we return the best
* match based on the gasmix.
*
* Some dive computers give cylinder indexes, some
* give just the gas mix.
*/
int get_cylinder_index(struct dive *dive, struct event *ev)
{
int i;
@ -296,10 +304,9 @@ int get_cylinder_index(struct dive *dive, struct event *ev)
int target_o2, target_he;
struct gasmix *g;
/*
* Crazy gas change events give us odd encoded o2/he in percent.
* Decode into our internal permille format.
*/
if (ev->gas.index >= 0)
return ev->gas.index;
g = get_gasmix_from_event(ev);
target_o2 = get_o2(g);
target_he = get_he(g);
@ -318,13 +325,8 @@ int get_cylinder_index(struct dive *dive, struct event *ev)
delta_o2 = get_o2(&cyl->gasmix) - target_o2;
delta_he = get_he(&cyl->gasmix) - target_he;
distance = delta_o2 * delta_o2;
/* Check the event type to figure out if we should care about the he part.
* SAMPLE_EVENT_GASCHANGE, aka without he
* SAMPLE_EVENT_GASCHANGE2, aka with he
*/
if (ev->type == SAMPLE_EVENT_GASCHANGE2)
distance += delta_he * delta_he;
if (distance >= score)
continue;
score = distance;

View file

@ -66,10 +66,10 @@ void DiveEventItem::setupPixmap()
setPixmap(EVENT_PIXMAP(":flag"));
} else if (strcmp(internalEvent->name, "heading") == 0) {
setPixmap(EVENT_PIXMAP(":flag"));
} else if (internalEvent->type == SAMPLE_EVENT_GASCHANGE || internalEvent->type == SAMPLE_EVENT_GASCHANGE2) {
if (internalEvent->value >> 16)
} else if (event_is_gaschange(internalEvent)) {
if (internalEvent->gas.mix.he.permille)
setPixmap(EVENT_PIXMAP_BIGGER(":gaschangeTrimix"));
else if (internalEvent->value == 0)
else if (gasmix_is_air(&internalEvent->gas.mix))
setPixmap(EVENT_PIXMAP_BIGGER(":gaschangeAir"));
else
setPixmap(EVENT_PIXMAP_BIGGER(":gaschangeNitrox"));
@ -86,7 +86,7 @@ void DiveEventItem::setupToolTipString()
int value = internalEvent->value;
int type = internalEvent->type;
if (value) {
if (type == SAMPLE_EVENT_GASCHANGE || type == SAMPLE_EVENT_GASCHANGE2) {
if (event_is_gaschange(internalEvent)) {
QModelIndexList result = dataModel->match(dataModel->index(0, DivePlotDataModel::TIME), Qt::DisplayRole, internalEvent->time.seconds);
if (result.isEmpty()) {
Q_ASSERT("can't find a spot in the dataModel");

View file

@ -119,6 +119,18 @@ static void save_tags(struct membuffer *b, struct tag_entry *tags)
put_string(b, "\n");
}
static void put_gasmix(struct membuffer *b, struct gasmix *mix)
{
int o2 = mix->o2.permille;
int he = mix->he.permille;
if (o2) {
put_format(b, " o2=%u.%u%%", FRACTION(o2, 10));
if (he)
put_format(b, " he=%u.%u%%", FRACTION(he, 10));
}
}
static void save_cylinder_info(struct membuffer *b, struct dive *dive)
{
int i, nr;
@ -128,8 +140,6 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive)
cylinder_t *cylinder = dive->cylinder + i;
int volume = cylinder->type.size.mliter;
const char *description = cylinder->type.description;
int o2 = cylinder->gasmix.o2.permille;
int he = cylinder->gasmix.he.permille;
put_string(b, "cylinder");
if (volume)
@ -137,11 +147,7 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive)
put_pressure(b, cylinder->type.workingpressure, " workpressure=", "bar");
show_utf8(b, " description=", description, "");
strip_mb(b);
if (o2) {
put_format(b, " o2=%u.%u%%", FRACTION(o2, 10));
if (he)
put_format(b, " he=%u.%u%%", FRACTION(he, 10));
}
put_gasmix(b, &cylinder->gasmix);
put_pressure(b, cylinder->start, " start=", "bar");
put_pressure(b, cylinder->end, " end=", "bar");
put_string(b, "\n");
@ -292,6 +298,13 @@ static void save_one_event(struct membuffer *b, struct event *ev)
show_index(b, ev->flags, "flags=", "");
show_index(b, ev->value, "value=", "");
show_utf8(b, " name=", ev->name, "");
if (event_is_gaschange(ev)) {
if (ev->gas.index >= 0) {
show_index(b, ev->gas.index, "cylinder=", "");
put_gasmix(b, &ev->gas.mix);
} else if (!event_gasmix_redundant(ev))
put_gasmix(b, &ev->gas.mix);
}
put_string(b, "\n");
}

View file

@ -148,6 +148,18 @@ static void save_overview(struct membuffer *b, struct dive *dive)
show_utf8(b, dive->suit, " <suit>", "</suit>\n", 0);
}
static void put_gasmix(struct membuffer *b, struct gasmix *mix)
{
int o2 = mix->o2.permille;
int he = mix->he.permille;
if (o2) {
put_format(b, " o2='%u.%u%%'", FRACTION(o2, 10));
if (he)
put_format(b, " he='%u.%u%%'", FRACTION(he, 10));
}
}
static void save_cylinder_info(struct membuffer *b, struct dive *dive)
{
int i, nr;
@ -158,19 +170,13 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive)
cylinder_t *cylinder = dive->cylinder + i;
int volume = cylinder->type.size.mliter;
const char *description = cylinder->type.description;
int o2 = cylinder->gasmix.o2.permille;
int he = cylinder->gasmix.he.permille;
put_format(b, " <cylinder");
if (volume)
put_milli(b, " size='", volume, " l'");
put_pressure(b, cylinder->type.workingpressure, " workpressure='", " bar'");
show_utf8(b, description, " description='", "'", 1);
if (o2) {
put_format(b, " o2='%u.%u%%'", FRACTION(o2, 10));
if (he)
put_format(b, " he='%u.%u%%'", FRACTION(he, 10));
}
put_gasmix(b, &cylinder->gasmix);
put_pressure(b, cylinder->start, " start='", " bar'");
put_pressure(b, cylinder->end, " end='", " bar'");
put_format(b, " />\n");
@ -261,6 +267,13 @@ static void save_one_event(struct membuffer *b, struct event *ev)
show_index(b, ev->flags, "flags='", "'");
show_index(b, ev->value, "value='", "'");
show_utf8(b, ev->name, " name='", "'", 1);
if (event_is_gaschange(ev)) {
if (ev->gas.index >= 0) {
show_index(b, ev->gas.index, "cylinder='", "'");
put_gasmix(b, &ev->gas.mix);
} else if (!event_gasmix_redundant(ev))
put_gasmix(b, &ev->gas.mix);
}
put_format(b, " />\n");
}