add more sample rate validation

This commit is contained in:
Jakob Ketterl 2024-01-17 22:39:05 +01:00
parent 374bbb599e
commit 402eadd280
21 changed files with 145 additions and 9 deletions

View File

@ -15,14 +15,16 @@ class RequiredValidator(Validator):
class Range(object): class Range(object):
def __init__(self, start: int, end: int): def __init__(self, start: int, end: int = None):
self.start = start self.start = start
self.end = end self.end = end if end is not None else start
def isInRange(self, value): def isInRange(self, value):
return self.start <= value <= self.end return self.start <= value <= self.end
def __str__(self): def __str__(self):
if self.start == self.end:
return str(self.start)
return "{start}...{end}".format(**vars(self)) return "{start}...{end}".format(**vars(self))
@ -46,7 +48,7 @@ class RangeListValidator(Validator):
def validate(self, key, value) -> None: def validate(self, key, value) -> None:
if not any(range for range in self.rangeList if range.isInRange(value)): if not any(range for range in self.rangeList if range.isInRange(value)):
raise ValidationError( raise ValidationError(
key, "Value is out of range {}".format(self._rangeStr()) key, "Value is outside of the allowed range(s) {}".format(self._rangeStr())
) )
def _rangeStr(self): def _rangeStr(self):

View File

@ -679,6 +679,6 @@ class SdrDeviceDescription(object):
self.getProfileOptionalKeys(), self.getProfileOptionalKeys(),
) )
def getSampleRateRanges(self) -> List[Range]: def getSampleRateRanges(self) -> list[Range]:
# semi-sane default value. should be overridden with more specific values per device. # semi-sane default value. should be overridden with more specific values per device.
return [Range(500000, 10000000)] return [Range(500000, 10000000)]

View File

@ -1,9 +1,8 @@
import re
from ipaddress import IPv4Address, AddressValueError from ipaddress import IPv4Address, AddressValueError
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input import Input, CheckboxInput, DropdownInput, Option from owrx.form.input import Input, CheckboxInput, DropdownInput, Option
from owrx.form.input.device import TextInput from owrx.form.input.device import TextInput
from owrx.form.input.validator import Validator, ValidationError from owrx.form.input.validator import Validator, ValidationError, Range
from typing import List from typing import List
@ -123,3 +122,6 @@ class AfedriDeviceDescription(SoapyConnectorDeviceDescription):
def getNumberOfChannels(self) -> int: def getNumberOfChannels(self) -> int:
return 4 return 4
def getSampleRateRanges(self) -> list[Range]:
return [Range(48000, 2400000)]

View File

@ -1,6 +1,7 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input import Input, CheckboxInput from owrx.form.input import Input, CheckboxInput
from owrx.form.input.device import BiasTeeInput from owrx.form.input.device import BiasTeeInput
from owrx.form.input.validator import Range
from typing import List from typing import List
@ -48,3 +49,14 @@ class AirspyDeviceDescription(SoapyConnectorDeviceDescription):
def getGainStages(self): def getGainStages(self):
return ["LNA", "MIX", "VGA"] return ["LNA", "MIX", "VGA"]
def getSampleRateRanges(self) -> list[Range]:
# Airspy R2 does 2.5 or 10 MS/s
# Airspy mini does 3 or 6 MS/s
# we don't know what device we're actually dealing with, but we can still clamp it down to a sum of the options.
return [
Range(2500000),
Range(3000000),
Range(6000000),
Range(10000000),
]

View File

@ -1,4 +1,5 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input.validator import Range
class AirspyhfSource(SoapyConnectorSource): class AirspyhfSource(SoapyConnectorSource):
@ -13,3 +14,13 @@ class AirspyhfDeviceDescription(SoapyConnectorDeviceDescription):
def supportsPpm(self): def supportsPpm(self):
# not currently supported by the SoapySDR module. # not currently supported by the SoapySDR module.
return False return False
def getSampleRateRanges(self) -> list[Range]:
return [
Range(192000),
Range(256000),
Range(384000),
Range(456000),
Range(768000),
Range(912000),
]

View File

