diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index 98f52f9a3..106f7c903 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -86,4 +86,5 @@ if (BUILD_DEBIAN) endif (BUILD_DEBIAN) add_subdirectory(filesource) +add_subdirectory(testsource) diff --git a/plugins/samplesource/airspyhf/airspyhfsettings.h b/plugins/samplesource/airspyhf/airspyhfsettings.h index 573e6dd70..49d284896 100644 --- a/plugins/samplesource/airspyhf/airspyhfsettings.h +++ b/plugins/samplesource/airspyhf/airspyhfsettings.h @@ -14,8 +14,8 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#ifndef _AIRSPY_AIRSPYHFSETTINGS_H_ -#define _AIRSPY_AIRSPYHFSETTINGS_H_ +#ifndef _AIRSPYHF_AIRSPYHFSETTINGS_H_ +#define _AIRSPYHF_AIRSPYHFSETTINGS_H_ struct AirspyHFSettings { typedef enum { @@ -41,4 +41,4 @@ struct AirspyHFSettings { bool deserialize(const QByteArray& data); }; -#endif /* _AIRSPY_AIRSPYHFSETTINGS_H_ */ +#endif /* _AIRSPYHF_AIRSPYHFSETTINGS_H_ */ diff --git a/plugins/samplesource/testsource/CMakeLists.txt b/plugins/samplesource/testsource/CMakeLists.txt new file mode 100644 index 000000000..24fd797fc --- /dev/null +++ b/plugins/samplesource/testsource/CMakeLists.txt @@ -0,0 +1,54 @@ +project(testsource) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +set(testsource_SOURCES + testsourcegui.cpp + testsourceinput.cpp + testsourceplugin.cpp + testsourcethread.cpp + testsourcesettings.cpp +) + +set(testsource_HEADERS + testsourcegui.h + testsourceinput.h + testsourceplugin.h + testsourcethread.h + testsourcesettings.h +) + +set(testsource_FORMS + testsourcegui.ui +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +#include(${QT_USE_FILE}) +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +#qt4_wrap_cpp(testsource_HEADERS_MOC ${testsource_HEADERS}) +qt5_wrap_ui(testsource_FORMS_HEADERS ${testsource_FORMS}) + +add_library(inputtestsource SHARED + ${testsource_SOURCES} + ${testsource_HEADERS_MOC} + ${testsource_FORMS_HEADERS} +) + +target_link_libraries(inputtestsource + ${QT_LIBRARIES} + sdrbase + sdrgui + swagger +) + +qt5_use_modules(inputtestsource Core Widgets) + +install(TARGETS inputtestsource DESTINATION lib/plugins/samplesource) diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp new file mode 100644 index 000000000..7ba992312 --- /dev/null +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -0,0 +1,424 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 "ui_testsourcegui.h" +#include "plugin/pluginapi.h" +#include "gui/colormapper.h" +#include "gui/glspectrum.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "util/db.h" + +#include "mainwindow.h" + +#include "testsourcegui.h" +#include +#include "device/deviceuiset.h" + +TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : + QWidget(parent), + ui(new Ui::TestSourceGui), + m_deviceUISet(deviceUISet), + m_settings(), + m_doApplySettings(true), + m_forceSettings(true), + m_sampleSource(0), + m_sampleRate(0), + m_centerFrequency(0), + m_tickCount(0), + m_lastEngineState((DSPDeviceSourceEngine::State)-1) +{ + qDebug("TestSourceGui::TestSourceGui"); + m_sampleSource = m_deviceUISet->m_deviceSourceAPI->getSampleSource(); + + ui->setupUi(this); + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->centerFrequency->setValueRange(7, 0, 9999999); + ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); + ui->sampleRate->setValueRange(7, 48000, 9999999); + ui->frequencyShift->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->frequencyShift->setValueRange(false, 7, -9999999, 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); +} + +TestSourceGui::~TestSourceGui() +{ + delete ui; +} + +void TestSourceGui::destroy() +{ + delete this; +} + +void TestSourceGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString TestSourceGui::getName() const +{ + return objectName(); +} + +void TestSourceGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 TestSourceGui::getCenterFrequency() const +{ + return m_centerFrequency; +} + +void TestSourceGui::setCenterFrequency(qint64 centerFrequency) +{ + m_centerFrequency = centerFrequency; + displaySettings(); + sendSettings(); +} + +QByteArray TestSourceGui::serialize() const +{ + return m_settings.serialize(); +} + +bool TestSourceGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + m_forceSettings = true; + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +void TestSourceGui::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + TestSourceInput::MsgStartStop *message = TestSourceInput::MsgStartStop::create(checked); + m_sampleSource->getInputMessageQueue()->push(message); + } +} + +void TestSourceGui::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void TestSourceGui::on_frequencyShift_changed(qint64 value) +{ + m_settings.m_frequencyShift = value; + sendSettings(); +} + +void TestSourceGui::on_decimation_currentIndexChanged(int index) +{ + if ((index < 0) || (index > 6)) { + return; + } + + m_settings.m_log2Decim = index; + sendSettings(); +} + +void TestSourceGui::on_fcPos_currentIndexChanged(int index) +{ + if ((index < 0) || (index > 2)) { + return; + } + + m_settings.m_fcPos = (TestSourceSettings::fcPos_t) index; + sendSettings(); +} + +void TestSourceGui::on_sampleRate_changed(quint64 value) +{ + updateFrequencyShiftLimit(); + m_settings.m_frequencyShift = ui->frequencyShift->getValueNew(); + m_settings.m_sampleRate = value; + sendSettings(); +} + +void TestSourceGui::on_sampleSize_currentIndexChanged(int index) +{ + if ((index < 0) || (index > 2)) { + return; + } + + updateAmpCoarseLimit(); + updateAmpFineLimit(); + displayAmplitude(); + m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + m_settings.m_sampleSizeIndex = index; + sendSettings(); +} + +void TestSourceGui::on_amplitudeCoarse_valueChanged(int value __attribute__((unused))) +{ + updateAmpFineLimit(); + displayAmplitude(); + m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + sendSettings(); +} + +void TestSourceGui::on_amplitudeFine_valueChanged(int value __attribute__((unused))) +{ + displayAmplitude(); + m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + sendSettings(); +} + +void TestSourceGui::on_record_toggled(bool checked) +{ + if (checked) { + ui->record->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + TestSourceInput::MsgFileRecord* message = TestSourceInput::MsgFileRecord::create(checked); + m_sampleSource->getInputMessageQueue()->push(message); +} + +void TestSourceGui::displayAmplitude() +{ + int amplitudeInt = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + double power; + + switch (ui->sampleSize->currentIndex()) + { + case 0: // 8 bits: 128 + power = (double) amplitudeInt*amplitudeInt / (double) (1<<14); + break; + case 1: // 12 bits 2048 + power = (double) amplitudeInt*amplitudeInt / (double) (1<<22); + break; + case 2: // 16 bits 32768 + default: + power = (double) amplitudeInt*amplitudeInt / (double) (1<<30); + break; + } + + ui->amplitudeBits->setText(QString(tr("%1 b").arg(amplitudeInt))); + double powerDb = CalcDb::dbPower(power); + ui->power->setText(QString(tr("%1 dB").arg(QString::number(powerDb, 'f', 1)))); +} + +void TestSourceGui::updateAmpCoarseLimit() +{ + switch (ui->sampleSize->currentIndex()) + { + case 0: // 8 bits: 128 + ui->amplitudeCoarse->setMaximum(1); + break; + case 1: // 12 bits 2048 + ui->amplitudeCoarse->setMaximum(20); + break; + case 2: // 16 bits 32768 + default: + ui->amplitudeCoarse->setMaximum(327); + break; + } +} + +void TestSourceGui::updateAmpFineLimit() +{ + switch (ui->sampleSize->currentIndex()) + { + case 0: // 8 bits: 128 + if (ui->amplitudeCoarse->value() == 1) { + ui->amplitudeFine->setMaximum(27); + } else { + ui->amplitudeFine->setMaximum(99); + } + break; + case 1: // 12 bits 2048 + if (ui->amplitudeCoarse->value() == 20) { + ui->amplitudeFine->setMaximum(47); + } else { + ui->amplitudeFine->setMaximum(99); + } + break; + case 2: // 16 bits 32768 + default: + if (ui->amplitudeCoarse->value() == 327) { + ui->amplitudeFine->setMaximum(67); + } else { + ui->amplitudeFine->setMaximum(99); + } + break; + } +} + +void TestSourceGui::updateFrequencyShiftLimit() +{ + int sampleRate = ui->sampleRate->getValueNew(); + ui->frequencyShift->setValueRange(false, 7, -sampleRate, sampleRate); +} + +void TestSourceGui::displaySettings() +{ + blockApplySettings(true); + + ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + ui->decimation->setCurrentIndex(m_settings.m_log2Decim); + ui->fcPos->setCurrentIndex((int) m_settings.m_fcPos); + ui->sampleRate->setValue(m_settings.m_sampleRate); + updateFrequencyShiftLimit(); + ui->frequencyShift->setValue(m_settings.m_frequencyShift); + ui->sampleSize->setCurrentIndex(m_settings.m_sampleSizeIndex); + updateAmpCoarseLimit(); + int amplitudeBits = m_settings.m_amplitudeBits; + ui->amplitudeCoarse->setValue(amplitudeBits/100); + updateAmpFineLimit(); + ui->amplitudeFine->setValue(amplitudeBits%100); + displayAmplitude(); + + blockApplySettings(false); +} + +void TestSourceGui::sendSettings() +{ + if(!m_updateTimer.isActive()) { + m_updateTimer.start(100); + } +} + +void TestSourceGui::updateHardware() +{ + if (m_doApplySettings) + { + TestSourceInput::MsgConfigureTestSource* message = TestSourceInput::MsgConfigureTestSource::create(m_settings, m_forceSettings); + m_sampleSource->getInputMessageQueue()->push(message); + m_forceSettings = false; + m_updateTimer.stop(); + } +} + +void TestSourceGui::updateStatus() +{ + int state = m_deviceUISet->m_deviceSourceAPI->state(); + + if(m_lastEngineState != state) + { + switch(state) + { + case DSPDeviceSourceEngine::StNotStarted: + ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case DSPDeviceSourceEngine::StIdle: + ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case DSPDeviceSourceEngine::StRunning: + ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); + break; + case DSPDeviceSourceEngine::StError: + ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); + break; + default: + break; + } + + m_lastEngineState = state; + } +} + +bool TestSourceGui::handleMessage(const Message& message) +{ + if (TestSourceInput::MsgConfigureTestSource::match(message)) + { + qDebug("TestSourceGui::handleMessage: MsgConfigureTestSource"); + const TestSourceInput::MsgConfigureTestSource& cfg = (TestSourceInput::MsgConfigureTestSource&) message; + m_settings = cfg.getSettings(); + displaySettings(); + return true; + } + else if (TestSourceInput::MsgStartStop::match(message)) + { + qDebug("TestSourceGui::handleMessage: MsgStartStop"); + TestSourceInput::MsgStartStop& notif = (TestSourceInput::MsgStartStop&) message; + blockApplySettings(true); + ui->startStop->setChecked(notif.getStartStop()); + blockApplySettings(false); + + return true; + } + else + { + return false; + } +} + +void TestSourceGui::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("TestSourceGui::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", + notif->getSampleRate(), + notif->getCenterFrequency()); + updateSampleRateAndFrequency(); + + delete message; + } + else + { + if (handleMessage(*message)) + { + delete message; + } + } + } +} + +void TestSourceGui::updateSampleRateAndFrequency() +{ + m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000)); +} + + diff --git a/plugins/samplesource/testsource/testsourcegui.h b/plugins/samplesource/testsource/testsourcegui.h new file mode 100644 index 000000000..c71625261 --- /dev/null +++ b/plugins/samplesource/testsource/testsourcegui.h @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 _TESTSOURCE_TESTSOURCEGUI_H_ +#define _TESTSOURCE_TESTSOURCEGUI_H_ + +#include +#include +#include + +#include "util/messagequeue.h" + +#include "testsourcesettings.h" +#include "testsourceinput.h" + +class DeviceUISet; + +namespace Ui { + class TestSourceGui; +} + +class TestSourceGui : public QWidget, public PluginInstanceGUI { + Q_OBJECT + +public: + explicit TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent = 0); + virtual ~TestSourceGui(); + 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::TestSourceGui* ui; + + DeviceUISet* m_deviceUISet; + TestSourceSettings m_settings; + QTimer m_updateTimer; + QTimer m_statusTimer; + bool m_doApplySettings; + bool m_forceSettings; + DeviceSampleSource* m_sampleSource; + int m_sampleRate; + quint64 m_centerFrequency; + std::size_t m_tickCount; + int m_deviceSampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + int m_lastEngineState; + MessageQueue m_inputMessageQueue; + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void displaySettings(); + void sendSettings(); + void updateSampleRateAndFrequency(); + void displayAmplitude(); + void updateAmpCoarseLimit(); + void updateAmpFineLimit(); + void updateFrequencyShiftLimit(); + +private slots: + void handleInputMessages(); + void on_startStop_toggled(bool checked); + void on_centerFrequency_changed(quint64 value); + void on_frequencyShift_changed(qint64 value); + void on_decimation_currentIndexChanged(int index); + void on_fcPos_currentIndexChanged(int index); + void on_sampleRate_changed(quint64 value); + void on_sampleSize_currentIndexChanged(int index); + void on_amplitudeCoarse_valueChanged(int value); + void on_amplitudeFine_valueChanged(int value); + void on_record_toggled(bool checked); + void updateStatus(); + void updateHardware(); + void tick(); +}; + +#endif // _TESTSOURCE_TESTSOURCEGUI_H_ diff --git a/plugins/samplesource/testsource/testsourcegui.ui b/plugins/samplesource/testsource/testsourcegui.ui new file mode 100644 index 000000000..222cfa4e0 --- /dev/null +++ b/plugins/samplesource/testsource/testsourcegui.ui @@ -0,0 +1,614 @@ + + + TestSourceGui + + + + 0 + 0 + 360 + 220 + + + + + 0 + 0 + + + + + 360 + 220 + + + + + Sans Serif + 9 + + + + 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 + + + + + DejaVu Sans Mono + 20 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Tuner center frequency in kHz + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + + + Shift + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + false + + + + PointingHandCursor + + + Shift from center frequency + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Dec + + + + + + + + 45 + 16777215 + + + + Decimation factor + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + Fp + + + + + + + + 50 + 16777215 + + + + Relative postion of generator center frequency + + + 2 + + + + Inf + + + + + Sup + + + + + Cen + + + + + + + + + + Qt::Horizontal + + + + + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + 28 + 0 + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + + + + PointingHandCursor + + + Generator sample rate (S/s) + + + + + + + S/s + + + + + + + Qt::Vertical + + + + + + + size + + + + + + + + 45 + 16777215 + + + + Sample size + + + 0 + + + + 8 + + + + + 12 + + + + + 16 + + + + + + + + bits + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + + + Amp fine + + + + + + + + 0 + 0 + + + + Amp coarse + + + + + + + true + + + Amplitude coarse (x100) + + + 327 + + + 1 + + + Qt::Horizontal + + + + + + + Amplitude in bits + + + 32768 b + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 52 + 0 + + + + Power + + + -100 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Amplitude fine (x1) + + + 1 + + + Qt::Horizontal + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+
+ + + + +
diff --git a/plugins/samplesource/testsource/testsourceinput.cpp b/plugins/samplesource/testsource/testsourceinput.cpp new file mode 100644 index 000000000..085e7fd42 --- /dev/null +++ b/plugins/samplesource/testsource/testsourceinput.cpp @@ -0,0 +1,336 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 "SWGDeviceSettings.h" +#include "SWGDeviceState.h" + +#include "testsourceinput.h" +#include "device/devicesourceapi.h" +#include "testsourcethread.h" +#include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "dsp/filerecord.h" + +MESSAGE_CLASS_DEFINITION(TestSourceInput::MsgConfigureTestSource, Message) +MESSAGE_CLASS_DEFINITION(TestSourceInput::MsgFileRecord, Message) +MESSAGE_CLASS_DEFINITION(TestSourceInput::MsgStartStop, Message) + + +TestSourceInput::TestSourceInput(DeviceSourceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_testSourceThread(0), + m_deviceDescription(), + m_running(false), + m_masterTimer(deviceAPI->getMasterTimer()) +{ + char recFileNameCStr[30]; + sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceAPI->getDeviceUID()); + m_fileSink = new FileRecord(std::string(recFileNameCStr)); + m_deviceAPI->addSink(m_fileSink); + + if (!m_sampleFifo.setSize(96000 * 4)) { + qCritical("TestSourceInput::TestSourceInput: Could not allocate SampleFifo"); + } +} + +TestSourceInput::~TestSourceInput() +{ + if (m_running) { stop(); } + m_deviceAPI->removeSink(m_fileSink); + delete m_fileSink; +} + +void TestSourceInput::destroy() +{ + delete this; +} + +void TestSourceInput::init() +{ + applySettings(m_settings, true); +} + +bool TestSourceInput::start() +{ + QMutexLocker mutexLocker(&m_mutex); + + if (m_running) stop(); + + if ((m_testSourceThread = new TestSourceThread(&m_sampleFifo)) == 0) + { + qFatal("TestSourceInput::start: out of memory"); + stop(); + return false; + } + + m_testSourceThread->setSamplerate(m_settings.m_sampleRate); + m_testSourceThread->connectTimer(m_masterTimer); + m_testSourceThread->startWork(); + + mutexLocker.unlock(); + + applySettings(m_settings, true); + m_running = true; + + return true; +} + +void TestSourceInput::stop() +{ + QMutexLocker mutexLocker(&m_mutex); + + if (m_testSourceThread != 0) + { + m_testSourceThread->stopWork(); + delete m_testSourceThread; + m_testSourceThread = 0; + } + + m_running = false; +} + +QByteArray TestSourceInput::serialize() const +{ + return m_settings.serialize(); +} + +bool TestSourceInput::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureTestSource* message = MsgConfigureTestSource::create(m_settings, true); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureTestSource* messageToGUI = MsgConfigureTestSource::create(m_settings, true); + m_guiMessageQueue->push(messageToGUI); + } + + return success; +} + +const QString& TestSourceInput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int TestSourceInput::getSampleRate() const +{ + return m_settings.m_sampleRate; +} + +quint64 TestSourceInput::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +void TestSourceInput::setCenterFrequency(qint64 centerFrequency) +{ + TestSourceSettings settings = m_settings; + settings.m_centerFrequency = centerFrequency; + + MsgConfigureTestSource* message = MsgConfigureTestSource::create(settings, false); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureTestSource* messageToGUI = MsgConfigureTestSource::create(settings, false); + m_guiMessageQueue->push(messageToGUI); + } +} + +bool TestSourceInput::handleMessage(const Message& message) +{ + if (MsgConfigureTestSource::match(message)) + { + MsgConfigureTestSource& conf = (MsgConfigureTestSource&) message; + qDebug() << "TestSourceInput::handleMessage: MsgConfigureTestSource"; + + bool success = applySettings(conf.getSettings(), conf.getForce()); + + if (!success) + { + qDebug("TestSourceInput::handleMessage: config error"); + } + + return true; + } + else if (MsgFileRecord::match(message)) + { + MsgFileRecord& conf = (MsgFileRecord&) message; + qDebug() << "RTLSDRInput::handleMessage: MsgFileRecord: " << conf.getStartStop(); + + if (conf.getStartStop()) { + m_fileSink->startRecording(); + } else { + m_fileSink->stopRecording(); + } + + return true; + } + else if (MsgStartStop::match(message)) + { + MsgStartStop& cmd = (MsgStartStop&) message; + qDebug() << "RTLSDRInput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop"); + + if (cmd.getStartStop()) + { + if (m_deviceAPI->initAcquisition()) + { + m_deviceAPI->startAcquisition(); + DSPEngine::instance()->startAudioOutput(); + } + } + else + { + m_deviceAPI->stopAcquisition(); + DSPEngine::instance()->stopAudioOutput(); + } + + return true; + } + else + { + return false; + } +} + +bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool force) +{ + if ((m_settings.m_sampleRate != settings.m_sampleRate) || force) + { + if (m_testSourceThread != 0) + { + m_testSourceThread->setSamplerate(settings.m_sampleRate); + qDebug("TestSourceInput::applySettings: sample rate set to %d", settings.m_sampleRate); + } + } + + if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) + { + if (m_testSourceThread != 0) + { + m_testSourceThread->setLog2Decimation(settings.m_log2Decim); + qDebug() << "TestSourceInput::applySettings: set decimation to " << (1<setFcPos((int) settings.m_fcPos); + m_testSourceThread->setFrequencyShift(f_img + settings.m_frequencyShift); + qDebug() << "TestSourceInput::applySettings: center freq: " << settings.m_centerFrequency << " Hz" + << " device center freq: " << deviceCenterFrequency << " Hz" + << " device sample rate: " << devSampleRate << "Hz" + << " Actual sample rate: " << devSampleRate/(1<setAmplitudeBits(settings.m_amplitudeBits); + } + } + + if ((m_settings.m_sampleSizeIndex != settings.m_sampleSizeIndex) || force) + { + if (m_testSourceThread != 0) { + m_testSourceThread->setBitSize(settings.m_sampleSizeIndex); + } + } + + if ((m_settings.m_sampleRate != settings.m_sampleRate) + || (m_settings.m_centerFrequency != settings.m_centerFrequency) + || (m_settings.m_log2Decim != settings.m_log2Decim) + || (m_settings.m_fcPos != settings.m_fcPos) || force) + { + int sampleRate = settings.m_sampleRate/(1<handleMessage(*notif); // forward to file sink + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); + } + + m_settings = settings; + return true; +} + +int TestSourceInput::webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage __attribute__((unused))) +{ + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + return 200; +} + +int TestSourceInput::webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage __attribute__((unused))) +{ + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + MsgStartStop *message = MsgStartStop::create(run); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgStartStop *msgToGUI = MsgStartStop::create(run); + m_guiMessageQueue->push(msgToGUI); + } + + return 200; +} diff --git a/plugins/samplesource/testsource/testsourceinput.h b/plugins/samplesource/testsource/testsourceinput.h new file mode 100644 index 000000000..e69624604 --- /dev/null +++ b/plugins/samplesource/testsource/testsourceinput.h @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 _TESTSOURCE_TESTSOURCEINPUT_H_ +#define _TESTSOURCE_TESTSOURCEINPUT_H_ + +#include +#include +#include + +#include +#include "testsourcesettings.h" + +class DeviceSourceAPI; +class TestSourceThread; +class FileRecord; + +class TestSourceInput : public DeviceSampleSource { +public: + class MsgConfigureTestSource : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const TestSourceSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureTestSource* create(const TestSourceSettings& settings, bool force) + { + return new MsgConfigureTestSource(settings, force); + } + + private: + TestSourceSettings m_settings; + bool m_force; + + MsgConfigureTestSource(const TestSourceSettings& 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) + { } + }; + + TestSourceInput(DeviceSourceAPI *deviceAPI); + virtual ~TestSourceInput(); + 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 quint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + + virtual bool handleMessage(const Message& message); + + virtual int webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + +private: + DeviceSourceAPI *m_deviceAPI; + FileRecord *m_fileSink; //!< File sink to record device I/Q output + QMutex m_mutex; + TestSourceSettings m_settings; + TestSourceThread* m_testSourceThread; + QString m_deviceDescription; + bool m_running; + const QTimer& m_masterTimer; + + bool applySettings(const TestSourceSettings& settings, bool force); +}; + +#endif // _TESTSOURCE_TESTSOURCEINPUT_H_ diff --git a/plugins/samplesource/testsource/testsourceplugin.cpp b/plugins/samplesource/testsource/testsourceplugin.cpp new file mode 100644 index 000000000..13bcacc88 --- /dev/null +++ b/plugins/samplesource/testsource/testsourceplugin.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 // +// // +// 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" +#include + +#ifdef SERVER_MODE +#include "testsourceinput.h" +#else +#include "testsourcegui.h" +#endif +#include "testsourceplugin.h" + +const PluginDescriptor TestSourcePlugin::m_pluginDescriptor = { + QString("Test Source input"), + QString("3.11.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +const QString TestSourcePlugin::m_hardwareID = "TestSource"; +const QString TestSourcePlugin::m_deviceTypeID = TESTSOURCE_DEVICE_TYPE_ID; + +TestSourcePlugin::TestSourcePlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& TestSourcePlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void TestSourcePlugin::initPlugin(PluginAPI* pluginAPI) +{ + pluginAPI->registerSampleSource(m_deviceTypeID, this); +} + +PluginInterface::SamplingDevices TestSourcePlugin::enumSampleSources() +{ + SamplingDevices result; + + result.append(SamplingDevice( + "TestSource", + m_hardwareID, + m_deviceTypeID, + QString::null, + 0, + PluginInterface::SamplingDevice::BuiltInDevice, + true, + 1, + 0)); + + return result; +} + +#ifdef SERVER_MODE +PluginInstanceGUI* TestSourcePlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId __attribute((unused)), + QWidget **widget __attribute((unused)), + DeviceUISet *deviceUISet __attribute((unused))) +{ + return 0; +} +#else +PluginInstanceGUI* TestSourcePlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + if(sourceId == m_deviceTypeID) { + TestSourceGui* gui = new TestSourceGui(deviceUISet); + *widget = gui; + return gui; + } else { + return 0; + } +} +#endif + +DeviceSampleSource *TestSourcePlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI) +{ + if (sourceId == m_deviceTypeID) + { + TestSourceInput* input = new TestSourceInput(deviceAPI); + return input; + } + else + { + return 0; + } +} + diff --git a/plugins/samplesource/testsource/testsourceplugin.h b/plugins/samplesource/testsource/testsourceplugin.h new file mode 100644 index 000000000..ce780e61b --- /dev/null +++ b/plugins/samplesource/testsource/testsourceplugin.h @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 _TESTSOURCE_TESTSOURCEPLUGIN_H +#define _TESTSOURCE_TESTSOURCEPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class PluginAPI; + +#define TESTSOURCE_DEVICE_TYPE_ID "sdrangel.samplesource.testsource" + +class TestSourcePlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID TESTSOURCE_DEVICE_TYPE_ID) + +public: + explicit TestSourcePlugin(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* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; +}; + +#endif // _TESTSOURCE_TESTSOURCEPLUGIN_H diff --git a/plugins/samplesource/testsource/testsourcesettings.cpp b/plugins/samplesource/testsource/testsourcesettings.cpp new file mode 100644 index 000000000..7d68d0e3e --- /dev/null +++ b/plugins/samplesource/testsource/testsourcesettings.cpp @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 "util/simpleserializer.h" +#include "testsourcesettings.h" + +TestSourceSettings::TestSourceSettings() +{ + resetToDefaults(); +} + +void TestSourceSettings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_frequencyShift = 0; + m_sampleRate = 768*1000; + m_log2Decim = 4; + m_fcPos = FC_POS_CENTER; + m_sampleSizeIndex = 0; + m_amplitudeBits = 127; +} + +QByteArray TestSourceSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(2, m_frequencyShift); + s.writeU32(3, m_sampleRate); + s.writeU32(4, m_log2Decim); + s.writeS32(5, (int) m_fcPos); + s.writeU32(6, m_sampleSizeIndex); + s.writeS32(7, m_amplitudeBits); + + return s.final(); +} + +bool TestSourceSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + int intval; + + d.readS32(2, &m_frequencyShift, 0); + d.readU32(3, &m_sampleRate, 768*1000); + d.readU32(4, &m_log2Decim, 4); + d.readS32(5, &intval, 0); + m_fcPos = (fcPos_t) intval; + d.readU32(6, &m_sampleSizeIndex, 0); + d.readS32(7, &m_amplitudeBits, 128); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + + + + + + diff --git a/plugins/samplesource/testsource/testsourcesettings.h b/plugins/samplesource/testsource/testsourcesettings.h new file mode 100644 index 000000000..9fac36aa4 --- /dev/null +++ b/plugins/samplesource/testsource/testsourcesettings.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 _TESTSOURCE_TESTSOURCESETTINGS_H_ +#define _TESTSOURCE_TESTSOURCESETTINGS_H_ + +struct TestSourceSettings { + typedef enum { + FC_POS_INFRA = 0, + FC_POS_SUPRA, + FC_POS_CENTER + } fcPos_t; + + quint64 m_centerFrequency; + qint32 m_frequencyShift; + quint32 m_sampleRate; + quint32 m_log2Decim; + fcPos_t m_fcPos; + quint32 m_sampleSizeIndex; + qint32 m_amplitudeBits; + + TestSourceSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + + + + + +#endif /* _TESTSOURCE_TESTSOURCESETTINGS_H_ */ diff --git a/plugins/samplesource/testsource/testsourcethread.cpp b/plugins/samplesource/testsource/testsourcethread.cpp new file mode 100644 index 000000000..4e1b138a1 --- /dev/null +++ b/plugins/samplesource/testsource/testsourcethread.cpp @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 "testsourcethread.h" + +#include "dsp/samplesinkfifo.h" + +#define TESTSOURCE_BLOCKSIZE 16384 + +TestSourceThread::TestSourceThread(SampleSinkFifo* sampleFifo, QObject* parent) : + QThread(parent), + m_running(false), + m_buf(0), + m_bufsize(0), + m_chunksize(0), + m_convertBuffer(TESTSOURCE_BLOCKSIZE), + m_sampleFifo(sampleFifo), + m_samplerate(48000), + m_log2Decim(4), + m_fcPos(0), + m_bitSizeIndex(0), + m_bitShift(8), + m_amplitudeBits(127), + m_frequency(435*1000), + m_fcPosShift(0), + m_throttlems(TESTSOURCE_THROTTLE_MS), + m_throttleToggle(false) +{ + m_chunksize = (m_samplerate * 4 * m_throttlems) / 1000; + setBuffers(m_chunksize); +} + +TestSourceThread::~TestSourceThread() +{ + stopWork(); +} + +void TestSourceThread::startWork() +{ + qDebug("TestSourceThread::startWork"); + m_startWaitMutex.lock(); + m_elapsedTimer.start(); + start(); + while(!m_running) + m_startWaiter.wait(&m_startWaitMutex, 100); + m_startWaitMutex.unlock(); +} + +void TestSourceThread::stopWork() +{ + qDebug("TestSourceThread::stopWork"); + m_running = false; + wait(); +} + +void TestSourceThread::setSamplerate(int samplerate) +{ + m_samplerate = samplerate; + m_chunksize = (m_samplerate * 4 * m_throttlems) / 1000; + setBuffers(m_chunksize); +} + +void TestSourceThread::setLog2Decimation(unsigned int log2_decim) +{ + m_log2Decim = log2_decim; +} + +void TestSourceThread::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +void TestSourceThread::setBitSize(quint32 bitSizeIndex) +{ + switch (bitSizeIndex) + { + case 0: + m_bitShift = 7; + m_bitSizeIndex = 0; + break; + case 1: + m_bitShift = 11; + m_bitSizeIndex = 1; + break; + case 2: + default: + m_bitShift = 15; + m_bitSizeIndex = 2; + break; + } +} + +void TestSourceThread::setAmplitudeBits(int32_t amplitudeBits) +{ + m_amplitudeBits = amplitudeBits; +} + +void TestSourceThread::setFrequencyShift(int shift) +{ + m_nco.setFreq(shift, m_samplerate); +} + +void TestSourceThread::run() +{ + m_running = true; + m_startWaiter.wakeAll(); + + qDebug("TestSourceThread::run: starting"); + + while (m_running) // actual work is in the tick() function + { + sleep(1); + } + + qDebug("TestSourceThread::run: ending"); + + m_running = false; +} + +void TestSourceThread::setBuffers(quint32 chunksize) +{ + if (chunksize > m_bufsize) + { + m_bufsize = chunksize; + + if (m_buf == 0) + { + qDebug() << "TestSourceThread::setBuffer: Allocate buffer"; + m_buf = (quint8*) malloc(m_bufsize); + } + else + { + qDebug() << "TestSourceThread::setBuffer: Re-allocate buffer"; + quint8 *buf = m_buf; + m_buf = (quint8*) realloc((void*) m_buf, m_bufsize); + if (!m_buf) free(buf); + } + + m_convertBuffer.resize(chunksize/4); + + qDebug() << "TestSourceThread::setBuffer: size: " << m_bufsize + << " #samples: " << (m_bufsize/4); + } +} + +void TestSourceThread::generate(quint32 chunksize) +{ + quint32 n = chunksize / 2; + qint16 *buf = (qint16*) m_buf; + + for (unsigned int i = 0; i < n;) + { + Complex c = m_nco.nextIQ(); + buf[i] = c.real() * (1<write(m_convertBuffer.begin(), it); +} + +void TestSourceThread::connectTimer(const QTimer& timer) +{ + qDebug() << "TestSourceThread::connectTimer"; + connect(&timer, SIGNAL(timeout()), this, SLOT(tick())); +} + +void TestSourceThread::tick() +{ + if (m_running) + { + qint64 throttlems = m_elapsedTimer.restart(); + + if (throttlems != m_throttlems) + { + m_throttlems = throttlems; + m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000); + m_throttleToggle = !m_throttleToggle; + setBuffers(m_chunksize); + } + + generate(m_chunksize); + } +} + diff --git a/plugins/samplesource/testsource/testsourcethread.h b/plugins/samplesource/testsource/testsourcethread.h new file mode 100644 index 000000000..13bc17333 --- /dev/null +++ b/plugins/samplesource/testsource/testsourcethread.h @@ -0,0 +1,326 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 // +// // +// 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 _TESTSOURCE_TESTSOURCETHREAD_H_ +#define _TESTSOURCE_TESTSOURCETHREAD_H_ + +#include +#include +#include +#include +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "dsp/decimators.h" +#include "dsp/ncof.h" + +#define TESTSOURCE_THROTTLE_MS 50 + +class TestSourceThread : public QThread { + Q_OBJECT + +public: + TestSourceThread(SampleSinkFifo* sampleFifo, QObject* parent = 0); + ~TestSourceThread(); + + void startWork(); + void stopWork(); + void setSamplerate(int samplerate); + void setLog2Decimation(unsigned int log2_decim); + void setFcPos(int fcPos); + void setBitSize(uint32_t bitSizeIndex); + void setAmplitudeBits(int32_t amplitudeBits); + void setFrequencyShift(int shift); + + void connectTimer(const QTimer& timer); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + + quint8 *m_buf; + quint32 m_bufsize; + quint32 m_chunksize; + SampleVector m_convertBuffer; + SampleSinkFifo* m_sampleFifo; + NCOF m_nco; + + int m_samplerate; + unsigned int m_log2Decim; + int m_fcPos; + uint32_t m_bitSizeIndex; + uint32_t m_bitShift; + int32_t m_amplitudeBits; + + uint64_t m_frequency; + int m_fcPosShift; + + int m_throttlems; + QElapsedTimer m_elapsedTimer; + bool m_throttleToggle; + + Decimators m_decimators_8; + Decimators m_decimators_12; + Decimators m_decimators_16; + + void run(); + void callback(const quint8* buf, qint32 len); + void setBuffers(quint32 chunksize); + void generate(quint32 chunksize); + + // Decimate according to specified log2 (ex: log2=4 => decim=16) + inline void convert_8(SampleVector::iterator* it, const quint8* buf, qint32 len) + { + if (m_log2Decim == 0) { + m_decimators_8.decimate1(it, buf, len); + } else { + if (m_fcPos == 0) { // Infradyne + switch (m_log2Decim) { + case 1: + m_decimators_8.decimate2_inf(it, buf, len); + break; + case 2: + m_decimators_8.decimate4_inf(it, buf, len); + break; + case 3: + m_decimators_8.decimate8_inf(it, buf, len); + break; + case 4: + m_decimators_8.decimate16_inf(it, buf, len); + break; + case 5: + m_decimators_8.decimate32_inf(it, buf, len); + break; + case 6: + m_decimators_8.decimate64_inf(it, buf, len); + break; + default: + break; + } + } else if (m_fcPos == 1) {// Supradyne + switch (m_log2Decim) { + case 1: + m_decimators_8.decimate2_sup(it, buf, len); + break; + case 2: + m_decimators_8.decimate4_sup(it, buf, len); + break; + case 3: + m_decimators_8.decimate8_sup(it, buf, len); + break; + case 4: + m_decimators_8.decimate16_sup(it, buf, len); + break; + case 5: + m_decimators_8.decimate32_sup(it, buf, len); + break; + case 6: + m_decimators_8.decimate64_sup(it, buf, len); + break; + default: + break; + } + } else { // Centered + switch (m_log2Decim) { + case 1: + m_decimators_8.decimate2_cen(it, buf, len); + break; + case 2: + m_decimators_8.decimate4_cen(it, buf, len); + break; + case 3: + m_decimators_8.decimate8_cen(it, buf, len); + break; + case 4: + m_decimators_8.decimate16_cen(it, buf, len); + break; + case 5: + m_decimators_8.decimate32_cen(it, buf, len); + break; + case 6: + m_decimators_8.decimate64_cen(it, buf, len); + break; + default: + break; + } + } + } + } + + void convert_12(SampleVector::iterator* it, const quint8* buf, qint32 len) + { + if (m_log2Decim == 0) { + m_decimators_12.decimate1(it, buf, len); + } else { + if (m_fcPos == 0) { // Infradyne + switch (m_log2Decim) { + case 1: + m_decimators_12.decimate2_inf(it, buf, len); + break; + case 2: + m_decimators_12.decimate4_inf(it, buf, len); + break; + case 3: + m_decimators_12.decimate8_inf(it, buf, len); + break; + case 4: + m_decimators_12.decimate16_inf(it, buf, len); + break; + case 5: + m_decimators_12.decimate32_inf(it, buf, len); + break; + case 6: + m_decimators_12.decimate64_inf(it, buf, len); + break; + default: + break; + } + } else if (m_fcPos == 1) {// Supradyne + switch (m_log2Decim) { + case 1: + m_decimators_12.decimate2_sup(it, buf, len); + break; + case 2: + m_decimators_12.decimate4_sup(it, buf, len); + break; + case 3: + m_decimators_12.decimate8_sup(it, buf, len); + break; + case 4: + m_decimators_12.decimate16_sup(it, buf, len); + break; + case 5: + m_decimators_12.decimate32_sup(it, buf, len); + break; + case 6: + m_decimators_12.decimate64_sup(it, buf, len); + break; + default: + break; + } + } else { // Centered + switch (m_log2Decim) { + case 1: + m_decimators_12.decimate2_cen(it, buf, len); + break; + case 2: + m_decimators_12.decimate4_cen(it, buf, len); + break; + case 3: + m_decimators_12.decimate8_cen(it, buf, len); + break; + case 4: + m_decimators_12.decimate16_cen(it, buf, len); + break; + case 5: + m_decimators_12.decimate32_cen(it, buf, len); + break; + case 6: + m_decimators_12.decimate64_cen(it, buf, len); + break; + default: + break; + } + } + } + } + + void convert_16(SampleVector::iterator* it, const quint8* buf, qint32 len) + { + if (m_log2Decim == 0) { + m_decimators_16.decimate1(it, buf, len); + } else { + if (m_fcPos == 0) { // Infradyne + switch (m_log2Decim) { + case 1: + m_decimators_16.decimate2_inf(it, buf, len); + break; + case 2: + m_decimators_16.decimate4_inf(it, buf, len); + break; + case 3: + m_decimators_16.decimate8_inf(it, buf, len); + break; + case 4: + m_decimators_16.decimate16_inf(it, buf, len); + break; + case 5: + m_decimators_16.decimate32_inf(it, buf, len); + break; + case 6: + m_decimators_16.decimate64_inf(it, buf, len); + break; + default: + break; + } + } else if (m_fcPos == 1) {// Supradyne + switch (m_log2Decim) { + case 1: + m_decimators_16.decimate2_sup(it, buf, len); + break; + case 2: + m_decimators_16.decimate4_sup(it, buf, len); + break; + case 3: + m_decimators_16.decimate8_sup(it, buf, len); + break; + case 4: + m_decimators_16.decimate16_sup(it, buf, len); + break; + case 5: + m_decimators_16.decimate32_sup(it, buf, len); + break; + case 6: + m_decimators_16.decimate64_sup(it, buf, len); + break; + default: + break; + } + } else { // Centered + switch (m_log2Decim) { + case 1: + m_decimators_16.decimate2_cen(it, buf, len); + break; + case 2: + m_decimators_16.decimate4_cen(it, buf, len); + break; + case 3: + m_decimators_16.decimate8_cen(it, buf, len); + break; + case 4: + m_decimators_16.decimate16_cen(it, buf, len); + break; + case 5: + m_decimators_16.decimate32_cen(it, buf, len); + break; + case 6: + m_decimators_16.decimate64_cen(it, buf, len); + break; + default: + break; + } + } + } + } + + +private slots: + void tick(); +}; + +#endif // _TESTSOURCE_TESTSOURCETHREAD_H_