From afcd8277d121cdc8022895cdc11a424f8d273be8 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 14 Feb 2023 18:36:17 +0100 Subject: [PATCH] add MSK144 parsing --- csdr/chain/digimodes.py | 9 ++++++-- csdr/module/__init__.py | 2 +- csdr/module/msk144.py | 49 ++++++++++++++++++++++++++++++++++++++++- owrx/wsjt.py | 13 +++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index da3faaf..d19deaa 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -1,5 +1,5 @@ from csdr.chain.demodulator import ServiceDemodulator, SecondaryDemodulator, DialFrequencyReceiver, SecondarySelectorChain -from csdr.module.msk144 import Msk144Module +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 @@ -21,17 +21,22 @@ class AudioChopperDemodulator(ServiceDemodulator, DialFrequencyReceiver): self.chopper.setDialFrequency(frequency) -class Msk144Demodulator(ServiceDemodulator): +class Msk144Demodulator(ServiceDemodulator, DialFrequencyReceiver): def __init__(self): + self.parser = ParserAdapter() workers = [ Convert(Format.FLOAT, Format.SHORT), Msk144Module(), + self.parser, ] super().__init__(workers) def getFixedAudioRate(self) -> int: return 12000 + def setDialFrequency(self, frequency: int) -> None: + self.parser.setDialFrequency(frequency) + class PacketDemodulator(ServiceDemodulator, DialFrequencyReceiver): def __init__(self, service: bool = False): diff --git a/csdr/module/__init__.py b/csdr/module/__init__.py index 1fea434..9168d4f 100644 --- a/csdr/module/__init__.py +++ b/csdr/module/__init__.py @@ -126,7 +126,7 @@ class PopenModule(AutoStartModule, metaclass=ABCMeta): # resume in case the reader has been stop()ed before self.reader.resume() Thread(target=self.pump(self.reader.read, self.process.stdin.write)).start() - Thread(target=self.pump(partial(self.process.stdout.read, 1024), self.writer.write)).start() + Thread(target=self.pump(partial(self.process.stdout.read1, 1024), self.writer.write)).start() def stop(self): if self.process is not None: diff --git a/csdr/module/msk144.py b/csdr/module/msk144.py index cf3fcff..ff2b859 100644 --- a/csdr/module/msk144.py +++ b/csdr/module/msk144.py @@ -1,5 +1,10 @@ from pycsdr.types import Format -from csdr.module import PopenModule +from csdr.module import PopenModule, ThreadModule +from owrx.wsjt import WsjtParser, Msk144Profile +import pickle + +import logging +logger = logging.getLogger(__name__) class Msk144Module(PopenModule): @@ -11,3 +16,45 @@ class Msk144Module(PopenModule): def getOutputFormat(self) -> Format: return Format.CHAR + + +class ParserAdapter(ThreadModule): + def __init__(self): + self.retained = bytes() + self.parser = WsjtParser() + self.dialFrequency = 0 + super().__init__() + + def run(self): + profile = Msk144Profile() + + while self.doRun: + data = self.reader.read() + if data is None: + self.doRun = False + else: + logger.debug('raw data: ' + str(bytes(data))) + 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] + + # parse all completed lines + for line in lines[0:-1]: + # actual messages from msk144decoder should start with "*** " + if line[0:4] == b"*** ": + self.writer.write(pickle.dumps(self.parser.parse(profile, self.dialFrequency, line[4:]))) + else: + logger.debug("ignoring static: %s", line.decode()) + + def getInputFormat(self) -> Format: + return Format.CHAR + + def getOutputFormat(self) -> Format: + return Format.CHAR + + def setDialFrequency(self, frequency: int) -> None: + self.dialFrequency = frequency diff --git a/owrx/wsjt.py b/owrx/wsjt.py index df31a4f..8fce19d 100644 --- a/owrx/wsjt.py +++ b/owrx/wsjt.py @@ -245,6 +245,17 @@ class Q65Profile(WsjtProfile): return ["jt9", "--q65", "-p", str(self.interval), "-b", self.mode.name, "-d", str(self.decoding_depth()), file] +class Msk144Profile(WsjtProfile): + def getMode(self): + return "MSK144" + + def getInterval(self): + return 15 + + def decoder_commandline(self, file): + return None + + class WsjtParser(AudioChopperParser): def parse(self, profile: WsjtProfile, freq: int, raw_msg: bytes): try: @@ -366,6 +377,8 @@ class Jt9Decoder(Decoder): # '0003 -4 0.4 1762 # CQ R2ABM KO85' # fst4 sample # '**** -23 0.6 3023 ` <...> <...> R 591631 BI53PV' + # MSK144 sample + # '221602 8 0.4 1488 & K1JT WA4CQG EM72' msg, timestamp = self.parse_timestamp(msg) wsjt_msg = msg[17:53].strip()