let's try to implement jt65 and jt9 as well

This commit is contained in:
Jakob Ketterl 2019-07-14 17:09:34 +02:00
parent 0bb8b5349d
commit 7dcfead843
5 changed files with 62 additions and 21 deletions

View File

@ -25,7 +25,7 @@ import os
import signal import signal
import threading import threading
from functools import partial from functools import partial
from owrx.wsjt import Ft8Chopper, WsprChopper from owrx.wsjt import Ft8Chopper, WsprChopper, Jt9Chopper, Jt65Chopper
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -277,6 +277,10 @@ class dsp(object):
chopper = Ft8Chopper(self.secondary_process_demod.stdout) chopper = Ft8Chopper(self.secondary_process_demod.stdout)
elif smd == "wspr": elif smd == "wspr":
chopper = WsprChopper(self.secondary_process_demod.stdout) chopper = WsprChopper(self.secondary_process_demod.stdout)
elif smd == "jt65":
chopper = Jt65Chopper(self.secondary_process_demod.stdout)
elif smd == "jt9":
chopper = Jt9Chopper(self.secondary_process_demod.stdout)
chopper.start() chopper.start()
self.output.add_output("wsjt_demod", chopper.read) self.output.add_output("wsjt_demod", chopper.read)
else: else:
@ -371,7 +375,7 @@ class dsp(object):
def isWsjtMode(self, demodulator = None): def isWsjtMode(self, demodulator = None):
if demodulator is None: if demodulator is None:
demodulator = self.get_secondary_demodulator() demodulator = self.get_secondary_demodulator()
return demodulator in ["ft8", "wspr"] return demodulator in ["ft8", "wspr", "jt65", "jt9"]
def set_output_rate(self,output_rate): def set_output_rate(self,output_rate):
self.output_rate=output_rate self.output_rate=output_rate

View File

@ -840,20 +840,22 @@ img.openwebrx-mirror-img
} }
#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-content-container #openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="jt9"] #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,
#openwebrx-panel-digimodes[data-mode="jt9"] #openwebrx-digimode-select-channel
{ {
display: none; display: none;
} }
#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-canvas-container #openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="jt9"] #openwebrx-digimode-canvas-container
{ {
height: 200px; height: 200px;
margin: -10px; margin: -10px;
} }
#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-select-channel
{
display: none;
}

View File

@ -82,6 +82,8 @@
<option value="bpsk31">BPSK31</option> <option value="bpsk31">BPSK31</option>
<option value="ft8">FT8</option> <option value="ft8">FT8</option>
<option value="wspr">WSPR</option> <option value="wspr">WSPR</option>
<option value="jt65">JT65</option>
<option value="jt9">JT9</option>
</select> </select>
</div> </div>
<div class="openwebrx-panel-line"> <div class="openwebrx-panel-line">

View File

@ -1385,7 +1385,7 @@ function update_wsjt_panel(msg) {
var t = new Date(msg['timestamp']); var t = new Date(msg['timestamp']);
var pad = function(i) { return ('' + i).padStart(2, "0"); } var pad = function(i) { return ('' + i).padStart(2, "0"); }
var linkedmsg = msg['msg']; var linkedmsg = msg['msg'];
if (msg['mode'] == 'FT8') { if (['FT8', 'JT65', 'JT9'].indexOf(msg['mode']) >= 0) {
var matches = linkedmsg.match(/(.*\s[A-Z0-9]+\s)([A-R]{2}[0-9]{2})$/); var matches = linkedmsg.match(/(.*\s[A-Z0-9]+\s)([A-R]{2}[0-9]{2})$/);
if (matches && matches[2] != 'RR73') { if (matches && matches[2] != 'RR73') {
linkedmsg = html_escape(matches[1]) + '<a href="/map?locator=' + matches[2] + '" target="_blank">' + matches[2] + '</a>'; linkedmsg = html_escape(matches[1]) + '<a href="/map?locator=' + matches[2] + '" target="_blank">' + matches[2] + '</a>';
@ -2709,6 +2709,8 @@ function demodulator_digital_replace(subtype)
case "rtty": case "rtty":
case "ft8": case "ft8":
case "wspr": case "wspr":
case "jt65":
case "jt9":
secondary_demod_start(subtype); secondary_demod_start(subtype);
demodulator_analog_replace('usb', true); demodulator_analog_replace('usb', true);
demodulator_buttons_update(); demodulator_buttons_update();
@ -2716,7 +2718,7 @@ function demodulator_digital_replace(subtype)
} }
$('#openwebrx-panel-digimodes').attr('data-mode', subtype); $('#openwebrx-panel-digimodes').attr('data-mode', subtype);
toggle_panel("openwebrx-panel-digimodes", true); toggle_panel("openwebrx-panel-digimodes", true);
toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr'].indexOf(subtype) >= 0); toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9'].indexOf(subtype) >= 0);
} }
function secondary_demod_create_canvas() function secondary_demod_create_canvas()
@ -2888,6 +2890,8 @@ function secondary_demod_listbox_changed()
case "rtty": case "rtty":
case "ft8": case "ft8":
case "wspr": case "wspr":
case "jt65":
case "jt9":
demodulator_digital_replace(sdm); demodulator_digital_replace(sdm);
break; break;
} }

