Porting MSK144 decoder from the OWRX 'develop' branch.

This commit is contained in:
Marat Fayzullin 2023-05-05 23:36:30 -04:00
parent 50c288dfaf
commit b500367e9a
13 changed files with 63 additions and 8 deletions

View File

@ -192,7 +192,8 @@
"jt9": 50312000,
"ft4": 50318000,
"js8": 50318000,
"q65": [50211000, 50275000]
"q65": [50211000, 50275000],
"msk144": 50260000
},
"tags": ["hamradio"]
},
@ -201,7 +202,8 @@
"lower_bound": 70150000,
"upper_bound": 70200000,
"frequencies": {
"wspr": 70091000
"wspr": 70091000,
"msk144": 70230000
},
"tags": ["hamradio"]
},
@ -215,7 +217,8 @@
"ft4": 144170000,
"jt65": 144120000,
"packet": [144800000, 145825000],
"q65": 144116000
"q65": 144116000,
"msk144": 144360000
},
"tags": ["hamradio"]
},
@ -225,7 +228,8 @@
"upper_bound": 440000000,
"frequencies": {
"pocsag": 439987500,
"q65": 432065000
"q65": 432065000,
"msk144": 432360000
},
"tags": ["hamradio"]
},

View File

@ -1,4 +1,5 @@
from csdr.chain.demodulator import ServiceDemodulator, SecondaryDemodulator, DialFrequencyReceiver, SecondarySelectorChain
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,6 +22,23 @@ class AudioChopperDemodulator(ServiceDemodulator, DialFrequencyReceiver):
self.chopper.setDialFrequency(frequency)
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, ais: bool = False):
self.parser = AprsParser()

2
debian/control vendored
View File

@ -11,6 +11,6 @@ Vcs-Git: https://github.com/luarvique/openwebrx.git
Package: openwebrx
Architecture: all
Depends: adduser, python3 (>= 3.5), python3-pkg-resources, owrx-connector (>= 0.6), soapysdr-tools, python3-csdr (>= 0.18.9), ${python3:Depends}, ${misc:Depends}
Recommends: python3-digiham (>= 0.6), direwolf (>= 1.4), wsjtx, js8call, runds-connector (>= 0.2), hpsdrconnector, aprs-symbols, m17-demod, js8call, python3-js8py (>= 0.1), nmux (>= 0.18), codecserver (>= 0.1)
Recommends: python3-digiham (>= 0.6), direwolf (>= 1.4), wsjtx, js8call, runds-connector (>= 0.2), hpsdrconnector, aprs-symbols, m17-demod, js8call, python3-js8py (>= 0.1), nmux (>= 0.18), codecserver (>= 0.1), msk144decoder
Description: multi-user web sdr
Open source, multi-user SDR receiver with a web interface

View File

@ -60,6 +60,10 @@ mv /wsjtx.patch ${WSJT_DIR}
cmakebuild ${WSJT_DIR}
rm ${WSJT_TGZ}
git clone https://github.com/alexander-sholohov/msk144decoder.git
# latest from main as of 2023-02-21
MAKEFLAGS="" cmakebuild msk144decoder fe2991681e455636e258e83c29fd4b2a72d16095
git clone --depth 1 -b 1.6 https://github.com/wb2osz/direwolf.git
cd direwolf
# hamlib is present (necessary for the wsjt-x and js8call builds) and would be used, but there's no real need.

View File

