diff --git a/owrx/wsjt.py b/owrx/wsjt.py index df3eda9..b0c8c4b 100644 --- a/owrx/wsjt.py +++ b/owrx/wsjt.py @@ -1,6 +1,6 @@ import threading import wave -from datetime import datetime, timedelta, date +from datetime import datetime, timedelta, date, timezone import time import sched import subprocess @@ -31,7 +31,7 @@ class WsjtChopper(threading.Thread): filename = "{tmp_dir}/openwebrx-wsjtchopper-{id}-{timestamp}.wav".format( tmp_dir = self.tmp_dir, id = id(self), - timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") + timestamp = datetime.utcnow().strftime(self.fileTimestampFormat) ) wavefile = wave.open(filename, "wb") wavefile.setnchannels(1) @@ -71,9 +71,9 @@ class WsjtChopper(threading.Thread): self._scheduleNextSwitch() def decoder_commandline(self, file): - ''' + """ must be overridden in child classes - ''' + """ return [] def decode(self): @@ -128,6 +128,7 @@ class WsjtChopper(threading.Thread): class Ft8Chopper(WsjtChopper): def __init__(self, source): self.interval = 15 + self.fileTimestampFormat = "%y%m%d_%H%M%S" super().__init__(source) def decoder_commandline(self, file): @@ -138,6 +139,7 @@ class Ft8Chopper(WsjtChopper): class WsprChopper(WsjtChopper): def __init__(self, source): self.interval = 120 + self.fileTimestampFormat = "%y%m%d_%H%M" super().__init__(source) def decoder_commandline(self, file): @@ -148,6 +150,7 @@ class WsprChopper(WsjtChopper): class Jt65Chopper(WsjtChopper): def __init__(self, source): self.interval = 60 + self.fileTimestampFormat = "%y%m%d_%H%M" super().__init__(source) def decoder_commandline(self, file): @@ -158,6 +161,7 @@ class Jt65Chopper(WsjtChopper): class Jt9Chopper(WsjtChopper): def __init__(self, source): self.interval = 60 + self.fileTimestampFormat = "%y%m%d_%H%M" super().__init__(source) def decoder_commandline(self, file): @@ -167,8 +171,6 @@ class Jt9Chopper(WsjtChopper): class WsjtParser(object): locator_pattern = re.compile(".*\\s([A-Z0-9]+)\\s([A-R]{2}[0-9]{2})$") - jt9_pattern = re.compile("^([0-9]{6}|\\*{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]+)") def __init__(self, handler): @@ -191,38 +193,45 @@ class WsjtParser(object): if msg.startswith(" EOF on input file"): return - out = {} - if WsjtParser.jt9_pattern.match(msg): + modes = list(WsjtParser.modes.keys()) + if msg[21] in modes or msg[19] in modes: out = self.parse_from_jt9(msg) - elif WsjtParser.wspr_pattern.match(msg): + else: out = self.parse_from_wsprd(msg) self.handler.write_wsjt_message(out) except ValueError: logger.exception("error while parsing wsjt message") + def parse_timestamp(self, instring, dateformat): + ts = datetime.strptime(instring, dateformat).replace(tzinfo=timezone.utc) + return int(datetime.combine(date.today(), ts.time(), timezone.utc).timestamp() * 1000) + def parse_from_jt9(self, msg): # ft8 sample # '222100 -15 -0.0 508 ~ CQ EA7MJ IM66' # jt65 sample - # '**** -10 0.4 1556 # CQ RN6AM KN95' - out = {} - if msg.startswith("****"): - out["timestamp"] = int(datetime.now().timestamp() * 1000) - msg = msg[5:] + # '2352 -7 0.4 1801 # R0WAS R2ABM KO85' + # '0003 -4 0.4 1762 # CQ R2ABM KO85' + modes = list(WsjtParser.modes.keys()) + if msg[19] in modes: + dateformat = "%H%M" else: - ts = datetime.strptime(msg[0:6], "%H%M%S") - 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]) + dateformat = "%H%M%S" + timestamp = self.parse_timestamp(msg[0:len(dateformat)], dateformat) + msg = msg[len(dateformat) + 1:] modeChar = msg[14:15] - out["mode"] = mode = WsjtParser.modes[modeChar] if modeChar in WsjtParser.modes else "unknown" + mode = WsjtParser.modes[modeChar] if modeChar in WsjtParser.modes else "unknown" wsjt_msg = msg[17:53].strip() self.parseLocator(wsjt_msg, mode) - out["msg"] = wsjt_msg - return out + return { + "timestamp": timestamp, + "db": float(msg[0:3]), + "dt": float(msg[4:8]), + "freq": int(msg[9:13]), + "mode": mode, + "msg": wsjt_msg + } def parseLocator(self, msg, mode): m = WsjtParser.locator_pattern.match(msg) @@ -237,19 +246,18 @@ class WsjtParser(object): def parse_from_wsprd(self, msg): # wspr sample # '2600 -24 0.4 0.001492 -1 G8AXA JO01 33' - out = {} - now = datetime.now() - ts = datetime.strptime(msg[0:4], "%M%S").replace(hour=now.hour) - out["timestamp"] = int(datetime.combine(date.today(), ts.time(), now.tzinfo).timestamp() * 1000) - out["db"] = float(msg[5:8]) - out["dt"] = float(msg[9:13]) - out["freq"] = float(msg[14:24]) - out["drift"] = int(msg[25:28]) - out["mode"] = "WSPR" + # '0052 -29 2.6 0.001486 0 G02CWT IO92 23' wsjt_msg = msg[29:].strip() - out["msg"] = wsjt_msg self.parseWsprMessage(wsjt_msg) - return out + return { + "timestamp": self.parse_timestamp(msg[0:4], "%H%M"), + "db": float(msg[5:8]), + "dt": float(msg[9:13]), + "freq": float(msg[14:24]), + "drift": int(msg[25:28]), + "mode": "WSPR", + "msg": wsjt_msg + } def parseWsprMessage(self, msg): m = WsjtParser.wspr_splitter_pattern.match(msg)