Change in osmocom-bb[master]: trx_toolkit/transceiver.py: add frequency hopping support

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Sun May 17 18:56:35 UTC 2020


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/18262 )

Change subject: trx_toolkit/transceiver.py: add frequency hopping support
......................................................................

trx_toolkit/transceiver.py: add frequency hopping support

There are two ways to implement frequency hopping:

  a) The Transceiver is configured with the hopping parameters, in
     particular HSN, MAIO, and the list of ARFCNs (channels), so the
     actual Rx/Tx frequencies are changed by the Transceiver itself
     depending on the current TDMA frame number.

  b) The L1 maintains several Transceivers (two or more), so each
     instance is assigned one dedicated RF carrier frequency, and
     hence the number of available hopping frequencies is equal to
     the number of Transceivers. In this case, it's the task of
     the L1 to commutate bursts between Transceivers (frequencies).

Variant a) is commonly known as "synthesizer frequency hopping"
whereas b) is known as "baseband frequency hopping".

For the MS side, a) is preferred, because a phone usually has only
one Transceiver (per RAT). On the other hand, b) is more suitable
for the BTS side, because it's relatively easy to implement and
there is no technical limitation on the amount of Transceivers.

FakeTRX obviously does support b) since multi-TRX feature has been
implemented, as well as a) by resolving UL/DL frequencies using a
preconfigured (by the L1) set of the hopping parameters. The later
can be enabled using the SETFH control command:

  CMD SETFH <HSN> <MAIO> <RXF1> <TXF1> [... <RXFN> <TXFN>]

where <RXFN> and <TXFN> is a pair of Rx/Tx frequencies (in kHz)
corresponding to one ARFCN the Mobile Allocation. Note that the
channel list is expected to be sorted in ascending order.

NOTE: in the current implementation, mode a) applies to the whole
Transceiver and all its timeslots, so using in for the BTS side
does not make any sense (imagine BCCH hopping together with DCCH).

Change-Id: I587e4f5da67c7b7f28e010ed46b24622c31a3fdd
---
M src/target/trx_toolkit/burst_fwd.py
M src/target/trx_toolkit/ctrl_if.py
M src/target/trx_toolkit/ctrl_if_trx.py
M src/target/trx_toolkit/fake_pm.py
M src/target/trx_toolkit/transceiver.py
5 files changed, 140 insertions(+), 18 deletions(-)

Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, but someone else must approve
  laforge: Looks good to me, approved



diff --git a/src/target/trx_toolkit/burst_fwd.py b/src/target/trx_toolkit/burst_fwd.py
index fd6013b..9fb308f 100644
--- a/src/target/trx_toolkit/burst_fwd.py
+++ b/src/target/trx_toolkit/burst_fwd.py
@@ -4,7 +4,8 @@
 # TRX Toolkit
 # Burst forwarding between transceivers
 #
-# (C) 2017-2018 by Vadim Yanitskiy <axilirator at gmail.com>
+# (C) 2017-2020 by Vadim Yanitskiy <axilirator at gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
 #
 # All Rights Reserved
 #
@@ -65,6 +66,10 @@
 		self.trx_list.remove(trx)
 
 	def forward_msg(self, src_trx, rx_msg):
+		# Originating Transceiver may use frequency hopping,
+		# so let's precalculate its Tx frequency in advance
+		tx_freq = src_trx.get_tx_freq(rx_msg.fn)
+
 		# Iterate over all known transceivers
 		for trx in self.trx_list:
 			if trx == src_trx:
@@ -73,11 +78,13 @@
 			# Check transceiver state
 			if not trx.running:
 				continue
-			if trx.rx_freq != src_trx.tx_freq:
-				continue
 			if rx_msg.tn not in trx.ts_list:
 				continue
 
+			# Match Tx/Rx frequencies of the both transceivers
+			if trx.get_rx_freq(rx_msg.fn) != tx_freq:
+				continue
+
 			# Transform from L12TRX to TRX2L1 and forward
 			tx_msg = rx_msg.gen_trx2l1(ver = trx.data_if._hdr_ver)
 			trx.handle_data_msg(src_trx, rx_msg, tx_msg)
diff --git a/src/target/trx_toolkit/ctrl_if.py b/src/target/trx_toolkit/ctrl_if.py
index 89dfe46..7333501 100644
--- a/src/target/trx_toolkit/ctrl_if.py
+++ b/src/target/trx_toolkit/ctrl_if.py
@@ -4,7 +4,8 @@
 # TRX Toolkit
 # CTRL interface implementation
 #
-# (C) 2016-2017 by Vadim Yanitskiy <axilirator at gmail.com>
+# (C) 2016-2020 by Vadim Yanitskiy <axilirator at gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
 #
 # All Rights Reserved
 #
@@ -61,13 +62,17 @@
 		# Now we have something like ["TXTUNE", "941600"]
 		return request
 
-	def verify_cmd(self, request, cmd, argc):
+	# If va is True, the command can have variable number of arguments
+	def verify_cmd(self, request, cmd, argc, va = False):
 		# Check if requested command matches
 		if request[0] != cmd:
 			return False
 
 		# And has enough arguments
-		if len(request) - 1 != argc:
+		req_len = len(request[1:])
+		if not va and req_len != argc:
+			return False
+		elif va and req_len < argc:
 			return False
 
 		return True
diff --git a/src/target/trx_toolkit/ctrl_if_trx.py b/src/target/trx_toolkit/ctrl_if_trx.py
index e44f8a9..65198cf 100644
--- a/src/target/trx_toolkit/ctrl_if_trx.py
+++ b/src/target/trx_toolkit/ctrl_if_trx.py
@@ -4,7 +4,8 @@
 # TRX Toolkit
 # CTRL interface implementation (common commands)
 #
-# (C) 2016-2019 by Vadim Yanitskiy <axilirator at gmail.com>
+# (C) 2016-2020 by Vadim Yanitskiy <axilirator at gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
 #
 # All Rights Reserved
 #
@@ -105,9 +106,9 @@
 				log.error("(%s) Transceiver already started" % self.trx)
 				return -1
 
-			# Ensure RX / TX freq. are set
-			if (self.trx.rx_freq is None) or (self.trx.tx_freq is None):
-				log.error("(%s) RX / TX freq. are not set" % self.trx)
+			# Ensure that transceiver is ready
+			if not self.trx.ready:
+				log.error("(%s) Transceiver is not ready" % self.trx)
 				return -1
 
 			log.info("(%s) Starting transceiver..." % self.trx)
@@ -134,14 +135,14 @@
 			log.debug("(%s) Recv RXTUNE cmd" % self.trx)
 
 			# TODO: check freq range
-			self.trx.rx_freq = int(request[1]) * 1000
+			self.trx._rx_freq = int(request[1]) * 1000
 			return 0
 
 		elif self.verify_cmd(request, "TXTUNE", 1):
 			log.debug("(%s) Recv TXTUNE cmd" % self.trx)
 
 			# TODO: check freq range
-			self.trx.tx_freq = int(request[1]) * 1000
+			self.trx._tx_freq = int(request[1]) * 1000
 			return 0
 
 		elif self.verify_cmd(request, "SETSLOT", 2):
@@ -187,6 +188,32 @@
 
 			return (0, [str(meas_dbm)])
 
+		# Frequency hopping configuration (variable length list):
+		#
+		#   CMD SETFH <HSN> <MAIO> <RXF1> <TXF1> [... <RXFN> <TXFN>]
+		#
+		# where <RXFN> and <TXFN> is a pair of Rx/Tx frequencies (in kHz)
+		# corresponding to one ARFCN the Mobile Allocation. Note that the
+		# channel list is expected to be sorted in ascending order.
+		if self.verify_cmd(request, "SETFH", 4, va = True):
+			log.debug("(%s) Recv SETFH cmd" % self.trx)
+
+			# Parse HSN and MAIO
+			hsn = int(request[1])
+			maio = int(request[2])
+
+			# Parse the list of hopping frequencies
+			ma = [int(f) * 1000 for f in request[3:]] # kHz -> Hz
+			ma = [(rx, tx) for rx, tx in zip(ma[0::2], ma[1::2])]
+
+			# Configure the hopping sequence generator
+			try:
+				self.trx.enable_fh(hsn, maio, ma)
+				return 0
+			except:
+				log.error("(%s) Failed to configure frequency hopping" % trx)
+				return -1
+
 		# TRXD header version negotiation
 		if self.verify_cmd(request, "SETFORMAT", 1):
 			log.debug("(%s) Recv SETFORMAT cmd" % self.trx)
diff --git a/src/target/trx_toolkit/fake_pm.py b/src/target/trx_toolkit/fake_pm.py
index 51dc057..0d66099 100644
--- a/src/target/trx_toolkit/fake_pm.py
+++ b/src/target/trx_toolkit/fake_pm.py
@@ -64,14 +64,19 @@
 	def rssi_trx(self):
 		return randint(self.trx_min, self.trx_max)
 
-	def measure(self, freq):
+	def measure(self, freq, fn = None):
 		# Iterate over all known transceivers
 		for trx in self.trx_list:
 			if not trx.running:
 				continue
 
+			# FIXME: we need to know current TDMA frame number here,
+			# because some transceivers may use frequency hopping
+			if trx.fh is not None and fn is None:
+				continue
+
 			# Match by given frequency
-			if trx.tx_freq == freq:
+			if trx.get_tx_freq(fn) == freq:
 				return self.rssi_trx
 
 		return self.rssi_noise
diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py
index b1a5c11..655d4f6 100644
--- a/src/target/trx_toolkit/transceiver.py
+++ b/src/target/trx_toolkit/transceiver.py
@@ -4,7 +4,8 @@
 # TRX Toolkit
 # Transceiver implementation
 #
-# (C) 2018-2019 by Vadim Yanitskiy <axilirator at gmail.com>
+# (C) 2018-2020 by Vadim Yanitskiy <axilirator at gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
 #
 # All Rights Reserved
 #
@@ -29,6 +30,8 @@
 from udp_link import UDPLink
 from trx_list import TRXList
 
+from gsm_shared import HoppingParams
+
 class Transceiver:
 	""" Base transceiver implementation.
 
@@ -88,6 +91,38 @@
 	that shall provide at least one method: measure(freq). This
 	is required for the MS side (i.e. OsmocomBB).
 
+	== Frequency hopping (optional)
+
+	There are two ways to implement frequency hopping:
+
+	  a) The Transceiver is configured with the hopping parameters, in
+	     particular HSN, MAIO, and the list of ARFCNs (channels), so the
+	     actual Rx/Tx frequencies are changed by the Transceiver itself
+	     depending on the current TDMA frame number.
+
+	  b) The L1 maintains several Transceivers (two or more), so each
+	     instance is assigned one dedicated RF carrier frequency, and
+	     hence the number of available hopping frequencies is equal to
+	     the number of Transceivers. In this case, it's the task of
+	     the L1 to commutate bursts between Transceivers (frequencies).
+
+	Variant a) is commonly known as "synthesizer frequency hopping"
+	whereas b) is known as "baseband frequency hopping".
+
+	For the MS side, a) is preferred, because a phone usually has only
+	one Transceiver (per RAT). On the other hand, b) is more suitable
+	for the BTS side, because it's relatively easy to implement and
+	there is no technical limitation on the amount of Transceivers.
+
+	FakeTRX obviously does support b) since multi-TRX feature has been
+	implemented, as well as a) by resolving UL/DL frequencies using a
+	preconfigured (by the L1) set of the hopping parameters. The later
+	can be enabled using the SETFH control command.
+
+	NOTE: in the current implementation, mode a) applies to the whole
+	Transceiver and all its timeslots, so using in for the BTS side
+	does not make any sense (imagine BCCH hopping together with DCCH).
+
 	"""
 
 	def __init__(self, bind_addr, remote_addr, base_port, name = None,
@@ -131,8 +166,11 @@
 		self.running = False
 
 		# Actual RX / TX frequencies
-		self.rx_freq = None
-		self.tx_freq = None
+		self._rx_freq = None
+		self._tx_freq = None
+
+		# Frequency hopping parameters (set by CTRL)
+		self.fh = None
 
 		# List of active (configured) timeslots
 		self.ts_list = []
@@ -149,6 +187,41 @@
 
 		return desc
 
+	@property
+	def ready(self):
+		# Make sure that either both Rx/Tx frequencies are set
+		if self._rx_freq is None or self._tx_freq is None:
+			# ... or frequency hopping is in use
+			if self.fh is None:
+				return False
+
+		return True
+
+	def get_rx_freq(self, fn):
+		if self.fh is None:
+			return self._rx_freq
+
+		# Frequency hopping in use, resolve by TDMA fn
+		(rx_freq, _) = self.fh.resolve(fn)
+		return rx_freq
+
+	def get_tx_freq(self, fn):
+		if self.fh is None:
+			return self._tx_freq
+
+		# Frequency hopping in use, resolve by TDMA fn
+		(_, tx_freq) = self.fh.resolve(fn)
+		return tx_freq
+
+	def enable_fh(self, *args):
+		self.fh = HoppingParams(*args)
+		log.info("(%s) Frequency hopping configured: %s" % (self, self.fh))
+
+	def disable_fh(self):
+		if self.fh is not None:
+			log.info("(%s) Frequency hopping disabled" % self)
+			self.fh = None
+
 	# To be overwritten if required,
 	# no custom command handlers by default
 	def ctrl_cmd_handler(self, request):
@@ -159,8 +232,13 @@
 		for trx in self.child_trx_list.trx_list:
 			if event == "POWERON":
 				trx.running = True
-			else:
+			elif event == "POWEROFF":
 				trx.running = False
+				trx.disable_fh()
+
+		# Reset frequency hopping parameters
+		if event == "POWEROFF":
+			self.disable_fh()
 
 		# Trigger clock generator if required
 		if self.clck_gen is not None:

-- 
To view, visit https://gerrit.osmocom.org/c/osmocom-bb/+/18262
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: I587e4f5da67c7b7f28e010ed46b24622c31a3fdd
Gerrit-Change-Number: 18262
Gerrit-PatchSet: 3
Gerrit-Owner: Vadim Yanitskiy <axilirator at gmail.com>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Vadim Yanitskiy <axilirator at gmail.com>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200517/2d9d6f40/attachment.htm>


More information about the gerrit-log mailing list