From 779f3fdb1692c50ec424b6119aaf0ad3aa89da9c Mon Sep 17 00:00:00 2001 From: Vort Date: Fri, 7 Jun 2019 10:33:33 +0300 Subject: [PATCH] Add basic support for KiwiSDR receivers --- CMakeLists.txt | 1 + plugins/samplesource/CMakeLists.txt | 1 + plugins/samplesource/kiwisdr/CMakeLists.txt | 56 +++ plugins/samplesource/kiwisdr/kiwisdrgui.cpp | 339 +++++++++++++++++ plugins/samplesource/kiwisdr/kiwisdrgui.h | 93 +++++ plugins/samplesource/kiwisdr/kiwisdrgui.ui | 347 +++++++++++++++++ plugins/samplesource/kiwisdr/kiwisdrinput.cpp | 348 ++++++++++++++++++ plugins/samplesource/kiwisdr/kiwisdrinput.h | 189 ++++++++++ .../samplesource/kiwisdr/kiwisdrplugin.cpp | 111 ++++++ plugins/samplesource/kiwisdr/kiwisdrplugin.h | 53 +++ .../samplesource/kiwisdr/kiwisdrsettings.cpp | 103 ++++++ .../samplesource/kiwisdr/kiwisdrsettings.h | 43 +++ .../samplesource/kiwisdr/kiwisdrworker.cpp | 147 ++++++++ plugins/samplesource/kiwisdr/kiwisdrworker.h | 64 ++++ 14 files changed, 1895 insertions(+) create mode 100644 plugins/samplesource/kiwisdr/CMakeLists.txt create mode 100644 plugins/samplesource/kiwisdr/kiwisdrgui.cpp create mode 100644 plugins/samplesource/kiwisdr/kiwisdrgui.h create mode 100644 plugins/samplesource/kiwisdr/kiwisdrgui.ui create mode 100644 plugins/samplesource/kiwisdr/kiwisdrinput.cpp create mode 100644 plugins/samplesource/kiwisdr/kiwisdrinput.h create mode 100644 plugins/samplesource/kiwisdr/kiwisdrplugin.cpp create mode 100644 plugins/samplesource/kiwisdr/kiwisdrplugin.h create mode 100644 plugins/samplesource/kiwisdr/kiwisdrsettings.cpp create mode 100644 plugins/samplesource/kiwisdr/kiwisdrsettings.h create mode 100644 plugins/samplesource/kiwisdr/kiwisdrworker.cpp create mode 100644 plugins/samplesource/kiwisdr/kiwisdrworker.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 459242878..e3ee1dd5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -291,6 +291,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(_required_qt_version "5.6.0") find_package(Qt5 COMPONENTS Core REQUIRED) find_package(Qt5 COMPONENTS Widgets REQUIRED) +find_package(Qt5 COMPONENTS WebSockets REQUIRED) find_package(Qt5 COMPONENTS Multimedia REQUIRED) find_package(Qt5 COMPONENTS MultimediaWidgets REQUIRED) diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index 4793a9fce..8649462b7 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -58,3 +58,4 @@ if(ENABLE_SOAPYSDR AND SOAPYSDR_FOUND) add_subdirectory(soapysdrinput) endif() +add_subdirectory(kiwisdr) \ No newline at end of file diff --git a/plugins/samplesource/kiwisdr/CMakeLists.txt b/plugins/samplesource/kiwisdr/CMakeLists.txt new file mode 100644 index 000000000..49b34c3f4 --- /dev/null +++ b/plugins/samplesource/kiwisdr/CMakeLists.txt @@ -0,0 +1,56 @@ +project(kiwisdr) + +set(kiwisdr_SOURCES + kiwisdrinput.cpp + kiwisdrplugin.cpp + kiwisdrworker.cpp + kiwisdrsettings.cpp +) + +set(kiwisdr_HEADERS + kiwisdrinput.h + kiwisdrplugin.h + kiwisdrworker.h + kiwisdrsettings.h +) + +include_directories( + ${Boost_INCLUDE_DIRS} +) + +if(NOT SERVER_MODE) + set(kiwisdr_SOURCES + ${kiwisdr_SOURCES} + kiwisdrgui.cpp + + kiwisdrgui.ui + ) + set(kiwisdr_HEADERS + ${kiwisdr_HEADERS} + kiwisdrgui.h + ) + + set(TARGET_NAME inputkiwisdr) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME inputkiwisdrsrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${kiwisdr_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + Qt5::WebSockets + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) \ No newline at end of file diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp new file mode 100644 index 000000000..1f1e2f917 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp @@ -0,0 +1,339 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include + +#include "ui_kiwisdrgui.h" +#include "plugin/pluginapi.h" +#include "gui/colormapper.h" +#include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "util/db.h" + +#include "mainwindow.h" + +#include "kiwisdrgui.h" +#include "device/deviceapi.h" +#include "device/deviceuiset.h" + +KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : + QWidget(parent), + ui(new Ui::KiwiSDRGui), + m_deviceUISet(deviceUISet), + m_settings(), + m_doApplySettings(true), + m_forceSettings(true), + m_sampleSource(0), + m_tickCount(0), + m_lastEngineState(DeviceAPI::StNotStarted) +{ + qDebug("KiwiSDRGui::KiwiSDRGui"); + m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource(); + + m_statusTooltips.push_back("Idle"); + m_statusTooltips.push_back("Connecting..."); + m_statusTooltips.push_back("Connected"); + m_statusTooltips.push_back("Error"); + + m_statusColors.push_back("gray"); + m_statusColors.push_back("rgb(232, 212, 35)"); + m_statusColors.push_back("rgb(35, 138, 35)"); + m_statusColors.push_back("rgb(232, 85, 85)"); + + ui->setupUi(this); + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->centerFrequency->setValueRange(7, 0, 9999999); + + displaySettings(); + + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); + m_statusTimer.start(500); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); + + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); +} + +KiwiSDRGui::~KiwiSDRGui() +{ + delete ui; +} + +void KiwiSDRGui::destroy() +{ + delete this; +} + +void KiwiSDRGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString KiwiSDRGui::getName() const +{ + return objectName(); +} + +void KiwiSDRGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 KiwiSDRGui::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +void KiwiSDRGui::setCenterFrequency(qint64 centerFrequency) +{ + m_settings.m_centerFrequency = centerFrequency; + displaySettings(); + sendSettings(); +} + +QByteArray KiwiSDRGui::serialize() const +{ + return m_settings.serialize(); +} + +bool KiwiSDRGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + m_forceSettings = true; + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +void KiwiSDRGui::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + KiwiSDRInput::MsgStartStop *message = KiwiSDRInput::MsgStartStop::create(checked); + m_sampleSource->getInputMessageQueue()->push(message); + } +} + +void KiwiSDRGui::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void KiwiSDRGui::on_serverAddress_returnPressed() +{ + on_serverAddressApplyButton_clicked(); +} + +void KiwiSDRGui::on_serverAddressApplyButton_clicked() +{ + m_settings.m_serverAddress = ui->serverAddress->text(); + sendSettings(); +} + +void KiwiSDRGui::on_record_toggled(bool checked) +{ + if (checked) { + ui->record->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + KiwiSDRInput::MsgFileRecord* message = KiwiSDRInput::MsgFileRecord::create(checked); + m_sampleSource->getInputMessageQueue()->push(message); +} + +void KiwiSDRGui::on_agc_toggled(bool checked) +{ + m_settings.m_useAGC = checked; + sendSettings(); +} + +void KiwiSDRGui::on_gain_valueChanged(int value) +{ + m_settings.m_gain = value; + ui->gainText->setText(QString::number(m_settings.m_gain) + " dB"); + sendSettings(); +} + +void KiwiSDRGui::displaySettings() +{ + blockApplySettings(true); + + ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + ui->serverAddress->setText(m_settings.m_serverAddress); + ui->gain->setValue(m_settings.m_gain); + ui->gainText->setText(QString::number(m_settings.m_gain) + " dB"); + ui->agc->setChecked(m_settings.m_useAGC); + + blockApplySettings(false); +} + +void KiwiSDRGui::sendSettings() +{ + if (!m_updateTimer.isActive()) { + m_updateTimer.start(100); + } +} + +void KiwiSDRGui::updateHardware() +{ + if (m_doApplySettings) + { + KiwiSDRInput::MsgConfigureKiwiSDR* message = KiwiSDRInput::MsgConfigureKiwiSDR::create(m_settings, m_forceSettings); + m_sampleSource->getInputMessageQueue()->push(message); + m_forceSettings = false; + m_updateTimer.stop(); + } +} + +void KiwiSDRGui::updateStatus() +{ + int state = m_deviceUISet->m_deviceAPI->state(); + + if (m_lastEngineState != state) + { + switch (state) + { + case DeviceAPI::StNotStarted: + ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case DeviceAPI::StIdle: + ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case DeviceAPI::StRunning: + ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); + break; + case DeviceAPI::StError: + ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage()); + break; + default: + break; + } + + m_lastEngineState = state; + } +} + +bool KiwiSDRGui::handleMessage(const Message& message) +{ + if (KiwiSDRInput::MsgConfigureKiwiSDR::match(message)) + { + qDebug("KiwiSDRGui::handleMessage: MsgConfigureKiwiSDR"); + const KiwiSDRInput::MsgConfigureKiwiSDR& cfg = (KiwiSDRInput::MsgConfigureKiwiSDR&) message; + m_settings = cfg.getSettings(); + displaySettings(); + return true; + } + else if (KiwiSDRInput::MsgStartStop::match(message)) + { + qDebug("KiwiSDRGui::handleMessage: MsgStartStop"); + KiwiSDRInput::MsgStartStop& notif = (KiwiSDRInput::MsgStartStop&) message; + blockApplySettings(true); + ui->startStop->setChecked(notif.getStartStop()); + blockApplySettings(false); + + return true; + } + else if (KiwiSDRInput::MsgSetStatus::match(message)) + { + qDebug("KiwiSDRGui::handleMessage: MsgSetStatus"); + KiwiSDRInput::MsgSetStatus& notif = (KiwiSDRInput::MsgSetStatus&) message; + int status = notif.getStatus(); + ui->statusIndicator->setToolTip(m_statusTooltips[status]); + ui->statusIndicator->setStyleSheet("QLabel { background-color: " + + m_statusColors[status] + "; border-radius: 7px; }"); + return true; + } + else + { + return false; + } +} + +void KiwiSDRGui::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + if (DSPSignalNotification::match(*message)) + { + DSPSignalNotification* notif = (DSPSignalNotification*) message; + m_deviceSampleRate = notif->getSampleRate(); + m_deviceCenterFrequency = notif->getCenterFrequency(); + qDebug("KiwiSDRGui::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", + notif->getSampleRate(), + notif->getCenterFrequency()); + updateSampleRateAndFrequency(); + + delete message; + } + else + { + if (handleMessage(*message)) + { + delete message; + } + } + } +} + +void KiwiSDRGui::updateSampleRateAndFrequency() +{ + m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000)); +} + +void KiwiSDRGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} \ No newline at end of file diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.h b/plugins/samplesource/kiwisdr/kiwisdrgui.h new file mode 100644 index 000000000..2f40caff1 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.h @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// 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 _KIWISDR_KIWISDRGUI_H_ +#define _KIWISDR_KIWISDRGUI_H_ + +#include +#include +#include + +#include "util/messagequeue.h" + +#include "kiwisdrsettings.h" +#include "kiwisdrinput.h" + +class DeviceUISet; + +namespace Ui { + class KiwiSDRGui; +} + +class KiwiSDRGui : public QWidget, public PluginInstanceGUI { + Q_OBJECT + +public: + explicit KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent = 0); + virtual ~KiwiSDRGui(); + virtual void destroy(); + + void setName(const QString& name); + QString getName() const; + + void resetToDefaults(); + virtual qint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual bool handleMessage(const Message& message); + +private: + Ui::KiwiSDRGui* ui; + + DeviceUISet* m_deviceUISet; + KiwiSDRSettings m_settings; + QTimer m_updateTimer; + QTimer m_statusTimer; + bool m_doApplySettings; + bool m_forceSettings; + DeviceSampleSource* m_sampleSource; + std::size_t m_tickCount; + int m_deviceSampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + int m_lastEngineState; + MessageQueue m_inputMessageQueue; + std::vector m_statusColors; + std::vector m_statusTooltips; + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void displaySettings(); + void sendSettings(); + void updateSampleRateAndFrequency(); + +private slots: + void handleInputMessages(); + void on_startStop_toggled(bool checked); + void on_centerFrequency_changed(quint64 value); + void on_record_toggled(bool checked); + void on_gain_valueChanged(int value); + void on_agc_toggled(bool checked); + void on_serverAddress_returnPressed(); + void on_serverAddressApplyButton_clicked(); + void openDeviceSettingsDialog(const QPoint& p); + void updateStatus(); + void updateHardware(); +}; + +#endif // _KIWISDR_KIWISDRGUI_H_ diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.ui b/plugins/samplesource/kiwisdr/kiwisdrgui.ui new file mode 100644 index 000000000..112e643a3 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.ui @@ -0,0 +1,347 @@ + + + KiwiSDRGui + + + + 0 + 0 + 360 + 120 + + + + + 0 + 0 + + + + + 360 + 120 + + + + + Liberation Sans + 9 + 50 + false + false + + + + Test source + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 4 + + + + + + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Toggle record I/Q samples from device + + + + + + + :/record_off.png:/record_off.png + + + + + + + + + + + + 58 + 0 + + + + I/Q sample rate kS/s + + + 0000.00k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 20 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Tuner center frequency in kHz + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + + + Addr + + + + + + + Server address + + + 127.0.0.1:8073 + + + + + + + + 0 + 0 + + + + + 14 + 14 + + + + Idle + + + QLabel { background-color: gray; border-radius: 7px; } + + + + + + + + + + + 30 + 16777215 + + + + Set + + + + + + + + + + + Gain + + + + + + + Manual gain + + + 120 + + + 1 + + + 20 + + + Qt::Horizontal + + + QSlider::NoTicks + + + + + + + + 40 + 0 + + + + 20 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Automatic gain control + + + AGC + + + true + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + + + +
diff --git a/plugins/samplesource/kiwisdr/kiwisdrinput.cpp b/plugins/samplesource/kiwisdr/kiwisdrinput.cpp new file mode 100644 index 000000000..01abc2bca --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrinput.cpp @@ -0,0 +1,348 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +#include +#include + +#include "kiwisdrinput.h" +#include "device/deviceapi.h" +#include "kiwisdrworker.h" +#include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "dsp/filerecord.h" + +MESSAGE_CLASS_DEFINITION(KiwiSDRInput::MsgConfigureKiwiSDR, Message) +MESSAGE_CLASS_DEFINITION(KiwiSDRInput::MsgFileRecord, Message) +MESSAGE_CLASS_DEFINITION(KiwiSDRInput::MsgStartStop, Message) +MESSAGE_CLASS_DEFINITION(KiwiSDRInput::MsgSetStatus, Message) + + +KiwiSDRInput::KiwiSDRInput(DeviceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_kiwiSDRWorker(nullptr), + m_deviceDescription(), + m_running(false), + m_masterTimer(deviceAPI->getMasterTimer()) +{ + m_kiwiSDRWorkerThread.start(); + + m_fileSink = new FileRecord(); + m_deviceAPI->setNbSourceStreams(1); + m_deviceAPI->addAncillarySink(m_fileSink); + + if (!m_sampleFifo.setSize(getSampleRate() * 2)) { + qCritical("KiwiSDRInput::KiwiSDRInput: Could not allocate SampleFifo"); + } + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); +} + +KiwiSDRInput::~KiwiSDRInput() +{ + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + + m_kiwiSDRWorkerThread.quit(); + m_kiwiSDRWorkerThread.wait(); + + m_deviceAPI->removeAncillarySink(m_fileSink); + delete m_fileSink; +} + +void KiwiSDRInput::destroy() +{ + delete this; +} + +void KiwiSDRInput::init() +{ + applySettings(m_settings, true); +} + +bool KiwiSDRInput::start() +{ + QMutexLocker mutexLocker(&m_mutex); + + if (m_running) stop(); + + m_kiwiSDRWorker = new KiwiSDRWorker(&m_sampleFifo); + m_kiwiSDRWorker->moveToThread(&m_kiwiSDRWorkerThread); + + connect(this, &KiwiSDRInput::setWorkerCenterFrequency, m_kiwiSDRWorker, &KiwiSDRWorker::onCenterFrequencyChanged); + connect(this, &KiwiSDRInput::setWorkerServerAddress, m_kiwiSDRWorker, &KiwiSDRWorker::onServerAddressChanged); + connect(this, &KiwiSDRInput::setWorkerGain, m_kiwiSDRWorker, &KiwiSDRWorker::onGainChanged); + connect(m_kiwiSDRWorker, &KiwiSDRWorker::updateStatus, this, &KiwiSDRInput::setWorkerStatus); + + mutexLocker.unlock(); + + applySettings(m_settings, true); + m_running = true; + + return true; +} + +void KiwiSDRInput::stop() +{ + QMutexLocker mutexLocker(&m_mutex); + + setWorkerStatus(0); + + if (m_kiwiSDRWorker != 0) + { + m_kiwiSDRWorker->deleteLater(); + m_kiwiSDRWorker = 0; + } + + m_running = false; +} + +QByteArray KiwiSDRInput::serialize() const +{ + return m_settings.serialize(); +} + +bool KiwiSDRInput::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureKiwiSDR* message = MsgConfigureKiwiSDR::create(m_settings, true); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureKiwiSDR* messageToGUI = MsgConfigureKiwiSDR::create(m_settings, true); + m_guiMessageQueue->push(messageToGUI); + } + + return success; +} + +const QString& KiwiSDRInput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int KiwiSDRInput::getSampleRate() const +{ + return 12000; +} + +quint64 KiwiSDRInput::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +void KiwiSDRInput::setCenterFrequency(qint64 centerFrequency) +{ + KiwiSDRSettings settings = m_settings; + settings.m_centerFrequency = centerFrequency; + + MsgConfigureKiwiSDR* message = MsgConfigureKiwiSDR::create(settings, false); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureKiwiSDR* messageToGUI = MsgConfigureKiwiSDR::create(settings, false); + m_guiMessageQueue->push(messageToGUI); + } +} + +void KiwiSDRInput::setWorkerStatus(int status) +{ + if (m_guiMessageQueue) + m_guiMessageQueue->push(MsgSetStatus::create(status)); +} + +bool KiwiSDRInput::handleMessage(const Message& message) +{ + if (MsgConfigureKiwiSDR::match(message)) + { + MsgConfigureKiwiSDR& conf = (MsgConfigureKiwiSDR&) message; + qDebug() << "KiwiSDRInput::handleMessage: MsgConfigureKiwiSDR"; + + bool success = applySettings(conf.getSettings(), conf.getForce()); + + if (!success) + { + qDebug("KiwiSDRInput::handleMessage: config error"); + } + + return true; + } + else if (MsgFileRecord::match(message)) + { + MsgFileRecord& conf = (MsgFileRecord&) message; + qDebug() << "KiwiSDRInput::handleMessage: MsgFileRecord: " << conf.getStartStop(); + + if (conf.getStartStop()) + { + m_fileSink->genUniqueFileName(m_deviceAPI->getDeviceUID()); + m_fileSink->startRecording(); + } + else + { + m_fileSink->stopRecording(); + } + + return true; + } + else if (MsgStartStop::match(message)) + { + MsgStartStop& cmd = (MsgStartStop&) message; + qDebug() << "KiwiSDRInput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop"); + + if (cmd.getStartStop()) + { + if (m_deviceAPI->initDeviceEngine()) + { + m_deviceAPI->startDeviceEngine(); + } + } + else + { + m_deviceAPI->stopDeviceEngine(); + } + + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + + return true; + } + else + { + return false; + } +} + +bool KiwiSDRInput::applySettings(const KiwiSDRSettings& settings, bool force) +{ + QList reverseAPIKeys; + + if (m_settings.m_serverAddress != settings.m_serverAddress || force) + emit setWorkerServerAddress(settings.m_serverAddress); + + if (m_settings.m_gain != settings.m_gain || + m_settings.m_useAGC != settings.m_useAGC || force) + { + emit setWorkerGain(settings.m_gain, settings.m_useAGC); + } + + if (m_settings.m_centerFrequency != settings.m_centerFrequency || force) + { + reverseAPIKeys.append("centerFrequency"); + + emit setWorkerCenterFrequency(settings.m_centerFrequency); + + DSPSignalNotification *notif = new DSPSignalNotification( + getSampleRate(), settings.m_centerFrequency); + m_fileSink->handleMessage(*notif); // forward to file sink + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); + } + + if (settings.m_useReverseAPI) + { + qDebug("KiwiSDRInput::applySettings: call webapiReverseSendSettings"); + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + return true; +} + +int KiwiSDRInput::webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + return 404; +} + +int KiwiSDRInput::webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + return 404; +} + +int KiwiSDRInput::webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) +{ + return 404; +} + +int KiwiSDRInput::webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage) +{ + return 404; +} + +void KiwiSDRInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const KiwiSDRSettings& settings) +{ +} + +void KiwiSDRInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const KiwiSDRSettings& settings, bool force) +{ +} + +void KiwiSDRInput::webapiReverseSendStartStop(bool start) +{ +} + +void KiwiSDRInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "KiwiSDRInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("KiwiSDRInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/kiwisdr/kiwisdrinput.h b/plugins/samplesource/kiwisdr/kiwisdrinput.h new file mode 100644 index 000000000..6c1b17653 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrinput.h @@ -0,0 +1,189 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// 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 _KIWISDR_KIWISDRINPUT_H_ +#define _KIWISDR_KIWISDRINPUT_H_ + +#include +#include +#include +#include +#include + +#include +#include "kiwisdrsettings.h" + +class DeviceAPI; +class KiwiSDRWorker; +class FileRecord; +class QNetworkAccessManager; +class QNetworkReply; + +class KiwiSDRInput : public DeviceSampleSource { + Q_OBJECT +public: + class MsgConfigureKiwiSDR : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const KiwiSDRSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureKiwiSDR* create(const KiwiSDRSettings& settings, bool force) + { + return new MsgConfigureKiwiSDR(settings, force); + } + + private: + KiwiSDRSettings m_settings; + bool m_force; + + MsgConfigureKiwiSDR(const KiwiSDRSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgFileRecord : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgFileRecord* create(bool startStop) { + return new MsgFileRecord(startStop); + } + + protected: + bool m_startStop; + + MsgFileRecord(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgStartStop* create(bool startStop) { + return new MsgStartStop(startStop); + } + + protected: + bool m_startStop; + + MsgStartStop(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + class MsgSetStatus : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getStatus() const { return m_status; } + + static MsgSetStatus* create(int status) { + return new MsgSetStatus(status); + } + + protected: + int m_status; + + MsgSetStatus(int status) : + Message(), + m_status(status) + { } + }; + + KiwiSDRInput(DeviceAPI *deviceAPI); + virtual ~KiwiSDRInput(); + virtual void destroy(); + + virtual void init(); + virtual bool start(); + virtual void stop(); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } + virtual const QString& getDeviceDescription() const; + virtual int getSampleRate() const; + virtual void setSampleRate(int sampleRate) { (void) sampleRate; } + virtual quint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + + virtual bool handleMessage(const Message& message); + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage); + + virtual int webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + +private: + DeviceAPI *m_deviceAPI; + FileRecord *m_fileSink; //!< File sink to record device I/Q output + QMutex m_mutex; + KiwiSDRSettings m_settings; + KiwiSDRWorker* m_kiwiSDRWorker; + QThread m_kiwiSDRWorkerThread; + QString m_deviceDescription; + bool m_running; + const QTimer& m_masterTimer; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool applySettings(const KiwiSDRSettings& settings, bool force); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const KiwiSDRSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const KiwiSDRSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +signals: + void startWorker(); + void stopWorker(); + void setWorkerCenterFrequency(quint64 centerFrequency); + void setWorkerServerAddress(QString serverAddress); + void setWorkerGain(quint32 gain, bool useAGC); + +private slots: + void setWorkerStatus(int status); + void networkManagerFinished(QNetworkReply *reply); +}; + +#endif // _KIWISDR_KIWISDRINPUT_H_ diff --git a/plugins/samplesource/kiwisdr/kiwisdrplugin.cpp b/plugins/samplesource/kiwisdr/kiwisdrplugin.cpp new file mode 100644 index 000000000..06337e495 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrplugin.cpp @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// 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 "plugin/pluginapi.h" +#include "util/simpleserializer.h" + +#ifdef SERVER_MODE +#include "kiwisdrinput.h" +#else +#include "kiwisdrgui.h" +#endif +#include "kiwisdrplugin.h" + +const PluginDescriptor KiwiSDRPlugin::m_pluginDescriptor = { + QString("KiwiSDR input"), + QString("0.0.1"), + QString("(c) Vort (c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +const QString KiwiSDRPlugin::m_hardwareID = "KiwiSDR"; +const QString KiwiSDRPlugin::m_deviceTypeID = KIWISDR_DEVICE_TYPE_ID; + +KiwiSDRPlugin::KiwiSDRPlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& KiwiSDRPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void KiwiSDRPlugin::initPlugin(PluginAPI* pluginAPI) +{ + pluginAPI->registerSampleSource(m_deviceTypeID, this); +} + +PluginInterface::SamplingDevices KiwiSDRPlugin::enumSampleSources() +{ + SamplingDevices result; + + result.append(SamplingDevice( + "KiwiSDR", + m_hardwareID, + m_deviceTypeID, + QString::null, + 0, + PluginInterface::SamplingDevice::BuiltInDevice, + PluginInterface::SamplingDevice::StreamSingleRx, + 1, + 0)); + + return result; +} + +#ifdef SERVER_MODE +PluginInstanceGUI* KiwiSDRPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + return 0; +} +#else +PluginInstanceGUI* KiwiSDRPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + if(sourceId == m_deviceTypeID) { + KiwiSDRGui* gui = new KiwiSDRGui(deviceUISet); + *widget = gui; + return gui; + } else { + return 0; + } +} +#endif + +DeviceSampleSource *KiwiSDRPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) +{ + if (sourceId == m_deviceTypeID) + { + KiwiSDRInput* input = new KiwiSDRInput(deviceAPI); + return input; + } + else + { + return 0; + } +} + diff --git a/plugins/samplesource/kiwisdr/kiwisdrplugin.h b/plugins/samplesource/kiwisdr/kiwisdrplugin.h new file mode 100644 index 000000000..25fae1f05 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrplugin.h @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// 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 _KIWISDR_KIWISDRPLUGIN_H +#define _KIWISDR_KIWISDRPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class PluginAPI; + +#define KIWISDR_DEVICE_TYPE_ID "sdrangel.samplesource.kiwisdrsource" + +class KiwiSDRPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID KIWISDR_DEVICE_TYPE_ID) + +public: + explicit KiwiSDRPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual SamplingDevices enumSampleSources(); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; +}; + +#endif // _KIWISDR_KIWISDRPLUGIN_H diff --git a/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp b/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp new file mode 100644 index 000000000..329863b62 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// 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 "util/simpleserializer.h" +#include "kiwisdrsettings.h" + +KiwiSDRSettings::KiwiSDRSettings() +{ + resetToDefaults(); +} + +void KiwiSDRSettings::resetToDefaults() +{ + m_centerFrequency = 1450000; + + m_gain = 20; + m_useAGC = true; + + m_serverAddress = "127.0.0.1:8073"; + + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; +} + +QByteArray KiwiSDRSettings::serialize() const +{ + SimpleSerializer s(2); + + s.writeString(2, m_serverAddress); + s.writeU32(3, m_gain); + s.writeBool(4, m_useAGC); + + s.writeBool(100, m_useReverseAPI); + s.writeString(101, m_reverseAPIAddress); + s.writeU32(102, m_reverseAPIPort); + s.writeU32(103, m_reverseAPIDeviceIndex); + + return s.final(); +} + +bool KiwiSDRSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 2) + { + uint32_t utmp; + + d.readString(2, &m_serverAddress, "127.0.0.1:8073"); + d.readU32(3, &m_gain, 20); + d.readBool(4, &m_useAGC, true); + + d.readBool(100, &m_useReverseAPI, false); + d.readString(101, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(102, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } + else { + m_reverseAPIPort = 8888; + } + + d.readU32(103, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + + + + + + diff --git a/plugins/samplesource/kiwisdr/kiwisdrsettings.h b/plugins/samplesource/kiwisdr/kiwisdrsettings.h new file mode 100644 index 000000000..f2958f8b4 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrsettings.h @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// Copyright (C) 2018 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 // +// (at your option) any later version. // +// // +// 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 _KIWISDR_KIWISDRSETTINGS_H_ +#define _KIWISDR_KIWISDRSETTINGS_H_ + +#include +#include + +struct KiwiSDRSettings { + uint32_t m_gain; + bool m_useAGC; + + quint64 m_centerFrequency; + QString m_serverAddress; + + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + + KiwiSDRSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* _KIWISDR_KIWISDRSETTINGS_H_ */ diff --git a/plugins/samplesource/kiwisdr/kiwisdrworker.cpp b/plugins/samplesource/kiwisdr/kiwisdrworker.cpp new file mode 100644 index 000000000..8859b9888 --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrworker.cpp @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// // +// 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 // +// (at your option) any later version. // +// // +// 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 "kiwisdrworker.h" + +KiwiSDRWorker::KiwiSDRWorker(SampleSinkFifo* sampleFifo) + : QObject(), + m_timer(this), + m_sampleFifo(sampleFifo), + m_samplesBuf(), + m_centerFrequency(1450000), + m_gain(20), + m_useAGC(true) +{ + connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); + + m_webSocket.setParent(this); + connect(&m_webSocket, &QWebSocket::connected, + this, &KiwiSDRWorker::onConnected); + connect(&m_webSocket, &QWebSocket::binaryMessageReceived, + this, &KiwiSDRWorker::onBinaryMessageReceived); + connect(&m_webSocket, QOverload::of(&QWebSocket::error), + this, &KiwiSDRWorker::onSocketError); +} + +void KiwiSDRWorker::onConnected() +{ + m_webSocket.sendTextMessage("SET auth t=kiwi p=#"); +} + +void KiwiSDRWorker::onSocketError(QAbstractSocket::SocketError error) +{ + emit updateStatus(3); +} + +void KiwiSDRWorker::sendCenterFrequency() +{ + if (!m_webSocket.isValid()) + return; + + QString freq = QString::number(m_centerFrequency / 1000.0, 'f', 3); + QString msg = "SET mod=iq low_cut=-5980 high_cut=5980 freq=" + freq; + m_webSocket.sendTextMessage(msg); +} + +void KiwiSDRWorker::sendGain() +{ + if (!m_webSocket.isValid()) + return; + + QString msg("SET agc="); + msg.append(m_useAGC ? "1" : "0"); + msg.append(" hang=0 thresh=-130 slope=6 decay=1000 manGain="); + msg.append(QString::number(m_gain)); + m_webSocket.sendTextMessage(msg); +} + +void KiwiSDRWorker::onBinaryMessageReceived(const QByteArray &message) +{ + if (message[0] == 'M' && message[1] == 'S' && message[2] == 'G') + { + QStringList al = QString::fromUtf8(message).split(' '); + if (al[1] == "audio_init=0" && + al[2] == "audio_rate=12000") + { + m_webSocket.sendTextMessage("SET AR OK in=12000 out=48000"); + m_webSocket.sendTextMessage("SERVER DE CLIENT KiwiAngel SND"); + sendGain(); + sendCenterFrequency(); + m_timer.start(5000); + emit updateStatus(2); + } + } + else if (message[0] == 'S' && message[1] == 'N' && message[2] == 'D') + { + int dataOffset = 20; + int sampleCount = 512; + const int16_t* messageSamples = (const int16_t*)(message.constData() + dataOffset); + + m_samplesBuf.clear(); + for (int i = 0; i < sampleCount; i++) + { + m_samplesBuf.push_back(Sample( + boost::endian::endian_reverse(messageSamples[i * 2]) << (SDR_RX_SAMP_SZ - 16), + boost::endian::endian_reverse(messageSamples[i * 2 + 1]) << (SDR_RX_SAMP_SZ - 16) + )); + } + + m_sampleFifo->write(m_samplesBuf.begin(), m_samplesBuf.end()); + } +} + +void KiwiSDRWorker::onCenterFrequencyChanged(quint64 centerFrequency) +{ + if (m_centerFrequency == centerFrequency) + return; + + m_centerFrequency = centerFrequency; + sendCenterFrequency(); +} + +void KiwiSDRWorker::onGainChanged(quint32 gain, bool useAGC) +{ + if (m_gain == gain && m_useAGC == useAGC) + return; + + m_gain = gain; + m_useAGC = useAGC; + + sendGain(); +} + +void KiwiSDRWorker::onServerAddressChanged(QString serverAddress) +{ + if (m_serverAddress == serverAddress) + return; + m_serverAddress = serverAddress; + + emit updateStatus(1); + + QString url("ws://"); + url.append(m_serverAddress); + url.append("/kiwi/"); + url.append(QString::number(QDateTime::currentMSecsSinceEpoch())); + url.append("/SND"); + m_webSocket.open(QUrl(url)); +} + +void KiwiSDRWorker::tick() +{ + m_webSocket.sendTextMessage("SET keepalive"); +} \ No newline at end of file diff --git a/plugins/samplesource/kiwisdr/kiwisdrworker.h b/plugins/samplesource/kiwisdr/kiwisdrworker.h new file mode 100644 index 000000000..5b516a9dd --- /dev/null +++ b/plugins/samplesource/kiwisdr/kiwisdrworker.h @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Vort // +// // +// 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 // +// (at your option) any later version. // +// // +// 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 _KIWISDR_KIWISDRWORKER_H_ +#define _KIWISDR_KIWISDRWORKER_H_ + +#include +#include + +#include "dsp/samplesinkfifo.h" + +class KiwiSDRWorker : public QObject { + Q_OBJECT + +public: + KiwiSDRWorker(SampleSinkFifo* sampleFifo); + +private: + QTimer m_timer; + QWebSocket m_webSocket; + + SampleVector m_samplesBuf; + SampleSinkFifo* m_sampleFifo; + + QString m_serverAddress; + uint64_t m_centerFrequency; + + uint32_t m_gain; + bool m_useAGC; + + void sendCenterFrequency(); + void sendGain(); + +signals: + void updateStatus(int status); + +public slots: + void onCenterFrequencyChanged(quint64 centerFrequency); + void onServerAddressChanged(QString serverAddress); + void onGainChanged(quint32 gain, bool useAGC); + +private slots: + void onConnected(); + void onBinaryMessageReceived(const QByteArray &message); + void onSocketError(QAbstractSocket::SocketError error); + + void tick(); +}; + +#endif // _KIWISDR_KIWISDRWORKER_H_