fixeria has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/39537?usp=email )
Change subject: trx_toolkit/*: Try to avoid copying burst data where possible ......................................................................
trx_toolkit/*: Try to avoid copying burst data where possible
Conveying burst data is the primary flow in data place of what fake_trx does, so the less copies we do, the less we make CPU loaded.
After this change I can finally run 1 BTS + 2 Mobile + 1 ccch_scan without hitting "Stale message ..." on fake_trx side. However fake_trx cpu load is close to 100% and there are internal clock overruns often:
[WARNING] clck_gen.py:97 CLCKGen: time overrun by -1385us; resetting the clock [WARNING] clck_gen.py:97 CLCKGen: time overrun by -2657us; resetting the clock [WARNING] clck_gen.py:97 CLCKGen: time overrun by -1264us; resetting the clock [WARNING] clck_gen.py:97 CLCKGen: time overrun by -2913us; resetting the clock [WARNING] clck_gen.py:97 CLCKGen: time overrun by -1836us; resetting the clock ...
This suggests that even though fake_trx.py + tx-queue started to work somehow, the rewrite of fake_trx in C, as explained in OS#6672, is still better to do.
Change-Id: I147da2f110dedc863361059c931f609c28a69e9c --- M src/target/trx_toolkit/data_if.py M src/target/trx_toolkit/data_msg.py 2 files changed, 27 insertions(+), 37 deletions(-)
Approvals: Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve fixeria: Looks good to me, approved
diff --git a/src/target/trx_toolkit/data_if.py b/src/target/trx_toolkit/data_if.py index 8e80894..5bc243f 100644 --- a/src/target/trx_toolkit/data_if.py +++ b/src/target/trx_toolkit/data_if.py @@ -67,7 +67,7 @@ # Attempt to parse a TRXD Tx message try: msg = TxMsg() - msg.parse_msg(bytearray(data)) + msg.parse_msg(data) except: log.error("Failed to parse a TRXD Tx message " "from R:%s:%u" % (self.remote_addr, self.remote_port)) diff --git a/src/target/trx_toolkit/data_msg.py b/src/target/trx_toolkit/data_msg.py index b5993d2..b1673f5 100644 --- a/src/target/trx_toolkit/data_msg.py +++ b/src/target/trx_toolkit/data_msg.py @@ -79,16 +79,16 @@ return 1 + 4 # (VER + TN) + FN
@abc.abstractmethod - def gen_hdr(self): - ''' Generate message specific header. ''' + def append_hdr_to(self, buf): + ''' Generate message specific header by appending it to buf. '''
@abc.abstractmethod def parse_hdr(self, hdr): ''' Parse message specific header. '''
@abc.abstractmethod - def gen_burst(self): - ''' Generate message specific burst. ''' + def append_burst_to(self, buf): + ''' Generate message specific burst by appending it to buf. '''
@abc.abstractmethod def parse_burst(self, burst): @@ -189,12 +189,11 @@ buf += struct.pack(">L", self.fn)
# Generate message specific header part - hdr = self.gen_hdr() - buf += hdr + self.append_hdr_to(buf)
# Generate burst if self.burst is not None: - buf += self.gen_burst() + self.append_burst_to(buf)
# This is a rudiment from (legacy) OpenBTS transceiver, # some L1 implementations still expect two dummy bytes. @@ -228,11 +227,11 @@ self.parse_hdr(msg)
# Copy burst, skipping header - msg_burst = msg[self.HDR_LEN:] - if len(msg_burst) > 0: - self.parse_burst(msg_burst) - else: + if len(msg) == self.HDR_LEN: self.burst = None + return + msg_burst = memoryview(msg)[self.HDR_LEN:] + self.parse_burst(msg_burst)
class TxMsg(Msg): ''' Tx (L1 -> TRX) message coding API. ''' @@ -308,28 +307,23 @@ # Strip useless whitespace and return return result.strip()
- def gen_hdr(self): - ''' Generate message specific header part. ''' - - # Allocate an empty byte-array - buf = bytearray() + def append_hdr_to(self, buf): + ''' Generate message specific header by appending it to buf. '''
# Put power buf.append(self.pwr)
- return buf - def parse_hdr(self, hdr): ''' Parse message specific header part. '''
# Parse power level self.pwr = hdr[5]
- def gen_burst(self): - ''' Generate message specific burst. ''' + def append_burst_to(self, buf): + ''' Generate message specific burst by appending it to buf. '''
# Copy burst 'as is' - return bytearray(self.burst) + return buf.extend(self.burst)
def parse_burst(self, burst): ''' Parse message specific burst. ''' @@ -338,9 +332,13 @@
# Distinguish between GSM and EDGE if length >= EDGE_BURST_LEN: - self.burst = bytearray(burst[:EDGE_BURST_LEN]) + if length > EDGE_BURST_LEN: + burst = memoryview(burst)[:EDGE_BURST_LEN] else: - self.burst = bytearray(burst[:GMSK_BURST_LEN]) + if length > GMSK_BURST_LEN: + burst = memoryview(burst)[:GMSK_BURST_LEN] + + self.burst = bytearray(burst)
def rand_burst(self, length = GMSK_BURST_LEN): ''' Generate a random message specific burst. ''' @@ -602,11 +600,8 @@ self.mod_type = Modulation.ModGMSK self.tsc_set = mts & 0b11
- def gen_hdr(self): - ''' Generate message specific header part. ''' - - # Allocate an empty byte-array - buf = bytearray() + def append_hdr_to(self, buf): + ''' Generate message specific header by appending it to buf. '''
# Put RSSI buf.append(-self.rssi) @@ -623,8 +618,6 @@ # C/I: Carrier-to-Interference ratio (in centiBels) buf += struct.pack(">h", self.ci)
- return buf - def parse_hdr(self, hdr): ''' Parse message specific header part. '''
@@ -641,14 +634,11 @@ # C/I: Carrier-to-Interference ratio (in centiBels) self.ci = struct.unpack(">h", hdr[9:11])[0]
- def gen_burst(self): - ''' Generate message specific burst. ''' + def append_burst_to(self, buf): + ''' Generate message specific burst appending it to buf. '''
# Convert soft-bits to unsigned soft-bits - burst_usbits = self.sbit2usbit(self.burst) - - # Encode to bytes - return bytearray(burst_usbits) + buf.extend( self.sbit2usbit(self.burst) ) # XXX copies; can probably remove them with numpy
def _parse_burst_v0(self, burst): ''' Parse message specific burst for header version 0. '''