implement feature and requirement details

This commit is contained in:
Jakob Ketterl 2019-07-05 22:31:46 +02:00
parent e61c0dcc12
commit 823a4a35f0
3 changed files with 126 additions and 28 deletions

View File

@ -1,6 +1,17 @@
<HTML><HEAD> <HTML><HEAD>
<TITLE>OpenWebRX Feature report</TITLE> <TITLE>OpenWebRX Feature report</TITLE>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.0/showdown.min.js"></script>
<script src="static/jquery-3.2.1.min.js"></script> <script src="static/jquery-3.2.1.min.js"></script>
<script src="static/features.js"></script> <script src="static/features.js"></script>
</HEAD><BODY> </HEAD><BODY>
<h1>OpenWebRX Feature Report</h1>
<table class="features table">
<tr>
<th>Feature</th>
<th>Requirement</th>
<th>Description</th>
<th>Available</th>
</tr>
</table>
</BODY></HTML> </BODY></HTML>

View File

@ -1,5 +1,24 @@
$(function(){ $(function(){
var converter = new showdown.Converter();
$.ajax('/api/features').done(function(data){ $.ajax('/api/features').done(function(data){
$('body').html(JSON.stringify(data)); $table = $('table.features');
$.each(data, function(name, details) {
requirements = $.map(details.requirements, function(r, name){
return '<tr>' +
'<td></td>' +
'<td>' + name + '</td>' +
'<td>' + converter.makeHtml(r.description) + '</td>' +
'<td>' + (r.available ? 'YES' : 'NO') + '</td>' +
'</tr>';
});
$table.append(
'<tr>' +
'<td colspan=2>' + name + '</td>' +
'<td>' + converter.makeHtml(details.description) + '</td>' +
'<td>' + (details.available ? 'YES' : 'NO') + '</td>' +
'</tr>' +
requirements.join("")
);
})
}); });
}); });

View File