View File

@ -144,9 +144,29 @@ class WsprChopper(WsjtChopper):
return ["wsprd", "-d", file] return ["wsprd", "-d", file]
class Jt65Chopper(WsjtChopper):
def __init__(self, source):
self.interval = 60
super().__init__(source)
def decoder_commandline(self, file):
#TODO expose decoding quality parameters through config
return ["jt9", "--jt65", "-d", "3", file]
class Jt9Chopper(WsjtChopper):
def __init__(self, source):
self.interval = 60
super().__init__(source)
def decoder_commandline(self, file):
#TODO expose decoding quality parameters through config
return ["jt9", "--jt9", "-d", "3", file]
class WsjtParser(object): class WsjtParser(object):
locator_pattern = re.compile(".*\\s([A-Z0-9]+)\\s([A-R]{2}[0-9]{2})$") locator_pattern = re.compile(".*\\s([A-Z0-9]+)\\s([A-R]{2}[0-9]{2})$")
jt9_pattern = re.compile("^[0-9]{6} .*") jt9_pattern = re.compile("^([0-9]{6}|\\*{4}) .*")
wspr_pattern = re.compile("^[0-9]{4} .*") wspr_pattern = re.compile("^[0-9]{4} .*")
wspr_splitter_pattern = re.compile("([A-Z0-9]*)\\s([A-R]{2}[0-9]{2})\\s([0-9]+)") wspr_splitter_pattern = re.compile("([A-Z0-9]*)\\s([A-R]{2}[0-9]{2})\\s([0-9]+)")
@ -154,7 +174,9 @@ class WsjtParser(object):
self.handler = handler self.handler = handler
modes = { modes = {
"~": "FT8" "~": "FT8",
"#": "JT65",
"@": "JT9"
} }
def parse(self, data): def parse(self, data):
@ -179,15 +201,22 @@ class WsjtParser(object):
def parse_from_jt9(self, msg): def parse_from_jt9(self, msg):
# ft8 sample # ft8 sample
# '222100 -15 -0.0 508 ~ CQ EA7MJ IM66' # '222100 -15 -0.0 508 ~ CQ EA7MJ IM66'
# jt65 sample
# '**** -10 0.4 1556 # CQ RN6AM KN95'
out = {} out = {}
ts = datetime.strptime(msg[0:6], "%H%M%S") if msg.startswith("****"):
out["timestamp"] = int(datetime.combine(date.today(), ts.time(), datetime.now().tzinfo).timestamp() * 1000) out["timestamp"] = int(datetime.now().timestamp() * 1000)
out["db"] = float(msg[7:10]) msg = msg[5:]
out["dt"] = float(msg[11:15]) else:
out["freq"] = int(msg[16:20]) ts = datetime.strptime(msg[0:6], "%H%M%S")
modeChar = msg[21:22] out["timestamp"] = int(datetime.combine(date.today(), ts.time(), datetime.now().tzinfo).timestamp() * 1000)
msg = msg[7:]
out["db"] = float(msg[0:3])
out["dt"] = float(msg[4:8])
out["freq"] = int(msg[9:13])
modeChar = msg[14:15]
out["mode"] = mode = WsjtParser.modes[modeChar] if modeChar in WsjtParser.modes else "unknown" out["mode"] = mode = WsjtParser.modes[modeChar] if modeChar in WsjtParser.modes else "unknown"
wsjt_msg = msg[24:60].strip() wsjt_msg = msg[17:53].strip()
self.parseLocator(wsjt_msg, mode) self.parseLocator(wsjt_msg, mode)
out["msg"] = wsjt_msg out["msg"] = wsjt_msg
return out return out