summaryrefslogtreecommitdiffstats
path: root/src/target/trx_toolkit/transceiver.py
blob: edd4d31f46b132a0f5fab894ca37046a49d6a589 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

# TRX Toolkit
# Transceiver implementation
#
# (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import logging as log

from ctrl_if_trx import CTRLInterfaceTRX
from data_if import DATAInterface
from udp_link import UDPLink

class Transceiver:
	""" Base transceiver implementation.

	Represents a single transceiver, that can be used as for the BTS side,
	as for the MS side. Each individual instance of Transceiver unifies
	three basic interfaces built on three independent UDP connections:

	  - CLCK (base port + 100/0) - clock indications from TRX to L1,
	  - CTRL (base port + 101/1) - control interface for L1,
	  - DATA (base port + 102/2) - bidirectional data interface for bursts.

	A transceiver can be either in active (i.e. working), or in idle mode.
	The active mode should ensure that both RX/TX frequencies are set.

	NOTE: CLCK is not required for some L1 implementations, so it is optional.

	== Timeslot configuration

	Transceiver has a list of active (i.e. configured) TDMA timeslots.
	The L1 should configure a timeslot before sending or expecting any
	data on it. This is done by SETSLOT control command, which also
	indicates an associated channel combination (see GSM TS 05.02).

	NOTE: we don't store the associated channel combinations,
	      as they are only useful for burst detection and demodulation.

	== Clock distribution (optional)

	The clock indications are not expected by L1 when transceiver
	is not running, so we monitor both POWERON / POWEROFF events
	from the control interface, and keep the list of CLCK links
	in a given CLCKGen instance updated. The clock generator is
	started and stopped automatically.

	NOTE: a single instance of CLCKGen can be shared between multiple
	      transceivers, as well as multiple transceivers may use
	      individual CLCKGen instances.

	== Power Measurement (optional)

	Transceiver may have an optional power measurement interface,
	that shall provide at least one method: measure(freq). This
	is required for the MS side (i.e. OsmocomBB).

	"""

	def __init__(self, bind_addr, remote_addr, base_port,
			clck_gen = None, pwr_meas = None):
		# Connection info
		self.remote_addr = remote_addr
		self.bind_addr = bind_addr
		self.base_port = base_port

		# Init DATA interface
		self.data_if = DATAInterface(
			remote_addr, base_port + 102,
			bind_addr, base_port + 2)

		# Init CTRL interface
		self.ctrl_if = CTRLInterfaceTRX(self,
			remote_addr, base_port + 101,
			bind_addr, base_port + 1)

		# Init optional CLCK interface
		self.clck_gen = clck_gen
		if clck_gen is not None:
			self.clck_if = UDPLink(
				remote_addr, base_port + 100,
				bind_addr, base_port)

		# Optional Power Measurement interface
		self.pwr_meas = pwr_meas

		# Internal state
		self.running = False

		# Actual RX / TX frequencies
		self.rx_freq = None
		self.tx_freq = None

		# List of active (configured) timeslots
		self.ts_list = []

	# To be overwritten if required,
	# no custom command handlers by default
	def ctrl_cmd_handler(self, request):
		return None

	def power_event_handler(self, event):
		# Trigger clock generator if required
		if self.clck_gen is not None:
			clck_links = self.clck_gen.clck_links
			if not self.running and (self.clck_if in clck_links):
				# Transceiver was stopped
				clck_links.remove(self.clck_if)
			elif self.running and (self.clck_if not in clck_links):
				# Transceiver was started
				clck_links.append(self.clck_if)

			if not self.clck_gen.timer and len(clck_links) > 0:
				log.info("Starting clock generator")
				self.clck_gen.start()
			elif self.clck_gen.timer and not clck_links:
				log.info("Stopping clock generator")
				self.clck_gen.stop()

	def recv_data_msg(self):
		# Read and parse data from socket
		msg = self.data_if.recv_l12trx_msg()
		if not msg:
			return None

		# Make sure that transceiver is configured and running
		if not self.running:
			log.warning("RX DATA message (%s), but transceiver "
				"is not running => dropping..." % msg.desc_hdr())
			return None

		# Make sure that indicated timeslot is configured
		if msg.tn not in self.ts_list:
			log.warning("RX DATA message (%s), but timeslot "
				"is not configured => dropping..." % msg.desc_hdr())
			return None

		return msg