So Suunto started pushing out a new firmware version for the EON Steel
this month: v1.1.15.

It turns out that some of the assumptions I made about how the dive
parsing worked were wrong.

A trivial issue is that some of the type descriptors now have
terminating newlines rather than using newlines just as separators.
That just means that we need to relax the type descriptor parsing a
bit.

That's the first of the two patches here.

The bigger problem is that the "fixed" type indexes aren't actually
fixed at all, and the fact that they never changed before, and that
they had special index numbers, seems to have been just a random
artifact rather than really part of the design.

That in turn means that rather than checking the type index number, we
really do have to check the type descriptor string as the ID of the
data.

The second patch fixes the parsing of the dive header fields (in
particular, the gas mix details and transmitter ID), and re-organizes
things a bit to be easier to parse.

The reason I say this is "initial work" is that this does *not* change
the parsing of the actual samples themselves, and that still uses the
incorrect fixed index numbers. Which means that we currently get the
cylinder pressure readings wrong (rather, we don't get them at all)
with the new firmware, and we don't parse some of the new information
(like the air-time remaining).

I'll get to the actual sample data later today, I hope.

I've updated my EON, but a firmware update doesn't actually change
what the dive computer reports for old dives, so I'll have to wait
until next week before I have dives of my own to test this all with.
But thanks to Nick I have a sample dive dump showing the new data,
which is how I know we got this wrong.

              Linus
From 3b2e6fde20343027036645b10186b93f453cb41e Mon Sep 17 00:00:00 2001
From: Linus Torvalds <[email protected]>
Date: Sat, 6 Jun 2015 17:14:25 -0700
Subject: [PATCH 1/2] EON Steel: empty descriptor lines are ok

Suunto's new v1.1.15 firmware ends up terminating some final descriptor
lines with a newline, rather than just using newlines as separators.  So
the last newline may not be followed by further data, but simple be the
end of the string.  Accept that case.

Signed-off-by: Linus Torvalds <[email protected]>
---
 src/suunto_eonsteel_parser.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c
index fd855ff315c4..ba4c5297b98e 100644
--- a/src/suunto_eonsteel_parser.c
+++ b/src/suunto_eonsteel_parser.c
@@ -87,6 +87,8 @@ static int record_type(suunto_eonsteel_parser_t *eon, unsigned short type, const
 			next++;
 		} else {
 			len = strlen(name);
+			if (!len)
+				break;
 		}
 
 		if (len < 5 || name[0] != '<' || name[4] != '>') {
-- 
2.4.2.337.gfae46aa

From 110677bcb73455f183fccbb486bdc09310e52769 Mon Sep 17 00:00:00 2001
From: Linus Torvalds <[email protected]>
Date: Mon, 8 Jun 2015 09:30:57 -0700
Subject: [PATCH 2/2] EON Steel: start moving away from "fixed" fields

I was initially fooled into thinking that the field type numbers have
some meaning: the types didn't change if the upper byte of the type
number was zero.  So I assumed that meant "fixed".

But the most recent firmware update made clear that no, they aren't
fixed, and the upper byte of the type must be some other thing.

This moves some more of the parsing over to comparing the strings,
rather than looking at the type index.  It still leaves the sample data
alone, and I really want to do something more efficient than comparing
the type descriptor string for that, but at least the dive header fields
are now just comparing strings.

The actual marshalling that Suunto uses also describes the encoding, and
it's all ignoring that for now.

Signed-off-by: Linus Torvalds <[email protected]>
---
 src/suunto_eonsteel_parser.c | 190 ++++++++++++++++++++++++++-----------------
 1 file changed, 114 insertions(+), 76 deletions(-)

diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c
index ba4c5297b98e..075a3224f611 100644
--- a/src/suunto_eonsteel_parser.c
+++ b/src/suunto_eonsteel_parser.c
@@ -628,30 +628,41 @@ static void set_depth_field(suunto_eonsteel_parser_t *eon, unsigned short d)
 	}
 }
 
-// gas type: 0=Off,1=Primary,2=?,3=Diluent
-static void add_gas_type(suunto_eonsteel_parser_t *eon, unsigned char type)
+// new gas:
+//  "sml.DeviceLog.Header.Diving.Gases+Gas.State"
+//
+// We eventually need to parse the descriptor for that 'enum type'.
+// Two versions so far:
+//   "enum:0=Off,1=Primary,2=?,3=Diluent"
+//   "enum:0=Off,1=Primary,3=Diluent,4=Oxygen"
+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++;
 	eon->cache.initialized |= 1 << DC_FIELD_GASMIX_COUNT;
+	return 0;
 }
 
+// "sml.DeviceLog.Header.Diving.Gases.Gas.Oxygen"
 // O2 percentage as a byte
