summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2017-11-04 08:37:48 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2017-11-04 08:37:48 +0100
commit863c1eea672314d9a5834c740d151ef2b0130cf3 (patch)
treeef17e41c5a09e0f10d99662bdad574c1652fe7bd
parent4d8310ae0ffe1f78a2b8623d55f63ae504ff1aa8 (diff)
downloaddabmod-863c1eea672314d9a5834c740d151ef2b0130cf3.tar.gz
dabmod-863c1eea672314d9a5834c740d151ef2b0130cf3.tar.bz2
dabmod-863c1eea672314d9a5834c740d151ef2b0130cf3.zip
Remove OutputUHD files
-rw-r--r--src/OutputUHD.cpp1035
-rw-r--r--src/OutputUHD.h250
-rw-r--r--src/OutputUHDFeedback.cpp362
-rw-r--r--src/OutputUHDFeedback.h119
-rw-r--r--src/output/UHD.h1
5 files changed, 0 insertions, 1767 deletions
diff --git a/src/OutputUHD.cpp b/src/OutputUHD.cpp
deleted file mode 100644
index e1fe9dd..0000000
--- a/src/OutputUHD.cpp
+++ /dev/null
@@ -1,1035 +0,0 @@
-/*
- 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
- */
-/*
- This file is part of ODR-DabMod.
-
- ODR-DabMod is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- ODR-DabMod is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "OutputUHD.h"
-
-#ifdef HAVE_OUTPUT_UHD
-
-#include "PcDebug.h"
-#include "Log.h"
-#include "RemoteControl.h"
-#include "Utils.h"
-
-#include <boost/thread/future.hpp>
-
-#include <uhd/utils/msg.hpp>
-
-#include <cmath>
-#include <iostream>
-#include <assert.h>
-#include <stdexcept>
-#include <stdio.h>
-#include <time.h>
-#include <errno.h>
-#include <unistd.h>
-#include <pthread.h>
-
-using namespace std;
-
-// Maximum number of frames that can wait in frames
-static const size_t FRAMES_MAX_SIZE = 8;
-
-typedef std::complex<float> complexf;
-
-std::string stringtrim(const std::string &s)
-{
- auto wsfront = std::find_if_not(s.begin(), s.end(),
- [](int c){ return std::isspace(c);} );
- return std::string(wsfront,
- std::find_if_not(s.rbegin(),
- std::string::const_reverse_iterator(wsfront),
- [](int c){ return std::isspace(c);} ).base());
-}
-
-void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
-{
- if (type == uhd::msg::warning) {
- etiLog.level(warn) << "UHD Warning: " << msg;
- }
- else if (type == uhd::msg::error) {
- etiLog.level(error) << "UHD Error: " << msg;
- }
- else {
- // do not print very short U messages and such
- if (stringtrim(msg).size() != 1) {
- etiLog.level(debug) << "UHD Message: " << msg;
- }
- }
-}
-
-static void tune_usrp_to(
- uhd::usrp::multi_usrp::sptr usrp,
- double lo_offset,
- double frequency)
-{
- if (lo_offset != 0.0) {
- etiLog.level(info) << std::fixed << std::setprecision(3) <<
- "OutputUHD:Setting freq to " << frequency <<
- " with LO offset " << lo_offset << "...";
-
- const auto tr = uhd::tune_request_t(frequency, lo_offset);
- uhd::tune_result_t result = usrp->set_tx_freq(tr);
-
- etiLog.level(debug) << "OutputUHD:" <<
- std::fixed << std::setprecision(0) <<
- " Target RF: " << result.target_rf_freq <<
- " Actual RF: " << result.actual_rf_freq <<
- " Target DSP: " << result.target_dsp_freq <<
- " Actual DSP: " << result.actual_dsp_freq;
- }
- else {
- //set the centre frequency
- etiLog.level(info) << std::fixed << std::setprecision(3) <<
- "OutputUHD:Setting freq to " << frequency << "...";
- usrp->set_tx_freq(frequency);
- }
-
- usrp->set_rx_freq(frequency);
-}
-
-// Check function for GPS TIMELOCK sensor from the ODR LEA-M8F board GPSDO
-bool check_gps_timelock(uhd::usrp::multi_usrp::sptr usrp)
-{
- try {
- std::string sensor_value(
- usrp->get_mboard_sensor("gps_timelock", 0).to_pp_string());
-
- if (sensor_value.find("TIME LOCKED") == std::string::npos) {
- etiLog.level(warn) << "OutputUHD: gps_timelock " << sensor_value;
- return false;
- }
-
- return true;
- }
- catch (uhd::lookup_error &e) {
- etiLog.level(warn) << "OutputUHD: no gps_timelock sensor";
- return false;
- }
-}
-
-// Check function for GPS LOCKED sensor from the Ettus GPSDO
-bool check_gps_locked(uhd::usrp::multi_usrp::sptr usrp)
-{
- try {
- uhd::sensor_value_t sensor_value(
- usrp->get_mboard_sensor("gps_locked", 0));
- if (not sensor_value.to_bool()) {
- etiLog.level(warn) << "OutputUHD: gps_locked " <<
- sensor_value.to_pp_string();
- return false;
- }
-
- return true;
- }
- catch (uhd::lookup_error &e) {
- etiLog.level(warn) << "OutputUHD: no gps_locked sensor";
- return false;
- }
-}
-
-
-OutputUHD::OutputUHD(
- OutputUHDConfig& config) :
- ModOutput(),
- RemoteControllable("uhd"),
- myConf(config),
- // Since we don't know the buffer size, we cannot initialise
- // the buffers at object initialisation.
- myDelayBuf(0),
- running(false)
-{
- myConf.muting = true; // is remote-controllable, and reset by the GPS fix check
- myConf.staticDelayUs = 0; // is remote-controllable
-
- // Variables needed for GPS fix check
- first_gps_fix_check.tv_sec = 0;
- last_gps_fix_check.tv_sec = 0;
- time_last_frame.tv_sec = 0;
-
-
- std::stringstream device;
- device << myConf.device;
-
- if (myConf.masterClockRate != 0) {
- if (device.str() != "") {
- device << ",";
- }
- device << "master_clock_rate=" << myConf.masterClockRate;
- }
-
- if (myConf.usrpType != "") {
- if (device.str() != "") {
- device << ",";
- }
- device << "type=" << myConf.usrpType;
- }
-
- MDEBUG("OutputUHD::OutputUHD(device: %s) @ %p\n",
- device.str().c_str(), this);
-
- /* register the parameters that can be remote controlled */
- RC_ADD_PARAMETER(txgain, "UHD analog daughterboard TX gain");
- RC_ADD_PARAMETER(rxgain, "UHD analog daughterboard RX gain for DPD feedback");
- RC_ADD_PARAMETER(freq, "UHD transmission frequency");
- RC_ADD_PARAMETER(muting, "Mute the output by stopping the transmitter");
- RC_ADD_PARAMETER(staticdelay, "Set static delay (uS) between 0 and 96000");
- RC_ADD_PARAMETER(underruns, "Read-only counter of number of underruns");
- RC_ADD_PARAMETER(latepackets, "Read-only counter of number of late packets");
- RC_ADD_PARAMETER(frames, "Read-only counter of number of frames modulated");
-
- uhd::msg::register_handler(uhd_msg_handler);
-
- uhd::set_thread_priority_safe();
-
- etiLog.log(info, "OutputUHD:Creating the usrp device with: %s...",
- device.str().c_str());
-
- myUsrp = uhd::usrp::multi_usrp::make(device.str());
-
- etiLog.log(info, "OutputUHD:Using device: %s...",
- myUsrp->get_pp_string().c_str());
-
- if (myConf.masterClockRate != 0.0) {
- double master_clk_rate = myUsrp->get_master_clock_rate();
- etiLog.log(debug, "OutputUHD:Checking master clock rate: %f...",
- master_clk_rate);
-
- if (fabs(master_clk_rate - myConf.masterClockRate) >
- (myConf.masterClockRate * 1e-6)) {
- throw std::runtime_error("Cannot set USRP master_clock_rate. Aborted.");
- }
- }
-
- MDEBUG("OutputUHD:Setting REFCLK and PPS input...\n");
-
- if (myConf.refclk_src == "gpsdo-ettus") {
- myUsrp->set_clock_source("gpsdo");
- }
- else {
- myUsrp->set_clock_source(myConf.refclk_src);
- }
- myUsrp->set_time_source(myConf.pps_src);
-
- if (myConf.subDevice != "") {
- myUsrp->set_tx_subdev_spec(uhd::usrp::subdev_spec_t(myConf.subDevice),
- uhd::usrp::multi_usrp::ALL_MBOARDS);
- }
-
- etiLog.level(debug) << "UHD clock source is " << myUsrp->get_clock_source(0);
-
- etiLog.level(debug) << "UHD time source is " << myUsrp->get_time_source(0);
-
- myUsrp->set_tx_rate(myConf.sampleRate);
- etiLog.log(debug, "OutputUHD:Set rate to %d. Actual TX Rate: %f sps...",
- myConf.sampleRate, myUsrp->get_tx_rate());
-
- if (fabs(myUsrp->get_tx_rate() / myConf.sampleRate) >
- myConf.sampleRate * 1e-6) {
- throw std::runtime_error("Cannot set USRP sample rate. Aborted.");
- }
-
- tune_usrp_to(myUsrp, myConf.lo_offset, myConf.frequency);
-
- myConf.frequency = myUsrp->get_tx_freq();
- etiLog.level(info) << std::fixed << std::setprecision(3) <<
- "OutputUHD:Actual TX frequency: " << myConf.frequency;
-
- etiLog.level(info) << std::fixed << std::setprecision(3) <<
- "OutputUHD:Actual RX frequency: " << myUsrp->get_tx_freq();
-
- myUsrp->set_tx_gain(myConf.txgain);
- etiLog.log(debug, "OutputUHD:Actual TX Gain: %f", myUsrp->get_tx_gain());
-
- etiLog.log(debug, "OutputUHD:Mute on missing timestamps: %s",
- myConf.muteNoTimestamps ? "enabled" : "disabled");
-
- // preparing output thread worker data
- sourceContainsTimestamp = false;
-
- SetDelayBuffer(myConf.dabMode);
-
- myUsrp->set_rx_rate(myConf.sampleRate);
- etiLog.log(debug, "OutputUHD:Actual RX Rate: %f sps.", myUsrp->get_rx_rate());
-
- myUsrp->set_rx_antenna("RX2");
- etiLog.log(debug, "OutputUHD:Set RX Antenna: %s",
- myUsrp->get_rx_antenna().c_str());
-
- myUsrp->set_rx_gain(myConf.rxgain);
- etiLog.log(debug, "OutputUHD:Actual RX Gain: %f", myUsrp->get_rx_gain());
-
- uhdFeedback = std::make_shared<OutputUHDFeedback>(
- myUsrp, myConf.dpdFeedbackServerPort, myConf.sampleRate);
-
- MDEBUG("OutputUHD:UHD ready.\n");
-}
-
-bool OutputUHD::refclk_loss_needs_check() const
-{
- if (suppress_refclk_loss_check) {
- return false;
- }
- return myConf.refclk_src != "internal";
-}
-
-bool OutputUHD::gpsfix_needs_check() const
-{
- if (myConf.refclk_src == "internal") {
- return false;
- }
- else if (myConf.refclk_src == "gpsdo") {
- return (myConf.maxGPSHoldoverTime != 0);
- }
- else if (myConf.refclk_src == "gpsdo-ettus") {
- return (myConf.maxGPSHoldoverTime != 0);
- }
- else {
- return false;
- }
-}
-
-bool OutputUHD::gpsdo_is_ettus() const
-{
- return (myConf.refclk_src == "gpsdo-ettus");
-}
-
-OutputUHD::~OutputUHD()
-{
- stop_threads();
-}
-
-void OutputUHD::stop_threads()
-{
- running.store(false);
- uhd_thread.interrupt();
- uhd_thread.join();
- async_rx_thread.join();
-}
-
-
-void OutputUHD::setETISource(EtiSource *etiSource)
-{
- myEtiSource = etiSource;
-}
-
-int transmission_frame_duration_ms(unsigned int dabMode)
-{
- switch (dabMode) {
- // could happen when called from constructor and we take the mode from ETI
- case 0: return 0;
-
- case 1: return 96;
- case 2: return 24;
- case 3: return 24;
- case 4: return 48;
- default:
- throw std::runtime_error("OutputUHD: invalid DAB mode");
- }
-}
-
-void OutputUHD::SetDelayBuffer(unsigned int dabMode)
-{
- // find out the duration of the transmission frame (Table 2 in ETSI 300 401)
- myTFDurationMs = transmission_frame_duration_ms(dabMode);
-
- // The buffer size equals the number of samples per transmission frame so
- // we calculate it by multiplying the duration of the transmission frame
- // with the samplerate.
- myDelayBuf.resize(myTFDurationMs * myConf.sampleRate / 1000);
-}
-
-int OutputUHD::process(Buffer* dataIn)
-{
- if (not gps_fix_verified) {
- if (gpsfix_needs_check()) {
- initial_gps_check();
-
- if (num_checks_without_gps_fix == 0) {
- set_usrp_time();
- gps_fix_verified = true;
- myConf.muting = false;
- }
- }
- else {
- set_usrp_time();
- gps_fix_verified = true;
- myConf.muting = false;
- }
- }
- else {
- if (first_run) {
- etiLog.level(debug) << "OutputUHD: UHD initialising...";
-
- // we only set the delay buffer from the dab mode signaled in ETI if the
- // dab mode was not set in contructor
- if (myTFDurationMs == 0) {
- SetDelayBuffer(myEtiSource->getMode());
- }
-
- running.store(true);
- uhd_thread = boost::thread(&OutputUHD::workerthread, this);
- async_rx_thread = boost::thread(
- &OutputUHD::print_async_thread, this);
-
- lastLen = dataIn->getLength();
- first_run = false;
- etiLog.level(debug) << "OutputUHD: UHD initialising complete";
- }
-
- if (lastLen != dataIn->getLength()) {
- // I expect that this never happens.
- etiLog.level(emerg) <<
- "OutputUHD: Fatal error, input length changed from " << lastLen <<
- " to " << dataIn->getLength();
- throw std::runtime_error("Non-constant input length!");
- }
-
- sourceContainsTimestamp = myConf.enableSync and
- myEtiSource->sourceContainsTimestamp();
-
- if (gpsfix_needs_check()) {
- try {
- check_gps();
- }
- catch (std::runtime_error& e) {
- running.store(false);
- etiLog.level(error) << e.what();
- }
- }
-
- // Prepare the frame for the worker
- UHDWorkerFrameData frame;
- frame.buf.resize(dataIn->getLength());
-
- // calculate delay and fill buffer
- uint32_t noSampleDelay = (myConf.staticDelayUs * (myConf.sampleRate / 1000)) / 1000;
- uint32_t noByteDelay = noSampleDelay * sizeof(complexf);
-
- const uint8_t* pInData = (uint8_t*)dataIn->getData();
-
- uint8_t *pTmp = &frame.buf[0];
- if (noByteDelay) {
- // copy remain from delaybuf
- memcpy(pTmp, &myDelayBuf[0], noByteDelay);
- // copy new data
- memcpy(&pTmp[noByteDelay], pInData, dataIn->getLength() - noByteDelay);
- // copy remaining data to delay buf
- memcpy(&myDelayBuf[0], &pInData[dataIn->getLength() - noByteDelay], noByteDelay);
- }
- else {
- std::copy(pInData, pInData + dataIn->getLength(),
- frame.buf.begin());
- }
-
- myEtiSource->calculateTimestamp(frame.ts);
-
- if (not running.load()) {
- uhd_thread.interrupt();
- uhd_thread.join();
- async_rx_thread.join();
- first_run = true;
-
- etiLog.level(error) << "OutputUHD UHD worker failed";
- throw std::runtime_error("UHD worker failed");
- }
-
- if (frame.ts.fct == -1) {
- etiLog.level(info) <<
- "OutputUHD: dropping one frame with invalid FCT";
- }
- else {
- try {
- uhdFeedback->set_tx_frame(frame.buf, frame.ts);
- }
- catch (const runtime_error& e) {
- etiLog.level(warn) <<
- "OutputUHD: Feedback server failed, restarting...";
-
- uhdFeedback = std::make_shared<OutputUHDFeedback>(
- myUsrp, myConf.dpdFeedbackServerPort, myConf.sampleRate);
- }
-
- size_t num_frames = frames.push_wait_if_full(frame,
- FRAMES_MAX_SIZE);
- etiLog.log(trace, "UHD,push %zu", num_frames);
- }
- }
-
- return dataIn->getLength();
-}
-
-
-void OutputUHD::set_usrp_time()
-{
- if (myConf.enableSync and (myConf.pps_src == "none")) {
- etiLog.level(warn) <<
- "OutputUHD: WARNING:"
- " you are using synchronous transmission without PPS input!";
-
- struct timespec now;
- if (clock_gettime(CLOCK_REALTIME, &now)) {
- perror("OutputUHD:Error: could not get time: ");
- etiLog.level(error) << "OutputUHD: could not get time";
- }
- else {
- myUsrp->set_time_now(uhd::time_spec_t(now.tv_sec));
- etiLog.level(info) << "OutputUHD: Setting USRP time to " <<
- std::fixed <<
- uhd::time_spec_t(now.tv_sec).get_real_secs();
- }
- }
-
- if (myConf.pps_src != "none") {
- /* handling time for synchronisation: wait until the next full
- * second, and set the USRP time at next PPS */
- struct timespec now;
- time_t seconds;
- if (clock_gettime(CLOCK_REALTIME, &now)) {
- etiLog.level(error) << "OutputUHD: could not get time :" <<
- strerror(errno);
- throw std::runtime_error("OutputUHD: could not get time.");
- }
- else {
- seconds = now.tv_sec;
-
- MDEBUG("OutputUHD:sec+1: %ld ; now: %ld ...\n", seconds+1, now.tv_sec);
- while (seconds + 1 > now.tv_sec) {
- usleep(1);
- if (clock_gettime(CLOCK_REALTIME, &now)) {
- etiLog.level(error) << "OutputUHD: could not get time :" <<
- strerror(errno);
- throw std::runtime_error("OutputUHD: could not get time.");
- }
- }
- MDEBUG("OutputUHD:sec+1: %ld ; now: %ld ...\n", seconds+1, now.tv_sec);
- /* We are now shortly after the second change. */
-
- usleep(200000); // 200ms, we want the PPS to be later
- myUsrp->set_time_unknown_pps(uhd::time_spec_t(seconds + 2));
- etiLog.level(info) << "OutputUHD: Setting USRP time next pps to " <<
- std::fixed <<
- uhd::time_spec_t(seconds + 2).get_real_secs();
- }
-
- usleep(1e6);
- etiLog.log(info, "OutputUHD: USRP time %f\n",
- myUsrp->get_time_now().get_real_secs());
- }
-}
-
-void OutputUHD::initial_gps_check()
-{
- if (first_gps_fix_check.tv_sec == 0) {
- etiLog.level(info) << "Waiting for GPS fix";
-
- if (clock_gettime(CLOCK_MONOTONIC, &first_gps_fix_check) != 0) {
- stringstream ss;
- ss << "clock_gettime failure: " << strerror(errno);
- throw std::runtime_error(ss.str());
- }
- }
-
- check_gps();
-
- if (last_gps_fix_check.tv_sec >
- first_gps_fix_check.tv_sec + initial_gps_fix_wait) {
- stringstream ss;
- ss << "GPS did not show time lock in " << initial_gps_fix_wait << " seconds";
- throw std::runtime_error(ss.str());
- }
-
- if (time_last_frame.tv_sec == 0) {
- if (clock_gettime(CLOCK_MONOTONIC, &time_last_frame) != 0) {
- stringstream ss;
- ss << "clock_gettime failure: " << strerror(errno);
- throw std::runtime_error(ss.str());
- }
- }
-
- struct timespec now;
- if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
- stringstream ss;
- ss << "clock_gettime failure: " << strerror(errno);
- throw std::runtime_error(ss.str());
- }
-
- long delta_us = timespecdiff_us(time_last_frame, now);
- long wait_time_us = transmission_frame_duration_ms(myConf.dabMode);
-
- if (wait_time_us - delta_us > 0) {
- usleep(wait_time_us - delta_us);
- }
-
- time_last_frame.tv_nsec += wait_time_us * 1000;
- if (time_last_frame.tv_nsec >= 1000000000L) {
- time_last_frame.tv_nsec -= 1000000000L;
- time_last_frame.tv_sec++;
- }
-}
-
-void OutputUHD::check_gps()
-{
- struct timespec time_now;
- if (clock_gettime(CLOCK_MONOTONIC, &time_now) != 0) {
- stringstream ss;
- ss << "clock_gettime failure: " << strerror(errno);
- throw std::runtime_error(ss.str());
- }
-
- // Divide interval by two because we alternate between
- // launch and check
- if (gpsfix_needs_check() and
- last_gps_fix_check.tv_sec + gps_fix_check_interval/2.0 <
- time_now.tv_sec) {
- last_gps_fix_check = time_now;
-
- // Alternate between launching thread and checking the
- // result.
- if (gps_fix_task.joinable()) {
- if (gps_fix_future.has_value()) {
-
- gps_fix_future.wait();
-
- gps_fix_task.join();
-
- if (not gps_fix_future.get()) {
- if (num_checks_without_gps_fix == 0) {
- etiLog.level(alert) <<
- "OutputUHD: GPS Time Lock lost";
- }
- num_checks_without_gps_fix++;
- }
- else {
- if (num_checks_without_gps_fix) {
- etiLog.level(info) <<
- "OutputUHD: GPS Time Lock recovered";
- }
- num_checks_without_gps_fix = 0;
- }
-
- if (gps_fix_check_interval * num_checks_without_gps_fix >
- myConf.maxGPSHoldoverTime) {
- std::stringstream ss;
- ss << "Lost GPS Time Lock for " << gps_fix_check_interval *
- num_checks_without_gps_fix << " seconds";
- throw std::runtime_error(ss.str());
- }
- }
- }
- else {
- // Checking the sensor here takes too much
- // time, it has to be done in a separate thread.
- if (gpsdo_is_ettus()) {
- gps_fix_pt = boost::packaged_task<bool>(
- boost::bind(check_gps_locked, myUsrp) );
- }
- else {
- gps_fix_pt = boost::packaged_task<bool>(
- boost::bind(check_gps_timelock, myUsrp) );
- }
- gps_fix_future = gps_fix_pt.get_future();
-
- gps_fix_task = boost::thread(boost::move(gps_fix_pt));
- }
- }
-}
-
-void OutputUHD::workerthread()
-{
- // Set thread priority to realtime
- if (int ret = set_realtime_prio(1)) {
- etiLog.level(error) << "Could not set priority for UHD worker:" << ret;
- }
-
- set_thread_name("uhdworker");
-
- last_tx_time_initialised = false;
-
- uhd::stream_args_t stream_args("fc32"); //complex floats
- myTxStream = myUsrp->get_tx_stream(stream_args);
-
- md.start_of_burst = false;
- md.end_of_burst = false;
-
- num_underflows = 0;
- num_late_packets = 0;
-
- size_t last_num_underflows = 0;
- size_t pop_prebuffering = FRAMES_MAX_SIZE;
-
- while (running.load()) {
- md.has_time_spec = false;
- md.time_spec = uhd::time_spec_t(0.0);
-
- struct UHDWorkerFrameData frame;
- etiLog.log(trace, "UHD,wait");
- frames.wait_and_pop(frame, pop_prebuffering);
- etiLog.log(trace, "UHD,pop");
-
- handle_frame(&frame);
- num_frames_modulated++;
-
- /* Ensure we fill frames after every underrun and
- * at startup to reduce underrun likelihood. */
- if (last_num_underflows < num_underflows) {
- pop_prebuffering = FRAMES_MAX_SIZE;
- }
- else {
- pop_prebuffering = 1;
- }
- last_num_underflows = num_underflows;
- }
- running.store(false);
- etiLog.level(warn) << "UHD worker terminated";
-}
-
-void OutputUHD::handle_frame(const struct UHDWorkerFrameData *frame)
-{
- // Transmit timeout
- static const double tx_timeout = 20.0;
-
- // Check for ref_lock
- if (refclk_loss_needs_check()) {
- try {
- if (not myUsrp->get_mboard_sensor("ref_locked", 0).to_bool()) {
- etiLog.log(alert,
- "OutputUHD: External reference clock lock lost !");
- if (myConf.refclk_lock_loss_behaviour == CRASH) {
- throw std::runtime_error(
- "OutputUHD: External reference clock lock lost.");
- }
- }
- }
- catch (uhd::lookup_error &e) {
- suppress_refclk_loss_check = true;
- etiLog.log(warn, "OutputUHD: This USRP does not have mboard "
- "sensor for ext clock loss. Check disabled.");
- }
- }
-
- double usrp_time = myUsrp->get_time_now().get_real_secs();
- bool timestamp_discontinuity = false;
-
- if (sourceContainsTimestamp) {
- // Tx time from MNSC and TIST
- uint32_t tx_second = frame->ts.timestamp_sec;
- uint32_t tx_pps = frame->ts.timestamp_pps;
-
- if (!frame->ts.timestamp_valid) {
- /* We have not received a full timestamp through
- * MNSC. We sleep through the frame.
- */
- etiLog.level(info) <<
- "OutputUHD: Throwing sample " << frame->ts.fct <<
- " away: incomplete timestamp " << tx_second <<
- " / " << tx_pps;
- usleep(20000); //TODO should this be TM-dependant ?
- return;
- }
-
- if (last_tx_time_initialised) {
- const size_t sizeIn = frame->buf.size() / sizeof(complexf);
- uint64_t increment = (uint64_t)sizeIn * 16384000ul /
- (uint64_t)myConf.sampleRate;
- // samps * ticks/s / (samps/s)
- // (samps * ticks * s) / (s * samps)
- // ticks
-
- uint32_t expected_sec = last_tx_second + increment / 16384000ul;
- uint32_t expected_pps = last_tx_pps + increment % 16384000ul;
-
- while (expected_pps >= 16384000) {
- expected_sec++;
- expected_pps -= 16384000;
- }
-
- if (expected_sec != tx_second or
- expected_pps != tx_pps) {
- etiLog.level(warn) << "OutputUHD: timestamp irregularity!" <<
- std::fixed <<
- " Expected " <<
- expected_sec << "+" << (double)expected_pps/16384000.0 <<
- "(" << expected_pps << ")" <<
- " Got " <<
- tx_second << "+" << (double)tx_pps/16384000.0 <<
- "(" << tx_pps << ")";
-
- timestamp_discontinuity = true;
- }
- }
-
- last_tx_second = tx_second;
- last_tx_pps = tx_pps;
- last_tx_time_initialised = true;
-
- double pps_offset = tx_pps / 16384000.0;
-
- md.has_time_spec = true;
- md.time_spec = uhd::time_spec_t(tx_second, pps_offset);
- etiLog.log(trace, "UHD,tist %f", md.time_spec.get_real_secs());
-
- // md is defined, let's do some checks
- if (md.time_spec.get_real_secs() + tx_timeout < usrp_time) {
- etiLog.level(warn) <<
- "OutputUHD: Timestamp in the past! offset: " <<
- std::fixed <<
- md.time_spec.get_real_secs() - usrp_time <<
- " (" << usrp_time << ")"
- " frame " << frame->ts.fct <<
- ", tx_second " << tx_second <<
- ", pps " << pps_offset;
- return;
- }
-
- if (md.time_spec.get_real_secs() > usrp_time + TIMESTAMP_ABORT_FUTURE) {
- etiLog.level(error) <<
- "OutputUHD: Timestamp way too far in the future! offset: " <<
- std::fixed <<
- md.time_spec.get_real_secs() - usrp_time;
- throw std::runtime_error("Timestamp error. Aborted.");
- }
- }
- else { // !sourceContainsTimestamp
- if (myConf.muting or myConf.muteNoTimestamps) {
- /* There was some error decoding the timestamp */
- if (myConf.muting) {
- etiLog.log(info,
- "OutputUHD: Muting sample %d requested\n",
- frame->ts.fct);
- }
- else {
- etiLog.log(info,
- "OutputUHD: Muting sample %d : no timestamp\n",
- frame->ts.fct);
- }
- usleep(20000);
- return;
- }
- }
-
- tx_frame(frame, timestamp_discontinuity);
-}
-
-void OutputUHD::tx_frame(const struct UHDWorkerFrameData *frame, bool ts_update)
-{
- const double tx_timeout = 20.0;
- const size_t sizeIn = frame->buf.size() / sizeof(complexf);
- const complexf* in_data = reinterpret_cast<const complexf*>(&frame->buf[0]);
-
- size_t usrp_max_num_samps = myTxStream->get_max_num_samps();
- size_t num_acc_samps = 0; //number of accumulated samples
- while (running.load() and (not myConf.muting) and (num_acc_samps < sizeIn)) {
- size_t samps_to_send = std::min(sizeIn - num_acc_samps, usrp_max_num_samps);
-
- uhd::tx_metadata_t md_tx = md;
-
- //ensure the the last packet has EOB set if the timestamps has been
- //refreshed and need to be reconsidered.
- md_tx.end_of_burst = (
- sourceContainsTimestamp and
- (frame->ts.timestamp_refresh or ts_update) and
- samps_to_send <= usrp_max_num_samps );
-
-
- //send a single packet
- size_t num_tx_samps = myTxStream->send(
- &in_data[num_acc_samps],
- samps_to_send, md_tx, tx_timeout);
- etiLog.log(trace, "UHD,sent %zu of %zu", num_tx_samps, samps_to_send);
-
- num_acc_samps += num_tx_samps;
-
- md_tx.time_spec = md.time_spec +
- uhd::time_spec_t(0, num_tx_samps/myConf.sampleRate);
-
- if (num_tx_samps == 0) {
- etiLog.log(warn,
- "OutputUHD::workerthread() unable to write to device, skipping frame!\n");
- break;
- }
- }
-}
-
-void OutputUHD::print_async_thread()
-{
- while (running.load()) {
- uhd::async_metadata_t async_md;
- if (myUsrp->get_device()->recv_async_msg(async_md, 1)) {
- const char* uhd_async_message = "";
- bool failure = false;
- switch (async_md.event_code) {
- case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
- break;
- case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
- uhd_async_message = "Underflow";
- num_underflows++;
- break;
- case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
- uhd_async_message = "Packet loss between host and device.";
- failure = true;
- break;
- case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
- uhd_async_message = "Packet had time that was late.";
- num_late_packets++;
- break;
- case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
- uhd_async_message = "Underflow occurred inside a packet.";
- failure = true;
- break;
- case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
- uhd_async_message = "Packet loss within a burst.";
- failure = true;
- break;
- default:
- uhd_async_message = "unknown event code";
- failure = true;
- break;
- }
-
- if (failure) {
- etiLog.level(alert) <<
- "Received Async UHD Message '" <<
- uhd_async_message << "' at time " <<
- md.time_spec.get_real_secs();
-
- }
- }
-
- auto time_now = std::chrono::steady_clock::now();
- if (last_print_time + std::chrono::seconds(1) < time_now) {
- const double usrp_time =
- myUsrp->get_time_now().get_real_secs();
-
- if ( (num_underflows > num_underflows_previous) or
- (num_late_packets > num_late_packets_previous)) {
- etiLog.log(info,
- "OutputUHD status (usrp time: %f): "
- "%d underruns and %d late packets since last status.\n",
- usrp_time,
- num_underflows, num_late_packets);
- }
-
- num_underflows_previous = num_underflows;
- num_late_packets_previous = num_late_packets;
-
- last_print_time = time_now;
- }
- }
-}
-
-// =======================================
-// Remote Control for UHD
-// =======================================
-void OutputUHD::set_parameter(const string& parameter, const string& value)
-{
- stringstream ss(value);
- ss.exceptions ( stringstream::failbit | stringstream::badbit );
-
- if (parameter == "txgain") {
- ss >> myConf.txgain;
- myUsrp->set_tx_gain(myConf.txgain);
- }
- else if (parameter == "rxgain") {
- ss >> myConf.rxgain;
- myUsrp->set_rx_gain(myConf.rxgain);
- }
- else if (parameter == "freq") {
- ss >> myConf.frequency;
- tune_usrp_to(myUsrp, myConf.lo_offset, myConf.frequency);
- myConf.frequency = myUsrp->get_tx_freq();
- }
- else if (parameter == "muting") {
- ss >> myConf.muting;
- }
- else if (parameter == "staticdelay") {
- int64_t adjust;
- ss >> adjust;
- if (adjust > (myTFDurationMs * 1000))
- { // reset static delay for values outside range
- myConf.staticDelayUs = 0;
- }
- else
- { // the new adjust value is added to the existing delay and the result
- // is wrapped around at TF duration
- int newStaticDelayUs = myConf.staticDelayUs + adjust;
- if (newStaticDelayUs > (myTFDurationMs * 1000))
- myConf.staticDelayUs = newStaticDelayUs - (myTFDurationMs * 1000);
- else if (newStaticDelayUs < 0)
- myConf.staticDelayUs = newStaticDelayUs + (myTFDurationMs * 1000);
- else
- myConf.staticDelayUs = newStaticDelayUs;
- }
- }
- else if (parameter == "underruns" or
- parameter == "latepackets" or
- parameter == "frames") {
- throw ParameterError("Parameter " + parameter + " is read-only.");
- }
- else {
- stringstream ss;
- ss << "Parameter '" << parameter
- << "' is not exported by controllable " << get_rc_name();
- throw ParameterError(ss.str());
- }
-}
-
-const string OutputUHD::get_parameter(const string& parameter) const
-{
- stringstream ss;
- if (parameter == "txgain") {
- ss << myConf.txgain;
- }
- else if (parameter == "rxgain") {
- ss << myConf.rxgain;
- }
- else if (parameter == "freq") {
- ss << myConf.frequency;
- }
- else if (parameter == "muting") {
- ss << myConf.muting;
- }
- else if (parameter == "staticdelay") {
- ss << myConf.staticDelayUs;
- }
- else if (parameter == "underruns") {
- ss << num_underflows;
- }
- else if (parameter == "latepackets") {
- ss << num_late_packets;
- }
- else if (parameter == "frames") {
- ss << num_frames_modulated;
- }
- else {
- ss << "Parameter '" << parameter <<
- "' is not exported by controllable " << get_rc_name();
- throw ParameterError(ss.str());
- }
- return ss.str();
-}
-
-#endif // HAVE_OUTPUT_UHD
-
diff --git a/src/OutputUHD.h b/src/OutputUHD.h
deleted file mode 100644
index dfa471d..0000000
--- a/src/OutputUHD.h
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- 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:
- It is an output driver for the USRP family of devices, and uses the UHD
- library. This version is multi-threaded. A separate thread sends the data to
- the device.
-
- Data between the modulator and the UHD thread are exchanged through a
- threadsafe queue.
-*/
-
-/*
- This file is part of ODR-DabMod.
-
- ODR-DabMod is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- ODR-DabMod is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifdef HAVE_OUTPUT_UHD
-
-#include <uhd/utils/thread_priority.hpp>
-#include <uhd/utils/safe_main.hpp>
-#include <uhd/usrp/multi_usrp.hpp>
-#include <boost/thread.hpp>
-#include <deque>
-#include <chrono>
-#include <memory>
-#include <string>
-#include <atomic>
-
-#include "Log.h"
-#include "ModPlugin.h"
-#include "EtiReader.h"
-#include "TimestampDecoder.h"
-#include "RemoteControl.h"
-#include "ThreadsafeQueue.h"
-#include "OutputUHDFeedback.h"
-
-#include <stdio.h>
-#include <sys/types.h>
-
-//#define MDEBUG(fmt, args...) fprintf(LOG, fmt , ## args)
-#define MDEBUG(fmt, args...)
-
-// If the timestamp is further in the future than
-// 100 seconds, abort
-#define TIMESTAMP_ABORT_FUTURE 100
-
-// Add a delay to increase buffers when
-// frames are too far in the future
-#define TIMESTAMP_MARGIN_FUTURE 0.5
-
-typedef std::complex<float> complexf;
-
-// Each frame contains one OFDM frame, and its
-// associated timestamp
-struct UHDWorkerFrameData {
- // Buffer holding frame data
- std::vector<uint8_t> buf;
-
- // A full timestamp contains a TIST according to standard
- // and time information within MNSC with tx_second.
- struct frame_timestamp ts;
-};
-
-enum refclk_lock_loss_behaviour_t { CRASH, IGNORE };
-
-/* This structure is used as initial configuration for OutputUHD.
- * It must also contain all remote-controllable settings, otherwise
- * they will get lost on a modulator restart. */
-struct OutputUHDConfig {
- std::string device;
- std::string usrpType; // e.g. b100, b200, usrp2
-
- // The USRP1 can accept two daughterboards
- std::string subDevice; // e.g. A:0
-
- 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 : auto, int, sma, mimo */
- std::string refclk_src;
-
- /* allowed values : int, sma, mimo */
- std::string pps_src;
-
- /* allowed values : 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;
-
- // static delay in microseconds
- int staticDelayUs = 0;
-
- // TCP port on which to serve TX and RX samples for the
- // digital pre distortion learning tool
- uint16_t dpdFeedbackServerPort = 0;
-};
-
-class OutputUHD: public ModOutput, public RemoteControllable {
- public:
- OutputUHD(OutputUHDConfig& config);
- OutputUHD(const OutputUHD& other) = delete;
- OutputUHD operator=(const OutputUHD& other) = delete;
- ~OutputUHD();
-
- int process(Buffer* dataIn);
-
- const char* name() { return "OutputUHD"; }
-
- void setETISource(EtiSource *etiSource);
-
- /*********** REMOTE CONTROL ***************/
-
- /* 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;
-
- protected:
- EtiSource *myEtiSource = nullptr;
- OutputUHDConfig& myConf;
- uhd::usrp::multi_usrp::sptr myUsrp;
- std::shared_ptr<boost::barrier> mySyncBarrier;
- bool first_run = true;
- bool gps_fix_verified = false;
- std::shared_ptr<OutputUHDFeedback> uhdFeedback;
-
- private:
- // Resize the internal delay buffer according to the dabMode and
- // the sample rate.
- void SetDelayBuffer(unsigned int dabMode);
-
- // data
- // The remote-controllable static delay is in the OutputUHDConfig
- int myTFDurationMs; // TF duration in milliseconds
- std::vector<complexf> myDelayBuf;
- size_t lastLen = 0;
-
- // GPS Fix check variables
- int num_checks_without_gps_fix = 1;
- struct timespec first_gps_fix_check;
- struct timespec last_gps_fix_check;
- struct timespec time_last_frame;
- boost::packaged_task<bool> gps_fix_pt;
- boost::unique_future<bool> gps_fix_future;
- boost::thread gps_fix_task;
-
- // Wait time in seconds to get fix
- static const int initial_gps_fix_wait = 180;
-
- // Interval for checking the GPS at runtime
- static constexpr double gps_fix_check_interval = 10.0; // seconds
-
- // Asynchronous message statistics
- size_t num_underflows = 0;
- size_t num_late_packets = 0;
- size_t num_underflows_previous = 0;
- size_t num_late_packets_previous = 0;
-
- size_t num_frames_modulated = 0;
-
- uhd::tx_metadata_t md;
- bool last_tx_time_initialised = false;
- uint32_t last_tx_second = 0;
- uint32_t last_tx_pps = 0;
-
- // Used to print statistics once a second
- std::chrono::steady_clock::time_point last_print_time;
-
- bool sourceContainsTimestamp = false;
-
- ThreadsafeQueue<UHDWorkerFrameData> frames;
-
- // Returns true if we want to verify loss of refclk
- bool refclk_loss_needs_check(void) const;
- bool suppress_refclk_loss_check = false;
-
- // Returns true if we want to check for the gps_timelock sensor
- bool gpsfix_needs_check(void) const;
-
- // Return true if the gpsdo is from ettus, false if it is the ODR
- // LEA-M8F board is used
- bool gpsdo_is_ettus(void) const;
-
- std::atomic<bool> running;
- boost::thread uhd_thread;
- boost::thread async_rx_thread;
- void stop_threads(void);
-
- uhd::tx_streamer::sptr myTxStream;
-
- // The worker thread decouples the modulator from UHD
- void workerthread();
- void handle_frame(const struct UHDWorkerFrameData *frame);
- void tx_frame(const struct UHDWorkerFrameData *frame, bool ts_update);
-
- // Poll asynchronous metadata from UHD
- void print_async_thread(void);
-
- void check_gps();
-
- void set_usrp_time();
-
- void initial_gps_check();
-};
-
-#endif // HAVE_OUTPUT_UHD
-
diff --git a/src/OutputUHDFeedback.cpp b/src/OutputUHDFeedback.cpp
deleted file mode 100644
index 68783f2..0000000
--- a/src/OutputUHDFeedback.cpp
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifdef HAVE_OUTPUT_UHD
-
-#include <vector>
-#include <complex>
-#include <cstring>
-#include <uhd/types/stream_cmd.hpp>
-#include <sys/socket.h>
-#include <errno.h>
-#include <poll.h>
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include "OutputUHDFeedback.h"
-#include "Utils.h"
-#include "Socket.h"
-
-using namespace std;
-typedef std::complex<float> complexf;
-
-OutputUHDFeedback::OutputUHDFeedback(
- uhd::usrp::multi_usrp::sptr usrp,
- uint16_t port,
- uint32_t sampleRate)
-{
- m_port = port;
- m_sampleRate = sampleRate;
- m_usrp = usrp;
-
- if (m_port) {
- m_running.store(true);
-
- rx_burst_thread = boost::thread(&OutputUHDFeedback::ReceiveBurstThread, this);
- burst_tcp_thread = boost::thread(&OutputUHDFeedback::ServeFeedbackThread, this);
- }
-}
-
-OutputUHDFeedback::~OutputUHDFeedback()
-{
- 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 OutputUHDFeedback::set_tx_frame(
- const std::vector<uint8_t> &buf,
- const struct frame_timestamp &buf_ts)
-{
- if (not m_running) {
- throw runtime_error("OutputUHDFeedback 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 OutputUHDFeedback::ReceiveBurstThread()
-{
- try {
- set_thread_name("uhdreceiveburst");
-
- uhd::stream_args_t stream_args("fc32"); //complex floats
- auto rxStream = m_usrp->get_rx_stream(stream_args);
-
- 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;
-
- uhd::stream_cmd_t cmd(
- uhd::stream_cmd_t::stream_mode_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
- cmd.num_samps = burstRequest.num_samples;
- cmd.stream_now = false;
-
- double pps = burstRequest.rx_pps / 16384000.0;
- cmd.time_spec = uhd::time_spec_t(burstRequest.rx_second, pps);
-
- // We need to free the mutex while we recv(), because otherwise we block the
- // TX thread
- lock.unlock();
-
- const double usrp_time = m_usrp->get_time_now().get_real_secs();
- const double cmd_time = cmd.time_spec.get_real_secs();
-
- rxStream->issue_stream_cmd(cmd);
-
- uhd::rx_metadata_t md;
-
- std::vector<uint8_t> buf(cmd.num_samps * sizeof(complexf));
-
- const double timeout = 60;
- size_t samples_read = rxStream->recv(&buf[0], cmd.num_samps, md, 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 = md.time_spec.get_full_secs();
- burstRequest.rx_pps = md.time_spec.get_frac_secs() * 16384000.0;
-
- 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 - usrp_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 OutputUHDFeedback::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 OutputUHDFeedback::ServeFeedbackThread()
-{
- set_thread_name("uhdservefeedback");
-
- 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);
-}
-
-#endif
diff --git a/src/OutputUHDFeedback.h b/src/OutputUHDFeedback.h
deleted file mode 100644
index 80d287f..0000000
--- a/src/OutputUHDFeedback.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifdef HAVE_OUTPUT_UHD
-
-#include <uhd/utils/thread_priority.hpp>
-#include <uhd/utils/safe_main.hpp>
-#include <uhd/usrp/multi_usrp.hpp>
-#include <boost/thread.hpp>
-#include <memory>
-#include <string>
-#include <atomic>
-
-#include "Log.h"
-#include "TimestampDecoder.h"
-
-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 UHDReceiveBurstRequest {
- // 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<uint8_t> tx_samples;
-
- // The timestamp of the first sample of the RX buffers
- uint32_t rx_second = 0;
- uint32_t rx_pps = 0;
-
- std::vector<uint8_t> rx_samples; // Also, actually complexf
-};
-
-// Serve TX samples and RX feedback samples over a TCP connection
-class OutputUHDFeedback {
- public:
- OutputUHDFeedback(
- uhd::usrp::multi_usrp::sptr usrp,
- uint16_t port,
- uint32_t sampleRate);
- OutputUHDFeedback(const OutputUHDFeedback& other) = delete;
- OutputUHDFeedback& operator=(const OutputUHDFeedback& other) = delete;
- ~OutputUHDFeedback();
-
- void set_tx_frame(const std::vector<uint8_t> &buf,
- const struct frame_timestamp& ts);
-
- private:
- // Thread that reacts to burstRequests and receives from the USRP
- 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;
-
- UHDReceiveBurstRequest burstRequest;
-
- std::atomic_bool m_running;
- uint16_t m_port = 0;
- uint32_t m_sampleRate = 0;
- uhd::usrp::multi_usrp::sptr m_usrp;
-};
-
-
-#endif // HAVE_OUTPUT_UHD
diff --git a/src/output/UHD.h b/src/output/UHD.h
index 3742924..5ae477b 100644
--- a/src/output/UHD.h
+++ b/src/output/UHD.h
@@ -50,7 +50,6 @@ DESCRIPTION:
#include "TimestampDecoder.h"
#include "RemoteControl.h"
#include "ThreadsafeQueue.h"
-#include "OutputUHDFeedback.h"
#include <stdio.h>
#include <sys/types.h>