implement individual gain stages option

This commit is contained in:
Jakob Ketterl 2021-02-20 00:16:32 +01:00
parent d0d946e09f
commit bd7e5b7166
4 changed files with 118 additions and 48 deletions

View File

@ -1,33 +1,113 @@
from owrx.form import Input
from owrx.soapy import SoapySettings
class GainInput(Input):
def render_input(self, value):
auto_mode = value is None or value == "auto"
def __init__(self, id, label, gain_stages=None):
super().__init__(id, label)
self.gain_stages = gain_stages
def render_input(self, value):
return """
<div id="{id}">
<select class="{classes}" id="{id}-select" name="{id}-select">
<option value="auto" {auto_selected}>Enable hardware AGC</option>
<option value="manual" {manual_selected}>Specify manual gain</option>
{options}
</select>
<div class="option manual" style="display: none;">
<input type="number" id="{id}-manual" name="{id}-manual" value="{value}" class="{classes}" placeholder="Manual device gain" value="{value}" step="any">
<input type="number" id="{id}-manual" name="{id}-manual" value="{value}" class="{classes}" placeholder="Manual device gain" step="any">
</div>
{stageoption}
</div>
""".format(
id=self.id,
classes=self.input_classes(),
value=value,
value="0.0" if value is None else value,
label=self.label,
auto_selected="selected" if auto_mode else "",
manual_selected="" if auto_mode else "selected",
options=self.render_options(value),
stageoption=self.render_stage_option(value),
)
def render_options(self, value):
options = [
("auto", "Enable hardware AGC"),
("manual", "Specify manual gain"),
]
if self.gain_stages:
options.append(("stages", "Specify gain stages individually"))
mode = self.getMode(value)
return "".join(
"""
<option value="{value}" {selected}>{text}</option>
""".format(
value=v[0],
text=v[1],
selected="selected" if mode == v[0] else ""
)
for v in options
)
def getMode(self, value):
if value is None or value == "auto":
return "auto"
try:
float(value)
return "manual"
except ValueError:
pass
return "stages"
def render_stage_option(self, value):
try:
value_dict = {k: v for item in SoapySettings.parse(value) for k, v in item.items()}
except (AttributeError, ValueError):
value_dict = {}
return """
<div class="option stages container container-fluid" style="display: none;">
{inputs}
</div>
""".format(
inputs="".join(
"""
<div class="row">
<div class="col-3">{stage}</div>
<input type="number" id="{id}-{stage}" name="{id}-{stage}" value="{value}" class="col-9 {classes}" placeholder="{stage}" step="any">
</div>
""".format(
id=self.id,
stage=stage,
value=value_dict[stage] if stage in value_dict else "",
classes=self.input_classes(),
)
for stage in self.gain_stages
)
)
def parse(self, data):
def getStageValue(stage):
input_id = "{id}-{stage}".format(id=self.id, stage=stage)
if input_id in data:
return data[input_id][0]
else:
return 0.0
select_id = "{id}-select".format(id=self.id)
if select_id in data:
input_id = "{id}-manual".format(id=self.id)
if data[select_id][0] == "manual" and input_id in data:
return {self.id: float(data[input_id][0])}
if data[select_id][0] == "manual":
input_id = "{id}-manual".format(id=self.id)
value = 0.0
if input_id in data:
try:
value = float(float(data[input_id][0]))
except ValueError:
pass
return {self.id: value}
if data[select_id][0] == "stages":
settings_dict = [{s: getStageValue(s)} for s in self.gain_stages]
return {self.id: SoapySettings.encode(settings_dict)}
return {self.id: None}

View File

@ -1,13 +0,0 @@
from owrx.form import FloatInput
class SoapyGainInput(FloatInput):
def __init__(self, id, label, gain_stages):
super().__init__(id, label)
self.gain_stages = gain_stages
def render_input(self, value):
if not self.gain_stages:
return super().render_input(value)
# TODO implement input for multiple gain stages here
return "soapy gain stages here..."

21
owrx/soapy.py Normal file
View File

@ -0,0 +1,21 @@
class SoapySettings(object):
@staticmethod
def parse(dstr):
def decodeComponent(c):
kv = c.split("=", 1)
if len(kv) < 2:
return c
else:
return {kv[0]: kv[1]}
return [decodeComponent(c) for c in dstr.split(",")]
@staticmethod
def encode(dobj):
def encodeComponent(c):
if isinstance(c, str):
return c
else:
return ",".join(["{0}={1}".format(key, value) for key, value in c.items()])
return ",".join([encodeComponent(c) for c in dobj])

View File

@ -4,7 +4,8 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription
from typing import List
from owrx.form import Input, TextInput
from owrx.form.converter import OptionalConverter
from owrx.form.soapy import SoapyGainInput
from owrx.form.device import GainInput
from owrx.soapy import SoapySettings
class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta):
@ -33,25 +34,6 @@ class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta):
def getEventNames(self):
return super().getEventNames() + list(self.getSoapySettingsMappings().keys())
def parseDeviceString(self, dstr):
def decodeComponent(c):
kv = c.split("=", 1)
if len(kv) < 2:
return c
else:
return {kv[0]: kv[1]}
return [decodeComponent(c) for c in dstr.split(",")]
def encodeDeviceString(self, dobj):
def encodeComponent(c):
if isinstance(c, str):
return c
else:
return ",".join(["{0}={1}".format(key, value) for key, value in c.items()])
return ",".join([encodeComponent(c) for c in dobj])
def buildSoapyDeviceParameters(self, parsed, values):
"""
this method always attempts to inject a driver= part into the soapysdr query, depending on what connector was used.
@ -79,11 +61,11 @@ class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta):
def getCommandValues(self):
values = super().getCommandValues()
if "device" in values and values["device"] is not None:
parsed = self.parseDeviceString(values["device"])
parsed = SoapySettings.parse(values["device"])
else:
parsed = []
modified = self.buildSoapyDeviceParameters(parsed, values)
values["device"] = self.encodeDeviceString(modified)
values["device"] = SoapySettings.encode(modified)
settings = ",".join(["{0}={1}".format(k, v) for k, v in self.buildSoapySettings(values).items()])
if len(settings):
values["soapy_settings"] = settings
@ -111,7 +93,7 @@ class SoapyConnectorDeviceDescription(ConnectorDeviceDescription):
infotext='SoapySDR device identifier string (example: "serial=123456789")',
converter=OptionalConverter()
),
SoapyGainInput(
GainInput(
"rf_gain",
"Device Gain",
gain_stages=self.getGainStages(),
@ -120,4 +102,4 @@ class SoapyConnectorDeviceDescription(ConnectorDeviceDescription):
)
def getGainStages(self):
return []
return None