feature detection for digital voice; display modulator buttons only when

available
This commit is contained in:
Jakob Ketterl 2019-05-13 19:19:15 +02:00
parent 823995d4ba
commit 2408d77f15
4 changed files with 56 additions and 6 deletions

View File

@ -91,12 +91,16 @@
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-cw" <div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-cw"
onclick="demodulator_analog_replace('cw');">CW</div> onclick="demodulator_analog_replace('cw');">CW</div>
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-dmr" <div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-dmr"
style="display:none;" data-feature="digital_voice"
onclick="demodulator_analog_replace('dmr');">DMR</div> onclick="demodulator_analog_replace('dmr');">DMR</div>
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-dstar" <div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-dstar"
style="display:none;" data-feature="digital_voice"
onclick="demodulator_analog_replace('dstar');">DStar</div> onclick="demodulator_analog_replace('dstar');">DStar</div>
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-nxdn" <div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-nxdn"
style="display:none;" data-feature="digital_voice"
onclick="demodulator_analog_replace('nxdn');">NXDN</div> onclick="demodulator_analog_replace('nxdn');">NXDN</div>
<div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-ysf" <div class="openwebrx-button openwebrx-demodulator-button" id="openwebrx-button-ysf"
style="display:none;" data-feature="digital_voice"
onclick="demodulator_analog_replace('ysf');">YSF</div> onclick="demodulator_analog_replace('ysf');">YSF</div>
</div> </div>
<div class="openwebrx-panel-line"> <div class="openwebrx-panel-line">

View File

@ -1233,6 +1233,11 @@ function on_ws_recv(evt)
return '<option value="' + profile.id + '">' + profile.name + "</option>"; return '<option value="' + profile.id + '">' + profile.name + "</option>";
}).join(""); }).join("");
break; break;
case "features":
for (var feature in json.value) {
$('[data-feature="' + feature + '"')[json.value[feature] ? "show" : "hide"]();
}
break;
default: default:
console.warn('received message of unknown type: ' + json.type); console.warn('received message of unknown type: ' + json.type);
} }

View File

@ -1,5 +1,6 @@
from owrx.config import PropertyManager from owrx.config import PropertyManager
from owrx.source import DspManager, CpuUsageThread, SdrService, ClientRegistry from owrx.source import DspManager, CpuUsageThread, SdrService, ClientRegistry
from owrx.feature import FeatureDetector
import json import json
import logging 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()] 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) self.write_profiles(profiles)
features = FeatureDetector().feature_availability()
self.write_features(features)
CpuUsageThread.getSharedInstance().add_client(self) CpuUsageThread.getSharedInstance().add_client(self)
def sendConfig(self, key, value): def sendConfig(self, key, value):
@ -121,6 +125,8 @@ class OpenWebRxClient(object):
self.protected_send({"type":"receiver_details","value":details}) self.protected_send({"type":"receiver_details","value":details})
def write_profiles(self, profiles): def write_profiles(self, profiles):
self.protected_send({"type":"profiles","value":profiles}) self.protected_send({"type":"profiles","value":profiles})
def write_features(self, features):
self.protected_send({"type":"features","value":features})
class WebSocketMessageHandler(object): class WebSocketMessageHandler(object):
def __init__(self): def __init__(self):

View File

@ -1,4 +1,7 @@
import os import os
import subprocess
from functools import reduce
from operator import and_
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -12,9 +15,13 @@ class FeatureDetector(object):
"core": [ "csdr", "nmux" ], "core": [ "csdr", "nmux" ],
"rtl_sdr": [ "rtl_sdr" ], "rtl_sdr": [ "rtl_sdr" ],
"sdrplay": [ "rx_tools" ], "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): def is_available(self, feature):
return self.has_requirements(self.get_requirements(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)) logger.error("detection of requirement {0} not implement. please fix in code!".format(requirement))
return passed return passed
def command_is_runnable(self, command):
return os.system("{0} 2>/dev/null >/dev/null".format(command)) != 32512
def has_csdr(self): def has_csdr(self):
return os.system("csdr 2> /dev/null") != 32512 return self.command_is_runnable("csdr")
def has_nmux(self): 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): 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): 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: 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): def has_hackrf_transfer(self):
# TODO i don't have a hackrf, so somebody doublecheck this. # TODO i don't have a hackrf, so somebody doublecheck this.
# TODO also check if it has the stdout feature # 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")