From b91039ecd0071020e91a5df2693c2996a7782f82 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 23 Feb 2020 20:03:43 -0500 Subject: [PATCH] initial hacks for python 2/3 agnosticism --- op25/gr-op25_repeater/apps/gr_gnuplot.py | 46 +- op25/gr-op25_repeater/apps/http_server.py | 449 ++++++++++++++++++ op25/gr-op25_repeater/apps/multi_rx.py | 6 +- op25/gr-op25_repeater/apps/p25_decoder.py | 4 +- op25/gr-op25_repeater/apps/p25_demodulator.py | 10 +- op25/gr-op25_repeater/apps/rx.py | 29 +- op25/gr-op25_repeater/apps/tdma/bit_utils.py | 14 +- op25/gr-op25_repeater/apps/tdma/lfsr.py | 66 +-- op25/gr-op25_repeater/apps/terminal.py | 8 +- op25/gr-op25_repeater/apps/trunking.py | 67 +-- op25/gr-op25_repeater/apps/tsvfile.py | 24 +- .../gr-op25_repeater/apps/tx/op25_c4fm_mod.py | 20 +- 12 files changed, 605 insertions(+), 138 deletions(-) create mode 100755 op25/gr-op25_repeater/apps/http_server.py diff --git a/op25/gr-op25_repeater/apps/gr_gnuplot.py b/op25/gr-op25_repeater/apps/gr_gnuplot.py index c9326e4..44317ad 100644 --- a/op25/gr-op25_repeater/apps/gr_gnuplot.py +++ b/op25/gr-op25_repeater/apps/gr_gnuplot.py @@ -131,13 +131,13 @@ class wrap_gp(object): s += 'e\n' self.buf=self.buf[self.sps:] plots.append('"-" with lines') - elif mode == 'constellation': + elif mode == 'constellation': plot_size = (240,240) self.buf = self.buf[:100] - for b in self.buf: + for b in self.buf: s += '%f\t%f\n' % (degrees(np.angle(b)), limit(np.abs(b),1.0)) - s += 'e\n' - plots.append('"-" with points') + s += 'e\n' + plots.append('"-" with points') for b in self.buf: #s += '%f\t%f\n' % (b.real, b.imag) s += '%f\t%f\n' % (degrees(np.angle(b)), limit(np.abs(b),1.0)) @@ -159,7 +159,7 @@ class wrap_gp(object): tune_freq = (self.center_freq - self.relative_freq) / 1e6 if self.center_freq and self.width: self.freqs = ((self.freqs * self.width) + self.center_freq + self.offset_freq) / 1e6 - for i in xrange(len(self.ffts)): + for i in range(len(self.ffts)): if mode == 'fft': self.avg_pwr[i] = ((1.0 - FFT_AVG) * self.avg_pwr[i]) + (FFT_AVG * np.abs(self.ffts[i])) else: @@ -223,37 +223,39 @@ class wrap_gp(object): h += 'set format ""\n' h += 'set style line 11 lt 1 lw 2 pt 2 ps 2\n' - h+= 'set title "Constellation"\n' + h+= 'set title "Constellation"\n' elif mode == 'eye': h+= background h+= 'set yrange [-4:4]\n' - h+= 'set title "Datascope"\n' + h+= 'set title "Datascope"\n' elif mode == 'symbol': h+= background h+= 'set yrange [-4:4]\n' - h+= 'set title "Symbol"\n' + h+= 'set title "Symbol"\n' elif mode == 'fft' or mode == 'mixer': h+= 'unset arrow; unset title\n' h+= 'set xrange [%f:%f]\n' % (self.freqs[0], self.freqs[len(self.freqs)-1]) - h+= 'set xlabel "Frequency"\n' - h+= 'set ylabel "Power(dB)"\n' - h+= 'set grid\n' + h+= 'set xlabel "Frequency"\n' + h+= 'set ylabel "Power(dB)"\n' + h+= 'set grid\n' h+= 'set yrange [-100:0]\n' if mode == 'mixer': # mixer - h+= 'set title "Mixer: balance %3.0f (smaller is better)"\n' % (np.abs(self.avg_sum_pwr * 1000)) + h+= 'set title "Mixer: balance %3.0f (smaller is better)"\n' % (np.abs(self.avg_sum_pwr * 1000)) else: # fft - h+= 'set title "Spectrum"\n' + h+= 'set title "Spectrum"\n' if self.center_freq: arrow_pos = (self.center_freq - self.relative_freq) / 1e6 h+= 'set arrow from %f, graph 0 to %f, graph 1 nohead\n' % (arrow_pos, arrow_pos) h+= 'set title "Spectrum: tuned to %f Mhz"\n' % arrow_pos elif mode == 'float': h+= 'set yrange [-2:2]\n' - h+= 'set title "Oscilloscope"\n' + h+= 'set title "Oscilloscope"\n' dat = '%s%splot %s\n%s' % (h0, h, ','.join(plots), s) - if self.logfile is not None: - with open(self.logfile, 'a') as fd: - fd.write(dat) + if sys.version[0] != '2': + dat = bytes(dat, 'utf8') + if self.logfile is not None: + with open(self.logfile, 'a') as fd: + fd.write(dat) self.gp.poll() if self.gp.returncode is None: # make sure gnuplot is still running try: @@ -293,7 +295,7 @@ class eye_sink_f(gr.sync_block): def work(self, input_items, output_items): in0 = input_items[0] - consumed = self.gnuplot.plot(in0, 100 * self.sps, mode='eye') + consumed = self.gnuplot.plot(in0, 100 * self.sps, mode='eye') return consumed ### len(input_items[0]) def kill(self): @@ -312,7 +314,7 @@ class constellation_sink_c(gr.sync_block): def work(self, input_items, output_items): in0 = input_items[0] - self.gnuplot.plot(in0, 1000, mode='constellation') + self.gnuplot.plot(in0, 1000, mode='constellation') return len(input_items[0]) def kill(self): @@ -335,7 +337,7 @@ class fft_sink_c(gr.sync_block): if self.skip >= 50: self.skip = 0 in0 = input_items[0] - self.gnuplot.plot(in0, FFT_BINS, mode='fft') + self.gnuplot.plot(in0, FFT_BINS, mode='fft') return len(input_items[0]) def kill(self): @@ -343,7 +345,7 @@ class fft_sink_c(gr.sync_block): def set_center_freq(self, f): self.gnuplot.set_center_freq(f) - self.gnuplot.set_relative_freq(0.0) + self.gnuplot.set_relative_freq(0.0) def set_relative_freq(self, f): self.gnuplot.set_relative_freq(f) @@ -390,7 +392,7 @@ class symbol_sink_f(gr.sync_block): def work(self, input_items, output_items): in0 = input_items[0] - self.gnuplot.plot(in0, 2400, mode='symbol') + self.gnuplot.plot(in0, 2400, mode='symbol') return len(input_items[0]) def kill(self): diff --git a/op25/gr-op25_repeater/apps/http_server.py b/op25/gr-op25_repeater/apps/http_server.py new file mode 100755 index 0000000..e05d371 --- /dev/null +++ b/op25/gr-op25_repeater/apps/http_server.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python + +# Copyright 2017, 2018 Max H. Parke KA1RBI +# +# This file is part of OP25 +# +# OP25 is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# OP25 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OP25; see the file COPYING. If not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Boston, MA +# 02110-1301, USA. + +import sys +import os +import time +import re +import json +import socket +import traceback +import threading +import glob +import subprocess +import zmq + +from gnuradio import gr +from waitress.server import create_server +from optparse import OptionParser +from multi_rx import byteify +from tsvfile import load_tsv, make_config + +my_input_q = None +my_output_q = None +my_recv_q = None +my_port = None +my_backend = None +CFG_DIR = '../www/config/' +TSV_DIR = './' + +""" +fake http and ajax server module +TODO: make less fake +""" + +def static_file(environ, start_response): + content_types = { 'png': 'image/png', 'jpeg': 'image/jpeg', 'jpg': 'image/jpeg', 'gif': 'image/gif', 'css': 'text/css', 'js': 'application/javascript', 'html': 'text/html'} + img_types = 'png jpg jpeg gif'.split() + if environ['PATH_INFO'] == '/': + filename = 'index.html' + else: + filename = re.sub(r'[^a-zA-Z0-9_.\-]', '', environ['PATH_INFO']) + suf = filename.split('.')[-1] + pathname = '../www/www-static' + if suf in img_types: + pathname = '../www/images' + pathname = '%s/%s' % (pathname, filename) + if suf not in content_types.keys() or '..' in filename or not os.access(pathname, os.R_OK): + sys.stderr.write('404 %s\n' % pathname) + status = '404 NOT FOUND' + content_type = 'text/plain' + output = status + else: + output = open(pathname, 'rb').read() + content_type = content_types[suf] + status = '200 OK' + return status, content_type, output + +def valid_tsv(filename): + if not os.access(filename, os.R_OK): + return False + line = open(filename).readline() + for word in 'Sysname Offset NAC Modulation TGID Whitelist Blacklist'.split(): + if word not in line: + return False + return True + +def tsv_config(filename): + DEFAULT_CFG = '../www/config/default.json' + filename = '%s%s' % (TSV_DIR, filename) + filename = filename.replace('[TSV]', '.tsv') + if not valid_tsv(filename): + return None + cfg = make_config(load_tsv(filename)) + default_cfg = json.loads(open(DEFAULT_CFG).read()) + + result = default_cfg + channels = [ {'active': True, + 'blacklist': cfg[nac]['blacklist'], + 'whitelist': cfg[nac]['whitelist'], + 'cclist': cfg[nac]['cclist'], + 'demod_type': 'cqpsk', + 'destination': 'udp://127.0.0.1:23456', + 'filter_type': 'rc', + 'frequency': 500000000, + 'if_rate': 24000, + 'nac': nac, + 'name': cfg[nac]['sysname'], + 'phase2_tdma': False, + 'plot': "", + 'tgids': cfg[nac]['tgid_map'], + 'trunked': True + } + for nac in cfg.keys() ] + result['channels'] = channels + return {'json_type':'config_data', 'data': result} + +def do_request(d): + global my_backend + if d['command'].startswith('rx-'): + msg = gr.message().make_from_string(json.dumps(d), -2, 0, 0) + if not my_backend.input_q.full_p(): + my_backend.input_q.insert_tail(msg) + return None + elif d['command'] == 'config-load': + if '[TSV]' in d['data']: + return tsv_config(d['data']) + filename = '%s%s.json' % (CFG_DIR, d['data']) + if not os.access(filename, os.R_OK): + return None + js_msg = json.loads(open(filename).read()) + return {'json_type':'config_data', 'data': js_msg} + elif d['command'] == 'config-list': + files = glob.glob('%s*.json' % CFG_DIR) + files = [x.replace('.json', '') for x in files] + files = [x.replace(CFG_DIR, '') for x in files] + if d['data'] == 'tsv': + tsvfiles = glob.glob('%s*.tsv' % TSV_DIR) + tsvfiles = [x for x in tsvfiles if valid_tsv(x)] + tsvfiles = [x.replace('.tsv', '[TSV]') for x in tsvfiles] + tsvfiles = [x.replace(TSV_DIR, '') for x in tsvfiles] + files += tsvfiles + return {'json_type':'config_list', 'data': files} + elif d['command'] == 'config-save': + name = d['data']['name'] + if '..' in name or '.json' in name or '/' in name: + return None + filename = '%s%s.json' % (CFG_DIR, d['data']['name']) + open(filename, 'w').write(json.dumps(d['data']['value'], indent=4, separators=[',',':'], sort_keys=True)) + return None + +def post_req(environ, start_response, postdata): + global my_input_q, my_output_q, my_recv_q, my_port + resp_msg = [] + try: + data = json.loads(postdata) + except: + sys.stderr.write('post_req: error processing input: %s:\n' % (postdata)) + traceback.print_exc(limit=None, file=sys.stderr) + sys.stderr.write('*** end traceback ***\n') + for d in data: + if d['command'].startswith('config-') or d['command'].startswith('rx-'): + resp = do_request(d) + if resp: + resp_msg.append(resp) + continue + if d['command'].startswith('settings-'): + msg = gr.message().make_from_string(json.dumps(d), -4, 0, 0) + else: + msg = gr.message().make_from_string(str(d['command']), -2, d['data'], 0) + if my_output_q.full_p(): + my_output_q.delete_head_nowait() # ignores result + if not my_output_q.full_p(): + my_output_q.insert_tail(msg) + time.sleep(0.2) + + while not my_recv_q.empty_p(): + msg = my_recv_q.delete_head() + if msg.type() == -4: + resp_msg.append(json.loads(msg.to_string())) + status = '200 OK' + content_type = 'application/json' + output = json.dumps(resp_msg) + return status, content_type, output + +def http_request(environ, start_response): + if environ['REQUEST_METHOD'] == 'GET': + status, content_type, output = static_file(environ, start_response) + elif environ['REQUEST_METHOD'] == 'POST': + postdata = environ['wsgi.input'].read() + status, content_type, output = post_req(environ, start_response, postdata) + else: + status = '200 OK' + content_type = 'text/plain' + output = status + sys.stderr.write('http_request: unexpected input %s\n' % environ['PATH_INFO']) + + response_headers = [('Content-type', content_type), + ('Content-Length', str(len(output)))] + start_response(status, response_headers) + + if sys.version[0] != '2': + if isinstance(output, str): + output = output.encode() + return [output] + +def application(environ, start_response): + failed = False + try: + result = http_request(environ, start_response) + except: + failed = True + sys.stderr.write('application: request failed:\n%s\n' % traceback.format_exc()) + sys.exit(1) + return result + +def process_qmsg(msg): + if my_recv_q.full_p(): + my_recv_q.delete_head_nowait() # ignores result + if my_recv_q.full_p(): + return + my_recv_q.insert_tail(msg) + +class http_server(object): + def __init__(self, input_q, output_q, endpoint, **kwds): + global my_input_q, my_output_q, my_recv_q, my_port + host, port = endpoint.split(':') + if my_port is not None: + raise AssertionError('this server is already active on port %s' % my_port) + my_input_q = input_q + my_output_q = output_q + my_port = int(port) + + my_recv_q = gr.msg_queue(10) + self.q_watcher = queue_watcher(my_input_q, process_qmsg) + + self.server = create_server(application, host=host, port=my_port) + + def run(self): + self.server.run() + +class queue_watcher(threading.Thread): + def __init__(self, msgq, callback, **kwds): + threading.Thread.__init__ (self, **kwds) + self.setDaemon(1) + self.msgq = msgq + self.callback = callback + self.keep_running = True + self.start() + + def run(self): + while(self.keep_running): + msg = self.msgq.delete_head() + self.callback(msg) + +class Backend(threading.Thread): + def __init__(self, options, input_q, output_q, init_config=None, **kwds): + threading.Thread.__init__ (self, **kwds) + self.setDaemon(1) + self.keep_running = True + self.rx_options = None + self.input_q = input_q + self.output_q = output_q + self.verbosity = options.verbosity + + self.zmq_context = zmq.Context() + self.zmq_port = options.zmq_port + + self.zmq_sub = self.zmq_context.socket(zmq.SUB) + self.zmq_sub.connect('tcp://localhost:%d' % self.zmq_port) + self.zmq_sub.setsockopt(zmq.SUBSCRIBE, '') + + self.zmq_pub = self.zmq_context.socket(zmq.PUB) + self.zmq_pub.sndhwm = 5 + self.zmq_pub.bind('tcp://*:%d' % (self.zmq_port+1)) + + self.start() + self.subproc = None + self.msg = None + + self.q_watcher = queue_watcher(self.input_q, self.process_msg) + + if init_config: + d = {'command': 'rx-start', 'data': init_config} + msg = gr.message().make_from_string(json.dumps(d), -4, 0, 0) + self.input_q.insert_tail(msg) + + def publish(self, msg): + t = msg.type() + s = msg.to_string() + a = msg.arg1() + self.zmq_pub.send(json.dumps({'command': s, 'data': a, 'msgtype': t})) + + def check_subproc(self): # return True if subprocess is active + if not self.subproc: + return False + rc = self.subproc.poll() + if rc is None: + return True + else: + self.subproc.wait() + self.subproc = None + return False + + def process_msg(self, msg): + def make_command(options, config_file): + trunked_ct = [True for x in options._js_config['channels'] if x['trunked']] + total_ct = [True for x in options._js_config['channels']] + if trunked_ct and len(trunked_ct) != len(total_ct): + self.msg = 'no suitable backend found for this configuration' + return None + if not trunked_ct: + self.backend = '%s/%s' % (os.getcwd(), 'multi_rx.py') + opts = [self.backend] + filename = '%s%s.json' % (CFG_DIR, config_file) + opts.append('--config-file') + opts.append(filename) + return opts + + types = {'costas-alpha': 'float', 'trunk-conf-file': 'str', 'demod-type': 'str', 'logfile-workers': 'int', 'decim-amt': 'int', 'wireshark-host': 'str', 'gain-mu': 'float', 'phase2-tdma': 'bool', 'seek': 'int', 'ifile': 'str', 'pause': 'bool', 'antenna': 'str', 'calibration': 'float', 'fine-tune': 'float', 'raw-symbols': 'str', 'audio-output': 'str', 'vocoder': 'bool', 'input': 'str', 'wireshark': 'bool', 'gains': 'str', 'args': 'str', 'sample-rate': 'int', 'terminal-type': 'str', 'gain': 'float', 'excess-bw': 'float', 'offset': 'float', 'audio-input': 'str', 'audio': 'bool', 'plot-mode': 'str', 'audio-if': 'bool', 'tone-detect': 'bool', 'frequency': 'int', 'freq-corr': 'float', 'hamlib-model': 'int', 'udp-player': 'bool', 'verbosity': 'int'} + self.backend = '%s/%s' % (os.getcwd(), 'rx.py') + opts = [self.backend] + for k in [ x for x in dir(options) if not x.startswith('_') ]: + kw = k.replace('_', '-') + val = getattr(options, k) + if kw not in types.keys(): + self.msg = 'make_command: unknown option: %s %s type %s' % (k, val, type(val)) + return None + elif types[kw] == 'str': + if val: + opts.append('--%s' % kw) + opts.append('%s' % (val)) + elif types[kw] == 'float': + opts.append('--%s' % kw) + if val: + opts.append('%f' % (val)) + else: + opts.append('%f' % (0)) + elif types[kw] == 'int': + opts.append('--%s' % kw) + if val: + opts.append('%d' % (val)) + else: + opts.append('%d' % (0)) + elif types[kw] == 'bool': + if val: + opts.append('--%s' % kw) + else: + self.msg = 'make_command: unknown2 option: %s %s type %s' % (k, val, type(val)) + return None + return opts + + msg = json.loads(msg.to_string()) + if msg['command'] == 'rx-start': + if self.check_subproc(): + self.msg = 'start command failed: subprocess pid %d already active' % self.subproc.pid + return + options = rx_options(msg['data']) + if getattr(options, '_js_config', None) is None: + self.msg = 'start command failed: rx_options: unable to initialize config=%s' % (msg['data']) + return + options.verbosity = self.verbosity + options.terminal_type = 'zmq:tcp:%d' % (self.zmq_port) + cmd = make_command(options, msg['data']) + if cmd: + self.subproc = subprocess.Popen(cmd) + elif msg['command'] == 'rx-stop': + if not self.check_subproc(): + self.msg = 'stop command failed: subprocess not active' + return + if msg['data'] == 'kill': + self.subproc.kill() + else: + self.subproc.terminate() + elif msg['command'] == 'rx-state': + d = {} + if self.check_subproc(): + d['rx-state'] = 'subprocess pid %d active' % self.subproc.pid + else: + d['rx-state'] = 'subprocess not active, last msg: %s' % self.msg + msg = gr.message().make_from_string(json.dumps(d), -4, 0, 0) + if not self.output_q.full_p(): + self.output_q.insert_tail(msg) + + def run(self): + while self.keep_running: + js = self.zmq_sub.recv() + if not self.keep_running: + break + msg = gr.message().make_from_string(js, -4, 0, 0) + if not self.output_q.full_p(): + self.output_q.insert_tail(msg) + +class rx_options(object): + def __init__(self, name): + def map_name(k): + return k.replace('-', '_') + + filename = '%s%s.json' % (CFG_DIR, name) + if not os.access(filename, os.R_OK): + sys.stderr.write('unable to access config file %s\n' % (filename)) + return + config = byteify(json.loads(open(filename).read())) + dev = [x for x in config['devices'] if x['active']][0] + if not dev: + return + chan = [x for x in config['channels'] if x['active']][0] + if not chan: + return + options = object() + for k in config['backend-rx'].keys(): + setattr(self, map_name(k), config['backend-rx'][k]) + for k in 'args frequency gains offset'.split(): + setattr(self, k, dev[k]) + self.demod_type = chan['demod_type'] + self.freq_corr = dev['ppm'] + self.sample_rate = dev['rate'] + self.plot_mode = chan['plot'] + self.phase2_tdma = chan['phase2_tdma'] + self.trunk_conf_file = filename + self._js_config = config + +def http_main(): + global my_backend + # command line argument parsing + parser = OptionParser() + parser.add_option("-c", "--config", type="string", default=None, help="config json name, without prefix/suffix") + parser.add_option("-e", "--endpoint", type="string", default="127.0.0.1:8080", help="address:port to listen on (use addr 0.0.0.0 to enable external clients)") + parser.add_option("-v", "--verbosity", type="int", default=0, help="message debug level") + parser.add_option("-p", "--pause", action="store_true", default=False, help="block on startup") + parser.add_option("-z", "--zmq-port", type="int", default=25000, help="backend sub port") + (options, args) = parser.parse_args() + + # wait for gdb + if options.pause: + print ('Ready for GDB to attach (pid = %d)' % (os.getpid(),)) + raw_input("Press 'Enter' to continue...") + + input_q = gr.msg_queue(20) + output_q = gr.msg_queue(20) + backend_input_q = gr.msg_queue(20) + backend_output_q = gr.msg_queue(20) + + my_backend = Backend(options, backend_input_q, backend_output_q, init_config=options.config) + server = http_server(input_q, output_q, options.endpoint) + q_watcher = queue_watcher(output_q, lambda msg : my_backend.publish(msg)) + backend_q_watcher = queue_watcher(backend_output_q, lambda msg : process_qmsg(msg)) + + server.run() + +if __name__ == '__main__': + http_main() diff --git a/op25/gr-op25_repeater/apps/multi_rx.py b/op25/gr-op25_repeater/apps/multi_rx.py index a7dde1b..231bfcf 100755 --- a/op25/gr-op25_repeater/apps/multi_rx.py +++ b/op25/gr-op25_repeater/apps/multi_rx.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017 Max H. Parke KA1RBI +# Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Max H. Parke KA1RBI # # This file is part of OP25 # @@ -51,6 +51,8 @@ _def_symbol_rate = 4800 # def byteify(input): # thx so + if sys.version[0] != '2': # hack, must be a better way + return input if isinstance(input, dict): return {byteify(key): byteify(value) for key, value in input.iteritems()} @@ -237,7 +239,7 @@ class rx_main(object): # wait for gdb if options.pause: - print 'Ready for GDB to attach (pid = %d)' % (os.getpid(),) + print ('Ready for GDB to attach (pid = %d)' % (os.getpid(),)) raw_input("Press 'Enter' to continue...") if options.config_file == '-': diff --git a/op25/gr-op25_repeater/apps/p25_decoder.py b/op25/gr-op25_repeater/apps/p25_decoder.py index dcbe17f..54e843d 100644 --- a/op25/gr-op25_repeater/apps/p25_decoder.py +++ b/op25/gr-op25_repeater/apps/p25_decoder.py @@ -67,7 +67,7 @@ class p25_decoder_sink_b(gr.hier_block2): @type debug: int """ - gr.hier_block2.__init__(self, "p25_demod_c", + gr.hier_block2.__init__(self, "p25_demod_c", gr.io_signature(1, 1, gr.sizeof_char), # Input signature gr.io_signature(0, 0, 0)) # Output signature @@ -99,7 +99,7 @@ class p25_decoder_sink_b(gr.hier_block2): num_decoders = 1 if num_ambe > 1: num_decoders += num_ambe - 1 - for slot in xrange(num_decoders): + for slot in range(num_decoders): self.p25_decoders.append(op25_repeater.p25_frame_assembler(wireshark_host, udp_port, debug, do_imbe, do_output, do_msgq, msgq, do_audio_output, do_phase2_tdma)) self.p25_decoders[slot].set_slotid(slot) diff --git a/op25/gr-op25_repeater/apps/p25_demodulator.py b/op25/gr-op25_repeater/apps/p25_demodulator.py index 09986a5..110e230 100644 --- a/op25/gr-op25_repeater/apps/p25_demodulator.py +++ b/op25/gr-op25_repeater/apps/p25_demodulator.py @@ -62,16 +62,16 @@ def get_decim(speed): for i_f in if_freqs: if s % i_f != 0: continue - q = int(s / i_f) + q = s // i_f if q & 1: continue if q >= 40 and q & 3 == 0: - decim = q/4 + decim = q//4 decim2 = 4 else: - decim = q/2 + decim = q//2 decim2 = 2 - return int(decim), int(decim2) + return decim, decim2 return None class p25_demod_base(gr.hier_block2): @@ -92,7 +92,7 @@ class p25_demod_base(gr.hier_block2): self.baseband_amp = blocks.multiply_const_ff(_def_bb_gain) coeffs = op25_c4fm_mod.c4fm_taps(sample_rate=self.if_rate, span=9, generator=op25_c4fm_mod.transfer_function_rx).generate() - sps = self.if_rate / self.symbol_rate + sps = self.if_rate // self.symbol_rate if filter_type == 'rrc': ntaps = 7 * sps if ntaps & 1 == 0: diff --git a/op25/gr-op25_repeater/apps/rx.py b/op25/gr-op25_repeater/apps/rx.py index 995f848..98112c8 100755 --- a/op25/gr-op25_repeater/apps/rx.py +++ b/op25/gr-op25_repeater/apps/rx.py @@ -2,7 +2,7 @@ # Copyright 2008-2011 Steve Glass # -# Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Max H. Parke KA1RBI +# Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Max H. Parke KA1RBI # # Copyright 2003,2004,2005,2006 Free Software Foundation, Inc. # (from radiorausch) @@ -114,27 +114,27 @@ class p25_rx_block (gr.top_block): import osmosdr self.src = osmosdr.source(options.args) except Exception: - print "osmosdr source_c creation failure" + print ("osmosdr source_c creation failure") ignore = True if any(x in options.args.lower() for x in ['rtl', 'airspy', 'hackrf', 'uhd']): - #print "'rtl' has been found in options.args (%s)" % (options.args) + #print ("'rtl' has been found in options.args (%s)" % (options.args)) self.rtl_found = True gain_names = self.src.get_gain_names() for name in gain_names: - range = self.src.get_gain_range(name) - print "gain: name: %s range: start %d stop %d step %d" % (name, range[0].start(), range[0].stop(), range[0].step()) + range1 = self.src.get_gain_range(name) + print ("gain: name: %s range: start %d stop %d step %d" % (name, range1[0].start(), range1[0].stop(), range1[0].step())) if options.gains: for tup in options.gains.split(","): name, gain = tup.split(":") gain = int(gain) - print "setting gain %s to %d" % (name, gain) + print ("setting gain %s to %d" % (name, gain)) self.src.set_gain(gain, name) rates = self.src.get_sample_rates() try: - print 'supported sample rates %d-%d step %d' % (rates.start(), rates.stop(), rates.step()) + print ('supported sample rates %d-%d step %d' % (rates.start(), rates.stop(), rates.step())) except: pass # ignore @@ -166,7 +166,7 @@ class p25_rx_block (gr.top_block): self.constellation_scope_connected = False - for i in xrange(len(speeds)): + for i in range(len(speeds)): if speeds[i] == _default_speed: self.current_speed = i self.default_speed_idx = i @@ -176,7 +176,7 @@ class p25_rx_block (gr.top_block): # wait for gdb if options.pause: - print 'Ready for GDB to attach (pid = %d)' % (os.getpid(),) + print ('Ready for GDB to attach (pid = %d)' % (os.getpid(),)) raw_input("Press 'Enter' to continue...") self.input_q = gr.msg_queue(10) @@ -298,7 +298,7 @@ class p25_rx_block (gr.top_block): if self.options.phase2_tdma: num_ambe = 2 if self.options.logfile_workers: - for i in xrange(self.options.logfile_workers): + for i in range(self.options.logfile_workers): demod = p25_demodulator.p25_demod_cb(input_rate=capture_rate, demod_type=self.options.demod_type, offset=self.options.offset) @@ -458,7 +458,7 @@ class p25_rx_block (gr.top_block): if self.rtl_found: self.src.set_gain(gain, 'LNA') if self.options.verbosity: - print 'RTL Gain of %d set to: %.1f' % (gain, self.src.get_gain('LNA')) + print ('RTL Gain of %d set to: %.1f' % (gain, self.src.get_gain('LNA'))) else: if self.baseband_input: f = 1.0 @@ -467,7 +467,7 @@ class p25_rx_block (gr.top_block): self.demod.set_baseband_gain(float(gain) * f) def set_audio_scaler(self, vol): - #print 'audio scaler: %f' % ((1 / 32768.0) * (vol * 0.1)) + #print ('audio scaler: %f' % ((1 / 32768.0) * (vol * 0.1))) if hasattr(self.decoder, 'set_scaler_k'): self.decoder.set_scaler_k((1 / 32768.0) * (vol * 0.1)) @@ -571,7 +571,7 @@ class p25_rx_block (gr.top_block): if file_seek > 0: rc = ifile.seek(file_seek*1024, gr.SEEK_SET) assert rc == True - #print "seek: %d, rc = %d" % (file_seek, rc) + #print ("seek: %d, rc = %d" % (file_seek, rc)) throttle = blocks.throttle(gr.sizeof_gr_complex, speed) self.source = blocks.multiply_const_cc(gain) self.connect(ifile, throttle, self.source) @@ -645,6 +645,9 @@ class p25_rx_block (gr.top_block): if t == -4: d = json.loads(s) s = d['command'] + if type(s) is not str and isinstance(s, bytes): + # should only get here if python3 + s = s.decode() if s == 'quit': return True elif s == 'update': self.freq_update() diff --git a/op25/gr-op25_repeater/apps/tdma/bit_utils.py b/op25/gr-op25_repeater/apps/tdma/bit_utils.py index df667dd..7238807 100644 --- a/op25/gr-op25_repeater/apps/tdma/bit_utils.py +++ b/op25/gr-op25_repeater/apps/tdma/bit_utils.py @@ -22,7 +22,7 @@ import numpy as np def rev_int(n,l): j=0 - for i in xrange(l): + for i in range(l): b=n&1 n=n>>1 j = (j << 1) | b @@ -30,7 +30,7 @@ def rev_int(n,l): def bits_to_dibits(bits): d = [] - for i in xrange(len(bits)>>1): + for i in range(len(bits)>>1): d.append((bits[i*2]<<1) + bits[i*2+1]) return d @@ -43,14 +43,14 @@ def dibits_to_bits(dibits): def mk_array(n, l): a = [] - for i in xrange(0,l): + for i in range(0,l): a.insert(0, n & 1) n = n >> 1 return np.array(a) def mk_int(a): - res= 0L - for i in xrange(0, len(a)): + res= 0 + for i in range(0, len(a)): res = res * 2 res = res + (a[i] & 1) return res @@ -61,7 +61,7 @@ def mk_str(a): def check_l(a,b): ans = 0 assert len(a) == len(b) - for i in xrange(len(a)): + for i in range(len(a)): if (a[i] == b[i]): ans += 1 return ans @@ -77,7 +77,7 @@ def fixup(a): return res def find_sym(pattern, symbols): - for i in xrange(0, len(symbols)-len(pattern)): + for i in range(0, len(symbols)-len(pattern)): chunk = symbols[i : i + len(pattern)] if chunk == pattern: return i diff --git a/op25/gr-op25_repeater/apps/tdma/lfsr.py b/op25/gr-op25_repeater/apps/tdma/lfsr.py index 6a7d8ac..3d6e875 100644 --- a/op25/gr-op25_repeater/apps/tdma/lfsr.py +++ b/op25/gr-op25_repeater/apps/tdma/lfsr.py @@ -25,54 +25,54 @@ class p25p2_lfsr(object): def __init__(self,nac,sysid,wacn): xorbits = self.mk_xor_bits(nac,sysid,wacn) #self.xorsyms = np.zeros(len(xorbits)/2) - self.xorsyms = [0] * (len(xorbits)/2) - for i in xrange(len(self.xorsyms)): + self.xorsyms = [0] * (len(xorbits)//2) + for i in range(len(self.xorsyms)): self.xorsyms[i] = (xorbits[i*2] << 1) + xorbits[i*2+1] self.xor_chars = ''.join([chr(c) for c in self.xorsyms]) def asm_reg(self,s1,s2,s3,s4,s5,s6): - s1 = s1 & 0xfL - s2 = s2 & 0x1fL - s3 = s3 & 0x3fL - s4 = s4 & 0x1fL - s5 = s5 & 0x3fffL - s6 = s6 & 0x3ffL + s1 = s1 & 0xf + s2 = s2 & 0x1f + s3 = s3 & 0x3f + s4 = s4 & 0x1f + s5 = s5 & 0x3fff + s6 = s6 & 0x3ff return (s1<<40)+(s2<<35)+(s3<<29)+(s4<<24)+(s5<<10)+s6 def disasm_reg(self,r): - s1 = (r>>40) & 0xfL - s2 = (r>>35) & 0x1fL - s3 = (r>>29) & 0x3fL - s4 = (r>>24) & 0x1fL - s5 = (r>>10) & 0x3fffL - s6 = r & 0x3ffL + s1 = (r>>40) & 0xf + s2 = (r>>35) & 0x1f + s3 = (r>>29) & 0x3f + s4 = (r>>24) & 0x1f + s5 = (r>>10) & 0x3fff + s6 = r & 0x3ff return s1,s2,s3,s4,s5,s6 def cyc_reg(self, reg): s1,s2,s3,s4,s5,s6 = self.disasm_reg(reg) - cy1 = (s1 >> 3) & 1L - cy2 = (s2 >> 4) & 1L - cy3 = (s3 >> 5) & 1L - cy4 = (s4 >> 4) & 1L - cy5 = (s5 >> 13) & 1L - cy6 = (s6 >> 9) & 1L + cy1 = (s1 >> 3) & 1 + cy2 = (s2 >> 4) & 1 + cy3 = (s3 >> 5) & 1 + cy4 = (s4 >> 4) & 1 + cy5 = (s5 >> 13) & 1 + cy6 = (s6 >> 9) & 1 x1 = cy1 ^ cy2 x2 = cy1 ^ cy3 x3 = cy1 ^ cy4 x4 = cy1 ^ cy5 x5 = cy1 ^ cy6 - s1 = (s1 << 1) & 0xfL - s2 = (s2 << 1) & 0x1fL - s3 = (s3 << 1) & 0x3fL - s4 = (s4 << 1) & 0x1fL - s5 = (s5 << 1) & 0x3fffL - s6 = (s6 << 1) & 0x3ffL - s1 = s1 | (x1 & 1L) - s2 = s2 | (x2 & 1L) - s3 = s3 | (x3 & 1L) - s4 = s4 | (x4 & 1L) - s5 = s5 | (x5 & 1L) - s6 = s6 | (cy1 & 1L) + s1 = (s1 << 1) & 0xf + s2 = (s2 << 1) & 0x1f + s3 = (s3 << 1) & 0x3f + s4 = (s4 << 1) & 0x1f + s5 = (s5 << 1) & 0x3fff + s6 = (s6 << 1) & 0x3ff + s1 = s1 | (x1 & 1) + s2 = s2 | (x2 & 1) + s3 = s3 | (x3 & 1) + s4 = s4 | (x4 & 1) + s5 = s5 | (x5 & 1) + s6 = s6 | (cy1 & 1) return self.asm_reg(s1,s2,s3,s4,s5,s6) def mk_xor_bits(self, nac,sysid,wacn): @@ -82,7 +82,7 @@ class p25p2_lfsr(object): reg = mk_int(np.dot(reg,M)) s = [] - for i in xrange(4320): + for i in range(4320): s.append((reg >> 43) & 1) reg = self.cyc_reg(reg) diff --git a/op25/gr-op25_repeater/apps/terminal.py b/op25/gr-op25_repeater/apps/terminal.py index 2814c9c..f80d4dc 100755 --- a/op25/gr-op25_repeater/apps/terminal.py +++ b/op25/gr-op25_repeater/apps/terminal.py @@ -68,7 +68,7 @@ class curses_terminal(threading.Thread): self.maxy, self.maxx = self.stdscr.getmaxyx() if (self.maxy < 6) or (self.maxx < 60): sys.stderr.write("Terminal window too small! Minimum size [70 x 6], actual [%d x %d]\n" % (self.maxx, self.maxy)) - print "Terminal window too small! Minimum size [70 x 6], actual [%d x %d]\n" % (self.maxx, self.maxy) + print ("Terminal window too small! Minimum size [70 x 6], actual [%d x %d]\n" % (self.maxx, self.maxy)) self.keep_running = False return @@ -157,7 +157,7 @@ class curses_terminal(threading.Thread): if c in COMMANDS.keys(): self.send_command(COMMANDS[c], 0) elif c == ord('q'): - return True + return True elif c == ord('t'): if self.current_nac: self.send_command('add_default_config', int(self.current_nac)) @@ -220,7 +220,7 @@ class curses_terminal(threading.Thread): self.top_bar.addstr(0, 0, s) self.top_bar.refresh() self.freq_list.erase() - for i in xrange(len(freqs)): + for i in range(len(freqs)): if i > (self.maxy - 6): break s=msg[current_nac]['frequencies'][freqs[i]] @@ -346,7 +346,7 @@ class zeromq_terminal(threading.Thread): class http_terminal(threading.Thread): def __init__(self, input_q, output_q, endpoint, **kwds): - from http import http_server + from http_server import http_server threading.Thread.__init__ (self, **kwds) self.setDaemon(1) diff --git a/op25/gr-op25_repeater/apps/trunking.py b/op25/gr-op25_repeater/apps/trunking.py index 1377b70..fc5b4cb 100644 --- a/op25/gr-op25_repeater/apps/trunking.py +++ b/op25/gr-op25_repeater/apps/trunking.py @@ -41,6 +41,16 @@ def crc16(dat,len): # slow version crc = crc ^ 0xffff return crc +def get_ordinals(s): + t = 0 + if type(s) is not str and isinstance(s, bytes): + for c in s: + t = (t << 8) + c + else: + for c in s: + t = (t << 8) + ord(c) + return t + class trunked_system (object): def __init__(self, debug=0, config=None): self.debug = debug @@ -104,7 +114,7 @@ class trunked_system (object): d['rxchan'] = self.rfss_chan d['txchan'] = self.rfss_txchan d['wacn'] = self.ns_wacn - d['secondary'] = self.secondary.keys() + d['secondary'] = [k for k in self.secondary.keys()] d['tsbks'] = self.stats['tsbks'] d['frequencies'] = {} d['frequency_data'] = {} @@ -120,7 +130,7 @@ class trunked_system (object): d['frequencies'][f] = 'voice frequency %f tgid(s) %s %4.1fs ago count %d' % (f / 1000000.0, tgs, t - self.voice_frequencies[f]['time'], self.voice_frequencies[f]['counter']) d['frequency_data'][f] = {'tgids': self.voice_frequencies[f]['tgid'], 'last_activity': '%7.1f' % (t - self.voice_frequencies[f]['time']), 'counter': self.voice_frequencies[f]['counter']} - d['adjacent_data'] = self.adjacent_data + d['adjacent_data'] = self.adjacent_data return json.dumps(d) def to_string(self): @@ -608,7 +618,7 @@ class rx_ctl (object): self.build_config_json(conf_file) else: self.build_config(conf_file) - self.nacs = self.configs.keys() + self.nacs = [int (x) for x in self.configs.keys()] self.current_nac = self.find_next_tsys() self.current_state = self.states.CC @@ -755,10 +765,10 @@ class rx_ctl (object): return s def process_qmsg(self, msg): - type = msg.type() + mtype = msg.type() updated = 0 curr_time = time.time() - if type == -3: # P25 call signalling data + if mtype == -3: # P25 call signalling data if self.debug > 10: sys.stderr.write("%f process_qmsg: P25 info: %s\n" % (time.time(), msg.to_string())) js = json.loads(msg.to_string()) @@ -777,55 +787,50 @@ class rx_ctl (object): if 'keyid' in js.keys(): tsys.current_keyid = js['keyid'] return - elif type == -2: # request from gui + elif mtype == -2: # request from gui cmd = msg.to_string() if self.debug > 10: sys.stderr.write('process_qmsg: command: %s\n' % cmd) self.update_state(cmd, curr_time) return - elif type == -1: # timeout + elif mtype == -1: # timeout if self.debug > 10: sys.stderr.write('%f process_data_unit timeout\n' % time.time()) self.update_state('timeout', curr_time) if self.logfile_workers: self.logging_scheduler(curr_time) return - elif type < 0: - sys.stderr.write('unknown message type %d\n' % (type)) + elif mtype < 0: + sys.stderr.write('unknown message type %d\n' % (mtype)) return s = msg.to_string() # nac is always 1st two bytes - nac = (ord(s[0]) << 8) + ord(s[1]) + nac = get_ordinals(s[:2]) #assert nac != 0xffff # FIXME: uncomment this after any remaining usages of 0xffff removed if nac == 0xffff: - if (type != 7) and (type != 12): # TDMA duid (end of call etc) - self.update_state('tdma_duid%d' % type, curr_time) + if (mtype != 7) and (mtype != 12): # TDMA duid (end of call etc) + self.update_state('tdma_duid%d' % mtype, curr_time) return else: # voice channel derived TSBK or MBT PDU nac = self.current_nac s = s[2:] if self.debug > 10: - sys.stderr.write('nac %x type %d at %f state %d len %d\n' %(nac, type, time.time(), self.current_state, len(s))) - if (type == 7 or type == 12) and nac not in self.trunked_systems: + sys.stderr.write('nac %x type %d at %f state %d len %d\n' %(nac, mtype, time.time(), self.current_state, len(s))) + if (mtype == 7 or mtype == 12) and nac not in self.trunked_systems: if not self.configs: # TODO: allow whitelist/blacklist rather than blind automatic-add self.add_trunked_system(nac) else: sys.stderr.write("%f NAC %x not configured\n" % (time.time(), nac)) return - if type == 7: # trunk: TSBK - t = 0 - for c in s: - t = (t << 8) + ord(c) + if mtype == 7: # trunk: TSBK + t = get_ordinals(s) updated += self.trunked_systems[nac].decode_tsbk(t) - elif type == 12: # trunk: MBT + elif mtype == 12: # trunk: MBT s1 = s[:10] # header without crc s2 = s[12:] - header = mbt_data = 0 - for c in s1: - header = (header << 8) + ord(c) - for c in s2: - mbt_data = (mbt_data << 8) + ord(c) + header = get_ordinals(s1) + mbt_data = get_ordinals(s2) fmt = (header >> 72) & 0x1f sap = (header >> 64) & 0x3f @@ -835,12 +840,15 @@ class rx_ctl (object): opcode = (header >> 16) & 0x3f if self.debug > 10: - sys.stderr.write('type %d at %f state %d len %d/%d opcode %x [%x/%x]\n' %(type, time.time(), self.current_state, len(s1), len(s2), opcode, header,mbt_data)) + sys.stderr.write('type %d at %f state %d len %d/%d opcode %x [%x/%x]\n' %(mtype, time.time(), self.current_state, len(s1), len(s2), opcode, header,mbt_data)) updated += self.trunked_systems[nac].decode_mbt_data(opcode, src, header << 16, mbt_data << 32) if nac != self.current_nac: if self.debug > 10: # this is occasionally expected if cycling between different tsys - sys.stderr.write("%f received NAC %x does not match expected NAC %x\n" % (time.time(), nac, self.current_nac)) + cnac = self.current_nac + if cnac is None: + cnac = 0 + sys.stderr.write("%f received NAC %x does not match expected NAC %x\n" % (time.time(), nac, cnac)) return if self.logfile_workers: @@ -850,7 +858,7 @@ class rx_ctl (object): if updated: self.update_state('update', curr_time) else: - self.update_state('duid%d' % type, curr_time) + self.update_state('duid%d' % mtype, curr_time) def find_available_worker(self): for worker in self.logfile_workers: @@ -962,6 +970,9 @@ class rx_ctl (object): if not self.configs: return # run in "manual mode" if no conf + if type(command) is not str and isinstance(command, bytes): + command = command.decode() + nac = self.current_nac tsys = self.trunked_systems[nac] @@ -1064,7 +1075,7 @@ class rx_ctl (object): self.tgid_hold = self.current_tgid self.tgid_hold_until = curr_time + 86400 * 10000 self.hold_mode = True - print 'set hold until %f' % self.tgid_hold_until + print ('set hold until %f' % self.tgid_hold_until) elif command == 'unset_hold': self.last_command = {'command': command, 'time': curr_time} if self.current_tgid: diff --git a/op25/gr-op25_repeater/apps/tsvfile.py b/op25/gr-op25_repeater/apps/tsvfile.py index e87bd63..77f74b5 100644 --- a/op25/gr-op25_repeater/apps/tsvfile.py +++ b/op25/gr-op25_repeater/apps/tsvfile.py @@ -42,14 +42,14 @@ def get_int_dict(s): v = v.split("\t",1) # split on tab try: v0 = int(v[0]) # first parameter is tgid or start of tgid range - v1 = v0 - if (len(v) > 1) and (int(v[1]) > v0): # second parameter if present is end of tgid range + v1 = v0 + if (len(v) > 1) and (int(v[1]) > v0): # second parameter if present is end of tgid range v1 = int(v[1]) - - for tg in range(v0, (v1 + 1)): - if tg not in d: # is this a new tg? - d[tg] = [] # if so, add to dict (key only, value null) - sys.stderr.write('added talkgroup %d from %s\n' % (tg,s)) + + for tg in range(v0, (v1 + 1)): + if tg not in d: # is this a new tg? + d[tg] = [] # if so, add to dict (key only, value null) + sys.stderr.write('added talkgroup %d from %s\n' % (tg,s)) except (IndexError, ValueError) as ex: continue @@ -62,7 +62,7 @@ def utf_ascii(ustr): def load_tsv(tsv_filename): hdrmap = [] configs = {} - with open(tsv_filename, 'rb') as csvfile: + with open(tsv_filename, 'r') as csvfile: sreader = csv.reader(csvfile, delimiter='\t', quotechar='"', quoting=csv.QUOTE_ALL) for row in sreader: if not hdrmap: @@ -73,7 +73,7 @@ def load_tsv(tsv_filename): hdrmap.append(hdr) continue fields = {} - for i in xrange(len(row)): + for i in range(len(row)): if row[i]: fields[hdrmap[i]] = row[i] if hdrmap[i] != 'sysname': @@ -99,12 +99,12 @@ def make_config(configs): result_config[nac][k] = get_int_dict(configs[nac][k]) if 'tgid_tags_file' in configs[nac]: import csv - with open(configs[nac]['tgid_tags_file'], 'rb') as csvfile: + with open(configs[nac]['tgid_tags_file'], 'r') as csvfile: sreader = csv.reader(csvfile, delimiter='\t', quotechar='"', quoting=csv.QUOTE_ALL) for row in sreader: try: tgid = int(row[0]) - txt = utf_ascii(row[1]) + txt = row[1] except (IndexError, ValueError) as ex: continue if len(row) >= 3: @@ -122,7 +122,7 @@ def make_config(configs): def main(): import json result = make_config(load_tsv(sys.argv[1])) - print json.dumps(result, indent=4, separators=[',',':'], sort_keys=True) + print (json.dumps(result, indent=4, separators=[',',':'], sort_keys=True)) if __name__ == '__main__': main() diff --git a/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py b/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py index 5c99017..d210914 100755 --- a/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py +++ b/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py @@ -54,7 +54,7 @@ def transfer_function_rx(symbol_rate=_def_symbol_rate): # Specs undefined above 2,880 Hz. It would be nice to have a sharper # rolloff, but this filter is cheap enough.... xfer = [] # frequency domain transfer function - for f in xrange(0,symbol_rate): + for f in range(0,symbol_rate): # D(f) t = pi * f / symbol_rate if t < 1e-6: @@ -66,7 +66,7 @@ def transfer_function_rx(symbol_rate=_def_symbol_rate): def transfer_function_tx(symbol_rate=_def_symbol_rate): xfer = [] # frequency domain transfer function - for f in xrange(0, 2881): # specs cover 0 - 2,880 Hz + for f in range(0, 2881): # specs cover 0 - 2,880 Hz # H(f) if f < 1920: hf = 1.0 @@ -84,7 +84,7 @@ def transfer_function_tx(symbol_rate=_def_symbol_rate): def transfer_function_dmr(symbol_rate=_def_symbol_rate): xfer = [] # frequency domain transfer function - for f in xrange(0, 2881): # specs cover 0 - 2,880 Hz + for f in range(0, 2881): # specs cover 0 - 2,880 Hz if f < 1920: hf = 1.0 else: @@ -102,7 +102,7 @@ def transfer_function_nxdn(symbol_rate=_def_symbol_rate): fh = int(0.5+(1+a)/(2*T)) xfer = [] - for f in xrange(0, symbol_rate): + for f in range(0, symbol_rate): if f < fl: hf = 1.0 elif f >= fl and f <= fh: @@ -132,7 +132,7 @@ class c4fm_taps(object): def generate(self): impulse_response = np.fft.fftshift(np.fft.irfft(self.generator(symbol_rate=self.symbol_rate), self.sample_rate)) - start = np.argmax(impulse_response) - (self.ntaps-1) / 2 + start = np.argmax(impulse_response) - (self.ntaps-1) // 2 coeffs = impulse_response[start: start+self.ntaps] gain = self.filter_gain / sum(coeffs) return coeffs * gain @@ -149,7 +149,7 @@ class gmsk_taps(object): self.bt = bt self.samples_per_symbol = self.sample_rate / self.symbol_rate - self.ntaps = self.span * self.samples_per_symbol + self.ntaps = self.span * self.samples_per_symbol def generate(self): # from gnuradio gr-digital/python/digital/gmsk.py @@ -201,7 +201,7 @@ class p25_mod_bf(gr.hier_block2): @type debug: bool """ - gr.hier_block2.__init__(self, "p25_c4fm_mod_bf", + gr.hier_block2.__init__(self, "p25_c4fm_mod_bf", gr.io_signature(1, 1, gr.sizeof_char), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature @@ -249,11 +249,11 @@ class p25_mod_bf(gr.hier_block2): self.connect(self.filter, self) def _print_verbage(self): - print "\nModulator:" - print "interpolation: %d decimation: %d" %(self._interp_factor, self._decimation) + print ("\nModulator:") + print ("interpolation: %d decimation: %d" %(self._interp_factor, self._decimation)) def _setup_logging(self): - print "Modulation logging turned on." + print ("Modulation logging turned on.") self.connect(self.C2S, gr.file_sink(gr.sizeof_float, "tx_chunks2symbols.dat")) self.connect(self.polarity,