diff --git a/htdocs/index.html b/htdocs/index.html
index 77b0fe3..9027ef1 100644
--- a/htdocs/index.html
+++ b/htdocs/index.html
@@ -33,6 +33,7 @@
+
diff --git a/htdocs/lib/Modes.js b/htdocs/lib/Modes.js
new file mode 100644
index 0000000..ea7e8b7
--- /dev/null
+++ b/htdocs/lib/Modes.js
@@ -0,0 +1,28 @@
+var Modes = {
+ modes: [],
+ features: {},
+ setModes:function(json){
+ this.modes = json.map(function(m){ return new Mode(m); });
+ },
+ setFeatures:function(features){
+ this.features = features;
+ },
+ findByModulation:function(modulation){
+ matches = this.modes.filter(function(m) { return m.modulation === modulation; });
+ if (matches.length) return matches[0]
+ }
+}
+
+var Mode = function(json){
+ this.modulation = json.modulation;
+ this.name = json.name;
+ this.requirements = json.requirements;
+};
+
+Mode.prototype.isAvailable = function(){
+ return this.requirements.map(function(r){
+ return Modes.features[r];
+ }).reduce(function(a, b){
+ return a && b;
+ }, true);
+}
\ No newline at end of file
diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js
index 05d4f51..41c91a4 100644
--- a/htdocs/openwebrx.js
+++ b/htdocs/openwebrx.js
@@ -1177,6 +1177,10 @@ function on_ws_recv(evt) {
// set a higher reconnection timeout right away to avoid additional load
reconnect_timeout = 16000;
break;
+ case 'modes':
+ Modes.setModes(json['value']);
+ console.info(Modes);
+ break;
default:
console.warn('received message of unknown type: ' + json['type']);
}
@@ -2014,6 +2018,11 @@ function demodulator_digital_replace_last() {
function demodulator_digital_replace(subtype) {
if (secondary_demod === subtype) return;
+ var mode = Modes.findByModulation(subtype);
+ if (mode && !mode.isAvailable()) {
+ divlog('Digital mode "' + mode.name + '" not supported. Please check requirements', true);
+ return;
+ }
switch (subtype) {
case "bpsk31":
case "bpsk63":
diff --git a/owrx/connection.py b/owrx/connection.py
index b483a22..4367e55 100644
--- a/owrx/connection.py
+++ b/owrx/connection.py
@@ -11,6 +11,7 @@ from owrx.bookmarks import Bookmarks
from owrx.map import Map
from owrx.locator import Locator
from owrx.property import PropertyStack
+from owrx.modes import Modes
from multiprocessing import Queue
from queue import Full
from js8py import Js8Frame
@@ -122,6 +123,9 @@ class OpenWebRxReceiverClient(Client):
features = FeatureDetector().feature_availability()
self.write_features(features)
+ modes = Modes.getModes()
+ self.write_modes(modes)
+
CpuUsageThread.getSharedInstance().add_client(self)
def __sendProfiles(self):
@@ -345,6 +349,13 @@ class OpenWebRxReceiverClient(Client):
"mode": frame.mode
}})
+ def write_modes(self, modes):
+ self.send({"type": "modes", "value": [{
+ "modulation": m.modulation,
+ "name": m.name,
+ "requirements": m.requirements
+ } for m in modes]})
+
class MapConnection(Client):
def __init__(self, conn):
diff --git a/owrx/form/__init__.py b/owrx/form/__init__.py
index fb7160e..7f59bf5 100644
--- a/owrx/form/__init__.py
+++ b/owrx/form/__init__.py
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
-from owrx.service import ServiceDetector
+from owrx.modes import Modes
from owrx.config import Config
@@ -196,7 +196,7 @@ class MultiCheckboxInput(Input):
class ServicesCheckboxInput(MultiCheckboxInput):
def __init__(self, id, label, infotext=None):
services = [
- Option(s, s.upper()) for s in ServiceDetector.getAvailableServices()
+ Option(s.modulation, s.name) for s in Modes.getAvailableServices()
]
super().__init__(id, label, services, infotext)
diff --git a/owrx/modes.py b/owrx/modes.py
new file mode 100644
index 0000000..b0721fc
--- /dev/null
+++ b/owrx/modes.py
@@ -0,0 +1,43 @@
+from owrx.feature import FeatureDetector
+from functools import reduce
+
+
+class Mode(object):
+ def __init__(self, modulation, name, requirements=None, service=False):
+ self.modulation = modulation
+ self.name = name
+ self.requirements = requirements if requirements is not None else []
+ self.service = service
+
+ def is_available(self):
+ fd = FeatureDetector()
+ return reduce(
+ lambda a, b: a and b, [fd.is_available(r) for r in self.requirements], True
+ )
+
+ def is_service(self):
+ return self.service
+
+
+class Modes(object):
+ mappings = [
+ Mode("ft8", "FT8", ["wsjt-x"], True),
+ Mode("ft4", "FT4", ["wsjt-x"], True),
+ Mode("jt65", "JT65", ["wsjt-x"], True),
+ Mode("jt9", "JT9", ["wsjt-x"], True),
+ Mode("wspr", "WSPR", ["wsjt-x"], True),
+ Mode("packet", "Packet", ["packet"], True),
+ Mode("js8", "JS8Call", ["js8call"], True),
+ ]
+
+ @staticmethod
+ def getModes():
+ return Modes.mappings
+
+ @staticmethod
+ def getAvailableModes():
+ return [m for m in Modes.getModes() if m.is_available()]
+
+ @staticmethod
+ def getAvailableServices():
+ return [m for m in Modes.getAvailableModes() if m.is_service()]
diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py
index 8310fa4..d4c97df 100644
--- a/owrx/service/__init__.py
+++ b/owrx/service/__init__.py
@@ -8,12 +8,11 @@ from owrx.aprs import AprsParser
from owrx.js8 import Js8Parser
from owrx.config import Config
from owrx.source.resampler import Resampler
-from owrx.feature import FeatureDetector
from owrx.property import PropertyLayer
from js8py import Js8Frame
from abc import ABCMeta, abstractmethod
from .schedule import ServiceScheduler
-from functools import reduce
+from owrx.modes import Modes
import logging
@@ -60,31 +59,6 @@ class Js8ServiceOutput(ServiceOutput):
return t == "js8_demod"
-class ServiceDetector(object):
- requirements = {
- "ft8": ["wsjt-x"],
- "ft4": ["wsjt-x"],
- "jt65": ["wsjt-x"],
- "jt9": ["wsjt-x"],
- "wspr": ["wsjt-x"],
- "packet": ["packet"],
- "js8": ["js8call"],
- }
-
- @staticmethod
- def getAvailableServices():
- # TODO this should be in a more central place (the frontend also needs this)
- fd = FeatureDetector()
-
- return [
- name
- for name, requirements in ServiceDetector.requirements.items()
- if reduce(
- lambda a, b: a and b, [fd.is_available(r) for r in requirements], True
- )
- ]
-
-
class ServiceHandler(object):
def __init__(self, source):
self.lock = threading.Lock()
@@ -120,7 +94,7 @@ class ServiceHandler(object):
def isSupported(self, mode):
configured = Config.get()["services_decoders"]
- available = ServiceDetector.getAvailableServices()
+ available = [m.modulation for m in Modes.getAvailableServices()]
return mode in configured and mode in available
def shutdown(self):