initial hacks for python 2/3 agnosticism
This commit is contained in:
parent
3de2d3e440
commit
b91039ecd0
|
@ -131,13 +131,13 @@ class wrap_gp(object):
|
||||||
s += 'e\n'
|
s += 'e\n'
|
||||||
self.buf=self.buf[self.sps:]
|
self.buf=self.buf[self.sps:]
|
||||||
plots.append('"-" with lines')
|
plots.append('"-" with lines')
|
||||||
elif mode == 'constellation':
|
elif mode == 'constellation':
|
||||||
plot_size = (240,240)
|
plot_size = (240,240)
|
||||||
self.buf = self.buf[:100]
|
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 += '%f\t%f\n' % (degrees(np.angle(b)), limit(np.abs(b),1.0))
|
||||||
s += 'e\n'
|
s += 'e\n'
|
||||||
plots.append('"-" with points')
|
plots.append('"-" with points')
|
||||||
for b in self.buf:
|
for b in self.buf:
|
||||||
#s += '%f\t%f\n' % (b.real, b.imag)
|
#s += '%f\t%f\n' % (b.real, b.imag)
|
||||||
s += '%f\t%f\n' % (degrees(np.angle(b)), limit(np.abs(b),1.0))
|
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
|
tune_freq = (self.center_freq - self.relative_freq) / 1e6
|
||||||
if self.center_freq and self.width:
|
if self.center_freq and self.width:
|
||||||
self.freqs = ((self.freqs * self.width) + self.center_freq + self.offset_freq) / 1e6
|
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':
|
if mode == 'fft':
|
||||||
self.avg_pwr[i] = ((1.0 - FFT_AVG) * self.avg_pwr[i]) + (FFT_AVG * np.abs(self.ffts[i]))
|
self.avg_pwr[i] = ((1.0 - FFT_AVG) * self.avg_pwr[i]) + (FFT_AVG * np.abs(self.ffts[i]))
|
||||||
else:
|
else:
|
||||||
|
@ -223,37 +223,39 @@ class wrap_gp(object):
|
||||||
h += 'set format ""\n'
|
h += 'set format ""\n'
|
||||||
h += 'set style line 11 lt 1 lw 2 pt 2 ps 2\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':
|
elif mode == 'eye':
|
||||||
h+= background
|
h+= background
|
||||||
h+= 'set yrange [-4:4]\n'
|
h+= 'set yrange [-4:4]\n'
|
||||||
h+= 'set title "Datascope"\n'
|
h+= 'set title "Datascope"\n'
|
||||||
elif mode == 'symbol':
|
elif mode == 'symbol':
|
||||||
h+= background
|
h+= background
|
||||||
h+= 'set yrange [-4:4]\n'
|
h+= 'set yrange [-4:4]\n'
|
||||||
h+= 'set title "Symbol"\n'
|
h+= 'set title "Symbol"\n'
|
||||||
elif mode == 'fft' or mode == 'mixer':
|
elif mode == 'fft' or mode == 'mixer':
|
||||||
h+= 'unset arrow; unset title\n'
|
h+= 'unset arrow; unset title\n'
|
||||||
h+= 'set xrange [%f:%f]\n' % (self.freqs[0], self.freqs[len(self.freqs)-1])
|
h+= 'set xrange [%f:%f]\n' % (self.freqs[0], self.freqs[len(self.freqs)-1])
|
||||||
h+= 'set xlabel "Frequency"\n'
|
h+= 'set xlabel "Frequency"\n'
|
||||||
h+= 'set ylabel "Power(dB)"\n'
|
h+= 'set ylabel "Power(dB)"\n'
|
||||||
h+= 'set grid\n'
|
h+= 'set grid\n'
|
||||||
h+= 'set yrange [-100:0]\n'
|
h+= 'set yrange [-100:0]\n'
|
||||||
if mode == 'mixer': # mixer
|
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
|
else: # fft
|
||||||
h+= 'set title "Spectrum"\n'
|
h+= 'set title "Spectrum"\n'
|
||||||
if self.center_freq:
|
if self.center_freq:
|
||||||
arrow_pos = (self.center_freq - self.relative_freq) / 1e6
|
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 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
|
h+= 'set title "Spectrum: tuned to %f Mhz"\n' % arrow_pos
|
||||||
elif mode == 'float':
|
elif mode == 'float':
|
||||||
h+= 'set yrange [-2:2]\n'
|
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)
|
dat = '%s%splot %s\n%s' % (h0, h, ','.join(plots), s)
|
||||||
if self.logfile is not None:
|
if sys.version[0] != '2':
|
||||||
with open(self.logfile, 'a') as fd:
|
dat = bytes(dat, 'utf8')
|
||||||
fd.write(dat)
|
if self.logfile is not None:
|
||||||
|
with open(self.logfile, 'a') as fd:
|
||||||
|
fd.write(dat)
|
||||||
self.gp.poll()
|
self.gp.poll()
|
||||||
if self.gp.returncode is None: # make sure gnuplot is still running
|
if self.gp.returncode is None: # make sure gnuplot is still running
|
||||||
try:
|
try:
|
||||||
|
@ -293,7 +295,7 @@ class eye_sink_f(gr.sync_block):
|
||||||
|
|
||||||
def work(self, input_items, output_items):
|
def work(self, input_items, output_items):
|
||||||
in0 = input_items[0]
|
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])
|
return consumed ### len(input_items[0])
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
|
@ -312,7 +314,7 @@ class constellation_sink_c(gr.sync_block):
|
||||||
|
|
||||||
def work(self, input_items, output_items):
|
def work(self, input_items, output_items):
|
||||||
in0 = input_items[0]
|
in0 = input_items[0]
|
||||||
self.gnuplot.plot(in0, 1000, mode='constellation')
|
self.gnuplot.plot(in0, 1000, mode='constellation')
|
||||||
return len(input_items[0])
|
return len(input_items[0])
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
|
@ -335,7 +337,7 @@ class fft_sink_c(gr.sync_block):
|
||||||
if self.skip >= 50:
|
if self.skip >= 50:
|
||||||
self.skip = 0
|
self.skip = 0
|
||||||
in0 = input_items[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])
|
return len(input_items[0])
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
|
@ -343,7 +345,7 @@ class fft_sink_c(gr.sync_block):
|
||||||
|
|
||||||
def set_center_freq(self, f):
|
def set_center_freq(self, f):
|
||||||
self.gnuplot.set_center_freq(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):
|
def set_relative_freq(self, f):
|
||||||
self.gnuplot.set_relative_freq(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):
|
def work(self, input_items, output_items):
|
||||||
in0 = input_items[0]
|
in0 = input_items[0]
|
||||||
self.gnuplot.plot(in0, 2400, mode='symbol')
|
self.gnuplot.plot(in0, 2400, mode='symbol')
|
||||||
return len(input_items[0])
|
return len(input_items[0])
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
|
|
|
@ -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()
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/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
|
# This file is part of OP25
|
||||||
#
|
#
|
||||||
|
@ -51,6 +51,8 @@ _def_symbol_rate = 4800
|
||||||
#
|
#
|
||||||
|
|
||||||
def byteify(input): # thx so
|
def byteify(input): # thx so
|
||||||
|
if sys.version[0] != '2': # hack, must be a better way
|
||||||
|
return input
|
||||||
if isinstance(input, dict):
|
if isinstance(input, dict):
|
||||||
return {byteify(key): byteify(value)
|
return {byteify(key): byteify(value)
|
||||||
for key, value in input.iteritems()}
|
for key, value in input.iteritems()}
|
||||||
|
@ -237,7 +239,7 @@ class rx_main(object):
|
||||||
|
|
||||||
# wait for gdb
|
# wait for gdb
|
||||||
if options.pause:
|
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...")
|
raw_input("Press 'Enter' to continue...")
|
||||||
|
|
||||||
if options.config_file == '-':
|
if options.config_file == '-':
|
||||||
|
|
|
@ -67,7 +67,7 @@ class p25_decoder_sink_b(gr.hier_block2):
|
||||||
@type debug: int
|
@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(1, 1, gr.sizeof_char), # Input signature
|
||||||
gr.io_signature(0, 0, 0)) # Output signature
|
gr.io_signature(0, 0, 0)) # Output signature
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ class p25_decoder_sink_b(gr.hier_block2):
|
||||||
num_decoders = 1
|
num_decoders = 1
|
||||||
if num_ambe > 1:
|
if num_ambe > 1:
|
||||||
num_decoders += 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.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)
|
self.p25_decoders[slot].set_slotid(slot)
|
||||||
|
|
||||||
|
|
|
@ -62,16 +62,16 @@ def get_decim(speed):
|
||||||
for i_f in if_freqs:
|
for i_f in if_freqs:
|
||||||
if s % i_f != 0:
|
if s % i_f != 0:
|
||||||
continue
|
continue
|
||||||
q = int(s / i_f)
|
q = s // i_f
|
||||||
if q & 1:
|
if q & 1:
|
||||||
continue
|
continue
|
||||||
if q >= 40 and q & 3 == 0:
|
if q >= 40 and q & 3 == 0:
|
||||||
decim = q/4
|
decim = q//4
|
||||||
decim2 = 4
|
decim2 = 4
|
||||||
else:
|
else:
|
||||||
decim = q/2
|
decim = q//2
|
||||||
decim2 = 2
|
decim2 = 2
|
||||||
return int(decim), int(decim2)
|
return decim, decim2
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class p25_demod_base(gr.hier_block2):
|
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)
|
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()
|
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':
|
if filter_type == 'rrc':
|
||||||
ntaps = 7 * sps
|
ntaps = 7 * sps
|
||||||
if ntaps & 1 == 0:
|
if ntaps & 1 == 0:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Copyright 2008-2011 Steve Glass
|
# 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.
|
# Copyright 2003,2004,2005,2006 Free Software Foundation, Inc.
|
||||||
# (from radiorausch)
|
# (from radiorausch)
|
||||||
|
@ -114,27 +114,27 @@ class p25_rx_block (gr.top_block):
|
||||||
import osmosdr
|
import osmosdr
|
||||||
self.src = osmosdr.source(options.args)
|
self.src = osmosdr.source(options.args)
|
||||||
except Exception:
|
except Exception:
|
||||||
print "osmosdr source_c creation failure"
|
print ("osmosdr source_c creation failure")
|
||||||
ignore = True
|
ignore = True
|
||||||
|
|
||||||
if any(x in options.args.lower() for x in ['rtl', 'airspy', 'hackrf', 'uhd']):
|
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
|
self.rtl_found = True
|
||||||
|
|
||||||
gain_names = self.src.get_gain_names()
|
gain_names = self.src.get_gain_names()
|
||||||
for name in gain_names:
|
for name in gain_names:
|
||||||
range = self.src.get_gain_range(name)
|
range1 = 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())
|
print ("gain: name: %s range: start %d stop %d step %d" % (name, range1[0].start(), range1[0].stop(), range1[0].step()))
|
||||||
if options.gains:
|
if options.gains:
|
||||||
for tup in options.gains.split(","):
|
for tup in options.gains.split(","):
|
||||||
name, gain = tup.split(":")
|
name, gain = tup.split(":")
|
||||||
gain = int(gain)
|
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)
|
self.src.set_gain(gain, name)
|
||||||
|
|
||||||
rates = self.src.get_sample_rates()
|
rates = self.src.get_sample_rates()
|
||||||
try:
|
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:
|
except:
|
||||||
pass # ignore
|
pass # ignore
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ class p25_rx_block (gr.top_block):
|
||||||
|
|
||||||
self.constellation_scope_connected = False
|
self.constellation_scope_connected = False
|
||||||
|
|
||||||
for i in xrange(len(speeds)):
|
for i in range(len(speeds)):
|
||||||
if speeds[i] == _default_speed:
|
if speeds[i] == _default_speed:
|
||||||
self.current_speed = i
|
self.current_speed = i
|
||||||
self.default_speed_idx = i
|
self.default_speed_idx = i
|
||||||
|
@ -176,7 +176,7 @@ class p25_rx_block (gr.top_block):
|
||||||
|
|
||||||
# wait for gdb
|
# wait for gdb
|
||||||
if options.pause:
|
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...")
|
raw_input("Press 'Enter' to continue...")
|
||||||
|
|
||||||
self.input_q = gr.msg_queue(10)
|
self.input_q = gr.msg_queue(10)
|
||||||
|
@ -298,7 +298,7 @@ class p25_rx_block (gr.top_block):
|
||||||
if self.options.phase2_tdma:
|
if self.options.phase2_tdma:
|
||||||
num_ambe = 2
|
num_ambe = 2
|
||||||
if self.options.logfile_workers:
|
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 = p25_demodulator.p25_demod_cb(input_rate=capture_rate,
|
||||||
demod_type=self.options.demod_type,
|
demod_type=self.options.demod_type,
|
||||||
offset=self.options.offset)
|
offset=self.options.offset)
|
||||||
|
@ -458,7 +458,7 @@ class p25_rx_block (gr.top_block):
|
||||||
if self.rtl_found:
|
if self.rtl_found:
|
||||||
self.src.set_gain(gain, 'LNA')
|
self.src.set_gain(gain, 'LNA')
|
||||||
if self.options.verbosity:
|
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:
|
else:
|
||||||
if self.baseband_input:
|
if self.baseband_input:
|
||||||
f = 1.0
|
f = 1.0
|
||||||
|
@ -467,7 +467,7 @@ class p25_rx_block (gr.top_block):
|
||||||
self.demod.set_baseband_gain(float(gain) * f)
|
self.demod.set_baseband_gain(float(gain) * f)
|
||||||
|
|
||||||
def set_audio_scaler(self, vol):
|
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'):
|
if hasattr(self.decoder, 'set_scaler_k'):
|
||||||
self.decoder.set_scaler_k((1 / 32768.0) * (vol * 0.1))
|
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:
|
if file_seek > 0:
|
||||||
rc = ifile.seek(file_seek*1024, gr.SEEK_SET)
|
rc = ifile.seek(file_seek*1024, gr.SEEK_SET)
|
||||||
assert rc == True
|
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)
|
throttle = blocks.throttle(gr.sizeof_gr_complex, speed)
|
||||||
self.source = blocks.multiply_const_cc(gain)
|
self.source = blocks.multiply_const_cc(gain)
|
||||||
self.connect(ifile, throttle, self.source)
|
self.connect(ifile, throttle, self.source)
|
||||||
|
@ -645,6 +645,9 @@ class p25_rx_block (gr.top_block):
|
||||||
if t == -4:
|
if t == -4:
|
||||||
d = json.loads(s)
|
d = json.loads(s)
|
||||||
s = d['command']
|
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
|
if s == 'quit': return True
|
||||||
elif s == 'update':
|
elif s == 'update':
|
||||||
self.freq_update()
|
self.freq_update()
|
||||||
|
|
|
@ -22,7 +22,7 @@ import numpy as np
|
||||||
|
|
||||||
def rev_int(n,l):
|
def rev_int(n,l):
|
||||||
j=0
|
j=0
|
||||||
for i in xrange(l):
|
for i in range(l):
|
||||||
b=n&1
|
b=n&1
|
||||||
n=n>>1
|
n=n>>1
|
||||||
j = (j << 1) | b
|
j = (j << 1) | b
|
||||||
|
@ -30,7 +30,7 @@ def rev_int(n,l):
|
||||||
|
|
||||||
def bits_to_dibits(bits):
|
def bits_to_dibits(bits):
|
||||||
d = []
|
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])
|
d.append((bits[i*2]<<1) + bits[i*2+1])
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@ -43,14 +43,14 @@ def dibits_to_bits(dibits):
|
||||||
|
|
||||||
def mk_array(n, l):
|
def mk_array(n, l):
|
||||||
a = []
|
a = []
|
||||||
for i in xrange(0,l):
|
for i in range(0,l):
|
||||||
a.insert(0, n & 1)
|
a.insert(0, n & 1)
|
||||||
n = n >> 1
|
n = n >> 1
|
||||||
return np.array(a)
|
return np.array(a)
|
||||||
|
|
||||||
def mk_int(a):
|
def mk_int(a):
|
||||||
res= 0L
|
res= 0
|
||||||
for i in xrange(0, len(a)):
|
for i in range(0, len(a)):
|
||||||
res = res * 2
|
res = res * 2
|
||||||
res = res + (a[i] & 1)
|
res = res + (a[i] & 1)
|
||||||
return res
|
return res
|
||||||
|
@ -61,7 +61,7 @@ def mk_str(a):
|
||||||
def check_l(a,b):
|
def check_l(a,b):
|
||||||
ans = 0
|
ans = 0
|
||||||
assert len(a) == len(b)
|
assert len(a) == len(b)
|
||||||
for i in xrange(len(a)):
|
for i in range(len(a)):
|
||||||
if (a[i] == b[i]):
|
if (a[i] == b[i]):
|
||||||
ans += 1
|
ans += 1
|
||||||
return ans
|
return ans
|
||||||
|
@ -77,7 +77,7 @@ def fixup(a):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def find_sym(pattern, symbols):
|
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)]
|
chunk = symbols[i : i + len(pattern)]
|
||||||
if chunk == pattern:
|
if chunk == pattern:
|
||||||
return i
|
return i
|
||||||
|
|
|
@ -25,54 +25,54 @@ class p25p2_lfsr(object):
|
||||||
def __init__(self,nac,sysid,wacn):
|
def __init__(self,nac,sysid,wacn):
|
||||||
xorbits = self.mk_xor_bits(nac,sysid,wacn)
|
xorbits = self.mk_xor_bits(nac,sysid,wacn)
|
||||||
#self.xorsyms = np.zeros(len(xorbits)/2)
|
#self.xorsyms = np.zeros(len(xorbits)/2)
|
||||||
self.xorsyms = [0] * (len(xorbits)/2)
|
self.xorsyms = [0] * (len(xorbits)//2)
|
||||||
for i in xrange(len(self.xorsyms)):
|
for i in range(len(self.xorsyms)):
|
||||||
self.xorsyms[i] = (xorbits[i*2] << 1) + xorbits[i*2+1]
|
self.xorsyms[i] = (xorbits[i*2] << 1) + xorbits[i*2+1]
|
||||||
self.xor_chars = ''.join([chr(c) for c in self.xorsyms])
|
self.xor_chars = ''.join([chr(c) for c in self.xorsyms])
|
||||||
|
|
||||||
def asm_reg(self,s1,s2,s3,s4,s5,s6):
|
def asm_reg(self,s1,s2,s3,s4,s5,s6):
|
||||||
s1 = s1 & 0xfL
|
s1 = s1 & 0xf
|
||||||
s2 = s2 & 0x1fL
|
s2 = s2 & 0x1f
|
||||||
s3 = s3 & 0x3fL
|
s3 = s3 & 0x3f
|
||||||
s4 = s4 & 0x1fL
|
s4 = s4 & 0x1f
|
||||||
s5 = s5 & 0x3fffL
|
s5 = s5 & 0x3fff
|
||||||
s6 = s6 & 0x3ffL
|
s6 = s6 & 0x3ff
|
||||||
return (s1<<40)+(s2<<35)+(s3<<29)+(s4<<24)+(s5<<10)+s6
|
return (s1<<40)+(s2<<35)+(s3<<29)+(s4<<24)+(s5<<10)+s6
|
||||||
|
|
||||||
def disasm_reg(self,r):
|
def disasm_reg(self,r):
|
||||||
s1 = (r>>40) & 0xfL
|
s1 = (r>>40) & 0xf
|
||||||
s2 = (r>>35) & 0x1fL
|
s2 = (r>>35) & 0x1f
|
||||||
s3 = (r>>29) & 0x3fL
|
s3 = (r>>29) & 0x3f
|
||||||
s4 = (r>>24) & 0x1fL
|
s4 = (r>>24) & 0x1f
|
||||||
s5 = (r>>10) & 0x3fffL
|
s5 = (r>>10) & 0x3fff
|
||||||
s6 = r & 0x3ffL
|
s6 = r & 0x3ff
|
||||||
return s1,s2,s3,s4,s5,s6
|
return s1,s2,s3,s4,s5,s6
|
||||||
|
|
||||||
def cyc_reg(self, reg):
|
def cyc_reg(self, reg):
|
||||||
s1,s2,s3,s4,s5,s6 = self.disasm_reg(reg)
|
s1,s2,s3,s4,s5,s6 = self.disasm_reg(reg)
|
||||||
cy1 = (s1 >> 3) & 1L
|
cy1 = (s1 >> 3) & 1
|
||||||
cy2 = (s2 >> 4) & 1L
|
cy2 = (s2 >> 4) & 1
|
||||||
cy3 = (s3 >> 5) & 1L
|
cy3 = (s3 >> 5) & 1
|
||||||
cy4 = (s4 >> 4) & 1L
|
cy4 = (s4 >> 4) & 1
|
||||||
cy5 = (s5 >> 13) & 1L
|
cy5 = (s5 >> 13) & 1
|
||||||
cy6 = (s6 >> 9) & 1L
|
cy6 = (s6 >> 9) & 1
|
||||||
x1 = cy1 ^ cy2
|
x1 = cy1 ^ cy2
|
||||||
x2 = cy1 ^ cy3
|
x2 = cy1 ^ cy3
|
||||||
x3 = cy1 ^ cy4
|
x3 = cy1 ^ cy4
|
||||||
x4 = cy1 ^ cy5
|
x4 = cy1 ^ cy5
|
||||||
x5 = cy1 ^ cy6
|
x5 = cy1 ^ cy6
|
||||||
s1 = (s1 << 1) & 0xfL
|
s1 = (s1 << 1) & 0xf
|
||||||
s2 = (s2 << 1) & 0x1fL
|
s2 = (s2 << 1) & 0x1f
|
||||||
s3 = (s3 << 1) & 0x3fL
|
s3 = (s3 << 1) & 0x3f
|
||||||
s4 = (s4 << 1) & 0x1fL
|
s4 = (s4 << 1) & 0x1f
|
||||||
s5 = (s5 << 1) & 0x3fffL
|
s5 = (s5 << 1) & 0x3fff
|
||||||
s6 = (s6 << 1) & 0x3ffL
|
s6 = (s6 << 1) & 0x3ff
|
||||||
s1 = s1 | (x1 & 1L)
|
s1 = s1 | (x1 & 1)
|
||||||
s2 = s2 | (x2 & 1L)
|
s2 = s2 | (x2 & 1)
|
||||||
s3 = s3 | (x3 & 1L)
|
s3 = s3 | (x3 & 1)
|
||||||
s4 = s4 | (x4 & 1L)
|
s4 = s4 | (x4 & 1)
|
||||||
s5 = s5 | (x5 & 1L)
|
s5 = s5 | (x5 & 1)
|
||||||
s6 = s6 | (cy1 & 1L)
|
s6 = s6 | (cy1 & 1)
|
||||||
return self.asm_reg(s1,s2,s3,s4,s5,s6)
|
return self.asm_reg(s1,s2,s3,s4,s5,s6)
|
||||||
|
|
||||||
def mk_xor_bits(self, nac,sysid,wacn):
|
def mk_xor_bits(self, nac,sysid,wacn):
|
||||||
|
@ -82,7 +82,7 @@ class p25p2_lfsr(object):
|
||||||
reg = mk_int(np.dot(reg,M))
|
reg = mk_int(np.dot(reg,M))
|
||||||
|
|
||||||
s = []
|
s = []
|
||||||
for i in xrange(4320):
|
for i in range(4320):
|
||||||
s.append((reg >> 43) & 1)
|
s.append((reg >> 43) & 1)
|
||||||
reg = self.cyc_reg(reg)
|
reg = self.cyc_reg(reg)
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class curses_terminal(threading.Thread):
|
||||||
self.maxy, self.maxx = self.stdscr.getmaxyx()
|
self.maxy, self.maxx = self.stdscr.getmaxyx()
|
||||||
if (self.maxy < 6) or (self.maxx < 60):
|
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))
|
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
|
self.keep_running = False
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ class curses_terminal(threading.Thread):
|
||||||
if c in COMMANDS.keys():
|
if c in COMMANDS.keys():
|
||||||
self.send_command(COMMANDS[c], 0)
|
self.send_command(COMMANDS[c], 0)
|
||||||
elif c == ord('q'):
|
elif c == ord('q'):
|
||||||
return True
|
return True
|
||||||
elif c == ord('t'):
|
elif c == ord('t'):
|
||||||
if self.current_nac:
|
if self.current_nac:
|
||||||
self.send_command('add_default_config', int(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.addstr(0, 0, s)
|
||||||
self.top_bar.refresh()
|
self.top_bar.refresh()
|
||||||
self.freq_list.erase()
|
self.freq_list.erase()
|
||||||
for i in xrange(len(freqs)):
|
for i in range(len(freqs)):
|
||||||
if i > (self.maxy - 6):
|
if i > (self.maxy - 6):
|
||||||
break
|
break
|
||||||
s=msg[current_nac]['frequencies'][freqs[i]]
|
s=msg[current_nac]['frequencies'][freqs[i]]
|
||||||
|
@ -346,7 +346,7 @@ class zeromq_terminal(threading.Thread):
|
||||||
|
|
||||||
class http_terminal(threading.Thread):
|
class http_terminal(threading.Thread):
|
||||||
def __init__(self, input_q, output_q, endpoint, **kwds):
|
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)
|
threading.Thread.__init__ (self, **kwds)
|
||||||
self.setDaemon(1)
|
self.setDaemon(1)
|
||||||
|
|
|
@ -41,6 +41,16 @@ def crc16(dat,len): # slow version
|
||||||
crc = crc ^ 0xffff
|
crc = crc ^ 0xffff
|
||||||
return crc
|
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):
|
class trunked_system (object):
|
||||||
def __init__(self, debug=0, config=None):
|
def __init__(self, debug=0, config=None):
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
@ -104,7 +114,7 @@ class trunked_system (object):
|
||||||
d['rxchan'] = self.rfss_chan
|
d['rxchan'] = self.rfss_chan
|
||||||
d['txchan'] = self.rfss_txchan
|
d['txchan'] = self.rfss_txchan
|
||||||
d['wacn'] = self.ns_wacn
|
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['tsbks'] = self.stats['tsbks']
|
||||||
d['frequencies'] = {}
|
d['frequencies'] = {}
|
||||||
d['frequency_data'] = {}
|
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['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['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)
|
return json.dumps(d)
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
|
@ -608,7 +618,7 @@ class rx_ctl (object):
|
||||||
self.build_config_json(conf_file)
|
self.build_config_json(conf_file)
|
||||||
else:
|
else:
|
||||||
self.build_config(conf_file)
|
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_nac = self.find_next_tsys()
|
||||||
self.current_state = self.states.CC
|
self.current_state = self.states.CC
|
||||||
|
|
||||||
|
@ -755,10 +765,10 @@ class rx_ctl (object):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def process_qmsg(self, msg):
|
def process_qmsg(self, msg):
|
||||||
type = msg.type()
|
mtype = msg.type()
|
||||||
updated = 0
|
updated = 0
|
||||||
curr_time = time.time()
|
curr_time = time.time()
|
||||||
if type == -3: # P25 call signalling data
|
if mtype == -3: # P25 call signalling data
|
||||||
if self.debug > 10:
|
if self.debug > 10:
|
||||||
sys.stderr.write("%f process_qmsg: P25 info: %s\n" % (time.time(), msg.to_string()))
|
sys.stderr.write("%f process_qmsg: P25 info: %s\n" % (time.time(), msg.to_string()))
|
||||||
js = json.loads(msg.to_string())
|
js = json.loads(msg.to_string())
|
||||||
|
@ -777,55 +787,50 @@ class rx_ctl (object):
|
||||||
if 'keyid' in js.keys():
|
if 'keyid' in js.keys():
|
||||||
tsys.current_keyid = js['keyid']
|
tsys.current_keyid = js['keyid']
|
||||||
return
|
return
|
||||||
elif type == -2: # request from gui
|
elif mtype == -2: # request from gui
|
||||||
cmd = msg.to_string()
|
cmd = msg.to_string()
|
||||||
if self.debug > 10:
|
if self.debug > 10:
|
||||||
sys.stderr.write('process_qmsg: command: %s\n' % cmd)
|
sys.stderr.write('process_qmsg: command: %s\n' % cmd)
|
||||||
self.update_state(cmd, curr_time)
|
self.update_state(cmd, curr_time)
|
||||||
return
|
return
|
||||||
elif type == -1: # timeout
|
elif mtype == -1: # timeout
|
||||||
if self.debug > 10:
|
if self.debug > 10:
|
||||||
sys.stderr.write('%f process_data_unit timeout\n' % time.time())
|
sys.stderr.write('%f process_data_unit timeout\n' % time.time())
|
||||||
self.update_state('timeout', curr_time)
|
self.update_state('timeout', curr_time)
|
||||||
if self.logfile_workers:
|
if self.logfile_workers:
|
||||||
self.logging_scheduler(curr_time)
|
self.logging_scheduler(curr_time)
|
||||||
return
|
return
|
||||||
elif type < 0:
|
elif mtype < 0:
|
||||||
sys.stderr.write('unknown message type %d\n' % (type))
|
sys.stderr.write('unknown message type %d\n' % (mtype))
|
||||||
return
|
return
|
||||||
s = msg.to_string()
|
s = msg.to_string()
|
||||||
# nac is always 1st two bytes
|
# 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
|
#assert nac != 0xffff # FIXME: uncomment this after any remaining usages of 0xffff removed
|
||||||
if nac == 0xffff:
|
if nac == 0xffff:
|
||||||
if (type != 7) and (type != 12): # TDMA duid (end of call etc)
|
if (mtype != 7) and (mtype != 12): # TDMA duid (end of call etc)
|
||||||
self.update_state('tdma_duid%d' % type, curr_time)
|
self.update_state('tdma_duid%d' % mtype, curr_time)
|
||||||
return
|
return
|
||||||
else: # voice channel derived TSBK or MBT PDU
|
else: # voice channel derived TSBK or MBT PDU
|
||||||
nac = self.current_nac
|
nac = self.current_nac
|
||||||
s = s[2:]
|
s = s[2:]
|
||||||
if self.debug > 10:
|
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)))
|
sys.stderr.write('nac %x type %d at %f state %d len %d\n' %(nac, mtype, time.time(), self.current_state, len(s)))
|
||||||
if (type == 7 or type == 12) and nac not in self.trunked_systems:
|
if (mtype == 7 or mtype == 12) and nac not in self.trunked_systems:
|
||||||
if not self.configs:
|
if not self.configs:
|
||||||
# TODO: allow whitelist/blacklist rather than blind automatic-add
|
# TODO: allow whitelist/blacklist rather than blind automatic-add
|
||||||
self.add_trunked_system(nac)
|
self.add_trunked_system(nac)
|
||||||
else:
|
else:
|
||||||
sys.stderr.write("%f NAC %x not configured\n" % (time.time(), nac))
|
sys.stderr.write("%f NAC %x not configured\n" % (time.time(), nac))
|
||||||
return
|
return
|
||||||
if type == 7: # trunk: TSBK
|
if mtype == 7: # trunk: TSBK
|
||||||
t = 0
|
t = get_ordinals(s)
|
||||||
for c in s:
|
|
||||||
t = (t << 8) + ord(c)
|
|
||||||
updated += self.trunked_systems[nac].decode_tsbk(t)
|
updated += self.trunked_systems[nac].decode_tsbk(t)
|
||||||
elif type == 12: # trunk: MBT
|
elif mtype == 12: # trunk: MBT
|
||||||
s1 = s[:10] # header without crc
|
s1 = s[:10] # header without crc
|
||||||
s2 = s[12:]
|
s2 = s[12:]
|
||||||
header = mbt_data = 0
|
header = get_ordinals(s1)
|
||||||
for c in s1:
|
mbt_data = get_ordinals(s2)
|
||||||
header = (header << 8) + ord(c)
|
|
||||||
for c in s2:
|
|
||||||
mbt_data = (mbt_data << 8) + ord(c)
|
|
||||||
|
|
||||||
fmt = (header >> 72) & 0x1f
|
fmt = (header >> 72) & 0x1f
|
||||||
sap = (header >> 64) & 0x3f
|
sap = (header >> 64) & 0x3f
|
||||||
|
@ -835,12 +840,15 @@ class rx_ctl (object):
|
||||||
|
|
||||||
opcode = (header >> 16) & 0x3f
|
opcode = (header >> 16) & 0x3f
|
||||||
if self.debug > 10:
|
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)
|
updated += self.trunked_systems[nac].decode_mbt_data(opcode, src, header << 16, mbt_data << 32)
|
||||||
|
|
||||||
if nac != self.current_nac:
|
if nac != self.current_nac:
|
||||||
if self.debug > 10: # this is occasionally expected if cycling between different tsys
|
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
|
return
|
||||||
|
|
||||||
if self.logfile_workers:
|
if self.logfile_workers:
|
||||||
|
@ -850,7 +858,7 @@ class rx_ctl (object):
|
||||||
if updated:
|
if updated:
|
||||||
self.update_state('update', curr_time)
|
self.update_state('update', curr_time)
|
||||||
else:
|
else:
|
||||||
self.update_state('duid%d' % type, curr_time)
|
self.update_state('duid%d' % mtype, curr_time)
|
||||||
|
|
||||||
def find_available_worker(self):
|
def find_available_worker(self):
|
||||||
for worker in self.logfile_workers:
|
for worker in self.logfile_workers:
|
||||||
|
@ -962,6 +970,9 @@ class rx_ctl (object):
|
||||||
if not self.configs:
|
if not self.configs:
|
||||||
return # run in "manual mode" if no conf
|
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
|
nac = self.current_nac
|
||||||
tsys = self.trunked_systems[nac]
|
tsys = self.trunked_systems[nac]
|
||||||
|
|
||||||
|
@ -1064,7 +1075,7 @@ class rx_ctl (object):
|
||||||
self.tgid_hold = self.current_tgid
|
self.tgid_hold = self.current_tgid
|
||||||
self.tgid_hold_until = curr_time + 86400 * 10000
|
self.tgid_hold_until = curr_time + 86400 * 10000
|
||||||
self.hold_mode = True
|
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':
|
elif command == 'unset_hold':
|
||||||
self.last_command = {'command': command, 'time': curr_time}
|
self.last_command = {'command': command, 'time': curr_time}
|
||||||
if self.current_tgid:
|
if self.current_tgid:
|
||||||
|
|
|
@ -42,14 +42,14 @@ def get_int_dict(s):
|
||||||
v = v.split("\t",1) # split on tab
|
v = v.split("\t",1) # split on tab
|
||||||
try:
|
try:
|
||||||
v0 = int(v[0]) # first parameter is tgid or start of tgid range
|
v0 = int(v[0]) # first parameter is tgid or start of tgid range
|
||||||
v1 = v0
|
v1 = v0
|
||||||
if (len(v) > 1) and (int(v[1]) > v0): # second parameter if present is end of tgid range
|
if (len(v) > 1) and (int(v[1]) > v0): # second parameter if present is end of tgid range
|
||||||
v1 = int(v[1])
|
v1 = int(v[1])
|
||||||
|
|
||||||
for tg in range(v0, (v1 + 1)):
|
for tg in range(v0, (v1 + 1)):
|
||||||
if tg not in d: # is this a new tg?
|
if tg not in d: # is this a new tg?
|
||||||
d[tg] = [] # if so, add to dict (key only, value null)
|
d[tg] = [] # if so, add to dict (key only, value null)
|
||||||
sys.stderr.write('added talkgroup %d from %s\n' % (tg,s))
|
sys.stderr.write('added talkgroup %d from %s\n' % (tg,s))
|
||||||
|
|
||||||
except (IndexError, ValueError) as ex:
|
except (IndexError, ValueError) as ex:
|
||||||
continue
|
continue
|
||||||
|
@ -62,7 +62,7 @@ def utf_ascii(ustr):
|
||||||
def load_tsv(tsv_filename):
|
def load_tsv(tsv_filename):
|
||||||
hdrmap = []
|
hdrmap = []
|
||||||
configs = {}
|
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)
|
sreader = csv.reader(csvfile, delimiter='\t', quotechar='"', quoting=csv.QUOTE_ALL)
|
||||||
for row in sreader:
|
for row in sreader:
|
||||||
if not hdrmap:
|
if not hdrmap:
|
||||||
|
@ -73,7 +73,7 @@ def load_tsv(tsv_filename):
|
||||||
hdrmap.append(hdr)
|
hdrmap.append(hdr)
|
||||||
continue
|
continue
|
||||||
fields = {}
|
fields = {}
|
||||||
for i in xrange(len(row)):
|
for i in range(len(row)):
|
||||||
if row[i]:
|
if row[i]:
|
||||||
fields[hdrmap[i]] = row[i]
|
fields[hdrmap[i]] = row[i]
|
||||||
if hdrmap[i] != 'sysname':
|
if hdrmap[i] != 'sysname':
|
||||||
|
@ -99,12 +99,12 @@ def make_config(configs):
|
||||||
result_config[nac][k] = get_int_dict(configs[nac][k])
|
result_config[nac][k] = get_int_dict(configs[nac][k])
|
||||||
if 'tgid_tags_file' in configs[nac]:
|
if 'tgid_tags_file' in configs[nac]:
|
||||||
import csv
|
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)
|
sreader = csv.reader(csvfile, delimiter='\t', quotechar='"', quoting=csv.QUOTE_ALL)
|
||||||
for row in sreader:
|
for row in sreader:
|
||||||
try:
|
try:
|
||||||
tgid = int(row[0])
|
tgid = int(row[0])
|
||||||
txt = utf_ascii(row[1])
|
txt = row[1]
|
||||||
except (IndexError, ValueError) as ex:
|
except (IndexError, ValueError) as ex:
|
||||||
continue
|
continue
|
||||||
if len(row) >= 3:
|
if len(row) >= 3:
|
||||||
|
@ -122,7 +122,7 @@ def make_config(configs):
|
||||||
def main():
|
def main():
|
||||||
import json
|
import json
|
||||||
result = make_config(load_tsv(sys.argv[1]))
|
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__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -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
|
# Specs undefined above 2,880 Hz. It would be nice to have a sharper
|
||||||
# rolloff, but this filter is cheap enough....
|
# rolloff, but this filter is cheap enough....
|
||||||
xfer = [] # frequency domain transfer function
|
xfer = [] # frequency domain transfer function
|
||||||
for f in xrange(0,symbol_rate):
|
for f in range(0,symbol_rate):
|
||||||
# D(f)
|
# D(f)
|
||||||
t = pi * f / symbol_rate
|
t = pi * f / symbol_rate
|
||||||
if t < 1e-6:
|
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):
|
def transfer_function_tx(symbol_rate=_def_symbol_rate):
|
||||||
xfer = [] # frequency domain transfer function
|
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)
|
# H(f)
|
||||||
if f < 1920:
|
if f < 1920:
|
||||||
hf = 1.0
|
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):
|
def transfer_function_dmr(symbol_rate=_def_symbol_rate):
|
||||||
xfer = [] # frequency domain transfer function
|
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:
|
if f < 1920:
|
||||||
hf = 1.0
|
hf = 1.0
|
||||||
else:
|
else:
|
||||||
|
@ -102,7 +102,7 @@ def transfer_function_nxdn(symbol_rate=_def_symbol_rate):
|
||||||
fh = int(0.5+(1+a)/(2*T))
|
fh = int(0.5+(1+a)/(2*T))
|
||||||
|
|
||||||
xfer = []
|
xfer = []
|
||||||
for f in xrange(0, symbol_rate):
|
for f in range(0, symbol_rate):
|
||||||
if f < fl:
|
if f < fl:
|
||||||
hf = 1.0
|
hf = 1.0
|
||||||
elif f >= fl and f <= fh:
|
elif f >= fl and f <= fh:
|
||||||
|
@ -132,7 +132,7 @@ class c4fm_taps(object):
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
impulse_response = np.fft.fftshift(np.fft.irfft(self.generator(symbol_rate=self.symbol_rate), self.sample_rate))
|
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]
|
coeffs = impulse_response[start: start+self.ntaps]
|
||||||
gain = self.filter_gain / sum(coeffs)
|
gain = self.filter_gain / sum(coeffs)
|
||||||
return coeffs * gain
|
return coeffs * gain
|
||||||
|
@ -149,7 +149,7 @@ class gmsk_taps(object):
|
||||||
self.bt = bt
|
self.bt = bt
|
||||||
|
|
||||||
self.samples_per_symbol = self.sample_rate / self.symbol_rate
|
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):
|
def generate(self):
|
||||||
# from gnuradio gr-digital/python/digital/gmsk.py
|
# from gnuradio gr-digital/python/digital/gmsk.py
|
||||||
|
@ -201,7 +201,7 @@ class p25_mod_bf(gr.hier_block2):
|
||||||
@type debug: bool
|
@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_char), # Input signature
|
||||||
gr.io_signature(1, 1, gr.sizeof_float)) # Output 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)
|
self.connect(self.filter, self)
|
||||||
|
|
||||||
def _print_verbage(self):
|
def _print_verbage(self):
|
||||||
print "\nModulator:"
|
print ("\nModulator:")
|
||||||
print "interpolation: %d decimation: %d" %(self._interp_factor, self._decimation)
|
print ("interpolation: %d decimation: %d" %(self._interp_factor, self._decimation))
|
||||||
|
|
||||||
def _setup_logging(self):
|
def _setup_logging(self):
|
||||||
print "Modulation logging turned on."
|
print ("Modulation logging turned on.")
|
||||||
self.connect(self.C2S,
|
self.connect(self.C2S,
|
||||||
gr.file_sink(gr.sizeof_float, "tx_chunks2symbols.dat"))
|
gr.file_sink(gr.sizeof_float, "tx_chunks2symbols.dat"))
|
||||||
self.connect(self.polarity,
|
self.connect(self.polarity,
|
||||||
|
|
Loading…
Reference in New Issue