diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ConfigParser.cpp | 477 | ||||
-rw-r--r-- | src/ConfigParser.h | 88 | ||||
-rw-r--r-- | src/DabMod.cpp | 847 | ||||
-rw-r--r-- | src/Utils.cpp | 48 | ||||
-rw-r--r-- | src/Utils.h | 4 |
5 files changed, 807 insertions, 657 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); + } +} + 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 37db71a..3554e4c 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,16 @@ # 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 + * wide-spectrum spikes. Depending on the Transmission Mode, the + * Gain Mode and the sample rate (and maybe other parameters), the + * samples can have peaks up to about 48000. The value of 50000 + * should guarantee that with a digital gain of 1.0, UHD never clips + * our samples. + */ +static const float normalise_factor = 50000.0f; typedef std::complex<float> complexf; @@ -94,8 +100,7 @@ struct modulator_data inputReader(nullptr), framecount(0), flowgraph(nullptr), - etiReader(nullptr), - rcs(nullptr) {} + etiReader(nullptr) {} InputReader* inputReader; Buffer data; @@ -103,7 +108,6 @@ struct modulator_data Flowgraph* flowgraph; EtiReader* etiReader; - RemoteControllers* rcs; }; enum class run_modulator_state_t { @@ -115,691 +119,179 @@ enum class run_modulator_state_t { run_modulator_state_t run_modulator(modulator_data& m); -static GainMode parse_gainmode(const std::string &gainMode_setting) +static void printModSettings(const mod_settings_t& mod_settings) { - 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"); -} - -int launch_modulator(int argc, char* argv[]) -{ - int ret = 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; - - 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; - - tii_config_t tiiConfig; - - /* UHD requires the input I and Q samples to be in the interval - * [-1.0,1.0], otherwise they get truncated, which creates very - * wide-spectrum spikes. Depending on the Transmission Mode, the - * Gain Mode and the sample rate (and maybe other parameters), the - * samples can have peaks up to about 48000. The value of 50000 - * should guarantee that with a digital gain of 1.0, UHD never clips - * our samples. - */ - const float normalise_factor = 50000.0f; - - std::string filterTapsFilename = ""; - - // Two configuration sources exist: command line and (new) INI file - bool use_configuration_cmdline = false; - bool use_configuration_file = false; - std::string configuration_file; - -#if defined(HAVE_OUTPUT_UHD) - OutputUHDConfig outputuhd_conf; -#endif - -#if defined(HAVE_SOAPYSDR) - OutputSoapyConfig outputsoapy_conf; -#endif - - 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; - shared_ptr<ModOutput> output; - - m.rcs = &rcs; - - bool run_again = true; - - InputFileReader inputFileReader; -#if defined(HAVE_ZEROMQ) - auto inputZeroMQReader = make_shared<InputZeroMQReader>(); -#endif - - auto inputTcpReader = make_shared<InputTcpReader>(); - - struct sigaction sa; - memset(&sa, 0, sizeof(struct sigaction)); - sa.sa_handler = &signalHandler; - - if (sigaction(SIGINT, &sa, NULL) == -1) { - perror("sigaction"); - return EXIT_FAILURE; - } - - // Set timezone to UTC - 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': - digitalgain = strtof(optarg, NULL); - break; - case 'c': - clockRate = strtol(optarg, NULL, 0); - break; - case 'f': -#if defined(HAVE_OUTPUT_UHD) - if (useUHDOutput) { - fprintf(stderr, "Options -u and -f are mutually exclusive\n"); - throw std::invalid_argument("Invalid command line options"); - } -#endif - outputName = optarg; - useFileOutput = 1; - break; - case 'F': -#if defined(HAVE_OUTPUT_UHD) - outputuhd_conf.frequency = strtof(optarg, NULL); -#endif - break; - case 'g': - gainMode = parse_gainmode(optarg); - break; - case 'G': -#if defined(HAVE_OUTPUT_UHD) - outputuhd_conf.txgain = strtod(optarg, NULL); -#endif - break; - case 'l': - loop = true; - break; - case 'o': - tist_offset_s = strtod(optarg, NULL); -#if defined(HAVE_OUTPUT_UHD) - outputuhd_conf.enableSync = true; -#endif - break; - case 'm': - dabMode = strtol(optarg, NULL, 0); - break; - case 'r': - outputRate = strtol(optarg, NULL, 0); - break; - case 'T': - filterTapsFilename = optarg; - break; - case 'u': -#if defined(HAVE_OUTPUT_UHD) - if (useFileOutput) { - fprintf(stderr, "Options -u and -f are mutually exclusive\n"); - throw std::invalid_argument("Invalid command line options"); - } - outputuhd_conf.device = optarg; - outputuhd_conf.refclk_src = "internal"; - outputuhd_conf.pps_src = "none"; - outputuhd_conf.pps_polarity = "pos"; - 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"); - } - } - - std::cerr << "ODR-DabMod version " << -#if defined(GITVERSION) - GITVERSION -#else - VERSION -#endif - << std::endl; - - std::cerr << "Compiled with features: " << -#if defined(HAVE_ZEROMQ) - "zeromq " << -#endif -#if defined(HAVE_OUTPUT_UHD) - "output_uhd " << -#endif -#if defined(HAVE_SOAPYSDR) - "output_soapysdr " << -#endif -#if defined(__FAST_MATH__) - "fast-math" << -#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) { - loop = true; - } - - inputTransport = pt.get("input.transport", "file"); - inputMaxFramesQueued = pt.get("input.max_frames_queued", - ZMQ_INPUT_MAX_FRAME_QUEUE); - - edi_max_delay_ms = pt.get("input.edi_max_delay", 0.0f); - - 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"); - gainMode = parse_gainmode(gainMode_setting); - - dabMode = pt.get("modulator.mode", dabMode); - clockRate = pt.get("modulator.dac_clk_rate", (size_t)0); - digitalgain = pt.get("modulator.digital_gain", digitalgain); - outputRate = pt.get("modulator.rate", outputRate); - - // FIR Filter parameters: - if (pt.get("firfilter.enabled", 0) == 1) { - 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 { - 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"); - } - useFileOutput = 1; - - fileOutputFormat = pt.get("fileoutput.format", fileOutputFormat); - } -#if defined(HAVE_OUTPUT_UHD) - else if (output_selected == "uhd") { - 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 = 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); - - useUHDOutput = 1; - } -#endif -#if defined(HAVE_SOAPYSDR) - else if (output_selected == "soapysdr") { - 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 = 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"); - } - - useSoapyOutput = 1; - } -#endif -#if defined(HAVE_ZEROMQ) - else if (output_selected == "zmq") { - outputName = pt.get<std::string>("zmqoutput.listen"); - zmqOutputSocketType = pt.get<std::string>("zmqoutput.socket_type"); - useZeroMQOutput = 1; - } -#endif - else { - std::cerr << "Error: Invalid output defined.\n"; - throw std::runtime_error("Configuration error"); - } - -#if defined(HAVE_OUTPUT_UHD) - outputuhd_conf.enableSync = (pt.get("delaymanagement.synchronous", 0) == 1); - if (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"); - } - } - - outputuhd_conf.muteNoTimestamps = (pt.get("delaymanagement.mutenotimestamps", 0) == 1); -#endif - - /* Read TII parameters from config file */ - tiiConfig.enable = pt.get("tii.enable", 0); - tiiConfig.comb = pt.get("tii.comb", 0); - tiiConfig.pattern = pt.get("tii.pattern", 0); - tiiConfig.old_variant = pt.get("tii.old_variant", 0); - } - - etiLog.level(info) << "Starting up version " << -#if defined(GITVERSION) - GITVERSION; -#else - VERSION; -#endif - - - // When using the FIRFilter, increase the modulator offset pipelining delay - // by the correct amount - if (not filterTapsFilename.empty()) { - tist_delay_stages += FIRFILTER_PIPELINE_DELAY; - } - - // Setting ETI input filename - if (use_configuration_cmdline && inputName == "") { - if (optind < argc) { - inputName = argv[optind++]; - - if (inputName.substr(0, 4) == "zmq+" && - inputName.find("://") != std::string::npos) { - // if the name starts with zmq+XYZ://somewhere:port - inputTransport = "zeromq"; - } - else if (inputName.substr(0, 6) == "tcp://") { - inputTransport = "tcp"; - } - else if (inputName.substr(0, 6) == "udp://") { - inputTransport = "edi"; - } - } - else { - 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"); - } - - if (!useFileOutput && !useUHDOutput && !useZeroMQOutput && !useSoapyOutput) { - etiLog.level(error) << "Output not specified"; - fprintf(stderr, "Must specify output !"); - throw std::runtime_error("Configuration error"); - } - // Print settings fprintf(stderr, "Input\n"); - fprintf(stderr, " Type: %s\n", inputTransport.c_str()); - fprintf(stderr, " Source: %s\n", inputName.c_str()); + fprintf(stderr, " Type: %s\n", mod_settings.inputTransport.c_str()); + fprintf(stderr, " Source: %s\n", mod_settings.inputName.c_str()); fprintf(stderr, "Output\n"); - if (useFileOutput) { - fprintf(stderr, " Name: %s\n", outputName.c_str()); + if (mod_settings.useFileOutput) { + fprintf(stderr, " Name: %s\n", mod_settings.outputName.c_str()); } #if defined(HAVE_OUTPUT_UHD) - else if (useUHDOutput) { + else if (mod_settings.useUHDOutput) { fprintf(stderr, " UHD\n" " Device: %s\n" " Type: %s\n" " master_clock_rate: %ld\n" " refclk: %s\n" " pps source: %s\n", - outputuhd_conf.device.c_str(), - outputuhd_conf.usrpType.c_str(), - outputuhd_conf.masterClockRate, - outputuhd_conf.refclk_src.c_str(), - outputuhd_conf.pps_src.c_str()); + mod_settings.outputuhd_conf.device.c_str(), + mod_settings.outputuhd_conf.usrpType.c_str(), + mod_settings.outputuhd_conf.masterClockRate, + mod_settings.outputuhd_conf.refclk_src.c_str(), + mod_settings.outputuhd_conf.pps_src.c_str()); } #endif #if defined(HAVE_SOAPYSDR) - else if (useSoapyOutput) { + else if (mod_settings.useSoapyOutput) { fprintf(stderr, " SoapySDR\n" " Device: %s\n" " master_clock_rate: %ld\n", - outputsoapy_conf.device.c_str(), - outputsoapy_conf.masterClockRate); + mod_settings.outputsoapy_conf.device.c_str(), + mod_settings.outputsoapy_conf.masterClockRate); } #endif - else if (useZeroMQOutput) { + else if (mod_settings.useZeroMQOutput) { fprintf(stderr, " ZeroMQ\n" " Listening on: %s\n" " Socket type : %s\n", - outputName.c_str(), - zmqOutputSocketType.c_str()); + mod_settings.outputName.c_str(), + mod_settings.zmqOutputSocketType.c_str()); } fprintf(stderr, " Sampling rate: "); - if (outputRate > 1000) { - if (outputRate > 1000000) { - fprintf(stderr, "%.4g MHz\n", outputRate / 1000000.0f); + if (mod_settings.outputRate > 1000) { + if (mod_settings.outputRate > 1000000) { + fprintf(stderr, "%.4g MHz\n", mod_settings.outputRate / 1000000.0f); } else { - fprintf(stderr, "%.4g kHz\n", outputRate / 1000.0f); + fprintf(stderr, "%.4g kHz\n", mod_settings.outputRate / 1000.0f); } } else { - fprintf(stderr, "%zu Hz\n", outputRate); + fprintf(stderr, "%zu Hz\n", mod_settings.outputRate); } +} +static shared_ptr<ModOutput> prepare_output( + mod_settings_t& s) +{ + shared_ptr<ModOutput> output; - EdiReader ediReader(tist_offset_s, tist_delay_stages); - EdiDecoder::ETIDecoder ediInput(ediReader, false); - if (edi_max_delay_ms > 0.0f) { - // setMaxDelay wants number of AF packets, which correspond to 24ms ETI frames - ediInput.setMaxDelay(lroundf(edi_max_delay_ms / 24.0f)); - } - EdiUdpInput ediUdpInput(ediInput); - - if (inputTransport == "file") { - // Opening ETI input file - if (inputFileReader.Open(inputName, loop) == -1) { - fprintf(stderr, "Unable to open input file!\n"); - etiLog.level(error) << "Unable to open input file!"; - ret = -1; - throw std::runtime_error("Unable to open input"); - } - - m.inputReader = &inputFileReader; - } - else if (inputTransport == "zeromq") { -#if !defined(HAVE_ZEROMQ) - fprintf(stderr, "Error, ZeroMQ input transport selected, but not compiled in!\n"); - ret = -1; - throw std::runtime_error("Unable to open input"); -#else - inputZeroMQReader->Open(inputName, inputMaxFramesQueued); - m.inputReader = inputZeroMQReader.get(); -#endif - } - else if (inputTransport == "tcp") { - inputTcpReader->Open(inputName); - m.inputReader = inputTcpReader.get(); - } - else if (inputTransport == "edi") { - ediUdpInput.Open(inputName); - } - else - { - fprintf(stderr, "Error, invalid input transport %s selected!\n", inputTransport.c_str()); - ret = -1; - throw std::runtime_error("Unable to open input"); - } - - if (useFileOutput) { - if (fileOutputFormat == "complexf") { - output = make_shared<OutputFile>(outputName); + if (s.useFileOutput) { + if (s.fileOutputFormat == "complexf") { + output = make_shared<OutputFile>(s.outputName); } - else if (fileOutputFormat == "s8") { + else if (s.fileOutputFormat == "s8") { // We must normalise the samples to the interval [-127.0; 127.0] - normalise = 127.0f / normalise_factor; - - format_converter = make_shared<FormatConverter>(); + s.normalise = 127.0f / normalise_factor; - output = make_shared<OutputFile>(outputName); + output = make_shared<OutputFile>(s.outputName); } } #if defined(HAVE_OUTPUT_UHD) - else if (useUHDOutput) { - normalise = 1.0f / normalise_factor; - outputuhd_conf.sampleRate = outputRate; - output = make_shared<OutputUHD>(outputuhd_conf); + else if (s.useUHDOutput) { + s.normalise = 1.0f / normalise_factor; + s.outputuhd_conf.sampleRate = s.outputRate; + output = make_shared<OutputUHD>(s.outputuhd_conf); rcs.enrol((OutputUHD*)output.get()); } #endif #if defined(HAVE_SOAPYSDR) - else if (useSoapyOutput) { + else if (s.useSoapyOutput) { /* We normalise the same way as for the UHD output */ - normalise = 1.0f / normalise_factor; - outputsoapy_conf.sampleRate = outputRate; - output = make_shared<OutputSoapy>(outputsoapy_conf); + s.normalise = 1.0f / normalise_factor; + s.outputsoapy_conf.sampleRate = s.outputRate; + output = make_shared<OutputSoapy>(s.outputsoapy_conf); rcs.enrol((OutputSoapy*)output.get()); } #endif #if defined(HAVE_ZEROMQ) - else if (useZeroMQOutput) { + else if (s.useZeroMQOutput) { /* We normalise the same way as for the UHD output */ - normalise = 1.0f / normalise_factor; - if (zmqOutputSocketType == "pub") { - output = make_shared<OutputZeroMQ>(outputName, ZMQ_PUB); + s.normalise = 1.0f / normalise_factor; + if (s.zmqOutputSocketType == "pub") { + output = make_shared<OutputZeroMQ>(s.outputName, ZMQ_PUB); } - else if (zmqOutputSocketType == "rep") { - output = make_shared<OutputZeroMQ>(outputName, ZMQ_REP); + else if (s.zmqOutputSocketType == "rep") { + output = make_shared<OutputZeroMQ>(s.outputName, ZMQ_REP); } else { std::stringstream ss; - ss << "ZeroMQ output socket type " << zmqOutputSocketType << " invalid"; + ss << "ZeroMQ output socket type " << s.zmqOutputSocketType << " invalid"; throw std::invalid_argument(ss.str()); } } #endif + return output; +} + +int launch_modulator(int argc, char* argv[]) +{ + int ret = 0; + + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = &signalHandler; + + if (sigaction(SIGINT, &sa, NULL) == -1) { + perror("sigaction"); + return EXIT_FAILURE; + } + + // Set timezone to UTC + setenv("TZ", "", 1); + tzset(); + + mod_settings_t mod_settings; + parse_args(argc, argv, mod_settings); + + printStartupInfo(); + + if (not (mod_settings.useFileOutput or + mod_settings.useUHDOutput or + mod_settings.useZeroMQOutput or + mod_settings.useSoapyOutput)) { + etiLog.level(error) << "Output not specified"; + fprintf(stderr, "Must specify output !"); + throw std::runtime_error("Configuration error"); + } + + // When using the FIRFilter, increase the modulator offset pipelining delay + // by the correct amount + if (not mod_settings.filterTapsFilename.empty()) { + mod_settings.tist_delay_stages += FIRFILTER_PIPELINE_DELAY; + } + + printModSettings(mod_settings); + + modulator_data m; + + shared_ptr<FormatConverter> format_converter; + if (mod_settings.useFileOutput and mod_settings.fileOutputFormat == "s8") { + format_converter = make_shared<FormatConverter>(); + } + + auto output = prepare_output(mod_settings); + // Set thread priority to realtime if (int r = set_realtime_prio(1)) { etiLog.level(error) << "Could not set priority for modulator:" << r; } set_thread_name("modulator"); - if (inputTransport == "edi") { + if (mod_settings.inputTransport == "edi") { + 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 + ediInput.setMaxDelay(lroundf(mod_settings.edi_max_delay_ms / 24.0f)); + } + EdiUdpInput ediUdpInput(ediInput); + + ediUdpInput.Open(mod_settings.inputName); if (not ediUdpInput.isEnabled()) { etiLog.level(error) << "inputTransport is edi, but ediUdpInput is not enabled"; return -1; @@ -807,9 +299,15 @@ int launch_modulator(int argc, char* argv[]) Flowgraph flowgraph; auto modulator = make_shared<DabModulator>( - ediReader, tiiConfig, outputRate, clockRate, - dabMode, gainMode, digitalgain, normalise, - filterTapsFilename); + ediReader, + mod_settings.tiiConfig, + mod_settings.outputRate, + mod_settings.clockRate, + mod_settings.dabMode, + mod_settings.gainMode, + mod_settings.digitalgain, + mod_settings.normalise, + mod_settings.filterTapsFilename); if (format_converter) { flowgraph.connect(modulator, format_converter); @@ -820,12 +318,12 @@ int launch_modulator(int argc, char* argv[]) } #if defined(HAVE_OUTPUT_UHD) - if (useUHDOutput) { + if (mod_settings.useUHDOutput) { ((OutputUHD*)output.get())->setETISource(modulator->getEtiSource()); } #endif #if defined(HAVE_SOAPYSDR) - if (useSoapyOutput) { + if (mod_settings.useSoapyOutput) { ((OutputSoapy*)output.get())->setETISource(modulator->getEtiSource()); } #endif @@ -852,20 +350,67 @@ int launch_modulator(int argc, char* argv[]) } } else { + shared_ptr<InputReader> inputReader; + + if (mod_settings.inputTransport == "file") { + auto inputFileReader = make_shared<InputFileReader>(); + + // Opening ETI input file + if (inputFileReader->Open(mod_settings.inputName, mod_settings.loop) == -1) { + fprintf(stderr, "Unable to open input file!\n"); + etiLog.level(error) << "Unable to open input file!"; + ret = -1; + throw std::runtime_error("Unable to open input"); + } + + inputReader = inputFileReader; + } + else if (mod_settings.inputTransport == "zeromq") { +#if !defined(HAVE_ZEROMQ) + fprintf(stderr, "Error, ZeroMQ input transport selected, but not compiled in!\n"); + ret = -1; + throw std::runtime_error("Unable to open input"); +#else + auto inputZeroMQReader = make_shared<InputZeroMQReader>(); + inputZeroMQReader->Open(mod_settings.inputName, mod_settings.inputMaxFramesQueued); + inputReader = inputZeroMQReader; +#endif + } + else if (mod_settings.inputTransport == "tcp") { + auto inputTcpReader = make_shared<InputTcpReader>(); + inputTcpReader->Open(mod_settings.inputName); + inputReader = inputTcpReader; + } + else + { + fprintf(stderr, "Error, invalid input transport %s selected!\n", mod_settings.inputTransport.c_str()); + ret = -1; + throw std::runtime_error("Unable to open input"); + } + + bool run_again = true; + while (run_again) { Flowgraph flowgraph; + m.inputReader = inputReader.get(); 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); auto modulator = make_shared<DabModulator>( - etiReader, tiiConfig, outputRate, clockRate, - dabMode, gainMode, digitalgain, normalise, - filterTapsFilename); + etiReader, + mod_settings.tiiConfig, + mod_settings.outputRate, + mod_settings.clockRate, + mod_settings.dabMode, + mod_settings.gainMode, + mod_settings.digitalgain, + mod_settings.normalise, + mod_settings.filterTapsFilename); if (format_converter) { flowgraph.connect(modulator, format_converter); @@ -876,17 +421,17 @@ int launch_modulator(int argc, char* argv[]) } #if defined(HAVE_OUTPUT_UHD) - if (useUHDOutput) { + if (mod_settings.useUHDOutput) { ((OutputUHD*)output.get())->setETISource(modulator->getEtiSource()); } #endif #if defined(HAVE_SOAPYSDR) - if (useSoapyOutput) { + if (mod_settings.useSoapyOutput) { ((OutputSoapy*)output.get())->setETISource(modulator->getEtiSource()); } #endif - m.inputReader->PrintInfo(); + inputReader->PrintInfo(); run_modulator_state_t st = run_modulator(m); etiLog.log(trace, "DABMOD,run_modulator() = %d", st); @@ -900,8 +445,8 @@ int launch_modulator(int argc, char* argv[]) case run_modulator_state_t::again: etiLog.level(warn) << "Restart modulator."; run_again = false; - if (inputTransport == "file") { - if (inputFileReader.Open(inputName, loop) == -1) { + if (auto in = dynamic_pointer_cast<InputFileReader>(inputReader)) { + if (in->Open(mod_settings.inputName, mod_settings.loop) == -1) { etiLog.level(error) << "Unable to open input file!"; ret = 1; } @@ -909,19 +454,19 @@ int launch_modulator(int argc, char* argv[]) run_again = true; } } - else if (inputTransport == "zeromq") { #if defined(HAVE_ZEROMQ) + else if (auto in = dynamic_pointer_cast<InputZeroMQReader>(inputReader)) { run_again = true; // Create a new input reader - inputZeroMQReader = make_shared<InputZeroMQReader>(); - inputZeroMQReader->Open(inputName, inputMaxFramesQueued); - m.inputReader = inputZeroMQReader.get(); -#endif + auto inputZeroMQReader = make_shared<InputZeroMQReader>(); + inputZeroMQReader->Open(mod_settings.inputName, mod_settings.inputMaxFramesQueued); + inputReader = inputZeroMQReader; } - else if (inputTransport == "tcp") { - inputTcpReader = make_shared<InputTcpReader>(); - inputTcpReader->Open(inputName); - m.inputReader = inputTcpReader.get(); +#endif + else if (auto in = dynamic_pointer_cast<InputTcpReader>(inputReader)) { + auto inputTcpReader = make_shared<InputTcpReader>(); + inputTcpReader->Open(mod_settings.inputName); + inputReader = inputTcpReader; } break; case run_modulator_state_t::reconfigure: diff --git a/src/Utils.cpp b/src/Utils.cpp index f4610a4..5bd93d9 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; @@ -89,13 +89,19 @@ void printVersion(void) { FILE *out = stderr; - fprintf(out, "Welcome to %s %s, compiled at %s, %s\n\n", - PACKAGE, VERSION, __DATE__, __TIME__); + fprintf(out, "%s %s, compiled at %s, %s\n\n", + PACKAGE, +#if defined(GITVERSION) + GITVERSION +#else + VERSION +#endif + ,__DATE__, __TIME__); fprintf(out, " ODR-DabMod is copyright (C) Her Majesty the Queen in Right of Canada,\n" - " 2009, 2010, 2011, 2012 Communications Research Centre (CRC),\n" + " 2005 -- 2012 Communications Research Centre (CRC),\n" " and\n" - " Copyright (C) 2014, 2015 Matthias P. Braendli, matthias.braendli@mpb.li\n" + " Copyright (C) 2017 Matthias P. Braendli, matthias.braendli@mpb.li\n" "\n" " http://opendigitalradio.org\n" "\n" @@ -113,7 +119,39 @@ void printVersion(void) " with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.\n" "\n" ); +} +void printStartupInfo() +{ + std::cerr << "ODR-DabMod version " << +#if defined(GITVERSION) + GITVERSION +#else + VERSION +#endif + << std::endl; + + std::cerr << "Compiled with features: " << +#if defined(HAVE_ZEROMQ) + "zeromq " << +#endif +#if defined(HAVE_OUTPUT_UHD) + "output_uhd " << +#endif +#if defined(HAVE_SOAPYSDR) + "output_soapysdr " << +#endif +#if defined(__FAST_MATH__) + "fast-math" << +#endif + "\n"; + + etiLog.level(info) << "Starting up version " << +#if defined(GITVERSION) + GITVERSION; +#else + VERSION; +#endif } int set_realtime_prio(int prio) diff --git a/src/Utils.h b/src/Utils.h index e2b8a2f..8da3a1b 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -37,10 +37,12 @@ #include <time.h> #include <string> -void printUsage(char* progName); +void printUsage(const char* progName); void printVersion(void); +void printStartupInfo(void); + inline long timespecdiff_us(struct timespec& oldTime, struct timespec& time) { long tv_sec; |