njcodewarrior has asked for the wisdom of the Perl Monks concerning the following question:

I subscribe to a service that allows me to login ( via Net::Telnet ) and download a small message containing the gps position of a number of platforms we have deployed in oceans around the world. The gps positions are triangulated using the ARGOS satellite constellation.

In addition to this ASCII encoded message, each platform transmits a 32 byte message from the platform itself, ( containing the position as measured by the platform's gps unit, status messages, etc ) are encoded according to a know specification. Here's an example:

05 F9 AD F4 EC 9E 00 05 05 F9 E2 F4 EC A6 02 5B 05 FF 28 F4 AF 84 FF FF 0B 5C 00 06 06 FD 74 2F

The first piece of the message contains the gps position of the platform. The latitude is encoded as a 3 byte signed integer:

05F9AD

the longitude as a 3 byte signed integer:

F4EC9E

and the relative number of seconds since the measurement was made as a 2 byte unsigned integer:

0005

At first glance, I figured pack/unpack was the way to go. I read over pfaut's pack/unpack tutorial, but I still can't seem to get my hands around how to decode this message.

Any help would be greatly appreciated.

njcodewarrior

Replies are listed 'Best First'.
Re: Decoding binary information
by liverpole (Monsignor) on Feb 10, 2007 at 17:28 UTC
    Hi njcodewarrior,

    Why not use hex?

    The first time I encountered it, it seemed like a bit of a misnomer; it actually interprets the given value as a hexadecimal integer, and converts it to decimal (not the reverse, as one might suppose).

    For example:

    use strict; use warnings; my @values = ( "05F9AD", "F4EC9E", "0005" ); foreach my $value (@values) { my $decval = hex($value); printf "Hex value 0x%08lx = %ld (decimal)\n", $decval, $decval; }

    which gives the following results:

    Hex value 0x0005f9ad = 391597 (decimal) Hex value 0x00f4ec9e = 16051358 (decimal) Hex value 0x00000005 = 5 (decimal)

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/

      Thanks liverpole.

      The first and third integers are properly converted using hex as you suggested. However, the second number is actually a negative number:

      F4EC9E = -725850

      Any ideas on why?

      Thank you very much for your help. You've already cleared up lots!

      njcodewarrior

        You should sign-extend it, Perl isn't used to working with 24 bit integers.

        Perl can handle 32 bit integers just fine. You could append '00', pack it into a 4 byte structure (a long), and unpack it as a signed long; and finally divide by 256, for the 2 extra zeros you added.

        print unpack('l', pack 'L', hex 'F4EC9E00')/256;
        Or, maybe easier, subtract 2**24 if you see it's 2**23 or more.
        $n = hex 'F4EC9E'; if($n >= 2**23) { $n -= 2**24 } print $n;
        What version of Perl do you have, and what code exactly are you running?

        I could see where F4EC9E might be interpreted as a negative value, since the highest order bit is set.  What I don't understand is why it would evaluate as -725850, which should be represented by F4ECA6:
        use strict; use warnings; my $value; $value = -725850; printf "Hex value 0x%08lX = %ld (decimal)\n", $value, $value; $value = -725858; printf "Hex value 0x%08lX = %ld (decimal)\n", $value, $value __END__ # Output: Hex value 0xFFF4ECA6 = -725850 (decimal) Hex value 0xFFF4EC9E = -725858 (decimal)

        s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Decoding binary information
by almut (Canon) on Feb 10, 2007 at 19:21 UTC

    If you are absolutely sure that F4EC9E is representing -725850, and that this is not just a typo (0 vs. 8) or a bug in the existing conversion program, it could be that your negative numbers are represented in some non-standard way. There are several ways to represent signed integers (the most popular being the two's complement). However, AFAICT, none of the common ones would map the given bit pattern to the decimal number -725850. So... if you are sure about this, it might help if you'd post the existing C conversion routine (if you have the source (and its size is reasonable)). Also, it would be interesting to know what the value FFFFFF is representing.

Re: Decoding binary information
by jwkrahn (Abbot) on Feb 10, 2007 at 22:25 UTC
    $ perl -le' my $message = "\x05\xF9\xAD\xF4\xEC\x9E\x00\x05\x05\xF9\xE2\xF4\xEC\xA +6\x02\x5B\x05\xFF\x28\xF4\xAF\x84\xFF\xFF\x0B\x5C\x00\x06\x06\xFD\x74 +\x2F"; print for map unpack( "N", substr "\0\0$_", -4 ), unpack "a3 a3 a2", $ +message; ' 391597 16051358 5