From 1e374791305e4b4c8a183ac481f9a4799bc34356 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 9 Sep 2015 09:13:24 +0200 Subject: [PATCH 1/7] Support for Airspy: interim state #1 --- plugins/samplesource/airspy/CMakeLists.txt | 52 ++ plugins/samplesource/airspy/airspygui.cpp | 386 +++++++++++++ plugins/samplesource/airspy/airspygui.h | 95 ++++ plugins/samplesource/airspy/airspygui.ui | 505 ++++++++++++++++++ plugins/samplesource/airspy/airspyinput.cpp | 429 +++++++++++++++ plugins/samplesource/airspy/airspyinput.h | 116 ++++ plugins/samplesource/airspy/airspyplugin.cpp | 118 ++++ plugins/samplesource/airspy/airspyplugin.h | 43 ++ .../samplesource/airspy/airspyserializer.cpp | 86 +++ .../samplesource/airspy/airspyserializer.h | 44 ++ plugins/samplesource/airspy/airspythread.cpp | 175 ++++++ plugins/samplesource/airspy/airspythread.h | 62 +++ 12 files changed, 2111 insertions(+) create mode 100644 plugins/samplesource/airspy/CMakeLists.txt create mode 100644 plugins/samplesource/airspy/airspygui.cpp create mode 100644 plugins/samplesource/airspy/airspygui.h create mode 100644 plugins/samplesource/airspy/airspygui.ui create mode 100644 plugins/samplesource/airspy/airspyinput.cpp create mode 100644 plugins/samplesource/airspy/airspyinput.h create mode 100644 plugins/samplesource/airspy/airspyplugin.cpp create mode 100644 plugins/samplesource/airspy/airspyplugin.h create mode 100644 plugins/samplesource/airspy/airspyserializer.cpp create mode 100644 plugins/samplesource/airspy/airspyserializer.h create mode 100644 plugins/samplesource/airspy/airspythread.cpp create mode 100644 plugins/samplesource/airspy/airspythread.h diff --git a/plugins/samplesource/airspy/CMakeLists.txt b/plugins/samplesource/airspy/CMakeLists.txt new file mode 100644 index 000000000..d3161d347 --- /dev/null +++ b/plugins/samplesource/airspy/CMakeLists.txt @@ -0,0 +1,52 @@ +project(airspy) + +set(airspy_SOURCES + airspygui.cpp + airspyinput.cpp + airspyplugin.cpp + airspyserializer.cpp + airspythread.cpp +) + +set(airspy_HEADERS + airspygui.h + airspyinput.h + airspyplugin.h + airspyserializer.h + airspythread.h +) + +set(airspy_FORMS + airspygui.ui +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include-gpl + ${LIBAIRSPY_INCLUDE_DIR} +) + +#include(${QT_USE_FILE}) +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +#qt4_wrap_cpp(airspy_HEADERS_MOC ${airspy_HEADERS}) +qt5_wrap_ui(airspy_FORMS_HEADERS ${airspy_FORMS}) + +add_library(inputairspy SHARED + ${airspy_SOURCES} + ${airspy_HEADERS_MOC} + ${airspy_FORMS_HEADERS} +) + +target_link_libraries(inputairspy + ${QT_LIBRARIES} + ${LIBAIRSPY_LIBRARIES} + ${LIBUSB_LIBRARIES} + sdrbase +) + +qt5_use_modules(inputairspy Core Widgets OpenGL Multimedia) diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp new file mode 100644 index 000000000..8d5066a09 --- /dev/null +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -0,0 +1,386 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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 "airspygui.h" +#include "ui_airspygui.h" +#include "plugin/pluginapi.h" +#include "gui/colormapper.h" +#include "dsp/dspengine.h" + +AirspyGui::AirspyGui(PluginAPI* pluginAPI, QWidget* parent) : + QWidget(parent), + ui(new Ui::AirspyGui), + m_pluginAPI(pluginAPI), + m_settings(), + m_sampleSource(NULL) +{ + ui->setupUi(this); + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold)); + ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + displaySettings(); + + m_sampleSource = new AirspyInput(); + DSPEngine::instance()->setSource(m_sampleSource); +} + +AirspyGui::~AirspyGui() +{ + delete m_sampleSource; // Valgrind memcheck + delete ui; +} + +void AirspyGui::destroy() +{ + delete this; +} + +void AirspyGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString AirspyGui::getName() const +{ + return objectName(); +} + +void AirspyGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 AirspyGui::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +QByteArray AirspyGui::serialize() const +{ + return m_settings.serialize(); +} + +bool AirspyGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool AirspyGui::handleMessage(const Message& message) +{ + if (AirspyInput::MsgReportAirspy::match(message)) + { + displaySettings(); + return true; + } + else + { + return false; + } +} + +void AirspyGui::displaySettings() +{ + ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + + ui->samplerateText->setText(tr("%1k").arg(m_settings.m_devSampleRate / 1000)); + unsigned int sampleRateIndex = AirspySampleRates::getRateIndex(m_settings.m_devSampleRate); + ui->samplerate->setValue(sampleRateIndex); + + ui->bandwidthText->setText(tr("%1k").arg(m_settings.m_bandwidth / 1000)); + unsigned int bandwidthIndex = AirspyBandwidths::getBandwidthIndex(m_settings.m_bandwidth); + ui->bandwidth->setValue(bandwidthIndex); + + ui->decimText->setText(tr("%1").arg(1<decim->setValue(m_settings.m_log2Decim); + + ui->fcPos->setCurrentIndex((int) m_settings.m_fcPos); + + ui->lnaGainText->setText(tr("%1dB").arg(m_settings.m_lnaGain*3)); + ui->lna->setValue(m_settings.m_lnaGain); + + ui->vga1Text->setText(tr("%1dB").arg(m_settings.m_vga1)); + ui->vga1->setValue(m_settings.m_vga1); + + ui->vga2Text->setText(tr("%1dB").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 AirspyGui::sendSettings() +{ + if(!m_updateTimer.isActive()) + m_updateTimer.start(100); +} + +void AirspyGui::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void AirspyGui::on_samplerate_valueChanged(int value) +{ + int newrate = AirspySampleRates::getRate(value); + ui->samplerateText->setText(tr("%1k").arg(newrate)); + m_settings.m_devSampleRate = newrate * 1000; + sendSettings(); +} + +void AirspyGui::on_bandwidth_valueChanged(int value) +{ + int newbw = AirspyBandwidths::getBandwidth(value); + ui->bandwidthText->setText(tr("%1k").arg(newbw)); + m_settings.m_bandwidth = newbw * 1000; + sendSettings(); +} + +void AirspyGui::on_decim_valueChanged(int value) +{ + if ((value <0) || (value > 5)) + return; + ui->decimText->setText(tr("%1").arg(1< BLADERF_RXVGA1_GAIN_MAX)) + return; + + ui->vga1Text->setText(tr("%1dB").arg(value)); + m_settings.m_vga1 = value; + sendSettings(); +} + +void AirspyGui::on_vga2_valueChanged(int value) +{ + if ((value < BLADERF_RXVGA2_GAIN_MIN) || (value > BLADERF_RXVGA2_GAIN_MAX)) + return; + + ui->vga2Text->setText(tr("%1dB").arg(value)); + m_settings.m_vga2 = value; + sendSettings(); +} + +void AirspyGui::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; + } + + if (m_settings.m_xb200) + { + ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); + } + else + { + ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN/1000, BLADERF_FREQUENCY_MAX/1000); + } + + sendSettings(); +} + +void AirspyGui::updateHardware() +{ + qDebug() << "AirspyGui::updateHardware"; + AirspyInput::MsgConfigureAirspy* message = AirspyInput::MsgConfigureAirspy::create( m_settings); + m_sampleSource->getInputMessageQueue()->push(message); + m_updateTimer.stop(); +} + +unsigned int AirspyGui::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 AirspySampleRates::m_rates[] = {2500, 10000}; +unsigned int AirspySampleRates::m_nb_rates = 2; + +unsigned int AirspySampleRates::getRate(unsigned int rate_index) +{ + if (rate_index < m_nb_rates) + { + return m_rates[rate_index]; + } + else + { + return m_rates[0]; + } +} + +unsigned int AirspySampleRates::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 AirspyBandwidths::m_halfbw[] = {}; +unsigned int AirspyBandwidths::m_nb_halfbw = 0; + +unsigned int AirspyBandwidths::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 AirspyBandwidths::getBandwidthIndex(unsigned int bandwidth) +{ + for (unsigned int i=0; i < m_nb_halfbw; i++) + { + if (bandwidth/2000 == m_halfbw[i]) + { + return i; + } + } + + return 0; +} diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h new file mode 100644 index 000000000..a39d1aeb2 --- /dev/null +++ b/plugins/samplesource/airspy/airspygui.h @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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_AIRSPYGUI_H +#define INCLUDE_AIRSPYGUI_H + +#include + +#include "airspyinput.h" +#include "plugin/plugingui.h" + +#define AIRSPY_MAX_DEVICE (32) + +class PluginAPI; + +namespace Ui { + class AirspyGui; + class AirspySampleRates; +} + +class AirspyGui : public QWidget, public PluginGUI { + Q_OBJECT + +public: + explicit AirspyGui(PluginAPI* pluginAPI, QWidget* parent = NULL); + virtual ~AirspyGui(); + void destroy(); + + void setName(const QString& name); + QString getName() const; + + void resetToDefaults(); + qint64 getCenterFrequency() const; + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual bool handleMessage(const Message& message); + +private: + Ui::AirspyGui* ui; + + PluginAPI* m_pluginAPI; + AirspyInput::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 on_fcPos_currentIndexChanged(int index); + void updateHardware(); +}; + +class AirspySampleRates { +public: + static unsigned int getRate(unsigned int rate_index); + static unsigned int getRateIndex(unsigned int rate); +private: + static unsigned int m_rates[14]; + static unsigned int m_nb_rates; +}; + +class AirspyBandwidths { +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_AIRSPYGUI_H diff --git a/plugins/samplesource/airspy/airspygui.ui b/plugins/samplesource/airspy/airspygui.ui new file mode 100644 index 000000000..8659ba029 --- /dev/null +++ b/plugins/samplesource/airspy/airspygui.ui @@ -0,0 +1,505 @@ + + + BladerfGui + + + + 0 + 0 + 198 + 239 + + + + + 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 + + + + + + + + + + 3 + + + + + LO correction ppm + + + LO ppm + + + + + + + -100 + + + 100 + + + 1 + + + Qt::Horizontal + + + + + + + 0.0 + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + Device Samplerate + + + 1 + + + 1 + + + 0 + + + 0 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Rate + + + + + + + + 40 + 0 + + + + --- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 3 + + + + + Dec. + + + + + + + 5 + + + 1 + + + 0 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 1 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 3 + + + + + Fc pos + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + Inf + + + + + Sup + + + + + Cent + + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + + 0 + 0 + + + + LNA + + + + + + + true + + + LNA gain dB + + + 14 + + + 1 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 3 + + + + + Mixer gain dB + + + 0 + + + 15 + + + 1 + + + 15 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Mixer + + + + + + + + + 3 + + + + + VGA + + + + + + + VGA gain dB + + + 15 + + + 1 + + + 1 + + + 9 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + + Bias Tee + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+
+ + +
diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp new file mode 100644 index 000000000..f1e5581bf --- /dev/null +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -0,0 +1,429 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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 "airspygui.h" +#include "airspyinput.h" +#include "util/simpleserializer.h" +#include "dsp/dspcommands.h" +#include "airspyserializer.h" +#include "airspythread.h" + +MESSAGE_CLASS_DEFINITION(AirspyInput::MsgConfigureAirspy, Message) +MESSAGE_CLASS_DEFINITION(AirspyInput::MsgReportAirspy, Message) + +AirspyInput::Settings::Settings() : + m_centerFrequency(435000*1000), + m_devSampleRate(2500000), + m_LOppmTenths(0), + m_lnaGain(1), + m_mixerGain(5), + m_vgaGain(5), + m_log2Decim(0), + m_fcPos(FC_POS_INFRA), + m_biasT(false) +{ +} + +void AirspyInput::Settings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_devSampleRate = 2500000; + m_LOppmTenths = 0; + m_lnaGain = 1; + m_mixerGain = 5; + m_vgaGain = 5; + m_log2Decim = 0; + m_fcPos = FC_POS_INFRA; + m_biasT = false; +} + +QByteArray AirspyInput::Settings::serialize() const +{ + AirspySerializer::AirspyData data; + + data.m_data.m_frequency = m_centerFrequency; + data.m_LOppmTenths = m_LOppmTenths; + data.m_sampleRate = m_devSampleRate; + data.m_log2Decim = m_log2Decim; + data.m_fcPos = m_fcPos; + data.m_lnaGain = m_lnaGain; + data.m_mixerGain = m_mixerGain; + data.m_vgaGain = m_vgaGain; + data.m_biasT = m_biasT; + + QByteArray byteArray; + + AirspySerializer::writeSerializedData(data, byteArray); + + return byteArray; +} + +bool AirspyInput::Settings::deserialize(const QByteArray& serializedData) +{ + AirspySerializer::AirspyData data; + + bool valid = AirspySerializer::readSerializedData(serializedData, data); + + m_centerFrequency = data.m_data.m_frequency; + m_LOppmTenths = data.m_LOppmTenths; + m_devSampleRate = data.m_sampleRate; + m_log2Decim = data.m_log2Decim; + m_fcPos = data.m_fcPos; + m_lnaGain = data.m_lnaGain; + m_mixerGain = data.m_mixerGain; + m_vgaGain = data.m_vgaGain; + m_biasT = data.m_biasT; + + return valid; +} + +AirspyInput::AirspyInput() : + m_settings(), + m_dev(0), + m_airspyThread(0), + m_deviceDescription("Airspy") +{ +} + +AirspyInput::~AirspyInput() +{ + stop(); +} + +bool AirspyInput::init(const Message& cmd) +{ + return false; +} + +bool AirspyInput::start(int device) +{ + QMutexLocker mutexLocker(&m_mutex); + airspy_error rc; + + rc = airspy_init(); + + if (rc != AIRSPY_SUCCESS) + { + qCritical("AirspyInput::start: failed to initiate Airspy library %s", airspy_error_name(rc)); + } + + if (m_dev != 0) + { + stop(); + } + + if (!m_sampleFifo.setSize(96000 * 4)) + { + qCritical("AirspyInput::start: could not allocate SampleFifo"); + return false; + } + + if ((m_dev = open_airspy_from_sequence(device)) == 0) // TODO: fix; Open first available device as there is no proper handling for multiple devices + { + qCritical("AirspyInput::start: could not open Airspy"); + return false; + } + +#ifdef LIBAIRSPY_OLD + m_sampleRates.push_back(2500000); + m_sampleRates.push_back(10000000) +#else + uint32_t nbSampleRates; + uint32_t sampleRates[]; + + airspy_get_samplerates(m_dev, &nbSampleRates, 0); + + sampleRates = new uint32_t[nbSampleRates]; + + airspy_get_samplerates(m_dev, sampleRates, nbSampleRates); + + for (int i=0; ipush(message); + + if((m_airspyThread = new AirspyThread(m_dev, &m_sampleFifo)) == NULL) { + qFatal("out of memory"); + goto failed; + } + + m_airspyThread->startWork(); + + mutexLocker.unlock(); + applySettings(m_settings, true); + + qDebug("AirspyInput::startInput: started"); + + return true; +} + +void AirspyInput::stop() +{ + QMutexLocker mutexLocker(&m_mutex); + + if(m_airspyThread != 0) + { + m_airspyThread->stopWork(); + delete m_airspyThread; + m_airspyThread = 0; + } + + if(m_dev != 0) + { + airspy_stop_rx(m_dev); + airspy_close(m_dev); + m_dev = 0; + } + + airspy_exit(); +} + +const QString& AirspyInput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int AirspyInput::getSampleRate() const +{ + int rate = m_settings.m_devSampleRate; + return (rate / (1<setSamplerate(m_settings.m_devSampleRate); + } + } + } + + if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) + { + m_settings.m_log2Decim = settings.m_log2Decim; + forwardChange = true; + + if(m_dev != 0) + { + m_airspyThread->setLog2Decimation(m_settings.m_log2Decim); + qDebug() << "AirspyInput: set decimation to " << (1<setFcPos((int) m_settings.m_fcPos); + qDebug() << "AirspyInput: set fc pos (enum) to " << (int) m_settings.m_fcPos; + } + } + + if (m_settings.m_centerFrequency != settings.m_centerFrequency) + { + forwardChange = true; + } + + m_settings.m_centerFrequency = settings.m_centerFrequency; + + qint64 deviceCenterFrequency = m_settings.m_centerFrequency; + qint64 f_img = deviceCenterFrequency; + qint64 f_cut = deviceCenterFrequency + m_settings.m_bandwidth/2; + + if ((m_settings.m_log2Decim == 0) || (m_settings.m_fcPos == FC_POS_CENTER)) + { + deviceCenterFrequency = m_settings.m_centerFrequency; + f_img = deviceCenterFrequency; + f_cut = deviceCenterFrequency + m_settings.m_bandwidth/2; + } + else + { + if (m_settings.m_fcPos == FC_POS_INFRA) + { + deviceCenterFrequency = m_settings.m_centerFrequency + (m_settings.m_devSampleRate / 4); + f_img = deviceCenterFrequency + m_settings.m_devSampleRate/2; + f_cut = deviceCenterFrequency + m_settings.m_bandwidth/2; + } + else if (m_settings.m_fcPos == FC_POS_SUPRA) + { + deviceCenterFrequency = m_settings.m_centerFrequency - (m_settings.m_devSampleRate / 4); + f_img = deviceCenterFrequency - m_settings.m_devSampleRate/2; + f_cut = deviceCenterFrequency - m_settings.m_bandwidth/2; + } + } + + if (m_dev != NULL) + { + if (bladerf_set_frequency( m_dev, BLADERF_MODULE_RX, deviceCenterFrequency ) != 0) + { + qDebug("bladerf_set_frequency(%lld) failed", m_settings.m_centerFrequency); + } + } + + if (forwardChange) + { + int sampleRate = m_settings.m_devSampleRate/(1<push(notif); + } + + qDebug() << "AirspyInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz" + << " device center freq: " << deviceCenterFrequency << " Hz" + << " device sample rate: " << m_settings.m_devSampleRate << "Hz" + << " Actual sample rate: " << m_settings.m_devSampleRate/(1<. // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AIRSPYINPUT_H +#define INCLUDE_AIRSPYINPUT_H + +#include "dsp/samplesource.h" +#include +#include + +class AirspyThread; + +class AirspyInput : public SampleSource { +public: + typedef enum { + FC_POS_INFRA = 0, + FC_POS_SUPRA, + FC_POS_CENTER + } fcPos_t; + + struct Settings { + quint64 m_centerFrequency; + qint32 m_LOppmTenths; + int m_devSampleRate; + quint32 m_lnaGain; + quint32 m_mixerGain; + quint32 m_vgaGain; + quint32 m_log2Decim; + fcPos_t m_fcPos; + bool m_biasT; + + Settings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + }; + + class MsgConfigureAirspy : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const Settings& getSettings() const { return m_settings; } + + static MsgConfigureAirspy* create(const Settings& settings) + { + return new MsgConfigureAirspy(settings); + } + + private: + Settings m_settings; + + MsgConfigureAirspy(const Settings& settings) : + Message(), + m_settings(settings) + { } + }; + + class MsgReportAirspy : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const std::vector& getSampleRates() const { return m_sampleRates; } + + static MsgReportAirspy* create(const std::vector& sampleRates) + { + return new MsgReportAirspy(sampleRates); + } + + protected: + std::vector m_sampleRates; + + MsgReportAirspy(const std::vector& sampleRates) : + Message(), + m_sampleRates(sampleRates) + { } + }; + + AirspyInput(); + virtual ~AirspyInput(); + + virtual bool init(const Message& message); + virtual bool start(int device); + virtual void stop(); + + virtual const QString& getDeviceDescription() const; + virtual int getSampleRate() const; + virtual quint64 getCenterFrequency() const; + + virtual bool handleMessage(const Message& message); + +private: + bool applySettings(const Settings& settings, bool force); + struct airspy_device *open_airspy_from_sequence(int sequence); + + QMutex m_mutex; + Settings m_settings; + struct airspy_device* m_dev; + AirspyThread* m_airspyThread; + QString m_deviceDescription; + std::vector m_sampleRates; +}; + +#endif // INCLUDE_AIRSPYINPUT_H diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp new file mode 100644 index 000000000..a5cd2cf0c --- /dev/null +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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 "airspygui.h" +#include "airspyplugin.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" + +const PluginDescriptor AirspyPlugin::m_pluginDescriptor = { + QString("Airspy Input"), + QString("---"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +AirspyPlugin::AirspyPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& AirspyPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void AirspyPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.airspy", this); +} + +PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() +{ + SampleSourceDevices result; + airspy_read_partid_serialno_t read_partid_serialno; + struct airspy_device *devinfo = 0; + airspy_error rc; + + rc = airspy_init(); + + if (rc != AIRSPY_SUCCESS) + { + qCritical() << "AirspyPlugin::enumSampleSources: failed to initiate Airspy library " + << airspy_error_name(rc) << std::endl; + } + + for (int i=0; i < AIRSPY_MAX_DEVICE; i++) + { + rc = airspy_open(&devinfo); + + if (rc == AIRSPY_SUCCESS) + { + rc = airspy_board_partid_serialno_read(devinfo, &read_partid_serialno); + + if (rc != AIRSPY_SUCCESS) + { + continue; // next + } + + QString serial_str = QString::number(read_partid_serialno.serial_no[2], 16) + QString::number(read_partid_serialno.serial_no[3], 16); + uint64_t serial_num = (read_partid_serialno.serial_no[2] << 32) + read_partid_serialno.serial_no[3]; + QString displayedName(QString("Airspy #%1 0x%2").arg(i+1).arg(serial_str)); + SimpleSerializer s(1); + s.writeS32(1, i); + s.writeString(2, serial_str); + s.writeU64(3, serial_num); + + result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.airspy", s.final())); + } + else + { + break; // finished + } + } + + airspy_exit(); + + return result; +} + +PluginGUI* AirspyPlugin::createSampleSourcePluginGUI(const QString& sourceName, const QByteArray& address) +{ + if (!m_pluginAPI) + { + return 0; + } + + if(sourceName == "org.osmocom.sdr.samplesource.airspy") + { + AirspyGui* gui = new AirspyGui(m_pluginAPI); + m_pluginAPI->setInputGUI(gui); + return gui; + } + else + { + return 0; + } +} diff --git a/plugins/samplesource/airspy/airspyplugin.h b/plugins/samplesource/airspy/airspyplugin.h new file mode 100644 index 000000000..cada63d88 --- /dev/null +++ b/plugins/samplesource/airspy/airspyplugin.h @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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_AIRSPYPLUGIN_H +#define INCLUDE_AIRSPYPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class AirspyPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "org.osmocom.sdr.samplesource.airspy") + +public: + explicit AirspyPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + SampleSourceDevices enumSampleSources(); + PluginGUI* createSampleSourcePluginGUI(const QString& sourceName, const QByteArray& address); + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_AIRSPYPLUGIN_H diff --git a/plugins/samplesource/airspy/airspyserializer.cpp b/plugins/samplesource/airspy/airspyserializer.cpp new file mode 100644 index 000000000..d2a54ef48 --- /dev/null +++ b/plugins/samplesource/airspy/airspyserializer.cpp @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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 "airspyserializer.h" + +void AirspySerializer::writeSerializedData(const AirspyData& data, QByteArray& serializedData) +{ + QByteArray sampleSourceSerialized; + SampleSourceSerializer::writeSerializedData(data.m_data, sampleSourceSerialized); + + SimpleSerializer s(1); + + s.writeBlob(1, sampleSourceSerialized); + s.writeS32(2, data.m_LOppmTenths); + s.writeS32(3, data.m_sampleRate); + s.writeU32(4, data.m_log2Decim); + s.writeU32(5, data.m_fcPos); + s.writeU32(6, data.m_lnaGain); + s.writeU32(7, data.m_mixerGain); + s.writeU32(8, data.m_vgaGain); + s.writeBool(9, data.m_biasT); + + serializedData = s.final(); +} + +bool AirspySerializer::readSerializedData(const QByteArray& serializedData, AirspyData& data) +{ + bool valid = SampleSourceSerializer::readSerializedData(serializedData, data.m_data); + + QByteArray sampleSourceSerialized; + + SimpleDeserializer d(serializedData); + + if (!d.isValid()) + { + setDefaults(data); + return false; + } + + if (d.getVersion() == SampleSourceSerializer::getSerializerVersion()) + { + int intval; + + d.readBlob(1, &sampleSourceSerialized); + d.readS32(2, &data.m_LOppmTenths, 0); + d.readS32(3, &data.m_sampleRate, 0); + d.readU32(4, &data.m_log2Decim, 0); + d.readU32(5, &data.m_fcPos, 0); + d.readU32(6, &data.m_lnaGain, 1); + d.readU32(7, &data.m_mixerGain, 5); + d.readU32(8, &data.m_vgaGain, 5); + d.readBool(9, &data.m_biasT, false); + + return SampleSourceSerializer::readSerializedData(sampleSourceSerialized, data.m_data); + } + else + { + setDefaults(data); + return false; + } +} + +void AirspySerializer::setDefaults(AirspyData& data) +{ + data.m_LOppmTenths = 0; + data.m_sampleRate = 0; + data.m_log2Decim = 0; + data.m_fcPos = 0; + data.m_lnaGain = 1; + data.m_mixerGain = 5; + data.m_vgaGain = 5; + data.m_biasT = false; +} diff --git a/plugins/samplesource/airspy/airspyserializer.h b/plugins/samplesource/airspy/airspyserializer.h new file mode 100644 index 000000000..6c0580fe1 --- /dev/null +++ b/plugins/samplesource/airspy/airspyserializer.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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 PLUGINS_SAMPLESOURCE_AIRSPY_AIRSPYSERIALIZER_H_ +#define PLUGINS_SAMPLESOURCE_AIRSPY_AIRSPYSERIALIZER_H_ + +#include "util/samplesourceserializer.h" + +class AirspySerializer +{ +public: + struct AirspyData + { + SampleSourceSerializer::Data m_data; + qint32 m_LOppmTenths; + qint32 m_sampleRate; + quint32 m_log2Decim; + quint32 m_fcPos; + quint32 m_lnaGain; + quint32 m_mixerGain; + quint32 m_vgaGain; + bool m_biasT; + }; + + static void writeSerializedData(const AirspyData& data, QByteArray& serializedData); + static bool readSerializedData(const QByteArray& serializedData, AirspyData& data); + static void setDefaults(AirspyData& data); +}; + + +#endif /* PLUGINS_SAMPLESOURCE_AIRSPY_AIRSPYSERIALIZER_H_ */ diff --git a/plugins/samplesource/airspy/airspythread.cpp b/plugins/samplesource/airspy/airspythread.cpp new file mode 100644 index 000000000..a8d16d4b5 --- /dev/null +++ b/plugins/samplesource/airspy/airspythread.cpp @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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 "airspythread.h" +#include "dsp/samplefifo.h" + + + +AirspyThread::AirspyThread(struct bladerf* dev, SampleFifo* sampleFifo, QObject* parent) : + QThread(parent), + m_running(false), + m_dev(dev), + m_convertBuffer(AIRSPY_BLOCKSIZE), + m_sampleFifo(sampleFifo), + m_samplerate(10), + m_log2Decim(0), + m_fcPos(0) +{ +} + +AirspyThread::~AirspyThread() +{ + stopWork(); +} + +void AirspyThread::startWork() +{ + m_startWaitMutex.lock(); + start(); + while(!m_running) + m_startWaiter.wait(&m_startWaitMutex, 100); + m_startWaitMutex.unlock(); +} + +void AirspyThread::stopWork() +{ + m_running = false; + wait(); +} + +void AirspyThread::setSamplerate(int samplerate) +{ + m_samplerate = samplerate; +} + +void AirspyThread::setLog2Decimation(unsigned int log2_decim) +{ + m_log2Decim = log2_decim; +} + +void AirspyThread::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +void AirspyThread::run() +{ + int res; + + m_running = true; + m_startWaiter.wakeAll(); + + while(m_running) { + if((res = bladerf_sync_rx(m_dev, m_buf, AIRSPY_BLOCKSIZE, NULL, 10000)) < 0) { + qCritical("AirspyThread: sync error: %s", strerror(errno)); + break; + } + + callback(m_buf, 2 * AIRSPY_BLOCKSIZE); + } + + m_running = false; +} + +// Decimate according to specified log2 (ex: log2=4 => decim=16) +void AirspyThread::callback(const qint16* buf, qint32 len) +{ + SampleVector::iterator it = m_convertBuffer.begin(); + + if (m_log2Decim == 0) + { + m_decimators.decimate1(&it, buf, len); + } + else + { + if (m_fcPos == 0) // Infra + { + switch (m_log2Decim) + { + case 1: + m_decimators.decimate2_inf(&it, buf, len); + break; + case 2: + m_decimators.decimate4_inf(&it, buf, len); + break; + case 3: + m_decimators.decimate8_inf(&it, buf, len); + break; + case 4: + m_decimators.decimate16_inf(&it, buf, len); + break; + case 5: + m_decimators.decimate32_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 1) // Supra + { + switch (m_log2Decim) + { + case 1: + m_decimators.decimate2_sup(&it, buf, len); + break; + case 2: + m_decimators.decimate4_sup(&it, buf, len); + break; + case 3: + m_decimators.decimate8_sup(&it, buf, len); + break; + case 4: + m_decimators.decimate16_sup(&it, buf, len); + break; + case 5: + m_decimators.decimate32_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 2) // Center + { + switch (m_log2Decim) + { + case 1: + m_decimators.decimate2_cen(&it, buf, len); + break; + case 2: + m_decimators.decimate4_cen(&it, buf, len); + break; + case 3: + m_decimators.decimate8_cen(&it, buf, len); + break; + case 4: + m_decimators.decimate16_cen(&it, buf, len); + break; + case 5: + m_decimators.decimate32_cen(&it, buf, len); + break; + default: + break; + } + } + } + + + m_sampleFifo->write(m_convertBuffer.begin(), it); +} diff --git a/plugins/samplesource/airspy/airspythread.h b/plugins/samplesource/airspy/airspythread.h new file mode 100644 index 000000000..8849a928e --- /dev/null +++ b/plugins/samplesource/airspy/airspythread.h @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// 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_AIRSPYTHREAD_H +#define INCLUDE_AIRSPYTHREAD_H + +#include +#include +#include +#include +#include "dsp/samplefifo.h" +#include "dsp/decimators.h" + +#define AIRSPY_BLOCKSIZE (1<<14) + +class AirspyThread : public QThread { + Q_OBJECT + +public: + AirspyThread(struct bladerf* dev, SampleFifo* sampleFifo, QObject* parent = NULL); + ~AirspyThread(); + + void startWork(); + void stopWork(); + void setSamplerate(int samplerate); + void setLog2Decimation(unsigned int log2_decim); + void setFcPos(int fcPos); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + + struct bladerf* m_dev; + qint16 m_buf[2*AIRSPY_BLOCKSIZE]; + SampleVector m_convertBuffer; + SampleFifo* m_sampleFifo; + + int m_samplerate; + unsigned int m_log2Decim; + int m_fcPos; + + Decimators m_decimators; + + void run(); + void callback(const qint16* buf, qint32 len); +}; + +#endif // INCLUDE_AIRSPYTHREAD_H From 61e6e3e2cc3b7fc9d2ce4cf8642df0696b580ad2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 10 Sep 2015 05:53:37 +0200 Subject: [PATCH 2/7] Support for Airspy: compiles --- Readme.md | 10 + cmake/Modules/FindLibAIRSPY.cmake | 28 ++ plugins/samplesource/CMakeLists.txt | 5 + plugins/samplesource/airspy/CMakeLists.txt | 1 + plugins/samplesource/airspy/airspygui.cpp | 213 +++----------- plugins/samplesource/airspy/airspygui.h | 35 +-- plugins/samplesource/airspy/airspygui.ui | 87 +----- plugins/samplesource/airspy/airspyinput.cpp | 274 +++++++++++------- plugins/samplesource/airspy/airspyinput.h | 13 +- plugins/samplesource/airspy/airspyplugin.cpp | 11 +- .../samplesource/airspy/airspyserializer.cpp | 10 +- .../samplesource/airspy/airspyserializer.h | 4 +- plugins/samplesource/airspy/airspythread.cpp | 34 ++- plugins/samplesource/airspy/airspythread.h | 7 +- 14 files changed, 318 insertions(+), 414 deletions(-) create mode 100644 cmake/Modules/FindLibAIRSPY.cmake diff --git a/Readme.md b/Readme.md index 6f4578fc9..47b980835 100644 --- a/Readme.md +++ b/Readme.md @@ -43,6 +43,16 @@ Use `cmake ../ -DV4L-RTL=ON` to build the Linux kernel driver for RTL-SDR (Exper

Supported hardware

+

Airspy

+ +Airspy is supported through the libairspy library that should be installed in your system for proper build of the software and operation support. Add `libairspy-dev` to the list of dependencies to install. + +If you use your own location for libairspy install directory you need to specify library and include locations. Example with `/opt/install/libairspy` with the following defines on `cmake` command line: + +`-DLIBAIRSPY_LIBRARIES=/opt/install/libairspy/lib/libairspy.so -DLIBAIRSPY_INCLUDE_DIR=/opt/install/libairspy/include` + +Please note that if you are not using a recent version of libairspy (>= 1.0.6) the dynamic retrieval of sample rates is not supported. In this case you should modify the `plugins/samplesource/airspy/CMakeLists.txt` and change line `add_definitions(${QT_DEFINITIONS})` by `add_definitions("${QT_DEFINITIONS} -DLIBAIRSPY_OLD")`. In fact both lines are present with the former one commented out. +

BladeRF

BladeRF is supported through the libbladerf library that should be installed in your system for proper build of the software and operation support. Add `libbladerf-dev` to the list of dependencies to install. diff --git a/cmake/Modules/FindLibAIRSPY.cmake b/cmake/Modules/FindLibAIRSPY.cmake new file mode 100644 index 000000000..2402f2f74 --- /dev/null +++ b/cmake/Modules/FindLibAIRSPY.cmake @@ -0,0 +1,28 @@ +if(NOT LIBAIRSPY_FOUND) + + pkg_check_modules (LIBAIRSPY_PKG libairspy) + find_path(LIBAIRSPY_INCLUDE_DIR NAMES libairspy/airspy.h + PATHS + ${LIBAIRSPY_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include + ) + + find_library(LIBAIRSPY_LIBRARIES NAMES airspy + PATHS + ${LIBAIRSPY_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + + if(LIBAIRSPY_INCLUDE_DIR AND LIBAIRSPY_LIBRARIES) + set(LIBAIRSPY_FOUND TRUE CACHE INTERNAL "libairspy found") + message(STATUS "Found libairspy: ${LIBAIRSPY_INCLUDE_DIR}, ${LIBAIRSPY_LIBRARIES}") + else(LIBAIRSPY_INCLUDE_DIR AND LIBAIRSPY_LIBRARIES) + set(LIBAIRSPY_FOUND FALSE CACHE INTERNAL "libairspy found") + message(STATUS "libairspy not found.") + endif(LIBAIRSPY_INCLUDE_DIR AND LIBAIRSPY_LIBRARIES) + + mark_as_advanced(LIBAIRSPY_INCLUDE_DIR LIBAIRSPY_LIBRARIES) + +endif(NOT LIBAIRSPY_FOUND) diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index 150632393..ae8d2babb 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -32,4 +32,9 @@ if(LIBUSB_FOUND AND LIBBLADERF_FOUND) add_subdirectory(bladerf) endif(LIBUSB_FOUND AND LIBBLADERF_FOUND) +find_package(LibAIRSPY) +if(LIBUSB_FOUND AND LIBAIRSPY_FOUND) + add_subdirectory(airspy) +endif(LIBUSB_FOUND AND LIBAIRSPY_FOUND) + add_subdirectory(filesource) diff --git a/plugins/samplesource/airspy/CMakeLists.txt b/plugins/samplesource/airspy/CMakeLists.txt index d3161d347..bf39ea115 100644 --- a/plugins/samplesource/airspy/CMakeLists.txt +++ b/plugins/samplesource/airspy/CMakeLists.txt @@ -29,6 +29,7 @@ include_directories( ) #include(${QT_USE_FILE}) +#add_definitions("${QT_DEFINITIONS} -DLIBAIRSPY_OLD") add_definitions(${QT_DEFINITIONS}) add_definitions(-DQT_PLUGIN) add_definitions(-DQT_SHARED) diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 8d5066a09..2cf2e838d 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -32,7 +32,7 @@ AirspyGui::AirspyGui(PluginAPI* pluginAPI, QWidget* parent) : { ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold)); - ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); + ui->centerFrequency->setValueRange(7, 24000U, 1900000U); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); displaySettings(); @@ -107,29 +107,26 @@ void AirspyGui::displaySettings() { ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); - ui->samplerateText->setText(tr("%1k").arg(m_settings.m_devSampleRate / 1000)); - unsigned int sampleRateIndex = AirspySampleRates::getRateIndex(m_settings.m_devSampleRate); - ui->samplerate->setValue(sampleRateIndex); + ui->LOppm->setValue(m_settings.m_LOppmTenths); + ui->LOppmText->setText(QString("%1").arg(QString::number(m_settings.m_LOppmTenths/10.0, 'f', 1))); - ui->bandwidthText->setText(tr("%1k").arg(m_settings.m_bandwidth / 1000)); - unsigned int bandwidthIndex = AirspyBandwidths::getBandwidthIndex(m_settings.m_bandwidth); - ui->bandwidth->setValue(bandwidthIndex); + ui->sampleRate->setCurrentIndex(m_settings.m_devSampleRateIndex); + + ui->biasT->setChecked(m_settings.m_biasT); ui->decimText->setText(tr("%1").arg(1<decim->setValue(m_settings.m_log2Decim); ui->fcPos->setCurrentIndex((int) m_settings.m_fcPos); - ui->lnaGainText->setText(tr("%1dB").arg(m_settings.m_lnaGain*3)); + ui->lnaGainText->setText(tr("%1dB").arg(m_settings.m_lnaGain)); ui->lna->setValue(m_settings.m_lnaGain); - ui->vga1Text->setText(tr("%1dB").arg(m_settings.m_vga1)); - ui->vga1->setValue(m_settings.m_vga1); + ui->mixText->setText(tr("%1dB").arg(m_settings.m_mixerGain)); + ui->mix->setValue(m_settings.m_mixerGain); - ui->vga2Text->setText(tr("%1dB").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)); + ui->vgaText->setText(tr("%1dB").arg(m_settings.m_vgaGain)); + ui->vga->setValue(m_settings.m_vgaGain); } void AirspyGui::sendSettings() @@ -144,19 +141,22 @@ void AirspyGui::on_centerFrequency_changed(quint64 value) sendSettings(); } -void AirspyGui::on_samplerate_valueChanged(int value) +void AirspyGui::on_LOppm_valueChanged(int value) { - int newrate = AirspySampleRates::getRate(value); - ui->samplerateText->setText(tr("%1k").arg(newrate)); - m_settings.m_devSampleRate = newrate * 1000; + m_settings.m_LOppmTenths = value; + ui->LOppmText->setText(QString("%1").arg(QString::number(m_settings.m_LOppmTenths/10.0, 'f', 1))); sendSettings(); } -void AirspyGui::on_bandwidth_valueChanged(int value) +void AirspyGui::on_sampleRate_currentIndexChanged(int index) { - int newbw = AirspyBandwidths::getBandwidth(value); - ui->bandwidthText->setText(tr("%1k").arg(newbw)); - m_settings.m_bandwidth = newbw * 1000; + m_settings.m_devSampleRateIndex = index; + sendSettings(); +} + +void AirspyGui::on_biasT_stateChanged(int state) +{ + m_settings.m_biasT = (state == Qt::Checked); sendSettings(); } @@ -185,9 +185,7 @@ void AirspyGui::on_fcPos_currentIndexChanged(int index) void AirspyGui::on_lna_valueChanged(int value) { - qDebug() << "AirspyGui: LNA gain = " << value; - - if ((value < 0) || (value > 2)) + if ((value < 0) || (value > 14)) return; ui->lnaGainText->setText(tr("%1dB").arg(value*3)); @@ -195,83 +193,23 @@ void AirspyGui::on_lna_valueChanged(int value) sendSettings(); } -void AirspyGui::on_vga1_valueChanged(int value) +void AirspyGui::on_mix_valueChanged(int value) { - if ((value < BLADERF_RXVGA1_GAIN_MIN) || (value > BLADERF_RXVGA1_GAIN_MAX)) + if ((value < 0) || (value > 15)) return; - ui->vga1Text->setText(tr("%1dB").arg(value)); - m_settings.m_vga1 = value; + ui->mixText->setText(tr("%1dB").arg(value*3)); + m_settings.m_lnaGain = value; sendSettings(); } -void AirspyGui::on_vga2_valueChanged(int value) +void AirspyGui::on_vga_valueChanged(int value) { - if ((value < BLADERF_RXVGA2_GAIN_MIN) || (value > BLADERF_RXVGA2_GAIN_MAX)) + if ((value < 0) || (value > 15)) return; - ui->vga2Text->setText(tr("%1dB").arg(value)); - m_settings.m_vga2 = value; - sendSettings(); -} - -void AirspyGui::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; - } - - if (m_settings.m_xb200) - { - ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); - } - else - { - ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN/1000, BLADERF_FREQUENCY_MAX/1000); - } - + ui->vgaText->setText(tr("%1dB").arg(value*3)); + m_settings.m_lnaGain = value; sendSettings(); } @@ -283,58 +221,9 @@ void AirspyGui::updateHardware() m_updateTimer.stop(); } -unsigned int AirspyGui::getXb200Index(bool xb_200, bladerf_xb200_path xb200Path, bladerf_xb200_filter xb200Filter) +uint32_t AirspyGui::getDevSampleRate(unsigned int rate_index) { - 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 AirspySampleRates::m_rates[] = {2500, 10000}; -unsigned int AirspySampleRates::m_nb_rates = 2; - -unsigned int AirspySampleRates::getRate(unsigned int rate_index) -{ - if (rate_index < m_nb_rates) + if (rate_index < m_rates.size()) { return m_rates[rate_index]; } @@ -344,43 +233,15 @@ unsigned int AirspySampleRates::getRate(unsigned int rate_index) } } -unsigned int AirspySampleRates::getRateIndex(unsigned int rate) +int AirspyGui::getDevSampleRateIndex(uint32_t sampeRate) { - for (unsigned int i=0; i < m_nb_rates; i++) + for (unsigned int i=0; i < m_rates.size(); i++) { - if (rate/1000 == m_rates[i]) + if (sampeRate == m_rates[i]) { return i; } } - return 0; -} - -unsigned int AirspyBandwidths::m_halfbw[] = {}; -unsigned int AirspyBandwidths::m_nb_halfbw = 0; - -unsigned int AirspyBandwidths::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 AirspyBandwidths::getBandwidthIndex(unsigned int bandwidth) -{ - for (unsigned int i=0; i < m_nb_halfbw; i++) - { - if (bandwidth/2000 == m_halfbw[i]) - { - return i; - } - } - - return 0; + return -1; } diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index a39d1aeb2..377d2d608 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -47,6 +47,8 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual bool handleMessage(const Message& message); + uint32_t getDevSampleRate(unsigned int index); + int getDevSampleRateIndex(uint32_t sampleRate); private: Ui::AirspyGui* ui; @@ -54,42 +56,23 @@ private: PluginAPI* m_pluginAPI; AirspyInput::Settings m_settings; QTimer m_updateTimer; - std::vector m_gains; + std::vector m_rates; 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_LOppm_valueChanged(int value); + void on_sampleRate_currentIndexChanged(int index); + void on_biasT_stateChanged(int state); 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 on_fcPos_currentIndexChanged(int index); + void on_lna_valueChanged(int value); + void on_mix_valueChanged(int value); + void on_vga_valueChanged(int value); void updateHardware(); }; -class AirspySampleRates { -public: - static unsigned int getRate(unsigned int rate_index); - static unsigned int getRateIndex(unsigned int rate); -private: - static unsigned int m_rates[14]; - static unsigned int m_nb_rates; -}; - -class AirspyBandwidths { -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_AIRSPYGUI_H diff --git a/plugins/samplesource/airspy/airspygui.ui b/plugins/samplesource/airspy/airspygui.ui index 8659ba029..72e00564b 100644 --- a/plugins/samplesource/airspy/airspygui.ui +++ b/plugins/samplesource/airspy/airspygui.ui @@ -1,13 +1,13 @@ - BladerfGui - + AirspyGui + 0 0 198 - 239 + 214 @@ -128,7 +128,7 @@ - + 0.0 @@ -148,30 +148,15 @@ 3 - - - - Device Samplerate - - - 1 - - - 1 - - - 0 - - - 0 - - - Qt::Horizontal + + + + Bias T - + 0 @@ -183,19 +168,10 @@ - - - - - 40 - 0 - - - - --- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + Device sample rate @@ -346,7 +322,7 @@ - 0 + 0dB Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -391,7 +367,7 @@ - 0 + 0dB Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -450,7 +426,7 @@ - 0 + 0dB Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -459,37 +435,6 @@ - - - - Qt::Horizontal - - - - - - - - - Bias Tee - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index f1e5581bf..79b318473 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -30,7 +30,7 @@ MESSAGE_CLASS_DEFINITION(AirspyInput::MsgReportAirspy, Message) AirspyInput::Settings::Settings() : m_centerFrequency(435000*1000), - m_devSampleRate(2500000), + m_devSampleRateIndex(0), m_LOppmTenths(0), m_lnaGain(1), m_mixerGain(5), @@ -44,7 +44,7 @@ AirspyInput::Settings::Settings() : void AirspyInput::Settings::resetToDefaults() { m_centerFrequency = 435000*1000; - m_devSampleRate = 2500000; + m_devSampleRateIndex = 0; m_LOppmTenths = 0; m_lnaGain = 1; m_mixerGain = 5; @@ -60,9 +60,9 @@ QByteArray AirspyInput::Settings::serialize() const data.m_data.m_frequency = m_centerFrequency; data.m_LOppmTenths = m_LOppmTenths; - data.m_sampleRate = m_devSampleRate; + data.m_sampleRateIndex = m_devSampleRateIndex; data.m_log2Decim = m_log2Decim; - data.m_fcPos = m_fcPos; + data.m_fcPos = (qint32) m_fcPos; data.m_lnaGain = m_lnaGain; data.m_mixerGain = m_mixerGain; data.m_vgaGain = m_vgaGain; @@ -83,9 +83,9 @@ bool AirspyInput::Settings::deserialize(const QByteArray& serializedData) m_centerFrequency = data.m_data.m_frequency; m_LOppmTenths = data.m_LOppmTenths; - m_devSampleRate = data.m_sampleRate; + m_devSampleRateIndex = data.m_sampleRateIndex; m_log2Decim = data.m_log2Decim; - m_fcPos = data.m_fcPos; + m_fcPos = (fcPos_t) data.m_fcPos; m_lnaGain = data.m_lnaGain; m_mixerGain = data.m_mixerGain; m_vgaGain = data.m_vgaGain; @@ -117,7 +117,7 @@ bool AirspyInput::start(int device) QMutexLocker mutexLocker(&m_mutex); airspy_error rc; - rc = airspy_init(); + rc = (airspy_error) airspy_init(); if (rc != AIRSPY_SUCCESS) { @@ -143,10 +143,10 @@ bool AirspyInput::start(int device) #ifdef LIBAIRSPY_OLD m_sampleRates.push_back(2500000); - m_sampleRates.push_back(10000000) + m_sampleRates.push_back(10000000); #else uint32_t nbSampleRates; - uint32_t sampleRates[]; + uint32_t *sampleRates; airspy_get_samplerates(m_dev, &nbSampleRates, 0); @@ -154,6 +154,12 @@ bool AirspyInput::start(int device) airspy_get_samplerates(m_dev, sampleRates, nbSampleRates); + if (nbSampleRates == 0) + { + qCritical("AirspyInput::start: could not obtain Airspy sample rates"); + return false; + } + for (int i=0; ipush(message); @@ -173,11 +189,14 @@ bool AirspyInput::start(int device) m_airspyThread->startWork(); mutexLocker.unlock(); - applySettings(m_settings, true); qDebug("AirspyInput::startInput: started"); return true; + +failed: + stop(); + return false; } void AirspyInput::stop() @@ -208,7 +227,7 @@ const QString& AirspyInput::getDeviceDescription() const int AirspyInput::getSampleRate() const { - int rate = m_settings.m_devSampleRate; + int rate = m_sampleRates[m_settings.m_devSampleRateIndex]; return (rate / (1<(freq_hz)); + + if (rc != AIRSPY_SUCCESS) + { + qWarning("AirspyInput::setCenterFrequency: could not frequency to %llu Hz", freq_hz); + } + else + { + qWarning("AirspyInput::setCenterFrequency: frequency set to %llu Hz", freq_hz); + } +} + bool AirspyInput::applySettings(const Settings& settings, bool force) { bool forwardChange = false; airspy_error rc; + qint64 deviceCenterFrequency = m_settings.m_centerFrequency; + qint64 f_img = deviceCenterFrequency; + quint32 devSampleRate = m_sampleRates[m_settings.m_devSampleRateIndex]; + QMutexLocker mutexLocker(&m_mutex); qDebug() << "AirspyInput::applySettings: m_dev: " << m_dev; - if ((m_settings.m_lnaGain != settings.m_lnaGain) || force) + if ((m_settings.m_devSampleRateIndex != settings.m_devSampleRateIndex) || force) { - m_settings.m_lnaGain = settings.m_lnaGain; - - if (m_dev != 0) + if (settings.m_devSampleRateIndex < m_sampleRates.size()) { - rc = airspy_set_lna_gain(m_dev, m_settings.m_lnaGain); - - if(rc != AIRSPY_SUCCESS) - { - qDebug("AirspyInput::applySettings: airspy_set_lna_gain failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: LNA gain set to " << m_settings.m_lnaGain; - } + m_settings.m_devSampleRateIndex = settings.m_devSampleRateIndex; } - } - - if ((m_settings.m_mixerGain != settings.m_mixerGain) || force) - { - m_settings.m_mixerGain = settings.m_mixerGain; - - if (m_dev != 0) + else { - rc = airspy_set_mixer_gain(m_dev, m_settings.m_mixerGain); - - if(rc != AIRSPY_SUCCESS) - { - qDebug("AirspyInput::applySettings: airspy_set_mixer_gain failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: mixer gain set to " << m_settings.m_mixerGain; - } + m_settings.m_devSampleRateIndex = m_sampleRates.size() - 1; } - } - if ((m_settings.m_vgaGain != settings.m_vgaGain) || force) - { - m_settings.m_vgaGain = settings.m_vgaGain; - - if (m_dev != 0) - { - rc = airspy_set_vga_gain(m_dev, m_settings.m_vgaGain); - - if(rc != AIRSPY_SUCCESS) - { - qDebug("AirspyInput::applySettings: airspy_set_vga_gain failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: VGA gain set to " << m_settings.m_vgaGain; - } - } - } - - if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) - { - m_settings.m_devSampleRate = settings.m_devSampleRate; forwardChange = true; if (m_dev != 0) { - unsigned int actualSamplerate; + rc = (airspy_error) airspy_set_samplerate(m_dev, static_cast(m_sampleRates[m_settings.m_devSampleRateIndex])); - if (bladerf_set_sample_rate(m_dev, BLADERF_MODULE_RX, m_settings.m_devSampleRate, &actualSamplerate) < 0) + if (rc != AIRSPY_SUCCESS) { - qCritical("could not set sample rate: %d", m_settings.m_devSampleRate); + qCritical("AirspyInput::applySettings: could not set sample rate index %u (%d S/s): %s", m_settings.m_devSampleRateIndex, m_sampleRates[m_settings.m_devSampleRateIndex], airspy_error_name(rc)); } else { - qDebug() << "bladerf_set_sample_rate(BLADERF_MODULE_RX) actual sample rate is " << actualSamplerate; - m_airspyThread->setSamplerate(m_settings.m_devSampleRate); + qDebug("AirspyInput::applySettings: sample rate set to index: %u (%d S/s)", m_settings.m_devSampleRateIndex, m_sampleRates[m_settings.m_devSampleRateIndex]); + m_airspyThread->setSamplerate(m_sampleRates[m_settings.m_devSampleRateIndex]); } } } @@ -346,63 +336,126 @@ bool AirspyInput::applySettings(const Settings& settings, bool force) } } - if (m_settings.m_centerFrequency != settings.m_centerFrequency) + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + m_settings.m_centerFrequency = settings.m_centerFrequency; + + if ((m_settings.m_log2Decim == 0) || (m_settings.m_fcPos == FC_POS_CENTER)) + { + deviceCenterFrequency = m_settings.m_centerFrequency; + f_img = deviceCenterFrequency; + } + else + { + if (m_settings.m_fcPos == FC_POS_INFRA) + { + deviceCenterFrequency = m_settings.m_centerFrequency + (devSampleRate / 4); + f_img = deviceCenterFrequency + devSampleRate/2; + } + else if (m_settings.m_fcPos == FC_POS_SUPRA) + { + deviceCenterFrequency = m_settings.m_centerFrequency - (devSampleRate / 4); + f_img = deviceCenterFrequency - devSampleRate/2; + } + } + + if (m_dev != 0) + { + setCenterFrequency(deviceCenterFrequency); + + qDebug() << "AirspyInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz" + << " device center freq: " << deviceCenterFrequency << " Hz" + << " device sample rate: " << devSampleRate << "Hz" + << " Actual sample rate: " << devSampleRate/(1<push(notif); } - qDebug() << "AirspyInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz" - << " device center freq: " << deviceCenterFrequency << " Hz" - << " device sample rate: " << m_settings.m_devSampleRate << "Hz" - << " Actual sample rate: " << m_settings.m_devSampleRate/(1<& getSampleRates() const { return m_sampleRates; } + const std::vector& getSampleRates() const { return m_sampleRates; } - static MsgReportAirspy* create(const std::vector& sampleRates) + static MsgReportAirspy* create(const std::vector& sampleRates) { return new MsgReportAirspy(sampleRates); } protected: - std::vector m_sampleRates; + std::vector m_sampleRates; - MsgReportAirspy(const std::vector& sampleRates) : + MsgReportAirspy(const std::vector& sampleRates) : Message(), m_sampleRates(sampleRates) { } @@ -104,13 +104,14 @@ public: private: bool applySettings(const Settings& settings, bool force); struct airspy_device *open_airspy_from_sequence(int sequence); + void setCenterFrequency(quint64 freq); QMutex m_mutex; Settings m_settings; struct airspy_device* m_dev; AirspyThread* m_airspyThread; QString m_deviceDescription; - std::vector m_sampleRates; + std::vector m_sampleRates; }; #endif // INCLUDE_AIRSPYINPUT_H diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index a5cd2cf0c..e5a0eaa6a 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -56,21 +56,20 @@ PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() struct airspy_device *devinfo = 0; airspy_error rc; - rc = airspy_init(); + rc = (airspy_error) airspy_init(); if (rc != AIRSPY_SUCCESS) { - qCritical() << "AirspyPlugin::enumSampleSources: failed to initiate Airspy library " - << airspy_error_name(rc) << std::endl; + qCritical("AirspyPlugin::enumSampleSources: failed to initiate Airspy library: %s", airspy_error_name(rc)); } for (int i=0; i < AIRSPY_MAX_DEVICE; i++) { - rc = airspy_open(&devinfo); + rc = (airspy_error) airspy_open(&devinfo); if (rc == AIRSPY_SUCCESS) { - rc = airspy_board_partid_serialno_read(devinfo, &read_partid_serialno); + rc = (airspy_error) airspy_board_partid_serialno_read(devinfo, &read_partid_serialno); if (rc != AIRSPY_SUCCESS) { @@ -78,7 +77,7 @@ PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() } QString serial_str = QString::number(read_partid_serialno.serial_no[2], 16) + QString::number(read_partid_serialno.serial_no[3], 16); - uint64_t serial_num = (read_partid_serialno.serial_no[2] << 32) + read_partid_serialno.serial_no[3]; + uint64_t serial_num = (((uint64_t) read_partid_serialno.serial_no[2])<<32) + read_partid_serialno.serial_no[3]; QString displayedName(QString("Airspy #%1 0x%2").arg(i+1).arg(serial_str)); SimpleSerializer s(1); s.writeS32(1, i); diff --git a/plugins/samplesource/airspy/airspyserializer.cpp b/plugins/samplesource/airspy/airspyserializer.cpp index d2a54ef48..9b29d2a04 100644 --- a/plugins/samplesource/airspy/airspyserializer.cpp +++ b/plugins/samplesource/airspy/airspyserializer.cpp @@ -25,9 +25,9 @@ void AirspySerializer::writeSerializedData(const AirspyData& data, QByteArray& s s.writeBlob(1, sampleSourceSerialized); s.writeS32(2, data.m_LOppmTenths); - s.writeS32(3, data.m_sampleRate); + s.writeU32(3, data.m_sampleRateIndex); s.writeU32(4, data.m_log2Decim); - s.writeU32(5, data.m_fcPos); + s.writeS32(5, data.m_fcPos); s.writeU32(6, data.m_lnaGain); s.writeU32(7, data.m_mixerGain); s.writeU32(8, data.m_vgaGain); @@ -56,9 +56,9 @@ bool AirspySerializer::readSerializedData(const QByteArray& serializedData, Airs d.readBlob(1, &sampleSourceSerialized); d.readS32(2, &data.m_LOppmTenths, 0); - d.readS32(3, &data.m_sampleRate, 0); + d.readU32(3, &data.m_sampleRateIndex, 0); d.readU32(4, &data.m_log2Decim, 0); - d.readU32(5, &data.m_fcPos, 0); + d.readS32(5, &data.m_fcPos, 0); d.readU32(6, &data.m_lnaGain, 1); d.readU32(7, &data.m_mixerGain, 5); d.readU32(8, &data.m_vgaGain, 5); @@ -76,7 +76,7 @@ bool AirspySerializer::readSerializedData(const QByteArray& serializedData, Airs void AirspySerializer::setDefaults(AirspyData& data) { data.m_LOppmTenths = 0; - data.m_sampleRate = 0; + data.m_sampleRateIndex = 0; data.m_log2Decim = 0; data.m_fcPos = 0; data.m_lnaGain = 1; diff --git a/plugins/samplesource/airspy/airspyserializer.h b/plugins/samplesource/airspy/airspyserializer.h index 6c0580fe1..eb6c1c8a2 100644 --- a/plugins/samplesource/airspy/airspyserializer.h +++ b/plugins/samplesource/airspy/airspyserializer.h @@ -26,9 +26,9 @@ public: { SampleSourceSerializer::Data m_data; qint32 m_LOppmTenths; - qint32 m_sampleRate; + quint32 m_sampleRateIndex; quint32 m_log2Decim; - quint32 m_fcPos; + qint32 m_fcPos; quint32 m_lnaGain; quint32 m_mixerGain; quint32 m_vgaGain; diff --git a/plugins/samplesource/airspy/airspythread.cpp b/plugins/samplesource/airspy/airspythread.cpp index a8d16d4b5..396ca8be0 100644 --- a/plugins/samplesource/airspy/airspythread.cpp +++ b/plugins/samplesource/airspy/airspythread.cpp @@ -20,9 +20,9 @@ #include "airspythread.h" #include "dsp/samplefifo.h" +AirspyThread *airspyThread = 0; - -AirspyThread::AirspyThread(struct bladerf* dev, SampleFifo* sampleFifo, QObject* parent) : +AirspyThread::AirspyThread(struct airspy_device* dev, SampleFifo* sampleFifo, QObject* parent) : QThread(parent), m_running(false), m_dev(dev), @@ -32,11 +32,13 @@ AirspyThread::AirspyThread(struct bladerf* dev, SampleFifo* sampleFifo, QObject* m_log2Decim(0), m_fcPos(0) { + airspyThread = this; } AirspyThread::~AirspyThread() { stopWork(); + airspyThread = 0; } void AirspyThread::startWork() @@ -54,7 +56,7 @@ void AirspyThread::stopWork() wait(); } -void AirspyThread::setSamplerate(int samplerate) +void AirspyThread::setSamplerate(uint32_t samplerate) { m_samplerate = samplerate; } @@ -71,18 +73,23 @@ void AirspyThread::setFcPos(int fcPos) void AirspyThread::run() { - int res; + airspy_error rc; m_running = true; m_startWaiter.wakeAll(); - while(m_running) { - if((res = bladerf_sync_rx(m_dev, m_buf, AIRSPY_BLOCKSIZE, NULL, 10000)) < 0) { - qCritical("AirspyThread: sync error: %s", strerror(errno)); - break; - } + rc = (airspy_error) airspy_start_rx(m_dev, rx_callback, NULL); - callback(m_buf, 2 * AIRSPY_BLOCKSIZE); + if (rc != AIRSPY_SUCCESS) + { + qCritical("AirspyInput::run: failed to start Airspy Rx: %s", airspy_error_name(rc)); + } + else + { + while ((m_running) && (airspy_is_streaming(m_dev) == AIRSPY_TRUE)) + { + sleep(1); + } } m_running = false; @@ -173,3 +180,10 @@ void AirspyThread::callback(const qint16* buf, qint32 len) m_sampleFifo->write(m_convertBuffer.begin(), it); } + + +int AirspyThread::rx_callback(airspy_transfer_t* transfer) +{ + qint32 bytes_to_write = transfer->sample_count * sizeof(qint16) * 2; + airspyThread->callback((qint16 *) transfer->samples, bytes_to_write); +} diff --git a/plugins/samplesource/airspy/airspythread.h b/plugins/samplesource/airspy/airspythread.h index 8849a928e..41db97862 100644 --- a/plugins/samplesource/airspy/airspythread.h +++ b/plugins/samplesource/airspy/airspythread.h @@ -30,12 +30,12 @@ class AirspyThread : public QThread { Q_OBJECT public: - AirspyThread(struct bladerf* dev, SampleFifo* sampleFifo, QObject* parent = NULL); + AirspyThread(struct airspy_device* dev, SampleFifo* sampleFifo, QObject* parent = NULL); ~AirspyThread(); void startWork(); void stopWork(); - void setSamplerate(int samplerate); + void setSamplerate(uint32_t samplerate); void setLog2Decimation(unsigned int log2_decim); void setFcPos(int fcPos); @@ -44,7 +44,7 @@ private: QWaitCondition m_startWaiter; bool m_running; - struct bladerf* m_dev; + struct airspy_device* m_dev; qint16 m_buf[2*AIRSPY_BLOCKSIZE]; SampleVector m_convertBuffer; SampleFifo* m_sampleFifo; @@ -57,6 +57,7 @@ private: void run(); void callback(const qint16* buf, qint32 len); + static int rx_callback(airspy_transfer_t* transfer); }; #endif // INCLUDE_AIRSPYTHREAD_H From 7e88cfcd4800529f75702b5cf3c5ac7a1300a862 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 10 Sep 2015 08:12:49 +0200 Subject: [PATCH 3/7] Support for Airspy: make pointer to thread private. Debug messages on enumeration --- plugins/samplesource/airspy/airspyplugin.cpp | 2 ++ plugins/samplesource/airspy/airspythread.cpp | 8 ++++---- plugins/samplesource/airspy/airspythread.h | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index e5a0eaa6a..b0394e4c2 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -73,6 +73,7 @@ PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() if (rc != AIRSPY_SUCCESS) { + qDebug("AirspyPlugin::enumSampleSources: failed to read serial no: %s", airspy_error_name(rc)); continue; // next } @@ -88,6 +89,7 @@ PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() } else { + qDebug("AirspyPlugin::enumSampleSources: enumerated %d Airspy devices %s", i, airspy_error_name(rc)); break; // finished } } diff --git a/plugins/samplesource/airspy/airspythread.cpp b/plugins/samplesource/airspy/airspythread.cpp index 396ca8be0..e837cce88 100644 --- a/plugins/samplesource/airspy/airspythread.cpp +++ b/plugins/samplesource/airspy/airspythread.cpp @@ -20,7 +20,7 @@ #include "airspythread.h" #include "dsp/samplefifo.h" -AirspyThread *airspyThread = 0; +AirspyThread *AirspyThread::m_this = 0; AirspyThread::AirspyThread(struct airspy_device* dev, SampleFifo* sampleFifo, QObject* parent) : QThread(parent), @@ -32,13 +32,13 @@ AirspyThread::AirspyThread(struct airspy_device* dev, SampleFifo* sampleFifo, QO m_log2Decim(0), m_fcPos(0) { - airspyThread = this; + m_this = this; } AirspyThread::~AirspyThread() { stopWork(); - airspyThread = 0; + m_this = 0; } void AirspyThread::startWork() @@ -185,5 +185,5 @@ void AirspyThread::callback(const qint16* buf, qint32 len) int AirspyThread::rx_callback(airspy_transfer_t* transfer) { qint32 bytes_to_write = transfer->sample_count * sizeof(qint16) * 2; - airspyThread->callback((qint16 *) transfer->samples, bytes_to_write); + m_this->callback((qint16 *) transfer->samples, bytes_to_write); } diff --git a/plugins/samplesource/airspy/airspythread.h b/plugins/samplesource/airspy/airspythread.h index 41db97862..1e9a7b880 100644 --- a/plugins/samplesource/airspy/airspythread.h +++ b/plugins/samplesource/airspy/airspythread.h @@ -52,6 +52,7 @@ private: int m_samplerate; unsigned int m_log2Decim; int m_fcPos; + static AirspyThread *m_this; Decimators m_decimators; From efa1a0c58ad3dd478b1db6869a5ef759e99b4064 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 11 Sep 2015 03:02:02 +0200 Subject: [PATCH 4/7] Support for Airspy: first working version --- Readme.md | 2 +- plugins/samplesource/airspy/CMakeLists.txt | 4 +- plugins/samplesource/airspy/airspygui.cpp | 55 ++++++++++++- plugins/samplesource/airspy/airspygui.h | 2 + plugins/samplesource/airspy/airspygui.ui | 4 +- plugins/samplesource/airspy/airspyinput.cpp | 82 +++++++++++--------- plugins/samplesource/airspy/airspyinput.h | 1 + plugins/samplesource/airspy/airspyplugin.cpp | 37 ++++++--- plugins/samplesource/airspy/airspythread.cpp | 16 +++- plugins/samplesource/airspy/airspythread.h | 2 +- 10 files changed, 147 insertions(+), 58 deletions(-) diff --git a/Readme.md b/Readme.md index 47b980835..5277342d5 100644 --- a/Readme.md +++ b/Readme.md @@ -51,7 +51,7 @@ If you use your own location for libairspy install directory you need to specify `-DLIBAIRSPY_LIBRARIES=/opt/install/libairspy/lib/libairspy.so -DLIBAIRSPY_INCLUDE_DIR=/opt/install/libairspy/include` -Please note that if you are not using a recent version of libairspy (>= 1.0.6) the dynamic retrieval of sample rates is not supported. In this case you should modify the `plugins/samplesource/airspy/CMakeLists.txt` and change line `add_definitions(${QT_DEFINITIONS})` by `add_definitions("${QT_DEFINITIONS} -DLIBAIRSPY_OLD")`. In fact both lines are present with the former one commented out. +Please note that if you are using a recent version of libairspy (>= 1.0.6) the dynamic retrieval of sample rates is supported. To benefit from it you should modify the `plugins/samplesource/airspy/CMakeLists.txt` and change line `add_definitions(${QT_DEFINITIONS})` by `add_definitions("${QT_DEFINITIONS} -DLIBAIRSPY_DYN_RATES")`. In fact both lines are present with the last one commented out.

BladeRF

diff --git a/plugins/samplesource/airspy/CMakeLists.txt b/plugins/samplesource/airspy/CMakeLists.txt index bf39ea115..5eaeaa731 100644 --- a/plugins/samplesource/airspy/CMakeLists.txt +++ b/plugins/samplesource/airspy/CMakeLists.txt @@ -29,8 +29,8 @@ include_directories( ) #include(${QT_USE_FILE}) -#add_definitions("${QT_DEFINITIONS} -DLIBAIRSPY_OLD") -add_definitions(${QT_DEFINITIONS}) +#add_definitions(${QT_DEFINITIONS}) +add_definitions("${QT_DEFINITIONS} -DLIBAIRSPY_DYN_RATES") add_definitions(-DQT_PLUGIN) add_definitions(-DQT_SHARED) diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 2cf2e838d..bf7189371 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -37,6 +37,9 @@ AirspyGui::AirspyGui(PluginAPI* pluginAPI, QWidget* parent) : displaySettings(); m_sampleSource = new AirspyInput(); + m_rates = ((AirspyInput*) m_sampleSource)->getSampleRates(); + displaySampleRates(); + connect(m_sampleSource->getOutputMessageQueueToGUI(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); DSPEngine::instance()->setSource(m_sampleSource); } @@ -94,7 +97,9 @@ bool AirspyGui::handleMessage(const Message& message) { if (AirspyInput::MsgReportAirspy::match(message)) { - displaySettings(); + qDebug() << "AirspyGui::handleMessage: MsgReportAirspy"; + m_rates = ((AirspyInput::MsgReportAirspy&) message).getSampleRates(); + displaySampleRates(); return true; } else @@ -103,6 +108,21 @@ bool AirspyGui::handleMessage(const Message& message) } } +void AirspyGui::handleSourceMessages() +{ + Message* message; + + while ((message = m_sampleSource->getOutputMessageQueueToGUI()->pop()) != 0) + { + qDebug("AirspyGui::HandleSourceMessages: message: %s", message->getIdentifier()); + + if (handleMessage(*message)) + { + delete message; + } + } +} + void AirspyGui::displaySettings() { ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); @@ -129,6 +149,33 @@ void AirspyGui::displaySettings() ui->vga->setValue(m_settings.m_vgaGain); } +void AirspyGui::displaySampleRates() +{ + int savedIndex = m_settings.m_devSampleRateIndex; + ui->sampleRate->blockSignals(true); + + if (m_rates.size() > 0) + { + ui->sampleRate->clear(); + + for (int i = 0; i < m_rates.size(); i++) + { + ui->sampleRate->addItem(QString("%1M").arg(QString::number(m_rates[i]/1000000.0, 'f', 3))); + } + } + + ui->sampleRate->blockSignals(false); + + if (savedIndex < m_rates.size()) + { + ui->sampleRate->setCurrentIndex(savedIndex); + } + else + { + ui->sampleRate->setCurrentIndex((int) m_rates.size()-1); + } +} + void AirspyGui::sendSettings() { if(!m_updateTimer.isActive()) @@ -188,7 +235,7 @@ void AirspyGui::on_lna_valueChanged(int value) if ((value < 0) || (value > 14)) return; - ui->lnaGainText->setText(tr("%1dB").arg(value*3)); + ui->lnaGainText->setText(tr("%1dB").arg(value)); m_settings.m_lnaGain = value; sendSettings(); } @@ -198,7 +245,7 @@ void AirspyGui::on_mix_valueChanged(int value) if ((value < 0) || (value > 15)) return; - ui->mixText->setText(tr("%1dB").arg(value*3)); + ui->mixText->setText(tr("%1dB").arg(value)); m_settings.m_lnaGain = value; sendSettings(); } @@ -208,7 +255,7 @@ void AirspyGui::on_vga_valueChanged(int value) if ((value < 0) || (value > 15)) return; - ui->vgaText->setText(tr("%1dB").arg(value*3)); + ui->vgaText->setText(tr("%1dB").arg(value)); m_settings.m_lnaGain = value; sendSettings(); } diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index 377d2d608..a65f3c9a9 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -60,6 +60,7 @@ private: SampleSource* m_sampleSource; void displaySettings(); + void displaySampleRates(); void sendSettings(); private slots: @@ -73,6 +74,7 @@ private slots: void on_mix_valueChanged(int value); void on_vga_valueChanged(int value); void updateHardware(); + void handleSourceMessages(); }; #endif // INCLUDE_AIRSPYGUI_H diff --git a/plugins/samplesource/airspy/airspygui.ui b/plugins/samplesource/airspy/airspygui.ui index 72e00564b..8a9071808 100644 --- a/plugins/samplesource/airspy/airspygui.ui +++ b/plugins/samplesource/airspy/airspygui.ui @@ -351,7 +351,7 @@ 1 - 15 + 5 Qt::Horizontal @@ -410,7 +410,7 @@ 1 - 9 + 5 Qt::Horizontal diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index 79b318473..541e8fec7 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -100,6 +100,8 @@ AirspyInput::AirspyInput() : m_airspyThread(0), m_deviceDescription("Airspy") { + m_sampleRates.push_back(10000000); + m_sampleRates.push_back(2500000); } AirspyInput::~AirspyInput() @@ -135,16 +137,13 @@ bool AirspyInput::start(int device) return false; } - if ((m_dev = open_airspy_from_sequence(device)) == 0) // TODO: fix; Open first available device as there is no proper handling for multiple devices + if ((m_dev = open_airspy_from_sequence(device)) == 0) { - qCritical("AirspyInput::start: could not open Airspy"); + qCritical("AirspyInput::start: could not open Airspy #%d", device); return false; } -#ifdef LIBAIRSPY_OLD - m_sampleRates.push_back(2500000); - m_sampleRates.push_back(10000000); -#else +#ifdef LIBAIRSPY_DYN_RATES uint32_t nbSampleRates; uint32_t *sampleRates; @@ -160,14 +159,20 @@ bool AirspyInput::start(int device) return false; } + m_sampleRates.clear(); + for (int i=0; ipush(message); + rc = (airspy_error) airspy_set_sample_type(m_dev, AIRSPY_SAMPLE_INT16_IQ); if (rc != AIRSPY_SUCCESS) @@ -176,31 +181,27 @@ bool AirspyInput::start(int device) return false; } - applySettings(m_settings, true); - - MsgReportAirspy *message = MsgReportAirspy::create(m_sampleRates); - getOutputMessageQueueToGUI()->push(message); - - if((m_airspyThread = new AirspyThread(m_dev, &m_sampleFifo)) == NULL) { - qFatal("out of memory"); - goto failed; + if((m_airspyThread = new AirspyThread(m_dev, &m_sampleFifo)) == 0) + { + qFatal("AirspyInput::start: out of memory"); + stop(); + return false; } m_airspyThread->startWork(); mutexLocker.unlock(); + applySettings(m_settings, true); + qDebug("AirspyInput::startInput: started"); return true; - -failed: - stop(); - return false; } void AirspyInput::stop() { + qDebug("AirspyInput::stop"); QMutexLocker mutexLocker(&m_mutex); if(m_airspyThread != 0) @@ -243,7 +244,9 @@ bool AirspyInput::handleMessage(const Message& message) MsgConfigureAirspy& conf = (MsgConfigureAirspy&) message; qDebug() << "AirspyInput::handleMessage: MsgConfigureAirspy"; - if (!applySettings(conf.getSettings(), false)) + bool success = applySettings(conf.getSettings(), false); + + if (!success) { qDebug("Airspy config error"); } @@ -274,32 +277,29 @@ void AirspyInput::setCenterFrequency(quint64 freq_hz) bool AirspyInput::applySettings(const Settings& settings, bool force) { - bool forwardChange = false; - airspy_error rc; - qint64 deviceCenterFrequency = m_settings.m_centerFrequency; - qint64 f_img = deviceCenterFrequency; - quint32 devSampleRate = m_sampleRates[m_settings.m_devSampleRateIndex]; - QMutexLocker mutexLocker(&m_mutex); - qDebug() << "AirspyInput::applySettings: m_dev: " << m_dev; + bool forwardChange = false; + airspy_error rc; + + qDebug() << "AirspyInput::applySettings"; if ((m_settings.m_devSampleRateIndex != settings.m_devSampleRateIndex) || force) { + forwardChange = true; + if (settings.m_devSampleRateIndex < m_sampleRates.size()) { m_settings.m_devSampleRateIndex = settings.m_devSampleRateIndex; } else { - m_settings.m_devSampleRateIndex = m_sampleRates.size() - 1; + m_settings.m_devSampleRateIndex = m_sampleRates.size() - 1; } - forwardChange = true; - if (m_dev != 0) { - rc = (airspy_error) airspy_set_samplerate(m_dev, static_cast(m_sampleRates[m_settings.m_devSampleRateIndex])); + rc = (airspy_error) airspy_set_samplerate(m_dev, static_cast(m_settings.m_devSampleRateIndex)); if (rc != AIRSPY_SUCCESS) { @@ -336,6 +336,10 @@ bool AirspyInput::applySettings(const Settings& settings, bool force) } } + qint64 deviceCenterFrequency = m_settings.m_centerFrequency; + qint64 f_img = deviceCenterFrequency; + quint32 devSampleRate = m_sampleRates[m_settings.m_devSampleRateIndex]; + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { m_settings.m_centerFrequency = settings.m_centerFrequency; @@ -461,12 +465,16 @@ bool AirspyInput::applySettings(const Settings& settings, bool force) struct airspy_device *AirspyInput::open_airspy_from_sequence(int sequence) { - struct airspy_device *devinfo; - int rc; + airspy_read_partid_serialno_t read_partid_serialno; + struct airspy_device *devinfo, *retdev = 0; + uint32_t serial_msb = 0; + uint32_t serial_lsb = 0; + airspy_error rc; + int i; - for (int i=0; i < AIRSPY_MAX_DEVICE; i++) + for (int i = 0; i < AIRSPY_MAX_DEVICE; i++) { - rc = airspy_open(&devinfo); + rc = (airspy_error) airspy_open(&devinfo); if (rc == AIRSPY_SUCCESS) { @@ -474,10 +482,14 @@ struct airspy_device *AirspyInput::open_airspy_from_sequence(int sequence) { return devinfo; } + else + { + airspy_close(devinfo); + } } else { - break; // finished + break; } } diff --git a/plugins/samplesource/airspy/airspyinput.h b/plugins/samplesource/airspy/airspyinput.h index 863f28ebd..aa2639efd 100644 --- a/plugins/samplesource/airspy/airspyinput.h +++ b/plugins/samplesource/airspy/airspyinput.h @@ -98,6 +98,7 @@ public: virtual const QString& getDeviceDescription() const; virtual int getSampleRate() const; virtual quint64 getCenterFrequency() const; + const std::vector& getSampleRates() const { return m_sampleRates; } virtual bool handleMessage(const Message& message); diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index b0394e4c2..5651ffef8 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -53,8 +53,11 @@ PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() { SampleSourceDevices result; airspy_read_partid_serialno_t read_partid_serialno; - struct airspy_device *devinfo = 0; + struct airspy_device *devinfo; + uint32_t serial_msb = 0; + uint32_t serial_lsb = 0; airspy_error rc; + int i; rc = (airspy_error) airspy_init(); @@ -63,29 +66,40 @@ PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() qCritical("AirspyPlugin::enumSampleSources: failed to initiate Airspy library: %s", airspy_error_name(rc)); } - for (int i=0; i < AIRSPY_MAX_DEVICE; i++) + for (i=0; i < AIRSPY_MAX_DEVICE; i++) { rc = (airspy_error) airspy_open(&devinfo); if (rc == AIRSPY_SUCCESS) { + qDebug("AirspyPlugin::enumSampleSources: try to enumerate Airspy device #%d", i); + rc = (airspy_error) airspy_board_partid_serialno_read(devinfo, &read_partid_serialno); if (rc != AIRSPY_SUCCESS) { qDebug("AirspyPlugin::enumSampleSources: failed to read serial no: %s", airspy_error_name(rc)); + airspy_close(devinfo); continue; // next } - QString serial_str = QString::number(read_partid_serialno.serial_no[2], 16) + QString::number(read_partid_serialno.serial_no[3], 16); - uint64_t serial_num = (((uint64_t) read_partid_serialno.serial_no[2])<<32) + read_partid_serialno.serial_no[3]; - QString displayedName(QString("Airspy #%1 0x%2").arg(i+1).arg(serial_str)); - SimpleSerializer s(1); - s.writeS32(1, i); - s.writeString(2, serial_str); - s.writeU64(3, serial_num); + if ((read_partid_serialno.serial_no[2] != serial_msb) && (read_partid_serialno.serial_no[3] != serial_lsb)) + { + serial_msb = read_partid_serialno.serial_no[2]; + serial_lsb = read_partid_serialno.serial_no[3]; - result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.airspy", s.final())); + QString serial_str = QString::number(serial_msb, 16) + QString::number(serial_lsb, 16); + uint64_t serial_num = (((uint64_t) serial_msb)<<32) + serial_lsb; + QString displayedName(QString("Airspy #%1 0x%2").arg(i).arg(serial_str)); + SimpleSerializer s(1); + s.writeS32(1, i); + s.writeString(2, serial_str); + s.writeU64(3, serial_num); + result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.airspy", s.final())); + qDebug("AirspyPlugin::enumSampleSources: enumerated Airspy device #%d", i); + } + + airspy_close(devinfo); } else { @@ -94,7 +108,8 @@ PluginInterface::SampleSourceDevices AirspyPlugin::enumSampleSources() } } - airspy_exit(); + rc = (airspy_error) airspy_exit(); + qDebug("AirspyPlugin::enumSampleSources: airspy_exit: %s", airspy_error_name(rc)); return result; } diff --git a/plugins/samplesource/airspy/airspythread.cpp b/plugins/samplesource/airspy/airspythread.cpp index e837cce88..c01035ecf 100644 --- a/plugins/samplesource/airspy/airspythread.cpp +++ b/plugins/samplesource/airspy/airspythread.cpp @@ -52,6 +52,7 @@ void AirspyThread::startWork() void AirspyThread::stopWork() { + qDebug("AirspyThread::stopWork"); m_running = false; wait(); } @@ -92,6 +93,17 @@ void AirspyThread::run() } } + rc = (airspy_error) airspy_stop_rx(m_dev); + + if (rc == AIRSPY_SUCCESS) + { + qDebug("AirspyInput::run: stopped Airspy Rx"); + } + else + { + qDebug("AirspyInput::run: failed to stop Airspy Rx: %s", airspy_error_name(rc)); + } + m_running = false; } @@ -177,13 +189,13 @@ void AirspyThread::callback(const qint16* buf, qint32 len) } } - m_sampleFifo->write(m_convertBuffer.begin(), it); } int AirspyThread::rx_callback(airspy_transfer_t* transfer) { - qint32 bytes_to_write = transfer->sample_count * sizeof(qint16) * 2; + qint32 bytes_to_write = transfer->sample_count * sizeof(qint16); m_this->callback((qint16 *) transfer->samples, bytes_to_write); + return 0; } diff --git a/plugins/samplesource/airspy/airspythread.h b/plugins/samplesource/airspy/airspythread.h index 1e9a7b880..29097bf39 100644 --- a/plugins/samplesource/airspy/airspythread.h +++ b/plugins/samplesource/airspy/airspythread.h @@ -24,7 +24,7 @@ #include "dsp/samplefifo.h" #include "dsp/decimators.h" -#define AIRSPY_BLOCKSIZE (1<<14) +#define AIRSPY_BLOCKSIZE (1<<17) class AirspyThread : public QThread { Q_OBJECT From 6e24ed6c284b325df1b2431b9f035eeb43743d3a Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 11 Sep 2015 08:23:04 +0200 Subject: [PATCH 5/7] Support for Airspy: make LO ppm correction effective --- plugins/samplesource/airspy/airspyinput.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index 541e8fec7..291e4ea67 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -340,9 +340,11 @@ bool AirspyInput::applySettings(const Settings& settings, bool force) qint64 f_img = deviceCenterFrequency; quint32 devSampleRate = m_sampleRates[m_settings.m_devSampleRateIndex]; - if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) || + (m_settings.m_LOppmTenths != settings.m_LOppmTenths)) { m_settings.m_centerFrequency = settings.m_centerFrequency; + m_settings.m_LOppmTenths = settings.m_LOppmTenths; if ((m_settings.m_log2Decim == 0) || (m_settings.m_fcPos == FC_POS_CENTER)) { From 0b7a8c8cdb5dc04a30e9a54908be8ba18205d322 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 11 Sep 2015 08:43:59 +0200 Subject: [PATCH 6/7] Support for Airspy: allow decimation by 64 --- include-gpl/dsp/decimators.h | 155 ++++++++++++++++++- plugins/samplesource/airspy/airspygui.cpp | 2 +- plugins/samplesource/airspy/airspygui.ui | 2 +- plugins/samplesource/airspy/airspythread.cpp | 9 ++ 4 files changed, 160 insertions(+), 8 deletions(-) diff --git a/include-gpl/dsp/decimators.h b/include-gpl/dsp/decimators.h index dbd37d67f..2579dcb26 100644 --- a/include-gpl/dsp/decimators.h +++ b/include-gpl/dsp/decimators.h @@ -34,6 +34,8 @@ struct decimation_shifts static const uint post16 = 0; static const uint pre32 = 0; static const uint post32 = 0; + static const uint pre64 = 0; + static const uint post64 = 0; }; template<> @@ -50,6 +52,8 @@ struct decimation_shifts<16, 16> static const uint post16 = 4; static const uint pre32 = 0; static const uint post32 = 5; + static const uint pre64 = 0; + static const uint post64 = 6; }; template<> @@ -66,22 +70,26 @@ struct decimation_shifts<16, 12> static const uint post16 = 0; static const uint pre32 = 0; static const uint post32 = 1; + static const uint pre64 = 0; + static const uint post64 = 2; }; template<> struct decimation_shifts<16, 8> { - static const uint pre1 = 5; - static const uint pre2 = 4; + static const uint pre1 = 6; + static const uint pre2 = 5; static const uint post2 = 0; - static const uint pre4 = 3; + static const uint pre4 = 4; static const uint post4 = 0; - static const uint pre8 = 2; + static const uint pre8 = 3; static const uint post8 = 0; - static const uint pre16 = 1; + static const uint pre16 = 2; static const uint post16 = 0; - static const uint pre32 = 0; + static const uint pre32 = 1; static const uint post32 = 0; + static const uint pre64 = 0; + static const uint post64 = 0; }; template @@ -105,6 +113,9 @@ public: void decimate32_inf(SampleVector::iterator* it, const T* buf, qint32 len); void decimate32_sup(SampleVector::iterator* it, const T* buf, qint32 len); void decimate32_cen(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate64_inf(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate64_sup(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate64_cen(SampleVector::iterator* it, const T* buf, qint32 len); private: IntHalfbandFilter m_decimator2; // 1st stages @@ -112,6 +123,7 @@ private: IntHalfbandFilter m_decimator8; // 3rd stages IntHalfbandFilter m_decimator16; // 4th stages IntHalfbandFilter m_decimator32; // 5th stages + IntHalfbandFilter m_decimator64; // 6th stages }; template @@ -352,6 +364,57 @@ void Decimators::decimate32_cen(SampleVector::iterator* i } } + +template +void Decimators::decimate64_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + int pos = 0; + + while (pos < len) + { + qint32 x0 = buf[pos+0] << decimation_shifts::pre64; + qint32 y0 = buf[pos+1] << decimation_shifts::pre64; + pos += 2; + + if (m_decimator2.workDecimateCenter(&x0, &y0)) + { + qint32 x1 = x0; + qint32 y1 = y0; + + if (m_decimator4.workDecimateCenter(&x1, &y1)) + { + qint32 x2 = x1; + qint32 y2 = y1; + + if (m_decimator8.workDecimateCenter(&x2, &y2)) + { + qint32 x3 = x2; + qint32 y3 = y2; + + if (m_decimator16.workDecimateCenter(&x3, &y3)) + { + qint32 x4 = x3; + qint32 y4 = y3; + + if (m_decimator32.workDecimateCenter(&x4, &y4)) + { + qint32 x5 = x4; + qint32 y5 = y4; + + if (m_decimator64.workDecimateCenter(&x4, &y4)) + { + (**it).setReal(x5 >> decimation_shifts::post64); + (**it).setImag(y5 >> decimation_shifts::post64); + ++(*it); + } + } + } + } + } + } + } +} + template void Decimators::decimate4_inf(SampleVector::iterator* it, const T* buf, qint32 len) { @@ -556,4 +619,84 @@ void Decimators::decimate32_sup(SampleVector::iterator* i } } +template +void Decimators::decimate64_inf(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[16], yimag[16]; + + for (int pos = 0; pos < len - 127; ) + { + for (int i = 0; i < 16; i++) + { + xreal[i] = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << decimation_shifts::pre64; + yimag[i] = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << decimation_shifts::pre64; + pos += 16; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + m_decimator2.myDecimate(xreal[4], yimag[4], &xreal[5], &yimag[3]); + m_decimator2.myDecimate(xreal[6], yimag[6], &xreal[7], &yimag[7]); + m_decimator2.myDecimate(xreal[8], yimag[8], &xreal[9], &yimag[9]); + m_decimator2.myDecimate(xreal[10], yimag[10], &xreal[11], &yimag[11]); + m_decimator2.myDecimate(xreal[12], yimag[12], &xreal[13], &yimag[13]); + m_decimator2.myDecimate(xreal[14], yimag[14], &xreal[15], &yimag[15]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + m_decimator4.myDecimate(xreal[5], yimag[5], &xreal[7], &yimag[7]); + m_decimator4.myDecimate(xreal[9], yimag[9], &xreal[11], &yimag[11]); + m_decimator4.myDecimate(xreal[13], yimag[13], &xreal[15], &yimag[15]); + + m_decimator8.myDecimate(xreal[3], yimag[3], &xreal[7], &yimag[7]); + m_decimator8.myDecimate(xreal[11], yimag[11], &xreal[15], &yimag[15]); + + m_decimator16.myDecimate(xreal[7], yimag[7], &xreal[15], &yimag[15]); + + (**it).setReal(xreal[15] >> decimation_shifts::post64); + (**it).setImag(yimag[15] >> decimation_shifts::post64); + + ++(*it); + } +} + +template +void Decimators::decimate64_sup(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[16], yimag[16]; + + for (int pos = 0; pos < len - 127; ) + { + for (int i = 0; i < 16; i++) + { + xreal[i] = (buf[pos+1] - buf[pos+2] - buf[pos+5] + buf[pos+6]) << decimation_shifts::pre32; + yimag[i] = (buf[pos+4] + buf[pos+7] - buf[pos+0] - buf[pos+3]) << decimation_shifts::pre32; + pos += 16; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + m_decimator2.myDecimate(xreal[4], yimag[4], &xreal[5], &yimag[3]); + m_decimator2.myDecimate(xreal[6], yimag[6], &xreal[7], &yimag[7]); + m_decimator2.myDecimate(xreal[8], yimag[8], &xreal[9], &yimag[9]); + m_decimator2.myDecimate(xreal[10], yimag[10], &xreal[11], &yimag[11]); + m_decimator2.myDecimate(xreal[12], yimag[12], &xreal[13], &yimag[13]); + m_decimator2.myDecimate(xreal[14], yimag[14], &xreal[15], &yimag[15]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + m_decimator4.myDecimate(xreal[5], yimag[5], &xreal[7], &yimag[7]); + m_decimator4.myDecimate(xreal[9], yimag[9], &xreal[11], &yimag[11]); + m_decimator4.myDecimate(xreal[13], yimag[13], &xreal[15], &yimag[15]); + + m_decimator8.myDecimate(xreal[3], yimag[3], &xreal[7], &yimag[7]); + m_decimator8.myDecimate(xreal[11], yimag[11], &xreal[15], &yimag[15]); + + m_decimator16.myDecimate(xreal[7], yimag[7], &xreal[15], &yimag[15]); + + (**it).setReal(xreal[15] >> decimation_shifts::post64); + (**it).setImag(yimag[15] >> decimation_shifts::post64); + + ++(*it); + } +} + #endif /* INCLUDE_GPL_DSP_DECIMATORS_H_ */ diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index bf7189371..0c26854aa 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -209,7 +209,7 @@ void AirspyGui::on_biasT_stateChanged(int state) void AirspyGui::on_decim_valueChanged(int value) { - if ((value <0) || (value > 5)) + if ((value <0) || (value > 6)) return; ui->decimText->setText(tr("%1").arg(1< - 5 + 6 1 diff --git a/plugins/samplesource/airspy/airspythread.cpp b/plugins/samplesource/airspy/airspythread.cpp index c01035ecf..b516f5bcd 100644 --- a/plugins/samplesource/airspy/airspythread.cpp +++ b/plugins/samplesource/airspy/airspythread.cpp @@ -137,6 +137,9 @@ void AirspyThread::callback(const qint16* buf, qint32 len) case 5: m_decimators.decimate32_inf(&it, buf, len); break; + case 6: + m_decimators.decimate64_inf(&it, buf, len); + break; default: break; } @@ -160,6 +163,9 @@ void AirspyThread::callback(const qint16* buf, qint32 len) case 5: m_decimators.decimate32_sup(&it, buf, len); break; + case 6: + m_decimators.decimate64_sup(&it, buf, len); + break; default: break; } @@ -183,6 +189,9 @@ void AirspyThread::callback(const qint16* buf, qint32 len) case 5: m_decimators.decimate32_cen(&it, buf, len); break; + case 6: + m_decimators.decimate64_cen(&it, buf, len); + break; default: break; } From efda927b9320aceaa83b30809f087c4fbacfad5a Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 11 Sep 2015 09:12:56 +0200 Subject: [PATCH 7/7] Support for Airspy: corrected open device by sequence number --- plugins/samplesource/airspy/airspyinput.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index 291e4ea67..8a8c50436 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -484,10 +484,6 @@ struct airspy_device *AirspyInput::open_airspy_from_sequence(int sequence) { return devinfo; } - else - { - airspy_close(devinfo); - } } else {