diff --git a/csdr/module/drm.py b/csdr/module/drm.py index f8d59de1..a7515bd8 100644 --- a/csdr/module/drm.py +++ b/csdr/module/drm.py @@ -1,14 +1,11 @@ -from csdr.module import PopenModule +from pycsdr.modules import ExecModule from pycsdr.types import Format -class DrmModule(PopenModule): - def getInputFormat(self) -> Format: - return Format.COMPLEX_FLOAT - - def getOutputFormat(self) -> Format: - return Format.SHORT - - def getCommand(self): - # dream -c 6 --sigsrate 48000 --audsrate 48000 -I - -O - - return ["dream", "-c", "6", "--sigsrate", "48000", "--audsrate", "48000", "-I", "-", "-O", "-"] +class DrmModule(ExecModule): + def __init__(self): + super().__init__( + Format.COMPLEX_SHORT, + Format.SHORT, + ["dream", "-c", "6", "--sigsrate", "48000", "--audsrate", "48000", "-I", "-", "-O", "-"] + ) diff --git a/csdr/module/freedv.py b/csdr/module/freedv.py index 2c1014f4..90bc38a7 100644 --- a/csdr/module/freedv.py +++ b/csdr/module/freedv.py @@ -1,13 +1,11 @@ from pycsdr.types import Format -from csdr.module import PopenModule +from pycsdr.modules import ExecModule -class FreeDVModule(PopenModule): - def getInputFormat(self) -> Format: - return Format.SHORT - - def getOutputFormat(self) -> Format: - return Format.SHORT - - def getCommand(self): - return ["freedv_rx", "1600", "-", "-"] +class FreeDVModule(ExecModule): + def __init__(self): + super().__init__( + Format.SHORT, + Format.SHORT, + ["freedv_rx", "1600", "-", "-"] + ) diff --git a/csdr/module/msk144.py b/csdr/module/msk144.py index 2ccc64cc..9d1b0023 100644 --- a/csdr/module/msk144.py +++ b/csdr/module/msk144.py @@ -1,5 +1,6 @@ from pycsdr.types import Format -from csdr.module import PopenModule, ThreadModule +from pycsdr.modules import ExecModule +from csdr.module import ThreadModule from owrx.wsjt import WsjtParser, Msk144Profile import pickle @@ -7,15 +8,13 @@ import logging logger = logging.getLogger(__name__) -class Msk144Module(PopenModule): - def getCommand(self): - return ["msk144decoder"] - - def getInputFormat(self) -> Format: - return Format.SHORT - - def getOutputFormat(self) -> Format: - return Format.CHAR +class Msk144Module(ExecModule): + def __init__(self): + super().__init__( + Format.SHORT, + Format.CHAR, + ["msk144decoder"] + ) class ParserAdapter(ThreadModule): diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index 7abb2f4f..5fbe55e7 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-08-18 (fmdemod replacement) -cmakebuild csdr 98de79de2187bf9b7d007fc6ae252175578c1459 +# latest develop as of 2023-08-21 (introduction of execmodule) +cmakebuild csdr 3a11e7f800d8226e2f5de89432150a724bc7960e git clone https://github.com/jketterl/pycsdr.git cd pycsdr -# latest develop as of 2023-08-16 (added lowpass) -git checkout eec718ae365583ebf5f315ae45967d2f635ff209 +# latest develop as of 2023-08-21 (introduction of execmodule) +git checkout ebb46984c9b2a31dcf3810ad1f11e461a839e114 ./setup.py install install_headers cd .. rm -rf pycsdr diff --git a/owrx/aprs/module.py b/owrx/aprs/module.py index 28bb51df..c4c1ded7 100644 --- a/owrx/aprs/module.py +++ b/owrx/aprs/module.py @@ -1,10 +1,7 @@ -from csdr.module import AutoStartModule from pycsdr.types import Format -from pycsdr.modules import Writer, TcpSource -from subprocess import Popen, PIPE +from pycsdr.modules import Writer, TcpSource, ExecModule, CallbackWriter from owrx.aprs.direwolf import DirewolfConfig, DirewolfConfigSubscriber from owrx.config.core import CoreConfig -import threading import time import os @@ -13,46 +10,55 @@ import logging logger = logging.getLogger(__name__) -class DirewolfModule(AutoStartModule, DirewolfConfigSubscriber): +class LogWriter(CallbackWriter): + def __init__(self): + self.retained = bytes() + super().__init__(Format.CHAR) + + def write(self, data: bytes) -> None: + self.retained += data + lines = self.retained.split(b"\n") + + # keep the last line + # this should either be empty if the last char was \n + # or an incomplete line if the read returned early + self.retained = lines[-1] + + # log all completed lines + for line in lines[0:-1]: + logger.info("{}: {}".format("STDOUT", line.strip(b'\n').decode())) + + +class DirewolfModule(ExecModule, DirewolfConfigSubscriber): def __init__(self, service: bool = False): - self.process = None self.tcpSource = None + self.writer = None self.service = service self.direwolfConfigPath = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format( tmp_dir=CoreConfig().get_temporary_directory(), myid=id(self) ) - self.direwolfConfig = None - super().__init__() - def setWriter(self, writer: Writer) -> None: - super().setWriter(writer) - if self.tcpSource is not None: - self.tcpSource.setWriter(writer) - - def getInputFormat(self) -> Format: - return Format.SHORT - - def getOutputFormat(self) -> Format: - return Format.CHAR - - def start(self): self.direwolfConfig = DirewolfConfig() self.direwolfConfig.wire(self) + self.__writeConfig() + + super().__init__(Format.SHORT, Format.CHAR, ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"]) + # direwolf supplies the data via a socket which we tap into in start() + # the output on its STDOUT is informative, but we still want to log it + super().setWriter(LogWriter()) + self.start() + + def __writeConfig(self): file = open(self.direwolfConfigPath, "w") file.write(self.direwolfConfig.getConfig(self.service)) file.close() - # direwolf -c {direwolf_config} -r {audio_rate} -t 0 -q d -q h 1>&2 - self.process = Popen( - ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"], - start_new_session=True, - stdin=PIPE, - ) - - # resume in case the reader has been stop()ed before - self.reader.resume() - threading.Thread(target=self.pump(self.reader.read, self.process.stdin.write)).start() + def setWriter(self, writer: Writer) -> None: + self.writer = writer + if self.tcpSource is not None: + self.tcpSource.setWriter(writer) + def start(self): delay = 0.5 retries = 0 while True: @@ -68,16 +74,16 @@ class DirewolfModule(AutoStartModule, DirewolfConfigSubscriber): retries += 1 time.sleep(delay) - def stop(self): - if self.process is not None: - self.process.terminate() - self.process.wait() - self.process = None + def restart(self): + self.__writeConfig() + super().restart() + self.start() + + def onConfigChanged(self): + self.restart() + + def stop(self) -> None: + super().stop() os.unlink(self.direwolfConfigPath) self.direwolfConfig.unwire(self) self.direwolfConfig = None - self.reader.stop() - - def onConfigChanged(self): - self.stop() - self.start()