Change in osmo-gsm-tester[master]: Initial srsLTE 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/.

pespin gerrit-no-reply at lists.osmocom.org
Fri Feb 14 14:24:08 UTC 2020


pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-gsm-tester/+/17139 )

Change subject: Initial srsLTE support
......................................................................

Initial srsLTE support

2 tests (iperf3, ping) working against a full srs{UE,ENB,EPC} network
using ZeroMQ backend for RF (so no real RF support yet, that will come
next).

Related: OS##4295, OS#4296

Change-Id: I290c0d79258a9f94f00c7ff2e1c6c5579c0e32f4
---
M example/defaults.conf
M example/resources.conf.prod
M src/osmo_gsm_tester/resource.py
A src/osmo_gsm_tester/srs_enb.py
A src/osmo_gsm_tester/srs_epc.py
A src/osmo_gsm_tester/srs_ue.py
M src/osmo_gsm_tester/suite.py
A src/osmo_gsm_tester/templates/srsenb.conf.tmpl
A src/osmo_gsm_tester/templates/srsenb_drb.conf.tmpl
A src/osmo_gsm_tester/templates/srsenb_rr.conf.tmpl
A src/osmo_gsm_tester/templates/srsenb_sib.conf.tmpl
A src/osmo_gsm_tester/templates/srsepc.conf.tmpl
A src/osmo_gsm_tester/templates/srsepc_user_db.csv.tmpl
A src/osmo_gsm_tester/templates/srsue.conf.tmpl
A suites/4g/iperf3.py
D suites/4g/mo_mt_sms.py
A suites/4g/ping.py
M suites/4g/suite.conf
18 files changed, 1,769 insertions(+), 24 deletions(-)

Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, approved



diff --git a/example/defaults.conf b/example/defaults.conf
index 353cf9b..f32e1f5 100644
--- a/example/defaults.conf
+++ b/example/defaults.conf
@@ -92,3 +92,11 @@
   max_trx: 1
   trx_list:
   - nominal_power: 25
+
+srsepc:
+  mcc: 901
+  mnc: 70
+
+srsenb:
+  mcc: 901
+  mnc: 70
diff --git a/example/resources.conf.prod b/example/resources.conf.prod
index 992a694..943160b 100644
--- a/example/resources.conf.prod
+++ b/example/resources.conf.prod
@@ -11,6 +11,12 @@
 - addr: 10.42.42.9
 - addr: 10.42.42.10
 
+run_node:
+- run_type: ssh
+  run_addr: 10.42.42.118
+  ssh_user: jenkins
+  ssh_addr: 10.42.42.116
+
 bts:
 - label: sysmoBTS 1002
   type: osmo-bts-sysmo
@@ -148,6 +154,12 @@
   direct_pcu: true
   ciphers: [a5_0, a5_1, a5_3]
 
+enb:
+- label: srsENB-zmq
+  type: srsenb
+  remote_user: jenkins
+  addr: 10.42.42.117
+
 arfcn:
   - arfcn: 512
     band: GSM-1800
@@ -227,5 +239,14 @@
   ciphers: [a5_0, a5_1]
   features: ['sms', 'voice', 'ussd', 'gprs', 'sim']
 
+- label: srsUE-zmq_1
+  type: srsue
+  remote_user: jenkins
+  addr: 10.42.42.116
+  imsi: '001010123456789'
+  ki: '00112233445566778899aabbccddeeff'
+  auth_algo: 'xor'
+
+
 osmocon_phone:
   - serial_device: '/dev/serial/by-id/usb-Silicon_Labs_CP2104_USB_to_UART_Bridge_Controller_00897B41-if00-port0'
diff --git a/src/osmo_gsm_tester/resource.py b/src/osmo_gsm_tester/resource.py
index 0cdcb8a..0804591 100644
--- a/src/osmo_gsm_tester/resource.py
+++ b/src/osmo_gsm_tester/resource.py
@@ -29,6 +29,7 @@
 from . import bts_sysmo, bts_osmotrx, bts_osmovirtual, bts_octphy, bts_nanobts, bts_oc2g
 from . import modem
 from . import ms_osmo_mobile
+from . import srs_ue, srs_enb
 
 from .util import is_dict, is_list
 
@@ -45,7 +46,8 @@
 R_ARFCN = 'arfcn'
 R_MODEM = 'modem'
 R_OSMOCON = 'osmocon_phone'
-R_ALL = (R_IP_ADDRESS, R_RUN_NODE, R_BTS, R_ARFCN, R_MODEM, R_OSMOCON)
+R_ENB = 'enb'
+R_ALL = (R_IP_ADDRESS, R_RUN_NODE, R_BTS, R_ARFCN, R_MODEM, R_OSMOCON, R_ENB)
 
 RESOURCES_SCHEMA = {
         'ip_address[].addr': schema.IPV4,
@@ -83,6 +85,11 @@
         'bts[].osmo_trx.max_trxd_version': schema.UINT,
         'bts[].osmo_trx.channels[].rx_path': schema.STR,
         'bts[].osmo_trx.channels[].tx_path': schema.STR,
+        'enb[].label': schema.STR,
+        'enb[].type': schema.STR,
+        'enb[].remote_user': schema.STR,
+        'enb[].addr': schema.IPV4,
+        'enb[].band': schema.BAND,
         'arfcn[].arfcn': schema.INT,
         'arfcn[].band': schema.BAND,
         'modem[].type': schema.STR,
@@ -91,6 +98,8 @@
         'modem[].imsi': schema.IMSI,
         'modem[].ki': schema.KI,
         'modem[].auth_algo': schema.AUTH_ALGO,
+        'modem[].remote_user': schema.STR,
+        'modem[].addr': schema.IPV4,
         'modem[].ciphers[]': schema.CIPHER,
         'modem[].features[]': schema.MODEM_FEATURE,
         'osmocon_phone[].serial_device': schema.STR,
@@ -115,12 +124,16 @@
         'nanobts': bts_nanobts.NanoBts,
     }
 
+KNOWN_ENB_TYPES = {
+        'srsenb': srs_enb.srsENB,
+}
 
 KNOWN_MS_TYPES = {
         # Map None to ofono for forward compability
         None: modem.Modem,
         'ofono': modem.Modem,
         'osmo-mobile': ms_osmo_mobile.MSOsmoMobile,
+        'srsue': srs_ue.srsUE,
 }
 
 
