diff options
Diffstat (limited to 'src/target/trx_toolkit/trxd_proto.py')
-rw-r--r-- | src/target/trx_toolkit/trxd_proto.py | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/src/target/trx_toolkit/trxd_proto.py b/src/target/trx_toolkit/trxd_proto.py new file mode 100644 index 00000000..a1bf1b53 --- /dev/null +++ b/src/target/trx_toolkit/trxd_proto.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- + +''' +TRXD PDU definitions based on declarative codec. +''' + +# TRX Toolkit +# +# (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +# Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> +# +# All Rights Reserved +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import codec + +class Header(codec.BitFieldSet): + ''' Header constructor for TRXD PDUs. ''' + + 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('shadow', bl=1) if batched + else codec.BitField.Spare(bl=1)) + f.append(codec.BitField('trxn', bl=6)) + + codec.BitFieldSet.__init__(self, set=tuple(f)) + +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') + +class BurstBits(codec.Buf): + ''' Soft-/hard-bits with variable length. ''' + + def __init__(self, name: str, *args, **kw): + codec.Buf.__init__(self, name, *args, **kw) + + # This field has a variable length that depends on moduletion type + self.get_len = lambda v, _: MTS.get_burst_len(v['mod']) + # This field is not present in NOPE / IDLE indications + self.get_pres = lambda v: not v['nope'] + + +class PDUv0Rx(codec.Envelope): + STRUCT = ( + Header(ver=0), + codec.Uint32BE('fn'), + codec.Uint('rssi', mult=-1), + codec.Int16BE('toa256'), + codec.Buf('soft-bits'), + codec.Buf('pad'), # Optional + ) + + def __init__(self, *args, **kw): + codec.Envelope.__init__(self, *args, **kw) + + # Field 'soft-bits' is either 148 (GMSK) or 444 (8-PSK) octets long + self.STRUCT[-2].get_len = lambda _, data: 444 if len(data) > 148 else 148 + +class PDUv0Tx(codec.Envelope): + STRUCT = ( + Header(ver=0), + codec.Uint32BE('fn'), + codec.Uint('pwr'), + codec.Buf('hard-bits'), + ) + + +class PDUv1Rx(codec.Envelope): + STRUCT = ( + Header(ver=1), + codec.Uint32BE('fn'), + codec.Uint('rssi', mult=-1), + codec.Int16BE('toa256'), + MTS(), + codec.Int16BE('cir'), + BurstBits('soft-bits'), + ) + +class PDUv1Tx(PDUv0Tx): + # Same structure as PDUv0Tx, only the version is different + STRUCT = ( + Header(ver=1), + *PDUv0Tx.STRUCT[1:] + ) + + +class PDUv2Rx(codec.Envelope): + class BPDU(codec.Envelope): + ''' Batched PDU part. ''' + STRUCT = ( + Header(ver=2, batched=True), + MTS(), + codec.Uint('rssi', mult=-1), + codec.Int16BE('toa256'), + codec.Int16BE('cir'), + BurstBits('soft-bits'), + ) + + STRUCT = ( + Header(ver=2), + MTS(), + codec.Uint('rssi', mult=-1), + codec.Int16BE('toa256'), + codec.Int16BE('cir'), + codec.Uint32BE('fn'), + BurstBits('soft-bits'), + codec.Sequence(item=BPDU()).f('bpdu'), + ) + +class PDUv2Tx(codec.Envelope): + class BPDU(codec.Envelope): + ''' Batched PDU part. ''' + STRUCT = ( + Header(ver=2, batched=True), + MTS(), + codec.Uint('pwr'), + codec.Int('scpir'), + codec.Spare('spare', len=3), + BurstBits('hard-bits'), + ) + + STRUCT = ( + Header(ver=2), + MTS(), + codec.Uint('pwr'), + codec.Int('scpir'), + codec.Spare('spare', len=3), + codec.Uint32BE('fn'), + BurstBits('hard-bits'), + codec.Sequence(item=BPDU()).f('bpdu'), + ) |