restore demodulation of digital voice modes

This commit is contained in:
Jakob Ketterl 2021-08-26 15:58:02 +02:00
parent 5032f4b66d
commit aecb79a4d4
4 changed files with 71 additions and 12 deletions

View File

@ -49,19 +49,31 @@ class Chain:
self.workers[index].stop() self.workers[index].stop()
self.workers[index] = newWorker self.workers[index] = newWorker
error = None
if index == 0: if index == 0:
if self.reader is not None: if self.reader is not None:
newWorker.setReader(self.reader) newWorker.setReader(self.reader)
else: else:
try:
previousWorker = self.workers[index - 1] previousWorker = self.workers[index - 1]
self._connect(previousWorker, newWorker) self._connect(previousWorker, newWorker)
except ValueError as e:
# store error for later raising, but still attempt the second connection
error = e
if index == len(self.workers) - 1: if index == len(self.workers) - 1:
if self.writer is not None: if self.writer is not None:
newWorker.setWriter(self.writer) newWorker.setWriter(self.writer)
else: else:
try:
nextWorker = self.workers[index + 1] nextWorker = self.workers[index + 1]
self._connect(newWorker, nextWorker) self._connect(newWorker, nextWorker)
except ValueError as e:
error = e
if error is not None:
raise e
def append(self, newWorker): def append(self, newWorker):
previousWorker = None previousWorker = None

View File

@ -3,11 +3,9 @@ from pycsdr.modules import AudioResampler, Convert, AdpcmEncoder, Limit
from pycsdr.types import Format from pycsdr.types import Format
class ClientAudioChain(Chain): class Converter(Chain):
def __init__(self, format: Format, inputRate: int, clientRate: int, compression: str): def __init__(self, format: Format, inputRate: int, clientRate: int):
workers = [] workers = []
self.inputRate = inputRate
self.clientRate = clientRate
if inputRate != clientRate: if inputRate != clientRate:
# we only have an audio resampler for float ATM so if we need to resample, we need to convert # we only have an audio resampler for float ATM so if we need to resample, we need to convert
if format != Format.FLOAT: if format != Format.FLOAT:
@ -15,20 +13,39 @@ class ClientAudioChain(Chain):
workers += [AudioResampler(inputRate, clientRate), Limit(), Convert(Format.FLOAT, Format.SHORT)] workers += [AudioResampler(inputRate, clientRate), Limit(), Convert(Format.FLOAT, Format.SHORT)]
elif format != Format.SHORT: elif format != Format.SHORT:
workers += [Convert(format, Format.SHORT)] workers += [Convert(format, Format.SHORT)]
super().__init__(workers)
class ClientAudioChain(Chain):
def __init__(self, format: Format, inputRate: int, clientRate: int, compression: str):
self.format = format
self.inputRate = inputRate
self.clientRate = clientRate
workers = [self._buildConverter()]
if compression == "adpcm": if compression == "adpcm":
workers += [AdpcmEncoder(sync=True)] workers += [AdpcmEncoder(sync=True)]
super().__init__(workers) super().__init__(workers)
def _buildConverter(self):
return Converter(self.format, self.inputRate, self.clientRate)
def setFormat(self, format: Format) -> None: def setFormat(self, format: Format) -> None:
pass if format == self.format:
return
self.format = format
self.replace(0, self._buildConverter())
def setInputRate(self, inputRate: int) -> None: def setInputRate(self, inputRate: int) -> None:
if inputRate == self.inputRate: if inputRate == self.inputRate:
return return
self.inputRate = inputRate
self.replace(0, self._buildConverter())
def setClientRate(self, clientRate: int) -> None: def setClientRate(self, clientRate: int) -> None:
if clientRate == self.clientRate: if clientRate == self.clientRate:
return return
self.clientRate = clientRate
self.replace(0, self._buildConverter())
def setAudioCompression(self, compression: str) -> None: def setAudioCompression(self, compression: str) -> None:
index = self.indexOf(lambda x: isinstance(x, AdpcmEncoder)) index = self.indexOf(lambda x: isinstance(x, AdpcmEncoder))

View File

