class MTS(codec.BitFieldSet): ''' Modulation and Training Sequence. ''' DEF_LEN = 1 STRUCT = ( codec.BitField('nope', bl=1), codec.BitField('mod', bl=4), codec.BitField('tsc', bl=3), ) @staticmethod def get_burst_len(mod: int) -> int: ''' Get burst length by modulation type. ''' GMSK_BURST_LEN = 148 if (mod >> 2) == 0b00: # GMSK return 1 * GMSK_BURST_LEN elif (mod >> 2) == 0b11: # AQPSK return 2 * GMSK_BURST_LEN elif (mod >> 1) == 0b010: # 8-PSK return 3 * GMSK_BURST_LEN elif (mod >> 1) == 0b100: # 16QAM return 4 * GMSK_BURST_LEN elif (mod >> 1) == 0b101: # 32QAM return 5 * GMSK_BURST_LEN elif mod == 0b0110: # GMSK (Access Burst) return 1 * GMSK_BURST_LEN raise ValueError('Unknown modulation type')
def test_overflow(self): with self.assertRaises(codec.ProtocolError): s = codec.BitFieldSet(len=1, set=( codec.BitField('f6', bl=6), codec.BitField('f4', bl=4), ))
def __init__(self, ver: int, batched: bool = False): f = [ # Dynamically generated field list codec.BitField('ver', bl=4, val=ver) if not batched else codec.BitField.Spare(bl=4), # RFU codec.BitField.Spare(bl=1), codec.BitField('tn', bl=3), ] if ver >= 2: # TRXDv2 and higher f.append(codec.BitField('batch', bl=1)) f.append(codec.BitField.Spare(bl=1)) f.append(codec.BitField('trxn', bl=6)) codec.BitFieldSet.__init__(self, set=tuple(f))
class TestPDU(codec.Envelope): STRUCT = ( codec.BitFieldSet(len=2, set=( codec.BitField('ver', bl=4), codec.BitField('flag', bl=1), )), codec.Uint16BE('len'), codec.Buf('data'), codec.Buf('tail', len=2), ) def __init__(self, *args, **kw): codec.Envelope.__init__(self, *args, **kw) self.STRUCT[-3].get_val = lambda v: len(v['data']) self.STRUCT[-2].get_len = lambda v, _: v['len'] self.STRUCT[-1].get_pres = lambda v: bool(v['flag']) def check(self, vals: dict) -> None: if not vals['ver'] in (0, 1, 2): raise ValueError('Unknown version %d' % vals['ver'])
def test_len_auto(self): with self.subTest('1 + 2 = 3 bits => 1 octet (with padding)'): s = codec.BitFieldSet(set=( codec.BitField('f1', bl=1), codec.BitField('f2', bl=2), )) self.assertEqual(s.len, 1) with self.subTest('4 + 2 + 2 = 8 bits => 1 octet'): s = codec.BitFieldSet(set=( codec.BitField('f4', bl=4), codec.BitField('f2a', bl=2), codec.BitField('f2b', bl=2), )) self.assertEqual(s.len, 1) with self.subTest('12 + 4 + 2 = 18 bits => 3 octets (with padding)'): s = codec.BitFieldSet(set=( codec.BitField('f12', bl=12), codec.BitField('f4', bl=4), codec.BitField('f2', bl=2), )) self.assertEqual(s.len, 3)
class BitFieldSet(unittest.TestCase): S16 = codec.BitFieldSet(set=( codec.BitField('f4a', bl=4), codec.BitField('f8', bl=8), codec.BitField('f4b', bl=4), )) S8M = codec.BitFieldSet(order='msb', set=( codec.BitField('f4', bl=4), codec.BitField('f1', bl=1), codec.BitField('f3', bl=3), )) S8L = codec.BitFieldSet(order='lsb', set=( codec.BitField('f4', bl=4), codec.BitField('f1', bl=1), codec.BitField('f3', bl=3), )) S8V = codec.BitFieldSet(set=( codec.BitField('f4', bl=4, val=2), codec.BitField('f1', bl=1, val=0), codec.BitField('f3', bl=3), )) S8P = codec.BitFieldSet(set=( codec.BitField.Spare(bl=4), codec.BitField('f4', bl=4), )) @staticmethod def from_bytes(s: codec.BitFieldSet, data: bytes) -> dict: vals = {} s.from_bytes(vals, data) return vals def test_len_auto(self): with self.subTest('1 + 2 = 3 bits => 1 octet (with padding)'): s = codec.BitFieldSet(set=( codec.BitField('f1', bl=1), codec.BitField('f2', bl=2), )) self.assertEqual(s.len, 1) with self.subTest('4 + 2 + 2 = 8 bits => 1 octet'): s = codec.BitFieldSet(set=( codec.BitField('f4', bl=4), codec.BitField('f2a', bl=2), codec.BitField('f2b', bl=2), )) self.assertEqual(s.len, 1) with self.subTest('12 + 4 + 2 = 18 bits => 3 octets (with padding)'): s = codec.BitFieldSet(set=( codec.BitField('f12', bl=12), codec.BitField('f4', bl=4), codec.BitField('f2', bl=2), )) self.assertEqual(s.len, 3) def test_overflow(self): with self.assertRaises(codec.ProtocolError): s = codec.BitFieldSet(len=1, set=( codec.BitField('f6', bl=6), codec.BitField('f4', bl=4), )) def test_offset_mask(self): calc = lambda s: [(f.name, f.offset, f.mask) for f in s._fields] with self.subTest('16 bit total (MSB): f4a + f8 + f4b'): om = [('f4a', 8 + 4, 0x0f), ('f8', 4, 0xff), ('f4b', 0, 0x0f)] self.assertEqual(len(self.S16._fields), 3) self.assertEqual(calc(self.S16), om) with self.subTest('8 bit total (MSB): f4 + f1 + f3'): om = [('f4', 1 + 3, 0x0f), ('f1', 3, 0x01), ('f3', 0, 0x07)] self.assertEqual(len(self.S8M._fields), 3) self.assertEqual(calc(self.S8M), om) with self.subTest('8 bit total (LSB): f4 + f1 + f3'): om = [('f3', 1 + 4, 0x07), ('f1', 4, 0x01), ('f4', 0, 0x0f)] self.assertEqual(len(self.S8L._fields), 3) self.assertEqual(calc(self.S8L), om) with self.subTest('8 bit total (LSB): s4 + f4'): om = [(None, 4, 0x0f), ('f4', 0, 0x0f)] self.assertEqual(len(self.S8P._fields), 2) self.assertEqual(calc(self.S8P), om) def test_to_bytes(self): with self.subTest('16 bit total (MSB): f4a + f8 + f4b'): vals = {'f4a': 0x0f, 'f8': 0xff, 'f4b': 0x0f} self.assertEqual(self.S16.to_bytes(vals), b'\xff\xff') vals = {'f4a': 0x00, 'f8': 0x00, 'f4b': 0x00} self.assertEqual(self.S16.to_bytes(vals), b'\x00\x00') vals = {'f4a': 0x0f, 'f8': 0x00, 'f4b': 0x0f} self.assertEqual(self.S16.to_bytes(vals), b'\xf0\x0f') vals = {'f4a': 0x00, 'f8': 0xff, 'f4b': 0x00} self.assertEqual(self.S16.to_bytes(vals), b'\x0f\xf0') with self.subTest('8 bit total (MSB): f4 + f1 + f3'): vals = {'f4': 0x0f, 'f1': 0x01, 'f3': 0x07} self.assertEqual(self.S8M.to_bytes(vals), b'\xff') vals = {'f4': 0x00, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.S8M.to_bytes(vals), b'\x00') vals = {'f4': 0x0f, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.S8M.to_bytes(vals), b'\xf0') with self.subTest('8 bit total (LSB): f4 + f1 + f3'): vals = {'f4': 0x0f, 'f1': 0x01, 'f3': 0x07} self.assertEqual(self.S8L.to_bytes(vals), b'\xff') vals = {'f4': 0x00, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.S8L.to_bytes(vals), b'\x00') vals = {'f4': 0x0f, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.S8L.to_bytes(vals), b'\x0f') def test_from_bytes(self): pad = b'\xff' * 64 with self.subTest('16 bit total (MSB): f4a + f8 + f4b'): vals = {'f4a': 0x0f, 'f8': 0xff, 'f4b': 0x0f} self.assertEqual(self.from_bytes(self.S16, b'\xff\xff' + pad), vals) vals = {'f4a': 0x00, 'f8': 0x00, 'f4b': 0x00} self.assertEqual(self.from_bytes(self.S16, b'\x00\x00' + pad), vals) vals = {'f4a': 0x0f, 'f8': 0x00, 'f4b': 0x0f} self.assertEqual(self.from_bytes(self.S16, b'\xf0\x0f' + pad), vals) vals = {'f4a': 0x00, 'f8': 0xff, 'f4b': 0x00} self.assertEqual(self.from_bytes(self.S16, b'\x0f\xf0' + pad), vals) with self.subTest('8 bit total (MSB): f4 + f1 + f3'): vals = {'f4': 0x0f, 'f1': 0x01, 'f3': 0x07} self.assertEqual(self.from_bytes(self.S8M, b'\xff' + pad), vals) vals = {'f4': 0x00, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.from_bytes(self.S8M, b'\x00' + pad), vals) vals = {'f4': 0x0f, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.from_bytes(self.S8M, b'\xf0' + pad), vals) with self.subTest('8 bit total (LSB): f4 + f1 + f3'): vals = {'f4': 0x0f, 'f1': 0x01, 'f3': 0x07} self.assertEqual(self.from_bytes(self.S8L, b'\xff' + pad), vals) vals = {'f4': 0x00, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.from_bytes(self.S8L, b'\x00' + pad), vals) vals = {'f4': 0x0f, 'f1': 0x00, 'f3': 0x00} self.assertEqual(self.from_bytes(self.S8L, b'\x0f' + pad), vals) def test_to_bytes_val(self): with self.subTest('fixed values in absence of user-supplied values'): vals = {'f3': 0x00} # | { 'f4' : 2, 'f1' : 0 } self.assertEqual(self.S8V.to_bytes(vals), b'\x20') with self.subTest('fixed values take precedence'): vals = {'f4': 1, 'f1': 1, 'f3': 0} self.assertEqual(self.S8V.to_bytes(vals), b'\x20') def test_from_bytes_val(self): with self.assertRaises(codec.DecodeError): self.S8V.from_bytes({}, b'\xf0') # 'f4': 15 vs 2 with self.assertRaises(codec.DecodeError): self.S8V.from_bytes({}, b'\x08') # 'f1': 1 vs 0 # Field 'f3' takes any value, no exceptions shall be raised for i in range(8): data, vals = bytes([0x20 + i]), {'f4': 2, 'f1': 0, 'f3': i} self.assertEqual(self.from_bytes(self.S8V, data), vals) def test_to_bytes_spare(self): self.assertEqual(self.S8P.to_bytes({'f4': 0x00}), b'\x00') self.assertEqual(self.S8P.to_bytes({'f4': 0x0f}), b'\x0f') self.assertEqual(self.S8P.to_bytes({'f4': 0xff}), b'\x0f') def test_from_bytes_spare(self): self.assertEqual(self.from_bytes(self.S8P, b'\x00'), {'f4': 0x00}) self.assertEqual(self.from_bytes(self.S8P, b'\x0f'), {'f4': 0x0f}) self.assertEqual(self.from_bytes(self.S8P, b'\xff'), {'f4': 0x0f})