-static void add_gas_o2(suunto_eonsteel_parser_t *eon, unsigned char o2)
+static int add_gas_o2(suunto_eonsteel_parser_t *eon, unsigned char o2)
 {
 	int idx = eon->cache.ngases-1;
 	if (idx >= 0)
 		eon->cache.gasmix[idx].oxygen = o2 / 100.0;
 	eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
+	return 0;
 }
 
+// "sml.DeviceLog.Header.Diving.Gases.Gas.Helium"
 // He percentage as a byte
-static void add_gas_he(suunto_eonsteel_parser_t *eon, unsigned char he)
+static int add_gas_he(suunto_eonsteel_parser_t *eon, unsigned char he)
 {
 	int idx = eon->cache.ngases-1;
 	if (idx >= 0)
 		eon->cache.gasmix[idx].helium = he / 100.0;
 	eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
+	return 0;
 }
 
 static int add_string(suunto_eonsteel_parser_t *eon, const char *desc, const char *value)
@@ -689,8 +700,10 @@ static float get_le32_float(const void *src)
 //   Info.SW
 //   Name
 //   SerialNumber
-static int traverse_device_fields(suunto_eonsteel_parser_t *eon, const char *name, const void *data, int len)
+static int traverse_device_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.Device.");
 	if (!strcmp(name, "SerialNumber"))
 		return add_string(eon, "Serial", data);
 	if (!strcmp(name, "Info.HW"))
@@ -704,68 +717,80 @@ static int traverse_device_fields(suunto_eonsteel_parser_t *eon, const char *nam
 	return 0;
 }
 
