Compare commits

...

2 Commits

Author SHA1 Message Date
Jakob Ketterl
258aebd0c3 correctly handle bookmarks with underlying mode in receiver 2023-02-28 17:07:13 +01:00
Jakob Ketterl
a54a5fd560 allow underlying mode to be specified in bandplan 2023-02-28 15:30:31 +01:00
6 changed files with 71 additions and 37 deletions

View File

@ -11,7 +11,7 @@ function BookmarkBar() {
if (!b || !b.frequency || !b.modulation) return; if (!b || !b.frequency || !b.modulation) return;
me.getDemodulator().set_offset_frequency(b.frequency - center_freq); me.getDemodulator().set_offset_frequency(b.frequency - center_freq);
if (b.modulation) { if (b.modulation) {
me.getDemodulatorPanel().setMode(b.modulation); me.getDemodulatorPanel().setMode(b.modulation, b.underlying);
} }
$bookmark.addClass('selected'); $bookmark.addClass('selected');
}); });

View File

@ -18,7 +18,12 @@ function DemodulatorPanel(el) {
el.on('click', '.openwebrx-demodulator-button', function() { el.on('click', '.openwebrx-demodulator-button', function() {
var modulation = $(this).data('modulation'); var modulation = $(this).data('modulation');
if (modulation) { if (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); self.setMode(modulation);
}
} else { } else {
self.disableDigiMode(); self.disableDigiMode();
} }
@ -80,12 +85,13 @@ DemodulatorPanel.prototype.render = function() {
this.el.find(".openwebrx-modes").html(html); this.el.find(".openwebrx-modes").html(html);
}; };
DemodulatorPanel.prototype.setMode = function(requestedModulation) { DemodulatorPanel.prototype.setMode = function(requestedModulation, underlyingModulation) {
var mode = Modes.findByModulation(requestedModulation); var mode = Modes.findByModulation(requestedModulation);
if (!mode) { if (!mode) {
return; return;
} }
if (this.mode === mode) {
if (this.mode === mode && this.underlyingModulation === underlyingModulation) {
return; return;
} }
if (!mode.isAvailable()) { if (!mode.isAvailable()) {
@ -93,17 +99,16 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation) {
return; return;
} }
var modulation;
if (mode.type === 'digimode') { if (mode.type === 'digimode') {
modulation = mode.underlying[0]; if (underlyingModulation) {
modulation = underlyingModulation
} else { } else {
if (this.mode && this.mode.type === 'digimode' && this.mode.underlying.indexOf(requestedModulation) >= 0) { modulation = mode.underlying[0];
// keep the mode, just switch underlying modulation }
mode = this.mode;
modulation = requestedModulation;
} else { } else {
modulation = mode.modulation; modulation = mode.modulation;
} }
}
var current = this.collectParams(); var current = this.collectParams();
if (this.demodulator) { if (this.demodulator) {
@ -142,6 +147,7 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation) {
this.demodulator.start(); this.demodulator.start();
this.mode = mode; this.mode = mode;
this.underlyingModulation = modulation;
this.updateButtons(); this.updateButtons();
this.updatePanels(); this.updatePanels();
@ -149,8 +155,6 @@ DemodulatorPanel.prototype.setMode = function(requestedModulation) {
}; };
DemodulatorPanel.prototype.disableDigiMode = function() { DemodulatorPanel.prototype.disableDigiMode = function() {
// just a little trick to get out of the digimode
delete this.mode;
this.setMode(this.getDemodulator().get_modulation()); this.setMode(this.getDemodulator().get_modulation());
}; };
@ -203,7 +207,11 @@ DemodulatorPanel.prototype.stopDemodulator = function() {
} }
DemodulatorPanel.prototype._apply = function(params) { DemodulatorPanel.prototype._apply = function(params) {
if (params.secondary_mod) {
this.setMode(params.secondary_mod, params.mod)
} else {
this.setMode(params.mod); this.setMode(params.mod);
}
this.getDemodulator().set_offset_frequency(params.offset_frequency); this.getDemodulator().set_offset_frequency(params.offset_frequency);
this.getDemodulator().setSquelch(params.squelch_level); this.getDemodulator().setSquelch(params.squelch_level);
this.updateButtons(); this.updateButtons();
@ -223,8 +231,9 @@ DemodulatorPanel.prototype.onHashChange = function() {
DemodulatorPanel.prototype.transformHashParams = function(params) { DemodulatorPanel.prototype.transformHashParams = function(params) {
var ret = { 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.offset_frequency) !== 'undefined') ret.offset_frequency = params.offset_frequency;
if (typeof(params.sql) !== 'undefined') ret.squelch_level = parseInt(params.sql); if (typeof(params.sql) !== 'undefined') ret.squelch_level = parseInt(params.sql);
return ret; return ret;
@ -329,7 +338,7 @@ DemodulatorPanel.prototype.updateHash = function() {
freq: demod.get_offset_frequency() + self.center_freq, freq: demod.get_offset_frequency() + self.center_freq,
mod: demod.get_modulation(), mod: demod.get_modulation(),
secondary_mod: demod.get_secondary_demod(), secondary_mod: demod.get_secondary_demod(),
sql: demod.getSquelch(), sql: demod.getSquelch()
}, function(value, key){ }, function(value, key){
if (typeof(value) === 'undefined' || value === false) return undefined; if (typeof(value) === 'undefined' || value === false) return undefined;
return key + '=' + value; return key + '=' + value;

View File

@ -831,7 +831,8 @@ function on_ws_recv(evt) {
return { return {
name: d['mode'].toUpperCase(), name: d['mode'].toUpperCase(),
modulation: d['mode'], modulation: d['mode'],
frequency: d['frequency'] frequency: d['frequency'],
underlying: d['underlying']
}; };
}); });
bookmarks.replace_bookmarks(as_bookmarks, 'dial_frequencies'); bookmarks.replace_bookmarks(as_bookmarks, 'dial_frequencies');

View File

@ -1,4 +1,4 @@
from owrx.modes import Modes from owrx.modes import Modes, DigitalMode
from datetime import datetime, timezone from datetime import datetime, timezone
import json import json
import os import os
@ -9,14 +9,14 @@ logger = logging.getLogger(__name__)
class Band(object): class Band(object):
def __init__(self, dict): def __init__(self, b_dict):
self.name = dict["name"] self.name = b_dict["name"]
self.lower_bound = dict["lower_bound"] self.lower_bound = b_dict["lower_bound"]
self.upper_bound = dict["upper_bound"] self.upper_bound = b_dict["upper_bound"]
self.frequencies = [] self.frequencies = []
if "frequencies" in dict: if "frequencies" in b_dict:
availableModes = [mode.modulation for mode in Modes.getAvailableModes()] 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: if mode not in availableModes:
logger.info( logger.info(
'Modulation "{mode}" is not available, bandplan bookmark will not be displayed'.format( 'Modulation "{mode}" is not available, bandplan bookmark will not be displayed'.format(
@ -27,14 +27,30 @@ class Band(object):
if not isinstance(freqs, list): if not isinstance(freqs, list):
freqs = [freqs] freqs = [freqs]
for f in 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( logger.warning(
"Frequency for {mode} on {band} is not within band limits: {frequency}".format( "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 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): def inBand(self, freq):
return self.lower_bound <= freq <= self.upper_bound return self.lower_bound <= freq <= self.upper_bound

View File

@ -55,6 +55,13 @@ class DigitalMode(Mode):
def get_modulation(self): def get_modulation(self):
return self.get_underlying_mode().get_modulation() 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): class AudioChopperMode(DigitalMode, metaclass=ABCMeta):
def __init__(self, modulation, name, bandpass=None, requirements=None): def __init__(self, modulation, name, bandpass=None, requirements=None):

View File

@ -123,13 +123,11 @@ class ServiceHandler(SdrSourceEventClient):
def updateServices(self): def updateServices(self):
def addService(dial, source): def addService(dial, source):
mode = dial["mode"]
frequency = dial["frequency"]
try: try:
service = self.setupService(mode, frequency, source) service = self.setupService(dial, source)
self.services.append(service) self.services.append(service)
except Exception: 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: with self.lock:
logger.debug("re-scheduling services due to sdr changes") logger.debug("re-scheduling services due to sdr changes")
@ -247,23 +245,26 @@ class ServiceHandler(SdrSourceEventClient):
return None return None
return best["groups"] return best["groups"]
def setupService(self, mode, frequency, source): def setupService(self, dial, source):
logger.debug("setting up service {0} on frequency {1}".format(mode, frequency)) 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): 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 return None
if "underlying" in dial:
modeObject = modeObject.for_underlying(dial["underlying"])
demod = self._getDemodulator(modeObject.get_modulation()) demod = self._getDemodulator(modeObject.get_modulation())
secondaryDemod = self._getSecondaryDemodulator(modeObject.modulation) secondaryDemod = self._getSecondaryDemodulator(modeObject.modulation)
center_freq = source.getProps()["center_freq"] center_freq = source.getProps()["center_freq"]
sampleRate = source.getProps()["samp_rate"] sampleRate = source.getProps()["samp_rate"]
bandpass = modeObject.get_bandpass() bandpass = modeObject.get_bandpass()
if isinstance(secondaryDemod, DialFrequencyReceiver): 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.setBandPass(bandpass.low_cut, bandpass.high_cut)
chain.setReader(source.getBuffer().getReader()) chain.setReader(source.getBuffer().getReader())