introduce the basic concept of optional keys

This commit is contained in:
Jakob Ketterl 2021-02-22 00:35:47 +01:00
parent 683a711b49
commit 770fd749cd
12 changed files with 146 additions and 122 deletions

View File

@ -71,7 +71,7 @@ class SdrDeviceController(SettingsFormController):
def getSections(self):
try:
description = SdrDeviceDescription.getByType(self.device["type"])
return [description.getSection()]
return [description.getSection(self.device)]
except SdrDeviceDescriptionMissing:
# TODO provide a generic interface that allows to switch the type
return []

View File

@ -376,6 +376,9 @@ class SdrDeviceDescriptionMissing(Exception):
class SdrDeviceDescription(object):
def __init__(self):
self.indexedInputs = {input.id: input for input in self.getInputs()}
@staticmethod
def getByType(sdr_type: str) -> "SdrDeviceDescription":
try:
@ -423,10 +426,14 @@ class SdrDeviceDescription(object):
# TODO `schedule`
]
def mergeInputs(self, *args):
# build a dictionary indexed by the input id to make sure every id only exists once
inputs = {input.id: input for input_list in args for input in input_list}
return inputs.values()
def getMandatoryKeys(self):
return ["name", "enabled"]
def getSection(self):
return Section("Device settings", *self.getInputs())
def getOptionalKeys(self):
return ["ppm", "always-on", "services", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
def getSection(self, data):
visible_keys = set(self.getMandatoryKeys() + [k for k in self.getOptionalKeys() if k in data])
inputs = [input for k, input in self.indexedInputs.items() if k in visible_keys]
# TODO: render remaining keys in optional area
return Section("Device settings", *inputs)

View File

@ -22,17 +22,21 @@ class AirspySource(SoapyConnectorSource):
class AirspyDeviceDescription(SoapyConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(
super().getInputs(),
[
BiasTeeInput(),
CheckboxInput(
"bitpack",
"",
checkboxText="Enable bit-packing",
infotext="Packs two 12-bit samples into 3 bytes."
+ " Lowers USB bandwidth consumption, increases CPU load",
converter=OptionalConverter(defaultFormValue=False),
),
],
)
return super().getInputs() + [
BiasTeeInput(),
CheckboxInput(
"bitpack",
"",
checkboxText="Enable bit-packing",
infotext="Packs two 12-bit samples into 3 bytes."
+ " Lowers USB bandwidth consumption, increases CPU load",
converter=OptionalConverter(defaultFormValue=False),
),
]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee", "bitpack"]
# TODO: find actual gain stages for airspay
# def getGainStages(self):
# return None

View File

@ -76,23 +76,23 @@ class ConnectorSource(SdrSource):
class ConnectorDeviceDescription(SdrDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(
super().getInputs(),
[
NumberInput(
"rtltcp_compat",
"Port for rtl_tcp compatible data",
infotext="Activate an rtl_tcp compatible interface on the port number specified.<br />"
+ "Note: Port is only available on the local machine, not on the network.<br />"
+ "Note: IQ data may be degraded by the downsampling process to 8 bits.",
converter=OptionalConverter(IntConverter()),
),
CheckboxInput(
"iqswap",
"",
checkboxText="Swap I and Q channels",
infotext="Swapping inverts the spectrum, so this is useful in combination with an inverting mixer",
converter=OptionalConverter(defaultFormValue=False),
),
],
)
return super().getInputs() + [
NumberInput(
"rtltcp_compat",
"Port for rtl_tcp compatible data",
infotext="Activate an rtl_tcp compatible interface on the port number specified.<br />"
+ "Note: Port is only available on the local machine, not on the network.<br />"
+ "Note: IQ data may be degraded by the downsampling process to 8 bits.",
converter=OptionalConverter(IntConverter()),
),
CheckboxInput(
"iqswap",
"",
checkboxText="Swap I and Q channels",
infotext="Swapping inverts the spectrum, so this is useful in combination with an inverting mixer",
converter=OptionalConverter(defaultFormValue=False),
),
]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["rtltcp_compat", "iqswap"]

View File

@ -16,4 +16,11 @@ class HackrfSource(SoapyConnectorSource):
class HackrfDeviceDescription(SoapyConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(super().getInputs(), [BiasTeeInput()])
return super().getInputs() + [BiasTeeInput()]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee"]
# TODO: find actual gain stages for hackrf
# def getGainStages(self):
# return None

View File

@ -18,16 +18,16 @@ class RtlSdrSource(ConnectorSource):
class RtlSdrDeviceDescription(ConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(
super().getInputs(),
[
TextInput(
"device",
"Device identifier",
infotext="Device serial number or index",
converter=OptionalConverter(),
),
BiasTeeInput(),
DirectSamplingInput()
],
)
return super().getInputs() + [
TextInput(
"device",
"Device identifier",
infotext="Device serial number or index",
converter=OptionalConverter(),
),
BiasTeeInput(),
DirectSamplingInput()
]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["device", "bias_tee", "direct_sampling"]

View File

@ -16,4 +16,7 @@ class RtlSdrSoapySource(SoapyConnectorSource):
class RtlSdrSoapyDeviceDescription(SoapyConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(super().getInputs(), [BiasTeeInput(), DirectSamplingInput()])
return super().getInputs() + [BiasTeeInput(), DirectSamplingInput()]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee", "direct_sampling"]

View File

@ -23,4 +23,7 @@ class RtlTcpSource(ConnectorSource):
class RtlTcpDeviceDescription(ConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(super().getInputs(), [RemoteInput()])
return super().getInputs() + [RemoteInput()]
def getMandatoryKeys(self):
return super().getMandatoryKeys() + ["device"]

View File

@ -39,13 +39,13 @@ class ProtocolOptions(DropdownEnum):
class RundsDeviceDescription(ConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(
super().getInputs(),
[
RemoteInput(),
DropdownInput("protocol", "Protocol", ProtocolOptions),
CheckboxInput(
"long", "", "Use 32-bit sample size (LONG)", converter=OptionalConverter(defaultFormValue=False)
),
],
)
return super().getInputs() + [
RemoteInput(),
DropdownInput("protocol", "Protocol", ProtocolOptions),
CheckboxInput(
"long", "", "Use 32-bit sample size (LONG)", converter=OptionalConverter(defaultFormValue=False)
),
]
def getMandatoryKeys(self):
return super().getMandatoryKeys() + ["device"]

View File

@ -38,29 +38,29 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription):
return ["RFGR", "IFGR"]
def getInputs(self) -> List[Input]:
return self.mergeInputs(
super().getInputs(),
[
BiasTeeInput(),
CheckboxInput(
"rf_notch",
"",
checkboxText="Enable RF notch filter",
converter=OptionalConverter(defaultFormValue=True),
return super().getInputs() + [
BiasTeeInput(),
CheckboxInput(
"rf_notch",
"",
checkboxText="Enable RF notch filter",
converter=OptionalConverter(defaultFormValue=True),
),
CheckboxInput(
"dab_notch",
"",
checkboxText="Enable DAB notch filter",
converter=OptionalConverter(defaultFormValue=True),
),
DropdownInput(
"if_mode",
"IF Mode",
IfModeOptions,
converter=OptionalConverter(
EnumConverter(IfModeOptions), defaultFormValue=IfModeOptions.IFMODE_ZERO_IF.name
),
CheckboxInput(
"dab_notch",
"",
checkboxText="Enable DAB notch filter",
converter=OptionalConverter(defaultFormValue=True),
),
DropdownInput(
"if_mode",
"IF Mode",
IfModeOptions,
converter=OptionalConverter(
EnumConverter(IfModeOptions), defaultFormValue=IfModeOptions.IFMODE_ZERO_IF.name
),
),
],
)
),
]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]

View File

@ -84,27 +84,27 @@ class SoapyConnectorSource(ConnectorSource, metaclass=ABCMeta):
class SoapyConnectorDeviceDescription(ConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(
super().getInputs(),
[
TextInput(
"device",
"Device Identifier",
infotext='SoapySDR device identifier string (example: "serial=123456789")',
converter=OptionalConverter()
),
GainInput(
"rf_gain",
"Device Gain",
gain_stages=self.getGainStages(),
),
TextInput(
"antenna",
"Antenna",
converter=OptionalConverter(),
),
],
)
return super().getInputs() + [
TextInput(
"device",
"Device Identifier",
infotext='SoapySDR device identifier string (example: "serial=123456789")',
converter=OptionalConverter()
),
GainInput(
"rf_gain",
"Device Gain",
gain_stages=self.getGainStages(),
),
TextInput(
"antenna",
"Antenna",
converter=OptionalConverter(),
),
]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["device", "rf_gain", "antenna"]
def getGainStages(self):
return None

View File

@ -22,12 +22,12 @@ class SoapyRemoteSource(SoapyConnectorSource):
class SoapyRemoteDeviceDescription(SoapyConnectorDeviceDescription):
def getInputs(self) -> List[Input]:
return self.mergeInputs(
super().getInputs(),
[
RemoteInput(),
TextInput(
"remote_driver", "Remote driver", infotext="SoapySDR driver to be used on the remote SoapySDRServer"
),
],
)
return super().getInputs() + [
RemoteInput(),
TextInput(
"remote_driver", "Remote driver", infotext="SoapySDR driver to be used on the remote SoapySDRServer"
),
]
def getOptionalKeys(self):
return super().getOptionalKeys() + ["remote_driver"]