amarisoft_enb: add NR support
this patch adds the ability to configure NR cells with
Amarisoft eNB. It adds the new DRB-NR template and updates
the normal enb.cfg template to allow using it as LTE only
or with NR cells (5G NSA).
Change-Id: Ia27bbc6db5920ce14bacabe8043601aa2adaa5fe
diff --git a/src/osmo_gsm_tester/obj/enb.py b/src/osmo_gsm_tester/obj/enb.py
index 252fa14..e976753 100644
--- a/src/osmo_gsm_tester/obj/enb.py
+++ b/src/osmo_gsm_tester/obj/enb.py
@@ -68,12 +68,18 @@
'cell_list[].ncell_list[].pci': schema.UINT,
'cell_list[].ncell_list[].dl_earfcn': schema.UINT,
'cell_list[].scell_list[]': schema.UINT,
+ 'cell_list[].nr_scell_list[]': schema.UINT,
'cell_list[].dl_earfcn': schema.UINT,
'cell_list[].root_seq_idx': schema.UINT,
'cell_list[].tac': schema.UINT,
'cell_list[].dl_rfemu.type': schema.STR,
'cell_list[].dl_rfemu.addr': schema.IPV4,
'cell_list[].dl_rfemu.ports[]': schema.UINT,
+ 'num_nr_cells': schema.UINT,
+ 'nr_cell_list[].rf_port': schema.UINT,
+ 'nr_cell_list[].cell_id': schema.UINT,
+ 'nr_cell_list[].band': schema.UINT,
+ 'nr_cell_list[].dl_nr_arfcn': schema.UINT,
}
for key, val in run_node.RunNode.schema().items():
resource_schema['run_node.%s' % key] = val
@@ -98,9 +104,11 @@
self.set_name('%s_%s' % (name, self._run_node.run_addr()))
self._txmode = 0
self._id = None
+ self._ran_config = "lte" # Used to determine whether we are in NSA
self._duplex = None
self._num_prb = 0
self._num_cells = None
+ self._num_nr_cells = None
self._epc = None
self.gen_conf = None
self.gr_broker = GrBroker.ref()
@@ -126,10 +134,11 @@
def calc_required_zmq_ports(self, cfg_values):
cell_list = cfg_values['enb']['cell_list']
- return len(cell_list) * self.num_ports() # *2 if MIMO
+ nr_cell_list = cfg_values['enb']['nr_cell_list']
+ return len(cell_list) * self.num_ports() + len(nr_cell_list) # *2 if LTE MIMO
def calc_required_zmq_ports_joined_earfcn(self, cfg_values):
- #gr_broker will join the earfcns, so we need to count uniqe earfcns:
+ #gr_broker will join the earfcns, so we need to count unique earfcns (only implemented for LTE):
cell_list = cfg_values['enb']['cell_list']
earfcn_li = []
[earfcn_li.append(int(cell['dl_earfcn'])) for cell in cell_list if int(cell['dl_earfcn']) not in earfcn_li]
@@ -142,6 +151,10 @@
for cell in cell_list:
cell[port_name] = base_port + port_offset
port_offset += self.num_ports()
+ nr_cell_list = cfg_values['enb']['nr_cell_list']
+ for nr_cell in nr_cell_list:
+ nr_cell[port_name] = base_port + port_offset
+ port_offset += 1
# TODO: do we need to assign cell_list back?
def assign_enb_zmq_ports_joined_earfcn(self, cfg_values, port_name, base_port):
@@ -176,7 +189,9 @@
config.overlay(values, dict(enb={ 'mme_addr': self._epc.addr() }))
config.overlay(values, dict(enb={ 'gtp_bind_addr': self._gtp_bind_addr }))
self._num_cells = int(values['enb'].get('num_cells', None))
- assert self._num_cells
+ self._num_nr_cells = int(values['enb'].get('num_nr_cells', None))
+ assert self._num_cells is not None
+ assert self._num_nr_cells is not None
# adjust cell_list to num_cells length:
len_cell_list = len(values['enb']['cell_list'])
@@ -231,6 +246,9 @@
def num_cells(self):
return self._num_cells
+ def num_nr_cells(self):
+ return self._num_nr_cells
+
########################
# PUBLIC - INTERNAL API
########################
@@ -280,6 +298,13 @@
rf_dev_args += ',rx_port%u=tcp://%s:%u' %(idx + 1, ul_rem_addr, cell['zmq_enb_peer_port'] + 1)
idx += self.num_ports()
+ # Only single antenna supported for NR cells
+ nr_cell_list = cfg_values['enb']['nr_cell_list']
+ for nr_cell in nr_cell_list:
+ rf_dev_args += ',tx_port%u=tcp://%s:%u' % (idx, self.addr(), nr_cell['zmq_enb_bind_port'] + 0)
+ rf_dev_args += ',rx_port%u=tcp://%s:%u' % (idx, ul_rem_addr, nr_cell['zmq_enb_peer_port'] + 0)
+ idx += 1
+
rf_dev_args += ',id=enb,base_srate=' + str(base_srate)
return rf_dev_args
@@ -300,6 +325,14 @@
if self.num_ports() > 1:
rf_dev_args += ',rx_port%u=tcp://%s:%u' %(idx + 1, self.addr(), cell['zmq_ue_peer_port'] + 1)
idx += self.num_ports()
+
+ # NR cells again only with single antenna support
+ nr_cell_list = self.gen_conf['enb']['nr_cell_list']
+ for nr_cell in nr_cell_list:
+ rf_dev_args += ',tx_port%u=tcp://%s:%u' %(idx, ue.addr(), nr_cell['zmq_ue_bind_port'] + 0)
+ rf_dev_args += ',rx_port%u=tcp://%s:%u' %(idx, self.addr(), nr_cell['zmq_ue_peer_port'] + 0)
+ idx += 1
+
# remove trailing comma:
if rf_dev_args[0] == ',':
return rf_dev_args[1:]
diff --git a/src/osmo_gsm_tester/obj/enb_amarisoft.py b/src/osmo_gsm_tester/obj/enb_amarisoft.py
index 405ed68..34ab5c1 100644
--- a/src/osmo_gsm_tester/obj/enb_amarisoft.py
+++ b/src/osmo_gsm_tester/obj/enb_amarisoft.py
@@ -22,6 +22,7 @@
from ..core import log, util, config, template, process, remote
from ..core import schema
+from ..core.event_loop import MainLoop
from . import enb
from . import rfemu
@@ -33,12 +34,16 @@
config_schema = {
'log_options': schema.STR,
+ 'nr_bandwidth': schema.INT,
}
schema.register_config_schema('amarisoftenb', config_schema)
def rf_type_valid(rf_type_str):
return rf_type_str in ('uhd', 'zmq', 'sdr')
+def ran_type_valid(ran_type_str):
+ return ran_type_str in ('lte', '5g_nsa')
+
class AmarisoftENB(enb.eNodeB):
REMOTE_DIR = '/osmo-gsm-tester-amarisoftenb'
@@ -48,6 +53,7 @@
CFGFILE_SIB23 = 'amarisoft_sib23.asn'
CFGFILE_RF = 'amarisoft_rf_driver.cfg'
CFGFILE_DRB = 'amarisoft_drb.cfg'
+ CFGFILE_DRB_NR = 'amarisoft_drb_nr.cfg'
LOGFILE = 'lteenb.log'
PHY_SIGNAL_FILE = 'lteenb.log.bin'
@@ -63,6 +69,7 @@
self.config_sib23_file = None
self.config_rf_file = None
self.config_drb_file = None
+ self.config_drb_nr_file = None
self.log_file = None
self.process = None
self.rem_host = None
@@ -72,8 +79,11 @@
self.remote_config_sib23_file = None
self.remote_config_rf_file = None
self.remote_config_drb_file = None
+ self.remote_config_drb_nr_file = None
self.remote_log_file = None
self.enable_measurements = False
+ self.nr_bandwidth = None
+ self.ran_type = None
self.testenv = testenv
if not rf_type_valid(conf.get('rf_dev_type', None)):
raise log.Error('Invalid rf_dev_type=%s' % conf.get('rf_dev_type', None))
@@ -131,8 +141,8 @@
self.process.launch()
def stop(self):
- # Not implemented
- pass
+ # Allow for some time to flush logs
+ MainLoop.sleep(5)
def gen_conf_file(self, path, filename, values):
self.dbg('AmarisoftENB ' + filename + ':\n' + pprint.pformat(values))
@@ -151,6 +161,7 @@
self.config_sib23_file = self.run_dir.child(AmarisoftENB.CFGFILE_SIB23)
self.config_rf_file = self.run_dir.child(AmarisoftENB.CFGFILE_RF)
self.config_drb_file = self.run_dir.child(AmarisoftENB.CFGFILE_DRB)
+ self.config_drb_nr_file = self.run_dir.child(AmarisoftENB.CFGFILE_DRB_NR)
self.log_file = self.run_dir.child(AmarisoftENB.LOGFILE)
self.phy_signal_file = self.run_dir.child(AmarisoftENB.PHY_SIGNAL_FILE)
@@ -165,6 +176,7 @@
self.remote_config_sib23_file = remote_run_dir.child(AmarisoftENB.CFGFILE_SIB23)
self.remote_config_rf_file = remote_run_dir.child(AmarisoftENB.CFGFILE_RF)
self.remote_config_drb_file = remote_run_dir.child(AmarisoftENB.CFGFILE_DRB)
+ self.remote_config_drb_nr_file = remote_run_dir.child(AmarisoftENB.CFGFILE_DRB_NR)
self.remote_log_file = remote_run_dir.child(AmarisoftENB.LOGFILE)
self.remote_phy_signal_file = remote_run_dir.child(AmarisoftENB.PHY_SIGNAL_FILE)
@@ -176,6 +188,17 @@
config.overlay(values, dict(enb={'enable_dl_awgn': util.str2bool(values['enb'].get('enable_dl_awgn', 'false'))}))
+ self.nr_bandwidth = int(values['enb'].get('nr_bandwidth', 10))
+ config.overlay(values, dict(enb={'nr_bandwidth': self.nr_bandwidth}))
+
+ if (self._num_cells > 0):
+ if (self._num_nr_cells <= 0):
+ self.ran_type = "lte"
+ else:
+ self.ran_type = "nsa"
+ else:
+ raise log.Error('5G SA not supported yet')
+
# Remove EEA0 from cipher list, if specified, as it's always assumed as default
cipher_list = values['enb'].get('cipher_list', None)
if "eea0" in cipher_list: cipher_list.remove("eea0")
@@ -237,6 +260,7 @@
self.gen_conf_file(self.config_sib23_file, AmarisoftENB.CFGFILE_SIB23, values)
self.gen_conf_file(self.config_rf_file, AmarisoftENB.CFGFILE_RF, values)
self.gen_conf_file(self.config_drb_file, AmarisoftENB.CFGFILE_DRB, values)
+ self.gen_conf_file(self.config_drb_nr_file, AmarisoftENB.CFGFILE_DRB_NR, values)
if not self._run_node.is_local():
self.rem_host.recreate_remote_dir(self.remote_inst)
@@ -247,6 +271,7 @@
self.rem_host.scp('scp-cfg-sib23-to-remote', self.config_sib23_file, self.remote_config_sib23_file)
self.rem_host.scp('scp-cfg-rr-to-remote', self.config_rf_file, self.remote_config_rf_file)
self.rem_host.scp('scp-cfg-drb-to-remote', self.config_drb_file, self.remote_config_drb_file)
+ self.rem_host.scp('scp-cfg-drb-nr-to-remote', self.config_drb_nr_file, self.remote_config_drb_nr_file)
def ue_add(self, ue):
if self.ue is not None:
@@ -279,11 +304,17 @@
rfemu_obj = rfemu.get_instance_by_type(rfemu_cfg['type'], rfemu_cfg)
return rfemu_obj
+ def get_nr_bandwidth(self):
+ return self.nr_bandwidth
+
def ue_max_rate(self, downlink=True, num_carriers=1):
- if self._duplex == 'fdd':
- return self.ue_max_rate_fdd(downlink, num_carriers)
+ if self.ran_type == 'lte':
+ if self._duplex == 'fdd':
+ return self.ue_max_rate_fdd(downlink, num_carriers)
+ else:
+ return self.ue_max_rate_tdd(downlink, num_carriers)
else:
- return self.ue_max_rate_tdd(downlink, num_carriers)
+ return self.ue_max_rate_nsa_tdd(downlink)
def ue_max_rate_fdd(self, downlink, num_carriers):
# The max rate for a single UE per PRB configuration in TM1 with MCS 28 QAM64
@@ -323,7 +354,7 @@
return max_rate
def ue_max_rate_tdd(self, downlink, num_carriers):
- # Max rate calculation for TDD depends on the acutal TDD configuration
+ # Max rate calculation for TDD depends on the actual TDD configuration
# See: https://www.sharetechnote.com/html/Handbook_LTE_ThroughputCalculationExample_TDD.html
# and https://i0.wp.com/www.techtrained.com/wp-content/uploads/2017/09/Blog_Post_1_TDD_Max_Throughput_Theoretical.jpg
max_phy_rate_tdd_uldl_config0_sp0 = { 6 : 1.5e6,
@@ -333,8 +364,21 @@
75 : 18.4e6,
100 : 54.5e6 }
if downlink:
- max_rate = max_phy_rate_tdd_uldl_config0_sp0[self.num_prb()]
+ return max_phy_rate_tdd_uldl_config0_sp0[self.num_prb()]
else:
return 1e6 # dummy value, we need to replace that later
+ def ue_max_rate_nsa_tdd(self, downlink):
+ # Max rate calculation based on https://5g-tools.com/5g-nr-throughput-calculator/
+ # Only FR1 15kHz SCS, QAM64, 6 DL slots, 3 UL slots
+ max_phy_rate_nsa_dl_fr1_15khz = { 10: 18.4e6,
+ 20: 38.0e6 }
+ max_phy_rate_nsa_ul_fr1_15khz = { 10: 10.7e6,
+ 20: 23.0e6 }
+
+ if downlink:
+ return max_phy_rate_nsa_dl_fr1_15khz[self.get_nr_bandwidth()]
+ else:
+ return max_phy_rate_nsa_ul_fr1_15khz[self.get_nr_bandwidth()]
+
# vim: expandtab tabstop=4 shiftwidth=4