aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--src/ConfigParser.cpp477
-rw-r--r--src/ConfigParser.h88
-rw-r--r--src/DabMod.cpp470
-rw-r--r--src/Utils.cpp2
-rw-r--r--src/Utils.h2
6 files changed, 575 insertions, 466 deletions
diff --git a/Makefile.am b/Makefile.am
index 96e2ff3..f4e8e00 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -50,6 +50,8 @@ odr_dabmod_SOURCES = src/DabMod.cpp \
src/DabModulator.h \
src/Buffer.cpp \
src/Buffer.h \
+ src/ConfigParser.cpp \
+ src/ConfigParser.h \
src/ModPlugin.cpp \
src/ModPlugin.h \
src/EtiReader.cpp \
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
new file mode 100644
index 0000000..c6eb329
--- /dev/null
+++ b/src/ConfigParser.cpp
@@ -0,0 +1,477 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+ Her Majesty the Queen in Right of Canada (Communications Research
+ Center Canada)
+
+ Copyright (C) 2017
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "ConfigParser.h"
+#include "porting.h"
+#include "Utils.h"
+#include "Log.h"
+#include "DabModulator.h"
+
+#include <unistd.h>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/ini_parser.hpp>
+
+using namespace std;
+
+static GainMode parse_gainmode(const std::string &gainMode_setting)
+{
+ string gainMode_minuscule(gainMode_setting);
+ std::transform(gainMode_minuscule.begin(), gainMode_minuscule.end(),
+ gainMode_minuscule.begin(), ::tolower);
+
+ if (gainMode_minuscule == "0" or gainMode_minuscule == "fix") {
+ return GainMode::GAIN_FIX;
+ }
+ else if (gainMode_minuscule == "1" or gainMode_minuscule == "max") {
+ return GainMode::GAIN_MAX;
+ }
+ else if (gainMode_minuscule == "2" or gainMode_minuscule == "var") {
+ return GainMode::GAIN_VAR;
+ }
+
+ cerr << "Modulator gainmode setting '" << gainMode_setting <<
+ "' not recognised." << endl;
+ throw std::runtime_error("Configuration error");
+}
+
+static void parse_configfile(
+ const std::string& configuration_file,
+ mod_settings_t& mod_settings)
+{
+ // First read parameters from the file
+ using boost::property_tree::ptree;
+ ptree pt;
+
+ try {
+ read_ini(configuration_file, pt);
+ }
+ catch (boost::property_tree::ini_parser::ini_parser_error &e)
+ {
+ std::cerr << "Error, cannot read configuration file '" << configuration_file.c_str() << "'" << std::endl;
+ std::cerr << " " << e.what() << std::endl;
+ throw std::runtime_error("Cannot read configuration file");
+ }
+
+ // remote controller:
+ if (pt.get("remotecontrol.telnet", 0) == 1) {
+ try {
+ int telnetport = pt.get<int>("remotecontrol.telnetport");
+ auto telnetrc = make_shared<RemoteControllerTelnet>(telnetport);
+ rcs.add_controller(telnetrc);
+ }
+ catch (std::exception &e) {
+ std::cerr << "Error: " << e.what() << "\n";
+ std::cerr << " telnet remote control enabled, but no telnetport defined.\n";
+ throw std::runtime_error("Configuration error");
+ }
+ }
+
+#if defined(HAVE_ZEROMQ)
+ if (pt.get("remotecontrol.zmqctrl", 0) == 1) {
+ try {
+ std::string zmqCtrlEndpoint = pt.get("remotecontrol.zmqctrlendpoint", "");
+ std::cerr << "ZmqCtrlEndpoint: " << zmqCtrlEndpoint << std::endl;
+ auto zmqrc = make_shared<RemoteControllerZmq>(zmqCtrlEndpoint);
+ rcs.add_controller(zmqrc);
+ }
+ catch (std::exception &e) {
+ std::cerr << "Error: " << e.what() << "\n";
+ std::cerr << " zmq remote control enabled, but no endpoint defined.\n";
+ throw std::runtime_error("Configuration error");
+ }
+ }
+#endif
+
+ // input params:
+ if (pt.get("input.loop", 0) == 1) {
+ mod_settings.loop = true;
+ }
+
+ mod_settings.inputTransport = pt.get("input.transport", "file");
+ mod_settings.inputMaxFramesQueued = pt.get("input.max_frames_queued",
+ ZMQ_INPUT_MAX_FRAME_QUEUE);
+
+ mod_settings.edi_max_delay_ms = pt.get("input.edi_max_delay", 0.0f);
+
+ mod_settings.inputName = pt.get("input.source", "/dev/stdin");
+
+ // log parameters:
+ if (pt.get("log.syslog", 0) == 1) {
+ LogToSyslog* log_syslog = new LogToSyslog();
+ etiLog.register_backend(log_syslog);
+ }
+
+ if (pt.get("log.filelog", 0) == 1) {
+ std::string logfilename;
+ try {
+ logfilename = pt.get<std::string>("log.filename");
+ }
+ catch (std::exception &e) {
+ std::cerr << "Error: " << e.what() << "\n";
+ std::cerr << " Configuration enables file log, but does not specify log filename\n";
+ throw std::runtime_error("Configuration error");
+ }
+
+ LogToFile* log_file = new LogToFile(logfilename);
+ etiLog.register_backend(log_file);
+ }
+
+ auto trace_filename = pt.get<std::string>("log.trace", "");
+ if (not trace_filename.empty()) {
+ LogTracer* tracer = new LogTracer(trace_filename);
+ etiLog.register_backend(tracer);
+ }
+
+
+ // modulator parameters:
+ const string gainMode_setting = pt.get("modulator.gainmode", "var");
+ mod_settings.gainMode = parse_gainmode(gainMode_setting);
+
+ mod_settings.dabMode = pt.get("modulator.mode", mod_settings.dabMode);
+ mod_settings.clockRate = pt.get("modulator.dac_clk_rate", (size_t)0);
+ mod_settings.digitalgain = pt.get("modulator.digital_gain", mod_settings.digitalgain);
+ mod_settings.outputRate = pt.get("modulator.rate", mod_settings.outputRate);
+
+ // FIR Filter parameters:
+ if (pt.get("firfilter.enabled", 0) == 1) {
+ mod_settings.filterTapsFilename =
+ pt.get<std::string>("firfilter.filtertapsfile", "default");
+ }
+
+ // Output options
+ std::string output_selected;
+ try {
+ output_selected = pt.get<std::string>("output.output");
+ }
+ catch (std::exception &e) {
+ std::cerr << "Error: " << e.what() << "\n";
+ std::cerr << " Configuration does not specify output\n";
+ throw std::runtime_error("Configuration error");
+ }
+
+ if (output_selected == "file") {
+ try {
+ mod_settings.outputName = pt.get<std::string>("fileoutput.filename");
+ }
+ catch (std::exception &e) {
+ std::cerr << "Error: " << e.what() << "\n";
+ std::cerr << " Configuration does not specify file name for file output\n";
+ throw std::runtime_error("Configuration error");
+ }
+ mod_settings.useFileOutput = 1;
+
+ mod_settings.fileOutputFormat = pt.get("fileoutput.format", mod_settings.fileOutputFormat);
+ }
+#if defined(HAVE_OUTPUT_UHD)
+ else if (output_selected == "uhd") {
+ OutputUHDConfig outputuhd_conf;
+
+ outputuhd_conf.device = pt.get("uhdoutput.device", "");
+ outputuhd_conf.usrpType = pt.get("uhdoutput.type", "");
+ outputuhd_conf.subDevice = pt.get("uhdoutput.subdevice", "");
+ outputuhd_conf.masterClockRate = pt.get<long>("uhdoutput.master_clock_rate", 0);
+
+ if (outputuhd_conf.device.find("master_clock_rate") != std::string::npos) {
+ std::cerr << "Warning:"
+ "setting master_clock_rate in [uhd] device is deprecated !\n";
+ }
+
+ if (outputuhd_conf.device.find("type=") != std::string::npos) {
+ std::cerr << "Warning:"
+ "setting type in [uhd] device is deprecated !\n";
+ }
+
+ outputuhd_conf.txgain = pt.get("uhdoutput.txgain", 0.0);
+ outputuhd_conf.frequency = pt.get<double>("uhdoutput.frequency", 0);
+ std::string chan = pt.get<std::string>("uhdoutput.channel", "");
+ outputuhd_conf.dabMode = mod_settings.dabMode;
+
+ if (outputuhd_conf.frequency == 0 && chan == "") {
+ std::cerr << " UHD output enabled, but neither frequency nor channel defined.\n";
+ throw std::runtime_error("Configuration error");
+ }
+ else if (outputuhd_conf.frequency == 0) {
+ outputuhd_conf.frequency = parseChannel(chan);
+ }
+ else if (outputuhd_conf.frequency != 0 && chan != "") {
+ std::cerr << " UHD output: cannot define both frequency and channel.\n";
+ throw std::runtime_error("Configuration error");
+ }
+
+
+ outputuhd_conf.refclk_src = pt.get("uhdoutput.refclk_source", "internal");
+ outputuhd_conf.pps_src = pt.get("uhdoutput.pps_source", "none");
+ outputuhd_conf.pps_polarity = pt.get("uhdoutput.pps_polarity", "pos");
+
+ std::string behave = pt.get("uhdoutput.behaviour_refclk_lock_lost", "ignore");
+
+ if (behave == "crash") {
+ outputuhd_conf.refclk_lock_loss_behaviour = CRASH;
+ }
+ else if (behave == "ignore") {
+ outputuhd_conf.refclk_lock_loss_behaviour = IGNORE;
+ }
+ else {
+ std::cerr << "Error: UHD output: behaviour_refclk_lock_lost invalid." << std::endl;
+ throw std::runtime_error("Configuration error");
+ }
+
+ outputuhd_conf.maxGPSHoldoverTime = pt.get("uhdoutput.max_gps_holdover_time", 0);
+
+ mod_settings.outputuhd_conf = outputuhd_conf;
+ mod_settings.useUHDOutput = 1;
+ }
+#endif
+#if defined(HAVE_SOAPYSDR)
+ else if (output_selected == "soapysdr") {
+ auto& outputsoapy_conf = mod_settings.outputsoapy_conf;
+ outputsoapy_conf.device = pt.get("soapyoutput.device", "");
+ outputsoapy_conf.masterClockRate = pt.get<long>("soapyoutput.master_clock_rate", 0);
+
+ outputsoapy_conf.txgain = pt.get("soapyoutput.txgain", 0.0);
+ outputsoapy_conf.frequency = pt.get<double>("soapyoutput.frequency", 0);
+ std::string chan = pt.get<std::string>("soapyoutput.channel", "");
+ outputsoapy_conf.dabMode = mod_settings.dabMode;
+
+ if (outputsoapy_conf.frequency == 0 && chan == "") {
+ std::cerr << " soapy output enabled, but neither frequency nor channel defined.\n";
+ throw std::runtime_error("Configuration error");
+ }
+ else if (outputsoapy_conf.frequency == 0) {
+ outputsoapy_conf.frequency = parseChannel(chan);
+ }
+ else if (outputsoapy_conf.frequency != 0 && chan != "") {
+ std::cerr << " soapy output: cannot define both frequency and channel.\n";
+ throw std::runtime_error("Configuration error");
+ }
+
+ mod_settings.useSoapyOutput = 1;
+ }
+#endif
+#if defined(HAVE_ZEROMQ)
+ else if (output_selected == "zmq") {
+ mod_settings.outputName = pt.get<std::string>("zmqoutput.listen");
+ mod_settings.zmqOutputSocketType = pt.get<std::string>("zmqoutput.socket_type");
+ mod_settings.useZeroMQOutput = 1;
+ }
+#endif
+ else {
+ std::cerr << "Error: Invalid output defined.\n";
+ throw std::runtime_error("Configuration error");
+ }
+
+#if defined(HAVE_OUTPUT_UHD)
+ mod_settings.outputuhd_conf.enableSync = (pt.get("delaymanagement.synchronous", 0) == 1);
+ mod_settings.outputuhd_conf.muteNoTimestamps = (pt.get("delaymanagement.mutenotimestamps", 0) == 1);
+ if (mod_settings.outputuhd_conf.enableSync) {
+ std::string delay_mgmt = pt.get<std::string>("delaymanagement.management", "");
+ std::string fixedoffset = pt.get<std::string>("delaymanagement.fixedoffset", "");
+ std::string offset_filename = pt.get<std::string>("delaymanagement.dynamicoffsetfile", "");
+
+ if (not(delay_mgmt.empty() and fixedoffset.empty() and offset_filename.empty())) {
+ std::cerr << "Warning: you are using the old config syntax for the offset management.\n";
+ std::cerr << " Please see the example.ini configuration for the new settings.\n";
+ }
+
+ try {
+ mod_settings.tist_offset_s = pt.get<double>("delaymanagement.offset");
+ }
+ catch (std::exception &e) {
+ std::cerr << "Error: delaymanagement: synchronous is enabled, but no offset defined!\n";
+ throw std::runtime_error("Configuration error");
+ }
+ }
+
+#endif
+
+ /* Read TII parameters from config file */
+ mod_settings.tiiConfig.enable = pt.get("tii.enable", 0);
+ mod_settings.tiiConfig.comb = pt.get("tii.comb", 0);
+ mod_settings.tiiConfig.pattern = pt.get("tii.pattern", 0);
+ mod_settings.tiiConfig.old_variant = pt.get("tii.old_variant", 0);
+}
+
+
+void parse_args(int argc, char **argv, mod_settings_t& mod_settings)
+{
+ bool use_configuration_cmdline = false;
+ bool use_configuration_file = false;
+ std::string configuration_file;
+
+ // No argument given ? You can't be serious ! Show usage.
+ if (argc == 1) {
+ printUsage(argv[0]);
+ throw std::invalid_argument("Invalid command line options");
+ }
+
+ while (true) {
+ int c = getopt(argc, argv, "a:C:c:f:F:g:G:hlm:o:O:r:T:u:V");
+ if (c == -1) {
+ break;
+ }
+
+ if (c != 'C') {
+ use_configuration_cmdline = true;
+ }
+
+ switch (c) {
+ case 'C':
+ use_configuration_file = true;
+ configuration_file = optarg;
+ break;
+
+ case 'a':
+ mod_settings.digitalgain = strtof(optarg, NULL);
+ break;
+ case 'c':
+ mod_settings.clockRate = strtol(optarg, NULL, 0);
+ break;
+ case 'f':
+#if defined(HAVE_OUTPUT_UHD)
+ if (mod_settings.useUHDOutput) {
+ fprintf(stderr, "Options -u and -f are mutually exclusive\n");
+ throw std::invalid_argument("Invalid command line options");
+ }
+#endif
+ mod_settings.outputName = optarg;
+ mod_settings.useFileOutput = 1;
+ break;
+ case 'F':
+#if defined(HAVE_OUTPUT_UHD)
+ mod_settings.outputuhd_conf.frequency = strtof(optarg, NULL);
+#endif
+ break;
+ case 'g':
+ mod_settings.gainMode = parse_gainmode(optarg);
+ break;
+ case 'G':
+#if defined(HAVE_OUTPUT_UHD)
+ mod_settings.outputuhd_conf.txgain = strtod(optarg, NULL);
+#endif
+ break;
+ case 'l':
+ mod_settings.loop = true;
+ break;
+ case 'o':
+ mod_settings.tist_offset_s = strtod(optarg, NULL);
+#if defined(HAVE_OUTPUT_UHD)
+ mod_settings.outputuhd_conf.enableSync = true;
+#endif
+ break;
+ case 'm':
+ mod_settings.dabMode = strtol(optarg, NULL, 0);
+ break;
+ case 'r':
+ mod_settings.outputRate = strtol(optarg, NULL, 0);
+ break;
+ case 'T':
+ mod_settings.filterTapsFilename = optarg;
+ break;
+ case 'u':
+#if defined(HAVE_OUTPUT_UHD)
+ if (mod_settings.useFileOutput) {
+ fprintf(stderr, "Options -u and -f are mutually exclusive\n");
+ throw std::invalid_argument("Invalid command line options");
+ }
+ mod_settings.outputuhd_conf.device = optarg;
+ mod_settings.outputuhd_conf.refclk_src = "internal";
+ mod_settings.outputuhd_conf.pps_src = "none";
+ mod_settings.outputuhd_conf.pps_polarity = "pos";
+ mod_settings.useUHDOutput = 1;
+#endif
+ break;
+ case 'V':
+ printVersion();
+ throw std::invalid_argument("");
+ break;
+ case '?':
+ case 'h':
+ printUsage(argv[0]);
+ throw std::invalid_argument("");
+ break;
+ default:
+ fprintf(stderr, "Option '%c' not coded yet!\n", c);
+ throw std::invalid_argument("Invalid command line options");
+ }
+ }
+
+ // If only one argument is given, interpret as configuration file name
+ if (argc == 2) {
+ use_configuration_file = true;
+ configuration_file = argv[1];
+ }
+
+ if (use_configuration_file && use_configuration_cmdline) {
+ fprintf(stderr, "Warning: configuration file and command "
+ "line parameters are defined:\n\t"
+ "Command line parameters override settings "
+ "in the configuration file !\n");
+ }
+
+ // Setting ETI input filename
+ if (use_configuration_cmdline && mod_settings.inputName == "") {
+ if (optind < argc) {
+ mod_settings.inputName = argv[optind++];
+
+ if (mod_settings.inputName.substr(0, 4) == "zmq+" &&
+ mod_settings.inputName.find("://") != std::string::npos) {
+ // if the name starts with zmq+XYZ://somewhere:port
+ mod_settings.inputTransport = "zeromq";
+ }
+ else if (mod_settings.inputName.substr(0, 6) == "tcp://") {
+ mod_settings.inputTransport = "tcp";
+ }
+ else if (mod_settings.inputName.substr(0, 6) == "udp://") {
+ mod_settings.inputTransport = "edi";
+ }
+ }
+ else {
+ mod_settings.inputName = "/dev/stdin";
+ }
+ }
+
+ // Checking unused arguments
+ if (use_configuration_cmdline && optind != argc) {
+ fprintf(stderr, "Invalid arguments:");
+ while (optind != argc) {
+ fprintf(stderr, " %s", argv[optind++]);
+ }
+ fprintf(stderr, "\n");
+ printUsage(argv[0]);
+ etiLog.level(error) << "Received invalid command line arguments";
+ throw std::invalid_argument("Invalid command line options");
+ }
+
+ if (use_configuration_file) {
+ parse_configfile(configuration_file, mod_settings);
+ }
+}
+
diff --git a/src/ConfigParser.h b/src/ConfigParser.h
new file mode 100644
index 0000000..fe48f01
--- /dev/null
+++ b/src/ConfigParser.h
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
+ Her Majesty the Queen in Right of Canada (Communications Research
+ Center Canada)
+
+ Copyright (C) 2017
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string>
+#include "GainControl.h"
+#include "TII.h"
+#if defined(HAVE_OUTPUT_UHD)
+# include "OutputUHD.h"
+#endif
+#if defined(HAVE_SOAPYSDR)
+# include "OutputSoapy.h"
+#endif
+
+#define ZMQ_INPUT_MAX_FRAME_QUEUE 500
+
+struct mod_settings_t {
+ std::string outputName;
+ int useZeroMQOutput = 0;
+ std::string zmqOutputSocketType = "";
+ int useFileOutput = 0;
+ std::string fileOutputFormat = "complexf";
+ int useUHDOutput = 0;
+ int useSoapyOutput = 0;
+
+ size_t outputRate = 2048000;
+ size_t clockRate = 0;
+ unsigned dabMode = 0;
+ float digitalgain = 1.0f;
+ float normalise = 1.0f;
+ GainMode gainMode = GainMode::GAIN_VAR;
+
+ // To handle the timestamp offset of the modulator
+ unsigned tist_delay_stages = 0;
+ double tist_offset_s = 0.0;
+
+ bool loop = false;
+ std::string inputName = "";
+ std::string inputTransport = "file";
+ unsigned inputMaxFramesQueued = ZMQ_INPUT_MAX_FRAME_QUEUE;
+ float edi_max_delay_ms = 0.0f;
+
+ tii_config_t tiiConfig;
+
+ std::string filterTapsFilename = "";
+
+
+#if defined(HAVE_OUTPUT_UHD)
+ OutputUHDConfig outputuhd_conf;
+#endif
+
+#if defined(HAVE_SOAPYSDR)
+ OutputSoapyConfig outputsoapy_conf;
+#endif
+
+};
+
+void parse_args(int argc, char **argv, mod_settings_t& mod_settings);
+
diff --git a/src/DabMod.cpp b/src/DabMod.cpp
index 7d6cf8f..a298fbe 100644
--- a/src/DabMod.cpp
+++ b/src/DabMod.cpp
@@ -48,14 +48,12 @@
#include "TimestampDecoder.h"
#include "FIRFilter.h"
#include "RemoteControl.h"
+#include "ConfigParser.h"
#include <memory>
-#include <boost/property_tree/ptree.hpp>
-#include <boost/property_tree/ini_parser.hpp>
#include <complex>
#include <string>
#include <stdlib.h>
-#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -72,8 +70,6 @@
# define memalign(a, b) malloc(b)
#endif
-#define ZMQ_INPUT_MAX_FRAME_QUEUE 500
-
/* UHD requires the input I and Q samples to be in the interval
* [-1.0,1.0], otherwise they get truncated, which creates very
@@ -125,56 +121,6 @@ enum class run_modulator_state_t {
run_modulator_state_t run_modulator(modulator_data& m);
-static GainMode parse_gainmode(const std::string &gainMode_setting)
-{
- string gainMode_minuscule(gainMode_setting);
- std::transform(gainMode_minuscule.begin(), gainMode_minuscule.end(), gainMode_minuscule.begin(), ::tolower);
-
- if (gainMode_minuscule == "0" or gainMode_minuscule == "fix") { return GainMode::GAIN_FIX; }
- else if (gainMode_minuscule == "1" or gainMode_minuscule == "max") { return GainMode::GAIN_MAX; }
- else if (gainMode_minuscule == "2" or gainMode_minuscule == "var") { return GainMode::GAIN_VAR; }
-
- cerr << "Modulator gainmode setting '" << gainMode_setting << "' not recognised." << endl;
- throw std::runtime_error("Configuration error");
-}
-
-struct mod_settings_t {
- std::string outputName;
- int useZeroMQOutput = 0;
- std::string zmqOutputSocketType = "";
- int useFileOutput = 0;
- std::string fileOutputFormat = "complexf";
- int useUHDOutput = 0;
- int useSoapyOutput = 0;
-
- size_t outputRate = 2048000;
- size_t clockRate = 0;
- unsigned dabMode = 0;
- float digitalgain = 1.0f;
- float normalise = 1.0f;
- GainMode gainMode = GainMode::GAIN_VAR;
-
- bool loop = false;
- std::string inputName = "";
- std::string inputTransport = "file";
- unsigned inputMaxFramesQueued = ZMQ_INPUT_MAX_FRAME_QUEUE;
- float edi_max_delay_ms = 0.0f;
-
- tii_config_t tiiConfig;
-
- std::string filterTapsFilename = "";
-
-
-#if defined(HAVE_OUTPUT_UHD)
- OutputUHDConfig outputuhd_conf;
-#endif
-
-#if defined(HAVE_SOAPYSDR)
- OutputSoapyConfig outputsoapy_conf;
-#endif
-
-};
-
static shared_ptr<ModOutput> prepare_output(
mod_settings_t& s)
{
@@ -232,18 +178,9 @@ static shared_ptr<ModOutput> prepare_output(
int launch_modulator(int argc, char* argv[])
{
int ret = 0;
- mod_settings_t mod_settings;
-
- // Two configuration sources exist: command line and (new) INI file
- bool use_configuration_cmdline = false;
- bool use_configuration_file = false;
- std::string configuration_file;
modulator_data m;
- // To handle the timestamp offset of the modulator
- unsigned tist_delay_stages = 0;
- double tist_offset_s = 0.0;
auto flowgraph = make_shared<Flowgraph>();
shared_ptr<FormatConverter> format_converter;
@@ -273,97 +210,8 @@ int launch_modulator(int argc, char* argv[])
setenv("TZ", "", 1);
tzset();
- while (true) {
- int c = getopt(argc, argv, "a:C:c:f:F:g:G:hlm:o:O:r:T:u:V");
- if (c == -1) {
- break;
- }
-
- if (c != 'C') {
- use_configuration_cmdline = true;
- }
-
- switch (c) {
- case 'C':
- use_configuration_file = true;
- configuration_file = optarg;
- break;
-
- case 'a':
- mod_settings.digitalgain = strtof(optarg, NULL);
- break;
- case 'c':
- mod_settings.clockRate = strtol(optarg, NULL, 0);
- break;
- case 'f':
-#if defined(HAVE_OUTPUT_UHD)
- if (mod_settings.useUHDOutput) {
- fprintf(stderr, "Options -u and -f are mutually exclusive\n");
- throw std::invalid_argument("Invalid command line options");
- }
-#endif
- mod_settings.outputName = optarg;
- mod_settings.useFileOutput = 1;
- break;
- case 'F':
-#if defined(HAVE_OUTPUT_UHD)
- mod_settings.outputuhd_conf.frequency = strtof(optarg, NULL);
-#endif
- break;
- case 'g':
- mod_settings.gainMode = parse_gainmode(optarg);
- break;
- case 'G':
-#if defined(HAVE_OUTPUT_UHD)
- mod_settings.outputuhd_conf.txgain = strtod(optarg, NULL);
-#endif
- break;
- case 'l':
- mod_settings.loop = true;
- break;
- case 'o':
- tist_offset_s = strtod(optarg, NULL);
-#if defined(HAVE_OUTPUT_UHD)
- mod_settings.outputuhd_conf.enableSync = true;
-#endif
- break;
- case 'm':
- mod_settings.dabMode = strtol(optarg, NULL, 0);
- break;
- case 'r':
- mod_settings.outputRate = strtol(optarg, NULL, 0);
- break;
- case 'T':
- mod_settings.filterTapsFilename = optarg;
- break;
- case 'u':
-#if defined(HAVE_OUTPUT_UHD)
- if (mod_settings.useFileOutput) {
- fprintf(stderr, "Options -u and -f are mutually exclusive\n");
- throw std::invalid_argument("Invalid command line options");
- }
- mod_settings.outputuhd_conf.device = optarg;
- mod_settings.outputuhd_conf.refclk_src = "internal";
- mod_settings.outputuhd_conf.pps_src = "none";
- mod_settings.outputuhd_conf.pps_polarity = "pos";
- mod_settings.useUHDOutput = 1;
-#endif
- break;
- case 'V':
- printVersion();
- throw std::invalid_argument("");
- break;
- case '?':
- case 'h':
- printUsage(argv[0]);
- throw std::invalid_argument("");
- break;
- default:
- fprintf(stderr, "Option '%c' not coded yet!\n", c);
- ret = -1;
- throw std::invalid_argument("Invalid command line options");
- }
- }
+ mod_settings_t mod_settings;
+ parse_args(argc, argv, mod_settings);
std::cerr << "ODR-DabMod version " <<
#if defined(GITVERSION)
@@ -388,277 +236,6 @@ int launch_modulator(int argc, char* argv[])
#endif
"\n";
- if (use_configuration_file && use_configuration_cmdline) {
- fprintf(stderr, "Warning: configuration file and command line parameters are defined:\n\t"
- "Command line parameters override settings in the configuration file !\n");
- }
-
- // No argument given ? You can't be serious ! Show usage.
- if (argc == 1) {
- printUsage(argv[0]);
- throw std::invalid_argument("Invalid command line options");
- }
-
- // If only one argument is given, interpret as configuration file name
- if (argc == 2) {
- use_configuration_file = true;
- configuration_file = argv[1];
- }
-
- if (use_configuration_file) {
- // First read parameters from the file
- using boost::property_tree::ptree;
- ptree pt;
-
- try {
- read_ini(configuration_file, pt);
- }
- catch (boost::property_tree::ini_parser::ini_parser_error &e)
- {
- std::cerr << "Error, cannot read configuration file '" << configuration_file.c_str() << "'" << std::endl;
- std::cerr << " " << e.what() << std::endl;
- throw std::runtime_error("Cannot read configuration file");
- }
-
- // remote controller:
- if (pt.get("remotecontrol.telnet", 0) == 1) {
- try {
- int telnetport = pt.get<int>("remotecontrol.telnetport");
- auto telnetrc = make_shared<RemoteControllerTelnet>(telnetport);
- rcs.add_controller(telnetrc);
- }
- catch (std::exception &e) {
- std::cerr << "Error: " << e.what() << "\n";
- std::cerr << " telnet remote control enabled, but no telnetport defined.\n";
- throw std::runtime_error("Configuration error");
- }
- }
-
-#if defined(HAVE_ZEROMQ)
- if (pt.get("remotecontrol.zmqctrl", 0) == 1) {
- try {
- std::string zmqCtrlEndpoint = pt.get("remotecontrol.zmqctrlendpoint", "");
- std::cerr << "ZmqCtrlEndpoint: " << zmqCtrlEndpoint << std::endl;
- auto zmqrc = make_shared<RemoteControllerZmq>(zmqCtrlEndpoint);
- rcs.add_controller(zmqrc);
- }
- catch (std::exception &e) {
- std::cerr << "Error: " << e.what() << "\n";
- std::cerr << " zmq remote control enabled, but no endpoint defined.\n";
- throw std::runtime_error("Configuration error");
- }
- }
-#endif
-
- // input params:
- if (pt.get("input.loop", 0) == 1) {
- mod_settings.loop = true;
- }
-
- mod_settings.inputTransport = pt.get("input.transport", "file");
- mod_settings.inputMaxFramesQueued = pt.get("input.max_frames_queued",
- ZMQ_INPUT_MAX_FRAME_QUEUE);
-
- mod_settings.edi_max_delay_ms = pt.get("input.edi_max_delay", 0.0f);
-
- mod_settings.inputName = pt.get("input.source", "/dev/stdin");
-
- // log parameters:
- if (pt.get("log.syslog", 0) == 1) {
- LogToSyslog* log_syslog = new LogToSyslog();
- etiLog.register_backend(log_syslog);
- }
-
- if (pt.get("log.filelog", 0) == 1) {
- std::string logfilename;
- try {
- logfilename = pt.get<std::string>("log.filename");
- }
- catch (std::exception &e) {
- std::cerr << "Error: " << e.what() << "\n";
- std::cerr << " Configuration enables file log, but does not specify log filename\n";
- throw std::runtime_error("Configuration error");
- }
-
- LogToFile* log_file = new LogToFile(logfilename);
- etiLog.register_backend(log_file);
- }
-
- auto trace_filename = pt.get<std::string>("log.trace", "");
- if (not trace_filename.empty()) {
- LogTracer* tracer = new LogTracer(trace_filename);
- etiLog.register_backend(tracer);
- }
-
-
- // modulator parameters:
- const string gainMode_setting = pt.get("modulator.gainmode", "var");
- mod_settings.gainMode = parse_gainmode(gainMode_setting);
-
- mod_settings.dabMode = pt.get("modulator.mode", mod_settings.dabMode);
- mod_settings.clockRate = pt.get("modulator.dac_clk_rate", (size_t)0);
- mod_settings.digitalgain = pt.get("modulator.digital_gain", mod_settings.digitalgain);
- mod_settings.outputRate = pt.get("modulator.rate", mod_settings.outputRate);
-
- // FIR Filter parameters:
- if (pt.get("firfilter.enabled", 0) == 1) {
- mod_settings.filterTapsFilename =
- pt.get<std::string>("firfilter.filtertapsfile", "default");
- }
-
- // Output options
- std::string output_selected;
- try {
- output_selected = pt.get<std::string>("output.output");
- }
- catch (std::exception &e) {
- std::cerr << "Error: " << e.what() << "\n";
- std::cerr << " Configuration does not specify output\n";
- throw std::runtime_error("Configuration error");
- }
-
- if (output_selected == "file") {
- try {
- mod_settings.outputName = pt.get<std::string>("fileoutput.filename");
- }
- catch (std::exception &e) {
- std::cerr << "Error: " << e.what() << "\n";
- std::cerr << " Configuration does not specify file name for file output\n";
- throw std::runtime_error("Configuration error");
- }
- mod_settings.useFileOutput = 1;
-
- mod_settings.fileOutputFormat = pt.get("fileoutput.format", mod_settings.fileOutputFormat);
- }
-#if defined(HAVE_OUTPUT_UHD)
- else if (output_selected == "uhd") {
- OutputUHDConfig outputuhd_conf;
-
- outputuhd_conf.device = pt.get("uhdoutput.device", "");
- outputuhd_conf.usrpType = pt.get("uhdoutput.type", "");
- outputuhd_conf.subDevice = pt.get("uhdoutput.subdevice", "");
- outputuhd_conf.masterClockRate = pt.get<long>("uhdoutput.master_clock_rate", 0);
-
- if (outputuhd_conf.device.find("master_clock_rate") != std::string::npos) {
- std::cerr << "Warning:"
- "setting master_clock_rate in [uhd] device is deprecated !\n";
- }
-
- if (outputuhd_conf.device.find("type=") != std::string::npos) {
- std::cerr << "Warning:"
- "setting type in [uhd] device is deprecated !\n";
- }
-
- outputuhd_conf.txgain = pt.get("uhdoutput.txgain", 0.0);
- outputuhd_conf.frequency = pt.get<double>("uhdoutput.frequency", 0);
- std::string chan = pt.get<std::string>("uhdoutput.channel", "");
- outputuhd_conf.dabMode = mod_settings.dabMode;
-
- if (outputuhd_conf.frequency == 0 && chan == "") {
- std::cerr << " UHD output enabled, but neither frequency nor channel defined.\n";
- throw std::runtime_error("Configuration error");
- }
- else if (outputuhd_conf.frequency == 0) {
- outputuhd_conf.frequency = parseChannel(chan);
- }
- else if (outputuhd_conf.frequency != 0 && chan != "") {
- std::cerr << " UHD output: cannot define both frequency and channel.\n";
- throw std::runtime_error("Configuration error");
- }
-
-
- outputuhd_conf.refclk_src = pt.get("uhdoutput.refclk_source", "internal");
- outputuhd_conf.pps_src = pt.get("uhdoutput.pps_source", "none");
- outputuhd_conf.pps_polarity = pt.get("uhdoutput.pps_polarity", "pos");
-
- std::string behave = pt.get("uhdoutput.behaviour_refclk_lock_lost", "ignore");
-
- if (behave == "crash") {
- outputuhd_conf.refclk_lock_loss_behaviour = CRASH;
- }
- else if (behave == "ignore") {
- outputuhd_conf.refclk_lock_loss_behaviour = IGNORE;
- }
- else {
- std::cerr << "Error: UHD output: behaviour_refclk_lock_lost invalid." << std::endl;
- throw std::runtime_error("Configuration error");
- }
-
- outputuhd_conf.maxGPSHoldoverTime = pt.get("uhdoutput.max_gps_holdover_time", 0);
-
- mod_settings.outputuhd_conf = outputuhd_conf;
- mod_settings.useUHDOutput = 1;
- }
-#endif
-#if defined(HAVE_SOAPYSDR)
- else if (output_selected == "soapysdr") {
- auto& outputsoapy_conf = mod_settings.outputsoapy_conf;
- outputsoapy_conf.device = pt.get("soapyoutput.device", "");
- outputsoapy_conf.masterClockRate = pt.get<long>("soapyoutput.master_clock_rate", 0);
-
- outputsoapy_conf.txgain = pt.get("soapyoutput.txgain", 0.0);
- outputsoapy_conf.frequency = pt.get<double>("soapyoutput.frequency", 0);
- std::string chan = pt.get<std::string>("soapyoutput.channel", "");
- outputsoapy_conf.dabMode = mod_settings.dabMode;
-
- if (outputsoapy_conf.frequency == 0 && chan == "") {
- std::cerr << " soapy output enabled, but neither frequency nor channel defined.\n";
- throw std::runtime_error("Configuration error");
- }
- else if (outputsoapy_conf.frequency == 0) {
- outputsoapy_conf.frequency = parseChannel(chan);
- }
- else if (outputsoapy_conf.frequency != 0 && chan != "") {
- std::cerr << " soapy output: cannot define both frequency and channel.\n";
- throw std::runtime_error("Configuration error");
- }
-
- mod_settings.useSoapyOutput = 1;
- }
-#endif
-#if defined(HAVE_ZEROMQ)
- else if (output_selected == "zmq") {
- mod_settings.outputName = pt.get<std::string>("zmqoutput.listen");
- mod_settings.zmqOutputSocketType = pt.get<std::string>("zmqoutput.socket_type");
- mod_settings.useZeroMQOutput = 1;
- }
-#endif
- else {
- std::cerr << "Error: Invalid output defined.\n";
- throw std::runtime_error("Configuration error");
- }
-
-#if defined(HAVE_OUTPUT_UHD)
- mod_settings.outputuhd_conf.enableSync = (pt.get("delaymanagement.synchronous", 0) == 1);
- mod_settings.outputuhd_conf.muteNoTimestamps = (pt.get("delaymanagement.mutenotimestamps", 0) == 1);
- if (mod_settings.outputuhd_conf.enableSync) {
- std::string delay_mgmt = pt.get<std::string>("delaymanagement.management", "");
- std::string fixedoffset = pt.get<std::string>("delaymanagement.fixedoffset", "");
- std::string offset_filename = pt.get<std::string>("delaymanagement.dynamicoffsetfile", "");
-
- if (not(delay_mgmt.empty() and fixedoffset.empty() and offset_filename.empty())) {
- std::cerr << "Warning: you are using the old config syntax for the offset management.\n";
- std::cerr << " Please see the example.ini configuration for the new settings.\n";
- }
-
- try {
- tist_offset_s = pt.get<double>("delaymanagement.offset");
- }
- catch (std::exception &e) {
- std::cerr << "Error: delaymanagement: synchronous is enabled, but no offset defined!\n";
- throw std::runtime_error("Configuration error");
- }
- }
-
-#endif
-
- /* Read TII parameters from config file */
- mod_settings.tiiConfig.enable = pt.get("tii.enable", 0);
- mod_settings.tiiConfig.comb = pt.get("tii.comb", 0);
- mod_settings.tiiConfig.pattern = pt.get("tii.pattern", 0);
- mod_settings.tiiConfig.old_variant = pt.get("tii.old_variant", 0);
- }
-
etiLog.level(info) << "Starting up version " <<
#if defined(GITVERSION)
GITVERSION;
@@ -670,42 +247,7 @@ int launch_modulator(int argc, char* argv[])
// When using the FIRFilter, increase the modulator offset pipelining delay
// by the correct amount
if (not mod_settings.filterTapsFilename.empty()) {
- tist_delay_stages += FIRFILTER_PIPELINE_DELAY;
- }
-
- // Setting ETI input filename
- if (use_configuration_cmdline && mod_settings.inputName == "") {
- if (optind < argc) {
- mod_settings.inputName = argv[optind++];
-
- if (mod_settings.inputName.substr(0, 4) == "zmq+" &&
- mod_settings.inputName.find("://") != std::string::npos) {
- // if the name starts with zmq+XYZ://somewhere:port
- mod_settings.inputTransport = "zeromq";
- }
- else if (mod_settings.inputName.substr(0, 6) == "tcp://") {
- mod_settings.inputTransport = "tcp";
- }
- else if (mod_settings.inputName.substr(0, 6) == "udp://") {
- mod_settings.inputTransport = "edi";
- }
- }
- else {
- mod_settings.inputName = "/dev/stdin";
- }
- }
-
- // Checking unused arguments
- if (use_configuration_cmdline && optind != argc) {
- fprintf(stderr, "Invalid arguments:");
- while (optind != argc) {
- fprintf(stderr, " %s", argv[optind++]);
- }
- fprintf(stderr, "\n");
- printUsage(argv[0]);
- ret = -1;
- etiLog.level(error) << "Received invalid command line arguments";
- throw std::invalid_argument("Invalid command line options");
+ mod_settings.tist_delay_stages += FIRFILTER_PIPELINE_DELAY;
}
if (not (mod_settings.useFileOutput or
@@ -770,7 +312,7 @@ int launch_modulator(int argc, char* argv[])
}
- EdiReader ediReader(tist_offset_s, tist_delay_stages);
+ EdiReader ediReader(mod_settings.tist_offset_s, mod_settings.tist_delay_stages);
EdiDecoder::ETIDecoder ediInput(ediReader, false);
if (mod_settings.edi_max_delay_ms > 0.0f) {
// setMaxDelay wants number of AF packets, which correspond to 24ms ETI frames
@@ -890,7 +432,7 @@ int launch_modulator(int argc, char* argv[])
m.flowgraph = &flowgraph;
m.data.setLength(6144);
- EtiReader etiReader(tist_offset_s, tist_delay_stages);
+ EtiReader etiReader(mod_settings.tist_offset_s, mod_settings.tist_delay_stages);
m.etiReader = &etiReader;
auto input = make_shared<InputMemory>(&m.data);
diff --git a/src/Utils.cpp b/src/Utils.cpp
index f4610a4..a2ac9f4 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -30,7 +30,7 @@
#include <sys/prctl.h>
#include <pthread.h>
-void printUsage(char* progName)
+void printUsage(const char* progName)
{
FILE* out = stderr;
diff --git a/src/Utils.h b/src/Utils.h
index e2b8a2f..4631292 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -37,7 +37,7 @@
#include <time.h>
#include <string>
-void printUsage(char* progName);
+void printUsage(const char* progName);
void printVersion(void);