from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, \ MetaProvider, DabServiceSelector, DialFrequencyReceiver from csdr.module import PickleModule from csdreti.modules import EtiDecoder from owrx.dab.dablin import DablinModule from pycsdr.modules import Downmix, Buffer, Shift, Writer from pycsdr.types import Format from typing import Optional from random import random import logging logger = logging.getLogger(__name__) class MetaProcessor(PickleModule): def __init__(self, shifter: Shift): self.shifter = shifter self.shift = 0.0 self.coarse_increment = -32 / 2048000 self.fine_increment = - (1/3) / 2048000 # carrier spacing is 1kHz, don't drift further than that. self.max_shift = 1000 / 2048000 super().__init__() def process(self, data): result = {} for key, value in data.items(): if key == "coarse_frequency_shift": if value > 0: self._nudgeShift(random() * self.coarse_increment) else: self._nudgeShift(random() * -self.coarse_increment) elif key == "fine_frequency_shift": if abs(value) > 10: logger.debug("ffs: %f", value) self._nudgeShift(self.fine_increment * value) else: # pass through everything else result[key] = value # don't send out data if there was nothing interesting for the client if not result: return result["mode"] = "DAB" return result def _nudgeShift(self, amount): self.shift += amount if self.shift > self.max_shift: self.shift = self.max_shift elif self.shift < -self.max_shift: self.shift = -self.max_shift logger.debug("new shift: %f", self.shift) self.shifter.setRate(self.shift) def resetShift(self): logger.debug("resetting shift") self.shift = 0 self.shifter.setRate(0) class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, MetaProvider, DabServiceSelector, DialFrequencyReceiver): def __init__(self): shift = Shift(0) self.decoder = EtiDecoder() metaBuffer = Buffer(Format.CHAR) self.decoder.setMetaWriter(metaBuffer) self.processor = MetaProcessor(shift) self.processor.setReader(metaBuffer.getReader()) # use a dummy to start with. it won't run without. # will be replaced by setMetaWriter(). self.processor.setWriter(Buffer(Format.CHAR)) self.dablin = DablinModule() workers = [ shift, self.decoder, self.dablin, Downmix(Format.FLOAT), ] super().__init__(workers) def _connect(self, w1, w2, buffer: Optional[Buffer] = None) -> None: if isinstance(w2, EtiDecoder): # eti decoder needs big chunks of data buffer = Buffer(w1.getOutputFormat(), size=2097152) super()._connect(w1, w2, buffer) def getFixedIfSampleRate(self) -> int: return 2048000 def getFixedAudioRate(self) -> int: return 48000 def stop(self): self.processor.stop() def setMetaWriter(self, writer: Writer) -> None: self.processor.setWriter(writer) def setDabServiceId(self, serviceId: int) -> None: self.decoder.setServiceIdFilter([serviceId]) self.dablin.setDabServiceId(serviceId) def setDialFrequency(self, frequency: int) -> None: self.processor.resetShift()