diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2017-02-17 22:24:30 +0100 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2017-02-24 11:58:06 +0100 |
commit | b51bea6c775007590e03f3dd230b1415c08c914a (patch) | |
tree | 859d8a086f757471545acb9ff92b69de8b71f7dc /src/ConfigParser.cpp | |
parent | f1d767969cf02ae64de912969859d4eaa9a1d09c (diff) | |
download | dabmod-b51bea6c775007590e03f3dd230b1415c08c914a.tar.gz dabmod-b51bea6c775007590e03f3dd230b1415c08c914a.tar.bz2 dabmod-b51bea6c775007590e03f3dd230b1415c08c914a.zip |
Move configuration and cmdline parser to new file
Diffstat (limited to 'src/ConfigParser.cpp')
-rw-r--r-- | src/ConfigParser.cpp | 477 |
1 files changed, 477 insertions, 0 deletions
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); + } +} + |