blob: 7514604434a0a9a82a689aaded87be130adf16d9 [file] [log] [blame]
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +02001# osmo_gsm_tester: base classes to share code among eNodeB subclasses.
2#
3# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
4#
5# Author: Pau Espin Pedrol <pespin@sysmocom.de>
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20from abc import ABCMeta, abstractmethod
Pau Espin Pedrole1a58bd2020-04-10 20:46:07 +020021from ..core import log, config
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020022from ..core import schema
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +020023
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020024def on_register_schemas():
25 resource_schema = {
26 'label': schema.STR,
27 'type': schema.STR,
28 'remote_user': schema.STR,
29 'addr': schema.IPV4,
30 'gtp_bind_addr': schema.IPV4,
31 'id': schema.UINT,
32 'num_prb': schema.UINT,
33 'transmission_mode': schema.LTE_TRANSMISSION_MODE,
34 'tx_gain': schema.UINT,
35 'rx_gain': schema.UINT,
36 'rf_dev_type': schema.STR,
37 'rf_dev_args': schema.STR,
38 'additional_args': schema.STR,
39 'enable_measurements': schema.BOOL_STR,
40 'a1_report_type': schema.STR,
41 'a1_report_value': schema.INT,
42 'a1_hysteresis': schema.INT,
43 'a1_time_to_trigger': schema.INT,
44 'a2_report_type': schema.STR,
45 'a2_report_value': schema.INT,
46 'a2_hysteresis': schema.INT,
47 'a2_time_to_trigger': schema.INT,
48 'a3_report_type': schema.STR,
49 'a3_report_value': schema.INT,
50 'a3_hysteresis': schema.INT,
51 'a3_time_to_trigger': schema.INT,
52 'num_cells': schema.UINT,
53 'cell_list[].cell_id': schema.UINT,
54 'cell_list[].pci': schema.UINT,
55 'cell_list[].ncell_list[]': schema.UINT,
56 'cell_list[].scell_list[]': schema.UINT,
57 'cell_list[].dl_earfcn': schema.UINT,
58 'cell_list[].dl_rfemu.type': schema.STR,
59 'cell_list[].dl_rfemu.addr': schema.IPV4,
60 'cell_list[].dl_rfemu.ports[]': schema.UINT,
61 }
62 schema.register_resource_schema('enb', resource_schema)
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +020063
64class eNodeB(log.Origin, metaclass=ABCMeta):
65
66##############
67# PROTECTED
68##############
69 def __init__(self, suite_run, conf, name):
70 super().__init__(log.C_RUN, '%s' % name)
71 self._conf = conf
72 self._addr = conf.get('addr', None)
73 if self._addr is None:
74 raise log.Error('addr not set')
Andre Puschmann4b5a09a2020-04-14 22:24:00 +020075 self._gtp_bind_addr = conf.get('gtp_bind_addr', None)
76 if self._gtp_bind_addr is None:
77 self._gtp_bind_addr = self._addr
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +020078 self.set_name('%s_%s' % (name, self._addr))
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +020079 self._txmode = 0
Pau Espin Pedrol491f77c2020-04-20 14:20:43 +020080 self._id = None
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +020081 self._num_prb = 0
Pau Espin Pedrolf46ae222020-04-17 16:23:54 +020082 self._num_cells = None
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +020083 self._epc = None
84
Pau Espin Pedrolc04528c2020-04-01 13:55:51 +020085 def configure(self, config_specifics_li):
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +020086 values = dict(enb=config.get_defaults('enb'))
Pau Espin Pedrolc04528c2020-04-01 13:55:51 +020087 for config_specifics in config_specifics_li:
88 config.overlay(values, dict(enb=config.get_defaults(config_specifics)))
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +020089 config.overlay(values, dict(enb=self.suite_run.config().get('enb', {})))
Pau Espin Pedrolc04528c2020-04-01 13:55:51 +020090 for config_specifics in config_specifics_li:
91 config.overlay(values, dict(enb=self.suite_run.config().get(config_specifics, {})))
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +020092 config.overlay(values, dict(enb=self._conf))
Pau Espin Pedrol491f77c2020-04-20 14:20:43 +020093 self._id = int(values['enb'].get('id', None))
94 assert self._id is not None
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +020095 self._num_prb = int(values['enb'].get('num_prb', None))
96 assert self._num_prb
97 self._txmode = int(values['enb'].get('transmission_mode', None))
98 assert self._txmode
99 config.overlay(values, dict(enb={ 'num_ports': self.num_ports() }))
100 assert self._epc is not None
101 config.overlay(values, dict(enb={ 'mme_addr': self._epc.addr() }))
Andre Puschmann4b5a09a2020-04-14 22:24:00 +0200102 config.overlay(values, dict(enb={ 'gtp_bind_addr': self._gtp_bind_addr }))
Pau Espin Pedrolf46ae222020-04-17 16:23:54 +0200103 self._num_cells = int(values['enb'].get('num_cells', None))
104 assert self._num_cells
105
106 # adjust cell_list to num_cells length:
107 len_cell_list = len(values['enb']['cell_list'])
108 if len_cell_list >= self._num_cells:
109 values['enb']['cell_list'] = values['enb']['cell_list'][:self._num_cells]
110 else:
111 raise log.Error('enb.cell_list items (%d) < enb.num_cells (%d) attribute!' % (len_cell_list, self._num_cells))
112 # adjust scell list (to only contain values available in cell_list):
113 cell_id_list = [c['cell_id'] for c in values['enb']['cell_list']]
114 for i in range(len(values['enb']['cell_list'])):
115 scell_list_old = values['enb']['cell_list'][i]['scell_list']
116 scell_list_new = []
117 for scell_id in scell_list_old:
118 if scell_id in cell_id_list:
119 scell_list_new.append(scell_id)
120 values['enb']['cell_list'][i]['scell_list'] = scell_list_new
121
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +0200122 return values
123
Pau Espin Pedrol491f77c2020-04-20 14:20:43 +0200124 def id(self):
125 return self._id
126
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +0200127 def num_ports(self):
128 if self._txmode == 1:
129 return 1
130 return 2
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +0200131
132########################
133# PUBLIC - INTERNAL API
134########################
135 def cleanup(self):
136 'Nothing to do by default. Subclass can override if required.'
137 pass
138
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +0200139 def num_prb(self):
140 return self._num_prb
141
Andre Puschmanne2a6da62020-04-20 20:39:34 +0200142 #reference: srsLTE.git srslte_symbol_sz()
143 def num_prb2symbol_sz(self, num_prb):
144 if num_prb <= 6:
145 return 128
146 if num_prb <= 15:
147 return 256
148 if num_prb <= 25:
149 return 384
150 if num_prb <= 50:
151 return 768
152 if num_prb <= 75:
153 return 1024
154 if num_prb <= 110:
155 return 1536
156 raise log.Error('invalid num_prb %r', num_prb)
157
158 def num_prb2base_srate(self, num_prb):
159 return self.num_prb2symbol_sz(num_prb) * 15 * 1000
160
161 def get_zmq_rf_dev_args(self):
162 base_srate = self.num_prb2base_srate(self.num_prb())
163 # Define all 8 possible RF ports (2x CA with 2x2 MIMO)
164 rf_dev_args = 'fail_on_disconnect=true' \
165 + ',tx_port0=tcp://' + self.addr() + ':2000' \
166 + ',tx_port1=tcp://' + self.addr() + ':2002' \
167 + ',tx_port2=tcp://' + self.addr() + ':2004' \
168 + ',tx_port3=tcp://' + self.addr() + ':2006' \
169 + ',rx_port0=tcp://' + self.ue.addr() + ':2001' \
170 + ',rx_port1=tcp://' + self.ue.addr() + ':2003' \
171 + ',rx_port2=tcp://' + self.ue.addr() + ':2005' \
172 + ',rx_port3=tcp://' + self.ue.addr() + ':2007'
173
174 if self._num_cells == 1:
175 # Single carrier
176 if self.num_ports() == 1:
177 # SISO
178 rf_dev_args += ',tx_freq0=2630e6,rx_freq0=2510e6'
179 elif self.num_ports() == 2:
180 # MIMO
181 rf_dev_args += ',tx_freq0=2630e6,tx_freq1=2630e6,rx_freq0=2510e6,rx_freq1=2510e6'
182 elif self._num_cells == 2:
183 # 2x class
184 if self.num_ports() == 1:
185 # SISO
186 rf_dev_args += ',tx_freq0=2630e6,tx_freq1=2650e6,rx_freq0=2510e6,rx_freq1=2530e6'
187 elif self.num_ports() == 2:
188 # MIMO
189 rf_dev_args += ',tx_freq0=2630e6,tx_freq1=2630e6,tx_freq2=2650e6,tx_freq3=2650e6,rx_freq0=2510e6,rx_freq1=2510e6,rx_freq2=2530e6,rx_freq3=2530e6'
190
191 rf_dev_args += ',id=enb,base_srate=' + str(base_srate)
192
193 return rf_dev_args
194
Pau Espin Pedrol1ee5ec52020-05-04 17:16:39 +0200195 def get_instance_by_type(suite_run, conf):
196 """Allocate a ENB child class based on type. Opts are passed to the newly created object."""
197 enb_type = conf.get('type')
198 if enb_type is None:
199 raise RuntimeError('ENB type is not defined!')
200
201 if enb_type == 'amarisoftenb':
202 from .enb_amarisoft import AmarisoftENB
203 enb_class = AmarisoftENB
204 elif enb_type == 'srsenb':
205 from .enb_srs import srsENB
206 enb_class = srsENB
207 else:
208 raise log.Error('ENB type not supported:', enb_type)
209 return enb_class(suite_run, conf)
210
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +0200211###################
212# PUBLIC (test API included)
213###################
214 @abstractmethod
215 def start(self, epc):
216 'Starts ENB, it will connect to "epc"'
217 pass
218
219 @abstractmethod
220 def ue_add(self, ue):
221 pass
222
223 @abstractmethod
224 def running(self):
225 pass
226
227 @abstractmethod
228 def ue_max_rate(self, downlink=True):
229 pass
230
Pau Espin Pedrold4404d52020-04-20 13:29:31 +0200231 @abstractmethod
232 def get_rfemu(self, cell=0, dl=True):
233 'Get rfemu.RFemulation subclass implementation object for given cell index and direction.'
234 pass
235
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +0200236 def addr(self):
237 return self._addr
238
Pau Espin Pedrole44e76a2020-03-31 12:35:19 +0200239 def ue_max_rate(self, downlink=True):
240 # The max rate for a single UE per PRB configuration in TM1
241 max_phy_rate_tm1_dl = { 6 : 3.5e6,
242 15 : 11e6,
243 25 : 18e6,
244 50 : 36e6,
245 75 : 55e6,
246 100 : 75e6 }
247 max_phy_rate_tm1_ul = { 6 : 0.9e6,
248 15 : 4.7e6,
249 25 : 10e6,
250 50 : 23e6,
251 75 : 34e6,
252 100 : 51e6 }
253 if downlink:
254 max_rate = max_phy_rate_tm1_dl[self.num_prb()]
255 else:
256 max_rate = max_phy_rate_tm1_ul[self.num_prb()]
257 #TODO: calculate for non-standard prb numbers.
258 if self._txmode > 2:
259 max_rate *= 2
260 # We use 3 control symbols for 6, 15 and 25 PRBs which results in lower max rate
261 if self.num_prb() < 50:
262 max_rate *= 0.9
263 return max_rate
264
Pau Espin Pedrol786a6bc2020-03-30 13:51:21 +0200265# vim: expandtab tabstop=4 shiftwidth=4