@ -1,4 +1,5 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input.validator import Range
class BladerfSource(SoapyConnectorSource): class BladerfSource(SoapyConnectorSource):
@ -9,3 +10,6 @@ class BladerfSource(SoapyConnectorSource):
class BladerfDeviceDescription(SoapyConnectorDeviceDescription): class BladerfDeviceDescription(SoapyConnectorDeviceDescription):
def getName(self): def getName(self):
return "Blade RF" return "Blade RF"
def getSampleRateRanges(self) -> list[Range]:
return [Range(160000, 40000000)]

View File

@ -1,4 +1,5 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input.validator import Range
class FcdppSource(SoapyConnectorSource): class FcdppSource(SoapyConnectorSource):
@ -9,3 +10,9 @@ class FcdppSource(SoapyConnectorSource):
class FcdppDeviceDescription(SoapyConnectorDeviceDescription): class FcdppDeviceDescription(SoapyConnectorDeviceDescription):
def getName(self): def getName(self):
return "FunCube Dongle Pro+" return "FunCube Dongle Pro+"
def getSampleRateRanges(self) -> list[Range]:
return [
Range(96000),
Range(192000),
]

View File

@ -7,6 +7,7 @@ from pycsdr.modules import Convert, Gain
from pycsdr.types import Format from pycsdr.types import Format
from typing import List from typing import List
from owrx.form.input import Input, TextInput from owrx.form.input import Input, TextInput
from owrx.form.input.validator import Range
import logging import logging
@ -69,3 +70,10 @@ class FifiSdrDeviceDescription(DirectSourceDeviceDescription):
def getDeviceOptionalKeys(self): def getDeviceOptionalKeys(self):
return super().getDeviceOptionalKeys() + ["device"] return super().getDeviceOptionalKeys() + ["device"]
def getSampleRateRanges(self) -> list[Range]:
return [
Range(48000),
Range(96000),
Range(192000),
]

View File

@ -1,6 +1,7 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input import Input from owrx.form.input import Input
from owrx.form.input.device import BiasTeeInput from owrx.form.input.device import BiasTeeInput
from owrx.form.input.validator import Range
from typing import List from typing import List
@ -34,3 +35,6 @@ class HackrfDeviceDescription(SoapyConnectorDeviceDescription):
def getGainStages(self): def getGainStages(self):
return ["LNA", "AMP", "VGA"] return ["LNA", "AMP", "VGA"]
def getSampleRateRanges(self) -> list[Range]:
return [Range(1000000, 20000000)]

View File

@ -1,8 +1,7 @@
from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription
from owrx.command import Option, Flag from owrx.command import Option, Flag
from owrx.form.error import ValidationError
from owrx.form.input import Input, NumberInput, TextInput, CheckboxInput from owrx.form.input import Input, NumberInput, TextInput, CheckboxInput
from owrx.form.input.validator import RangeValidator from owrx.form.input.validator import RangeValidator, Range
from typing import List from typing import List
# These are the command line options available: # These are the command line options available:
@ -28,6 +27,7 @@ from typing import List
# Radio 1: (Remote IP: 192.168.1.11, Server port: 7300) # Radio 1: (Remote IP: 192.168.1.11, Server port: 7300)
# Radio 2: (Remote IP: 192.168.1.22, Server port: 7301) # Radio 2: (Remote IP: 192.168.1.22, Server port: 7301)
class HpsdrSource(ConnectorSource): class HpsdrSource(ConnectorSource):
def getCommandMapper(self): def getCommandMapper(self):
return ( return (
@ -46,6 +46,7 @@ class HpsdrSource(ConnectorSource):
) )
) )
class RemoteInput(TextInput): class RemoteInput(TextInput):
def __init__(self): def __init__(self):
super().__init__( super().__init__(
@ -57,6 +58,7 @@ class RemoteInput(TextInput):
) )
) )
class HpsdrDeviceDescription(ConnectorDeviceDescription): class HpsdrDeviceDescription(ConnectorDeviceDescription):
def getName(self): def getName(self):
return "HPSDR devices (Hermes / Hermes Lite 2 / Red Pitaya)" return "HPSDR devices (Hermes / Hermes Lite 2 / Red Pitaya)"
@ -88,3 +90,10 @@ class HpsdrDeviceDescription(ConnectorDeviceDescription):
def getProfileOptionalKeys(self): def getProfileOptionalKeys(self):
return list(filter(lambda x : x != "iqswap", super().getProfileOptionalKeys())) return list(filter(lambda x : x != "iqswap", super().getProfileOptionalKeys()))
def getSampleRateRanges(self) -> list[Range]:
return [
Range(48000),
Range(96000),
Range(192000),
Range(384000),
]

