diff --git a/owrx/controllers/settings.py b/owrx/controllers/settings.py
index 150745f..a787a2f 100644
--- a/owrx/controllers/settings.py
+++ b/owrx/controllers/settings.py
@@ -12,7 +12,9 @@ from owrx.form import (
Option,
ServicesCheckboxInput,
Js8ProfileCheckboxInput,
- ReceiverKeysInput,
+ ReceiverKeysConverter,
+ WfmTauValues,
+ WfmTauConverter
)
from urllib.parse import quote
import json
@@ -104,9 +106,10 @@ class GeneralSettingsController(AdminController):
),
Section(
"Receiver listings",
- ReceiverKeysInput(
+ TextInput(
"receiver_keys",
"Receiver keys",
+ converter=ReceiverKeysConverter(),
infotext="Put the keys you receive on listing sites (e.g. "
+ 'Receiverbook) here, one per line',
),
@@ -154,6 +157,17 @@ class GeneralSettingsController(AdminController):
CheckboxInput("digimodes_enable", "", checkboxText="Enable Digimodes"),
NumberInput("digimodes_fft_size", "Digimodes FFT size", append="bins"),
),
+ Section(
+ "Wideband FM settings",
+ DropdownInput(
+ "wfm_deemphasis_tau",
+ "Tau setting for WFM (broadcast FM) deemphasis",
+ options=[o.toOption() for o in WfmTauValues],
+ converter=WfmTauConverter(),
+ infotext='See '
+ + "this Wikipedia article for more information",
+ ),
+ ),
Section(
"Digital voice",
NumberInput(
diff --git a/owrx/form/__init__.py b/owrx/form/__init__.py
index 8ed4099..37f6537 100644
--- a/owrx/form/__init__.py
+++ b/owrx/form/__init__.py
@@ -1,13 +1,36 @@
from abc import ABC, abstractmethod
from owrx.modes import Modes
from owrx.config import Config
+from enum import Enum
+
+
+class Converter(ABC):
+ @abstractmethod
+ def convert_to_form(self, value):
+ pass
+
+ @abstractmethod
+ def convert_from_form(self, value):
+ pass
+
+
+class NullConverter(Converter):
+ def convert_to_form(self, value):
+ return value
+
+ def convert_from_form(self, value):
+ return value
class Input(ABC):
- def __init__(self, id, label, infotext=None):
+ def __init__(self, id, label, infotext=None, converter: Converter = None):
self.id = id
self.label = label
self.infotext = infotext
+ self.converter = self.defaultConverter() if converter is None else converter
+
+ def defaultConverter(self):
+ return NullConverter()
def bootstrap_decorate(self, input):
infotext = "{text}".format(text=self.infotext) if self.infotext else ""
@@ -30,17 +53,11 @@ class Input(ABC):
def render_input(self, value):
pass
- def convert_to_form(self, value):
- return value
-
- def convert_from_form(self, value):
- return value
-
def render(self, config):
- return self.bootstrap_decorate(self.render_input(self.convert_to_form(config[self.id])))
+ return self.bootstrap_decorate(self.render_input(self.converter.convert_to_form(config[self.id])))
def parse(self, data):
- return {self.id: self.convert_from_form(data[self.id][0])} if self.id in data else {}
+ return {self.id: self.converter.convert_from_form(data[self.id][0])} if self.id in data else {}
class TextInput(Input):
@@ -52,12 +69,23 @@ class TextInput(Input):
)
+class IntConverter(Converter):
+ def convert_to_form(self, value):
+ return str(value)
+
+ def convert_from_form(self, value):
+ return int(value)
+
+
class NumberInput(Input):
- def __init__(self, id, label, infotext=None, append=""):
- super().__init__(id, label, infotext)
+ def __init__(self, id, label, infotext=None, append="", converter: Converter = None):
+ super().__init__(id, label, infotext, converter=converter)
self.step = None
self.append = append
+ def defaultConverter(self):
+ return IntConverter()
+
def render_input(self, value):
if self.append:
append = """
@@ -84,17 +112,22 @@ class NumberInput(Input):
append=append,
)
- def convert_from_form(self, v):
- return int(v)
+
+class FloatConverter(Converter):
+ def convert_to_form(self, value):
+ return str(value)
+
+ def convert_from_form(self, value):
+ return float(value)
class FloatInput(NumberInput):
- def __init__(self, id, label, infotext=None):
- super().__init__(id, label, infotext)
+ def __init__(self, id, label, infotext=None, converter: Converter = None):
+ super().__init__(id, label, infotext, converter=converter)
self.step = "any"
- def convert_from_form(self, v):
- return float(v)
+ def defaultConverter(self):
+ return FloatConverter()
class LocationInput(Input):
@@ -137,7 +170,7 @@ class TextAreaInput(Input):
)
-class ReceiverKeysInput(TextAreaInput):
+class ReceiverKeysConverter(Converter):
def convert_to_form(self, value):
return "\n".join(value)
@@ -235,8 +268,8 @@ class Js8ProfileCheckboxInput(MultiCheckboxInput):
class DropdownInput(Input):
- def __init__(self, id, label, options, infotext=None):
- super().__init__(id, label, infotext=infotext)
+ def __init__(self, id, label, options, infotext=None, converter: Converter = None):
+ super().__init__(id, label, infotext=infotext, converter=converter)
self.options = options
def render_input(self, value):
@@ -258,3 +291,29 @@ class DropdownInput(Input):
for o in self.options
]
return "".join(options)
+
+
+class WfmTauValues(Enum):
+ TAU_50_MICRO = (50, "most regions")
+ TAU_75_MICRO = (75, "Americas and South Korea")
+
+ def __new__(cls, *args, **kwargs):
+ value, description = args
+ obj = object.__new__(cls)
+ obj._value_ = value
+ obj.description = description
+ return obj
+
+ def __str__(self):
+ return "{}µs ({})".format(self.value, self.description)
+
+ def toOption(self):
+ return Option(self.name, str(self))
+
+
+class WfmTauConverter(Converter):
+ def convert_to_form(self, value):
+ return WfmTauValues(value * 1e6).name
+
+ def convert_from_form(self, value):
+ return WfmTauValues[value].value / 1e6