From fed9a724ca91417e84071255890ec26ff797fd4f Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 12 Jun 2015 07:42:22 +0200 Subject: Simplify TIST offset handling, add offset to RC This breaks old configuration files using synchronous=1 --- doc/example.ini | 17 +++----- src/DabMod.cpp | 64 +++++++++--------------------- src/DabModulator.cpp | 4 +- src/DabModulator.h | 2 +- src/EtiReader.cpp | 12 +++--- src/EtiReader.h | 3 +- src/OutputUHD.cpp | 2 +- src/OutputUHD.h | 2 +- src/TimestampDecoder.cpp | 100 +++++++++++++++-------------------------------- src/TimestampDecoder.h | 64 ++++++++++++++++-------------- src/Utils.cpp | 5 +-- 11 files changed, 104 insertions(+), 171 deletions(-) diff --git a/doc/example.ini b/doc/example.ini index d7f0a4f..322bbe9 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -221,16 +221,11 @@ synchronous=0 ; Whether to mute the TX when incoming frames have no timestamp mutenotimestamps=0 -; Choose between fixed and dynamic offset definition -; fixed defines an offset in this file that cannot be changed while -; the modulator runs. -; -; dynamic reads the offset from a file, and if the value changes, -; the chain does a re-sync. -management=dynamic +; This iffset is added to the TIST, and the sum defines the +; TX time of the transmission frame. It can by changed at runtime +; through the remote control. +offset=0.002 -fixedoffset=0.002 +; The previous static vs dynamic offset distinction, and reading the +; modulatoroffset from a file has been removed. -; The file should contain a single floating point value, written -; in ASCII (it's human-readable, not binary) -dynamicoffsetfile=modulator_offset diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 045138a..1fc7e3c 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -157,10 +157,8 @@ int launch_modulator(int argc, char* argv[]) modulator_data m; // To handle the timestamp offset of the modulator - struct modulator_offset_config modconf; - modconf.use_offset_file = false; - modconf.use_offset_fixed = false; - modconf.delay_calculation_pipeline_stages = 0; + unsigned tist_delay_stages = 0; + double tist_offset_s = 0.0; shared_ptr flowgraph(new Flowgraph()); shared_ptr format_converter; @@ -238,25 +236,7 @@ int launch_modulator(int argc, char* argv[]) loop = true; break; case 'o': - if (modconf.use_offset_file) - { - fprintf(stderr, "Options -o and -O are mutually exclusive\n"); - throw std::invalid_argument("Invalid command line options"); - } - modconf.use_offset_fixed = true; - modconf.offset_fixed = strtod(optarg, NULL); -#if defined(HAVE_OUTPUT_UHD) - outputuhd_conf.enableSync = true; -#endif - break; - case 'O': - if (modconf.use_offset_fixed) - { - fprintf(stderr, "Options -o and -O are mutually exclusive\n"); - throw std::invalid_argument("Invalid command line options"); - } - modconf.use_offset_file = true; - modconf.offset_filename = std::string(optarg); + tist_offset_s = strtod(optarg, NULL); #if defined(HAVE_OUTPUT_UHD) outputuhd_conf.enableSync = true; #endif @@ -578,23 +558,20 @@ int launch_modulator(int argc, char* argv[]) #if defined(HAVE_OUTPUT_UHD) outputuhd_conf.enableSync = (pt.get("delaymanagement.synchronous", 0) == 1); if (outputuhd_conf.enableSync) { + std::string delay_mgmt = pt.get("delaymanagement.management", ""); + std::string fixedoffset = pt.get("delaymanagement.fixedoffset", ""); + std::string offset_filename = pt.get("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 { - std::string delay_mgmt = pt.get("delaymanagement.management"); - if (delay_mgmt == "fixed") { - modconf.offset_fixed = pt.get("delaymanagement.fixedoffset"); - modconf.use_offset_fixed = true; - } - else if (delay_mgmt == "dynamic") { - modconf.offset_filename = pt.get("delaymanagement.dynamicoffsetfile"); - modconf.use_offset_file = true; - } - else { - throw std::runtime_error("invalid management value"); - } + tist_offset_s = pt.get("delaymanagement.offset"); } catch (std::exception &e) { - std::cerr << "Error: " << e.what() << "\n"; - std::cerr << " Synchronised transmission enabled, but delay management specification is incomplete.\n"; + std::cerr << "Error: delaymanagement: synchronous is enabled, but no offset defined!\n"; throw std::runtime_error("Configuration error"); } } @@ -611,16 +588,10 @@ int launch_modulator(int argc, char* argv[]) etiLog.level(info) << "Starting up"; - if (!(modconf.use_offset_file || modconf.use_offset_fixed)) { - etiLog.level(debug) << "No Modulator offset defined, setting to 0"; - modconf.use_offset_fixed = true; - modconf.offset_fixed = 0; - } - // When using the FIRFilter, increase the modulator offset pipelining delay // by the correct amount if (filterTapsFilename != "") { - modconf.delay_calculation_pipeline_stages += FIRFILTER_PIPELINE_DELAY; + tist_delay_stages += FIRFILTER_PIPELINE_DELAY; } // Setting ETI input filename @@ -777,8 +748,9 @@ int launch_modulator(int argc, char* argv[]) shared_ptr input(new InputMemory(&m.data)); shared_ptr modulator( - new DabModulator(modconf, &rcs, outputRate, clockRate, - dabMode, gainMode, digitalgain, normalise, filterTapsFilename)); + new DabModulator(tist_offset_s, tist_delay_stages, &rcs, + outputRate, clockRate, dabMode, gainMode, digitalgain, + normalise, filterTapsFilename)); flowgraph.connect(input, modulator); if (format_converter) { diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 4769502..35ef7cb 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -55,7 +55,7 @@ using namespace boost; DabModulator::DabModulator( - struct modulator_offset_config& modconf, + double tist_offset_s, unsigned tist_delay_stages, RemoteControllers* rcs, unsigned outputRate, unsigned clockRate, unsigned dabMode, GainMode gainMode, @@ -69,7 +69,7 @@ DabModulator::DabModulator( myGainMode(gainMode), myDigGain(digGain), myNormalise(normalise), - myEtiReader(EtiReader(modconf)), + myEtiReader(EtiReader(tist_offset_s, tist_delay_stages, rcs)), myFlowgraph(NULL), myFilterTapsFilename(filterTapsFilename), myRCs(rcs) diff --git a/src/DabModulator.h b/src/DabModulator.h index 5337f8c..1a9e477 100644 --- a/src/DabModulator.h +++ b/src/DabModulator.h @@ -49,7 +49,7 @@ class DabModulator : public ModCodec { public: DabModulator( - struct modulator_offset_config& modconf, + double tist_offset_s, unsigned tist_delay_stages, RemoteControllers* rcs, unsigned outputRate = 2048000, unsigned clockRate = 0, unsigned dabMode = 0, GainMode gainMode = GAIN_VAR, diff --git a/src/EtiReader.cpp b/src/EtiReader.cpp index 100fbdb..2f088d5 100644 --- a/src/EtiReader.cpp +++ b/src/EtiReader.cpp @@ -51,13 +51,16 @@ enum ETI_READER_STATE { }; -EtiReader::EtiReader(struct modulator_offset_config& modconf) : +EtiReader::EtiReader(double tist_offset_s, unsigned tist_delay_stages, + RemoteControllers* rcs) : state(EtiReaderStateSync), myFicSource(NULL), - myTimestampDecoder(modconf) + myTimestampDecoder(tist_offset_s, tist_delay_stages) { PDEBUG("EtiReader::EtiReader()\n"); + myTimestampDecoder.enrol_at(*rcs); + myCurrentFrame = 0; eti_fc_valid = false; } @@ -284,11 +287,6 @@ int EtiReader::process(const Buffer* dataIn) myTimestampDecoder.updateTimestampEti(eti_fc.FP & 0x3, eti_eoh.MNSC, getPPSOffset(), eti_fc.FCT); - if (eti_fc.FCT % 125 == 0) //every 3 seconds is fine enough - { - myTimestampDecoder.updateModulatorOffset(); - } - return dataIn->getLength() - input_size; } diff --git a/src/EtiReader.h b/src/EtiReader.h index 58a1976..e3b8b0e 100644 --- a/src/EtiReader.h +++ b/src/EtiReader.h @@ -47,7 +47,8 @@ class EtiReader { public: - EtiReader(struct modulator_offset_config& modconf); + EtiReader(double tist_offset_s, unsigned tist_delay_stages, + RemoteControllers* rcs); virtual ~EtiReader(); EtiReader(const EtiReader&); EtiReader& operator=(const EtiReader&); diff --git a/src/OutputUHD.cpp b/src/OutputUHD.cpp index 24d206f..72d4976 100644 --- a/src/OutputUHD.cpp +++ b/src/OutputUHD.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2014 + Copyright (C) 2014, 2015 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org diff --git a/src/OutputUHD.h b/src/OutputUHD.h index 44dd0ff..bf5b27d 100644 --- a/src/OutputUHD.h +++ b/src/OutputUHD.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2014 + Copyright (C) 2014, 2015 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp index c833e7a..b03ecbb 100644 --- a/src/TimestampDecoder.cpp +++ b/src/TimestampDecoder.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2014 + Copyright (C) 2014, 2015 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -62,8 +62,8 @@ void TimestampDecoder::calculateTimestamp(struct frame_timestamp& ts) * * Therefore, use <= and not < for comparison */ - if (queue_timestamps.size() <= modconfig.delay_calculation_pipeline_stages) { - //fprintf(stderr, "* %zu %u ", queue_timestamps.size(), modconfig.delay_calculation_pipeline_stages); + if (queue_timestamps.size() <= m_tist_delay_stages) { + //fprintf(stderr, "* %zu %u ", queue_timestamps.size(), m_tist_delay_stages); /* Return invalid timestamp until the queue is full */ ts.timestamp_valid = false; ts.timestamp_sec = 0; @@ -93,9 +93,9 @@ void TimestampDecoder::calculateTimestamp(struct frame_timestamp& ts) MDEBUG("Timestamp queue size %zu, delay_calc %u\n", queue_timestamps.size(), - modconfig.delay_calculation_pipeline_stages); + m_tist_delay_stages); - if (queue_timestamps.size() > modconfig.delay_calculation_pipeline_stages) { + if (queue_timestamps.size() > m_tist_delay_stages) { etiLog.level(error) << "Error: Timestamp queue is too large : size " << queue_timestamps.size() << "! This should not happen !"; } @@ -198,77 +198,41 @@ void TimestampDecoder::updateTimestampEti( latestFCT = fct; } - -bool TimestampDecoder::updateModulatorOffset() +void TimestampDecoder::set_parameter( + const std::string& parameter, + const std::string& value) { using namespace std; - using boost::lexical_cast; - using boost::bad_lexical_cast; - - if (modconfig.use_offset_fixed) - { - timestamp_offset = modconfig.offset_fixed; - return true; - } - else if (modconfig.use_offset_file) - { - bool r = false; - double newoffset; - std::string filedata; - ifstream filestream; + stringstream ss(value); + ss.exceptions ( stringstream::failbit | stringstream::badbit ); - try - { - filestream.open(modconfig.offset_filename.c_str()); - if (!filestream.eof()) - { - getline(filestream, filedata); - try - { - newoffset = lexical_cast(filedata); - r = true; - } - catch (bad_lexical_cast& e) - { - etiLog.level(error) << - "Error parsing timestamp offset from file '" << - modconfig.offset_filename << "'"; - r = false; - } - } - else - { - etiLog.level(error) << - "Error reading from timestamp offset file: eof reached\n"; - r = false; - } - filestream.close(); - } - catch (exception& e) - { - etiLog.level(error) << "Error opening timestamp offset file\n"; - r = false; - } - - - if (r) - { - if (timestamp_offset != newoffset) - { - timestamp_offset = newoffset; - etiLog.level(info) << - "TimestampDecoder::updateTimestampOffset: new offset is " << - timestamp_offset; - offset_changed = true; - } + if (parameter == "offset") { + ss >> timestamp_offset; + offset_changed = true; + } + else { + stringstream ss; + ss << "Parameter '" << parameter + << "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); + } +} - } +const std::string TimestampDecoder::get_parameter( + const std::string& parameter) const +{ + using namespace std; - return r; + stringstream ss; + if (parameter == "offset") { + ss << timestamp_offset; } else { - return false; + ss << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); } + return ss.str(); } diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index 82753d1..1b805ca 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2014 + Copyright (C) 2014, 2015 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -34,23 +34,7 @@ #include #include "Eti.h" #include "Log.h" - -struct modulator_offset_config -{ - bool use_offset_fixed; - double offset_fixed; - /* These two fields are used when the modulator is run with a fixed offset */ - - bool use_offset_file; - std::string offset_filename; - /* These two fields are used when the modulator reads the offset from a file */ - - unsigned delay_calculation_pipeline_stages; - /* Specifies by how many stages the timestamp must be delayed. - * (e.g. The FIRFilter is pipelined, therefore we must increase - * delay_calculation_pipeline_stages by one if the filter is used - */ -}; +#include "RemoteControl.h" struct frame_timestamp { @@ -109,13 +93,24 @@ struct frame_timestamp }; /* This module decodes MNSC time information */ -class TimestampDecoder +class TimestampDecoder : public RemoteControllable { public: TimestampDecoder( - struct modulator_offset_config& config) : - modconfig(config) + /* The modulator adds this offset to the TIST to define time of + * frame transmission + */ + double offset_s, + + /* Specifies by how many stages the timestamp must be delayed. + * (e.g. The FIRFilter is pipelined, therefore we must increase + * tist_delay_stages by one if the filter is used + */ + unsigned tist_delay_stages) : + RemoteControllable("tist") { + timestamp_offset = offset_s; + m_tist_delay_stages = tist_delay_stages; inhibit_second_update = 0; time_pps = 0.0; time_secs = 0; @@ -125,10 +120,10 @@ class TimestampDecoder gmtime_r(0, &temp_time); offset_changed = false; + RC_ADD_PARAMETER(offset, "TIST offset [s]"); + etiLog.level(info) << "Setting up timestamp decoder with " << - (modconfig.use_offset_fixed ? "fixed" : - (modconfig.use_offset_file ? "dynamic" : "none")) << - " offset"; + timestamp_offset << " offset"; }; @@ -142,9 +137,21 @@ class TimestampDecoder double pps, int32_t fct); - /* Update the modulator timestamp offset according to the modconf + /*********** REMOTE CONTROL ***************/ + /* virtual void enrol_at(BaseRemoteController& controller) + * is inherited */ - bool updateModulatorOffset(); + + /* Base function to set parameters. */ + virtual void set_parameter(const std::string& parameter, + const std::string& value); + + /* Getting a parameter always returns a string. */ + virtual const std::string get_parameter( + const std::string& parameter) const; + + const char* name() { return "TS"; } + protected: /* Push a new MNSC field into the decoder */ @@ -167,12 +174,10 @@ class TimestampDecoder int32_t latestFCT; double time_pps; double timestamp_offset; + unsigned m_tist_delay_stages; int inhibit_second_update; bool offset_changed; - /* configuration for the offset management */ - struct modulator_offset_config& modconfig; - /* When the type or identifier don't match, the decoder must * be disabled */ @@ -191,3 +196,4 @@ class TimestampDecoder }; #endif + diff --git a/src/Utils.cpp b/src/Utils.cpp index 8b97602..6c9b0fc 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -49,7 +49,6 @@ void printUsage(char* progName) " (-f filename | -u uhddevice -F frequency) " " [-G txgain]" " [-o offset]" - " [-O offsetfile]" " [-T filter_taps_file]" " [-a gain]" " [-c clockrate]" @@ -66,9 +65,7 @@ void printUsage(char* progName) fprintf(out, "-F frequency: Set the transmit frequency when using UHD output. (mandatory option when using UHD)\n"); fprintf(out, "-G txgain: Set the transmit gain for the UHD driver (default: 0)\n"); fprintf(out, "-o: (UHD only) Set the timestamp offset added to the timestamp in the ETI. The offset is a double.\n"); - fprintf(out, "-O: (UHD only) Set the file containing the timestamp offset added to the timestamp in the ETI.\n" - "The file is read every six seconds, and must contain a double value.\n"); - fprintf(out, " Specifying either -o or -O has two implications: It enables synchronous transmission,\n" + fprintf(out, " Specifying this option has two implications: It enables synchronous transmission,\n" " requiring an external REFCLK and PPS signal and frames that do not contain a valid timestamp\n" " get muted.\n\n"); fprintf(out, "-T taps_file: Enable filtering before the output, using the specified file containing the filter taps.\n"); -- cgit v1.2.3