View File

@ -1,4 +1,5 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input.validator import Range
class LimeSdrSource(SoapyConnectorSource): class LimeSdrSource(SoapyConnectorSource):
@ -9,3 +10,6 @@ class LimeSdrSource(SoapyConnectorSource):
class LimeSdrDeviceDescription(SoapyConnectorDeviceDescription): class LimeSdrDeviceDescription(SoapyConnectorDeviceDescription):
def getName(self): def getName(self):
return "LimeSDR device" return "LimeSDR device"
def getSampleRateRanges(self) -> list[Range]:
return [Range(100000, 65000000)]

View File

@ -1,6 +1,7 @@
from owrx.source.direct import DirectSource, DirectSourceDeviceDescription from owrx.source.direct import DirectSource, DirectSourceDeviceDescription
from owrx.command import Option, Flag from owrx.command import Option, Flag
from owrx.form.input import Input, DropdownEnum, DropdownInput, CheckboxInput from owrx.form.input import Input, DropdownEnum, DropdownInput, CheckboxInput
from owrx.form.input.validator import Range
from typing import List from typing import List
@ -81,3 +82,12 @@ class PerseussdrDeviceDescription(DirectSourceDeviceDescription):
"adc_dither", "adc_dither",
"wideband", "wideband",
] ]
def getSampleRateRanges(self) -> list[Range]:
return [
Range(125000),
Range(250000),
Range(500000),
Range(1000000),
Range(2000000),
]

View File

@ -1,4 +1,5 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input.validator import Range
class PlutoSdrSource(SoapyConnectorSource): class PlutoSdrSource(SoapyConnectorSource):
@ -9,3 +10,6 @@ class PlutoSdrSource(SoapyConnectorSource):
class PlutoSdrDeviceDescription(SoapyConnectorDeviceDescription): class PlutoSdrDeviceDescription(SoapyConnectorDeviceDescription):
def getName(self): def getName(self):
return "PlutoSDR" return "PlutoSDR"
def getSampleRateRanges(self) -> list[Range]:
return [Range(520833, 61440000)]

View File

@ -1,4 +1,5 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input.validator import Range
class RadioberrySource(SoapyConnectorSource): class RadioberrySource(SoapyConnectorSource):
@ -9,3 +10,11 @@ class RadioberrySource(SoapyConnectorSource):
class RadioberryDeviceDescription(SoapyConnectorDeviceDescription): class RadioberryDeviceDescription(SoapyConnectorDeviceDescription):
def getName(self): def getName(self):
return "RadioBerry" return "RadioBerry"
def getSampleRateRanges(self) -> list[Range]:
return [
Range(48000),
Range(96000),
Range(192000),
Range(384000),
]

View File

@ -37,5 +37,5 @@ class RtlSdrDeviceDescription(ConnectorDeviceDescription):
def getProfileOptionalKeys(self): def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"] return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"]
def getSampleRateRanges(self) -> List[Range]: def getSampleRateRanges(self) -> list[Range]:
return [Range(250000, 3200000)] return [Range(250000, 3200000)]

View File

@ -1,6 +1,7 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input import Input from owrx.form.input import Input
from owrx.form.input.device import BiasTeeInput, DirectSamplingInput from owrx.form.input.device import BiasTeeInput, DirectSamplingInput
from owrx.form.input.validator import Range
from typing import List from typing import List
@ -26,3 +27,6 @@ class RtlSdrSoapyDeviceDescription(SoapyConnectorDeviceDescription):
def getProfileOptionalKeys(self): def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"] return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"]
def getSampleRateRanges(self) -> list[Range]:
return [Range(250000, 3200000)]

