diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/ConfigParser.cpp | 477 | ||||
-rw-r--r-- | src/ConfigParser.h | 88 | ||||
-rw-r--r-- | src/DabMod.cpp | 470 | ||||
-rw-r--r-- | src/Utils.cpp | 2 | ||||
-rw-r--r-- | src/Utils.h | 2 |
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); |