aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--INSTALL.md85
-rw-r--r--README.md24
-rw-r--r--TODO20
-rw-r--r--configure.ac4
-rw-r--r--lib/Json.cpp4
-rw-r--r--lib/Json.h4
-rw-r--r--lib/Socket.cpp22
-rw-r--r--lib/Socket.h10
-rw-r--r--lib/ThreadsafeQueue.h38
-rw-r--r--lib/fec/decode_rs.h12
-rw-r--r--src/Buffer.h3
-rw-r--r--src/GainControl.cpp24
-rw-r--r--src/Utils.cpp4
14 files changed, 161 insertions, 102 deletions
diff --git a/ChangeLog b/ChangeLog
index e49c9c0..588013c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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):
diff --git a/INSTALL.md b/INSTALL.md
index b53e204..5ec1fc0 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -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
+
+[![Packaging status](https://repology.org/badge/vertical-allrepos/odr-dabmod.svg)](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.
diff --git a/README.md b/README.md
index a331c8b..2ec479a 100644
--- a/README.md
+++ b/README.md
@@ -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/>
diff --git a/TODO b/TODO
index 4580945..6fd9553 100644
--- a/TODO
+++ b/TODO
@@ -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"
diff --git a/lib/Json.h b/lib/Json.h
index b082f92..0168583 100644
--- a/lib/Json.h
+++ b/lib/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");