Maths question, combing two 16 bit data streams into a 32 bit stream?

http://www.gatesgarth.com/maths.zip Means little to me, but someone posted this a few years ago on a Motec forum when they were trying to make a Motec device read the GPS serial data stream, pre Motec's own implementation as a supported feature. It just MAY mean something to you experts ;)

That doesn't have combining code in it as far as I can see.
 
Chris - I don't know if you looked back at this thread you started - http://forums.overclockers.co.uk/showthread.php?t=18296221 but the 10hz GPS I'm using is accurate to 5 decimal places (52.12345 etc), 100th of a second and is simply recorded on a mobile phone in .CSV format (excel file of a run I did here) for £50 outlay and had got to be simpler than all this higher level maths :D

I realise it's not a MOTEC solution but for £50 is it something you could use or do you need live data streaming, 20hz GPS etc?
 
Last edited:
If it's not doing something equivalent to:

Code:
float longitude;
short* s = (s*)&longitude;
s[0] = HW;
s[1] = LW;

(Possibly with HW and LW reversed) I'll eat my hat. It'd be insane to do anything else.

It's not doing this. I checked. Unless you're using something with a different float format.
 
The approach that's already been shown for combining the two 16bit values into a single 32bit integer is nearly correct. How you treat the LW value is slightly more subtle than the approach grumpysculler showed. Hopefully this code is clear.

Code:
combineNumbers <- function(HW, LW) {
    if(LW < 0) 
        return(2^16 * HW + (2^16 + LW))
    else
        return(2^16 * HW + LW)
}

You're then left with a signed 32bit integer.

First we'll look at the time/date example, as that's easier to interperate.

GPS Time HW : 1614
GPS Time LW : -8040
GPS Time (combined) : 105832.600

If you run the code above with those values you get:

Code:
combineNumbers(1614, -8040) = 105832600

The relationship between our 32bit integer and the desired result is obvious. I'm going to make the assumption that the software that interperates this always chops that into 5 characters for the seconds, 2 for the minutes and whatever's left is the hour, i.e it has a predefined understanding of how to interperate the 32bit integer in a particular situation.

We can then apply the similar logic to the coordinate data. I'll take the first example here:

ECU output: GPS Latitude HW : 8015
GPS Latitude LW : -13983
Maths gives : Coordinate 52.8870988 degrees latitude

Combine the two 16bit values into a single signed 32bit number.

Code:
combineNumbers(8015, -13983) = 525322593

People have already noted the degree part seems to be correct, but the bits after the decimal point don't work when dividing by 10^7. So the key to understanding what's going on with the fractional part is to remember that although the result we're looking for is in decimal fractions of degrees, you can also represent the values as degrees, minutes and seconds. Hence we need to work in base 60 (or atleast some multiple of 60), rather than 10.

Like the time of day example, we'll assume that a specific number of digits in our integer represent each of the seconds, minutes and degrees (in our case it's 3, 4 and 2), starting at the "right side" of the integer. If it's negative ignore that for the moment.

Code:
525322593 = 52 | 5322 | 593

We can then combine these as:

Code:
52 + (5322 / 6000) + (593 / 6000000) = 52.88709883

You'd then multiply by -1 if the orginal integer was negative.

Just to check it works we'll do the other example:

ECU output : GPS Longitude HW : -364
GPS Longitude LW : 14387
Maths gives : Coordinate -2.6401195 degrees longitude

Stick those values in the procedure:

Code:
combineNumbers(-364, 14387) = -23840717

2 + (3840 / 6000) + (717 / 6000000) = 2.6401195

Multiply by -1

-2.6401195

Hopefully that makes sense.
 
Code:
combineNumbers <- function(HW, LW) {
    if(LW < 0) 
        return(2^16 * HW + (2^16 + LW))
    else
        return(2^16 * HW + LW)
}

Wow thats a lot of work, can do it all in one line.

Code:
        return ((HW<<16) | (unsigned short)LW);
 
Last edited:
Wow thats a lot of work, can do it all in one line.

Code:
        return ((HW<<16) | (unsigned short)LW);

Sure you can, but given this is meant to be an explanation of how to get to the end point, I think a slightly more verbose version is more helpful. I wouldn't expect everyone to know what bitshift operators or casting are doing.

I was also hoping it would highlight why the numeric examples people attempted earlier were wrong.
 
Back
Top Bottom