Skip to content

Instantly share code, notes, and snippets.

@auscompgeek
Created September 4, 2018 11:25
Show Gist options
  • Save auscompgeek/c05de7daa064a03b05f9b3b84a2bd7d1 to your computer and use it in GitHub Desktop.
Save auscompgeek/c05de7daa064a03b05f9b3b84a2bd7d1 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import binascii
import enum
import typing
class Mode(enum.IntEnum):
RAIL = 0
FERRY = 1
BUS = 2
class Usage(enum.IntEnum):
UNUSED = 0
NEW_JOURNEY = 1
TRANSFER_SAME_MODE = 2
TRANSFER_INTERMODE = 3
NEW_JOURNEY_MCQ = 4
TRANSFER_SAME_MODE_MCQ = 5
TRANSFER_INTERMODE_MCQ = 6
DISTANCE_BASED_FARE = 7
FLAT_FARE = 8
AUTO_COMPLETE_JOURNEY = 9
END_NO_START = 10
TAP_REVERSAL = 11
UNSUCCESSFUL = 12
class FreeReadData(typing.NamedTuple):
serial: str
serial_check: int
blocked: bool
seq_num: int
balance: int
last_date: int
last_time: int
last_mode: Mode
last_usage: Usage
auto_topup_enabled: bool
journey_count: int
crc16: int
def twos_complement(input_value: int, num_bits: int) -> int:
"""Calculate a two's complement integer from the given input value's bits."""
mask = 1 << (num_bits - 1)
return (input_value & ~mask) - (input_value & mask)
def split_bits(data: int, num_bits: int) -> typing.Tuple[int, int]:
return data & ((1 << num_bits) - 1), data >> num_bits
def parse_data(b: bytes) -> FreeReadData:
assert len(b) == 16
data = int.from_bytes(b, "little")
serial = "%09d" % (data & 0xFFFFFFFF)
data >>= 32
serial_check = data & 0xF
data >>= 4
blocked = bool(data & 1)
data >>= 1
seq_num = data & 0xFFFF
data >>= 16
balance_unsigned, data = split_bits(data, 21)
last_date, data = split_bits(data, 15)
last_time, data = split_bits(data, 11)
last_mode = Mode(data & 0b111)
data >>= 3
last_usage = Usage(data & 0xF)
data >>= 4
auto_topup_enabled = bool(data & 1)
data >>= 1
journey_count = data & 0xF
data >>= 4
crc16 = data
balance = twos_complement(balance_unsigned, 21)
return FreeReadData(
serial,
serial_check,
blocked,
seq_num,
balance,
last_date,
last_time,
last_mode,
last_usage,
auto_topup_enabled,
journey_count,
crc16,
)
if __name__ == "__main__":
b = bytes.fromhex(input())
data = parse_data(b)
print(data)
# print(binascii.crc_hqx(b[:-2], 0))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment