diff options
Diffstat (limited to 'src/target/trx_toolkit/transceiver.py')
-rw-r--r-- | src/target/trx_toolkit/transceiver.py | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py new file mode 100644 index 00000000..edd4d31f --- /dev/null +++ b/src/target/trx_toolkit/transceiver.py @@ -0,0 +1,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 |