ODR-DabMod-2.6.0/ 0000755 0001750 0001750 00000000000 14230010227 012262 5 ustar robin robin ODR-DabMod-2.6.0/NEWS 0000644 0001750 0001750 00000000000 14230010227 012747 0 ustar robin robin ODR-DabMod-2.6.0/LICENCE 0000644 0001750 0001750 00000001710 14230010227 013246 0 ustar robin robin LICENSING
=========
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 2012
Her Majesty the Queen in Right of Canada (Communications Research
Center Canada)
Copyright (C) 2013, 2014
Matthias P. Braendli, http://mpb.li
This file is part of ODR-DabMod. ODR-DabMod is a fork of CRC-DabMod,
which was developed by the Communications Research Center Canada.
ODR-DabMod is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
ODR-DabMod is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with ODR-DabMod. If not, see .
ODR-DabMod-2.6.0/INSTALL.md 0000644 0001750 0001750 00000007555 14230010227 013726 0 ustar robin robin Required dependencies:
======================
* A C++11 capable compiler
* pkg-config
* FFTW 3.x
* autoconf
* Optional UHD for USRP
* Optional LimeSuite for LimeSDR support
* Optional SoapySDR (see below)
* Optional bladerf (see below)
* Optional ZeroMQ http://www.zeromq.org
Simple install procedure:
=========================
% tar xjf odr-dabmod-X.Y.Z.tar.bz2 # Unpack the source
% cd odr-dabmod-X.Y.Z # Change to the source directory
% ./configure
# Run the configure script
% make # Build ODR-DabMod
[ as root ]
% make install # Install ODR-DabMod
Configure options
=================
The configure script can be launched with a variety of options:
Disable ZeroMQ input (to be used with ODR-DabMux), 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`
On some ARM systems you might need `--with-boost-libdir=/usr/lib/arm-linux-gnueabihf` so that boost gets found.
Do not pass `-march=native` to the compiler: `--disable-native`
**Remark:** Do not compile ODR-DabMod with `-march=native` compiler option. This is meant for distribution package maintainers who want to use their own march option, and for people running into compilation issues due to `-march=native`. (e.g. GCC bug 70132 on ARM systems)
**Debugging options:** You should not enable any debug option if you need good performance.
Create debugging files for each DSP block for data analysis: `--enable-trace`
For more information, call:
% ./configure --help
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`
Applying all together:
% ./configure CFLAGS="-O3 -DNDEBUG" CXXFLAGS="-O3 -DNDEBUG" --enable-fast-math
Checking for memory usage issues
--------------------------------
If your compiler supports it, you can enable the address sanitizer to check for memory
issues:
% ./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.
Nearly as simple install procedure using repository:
====================================================
* Download and install dependencies as above
* Clone the git repository
* Bootstrap autotools:
% ./bootstrap.sh
In case this fails, try:
% aclocal && automake --gnu --add-missing && autoconf
* Then use `./configure` as above
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
2. The LimeSuite for the LimeSDR from https://github.com/myriadrf/LimeSuite
3. 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
not.
A configuration example is available in `doc/example.ini`
BladeRF support
===============
In order to use `--enable-bladerf`, you need to install the `libbladerf2` including the -dev package.
ODR-DabMod-2.6.0/doc/ 0000755 0001750 0001750 00000000000 14230010227 013027 5 ustar robin robin ODR-DabMod-2.6.0/doc/example.ini 0000644 0001750 0001750 00000030514 14230010227 015166 0 ustar robin robin ; Sample configuration file for ODR-DabMod
[remotecontrol]
; The RC feature is described in detail in doc/README-RC.md
; enable the telnet remote control on localhost:2121
telnet=1
telnetport=2121
; Enable zmq remote control.
zmqctrl=1
zmqctrlendpoint=tcp://127.0.0.1:9400
; accepted formats according to man zmq_bind, i.e.
; tcp://:, e.g. tcp://lo:9400
; and tcp://:
[log]
; Write to a logfile or to syslog.
; Setting filename to stderr is not necessary, as all messages are
; automatically written to stderr.
syslog=0
filelog=0
filename=odr-dabmod.log
; If you don't want to see the flowgraph processing time, set:
;show_process_time=0
[input]
; A file or fifo input is using transport=file
transport=file
source=/dev/stdin
; When the end of file is reached, it is possible to rewind it
loop=0
; EDI input.
; Listen for EDI data on a given UDP port, unicast or multicast.
;transport=edi
;
; EDI over TCP:
;
; Connect to TCP server on a given host
;source=tcp://localhost:9201
;
; EDI over UDP:
;
; Supported syntax for the source setting:
; Bind to default interface and receive data from port 12000
;source=udp://:12000
;
; Bind to interface with IP:192.168.1.22 and receive data from port 12000
;source=udp://192.168.1.22:12000
;
; Bind to interface with IP:192.168.1.22 and join multicast group:
; 239.100.101.22 and receive data from port 12000
;source=udp://192.168.1.22@239.100.101.22:12000
;
; Bind to default interface (which routes to multicast) and join multicast
; group: 239.100.101.22 and receive data from port 12000
;source=udp://@239.100.101.22:12000
;
; Maximum delay in milliseconds that the EDI input is willing to wait
; before it timeouts
;edi_max_delay=240
; This EDI implementation does not support EDI Packet Resend
; ETI-over-TCP example:
;transport=tcp
;source=localhost:9200
; When recieving data using ZeroMQ, the source is the URI to be used
;transport=zeromq
;source=tcp://localhost:9100
; The option max_frames_queued defines the maximum number of ETI frames
; (frame duration: 24ms) that can be in the input queue
;max_frames_queued=100
[modulator]
; Mode 'fix' uses a fixed factor and is really not recommended. It is more
; useful on an academic perspective for people trying to understand the DAB
; modulation.
;
; Mode 'max' is the normalization of every OFDM symbol. No overshoot, no
; truncating, but varying output power (around 3dB) which might not be the best
; for some power amplifier. The output samples are limited to a magnitude
; of 32768.
;
; Mode 'var' uses the method specified in ETSI 300 798 Clause 5.3. This method
; normalizes to 4 times the standard deviation for an approximation of the RMS
; power. So around 6/100000 samples will be truncated and will introduce some
; really minor distortion. But this mode also maximizes the output power. This
; is the gain mode recommended for real world operation as it is based on a DAB
; standard; the only difference is that ODR-DabMod internally calculates this with
; 32-bit floats instead of 8 bits.
gainmode=var
;
; In mode var, you can choose to normalise the samples to something else than
; 4 times the standard deviation.
;normalise_variance=4
; Transmission mode
; If not defined, use Transmission Mode 1
;mode=1
; The digital gain is a value that is multiplied to each sample. It is used
; to tune the chain to make sure that no non-linearities appear up to the
; USRP daughterboard programmable gain amplifier (PGA).
; If there is clipping, the spectral quality of the signal will quickly deteriorate,
; and wide-band noise will be generated.
;
; Be aware that there is a dependency with resampling.
digital_gain=0.8
; Output sample rate. Values other than 2048000 enable
; resampling.
; Warning! digital_gain settings are different if resampling
; is enabled or not !
rate=2048000
; (DEPRECATED) CIC equaliser for USRP1 and USRP2
; These USRPs have an upsampler in FPGA that does not have a flat frequency
; response. The CIC equaliser compensates this. This setting is specific to
; the USRP1 and USRP2 devices.
; Set to 0 to disable CicEqualiser
;dac_clk_rate=0
; The USRP1 does not have flexible clocking, you will need
;rate=3200000
; and
;dac_clk_rate=128000000
; When nonzero, overlap ofdmwindowing samples from each OFDM symbol
; onto the previous and next symbol, using a raised cosine window function.
; This has the effect of smoothing the transition from one symbol to the next,
; which improves spectrum shape.
; In Transmission Mode I, every data symbol is composed of 2552 samples.
;ofdmwindowing=10
; Settings for crest factor reduction. Statistics for ratio of
; samples that were clipped are available through the RC.
[cfr]
enable=0
; At what amplitude the signal should be clipped
clip=50.0
; How much to clip the error signal used to compensate the effect
; of clipping
error_clip=0.1
[firfilter]
; The FIR Filter can be used to create a better spectral quality.
enabled=1
; The filter taps can be calculated with the python script
; doc/fir-filter/generate-filter.py
; If filtertapsfile is not given, the default taps are used.
;filtertapsfile=simple_taps.txt
[poly]
;Predistortion using memoryless polynom, see dpd/ folder for more info
enabled=0
polycoeffile=polyCoefs
[output]
; choose output: possible values: uhd, file, zmq, soapysdr, limesdr, bladerf
output=uhd
[fileoutput]
; Two output formats are supported: In the default mode,
; the file output writes I/Q float values (i.e. complex
; float) to the file. The I and Q samples can take values up
; to 810000 in absolute magnitude with gainmode FIX. With
; gainmode VAR and FIX, they should never exceed 50000.
;format=complexf
;
; The complexf_normalised format applies a compensation factor to the complexf
; output to bring the range of the I and Q components to [-1.0 .. 1.0]. The
; digital_gain is still applied on top of that normalisation.
;format=complexf_normalised
;
; When the format is set to s8, the output writes I/Q 8-bit
; signed integers, where the magnitude is multiplied by 128/50000
; effectively mapping the gainmode VAR range of -50000 -- 50000
; to -128 -- 128. For other gainmodes, use the digital_gain setting
; to make sure you don't create clipping.
;
; The format u8 is the same as s8, except that the values are mapped
; between 0 and 255. Use u8 for welle.io, qt-dab or other tools.
;
; Also supported is s16, with system endianness (little endian on x86_64 and ARM)
;format=s8
; The output file:
filename=ofdm.iq
show_metadata=0
[uhdoutput]
; The UHD output can be directly used with the Ettus USRP devices
;
; You have to set master_clock_rate to a multiple of the
; sample_rate. Ideally, it should be
; master_clock_rate = 4 * sample_rate
; or even a higher factor.
;
; Settings for the B200:
device=
master_clock_rate=32768000
type=b200
txgain=40
; The B200 needs larger gains (up to 89dB) but,
; "Gain settings are application specific, but it is recommended that users
; consider using at least half of the available gain to get reasonable dynamic
; range."
; From the B200 User Manual
; http://files.ettus.com/uhd_docs/manual/html/usrp_b200.html
;
; More information and measurements available on:
; http://wiki.opendigitalradio.org/index.php/USRP_B200_Measurements
; You can set what TX and RX antennas to use. This will depend on the
; USRP model you are using.
;tx_antenna=
;rx_antenna=RX2
; Settings for a USRP B100:
;device=
; you can put additional UHD device settings here
;master_clock_rate=32768000
;type=b100
;txgain=2.0
; Try first with small gain values
; Also set rate to 2048000
; For the USRP1
;device=
;type=usrp1
; the usrp1 can have two daughterboards, the subdevice parameter allows you
; to choose which one to use
;subdevice=A:0
; The USRP1 doesn't support master_clock_rate, you need to enable resampling
; You must specify either frequency or channel, but not both.
;frequency=234208000
channel=13C
; Override automatic analog frontend bandwidth calculation. Units: Hz
;bandwidth=2000000
; Some USRP boards/frontends support setting an LO offset that has the
; effect of shifting DC out of the signal bandwidth. This should also
; improve IQ imbalance effects, because the mirror will centered on another
; frequency (it should be on frequency + 2*lo_offset)
;
; The value can be negative, and its absolute value must be smaller than
; master_clock_rate/2.
;lo_offset=2048000
; The reference clock to use. The gpsdo is the ODR LEA-M8F board, the
; official Ettus GPSDO is selected with gpsdo-ettus
; possible values : internal, external, MIMO, gpsdo, gpsdo-ettus
refclk_source=internal
; The reference one pulse-per second to use
; possible values : none, external, MIMO, gpsdo
pps_source=none
; Behaviour when external clock reference lock lost
; possible values: ignore, crash
behaviour_refclk_lock_lost=ignore
; The maximum accepted holdover time for the gpsdo once it
; started operating. Initial check interval for GPSDO lock
; at startup is always 180s.
; Valid only if the refclk and pps_source are set to gpsdo.
; Units: seconds
; Set to 0 to disable holdover check
; default value: 0
max_gps_holdover_time=600
; Enable the TCP server to communicate TX and RX feedback for
; digital predistortion.
; Set to 0 to disable
;dpd_port=50055
; section defining ZeroMQ output properties
[zmqoutput]
; on which port to listen for connections
; please see the Transports section in man zmq
; for more information regarding the syntax
listen=tcp://*:54001
; what ZMQ socket type to use. Valid values: PUB, REP
; Please see man zmq_socket for documentation
socket_type=pub
; section defining the SoapySDR output settings.
[soapyoutput]
; These options are given to the SoapySDR library:
device=
master_clock_rate=32768000
txgain=40
;frequency=234208000
channel=13C
;lo_offset=2048000
; Override automatic analog frontend bandwidth calculation. Units: Hz
;bandwidth=2000000
; You can set what TX antenna to use. This will depend on the
; SDR device you are using.
;tx_antenna=
; Enable the TCP server to communicate TX and RX feedback for
; digital predistortion.
; Set to 0 to disable
;dpd_port=50055
[limeoutput]
; Lime output directly runs against the LMS device driver. It does not support SFN nor predistortion.
device=
;master_clock_rate=
; txgain range: 0 .. 100
txgain=20
tx_antenna=BAND1
;lo_offset=2048000
;frequency=234208000
channel=13C
; The LimeSDR contains a FIR filter in FPGA that can be used to filter the IQ signal.
; This is useful because it allows us to upsample in a very cheap way in software instead
; of using the FFT-based resampler.
upsample=1
; section defining the BladeRF output settings.
[bladerfoutput]
; bladerfoutput is currently under development
device=
; The reference clock to use.
; possible values : pps, 10mhz
; Others values than above lead to disable refclk_src
refclk_source=
;master_clock_rate, not configurable with bladeRF, fundamental clock runs at 38.4 MHz
; txgain range: -23.75 .. 66 [dB]
txgain = 20
;tx_antenna -> not available
channel = 13C
bandwidth = 1800000
; Used for running single-frequency networks
[delaymanagement]
; Enable handling of timestamps for SFN
synchronous=0
; Whether to mute the TX when incoming frames have no timestamp
mutenotimestamps=0
; This offset is added to the TIST, and the sum defines the
; TX time of the transmission frame. It can by changed at runtime
; through the remote control.
offset=0.002
; The way the timestamps are interpreted in ODR-DabMod up to v1.1.0 was not
; specified, and you should not assume that two different versions will
; transmit synchronously given the same settings. Always run SFNs with
; identical versions!
; Furthermore, when combining ODR-DabMod with third-party modulators, always
; measure!
[tii]
; If these options are set, TII transmission is enabled.
; DAB modes I and II are supported, and must be set explicitly in
; this file. Reading DAB mode from ETI is not supported.
enable=0
; comb is also known as sub-identifier.
comb=1
; pattern is also known as main-identifier. If you run several transmitters
; in SFN, it is better to use the same pattern for all, and vary the comb.
; Otherwise identification of the transmitters may be more difficult.
pattern=11
; There are two variants of TII being used. The old variant that uses the wrong
; phase on the second carrier in each carrier pairs and is therefore not
; conforming to the specification. Modern analysers can decode both variants,
; while others, like the Philips DAB752 and the VAD v2 monitor are known to
; decode only the old non-conforming variant.
old_variant=0
ODR-DabMod-2.6.0/doc/README-RC.md 0000644 0001750 0001750 00000004237 14230010227 014616 0 ustar robin robin Remote Control Interface
========================
The RC interface allows you to change settings at runtime and to access some
statistics. Two interfaces are available: Telnet and based on ZeroMQ.
The Telnet interface is designed for human interaction. Once you have enabled
the interface and set the port, use any telnet client to connect to the server
to get the RC command line interface. Since this is totally unsecure telnet,
the software will only listen on the local loopback interface. To get secure
remote access, use SSH port forwarding.
The ZeroMQ interface is designed for machine interaction, e.g. for usage in
scripts or from third party tools. The Munin monitoring is also using this
interface, please see `doc/stats_dabmod_munin.py`.
An example python script to connect to that
interface is available in `doc/zmq-ctrl/zmq_remote.py`,
and example C++ code is available in `doc/zmq-ctrl/cpp/`.
Both interfaces may be enabled simultaneously.
Statistics available
--------------------
The following statistics are presented through the RC:
* Value of TIST in `tist timestamp`
* UHD: number of underruns, overruns and frames transmitted
* SoapySDR: number of underruns and overruns
* OFDM Generator: CFR stats and MER after CFR (if CFR enabled) in `ofdm clip_stats`
* OFDM Generator: PAPR before and after CFR in `ofdm papr`
More statistics are likely to be added in the future, and we are always open
for suggestions.
ZMQ RC Protocol
---------------
ODR-DabMod binds a zmq rep socket so clients must connect
using either req or dealer socket.
[] denotes message part as zmq multi-part message are used for delimitation.
All message parts are utf-8 encoded strings and match the Telnet command set.
Messages to be sent as literal strings are denoted with "" below.
The following commands are supported:
REQ: ["ping"]
REP: ["ok"]
REQ: ["list"]
REP: ["ok"][module name][module name]...
REQ: ["show"][module name]
REP: ["ok"][parameter: value][parameter: value]...
REQ: ["get"][module name][parameter]
REP: [value] _OR_ ["fail"][error description]
REQ: ["set"][module name][parameter][value]
REP: ["ok"] _OR_ ["fail"][error description]
ODR-DabMod-2.6.0/doc/zmq-ctrl/ 0000755 0001750 0001750 00000000000 14230010227 014600 5 ustar robin robin ODR-DabMod-2.6.0/doc/zmq-ctrl/cpp/ 0000755 0001750 0001750 00000000000 14230010227 015362 5 ustar robin robin ODR-DabMod-2.6.0/doc/zmq-ctrl/cpp/test/ 0000755 0001750 0001750 00000000000 14230010227 016341 5 ustar robin robin ODR-DabMod-2.6.0/doc/zmq-ctrl/cpp/test/ctrl_test.cpp 0000644 0001750 0001750 00000005327 14230010227 021057 0 ustar robin robin /**
* This is a test program for the zmq ctrl API of the odr-dabmod.
*
* Copyright (c) 2015 by Jörgen Scott (jorgen.scott@paneda.se)
*
* This file is part of CtrlTest.
*
* ODR-DabMod is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* ODR-DabMod is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ODR-DabMod. If not, see .
**/
#define BOOST_TEST_MODULE "C++ unit tests for ODR-DabMod ZMQ Remote Control"
#include
#include "OdrModCtrl.hpp"
// Variables used in the test suite
struct TemplateVars
{
std::string error;
zmq::context_t context;
COdrModCtrl modCtrl;
// NOTE: Make sure odr-dabmod is started before running the test and
// that the zmq endpoint matches.
TemplateVars() : context(1), modCtrl(&context, "tcp://127.0.0.1:9400", 1000) {}
~TemplateVars() {}
};
// Note. ODR-DabMod does not validate parameters therefore there are no tests
// made for setting invalid parameters.
BOOST_FIXTURE_TEST_SUITE(test_template1, TemplateVars)
BOOST_AUTO_TEST_CASE (Ping)
{
BOOST_CHECK(modCtrl.Ping() == true);
}
BOOST_AUTO_TEST_CASE (DigitalGain)
{
BOOST_CHECK(modCtrl.SetDigitalGain(0.5, error) == true);
double value;
BOOST_CHECK(modCtrl.GetDigitalGain(value, error) == true);
BOOST_CHECK(value == 0.5);
}
BOOST_AUTO_TEST_CASE (TxGain)
{
BOOST_CHECK(modCtrl.SetTxGain(50, error) == true);
double value;
BOOST_CHECK(modCtrl.GetTxGain(value, error) == true);
BOOST_CHECK(value == 50);
}
BOOST_AUTO_TEST_CASE (TxFrequency)
{
BOOST_CHECK(modCtrl.SetTxFrequency(234208000, error) == true);
double value;
BOOST_CHECK(modCtrl.GetTxFrequency(value, error) == true);
BOOST_CHECK(value == 234208000);
}
BOOST_AUTO_TEST_CASE (Muting)
{
BOOST_CHECK(modCtrl.SetMuting(true, error) == true);
bool value;
BOOST_CHECK(modCtrl.GetMuting(value, error) == true);
BOOST_CHECK(value == true);
BOOST_CHECK(modCtrl.SetMuting(false, error) == true);
}
BOOST_AUTO_TEST_CASE (StaticDelay)
{
// reset first (by setting out of range value) or else test
// will fail on successive runs
BOOST_CHECK(modCtrl.SetStaticDelay(100000, error) == true);
BOOST_CHECK(modCtrl.SetStaticDelay(45000, error) == true);
uint32_t value;
BOOST_CHECK(modCtrl.GetStaticDelay(value, error) == true);
BOOST_CHECK(value == 45000);
}
BOOST_AUTO_TEST_SUITE_END()
ODR-DabMod-2.6.0/doc/zmq-ctrl/cpp/test/README 0000644 0001750 0001750 00000000555 14230010227 017226 0 ustar robin robin Instructions for zmq ctrl api test program
Dependencies boost, zmq (and cpp binding through zmq.hpp)
Build instruction (make sure your in the directory of this file)
* mkdir build
* cd build
* cmake ../
* make
Run
* make sure the ODR-DABMOD is started and that zmq ctrl api is enabled
* make sure the zmq endpoint matches (see ctrl_test.cpp)
* run the ctrl_test
ODR-DabMod-2.6.0/doc/zmq-ctrl/cpp/test/CMakeLists.txt 0000755 0001750 0001750 00000001344 14230010227 021106 0 ustar robin robin cmake_minimum_required(VERSION 2.6)
project (ctrl_test)
ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -D_SCL_SECURE_NO_WARNINGS)
set(BOOST_LIBRARYDIR)
set(BOOST_INCLUDEDIR)
set(BOOST_USE_MULTITHREADED ON)
set(BOOST_USE_STATIC_LIBS ON)
set(BOOST_MIN_VERSION 1.55)
find_package( Boost ${BOOST_MIN_VERSION} REQUIRED
unit_test_framework
system
)
set(PROJECT_TEST_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/ctrl_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../OdrModCtrl.cpp
)
include_directories( ${PROJECT_SOURCE_DIR}/../ )
link_directories (/usr/local/lib)
add_executable(${PROJECT_NAME} ${PROJECT_TEST_SRCS})
target_link_libraries(${PROJECT_NAME}
zmq
${Boost_LIBRARIES}
)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
ODR-DabMod-2.6.0/doc/zmq-ctrl/cpp/OdrModCtrl.cpp 0000644 0001750 0001750 00000020207 14230010227 020100 0 ustar robin robin /*!
* This is an implementation for the zmq ctrl API of the odr-dabmod.
*
* Copyright (c) 2015 by Jörgen Scott (jorgen.scott@paneda.se)
*
* ODR-DabMod is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* ODR-DabMod is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ODR-DabMod. If not, see .
*
* \code
* #include "OdrModCtrl.hpp"
* #include
* ...
* zmq::context_t ctx;
* std::string error;
* COdrModCtrl *pCtrl = new COdrModCtrl(&context, // zmq context
* "tcp://127.0.0.1:9400", // zmq endpoint
* 1000); // timeout in milliseconds
* if (pCtrl->SetTxGain(50, error))
* std::cout << "Tx gain set to 50" << std::endl;
* else
* std::cout << "An error occured: " << error << std::endl;
* delete pCtrl; // destructor will close zmq socket
*
* \endcode
**/
#include "OdrModCtrl.hpp"
#include
#define MOD_GAIN "gain"
#define MOD_UHD "uhd"
#define PARAM_DIG_GAIN "digital"
#define PARAM_TX_GAIN "txgain"
#define PARAM_FREQ "freq"
#define PARAM_MUTE "muting"
#define PARAM_STAT_DELAY "staticdelay"
COdrModCtrl::COdrModCtrl(zmq::context_t *pContext, std::string odrEndpoint,
unsigned int timeoutMs)
{
m_pContext = pContext;
m_odrEndpoint = odrEndpoint;
m_timeoutMs = (uint32_t) timeoutMs;
m_pReqSocket = NULL;
}
COdrModCtrl::~COdrModCtrl()
{
if (m_pReqSocket != NULL)
{
m_pReqSocket->close();
delete m_pReqSocket;
}
}
//// public get methods /////////////////////////////////////////////////////////
bool COdrModCtrl::GetDigitalGain(double &gain, std::string &error)
{
return DoGet(MOD_GAIN, PARAM_DIG_GAIN, gain, error);
}
bool COdrModCtrl::GetTxGain(double &gain, std::string &error)
{
return DoGet(MOD_UHD, PARAM_TX_GAIN, gain, error);
}
bool COdrModCtrl::GetTxFrequency(double &freqHz, std::string &error)
{
return DoGet(MOD_UHD, PARAM_FREQ, freqHz, error);
}
bool COdrModCtrl::GetMuting(bool &mute, std::string &error)
{
return DoGet(MOD_UHD, PARAM_MUTE, (uint32_t&) mute, error);
}
bool COdrModCtrl::GetStaticDelay(uint32_t &delayUs, std::string &error)
{
return DoGet(MOD_UHD, PARAM_STAT_DELAY, delayUs, error);
}
//// public set methods /////////////////////////////////////////////////////////
bool COdrModCtrl::Ping()
{
std::string error;
if (m_pReqSocket == NULL)
{
m_pReqSocket = new zmq::socket_t(*m_pContext, ZMQ_REQ);
if (!ConnectSocket(m_pReqSocket, m_odrEndpoint, error))
return false;
}
std::vector msg;
msg.push_back("ping");
// send the message
if (!SendMessage(m_pReqSocket, msg, error))
{
// destroy the socket according to the "Lazy Pirate Pattern" in
// the zmq guide
m_pReqSocket->close();
delete m_pReqSocket;
m_pReqSocket = NULL;
return false;
}
// wait for reply
if (!RecvAll(m_pReqSocket, msg, m_timeoutMs, error))
return false;
return true;
}
bool COdrModCtrl::SetDigitalGain(const double gain, std::string &error)
{
return DoSet(MOD_GAIN, PARAM_DIG_GAIN, gain, error);
}
bool COdrModCtrl::SetTxGain(const double gain, std::string &error)
{
return DoSet(MOD_UHD, PARAM_TX_GAIN, gain, error);
}
bool COdrModCtrl::SetTxFrequency(const double freqHz, std::string &error)
{
return DoSet(MOD_UHD, PARAM_FREQ, freqHz, error);
}
bool COdrModCtrl::SetMuting(const bool mute, std::string &error)
{
return DoSet(MOD_UHD, PARAM_MUTE, mute, error);
}
bool COdrModCtrl::SetStaticDelay(const int32_t delayUs, std::string &error)
{
return DoSet(MOD_UHD, PARAM_STAT_DELAY, delayUs, error);
}
//// private methods ////////////////////////////////////////////////////////////
template
bool COdrModCtrl::DoSet(const std::string module, const std::string parameter,
const Type value, std::string &error)
{
if (m_pReqSocket == NULL)
{
m_pReqSocket = new zmq::socket_t(*m_pContext, ZMQ_REQ);
if (!ConnectSocket(m_pReqSocket, m_odrEndpoint, error))
return false;
}
std::vector msg;
msg.push_back("set");
msg.push_back(module);
msg.push_back(parameter);
std::stringstream ss;
ss << value;
msg.push_back(ss.str());
// send the message
if (!SendMessage(m_pReqSocket, msg, error))
{
// destroy the socket according to the "Lazy Pirate Pattern" in
// the zmq guide
m_pReqSocket->close();
delete m_pReqSocket;
m_pReqSocket = NULL;
return false;
}
// wait for reply
if (!RecvAll(m_pReqSocket, msg, m_timeoutMs, error))
return false;
return ParseSetReply(msg, error);
}
bool COdrModCtrl::ParseSetReply(const std::vector &msg,
std::string &error)
{
error = "";
if (msg.size() < 1)
error = "Bad reply format";
else if (msg.size() == 1 && msg[0] == "ok")
return true;
else if (msg.size() == 2 && msg[0] == "fail")
{
error = msg[1];
return false;
}
else
{
error = "Bad reply format";
return false;
}
}
template
bool COdrModCtrl::DoGet(const std::string module, const std::string parameter,
Type &value, std::string &error)
{
if (m_pReqSocket == NULL)
{
m_pReqSocket = new zmq::socket_t(*m_pContext, ZMQ_REQ);
if (!ConnectSocket(m_pReqSocket, m_odrEndpoint, error))
return false;
}
std::vector msg;
msg.push_back("get");
msg.push_back(module);
msg.push_back(parameter);
// send the message
if (!SendMessage(m_pReqSocket, msg, error))
{
// destroy the socket according to the "Lazy Pirate Pattern"
// in the zmq guide
m_pReqSocket->close();
delete m_pReqSocket;
m_pReqSocket = NULL;
return false;
}
// wait for reply
if (!RecvAll(m_pReqSocket, msg, m_timeoutMs, error))
return false;
return ParseGetReply(msg, value, error);
}
template
bool COdrModCtrl::ParseGetReply(const std::vector &msg,
Type &value, std::string &error)
{
error = "";
if (msg.size() < 1)
error = "Bad reply format";
else if (msg.size() == 1)
{
std::stringstream ss(msg[0]);
ss >> value;
return true;
}
else if (msg.size() == 2 && msg[0] == "fail")
{
error = msg[1];
return false;
}
else
{
error = "Bad reply format";
return false;
}
}
bool COdrModCtrl::ConnectSocket(zmq::socket_t *pSocket, const std::string endpoint,
std::string &error)
{
error = "";
try
{
int hwm = 1;
int linger = 0;
pSocket->setsockopt(ZMQ_RCVHWM, &hwm, sizeof(hwm));
pSocket->setsockopt(ZMQ_SNDHWM, &hwm, sizeof(hwm));
pSocket->setsockopt(ZMQ_LINGER, &linger, sizeof(linger));
pSocket->connect(endpoint.c_str());
return true;
}
catch(zmq::error_t &ex)
{
error = "Failed to connect: " + endpoint +
std::string(". ZMQ: " + std::string(ex.what()));
return false;
}
}
bool COdrModCtrl::SendMessage(zmq::socket_t* pSocket,
const std::vector &message, std::string &error)
{
error = "";
try
{
std::vector::size_type i = 0;
for ( ; i < message.size() - 1; i++)
{
zmq::message_t zmqMsg(message[i].length());
memcpy ((void*) zmqMsg.data(), message[i].data(), message[i].length());
pSocket->send(zmqMsg, ZMQ_SNDMORE);
}
zmq::message_t zmqMsg(message[i].length());
memcpy ((void*) zmqMsg.data(), message[i].data(), message[i].length());
pSocket->send(zmqMsg, 0);
return true;
}
catch(zmq::error_t &ex)
{
error = "ZMQ send error: " + std::string(ex.what());
return false;
}
}
bool COdrModCtrl::RecvAll(zmq::socket_t* pSocket,
std::vector &message, unsigned int timeoutMs,
std::string &error)
{
error = "";
message.clear();
int more = -1;
size_t more_size = sizeof(more);
zmq::pollitem_t pollItems[] = { {*pSocket, 0, ZMQ_POLLIN, 0} };
zmq::poll(&pollItems[0], 1, timeoutMs);
while (more != 0)
{
if (pollItems[0].revents & ZMQ_POLLIN)
{
zmq::message_t msg;
pSocket->recv(&msg);
message.push_back(std::string((char*)msg.data(), msg.size()));
pSocket->getsockopt(ZMQ_RCVMORE, &more, &more_size);
}
else
{
error = "Receive timeout";
return false;
}
}
return true;
}
ODR-DabMod-2.6.0/doc/zmq-ctrl/cpp/OdrModCtrl.hpp 0000644 0001750 0001750 00000005675 14230010227 020121 0 ustar robin robin /**
* This is an interface for the zmq ctrl API of the odr-dabmod.
* The class is intended for clients that wish to control the odr-mod.
*
* Copyright (c) 2015 by Jörgen Scott (jorgen.scott@paneda.se)
*
* ODR-DabMod is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* ODR-DabMod is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ODR-DabMod. If not, see .
**/
#pragma once
#include
#include
#include
#include
class COdrModCtrl
{
public:
// ctors
COdrModCtrl(zmq::context_t *pContext, std::string odrEndpoint,
unsigned int timeoutMs);
virtual ~COdrModCtrl();
// All methods return true if successful, when false check the error
// string.
//
// IMPORTANT! All methods must be accessed from the same thread.
//
// For a detailed description of the various parameters, see
// example.ini.
virtual bool Ping(void);
virtual bool GetDigitalGain(double &gain, std::string &error);
virtual bool GetTxGain(double &gain, std::string &error);
virtual bool GetTxFrequency(double &freqHz, std::string &error);
virtual bool GetMuting(bool &mute, std::string &error);
virtual bool GetStaticDelay(uint32_t &delayUs, std::string &error);
virtual bool SetDigitalGain(const double gain, std::string &error);
virtual bool SetTxGain(const double gain, std::string &error);
virtual bool SetTxFrequency(const double freqHz, std::string &error);
virtual bool SetMuting(const bool mute, std::string &error);
virtual bool SetStaticDelay(const int32_t delayUs, std::string &error);
private:
// methods
template
bool DoSet(const std::string module, const std::string parameter,
const Type value, std::string &error);
bool ParseSetReply(const std::vector &msg, std::string &error);
template
bool DoGet(const std::string module, const std::string parameter,
Type &value, std::string &error);
template
bool ParseGetReply(const std::vector &msg, Type &value,
std::string &error);
bool ConnectSocket(zmq::socket_t *pSocket, const std::string endpoint,
std::string &error);
bool SendMessage(zmq::socket_t* pSocket,
const std::vector &message, std::string &error);
bool RecvAll(zmq::socket_t* pSocket,
std::vector &message, unsigned int timeoutMs,
std::string &error);
// data
zmq::context_t *m_pContext;
std::string m_odrEndpoint;
uint32_t m_timeoutMs;
zmq::socket_t *m_pReqSocket;
};
ODR-DabMod-2.6.0/doc/zmq-ctrl/zmq_remote.py 0000755 0001750 0001750 00000004745 14230010227 017351 0 ustar robin robin #!/usr/bin/env python
#
# This is an example program that illustrates
# how to interact with the zeromq remote control
#
# LICENSE: see bottom of file
import sys
import zmq
context = zmq.Context()
sock = context.socket(zmq.REQ)
poller = zmq.Poller()
poller.register(sock, zmq.POLLIN)
if len(sys.argv) < 2:
print("Usage: program url cmd [args...]")
sys.exit(1)
sock.connect(sys.argv[1])
message_parts = sys.argv[2:]
# first do a ping test
print("ping")
sock.send(b"ping")
socks = dict(poller.poll(1000))
if socks:
if socks.get(sock) == zmq.POLLIN:
data = sock.recv_multipart()
print("Received: {}".format(len(data)))
for i,part in enumerate(data):
print(" {}".format(part))
for i, part in enumerate(message_parts):
if i == len(message_parts) - 1:
f = 0
else:
f = zmq.SNDMORE
print("Send {}({}): '{}'".format(i, f, part))
sock.send(part.encode(), flags=f)
data = sock.recv_multipart()
print("Received: {}".format(len(data)))
for i,part in enumerate(data):
print(" RX {}: {}".format(i, part.decode().replace('\n',' ')))
else:
print("ZMQ error: timeout")
context.destroy(linger=5)
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to
ODR-DabMod-2.6.0/doc/easydabv3.ini 0000644 0001750 0001750 00000001173 14230010227 015413 0 ustar robin robin ; This sample configuration is useful if ODR-DabMod is compiled
; with --enable-easydabv3
[remotecontrol]
zmqctrl=1
zmqctrlendpoint=tcp://127.0.0.1:9400
; There is no telnet RC available in this build
[log]
syslog=0
filelog=0
filename=odr-dabmod.log
[input]
transport=zeromq
source=tcp://localhost:9100
max_frames_queued=400
; There are no [modulator], [cfr], [firfilter], [poly] nor [tii] sections
[output]
output=file
[fileoutput]
; to be confirmed
format=complexf
filename=/dev/csdiof1
show_metadata=0
; TODO add option for writing out timestamps to csdiof1
[delaymanagement]
synchronous=0
mutenotimestamps=0
offset=1.002
ODR-DabMod-2.6.0/doc/time-freq-plot.py 0000755 0001750 0001750 00000005321 14230010227 016252 0 ustar robin robin #!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Print scope and spectrum from ODR-DabMod I/Q file
#
# The MIT License (MIT)
#
# Copyright (c) 2017 Matthias P. Braendli
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import matplotlib.pyplot as plt
import numpy as np
rate=2048000
# T = 1/2048000 s
# NULL symbol is 2656 T (about 1.3ms) long.
T_NULL = 2656
# Full transmission frame in TM1 is 96ms = 196608 T.
T_TF = 196608
num_skip_samples = 8 * T_TF
num_analyse_samples = 2 * T_TF
if len(sys.argv) < 2:
print("Specify .iq file name")
print("Expected format: complex float I/Q, 2048000 Sps")
print("The input file must contain at least 10 transmission frames,")
print("i.e. {} samples = {} seconds".format(T_TF * 10, T_TF * 10.0 / rate))
sys.exit(1)
fd = open(sys.argv[1], 'rb')
# The IQ files potentially have zero samples in the beginning, we need
# to skip a few transmission frames
source_data = np.fromfile(file=fd, dtype=np.complex64, count=num_skip_samples + num_analyse_samples)
print("Read in {} samples".format(len(source_data)))
source_data = source_data[num_skip_samples:]
source_data_time = np.linspace(0, num_analyse_samples/rate, len(source_data))
print("Signal power: {} of {} samples".format(np.sum(np.abs(source_data**2)), len(source_data)))
fft_size = 4096
plt.figure(figsize=(10,8))
plt.subplot(211)
plt.title("Real part of signal")
plt.plot(source_data_time, np.real(source_data))
signal_spectrum = np.abs(np.fft.fftshift(np.fft.fft(source_data[T_NULL:], fft_size)))
freqs = np.fft.fftshift(np.fft.fftfreq(fft_size, d=1./rate))
plt.subplot(212)
plt.title("Spectrum of {} samples after the NULL symbol".format(fft_size))
plt.semilogy(freqs, signal_spectrum)
plt.show()
ODR-DabMod-2.6.0/doc/README-SFN.md 0000644 0001750 0001750 00000010615 14230010227 014735 0 ustar robin robin On the Usage of ODR-DabMod for Synchronous Transmissions
========================================================
Summary
-------
ODR-DabMux and ODR-DabMod offer support for timestamped transmission
when the UHD output is used. This README explains how this functionality
works, and how to set it up.
This feature is a prerequisite for the creation of a single-frequency
network.
Concept
-------
The goal of this functionality is to synchronise the transmission for
several transmitters. This has been tested with the USRP B100, B200 and the
USRP2, that both have the necessary REFCLK and 1PPS inputs. Both are
required to synchronise two USRPs:
- The REFCLK is used in the USRP for timekeeping. If we want two
USRPs to stay synchronised, they both must have a precise 10MHz
source at the REFCLK, otherwise their internal clocks will drift
off.
- The 1PPS signal is used to set the time inside the USRPs. The rising
edge of the 1PPS signal has happen synchronously for all transmitters.
Usually, GPS is used to drive this 1PPS.
For such a system, there will be one multiplexer, which will send the ETI
stream to several modulators. The ETI stream, in this case, is transported
over the ZMQ interconnection.
Each modulator receives ETI frames that contain absolute timestamps, defining
the exact point in time when the frame has to be transmitted. These in-band
timestamps are composed of two parts:
- The TIST field as defined in the ETI standard, giving an offset after the
pulse per second signal;
- A time information transmitted using the MNSC, representing the precise time
when the frame must be transmitted, with one-second resolution.
When ODR-DabMux is configured accordingly, the TIST is defined in each frame.
The time is always encoded in the MNSC.
When the ETI stream is sent to several modulators using non-blocking I/O, it
is not possible to rely on a modulator to back-pressure the Ensemble multiplexer.
It is therefore necessary to throttle multiplexer output.
Each modulator then receives the ETI stream through a ZMQ connection. Each frame
contains the complete timestamp, to which an per-modulator offset is added.
The sum is then given to the USRP. The offset can be specified in the
configuration file of ODR-DabMod, and can be modified on the fly using
the remote control interface.
ODR-DabMod uses the UHD library to output modulated samples to the USRP device.
When started, it defines the USRP time using the local time and the PPS signal.
It is therefore important to synchronise computer time using NTP.
When a frame arrives with a timestamp that is in the past, the frame is dropped.
If the timestamp is too far in the future, the output module waits a short
delay.
Synchronisation can be verified by using an oscilloscope and a receiver. It is
very easy to see if the null symbols align. Then tune the receiver to the
ensemble, and alternatively lower the tx gain of each modulator to see if the
receiver is still able to receive the ensemble without hiccup.
Time and frequency references
-----------------------------
In addition to the 10MHz refclk and 1PPS inputs on the USRP, some USRPs also
support an integrated GPSDO. For the B200, there are two GPSDOs modules that
can be used: The Ettus GPSDO (Jackson Labs Firefly), and the u-blox LEA-M8F on
the [Opendigitalradio board](http://www.opendigitalradio.org/lea-m8f-gpsdo).
To use the LEA-M8F, some modifications in the UHD library are necessary, because
the module outputs a 30.72MHz refclk instead of a 10MHz. The changes are
available in the [ODR repository of UHD](https://github.com/Opendigitalradio/uhd),
with branch names called *lea-m8f-UHDVERSION*.
When using the integrated GPSDO, ODR-DabMod will also monitor if the GPS
reception is ok, and if the time reference is usable.
Hardware requirements
---------------------
The following hardware is required to build a SFN with the ODR-mmbTools:
- Two USRPs ;
- One or two computers with the mmbTools installed ;
- A network connection between the two computers ;
- A 10MHz refclk source ;
- A 1PPS source synchronised to the 10MHz ;
- An oscilloscope to check synchronisation.
It is possible to use signal generators as REFCLK source and 1PPS, if there is
no GPS-disciplined oscillator available. It is necessary to synchronise the
1PPS source to the 10MHz source.
###########
june 2012, initial version, Matthias P. Braendli
feb 2014, renamed crc-dabXYZ to odr-dabXYZ, mpb
sep 2015, overhaul, talk about GPSDOs, mpb
ODR-DabMod-2.6.0/doc/fir-filter/ 0000755 0001750 0001750 00000000000 14230010227 015072 5 ustar robin robin ODR-DabMod-2.6.0/doc/fir-filter/filtertaps.txt 0000644 0001750 0001750 00000001406 14230010227 020011 0 ustar robin robin 45
-0.00110450468492
0.00120703084394
-0.000840645749122
-0.000187368263141
0.00184351124335
-0.00355578539893
0.00419321097434
-0.00254214904271
-0.00183473504148
0.00781436730176
-0.0125957569107
0.0126200336963
-0.00537294941023
-0.00866683479398
0.0249746385962
-0.0356550291181
0.0319730602205
-0.00795613788068
-0.0363943465054
0.0938014090061
-0.151176810265
0.193567320704
0.791776955128
0.193567320704
-0.151176810265
0.0938014090061
-0.0363943465054
-0.00795613788068
0.0319730602205
-0.0356550291181
0.0249746385962
-0.00866683479398
-0.00537294941023
0.0126200336963
-0.0125957569107
0.00781436730176
-0.00183473504148
-0.00254214904271
0.00419321097434
-0.00355578539893
0.00184351124335
-0.000187368263141
-0.000840645749122
0.00120703084394
-0.00110450468492
ODR-DabMod-2.6.0/doc/fir-filter/simplefiltertaps.txt 0000644 0001750 0001750 00000000026 14230010227 021220 0 ustar robin robin 5
0.0
0.0
1.0
0.0
0.0
ODR-DabMod-2.6.0/doc/fir-filter/generate-filter.py 0000755 0001750 0001750 00000004443 14230010227 020531 0 ustar robin robin #!/usr/bin/env python
# This tool uses gnuradio to generate FIR filter taps
# that can be used for the FIRFilter function in
# ODR-DabMod
#
# Usage:
# 1) adapt the filter settings below
# 2) Call this script and redirect the output of this script into a file
#
# Requires:
# A recent gnuradio version (3.7)
#
#
# The MIT License (MIT)
#
# Copyright (c) 2013 Matthias P. Braendli
# http://mpb.li
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import gnuradio
from gnuradio import digital
# From documentation at
# http://gnuradio.org/doc/doxygen/classgr_1_1filter_1_1firdes.html
# use "window method" to design a low-pass FIR filter
#
# gain: overall gain of filter (typically 1.0)
# sampling_freq: sampling freq (Hz)
# cutoff_freq: center of transition band (Hz)
# transition_width: width of transition band (Hz).
# The normalized width of the transition band is what sets the number of taps required. Narrow --> more taps
# window_type: What kind of window to use. Determines maximum attenuation and passband ripple.
# beta: parameter for Kaiser window
gain = 1
sampling_freq = 2.048e6
cutoff = 810e3
transition_width = 250e3
# Generate filter taps and print them out
taps = digital.filter.firdes_low_pass(gain, sampling_freq, cutoff, transition_width) # hamming window
print(len(taps))
for t in taps:
print(t)
ODR-DabMod-2.6.0/doc/fir-filter/README 0000644 0001750 0001750 00000000270 14230010227 015751 0 ustar robin robin This tool can be used to generate FIRFilter taps for the
corresponding functionality in ODR-DabMod.
The filter parameters are defined in the script, they
can be easily adapted there.
ODR-DabMod-2.6.0/doc/stats_dabmod_munin.py 0000755 0001750 0001750 00000023710 14230010227 017261 0 ustar robin robin #!/usr/bin/env python2
#
# present statistics from ODR-DabMod's
# RC interface to munin. Expects ZeroMQ on port
# 9400.
#
# Copy this file to /etc/munin/plugins/dabmod
# to use it, and make sure it's executable (chmod +x)
import sys
import json
import zmq
import os
import re
# Values monitored:
config_all = ""
#default data type is GAUGE
# One GAUGE multigraph from 0% to 100% with
# ofdm clip_stats clip_ratio
# ofdm clip_stats errorclip_ratio
config_all += """
multigraph ofdm_clip_stats
graph_title OFDM CFR clip stats
graph_order clip_ratio errorclip_ratio
graph_vlabel number of samples/errors clipped during last ${{graph_period}}
graph_category dabmod
graph_info This graph shows CFR clipping statistics
clip_ratio.info Number of samples clipped
clip_ratio.label Number of samples clipped
clip_ratio.min 0
clip_ratio.max 100
errorclip_ratio.info Number of errors clipped
errorclip_ratio.label Number of errors clipped
errorclip_ratio.min 0
errorclip_ratio.max 100"""
# One GAUGE multigraph
# ofdm clip_stats mer
config_all += """
multigraph ofdm_clip_stats_mer
graph_title OFDM MER after CFR
graph_order mer
graph_vlabel MER in dB after CFR
graph_category dabmod
graph_info This graph shows MER after CFR
mer.info MER dB
mer.label MER dB
mer.min 0
mer.max 100"""
# One GAUGE multigraph in dB for
# ofdm papr before-cfr
# ofdm papr after-cfr
config_all += """
multigraph ofdm_papr
graph_title OFDM PAPR stats
graph_order before_cfr after_cfr
graph_args --base 1000
graph_vlabel Averate PAPR before/after CFR during last ${{graph_period}}
graph_category dabmod
graph_info This graph shows the Peak-to-Average Power Ratio before and after CFR
before_cfr.info PAPR before CFR
before_cfr.label PAPR before CFR
before_cfr.min 0
after_cfr.info PAPR after CFR
after_cfr.label PAPR after CFR
after_cfr.min 0"""
# One GAUGE graph for
# tist offset
config_all += """
multigraph tist_offset
graph_title TIST configured offset
graph_order offset
graph_args --base 1000
graph_vlabel Configured offset
graph_category dabmod
graph_info This graph shows the configured TIST offset
offset.info Configured offset
offset.label Configured offset
offset.min 0
offset.max 300"""
# One DDERIVE graph for
# tist timestamp timestamps
config_all += """
multigraph tist_timestamp
graph_title TIST timestamp
graph_order timestamp
graph_args --base 1000
graph_vlabel timestamp value
graph_category dabmod
graph_info This graph shows the timestamp value in seconds
timestamp.info timestamp
timestamp.label timestamp
timestamp.type DDERIVE
timestamp.min 0"""
# One DERIVE (min 0) multigraph for
# sdr underruns
# sdr latepackets
config_all += """
multigraph sdr_stats
graph_title SDR device statistics
graph_order underruns latepackets
graph_args --base 1000
graph_vlabel Number of underruns and late packets
graph_category dabmod
graph_info This graph shows the number of underruns and late packets
underruns.info Number of SoapySDR/UHD underruns
underruns.label Number of SoapySDR/UHD underruns
underruns.type DERIVE
underruns.min 0
latepackets.info Number of SoapySDR/UHD late packets
latepackets.label Number of SoapySDR/UHD late packets
latepackets.type DERIVE
latepackets.min 0"""
# One DERIVE (min 0) graph for
# sdr frames
config_all += """
multigraph sdr_frames
graph_title SDR number of frames transmitted
graph_order frames
graph_args --base 1000
graph_vlabel Number of frames transmitted
graph_category dabmod
graph_info This graph shows the number of frames transmitted
frames.info Number of SoapySDR/UHD frames
frames.label Number of SoapySDR/UHD frames
frames.type DERIVE
frames.min 0"""
# One GAUGE multigraph
# sdr gpsdo_num_sv
# and one for device sensors
# sdr temp
config_all += """
multigraph sdr_gpsdo_sv_holdover
graph_title Number of GNSS SVs used and holdover state
graph_order num_sv holdover
graph_vlabel Number of GNSS SVs and holdover state
graph_category dabmod
graph_info This graph shows the number of Satellite Vehicles the GNSS receiver uses (Field 7 of GNGGA NMEA sentence), and if it is in holdover
num_sv.info Num SVs
num_sv.label Num SVs
num_sv.min 0
num_sv.max 20
holdover.info Holdover
holdover.label Holdover
holdover.min 0
holdover.max 1
multigraph sdr_sensors
graph_title SDR Sensors
graph_order temp
graph_vlabel SDR Sensors
graph_category dabmod
graph_info This graph shows the device temperature in Celsius
temp.info Device temperature in Celsius
temp.label Celsius
temp.min 0
temp.max 100"""
ctx = zmq.Context()
class RCException(Exception):
pass
def do_transaction(message_parts, sock):
"""To a send + receive transaction, quit whole program on timeout"""
if isinstance(message_parts, str):
sys.stderr.write("do_transaction expects a list!\n");
sys.exit(1)
for i, part in enumerate(message_parts):
if i == len(message_parts) - 1:
f = 0
else:
f = zmq.SNDMORE
sock.send(part, flags=f)
poller = zmq.Poller()
poller.register(sock, zmq.POLLIN)
socks = dict(poller.poll(1000))
if socks:
if socks.get(sock) == zmq.POLLIN:
rxpackets = sock.recv_multipart()
return rxpackets
raise RCException("Could not receive data for command '{}'\n".format(
message_parts))
def connect():
"""Create a connection to the dabmod RC
returns: the socket"""
sock = zmq.Socket(ctx, zmq.REQ)
sock.set(zmq.LINGER, 5)
sock.connect("tcp://localhost:9400")
try:
ping_answer = do_transaction([b"ping"], sock)
if not ping_answer == [b"ok"]:
sys.stderr.write("Wrong answer to ping\n")
sys.exit(1)
except RCException as e:
print("connect failed because: {}".format(e))
sys.exit(1)
return sock
def get_rc_value(module, name, sock):
try:
parts = do_transaction([b"get", module.encode(), name.encode()], sock)
if len(parts) != 1:
sys.stderr.write("Received unexpected multipart message {}\n".format(
parts))
sys.exit(1)
return parts[0].decode()
except RCException as e:
print("get {} {} fail: {}".format(module, name, e))
return ""
def handle_re(graph_name, re, rc_value, group_number=1):
match = re.search(rc_value)
if match:
return "{}.value {}\n".format(graph_name, match.group(group_number))
else:
return "{}.value U\n".format(graph_name)
re_double_value = re.compile(r"(\d+\.\d+)", re.X)
re_int_value = re.compile(r"(\d+)", re.X)
if len(sys.argv) == 1:
sock = connect()
munin_values = ""
munin_values += "multigraph ofdm_clip_stats\n"
ofdm_clip_stats = get_rc_value("ofdm", "clip_stats", sock)
re_clip_samples = re.compile(r"(\d+\.\d+)%\ samples\ clipped", re.X)
munin_values += handle_re("clip_ratio", re_clip_samples, ofdm_clip_stats)
re_clip_errors = re.compile(r"(\d+\.\d+)%\ errors\ clipped", re.X)
munin_values += handle_re("errorclip_ratio",
re_clip_errors, ofdm_clip_stats)
munin_values += "multigraph ofdm_clip_stats_mer\n"
re_clip_mer = re.compile(r"MER\ after\ CFR:\ (\d+\.\d+)", re.X)
munin_values += handle_re("mer",
re_clip_mer, ofdm_clip_stats)
munin_values += "multigraph ofdm_papr\n"
ofdm_papr_stats = get_rc_value("ofdm", "papr", sock)
def muninise_papr(papr):
if "N/A" in papr:
return "U"
else:
return float(papr.strip())
# Format is as follows:
# "PAPR [dB]: " << std::fixed <<
# (papr_before == 0 ? string("N/A") : to_string(papr_before)) <<
# ", " <<
# (papr_after == 0 ? string("N/A") : to_string(papr_after));
try:
_, _, both_papr = ofdm_papr_stats.partition(":")
papr_before, papr_after = both_papr.split(",")
papr_before = muninise_papr(papr_before)
munin_values += "before_cfr.value {}\n".format(papr_before)
except:
munin_values += "before_cfr.value U\n"
try:
_, _, both_papr = ofdm_papr_stats.partition(":")
papr_before, papr_after = both_papr.split(",")
papr_after = muninise_papr(papr_after)
munin_values += "after_cfr.value {}\n".format(papr_after)
except:
munin_values += "after_cfr.value U\n"
munin_values += "multigraph tist_offset\n"
tist_offset = get_rc_value("tist", "offset", sock)
munin_values += handle_re("offset", re_double_value, tist_offset)
# Plotting FCT is not useful because it overflows in 6s, and the poll
# interval is usually 5min
tist_timestamp = get_rc_value("tist", "timestamp", sock)
re_tist_timestamp = re.compile(r"(\d+\.\d+)\ for\ frame\ FCT\ (\d+)", re.X)
munin_values += "multigraph tist_timestamp\n"
munin_values += handle_re("timestamp", re_tist_timestamp, tist_timestamp, 1)
munin_values += "multigraph sdr_stats\n"
sdr_underruns = get_rc_value("sdr", "underruns", sock)
munin_values += handle_re("underruns", re_int_value, sdr_underruns)
sdr_latepackets = get_rc_value("sdr", "latepackets", sock)
munin_values += handle_re("latepackets", re_int_value, sdr_latepackets)
munin_values += "multigraph sdr_gpsdo_sv_holdover\n"
try:
gps_num_sv = get_rc_value("sdr", "gpsdo_num_sv", sock)
munin_values += "num_sv.value {}\n".format(gps_num_sv)
except:
munin_values += "num_sv.value U\n"
try:
gps_holdover = get_rc_value("sdr", "gpsdo_holdover", sock)
munin_values += "holdover.value {}\n".format(gps_holdover)
except:
munin_values += "holdover.value U\n"
munin_values += "multigraph sdr_sensors\n"
try:
sdr_temp = get_rc_value("sdr", "temp", sock)
munin_values += "temp.value {}\n".format(sdr_temp)
except:
munin_values += "temp.value U\n"
munin_values += "multigraph sdr_frames\n"
sdr_frames = get_rc_value("sdr", "frames", sock)
munin_values += handle_re("frames", re_int_value, sdr_frames)
print(munin_values)
elif len(sys.argv) == 2 and sys.argv[1] == "config":
# No need to connect
print(config_all)
else:
sys.stderr.write("Invalid command line arguments")
sys.exit(1)
ODR-DabMod-2.6.0/doc/README-Fileinput 0000644 0001750 0001750 00000001670 14230010227 015650 0 ustar robin robin The file input support three file formats:
Framed format is used for file recording. It is the default format. The
padding can be removed from data. Format:
uint32_t nbFrames
for each frame
uint16_t frameSize
uint8_t data[frameSize]
Streamed format is used for streamed applications. As the total number of
frames is unknown before end of transmission, the corresponding field is
removed. The padding can be removed from data. Format:
for each frame
uint16_t frameSize
uint8_t data[frameSize]
Raw format is a bit-by-bit (but byte aligned on sync) recording of a G.703
data stream. The padding is always present. Format:
for each frame
uint8_t data[6144]
Please note that our raw format can also be referred to as ETI(NI, G.703) or ETI(NI).
All numbers are little-endian.
This description has been taken from the CRC mmbTools forum.
ODR-DabMod-2.6.0/TODO 0000644 0001750 0001750 00000005275 14230010227 012763 0 ustar robin robin This TODO file lists ideas and features for future developments. They are
more or less ordered according to their benefit, but that is subjective
to some degree.
Unless written, no activity has been started on the topics.
TODOs for ongoing SDR output refactoring
----------------------------------------
Currently, all the frontend tuning and timestamping settings are UHD-specific.
To make it possible to run with synchronous=1 using Soapy, refactoring the
output to share the parts that are common.
This would go towards SFN support with LimeSDR devices, under the condition
that the LimeSDR gets support for a HW timestamp that can be set from a PPS
signal. Discussion ongoing here
https://discourse.myriadrf.org/t/synchronize-two-limesdr/1714
DPD will be possible too.
Test sleep_through_frame implementation.
Clean up and separate GPS and refclk checks.
* *done* handle UHD GPSDO and time
* *done* handle SoapySDR time
* Add refclk stuff and timestamps to Soapy.
* Ensure muting is set properly at startup.
* Ensure synchronous is taken in account.
* Verify resync after underflow and muting
* Add GPSDO status to RC and munin.
*done* Add antenna selection to config.
Test RC entries.
*done* Portability: replace clock_gettime with std::chrono
*done* Make an abstraction for the DPD feedback server, use it for Soapy and UHD.
*optional* Move staticdelay into a new process block
Double-check all #includes
Move other non SDR outputs to the output folder.
Tests, both with B200 and LimeSDR:
- No timestamps
- with timestamps
- LO offset
- muting through RC
- proper muting in absence of timestamps
- GPS lock loss behaviour
- All RC commands
- Underrun recovery
- Proper teardown
- DPD server
Smaller things
--------------
Remove GuardIntervalInserter implementation for window==0, as it was shown both are equivalent.
Finalise EDI input
------------------
The EDI input, based on work started in http://git.mpb.li/git/odr-edilib/
is not complete:
* Add option to define max fill of input udp buffer.
* Flag and present errors in some way (packets missing, RS faults, sequence errors, etc)
* Detect and handle changes in mux composition
* Fix misbehaviours when packets are intentionnally interleaved
* Fix hangup where it tries to decode old PSEQs for too long
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.
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.
ODR-DabMod-2.6.0/.gitignore 0000644 0001750 0001750 00000000456 14230010227 014257 0 ustar robin robin
autom4te.cache
*.o
stamp-h1
.deps
.dirstamp
lib/kiss_fft129
**/Makefile
Makefile.in
aclocal.m4
build-aux
configure
config.log
config.h
config.h.in
config.status
odr-dabmod
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
python/gui/logs/access.log
python/gui/logs/error.log
python/gui/static/dpd/*png
ODR-DabMod-2.6.0/configure.ac 0000644 0001750 0001750 00000022164 14230010227 014555 0 ustar robin robin # Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Her Majesty the
# Queen in Right of Canada (Communications Research Center Canada)
# Copyright (C) 2022 Matthias P. Braendli, http://opendigitalradio.org
# This file is part of ODR-DabMod.
#
# ODR-DabMod is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# ODR-DabMod is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ODR-DabMod. If not, see .
AC_PREREQ([2.69])
AC_INIT([ODR-DabMod],[2.6.0],[matthias.braendli@mpb.li])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
AC_CONFIG_SRCDIR([src/DabMod.cpp])
AC_CONFIG_HEADERS([config.h])
AM_SILENT_RULES([yes])
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_INSTALL
AX_CXX_COMPILE_STDCXX(11,noext,mandatory)
EXTRA=""
AC_ARG_ENABLE([prof],
[AS_HELP_STRING([--enable-prof], [Enable profiling])],
[], [enable_prof=no])
AC_ARG_ENABLE([fast-math],
[AS_HELP_STRING([--enable-fast-math], [Set -ffast-math])],
[], [enable_fast_math=no])
AC_ARG_ENABLE([trace],
[AS_HELP_STRING([--enable-trace], [Enable trace output])],
[], [enable_trace=no])
AC_ARG_ENABLE([zeromq],
[AS_HELP_STRING([--disable-zeromq], [Disable ZeroMQ input, output and remote control])],
[], [enable_zeromq=yes])
AC_ARG_ENABLE([native],
[AS_HELP_STRING([--disable-native], [Do not compile with -march=native])],
[], [enable_native=yes])
AC_ARG_ENABLE([easydabv3],
[AS_HELP_STRING([--enable-easydabv3], [Build for EasyDABv3 board])],
[], [enable_easydabv3=no])
AC_ARG_ENABLE([limesdr],
[AS_HELP_STRING([--enable-limesdr], [Build for LimeSDR board])],
[], [enable_limesdr=no])
AC_ARG_ENABLE([bladerf],
[AS_HELP_STRING([--enable-bladerf], [Build for BladeRF boards])],
[], [enable_bladerf=no])
# UHD support control
AC_ARG_ENABLE([output_uhd],
[AS_HELP_STRING([--disable-output-uhd], [Disable UHD output])],
[], [enable_output_uhd=yes])
AC_LANG_PUSH([C++])
AX_CHECK_COMPILE_FLAG([-Wduplicated-cond], [CXXFLAGS="$CXXFLAGS -Wduplicated-cond"], [], ["-Werror"])
AX_CHECK_COMPILE_FLAG([-Wduplicated-branches], [CXXFLAGS="$CXXFLAGS -Wduplicated-branches"], [], ["-Werror"])
AX_CHECK_COMPILE_FLAG([-Wlogical-op], [CXXFLAGS="$CXXFLAGS -Wlogical-op"], [], ["-Werror"])
AX_CHECK_COMPILE_FLAG([-Wrestrict], [CXXFLAGS="$CXXFLAGS -Wrestrict"], [], ["-Werror"])
AX_CHECK_COMPILE_FLAG([-Wno-pragmas], [CXXFLAGS="$CXXFLAGS -Wno-pragmas"], [], ["-Werror"])
AX_CHECK_COMPILE_FLAG([-Wdouble-promotion], [CXXFLAGS="$CXXFLAGS -Wdouble-promotion"], [], ["-Werror"])
AX_CHECK_COMPILE_FLAG(["-Wformat=2"], [CXXFLAGS="$CXXFLAGS -Wformat=2"], [], ["-Werror"])
AC_LANG_POP([C++])
AS_IF([test "x$enable_easydabv3" = "xno"],
[PKG_CHECK_MODULES([FFTW], [fftw3f], [], [AC_MSG_ERROR([FFTW is required])])])
echo "Checking zeromq"
AS_IF([test "x$enable_zeromq" = "xyes"],
[AC_CHECK_LIB([zmq], [zmq_init], [ZMQ_LIBS="-lzmq"],
[AC_MSG_ERROR([ZeroMQ libzmq is required])])])
AS_IF([test "x$enable_zeromq" = "xyes"],
[AC_DEFINE(HAVE_ZEROMQ, [1], [Define if ZeroMQ is enabled])])
AS_IF([test "x$enable_prof" != "xno"],
[EXTRA="$EXTRA -pg"])
AS_IF([test "x$enable_fast_math" != "xno"],
[EXTRA="$EXTRA -ffast-math"])
AS_IF([test "x$enable_trace" != "xno"],
AC_DEFINE(TRACE, [1], [Enable trace output for all blocks]))
# Define conditionals for Makefile.am
AM_CONDITIONAL([IS_GIT_REPO], [test -d '.git'])
AM_CONDITIONAL([COMPILE_FOR_EASYDABV3], [test "x$enable_easydabv3" = "xyes"])
# Defines for config.h
AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
AS_IF([test "x$enable_easydabv3" = "xno"],
[PKG_CHECK_MODULES([SOAPYSDR], [SoapySDR], enable_soapysdr=yes, enable_soapysdr=no)])
AS_IF([test "x$enable_limesdr" = "xyes"],
[AC_CHECK_LIB([LimeSuite], [LMS_Init], [LIMESDR_LIBS="-lLimeSuite"],
[AC_MSG_ERROR([LimeSDR LimeSuite is required])])])
AS_IF([test "x$enable_bladerf" = "xyes"],
[AC_CHECK_LIB([bladeRF], [bladerf_open], [BLADERF_LIBS="-lbladeRF"],
[AC_MSG_ERROR([BladeRF library is required])])])
AC_SUBST([CFLAGS], ["$CFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"])
AC_SUBST([CXXFLAGS], ["$CXXFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"])
AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS $LIMESDR_LIBS $BLADERF_LIBS"])
AS_IF([test "x$enable_easydabv3" = "xyes" && test "x$enable_output_uhd" == "xyes"],
AC_MSG_ERROR([Cannot enable both EasyDABv3 and UHD output]))
# Checks for UHD.
AS_IF([test "x$enable_output_uhd" = "xyes"],
[ PKG_CHECK_MODULES([UHD], [uhd], [], [AC_MSG_ERROR([UHD is required])])
])
AS_IF([test "x$enable_output_uhd" = "xyes"],
[AC_DEFINE(HAVE_OUTPUT_UHD, [1], [Define if UHD output is enabled])])
# I don't know why this is needed, I think the uhd pkg-config file should also
# tell us to link against boost thread, but it doesn't.
AS_IF([test "x$enable_output_uhd" = "xyes"],
[ AX_BOOST_BASE([1.55.0], [], AC_MSG_ERROR([BOOST 1.55 or later is required]))
AX_BOOST_THREAD ])
AS_IF([test "x$enable_soapysdr" = "xyes"],
[AC_DEFINE(HAVE_SOAPYSDR, [1], [Define if SoapySDR output is enabled])])
AS_IF([test "x$enable_limesdr" = "xyes"],
[AC_DEFINE(HAVE_LIMESDR, [1], [Define if LimeSDR output is enabled]) ])
AS_IF([test "x$enable_bladerf" = "xyes"],
[AC_DEFINE(HAVE_BLADERF, [1], [Define if BladeRF output is enabled]) ])
AS_IF([test "x$enable_easydabv3" = "xyes"],
AC_DEFINE(BUILD_FOR_EASYDABV3, [1], [Define if we are building for EasyDABv3]))
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h limits.h memory.h netinet/in.h stdint.h stdlib.h string.h sys/time.h sys/timeb.h unistd.h])
AC_MSG_CHECKING(for M_PIl existence)
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include ]], [[double pi = M_PIl;]])],
[AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_DEFINE([M_PIl], [M_PI], [Replacing define])])
AC_LANG_POP([C++])
# Linux has prctl to set thread names
AC_MSG_CHECKING(for prctl and PR_SET_NAME)
AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[
#include
void set_thread_name() {
prctl(PR_SET_NAME,"test",0,0,0);
}
]])],
[ AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_PRCTL, 1, [Define this symbol if you have prctl and PR_SET_NAME]) ],
[ AC_MSG_RESULT(no) ])
# Linux defines MSG_NOSIGNAL, some other systems have SO_NOSIGPIPE instead
AC_MSG_CHECKING(for MSG_NOSIGNAL)
AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[
#include
int f = MSG_NOSIGNAL;
]])],
[ AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_MSG_NOSIGNAL, 1, [Define this symbol if you have MSG_NOSIGNAL]) ],
[ AC_MSG_RESULT(no) ])
AC_MSG_CHECKING(for SO_NOSIGPIPE)
AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[
#include
int f = SO_NOSIGPIPE;
]])],
[ AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_SO_NOSIGPIPE, 1, [Define this symbol if you have SO_NOSIGPIPE]) ],
[ AC_MSG_RESULT(no) ])
# Check for march
AS_IF([test "x$enable_native" = "xyes"],
[AC_MSG_CHECKING(if we can add -march=native to CFLAGS)
save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS -march=native"
AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[
void testfunc(void) {}
]])],
[supports_march_native=yes],
[supports_march_native=no]
)
AC_MSG_RESULT($supports_march_native)
if test x"$supports_march_native" = xno; then
CXXFLAGS="$save_CXXFLAGS"
fi
])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
echo
echo "***********************************************"
echo
enabled=""
disabled=""
for feat in prof trace output_uhd zeromq soapysdr easydabv3 limesdr bladerf
do
eval var=\$enable_$feat
AS_IF([test "x$var" = "xyes"],
[enabled="$enabled $feat"],
[disabled="$disabled $feat"])
done
echo " Features"
echo " Enabled: $enabled"
echo " Disabled: $disabled"
echo
echo
enabled=""
disabled=""
for feat in supports_march_native enable_fast_math
do
eval var=\$$feat
AS_IF([test "x$var" != "xno"],
[enabled="$enabled $feat"],
[disabled="$disabled $feat"])
done
echo " Options"
echo " Active: $enabled"
echo " Disabled: $disabled"
echo
echo "***********************************************"
echo
ODR-DabMod-2.6.0/lib/ 0000755 0001750 0001750 00000000000 14230010227 013030 5 ustar robin robin ODR-DabMod-2.6.0/lib/zmq.hpp 0000644 0001750 0001750 00000161715 14230010227 014363 0 ustar robin robin /*
Copyright (c) 2016-2017 ZeroMQ community
Copyright (c) 2009-2011 250bpm s.r.o.
Copyright (c) 2011 Botond Ballo
Copyright (c) 2007-2009 iMatix Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
#ifndef __ZMQ_HPP_INCLUDED__
#define __ZMQ_HPP_INCLUDED__
// macros defined if has a specific standard or greater
#if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900)
#define ZMQ_CPP11
#endif
#if (defined(__cplusplus) && __cplusplus >= 201402L) || \
(defined(_HAS_CXX14) && _HAS_CXX14 == 1) || \
(defined(_HAS_CXX17) && _HAS_CXX17 == 1) // _HAS_CXX14 might not be defined when using C++17 on MSVC
#define ZMQ_CPP14
#endif
#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1)
#define ZMQ_CPP17
#endif
#if defined(ZMQ_CPP14)
#define ZMQ_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(_MSC_VER)
#define ZMQ_DEPRECATED(msg) __declspec(deprecated(msg))
#elif defined(__GNUC__)
#define ZMQ_DEPRECATED(msg) __attribute__((deprecated(msg)))
#endif
#if defined(ZMQ_CPP17)
#define ZMQ_NODISCARD [[nodiscard]]
#else
#define ZMQ_NODISCARD
#endif
#if defined(ZMQ_CPP11)
#define ZMQ_NOTHROW noexcept
#define ZMQ_EXPLICIT explicit
#define ZMQ_OVERRIDE override
#define ZMQ_NULLPTR nullptr
#define ZMQ_CONSTEXPR_FN constexpr
#define ZMQ_CONSTEXPR_VAR constexpr
#else
#define ZMQ_NOTHROW throw()
#define ZMQ_EXPLICIT
#define ZMQ_OVERRIDE
#define ZMQ_NULLPTR 0
#define ZMQ_CONSTEXPR_FN
#define ZMQ_CONSTEXPR_VAR const
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef ZMQ_CPP11
#include
#include
#include
#include
#endif
#ifdef ZMQ_CPP17
#ifdef __has_include
#if __has_include()
#include
#define ZMQ_HAS_OPTIONAL 1
#endif
#if __has_include()
#include
#define ZMQ_HAS_STRING_VIEW 1
#endif
#endif
#endif
/* Version macros for compile-time API version detection */
#define CPPZMQ_VERSION_MAJOR 4
#define CPPZMQ_VERSION_MINOR 6
#define CPPZMQ_VERSION_PATCH 0
#define CPPZMQ_VERSION \
ZMQ_MAKE_VERSION(CPPZMQ_VERSION_MAJOR, CPPZMQ_VERSION_MINOR, \
CPPZMQ_VERSION_PATCH)
// Detect whether the compiler supports C++11 rvalue references.
#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) \
&& defined(__GXX_EXPERIMENTAL_CXX0X__))
#define ZMQ_HAS_RVALUE_REFS
#define ZMQ_DELETED_FUNCTION = delete
#elif defined(__clang__)
#if __has_feature(cxx_rvalue_references)
#define ZMQ_HAS_RVALUE_REFS
#endif
#if __has_feature(cxx_deleted_functions)
#define ZMQ_DELETED_FUNCTION = delete
#else
#define ZMQ_DELETED_FUNCTION
#endif
#elif defined(_MSC_VER) && (_MSC_VER >= 1900)
#define ZMQ_HAS_RVALUE_REFS
#define ZMQ_DELETED_FUNCTION = delete
#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
#define ZMQ_HAS_RVALUE_REFS
#define ZMQ_DELETED_FUNCTION
#else
#define ZMQ_DELETED_FUNCTION
#endif
#if defined(ZMQ_CPP11) && !defined(__llvm__) && !defined(__INTEL_COMPILER) \
&& defined(__GNUC__) && __GNUC__ < 5
#define ZMQ_CPP11_PARTIAL
#elif defined(__GLIBCXX__) && __GLIBCXX__ < 20160805
//the date here is the last date of gcc 4.9.4, which
// effectively means libstdc++ from gcc 5.5 and higher won't trigger this branch
#define ZMQ_CPP11_PARTIAL
#endif
#ifdef ZMQ_CPP11
#ifdef ZMQ_CPP11_PARTIAL
#define ZMQ_IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T)
#else
#include
#define ZMQ_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable::value
#endif
#endif
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0)
#define ZMQ_NEW_MONITOR_EVENT_LAYOUT
#endif
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0)
#define ZMQ_HAS_PROXY_STEERABLE
/* Socket event data */
typedef struct
{
uint16_t event; // id of the event as bitfield
int32_t value; // value is either error code, fd or reconnect interval
} zmq_event_t;
#endif
// Avoid using deprecated message receive function when possible
#if ZMQ_VERSION < ZMQ_MAKE_VERSION(3, 2, 0)
#define zmq_msg_recv(msg, socket, flags) zmq_recvmsg(socket, msg, flags)
#endif
// In order to prevent unused variable warnings when building in non-debug
// mode use this macro to make assertions.
#ifndef NDEBUG
#define ZMQ_ASSERT(expression) assert(expression)
#else
#define ZMQ_ASSERT(expression) (void) (expression)
#endif
namespace zmq
{
#ifdef ZMQ_CPP11
namespace detail
{
namespace ranges
{
using std::begin;
using std::end;
template
auto begin(T&& r) -> decltype(begin(std::forward(r)))
{
return begin(std::forward(r));
}
template
auto end(T&& r) -> decltype(end(std::forward(r)))
{
return end(std::forward(r));
}
} // namespace ranges
template using void_t = void;
template
using iter_value_t = typename std::iterator_traits::value_type;
template
using range_iter_t = decltype(
ranges::begin(std::declval::type &>()));
template
using range_value_t = iter_value_t>;
template struct is_range : std::false_type
{
};
template
struct is_range<
T,
void_t::type &>())
== ranges::end(std::declval::type &>()))>>
: std::true_type
{
};
} // namespace detail
#endif
typedef zmq_free_fn free_fn;
typedef zmq_pollitem_t pollitem_t;
class error_t : public std::exception
{
public:
error_t() : errnum(zmq_errno()) {}
virtual const char *what() const ZMQ_NOTHROW ZMQ_OVERRIDE { return zmq_strerror(errnum); }
int num() const { return errnum; }
private:
int errnum;
};
inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_ = -1)
{
int rc = zmq_poll(items_, static_cast(nitems_), timeout_);
if (rc < 0)
throw error_t();
return rc;
}
ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
inline int poll(zmq_pollitem_t const *items_, size_t nitems_, long timeout_ = -1)
{
return poll(const_cast(items_), nitems_, timeout_);
}
#ifdef ZMQ_CPP11
ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
inline int
poll(zmq_pollitem_t const *items, size_t nitems, std::chrono::milliseconds timeout)
{
return poll(const_cast(items), nitems, static_cast(timeout.count()));
}
ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
inline int poll(std::vector const &items,
std::chrono::milliseconds timeout)
{
return poll(const_cast(items.data()), items.size(), static_cast(timeout.count()));
}
ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
inline int poll(std::vector const &items, long timeout_ = -1)
{
return poll(const_cast(items.data()), items.size(), timeout_);
}
inline int
poll(zmq_pollitem_t *items, size_t nitems, std::chrono::milliseconds timeout)
{
return poll(items, nitems, static_cast(timeout.count()));
}
inline int poll(std::vector &items,
std::chrono::milliseconds timeout)
{
return poll(items.data(), items.size(), static_cast(timeout.count()));
}
inline int poll(std::vector &items, long timeout_ = -1)
{
return poll(items.data(), items.size(), timeout_);
}
#endif
inline void version(int *major_, int *minor_, int *patch_)
{
zmq_version(major_, minor_, patch_);
}
#ifdef ZMQ_CPP11
inline std::tuple version()
{
std::tuple v;
zmq_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v));
return v;
}
#endif
class message_t
{
public:
message_t() ZMQ_NOTHROW
{
int rc = zmq_msg_init(&msg);
ZMQ_ASSERT(rc == 0);
}
explicit message_t(size_t size_)
{
int rc = zmq_msg_init_size(&msg, size_);
if (rc != 0)
throw error_t();
}
template message_t(ForwardIter first, ForwardIter last)
{
typedef typename std::iterator_traits::value_type value_t;
assert(std::distance(first, last) >= 0);
size_t const size_ =
static_cast(std::distance(first, last)) * sizeof(value_t);
int const rc = zmq_msg_init_size(&msg, size_);
if (rc != 0)
throw error_t();
std::copy(first, last, data());
}
message_t(const void *data_, size_t size_)
{
int rc = zmq_msg_init_size(&msg, size_);
if (rc != 0)
throw error_t();
memcpy(data(), data_, size_);
}
message_t(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR)
{
int rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_);
if (rc != 0)
throw error_t();
}
#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL)
template::value
&& ZMQ_IS_TRIVIALLY_COPYABLE(detail::range_value_t)
&& !std::is_same::value>::type>
explicit message_t(const Range &rng) :
message_t(detail::ranges::begin(rng), detail::ranges::end(rng))
{
}
#endif
#ifdef ZMQ_HAS_RVALUE_REFS
message_t(message_t &&rhs) ZMQ_NOTHROW : msg(rhs.msg)
{
int rc = zmq_msg_init(&rhs.msg);
ZMQ_ASSERT(rc == 0);
}
message_t &operator=(message_t &&rhs) ZMQ_NOTHROW
{
std::swap(msg, rhs.msg);
return *this;
}
#endif
~message_t() ZMQ_NOTHROW
{
int rc = zmq_msg_close(&msg);
ZMQ_ASSERT(rc == 0);
}
void rebuild()
{
int rc = zmq_msg_close(&msg);
if (rc != 0)
throw error_t();
rc = zmq_msg_init(&msg);
ZMQ_ASSERT(rc == 0);
}
void rebuild(size_t size_)
{
int rc = zmq_msg_close(&msg);
if (rc != 0)
throw error_t();
rc = zmq_msg_init_size(&msg, size_);
if (rc != 0)
throw error_t();
}
void rebuild(const void *data_, size_t size_)
{
int rc = zmq_msg_close(&msg);
if (rc != 0)
throw error_t();
rc = zmq_msg_init_size(&msg, size_);
if (rc != 0)
throw error_t();
memcpy(data(), data_, size_);
}
void rebuild(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR)
{
int rc = zmq_msg_close(&msg);
if (rc != 0)
throw error_t();
rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_);
if (rc != 0)
throw error_t();
}
ZMQ_DEPRECATED("from 4.3.1, use move taking non-const reference instead")
void move(message_t const *msg_)
{
int rc = zmq_msg_move(&msg, const_cast(msg_->handle()));
if (rc != 0)
throw error_t();
}
void move(message_t &msg_)
{
int rc = zmq_msg_move(&msg, msg_.handle());
if (rc != 0)
throw error_t();
}
ZMQ_DEPRECATED("from 4.3.1, use copy taking non-const reference instead")
void copy(message_t const *msg_)
{
int rc = zmq_msg_copy(&msg, const_cast(msg_->handle()));
if (rc != 0)
throw error_t();
}
void copy(message_t &msg_)
{
int rc = zmq_msg_copy(&msg, msg_.handle());
if (rc != 0)
throw error_t();
}
bool more() const ZMQ_NOTHROW
{
int rc = zmq_msg_more(const_cast(&msg));
return rc != 0;
}
void *data() ZMQ_NOTHROW { return zmq_msg_data(&msg); }
const void *data() const ZMQ_NOTHROW
{
return zmq_msg_data(const_cast(&msg));
}
size_t size() const ZMQ_NOTHROW
{
return zmq_msg_size(const_cast(&msg));
}
ZMQ_NODISCARD bool empty() const ZMQ_NOTHROW
{
return size() == 0u;
}
template T *data() ZMQ_NOTHROW { return static_cast(data()); }
template T const *data() const ZMQ_NOTHROW
{
return static_cast(data());
}
ZMQ_DEPRECATED("from 4.3.0, use operator== instead")
bool equal(const message_t *other) const ZMQ_NOTHROW
{
return *this == *other;
}
bool operator==(const message_t &other) const ZMQ_NOTHROW
{
const size_t my_size = size();
return my_size == other.size() && 0 == memcmp(data(), other.data(), my_size);
}
bool operator!=(const message_t &other) const ZMQ_NOTHROW
{
return !(*this == other);
}
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 2, 0)
int get(int property_)
{
int value = zmq_msg_get(&msg, property_);
if (value == -1)
throw error_t();
return value;
}
#endif
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0)
const char *gets(const char *property_)
{
const char *value = zmq_msg_gets(&msg, property_);
if (value == ZMQ_NULLPTR)
throw error_t();
return value;
}
#endif
#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
uint32_t routing_id() const
{
return zmq_msg_routing_id(const_cast(&msg));
}
void set_routing_id(uint32_t routing_id)
{
int rc = zmq_msg_set_routing_id(&msg, routing_id);
if (rc != 0)
throw error_t();
}
const char* group() const
{
return zmq_msg_group(const_cast(&msg));
}
void set_group(const char* group)
{
int rc = zmq_msg_set_group(&msg, group);
if (rc != 0)
throw error_t();
}
#endif
// interpret message content as a string
std::string to_string() const
{
return std::string(static_cast(data()), size());
}
#ifdef ZMQ_CPP17
// interpret message content as a string
std::string_view to_string_view() const noexcept
{
return std::string_view(static_cast(data()), size());
}
#endif
/** Dump content to string for debugging.
* Ascii chars are readable, the rest is printed as hex.
* Probably ridiculously slow.
* Use to_string() or to_string_view() for
* interpreting the message as a string.
*/
std::string str() const
{
// Partly mutuated from the same method in zmq::multipart_t
std::stringstream os;
const unsigned char *msg_data = this->data();
unsigned char byte;
size_t size = this->size();
int is_ascii[2] = {0, 0};
os << "zmq::message_t [size " << std::dec << std::setw(3)
<< std::setfill('0') << size << "] (";
// Totally arbitrary
if (size >= 1000) {
os << "... too big to print)";
} else {
while (size--) {
byte = *msg_data++;
is_ascii[1] = (byte >= 32 && byte < 127);
if (is_ascii[1] != is_ascii[0])
os << " "; // Separate text/non text
if (is_ascii[1]) {
os << byte;
} else {
os << std::hex << std::uppercase << std::setw(2)
<< std::setfill('0') << static_cast(byte);
}
is_ascii[0] = is_ascii[1];
}
os << ")";
}
return os.str();
}
void swap(message_t &other) ZMQ_NOTHROW
{
// this assumes zmq::msg_t from libzmq is trivially relocatable
std::swap(msg, other.msg);
}
ZMQ_NODISCARD zmq_msg_t *handle() ZMQ_NOTHROW { return &msg; }
ZMQ_NODISCARD const zmq_msg_t *handle() const ZMQ_NOTHROW { return &msg; }
private:
// The underlying message
zmq_msg_t msg;
// Disable implicit message copying, so that users won't use shared
// messages (less efficient) without being aware of the fact.
message_t(const message_t &) ZMQ_DELETED_FUNCTION;
void operator=(const message_t &) ZMQ_DELETED_FUNCTION;
};
inline void swap(message_t &a, message_t &b) ZMQ_NOTHROW
{
a.swap(b);
}
class context_t
{
public:
context_t()
{
ptr = zmq_ctx_new();
if (ptr == ZMQ_NULLPTR)
throw error_t();
}
explicit context_t(int io_threads_,
int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT)
{
ptr = zmq_ctx_new();
if (ptr == ZMQ_NULLPTR)
throw error_t();
int rc = zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_);
ZMQ_ASSERT(rc == 0);
rc = zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_);
ZMQ_ASSERT(rc == 0);
}
#ifdef ZMQ_HAS_RVALUE_REFS
context_t(context_t &&rhs) ZMQ_NOTHROW : ptr(rhs.ptr) { rhs.ptr = ZMQ_NULLPTR; }
context_t &operator=(context_t &&rhs) ZMQ_NOTHROW
{
close();
std::swap(ptr, rhs.ptr);
return *this;
}
#endif
int setctxopt(int option_, int optval_)
{
int rc = zmq_ctx_set(ptr, option_, optval_);
ZMQ_ASSERT(rc == 0);
return rc;
}
int getctxopt(int option_) { return zmq_ctx_get(ptr, option_); }
~context_t() ZMQ_NOTHROW { close(); }
void close() ZMQ_NOTHROW
{
if (ptr == ZMQ_NULLPTR)
return;
int rc;
do {
rc = zmq_ctx_destroy(ptr);
} while (rc == -1 && errno == EINTR);
ZMQ_ASSERT(rc == 0);
ptr = ZMQ_NULLPTR;
}
// Be careful with this, it's probably only useful for
// using the C api together with an existing C++ api.
// Normally you should never need to use this.
ZMQ_EXPLICIT operator void *() ZMQ_NOTHROW { return ptr; }
ZMQ_EXPLICIT operator void const *() const ZMQ_NOTHROW { return ptr; }
operator bool() const ZMQ_NOTHROW { return ptr != ZMQ_NULLPTR; }
void swap(context_t &other) ZMQ_NOTHROW
{
std::swap(ptr, other.ptr);
}
private:
void *ptr;
context_t(const context_t &) ZMQ_DELETED_FUNCTION;
void operator=(const context_t &) ZMQ_DELETED_FUNCTION;
};
inline void swap(context_t &a, context_t &b) ZMQ_NOTHROW {
a.swap(b);
}
#ifdef ZMQ_CPP11
struct recv_buffer_size
{
size_t size; // number of bytes written to buffer
size_t untruncated_size; // untruncated message size in bytes
ZMQ_NODISCARD bool truncated() const noexcept
{
return size != untruncated_size;
}
};
#if defined(ZMQ_HAS_OPTIONAL) && (ZMQ_HAS_OPTIONAL > 0)
using send_result_t = std::optional;
using recv_result_t = std::optional;
using recv_buffer_result_t = std::optional;
#else
namespace detail
{
// A C++11 type emulating the most basic
// operations of std::optional for trivial types
template class trivial_optional
{
public:
static_assert(std::is_trivial::value, "T must be trivial");
using value_type = T;
trivial_optional() = default;
trivial_optional(T value) noexcept : _value(value), _has_value(true) {}
const T *operator->() const noexcept
{
assert(_has_value);
return &_value;
}
T *operator->() noexcept
{
assert(_has_value);
return &_value;
}
const T &operator*() const noexcept
{
assert(_has_value);
return _value;
}
T &operator*() noexcept
{
assert(_has_value);
return _value;
}
T &value()
{
if (!_has_value)
throw std::exception();
return _value;
}
const T &value() const
{
if (!_has_value)
throw std::exception();
return _value;
}
explicit operator bool() const noexcept { return _has_value; }
bool has_value() const noexcept { return _has_value; }
private:
T _value{};
bool _has_value{false};
};
} // namespace detail
using send_result_t = detail::trivial_optional;
using recv_result_t = detail::trivial_optional;
using recv_buffer_result_t = detail::trivial_optional;
#endif
namespace detail
{
template
constexpr T enum_bit_or(T a, T b) noexcept
{
static_assert(std::is_enum::value, "must be enum");
using U = typename std::underlying_type::type;
return static_cast(static_cast(a) | static_cast(b));
}
template
constexpr T enum_bit_and(T a, T b) noexcept
{
static_assert(std::is_enum::value, "must be enum");
using U = typename std::underlying_type::type;
return static_cast(static_cast(a) & static_cast(b));
}
template
constexpr T enum_bit_xor(T a, T b) noexcept
{
static_assert(std::is_enum::value, "must be enum");
using U = typename std::underlying_type::type;
return static_cast(static_cast(a) ^ static_cast(b));
}
template
constexpr T enum_bit_not(T a) noexcept
{
static_assert(std::is_enum::value, "must be enum");
using U = typename std::underlying_type::type;
return static_cast(~static_cast(a));
}
} // namespace detail
// partially satisfies named requirement BitmaskType
enum class send_flags : int
{
none = 0,
dontwait = ZMQ_DONTWAIT,
sndmore = ZMQ_SNDMORE
};
constexpr send_flags operator|(send_flags a, send_flags b) noexcept
{
return detail::enum_bit_or(a, b);
}
constexpr send_flags operator&(send_flags a, send_flags b) noexcept
{
return detail::enum_bit_and(a, b);
}
constexpr send_flags operator^(send_flags a, send_flags b) noexcept
{
return detail::enum_bit_xor(a, b);
}
constexpr send_flags operator~(send_flags a) noexcept
{
return detail::enum_bit_not(a);
}
// partially satisfies named requirement BitmaskType
enum class recv_flags : int
{
none = 0,
dontwait = ZMQ_DONTWAIT
};
constexpr recv_flags operator|(recv_flags a, recv_flags b) noexcept
{
return detail::enum_bit_or(a, b);
}
constexpr recv_flags operator&(recv_flags a, recv_flags b) noexcept
{
return detail::enum_bit_and(a, b);
}
constexpr recv_flags operator^(recv_flags a, recv_flags b) noexcept
{
return detail::enum_bit_xor(a, b);
}
constexpr recv_flags operator~(recv_flags a) noexcept
{
return detail::enum_bit_not(a);
}
// mutable_buffer, const_buffer and buffer are based on
// the Networking TS specification, draft:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4771.pdf
class mutable_buffer
{
public:
constexpr mutable_buffer() noexcept : _data(nullptr), _size(0) {}
constexpr mutable_buffer(void *p, size_t n) noexcept : _data(p), _size(n)
{
#ifdef ZMQ_CPP14
assert(p != nullptr || n == 0);
#endif
}
constexpr void *data() const noexcept { return _data; }
constexpr size_t size() const noexcept { return _size; }
mutable_buffer &operator+=(size_t n) noexcept
{
// (std::min) is a workaround for when a min macro is defined
const auto shift = (std::min)(n, _size);
_data = static_cast(_data) + shift;
_size -= shift;
return *this;
}
private:
void *_data;
size_t _size;
};
inline mutable_buffer operator+(const mutable_buffer &mb, size_t n) noexcept
{
return mutable_buffer(static_cast(mb.data()) + (std::min)(n, mb.size()),
mb.size() - (std::min)(n, mb.size()));
}
inline mutable_buffer operator+(size_t n, const mutable_buffer &mb) noexcept
{
return mb + n;
}
class const_buffer
{
public:
constexpr const_buffer() noexcept : _data(nullptr), _size(0) {}
constexpr const_buffer(const void *p, size_t n) noexcept : _data(p), _size(n)
{
#ifdef ZMQ_CPP14
assert(p != nullptr || n == 0);
#endif
}
constexpr const_buffer(const mutable_buffer &mb) noexcept :
_data(mb.data()),
_size(mb.size())
{
}
constexpr const void *data() const noexcept { return _data; }
constexpr size_t size() const noexcept { return _size; }
const_buffer &operator+=(size_t n) noexcept
{
const auto shift = (std::min)(n, _size);
_data = static_cast(_data) + shift;
_size -= shift;
return *this;
}
private:
const void *_data;
size_t _size;
};
inline const_buffer operator+(const const_buffer &cb, size_t n) noexcept
{
return const_buffer(static_cast(cb.data())
+ (std::min)(n, cb.size()),
cb.size() - (std::min)(n, cb.size()));
}
inline const_buffer operator+(size_t n, const const_buffer &cb) noexcept
{
return cb + n;
}
// buffer creation
constexpr mutable_buffer buffer(void* p, size_t n) noexcept
{
return mutable_buffer(p, n);
}
constexpr const_buffer buffer(const void* p, size_t n) noexcept
{
return const_buffer(p, n);
}
constexpr mutable_buffer buffer(const mutable_buffer& mb) noexcept
{
return mb;
}
inline mutable_buffer buffer(const mutable_buffer& mb, size_t n) noexcept
{
return mutable_buffer(mb.data(), (std::min)(mb.size(), n));
}
constexpr const_buffer buffer(const const_buffer& cb) noexcept
{
return cb;
}
inline const_buffer buffer(const const_buffer& cb, size_t n) noexcept
{
return const_buffer(cb.data(), (std::min)(cb.size(), n));
}
namespace detail
{
template
struct is_buffer
{
static constexpr bool value =
std::is_same::value ||
std::is_same::value;
};
template struct is_pod_like
{
// NOTE: The networking draft N4771 section 16.11 requires
// T in the buffer functions below to be
// trivially copyable OR standard layout.
// Here we decide to be conservative and require both.
static constexpr bool value =
ZMQ_IS_TRIVIALLY_COPYABLE(T) && std::is_standard_layout::value;
};
template constexpr auto seq_size(const C &c) noexcept -> decltype(c.size())
{
return c.size();
}
template
constexpr size_t seq_size(const T (&/*array*/)[N]) noexcept
{
return N;
}
template
auto buffer_contiguous_sequence(Seq &&seq) noexcept
-> decltype(buffer(std::addressof(*std::begin(seq)), size_t{}))
{
using T = typename std::remove_cv<
typename std::remove_reference::type>::type;
static_assert(detail::is_pod_like::value, "T must be POD");
const auto size = seq_size(seq);
return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr,
size * sizeof(T));
}
template
auto buffer_contiguous_sequence(Seq &&seq, size_t n_bytes) noexcept
-> decltype(buffer_contiguous_sequence(seq))
{
using T = typename std::remove_cv<
typename std::remove_reference::type>::type;
static_assert(detail::is_pod_like::value, "T must be POD");
const auto size = seq_size(seq);
return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr,
(std::min)(size * sizeof(T), n_bytes));
}
} // namespace detail
// C array
template mutable_buffer buffer(T (&data)[N]) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
mutable_buffer buffer(T (&data)[N], size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
template const_buffer buffer(const T (&data)[N]) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
const_buffer buffer(const T (&data)[N], size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
// std::array
template mutable_buffer buffer(std::array &data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
mutable_buffer buffer(std::array &data, size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
template
const_buffer buffer(std::array &data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
const_buffer buffer(std::array &data, size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
template
const_buffer buffer(const std::array &data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
const_buffer buffer(const std::array &data, size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
// std::vector
template
mutable_buffer buffer(std::vector &data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
mutable_buffer buffer(std::vector &data, size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
template
const_buffer buffer(const std::vector &data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
const_buffer buffer(const std::vector &data, size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
// std::basic_string
template
mutable_buffer buffer(std::basic_string &data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
mutable_buffer buffer(std::basic_string &data,
size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
template
const_buffer buffer(const std::basic_string &data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
const_buffer buffer(const std::basic_string &data,
size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
#if defined(ZMQ_HAS_STRING_VIEW) && (ZMQ_HAS_STRING_VIEW > 0)
// std::basic_string_view
template
const_buffer buffer(std::basic_string_view data) noexcept
{
return detail::buffer_contiguous_sequence(data);
}
template
const_buffer buffer(std::basic_string_view data, size_t n_bytes) noexcept
{
return detail::buffer_contiguous_sequence(data, n_bytes);
}
#endif
// Buffer for a string literal (null terminated)
// where the buffer size excludes the terminating character.
// Equivalent to zmq::buffer(std::string_view("...")).
template
constexpr const_buffer str_buffer(const Char (&data)[N]) noexcept
{
static_assert(detail::is_pod_like::value, "Char must be POD");
#ifdef ZMQ_CPP14
assert(data[N - 1] == Char{0});
#endif
return const_buffer(static_cast(data),
(N - 1) * sizeof(Char));
}
namespace literals
{
constexpr const_buffer operator"" _zbuf(const char* str, size_t len) noexcept
{
return const_buffer(str, len * sizeof(char));
}
constexpr const_buffer operator"" _zbuf(const wchar_t* str, size_t len) noexcept
{
return const_buffer(str, len * sizeof(wchar_t));
}
constexpr const_buffer operator"" _zbuf(const char16_t* str, size_t len) noexcept
{
return const_buffer(str, len * sizeof(char16_t));
}
constexpr const_buffer operator"" _zbuf(const char32_t* str, size_t len) noexcept
{
return const_buffer(str, len * sizeof(char32_t));
}
}
#endif // ZMQ_CPP11
namespace detail
{
class socket_base
{
public:
socket_base() ZMQ_NOTHROW : _handle(ZMQ_NULLPTR) {}
ZMQ_EXPLICIT socket_base(void *handle) ZMQ_NOTHROW : _handle(handle) {}
template void setsockopt(int option_, T const &optval)
{
setsockopt(option_, &optval, sizeof(T));
}
void setsockopt(int option_, const void *optval_, size_t optvallen_)
{
int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_);
if (rc != 0)
throw error_t();
}
void getsockopt(int option_, void *optval_, size_t *optvallen_) const
{
int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_);
if (rc != 0)
throw error_t();
}
template T getsockopt(int option_) const
{
T optval;
size_t optlen = sizeof(T);
getsockopt(option_, &optval, &optlen);
return optval;
}
void bind(std::string const &addr) { bind(addr.c_str()); }
void bind(const char *addr_)
{
int rc = zmq_bind(_handle, addr_);
if (rc != 0)
throw error_t();
}
void unbind(std::string const &addr) { unbind(addr.c_str()); }
void unbind(const char *addr_)
{
int rc = zmq_unbind(_handle, addr_);
if (rc != 0)
throw error_t();
}
void connect(std::string const &addr) { connect(addr.c_str()); }
void connect(const char *addr_)
{
int rc = zmq_connect(_handle, addr_);
if (rc != 0)
throw error_t();
}
void disconnect(std::string const &addr) { disconnect(addr.c_str()); }
void disconnect(const char *addr_)
{
int rc = zmq_disconnect(_handle, addr_);
if (rc != 0)
throw error_t();
}
bool connected() const ZMQ_NOTHROW { return (_handle != ZMQ_NULLPTR); }
#ifdef ZMQ_CPP11
ZMQ_DEPRECATED("from 4.3.1, use send taking a const_buffer and send_flags")
#endif
size_t send(const void *buf_, size_t len_, int flags_ = 0)
{
int nbytes = zmq_send(_handle, buf_, len_, flags_);
if (nbytes >= 0)
return static_cast(nbytes);
if (zmq_errno() == EAGAIN)
return 0;
throw error_t();
}
#ifdef ZMQ_CPP11
ZMQ_DEPRECATED("from 4.3.1, use send taking message_t and send_flags")
#endif
bool send(message_t &msg_,
int flags_ = 0) // default until removed
{
int nbytes = zmq_msg_send(msg_.handle(), _handle, flags_);
if (nbytes >= 0)
return true;
if (zmq_errno() == EAGAIN)
return false;
throw error_t();
}
template
#ifdef ZMQ_CPP11
ZMQ_DEPRECATED("from 4.4.1, use send taking message_t or buffer (for contiguous ranges), and send_flags")
#endif
bool send(T first, T last, int flags_ = 0)
{
zmq::message_t msg(first, last);
int nbytes = zmq_msg_send(msg.handle(), _handle, flags_);
if (nbytes >= 0)
return true;
if (zmq_errno() == EAGAIN)
return false;
throw error_t();
}
#ifdef ZMQ_HAS_RVALUE_REFS
#ifdef ZMQ_CPP11
ZMQ_DEPRECATED("from 4.3.1, use send taking message_t and send_flags")
#endif
bool send(message_t &&msg_,
int flags_ = 0) // default until removed
{
#ifdef ZMQ_CPP11
return send(msg_, static_cast(flags_)).has_value();
#else
return send(msg_, flags_);
#endif
}
#endif
#ifdef ZMQ_CPP11
send_result_t send(const_buffer buf, send_flags flags = send_flags::none)
{
const int nbytes =
zmq_send(_handle, buf.data(), buf.size(), static_cast(flags));
if (nbytes >= 0)
return static_cast(nbytes);
if (zmq_errno() == EAGAIN)
return {};
throw error_t();
}
send_result_t send(message_t &msg, send_flags flags)
{
int nbytes = zmq_msg_send(msg.handle(), _handle, static_cast(flags));
if (nbytes >= 0)
return static_cast(nbytes);
if (zmq_errno() == EAGAIN)
return {};
throw error_t();
}
send_result_t send(message_t &&msg, send_flags flags)
{
return send(msg, flags);
}
#endif
#ifdef ZMQ_CPP11
ZMQ_DEPRECATED("from 4.3.1, use recv taking a mutable_buffer and recv_flags")
#endif
size_t recv(void *buf_, size_t len_, int flags_ = 0)
{
int nbytes = zmq_recv(_handle, buf_, len_, flags_);
if (nbytes >= 0)
return static_cast(nbytes);
if (zmq_errno() == EAGAIN)
return 0;
throw error_t();
}
#ifdef ZMQ_CPP11
ZMQ_DEPRECATED("from 4.3.1, use recv taking a reference to message_t and recv_flags")
#endif
bool recv(message_t *msg_, int flags_ = 0)
{
int nbytes = zmq_msg_recv(msg_->handle(), _handle, flags_);
if (nbytes >= 0)
return true;
if (zmq_errno() == EAGAIN)
return false;
throw error_t();
}
#ifdef ZMQ_CPP11
ZMQ_NODISCARD
recv_buffer_result_t recv(mutable_buffer buf,
recv_flags flags = recv_flags::none)
{
const int nbytes =
zmq_recv(_handle, buf.data(), buf.size(), static_cast(flags));
if (nbytes >= 0) {
return recv_buffer_size{(std::min)(static_cast(nbytes), buf.size()),
static_cast(nbytes)};
}
if (zmq_errno() == EAGAIN)
return {};
throw error_t();
}
ZMQ_NODISCARD
recv_result_t recv(message_t &msg, recv_flags flags = recv_flags::none)
{
const int nbytes = zmq_msg_recv(msg.handle(), _handle, static_cast(flags));
if (nbytes >= 0) {
assert(msg.size() == static_cast(nbytes));
return static_cast(nbytes);
}
if (zmq_errno() == EAGAIN)
return {};
throw error_t();
}
#endif
#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
void join(const char* group)
{
int rc = zmq_join(_handle, group);
if (rc != 0)
throw error_t();
}
void leave(const char* group)
{
int rc = zmq_leave(_handle, group);
if (rc != 0)
throw error_t();
}
#endif
ZMQ_NODISCARD void *handle() ZMQ_NOTHROW { return _handle; }
ZMQ_NODISCARD const void *handle() const ZMQ_NOTHROW { return _handle; }
ZMQ_EXPLICIT operator bool() const ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; }
// note: non-const operator bool can be removed once
// operator void* is removed from socket_t
ZMQ_EXPLICIT operator bool() ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; }
protected:
void *_handle;
};
} // namespace detail
#ifdef ZMQ_CPP11
enum class socket_type : int
{
req = ZMQ_REQ,
rep = ZMQ_REP,
dealer = ZMQ_DEALER,
router = ZMQ_ROUTER,
pub = ZMQ_PUB,
sub = ZMQ_SUB,
xpub = ZMQ_XPUB,
xsub = ZMQ_XSUB,
push = ZMQ_PUSH,
pull = ZMQ_PULL,
#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
server = ZMQ_SERVER,
client = ZMQ_CLIENT,
radio = ZMQ_RADIO,
dish = ZMQ_DISH,
#endif
#if ZMQ_VERSION_MAJOR >= 4
stream = ZMQ_STREAM,
#endif
pair = ZMQ_PAIR
};
#endif
struct from_handle_t
{
struct _private {}; // disabling use other than with from_handle
ZMQ_CONSTEXPR_FN ZMQ_EXPLICIT from_handle_t(_private /*p*/) ZMQ_NOTHROW {}
};
ZMQ_CONSTEXPR_VAR from_handle_t from_handle = from_handle_t(from_handle_t::_private());
// A non-owning nullable reference to a socket.
// The reference is invalidated on socket close or destruction.
class socket_ref : public detail::socket_base
{
public:
socket_ref() ZMQ_NOTHROW : detail::socket_base() {}
#ifdef ZMQ_CPP11
socket_ref(std::nullptr_t) ZMQ_NOTHROW : detail::socket_base() {}
#endif
socket_ref(from_handle_t /*fh*/, void *handle) ZMQ_NOTHROW
: detail::socket_base(handle) {}
};
#ifdef ZMQ_CPP11
inline bool operator==(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW
{
return sr.handle() == nullptr;
}
inline bool operator==(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW
{
return sr.handle() == nullptr;
}
inline bool operator!=(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW
{
return !(sr == nullptr);
}
inline bool operator!=(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW
{
return !(sr == nullptr);
}
#endif
inline bool operator==(socket_ref a, socket_ref b) ZMQ_NOTHROW
{
return std::equal_to()(a.handle(), b.handle());
}
inline bool operator!=(socket_ref a, socket_ref b) ZMQ_NOTHROW
{
return !(a == b);
}
inline bool operator<(socket_ref a, socket_ref b) ZMQ_NOTHROW
{
return std::less()(a.handle(), b.handle());
}
inline bool operator>(socket_ref a, socket_ref b) ZMQ_NOTHROW
{
return b < a;
}
inline bool operator<=(socket_ref a, socket_ref b) ZMQ_NOTHROW
{
return !(a > b);
}
inline bool operator>=(socket_ref a, socket_ref b) ZMQ_NOTHROW
{
return !(a < b);
}
} // namespace zmq
#ifdef ZMQ_CPP11
namespace std
{
template<>
struct hash
{
size_t operator()(zmq::socket_ref sr) const ZMQ_NOTHROW
{
return hash()(sr.handle());
}
};
} // namespace std
#endif
namespace zmq
{
class socket_t : public detail::socket_base
{
friend class monitor_t;
public:
socket_t() ZMQ_NOTHROW
: detail::socket_base(ZMQ_NULLPTR)
, ctxptr(ZMQ_NULLPTR)
{
}
socket_t(context_t &context_, int type_)
: detail::socket_base(zmq_socket(static_cast(context_), type_))
, ctxptr(static_cast(context_))
{
if (_handle == ZMQ_NULLPTR)
throw error_t();
}
#ifdef ZMQ_CPP11
socket_t(context_t &context_, socket_type type_)
: socket_t(context_, static_cast(type_))
{
}
#endif
#ifdef ZMQ_HAS_RVALUE_REFS
socket_t(socket_t &&rhs) ZMQ_NOTHROW : detail::socket_base(rhs._handle), ctxptr(rhs.ctxptr)
{
rhs._handle = ZMQ_NULLPTR;
rhs.ctxptr = ZMQ_NULLPTR;
}
socket_t &operator=(socket_t &&rhs) ZMQ_NOTHROW
{
close();
std::swap(_handle, rhs._handle);
return *this;
}
#endif
~socket_t() ZMQ_NOTHROW { close(); }
operator void *() ZMQ_NOTHROW { return _handle; }
operator void const *() const ZMQ_NOTHROW { return _handle; }
void close() ZMQ_NOTHROW
{
if (_handle == ZMQ_NULLPTR)
// already closed
return;
int rc = zmq_close(_handle);
ZMQ_ASSERT(rc == 0);
_handle = ZMQ_NULLPTR;
}
void swap(socket_t &other) ZMQ_NOTHROW
{
std::swap(_handle, other._handle);
std::swap(ctxptr, other.ctxptr);
}
operator socket_ref() ZMQ_NOTHROW
{
return socket_ref(from_handle, _handle);
}
private:
void *ctxptr;
socket_t(const socket_t &) ZMQ_DELETED_FUNCTION;
void operator=(const socket_t &) ZMQ_DELETED_FUNCTION;
// used by monitor_t
socket_t(void *context_, int type_)
: detail::socket_base(zmq_socket(context_, type_))
, ctxptr(context_)
{
if (_handle == ZMQ_NULLPTR)
throw error_t();
}
};
inline void swap(socket_t &a, socket_t &b) ZMQ_NOTHROW {
a.swap(b);
}
ZMQ_DEPRECATED("from 4.3.1, use proxy taking socket_t objects")
inline void proxy(void *frontend, void *backend, void *capture)
{
int rc = zmq_proxy(frontend, backend, capture);
if (rc != 0)
throw error_t();
}
inline void
proxy(socket_ref frontend, socket_ref backend, socket_ref capture = socket_ref())
{
int rc = zmq_proxy(frontend.handle(), backend.handle(), capture.handle());
if (rc != 0)
throw error_t();
}
#ifdef ZMQ_HAS_PROXY_STEERABLE
ZMQ_DEPRECATED("from 4.3.1, use proxy_steerable taking socket_t objects")
inline void
proxy_steerable(void *frontend, void *backend, void *capture, void *control)
{
int rc = zmq_proxy_steerable(frontend, backend, capture, control);
if (rc != 0)
throw error_t();
}
inline void proxy_steerable(socket_ref frontend,
socket_ref backend,
socket_ref capture,
socket_ref control)
{
int rc = zmq_proxy_steerable(frontend.handle(), backend.handle(),
capture.handle(), control.handle());
if (rc != 0)
throw error_t();
}
#endif
class monitor_t
{
public:
monitor_t() : _socket(), _monitor_socket() {}
virtual ~monitor_t()
{
close();
}
#ifdef ZMQ_HAS_RVALUE_REFS
monitor_t(monitor_t &&rhs) ZMQ_NOTHROW : _socket(), _monitor_socket()
{
std::swap(_socket, rhs._socket);
std::swap(_monitor_socket, rhs._monitor_socket);
}
monitor_t &operator=(monitor_t &&rhs) ZMQ_NOTHROW
{
close();
_socket = socket_ref();
std::swap(_socket, rhs._socket);
std::swap(_monitor_socket, rhs._monitor_socket);
return *this;
}
#endif
void
monitor(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL)
{
monitor(socket, addr.c_str(), events);
}
void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL)
{
init(socket, addr_, events);
while (true) {
check_event(-1);
}
}
void init(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL)
{
init(socket, addr.c_str(), events);
}
void init(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL)
{
int rc = zmq_socket_monitor(socket.handle(), addr_, events);
if (rc != 0)
throw error_t();
_socket = socket;
_monitor_socket = socket_t(socket.ctxptr, ZMQ_PAIR);
_monitor_socket.connect(addr_);
on_monitor_started();
}
bool check_event(int timeout = 0)
{
assert(_monitor_socket);
zmq_msg_t eventMsg;
zmq_msg_init(&eventMsg);
zmq::pollitem_t items[] = {
{_monitor_socket.handle(), 0, ZMQ_POLLIN, 0},
};
zmq::poll(&items[0], 1, timeout);
if (items[0].revents & ZMQ_POLLIN) {
int rc = zmq_msg_recv(&eventMsg, _monitor_socket.handle(), 0);
if (rc == -1 && zmq_errno() == ETERM)
return false;
assert(rc != -1);
} else {
zmq_msg_close(&eventMsg);
return false;
}
#if ZMQ_VERSION_MAJOR >= 4
const char *data = static_cast(zmq_msg_data(&eventMsg));
zmq_event_t msgEvent;
memcpy(&msgEvent.event, data, sizeof(uint16_t));
data += sizeof(uint16_t);
memcpy(&msgEvent.value, data, sizeof(int32_t));
zmq_event_t *event = &msgEvent;
#else
zmq_event_t *event = static_cast(zmq_msg_data(&eventMsg));
#endif
#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT
zmq_msg_t addrMsg;
zmq_msg_init(&addrMsg);
int rc = zmq_msg_recv(&addrMsg, _monitor_socket.handle(), 0);
if (rc == -1 && zmq_errno() == ETERM) {
zmq_msg_close(&eventMsg);
return false;
}
assert(rc != -1);
const char *str = static_cast(zmq_msg_data(&addrMsg));
std::string address(str, str + zmq_msg_size(&addrMsg));
zmq_msg_close(&addrMsg);
#else
// Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types.
std::string address = event->data.connected.addr;
#endif
#ifdef ZMQ_EVENT_MONITOR_STOPPED
if (event->event == ZMQ_EVENT_MONITOR_STOPPED) {
zmq_msg_close(&eventMsg);
return false;
}
#endif
switch (event->event) {
case ZMQ_EVENT_CONNECTED:
on_event_connected(*event, address.c_str());
break;
case ZMQ_EVENT_CONNECT_DELAYED:
on_event_connect_delayed(*event, address.c_str());
break;
case ZMQ_EVENT_CONNECT_RETRIED:
on_event_connect_retried(*event, address.c_str());
break;
case ZMQ_EVENT_LISTENING:
on_event_listening(*event, address.c_str());
break;
case ZMQ_EVENT_BIND_FAILED:
on_event_bind_failed(*event, address.c_str());
break;
case ZMQ_EVENT_ACCEPTED:
on_event_accepted(*event, address.c_str());
break;
case ZMQ_EVENT_ACCEPT_FAILED:
on_event_accept_failed(*event, address.c_str());
break;
case ZMQ_EVENT_CLOSED:
on_event_closed(*event, address.c_str());
break;
case ZMQ_EVENT_CLOSE_FAILED:
on_event_close_failed(*event, address.c_str());
break;
case ZMQ_EVENT_DISCONNECTED:
on_event_disconnected(*event, address.c_str());
break;
#ifdef ZMQ_BUILD_DRAFT_API
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL:
on_event_handshake_failed_no_detail(*event, address.c_str());
break;
case ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL:
on_event_handshake_failed_protocol(*event, address.c_str());
break;
case ZMQ_EVENT_HANDSHAKE_FAILED_AUTH:
on_event_handshake_failed_auth(*event, address.c_str());
break;
case ZMQ_EVENT_HANDSHAKE_SUCCEEDED:
on_event_handshake_succeeded(*event, address.c_str());
break;
#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1)
case ZMQ_EVENT_HANDSHAKE_FAILED:
on_event_handshake_failed(*event, address.c_str());
break;
case ZMQ_EVENT_HANDSHAKE_SUCCEED:
on_event_handshake_succeed(*event, address.c_str());
break;
#endif
#endif
default:
on_event_unknown(*event, address.c_str());
break;
}
zmq_msg_close(&eventMsg);
return true;
}
#ifdef ZMQ_EVENT_MONITOR_STOPPED
void abort()
{
if (_socket)
zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0);
_socket = socket_ref();
}
#endif
virtual void on_monitor_started() {}
virtual void on_event_connected(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_connect_delayed(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_connect_retried(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_listening(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_bind_failed(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_accepted(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_accept_failed(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_closed(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_close_failed(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_disconnected(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
virtual void on_event_handshake_failed_no_detail(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_handshake_failed_protocol(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_handshake_failed_auth(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_handshake_succeeded(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1)
virtual void on_event_handshake_failed(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
virtual void on_event_handshake_succeed(const zmq_event_t &event_,
const char *addr_)
{
(void) event_;
(void) addr_;
}
#endif
virtual void on_event_unknown(const zmq_event_t &event_, const char *addr_)
{
(void) event_;
(void) addr_;
}
private:
monitor_t(const monitor_t &) ZMQ_DELETED_FUNCTION;
void operator=(const monitor_t &) ZMQ_DELETED_FUNCTION;
socket_ref _socket;
socket_t _monitor_socket;
void close() ZMQ_NOTHROW
{
if (_socket)
zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0);
_monitor_socket.close();
}
};
#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
// polling events
enum class event_flags : short
{
none = 0,
pollin = ZMQ_POLLIN,
pollout = ZMQ_POLLOUT,
pollerr = ZMQ_POLLERR,
pollpri = ZMQ_POLLPRI
};
constexpr event_flags operator|(event_flags a, event_flags b) noexcept
{
return detail::enum_bit_or(a, b);
}
constexpr event_flags operator&(event_flags a, event_flags b) noexcept
{
return detail::enum_bit_and(a, b);
}
constexpr event_flags operator^(event_flags a, event_flags b) noexcept
{
return detail::enum_bit_xor(a, b);
}
constexpr event_flags operator~(event_flags a) noexcept
{
return detail::enum_bit_not(a);
}
struct no_user_data;
// layout compatible with zmq_poller_event_t
template
struct poller_event
{
socket_ref socket;
#ifdef _WIN32
SOCKET fd;
#else
int fd;
#endif
T *user_data;
event_flags events;
};
template class poller_t
{
public:
using event_type = poller_event;
poller_t() : poller_ptr(zmq_poller_new())
{
if (!poller_ptr)
throw error_t();
}
template<
typename Dummy = void,
typename =
typename std::enable_if::value, Dummy>::type>
void add(zmq::socket_ref socket, event_flags events, T *user_data)
{
add_impl(socket, events, user_data);
}
void add(zmq::socket_ref socket, event_flags events)
{
add_impl(socket, events, nullptr);
}
void remove(zmq::socket_ref socket)
{
if (0 != zmq_poller_remove(poller_ptr.get(), socket.handle())) {
throw error_t();
}
}
void modify(zmq::socket_ref socket, event_flags events)
{
if (0
!= zmq_poller_modify(poller_ptr.get(), socket.handle(),
static_cast(events))) {
throw error_t();
}
}
size_t wait_all(std::vector &poller_events,
const std::chrono::milliseconds timeout)
{
int rc = zmq_poller_wait_all(
poller_ptr.get(),
reinterpret_cast(poller_events.data()),
static_cast(poller_events.size()),
static_cast(timeout.count()));
if (rc > 0)
return static_cast(rc);
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
if (zmq_errno() == EAGAIN)
#else
if (zmq_errno() == ETIMEDOUT)
#endif
return 0;
throw error_t();
}
private:
struct destroy_poller_t
{
void operator()(void *ptr) noexcept
{
int rc = zmq_poller_destroy(&ptr);
ZMQ_ASSERT(rc == 0);
}
};
std::unique_ptr poller_ptr;
void add_impl(zmq::socket_ref socket, event_flags events, T *user_data)
{
if (0
!= zmq_poller_add(poller_ptr.get(), socket.handle(),
user_data, static_cast(events))) {
throw error_t();
}
}
};
#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
inline std::ostream &operator<<(std::ostream &os, const message_t &msg)
{
return os << msg.str();
}
} // namespace zmq
#endif // __ZMQ_HPP_INCLUDED__
ODR-DabMod-2.6.0/lib/Log.cpp 0000644 0001750 0001750 00000013316 14230010227 014261 0 ustar robin robin /*
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Her Majesty the Queen in Right of Canada (Communications Research
Center Canada)
Copyright (C) 2018
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
*/
/*
This file is part of the ODR-mmbTools.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include "Log.h"
using namespace std;
Logger::Logger()
{
m_io_thread = std::thread(&Logger::io_process, this);
}
Logger::~Logger() {
m_message_queue.trigger_wakeup();
m_io_thread.join();
std::lock_guard guard(m_backend_mutex);
backends.clear();
}
void Logger::register_backend(std::shared_ptr backend)
{
std::lock_guard guard(m_backend_mutex);
backends.push_back(backend);
}
void Logger::log(log_level_t level, const char* fmt, ...)
{
if (level == discard) {
return;
}
int size = 100;
std::string str;
va_list ap;
while (1) {
str.resize(size);
va_start(ap, fmt);
int n = vsnprintf((char *)str.c_str(), size, fmt, ap);
va_end(ap);
if (n > -1 && n < size) {
str.resize(n);
break;
}
if (n > -1)
size = n + 1;
else
size *= 2;
}
logstr(level, move(str));
}
void Logger::logstr(log_level_t level, std::string&& message)
{
if (level == discard) {
return;
}
log_message_t m(level, move(message));
m_message_queue.push(move(m));
}
void Logger::io_process()
{
while (1) {
log_message_t m;
try {
m_message_queue.wait_and_pop(m);
}
catch (const ThreadsafeQueueWakeup&) {
break;
}
auto message = m.message;
/* Remove a potential trailing newline.
* It doesn't look good in syslog
*/
if (message[message.length()-1] == '\n') {
message.resize(message.length()-1);
}
{
std::lock_guard guard(m_backend_mutex);
for (auto &backend : backends) {
backend->log(m.level, message);
}
if (m.level != log_level_t::trace) {
using namespace std::chrono;
time_t t = system_clock::to_time_t(system_clock::now());
cerr << put_time(std::gmtime(&t), "%Y-%m-%dZ%H:%M:%S") << " " << levels_as_str[m.level] << " " << message << endl;
}
}
}
}
LogLine Logger::level(log_level_t level)
{
return LogLine(this, level);
}
LogToFile::LogToFile(const std::string& filename) : name("FILE")
{
FILE* fd = fopen(filename.c_str(), "a");
if (fd == nullptr) {
fprintf(stderr, "Cannot open log file !");
throw std::runtime_error("Cannot open log file !");
}
log_file.reset(fd);
}
void LogToFile::log(log_level_t level, const std::string& message)
{
if (not (level == log_level_t::trace or level == log_level_t::discard)) {
const char* log_level_text[] = {
"DEBUG", "INFO", "WARN", "ERROR", "ALERT", "EMERG"};
// fprintf is thread-safe
fprintf(log_file.get(), SYSLOG_IDENT ": %s: %s\n",
log_level_text[(size_t)level], message.c_str());
fflush(log_file.get());
}
}
void LogToSyslog::log(log_level_t level, const std::string& message)
{
if (not (level == log_level_t::trace or level == log_level_t::discard)) {
int syslog_level = LOG_EMERG;
switch (level) {
case debug: syslog_level = LOG_DEBUG; break;
case info: syslog_level = LOG_INFO; break;
/* we don't have the notice level */
case warn: syslog_level = LOG_WARNING; break;
case error: syslog_level = LOG_ERR; break;
default: syslog_level = LOG_CRIT; break;
case alert: syslog_level = LOG_ALERT; break;
case emerg: syslog_level = LOG_EMERG; break;
}
syslog(syslog_level, SYSLOG_IDENT " %s", message.c_str());
}
}
LogTracer::LogTracer(const string& trace_filename) : name("TRACE")
{
etiLog.level(info) << "Setting up TRACE to " << trace_filename;
FILE* fd = fopen(trace_filename.c_str(), "a");
if (fd == nullptr) {
fprintf(stderr, "Cannot open trace file !");
throw std::runtime_error("Cannot open trace file !");
}
m_trace_file.reset(fd);
using namespace std::chrono;
auto now = steady_clock::now().time_since_epoch();
m_trace_micros_startup = duration_cast(now).count();
fprintf(m_trace_file.get(),
"0,TRACER,startup at %" PRIu64 "\n", m_trace_micros_startup);
}
void LogTracer::log(log_level_t level, const std::string& message)
{
if (level == log_level_t::trace) {
using namespace std::chrono;
const auto now = steady_clock::now().time_since_epoch();
const auto micros = duration_cast(now).count();
fprintf(m_trace_file.get(), "%" PRIu64 ",%s\n",
micros - m_trace_micros_startup,
message.c_str());
}
}
ODR-DabMod-2.6.0/lib/Socket.cpp 0000644 0001750 0001750 00000075150 14230010227 014774 0 ustar robin robin /*
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
Queen in Right of Canada (Communications Research Center Canada)
Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
*/
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "Socket.h"
#include
#include
#include
#include
#include
#include
namespace Socket {
using namespace std;
void InetAddress::resolveUdpDestination(const std::string& destination, int port)
{
char service[NI_MAXSERV];
snprintf(service, NI_MAXSERV-1, "%d", port);
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
hints.ai_flags = 0;
hints.ai_protocol = 0;
struct addrinfo *result, *rp;
int s = getaddrinfo(destination.c_str(), service, &hints, &result);
if (s != 0) {
throw runtime_error(string("getaddrinfo failed: ") + gai_strerror(s));
}
for (rp = result; rp != nullptr; rp = rp->ai_next) {
// Take the first result
memcpy(&addr, rp->ai_addr, rp->ai_addrlen);
break;
}
freeaddrinfo(result);
if (rp == nullptr) {
throw runtime_error("Could not resolve");
}
}
string InetAddress::to_string() const
{
char received_from_str[64] = {};
sockaddr *addr = reinterpret_cast(&addr);
const char* ret = inet_ntop(AF_INET, addr, received_from_str, 63);
if (ret == nullptr) {
throw invalid_argument(string("Error converting InetAddress") + strerror(errno));
}
return ret;
}
UDPPacket::UDPPacket() { }
UDPPacket::UDPPacket(size_t initSize) :
buffer(initSize),
address()
{ }
UDPSocket::UDPSocket()
{
reinit(0, "");
}
UDPSocket::UDPSocket(int port)
{
reinit(port, "");
}
UDPSocket::UDPSocket(int port, const std::string& name)
{
reinit(port, name);
}
UDPSocket::UDPSocket(UDPSocket&& other)
{
m_sock = other.m_sock;
m_port = other.m_port;
other.m_port = 0;
other.m_sock = INVALID_SOCKET;
}
const UDPSocket& UDPSocket::operator=(UDPSocket&& other)
{
m_sock = other.m_sock;
m_port = other.m_port;
other.m_port = 0;
other.m_sock = INVALID_SOCKET;
return *this;
}
void UDPSocket::setBlocking(bool block)
{
int res = fcntl(m_sock, F_SETFL, block ? 0 : O_NONBLOCK);
if (res == -1) {
throw runtime_error(string("Can't change blocking state of socket: ") + strerror(errno));
}
}
void UDPSocket::reinit(int port)
{
return reinit(port, "");
}
void UDPSocket::reinit(int port, const std::string& name)
{
if (m_sock != INVALID_SOCKET) {
::close(m_sock);
}
m_port = port;
if (port == 0) {
// No need to bind to a given port, creating the
// socket is enough
m_sock = ::socket(AF_INET, SOCK_DGRAM, 0);
return;
}
char service[NI_MAXSERV];
snprintf(service, NI_MAXSERV-1, "%d", port);
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
hints.ai_canonname = nullptr;
hints.ai_addr = nullptr;
hints.ai_next = nullptr;
struct addrinfo *result, *rp;
int s = getaddrinfo(name.empty() ? nullptr : name.c_str(),
port == 0 ? nullptr : service,
&hints, &result);
if (s != 0) {
throw runtime_error(string("getaddrinfo failed: ") + gai_strerror(s));
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != nullptr; rp = rp->ai_next) {
int sfd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1) {
continue;
}
if (::bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {
m_sock = sfd;
break;
}
::close(sfd);
}
freeaddrinfo(result);
if (rp == nullptr) {
throw runtime_error("Could not bind");
}
}
void UDPSocket::close()
{
if (m_sock != INVALID_SOCKET) {
::close(m_sock);
}
m_sock = INVALID_SOCKET;
}
UDPSocket::~UDPSocket()
{
if (m_sock != INVALID_SOCKET) {
::close(m_sock);
}
}
UDPPacket UDPSocket::receive(size_t max_size)
{
UDPPacket packet(max_size);
socklen_t addrSize;
addrSize = sizeof(*packet.address.as_sockaddr());
ssize_t ret = recvfrom(m_sock,
packet.buffer.data(),
packet.buffer.size(),
0,
packet.address.as_sockaddr(),
&addrSize);
if (ret == SOCKET_ERROR) {
packet.buffer.resize(0);
// This suppresses the -Wlogical-op warning
#if EAGAIN == EWOULDBLOCK
if (errno == EAGAIN)
#else
if (errno == EAGAIN or errno == EWOULDBLOCK)
#endif
{
return 0;
}
throw runtime_error(string("Can't receive data: ") + strerror(errno));
}
packet.buffer.resize(ret);
return packet;
}
void UDPSocket::send(UDPPacket& packet)
{
const int ret = sendto(m_sock, packet.buffer.data(), packet.buffer.size(), 0,
packet.address.as_sockaddr(), sizeof(*packet.address.as_sockaddr()));
if (ret == SOCKET_ERROR && errno != ECONNREFUSED) {
throw runtime_error(string("Can't send UDP packet: ") + strerror(errno));
}
}
void UDPSocket::send(const std::vector& data, InetAddress destination)
{
const int ret = sendto(m_sock, data.data(), data.size(), 0,
destination.as_sockaddr(), sizeof(*destination.as_sockaddr()));
if (ret == SOCKET_ERROR && errno != ECONNREFUSED) {
throw runtime_error(string("Can't send UDP packet: ") + strerror(errno));
}
}
void UDPSocket::send(const std::string& data, InetAddress destination)
{
const int ret = sendto(m_sock, data.data(), data.size(), 0,
destination.as_sockaddr(), sizeof(*destination.as_sockaddr()));
if (ret == SOCKET_ERROR && errno != ECONNREFUSED) {
throw runtime_error(string("Can't send UDP packet: ") + strerror(errno));
}
}
void UDPSocket::joinGroup(const char* groupname, const char* if_addr)
{
ip_mreqn group;
if ((group.imr_multiaddr.s_addr = inet_addr(groupname)) == INADDR_NONE) {
throw runtime_error("Cannot convert multicast group name");
}
if (!IN_MULTICAST(ntohl(group.imr_multiaddr.s_addr))) {
throw runtime_error("Group name is not a multicast address");
}
if (if_addr) {
group.imr_address.s_addr = inet_addr(if_addr);
}
else {
group.imr_address.s_addr = htons(INADDR_ANY);
}
group.imr_ifindex = 0;
if (setsockopt(m_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group))
== SOCKET_ERROR) {
throw runtime_error(string("Can't join multicast group") + strerror(errno));
}
}
void UDPSocket::setMulticastSource(const char* source_addr)
{
struct in_addr addr;
if (inet_aton(source_addr, &addr) == 0) {
throw runtime_error(string("Can't parse source address") + strerror(errno));
}
if (setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr))
== SOCKET_ERROR) {
throw runtime_error(string("Can't set source address") + strerror(errno));
}
}
void UDPSocket::setMulticastTTL(int ttl)
{
if (setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))
== SOCKET_ERROR) {
throw runtime_error(string("Can't set multicast ttl") + strerror(errno));
}
}
SOCKET UDPSocket::getNativeSocket() const
{
return m_sock;
}
int UDPSocket::getPort() const
{
return m_port;
}
void UDPReceiver::add_receive_port(int port, const string& bindto, const string& mcastaddr) {
UDPSocket sock;
if (IN_MULTICAST(ntohl(inet_addr(mcastaddr.c_str())))) {
sock.reinit(port, mcastaddr);
sock.setMulticastSource(bindto.c_str());
sock.joinGroup(mcastaddr.c_str(), bindto.c_str());
}
else {
sock.reinit(port, bindto);
}
m_sockets.push_back(move(sock));
}
vector UDPReceiver::receive(int timeout_ms)
{
constexpr size_t MAX_FDS = 64;
struct pollfd fds[MAX_FDS];
if (m_sockets.size() > MAX_FDS) {
throw std::runtime_error("UDPReceiver only supports up to 64 ports");
}
for (size_t i = 0; i < m_sockets.size(); i++) {
fds[i].fd = m_sockets[i].getNativeSocket();
fds[i].events = POLLIN;
}
int retval = poll(fds, m_sockets.size(), timeout_ms);
if (retval == -1 and errno == EINTR) {
throw Interrupted();
}
else if (retval == -1) {
std::string errstr(strerror(errno));
throw std::runtime_error("UDP receive with poll() error: " + errstr);
}
else if (retval > 0) {
vector received;
for (size_t i = 0; i < m_sockets.size(); i++) {
if (fds[i].revents & POLLIN) {
auto p = m_sockets[i].receive(2048); // This is larger than the usual MTU
ReceivedPacket rp;
rp.packetdata = move(p.buffer);
rp.received_from = move(p.address);
rp.port_received_on = m_sockets[i].getPort();
received.push_back(move(rp));
}
}
return received;
}
else {
throw Timeout();
}
}
TCPSocket::TCPSocket()
{
}
TCPSocket::~TCPSocket()
{
if (m_sock != -1) {
::close(m_sock);
}
}
TCPSocket::TCPSocket(TCPSocket&& other) :
m_sock(other.m_sock),
m_remote_address(move(other.m_remote_address))
{
if (other.m_sock != -1) {
other.m_sock = -1;
}
}
TCPSocket& TCPSocket::operator=(TCPSocket&& other)
{
swap(m_remote_address, other.m_remote_address);
m_sock = other.m_sock;
if (other.m_sock != -1) {
other.m_sock = -1;
}
return *this;
}
bool TCPSocket::valid() const
{
return m_sock != -1;
}
void TCPSocket::connect(const std::string& hostname, int port, int timeout_ms)
{
if (m_sock != INVALID_SOCKET) {
throw std::logic_error("You may only connect an invalid TCPSocket");
}
char service[NI_MAXSERV];
snprintf(service, NI_MAXSERV-1, "%d", port);
/* Obtain address(es) matching host/port */
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
struct addrinfo *result, *rp;
int s = getaddrinfo(hostname.c_str(), service, &hints, &result);
if (s != 0) {
throw runtime_error(string("getaddrinfo failed: ") + gai_strerror(s));
}
int flags = 0;
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully connect(2).
If socket(2) (or connect(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != nullptr; rp = rp->ai_next) {
int sfd = ::socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
flags = fcntl(sfd, F_GETFL);
if (flags == -1) {
std::string errstr(strerror(errno));
throw std::runtime_error("TCP: Could not get socket flags: " + errstr);
}
if (fcntl(sfd, F_SETFL, flags | O_NONBLOCK) == -1) {
std::string errstr(strerror(errno));
throw std::runtime_error("TCP: Could not set O_NONBLOCK: " + errstr);
}
int ret = ::connect(sfd, rp->ai_addr, rp->ai_addrlen);
if (ret == 0) {
m_sock = sfd;
break;
}
if (ret == -1 and errno == EINPROGRESS) {
m_sock = sfd;
struct pollfd fds[1];
fds[0].fd = m_sock;
fds[0].events = POLLOUT;
int retval = poll(fds, 1, timeout_ms);
if (retval == -1) {
std::string errstr(strerror(errno));
::close(m_sock);
freeaddrinfo(result);
throw runtime_error("TCP: connect error on poll: " + errstr);
}
else if (retval > 0) {
int so_error = 0;
socklen_t len = sizeof(so_error);
if (getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &so_error, &len) == -1) {
std::string errstr(strerror(errno));
::close(m_sock);
freeaddrinfo(result);
throw runtime_error("TCP: getsockopt error connect: " + errstr);
}
if (so_error == 0) {
break;
}
}
else {
::close(m_sock);
freeaddrinfo(result);
throw runtime_error("Timeout on connect");
}
break;
}
::close(sfd);
}
if (m_sock != INVALID_SOCKET) {
#if defined(HAVE_SO_NOSIGPIPE)
int val = 1;
if (setsockopt(m_sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))
== SOCKET_ERROR) {
throw runtime_error("Can't set SO_NOSIGPIPE");
}
#endif
}
// Don't keep the socket blocking
if (fcntl(m_sock, F_SETFL, flags) == -1) {
std::string errstr(strerror(errno));
throw std::runtime_error("TCP: Could not set O_NONBLOCK: " + errstr);
}
freeaddrinfo(result);
if (rp == nullptr) {
throw runtime_error("Could not connect");
}
}
void TCPSocket::connect(const std::string& hostname, int port, bool nonblock)
{
if (m_sock != INVALID_SOCKET) {
throw std::logic_error("You may only connect an invalid TCPSocket");
}
char service[NI_MAXSERV];
snprintf(service, NI_MAXSERV-1, "%d", port);
/* Obtain address(es) matching host/port */
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
struct addrinfo *result, *rp;
int s = getaddrinfo(hostname.c_str(), service, &hints, &result);
if (s != 0) {
throw runtime_error(string("getaddrinfo failed: ") + gai_strerror(s));
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully connect(2).
If socket(2) (or connect(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != nullptr; rp = rp->ai_next) {
int sfd = ::socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if (nonblock) {
int flags = fcntl(sfd, F_GETFL);
if (flags == -1) {
std::string errstr(strerror(errno));
freeaddrinfo(result);
::close(sfd);
throw std::runtime_error("TCP: Could not get socket flags: " + errstr);
}
if (fcntl(sfd, F_SETFL, flags | O_NONBLOCK) == -1) {
std::string errstr(strerror(errno));
freeaddrinfo(result);
::close(sfd);
throw std::runtime_error("TCP: Could not set O_NONBLOCK: " + errstr);
}
}
int ret = ::connect(sfd, rp->ai_addr, rp->ai_addrlen);
if (ret != -1 or (ret == -1 and errno == EINPROGRESS)) {
m_sock = sfd;
break;
}
::close(sfd);
}
if (m_sock != INVALID_SOCKET) {
#if defined(HAVE_SO_NOSIGPIPE)
int val = 1;
if (setsockopt(m_sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))
== SOCKET_ERROR) {
throw std::runtime_error("Can't set SO_NOSIGPIPE");
}
#endif
}
freeaddrinfo(result); /* No longer needed */
if (rp == nullptr) {
throw runtime_error("Could not connect");
}
}
void TCPSocket::listen(int port, const string& name)
{
if (m_sock != INVALID_SOCKET) {
throw std::logic_error("You may only listen with an invalid TCPSocket");
}
char service[NI_MAXSERV];
snprintf(service, NI_MAXSERV-1, "%d", port);
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0;
hints.ai_canonname = nullptr;
hints.ai_addr = nullptr;
hints.ai_next = nullptr;
struct addrinfo *result, *rp;
int s = getaddrinfo(name.empty() ? nullptr : name.c_str(), service, &hints, &result);
if (s != 0) {
throw runtime_error(string("getaddrinfo failed: ") + gai_strerror(s));
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != nullptr; rp = rp->ai_next) {
int sfd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1) {
continue;
}
int reuse_setting = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse_setting, sizeof(reuse_setting)) == -1) {
throw runtime_error("Can't reuse address");
}
if (::bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {
m_sock = sfd;
break;
}
::close(sfd);
}
freeaddrinfo(result);
if (m_sock != INVALID_SOCKET) {
#if defined(HAVE_SO_NOSIGPIPE)
int val = 1;
if (setsockopt(m_sock, SOL_SOCKET, SO_NOSIGPIPE,
&val, sizeof(val)) < 0) {
throw std::runtime_error("Can't set SO_NOSIGPIPE");
}
#endif
int ret = ::listen(m_sock, 0);
if (ret == -1) {
throw std::runtime_error(string("Could not listen: ") + strerror(errno));
}
}
if (rp == nullptr) {
throw runtime_error("Could not bind");
}
}
void TCPSocket::close()
{
::close(m_sock);
m_sock = -1;
}
TCPSocket TCPSocket::accept(int timeout_ms)
{
if (timeout_ms == 0) {
InetAddress remote_addr;
socklen_t client_len = sizeof(remote_addr.addr);
int sockfd = ::accept(m_sock, remote_addr.as_sockaddr(), &client_len);
TCPSocket s(sockfd, remote_addr);
return s;
}
else {
struct pollfd fds[1];
fds[0].fd = m_sock;
fds[0].events = POLLIN;
int retval = poll(fds, 1, timeout_ms);
if (retval == -1) {
std::string errstr(strerror(errno));
throw std::runtime_error("TCP Socket accept error: " + errstr);
}
else if (retval > 0) {
InetAddress remote_addr;
socklen_t client_len = sizeof(remote_addr.addr);
int sockfd = ::accept(m_sock, remote_addr.as_sockaddr(), &client_len);
TCPSocket s(sockfd, remote_addr);
return s;
}
else {
TCPSocket s(-1);
return s;
}
}
}
ssize_t TCPSocket::sendall(const void *buffer, size_t buflen)
{
uint8_t *buf = (uint8_t*)buffer;
while (buflen > 0) {
/* On Linux, the MSG_NOSIGNAL flag ensures that the process
* would not receive a SIGPIPE and die.
* Other systems have SO_NOSIGPIPE set on the socket for the
* same effect. */
#if defined(HAVE_MSG_NOSIGNAL)
const int flags = MSG_NOSIGNAL;
#else
const int flags = 0;
#endif
ssize_t sent = ::send(m_sock, buf, buflen, flags);
if (sent < 0) {
return -1;
}
else {
buf += sent;
buflen -= sent;
}
}
return buflen;
}
ssize_t TCPSocket::send(const void* data, size_t size, int timeout_ms)
{
if (timeout_ms) {
struct pollfd fds[1];
fds[0].fd = m_sock;
fds[0].events = POLLOUT;
const int retval = poll(fds, 1, timeout_ms);
if (retval == -1) {
throw std::runtime_error(string("TCP Socket send error on poll(): ") + strerror(errno));
}
else if (retval == 0) {
// Timed out
return 0;
}
}
/* On Linux, the MSG_NOSIGNAL flag ensures that the process would not
* receive a SIGPIPE and die.
* Other systems have SO_NOSIGPIPE set on the socket for the same effect. */
#if defined(HAVE_MSG_NOSIGNAL)
const int flags = MSG_NOSIGNAL;
#else
const int flags = 0;
#endif
const ssize_t ret = ::send(m_sock, (const char*)data, size, flags);
if (ret == SOCKET_ERROR) {
throw std::runtime_error(string("TCP Socket send error: ") + strerror(errno));
}
return ret;
}
ssize_t TCPSocket::recv(void *buffer, size_t length, int flags)
{
ssize_t ret = ::recv(m_sock, buffer, length, flags);
if (ret == -1) {
if (errno == EINTR) {
throw Interrupted();
}
else {
std::string errstr(strerror(errno));
throw std::runtime_error("TCP receive error: " + errstr);
}
}
return ret;
}
ssize_t TCPSocket::recv(void *buffer, size_t length, int flags, int timeout_ms)
{
struct pollfd fds[1];
fds[0].fd = m_sock;
fds[0].events = POLLIN;
int retval = poll(fds, 1, timeout_ms);
if (retval == -1 and errno == EINTR) {
throw Interrupted();
}
else if (retval == -1) {
std::string errstr(strerror(errno));
throw std::runtime_error("TCP receive with poll() error: " + errstr);
}
else if (retval > 0 and (fds[0].revents & POLLIN)) {
ssize_t ret = ::recv(m_sock, buffer, length, flags);
if (ret == -1) {
if (errno == ECONNREFUSED) {
return 0;
}
std::string errstr(strerror(errno));
throw std::runtime_error("TCP receive after poll() error: " + errstr);
}
return ret;
}
else {
throw Timeout();
}
}
TCPSocket::TCPSocket(int sockfd) :
m_sock(sockfd),
m_remote_address()
{ }
TCPSocket::TCPSocket(int sockfd, InetAddress remote_address) :
m_sock(sockfd),
m_remote_address(remote_address)
{ }
void TCPClient::connect(const std::string& hostname, int port)
{
m_hostname = hostname;
m_port = port;
reconnect();
}
ssize_t TCPClient::recv(void *buffer, size_t length, int flags, int timeout_ms)
{
try {
ssize_t ret = m_sock.recv(buffer, length, flags, timeout_ms);
if (ret == 0) {
m_sock.close();
reconnect();
}
return ret;
}
catch (const TCPSocket::Interrupted&) {
return -1;
}
catch (const TCPSocket::Timeout&) {
return 0;
}
return 0;
}
void TCPClient::reconnect()
{
TCPSocket newsock;
m_sock = std::move(newsock);
m_sock.connect(m_hostname, m_port, true);
}
TCPConnection::TCPConnection(TCPSocket&& sock) :
queue(),
m_running(true),
m_sender_thread(),
m_sock(move(sock))
{
#if MISSING_OWN_ADDR
auto own_addr = m_sock.getOwnAddress();
auto addr = m_sock.getRemoteAddress();
etiLog.level(debug) << "New TCP Connection on port " <<
own_addr.getPort() << " from " <<
addr.getHostAddress() << ":" << addr.getPort();
#endif
m_sender_thread = std::thread(&TCPConnection::process, this);
}
TCPConnection::~TCPConnection()
{
m_running = false;
vector termination_marker;
queue.push(termination_marker);
if (m_sender_thread.joinable()) {
m_sender_thread.join();
}
}
void TCPConnection::process()
{
while (m_running) {
vector data;
queue.wait_and_pop(data);
if (data.empty()) {
// empty vector is the termination marker
m_running = false;
break;
}
try {
ssize_t remaining = data.size();
const uint8_t *buf = reinterpret_cast