@ -14,7 +14,7 @@ from csdr.chain.demodulator import BaseDemodulatorChain
from csdr.chain.selector import Selector from csdr.chain.selector import Selector
from csdr.chain.clientaudio import ClientAudioChain from csdr.chain.clientaudio import ClientAudioChain
from csdr.chain.analog import NFm, WFm, Am, Ssb from csdr.chain.analog import NFm, WFm, Am, Ssb
from csdr.chain.digiham import Dmr, Dstar, Nxdn, Ysf from csdr.chain.digiham import DigihamChain, Dmr, Dstar, Nxdn, Ysf
from pycsdr.modules import Buffer, Writer from pycsdr.modules import Buffer, Writer
from pycsdr.types import Format from pycsdr.types import Format
from typing import Union from typing import Union
@ -34,9 +34,18 @@ class ClientDemodulatorChain(Chain):
self.selector.setBandpass(-4000, 4000) self.selector.setBandpass(-4000, 4000)
self.demodulator = demod self.demodulator = demod
self.clientAudioChain = ClientAudioChain(Format.FLOAT, outputRate, outputRate, audioCompression) self.clientAudioChain = ClientAudioChain(Format.FLOAT, outputRate, outputRate, audioCompression)
self.metaWriter = None
self.squelchLevel = -150
super().__init__([self.selector, self.demodulator, self.clientAudioChain]) super().__init__([self.selector, self.demodulator, self.clientAudioChain])
def setDemodulator(self, demodulator: BaseDemodulatorChain): def setDemodulator(self, demodulator: BaseDemodulatorChain):
try:
self.clientAudioChain.setFormat(demodulator.getOutputFormat())
except ValueError:
# this will happen if the new format does not match the current demodulator.
# it's expected and should be mended when swapping out the demodulator in the next step
pass
self.replace(1, demodulator) self.replace(1, demodulator)
if self.demodulator is not None: if self.demodulator is not None:
@ -58,8 +67,11 @@ class ClientDemodulatorChain(Chain):
if not demodulator.supportsSquelch(): if not demodulator.supportsSquelch():
self.selector.setSquelchLevel(-150) self.selector.setSquelchLevel(-150)
else:
self.selector.setSquelchLevel(self.squelchLevel)
self.clientAudioChain.setFormat(demodulator.getOutputFormat()) if self.metaWriter is not None and isinstance(demodulator, DigihamChain):
demodulator.setMetaWriter(self.metaWriter)
def setLowCut(self, lowCut): def setLowCut(self, lowCut):
self.selector.setLowCut(lowCut) self.selector.setLowCut(lowCut)
@ -78,6 +90,9 @@ class ClientDemodulatorChain(Chain):
self.clientAudioChain.setAudioCompression(compression) self.clientAudioChain.setAudioCompression(compression)
def setSquelchLevel(self, level: float) -> None: def setSquelchLevel(self, level: float) -> None:
if level == self.squelchLevel:
return
self.squelchLevel = level
if not self.demodulator.supportsSquelch(): if not self.demodulator.supportsSquelch():
return return
self.selector.setSquelchLevel(level) self.selector.setSquelchLevel(level)
@ -95,6 +110,13 @@ class ClientDemodulatorChain(Chain):
def setPowerWriter(self, writer: Writer) -> None: def setPowerWriter(self, writer: Writer) -> None:
self.selector.setPowerWriter(writer) self.selector.setPowerWriter(writer)
def setMetaWriter(self, writer: Writer) -> None:
if writer is self.metaWriter:
return
self.metaWriter = writer
if isinstance(self.demodulator, DigihamChain):
self.demodulator.setMetaWriter(self.metaWriter)
def setSampleRate(self, sampleRate: int) -> None: def setSampleRate(self, sampleRate: int) -> None:
if sampleRate == self.sampleRate: if sampleRate == self.sampleRate:
return return
@ -182,6 +204,12 @@ class DspManager(Output, SdrSourceEventClient):
reader = buffer.getReader() reader = buffer.getReader()
self.send_output("smeter", reader.read) self.send_output("smeter", reader.read)
# wire meta output
buffer = Buffer(Format.CHAR)
self.chain.setMetaWriter(buffer)
reader = buffer.getReader()
self.send_output("meta", reader.read)
def set_dial_freq(changes): def set_dial_freq(changes):
if ( if (
"center_freq" not in self.props "center_freq" not in self.props

View File

@ -171,7 +171,9 @@ class MetaParser(Parser):
} }
self.currentMetaData = None self.currentMetaData = None
def parse(self, raw: str): def parse(self, raw: memoryview):
raw = raw.tobytes().decode("utf-8").rstrip("\n")
for meta in raw.split("\n"): for meta in raw.split("\n"):
fields = meta.split(";") fields = meta.split(";")
meta = {v[0]: ":".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""} meta = {v[0]: ":".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""}