diff --git a/csdr/chain/analog.py b/csdr/chain/analog.py index d35c6908..0f1b3a7c 100644 --- a/csdr/chain/analog.py +++ b/csdr/chain/analog.py @@ -1,6 +1,11 @@ -from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, HdAudio, DeemphasisTauChain -from pycsdr.modules import AmDemod, DcBlock, FmDemod, Limit, NfmDeemphasis, Agc, Afc, WfmDeemphasis, FractionalDecimator, RealPart +from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, HdAudio, \ + DeemphasisTauChain, MetaProvider, RdsChain +from pycsdr.modules import AmDemod, DcBlock, FmDemod, Limit, NfmDeemphasis, Agc, Afc, \ + 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 +43,29 @@ class NFm(BaseDemodulatorChain): self.replace(2, NfmDeemphasis(sampleRate)) -class WFm(BaseDemodulatorChain, FixedIfSampleRateChain, DeemphasisTauChain, HdAudio): - 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) workers = [ FmDemod(), - Limit(), + self.limit, FractionalDecimator(Format.FLOAT, 200000.0 / self.sampleRate, prefilter=True), WfmDeemphasis(self.sampleRate, self.tau), ] + self.metaChain = None + self.metaWriter = 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 +82,30 @@ 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("rds"): + return + if self.metaChain is None: + self.metaChain = Redsea(self.getFixedIfSampleRate(), self.rdsRbds) + self.metaChain.setReader(self.metaTapBuffer.getReader()) + 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): def __init__(self): diff --git a/csdr/chain/redsea.py b/csdr/chain/redsea.py new file mode 100644 index 00000000..3e625ef6 --- /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 csdr.module.toolbox import RedseaModule +from csdr.module import JsonParser + + +class Redsea(Chain): + def __init__(self, sampleRate: int, rbds: bool): + super().__init__([ + Convert(Format.FLOAT, Format.SHORT), + RedseaModule(sampleRate, rbds), + JsonParser("WFM"), + ]) diff --git a/owrx/feature.py b/owrx/feature.py index 1292b612..97248419 100644 --- a/owrx/feature.py +++ b/owrx/feature.py @@ -93,7 +93,7 @@ class FeatureDetector(object): "page": ["multimon"], "selcall": ["multimon"], "rds": ["redsea"], - "dab": ["csdreti", "dablin"] + "dab": ["csdreti", "dablin"], "png": ["imagemagick"], } diff --git a/owrx/map.py b/owrx/map.py index 03913958..81f28690 100644 --- a/owrx/map.py +++ b/owrx/map.py @@ -1,6 +1,7 @@ from datetime import datetime, timedelta, timezone from owrx.config import Config from owrx.bands import Band +from abc import abstractmethod, ABC, ABCMeta import threading import time import sys diff --git a/owrx/markers.py b/owrx/markers.py index f3a421e7..d4c9e3df 100644 --- a/owrx/markers.py +++ b/owrx/markers.py @@ -6,7 +6,7 @@ from owrx.aprs import getSymbolData from owrx.eibi import EIBI from owrx.repeaters import Repeaters from json import JSONEncoder -from datetime import datetime +from datetime import datetime, timedelta import urllib import threading @@ -169,10 +169,13 @@ class Markers(object): map.removeLocation(key) del self.txmarkers[key] + # Create a timestamp far into the future, for permanent markers + permanent = datetime.utcnow() + timedelta(weeks=500) + # Update station markers that have transmissions for key in tx.keys(): r = tx[key] - map.updateLocation(r.getId(), r, r.getMode(), permanent=True) + map.updateLocation(r.getId(), r, r.getMode(), permanent) self.txmarkers[key] = r # Done with the schedule @@ -197,7 +200,7 @@ class Markers(object): # Update receiver markers that are online for key in rx.keys(): r = rx[key] - map.updateLocation(r.getId(), r, r.getMode(), permanent=True) + map.updateLocation(r.getId(), r, r.getMode(), permanent) self.rxmarkers[key] = r # Done updating receivers norx = None @@ -242,7 +245,7 @@ class Markers(object): # Update given markers on the map def updateMap(self, markers): for r in markers.values(): - Map.getSharedInstance().updateLocation(r.getId(), r, r.getMode(), permanent=True) + Map.getSharedInstance().updateLocation(r.getId(), r, r.getMode(), permanent) # Scrape online databases, updating cache file def updateCache(self):