From 1c7ea3dfb36d2ae3e3d9ef5c79ff36044bf365e4 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 7 Jun 2015 03:30:28 +0200 Subject: [PATCH] Added a plugin for BladeRF. Removed GNUradio from the build (gr-osmocom source) as this does not work properly --- .gitignore | 1 + cmake/Modules/FindLibBLADERF.cmake | 28 + include/plugin/plugingui.h | 1 + plugins/channel/am/amdemodgui.cpp | 5 + plugins/channel/am/amdemodgui.h | 1 + plugins/channel/lora/lorademodgui.cpp | 5 + plugins/channel/lora/lorademodgui.h | 1 + plugins/channel/nfm/nfmdemodgui.cpp | 5 + plugins/channel/nfm/nfmdemodgui.h | 1 + plugins/channel/ssb/ssbdemodgui.cpp | 5 + plugins/channel/ssb/ssbdemodgui.h | 1 + plugins/channel/tcpsrc/tcpsrcgui.cpp | 5 + plugins/channel/tcpsrc/tcpsrcgui.h | 1 + plugins/channel/wfm/wfmdemodgui.cpp | 5 + plugins/channel/wfm/wfmdemodgui.h | 1 + plugins/samplesource/CMakeLists.txt | 13 +- plugins/samplesource/bladerf/CMakeLists.txt | 50 ++ plugins/samplesource/bladerf/bladerfgui.cpp | 356 +++++++++++ plugins/samplesource/bladerf/bladerfgui.h | 79 +++ plugins/samplesource/bladerf/bladerfgui.ui | 556 ++++++++++++++++++ plugins/samplesource/bladerf/bladerfinput.cpp | 392 ++++++++++++ plugins/samplesource/bladerf/bladerfinput.h | 110 ++++ .../samplesource/bladerf/bladerfplugin.cpp | 60 ++ plugins/samplesource/bladerf/bladerfplugin.h | 27 + .../samplesource/bladerf/bladerfthread.cpp | 213 +++++++ plugins/samplesource/bladerf/bladerfthread.h | 68 +++ plugins/samplesource/fcd/fcdgui.cpp | 5 + plugins/samplesource/fcd/fcdgui.h | 1 + plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 5 + plugins/samplesource/rtlsdr/rtlsdrgui.h | 1 + sdrbase/dsp/samplesource/samplesource.cpp | 5 + sdrbase/mainwindow.cpp | 42 +- sdrbase/plugin/pluginmanager.cpp | 20 +- sdrbase/settings/preset.cpp | 23 +- 34 files changed, 2066 insertions(+), 26 deletions(-) create mode 100644 cmake/Modules/FindLibBLADERF.cmake create mode 100644 plugins/samplesource/bladerf/CMakeLists.txt create mode 100644 plugins/samplesource/bladerf/bladerfgui.cpp create mode 100644 plugins/samplesource/bladerf/bladerfgui.h create mode 100644 plugins/samplesource/bladerf/bladerfgui.ui create mode 100644 plugins/samplesource/bladerf/bladerfinput.cpp create mode 100644 plugins/samplesource/bladerf/bladerfinput.h create mode 100644 plugins/samplesource/bladerf/bladerfplugin.cpp create mode 100644 plugins/samplesource/bladerf/bladerfplugin.h create mode 100644 plugins/samplesource/bladerf/bladerfthread.cpp create mode 100644 plugins/samplesource/bladerf/bladerfthread.h diff --git a/.gitignore b/.gitignore index 588976d8e..f13285b5f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build/* LOCAL/* .cproject .project +.settings/ diff --git a/cmake/Modules/FindLibBLADERF.cmake b/cmake/Modules/FindLibBLADERF.cmake new file mode 100644 index 000000000..8741c9d46 --- /dev/null +++ b/cmake/Modules/FindLibBLADERF.cmake @@ -0,0 +1,28 @@ +if(NOT LIBBLADERF_FOUND) + + pkg_check_modules (LIBBLADERF_PKG libbladeRF) + find_path(LIBBLADERF_INCLUDE_DIR NAMES libbladeRF.h + PATHS + ${LIBBLADERF_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include + ) + + find_library(LIBBLADERF_LIBRARIES NAMES bladeRF + PATHS + ${LIBBLADERF_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + + if(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES) + set(LIBBLADERF_FOUND TRUE CACHE INTERNAL "libbladerf found") + message(STATUS "Found libbladerf: ${LIBBLADERF_INCLUDE_DIR}, ${LIBBLADERF_LIBRARIES}") + else(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES) + set(LIBBLADERF_FOUND FALSE CACHE INTERNAL "libbladerf found") + message(STATUS "libbladerf not found.") + endif(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES) + + mark_as_advanced(LIBBLADERF_INCLUDE_DIR LIBBLADERF_LIBRARIES) + +endif(NOT LIBBLADERDF_FOUND) diff --git a/include/plugin/plugingui.h b/include/plugin/plugingui.h index 586e44678..d0e2e3543 100644 --- a/include/plugin/plugingui.h +++ b/include/plugin/plugingui.h @@ -12,6 +12,7 @@ public: virtual void destroy() = 0; virtual void setName(const QString& name) = 0; + virtual QString getName() const = 0; virtual void resetToDefaults() = 0; diff --git a/plugins/channel/am/amdemodgui.cpp b/plugins/channel/am/amdemodgui.cpp index 6c4460bda..d6d73442b 100644 --- a/plugins/channel/am/amdemodgui.cpp +++ b/plugins/channel/am/amdemodgui.cpp @@ -34,6 +34,11 @@ void AMDemodGUI::setName(const QString& name) setObjectName(name); } +QString AMDemodGUI::getName() const +{ + return objectName(); +} + void AMDemodGUI::resetToDefaults() { ui->rfBW->setValue(4); diff --git a/plugins/channel/am/amdemodgui.h b/plugins/channel/am/amdemodgui.h index 0ee51e09b..95d13944f 100644 --- a/plugins/channel/am/amdemodgui.h +++ b/plugins/channel/am/amdemodgui.h @@ -25,6 +25,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/channel/lora/lorademodgui.cpp b/plugins/channel/lora/lorademodgui.cpp index 8de48e910..2d74f1d97 100644 --- a/plugins/channel/lora/lorademodgui.cpp +++ b/plugins/channel/lora/lorademodgui.cpp @@ -29,6 +29,11 @@ void LoRaDemodGUI::setName(const QString& name) setObjectName(name); } +QString LoRaDemodGUI::getName() const +{ + return objectName(); +} + void LoRaDemodGUI::resetToDefaults() { ui->BW->setValue(0); diff --git a/plugins/channel/lora/lorademodgui.h b/plugins/channel/lora/lorademodgui.h index 64d6a116a..a0ec12a7b 100644 --- a/plugins/channel/lora/lorademodgui.h +++ b/plugins/channel/lora/lorademodgui.h @@ -25,6 +25,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/channel/nfm/nfmdemodgui.cpp b/plugins/channel/nfm/nfmdemodgui.cpp index fe97a7d6e..3533a6c90 100644 --- a/plugins/channel/nfm/nfmdemodgui.cpp +++ b/plugins/channel/nfm/nfmdemodgui.cpp @@ -33,6 +33,11 @@ void NFMDemodGUI::setName(const QString& name) setObjectName(name); } +QString NFMDemodGUI::getName() const +{ + return objectName(); +} + void NFMDemodGUI::resetToDefaults() { ui->rfBW->setValue(4); diff --git a/plugins/channel/nfm/nfmdemodgui.h b/plugins/channel/nfm/nfmdemodgui.h index ae927add1..67319efb7 100644 --- a/plugins/channel/nfm/nfmdemodgui.h +++ b/plugins/channel/nfm/nfmdemodgui.h @@ -25,6 +25,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/channel/ssb/ssbdemodgui.cpp b/plugins/channel/ssb/ssbdemodgui.cpp index ba72d47b3..6dae073d0 100644 --- a/plugins/channel/ssb/ssbdemodgui.cpp +++ b/plugins/channel/ssb/ssbdemodgui.cpp @@ -31,6 +31,11 @@ void SSBDemodGUI::setName(const QString& name) setObjectName(name); } +QString SSBDemodGUI::getName() const +{ + return objectName(); +} + void SSBDemodGUI::resetToDefaults() { ui->BW->setValue(30); diff --git a/plugins/channel/ssb/ssbdemodgui.h b/plugins/channel/ssb/ssbdemodgui.h index 042e2582b..3cd40f6bc 100644 --- a/plugins/channel/ssb/ssbdemodgui.h +++ b/plugins/channel/ssb/ssbdemodgui.h @@ -25,6 +25,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/channel/tcpsrc/tcpsrcgui.cpp b/plugins/channel/tcpsrc/tcpsrcgui.cpp index 5d8c91ebe..e92315e3d 100644 --- a/plugins/channel/tcpsrc/tcpsrcgui.cpp +++ b/plugins/channel/tcpsrc/tcpsrcgui.cpp @@ -24,6 +24,11 @@ void TCPSrcGUI::setName(const QString& name) setObjectName(name); } +QString TCPSrcGUI::getName() const +{ + return objectName(); +} + void TCPSrcGUI::resetToDefaults() { ui->sampleFormat->setCurrentIndex(0); diff --git a/plugins/channel/tcpsrc/tcpsrcgui.h b/plugins/channel/tcpsrc/tcpsrcgui.h index 9d539fd2e..881658c25 100644 --- a/plugins/channel/tcpsrc/tcpsrcgui.h +++ b/plugins/channel/tcpsrc/tcpsrcgui.h @@ -25,6 +25,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/channel/wfm/wfmdemodgui.cpp b/plugins/channel/wfm/wfmdemodgui.cpp index a0cec29df..0511f00ca 100644 --- a/plugins/channel/wfm/wfmdemodgui.cpp +++ b/plugins/channel/wfm/wfmdemodgui.cpp @@ -43,6 +43,11 @@ void WFMDemodGUI::setName(const QString& name) setObjectName(name); } +QString WFMDemodGUI::getName() const +{ + return objectName(); +} + void WFMDemodGUI::resetToDefaults() { ui->rfBW->setValue(4); diff --git a/plugins/channel/wfm/wfmdemodgui.h b/plugins/channel/wfm/wfmdemodgui.h index ec0a96a88..c3f8123e7 100644 --- a/plugins/channel/wfm/wfmdemodgui.h +++ b/plugins/channel/wfm/wfmdemodgui.h @@ -25,6 +25,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index e48d7b8b6..6c9de667d 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -1,14 +1,6 @@ project(samplesource) find_package(LibUSB) -#find_package(LibOsmoSDR) - -add_subdirectory(gnuradio) -#add_subdirectory(remote) - -#if(LIBUSB_FOUND AND LIBOSMOSDR_FOUND) -# add_subdirectory(osmosdr) -#endif(LIBUSB_FOUND AND LIBOSMOSDR_FOUND) if(V4L-RTL) FIND_LIBRARY (LIBV4L2 v4l2) @@ -34,3 +26,8 @@ if(LIBUSB_FOUND AND LIBRTLSDR_FOUND) add_subdirectory(rtlsdr) endif(LIBUSB_FOUND AND LIBRTLSDR_FOUND) +find_package(LibBLADERF) +if(LIBUSB_FOUND AND LIBBLADERF_FOUND) + add_subdirectory(bladerf) +endif(LIBUSB_FOUND AND LIBBLADERF_FOUND) + diff --git a/plugins/samplesource/bladerf/CMakeLists.txt b/plugins/samplesource/bladerf/CMakeLists.txt new file mode 100644 index 000000000..3c7a3bc0f --- /dev/null +++ b/plugins/samplesource/bladerf/CMakeLists.txt @@ -0,0 +1,50 @@ +project(bladerf) + +set(bladerf_SOURCES + bladerfgui.cpp + bladerfinput.cpp + bladerfplugin.cpp + bladerfthread.cpp +) + +set(bladerf_HEADERS + bladerfgui.h + bladerfinput.h + bladerfplugin.h + bladerfthread.h +) + +set(bladerf_FORMS + bladerfgui.ui +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include-gpl + ${LIBRTLSDR_INCLUDE_DIR} +) + +#include(${QT_USE_FILE}) +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +#qt4_wrap_cpp(bladerf_HEADERS_MOC ${bladerf_HEADERS}) +qt5_wrap_ui(bladerf_FORMS_HEADERS ${bladerf_FORMS}) + +add_library(inputbladerf SHARED + ${bladerf_SOURCES} + ${bladerf_HEADERS_MOC} + ${bladerf_FORMS_HEADERS} +) + +target_link_libraries(inputbladerf + ${QT_LIBRARIES} + ${LIBBLADERF_LIBRARIES} + ${LIBUSB_LIBRARIES} + sdrbase +) + +qt5_use_modules(inputbladerf Core Widgets OpenGL Multimedia) diff --git a/plugins/samplesource/bladerf/bladerfgui.cpp b/plugins/samplesource/bladerf/bladerfgui.cpp new file mode 100644 index 000000000..90b451a9a --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfgui.cpp @@ -0,0 +1,356 @@ +#include +#include + +#include "ui_bladerfgui.h" +#include "plugin/pluginapi.h" +#include "bladerfgui.h" + +BladerfGui::BladerfGui(PluginAPI* pluginAPI, QWidget* parent) : + QWidget(parent), + ui(new Ui::BladerfGui), + m_pluginAPI(pluginAPI), + m_settings(), + m_sampleSource(NULL) +{ + ui->setupUi(this); + ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN/1000, BLADERF_FREQUENCY_MAX/1000); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + displaySettings(); + + m_sampleSource = new BladerfInput(m_pluginAPI->getMainWindowMessageQueue()); + m_pluginAPI->setSampleSource(m_sampleSource); +} + +BladerfGui::~BladerfGui() +{ + delete ui; +} + +void BladerfGui::destroy() +{ + delete this; +} + +void BladerfGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString BladerfGui::getName() const +{ + return objectName(); +} + +void BladerfGui::resetToDefaults() +{ + m_generalSettings.resetToDefaults(); + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +QByteArray BladerfGui::serializeGeneral() const +{ + return m_generalSettings.serialize(); +} + +bool BladerfGui::deserializeGeneral(const QByteArray&data) +{ + if(m_generalSettings.deserialize(data)) { + displaySettings(); + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +quint64 BladerfGui::getCenterFrequency() const +{ + return m_generalSettings.m_centerFrequency; +} + +QByteArray BladerfGui::serialize() const +{ + return m_settings.serialize(); +} + +bool BladerfGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool BladerfGui::handleMessage(Message* message) +{ + if(BladerfInput::MsgReportBladerf::match(message)) { + displaySettings(); + message->completed(); + return true; + } else { + return false; + } +} + +void BladerfGui::displaySettings() +{ + ui->centerFrequency->setValue(m_generalSettings.m_centerFrequency / 1000); + + ui->samplerateText->setText(tr("%1k").arg(m_settings.m_samplerate / 1000)); + unsigned int sampleRateIndex = BladerfSampleRates::getRateIndex(m_settings.m_samplerate); + ui->samplerate->setValue(sampleRateIndex); + + ui->bandwidthText->setText(tr("%1k").arg(m_settings.m_bandwidth / 1000)); + unsigned int bandwidthIndex = BladerfBandwidths::getBandwidthIndex(m_settings.m_bandwidth); + ui->bandwidth->setValue(bandwidthIndex); + + ui->decimText->setText(tr("%1").arg(1<decim->setValue(m_settings.m_log2Decim); + + ui->lnaGainText->setText(tr("%1").arg(m_settings.m_lnaGain)); + ui->lna->setValue(m_settings.m_lnaGain); + + ui->vga1Text->setText(tr("%1").arg(m_settings.m_vga1)); + ui->vga1->setValue(m_settings.m_vga1); + + ui->vga2Text->setText(tr("%1").arg(m_settings.m_vga2)); + ui->vga2->setValue(m_settings.m_vga2); + + ui->xb200->setCurrentIndex(getXb200Index(m_settings.m_xb200, m_settings.m_xb200Path, m_settings.m_xb200Filter)); +} + +void BladerfGui::sendSettings() +{ + if(!m_updateTimer.isActive()) + m_updateTimer.start(100); +} + +void BladerfGui::on_centerFrequency_changed(quint64 value) +{ + m_generalSettings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void BladerfGui::on_samplerate_valueChanged(int value) +{ + int newrate = BladerfSampleRates::getRate(value); + ui->samplerateText->setText(tr("%1k").arg(newrate)); + m_settings.m_samplerate = newrate * 1000; + sendSettings(); +} + +void BladerfGui::on_bandwidth_valueChanged(int value) +{ + int newbw = BladerfBandwidths::getBandwidth(value); + ui->bandwidthText->setText(tr("%1k").arg(newbw)); + m_settings.m_bandwidth = newbw * 1000; + sendSettings(); +} + +void BladerfGui::on_decim_valueChanged(int value) +{ + if ((value <0) || (value > 4)) + return; + ui->decimText->setText(tr("%1").arg(1< BLADERF_RXVGA1_GAIN_MAX)) + return; + + ui->vga1Text->setText(tr("%1").arg(value)); + m_settings.m_vga1 = value; + sendSettings(); +} + +void BladerfGui::on_vga2_valueChanged(int value) +{ + if ((value < BLADERF_RXVGA2_GAIN_MIN) || (value > BLADERF_RXVGA2_GAIN_MAX)) + return; + + ui->vga2Text->setText(tr("%1").arg(value)); + m_settings.m_vga2 = value; + sendSettings(); +} + +void BladerfGui::on_xb200_currentIndexChanged(int index) +{ + if (index == 1) // bypass + { + m_settings.m_xb200 = true; + m_settings.m_xb200Path = BLADERF_XB200_BYPASS; + } + else if (index == 2) // Auto 1dB + { + m_settings.m_xb200 = true; + m_settings.m_xb200Path = BLADERF_XB200_MIX; + m_settings.m_xb200Filter = BLADERF_XB200_AUTO_1DB; + } + else if (index == 3) // Auto 3dB + { + m_settings.m_xb200 = true; + m_settings.m_xb200Path = BLADERF_XB200_MIX; + m_settings.m_xb200Filter = BLADERF_XB200_AUTO_3DB; + } + else if (index == 4) // Custom + { + m_settings.m_xb200 = true; + m_settings.m_xb200Path = BLADERF_XB200_MIX; + m_settings.m_xb200Filter = BLADERF_XB200_CUSTOM; + } + else if (index == 5) // 50 MHz + { + m_settings.m_xb200 = true; + m_settings.m_xb200Path = BLADERF_XB200_MIX; + m_settings.m_xb200Filter = BLADERF_XB200_50M; + } + else if (index == 6) // 144 MHz + { + m_settings.m_xb200 = true; + m_settings.m_xb200Path = BLADERF_XB200_MIX; + m_settings.m_xb200Filter = BLADERF_XB200_144M; + } + else if (index == 7) // 222 MHz + { + m_settings.m_xb200 = true; + m_settings.m_xb200Path = BLADERF_XB200_MIX; + m_settings.m_xb200Filter = BLADERF_XB200_222M; + } + else // no xb200 + { + m_settings.m_xb200 = false; + } + + sendSettings(); +} + +void BladerfGui::updateHardware() +{ + BladerfInput::MsgConfigureBladerf* message = BladerfInput::MsgConfigureBladerf::create(m_generalSettings, m_settings); + message->submit(m_pluginAPI->getDSPEngineMessageQueue()); + m_updateTimer.stop(); +} + +unsigned int BladerfGui::getXb200Index(bool xb_200, bladerf_xb200_path xb200Path, bladerf_xb200_filter xb200Filter) +{ + if (xb_200) + { + if (xb200Path == BLADERF_XB200_BYPASS) + { + return 1; + } + else + { + if (xb200Filter == BLADERF_XB200_AUTO_1DB) + { + return 2; + } + else if (xb200Filter == BLADERF_XB200_AUTO_3DB) + { + return 3; + } + else if (xb200Filter == BLADERF_XB200_CUSTOM) + { + return 4; + } + else if (xb200Filter == BLADERF_XB200_50M) + { + return 5; + } + else if (xb200Filter == BLADERF_XB200_144M) + { + return 6; + } + else if (xb200Filter == BLADERF_XB200_222M) + { + return 7; + } + else + { + return 0; + } + } + } + else + { + return 0; + } +} + +unsigned int BladerfSampleRates::m_rates[] = {384, 768, 1536, 2304, 3072, 6144, 12288, 24576, 30720, 39936}; +unsigned int BladerfSampleRates::m_nb_rates = 10; + +unsigned int BladerfSampleRates::getRate(unsigned int rate_index) +{ + if (rate_index < m_nb_rates) + { + return m_rates[rate_index]; + } + else + { + return m_rates[0]; + } +} + +unsigned int BladerfSampleRates::getRateIndex(unsigned int rate) +{ + for (unsigned int i=0; i < m_nb_rates; i++) + { + if (rate/1000 == m_rates[i]) + { + return i; + } + } + + return 0; +} + +unsigned int BladerfBandwidths::m_halfbw[] = {750, 875, 1250, 1375, 1500, 1920, 2500, 2750, 3000, 3500, 4375, 5000, 6000, 7000, 10000, 14000}; +unsigned int BladerfBandwidths::m_nb_halfbw = 16; + +unsigned int BladerfBandwidths::getBandwidth(unsigned int bandwidth_index) +{ + if (bandwidth_index < m_nb_halfbw) + { + return m_halfbw[bandwidth_index] * 2; + } + else + { + return m_halfbw[0] * 2; + } +} + +unsigned int BladerfBandwidths::getBandwidthIndex(unsigned int bandwidth) +{ + for (unsigned int i=0; i < m_nb_halfbw; i++) + { + if (bandwidth/2 == m_halfbw[i]) + { + return i; + } + } + + return 0; +} diff --git a/plugins/samplesource/bladerf/bladerfgui.h b/plugins/samplesource/bladerf/bladerfgui.h new file mode 100644 index 000000000..26059a864 --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfgui.h @@ -0,0 +1,79 @@ +#ifndef INCLUDE_BLADERFGUI_H +#define INCLUDE_BLADERFGUI_H + +#include +#include "plugin/plugingui.h" + +#include "bladerfinput.h" + +class PluginAPI; + +namespace Ui { + class BladerfGui; + class BladerfSampleRates; +} + +class BladerfGui : public QWidget, public PluginGUI { + Q_OBJECT + +public: + explicit BladerfGui(PluginAPI* pluginAPI, QWidget* parent = NULL); + ~BladerfGui(); + void destroy(); + + void setName(const QString& name); + QString getName() const; + + void resetToDefaults(); + QByteArray serializeGeneral() const; + bool deserializeGeneral(const QByteArray&data); + quint64 getCenterFrequency() const; + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + bool handleMessage(Message* message); + +private: + Ui::BladerfGui* ui; + + PluginAPI* m_pluginAPI; + SampleSource::GeneralSettings m_generalSettings; + BladerfInput::Settings m_settings; + QTimer m_updateTimer; + std::vector m_gains; + SampleSource* m_sampleSource; + + void displaySettings(); + void sendSettings(); + unsigned int getXb200Index(bool xb_200, bladerf_xb200_path xb200Path, bladerf_xb200_filter xb200Filter); + +private slots: + void on_centerFrequency_changed(quint64 value); + void on_samplerate_valueChanged(int value); + void on_bandwidth_valueChanged(int value); + void on_decim_valueChanged(int value); + void on_lna_valueChanged(int value); + void on_vga1_valueChanged(int value); + void on_vga2_valueChanged(int value); + void on_xb200_currentIndexChanged(int index); + void updateHardware(); +}; + +class BladerfSampleRates { +public: + static unsigned int getRate(unsigned int rate_index); + static unsigned int getRateIndex(unsigned int rate); +private: + static unsigned int m_rates[10]; + static unsigned int m_nb_rates; +}; + +class BladerfBandwidths { +public: + static unsigned int getBandwidth(unsigned int bandwidth_index); + static unsigned int getBandwidthIndex(unsigned int bandwidth); +private: + static unsigned int m_halfbw[16]; + static unsigned int m_nb_halfbw; +}; + +#endif // INCLUDE_BLADERFGUI_H diff --git a/plugins/samplesource/bladerf/bladerfgui.ui b/plugins/samplesource/bladerf/bladerfgui.ui new file mode 100644 index 000000000..6f6af9e61 --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfgui.ui @@ -0,0 +1,556 @@ + + + BladerfGui + + + + 0 + 0 + 198 + 255 + + + + + 0 + 0 + + + + BladeRF + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Monospace + 20 + + + + SizeVerCursor + + + Qt::StrongFocus + + + Tuner center frequency in kHz + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + None + + + 0 + + + 5 + + + + None + + + + + Bypass + + + + + Auto 1dB + + + + + Auto 3dB + + + + + Custom + + + + + 50M + + + + + 144M + + + + + 222M + + + + + + + + xb200 + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + Device Samplerate + + + 9 + + + 1 + + + 3 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Rate + + + + + + + + 40 + 0 + + + + --- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + Device Samplerate + + + 15 + + + 1 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + BW + + + + + + + + 40 + 0 + + + + --- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + Dec. + + + + + + + 4 + + + 1 + + + 0 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 1 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + + 0 + 0 + + + + LNA + + + + + + + false + + + LNA amplification + + + 2 + + + 1 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + Amplifier before filtering + + + 5 + + + 30 + + + 1 + + + 20 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 20 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + VGA1 + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + VGA2 + + + + + + + Amplifier before ADC + + + 30 + + + 1 + + + 9 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 9 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+
+ + +
diff --git a/plugins/samplesource/bladerf/bladerfinput.cpp b/plugins/samplesource/bladerf/bladerfinput.cpp new file mode 100644 index 000000000..5fd243a70 --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfinput.cpp @@ -0,0 +1,392 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "util/simpleserializer.h" +#include "bladerfgui.h" +#include "bladerfinput.h" +#include "bladerfthread.h" + +MESSAGE_CLASS_DEFINITION(BladerfInput::MsgConfigureBladerf, Message) +MESSAGE_CLASS_DEFINITION(BladerfInput::MsgReportBladerf, Message) + +BladerfInput::Settings::Settings() : + m_lnaGain(0), + m_vga1(20), + m_vga2(9), + m_samplerate(2400000), + m_bandwidth(1500000), + m_log2Decim(0), + m_xb200(false), + m_xb200Path(BLADERF_XB200_MIX), + m_xb200Filter(BLADERF_XB200_AUTO_1DB) +{ +} + +void BladerfInput::Settings::resetToDefaults() +{ + m_lnaGain = 0; + m_vga1 = 20; + m_vga2 = 9; + m_samplerate = 2400000; + m_log2Decim = 0; + m_xb200 = false; + m_xb200Path = BLADERF_XB200_MIX; + m_xb200Filter = BLADERF_XB200_AUTO_1DB; +} + +QByteArray BladerfInput::Settings::serialize() const +{ + SimpleSerializer s(1); + s.writeS32(1, m_lnaGain); + s.writeS32(2, m_vga1); + s.writeS32(3, m_vga2); + s.writeS32(4, m_samplerate); + s.writeU32(5, m_log2Decim); + s.writeBool(6, m_xb200); + s.writeS32(7, (int) m_xb200Path); + s.writeS32(8, (int) m_xb200Filter); + return s.final(); +} + +bool BladerfInput::Settings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) { + int intval; + d.readS32(1, &m_lnaGain, 0); + d.readS32(2, &m_vga1, 20); + d.readS32(3, &m_vga2, 9); + d.readS32(4, &m_samplerate, 0); + d.readU32(5, &m_log2Decim, 4); + d.readBool(6, &m_xb200); + d.readS32(7, &intval); + m_xb200Path = (bladerf_xb200_path) intval; + d.readS32(8, &intval); + m_xb200Filter = (bladerf_xb200_filter) intval; + return true; + } else { + resetToDefaults(); + return false; + } +} + +BladerfInput::BladerfInput(MessageQueue* msgQueueToGUI) : + SampleSource(msgQueueToGUI), + m_settings(), + m_dev(NULL), + m_bladerfThread(NULL), + m_deviceDescription() +{ +} + +BladerfInput::~BladerfInput() +{ + stopInput(); +} + +bool BladerfInput::startInput(int device) +{ + QMutexLocker mutexLocker(&m_mutex); + + if(m_dev != NULL) + stopInput(); + + int res; + int fpga_loaded; + + if(!m_sampleFifo.setSize(96000 * 4)) { + qCritical("Could not allocate SampleFifo"); + return false; + } + + if ((m_dev = open_bladerf_from_serial(0)) == NULL) // TODO: fix; Open first available device as there is no proper handling for multiple devices + { + qCritical("could not open BladeRF"); + return false; + } + + fpga_loaded = bladerf_is_fpga_configured(m_dev); + + if (fpga_loaded < 0) { + qCritical("Failed to check FPGA state: %s", + bladerf_strerror(fpga_loaded)); + return false; + } else if (fpga_loaded == 0) { + qCritical("The device's FPGA is not loaded."); + return false; + } + + // TODO: adjust USB transfer data according to sample rate + if ((res = bladerf_sync_config(m_dev, BLADERF_MODULE_RX, BLADERF_FORMAT_SC16_Q11, 64, 8192, 32, 10000)) < 0) + { + qCritical("bladerf_sync_config with return code %d", res); + goto failed; + } + + if ((res = bladerf_enable_module(m_dev, BLADERF_MODULE_RX, true)) < 0) + { + qCritical("bladerf_enable_module with return code %d", res); + goto failed; + } + + if((m_bladerfThread = new BladerfThread(m_dev, &m_sampleFifo)) == NULL) { + qFatal("out of memory"); + goto failed; + } + + m_bladerfThread->startWork(); + + mutexLocker.unlock(); + applySettings(m_generalSettings, m_settings, true); + + qDebug("bladerfInput: start"); + //MsgReportBladerf::create(m_gains)->submit(m_guiMessageQueue); Pass anything here + + return true; + +failed: + stopInput(); + return false; +} + +void BladerfInput::stopInput() +{ + QMutexLocker mutexLocker(&m_mutex); + + if(m_bladerfThread != NULL) { + m_bladerfThread->stopWork(); + delete m_bladerfThread; + m_bladerfThread = NULL; + } + if(m_dev != NULL) { + bladerf_close(m_dev); + m_dev = NULL; + } + m_deviceDescription.clear(); +} + +const QString& BladerfInput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int BladerfInput::getSampleRate() const +{ + int rate = m_settings.m_samplerate; + return (rate / (1<getGeneralSettings(), conf->getSettings(), false)) + qDebug("BladeRF config error"); + message->completed(); + return true; + } else { + return false; + } +} + +bool BladerfInput::applySettings(const GeneralSettings& generalSettings, const Settings& settings, bool force) +{ + QMutexLocker mutexLocker(&m_mutex); + + if((m_settings.m_lnaGain != settings.m_lnaGain) || force) { + m_settings.m_lnaGain = settings.m_lnaGain; + if(m_dev != NULL) { + if(bladerf_set_lna_gain(m_dev, getLnaGain(m_settings.m_lnaGain)) != 0) { + qDebug("bladerf_set_lna_gain() failed"); + } else { + std::cerr << "BladerfInput: LNA gain set to " << getLnaGain(m_settings.m_lnaGain) << std::endl; + } + } + } + + if((m_settings.m_vga1 != settings.m_vga1) || force) { + m_settings.m_vga1 = settings.m_vga1; + if(m_dev != NULL) { + if(bladerf_set_rxvga1(m_dev, m_settings.m_vga1) != 0) { + qDebug("bladerf_set_rxvga1() failed"); + } else { + std::cerr << "BladerfInput: VGA1 gain set to " << m_settings.m_vga1 << std::endl; + } + } + } + + if((m_settings.m_vga2 != settings.m_vga2) || force) { + m_settings.m_vga2 = settings.m_vga2; + if(m_dev != NULL) { + if(bladerf_set_rxvga2(m_dev, m_settings.m_vga2) != 0) { + qDebug("bladerf_set_rxvga2() failed"); + } else { + std::cerr << "BladerfInput: VGA2 gain set to " << m_settings.m_vga2 << std::endl; + } + } + } + + if((m_settings.m_xb200 != settings.m_xb200) || force) { + m_settings.m_xb200 = settings.m_xb200; + if(m_dev != NULL) { + if (m_settings.m_xb200) { + if (bladerf_expansion_attach(m_dev, BLADERF_XB_200) != 0) { + qDebug("bladerf_expansion_attach(xb200) failed"); + } else { + std::cerr << "BladerfInput: Attach XB200" << std::endl; + } + } else { + if (bladerf_expansion_attach(m_dev, BLADERF_XB_NONE) != 0) { + qDebug("bladerf_expansion_attach(none) failed"); + } else { + std::cerr << "BladerfInput: Detach XB200" << std::endl; + } + } + } + } + + if((m_settings.m_xb200Path != settings.m_xb200Path) || force) { + m_settings.m_xb200Path = settings.m_xb200Path; + if(m_dev != NULL) { + if(bladerf_xb200_set_path(m_dev, BLADERF_MODULE_RX, m_settings.m_xb200Path) != 0) { + qDebug("bladerf_xb200_set_path(BLADERF_MODULE_RX) failed"); + } else { + std::cerr << "BladerfInput: set xb200 path to " << m_settings.m_xb200Path << std::endl; + } + } + } + + if((m_settings.m_xb200Filter != settings.m_xb200Filter) || force) { + m_settings.m_xb200Filter = settings.m_xb200Filter; + if(m_dev != NULL) { + if(bladerf_xb200_set_filterbank(m_dev, BLADERF_MODULE_RX, m_settings.m_xb200Filter) != 0) { + qDebug("bladerf_xb200_set_filterbank(BLADERF_MODULE_RX) failed"); + } else { + std::cerr << "BladerfInput: set xb200 filter to " << m_settings.m_xb200Filter << std::endl; + } + } + } + + if((m_settings.m_samplerate != settings.m_samplerate) || force) { + if(m_dev != NULL) { + unsigned int actualSamplerate; + if( bladerf_set_sample_rate(m_dev, BLADERF_MODULE_RX, settings.m_samplerate, &actualSamplerate) < 0) + qCritical("could not set sample rate: %d", settings.m_samplerate); + else { + std::cerr << "bladerf_set_sample_rate(BLADERF_MODULE_RX) actual sample rate is " << actualSamplerate << std::endl; + m_settings.m_samplerate = settings.m_samplerate; + m_bladerfThread->setSamplerate(settings.m_samplerate); + } + } + } + + if((m_settings.m_bandwidth != settings.m_bandwidth) || force) { + if(m_dev != NULL) { + unsigned int actualBandwidth; + if( bladerf_set_bandwidth(m_dev, BLADERF_MODULE_RX, settings.m_bandwidth, &actualBandwidth) < 0) + qCritical("could not set sample rate: %d", settings.m_samplerate); + else { + std::cerr << "bladerf_set_bandwidth(BLADERF_MODULE_RX) actual bandwidth is " << actualBandwidth << std::endl; + m_settings.m_bandwidth = settings.m_bandwidth; + } + } + } + + if((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + if(m_dev != NULL) { + m_settings.m_log2Decim = settings.m_log2Decim; + m_bladerfThread->setLog2Decimation(settings.m_log2Decim); + } + } + + m_generalSettings.m_centerFrequency = generalSettings.m_centerFrequency; + if(m_dev != NULL) { + qint64 centerFrequency = m_generalSettings.m_centerFrequency + (m_settings.m_samplerate / 4); + + if (m_settings.m_log2Decim == 0) { // Little wooby-doop if no decimation + centerFrequency = m_generalSettings.m_centerFrequency; + } else { + centerFrequency = m_generalSettings.m_centerFrequency + (m_settings.m_samplerate / 4); + } + + if(bladerf_set_frequency( m_dev, BLADERF_MODULE_RX, centerFrequency ) != 0) { + qDebug("bladerf_set_frequency(%lld) failed", m_generalSettings.m_centerFrequency); + } + } + + return true; +} + +bladerf_lna_gain BladerfInput::getLnaGain(int lnaGain) +{ + if (lnaGain == 2) { + return BLADERF_LNA_GAIN_MAX; + } else if (lnaGain == 1) { + return BLADERF_LNA_GAIN_MID; + } else { + return BLADERF_LNA_GAIN_BYPASS; + } +} + +struct bladerf *BladerfInput::open_bladerf_from_serial(const char *serial) +{ + int status; + struct bladerf *dev; + struct bladerf_devinfo info; + + /* Initialize all fields to "don't care" wildcard values. + * + * Immediately passing this to bladerf_open_with_devinfo() would cause + * libbladeRF to open any device on any available backend. */ + bladerf_init_devinfo(&info); + + /* Specify the desired device's serial number, while leaving all other + * fields in the info structure wildcard values */ + if (serial != NULL) { + strncpy(info.serial, serial, BLADERF_SERIAL_LENGTH - 1); + info.serial[BLADERF_SERIAL_LENGTH - 1] = '\0'; + } + + status = bladerf_open_with_devinfo(&dev, &info); + + if (status == BLADERF_ERR_NODEV) { + fprintf(stderr, "No devices available with serial=%s\n", serial); + return NULL; + } else if (status != 0) { + fprintf(stderr, "Failed to open device with serial=%s (%s)\n", + serial, bladerf_strerror(status)); + return NULL; + } else { + return dev; + } +} diff --git a/plugins/samplesource/bladerf/bladerfinput.h b/plugins/samplesource/bladerf/bladerfinput.h new file mode 100644 index 000000000..0039ea851 --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfinput.h @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_BLADERFINPUT_H +#define INCLUDE_BLADERFINPUT_H + +#include "dsp/samplesource/samplesource.h" +#include +#include + +class BladerfThread; + +class BladerfInput : public SampleSource { +public: + struct Settings { + qint32 m_lnaGain; + qint32 m_vga1; + qint32 m_vga2; + qint32 m_samplerate; + qint32 m_bandwidth; + quint32 m_log2Decim; + bool m_xb200; + bladerf_xb200_path m_xb200Path; + bladerf_xb200_filter m_xb200Filter; + + Settings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + }; + + class MsgConfigureBladerf : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const GeneralSettings& getGeneralSettings() const { return m_generalSettings; } + const Settings& getSettings() const { return m_settings; } + + static MsgConfigureBladerf* create(const GeneralSettings& generalSettings, const Settings& settings) + { + return new MsgConfigureBladerf(generalSettings, settings); + } + + private: + GeneralSettings m_generalSettings; + Settings m_settings; + + MsgConfigureBladerf(const GeneralSettings& generalSettings, const Settings& settings) : + Message(), + m_generalSettings(generalSettings), + m_settings(settings) + { } + }; + + class MsgReportBladerf : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgReportBladerf* create() + { + return new MsgReportBladerf(); + } + + protected: + + MsgReportBladerf() : + Message() + { } + }; + + BladerfInput(MessageQueue* msgQueueToGUI); + ~BladerfInput(); + + bool startInput(int device); + void stopInput(); + + const QString& getDeviceDescription() const; + int getSampleRate() const; + quint64 getCenterFrequency() const; + + bool handleMessage(Message* message); + +private: + QMutex m_mutex; + Settings m_settings; + struct bladerf* m_dev; + BladerfThread* m_bladerfThread; + QString m_deviceDescription; + + bool applySettings(const GeneralSettings& generalSettings, const Settings& settings, bool force); + bladerf_lna_gain getLnaGain(int lnaGain); + struct bladerf *open_bladerf_from_serial(const char *serial); +}; + +#endif // INCLUDE_BLADERFINPUT_H diff --git a/plugins/samplesource/bladerf/bladerfplugin.cpp b/plugins/samplesource/bladerf/bladerfplugin.cpp new file mode 100644 index 000000000..974218a01 --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfplugin.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" +#include "bladerfgui.h" +#include "bladerfplugin.h" + +const PluginDescriptor BlderfPlugin::m_pluginDescriptor = { + QString("BladerRF Input"), + QString("1.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/rtl-sdrangelove/tree/f4exb"), + true, + QString("https://github.com/f4exb/rtl-sdrangelove/tree/f4exb") +}; + +BlderfPlugin::BlderfPlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& BlderfPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void BlderfPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.bladerf", this); +} + +PluginInterface::SampleSourceDevices BlderfPlugin::enumSampleSources() +{ + SampleSourceDevices result; + struct bladerf_devinfo *devinfo; + int count = bladerf_get_device_list(&devinfo); + + for(int i = 0; i < count; i++) + { + QString displayedName(QString("BladeRF #%1 %2 (%3,%4)").arg(devinfo[i].instance).arg(devinfo[i].serial).arg(devinfo[i].usb_bus).arg(devinfo[i].usb_addr)); + SimpleSerializer s(1); + s.writeS32(1, i); + s.writeString(2, devinfo[i].serial); + result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.bladerf", s.final())); + } + return result; +} + +PluginGUI* BlderfPlugin::createSampleSource(const QString& sourceName, const QByteArray& address) +{ + if(sourceName == "org.osmocom.sdr.samplesource.bladerf") { + BladerfGui* gui = new BladerfGui(m_pluginAPI); + m_pluginAPI->setInputGUI(gui); + return gui; + } else { + return NULL; + } +} diff --git a/plugins/samplesource/bladerf/bladerfplugin.h b/plugins/samplesource/bladerf/bladerfplugin.h new file mode 100644 index 000000000..f0d49388d --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfplugin.h @@ -0,0 +1,27 @@ +#ifndef INCLUDE_BLADERFPLUGIN_H +#define INCLUDE_BLADERFPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class BlderfPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "org.osmocom.sdr.samplesource.bladerf") + +public: + explicit BlderfPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + SampleSourceDevices enumSampleSources(); + PluginGUI* createSampleSource(const QString& sourceName, const QByteArray& address); + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_BLADERFPLUGIN_H diff --git a/plugins/samplesource/bladerf/bladerfthread.cpp b/plugins/samplesource/bladerf/bladerfthread.cpp new file mode 100644 index 000000000..9549046c2 --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfthread.cpp @@ -0,0 +1,213 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "dsp/samplefifo.h" +#include "bladerfthread.h" + + + +BladerfThread::BladerfThread(struct bladerf* dev, SampleFifo* sampleFifo, QObject* parent) : + QThread(parent), + m_running(false), + m_dev(dev), + m_convertBuffer(BLADERF_BLOCKSIZE), + m_sampleFifo(sampleFifo), + m_samplerate(2400000), + m_log2Decim(0) +{ +} + +BladerfThread::~BladerfThread() +{ + stopWork(); +} + +void BladerfThread::startWork() +{ + m_startWaitMutex.lock(); + start(); + while(!m_running) + m_startWaiter.wait(&m_startWaitMutex, 100); + m_startWaitMutex.unlock(); +} + +void BladerfThread::stopWork() +{ + m_running = false; + wait(); +} + +void BladerfThread::setSamplerate(int samplerate) +{ + m_samplerate = samplerate; +} + +void BladerfThread::setLog2Decimation(unsigned int log2_decim) +{ + m_log2Decim = log2_decim; +} + +void BladerfThread::run() +{ + int res; + + m_running = true; + m_startWaiter.wakeAll(); + + while(m_running) { + if((res = bladerf_sync_rx(m_dev, m_buf, BLADERF_BLOCKSIZE, NULL, 10000)) < 0) { + qCritical("BladerfThread: sync error: %s", strerror(errno)); + break; + } + + callback(m_buf, 2 * BLADERF_BLOCKSIZE); + } + + m_running = false; +} + +void BladerfThread::decimate1(SampleVector::iterator* it, const qint16* buf, qint32 len) +{ + qint16 xreal, yimag; + + for (int pos = 0; pos < len; pos += 2) { + xreal = buf[pos+0]; + yimag = buf[pos+1]; + Sample s( xreal * 16, yimag * 16 ); // shift by 4 bit positions (signed) + **it = s; + (*it)++; + } +} + +void BladerfThread::decimate2_u(SampleVector::iterator* it, const quint16* buf, qint32 len) +{ + qint16 xreal, yimag; + for (int pos = 0; pos < len - 7; pos += 8) { + xreal = buf[pos+0] - buf[pos+3]; + yimag = buf[pos+1] + buf[pos+2] - 255; + Sample s( xreal << 3, yimag << 3 ); + **it = s; + (*it)++; + xreal = buf[pos+7] - buf[pos+4]; + yimag = 255 - buf[pos+5] - buf[pos+6]; + Sample t( xreal << 3, yimag << 3 ); + **it = t; + (*it)++; + } +} + +void BladerfThread::decimate2(SampleVector::iterator* it, const qint16* buf, qint32 len) +{ + qint16 xreal, yimag; + for (int pos = 0; pos < len - 7; pos += 8) { + xreal = buf[pos+0] - buf[pos+3]; + yimag = buf[pos+1] + buf[pos+2]; + Sample s( xreal << 3, yimag << 3 ); + **it = s; + (*it)++; + xreal = buf[pos+7] - buf[pos+4]; + yimag = - buf[pos+5] - buf[pos+6]; + Sample t( xreal << 3, yimag << 3 ); + **it = t; + (*it)++; + } +} + +void BladerfThread::decimate4(SampleVector::iterator* it, const qint16* buf, qint32 len) +{ + qint16 xreal, yimag; + for (int pos = 0; pos < len - 7; pos += 8) { + xreal = buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]; + yimag = buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]; + Sample s( xreal << 2, yimag << 2 ); // was shift 3 + **it = s; + (*it)++; + } +} + +void BladerfThread::decimate8(SampleVector::iterator* it, const qint16* buf, qint32 len) +{ + qint16 xreal, yimag; + for (int pos = 0; pos < len - 15; pos += 8) { + xreal = buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]; + yimag = buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]; + Sample s1( xreal << 2, yimag << 2 ); // was shift 3 + pos += 8; + xreal = buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]; + yimag = buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]; + Sample s2( xreal << 2, yimag << 2 ); // was shift 3 + + m_decimator2.myDecimate(&s1, &s2); + **it = s2; + (*it)++; + } +} + +void BladerfThread::decimate16(SampleVector::iterator* it, const qint16* buf, qint32 len) +{ + // Offset tuning: 4x downsample and rotate, then + // downsample 4x more. [ rotate: 0, 1, -3, 2, -4, -5, 7, -6] + qint16 xreal[4], yimag[4]; + + for (int pos = 0; pos < len - 31; ) { + for (int i = 0; i < 4; i++) { + xreal[i] = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << 2; // was shift 4 + yimag[i] = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << 2; // was shift 4 + pos += 8; + } + Sample s1( xreal[0], yimag[0] ); + Sample s2( xreal[1], yimag[1] ); + Sample s3( xreal[2], yimag[2] ); + Sample s4( xreal[3], yimag[3] ); + m_decimator2.myDecimate(&s1, &s2); + m_decimator2.myDecimate(&s3, &s4); + m_decimator4.myDecimate(&s2, &s4); + **it = s4; + (*it)++; + } +} + +// Decimate according to specified log2 (ex: log2=4 => decim=16) +void BladerfThread::callback(const qint16* buf, qint32 len) +{ + SampleVector::iterator it = m_convertBuffer.begin(); + + switch (m_log2Decim) + { + case 0: + decimate1(&it, buf, len); + break; + case 1: + decimate2(&it, buf, len); + break; + case 2: + decimate4(&it, buf, len); + break; + case 3: + decimate8(&it, buf, len); + break; + case 4: + decimate16(&it, buf, len); + break; + default: + break; + } + + m_sampleFifo->write(m_convertBuffer.begin(), it); +} diff --git a/plugins/samplesource/bladerf/bladerfthread.h b/plugins/samplesource/bladerf/bladerfthread.h new file mode 100644 index 000000000..f18b00454 --- /dev/null +++ b/plugins/samplesource/bladerf/bladerfthread.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_BLADERFTHREAD_H +#define INCLUDE_BLADERFTHREAD_H + +#include +#include +#include +#include +#include "dsp/samplefifo.h" +#include "dsp/inthalfbandfilter.h" + +#define BLADERF_BLOCKSIZE 16384 + +class BladerfThread : public QThread { + Q_OBJECT + +public: + BladerfThread(struct bladerf* dev, SampleFifo* sampleFifo, QObject* parent = NULL); + ~BladerfThread(); + + void startWork(); + void stopWork(); + void setSamplerate(int samplerate); + void setLog2Decimation(unsigned int log2_decim); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + + struct bladerf* m_dev; + qint16 m_buf[2*BLADERF_BLOCKSIZE]; + SampleVector m_convertBuffer; + SampleFifo* m_sampleFifo; + + int m_samplerate; + unsigned int m_log2Decim; + + IntHalfbandFilter m_decimator2; + IntHalfbandFilter m_decimator4; + + void run(); + void decimate1(SampleVector::iterator* it, const qint16* buf, qint32 len); + void decimate2_u(SampleVector::iterator* it, const quint16* buf, qint32 len); + void decimate2(SampleVector::iterator* it, const qint16* buf, qint32 len); + void decimate4(SampleVector::iterator* it, const qint16* buf, qint32 len); + void decimate8(SampleVector::iterator* it, const qint16* buf, qint32 len); + void decimate16(SampleVector::iterator* it, const qint16* buf, qint32 len); + void callback(const qint16* buf, qint32 len); +}; + +#endif // INCLUDE_BLADERFTHREAD_H diff --git a/plugins/samplesource/fcd/fcdgui.cpp b/plugins/samplesource/fcd/fcdgui.cpp index e5ec71d76..dfda51a93 100644 --- a/plugins/samplesource/fcd/fcdgui.cpp +++ b/plugins/samplesource/fcd/fcdgui.cpp @@ -33,6 +33,11 @@ void FCDGui::setName(const QString& name) setObjectName(name); } +QString FCDGui::getName() const +{ + return objectName(); +} + void FCDGui::resetToDefaults() { m_generalSettings.resetToDefaults(); diff --git a/plugins/samplesource/fcd/fcdgui.h b/plugins/samplesource/fcd/fcdgui.h index 72b37593a..be7a50309 100644 --- a/plugins/samplesource/fcd/fcdgui.h +++ b/plugins/samplesource/fcd/fcdgui.h @@ -20,6 +20,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serializeGeneral() const; diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 3fa641344..577ca3943 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -34,6 +34,11 @@ void RTLSDRGui::setName(const QString& name) setObjectName(name); } +QString RTLSDRGui::getName() const +{ + return objectName(); +} + void RTLSDRGui::resetToDefaults() { m_generalSettings.resetToDefaults(); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index a0882ce65..e392f06a1 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -21,6 +21,7 @@ public: void destroy(); void setName(const QString& name); + QString getName() const; void resetToDefaults(); QByteArray serializeGeneral() const; diff --git a/sdrbase/dsp/samplesource/samplesource.cpp b/sdrbase/dsp/samplesource/samplesource.cpp index dda41d060..a13e05cbe 100644 --- a/sdrbase/dsp/samplesource/samplesource.cpp +++ b/sdrbase/dsp/samplesource/samplesource.cpp @@ -18,6 +18,8 @@ #include "dsp/samplesource/samplesource.h" #include "util/simpleserializer.h" +#include + SampleSource::GeneralSettings::GeneralSettings() : m_centerFrequency(100000000) { @@ -40,12 +42,15 @@ bool SampleSource::GeneralSettings::deserialize(const QByteArray& data) SimpleDeserializer d(data); if(!d.isValid()) { + std::cerr << "SampleSource::GeneralSettings::deserialize: invalid deserializer" << std::endl; resetToDefaults(); return false; } if(d.getVersion() == 1) { d.readU64(1, &m_centerFrequency, 100000000); + std::cerr << "SampleSource::GeneralSettings::deserialize: center frequency = " + << m_centerFrequency << std::endl; return true; } else { resetToDefaults(); diff --git a/sdrbase/mainwindow.cpp b/sdrbase/mainwindow.cpp index 06def4656..d427a3330 100644 --- a/sdrbase/mainwindow.cpp +++ b/sdrbase/mainwindow.cpp @@ -36,6 +36,8 @@ #include "plugin/pluginapi.h" #include "plugin/plugingui.h" +#include + MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), @@ -174,22 +176,29 @@ void MainWindow::setInputGUI(QWidget* gui) void MainWindow::loadSettings() { - m_settings.load(); + std::cerr << "MainWindow::loadSettings" << std::endl; + m_settings.load(); - for(int i = 0; i < m_settings.getPresetCount(); ++i) - addPresetToTree(m_settings.getPreset(i)); + for(int i = 0; i < m_settings.getPresetCount(); ++i) + { + addPresetToTree(m_settings.getPreset(i)); + } - Preset* current = m_settings.getCurrent(); + Preset* current = m_settings.getCurrent(); - loadSettings(current); + loadSettings(current); } void MainWindow::loadSettings(const Preset* preset) { - if(preset->getShowScope()) { + std::cerr << "MainWindow::loadSettings(preset): " << preset->getSource().toStdString() << std::endl; + + if(preset->getShowScope()) + { on_action_Oscilloscope_triggered(); m_scopeWindow->deserialize(preset->getScopeConfig()); - } + } + ui->glSpectrumGUI->deserialize(preset->getSpectrumConfig()); ui->dcOffset->setChecked(preset->getDCOffsetCorrection()); ui->iqImbalance->setChecked(preset->getIQImbalanceCorrection()); @@ -202,21 +211,32 @@ void MainWindow::loadSettings(const Preset* preset) void MainWindow::saveSettings() { + std::cerr << "MainWindow::saveSettings" << std::endl; + saveSettings(m_settings.getCurrent()); m_settings.save(); + } void MainWindow::saveSettings(Preset* preset) { + std::cerr << "MainWindow::saveSettings(preset): " << preset->getSource().toStdString() << std::endl; + preset->setSpectrumConfig(ui->glSpectrumGUI->serialize()); + if(preset->getShowScope()) + { preset->setScopeConfig(m_scopeWindow->serialize()); - else preset->setScopeConfig(QByteArray()); + } + else + { + preset->setScopeConfig(QByteArray()); + } - preset->clearChannels(); - m_pluginManager->saveSettings(preset); + preset->clearChannels(); + m_pluginManager->saveSettings(preset); - preset->setLayout(saveState()); + preset->setLayout(saveState()); } void MainWindow::createStatusBar() diff --git a/sdrbase/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp index 2f01a254a..2f31c32e4 100644 --- a/sdrbase/plugin/pluginmanager.cpp +++ b/sdrbase/plugin/pluginmanager.cpp @@ -8,6 +8,10 @@ #include "dsp/dspengine.h" #include "dsp/samplesource/samplesource.h" +#include +#include +#include "util/stacktrace.h" + PluginManager::PluginManager(MainWindow* mainWindow, DSPEngine* dspEngine, QObject* parent) : QObject(parent), m_pluginAPI(this, mainWindow, dspEngine), @@ -67,12 +71,18 @@ void PluginManager::removeChannelInstance(PluginGUI* pluginGUI) void PluginManager::registerSampleSource(const QString& sourceName, PluginInterface* plugin) { + std::cerr << "PluginManager::registerSampleSource " + << plugin->getPluginDescriptor().displayedName.toStdString() + << " with source name " << sourceName.toStdString() << std::endl; + m_sampleSourceRegistrations.append(SampleSourceRegistration(sourceName, plugin)); } void PluginManager::loadSettings(const Preset* preset) { - qDebug("-------- [%s | %s] --------", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + std::cerr << "PluginManager::loadSettings" << std::endl; + + fprintf(stderr, "-------- [%s | %s] --------\n", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); // copy currently open channels and clear list ChannelInstanceRegistrations openChannels = m_channelInstanceRegistrations; @@ -114,8 +124,10 @@ void PluginManager::loadSettings(const Preset* preset) renameChannelInstances(); if(m_sampleSourceInstance != NULL) { + std::cerr << "m_sampleSourceInstance->deserializeGeneral (" << m_sampleSourceInstance->getName().toStdString() << ")" << std::endl; m_sampleSourceInstance->deserializeGeneral(preset->getSourceGeneralConfig()); if(m_sampleSource == preset->getSource()) { + std::cerr << "m_sampleSourceInstance->deserialize" << std::endl; m_sampleSourceInstance->deserialize(preset->getSourceConfig()); } } @@ -187,6 +199,8 @@ void PluginManager::fillSampleSourceSelector(QComboBox* comboBox) int PluginManager::selectSampleSource(int index) { + std::cout << "PluginManager::selectSampleSource by index" << std::endl; + m_dspEngine->stopAcquistion(); if(m_sampleSourceInstance != NULL) { @@ -215,12 +229,15 @@ int PluginManager::selectSampleSource(int index) return -1; m_sampleSource = m_sampleSourceDevices[index].m_sourceName; + std::cerr << "m_sampleSource at index " << index << " is " << m_sampleSource.toStdString() << std::endl; m_sampleSourceInstance = m_sampleSourceDevices[index].m_plugin->createSampleSource(m_sampleSource, m_sampleSourceDevices[index].m_address); return index; } int PluginManager::selectSampleSource(const QString& source) { + std::cout << "PluginManager::selectSampleSource by name: " << source.toStdString() << std::endl; + int index = -1; m_dspEngine->stopAcquistion(); @@ -249,6 +266,7 @@ int PluginManager::selectSampleSource(const QString& source) return -1; m_sampleSource = m_sampleSourceDevices[index].m_sourceName; + std::cerr << "m_sampleSource at index " << index << " is " << m_sampleSource.toStdString() << std::endl; m_sampleSourceInstance = m_sampleSourceDevices[index].m_plugin->createSampleSource(m_sampleSource, m_sampleSourceDevices[index].m_address); return index; } diff --git a/sdrbase/settings/preset.cpp b/sdrbase/settings/preset.cpp index 8af9dc972..85f5480dc 100644 --- a/sdrbase/settings/preset.cpp +++ b/sdrbase/settings/preset.cpp @@ -1,6 +1,8 @@ #include "util/simpleserializer.h" #include "settings/preset.h" +#include + Preset::Preset() { resetToDefaults(); @@ -25,6 +27,8 @@ void Preset::resetToDefaults() QByteArray Preset::serialize() const { + std::cerr << "Preset::serialize (" << this->getSource().toStdString()<< ")" << std::endl; + SimpleSerializer s(1); s.writeString(1, m_group); s.writeString(2, m_description); @@ -39,10 +43,13 @@ QByteArray Preset::serialize() const s.writeBlob(11, m_sourceGeneralConfig); s.writeBlob(12, m_sourceConfig); - s.writeS32(100, m_channelConfigs.size()); + s.writeS32(200, m_channelConfigs.size()); + + std::cerr << " m_group: " << m_group.toStdString() << std::endl; + for(int i = 0; i < m_channelConfigs.size(); i++) { - s.writeString(101 + i * 2, m_channelConfigs[i].m_channel); - s.writeBlob(102 + i * 2, m_channelConfigs[i].m_config); + s.writeString(201 + i * 2, m_channelConfigs[i].m_channel); + s.writeBlob(202 + i * 2, m_channelConfigs[i].m_config); } return s.final(); @@ -50,6 +57,7 @@ QByteArray Preset::serialize() const bool Preset::deserialize(const QByteArray& data) { + std::cerr << "Preset::deserialize (" << this->getSource().toStdString() << ")" << std::endl; SimpleDeserializer d(data); if(!d.isValid()) { @@ -71,13 +79,16 @@ bool Preset::deserialize(const QByteArray& data) d.readBlob(11, &m_sourceGeneralConfig); d.readBlob(12, &m_sourceConfig); + std::cerr << " m_group: " << m_group.toStdString() << std::endl; + qint32 channelCount = 0; - d.readS32(100, &channelCount, 0); + d.readS32(200, &channelCount, 0); + for(int i = 0; i < channelCount; i++) { QString channel; QByteArray config; - d.readString(101 + i * 2, &channel, "unknown-channel"); - d.readBlob(102 + i * 2, &config); + d.readString(201 + i * 2, &channel, "unknown-channel"); + d.readBlob(202 + i * 2, &config); m_channelConfigs.append(ChannelConfig(channel, config)); } return true;