multi-profile decoding

This commit is contained in:
Jakob Ketterl 2020-04-23 00:21:59 +02:00
parent 0120b33a25
commit 5ab2f02f63
3 changed files with 99 additions and 78 deletions

View File

@ -5,7 +5,7 @@ import threading
import wave import wave
import subprocess import subprocess
import os import os
from multiprocessing.connection import Pipe from multiprocessing.connection import Pipe, wait
from datetime import datetime, timedelta from datetime import datetime, timedelta
from queue import Queue, Full from queue import Queue, Full
@ -13,7 +13,7 @@ from queue import Queue, Full
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO) #logger.setLevel(logging.INFO)
class QueueJob(object): class QueueJob(object):
@ -117,7 +117,7 @@ class AudioChopperProfile(ABC):
return 3 return 3
class AudioChopper(threading.Thread, metaclass=ABCMeta): class AudioWriter(object):
def __init__(self, dsp, source, profile: AudioChopperProfile): def __init__(self, dsp, source, profile: AudioChopperProfile):
self.dsp = dsp self.dsp = dsp
self.source = source self.source = source
@ -128,12 +128,12 @@ class AudioChopper(threading.Thread, metaclass=ABCMeta):
self.switchingLock = threading.Lock() self.switchingLock = threading.Lock()
self.timer = None self.timer = None
(self.outputReader, self.outputWriter) = Pipe() (self.outputReader, self.outputWriter) = Pipe()
self.doRun = True
super().__init__()
def getWaveFile(self): def getWaveFile(self):
filename = "{tmp_dir}/openwebrx-audiochopper-{id}-{timestamp}.wav".format( filename = "{tmp_dir}/openwebrx-audiochopper-{id}-{timestamp}.wav".format(
tmp_dir=self.tmp_dir, id=id(self), timestamp=datetime.utcnow().strftime(self.profile.getFileTimestampFormat()) tmp_dir=self.tmp_dir,
id=id(self.profile),
timestamp=datetime.utcnow().strftime(self.profile.getFileTimestampFormat()),
) )
wavefile = wave.open(filename, "wb") wavefile = wave.open(filename, "wb")
wavefile.setnchannels(1) wavefile.setnchannels(1)
@ -158,7 +158,6 @@ class AudioChopper(threading.Thread, metaclass=ABCMeta):
def _scheduleNextSwitch(self): def _scheduleNextSwitch(self):
self.cancelTimer() self.cancelTimer()
if self.doRun:
delta = self.getNextDecodingTime() - datetime.utcnow() delta = self.getNextDecodingTime() - datetime.utcnow()
self.timer = threading.Timer(delta.total_seconds(), self.switchFiles) self.timer = threading.Timer(delta.total_seconds(), self.switchFiles)
self.timer.start() self.timer.start()
@ -197,20 +196,16 @@ class AudioChopper(threading.Thread, metaclass=ABCMeta):
decoder.kill() decoder.kill()
os.unlink(job.file) os.unlink(job.file)
def run(self) -> None: def start(self):
logger.debug("WSJT chopper starting up")
(self.wavefilename, self.wavefile) = self.getWaveFile() (self.wavefilename, self.wavefile) = self.getWaveFile()
self._scheduleNextSwitch() self._scheduleNextSwitch()
while self.doRun:
data = self.source.read(256) def write(self, data):
if data is None or (isinstance(data, bytes) and len(data) == 0):
self.doRun = False
else:
self.switchingLock.acquire() self.switchingLock.acquire()
self.wavefile.writeframes(data) self.wavefile.writeframes(data)
self.switchingLock.release() self.switchingLock.release()
logger.debug("WSJT chopper shutting down") def stop(self):
self.outputReader.close() self.outputReader.close()
self.outputWriter.close() self.outputWriter.close()
self.cancelTimer() self.cancelTimer()
@ -219,8 +214,33 @@ class AudioChopper(threading.Thread, metaclass=ABCMeta):
except Exception: except Exception:
logger.exception("error removing undecoded file") logger.exception("error removing undecoded file")
class AudioChopper(threading.Thread, metaclass=ABCMeta):
def __init__(self, dsp, source, *profiles: AudioChopperProfile):
self.source = source
self.writers = [AudioWriter(dsp, source, p) for p in profiles]
self.doRun = True
super().__init__()
def run(self) -> None:
logger.debug("Audio chopper starting up")
for w in self.writers:
w.start()
while self.doRun:
data = self.source.read(256)
if data is None or (isinstance(data, bytes) and len(data) == 0):
self.doRun = False
else:
for w in self.writers:
w.write(data)
logger.debug("Audio chopper shutting down")
for w in self.writers:
w.stop()
def read(self): def read(self):
try: try:
return self.outputReader.recv() readers = wait([w.outputReader for w in self.writers])
return [r.recv() for r in readers]
except EOFError: except EOFError:
return None return None

View File

@ -26,7 +26,8 @@ class Js8NormalProfile(AudioChopperProfile):
class Js8Parser(Parser): class Js8Parser(Parser):
decoderRegex = re.compile(" ?<Decode(Started|Debug|Finished)>") decoderRegex = re.compile(" ?<Decode(Started|Debug|Finished)>")
def parse(self, raw): def parse(self, messages):
for raw in messages:
try: try:
freq, raw_msg = raw freq, raw_msg = raw
self.setDialFrequency(freq) self.setDialFrequency(freq)

View File

@ -10,7 +10,6 @@ from abc import ABC, abstractmethod
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class Ft8Profile(AudioChopperProfile): class Ft8Profile(AudioChopperProfile):
@ -75,7 +74,8 @@ class Ft4Profile(AudioChopperProfile):
class WsjtParser(Parser): class WsjtParser(Parser):
modes = {"~": "FT8", "#": "JT65", "@": "JT9", "+": "FT4"} modes = {"~": "FT8", "#": "JT65", "@": "JT9", "+": "FT4"}
def parse(self, data): def parse(self, messages):
for data in messages:
try: try:
freq, raw_msg = data freq, raw_msg = data
self.setDialFrequency(freq) self.setDialFrequency(freq)