Adding CW skimmer.

This commit is contained in:
Marat Fayzullin 2024-11-12 21:44:30 -05:00
parent 3964f9a23c
commit 399cc3f267
5 changed files with 67 additions and 2 deletions

View File

@ -1,8 +1,8 @@
from csdr.chain.demodulator import ServiceDemodulator, DialFrequencyReceiver
from csdr.module.toolbox import Rtl433Module, MultimonModule, DumpHfdlModule, DumpVdl2Module, Dump1090Module, AcarsDecModule, RedseaModule, SatDumpModule
from csdr.module.toolbox import Rtl433Module, MultimonModule, DumpHfdlModule, DumpVdl2Module, Dump1090Module, AcarsDecModule, RedseaModule, SatDumpModule, CwSkimmerModule
from pycsdr.modules import FmDemod, AudioResampler, Convert, Agc, Squelch
from pycsdr.types import Format
from owrx.toolbox import TextParser, PageParser, SelCallParser, EasParser, IsmParser, RdsParser
from owrx.toolbox import TextParser, PageParser, SelCallParser, EasParser, IsmParser, RdsParser, CwSkimmerParser
from owrx.aircraft import HfdlParser, Vdl2Parser, AdsbParser, AcarsParser
from datetime import datetime
@ -223,6 +223,27 @@ class RdsDemodulator(ServiceDemodulator, DialFrequencyReceiver):
self.parser.setDialFrequency(frequency)
class CwSkimmerDemodulator(ServiceDemodulator, DialFrequencyReceiver):
def __init__(self, sampleRate: int = 12000, charCount: int = 8):
self.sampleRate = sampleRate
self.parser = CwSkimmerParser()
workers = [
CwSkimmerModule(sampleRate, charCount),
self.parser,
]
# Connect all the workers
super().__init__(workers)
def getFixedAudioRate(self) -> int:
return self.sampleRate
def supportsSquelch(self) -> bool:
return False
def setDialFrequency(self, frequency: int) -> None:
self.parser.setDialFrequency(frequency)
class NoaaAptDemodulator(ServiceDemodulator):
def __init__(self, satellite: int = 19, service: bool = False):
d = datetime.utcnow()

View File

@ -119,6 +119,12 @@ class AcarsDecModule(WavFileModule):
return Format.CHAR
class CwSkimmerModule(ExecModule):
def __init__(self, sampleRate: int = 12000, charCount: int = 8):
cmd = ["csdr-cwskimmer", "-r", str(sampleRate), "-n", str(charCount)]
super().__init__(Format.FLOAT, Format.CHAR, cmd)
class RedseaModule(ExecModule):
def __init__(self, sampleRate: int = 171000, rbds: bool = False):
cmd = [ "redsea", "--input", "mpx", "--samplerate", str(sampleRate) ]

View File

@ -720,6 +720,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
elif mod == "cwdecoder":
from csdr.chain.digimodes import CwDemodulator
return CwDemodulator(75.0)
elif mod == "cwskimmer":
from csdr.chain.digimodes import CwSkimmerDemodulator
return CwSkimmerDemodulator()
elif mod == "mfrtty170":
from csdr.chain.digimodes import MFRttyDemodulator
return MFRttyDemodulator(170.0, 45.45, reverse = False)

View File

@ -194,6 +194,7 @@ class Modes(object):
squelch=False,
),
DigitalMode("cwdecoder", "CW Decoder", underlying=["usb", "lsb"]),
DigitalMode("cwskimmer", "CW Skimmer", underlying=["usb", "lsb"]),
# Replaced by Jakob's RTTY decoder
# DigitalMode("mfrtty170", "RTTY-170", underlying=["usb"]),
# DigitalMode("mfrtty450", "RTTY-450", underlying=["usb"]),

View File

@ -414,3 +414,37 @@ class EasParser(TextParser):
# Return received message as text
return "\n".join(out)
class CwSkimmerParser(TextParser):
def __init__(self, charTotal: int = 32, service: bool = False):
self.reLine = re.compile("^(\d+):\s*(.*)$")
self.data = {}
# Construct parent object
super().__init__(filePrefix="CW", service=service)
def parse(self, msg: bytes):
# Do not parse in service mode
if self.service:
return None
# Parse CW messages by frequency
msg = msg.decode("utf-8", "replace")
r = self.reLine.match(msg)
if r is not None:
freq = int(r.group(1))
text = r.group(2)
# Add up newly decoded characters
if freq in self.data:
text = self.data[freq] + text
# Truncate received text to charTotal
text = text if len(text) <= charTotal else text[:charTotal]
self.data[freq] = text
# Compose output
out = { "mode": "CW", "text": text }
# Add frequency, if known
if self.frequency:
out["freq"] = self.frequency + freq
# Done
return out
# No result
return None