From b299c6cd6f47f0b8dd0cf07dd086cbc78ad27f87 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 20 Feb 2024 00:53:11 +0100 Subject: [PATCH] introduce a way for different bandpass setups for underlying modes --- htdocs/lib/DemodulatorPanel.js | 9 ++--- htdocs/lib/Modes.js | 2 +- owrx/bands.py | 2 +- owrx/connection.py | 8 ++++- owrx/modes.py | 66 +++++++++++++++++++++++----------- 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/htdocs/lib/DemodulatorPanel.js b/htdocs/lib/DemodulatorPanel.js index 06eb8f47..4d71fae7 100644 --- a/htdocs/lib/DemodulatorPanel.js +++ b/htdocs/lib/DemodulatorPanel.js @@ -18,7 +18,7 @@ function DemodulatorPanel(el) { el.on('click', '.openwebrx-demodulator-button', function() { var modulation = $(this).data('modulation'); if (modulation) { - if (self.mode && self.mode.type === 'digimode' && self.mode.underlying.indexOf(modulation) >= 0) { + if (self.mode && self.mode.type === 'digimode' && modulation in self.mode.configs) { // keep the mode, just switch underlying modulation self.setMode(self.mode.modulation, modulation) } else { @@ -101,7 +101,7 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation, underlyingMod var modulation; if (mode.type === 'digimode') { - modulation = underlyingModulation = underlyingModulation || mode.underlying[0]; + modulation = underlyingModulation = underlyingModulation || Object.keys(mode.configs)[0]; } else { underlyingModulation = undefined; modulation = mode.modulation; @@ -136,7 +136,8 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation, underlyingMod if (mode.type === 'digimode') { this.demodulator.set_secondary_demod(mode.modulation); var uMode = Modes.findByModulation(underlyingModulation); - var bandpass = mode.bandpass || (uMode && uMode.bandpass); + var config = mode.configs[underlyingModulation] || {}; + var bandpass = config.bandpass || mode.bandpass || (uMode && uMode.bandpass); if (bandpass) { this.demodulator.setBandpass(bandpass); } else { @@ -262,7 +263,7 @@ DemodulatorPanel.prototype.updateButtons = function() { var mode = Modes.findByModulation(secondary_demod); if (mode) { var self = this; - mode.underlying.filter(function(m) { + Object.keys(mode.configs).filter(function(m) { return m !== demod.get_modulation(); }).forEach(function(m) { self.el.find('[data-modulation=' + m + ']').addClass('same-mod') diff --git a/htdocs/lib/Modes.js b/htdocs/lib/Modes.js index 0bdac90b..46c0fc28 100644 --- a/htdocs/lib/Modes.js +++ b/htdocs/lib/Modes.js @@ -45,7 +45,7 @@ var Mode = function(json){ this.ifRate = json.ifRate; } if (this.type === 'digimode') { - this.underlying = json.underlying; + this.configs = json.configs; this.secondaryFft = json.secondaryFft; } }; diff --git a/owrx/bands.py b/owrx/bands.py index ef4d0e91..210cb0f9 100644 --- a/owrx/bands.py +++ b/owrx/bands.py @@ -43,7 +43,7 @@ class Band(object): if not isinstance(m, DigitalMode): logger.warning("%s is not a digital mode, cannot be used with \"underlying\" config", mode) continue - if f_dict["underlying"] not in m.underlying: + if f_dict["underlying"] not in m.configs: logger.warning( "%s is not a valid underlying mode for %s; skipping", f_dict["underlying"], diff --git a/owrx/connection.py b/owrx/connection.py index 0350e155..14fb2811 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -445,7 +445,13 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): if m.ifRate is not None: res["ifRate"] = m.ifRate if isinstance(m, DigitalMode): - res["underlying"] = m.underlying + configs = {} + for k, c in m.configs.items(): + config = {} + if c.bandpass is not None: + config["bandpass"] = {"low_cut": c.bandpass.low_cut, "high_cut": c.bandpass.high_cut} + configs[k] = config + res["configs"] = configs res["secondaryFft"] = m.secondaryFft return res diff --git a/owrx/modes.py b/owrx/modes.py index b9aaab9c..eac10043 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -2,6 +2,7 @@ from owrx.feature import FeatureDetector from owrx.audio import ProfileSource from functools import reduce from abc import ABCMeta, abstractmethod +from typing import Dict, List class Bandpass(object): @@ -41,25 +42,35 @@ class AnalogMode(Mode): pass +class DigitalUnderlyingConfig(object): + def __init__(self, bandpass: Bandpass = None): + self.bandpass = bandpass + + class DigitalMode(Mode): def __init__( self, modulation, name, - underlying, bandpass: Bandpass = None, - ifRate = None, + ifRate=None, requirements=None, service=False, squelch=True, - secondaryFft=True + secondaryFft=True, + configs: List[str] | Dict[str, DigitalUnderlyingConfig] = None, ): super().__init__(modulation, name, bandpass, ifRate, requirements, service, squelch) - self.underlying = underlying self.secondaryFft = secondaryFft + if configs is None: + self.configs = {} + elif isinstance(configs, list): + self.configs = {x: DigitalUnderlyingConfig() for x in configs} + else: + self.configs = configs def get_underlying_mode(self): - mode = Modes.findByModulation(self.underlying[0]) + mode = Modes.findByModulation(list(self.configs.keys())[0]) if mode is None: mode = EmptyMode return mode @@ -73,10 +84,20 @@ class DigitalMode(Mode): return self.get_underlying_mode().get_modulation() def for_underlying(self, underlying: str): - if underlying not in self.underlying: + if underlying not in self.configs: raise ValueError("{} is not a valid underlying mode for {}".format(underlying, self.modulation)) + config = self.configs[underlying] + bandpass = self.bandpass if config.bandpass is None else config.bandpass return DigitalMode( - self.modulation, self.name, [underlying], self.bandpass, self.requirements, self.service, self.squelch + self.modulation, + self.name, + bandpass, + self.ifRate, + self.requirements, + self.service, + self.squelch, + self.secondaryFft, + {underlying: self.configs[underlying]} ) @@ -84,7 +105,7 @@ class AudioChopperMode(DigitalMode, metaclass=ABCMeta): def __init__(self, modulation, name, bandpass=None, requirements=None): if bandpass is None: bandpass = Bandpass(0, 3000) - super().__init__(modulation, name, ["usb"], bandpass=bandpass, requirements=requirements, service=True) + super().__init__(modulation, name, bandpass=bandpass, requirements=requirements, service=True, configs=["usb"]) @abstractmethod def get_profile_source(self) -> ProfileSource: @@ -135,15 +156,18 @@ class Modes(object): ), AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False), AnalogMode("dab", "DAB", bandpass=None, ifRate=2.048e6, requirements=["dab"], squelch=False), - DigitalMode("bpsk31", "BPSK31", underlying=["usb"]), - DigitalMode("bpsk63", "BPSK63", underlying=["usb"]), - DigitalMode("rtty170", "RTTY 45/170", underlying=["usb", "lsb"]), - DigitalMode("rtty450", "RTTY 50N/450", underlying=["lsb", "usb"]), - DigitalMode("rtty85", "RTTY 50N/85", underlying=["lsb", "usb"]), + DigitalMode("bpsk31", "BPSK31", configs=["usb"]), + DigitalMode("bpsk63", "BPSK63", configs=["usb"]), + DigitalMode("rtty170", "RTTY 45/170", configs=["usb", "lsb"]), + DigitalMode("rtty450", "RTTY 50N/450", configs=["lsb", "usb"]), + DigitalMode("rtty85", "RTTY 50N/85", configs=["lsb", "usb"]), DigitalMode( "sstv", "SSTV", - underlying=["usb", "lsb"], + configs={ + "usb": DigitalUnderlyingConfig(bandpass=Bandpass(1100, 2400)), + "lsb": DigitalUnderlyingConfig(bandpass=Bandpass(-2400, -1100)), + }, bandpass=Bandpass(1100, 2400), requirements=["sstv"], service=True @@ -156,12 +180,12 @@ class Modes(object): WsjtMode("fst4", "FST4", requirements=["wsjt-x-2-3"]), WsjtMode("fst4w", "FST4W", bandpass=Bandpass(1350, 1650), requirements=["wsjt-x-2-3"]), WsjtMode("q65", "Q65", requirements=["wsjt-x-2-4"]), - DigitalMode("msk144", "MSK144", requirements=["msk144"], underlying=["usb"], service=True), + DigitalMode("msk144", "MSK144", requirements=["msk144"], configs=["usb"], service=True), Js8Mode("js8", "JS8Call"), DigitalMode( "packet", "Packet", - underlying=["nfm", "usb", "lsb"], + configs=["nfm", "usb", "lsb"], bandpass=Bandpass(-6250, 6250), requirements=["packet"], service=True, @@ -170,7 +194,7 @@ class Modes(object): DigitalMode( "pocsag", "Pocsag", - underlying=["nfm"], + configs=["nfm"], bandpass=Bandpass(-6250, 6250), requirements=["pocsag"], service=True, @@ -179,7 +203,7 @@ class Modes(object): DigitalMode( "adsb", "ADS-B", - underlying=["empty"], + configs=["empty"], bandpass=None, ifRate=2.4e6, requirements=["dump1090"], @@ -190,7 +214,7 @@ class Modes(object): DigitalMode( "ism", "ISM", - underlying=["empty"], + configs=["empty"], bandpass=Bandpass(-125000, 125000), requirements=["ism"], service=True, @@ -199,7 +223,7 @@ class Modes(object): DigitalMode( "hfdl", "HFDL", - underlying=["empty"], + configs=["empty"], bandpass=Bandpass(0, 3000), requirements=["dumphfdl"], service=True, @@ -208,7 +232,7 @@ class Modes(object): DigitalMode( "vdl2", "VDL2", - underlying=["empty"], + configs=["empty"], bandpass=Bandpass(-12500, 12500), requirements=["dumpvdl2"], service=True,