diff --git a/Readme.md b/Readme.md index 65b0e549a..c277627d7 100644 --- a/Readme.md +++ b/Readme.md @@ -53,6 +53,8 @@ If you use your own location for libairspy install directory you need to specify 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. +Be also aware that the lower rates (2.5 MS/s or 5 MS/s with modified firmware) are affected by a noise artifact so 10 MS/s is preferable for weak signal work or instrumentation. A decimation by 64 was implemented to facilitate narrow band work at 10 MS/s input rate. +

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. @@ -67,6 +69,20 @@ Both Pro and Pro+ are supported with the plugins in fcdpro and fcdproplus respec The control interface is based on qthid and has been built in the software in the fcdhid library. You don't need anything else than libusb support. Library fcdlib is used to store the constants for each dongle type. +

HackRF

+ +HackRF is supported through the libhackrf library that should be installed in your system for proper build of the software and operation support. Add `libhackrf-dev` to the list of dependencies to install. + +If you use your own location for libhackrf install directory you need to specify library and include locations. Example with `/opt/install/libhackrf` with the following defines on `cmake` command line: + +`-DLIBHACKRF_LIBRARIES=/opt/install/libhackrf/lib/libhackrf.so -DLIBHACKRF_INCLUDE_DIR=/opt/install/libhackrf/include` + +

FunCube Dongle

+ +Both Pro and Pro+ are supported with the plugins in fcdpro and fcdproplus respectively. For the Pro+ the band filter selection is not effective as it is handled by the firmware using the center frequency. + +The control interface is based on qthid and has been built in the software in the fcdhid library. You don't need anything else than libusb support. Library fcdlib is used to store the constants for each dongle type. +

RTL-SDR

RTL-SDR based dongles are supported through the librtlsdr library that should be installed in your system for proper build of the software and operation support. Add `librtlsdr-dev` to the list of dependencies to install. @@ -163,10 +179,12 @@ Assuming Debian Jessie is used: - Many other little things...

To Do