diff --git a/src/osmo_gsm_tester/srs_enb.py b/src/osmo_gsm_tester/srs_enb.py
new file mode 100644
index 0000000..2b65a5b
--- /dev/null
+++ b/src/osmo_gsm_tester/srs_enb.py
@@ -0,0 +1,197 @@
+# osmo_gsm_tester: specifics for running an SRS eNodeB process
+#
+# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at 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 os
+import pprint
+
+from . import log, util, config, template, process, remote
+
+class srsENB(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-srsenb'
+    BINFILE = 'srsenb'
+    CFGFILE = 'srsenb.conf'
+    CFGFILE_SIB = 'srsenb_sib.conf'
+    CFGFILE_RR = 'srsenb_rr.conf'
+    CFGFILE_DRB = 'srsenb_drb.conf'
+    LOGFILE = 'srsenb.log'
+
+    def __init__(self, suite_run, conf):
+        super().__init__(log.C_RUN, 'srsenb')
+        self._addr = conf.get('addr', None)
+        if self._addr is None:
+            raise log.Error('addr not set')
+        self.set_name('srsenb_%s' % self._addr)
+        self.ue = None
+        self.epc = None
+        self.run_dir = None
+        self.config_file = None
+        self.config_sib_file = None
+        self.config_rr_file = None
+        self.config_drb_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_config_file =  None
+        self.remote_config_sib_file = None
+        self.remote_config_rr_file = None
+        self.remote_config_drb_file = None
+        self.remote_log_file = None
+        self.suite_run = suite_run
+        self.nof_prb=100
+        if self.nof_prb == 75:
+          self.base_srate=15.36e6
+        else:
+          self.base_srate=23.04e6
+        self.remote_user = conf.get('remote_user', None)
+
+    def cleanup(self):
+        if self.process is None:
+            return
+        if self.setup_runs_locally():
+            return
+        # copy back files (may not exist, for instance if there was an early error of process):
+        try:
+            self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file)
+        except Exception as e:
+            self.log(repr(e))
+
+    def setup_runs_locally(self):
+        return self.remote_user is None
+
+    def start(self, epc):
+        self.log('Starting srsENB')
+        self.epc = epc
+        self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name()))
+        self.configure()
+        if self.remote_user:
+            self.start_remotely()
+        else:
+            self.start_locally()
+
+    def start_remotely(self):
+        self.inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte')))
+        lib = self.inst.child('lib')
+        if not os.path.isdir(lib):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', srsENB.BINFILE):
+            raise log.Error('No %s binary in' % srsENB.BINFILE, self.inst)
+
+        self.rem_host = remote.RemoteHost(self.run_dir, self.remote_user, self._addr)
+        remote_prefix_dir = util.Dir(srsENB.REMOTE_DIR)
+        self.remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst))))
+        remote_run_dir = util.Dir(remote_prefix_dir.child(srsENB.BINFILE))
+
+        self.remote_config_file = remote_run_dir.child(srsENB.CFGFILE)
+        self.remote_config_sib_file = remote_run_dir.child(srsENB.CFGFILE_SIB)
+        self.remote_config_rr_file = remote_run_dir.child(srsENB.CFGFILE_RR)
+        self.remote_config_drb_file = remote_run_dir.child(srsENB.CFGFILE_DRB)
+        self.remote_log_file = remote_run_dir.child(srsENB.LOGFILE)
+
+        self.rem_host.recreate_remote_dir(self.remote_inst)
+        self.rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir)
+        self.rem_host.create_remote_dir(remote_run_dir)
+        self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file)
+        self.rem_host.scp('scp-cfg-sib-to-remote', self.config_sib_file, self.remote_config_sib_file)
+        self.rem_host.scp('scp-cfg-rr-to-remote', self.config_rr_file, self.remote_config_rr_file)
+        self.rem_host.scp('scp-cfg-drb-to-remote', self.config_drb_file, self.remote_config_drb_file)
+
+        remote_env = { 'LD_LIBRARY_PATH': self.remote_inst.child('lib') }
+        remote_binary = self.remote_inst.child('bin', srsENB.BINFILE)
+        args = (remote_binary, self.remote_config_file,
+                '--enb_files.sib_config=' + self.remote_config_sib_file,
+                '--enb_files.rr_config=' + self.remote_config_rr_file,
+                '--enb_files.drb_config=' + self.remote_config_drb_file,
+                '--rf.device_name=zmq',
+                '--rf.device_args="fail_on_disconnect=true,tx_port=tcp://'+ self.addr() +':2000,rx_port=tcp://'+ self.ue.addr() +':2001,id=enb,base_srate='+ str(self.base_srate) + '"',
+                '--expert.nof_phy_threads=1',
+                '--expert.rrc_inactivity_timer=1500',
+                '--enb.n_prb=' + str(self.nof_prb),
+                '--log.filename=' + self.remote_log_file)
+
+        self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsENB.BINFILE, util.Dir(srsENB.REMOTE_DIR), args, remote_env=remote_env)
+        self.suite_run.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte')))
+
+        binary = inst.child('bin', BINFILE)
+        if not os.path.isfile(binary):
+            raise log.Error('Binary missing:', binary)
+        lib = inst.child('lib')
+        if not os.path.isdir(lib):
+            raise log.Error('No lib/ in', inst)
+
+        env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
+
+        self.dbg(run_dir=self.run_dir, binary=binary, env=env)
+        args = (binary, os.path.abspath(self.config_file),
+                '--enb_files.sib_config=' + os.path.abspath(self.config_sib_file),
+                '--enb_files.rr_config=' + os.path.abspath(self.config_rr_file),
+                '--enb_files.drb_config=' + os.path.abspath(self.config_drb_file),
+                '--rf.device_name=zmq',
+                '--rf.device_args="fail_on_disconnect=true,tx_port=tcp://'+ self.addr() +':2000,rx_port=tcp://'+ self.ue.addr() +':2001,id=enb,base_srate='+ str(self.base_srate) + '"',
+                '--expert.nof_phy_threads=1',
+                '--expert.rrc_inactivity_timer=1500',
+                '--enb.n_prb=' + str(self.nof_prb),
+                '--log.filename=' + self.log_file)
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.suite_run.remember_to_stop(self.process)
+        self.process.launch()
+
+    def gen_conf_file(self, path, filename):
+        self.dbg(config_file=path)
+
+        values = dict(enb=config.get_defaults('srsenb'))
+        config.overlay(values, self.suite_run.config())
+        config.overlay(values, dict(enb={ 'run_addr': self.addr(),
+                                          'mme_addr': self.epc.addr()}))
+
+        self.dbg('srsENB ' + filename + ':\n' + pprint.pformat(values))
+
+        with open(path, 'w') as f:
+            r = template.render(filename, values)
+            self.dbg(r)
+            f.write(r)
+
+    def configure(self):
+        self.config_file = self.run_dir.new_file(srsENB.CFGFILE)
+        self.config_sib_file = self.run_dir.new_file(srsENB.CFGFILE_SIB)
+        self.config_rr_file = self.run_dir.new_file(srsENB.CFGFILE_RR)
+        self.config_drb_file = self.run_dir.new_file(srsENB.CFGFILE_DRB)
+        self.log_file = self.run_dir.new_file(srsENB.LOGFILE)
+
+        self.gen_conf_file(self.config_file, srsENB.CFGFILE)
+        self.gen_conf_file(self.config_sib_file, srsENB.CFGFILE_SIB)
+        self.gen_conf_file(self.config_rr_file, srsENB.CFGFILE_RR)
+        self.gen_conf_file(self.config_drb_file, srsENB.CFGFILE_DRB)
+
+    def ue_add(self, ue):
+        if self.ue is not None:
+            raise log.Error("More than one UE per ENB not yet supported (ZeroMQ)")
+        self.ue = ue
+
+    def running(self):
+        return not self.process.terminated()
+
+    def addr(self):
+        return self._addr
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/srs_epc.py b/src/osmo_gsm_tester/srs_epc.py
new file mode 100644
index 0000000..0a0036e
--- /dev/null
+++ b/src/osmo_gsm_tester/srs_epc.py
@@ -0,0 +1,207 @@
+# osmo_gsm_tester: specifics for running an SRS EPC process
+#
+# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at 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 os
+import pprint
+
+from . import log, util, config, template, process, remote
+
+class srsEPC(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-srsepc'
+    BINFILE = 'srsepc'
+    CFGFILE = 'srsepc.conf'
+    DBFILE = 'srsepc_user_db.csv'
+    PCAPFILE = 'srsepc.pcap'
+    LOGFILE = 'srsepc.log'
+
+    def __init__(self, suite_run, run_node):
+        super().__init__(log.C_RUN, 'srsepc')
+        self._addr = run_node.run_addr()
+        self.set_name('srsepc_%s' % self._addr)
+        self.run_dir = None
+        self.config_file = None
+        self.db_file = None
+        self.log_file = None
+        self.pcap_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_config_file = None
+        self.remote_db_file = None
+        self.remote_log_file = None
+        self.remote_pcap_file = None
+        self.subscriber_list = []
+        self.suite_run = suite_run
+        self._run_node = run_node
+
+    def cleanup(self):
+        if self.process is None:
+            return
+        if self._run_node.is_local():
+            return
+        # copy back files (may not exist, for instance if there was an early error of process):
+        try:
+            self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file)
+        except Exception as e:
+            self.log(repr(e))
+        try:
+            self.rem_host.scpfrom('scp-back-pcap', self.remote_pcap_file, self.pcap_file)
+        except Exception as e:
+            self.log(repr(e))
+
+    def start(self):
+        self.log('Starting srsepc')
+        self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name()))
+        self.configure()
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        self.inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte')))
+        lib = self.inst.child('lib')
+        if not os.path.isdir(lib):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', srsEPC.BINFILE):
+            raise log.Error('No %s binary in' % srsEPC.BINFILE, self.inst)
+
+        self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+        remote_prefix_dir = util.Dir(srsEPC.REMOTE_DIR)
+        remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst))))
+        remote_run_dir = util.Dir(remote_prefix_dir.child(srsEPC.BINFILE))
+        self.remote_config_file = remote_run_dir.child(srsEPC.CFGFILE)
+        self.remote_db_file = remote_run_dir.child(srsEPC.DBFILE)
+        self.remote_log_file = remote_run_dir.child(srsEPC.LOGFILE)
+        self.remote_pcap_file = remote_run_dir.child(srsEPC.PCAPFILE)
+
+        self.rem_host.recreate_remote_dir(remote_inst)
+        self.rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir)
+        self.rem_host.create_remote_dir(remote_run_dir)
+        self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file)
+        self.rem_host.scp('scp-db-to-remote', self.db_file, self.remote_db_file)
+
+        remote_lib = remote_inst.child('lib')
+        remote_binary = remote_inst.child('bin', srsEPC.BINFILE)
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for srsepc')
+        self.rem_host.change_elf_rpath(remote_binary, remote_lib)
+        # srsepc requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_NET_ADMIN capability to srsepc')
+        self.rem_host.setcap_net_admin(remote_binary)
+
+        args = (remote_binary, self.remote_config_file,
+                '--hss.db_file=' + self.remote_db_file,
+                '--log.filename=' + self.remote_log_file,
+                '--pcap.enable=true',
+                '--pcap.filename=' + self.remote_pcap_file)
+
+        self.process = self.rem_host.RemoteProcess(srsEPC.BINFILE, args)
+        #self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsEPC.BINFILE, remote_run_dir, args)
+        self.suite_run.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte')))
+
+        binary = inst.child('bin', BINFILE)
+        if not os.path.isfile(binary):
+            raise log.Error('Binary missing:', binary)
+        lib = inst.child('lib')
+        if not os.path.isdir(lib):
+            raise log.Error('No lib/ in', inst)
+
+        env = {}
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for srsepc')
+        # srsepc binary needs patchelf <= 0.9 (0.10 and current master fail) to avoid failing during patch. OS#4389, patchelf-GH#192.
+        util.change_elf_rpath(binary, util.prepend_library_path(lib), self.run_dir.new_dir('patchelf'))
+        # srsepc requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_NET_ADMIN capability to srsepc')
+        util.setcap_net_admin(binary, self.run_dir.new_dir('setcap_net_admin'))
+
+        self.dbg(run_dir=self.run_dir, binary=binary, env=env)
+        args = (binary, os.path.abspath(self.config_file),
+                '--hss.db_file=' + self.db_file,
+                '--log.filename=' + self.log_file,
+                '--pcap.enable=true',
+                '--pcap.filename=' + self.pcap_file)
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.suite_run.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self):
+        self.config_file = self.run_dir.new_file(srsEPC.CFGFILE)
+        self.db_file = self.run_dir.new_file(srsEPC.DBFILE)
+        self.log_file = self.run_dir.new_file(srsEPC.LOGFILE)
+        self.pcap_file = self.run_dir.new_file(srsEPC.PCAPFILE)
+        self.dbg(config_file=self.config_file, db_file=self.db_file)
+
+        values = dict(epc=config.get_defaults('srsepc'))
+        config.overlay(values, dict(epc=dict(hss=dict(subscribers=self.subscriber_list))))
+        config.overlay(values, self.suite_run.config())
+        config.overlay(values, dict(epc={'run_addr': self.addr()}))
+
+        self.dbg('SRSEPC CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(srsEPC.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+        with open(self.db_file, 'w') as f:
+            r = template.render(srsEPC.DBFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+    def subscriber_add(self, modem, msisdn=None, algo_str=None):
+        if msisdn is None:
+            msisdn = self.suite_run.resources_pool.next_msisdn(modem)
+        modem.set_msisdn(msisdn)
+
+        if algo_str is None:
+            algo_str = modem.auth_algo() or util.OSMO_AUTH_ALGO_NONE
+
+        if algo_str != util.OSMO_AUTH_ALGO_NONE and not modem.ki():
+            raise log.Error("Auth algo %r selected but no KI specified" % algo_str)
+
+        subscriber_id = len(self.subscriber_list) # list index
+        self.subscriber_list.append({'id': subscriber_id, 'imsi': modem.imsi(), 'msisdn': msisdn, 'auth_algo': algo_str, 'ki': modem.ki(), 'opc': None})
+
+        self.log('Add subscriber', msisdn=msisdn, imsi=modem.imsi(), subscriber_id=subscriber_id,
+                 algo_str=algo_str)
+        return subscriber_id
+
+    def enb_is_connected(self, enb):
+        # FIXME: srspec's stdout: "S1 Setup Request - eNB Id 0x66c0", but srsenb.conf has "enb_id = 0x19B"
+        return 'S1 Setup Request - eNB Id' in (self.process.get_stdout() or '')
+
+    def running(self):
+        return not self.process.terminated()
+
+    def addr(self):
+        return self._addr
+
+    def tun_addr(self):
+        return '172.16.0.1'
+
+    def run_node(self):
+        return self._run_node
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/srs_ue.py b/src/osmo_gsm_tester/srs_ue.py
new file mode 100644
index 0000000..c68c726
--- /dev/null
+++ b/src/osmo_gsm_tester/srs_ue.py
@@ -0,0 +1,213 @@
+# osmo_gsm_tester: specifics for running an SRS UE process
+#
+# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at 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 os
+import pprint
+
+from . import log, util, config, template, process, remote
+from .run_node import RunNode
+from .ms import MS
+
+class srsUE(MS):
+
+    REMOTE_DIR = '/osmo-gsm-tester-srsue'
+    BINFILE = 'srsue'
+    CFGFILE = 'srsue.conf'
+    PCAPFILE = 'srsue.pcap'
+    LOGFILE = 'srsue.log'
+
+    def __init__(self, suite_run, conf):
+        self._addr = conf.get('addr', None)
+        if self._addr is None:
+            raise log.Error('addr not set')
+        super().__init__('srsue_%s' % self._addr, conf)
+        self.enb = None
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.pcap_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+        self.remote_pcap_file = None
+        self.suite_run = suite_run
+        self.nof_prb=50
+        if self.nof_prb == 75:
+          self.base_srate=15.36e6
+        else:
+          self.base_srate=23.04e6
+        self.remote_user = conf.get('remote_user', None)
+
+    def cleanup(self):
+        if self.process is None:
+            return
+        if self.setup_runs_locally():
+            return
+        # copy back files (may not exist, for instance if there was an early error of process):
+        try:
+            self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file)
+        except Exception as e:
+            self.log(repr(e))
+        try:
+            self.rem_host.scpfrom('scp-back-pcap', self.remote_pcap_file, self.pcap_file)
+        except Exception as e:
+            self.log(repr(e))
+
+    def setup_runs_locally(self):
+        return self.remote_user is None
+
+    def netns(self):
+        return "srsue1"
+
+    def connect(self, enb):
+        self.log('Starting srsue')
+        self.enb = enb
+        self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name()))
+        self.configure()
+        if self.setup_runs_locally():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        self.inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte')))
+        lib = self.inst.child('lib')
+        if not os.path.isdir(lib):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', srsUE.BINFILE):
+            raise log.Error('No %s binary in' % srsUE.BINFILE, self.inst)
+
+        self.rem_host = remote.RemoteHost(self.run_dir, self.remote_user, self._addr)
+        remote_prefix_dir = util.Dir(srsUE.REMOTE_DIR)
+        remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst))))
+        remote_run_dir = util.Dir(remote_prefix_dir.child(srsUE.BINFILE))
+        self.remote_config_file = remote_run_dir.child(srsUE.CFGFILE)
+        self.remote_log_file = remote_run_dir.child(srsUE.LOGFILE)
+        self.remote_pcap_file = remote_run_dir.child(srsUE.PCAPFILE)
+
+        self.rem_host.recreate_remote_dir(remote_inst)
+        self.rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir)
+        self.rem_host.create_remote_dir(remote_run_dir)
+        self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file)
+
+        remote_lib = remote_inst.child('lib')
+        remote_binary = remote_inst.child('bin', srsUE.BINFILE)
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for srsue')
+        # srsue binary needs patchelf >= 0.9+52 to avoid failing during patch. OS#4389, patchelf-GH#192.
+        self.rem_host.set_remote_env({'PATCHELF_BIN': '/opt/bin/patchelf-v0.10' })
+        self.rem_host.change_elf_rpath(remote_binary, remote_lib)
+
+        # srsue requires CAP_SYS_ADMIN to cjump to net network namespace: netns(CLONE_NEWNET):
+        # srsue requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_SYS_ADMIN+CAP_NET_ADMIN capability to srsue')
+        self.rem_host.setcap_netsys_admin(remote_binary)
+
+        #'strace', '-ff',
+        args = (remote_binary, self.remote_config_file,
+                '--rf.device_name=zmq',
+                '--rf.device_args="tx_port=tcp://'+ self.addr() +':2001,rx_port=tcp://'+ self.enb.addr() +':2000,id=ue,base_srate='+ str(self.base_srate) + '"',
+                '--phy.nof_phy_threads=1',
+                '--gw.netns=' + self.netns(),
+                '--log.filename=' + 'stdout', #self.remote_log_file,
+                '--pcap.enable=true',
+                '--pcap.filename=' + self.remote_pcap_file)
+
+        self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsUE.BINFILE, util.Dir(srsUE.REMOTE_DIR), args)
+        #self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsUE.BINFILE, remote_run_dir, args, remote_lib)
+        self.suite_run.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte')))
+
+        binary = inst.child('bin', BINFILE)
+        if not os.path.isfile(binary):
+            raise log.Error('Binary missing:', binary)
+        lib = inst.child('lib')
+        if not os.path.isdir(lib):
+            raise log.Error('No lib/ in', inst)
+
+        env = {}
+
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for srsue')
+        util.change_elf_rpath(binary, util.prepend_library_path(lib), self.run_dir.new_dir('patchelf'))
+
+        # srsue requires CAP_SYS_ADMIN to cjump to net network namespace: netns(CLONE_NEWNET):
+        # srsue requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_SYS_ADMIN+CAP_NET_ADMIN capability to srsue')
+        util.setcap_netsys_admin(binary, self.run_dir.new_dir('setcap_netsys_admin'))
+
+        args = (binary, os.path.abspath(self.config_file),
+                '--rf.device_name=zmq',
+                '--rf.device_args="tx_port=tcp://'+ self.addr() +':2001,rx_port=tcp://'+ self.enb.addr() +':2000,id=ue,base_srate='+ str(self.base_srate) + '"',
+                '--phy.nof_phy_threads=1',
+                '--gw.netns=' + self.netns(),
+                '--log.filename=' + self.log_file,
+                '--pcap.enable=true',
+                '--pcap.filename=' + self.pcap_file)
+
+        self.dbg(run_dir=self.run_dir, binary=binary, env=env)
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.suite_run.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self):
+        self.config_file = self.run_dir.new_file(srsUE.CFGFILE)
+        self.log_file = self.run_dir.child(srsUE.LOGFILE)
+        self.pcap_file = self.run_dir.new_file(srsUE.PCAPFILE)
+        self.dbg(config_file=self.config_file)
+
+        values = dict(ue=config.get_defaults('srsue'))
+        config.overlay(values, self.suite_run.config())
+        config.overlay(values, dict(ue=self._conf))
+
+        self.dbg('SRSUE CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(srsUE.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+    def is_connected(self, mcc_mnc=None):
+        return 'Network attach successful.' in (self.process.get_stdout() or '')
+
+    def is_attached(self):
+        return self.is_connected()
+
+    def running(self):
+        return not self.process.terminated()
+
+    def addr(self):
+        return self._addr
+
+    def run_node(self):
+        # TODO: move to an object
+        return RunNode(RunNode.T_REM_SSH, self._addr, self.remote_user, self._addr)
+
+    def run_netns_wait(self, name, popen_args):
+        if self.setup_runs_locally():
+            proc = process.NetNSProcess(name, self.run_dir.new_dir(name), self.netns(), popen_args, env={})
+        else:
+            proc = self.rem_host.RemoteNetNSProcess(name, self.netns(), popen_args, env={})
+        proc.launch_sync()
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/suite.py b/src/osmo_gsm_tester/suite.py
index 457bfb3..c2faa36 100644
--- a/src/osmo_gsm_tester/suite.py
+++ b/src/osmo_gsm_tester/suite.py
@@ -25,6 +25,7 @@
 from .event_loop import MainLoop
 from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, osmo_ggsn, osmo_sgsn, esme, osmocon, ms_driver, iperf3, process
 from . import run_node
+from . import srs_epc
 
 class Timeout(Exception):
     pass
@@ -362,6 +363,18 @@
     def run_node(self, specifics=None):
         return run_node.RunNode.from_conf(self.reserved_resources.get(resource.R_RUN_NODE, specifics=specifics))
 
+    def enb(self, specifics=None):
+        enb = enb_obj(self, self.reserved_resources.get(resource.R_ENB, specifics=specifics))
+        self.register_for_cleanup(enb)
+        return enb
+
+    def epc(self, run_node=None):
+        if run_node is None:
+            run_node = self.run_node()
+        epc_obj = srs_epc.srsEPC(self, run_node)
+        self.register_for_cleanup(epc_obj)
+        return epc_obj
+
     def osmocon(self, specifics=None):
         conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)
         osmocon_obj = osmocon.Osmocon(self, conf=conf)
