diff --git a/doc/examples/4g_srsLTE/defaults.conf b/doc/examples/4g_srsLTE/defaults.conf index 4a3b4c4a..1d1f286e 100644 --- a/doc/examples/4g_srsLTE/defaults.conf +++ b/doc/examples/4g_srsLTE/defaults.conf @@ -12,6 +12,7 @@ enb: mnc: 70 transmission_mode: 1 num_cells: 1 + num_nr_cells: 0 enable_measurements: false a1_report_type: rsrp a1_report_value: -105 diff --git a/src/osmo_gsm_tester/obj/enb.py b/src/osmo_gsm_tester/obj/enb.py index 252fa143..e9767534 100644 --- a/src/osmo_gsm_tester/obj/enb.py +++ b/src/osmo_gsm_tester/obj/enb.py @@ -68,12 +68,18 @@ def on_register_schemas(): '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 @@ class eNodeB(log.Origin, metaclass=ABCMeta): 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 @@ class eNodeB(log.Origin, metaclass=ABCMeta): 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 @@ class eNodeB(log.Origin, metaclass=ABCMeta): 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 @@ class eNodeB(log.Origin, metaclass=ABCMeta): 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 @@ class eNodeB(log.Origin, metaclass=ABCMeta): 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 @@ class eNodeB(log.Origin, metaclass=ABCMeta): 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 @@ class eNodeB(log.Origin, metaclass=ABCMeta): 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 405ed682..34ab5c1c 100644 --- a/src/osmo_gsm_tester/obj/enb_amarisoft.py +++ b/src/osmo_gsm_tester/obj/enb_amarisoft.py @@ -22,6 +22,7 @@ import pprint 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 @@ def on_register_schemas(): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 @@ class AmarisoftENB(enb.eNodeB): 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 diff --git a/src/osmo_gsm_tester/templates/amarisoft_drb_nr.cfg.tmpl b/src/osmo_gsm_tester/templates/amarisoft_drb_nr.cfg.tmpl new file mode 100644 index 00000000..82f3b8cf --- /dev/null +++ b/src/osmo_gsm_tester/templates/amarisoft_drb_nr.cfg.tmpl @@ -0,0 +1,423 @@ +/* DRB configuration for each QCI value. + QCI characteristics in TS 23.203 table 6.1.7 */ +[ + /**************************************** GBR */ + { + qci: 1, /* UM - real time (RTP for VOIP) */ + use_for_en_dc: false, + pdcp_config: { + discardTimer: 100, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 12, + pdcp_SN_SizeDL: 12, + statusReportRequired: false, + outOfOrderDelivery: false, + t_Reordering: 0, + /* ROHC header compression */ + /* + headerCompression: { + maxCID: 15, + profile0x0001: true, // RTP profile + profile0x0002: true, // UDP profile + profile0x0004: false, // IP profile + }, + */ + }, + rlc_config: { + ul_um: { + sn_FieldLength: 6, + }, + dl_um: { + sn_FieldLength: 6, + t_Reassembly: 50, + }, + }, + logical_channel_config: { + priority: 7, + prioritisedBitRate: 0, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 1, + }, + }, + { + qci: 2, /* UM - real time (video) */ + pdcp_config: { + discardTimer: 150, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: false, + outOfOrderDelivery: false, + t_Reordering: 0, + }, + rlc_config: { + ul_um: { + sn_FieldLength: 12, + }, + dl_um: { + sn_FieldLength: 12, + t_Reassembly: 50, + }, + }, + logical_channel_config: { + priority: 8, + prioritisedBitRate: 0, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 1, + }, + }, + { + qci: 3, /* UM - real time (gaming) */ + pdcp_config: { + discardTimer: 100, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: false, + outOfOrderDelivery: false, + t_Reordering: 0, + }, + rlc_config: { + ul_um: { + sn_FieldLength: 12, + }, + dl_um: { + sn_FieldLength: 12, + t_Reassembly: 50, + }, + }, + logical_channel_config: { + priority: 7, + prioritisedBitRate: 0, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 2, + }, + }, + { + qci: 4, /* AM - Non-Conversational Video (Buffered Streaming) */ + pdcp_config: { + discardTimer: 0, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: true, + outOfOrderDelivery: false, + }, + rlc_config: { + ul_am: { + sn_FieldLength: 18, + t_PollRetransmit: 80, /* in ms */ + pollPDU: 64, + pollByte: 125, /* in kBytes, 0 means infinity */ + maxRetxThreshold: 4, + }, + dl_am: { + sn_FieldLength: 18, + t_Reassembly: 80, /* in ms */ + t_StatusProhibit: 10, /* in ms */ + }, + }, + logical_channel_config: { + priority: 9, + prioritisedBitRate: 8, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 3, + }, + }, + { + qci: 65, /* UM - real time (MC-PTT voice) */ + use_for_en_dc: false, + pdcp_config: { + discardTimer: 100, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 12, + pdcp_SN_SizeDL: 12, + statusReportRequired: false, + outOfOrderDelivery: false, + t_Reordering: 0, + /* ROHC header compression */ + /* + headerCompression: { + maxCID: 15, + profile0x0001: true, // RTP profile + profile0x0002: true, // UDP profile + profile0x0004: false, // IP profile + }, + */ + }, + rlc_config: { + ul_um: { + sn_FieldLength: 6, + }, + dl_um: { + sn_FieldLength: 6, + t_Reassembly: 50, + }, + }, + logical_channel_config: { + priority: 5, + prioritisedBitRate: 0, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 4, + }, + }, + { + qci: 66, /* UM - real time (non MC-PTT voice) */ + use_for_en_dc: false, + pdcp_config: { + discardTimer: 150, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: false, + outOfOrderDelivery: false, + t_Reordering: 0, + }, + rlc_config: { + ul_um: { + sn_FieldLength: 12, + }, + dl_um: { + sn_FieldLength: 12, + t_Reassembly: 50, + }, + }, + logical_channel_config: { + priority: 7, + prioritisedBitRate: 0, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 4, + }, + }, + { + qci: 67, /* UM - Mission Critical Video user plane */ + use_for_en_dc: false, + pdcp_config: { + discardTimer: 100, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: false, + outOfOrderDelivery: false, + t_Reordering: 0, + }, + rlc_config: { + ul_um: { + sn_FieldLength: 12, + }, + dl_um: { + sn_FieldLength: 12, + t_Reassembly: 50, + }, + }, + logical_channel_config: { + priority: 6, + prioritisedBitRate: 0, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 5, + }, + }, + /**************************************** non GBR */ + { + qci: 5, /* AM - high priority (SIP) */ + use_for_en_dc: false, + pdcp_config: { + discardTimer: 0, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: true, + outOfOrderDelivery: false, + }, + rlc_config: { + ul_am: { + sn_FieldLength: 18, + t_PollRetransmit: 80, /* in ms */ + pollPDU: 64, + pollByte: 125, /* in kBytes, 0 means infinity */ + maxRetxThreshold: 4, + }, + dl_am: { + sn_FieldLength: 18, + t_Reassembly: 80, /* in ms */ + t_StatusProhibit: 10, /* in ms */ + }, + }, + logical_channel_config: { + priority: 6, + prioritisedBitRate: 8, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 4, + }, + }, + { + qci: 6, /* AM - Video (buffered streaming) */ + pdcp_config: { + discardTimer: 0, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: true, + outOfOrderDelivery: false, + }, + rlc_config: { + ul_am: { + sn_FieldLength: 18, + t_PollRetransmit: 80, /* in ms */ + pollPDU: 64, + pollByte: 125, /* in kBytes, 0 means infinity */ + maxRetxThreshold: 4, + }, + dl_am: { + sn_FieldLength: 18, + t_Reassembly: 80, /* in ms */ + t_StatusProhibit: 10, /* in ms */ + }, + }, + logical_channel_config: { + priority: 10, + prioritisedBitRate: 8, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 5, + }, + }, + { + qci: 7, /* UM - voice, video (live streaming), interactive gaming */ + pdcp_config: { + discardTimer: 100, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: false, + outOfOrderDelivery: false, + t_Reordering: 0, + }, + rlc_config: { + ul_um: { + sn_FieldLength: 12, + }, + dl_um: { + sn_FieldLength: 12, + t_Reassembly: 50, + }, + }, + logical_channel_config: { + priority: 11, + prioritisedBitRate: 0, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 6, + }, + }, + { + qci: 8, /* AM - best effort (Internet traffic) */ + pdcp_config: { + discardTimer: 0, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: true, + outOfOrderDelivery: false, + }, + rlc_config: { + ul_am: { + sn_FieldLength: 18, + t_PollRetransmit: 80, /* in ms */ + pollPDU: 64, + pollByte: 125, /* in kBytes, 0 means infinity */ + maxRetxThreshold: 4, + }, + dl_am: { + sn_FieldLength: 18, + t_Reassembly: 80, /* in ms */ + t_StatusProhibit: 10, /* in ms */ + }, + }, + logical_channel_config: { + priority: 12, + prioritisedBitRate: 8, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 7, + }, + }, + { + qci: 9, /* AM - best effort (Internet traffic) */ + pdcp_config: { + discardTimer: 0, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: true, + outOfOrderDelivery: false, + }, + rlc_config: { + ul_am: { + sn_FieldLength: 18, + t_PollRetransmit: 80, /* in ms */ + pollPDU: 64, + pollByte: 125, /* in kBytes, 0 means infinity */ + maxRetxThreshold: 4, + }, + dl_am: { + sn_FieldLength: 18, + t_Reassembly: 80, /* in ms */ + t_StatusProhibit: 10, /* in ms */ + }, + }, + logical_channel_config: { + priority: 13, + prioritisedBitRate: 8, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 7, + }, + }, + { + qci: 69, /* AM - high priority (MC-PTT signalling) */ + use_for_en_dc: false, + pdcp_config: { + discardTimer: 0, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: true, + outOfOrderDelivery: false, + }, + rlc_config: { + ul_am: { + sn_FieldLength: 18, + t_PollRetransmit: 80, /* in ms */ + pollPDU: 64, + pollByte: 125, /* in kBytes, 0 means infinity */ + maxRetxThreshold: 4, + }, + dl_am: { + sn_FieldLength: 18, + t_Reassembly: 80, /* in ms */ + t_StatusProhibit: 10, /* in ms */ + }, + }, + logical_channel_config: { + priority: 4, + prioritisedBitRate: 8, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 4, + }, + }, + { + qci: 70, /* AM - MC data */ + use_for_en_dc: false, + pdcp_config: { + discardTimer: 0, /* in ms, 0 means infinity */ + pdcp_SN_SizeUL: 18, + pdcp_SN_SizeDL: 18, + statusReportRequired: true, + outOfOrderDelivery: false, + }, + rlc_config: { + ul_am: { + sn_FieldLength: 18, + t_PollRetransmit: 80, /* in ms */ + pollPDU: 64, + pollByte: 125, /* in kBytes, 0 means infinity */ + maxRetxThreshold: 4, + }, + dl_am: { + sn_FieldLength: 18, + t_Reassembly: 80, /* in ms */ + t_StatusProhibit: 10, /* in ms */ + }, + }, + logical_channel_config: { + priority: 11, + prioritisedBitRate: 8, /* in kb/s, -1 means infinity */ + bucketSizeDuration: 100, /* in ms */ + logicalChannelGroup: 5, + }, + }, +] diff --git a/src/osmo_gsm_tester/templates/amarisoft_enb.cfg.tmpl b/src/osmo_gsm_tester/templates/amarisoft_enb.cfg.tmpl index 2c6fcc03..89b531cf 100644 --- a/src/osmo_gsm_tester/templates/amarisoft_enb.cfg.tmpl +++ b/src/osmo_gsm_tester/templates/amarisoft_enb.cfg.tmpl @@ -1,6 +1,5 @@ -/* lteenb configuration file version 2018-10-18 - * Copyright (C) 2015-2018 Amarisoft - */ +/* OGT templated version of the lteenb configuration file for 4G and 5G NSA */ + { %if enb.license_server_addr != '0.0.0.0': @@ -64,10 +63,15 @@ /* high 20 bits of SIB1.cellIdentifier */ enb_id: ${enb.id}, +% if int(enb.num_nr_cells) > 0: + nr_support: true, +% endif + /* list of cells */ cell_list: [ %for cell in enb.cell_list: +%if loop.index == 0: { dl_earfcn: ${cell.dl_earfcn}, rf_port: ${cell.rf_port}, @@ -85,12 +89,38 @@ scell_list: [ %for scell_id in cell.scell_list: { cell_id: ${scell_id}, cross_carrier_scheduling: false, scheduling_cell_id: ${cell.cell_id}, ul_allowed: true}, +%endfor + ], + + nr_scell_list: [ +%for nr_scell_id in cell.nr_scell_list: + { cell_id: ${nr_scell_id} }, %endfor ], }, +%endif %endfor ], /* cell_list */ +% if int(enb.num_nr_cells) > 0: + + nr_cell_list: [ + +%for nr_cell in enb.nr_cell_list: + %if loop.index == 0: + { + rf_port: ${nr_cell.rf_port}, + cell_id: ${nr_cell.cell_id}, + band: ${nr_cell.band}, + dl_nr_arfcn: ${nr_cell.dl_nr_arfcn}, + }, + %endif +%endfor + + ], /* nr_cell_list */ + +% endif + /* default cell parameters */ cell_default: { /* Broadcasted PLMN identities */ @@ -123,7 +153,11 @@ /* other SIBs, in same order as the scheduling list in SIB 1 */ sib_sched_list: [ "amarisoft_sib23.asn" ], +% if int(enb.num_prb) == 6: si_coderate: 0.30, /* maximum code rate for SI/RA/P-RNTI messages */ +% else: + si_coderate: 0.20, /* maximum code rate for SI/RA/P-RNTI messages */ +% endif si_pdcch_format: 2, /* 2 or 3. Log2 of the number of CCEs for PDCCH for SI/RA/P-RNTI */ @@ -154,7 +188,11 @@ pusch_msg3_mcs: 0, /* this CQI value is assumed when none is received from the UE */ +% if int(enb.num_prb) == 6: initial_cqi: 5, +% else: + initial_cqi: 3, +% endif /* if defined, force the PUSCH MCS for all UEs. Otherwise it is computed from the last received SRS/PUSCH. */ @@ -162,6 +200,9 @@ transmission_mode: ${enb.transmission_mode}, + dl_256qam: true, + ul_64qam: true, + /* Scheduling request period (ms). Must be >= 40 for HD-FDD */ sr_period: 20, @@ -188,6 +229,9 @@ // tdd_ack_nack_feedback_mode_r10: "cs", % endif + n1_pucch_sr_count: 11, /* increase if more UEs are needed */ + cqi_pucch_n_rb: 1, /* increase if more UEs are needed */ + /* number of PUCCH 1b CS resources. It determines the maximum number of UEs that can be scheduled in one TTI using carrier aggregation with PUCCH 1b CS ack/nack feedback. */ @@ -266,6 +310,14 @@ a3_offset: ${enb.a3_report_value}, a3_hysteresis: ${enb.a3_hysteresis}, a3_time_to_trigger: ${enb.a3_time_to_trigger}, +% if int(enb.num_nr_cells) > 0: + // NR events hard-coded + nr_b1_report_type: "rsrp", + nr_b1_rsrp: -100, + nr_b1_hysteresis: 0, + nr_b1_time_to_trigger: 100, + nr_rsrp_filter_coeff: 3 +% endif }, /* measurement gap configuration */ @@ -276,4 +328,357 @@ ho_from_meas: true, % endif }, -} + +% if int(enb.num_nr_cells) > 0: + nr_cell_default: { + subcarrier_spacing: 15, /* kHz */ + ssb_subcarrier_spacing: 30, + bandwidth: ${enb.nr_bandwidth}, /* MHz */ + n_antenna_dl: 1, + n_antenna_ul: 1, + + /* force the timing TA offset (optional) */ + n_timing_advance_offset: 0, + + tdd_ul_dl_config: { + pattern1: { + period: 10, + dl_slots: 6, + dl_symbols: 0, + ul_slots: 3, + ul_symbols: 0, + }, + }, + ssb_pos_bitmap: "10000000", + ssb_period: 20, /* in ms */ + n_id_cell: 500, + + root_sequence_index: 1, /* PRACH root sequence index */ + + /* Scheduling request period (slots). */ + sr_period: 40, + + dmrs_type_a_pos: 2, + + /* to limit the number of HARQ feedback in UL, use pdsch_harq_ack_max; + allows to workaround issues with SM-G977N for example */ + //pdsch_harq_ack_max: 2, + + prach: { + prach_config_index: 0, + msg1_subcarrier_spacing: 15, /* kHz */ + msg1_fdm: 1, +% if int(enb.nr_bandwidth) == 10: + msg1_frequency_start: 1, +% else: + msg1_frequency_start: 3, +% endif + zero_correlation_zone_config: 0, + preamble_received_target_power: -110, /* in dBm */ + preamble_trans_max: 7, + power_ramping_step: 4, /* in dB */ + ra_response_window: 10, /* in slots */ + restricted_set_config: "unrestricted_set", + ra_contention_resolution_timer: 64, /* in ms */ + ssb_per_prach_occasion: 1, + cb_preambles_per_ssb: 8, + }, + + pdcch: { + common_coreset: { + rb_start: -1, /* -1 to have the maximum bandwidth */ + l_crb: -1, /* -1 means all the bandwidth */ + duration: 1, + precoder_granularity: "sameAsREG_bundle", + //dmrs_scid: 0, + }, + + dedicated_coreset: { + rb_start: -1, /* -1 to have the maximum bandwidth */ + l_crb: -1, /* -1 means all the bandwidth */ + duration: 1, + precoder_granularity: "sameAsREG_bundle", + //dmrs_scid: 0, + }, + + css: { + n_candidates: [ 1, 1, 1, 0, 0 ], + }, + rar_al_index: 2, + + uss: { + n_candidates: [ 0, 2, 1, 0, 0 ], + dci_0_1_and_1_1: false, + force_dci_0_0: true, // Forces DCI format 0_0 for Uplink + force_dci_1_0: true, // Forces DCI format 1_0 for Downlink + }, + al_index: 1, + }, + + pdsch: { + mapping_type: "typeA", + start_symb: 1, + n_symb: 13, + dmrs_add_pos: 1, + dmrs_type: 1, + dmrs_max_len: 1, + k0: 0, /* delay in slots from DCI to PDSCH */ + /* delay in slots from PDSCH to PUCCH/PUSCH ACK/NACK */ + k1: [ 8, 7, 6, 6, 5, 4], + mcs_table: "qam64", + + rar_mcs: 2, + /* If defined, force the PDSCH MCS for all UEs. Otherwise it is computed + * based on DL channel quality estimation */ + /* mcs: 24, */ + }, + + csi_rs: { + nzp_csi_rs_resource: [ + { + csi_rs_id: 0, + n_ports: 1, + frequency_domain_allocation: "row2", + bitmap: "100000000000", + cdm_type: "no_cdm", + density: 1, + first_symb: 4, + rb_start: 0, + l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */ + power_control_offset: 0, /* dB */ + power_control_offset_ss: 0, /* dB */ + scrambling_id: 0, + period: 80, + offset: 1, /* != 0 to avoid collision with SSB */ + qcl_info_periodic_csi_rs: 0, + }, +#define USE_TRS +#ifdef USE_TRS + /* TRS : period of 40 ms, slots 1 & 2, symbols 4 and 8 */ + { + csi_rs_id: 1, + n_ports: 1, + frequency_domain_allocation: "row1", + bitmap: "0001", + cdm_type: "no_cdm", + density: 3, + first_symb: 4, + rb_start: 0, + l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */ + power_control_offset: 0, /* dB */ + power_control_offset_ss: 0, /* dB */ + scrambling_id: 0, + period: 40, + offset: 11, + qcl_info_periodic_csi_rs: 0, + }, + { + csi_rs_id: 2, + n_ports: 1, + frequency_domain_allocation: "row1", + bitmap: "0001", + cdm_type: "no_cdm", + density: 3, + first_symb: 8, + rb_start: 0, + l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */ + power_control_offset: 0, /* dB */ + power_control_offset_ss: 0, /* dB */ + scrambling_id: 0, + period: 40, + offset: 11, + qcl_info_periodic_csi_rs: 0, + }, + { + csi_rs_id: 3, + n_ports: 1, + frequency_domain_allocation: "row1", + bitmap: "0001", + cdm_type: "no_cdm", + density: 3, + first_symb: 4, + rb_start: 0, + l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */ + power_control_offset: 0, /* dB */ + power_control_offset_ss: 0, /* dB */ + scrambling_id: 0, + period: 40, + offset: 12, + qcl_info_periodic_csi_rs: 0, + }, + { + csi_rs_id: 4, + n_ports: 1, + frequency_domain_allocation: "row1", + bitmap: "0001", + cdm_type: "no_cdm", + density: 3, + first_symb: 8, + rb_start: 0, + l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */ + power_control_offset: 0, /* dB */ + power_control_offset_ss: 0, /* dB */ + scrambling_id: 0, + period: 40, + offset: 12, + qcl_info_periodic_csi_rs: 0, + }, +#endif + ], + nzp_csi_rs_resource_set: [ + { + csi_rs_set_id: 0, + nzp_csi_rs_resources: [ 0 ], + repetition: false, + }, +#ifdef USE_TRS + { + csi_rs_set_id: 1, + nzp_csi_rs_resources: [ 1, 2, 3, 4 ], + repetition: false, + trs_info: true, + }, +#endif + ], + + csi_im_resource: [ + { + csi_im_id: 0, + pattern: 1, + subcarrier_location: 8, + symbol_location: 8, + rb_start: 0, + l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */ + period: 80, + offset: 1, /* != 0 to avoid collision with SSB */ + }, + ], + csi_im_resource_set: [ + { + csi_im_set_id: 0, + csi_im_resources: [ 0 ], + } + ], + /* ZP CSI-RS to set the CSI-IM REs to zero */ + zp_csi_rs_resource: [ + { + csi_rs_id: 0, + frequency_domain_allocation: "row4", + bitmap: "100", + n_ports: 4, + cdm_type: "fd_cdm2", + first_symb: 8, + density: 1, + rb_start: 0, + l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */ + period: 80, + offset: 1, + }, + ], + p_zp_csi_rs_resource_set: [ + { + zp_csi_rs_resources: [ 0 ], + }, + ], + + csi_resource_config: [ + { + csi_rsc_config_id: 0, + nzp_csi_rs_resource_set_list: [ 0 ], + resource_type: "periodic", + }, + { + csi_rsc_config_id: 1, + csi_im_resource_set_list: [ 0 ], + resource_type: "periodic", + }, +#ifdef USE_TRS + { + csi_rsc_config_id: 2, + nzp_csi_rs_resource_set_list: [ 1 ], + resource_type: "periodic", + }, +#endif + ], + csi_report_config: [ + { + resources_for_channel_measurement: 0, + csi_im_resources_for_interference: 1, + report_config_type: "periodic", + period: 80, + report_quantity: "CRI_RI_PMI_CQI", + cqi_table: 2, + subband_size: "value1", + }, + ], + }, + + pucch: { + pucch_group_hopping: "neither", + hopping_id: -1, /* -1 = n_cell_id */ + p0_nominal: -90, + pucch1: { + n_cs: 3, + n_occ: 3, + freq_hopping: false, + }, + pucch2: { + n_symb: 2, + n_prb: 1, + freq_hopping: false, + simultaneous_harq_ack_csi: false, + max_code_rate: 0.25, + }, + }, + + pusch: { + mapping_type: "typeA", + n_symb: 14, + dmrs_add_pos: 1, + dmrs_type: 1, + dmrs_max_len: 1, + tf_precoding: false, + mcs_table: "qam64", /* without transform precoding */ + mcs_table_tp: "qam64", /* with transform precoding */ + ldpc_max_its: 5, + k2: 4, /* delay in slots from DCI to PUSCH */ + p0_nominal_with_grant: -90, + msg3_k2: 5, + msg3_mcs: 4, + msg3_delta_power: 0, /* in dB */ + beta_offset_ack_index: 9, + + /* hardcoded scheduling parameters */ + n_dmrs_cdm_groups: 1, + n_layer: 1, + /* if defined, force the PUSCH MCS for all UEs. Otherwise it is + computed from the last received PUSCH. */ + //mcs: 16, + //max_mcs: 16, + }, + + /* MAC configuration */ + mac_config: { + msg3_max_harq_tx: 5, + ul_max_harq_tx: 5, /* max number of HARQ transmissions for uplink */ + dl_max_harq_tx: 5, /* max number of HARQ transmissions for downlink */ + ul_max_consecutive_retx: 30, /* disconnect UE if reached */ + dl_max_consecutive_retx: 30, /* disconnect UE if reached */ + periodic_bsr_timer: 20, + retx_bsr_timer: 320, + periodic_phr_timer: 500, + prohibit_phr_timer: 200, + phr_tx_power_factor_change: "dB3", + sr_prohibit_timer: 0, /* in ms, 0 to disable the timer */ + sr_trans_max: 64, + }, + + cipher_algo_pref: [${', '.join(list(dict.fromkeys(enb.cipher_list))).split("eea")[1] if len(list(dict.fromkeys(enb.cipher_list))) > 0 else ''}], + integ_algo_pref: [${', '.join(list(dict.fromkeys(enb.integrity_list))).split("eia")[1]}], + + inactivity_timer: ${enb.inactivity_timer}, + + drb_config: "amarisoft_drb_nr.cfg", + }, +% endif +} \ No newline at end of file diff --git a/src/osmo_gsm_tester/templates/amarisoft_rf_driver.cfg.tmpl b/src/osmo_gsm_tester/templates/amarisoft_rf_driver.cfg.tmpl index 36d34ad7..f2942d79 100644 --- a/src/osmo_gsm_tester/templates/amarisoft_rf_driver.cfg.tmpl +++ b/src/osmo_gsm_tester/templates/amarisoft_rf_driver.cfg.tmpl @@ -15,6 +15,26 @@ rf_driver: { tx_gain: ${trx.tx_gain}, /* TX gain (in dB) B2x0: 0 to 89.8 dB */ rx_gain: ${trx.rx_gain}, /* RX gain (in dB) B2x0: 0 to 73 dB */ +rf_ports: [ + % if trx.rf_dev_type == 'sdr': + { + sample_rate: 23.04, + }, + { + sample_rate: 61.44, + dl_freq: 3502.8, // Moves NR DL LO frequency -5.76 MHz + ul_freq: 3502.8, // Moves NR UL LO frequency -5.76 MHz + } + % else: + { + sample_rate: ${enb.sample_rate}, + }, + { + sample_rate: ${enb.sample_rate}, + } + % endif +], + // only the B210 requires a sample offset % if "b200" in trx.rf_dev_args: tx_time_offset: -150, diff --git a/sysmocom/defaults.conf b/sysmocom/defaults.conf index 8740d626..5af29627 100644 --- a/sysmocom/defaults.conf +++ b/sysmocom/defaults.conf @@ -114,6 +114,7 @@ enb: duplex: fdd transmission_mode: 1 num_cells: 1 + num_nr_cells: 0 inactivity_timer: 20000 enable_measurements: false enable_dl_awgn: false @@ -147,6 +148,8 @@ enb: root_seq_idx: 205 scell_list: [] ncell_list: [] + nr_scell_list: [] + nr_cell_list: [] cipher_list: - eea0 - eea2