diff --git a/csdr/chain/m17.py b/csdr/chain/m17.py new file mode 100644 index 0000000..4c21d47 --- /dev/null +++ b/csdr/chain/m17.py @@ -0,0 +1,26 @@ +from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain +from owrx.m17 import M17Module +from pycsdr.modules import FmDemod, Limit, Convert +from pycsdr.types import Format +from digiham.modules import DcBlock + + +class M17Chain(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain): + def __init__(self): + workers = [ + FmDemod(), + DcBlock(), + Limit(), + Convert(Format.FLOAT, Format.SHORT), + M17Module(), + ] + super().__init__(workers) + + def getFixedIfSampleRate(self) -> int: + return 48000 + + def getFixedAudioRate(self) -> int: + return 8000 + + def supportsSquelch(self) -> bool: + return False diff --git a/csdr/module.py b/csdr/module.py index b12d950..efd5c1f 100644 --- a/csdr/module.py +++ b/csdr/module.py @@ -28,12 +28,7 @@ class Module(BaseModule, metaclass=ABCMeta): pass -class ThreadModule(Module, Thread, metaclass=ABCMeta): - def __init__(self): - self.doRun = True - super().__init__() - Thread.__init__(self) - +class AutoStartModule(Module, metaclass=ABCMeta): def _checkStart(self) -> None: if self.reader is not None and self.writer is not None: self.start() @@ -46,6 +41,17 @@ class ThreadModule(Module, Thread, metaclass=ABCMeta): super().setWriter(writer) self._checkStart() + @abstractmethod + def start(self): + pass + + +class ThreadModule(AutoStartModule, Thread, metaclass=ABCMeta): + def __init__(self): + self.doRun = True + super().__init__() + Thread.__init__(self) + @abstractmethod def run(self): pass @@ -54,6 +60,9 @@ class ThreadModule(Module, Thread, metaclass=ABCMeta): self.doRun = False self.reader.stop() + def start(self): + Thread.start(self) + class PickleModule(ThreadModule): def getInputFormat(self) -> Format: diff --git a/owrx/aprs/module.py b/owrx/aprs/module.py index 5e559b0..ddbbde6 100644 --- a/owrx/aprs/module.py +++ b/owrx/aprs/module.py @@ -1,4 +1,4 @@ -from csdr.module import Module +from csdr.module import AutoStartModule from pycsdr.types import Format from pycsdr.modules import Reader, Writer, TcpSource from subprocess import Popen, PIPE @@ -12,7 +12,7 @@ import logging logger = logging.getLogger(__name__) -class DirewolfModule(Module): +class DirewolfModule(AutoStartModule): def __init__(self, service: bool = False): self.process = None self.inputReader = None @@ -20,10 +20,6 @@ class DirewolfModule(Module): self.service = service super().__init__() - def setReader(self, reader: Reader) -> None: - super().setReader(reader) - self.start() - def setWriter(self, writer: Writer) -> None: super().setWriter(writer) if self.tcpSource is not None: diff --git a/owrx/dsp.py b/owrx/dsp.py index 71c94a6..81c0e09 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -12,6 +12,7 @@ from csdr.chain.selector import Selector from csdr.chain.clientaudio import ClientAudioChain from csdr.chain.analog import NFm, WFm, Am, Ssb from csdr.chain.digiham import DigihamChain, Dmr, Dstar, Nxdn, Ysf +from csdr.chain.m17 import M17Chain from csdr.chain.fft import FftChain from csdr.chain.digimodes import AudioChopperDemodulator, PacketDemodulator, PocsagDemodulator from pycsdr.modules import Buffer, Writer @@ -425,25 +426,24 @@ class DspManager(Output, SdrSourceEventClient): if isinstance(demod, BaseDemodulatorChain): return demod # TODO: move this to Modes - demodChain = None if demod == "nfm": - demodChain = NFm(self.props["output_rate"]) + return NFm(self.props["output_rate"]) elif demod == "wfm": - demodChain = WFm(self.props["hd_output_rate"], self.props["wfm_deemphasis_tau"]) + return WFm(self.props["hd_output_rate"], self.props["wfm_deemphasis_tau"]) elif demod == "am": - demodChain = Am() + return Am() elif demod in ["usb", "lsb", "cw"]: - demodChain = Ssb() + return Ssb() elif demod == "dmr": - demodChain = Dmr(self.props["digital_voice_codecserver"]) + return Dmr(self.props["digital_voice_codecserver"]) elif demod == "dstar": - demodChain = Dstar(self.props["digital_voice_codecserver"]) + return Dstar(self.props["digital_voice_codecserver"]) elif demod == "ysf": - demodChain = Ysf(self.props["digital_voice_codecserver"]) + return Ysf(self.props["digital_voice_codecserver"]) elif demod == "nxdn": - demodChain = Nxdn(self.props["digital_voice_codecserver"]) - - return demodChain + return Nxdn(self.props["digital_voice_codecserver"]) + elif demod == "m17": + return M17Chain() def setDemodulator(self, mod): demodulator = self._getDemodulator(mod) diff --git a/owrx/m17.py b/owrx/m17.py new file mode 100644 index 0000000..d4c6944 --- /dev/null +++ b/owrx/m17.py @@ -0,0 +1,42 @@ +from csdr.module import AutoStartModule +from pycsdr.types import Format +from subprocess import Popen, PIPE +import threading + + +class M17Module(AutoStartModule): + def __init__(self): + self.process = None + super().__init__() + + def getInputFormat(self) -> Format: + return Format.SHORT + + def getOutputFormat(self) -> Format: + return Format.SHORT + + def start(self): + self.process = Popen(["m17-demod"], stdin=PIPE, stdout=PIPE) + threading.Thread(target=self.pump(self.reader.read, self.process.stdin.write)).start() + threading.Thread(target=self.pump(self.process.stdout.read, self.writer.write)).start() + + def stop(self): + if self.process is not None: + self.process.terminate() + self.process.wait() + self.process = None + self.reader.stop() + + def pump(self, read, write): + def copy(): + while True: + data = None + try: + data = read() + except ValueError: + pass + if data is None or isinstance(data, bytes) and len(data) == 0: + break + write(data) + + return copy diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index 3a7d2f1..d6d6698 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -273,13 +273,10 @@ class ServiceHandler(SdrSourceEventClient): if isinstance(demod, BaseDemodulatorChain): return demod # TODO: move this to Modes - demodChain = None if demod == "nfm": - demodChain = NFm(48000) + return NFm(48000) elif demod in ["usb", "lsb", "cw"]: - demodChain = Ssb() - - return demodChain + return Ssb() # TODO move this elsewhere def _getSecondaryDemodulator(self, mod):