diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2017-02-10 11:31:05 +0100 | 
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2017-02-10 11:31:05 +0100 | 
| commit | 51afbb4f9d0009b88de6cb065bbbb25a4c97f711 (patch) | |
| tree | df0163a2387e1424ebe104439333ad09361bfed6 /src | |
| parent | 245e62aed2fd55ef3b6569e5c0b8466f5aa72cbd (diff) | |
| download | dabmod-51afbb4f9d0009b88de6cb065bbbb25a4c97f711.tar.gz dabmod-51afbb4f9d0009b88de6cb065bbbb25a4c97f711.tar.bz2 dabmod-51afbb4f9d0009b88de6cb065bbbb25a4c97f711.zip | |
Add support for SoapySDR
Diffstat (limited to 'src')
| -rw-r--r-- | src/DabMod.cpp | 111 | ||||
| -rw-r--r-- | src/OutputSoapy.cpp | 287 | ||||
| -rw-r--r-- | src/OutputSoapy.h | 138 | ||||
| -rw-r--r-- | src/OutputUHD.cpp | 3 | ||||
| -rw-r--r-- | src/OutputUHD.h | 3 | ||||
| -rw-r--r-- | src/Utils.cpp | 47 | ||||
| -rw-r--r-- | src/Utils.h | 9 | 
7 files changed, 546 insertions, 52 deletions
| 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<long>("soapyoutput.master_clock_rate", 0); + +            outputsoapy_conf.txgain = pt.get("soapyoutput.txgain", 0.0); +            outputsoapy_conf.frequency = pt.get<double>("soapyoutput.frequency", 0); +            std::string chan = pt.get<std::string>("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<std::string>("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"); @@ -679,6 +672,15 @@ int launch_modulator(int argc, char* argv[])                  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"                          "  Listening on: %s\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>(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 <http://www.gnu.org/licenses/>. + */ + +#include "OutputSoapy.h" +#ifdef HAVE_SOAPYSDR + +#include <SoapySDR/Errors.hpp> +#include <deque> +#include <chrono> + +#include "Log.h" +#include "Utils.h" + +#include <stdio.h> + +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<size_t> 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<complexf*>(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<uint8_t*>(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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#   include <config.h> +#endif + +#ifdef HAVE_SOAPYSDR +#include <SoapySDR/Version.hpp> +#include <SoapySDR/Modules.hpp> +#include <SoapySDR/Registry.hpp> +#include <SoapySDR/Device.hpp> + +#include <string> +#include <memory> + +#include "ModPlugin.h" +#include "EtiReader.h" +#include "RemoteControl.h" +#include "ThreadsafeQueue.h" + +typedef std::complex<float> 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<uint8_t> 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<SoapyWorkerFrameData> queue; +        SoapySDR::Device *m_device; +        std::atomic<bool> 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<float> 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<float> 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 <http://www.gnu.org/licenses/>.   */ -#ifndef __UTILS_H_ -#define __UTILS_H_ +#pragma once  #ifdef HAVE_CONFIG_H  #   include "config.h" @@ -36,6 +35,7 @@  #include <unistd.h>  #include <stdio.h>  #include <time.h> +#include <string>  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); | 
