From 173f1d8bf78f59320c95148dd21611ea3b6b7153 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 16 Mar 2014 22:34:05 +0100 Subject: Handle master_clock_rate better, add verification The configuration file supports type and master_clock_rate in the [uhd] section. The device option is still supported. But the master_clock_rate can only be verified if it is set ! --- src/DabMod.cpp | 27 ++++++++- src/OutputUHD.cpp | 168 ++++++++++++++++++++++++++++++++++-------------------- src/OutputUHD.h | 27 +++++---- 3 files changed, 143 insertions(+), 79 deletions(-) (limited to 'src') diff --git a/src/DabMod.cpp b/src/DabMod.cpp index bd83531..fc589ec 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -3,8 +3,10 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Includes modifications for which no copyright is claimed - 2012, Matthias P. Braendli, matthias.braendli@mpb.li + Copyright (C) 2014 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org */ /* This file is part of ODR-DabMod. @@ -413,6 +415,19 @@ int main(int argc, char* argv[]) } else if (output_selected == "uhd") { outputuhd_conf.device = pt.get("uhdoutput.device", ""); + outputuhd_conf.usrpType = pt.get("uhdoutput.type", ""); + outputuhd_conf.masterClockRate = pt.get("uhdoutput.master_clock_rate", 0); + + if (outputuhd_conf.device.find("master_clock_rate") != std::string::npos) { + std::cerr << "Warning:" + "setting master_clock_rate in [uhd] device is deprecated !\n"; + } + + if (outputuhd_conf.device.find("type=") != std::string::npos) { + std::cerr << "Warning:" + "setting type in [uhd] device is deprecated !\n"; + } + outputuhd_conf.txgain = pt.get("uhdoutput.txgain", 0); outputuhd_conf.frequency = pt.get("uhdoutput.frequency", 0); std::string chan = pt.get("uhdoutput.channel", ""); @@ -584,7 +599,13 @@ int main(int argc, char* argv[]) fprintf(stderr, " Source: %s\n", inputName.c_str()); fprintf(stderr, "Output\n"); if (useUHDOutput) { - fprintf(stderr, " UHD, Device: %s\n", outputuhd_conf.device.c_str()); + fprintf(stderr, " UHD\n" + " Device: %s\n" + " Type: %s\n" + " master_clock_rate: %ld\n", + outputuhd_conf.device.c_str(), + outputuhd_conf.usrpType.c_str(), + outputuhd_conf.masterClockRate); } else if (useFileOutput) { fprintf(stderr, " Name: %s\n", outputName.c_str()); diff --git a/src/OutputUHD.cpp b/src/OutputUHD.cpp index b8d421f..a57a46a 100644 --- a/src/OutputUHD.cpp +++ b/src/OutputUHD.cpp @@ -2,8 +2,10 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Includes modifications for which no copyright is claimed - 2012, Matthias P. Braendli, matthias.braendli@mpb.li + Copyright (C) 2014 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org */ /* This file is part of ODR-DabMod. @@ -44,41 +46,66 @@ OutputUHD::OutputUHD( Logger& logger) : ModOutput(ModFormat(1), ModFormat(0)), RemoteControllable("uhd"), - myLogger(logger) + myLogger(logger), + myConf(config), + // Since we don't know the buffer size, we cannot initialise + // the buffers at object initialisation. + first_run(true), + activebuffer(1) + { + myMuting = 0; // is remote-controllable + + std::stringstream device; + device << myConf.device; - mySampleRate = config.sampleRate; - myTxGain = config.txgain; - myFrequency = config.frequency; - mute_no_timestamps = config.muteNoTimestamps; - enable_sync = config.enableSync; - myDevice = config.device; - myMuting = 0; + 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", - myDevice.c_str(), this); + 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(freq, "UHD transmission frequency"); RC_ADD_PARAMETER(muting, "mute the output by stopping the transmitter"); - #if ENABLE_UHD uhd::set_thread_priority_safe(); //create a usrp device MDEBUG("OutputUHD:Creating the usrp device with: %s...\n", - myDevice.c_str()); + device.str().c_str()); - myUsrp = uhd::usrp::multi_usrp::make(myDevice); + myUsrp = uhd::usrp::multi_usrp::make(device.str()); MDEBUG("OutputUHD:Using device: %s...\n", myUsrp->get_pp_string().c_str()); + if (myConf.masterClockRate != 0.0) { + double master_clk_rate = myUsrp->get_master_clock_rate(); + MDEBUG("OutputUHD:Checking master clock rate: %f...\n", master_clk_rate); + + if (myConf.masterClockRate != master_clk_rate) { + throw std::runtime_error("Cannot set USRP master_clock_rate. Aborted."); + } + } + + MDEBUG("OutputUHD:Setting REFCLK and PPS input...\n"); - myUsrp->set_clock_source(config.refclk_src); - myUsrp->set_time_source(config.pps_src); + myUsrp->set_clock_source(myConf.refclk_src); + myUsrp->set_time_source(myConf.pps_src); std::cerr << "UHD clock source is " << myUsrp->get_clock_source(0) << std::endl; @@ -87,28 +114,32 @@ OutputUHD::OutputUHD( myUsrp->get_time_source(0) << std::endl; //set the tx sample rate - MDEBUG("OutputUHD:Setting rate to %d...\n", mySampleRate); - myUsrp->set_tx_rate(mySampleRate); + MDEBUG("OutputUHD:Setting rate to %d...\n", myConf.sampleRate); + myUsrp->set_tx_rate(myConf.sampleRate); MDEBUG("OutputUHD:Actual TX Rate: %f Msps...\n", myUsrp->get_tx_rate()); - if (mySampleRate != myUsrp->get_tx_rate()) { + if (myConf.sampleRate != myUsrp->get_tx_rate()) { MDEBUG("OutputUHD: Cannot set sample\n"); throw std::runtime_error("Cannot set USRP sample rate. Aborted."); } //set the centre frequency - MDEBUG("OutputUHD:Setting freq to %f...\n", myFrequency); - myUsrp->set_tx_freq(myFrequency); - myFrequency = myUsrp->get_tx_freq(); - MDEBUG("OutputUHD:Actual frequency: %f\n", myFrequency); + MDEBUG("OutputUHD:Setting freq to %f...\n", myConf.frequency); + myUsrp->set_tx_freq(myConf.frequency); + myConf.frequency = myUsrp->get_tx_freq(); + MDEBUG("OutputUHD:Actual frequency: %f\n", myConf.frequency); - myUsrp->set_tx_gain(myTxGain); + myUsrp->set_tx_gain(myConf.txgain); MDEBUG("OutputUHD:Actual TX Gain: %f ...\n", myUsrp->get_tx_gain()); - MDEBUG("OutputUHD:Mute on missing timestamps: %s ...\n", mute_no_timestamps ? "enabled" : "disabled"); + MDEBUG("OutputUHD:Mute on missing timestamps: %s ...\n", + myConf.muteNoTimestamps ? "enabled" : "disabled"); + + if (myConf.enableSync && (myConf.pps_src == "none")) { + myLogger.level(warn) << + "OutputUHD: WARNING:" + " you are using synchronous transmission without PPS input!"; - if (enable_sync && (config.pps_src == "none")) { - myLogger.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: "); @@ -121,7 +152,7 @@ OutputUHD::OutputUHD( } } - if (config.pps_src != "none") { + 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; @@ -161,20 +192,18 @@ OutputUHD::OutputUHD( // preparing output thread worker data uwd.myUsrp = myUsrp; #else - myLogger.level(error) << "OutputUHD: UHD initialisation disabled at compile-time"; + myLogger.level(error) << + "OutputUHD: UHD initialisation disabled at compile-time"; #endif uwd.frame0.ts.timestamp_valid = false; uwd.frame1.ts.timestamp_valid = false; - uwd.sampleRate = mySampleRate; + uwd.sampleRate = myConf.sampleRate; uwd.sourceContainsTimestamp = false; - uwd.muteNoTimestamps = mute_no_timestamps; + uwd.muteNoTimestamps = myConf.muteNoTimestamps; uwd.logger = &myLogger; - uwd.refclk_lock_loss_behaviour = config.refclk_lock_loss_behaviour; + uwd.refclk_lock_loss_behaviour = myConf.refclk_lock_loss_behaviour; - // Since we don't know the buffer size, we cannot initialise - // the buffers here - first_run = true; shared_ptr b(new barrier(2)); mySyncBarrier = b; @@ -201,7 +230,7 @@ int OutputUHD::process(Buffer* dataIn, Buffer* dataOut) // On the first call, we must do some allocation and we must fill // the first buffer - // We will only wait on the barrier on the subsequent calls to + // We will only wait on the barrier on the subsequent calls to // OutputUHD::process if (first_run) { myLogger.level(debug) << "OutputUHD: UHD initialising..."; @@ -210,7 +239,8 @@ int OutputUHD::process(Buffer* dataIn, Buffer* dataOut) uwd.frame0.buf = malloc(uwd.bufsize); uwd.frame1.buf = malloc(uwd.bufsize); - uwd.sourceContainsTimestamp = enable_sync && myEtiReader->sourceContainsTimestamp(); + uwd.sourceContainsTimestamp = myConf.enableSync && + myEtiReader->sourceContainsTimestamp(); // The worker begins by transmitting buf0 memcpy(uwd.frame0.buf, dataIn->getData(), uwd.bufsize); @@ -229,8 +259,9 @@ int OutputUHD::process(Buffer* dataIn, Buffer* dataOut) if (lastLen != dataIn->getLength()) { // I expect that this never happens. - myLogger.level(emerg) << "OutputUHD: Fatal error, input length changed from " << - lastLen << " to " << dataIn->getLength(); + myLogger.level(emerg) << + "OutputUHD: Fatal error, input length changed from " << lastLen << + " to " << dataIn->getLength(); throw std::runtime_error("Non-constant input length!"); } mySyncBarrier.get()->wait(); @@ -239,7 +270,8 @@ int OutputUHD::process(Buffer* dataIn, Buffer* dataOut) // the worker sends the other. myEtiReader->calculateTimestamp(ts); - uwd.sourceContainsTimestamp = enable_sync && myEtiReader->sourceContainsTimestamp(); + uwd.sourceContainsTimestamp = myConf.enableSync && + myEtiReader->sourceContainsTimestamp(); if (activebuffer == 0) { memcpy(uwd.frame0.buf, dataIn->getData(), uwd.bufsize); @@ -286,7 +318,7 @@ void UHDWorker::process() size_t bufsize = myTxStream->get_max_num_samps(); #endif - bool check_refclk_loss = true; + bool check_refclk_loss = false; const complexf* in; uhd::tx_metadata_t md; @@ -311,7 +343,8 @@ void UHDWorker::process() frame = &(uwd->frame1); } else { - throw std::runtime_error("UHDWorker.process: workerbuffer is neither 0 nor 1 !"); + throw std::runtime_error( + "UHDWorker.process: workerbuffer is neither 0 nor 1 !"); } in = reinterpret_cast(frame->buf); @@ -329,16 +362,19 @@ void UHDWorker::process() try { // TODO: Is this check specific to the B100 and USRP2 ? if (! uwd->myUsrp->get_mboard_sensor("ref_locked", 0).to_bool()) { - uwd->logger->log(alert, "OutputUHD: External reference clock lock lost !"); + uwd->logger->log(alert, + "OutputUHD: External reference clock lock lost !"); if (uwd->refclk_lock_loss_behaviour == CRASH) { - throw std::runtime_error("OutputUHD: External reference clock lock lost."); + throw std::runtime_error( + "OutputUHD: External reference clock lock lost."); } } } catch (uhd::lookup_error &e) { check_refclk_loss = false; uwd->logger->log(warn, - "OutputUHD: This USRP does not have mboard sensor for ext clock loss. Check disabled."); + "OutputUHD: This USRP does not have mboard sensor for ext clock loss." + " Check disabled."); } } @@ -352,8 +388,10 @@ void UHDWorker::process() /* We have not received a full timestamp through * MNSC. We sleep through the frame. */ - uwd->logger->level(info) << "OutputUHD: Throwing sample " << - frame->fct << " away: incomplete timestamp " << tx_second << " + " << pps_offset; + uwd->logger->level(info) << + "OutputUHD: Throwing sample " << frame->fct << + " away: incomplete timestamp " << tx_second << + " + " << pps_offset; usleep(20000); //TODO should this be TM-dependant ? goto loopend; } @@ -391,7 +429,8 @@ void UHDWorker::process() if (last_pps > pps_offset) { uwd->logger->log(info, - "OutputUHD (usrp time: %f): frame %d; tx_second %zu; pps %.9f\n", + "OutputUHD (usrp time: %f): frame %d;" + " tx_second %zu; pps %.9f\n", usrp_time, frame->fct, tx_second, pps_offset); } @@ -402,11 +441,13 @@ void UHDWorker::process() /* There was some error decoding the timestamp */ if (uwd->muting) { - uwd->logger->log(info, "OutputUHD: Muting sample %d requested\n", + uwd->logger->log(info, + "OutputUHD: Muting sample %d requested\n", frame->fct); } else { - uwd->logger->log(info, "OutputUHD: Muting sample %d : no timestamp\n", + uwd->logger->log(info, + "OutputUHD: Muting sample %d : no timestamp\n", frame->fct); } usleep(20000); @@ -427,9 +468,10 @@ void UHDWorker::process() while (running && !uwd->muting && (num_acc_samps < sizeIn)) { size_t samps_to_send = std::min(sizeIn - num_acc_samps, bufsize); - //ensure the the last packet has EOB set if the timestamps has been refreshed - //and need to be reconsidered. - md.end_of_burst = (frame->ts.timestamp_refresh && (samps_to_send <= bufsize)); + //ensure the the last packet has EOB set if the timestamps has been + //refreshed and need to be reconsidered. + md.end_of_burst = (frame->ts.timestamp_refresh && + (samps_to_send <= bufsize)); //send a single packet size_t num_tx_samps = myTxStream->send( @@ -549,16 +591,16 @@ void OutputUHD::set_parameter(const string& parameter, const string& value) ss.exceptions ( stringstream::failbit | stringstream::badbit ); if (parameter == "txgain") { - ss >> myTxGain; + ss >> myConf.txgain; #if ENABLE_UHD - myUsrp->set_tx_gain(myTxGain); + myUsrp->set_tx_gain(myConf.txgain); #endif } else if (parameter == "freq") { - ss >> myFrequency; + ss >> myConf.frequency; #if ENABLE_UHD - myUsrp->set_tx_freq(myFrequency); - myFrequency = myUsrp->get_tx_freq(); + myUsrp->set_tx_freq(myConf.frequency); + myConf.frequency = myUsrp->get_tx_freq(); #endif } else if (parameter == "muting") { @@ -566,7 +608,8 @@ void OutputUHD::set_parameter(const string& parameter, const string& value) } else { stringstream ss; - ss << "Parameter '" << parameter << "' is not exported by controllable " << get_rc_name(); + ss << "Parameter '" << parameter + << "' is not exported by controllable " << get_rc_name(); throw ParameterError(ss.str()); } } @@ -575,16 +618,17 @@ const string OutputUHD::get_parameter(const string& parameter) const { stringstream ss; if (parameter == "txgain") { - ss << myTxGain; + ss << myConf.txgain; } else if (parameter == "freq") { - ss << myFrequency; + ss << myConf.frequency; } else if (parameter == "muting") { ss << myMuting; } else { - ss << "Parameter '" << parameter << "' is not exported by controllable " << get_rc_name(); + ss << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); throw ParameterError(ss.str()); } return ss.str(); diff --git a/src/OutputUHD.h b/src/OutputUHD.h index e30f897..86ecaae 100644 --- a/src/OutputUHD.h +++ b/src/OutputUHD.h @@ -2,16 +2,18 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Includes modifications for which no copyright is claimed - 2012, Matthias P. Braendli, matthias.braendli@mpb.li + Copyright (C) 2014 + 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. One thread runs - the whole modulator, and the second threads sends the data to the - device. The two threads are synchronised using a barrier. Communication - between the two threads is implemented using double buffering. At the - barrier, they exchange the buffers. + 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 is exchanged by swapping + buffers at a synchronisation barrier. */ /* @@ -149,6 +151,8 @@ class UHDWorker { /* This structure is used as initial configuration for OutputUHD */ struct OutputUHDConfig { std::string device; + std::string usrpType; // e.g. b100, b200, usrp2 + long masterClockRate; unsigned sampleRate; double frequency; int txgain; @@ -199,18 +203,13 @@ class OutputUHD: public ModOutput, public RemoteControllable { protected: Logger& myLogger; EtiReader *myEtiReader; - std::string myDevice; - unsigned mySampleRate; - int myTxGain; - double myFrequency; + OutputUHDConfig myConf; uhd::usrp::multi_usrp::sptr myUsrp; shared_ptr mySyncBarrier; UHDWorker worker; bool first_run; struct UHDWorkerData uwd; int activebuffer; - bool mute_no_timestamps; - bool enable_sync; // muting can only be changed using the remote control bool myMuting; -- cgit v1.2.3