restore PSK decoding
This commit is contained in:
		@@ -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]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user