2021-02-20 18:20:31 +00:00
|
|
|
from owrx.form import Input, CheckboxInput, DropdownInput, DropdownEnum, TextInput
|
2021-02-20 17:48:12 +00:00
|
|
|
from owrx.form.converter import OptionalConverter, EnumConverter
|
2021-02-19 23:16:32 +00:00
|
|
|
from owrx.soapy import SoapySettings
|
2021-02-19 20:07:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
class GainInput(Input):
|
2021-02-19 23:16:32 +00:00
|
|
|
def __init__(self, id, label, gain_stages=None):
|
|
|
|
super().__init__(id, label)
|
|
|
|
self.gain_stages = gain_stages
|
2021-02-19 20:07:13 +00:00
|
|
|
|
2021-02-19 23:16:32 +00:00
|
|
|
def render_input(self, value):
|
2021-02-19 23:36:18 +00:00
|
|
|
try:
|
|
|
|
display_value = float(value)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
display_value = "0.0"
|
|
|
|
|
2021-02-19 20:07:13 +00:00
|
|
|
return """
|
|
|
|
<div id="{id}">
|
|
|
|
<select class="{classes}" id="{id}-select" name="{id}-select">
|
2021-02-19 23:16:32 +00:00
|
|
|
{options}
|
2021-02-19 20:07:13 +00:00
|
|
|
</select>
|
|
|
|
<div class="option manual" style="display: none;">
|
2021-02-19 23:36:18 +00:00
|
|
|
<input type="number" id="{id}-manual" name="{id}-manual" value="{value}" class="{classes}"
|
|
|
|
placeholder="Manual device gain" step="any">
|
2021-02-19 20:07:13 +00:00
|
|
|
</div>
|
2021-02-19 23:16:32 +00:00
|
|
|
{stageoption}
|
2021-02-19 20:07:13 +00:00
|
|
|
</div>
|
|
|
|
""".format(
|
|
|
|
id=self.id,
|
|
|
|
classes=self.input_classes(),
|
2021-02-19 23:36:18 +00:00
|
|
|
value=display_value,
|
2021-02-19 20:07:13 +00:00
|
|
|
label=self.label,
|
2021-02-19 23:16:32 +00:00
|
|
|
options=self.render_options(value),
|
2021-02-19 23:36:18 +00:00
|
|
|
stageoption="" if self.gain_stages is None else self.render_stage_option(value),
|
2021-02-19 23:16:32 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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(
|
2021-02-19 23:36:18 +00:00
|
|
|
value=v[0], text=v[1], selected="selected" if mode == v[0] else ""
|
2021-02-19 23:16:32 +00:00
|
|
|
)
|
|
|
|
for v in options
|
|
|
|
)
|
|
|
|
|
|
|
|
def getMode(self, value):
|
|
|
|
if value is None or value == "auto":
|
|
|
|
return "auto"
|
|
|
|
|
|
|
|
try:
|
|
|
|
float(value)
|
|
|
|
return "manual"
|
2021-02-19 23:36:18 +00:00
|
|
|
except (ValueError, TypeError):
|
2021-02-19 23:16:32 +00:00
|
|
|
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>
|
2021-02-19 23:36:18 +00:00
|
|
|
<input type="number" id="{id}-{stage}" name="{id}-{stage}" value="{value}"
|
|
|
|
class="col-9 {classes}" placeholder="{stage}" step="any">
|
2021-02-19 23:16:32 +00:00
|
|
|
</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
|
|
|
|
)
|
2021-02-19 20:07:13 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
def parse(self, data):
|
2021-02-19 23:16:32 +00:00
|
|
|
def getStageValue(stage):
|
|
|
|
input_id = "{id}-{stage}".format(id=self.id, stage=stage)
|
|
|
|
if input_id in data:
|
|
|
|
return data[input_id][0]
|
|
|
|
else:
|
2021-02-19 23:36:18 +00:00
|
|
|
return None
|
2021-02-19 23:16:32 +00:00
|
|
|
|
2021-02-19 20:07:13 +00:00
|
|
|
select_id = "{id}-select".format(id=self.id)
|
|
|
|
if select_id in data:
|
2021-02-19 23:16:32 +00:00
|
|
|
if data[select_id][0] == "manual":
|
|
|
|
input_id = "{id}-manual".format(id=self.id)
|
|
|
|
value = 0.0
|
|
|
|
if input_id in data:
|
|
|
|
try:
|
2021-02-19 23:36:18 +00:00
|
|
|
value = float(data[input_id][0])
|
2021-02-19 23:16:32 +00:00
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
return {self.id: value}
|
2021-02-19 23:36:18 +00:00
|
|
|
if self.gain_stages is not None and data[select_id][0] == "stages":
|
2021-02-19 23:16:32 +00:00
|
|
|
settings_dict = [{s: getStageValue(s)} for s in self.gain_stages]
|
2021-02-19 23:36:18 +00:00
|
|
|
# filter out empty ones
|
|
|
|
settings_dict = [s for s in settings_dict if next(iter(s.values()))]
|
2021-02-19 23:16:32 +00:00
|
|
|
return {self.id: SoapySettings.encode(settings_dict)}
|
|
|
|
|
2021-02-19 20:07:13 +00:00
|
|
|
return {self.id: None}
|
2021-02-20 17:48:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
class BiasTeeInput(CheckboxInput):
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__(
|
|
|
|
"bias_tee", "", "Enable Bias-Tee power supply", converter=OptionalConverter(defaultFormValue=False)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class DirectSamplingOptions(DropdownEnum):
|
|
|
|
DIRECT_SAMPLING_OFF = (0, "Off")
|
|
|
|
DIRECT_SAMPLING_I = (1, "Direct Sampling (I branch)")
|
|
|
|
DIRECT_SAMPLING_Q = (2, "Direct Sampling (Q branch)")
|
|
|
|
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
|
|
value, description = args
|
|
|
|
obj = object.__new__(cls)
|
|
|
|
obj._value_ = value
|
|
|
|
obj.description = description
|
|
|
|
return obj
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.description
|
|
|
|
|
|
|
|
|
|
|
|
class DirectSamplingInput(DropdownInput):
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__(
|
|
|
|
"direct_sampling",
|
|
|
|
"Direct Sampling",
|
|
|
|
DirectSamplingOptions,
|
|
|
|
converter=OptionalConverter(
|
|
|
|
EnumConverter(DirectSamplingOptions),
|
|
|
|
defaultFormValue=DirectSamplingOptions.DIRECT_SAMPLING_OFF.name,
|
|
|
|
),
|
|
|
|
)
|
2021-02-20 18:20:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RemoteInput(TextInput):
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__(
|
|
|
|
"remote", "Remote IP and Port", infotext="Remote hostname or IP and port to connect to. Format = IP:Port"
|
|
|
|
)
|