From 1fb0f249566def63031829083aa5bb2277a4ed5a Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 30 Jun 2023 11:58:05 +0200 Subject: [PATCH 001/204] replace dc blocker with the csdr version --- csdr/chain/digiham.py | 4 ++-- csdr/chain/m17.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/csdr/chain/digiham.py b/csdr/chain/digiham.py index fbbd91af..abf426bc 100644 --- a/csdr/chain/digiham.py +++ b/csdr/chain/digiham.py @@ -1,7 +1,7 @@ from csdr.chain.demodulator import BaseDemodulatorChain, FixedAudioRateChain, FixedIfSampleRateChain, DialFrequencyReceiver, MetaProvider, SlotFilterChain, DemodulatorError, ServiceDemodulator -from pycsdr.modules import FmDemod, Agc, Writer, Buffer +from pycsdr.modules import FmDemod, Agc, Writer, Buffer, DcBlock from pycsdr.types import Format -from digiham.modules import DstarDecoder, DcBlock, FskDemodulator, GfskDemodulator, DigitalVoiceFilter, MbeSynthesizer, NarrowRrcFilter, NxdnDecoder, DmrDecoder, WideRrcFilter, YsfDecoder, PocsagDecoder +from digiham.modules import DstarDecoder, FskDemodulator, GfskDemodulator, DigitalVoiceFilter, MbeSynthesizer, NarrowRrcFilter, NxdnDecoder, DmrDecoder, WideRrcFilter, YsfDecoder, PocsagDecoder from digiham.ambe import Modes, ServerError from owrx.meta import MetaParser from owrx.pocsag import PocsagParser diff --git a/csdr/chain/m17.py b/csdr/chain/m17.py index a54ecd7d..937103c3 100644 --- a/csdr/chain/m17.py +++ b/csdr/chain/m17.py @@ -1,8 +1,7 @@ from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, MetaProvider from csdr.module.m17 import M17Module -from pycsdr.modules import FmDemod, Limit, Convert, Writer +from pycsdr.modules import FmDemod, Limit, Convert, Writer, DcBlock from pycsdr.types import Format -from digiham.modules import DcBlock class M17(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, MetaProvider): From b6124429963e1bbe41e52015a355dc0f6294de9c Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 30 Jun 2023 11:58:34 +0200 Subject: [PATCH 002/204] m17 no longer depends on digiham --- owrx/feature.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/owrx/feature.py b/owrx/feature.py index 1d02022b..00d28ba7 100644 --- a/owrx/feature.py +++ b/owrx/feature.py @@ -76,7 +76,7 @@ class FeatureDetector(object): # optional features and their requirements "digital_voice_digiham": ["digiham", "codecserver_ambe"], "digital_voice_freedv": ["freedv_rx"], - "digital_voice_m17": ["m17_demod", "digiham"], + "digital_voice_m17": ["m17_demod"], "wsjt-x": ["wsjtx"], "wsjt-x-2-3": ["wsjtx_2_3"], "wsjt-x-2-4": ["wsjtx_2_4"], @@ -406,7 +406,7 @@ class FeatureDetector(object): You can find more information [here](https://github.com/mobilinkd/m17-cxx-demod) """ - return self.command_is_runnable("m17-demod") + return self.command_is_runnable("m17-demod", 0) def has_direwolf(self): """ From 7d3e90ede7c0d5c782b7b696098f34a15e2be4d7 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 30 Jun 2023 12:32:52 +0200 Subject: [PATCH 003/204] update docker dependencies --- docker/scripts/install-owrx-tools.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index ae9eb7ee..0659f981 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -32,7 +32,8 @@ popd rm -rf js8py git clone https://github.com/jketterl/csdr.git -cmakebuild csdr 0.18.1 +# latest develop as of 2023-06-30 (migrated dc block) +cmakebuild csdr ff2fddfd1347b201a70a4620970cb148a47922dd git clone https://github.com/jketterl/pycsdr.git cd pycsdr @@ -47,11 +48,13 @@ cp codecserver/conf/codecserver.conf /usr/local/etc/codecserver cmakebuild codecserver 0.2.0 git clone https://github.com/jketterl/digiham.git -cmakebuild digiham 0.6.1 +# latest develop as of 2023-06-30 (migrated dc block) +cmakebuild digiham 446c5eb15379580cb0371ef7f280643f330ba47c git clone https://github.com/jketterl/pydigiham.git cd pydigiham -git checkout 0.6.1 +# latest develop as of 2023-06-30 (migrated dc block) +git checkout 45d3afa68c8c6f2cbce62144878e7c045e87073a ./setup.py install cd .. rm -rf pydigiham From cb3fe19db9bf87e10d2b1f356f0c190de8a80d81 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Sat, 1 Jul 2023 08:28:39 +0200 Subject: [PATCH 004/204] update tools --- docker/scripts/install-connectors.sh | 4 ++-- docker/scripts/install-owrx-tools.sh | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docker/scripts/install-connectors.sh b/docker/scripts/install-connectors.sh index d67de1f9..cca21940 100755 --- a/docker/scripts/install-connectors.sh +++ b/docker/scripts/install-connectors.sh @@ -24,8 +24,8 @@ apt-get update apt-get -y install --no-install-recommends $BUILD_PACKAGES git clone https://github.com/jketterl/owrx_connector.git -# latest develop as of 2023-03-18 (added --listdriver option) -cmakebuild owrx_connector beda260363c0e77617d4e17ef864e1f5c3fd86b2 +# latest develop as of 2023-07-01 (csdr cleanup) +cmakebuild owrx_connector a01034cb5dee5b438c6943619bfb6267e2aff419 apt-get -y purge --autoremove $BUILD_PACKAGES apt-get clean diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index 0659f981..3f7cbb80 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -32,12 +32,13 @@ popd rm -rf js8py git clone https://github.com/jketterl/csdr.git -# latest develop as of 2023-06-30 (migrated dc block) -cmakebuild csdr ff2fddfd1347b201a70a4620970cb148a47922dd +# latest develop as of 2023-06-30 (csdr cleanup) +cmakebuild csdr 7e7a7f27b9fd1a65d4a9d0725583dfe6309a5245 git clone https://github.com/jketterl/pycsdr.git cd pycsdr -git checkout 0.18.1 +# latest develop as of 2023-06-30 (csdr cleanup) +git checkout be8b0e5e0b972ebb302e7397bac0058f620ec374 ./setup.py install install_headers cd .. rm -rf pycsdr @@ -48,13 +49,13 @@ cp codecserver/conf/codecserver.conf /usr/local/etc/codecserver cmakebuild codecserver 0.2.0 git clone https://github.com/jketterl/digiham.git -# latest develop as of 2023-06-30 (migrated dc block) -cmakebuild digiham 446c5eb15379580cb0371ef7f280643f330ba47c +# latest develop as of 2023-06-30 (csdr cleanup) +cmakebuild digiham ebb97eba8bff58a1d7b0e9f29f9a066e4a91da3d git clone https://github.com/jketterl/pydigiham.git cd pydigiham -# latest develop as of 2023-06-30 (migrated dc block) -git checkout 45d3afa68c8c6f2cbce62144878e7c045e87073a +# latest develop as of 2023-06-30 (csdr cleanup) +git checkout 894aa87ea9a3534d1e7109da86194c7cd5e0b7c7 ./setup.py install cd .. rm -rf pydigiham From 90955819bfb3348d47cbb66cb351a0c0464f2aec Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Sun, 2 Jul 2023 00:43:54 +0200 Subject: [PATCH 005/204] improve handling of codecserver errors --- csdr/chain/digiham.py | 7 +++++++ owrx/feature.py | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/csdr/chain/digiham.py b/csdr/chain/digiham.py index abf426bc..a17217f7 100644 --- a/csdr/chain/digiham.py +++ b/csdr/chain/digiham.py @@ -6,6 +6,10 @@ from digiham.ambe import Modes, ServerError from owrx.meta import MetaParser from owrx.pocsag import PocsagParser +import logging + +logger = logging.getLogger(__name__) + class DigihamChain(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, DialFrequencyReceiver, MetaProvider): def __init__(self, fskDemodulator, decoder, mbeMode, filter=None, codecserver: str = ""): @@ -24,6 +28,9 @@ class DigihamChain(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateC raise DemodulatorError("Connection to codecserver failed: {}".format(ce)) except ServerError as se: raise DemodulatorError("Codecserver error: {}".format(se)) + except RuntimeError as re: + logger.exception("Codecserver error while instantiating MbeSynthesizer:") + raise DemodulatorError("Fatal codecserver error. Please check receiver logs.") workers += [ fskDemodulator, decoder, diff --git a/owrx/feature.py b/owrx/feature.py index 00d28ba7..036a17f8 100644 --- a/owrx/feature.py +++ b/owrx/feature.py @@ -13,7 +13,6 @@ from datetime import datetime, timedelta import logging logger = logging.getLogger(__name__) -logger.setLevel(logging.INFO) class UnknownFeatureException(Exception): @@ -137,14 +136,12 @@ class FeatureDetector(object): if cache.has(requirement): return cache.get(requirement) - logger.debug("performing feature check for %s", requirement) method = self._get_requirement_method(requirement) result = False if method is not None: result = method() else: logger.error("detection of requirement {0} not implement. please fix in code!".format(requirement)) - logger.debug("feature check for %s complete. result: %s", requirement, result) cache.set(requirement, result) return result @@ -564,3 +561,6 @@ class FeatureDetector(object): return False except ConnectionError: return False + except RuntimeError as e: + logger.exception("Codecserver error while checking for AMBE support:") + return False From 1971edf4124cdf37e1be25007f97be6294b4271e Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Sun, 2 Jul 2023 03:41:04 +0200 Subject: [PATCH 006/204] fix switching of secondary fft compression --- owrx/dsp.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/owrx/dsp.py b/owrx/dsp.py index 62d55178..749f0b94 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -366,7 +366,7 @@ class ClientDemodulatorChain(Chain): def getSecondaryFftOutputFormat(self) -> Format: if self.secondaryFftCompression == "adpcm": return Format.CHAR - return Format.SHORT + return Format.FLOAT def setWfmDeemphasisTau(self, tau: float) -> None: if tau == self.wfmDeemphasisTau: @@ -470,7 +470,7 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) self.subscriptions = [ self.props.wireProperty("audio_compression", self.setAudioCompression), - self.props.wireProperty("fft_compression", self.chain.setSecondaryFftCompression), + self.props.wireProperty("fft_compression", self.setSecondaryFftCompression), self.props.wireProperty("fft_voverlap_factor", self.chain.setSecondaryFftOverlapFactor), self.props.wireProperty("fft_fps", self.chain.setSecondaryFftFps), self.props.wireProperty("digimodes_fft_size", self.setSecondaryFftSize), @@ -617,6 +617,17 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) self.chain.setWriter(buffer) self.wireOutput(self.audioOutput, buffer) + def setSecondaryFftCompression(self, compression): + try: + self.chain.setSecondaryFftCompression(compression) + except ValueError: + # wrong output format... need to re-wire + pass + + buffer = Buffer(self.chain.getSecondaryFftOutputFormat()) + self.chain.setSecondaryFftWriter(buffer) + self.wireOutput("secondary_fft", buffer) + def start(self): if self.sdrSource.isAvailable(): self.chain.setReader(self.sdrSource.getBuffer().getReader()) From 8543251a3f2359c6777af73aa80881dd22dde943 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Mon, 3 Jul 2023 18:13:56 +0200 Subject: [PATCH 007/204] update codecserver --- docker/scripts/install-owrx-tools.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index 3f7cbb80..405ae97a 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -46,7 +46,8 @@ rm -rf pycsdr git clone https://github.com/jketterl/codecserver.git mkdir -p /usr/local/etc/codecserver cp codecserver/conf/codecserver.conf /usr/local/etc/codecserver -cmakebuild codecserver 0.2.0 +# latest develop as of 2023-07-03 (fixed encoding) +cmakebuild codecserver 2525a96ff8ec367ed1f0f6e347a44dbe564e2456 git clone https://github.com/jketterl/digiham.git # latest develop as of 2023-06-30 (csdr cleanup) From 427e3d894e96406fb8c3eaef563e29499949c94c Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 4 Jul 2023 01:16:43 +0200 Subject: [PATCH 008/204] update codecserver --- docker/scripts/install-owrx-tools.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index 405ae97a..a3132b74 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -46,12 +46,12 @@ rm -rf pycsdr git clone https://github.com/jketterl/codecserver.git mkdir -p /usr/local/etc/codecserver cp codecserver/conf/codecserver.conf /usr/local/etc/codecserver -# latest develop as of 2023-07-03 (fixed encoding) -cmakebuild codecserver 2525a96ff8ec367ed1f0f6e347a44dbe564e2456 +# latest develop as of 2023-07-03 (error handling) +cmakebuild codecserver 0f3703ce285acd85fcd28f6620d7795dc173cb50 git clone https://github.com/jketterl/digiham.git -# latest develop as of 2023-06-30 (csdr cleanup) -cmakebuild digiham ebb97eba8bff58a1d7b0e9f29f9a066e4a91da3d +# latest develop as of 2023-07-02 (codecserver protocol version) +cmakebuild digiham 262e6dfd9a2c56778bd4b597240756ad0fb9861d git clone https://github.com/jketterl/pydigiham.git cd pydigiham From 567369969607f896c5f795a7396b085c567cb8a9 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 4 Jul 2023 18:54:14 +0200 Subject: [PATCH 009/204] introduce mouse wheel distance normalization --- htdocs/lib/FrequencyDisplay.js | 8 +------- htdocs/lib/wheelDelta.js | 21 +++++++++++++++++++++ htdocs/openwebrx.js | 17 ++++++++--------- owrx/controllers/assets.py | 1 + 4 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 htdocs/lib/wheelDelta.js diff --git a/htdocs/lib/FrequencyDisplay.js b/htdocs/lib/FrequencyDisplay.js index 7aeecc74..b6eb1964 100644 --- a/htdocs/lib/FrequencyDisplay.js +++ b/htdocs/lib/FrequencyDisplay.js @@ -98,13 +98,7 @@ TuneableFrequencyDisplay.prototype.setupEvents = function() { if (index < 0) return; var delta = 10 ** (Math.floor(Math.max(me.exponent, Math.log10(me.frequency))) - index); - var newFrequency; - if ('deltaMode' in e.originalEvent && e.originalEvent.deltaMode === 0) { - newFrequency = me.frequency - delta * (e.originalEvent.deltaY / 50); - } else { - if (e.originalEvent.deltaY > 0) delta *= -1; - newFrequency = me.frequency + delta; - } + var newFrequency = me.frequency - delta * wheelDelta(e.originalEvent); me.element.trigger('frequencychange', newFrequency); }); diff --git a/htdocs/lib/wheelDelta.js b/htdocs/lib/wheelDelta.js new file mode 100644 index 00000000..1dbe2549 --- /dev/null +++ b/htdocs/lib/wheelDelta.js @@ -0,0 +1,21 @@ +/* + * Normalize scroll wheel events. + * + * It seems like there's no consent as to how mouse wheel events are presented in the javascript API. The standard + * states that a MouseEvent has a deltaY property that contains the scroll distances, together with a deltaMode + * property that state the "unit" that deltaY has been measured in. The deltaMode can be either pixels, lines or + * pages. The latter is seldomly used in practise. + * + * The troublesome part is that there is no standard on how to correlate the two at this point. + * + * The basic idea is that one tick of a mouse wheel results in a total return value of +/- 1 from this method. + * It's important to keep in mind that one tick of a wheel may result in multiple events in the browser. The aim + * of this method is to scale the sum of deltaY over + */ +function wheelDelta(evt) { + if ('deltaMode' in evt && evt.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { + // chrome and webkit-based browsers seem to correlate one tick of the wheel to 120 pixels. + return evt.deltaY / 120; + } + return evt.deltaY; +} diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index a12e1d85..da229118 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -619,11 +619,7 @@ function get_relative_x(evt) { function canvas_mousewheel(evt) { if (!waterfall_setup_done) return; - var delta = -evt.deltaY; - // deltaMode 0 means pixels instead of lines - if ('deltaMode' in evt && evt.deltaMode === 0) { - delta /= 50; - } + var delta = -wheelDelta(evt); var relativeX = get_relative_x(evt); zoom_step(delta, relativeX, zoom_center_where_calc(evt.pageX)); evt.preventDefault(); @@ -1258,11 +1254,14 @@ function initSliders() { var $slider = $(this); if (!$slider.attr('step')) return; var val = Number($slider.val()); + // restore previous high-resolution mouse wheel delta + var mouseDelta = Number($slider.data('mouseDelta')); + if (mouseDelta) val += mouseDelta; var step = Number($slider.attr('step')); - if (ev.originalEvent.deltaY > 0) { - step *= -1; - } - $slider.val(val + step); + var newVal = val + step * -wheelDelta(ev.originalEvent); + $slider.val(newVal); + // the calculated value can have a higher resolution than the element can store, so we put the delta into the data attributes + $slider.data('mouseDelta', newVal - $slider.val()); $slider.trigger('change'); }); diff --git a/owrx/controllers/assets.py b/owrx/controllers/assets.py index 2ae1ab14..d9a273fa 100644 --- a/owrx/controllers/assets.py +++ b/owrx/controllers/assets.py @@ -119,6 +119,7 @@ class CompiledAssetsController(GzipMixin, ModificationAwareController): profiles = { "receiver.js": [ "lib/chroma.min.js", + "lib/wheelDelta.js", "openwebrx.js", "lib/jquery-3.2.1.min.js", "lib/jquery.nanoscroller.min.js", From 0abec76b79fe7d7fb1c0f0aab7345fa3101db5d6 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 4 Jul 2023 23:00:26 +0200 Subject: [PATCH 010/204] add config command line argument --- owrx/__main__.py | 11 +++++++++++ owrx/config/core.py | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/owrx/__main__.py b/owrx/__main__.py index 4c791ea1..468d4912 100644 --- a/owrx/__main__.py +++ b/owrx/__main__.py @@ -20,6 +20,7 @@ from owrx.reporting import ReportingEngine from owrx.version import openwebrx_version from owrx.audio.queue import DecoderQueue from owrx.admin import add_admin_parser, run_admin_action +from pathlib import Path import signal import argparse import socket @@ -44,6 +45,14 @@ def handleSignal(sig, frame): def main(): parser = argparse.ArgumentParser(description="OpenWebRX - Open Source SDR Web App for Everyone!") + parser.add_argument( + "-c", + "--config", + action="store", + help="Read core configuration from specified file", + metavar="configfile", + type=Path, + ) parser.add_argument("-v", "--version", action="store_true", help="Show the software version") parser.add_argument("--debug", action="store_true", help="Set loglevel to DEBUG") @@ -66,6 +75,8 @@ def main(): print("OpenWebRX version {version}".format(version=openwebrx_version)) return 0 + CoreConfig.load(args.config) + if args.module == "admin": return run_admin_action(adminparser, args) diff --git a/owrx/config/core.py b/owrx/config/core.py index e6026fb4..0d8341cb 100644 --- a/owrx/config/core.py +++ b/owrx/config/core.py @@ -1,10 +1,12 @@ from owrx.config import ConfigError from configparser import ConfigParser +from pathlib import Path import os -from glob import glob class CoreConfig(object): + defaultSearchLocations = ["./openwebrx.conf", "/etc/openwebrx/openwebrx.conf"] + defaults = { "core": { "data_directory": "/var/lib/openwebrx", @@ -20,18 +22,41 @@ class CoreConfig(object): } } - def __init__(self): + sharedConfig = None + + @staticmethod + def load(file: Path = None): + + def expand_base(base: Path): + # check if config exists + if not base.exists() or not base.is_file(): + return [] + # every location can additionally have a directory containing config overrides + # this directory must have the same name, with the ".d" suffix + override_dir = Path(str(base) + ".d") + # check if override dir exists + if not override_dir.exists() or not override_dir.is_dir(): + return [base] + # load all .conf files from the override dir + overrides = override_dir.glob("*.conf") + return [base] + [o for o in overrides if o.is_file()] + + if file is None: + bases = [Path(b) for b in CoreConfig.defaultSearchLocations] + else: + bases = [file] + configFiles = [o for b in bases for o in expand_base(b)] + config = ConfigParser() # set up config defaults config.read_dict(CoreConfig.defaults) - # check for overrides - overrides_dir = "/etc/openwebrx/openwebrx.conf.d" - if os.path.exists(overrides_dir) and os.path.isdir(overrides_dir): - overrides = glob(overrides_dir + "/*.conf") - else: - overrides = [] - # sequence things together - config.read(["./openwebrx.conf", "/etc/openwebrx/openwebrx.conf"] + overrides) + # read the allocated files + config.read(configFiles) + + CoreConfig.sharedConfig = config + + def __init__(self): + config = CoreConfig.sharedConfig self.data_directory = config.get("core", "data_directory") CoreConfig.checkDirectory(self.data_directory, "data_directory") self.temporary_directory = config.get("core", "temporary_directory") From edbd576544ecf97f799bdb7cbac7e4c404b0931c Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 19 Jul 2023 18:26:55 +0200 Subject: [PATCH 011/204] update dependencies --- docker/scripts/install-connectors.sh | 4 ++-- docker/scripts/install-dependencies-runds.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/scripts/install-connectors.sh b/docker/scripts/install-connectors.sh index cca21940..413087ee 100755 --- a/docker/scripts/install-connectors.sh +++ b/docker/scripts/install-connectors.sh @@ -24,8 +24,8 @@ apt-get update apt-get -y install --no-install-recommends $BUILD_PACKAGES git clone https://github.com/jketterl/owrx_connector.git -# latest develop as of 2023-07-01 (csdr cleanup) -cmakebuild owrx_connector a01034cb5dee5b438c6943619bfb6267e2aff419 +# latest develop as of 2023-07-04 (cmake exports) +cmakebuild owrx_connector 5266c7d44be48c2583dd76cd2341b0d453c93dd7 apt-get -y purge --autoremove $BUILD_PACKAGES apt-get clean diff --git a/docker/scripts/install-dependencies-runds.sh b/docker/scripts/install-dependencies-runds.sh index e72902d4..1723bb44 100755 --- a/docker/scripts/install-dependencies-runds.sh +++ b/docker/scripts/install-dependencies-runds.sh @@ -25,8 +25,8 @@ apt-get update apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES git clone https://github.com/jketterl/runds_connector.git -# latest develop as of 2022-12-11 (std::endl implicit flushing) -cmakebuild runds_connector 06ca993a3c81ddb0a2581b1474895da07752a9e1 +# latest develop as of 2023-07-04 (cmake exports) +cmakebuild runds_connector 435364002d756735015707e7f59aa40e8d743585 apt-get -y purge --autoremove $BUILD_PACKAGES apt-get clean From 0466c76acb52af009ce0b06a5d56d22e24e827b3 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 4 Aug 2023 02:02:40 +0200 Subject: [PATCH 012/204] update codec2 due to repository migration --- docker/scripts/install-dependencies.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/scripts/install-dependencies.sh b/docker/scripts/install-dependencies.sh index ca3ffc42..f95d50f6 100755 --- a/docker/scripts/install-dependencies.sh +++ b/docker/scripts/install-dependencies.sh @@ -86,8 +86,7 @@ rm -rf /usr/local/share/doc/direwolf/examples/ git clone https://github.com/drowe67/codec2.git cd codec2 -# latest commit from master as of 2020-10-04 -git checkout 55d7bb8d1bddf881bdbfcb971a718b83e6344598 +git checkout 1.2.0 mkdir build cd build cmake .. From be3193ce50742ebc334b8d67fb12f84310cdd039 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 4 Aug 2023 02:18:29 +0200 Subject: [PATCH 013/204] add direct_sampling dropdown for rtl_tcp devices --- docker/scripts/install-connectors.sh | 4 ++-- owrx/source/rtl_tcp.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docker/scripts/install-connectors.sh b/docker/scripts/install-connectors.sh index 413087ee..e96a67ff 100755 --- a/docker/scripts/install-connectors.sh +++ b/docker/scripts/install-connectors.sh @@ -24,8 +24,8 @@ apt-get update apt-get -y install --no-install-recommends $BUILD_PACKAGES git clone https://github.com/jketterl/owrx_connector.git -# latest develop as of 2023-07-04 (cmake exports) -cmakebuild owrx_connector 5266c7d44be48c2583dd76cd2341b0d453c93dd7 +# latest develop as of 2023-08-04 (direct_sampling for rtl_tcp_connector) +cmakebuild owrx_connector 5341601af353a60d5b3c66ece8bb8ac85bfdae7d apt-get -y purge --autoremove $BUILD_PACKAGES apt-get clean diff --git a/owrx/source/rtl_tcp.py b/owrx/source/rtl_tcp.py index 6c3f7d2c..6e1a3c40 100644 --- a/owrx/source/rtl_tcp.py +++ b/owrx/source/rtl_tcp.py @@ -1,7 +1,7 @@ from owrx.source.connector import ConnectorSource, ConnectorDeviceDescription from owrx.command import Flag, Option, Argument from owrx.form.input import Input -from owrx.form.input.device import RemoteInput +from owrx.form.input.device import RemoteInput, DirectSamplingInput from typing import List @@ -26,7 +26,13 @@ class RtlTcpDeviceDescription(ConnectorDeviceDescription): return "RTL-SDR device (via rtl_tcp)" def getInputs(self) -> List[Input]: - return super().getInputs() + [RemoteInput()] + return super().getInputs() + [RemoteInput(), DirectSamplingInput()] def getDeviceMandatoryKeys(self): return super().getDeviceMandatoryKeys() + ["remote"] + + def getDeviceOptionalKeys(self): + return super().getDeviceOptionalKeys() + ["direct_sampling"] + + def getProfileOptionalKeys(self): + return super().getProfileOptionalKeys() + ["direct_sampling"] From bd1a15f3c99b7e57dd62d38c58118fbb77765a7a Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 4 Aug 2023 16:44:23 +0200 Subject: [PATCH 014/204] fix dependencies --- docker/scripts/install-connectors.sh | 3 ++- docker/scripts/install-dependencies-runds.sh | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docker/scripts/install-connectors.sh b/docker/scripts/install-connectors.sh index e96a67ff..d950a4be 100755 --- a/docker/scripts/install-connectors.sh +++ b/docker/scripts/install-connectors.sh @@ -18,10 +18,11 @@ function cmakebuild() { cd /tmp +STATIC_PACKAGES="libfftw3-bin" BUILD_PACKAGES="git cmake make gcc g++ libsamplerate-dev libfftw3-dev" apt-get update -apt-get -y install --no-install-recommends $BUILD_PACKAGES +apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES git clone https://github.com/jketterl/owrx_connector.git # latest develop as of 2023-08-04 (direct_sampling for rtl_tcp_connector) diff --git a/docker/scripts/install-dependencies-runds.sh b/docker/scripts/install-dependencies-runds.sh index 1723bb44..6e337175 100755 --- a/docker/scripts/install-dependencies-runds.sh +++ b/docker/scripts/install-dependencies-runds.sh @@ -18,8 +18,8 @@ function cmakebuild() { cd /tmp -STATIC_PACKAGES="" -BUILD_PACKAGES="git cmake make gcc g++ pkg-config" +STATIC_PACKAGES="libfftw3-bin" +BUILD_PACKAGES="git cmake make gcc g++ pkg-config libfftw3-dev" apt-get update apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES From c3ebb0d2c54a1d60ce123c730b784024aae578e9 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Thu, 10 Aug 2023 21:29:18 +0200 Subject: [PATCH 015/204] update timingrecovery api --- csdr/chain/digimodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index d19deaae..305deef8 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -69,7 +69,7 @@ class PskDemodulator(SecondaryDemodulator, SecondarySelectorChain): secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3 workers = [ Agc(Format.COMPLEX_FLOAT), - TimingRecovery(secondary_samples_per_bits, 0.5, 2, useQ=True), + TimingRecovery(Format.COMPLEX_FLOAT, secondary_samples_per_bits, 0.5, 2), DBPskDecoder(), VaricodeDecoder(), ] @@ -83,4 +83,4 @@ class PskDemodulator(SecondaryDemodulator, SecondarySelectorChain): return self.sampleRate = sampleRate secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3 - self.replace(1, TimingRecovery(secondary_samples_per_bits, 0.5, 2, useQ=True)) + self.replace(1, TimingRecovery(Format.FLOAT, secondary_samples_per_bits, 0.5, 2)) From 473929ce973631ce18e07417df3f96fd2695d480 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 15 Aug 2023 16:42:59 +0200 Subject: [PATCH 016/204] introduce rtty mode --- csdr/chain/digimodes.py | 32 ++++++++++++++++++++++++++-- docker/scripts/install-owrx-tools.sh | 8 +++---- owrx/dsp.py | 6 ++++++ owrx/modes.py | 8 ++++--- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 305deef8..a399c58d 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -3,7 +3,7 @@ from csdr.module.msk144 import Msk144Module, ParserAdapter from owrx.audio.chopper import AudioChopper, AudioChopperParser from owrx.aprs.kiss import KissDeframer from owrx.aprs import Ax25Parser, AprsParser -from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder +from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, RttyDecoder, BaudotDecoder from pycsdr.types import Format from owrx.aprs.module import DirewolfModule @@ -83,4 +83,32 @@ class PskDemodulator(SecondaryDemodulator, SecondarySelectorChain): return self.sampleRate = sampleRate secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3 - self.replace(1, TimingRecovery(Format.FLOAT, secondary_samples_per_bits, 0.5, 2)) + self.replace(1, TimingRecovery(Format.COMPLEX_FLOAT, secondary_samples_per_bits, 0.5, 2)) + + +class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): + def __init__(self, baudRate, bandWidth, invert=False): + self.baudRate = baudRate + self.bandWidth = bandWidth + self.invert = invert + # this is an assumption, we will adjust in setSampleRate + self.sampleRate = 12000 + secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) + workers = [ + Agc(Format.COMPLEX_FLOAT), + FmDemod(), + TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 0.5, 2), + RttyDecoder(invert), + BaudotDecoder(), + ] + super().__init__(workers) + + def getBandwidth(self) -> float: + return self.bandWidth + + def setSampleRate(self, sampleRate: int) -> None: + if sampleRate == self.sampleRate: + return + self.sampleRate = sampleRate + secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) + self.replace(2, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 0.5, 2)) diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index a3132b74..7938b5f5 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -32,13 +32,13 @@ popd rm -rf js8py git clone https://github.com/jketterl/csdr.git -# latest develop as of 2023-06-30 (csdr cleanup) -cmakebuild csdr 7e7a7f27b9fd1a65d4a9d0725583dfe6309a5245 +# latest develop as of 2023-08-15 (rtty and baudot) +cmakebuild csdr 8966688f748d04486560e973a599c26ed4297f34 git clone https://github.com/jketterl/pycsdr.git cd pycsdr -# latest develop as of 2023-06-30 (csdr cleanup) -git checkout be8b0e5e0b972ebb302e7397bac0058f620ec374 +# latest develop as of 2023-08-15 (rtty and baudot) +git checkout 4e30b6c6a4d73ab4cf99698847c4df68e8206f73 ./setup.py install install_headers cd .. rm -rf pycsdr diff --git a/owrx/dsp.py b/owrx/dsp.py index 749f0b94..f5e9395b 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -600,6 +600,12 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif mod == "bpsk63": from csdr.chain.digimodes import PskDemodulator return PskDemodulator(62.5) + elif mod == "rtty170": + from csdr.chain.digimodes import RttyDemodulator + return RttyDemodulator(45.45, 170) + elif mod == "rtty450": + from csdr.chain.digimodes import RttyDemodulator + return RttyDemodulator(50, 450, invert=True) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) diff --git a/owrx/modes.py b/owrx/modes.py index c27e85b8..131292b6 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -106,19 +106,21 @@ class Modes(object): AnalogMode("lsb", "LSB", bandpass=Bandpass(-3000, -300)), AnalogMode("usb", "USB", bandpass=Bandpass(300, 3000)), AnalogMode("cw", "CW", bandpass=Bandpass(700, 900)), - AnalogMode("dmr", "DMR", bandpass=Bandpass(-4000, 4000), requirements=["digital_voice_digiham"], squelch=False), + AnalogMode("dmr", "DMR", bandpass=Bandpass(-6250, 6250), requirements=["digital_voice_digiham"], squelch=False), AnalogMode( "dstar", "D-Star", bandpass=Bandpass(-3250, 3250), requirements=["digital_voice_digiham"], squelch=False ), AnalogMode("nxdn", "NXDN", bandpass=Bandpass(-3250, 3250), requirements=["digital_voice_digiham"], squelch=False), - AnalogMode("ysf", "YSF", bandpass=Bandpass(-4000, 4000), requirements=["digital_voice_digiham"], squelch=False), - AnalogMode("m17", "M17", bandpass=Bandpass(-4000, 4000), requirements=["digital_voice_m17"], squelch=False), + AnalogMode("ysf", "YSF", bandpass=Bandpass(-6250, 6250), requirements=["digital_voice_digiham"], squelch=False), + AnalogMode("m17", "M17", bandpass=Bandpass(-6250, 6250), requirements=["digital_voice_m17"], squelch=False), AnalogMode( "freedv", "FreeDV", bandpass=Bandpass(300, 3000), requirements=["digital_voice_freedv"], squelch=False ), AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False), DigitalMode("bpsk31", "BPSK31", underlying=["usb"]), DigitalMode("bpsk63", "BPSK63", underlying=["usb"]), + DigitalMode("rtty170", "RTTY 45/170", underlying=["usb", "lsb"]), + DigitalMode("rtty450", "RTTY 50N/450", underlying=["lsb", "usb"]), WsjtMode("ft8", "FT8"), WsjtMode("ft4", "FT4"), WsjtMode("jt65", "JT65"), From 8ee5edc9f243c0ad4da889f01e980e25a4132741 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 16 Aug 2023 17:07:18 +0200 Subject: [PATCH 017/204] add a lowpass to improve decoding performance --- csdr/chain/digimodes.py | 10 +++++++--- docker/scripts/install-owrx-tools.sh | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index a399c58d..015624be 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -3,7 +3,7 @@ from csdr.module.msk144 import Msk144Module, ParserAdapter from owrx.audio.chopper import AudioChopper, AudioChopperParser from owrx.aprs.kiss import KissDeframer from owrx.aprs import Ax25Parser, AprsParser -from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, RttyDecoder, BaudotDecoder +from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, RttyDecoder, BaudotDecoder, Lowpass from pycsdr.types import Format from owrx.aprs.module import DirewolfModule @@ -94,10 +94,12 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): # this is an assumption, we will adjust in setSampleRate self.sampleRate = 12000 secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) + cutoff = self.baudRate / self.sampleRate workers = [ Agc(Format.COMPLEX_FLOAT), FmDemod(), - TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 0.5, 2), + Lowpass(Format.FLOAT, cutoff), + TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 5, 2), RttyDecoder(invert), BaudotDecoder(), ] @@ -111,4 +113,6 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): return self.sampleRate = sampleRate secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) - self.replace(2, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 0.5, 2)) + cutoff = self.baudRate / self.sampleRate + self.replace(2, Lowpass(Format.FLOAT, cutoff)) + self.replace(3, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 5, 2)) diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index 7938b5f5..bc1d1c5c 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -32,13 +32,13 @@ popd rm -rf js8py git clone https://github.com/jketterl/csdr.git -# latest develop as of 2023-08-15 (rtty and baudot) -cmakebuild csdr 8966688f748d04486560e973a599c26ed4297f34 +# latest develop as of 2023-08-16 (added lowpass) +cmakebuild csdr 64a1603c4433e79f4b6a449711bfd86f977a997b git clone https://github.com/jketterl/pycsdr.git cd pycsdr -# latest develop as of 2023-08-15 (rtty and baudot) -git checkout 4e30b6c6a4d73ab4cf99698847c4df68e8206f73 +# latest develop as of 2023-08-16 (added lowpass) +git checkout eec718ae365583ebf5f315ae45967d2f635ff209 ./setup.py install install_headers cd .. rm -rf pycsdr From f88fd1c160aa873b76a371e0ca93d3dccbaf6deb Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 16 Aug 2023 17:07:42 +0200 Subject: [PATCH 018/204] add a narrow rtty mode for DWD 147.3 --- owrx/dsp.py | 3 +++ owrx/modes.py | 1 + 2 files changed, 4 insertions(+) diff --git a/owrx/dsp.py b/owrx/dsp.py index f5e9395b..5b5aa5e0 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -606,6 +606,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif mod == "rtty450": from csdr.chain.digimodes import RttyDemodulator return RttyDemodulator(50, 450, invert=True) + elif mod == "rtty85": + from csdr.chain.digimodes import RttyDemodulator + return RttyDemodulator(50, 85, invert=True) def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) diff --git a/owrx/modes.py b/owrx/modes.py index 131292b6..1cd3aac5 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -121,6 +121,7 @@ class Modes(object): DigitalMode("bpsk63", "BPSK63", underlying=["usb"]), DigitalMode("rtty170", "RTTY 45/170", underlying=["usb", "lsb"]), DigitalMode("rtty450", "RTTY 50N/450", underlying=["lsb", "usb"]), + DigitalMode("rtty85", "RTTY 50N/85", underlying=["lsb", "usb"]), WsjtMode("ft8", "FT8"), WsjtMode("ft4", "FT4"), WsjtMode("jt65", "JT65"), From 6026aaedbae23ec025da3043ebfd188b4ea071b4 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 18 Aug 2023 18:12:26 +0200 Subject: [PATCH 019/204] change flexing so that inputs don't cause overflow --- htdocs/css/admin.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/css/admin.css b/htdocs/css/admin.css index 742fe328..02e9a8ad 100644 --- a/htdocs/css/admin.css +++ b/htdocs/css/admin.css @@ -127,7 +127,7 @@ h1 { } .removable-group.removable .removable-item, .add-group .add-group-select { - flex: 1 0 auto; + flex: 1 0 0; margin-right: .25rem; } From 5997923f366715a46e7d648c94559d93430eb3c3 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 18 Aug 2023 21:52:39 +0200 Subject: [PATCH 020/204] update dependencies --- docker/scripts/install-owrx-tools.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index bc1d1c5c..7abb2f4f 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -32,8 +32,8 @@ popd rm -rf js8py git clone https://github.com/jketterl/csdr.git -# latest develop as of 2023-08-16 (added lowpass) -cmakebuild csdr 64a1603c4433e79f4b6a449711bfd86f977a997b +# latest develop as of 2023-08-18 (fmdemod replacement) +cmakebuild csdr 98de79de2187bf9b7d007fc6ae252175578c1459 git clone https://github.com/jketterl/pycsdr.git cd pycsdr From c1bfb480e79fe66cd4214bb2ac06a5da06c24db3 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Mon, 21 Aug 2023 17:30:07 +0200 Subject: [PATCH 021/204] tweak timing recovery parameters --- csdr/chain/digimodes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 015624be..4b78ca58 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -95,11 +95,12 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): self.sampleRate = 12000 secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) cutoff = self.baudRate / self.sampleRate + loop_gain = self.sampleRate / self.getBandwidth() / 5 workers = [ Agc(Format.COMPLEX_FLOAT), FmDemod(), Lowpass(Format.FLOAT, cutoff), - TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 5, 2), + TimingRecovery(Format.FLOAT, secondary_samples_per_bit, loop_gain, 10), RttyDecoder(invert), BaudotDecoder(), ] @@ -114,5 +115,6 @@ class RttyDemodulator(SecondaryDemodulator, SecondarySelectorChain): self.sampleRate = sampleRate secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) cutoff = self.baudRate / self.sampleRate + loop_gain = self.sampleRate / self.getBandwidth() / 5 self.replace(2, Lowpass(Format.FLOAT, cutoff)) - self.replace(3, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, 5, 2)) + self.replace(3, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, loop_gain, 10)) From 69d9a5ae79264b224d17d4aff6659d9e059270a8 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Mon, 21 Aug 2023 23:12:10 +0200 Subject: [PATCH 022/204] introduce a filter for d-star --- csdr/chain/digiham.py | 1 + 1 file changed, 1 insertion(+) diff --git a/csdr/chain/digiham.py b/csdr/chain/digiham.py index a17217f7..77e1d283 100644 --- a/csdr/chain/digiham.py +++ b/csdr/chain/digiham.py @@ -79,6 +79,7 @@ class Dstar(DigihamChain): fskDemodulator=FskDemodulator(samplesPerSymbol=10), decoder=DstarDecoder(), mbeMode=Modes.DStarMode, + filter=WideRrcFilter(), codecserver=codecserver ) From 32fcfad4d53318229bca6efeacc09bd6f22b35ae Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Mon, 21 Aug 2023 23:18:58 +0200 Subject: [PATCH 023/204] use the new execmodule where appropriate --- csdr/module/drm.py | 19 +++--- csdr/module/freedv.py | 18 +++--- csdr/module/msk144.py | 19 +++--- docker/scripts/install-owrx-tools.sh | 8 +-- owrx/aprs/module.py | 86 +++++++++++++++------------- 5 files changed, 75 insertions(+), 75 deletions(-) diff --git a/csdr/module/drm.py b/csdr/module/drm.py index f8d59de1..a7515bd8 100644 --- a/csdr/module/drm.py +++ b/csdr/module/drm.py @@ -1,14 +1,11 @@ -from csdr.module import PopenModule +from pycsdr.modules import ExecModule from pycsdr.types import Format -class DrmModule(PopenModule): - def getInputFormat(self) -> Format: - return Format.COMPLEX_FLOAT - - def getOutputFormat(self) -> Format: - return Format.SHORT - - def getCommand(self): - # dream -c 6 --sigsrate 48000 --audsrate 48000 -I - -O - - return ["dream", "-c", "6", "--sigsrate", "48000", "--audsrate", "48000", "-I", "-", "-O", "-"] +class DrmModule(ExecModule): + def __init__(self): + super().__init__( + Format.COMPLEX_SHORT, + Format.SHORT, + ["dream", "-c", "6", "--sigsrate", "48000", "--audsrate", "48000", "-I", "-", "-O", "-"] + ) diff --git a/csdr/module/freedv.py b/csdr/module/freedv.py index 2c1014f4..90bc38a7 100644 --- a/csdr/module/freedv.py +++ b/csdr/module/freedv.py @@ -1,13 +1,11 @@ from pycsdr.types import Format -from csdr.module import PopenModule +from pycsdr.modules import ExecModule -class FreeDVModule(PopenModule): - def getInputFormat(self) -> Format: - return Format.SHORT - - def getOutputFormat(self) -> Format: - return Format.SHORT - - def getCommand(self): - return ["freedv_rx", "1600", "-", "-"] +class FreeDVModule(ExecModule): + def __init__(self): + super().__init__( + Format.SHORT, + Format.SHORT, + ["freedv_rx", "1600", "-", "-"] + ) diff --git a/csdr/module/msk144.py b/csdr/module/msk144.py index 2ccc64cc..9d1b0023 100644 --- a/csdr/module/msk144.py +++ b/csdr/module/msk144.py @@ -1,5 +1,6 @@ from pycsdr.types import Format -from csdr.module import PopenModule, ThreadModule +from pycsdr.modules import ExecModule +from csdr.module import ThreadModule from owrx.wsjt import WsjtParser, Msk144Profile import pickle @@ -7,15 +8,13 @@ import logging logger = logging.getLogger(__name__) -class Msk144Module(PopenModule): - def getCommand(self): - return ["msk144decoder"] - - def getInputFormat(self) -> Format: - return Format.SHORT - - def getOutputFormat(self) -> Format: - return Format.CHAR +class Msk144Module(ExecModule): + def __init__(self): + super().__init__( + Format.SHORT, + Format.CHAR, + ["msk144decoder"] + ) class ParserAdapter(ThreadModule): diff --git a/docker/scripts/install-owrx-tools.sh b/docker/scripts/install-owrx-tools.sh index 7abb2f4f..5fbe55e7 100755 --- a/docker/scripts/install-owrx-tools.sh +++ b/docker/scripts/install-owrx-tools.sh @@ -32,13 +32,13 @@ popd rm -rf js8py git clone https://github.com/jketterl/csdr.git -# latest develop as of 2023-08-18 (fmdemod replacement) -cmakebuild csdr 98de79de2187bf9b7d007fc6ae252175578c1459 +# latest develop as of 2023-08-21 (introduction of execmodule) +cmakebuild csdr 3a11e7f800d8226e2f5de89432150a724bc7960e git clone https://github.com/jketterl/pycsdr.git cd pycsdr -# latest develop as of 2023-08-16 (added lowpass) -git checkout eec718ae365583ebf5f315ae45967d2f635ff209 +# latest develop as of 2023-08-21 (introduction of execmodule) +git checkout ebb46984c9b2a31dcf3810ad1f11e461a839e114 ./setup.py install install_headers cd .. rm -rf pycsdr diff --git a/owrx/aprs/module.py b/owrx/aprs/module.py index 28bb51df..c4c1ded7 100644 --- a/owrx/aprs/module.py +++ b/owrx/aprs/module.py @@ -1,10 +1,7 @@ -from csdr.module import AutoStartModule from pycsdr.types import Format -from pycsdr.modules import Writer, TcpSource -from subprocess import Popen, PIPE +from pycsdr.modules import Writer, TcpSource, ExecModule, CallbackWriter from owrx.aprs.direwolf import DirewolfConfig, DirewolfConfigSubscriber from owrx.config.core import CoreConfig -import threading import time import os @@ -13,46 +10,55 @@ import logging logger = logging.getLogger(__name__) -class DirewolfModule(AutoStartModule, DirewolfConfigSubscriber): +class LogWriter(CallbackWriter): + def __init__(self): + self.retained = bytes() + super().__init__(Format.CHAR) + + def write(self, data: bytes) -> None: + self.retained += data + lines = self.retained.split(b"\n") + + # keep the last line + # this should either be empty if the last char was \n + # or an incomplete line if the read returned early + self.retained = lines[-1] + + # log all completed lines + for line in lines[0:-1]: + logger.info("{}: {}".format("STDOUT", line.strip(b'\n').decode())) + + +class DirewolfModule(ExecModule, DirewolfConfigSubscriber): def __init__(self, service: bool = False): - self.process = None self.tcpSource = None + self.writer = None self.service = service self.direwolfConfigPath = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format( tmp_dir=CoreConfig().get_temporary_directory(), myid=id(self) ) - self.direwolfConfig = None - super().__init__() - def setWriter(self, writer: Writer) -> None: - super().setWriter(writer) - if self.tcpSource is not None: - self.tcpSource.setWriter(writer) - - def getInputFormat(self) -> Format: - return Format.SHORT - - def getOutputFormat(self) -> Format: - return Format.CHAR - - def start(self): self.direwolfConfig = DirewolfConfig() self.direwolfConfig.wire(self) + self.__writeConfig() + + super().__init__(Format.SHORT, Format.CHAR, ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"]) + # direwolf supplies the data via a socket which we tap into in start() + # the output on its STDOUT is informative, but we still want to log it + super().setWriter(LogWriter()) + self.start() + + def __writeConfig(self): file = open(self.direwolfConfigPath, "w") file.write(self.direwolfConfig.getConfig(self.service)) file.close() - # direwolf -c {direwolf_config} -r {audio_rate} -t 0 -q d -q h 1>&2 - self.process = Popen( - ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"], - start_new_session=True, - stdin=PIPE, - ) - - # resume in case the reader has been stop()ed before - self.reader.resume() - threading.Thread(target=self.pump(self.reader.read, self.process.stdin.write)).start() + def setWriter(self, writer: Writer) -> None: + self.writer = writer + if self.tcpSource is not None: + self.tcpSource.setWriter(writer) + def start(self): delay = 0.5 retries = 0 while True: @@ -68,16 +74,16 @@ class DirewolfModule(AutoStartModule, DirewolfConfigSubscriber): retries += 1 time.sleep(delay) - def stop(self): - if self.process is not None: - self.process.terminate() - self.process.wait() - self.process = None + def restart(self): + self.__writeConfig() + super().restart() + self.start() + + def onConfigChanged(self): + self.restart() + + def stop(self) -> None: + super().stop() os.unlink(self.direwolfConfigPath) self.direwolfConfig.unwire(self) self.direwolfConfig = None - self.reader.stop() - - def onConfigChanged(self): - self.stop() - self.start() From 4eae45ddbffecc5aa241e128a1047b76a52ac108 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Mon, 21 Aug 2023 23:24:43 +0200 Subject: [PATCH 024/204] move direwolf module into the direwolf namespace --- csdr/chain/digimodes.py | 2 +- owrx/aprs/direwolf.py | 86 ++++++++++++++++++++++++++++++++++++++- owrx/aprs/module.py | 89 ----------------------------------------- 3 files changed, 86 insertions(+), 91 deletions(-) delete mode 100644 owrx/aprs/module.py diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 4b78ca58..a8420609 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -5,7 +5,7 @@ from owrx.aprs.kiss import KissDeframer from owrx.aprs import Ax25Parser, AprsParser from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, RttyDecoder, BaudotDecoder, Lowpass from pycsdr.types import Format -from owrx.aprs.module import DirewolfModule +from owrx.aprs.direwolf import DirewolfModule class AudioChopperDemodulator(ServiceDemodulator, DialFrequencyReceiver): diff --git a/owrx/aprs/direwolf.py b/owrx/aprs/direwolf.py index 8237f97a..0b7929c8 100644 --- a/owrx/aprs/direwolf.py +++ b/owrx/aprs/direwolf.py @@ -1,6 +1,11 @@ -import random +from pycsdr.types import Format +from pycsdr.modules import Writer, TcpSource, ExecModule, CallbackWriter +from owrx.config.core import CoreConfig from owrx.config import Config from abc import ABC, abstractmethod +import time +import os +import random import socket import logging @@ -136,3 +141,82 @@ IGLOGIN {callsign} {password} ) return config + + +class LogWriter(CallbackWriter): + def __init__(self): + self.retained = bytes() + super().__init__(Format.CHAR) + + def write(self, data: bytes) -> None: + self.retained += data + lines = self.retained.split(b"\n") + + # keep the last line + # this should either be empty if the last char was \n + # or an incomplete line if the read returned early + self.retained = lines[-1] + + # log all completed lines + for line in lines[0:-1]: + logger.info("{}: {}".format("STDOUT", line.strip(b'\n').decode())) + + +class DirewolfModule(ExecModule, DirewolfConfigSubscriber): + def __init__(self, service: bool = False): + self.tcpSource = None + self.writer = None + self.service = service + self.direwolfConfigPath = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format( + tmp_dir=CoreConfig().get_temporary_directory(), myid=id(self) + ) + + self.direwolfConfig = DirewolfConfig() + self.direwolfConfig.wire(self) + self.__writeConfig() + + super().__init__(Format.SHORT, Format.CHAR, ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"]) + # direwolf supplies the data via a socket which we tap into in start() + # the output on its STDOUT is informative, but we still want to log it + super().setWriter(LogWriter()) + self.start() + + def __writeConfig(self): + file = open(self.direwolfConfigPath, "w") + file.write(self.direwolfConfig.getConfig(self.service)) + file.close() + + def setWriter(self, writer: Writer) -> None: + self.writer = writer + if self.tcpSource is not None: + self.tcpSource.setWriter(writer) + + def start(self): + delay = 0.5 + retries = 0 + while True: + try: + self.tcpSource = TcpSource(self.direwolfConfig.getPort(), Format.CHAR) + if self.writer: + self.tcpSource.setWriter(self.writer) + break + except ConnectionError: + if retries > 20: + logger.error("maximum number of connection attempts reached. did direwolf start up correctly?") + raise + retries += 1 + time.sleep(delay) + + def restart(self): + self.__writeConfig() + super().restart() + self.start() + + def onConfigChanged(self): + self.restart() + + def stop(self) -> None: + super().stop() + os.unlink(self.direwolfConfigPath) + self.direwolfConfig.unwire(self) + self.direwolfConfig = None diff --git a/owrx/aprs/module.py b/owrx/aprs/module.py deleted file mode 100644 index c4c1ded7..00000000 --- a/owrx/aprs/module.py +++ /dev/null @@ -1,89 +0,0 @@ -from pycsdr.types import Format -from pycsdr.modules import Writer, TcpSource, ExecModule, CallbackWriter -from owrx.aprs.direwolf import DirewolfConfig, DirewolfConfigSubscriber -from owrx.config.core import CoreConfig -import time -import os - -import logging - -logger = logging.getLogger(__name__) - - -class LogWriter(CallbackWriter): - def __init__(self): - self.retained = bytes() - super().__init__(Format.CHAR) - - def write(self, data: bytes) -> None: - self.retained += data - lines = self.retained.split(b"\n") - - # keep the last line - # this should either be empty if the last char was \n - # or an incomplete line if the read returned early - self.retained = lines[-1] - - # log all completed lines - for line in lines[0:-1]: - logger.info("{}: {}".format("STDOUT", line.strip(b'\n').decode())) - - -class DirewolfModule(ExecModule, DirewolfConfigSubscriber): - def __init__(self, service: bool = False): - self.tcpSource = None - self.writer = None - self.service = service - self.direwolfConfigPath = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format( - tmp_dir=CoreConfig().get_temporary_directory(), myid=id(self) - ) - - self.direwolfConfig = DirewolfConfig() - self.direwolfConfig.wire(self) - self.__writeConfig() - - super().__init__(Format.SHORT, Format.CHAR, ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"]) - # direwolf supplies the data via a socket which we tap into in start() - # the output on its STDOUT is informative, but we still want to log it - super().setWriter(LogWriter()) - self.start() - - def __writeConfig(self): - file = open(self.direwolfConfigPath, "w") - file.write(self.direwolfConfig.getConfig(self.service)) - file.close() - - def setWriter(self, writer: Writer) -> None: - self.writer = writer - if self.tcpSource is not None: - self.tcpSource.setWriter(writer) - - def start(self): - delay = 0.5 - retries = 0 - while True: - try: - self.tcpSource = TcpSource(self.direwolfConfig.getPort(), Format.CHAR) - if self.writer: - self.tcpSource.setWriter(self.writer) - break - except ConnectionError: - if retries > 20: - logger.error("maximum number of connection attempts reached. did direwolf start up correctly?") - raise - retries += 1 - time.sleep(delay) - - def restart(self): - self.__writeConfig() - super().restart() - self.start() - - def onConfigChanged(self): - self.restart() - - def stop(self) -> None: - super().stop() - os.unlink(self.direwolfConfigPath) - self.direwolfConfig.unwire(self) - self.direwolfConfig = None From 154897ab3f088d77264f6ff898acfdaae86da211 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 22 Aug 2023 01:52:34 +0200 Subject: [PATCH 025/204] use a lowpass as a matched filter for pocsag --- csdr/chain/digiham.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/csdr/chain/digiham.py b/csdr/chain/digiham.py index 77e1d283..ebd282f0 100644 --- a/csdr/chain/digiham.py +++ b/csdr/chain/digiham.py @@ -1,5 +1,5 @@ from csdr.chain.demodulator import BaseDemodulatorChain, FixedAudioRateChain, FixedIfSampleRateChain, DialFrequencyReceiver, MetaProvider, SlotFilterChain, DemodulatorError, ServiceDemodulator -from pycsdr.modules import FmDemod, Agc, Writer, Buffer, DcBlock +from pycsdr.modules import FmDemod, Agc, Writer, Buffer, DcBlock, Lowpass from pycsdr.types import Format from digiham.modules import DstarDecoder, FskDemodulator, GfskDemodulator, DigitalVoiceFilter, MbeSynthesizer, NarrowRrcFilter, NxdnDecoder, DmrDecoder, WideRrcFilter, YsfDecoder, PocsagDecoder from digiham.ambe import Modes, ServerError @@ -125,6 +125,8 @@ class PocsagDemodulator(ServiceDemodulator, DialFrequencyReceiver): self.parser = PocsagParser() workers = [ FmDemod(), + DcBlock(), + Lowpass(Format.FLOAT, 1200 / self.getFixedAudioRate()), FskDemodulator(samplesPerSymbol=40, invert=True), PocsagDecoder(), self.parser, From f1d96416e54589f203d7268ed4455f2a533a30b3 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 22 Aug 2023 01:53:47 +0200 Subject: [PATCH 026/204] escape messages where appropriate; refs #350 --- htdocs/lib/MessagePanel.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/htdocs/lib/MessagePanel.js b/htdocs/lib/MessagePanel.js index 51e258a4..3358582e 100644 --- a/htdocs/lib/MessagePanel.js +++ b/htdocs/lib/MessagePanel.js @@ -47,6 +47,10 @@ MessagePanel.prototype.initClearButton = function() { $(me.el).append(me.clearButton); }; +MessagePanel.prototype.htmlEscape = function(input) { + return $('
').text(input).html() +} + function WsjtMessagePanel(el) { MessagePanel.call(this, el); this.initClearTimer(); @@ -85,23 +89,19 @@ WsjtMessagePanel.prototype.pushMessage = function(msg) { var linkedmsg = msg['msg']; var matches; - var html_escape = function(input) { - return $('
').text(input).html() - }; - if (this.qsoModes.indexOf(msg['mode']) >= 0) { matches = linkedmsg.match(/(.*\s[A-Z0-9]+\s)([A-R]{2}[0-9]{2})$/); if (matches && matches[2] !== 'RR73') { - linkedmsg = html_escape(matches[1]) + '' + matches[2] + ''; + linkedmsg = this.htmlEscape(matches[1]) + '' + matches[2] + ''; } else { - linkedmsg = html_escape(linkedmsg); + linkedmsg = this.htmlEscape(linkedmsg); } } else if (this.beaconModes.indexOf(msg['mode']) >= 0) { matches = linkedmsg.match(/([A-Z0-9]*\s)([A-R]{2}[0-9]{2})(\s[0-9]+)/); if (matches) { - linkedmsg = html_escape(matches[1]) + '' + matches[2] + '' + html_escape(matches[3]); + linkedmsg = this.htmlEscape(matches[1]) + '' + matches[2] + '' + this.htmlEscape(matches[3]); } else { - linkedmsg = html_escape(linkedmsg); + linkedmsg = this.htmlEscape(linkedmsg); } } $b.append($( @@ -216,7 +216,7 @@ PacketMessagePanel.prototype.pushMessage = function(msg) { '' + timestamp + '' + '' + callsign + '' + '' + link + '' + - '' + (msg.comment || msg.message || '') + '' + + '' + this.htmlEscape(msg.comment || msg.message || '') + '' + '' )); $b.scrollTop($b[0].scrollHeight); @@ -257,7 +257,7 @@ PocsagMessagePanel.prototype.pushMessage = function(msg) { $b.append($( '' + '' + msg.address + '' + - '' + msg.message + '' + + '' + this.htmlEscape(msg.message) + '' + '' )); $b.scrollTop($b[0].scrollHeight); From f62b5363885041feea3124a433249960c00f1df8 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 22 Aug 2023 02:04:30 +0200 Subject: [PATCH 027/204] increase pocsag bandwidth to 12.5kHz --- owrx/modes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owrx/modes.py b/owrx/modes.py index 1cd3aac5..1b262ec1 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -145,7 +145,7 @@ class Modes(object): "pocsag", "Pocsag", underlying=["nfm"], - bandpass=Bandpass(-6000, 6000), + bandpass=Bandpass(-6250, 6250), requirements=["pocsag"], squelch=False, ), From 063023564bf5d930b643cf7457e1d27d168869a6 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 22 Aug 2023 19:41:24 +0200 Subject: [PATCH 028/204] add empty demodulator (for modes that don't produce any usable audio) --- csdr/chain/analog.py | 11 +++++++++++ csdr/chain/dump1090.py | 0 owrx/adsb/dump1090.py | 0 owrx/dsp.py | 3 +++ 4 files changed, 14 insertions(+) create mode 100644 csdr/chain/dump1090.py create mode 100644 owrx/adsb/dump1090.py diff --git a/csdr/chain/analog.py b/csdr/chain/analog.py index 6bb277f0..2a2341e6 100644 --- a/csdr/chain/analog.py +++ b/csdr/chain/analog.py @@ -74,3 +74,14 @@ class Ssb(BaseDemodulatorChain): Agc(Format.FLOAT), ] super().__init__(workers) + + +class Empty(BaseDemodulatorChain): + def __init__(self): + super().__init__([]) + + def getOutputFormat(self) -> Format: + return Format.FLOAT + + def setWriter(self, writer): + pass diff --git a/csdr/chain/dump1090.py b/csdr/chain/dump1090.py new file mode 100644 index 00000000..e69de29b diff --git a/owrx/adsb/dump1090.py b/owrx/adsb/dump1090.py new file mode 100644 index 00000000..e69de29b diff --git a/owrx/dsp.py b/owrx/dsp.py index 5b5aa5e0..b112b38a 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -554,6 +554,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif demod == "freedv": from csdr.chain.freedv import FreeDV return FreeDV() + elif demod == "none": + from csdr.chain.analog import Empty + return Empty() def setDemodulator(self, mod): self.chain.stopDemodulator() From ce1ad5ce02af2024d75ee016f9a58109eec23bc9 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 22 Aug 2023 19:42:04 +0200 Subject: [PATCH 029/204] move the logging writer for general use --- csdr/module/__init__.py | 23 ++++++++++++++++++++++- owrx/aprs/direwolf.py | 22 ++-------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/csdr/module/__init__.py b/csdr/module/__init__.py index 9168d4fb..f2c1f9fa 100644 --- a/csdr/module/__init__.py +++ b/csdr/module/__init__.py @@ -1,5 +1,5 @@ from pycsdr.modules import Module as BaseModule -from pycsdr.modules import Reader, Writer +from pycsdr.modules import Reader, Writer, CallbackWriter from pycsdr.types import Format from abc import ABCMeta, abstractmethod from threading import Thread @@ -7,6 +7,7 @@ from io import BytesIO from subprocess import Popen, PIPE from functools import partial import pickle +import logging class Module(BaseModule, metaclass=ABCMeta): @@ -134,3 +135,23 @@ class PopenModule(AutoStartModule, metaclass=ABCMeta): self.process.wait() self.process = None self.reader.stop() + + +class LogWriter(CallbackWriter): + def __init__(self, prefix: str): + self.logger = logging.getLogger(prefix) + self.retained = bytes() + super().__init__(Format.CHAR) + + def write(self, data: bytes) -> None: + self.retained += data + lines = self.retained.split(b"\n") + + # keep the last line + # this should either be empty if the last char was \n + # or an incomplete line if the read returned early + self.retained = lines[-1] + + # log all completed lines + for line in lines[0:-1]: + self.logger.info("{}: {}".format("STDOUT", line.strip(b'\n').decode())) diff --git a/owrx/aprs/direwolf.py b/owrx/aprs/direwolf.py index 0b7929c8..7e0eae90 100644 --- a/owrx/aprs/direwolf.py +++ b/owrx/aprs/direwolf.py @@ -1,5 +1,6 @@ from pycsdr.types import Format from pycsdr.modules import Writer, TcpSource, ExecModule, CallbackWriter +from csdr.module import LogWriter from owrx.config.core import CoreConfig from owrx.config import Config from abc import ABC, abstractmethod @@ -143,25 +144,6 @@ IGLOGIN {callsign} {password} return config -class LogWriter(CallbackWriter): - def __init__(self): - self.retained = bytes() - super().__init__(Format.CHAR) - - def write(self, data: bytes) -> None: - self.retained += data - lines = self.retained.split(b"\n") - - # keep the last line - # this should either be empty if the last char was \n - # or an incomplete line if the read returned early - self.retained = lines[-1] - - # log all completed lines - for line in lines[0:-1]: - logger.info("{}: {}".format("STDOUT", line.strip(b'\n').decode())) - - class DirewolfModule(ExecModule, DirewolfConfigSubscriber): def __init__(self, service: bool = False): self.tcpSource = None @@ -178,7 +160,7 @@ class DirewolfModule(ExecModule, DirewolfConfigSubscriber): super().__init__(Format.SHORT, Format.CHAR, ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"]) # direwolf supplies the data via a socket which we tap into in start() # the output on its STDOUT is informative, but we still want to log it - super().setWriter(LogWriter()) + super().setWriter(LogWriter(__name__)) self.start() def __writeConfig(self): From 387d94b4cee0b75d7bb399b1d69d84501014865a Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 22 Aug 2023 19:59:00 +0200 Subject: [PATCH 030/204] add dump1090 demodulator (raw message only for now) --- csdr/chain/dump1090.py | 18 +++++++++++++++++ owrx/adsb/dump1090.py | 46 ++++++++++++++++++++++++++++++++++++++++++ owrx/dsp.py | 3 +++ owrx/feature.py | 4 ++++ owrx/modes.py | 8 ++++++++ 5 files changed, 79 insertions(+) diff --git a/csdr/chain/dump1090.py b/csdr/chain/dump1090.py index e69de29b..0eaf609f 100644 --- a/csdr/chain/dump1090.py +++ b/csdr/chain/dump1090.py @@ -0,0 +1,18 @@ +from pycsdr.modules import Convert +from pycsdr.types import Format +from csdr.chain.demodulator import ServiceDemodulator +from owrx.adsb.dump1090 import Dump1090Module + + +class Dump1090(ServiceDemodulator): + def __init__(self): + workers = [ + Convert(Format.COMPLEX_FLOAT, Format.COMPLEX_SHORT), + Dump1090Module() + ] + + super().__init__(workers) + pass + + def getFixedAudioRate(self) -> int: + return 2400000 diff --git a/owrx/adsb/dump1090.py b/owrx/adsb/dump1090.py index e69de29b..3032999e 100644 --- a/owrx/adsb/dump1090.py +++ b/owrx/adsb/dump1090.py @@ -0,0 +1,46 @@ +from pycsdr.modules import ExecModule, Writer, TcpSource +from pycsdr.types import Format +from csdr.module import LogWriter +from owrx.socket import getAvailablePort +import time + +import logging + +logger = logging.getLogger(__name__) + + +class Dump1090Module(ExecModule): + def __init__(self): + self.tcpSource = None + self.writer = None + self.port = getAvailablePort() + + super().__init__( + Format.COMPLEX_SHORT, + Format.CHAR, + ["dump1090", "--ifile", "-", "--iformat", "SC16", "--quiet", "--net-ro-port", str(self.port)] + ) + super().setWriter(LogWriter(__name__)) + + self.start() + + def start(self): + delay = 0.5 + retries = 0 + while True: + try: + self.tcpSource = TcpSource(self.port, Format.CHAR) + if self.writer: + self.tcpSource.setWriter(self.writer) + break + except ConnectionError: + if retries > 20: + logger.error("maximum number of connection attempts reached. did dump1090 start up correctly?") + raise + retries += 1 + time.sleep(delay) + + def setWriter(self, writer: Writer) -> None: + self.writer = writer + if self.tcpSource is not None: + self.tcpSource.setWriter(writer) diff --git a/owrx/dsp.py b/owrx/dsp.py index b112b38a..c2776672 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -612,6 +612,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif mod == "rtty85": from csdr.chain.digimodes import RttyDemodulator return RttyDemodulator(50, 85, invert=True) + elif mod == "adsb": + from csdr.chain.dump1090 import Dump1090 + return Dump1090() def setSecondaryDemodulator(self, mod): demodulator = self._getSecondaryDemodulator(mod) diff --git a/owrx/feature.py b/owrx/feature.py index 036a17f8..3b8a77f7 100644 --- a/owrx/feature.py +++ b/owrx/feature.py @@ -84,6 +84,7 @@ class FeatureDetector(object): "pocsag": ["digiham"], "js8call": ["js8", "js8py"], "drm": ["dream"], + "dump1090": ["dump1090"], } def feature_availability(self): @@ -564,3 +565,6 @@ class FeatureDetector(object): except RuntimeError as e: logger.exception("Codecserver error while checking for AMBE support:") return False + + def has_dump1090(self): + return self.command_is_runnable("dump1090") diff --git a/owrx/modes.py b/owrx/modes.py index 1b262ec1..92e4b93d 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -149,6 +149,14 @@ class Modes(object): requirements=["pocsag"], squelch=False, ), + DigitalMode( + "adsb", + "ADS-B", + underlying=["none"], + bandpass=Bandpass(-1e6, 1e6), + requirements=["dump1090"], + squelch=False, + ), ] @staticmethod From 476e4ebc5bacad7884c8e59f1d48a8ec462cca72 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 22 Aug 2023 21:16:09 +0200 Subject: [PATCH 031/204] introduce some frontend to show messages --- htdocs/index.html | 1 + htdocs/lib/DemodulatorPanel.js | 1 + htdocs/lib/MessagePanel.js | 42 ++++++++++++++++++++++++++++++++++ htdocs/openwebrx.js | 2 ++ 4 files changed, 46 insertions(+) diff --git a/htdocs/index.html b/htdocs/index.html index 18834b3f..0c147d2b 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -74,6 +74,7 @@ +