implement first stages of active mode communication
This commit is contained in:
parent
e61d3a22a3
commit
907787cfdc
@ -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
28
htdocs/lib/Modes.js
Normal 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);
|
||||||
|
}
|
@ -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":
|
||||||
|
@ -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):
|
||||||
|
@ -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
43
owrx/modes.py
Normal 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()]
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user