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
+
+ 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