implement feature and requirement details
This commit is contained in:
parent
e61c0dcc12
commit
823a4a35f0
@ -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>
|
@ -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("")
|
||||||
|
);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
122
owrx/feature.py
122
owrx/feature.py
@ -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,45 +59,84 @@ 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:
|
||||||
methodname = "has_" + requirement
|
passed = passed and self.has_requirement(requirement)
|
||||||
if hasattr(self, methodname) and callable(getattr(self, methodname)):
|
|
||||||
passed = passed and getattr(self, methodname)()
|
|
||||||
else:
|
|
||||||
logger.error("detection of requirement {0} not implement. please fix in code!".format(requirement))
|
|
||||||
return passed
|
return passed
|
||||||
|
|
||||||
|
def _get_requirement_method(self, requirement):
|
||||||
|
methodname = "has_" + requirement
|
||||||
|
if hasattr(self, methodname) and callable(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:
|
||||||
|
logger.error("detection of requirement {0} not implement. please fix in code!".format(requirement))
|
||||||
|
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")
|
||||||
|
|
||||||
"""
|
|
||||||
To use a HackRF, compile the HackRF host tools from its "stdout" branch:
|
|
||||||
git clone https://github.com/mossmann/hackrf/
|
|
||||||
cd hackrf
|
|
||||||
git fetch
|
|
||||||
git checkout origin/stdout
|
|
||||||
cd host
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DINSTALL_UDEV_RULES=ON
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
"""
|
|
||||||
def has_hackrf_transfer(self):
|
def has_hackrf_transfer(self):
|
||||||
|
"""
|
||||||
|
To use a HackRF, compile the HackRF host tools from its "stdout" branch:
|
||||||
|
```
|
||||||
|
git clone https://github.com/mossmann/hackrf/
|
||||||
|
cd hackrf
|
||||||
|
git fetch
|
||||||
|
git checkout origin/stdout
|
||||||
|
cd host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DINSTALL_UDEV_RULES=ON
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
"""
|
||||||
# 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
|
||||||
|
|
||||||
"""
|
|
||||||
To use DMR and YSF, the digiham package is required. You can find the package and installation instructions here:
|
|
||||||
https://github.com/jketterl/digiham
|
|
||||||
|
|
||||||
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.
|
|
||||||
As of now, we require version 0.2 of digiham.
|
|
||||||
"""
|
|
||||||
def has_digiham(self):
|
def has_digiham(self):
|
||||||
|
"""
|
||||||
|
To use digital voice modes, the digiham package is required. You can find the package and installation
|
||||||
|
instructions [here](https://github.com/jketterl/digiham).
|
||||||
|
|
||||||
|
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.
|
||||||
|
As of now, we require version 0.2 of digiham.
|
||||||
|
"""
|
||||||
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")
|
||||||
|
Loading…
Reference in New Issue
Block a user