diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index 19fbf99..64a01ca 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -34,27 +35,30 @@ enum eon_sample { ES_none = 0, - ES_dtime, // duint16,precision=3 (time delta in ms) - ES_depth, // uint16,precision=2,nillable=65535 (depth in cm) - ES_temp, // int16,precision=2,nillable=-3000 (temp in deci-Celsius) - ES_ndl, // int16,nillable=-1 (ndl in minutes) - ES_ceiling, // uint16,precision=2,nillable=65535 (ceiling in cm) - ES_tts, // uint16,nillable=65535 (time to surface) - ES_heading, // uint16,precision=4,nillable=65535 (heading in degrees) - ES_abspressure, // uint16,precision=0,nillable=65535 (abs presure in centibar) - ES_gastime, // int16,nillable=-1 (remaining gas time in minutes) - ES_ventilation, // uint16,precision=6,nillable=65535 ("x/6000000,x"? No idea) - ES_gasnr, // uint8 - ES_pressure, // uint16,nillable=65535 (cylinder pressure in centibar) - ES_state, - ES_state_active, - ES_notify, - ES_notify_active, - ES_warning, - ES_warning_active, + ES_dtime, // duint16,precision=3 (time delta in ms) + ES_depth, // uint16,precision=2,nillable=65535 (depth in cm) + ES_temp, // int16,precision=2,nillable=-3000 (temp in deci-Celsius) + ES_ndl, // int16,nillable=-1 (ndl in minutes) + ES_ceiling, // uint16,precision=2,nillable=65535 (ceiling in cm) + ES_tts, // uint16,nillable=65535 (time to surface) + ES_heading, // uint16,precision=4,nillable=65535 (heading in degrees) + ES_abspressure, // uint16,precision=0,nillable=65535 (abs presure in centibar) + ES_gastime, // int16,nillable=-1 (remaining gas time in minutes) + ES_ventilation, // uint16,precision=6,nillable=65535 ("x/6000000,x"? No idea) + ES_gasnr, // uint8 + ES_pressure, // uint16,nillable=65535 (cylinder pressure in centibar) + ES_state, // enum:0=Wet Outside,1=Below Wet Activation Depth,2=Below Surface,3=Dive Active,4=Surface Calculation,5=Tank pressure available,6=Closed Circuit Mode + ES_state_active, // bool + ES_notify, // enum:0=NoFly Time,1=Depth,2=Surface Time,3=Tissue Level,4=Deco,5=Deco Window,6=Safety Stop Ahead,7=Safety Stop,8=Safety Stop Broken,9=Deep Stop Ahead,10=Deep Stop,11=Dive Time,12=Gas Available,13=SetPoint Switch,14=Diluent Hypoxia,15=Air Time,16=Tank Pressure + ES_notify_active, // bool + ES_warning, // enum:0=ICD Penalty,1=Deep Stop Penalty,2=Mandatory Safety Stop,3=OTU250,4=OTU300,5=CNS80%,6=CNS100%,7=Max.Depth,8=Air Time,9=Tank Pressure,10=Safety Stop Broken,11=Deep Stop Broken,12=Ceiling Broken,13=PO2 High + ES_warning_active, // bool ES_alarm, ES_alarm_active, - ES_gasswitch, // uint16 + ES_gasswitch, // uint16 + ES_setpoint_type, // enum:0=Low,1=High,2=Custom + ES_setpoint_po2, // uint32 + ES_setpoint_automatic, // bool ES_bookmark, }; @@ -82,15 +86,28 @@ typedef struct suunto_eonsteel_parser_t { dc_gasmix_t gasmix[MAXGASES]; dc_salinity_t salinity; double surface_pressure; + dc_divemode_t divemode; + double lowsetpoint; + double highsetpoint; + double customsetpoint; + dc_tankvolume_t tankinfo[MAXGASES]; + double tanksize[MAXGASES]; + double tankworkingpressure[MAXGASES]; } cache; } suunto_eonsteel_parser_t; typedef int (*eon_data_cb_t)(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user); +typedef struct eon_event_t { + const char *name; + parser_sample_event_t type; +} eon_event_t; + static const struct { const char *name; enum eon_sample type; } type_translation[] = { + { "+Time", ES_dtime }, { "Depth", ES_depth }, { "Temperature", ES_temp }, { "NoDecTime", ES_ndl }, @@ -112,6 +129,9 @@ static const struct { { "Events.Alarm.Active", ES_alarm_active }, { "Events.Bookmark.Name", ES_bookmark }, { "Events.GasSwitch.GasNumber", ES_gasswitch }, + { "Events.SetPoint.Type", ES_setpoint_type }, + { "Events.Events.SetPoint.PO2", ES_setpoint_po2 }, + { "Events.SetPoint.Automatic", ES_setpoint_automatic }, { "Events.DiveTimer.Active", ES_none }, { "Events.DiveTimer.Time", ES_none }, }; @@ -148,6 +168,26 @@ static enum eon_sample lookup_descriptor_type(suunto_eonsteel_parser_t *eon, str return ES_none; } +static parser_sample_event_t lookup_event(const char *name, const eon_event_t events[], size_t n) +{ + for (size_t i = 0; i < n; ++i) { + if (!strcasecmp(name, events[i].name)) + return events[i].type; + } + + return SAMPLE_EVENT_NONE; +} + +static const char *desc_type_name(enum eon_sample type) +{ + int i; + for (i = 0; i < C_ARRAY_SIZE(type_translation); i++) { + if (type == type_translation[i].type) + return type_translation[i].name; + } + return "Unknown"; +} + static int lookup_descriptor_size(suunto_eonsteel_parser_t *eon, struct type_desc *desc) { const char *format = desc->format; @@ -424,8 +464,8 @@ struct sample_data { dc_sample_callback_t callback; void *userdata; unsigned int time; - unsigned char state_type, notify_type; - unsigned char warning_type, alarm_type; + const char *state_type, *notify_type; + const char *warning_type, *alarm_type; /* We gather up deco and cylinder pressure information */ int gasnr; @@ -575,154 +615,256 @@ static void sample_gas_switch_event(struct sample_data *info, unsigned short idx #endif } +/* + * Look up the string from an enumeration. + * + * Enumerations have the enum values in the "format" string, + * and all start with "enum:" followed by a comma-separated list + * of enumeration values and strings. Example: + * + * "enum:0=NoFly Time,1=Depth,2=Surface Time,3=..." + */ +static const char *lookup_enum(const struct type_desc *desc, unsigned char value) +{ + const char *str = desc->format; + unsigned char c; + + if (!str) + return NULL; + if (strncmp(str, "enum:", 5)) + return NULL; + str += 5; + + while ((c = *str) != 0) { + unsigned char n; + const char *begin, *end; + char *ret; + + str++; + if (!isdigit(c)) + continue; + n = c - '0'; + + // We only handle one or two digits + if (isdigit(*str)) { + n = n*10 + *str - '0'; + str++; + } + + begin = end = str; + while ((c = *str) != 0) { + str++; + if (c == ',') + break; + end = str; + } + + // Verify that it has the 'n=string' format and skip the equals sign + if (*begin != '=') + continue; + begin++; + + // Is it the value we're looking for? + if (n != value) + continue; + + ret = malloc(end - begin + 1); + if (!ret) + break; + + memcpy(ret, begin, end-begin); + ret[end-begin] = 0; + return ret; + } + return NULL; +} + /* * The EON Steel has four different sample events: "state", "notification", * "warning" and "alarm". All end up having two fields: type and a boolean value. - * - * The type enumerations are available as part of the type descriptor, and we - * *should* probably parse them dynamically, but this hardcodes the different - * type values. - * - * For event states, the types are: - * - * 0=Wet Outside - * 1=Below Wet Activation Depth - * 2=Below Surface - * 3=Dive Active - * 4=Surface Calculation - * 5=Tank pressure available - * - * FIXME! This needs to parse the actual type descriptor enum */ -static void sample_event_state_type(struct sample_data *info, unsigned char type) +static void sample_event_state_type(const struct type_desc *desc, struct sample_data *info, unsigned char type) { - info->state_type = type; + info->state_type = lookup_enum(desc, type); } -static void sample_event_state_value(struct sample_data *info, unsigned char value) -{ - /* - * We could turn these into sample events, but they don't actually - * match any libdivecomputer events. - * - * unsigned int state = info->state_type; - * dc_sample_value_t sample = {0}; - * sample.event.type = ... - * sample.event.value = value; - * if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata); - */ -} - -static void sample_event_notify_type(struct sample_data *info, unsigned char type) -{ - info->notify_type = type; -} - - -// FIXME! This needs to parse the actual type descriptor enum -static void sample_event_notify_value(struct sample_data *info, unsigned char value) +static void sample_event_state_value(const struct type_desc *desc, struct sample_data *info, unsigned char value) { dc_sample_value_t sample = {0}; - static const enum parser_sample_event_t translate_notification[] = { - SAMPLE_EVENT_NONE, // 0=NoFly Time - SAMPLE_EVENT_NONE, // 1=Depth - SAMPLE_EVENT_NONE, // 2=Surface Time - SAMPLE_EVENT_TISSUELEVEL, // 3=Tissue Level - SAMPLE_EVENT_NONE, // 4=Deco - SAMPLE_EVENT_NONE, // 5=Deco Window - SAMPLE_EVENT_SAFETYSTOP_VOLUNTARY, // 6=Safety Stop Ahead - SAMPLE_EVENT_SAFETYSTOP, // 7=Safety Stop - SAMPLE_EVENT_CEILING_SAFETYSTOP, // 8=Safety Stop Broken - SAMPLE_EVENT_NONE, // 9=Deep Stop Ahead - SAMPLE_EVENT_DEEPSTOP, // 10=Deep Stop - SAMPLE_EVENT_DIVETIME, // 11=Dive Time - SAMPLE_EVENT_NONE, // 12=Gas Available - SAMPLE_EVENT_NONE, // 13=SetPoint Switch - SAMPLE_EVENT_NONE, // 14=Diluent Hypoxia - SAMPLE_EVENT_NONE, // 15=Tank Pressure + static const eon_event_t states[] = { + {"Wet Outside", SAMPLE_EVENT_NONE}, + {"Below Wet Activation Depth", SAMPLE_EVENT_NONE}, + {"Below Surface", SAMPLE_EVENT_NONE}, + {"Dive Active", SAMPLE_EVENT_NONE}, + {"Surface Calculation", SAMPLE_EVENT_NONE}, + {"Tank pressure available", SAMPLE_EVENT_NONE}, + {"Closed Circuit Mode", SAMPLE_EVENT_NONE}, }; + const char *name; - if (info->notify_type > 15) + name = info->state_type; + if (!name) return; - sample.event.type = translate_notification[info->notify_type]; + sample.event.type = lookup_event(name, states, C_ARRAY_SIZE(states)); if (sample.event.type == SAMPLE_EVENT_NONE) return; - sample.event.value = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END; + sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END; + if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata); +} + +static void sample_event_notify_type(const struct type_desc *desc, struct sample_data *info, unsigned char type) +{ + info->notify_type = lookup_enum(desc, type); +} + +static void sample_event_notify_value(const struct type_desc *desc, struct sample_data *info, unsigned char value) +{ + static const eon_event_t notifications[] = { + {"NoFly Time", SAMPLE_EVENT_NONE}, + {"Depth", SAMPLE_EVENT_NONE}, + {"Surface Time", SAMPLE_EVENT_NONE}, + {"Tissue Level", SAMPLE_EVENT_TISSUELEVEL}, + {"Deco", SAMPLE_EVENT_NONE}, + {"Deco Window", SAMPLE_EVENT_NONE}, + {"Safety Stop Ahead", SAMPLE_EVENT_NONE}, + {"Safety Stop", SAMPLE_EVENT_SAFETYSTOP}, + {"Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP}, + {"Deep Stop Ahead", SAMPLE_EVENT_NONE}, + {"Deep Stop", SAMPLE_EVENT_DEEPSTOP}, + {"Dive Time", SAMPLE_EVENT_DIVETIME}, + {"Gas Available", SAMPLE_EVENT_NONE}, + {"SetPoint Switch", SAMPLE_EVENT_NONE}, + {"Diluent Hypoxia", SAMPLE_EVENT_NONE}, + {"Air Time", SAMPLE_EVENT_NONE}, + {"Tank Pressure", SAMPLE_EVENT_NONE}, + }; + dc_sample_value_t sample = {0}; + const char *name; + + name = info->notify_type; + if (!name) + return; + + sample.event.type = lookup_event(name, notifications, C_ARRAY_SIZE(notifications)); + if (sample.event.type == SAMPLE_EVENT_NONE) + return; + + sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END; if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata); } -static void sample_event_warning_type(struct sample_data *info, unsigned char type) +static void sample_event_warning_type(const struct type_desc *desc, struct sample_data *info, unsigned char type) { - info->warning_type = type; + info->warning_type = lookup_enum(desc, type); } - -static void sample_event_warning_value(struct sample_data *info, unsigned char value) +static void sample_event_warning_value(const struct type_desc *desc, struct sample_data *info, unsigned char value) { - dc_sample_value_t sample = {0}; - static const enum parser_sample_event_t translate_warning[] = { - SAMPLE_EVENT_NONE, // 0=ICD Penalty ("Isobaric counterdiffusion") - SAMPLE_EVENT_VIOLATION, // 1=Deep Stop Penalty - SAMPLE_EVENT_SAFETYSTOP_MANDATORY, // 2=Mandatory Safety Stop - SAMPLE_EVENT_NONE, // 3=OTU250 - SAMPLE_EVENT_NONE, // 4=OTU300 - SAMPLE_EVENT_NONE, // 5=CNS80% - SAMPLE_EVENT_NONE, // 6=CNS100% - SAMPLE_EVENT_AIRTIME, // 7=Air Time - SAMPLE_EVENT_MAXDEPTH, // 8=Max.Depth - SAMPLE_EVENT_AIRTIME, // 9=Tank Pressure - SAMPLE_EVENT_CEILING_SAFETYSTOP, // 10=Safety Stop Broken - SAMPLE_EVENT_CEILING_SAFETYSTOP, // 11=Deep Stop Broken - SAMPLE_EVENT_CEILING, // 12=Ceiling Broken - SAMPLE_EVENT_PO2, // 13=PO2 High + static const eon_event_t warnings[] = { + {"ICD Penalty", SAMPLE_EVENT_NONE}, + {"Deep Stop Penalty", SAMPLE_EVENT_VIOLATION}, + {"Mandatory Safety Stop", SAMPLE_EVENT_SAFETYSTOP_MANDATORY}, + {"OTU250", SAMPLE_EVENT_NONE}, + {"OTU300", SAMPLE_EVENT_NONE}, + {"CNS80%", SAMPLE_EVENT_NONE}, + {"CNS100%", SAMPLE_EVENT_NONE}, + {"Max.Depth", SAMPLE_EVENT_MAXDEPTH}, + {"Air Time", SAMPLE_EVENT_AIRTIME}, + {"Tank Pressure", SAMPLE_EVENT_NONE}, + {"Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP}, + {"Deep Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP}, + {"Ceiling Broken", SAMPLE_EVENT_CEILING}, + {"PO2 High", SAMPLE_EVENT_PO2}, }; + dc_sample_value_t sample = {0}; + const char *name; - if (info->warning_type > 13) + name = info->warning_type; + if (!name) return; - sample.event.type = translate_warning[info->warning_type]; + sample.event.type = lookup_event(name, warnings, C_ARRAY_SIZE(warnings)); if (sample.event.type == SAMPLE_EVENT_NONE) return; - sample.event.value = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END; + sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END; if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata); } -static void sample_event_alarm_type(struct sample_data *info, unsigned char type) +static void sample_event_alarm_type(const struct type_desc *desc, struct sample_data *info, unsigned char type) { - info->alarm_type = type; + info->alarm_type = lookup_enum(desc, type); } -// FIXME! This needs to parse the actual type descriptor enum -static void sample_event_alarm_value(struct sample_data *info, unsigned char value) +static void sample_event_alarm_value(const struct type_desc *desc, struct sample_data *info, unsigned char value) { - dc_sample_value_t sample = {0}; - static const enum parser_sample_event_t translate_alarm[] = { - SAMPLE_EVENT_CEILING_SAFETYSTOP, // 0=Mandatory Safety Stop Broken - SAMPLE_EVENT_ASCENT, // 1=Ascent Speed - SAMPLE_EVENT_NONE, // 2=Diluent Hyperoxia - SAMPLE_EVENT_VIOLATION, // 3=Violated Deep Stop - SAMPLE_EVENT_CEILING, // 4=Ceiling Broken - SAMPLE_EVENT_PO2, // 5=PO2 High - SAMPLE_EVENT_PO2, // 6=PO2 Low + static const eon_event_t alarms[] = { + {"Mandatory Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP}, + {"Ascent Speed", SAMPLE_EVENT_ASCENT}, + {"Diluent Hyperoxia", SAMPLE_EVENT_NONE}, + {"Violated Deep Stop", SAMPLE_EVENT_VIOLATION}, + {"Ceiling Broken", SAMPLE_EVENT_CEILING}, + {"PO2 High", SAMPLE_EVENT_PO2}, + {"PO2 Low", SAMPLE_EVENT_PO2}, }; + dc_sample_value_t sample = {0}; + const char *name; - if (info->alarm_type > 6) + name = info->alarm_type; + if (!name) return; - sample.event.type = translate_alarm[info->alarm_type]; + sample.event.type = lookup_event(name, alarms, C_ARRAY_SIZE(alarms)); if (sample.event.type == SAMPLE_EVENT_NONE) return; - sample.event.value = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END; + sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END; if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata); } -static int handle_sample_type(struct sample_data *info, enum eon_sample type, const unsigned char *data) +// enum:0=Low,1=High,2=Custom +static void sample_setpoint_type(const struct type_desc *desc, struct sample_data *info, unsigned char value) +{ + dc_sample_value_t sample = {0}; + const char *type = lookup_enum(desc, value); + + if (!type) { + DEBUG(info->eon->base.context, "sample_setpoint_type(%u) did not match anything in %s", value, desc->format); + return; + } + + if (!strcasecmp(type, "Low")) + sample.ppo2 = info->eon->cache.lowsetpoint; + else if (!strcasecmp(type, "High")) + sample.ppo2 = info->eon->cache.highsetpoint; + else if (!strcasecmp(type, "Custom")) + sample.ppo2 = info->eon->cache.customsetpoint; + else { + DEBUG(info->eon->base.context, "sample_setpoint_type(%u) unknown type '%s'", value, type); + return; + } + + if (info->callback) info->callback(DC_SAMPLE_SETPOINT, sample, info->userdata); +} + +// uint32 +static void sample_setpoint_po2(struct sample_data *info, unsigned int pressure) +{ + // I *think* this just sets the custom SP, and then + // we'll get a setpoint_type(2) later. + info->eon->cache.customsetpoint = pressure / 100000.0; // Pascal to bar +} + +static void sample_setpoint_automatic(struct sample_data *info, unsigned char value) +{ + DEBUG(info->eon->base.context, "sample_setpoint_automatic(%u)", value); +} + +static int handle_sample_type(const struct type_desc *desc, struct sample_data *info, enum eon_sample type, const unsigned char *data) { switch (type) { case ES_dtime: @@ -774,35 +916,35 @@ static int handle_sample_type(struct sample_data *info, enum eon_sample type, co return 2; case ES_state: - sample_event_state_type(info, data[0]); + sample_event_state_type(desc, info, data[0]); return 1; case ES_state_active: - sample_event_state_value(info, data[0]); + sample_event_state_value(desc, info, data[0]); return 1; case ES_notify: - sample_event_notify_type(info, data[0]); + sample_event_notify_type(desc, info, data[0]); return 1; case ES_notify_active: - sample_event_notify_value(info, data[0]); + sample_event_notify_value(desc, info, data[0]); return 1; case ES_warning: - sample_event_warning_type(info, data[0]); + sample_event_warning_type(desc, info, data[0]); return 1; case ES_warning_active: - sample_event_warning_value(info, data[0]); + sample_event_warning_value(desc, info, data[0]); return 1; case ES_alarm: - sample_event_alarm_type(info, data[0]); + sample_event_alarm_type(desc, info, data[0]); return 1; case ES_alarm_active: - sample_event_alarm_value(info, data[0]); + sample_event_alarm_value(desc, info, data[0]); return 1; case ES_bookmark: @@ -813,6 +955,18 @@ static int handle_sample_type(struct sample_data *info, enum eon_sample type, co sample_gas_switch_event(info, array_uint16_le(data)); return 2; + case ES_setpoint_type: + sample_setpoint_type(desc, info, data[0]); + return 1; + + case ES_setpoint_po2: + sample_setpoint_po2(info, array_uint32_le(data)); + return 4; + + case ES_setpoint_automatic: // bool + sample_setpoint_automatic(info, data[0]); + return 1; + default: return 0; } @@ -833,7 +987,7 @@ static int traverse_samples(unsigned short type, const struct type_desc *desc, c for (i = 0; i < EON_MAX_GROUP; i++) { enum eon_sample type = desc->type[i]; - int bytes = handle_sample_type(info, type, data); + int bytes = handle_sample_type(desc, info, type, data); if (!bytes) break; @@ -879,9 +1033,11 @@ suunto_eonsteel_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback static dc_status_t suunto_eonsteel_parser_get_field(dc_parser_t *parser, dc_field_type_t type, unsigned int flags, void *value) { + dc_tank_t *tank = (dc_tank_t *) value; + suunto_eonsteel_parser_t *eon = (suunto_eonsteel_parser_t *)parser; - if (!(eon->cache.initialized >> type)) + if (!(eon->cache.initialized & (1 << type))) return DC_STATUS_UNSUPPORTED; switch (type) { @@ -895,6 +1051,7 @@ suunto_eonsteel_parser_get_field(dc_parser_t *parser, dc_field_type_t type, unsi field_value(value, eon->cache.avgdepth); break; case DC_FIELD_GASMIX_COUNT: + case DC_FIELD_TANK_COUNT: field_value(value, eon->cache.ngases); break; case DC_FIELD_GASMIX: @@ -908,6 +1065,39 @@ suunto_eonsteel_parser_get_field(dc_parser_t *parser, dc_field_type_t type, unsi case DC_FIELD_ATMOSPHERIC: field_value(value, eon->cache.surface_pressure); break; + case DC_FIELD_DIVEMODE: + field_value(value, eon->cache.divemode); + break; + case DC_FIELD_TANK: + /* + * Sadly it seems that the EON Steel doesn't tell us whether + * we get imperial or metric data - the only indication is + * that metric is (at least so far) always whole liters + */ + tank->volume = eon->cache.tanksize[flags]; + tank->gasmix = flags; + + /* + * The pressure reported is NOT the pressure the user enters. + * + * So 3000psi turns into 206.700 bar instead of 206.843 bar; + * We report it as we get it and let the application figure out + * what to do with that + */ + tank->workpressure = eon->cache.tankworkingpressure[flags]; + tank->type = eon->cache.tankinfo[flags]; + + /* + * See if we should call this imperial instead. + * + * We need to have workpressure and a valid tank. In that case, + * a fractional tank size implies imperial. + */ + if (tank->workpressure && (tank->type == DC_TANKVOLUME_METRIC)) { + if (fabs(tank->volume - rint(tank->volume)) > 0.001) + tank->type = DC_TANKVOLUME_IMPERIAL; + } + break; default: return DC_STATUS_UNSUPPORTED; } @@ -953,11 +1143,38 @@ static void set_depth_field(suunto_eonsteel_parser_t *eon, unsigned short d) // Two versions so far: // "enum:0=Off,1=Primary,2=?,3=Diluent" // "enum:0=Off,1=Primary,3=Diluent,4=Oxygen" +// +// We turn that into the DC_TANKVOLUME data here, but +// initially consider all non-off tanks to me METRIC. +// +// We may later turn the METRIC tank size into IMPERIAL if we +// get a working pressure and non-integral size static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, unsigned char type) { - if (eon->cache.ngases < MAXGASES) - eon->cache.ngases++; + int idx = eon->cache.ngases; + dc_tankvolume_t tankinfo = DC_TANKVOLUME_METRIC; + const char *name; + + if (idx >= MAXGASES) + return 0; + + eon->cache.ngases = idx+1; + name = lookup_enum(desc, type); + if (!name) + DEBUG(eon->base.context, "Unable to look up gas type %u in %s", type, desc->format); + else if (!strcasecmp(name, "Diluent")) + ; + else if (!strcasecmp(name, "Oxygen")) + ; + else if (!strcasecmp(name, "None")) + tankinfo = 0; + else if (strcasecmp(name, "Primary")) + DEBUG(eon->base.context, "Unknown gas type %u (%s)", type, name); + + eon->cache.tankinfo[idx] = tankinfo; + eon->cache.initialized |= 1 << DC_FIELD_GASMIX_COUNT; + eon->cache.initialized |= 1 << DC_FIELD_TANK_COUNT; return 0; } @@ -983,6 +1200,23 @@ static int add_gas_he(suunto_eonsteel_parser_t *eon, unsigned char he) return 0; } +static int add_gas_size(suunto_eonsteel_parser_t *eon, float l) +{ + int idx = eon->cache.ngases-1; + if (idx >= 0) + eon->cache.tanksize[idx] = l; + eon->cache.initialized |= 1 << DC_FIELD_TANK; + return 0; +} + +static int add_gas_workpressure(suunto_eonsteel_parser_t *eon, float wp) +{ + int idx = eon->cache.ngases-1; + if (idx >= 0) + eon->cache.tankworkingpressure[idx] = wp; + return 0; +} + static float get_le32_float(const unsigned char *src) { union { @@ -1010,19 +1244,45 @@ static int traverse_device_fields(suunto_eonsteel_parser_t *eon, const struct ty return 0; } +// "sml.DeviceLog.Header.Diving.Gases" +// +// +Gas.State (enum:0=Off,1=Primary,3=Diluent,4=Oxygen) +// .Gas.Oxygen (uint8,precision=2) +// .Gas.Helium (uint8,precision=2) +// .Gas.PO2 (uint32) +// .Gas.TransmitterID (utf8) +// .Gas.TankSize (float32,precision=5) +// .Gas.TankFillPressure (float32,precision=0) +// .Gas.StartPressure (float32,precision=0) +// .Gas.EndPressure (float32,precision=0) +// .Gas.TransmitterStartBatteryCharge (int8,precision=2) +// .Gas.TransmitterEndBatteryCharge (int8,precision=2) +static int traverse_gas_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, + const unsigned char *data, int len) +{ + const char *name = desc->desc + strlen("sml.DeviceLog.Header.Diving.Gases"); + + if (!strcmp(name, "+Gas.State")) + return add_gas_type(eon, desc, data[0]); + + if (!strcmp(name, ".Gas.Oxygen")) + return add_gas_o2(eon, data[0]); + + if (!strcmp(name, ".Gas.Helium")) + return add_gas_he(eon, data[0]); + + if (!strcmp(name, ".Gas.TankSize")) + return add_gas_size(eon, get_le32_float(data)); + + if (!strcmp(name, ".Gas.TankFillPressure")) + return add_gas_workpressure(eon, get_le32_float(data)); + + return 0; +} + + // "sml.DeviceLog.Header.Diving." // -// Gases+Gas.State (enum:0=Off,1=Primary,3=Diluent,4=Oxygen) -// Gases.Gas.Oxygen (uint8,precision=2) -// Gases.Gas.Helium (uint8,precision=2) -// Gases.Gas.PO2 (uint32) -// Gases.Gas.TransmitterID (utf8) -// Gases.Gas.TankSize (float32,precision=5) -// Gases.Gas.TankFillPressure (float32,precision=0) -// Gases.Gas.StartPressure (float32,precision=0) -// Gases.Gas.EndPressure (float32,precision=0) -// Gases.Gas.TransmitterStartBatteryCharge (int8,precision=2) -// Gases.Gas.TransmitterEndBatteryCharge (int8,precision=2) // SurfaceTime (uint32) // NumberInSeries (uint32) // Algorithm (utf8) @@ -1032,6 +1292,12 @@ static int traverse_device_fields(suunto_eonsteel_parser_t *eon, const struct ty // AlgorithmTransitionDepth (uint8) // DaysInSeries (uint32) // PreviousDiveDepth (float32,precision=2) +// LowSetPoint (uint32) +// HighSetPoint (uint32) +// SwitchHighSetPoint.Enabled (bool) +// SwitchHighSetPoint.Depth (float32,precision=1) +// SwitchLowSetPoint.Enabled (bool) +// SwitchLowSetPoint.Depth (float32,precision=1) // StartTissue.CNS (float32,precision=3) // StartTissue.OTU (float32) // StartTissue.OLF (float32,precision=3) @@ -1057,14 +1323,8 @@ static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct ty { const char *name = desc->desc + strlen("sml.DeviceLog.Header.Diving."); - if (!strcmp(name, "Gases+Gas.State")) - return add_gas_type(eon, desc, data[0]); - - if (!strcmp(name, "Gases.Gas.Oxygen")) - return add_gas_o2(eon, data[0]); - - if (!strcmp(name, "Gases.Gas.Helium")) - return add_gas_he(eon, data[0]); + if (!strncmp(name, "Gases", 5)) + return traverse_gas_fields(eon, desc, data, len); if (!strcmp(name, "SurfacePressure")) { unsigned int pressure = array_uint32_le(data); // in SI units - Pascal @@ -1073,6 +1333,26 @@ static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct ty return 0; } + if (!strcmp(name, "DiveMode")) { + if (!strncmp(data, "CCR", 3)) { + eon->cache.divemode = DC_DIVEMODE_CC; + eon->cache.initialized |= 1 << DC_FIELD_DIVEMODE; + } + return 0; + } + + if (!strcmp(name, "LowSetPoint")) { + unsigned int pressure = array_uint32_le(data); // in SI units - Pascal + eon->cache.lowsetpoint = pressure / 100000.0; // bar + return 0; + } + + if (!strcmp(name, "HighSetPoint")) { + unsigned int pressure = array_uint32_le(data); // in SI units - Pascal + eon->cache.highsetpoint = pressure / 100000.0; // bar + return 0; + } + return 0; } @@ -1174,6 +1454,31 @@ static void initialize_field_caches(suunto_eonsteel_parser_t *eon) eon->cache.divetime /= 1000; } +static void show_descriptor(suunto_eonsteel_parser_t *eon, int nr, struct type_desc *desc) +{ + int i; + + if (!desc->desc) + return; + DEBUG(eon->base.context, "Descriptor %d: '%s', size %d bytes", nr, desc->desc, desc->size); + if (desc->format) + DEBUG(eon->base.context, " format '%s'", desc->format); + if (desc->mod) + DEBUG(eon->base.context, " mod '%s'", desc->mod); + for (i = 0; i < EON_MAX_GROUP; i++) { + enum eon_sample type = desc->type[i]; + if (!type) + continue; + DEBUG(eon->base.context, " %d: %d (%s)", i, type, desc_type_name(type)); + } +} + +static void show_all_descriptors(suunto_eonsteel_parser_t *eon) +{ + for (unsigned int i = 0; i < MAXTYPE; ++i) + show_descriptor(eon, i, eon->type_desc+i); +} + static dc_status_t suunto_eonsteel_parser_set_data(dc_parser_t *parser, const unsigned char *data, unsigned int size) { @@ -1182,6 +1487,7 @@ suunto_eonsteel_parser_set_data(dc_parser_t *parser, const unsigned char *data, desc_free(eon->type_desc, MAXTYPE); memset(eon->type_desc, 0, sizeof(eon->type_desc)); initialize_field_caches(eon); + show_all_descriptors(eon); return DC_STATUS_SUCCESS; }