View File

@ -2,6 +2,7 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription
from owrx.command import Flag, Option, Argument from owrx.command import Flag, Option, Argument
from owrx.form.input import Input from owrx.form.input import Input
from owrx.form.input.device import RemoteInput, DirectSamplingInput from owrx.form.input.device import RemoteInput, DirectSamplingInput
from owrx.form.input.validator import Range
from typing import List from typing import List
@ -36,3 +37,6 @@ class RtlTcpDeviceDescription(ConnectorDeviceDescription):
def getProfileOptionalKeys(self): def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["direct_sampling"] return super().getProfileOptionalKeys() + ["direct_sampling"]
def getSampleRateRanges(self) -> list[Range]:
return [Range(250000, 3200000)]

View File

@ -2,6 +2,7 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription
from owrx.command import Argument, Flag, Option from owrx.command import Argument, Flag, Option
from owrx.form.input import Input, DropdownInput, DropdownEnum, CheckboxInput from owrx.form.input import Input, DropdownInput, DropdownEnum, CheckboxInput
from owrx.form.input.device import RemoteInput from owrx.form.input.device import RemoteInput
from owrx.form.input.validator import Range
from typing import List from typing import List
@ -56,3 +57,7 @@ class RundsDeviceDescription(ConnectorDeviceDescription):
def getDeviceOptionalKeys(self): def getDeviceOptionalKeys(self):
return super().getDeviceOptionalKeys() + ["protocol", "long"] return super().getDeviceOptionalKeys() + ["protocol", "long"]
def getSampleRateRanges(self) -> list[Range]:
# can't be very specific here due to the wide range of devices, so this is more of a sanity check.
return [Range(0, 20000000)]

View File

@ -1,4 +1,5 @@
from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription
from owrx.form.input.validator import Range
class SddcSource(ConnectorSource): class SddcSource(ConnectorSource):
@ -12,3 +13,7 @@ class SddcDeviceDescription(ConnectorDeviceDescription):
def hasAgc(self): def hasAgc(self):
return False return False
def getSampleRateRanges(self) -> list[Range]:
# resampling is done in software... it can't cover the full range, but it's not finished either.
return [Range(0, 64000000)]

View File

@ -1,6 +1,7 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input import Input, CheckboxInput, DropdownInput, DropdownEnum from owrx.form.input import Input, CheckboxInput, DropdownInput, DropdownEnum
from owrx.form.input.device import BiasTeeInput from owrx.form.input.device import BiasTeeInput
from owrx.form.input.validator import Range
from typing import List from typing import List
@ -62,3 +63,29 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription):
def getProfileOptionalKeys(self): def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"] return super().getProfileOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]
def getSampleRateRanges(self) -> list[Range]:
# this is from SoapySDRPlay3's implementation of listSampleRates().
# i don't think it's accurate, but this is the limitation we'd be running into if we had proper soapy
# integration.
return [
Range(62500),
Range(96000),
Range(125000),
Range(192000),
Range(250000),
Range(384000),
Range(500000),
Range(768000),
Range(1000000),
Range(2000000),
Range(2048000),
Range(3000000),
Range(4000000),
Range(5000000),
Range(6000000),
Range(7000000),
Range(8000000),
Range(9000000),
Range(10000000),
]

View File

@ -1,4 +1,5 @@
from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription
from owrx.form.input.validator import Range
class UhdSource(SoapyConnectorSource): class UhdSource(SoapyConnectorSource):
@ -9,3 +10,7 @@ class UhdSource(SoapyConnectorSource):
class UhdDeviceDescription(SoapyConnectorDeviceDescription): class UhdDeviceDescription(SoapyConnectorDeviceDescription):
def getName(self): def getName(self):
return "Ettus Research USRP device" return "Ettus Research USRP device"
def getSampleRateRanges(self) -> list[Range]:
# not sure since this depends of the specific model
return [Range(0, 64000000)]