enb: Implement initial RF emulation API
Two implementations are provided: * Amarisoft Ctrl interface (websocket) * Mini-Circuits Programmable Attenuator (HW, HTTP API) [1] in Amarisoft ENBs, if no rfemu is configured explicitly, the Ctrl interface one is used by default, while still being possible to use the HW one. [1] https://www.minicircuits.com/pdfs/RC4DAT-6G-60.pdf Change-Id: Ie98a3fb9bcd2b87b96ecbb5b79e0f53981892a32
This commit is contained in:
parent
ca89bb3924
commit
d4404d54c0
|
@ -113,6 +113,11 @@ class eNodeB(log.Origin, metaclass=ABCMeta):
|
|||
def ue_max_rate(self, downlink=True):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_rfemu(self, cell=0, dl=True):
|
||||
'Get rfemu.RFemulation subclass implementation object for given cell index and direction.'
|
||||
pass
|
||||
|
||||
def addr(self):
|
||||
return self._addr
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import pprint
|
|||
|
||||
from ..core import log, util, config, template, process, remote
|
||||
from . import enb
|
||||
from . import rfemu
|
||||
|
||||
def rf_type_valid(rf_type_str):
|
||||
return rf_type_str in ('uhd', 'zmq')
|
||||
|
@ -62,6 +63,7 @@ class AmarisoftENB(enb.eNodeB):
|
|||
self.run_dir = None
|
||||
self.inst = None
|
||||
self._bin_prefix = None
|
||||
self.gen_conf = None
|
||||
self.config_file = None
|
||||
self.config_sib1_file = None
|
||||
self.config_sib23_file = None
|
||||
|
@ -209,6 +211,8 @@ class AmarisoftENB(enb.eNodeB):
|
|||
config.overlay(values, dict(trx=dict(rf_dev_type=values['enb'].get('rf_dev_type', None),
|
||||
rf_dev_args=values['enb'].get('rf_dev_args', None))))
|
||||
|
||||
self.gen_conf = values
|
||||
|
||||
self.gen_conf_file(self.config_file, AmarisoftENB.CFGFILE, values)
|
||||
self.gen_conf_file(self.config_sib1_file, AmarisoftENB.CFGFILE_SIB1, values)
|
||||
self.gen_conf_file(self.config_sib23_file, AmarisoftENB.CFGFILE_SIB23, values)
|
||||
|
@ -233,4 +237,19 @@ class AmarisoftENB(enb.eNodeB):
|
|||
def running(self):
|
||||
return not self.process.terminated()
|
||||
|
||||
def get_rfemu(self, cell=0, dl=True):
|
||||
cell_list = self.gen_conf['enb'].get('cell_list', None)
|
||||
if cell_list is None or len(cell_list) < cell + 1:
|
||||
raise log.Error('cell_list attribute or subitem not found!')
|
||||
rfemu_cfg = cell_list[cell].get('dl_rfemu', None)
|
||||
if rfemu_cfg is None: # craft amarisfot by default:
|
||||
rfemu_cfg = {'type': 'amarisoftctl',
|
||||
'addr': self.addr(),
|
||||
'ports': [9001]
|
||||
}
|
||||
if rfemu_cfg['type'] == 'amarisoftctl': # this one requires extra config:
|
||||
config.overlay(rfemu_cfg, dict(cell_id=cell_list[cell]['cell_id']))
|
||||
rfemu_obj = rfemu.get_instance_by_type(rfemu_cfg['type'], rfemu_cfg)
|
||||
return rfemu_obj
|
||||
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
||||
|
|
|
@ -22,6 +22,7 @@ import pprint
|
|||
|
||||
from ..core import log, util, config, template, process, remote
|
||||
from . import enb
|
||||
from . import rfemu
|
||||
|
||||
def rf_type_valid(rf_type_str):
|
||||
return rf_type_str in ('zmq', 'uhd', 'soapy', 'bladerf')
|
||||
|
@ -60,6 +61,7 @@ class srsENB(enb.eNodeB):
|
|||
super().__init__(suite_run, conf, srsENB.BINFILE)
|
||||
self.ue = None
|
||||
self.run_dir = None
|
||||
self.gen_conf = None
|
||||
self.config_file = None
|
||||
self.config_sib_file = None
|
||||
self.config_rr_file = None
|
||||
|
@ -221,6 +223,8 @@ class srsENB(enb.eNodeB):
|
|||
|
||||
config.overlay(values, dict(enb=dict(rf_dev_args=rf_dev_args)))
|
||||
|
||||
self.gen_conf = values
|
||||
|
||||
self.gen_conf_file(self.config_file, srsENB.CFGFILE, values)
|
||||
self.gen_conf_file(self.config_sib_file, srsENB.CFGFILE_SIB, values)
|
||||
self.gen_conf_file(self.config_rr_file, srsENB.CFGFILE_RR, values)
|
||||
|
@ -243,4 +247,14 @@ class srsENB(enb.eNodeB):
|
|||
def running(self):
|
||||
return not self.process.terminated()
|
||||
|
||||
def get_rfemu(self, cell=0, dl=True):
|
||||
cell_list = self.gen_conf['enb'].get('cell_list', None)
|
||||
if cell_list is None or len(cell_list) < cell + 1:
|
||||
raise log.Error('cell_list attribute or subitem not found!')
|
||||
rfemu_cfg = cell_list[cell].get('dl_rfemu', None)
|
||||
if rfemu_cfg is None: # craft amarisfot by default:
|
||||
raise log.Error('rfemu attribute not found in cell_list item!')
|
||||
rfemu_obj = rfemu.get_instance_by_type(rfemu_cfg['type'], rfemu_cfg)
|
||||
return rfemu_obj
|
||||
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# osmo_gsm_tester: class defining a RF emulation object
|
||||
#
|
||||
# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
|
||||
#
|
||||
# Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from ..core import log
|
||||
from ..core.event_loop import MainLoop
|
||||
|
||||
class RFemulation(log.Origin, metaclass=ABCMeta):
|
||||
|
||||
##############
|
||||
# PROTECTED
|
||||
##############
|
||||
def __init__(self, conf, name):
|
||||
"""Base constructor. Must be called by subclass."""
|
||||
super().__init__(log.C_RUN, name)
|
||||
self.conf = conf
|
||||
|
||||
#############################
|
||||
# PUBLIC (test API included)
|
||||
#############################
|
||||
@abstractmethod
|
||||
def set_attenuation(self, db):
|
||||
"""Set attenuation in dB on the configured channel"""
|
||||
pass
|
||||
|
||||
def get_instance_by_type(rfemu_type, rfemu_opt):
|
||||
"""Allocate a RFemulation child class based on type. Opts are passed to the newly created object."""
|
||||
if rfemu_type == 'amarisoftctl':
|
||||
from .rfemu_amarisoftctrl import RFemulationAmarisoftCtrl
|
||||
obj = RFemulationAmarisoftCtrl
|
||||
elif rfemu_type == 'minicircuits':
|
||||
from .rfemu_minicircuits import RFemulationMinicircuitsHTTP
|
||||
obj = RFemulationMinicircuitsHTTP
|
||||
else:
|
||||
raise log.Error('RFemulation type not supported:', rfemu_type)
|
||||
|
||||
return obj(rfemu_opt)
|
||||
|
||||
|
||||
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
|
@ -0,0 +1,62 @@
|
|||
# osmo_gsm_tester: class defining a RF emulation object implemented using Amarisoft Ctl interface
|
||||
#
|
||||
# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
|
||||
#
|
||||
# Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import json
|
||||
from websocket import create_connection
|
||||
|
||||
from ..core import log
|
||||
from .rfemu import RFemulation
|
||||
|
||||
class RFemulationAmarisoftCtrl(RFemulation):
|
||||
##############
|
||||
# PROTECTED
|
||||
##############
|
||||
def __init__(self, conf):
|
||||
super().__init__(conf, 'amarisoftctl')
|
||||
self.addr = conf.get('addr')
|
||||
self.port = conf.get('ports')
|
||||
if self.addr is None:
|
||||
raise log.Error('No "addr" attribute provided in supply conf!')
|
||||
if self.port is None or len(self.port) != 1:
|
||||
raise log.Error('No "port" attribute provided in supply conf!')
|
||||
self.port = self.port[0]
|
||||
self.set_name('amarisoftctl(%s:%d)' % (self.addr, self.port))
|
||||
self.cell_id = conf.get('cell_id')
|
||||
if self.cell_id is None:
|
||||
raise log.Error('No "cell_id" attribute provided in supply conf!')
|
||||
self.ws = create_connection("ws://%s:%s" % (self.addr, self.port))
|
||||
|
||||
def __del__(self):
|
||||
self.dbg('closing CTRL websocket')
|
||||
self.ws.close()
|
||||
|
||||
#############################
|
||||
# PUBLIC (test API included)
|
||||
#############################
|
||||
def set_attenuation(self, db):
|
||||
msg = { "message": "cell_gain", "cell_id": int(self.cell_id), "gain": -db }
|
||||
msg_str = json.dumps(msg)
|
||||
self.dbg('sending CTRL msg: "%s"' % msg_str)
|
||||
self.ws.send(msg_str)
|
||||
self.dbg('waiting CTRL recv...')
|
||||
result = self.ws.recv()
|
||||
self.dbg('Received CTRL msg: "%s"' % result)
|
||||
|
||||
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
|
@ -0,0 +1,67 @@
|
|||
# osmo_gsm_tester: class defining a RF emulation object implemented using a Minicircuits RC4DAT-6G-60 device
|
||||
#
|
||||
# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
|
||||
#
|
||||
# Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from ..core import log
|
||||
from .rfemu import RFemulation
|
||||
|
||||
# https://www.minicircuits.com/softwaredownload/Prog_Manual-6-Programmable_Attenuator.pdf
|
||||
class RFemulationMinicircuitsHTTP(RFemulation):
|
||||
|
||||
# HTTP request timeout, in seconds
|
||||
HTTP_TIMEOUT = 5
|
||||
|
||||
##############
|
||||
# PROTECTED
|
||||
##############
|
||||
def __init__(self, conf):
|
||||
super().__init__(conf, 'minicircuits')
|
||||
self.addr = conf.get('addr')
|
||||
self.ports = conf.get('ports')
|
||||
if self.addr is None:
|
||||
raise log.Error('No "addr" attribute provided in supply conf!')
|
||||
if self.ports is None or len(self.ports) == 0:
|
||||
raise log.Error('No "port" attribute provided in supply conf!')
|
||||
self.set_name('minicircuits(%s:%r)' % (self.addr, self.ports))
|
||||
|
||||
def _url_prefix(self):
|
||||
#http://10.12.1.216/:SetAttPerChan:1:0_2:0_3:0_4:0
|
||||
return 'http://' + self.addr
|
||||
|
||||
def _utl_set_attenauation(self, db):
|
||||
ports_str = ""
|
||||
for port in self.ports:
|
||||
ports_str = ports_str + str(port) + ":"
|
||||
|
||||
return self._url_prefix() + '/:CHAN:' + ports_str + 'SETATT:' + str(db)
|
||||
|
||||
#############################
|
||||
# PUBLIC (test API included)
|
||||
#############################
|
||||
def set_attenuation(self, db):
|
||||
url = self._utl_set_attenauation(db)
|
||||
self.dbg('sending HTTP req: "%s"' % url)
|
||||
data = urllib.request.urlopen(url, timeout = self.HTTP_TIMEOUT).read()
|
||||
data_str = str(data, 'utf-8')
|
||||
self.dbg('Received response: "%s"' % data_str)
|
||||
if data_str != '1':
|
||||
raise log.Error('Mini-circuits attenuation device returned failure! %s' & data_str)
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
|
@ -111,6 +111,9 @@ RESOURCES_SCHEMA = {
|
|||
'enb[].cell_list[].cell_id': schema.UINT,
|
||||
'enb[].cell_list[].scell_list[]': schema.UINT,
|
||||
'enb[].cell_list[].dl_earfcn': schema.UINT,
|
||||
'enb[].cell_list[].dl_rfemu.type': schema.STR,
|
||||
'enb[].cell_list[].dl_rfemu.addr': schema.IPV4,
|
||||
'enb[].cell_list[].dl_rfemu.ports[]': schema.UINT,
|
||||
'arfcn[].arfcn': schema.INT,
|
||||
'arfcn[].band': schema.BAND,
|
||||
'modem[].type': schema.STR,
|
||||
|
|
Loading…
Reference in New Issue