new modificitions for owrx_connector support
This commit is contained in:
parent
dc5ac081ce
commit
ada94f69c3
@ -19,6 +19,7 @@ class FeatureDetector(object):
|
|||||||
features = {
|
features = {
|
||||||
"core": ["csdr", "nmux", "nc"],
|
"core": ["csdr", "nmux", "nc"],
|
||||||
"rtl_sdr": ["rtl_sdr"],
|
"rtl_sdr": ["rtl_sdr"],
|
||||||
|
"rtl_sdr_socket": ["owrx_connector"],
|
||||||
"sdrplay": ["rx_tools"],
|
"sdrplay": ["rx_tools"],
|
||||||
"hackrf": ["hackrf_transfer"],
|
"hackrf": ["hackrf_transfer"],
|
||||||
"airspy": ["airspy_rx"],
|
"airspy": ["airspy_rx"],
|
||||||
@ -163,7 +164,10 @@ class FeatureDetector(object):
|
|||||||
def check_digiham_version(command):
|
def check_digiham_version(command):
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen([command, "--version"], stdout=subprocess.PIPE)
|
process = subprocess.Popen([command, "--version"], stdout=subprocess.PIPE)
|
||||||
version = LooseVersion(digiham_version_regex.match(process.stdout.readline().decode()).group(1))
|
matches = digiham_version_regex.match(process.stdout.readline().decode())
|
||||||
|
if matches is None:
|
||||||
|
return False
|
||||||
|
version = LooseVersion(matches.group(1))
|
||||||
process.wait(1)
|
process.wait(1)
|
||||||
return version >= required_version
|
return version >= required_version
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
@ -185,6 +189,40 @@ class FeatureDetector(object):
|
|||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def has_owrx_connector(self):
|
||||||
|
"""
|
||||||
|
The owrx_connector package offers direct interfacing between your hardware and openwebrx. It allows quicker
|
||||||
|
frequency switching, uses less CPU and can even provide more stability in some cases.
|
||||||
|
|
||||||
|
You can get it here: https://github.com/jketterl/owrx_connector
|
||||||
|
"""
|
||||||
|
required_version = LooseVersion("0.1")
|
||||||
|
|
||||||
|
owrx_connector_version_regex = re.compile("^owrx-connector version (.*)$")
|
||||||
|
|
||||||
|
def check_owrx_connector_version(command):
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen([command, "--version"], stdout=subprocess.PIPE)
|
||||||
|
matches = owrx_connector_version_regex.match(process.stdout.readline().decode())
|
||||||
|
if matches is None:
|
||||||
|
return False
|
||||||
|
version = LooseVersion(matches.group(1))
|
||||||
|
process.wait(1)
|
||||||
|
return version >= required_version
|
||||||
|
except FileNotFoundError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return reduce(
|
||||||
|
and_,
|
||||||
|
map(
|
||||||
|
check_owrx_connector_version,
|
||||||
|
[
|
||||||
|
"rtl_connector",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
def has_dsd(self):
|
def has_dsd(self):
|
||||||
"""
|
"""
|
||||||
The digital voice modes NXDN and D-Star can be decoded by the dsd project. Please note that you need the version
|
The digital voice modes NXDN and D-Star can be decoded by the dsd project. Please note that you need the version
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import threading
|
import threading
|
||||||
import socket
|
from owrx.socket import getAvailablePort
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from owrx.source import SdrService
|
from owrx.source import SdrService
|
||||||
from owrx.bands import Bandplan
|
from owrx.bands import Bandplan
|
||||||
@ -245,14 +245,6 @@ class ServiceHandler(object):
|
|||||||
self.startupTimer = threading.Timer(10, self.updateServices)
|
self.startupTimer = threading.Timer(10, self.updateServices)
|
||||||
self.startupTimer.start()
|
self.startupTimer.start()
|
||||||
|
|
||||||
def getAvailablePort(self):
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
s.bind(("", 0))
|
|
||||||
s.listen(1)
|
|
||||||
port = s.getsockname()[1]
|
|
||||||
s.close()
|
|
||||||
return port
|
|
||||||
|
|
||||||
def updateServices(self):
|
def updateServices(self):
|
||||||
logger.debug("re-scheduling services due to sdr changes")
|
logger.debug("re-scheduling services due to sdr changes")
|
||||||
self.stopServices()
|
self.stopServices()
|
||||||
@ -293,7 +285,7 @@ class ServiceHandler(object):
|
|||||||
resampler_props["center_freq"] = cf
|
resampler_props["center_freq"] = cf
|
||||||
# TODO the + 24000 is a temporary fix since the resampling optimizer does not account for required bandwidths
|
# TODO the + 24000 is a temporary fix since the resampling optimizer does not account for required bandwidths
|
||||||
resampler_props["samp_rate"] = bw + 24000
|
resampler_props["samp_rate"] = bw + 24000
|
||||||
resampler = Resampler(resampler_props, self.getAvailablePort(), self.source)
|
resampler = Resampler(resampler_props, getAvailablePort(), self.source)
|
||||||
resampler.start()
|
resampler.start()
|
||||||
|
|
||||||
for dial in group:
|
for dial in group:
|
||||||
|
10
owrx/socket.py
Normal file
10
owrx/socket.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
def getAvailablePort():
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.bind(("", 0))
|
||||||
|
s.listen(1)
|
||||||
|
port = s.getsockname()[1]
|
||||||
|
s.close()
|
||||||
|
return port
|
104
owrx/source.py
104
owrx/source.py
@ -5,6 +5,7 @@ from owrx.meta import MetaParser
|
|||||||
from owrx.wsjt import WsjtParser
|
from owrx.wsjt import WsjtParser
|
||||||
from owrx.aprs import AprsParser
|
from owrx.aprs import AprsParser
|
||||||
from owrx.metrics import Metrics, DirectMetric
|
from owrx.metrics import Metrics, DirectMetric
|
||||||
|
from owrx.socket import getAvailablePort
|
||||||
import threading
|
import threading
|
||||||
import csdr
|
import csdr
|
||||||
import time
|
import time
|
||||||
@ -105,15 +106,10 @@ class SdrSource(object):
|
|||||||
self.profile_id = None
|
self.profile_id = None
|
||||||
self.activateProfile()
|
self.activateProfile()
|
||||||
self.rtlProps = self.props.collect(
|
self.rtlProps = self.props.collect(
|
||||||
"samp_rate", "nmux_memory", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain"
|
*self.getEventNames()
|
||||||
).defaults(PropertyManager.getSharedInstance())
|
).defaults(PropertyManager.getSharedInstance())
|
||||||
|
self.wireEvents()
|
||||||
|
|
||||||
def restart(name, value):
|
|
||||||
logger.debug("restarting sdr source due to property change: {0} changed to {1}".format(name, value))
|
|
||||||
self.stop()
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
self.rtlProps.wire(restart)
|
|
||||||
self.port = port
|
self.port = port
|
||||||
self.monitor = None
|
self.monitor = None
|
||||||
self.clients = []
|
self.clients = []
|
||||||
@ -123,6 +119,17 @@ class SdrSource(object):
|
|||||||
self.modificationLock = threading.Lock()
|
self.modificationLock = threading.Lock()
|
||||||
self.failed = False
|
self.failed = False
|
||||||
|
|
||||||
|
def getEventNames(self):
|
||||||
|
return ["samp_rate", "nmux_memory", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain"]
|
||||||
|
|
||||||
|
def wireEvents(self):
|
||||||
|
def restart(name, value):
|
||||||
|
logger.debug("restarting sdr source due to property change: {0} changed to {1}".format(name, value))
|
||||||
|
self.stop()
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
self.rtlProps.wire(restart)
|
||||||
|
|
||||||
# override this in subclasses
|
# override this in subclasses
|
||||||
def getCommand(self):
|
def getCommand(self):
|
||||||
pass
|
pass
|
||||||
@ -164,6 +171,9 @@ class SdrSource(object):
|
|||||||
def getPort(self):
|
def getPort(self):
|
||||||
return self.port
|
return self.port
|
||||||
|
|
||||||
|
def useNmux(self):
|
||||||
|
return True
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.modificationLock.acquire()
|
self.modificationLock.acquire()
|
||||||
if self.monitor:
|
if self.monitor:
|
||||||
@ -172,7 +182,7 @@ class SdrSource(object):
|
|||||||
|
|
||||||
props = self.rtlProps
|
props = self.rtlProps
|
||||||
|
|
||||||
start_sdr_command = self.getCommand().format(
|
cmd = self.getCommand().format(
|
||||||
**props.collect(
|
**props.collect(
|
||||||
"samp_rate", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain"
|
"samp_rate", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain"
|
||||||
).__dict__()
|
).__dict__()
|
||||||
@ -180,25 +190,27 @@ class SdrSource(object):
|
|||||||
|
|
||||||
format_conversion = self.getFormatConversion()
|
format_conversion = self.getFormatConversion()
|
||||||
if format_conversion is not None:
|
if format_conversion is not None:
|
||||||
start_sdr_command += " | " + format_conversion
|
cmd += " | " + format_conversion
|
||||||
|
|
||||||
nmux_bufcnt = nmux_bufsize = 0
|
if self.useNmux():
|
||||||
while nmux_bufsize < props["samp_rate"] / 4:
|
nmux_bufcnt = nmux_bufsize = 0
|
||||||
nmux_bufsize += 4096
|
while nmux_bufsize < props["samp_rate"] / 4:
|
||||||
while nmux_bufsize * nmux_bufcnt < props["nmux_memory"] * 1e6:
|
nmux_bufsize += 4096
|
||||||
nmux_bufcnt += 1
|
while nmux_bufsize * nmux_bufcnt < props["nmux_memory"] * 1e6:
|
||||||
if nmux_bufcnt == 0 or nmux_bufsize == 0:
|
nmux_bufcnt += 1
|
||||||
logger.error(
|
if nmux_bufcnt == 0 or nmux_bufsize == 0:
|
||||||
"Error: nmux_bufsize or nmux_bufcnt is zero. These depend on nmux_memory and samp_rate options in config_webrx.py"
|
logger.error(
|
||||||
|
"Error: nmux_bufsize or nmux_bufcnt is zero. These depend on nmux_memory and samp_rate options in config_webrx.py"
|
||||||
|
)
|
||||||
|
self.modificationLock.release()
|
||||||
|
return
|
||||||
|
logger.debug("nmux_bufsize = %d, nmux_bufcnt = %d" % (nmux_bufsize, nmux_bufcnt))
|
||||||
|
cmd = cmd + " | nmux --bufsize %d --bufcnt %d --port %d --address 127.0.0.1" % (
|
||||||
|
nmux_bufsize,
|
||||||
|
nmux_bufcnt,
|
||||||
|
self.port,
|
||||||
)
|
)
|
||||||
self.modificationLock.release()
|
|
||||||
return
|
|
||||||
logger.debug("nmux_bufsize = %d, nmux_bufcnt = %d" % (nmux_bufsize, nmux_bufcnt))
|
|
||||||
cmd = start_sdr_command + " | nmux --bufsize %d --bufcnt %d --port %d --address 127.0.0.1" % (
|
|
||||||
nmux_bufsize,
|
|
||||||
nmux_bufcnt,
|
|
||||||
self.port,
|
|
||||||
)
|
|
||||||
self.process = subprocess.Popen(cmd, shell=True, preexec_fn=os.setpgrp)
|
self.process = subprocess.Popen(cmd, shell=True, preexec_fn=os.setpgrp)
|
||||||
logger.info("Started rtl source: " + cmd)
|
logger.info("Started rtl source: " + cmd)
|
||||||
|
|
||||||
@ -229,6 +241,8 @@ class SdrSource(object):
|
|||||||
if not available:
|
if not available:
|
||||||
self.failed = True
|
self.failed = True
|
||||||
|
|
||||||
|
self.postStart()
|
||||||
|
|
||||||
self.modificationLock.release()
|
self.modificationLock.release()
|
||||||
|
|
||||||
for c in self.clients:
|
for c in self.clients:
|
||||||
@ -237,6 +251,9 @@ class SdrSource(object):
|
|||||||
else:
|
else:
|
||||||
c.onSdrAvailable()
|
c.onSdrAvailable()
|
||||||
|
|
||||||
|
def postStart(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def isAvailable(self):
|
def isAvailable(self):
|
||||||
return self.monitor is not None
|
return self.monitor is not None
|
||||||
|
|
||||||
@ -390,6 +407,43 @@ class Resampler(SdrSource):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RtlSdrSocketSource(SdrSource):
|
||||||
|
def __init__(self, id, props, port):
|
||||||
|
super().__init__(id, props, port)
|
||||||
|
self.controlSocket = None
|
||||||
|
self.controlPort = getAvailablePort()
|
||||||
|
|
||||||
|
def getEventNames(self):
|
||||||
|
return ["samp_rate", "center_freq", "ppm", "rf_gain"]
|
||||||
|
|
||||||
|
def wireEvents(self):
|
||||||
|
def reconfigure(prop, value):
|
||||||
|
logger.debug("sending property change over control socket: {0} changed to {1}".format(prop, value))
|
||||||
|
self.controlSocket.send("{prop}:{value}\n".format(prop=prop, value=value).encode())
|
||||||
|
|
||||||
|
self.rtlProps.wire(reconfigure)
|
||||||
|
|
||||||
|
def postStart(self):
|
||||||
|
logger.debug("opening control socket...")
|
||||||
|
self.controlSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.controlSocket.connect(("localhost", self.controlPort))
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
super().stop()
|
||||||
|
if self.controlSocket:
|
||||||
|
self.controlSocket.close()
|
||||||
|
self.controlSocket = None
|
||||||
|
|
||||||
|
def getCommand(self):
|
||||||
|
return "rtl_connector -p {port} -c {controlPort}".format(port=self.port, controlPort=self.controlPort) + " -s {samp_rate} -f {center_freq} -g {rf_gain}"
|
||||||
|
|
||||||
|
def getFormatConversion(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def useNmux(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class RtlSdrSource(SdrSource):
|
class RtlSdrSource(SdrSource):
|
||||||
def getCommand(self):
|
def getCommand(self):
|
||||||
return "rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -"
|
return "rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -"
|
||||||
|
Loading…
Reference in New Issue
Block a user