use a single connection to avoid the managing overhead
This commit is contained in:
parent
cb3cb50cbd
commit
4993a56235
@ -22,8 +22,7 @@ class AudioChopper(threading.Thread, Output, ProfileSourceSubscriber):
|
|||||||
if mode is None or not isinstance(mode, AudioChopperMode):
|
if mode is None or not isinstance(mode, AudioChopperMode):
|
||||||
raise ValueError("Mode {} is not an audio chopper mode".format(mode_str))
|
raise ValueError("Mode {} is not an audio chopper mode".format(mode_str))
|
||||||
self.profile_source = mode.get_profile_source()
|
self.profile_source = mode.get_profile_source()
|
||||||
self.writersChangedOut = None
|
(self.outputReader, self.outputWriter) = Pipe()
|
||||||
self.writersChangedIn = None
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def stop_writers(self):
|
def stop_writers(self):
|
||||||
@ -34,11 +33,10 @@ class AudioChopper(threading.Thread, Output, ProfileSourceSubscriber):
|
|||||||
self.stop_writers()
|
self.stop_writers()
|
||||||
sorted_profiles = sorted(self.profile_source.getProfiles(), key=lambda p: p.getInterval())
|
sorted_profiles = sorted(self.profile_source.getProfiles(), key=lambda p: p.getInterval())
|
||||||
groups = {interval: list(group) for interval, group in groupby(sorted_profiles, key=lambda p: p.getInterval())}
|
groups = {interval: list(group) for interval, group in groupby(sorted_profiles, key=lambda p: p.getInterval())}
|
||||||
writers = [AudioWriter(self.dsp, interval, profiles) for interval, profiles in groups.items()]
|
writers = [AudioWriter(self.dsp, self.outputWriter, interval, profiles) for interval, profiles in groups.items()]
|
||||||
for w in writers:
|
for w in writers:
|
||||||
w.start()
|
w.start()
|
||||||
self.writers = writers
|
self.writers = writers
|
||||||
self.writersChangedOut.send(None)
|
|
||||||
|
|
||||||
def supports_type(self, t):
|
def supports_type(self, t):
|
||||||
return t == "audio"
|
return t == "audio"
|
||||||
@ -49,7 +47,6 @@ class AudioChopper(threading.Thread, Output, ProfileSourceSubscriber):
|
|||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
logger.debug("Audio chopper starting up")
|
logger.debug("Audio chopper starting up")
|
||||||
self.writersChangedIn, self.writersChangedOut = Pipe()
|
|
||||||
self.setup_writers()
|
self.setup_writers()
|
||||||
self.profile_source.subscribe(self)
|
self.profile_source.subscribe(self)
|
||||||
while self.doRun:
|
while self.doRun:
|
||||||
@ -67,20 +64,25 @@ class AudioChopper(threading.Thread, Output, ProfileSourceSubscriber):
|
|||||||
logger.debug("Audio chopper shutting down")
|
logger.debug("Audio chopper shutting down")
|
||||||
self.profile_source.unsubscribe(self)
|
self.profile_source.unsubscribe(self)
|
||||||
self.stop_writers()
|
self.stop_writers()
|
||||||
self.writersChangedOut.close()
|
self.outputWriter.close()
|
||||||
self.writersChangedIn.close()
|
self.outputWriter = None
|
||||||
|
|
||||||
|
# drain messages left in the queue so that the queue can be successfully closed
|
||||||
|
# this is necessary since python keeps the file descriptors open otherwise
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
self.outputReader.recv()
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
|
self.outputReader.close()
|
||||||
|
self.outputReader = None
|
||||||
|
|
||||||
def onProfilesChanged(self):
|
def onProfilesChanged(self):
|
||||||
logger.debug("profile change received, resetting writers...")
|
logger.debug("profile change received, resetting writers...")
|
||||||
self.setup_writers()
|
self.setup_writers()
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
while True:
|
try:
|
||||||
try:
|
return self.outputReader.recv()
|
||||||
readers = wait([w.outputReader for w in self.writers] + [self.writersChangedIn])
|
except (EOFError, OSError):
|
||||||
received = [(r, r.recv()) for r in readers]
|
return None
|
||||||
data = [d for r, d in received if r is not self.writersChangedIn]
|
|
||||||
if data:
|
|
||||||
return data
|
|
||||||
except (EOFError, OSError):
|
|
||||||
return None
|
|
||||||
|
@ -38,14 +38,14 @@ class WaveFile(object):
|
|||||||
|
|
||||||
|
|
||||||
class AudioWriter(object):
|
class AudioWriter(object):
|
||||||
def __init__(self, active_dsp, interval, profiles: List[AudioChopperProfile]):
|
def __init__(self, active_dsp, outputWriter, interval, profiles: List[AudioChopperProfile]):
|
||||||
self.dsp = active_dsp
|
self.dsp = active_dsp
|
||||||
|
self.outputWriter = outputWriter
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
self.profiles = profiles
|
self.profiles = profiles
|
||||||
self.wavefile = None
|
self.wavefile = None
|
||||||
self.switchingLock = threading.Lock()
|
self.switchingLock = threading.Lock()
|
||||||
self.timer = None
|
self.timer = None
|
||||||
(self.outputReader, self.outputWriter) = Pipe()
|
|
||||||
|
|
||||||
def getWaveFile(self):
|
def getWaveFile(self):
|
||||||
tmp_dir = CoreConfig().get_temporary_directory()
|
tmp_dir = CoreConfig().get_temporary_directory()
|
||||||
@ -114,19 +114,6 @@ class AudioWriter(object):
|
|||||||
self.wavefile.writeframes(data)
|
self.wavefile.writeframes(data)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.outputWriter.close()
|
|
||||||
self.outputWriter = None
|
|
||||||
|
|
||||||
# drain messages left in the queue so that the queue can be successfully closed
|
|
||||||
# this is necessary since python keeps the file descriptors open otherwise
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
self.outputReader.recv()
|
|
||||||
except EOFError:
|
|
||||||
pass
|
|
||||||
self.outputReader.close()
|
|
||||||
self.outputReader = None
|
|
||||||
|
|
||||||
self.cancelTimer()
|
self.cancelTimer()
|
||||||
try:
|
try:
|
||||||
self.wavefile.close()
|
self.wavefile.close()
|
||||||
|
59
owrx/js8.py
59
owrx/js8.py
@ -84,40 +84,39 @@ class Js8TurboProfile(Js8Profile):
|
|||||||
class Js8Parser(Parser):
|
class Js8Parser(Parser):
|
||||||
decoderRegex = re.compile(" ?<Decode(Started|Debug|Finished)>")
|
decoderRegex = re.compile(" ?<Decode(Started|Debug|Finished)>")
|
||||||
|
|
||||||
def parse(self, messages):
|
def parse(self, raw):
|
||||||
for raw in messages:
|
try:
|
||||||
try:
|
profile, 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):
|
return
|
||||||
return
|
if msg.startswith(" EOF on input file"):
|
||||||
if msg.startswith(" EOF on input file"):
|
return
|
||||||
return
|
|
||||||
|
|
||||||
frame = Js8().parse_message(msg)
|
frame = Js8().parse_message(msg)
|
||||||
self.handler.write_js8_message(frame, self.dial_freq)
|
self.handler.write_js8_message(frame, self.dial_freq)
|
||||||
|
|
||||||
self.pushDecode()
|
self.pushDecode()
|
||||||
|
|
||||||
if (isinstance(frame, Js8FrameHeartbeat) or isinstance(frame, Js8FrameCompound)) and frame.grid:
|
if (isinstance(frame, Js8FrameHeartbeat) or isinstance(frame, Js8FrameCompound)) and frame.grid:
|
||||||
Map.getSharedInstance().updateLocation(
|
Map.getSharedInstance().updateLocation(
|
||||||
frame.callsign, LocatorLocation(frame.grid), "JS8", self.band
|
frame.callsign, LocatorLocation(frame.grid), "JS8", self.band
|
||||||
)
|
)
|
||||||
ReportingEngine.getSharedInstance().spot(
|
ReportingEngine.getSharedInstance().spot(
|
||||||
{
|
{
|
||||||
"callsign": frame.callsign,
|
"callsign": frame.callsign,
|
||||||
"mode": "JS8",
|
"mode": "JS8",
|
||||||
"locator": frame.grid,
|
"locator": frame.grid,
|
||||||
"freq": self.dial_freq + frame.freq,
|
"freq": self.dial_freq + frame.freq,
|
||||||
"db": frame.db,
|
"db": frame.db,
|
||||||
"timestamp": frame.timestamp,
|
"timestamp": frame.timestamp,
|
||||||
"msg": str(frame),
|
"msg": str(frame),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("error while parsing js8 message")
|
logger.exception("error while parsing js8 message")
|
||||||
|
|
||||||
def pushDecode(self):
|
def pushDecode(self):
|
||||||
metrics = Metrics.getSharedInstance()
|
metrics = Metrics.getSharedInstance()
|
||||||
|
69
owrx/wsjt.py
69
owrx/wsjt.py
@ -246,44 +246,43 @@ class Q65Profile(WsjtProfile):
|
|||||||
|
|
||||||
|
|
||||||
class WsjtParser(Parser):
|
class WsjtParser(Parser):
|
||||||
def parse(self, messages):
|
def parse(self, data):
|
||||||
for data in messages:
|
try:
|
||||||
try:
|
profile, 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
|
if msg.startswith("<DecodeFinished>"):
|
||||||
if msg.startswith("<DecodeFinished>"):
|
return
|
||||||
return
|
if msg.startswith(" EOF on input file"):
|
||||||
if msg.startswith(" EOF on input file"):
|
return
|
||||||
return
|
|
||||||
|
|
||||||
mode = profile.getMode()
|
mode = profile.getMode()
|
||||||
if mode in ["WSPR", "FST4W"]:
|
if mode in ["WSPR", "FST4W"]:
|
||||||
messageParser = BeaconMessageParser()
|
messageParser = BeaconMessageParser()
|
||||||
else:
|
else:
|
||||||
messageParser = QsoMessageParser()
|
messageParser = QsoMessageParser()
|
||||||
if mode == "WSPR":
|
if mode == "WSPR":
|
||||||
decoder = WsprDecoder(profile, messageParser)
|
decoder = WsprDecoder(profile, messageParser)
|
||||||
else:
|
else:
|
||||||
decoder = Jt9Decoder(profile, messageParser)
|
decoder = Jt9Decoder(profile, messageParser)
|
||||||
out = decoder.parse(msg, freq)
|
out = decoder.parse(msg, freq)
|
||||||
if isinstance(profile, Q65Profile) and not out["msg"]:
|
if isinstance(profile, Q65Profile) and not out["msg"]:
|
||||||
# all efforts in vain, it's just a potential signal indicator
|
# all efforts in vain, it's just a potential signal indicator
|
||||||
return
|
return
|
||||||
out["mode"] = mode
|
out["mode"] = mode
|
||||||
out["interval"] = profile.getInterval()
|
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:
|
||||||
Map.getSharedInstance().updateLocation(
|
Map.getSharedInstance().updateLocation(
|
||||||
out["callsign"], LocatorLocation(out["locator"]), mode, self.band
|
out["callsign"], LocatorLocation(out["locator"]), mode, self.band
|
||||||
)
|
)
|
||||||
ReportingEngine.getSharedInstance().spot(out)
|
ReportingEngine.getSharedInstance().spot(out)
|
||||||
|
|
||||||
self.handler.write_wsjt_message(out)
|
self.handler.write_wsjt_message(out)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Exception while parsing wsjt message")
|
logger.exception("Exception while parsing wsjt message")
|
||||||
|
|
||||||
def pushDecode(self, mode):
|
def pushDecode(self, mode):
|
||||||
metrics = Metrics.getSharedInstance()
|
metrics = Metrics.getSharedInstance()
|
||||||
|
Loading…
Reference in New Issue
Block a user