+ - Review queue messaging to and from the source plugin to fix performance problems with rates > 4MS/s - Enhance presets management (Edit, Move, Import/Export from/to human readable format like JSON). Allow several sample source plugins to share the same presets + - Allow arbitrary sample rate for channelizers and demodulators (not multiple of 48 kHz). Prerequisite for polyphase channelizer + - Implement polyphase channelizer - Level calibration - Tx support with the BladeRF - Enhance WFM (stereo, RDS?) - Even more demods ... - - Support for Hack-RF diff --git a/cmake/Modules/FindLibHACKRF.cmake b/cmake/Modules/FindLibHACKRF.cmake new file mode 100644 index 000000000..7d0b3ddc7 --- /dev/null +++ b/cmake/Modules/FindLibHACKRF.cmake @@ -0,0 +1,28 @@ +if(NOT LIBHACKRF_FOUND) + + pkg_check_modules (LIBHACKRF_PKG libairspy) + find_path(LIBHACKRF_INCLUDE_DIR NAMES libhackrf/hackrf.h + PATHS + ${LIBHACKRF_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include + ) + + find_library(LIBHACKRF_LIBRARIES NAMES hackrf + PATHS + ${LIBHACKRF_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + + if(LIBHACKRF_INCLUDE_DIR AND LIBHACKRF_LIBRARIES) + set(LIBHACKRF_FOUND TRUE CACHE INTERNAL "libhackrf found") + message(STATUS "Found libhackrf: ${LIBHACKRF_INCLUDE_DIR}, ${LIBHACKRF_LIBRARIES}") + else(LIBHACKRF_INCLUDE_DIR AND LIBHACKRF_LIBRARIES) + set(LIBHACKRF_FOUND FALSE CACHE INTERNAL "libhackrf found") + message(STATUS "libhackrf not found.") + endif(LIBHACKRF_INCLUDE_DIR AND LIBHACKRF_LIBRARIES) + + mark_as_advanced(LIBHACKRF_INCLUDE_DIR LIBHACKRF_LIBRARIES) + +endif(NOT LIBHACKRF_FOUND) diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index ae8d2babb..032b0b2eb 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -37,4 +37,9 @@ if(LIBUSB_FOUND AND LIBAIRSPY_FOUND) add_subdirectory(airspy) endif(LIBUSB_FOUND AND LIBAIRSPY_FOUND) +find_package(LibHACKRF) +if(LIBUSB_FOUND AND LIBHACKRF_FOUND) + add_subdirectory(hackrf) +endif(LIBUSB_FOUND AND LIBHACKRF_FOUND) + add_subdirectory(filesource) diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index d935d7dfa..b7eeacace 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -168,6 +168,10 @@ bool AirspyInput::start(int device) } delete[] sampleRates; +#else + m_sampleRates.clear(); + m_sampleRates.push_back(10000000); + m_sampleRates.push_back(2500000); #endif MsgReportAirspy *message = MsgReportAirspy::create(m_sampleRates); diff --git a/plugins/samplesource/hackrf/CMakeLists.txt b/plugins/samplesource/hackrf/CMakeLists.txt new file mode 100644 index 000000000..971f029ff --- /dev/null +++ b/plugins/samplesource/hackrf/CMakeLists.txt @@ -0,0 +1,53 @@ +project(hackrf) + +set(hackrf_SOURCES + hackrfgui.cpp + hackrfinput.cpp + hackrfplugin.cpp + hackrfserializer.cpp + hackrfthread.cpp +) + +set(hackrf_HEADERS + hackrfgui.h + hackrfinput.h + hackrfplugin.h + hackrfserializer.h + hackrfthread.h +) + +set(hackrf_FORMS + hackrfgui.ui +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include-gpl + ${LIBHACKRF_INCLUDE_DIR} +) + +#include(${QT_USE_FILE}) +#add_definitions(${QT_DEFINITIONS}) +add_definitions("${QT_DEFINITIONS} -DLIBHACKRF_DYN_RATES") +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +#qt4_wrap_cpp(hackrf_HEADERS_MOC ${hackrf_HEADERS}) +qt5_wrap_ui(hackrf_FORMS_HEADERS ${hackrf_FORMS}) + +add_library(inputhackrf SHARED + ${hackrf_SOURCES} + ${hackrf_HEADERS_MOC} + ${hackrf_FORMS_HEADERS} +) + +target_link_libraries(inputhackrf + ${QT_LIBRARIES} + ${LIBHACKRF_LIBRARIES} + ${LIBUSB_LIBRARIES} + sdrbase +) + +qt5_use_modules(inputhackrf Core Widgets OpenGL Multimedia) diff --git a/plugins/samplesource/hackrf/hackrfgui.cpp b/plugins/samplesource/hackrf/hackrfgui.cpp new file mode 100644 index 000000000..099160e22 --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfgui.cpp @@ -0,0 +1,359 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "plugin/pluginapi.h" +#include "gui/colormapper.h" +#include "dsp/dspengine.h" +#include "hackrfgui.h" +#include "ui_hackrfgui.h" + +HackRFGui::HackRFGui(PluginAPI* pluginAPI, QWidget* parent) : + QWidget(parent), + ui(new Ui::HackRFGui), + m_pluginAPI(pluginAPI), + m_settings(), + m_sampleSource(NULL) +{ + ui->setupUi(this); + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold)); + ui->centerFrequency->setValueRange(7, 0U, 7250000U); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + displaySettings(); + + m_sampleSource = new HackRFInput(); + + displaySampleRates(); + displayBandwidths(); + displayImgRejFilters(); + + connect(m_sampleSource->getOutputMessageQueueToGUI(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); + DSPEngine::instance()->setSource(m_sampleSource); +} + +HackRFGui::~HackRFGui() +{ + delete m_sampleSource; // Valgrind memcheck + delete ui; +} + +void HackRFGui::destroy() +{ + delete this; +} + +void HackRFGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString HackRFGui::getName() const +{ + return objectName(); +} + +void HackRFGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 HackRFGui::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +QByteArray HackRFGui::serialize() const +{ + return m_settings.serialize(); +} + +bool HackRFGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool HackRFGui::handleMessage(const Message& message) +{ + return false; +} + +void HackRFGui::handleSourceMessages() +{ + Message* message; + + while ((message = m_sampleSource->getOutputMessageQueueToGUI()->pop()) != 0) + { + qDebug("AirspyGui::HandleSourceMessages: message: %s", message->getIdentifier()); + + if (handleMessage(*message)) + { + delete message; + } + } +} + +void HackRFGui::displaySettings() +{ + ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + + ui->LOppm->setValue(m_settings.m_LOppmTenths); + ui->LOppmText->setText(QString("%1").arg(QString::number(m_settings.m_LOppmTenths/10.0, 'f', 1))); + + 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)); + ui->lna->setValue(m_settings.m_lnaGain); + + ui->vgaText->setText(tr("%1dB").arg(m_settings.m_vgaGain)); + ui->vga->setValue(m_settings.m_vgaGain); +} + +void HackRFGui::displaySampleRates() +{ + int savedIndex = m_settings.m_devSampleRateIndex; + ui->sampleRate->blockSignals(true); + ui->sampleRate->clear(); + + for (int i = 0; i < HackRFSampleRates::m_nb_rates; i++) + { + ui->sampleRate->addItem(QString("%1M").arg(QString::number(HackRFSampleRates::m_rates[i]/1000.0, 'f', 1))); + } + + ui->sampleRate->blockSignals(false); + + if (savedIndex < HackRFSampleRates::m_nb_rates) + { + ui->sampleRate->setCurrentIndex(savedIndex); + } + else + { + ui->sampleRate->setCurrentIndex((int) HackRFSampleRates::m_nb_rates-1); + } +} + +void HackRFGui::displayBandwidths() +{ + int savedIndex = m_settings.m_bandwidthIndex; + ui->bbFilter->blockSignals(true); + ui->bbFilter->clear(); + + for (int i = 0; i < HackRFBandwidths::m_nb_bw; i++) + { + ui->bbFilter->addItem(QString("%1M").arg(QString::number(HackRFBandwidths::m_bw[i]/1000.0, 'f', 2))); + } + + ui->bbFilter->blockSignals(false); + + if (savedIndex < HackRFBandwidths::m_nb_bw) + { + ui->bbFilter->setCurrentIndex(savedIndex); + } + else + { + ui->bbFilter->setCurrentIndex((int) HackRFBandwidths::m_nb_bw-1); + } +} + +void HackRFGui::displayImgRejFilters() +{ + int savedIndex = m_settings.m_imjRejFilterIndex; + ui->rej->blockSignals(true); + ui->rej->clear(); + + for (int i = 0; i < HackRFImageRejectFilters::m_nb_rej; i++) + { + ui->rej->addItem(HackRFImageRejectFilters::m_rejName[i]); + } + + ui->rej->blockSignals(false); + + if (savedIndex < HackRFImageRejectFilters::m_nb_rej) + { + ui->rej->setCurrentIndex(savedIndex); + } + else + { + ui->rej->setCurrentIndex((int) HackRFImageRejectFilters::m_nb_rej-1); + } +} + +void HackRFGui::sendSettings() +{ + if(!m_updateTimer.isActive()) + m_updateTimer.start(100); +} + +void HackRFGui::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void HackRFGui::on_LOppm_valueChanged(int value) +{ + m_settings.m_LOppmTenths = value; + ui->LOppmText->setText(QString("%1").arg(QString::number(m_settings.m_LOppmTenths/10.0, 'f', 1))); + sendSettings(); +} + +void HackRFGui::on_sampleRate_currentIndexChanged(int index) +{ + m_settings.m_devSampleRateIndex = index; + sendSettings(); +} + +void HackRFGui::on_biasT_stateChanged(int state) +{ + m_settings.m_biasT = (state == Qt::Checked); + sendSettings(); +} + +void HackRFGui::on_decim_valueChanged(int value) +{ + if ((value <0) || (value > 6)) + return; + ui->decimText->setText(tr("%1").arg(1< 14)) + return; + + ui->lnaGainText->setText(tr("%1dB").arg(value)); + m_settings.m_lnaGain = value; + sendSettings(); +} + +void HackRFGui::on_vga_valueChanged(int value) +{ + if ((value < 0) || (value > 15)) + return; + + ui->vgaText->setText(tr("%1dB").arg(value)); + m_settings.m_vgaGain = value; + sendSettings(); +} + +void HackRFGui::updateHardware() +{ + qDebug() << "AirspyGui::updateHardware"; + HackRFInput::MsgConfigureHackRT* message = HackRFInput::MsgConfigureHackRT::create( m_settings); + m_sampleSource->getInputMessageQueue()->push(message); + m_updateTimer.stop(); +} + +unsigned int HackRFSampleRates::m_rates[] = {2400, 3200, 4800, 6400, 9680, 12800, 19200}; + +unsigned int HackRFSampleRates::getRate(unsigned int rate_index) +{ + if (rate_index < m_nb_rates) + { + return m_rates[rate_index]; + } + else + { + return m_rates[0]; + } +} + +unsigned int HackRFSampleRates::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 HackRFBandwidths::m_bw[] = {1750, 2500, 3500, 5000, 5500, 6000, 7000, 8000, 9000, 10000, 12000, 14000, 15000, 20000, 24000, 28000}; + +unsigned int HackRFBandwidths::getBandwidth(unsigned int bandwidth_index) +{ + if (bandwidth_index < m_nb_bw) + { + return m_bw[bandwidth_index]; + } + else + { + return m_bw[0]; + } +} + +unsigned int HackRFBandwidths::getBandwidthIndex(unsigned int bandwidth) +{ + for (unsigned int i=0; i < m_nb_bw; i++) + { + if (bandwidth == m_bw[i]) + { + return i; + } + } + + return 0; +} + +QString HackRFImageRejectFilters::m_rejName[] = {QString("None"), QString("Low"), QString("Hi")}; + +QString& HackRFImageRejectFilters::getImageRejectFilterName(unsigned int filter_index) +{ + if (filter_index < m_nb_rej) + { + return m_rejName[filter_index]; + } + else + { + return m_rejName[0]; + } +} diff --git a/plugins/samplesource/hackrf/hackrfgui.h b/plugins/samplesource/hackrf/hackrfgui.h new file mode 100644 index 000000000..8aeee0a10 --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfgui.h @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_HACKRFGUI_H +#define INCLUDE_HACKRFGUI_H + +#include + +#include "hackrfinput.h" +#include "plugin/plugingui.h" + +#define HACKRF_MAX_DEVICE (32) + +class PluginAPI; + +namespace Ui { + class HackRFGui; +} + +class HackRFGui : public QWidget, public PluginGUI { + Q_OBJECT + +public: + typedef enum + { + HACKRF_IMGREJ_BYPASS = 0, + HACKRF_IMGREJ_LOWPASS, + HACKRF_IMGREJ_HIGHPASS, + HACKRF_IMGREJ_NB + } HackRFImgRejValue; + + explicit HackRFGui(PluginAPI* pluginAPI, QWidget* parent = NULL); + virtual ~HackRFGui(); + 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::HackRFGui* ui; + + PluginAPI* m_pluginAPI; + HackRFInput::Settings m_settings; + QTimer m_updateTimer; + SampleSource* m_sampleSource; + + void displaySettings(); + void displaySampleRates(); + void displayBandwidths(); + void displayImgRejFilters(); + void sendSettings(); + +private slots: + void on_centerFrequency_changed(quint64 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_fcPos_currentIndexChanged(int index); + void on_lna_valueChanged(int value); + void on_vga_valueChanged(int value); + void updateHardware(); + void handleSourceMessages(); +}; + +class HackRFSampleRates { +public: + static unsigned int getRate(unsigned int rate_index); + static unsigned int getRateIndex(unsigned int rate); + static const unsigned int m_nb_rates = 7; + static unsigned int m_rates[m_nb_rates]; +}; + +class HackRFBandwidths { +public: + static unsigned int getBandwidth(unsigned int bandwidth_index); + static unsigned int getBandwidthIndex(unsigned int bandwidth); + static const unsigned int m_nb_bw = 16; + static unsigned int m_bw[m_nb_bw]; +}; + +class HackRFImageRejectFilters { +public: + static QString& getImageRejectFilterName(unsigned int filter_index); + static const unsigned int m_nb_rej = HackRFGui::HACKRF_IMGREJ_NB; + static QString m_rejName[m_nb_rej]; +}; + +#endif // INCLUDE_HACKRFGUI_H diff --git a/plugins/samplesource/hackrf/hackrfgui.ui b/plugins/samplesource/hackrf/hackrfgui.ui new file mode 100644 index 000000000..bb6bb039a --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfgui.ui @@ -0,0 +1,425 @@ + + + HackRFGui + + + + 0 + 0 + 198 + 214 + + + + + 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 + + + + + Bias T + + + + + + + + 0 + 0 + + + + Rate + + + + + + + Device sample rate + + + + + + + + + 3 + + + + + Dec. + + + + + + + 6 + + + 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 + + + + 0dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 3 + + + + + + + + Rej + + + + + + + BBF + + + + + + + + + + + + 3 + + + + + VGA + + + + + + + VGA gain dB + + + 15 + + + 1 + + + 1 + + + 5 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 0dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+
+ + +
diff --git a/plugins/samplesource/hackrf/hackrfinput.cpp b/plugins/samplesource/hackrf/hackrfinput.cpp new file mode 100644 index 000000000..8785e2278 --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfinput.cpp @@ -0,0 +1,496 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "util/simpleserializer.h" +#include "dsp/dspcommands.h" +#include "hackrfinput.h" + +#include "hackrfgui.h" +#include "hackrfserializer.h" +#include "hackrfthread.h" + +MESSAGE_CLASS_DEFINITION(HackRFInput::MsgConfigureHackRT, Message) +MESSAGE_CLASS_DEFINITION(HackRFInput::MsgReportHackRF, Message) + +HackRFInput::Settings::Settings() : + m_centerFrequency(435000*1000), + m_devSampleRateIndex(0), + m_LOppmTenths(0), + m_lnaGain(14), + m_mixerGain(15), + m_vgaGain(4), + m_log2Decim(0), + m_fcPos(FC_POS_CENTER), + m_biasT(false) +{ +} + +void HackRFInput::Settings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_devSampleRateIndex = 0; + m_LOppmTenths = 0; + m_lnaGain = 14; + m_mixerGain = 15; + m_vgaGain = 4; + m_log2Decim = 0; + m_fcPos = FC_POS_CENTER; + m_biasT = false; +} + +QByteArray HackRFInput::Settings::serialize() const +{ + HackRFSerializer::AirspyData data; + + data.m_data.m_frequency = m_centerFrequency; + data.m_LOppmTenths = m_LOppmTenths; + data.m_sampleRateIndex = m_devSampleRateIndex; + data.m_log2Decim = m_log2Decim; + data.m_fcPos = (qint32) m_fcPos; + data.m_lnaGain = m_lnaGain; + data.m_mixerGain = m_mixerGain; + data.m_vgaGain = m_vgaGain; + data.m_biasT = m_biasT; + + QByteArray byteArray; + + HackRFSerializer::writeSerializedData(data, byteArray); + + return byteArray; +} + +bool HackRFInput::Settings::deserialize(const QByteArray& serializedData) +{ + HackRFSerializer::AirspyData data; + + bool valid = HackRFSerializer::readSerializedData(serializedData, data); + + m_centerFrequency = data.m_data.m_frequency; + m_LOppmTenths = data.m_LOppmTenths; + m_devSampleRateIndex = data.m_sampleRateIndex; + m_log2Decim = data.m_log2Decim; + m_fcPos = (fcPos_t) 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; +} + +HackRFInput::HackRFInput() : + m_settings(), + m_dev(0), + m_airspyThread(0), + m_deviceDescription("Airspy") +{ + m_sampleRates.push_back(10000000); + m_sampleRates.push_back(2500000); +} + +HackRFInput::~HackRFInput() +{ + stop(); +} + +bool HackRFInput::init(const Message& cmd) +{ + return false; +} + +bool HackRFInput::start(int device) +{ + QMutexLocker mutexLocker(&m_mutex); + airspy_error rc; + + rc = (airspy_error) 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(1<<19)) + { + qCritical("AirspyInput::start: could not allocate SampleFifo"); + return false; + } + + if ((m_dev = open_airspy_from_sequence(device)) == 0) + { + qCritical("AirspyInput::start: could not open Airspy #%d", device); + return false; + } + +#ifdef LIBAIRSPY_DYN_RATES + 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); + + if (nbSampleRates == 0) + { + qCritical("AirspyInput::start: could not obtain Airspy sample rates"); + 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) + { + qCritical("AirspyInput::start: could not set sample type to INT16_IQ"); + return false; + } + + if((m_airspyThread = new HackRFThread(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; +} + +void HackRFInput::stop() +{ + qDebug("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& HackRFInput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int HackRFInput::getSampleRate() const +{ + 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 HackRFInput::applySettings(const Settings& settings, bool force) +{ + QMutexLocker mutexLocker(&m_mutex); + + 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; + } + + if (m_dev != 0) + { + rc = (airspy_error) airspy_set_samplerate(m_dev, static_cast(m_settings.m_devSampleRateIndex)); + + if (rc != AIRSPY_SUCCESS) + { + 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("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]); + } + } + } + + 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; + } + } + + qint64 deviceCenterFrequency = m_settings.m_centerFrequency; + qint64 f_img = deviceCenterFrequency; + quint32 devSampleRate = m_sampleRates[m_settings.m_devSampleRateIndex]; + + 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)) + { + 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); + } + + return true; +} + +struct airspy_device *HackRFInput::open_airspy_from_sequence(int sequence) +{ + 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++) + { + rc = (airspy_error) airspy_open(&devinfo); + + if (rc == AIRSPY_SUCCESS) + { + if (i == sequence) + { + return devinfo; + } + } + else + { + break; + } + } + + return 0; +} diff --git a/plugins/samplesource/hackrf/hackrfinput.h b/plugins/samplesource/hackrf/hackrfinput.h new file mode 100644 index 000000000..eda20989e --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfinput.h @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_HACKRFINPUT_H +#define INCLUDE_HACKRFINPUT_H + +#include "dsp/samplesource.h" +#include +#include + +class HackRFThread; + +class HackRFInput : 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; + quint32 m_devSampleRateIndex; + quint32 m_bandwidthIndex; + quint32 m_imjRejFilterIndex; + 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 MsgConfigureHackRT : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const Settings& getSettings() const { return m_settings; } + + static MsgConfigureHackRT* create(const Settings& settings) + { + return new MsgConfigureHackRT(settings); + } + + private: + Settings m_settings; + + MsgConfigureHackRT(const Settings& settings) : + Message(), + m_settings(settings) + { } + }; + + class MsgReportHackRF : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const std::vector& getSampleRates() const { return m_sampleRates; } + + static MsgReportHackRF* create(const std::vector& sampleRates) + { + return new MsgReportHackRF(sampleRates); + } + + protected: + std::vector m_sampleRates; + + MsgReportHackRF(const std::vector& sampleRates) : + Message(), + m_sampleRates(sampleRates) + { } + }; + + HackRFInput(); + virtual ~HackRFInput(); + + 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; + const std::vector& getSampleRates() const { return m_sampleRates; } + + virtual bool handleMessage(const Message& message); + +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; + HackRFThread* m_airspyThread; + QString m_deviceDescription; + std::vector m_sampleRates; +}; + +#endif // INCLUDE_HACKRFINPUT_H diff --git a/plugins/samplesource/hackrf/hackrfplugin.cpp b/plugins/samplesource/hackrf/hackrfplugin.cpp new file mode 100644 index 000000000..3be3ece81 --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfplugin.cpp @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "hackrfplugin.h" + +#include "hackrfgui.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" + +const PluginDescriptor HackRFPlugin::m_pluginDescriptor = { + QString("Airspy Input"), + QString("---"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +HackRFPlugin::HackRFPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& HackRFPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void HackRFPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.airspy", this); +} + +PluginInterface::SampleSourceDevices HackRFPlugin::enumSampleSources() +{ + SampleSourceDevices result; + airspy_read_partid_serialno_t read_partid_serialno; + struct airspy_device *devinfo; + uint32_t serial_msb = 0; + uint32_t serial_lsb = 0; + airspy_error rc; + int i; + + rc = (airspy_error) airspy_init(); + + if (rc != AIRSPY_SUCCESS) + { + qCritical("AirspyPlugin::enumSampleSources: failed to initiate Airspy library: %s", airspy_error_name(rc)); + } + + 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 + } + + 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]; + + 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 + { + qDebug("AirspyPlugin::enumSampleSources: enumerated %d Airspy devices %s", i, airspy_error_name(rc)); + break; // finished + } + } + + rc = (airspy_error) airspy_exit(); + qDebug("AirspyPlugin::enumSampleSources: airspy_exit: %s", airspy_error_name(rc)); + + return result; +} + +PluginGUI* HackRFPlugin::createSampleSourcePluginGUI(const QString& sourceName, const QByteArray& address) +{ + if (!m_pluginAPI) + { + return 0; + } + + if(sourceName == "org.osmocom.sdr.samplesource.airspy") + { + HackRFGui* gui = new HackRFGui(m_pluginAPI); + m_pluginAPI->setInputGUI(gui); + return gui; + } + else + { + return 0; + } +} diff --git a/plugins/samplesource/hackrf/hackrfplugin.h b/plugins/samplesource/hackrf/hackrfplugin.h new file mode 100644 index 000000000..9bf1b026d --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfplugin.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_HACKRFPLUGIN_H +#define INCLUDE_HACKRFPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class HackRFPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "org.osmocom.sdr.samplesource.hackrf") + +public: + explicit HackRFPlugin(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_HACKRFPLUGIN_H diff --git a/plugins/samplesource/hackrf/hackrfserializer.cpp b/plugins/samplesource/hackrf/hackrfserializer.cpp new file mode 100644 index 000000000..63fd3d0ea --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfserializer.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 "hackrfserializer.h" + +void HackRFSerializer::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.writeU32(3, data.m_sampleRateIndex); + s.writeU32(4, data.m_log2Decim); + s.writeS32(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 HackRFSerializer::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.readU32(3, &data.m_sampleRateIndex, 0); + d.readU32(4, &data.m_log2Decim, 0); + d.readS32(5, &data.m_fcPos, 0); + d.readU32(6, &data.m_lnaGain, 14); + d.readU32(7, &data.m_mixerGain, 15); + d.readU32(8, &data.m_vgaGain, 4); + d.readBool(9, &data.m_biasT, false); + + return SampleSourceSerializer::readSerializedData(sampleSourceSerialized, data.m_data); + } + else + { + setDefaults(data); + return false; + } +} + +void HackRFSerializer::setDefaults(AirspyData& data) +{ + data.m_LOppmTenths = 0; + data.m_sampleRateIndex = 0; + data.m_log2Decim = 0; + data.m_fcPos = 0; + data.m_lnaGain = 14; + data.m_mixerGain = 15; + data.m_vgaGain = 4; + data.m_biasT = false; +} diff --git a/plugins/samplesource/hackrf/hackrfserializer.h b/plugins/samplesource/hackrf/hackrfserializer.h new file mode 100644 index 000000000..6de5f6dc6 --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfserializer.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_HACKRF_HACKRFSERIALIZER_H_ +#define PLUGINS_SAMPLESOURCE_HACKRF_HACKRFSERIALIZER_H_ + +#include "util/samplesourceserializer.h" + +class HackRFSerializer +{ +public: + struct AirspyData + { + SampleSourceSerializer::Data m_data; + qint32 m_LOppmTenths; + quint32 m_sampleRateIndex; + quint32 m_log2Decim; + qint32 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_HACKRF_HACKRFSERIALIZER_H_ */ diff --git a/plugins/samplesource/hackrf/hackrfthread.cpp b/plugins/samplesource/hackrf/hackrfthread.cpp new file mode 100644 index 000000000..dd803935f --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfthread.cpp @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "dsp/samplefifo.h" +#include "hackrfthread.h" + +HackRFThread *HackRFThread::m_this = 0; + +HackRFThread::HackRFThread(struct airspy_device* 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) +{ + m_this = this; +} + +HackRFThread::~HackRFThread() +{ + stopWork(); + m_this = 0; +} + +void HackRFThread::startWork() +{ + m_startWaitMutex.lock(); + start(); + while(!m_running) + m_startWaiter.wait(&m_startWaitMutex, 100); + m_startWaitMutex.unlock(); +} + +void HackRFThread::stopWork() +{ + qDebug("AirspyThread::stopWork"); + m_running = false; + wait(); +} + +void HackRFThread::setSamplerate(uint32_t samplerate) +{ + m_samplerate = samplerate; +} + +void HackRFThread::setLog2Decimation(unsigned int log2_decim) +{ + m_log2Decim = log2_decim; +} + +void HackRFThread::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +void HackRFThread::run() +{ + airspy_error rc; + + m_running = true; + m_startWaiter.wakeAll(); + + rc = (airspy_error) airspy_start_rx(m_dev, rx_callback, NULL); + + 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); + } + } + + 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; +} + +// Decimate according to specified log2 (ex: log2=4 => decim=16) +void HackRFThread::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; + case 6: + m_decimators.decimate64_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; + case 6: + m_decimators.decimate64_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; + case 6: + m_decimators.decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + m_sampleFifo->write(m_convertBuffer.begin(), it); +} + + +int HackRFThread::rx_callback(airspy_transfer_t* transfer) +{ + 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/hackrf/hackrfthread.h b/plugins/samplesource/hackrf/hackrfthread.h new file mode 100644 index 000000000..4a4ff8b0c --- /dev/null +++ b/plugins/samplesource/hackrf/hackrfthread.h @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_HACKRFTHREAD_H +#define INCLUDE_HACKRFTHREAD_H + +#include +#include +#include +#include +#include "dsp/samplefifo.h" +#include "dsp/decimators.h" + +#define HACKRF_BLOCKSIZE (1<<17) + +class HackRFThread : public QThread { + Q_OBJECT + +public: + HackRFThread(struct airspy_device* dev, SampleFifo* sampleFifo, QObject* parent = NULL); + ~HackRFThread(); + + void startWork(); + void stopWork(); + void setSamplerate(uint32_t samplerate); + void setLog2Decimation(unsigned int log2_decim); + void setFcPos(int fcPos); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + + struct airspy_device* m_dev; + qint16 m_buf[2*HACKRF_BLOCKSIZE]; + SampleVector m_convertBuffer; + SampleFifo* m_sampleFifo; + + int m_samplerate; + unsigned int m_log2Decim; + int m_fcPos; + static HackRFThread *m_this; + + Decimators m_decimators; + + void run(); + void callback(const qint16* buf, qint32 len); + static int rx_callback(airspy_transfer_t* transfer); +}; + +#endif // INCLUDE_HACKRFTHREAD_H