From c5df6a1527f80ec51064ef46ff8614d8de9d2a32 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 23 Feb 2021 18:32:23 +0100 Subject: [PATCH] implement profile editing page --- owrx/controllers/settings/sdr.py | 69 +++++++++++++++++++++++++++----- owrx/form/__init__.py | 6 +++ owrx/http.py | 17 ++++++-- owrx/source/__init__.py | 23 ++++++++++- owrx/source/airspy.py | 3 ++ owrx/source/connector.py | 3 ++ owrx/source/hackrf.py | 3 ++ owrx/source/rtl_sdr.py | 3 ++ owrx/source/rtl_sdr_soapy.py | 3 ++ owrx/source/sdrplay.py | 3 ++ owrx/source/soapy.py | 3 ++ 11 files changed, 120 insertions(+), 16 deletions(-) diff --git a/owrx/controllers/settings/sdr.py b/owrx/controllers/settings/sdr.py index 926001e..855355b 100644 --- a/owrx/controllers/settings/sdr.py +++ b/owrx/controllers/settings/sdr.py @@ -5,6 +5,7 @@ from owrx.source import SdrDeviceDescription, SdrDeviceDescriptionMissing from owrx.config import Config from urllib.parse import quote, unquote from owrx.sdr import SdrService +from abc import ABCMeta class SdrDeviceListController(AuthorizationMixin, WebpageController): @@ -52,14 +53,11 @@ class SdrDeviceListController(AuthorizationMixin, WebpageController): self.serve_template("settings/general.html", **self.template_variables()) -class SdrDeviceController(SettingsFormController): +class SdrFormController(SettingsFormController, metaclass=ABCMeta): def __init__(self, handler, request, options): super().__init__(handler, request, options) self.device_id, self.device = self._get_device() - def getData(self): - return self.device - def store(self): # need to overwrite the existing key in the config since the layering won't capture the changes otherwise config = Config.get() @@ -68,10 +66,21 @@ class SdrDeviceController(SettingsFormController): config["sdrs"] = sdrs super().store() + def _get_device(self): + device_id = unquote(self.request.matches.group(1)) + if device_id not in Config.get()["sdrs"]: + return None + return device_id, Config.get()["sdrs"][device_id] + + +class SdrDeviceController(SdrFormController): + def getData(self): + return self.device + def getSections(self): try: description = SdrDeviceDescription.getByType(self.device["type"]) - return [description.getSection()] + return [description.getDeviceSection()] except SdrDeviceDescriptionMissing: # TODO provide a generic interface that allows to switch the type return [] @@ -79,12 +88,6 @@ class SdrDeviceController(SettingsFormController): def getTitle(self): return self.device["name"] - def _get_device(self): - device_id = unquote(self.request.matches.group(1)) - if device_id not in Config.get()["sdrs"]: - return None - return device_id, Config.get()["sdrs"][device_id] - def header_variables(self): variables = super().header_variables() variables["assets_prefix"] = "../../" @@ -123,3 +126,47 @@ class SdrDeviceController(SettingsFormController): self.send_response("device not found", code=404) return self.serve_template("settings/general.html", **self.template_variables()) + + +class SdrProfileController(SdrFormController): + def __init__(self, handler, request, options): + super().__init__(handler, request, options) + self.profile_id, self.profile = self._get_profile() + + def getData(self): + return self.profile + + def _get_profile(self): + profile_id = unquote(self.request.matches.group(2)) + if self.device_id not in Config.get()["sdrs"]: + return None + if profile_id not in Config.get()["sdrs"][self.device_id]["profiles"]: + return None + return profile_id, Config.get()["sdrs"][self.device_id]["profiles"][profile_id] + + def getSections(self): + try: + description = SdrDeviceDescription.getByType(self.device["type"]) + return [description.getProfileSection()] + except SdrDeviceDescriptionMissing: + # TODO provide a generic interface that allows to switch the type + return [] + + def getTitle(self): + return self.profile["name"] + + def header_variables(self): + variables = super().header_variables() + variables["assets_prefix"] = "../../../" + return variables + + def template_variables(self): + variables = super().template_variables() + variables["assets_prefix"] = "../../../" + return variables + + def indexAction(self): + if self.profile is None: + self.send_response("profile not found", code=404) + return + self.serve_template("settings/general.html", **self.template_variables()) diff --git a/owrx/form/__init__.py b/owrx/form/__init__.py index ecf17fd..08d6408 100644 --- a/owrx/form/__init__.py +++ b/owrx/form/__init__.py @@ -313,3 +313,9 @@ class DropdownInput(Input): class DropdownEnum(Enum): def toOption(self): return Option(self.name, str(self)) + + +class ModesInput(DropdownInput): + def __init__(self, id, label): + options = [Option(m.modulation, m.name) for m in Modes.getAvailableModes()] + super().__init__(id, label, options) diff --git a/owrx/http.py b/owrx/http.py index e3db2e6..588e251 100644 --- a/owrx/http.py +++ b/owrx/http.py @@ -6,7 +6,7 @@ from owrx.controllers.api import ApiController from owrx.controllers.metrics import MetricsController from owrx.controllers.settings import SettingsController from owrx.controllers.settings.general import GeneralSettingsController -from owrx.controllers.settings.sdr import SdrDeviceListController, SdrDeviceController +from owrx.controllers.settings.sdr import SdrDeviceListController, SdrDeviceController, SdrProfileController from owrx.controllers.settings.reporting import ReportingController from owrx.controllers.settings.backgrounddecoding import BackgroundDecodingController from owrx.controllers.settings.decoding import DecodingSettingsController @@ -117,11 +117,22 @@ class Router(object): ), StaticRoute("/settings/sdr", SdrDeviceListController), RegexRoute("^/settings/sdr/([^/]+)$", SdrDeviceController), - RegexRoute("^/settings/sdr/([^/]+)$", SdrDeviceController, method="POST", options={"action": "processFormData"}), + RegexRoute( + "^/settings/sdr/([^/]+)$", SdrDeviceController, method="POST", options={"action": "processFormData"} + ), + RegexRoute("^/settings/sdr/([^/]+)/([^/]+)$", SdrProfileController), + RegexRoute( + "^/settings/sdr/([^/]+)/([^/]+)$", + SdrProfileController, + method="POST", + options={"action": "processFormData"}, + ), StaticRoute("/settings/bookmarks", BookmarksController), StaticRoute("/settings/bookmarks", BookmarksController, method="POST", options={"action": "new"}), RegexRoute("^/settings/bookmarks/(.+)$", BookmarksController, method="POST", options={"action": "update"}), - RegexRoute("^/settings/bookmarks/(.+)$", BookmarksController, method="DELETE", options={"action": "delete"}), + RegexRoute( + "^/settings/bookmarks/(.+)$", BookmarksController, method="DELETE", options={"action": "delete"} + ), StaticRoute("/settings/reporting", ReportingController), StaticRoute( "/settings/reporting", ReportingController, method="POST", options={"action": "processFormData"} diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index c034102..bfb28d1 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -10,7 +10,7 @@ from abc import ABC, abstractmethod from owrx.command import CommandMapper from owrx.socket import getAvailablePort from owrx.property import PropertyStack, PropertyLayer -from owrx.form import Input, TextInput, NumberInput, CheckboxInput +from owrx.form import Input, TextInput, NumberInput, CheckboxInput, ModesInput from owrx.form.converter import OptionalConverter from owrx.form.device import GainInput from owrx.controllers.settings import Section @@ -492,6 +492,11 @@ class SdrDeviceDescription(object): NumberInput("waterfall_min_level", "Lowest waterfall level", append="dBFS"), NumberInput("waterfall_max_level", "Highest waterfall level", append="dBFS"), # TODO `schedule` + NumberInput("center_freq", "Center frequency", append="Hz"), + NumberInput("samp_rate", "Sample rate", append="S/s"), + NumberInput("start_freq", "Initial frequency", append="Hz"), + ModesInput("start_mod", "Initial modulation"), + NumberInput("initial_squelch_level", "Initial squelch level", append="dBFS"), ] def getMandatoryKeys(self): @@ -500,5 +505,19 @@ class SdrDeviceDescription(object): def getOptionalKeys(self): return ["ppm", "always-on", "services", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"] - def getSection(self): + def getProfileMandatoryKeys(self): + 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"] + + def getDeviceSection(self): return OptionalSection("Device settings", self.getInputs(), self.getMandatoryKeys(), self.getOptionalKeys()) + + def getProfileSection(self): + return OptionalSection( + "Profile settings", + self.getInputs(), + self.getProfileMandatoryKeys(), + self.getProfileOptionalKeys(), + ) diff --git a/owrx/source/airspy.py b/owrx/source/airspy.py index 96aad45..a6b64ff 100644 --- a/owrx/source/airspy.py +++ b/owrx/source/airspy.py @@ -34,5 +34,8 @@ class AirspyDeviceDescription(SoapyConnectorDeviceDescription): def getOptionalKeys(self): return super().getOptionalKeys() + ["bias_tee", "bitpack"] + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["bias_tee"] + def getGainStages(self): return ["LNA", "MIX", "VGA"] diff --git a/owrx/source/connector.py b/owrx/source/connector.py index 6af4692..6789e3f 100644 --- a/owrx/source/connector.py +++ b/owrx/source/connector.py @@ -92,3 +92,6 @@ class ConnectorDeviceDescription(SdrDeviceDescription): def getOptionalKeys(self): return super().getOptionalKeys() + ["rtltcp_compat", "iqswap"] + + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["iqswap"] diff --git a/owrx/source/hackrf.py b/owrx/source/hackrf.py index 86bf949..fbbab7d 100644 --- a/owrx/source/hackrf.py +++ b/owrx/source/hackrf.py @@ -21,5 +21,8 @@ class HackrfDeviceDescription(SoapyConnectorDeviceDescription): def getOptionalKeys(self): return super().getOptionalKeys() + ["bias_tee"] + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["bias_tee"] + def getGainStages(self): return ["LNA", "AMP", "VGA"] diff --git a/owrx/source/rtl_sdr.py b/owrx/source/rtl_sdr.py index a4da374..f725e3d 100644 --- a/owrx/source/rtl_sdr.py +++ b/owrx/source/rtl_sdr.py @@ -29,3 +29,6 @@ class RtlSdrDeviceDescription(ConnectorDeviceDescription): def getOptionalKeys(self): return super().getOptionalKeys() + ["device", "bias_tee", "direct_sampling"] + + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"] diff --git a/owrx/source/rtl_sdr_soapy.py b/owrx/source/rtl_sdr_soapy.py index 0257810..e437b80 100644 --- a/owrx/source/rtl_sdr_soapy.py +++ b/owrx/source/rtl_sdr_soapy.py @@ -20,3 +20,6 @@ class RtlSdrSoapyDeviceDescription(SoapyConnectorDeviceDescription): def getOptionalKeys(self): return super().getOptionalKeys() + ["bias_tee", "direct_sampling"] + + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"] diff --git a/owrx/source/sdrplay.py b/owrx/source/sdrplay.py index 19b1509..3bfb385 100644 --- a/owrx/source/sdrplay.py +++ b/owrx/source/sdrplay.py @@ -56,3 +56,6 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription): def getOptionalKeys(self): return super().getOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"] + + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"] diff --git a/owrx/source/soapy.py b/owrx/source/soapy.py index 02973f6..6a7372e 100644 --- a/owrx/source/soapy.py +++ b/owrx/source/soapy.py @@ -100,5 +100,8 @@ class SoapyConnectorDeviceDescription(ConnectorDeviceDescription): def getOptionalKeys(self): return super().getOptionalKeys() + ["device", "rf_gain", "antenna"] + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["antenna"] + def getGainStages(self): return None