2024-07-10 13:49:40 +02:00
|
|
|
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, DynamicAudioRateChain, HdAudio, \
|
2024-01-26 20:50:03 +01:00
|
|
|
MetaProvider, DabServiceSelector, DialFrequencyReceiver
|
2024-01-23 21:52:41 +01:00
|
|
|
from csdr.module import PickleModule
|
2024-01-22 02:38:20 +01:00
|
|
|
from csdreti.modules import EtiDecoder
|
|
|
|
|
from owrx.dab.dablin import DablinModule
|
2024-01-24 22:37:20 +01:00
|
|
|
from pycsdr.modules import Downmix, Buffer, Shift, Writer
|
2024-01-22 02:38:20 +01:00
|
|
|
from pycsdr.types import Format
|
2024-01-23 21:52:41 +01:00
|
|
|
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
|
2024-01-26 20:50:03 +01:00
|
|
|
# carrier spacing is 1kHz, don't drift further than that.
|
|
|
|
|
self.max_shift = 1000 / 2048000
|
2024-01-23 21:52:41 +01:00
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
|
|
def process(self, data):
|
2024-01-24 22:37:20 +01:00
|
|
|
result = {}
|
2024-01-25 18:40:31 +01:00
|
|
|
for key, value in data.items():
|
|
|
|
|
if key == "coarse_frequency_shift":
|
|
|
|
|
if value > 0:
|
2024-01-26 20:50:03 +01:00
|
|
|
self._nudgeShift(random() * self.coarse_increment)
|
2024-01-25 18:40:31 +01:00
|
|
|
else:
|
2024-01-26 20:50:03 +01:00
|
|
|
self._nudgeShift(random() * -self.coarse_increment)
|
2024-01-25 18:40:31 +01:00
|
|
|
elif key == "fine_frequency_shift":
|
|
|
|
|
if abs(value) > 10:
|
2024-01-26 20:50:03 +01:00
|
|
|
self._nudgeShift(self.fine_increment * value)
|
2024-01-25 18:40:31 +01:00
|
|
|
else:
|
|
|
|
|
# pass through everything else
|
|
|
|
|
result[key] = value
|
2024-01-24 22:37:20 +01:00
|
|
|
# don't send out data if there was nothing interesting for the client
|
2024-01-24 23:01:51 +01:00
|
|
|
if not result:
|
|
|
|
|
return
|
|
|
|
|
result["mode"] = "DAB"
|
|
|
|
|
return result
|
2024-01-22 02:38:20 +01:00
|
|
|
|
2024-01-26 20:50:03 +01:00
|
|
|
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)
|
2024-01-22 02:38:20 +01:00
|
|
|
|
2024-01-26 20:50:03 +01:00
|
|
|
def resetShift(self):
|
|
|
|
|
logger.debug("resetting shift")
|
|
|
|
|
self.shift = 0
|
|
|
|
|
self.shifter.setRate(0)
|
|
|
|
|
|
|
|
|
|
|
2024-07-10 13:49:40 +02:00
|
|
|
class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, DynamicAudioRateChain, HdAudio, MetaProvider, DabServiceSelector, DialFrequencyReceiver):
|
2024-01-22 02:38:20 +01:00
|
|
|
def __init__(self):
|
2024-07-10 13:49:40 +02:00
|
|
|
self.audioRate = 48000
|
|
|
|
|
|
2024-01-23 21:52:41 +01:00
|
|
|
shift = Shift(0)
|
2024-02-12 13:53:06 +01:00
|
|
|
self.decoder = EtiDecoder()
|
2024-01-23 21:52:41 +01:00
|
|
|
|
|
|
|
|
metaBuffer = Buffer(Format.CHAR)
|
2024-02-12 13:53:06 +01:00
|
|
|
self.decoder.setMetaWriter(metaBuffer)
|
2024-01-23 21:52:41 +01:00
|
|
|
self.processor = MetaProcessor(shift)
|
|
|
|
|
self.processor.setReader(metaBuffer.getReader())
|
2024-01-24 22:37:20 +01:00
|
|
|
# use a dummy to start with. it won't run without.
|
|
|
|
|
# will be replaced by setMetaWriter().
|
2024-01-23 21:52:41 +01:00
|
|
|
self.processor.setWriter(Buffer(Format.CHAR))
|
|
|
|
|
|
2024-01-25 00:10:25 +01:00
|
|
|
self.dablin = DablinModule()
|
|
|
|
|
|
2024-01-22 02:38:20 +01:00
|
|
|
workers = [
|
2024-01-23 21:52:41 +01:00
|
|
|
shift,
|
2024-02-12 13:53:06 +01:00
|
|
|
self.decoder,
|
2024-01-25 00:10:25 +01:00
|
|
|
self.dablin,
|
2024-07-10 13:49:40 +02:00
|
|
|
Downmix(Format.SHORT),
|
2024-01-22 02:38:20 +01:00
|
|
|
]
|
|
|
|
|
super().__init__(workers)
|
|
|
|
|
|
2024-01-23 21:52:41 +01:00
|
|
|
def _connect(self, w1, w2, buffer: Optional[Buffer] = None) -> None:
|
|
|
|
|
if isinstance(w2, EtiDecoder):
|
|
|
|
|
# eti decoder needs big chunks of data
|
2024-01-26 22:54:28 +01:00
|
|
|
buffer = Buffer(w1.getOutputFormat(), size=2097152)
|
2024-01-23 21:52:41 +01:00
|
|
|
super()._connect(w1, w2, buffer)
|
|
|
|
|
|
2024-01-22 02:38:20 +01:00
|
|
|
def getFixedIfSampleRate(self) -> int:
|
|
|
|
|
return 2048000
|
|
|
|
|
|
2024-07-10 13:49:40 +02:00
|
|
|
def getAudioRate(self) -> int:
|
|
|
|
|
return self.audioRate
|
2024-01-23 21:52:41 +01:00
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
|
self.processor.stop()
|
2024-01-24 22:37:20 +01:00
|
|
|
|
|
|
|
|
def setMetaWriter(self, writer: Writer) -> None:
|
|
|
|
|
self.processor.setWriter(writer)
|
2024-01-25 00:10:25 +01:00
|
|
|
|
|
|
|
|
def setDabServiceId(self, serviceId: int) -> None:
|
2024-02-12 13:53:06 +01:00
|
|
|
self.decoder.setServiceIdFilter([serviceId])
|
2024-01-25 00:10:25 +01:00
|
|
|
self.dablin.setDabServiceId(serviceId)
|
2024-01-26 20:50:03 +01:00
|
|
|
|
|
|
|
|
def setDialFrequency(self, frequency: int) -> None:
|
|
|
|
|
self.processor.resetShift()
|