openwebrx/owrx/modes.py

220 lines
7.4 KiB
Python
Raw Normal View History

from owrx.feature import FeatureDetector
from owrx.audio import ProfileSource
from functools import reduce
2021-04-09 18:16:25 +02:00
from abc import ABCMeta, abstractmethod
class Bandpass(object):
def __init__(self, low_cut, high_cut):
self.low_cut = low_cut
self.high_cut = high_cut
2021-08-31 21:53:15 +02:00
class Mode:
def __init__(self, modulation: str, name: str, bandpass: Bandpass = None, requirements=None, service=False, squelch=True):
self.modulation = modulation
self.name = name
self.requirements = requirements if requirements is not None else []
self.service = service
self.bandpass = bandpass
self.squelch = squelch
def is_available(self):
fd = FeatureDetector()
return reduce(lambda a, b: a and b, [fd.is_available(r) for r in self.requirements], True)
def is_service(self):
return self.service
def get_bandpass(self):
return self.bandpass
def get_modulation(self):
return self.modulation
2023-08-24 19:04:30 +02:00
EmptyMode = Mode("empty", "Empty")
class AnalogMode(Mode):
pass
class DigitalMode(Mode):
def __init__(
self,
modulation,
name,
underlying,
bandpass: Bandpass = None,
requirements=None,
service=False,
squelch=True,
secondaryFft=True
):
super().__init__(modulation, name, bandpass, requirements, service, squelch)
self.underlying = underlying
self.secondaryFft = secondaryFft
2021-08-31 21:53:15 +02:00
def get_underlying_mode(self):
2023-08-24 19:04:30 +02:00
mode = Modes.findByModulation(self.underlying[0])
if mode is None:
mode = EmptyMode
return mode
2021-08-31 21:53:15 +02:00
def get_bandpass(self):
if self.bandpass is not None:
return self.bandpass
2021-08-31 21:53:15 +02:00
return self.get_underlying_mode().get_bandpass()
def get_modulation(self):
2021-08-31 21:53:15 +02:00
return self.get_underlying_mode().get_modulation()
def for_underlying(self, underlying: str):
if underlying not in self.underlying:
raise ValueError("{} is not a valid underlying mode for {}".format(underlying, self.modulation))
return DigitalMode(
self.modulation, self.name, [underlying], self.bandpass, self.requirements, self.service, self.squelch
)
2021-04-09 18:16:25 +02:00
class AudioChopperMode(DigitalMode, metaclass=ABCMeta):
2021-02-15 22:14:56 +01:00
def __init__(self, modulation, name, bandpass=None, requirements=None):
if bandpass is None:
bandpass = Bandpass(0, 3000)
2021-04-09 18:16:25 +02:00
super().__init__(modulation, name, ["usb"], bandpass=bandpass, requirements=requirements, service=True)
@abstractmethod
def get_profile_source(self) -> ProfileSource:
2021-04-09 18:16:25 +02:00
pass
class WsjtMode(AudioChopperMode):
def __init__(self, modulation, name, bandpass=None, requirements=None):
2021-02-15 22:14:56 +01:00
if requirements is None:
requirements = ["wsjt-x"]
2021-04-09 18:16:25 +02:00
super().__init__(modulation, name, bandpass=bandpass, requirements=requirements)
def get_profile_source(self) -> ProfileSource:
2021-04-09 18:16:25 +02:00
# inline import due to circular dependencies
from owrx.wsjt import WsjtProfiles
return WsjtProfiles.getSource(self.modulation)
2021-04-09 18:16:25 +02:00
class Js8Mode(AudioChopperMode):
def __init__(self, modulation, name, bandpass=None, requirements=None):
if requirements is None:
requirements = ["js8call"]
super().__init__(modulation, name, bandpass, requirements)
def get_profile_source(self) -> ProfileSource:
2021-04-09 18:16:25 +02:00
# inline import due to circular dependencies
from owrx.js8 import Js8ProfileSource
return Js8ProfileSource()
2021-02-15 22:14:56 +01:00
class Modes(object):
mappings = [
AnalogMode("nfm", "FM", bandpass=Bandpass(-4000, 4000)),
AnalogMode("wfm", "WFM", bandpass=Bandpass(-75000, 75000)),
AnalogMode("am", "AM", bandpass=Bandpass(-4000, 4000)),
AnalogMode("lsb", "LSB", bandpass=Bandpass(-3000, -300)),
AnalogMode("usb", "USB", bandpass=Bandpass(300, 3000)),
AnalogMode("cw", "CW", bandpass=Bandpass(700, 900)),
2023-08-15 16:42:59 +02:00
AnalogMode("dmr", "DMR", bandpass=Bandpass(-6250, 6250), requirements=["digital_voice_digiham"], squelch=False),
2021-01-20 17:01:46 +01:00
AnalogMode(
"dstar", "D-Star", bandpass=Bandpass(-3250, 3250), requirements=["digital_voice_digiham"], squelch=False
2021-01-20 17:01:46 +01:00
),
2021-06-17 14:13:17 +02:00
AnalogMode("nxdn", "NXDN", bandpass=Bandpass(-3250, 3250), requirements=["digital_voice_digiham"], squelch=False),
2023-08-15 16:42:59 +02:00
AnalogMode("ysf", "YSF", bandpass=Bandpass(-6250, 6250), requirements=["digital_voice_digiham"], squelch=False),
AnalogMode("m17", "M17", bandpass=Bandpass(-6250, 6250), requirements=["digital_voice_m17"], squelch=False),
2021-01-20 17:01:46 +01:00
AnalogMode(
"freedv", "FreeDV", bandpass=Bandpass(300, 3000), requirements=["digital_voice_freedv"], squelch=False
),
2020-09-04 18:09:02 +02:00
AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False),
DigitalMode("bpsk31", "BPSK31", underlying=["usb"]),
DigitalMode("bpsk63", "BPSK63", underlying=["usb"]),
2023-08-15 16:42:59 +02:00
DigitalMode("rtty170", "RTTY 45/170", underlying=["usb", "lsb"]),
DigitalMode("rtty450", "RTTY 50N/450", underlying=["lsb", "usb"]),
2023-08-16 17:07:42 +02:00
DigitalMode("rtty85", "RTTY 50N/85", underlying=["lsb", "usb"]),
2021-02-15 22:14:56 +01:00
WsjtMode("ft8", "FT8"),
WsjtMode("ft4", "FT4"),
WsjtMode("jt65", "JT65"),
WsjtMode("jt9", "JT9"),
WsjtMode("wspr", "WSPR", bandpass=Bandpass(1350, 1650)),
WsjtMode("fst4", "FST4", requirements=["wsjt-x-2-3"]),
WsjtMode("fst4w", "FST4W", bandpass=Bandpass(1350, 1650), requirements=["wsjt-x-2-3"]),
WsjtMode("q65", "Q65", requirements=["wsjt-x-2-4"]),
2023-02-14 15:39:59 +01:00
DigitalMode("msk144", "MSK144", requirements=["msk144"], underlying=["usb"], service=True),
2021-04-09 18:16:25 +02:00
Js8Mode("js8", "JS8Call"),
DigitalMode(
"packet",
"Packet",
underlying=["nfm", "usb", "lsb"],
bandpass=Bandpass(-6250, 6250),
requirements=["packet"],
service=True,
squelch=False,
),
DigitalMode(
"pocsag",
"Pocsag",
underlying=["nfm"],
2023-08-22 02:04:30 +02:00
bandpass=Bandpass(-6250, 6250),
requirements=["pocsag"],
squelch=False,
),
DigitalMode(
"adsb",
"ADS-B",
2023-08-24 19:04:30 +02:00
underlying=["empty"],
bandpass=None,
requirements=["dump1090"],
2023-08-25 01:24:43 +02:00
service=True,
squelch=False,
secondaryFft=False,
),
DigitalMode(
"ism",
"ISM",
underlying=["empty"],
bandpass=None,
requirements=["ism"],
squelch=False,
2023-09-03 23:48:56 +02:00
),
DigitalMode(
"hfdl",
"HFDL",
underlying=["empty"],
bandpass=Bandpass(0, 3000),
requirements=["dumphfdl"],
squelch=False,
2023-09-04 19:02:43 +02:00
),
DigitalMode(
"vdl2",
"VDL2",
underlying=["empty"],
bandpass=Bandpass(-12500, 12500),
requirements=["dumpvdl2"],
squelch=False,
)
]
@staticmethod
def getModes():
return Modes.mappings
@staticmethod
def getAvailableModes():
return [m for m in Modes.getModes() if m.is_available()]
@staticmethod
def getAvailableServices():
return [m for m in Modes.getAvailableModes() if m.is_service()]
2020-04-26 22:46:30 +02:00
@staticmethod
def findByModulation(modulation):
modes = [m for m in Modes.getAvailableModes() if m.modulation == modulation]
if modes:
return modes[0]