From 450c1e1d29a08326f4a370005bacafd528cd25e7 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Thu, 2 Nov 2017 14:11:26 +0100 Subject: Create new SDR output abstraction and port Soapy --- src/TimestampDecoder.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/TimestampDecoder.h') diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index e0dee2a..34d862f 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -43,7 +43,7 @@ struct frame_timestamp uint32_t timestamp_sec; uint32_t timestamp_pps; // In units of 1/16384000 s - bool timestamp_valid; + bool timestamp_valid = false; bool timestamp_refresh; frame_timestamp() = default; @@ -88,6 +88,12 @@ struct frame_timestamp return timestamp_pps / 16384000.0; } + double get_real_secs() const { + double t = timestamp_sec; + t += timestamp_pps; + return t; + } + void print(const char* t) { fprintf(stderr, -- cgit v1.2.3 From 4d8310ae0ffe1f78a2b8623d55f63ae504ff1aa8 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 4 Nov 2017 08:36:03 +0100 Subject: Make DPD Feedback server SDRDevice-agnostic --- Makefile.am | 17 +-- TODO | 21 +-- src/ConfigParser.cpp | 8 +- src/TimestampDecoder.h | 6 + src/output/Feedback.cpp | 356 ++++++++++++++++++++++++++++++++++++++++++++++++ src/output/Feedback.h | 116 ++++++++++++++++ src/output/SDR.cpp | 22 ++- src/output/SDR.h | 85 +----------- src/output/SDRDevice.h | 136 ++++++++++++++++++ src/output/Soapy.cpp | 57 ++++++-- src/output/Soapy.h | 11 +- src/output/UHD.cpp | 40 ++++++ src/output/UHD.h | 9 ++ 13 files changed, 771 insertions(+), 113 deletions(-) create mode 100644 src/output/Feedback.cpp create mode 100644 src/output/Feedback.h create mode 100644 src/output/SDRDevice.h (limited to 'src/TimestampDecoder.h') diff --git a/Makefile.am b/Makefile.am index 8ce5f89..6e5c1a3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -81,20 +81,21 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ src/OutputZeroMQ.h \ src/TimestampDecoder.h \ src/TimestampDecoder.cpp \ - src/output/UHD.cpp \ - src/output/UHD.h \ - src/OutputUHDFeedback.cpp \ - src/OutputUHDFeedback.h \ + src/output/Feedback.cpp \ + src/output/Feedback.h \ + src/output/SDR.cpp \ + src/output/SDR.h \ + src/output/SDRDevice.h \ src/output/Soapy.cpp \ src/output/Soapy.h \ - src/output/SDR.h \ - src/output/SDR.cpp \ + src/output/UHD.cpp \ + src/output/UHD.h \ + src/InputFileReader.cpp \ src/InputMemory.cpp \ src/InputMemory.h \ - src/InputFileReader.cpp \ + src/InputReader.h \ src/InputTcpReader.cpp \ src/InputZeroMQReader.cpp \ - src/InputReader.h \ src/OutputFile.cpp \ src/OutputFile.h \ src/FrameMultiplexer.cpp \ diff --git a/TODO b/TODO index be42465..a566ff9 100644 --- a/TODO +++ b/TODO @@ -6,6 +6,13 @@ Unless written, no activity has been started on the topics. TODOs for ongoing SDR output refactoring ---------------------------------------- +Currently, all the frontend tuning and timestamping settings are UHD-specific. +To make it possible to run with synchronised=1 using Soapy, refactoring the +output to share the parts that are common. + +This would enable SFN support with LimeSDR devices. + + Clean up and separate GPS and refclk checks. @@ -13,10 +20,13 @@ Add antenna selection to config. Add refclk stuff and timestamps to Soapy. -Make an abstraction for the DPD feedback server, use it for Soapy and UHD. +*done* Make an abstraction for the DPD feedback server, use it for Soapy and UHD. Move staticdelay into a new process block +Double-check all #includes + +Move other non SDR outputs to the output folder. Tests, both with B200 and LimeSDR: - No timestamps @@ -41,15 +51,6 @@ However, when enabled, some receivers are not able to lock on the signal. Is the power of the TII too strong? Synchronisation wrong? -Rework Soapy and UHD outputs ----------------------------- -Currently, all the frontend tuning and timestamping settings are UHD-specific. -To make it possible to run with synchronised=1 using Soapy, refactoring the -output to share the parts that are common. - -This would enable SFN support with LimeSDR devices. - - Finalise EDI input ------------------ The EDI input, based on work started in http://git.mpb.li/git/odr-edilib/ diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index c532ab5..8facec3 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -29,15 +29,17 @@ # include "config.h" #endif +#include +#include +#include + #include "ConfigParser.h" #include "porting.h" #include "Utils.h" #include "Log.h" #include "DabModulator.h" +#include "output/SDR.h" -#include -#include -#include using namespace std; diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index 34d862f..1ef493e 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -94,6 +94,12 @@ struct frame_timestamp return t; } + long long int get_ns() const { + long long int ns = timestamp_sec * 1000000000ull; + ns += llrint((double)timestamp_pps / 0.016384); + return ns; + } + void print(const char* t) { fprintf(stderr, diff --git a/src/output/Feedback.cpp b/src/output/Feedback.cpp new file mode 100644 index 0000000..f0bbd98 --- /dev/null +++ b/src/output/Feedback.cpp @@ -0,0 +1,356 @@ +/* + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 + +DESCRIPTION: + This presents a TCP socket to an external tool which calculates + a Digital Predistortion model from a short sequence of transmit + samples and corresponding receive samples. +*/ + +/* + 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 . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "output/Feedback.h" +#include "Utils.h" +#include "Socket.h" + +using namespace std; + +namespace Output { + +DPDFeedbackServer::DPDFeedbackServer( + std::shared_ptr device, + uint16_t port, + uint32_t sampleRate) : + m_port(port), + m_sampleRate(sampleRate), + m_device(device) +{ + if (m_port) { + m_running.store(true); + + rx_burst_thread = boost::thread( + &DPDFeedbackServer::ReceiveBurstThread, this); + + burst_tcp_thread = boost::thread( + &DPDFeedbackServer::ServeFeedbackThread, this); + } +} + +DPDFeedbackServer::~DPDFeedbackServer() +{ + m_running.store(false); + + rx_burst_thread.interrupt(); + if (rx_burst_thread.joinable()) { + rx_burst_thread.join(); + } + + burst_tcp_thread.interrupt(); + if (burst_tcp_thread.joinable()) { + burst_tcp_thread.join(); + } +} + +void DPDFeedbackServer::set_tx_frame( + const std::vector &buf, + const struct frame_timestamp &buf_ts) +{ + if (not m_running) { + throw runtime_error("DPDFeedbackServer not running"); + } + + boost::mutex::scoped_lock lock(burstRequest.mutex); + + if (buf.size() % sizeof(complexf) != 0) { + throw std::logic_error("Buffer for tx frame has incorrect size"); + } + + if (burstRequest.state == BurstRequestState::SaveTransmitFrame) { + const size_t n = std::min( + burstRequest.num_samples * sizeof(complexf), buf.size()); + + burstRequest.num_samples = n / sizeof(complexf); + + burstRequest.tx_samples.clear(); + burstRequest.tx_samples.resize(n); + // A frame will always begin with the NULL symbol, which contains + // no power. Instead of taking n samples at the beginning of the + // frame, we take them at the end and adapt the timestamp accordingly. + + const size_t start_ix = buf.size() - n; + copy(buf.begin() + start_ix, buf.end(), burstRequest.tx_samples.begin()); + + frame_timestamp ts = buf_ts; + ts += (1.0 * start_ix) / (sizeof(complexf) * m_sampleRate); + + burstRequest.tx_second = ts.timestamp_sec; + burstRequest.tx_pps = ts.timestamp_pps; + + // Prepare the next state + burstRequest.rx_second = ts.timestamp_sec; + burstRequest.rx_pps = ts.timestamp_pps; + burstRequest.state = BurstRequestState::SaveReceiveFrame; + + lock.unlock(); + burstRequest.mutex_notification.notify_one(); + } + else { + lock.unlock(); + } +} + +void DPDFeedbackServer::ReceiveBurstThread() +{ + try { + set_thread_name("dpdreceiveburst"); + + while (m_running) { + boost::mutex::scoped_lock lock(burstRequest.mutex); + while (burstRequest.state != BurstRequestState::SaveReceiveFrame) { + if (not m_running) break; + burstRequest.mutex_notification.wait(lock); + } + + if (not m_running) break; + + const size_t num_samps = burstRequest.num_samples; + + frame_timestamp ts; + ts.timestamp_sec = burstRequest.rx_second; + ts.timestamp_pps = burstRequest.rx_pps; + ts.timestamp_valid = true; + + // We need to free the mutex while we recv(), because otherwise we block the + // TX thread + lock.unlock(); + + const double device_time = m_device->get_real_secs(); + const double cmd_time = ts.get_real_secs(); + + std::vector buf(num_samps * sizeof(complexf)); + + const double timeout = 60; + size_t samples_read = m_device->receive_frame( + reinterpret_cast(buf.data()), + num_samps, ts, timeout); + + lock.lock(); + burstRequest.rx_samples = std::move(buf); + burstRequest.rx_samples.resize(samples_read * sizeof(complexf)); + + // The recv might have happened at another time than requested + burstRequest.rx_second = ts.timestamp_sec; + burstRequest.rx_pps = ts.timestamp_pps; + + etiLog.level(debug) << "DPD: acquired " << samples_read << + " RX feedback samples " << + "at time " << burstRequest.tx_second << " + " << + std::fixed << burstRequest.tx_pps / 16384000.0 << + " Delta=" << cmd_time - device_time; + + burstRequest.state = BurstRequestState::Acquired; + + lock.unlock(); + burstRequest.mutex_notification.notify_one(); + } + } + catch (const runtime_error &e) { + etiLog.level(error) << "DPD Feedback RX runtime error: " << e.what(); + } + catch (const std::exception &e) { + etiLog.level(error) << "DPD Feedback RX exception: " << e.what(); + } + catch (...) { + etiLog.level(error) << "DPD Feedback RX unknown exception!"; + } + + m_running.store(false); +} + +void DPDFeedbackServer::ServeFeedback() +{ + TCPSocket m_server_sock; + m_server_sock.listen(m_port); + + etiLog.level(info) << "DPD Feedback server listening on port " << m_port; + + while (m_running) { + struct sockaddr_in client; + TCPSocket client_sock = m_server_sock.accept_with_timeout(1000, &client); + + if (not client_sock.valid()) { + // No connection request received + continue; + } + + uint8_t request_version = 0; + ssize_t read = client_sock.recv(&request_version, 1, 0); + if (!read) break; // done reading + if (read < 0) { + etiLog.level(info) << + "DPD Feedback Server Client read request version failed: " << strerror(errno); + break; + } + + if (request_version != 1) { + etiLog.level(info) << "DPD Feedback Server wrong request version"; + break; + } + + uint32_t num_samples = 0; + read = client_sock.recv(&num_samples, 4, 0); + if (!read) break; // done reading + if (read < 0) { + etiLog.level(info) << + "DPD Feedback Server Client read num samples failed"; + break; + } + + // We are ready to issue the request now + { + boost::mutex::scoped_lock lock(burstRequest.mutex); + burstRequest.num_samples = num_samples; + burstRequest.state = BurstRequestState::SaveTransmitFrame; + + lock.unlock(); + } + + // Wait for the result to be ready + boost::mutex::scoped_lock lock(burstRequest.mutex); + while (burstRequest.state != BurstRequestState::Acquired) { + if (not m_running) break; + burstRequest.mutex_notification.wait(lock); + } + + burstRequest.state = BurstRequestState::None; + lock.unlock(); + + burstRequest.num_samples = std::min(burstRequest.num_samples, + std::min( + burstRequest.tx_samples.size() / sizeof(complexf), + burstRequest.rx_samples.size() / sizeof(complexf))); + + uint32_t num_samples_32 = burstRequest.num_samples; + if (client_sock.sendall(&num_samples_32, sizeof(num_samples_32)) < 0) { + etiLog.level(info) << + "DPD Feedback Server Client send num_samples failed"; + break; + } + + if (client_sock.sendall( + &burstRequest.tx_second, + sizeof(burstRequest.tx_second)) < 0) { + etiLog.level(info) << + "DPD Feedback Server Client send tx_second failed"; + break; + } + + if (client_sock.sendall( + &burstRequest.tx_pps, + sizeof(burstRequest.tx_pps)) < 0) { + etiLog.level(info) << + "DPD Feedback Server Client send tx_pps failed"; + break; + } + + const size_t frame_bytes = burstRequest.num_samples * sizeof(complexf); + + if (burstRequest.tx_samples.size() < frame_bytes) { + throw logic_error("DPD Feedback burstRequest invalid: not enough TX samples"); + } + + if (client_sock.sendall( + &burstRequest.tx_samples[0], + frame_bytes) < 0) { + etiLog.level(info) << + "DPD Feedback Server Client send tx_frame failed"; + break; + } + + if (client_sock.sendall( + &burstRequest.rx_second, + sizeof(burstRequest.rx_second)) < 0) { + etiLog.level(info) << + "DPD Feedback Server Client send rx_second failed"; + break; + } + + if (client_sock.sendall( + &burstRequest.rx_pps, + sizeof(burstRequest.rx_pps)) < 0) { + etiLog.level(info) << + "DPD Feedback Server Client send rx_pps failed"; + break; + } + + if (burstRequest.rx_samples.size() < frame_bytes) { + throw logic_error("DPD Feedback burstRequest invalid: not enough RX samples"); + } + + if (client_sock.sendall( + &burstRequest.rx_samples[0], + frame_bytes) < 0) { + etiLog.level(info) << + "DPD Feedback Server Client send rx_frame failed"; + break; + } + } +} + +void DPDFeedbackServer::ServeFeedbackThread() +{ + set_thread_name("dpdfeedbackserver"); + + while (m_running) { + try { + ServeFeedback(); + } + catch (const runtime_error &e) { + etiLog.level(error) << "DPD Feedback Server runtime error: " << e.what(); + } + catch (const std::exception &e) { + etiLog.level(error) << "DPD Feedback Server exception: " << e.what(); + } + catch (...) { + etiLog.level(error) << "DPD Feedback Server unknown exception!"; + } + + boost::this_thread::sleep(boost::posix_time::seconds(5)); + } + + m_running.store(false); +} + +} // namespace Output diff --git a/src/output/Feedback.h b/src/output/Feedback.h new file mode 100644 index 0000000..2cad508 --- /dev/null +++ b/src/output/Feedback.h @@ -0,0 +1,116 @@ +/* + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 + +DESCRIPTION: + This presents a TCP socket to an external tool which calculates + a Digital Predistortion model from a short sequence of transmit + samples and corresponding receive samples. +*/ + +/* + 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 . + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "Log.h" +#include "TimestampDecoder.h" +#include "output/SDRDevice.h" + +namespace Output { + +enum class BurstRequestState { + None, // To pending request + SaveTransmitFrame, // The TX thread has to save an outgoing frame + SaveReceiveFrame, // The RX thread has to save an incoming frame + Acquired, // Both TX and RX frames are ready +}; + +struct FeedbackBurstRequest { + // All fields in this struct are protected + mutable boost::mutex mutex; + boost::condition_variable mutex_notification; + + BurstRequestState state = BurstRequestState::None; + + // In the SaveTransmit states, num_samples complexf samples are saved into + // the vectors + size_t num_samples = 0; + + // The timestamp of the first sample of the TX buffers + uint32_t tx_second = 0; + uint32_t tx_pps = 0; // in units of 1/16384000s + + // Samples contain complexf, but since our internal representation is uint8_t + // we keep it like that + std::vector tx_samples; + + // The timestamp of the first sample of the RX buffers + uint32_t rx_second = 0; + uint32_t rx_pps = 0; + + std::vector rx_samples; // Also, actually complexf +}; + +// Serve TX samples and RX feedback samples over a TCP connection +class DPDFeedbackServer { + public: + DPDFeedbackServer( + std::shared_ptr device, + uint16_t port, // Set to 0 to disable the Feedbackserver + uint32_t sampleRate); + DPDFeedbackServer(const DPDFeedbackServer& other) = delete; + DPDFeedbackServer& operator=(const DPDFeedbackServer& other) = delete; + ~DPDFeedbackServer(); + + void set_tx_frame(const std::vector &buf, + const struct frame_timestamp& ts); + + private: + // Thread that reacts to burstRequests and receives from the SDR device + void ReceiveBurstThread(void); + + // Thread that listens for requests over TCP to get TX and RX feedback + void ServeFeedbackThread(void); + void ServeFeedback(void); + + boost::thread rx_burst_thread; + boost::thread burst_tcp_thread; + + FeedbackBurstRequest burstRequest; + + std::atomic_bool m_running; + uint16_t m_port = 0; + uint32_t m_sampleRate = 0; + std::shared_ptr m_device; +}; + +} // namespace Output diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 2ad8e57..66e93e2 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -67,6 +67,13 @@ SDR::SDR(SDRDeviceConfig& config, std::shared_ptr device) : m_config.muting = true; m_device_thread = std::thread(&SDR::process_thread_entry, this); + + if (m_config.dpdFeedbackServerPort != 0) { + m_dpd_feedback_server = make_shared( + m_device, + m_config.dpdFeedbackServerPort, + m_config.sampleRate); + } } SDR::~SDR() @@ -108,10 +115,21 @@ int SDR::process(Buffer *dataIn) if (frame.ts.fct == -1) { etiLog.level(info) << - "OutputUHD: dropping one frame with invalid FCT"; + "SDR output: dropping one frame with invalid FCT"; } else { - // TODO setup Feedback and set tx_frame + try { + m_dpd_feedback_server->set_tx_frame(frame.buf, frame.ts); + } + catch (const runtime_error& e) { + etiLog.level(warn) << + "SDR output: Feedback server failed, restarting..."; + + m_dpd_feedback_server = std::make_shared( + m_device, + m_config.dpdFeedbackServerPort, + m_config.sampleRate); + } size_t num_frames = m_queue.push_wait_if_full(frame, FRAMES_MAX_SIZE); diff --git a/src/output/SDR.h b/src/output/SDR.h index 370bc38..5593ce3 100644 --- a/src/output/SDR.h +++ b/src/output/SDR.h @@ -36,92 +36,13 @@ DESCRIPTION: #include "ModPlugin.h" #include "EtiReader.h" +#include "output/SDRDevice.h" +#include "output/Feedback.h" namespace Output { using complexf = std::complex; -enum refclk_lock_loss_behaviour_t { CRASH, IGNORE }; - -/* This structure is used as initial configuration for all SDR devices. - * It must also contain all remote-controllable settings, otherwise - * they will get lost on a modulator restart. */ -struct SDRDeviceConfig { - std::string device; - std::string subDevice; // For UHD - - long masterClockRate = 32768000; - unsigned sampleRate = 2048000; - double frequency = 0.0; - double lo_offset = 0.0; - double txgain = 0.0; - double rxgain = 0.0; - bool enableSync = false; - - // When working with timestamps, mute the frames that - // do not have a timestamp - bool muteNoTimestamps = false; - unsigned dabMode = 0; - unsigned maxGPSHoldoverTime = 0; - - /* allowed values for UHD : auto, int, sma, mimo */ - std::string refclk_src; - - /* allowed values for UHD : int, sma, mimo */ - std::string pps_src; - - /* allowed values for UHD : pos, neg */ - std::string pps_polarity; - - /* What to do when the reference clock PLL loses lock */ - refclk_lock_loss_behaviour_t refclk_lock_loss_behaviour; - - // muting can only be changed using the remote control - bool muting = false; - - // TCP port on which to serve TX and RX samples for the - // digital pre distortion learning tool - uint16_t dpdFeedbackServerPort = 0; -}; - -// Each frame contains one OFDM frame, and its -// associated timestamp -struct FrameData { - // Buffer holding frame data - std::vector buf; - - // A full timestamp contains a TIST according to standard - // and time information within MNSC with tx_second. - struct frame_timestamp ts; -}; - - -// All SDR Devices must implement the SDRDevice interface -class SDRDevice { - public: - struct RunStatistics { - size_t num_underruns; - size_t num_late_packets; - size_t num_overruns; - size_t num_frames_modulated; //TODO increment - }; - - // TODO make some functions const - virtual void tune(double lo_offset, double frequency) = 0; - virtual double get_tx_freq(void) = 0; - virtual void set_txgain(double txgain) = 0; - virtual double get_txgain(void) = 0; - virtual void transmit_frame(const struct FrameData& frame) = 0; - virtual RunStatistics get_run_statistics(void) = 0; - virtual double get_real_secs(void) = 0; - - - // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok(void) = 0; - - virtual const char* device_name(void) = 0; -}; - class SDR : public ModOutput, public RemoteControllable { public: SDR(SDRDeviceConfig& config, std::shared_ptr device); @@ -159,6 +80,8 @@ class SDR : public ModOutput, public RemoteControllable { std::shared_ptr m_device; std::string m_name; + std::shared_ptr m_dpd_feedback_server; + EtiSource *m_eti_source = nullptr; bool sourceContainsTimestamp = false; bool last_tx_time_initialised = false; diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h new file mode 100644 index 0000000..856233b --- /dev/null +++ b/src/output/SDRDevice.h @@ -0,0 +1,136 @@ +/* + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 + +DESCRIPTION: + Common interface for all SDR outputs +*/ + +/* + 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 . + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "TimestampDecoder.h" + +namespace Output { + +enum refclk_lock_loss_behaviour_t { CRASH, IGNORE }; + +using complexf = std::complex; + +/* This structure is used as initial configuration for all SDR devices. + * It must also contain all remote-controllable settings, otherwise + * they will get lost on a modulator restart. */ +struct SDRDeviceConfig { + std::string device; + std::string subDevice; // For UHD + + long masterClockRate = 32768000; + unsigned sampleRate = 2048000; + double frequency = 0.0; + double lo_offset = 0.0; + double txgain = 0.0; + double rxgain = 0.0; + bool enableSync = false; + + // When working with timestamps, mute the frames that + // do not have a timestamp + bool muteNoTimestamps = false; + unsigned dabMode = 0; + unsigned maxGPSHoldoverTime = 0; + + /* allowed values for UHD : auto, int, sma, mimo */ + std::string refclk_src; + + /* allowed values for UHD : int, sma, mimo */ + std::string pps_src; + + /* allowed values for UHD : pos, neg */ + std::string pps_polarity; + + /* What to do when the reference clock PLL loses lock */ + refclk_lock_loss_behaviour_t refclk_lock_loss_behaviour; + + // muting can only be changed using the remote control + bool muting = false; + + // TCP port on which to serve TX and RX samples for the + // digital pre distortion learning tool + uint16_t dpdFeedbackServerPort = 0; +}; + +// Each frame contains one OFDM frame, and its +// associated timestamp +struct FrameData { + // Buffer holding frame data + std::vector buf; + + // A full timestamp contains a TIST according to standard + // and time information within MNSC with tx_second. + struct frame_timestamp ts; +}; + + +// All SDR Devices must implement the SDRDevice interface +class SDRDevice { + public: + struct RunStatistics { + size_t num_underruns; + size_t num_late_packets; + size_t num_overruns; + size_t num_frames_modulated; //TODO increment + }; + + // TODO make some functions const + virtual void tune(double lo_offset, double frequency) = 0; + virtual double get_tx_freq(void) = 0; + virtual void set_txgain(double txgain) = 0; + virtual double get_txgain(void) = 0; + virtual void transmit_frame(const struct FrameData& frame) = 0; + virtual RunStatistics get_run_statistics(void) = 0; + virtual double get_real_secs(void) = 0; + virtual void set_rxgain(double rxgain) = 0; + virtual double get_rxgain(void) = 0; + virtual size_t receive_frame( + complexf *buf, + size_t num_samples, + struct frame_timestamp& ts, + double timeout_secs) = 0; + + + // Return true if GPS and reference clock inputs are ok + virtual bool is_clk_source_ok(void) = 0; + + virtual const char* device_name(void) = 0; +}; + +} // namespace Output diff --git a/src/output/Soapy.cpp b/src/output/Soapy.cpp index d6b5953..e0a6f10 100644 --- a/src/output/Soapy.cpp +++ b/src/output/Soapy.cpp @@ -88,17 +88,18 @@ Soapy::Soapy(SDRDeviceConfig& config) : etiLog.level(info) << "SoapySDR:Actual tx gain: " << m_device->getGain(SOAPY_SDR_TX, 0); - std::vector channels; - channels.push_back(0); - m_stream = m_device->setupStream(SOAPY_SDR_TX, "CF32", channels); - m_device->activateStream(m_stream); + const std::vector channels({0}); + m_tx_stream = m_device->setupStream(SOAPY_SDR_TX, "CF32", channels); + m_device->activateStream(m_tx_stream); + + m_rx_stream = m_device->setupStream(SOAPY_SDR_RX, "CF32", channels); } Soapy::~Soapy() { if (m_device != nullptr) { - if (m_stream != nullptr) { - m_device->closeStream(m_stream); + if (m_tx_stream != nullptr) { + m_device->closeStream(m_tx_stream); } SoapySDR::Device::unmake(m_device); } @@ -156,6 +157,46 @@ double Soapy::get_real_secs(void) } } +void Soapy::set_rxgain(double rxgain) +{ + m_device->setGain(SOAPY_SDR_RX, 0, m_conf.rxgain); + m_conf.rxgain = m_device->getGain(SOAPY_SDR_RX, 0); +} + +double Soapy::get_rxgain(void) +{ + return m_device->getGain(SOAPY_SDR_RX, 0); +} + +size_t Soapy::receive_frame( + complexf *buf, + size_t num_samples, + struct frame_timestamp& ts, + double timeout_secs) +{ + int flags = 0; + long long timeNs = ts.get_ns(); + const size_t numElems = num_samples; + + void *buffs[1]; + buffs[0] = buf; + + m_device->activateStream(m_rx_stream, flags, timeNs, numElems); + + auto ret = m_device->readStream(m_tx_stream, buffs, num_samples, flags, timeNs); + + m_device->deactivateStream(m_rx_stream); + + // TODO update effective receive ts + + if (ret < 0) { + throw runtime_error("Soapy readStream error: " + to_string(ret)); + } + + return ret; +} + + bool Soapy::is_clk_source_ok() { // TODO @@ -181,7 +222,7 @@ void Soapy::transmit_frame(const struct FrameData& frame) } // Stream MTU is in samples, not bytes. - const size_t mtu = m_device->getStreamMTU(m_stream); + const size_t mtu = m_device->getStreamMTU(m_tx_stream); size_t num_acc_samps = 0; while (num_acc_samps < numSamples) { @@ -192,7 +233,7 @@ void Soapy::transmit_frame(const struct FrameData& frame) int flags = 0; - auto ret = m_device->writeStream(m_stream, buffs, samps_to_send, flags); + auto ret = m_device->writeStream(m_tx_stream, buffs, samps_to_send, flags); if (ret == SOAPY_SDR_TIMEOUT) { continue; diff --git a/src/output/Soapy.h b/src/output/Soapy.h index 97076b5..c603193 100644 --- a/src/output/Soapy.h +++ b/src/output/Soapy.h @@ -68,6 +68,14 @@ class Soapy : public Output::SDRDevice virtual RunStatistics get_run_statistics(void) override; virtual double get_real_secs(void) override; + virtual void set_rxgain(double rxgain) override; + virtual double get_rxgain(void) override; + virtual size_t receive_frame( + complexf *buf, + size_t num_samples, + struct frame_timestamp& ts, + double timeout_secs) override; + // Return true if GPS and reference clock inputs are ok virtual bool is_clk_source_ok(void) override; virtual const char* device_name(void) override; @@ -75,7 +83,8 @@ class Soapy : public Output::SDRDevice private: SDRDeviceConfig& m_conf; SoapySDR::Device *m_device = nullptr; - SoapySDR::Stream *m_stream = nullptr; + SoapySDR::Stream *m_tx_stream = nullptr; + SoapySDR::Stream *m_rx_stream = nullptr; size_t underflows = 0; size_t overflows = 0; diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index 13a0a57..3769c60 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -235,6 +235,10 @@ UHD::UHD( m_usrp, m_conf.dpdFeedbackServerPort, m_conf.sampleRate); */ + const uhd::stream_args_t stream_args("fc32"); //complex floats + m_rx_stream = m_usrp->get_rx_stream(stream_args); + m_tx_stream = m_usrp->get_tx_stream(stream_args); + MDEBUG("OutputUHD:UHD ready.\n"); } @@ -342,6 +346,42 @@ double UHD::get_real_secs(void) return m_usrp->get_time_now().get_real_secs(); } +void UHD::set_rxgain(double rxgain) +{ + m_usrp->set_rx_gain(m_conf.rxgain); + m_conf.rxgain = m_usrp->get_rx_gain(); +} + +double UHD::get_rxgain() +{ + return m_usrp->get_rx_gain(); +} + +size_t UHD::receive_frame( + complexf *buf, + size_t num_samples, + struct frame_timestamp& ts, + double timeout_secs) +{ + uhd::stream_cmd_t cmd( + uhd::stream_cmd_t::stream_mode_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + cmd.num_samps = num_samples; + cmd.stream_now = false; + cmd.time_spec = uhd::time_spec_t(ts.timestamp_sec, ts.pps_offset()); + + m_rx_stream->issue_stream_cmd(cmd); + + uhd::rx_metadata_t md; + + constexpr double timeout = 60; + size_t samples_read = m_rx_stream->recv(buf, num_samples, md, timeout); + + // Update the ts with the effective receive TS + ts.timestamp_sec = md.time_spec.get_full_secs(); + ts.timestamp_pps = md.time_spec.get_frac_secs() * 16384000.0; + return samples_read; +} + // Return true if GPS and reference clock inputs are ok bool UHD::is_clk_source_ok(void) { diff --git a/src/output/UHD.h b/src/output/UHD.h index 220e4c8..3742924 100644 --- a/src/output/UHD.h +++ b/src/output/UHD.h @@ -84,6 +84,14 @@ class UHD : public Output::SDRDevice virtual RunStatistics get_run_statistics(void) override; virtual double get_real_secs(void) override; + virtual void set_rxgain(double rxgain) override; + virtual double get_rxgain(void) override; + virtual size_t receive_frame( + complexf *buf, + size_t num_samples, + struct frame_timestamp& ts, + double timeout_secs) override; + // Return true if GPS and reference clock inputs are ok virtual bool is_clk_source_ok(void) override; virtual const char* device_name(void) override; @@ -92,6 +100,7 @@ class UHD : public Output::SDRDevice SDRDeviceConfig& m_conf; uhd::usrp::multi_usrp::sptr m_usrp; uhd::tx_streamer::sptr m_tx_stream; + uhd::rx_streamer::sptr m_rx_stream; size_t num_underflows = 0; size_t num_overflows = 0; -- cgit v1.2.3 From 1d833b718845b97a5b1d90f33b547b1772bc0708 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 7 Jan 2018 08:49:29 +0100 Subject: Add new flowgraph path for metadata --- src/BlockPartitioner.cpp | 45 ++++++++++++++++++++++++------ src/BlockPartitioner.h | 21 ++++++++------ src/DabModulator.cpp | 13 +++++++-- src/DabModulator.h | 9 ++++-- src/EtiReader.cpp | 7 +++-- src/FicSource.cpp | 24 +++++++++++++++- src/FicSource.h | 11 ++++++-- src/Flowgraph.cpp | 68 ++++++++++++++++++++++++++++++++++++++++------ src/Flowgraph.h | 14 +++++++--- src/ModPlugin.h | 17 ++++++++++++ src/OutputFile.cpp | 56 ++++++++++++++++++++++++++++---------- src/OutputFile.h | 22 +++++++++++---- src/OutputMemory.cpp | 14 +++++++++- src/OutputMemory.h | 16 ++++++++--- src/TimestampDecoder.cpp | 48 ++++++++++++++++++++++++++++++-- src/TimestampDecoder.h | 71 ++++++++++++++++-------------------------------- 16 files changed, 341 insertions(+), 115 deletions(-) (limited to 'src/TimestampDecoder.h') diff --git a/src/BlockPartitioner.cpp b/src/BlockPartitioner.cpp index 9e9f80b..54405d9 100644 --- a/src/BlockPartitioner.cpp +++ b/src/BlockPartitioner.cpp @@ -1,6 +1,11 @@ /* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) + + Copyright (C) 2018 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org */ /* This file is part of ODR-DabMod. @@ -21,6 +26,8 @@ #include "BlockPartitioner.h" #include "PcDebug.h" +#include "Log.h" +#include "TimestampDecoder.h" #include #include @@ -31,6 +38,7 @@ BlockPartitioner::BlockPartitioner(unsigned mode, unsigned phase) : ModMux(), + ModMetadata(), d_mode(mode) { PDEBUG("BlockPartitioner::BlockPartitioner(%i)\n", mode); @@ -68,17 +76,11 @@ BlockPartitioner::BlockPartitioner(unsigned mode, unsigned phase) : d_cifNb = 0; // For Synchronisation purpose, count nb of CIF to drop d_cifPhase = phase % d_cifCount; + d_metaPhase = phase % d_cifCount; d_cifSize = 864 * 8; } -BlockPartitioner::~BlockPartitioner() -{ - PDEBUG("BlockPartitioner::~BlockPartitioner()\n"); - -} - - // dataIn[0] -> FIC // dataIn[1] -> CIF int BlockPartitioner::process(std::vector dataIn, Buffer* dataOut) @@ -124,10 +126,10 @@ int BlockPartitioner::process(std::vector dataIn, Buffer* dataOut) uint8_t* out = reinterpret_cast(dataOut->getData()); // Copy FIC data - PDEBUG("Writting FIC %zu bytes to %zu\n", d_ficSize, d_cifNb * d_ficSize); + PDEBUG("Writing FIC %zu bytes to %zu\n", d_ficSize, d_cifNb * d_ficSize); memcpy(out + (d_cifNb * d_ficSize), fic, d_ficSize); // Copy CIF data - PDEBUG("Writting CIF %u bytes to %zu\n", 864 * 8, + PDEBUG("Writing CIF %u bytes to %zu\n", 864 * 8, (d_cifCount * d_ficSize) + (d_cifNb * 864 * 8)); memcpy(out + (d_cifCount * d_ficSize) + (d_cifNb * 864 * 8), cif, 864 * 8); @@ -137,3 +139,28 @@ int BlockPartitioner::process(std::vector dataIn, Buffer* dataOut) return d_cifNb == 0; } + +meta_vec_t BlockPartitioner::process_metadata(const meta_vec_t& metadataIn) +{ + // Synchronize CIF phase + if (d_metaPhase != 0) { + if (++d_metaPhase == d_cifCount) { + d_metaPhase = 0; + } + // Drop this metadata + return {}; + } + + if (d_cifNb == 1) { + d_meta.clear(); + } + + std::copy(metadataIn.begin(), metadataIn.end(), std::back_inserter(d_meta)); + + if (d_cifNb == 0) { + return d_meta; + } + else { + return {}; + } +} diff --git a/src/BlockPartitioner.h b/src/BlockPartitioner.h index 90cffa3..a4656a1 100644 --- a/src/BlockPartitioner.h +++ b/src/BlockPartitioner.h @@ -1,6 +1,11 @@ /* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) + + Copyright (C) 2018 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org */ /* This file is part of ODR-DabMod. @@ -25,32 +30,32 @@ # include #endif - #include "ModPlugin.h" #include +#include -#include - - -class BlockPartitioner : public ModMux +class BlockPartitioner : public ModMux, public ModMetadata { public: BlockPartitioner(unsigned mode, unsigned phase); - virtual ~BlockPartitioner(); - BlockPartitioner(const BlockPartitioner&); - BlockPartitioner& operator=(const BlockPartitioner&); int process(std::vector dataIn, Buffer* dataOut); const char* name() { return "BlockPartitioner"; } + // The implementation assumes process_metadata is always called after process + virtual meta_vec_t process_metadata(const meta_vec_t& metadataIn); + protected: int d_mode; size_t d_ficSize; size_t d_cifCount; size_t d_cifNb; size_t d_cifPhase; + size_t d_metaPhase; size_t d_cifSize; size_t d_outputFramesize; size_t d_outputFramecount; + + meta_vec_t d_meta; }; diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 0818f4f..f1ea071 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2017 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -218,7 +218,7 @@ int DabModulator::process(Buffer* dataOut) rcs.enrol(cifPoly.get()); } - auto myOutput = make_shared(dataOut); + myOutput = make_shared(dataOut); shared_ptr cifRes; if (m_settings.outputRate != 2048000) { @@ -392,3 +392,12 @@ int DabModulator::process(Buffer* dataOut) return myFlowgraph->run(); } +meta_vec_t DabModulator::process_metadata(const meta_vec_t& metadataIn) +{ + if (myOutput) { + return myOutput->get_latest_metadata(); + } + + return {}; +} + diff --git a/src/DabModulator.h b/src/DabModulator.h index 6878853..e806c92 100644 --- a/src/DabModulator.h +++ b/src/DabModulator.h @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2017 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -46,7 +46,7 @@ #include "TII.h" -class DabModulator : public ModInput +class DabModulator : public ModInput, public ModMetadata { public: DabModulator(EtiSource& etiSource, @@ -55,6 +55,9 @@ public: int process(Buffer* dataOut); const char* name() { return "DabModulator"; } + virtual meta_vec_t process_metadata( + const meta_vec_t& metadataIn); + /* Required to get the timestamp */ EtiSource* getEtiSource() { return &myEtiSource; } @@ -73,5 +76,7 @@ protected: size_t mySymSize; size_t myFicSizeOut; size_t myFicSizeIn; + + std::shared_ptr myOutput; }; diff --git a/src/EtiReader.cpp b/src/EtiReader.cpp index d84ed1f..dc5df84 100644 --- a/src/EtiReader.cpp +++ b/src/EtiReader.cpp @@ -281,6 +281,8 @@ int EtiReader::loadEtiData(const Buffer& dataIn) myTimestampDecoder.updateTimestampEti(eti_fc.FP & 0x3, eti_eoh.MNSC, getPPSOffset(), eti_fc.FCT); + myFicSource->loadTimestamp(myTimestampDecoder.getTimestamp()); + return dataIn.getLength() - input_size; } @@ -533,8 +535,9 @@ void EdiReader::assemble() const std::time_t posix_timestamp_1_jan_2000 = 946684800; auto utc_ts = posix_timestamp_1_jan_2000 + m_seconds - m_utco; - m_timestamp_decoder.updateTimestampEdi( - utc_ts, m_fc.tsta, m_fc.fct()); + m_timestamp_decoder.updateTimestampEdi(utc_ts, m_fc.tsta, m_fc.fct(), m_fc.fp); + + myFicSource->loadTimestamp(m_timestamp_decoder.getTimestamp()); m_frameReady = true; } diff --git a/src/FicSource.cpp b/src/FicSource.cpp index 04197db..2b95085 100644 --- a/src/FicSource.cpp +++ b/src/FicSource.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2016 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -26,6 +26,8 @@ #include "FicSource.h" #include "PcDebug.h" +#include "Log.h" +#include "TimestampDecoder.h" #include #include @@ -92,3 +94,23 @@ int FicSource::process(Buffer* outputData) return outputData->getLength(); } +void FicSource::loadTimestamp(const std::shared_ptr& ts) +{ + d_ts = ts; +} + + +meta_vec_t FicSource::process_metadata(const meta_vec_t& metadataIn) +{ + if (not d_ts) { + return {}; + } + + using namespace std; + meta_vec_t md_vec; + flowgraph_metadata meta; + meta.ts = d_ts; + md_vec.push_back(meta); + return md_vec; +} + diff --git a/src/FicSource.h b/src/FicSource.h index 77ac741..93c1a7f 100644 --- a/src/FicSource.h +++ b/src/FicSource.h @@ -36,7 +36,7 @@ #include #include -class FicSource : public ModInput +class FicSource : public ModInput, public ModMetadata { public: FicSource(unsigned ficf, unsigned mid); @@ -45,12 +45,17 @@ public: const std::vector& get_rules(); void loadFicData(const Buffer& fic); - int process(Buffer* outputData); - const char* name() { return "FicSource"; } + int process(Buffer* outputData) override; + const char* name() override { return "FicSource"; } + + void loadTimestamp(const std::shared_ptr& ts); + virtual meta_vec_t process_metadata( + const meta_vec_t& metadataIn) override; private: size_t d_framesize; Buffer d_buffer; + std::shared_ptr d_ts; std::vector d_puncturing_rules; }; diff --git a/src/Flowgraph.cpp b/src/Flowgraph.cpp index 465ef41..0b80a8c 100644 --- a/src/Flowgraph.cpp +++ b/src/Flowgraph.cpp @@ -26,6 +26,8 @@ #include "Flowgraph.h" #include "PcDebug.h" +#include "Log.h" +#include "TimestampDecoder.h" #include #include #include @@ -57,9 +59,10 @@ Node::~Node() assert(myOutputBuffers.size() == 0); } -void Node::addOutputBuffer(Buffer::sptr& buffer) +void Node::addOutputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md) { myOutputBuffers.push_back(buffer); + myOutputMetadata.push_back(md); #if DEBUG std::string fname = string(myPlugin->name()) + "-" + to_string(myDebugFiles.size()) + @@ -71,7 +74,7 @@ void Node::addOutputBuffer(Buffer::sptr& buffer) #endif } -void Node::removeOutputBuffer(Buffer::sptr& buffer) +void Node::removeOutputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md) { auto it = std::find( myOutputBuffers.begin(), @@ -89,14 +92,23 @@ void Node::removeOutputBuffer(Buffer::sptr& buffer) #endif myOutputBuffers.erase(it); } + + auto mdit = std::find( + myOutputMetadata.begin(), + myOutputMetadata.end(), + md); + if (mdit != myOutputMetadata.end()) { + myOutputMetadata.erase(mdit); + } } -void Node::addInputBuffer(Buffer::sptr& buffer) +void Node::addInputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md) { myInputBuffers.push_back(buffer); + myInputMetadata.push_back(md); } -void Node::removeInputBuffer(Buffer::sptr& buffer) +void Node::removeInputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md) { auto it = std::find( myInputBuffers.begin(), @@ -105,6 +117,14 @@ void Node::removeInputBuffer(Buffer::sptr& buffer) if (it != myInputBuffers.end()) { myInputBuffers.erase(it); } + + auto mdit = std::find( + myInputMetadata.begin(), + myInputMetadata.end(), + md); + if (mdit != myInputMetadata.end()) { + myInputMetadata.erase(mdit); + } } int Node::process() @@ -127,6 +147,36 @@ int Node::process() } int ret = myPlugin->process(inBuffers, outBuffers); + + // Collect all incoming metadata into a single vector + meta_vec_t all_input_mds; + for (auto& md_vec_sp : myInputMetadata) { + if (md_vec_sp) { + copy(md_vec_sp->begin(), md_vec_sp->end(), + back_inserter(all_input_mds)); + } + } + + auto mod_meta = dynamic_pointer_cast(myPlugin); + if (mod_meta) { + auto outputMetadata = mod_meta->process_metadata(all_input_mds); + // Distribute the result metadata to all outputs + for (auto& out_md : myOutputMetadata) { + out_md->clear(); + std::move(outputMetadata.begin(), outputMetadata.end(), + std::back_inserter(*out_md)); + } + } + else { + // Propagate the unmodified input metadata to all outputs + for (auto& out_md : myOutputMetadata) { + out_md->clear(); + std::move(all_input_mds.begin(), all_input_mds.end(), + std::back_inserter(*out_md)); + } + } + + #if DEBUG assert(myDebugFiles.size() == myOutputBuffers.size()); @@ -158,8 +208,10 @@ Edge::Edge(shared_ptr& srcNode, shared_ptr& dstNode) : this); myBuffer = make_shared(); - srcNode->addOutputBuffer(myBuffer); - dstNode->addInputBuffer(myBuffer); + myMetadata = make_shared >(); + + srcNode->addOutputBuffer(myBuffer, myMetadata); + dstNode->addInputBuffer(myBuffer, myMetadata); } @@ -168,8 +220,8 @@ Edge::~Edge() PDEBUG("Edge::~Edge() @ %p\n", this); if (myBuffer) { - mySrcNode->removeOutputBuffer(myBuffer); - myDstNode->removeInputBuffer(myBuffer); + mySrcNode->removeOutputBuffer(myBuffer, myMetadata); + myDstNode->removeInputBuffer(myBuffer, myMetadata); } } diff --git a/src/Flowgraph.h b/src/Flowgraph.h index ebb7314..2742824 100644 --- a/src/Flowgraph.h +++ b/src/Flowgraph.h @@ -39,6 +39,8 @@ #include #include +using Metadata_vec_sptr = std::shared_ptr >; + class Node { public: @@ -55,15 +57,18 @@ public: myProcessTime += processTime; } - void addOutputBuffer(Buffer::sptr& buffer); - void removeOutputBuffer(Buffer::sptr& buffer); + void addOutputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md); + void removeOutputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md); - void addInputBuffer(Buffer::sptr& buffer); - void removeInputBuffer(Buffer::sptr& buffer); + void addInputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md); + void removeInputBuffer(Buffer::sptr& buffer, Metadata_vec_sptr& md); protected: std::list myInputBuffers; std::list myOutputBuffers; + std::list myInputMetadata; + std::list myOutputMetadata; + #if DEBUG std::list myDebugFiles; #endif @@ -85,6 +90,7 @@ protected: std::shared_ptr mySrcNode; std::shared_ptr myDstNode; std::shared_ptr myBuffer; + std::shared_ptr > myMetadata; }; diff --git a/src/ModPlugin.h b/src/ModPlugin.h index d3aa780..3c3e8b3 100644 --- a/src/ModPlugin.h +++ b/src/ModPlugin.h @@ -119,3 +119,20 @@ public: std::vector dataOut); virtual int process(Buffer* dataIn) = 0; }; + +struct frame_timestamp; +struct flowgraph_metadata { + std::shared_ptr ts; +}; + + +using meta_vec_t = std::vector; + +/* Some ModPlugins also support metadata */ +class ModMetadata { + public: + // Receives metadata from all inputs, and process them, and output + // a sequence of metadata. + virtual meta_vec_t process_metadata(const meta_vec_t& metadataIn) = 0; +}; + diff --git a/src/OutputFile.cpp b/src/OutputFile.cpp index 23d5523..3bb45c4 100644 --- a/src/OutputFile.cpp +++ b/src/OutputFile.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2016 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -26,47 +26,73 @@ #include "OutputFile.h" #include "PcDebug.h" +#include "Log.h" +#include "TimestampDecoder.h" #include #include #include +using namespace std; + OutputFile::OutputFile(std::string filename) : ModOutput(), + ModMetadata(), myFilename(filename) { PDEBUG("OutputFile::OutputFile(filename: %s) @ %p\n", filename.c_str(), this); - myFile = fopen(filename.c_str(), "w"); - if (myFile == NULL) { + FILE* fd = fopen(filename.c_str(), "w"); + if (fd == nullptr) { perror(filename.c_str()); throw std::runtime_error( "OutputFile::OutputFile() unable to open file!"); } -} - - -OutputFile::~OutputFile() -{ - PDEBUG("OutputFile::~OutputFile() @ %p\n", this); - - if (myFile != NULL) { - fclose(myFile); - } + myFile.reset(fd); } int OutputFile::process(Buffer* dataIn) { PDEBUG("OutputFile::process(%p)\n", dataIn); - assert(dataIn != NULL); + assert(dataIn != nullptr); - if (fwrite(dataIn->getData(), dataIn->getLength(), 1, myFile) == 0) { + if (fwrite(dataIn->getData(), dataIn->getLength(), 1, myFile.get()) == 0) { throw std::runtime_error( "OutputFile::process() unable to write to file!"); } return dataIn->getLength(); } + +meta_vec_t OutputFile::process_metadata(const meta_vec_t& metadataIn) +{ + stringstream ss; + + if (metadataIn.empty()) { + etiLog.level(debug) << "OutputFile: no mdIn"; + } + + for (const auto& md : metadataIn) { + if (md.ts) { + ss << "FCT=" << md.ts->fct << + " FP=" << (int)md.ts->fp; + if (md.ts->timestamp_valid) { + ss << " ts=" << md.ts->timestamp_sec << + "+" << md.ts->timestamp_pps << ", "; + } + else { + ss << " no ts"; + } + } + else { + ss << "void, "; + } + } + + etiLog.level(debug) << "Output File got metadata: " << ss.str(); + + return {}; +} diff --git a/src/OutputFile.h b/src/OutputFile.h index 7121ef3..2b92369 100644 --- a/src/OutputFile.h +++ b/src/OutputFile.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2016 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -36,19 +36,29 @@ #include #include #include +#include +struct FILEDeleter { + void operator()(FILE* fd) { + if (fd) { + fclose(fd); + } + } +}; -class OutputFile : public ModOutput +class OutputFile : public ModOutput, public ModMetadata { public: OutputFile(std::string filename); - virtual ~OutputFile(); - virtual int process(Buffer* dataIn); - const char* name() { return "OutputFile"; } + virtual int process(Buffer* dataIn) override; + const char* name() override { return "OutputFile"; } + + virtual meta_vec_t process_metadata( + const meta_vec_t& metadataIn) override; protected: std::string myFilename; - FILE* myFile; + std::unique_ptr myFile; }; diff --git a/src/OutputMemory.cpp b/src/OutputMemory.cpp index 6e2fd49..5f24095 100644 --- a/src/OutputMemory.cpp +++ b/src/OutputMemory.cpp @@ -2,7 +2,7 @@ Copyright (C) 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2016 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -26,6 +26,7 @@ #include "OutputMemory.h" #include "PcDebug.h" +#include "Log.h" #include #include @@ -94,3 +95,14 @@ int OutputMemory::process(Buffer* dataIn) return myDataOut->getLength(); } +meta_vec_t OutputMemory::process_metadata(const meta_vec_t& metadataIn) +{ + myMetadata = metadataIn; + return {}; +} + +meta_vec_t OutputMemory::get_latest_metadata() +{ + return myMetadata; +} + diff --git a/src/OutputMemory.h b/src/OutputMemory.h index 715cb2d..f0a5fbb 100644 --- a/src/OutputMemory.h +++ b/src/OutputMemory.h @@ -2,7 +2,7 @@ Copyright (C) 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2016 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -46,18 +46,26 @@ #include "ModPlugin.h" -class OutputMemory : public ModOutput +class OutputMemory : public ModOutput, public ModMetadata { public: OutputMemory(Buffer* dataOut); virtual ~OutputMemory(); - virtual int process(Buffer* dataIn); - const char* name() { return "OutputMemory"; } + OutputMemory(OutputMemory& other) = delete; + OutputMemory& operator=(OutputMemory& other) = delete; + + virtual int process(Buffer* dataIn) override; + const char* name() override { return "OutputMemory"; } + virtual meta_vec_t process_metadata( + const meta_vec_t& metadataIn) override; + + meta_vec_t get_latest_metadata(void); void setOutput(Buffer* dataOut); protected: Buffer* myDataOut; + meta_vec_t myMetadata; #if OUTPUT_MEM_HISTOGRAM // keep track of max value diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp index 26deb60..6cf2875 100644 --- a/src/TimestampDecoder.cpp +++ b/src/TimestampDecoder.cpp @@ -37,6 +37,22 @@ //#define MDEBUG(fmt, args...) fprintf (LOG, "*****" fmt , ## args) #define MDEBUG(fmt, args...) PDEBUG(fmt, ## args) +TimestampDecoder::TimestampDecoder(double& offset_s, unsigned tist_delay_stages) : + RemoteControllable("tist"), + timestamp_offset(offset_s), + m_tist_delay_stages(tist_delay_stages) +{ + // Properly initialise temp_time + memset(&temp_time, 0, sizeof(temp_time)); + const time_t timep = 0; + gmtime_r(&timep, &temp_time); + + RC_ADD_PARAMETER(offset, "TIST offset [s]"); + RC_ADD_PARAMETER(timestamp, "FCT and timestamp [s]"); + + etiLog.level(info) << "Setting up timestamp decoder with " << + timestamp_offset << " offset"; +} void TimestampDecoder::calculateTimestamp(frame_timestamp& ts) { @@ -48,6 +64,7 @@ void TimestampDecoder::calculateTimestamp(frame_timestamp& ts) ts_queued->timestamp_sec = time_secs; ts_queued->timestamp_pps = time_pps; ts_queued->fct = latestFCT; + ts_queued->fp = latestFP; ts_queued->timestamp_refresh = offset_changed; offset_changed = false; @@ -102,7 +119,29 @@ void TimestampDecoder::calculateTimestamp(frame_timestamp& ts) //ts.print("calc2 "); } -void TimestampDecoder::pushMNSCData(int framephase, uint16_t mnsc) +std::shared_ptr TimestampDecoder::getTimestamp() +{ + std::shared_ptr ts = + std::make_shared(); + + /* Push new timestamp into queue */ + ts->timestamp_valid = full_timestamp_received; + ts->timestamp_sec = time_secs; + ts->timestamp_pps = time_pps; + ts->fct = latestFCT; + ts->fp = latestFP; + + ts->timestamp_refresh = offset_changed; + offset_changed = false; + + MDEBUG("time_secs=%d, time_pps=%f\n", time_secs, + (double)time_pps / 16384000.0); + *ts += timestamp_offset; + + return ts; +} + +void TimestampDecoder::pushMNSCData(uint8_t framephase, uint16_t mnsc) { struct eti_MNSC_TIME_0 *mnsc0; struct eti_MNSC_TIME_1 *mnsc1; @@ -190,7 +229,7 @@ void TimestampDecoder::updateTimestampPPS(uint32_t pps) } void TimestampDecoder::updateTimestampEti( - int framephase, + uint8_t framephase, uint16_t mnsc, uint32_t pps, // In units of 1/16384000 s int32_t fct) @@ -198,16 +237,19 @@ void TimestampDecoder::updateTimestampEti( updateTimestampPPS(pps); pushMNSCData(framephase, mnsc); latestFCT = fct; + latestFP = framephase; } void TimestampDecoder::updateTimestampEdi( uint32_t seconds_utc, uint32_t pps, // In units of 1/16384000 s - int32_t fct) + int32_t fct, + uint8_t framephase) { time_secs = seconds_utc; time_pps = pps; latestFCT = fct; + latestFP = framephase; full_timestamp_received = true; } diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index 943b241..4bbf5cc 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -40,6 +40,7 @@ struct frame_timestamp { // Which frame count does this timestamp apply to int32_t fct; + uint8_t fp; // Frame Phase uint32_t timestamp_sec; uint32_t timestamp_pps; // In units of 1/16384000 s @@ -56,6 +57,7 @@ struct frame_timestamp this->timestamp_valid = rhs.timestamp_valid; this->timestamp_refresh = rhs.timestamp_refresh; this->fct = rhs.fct; + this->fp = rhs.fp; } return *this; @@ -103,49 +105,23 @@ struct frame_timestamp class TimestampDecoder : public RemoteControllable { public: - TimestampDecoder( - /* 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; - latestFCT = 0; - enableDecode = false; - full_timestamp_received = false; - - // Properly initialise temp_time - memset(&temp_time, 0, sizeof(temp_time)); - const time_t timep = 0; - gmtime_r(&timep, &temp_time); - - offset_changed = false; - - RC_ADD_PARAMETER(offset, "TIST offset [s]"); - RC_ADD_PARAMETER(timestamp, "FCT and timestamp [s]"); - - etiLog.level(info) << "Setting up timestamp decoder with " << - timestamp_offset << " offset"; - - }; + /* offset_s: The modulator adds this offset to the TIST to define time of + * frame transmission + * + * tist_delay_stages: 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 + */ + TimestampDecoder(double& offset_s, unsigned tist_delay_stages); /* Calculate the timestamp for the current frame. */ void calculateTimestamp(frame_timestamp& ts); + std::shared_ptr getTimestamp(void); + /* Update timestamp data from ETI */ void updateTimestampEti( - int framephase, + uint8_t framephase, uint16_t mnsc, uint32_t pps, // In units of 1/16384000 s int32_t fct); @@ -154,7 +130,8 @@ class TimestampDecoder : public RemoteControllable void updateTimestampEdi( uint32_t seconds_utc, uint32_t pps, // In units of 1/16384000 s - int32_t fct); + int32_t fct, + uint8_t framephase); /*********** REMOTE CONTROL ***************/ @@ -171,7 +148,7 @@ class TimestampDecoder : public RemoteControllable protected: /* Push a new MNSC field into the decoder */ - void pushMNSCData(int framephase, uint16_t mnsc); + void pushMNSCData(uint8_t framephase, uint16_t mnsc); /* Each frame contains the TIST field with the PPS offset. * For each frame, this function must be called to update @@ -191,21 +168,22 @@ class TimestampDecoder : public RemoteControllable void updateTimestampSeconds(uint32_t secs); struct tm temp_time; - uint32_t time_secs; - int32_t latestFCT; - uint32_t time_pps; + uint32_t time_secs = 0; + int32_t latestFCT = 0; + uint32_t latestFP = 0; + uint32_t time_pps = 0; double& timestamp_offset; unsigned m_tist_delay_stages; - int inhibit_second_update; - bool offset_changed; + int inhibit_second_update = 0; + bool offset_changed = false; /* When the type or identifier don't match, the decoder must * be disabled */ - bool enableDecode; + bool enableDecode = false; /* Disable timstamps until full time has been received */ - bool full_timestamp_received; + bool full_timestamp_received = false; /* when pipelining, we must shift the calculated timestamps * through this queue. Otherwise, it would not be possible to @@ -213,6 +191,5 @@ class TimestampDecoder : public RemoteControllable * FIRFilter (1 stage pipeline) */ std::queue > queue_timestamps; - }; -- cgit v1.2.3 From 1e76247e059021ea52309ebd258b6eed033d6aee Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 7 Jan 2018 11:11:12 +0100 Subject: Remove calculateTimestamp --- src/ConfigParser.h | 1 - src/DabMod.cpp | 38 ++------------------------ src/EtiReader.cpp | 20 +++----------- src/EtiReader.h | 11 ++------ src/OutputFile.cpp | 10 ------- src/OutputFile.h | 5 ---- src/TimestampDecoder.cpp | 70 ++---------------------------------------------- src/TimestampDecoder.h | 19 ++----------- src/output/SDR.cpp | 5 ---- src/output/SDR.h | 3 --- 10 files changed, 12 insertions(+), 170 deletions(-) (limited to 'src/TimestampDecoder.h') diff --git a/src/ConfigParser.h b/src/ConfigParser.h index 3f64883..0463470 100644 --- a/src/ConfigParser.h +++ b/src/ConfigParser.h @@ -58,7 +58,6 @@ struct mod_settings_t { float gainmodeVariance = 4.0f; // To handle the timestamp offset of the modulator - unsigned tist_delay_stages = 1; // because GainControl is pipelined double tist_offset_s = 0.0; bool loop = false; diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 9880938..31fa76d 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -269,12 +269,6 @@ int launch_modulator(int argc, char* argv[]) 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; @@ -295,7 +289,7 @@ int launch_modulator(int argc, char* argv[]) set_thread_name("modulator"); if (mod_settings.inputTransport == "edi") { - EdiReader ediReader(mod_settings.tist_offset_s, mod_settings.tist_delay_stages); + EdiReader ediReader(mod_settings.tist_offset_s); 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 @@ -320,17 +314,6 @@ int launch_modulator(int argc, char* argv[]) flowgraph.connect(modulator, output); } - if (false -#if defined(HAVE_OUTPUT_UHD) - or mod_settings.useUHDOutput -#endif -#if defined(HAVE_SOAPYSDR) - or mod_settings.useSoapyOutput -#endif - ) { - ((Output::SDR*)output.get())->setETISource(modulator->getEtiSource()); - } - size_t framecount = 0; while (running) { @@ -400,7 +383,7 @@ int launch_modulator(int argc, char* argv[]) m.flowgraph = &flowgraph; m.data.setLength(6144); - EtiReader etiReader(mod_settings.tist_offset_s, mod_settings.tist_delay_stages); + EtiReader etiReader(mod_settings.tist_offset_s); m.etiReader = &etiReader; auto input = make_shared(&m.data); @@ -414,23 +397,6 @@ int launch_modulator(int argc, char* argv[]) flowgraph.connect(modulator, output); } - if (false -#if defined(HAVE_OUTPUT_UHD) - or mod_settings.useUHDOutput -#endif -#if defined(HAVE_SOAPYSDR) - or mod_settings.useSoapyOutput -#endif - ) { - ((Output::SDR*)output.get())->setETISource(modulator->getEtiSource()); - } - - // TODO remove - auto output_as_file = dynamic_pointer_cast(output); - if (output_as_file) { - output_as_file->setETISource(modulator->getEtiSource()); - } - inputReader->PrintInfo(); run_modulator_state_t st = run_modulator(m); diff --git a/src/EtiReader.cpp b/src/EtiReader.cpp index dc5df84..17f4953 100644 --- a/src/EtiReader.cpp +++ b/src/EtiReader.cpp @@ -54,10 +54,9 @@ enum ETI_READER_STATE { EtiReader::EtiReader( - double& tist_offset_s, - unsigned tist_delay_stages) : + double& tist_offset_s) : state(EtiReaderStateSync), - myTimestampDecoder(tist_offset_s, tist_delay_stages), + myTimestampDecoder(tist_offset_s), eti_fc_valid(false) { rcs.enrol(&myTimestampDecoder); @@ -292,11 +291,6 @@ bool EtiReader::sourceContainsTimestamp() /* See ETS 300 799, Annex C.2.2 */ } -void EtiReader::calculateTimestamp(struct frame_timestamp& ts) -{ - myTimestampDecoder.calculateTimestamp(ts); -} - uint32_t EtiReader::getPPSOffset() { if (!sourceContainsTimestamp()) { @@ -311,9 +305,8 @@ uint32_t EtiReader::getPPSOffset() } EdiReader::EdiReader( - double& tist_offset_s, - unsigned tist_delay_stages) : - m_timestamp_decoder(tist_offset_s, tist_delay_stages) + double& tist_offset_s) : + m_timestamp_decoder(tist_offset_s) { rcs.enrol(&m_timestamp_decoder); } @@ -361,11 +354,6 @@ bool EdiReader::sourceContainsTimestamp() return m_fc.tsta != 0xFFFFFF; } -void EdiReader::calculateTimestamp(struct frame_timestamp& ts) -{ - m_timestamp_decoder.calculateTimestamp(ts); -} - bool EdiReader::isFrameReady() { return m_frameReady; diff --git a/src/EtiReader.h b/src/EtiReader.h index f3a9764..8270592 100644 --- a/src/EtiReader.h +++ b/src/EtiReader.h @@ -59,7 +59,6 @@ public: /* Returns true if we have valid time stamps in the ETI*/ virtual bool sourceContainsTimestamp() = 0; - virtual void calculateTimestamp(struct frame_timestamp& ts) = 0; /* Return the FIC source to be used for modulation */ virtual std::shared_ptr& getFic(void); @@ -75,9 +74,7 @@ protected: class EtiReader : public EtiSource { public: - EtiReader( - double& tist_offset_s, - unsigned tist_delay_stages); + EtiReader(double& tist_offset_s); virtual unsigned getMode(); virtual unsigned getFp(); @@ -88,7 +85,6 @@ public: int loadEtiData(const Buffer& dataIn); virtual bool sourceContainsTimestamp(); - virtual void calculateTimestamp(struct frame_timestamp& ts); virtual const std::vector > getSubchannels() const; @@ -118,14 +114,11 @@ private: class EdiReader : public EtiSource, public EdiDecoder::DataCollector { public: - EdiReader( - double& tist_offset_s, - unsigned tist_delay_stages); + EdiReader(double& tist_offset_s); virtual unsigned getMode(); virtual unsigned getFp(); virtual bool sourceContainsTimestamp(); - virtual void calculateTimestamp(struct frame_timestamp& ts); virtual const std::vector > getSubchannels() const; virtual bool isFrameReady(void); diff --git a/src/OutputFile.cpp b/src/OutputFile.cpp index 481e858..1c2f5ed 100644 --- a/src/OutputFile.cpp +++ b/src/OutputFile.cpp @@ -85,18 +85,8 @@ meta_vec_t OutputFile::process_metadata(const meta_vec_t& metadataIn) } } - if (myEtiSource) { - frame_timestamp ts; - myEtiSource->calculateTimestamp(ts); - ss << " ETI FCT=" << ts.fct; - } - etiLog.level(debug) << "Output File got metadata: " << ss.str(); return {}; } -void OutputFile::setETISource(EtiSource *etiSource) -{ - myEtiSource = etiSource; -} diff --git a/src/OutputFile.h b/src/OutputFile.h index a586921..fedea40 100644 --- a/src/OutputFile.h +++ b/src/OutputFile.h @@ -50,12 +50,7 @@ public: virtual meta_vec_t process_metadata( const meta_vec_t& metadataIn) override; - void setETISource(EtiSource *etiSource); - protected: - // TODO remove - EtiSource *myEtiSource = nullptr; - std::string myFilename; struct FILEDeleter{ void operator()(FILE* fd){ if (fd) fclose(fd); }}; diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp index 6cf2875..523551c 100644 --- a/src/TimestampDecoder.cpp +++ b/src/TimestampDecoder.cpp @@ -37,10 +37,9 @@ //#define MDEBUG(fmt, args...) fprintf (LOG, "*****" fmt , ## args) #define MDEBUG(fmt, args...) PDEBUG(fmt, ## args) -TimestampDecoder::TimestampDecoder(double& offset_s, unsigned tist_delay_stages) : +TimestampDecoder::TimestampDecoder(double& offset_s) : RemoteControllable("tist"), - timestamp_offset(offset_s), - m_tist_delay_stages(tist_delay_stages) + timestamp_offset(offset_s) { // Properly initialise temp_time memset(&temp_time, 0, sizeof(temp_time)); @@ -54,71 +53,6 @@ TimestampDecoder::TimestampDecoder(double& offset_s, unsigned tist_delay_stages) timestamp_offset << " offset"; } -void TimestampDecoder::calculateTimestamp(frame_timestamp& ts) -{ - std::shared_ptr ts_queued = - std::make_shared(); - - /* Push new timestamp into queue */ - ts_queued->timestamp_valid = full_timestamp_received; - ts_queued->timestamp_sec = time_secs; - ts_queued->timestamp_pps = time_pps; - ts_queued->fct = latestFCT; - ts_queued->fp = latestFP; - - ts_queued->timestamp_refresh = offset_changed; - offset_changed = false; - - MDEBUG("time_secs=%d, time_pps=%f\n", time_secs, - (double)time_pps / 16384000.0); - *ts_queued += timestamp_offset; - - queue_timestamps.push(ts_queued); - - /* Here, the queue size is one more than the pipeline delay, because - * we've just added a new element in the queue. - * - * Therefore, use <= and not < for comparison - */ - 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; - ts.timestamp_pps = 0; - ts.timestamp_refresh = false; - ts.fct = -1; - } - else { - //fprintf(stderr, ". %zu ", queue_timestamps.size()); - /* Return timestamp from queue */ - ts_queued = queue_timestamps.front(); - queue_timestamps.pop(); - /*fprintf(stderr, "ts_queued v:%d, sec:%d, pps:%f, ref:%d\n", - ts_queued->timestamp_valid, - ts_queued->timestamp_sec, - ts_queued->timestamp_pps_offset, - ts_queued->timestamp_refresh);*/ - ts = *ts_queued; - /*fprintf(stderr, "ts v:%d, sec:%d, pps:%f, ref:%d\n\n", - ts.timestamp_valid, - ts.timestamp_sec, - ts.timestamp_pps_offset, - ts.timestamp_refresh);*/ - } - - MDEBUG("Timestamp queue size %zu, delay_calc %u\n", - queue_timestamps.size(), - m_tist_delay_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 !"; - } - - //ts.print("calc2 "); -} - std::shared_ptr TimestampDecoder::getTimestamp() { std::shared_ptr ts = diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index 0953f76..000156d 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) 2017 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -119,15 +119,8 @@ class TimestampDecoder : public RemoteControllable public: /* offset_s: The modulator adds this offset to the TIST to define time of * frame transmission - * - * tist_delay_stages: 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 */ - TimestampDecoder(double& offset_s, unsigned tist_delay_stages); - - /* Calculate the timestamp for the current frame. */ - void calculateTimestamp(frame_timestamp& ts); + TimestampDecoder(double& offset_s); std::shared_ptr getTimestamp(void); @@ -185,7 +178,6 @@ class TimestampDecoder : public RemoteControllable uint32_t latestFP = 0; uint32_t time_pps = 0; double& timestamp_offset; - unsigned m_tist_delay_stages; int inhibit_second_update = 0; bool offset_changed = false; @@ -196,12 +188,5 @@ class TimestampDecoder : public RemoteControllable /* Disable timstamps until full time has been received */ bool full_timestamp_received = false; - - /* when pipelining, we must shift the calculated timestamps - * through this queue. Otherwise, it would not be possible to - * synchronise two modulators if only one uses (for instance) the - * FIRFilter (1 stage pipeline) - */ - std::queue > queue_timestamps; }; diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index d7d777f..5290e5d 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -217,11 +217,6 @@ const char* SDR::name() return m_name.c_str(); } -void SDR::setETISource(EtiSource *etiSource) -{ - m_eti_source = etiSource; -} - void SDR::sleep_through_frame() { struct timespec now; diff --git a/src/output/SDR.h b/src/output/SDR.h index ea787c3..8affaf9 100644 --- a/src/output/SDR.h +++ b/src/output/SDR.h @@ -55,8 +55,6 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { virtual const char* name() override; - void setETISource(EtiSource *etiSource); - /*********** REMOTE CONTROL ***************/ /* Base function to set parameters. */ @@ -85,7 +83,6 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { std::shared_ptr m_dpd_feedback_server; - EtiSource *m_eti_source = nullptr; bool sourceContainsTimestamp = false; bool last_tx_time_initialised = false; uint32_t last_tx_second = 0; -- cgit v1.2.3 From 28d794ab7d47e4f2585ac73f8a69c9c68962b678 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 7 Jan 2018 11:51:03 +0100 Subject: Make frame_timestamp movable --- src/Flowgraph.cpp | 3 ++- src/OutputFile.cpp | 17 ++++++++++++----- src/TimestampDecoder.h | 16 ---------------- 3 files changed, 14 insertions(+), 22 deletions(-) (limited to 'src/TimestampDecoder.h') diff --git a/src/Flowgraph.cpp b/src/Flowgraph.cpp index e12ff1e..ae86417 100644 --- a/src/Flowgraph.cpp +++ b/src/Flowgraph.cpp @@ -151,8 +151,9 @@ int Node::process() meta_vec_t all_input_mds; for (auto& md_vec_sp : myInputMetadata) { if (md_vec_sp) { - copy(md_vec_sp->begin(), md_vec_sp->end(), + move(md_vec_sp->begin(), md_vec_sp->end(), back_inserter(all_input_mds)); + md_vec_sp->clear(); } } diff --git a/src/OutputFile.cpp b/src/OutputFile.cpp index 1c2f5ed..b7bf59a 100644 --- a/src/OutputFile.cpp +++ b/src/OutputFile.cpp @@ -71,21 +71,28 @@ meta_vec_t OutputFile::process_metadata(const meta_vec_t& metadataIn) { stringstream ss; - if (metadataIn.empty()) { - etiLog.level(debug) << "OutputFile: no mdIn"; - } - for (const auto& md : metadataIn) { if (md.ts) { ss << " FCT=" << md.ts->fct << " FP=" << (int)md.ts->fp; + if (md.ts->timestamp_valid) { + ss << " TS=" << md.ts->timestamp_sec << ";"; + } + else { + ss << " No TS;"; + } } else { ss << " void, "; } } - etiLog.level(debug) << "Output File got metadata: " << ss.str(); + if (metadataIn.empty()) { + etiLog.level(debug) << "Output File got no mdIn"; + } + else { + etiLog.level(debug) << "Output File got metadata: " << ss.str(); + } return {}; } diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index cffba2f..ff52e4c 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -47,22 +47,6 @@ struct frame_timestamp bool timestamp_valid = false; bool timestamp_refresh; - frame_timestamp() = default; - frame_timestamp(const frame_timestamp& other) = default; - frame_timestamp& operator=(const frame_timestamp &rhs) - { - if (this != &rhs) { - this->timestamp_sec = rhs.timestamp_sec; - this->timestamp_pps = rhs.timestamp_pps; - this->timestamp_valid = rhs.timestamp_valid; - this->timestamp_refresh = rhs.timestamp_refresh; - this->fct = rhs.fct; - this->fp = rhs.fp; - } - - return *this; - } - frame_timestamp& operator+=(const double& diff) { double offset_pps, offset_secs; -- cgit v1.2.3 From 4f7636694a752fa8c652c684a3c9ebf1488ceaa4 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 17 Jan 2018 10:25:20 +0100 Subject: Cosmetics for TimestampDecoder --- src/TimestampDecoder.cpp | 29 ++++++++++------------------- src/TimestampDecoder.h | 16 +++++----------- 2 files changed, 15 insertions(+), 30 deletions(-) (limited to 'src/TimestampDecoder.h') diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp index 5bede08..b942c37 100644 --- a/src/TimestampDecoder.cpp +++ b/src/TimestampDecoder.cpp @@ -24,15 +24,13 @@ along with ODR-DabMod. If not, see . */ -#include #include #include #include -#include #include "PcDebug.h" #include "TimestampDecoder.h" -#include "Eti.h" #include "Log.h" +#include "Eti.h" //#define MDEBUG(fmt, args...) fprintf (LOG, "*****" fmt , ## args) #define MDEBUG(fmt, args...) PDEBUG(fmt, ## args) @@ -55,10 +53,8 @@ TimestampDecoder::TimestampDecoder(double& offset_s) : std::shared_ptr TimestampDecoder::getTimestamp() { - std::shared_ptr ts = - std::make_shared(); + auto ts = std::make_shared(); - /* Push new timestamp into queue */ ts->timestamp_valid = full_timestamp_received; ts->timestamp_sec = time_secs; ts->timestamp_pps = time_pps; @@ -82,8 +78,7 @@ void TimestampDecoder::pushMNSCData(uint8_t framephase, uint16_t mnsc) struct eti_MNSC_TIME_2 *mnsc2; struct eti_MNSC_TIME_3 *mnsc3; - switch (framephase) - { + switch (framephase) { case 0: mnsc0 = (struct eti_MNSC_TIME_0*)&mnsc; enableDecode = (mnsc0->type == 0) && @@ -99,10 +94,10 @@ void TimestampDecoder::pushMNSCData(uint8_t framephase, uint16_t mnsc) temp_time.tm_sec = mnsc1->second_tens * 10 + mnsc1->second_unit; temp_time.tm_min = mnsc1->minute_tens * 10 + mnsc1->minute_unit; - if (!mnsc1->sync_to_frame) - { + if (!mnsc1->sync_to_frame) { enableDecode = false; - PDEBUG("TimestampDecoder: MNSC time info is not synchronised to frame\n"); + PDEBUG("TimestampDecoder: " + "MNSC time info is not synchronised to frame\n"); } break; @@ -118,8 +113,7 @@ void TimestampDecoder::pushMNSCData(uint8_t framephase, uint16_t mnsc) temp_time.tm_mon = (mnsc3->month_tens * 10 + mnsc3->month_unit) - 1; temp_time.tm_year = (mnsc3->year_tens * 10 + mnsc3->year_unit) + 100; - if (enableDecode) - { + if (enableDecode) { updateTimestampSeconds(mktime(&temp_time)); } break; @@ -132,13 +126,11 @@ void TimestampDecoder::pushMNSCData(uint8_t framephase, uint16_t mnsc) void TimestampDecoder::updateTimestampSeconds(uint32_t secs) { - if (inhibit_second_update > 0) - { + if (inhibit_second_update > 0) { MDEBUG("TimestampDecoder::updateTimestampSeconds(%d) inhibit\n", secs); inhibit_second_update--; } - else - { + else { MDEBUG("TimestampDecoder::updateTimestampSeconds(%d) apply\n", secs); time_secs = secs; full_timestamp_received = true; @@ -149,8 +141,7 @@ void TimestampDecoder::updateTimestampPPS(uint32_t pps) { MDEBUG("TimestampDecoder::updateTimestampPPS(%f)\n", (double)pps / 16384000.0); - if (time_pps > pps) // Second boundary crossed - { + if (time_pps > pps) { // Second boundary crossed MDEBUG("TimestampDecoder::updateTimestampPPS crossed second\n"); // The second for the next eight frames will not diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index ff52e4c..1a13394 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -26,14 +26,11 @@ #pragma once -#include +#include #include #include -#include #include #include -#include "Eti.h" -#include "Log.h" #include "RemoteControl.h" struct frame_timestamp @@ -55,16 +52,14 @@ struct frame_timestamp this->timestamp_sec += lrintf(offset_secs); this->timestamp_pps += lrintf(offset_pps * 16384000.0); - while (this->timestamp_pps >= 16384000) - { + while (this->timestamp_pps >= 16384000) { this->timestamp_pps -= 16384000; this->timestamp_sec += 1; - }; + } return *this; } - const frame_timestamp operator+(const double diff) - { + const frame_timestamp operator+(const double diff) { frame_timestamp ts = *this; ts += diff; return ts; @@ -86,8 +81,7 @@ struct frame_timestamp return ns; } - void print(const char* t) - { + void print(const char* t) const { fprintf(stderr, "%s \n", t, this->timestamp_valid ? "valid" : "invalid", -- cgit v1.2.3 From 4325e313a85cccfd7be6fff6e14511ffc3e524f0 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Thu, 18 Jan 2018 08:39:08 +0100 Subject: Fix frame_timestamp get_real_secs() --- src/TimestampDecoder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/TimestampDecoder.h') diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index 1a13394..33d9992 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -71,7 +71,7 @@ struct frame_timestamp double get_real_secs() const { double t = timestamp_sec; - t += timestamp_pps; + t += pps_offset(); return t; } -- cgit v1.2.3