diff --git a/csdr/chain/analog.py b/csdr/chain/analog.py index 2a2341e6..6e0e464b 100644 --- a/csdr/chain/analog.py +++ b/csdr/chain/analog.py @@ -1,6 +1,10 @@ -from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, HdAudio, DeemphasisTauChain -from pycsdr.modules import AmDemod, DcBlock, FmDemod, Limit, NfmDeemphasis, Agc, WfmDeemphasis, FractionalDecimator, RealPart +from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, HdAudio, DeemphasisTauChain, MetaProvider +from pycsdr.modules import AmDemod, DcBlock, FmDemod, Limit, NfmDeemphasis, Agc, WfmDeemphasis, FractionalDecimator, \ + RealPart, Writer, Buffer from pycsdr.types import Format, AgcProfile +from csdr.chain.redsea import Redsea +from typing import Optional +from owrx.feature import FeatureDetector class Am(BaseDemodulatorChain): @@ -38,18 +42,27 @@ class NFm(BaseDemodulatorChain): self.replace(2, NfmDeemphasis(sampleRate)) -class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAudio): +class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAudio, MetaProvider): def __init__(self, sampleRate: int, tau: float): self.sampleRate = sampleRate self.tau = tau + self.limit = Limit() + # this buffer is used to tap into the raw audio stream for redsea RDS decoding + self.metaTapBuffer = Buffer(Format.FLOAT) workers = [ FmDemod(), - Limit(), + self.limit, FractionalDecimator(Format.FLOAT, 200000.0 / self.sampleRate, prefilter=True), WfmDeemphasis(self.sampleRate, self.tau), ] + self.metaChain = None super().__init__(workers) + def _connect(self, w1, w2, buffer: Optional[Buffer] = None) -> None: + if w1 is self.limit: + buffer = self.metaTapBuffer + super()._connect(w1, w2, buffer) + def getFixedIfSampleRate(self): return 200000 @@ -66,6 +79,14 @@ class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAu self.replace(2, FractionalDecimator(Format.FLOAT, 200000.0 / self.sampleRate, prefilter=True)) self.replace(3, WfmDeemphasis(self.sampleRate, self.tau)) + def setMetaWriter(self, writer: Writer) -> None: + if not FeatureDetector().is_available("redsea"): + return + if self.metaChain is None: + self.metaChain = Redsea(self.getFixedIfSampleRate()) + self.metaChain.setReader(self.metaTapBuffer.getReader()) + self.metaChain.setWriter(writer) + class Ssb(BaseDemodulatorChain): def __init__(self): diff --git a/csdr/chain/redsea.py b/csdr/chain/redsea.py new file mode 100644 index 00000000..1af492e4 --- /dev/null +++ b/csdr/chain/redsea.py @@ -0,0 +1,14 @@ +from csdr.chain import Chain +from pycsdr.modules import Convert +from pycsdr.types import Format +from owrx.rds.redsea import RedseaModule +from csdr.module import JsonParser + + +class Redsea(Chain): + def __init__(self, sampleRate: int): + super().__init__([ + Convert(Format.FLOAT, Format.SHORT), + RedseaModule(sampleRate), + JsonParser("RDS"), + ]) diff --git a/owrx/feature.py b/owrx/feature.py index 92c03e57..c38b0396 100644 --- a/owrx/feature.py +++ b/owrx/feature.py @@ -89,6 +89,7 @@ class FeatureDetector(object): "ism": ["rtl_433"], "dumphfdl": ["dumphfdl"], "dumpvdl2": ["dumpvdl2"], + "redsea": ["redsea"], } def feature_availability(self): @@ -636,3 +637,14 @@ class FeatureDetector(object): `dumpvdl2`. """ return self.command_is_runnable("dumpvdl2 --version") + + def has_redsea(self): + """ + OpenWebRX can decode RDS data on WFM broadcast station if the `redsea` decoder is available. + + You can find more information [here](https://github.com/windytan/redsea) + + If you are using the OpenWebRX Debian or Ubuntu repository, you should be able to install the package + `redsea`. + """ + return self.command_is_runnable("redsea --version") diff --git a/owrx/rds/redsea.py b/owrx/rds/redsea.py new file mode 100644 index 00000000..b9409773 --- /dev/null +++ b/owrx/rds/redsea.py @@ -0,0 +1,11 @@ +from pycsdr.modules import ExecModule +from pycsdr.types import Format + + +class RedseaModule(ExecModule): + def __init__(self, sampleRate: int): + super().__init__( + Format.SHORT, + Format.CHAR, + ["redsea", "--input", "mpx", "--samplerate", str(sampleRate)] + )