aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--configure.ac16
-rw-r--r--src/DabMod.cpp1
-rw-r--r--src/OfdmGenerator.cpp2
-rw-r--r--src/output/Lime.cpp346
-rw-r--r--src/output/Lime.h101
6 files changed, 466 insertions, 4 deletions
diff --git a/Makefile.am b/Makefile.am
index 4787cff..382b578 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -621,6 +621,8 @@ odr_dabmod_SOURCES += \
src/output/UHD.h \
src/output/USRPTime.cpp \
src/output/USRPTime.h \
+ src/output/Lime.cpp \
+ src/output/Lime.h \
src/PhaseReference.cpp \
src/PhaseReference.h \
src/QpskSymbolMapper.cpp \
@@ -645,7 +647,7 @@ odr_dabmod_SOURCES += \
src/TII.h \
$(ASIO_FILES)
-odr_dabmod_LDADD += $(UHD_LIBS)
+odr_dabmod_LDADD += $(UHD_LIBS) $(LIMESDR_LIBS)
endif
if COMPILE_EDI
diff --git a/configure.ac b/configure.ac
index 753b407..e05be61 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,6 +58,9 @@ AC_ARG_ENABLE([native],
AC_ARG_ENABLE([easydabv3],
[AS_HELP_STRING([--enable-easydabv3], [Build for EasyDABv3 board])],
[], [enable_easydabv3=no])
+AC_ARG_ENABLE([limesdr],
+ [AS_HELP_STRING([--enable-limesdr], [Build for LimeSDR board])],
+ [], [enable_limesdr=no])
# UHD support control
AC_ARG_ENABLE([output_uhd],
@@ -106,9 +109,13 @@ AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
AS_IF([test "x$enable_easydabv3" = "xno"],
[PKG_CHECK_MODULES([SOAPYSDR], [SoapySDR], enable_soapysdr=yes, enable_soapysdr=no)])
+AS_IF([test "x$enable_limesdr" = "xyes"],
+ [AC_CHECK_LIB([LimeSuite], [LMS_Init], [LIMESDR_LIBS="-lLimeSuite"],
+ [AC_MSG_ERROR([LimeSDR LimeSuite is required])])])
+
AC_SUBST([CFLAGS], ["$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"])
+AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS $LIMESDR_LIBS"])
AS_IF([test "x$enable_easydabv3" = "xyes" && test "x$enable_output_uhd" == "xyes"],
AC_MSG_ERROR([Cannot enable both EasyDABv3 and UHD output]))
@@ -127,6 +134,11 @@ AS_IF([test "x$enable_soapysdr" = "xyes"],
AS_IF([test "x$enable_edi" = "xyes"],
[AC_DEFINE(HAVE_EDI, [1], [Define if EDI input is enabled]) ])
+AS_IF([test "x$enable_limesdr" = "xyes"],
+ [AC_DEFINE(HAVE_LIMESDR, [1], [Define if LimeSDR input is enabled]) ])
+
+
+
AM_CONDITIONAL([COMPILE_EDI], [test "x$enable_edi" = "xyes"])
AS_IF([test "x$enable_easydabv3" = "xyes"],
@@ -200,7 +212,7 @@ echo "***********************************************"
echo
enabled=""
disabled=""
-for feat in edi prof trace output_uhd zeromq soapysdr easydabv3
+for feat in edi prof trace output_uhd zeromq soapysdr easydabv3 limesdr
do
eval var=\$enable_$feat
AS_IF([test "x$var" = "xyes"],
diff --git a/src/DabMod.cpp b/src/DabMod.cpp
index c53b7c9..f804512 100644
--- a/src/DabMod.cpp
+++ b/src/DabMod.cpp
@@ -55,6 +55,7 @@
#include "output/SDR.h"
#include "output/UHD.h"
#include "output/Soapy.h"
+#include "output/Lime.h"
#include "OutputZeroMQ.h"
#include "InputReader.h"
#include "PcDebug.h"
diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp
index 2642bff..00b53ad 100644
--- a/src/OfdmGenerator.cpp
+++ b/src/OfdmGenerator.cpp
@@ -229,7 +229,7 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut)
if (myCfr) {
complexf *symbol = reinterpret_cast<complexf*>(myFftOut);
- myPaprBeforeCFR.process_block(symbol, mySpacing);
+ myPaprBeforeCFR.process_block(symbol, mySpacing);
if (myMERCalcIndex == i) {
before_cfr.resize(mySpacing);
diff --git a/src/output/Lime.cpp b/src/output/Lime.cpp
new file mode 100644
index 0000000..82a42be
--- /dev/null
+++ b/src/output/Lime.cpp
@@ -0,0 +1,346 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2018
+ Evariste F5OEO, evaristec@gmail.com
+
+
+DESCRIPTION:
+ It is an output driver using the LimeSDR library.
+*/
+
+/*
+ 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 "output/Lime.h"
+
+#ifdef HAVE_LIMESDR
+
+//#include <SoapySDR/Errors.hpp>
+#include <chrono>
+#include <limits>
+#include <cstdio>
+#include <iomanip>
+
+#include "Log.h"
+#include "Utils.h"
+
+using namespace std;
+
+namespace Output {
+
+static constexpr size_t FRAMES_MAX_SIZE = 2;
+
+Lime::Lime(SDRDeviceConfig& config) :
+ SDRDevice(),
+ m_conf(config)
+{
+ etiLog.level(info) <<
+ "Lime:Creating the device with: " <<
+ m_conf.device;
+ /*
+ try {
+ m_device = SoapySDR::Device::make(m_conf.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(m_conf.masterClockRate);
+ etiLog.level(info) << "SoapySDR master clock rate set to " <<
+ std::fixed << std::setprecision(4) <<
+ m_device->getMasterClockRate()/1000.0 << " kHz";
+
+ m_device->setSampleRate(SOAPY_SDR_TX, 0, m_conf.sampleRate);
+ etiLog.level(info) << "SoapySDR:Actual TX rate: " <<
+ std::fixed << std::setprecision(4) <<
+ m_device->getSampleRate(SOAPY_SDR_TX, 0) / 1000.0 <<
+ " ksps.";
+
+ tune(m_conf.lo_offset, m_conf.frequency);
+ m_conf.frequency = m_device->getFrequency(SOAPY_SDR_TX, 0);
+ etiLog.level(info) << "SoapySDR:Actual frequency: " <<
+ std::fixed << std::setprecision(3) <<
+ m_conf.frequency / 1000.0 << " kHz.";
+
+ m_device->setGain(SOAPY_SDR_TX, 0, m_conf.txgain);
+ etiLog.level(info) << "SoapySDR:Actual tx gain: " <<
+ std::fixed << std::setprecision(2) <<
+ m_device->getGain(SOAPY_SDR_TX, 0);
+
+ if (not m_conf.tx_antenna.empty()) {
+ m_device->setAntenna(SOAPY_SDR_TX, 0, m_conf.tx_antenna);
+ }
+ etiLog.level(info) << "SoapySDR:Actual tx antenna: " <<
+ m_device->getAntenna(SOAPY_SDR_TX, 0);
+
+ if (m_device->hasHardwareTime()) {
+ using namespace std::chrono;
+ auto n = system_clock::now();
+ const long long ticks = duration_cast<nanoseconds>(n.time_since_epoch()).count();
+ m_device->setHardwareTime(ticks);
+ }
+
+ const std::vector<size_t> channels({0});
+ m_tx_stream = m_device->setupStream(SOAPY_SDR_TX, "CF32", channels);
+ m_rx_stream = m_device->setupStream(SOAPY_SDR_RX, "CF32", channels);
+ */
+}
+
+Lime::~Lime()
+{
+ /*
+ if (m_device != nullptr) {
+ if (m_tx_stream != nullptr) {
+ m_device->closeStream(m_tx_stream);
+ }
+
+ if (m_rx_stream != nullptr) {
+ m_device->closeStream(m_rx_stream);
+ }
+ SoapySDR::Device::unmake(m_device);
+ }*/
+}
+
+void Lime::tune(double lo_offset, double frequency)
+{
+ /* if (not m_device) throw runtime_error("Soapy device not set up");
+
+ SoapySDR::Kwargs offset_arg;
+ offset_arg["OFFSET"] = to_string(lo_offset);
+ m_device->setFrequency(SOAPY_SDR_TX, 0, m_conf.frequency, offset_arg);
+ */
+}
+
+double Lime::get_tx_freq(void) const
+{
+ if (not m_device) throw runtime_error("Lime device not set up");
+
+ // TODO lo offset
+ return 0;//m_device->getFrequency(SOAPY_SDR_TX, 0);
+}
+
+void Lime::set_txgain(double txgain)
+{
+ m_conf.txgain = txgain;
+ if (not m_device) throw runtime_error("Lime device not set up");
+ //m_device->setGain(SOAPY_SDR_TX, 0, m_conf.txgain);
+}
+
+double Lime::get_txgain(void) const
+{
+ if (not m_device) throw runtime_error("Lime device not set up");
+ return 0;//m_device->getGain(SOAPY_SDR_TX, 0);
+}
+
+SDRDevice::RunStatistics Lime::get_run_statistics(void) const
+{
+ RunStatistics rs;
+ rs.num_underruns = underflows;
+ rs.num_overruns = overflows;
+ rs.num_late_packets = late_packets;
+ rs.num_frames_modulated = num_frames_modulated;
+ return rs;
+}
+
+
+double Lime::get_real_secs(void) const
+{
+ /*if (m_device) {
+ long long time_ns = m_device->getHardwareTime();
+ return time_ns / 1e9;
+ }
+ else {
+ return 0.0;
+ }*/
+ return 0.0;
+}
+
+void Lime::set_rxgain(double rxgain)
+{
+ /*m_device->setGain(SOAPY_SDR_RX, 0, m_conf.rxgain);
+ m_conf.rxgain = m_device->getGain(SOAPY_SDR_RX, 0);
+ */
+}
+
+double Lime::get_rxgain(void) const
+{
+ return 0.0;//m_device->getGain(SOAPY_SDR_RX, 0);
+}
+
+size_t Lime::receive_frame(
+ complexf *buf,
+ size_t num_samples,
+ struct frame_timestamp& ts,
+ double timeout_secs)
+{
+ /*int flags = 0;
+ long long timeNs = ts.get_ns();
+ const size_t numElems = num_samples;
+
+ void *buffs[1];
+ buffs[0] = buf;
+
+ int ret = m_device->activateStream(m_rx_stream, flags, timeNs, numElems);
+ if (ret != 0) {
+ throw std::runtime_error(string("Soapy activate RX stream failed: ") +
+ SoapySDR::errToStr(ret));
+ }
+ m_rx_stream_active = true;
+
+ int n_read = m_device->readStream(
+ m_rx_stream, buffs, num_samples, flags, timeNs);
+
+ ret = m_device->deactivateStream(m_rx_stream);
+ if (ret != 0) {
+ throw std::runtime_error(string("Soapy deactivate RX stream failed: ") +
+ SoapySDR::errToStr(ret));
+ }
+ m_rx_stream_active = false;
+
+ if (n_read < 0) {
+ throw std::runtime_error(string("Soapy failed to read from RX stream : ") +
+ SoapySDR::errToStr(ret));
+ }
+
+ ts.set_ns(timeNs);
+
+ return n_read;
+ */
+ return 0;
+}
+
+
+bool Lime::is_clk_source_ok() const
+{
+ // TODO
+ return true;
+}
+
+const char* Lime::device_name(void) const
+{
+ return "Lime";
+}
+
+double Lime::get_temperature(void) const
+{
+ // TODO Unimplemented
+ // LimeSDR exports 'lms7_temp'
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+void Lime::transmit_frame(const struct FrameData& frame)
+{
+ if (not m_device) throw runtime_error("Lime device not set up");
+/*
+ long long int timeNs = frame.ts.get_ns();
+ // muting and mutenotimestamp is handled by SDR
+ const bool has_time_spec = (m_conf.enableSync and frame.ts.timestamp_valid);
+
+ if (not m_tx_stream_active) {
+ int flags = has_time_spec ? SOAPY_SDR_HAS_TIME : 0;
+ int ret = m_device->activateStream(m_tx_stream, flags, timeNs);
+ if (ret != 0) {
+ throw std::runtime_error(string("Soapy activate TX stream failed: ") +
+ SoapySDR::errToStr(ret));
+ }
+ m_tx_stream_active = true;
+ }
+
+ // The frame buffer contains bytes representing FC32 samples
+ const complexf *buf = reinterpret_cast<const complexf*>(frame.buf.data());
+ const size_t numSamples = frame.buf.size() / sizeof(complexf);
+ if ((frame.buf.size() % sizeof(complexf)) != 0) {
+ throw std::runtime_error("Soapy: invalid buffer size");
+ }
+
+ // Stream MTU is in samples, not bytes.
+ const size_t mtu = m_device->getStreamMTU(m_tx_stream);
+
+ size_t num_acc_samps = 0;
+ while (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);
+
+ const bool eob_because_muting = m_conf.muting;
+ const bool end_of_burst = eob_because_muting or (
+ frame.ts.timestamp_valid and
+ frame.ts.timestamp_refresh and
+ samps_to_send <= mtu );
+
+ int flags = 0;
+
+ auto num_sent = m_device->writeStream(
+ m_tx_stream, buffs, samps_to_send, flags, timeNs);
+
+ if (num_sent == SOAPY_SDR_TIMEOUT) {
+ continue;
+ }
+ else if (num_sent == SOAPY_SDR_OVERFLOW) {
+ overflows++;
+ continue;
+ }
+ else if (num_sent == SOAPY_SDR_UNDERFLOW) {
+ underflows++;
+ continue;
+ }
+
+ if (num_sent < 0) {
+ etiLog.level(error) << "Unexpected stream error " <<
+ SoapySDR::errToStr(num_sent);
+ throw std::runtime_error("Fault in Soapy");
+ }
+
+ timeNs += 1e9 * num_sent/m_conf.sampleRate;
+
+ num_acc_samps += num_sent;
+
+ if (end_of_burst) {
+ int ret_deact = m_device->deactivateStream(m_tx_stream);
+ if (ret_deact != 0) {
+ throw std::runtime_error(
+ string("Soapy activate TX stream failed: ") +
+ SoapySDR::errToStr(ret_deact));
+ }
+ m_tx_stream_active = false;
+ }
+
+ if (eob_because_muting) {
+ break;
+ }
+ }
+ num_frames_modulated++;
+ */
+}
+
+} // namespace Output
+
+#endif // HAVE_LIMESDR
+
+
diff --git a/src/output/Lime.h b/src/output/Lime.h
new file mode 100644
index 0000000..ad594c0
--- /dev/null
+++ b/src/output/Lime.h
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2018
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://opendigitalradio.org
+
+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_LIMESDR
+
+#include <string>
+#include <memory>
+
+#include "output/SDR.h"
+#include "ModPlugin.h"
+#include "EtiReader.h"
+#include "RemoteControl.h"
+#include <lime/LimeSuite.h>
+
+namespace Output {
+
+class Lime : public Output::SDRDevice
+{
+ public:
+ Lime(SDRDeviceConfig& config);
+ Lime(const Lime& other) = delete;
+ Lime& operator=(const Lime& other) = delete;
+ ~Lime();
+
+ virtual void tune(double lo_offset, double frequency) override;
+ virtual double get_tx_freq(void) const override;
+ virtual void set_txgain(double txgain) override;
+ virtual double get_txgain(void) const override;
+ virtual void transmit_frame(const struct FrameData& frame) override;
+ virtual RunStatistics get_run_statistics(void) const override;
+ virtual double get_real_secs(void) const override;
+
+ virtual void set_rxgain(double rxgain) override;
+ virtual double get_rxgain(void) const override;
+ virtual size_t receive_frame(
+ complexf *buf,
+ size_t num_samples,
+ struct frame_timestamp& ts,
+ double timeout_secs) override;
+
+ // Return true if GPS and reference clock inputs are ok
+ virtual bool is_clk_source_ok(void) const override;
+ virtual const char* device_name(void) const override;
+
+ virtual double get_temperature(void) const override;
+
+ private:
+ SDRDeviceConfig& m_conf;
+ lms_device_t *m_device=nullptr;
+ /*
+ SoapySDR::Device *m_device = nullptr;
+ SoapySDR::Stream *m_tx_stream = nullptr;
+ bool m_tx_stream_active = false;
+ SoapySDR::Stream *m_rx_stream = nullptr;
+ */
+ bool m_rx_stream_active = false;
+
+ size_t underflows = 0;
+ size_t overflows = 0;
+ size_t late_packets = 0;
+ size_t num_frames_modulated = 0;
+};
+
+} // namespace Output
+
+#endif //HAVE_SOAPYSDR
+