openwebrx-clone/owrx/dsp.py

574 lines
22 KiB
Python
Raw Normal View History

from owrx.meta import MetaParser
from owrx.wsjt import WsjtParser
2020-04-12 11:10:23 +00:00
from owrx.js8 import Js8Parser
from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass
from owrx.property import PropertyStack, PropertyLayer, PropertyValidator
from owrx.property.validators import OrValidator, RegexValidator, BoolValidator
2020-04-26 20:46:30 +00:00
from owrx.modes import Modes
2021-04-09 16:16:25 +00:00
from csdr.output import Output
2021-08-23 12:25:28 +00:00
from csdr.chain import Chain
2021-08-31 20:46:11 +00:00
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, SecondaryDemodulator, DialFrequencyReceiver
2021-08-23 12:25:28 +00:00
from csdr.chain.selector import Selector
from csdr.chain.clientaudio import ClientAudioChain
from csdr.chain.analog import NFm, WFm, Am, Ssb
from csdr.chain.digiham import DigihamChain, Dmr, Dstar, Nxdn, Ysf
2021-09-07 12:45:52 +00:00
from csdr.chain.m17 import M17Chain
2021-08-27 22:10:46 +00:00
from csdr.chain.fft import FftChain
2021-09-06 18:00:14 +00:00
from csdr.chain.digimodes import AudioChopperDemodulator, PacketDemodulator, PocsagDemodulator
2021-08-23 12:25:28 +00:00
from pycsdr.modules import Buffer, Writer
from pycsdr.types import Format
from typing import Union
import threading
import re
import logging
logger = logging.getLogger(__name__)
2021-08-23 12:25:28 +00:00
class ClientDemodulatorChain(Chain):
2021-08-27 16:30:46 +00:00
def __init__(self, demod: BaseDemodulatorChain, sampleRate: int, outputRate: int, hdOutputRate: int, audioCompression: str):
2021-08-23 12:25:28 +00:00
self.sampleRate = sampleRate
self.outputRate = outputRate
2021-08-27 16:30:46 +00:00
self.hdOutputRate = hdOutputRate
2021-08-23 12:25:28 +00:00
self.selector = Selector(sampleRate, outputRate, 0.0)
self.selector.setBandpass(-4000, 4000)
2021-08-27 22:10:46 +00:00
self.selectorBuffer = Buffer(Format.COMPLEX_FLOAT)
2021-08-31 14:54:37 +00:00
self.audioBuffer = None
2021-08-23 12:25:28 +00:00
self.demodulator = demod
2021-08-31 14:54:37 +00:00
self.secondaryDemodulator = None
2021-08-31 20:46:11 +00:00
self.centerFrequency = None
self.frequencyOffset = None
2021-08-27 16:30:46 +00:00
inputRate = demod.getFixedAudioRate() if isinstance(demod, FixedAudioRateChain) else outputRate
oRate = hdOutputRate if isinstance(demod, HdAudio) else outputRate
self.clientAudioChain = ClientAudioChain(demod.getOutputFormat(), inputRate, oRate, audioCompression)
2021-08-27 22:10:46 +00:00
self.secondaryFftChain = None
self.metaWriter = None
2021-08-31 14:54:37 +00:00
self.secondaryFftWriter = None
self.secondaryWriter = None
self.squelchLevel = -150
2021-08-23 12:25:28 +00:00
super().__init__([self.selector, self.demodulator, self.clientAudioChain])
2021-08-31 14:54:37 +00:00
def stop(self):
super().stop()
if self.secondaryFftChain is not None:
self.secondaryFftChain.stop()
self.secondaryFftChain = None
if self.secondaryDemodulator is not None:
self.secondaryDemodulator.stop()
self.secondaryDemodulator = None
2021-08-27 22:10:46 +00:00
def _connect(self, w1, w2, buffer: Union[Buffer, None] = None) -> None:
if w1 is self.selector:
super()._connect(w1, w2, self.selectorBuffer)
2021-08-31 14:54:37 +00:00
elif w2 is self.clientAudioChain:
format = w1.getOutputFormat()
if self.audioBuffer is None or self.audioBuffer.getFormat() != format:
self.audioBuffer = Buffer(format)
2021-09-06 13:05:33 +00:00
if self.secondaryDemodulator is not None and self.secondaryDemodulator.getInputFormat() is not Format.COMPLEX_FLOAT:
2021-08-31 14:54:37 +00:00
self.secondaryDemodulator.setReader(self.audioBuffer.getReader())
super()._connect(w1, w2, self.audioBuffer)
2021-08-27 22:10:46 +00:00
else:
super()._connect(w1, w2)
2021-08-23 12:25:28 +00:00
def setDemodulator(self, demodulator: BaseDemodulatorChain):
2021-08-31 14:54:37 +00:00
if demodulator is self.demodulator:
return
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
2021-08-23 12:25:28 +00:00
self.replace(1, demodulator)
if self.demodulator is not None:
self.demodulator.stop()
self.demodulator = demodulator
2021-08-27 16:30:46 +00:00
outputRate = self.hdOutputRate if isinstance(self.demodulator, HdAudio) else self.outputRate
2021-08-27 15:34:48 +00:00
if isinstance(self.demodulator, FixedIfSampleRateChain):
self.selector.setOutputRate(self.demodulator.getFixedIfSampleRate())
2021-09-06 13:05:33 +00:00
elif isinstance(self.secondaryDemodulator, FixedAudioRateChain):
2021-08-31 14:54:37 +00:00
self.selector.setOutputRate(self.secondaryDemodulator.getFixedAudioRate())
2021-08-23 12:25:28 +00:00
else:
2021-08-27 16:30:46 +00:00
self.selector.setOutputRate(outputRate)
2021-09-06 13:05:33 +00:00
self.demodulator.setSampleRate(outputRate)
2021-08-23 12:25:28 +00:00
2021-08-27 15:34:48 +00:00
if isinstance(self.demodulator, FixedAudioRateChain):
self.clientAudioChain.setInputRate(self.demodulator.getFixedAudioRate())
2021-09-06 13:05:33 +00:00
elif isinstance(self.secondaryDemodulator, FixedAudioRateChain):
2021-08-31 14:54:37 +00:00
self.clientAudioChain.setInputRate(self.secondaryDemodulator.getFixedAudioRate())
2021-08-23 12:25:28 +00:00
else:
2021-08-27 16:30:46 +00:00
self.clientAudioChain.setInputRate(outputRate)
2021-08-23 12:25:28 +00:00
2021-09-06 13:05:33 +00:00
self._syncSquelch()
2021-08-23 12:25:28 +00:00
2021-08-27 16:30:46 +00:00
self.clientAudioChain.setClientRate(outputRate)
if self.metaWriter is not None and isinstance(demodulator, DigihamChain):
demodulator.setMetaWriter(self.metaWriter)
2021-08-23 12:25:28 +00:00
2021-09-06 13:05:33 +00:00
def _getSelectorOutputRate(self):
if isinstance(self.secondaryDemodulator, FixedAudioRateChain):
if isinstance(self.demodulator, FixedAudioRateChain) and self.demodulator.getFixedAudioRate() != self.secondaryDemodulator.getFixedAudioRate():
raise ValueError("secondary and primary demodulator chain audio rates do not match!")
return self.secondaryDemodulator.getFixedAudioRate()
return self.outputRate
2021-08-31 14:54:37 +00:00
def setSecondaryDemodulator(self, demod: Union[SecondaryDemodulator, None]):
if demod is self.secondaryDemodulator:
return
if self.secondaryDemodulator is not None:
self.secondaryDemodulator.stop()
self.secondaryDemodulator = demod
2021-09-06 13:05:33 +00:00
rate = self._getSelectorOutputRate()
2021-08-31 14:54:37 +00:00
self.selector.setOutputRate(rate)
self.clientAudioChain.setInputRate(rate)
2021-09-06 13:05:33 +00:00
self.demodulator.setSampleRate(rate)
2021-08-31 20:46:11 +00:00
self._updateDialFrequency()
2021-09-06 13:05:33 +00:00
self._syncSquelch()
2021-08-31 14:54:37 +00:00
if self.secondaryDemodulator is not None:
2021-09-06 13:05:33 +00:00
if self.secondaryDemodulator.getInputFormat() is Format.COMPLEX_FLOAT:
self.secondaryDemodulator.setReader(self.selectorBuffer.getReader())
else:
self.secondaryDemodulator.setReader(self.audioBuffer.getReader())
2021-08-31 14:54:37 +00:00
self.secondaryDemodulator.setWriter(self.secondaryWriter)
if self.secondaryDemodulator is None and self.secondaryFftChain is not None:
self.secondaryFftChain.stop()
self.secondaryFftChain = None
if self.secondaryDemodulator is not None and self.secondaryFftChain is None:
# TODO eliminate constants
2021-09-06 13:05:33 +00:00
self.secondaryFftChain = FftChain(self._getSelectorOutputRate(), 2048, 0.3, 9, "adpcm")
2021-08-31 14:54:37 +00:00
self.secondaryFftChain.setReader(self.selectorBuffer.getReader())
self.secondaryFftChain.setWriter(self.secondaryFftWriter)
2021-09-06 13:05:33 +00:00
if self.secondaryFftChain is not None:
self.secondaryFftChain.setSampleRate(rate)
def _syncSquelch(self):
if not self.demodulator.supportsSquelch() or (self.secondaryDemodulator is not None and not self.secondaryDemodulator.supportsSquelch()):
self.selector.setSquelchLevel(-150)
else:
self.selector.setSquelchLevel(self.squelchLevel)
2021-08-23 12:25:28 +00:00
def setLowCut(self, lowCut):
self.selector.setLowCut(lowCut)
def setHighCut(self, highCut):
self.selector.setHighCut(highCut)
def setBandpass(self, lowCut, highCut):
self.selector.setBandpass(lowCut, highCut)
def setFrequencyOffset(self, offset: int) -> None:
2021-08-31 20:46:11 +00:00
if offset == self.frequencyOffset:
return
self.frequencyOffset = offset
2021-08-23 12:25:28 +00:00
shift = -offset / self.sampleRate
self.selector.setShiftRate(shift)
2021-08-31 20:46:11 +00:00
self._updateDialFrequency()
def setCenterFrequency(self, frequency: int) -> None:
if frequency == self.centerFrequency:
return
self.centerFrequency = frequency
self._updateDialFrequency()
def _updateDialFrequency(self):
if self.centerFrequency is None or self.frequencyOffset is None:
return
dialFrequency = self.centerFrequency + self.frequencyOffset
if isinstance(self.secondaryDemodulator, DialFrequencyReceiver):
self.secondaryDemodulator.setDialFrequency(dialFrequency)
2021-08-23 12:25:28 +00:00
def setAudioCompression(self, compression: str) -> None:
self.clientAudioChain.setAudioCompression(compression)
def setSquelchLevel(self, level: float) -> None:
if level == self.squelchLevel:
return
self.squelchLevel = level
2021-09-06 13:05:33 +00:00
self._syncSquelch()
2021-08-23 12:25:28 +00:00
def setOutputRate(self, outputRate) -> None:
if outputRate == self.outputRate:
return
self.outputRate = outputRate
2021-08-27 16:30:46 +00:00
if isinstance(self.demodulator, HdAudio):
return
if not isinstance(self.demodulator, FixedIfSampleRateChain):
self.selector.setOutputRate(outputRate)
2021-09-06 13:05:33 +00:00
self.demodulator.setSampleRate(outputRate)
2021-08-27 16:30:46 +00:00
if not isinstance(self.demodulator, FixedAudioRateChain):
self.clientAudioChain.setClientRate(outputRate)
def setHdOutputRate(self, outputRate) -> None:
if outputRate == self.hdOutputRate:
return
self.hdOutputRate = outputRate
if not isinstance(self.demodulator, HdAudio):
return
2021-08-27 15:34:48 +00:00
if not isinstance(self.demodulator, FixedIfSampleRateChain):
2021-08-23 12:25:28 +00:00
self.selector.setOutputRate(outputRate)
2021-08-27 15:34:48 +00:00
if not isinstance(self.demodulator, FixedAudioRateChain):
2021-08-23 12:25:28 +00:00
self.clientAudioChain.setClientRate(outputRate)
2021-08-27 22:10:46 +00:00
def setSampleRate(self, sampleRate: int) -> None:
if sampleRate == self.sampleRate:
return
self.sampleRate = sampleRate
self.selector.setInputRate(sampleRate)
# TODO update secondary FFT
2021-08-23 12:25:28 +00:00
def setPowerWriter(self, writer: Writer) -> None:
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)
2021-08-31 14:54:37 +00:00
def setSecondaryFftWriter(self, writer: Writer) -> None:
if writer is self.secondaryFftWriter:
return
self.secondaryFftWriter = writer
2021-08-27 22:10:46 +00:00
2021-08-31 14:54:37 +00:00
if self.secondaryFftChain is not None:
2021-08-27 22:10:46 +00:00
self.secondaryFftChain.setWriter(writer)
2021-08-31 14:54:37 +00:00
def setSecondaryWriter(self, writer: Writer) -> None:
if writer is self.secondaryWriter:
return
self.secondaryWriter = writer
if self.secondaryDemodulator is not None:
self.secondaryDemodulator.setWriter(writer)
2021-08-27 22:10:46 +00:00
def setSecondaryFftSize(self, size: int) -> None:
# TODO
pass
2021-08-23 12:25:28 +00:00
class ModulationValidator(OrValidator):
"""
This validator only allows alphanumeric characters and numbers, but no spaces or special characters
"""
2021-01-24 21:54:58 +00:00
def __init__(self):
super().__init__(BoolValidator(), RegexValidator(re.compile("^[a-z0-9]+$")))
2021-04-09 16:16:25 +00:00
class DspManager(Output, SdrSourceEventClient):
def __init__(self, handler, sdrSource):
self.handler = handler
self.sdrSource = sdrSource
self.parsers = {
"meta": MetaParser(self.handler),
}
2020-03-24 21:13:42 +00:00
self.props = PropertyStack()
2020-03-24 21:13:42 +00:00
# local demodulator properties not forwarded to the sdr
# ensure strict validation since these can be set from the client
# and are used to build executable commands
validators = {
"output_rate": "int",
"hd_output_rate": "int",
"squelch_level": "num",
"secondary_mod": ModulationValidator(),
"low_cut": "num",
"high_cut": "num",
"offset_freq": "int",
"mod": ModulationValidator(),
"secondary_offset_freq": "int",
"dmr_filter": "int",
}
self.localProps = PropertyValidator(PropertyLayer().filter(*validators.keys()), validators)
self.props.addLayer(0, self.localProps)
2020-03-24 21:13:42 +00:00
# properties that we inherit from the sdr
self.props.addLayer(
1,
self.sdrSource.getProps().filter(
"audio_compression",
"fft_compression",
"digimodes_fft_size",
"samp_rate",
"center_freq",
"start_mod",
"start_freq",
"wfm_deemphasis_tau",
2021-05-29 16:50:17 +00:00
"digital_voice_codecserver",
),
)
2021-08-23 12:25:28 +00:00
# TODO wait for the rate to come from the client
if "output_rate" not in self.props:
self.props["output_rate"] = 12000
2021-08-27 16:30:46 +00:00
if "hd_output_rate" not in self.props:
self.props["hd_output_rate"] = 48000
2021-08-23 12:25:28 +00:00
self.chain = ClientDemodulatorChain(
self._getDemodulator("nfm"),
self.props["samp_rate"],
self.props["output_rate"],
2021-08-27 16:30:46 +00:00
self.props["hd_output_rate"],
2021-08-23 12:25:28 +00:00
self.props["audio_compression"]
)
2021-08-27 14:11:03 +00:00
self.readers = {}
2021-08-26 15:21:52 +00:00
2021-08-23 12:25:28 +00:00
# wire audio output
buffer = Buffer(self.chain.getOutputFormat())
self.chain.setWriter(buffer)
2021-08-27 16:30:46 +00:00
# TODO check for hd audio
2021-08-26 15:21:52 +00:00
self.wireOutput("audio", buffer)
2021-08-23 12:25:28 +00:00
# wire power level output
buffer = Buffer(Format.FLOAT)
self.chain.setPowerWriter(buffer)
2021-08-26 15:21:52 +00:00
self.wireOutput("smeter", buffer)
# wire meta output
buffer = Buffer(Format.CHAR)
self.chain.setMetaWriter(buffer)
2021-08-26 15:21:52 +00:00
self.wireOutput("meta", buffer)
2021-08-31 14:54:37 +00:00
# wire secondary FFT
# TODO format is different depending on compression
buffer = Buffer(Format.CHAR)
self.chain.setSecondaryFftWriter(buffer)
self.wireOutput("secondary_fft", buffer)
# wire secondary demodulator
buffer = Buffer(Format.CHAR)
self.chain.setSecondaryWriter(buffer)
# TODO there's multiple outputs depending on the modulation right now
2021-09-06 13:05:33 +00:00
self.wireOutput("secondary_demod", buffer)
2021-08-31 14:54:37 +00:00
2020-04-26 20:46:30 +00:00
if "start_mod" in self.props:
2021-08-23 12:25:28 +00:00
self.setDemodulator(self.props["start_mod"])
2020-04-26 20:46:30 +00:00
mode = Modes.findByModulation(self.props["start_mod"])
if mode and mode.bandpass:
2021-08-23 12:25:28 +00:00
bpf = [mode.bandpass.low_cut, mode.bandpass.high_cut]
self.chain.setBandpass(*bpf)
2020-04-26 20:46:30 +00:00
if "start_freq" in self.props and "center_freq" in self.props:
2021-08-23 12:25:28 +00:00
self.chain.setFrequencyOffset(self.props["start_freq"] - self.props["center_freq"])
2020-04-26 20:46:30 +00:00
else:
2021-08-23 12:25:28 +00:00
self.chain.setFrequencyOffset(0)
2020-04-26 20:46:30 +00:00
self.subscriptions = [
2021-08-27 14:11:03 +00:00
self.props.wireProperty("audio_compression", self.setAudioCompression),
2021-08-23 12:25:28 +00:00
# probably unused:
# self.props.wireProperty("fft_compression", self.dsp.set_fft_compression),
2021-08-27 22:10:46 +00:00
self.props.wireProperty("digimodes_fft_size", self.chain.setSecondaryFftSize),
2021-08-23 12:25:28 +00:00
self.props.wireProperty("samp_rate", self.chain.setSampleRate),
self.props.wireProperty("output_rate", self.chain.setOutputRate),
2021-08-27 16:30:46 +00:00
self.props.wireProperty("hd_output_rate", self.chain.setHdOutputRate),
2021-08-23 12:25:28 +00:00
self.props.wireProperty("offset_freq", self.chain.setFrequencyOffset),
2021-08-31 20:46:11 +00:00
self.props.wireProperty("center_freq", self.chain.setCenterFrequency),
2021-08-23 12:25:28 +00:00
self.props.wireProperty("squelch_level", self.chain.setSquelchLevel),
self.props.wireProperty("low_cut", self.chain.setLowCut),
self.props.wireProperty("high_cut", self.chain.setHighCut),
self.props.wireProperty("mod", self.setDemodulator),
# TODO
# self.props.wireProperty("dmr_filter", self.dsp.set_dmr_filter),
# TODO
# self.props.wireProperty("wfm_deemphasis_tau", self.dsp.set_wfm_deemphasis_tau),
# TODO
# self.props.wireProperty("digital_voice_codecserver", self.dsp.set_codecserver),
]
2021-08-23 12:25:28 +00:00
# TODO
# sp.set_temporary_directory(CoreConfig().get_temporary_directory())
def set_secondary_mod(mod):
if mod == False:
mod = None
self.dsp.set_secondary_demodulator(mod)
2021-08-27 22:10:46 +00:00
#if mod is not None:
#send_secondary_config()
self.subscriptions += [
2021-08-27 22:10:46 +00:00
self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator),
self.props.wireProperty("digimodes_fft_size", self.chain.setSecondaryFftSize),
2021-08-23 12:25:28 +00:00
# TODO
# self.props.wireProperty("secondary_offset_freq", self.dsp.set_secondary_offset_freq),
]
2020-04-30 20:54:44 +00:00
self.startOnAvailable = False
self.sdrSource.addClient(self)
super().__init__()
2021-08-23 12:25:28 +00:00
def _getDemodulator(self, demod: Union[str, BaseDemodulatorChain]):
if isinstance(demod, BaseDemodulatorChain):
return demod
# TODO: move this to Modes
if demod == "nfm":
2021-09-07 12:45:52 +00:00
return NFm(self.props["output_rate"])
2021-08-23 12:25:28 +00:00
elif demod == "wfm":
2021-09-07 12:45:52 +00:00
return WFm(self.props["hd_output_rate"], self.props["wfm_deemphasis_tau"])
2021-08-23 12:25:28 +00:00
elif demod == "am":
2021-09-07 12:45:52 +00:00
return Am()
2021-08-23 12:25:28 +00:00
elif demod in ["usb", "lsb", "cw"]:
2021-09-07 12:45:52 +00:00
return Ssb()
2021-08-23 12:25:28 +00:00
elif demod == "dmr":
2021-09-07 12:45:52 +00:00
return Dmr(self.props["digital_voice_codecserver"])
2021-08-23 12:25:28 +00:00
elif demod == "dstar":
2021-09-07 12:45:52 +00:00
return Dstar(self.props["digital_voice_codecserver"])
2021-08-23 12:25:28 +00:00
elif demod == "ysf":
2021-09-07 12:45:52 +00:00
return Ysf(self.props["digital_voice_codecserver"])
2021-08-23 12:25:28 +00:00
elif demod == "nxdn":
2021-09-07 12:45:52 +00:00
return Nxdn(self.props["digital_voice_codecserver"])
elif demod == "m17":
return M17Chain()
2021-08-23 12:25:28 +00:00
def setDemodulator(self, mod):
demodulator = self._getDemodulator(mod)
if demodulator is None:
raise ValueError("unsupported demodulator: {}".format(mod))
self.chain.setDemodulator(demodulator)
2021-08-27 16:30:46 +00:00
# re-wire the audio to the correct client API
buffer = Buffer(self.chain.getOutputFormat())
self.chain.setWriter(buffer)
if isinstance(demodulator, HdAudio):
self.wireOutput("hd_audio", buffer)
else:
self.wireOutput("audio", buffer)
2021-08-27 22:10:46 +00:00
def sendSecondaryConfig(self):
self.handler.write_secondary_dsp_config(
{
"secondary_fft_size": self.props["digimodes_fft_size"],
"if_samp_rate": self.props["output_rate"],
# TODO
"secondary_bw": 31.25
}
)
2021-08-31 14:54:37 +00:00
def _getSecondaryDemodulator(self, mod):
if isinstance(mod, SecondaryDemodulator):
return mod
# TODO add remaining modes
2021-08-31 15:01:52 +00:00
if mod in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]:
2021-08-31 14:54:37 +00:00
return AudioChopperDemodulator(mod, WsjtParser())
2021-09-06 20:50:57 +00:00
elif mod == "js8":
return AudioChopperDemodulator(mod, Js8Parser())
2021-09-06 13:05:33 +00:00
elif mod == "packet":
return PacketDemodulator()
2021-09-06 18:00:14 +00:00
elif mod == "pocsag":
return PocsagDemodulator()
2021-08-31 14:54:37 +00:00
return None
2021-08-27 22:10:46 +00:00
def setSecondaryDemodulator(self, mod):
2021-08-31 14:54:37 +00:00
demodulator = self._getSecondaryDemodulator(mod)
if not demodulator:
self.chain.setSecondaryDemodulator(None)
2021-08-27 22:10:46 +00:00
else:
self.sendSecondaryConfig()
2021-08-31 14:54:37 +00:00
self.chain.setSecondaryDemodulator(demodulator)
2021-08-27 22:10:46 +00:00
2021-08-27 14:11:03 +00:00
def setAudioCompression(self, comp):
try:
self.chain.setAudioCompression(comp)
except ValueError:
# wrong output format... need to re-wire
buffer = Buffer(self.chain.getOutputFormat())
self.chain.setWriter(buffer)
2021-08-27 16:30:46 +00:00
# TODO check if this is hd audio
2021-08-27 14:11:03 +00:00
self.wireOutput("audio", buffer)
def start(self):
if self.sdrSource.isAvailable():
2021-08-23 12:25:28 +00:00
self.chain.setReader(self.sdrSource.getBuffer().getReader())
2020-04-30 20:54:44 +00:00
else:
self.startOnAvailable = True
2021-08-27 16:30:46 +00:00
def unwireOutput(self, t: str):
if t in self.readers:
self.readers[t].stop()
del self.readers[t]
2021-08-26 15:21:52 +00:00
def wireOutput(self, t: str, buffer: Buffer):
logger.debug("wiring new output of type %s", t)
writers = {
"audio": self.handler.write_dsp_data,
2020-08-08 19:29:25 +00:00
"hd_audio": self.handler.write_hd_audio,
"smeter": self.handler.write_s_meter_level,
"secondary_fft": self.handler.write_secondary_fft,
"secondary_demod": self.handler.write_secondary_demod,
}
for demod, parser in self.parsers.items():
writers[demod] = parser.parse
write = writers[t]
2021-08-27 16:30:46 +00:00
self.unwireOutput(t)
2021-08-27 14:11:03 +00:00
2021-08-26 15:21:52 +00:00
reader = buffer.getReader()
2021-08-27 14:11:03 +00:00
self.readers[t] = reader
2021-08-26 15:21:52 +00:00
threading.Thread(target=self.pump(reader.read, write), name="dsp_pump_{}".format(t)).start()
def stop(self):
2021-08-23 12:25:28 +00:00
self.chain.stop()
self.chain = None
2021-08-27 14:11:03 +00:00
for reader in self.readers.values():
reader.stop()
self.readers = {}
2021-08-23 12:25:28 +00:00
2020-04-30 20:54:44 +00:00
self.startOnAvailable = False
self.sdrSource.removeClient(self)
for sub in self.subscriptions:
sub.cancel()
self.subscriptions = []
def setProperties(self, props):
for k, v in props.items():
self.setProperty(k, v)
def setProperty(self, prop, value):
self.localProps[prop] = value
2021-02-20 21:54:07 +00:00
def getClientClass(self) -> SdrClientClass:
return SdrClientClass.USER
2021-02-20 21:54:07 +00:00
def onStateChange(self, state: SdrSourceState):
if state is SdrSourceState.RUNNING:
logger.debug("received STATE_RUNNING, attempting DspSource restart")
2020-04-30 20:54:44 +00:00
if self.startOnAvailable:
2021-08-23 12:25:28 +00:00
self.chain.setReader(self.sdrSource.getBuffer().getReader())
2020-04-30 20:54:44 +00:00
self.startOnAvailable = False
2021-02-20 21:54:07 +00:00
elif state is SdrSourceState.STOPPING:
logger.debug("received STATE_STOPPING, shutting down DspSource")
2021-08-23 12:25:28 +00:00
self.stop()
def onFail(self):
logger.debug("received onFail(), shutting down DspSource")
2021-08-23 12:25:28 +00:00
self.stop()
def onShutdown(self):
2021-08-23 12:25:28 +00:00
self.stop()