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 functools import partial
|
||||||
|
|
||||||
from owrx.kiss import KissClient, DirewolfConfig
|
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.js8 import Js8Profiles
|
||||||
from owrx.audio import AudioChopper
|
from owrx.audio import AudioChopper
|
||||||
|
|
||||||
@ -412,19 +412,23 @@ class dsp(object):
|
|||||||
|
|
||||||
if self.isWsjtMode():
|
if self.isWsjtMode():
|
||||||
smd = self.get_secondary_demodulator()
|
smd = self.get_secondary_demodulator()
|
||||||
chopper_profile = None
|
chopper_profiles = None
|
||||||
if smd == "ft8":
|
if smd == "ft8":
|
||||||
chopper_profile = Ft8Profile()
|
chopper_profiles = [Ft8Profile()]
|
||||||
elif smd == "wspr":
|
elif smd == "wspr":
|
||||||
chopper_profile = WsprProfile()
|
chopper_profiles = [WsprProfile()]
|
||||||
elif smd == "jt65":
|
elif smd == "jt65":
|
||||||
chopper_profile = Jt65Profile()
|
chopper_profiles = [Jt65Profile()]
|
||||||
elif smd == "jt9":
|
elif smd == "jt9":
|
||||||
chopper_profile = Jt9Profile()
|
chopper_profiles = [Jt9Profile()]
|
||||||
elif smd == "ft4":
|
elif smd == "ft4":
|
||||||
chopper_profile = Ft4Profile()
|
chopper_profiles = [Ft4Profile()]
|
||||||
if chopper_profile is not None:
|
elif smd == "fst4":
|
||||||
chopper = AudioChopper(self, self.secondary_process_demod.stdout, chopper_profile)
|
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()
|
chopper.start()
|
||||||
self.output.send_output("wsjt_demod", chopper.read)
|
self.output.send_output("wsjt_demod", chopper.read)
|
||||||
elif self.isJs8():
|
elif self.isJs8():
|
||||||
@ -566,7 +570,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", "jt65", "jt9", "ft4"]
|
return demodulator in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w"]
|
||||||
|
|
||||||
def isJs8(self, demodulator = None):
|
def isJs8(self, demodulator = None):
|
||||||
if demodulator is None:
|
if demodulator is None:
|
||||||
|
@ -74,6 +74,8 @@ class Modes(object):
|
|||||||
DigitalMode(
|
DigitalMode(
|
||||||
"wspr", "WSPR", underlying=["usb"], bandpass=Bandpass(1350, 1650), requirements=["wsjt-x"], service=True
|
"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("js8", "JS8Call", underlying=["usb"], bandpass=Bandpass(0, 3000), requirements=["js8call"], service=True),
|
||||||
DigitalMode(
|
DigitalMode(
|
||||||
"packet",
|
"packet",
|
||||||
|
79
owrx/wsjt.py
79
owrx/wsjt.py
@ -85,8 +85,52 @@ class Ft4Profile(WsjtProfile):
|
|||||||
return ["jt9", "--ft4", "-d", str(self.decoding_depth("ft4")), file]
|
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):
|
class WsjtParser(Parser):
|
||||||
modes = {"~": "FT8", "#": "JT65", "@": "JT9", "+": "FT4"}
|
modes = {"~": "FT8", "#": "JT65", "@": "JT9", "+": "FT4", "`": "FST4"}
|
||||||
|
|
||||||
def parse(self, messages):
|
def parse(self, messages):
|
||||||
for data in messages:
|
for data in messages:
|
||||||
@ -115,7 +159,7 @@ class WsjtParser(Parser):
|
|||||||
PskReporter.getSharedInstance().spot(out)
|
PskReporter.getSharedInstance().spot(out)
|
||||||
|
|
||||||
self.handler.write_wsjt_message(out)
|
self.handler.write_wsjt_message(out)
|
||||||
except ValueError:
|
except (ValueError, IndexError):
|
||||||
logger.exception("error while parsing wsjt message")
|
logger.exception("error while parsing wsjt message")
|
||||||
|
|
||||||
def pushDecode(self, mode):
|
def pushDecode(self, mode):
|
||||||
@ -139,6 +183,8 @@ class WsjtParser(Parser):
|
|||||||
|
|
||||||
|
|
||||||
class Decoder(ABC):
|
class Decoder(ABC):
|
||||||
|
locator_pattern = re.compile(".*\\s([A-Z0-9]+)\\s([A-R]{2}[0-9]{2})$")
|
||||||
|
|
||||||
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(
|
||||||
@ -149,23 +195,36 @@ 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)
|
||||||
|
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):
|
class Jt9Decoder(Decoder):
|
||||||
locator_pattern = re.compile(".*\\s([A-Z0-9]+)\\s([A-R]{2}[0-9]{2})$")
|
|
||||||
|
|
||||||
def parse(self, msg, dial_freq):
|
def parse(self, msg, dial_freq):
|
||||||
# ft8 sample
|
# ft8 sample
|
||||||
# '222100 -15 -0.0 508 ~ CQ EA7MJ IM66'
|
# '222100 -15 -0.0 508 ~ CQ EA7MJ IM66'
|
||||||
# jt65 sample
|
# jt65 sample
|
||||||
# '2352 -7 0.4 1801 # R0WAS R2ABM KO85'
|
# '2352 -7 0.4 1801 # R0WAS R2ABM KO85'
|
||||||
# '0003 -4 0.4 1762 # CQ R2ABM KO85'
|
# '0003 -4 0.4 1762 # CQ R2ABM KO85'
|
||||||
|
# fst4 sample
|
||||||
|
# '**** -23 0.6 3023 ` <...> <...> R 591631 BI53PV'
|
||||||
modes = list(WsjtParser.modes.keys())
|
modes = list(WsjtParser.modes.keys())
|
||||||
if msg[19] in modes:
|
if msg[19] in modes:
|
||||||
dateformat = "%H%M"
|
dateformat = "%H%M"
|
||||||
else:
|
else:
|
||||||
dateformat = "%H%M%S"
|
dateformat = "%H%M%S"
|
||||||
|
try:
|
||||||
timestamp = self.parse_timestamp(msg[0 : len(dateformat)], dateformat)
|
timestamp = self.parse_timestamp(msg[0 : len(dateformat)], dateformat)
|
||||||
msg = msg[len(dateformat) + 1 :]
|
except ValueError:
|
||||||
|
timestamp = None
|
||||||
|
msg = msg[len(dateformat) + 1:]
|
||||||
modeChar = msg[14:15]
|
modeChar = msg[14:15]
|
||||||
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()
|
wsjt_msg = msg[17:53].strip()
|
||||||
@ -181,16 +240,6 @@ class Jt9Decoder(Decoder):
|
|||||||
result.update(self.parseMessage(wsjt_msg))
|
result.update(self.parseMessage(wsjt_msg))
|
||||||
return result
|
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):
|
class WsprDecoder(Decoder):
|
||||||
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]+)")
|
||||||
|
Loading…
Reference in New Issue
Block a user