diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 305deef8..a399c58d 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -3,7 +3,7 @@ from csdr.module.msk144 import Msk144Module, ParserAdapter from owrx.audio.chopper import AudioChopper, AudioChopperParser from owrx.aprs.kiss import KissDeframer from owrx.aprs import Ax25Parser, AprsParser -from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder +from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, RttyDecoder, BaudotDecoder from pycsdr.types import Format from owrx.aprs.module import DirewolfModule @@ -83,4 +83,32 @@ class PskDemodulator(SecondaryDemodulator, SecondarySelectorChain): return self.sampleRate = sampleRate secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3 - self.replace(1, TimingRecovery(Format.FLOAT, secondary_samples_per_bits, 0.5, 2)) + self.replace(1, TimingRecovery(Format.COMPLEX_FLOAT, secondary_samples_per_bits, 0.5, 2)) + + +class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): + def __init__(self, baudRate, bandWidth, invert=False): + self.baudRate = baudRate + self.bandWidth = bandWidth + self.invert = invert + # this is an assumption, we will adjust in setSampleRate + self.sampleRate = 12000 + secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) + workers = [ + Agc(Format.COMPLEX_FLOAT), + FmDemod(), + TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 0.5, 2), + RttyDecoder(invert), + BaudotDecoder(), + ] + super().__init__(workers) + + def getBandwidth(self) -> float: + return self.bandWidth + + def setSampleRate(self, sampleRate: int) -> None: + if sampleRate == self.sampleRate: + return + self.sampleRate = sampleRate + secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) + self.replace(2, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 0.5, 2)) diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index a3132b74..7938b5f5 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -32,13 +32,13 @@ popd rm -rf js8py git clone https://github.com/jketterl/csdr.git -# latest develop as of 2023-06-30 (csdr cleanup) -cmakebuild csdr 7e7a7f27b9fd1a65d4a9d0725583dfe6309a5245 +# latest develop as of 2023-08-15 (rtty and baudot) +cmakebuild csdr 8966688f748d04486560e973a599c26ed4297f34 git clone https://github.com/jketterl/pycsdr.git cd pycsdr -# latest develop as of 2023-06-30 (csdr cleanup) -git checkout be8b0e5e0b972ebb302e7397bac0058f620ec374 +# latest develop as of 2023-08-15 (rtty and baudot) +git checkout 4e30b6c6a4d73ab4cf99698847c4df68e8206f73 ./setup.py install install_headers cd .. rm -rf pycsdr diff --git a/owrx/dsp.py b/owrx/dsp.py index 749f0b94..f5e9395b 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -600,6 +600,12 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif mod == "bpsk63": from csdr.chain.digimodes import PskDemodulator return PskDemodulator(62.5) + elif mod == "rtty170": + from csdr.chain.digimodes import RttyDemodulator + return RttyDemodulator(45.45, 170) + elif mod == "rtty450": + from csdr.chain.digimodes import RttyDemodulator + return RttyDemodulator(50, 450, invert=True) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) diff --git a/owrx/modes.py b/owrx/modes.py index c27e85b8..131292b6 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -106,19 +106,21 @@ class Modes(object): AnalogMode("lsb", "LSB", bandpass=Bandpass(-3000, -300)), AnalogMode("usb", "USB", bandpass=Bandpass(300, 3000)), AnalogMode("cw", "CW", bandpass=Bandpass(700, 900)), - AnalogMode("dmr", "DMR", bandpass=Bandpass(-4000, 4000), requirements=["digital_voice_digiham"], squelch=False), + AnalogMode("dmr", "DMR", bandpass=Bandpass(-6250, 6250), requirements=["digital_voice_digiham"], squelch=False), AnalogMode( "dstar", "D-Star", bandpass=Bandpass(-3250, 3250), requirements=["digital_voice_digiham"], squelch=False ), AnalogMode("nxdn", "NXDN", bandpass=Bandpass(-3250, 3250), requirements=["digital_voice_digiham"], squelch=False), - AnalogMode("ysf", "YSF", bandpass=Bandpass(-4000, 4000), requirements=["digital_voice_digiham"], squelch=False), - AnalogMode("m17", "M17", bandpass=Bandpass(-4000, 4000), requirements=["digital_voice_m17"], squelch=False), + 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), AnalogMode( "freedv", "FreeDV", bandpass=Bandpass(300, 3000), requirements=["digital_voice_freedv"], squelch=False ), AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False), DigitalMode("bpsk31", "BPSK31", underlying=["usb"]), DigitalMode("bpsk63", "BPSK63", underlying=["usb"]), + DigitalMode("rtty170", "RTTY 45/170", underlying=["usb", "lsb"]), + DigitalMode("rtty450", "RTTY 50N/450", underlying=["lsb", "usb"]), WsjtMode("ft8", "FT8"), WsjtMode("ft4", "FT4"), WsjtMode("jt65", "JT65"),