<p>fixeria <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmocom-bb/+/19228">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Jenkins Builder: Verified
pespin: Looks good to me, approved
laforge: Looks good to me, but someone else must approve
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">trx_toolkit/transceiver.py: implement the transmit burst queue<br><br>In order to reflect the UL/DL delay caused by the premature burst<br>scheduling (a.k.a. 'fn-advance') in a virtual environment, the<br>Transceiver implementation now queues all to be transmitted bursts,<br>so they remain in the queue until the appropriate time of transmission.<br><br>The API user is supposed to call recv_data_msg() in order to obtain<br>a L12TRX message on the TRXD (data) inteface, so it gets queued by<br>this function. Then, to ensure the timeous transmission, the user<br>of this implementation needs to call clck_tick() on each TDMA<br>frame. Both functions are thread-safe (queue mutex).<br><br>In a multi-trx configuration, the use of queue additionally ensures<br>proper burst aggregation on multiple TRXD connections, so all L12TRX<br>messages are guaranteed to be sent in the right order, i.e. with<br>monolithically-increasing TDMA frame numbers.<br><br>Of course, this change increases the overall CPU usage, given that<br>each transceiver gets its own queue, and we need to serve them all<br>on every TDMA frame. According to my measurements, when running<br>test cases from ttcn3-bts-test, the average load is ~50% higher<br>than what it used to be. Still not significantly high, though.<br><br>Change-Id: Ie66ef9667dc8d156ad578ce324941a816c07c105<br>Related: OS#4658, OS#4546<br>---<br>M src/target/trx_toolkit/fake_trx.py<br>M src/target/trx_toolkit/transceiver.py<br>2 files changed, 75 insertions(+), 4 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/target/trx_toolkit/fake_trx.py b/src/target/trx_toolkit/fake_trx.py</span><br><span>index 27c2b88..b487a93 100755</span><br><span>--- a/src/target/trx_toolkit/fake_trx.py</span><br><span>+++ b/src/target/trx_toolkit/fake_trx.py</span><br><span>@@ -388,6 +388,8 @@</span><br><span> </span><br><span> # Init shared clock generator</span><br><span> self.clck_gen = CLCKGen([])</span><br><span style="color: hsl(120, 100%, 40%);">+ # This method will be called on each TDMA frame</span><br><span style="color: hsl(120, 100%, 40%);">+ self.clck_gen.clck_handler = self.clck_handler</span><br><span> </span><br><span> # Power measurement emulation</span><br><span> # Noise: -120 .. -105</span><br><span>@@ -456,14 +458,18 @@</span><br><span> for trx in self.trx_list.trx_list:</span><br><span> # DATA interface</span><br><span> if trx.data_if.sock in r_event:</span><br><span style="color: hsl(0, 100%, 40%);">- msg = trx.recv_data_msg()</span><br><span style="color: hsl(0, 100%, 40%);">- if msg is not None:</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.forward_msg(trx, msg)</span><br><span style="color: hsl(120, 100%, 40%);">+ trx.recv_data_msg()</span><br><span> </span><br><span> # CTRL interface</span><br><span> if trx.ctrl_if.sock in r_event:</span><br><span> trx.ctrl_if.handle_rx()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # This method will be called by the clock thread</span><br><span style="color: hsl(120, 100%, 40%);">+ def clck_handler(self, fn):</span><br><span style="color: hsl(120, 100%, 40%);">+ # We assume that this list is immutable at run-time</span><br><span style="color: hsl(120, 100%, 40%);">+ for trx in self.trx_list.trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ trx.clck_tick(self.burst_fwd, fn)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def shutdown(self):</span><br><span> log.info("Shutting down...")</span><br><span> </span><br><span>diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py</span><br><span>index 474834d..3c1ddc8 100644</span><br><span>--- a/src/target/trx_toolkit/transceiver.py</span><br><span>+++ b/src/target/trx_toolkit/transceiver.py</span><br><span>@@ -24,6 +24,7 @@</span><br><span> # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span> </span><br><span> import logging as log</span><br><span style="color: hsl(120, 100%, 40%);">+import threading</span><br><span> </span><br><span> from ctrl_if_trx import CTRLInterfaceTRX</span><br><span> from data_if import DATAInterface</span><br><span>@@ -123,6 +124,37 @@</span><br><span> Transceiver and all its timeslots, so using in for the BTS side</span><br><span> does not make any sense (imagine BCCH hopping together with DCCH).</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ == The transmit burst queue</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ According to 3GPP 45.002, the time difference between Uplink and</span><br><span style="color: hsl(120, 100%, 40%);">+ Downlink corresponds to three TDMA timeslot periods. However,</span><br><span style="color: hsl(120, 100%, 40%);">+ in general the L1 implementations (such as osmo-bts-trx and trxcon)</span><br><span style="color: hsl(120, 100%, 40%);">+ never schedule to be transmitted bursts for the current TDMA frame</span><br><span style="color: hsl(120, 100%, 40%);">+ immediately. Instead, they are being scheduled prematurely.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The rationale is that both transceiver and the L1 implementation</span><br><span style="color: hsl(120, 100%, 40%);">+ are separete processes that are not perfectly synchronized in time.</span><br><span style="color: hsl(120, 100%, 40%);">+ Moreover, the transceiver needs some time to prepare a burst for</span><br><span style="color: hsl(120, 100%, 40%);">+ transmission. This is why the time difference between Uplink and</span><br><span style="color: hsl(120, 100%, 40%);">+ Downlink is actually much higher on practice (20 TDMA frame periods</span><br><span style="color: hsl(120, 100%, 40%);">+ by default, at the moment of writing this patch).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ In order to reflect that delay in a virtual environment, this</span><br><span style="color: hsl(120, 100%, 40%);">+ implementation, just like a normal transceiver (e.g. osmo-trx),</span><br><span style="color: hsl(120, 100%, 40%);">+ queues all to be transmitted (L12TRX) bursts, so hey remain in</span><br><span style="color: hsl(120, 100%, 40%);">+ the transmit queue until the appropriate time of transmission.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The API user is supposed to call recv_data_msg() in order to obtain</span><br><span style="color: hsl(120, 100%, 40%);">+ a L12TRX message on the TRXD (data) inteface, so it gets queued by</span><br><span style="color: hsl(120, 100%, 40%);">+ this function. Then, to ensure the timeous transmission, the user</span><br><span style="color: hsl(120, 100%, 40%);">+ of this implementation needs to call clck_tick() on each TDMA</span><br><span style="color: hsl(120, 100%, 40%);">+ frame. Both functions are thread-safe (queue mutex).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ In a multi-trx configuration, the use of queue additionally ensures</span><br><span style="color: hsl(120, 100%, 40%);">+ proper burst aggregation on multiple TRXD connections, so all L12TRX</span><br><span style="color: hsl(120, 100%, 40%);">+ messages are guaranteed to be sent in the right order, i.e. with</span><br><span style="color: hsl(120, 100%, 40%);">+ monolithically-increasing TDMA frame numbers.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> """</span><br><span> </span><br><span> def __init__(self, bind_addr, remote_addr, base_port, name = None,</span><br><span>@@ -178,6 +210,10 @@</span><br><span> # List of child transceivers</span><br><span> self.child_trx_list = TRXList()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # Tx (L12TRX) burst queue and mutex</span><br><span style="color: hsl(120, 100%, 40%);">+ self._tx_queue_lock = threading.Lock()</span><br><span style="color: hsl(120, 100%, 40%);">+ self._tx_queue = []</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def __str__(self):</span><br><span> desc = "%s:%d" % (self.remote_addr, self.base_port)</span><br><span> if self.child_idx > 0:</span><br><span>@@ -234,10 +270,12 @@</span><br><span> trx.running = True</span><br><span> elif event == "POWEROFF":</span><br><span> trx.running = False</span><br><span style="color: hsl(120, 100%, 40%);">+ trx.tx_queue_clear()</span><br><span> trx.disable_fh()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Reset frequency hopping parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ # Reset frequency hopping parameters, clear the queue</span><br><span> if event == "POWEROFF":</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tx_queue_clear()</span><br><span> self.disable_fh()</span><br><span> </span><br><span> # Trigger clock generator if required</span><br><span>@@ -275,8 +313,35 @@</span><br><span> "configured => dropping..." % (self, msg.desc_hdr()))</span><br><span> return None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # Enque the message, it will be sent later</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tx_queue_append(msg)</span><br><span> return msg</span><br><span> </span><br><span> def handle_data_msg(self, msg):</span><br><span> # TODO: make legacy mode configurable (via argv?)</span><br><span> self.data_if.send_msg(msg, legacy = True)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def tx_queue_append(self, msg):</span><br><span style="color: hsl(120, 100%, 40%);">+ with self._tx_queue_lock:</span><br><span style="color: hsl(120, 100%, 40%);">+ self._tx_queue.append(msg)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def tx_queue_clear(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ with self._tx_queue_lock:</span><br><span style="color: hsl(120, 100%, 40%);">+ # TODO: Python3: self._tx_queue.clear()</span><br><span style="color: hsl(120, 100%, 40%);">+ del self._tx_queue[:]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def clck_tick(self, fwd, fn):</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.running:</span><br><span style="color: hsl(120, 100%, 40%);">+ return</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ self._tx_queue_lock.acquire()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for msg in self._tx_queue:</span><br><span style="color: hsl(120, 100%, 40%);">+ if msg.fn == fn:</span><br><span style="color: hsl(120, 100%, 40%);">+ fwd.forward_msg(self, msg)</span><br><span style="color: hsl(120, 100%, 40%);">+ elif msg.fn < fn:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.warning("(%s) Stale TRXD message (fn=%u): %s"</span><br><span style="color: hsl(120, 100%, 40%);">+ % (self, fn, msg.desc_hdr()))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ self._tx_queue = [msg for msg in self._tx_queue if msg.fn > fn]</span><br><span style="color: hsl(120, 100%, 40%);">+ self._tx_queue_lock.release()</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmocom-bb/+/19228">change 19228</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmocom-bb/+/19228"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: osmocom-bb </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ie66ef9667dc8d156ad578ce324941a816c07c105 </div>
<div style="display:none"> Gerrit-Change-Number: 19228 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>