kirr has uploaded this change for review. (
https://gerrit.osmocom.org/c/osmocom-bb/+/40075?usp=email )
Change subject: trx_toolkit/data_msg: Switch *Msg to cdef classes
......................................................................
trx_toolkit/data_msg: Switch *Msg to cdef classes
- Put fields into the object struct; fields are now accessed directly
via that C-level struct instead of via __dict__ lookup
- Type .burst explicitly: it is bytearray for TxMsg and
array.array[int8_t] for RxMsg(*)
- switch to invoke several internal *Msg functions via C-level where
appropriate.
- cimport instead of import *Msg at the users
- type-annotate users appropriately
- switch Modulation .bl and .coding to be cdef-only, as the only users
of those fields are *Msg classes, and they now access Modulation via C.
(*) it was also considered to use either array.array[uint8_t] or
uint8_t[::1] memoryview for tx burst. Unfortunately that is not good
for performance as converting explicit types, e.g. bytearray, to
memoryview on a call is relatively costly, and also because cython
generates suboptimal code with lots of overhead when
passing/returning arrays. This way we use concrete type bytearray
for tx bursts which does not have those problems. For rx burst, we
cannot use bytearray because existing semantic requires rx_burst[i] to
return signed int8_t, which bytearray does not provide. So we use the
least bad array.array[int8_t] for that.
Change-Id: Icb63ec0a0d899e352400915bb163a3e2f354c280
---
M src/target/trx_toolkit/_fake_trx.pyx
M src/target/trx_toolkit/burst_fwd.pxd
M src/target/trx_toolkit/burst_fwd.pyx
M src/target/trx_toolkit/data_msg.pxd
M src/target/trx_toolkit/data_msg.pyx
M src/target/trx_toolkit/transceiver.pxd
M src/target/trx_toolkit/transceiver.pyx
7 files changed, 142 insertions(+), 74 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/75/40075/1
diff --git a/src/target/trx_toolkit/_fake_trx.pyx b/src/target/trx_toolkit/_fake_trx.pyx
index fd12863..5c55486 100644
--- a/src/target/trx_toolkit/_fake_trx.pyx
+++ b/src/target/trx_toolkit/_fake_trx.pyx
@@ -24,7 +24,7 @@
from burst_fwd cimport BurstForwarder
from _clck_gen cimport CLCKGen
from transceiver cimport Transceiver
-from data_msg cimport Modulation, ModGMSK
+from data_msg cimport Modulation, ModGMSK, TxMsg, RxMsg
from udp_link cimport _raise_oserr
import gsm_shared
cdef object TrainingSeqGMSK = gsm_shared.TrainingSeqGMSK
@@ -198,7 +198,7 @@
return False
- cdef _handle_data_msg_v1(self, src_msg, msg):
+ cdef _handle_data_msg_v1(self, TxMsg src_msg, RxMsg msg):
# C/I (Carrier-to-Interference ratio)
msg.ci = self.ci()
@@ -218,7 +218,7 @@
# Takes (partially initialized) TRXD Rx message,
# simulates RF path parameters (such as RSSI),
# and sends towards the L1
- cdef handle_data_msg(self, Transceiver src_trx, src_msg, msg):
+ cdef handle_data_msg(self, Transceiver src_trx, TxMsg src_msg, RxMsg msg):
if self.rf_muted:
msg.nope_ind = True
elif not msg.nope_ind:
diff --git a/src/target/trx_toolkit/burst_fwd.pxd b/src/target/trx_toolkit/burst_fwd.pxd
index 43b788c..486faa1 100644
--- a/src/target/trx_toolkit/burst_fwd.pxd
+++ b/src/target/trx_toolkit/burst_fwd.pxd
@@ -1,8 +1,9 @@
# cython: language_level=3
+from data_msg cimport TxMsg
from transceiver cimport Transceiver
cdef class BurstForwarder:
cdef list[Transceiver] trx_list
- cdef forward_msg(self, Transceiver src_trx, rx_msg)
+ cdef forward_msg(self, Transceiver src_trx, TxMsg rx_msg)
diff --git a/src/target/trx_toolkit/burst_fwd.pyx b/src/target/trx_toolkit/burst_fwd.pyx
index 4c8a0ef..d13f784 100644
--- a/src/target/trx_toolkit/burst_fwd.pyx
+++ b/src/target/trx_toolkit/burst_fwd.pyx
@@ -43,7 +43,7 @@
def __cinit__(self, list trx_list):
self.trx_list = trx_list
- cdef forward_msg(self, Transceiver src_trx, rx_msg):
+ cdef forward_msg(self, Transceiver src_trx, TxMsg rx_msg):
# Originating Transceiver may use frequency hopping,
# so let's precalculate its Tx frequency in advance
tx_freq = src_trx.get_tx_freq(rx_msg.fn)
diff --git a/src/target/trx_toolkit/data_msg.pxd b/src/target/trx_toolkit/data_msg.pxd
index a1b2192..cbdca4b 100644
--- a/src/target/trx_toolkit/data_msg.pxd
+++ b/src/target/trx_toolkit/data_msg.pxd
@@ -1,9 +1,13 @@
# -*- coding: utf-8 -*-
# cython: language_level=3
+from cython cimport final
+from cpython cimport array
+from libc.stdint cimport uint8_t
+
cdef class Modulation:
- cdef readonly int coding # Coding in TRXD header
- cdef readonly int bl # Burst length
+ cdef int coding # Coding in TRXD header
+ cdef int bl # Burst length
@staticmethod
cdef Modulation pick(int coding) # -> Modulation | None
@@ -13,3 +17,54 @@
cdef Modulation ModGMSK
+
+
+cdef class Msg:
+ cdef readonly int ver
+ cdef readonly object fn # int | None
+ cdef readonly object tn # int | None
+ cdef public bint nope_ind
+
+
+ @staticmethod
+ cdef int CHDR_LEN() noexcept
+ cdef int HDR_LEN(self) except -1
+
+ cdef append_hdr_to(self, bytearray buf)
+ cdef append_burst_to(self, bytearray buf)
+ cdef parse_hdr(self, msg)
+ cdef parse_burst(self, burst)
+ cdef _validate(self)
+
+
+@final
+cdef class TxMsg(Msg):
+ cdef public bytearray burst # | None
+ cdef readonly object pwr # int | None
+
+ cpdef validate(self)
+
+
+@final
+cdef class RxMsg(Msg):
+ cdef public array.array burst # array.array[b] | None
+
+ cdef public object rssi # int | None
+ cdef public object toa256 # int | None
+
+ # Version 0x01 specific
+ cdef public Modulation mod_type # Modulation | None
+
+ cdef public object tsc_set # int | None
+ cdef public object tsc # int | None
+ cdef public object ci # int | None
+
+
+ cdef _validate_burst_v0(self)
+ cdef _validate_burst_v1(self)
+ cdef validate_burst(self)
+ cpdef validate(self)
+ cdef int gen_mts(self) except -1
+
+ cdef _parse_burst_v0(self, burst)
+ cdef int parse_mts(self, uint8_t mts) except -1
diff --git a/src/target/trx_toolkit/data_msg.pyx b/src/target/trx_toolkit/data_msg.pyx
index 4b59fce..02194ba 100644
--- a/src/target/trx_toolkit/data_msg.pyx
+++ b/src/target/trx_toolkit/data_msg.pyx
@@ -18,6 +18,8 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
+from cython cimport final
+
import random
import struct
import abc
@@ -70,39 +72,42 @@
cdef int KNOWN_VERSION_MAX = 1 # 0, 1
-class Msg(abc.ABC):
+cdef class Msg:
''' TRXD (DATA) message coding API (common part). '''
# NOTE: up to 16 versions can be encoded
CHDR_VERSION_MAX = 0b1111
KNOWN_VERSIONS = tuple(range(KNOWN_VERSION_MAX+1))
- def __init__(self, fn = None, tn = None, ver = 0):
- self.burst = None # bytes|bytearray for ubit, array[b] for sbit, array[B] for usbit
+ def __cinit__(self, fn = -1, tn = -1, int ver = 0):
self.ver = ver
self.fn = fn
self.tn = tn
+ self.nope_ind = False
- @property
- def CHDR_LEN(self):
+ @staticmethod
+ cdef int CHDR_LEN() noexcept:
''' The common header length. '''
return 1 + 4 # (VER + TN) + FN
- @abc.abstractmethod
- def append_hdr_to(self, buf):
+ cdef int HDR_LEN(self) except -1:
+ raise NotImplementedError
+
+ cdef append_hdr_to(self, bytearray buf):
''' Generate message specific header by appending it to buf.
'''
+ raise NotImplementedError
- @abc.abstractmethod
- def parse_hdr(self, hdr):
+ cdef parse_hdr(self, msg):
''' Parse message specific header. '''
+ raise NotImplementedError
- @abc.abstractmethod
- def append_burst_to(self, buf):
+ cdef append_burst_to(self, bytearray buf):
''' Generate message specific burst by appending it to buf.
'''
+ raise NotImplementedError
- @abc.abstractmethod
- def parse_burst(self, burst):
- ''' Parse message specific burst. '''
+ cdef parse_burst(self, burst):
+ ''' Parse message specific burst to burst. '''
+ raise NotImplementedError
@abc.abstractmethod
def rand_burst(self):
@@ -165,7 +170,7 @@
_tab_ubit2sbit = array('b', [-127 if b else 127 for b in range(0x100)])
- def validate(self):
+ cdef _validate(self):
''' Validate the message fields (throws ValueError). '''
if not (0 <= self.ver <= KNOWN_VERSION_MAX):
@@ -187,7 +192,7 @@
''' Generate a TRX DATA message. '''
# Validate all the fields
- self.validate()
+ self._validate()
# Allocate an empty byte-array
buf = bytearray()
@@ -216,7 +221,7 @@
''' Parse a TRX DATA message. '''
# Make sure we have at least common header
- if len(msg) < self.CHDR_LEN:
+ if len(msg) < Msg.CHDR_LEN():
raise ValueError("Message is to short: missing common header")
# Parse the header version first
@@ -230,17 +235,17 @@
# Make sure we have the whole header,
# including the version specific fields
- if len(msg) < self.HDR_LEN:
+ if len(msg) < self.HDR_LEN():
raise ValueError("Message is to short: missing version specific header")
# Specific message part
self.parse_hdr(msg)
# Copy burst, skipping header
- if len(msg) == self.HDR_LEN:
+ if len(msg) == self.HDR_LEN():
self.burst = None
return
- msg_burst = memoryview(msg)[self.HDR_LEN:]
+ msg_burst = memoryview(msg)[self.HDR_LEN():]
self.parse_burst(msg_burst)
@@ -248,18 +253,19 @@
cdef int PWR_MIN = 0x00
cdef int PWR_MAX = 0xff
-class TxMsg(Msg):
+@final
+cdef class TxMsg(Msg):
''' Tx (L1 -> TRX) message coding API. '''
- # Specific message fields
- pwr = None
+ def __cinit__(self):
+ self.burst = None
+ self.pwr = None
- @property
- def HDR_LEN(self):
+ cdef int HDR_LEN(self) except -1:
''' Calculate header length depending on its version. '''
# Common header length
- length = self.CHDR_LEN
+ length = Msg.CHDR_LEN()
# Message specific header length
if self.ver in (0x00, 0x01):
@@ -269,11 +275,11 @@
return length
- def validate(self):
+ cpdef validate(self):
''' Validate the message fields (throws ValueError). '''
# Validate common fields
- Msg.validate(self)
+ Msg._validate(self)
if self.pwr is None:
raise ValueError("Tx Attenuation level is not set")
@@ -318,25 +324,25 @@
# Strip useless whitespace and return
return result.strip()
- def append_hdr_to(self, buf):
+ cdef append_hdr_to(self, bytearray buf):
''' Generate message specific header by appending it to buf.
'''
# Put power
buf.append(self.pwr)
- def parse_hdr(self, hdr):
+ cdef parse_hdr(self, hdr):
''' Parse message specific header part. '''
# Parse power level
self.pwr = hdr[5]
- def append_burst_to(self, buf):
+ cdef append_burst_to(self, bytearray buf):
''' Generate message specific burst by appending it to buf.
'''
# Copy burst 'as is'
return buf.extend(self.burst)
- def parse_burst(self, burst):
+ cdef parse_burst(self, burst):
''' Parse message specific burst. '''
length = len(burst)
@@ -389,27 +395,28 @@
# IDLE frame / nope detection indicator
cdef int NOPE_IND = (1 << 7)
-class RxMsg(Msg):
+
+@final
+cdef class RxMsg(Msg):
''' Rx (TRX -> L1) message coding API. '''
- # Specific message fields
- rssi = None
- toa256 = None
+ def __cinit__(self):
+ self.rssi = None
+ self.toa256 = None
- # Version 0x01 specific (default values)
- mod_type = ModGMSK
- nope_ind = False
+ # Version 0x01 specific (default values)
+ self.mod_type = ModGMSK
+ self.nope_ind = False
- tsc_set = None
- tsc = None
- ci = None
+ self.tsc_set = None
+ self.tsc = None
+ self.ci = None
- @property
- def HDR_LEN(self):
+ cdef int HDR_LEN(self) except -1:
''' Calculate header length depending on its version. '''
# Common header length
- length = self.CHDR_LEN
+ length = Msg.CHDR_LEN()
# Message specific header length
if self.ver == 0x00:
@@ -423,7 +430,7 @@
return length
- def _validate_burst_v0(self):
+ cdef _validate_burst_v0(self):
# Burst is mandatory
if self.burst is None:
raise ValueError("Rx burst bits are not set")
@@ -432,7 +439,7 @@
if len(self.burst) not in (GMSK_BURST_LEN, EDGE_BURST_LEN):
raise ValueError("Rx burst has odd length %u" % len(self.burst))
- def _validate_burst_v1(self):
+ cdef _validate_burst_v1(self):
# Burst is omitted in case of an IDLE / NOPE indication
if self.nope_ind and self.burst is None:
return
@@ -443,10 +450,11 @@
raise ValueError("Rx burst bits are not set")
# Burst length depends on modulation type
+ assert self.mod_type is not None
if len(self.burst) != self.mod_type.bl:
raise ValueError("Rx burst has odd length %u" % len(self.burst))
- def validate_burst(self):
+ cdef validate_burst(self):
''' Validate the burst (throws ValueError). '''
if self.ver == 0x00:
@@ -454,11 +462,11 @@
elif self.ver >= 0x01:
self._validate_burst_v1()
- def validate(self):
+ cpdef validate(self):
''' Validate the message header fields (throws ValueError).
'''
# Validate common fields
- Msg.validate(self)
+ Msg._validate(self)
if self.rssi is None:
raise ValueError("RSSI is not set")
@@ -481,10 +489,10 @@
raise ValueError("TSC set is not set")
if self.mod_type is ModGMSK:
- if self.tsc_set not in range(0, 4):
+ if not (0 <= self.tsc_set < 4):
raise ValueError("TSC set %d is out of range" % self.tsc_set)
else:
- if self.tsc_set not in range(0, 2):
+ if not (0 <= self.tsc_set < 2):
raise ValueError("TSC set %d is out of range" % self.tsc_set)
if self.tsc is None:
@@ -571,7 +579,7 @@
# Strip useless whitespace and return
return result.strip()
- def gen_mts(self):
+ cdef int gen_mts(self) except -1:
''' Encode Modulation and Training Sequence info. '''
# IDLE / nope indication has no MTS info
@@ -582,12 +590,13 @@
mts = self.tsc & 0b111
# MTS: . X X X X . . .
+ assert self.mod_type is not None
mts |= self.mod_type.coding << 3
mts |= self.tsc_set << 3
return mts
- def parse_mts(self, mts):
+ cdef int parse_mts(self, uint8_t mts) except -1:
''' Parse Modulation and Training Sequence info. '''
# IDLE / nope indication has no MTS info
@@ -596,7 +605,7 @@
self.mod_type = None
self.tsc_set = None
self.tsc = None
- return
+ return 0
# TSC: . . . . . X X X
self.tsc = mts & 0b111
@@ -612,7 +621,7 @@
self.mod_type = ModGMSK
self.tsc_set = mts & 0b11
- def append_hdr_to(self, buf):
+ cdef append_hdr_to(self, bytearray buf):
''' Generate message specific header by appending it to buf.
'''
# Put RSSI
@@ -630,7 +639,7 @@
# C/I: Carrier-to-Interference ratio (in centiBels)
buf += struct.pack(">h", self.ci)
- def parse_hdr(self, hdr):
+ cdef parse_hdr(self, hdr):
''' Parse message specific header part. '''
# Parse RSSI
@@ -646,13 +655,13 @@
# C/I: Carrier-to-Interference ratio (in centiBels)
self.ci = struct.unpack(">h", hdr[9:11])[0]
- def append_burst_to(self, buf):
+ cdef append_burst_to(self, bytearray buf):
''' Generate message specific burst appending it to buf. '''
# Convert soft-bits to unsigned soft-bits
buf.extend( self.sbit2usbit(self.burst) ) # XXX copies; can probably remove them with
numpy
- def _parse_burst_v0(self, burst):
+ cdef _parse_burst_v0(self, burst):
''' Parse message specific burst for header version 0. '''
bl = len(burst)
@@ -668,7 +677,7 @@
return burst[:self.mod_type.bl]
- def parse_burst(self, burst):
+ cdef parse_burst(self, burst):
''' Parse message specific burst. '''
burst = array('B', burst)
diff --git a/src/target/trx_toolkit/transceiver.pxd
b/src/target/trx_toolkit/transceiver.pxd
index 74ee982..2fb2fb7 100644
--- a/src/target/trx_toolkit/transceiver.pxd
+++ b/src/target/trx_toolkit/transceiver.pxd
@@ -1,6 +1,7 @@
# cython: language_level=3
from data_if cimport DATAInterface
+from data_msg cimport TxMsg, RxMsg
from burst_fwd cimport BurstForwarder
from _clck_gen cimport CLCKGen
@@ -41,13 +42,13 @@
object ctrl_if # CTRLInterfaceTRX
object clck_if # UDPLink
- list _tx_queue # [] of TxMsg
+ list[TxMsg] _tx_queue
cdef get_rx_freq(self, fn)
cpdef get_tx_freq(self, fn)
- cdef recv_data_msg(self)
- cdef send_data_msg(self, msg)
- cdef handle_data_msg(self, Transceiver src_trx, src_msg, msg)
+ cdef TxMsg recv_data_msg(self)
+ cdef send_data_msg(self, RxMsg msg)
+ cdef handle_data_msg(self, Transceiver src_trx, TxMsg src_msg, RxMsg msg)
cdef clck_tick(self, BurstForwarder fwd, fn)
diff --git a/src/target/trx_toolkit/transceiver.pyx
b/src/target/trx_toolkit/transceiver.pyx
index 19bdd09..e852eda 100644
--- a/src/target/trx_toolkit/transceiver.pyx
+++ b/src/target/trx_toolkit/transceiver.pyx
@@ -274,7 +274,7 @@
log.info("Stopping clock generator")
self.clck_gen.stop()
- cdef recv_data_msg(self):
+ cdef TxMsg recv_data_msg(self): # -> TxMsg | None
# Read and parse data from socket
msg = self.data_if.recv_tx_msg()
if not msg:
@@ -290,17 +290,19 @@
self._tx_queue.append(msg)
return msg
- cdef send_data_msg(self, msg):
+ cdef send_data_msg(self, RxMsg msg):
# TODO: make legacy mode configurable (via argv?)
self.data_if.send_msg(msg, legacy = True)
- cdef handle_data_msg(self, Transceiver src_trx, src_msg, msg):
+ cdef handle_data_msg(self, Transceiver src_trx, TxMsg src_msg, RxMsg msg):
self.send_data_msg(msg)
cdef clck_tick(self, BurstForwarder fwd, fn):
if not self.running:
return
+ cdef TxMsg msg
+
drop = []
emit = []
wait = []
--
To view, visit
https://gerrit.osmocom.org/c/osmocom-bb/+/40075?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: Icb63ec0a0d899e352400915bb163a3e2f354c280
Gerrit-Change-Number: 40075
Gerrit-PatchSet: 1
Gerrit-Owner: kirr <kirr(a)nexedi.com>
Gerrit-CC: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-CC: osmith <osmith(a)sysmocom.de>
Gerrit-CC: pespin <pespin(a)sysmocom.de>