restore demodulation of digital voice modes
This commit is contained in:
parent
5032f4b66d
commit
aecb79a4d4
@ -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
|
||||||
|
@ -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))
|
||||||
|
32
owrx/dsp.py
32
owrx/dsp.py
@ -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
|
||||||
|
@ -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] != ""}
|
||||||
|
Loading…
Reference in New Issue
Block a user