test sdrs and their availability early on

use polymorphism to load sdrs in
This commit is contained in:
Jakob Ketterl 2019-05-10 14:23:54 +02:00
parent 56ef86aab6
commit bbd6412e3d
3 changed files with 67 additions and 34 deletions

View File

@ -81,6 +81,9 @@ class PropertyManager(object):
p.setValue(other_pm[key]) p.setValue(other_pm[key])
return self return self
class UnknownFeatureException(Exception):
pass
class RequirementMissingException(Exception): class RequirementMissingException(Exception):
pass pass
@ -96,7 +99,10 @@ class FeatureDetector(object):
return self.has_requirements(self.get_requirements(feature)) return self.has_requirements(self.get_requirements(feature))
def get_requirements(self, feature): def get_requirements(self, feature):
try:
return FeatureDetector.features[feature] return FeatureDetector.features[feature]
except KeyError:
raise UnknownFeatureException("Feature \"{0}\" is not known.".format(feature))
def has_requirements(self, requirements): def has_requirements(self, requirements):
passed = True passed = True

View File

@ -1,10 +1,11 @@
import subprocess import subprocess
from owrx.config import PropertyManager, FeatureDetector from owrx.config import PropertyManager, FeatureDetector, UnknownFeatureException
import threading import threading
import csdr import csdr
import time import time
import os import os
import signal import signal
import sys
class SdrService(object): class SdrService(object):
sdrProps = None sdrProps = None
@ -22,39 +23,42 @@ class SdrService(object):
raise IndexError("no more available ports to start more sdrs") raise IndexError("no more available ports to start more sdrs")
return SdrService.lastPort return SdrService.lastPort
@staticmethod @staticmethod
def getSource(id = None): def loadProps():
if SdrService.sdrProps is None: if SdrService.sdrProps is None:
pm = PropertyManager.getSharedInstance() pm = PropertyManager.getSharedInstance()
featureDetector = FeatureDetector()
def loadIntoPropertyManager(dict: dict): def loadIntoPropertyManager(dict: dict):
propertyManager = PropertyManager() propertyManager = PropertyManager()
for (name, value) in dict.items(): for (name, value) in dict.items():
propertyManager[name] = value propertyManager[name] = value
return propertyManager return propertyManager
SdrService.sdrProps = dict((name, loadIntoPropertyManager(value)) for (name, value) in pm["sdrs"].items()) def sdrTypeAvailable(value):
print(SdrService.sdrProps) try:
if not featureDetector.is_available(value["type"]):
print("The RTL source type \"{0}\" is not available. please check requirements.".format(value["type"]))
return False
return True
except UnknownFeatureException:
print("The RTL source type \"{0}\" is invalid. Please check your configuration".format(value["type"]))
return False
# transform all dictionary items into PropertyManager object, filtering out unavailable ones
SdrService.sdrProps = {
name: loadIntoPropertyManager(value) for (name, value) in pm["sdrs"].items() if sdrTypeAvailable(value)
}
print("SDR sources loaded. Availables SDRs: {0}".format(", ".join(map(lambda x: x["name"], SdrService.sdrProps.values()))))
@staticmethod
def getSource(id = None):
SdrService.loadProps()
if id is None: if id is None:
# TODO: configure default sdr in config? right now it will pick the first one off the list. # TODO: configure default sdr in config? right now it will pick the first one off the list.
id = list(SdrService.sdrProps.keys())[0] id = list(SdrService.sdrProps.keys())[0]
if not id in SdrService.sources: if not id in SdrService.sources:
SdrService.sources[id] = SdrSource(SdrService.sdrProps[id], SdrService.getNextPort()) props = SdrService.sdrProps[id]
className = ''.join(x for x in props["type"].title() if x.isalnum()) + "Source"
cls = getattr(sys.modules[__name__], className)
SdrService.sources[id] = cls(props, SdrService.getNextPort())
return SdrService.sources[id] return SdrService.sources[id]
sdr_types = {
"rtl_sdr": {
"command": "rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -",
"format_conversion": "csdr convert_u8_f"
},
"hackrf": {
"command": "hackrf_transfer -s {samp_rate} -f {center_freq} -g {rf_gain} -l{lna_gain} -a{rf_amp} -r-",
"format_conversion": "csdr convert_s8_f"
},
"sdrplay": {
"command": "rx_sdr -F CF32 -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -",
"format_conversion": None,
"sleep": 1
}
}
class SdrSource(object): class SdrSource(object):
def __init__(self, props, port): def __init__(self, props, port):
self.props = props self.props = props
@ -70,6 +74,10 @@ class SdrSource(object):
self.port = port self.port = port
self.monitor = None self.monitor = None
# override these in subclasses as necessary
self.command = None
self.format_conversion = None
def getProps(self): def getProps(self):
return self.props return self.props
@ -81,14 +89,7 @@ class SdrSource(object):
props = self.rtlProps props = self.rtlProps
featureDetector = FeatureDetector() start_sdr_command = self.command.format(
if not featureDetector.is_available(props["type"]):
print("The RTL source type {0} is not available. please check requirements.".format(props["rtl_type"]))
return
self.params = sdr_types[props["type"]]
start_sdr_command = self.params["command"].format(
samp_rate = props["samp_rate"], samp_rate = props["samp_rate"],
center_freq = props["center_freq"], center_freq = props["center_freq"],
ppm = props["ppm"], ppm = props["ppm"],
@ -97,8 +98,8 @@ class SdrSource(object):
rf_amp = props["rf_amp"] rf_amp = props["rf_amp"]
) )
if self.params["format_conversion"] is not None: if self.format_conversion is not None:
start_sdr_command += " | " + self.params["format_conversion"] start_sdr_command += " | " + self.format_conversion
nmux_bufcnt = nmux_bufsize = 0 nmux_bufcnt = nmux_bufsize = 0
while nmux_bufsize < props["samp_rate"]/4: nmux_bufsize += 4096 while nmux_bufsize < props["samp_rate"]/4: nmux_bufsize += 4096
@ -125,8 +126,31 @@ class SdrSource(object):
def stop(self): def stop(self):
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM) os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
self.monitor.join() self.monitor.join()
if "sleep" in self.params: self.sleepOnRestart()
time.sleep(self.params["sleep"])
def sleepOnRestart(self):
pass
class RtlSdrSource(SdrSource):
def __init__(self, props, port):
super().__init__(props, port)
self.command = "rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -"
self.format_conversion = "csdr convert_u8_f"
class HackrfSource(SdrSource):
def __init__(self, props, port):
super().__init__(props, port)
self.command = "hackrf_transfer -s {samp_rate} -f {center_freq} -g {rf_gain} -l{lna_gain} -a{rf_amp} -r-"
self.format_conversion = "csdr convert_s8_f"
class SdrplaySource(SdrSource):
def __init__(self, props, port):
super().__init__(props, port)
self.command = "rx_sdr -F CF32 -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -"
self.format_conversion = None
def sleepOnRestart(self):
time.sleep(1)
class SpectrumThread(object): class SpectrumThread(object):
def __init__(self, sdrSource): def __init__(self, sdrSource):

View File

@ -28,6 +28,9 @@ def main():
print(", ".join(featureDetector.get_requirements("core"))) print(", ".join(featureDetector.get_requirements("core")))
return return
# Get error messages about unknown / unavailable features as soon as possible
SdrService.loadProps()
server = ThreadedHttpServer(('0.0.0.0', pm.getPropertyValue("web_port")), RequestHandler) server = ThreadedHttpServer(('0.0.0.0', pm.getPropertyValue("web_port")), RequestHandler)
server.serve_forever() server.serve_forever()