implement individual gain stages option
This commit is contained in:
parent
d0d946e09f
commit
bd7e5b7166
@ -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:
|
||||||
|
if data[select_id][0] == "manual":
|
||||||
input_id = "{id}-manual".format(id=self.id)
|
input_id = "{id}-manual".format(id=self.id)
|
||||||
if data[select_id][0] == "manual" and input_id in data:
|
value = 0.0
|
||||||
return {self.id: float(data[input_id][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}
|
||||||
|
@ -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
21
owrx/soapy.py
Normal 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])
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user