Merge branch 'develop' into packet

This commit is contained in:
Jakob Ketterl
2019-06-22 18:20:01 +02:00
33 changed files with 784 additions and 71 deletions

View File

@ -2,6 +2,8 @@ import os
import subprocess
from functools import reduce
from operator import and_
import re
from distutils.version import LooseVersion
import logging
logger = logging.getLogger(__name__)
@ -16,8 +18,9 @@ class FeatureDetector(object):
"rtl_sdr": [ "rtl_sdr" ],
"sdrplay": [ "rx_tools" ],
"hackrf": [ "hackrf_transfer" ],
"airspy": [ "airspy_rx" ],
"digital_voice_digiham": [ "digiham", "sox" ],
"digital_voice_dsd": [ "dsd", "sox" ],
"digital_voice_dsd": [ "dsd", "sox", "digiham" ],
"packet": [ "direwolf" ]
}
@ -82,19 +85,31 @@ class FeatureDetector(object):
def command_exists(self, command):
return os.system("which {0}".format(command)) == 0
"""
To use DMR and YSF, the digiham package is required. You can find the package and installation instructions here:
https://github.com/jketterl/digiham
Please note: there is close interaction between digiham and openwebrx, so older versions will probably not work.
If you have an older verison of digiham installed, please update it along with openwebrx.
As of now, we require version 0.2 of digiham.
"""
def has_digiham(self):
# the digiham tools expect to be fed via stdin, they will block until their stdin is closed.
def check_with_stdin(command):
required_version = LooseVersion("0.2")
digiham_version_regex = re.compile('^digiham version (.*)$')
def check_digiham_version(command):
try:
process = subprocess.Popen(command, stdin=subprocess.PIPE)
process.communicate("")
return process.wait() == 0
process = subprocess.Popen([command, "--version"], stdout=subprocess.PIPE)
version = LooseVersion(digiham_version_regex.match(process.stdout.readline().decode()).group(1))
process.wait(1)
return version >= required_version
except FileNotFoundError:
return False
return reduce(and_,
map(
check_with_stdin,
["rrc_filter", "ysf_decoder", "dmr_decoder", "mbe_synthesizer", "gfsk_demodulator"]
check_digiham_version,
["rrc_filter", "ysf_decoder", "dmr_decoder", "mbe_synthesizer", "gfsk_demodulator",
"digitalvoice_filter"]
),
True)
@ -105,4 +120,7 @@ class FeatureDetector(object):
return self.command_is_runnable("sox")
def has_direwolf(self):
return self.command_is_runnable("direwolf --help")
return self.command_is_runnable("direwolf --help")
def has_airspy_rx(self):
return self.command_is_runnable("airspy_rx --help 2> /dev/null")

View File

@ -18,7 +18,9 @@ class Router(object):
{"route": "/status", "controller": StatusController},
{"regex": "/static/(.+)", "controller": AssetsController},
{"route": "/ws/", "controller": WebSocketController},
{"regex": "(/favicon.ico)", "controller": AssetsController}
{"regex": "(/favicon.ico)", "controller": AssetsController},
# backwards compatibility for the sdr.hu portal
{"regex": "/(gfx/openwebrx-avatar.png)", "controller": AssetsController}
]
def find_controller(self, path):
for m in Router.mappings:

View File

@ -64,11 +64,13 @@ class MetaParser(object):
enrichers = {
"DMR": DmrMetaEnricher()
}
def __init__(self, handler):
self.handler = handler
def parse(self, meta):
fields = meta.split(";")
meta = {v[0] : "".join(v[1:]) for v in map(lambda x: x.split(":"), fields)}
meta = {v[0]: "".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""}
if "protocol" in meta:
protocol = meta["protocol"]

View File

@ -257,6 +257,16 @@ class SdrplaySource(SdrSource):
def sleepOnRestart(self):
time.sleep(1)
class AirspySource(SdrSource):
def getCommand(self):
frequency = self.props['center_freq'] / 1e6
command = "airspy_rx"
command += " -f{0}".format(frequency)
command += " -r /dev/stdout -a{samp_rate} -g {rf_gain}"
return command
def getFormatConversion(self):
return "csdr convert_s16_f"
class SpectrumThread(csdr.output):
def __init__(self, sdrSource):
self.sdrSource = sdrSource
@ -339,7 +349,8 @@ class DspManager(csdr.output):
self.localProps = self.sdrSource.getProps().collect(
"audio_compression", "fft_compression", "digimodes_fft_size", "csdr_dynamic_bufsize",
"csdr_print_bufsizes", "csdr_through", "digimodes_enable", "samp_rate", "digital_voice_unvoiced_quality"
"csdr_print_bufsizes", "csdr_through", "digimodes_enable", "samp_rate", "digital_voice_unvoiced_quality",
"dmr_filter"
).defaults(PropertyManager.getSharedInstance())
self.dsp = csdr.dsp(self)
@ -366,7 +377,8 @@ class DspManager(csdr.output):
self.localProps.getProperty("low_cut").wire(set_low_cut),
self.localProps.getProperty("high_cut").wire(set_high_cut),
self.localProps.getProperty("mod").wire(self.dsp.set_demodulator),
self.localProps.getProperty("digital_voice_unvoiced_quality").wire(self.dsp.set_unvoiced_quality)
self.localProps.getProperty("digital_voice_unvoiced_quality").wire(self.dsp.set_unvoiced_quality),
self.localProps.getProperty("dmr_filter").wire(self.dsp.set_dmr_filter)
]
self.dsp.set_offset_freq(0)

View File

@ -38,12 +38,16 @@ class WebSocketConnection(object):
# string-type messages are sent as text frames
if (type(data) == str):
header = self.get_header(len(data), 1)
self.handler.wfile.write(header + data.encode('utf-8'))
self.handler.wfile.flush()
data_to_send = header + data.encode('utf-8')
# anything else as binary
else:
header = self.get_header(len(data), 2)
self.handler.wfile.write(header + data)
data_to_send = header + data
written = self.handler.wfile.write(data_to_send)
if (written != len(data_to_send)):
logger.error("incomplete write! closing socket!")
self.close()
else:
self.handler.wfile.flush()
def read_loop(self):
@ -78,7 +82,9 @@ class WebSocketConnection(object):
self.handler.wfile.write(header)
self.handler.wfile.flush()
except ValueError:
logger.exception("while writing close frame:")
logger.exception("ValueError while writing close frame:")
except OSError:
logger.exception("OSError while writing close frame:")
try:
self.handler.finish()