Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python, how to decode Binary coded decimal (BCD)

Description of the binary field is:

Caller number, expressed with compressed BCD code, and the surplus bits are filled with “0xF”

I have tried to print with struct format '16c' and I get: ('3', '\x00', '\x02', '\x05', '\x15', '\x13', 'G', 'O', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff') and if I use '16b' i get (51, 0, 2, 5, 21, 19, 71, 79, -1, -1, -1, -1, -1, -1, -1, -1). And it is not correct, I should get phone number, and numbers above are invalid.

print struct.unpack_from('>16b', str(data.read()),offset=46)

Above is code that didn't work and I get invalid numbers. With what format should I unpack that 16 byte field and how to convert BCD code ?

like image 992
Whit3H0rse Avatar asked Jul 26 '12 12:07

Whit3H0rse


People also ask

How do I read a BCD code?

Binary Coded Decimal Representation of a Decimal Number In the BCD number system, the binary weight of each digit increases by a factor of 2 as shown. Then the first digit has a weight of 1 ( 20 ), the second digit has a weight of 2 ( 21 ), the third a weight of 4 ( 22 ), the fourth a weight of 8 ( 23 ).

Is BCD code same as binary?

BCD numbers encode less information than binary numbers with the same number of bits, as 6 of 16 possible states are not used for each 4-bit BCD number. Mathematically, each BCD “bit” only encodes 10^(1/4) = 1.778 states, while each binary bit encodes 2 states.


2 Answers

a = b'\x12\x34\x5f'

def comp3_to_int(a):
    b = a.hex()
    c = int(b[0:len(b)-1])
    if b[len(b)-1:] == 'd':
        c = -c
    return c
like image 137
zemaCyrineu Avatar answered Sep 20 '22 12:09

zemaCyrineu


BCD codes work with 4 bits per number, and normally encode only the digits 0 - 9. So each byte in your sequence contains 2 numbers, 1 per 4 bits of information.

The following method uses a generator to produce those digits; I am assuming that a 0xF value means there are no more digits to follow:

def bcdDigits(chars):
    for char in chars:
        char = ord(char)
        for val in (char >> 4, char & 0xF):
            if val == 0xF:
                return
            yield val

Here I use a right-shift operator to move the left-most 4 bits to the right, and a bitwise AND to select just the right-most 4 bits.

Demonstration:

>>> characters = ('3', '\x00', '\x02', '\x05', '\x15', '\x13', 'G', 'O', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff')
>>> list(bcdDigits(characters))
[3, 3, 0, 0, 0, 2, 0, 5, 1, 5, 1, 3, 4, 7, 4]

The method works with the c output; you can skip the ord call in the method if you pass integers directly (but use the B unsigned variant instead). Alternatively, you could just read those 16 bytes straight from your file and apply this function to those bytes directly without using struct.

like image 21
Martijn Pieters Avatar answered Sep 18 '22 12:09

Martijn Pieters