From a54a5fd5600091acae4dc47ca808cf73d48f8956 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 28 Feb 2023 15:30:31 +0100 Subject: [PATCH 1/3] allow underlying mode to be specified in bandplan --- owrx/bands.py | 36 ++++++++++++++++++++++++++---------- owrx/modes.py | 7 +++++++ owrx/service/__init__.py | 21 +++++++++++---------- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/owrx/bands.py b/owrx/bands.py index 1aba72e5..ef4d0e91 100644 --- a/owrx/bands.py +++ b/owrx/bands.py @@ -1,4 +1,4 @@ -from owrx.modes import Modes +from owrx.modes import Modes, DigitalMode from datetime import datetime, timezone import json import os @@ -9,14 +9,14 @@ logger = logging.getLogger(__name__) class Band(object): - def __init__(self, dict): - self.name = dict["name"] - self.lower_bound = dict["lower_bound"] - self.upper_bound = dict["upper_bound"] + def __init__(self, b_dict): + self.name = b_dict["name"] + self.lower_bound = b_dict["lower_bound"] + self.upper_bound = b_dict["upper_bound"] self.frequencies = [] - if "frequencies" in dict: + if "frequencies" in b_dict: availableModes = [mode.modulation for mode in Modes.getAvailableModes()] - for (mode, freqs) in dict["frequencies"].items(): + for (mode, freqs) in b_dict["frequencies"].items(): if mode not in availableModes: logger.info( 'Modulation "{mode}" is not available, bandplan bookmark will not be displayed'.format( @@ -27,14 +27,30 @@ class Band(object): if not isinstance(freqs, list): freqs = [freqs] for f in freqs: - if not self.inBand(f): + f_dict = {"frequency": f} if not isinstance(f, dict) else f + f_dict["mode"] = mode + + if not self.inBand(f_dict["frequency"]): logger.warning( "Frequency for {mode} on {band} is not within band limits: {frequency}".format( - mode=mode, frequency=f, band=self.name + mode=mode, frequency=f_dict["frequency"], band=self.name ) ) continue - self.frequencies.append({"mode": mode, "frequency": f}) + + if "underlying" in f_dict: + m = Modes.findByModulation(mode) + 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: + logger.warning( + "%s is not a valid underlying mode for %s; skipping", + f_dict["underlying"], + mode + ) + + self.frequencies.append(f_dict) def inBand(self, freq): return self.lower_bound <= freq <= self.upper_bound diff --git a/owrx/modes.py b/owrx/modes.py index 130ae551..c27e85b8 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -55,6 +55,13 @@ class DigitalMode(Mode): def get_modulation(self): return self.get_underlying_mode().get_modulation() + def for_underlying(self, underlying: str): + if underlying not in self.underlying: + raise ValueError("{} is not a valid underlying mode for {}".format(underlying, self.modulation)) + return DigitalMode( + self.modulation, self.name, [underlying], self.bandpass, self.requirements, self.service, self.squelch + ) + class AudioChopperMode(DigitalMode, metaclass=ABCMeta): def __init__(self, modulation, name, bandpass=None, requirements=None): diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index 6361b0ca..789ca09c 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -123,13 +123,11 @@ class ServiceHandler(SdrSourceEventClient): def updateServices(self): def addService(dial, source): - mode = dial["mode"] - frequency = dial["frequency"] try: - service = self.setupService(mode, frequency, source) + service = self.setupService(dial, source) self.services.append(service) except Exception: - logger.exception("Error setting up service %s on frequency %d", mode, frequency) + logger.exception("Error setting up service {mode} on frequency {frequency}".format(**dial)) with self.lock: logger.debug("re-scheduling services due to sdr changes") @@ -247,23 +245,26 @@ class ServiceHandler(SdrSourceEventClient): return None return best["groups"] - def setupService(self, mode, frequency, source): - logger.debug("setting up service {0} on frequency {1}".format(mode, frequency)) + def setupService(self, dial, source): + logger.debug("setting up service {mode} on frequency {frequency}".format(**dial)) - modeObject = Modes.findByModulation(mode) + modeObject = Modes.findByModulation(dial["mode"]) if not isinstance(modeObject, DigitalMode): - logger.warning("mode is not a digimode: %s", mode) + logger.warning("mode is not a digimode: %s", dial["mode"]) return None + if "underlying" in dial: + modeObject = modeObject.for_underlying(dial["underlying"]) + demod = self._getDemodulator(modeObject.get_modulation()) secondaryDemod = self._getSecondaryDemodulator(modeObject.modulation) center_freq = source.getProps()["center_freq"] sampleRate = source.getProps()["samp_rate"] bandpass = modeObject.get_bandpass() if isinstance(secondaryDemod, DialFrequencyReceiver): - secondaryDemod.setDialFrequency(frequency) + secondaryDemod.setDialFrequency(dial["frequency"]) - chain = ServiceDemodulatorChain(demod, secondaryDemod, sampleRate, frequency - center_freq) + chain = ServiceDemodulatorChain(demod, secondaryDemod, sampleRate, dial["frequency"] - center_freq) chain.setBandPass(bandpass.low_cut, bandpass.high_cut) chain.setReader(source.getBuffer().getReader()) From 258aebd0c3888e8212e1d6faee4607435ff914b7 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 28 Feb 2023 17:07:13 +0100 Subject: [PATCH 2/3] correctly handle bookmarks with underlying mode in receiver --- htdocs/lib/BookmarkBar.js | 2 +- htdocs/lib/DemodulatorPanel.js | 39 +++++++++++++++++++++------------- htdocs/openwebrx.js | 3 ++- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/htdocs/lib/BookmarkBar.js b/htdocs/lib/BookmarkBar.js index e0993a5e..7c40892f 100644 --- a/htdocs/lib/BookmarkBar.js +++ b/htdocs/lib/BookmarkBar.js @@ -11,7 +11,7 @@ function BookmarkBar() { if (!b || !b.frequency || !b.modulation) return; me.getDemodulator().set_offset_frequency(b.frequency - center_freq); if (b.modulation) { - me.getDemodulatorPanel().setMode(b.modulation); + me.getDemodulatorPanel().setMode(b.modulation, b.underlying); } $bookmark.addClass('selected'); }); diff --git a/htdocs/lib/DemodulatorPanel.js b/htdocs/lib/DemodulatorPanel.js index eb4e8c55..612c7522 100644 --- a/htdocs/lib/DemodulatorPanel.js +++ b/htdocs/lib/DemodulatorPanel.js @@ -18,7 +18,12 @@ function DemodulatorPanel(el) { el.on('click', '.openwebrx-demodulator-button', function() { var modulation = $(this).data('modulation'); if (modulation) { - self.setMode(modulation); + if (self.mode && self.mode.type === 'digimode' && self.mode.underlying.indexOf(modulation) >= 0) { + // keep the mode, just switch underlying modulation + self.setMode(self.mode.modulation, modulation) + } else { + self.setMode(modulation); + } } else { self.disableDigiMode(); } @@ -80,12 +85,13 @@ DemodulatorPanel.prototype.render = function() { this.el.find(".openwebrx-modes").html(html); }; -DemodulatorPanel.prototype.setMode = function(requestedModulation) { +DemodulatorPanel.prototype.setMode = function(requestedModulation, underlyingModulation) { var mode = Modes.findByModulation(requestedModulation); if (!mode) { return; } - if (this.mode === mode) { + + if (this.mode === mode && this.underlyingModulation === underlyingModulation) { return; } if (!mode.isAvailable()) { @@ -93,16 +99,15 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation) { return; } + var modulation; if (mode.type === 'digimode') { - modulation = mode.underlying[0]; - } else { - if (this.mode && this.mode.type === 'digimode' && this.mode.underlying.indexOf(requestedModulation) >= 0) { - // keep the mode, just switch underlying modulation - mode = this.mode; - modulation = requestedModulation; + if (underlyingModulation) { + modulation = underlyingModulation } else { - modulation = mode.modulation; + modulation = mode.underlying[0]; } + } else { + modulation = mode.modulation; } var current = this.collectParams(); @@ -142,6 +147,7 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation) { this.demodulator.start(); this.mode = mode; + this.underlyingModulation = modulation; this.updateButtons(); this.updatePanels(); @@ -149,8 +155,6 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation) { }; DemodulatorPanel.prototype.disableDigiMode = function() { - // just a little trick to get out of the digimode - delete this.mode; this.setMode(this.getDemodulator().get_modulation()); }; @@ -203,7 +207,11 @@ DemodulatorPanel.prototype.stopDemodulator = function() { } DemodulatorPanel.prototype._apply = function(params) { - this.setMode(params.mod); + if (params.secondary_mod) { + this.setMode(params.secondary_mod, params.mod) + } else { + this.setMode(params.mod); + } this.getDemodulator().set_offset_frequency(params.offset_frequency); this.getDemodulator().setSquelch(params.squelch_level); this.updateButtons(); @@ -223,8 +231,9 @@ DemodulatorPanel.prototype.onHashChange = function() { DemodulatorPanel.prototype.transformHashParams = function(params) { var ret = { - mod: params.secondary_mod || params.mod + mod: params.mod }; + if (typeof(params.secondary_mod) !== 'undefined') ret.secondary_mod = params.secondary_mod; if (typeof(params.offset_frequency) !== 'undefined') ret.offset_frequency = params.offset_frequency; if (typeof(params.sql) !== 'undefined') ret.squelch_level = parseInt(params.sql); return ret; @@ -329,7 +338,7 @@ DemodulatorPanel.prototype.updateHash = function() { freq: demod.get_offset_frequency() + self.center_freq, mod: demod.get_modulation(), secondary_mod: demod.get_secondary_demod(), - sql: demod.getSquelch(), + sql: demod.getSquelch() }, function(value, key){ if (typeof(value) === 'undefined' || value === false) return undefined; return key + '=' + value; diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index f76aadd1..7309ba53 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -831,7 +831,8 @@ function on_ws_recv(evt) { return { name: d['mode'].toUpperCase(), modulation: d['mode'], - frequency: d['frequency'] + frequency: d['frequency'], + underlying: d['underlying'] }; }); bookmarks.replace_bookmarks(as_bookmarks, 'dial_frequencies'); From 101d385c053753f8e480e6c9167c8c199573a5dd Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 17 Mar 2023 17:47:11 +0100 Subject: [PATCH 3/3] use bandpass from underlying mode if applicable --- owrx/service/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index 789ca09c..fa50fdcc 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -174,11 +174,17 @@ class ServiceHandler(SdrSourceEventClient): addService(dial, self.source) def get_min_max(self, group): + def find_bandpass(dial): + mode = Modes.findByModulation(dial["mode"]) + if "underlying" in dial: + mode = mode.for_underlying(dial["underlying"]) + return mode.get_bandpass() + frequencies = sorted(group, key=lambda f: f["frequency"]) lowest = frequencies[0] - min = lowest["frequency"] + Modes.findByModulation(lowest["mode"]).get_bandpass().low_cut + min = lowest["frequency"] + find_bandpass(lowest).low_cut highest = frequencies[-1] - max = highest["frequency"] + Modes.findByModulation(highest["mode"]).get_bandpass().high_cut + max = highest["frequency"] + find_bandpass(highest).high_cut return min, max def get_center_frequency(self, group):