add airspy source; fix offset tuning

This commit is contained in:
Jakob Ketterl 2019-11-21 15:31:37 +01:00
parent 5375580104
commit de51e266f6
5 changed files with 78 additions and 49 deletions

View File

@ -106,7 +106,7 @@ sdrs = {
"type": "rtl_sdr", "type": "rtl_sdr",
"ppm": 0, "ppm": 0,
# you can change this if you use an upconverter. formula is: # you can change this if you use an upconverter. formula is:
# shown_center_freq = center_freq + lfo_offset # center_freq + lfo_offset = actual frequency on the sdr
# "lfo_offset": 0, # "lfo_offset": 0,
"profiles": { "profiles": {
"70cm": { "70cm": {

View File

@ -1046,7 +1046,7 @@ function on_ws_recv(evt) {
starting_mod = config['start_mod']; starting_mod = config['start_mod'];
starting_offset_frequency = config['start_offset_freq']; starting_offset_frequency = config['start_offset_freq'];
bandwidth = config['samp_rate']; bandwidth = config['samp_rate'];
center_freq = config['center_freq'] + config['lfo_offset']; center_freq = config['center_freq'];
fft_size = config['fft_size']; fft_size = config['fft_size'];
fft_fps = config['fft_fps']; fft_fps = config['fft_fps'];
var audio_compression = config['audio_compression']; var audio_compression = config['audio_compression'];

View File

@ -57,7 +57,6 @@ class OpenWebRxReceiverClient(Client):
"waterfall_min_level", "waterfall_min_level",
"waterfall_max_level", "waterfall_max_level",
"waterfall_auto_level_margin", "waterfall_auto_level_margin",
"lfo_offset",
"samp_rate", "samp_rate",
"fft_size", "fft_size",
"fft_fps", "fft_fps",

View File

@ -20,11 +20,12 @@ class FeatureDetector(object):
features = { features = {
"core": ["csdr", "nmux", "nc"], "core": ["csdr", "nmux", "nc"],
"rtl_sdr": ["rtl_sdr"], "rtl_sdr": ["rtl_sdr"],
"rtl_sdr_connector": ["owrx_connector"], "rtl_sdr_connector": ["rtl_connector"],
"sdrplay": ["rx_tools"], "sdrplay": ["rx_tools"],
"sdrplay_connector": ["owrx_connector"], "sdrplay_connector": ["soapy_connector"],
"hackrf": ["hackrf_transfer"], "hackrf": ["hackrf_transfer"],
"airspy": ["airspy_rx"], "airspy": ["airspy_rx"],
"airspy_connector": ["soapy_connector"],
"digital_voice_digiham": ["digiham", "sox"], "digital_voice_digiham": ["digiham", "sox"],
"digital_voice_dsd": ["dsd", "sox", "digiham"], "digital_voice_dsd": ["dsd", "sox", "digiham"],
"wsjt-x": ["wsjtx", "sox"], "wsjt-x": ["wsjtx", "sox"],
@ -194,39 +195,39 @@ class FeatureDetector(object):
True, True,
) )
def has_owrx_connector(self): def _check_connector(self, command):
required_version = LooseVersion("0.1")
owrx_connector_version_regex = re.compile("^owrx-connector version (.*)$")
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
def has_rtl_connector(self):
""" """
The owrx_connector package offers direct interfacing between your hardware and openwebrx. It allows quicker 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. 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 You can get it here: https://github.com/jketterl/owrx_connector
""" """
required_version = LooseVersion("0.1") return self._check_connector("rtl_connector")
owrx_connector_version_regex = re.compile("^owrx-connector version (.*)$") def has_soapy_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.
def check_owrx_connector_version(command): You can get it here: https://github.com/jketterl/owrx_connector
try: """
process = subprocess.Popen([command, "--version"], stdout=subprocess.PIPE) return self._check_connector("soapy_connector")
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):
""" """

View File

@ -136,7 +136,7 @@ class SdrSource(object):
self.busyState = SdrSource.BUSYSTATE_IDLE self.busyState = SdrSource.BUSYSTATE_IDLE
def getEventNames(self): def getEventNames(self):
return ["samp_rate", "nmux_memory", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain"] return ["samp_rate", "nmux_memory", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain", "lfo_offset"]
def wireEvents(self): def wireEvents(self):
def restart(name, value): def restart(name, value):
@ -190,6 +190,14 @@ class SdrSource(object):
def useNmux(self): def useNmux(self):
return True return True
def getCommandValues(self):
dict = self.rtlProps.collect(*self.getEventNames()).__dict__()
if "lfo_offset" in dict:
dict["tuner_freq"] = dict["center_freq"] + dict["lfo_offset"]
else:
dict["tuner_freq"] = dict["center_freq"]
return dict
def start(self): def start(self):
self.modificationLock.acquire() self.modificationLock.acquire()
if self.monitor: if self.monitor:
@ -199,7 +207,7 @@ class SdrSource(object):
props = self.rtlProps props = self.rtlProps
cmd = self.getCommand().format( cmd = self.getCommand().format(
**props.collect(*self.getEventNames()).__dict__() **self.getCommandValues()
) )
format_conversion = self.getFormatConversion() format_conversion = self.getFormatConversion()
@ -447,8 +455,11 @@ class ConnectorSource(SdrSource):
def reconfigure(prop, value): def reconfigure(prop, value):
if self.monitor is None: if self.monitor is None:
return return
logger.debug("sending property change over control socket: {0} changed to {1}".format(prop, value)) v = value
self.controlSocket.sendall("{prop}:{value}\n".format(prop=prop, value=value).encode()) if prop == "center_freq" and "lfo_offset" in self.rtlProps:
v = value + self.rtlProps["lfo_offset"]
logger.debug("sending property change over control socket: {0} changed to {1}".format(prop, v))
self.controlSocket.sendall("{prop}:{value}\n".format(prop=prop, value=v).encode())
self.rtlProps.wire(reconfigure) self.rtlProps.wire(reconfigure)
@ -472,27 +483,45 @@ class ConnectorSource(SdrSource):
class RtlSdrConnectorSource(ConnectorSource): class RtlSdrConnectorSource(ConnectorSource):
def getEventNames(self): def getEventNames(self):
return ["samp_rate", "center_freq", "ppm", "rf_gain", "device"] return ["samp_rate", "center_freq", "ppm", "rf_gain", "device", "iqswap", "lfo_offset"]
def getCommand(self): 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} -P {ppm} -d {device}" 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} -d {device}"
class SdrplayConnectorSource(ConnectorSource):
def getEventNames(self):
return ["samp_rate", "center_freq", "ppm", "rf_gain", "antenna", "device", "iqswap"]
def getCommand(self):
cmd = "soapy_connector -p {port} -c {controlPort}".format(port=self.port, controlPort=self.controlPort) +\
" -s {samp_rate} -f {center_freq} -g \"{rf_gain}\" -P {ppm} -a \"{antenna}\" -d \"{device}\""
if self.rtlProps["iqswap"]: if self.rtlProps["iqswap"]:
cmd += " -i" cmd += " -i"
return cmd return cmd
class SdrplayConnectorSource(ConnectorSource):
def getEventNames(self):
return ["samp_rate", "center_freq", "ppm", "rf_gain", "antenna", "device", "iqswap", "lfo_offset"]
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}\""
if self.rtlProps["iqswap"]:
cmd += " -i"
return cmd
class AirspyConnectorSource(ConnectorSource):
def getEventNames(self):
return ["samp_rate", "center_freq", "ppm", "rf_gain", "device", "iqswap", "lfo_offset", "bias_tee"]
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} -d \"{device}\""
if self.rtlProps["iqswap"]:
cmd += " -i"
if self.rtlProps["bias_tee"]:
cmd += " -t biastee=true"
return cmd
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 {tuner_freq} -p {ppm} -g {rf_gain} -"
def getFormatConversion(self): def getFormatConversion(self):
return "csdr convert_u8_f" return "csdr convert_u8_f"
@ -500,7 +529,7 @@ class RtlSdrSource(SdrSource):
class HackrfSource(SdrSource): class HackrfSource(SdrSource):
def getCommand(self): def getCommand(self):
return "hackrf_transfer -s {samp_rate} -f {center_freq} -g {rf_gain} -l{lna_gain} -a{rf_amp} -r-" return "hackrf_transfer -s {samp_rate} -f {tuner_freq} -g {rf_gain} -l{lna_gain} -a{rf_amp} -r-"
def getFormatConversion(self): def getFormatConversion(self):
return "csdr convert_s8_f" return "csdr convert_s8_f"
@ -508,7 +537,7 @@ class HackrfSource(SdrSource):
class SdrplaySource(SdrSource): class SdrplaySource(SdrSource):
def getCommand(self): def getCommand(self):
command = "rx_sdr -F CF32 -s {samp_rate} -f {center_freq} -p {ppm}" command = "rx_sdr -F CF32 -s {samp_rate} -f {tuner_freq} -p {ppm}"
gainMap = {"rf_gain": "RFGR", "if_gain": "IFGR"} gainMap = {"rf_gain": "RFGR", "if_gain": "IFGR"}
gains = [ gains = [
"{0}={{{1}}}".format(gainMap[name], name) "{0}={{{1}}}".format(gainMap[name], name)
@ -528,7 +557,7 @@ class SdrplaySource(SdrSource):
class AirspySource(SdrSource): class AirspySource(SdrSource):
def getCommand(self): def getCommand(self):
frequency = self.props["center_freq"] / 1e6 frequency = self.props["tuner_freq"] / 1e6
command = "airspy_rx" command = "airspy_rx"
command += " -f{0}".format(frequency) command += " -f{0}".format(frequency)
command += " -r /dev/stdout -a{samp_rate} -g {rf_gain}" command += " -r /dev/stdout -a{samp_rate} -g {rf_gain}"