diff --git a/config_webrx.py b/config_webrx.py index e75bce8..636f65f 100644 --- a/config_webrx.py +++ b/config_webrx.py @@ -33,7 +33,7 @@ config_webrx: configuration options for OpenWebRX """ # configuration version. please only modify if you're able to perform the associated migration steps. -version = 4 +version = 5 # NOTE: you can find additional information about configuring OpenWebRX in the Wiki: # https://github.com/jketterl/openwebrx/wiki/Configuration-guide @@ -258,13 +258,12 @@ sdrs = { #waterfall_colors = [0x0000FF, 0x00FF00, 0xFF0000] ### Waterfall calibration -#waterfall_min_level = -88 # in dB -#waterfall_max_level = -20 +#waterfall_levels = {"min": -88, "max": -20} # in dB waterfall_auto_level_margin = {"min": 3, "max": 10, "min_range": 50} # Note: When the auto waterfall level button is clicked, the following happens: -# [waterfall_min_level] = [current_min_power_level] - [waterfall_auto_level_margin["min"]] -# [waterfall_max_level] = [current_max_power_level] + [waterfall_auto_level_margin["max"]] +# [waterfall_levels.min] = [current_min_power_level] - [waterfall_auto_level_margin["min"]] +# [waterfall_levels.max] = [current_max_power_level] + [waterfall_auto_level_margin["max"]] # # ___|________________________________________|____________________________________|________________________________________|___> signal power # \_waterfall_auto_level_margin["min"]_/ |__ current_min_power_level | \_waterfall_auto_level_margin["max"]_/ diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index d7f03bd..a1213bf 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -717,10 +717,10 @@ function on_ws_recv(evt) { var config = json['value']; if ('waterfall_colors' in config) waterfall_colors = buildWaterfallColors(config['waterfall_colors']); - if ('waterfall_min_level' in config) - waterfall_min_level_default = config['waterfall_min_level']; - if ('waterfall_max_level' in config) - waterfall_max_level_default = config['waterfall_max_level']; + if ('waterfall_levels' in config) { + waterfall_min_level_default = config['waterfall_levels']['min']; + waterfall_max_level_default = config['waterfall_levels']['max']; + } if ('waterfall_auto_level_margin' in config) waterfall_auto_level_margin = config['waterfall_auto_level_margin']; waterfallColorsDefault(); diff --git a/owrx/config/defaults.py b/owrx/config/defaults.py index 6ce3820..afb74d8 100644 --- a/owrx/config/defaults.py +++ b/owrx/config/defaults.py @@ -2,7 +2,7 @@ from owrx.property import PropertyLayer defaultConfig = PropertyLayer( - version=4, + version=5, max_clients=20, receiver_name="[Callsign]", receiver_location="Budapest, Hungary", @@ -23,8 +23,7 @@ defaultConfig = PropertyLayer( digital_voice_dmr_id_lookup=True, # sdrs=... waterfall_scheme="GoogleTurboWaterfall", - waterfall_min_level=-88, - waterfall_max_level=-20, + waterfall_levels=PropertyLayer(min=-88, max=-20), waterfall_auto_level_margin=PropertyLayer(min=3, max=10, min_range=50), frequency_display_precision=4, squelch_auto_margin=10, diff --git a/owrx/config/dynamic.py b/owrx/config/dynamic.py index b636468..97ecd44 100644 --- a/owrx/config/dynamic.py +++ b/owrx/config/dynamic.py @@ -59,3 +59,6 @@ class DynamicConfig(PropertyLayer): def __dict__(self): return {k: v for k, v in super().__dict__().items() if v is not DynamicConfig._deleted} + + def keys(self): + return [k for k in super().keys() if self.__contains__(k)] diff --git a/owrx/config/migration.py b/owrx/config/migration.py index 21e9836..a241e01 100644 --- a/owrx/config/migration.py +++ b/owrx/config/migration.py @@ -41,7 +41,6 @@ class ConfigMigratorVersion2(ConfigMigrator): class ConfigMigratorVersion3(ConfigMigrator): - def migrate(self, config): # inline import due to circular dependencies from owrx.waterfall import WaterfallOptions @@ -61,12 +60,42 @@ class ConfigMigratorVersion3(ConfigMigrator): config["version"] = 4 +class ConfigMigratorVersion4(ConfigMigrator): + def _replaceWaterfallLevels(self, instance): + if ( + "waterfall_min_level" in instance + and "waterfall_max_level" in instance + and not "waterfall_levels" in instance + ): + instance["waterfall_levels"] = { + "min": instance["waterfall_min_level"], + "max": instance["waterfall_max_level"], + } + del instance["waterfall_min_level"] + del instance["waterfall_max_level"] + + def migrate(self, config): + # migrate root level + self._replaceWaterfallLevels(config) + if "sdrs" in config: + for device in config["sdrs"].__dict__().values(): + # migrate device level + self._replaceWaterfallLevels(device) + if "profiles" in device: + for profile in device["profiles"].__dict__().values(): + # migrate profile level + self._replaceWaterfallLevels(profile) + + config["version"] = 5 + + class Migrator(object): - currentVersion = 4 + currentVersion = 5 migrators = { 1: ConfigMigratorVersion1(), 2: ConfigMigratorVersion2(), 3: ConfigMigratorVersion3(), + 4: ConfigMigratorVersion4(), } @staticmethod diff --git a/owrx/connection.py b/owrx/connection.py index 03c3350..d215b90 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -113,8 +113,7 @@ class OpenWebRxClient(Client, metaclass=ABCMeta): class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): sdr_config_keys = [ - "waterfall_min_level", - "waterfall_max_level", + "waterfall_levels", "samp_rate", "start_mod", "start_freq", diff --git a/owrx/controllers/settings/general.py b/owrx/controllers/settings/general.py index 07f9a7e..84cd36f 100644 --- a/owrx/controllers/settings/general.py +++ b/owrx/controllers/settings/general.py @@ -12,6 +12,7 @@ from owrx.form import ( from owrx.form.converter import WaterfallColorsConverter from owrx.form.receiverid import ReceiverKeysConverter from owrx.form.gfx import AvatarInput, TopPhotoInput +from owrx.form.device import WaterfallLevelsInput from owrx.waterfall import WaterfallOptions import shutil import os @@ -102,8 +103,7 @@ class GeneralSettingsController(SettingsFormController): infotext="If fft_voverlap_factor is above 0, multiple FFTs will be used for creating a line on the " + "diagram.", ), - NumberInput("waterfall_min_level", "Lowest waterfall level", append="dBFS"), - NumberInput("waterfall_max_level", "Highest waterfall level", append="dBFS"), + WaterfallLevelsInput("waterfall_levels", "Waterfall levels"), ), Section( "Compression", diff --git a/owrx/form/device.py b/owrx/form/device.py index 8893093..222685a 100644 --- a/owrx/form/device.py +++ b/owrx/form/device.py @@ -338,3 +338,47 @@ class SchedulerInput(Input): return {self.id: {"type": "daylight", "schedule": settings_dict}} return {} + + +class WaterfallLevelsInput(Input): + def render_input(self, value): + return """ +
+ {inputs} +
+ """.format( + id=self.id, + inputs="".join( + """ +
+ +
+ +
+ dBFS +
+
+
+ """.format( + id=self.id, + name=name, + label=label, + value=value[name] if value and name in value else "0", + classes=self.input_classes(), + disabled="disabled" if self.disabled else "", + ) + for name, label in [("min", "Minimum"), ("max", "Maximum")] + ) + ) + + def parse(self, data): + def getValue(name): + key = "{}-{}".format(self.id, name) + if key in data: + return {name: data[key][0]} + raise KeyError("waterfall key not found") + + try: + return {self.id: {k: v for name in ["min", "max"] for k, v in getValue(name).items()}} + except KeyError: + return {} diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index d71ca10..aa4cac3 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -13,7 +13,7 @@ from owrx.property import PropertyStack, PropertyLayer, PropertyFilter from owrx.property.filter import ByLambda from owrx.form import Input, TextInput, NumberInput, CheckboxInput, ModesInput from owrx.form.converter import OptionalConverter -from owrx.form.device import GainInput, SchedulerInput +from owrx.form.device import GainInput, SchedulerInput, WaterfallLevelsInput from owrx.controllers.settings import Section from typing import List from enum import Enum, auto @@ -492,8 +492,7 @@ class SdrDeviceDescription(object): infotext="Use this when the actual receiving frequency differs from the frequency to be tuned on the" + " device.
Formula: Center frequency + oscillator offset = sdr tune frequency", ), - NumberInput("waterfall_min_level", "Lowest waterfall level", append="dBFS"), - NumberInput("waterfall_max_level", "Highest waterfall level", append="dBFS"), + WaterfallLevelsInput("waterfall_levels", "Waterfall levels"), SchedulerInput("scheduler", "Scheduler"), NumberInput("center_freq", "Center frequency", append="Hz"), NumberInput("samp_rate", "Sample rate", append="S/s"), @@ -516,8 +515,7 @@ class SdrDeviceDescription(object): "services", "rf_gain", "lfo_offset", - "waterfall_min_level", - "waterfall_max_level", + "waterfall_levels", "scheduler", ] @@ -525,7 +523,7 @@ class SdrDeviceDescription(object): return ["center_freq", "samp_rate", "start_freq", "start_mod"] def getProfileOptionalKeys(self): - return ["initial_squelch_level", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"] + return ["initial_squelch_level", "rf_gain", "lfo_offset", "waterfall_levels"] def getDeviceSection(self): return OptionalSection("Device settings", self.getInputs(), self.getMandatoryKeys(), self.getOptionalKeys())