From 51afbb4f9d0009b88de6cb065bbbb25a4c97f711 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 10 Feb 2017 11:31:05 +0100 Subject: Add support for SoapySDR --- Makefile.am | 2 + configure.ac | 13 ++- doc/example.ini | 11 +- src/DabMod.cpp | 111 ++++++++++++-------- src/OutputSoapy.cpp | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/OutputSoapy.h | 138 +++++++++++++++++++++++++ src/OutputUHD.cpp | 3 + src/OutputUHD.h | 3 - src/Utils.cpp | 47 +++++++++ src/Utils.h | 9 +- 10 files changed, 567 insertions(+), 57 deletions(-) create mode 100644 src/OutputSoapy.cpp create mode 100644 src/OutputSoapy.h diff --git a/Makefile.am b/Makefile.am index c0dbe46..96e2ff3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -78,6 +78,8 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ src/TimestampDecoder.cpp \ src/OutputUHD.cpp \ src/OutputUHD.h \ + src/OutputSoapy.cpp \ + src/OutputSoapy.h \ src/InputMemory.cpp \ src/InputMemory.h \ src/InputFileReader.cpp \ diff --git a/configure.ac b/configure.ac index f622471..e2a7dd5 100644 --- a/configure.ac +++ b/configure.ac @@ -105,9 +105,11 @@ AM_CONDITIONAL([IS_GIT_REPO], [test -d '.git']) # Defines for config.h AX_PTHREAD([], AC_MSG_ERROR([requires pthread])) -AC_SUBST([CFLAGS], ["$EXTRA $FFTW_CFLAGS $PTHREAD_CFLAGS"]) -AC_SUBST([CXXFLAGS], ["$CXXFLAGS $EXTRA $FFTW_CFLAGS $PTHREAD_CFLAGS"]) -AC_SUBST([LIBS], ["$FFTW_LIBS $PTHREAD_LIBS $ZMQ_LIBS"]) +PKG_CHECK_MODULES([SOAPYSDR], [SoapySDR], enable_soapysdr=yes, enable_soapysdr=no) + +AC_SUBST([CFLAGS], ["$EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"]) +AC_SUBST([CXXFLAGS], ["$CXXFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"]) +AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS"]) # Checks for UHD. AS_IF([test "x$enable_output_uhd" = "xyes"], @@ -117,6 +119,9 @@ AS_IF([test "x$enable_output_uhd" = "xyes"], AS_IF([test "x$enable_output_uhd" = "xyes"], [AC_DEFINE(HAVE_OUTPUT_UHD, [1], [Define if UHD output is enabled])]) +AS_IF([test "x$enable_soapysdr" = "xyes"], + [AC_DEFINE(HAVE_SOAPYSDR, [1], [Define if SoapySDR output is enabled])]) + AX_BOOST_BASE([1.54.0], [], AC_MSG_ERROR([BOOST 1.54 or later is required])) AC_CHECK_LIB([boost_system], [main], [], [AC_MSG_ERROR([library boost_system is missing])]) AC_CHECK_LIB([boost_thread], [main], [], [AC_MSG_ERROR([library boost_thread is missing])]) @@ -192,7 +197,7 @@ echo "***********************************************" echo enabled="" disabled="" -for feat in debug prof trace output_uhd zeromq +for feat in debug prof trace output_uhd zeromq soapysdr do eval var=\$enable_$feat AS_IF([test "x$var" = "xyes"], diff --git a/doc/example.ini b/doc/example.ini index 3dd8505..6887972 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -132,7 +132,7 @@ enabled=1 ;filtertapsfile=simple_taps.txt [output] -; choose output: possible values: uhd, file, zmq +; choose output: possible values: uhd, file, zmq, soapysdr output=uhd [fileoutput] @@ -236,6 +236,15 @@ listen=tcp://*:54001 ; Please see man zmq_socket for documentation socket_type=pub +; section defining the SoapySDR output settings. All these +; options are given to the SoapySDR library. +[soapyoutput] +device= +master_clock_rate=32768000 +txgain=40 +;frequency=234208000 +channel=13C + ; Used for SFN with the UHD output [delaymanagement] diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 44951d7..9cb25a3 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -39,6 +39,9 @@ #if defined(HAVE_OUTPUT_UHD) # include "OutputUHD.h" #endif +#if defined(HAVE_SOAPYSDR) +# include "OutputSoapy.h" +#endif #include "OutputZeroMQ.h" #include "InputReader.h" #include "PcDebug.h" @@ -140,6 +143,7 @@ int launch_modulator(int argc, char* argv[]) int useFileOutput = 0; std::string fileOutputFormat = "complexf"; int useUHDOutput = 0; + int useSoapyOutput = 0; size_t outputRate = 2048000; size_t clockRate = 0; @@ -171,6 +175,10 @@ int launch_modulator(int argc, char* argv[]) OutputUHDConfig outputuhd_conf; #endif +#if defined(HAVE_SOAPYSDR) + OutputSoapyConfig outputsoapy_conf; +#endif + modulator_data m; // To handle the timestamp offset of the modulator @@ -312,6 +320,9 @@ int launch_modulator(int argc, char* argv[]) #if defined(HAVE_OUTPUT_UHD) "output_uhd " << #endif +#if defined(HAVE_SOAPYSDR) + "output_soapysdr " << +#endif #if defined(__FAST_MATH__) "fast-math" << #endif @@ -485,50 +496,7 @@ int launch_modulator(int argc, char* argv[]) throw std::runtime_error("Configuration error"); } else if (outputuhd_conf.frequency == 0) { - double freq; - if (chan == "5A") freq = 174928000; - else if (chan == "5B") freq = 176640000; - else if (chan == "5C") freq = 178352000; - else if (chan == "5D") freq = 180064000; - else if (chan == "6A") freq = 181936000; - else if (chan == "6B") freq = 183648000; - else if (chan == "6C") freq = 185360000; - else if (chan == "6D") freq = 187072000; - else if (chan == "7A") freq = 188928000; - else if (chan == "7B") freq = 190640000; - else if (chan == "7C") freq = 192352000; - else if (chan == "7D") freq = 194064000; - else if (chan == "8A") freq = 195936000; - else if (chan == "8B") freq = 197648000; - else if (chan == "8C") freq = 199360000; - else if (chan == "8D") freq = 201072000; - else if (chan == "9A") freq = 202928000; - else if (chan == "9B") freq = 204640000; - else if (chan == "9C") freq = 206352000; - else if (chan == "9D") freq = 208064000; - else if (chan == "10A") freq = 209936000; - else if (chan == "10B") freq = 211648000; - else if (chan == "10C") freq = 213360000; - else if (chan == "10D") freq = 215072000; - else if (chan == "11A") freq = 216928000; - else if (chan == "11B") freq = 218640000; - else if (chan == "11C") freq = 220352000; - else if (chan == "11D") freq = 222064000; - else if (chan == "12A") freq = 223936000; - else if (chan == "12B") freq = 225648000; - else if (chan == "12C") freq = 227360000; - else if (chan == "12D") freq = 229072000; - else if (chan == "13A") freq = 230784000; - else if (chan == "13B") freq = 232496000; - else if (chan == "13C") freq = 234208000; - else if (chan == "13D") freq = 235776000; - else if (chan == "13E") freq = 237488000; - else if (chan == "13F") freq = 239200000; - else { - std::cerr << " UHD output: channel " << chan << " does not exist in table\n"; - throw std::out_of_range("UHD channel selection error"); - } - outputuhd_conf.frequency = freq; + outputuhd_conf.frequency = parseChannel(chan); } else if (outputuhd_conf.frequency != 0 && chan != "") { std::cerr << " UHD output: cannot define both frequency and channel.\n"; @@ -558,6 +526,31 @@ int launch_modulator(int argc, char* argv[]) useUHDOutput = 1; } #endif +#if defined(HAVE_SOAPYSDR) + else if (output_selected == "soapysdr") { + outputsoapy_conf.device = pt.get("soapyoutput.device", ""); + outputsoapy_conf.masterClockRate = pt.get("soapyoutput.master_clock_rate", 0); + + outputsoapy_conf.txgain = pt.get("soapyoutput.txgain", 0.0); + outputsoapy_conf.frequency = pt.get("soapyoutput.frequency", 0); + std::string chan = pt.get("soapyoutput.channel", ""); + outputsoapy_conf.dabMode = dabMode; + + if (outputsoapy_conf.frequency == 0 && chan == "") { + std::cerr << " soapy output enabled, but neither frequency nor channel defined.\n"; + throw std::runtime_error("Configuration error"); + } + else if (outputsoapy_conf.frequency == 0) { + outputsoapy_conf.frequency = parseChannel(chan); + } + else if (outputsoapy_conf.frequency != 0 && chan != "") { + std::cerr << " soapy output: cannot define both frequency and channel.\n"; + throw std::runtime_error("Configuration error"); + } + + useSoapyOutput = 1; + } +#endif #if defined(HAVE_ZEROMQ) else if (output_selected == "zmq") { outputName = pt.get("zmqoutput.listen"); @@ -649,7 +642,7 @@ int launch_modulator(int argc, char* argv[]) throw std::invalid_argument("Invalid command line options"); } - if (!useFileOutput && !useUHDOutput && !useZeroMQOutput) { + if (!useFileOutput && !useUHDOutput && !useZeroMQOutput && !useSoapyOutput) { etiLog.level(error) << "Output not specified"; fprintf(stderr, "Must specify output !"); throw std::runtime_error("Configuration error"); @@ -678,6 +671,15 @@ int launch_modulator(int argc, char* argv[]) outputuhd_conf.refclk_src.c_str(), outputuhd_conf.pps_src.c_str()); } +#endif +#if defined(HAVE_SOAPYSDR) + else if (useSoapyOutput) { + fprintf(stderr, " SoapySDR\n" + " Device: %s\n" + " master_clock_rate: %ld\n", + outputsoapy_conf.device.c_str(), + outputsoapy_conf.masterClockRate); + } #endif else if (useZeroMQOutput) { fprintf(stderr, " ZeroMQ\n" @@ -763,6 +765,15 @@ int launch_modulator(int argc, char* argv[]) rcs.enrol((OutputUHD*)output.get()); } #endif +#if defined(HAVE_SOAPYSDR) + else if (useSoapyOutput) { + /* We normalise the same way as for the UHD output */ + normalise = 1.0f / normalise_factor; + outputsoapy_conf.sampleRate = outputRate; + output = make_shared(outputsoapy_conf); + rcs.enrol((OutputSoapy*)output.get()); + } +#endif #if defined(HAVE_ZEROMQ) else if (useZeroMQOutput) { /* We normalise the same way as for the UHD output */ @@ -812,6 +823,11 @@ int launch_modulator(int argc, char* argv[]) ((OutputUHD*)output.get())->setETISource(modulator->getEtiSource()); } #endif +#if defined(HAVE_SOAPYSDR) + if (useSoapyOutput) { + ((OutputSoapy*)output.get())->setETISource(modulator->getEtiSource()); + } +#endif size_t framecount = 0; @@ -863,6 +879,11 @@ int launch_modulator(int argc, char* argv[]) ((OutputUHD*)output.get())->setETISource(modulator->getEtiSource()); } #endif +#if defined(HAVE_SOAPYSDR) + if (useSoapyOutput) { + ((OutputSoapy*)output.get())->setETISource(modulator->getEtiSource()); + } +#endif m.inputReader->PrintInfo(); diff --git a/src/OutputSoapy.cpp b/src/OutputSoapy.cpp new file mode 100644 index 0000000..3f8db88 --- /dev/null +++ b/src/OutputSoapy.cpp @@ -0,0 +1,287 @@ +/* + 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 using the SoapySDR library that can output to + many devices. +*/ + +/* + 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 . + */ + +#include "OutputSoapy.h" +#ifdef HAVE_SOAPYSDR + +#include +#include +#include + +#include "Log.h" +#include "Utils.h" + +#include + +static const size_t FRAMES_MAX_SIZE = 2; + + +using namespace std; + + + +OutputSoapy::OutputSoapy(OutputSoapyConfig& config) : + ModOutput(), + RemoteControllable("soapy"), + m_conf(config), + m_device(nullptr) +{ + RC_ADD_PARAMETER(txgain, "SoapySDR analog daughterboard TX gain"); + RC_ADD_PARAMETER(freq, "SoapySDR transmission frequency"); + RC_ADD_PARAMETER(overflows, "SoapySDR overflow count [r/o]"); + RC_ADD_PARAMETER(underflows, "SoapySDR underflow count [r/o]"); + + etiLog.level(info) << + "OutputSoapy:Creating the device with: " << + config.device; + try + { + m_device = SoapySDR::Device::make(config.device); + stringstream ss; + ss << "SoapySDR driver=" << m_device->getDriverKey(); + ss << " hardware=" << m_device->getHardwareKey(); + for (const auto &it : m_device->getHardwareInfo()) + { + ss << " " << it.first << "=" << it.second; + } + } + catch (const std::exception &ex) + { + etiLog.level(error) << "Error making SoapySDR device: " << + ex.what(); + throw std::runtime_error("Cannot create SoapySDR output"); + } + + m_device->setMasterClockRate(config.masterClockRate); + etiLog.level(info) << "SoapySDR master clock rate set to " << + m_device->getMasterClockRate()/1000.0 << " kHz"; + + m_device->setSampleRate(SOAPY_SDR_TX, 0, m_conf.sampleRate); + etiLog.level(info) << "OutputSoapySDR:Actual TX rate: " << + m_device->getSampleRate(SOAPY_SDR_TX, 0) / 1000.0 << + " ksps."; + + m_device->setFrequency(SOAPY_SDR_TX, 0, m_conf.frequency); + m_conf.frequency = m_device->getFrequency(SOAPY_SDR_TX, 0); + etiLog.level(info) << "OutputSoapySDR:Actual frequency: " << + m_conf.frequency / 1000.0 << + " kHz."; + + m_device->setGain(SOAPY_SDR_TX, 0, m_conf.txgain); + etiLog.level(info) << "OutputSoapySDR:Actual tx gain: " << + m_device->getGain(SOAPY_SDR_TX, 0); + +} + +OutputSoapy::~OutputSoapy() +{ + m_worker.stop(); + if (m_device != nullptr) { + SoapySDR::Device::unmake(m_device); + } +} + +void SoapyWorker::stop() +{ + running = false; + queue.push({}); + if (m_thread.joinable()) { + m_thread.join(); + } +} + +void SoapyWorker::start(SoapySDR::Device *device) +{ + m_device = device; + underflows = 0; + overflows = 0; + running = true; + m_thread = std::thread(&SoapyWorker::process_start, this); +} + +void SoapyWorker::process_start() +{ + // Set thread priority to realtime + if (int ret = set_realtime_prio(1)) { + etiLog.level(error) << "Could not set priority for SoapySDR worker:" << ret; + } + + set_thread_name("soapyworker"); + + std::vector channels; + channels.push_back(0); + auto stream = m_device->setupStream(SOAPY_SDR_TX, "CF32", channels); + m_device->activateStream(stream); + process(stream); + m_device->closeStream(stream); + running = false; + etiLog.level(warn) << "SoapySDR worker terminated"; +} + +void SoapyWorker::process(SoapySDR::Stream *stream) +{ + while (running) { + struct SoapyWorkerFrameData frame; + queue.wait_and_pop(frame); + + // The frame buffer contains bytes representing FC32 samples + const complexf *buf = reinterpret_cast(frame.buf.data()); + const size_t numSamples = frame.buf.size() / sizeof(complexf); + if ((frame.buf.size() % sizeof(complexf)) != 0) { + throw std::runtime_error("OutputSoapy: invalid buffer size"); + } + + // Stream MTU is in samples, not bytes. + const size_t mtu = m_device->getStreamMTU(stream); + + size_t num_acc_samps = 0; + while (running && (num_acc_samps < numSamples)) { + const void *buffs[1]; + buffs[0] = buf + num_acc_samps; + + const size_t samps_to_send = std::min(numSamples - num_acc_samps, mtu); + + int flags = 0; + + auto ret = m_device->writeStream(stream, buffs, samps_to_send, flags); + + if (ret == SOAPY_SDR_TIMEOUT) { + continue; + } + else if (ret == SOAPY_SDR_OVERFLOW) { + overflows++; + continue; + } + else if (ret == SOAPY_SDR_UNDERFLOW) { + underflows++; + continue; + } + + if (ret < 0) { + etiLog.level(error) << "Unexpected stream error " << + SoapySDR::errToStr(ret); + running = false; + } + + num_acc_samps += ret; + } + } +} + +int OutputSoapy::process(Buffer* dataIn) +{ + if (first_run) { + m_worker.start(m_device); + first_run = false; + } + else if (!m_worker.running) { + etiLog.level(error) << "OutputSoapy: worker thread died"; + throw std::runtime_error("Fault in OutputSoapy"); + } + + SoapyWorkerFrameData frame; + m_eti_source->calculateTimestamp(frame.ts); + + + if (frame.ts.fct == -1) { + etiLog.level(info) << + "OutputSoapy: dropping one frame with invalid FCT"; + } + else { + const uint8_t* pInData = reinterpret_cast(dataIn->getData()); + frame.buf.resize(dataIn->getLength()); + std::copy(pInData, pInData + dataIn->getLength(), + frame.buf.begin()); + m_worker.queue.push_wait_if_full(frame, FRAMES_MAX_SIZE); + } + + return dataIn->getLength(); +} + + +void OutputSoapy::setETISource(EtiSource *etiSource) +{ + m_eti_source = etiSource; +} + +void OutputSoapy::set_parameter(const string& parameter, const string& value) +{ + stringstream ss(value); + ss.exceptions ( stringstream::failbit | stringstream::badbit ); + + if (parameter == "txgain") { + ss >> m_conf.txgain; + m_device->setGain(SOAPY_SDR_TX, 0, m_conf.txgain); + } + else if (parameter == "freq") { + ss >> m_conf.frequency; + m_device->setFrequency(SOAPY_SDR_TX, 0, m_conf.frequency); + m_conf.frequency = m_device->getFrequency(SOAPY_SDR_TX, 0); + } + else if (parameter == "underflows") { + throw ParameterError("Parameter 'underflows' is read-only"); + } + else if (parameter == "overflows") { + throw ParameterError("Parameter 'overflows' is read-only"); + } + else { + stringstream ss; + ss << "Parameter '" << parameter + << "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); + } +} + +const string OutputSoapy::get_parameter(const string& parameter) const +{ + stringstream ss; + if (parameter == "txgain") { + ss << m_conf.txgain; + } + else if (parameter == "freq") { + ss << m_conf.frequency; + } + else if (parameter == "underflows") { + ss << m_worker.underflows; + } + else if (parameter == "overflows") { + ss << m_worker.overflows; + } + else { + ss << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); + } + return ss.str(); +} + +#endif // HAVE_SOAPYSDR + diff --git a/src/OutputSoapy.h b/src/OutputSoapy.h new file mode 100644 index 0000000..230f11b --- /dev/null +++ b/src/OutputSoapy.h @@ -0,0 +1,138 @@ +/* + 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 using the SoapySDR library that can output to + many devices. +*/ + +/* + 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 + +#ifdef HAVE_SOAPYSDR +#include +#include +#include +#include + +#include +#include + +#include "ModPlugin.h" +#include "EtiReader.h" +#include "RemoteControl.h" +#include "ThreadsafeQueue.h" + +typedef std::complex complexf; + +/* This structure is used as initial configuration for the Soapy output. + * It must also contain all remote-controllable settings, otherwise + * they will get lost on a modulator restart. */ +struct OutputSoapyConfig { + std::string device; + + long masterClockRate = 32768000; + unsigned sampleRate = 2048000; + double frequency = 0.0; + double txgain = 0.0; + unsigned dabMode = 0; +}; + +// Each frame contains one OFDM frame, and its +// associated timestamp +struct SoapyWorkerFrameData { + // 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; +}; + +class SoapyWorker +{ + public: + ThreadsafeQueue queue; + SoapySDR::Device *m_device; + std::atomic running; + size_t underflows; + size_t overflows; + + SoapyWorker() {} + SoapyWorker(const SoapyWorker&) = delete; + SoapyWorker operator=(const SoapyWorker&) = delete; + ~SoapyWorker() { stop(); } + + void start(SoapySDR::Device *device); + void stop(void); + + private: + std::thread m_thread; + + void process_start(void); + void process(SoapySDR::Stream *stream); +}; + +class OutputSoapy: public ModOutput, public RemoteControllable +{ + public: + OutputSoapy(OutputSoapyConfig& config); + OutputSoapy(const OutputSoapy& other) = delete; + OutputSoapy& operator=(const OutputSoapy& other) = delete; + ~OutputSoapy(); + + int process(Buffer* dataIn); + + const char* name() { return "OutputSoapy"; } + + 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: + SoapyWorker m_worker; + EtiSource *m_eti_source; + OutputSoapyConfig& m_conf; + + SoapySDR::Device *m_device; + + bool first_run = true; +}; + + +#endif //HAVE_SOAPYSDR diff --git a/src/OutputUHD.cpp b/src/OutputUHD.cpp index d26ce77..b4344f5 100644 --- a/src/OutputUHD.cpp +++ b/src/OutputUHD.cpp @@ -49,6 +49,9 @@ using namespace std; +// Maximum number of frames that can wait in uwd.frames +static const size_t FRAMES_MAX_SIZE = 2; + typedef std::complex complexf; std::string stringtrim(const std::string &s) diff --git a/src/OutputUHD.h b/src/OutputUHD.h index 9766f36..e477e47 100644 --- a/src/OutputUHD.h +++ b/src/OutputUHD.h @@ -71,9 +71,6 @@ DESCRIPTION: // frames are too far in the future #define TIMESTAMP_MARGIN_FUTURE 0.5 -// Maximum number of frames that can wait in uwd.frames -#define FRAMES_MAX_SIZE 2 - typedef std::complex complexf; // Each frame contains one OFDM frame, and its diff --git a/src/Utils.cpp b/src/Utils.cpp index b93f2c1..f4610a4 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -131,3 +131,50 @@ void set_thread_name(const char *name) prctl(PR_SET_NAME,name,0,0,0); } +double parseChannel(const std::string& chan) +{ + double freq; + if (chan == "5A") freq = 174928000; + else if (chan == "5B") freq = 176640000; + else if (chan == "5C") freq = 178352000; + else if (chan == "5D") freq = 180064000; + else if (chan == "6A") freq = 181936000; + else if (chan == "6B") freq = 183648000; + else if (chan == "6C") freq = 185360000; + else if (chan == "6D") freq = 187072000; + else if (chan == "7A") freq = 188928000; + else if (chan == "7B") freq = 190640000; + else if (chan == "7C") freq = 192352000; + else if (chan == "7D") freq = 194064000; + else if (chan == "8A") freq = 195936000; + else if (chan == "8B") freq = 197648000; + else if (chan == "8C") freq = 199360000; + else if (chan == "8D") freq = 201072000; + else if (chan == "9A") freq = 202928000; + else if (chan == "9B") freq = 204640000; + else if (chan == "9C") freq = 206352000; + else if (chan == "9D") freq = 208064000; + else if (chan == "10A") freq = 209936000; + else if (chan == "10B") freq = 211648000; + else if (chan == "10C") freq = 213360000; + else if (chan == "10D") freq = 215072000; + else if (chan == "11A") freq = 216928000; + else if (chan == "11B") freq = 218640000; + else if (chan == "11C") freq = 220352000; + else if (chan == "11D") freq = 222064000; + else if (chan == "12A") freq = 223936000; + else if (chan == "12B") freq = 225648000; + else if (chan == "12C") freq = 227360000; + else if (chan == "12D") freq = 229072000; + else if (chan == "13A") freq = 230784000; + else if (chan == "13B") freq = 232496000; + else if (chan == "13C") freq = 234208000; + else if (chan == "13D") freq = 235776000; + else if (chan == "13E") freq = 237488000; + else if (chan == "13F") freq = 239200000; + else { + std::cerr << " soapy output: channel " << chan << " does not exist in table\n"; + throw std::out_of_range("soapy channel selection error"); + } + return freq; +} diff --git a/src/Utils.h b/src/Utils.h index 46c46d5..e2b8a2f 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2015 + Copyright (C) 2017 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -25,8 +25,7 @@ along with ODR-DabMod. If not, see . */ -#ifndef __UTILS_H_ -#define __UTILS_H_ +#pragma once #ifdef HAVE_CONFIG_H # include "config.h" @@ -36,6 +35,7 @@ #include #include #include +#include void printUsage(char* progName); @@ -62,5 +62,6 @@ int set_realtime_prio(int prio); void set_thread_name(const char *name); -#endif +// Convert a channel like 10A to a frequency +double parseChannel(const std::string& chan); -- cgit v1.2.3