@ -4,6 +4,7 @@ from functools import reduce
from operator import and_ from operator import and_
import re import re
from distutils.version import LooseVersion from distutils.version import LooseVersion
import inspect
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -12,6 +13,7 @@ logger = logging.getLogger(__name__)
class UnknownFeatureException(Exception): class UnknownFeatureException(Exception):
pass pass
class FeatureDetector(object): class FeatureDetector(object):
features = { features = {
"core": [ "csdr", "nmux", "nc" ], "core": [ "csdr", "nmux", "nc" ],
@ -27,8 +29,22 @@ class FeatureDetector(object):
return {name: self.is_available(name) for name in FeatureDetector.features} return {name: self.is_available(name) for name in FeatureDetector.features}
def feature_report(self): def feature_report(self):
def requirement_details(name):
available = self.has_requirement(name)
return {
"available": available,
# as of now, features are always enabled as soon as they are available. this may change in the future.
"enabled": available,
"description": self.get_requirement_description(name)
}
def feature_details(name): def feature_details(name):
return self.get_requirements(name) return {
"description": "",
"available": self.is_available(name),
"requirements": {name: requirement_details(name) for name in self.get_requirements(name)}
}
return {name: feature_details(name) for name in FeatureDetector.features} return {name: feature_details(name) for name in FeatureDetector.features}
def is_available(self, feature): def is_available(self, feature):
@ -43,33 +59,72 @@ class FeatureDetector(object):
def has_requirements(self, requirements): def has_requirements(self, requirements):
passed = True passed = True
for requirement in requirements: for requirement in requirements:
passed = passed and self.has_requirement(requirement)
return passed
def _get_requirement_method(self, requirement):
methodname = "has_" + requirement methodname = "has_" + requirement
if hasattr(self, methodname) and callable(getattr(self, methodname)): if hasattr(self, methodname) and callable(getattr(self, methodname)):
passed = passed and getattr(self, methodname)() return getattr(self, methodname)
return None
def has_requirement(self, requirement):
method = self._get_requirement_method(requirement)
if method is not None:
return method()
else: else:
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 False
def get_requirement_description(self, requirement):
return inspect.getdoc(self._get_requirement_method(requirement))
def command_is_runnable(self, command): def command_is_runnable(self, command):
return os.system("{0} 2>/dev/null >/dev/null".format(command)) != 32512 return os.system("{0} 2>/dev/null >/dev/null".format(command)) != 32512
def has_csdr(self): def has_csdr(self):
"""
OpenWebRX uses the demodulator and pipeline tools provided by the csdr project. Please check out [the project
page on github](https://github.com/simonyiszk/csdr) for further details and installation instructions.
"""
return self.command_is_runnable("csdr") return self.command_is_runnable("csdr")
def has_nmux(self): def has_nmux(self):
"""
Nmux is another tool provided by the csdr project. It is used for internal multiplexing of the IQ data streams.
If you're missing nmux even though you have csdr installed, please update your csdr version.
"""
return self.command_is_runnable("nmux --help") return self.command_is_runnable("nmux --help")
def has_nc(self): def has_nc(self):
"""
Nc is the client used to connect to the nmux multiplexer. It is provided by either the BSD netcat (recommended
for better performance) or GNU netcat packages. Please check your distribution package manager for options.
"""
return self.command_is_runnable('nc --help') return self.command_is_runnable('nc --help')
def has_rtl_sdr(self): def has_rtl_sdr(self):
"""
The rtl-sdr command is required to read I/Q data from an RTL SDR USB-Stick. It is available in most
distribution package managers.
"""
return self.command_is_runnable("rtl_sdr --help") return self.command_is_runnable("rtl_sdr --help")
def has_rx_tools(self): def has_rx_tools(self):
"""
The rx_tools package can be used to interface with SDR devices compatible with SoapySDR. It is currently used
to connect to SDRPlay devices. Please check the following pages for more details:
* [rx_tools GitHub page](https://github.com/rxseger/rx_tools)
* [SoapySDR Project wiki](https://github.com/pothosware/SoapySDR/wiki)
* [SDRPlay homepage](https://www.sdrplay.com/)
"""
return self.command_is_runnable("rx_sdr --help") return self.command_is_runnable("rx_sdr --help")
def has_hackrf_transfer(self):
""" """
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:
```
git clone https://github.com/mossmann/hackrf/ git clone https://github.com/mossmann/hackrf/
cd hackrf cd hackrf
git fetch git fetch
@ -80,8 +135,8 @@ class FeatureDetector(object):
cmake .. -DINSTALL_UDEV_RULES=ON cmake .. -DINSTALL_UDEV_RULES=ON
make make
sudo make install sudo make install
```
""" """
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 self.command_is_runnable("hackrf_transfer --help") return self.command_is_runnable("hackrf_transfer --help")
@ -89,15 +144,15 @@ class FeatureDetector(object):
def command_exists(self, command): def command_exists(self, command):
return os.system("which {0}".format(command)) == 0 return os.system("which {0}".format(command)) == 0
def has_digiham(self):
""" """
To use DMR and YSF, the digiham package is required. You can find the package and installation instructions here: To use digital voice modes, the digiham package is required. You can find the package and installation
https://github.com/jketterl/digiham instructions [here](https://github.com/jketterl/digiham).
Please note: there is close interaction between digiham and openwebrx, so older versions will probably not work. Please note: there is close interaction between digiham and openwebrx, so older versions will probably not work.
If you have an older verison of digiham installed, please update it along with openwebrx. If you have an older verison of digiham installed, please update it along with openwebrx.
As of now, we require version 0.2 of digiham. As of now, we require version 0.2 of digiham.
""" """
def has_digiham(self):
required_version = LooseVersion("0.2") required_version = LooseVersion("0.2")
digiham_version_regex = re.compile('^digiham version (.*)$') digiham_version_regex = re.compile('^digiham version (.*)$')
@ -118,10 +173,23 @@ class FeatureDetector(object):
True) True)
def has_dsd(self): def has_dsd(self):
"""
The digital voice modes NXDN and D-Star can be decoded by the dsd project. Please note that you need the version
modified by F4EXB that provides stdin/stdout support. You can find it [here](https://github.com/f4exb/dsd).
"""
return self.command_is_runnable("dsd") return self.command_is_runnable("dsd")
def has_sox(self): def has_sox(self):
"""
The sox audio library is used to convert between the typical 8 kHz audio sampling rate used by digital modes and
the audio sampling rate requested by the client.
It is available for most distributions through the respective package manager.
"""
return self.command_is_runnable("sox") return self.command_is_runnable("sox")
def has_airspy_rx(self): def has_airspy_rx(self):
"""
In order to use an Airspy Receiver, you need to install the airspy_rx receiver software.
"""
return self.command_is_runnable("airspy_rx --help 2> /dev/null") return self.command_is_runnable("airspy_rx --help 2> /dev/null")