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.form import Input
from owrx.soapy import SoapySettings
class GainInput(Input): class GainInput(Input):
def render_input(self, value): def __init__(self, id, label, gain_stages=None):
auto_mode = value is None or value == "auto" super().__init__(id, label)
self.gain_stages = gain_stages
def render_input(self, value):
return """ return """
<div id="{id}"> <div id="{id}">
<select class="{classes}" id="{id}-select" name="{id}-select"> <select class="{classes}" id="{id}-select" name="{id}-select">
<option value="auto" {auto_selected}>Enable hardware AGC</option> {options}
<option value="manual" {manual_selected}>Specify manual gain</option>
</select> </select>
<div class="option manual" style="display: none;"> <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> </div>
{stageoption}
</div> </div>
""".format( """.format(
id=self.id, id=self.id,
classes=self.input_classes(), classes=self.input_classes(),
value=value, value="0.0" if value is None else value,
label=self.label, label=self.label,
auto_selected="selected" if auto_mode else "", options=self.render_options(value),
manual_selected="" if auto_mode else "selected", 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 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) select_id = "{id}-select".format(id=self.id)
if select_id in data: if select_id in data:
input_id = "{id}-manual".format(id=self.id) if data[select_id][0] == "manual":
if data[select_id][0] == "manual" and input_id in data: input_id = "{id}-manual".format(id=self.id)
return {self.id: float(data[input_id][0])} 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} 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 typing import List
from owrx.form import Input, TextInput from owrx.form import Input, TextInput
from owrx.form.converter import OptionalConverter 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): class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta):
@ -33,25 +34,6 @@ class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta):
def getEventNames(self): def getEventNames(self):
return super().getEventNames() + list(self.getSoapySettingsMappings().keys()) 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): def buildSoapyDeviceParameters(self, parsed, values):
""" """
this method always attempts to inject a driver= part into the soapysdr query, depending on what connector was used. 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): def getCommandValues(self):
values = super().getCommandValues() values = super().getCommandValues()
if "device" in values and values["device"] is not None: if "device" in values and values["device"] is not None:
parsed = self.parseDeviceString(values["device"]) parsed = SoapySettings.parse(values["device"])
else: else:
parsed = [] parsed = []
modified = self.buildSoapyDeviceParameters(parsed, values) 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()]) settings = ",".join(["{0}={1}".format(k, v) for k, v in self.buildSoapySettings(values).items()])
if len(settings): if len(settings):
values["soapy_settings"] = settings values["soapy_settings"] = settings
@ -111,7 +93,7 @@ class SoapyConnectorDeviceDescription(ConnectorDeviceDescription):
infotext='SoapySDR device identifier string (example: "serial=123456789")', infotext='SoapySDR device identifier string (example: "serial=123456789")',
converter=OptionalConverter() converter=OptionalConverter()
), ),
SoapyGainInput( GainInput(
"rf_gain", "rf_gain",
"Device Gain", "Device Gain",
gain_stages=self.getGainStages(), gain_stages=self.getGainStages(),
@ -120,4 +102,4 @@ class SoapyConnectorDeviceDescription(ConnectorDeviceDescription):
) )
def getGainStages(self): def getGainStages(self):
return [] return None