From c91365a61244a6b43ac6fda36a54f405398e3b39 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Thu, 21 Nov 2024 19:09:20 -0500 Subject: [PATCH] Added CWSkimmer decoder using csdr-cwskimmer tool. --- buildall.sh | 16 ++++++ csdr/chain/toolbox.py | 26 +++++++++- csdr/module/toolbox.py | 6 +++ htdocs/css/openwebrx.css | 94 ++++++++++++++++++++++------------ htdocs/index.html | 1 + htdocs/lib/DemodulatorPanel.js | 6 +-- htdocs/lib/MessagePanel.js | 75 +++++++++++++++++++++++++++ htdocs/openwebrx.js | 2 +- owrx/dsp.py | 3 ++ owrx/feature.py | 9 ++++ owrx/modes.py | 14 +++-- owrx/toolbox.py | 37 +++++++++++++ 12 files changed, 246 insertions(+), 43 deletions(-) diff --git a/buildall.sh b/buildall.sh index 230279dd..e5951de0 100755 --- a/buildall.sh +++ b/buildall.sh @@ -18,6 +18,7 @@ GIT_CSDR_ETI=https://github.com/luarvique/csdr-eti.git GIT_PYCSDR_ETI=https://github.com/luarvique/pycsdr-eti.git GIT_JS8PY=https://github.com/jketterl/js8py.git GIT_REDSEA=https://github.com/luarvique/redsea.git +GIT_CWSKIMMER=https://github.com/luarvique/csdr-cwskimmer.git GIT_SOAPYSDRPLAY3=https://github.com/luarvique/SoapySDRPlay3.git GIT_OPENWEBRX=https://github.com/luarvique/openwebrx.git @@ -45,6 +46,8 @@ if [ "${1:-}" == "--ask" ]; then [[ "$ret" == [Yy]* ]] && BUILD_JS8PY=y || BUILD_JS8PY=n echo;read -n1 -p "Build Redsea? [yN] " ret [[ "$ret" == [Yy]* ]] && BUILD_REDSEA=y || BUILD_REDSEA=n + echo;read -n1 -p "Build csdr-cwskimmer? [yN] " ret + [[ "$ret" == [Yy]* ]] && BUILD_CWSKIMMER=y || BUILD_CWSKIMMER=n echo;read -n1 -p "Build SoapySDRPlay3? [yN] " ret [[ "$ret" == [Yy]* ]] && BUILD_SOAPYSDRPLAY3=y || BUILD_SOAPYSDRPLAY3=n echo;read -n1 -p "Build OpenWebRX+? [Yn] " ret @@ -65,6 +68,7 @@ else BUILD_PYCSDR_ETI=y BUILD_JS8PY=y BUILD_REDSEA=y + BUILD_CWSKIMMER=y CLEAN_OUTPUT=y fi @@ -100,6 +104,7 @@ echo "csdr-eti: $BUILD_CSDR_ETI" echo "pycsdr-eti: $BUILD_PYCSDR_ETI" echo "js8py: $BUILD_JS8PY" echo "redsea: $BUILD_REDSEA" +echo "csdr-cwskimmer: $BUILD_CWSKIMMER" echo "SoapySDRPlay3: $BUILD_SOAPYSDRPLAY3" echo "OpenWebRx: $BUILD_OWRX" echo "Clean OUTPUT folder: $CLEAN_OUTPUT" @@ -226,6 +231,17 @@ if [ "${BUILD_REDSEA:-}" == "y" ]; then #sudo dpkg -i *redsea*.deb fi +if [ "${BUILD_CWSKIMMER:-}" == "y" ]; then + echo "##### Building csdr-cwskimmer... #####" + git clone -b master "$GIT_CWSKIMMER" + pushd csdr-cwskimmer + dpkg-buildpackage -us -uc + popd + # Not installing csdr-cwskimmer here since there are no further + # build steps depending on it + #sudo dpkg -i csdr-cwskimmer*.deb +fi + if [ "${BUILD_SOAPYSDRPLAY3:-}" == "y" ]; then echo "##### Building SoapySDRPlay3 ... #####" git clone -b master "$GIT_SOAPYSDRPLAY3" diff --git a/csdr/chain/toolbox.py b/csdr/chain/toolbox.py index 0868d2a7..8530f847 100644 --- a/csdr/chain/toolbox.py +++ b/csdr/chain/toolbox.py @@ -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,28 @@ class RdsDemodulator(ServiceDemodulator, DialFrequencyReceiver): self.parser.setDialFrequency(frequency) +class CwSkimmerDemodulator(ServiceDemodulator, DialFrequencyReceiver): + def __init__(self, sampleRate: int = 48000, charCount: int = 4, service: bool = False): + self.sampleRate = sampleRate + self.parser = CwSkimmerParser(service) + workers = [ + Convert(Format.FLOAT, Format.SHORT), + 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() diff --git a/csdr/module/toolbox.py b/csdr/module/toolbox.py index 2308a40f..86682d82 100644 --- a/csdr/module/toolbox.py +++ b/csdr/module/toolbox.py @@ -119,6 +119,12 @@ class AcarsDecModule(WavFileModule): return Format.CHAR +class CwSkimmerModule(ExecModule): + def __init__(self, sampleRate: int = 48000, charCount: int = 4): + cmd = ["csdr-cwskimmer", "-i", "-r", str(sampleRate), "-n", str(charCount)] + super().__init__(Format.SHORT, Format.CHAR, cmd) + + class RedseaModule(ExecModule): def __init__(self, sampleRate: int = 171000, rbds: bool = False): cmd = [ "redsea", "--input", "mpx", "--samplerate", str(sampleRate) ] diff --git a/htdocs/css/openwebrx.css b/htdocs/css/openwebrx.css index a38f0bb6..aa56c74f 100644 --- a/htdocs/css/openwebrx.css +++ b/htdocs/css/openwebrx.css @@ -1376,6 +1376,62 @@ img.openwebrx-mirror-img text-align: right; } +#openwebrx-panel-cwskimmer-message { + height: 310px; +} + +#openwebrx-panel-cwskimmer-message tbody { + height: 280px; +} + +#openwebrx-panel-cwskimmer-message .freq { + width: 70px; +} + +#openwebrx-panel-cwskimmer-message td.freq { + text-align: right; +} + +#openwebrx-panel-cwskimmer-message .text { + width: 500px; +} + +#openwebrx-panel-cwskimmer-message td.text { + font-family: monospace; + white-space: nowrap; +} + +#openwebrx-panel-ism-message { + height: 310px; +} + +#openwebrx-panel-ism-message tbody { + height: 280px; +} + +#openwebrx-panel-ism-message .address { + width: 120px; + text-align: center; +} + +#openwebrx-panel-ism-message .device { + width: 220px; + text-align: center; +} + +#openwebrx-panel-ism-message .timestamp { + width: 246px; + max-width: 486px; + white-space: pre; + text-align: center; +} + +#openwebrx-panel-ism-message .attr { + width: 283px; + padding-left: 10px; + padding-right: 10px; +} + #openwebrx-panel-dsc-message { height: 310px; } @@ -1415,37 +1471,6 @@ img.openwebrx-mirror-img word-wrap: break-word; } -#openwebrx-panel-ism-message { - height: 310px; -} - -#openwebrx-panel-ism-message tbody { - height: 280px; -} - -#openwebrx-panel-ism-message .address { - width: 120px; - text-align: center; -} - -#openwebrx-panel-ism-message .device { - width: 220px; - text-align: center; -} - -#openwebrx-panel-ism-message .timestamp { - width: 246px; - max-width: 486px; - white-space: pre; - text-align: center; -} - -#openwebrx-panel-ism-message .attr { - width: 283px; - padding-left: 10px; - padding-right: 10px; -} - #openwebrx-panel-sstv-message { height: 310px; width: 365px; @@ -1588,6 +1613,7 @@ img.openwebrx-mirror-img #openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="dsc"] #openwebrx-digimode-content-container, +#openwebrx-panel-digimodes[data-mode="cwskimmer"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-select-channel, @@ -1611,7 +1637,8 @@ img.openwebrx-mirror-img #openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="selcall"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="zvei"] #openwebrx-digimode-select-channel, -#openwebrx-panel-digimodes[data-mode="eas"] #openwebrx-digimode-select-channel +#openwebrx-panel-digimodes[data-mode="eas"] #openwebrx-digimode-select-channel, +#openwebrx-panel-digimodes[data-mode="cwskimmer"] #openwebrx-digimode-select-channel { display: none; } @@ -1637,7 +1664,8 @@ img.openwebrx-mirror-img #openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-canvas-container, -#openwebrx-panel-digimodes[data-mode="dsc"] #openwebrx-digimode-canvas-container +#openwebrx-panel-digimodes[data-mode="dsc"] #openwebrx-digimode-canvas-container, +#openwebrx-panel-digimodes[data-mode="cwskimmer"] #openwebrx-digimode-canvas-container { height: 200px; margin: -10px; diff --git a/htdocs/index.html b/htdocs/index.html index 16873c1b..e63ad4eb 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -98,6 +98,7 @@ +