smerchek / node-fit

Garmin FIT file parser for nodejs
MIT License
11 stars 4 forks source link

Values of messages are completely wrong in Linux (endianness problem) #3

Open smerchek opened 11 years ago

smerchek commented 11 years ago

The FIT SDK claims to handle the endianness of the various fields for each message. I do see that there is some logic for this; however, running decoding the example Activity.fit file yields different results on a Windows system and a Linux system.

For example, for the first record, mesg.GetTimestamp(), on Windows returns 702940946 which seems correct based on the csv value for the same record. However, on linux, the value is 319284777. I get this same value on Windows if I swap the bytes before printing the result.

I've asked for help on who's responsibility this is in the FIT SDK forum.

The solution may just be to detect the endianness of the the environment and adjust values accordingly. I feel like the SDK should handle this though. I could also just be doing something wrong.

smerchek commented 11 years ago

I've found the source of the issue in the FIT SDK:

FIT_UINT32 Field::GetUINT32Value(const FIT_UINT8 fieldArrayIndex, const FIT_UINT16 subFieldIndex) const
{
   if ((fieldArrayIndex >= values.size()) || (values[fieldArrayIndex].size() < sizeof(FIT_UINT32)))
      return FIT_UINT32_INVALID;

   return ((FIT_UINT32) values[fieldArrayIndex][3] << 24) | ((FIT_UINT32) values[fieldArrayIndex][2] << 16) | ((FIT_UINT32) values[fieldArrayIndex][1] << 8) | values[fieldArrayIndex][0];
}

This code always returns the unsigned integer in big endian format. If we detect the endianness, and adjust accordingly, then we can fix this problem.

if (fit::GetArch()) {
   //Big Endian
   return ((FIT_UINT32) values[fieldArrayIndex][3] << 24) | ((FIT_UINT32) values[fieldArrayIndex][2] << 16) | ((FIT_UINT32) values[fieldArrayIndex][1] << 8) | values[fieldArrayIndex][0];
}
else {
   return ((FIT_UINT32) values[fieldArrayIndex][0] << 24) | ((FIT_UINT32) values[fieldArrayIndex][1] << 16) | ((FIT_UINT32) values[fieldArrayIndex][2] << 8) | values[fieldArrayIndex][3];
}

This problem exists for each data type. I'd prefer to fix the problem at this field retrieval level rather than have to deal with swapping the bytes every time I use the messages.

smerchek commented 11 years ago

Field::Get Methods to fix

smerchek commented 11 years ago

As it turns out, both my windows machine and linux machine are big endian according to the fit::GetArch() function. While that may or may not be surprising, it is surprising that they behave as if they are different. Why are the bytes reversed on linux if it is the same endian format? Is something different about the file being read in?

smerchek commented 11 years ago

I'm still not sure why there is a difference between linux and Windows; however I implemented a workaround in 35e15b2 by using a GetOS function.

RusAlex commented 10 years ago

I've just tried your software. And it does not work for me. Linux OS.

This is the set of timestamps of records in decode callback:

Sat Sep 22 1990 20:28:46 GMT+0500 (MSK)
Fri Apr 05 1991 00:49:02 GMT+0500 (MSK)
Sat Nov 07 1992 12:49:50 GMT+0400 (MSK)
Sat Dec 24 1994 06:10:54 GMT+0400 (MSK)
Thu Jul 06 1995 11:31:10 GMT+0500 (MSK)
Fri Feb 07 1997 23:31:58 GMT+0400 (MSK)
Tue Mar 03 1998 08:12:30 GMT+0400 (MSK)
Fri Mar 26 1999 16:53:02 GMT+0400 (MSK)
Wed Oct 06 1999 22:13:18 GMT+0500 (MSK)
Tue Apr 18 2000 02:33:34 GMT+0500 (MSK)
Sun Oct 29 2000 05:53:50 GMT+0400 (MSK)
Wed Nov 21 2001 14:34:22 GMT+0400 (MSK)

Timestamps completely incorrect.