implement reporting of FST4W spots (in theory)
This commit is contained in:
parent
a65f15869b
commit
885e361bab
@ -78,14 +78,14 @@ WsjtMessagePanel.prototype.pushMessage = function(msg) {
|
|||||||
return $('<div/>').text(input).html()
|
return $('<div/>').text(input).html()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (['FT8', 'JT65', 'JT9', 'FT4', 'FST4', 'FST4W'].indexOf(msg['mode']) >= 0) {
|
if (['FT8', 'JT65', 'JT9', 'FT4', 'FST4'].indexOf(msg['mode']) >= 0) {
|
||||||
matches = linkedmsg.match(/(.*\s[A-Z0-9]+\s)([A-R]{2}[0-9]{2})$/);
|
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="openwebrx-map">' + matches[2] + '</a>';
|
linkedmsg = html_escape(matches[1]) + '<a href="map?locator=' + matches[2] + '" target="openwebrx-map">' + matches[2] + '</a>';
|
||||||
} else {
|
} else {
|
||||||
linkedmsg = html_escape(linkedmsg);
|
linkedmsg = html_escape(linkedmsg);
|
||||||
}
|
}
|
||||||
} else if (msg['mode'] === 'WSPR') {
|
} else if (['WSPR', 'FST4W'].indexOf(msg['mode']) >= 0) {
|
||||||
matches = linkedmsg.match(/([A-Z0-9]*\s)([A-R]{2}[0-9]{2})(\s[0-9]+)/);
|
matches = linkedmsg.match(/([A-Z0-9]*\s)([A-R]{2}[0-9]{2})(\s[0-9]+)/);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
linkedmsg = html_escape(matches[1]) + '<a href="map?locator=' + matches[2] + '" target="openwebrx-map">' + matches[2] + '</a>' + html_escape(matches[3]);
|
linkedmsg = html_escape(matches[1]) + '<a href="map?locator=' + matches[2] + '" target="openwebrx-map">' + matches[2] + '</a>' + html_escape(matches[3]);
|
||||||
|
54
owrx/wsjt.py
54
owrx/wsjt.py
@ -156,12 +156,17 @@ class WsjtParser(Parser):
|
|||||||
return
|
return
|
||||||
|
|
||||||
mode = profile.getMode()
|
mode = profile.getMode()
|
||||||
if mode == "WSPR":
|
if mode in ["WSPR", "FST4W"]:
|
||||||
decoder = WsprDecoder(profile)
|
messageParser = BeaconMessageParser()
|
||||||
else:
|
else:
|
||||||
decoder = Jt9Decoder(profile)
|
messageParser = QsoMessageParser()
|
||||||
|
if mode == "WSPR":
|
||||||
|
decoder = WsprDecoder(profile, messageParser)
|
||||||
|
else:
|
||||||
|
decoder = Jt9Decoder(profile, messageParser)
|
||||||
out = decoder.parse(msg, freq)
|
out = decoder.parse(msg, freq)
|
||||||
out["mode"] = mode
|
out["mode"] = mode
|
||||||
|
out["interval"] = profile.getInterval()
|
||||||
|
|
||||||
self.pushDecode(mode)
|
self.pushDecode(mode)
|
||||||
if "callsign" in out and "locator" in out:
|
if "callsign" in out and "locator" in out:
|
||||||
@ -195,10 +200,9 @@ class WsjtParser(Parser):
|
|||||||
|
|
||||||
|
|
||||||
class Decoder(ABC):
|
class Decoder(ABC):
|
||||||
locator_pattern = re.compile(".*\\s([A-Z0-9/]{2,})(\\sR)?\\s([A-R]{2}[0-9]{2})$")
|
def __init__(self, profile, messageParser):
|
||||||
|
|
||||||
def __init__(self, profile):
|
|
||||||
self.profile = profile
|
self.profile = profile
|
||||||
|
self.messageParser = messageParser
|
||||||
|
|
||||||
def parse_timestamp(self, instring):
|
def parse_timestamp(self, instring):
|
||||||
dateformat = self.profile.getTimestampFormat()
|
dateformat = self.profile.getTimestampFormat()
|
||||||
@ -215,8 +219,19 @@ class Decoder(ABC):
|
|||||||
def parse(self, msg, dial_freq):
|
def parse(self, msg, dial_freq):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def parseMessage(self, msg):
|
|
||||||
m = Decoder.locator_pattern.match(msg)
|
class MessageParser(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def parse(self, msg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Used in QSO-style modes (FT8, FT4, FST4)
|
||||||
|
class QsoMessageParser(MessageParser):
|
||||||
|
locator_pattern = re.compile(".*\\s([A-Z0-9/]{2,})(\\sR)?\\s([A-R]{2}[0-9]{2})$")
|
||||||
|
|
||||||
|
def parse(self, msg):
|
||||||
|
m = QsoMessageParser.locator_pattern.match(msg)
|
||||||
if m is None:
|
if m is None:
|
||||||
return {}
|
return {}
|
||||||
# this is a valid locator in theory, but it's somewhere in the arctic ocean, near the north pole, so it's very
|
# this is a valid locator in theory, but it's somewhere in the arctic ocean, near the north pole, so it's very
|
||||||
@ -226,6 +241,17 @@ class Decoder(ABC):
|
|||||||
return {"callsign": m.group(1), "locator": m.group(3)}
|
return {"callsign": m.group(1), "locator": m.group(3)}
|
||||||
|
|
||||||
|
|
||||||
|
# Used in propagation reporting / beacon modes (WSPR / FST4W)
|
||||||
|
class BeaconMessageParser(MessageParser):
|
||||||
|
wspr_splitter_pattern = re.compile("([A-Z0-9/]*)\\s([A-R]{2}[0-9]{2})\\s([0-9]+)")
|
||||||
|
|
||||||
|
def parse(self, msg):
|
||||||
|
m = BeaconMessageParser.wspr_splitter_pattern.match(msg)
|
||||||
|
if m is None:
|
||||||
|
return {}
|
||||||
|
return {"callsign": m.group(1), "locator": m.group(2), "dbm": m.group(3)}
|
||||||
|
|
||||||
|
|
||||||
class Jt9Decoder(Decoder):
|
class Jt9Decoder(Decoder):
|
||||||
def parse(self, msg, dial_freq):
|
def parse(self, msg, dial_freq):
|
||||||
# ft8 sample
|
# ft8 sample
|
||||||
@ -245,13 +271,11 @@ class Jt9Decoder(Decoder):
|
|||||||
"freq": dial_freq + int(msg[9:13]),
|
"freq": dial_freq + int(msg[9:13]),
|
||||||
"msg": wsjt_msg,
|
"msg": wsjt_msg,
|
||||||
}
|
}
|
||||||
result.update(self.parseMessage(wsjt_msg))
|
result.update(self.messageParser.parse(wsjt_msg))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class WsprDecoder(Decoder):
|
class WsprDecoder(Decoder):
|
||||||
wspr_splitter_pattern = re.compile("([A-Z0-9/]*)\\s([A-R]{2}[0-9]{2})\\s([0-9]+)")
|
|
||||||
|
|
||||||
def parse(self, msg, dial_freq):
|
def parse(self, msg, dial_freq):
|
||||||
# wspr sample
|
# wspr sample
|
||||||
# '2600 -24 0.4 0.001492 -1 G8AXA JO01 33'
|
# '2600 -24 0.4 0.001492 -1 G8AXA JO01 33'
|
||||||
@ -266,11 +290,5 @@ class WsprDecoder(Decoder):
|
|||||||
"drift": int(msg[20:23]),
|
"drift": int(msg[20:23]),
|
||||||
"msg": wsjt_msg,
|
"msg": wsjt_msg,
|
||||||
}
|
}
|
||||||
result.update(self.parseMessage(wsjt_msg))
|
result.update(self.messageParser.parse(wsjt_msg))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def parseMessage(self, msg):
|
|
||||||
m = WsprDecoder.wspr_splitter_pattern.match(msg)
|
|
||||||
if m is None:
|
|
||||||
return {}
|
|
||||||
return {"callsign": m.group(1), "locator": m.group(2), "dbm": m.group(3)}
|
|
||||||
|
@ -32,6 +32,13 @@ class Worker(threading.Thread):
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Exception while uploading WSPRNet spot")
|
logger.exception("Exception while uploading WSPRNet spot")
|
||||||
|
|
||||||
|
def _getMode(self, spot):
|
||||||
|
interval = round(spot["interval"] / 60)
|
||||||
|
# FST4W modes are mapped not to conflict with WSPR modes 2 and 15:
|
||||||
|
if spot["mode"] != "WSPR" and interval in [2, 15]:
|
||||||
|
return interval + 1
|
||||||
|
return interval
|
||||||
|
|
||||||
def uploadSpot(self, spot):
|
def uploadSpot(self, spot):
|
||||||
# function=wspr&date=210114&time=1732&sig=-15&dt=0.5&drift=0&tqrg=7.040019&tcall=DF2UU&tgrid=JN48&dbm=37&version=2.3.0-rc3&rcall=DD5JFK&rgrid=JN58SC&rqrg=7.040047&mode=2
|
# function=wspr&date=210114&time=1732&sig=-15&dt=0.5&drift=0&tqrg=7.040019&tcall=DF2UU&tgrid=JN48&dbm=37&version=2.3.0-rc3&rcall=DD5JFK&rgrid=JN58SC&rqrg=7.040047&mode=2
|
||||||
# {'timestamp': 1610655960000, 'db': -23.0, 'dt': 0.3, 'freq': 7040048, 'drift': -1, 'msg': 'LA3JJ JO59 37', 'callsign': 'LA3JJ', 'locator': 'JO59', 'mode': 'WSPR'}
|
# {'timestamp': 1610655960000, 'db': -23.0, 'dt': 0.3, 'freq': 7040048, 'drift': -1, 'msg': 'LA3JJ JO59 37', 'callsign': 'LA3JJ', 'locator': 'JO59', 'mode': 'WSPR'}
|
||||||
@ -42,7 +49,8 @@ class Worker(threading.Thread):
|
|||||||
"time": date.strftime("%H%M"),
|
"time": date.strftime("%H%M"),
|
||||||
"sig": spot["db"],
|
"sig": spot["db"],
|
||||||
"dt": spot["dt"],
|
"dt": spot["dt"],
|
||||||
"drift": spot["drift"],
|
# FST4W does not have drift
|
||||||
|
"drift": spot["drift"] if "drift" in spot else 0,
|
||||||
"tqrg": spot["freq"] / 1E6,
|
"tqrg": spot["freq"] / 1E6,
|
||||||
"tcall": spot["callsign"],
|
"tcall": spot["callsign"],
|
||||||
"tgrid": spot["locator"],
|
"tgrid": spot["locator"],
|
||||||
@ -51,8 +59,7 @@ class Worker(threading.Thread):
|
|||||||
"rcall": self.callsign,
|
"rcall": self.callsign,
|
||||||
"rgrid": self.locator,
|
"rgrid": self.locator,
|
||||||
# mode 2 = WSPR 2 minutes
|
# mode 2 = WSPR 2 minutes
|
||||||
# TODO implement FST4W mode codes
|
"mode": self._getMode(spot)
|
||||||
"mode": 2
|
|
||||||
}).encode()
|
}).encode()
|
||||||
request.urlopen("http://wsprnet.org/post/", data)
|
request.urlopen("http://wsprnet.org/post/", data)
|
||||||
|
|
||||||
@ -80,4 +87,4 @@ class WsprnetReporter(Reporter):
|
|||||||
logger.warning("WSPRNet Queue overflow, one spot lost")
|
logger.warning("WSPRNet Queue overflow, one spot lost")
|
||||||
|
|
||||||
def getSupportedModes(self):
|
def getSupportedModes(self):
|
||||||
return ["WSPR"]
|
return ["WSPR", "FST4W"]
|
||||||
|
Loading…
Reference in New Issue
Block a user