implement first stages of active mode communication

This commit is contained in:
Jakob Ketterl 2020-04-26 15:17:03 +02:00
parent e61d3a22a3
commit 907787cfdc
7 changed files with 96 additions and 30 deletions

View File

@ -33,6 +33,7 @@
<script src="static/lib/Measurement.js"></script> <script src="static/lib/Measurement.js"></script>
<script src="static/lib/FrequencyDisplay.js"></script> <script src="static/lib/FrequencyDisplay.js"></script>
<script src="static/lib/Js8Threads.js"></script> <script src="static/lib/Js8Threads.js"></script>
<script src="static/lib/Modes.js"></script>
<link rel="stylesheet" type="text/css" href="static/lib/nanoscroller.css" /> <link rel="stylesheet" type="text/css" href="static/lib/nanoscroller.css" />
<link rel="stylesheet" type="text/css" href="static/css/openwebrx.css" /> <link rel="stylesheet" type="text/css" href="static/css/openwebrx.css" />
<meta charset="utf-8"> <meta charset="utf-8">

28
htdocs/lib/Modes.js Normal file
View File

@ -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);
}

View File

@ -1177,6 +1177,10 @@ function on_ws_recv(evt) {
// set a higher reconnection timeout right away to avoid additional load // set a higher reconnection timeout right away to avoid additional load
reconnect_timeout = 16000; reconnect_timeout = 16000;
break; break;
case 'modes':
Modes.setModes(json['value']);
console.info(Modes);
break;
default: default:
console.warn('received message of unknown type: ' + json['type']); console.warn('received message of unknown type: ' + json['type']);
} }
@ -2014,6 +2018,11 @@ function demodulator_digital_replace_last() {
function demodulator_digital_replace(subtype) { function demodulator_digital_replace(subtype) {
if (secondary_demod === subtype) return; 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) { switch (subtype) {
case "bpsk31": case "bpsk31":
case "bpsk63": case "bpsk63":

View File

@ -11,6 +11,7 @@ from owrx.bookmarks import Bookmarks
from owrx.map import Map from owrx.map import Map
from owrx.locator import Locator from owrx.locator import Locator
from owrx.property import PropertyStack from owrx.property import PropertyStack
from owrx.modes import Modes
from multiprocessing import Queue from multiprocessing import Queue
from queue import Full from queue import Full
from js8py import Js8Frame from js8py import Js8Frame
@ -122,6 +123,9 @@ class OpenWebRxReceiverClient(Client):
features = FeatureDetector().feature_availability() features = FeatureDetector().feature_availability()
self.write_features(features) self.write_features(features)
modes = Modes.getModes()
self.write_modes(modes)
CpuUsageThread.getSharedInstance().add_client(self) CpuUsageThread.getSharedInstance().add_client(self)
def __sendProfiles(self): def __sendProfiles(self):
@ -345,6 +349,13 @@ class OpenWebRxReceiverClient(Client):
"mode": frame.mode "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): class MapConnection(Client):
def __init__(self, conn): def __init__(self, conn):

View File

@ -1,5 +1,5 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from owrx.service import ServiceDetector from owrx.modes import Modes
from owrx.config import Config from owrx.config import Config
@ -196,7 +196,7 @@ class MultiCheckboxInput(Input):
class ServicesCheckboxInput(MultiCheckboxInput): class ServicesCheckboxInput(MultiCheckboxInput):
def __init__(self, id, label, infotext=None): def __init__(self, id, label, infotext=None):
services = [ 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) super().__init__(id, label, services, infotext)

43
owrx/modes.py Normal file
View File

@ -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()]

View File

@ -8,12 +8,11 @@ from owrx.aprs import AprsParser
from owrx.js8 import Js8Parser from owrx.js8 import Js8Parser
from owrx.config import Config from owrx.config import Config
from owrx.source.resampler import Resampler from owrx.source.resampler import Resampler
from owrx.feature import FeatureDetector
from owrx.property import PropertyLayer from owrx.property import PropertyLayer
from js8py import Js8Frame from js8py import Js8Frame
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from .schedule import ServiceScheduler from .schedule import ServiceScheduler
from functools import reduce from owrx.modes import Modes
import logging import logging
@ -60,31 +59,6 @@ class Js8ServiceOutput(ServiceOutput):
return t == "js8_demod" 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): class ServiceHandler(object):
def __init__(self, source): def __init__(self, source):
self.lock = threading.Lock() self.lock = threading.Lock()
@ -120,7 +94,7 @@ class ServiceHandler(object):
def isSupported(self, mode): def isSupported(self, mode):
configured = Config.get()["services_decoders"] configured = Config.get()["services_decoders"]
available = ServiceDetector.getAvailableServices() available = [m.modulation for m in Modes.getAvailableServices()]
return mode in configured and mode in available return mode in configured and mode in available
def shutdown(self): def shutdown(self):