-// "Header" fields are:
-//   Activity (utf8)
-//   DateTime (utf8)
-//   Depth.Avg (float32,precision=2)
-//   Depth.Max (float32,precision=2)
-//   Diving.AlgorithmAscentTime (uint32)
-//   Diving.AlgorithmBottomMixture.Helium (uint8,precision=2) (0.01*x,100*x)
-//   Diving.AlgorithmBottomMixture.Oxygen (uint8,precision=2) (0.01*x,100*x)
-//   Diving.AlgorithmBottomTime (uint32)
-//   Diving.AlgorithmTransitionDepth (uint8)
-//   Diving.Algorithm (utf8)
-//   Diving.Altitude (uint16)
-//   Diving.Conservatism (int8)
-//   Diving.DaysInSeries (uint32)
-//   Diving.DesaturationTime (uint32)
-//   Diving.DiveMode (utf8)
-//   Diving.EndTissue.CNS (float32,precision=3)
-//   Diving.EndTissue.Helium+Pressure (uint32)
-//   Diving.EndTissue.Nitrogen+Pressure (uint32)
-//   Diving.EndTissue.OLF (float32,precision=3)
-//   Diving.EndTissue.OTU (float32)
-//   Diving.EndTissue.RgbmHelium (float32,precision=3)
-//   Diving.EndTissue.RgbmNitrogen (float32,precision=3)
-//   Diving.NumberInSeries (uint32)
-//   Diving.PreviousDiveDepth (float32,precision=2)
-//   Diving.StartTissue.CNS (float32,precision=3)
-//   Diving.StartTissue.Helium+Pressure (uint32)
-//   Diving.StartTissue.Nitrogen+Pressure (uint32)
-//   Diving.StartTissue.OLF (float32,precision=3)
-//   Diving.StartTissue.OTU (float32)
-//   Diving.StartTissue.RgbmHelium (float32,precision=3)
-//   Diving.StartTissue.RgbmNitrogen (float32,precision=3)
-//   Diving.SurfacePressure (uint32)
-//   Diving.SurfaceTime (uint32)
-//   Duration (uint32)
-//   PauseDuration (uint32)
-//   SampleInterval (uint8)
-static int traverse_header_fields(suunto_eonsteel_parser_t *eon, const char *name, const void *data, int len)
-{
-	if (!strcmp(name, "Depth.Max")) {
-		double d = get_le32_float(data);
-		if (d > eon->cache.maxdepth)
-			eon->cache.maxdepth = d;
-		return 0;
-	}
-	if (!strcmp(name, "Diving.SurfacePressure")) {
+// "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)
+//   SurfacePressure (uint32)
+//   Conservatism (int8)
+//   Altitude (uint16)
+//   AlgorithmTransitionDepth (uint8)
+//   DaysInSeries (uint32)
+//   PreviousDiveDepth (float32,precision=2)
+//   StartTissue.CNS (float32,precision=3)
+//   StartTissue.OTU (float32)
+//   StartTissue.OLF (float32,precision=3)
+//   StartTissue.Nitrogen+Pressure (uint32)
+//   StartTissue.Helium+Pressure (uint32)
+//   StartTissue.RgbmNitrogen (float32,precision=3)
+//   StartTissue.RgbmHelium (float32,precision=3)
+//   DiveMode (utf8)
+//   AlgorithmBottomTime (uint32)
+//   AlgorithmAscentTime (uint32)
+//   AlgorithmBottomMixture.Oxygen (uint8,precision=2)
+//   AlgorithmBottomMixture.Helium (uint8,precision=2)
+//   DesaturationTime (uint32)
+//   EndTissue.CNS (float32,precision=3)
+//   EndTissue.OTU (float32)
+//   EndTissue.OLF (float32,precision=3)
+//   EndTissue.Nitrogen+Pressure (uint32)
+//   EndTissue.Helium+Pressure (uint32)
+//   EndTissue.RgbmNitrogen (float32,precision=3)
+//   EndTissue.RgbmHelium (float32,precision=3)
+static int traverse_diving_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.");
+
+	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 (!strcmp(name, "Gases.Gas.TransmitterID"))
+		return add_string(eon, "Transmitter ID", data);
+
+	if (!strcmp(name, "SurfacePressure")) {
 		unsigned int pressure = array_uint32_le(data); // in SI units - Pascal
 		eon->cache.surface_pressure = pressure / 100000.0; // bar
 		eon->cache.initialized |= 1 << DC_FIELD_ATMOSPHERIC;
 		return 0;
 	}
-	if (!strcmp(name, "DateTime"))
-		return add_string(eon, "Dive ID", data);
 
-	if (!strcmp(name, "Diving.Algorithm"))
+	if (!strcmp(name, "Algorithm"))
 		return add_string(eon, "Deco algorithm", data);
 
-	if (!strcmp(name, "Diving.DiveMode"))
+	if (!strcmp(name, "DiveMode"))
 		return add_string(eon, "Dive Mode", data);
 
 	/* Signed byte of conservatism (-2 .. +2) */
-	if (!strcmp(name, "Diving.Conservatism")) {
+	if (!strcmp(name, "Conservatism")) {
 		char buffer[10];
 		int val = *(signed char *)data;
 
@@ -776,7 +801,36 @@ static int traverse_header_fields(suunto_eonsteel_parser_t *eon, const char *nam
 	return 0;
 }
 
-static int traverse_dynamic_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, const void *data, int len)
+// "Header" fields are:
+//   Activity (utf8)
+//   DateTime (utf8)
+//   Depth.Avg (float32,precision=2)
+//   Depth.Max (float32,precision=2)
+//   Diving.*
+//   Duration (uint32)
+//   PauseDuration (uint32)
+//   SampleInterval (uint8)
+static int traverse_header_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.");
+
+	if (!strncmp(name, "Diving.", 7))
+		return traverse_diving_fields(eon, desc, data, len);
+
+	if (!strcmp(name, "Depth.Max")) {
+		double d = get_le32_float(data);
+		if (d > eon->cache.maxdepth)
+			eon->cache.maxdepth = d;
+		return 0;
+	}
+	if (!strcmp(name, "DateTime"))
+		return add_string(eon, "Dive ID", data);
+
+	return 0;
+}
+
+static int traverse_dynamic_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, const unsigned char *data, int len)
 {
 	const char *name = desc->desc;
 
@@ -785,9 +839,9 @@ static int traverse_dynamic_fields(suunto_eonsteel_parser_t *eon, const struct t
 		if (!strncmp(name, "DeviceLog.", 10)) {
 			name += 10;
 			if (!strncmp(name, "Device.", 7))
-				return traverse_device_fields(eon, name+7, data, len);
+				return traverse_device_fields(eon, desc, data, len);
 			if (!strncmp(name, "Header.", 7)) {
-				return traverse_header_fields(eon, name+7, data, len);
+				return traverse_header_fields(eon, desc, data, len);
 			}
 		}
 	}
@@ -809,24 +863,8 @@ static int traverse_fields(unsigned short type, const struct type_desc *desc, co
 	case 0x0003: // depth in first word
 		set_depth_field(eon, array_uint16_le(data));
 		break;
-	case 0x000d: // gas state in first byte
-		add_gas_type(eon, data[0]);
-		break;
-	case 0x000e: // Oxygen percentage in first byte
-		add_gas_o2(eon, data[0]);
-		break;
-	case 0x000f: // Helium percentage in first byte
-		add_gas_he(eon, data[0]);
-		break;
-	case 0x0011: // Transmitter ID
-		add_string(eon, "Transmitter ID", data);
-		break;
 	default:
-	// The types with the high byte set seem to be dynamic
-	// although not all of them seem to change. But let's
-	// just check the descriptor name for them.
-		if (type > 255)
-			traverse_dynamic_fields(eon, desc, data, len);
+		traverse_dynamic_fields(eon, desc, data, len);
 		break;
 	}
 	return 0;
-- 
2.4.2.337.gfae46aa

_______________________________________________
subsurface mailing list
[email protected]
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to