add FST4 and FST4W modes
This commit is contained in:
parent
71c649b016
commit
ac4401175f
24
csdr/csdr.py
24
csdr/csdr.py
@ -29,7 +29,7 @@ import math
|
||||
from functools import partial
|
||||
|
||||
from owrx.kiss import KissClient, DirewolfConfig
|
||||
from owrx.wsjt import Ft8Profile, WsprProfile, Jt9Profile, Jt65Profile, Ft4Profile
|
||||
from owrx.wsjt import Ft8Profile, WsprProfile, Jt9Profile, Jt65Profile, Ft4Profile, Fst4Profile, Fst4wProfile
|
||||
from owrx.js8 import Js8Profiles
|
||||
from owrx.audio import AudioChopper
|
||||
|
||||
@ -412,19 +412,23 @@ class dsp(object):
|
||||
|
||||
if self.isWsjtMode():
|
||||
smd = self.get_secondary_demodulator()
|
||||
chopper_profile = None
|
||||
chopper_profiles = None
|
||||
if smd == "ft8":
|
||||
chopper_profile = Ft8Profile()
|
||||
chopper_profiles = [Ft8Profile()]
|
||||
elif smd == "wspr":
|
||||
chopper_profile = WsprProfile()
|
||||
chopper_profiles = [WsprProfile()]
|
||||
elif smd == "jt65":
|
||||
chopper_profile = Jt65Profile()
|
||||
chopper_profiles = [Jt65Profile()]
|
||||
elif smd == "jt9":
|
||||
chopper_profile = Jt9Profile()
|
||||
chopper_profiles = [Jt9Profile()]
|
||||
elif smd == "ft4":
|
||||
chopper_profile = Ft4Profile()
|
||||
if chopper_profile is not None:
|
||||
chopper = AudioChopper(self, self.secondary_process_demod.stdout, chopper_profile)
|
||||
chopper_profiles = [Ft4Profile()]
|
||||
elif smd == "fst4":
|
||||
chopper_profiles = Fst4Profile.getEnabledProfiles()
|
||||
elif smd == "fst4w":
|
||||
chopper_profiles = Fst4wProfile.getEnabledProfiles()
|
||||
if chopper_profiles is not None and len(chopper_profiles):
|
||||
chopper = AudioChopper(self, self.secondary_process_demod.stdout, *chopper_profiles)
|
||||
chopper.start()
|
||||
self.output.send_output("wsjt_demod", chopper.read)
|
||||
elif self.isJs8():
|
||||
@ -566,7 +570,7 @@ class dsp(object):
|
||||
def isWsjtMode(self, demodulator=None):
|
||||
if demodulator is None:
|
||||
demodulator = self.get_secondary_demodulator()
|
||||
return demodulator in ["ft8", "wspr", "jt65", "jt9", "ft4"]
|
||||
return demodulator in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w"]
|
||||
|
||||
def isJs8(self, demodulator = None):
|
||||
if demodulator is None:
|
||||
|
@ -74,6 +74,8 @@ class Modes(object):
|
||||
DigitalMode(
|
||||
"wspr", "WSPR", underlying=["usb"], bandpass=Bandpass(1350, 1650), requirements=["wsjt-x"], service=True
|
||||
),
|
||||
DigitalMode("fst4", "FST4", underlying=["usb"], bandpass=Bandpass(0, 3000), requirements=["wsjt-x"], service=True),
|
||||
DigitalMode("fst4w", "FST4W", underlying=["usb"], bandpass=Bandpass(1350, 1650), requirements=["wsjt-x"], service=True),
|
||||
DigitalMode("js8", "JS8Call", underlying=["usb"], bandpass=Bandpass(0, 3000), requirements=["js8call"], service=True),
|
||||
DigitalMode(
|
||||
"packet",
|
||||
|
81
owrx/wsjt.py
81
owrx/wsjt.py
@ -85,8 +85,52 @@ class Ft4Profile(WsjtProfile):
|
||||
return ["jt9", "--ft4", "-d", str(self.decoding_depth("ft4")), file]
|
||||
|
||||
|
||||
class Fst4Profile(WsjtProfile):
|
||||
availableIntervals = [15, 30, 60, 120, 300, 900, 1800]
|
||||
|
||||
def __init__(self, interval):
|
||||
self.interval = interval
|
||||
|
||||
def getInterval(self):
|
||||
return self.interval
|
||||
|
||||
def getFileTimestampFormat(self):
|
||||
return "%y%m%d_%H%M%S"
|
||||
|
||||
def decoder_commandline(self, file):
|
||||
return ["jt9", "--fst4", "-b", "FST4-{0}".format(self.interval), "-d", str(self.decoding_depth("fst4")), file]
|
||||
|
||||
@staticmethod
|
||||
def getEnabledProfiles():
|
||||
config = Config.get()
|
||||
profiles = config["fst4_enabled_intervals"] if "fst4_enabled_intervals" in config else []
|
||||
return [Fst4Profile(i) for i in profiles if i in Fst4Profile.availableIntervals]
|
||||
|
||||
|
||||
class Fst4wProfile(WsjtProfile):
|
||||
availableIntervals = [120, 300, 900, 1800]
|
||||
|
||||
def __init__(self, interval):
|
||||
self.interval = interval
|
||||
|
||||
def getInterval(self):
|
||||
return self.interval
|
||||
|
||||
def getFileTimestampFormat(self):
|
||||
return "%y%m%d_%H%M%S"
|
||||
|
||||
def decoder_commandline(self, file):
|
||||
return ["jt9", "--fst4w", "-b", "FST4W-{0}".format(self.interval), "-d", str(self.decoding_depth("fst4w")), file]
|
||||
|
||||
@staticmethod
|
||||
def getEnabledProfiles():
|
||||
config = Config.get()
|
||||
profiles = config["fst4w_enabled_intervals"] if "fst4w_enabled_intervals" in config else []
|
||||
return [Fst4Profile(i) for i in profiles if i in Fst4Profile.availableIntervals]
|
||||
|
||||
|
||||
class WsjtParser(Parser):
|
||||
modes = {"~": "FT8", "#": "JT65", "@": "JT9", "+": "FT4"}
|
||||
modes = {"~": "FT8", "#": "JT65", "@": "JT9", "+": "FT4", "`": "FST4"}
|
||||
|
||||
def parse(self, messages):
|
||||
for data in messages:
|
||||
@ -115,7 +159,7 @@ class WsjtParser(Parser):
|
||||
PskReporter.getSharedInstance().spot(out)
|
||||
|
||||
self.handler.write_wsjt_message(out)
|
||||
except ValueError:
|
||||
except (ValueError, IndexError):
|
||||
logger.exception("error while parsing wsjt message")
|
||||
|
||||
def pushDecode(self, mode):
|
||||
@ -139,6 +183,8 @@ class WsjtParser(Parser):
|
||||
|
||||
|
||||
class Decoder(ABC):
|
||||
locator_pattern = re.compile(".*\\s([A-Z0-9]+)\\s([A-R]{2}[0-9]{2})$")
|
||||
|
||||
def parse_timestamp(self, instring, dateformat):
|
||||
ts = datetime.strptime(instring, dateformat)
|
||||
return int(
|
||||
@ -149,23 +195,36 @@ class Decoder(ABC):
|
||||
def parse(self, msg, dial_freq):
|
||||
pass
|
||||
|
||||
def parseMessage(self, msg):
|
||||
m = Decoder.locator_pattern.match(msg)
|
||||
if m is None:
|
||||
return {}
|
||||
# this is a valid locator in theory, but it's somewhere in the arctic ocean, near the north pole, so it's very
|
||||
# likely this just means roger roger goodbye.
|
||||
if m.group(2) == "RR73":
|
||||
return {"callsign": m.group(1)}
|
||||
return {"callsign": m.group(1), "locator": m.group(2)}
|
||||
|
||||
|
||||
class Jt9Decoder(Decoder):
|
||||
locator_pattern = re.compile(".*\\s([A-Z0-9]+)\\s([A-R]{2}[0-9]{2})$")
|
||||
|
||||
def parse(self, msg, dial_freq):
|
||||
# ft8 sample
|
||||
# '222100 -15 -0.0 508 ~ CQ EA7MJ IM66'
|
||||
# jt65 sample
|
||||
# '2352 -7 0.4 1801 # R0WAS R2ABM KO85'
|
||||
# '0003 -4 0.4 1762 # CQ R2ABM KO85'
|
||||
# fst4 sample
|
||||
# '**** -23 0.6 3023 ` <...> <...> R 591631 BI53PV'
|
||||
modes = list(WsjtParser.modes.keys())
|
||||
if msg[19] in modes:
|
||||
dateformat = "%H%M"
|
||||
else:
|
||||
dateformat = "%H%M%S"
|
||||
timestamp = self.parse_timestamp(msg[0 : len(dateformat)], dateformat)
|
||||
msg = msg[len(dateformat) + 1 :]
|
||||
try:
|
||||
timestamp = self.parse_timestamp(msg[0 : len(dateformat)], dateformat)
|
||||
except ValueError:
|
||||
timestamp = None
|
||||
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()
|
||||
@ -181,16 +240,6 @@ class Jt9Decoder(Decoder):
|
||||
result.update(self.parseMessage(wsjt_msg))
|
||||
return result
|
||||
|
||||
def parseMessage(self, msg):
|
||||
m = Jt9Decoder.locator_pattern.match(msg)
|
||||
if m is None:
|
||||
return {}
|
||||
# this is a valid locator in theory, but it's somewhere in the arctic ocean, near the north pole, so it's very
|
||||
# likely this just means roger roger goodbye.
|
||||
if m.group(2) == "RR73":
|
||||
return {"callsign": m.group(1)}
|
||||
return {"callsign": m.group(1), "locator": m.group(2)}
|
||||
|
||||
|
||||
class WsprDecoder(Decoder):
|
||||
wspr_splitter_pattern = re.compile("([A-Z0-9]*)\\s([A-R]{2}[0-9]{2})\\s([0-9]+)")
|
||||
|
Loading…
Reference in New Issue
Block a user