introduce the basic concept of optional keys
This commit is contained in:
		@@ -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 []
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user