118 lines
4.1 KiB
Python
118 lines
4.1 KiB
Python
import array
|
|
import copy
|
|
from enum import Enum
|
|
from xml.etree import ElementTree as ET
|
|
|
|
from urh.util import util
|
|
from urh.util.GenericCRC import GenericCRC
|
|
|
|
|
|
class WSPChecksum(object):
|
|
"""
|
|
This class implements the three checksums from Wireless Short Packet (WSP) standard
|
|
http://hes-standards.org/doc/SC25_WG1_N1493.pdf
|
|
"""
|
|
|
|
class ChecksumMode(Enum):
|
|
auto = 0
|
|
checksum4 = 1
|
|
checksum8 = 2
|
|
crc8 = 3
|
|
|
|
CRC_8_POLYNOMIAL = array.array("B", [1,
|
|
0, 0, 0, 0, 0, 1, 1, 1]) # x^8+x^2+x+1
|
|
|
|
def __init__(self, mode=ChecksumMode.auto):
|
|
self.mode = mode
|
|
self.caption = str(mode)
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, WSPChecksum):
|
|
return False
|
|
|
|
return self.mode == other.mode
|
|
|
|
def __hash__(self):
|
|
return hash(self.mode)
|
|
|
|
def calculate(self, msg: array.array) -> array.array:
|
|
"""
|
|
Get the checksum for a WSP message. There are three hashes possible:
|
|
1) 4 Bit Checksum - For Switch Telegram (RORG=5 or 6 and STATUS = 0x20 or 0x30)
|
|
2) 8 Bit Checksum: STATUS bit 2^7 = 0
|
|
3) 8 Bit CRC: STATUS bit 2^7 = 1
|
|
|
|
:param msg: the message without Preamble/SOF and EOF. Message starts with RORG and ends with CRC
|
|
"""
|
|
try:
|
|
if self.mode == self.ChecksumMode.auto:
|
|
if msg[0:4] == util.hex2bit("5") or msg[0:4] == util.hex2bit("6"):
|
|
# Switch telegram
|
|
return self.checksum4(msg)
|
|
|
|
status = msg[-16:-8]
|
|
if status[0]:
|
|
return self.crc8(msg[:-8]) # ignore trailing hash
|
|
else:
|
|
return self.checksum8(msg[:-8]) # ignore trailing hash
|
|
|
|
elif self.mode == self.ChecksumMode.checksum4:
|
|
return self.checksum4(msg)
|
|
elif self.mode == self.ChecksumMode.checksum8:
|
|
return self.checksum8(msg[:-8])
|
|
elif self.mode == self.ChecksumMode.crc8:
|
|
return self.crc8(msg[:-8])
|
|
|
|
except IndexError:
|
|
return None
|
|
|
|
@classmethod
|
|
def search_for_wsp_checksum(cls, bits_behind_sync):
|
|
data_start, data_stop, crc_start, crc_stop = 0, 0, 0, 0
|
|
|
|
if bits_behind_sync[-4:].tobytes() != array.array("B", [1, 0, 1, 1]).tobytes():
|
|
return 0, 0, 0, 0 # Check for EOF
|
|
|
|
rorg = bits_behind_sync[0:4].tobytes()
|
|
if rorg == array.array("B", [0, 1, 0, 1]).tobytes() or rorg == array.array("B", [0, 1, 1, 0]).tobytes():
|
|
# Switch telegram
|
|
if cls.checksum4(bits_behind_sync[-8:]).tobytes() == bits_behind_sync[-8:-4].tobytes():
|
|
crc_start = len(bits_behind_sync) - 8
|
|
crc_stop = len(bits_behind_sync) - 4
|
|
data_stop = crc_start
|
|
return data_start, data_stop, crc_start, crc_stop
|
|
|
|
# todo: Find crc8 and checksum8
|
|
|
|
return 0, 0, 0, 0
|
|
|
|
@classmethod
|
|
def checksum4(cls, bits: array.array) -> array.array:
|
|
hash = 0
|
|
val = copy.copy(bits)
|
|
val[-4:] = array.array("B", [False, False, False, False])
|
|
for i in range(0, len(val), 8):
|
|
hash += int("".join(map(str, map(int, val[i:i + 8]))), 2)
|
|
hash = (((hash & 0xf0) >> 4) + (hash & 0x0f)) & 0x0f
|
|
return array.array("B", list(map(bool, map(int, "{0:04b}".format(hash)))))
|
|
|
|
@classmethod
|
|
def checksum8(cls, bits: array.array) -> array.array:
|
|
hash = 0
|
|
for i in range(0, len(bits) - 8, 8):
|
|
hash += int("".join(map(str, map(int, bits[i:i + 8]))), 2)
|
|
return array.array("B", list(map(bool, map(int, "{0:08b}".format(hash % 256)))))
|
|
|
|
@classmethod
|
|
def crc8(cls, bits: array.array):
|
|
return array.array("B", GenericCRC(polynomial=cls.CRC_8_POLYNOMIAL).crc(bits))
|
|
|
|
def to_xml(self) -> ET.Element:
|
|
root = ET.Element("wsp_checksum")
|
|
root.set("mode", str(self.mode.name))
|
|
return root
|
|
|
|
@classmethod
|
|
def from_xml(cls, tag: ET.Element):
|
|
return WSPChecksum(mode=WSPChecksum.ChecksumMode[tag.get("mode", "auto")])
|