refactor sources to be more flexible
This commit is contained in:
parent
ca4d9771cc
commit
8371d3b67a
66
owrx/command.py
Normal file
66
owrx/command.py
Normal file
@ -0,0 +1,66 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class CommandMapper(object):
|
||||
def __init__(self, base=None, mappings={}, static=None):
|
||||
self.base = base
|
||||
self.mappings = mappings
|
||||
self.static = static
|
||||
|
||||
def map(self, values):
|
||||
args = [self.mappings[k].map(v) for k, v in values.items() if k in self.mappings]
|
||||
args = [a for a in args if a != ""]
|
||||
options = " ".join(args)
|
||||
command = "{0} {1}".format(self.base, options)
|
||||
if self.static is not None:
|
||||
command += " " + self.static
|
||||
return command
|
||||
|
||||
def setMapping(self, key, mapping):
|
||||
self.mappings[key] = mapping
|
||||
return self
|
||||
|
||||
def setMappings(self, mappings):
|
||||
for k, v in mappings.items():
|
||||
self.setMapping(k, v)
|
||||
return self
|
||||
|
||||
def setBase(self, base):
|
||||
self.base = base
|
||||
return self
|
||||
|
||||
def setStatic(self, static):
|
||||
self.static = static
|
||||
return self
|
||||
|
||||
|
||||
class CommandMapping(ABC):
|
||||
@abstractmethod
|
||||
def map(self, value):
|
||||
pass
|
||||
|
||||
|
||||
class Flag(CommandMapping):
|
||||
def __init__(self, flag):
|
||||
self.flag = flag
|
||||
|
||||
def map(self, value):
|
||||
if value is not None and value:
|
||||
return self.flag
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
class Option(CommandMapping):
|
||||
def __init__(self, option):
|
||||
self.option = option
|
||||
|
||||
def map(self, value):
|
||||
if value is not None:
|
||||
if isinstance(value, str) and " " in value:
|
||||
template = "{0} \"{1}\""
|
||||
else:
|
||||
template = "{0} {1}"
|
||||
return template.format(self.option, value)
|
||||
else:
|
||||
return ""
|
@ -248,7 +248,7 @@ class OpenWebRxReceiverClient(Client):
|
||||
# only the keys in the protected property manager can be overridden from the web
|
||||
protected = (
|
||||
self.sdr.getProps()
|
||||
.collect("samp_rate", "center_freq", "rf_gain", "type", "if_gain")
|
||||
.collect("samp_rate", "center_freq", "rf_gain", "type")
|
||||
.defaults(PropertyManager.getSharedInstance())
|
||||
)
|
||||
for key, value in params.items():
|
||||
|
@ -6,13 +6,15 @@ import socket
|
||||
import shlex
|
||||
import time
|
||||
import signal
|
||||
from abc import ABC, abstractmethod
|
||||
from owrx.command import CommandMapper
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SdrSource(object):
|
||||
class SdrSource(ABC):
|
||||
STATE_STOPPED = 0
|
||||
STATE_STARTING = 1
|
||||
STATE_RUNNING = 2
|
||||
@ -34,6 +36,7 @@ class SdrSource(object):
|
||||
self.activateProfile()
|
||||
self.rtlProps = self.props.collect(*self.getEventNames()).defaults(PropertyManager.getSharedInstance())
|
||||
self.wireEvents()
|
||||
self.commandMapper = CommandMapper()
|
||||
|
||||
self.port = port
|
||||
self.monitor = None
|
||||
@ -49,32 +52,24 @@ class SdrSource(object):
|
||||
def getEventNames(self):
|
||||
return [
|
||||
"samp_rate",
|
||||
"nmux_memory",
|
||||
"center_freq",
|
||||
"ppm",
|
||||
"rf_gain",
|
||||
"lna_gain",
|
||||
"rf_amp",
|
||||
"antenna",
|
||||
"if_gain",
|
||||
"lfo_offset",
|
||||
]
|
||||
|
||||
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()
|
||||
def getCommandMapper(self):
|
||||
return self.commandMapper
|
||||
|
||||
self.rtlProps.wire(restart)
|
||||
|
||||
# override this in subclasses
|
||||
def getCommand(self):
|
||||
@abstractmethod
|
||||
def onPropertyChange(self, name, value):
|
||||
pass
|
||||
|
||||
# override this in subclasses, if necessary
|
||||
def getFormatConversion(self):
|
||||
return None
|
||||
def wireEvents(self):
|
||||
self.rtlProps.wire(self.onPropertyChange)
|
||||
|
||||
def getCommand(self):
|
||||
return [self.getCommandMapper().map(self.getCommandValues())]
|
||||
|
||||
def activateProfile(self, profile_id=None):
|
||||
profiles = self.props["profiles"]
|
||||
@ -113,9 +108,6 @@ class SdrSource(object):
|
||||
def getPort(self):
|
||||
return self.port
|
||||
|
||||
def useNmux(self):
|
||||
return True
|
||||
|
||||
def getCommandValues(self):
|
||||
dict = self.rtlProps.collect(*self.getEventNames()).__dict__()
|
||||
if "lfo_offset" in dict and dict["lfo_offset"] is not None:
|
||||
@ -125,81 +117,58 @@ class SdrSource(object):
|
||||
return dict
|
||||
|
||||
def start(self):
|
||||
self.modificationLock.acquire()
|
||||
if self.monitor:
|
||||
self.modificationLock.release()
|
||||
return
|
||||
|
||||
props = self.rtlProps
|
||||
|
||||
cmd = self.getCommand().format(**self.getCommandValues())
|
||||
|
||||
format_conversion = self.getFormatConversion()
|
||||
if format_conversion is not None:
|
||||
cmd += " | " + format_conversion
|
||||
|
||||
if self.useNmux():
|
||||
nmux_bufcnt = nmux_bufsize = 0
|
||||
while nmux_bufsize < props["samp_rate"] / 4:
|
||||
nmux_bufsize += 4096
|
||||
while nmux_bufsize * nmux_bufcnt < props["nmux_memory"] * 1e6:
|
||||
nmux_bufcnt += 1
|
||||
if nmux_bufcnt == 0 or nmux_bufsize == 0:
|
||||
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()
|
||||
with self.modificationLock:
|
||||
if self.monitor:
|
||||
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,
|
||||
)
|
||||
|
||||
# don't use shell mode for commands without piping
|
||||
if "|" in cmd:
|
||||
self.process = subprocess.Popen(cmd, shell=True, preexec_fn=os.setpgrp)
|
||||
else:
|
||||
# preexec_fn can go as soon as there's no piped commands left
|
||||
# the os.killpg call must be replaced with something more reasonable at the same time
|
||||
self.process = subprocess.Popen(shlex.split(cmd), preexec_fn=os.setpgrp)
|
||||
logger.info("Started rtl source: " + cmd)
|
||||
cmd = self.getCommand()
|
||||
cmd = [c for c in cmd if c is not None]
|
||||
|
||||
available = False
|
||||
# don't use shell mode for commands without piping
|
||||
if len(cmd) > 1:
|
||||
# multiple commands with pipes
|
||||
cmd = "|".join(cmd)
|
||||
self.process = subprocess.Popen(cmd, shell=True, preexec_fn=os.setpgrp)
|
||||
else:
|
||||
# single command
|
||||
cmd = cmd[0]
|
||||
# preexec_fn can go as soon as there's no piped commands left
|
||||
# the os.killpg call must be replaced with something more reasonable at the same time
|
||||
self.process = subprocess.Popen(shlex.split(cmd), preexec_fn=os.setpgrp)
|
||||
logger.info("Started rtl source: " + cmd)
|
||||
|
||||
def wait_for_process_to_end():
|
||||
rc = self.process.wait()
|
||||
logger.debug("shut down with RC={0}".format(rc))
|
||||
self.monitor = None
|
||||
available = False
|
||||
|
||||
self.monitor = threading.Thread(target=wait_for_process_to_end)
|
||||
self.monitor.start()
|
||||
def wait_for_process_to_end():
|
||||
rc = self.process.wait()
|
||||
logger.debug("shut down with RC={0}".format(rc))
|
||||
self.monitor = None
|
||||
|
||||
self.monitor = threading.Thread(target=wait_for_process_to_end)
|
||||
self.monitor.start()
|
||||
|
||||
retries = 1000
|
||||
while retries > 0:
|
||||
retries -= 1
|
||||
if self.monitor is None:
|
||||
break
|
||||
testsock = socket.socket()
|
||||
try:
|
||||
testsock.connect(("127.0.0.1", self.getPort()))
|
||||
testsock.close()
|
||||
available = True
|
||||
break
|
||||
except:
|
||||
time.sleep(0.1)
|
||||
|
||||
if not available:
|
||||
self.failed = True
|
||||
|
||||
retries = 1000
|
||||
while retries > 0:
|
||||
retries -= 1
|
||||
if self.monitor is None:
|
||||
break
|
||||
testsock = socket.socket()
|
||||
try:
|
||||
testsock.connect(("127.0.0.1", self.getPort()))
|
||||
testsock.close()
|
||||
available = True
|
||||
break
|
||||
except:
|
||||
time.sleep(0.1)
|
||||
|
||||
if not available:
|
||||
self.failed = True
|
||||
|
||||
try:
|
||||
self.postStart()
|
||||
except Exception:
|
||||
logger.exception("Exception during postStart()")
|
||||
self.failed = True
|
||||
|
||||
self.modificationLock.release()
|
||||
self.postStart()
|
||||
except Exception:
|
||||
logger.exception("Exception during postStart()")
|
||||
self.failed = True
|
||||
|
||||
self.setState(SdrSource.STATE_FAILED if self.failed else SdrSource.STATE_RUNNING)
|
||||
|
||||
@ -215,24 +184,19 @@ class SdrSource(object):
|
||||
def stop(self):
|
||||
self.setState(SdrSource.STATE_STOPPING)
|
||||
|
||||
self.modificationLock.acquire()
|
||||
with self.modificationLock:
|
||||
|
||||
if self.process is not None:
|
||||
try:
|
||||
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
|
||||
except ProcessLookupError:
|
||||
# been killed by something else, ignore
|
||||
pass
|
||||
if self.monitor:
|
||||
self.monitor.join()
|
||||
self.sleepOnRestart()
|
||||
self.modificationLock.release()
|
||||
if self.process is not None:
|
||||
try:
|
||||
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
|
||||
except ProcessLookupError:
|
||||
# been killed by something else, ignore
|
||||
pass
|
||||
if self.monitor:
|
||||
self.monitor.join()
|
||||
|
||||
self.setState(SdrSource.STATE_STOPPED)
|
||||
|
||||
def sleepOnRestart(self):
|
||||
pass
|
||||
|
||||
def hasClients(self, *args):
|
||||
clients = [c for c in self.clients if c.getClientClass() in args]
|
||||
return len(clients) > 0
|
||||
|
@ -8,11 +8,12 @@ class AirspySource(SoapyConnectorSource):
|
||||
def getEventNames(self):
|
||||
return super().getEventNames() + ["bias_tee"]
|
||||
|
||||
'''
|
||||
def getCommand(self):
|
||||
cmd = (
|
||||
cmd = [
|
||||
"soapy_connector -p {port} -c {controlPort}".format(port=self.port, controlPort=self.controlPort)
|
||||
+ ' -s {samp_rate} -f {tuner_freq} -g "{rf_gain}" -P {ppm} -d "{device}"'
|
||||
)
|
||||
]
|
||||
values = self.getCommandValues()
|
||||
if values["iqswap"]:
|
||||
cmd += " -i"
|
||||
@ -21,3 +22,4 @@ class AirspySource(SoapyConnectorSource):
|
||||
if values["bias_tee"]:
|
||||
cmd += " -t biastee=true"
|
||||
return cmd
|
||||
'''
|
||||
|
@ -1,6 +1,7 @@
|
||||
from . import SdrSource
|
||||
from owrx.socket import getAvailablePort
|
||||
import socket
|
||||
from owrx.command import CommandMapper, Flag, Option
|
||||
|
||||
import logging
|
||||
|
||||
@ -12,16 +13,22 @@ class ConnectorSource(SdrSource):
|
||||
super().__init__(id, props, port)
|
||||
self.controlSocket = None
|
||||
self.controlPort = getAvailablePort()
|
||||
self.getCommandMapper().setMappings({
|
||||
"samp_rate": Option("-s"),
|
||||
"tuner_freq": Option("-f"),
|
||||
"port": Option("-p"),
|
||||
"controlPort": Option("-c"),
|
||||
"device": Option("-d"),
|
||||
"iqswap": Flag("-i"),
|
||||
"rtltcp_compat": Flag("-r"),
|
||||
"ppm": Option("-p"),
|
||||
"rf_gain": Option("-g")
|
||||
})
|
||||
|
||||
def getEventNames(self):
|
||||
return [
|
||||
"samp_rate",
|
||||
"center_freq",
|
||||
"ppm",
|
||||
"rf_gain",
|
||||
return super().getEventNames() + [
|
||||
"device",
|
||||
"iqswap",
|
||||
"lfo_offset",
|
||||
"rtltcp_compat",
|
||||
]
|
||||
|
||||
@ -29,21 +36,18 @@ class ConnectorSource(SdrSource):
|
||||
logger.debug("sending property change over control socket: {0} changed to {1}".format(prop, value))
|
||||
self.controlSocket.sendall("{prop}:{value}\n".format(prop=prop, value=value).encode())
|
||||
|
||||
def wireEvents(self):
|
||||
def reconfigure(prop, value):
|
||||
if self.monitor is None:
|
||||
return
|
||||
if (
|
||||
(prop == "center_freq" or prop == "lfo_offset")
|
||||
and "lfo_offset" in self.rtlProps
|
||||
and self.rtlProps["lfo_offset"] is not None
|
||||
):
|
||||
freq = self.rtlProps["center_freq"] + self.rtlProps["lfo_offset"]
|
||||
self.sendControlMessage("center_freq", freq)
|
||||
else:
|
||||
self.sendControlMessage(prop, value)
|
||||
|
||||
self.rtlProps.wire(reconfigure)
|
||||
def onPropertyChange(self, prop, value):
|
||||
if self.monitor is None:
|
||||
return
|
||||
if (
|
||||
(prop == "center_freq" or prop == "lfo_offset")
|
||||
and "lfo_offset" in self.rtlProps
|
||||
and self.rtlProps["lfo_offset"] is not None
|
||||
):
|
||||
freq = self.rtlProps["center_freq"] + self.rtlProps["lfo_offset"]
|
||||
self.sendControlMessage("center_freq", freq)
|
||||
else:
|
||||
self.sendControlMessage(prop, value)
|
||||
|
||||
def postStart(self):
|
||||
logger.debug("opening control socket...")
|
||||
@ -56,8 +60,11 @@ class ConnectorSource(SdrSource):
|
||||
self.controlSocket.close()
|
||||
self.controlSocket = None
|
||||
|
||||
def getFormatConversion(self):
|
||||
return None
|
||||
def getControlPort(self):
|
||||
return self.controlPort
|
||||
|
||||
def useNmux(self):
|
||||
return False
|
||||
def getCommandValues(self):
|
||||
values = super().getCommandValues()
|
||||
values["port"] = self.getPort()
|
||||
values["controlPort"] = self.getControlPort()
|
||||
return values
|
||||
|
53
owrx/source/direct.py
Normal file
53
owrx/source/direct.py
Normal file
@ -0,0 +1,53 @@
|
||||
from abc import ABCMeta
|
||||
from . import SdrSource
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DirectSource(SdrSource, metaclass=ABCMeta):
|
||||
def onPropertyChange(self, name, value):
|
||||
logger.debug("restarting sdr source due to property change: {0} changed to {1}".format(name, value))
|
||||
self.stop()
|
||||
self.sleepOnRestart()
|
||||
self.start()
|
||||
|
||||
def getEventNames(self):
|
||||
return super().getEventNames() + [
|
||||
"nmux_memory",
|
||||
]
|
||||
|
||||
def getNmuxCommand(self):
|
||||
props = self.rtlProps
|
||||
|
||||
nmux_bufcnt = nmux_bufsize = 0
|
||||
while nmux_bufsize < props["samp_rate"] / 4:
|
||||
nmux_bufsize += 4096
|
||||
while nmux_bufsize * nmux_bufcnt < props["nmux_memory"] * 1e6:
|
||||
nmux_bufcnt += 1
|
||||
if nmux_bufcnt == 0 or nmux_bufsize == 0:
|
||||
raise ValueError(
|
||||
"Error: nmux_bufsize or nmux_bufcnt is zero. "
|
||||
"These depend on nmux_memory and samp_rate options in config_webrx.py"
|
||||
)
|
||||
|
||||
return "nmux --bufsize %d --bufcnt %d --port %d --address 127.0.0.1" % (
|
||||
nmux_bufsize,
|
||||
nmux_bufcnt,
|
||||
self.port,
|
||||
)
|
||||
|
||||
def getCommand(self):
|
||||
return super().getCommand() + [
|
||||
self.getFormatConversion(),
|
||||
self.getNmuxCommand(),
|
||||
]
|
||||
|
||||
# override this in subclasses, if necessary
|
||||
def getFormatConversion(self):
|
||||
return None
|
||||
|
||||
# override this in subclasses, if necessary
|
||||
def sleepOnRestart(self):
|
||||
pass
|
@ -1,9 +1,19 @@
|
||||
from . import SdrSource
|
||||
from owrx.command import Option
|
||||
from .direct import DirectSource
|
||||
|
||||
|
||||
class FifiSdrSource(SdrSource):
|
||||
def getCommand(self):
|
||||
return "arecord -D hw:2,0 -f S16_LE -r {samp_rate} -c2 -"
|
||||
class FifiSdrSource(DirectSource):
|
||||
def __init__(self, id, props, port):
|
||||
super().__init__(id, props, port)
|
||||
self.getCommandMapper().setBase("arecord").setMappings({
|
||||
"device": Option("-D"),
|
||||
"samp_rate": Option("-r")
|
||||
}).setStatic("-f S16_LE -c2 -")
|
||||
|
||||
def getEventNames(self):
|
||||
return super().getEventNames() + [
|
||||
"device"
|
||||
]
|
||||
|
||||
def getFormatConversion(self):
|
||||
return "csdr convert_s16_f | csdr gain_ff 30"
|
||||
|
@ -1,9 +1,23 @@
|
||||
from . import SdrSource
|
||||
from .direct import DirectSource
|
||||
from owrx.command import Flag, Option
|
||||
|
||||
|
||||
class HackrfSource(SdrSource):
|
||||
def getCommand(self):
|
||||
return "hackrf_transfer -s {samp_rate} -f {tuner_freq} -g {rf_gain} -l{lna_gain} -a{rf_amp} -r-"
|
||||
class HackrfSource(DirectSource):
|
||||
def __init__(self, id, props, port):
|
||||
super().__init__(id, props, port)
|
||||
self.getCommandMapper().setBase("hackrf_transfer").setMappings({
|
||||
"samp_rate": Option("-s"),
|
||||
"tuner_freq": Option("-f"),
|
||||
"rf_gain": Option("-g"),
|
||||
"lna_gain": Option("-l"),
|
||||
"rf_amp": Option("-a")
|
||||
}).setStatic("-r-")
|
||||
|
||||
def getEventNames(self):
|
||||
return super().getEventNames() + [
|
||||
"lna_gain",
|
||||
"rf_amp",
|
||||
]
|
||||
|
||||
def getFormatConversion(self):
|
||||
return "csdr convert_s8_f"
|
||||
|
@ -1,3 +1,4 @@
|
||||
from .direct import DirectSource
|
||||
from . import SdrSource
|
||||
import subprocess
|
||||
import threading
|
||||
@ -10,7 +11,10 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Resampler(SdrSource):
|
||||
class Resampler(DirectSource):
|
||||
def onPropertyChange(self, name, value):
|
||||
logger.warning("Resampler is unable to handle property change ({0} changed to {1})".format(name, value))
|
||||
|
||||
def __init__(self, props, port, sdr):
|
||||
sdrProps = sdr.getProps()
|
||||
self.shift = (sdrProps["center_freq"] - props["center_freq"]) / sdrProps["samp_rate"]
|
||||
@ -22,77 +26,16 @@ class Resampler(SdrSource):
|
||||
self.sdr = sdr
|
||||
super().__init__(None, props, port)
|
||||
|
||||
def start(self):
|
||||
if self.isFailed():
|
||||
return
|
||||
|
||||
self.modificationLock.acquire()
|
||||
if self.monitor:
|
||||
self.modificationLock.release()
|
||||
return
|
||||
|
||||
self.setState(SdrSource.STATE_STARTING)
|
||||
|
||||
props = self.rtlProps
|
||||
|
||||
resampler_command = [
|
||||
def getCommand(self):
|
||||
return [
|
||||
"nc -v 127.0.0.1 {nc_port}".format(nc_port=self.sdr.getPort()),
|
||||
"csdr shift_addition_cc {shift}".format(shift=self.shift),
|
||||
"csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING".format(
|
||||
decimation=self.decimation, ddc_transition_bw=self.transition_bw
|
||||
),
|
||||
self.getNmuxCommand()
|
||||
]
|
||||
|
||||
nmux_bufcnt = nmux_bufsize = 0
|
||||
while nmux_bufsize < props["samp_rate"] / 4:
|
||||
nmux_bufsize += 4096
|
||||
while nmux_bufsize * nmux_bufcnt < props["nmux_memory"] * 1e6:
|
||||
nmux_bufcnt += 1
|
||||
if nmux_bufcnt == 0 or nmux_bufsize == 0:
|
||||
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))
|
||||
resampler_command += [
|
||||
"nmux --bufsize %d --bufcnt %d --port %d --address 127.0.0.1" % (nmux_bufsize, nmux_bufcnt, self.port)
|
||||
]
|
||||
cmd = " | ".join(resampler_command)
|
||||
logger.debug("resampler command: %s", cmd)
|
||||
self.process = subprocess.Popen(cmd, shell=True, preexec_fn=os.setpgrp)
|
||||
logger.info("Started resampler source: " + cmd)
|
||||
|
||||
available = False
|
||||
|
||||
def wait_for_process_to_end():
|
||||
rc = self.process.wait()
|
||||
logger.debug("shut down with RC={0}".format(rc))
|
||||
self.monitor = None
|
||||
|
||||
self.monitor = threading.Thread(target=wait_for_process_to_end)
|
||||
self.monitor.start()
|
||||
|
||||
retries = 1000
|
||||
while retries > 0:
|
||||
retries -= 1
|
||||
if self.monitor is None:
|
||||
break
|
||||
testsock = socket.socket()
|
||||
try:
|
||||
testsock.connect(("127.0.0.1", self.getPort()))
|
||||
testsock.close()
|
||||
available = True
|
||||
break
|
||||
except:
|
||||
time.sleep(0.1)
|
||||
|
||||
if not available:
|
||||
self.failed = True
|
||||
|
||||
self.modificationLock.release()
|
||||
|
||||
self.setState(SdrSource.STATE_FAILED if self.failed else SdrSource.STATE_RUNNING)
|
||||
|
||||
def activateProfile(self, profile_id=None):
|
||||
logger.warning("Resampler does not support setting profiles")
|
||||
pass
|
||||
|
@ -2,15 +2,6 @@ from .connector import ConnectorSource
|
||||
|
||||
|
||||
class RtlSdrSource(ConnectorSource):
|
||||
def getCommand(self):
|
||||
cmd = (
|
||||
"rtl_connector -p {port} -c {controlPort}".format(port=self.port, controlPort=self.controlPort)
|
||||
+ " -s {samp_rate} -f {tuner_freq} -g {rf_gain} -P {ppm}"
|
||||
)
|
||||
if "device" in self.rtlProps and self.rtlProps["device"] is not None:
|
||||
cmd += ' -d "{device}"'
|
||||
if self.rtlProps["iqswap"]:
|
||||
cmd += " -i"
|
||||
if self.rtlProps["rtltcp_compat"]:
|
||||
cmd += " -r"
|
||||
return cmd
|
||||
def __init__(self, id, props, port):
|
||||
super().__init__(id, props, port)
|
||||
self.getCommandMapper().setBase("rtl_connector")
|
||||
|
@ -4,18 +4,3 @@ from .soapy import SoapyConnectorSource
|
||||
class SdrplaySource(SoapyConnectorSource):
|
||||
def getDriver(self):
|
||||
return "sdrplay"
|
||||
|
||||
def getEventNames(self):
|
||||
return super().getEventNames() + ["antenna"]
|
||||
|
||||
def getCommand(self):
|
||||
cmd = (
|
||||
"soapy_connector -p {port} -c {controlPort}".format(port=self.port, controlPort=self.controlPort)
|
||||
+ ' -s {samp_rate} -f {tuner_freq} -g "{rf_gain}" -P {ppm} -a "{antenna}" -d "{device}"'
|
||||
)
|
||||
values = self.getCommandValues()
|
||||
if values["iqswap"]:
|
||||
cmd += " -i"
|
||||
if self.rtlProps["rtltcp_compat"]:
|
||||
cmd += " -r"
|
||||
return cmd
|
||||
|
@ -1,14 +1,29 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from owrx.command import Option
|
||||
|
||||
from .connector import ConnectorSource
|
||||
|
||||
|
||||
class SoapyConnectorSource(ConnectorSource):
|
||||
class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta):
|
||||
def __init__(self, id, props, port):
|
||||
super().__init__(id, props, port)
|
||||
self.getCommandMapper().setBase("soapy_connector").setMappings({
|
||||
"antenna": Option("-a")
|
||||
})
|
||||
|
||||
"""
|
||||
must be implemented by child classes to be able to build a driver-based device selector by default.
|
||||
return value must be the corresponding soapy driver identifier.
|
||||
"""
|
||||
@abstractmethod
|
||||
def getDriver(self):
|
||||
pass
|
||||
|
||||
def getEventNames(self):
|
||||
return super().getEventNames() + [
|
||||
"antenna",
|
||||
]
|
||||
|
||||
def parseDeviceString(self, dstr):
|
||||
|
||||
def decodeComponent(c):
|
||||
|
Loading…
Reference in New Issue
Block a user