Pau Espin Pedrol | 4109123 | 2020-10-05 19:23:38 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python2 |
| 2 | |
Pau Espin Pedrol | d6deb28 | 2020-10-16 18:49:39 +0200 | [diff] [blame] | 3 | # This is script is aimed at being copied to some remote target host where it |
| 4 | # will be run by osmo-gsm-tester through ssh |
| 5 | |
Pau Espin Pedrol | 4109123 | 2020-10-05 19:23:38 +0200 | [diff] [blame] | 6 | from distutils.version import StrictVersion |
| 7 | |
| 8 | from gnuradio.fft import window |
| 9 | from gnuradio import blocks |
| 10 | from gnuradio import gr |
| 11 | from gnuradio.filter import firdes |
| 12 | import sys |
| 13 | import json |
| 14 | from argparse import ArgumentParser |
| 15 | from gnuradio.eng_arg import eng_float, intx |
| 16 | from gnuradio import eng_notation |
| 17 | from gnuradio import zeromq |
| 18 | import socket |
| 19 | import argparse |
| 20 | from signal import * |
| 21 | |
| 22 | class GrBroker(gr.top_block): |
| 23 | |
| 24 | def __init__(self, args, cfg): |
Andre Puschmann | b33f36b | 2020-11-26 21:59:53 +0100 | [diff] [blame] | 25 | gr.top_block.__init__(self, "InterENB Handover Flowgraph") |
Pau Espin Pedrol | 4109123 | 2020-10-05 19:23:38 +0200 | [diff] [blame] | 26 | |
| 27 | ################################################## |
| 28 | # Variables |
| 29 | ################################################## |
| 30 | self.args = args |
| 31 | self.cfg = cfg |
| 32 | self.samp_rate = samp_rate = 23040000 |
| 33 | self.relative_gain = relative_gain = 1.0 |
| 34 | self.blocks_add = {} |
| 35 | |
| 36 | ################################################## |
| 37 | # Blocks |
| 38 | ################################################## |
| 39 | |
| 40 | # Build ENB side + connect to per stream multilier: |
| 41 | for enb in self.cfg['enb']: |
| 42 | for it in enb: |
| 43 | source_addr = 'tcp://%s:%u' % (it['peer_addr'].encode('utf-8'), it['peer_port']) |
| 44 | sink_addr = 'tcp://%s:%u' % (args.bind_addr, it['bind_port']) |
| 45 | print('enb: earfcn=%u source=%r sink=%r' % (it['earfcn'], source_addr, sink_addr)) |
| 46 | it['gr_block_zmq_source'] = zeromq.req_source(gr.sizeof_gr_complex, 1, source_addr, 100, False, -1) |
| 47 | it['gr_block_zmq_sink'] = zeromq.rep_sink(gr.sizeof_gr_complex, 1, sink_addr, 100, False, -1) |
| 48 | it['gr_block_multiply'] = blocks.multiply_const_cc(relative_gain) |
| 49 | it['gr_block_multiply'].set_block_alias('relative_gain %s' % source_addr) |
| 50 | self.connect((it['gr_block_zmq_source'], 0), (it['gr_block_multiply'], 0)) |
| 51 | if it['use_mimo']: |
| 52 | source_addr = 'tcp://%s:%u' % (it['peer_addr'].encode('utf-8'), it['peer_port'] + 1) |
| 53 | sink_addr = 'tcp://%s:%u' % (args.bind_addr, it['bind_port'] + 1) |
| 54 | print('enb: earfcn=%u source=%r sink=%r (MIMO)' % (it['earfcn'], source_addr, sink_addr)) |
| 55 | it['gr_block_zmq_source2'] = zeromq.req_source(gr.sizeof_gr_complex, 1, source_addr, 100, False, -1) |
| 56 | it['gr_block_zmq_sink2'] = zeromq.rep_sink(gr.sizeof_gr_complex, 1, sink_addr, 100, False, -1) |
| 57 | it['gr_block_multiply2'] = blocks.multiply_const_cc(relative_gain) |
| 58 | it['gr_block_multiply2'].set_block_alias('relative_gain %s' % source_addr) |
| 59 | self.connect((it['gr_block_zmq_source2'], 0), (it['gr_block_multiply2'], 0)) |
| 60 | |
| 61 | # Build UE side: |
| 62 | for ue in self.cfg['ue']: |
| 63 | for it in ue: |
| 64 | source_addr = 'tcp://%s:%u' % (it['peer_addr'].encode('utf-8'), it['peer_port']) |
| 65 | sink_addr = 'tcp://%s:%u' % (args.bind_addr, it['bind_port']) |
| 66 | print('ue: earfcn=%u source=%r sink=%r' % (it['earfcn'], source_addr, sink_addr)) |
| 67 | it['gr_block_zmq_source'] = zeromq.req_source(gr.sizeof_gr_complex, 1, source_addr, 100, False, -1) |
| 68 | it['gr_block_zmq_sink'] = zeromq.rep_sink(gr.sizeof_gr_complex, 1, sink_addr, 100, False, -1) |
| 69 | if it['use_mimo']: |
| 70 | source_addr = 'tcp://%s:%u' % (it['peer_addr'].encode('utf-8'), it['peer_port'] + 1) |
| 71 | sink_addr = 'tcp://%s:%u' % (args.bind_addr, it['bind_port'] + 1) |
| 72 | print('ue: earfcn=%u source=%r sink=%r (MIMO)' % (it['earfcn'], source_addr, sink_addr)) |
| 73 | it['gr_block_zmq_source2'] = zeromq.req_source(gr.sizeof_gr_complex, 1, source_addr, 100, False, -1) |
| 74 | it['gr_block_zmq_sink2'] = zeromq.rep_sink(gr.sizeof_gr_complex, 1, sink_addr, 100, False, -1) |
| 75 | |
| 76 | # Create per EARFCN adder (only 2->1 supported so far) |
| 77 | earfcn_li = self.calc_earfcn_list() |
| 78 | blocks_add_next_avail_port = {} |
| 79 | for earfcn in earfcn_li: |
| 80 | self.blocks_add[earfcn] = blocks.add_vcc(1) |
| 81 | blocks_add_next_avail_port[earfcn] = 0 |
| 82 | # Connect the ENB-side multipliers to the Adder input ports: |
| 83 | idx = 0 |
| 84 | for enb in self.cfg['enb']: |
| 85 | for it in enb: |
Andre Puschmann | b33f36b | 2020-11-26 21:59:53 +0100 | [diff] [blame] | 86 | print('Connecting ENB port %u to Adder[%u] for earfcn %u' % (it['bind_port'], blocks_add_next_avail_port[it['earfcn']], it['earfcn'])) |
| 87 | self.connect((it['gr_block_multiply'], 0), (self.blocks_add[it['earfcn']], blocks_add_next_avail_port[it['earfcn']])) |
Pau Espin Pedrol | 4109123 | 2020-10-05 19:23:38 +0200 | [diff] [blame] | 88 | # TODO: if it['use_mimo'], connect it['gr_block_multiply2'] to some adder... |
Andre Puschmann | b33f36b | 2020-11-26 21:59:53 +0100 | [diff] [blame] | 89 | blocks_add_next_avail_port[it['earfcn']] += 1 |
Pau Espin Pedrol | 4109123 | 2020-10-05 19:23:38 +0200 | [diff] [blame] | 90 | |
| 91 | # Connect the Adder to the UE-side (Dl): |
| 92 | for earfcn, bl_add in self.blocks_add.items(): |
| 93 | for ue in self.cfg['ue']: |
| 94 | for it in ue: |
| 95 | if it['earfcn'] != earfcn: |
| 96 | continue |
| 97 | print('Connecting Adder for earfcn %u to UE port %u' % (earfcn, it['bind_port'])) |
| 98 | self.connect((bl_add, 0), (it['gr_block_zmq_sink'], 0)) |
| 99 | # TODO: if it['use_mimo'], connect some adder to it['gr_block_zmq_sink2']... |
| 100 | |
| 101 | # UL: Connect 1 UE port splitting it into N ENB ports: |
| 102 | for ue in self.cfg['ue']: |
| 103 | for it_ue in ue: |
| 104 | for enb in self.cfg['enb']: |
| 105 | for it_enb in enb: |
| 106 | if it_ue['earfcn'] != it_enb['earfcn']: |
| 107 | continue |
| 108 | print('connecting UE port %u to ENB port %u, earfcn=%u' % (it_ue['bind_port'], it_enb['bind_port'], it_enb['earfcn'])) |
| 109 | self.connect((it_ue['gr_block_zmq_source'], 0), (it_enb['gr_block_zmq_sink'], 0)) |
| 110 | if it_ue['use_mimo'] and it_enb['use_mimo']: |
| 111 | self.connect((it_ue['gr_block_zmq_source2'], 0), (it_enb['gr_block_zmq_sink2'], 0)) |
| 112 | |
| 113 | def calc_earfcn_list(self): |
| 114 | earfcn_li = [] |
| 115 | for enb in self.cfg['enb']: |
| 116 | for it in enb: |
| 117 | if it['earfcn'] not in earfcn_li: |
| 118 | earfcn_li.append(it['earfcn']) |
| 119 | return earfcn_li |
| 120 | |
| 121 | def set_relative_gain(self, port, relative_gain): |
| 122 | for enb in self.cfg['enb']: |
| 123 | for it in enb: |
| 124 | if it['bind_port'] == port: |
| 125 | print('setting port %u rel_gain to %f' % (port, relative_gain)) |
| 126 | it['gr_block_multiply'].set_k(relative_gain) |
| 127 | return |
| 128 | |
| 129 | def mainloop(sock, broker): |
| 130 | while True: |
| 131 | chunk = sock.recv(4096) |
| 132 | stringdata = chunk.decode('utf-8') |
| 133 | msg = json.loads(stringdata) |
| 134 | print('Received msg: %s' % msg) |
| 135 | |
| 136 | if msg['action'] == 'exit': |
| 137 | print('Received exit command. Stopping radio...') |
| 138 | return |
| 139 | elif msg['action'] == 'set_relative_gain': |
| 140 | broker.set_relative_gain(msg['port'], msg['rel_gain']) |
| 141 | else: |
| 142 | print('Unknwon action for message: %s' % msg) |
| 143 | |
| 144 | |
| 145 | def sig_handler_cleanup(signum, frame): |
| 146 | print("killed by signal %d" % signum) |
| 147 | # This sys.exit() will raise a SystemExit base exception at the current |
| 148 | # point of execution. Code must be prepared to clean system-wide resources |
| 149 | # by using the "finally" section. This allows at the end 'atexit' hooks to |
| 150 | # be called before exiting. |
| 151 | sys.exit(1) |
| 152 | |
| 153 | def main(): |
| 154 | |
| 155 | for sig in (SIGINT, SIGTERM, SIGQUIT, SIGPIPE, SIGHUP): |
| 156 | signal(sig, sig_handler_cleanup) |
| 157 | |
| 158 | parser = argparse.ArgumentParser() |
| 159 | parser.add_argument('-b', '--bind-addr', dest='bind_addr', help="Address where local sockets are bound to") |
| 160 | parser.add_argument('-c', '--ctrl-port', dest='ctrl_port', type=int, default=5005, help="Port where CTRL interface is bound to") |
| 161 | args = parser.parse_args() |
| 162 | |
| 163 | print('bind_addr:', repr(args.bind_addr)) |
| 164 | print('ctrl_port:', repr(args.ctrl_port)) |
| 165 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
| 166 | sock.bind((args.bind_addr, args.ctrl_port)) |
| 167 | |
| 168 | broker = None |
| 169 | try: |
| 170 | print('waiting for configuration on UDP socket...') |
| 171 | chunk = sock.recv(4096) |
| 172 | print('Received udp packet') |
| 173 | stringdata = chunk.decode('utf-8') |
| 174 | cfg = json.loads(stringdata) |
| 175 | print('Got config:', stringdata) |
| 176 | broker = GrBroker(args, cfg) |
| 177 | print('Starting...') |
| 178 | broker.start() |
| 179 | print('in mainloop') |
| 180 | mainloop(sock, broker) |
| 181 | except KeyboardInterrupt: |
| 182 | pass |
| 183 | print('main loop ended, exiting...') |
| 184 | # closing flowgraph and socket |
| 185 | sock.close() |
| 186 | if broker: |
| 187 | broker.stop() |
| 188 | broker.wait() |
| 189 | |
| 190 | |
| 191 | if __name__ == '__main__': |
| 192 | main() |
| 193 | print("exit") |