mirror of
https://github.com/ha7ilm/openwebrx.git
synced 2026-01-23 00:30:21 +01:00
add config option for rbds decoding (US)
This commit is contained in:
parent
91a7612d6b
commit
8b0b05e31d
|
|
@ -1,4 +1,5 @@
|
|||
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, HdAudio, DeemphasisTauChain, MetaProvider
|
||||
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, HdAudio, DeemphasisTauChain, \
|
||||
MetaProvider, RdsChain
|
||||
from pycsdr.modules import AmDemod, DcBlock, FmDemod, Limit, NfmDeemphasis, Agc, WfmDeemphasis, FractionalDecimator, \
|
||||
RealPart, Writer, Buffer
|
||||
from pycsdr.types import Format, AgcProfile
|
||||
|
|
@ -42,10 +43,11 @@ class NFm(BaseDemodulatorChain):
|
|||
self.replace(2, NfmDeemphasis(sampleRate))
|
||||
|
||||
|
||||
class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAudio, MetaProvider):
|
||||
def __init__(self, sampleRate: int, tau: float):
|
||||
class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAudio, MetaProvider, RdsChain):
|
||||
def __init__(self, sampleRate: int, tau: float, rdsRbds: bool):
|
||||
self.sampleRate = sampleRate
|
||||
self.tau = tau
|
||||
self.rdsRbds = rdsRbds
|
||||
self.limit = Limit()
|
||||
# this buffer is used to tap into the raw audio stream for redsea RDS decoding
|
||||
self.metaTapBuffer = Buffer(Format.FLOAT)
|
||||
|
|
@ -56,6 +58,7 @@ class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAu
|
|||
WfmDeemphasis(self.sampleRate, self.tau),
|
||||
]
|
||||
self.metaChain = None
|
||||
self.metaWriter = None
|
||||
super().__init__(workers)
|
||||
|
||||
def _connect(self, w1, w2, buffer: Optional[Buffer] = None) -> None:
|
||||
|
|
@ -83,15 +86,25 @@ class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAu
|
|||
if not FeatureDetector().is_available("redsea"):
|
||||
return
|
||||
if self.metaChain is None:
|
||||
self.metaChain = Redsea(self.getFixedIfSampleRate())
|
||||
self.metaChain = Redsea(self.getFixedIfSampleRate(), self.rdsRbds)
|
||||
self.metaChain.setReader(self.metaTapBuffer.getReader())
|
||||
self.metaChain.setWriter(writer)
|
||||
self.metaWriter = writer
|
||||
self.metaChain.setWriter(self.metaWriter)
|
||||
|
||||
def stop(self):
|
||||
super().stop()
|
||||
if self.metaChain is not None:
|
||||
self.metaChain.stop()
|
||||
self.metaChain = None
|
||||
self.metaWriter = None
|
||||
|
||||
def setRdsRbds(self, rdsRbds: bool) -> None:
|
||||
self.rdsRbds = rdsRbds
|
||||
if self.metaChain is not None:
|
||||
self.metaChain.stop()
|
||||
self.metaChain = Redsea(self.getFixedIfSampleRate(), self.rdsRbds)
|
||||
self.metaChain.setReader(self.metaTapBuffer.getReader())
|
||||
self.metaChain.setWriter(self.metaWriter)
|
||||
|
||||
|
||||
class Ssb(BaseDemodulatorChain):
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ class DeemphasisTauChain(ABC):
|
|||
pass
|
||||
|
||||
|
||||
class RdsChain(ABC):
|
||||
@abstractmethod
|
||||
def setRdsRbds(self, rdsRbds: bool) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class BaseDemodulatorChain(Chain):
|
||||
def supportsSquelch(self) -> bool:
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ from csdr.module import JsonParser
|
|||
|
||||
|
||||
class Redsea(Chain):
|
||||
def __init__(self, sampleRate: int):
|
||||
def __init__(self, sampleRate: int, rbds: bool):
|
||||
super().__init__([
|
||||
Convert(Format.FLOAT, Format.SHORT),
|
||||
RedseaModule(sampleRate),
|
||||
RedseaModule(sampleRate, rbds),
|
||||
JsonParser("WFM"),
|
||||
])
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ defaultConfig = PropertyLayer(
|
|||
audio_compression="adpcm",
|
||||
fft_compression="adpcm",
|
||||
wfm_deemphasis_tau=50e-6,
|
||||
wfm_rds_rbds=False,
|
||||
digimodes_fft_size=2048,
|
||||
digital_voice_dmr_id_lookup=True,
|
||||
digital_voice_nxdn_id_lookup=True,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ class DecodingSettingsController(SettingsFormController):
|
|||
infotext='See <a href="https://en.wikipedia.org/wiki/FM_broadcasting#Pre-emphasis_and_de-emphasis"'
|
||||
+ ' target="_blank">this Wikipedia article</a> for more information',
|
||||
),
|
||||
CheckboxInput(
|
||||
"wfm_rds_rbds",
|
||||
"Enable RBDS decoding (US RDS standard)",
|
||||
),
|
||||
),
|
||||
Section(
|
||||
"Digital voice",
|
||||
|
|
|
|||
17
owrx/dsp.py
17
owrx/dsp.py
|
|
@ -3,7 +3,7 @@ from owrx.property import PropertyStack, PropertyLayer, PropertyValidator, Prope
|
|||
from owrx.property.validators import OrValidator, RegexValidator, BoolValidator
|
||||
from owrx.modes import Modes, DigitalMode
|
||||
from csdr.chain import Chain
|
||||
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, SecondaryDemodulator, DialFrequencyReceiver, MetaProvider, SlotFilterChain, SecondarySelectorChain, DeemphasisTauChain, DemodulatorError
|
||||
from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, SecondaryDemodulator, DialFrequencyReceiver, MetaProvider, SlotFilterChain, SecondarySelectorChain, DeemphasisTauChain, DemodulatorError, RdsChain
|
||||
from csdr.chain.selector import Selector, SecondarySelector
|
||||
from csdr.chain.clientaudio import ClientAudioChain
|
||||
from csdr.chain.fft import FftChain
|
||||
|
|
@ -47,6 +47,7 @@ class ClientDemodulatorChain(Chain):
|
|||
self.centerFrequency = None
|
||||
self.frequencyOffset = None
|
||||
self.wfmDeemphasisTau = 50e-6
|
||||
self.rdsRbds = False
|
||||
inputRate = demod.getFixedAudioRate() if isinstance(demod, FixedAudioRateChain) else outputRate
|
||||
oRate = hdOutputRate if isinstance(demod, HdAudio) else outputRate
|
||||
self.clientAudioChain = ClientAudioChain(demod.getOutputFormat(), inputRate, oRate, audioCompression)
|
||||
|
|
@ -109,6 +110,9 @@ class ClientDemodulatorChain(Chain):
|
|||
if isinstance(self.demodulator, DeemphasisTauChain):
|
||||
self.demodulator.setDeemphasisTau(self.wfmDeemphasisTau)
|
||||
|
||||
if isinstance(self.demodulator, RdsChain):
|
||||
self.demodulator.setRdsRbds(self.rdsRbds)
|
||||
|
||||
self._updateDialFrequency()
|
||||
self._syncSquelch()
|
||||
|
||||
|
|
@ -377,6 +381,13 @@ class ClientDemodulatorChain(Chain):
|
|||
if isinstance(self.demodulator, DeemphasisTauChain):
|
||||
self.demodulator.setDeemphasisTau(self.wfmDeemphasisTau)
|
||||
|
||||
def setRdsRbds(self, rdsRbds: bool) -> None:
|
||||
if rdsRbds == self.rdsRbds:
|
||||
return
|
||||
self.rdsRbds = rdsRbds
|
||||
if isinstance(self.demodulator, RdsChain):
|
||||
self.demodulator.setRdsRbds(self.rdsRbds)
|
||||
|
||||
|
||||
class ModulationValidator(OrValidator):
|
||||
"""
|
||||
|
|
@ -427,6 +438,7 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||
"start_mod",
|
||||
"start_freq",
|
||||
"wfm_deemphasis_tau",
|
||||
"wfm_rds_rbds",
|
||||
"digital_voice_codecserver",
|
||||
),
|
||||
)
|
||||
|
|
@ -491,6 +503,7 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||
self.props.wireProperty("mod", self.setDemodulator),
|
||||
self.props.wireProperty("dmr_filter", self.chain.setSlotFilter),
|
||||
self.props.wireProperty("wfm_deemphasis_tau", self.chain.setWfmDeemphasisTau),
|
||||
self.props.wireProperty("wfm_rds_rbds", self.chain.setRdsRbds),
|
||||
self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator),
|
||||
self.props.wireProperty("secondary_offset_freq", self.chain.setSecondaryFrequencyOffset),
|
||||
]
|
||||
|
|
@ -532,7 +545,7 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
|
|||
return NFm(self.props["output_rate"])
|
||||
elif demod == "wfm":
|
||||
from csdr.chain.analog import WFm
|
||||
return WFm(self.props["hd_output_rate"], self.props["wfm_deemphasis_tau"])
|
||||
return WFm(self.props["hd_output_rate"], self.props["wfm_deemphasis_tau"], self.props["wfm_rds_rbds"])
|
||||
elif demod == "am":
|
||||
from csdr.chain.analog import Am
|
||||
return Am()
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@ from pycsdr.types import Format
|
|||
|
||||
|
||||
class RedseaModule(ExecModule):
|
||||
def __init__(self, sampleRate: int):
|
||||
def __init__(self, sampleRate: int, rbds: bool):
|
||||
args = ["redsea", "--input", "mpx", "--samplerate", str(sampleRate)]
|
||||
if rbds:
|
||||
args += ["--rbds"]
|
||||
super().__init__(
|
||||
Format.SHORT,
|
||||
Format.CHAR,
|
||||
["redsea", "--input", "mpx", "--samplerate", str(sampleRate)]
|
||||
args
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue