From 21040f1ad0d52c290378e43dfc3161f6c503b0a9 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Sun, 29 Jan 2023 21:27:51 -0500 Subject: [PATCH 1/9] Adding RTTY decoder. --- csdr/chain/digimodes.py | 26 +++++++++++++++++++++++++- owrx/dsp.py | 3 +++ owrx/modes.py | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 98937ae0..44e0432a 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -2,7 +2,7 @@ from csdr.chain.demodulator import ServiceDemodulator, SecondaryDemodulator, Dia 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, CwDecoder, RealPart +from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, CwDecoder, RttyDecoder, RealPart from pycsdr.types import Format from owrx.aprs.module import DirewolfModule from digiham.modules import FskDemodulator, PocsagDecoder @@ -111,3 +111,27 @@ class CwDemodulator(SecondaryDemodulator, SecondarySelectorChain): self.sampleRate = sampleRate self.replace(1, CwDecoder(sampleRate, 0, int(self.baudRate))) + +class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): + def __init__(self, baudRate: float): + # Our input "baud rate" is actually frequency shift here + # Real RTTY baud rate is different + self.sampleRate = 12000 + self.targetWidth = baudRate + self.baudRate = 45.45 + workers = [ + RealPart(), + Agc(Format.FLOAT), + RttyDecoder(self.sampleRate, 0, int(self.targetWidth), self.baudRate), + ] + super().__init__(workers) + + def getBandwidth(self): + return self.targetWidth + + def setSampleRate(self, sampleRate: int) -> None: + if sampleRate == self.sampleRate: + return + self.sampleRate = sampleRate + self.replace(1, RttyDecoder(sampleRate, 0, int(self.targetWidth), self.baudRate)) + diff --git a/owrx/dsp.py b/owrx/dsp.py index bdb8cfa5..d54d8ca4 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -616,6 +616,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif mod == "cwdecoder": from csdr.chain.digimodes import CwDemodulator return CwDemodulator(75.0) + elif mod == "rtty": + from csdr.chain.digimodes import RttyDemodulator + return RttyDemodulator(170.0) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) diff --git a/owrx/modes.py b/owrx/modes.py index c35a556f..38c08f0f 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -139,6 +139,7 @@ class Modes(object): squelch=False, ), DigitalMode("cwdecoder", "CWDecoder", underlying=["usb"]), + DigitalMode("rtty", "RTTY", underlying=["usb"]), ] @staticmethod From dc5208537578c514c8c45152a8127dcec4f3eb84 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Sun, 29 Jan 2023 23:14:47 -0500 Subject: [PATCH 2/9] Adding separate 170Hz (HAM) and 450Hz (DDK) RTTY modes. --- csdr/chain/digimodes.py | 8 +++----- owrx/dsp.py | 7 +++++-- owrx/modes.py | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 44e0432a..15ce13e7 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -113,12 +113,10 @@ class CwDemodulator(SecondaryDemodulator, SecondarySelectorChain): class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): - def __init__(self, baudRate: float): - # Our input "baud rate" is actually frequency shift here - # Real RTTY baud rate is different + def __init__(self, targetWidth: float, baudRate: float): self.sampleRate = 12000 - self.targetWidth = baudRate - self.baudRate = 45.45 + self.targetWidth = targetWidth + self.baudRate = baudRate workers = [ RealPart(), Agc(Format.FLOAT), diff --git a/owrx/dsp.py b/owrx/dsp.py index d54d8ca4..e30e9737 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -616,9 +616,12 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif mod == "cwdecoder": from csdr.chain.digimodes import CwDemodulator return CwDemodulator(75.0) - elif mod == "rtty": + elif mod == "rtty170": from csdr.chain.digimodes import RttyDemodulator - return RttyDemodulator(170.0) + return RttyDemodulator(170.0, 45.45) + elif mod == "rtty450": + from csdr.chain.digimodes import RttyDemodulator + return RttyDemodulator(450.0, 50.0) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) diff --git a/owrx/modes.py b/owrx/modes.py index 38c08f0f..6e1ae8e1 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -139,7 +139,8 @@ class Modes(object): squelch=False, ), DigitalMode("cwdecoder", "CWDecoder", underlying=["usb"]), - DigitalMode("rtty", "RTTY", underlying=["usb"]), + DigitalMode("rtty170", "RTTY170", underlying=["usb"]), + DigitalMode("rtty450", "RTTY450", underlying=["usb"]), ] @staticmethod From 9ab69952b7c958a366bda505f0d07237f4624d00 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Mon, 30 Jan 2023 00:21:53 -0500 Subject: [PATCH 3/9] Adding +-50Hz at the sides of the RTTY signal. --- csdr/chain/digimodes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 15ce13e7..99387754 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -120,16 +120,16 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): workers = [ RealPart(), Agc(Format.FLOAT), - RttyDecoder(self.sampleRate, 0, int(self.targetWidth), self.baudRate), + RttyDecoder(self.sampleRate, 50, int(self.targetWidth), self.baudRate), ] super().__init__(workers) def getBandwidth(self): - return self.targetWidth + return self.targetWidth + 100.0 def setSampleRate(self, sampleRate: int) -> None: if sampleRate == self.sampleRate: return self.sampleRate = sampleRate - self.replace(1, RttyDecoder(sampleRate, 0, int(self.targetWidth), self.baudRate)) + self.replace(1, RttyDecoder(sampleRate, 50, int(self.targetWidth), self.baudRate)) From 15573e210e122029e7e02deb0628d1c22fd0a950 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Mon, 30 Jan 2023 22:16:57 -0500 Subject: [PATCH 4/9] Adding reverse parameter to RTTY modes. --- csdr/chain/digimodes.py | 7 ++++--- owrx/dsp.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 99387754..47e103d2 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -113,14 +113,15 @@ class CwDemodulator(SecondaryDemodulator, SecondarySelectorChain): class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): - def __init__(self, targetWidth: float, baudRate: float): + def __init__(self, targetWidth: float, baudRate: float, reverse: boolean): self.sampleRate = 12000 self.targetWidth = targetWidth self.baudRate = baudRate + self.reverse = reverse workers = [ RealPart(), Agc(Format.FLOAT), - RttyDecoder(self.sampleRate, 50, int(self.targetWidth), self.baudRate), + RttyDecoder(self.sampleRate, 50, int(self.targetWidth), self.baudRate, self.reverse), ] super().__init__(workers) @@ -131,5 +132,5 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): if sampleRate == self.sampleRate: return self.sampleRate = sampleRate - self.replace(1, RttyDecoder(sampleRate, 50, int(self.targetWidth), self.baudRate)) + self.replace(1, RttyDecoder(sampleRate, 50, int(self.targetWidth), self.baudRate, self.reverse)) diff --git a/owrx/dsp.py b/owrx/dsp.py index e30e9737..391a5823 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -618,10 +618,10 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) return CwDemodulator(75.0) elif mod == "rtty170": from csdr.chain.digimodes import RttyDemodulator - return RttyDemodulator(170.0, 45.45) + return RttyDemodulator(170.0, 45.45, false) elif mod == "rtty450": from csdr.chain.digimodes import RttyDemodulator - return RttyDemodulator(450.0, 50.0) + return RttyDemodulator(450.0, 50.0, true) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) From ef504489bda83b220c386cca9776d8e32feb9f4c Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Mon, 30 Jan 2023 22:35:30 -0500 Subject: [PATCH 5/9] Fixing boolean type. --- csdr/chain/digimodes.py | 2 +- owrx/modes.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 47e103d2..1bb80c04 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -113,7 +113,7 @@ class CwDemodulator(SecondaryDemodulator, SecondarySelectorChain): class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): - def __init__(self, targetWidth: float, baudRate: float, reverse: boolean): + def __init__(self, targetWidth: float, baudRate: float, reverse: bool): self.sampleRate = 12000 self.targetWidth = targetWidth self.baudRate = baudRate diff --git a/owrx/modes.py b/owrx/modes.py index 6e1ae8e1..e7de647b 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -139,8 +139,8 @@ class Modes(object): squelch=False, ), DigitalMode("cwdecoder", "CWDecoder", underlying=["usb"]), - DigitalMode("rtty170", "RTTY170", underlying=["usb"]), - DigitalMode("rtty450", "RTTY450", underlying=["usb"]), + DigitalMode("rtty170", "RTTY-170", underlying=["usb"]), + DigitalMode("rtty450", "RTTY-450", underlying=["usb"]), ] @staticmethod From 0a6ea91fd6d83ecd43de1769067ce667e63b752a Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Mon, 30 Jan 2023 22:38:35 -0500 Subject: [PATCH 6/9] Fixing booleans. --- owrx/dsp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/owrx/dsp.py b/owrx/dsp.py index 391a5823..17807482 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -618,10 +618,10 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) return CwDemodulator(75.0) elif mod == "rtty170": from csdr.chain.digimodes import RttyDemodulator - return RttyDemodulator(170.0, 45.45, false) + return RttyDemodulator(170.0, 45.45, False) elif mod == "rtty450": from csdr.chain.digimodes import RttyDemodulator - return RttyDemodulator(450.0, 50.0, true) + return RttyDemodulator(450.0, 50.0, True) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) From 3ee57764a88d01434e2331a7bf8c6be3372695cd Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Mon, 30 Jan 2023 22:49:29 -0500 Subject: [PATCH 7/9] Cleanup --- owrx/dsp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/owrx/dsp.py b/owrx/dsp.py index 17807482..185680a5 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -618,10 +618,10 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) return CwDemodulator(75.0) elif mod == "rtty170": from csdr.chain.digimodes import RttyDemodulator - return RttyDemodulator(170.0, 45.45, False) + return RttyDemodulator(170.0, 45.45, reverse = False) elif mod == "rtty450": from csdr.chain.digimodes import RttyDemodulator - return RttyDemodulator(450.0, 50.0, True) + return RttyDemodulator(450.0, 50.0, reverse = True) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) From 944cdef12a16d0dc6c3ef2878ff2a1595ef654e4 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Tue, 31 Jan 2023 12:21:46 -0500 Subject: [PATCH 8/9] Fixing digital modes chain. --- csdr/chain/digimodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 1bb80c04..86d60c65 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -109,7 +109,7 @@ class CwDemodulator(SecondaryDemodulator, SecondarySelectorChain): if sampleRate == self.sampleRate: return self.sampleRate = sampleRate - self.replace(1, CwDecoder(sampleRate, 0, int(self.baudRate))) + self.replace(2, CwDecoder(sampleRate, 0, int(self.baudRate))) class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): @@ -132,5 +132,5 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): if sampleRate == self.sampleRate: return self.sampleRate = sampleRate - self.replace(1, RttyDecoder(sampleRate, 50, int(self.targetWidth), self.baudRate, self.reverse)) + self.replace(2, RttyDecoder(sampleRate, 50, int(self.targetWidth), self.baudRate, self.reverse)) From 770aefb88836cf81fe4cb68c7510ded20086cb59 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Thu, 2 Feb 2023 21:04:23 -0500 Subject: [PATCH 9/9] Switching RTTY decoder to processing complex input. --- csdr/chain/digimodes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 86d60c65..62433985 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -2,7 +2,7 @@ from csdr.chain.demodulator import ServiceDemodulator, SecondaryDemodulator, Dia 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, CwDecoder, RttyDecoder, RealPart +from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, CwDecoder, RttyDecoder, RealPart, Shift from pycsdr.types import Format from owrx.aprs.module import DirewolfModule from digiham.modules import FskDemodulator, PocsagDecoder @@ -119,8 +119,8 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): self.baudRate = baudRate self.reverse = reverse workers = [ - RealPart(), - Agc(Format.FLOAT), + Shift(self.getBandwidth() / 2.0 / self.sampleRate), + Agc(Format.COMPLEX_FLOAT), RttyDecoder(self.sampleRate, 50, int(self.targetWidth), self.baudRate, self.reverse), ] super().__init__(workers) @@ -132,5 +132,5 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): if sampleRate == self.sampleRate: return self.sampleRate = sampleRate + self.replace(0, Shift(self.getBandwidth() / 2.0 / sampleRate)) self.replace(2, RttyDecoder(sampleRate, 50, int(self.targetWidth), self.baudRate, self.reverse)) -