@@ -481,4 +494,12 @@
         raise RuntimeError('No such BTS type is defined: %r' % bts_type)
     return bts_class(suite_run, conf)
 
+def enb_obj(suite_run, conf):
+    enb_type = conf.get('type')
+    log.dbg('create ENB object', type=enb_type)
+    enb_class = resource.KNOWN_ENB_TYPES.get(enb_type)
+    if enb_class is None:
+        raise RuntimeError('No such ENB type is defined: %r' % enb_type)
+    return enb_class(suite_run, conf)
+
 # vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/templates/srsenb.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb.conf.tmpl
new file mode 100644
index 0000000..0eba35b
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/srsenb.conf.tmpl
@@ -0,0 +1,279 @@
+#####################################################################
+#                   srsENB configuration file
+#####################################################################
+
+#####################################################################
+# eNB configuration
+#
+# enb_id:         20-bit eNB identifier.
+# cell_id:        8-bit cell identifier.
+# tac:            16-bit Tracking Area Code.
+# mcc:            Mobile Country Code
+# mnc:            Mobile Network Code
+# mme_addr:       IP address of MME for S1 connnection
+# gtp_bind_addr:  Local IP address to bind for GTP connection
+# s1c_bind_addr:  Local IP address to bind for S1AP connection
+# n_prb:          Number of Physical Resource Blocks (6,15,25,50,75,100)
+# tm:             Transmission mode 1-4 (TM1 default)
+# nof_ports:      Number of Tx ports (1 port default, set to 2 for TM2/3/4)
+#
+#####################################################################
+[enb]
+enb_id = 0x19B
+cell_id = 0x01
+phy_cell_id = 1
+tac = 0x0007
+mcc = ${enb.mcc}
+mnc = ${enb.mnc}
+mme_addr = ${enb.mme_addr}
+gtp_bind_addr = ${enb.run_addr}
+s1c_bind_addr = ${enb.run_addr}
+n_prb = 50
+#tm = 4
+#nof_ports = 2
+
+#####################################################################
+# eNB configuration files
+#
+# sib_config:  SIB1, SIB2 and SIB3 configuration file
+# note: when enabling mbms, use the sib.conf.mbsfn configuration file which includes SIB13
+# rr_config:   Radio Resources configuration file
+# drb_config:  DRB configuration file
+#####################################################################
+[enb_files]
+sib_config = sib.conf
+rr_config  = rr.conf
+drb_config = drb.conf
+
+#####################################################################
+# RF configuration
+#
+# dl_earfcn: EARFCN code for DL
+# tx_gain: Transmit gain (dB).
+# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled
+#
+# Optional parameters:
+# dl_freq:            Override DL frequency corresponding to dl_earfcn
+# ul_freq:            Override UL frequency corresponding to dl_earfcn (must be set if dl_freq is set)
+# device_name:        Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF"
+# device_args:        Arguments for the device driver. Options are "auto" or any string.
+#                     Default for UHD: "recv_frame_size=9232,send_frame_size=9232"
+#                     Default for bladeRF: ""
+# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay
+#                     from antenna to timestamp insertion.
+#                     Default "auto". B210 USRP: 100 samples, bladeRF: 27.
+# burst_preamble_us:  Preamble length to transmit before start of burst.
+#                     Default "auto". B210 USRP: 400 us, bladeRF: 0 us.
+#####################################################################
+[rf]
+dl_earfcn = 3400
+tx_gain = 80
+rx_gain = 40
+
+#device_name = auto
+
+# For best performance in 2x2 MIMO and >= 15 MHz use the following device_args settings:
+#     USRP B210: num_recv_frames=64,num_send_frames=64
+
+# For best performance when BW<5 MHz (25 PRB), use the following device_args settings:
+#     USRP B210: send_frame_size=512,recv_frame_size=512
+
+#device_args = auto
+#time_adv_nsamples = auto
+#burst_preamble_us = auto
+
+
+#####################################################################
+# MAC-layer packet capture configuration
+#
+# Packets are captured to file in the compact format decoded by
+# the Wireshark mac-lte-framed dissector and with DLT 147.
+# To use the dissector, edit the preferences for DLT_USER to
+# add an entry with DLT=147, Payload Protocol=mac-lte-framed.
+# For more information see: https://wiki.wireshark.org/MAC-LTE
+#
+# Please note that this setting will by default only capture MAC
+# frames on dedicated channels, and not SIB.  You have to build with
+# WRITE_SIB_PCAP enabled in enb/src/stack/mac/mac.cc if you want
+# SIB to be part of the MAC pcap file.
+#
+# enable:   Enable MAC layer packet captures (true/false)
+# filename: File path to use for packet captures
+#####################################################################
+[pcap]
+enable = false
+filename = /tmp/enb.pcap
+
+#####################################################################
+# Log configuration
+#
+# Log levels can be set for individual layers. "all_level" sets log
+# level for all layers unless otherwise configured.
+# Format: e.g. phy_level = info
+#
+# In the same way, packet hex dumps can be limited for each level.
+# "all_hex_limit" sets the hex limit for all layers unless otherwise
+# configured.
+# Format: e.g. phy_hex_limit = 32
+#
+# Logging layers: rf, phy, phy_lib, mac, rlc, pdcp, rrc, gtpu, s1ap, all
+# Logging levels: debug, info, warning, error, none
+#
+# filename: File path to use for log output. Can be set to stdout
+#           to print logs to standard output
+# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created.
+#                If set to negative, a single log file will be created.
+#####################################################################
+[log]
+all_level = warning
+all_hex_limit = 32
+filename = /tmp/enb.log
+file_max_size = -1
+
+[gui]
+enable = false
+
+#####################################################################
+# Scheduler configuration options
+#
+# max_aggr_level:    Optional maximum aggregation level index (l=log2(L) can be 0, 1, 2 or 3)
+# pdsch_mcs:         Optional fixed PDSCH MCS (ignores reported CQIs if specified)
+# pdsch_max_mcs:     Optional PDSCH MCS limit
+# pusch_mcs:         Optional fixed PUSCH MCS (ignores reported CQIs if specified)
+# pusch_max_mcs:     Optional PUSCH MCS limit
+# #nof_ctrl_symbols: Number of control symbols
+#
+#####################################################################
+[scheduler]
+#max_aggr_level   = -1
+#pdsch_mcs        = -1
+#pdsch_max_mcs    = -1
+#pusch_mcs        = -1
+pusch_max_mcs    = 16
+nof_ctrl_symbols = 3
+
+#####################################################################
+# eMBMS configuration options
+#
+# enable:               Enable MBMS transmission in the eNB
+# m1u_multiaddr:        Multicast addres the M1-U socket will register to
+# m1u_if_addr:          Address of the inteferface the M1-U interface will listen for multicast packets.
+#
+#####################################################################
+[embms]
+#enable = false
+#m1u_multiaddr = 239.255.0.1
+#m1u_if_addr = 127.0.1.201
+
+
+
+#####################################################################
+# Channel emulator options:
+# enable:            Enable/Disable internal Downlink/Uplink channel emulator
+#
+# -- Fading emulator
+# fading.enable:     Enable/disable fading simulator
+# fading.model:      Fading model + maximum doppler (E.g. none, epa5, eva70, etu300, etc)
+#
+# -- Delay Emulator     delay(t) = delay_min + (delay_max - delay_min) * (1 + sin(2pi*t/period)) / 2
+#                       Maximum speed [m/s]: (delay_max - delay_min) * pi * 300 / period
+# delay.enable:      Enable/disable delay simulator
+# delay.period_s:    Delay period in seconds.
+# delay.init_time_s: Delay initial time in seconds.
+# delay.maximum_us:  Maximum delay in microseconds
+# delay.minumum_us:  Minimum delay in microseconds
+#
+# -- Radio-Link Failure (RLF) Emulator
+# rlf.enable:        Enable/disable RLF simulator
+# rlf.t_on_ms:       Time for On state of the channel (ms)
+# rlf.t_off_ms:      Time for Off state of the channel (ms)
+#
+# -- High Speed Train Doppler model simulator
+# hst.enable:        Enable/Disable HST simulator
+# hst.period_s:      HST simulation period in seconds
+# hst.fd_hz:         Doppler frequency in Hz
+# hst.init_time_s:   Initial time in seconds
+#####################################################################
+[channel.dl]
+#enable        = false
+
+[channel.dl.fading]
+#enable        = false
+#model         = none
+
+[channel.dl.delay]
+#enable        = false
+#period_s      = 3600
+#init_time_s   = 0
+#maximum_us    = 100
+#minimum_us    = 10
+
+[channel.dl.rlf]
+#enable        = false
+#t_on_ms       = 10000
+#t_off_ms      = 2000
+
+[channel.dl.hst]
+#enable        = false
+#period_s      = 7.2
+#fd_hz         = 750.0
+#init_time_s   = 0.0
+
+[channel.ul]
+#enable        = false
+
+[channel.ul.fading]
+#enable        = false
+#model         = none
+
+[channel.ul.delay]
+#enable        = false
+#period_s      = 3600
+#init_time_s   = 0
+#maximum_us    = 100
+#minimum_us    = 10
+
+[channel.ul.rlf]
+#enable        = false
+#t_on_ms       = 10000
+#t_off_ms      = 2000
+
+[channel.ul.hst]
+#enable        = false
+#period_s      = 7.2
+#fd_hz         = -750.0
+#init_time_s   = 0.0
+
+
+#####################################################################
+# Expert configuration options
+#
+# pusch_max_its:        Maximum number of turbo decoder iterations (Default 4)
+# pusch_8bit_decoder:   Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental)
+# nof_phy_threads:      Selects the number of PHY threads (maximum 4, minimum 1, default 2)
+# metrics_period_secs:  Sets the period at which metrics are requested from the eNB.
+# metrics_csv_enable:   Write eNB metrics to CSV file.
+# metrics_csv_filename: File path to use for CSV metrics.
+# pregenerate_signals:  Pregenerate uplink signals after attach. Improves CPU performance.
+# tx_amplitude:         Transmit amplitude factor (set 0-1 to reduce PAPR)
+# link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered.
+#                       a link failure is when SNR<0 and CRC=KO
+# max_prach_offset_us:  Maximum allowed RACH offset (in us)
+# eea_pref_list:        Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).
+# eia_pref_list:        Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).
+#
+#####################################################################
+[expert]
+#pusch_max_its        = 8 # These are half iterations
+#pusch_8bit_decoder   = false
+#nof_phy_threads      = 3
+#metrics_period_secs  = 1
+#metrics_csv_enable   = false
+#metrics_csv_filename = /tmp/enb_metrics.csv
+#pregenerate_signals  = false
+#tx_amplitude         = 0.6
+#link_failure_nof_err = 50
+#rrc_inactivity_timer = 60000
+#max_prach_offset_us  = 30
+#eea_pref_list = EEA0, EEA2, EEA1
+#eia_pref_list = EIA2, EIA1, EIA0
diff --git a/src/osmo_gsm_tester/templates/srsenb_drb.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb_drb.conf.tmpl
new file mode 100644
index 0000000..32b6e93
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/srsenb_drb.conf.tmpl
@@ -0,0 +1,54 @@
+
+// All times are in ms. Use -1 for infinity, where available
+
+qci_config = (
+
+{
+  qci=7;
+  pdcp_config = {
+    discard_timer = 100;                
+    pdcp_sn_size = 12;                  
+  }
+  rlc_config = {
+    ul_um = {
+      sn_field_length = 10; 
+    };
+    dl_um = {
+      sn_field_length = 10; 
+      t_reordering    = 45;             
+    };
+  };
+  logical_channel_config = {
+    priority = 13; 
+    prioritized_bit_rate   = -1; 
+    bucket_size_duration  = 100; 
+    log_chan_group = 2; 
+  };
+},
+{
+  qci=9;
+  pdcp_config = {
+    discard_timer = -1;
+    status_report_required = true;
+  }
+  rlc_config = {
+    ul_am = {
+      t_poll_retx = 120;
+      poll_pdu = 64;
+      poll_byte = 750;
+      max_retx_thresh = 16;
+    };
+    dl_am = {
+      t_reordering = 50;
+      t_status_prohibit = 50;
+    };
+  };
+  logical_channel_config = {
+    priority = 11; 
+    prioritized_bit_rate   = -1; 
+    bucket_size_duration  = 100; 
+    log_chan_group = 3; 
+  };
+}
+
+);
diff --git a/src/osmo_gsm_tester/templates/srsenb_rr.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb_rr.conf.tmpl
new file mode 100644
index 0000000..1fff189
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/srsenb_rr.conf.tmpl
@@ -0,0 +1,90 @@
+mac_cnfg =
+{
+  phr_cnfg = 
+  {
+    dl_pathloss_change = "dB3"; // Valid: 1, 3, 6 or INFINITY
+    periodic_phr_timer = 50;
+    prohibit_phr_timer = 0;
+  };
+  ulsch_cnfg = 
+  {
+    max_harq_tx = 4;
+    periodic_bsr_timer = 20; // in ms
+    retx_bsr_timer = 320;   // in ms
+  };
+  
+  time_alignment_timer = -1; // -1 is infinity
+};
+
+phy_cnfg =
+{
+  phich_cnfg = 
+  {
+    duration  = "Normal";
+    resources = "1/6"; 
+  };
+
+  pusch_cnfg_ded =
+  {
+    beta_offset_ack_idx = 6;
+    beta_offset_ri_idx  = 6;
+    beta_offset_cqi_idx = 6;
+  };
+  
+  // PUCCH-SR resources are scheduled on time-frequeny domain first, then multiplexed in the same resource. 
+  sched_request_cnfg =
+  {
+    dsr_trans_max = 64;
+    period = 20;       // in ms
+    subframe = [1];    // vector of subframe indices allowed for SR transmissions 
+    nof_prb = 2;       // number of PRBs on each extreme used for SR (total prb is twice this number)
+  };
+  cqi_report_cnfg =
+  { 
+    mode = "periodic";
+    simultaneousAckCQI = true; 
+    period = 40; // in ms
+    subframe = [0];
+    nof_prb = 2; 
+    m_ri = 8; // RI period in CQI period
+  };
+};
+
+cell_list =
+(
+  {
+    // rf_port = 0;
+    // cell_id = 0x01;
+    // tac = 0x0001;
+    // pci = 1;
+    // root_seq_idx = 204;
+    // dl_earfcn = 3400;
+    // ul_earfcn = 474;
+    ho_active = false;
+
+    // CA cells
+    scell_list = (
+      {cell_id = 0x02; cross_carrier_scheduling = false; scheduling_cell_id = 0x02; ul_allowed = true}
+    )
+
+    // Cells available for handover
+    meas_cell_list =
+    (
+      {
+        eci = 0x19C02;
+        dl_earfcn = 2850;
+        pci = 2;
+      }
+    );
+
+    // ReportCfg (only A3 supported)
+    meas_report_desc = {
+      a3_report_type = "RSRP";
+      a3_offset = 6;
+      a3_hysteresis = 0;
+      a3_time_to_trigger = 480;
+      rsrq_config = 4;
+    };
+  }
+  // Add here more cells
+);
diff --git a/src/osmo_gsm_tester/templates/srsenb_sib.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb_sib.conf.tmpl
new file mode 100644
index 0000000..201d022
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/srsenb_sib.conf.tmpl
@@ -0,0 +1,118 @@
+sib1 =
+{
+    intra_freq_reselection = "Allowed";
+    q_rx_lev_min = -65;
+    //p_max = 3;
+    cell_barred = "NotBarred"
+    si_window_length = 20;
+    sched_info =
+    (
+        {
+            si_periodicity = 16;
+            si_mapping_info = []; // comma-separated array of SIB-indexes (from 3 to 13). 
+            // Leave empty or commented to just scheduler sib2
+        }
+    );
+    system_info_value_tag = 0;
+};
+
+sib2 = 
+{
+    rr_config_common_sib =
+    {
+        rach_cnfg = 
+        {
+            num_ra_preambles = 52;
+            preamble_init_rx_target_pwr = -104;
+            pwr_ramping_step = 6;  // in dB
+            preamble_trans_max = 10;
+            ra_resp_win_size = 10;  // in ms
+            mac_con_res_timer = 64; // in ms
+            max_harq_msg3_tx = 4;
+        };
+        bcch_cnfg = 
+        {
+            modification_period_coeff = 16; // in ms
+        };
+        pcch_cnfg = 
+        {
+            default_paging_cycle = 32; // in rf
+            nB = "1";
+        };
+        prach_cnfg =
+        {
+            root_sequence_index = 128;
+            prach_cnfg_info =
+            {
+                high_speed_flag = false;
+                prach_config_index = 3;
+                prach_freq_offset = 2;
+                zero_correlation_zone_config = 5;
+            };
+        };
+        pdsch_cnfg = 
+        {
+            /* Warning: Currently disabled and forced to p_b=1 for TM2/3/4 and p_b=0 for TM1
+             */
+            p_b = 1;
+            rs_power = 0;
+        };
+        pusch_cnfg = 
+        {
+            n_sb = 1;
+            hopping_mode = "inter-subframe";
+            pusch_hopping_offset = 2;
+            enable_64_qam = false; // 64QAM PUSCH is not currently enabled
+            ul_rs = 
+            {
+                cyclic_shift = 0; 
+                group_assignment_pusch = 0;
+                group_hopping_enabled = false; 
+                sequence_hopping_enabled = false; 
+            };
+        };
+        pucch_cnfg =
+        {
+            delta_pucch_shift = 2;
+            n_rb_cqi = 2;
+            n_cs_an = 0;
+            n1_pucch_an = 12;
+        };
+        ul_pwr_ctrl =
+        {
+            p0_nominal_pusch = -85;
+            alpha = 0.7;
+            p0_nominal_pucch = -107;
+            delta_flist_pucch =
+            {
+                format_1  = 0;
+                format_1b = 3; 
+                format_2  = 1;
+                format_2a = 2;
+                format_2b = 2;
+            };
+            delta_preamble_msg3 = 6;
+        };
+        ul_cp_length = "len1";
+    };
+
+    ue_timers_and_constants =
+    {
+        t300 = 2000; // in ms
+        t301 = 100;  // in ms
+        t310 = 1000; // in ms
+        n310 = 1;
+        t311 = 1000; // in ms
+        n311 = 1;
+    };
+
+    freqInfo = 
+    {
+        ul_carrier_freq_present = true; 
+        ul_bw_present = true; 
+        additional_spectrum_emission = 1; 
+    };
+
+    time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc.
+};
+
diff --git a/src/osmo_gsm_tester/templates/srsepc.conf.tmpl b/src/osmo_gsm_tester/templates/srsepc.conf.tmpl
new file mode 100644
index 0000000..8acd22f
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/srsepc.conf.tmpl
@@ -0,0 +1,106 @@
+#####################################################################
+#                   srsEPC configuration file
+#####################################################################
+
+#####################################################################
+# MME configuration
+#
+# mme_code:         8-bit MME code identifies the MME within a group.
+# mme_group:        16-bit MME group identifier.
+# tac:              16-bit Tracking Area Code.
+# mcc:              Mobile Country Code
+# mnc:              Mobile Network Code
+# apn:		          Set Access Point Name (APN)
+# mme_bind_addr:    IP bind addr to listen for eNB S1-MME connnections
+# dns_addr:         DNS server address for the UEs
+# encryption_algo:  Preferred encryption algorithm for NAS layer
+#                   (default: EEA0, support: EEA1, EEA2)
+# integrity_algo:   Preferred integrity protection algorithm for NAS
+#                   (default: EIA1, support: EIA1, EIA2 (EIA0 not support)
+# paging_timer:     Value of paging timer in seconds (T3413)
+#
+#####################################################################
+[mme]
+mme_code = 0x1a
+mme_group = 0x0001
+tac = 0x0007
+mcc = ${epc.mcc}
+mnc = ${epc.mnc}
+mme_bind_addr = ${epc.run_addr}
+apn = srsapn
+dns_addr = 8.8.8.8
+encryption_algo = EEA0
+integrity_algo = EIA1
+paging_timer = 2
+
+#####################################################################
+# HSS configuration
+#
+# db_file:         Location of .csv file that stores UEs information.
+#
+#####################################################################
+[hss]
+db_file = user_db.csv
+
+#####################################################################
+# SP-GW configuration
+#
+# gtpu_bind_addr:   GTP-U bind address.
+# sgi_if_addr:      SGi TUN interface IP address.
+# sgi_if_name:      SGi TUN interface name.
+# max_paging_queue: Maximum packets in paging queue (per UE).
+#
+#####################################################################
+
+[spgw]
+gtpu_bind_addr   = ${epc.run_addr}
+sgi_if_addr      = 172.16.0.1
+sgi_if_name      = srs_spgw_sgi
+max_paging_queue = 100
+
+####################################################################
+# PCAP configuration
+#
+# Packets are captured to file in the compact format decoded by
+# the Wireshark s1ap dissector and with DLT 150.
+# To use the dissector, edit the preferences for DLT_USER to
+# add an entry with DLT=150, Payload Protocol=s1ap.
+#
+# enable:   Enable or disable the PCAP.
+# filename: File name where to save the PCAP.
+#
+####################################################################
+[pcap]
+enable   = false
+filename = /tmp/epc.pcap
+
+####################################################################
+# Log configuration
+#
+# Log levels can be set for individual layers. "all_level" sets log
+# level for all layers unless otherwise configured.
+# Format: e.g. s1ap_level = info
+#
+# In the same way, packet hex dumps can be limited for each level.
+# "all_hex_limit" sets the hex limit for all layers unless otherwise
+# configured.
+# Format: e.g. s1ap_hex_limit = 32
+#
+# Logging layers: nas, s1ap, mme_gtpc, spgw_gtpc, gtpu, spgw, hss, all
+# Logging levels: debug, info, warning, error, none
+#
+# filename: File path to use for log output. Can be set to stdout
+#           to print logs to standard output
+#####################################################################
+[log]
+all_level = info
+all_hex_limit = 32
+filename = /tmp/epc.log
+
+#nas_level = debug
+#s1ap_level = debug
+#mme_gtpc_level = debug
+#spgw_gtpc_level = debug
+#gtpu_level = debug
+#spgw_level = debug
+#hss_level = debug
diff --git a/src/osmo_gsm_tester/templates/srsepc_user_db.csv.tmpl b/src/osmo_gsm_tester/templates/srsepc_user_db.csv.tmpl
new file mode 100644
index 0000000..32df58d
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/srsepc_user_db.csv.tmpl
@@ -0,0 +1,24 @@
+#
+# .csv to store UE's information in HSS
+# Kept in the following format: "Name,Auth,IMSI,Key,OP_Type,OP,AMF,SQN,QCI,IP_alloc"
+#
+# Name:     Human readable name to help distinguish UE's. Ignored by the HSS
+# IMSI:     UE's IMSI value
+# Auth:     Authentication algorithm used by the UE. Valid algorithms are XOR
+#           (xor) and MILENAGE (mil)
+# Key:      UE's key, where other keys are derived from. Stored in hexadecimal
+# OP_Type:  Operator's code type, either OP or OPc
+# OP/OPc:   Operator Code/Cyphered Operator Code, stored in hexadecimal
+# AMF:      Authentication management field, stored in hexadecimal
+# SQN:      UE's Sequence number for freshness of the authentication
+# QCI:      QoS Class Identifier for the UE's default bearer.
+# IP_alloc: IP allocation stratagy for the SPGW.
+#           With 'dynamic' the SPGW will automatically allocate IPs
+#           With a valid IPv4 (e.g. '172.16.0.2') the UE will have a statically assigned IP.
+#
+# Note: Lines starting by '#' are ignored and will be overwritten
+#ue2,mil,001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234,7,dynamic
+#ue1,xor,001010123456789,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,9001,000000001255,7,dynamic
+%for sub in epc.hss.subscribers:
+ogt${sub.id},${sub.auth_algo},${sub.imsi},${sub.ki},opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234,7,dynamic
+%endfor
diff --git a/src/osmo_gsm_tester/templates/srsue.conf.tmpl b/src/osmo_gsm_tester/templates/srsue.conf.tmpl
new file mode 100644
index 0000000..5e5676d
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/srsue.conf.tmpl
@@ -0,0 +1,347 @@
+#####################################################################
+#                   srsUE configuration file
+#####################################################################
+
+#####################################################################
+# RF configuration
+#
+# dl_earfcn: Downlink EARFCN code.
+# freq_offset: Uplink and Downlink optional frequency offset (in Hz)
+# tx_gain: Transmit gain (dB).
+# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled
+#
+# Optional parameters:
+# dl_freq:            Override DL frequency corresponding to dl_earfcn
+# ul_freq:            Override UL frequency corresponding to dl_earfcn
+# nof_radios:         Number of available RF devices
+# nof_rf_channels:    Number of RF channels per radio
+# nof_rx_ant:         Number of RX antennas per channel
+# device_name:        Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF"
+# device_args:        Arguments for the device driver. Options are "auto" or any string.
+#                     Default for UHD: "recv_frame_size=9232,send_frame_size=9232"
+#                     Default for bladeRF: ""
+# device_args_2:      Arguments for the RF device driver 2.
+# device_args_3:      Arguments for the RF device driver 3.
+# time_adv_nsamples:  Transmission time advance (in number of samples) to compensate for RF delay
+#                     from antenna to timestamp insertion.
+#                     Default "auto". B210 USRP: 100 samples, bladeRF: 27.
+# burst_preamble_us:  Preamble length to transmit before start of burst.
+#                     Default "auto". B210 USRP: 400 us, bladeRF: 0 us.
+# continuous_tx:      Transmit samples continuously to the radio or on bursts (auto/yes/no).
+#                     Default is auto (yes for UHD, no for rest)
+#####################################################################
+[rf]
+dl_earfcn = 3400
+freq_offset = 0
+tx_gain = 80
+#rx_gain = 40
+
+#nof_radios = 1
+#nof_rx_ant = 1
+
+# For best performance in 2x2 MIMO and >= 15 MHz use the following device_args settings:
+#     USRP B210: num_recv_frames=64,num_send_frames=64
+
+# For best performance when BW<5 MHz (25 PRB), use the following device_args settings:
+#     USRP B210: send_frame_size=512,recv_frame_size=512
+
+#device_args = auto
+#time_adv_nsamples = auto
+#burst_preamble_us = auto
+#continuous_tx     = auto
+
+
+#####################################################################
+# Packet capture configuration
+#
+# Packet capture is supported at both MAC and NAS layers.
+# MAC-layer packets are captured to file in the compact format
+# decoded by the Wireshark mac-lte-framed dissector.
+# To use this dissector, edit the preferences for DLT_USER to
+# add an entry with DLT=147, Payload Protocol=mac-lte-framed.
+# For more information see: https://wiki.wireshark.org/MAC-LTE
+# NAS-layer packets are dissected with DLT=148, and
+# Payload Protocol = nas-eps.
+#
+# enable:       Enable MAC layer packet captures (true/false)
+# filename:     File path to use for MAC packet captures
+# nas_enable:   Enable NAS layer packet captures (true/false)
+# nas_filename: File path to use for NAS packet captures
+#####################################################################
+[pcap]
+enable = false
+filename = /tmp/ue.pcap
+nas_enable = false
+nas_filename = /tmp/nas.pcap
+
+#####################################################################
+# Log configuration
+#
+# Log levels can be set for individual layers. "all_level" sets log
+# level for all layers unless otherwise configured.
+# Format: e.g. phy_level = info
+#
+# In the same way, packet hex dumps can be limited for each level.
+# "all_hex_limit" sets the hex limit for all layers unless otherwise
+# configured.
+# Format: e.g. phy_hex_limit = 32
+#
+# Logging layers: rf, phy, mac, rlc, pdcp, rrc, nas, gw, usim, all
+# Logging levels: debug, info, warning, error, none
+#
+# filename: File path to use for log output. Can be set to stdout
+#           to print logs to standard output
+# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created.
+#                If set to negative, a single log file will be created.
+#####################################################################
+[log]
+all_level = warning
+phy_lib_level = none
+all_hex_limit = 32
+filename = /tmp/ue.log
+file_max_size = -1
+
+#####################################################################
+# USIM configuration
+#
+# mode:   USIM mode (soft/pcsc)
+# algo:   Authentication algorithm (xor/milenage)
+# op/opc: 128-bit Operator Variant Algorithm Configuration Field (hex)
+#         - Specify either op or opc (only used in milenage)
+# k:      128-bit subscriber key (hex)
+# imsi:   15 digit International Mobile Subscriber Identity
+# imei:   15 digit International Mobile Station Equipment Identity
+# pin:    PIN in case real SIM card is used
+# reader: Specify card reader by it's name as listed by 'pcsc_scan'. If empty, try all available readers.
+#####################################################################
+[usim]
+mode = soft
+algo = ${ue.auth_algo}
+#opc  = 63BFA50EE6523365FF14C1F45F88737D
+k    = ${ue.ki}
+imsi = ${ue.imsi}
+imei = 353490069873319
+#reader =
+#pin  = 1234
+
+#####################################################################
+# RRC configuration
+#
+# ue_category:       Sets UE category (range 1-5). Default: 4
+# release:           UE Release (8 to 10)
+# feature_group:     Hex value of the featureGroupIndicators field in the
+#                    UECapabilityInformation message. Default 0xe6041000
+# mbms_service_id:   MBMS service id for autostarting MBMS reception
+#                    (default -1 means disabled)
+# mbms_service_port: Port of the MBMS service
+#####################################################################
+[rrc]
+#ue_category       = 4
+#release           = 8
+#feature_group     = 0xe6041000
+#mbms_service_id   = -1
+#mbms_service_port = 4321
+
+#####################################################################
+# NAS configuration
+#
+# apn:               Set Access Point Name (APN)
+# apn_protocol:      Set APN protocol (IPv4, IPv6 or IPv4v6.)
+# user:              Username for CHAP authentication
+# pass:              Password for CHAP authentication
+# force_imsi_attach: Whether to always perform an IMSI attach
+# eia:               List of integrity algorithms included in UE capabilities
+#                      Supported: 1 - Snow3G, 2 - AES
+# eea:               List of ciphering algorithms included in UE capabilities
+#                      Supported: 0 - NULL, 1 - Snow3G, 2 - AES
+#####################################################################
+[nas]
+#apn = internetinternet
+#apn_protocol = ipv4
+#user = srsuser
+#pass = srspass
+#force_imsi_attach = false
+#eia = 1,2
+#eea = 0,1,2
+
+#####################################################################
+# GW configuration
+#
+# netns:                Network namespace to create TUN device. Default: empty
+# ip_devname:           Name of the tun_srsue device. Default: tun_srsue
+# ip_netmask:           Netmask of the tun_srsue device. Default: 255.255.255.0
+#####################################################################
+[gw]
+#netns =
+#ip_devname = tun_srsue
+#ip_netmask = 255.255.255.0
+
+#####################################################################
+# GUI configuration
+#
+# Simple GUI displaying PDSCH constellation and channel freq response.
+# (Requires building with srsGUI)
+# enable:               Enable the graphical interface (true/false)
+#####################################################################
+[gui]
+enable = false
+
+#####################################################################
+# Channel emulator options:
+# enable:            Enable/Disable internal Downlink/Uplink channel emulator
+#
+# -- Fading emulator
+# fading.enable:     Enable/disable fading simulator
+# fading.model:      Fading model + maximum doppler (E.g. none, epa5, eva70, etu300, etc)
+#
+# -- Delay Emulator     delay(t) = delay_min + (delay_max - delay_min) * (1 + sin(2pi*t/period)) / 2
+#                       Maximum speed [m/s]: (delay_max - delay_min) * pi * 300 / period
+# delay.enable:      Enable/disable delay simulator
+# delay.period_s:    Delay period in seconds.
+# delay.init_time_s: Delay initial time in seconds.
+# delay.maximum_us:  Maximum delay in microseconds
+# delay.minumum_us:  Minimum delay in microseconds
+#
+# -- Radio-Link Failure (RLF) Emulator
+# rlf.enable:        Enable/disable RLF simulator
+# rlf.t_on_ms:       Time for On state of the channel (ms)
+# rlf.t_off_ms:      Time for Off state of the channel (ms)
+#
+# -- High Speed Train Doppler model simulator
+# hst.enable:        Enable/Disable HST simulator
+# hst.period_s:      HST simulation period in seconds
+# hst.fd_hz:         Doppler frequency in Hz
+# hst.init_time_s:   Initial time in seconds
+#####################################################################
+[channel.dl]
+#enable        = false
+
+[channel.dl.fading]
+#enable        = false
+#model         = none
+
+[channel.dl.delay]
+#enable        = false
+#period_s      = 3600
+#init_time_s   = 0
+#maximum_us    = 100
+#minimum_us    = 10
+
+[channel.dl.rlf]
+#enable        = false
+#t_on_ms       = 10000
+#t_off_ms      = 2000
+
+[channel.dl.hst]
+#enable        = false
+#period_s      = 7.2
+#fd_hz         = 750.0
+#init_time_s   = 0.0
+
+[channel.ul]
+#enable        = false
+
+[channel.ul.fading]
+#enable        = false
+#model         = none
+
+[channel.ul.delay]
+#enable        = false
+#period_s      = 3600
+#init_time_s   = 0
+#maximum_us    = 100
+#minimum_us    = 10
+
+[channel.ul.rlf]
+#enable        = false
+#t_on_ms       = 10000
+#t_off_ms      = 2000
+
+[channel.ul.hst]
+#enable        = false
+#period_s      = 7.2
+#fd_hz         = -750.0
+#init_time_s   = 0.0
+
+#####################################################################
+# PHY configuration options
+#
+# rx_gain_offset:       RX Gain offset to add to rx_gain to calibrate RSRP readings
+# prach_gain:           PRACH gain (dB). If defined, forces a gain for the tranmsission of PRACH only.,
+#                       Default is to use tx_gain in [rf] section.
+# cqi_max:              Upper bound on the maximum CQI to be reported. Default 15.
+# cqi_fixed:            Fixes the reported CQI to a constant value. Default disabled.
+# snr_ema_coeff:        Sets the SNR exponential moving average coefficient (Default 0.1)
+# snr_estim_alg:        Sets the noise estimation algorithm. (Default refs)
+#                          Options: pss:   use difference between received and known pss signal,
+#                                   refs:  use difference between noise references and noiseless (after filtering)
+#                                   empty: use empty subcarriers in the boarder of pss/sss signal
+# pdsch_max_its:        Maximum number of turbo decoder iterations (Default 4)
+# nof_phy_threads:      Selects the number of PHY threads (maximum 4, minimum 1, default 2)
+# equalizer_mode:       Selects equalizer mode. Valid modes are: "mmse", "zf" or any
+#                       non-negative real number to indicate a regularized zf coefficient.
+#                       Default is MMSE.
+# sfo_ema:              EMA coefficient to average sample offsets used to compute SFO
+# sfo_correct_period:   Period in ms to correct sample time to adjust for SFO
+# sss_algorithm:        Selects the SSS estimation algorithm. Can choose between
+#                       {full, partial, diff}.
+# estimator_fil_auto:   The channel estimator smooths the channel estimate with an adaptative filter.
+# estimator_fil_stddev: Sets the channel estimator smooth gaussian filter standard deviation.
+# estimator_fil_order:  Sets the channel estimator smooth gaussian filter order (even values perform better).
+#                       The taps are [w, 1-2w, w]
+#
+# snr_to_cqi_offset:    Sets an offset in the SNR to CQI table. This is used to adjust the reported CQI.
+#
+# pregenerate_signals:  Pregenerate uplink signals after attach. Improves CPU performance.
+#
+# interpolate_subframe_enabled: Interpolates in the time domain the channel estimates within 1 subframe. Default is to average.
+#
+# sic_pss_enabled:      Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells.
+#                       Must be disabled if cells have identical channel and timing, for instance if generated from
+#                       the same source.
+#
+# pdsch_csi_enabled:     Stores the Channel State Information and uses it for weightening the softbits. It is only
+#                        used in TM1. It is True by default.
+#
+# pdsch_8bit_decoder:    Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental)
+# force_ul_amplitude:    Forces the peak amplitude in the PUCCH, PUSCH and SRS (set 0.0 to 1.0, set to 0 or negative for disabling)
+#
+#####################################################################
+[phy]
+#rx_gain_offset      = 62
+#prach_gain          = 30
+#cqi_max             = 15
+#cqi_fixed           = 10
+#snr_ema_coeff       = 0.1
+#snr_estim_alg       = refs
+#pdsch_max_its       = 8    # These are half iterations
+#nof_phy_threads     = 3
+#equalizer_mode      = mmse
+#sfo_ema             = 0.1
+#sfo_correct_period  = 10
+#sss_algorithm       = full
+#estimator_fil_auto  = false
+#estimator_fil_stddev  = 1.0
+#estimator_fil_order  = 4
+#snr_to_cqi_offset   = 0.0
+#interpolate_subframe_enabled = false
+#sic_pss_enabled     = true
+#pregenerate_signals = false
+#pdsch_csi_enabled  = true
+#pdsch_8bit_decoder = false
+#force_ul_amplitude = 0
+
+#####################################################################
+# General configuration options
+#
+# metrics_csv_enable:   Write UE metrics to CSV file.
+#
+# metrics_period_secs:  Sets the period at which metrics are requested from the UE.
+#
+# metrics_csv_filename: File path to use for CSV metrics.
+#
+#####################################################################
+[general]
+#metrics_csv_enable  = false
+#metrics_period_secs = 1
+#metrics_csv_filename = /tmp/ue_metrics.csv
diff --git a/suites/4g/iperf3.py b/suites/4g/iperf3.py
new file mode 100755
index 0000000..5ccc922
--- /dev/null
+++ b/suites/4g/iperf3.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+from osmo_gsm_tester.testenv import *
+
+def print_results(cli_res, srv_res):
+    cli_sent = cli_res['end']['sum_sent']
+    cli_recv = cli_res['end']['sum_received']
+    print("RESULT client:")
+    print("\tSEND: %d KB, %d kbps, %d seconds (%d retrans)" % (cli_sent['bytes']/1000, cli_sent['bits_per_second']/1000,  cli_sent['seconds'], cli_sent['retransmits']))
+    print("\tRECV: %d KB, %d kbps, %d seconds" % (cli_recv['bytes']/1000, cli_recv['bits_per_second']/1000, cli_recv['seconds']))
+    print("RESULT server:")
+    print("\tSEND: %d KB, %d kbps, %d seconds" % (cli_sent['bytes']/1000, cli_sent['bits_per_second']/1000, cli_sent['seconds']))
+    print("\tRECV: %d KB, %d kbps, %d seconds" % (cli_recv['bytes']/1000, cli_recv['bits_per_second']/1000, cli_recv['seconds']))
+
+
+epc = suite.epc()
+enb = suite.enb()
+ue = suite.modem()
+iperf3srv = suite.iperf3srv({'addr': epc.tun_addr()})
+iperf3srv.set_run_node(epc.run_node())
+iperf3cli = iperf3srv.create_client()
+iperf3cli.set_run_node(ue.run_node())
+
+epc.subscriber_add(ue)
+epc.start()
+enb.ue_add(ue)
+enb.start(epc)
+
+print('waiting for ENB to connect to EPC...')
+wait(epc.enb_is_connected, enb)
+print('ENB is connected to EPC')
+
+ue.connect(enb)
+
+iperf3srv.start()
+proc = iperf3cli.prepare_test_proc(ue.netns())
+
+print('waiting for UE to attach...')
+wait(ue.is_connected, None)
+print('UE is attached')
+
+print("Running iperf3 client to %s through %s" % (str(iperf3cli), ue.netns()))
+proc.launch_sync()
+iperf3srv.stop()
+print_results(iperf3cli.get_results(), iperf3srv.get_results())
diff --git a/suites/4g/mo_mt_sms.py b/suites/4g/mo_mt_sms.py
deleted file mode 100755
index 8aa4357..0000000
--- a/suites/4g/mo_mt_sms.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env python3
-from osmo_gsm_tester.testenv import *
-
-#epc = suite.epc()
-#enb = suite.enb()
-ue = suite.modem()
-
-#enb.start()
-#epc.enb_add(enb)
-#epc.start()
-
-#wait(epc.enb_is_connected, enb)
-
-#hss/epc.subscriber_add(ue)
-
-#ue.connect(epc.mcc_mnc())
-ue.connect()
-
-
-print('waiting for modem to attach...')
-#wait(ue.is_connected, msc.mcc_mnc())
-sleep(10)
diff --git a/suites/4g/ping.py b/suites/4g/ping.py
new file mode 100755
index 0000000..b44ff84
--- /dev/null
+++ b/suites/4g/ping.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+from osmo_gsm_tester.testenv import *
+
+epc = suite.epc()
+enb = suite.enb()
+ue = suite.modem()
+
+epc.subscriber_add(ue)
+epc.start()
+enb.ue_add(ue)
+enb.start(epc)
+
+print('waiting for ENB to connect to EPC...')
+wait(epc.enb_is_connected, enb)
+print('ENB is connected to EPC')
+
+ue.connect(enb)
+print('waiting for UE to attach...')
+wait(ue.is_connected, None)
+print('UE is attached')
+
+ue.run_netns_wait('ping', ('ping', '-c', '10', epc.tun_addr()))
diff --git a/suites/4g/suite.conf b/suites/4g/suite.conf
index c55b610..352293a 100644
--- a/suites/4g/suite.conf
+++ b/suites/4g/suite.conf
@@ -1,6 +1,9 @@
 resources:
-  ip_address:
+  run_node: # for EPC
   - times: 1
+  enb:
+  - times: 1
+    type: srsenb
   modem:
   - times: 1
     type: srsue

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-gsm-tester/+/17139
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-gsm-tester
Gerrit-Branch: master
Gerrit-Change-Id: I290c0d79258a9f94f00c7ff2e1c6c5579c0e32f4
Gerrit-Change-Number: 17139
Gerrit-PatchSet: 3
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
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/20200214/49a59e60/attachment.htm>


More information about the gerrit-log mailing list