diff --git a/owrx/form/input/validator.py b/owrx/form/input/validator.py index 35c99920..fc844d86 100644 --- a/owrx/form/input/validator.py +++ b/owrx/form/input/validator.py @@ -15,14 +15,16 @@ class RequiredValidator(Validator): class Range(object): - def __init__(self, start: int, end: int): + def __init__(self, start: int, end: int = None): self.start = start - self.end = end + self.end = end if end is not None else start def isInRange(self, value): return self.start <= value <= self.end def __str__(self): + if self.start == self.end: + return str(self.start) return "{start}...{end}".format(**vars(self)) @@ -46,7 +48,7 @@ class RangeListValidator(Validator): def validate(self, key, value) -> None: if not any(range for range in self.rangeList if range.isInRange(value)): 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): diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index 7da39e94..13841367 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -679,6 +679,6 @@ class SdrDeviceDescription(object): 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. return [Range(500000, 10000000)] diff --git a/owrx/source/afedri.py b/owrx/source/afedri.py index 79c9fc14..52cc7a0e 100644 --- a/owrx/source/afedri.py +++ b/owrx/source/afedri.py @@ -1,9 +1,8 @@ -import re from ipaddress import IPv4Address, AddressValueError from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.form.input import Input, CheckboxInput, DropdownInput, Option 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 @@ -123,3 +122,6 @@ class AfedriDeviceDescription(SoapyConnectorDeviceDescription): def getNumberOfChannels(self) -> int: return 4 + + def getSampleRateRanges(self) -> list[Range]: + return [Range(48000, 2400000)] diff --git a/owrx/source/airspy.py b/owrx/source/airspy.py index e851ab3f..52034446 100644 --- a/owrx/source/airspy.py +++ b/owrx/source/airspy.py @@ -1,6 +1,7 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.form.input import Input, CheckboxInput from owrx.form.input.device import BiasTeeInput +from owrx.form.input.validator import Range from typing import List @@ -48,3 +49,14 @@ class AirspyDeviceDescription(SoapyConnectorDeviceDescription): def getGainStages(self): 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), + ] diff --git a/owrx/source/airspyhf.py b/owrx/source/airspyhf.py index c7399a63..9e3c4317 100644 --- a/owrx/source/airspyhf.py +++ b/owrx/source/airspyhf.py @@ -1,4 +1,5 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription +from owrx.form.input.validator import Range class AirspyhfSource(SoapyConnectorSource): @@ -13,3 +14,13 @@ class AirspyhfDeviceDescription(SoapyConnectorDeviceDescription): def supportsPpm(self): # not currently supported by the SoapySDR module. return False + + def getSampleRateRanges(self) -> list[Range]: + return [ + Range(192000), + Range(256000), + Range(384000), + Range(456000), + Range(768000), + Range(912000), + ] diff --git a/owrx/source/bladerf.py b/owrx/source/bladerf.py index b08e4a2a..030817b6 100644 --- a/owrx/source/bladerf.py +++ b/owrx/source/bladerf.py @@ -1,4 +1,5 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription +from owrx.form.input.validator import Range class BladerfSource(SoapyConnectorSource): @@ -9,3 +10,6 @@ class BladerfSource(SoapyConnectorSource): class BladerfDeviceDescription(SoapyConnectorDeviceDescription): def getName(self): return "Blade RF" + + def getSampleRateRanges(self) -> list[Range]: + return [Range(160000, 40000000)] diff --git a/owrx/source/fcdpp.py b/owrx/source/fcdpp.py index 8f9b7af0..b12b9f7b 100644 --- a/owrx/source/fcdpp.py +++ b/owrx/source/fcdpp.py @@ -1,4 +1,5 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription +from owrx.form.input.validator import Range class FcdppSource(SoapyConnectorSource): @@ -9,3 +10,9 @@ class FcdppSource(SoapyConnectorSource): class FcdppDeviceDescription(SoapyConnectorDeviceDescription): def getName(self): return "FunCube Dongle Pro+" + + def getSampleRateRanges(self) -> list[Range]: + return [ + Range(96000), + Range(192000), + ] diff --git a/owrx/source/fifi_sdr.py b/owrx/source/fifi_sdr.py index 24dbc452..02ba25d3 100644 --- a/owrx/source/fifi_sdr.py +++ b/owrx/source/fifi_sdr.py @@ -7,6 +7,7 @@ from pycsdr.modules import Convert, Gain from pycsdr.types import Format from typing import List from owrx.form.input import Input, TextInput +from owrx.form.input.validator import Range import logging @@ -69,3 +70,10 @@ class FifiSdrDeviceDescription(DirectSourceDeviceDescription): def getDeviceOptionalKeys(self): return super().getDeviceOptionalKeys() + ["device"] + + def getSampleRateRanges(self) -> list[Range]: + return [ + Range(48000), + Range(96000), + Range(192000), + ] diff --git a/owrx/source/hackrf.py b/owrx/source/hackrf.py index 56ef27ba..9791e800 100644 --- a/owrx/source/hackrf.py +++ b/owrx/source/hackrf.py @@ -1,6 +1,7 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.form.input import Input from owrx.form.input.device import BiasTeeInput +from owrx.form.input.validator import Range from typing import List @@ -34,3 +35,6 @@ class HackrfDeviceDescription(SoapyConnectorDeviceDescription): def getGainStages(self): return ["LNA", "AMP", "VGA"] + + def getSampleRateRanges(self) -> list[Range]: + return [Range(1000000, 20000000)] diff --git a/owrx/source/hpsdr.py b/owrx/source/hpsdr.py index d7334a29..5c9b0468 100644 --- a/owrx/source/hpsdr.py +++ b/owrx/source/hpsdr.py @@ -1,8 +1,7 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription 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.validator import RangeValidator +from owrx.form.input.validator import RangeValidator, Range from typing import List # 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 2: (Remote IP: 192.168.1.22, Server port: 7301) + class HpsdrSource(ConnectorSource): def getCommandMapper(self): return ( @@ -46,6 +46,7 @@ class HpsdrSource(ConnectorSource): ) ) + class RemoteInput(TextInput): def __init__(self): super().__init__( @@ -57,6 +58,7 @@ class RemoteInput(TextInput): ) ) + class HpsdrDeviceDescription(ConnectorDeviceDescription): def getName(self): return "HPSDR devices (Hermes / Hermes Lite 2 / Red Pitaya)" @@ -88,3 +90,10 @@ class HpsdrDeviceDescription(ConnectorDeviceDescription): def getProfileOptionalKeys(self): return list(filter(lambda x : x != "iqswap", super().getProfileOptionalKeys())) + def getSampleRateRanges(self) -> list[Range]: + return [ + Range(48000), + Range(96000), + Range(192000), + Range(384000), + ] diff --git a/owrx/source/lime_sdr.py b/owrx/source/lime_sdr.py index f7e6ba43..069a3b66 100644 --- a/owrx/source/lime_sdr.py +++ b/owrx/source/lime_sdr.py @@ -1,4 +1,5 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription +from owrx.form.input.validator import Range class LimeSdrSource(SoapyConnectorSource): @@ -9,3 +10,6 @@ class LimeSdrSource(SoapyConnectorSource): class LimeSdrDeviceDescription(SoapyConnectorDeviceDescription): def getName(self): return "LimeSDR device" + + def getSampleRateRanges(self) -> list[Range]: + return [Range(100000, 65000000)] diff --git a/owrx/source/perseussdr.py b/owrx/source/perseussdr.py index 74ed1ca1..00eb22da 100644 --- a/owrx/source/perseussdr.py +++ b/owrx/source/perseussdr.py @@ -1,6 +1,7 @@ from owrx.source.direct import DirectSource, DirectSourceDeviceDescription from owrx.command import Option, Flag from owrx.form.input import Input, DropdownEnum, DropdownInput, CheckboxInput +from owrx.form.input.validator import Range from typing import List @@ -81,3 +82,12 @@ class PerseussdrDeviceDescription(DirectSourceDeviceDescription): "adc_dither", "wideband", ] + + def getSampleRateRanges(self) -> list[Range]: + return [ + Range(125000), + Range(250000), + Range(500000), + Range(1000000), + Range(2000000), + ] diff --git a/owrx/source/pluto_sdr.py b/owrx/source/pluto_sdr.py index 593b5c78..a73eb20a 100644 --- a/owrx/source/pluto_sdr.py +++ b/owrx/source/pluto_sdr.py @@ -1,4 +1,5 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription +from owrx.form.input.validator import Range class PlutoSdrSource(SoapyConnectorSource): @@ -9,3 +10,6 @@ class PlutoSdrSource(SoapyConnectorSource): class PlutoSdrDeviceDescription(SoapyConnectorDeviceDescription): def getName(self): return "PlutoSDR" + + def getSampleRateRanges(self) -> list[Range]: + return [Range(520833, 61440000)] diff --git a/owrx/source/radioberry.py b/owrx/source/radioberry.py index 932a5e4a..0fa9b08d 100644 --- a/owrx/source/radioberry.py +++ b/owrx/source/radioberry.py @@ -1,4 +1,5 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription +from owrx.form.input.validator import Range class RadioberrySource(SoapyConnectorSource): @@ -9,3 +10,11 @@ class RadioberrySource(SoapyConnectorSource): class RadioberryDeviceDescription(SoapyConnectorDeviceDescription): def getName(self): return "RadioBerry" + + def getSampleRateRanges(self) -> list[Range]: + return [ + Range(48000), + Range(96000), + Range(192000), + Range(384000), + ] diff --git a/owrx/source/rtl_sdr.py b/owrx/source/rtl_sdr.py index 1c23e957..c1f2c68e 100644 --- a/owrx/source/rtl_sdr.py +++ b/owrx/source/rtl_sdr.py @@ -37,5 +37,5 @@ class RtlSdrDeviceDescription(ConnectorDeviceDescription): def getProfileOptionalKeys(self): return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"] - def getSampleRateRanges(self) -> List[Range]: + def getSampleRateRanges(self) -> list[Range]: return [Range(250000, 3200000)] diff --git a/owrx/source/rtl_sdr_soapy.py b/owrx/source/rtl_sdr_soapy.py index a308c7d4..ffea9c6f 100644 --- a/owrx/source/rtl_sdr_soapy.py +++ b/owrx/source/rtl_sdr_soapy.py @@ -1,6 +1,7 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.form.input import Input from owrx.form.input.device import BiasTeeInput, DirectSamplingInput +from owrx.form.input.validator import Range from typing import List @@ -26,3 +27,6 @@ class RtlSdrSoapyDeviceDescription(SoapyConnectorDeviceDescription): def getProfileOptionalKeys(self): return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"] + + def getSampleRateRanges(self) -> list[Range]: + return [Range(250000, 3200000)] diff --git a/owrx/source/rtl_tcp.py b/owrx/source/rtl_tcp.py index 6e1a3c40..5f895574 100644 --- a/owrx/source/rtl_tcp.py +++ b/owrx/source/rtl_tcp.py @@ -2,6 +2,7 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription from owrx.command import Flag, Option, Argument from owrx.form.input import Input from owrx.form.input.device import RemoteInput, DirectSamplingInput +from owrx.form.input.validator import Range from typing import List @@ -36,3 +37,6 @@ class RtlTcpDeviceDescription(ConnectorDeviceDescription): def getProfileOptionalKeys(self): return super().getProfileOptionalKeys() + ["direct_sampling"] + + def getSampleRateRanges(self) -> list[Range]: + return [Range(250000, 3200000)] diff --git a/owrx/source/runds.py b/owrx/source/runds.py index 0f4a4679..7c94a9b9 100644 --- a/owrx/source/runds.py +++ b/owrx/source/runds.py @@ -2,6 +2,7 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription from owrx.command import Argument, Flag, Option from owrx.form.input import Input, DropdownInput, DropdownEnum, CheckboxInput from owrx.form.input.device import RemoteInput +from owrx.form.input.validator import Range from typing import List @@ -56,3 +57,7 @@ class RundsDeviceDescription(ConnectorDeviceDescription): def getDeviceOptionalKeys(self): 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)] diff --git a/owrx/source/sddc.py b/owrx/source/sddc.py index 5c3255f0..23dfba77 100644 --- a/owrx/source/sddc.py +++ b/owrx/source/sddc.py @@ -1,4 +1,5 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription +from owrx.form.input.validator import Range class SddcSource(ConnectorSource): @@ -12,3 +13,7 @@ class SddcDeviceDescription(ConnectorDeviceDescription): def hasAgc(self): 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)] diff --git a/owrx/source/sdrplay.py b/owrx/source/sdrplay.py index 454e4722..ecd7bcf7 100644 --- a/owrx/source/sdrplay.py +++ b/owrx/source/sdrplay.py @@ -1,6 +1,7 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription from owrx.form.input import Input, CheckboxInput, DropdownInput, DropdownEnum from owrx.form.input.device import BiasTeeInput +from owrx.form.input.validator import Range from typing import List @@ -62,3 +63,29 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription): def getProfileOptionalKeys(self): 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), + ] diff --git a/owrx/source/uhd.py b/owrx/source/uhd.py index 56f66b65..ef4dd9b6 100644 --- a/owrx/source/uhd.py +++ b/owrx/source/uhd.py @@ -1,4 +1,5 @@ from owrx.source.soapy import SoapyConnectorSource, SoapyConnectorDeviceDescription +from owrx.form.input.validator import Range class UhdSource(SoapyConnectorSource): @@ -9,3 +10,7 @@ class UhdSource(SoapyConnectorSource): class UhdDeviceDescription(SoapyConnectorDeviceDescription): def getName(self): return "Ettus Research USRP device" + + def getSampleRateRanges(self) -> list[Range]: + # not sure since this depends of the specific model + return [Range(0, 64000000)]