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