use profiles instead of parsing to detect mode
This commit is contained in:
		| @@ -186,7 +186,7 @@ class AudioWriter(object): | |||||||
|         ) |         ) | ||||||
|         try: |         try: | ||||||
|             for line in decoder.stdout: |             for line in decoder.stdout: | ||||||
|                 self.outputWriter.send((job.freq, line)) |                 self.outputWriter.send((self.profile, job.freq, line)) | ||||||
|         except OSError: |         except OSError: | ||||||
|             decoder.stdout.flush() |             decoder.stdout.flush() | ||||||
|             # TODO uncouple parsing from the output so that decodes can still go to the map and the spotters |             # TODO uncouple parsing from the output so that decodes can still go to the map and the spotters | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ class Js8Profiles(object): | |||||||
|  |  | ||||||
|  |  | ||||||
| class Js8Profile(AudioChopperProfile, metaclass=ABCMeta): | class Js8Profile(AudioChopperProfile, metaclass=ABCMeta): | ||||||
|     def decoding_depth(self, mode): |     def decoding_depth(self): | ||||||
|         pm = Config.get() |         pm = Config.get() | ||||||
|         # return global default |         # return global default | ||||||
|         if "js8_decoding_depth" in pm: |         if "js8_decoding_depth" in pm: | ||||||
| @@ -40,7 +40,7 @@ class Js8Profile(AudioChopperProfile, metaclass=ABCMeta): | |||||||
|         return "%y%m%d_%H%M%S" |         return "%y%m%d_%H%M%S" | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         return ["js8", "--js8", "-b", self.get_sub_mode(), "-d", str(self.decoding_depth("js8")), file] |         return ["js8", "--js8", "-b", self.get_sub_mode(), "-d", str(self.decoding_depth()), file] | ||||||
|  |  | ||||||
|     @abstractmethod |     @abstractmethod | ||||||
|     def get_sub_mode(self): |     def get_sub_mode(self): | ||||||
| @@ -85,7 +85,7 @@ class Js8Parser(Parser): | |||||||
|     def parse(self, messages): |     def parse(self, messages): | ||||||
|         for raw in messages: |         for raw in messages: | ||||||
|             try: |             try: | ||||||
|                 freq, raw_msg = raw |                 profile, freq, raw_msg = raw | ||||||
|                 self.setDialFrequency(freq) |                 self.setDialFrequency(freq) | ||||||
|                 msg = raw_msg.decode().rstrip() |                 msg = raw_msg.decode().rstrip() | ||||||
|                 if Js8Parser.decoderRegex.match(msg): |                 if Js8Parser.decoderRegex.match(msg): | ||||||
|   | |||||||
							
								
								
									
										107
									
								
								owrx/wsjt.py
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								owrx/wsjt.py
									
									
									
									
									
								
							| @@ -14,8 +14,9 @@ logger = logging.getLogger(__name__) | |||||||
