<p>Vadim Yanitskiy <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12264">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Jenkins Builder: Verified
Pau Espin Pedrol: Looks good to me, but someone else must approve
Harald Welte: Looks good to me, approved
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">trx_toolkit/fake_trx.py: refactor global class hierarchy<br><br>This change is a big step towards handling of multiple transceivers<br>in a single process, i.e. multiple MS and multiple BTS connections.<br><br>The old class hierarchy wasn't flexible enough, because initially<br>fake_trx was designed as a bridge between OsmocomBB and OsmoBTS,<br>but not as the burst router. There were two separate, but 90%<br>similar implementations of the CTRL interface, two variations<br>of each simulation parameter - one for UL, another for DL.<br><br>The following new classes are introduced:<br><br> - Transceiver - represents a single transceiver, that can be<br> used as for the BTS side, as for the MS side. Each instance<br> has its own CTRL, DATA, and (optionally) CLCK interfaces,<br> among with basic state variables, such as both RX / TX freq.,<br> power state (running or idle) and list of active timeslots.<br><br> - CTRLInterfaceTRX - unified control interface handler for<br> common transceiver management commands, such as POWERON,<br> RXTUNE, and SETSLOT. Deprecates both CTRLInterface{BB|BTS}.<br><br> - FakeTRX - basically, a child of Transceiver, extended with<br> RF path (burst loss, RSSI, TA, ToA) simulation. Implements<br> a custom CTRL command handler for CTRLInterfaceTRX.<br><br>The following classes were refactored:<br><br> - BurstForwarder - still performs burst forwarding, but now<br> it doesn't store any simulation parameters, and doesn't<br> know who is BTS, and who is MS. Actually, BurstForwarder<br> transforms each L12TRX message into a TRX2L1 message, and<br> dispatches it between running transceivers with matching<br> RX frequency and matching timeslot.<br><br> - FakePM - still generates random RSSI values, but doesn't<br> distinguish between MS and BTS anymore. As soon as a<br> measurement request is received, it attempts to find<br> at least one running TRX on a given frequency.<br><br>Please note that fake_trx.py still does handle only a single pair<br>of MS and BTS. No regressions have been observed. Both new and<br>refactored classes were documented.<br><br>Change-Id: Ice44e2b22566b3652ef6d43896055963b13ab185<br>Related: OS#3667<br>---<br>M src/target/trx_toolkit/burst_fwd.py<br>D src/target/trx_toolkit/ctrl_if_bb.py<br>D src/target/trx_toolkit/ctrl_if_bts.py<br>A src/target/trx_toolkit/ctrl_if_trx.py<br>M src/target/trx_toolkit/fake_pm.py<br>M src/target/trx_toolkit/fake_trx.py<br>A src/target/trx_toolkit/transceiver.py<br>7 files changed, 652 insertions(+), 779 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/target/trx_toolkit/burst_fwd.py b/src/target/trx_toolkit/burst_fwd.py</span><br><span>index 3cb6acd..38ce18f 100644</span><br><span>--- a/src/target/trx_toolkit/burst_fwd.py</span><br><span>+++ b/src/target/trx_toolkit/burst_fwd.py</span><br><span>@@ -2,7 +2,7 @@</span><br><span> # -*- coding: utf-8 -*-</span><br><span> </span><br><span> # TRX Toolkit</span><br><span style="color: hsl(0, 100%, 40%);">-# BTS <-> BB burst forwarding</span><br><span style="color: hsl(120, 100%, 40%);">+# Burst forwarding between transceivers</span><br><span> #</span><br><span> # (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span> #</span><br><span>@@ -23,321 +23,62 @@</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(0, 100%, 40%);">-import random</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-from data_msg import *</span><br><span> </span><br><span> class BurstForwarder:</span><br><span style="color: hsl(0, 100%, 40%);">- """ Performs burst forwarding and preprocessing between MS and BTS.</span><br><span style="color: hsl(120, 100%, 40%);">+ """ Performs burst forwarding between transceivers.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- == Pass-filtering parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ BurstForwarder distributes bursts between the list of given</span><br><span style="color: hsl(120, 100%, 40%);">+ FakeTRX (Transceiver) instances depending on the following</span><br><span style="color: hsl(120, 100%, 40%);">+ parameters of each transceiver:</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- BurstForwarder may drop or pass an UL/DL burst depending</span><br><span style="color: hsl(0, 100%, 40%);">- on the following parameters:</span><br><span style="color: hsl(120, 100%, 40%);">+ - execution state (running or idle),</span><br><span style="color: hsl(120, 100%, 40%);">+ - actual RX / TX frequencies,</span><br><span style="color: hsl(120, 100%, 40%);">+ - list of active timeslots.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- - bts_freq / bb_freq - the current BTS / MS frequency</span><br><span style="color: hsl(0, 100%, 40%);">- that was set using RXTUNE control command. By default,</span><br><span style="color: hsl(0, 100%, 40%);">- both freq. values are set to None, so nothing is being</span><br><span style="color: hsl(0, 100%, 40%);">- forwarded (i.e. bursts are getting dropped).</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- FIXME: currently, we don't care about TXTUNE command</span><br><span style="color: hsl(0, 100%, 40%);">- and transmit frequencies. It would be great to distinguish</span><br><span style="color: hsl(0, 100%, 40%);">- between RX and TX frequencies for both BTS and MS.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- - ts_pass_list - the list of active (i.e. configured)</span><br><span style="color: hsl(0, 100%, 40%);">- timeslot numbers for the MS. A timeslot can be activated</span><br><span style="color: hsl(0, 100%, 40%);">- or deactivated using SETSLOT control command from the MS.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- FIXME: there is no such list for the BTS side.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- == Preprocessing and measurement simulation</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- Since this is a virtual environment, we can simulate different</span><br><span style="color: hsl(0, 100%, 40%);">- parameters of a virtual RF interface:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- - ToA (Timing of Arrival) - measured difference between expected</span><br><span style="color: hsl(0, 100%, 40%);">- and actual time of burst arrival in units of 1/256 of GSM symbol</span><br><span style="color: hsl(0, 100%, 40%);">- periods. A pair of both base and threshold values defines a range</span><br><span style="color: hsl(0, 100%, 40%);">- of ToA value randomization:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- DL: from (toa256_dl_base - toa256_dl_threshold)</span><br><span style="color: hsl(0, 100%, 40%);">- to (toa256_dl_base + toa256_dl_threshold),</span><br><span style="color: hsl(0, 100%, 40%);">- UL: from (toa256_ul_base - toa256_ul_threshold)</span><br><span style="color: hsl(0, 100%, 40%);">- to (toa256_ul_base + toa256_ul_threshold).</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- - RSSI (Received Signal Strength Indication) - measured "power" of</span><br><span style="color: hsl(0, 100%, 40%);">- the signal (per burst) in dBm. A pair of both base and threshold</span><br><span style="color: hsl(0, 100%, 40%);">- values defines a range of RSSI value randomization:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- DL: from (rssi_dl_base - rssi_dl_threshold)</span><br><span style="color: hsl(0, 100%, 40%);">- to (rssi_dl_base + rssi_dl_threshold),</span><br><span style="color: hsl(0, 100%, 40%);">- UL: from (rssi_ul_base - rssi_ul_threshold)</span><br><span style="color: hsl(0, 100%, 40%);">- to (rssi_ul_base + rssi_ul_threshold).</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- Please note that the randomization of both RSSI and ToA</span><br><span style="color: hsl(0, 100%, 40%);">- is optional, and can be enabled from the control interface.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- === Timing Advance handling</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- The BTS is using ToA measurements for UL bursts in order to calculate</span><br><span style="color: hsl(0, 100%, 40%);">- Timing Advance value, that is then indicated to a MS, which in its turn</span><br><span style="color: hsl(0, 100%, 40%);">- shall apply this value to the transmitted signal in order to compensate</span><br><span style="color: hsl(0, 100%, 40%);">- the delay. Basically, every burst is transmitted in advance defined by</span><br><span style="color: hsl(0, 100%, 40%);">- the indicated Timing Advance value. The valid range is 0..63, where</span><br><span style="color: hsl(0, 100%, 40%);">- each unit means one GSM symbol advance. The actual Timing Advance value</span><br><span style="color: hsl(0, 100%, 40%);">- is set using SETTA control command from MS. By default, it's set to 0.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- === Path loss simulation - burst dropping</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- In some cases, e.g. due to a weak signal or high interference, a burst</span><br><span style="color: hsl(0, 100%, 40%);">- can be lost, i.e. not detected by the receiver. This can also be</span><br><span style="color: hsl(0, 100%, 40%);">- simulated using FAKE_DROP command on both control interfaces:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- - burst_{dl|ul}_drop_amount - the amount of DL/UL bursts</span><br><span style="color: hsl(0, 100%, 40%);">- to be dropped (i.e. not forwarded towards the MS/BTS),</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- - burst_{dl|ul}_drop_period - drop every X DL/UL burst, e.g.</span><br><span style="color: hsl(0, 100%, 40%);">- 1 - drop every consequent burst, 2 - drop every second burst, etc.</span><br><span style="color: hsl(120, 100%, 40%);">+ Each to be distributed L12TRX message is being transformed</span><br><span style="color: hsl(120, 100%, 40%);">+ into a TRX2L1 message, and then forwarded to transceivers</span><br><span style="color: hsl(120, 100%, 40%);">+ with partially initialized header. All uninitialized header</span><br><span style="color: hsl(120, 100%, 40%);">+ fields (such as rssi and toa256) shall be set by each</span><br><span style="color: hsl(120, 100%, 40%);">+ transceiver individually before sending towards the L1.</span><br><span> </span><br><span> """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- def __init__(self, bts_link, bb_link):</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_link = bts_link</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_link = bb_link</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, trx_list = []):</span><br><span style="color: hsl(120, 100%, 40%);">+ # List of Transceiver instances</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx_list = trx_list</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Init default parameters</span><br><span style="color: hsl(0, 100%, 40%);">- self.reset_dl()</span><br><span style="color: hsl(0, 100%, 40%);">- self.reset_ul()</span><br><span style="color: hsl(120, 100%, 40%);">+ def add_trx(self, trx):</span><br><span style="color: hsl(120, 100%, 40%);">+ if trx in self.trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("TRX is already in the list")</span><br><span style="color: hsl(120, 100%, 40%);">+ return</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Initialize (or reset to) default parameters for Downlink</span><br><span style="color: hsl(0, 100%, 40%);">- def reset_dl(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Unset current DL freq.</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_freq = None</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx_list.append(trx)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Indicated RSSI / ToA values</span><br><span style="color: hsl(0, 100%, 40%);">- self.toa256_dl_base = 0</span><br><span style="color: hsl(0, 100%, 40%);">- self.rssi_dl_base = -60</span><br><span style="color: hsl(120, 100%, 40%);">+ def del_trx(self, trx):</span><br><span style="color: hsl(120, 100%, 40%);">+ if trx not in self.trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("TRX is not in the list")</span><br><span style="color: hsl(120, 100%, 40%);">+ return</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # RSSI / ToA randomization threshold</span><br><span style="color: hsl(0, 100%, 40%);">- self.toa256_dl_threshold = 0</span><br><span style="color: hsl(0, 100%, 40%);">- self.rssi_dl_threshold = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx_list.remove(trx)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation (burst dropping)</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_dl_drop_amount = 0</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_dl_drop_period = 1</span><br><span style="color: hsl(120, 100%, 40%);">+ def forward_msg(self, src_trx, rx_msg):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Transform from L12TRX to TRX2L1</span><br><span style="color: hsl(120, 100%, 40%);">+ tx_msg = rx_msg.gen_trx2l1()</span><br><span style="color: hsl(120, 100%, 40%);">+ if tx_msg is None:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("Forwarding failed, could not transform "</span><br><span style="color: hsl(120, 100%, 40%);">+ "message (%s) => dropping..." % rx_msg.desc_hdr())</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Initialize (or reset to) default parameters for Uplink</span><br><span style="color: hsl(0, 100%, 40%);">- def reset_ul(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Unset current DL freq.</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_freq = None</span><br><span style="color: hsl(120, 100%, 40%);">+ # Iterate over all known transceivers</span><br><span style="color: hsl(120, 100%, 40%);">+ for trx in self.trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ if trx == src_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Indicated RSSI / ToA values</span><br><span style="color: hsl(0, 100%, 40%);">- self.rssi_ul_base = -70</span><br><span style="color: hsl(0, 100%, 40%);">- self.toa256_ul_base = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ # Check transceiver state</span><br><span style="color: hsl(120, 100%, 40%);">+ if not trx.running:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span style="color: hsl(120, 100%, 40%);">+ if trx.rx_freq != src_trx.tx_freq:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span style="color: hsl(120, 100%, 40%);">+ if tx_msg.tn not in trx.ts_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # RSSI / ToA randomization threshold</span><br><span style="color: hsl(0, 100%, 40%);">- self.toa256_ul_threshold = 0</span><br><span style="color: hsl(0, 100%, 40%);">- self.rssi_ul_threshold = 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation (burst dropping)</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_ul_drop_amount = 0</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_ul_drop_period = 1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Init timeslot filter (drop everything by default)</span><br><span style="color: hsl(0, 100%, 40%);">- self.ts_pass_list = []</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Reset Timing Advance value</span><br><span style="color: hsl(0, 100%, 40%);">- self.ta = 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Converts TA value from symbols to</span><br><span style="color: hsl(0, 100%, 40%);">- # units of 1/256 of GSM symbol periods</span><br><span style="color: hsl(0, 100%, 40%);">- def calc_ta256(self):</span><br><span style="color: hsl(0, 100%, 40%);">- return self.ta * 256</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculates a random ToA value for Downlink bursts</span><br><span style="color: hsl(0, 100%, 40%);">- def calc_dl_toa256(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Check if randomization is required</span><br><span style="color: hsl(0, 100%, 40%);">- if self.toa256_dl_threshold is 0:</span><br><span style="color: hsl(0, 100%, 40%);">- return self.toa256_dl_base</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculate a range for randomization</span><br><span style="color: hsl(0, 100%, 40%);">- toa256_min = self.toa256_dl_base - self.toa256_dl_threshold</span><br><span style="color: hsl(0, 100%, 40%);">- toa256_max = self.toa256_dl_base + self.toa256_dl_threshold</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Generate a random ToA value</span><br><span style="color: hsl(0, 100%, 40%);">- toa256 = random.randint(toa256_min, toa256_max)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return toa256</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculates a random ToA value for Uplink bursts</span><br><span style="color: hsl(0, 100%, 40%);">- def calc_ul_toa256(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Check if randomization is required</span><br><span style="color: hsl(0, 100%, 40%);">- if self.toa256_ul_threshold is 0:</span><br><span style="color: hsl(0, 100%, 40%);">- return self.toa256_ul_base</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculate a range for randomization</span><br><span style="color: hsl(0, 100%, 40%);">- toa256_min = self.toa256_ul_base - self.toa256_ul_threshold</span><br><span style="color: hsl(0, 100%, 40%);">- toa256_max = self.toa256_ul_base + self.toa256_ul_threshold</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Generate a random ToA value</span><br><span style="color: hsl(0, 100%, 40%);">- toa256 = random.randint(toa256_min, toa256_max)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return toa256</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculates a random RSSI value for Downlink bursts</span><br><span style="color: hsl(0, 100%, 40%);">- def calc_dl_rssi(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Check if randomization is required</span><br><span style="color: hsl(0, 100%, 40%);">- if self.rssi_dl_threshold is 0:</span><br><span style="color: hsl(0, 100%, 40%);">- return self.rssi_dl_base</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculate a range for randomization</span><br><span style="color: hsl(0, 100%, 40%);">- rssi_min = self.rssi_dl_base - self.rssi_dl_threshold</span><br><span style="color: hsl(0, 100%, 40%);">- rssi_max = self.rssi_dl_base + self.rssi_dl_threshold</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Generate a random RSSI value</span><br><span style="color: hsl(0, 100%, 40%);">- return random.randint(rssi_min, rssi_max)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculates a random RSSI value for Uplink bursts</span><br><span style="color: hsl(0, 100%, 40%);">- def calc_ul_rssi(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Check if randomization is required</span><br><span style="color: hsl(0, 100%, 40%);">- if self.rssi_ul_threshold is 0:</span><br><span style="color: hsl(0, 100%, 40%);">- return self.rssi_ul_base</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculate a range for randomization</span><br><span style="color: hsl(0, 100%, 40%);">- rssi_min = self.rssi_ul_base - self.rssi_ul_threshold</span><br><span style="color: hsl(0, 100%, 40%);">- rssi_max = self.rssi_ul_base + self.rssi_ul_threshold</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Generate a random RSSI value</span><br><span style="color: hsl(0, 100%, 40%);">- return random.randint(rssi_min, rssi_max)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # DL path loss simulation</span><br><span style="color: hsl(0, 100%, 40%);">- def path_loss_sim_dl(self, msg):</span><br><span style="color: hsl(0, 100%, 40%);">- # Burst dropping</span><br><span style="color: hsl(0, 100%, 40%);">- if self.burst_dl_drop_amount > 0:</span><br><span style="color: hsl(0, 100%, 40%);">- if msg.fn % self.burst_dl_drop_period == 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Simulation: dropping DL burst (fn=%u %% %u == 0)"</span><br><span style="color: hsl(0, 100%, 40%);">- % (msg.fn, self.burst_dl_drop_period))</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_dl_drop_amount -= 1</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return msg</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # UL path loss simulation</span><br><span style="color: hsl(0, 100%, 40%);">- def path_loss_sim_ul(self, msg):</span><br><span style="color: hsl(0, 100%, 40%);">- # Burst dropping</span><br><span style="color: hsl(0, 100%, 40%);">- if self.burst_ul_drop_amount > 0:</span><br><span style="color: hsl(0, 100%, 40%);">- if msg.fn % self.burst_ul_drop_period == 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Simulation: dropping UL burst (fn=%u %% %u == 0)"</span><br><span style="color: hsl(0, 100%, 40%);">- % (msg.fn, self.burst_ul_drop_period))</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_ul_drop_amount -= 1</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return msg</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # DL burst preprocessing</span><br><span style="color: hsl(0, 100%, 40%);">- def preprocess_dl_burst(self, msg):</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculate both RSSI and ToA values</span><br><span style="color: hsl(0, 100%, 40%);">- msg.toa256 = self.calc_dl_toa256()</span><br><span style="color: hsl(0, 100%, 40%);">- msg.rssi = self.calc_dl_rssi()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # UL burst preprocessing</span><br><span style="color: hsl(0, 100%, 40%);">- def preprocess_ul_burst(self, msg):</span><br><span style="color: hsl(0, 100%, 40%);">- # Calculate both RSSI and ToA values,</span><br><span style="color: hsl(0, 100%, 40%);">- # also apply Timing Advance</span><br><span style="color: hsl(0, 100%, 40%);">- msg.toa256 = self.calc_ul_toa256()</span><br><span style="color: hsl(0, 100%, 40%);">- msg.toa256 -= self.calc_ta256()</span><br><span style="color: hsl(0, 100%, 40%);">- msg.rssi = self.calc_ul_rssi()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Converts a L12TRX message to TRX2L1 message</span><br><span style="color: hsl(0, 100%, 40%);">- def transform_msg(self, msg_raw):</span><br><span style="color: hsl(0, 100%, 40%);">- # Attempt to parse a message</span><br><span style="color: hsl(0, 100%, 40%);">- try:</span><br><span style="color: hsl(0, 100%, 40%);">- msg_l12trx = DATAMSG_L12TRX()</span><br><span style="color: hsl(0, 100%, 40%);">- msg_l12trx.parse_msg(bytearray(msg_raw))</span><br><span style="color: hsl(0, 100%, 40%);">- except:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("Dropping unhandled DL message...")</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Compose a new message for L1</span><br><span style="color: hsl(0, 100%, 40%);">- return msg_l12trx.gen_trx2l1()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Downlink handler: BTS -> BB</span><br><span style="color: hsl(0, 100%, 40%);">- def bts2bb(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Read data from socket</span><br><span style="color: hsl(0, 100%, 40%);">- data, addr = self.bts_link.sock.recvfrom(512)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # BB is not connected / tuned</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bb_freq is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Freq. filter</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bb_freq != self.bts_freq:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Process a message</span><br><span style="color: hsl(0, 100%, 40%);">- msg = self.transform_msg(data)</span><br><span style="color: hsl(0, 100%, 40%);">- if msg is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Timeslot filter</span><br><span style="color: hsl(0, 100%, 40%);">- if msg.tn not in self.ts_pass_list:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation</span><br><span style="color: hsl(0, 100%, 40%);">- msg = self.path_loss_sim_dl(msg)</span><br><span style="color: hsl(0, 100%, 40%);">- if msg is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Burst preprocessing</span><br><span style="color: hsl(0, 100%, 40%);">- self.preprocess_dl_burst(msg)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Validate and generate the payload</span><br><span style="color: hsl(0, 100%, 40%);">- payload = msg.gen_msg()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Append two unused bytes at the end</span><br><span style="color: hsl(0, 100%, 40%);">- # in order to keep the compatibility</span><br><span style="color: hsl(0, 100%, 40%);">- payload += bytearray(2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Send burst to BB</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_link.send(payload)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Uplink handler: BB -> BTS</span><br><span style="color: hsl(0, 100%, 40%);">- def bb2bts(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Read data from socket</span><br><span style="color: hsl(0, 100%, 40%);">- data, addr = self.bb_link.sock.recvfrom(512)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # BTS is not connected / tuned</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bts_freq is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Freq. filter</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bb_freq != self.bts_freq:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Process a message</span><br><span style="color: hsl(0, 100%, 40%);">- msg = self.transform_msg(data)</span><br><span style="color: hsl(0, 100%, 40%);">- if msg is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Timeslot filter</span><br><span style="color: hsl(0, 100%, 40%);">- if msg.tn not in self.ts_pass_list:</span><br><span style="color: hsl(0, 100%, 40%);">- log.warning("TS %u is not configured, dropping UL burst..." % msg.tn)</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation</span><br><span style="color: hsl(0, 100%, 40%);">- msg = self.path_loss_sim_ul(msg)</span><br><span style="color: hsl(0, 100%, 40%);">- if msg is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Burst preprocessing</span><br><span style="color: hsl(0, 100%, 40%);">- self.preprocess_ul_burst(msg)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Validate and generate the payload</span><br><span style="color: hsl(0, 100%, 40%);">- payload = msg.gen_msg()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Append two unused bytes at the end</span><br><span style="color: hsl(0, 100%, 40%);">- # in order to keep the compatibility</span><br><span style="color: hsl(0, 100%, 40%);">- payload += bytearray(2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Send burst to BTS</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_link.send(payload)</span><br><span style="color: hsl(120, 100%, 40%);">+ trx.send_data_msg(src_trx, tx_msg)</span><br><span>diff --git a/src/target/trx_toolkit/ctrl_if_bb.py b/src/target/trx_toolkit/ctrl_if_bb.py</span><br><span>deleted file mode 100644</span><br><span>index d25aa30..0000000</span><br><span>--- a/src/target/trx_toolkit/ctrl_if_bb.py</span><br><span>+++ /dev/null</span><br><span>@@ -1,219 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python2</span><br><span style="color: hsl(0, 100%, 40%);">-# -*- coding: utf-8 -*-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-# TRX Toolkit</span><br><span style="color: hsl(0, 100%, 40%);">-# CTRL interface implementation (OsmocomBB specific)</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# All Rights Reserved</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(0, 100%, 40%);">-# it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(0, 100%, 40%);">-# the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(0, 100%, 40%);">-# (at your option) any later version.</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(0, 100%, 40%);">-# but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(0, 100%, 40%);">-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span><br><span style="color: hsl(0, 100%, 40%);">-# GNU General Public License for more details.</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(0, 100%, 40%);">-# with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(0, 100%, 40%);">-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-import logging as log</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-from ctrl_if import CTRLInterface</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class CTRLInterfaceBB(CTRLInterface):</span><br><span style="color: hsl(0, 100%, 40%);">- # Internal state variables</span><br><span style="color: hsl(0, 100%, 40%);">- trx_started = False</span><br><span style="color: hsl(0, 100%, 40%);">- burst_fwd = None</span><br><span style="color: hsl(0, 100%, 40%);">- rx_freq = None</span><br><span style="color: hsl(0, 100%, 40%);">- tx_freq = None</span><br><span style="color: hsl(0, 100%, 40%);">- pm = None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- def __init__(self, *udp_link_args):</span><br><span style="color: hsl(0, 100%, 40%);">- CTRLInterface.__init__(self, *udp_link_args)</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Init CTRL interface for BB (%s)" % self.desc_link())</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- def parse_cmd(self, request):</span><br><span style="color: hsl(0, 100%, 40%);">- # Power control</span><br><span style="color: hsl(0, 100%, 40%);">- if self.verify_cmd(request, "POWERON", 0):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv POWERON CMD")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Ensure transceiver isn't working</span><br><span style="color: hsl(0, 100%, 40%);">- if self.trx_started:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("Transceiver already started")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Ensure RX / TX freq. are set</span><br><span style="color: hsl(0, 100%, 40%);">- if (self.rx_freq is None) or (self.tx_freq is None):</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("RX / TX freq. are not set")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Starting transceiver...")</span><br><span style="color: hsl(0, 100%, 40%);">- self.trx_started = True</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "POWEROFF", 0):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv POWEROFF cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Stopping transceiver...")</span><br><span style="color: hsl(0, 100%, 40%);">- self.trx_started = False</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Tuning Control</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "RXTUNE", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv RXTUNE cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # TODO: check freq range</span><br><span style="color: hsl(0, 100%, 40%);">- self.rx_freq = int(request[1]) * 1000</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.bb_freq = self.rx_freq</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "TXTUNE", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv TXTUNE cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # TODO: check freq range</span><br><span style="color: hsl(0, 100%, 40%);">- self.tx_freq = int(request[1]) * 1000</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Power measurement</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "MEASURE", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv MEASURE cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if self.pm is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # TODO: check freq range</span><br><span style="color: hsl(0, 100%, 40%);">- meas_freq = int(request[1]) * 1000</span><br><span style="color: hsl(0, 100%, 40%);">- meas_dbm = str(self.pm.measure(meas_freq))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return (0, [meas_dbm])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "SETSLOT", 2):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv SETSLOT cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if self.burst_fwd is None:</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Obtain TS index</span><br><span style="color: hsl(0, 100%, 40%);">- ts = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- if ts not in range(0, 8):</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("TS index should be in range: 0..7")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse TS type</span><br><span style="color: hsl(0, 100%, 40%);">- ts_type = int(request[2])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # TS activation / deactivation</span><br><span style="color: hsl(0, 100%, 40%);">- # We don't care about ts_type</span><br><span style="color: hsl(0, 100%, 40%);">- if ts_type == 0:</span><br><span style="color: hsl(0, 100%, 40%);">- # Deactivate TS (remove from TS pass-filter list)</span><br><span style="color: hsl(0, 100%, 40%);">- if ts in self.burst_fwd.ts_pass_list:</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.ts_pass_list.remove(ts)</span><br><span style="color: hsl(0, 100%, 40%);">- else:</span><br><span style="color: hsl(0, 100%, 40%);">- # Activate TS (add to TS pass-filter list)</span><br><span style="color: hsl(0, 100%, 40%);">- if ts not in self.burst_fwd.ts_pass_list:</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.ts_pass_list.append(ts)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Timing Advance</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "SETTA", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv SETTA cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Save to the BurstForwarder instance</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.ta = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Timing of Arrival simulation for Uplink</span><br><span style="color: hsl(0, 100%, 40%);">- # Absolute form: CMD FAKE_TOA <BASE> <THRESH></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_TOA", 2):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_TOA cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply both base and threshold</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.toa256_ul_base = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.toa256_ul_threshold = int(request[2])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Timing of Arrival simulation for Uplink</span><br><span style="color: hsl(0, 100%, 40%);">- # Relative form: CMD FAKE_TOA <+-BASE_DELTA></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_TOA", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_TOA cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply delta</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.toa256_ul_base += int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # RSSI simulation for Uplink</span><br><span style="color: hsl(0, 100%, 40%);">- # Absolute form: CMD FAKE_RSSI <BASE> <THRESH></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_RSSI", 2):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_RSSI cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply both base and threshold</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.rssi_ul_base = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.rssi_ul_threshold = int(request[2])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # RSSI simulation for Uplink</span><br><span style="color: hsl(0, 100%, 40%);">- # Relative form: CMD FAKE_RSSI <+-BASE_DELTA></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_RSSI", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_RSSI cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply delta</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.rssi_ul_base += int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation for UL: burst dropping</span><br><span style="color: hsl(0, 100%, 40%);">- # Syntax: CMD FAKE_DROP <AMOUNT></span><br><span style="color: hsl(0, 100%, 40%);">- # Dropping pattern: fn % 1 == 0</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_DROP", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_DROP cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse / validate amount of bursts</span><br><span style="color: hsl(0, 100%, 40%);">- num = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- if num < 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("FAKE_DROP amount shall not be negative")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_ul_drop_amount = num</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_ul_drop_period = 1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation for UL: burst dropping</span><br><span style="color: hsl(0, 100%, 40%);">- # Syntax: CMD FAKE_DROP <AMOUNT> <FN_PERIOD></span><br><span style="color: hsl(0, 100%, 40%);">- # Dropping pattern: fn % period == 0</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_DROP", 2):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_DROP cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse / validate amount of bursts</span><br><span style="color: hsl(0, 100%, 40%);">- num = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- if num < 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("FAKE_DROP amount shall not be negative")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse / validate period</span><br><span style="color: hsl(0, 100%, 40%);">- period = int(request[2])</span><br><span style="color: hsl(0, 100%, 40%);">- if period <= 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("FAKE_DROP period shall be greater than zero")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_ul_drop_amount = num</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_ul_drop_period = period</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Wrong / unknown command</span><br><span style="color: hsl(0, 100%, 40%);">- else:</span><br><span style="color: hsl(0, 100%, 40%);">- # We don't care about other commands,</span><br><span style="color: hsl(0, 100%, 40%);">- # so let's merely ignore them ;)</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Ignore CMD %s" % request[0])</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span>diff --git a/src/target/trx_toolkit/ctrl_if_bts.py b/src/target/trx_toolkit/ctrl_if_bts.py</span><br><span>deleted file mode 100644</span><br><span>index cb38b67..0000000</span><br><span>--- a/src/target/trx_toolkit/ctrl_if_bts.py</span><br><span>+++ /dev/null</span><br><span>@@ -1,189 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python2</span><br><span style="color: hsl(0, 100%, 40%);">-# -*- coding: utf-8 -*-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-# TRX Toolkit</span><br><span style="color: hsl(0, 100%, 40%);">-# CTRL interface implementation (OsmoBTS specific)</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# All Rights Reserved</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(0, 100%, 40%);">-# it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(0, 100%, 40%);">-# the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(0, 100%, 40%);">-# (at your option) any later version.</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(0, 100%, 40%);">-# but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(0, 100%, 40%);">-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span><br><span style="color: hsl(0, 100%, 40%);">-# GNU General Public License for more details.</span><br><span style="color: hsl(0, 100%, 40%);">-#</span><br><span style="color: hsl(0, 100%, 40%);">-# You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(0, 100%, 40%);">-# with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(0, 100%, 40%);">-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-import logging as log</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-from ctrl_if import CTRLInterface</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class CTRLInterfaceBTS(CTRLInterface):</span><br><span style="color: hsl(0, 100%, 40%);">- # Internal state variables</span><br><span style="color: hsl(0, 100%, 40%);">- trx_started = False</span><br><span style="color: hsl(0, 100%, 40%);">- burst_fwd = None</span><br><span style="color: hsl(0, 100%, 40%);">- clck_gen = None</span><br><span style="color: hsl(0, 100%, 40%);">- rx_freq = None</span><br><span style="color: hsl(0, 100%, 40%);">- tx_freq = None</span><br><span style="color: hsl(0, 100%, 40%);">- pm = None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- def __init__(self, *udp_link_args):</span><br><span style="color: hsl(0, 100%, 40%);">- CTRLInterface.__init__(self, *udp_link_args)</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Init CTRL interface for BTS (%s)" % self.desc_link())</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- def parse_cmd(self, request):</span><br><span style="color: hsl(0, 100%, 40%);">- # Power control</span><br><span style="color: hsl(0, 100%, 40%);">- if self.verify_cmd(request, "POWERON", 0):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv POWERON CMD")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Ensure transceiver isn't working</span><br><span style="color: hsl(0, 100%, 40%);">- if self.trx_started:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("Transceiver already started")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Ensure RX / TX freq. are set</span><br><span style="color: hsl(0, 100%, 40%);">- if (self.rx_freq is None) or (self.tx_freq is None):</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("RX / TX freq. are not set")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Starting transceiver...")</span><br><span style="color: hsl(0, 100%, 40%);">- self.trx_started = True</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Power emulation</span><br><span style="color: hsl(0, 100%, 40%);">- if self.pm is not None:</span><br><span style="color: hsl(0, 100%, 40%);">- self.pm.add_bts_list([self.tx_freq])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Start clock indications</span><br><span style="color: hsl(0, 100%, 40%);">- if self.clck_gen is not None:</span><br><span style="color: hsl(0, 100%, 40%);">- self.clck_gen.start()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "POWEROFF", 0):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv POWEROFF cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- log.info("Stopping transceiver...")</span><br><span style="color: hsl(0, 100%, 40%);">- self.trx_started = False</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Power emulation</span><br><span style="color: hsl(0, 100%, 40%);">- if self.pm is not None:</span><br><span style="color: hsl(0, 100%, 40%);">- self.pm.del_bts_list([self.tx_freq])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Stop clock indications</span><br><span style="color: hsl(0, 100%, 40%);">- if self.clck_gen is not None:</span><br><span style="color: hsl(0, 100%, 40%);">- self.clck_gen.stop()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Tuning Control</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "RXTUNE", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv RXTUNE cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # TODO: check freq range</span><br><span style="color: hsl(0, 100%, 40%);">- self.rx_freq = int(request[1]) * 1000</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "TXTUNE", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv TXTUNE cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # TODO: check freq range</span><br><span style="color: hsl(0, 100%, 40%);">- self.tx_freq = int(request[1]) * 1000</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.bts_freq = self.tx_freq</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Timing of Arrival simulation for Downlink</span><br><span style="color: hsl(0, 100%, 40%);">- # Absolute form: CMD FAKE_TOA <BASE> <THRESH></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_TOA", 2):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_TOA cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply both base and threshold</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.toa256_dl_base = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.toa256_dl_threshold = int(request[2])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Timing of Arrival simulation for Downlink</span><br><span style="color: hsl(0, 100%, 40%);">- # Relative form: CMD FAKE_TOA <+-BASE_DELTA></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_TOA", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_TOA cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply delta</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.toa256_dl_base += int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # RSSI simulation for Downlink</span><br><span style="color: hsl(0, 100%, 40%);">- # Absolute form: CMD FAKE_RSSI <BASE> <THRESH></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_RSSI", 2):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_RSSI cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply both base and threshold</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.rssi_dl_base = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.rssi_dl_threshold = int(request[2])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # RSSI simulation for Downlink</span><br><span style="color: hsl(0, 100%, 40%);">- # Relative form: CMD FAKE_RSSI <+-BASE_DELTA></span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_RSSI", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_RSSI cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse and apply delta</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.rssi_dl_base += int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation for DL: burst dropping</span><br><span style="color: hsl(0, 100%, 40%);">- # Syntax: CMD FAKE_DROP <AMOUNT></span><br><span style="color: hsl(0, 100%, 40%);">- # Dropping pattern: fn % 1 == 0</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_DROP", 1):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_DROP cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse / validate amount of bursts</span><br><span style="color: hsl(0, 100%, 40%);">- num = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- if num < 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("FAKE_DROP amount shall not be negative")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_dl_drop_amount = num</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_dl_drop_period = 1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Path loss simulation for DL: burst dropping</span><br><span style="color: hsl(0, 100%, 40%);">- # Syntax: CMD FAKE_DROP <AMOUNT> <FN_PERIOD></span><br><span style="color: hsl(0, 100%, 40%);">- # Dropping pattern: fn % period == 0</span><br><span style="color: hsl(0, 100%, 40%);">- elif self.verify_cmd(request, "FAKE_DROP", 2):</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Recv FAKE_DROP cmd")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse / validate amount of bursts</span><br><span style="color: hsl(0, 100%, 40%);">- num = int(request[1])</span><br><span style="color: hsl(0, 100%, 40%);">- if num < 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("FAKE_DROP amount shall not be negative")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Parse / validate period</span><br><span style="color: hsl(0, 100%, 40%);">- period = int(request[2])</span><br><span style="color: hsl(0, 100%, 40%);">- if period <= 0:</span><br><span style="color: hsl(0, 100%, 40%);">- log.error("FAKE_DROP period shall be greater than zero")</span><br><span style="color: hsl(0, 100%, 40%);">- return -1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_dl_drop_amount = num</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.burst_dl_drop_period = period</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Wrong / unknown command</span><br><span style="color: hsl(0, 100%, 40%);">- else:</span><br><span style="color: hsl(0, 100%, 40%);">- # We don't care about other commands,</span><br><span style="color: hsl(0, 100%, 40%);">- # so let's merely ignore them ;)</span><br><span style="color: hsl(0, 100%, 40%);">- log.debug("Ignore CMD %s" % request[0])</span><br><span style="color: hsl(0, 100%, 40%);">- return 0</span><br><span>diff --git a/src/target/trx_toolkit/ctrl_if_trx.py b/src/target/trx_toolkit/ctrl_if_trx.py</span><br><span>new file mode 100644</span><br><span>index 0000000..83d55db</span><br><span>--- /dev/null</span><br><span>+++ b/src/target/trx_toolkit/ctrl_if_trx.py</span><br><span>@@ -0,0 +1,155 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python2</span><br><span style="color: hsl(120, 100%, 40%);">+# -*- coding: utf-8 -*-</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TRX Toolkit</span><br><span style="color: hsl(120, 100%, 40%);">+# CTRL interface implementation (common commands)</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# (C) 2016-2018 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+# it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+# the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+# (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+# but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span><br><span style="color: hsl(120, 100%, 40%);">+# GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+# with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import logging as log</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from ctrl_if import CTRLInterface</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CTRLInterfaceTRX(CTRLInterface):</span><br><span style="color: hsl(120, 100%, 40%);">+ """ CTRL interface handler for common transceiver management commands.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The following set of commands is mandatory for every transceiver:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - POWERON / POWEROFF - state management (running / idle),</span><br><span style="color: hsl(120, 100%, 40%);">+ - RXTUNE / TXTUNE - RX / TX frequency management,</span><br><span style="color: hsl(120, 100%, 40%);">+ - SETSLOT - timeslot management.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Additionally, there is an optional MEASURE command, which is used</span><br><span style="color: hsl(120, 100%, 40%);">+ by OsmocomBB to perform power measurement on a given frequency.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ A given transceiver may also define its own command handler,</span><br><span style="color: hsl(120, 100%, 40%);">+ that is prioritized, i.e. it can overwrite any commands mentioned</span><br><span style="color: hsl(120, 100%, 40%);">+ above. If None is returned, a command is considered as unhandled.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, trx, *udp_link_args):</span><br><span style="color: hsl(120, 100%, 40%);">+ CTRLInterface.__init__(self, *udp_link_args)</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("Init CTRL interface (%s)" % self.desc_link())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Link with Transceiver instance we belong to</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx = trx</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def parse_cmd(self, request):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Custom command handlers (prioritized)</span><br><span style="color: hsl(120, 100%, 40%);">+ res = self.trx.ctrl_cmd_handler(request)</span><br><span style="color: hsl(120, 100%, 40%);">+ if res is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ return res</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Power control</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.verify_cmd(request, "POWERON", 0):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv POWERON CMD")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Ensure transceiver isn't working</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.trx.running:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("Transceiver already started")</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Ensure RX / TX freq. are set</span><br><span style="color: hsl(120, 100%, 40%);">+ if (self.trx.rx_freq is None) or (self.trx.tx_freq is None):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("RX / TX freq. are not set")</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("Starting transceiver...")</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.running = True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Notify transceiver about that</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.power_event_handler("POWERON")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.verify_cmd(request, "POWEROFF", 0):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv POWEROFF cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("Stopping transceiver...")</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.running = False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Notify transceiver about that</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.power_event_handler("POWEROFF")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Tuning Control</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.verify_cmd(request, "RXTUNE", 1):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv RXTUNE cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # TODO: check freq range</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.rx_freq = int(request[1]) * 1000</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.verify_cmd(request, "TXTUNE", 1):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv TXTUNE cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # TODO: check freq range</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.tx_freq = int(request[1]) * 1000</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.verify_cmd(request, "SETSLOT", 2):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv SETSLOT cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Obtain TS index</span><br><span style="color: hsl(120, 100%, 40%);">+ ts = int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ if ts not in range(0, 8):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("TS index should be in range: 0..7")</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse TS type</span><br><span style="color: hsl(120, 100%, 40%);">+ ts_type = int(request[2])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # TS activation / deactivation</span><br><span style="color: hsl(120, 100%, 40%);">+ # We don't care about ts_type</span><br><span style="color: hsl(120, 100%, 40%);">+ if ts_type == 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Deactivate TS (remove from the list of active timeslots)</span><br><span style="color: hsl(120, 100%, 40%);">+ if ts in self.trx.ts_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.ts_list.remove(ts)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Activate TS (add to the list of active timeslots)</span><br><span style="color: hsl(120, 100%, 40%);">+ if ts not in self.trx.ts_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx.ts_list.append(ts)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Power measurement</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.verify_cmd(request, "MEASURE", 1):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv MEASURE cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Power Measurement interface is optional</span><br><span style="color: hsl(120, 100%, 40%);">+ # for Transceiver, thus may be uninitialized</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.trx.pwr_meas is None:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("Power Measurement interface "</span><br><span style="color: hsl(120, 100%, 40%);">+ "is not initialized => rejecting command")</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # TODO: check freq range</span><br><span style="color: hsl(120, 100%, 40%);">+ meas_freq = int(request[1]) * 1000</span><br><span style="color: hsl(120, 100%, 40%);">+ meas_dbm = self.trx.pwr_meas.measure(meas_freq)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return (0, [str(meas_dbm)])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Wrong / unknown command</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ # We don't care about other commands,</span><br><span style="color: hsl(120, 100%, 40%);">+ # so let's merely ignore them ;)</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Ignore CMD %s" % request[0])</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span>diff --git a/src/target/trx_toolkit/fake_pm.py b/src/target/trx_toolkit/fake_pm.py</span><br><span>index 840b4e4..1992f8d 100644</span><br><span>--- a/src/target/trx_toolkit/fake_pm.py</span><br><span>+++ b/src/target/trx_toolkit/fake_pm.py</span><br><span>@@ -2,9 +2,9 @@</span><br><span> # -*- coding: utf-8 -*-</span><br><span> </span><br><span> # TRX Toolkit</span><br><span style="color: hsl(0, 100%, 40%);">-# Power measurement emulation for BB</span><br><span style="color: hsl(120, 100%, 40%);">+# Power measurement emulation</span><br><span> #</span><br><span style="color: hsl(0, 100%, 40%);">-# (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span style="color: hsl(120, 100%, 40%);">+# (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span> #</span><br><span> # All Rights Reserved</span><br><span> #</span><br><span>@@ -25,29 +25,53 @@</span><br><span> from random import randint</span><br><span> </span><br><span> class FakePM:</span><br><span style="color: hsl(0, 100%, 40%);">- # Freq. list for good power level</span><br><span style="color: hsl(0, 100%, 40%);">- bts_list = []</span><br><span style="color: hsl(120, 100%, 40%);">+ """ Power measurement emulation for fake transceivers.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- def __init__(self, noise_min, noise_max, bts_min, bts_max):</span><br><span style="color: hsl(0, 100%, 40%);">- # Save power level ranges</span><br><span style="color: hsl(120, 100%, 40%);">+ There is no such thing like RF signal level in fake Um-interface,</span><br><span style="color: hsl(120, 100%, 40%);">+ so we need to emulate this. The main idea is to have a list of</span><br><span style="color: hsl(120, 100%, 40%);">+ all running and idle transceivers. As soon as a measurement</span><br><span style="color: hsl(120, 100%, 40%);">+ request is received, FakePM will attempt to find a running</span><br><span style="color: hsl(120, 100%, 40%);">+ transceiver on a given frequency.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The result of such "measurement" is a random RSSI value</span><br><span style="color: hsl(120, 100%, 40%);">+ in one of the following ranges:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - trx_min ... trx_max - if at least one TRX was found,</span><br><span style="color: hsl(120, 100%, 40%);">+ - noise_min ... noise_max - no TRX instances were found.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ FIXME: it would be great to average the rate of bursts</span><br><span style="color: hsl(120, 100%, 40%);">+ and indicated power / attenuation values for all</span><br><span style="color: hsl(120, 100%, 40%);">+ matching transceivers, so "pure traffic" ARFCNs</span><br><span style="color: hsl(120, 100%, 40%);">+ would be handled properly.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, noise_min, noise_max, trx_min, trx_max):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Init list of transceivers</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx_list = []</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # RSSI randomization ranges</span><br><span> self.noise_min = noise_min</span><br><span> self.noise_max = noise_max</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_min = bts_min</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_max = bts_max</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx_min = trx_min</span><br><span style="color: hsl(120, 100%, 40%);">+ self.trx_max = trx_max</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- def measure(self, bts):</span><br><span style="color: hsl(0, 100%, 40%);">- if bts in self.bts_list:</span><br><span style="color: hsl(0, 100%, 40%);">- return randint(self.bts_min, self.bts_max)</span><br><span style="color: hsl(0, 100%, 40%);">- else:</span><br><span style="color: hsl(0, 100%, 40%);">- return randint(self.noise_min, self.noise_max)</span><br><span style="color: hsl(120, 100%, 40%);">+ @property</span><br><span style="color: hsl(120, 100%, 40%);">+ def rssi_noise(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ return randint(self.noise_min, self.noise_max)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- def update_bts_list(self, new_list):</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_list = new_list</span><br><span style="color: hsl(120, 100%, 40%);">+ @property</span><br><span style="color: hsl(120, 100%, 40%);">+ def rssi_trx(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ return randint(self.trx_min, self.trx_max)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- def add_bts_list(self, add_list):</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_list += add_list</span><br><span style="color: hsl(120, 100%, 40%);">+ def measure(self, freq):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Iterate over all known transceivers</span><br><span style="color: hsl(120, 100%, 40%);">+ for trx in self.trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ if not trx.running:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- def del_bts_list(self, del_list):</span><br><span style="color: hsl(0, 100%, 40%);">- for item in del_list:</span><br><span style="color: hsl(0, 100%, 40%);">- if item in self.bts_list:</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_list.remove(item)</span><br><span style="color: hsl(120, 100%, 40%);">+ # Match by given frequency</span><br><span style="color: hsl(120, 100%, 40%);">+ if trx.tx_freq == freq:</span><br><span style="color: hsl(120, 100%, 40%);">+ return self.rssi_trx</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return self.rssi_noise</span><br><span>diff --git a/src/target/trx_toolkit/fake_trx.py b/src/target/trx_toolkit/fake_trx.py</span><br><span>index 95261df..46b413a 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>@@ -27,17 +27,237 @@</span><br><span> import logging as log</span><br><span> import signal</span><br><span> import argparse</span><br><span style="color: hsl(120, 100%, 40%);">+import random</span><br><span> import select</span><br><span> import sys</span><br><span> </span><br><span> from app_common import ApplicationBase</span><br><span style="color: hsl(0, 100%, 40%);">-from ctrl_if_bts import CTRLInterfaceBTS</span><br><span style="color: hsl(0, 100%, 40%);">-from ctrl_if_bb import CTRLInterfaceBB</span><br><span> from burst_fwd import BurstForwarder</span><br><span style="color: hsl(120, 100%, 40%);">+from transceiver import Transceiver</span><br><span style="color: hsl(120, 100%, 40%);">+from clck_gen import CLCKGen</span><br><span> from fake_pm import FakePM</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from udp_link import UDPLink</span><br><span style="color: hsl(0, 100%, 40%);">-from clck_gen import CLCKGen</span><br><span style="color: hsl(120, 100%, 40%);">+class FakeTRX(Transceiver):</span><br><span style="color: hsl(120, 100%, 40%);">+ """ Fake transceiver with RF path (burst loss, RSSI, TA, ToA) simulation.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == ToA / RSSI measurement simulation</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Since this is a virtual environment, we can simulate different</span><br><span style="color: hsl(120, 100%, 40%);">+ parameters of the physical RF interface:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - ToA (Timing of Arrival) - measured difference between expected</span><br><span style="color: hsl(120, 100%, 40%);">+ and actual time of burst arrival in units of 1/256 of GSM symbol</span><br><span style="color: hsl(120, 100%, 40%);">+ periods. A pair of both base and threshold values defines a range</span><br><span style="color: hsl(120, 100%, 40%);">+ of ToA value randomization:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ from (toa256_base - toa256_rand_threshold)</span><br><span style="color: hsl(120, 100%, 40%);">+ to (toa256_base + toa256_rand_threshold).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - RSSI (Received Signal Strength Indication) - measured "power" of</span><br><span style="color: hsl(120, 100%, 40%);">+ the signal (per burst) in dBm. A pair of both base and threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ values defines a range of RSSI value randomization:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ from (rssi_base - rssi_rand_threshold)</span><br><span style="color: hsl(120, 100%, 40%);">+ to (rssi_base + rssi_rand_threshold).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Please note that randomization of both RSSI and ToA is optional,</span><br><span style="color: hsl(120, 100%, 40%);">+ and can be enabled from the control interface.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Timing Advance handling</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The BTS is using ToA measurements for UL bursts in order to calculate</span><br><span style="color: hsl(120, 100%, 40%);">+ Timing Advance value, that is then indicated to a MS, which in its turn</span><br><span style="color: hsl(120, 100%, 40%);">+ shall apply this value to the transmitted signal in order to compensate</span><br><span style="color: hsl(120, 100%, 40%);">+ the delay. Basically, every burst is transmitted in advance defined by</span><br><span style="color: hsl(120, 100%, 40%);">+ the indicated Timing Advance value. The valid range is 0..63, where</span><br><span style="color: hsl(120, 100%, 40%);">+ each unit means one GSM symbol advance. The actual Timing Advance value</span><br><span style="color: hsl(120, 100%, 40%);">+ is set using SETTA control command from MS. By default, it's set to 0.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Path loss simulation</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ === Burst dropping</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ In some cases, e.g. due to a weak signal or high interference, a burst</span><br><span style="color: hsl(120, 100%, 40%);">+ can be lost, i.e. not detected by the receiver. This can also be</span><br><span style="color: hsl(120, 100%, 40%);">+ simulated using FAKE_DROP command on the control interface:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - burst_drop_amount - the amount of DL/UL bursts</span><br><span style="color: hsl(120, 100%, 40%);">+ to be dropped (i.e. not forwarded towards the MS/BTS),</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - burst_drop_period - drop a DL/UL burst if its (fn % period) == 0.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Configuration</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ All simulation parameters mentioned above can be changed at runtime</span><br><span style="color: hsl(120, 100%, 40%);">+ using the commands with prefix 'FAKE_' on the control interface.</span><br><span style="color: hsl(120, 100%, 40%);">+ All of them are handled by our custom CTRL command handler.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ TOA256_BASE_DEFAULT = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ RSSI_BASE_DEFAULT = -60</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, *trx_args, **trx_kwargs):</span><br><span style="color: hsl(120, 100%, 40%);">+ Transceiver.__init__(self, *trx_args, **trx_kwargs)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Actual ToA / RSSI / TA values</span><br><span style="color: hsl(120, 100%, 40%);">+ self.toa256_base = self.TOA256_BASE_DEFAULT</span><br><span style="color: hsl(120, 100%, 40%);">+ self.rssi_base = self.RSSI_BASE_DEFAULT</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ta = 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # ToA / RSSI randomization threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ self.toa256_rand_threshold = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ self.rssi_rand_threshold = 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Path loss simulation (burst dropping)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_drop_amount = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_drop_period = 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @property</span><br><span style="color: hsl(120, 100%, 40%);">+ def toa256(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Check if randomization is required</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.toa256_rand_threshold is 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ return self.toa256_base</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Generate a random ToA value in required range</span><br><span style="color: hsl(120, 100%, 40%);">+ toa256_min = self.toa256_base - self.toa256_rand_threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ toa256_max = self.toa256_base + self.toa256_rand_threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ return random.randint(toa256_min, toa256_max)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @property</span><br><span style="color: hsl(120, 100%, 40%);">+ def rssi(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Check if randomization is required</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.rssi_rand_threshold is 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ return self.rssi_base</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Generate a random RSSI value in required range</span><br><span style="color: hsl(120, 100%, 40%);">+ rssi_min = self.rssi_base - self.rssi_rand_threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ rssi_max = self.rssi_base + self.rssi_rand_threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ return random.randint(rssi_min, rssi_max)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Path loss simulation: burst dropping</span><br><span style="color: hsl(120, 100%, 40%);">+ # Returns: True - drop, False - keep</span><br><span style="color: hsl(120, 100%, 40%);">+ def sim_burst_drop(self, msg):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Check if dropping is required</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.burst_drop_amount is 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ return False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if msg.fn % self.burst_drop_period == 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("Simulation: dropping burst (fn=%u %% %u == 0)"</span><br><span style="color: hsl(120, 100%, 40%);">+ % (msg.fn, self.burst_drop_period))</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_drop_amount -= 1</span><br><span style="color: hsl(120, 100%, 40%);">+ return True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Takes (partially initialized) TRX2L1 message,</span><br><span style="color: hsl(120, 100%, 40%);">+ # simulates RF path parameters (such as RSSI),</span><br><span style="color: hsl(120, 100%, 40%);">+ # and sends towards the L1</span><br><span style="color: hsl(120, 100%, 40%);">+ def send_data_msg(self, src_trx, msg):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Complete message header</span><br><span style="color: hsl(120, 100%, 40%);">+ msg.toa256 = self.toa256</span><br><span style="color: hsl(120, 100%, 40%);">+ msg.rssi = self.rssi</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Apply optional Timing Advance</span><br><span style="color: hsl(120, 100%, 40%);">+ if src_trx.ta is not 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ msg.toa256 -= src_trx.ta * 256</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Path loss simulation</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.sim_burst_drop(msg):</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%);">+ # TODO: make legacy mode configurable (via argv?)</span><br><span style="color: hsl(120, 100%, 40%);">+ 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%);">+ # Simulation specific CTRL command handler</span><br><span style="color: hsl(120, 100%, 40%);">+ def ctrl_cmd_handler(self, request):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Timing Advance</span><br><span style="color: hsl(120, 100%, 40%);">+ # Syntax: CMD SETTA <TA></span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ctrl_if.verify_cmd(request, "SETTA", 1):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv SETTA cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Store indicated value</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ta = int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Timing of Arrival simulation</span><br><span style="color: hsl(120, 100%, 40%);">+ # Absolute form: CMD FAKE_TOA <BASE> <THRESH></span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ctrl_if.verify_cmd(request, "FAKE_TOA", 2):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv FAKE_TOA cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse and apply both base and threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ self.toa256_base = int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ self.toa256_rand_threshold = int(request[2])</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Timing of Arrival simulation</span><br><span style="color: hsl(120, 100%, 40%);">+ # Relative form: CMD FAKE_TOA <+-BASE_DELTA></span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ctrl_if.verify_cmd(request, "FAKE_TOA", 1):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv FAKE_TOA cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse and apply delta</span><br><span style="color: hsl(120, 100%, 40%);">+ self.toa256_base += int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # RSSI simulation</span><br><span style="color: hsl(120, 100%, 40%);">+ # Absolute form: CMD FAKE_RSSI <BASE> <THRESH></span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ctrl_if.verify_cmd(request, "FAKE_RSSI", 2):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv FAKE_RSSI cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse and apply both base and threshold</span><br><span style="color: hsl(120, 100%, 40%);">+ self.rssi_base = int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ self.rssi_rand_threshold = int(request[2])</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # RSSI simulation</span><br><span style="color: hsl(120, 100%, 40%);">+ # Relative form: CMD FAKE_RSSI <+-BASE_DELTA></span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ctrl_if.verify_cmd(request, "FAKE_RSSI", 1):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv FAKE_RSSI cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse and apply delta</span><br><span style="color: hsl(120, 100%, 40%);">+ self.rssi_base += int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Path loss simulation: burst dropping</span><br><span style="color: hsl(120, 100%, 40%);">+ # Syntax: CMD FAKE_DROP <AMOUNT></span><br><span style="color: hsl(120, 100%, 40%);">+ # Dropping pattern: fn % 1 == 0</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ctrl_if.verify_cmd(request, "FAKE_DROP", 1):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv FAKE_DROP cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse / validate amount of bursts</span><br><span style="color: hsl(120, 100%, 40%);">+ num = int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ if num < 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("FAKE_DROP amount shall not be negative")</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_drop_amount = num</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_drop_period = 1</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Path loss simulation: burst dropping</span><br><span style="color: hsl(120, 100%, 40%);">+ # Syntax: CMD FAKE_DROP <AMOUNT> <FN_PERIOD></span><br><span style="color: hsl(120, 100%, 40%);">+ # Dropping pattern: fn % period == 0</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ctrl_if.verify_cmd(request, "FAKE_DROP", 2):</span><br><span style="color: hsl(120, 100%, 40%);">+ log.debug("Recv FAKE_DROP cmd")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse / validate amount of bursts</span><br><span style="color: hsl(120, 100%, 40%);">+ num = int(request[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ if num < 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("FAKE_DROP amount shall not be negative")</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse / validate period</span><br><span style="color: hsl(120, 100%, 40%);">+ period = int(request[2])</span><br><span style="color: hsl(120, 100%, 40%);">+ if period <= 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.error("FAKE_DROP period shall be greater than zero")</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_drop_amount = num</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_drop_period = period</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Unhandled command</span><br><span style="color: hsl(120, 100%, 40%);">+ return None</span><br><span> </span><br><span> class Application(ApplicationBase):</span><br><span> def __init__(self):</span><br><span>@@ -51,72 +271,58 @@</span><br><span> self.app_init_logging(self.argv)</span><br><span> </span><br><span> def run(self):</span><br><span style="color: hsl(0, 100%, 40%);">- # Init TRX CTRL interface for BTS</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_ctrl = CTRLInterfaceBTS(</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.bts_addr, self.argv.bts_base_port + 101,</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.trx_bind_addr, self.argv.bts_base_port + 1)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Init TRX CTRL interface for BB</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_ctrl = CTRLInterfaceBB(</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.bb_addr, self.argv.bb_base_port + 101,</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.trx_bind_addr, self.argv.bb_base_port + 1)</span><br><span style="color: hsl(120, 100%, 40%);">+ # Init shared clock generator</span><br><span style="color: hsl(120, 100%, 40%);">+ self.clck_gen = CLCKGen([])</span><br><span> </span><br><span> # Power measurement emulation</span><br><span> # Noise: -120 .. -105</span><br><span> # BTS: -75 .. -50</span><br><span style="color: hsl(0, 100%, 40%);">- self.pm = FakePM(-120, -105, -75, -50)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.fake_pm = FakePM(-120, -105, -75, -50)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Share a FakePM instance between both BTS and BB</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_ctrl.pm = self.pm</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_ctrl.pm = self.pm</span><br><span style="color: hsl(120, 100%, 40%);">+ # Init TRX instance for BTS</span><br><span style="color: hsl(120, 100%, 40%);">+ self.bts_trx = FakeTRX(self.argv.trx_bind_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+ self.argv.bts_addr, self.argv.bts_base_port,</span><br><span style="color: hsl(120, 100%, 40%);">+ clck_gen = self.clck_gen)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Init DATA links</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_data = UDPLink(</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.bts_addr, self.argv.bts_base_port + 102,</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.trx_bind_addr, self.argv.bts_base_port + 2)</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_data = UDPLink(</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.bb_addr, self.argv.bb_base_port + 102,</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.trx_bind_addr, self.argv.bb_base_port + 2)</span><br><span style="color: hsl(120, 100%, 40%);">+ # Init TRX instance for BB</span><br><span style="color: hsl(120, 100%, 40%);">+ self.bb_trx = FakeTRX(self.argv.trx_bind_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+ self.argv.bb_addr, self.argv.bb_base_port,</span><br><span style="color: hsl(120, 100%, 40%);">+ pwr_meas = self.fake_pm)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # BTS <-> BB burst forwarding</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd = BurstForwarder(self.bts_data, self.bb_data)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Share a BurstForwarder instance between BTS and BB</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_ctrl.burst_fwd = self.burst_fwd</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_ctrl.burst_fwd = self.burst_fwd</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Provide clock to BTS</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_clck = UDPLink(</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.bts_addr, self.argv.bts_base_port + 100,</span><br><span style="color: hsl(0, 100%, 40%);">- self.argv.trx_bind_addr, self.argv.bts_base_port)</span><br><span style="color: hsl(0, 100%, 40%);">- self.clck_gen = CLCKGen([self.bts_clck])</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_ctrl.clck_gen = self.clck_gen</span><br><span style="color: hsl(120, 100%, 40%);">+ # Burst forwarding between transceivers</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_fwd = BurstForwarder()</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_fwd.add_trx(self.bts_trx)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_fwd.add_trx(self.bb_trx)</span><br><span> </span><br><span> log.info("Init complete")</span><br><span> </span><br><span> # Enter main loop</span><br><span> while True:</span><br><span style="color: hsl(0, 100%, 40%);">- socks = [self.bts_ctrl.sock, self.bb_ctrl.sock,</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_data.sock, self.bb_data.sock]</span><br><span style="color: hsl(120, 100%, 40%);">+ socks = [self.bts_trx.ctrl_if.sock, self.bb_trx.ctrl_if.sock,</span><br><span style="color: hsl(120, 100%, 40%);">+ self.bts_trx.data_if.sock, self.bb_trx.data_if.sock]</span><br><span> </span><br><span> # Wait until we get any data on any socket</span><br><span> r_event, w_event, x_event = select.select(socks, [], [])</span><br><span> </span><br><span> # Downlink: BTS -> BB</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bts_data.sock in r_event:</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.bts2bb()</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.bts_trx.data_if.sock in r_event:</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = self.bts_trx.recv_data_msg()</span><br><span style="color: hsl(120, 100%, 40%);">+ if msg is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_fwd.forward_msg(self.bts_trx, msg)</span><br><span> </span><br><span> # Uplink: BB -> BTS</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bb_data.sock in r_event:</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst_fwd.bb2bts()</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.bb_trx.data_if.sock in r_event:</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = self.bb_trx.recv_data_msg()</span><br><span style="color: hsl(120, 100%, 40%);">+ if msg is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst_fwd.forward_msg(self.bb_trx, msg)</span><br><span> </span><br><span> # CTRL commands from BTS</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bts_ctrl.sock in r_event:</span><br><span style="color: hsl(0, 100%, 40%);">- self.bts_ctrl.handle_rx()</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.bts_trx.ctrl_if.sock in r_event:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.bts_trx.ctrl_if.handle_rx()</span><br><span> </span><br><span> # CTRL commands from BB</span><br><span style="color: hsl(0, 100%, 40%);">- if self.bb_ctrl.sock in r_event:</span><br><span style="color: hsl(0, 100%, 40%);">- self.bb_ctrl.handle_rx()</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.bb_trx.ctrl_if.sock in r_event:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.bb_trx.ctrl_if.handle_rx()</span><br><span> </span><br><span> def shutdown(self):</span><br><span> log.info("Shutting down...")</span><br><span>diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py</span><br><span>new file mode 100644</span><br><span>index 0000000..edd4d31</span><br><span>--- /dev/null</span><br><span>+++ b/src/target/trx_toolkit/transceiver.py</span><br><span>@@ -0,0 +1,155 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python2</span><br><span style="color: hsl(120, 100%, 40%);">+# -*- coding: utf-8 -*-</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TRX Toolkit</span><br><span style="color: hsl(120, 100%, 40%);">+# Transceiver implementation</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+# it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+# the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+# (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+# but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span><br><span style="color: hsl(120, 100%, 40%);">+# GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+# with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import logging as log</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from ctrl_if_trx import CTRLInterfaceTRX</span><br><span style="color: hsl(120, 100%, 40%);">+from data_if import DATAInterface</span><br><span style="color: hsl(120, 100%, 40%);">+from udp_link import UDPLink</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class Transceiver:</span><br><span style="color: hsl(120, 100%, 40%);">+ """ Base transceiver implementation.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Represents a single transceiver, that can be used as for the BTS side,</span><br><span style="color: hsl(120, 100%, 40%);">+ as for the MS side. Each individual instance of Transceiver unifies</span><br><span style="color: hsl(120, 100%, 40%);">+ three basic interfaces built on three independent UDP connections:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - CLCK (base port + 100/0) - clock indications from TRX to L1,</span><br><span style="color: hsl(120, 100%, 40%);">+ - CTRL (base port + 101/1) - control interface for L1,</span><br><span style="color: hsl(120, 100%, 40%);">+ - DATA (base port + 102/2) - bidirectional data interface for bursts.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ A transceiver can be either in active (i.e. working), or in idle mode.</span><br><span style="color: hsl(120, 100%, 40%);">+ The active mode should ensure that both RX/TX frequencies are set.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ NOTE: CLCK is not required for some L1 implementations, so it is optional.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Timeslot configuration</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Transceiver has a list of active (i.e. configured) TDMA timeslots.</span><br><span style="color: hsl(120, 100%, 40%);">+ The L1 should configure a timeslot before sending or expecting any</span><br><span style="color: hsl(120, 100%, 40%);">+ data on it. This is done by SETSLOT control command, which also</span><br><span style="color: hsl(120, 100%, 40%);">+ indicates an associated channel combination (see GSM TS 05.02).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ NOTE: we don't store the associated channel combinations,</span><br><span style="color: hsl(120, 100%, 40%);">+ as they are only useful for burst detection and demodulation.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Clock distribution (optional)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The clock indications are not expected by L1 when transceiver</span><br><span style="color: hsl(120, 100%, 40%);">+ is not running, so we monitor both POWERON / POWEROFF events</span><br><span style="color: hsl(120, 100%, 40%);">+ from the control interface, and keep the list of CLCK links</span><br><span style="color: hsl(120, 100%, 40%);">+ in a given CLCKGen instance updated. The clock generator is</span><br><span style="color: hsl(120, 100%, 40%);">+ started and stopped automatically.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ NOTE: a single instance of CLCKGen can be shared between multiple</span><br><span style="color: hsl(120, 100%, 40%);">+ transceivers, as well as multiple transceivers may use</span><br><span style="color: hsl(120, 100%, 40%);">+ individual CLCKGen instances.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Power Measurement (optional)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Transceiver may have an optional power measurement interface,</span><br><span style="color: hsl(120, 100%, 40%);">+ that shall provide at least one method: measure(freq). This</span><br><span style="color: hsl(120, 100%, 40%);">+ is required for the MS side (i.e. OsmocomBB).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, bind_addr, remote_addr, base_port,</span><br><span style="color: hsl(120, 100%, 40%);">+ clck_gen = None, pwr_meas = None):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Connection info</span><br><span style="color: hsl(120, 100%, 40%);">+ self.remote_addr = remote_addr</span><br><span style="color: hsl(120, 100%, 40%);">+ self.bind_addr = bind_addr</span><br><span style="color: hsl(120, 100%, 40%);">+ self.base_port = base_port</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Init DATA interface</span><br><span style="color: hsl(120, 100%, 40%);">+ self.data_if = DATAInterface(</span><br><span style="color: hsl(120, 100%, 40%);">+ remote_addr, base_port + 102,</span><br><span style="color: hsl(120, 100%, 40%);">+ bind_addr, base_port + 2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Init CTRL interface</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ctrl_if = CTRLInterfaceTRX(self,</span><br><span style="color: hsl(120, 100%, 40%);">+ remote_addr, base_port + 101,</span><br><span style="color: hsl(120, 100%, 40%);">+ bind_addr, base_port + 1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Init optional CLCK interface</span><br><span style="color: hsl(120, 100%, 40%);">+ self.clck_gen = clck_gen</span><br><span style="color: hsl(120, 100%, 40%);">+ if clck_gen is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.clck_if = UDPLink(</span><br><span style="color: hsl(120, 100%, 40%);">+ remote_addr, base_port + 100,</span><br><span style="color: hsl(120, 100%, 40%);">+ bind_addr, base_port)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Optional Power Measurement interface</span><br><span style="color: hsl(120, 100%, 40%);">+ self.pwr_meas = pwr_meas</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Internal state</span><br><span style="color: hsl(120, 100%, 40%);">+ self.running = False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Actual RX / TX frequencies</span><br><span style="color: hsl(120, 100%, 40%);">+ self.rx_freq = None</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tx_freq = None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # List of active (configured) timeslots</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ts_list = []</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # To be overwritten if required,</span><br><span style="color: hsl(120, 100%, 40%);">+ # no custom command handlers by default</span><br><span style="color: hsl(120, 100%, 40%);">+ def ctrl_cmd_handler(self, request):</span><br><span style="color: hsl(120, 100%, 40%);">+ return None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def power_event_handler(self, event):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Trigger clock generator if required</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.clck_gen is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ clck_links = self.clck_gen.clck_links</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.running and (self.clck_if in clck_links):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Transceiver was stopped</span><br><span style="color: hsl(120, 100%, 40%);">+ clck_links.remove(self.clck_if)</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.running and (self.clck_if not in clck_links):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Transceiver was started</span><br><span style="color: hsl(120, 100%, 40%);">+ clck_links.append(self.clck_if)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.clck_gen.timer and len(clck_links) > 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("Starting clock generator")</span><br><span style="color: hsl(120, 100%, 40%);">+ self.clck_gen.start()</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.clck_gen.timer and not clck_links:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("Stopping clock generator")</span><br><span style="color: hsl(120, 100%, 40%);">+ self.clck_gen.stop()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def recv_data_msg(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Read and parse data from socket</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = self.data_if.recv_l12trx_msg()</span><br><span style="color: hsl(120, 100%, 40%);">+ if not msg:</span><br><span style="color: hsl(120, 100%, 40%);">+ return None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Make sure that transceiver is configured and running</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.running:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.warning("RX DATA message (%s), but transceiver "</span><br><span style="color: hsl(120, 100%, 40%);">+ "is not running => dropping..." % msg.desc_hdr())</span><br><span style="color: hsl(120, 100%, 40%);">+ return None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Make sure that indicated timeslot is configured</span><br><span style="color: hsl(120, 100%, 40%);">+ if msg.tn not in self.ts_list:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.warning("RX DATA message (%s), but timeslot "</span><br><span style="color: hsl(120, 100%, 40%);">+ "is not configured => dropping..." % msg.desc_hdr())</span><br><span style="color: hsl(120, 100%, 40%);">+ return None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return msg</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12264">change 12264</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/12264"/><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-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ice44e2b22566b3652ef6d43896055963b13ab185 </div>
<div style="display:none"> Gerrit-Change-Number: 12264 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: Vadim Yanitskiy <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Vadim Yanitskiy <axilirator@gmail.com> </div>