80 lines
2.5 KiB
Python
80 lines
2.5 KiB
Python
|
import threading
|
||
|
import wave
|
||
|
from datetime import datetime, timedelta
|
||
|
import time
|
||
|
import sched
|
||
|
import subprocess
|
||
|
|
||
|
import logging
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class Ft8Chopper(threading.Thread):
|
||
|
def __init__(self, source):
|
||
|
self.source = source
|
||
|
(self.wavefilename, self.wavefile) = self.getWaveFile()
|
||
|
self.scheduler = sched.scheduler(time.time, time.sleep)
|
||
|
self.queue = []
|
||
|
self.doRun = True
|
||
|
super().__init__()
|
||
|
|
||
|
def getWaveFile(self):
|
||
|
filename = "/tmp/openwebrx-ft8chopper-{0}.wav".format(datetime.now().strftime("%Y%m%d-%H%M%S"))
|
||
|
wavefile = wave.open(filename, "wb")
|
||
|
wavefile.setnchannels(1)
|
||
|
wavefile.setsampwidth(2)
|
||
|
wavefile.setframerate(12000)
|
||
|
return (filename, wavefile)
|
||
|
|
||
|
def getNextDecodingTime(self):
|
||
|
t = datetime.now()
|
||
|
seconds = (int(t.second / 15) + 1) * 15
|
||
|
if seconds >= 60:
|
||
|
t = t + timedelta(minutes = 1)
|
||
|
seconds = 0
|
||
|
t = t.replace(second = seconds, microsecond = 0)
|
||
|
logger.debug("scheduling: {0}".format(t))
|
||
|
return t.timestamp()
|
||
|
|
||
|
def startScheduler(self):
|
||
|
self._scheduleNextSwitch()
|
||
|
threading.Thread(target = self.scheduler.run).start()
|
||
|
|
||
|
def emptyScheduler(self):
|
||
|
for event in self.scheduler.queue:
|
||
|
self.scheduler.cancel(event)
|
||
|
|
||
|
def _scheduleNextSwitch(self):
|
||
|
self.scheduler.enterabs(self.getNextDecodingTime(), 1, self.switchFiles)
|
||
|
|
||
|
def switchFiles(self):
|
||
|
file = self.wavefile
|
||
|
filename = self.wavefilename
|
||
|
(self.wavefilename, self.wavefile) = self.getWaveFile()
|
||
|
|
||
|
file.close()
|
||
|
self.queue.append(filename)
|
||
|
self._scheduleNextSwitch()
|
||
|
|
||
|
def decode(self):
|
||
|
if self.queue:
|
||
|
file = self.queue.pop()
|
||
|
logger.debug("processing file {0}".format(file))
|
||
|
#TODO expose decoding quality parameters through config
|
||
|
self.decoder = subprocess.Popen(["jt9", "--ft8", "-d", "3", file])
|
||
|
|
||
|
def run(self) -> None:
|
||
|
logger.debug("FT8 chopper starting up")
|
||
|
self.startScheduler()
|
||
|
while self.doRun:
|
||
|
data = self.source.read(256)
|
||
|
if data is None or (isinstance(data, bytes) and len(data) == 0):
|
||
|
logger.warning("zero read on ft8 chopper")
|
||
|
self.doRun = False
|
||
|
else:
|
||
|
self.wavefile.writeframes(data)
|
||
|
|
||
|
self.decode()
|
||
|
logger.debug("FT8 chopper shutting down")
|
||
|
self.emptyScheduler()
|