From d82dfd55374cb91fb2aea175c88ce32a9ab7efb5 Mon Sep 17 00:00:00 2001 From: Yannick Kirschhoffer Date: Sun, 4 Nov 2018 00:17:09 +0100 Subject: [PATCH 1/3] Extended test to stress the CRC function. --- tests/test_common.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_common.py b/tests/test_common.py index b83968a..12060d0 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -5,8 +5,9 @@ def test_hex2bin(): assert common.hex2bin('6E406B') == "011011100100000001101011" def test_crc_decode(): - checksum = common.crc("8D406B902015A678D4D220AA4BDA") - assert checksum == "000000000000000000000000" + for i in range(5000): + checksum = common.crc("8D406B902015A678D4D220AA4BDA") + assert checksum == "000000000000000000000000" def test_crc_encode(): parity = common.crc("8D406B902015A678D4D220AA4BDA", encode=True) From 32e6ee3904778ecd87d96f21ce1942cc4c746930 Mon Sep 17 00:00:00 2001 From: Yannick Kirschhoffer Date: Sun, 4 Nov 2018 00:19:03 +0100 Subject: [PATCH 2/3] Implement an alternative faster CRC. --- pyModeS/decoder/common.py | 27 +++++++---------------- pyModeS/decoder/fastcrc.py | 45 ++++++++++++++++++++++++++++++++++++++ tests/test_common.py | 19 ++++++++++++---- 3 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 pyModeS/decoder/fastcrc.py diff --git a/pyModeS/decoder/common.py b/pyModeS/decoder/common.py index 4225568..7512bb5 100644 --- a/pyModeS/decoder/common.py +++ b/pyModeS/decoder/common.py @@ -1,6 +1,9 @@ from __future__ import absolute_import, print_function, division import numpy as np +from pyModeS.decoder import fastcrc + + def hex2bin(hexstr): """Convert a hexdecimal string to binary string, with zero fillings. """ num_of_bits = len(hexstr) * 4 @@ -41,29 +44,15 @@ def crc(msg, encode=False): msg (string): 28 bytes hexadecimal message string encode (bool): True to encode the date only and return the checksum Returns: - string: message checksum, or partity bits (encoder) + int: message checksum, or partity bits (encoder) """ - # the polynominal generattor code for CRC [1111111111111010000001001] - generator = np.array([1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1,0,0,1]) - ng = len(generator) - - msgnpbin = bin2np(hex2bin(msg)) - if encode: - msgnpbin[-24:] = [0] * 24 + msg = msg[:-6] + "000000" - # loop all bits, except last 24 piraty bits - for i in range(len(msgnpbin)-24): - if msgnpbin[i] == 0: - continue + reminder_int = fastcrc.crc(msg) - # perform XOR, when 1 - msgnpbin[i:i+ng] = np.bitwise_xor(msgnpbin[i:i+ng], generator) - - # last 24 bits - reminder = np2bin(msgnpbin[-24:]) - return reminder + return reminder_int def floor(x): @@ -92,7 +81,7 @@ def icao(msg): if DF in (11, 17, 18): addr = msg[2:8] elif DF in (0, 4, 5, 16, 20, 21): - c0 = bin2int(crc(msg, encode=True)) + c0 = crc(msg, encode=True) c1 = hex2int(msg[-6:]) addr = '%06X' % (c0 ^ c1) else: diff --git a/pyModeS/decoder/fastcrc.py b/pyModeS/decoder/fastcrc.py new file mode 100644 index 0000000..d793d23 --- /dev/null +++ b/pyModeS/decoder/fastcrc.py @@ -0,0 +1,45 @@ +GENERATOR = [int("11111111", 2), int("11111010", 2), int("00000100", 2), int("10000000", 2)] + + +class BytesWrapper: + + def __init__(self, hex: str): + if len(hex) % 2 == 1: + hex += '0' + + self._bytes = [b for b in bytes.fromhex(hex)] + + def byte_count(self) -> int: + return len(self._bytes) - 3 + + def get_bit(self, byte_index, bit_index): + mask = 0x80 >> bit_index + bits = self._bytes[byte_index] & mask + return 0 if bits == 0 else 1 + + def apply_matrix(self, byte_index, bit_index): + self._bytes[byte_index] = self._bytes[byte_index] ^ (GENERATOR[0] >> bit_index) + self._bytes[byte_index + 1] = self._bytes[byte_index + 1] ^ \ + (0xFF & ((GENERATOR[0] << 8 - bit_index) | (GENERATOR[1] >> bit_index))) + self._bytes[byte_index + 2] = self._bytes[byte_index + 2] ^ \ + (0xFF & ((GENERATOR[1] << 8 - bit_index) | (GENERATOR[2] >> bit_index))) + self._bytes[byte_index + 3] = self._bytes[byte_index + 3] ^ \ + (0xFF & ((GENERATOR[2] << 8 - bit_index) | (GENERATOR[3] >> bit_index))) + + def get_suffix(self) -> int: + return (self._bytes[-3] << 16) | (self._bytes[-2] << 8) | self._bytes[-1] + + +def crc(msg: str) -> int: + msgbin = BytesWrapper(msg) + + for byte_index in range(msgbin.byte_count()): + for bit_index in range(8): + b = msgbin.get_bit(byte_index, bit_index) + + if b == 1: + msgbin.apply_matrix(byte_index, bit_index) + + result = msgbin.get_suffix() + + return result diff --git a/tests/test_common.py b/tests/test_common.py index 12060d0..aa6189f 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -5,13 +5,24 @@ def test_hex2bin(): assert common.hex2bin('6E406B') == "011011100100000001101011" def test_crc_decode(): - for i in range(5000): - checksum = common.crc("8D406B902015A678D4D220AA4BDA") - assert checksum == "000000000000000000000000" + assert common.crc("8D406B902015A678D4D220AA4BDA") == 0 + assert common.crc('8d8960ed58bf053cf11bc5932b7d') == 0 + assert common.crc('8d45cab390c39509496ca9a32912') == 0 + assert common.crc('8d49d3d4e1089d00000000744c3b') == 0 + assert common.crc('8d74802958c904e6ef4ba0184d5c') == 0 + assert common.crc('8d4400cd9b0000b4f87000e71a10') == 0 + assert common.crc('8d4065de58a1054a7ef0218e226a') == 0 + + assert common.crc('c80b2dca34aa21dd821a04cb64d4') == 10719924 + assert common.crc('a800089d8094e33a6004e4b8a522') == 4805588 + assert common.crc('a8000614a50b6d32bed000bbe0ed') == 5659991 + assert common.crc('a0000410bc900010a40000f5f477') == 11727682 + assert common.crc('8d4ca251204994b1c36e60a5343d') == 16 + assert common.crc('b0001718c65632b0a82040715b65') == 353333 def test_crc_encode(): parity = common.crc("8D406B902015A678D4D220AA4BDA", encode=True) - assert common.hex2bin("AA4BDA") == parity + assert int("AA4BDA", 16) == parity def test_icao(): assert common.icao("8D406B902015A678D4D220AA4BDA") == "406B90" From bfdb8221a047e8fc4dd1c438a1db3727667b1d3e Mon Sep 17 00:00:00 2001 From: alcibiade <44736242+alcibiade@users.noreply.github.com> Date: Mon, 5 Nov 2018 21:55:45 +0100 Subject: [PATCH 3/3] Remove remaining Python3 explicit typing. The original version of this code is built against Python3.5+ and uses explicit typing. As I think the pyModeS supports Python2, I thought it was preferable top remove these types. --- pyModeS/decoder/fastcrc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyModeS/decoder/fastcrc.py b/pyModeS/decoder/fastcrc.py index d793d23..d767824 100644 --- a/pyModeS/decoder/fastcrc.py +++ b/pyModeS/decoder/fastcrc.py @@ -9,7 +9,7 @@ class BytesWrapper: self._bytes = [b for b in bytes.fromhex(hex)] - def byte_count(self) -> int: + def byte_count(self): return len(self._bytes) - 3 def get_bit(self, byte_index, bit_index): @@ -30,7 +30,7 @@ class BytesWrapper: return (self._bytes[-3] << 16) | (self._bytes[-2] << 8) | self._bytes[-1] -def crc(msg: str) -> int: +def crc(msg): msgbin = BytesWrapper(msg) for byte_index in range(msgbin.byte_count()):