Python, how to decode Binary coded decimal (BCD)
Asked Answered
D

4

12

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 ?

Domesticate answered 26/7, 2012 at 12:8 Comment(0)
D
19

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.

Divulge answered 26/7, 2012 at 12:21 Comment(0)
M
1
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
Mono answered 12/3, 2021 at 14:34 Comment(1)
@ack, hex() is a method of bytes (which a is in above code, assuming comp3_to_int(a) is called somewhere). See docs.python.org/3/library/stdtypes.html#bytes.hexCudlip
V
1

Function from my project. Second argument places decimal point.

def bcd_decode(data: bytes, decimals: int):
    '''
    Decode BCD number
    '''
    res = 0
    for n, b in enumerate(reversed(data)):
        res += (b & 0x0F) * 10 ** (n * 2 - decimals)
        res += (b >> 4) * 10 ** (n * 2 + 1 - decimals)
    return res
Vulpecula answered 26/3, 2022 at 9:23 Comment(0)
A
0

100 times faster solution:

import binascii
binascii.hexlify(byteArray).decode()

another slower solution :

''.join(format(x, '02X') for x in byteArray

if you need a number, just wrap the result into int()

Alidaalidade answered 5/10, 2023 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.