diff --git a/htdocs/index.html b/htdocs/index.html
index a223eec..0fa04c4 100644
--- a/htdocs/index.html
+++ b/htdocs/index.html
@@ -91,12 +91,16 @@
CW
DMR
DStar
NXDN
YSF
diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js
index f5b7da8..8610778 100644
--- a/htdocs/openwebrx.js
+++ b/htdocs/openwebrx.js
@@ -1233,6 +1233,11 @@ function on_ws_recv(evt)
return '";
}).join("");
break;
+ case "features":
+ for (var feature in json.value) {
+ $('[data-feature="' + feature + '"')[json.value[feature] ? "show" : "hide"]();
+ }
+ break;
default:
console.warn('received message of unknown type: ' + json.type);
}
diff --git a/owrx/connection.py b/owrx/connection.py
index 95ce84f..346f56d 100644
--- a/owrx/connection.py
+++ b/owrx/connection.py
@@ -1,5 +1,6 @@
from owrx.config import PropertyManager
from owrx.source import DspManager, CpuUsageThread, SdrService, ClientRegistry
+from owrx.feature import FeatureDetector
import json
import logging
@@ -33,6 +34,9 @@ class OpenWebRxClient(object):
profiles = [{"name": s.getName() + " " + p["name"], "id":sid + "|" + pid} for (sid, s) in SdrService.getSources().items() for (pid, p) in s.getProfiles().items()]
self.write_profiles(profiles)
+ features = FeatureDetector().feature_availability()
+ self.write_features(features)
+
CpuUsageThread.getSharedInstance().add_client(self)
def sendConfig(self, key, value):
@@ -121,6 +125,8 @@ class OpenWebRxClient(object):
self.protected_send({"type":"receiver_details","value":details})
def write_profiles(self, profiles):
self.protected_send({"type":"profiles","value":profiles})
+ def write_features(self, features):
+ self.protected_send({"type":"features","value":features})
class WebSocketMessageHandler(object):
def __init__(self):
diff --git a/owrx/feature.py b/owrx/feature.py
index 83f9232..457588b 100644
--- a/owrx/feature.py
+++ b/owrx/feature.py
@@ -1,4 +1,7 @@
import os
+import subprocess
+from functools import reduce
+from operator import and_
import logging
logger = logging.getLogger(__name__)
@@ -12,9 +15,13 @@ class FeatureDetector(object):
"core": [ "csdr", "nmux" ],
"rtl_sdr": [ "rtl_sdr" ],
"sdrplay": [ "rx_tools" ],
- "hackrf": [ "hackrf_transfer" ]
+ "hackrf": [ "hackrf_transfer" ],
+ "digital_voice": [ "digiham" ]
}
+ def feature_availability(self):
+ return {name: self.is_available(name) for name in FeatureDetector.features}
+
def is_available(self, feature):
return self.has_requirements(self.get_requirements(feature))
@@ -34,17 +41,20 @@ class FeatureDetector(object):
logger.error("detection of requirement {0} not implement. please fix in code!".format(requirement))
return passed
+ def command_is_runnable(self, command):
+ return os.system("{0} 2>/dev/null >/dev/null".format(command)) != 32512
+
def has_csdr(self):
- return os.system("csdr 2> /dev/null") != 32512
+ return self.command_is_runnable("csdr")
def has_nmux(self):
- return os.system("nmux --help 2> /dev/null") != 32512
+ return self.command_is_runnable("nmux --help")
def has_rtl_sdr(self):
- return os.system("rtl_sdr --help 2> /dev/null") != 32512
+ return self.command_is_runnable("rtl_sdr --help")
def has_rx_tools(self):
- return os.system("rx_sdr --help 2> /dev/null") != 32512
+ return self.command_is_runnable("rx_sdr --help")
"""
To use a HackRF, compile the HackRF host tools from its "stdout" branch:
@@ -62,4 +72,29 @@ class FeatureDetector(object):
def has_hackrf_transfer(self):
# TODO i don't have a hackrf, so somebody doublecheck this.
# TODO also check if it has the stdout feature
- return os.system("hackrf_transfer --help 2> /dev/null") != 32512
+ return self.command_is_runnable("hackrf_transfer --help")
+
+ def command_exists(self, command):
+ return os.system("which {0}".format(command)) == 0
+
+ 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):
+ try:
+ process = subprocess.Popen(command, stdin=subprocess.PIPE)
+ process.communicate("")
+ return process.wait() == 0
+ except FileNotFoundError:
+ return False
+ return reduce(and_,
+ map(
+ check_with_stdin,
+ ["rrc_filter", "ysf_decoder", "dmr_decoder", "mbe_synthesizer"]
+ ),
+ True)
+
+ def has_dsd(self):
+ return self.command_is_runnable("dsd")
+
+ def has_sox(self):
+ return self.command_is_runnable("sox")
\ No newline at end of file