diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | INSTALL.md | 85 | ||||
-rw-r--r-- | README.md | 24 | ||||
-rw-r--r-- | TODO | 20 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | lib/Json.cpp | 4 | ||||
-rw-r--r-- | lib/Json.h | 4 | ||||
-rw-r--r-- | lib/Socket.cpp | 22 | ||||
-rw-r--r-- | lib/Socket.h | 10 | ||||
-rw-r--r-- | lib/ThreadsafeQueue.h | 38 | ||||
-rw-r--r-- | lib/fec/decode_rs.h | 12 | ||||
-rw-r--r-- | src/Buffer.h | 3 | ||||
-rw-r--r-- | src/GainControl.cpp | 24 | ||||
-rw-r--r-- | src/Utils.cpp | 4 |
14 files changed, 161 insertions, 102 deletions
@@ -1,8 +1,12 @@ This file contains information about the changes done to ODR-DabMod in this repository -2023-11-21: Matthias P. Braendli <matthias@mpb.li> - (upcoming release): +2025-09-08: Matthias P. Braendli <matthias@mpb.li> + (v3.0.1): + Fix compilation on debian gcc-15. + +2025-02-26: Matthias P. Braendli <matthias@mpb.li> + (v3.0.0): Add support for the PrecisionWave DEXTER Modulator. Improve timestamp decoding and handling. Fix TII carrier levels. @@ -11,6 +15,7 @@ ODR-DabMod in this repository Remove EasyDABv3 support. Remove ZeroMQ input. Require C++17. + v2.7.0 retracted. 2022-04-20: Matthias P. Braendli <matthias@mpb.li> (v2.6.0): @@ -1,25 +1,22 @@ -You have 3 ways to install odr-dabmod on your host: +# Installation -# Using your linux distribution packaging system -`odr-dabmod` is available on the official repositories of several debian-based distributions, such as Debian -(from Debian 12), Ubuntu (from 24.10), Opensuse and Arch. +You have 2 ways to install odr-dabmod on your host: -If you are using Debian 12 (Bookworm), you will need to -[add the backports repository](https://backports.debian.org/Instructions/) +## Installing binary packages on some linux distributions + +[](https://repology.org/project/odr-dabmod/versions) **Notice**: this package does not include the web-based GUI and Digital Predistortion Computation engine -# Using installation scripts -If your linux distribution is debian-based, you can install odr-dabmod -as well as the other main components of the mmbTools set with the -[Opendigitalradio dab-scripts](https://github.com/opendigitalradio/dab-scripts.git) +## Compiling manually -# Compiling manually -Unlike the 2 previous options, this one allows you to compile odr-dabmod with the features you really need. +Unlike the previous option, this one allows you to compile odr-dabmod with the features you really need. -## Dependencies -### Debian Bullseye-based OS: -``` +### Dependencies + +#### Debian Bullseye-based OS + +```sh # Required packages ## C++11 compiler sudo apt-get install --yes build-essential automake libtool @@ -29,7 +26,7 @@ sudo apt-get install --yes libfftw3-dev # optional packages ## ZeroMQ http://www.zeromq.org -sudo apt-get install --yes libzmq3-dev libzmq5 +sudo apt-get install --yes libzmq3-dev ## UHD for USRP sudo apt-get install --yes libuhd-dev @@ -44,29 +41,37 @@ sudo apt-get install --yes libsoapysdr-dev sudo apt-get install --yes libbladerf-dev ``` -## Compilation +### Compilation + 1. Clone this repository: - ``` + + ```sh # stable version: git clone https://github.com/Opendigitalradio/ODR-DabMod.git # or development version (at your own risk): git clone https://github.com/Opendigitalradio/ODR-DabMod.git -b next ``` + 1. Configure the project - ``` + + ```sh cd ODR-DabMod - ./bootstrap + ./bootstrap.sh ./configure ``` + 1. Compile and install: - ``` + + ```sh make sudo make install ``` -### Configure options +#### Configure options + The configure script can be launched with a variety of options: + - Disable ZeroMQ input (to be used with ODR-DabMod), output and remotecontrol: `--disable-zeromq` - Disable the binding to the UHD driver for USRPs: `--disable-output-uhd` - Compile using the `-ffast-math` option that gives a substantial speedup at the cost of floating point correctness: `--enable-fast-math` @@ -79,45 +84,52 @@ The configure script can be launched with a variety of options: Create debugging files for each DSP block for data analysis: `--enable-trace` For more information, call: -``` + +```sh ./configure --help ``` -#### Performance optimisation +##### Performance optimisation + While the performance of modern systems is good enough in most cases to run ODR-DabMod, it is sometimes necessary to increase the compilation optimisation if all features are used or on slow systems. Tricks for best performance: -* Do not use `--disable-native` -* Use `--enable-fast-math` -* Add `-O3` to compiler flags -* Disable assertions with `-DNDEBUG` +- Do not use `--disable-native` +- Use `--enable-fast-math` +- Add `-O3` to compiler flags +- Disable assertions with `-DNDEBUG` Applying all together: -``` + +```sh ./configure CFLAGS="-O3 -DNDEBUG" CXXFLAGS="-O3 -DNDEBUG" --enable-fast-math ``` -#### Checking for memory usage issues +##### Checking for memory usage issues + If your compiler supports it, you can enable the address sanitizer to check for memory issues: -``` + +```sh ./configure CFLAGS="-fsanitize=address -g -O2" CXXFLAGS="-fsanitize=address -g -O2" ``` The resulting binary will be instrumented with additional memory checks, which have a measurable overhead. Please report if you get warnings or errors when using the sanitizer. -## SoapySDR support and required dependencies +### SoapySDR support and required dependencies + SoapySDR is a vendor-neutral library to drive SDR devices. It can be used to drive the HackRF and the LimeSDR among others. Required dependencies that need to be installed are, in order: -1. SoapySDR itself from https://github.com/pothosware/SoapySDR -1. The LimeSuite for the LimeSDR from https://github.com/myriadrf/LimeSuite -1. HackRF support for SoapySDR from https://github.com/pothosware/SoapyHackRF + +1. SoapySDR itself from <https://github.com/pothosware/SoapySDR> +1. The LimeSuite for the LimeSDR from <https://github.com/myriadrf/LimeSuite> +1. HackRF support for SoapySDR from <https://github.com/pothosware/SoapyHackRF> ODR-DabMod will automatically recognise if the SoapySDR library is installed on your system, and will print at the end of `./configure` if support is enabled or @@ -125,5 +137,6 @@ not. A configuration example is available in `doc/example.ini` -## BladeRF support +### BladeRF support + In order to use `--enable-bladerf`, you need to install the `libbladerf2` including the -dev package. @@ -1,5 +1,5 @@ -OVERVIEW -======== +# OVERVIEW + ODR-DabMod is a *DAB (Digital Audio Broadcasting)* modulator compliant to ETSI EN 300 401. It is the continuation of the work started by the Communications Research Center Canada, and is now pursued in the @@ -9,8 +9,7 @@ ODR-DabMod is part of the ODR-mmbTools tool-set. More information about the ODR-mmbTools is available in the *guide*, available on the [Opendigitalradio mmbTools page](http://www.opendigitalradio.org/mmbtools). -Features --------- +## Features - Reads ETI and EDI, outputs compliant COFDM I/Q - Supports native DAB sample rate and can also resample to other rates @@ -38,6 +37,7 @@ Features - ZeroMQ PUB and REP output, useful for sending IQ to GNURadio flowgraphs. Development has stalled on the following topics: + - Experimental prototype about digital predistortion for PA linearisation. - See `python/dpd/README.md` - A web GUI for control and supervision of modulator and predistortion engine. See `python/gui/README.md` @@ -53,18 +53,18 @@ ODR-DabMod. The `python/` directory contains a web-based graphical control interface and the digital predistortion project. -INSTALL -======= -See the `INSTALL.md` file for installation instructions. +## Install + +[Check the installation instructions.](INSTALL.md) + +## License -LICENCE -======= See the files `LICENCE` and `COPYING` -CONTACT -======= +## Contact + Matthias P. Braendli *matthias [at] mpb [dot] li* With thanks to other contributors listed in AUTHORS -http://opendigitalradio.org/ +<http://opendigitalradio.org/> @@ -1,25 +1,21 @@ +# To do + This TODO file lists ideas and features for future developments. Unless written, no activity has been started on the topics. - -Smaller things --------------- +## Smaller things Remove GuardIntervalInserter implementation for window==0, as it was shown both are equivalent. +## Resampler improvements -Resampler improvements ----------------------- - * Assess quality of window currently used. - * Evaluate usefulness of other windows. - * Distribute energy of Fs bin equally to both negative and positive - frequencies in the back buffer. +- Assess quality of window currently used. +- Evaluate usefulness of other windows. +- Distribute energy of Fs bin equally to both negative and positive frequencies in the back buffer. +## Review CicEq -Review CicEq ------------- The CIC Equaliser was used for the USRP1 to compensate for spectrum flatness. It is not documented, and its effect poorly explained. Review if still needed, and document appropriately. - diff --git a/configure.ac b/configure.ac index 5c3de90..ccce564 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Her Majesty the # Queen in Right of Canada (Communications Research Center Canada) -# Copyright (C) 2023 Matthias P. Braendli, http://opendigitalradio.org +# Copyright (C) 2025 Matthias P. Braendli, http://opendigitalradio.org # This file is part of ODR-DabMod. # @@ -19,7 +19,7 @@ # along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>. AC_PREREQ([2.69]) -AC_INIT([ODR-DabMod],[2.6.0],[matthias.braendli@mpb.li]) +AC_INIT([ODR-DabMod],[3.0.1],[matthias.braendli@mpb.li]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_TARGET diff --git a/lib/Json.cpp b/lib/Json.cpp index 361a149..ee33671 100644 --- a/lib/Json.cpp +++ b/lib/Json.cpp @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2023 + Copyright (C) 2025 Matthias P. Braendli, matthias.braendli@mpb.li http://www.opendigitalradio.org @@ -27,7 +27,7 @@ #include <sstream> #include <iomanip> #include <string> -#include <algorithm> +#include <stdexcept> #include "Json.h" @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2023 + Copyright (C) 2025 Matthias P. Braendli, matthias.braendli@mpb.li http://www.opendigitalradio.org @@ -34,10 +34,10 @@ #include <vector> #include <memory> #include <optional> -#include <stdexcept> #include <string> #include <unordered_map> #include <variant> +#include <cstdint> namespace json { diff --git a/lib/Socket.cpp b/lib/Socket.cpp index 938b573..5c920d7 100644 --- a/lib/Socket.cpp +++ b/lib/Socket.cpp @@ -24,6 +24,7 @@ #include "Socket.h" +#include <numeric> #include <stdexcept> #include <cstdio> #include <cstring> @@ -1063,6 +1064,17 @@ void TCPConnection::process() #endif } +TCPConnection::stats_t TCPConnection::get_stats() const +{ + TCPConnection::stats_t s; + const vector<size_t> buffer_sizes = queue.map<size_t>( + [](const vector<uint8_t>& vec) { return vec.size(); } + ); + + s.buffer_fullness = std::accumulate(buffer_sizes.cbegin(), buffer_sizes.cend(), 0); + s.remote_address = m_sock.get_remote_address(); + return s; +} TCPDataDispatcher::TCPDataDispatcher(size_t max_queue_size, size_t buffers_to_preroll) : m_max_queue_size(max_queue_size), @@ -1136,6 +1148,16 @@ void TCPDataDispatcher::process() } } + +std::vector<TCPConnection::stats_t> TCPDataDispatcher::get_stats() const +{ + std::vector<TCPConnection::stats_t> s; + for (const auto& conn : m_connections) { + s.push_back(conn.get_stats()); + } + return s; +} + TCPReceiveServer::TCPReceiveServer(size_t blocksize) : m_blocksize(blocksize) { diff --git a/lib/Socket.h b/lib/Socket.h index 7709145..29b618a 100644 --- a/lib/Socket.h +++ b/lib/Socket.h @@ -213,6 +213,8 @@ class TCPSocket { SOCKET get_sockfd() const { return m_sock; } + InetAddress get_remote_address() const { return m_remote_address; } + private: explicit TCPSocket(int sockfd); explicit TCPSocket(int sockfd, InetAddress remote_address); @@ -254,6 +256,12 @@ class TCPConnection ThreadsafeQueue<std::vector<uint8_t> > queue; + struct stats_t { + size_t buffer_fullness = 0; + InetAddress remote_address; + }; + stats_t get_stats() const; + private: std::atomic<bool> m_running; std::thread m_sender_thread; @@ -276,6 +284,8 @@ class TCPDataDispatcher void start(int port, const std::string& address); void write(const std::vector<uint8_t>& data); + std::vector<TCPConnection::stats_t> get_stats() const; + private: void process(); diff --git a/lib/ThreadsafeQueue.h b/lib/ThreadsafeQueue.h index 8b385d6..13bc19e 100644 --- a/lib/ThreadsafeQueue.h +++ b/lib/ThreadsafeQueue.h @@ -2,7 +2,7 @@ Copyright (C) 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2023 + Copyright (C) 2025 Matthias P. Braendli, matthias.braendli@mpb.li An implementation for a threadsafe queue, depends on C++11 @@ -28,6 +28,7 @@ #pragma once +#include <functional> #include <mutex> #include <condition_variable> #include <queue> @@ -63,10 +64,10 @@ public: std::unique_lock<std::mutex> lock(the_mutex); size_t queue_size_before = the_queue.size(); if (max_size == 0) { - the_queue.push(val); + the_queue.push_back(val); } else if (queue_size_before < max_size) { - the_queue.push(val); + the_queue.push_back(val); } size_t queue_size = the_queue.size(); lock.unlock(); @@ -80,10 +81,10 @@ public: std::unique_lock<std::mutex> lock(the_mutex); size_t queue_size_before = the_queue.size(); if (max_size == 0) { - the_queue.emplace(std::move(val)); + the_queue.emplace_back(std::move(val)); } else if (queue_size_before < max_size) { - the_queue.emplace(std::move(val)); + the_queue.emplace_back(std::move(val)); } size_t queue_size = the_queue.size(); lock.unlock(); @@ -110,9 +111,9 @@ public: bool overflow = false; while (the_queue.size() >= max_size) { overflow = true; - the_queue.pop(); + the_queue.pop_front(); } - the_queue.push(val); + the_queue.push_back(val); const size_t queue_size = the_queue.size(); lock.unlock(); @@ -129,9 +130,9 @@ public: bool overflow = false; while (the_queue.size() >= max_size) { overflow = true; - the_queue.pop(); + the_queue.pop_front(); } - the_queue.emplace(std::move(val)); + the_queue.emplace_back(std::move(val)); const size_t queue_size = the_queue.size(); lock.unlock(); @@ -152,7 +153,7 @@ public: while (the_queue.size() >= threshold) { the_tx_notification.wait(lock); } - the_queue.push(val); + the_queue.push_back(val); size_t queue_size = the_queue.size(); lock.unlock(); @@ -198,7 +199,7 @@ public: } popped_value = the_queue.front(); - the_queue.pop(); + the_queue.pop_front(); lock.unlock(); the_tx_notification.notify_one(); @@ -220,15 +221,26 @@ public: } else { std::swap(popped_value, the_queue.front()); - the_queue.pop(); + the_queue.pop_front(); lock.unlock(); the_tx_notification.notify_one(); } } + template<typename R> + std::vector<R> map(std::function<R(const T&)> func) const + { + std::vector<R> result; + std::unique_lock<std::mutex> lock(the_mutex); + for (const T& elem : the_queue) { + result.push_back(func(elem)); + } + return result; + } + private: - std::queue<T> the_queue; + std::deque<T> the_queue; mutable std::mutex the_mutex; std::condition_variable the_rx_notification; std::condition_variable the_tx_notification; diff --git a/lib/fec/decode_rs.h b/lib/fec/decode_rs.h index c165cf3..647b885 100644 --- a/lib/fec/decode_rs.h +++ b/lib/fec/decode_rs.h @@ -145,15 +145,15 @@ count++; } if (count != no_eras) { - printf("count = %d no_eras = %d\n lambda(x) is WRONG\n",count,no_eras); + fprintf(stderr, "count = %d no_eras = %d\n lambda(x) is WRONG\n",count,no_eras); count = -1; goto finish; } #if DEBUG >= 2 - printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n"); + fprintf(stderr, "\n Erasure positions as determined by roots of Eras Loc Poly:\n"); for (i = 0; i < count; i++) - printf("%d ", loc[i]); - printf("\n"); + fprintf(stderr, "%d ", loc[i]); + fprintf(stderr, "\n"); #endif #endif } @@ -227,7 +227,7 @@ continue; /* Not a root */ /* store root (index-form) and error location number */ #if DEBUG>=2 - printf("count %d root %d loc %d\n",count,i,k); + fprintf(stderr, "count %d root %d loc %d\n",count,i,k); #endif root[count] = i; loc[count] = k; @@ -279,7 +279,7 @@ } #if DEBUG >= 1 if (den == 0) { - printf("\n ERROR: denominator = 0\n"); + fprintf(stderr, "\n ERROR: denominator = 0\n"); count = -1; goto finish; } diff --git a/src/Buffer.h b/src/Buffer.h index 2c2a65e..a8130ed 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -34,11 +34,12 @@ #include <vector> #include <memory> #include <complex> +#include <cstdint> #include "fpm/fixed.hpp" typedef std::complex<float> complexf; -using fixed_16 = fpm::fixed<std::int16_t, std::int32_t, 14>; +using fixed_16 = fpm::fixed<int16_t, int32_t, 14>; typedef std::complex<fixed_16> complexfix; typedef std::complex<fpm::fixed_16_16> complexfix_wide; diff --git a/src/GainControl.cpp b/src/GainControl.cpp index 84cf065..e3e280f 100644 --- a/src/GainControl.cpp +++ b/src/GainControl.cpp @@ -35,7 +35,7 @@ #ifdef __SSE__ # include <xmmintrin.h> -union __u128 { +union u128_union_t { __m128 m; float f[4]; }; @@ -122,7 +122,7 @@ int GainControl::internal_process(Buffer* const dataIn, Buffer* dataOut) __m128* out = reinterpret_cast<__m128*>(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(__m128); size_t sizeOut = dataOut->getLength() / sizeof(__m128); - __u128 gain128; + u128_union_t gain128; if ((sizeIn % m_frameSize) != 0) { @@ -200,10 +200,10 @@ __m128 GainControl::computeGainFix(const __m128* in, size_t sizeIn) __m128 GainControl::computeGainMax(const __m128* in, size_t sizeIn) { - __u128 gain128; - __u128 min128; - __u128 max128; - __u128 tmp128; + u128_union_t gain128; + u128_union_t min128; + u128_union_t max128; + u128_union_t tmp128; static const __m128 factor128 = _mm_set1_ps(0x7fff); //////////////////////////////////////////////////////////////////////// @@ -250,10 +250,10 @@ __m128 GainControl::computeGainMax(const __m128* in, size_t sizeIn) __m128 GainControl::computeGainVar(const __m128* in, size_t sizeIn) { - __u128 gain128; - __u128 mean128; - __u128 var128; - __u128 tmp128; + u128_union_t gain128; + u128_union_t mean128; + u128_union_t var128; + u128_union_t tmp128; static const __m128 factor128 = _mm_set1_ps(0x7fff); mean128.m = _mm_setzero_ps(); @@ -297,8 +297,8 @@ __m128 GainControl::computeGainVar(const __m128* in, size_t sizeIn) var128.m = _mm_add_ps(var128.m, q128); /* - __u128 udiff128; udiff128.m = diff128; - __u128 udelta128; udelta128.m = delta128; + u128_union_t udiff128; udiff128.m = diff128; + u128_union_t udelta128; udelta128.m = delta128; for (int off=0; off<4; off+=2) { printf("S %zu, %.2f+%.2fj\t", sample, diff --git a/src/Utils.cpp b/src/Utils.cpp index 78f36f0..23e0aa5 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -89,8 +89,8 @@ void printUsage(const char* progName) "\n\t" #if defined(HAVE_OUTPUT_UHD) " [-G txgain]" - " [-T filter_taps_file]" #endif // defined(HAVE_OUTPUT_UHD) + " [-T filter_taps_file]" " [-a gain]" " [-c clockrate]" "\n\t" @@ -114,9 +114,9 @@ void printUsage(const char* progName) fprintf(out, "-u device: Use UHD output with given device string. (use "" for default device)\n"); fprintf(out, "-F frequency: Set the transmit frequency when using UHD output. (mandatory option when using UHD)\n"); fprintf(out, "-G txgain: Set the transmit gain for the UHD driver (default: 0)\n"); +#endif // defined(HAVE_OUTPUT_UHD) fprintf(out, "-T taps_file: Enable filtering before the output, using the specified file containing the filter taps.\n"); fprintf(out, " Use 'default' as taps_file to use the internal taps.\n"); -#endif // defined(HAVE_OUTPUT_UHD) fprintf(out, "-a gain: Apply digital amplitude gain.\n"); fprintf(out, "-c rate: Set the DAC clock rate and enable Cic Equalisation.\n"); fprintf(out, "-g gainmode: Set computation gain mode: fix, max or var\n"); |