|  |  | ||||||
|  |  | ||||||
| class WsjtProfile(AudioChopperProfile, metaclass=ABCMeta): | class WsjtProfile(AudioChopperProfile, metaclass=ABCMeta): | ||||||
|     def decoding_depth(self, mode): |     def decoding_depth(self): | ||||||
|         pm = Config.get() |         pm = Config.get() | ||||||
|  |         mode = self.getMode().lower() | ||||||
|         # mode-specific setting? |         # mode-specific setting? | ||||||
|         if "wsjt_decoding_depths" in pm and mode in pm["wsjt_decoding_depths"]: |         if "wsjt_decoding_depths" in pm and mode in pm["wsjt_decoding_depths"]: | ||||||
|             return pm["wsjt_decoding_depths"][mode] |             return pm["wsjt_decoding_depths"][mode] | ||||||
| @@ -25,64 +26,76 @@ class WsjtProfile(AudioChopperProfile, metaclass=ABCMeta): | |||||||
|         # default when no setting is provided |         # default when no setting is provided | ||||||
|         return 3 |         return 3 | ||||||
|  |  | ||||||
|  |     def getTimestampFormat(self): | ||||||
|  |         if self.getInterval() < 60: | ||||||
|  |             return "%H%M%S" | ||||||
|  |         return "%H%M" | ||||||
|  |  | ||||||
|  |     def getFileTimestampFormat(self): | ||||||
|  |         return "%y%m%d_" + self.getTimestampFormat() | ||||||
|  |  | ||||||
|  |     @abstractmethod | ||||||
|  |     def getMode(self): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |  | ||||||
| class Ft8Profile(WsjtProfile): | class Ft8Profile(WsjtProfile): | ||||||
|     def getInterval(self): |     def getInterval(self): | ||||||
|         return 15 |         return 15 | ||||||
|  |  | ||||||
|     def getFileTimestampFormat(self): |  | ||||||
|         return "%y%m%d_%H%M%S" |  | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         return ["jt9", "--ft8", "-d", str(self.decoding_depth("ft8")), file] |         return ["jt9", "--ft8", "-d", str(self.decoding_depth()), file] | ||||||
|  |  | ||||||
|  |     def getMode(self): | ||||||
|  |         return "FT8" | ||||||
|  |  | ||||||
|  |  | ||||||
| class WsprProfile(WsjtProfile): | class WsprProfile(WsjtProfile): | ||||||
|     def getInterval(self): |     def getInterval(self): | ||||||
|         return 120 |         return 120 | ||||||
|  |  | ||||||
|     def getFileTimestampFormat(self): |  | ||||||
|         return "%y%m%d_%H%M" |  | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         cmd = ["wsprd"] |         cmd = ["wsprd"] | ||||||
|         if self.decoding_depth("wspr") > 1: |         if self.decoding_depth() > 1: | ||||||
|             cmd += ["-d"] |             cmd += ["-d"] | ||||||
|         cmd += [file] |         cmd += [file] | ||||||
|         return cmd |         return cmd | ||||||
|  |  | ||||||
|  |     def getMode(self): | ||||||
|  |         return "WSPR" | ||||||
|  |  | ||||||
|  |  | ||||||
| class Jt65Profile(WsjtProfile): | class Jt65Profile(WsjtProfile): | ||||||
|     def getInterval(self): |     def getInterval(self): | ||||||
|         return 60 |         return 60 | ||||||
|  |  | ||||||
|     def getFileTimestampFormat(self): |  | ||||||
|         return "%y%m%d_%H%M" |  | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         return ["jt9", "--jt65", "-d", str(self.decoding_depth("jt65")), file] |         return ["jt9", "--jt65", "-d", str(self.decoding_depth()), file] | ||||||
|  |  | ||||||
|  |     def getMode(self): | ||||||
|  |         return "JT65" | ||||||
|  |  | ||||||
|  |  | ||||||
| class Jt9Profile(WsjtProfile): | class Jt9Profile(WsjtProfile): | ||||||
|     def getInterval(self): |     def getInterval(self): | ||||||
|         return 60 |         return 60 | ||||||
|  |  | ||||||
|     def getFileTimestampFormat(self): |  | ||||||
|         return "%y%m%d_%H%M" |  | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         return ["jt9", "--jt9", "-d", str(self.decoding_depth("jt9")), file] |         return ["jt9", "--jt9", "-d", str(self.decoding_depth()), file] | ||||||
|  |  | ||||||
|  |     def getMode(self): | ||||||
|  |         return "JT9" | ||||||
|  |  | ||||||
|  |  | ||||||
| class Ft4Profile(WsjtProfile): | class Ft4Profile(WsjtProfile): | ||||||
|     def getInterval(self): |     def getInterval(self): | ||||||
|         return 7.5 |         return 7.5 | ||||||
|  |  | ||||||
|     def getFileTimestampFormat(self): |  | ||||||
|         return "%y%m%d_%H%M%S" |  | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         return ["jt9", "--ft4", "-d", str(self.decoding_depth("ft4")), file] |         return ["jt9", "--ft4", "-d", str(self.decoding_depth()), file] | ||||||
|  |  | ||||||
|  |     def getMode(self): | ||||||
|  |         return "FT4" | ||||||
|  |  | ||||||
|  |  | ||||||
| class Fst4Profile(WsjtProfile): | class Fst4Profile(WsjtProfile): | ||||||
| @@ -94,13 +107,11 @@ class Fst4Profile(WsjtProfile): | |||||||
|     def getInterval(self): |     def getInterval(self): | ||||||
|         return self.interval |         return self.interval | ||||||
|  |  | ||||||
|     def getFileTimestampFormat(self): |  | ||||||
|         if self.interval < 60: |  | ||||||
|             return "%y%m%d_%H%M%S" |  | ||||||
|         return "%y%m%d_%H%M" |  | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         return ["jt9", "--fst4", "-p", str(self.interval), "-d", str(self.decoding_depth("fst4")), file] |         return ["jt9", "--fst4", "-p", str(self.interval), "-d", str(self.decoding_depth()), file] | ||||||
|  |  | ||||||
|  |     def getMode(self): | ||||||
|  |         return "FST4" | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def getEnabledProfiles(): |     def getEnabledProfiles(): | ||||||
| @@ -118,13 +129,11 @@ class Fst4wProfile(WsjtProfile): | |||||||
|     def getInterval(self): |     def getInterval(self): | ||||||
|         return self.interval |         return self.interval | ||||||
|  |  | ||||||
|     def getFileTimestampFormat(self): |  | ||||||
|         if self.interval < 60: |  | ||||||
|             return "%y%m%d_%H%M%S" |  | ||||||
|         return "%y%m%d_%H%M" |  | ||||||
|  |  | ||||||
|     def decoder_commandline(self, file): |     def decoder_commandline(self, file): | ||||||
|         return ["jt9", "--fst4w", "-p", str(self.interval), "-d", str(self.decoding_depth("fst4w")), file] |         return ["jt9", "--fst4w", "-p", str(self.interval), "-d", str(self.decoding_depth()), file] | ||||||
|  |  | ||||||
|  |     def getMode(self): | ||||||
|  |         return "FST4W" | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def getEnabledProfiles(): |     def getEnabledProfiles(): | ||||||
| @@ -134,12 +143,10 @@ class Fst4wProfile(WsjtProfile): | |||||||
|  |  | ||||||
|  |  | ||||||
| class WsjtParser(Parser): | class WsjtParser(Parser): | ||||||
|     modes = {"~": "FT8", "#": "JT65", "@": "JT9", "+": "FT4", "`": "FST4"} |  | ||||||
|  |  | ||||||
|     def parse(self, messages): |     def parse(self, messages): | ||||||
|         for data in messages: |         for data in messages: | ||||||
|             try: |             try: | ||||||
|                 freq, raw_msg = data |                 profile, freq, raw_msg = data | ||||||
|                 self.setDialFrequency(freq) |                 self.setDialFrequency(freq) | ||||||
|                 msg = raw_msg.decode().rstrip() |                 msg = raw_msg.decode().rstrip() | ||||||
|                 # known debug messages we know to skip |                 # known debug messages we know to skip | ||||||
| @@ -148,17 +155,18 @@ class WsjtParser(Parser): | |||||||
|                 if msg.startswith(" EOF on input file"): |                 if msg.startswith(" EOF on input file"): | ||||||
|                     return |                     return | ||||||
|  |  | ||||||
|                 modes = list(WsjtParser.modes.keys()) |                 mode = profile.getMode() | ||||||
|                 if msg[21] in modes or msg[19] in modes: |                 if mode == "WSPR": | ||||||
|                     decoder = Jt9Decoder() |                     decoder = WsprDecoder(profile) | ||||||
|                 else: |                 else: | ||||||
|                     decoder = WsprDecoder() |                     decoder = Jt9Decoder(profile) | ||||||
|                 out = decoder.parse(msg, freq) |                 out = decoder.parse(msg, freq) | ||||||
|                 if "mode" in out: |                 out["mode"] = mode | ||||||
|                     self.pushDecode(out["mode"]) |  | ||||||
|  |                 self.pushDecode(mode) | ||||||
|                 if "callsign" in out and "locator" in out: |                 if "callsign" in out and "locator" in out: | ||||||
|                     Map.getSharedInstance().updateLocation( |                     Map.getSharedInstance().updateLocation( | ||||||
|                             out["callsign"], LocatorLocation(out["locator"]), out["mode"], self.band |                         out["callsign"], LocatorLocation(out["locator"]), mode, self.band | ||||||
|                     ) |                     ) | ||||||
|                     PskReporter.getSharedInstance().spot(out) |                     PskReporter.getSharedInstance().spot(out) | ||||||
|  |  | ||||||
| @@ -189,6 +197,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})$") |     locator_pattern = re.compile(".*\\s([A-Z0-9]{2,})(\\sR)?\\s([A-R]{2}[0-9]{2})$") | ||||||
|  |  | ||||||
|  |     def __init__(self, profile): | ||||||
|  |         self.profile = profile | ||||||
|  |  | ||||||
|     def parse_timestamp(self, instring, dateformat): |     def parse_timestamp(self, instring, dateformat): | ||||||
|         ts = datetime.strptime(instring, dateformat) |         ts = datetime.strptime(instring, dateformat) | ||||||
|         return int( |         return int( | ||||||
| @@ -219,18 +230,12 @@ class Jt9Decoder(Decoder): | |||||||
|         # '0003  -4  0.4 1762 #  CQ R2ABM KO85' |         # '0003  -4  0.4 1762 #  CQ R2ABM KO85' | ||||||
|         # fst4 sample |         # fst4 sample | ||||||
|         # '**** -23  0.6 3023 `  <...> <...> R 591631 BI53PV' |         # '**** -23  0.6 3023 `  <...> <...> R 591631 BI53PV' | ||||||
|         modes = list(WsjtParser.modes.keys()) |         dateformat = self.profile.getTimestampFormat() | ||||||
|         if msg[19] in modes: |  | ||||||
|             dateformat = "%H%M" |  | ||||||
|         else: |  | ||||||
|             dateformat = "%H%M%S" |  | ||||||
|         try: |         try: | ||||||
|             timestamp = self.parse_timestamp(msg[0:len(dateformat)], dateformat) |             timestamp = self.parse_timestamp(msg[0:len(dateformat)], dateformat) | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             timestamp = None |             timestamp = None | ||||||
|         msg = msg[len(dateformat) + 1:] |         msg = msg[len(dateformat) + 1:] | ||||||
|         modeChar = msg[14:15] |  | ||||||
|         mode = WsjtParser.modes[modeChar] if modeChar in WsjtParser.modes else "unknown" |  | ||||||
|         wsjt_msg = msg[17:53].strip() |         wsjt_msg = msg[17:53].strip() | ||||||
|  |  | ||||||
|         result = { |         result = { | ||||||
| @@ -238,7 +243,6 @@ class Jt9Decoder(Decoder): | |||||||
|             "db": float(msg[0:3]), |             "db": float(msg[0:3]), | ||||||
|             "dt": float(msg[4:8]), |             "dt": float(msg[4:8]), | ||||||
|             "freq": dial_freq + int(msg[9:13]), |             "freq": dial_freq + int(msg[9:13]), | ||||||
|             "mode": mode, |  | ||||||
|             "msg": wsjt_msg, |             "msg": wsjt_msg, | ||||||
|         } |         } | ||||||
|         result.update(self.parseMessage(wsjt_msg)) |         result.update(self.parseMessage(wsjt_msg)) | ||||||
| @@ -259,7 +263,6 @@ class WsprDecoder(Decoder): | |||||||
|             "dt": float(msg[9:13]), |             "dt": float(msg[9:13]), | ||||||
|             "freq": dial_freq + int(float(msg[14:24]) * 1e6), |             "freq": dial_freq + int(float(msg[14:24]) * 1e6), | ||||||
|             "drift": int(msg[25:28]), |             "drift": int(msg[25:28]), | ||||||
|             "mode": "WSPR", |  | ||||||
|             "msg": wsjt_msg, |             "msg": wsjt_msg, | ||||||
|         } |         } | ||||||
|         result.update(self.parseMessage(wsjt_msg)) |         result.update(self.parseMessage(wsjt_msg)) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jakob Ketterl
					Jakob Ketterl