@ -1331,6 +1331,7 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-select-channel,
@ -1345,6 +1346,7 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-select-channel
{
@ -1363,6 +1365,7 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-canvas-container
{

View File

@ -162,7 +162,7 @@ DemodulatorPanel.prototype.updatePanels = function() {
var modulation = this.getDemodulator().get_secondary_demod();
$('#openwebrx-panel-digimodes').attr('data-mode', modulation);
toggle_panel("openwebrx-panel-digimodes", !!modulation);
toggle_panel("openwebrx-panel-wsjt-message", ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"].indexOf(modulation) >= 0);
toggle_panel("openwebrx-panel-wsjt-message", ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65", "msk144"].indexOf(modulation) >= 0);
toggle_panel("openwebrx-panel-js8-message", modulation == "js8");
toggle_panel("openwebrx-panel-packet-message", ["packet", "ais"].indexOf(modulation) >= 0);
toggle_panel("openwebrx-panel-pocsag-message", modulation === "pocsag");

View File

@ -50,7 +50,7 @@ MessagePanel.prototype.initClearButton = function() {
function WsjtMessagePanel(el) {
MessagePanel.call(this, el);
this.initClearTimer();
this.qsoModes = ['FT8', 'JT65', 'JT9', 'FT4', 'FST4', 'Q65'];
this.qsoModes = ['FT8', 'JT65', 'JT9', 'FT4', 'FST4', 'Q65', 'MSK144'];
this.beaconModes = ['WSPR', 'FST4W'];
this.modes = [].concat(this.qsoModes, this.beaconModes);
}

View File

@ -600,6 +600,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
from csdr.chain.digimodes import AudioChopperDemodulator
from owrx.wsjt import WsjtParser
return AudioChopperDemodulator(mod, WsjtParser())
elif mod == "msk144":
from csdr.chain.digimodes import Msk144Demodulator
return Msk144Demodulator()
elif mod == "js8":
from csdr.chain.digimodes import AudioChopperDemodulator
from owrx.js8 import Js8Parser

View File

@ -80,6 +80,7 @@ class FeatureDetector(object):
"wsjt-x": ["wsjtx"],
"wsjt-x-2-3": ["wsjtx_2_3"],
"wsjt-x-2-4": ["wsjtx_2_4"],
"msk144": ["msk144decoder"],
"packet": ["direwolf"],
"pocsag": ["digiham"],
"js8call": ["js8", "js8py"],
@ -460,6 +461,13 @@ class FeatureDetector(object):
"""
return self.has_wsjtx() and self._has_wsjtx_version(LooseVersion("2.4"))
def has_msk144decoder(self):
"""
To decode the MSK144 digimode please install the "msk144decoder". See the
[project page](https://github.com/alexander-sholohov/msk144decoder) for more details.
"""
return self.command_is_runnable("msk144decoder")
def has_js8(self):
"""
To decode JS8, you will need to install [JS8Call](http://js8call.com/)

View File

@ -128,6 +128,7 @@ class Modes(object):
WsjtMode("fst4", "FST4", requirements=["wsjt-x-2-3"]),
WsjtMode("fst4w", "FST4W", bandpass=Bandpass(1350, 1650), requirements=["wsjt-x-2-3"]),
WsjtMode("q65", "Q65", requirements=["wsjt-x-2-4"]),
DigitalMode("msk144", "MSK144", requirements=["msk144"], underlying=["usb"], service=True),
Js8Mode("js8", "JS8Call"),
DigitalMode(
"packet",

View File

@ -29,7 +29,7 @@ class PskReporter(Reporter):
Current version at the time of the last change:
https://www.adif.org/312/ADIF_312.htm#Mode_Enumeration
"""
return ["FT8", "FT4", "JT9", "JT65", "FST4", "JS8", "Q65", "WSPR", "FST4W"]
return ["FT8", "FT4", "JT9", "JT65", "FST4", "JS8", "Q65", "WSPR", "FST4W", "MSK144"]
def stop(self):
self.cancelTimer()

View File

@ -299,6 +299,9 @@ class ServiceHandler(SdrSourceEventClient):
from csdr.chain.digimodes import AudioChopperDemodulator
from owrx.wsjt import WsjtParser
return AudioChopperDemodulator(mod, WsjtParser())
elif mod == "msk144":
from csdr.chain.digimodes import Msk144Demodulator
return Msk144Demodulator()
elif mod == "js8":
from csdr.chain.digimodes import AudioChopperDemodulator
from owrx.js8 import Js8Parser

View File

@ -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: