feature detection for digital voice; display modulator buttons only when
available
This commit is contained in:
		| @@ -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"> | ||||||
|   | |||||||
| @@ -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); | ||||||
| 		        } | 		        } | ||||||
|   | |||||||
| @@ -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): | ||||||
|   | |||||||
| @@ -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") | ||||||
		Reference in New Issue
	
	Block a user
	 Jakob Ketterl
					Jakob Ketterl