restore PSK decoding
This commit is contained in:
parent
3fa3aac766
commit
cbcba5807f
@ -1,21 +1,8 @@
|
|||||||
from csdr.chain import Chain
|
from csdr.chain import Chain
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, ABCMeta, abstractmethod
|
||||||
from pycsdr.modules import Writer
|
from pycsdr.modules import Writer
|
||||||
|
|
||||||
|
|
||||||
class BaseDemodulatorChain(Chain):
|
|
||||||
def supportsSquelch(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def setSampleRate(self, sampleRate: int) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SecondaryDemodulator(Chain):
|
|
||||||
def supportsSquelch(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class FixedAudioRateChain(ABC):
|
class FixedAudioRateChain(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def getFixedAudioRate(self) -> int:
|
def getFixedAudioRate(self) -> int:
|
||||||
@ -49,3 +36,28 @@ class SlotFilterChain(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def setSlotFilter(self, filter: int) -> None:
|
def setSlotFilter(self, filter: int) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SecondarySelectorChain(ABC):
|
||||||
|
def getBandwidth(self) -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDemodulatorChain(Chain):
|
||||||
|
def supportsSquelch(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setSampleRate(self, sampleRate: int) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SecondaryDemodulator(Chain):
|
||||||
|
def supportsSquelch(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setSampleRate(self, sampleRate: int) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceDemodulator(SecondaryDemodulator, FixedAudioRateChain, metaclass=ABCMeta):
|
||||||
|
pass
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
from csdr.chain.demodulator import SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver
|
from csdr.chain.demodulator import ServiceDemodulator, SecondaryDemodulator, DialFrequencyReceiver, SecondarySelectorChain
|
||||||
from owrx.audio.chopper import AudioChopper
|
from owrx.audio.chopper import AudioChopper
|
||||||
from owrx.aprs.kiss import KissDeframer
|
from owrx.aprs.kiss import KissDeframer
|
||||||
from owrx.aprs import Ax25Parser, AprsParser
|
from owrx.aprs import Ax25Parser, AprsParser
|
||||||
from pycsdr.modules import Convert, FmDemod
|
from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from owrx.aprs.module import DirewolfModule
|
from owrx.aprs.module import DirewolfModule
|
||||||
from digiham.modules import FskDemodulator, PocsagDecoder
|
from digiham.modules import FskDemodulator, PocsagDecoder
|
||||||
from owrx.pocsag import PocsagParser
|
from owrx.pocsag import PocsagParser
|
||||||
|
|
||||||
|
|
||||||
class AudioChopperDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver):
|
class AudioChopperDemodulator(ServiceDemodulator, DialFrequencyReceiver):
|
||||||
# TODO parser typing
|
# TODO parser typing
|
||||||
def __init__(self, mode: str, parser):
|
def __init__(self, mode: str, parser):
|
||||||
self.chopper = AudioChopper(mode, parser)
|
self.chopper = AudioChopper(mode, parser)
|
||||||
@ -23,7 +23,7 @@ class AudioChopperDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFre
|
|||||||
self.chopper.setDialFrequency(frequency)
|
self.chopper.setDialFrequency(frequency)
|
||||||
|
|
||||||
|
|
||||||
class PacketDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver):
|
class PacketDemodulator(ServiceDemodulator, DialFrequencyReceiver):
|
||||||
def __init__(self, service: bool = False):
|
def __init__(self, service: bool = False):
|
||||||
self.parser = AprsParser()
|
self.parser = AprsParser()
|
||||||
workers = [
|
workers = [
|
||||||
@ -46,7 +46,7 @@ class PacketDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequency
|
|||||||
self.parser.setDialFrequency(frequency)
|
self.parser.setDialFrequency(frequency)
|
||||||
|
|
||||||
|
|
||||||
class PocsagDemodulator(SecondaryDemodulator, FixedAudioRateChain):
|
class PocsagDemodulator(ServiceDemodulator):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
workers = [
|
workers = [
|
||||||
FmDemod(),
|
FmDemod(),
|
||||||
@ -61,3 +61,28 @@ class PocsagDemodulator(SecondaryDemodulator, FixedAudioRateChain):
|
|||||||
|
|
||||||
def getFixedAudioRate(self) -> int:
|
def getFixedAudioRate(self) -> int:
|
||||||
return 48000
|
return 48000
|
||||||
|
|
||||||
|
|
||||||
|
class PskDemodulator(SecondaryDemodulator, SecondarySelectorChain):
|
||||||
|
def __init__(self, baudRate: float):
|
||||||
|
self.baudRate = baudRate
|
||||||
|
# this is an assumption, we will adjust in setSampleRate
|
||||||
|
self.sampleRate = 12000
|
||||||
|
secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3
|
||||||
|
workers = [
|
||||||
|
Agc(Format.COMPLEX_FLOAT),
|
||||||
|
TimingRecovery(secondary_samples_per_bits, 0.5, 2, useQ=True),
|
||||||
|
DBPskDecoder(),
|
||||||
|
VaricodeDecoder(),
|
||||||
|
]
|
||||||
|
super().__init__(workers)
|
||||||
|
|
||||||
|
def getBandwidth(self):
|
||||||
|
return self.baudRate
|
||||||
|
|
||||||
|
def setSampleRate(self, sampleRate: int) -> None:
|
||||||
|
if sampleRate == self.sampleRate:
|
||||||
|
return
|
||||||
|
self.sampleRate = sampleRate
|
||||||
|
secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3
|
||||||
|
self.replace(1, TimingRecovery(secondary_samples_per_bits, 0.5, 2, useQ=True))
|
||||||
|
@ -62,10 +62,12 @@ class Decimator(Chain):
|
|||||||
|
|
||||||
|
|
||||||
class Selector(Chain):
|
class Selector(Chain):
|
||||||
def __init__(self, inputRate: int, outputRate: int, shiftRate: float, withSquelch: bool = True):
|
def __init__(self, inputRate: int, outputRate: int, withSquelch: bool = True):
|
||||||
|
self.inputRate = inputRate
|
||||||
self.outputRate = outputRate
|
self.outputRate = outputRate
|
||||||
|
self.frequencyOffset = 0
|
||||||
|
|
||||||
self.shift = Shift(shiftRate)
|
self.shift = Shift(0.0)
|
||||||
|
|
||||||
self.decimation = Decimator(inputRate, outputRate)
|
self.decimation = Decimator(inputRate, outputRate)
|
||||||
|
|
||||||
@ -88,8 +90,15 @@ class Selector(Chain):
|
|||||||
bp_transition = 320.0 / self.outputRate
|
bp_transition = 320.0 / self.outputRate
|
||||||
return Bandpass(transition=bp_transition, use_fft=True)
|
return Bandpass(transition=bp_transition, use_fft=True)
|
||||||
|
|
||||||
def setShiftRate(self, rate: float) -> None:
|
def setFrequencyOffset(self, offset: int) -> None:
|
||||||
self.shift.setRate(rate)
|
if offset == self.frequencyOffset:
|
||||||
|
return
|
||||||
|
self.frequencyOffset = offset
|
||||||
|
self._updateShift()
|
||||||
|
|
||||||
|
def _updateShift(self):
|
||||||
|
shift = -self.frequencyOffset / self.inputRate
|
||||||
|
self.shift.setRate(shift)
|
||||||
|
|
||||||
def _convertToLinear(self, db: float) -> float:
|
def _convertToLinear(self, db: float) -> float:
|
||||||
return float(math.pow(10, db / 10))
|
return float(math.pow(10, db / 10))
|
||||||
@ -125,4 +134,27 @@ class Selector(Chain):
|
|||||||
self.replace(2, self.bandpass)
|
self.replace(2, self.bandpass)
|
||||||
|
|
||||||
def setInputRate(self, inputRate: int) -> None:
|
def setInputRate(self, inputRate: int) -> None:
|
||||||
|
if inputRate == self.inputRate:
|
||||||
|
return
|
||||||
|
self.inputRate = inputRate
|
||||||
self.decimation.setInputRate(inputRate)
|
self.decimation.setInputRate(inputRate)
|
||||||
|
self._updateShift()
|
||||||
|
|
||||||
|
|
||||||
|
class SecondarySelector(Chain):
|
||||||
|
def __init__(self, sampleRate: int, bandwidth: float):
|
||||||
|
self.sampleRate = sampleRate
|
||||||
|
self.frequencyOffset = 0
|
||||||
|
self.shift = Shift(0.0)
|
||||||
|
cutoffRate = bandwidth / sampleRate
|
||||||
|
self.bandpass = Bandpass(-cutoffRate, cutoffRate, cutoffRate, use_fft=True)
|
||||||
|
workers = [self.shift, self.bandpass]
|
||||||
|
super().__init__(workers)
|
||||||
|
|
||||||
|
def setFrequencyOffset(self, offset: int) -> None:
|
||||||
|
if offset == self.frequencyOffset:
|
||||||
|
return
|
||||||
|
self.frequencyOffset = offset
|
||||||
|
if self.frequencyOffset is None:
|
||||||
|
return
|
||||||
|
self.shift.setRate(-offset / self.sampleRate)
|
||||||
|
60
owrx/dsp.py
60
owrx/dsp.py
@ -3,8 +3,8 @@ from owrx.property import PropertyStack, PropertyLayer, PropertyValidator
|
|||||||
from owrx.property.validators import OrValidator, RegexValidator, BoolValidator
|
from owrx.property.validators import OrValidator, RegexValidator, BoolValidator
|
||||||
from owrx.modes import Modes
|
from owrx.modes import Modes
|
||||||
from csdr.chain import Chain
|
from csdr.chain import Chain
|
||||||
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, SecondaryDemodulator, DialFrequencyReceiver, MetaProvider, SlotFilterChain
|
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, SecondaryDemodulator, DialFrequencyReceiver, MetaProvider, SlotFilterChain, SecondarySelectorChain
|
||||||
from csdr.chain.selector import Selector
|
from csdr.chain.selector import Selector, SecondarySelector
|
||||||
from csdr.chain.clientaudio import ClientAudioChain
|
from csdr.chain.clientaudio import ClientAudioChain
|
||||||
from csdr.chain.fft import FftChain
|
from csdr.chain.fft import FftChain
|
||||||
from pycsdr.modules import Buffer, Writer
|
from pycsdr.modules import Buffer, Writer
|
||||||
@ -25,7 +25,7 @@ class ClientDemodulatorChain(Chain):
|
|||||||
self.sampleRate = sampleRate
|
self.sampleRate = sampleRate
|
||||||
self.outputRate = outputRate
|
self.outputRate = outputRate
|
||||||
self.hdOutputRate = hdOutputRate
|
self.hdOutputRate = hdOutputRate
|
||||||
self.selector = Selector(sampleRate, outputRate, 0.0)
|
self.selector = Selector(sampleRate, outputRate)
|
||||||
self.selector.setBandpass(-4000, 4000)
|
self.selector.setBandpass(-4000, 4000)
|
||||||
self.selectorBuffer = Buffer(Format.COMPLEX_FLOAT)
|
self.selectorBuffer = Buffer(Format.COMPLEX_FLOAT)
|
||||||
self.audioBuffer = None
|
self.audioBuffer = None
|
||||||
@ -41,6 +41,8 @@ class ClientDemodulatorChain(Chain):
|
|||||||
self.secondaryFftWriter = None
|
self.secondaryFftWriter = None
|
||||||
self.secondaryWriter = None
|
self.secondaryWriter = None
|
||||||
self.squelchLevel = -150
|
self.squelchLevel = -150
|
||||||
|
self.secondarySelector = None
|
||||||
|
self.secondaryFrequencyOffset = None
|
||||||
super().__init__([self.selector, self.demodulator, self.clientAudioChain])
|
super().__init__([self.selector, self.demodulator, self.clientAudioChain])
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
@ -131,8 +133,20 @@ class ClientDemodulatorChain(Chain):
|
|||||||
self._updateDialFrequency()
|
self._updateDialFrequency()
|
||||||
self._syncSquelch()
|
self._syncSquelch()
|
||||||
|
|
||||||
|
if isinstance(self.secondaryDemodulator, SecondarySelectorChain):
|
||||||
|
self.secondarySelector = SecondarySelector(rate, self.secondaryDemodulator.getBandwidth())
|
||||||
|
self.secondarySelector.setReader(self.selectorBuffer.getReader())
|
||||||
|
self.secondarySelector.setFrequencyOffset(self.secondaryFrequencyOffset)
|
||||||
|
else:
|
||||||
|
self.secondarySelector = None
|
||||||
|
|
||||||
if self.secondaryDemodulator is not None:
|
if self.secondaryDemodulator is not None:
|
||||||
if self.secondaryDemodulator.getInputFormat() is Format.COMPLEX_FLOAT:
|
self.secondaryDemodulator.setSampleRate(rate)
|
||||||
|
if self.secondarySelector is not None:
|
||||||
|
buffer = Buffer(Format.COMPLEX_FLOAT)
|
||||||
|
self.secondarySelector.setWriter(buffer)
|
||||||
|
self.secondaryDemodulator.setReader(buffer.getReader())
|
||||||
|
elif self.secondaryDemodulator.getInputFormat() is Format.COMPLEX_FLOAT:
|
||||||
self.secondaryDemodulator.setReader(self.selectorBuffer.getReader())
|
self.secondaryDemodulator.setReader(self.selectorBuffer.getReader())
|
||||||
else:
|
else:
|
||||||
self.secondaryDemodulator.setReader(self.audioBuffer.getReader())
|
self.secondaryDemodulator.setReader(self.audioBuffer.getReader())
|
||||||
@ -170,10 +184,7 @@ class ClientDemodulatorChain(Chain):
|
|||||||
if offset == self.frequencyOffset:
|
if offset == self.frequencyOffset:
|
||||||
return
|
return
|
||||||
self.frequencyOffset = offset
|
self.frequencyOffset = offset
|
||||||
|
self.selector.setFrequencyOffset(offset)
|
||||||
shift = -offset / self.sampleRate
|
|
||||||
self.selector.setShiftRate(shift)
|
|
||||||
|
|
||||||
self._updateDialFrequency()
|
self._updateDialFrequency()
|
||||||
|
|
||||||
def setCenterFrequency(self, frequency: int) -> None:
|
def setCenterFrequency(self, frequency: int) -> None:
|
||||||
@ -211,6 +222,8 @@ class ClientDemodulatorChain(Chain):
|
|||||||
if not isinstance(self.demodulator, FixedIfSampleRateChain):
|
if not isinstance(self.demodulator, FixedIfSampleRateChain):
|
||||||
self.selector.setOutputRate(outputRate)
|
self.selector.setOutputRate(outputRate)
|
||||||
self.demodulator.setSampleRate(outputRate)
|
self.demodulator.setSampleRate(outputRate)
|
||||||
|
if self.secondaryDemodulator is not None:
|
||||||
|
self.secondaryDemodulator.setSampleRate(outputRate)
|
||||||
if not isinstance(self.demodulator, FixedAudioRateChain):
|
if not isinstance(self.demodulator, FixedAudioRateChain):
|
||||||
self.clientAudioChain.setClientRate(outputRate)
|
self.clientAudioChain.setClientRate(outputRate)
|
||||||
|
|
||||||
@ -268,6 +281,15 @@ class ClientDemodulatorChain(Chain):
|
|||||||
# TODO
|
# TODO
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def setSecondaryFrequencyOffset(self, freq: int) -> None:
|
||||||
|
if self.secondaryFrequencyOffset == freq:
|
||||||
|
return
|
||||||
|
self.secondaryFrequencyOffset = freq
|
||||||
|
|
||||||
|
if self.secondarySelector is None:
|
||||||
|
return
|
||||||
|
self.secondarySelector.setFrequencyOffset(self.secondaryFrequencyOffset)
|
||||||
|
|
||||||
|
|
||||||
class ModulationValidator(OrValidator):
|
class ModulationValidator(OrValidator):
|
||||||
"""
|
"""
|
||||||
@ -397,8 +419,6 @@ class DspManager(SdrSourceEventClient):
|
|||||||
self.props.wireProperty("dmr_filter", self.chain.setSlotFilter),
|
self.props.wireProperty("dmr_filter", self.chain.setSlotFilter),
|
||||||
# TODO
|
# TODO
|
||||||
# self.props.wireProperty("wfm_deemphasis_tau", self.dsp.set_wfm_deemphasis_tau),
|
# self.props.wireProperty("wfm_deemphasis_tau", self.dsp.set_wfm_deemphasis_tau),
|
||||||
# TODO
|
|
||||||
# self.props.wireProperty("digital_voice_codecserver", self.dsp.set_codecserver),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
@ -414,8 +434,7 @@ class DspManager(SdrSourceEventClient):
|
|||||||
self.subscriptions += [
|
self.subscriptions += [
|
||||||
self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator),
|
self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator),
|
||||||
self.props.wireProperty("digimodes_fft_size", self.chain.setSecondaryFftSize),
|
self.props.wireProperty("digimodes_fft_size", self.chain.setSecondaryFftSize),
|
||||||
# TODO
|
self.props.wireProperty("secondary_offset_freq", self.chain.setSecondaryFrequencyOffset),
|
||||||
# self.props.wireProperty("secondary_offset_freq", self.dsp.set_secondary_offset_freq),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
self.startOnAvailable = False
|
self.startOnAvailable = False
|
||||||
@ -424,7 +443,7 @@ class DspManager(SdrSourceEventClient):
|
|||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def _getDemodulator(self, demod: Union[str, BaseDemodulatorChain]):
|
def _getDemodulator(self, demod: Union[str, BaseDemodulatorChain]) -> Optional[BaseDemodulatorChain]:
|
||||||
if isinstance(demod, BaseDemodulatorChain):
|
if isinstance(demod, BaseDemodulatorChain):
|
||||||
return demod
|
return demod
|
||||||
# TODO: move this to Modes
|
# TODO: move this to Modes
|
||||||
@ -486,7 +505,7 @@ class DspManager(SdrSourceEventClient):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def _getSecondaryDemodulator(self, mod):
|
def _getSecondaryDemodulator(self, mod) -> Optional[SecondaryDemodulator]:
|
||||||
if isinstance(mod, SecondaryDemodulator):
|
if isinstance(mod, SecondaryDemodulator):
|
||||||
return mod
|
return mod
|
||||||
# TODO add remaining modes
|
# TODO add remaining modes
|
||||||
@ -504,7 +523,12 @@ class DspManager(SdrSourceEventClient):
|
|||||||
elif mod == "pocsag":
|
elif mod == "pocsag":
|
||||||
from csdr.chain.digimodes import PocsagDemodulator
|
from csdr.chain.digimodes import PocsagDemodulator
|
||||||
return PocsagDemodulator()
|
return PocsagDemodulator()
|
||||||
return None
|
elif mod == "bpsk31":
|
||||||
|
from csdr.chain.digimodes import PskDemodulator
|
||||||
|
return PskDemodulator(31.25)
|
||||||
|
elif mod == "bpsk63":
|
||||||
|
from csdr.chain.digimodes import PskDemodulator
|
||||||
|
return PskDemodulator(62.5)
|
||||||
|
|
||||||
def setSecondaryDemodulator(self, mod):
|
def setSecondaryDemodulator(self, mod):
|
||||||
demodulator = self._getSecondaryDemodulator(mod)
|
demodulator = self._getSecondaryDemodulator(mod)
|
||||||
@ -556,12 +580,16 @@ class DspManager(SdrSourceEventClient):
|
|||||||
|
|
||||||
def _unpickle(self, callback):
|
def _unpickle(self, callback):
|
||||||
def unpickler(data):
|
def unpickler(data):
|
||||||
io = BytesIO(data.tobytes())
|
b = data.tobytes()
|
||||||
|
io = BytesIO(b)
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
callback(pickle.load(io))
|
callback(pickle.load(io))
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
# TODO: this is not ideal. is there a way to know beforehand if the data will be pickled?
|
||||||
|
except pickle.UnpicklingError:
|
||||||
|
callback(b.decode("ascii"))
|
||||||
|
|
||||||
return unpickler
|
return unpickler
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ from owrx.property import PropertyLayer, PropertyDeleted
|
|||||||
from owrx.service.schedule import ServiceScheduler
|
from owrx.service.schedule import ServiceScheduler
|
||||||
from owrx.service.chain import ServiceDemodulatorChain
|
from owrx.service.chain import ServiceDemodulatorChain
|
||||||
from owrx.modes import Modes, DigitalMode
|
from owrx.modes import Modes, DigitalMode
|
||||||
from typing import Union
|
from typing import Union, Optional
|
||||||
from csdr.chain.demodulator import BaseDemodulatorChain, SecondaryDemodulator, DialFrequencyReceiver
|
from csdr.chain.demodulator import BaseDemodulatorChain, ServiceDemodulator, DialFrequencyReceiver
|
||||||
from pycsdr.modules import Buffer
|
from pycsdr.modules import Buffer
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -250,12 +250,11 @@ class ServiceHandler(SdrSourceEventClient):
|
|||||||
secondaryDemod = self._getSecondaryDemodulator(modeObject.modulation)
|
secondaryDemod = self._getSecondaryDemodulator(modeObject.modulation)
|
||||||
center_freq = source.getProps()["center_freq"]
|
center_freq = source.getProps()["center_freq"]
|
||||||
sampleRate = source.getProps()["samp_rate"]
|
sampleRate = source.getProps()["samp_rate"]
|
||||||
shift = (center_freq - frequency) / sampleRate
|
|
||||||
bandpass = modeObject.get_bandpass()
|
bandpass = modeObject.get_bandpass()
|
||||||
if isinstance(secondaryDemod, DialFrequencyReceiver):
|
if isinstance(secondaryDemod, DialFrequencyReceiver):
|
||||||
secondaryDemod.setDialFrequency(frequency)
|
secondaryDemod.setDialFrequency(frequency)
|
||||||
|
|
||||||
chain = ServiceDemodulatorChain(demod, secondaryDemod, sampleRate, shift)
|
chain = ServiceDemodulatorChain(demod, secondaryDemod, sampleRate, frequency - center_freq)
|
||||||
chain.setBandPass(bandpass.low_cut, bandpass.high_cut)
|
chain.setBandPass(bandpass.low_cut, bandpass.high_cut)
|
||||||
chain.setReader(source.getBuffer().getReader())
|
chain.setReader(source.getBuffer().getReader())
|
||||||
|
|
||||||
@ -277,8 +276,8 @@ class ServiceHandler(SdrSourceEventClient):
|
|||||||
return Ssb()
|
return Ssb()
|
||||||
|
|
||||||
# TODO move this elsewhere
|
# TODO move this elsewhere
|
||||||
def _getSecondaryDemodulator(self, mod):
|
def _getSecondaryDemodulator(self, mod) -> Optional[ServiceDemodulator]:
|
||||||
if isinstance(mod, SecondaryDemodulator):
|
if isinstance(mod, ServiceDemodulatorChain):
|
||||||
return mod
|
return mod
|
||||||
# TODO add remaining modes
|
# TODO add remaining modes
|
||||||
if mod in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]:
|
if mod in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]:
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
from csdr.chain import Chain
|
from csdr.chain import Chain
|
||||||
from csdr.chain.selector import Selector
|
from csdr.chain.selector import Selector
|
||||||
from csdr.chain.demodulator import BaseDemodulatorChain, SecondaryDemodulator, FixedAudioRateChain
|
from csdr.chain.demodulator import BaseDemodulatorChain, ServiceDemodulator
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
|
|
||||||
|
|
||||||
class ServiceDemodulatorChain(Chain):
|
class ServiceDemodulatorChain(Chain):
|
||||||
def __init__(self, demod: BaseDemodulatorChain, secondaryDemod: SecondaryDemodulator, sampleRate: int, shiftRate: float):
|
def __init__(self, demod: BaseDemodulatorChain, secondaryDemod: ServiceDemodulator, sampleRate: int, frequencyOffset: int):
|
||||||
# TODO magic number... check if this edge case even exsists and change the api if possible
|
self.selector = Selector(sampleRate, secondaryDemod.getFixedAudioRate(), withSquelch=False)
|
||||||
rate = secondaryDemod.getFixedAudioRate() if isinstance(secondaryDemod, FixedAudioRateChain) else 1200
|
self.selector.setFrequencyOffset(frequencyOffset)
|
||||||
|
|
||||||
self.selector = Selector(sampleRate, rate, shiftRate, withSquelch=False)
|
|
||||||
|
|
||||||
workers = [self.selector]
|
workers = [self.selector]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user