diff --git a/plugins/samplesink/CMakeLists.txt b/plugins/samplesink/CMakeLists.txt index e8875d6b1..c5e58c985 100644 --- a/plugins/samplesink/CMakeLists.txt +++ b/plugins/samplesink/CMakeLists.txt @@ -17,10 +17,18 @@ if(LIBUSB_FOUND AND LIMESUITE_FOUND) add_subdirectory(limesdroutput) endif(LIBUSB_FOUND AND LIMESUITE_FOUND) +find_package(CM256cc) +if(CM256CC_FOUND AND LIBNANOMSG_FOUND) + add_subdirectory(sdrdaemonsink) +endif(CM256CC_FOUND AND LIBNANOMSG_FOUND) + if (BUILD_DEBIAN) add_subdirectory(bladerfoutput) add_subdirectory(hackrfoutput) add_subdirectory(limesdroutput) + if (LIBNANOMSG_FOUND) + add_subdirectory(sdrdaemonsink) + endif (LIBNANOMSG_FOUND) endif (BUILD_DEBIAN) add_subdirectory(filesink) diff --git a/plugins/samplesink/sdrdaemonsink/CMakeLists.txt b/plugins/samplesink/sdrdaemonsink/CMakeLists.txt new file mode 100644 index 000000000..3223c8ea0 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/CMakeLists.txt @@ -0,0 +1,47 @@ +project(sdrdaemonsink) + +set(sdrdaemonsink_SOURCES +# sdrdaemonsinkgui.cpp +# sdrdaemonsinkoutput.cpp +# sdrdaemonsinkplugin.cpp + sdrdaemonsinksettings.cpp +# sdrdaemonsinkthread.cpp +) + +set(sdrdaemonsink_HEADERS +# sdrdaemonsinkgui.h +# sdrdaemonsinkoutput.h +# sdrdaemonsinkplugin.h + sdrdaemonsinksettings.h +# sdrdaemonsinkthread.h +) + +#set(sdrdaemonsink_FORMS +# sdrdaemonsinkgui.ui +#) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +#qt5_wrap_ui(sdrdaemonsink_FORMS_HEADERS ${sdrdaemonsink_FORMS}) + +add_library(outputsdrdaemonsink SHARED + ${sdrdaemonsink_SOURCES} + ${sdrdaemonsink_HEADERS_MOC} + ${sdrdaemonsink_FORMS_HEADERS} +) + +target_link_libraries(outputsdrdaemonsink + ${QT_LIBRARIES} + sdrbase +) + +qt5_use_modules(outputsdrdaemonsink Core Widgets) + +install(TARGETS outputsdrdaemonsink DESTINATION lib/plugins/samplesink) diff --git a/plugins/samplesink/sdrdaemonsink/filesink.pro b/plugins/samplesink/sdrdaemonsink/filesink.pro new file mode 100644 index 000000000..f21bd9c65 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/filesink.pro @@ -0,0 +1,41 @@ +#-------------------------------------------------------- +# +# Pro file for Android and Windows builds with Qt Creator +# +#-------------------------------------------------------- + +TEMPLATE = lib +CONFIG += plugin + +QT += core gui widgets multimedia opengl + +TARGET = outputfilesink + +DEFINES += USE_SSE2=1 +QMAKE_CXXFLAGS += -msse2 +DEFINES += USE_SSE4_1=1 +QMAKE_CXXFLAGS += -msse4.1 + +INCLUDEPATH += $$PWD +INCLUDEPATH += ../../../sdrbase + +CONFIG(Release):build_subdir = release +CONFIG(Debug):build_subdir = debug + +SOURCES += filesinkgui.cpp\ + filesinkoutput.cpp\ + filesinkplugin.cpp\ + filesinksettings.cpp\ + filesinkthread.cpp + +HEADERS += filesinkgui.h\ + filesinkoutput.h\ + filesinkplugin.h\ + filesinksettings.h\ + filesinkthread.h + +FORMS += filesinkgui.ui + +LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase + +RESOURCES = ../../../sdrbase/resources/res.qrc diff --git a/plugins/samplesink/sdrdaemonsink/filesinkgui.ui b/plugins/samplesink/sdrdaemonsink/filesinkgui.ui new file mode 100644 index 000000000..8def6a752 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/filesinkgui.ui @@ -0,0 +1,421 @@ + + + FileSinkGui + + + + 0 + 0 + 350 + 190 + + + + + 0 + 0 + + + + + 350 + 190 + + + + + Sans Serif + 9 + + + + FileSource + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + + + + start/stop generation + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + + + + + + 50 + 0 + + + + I/Q sample rate kS/s + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + true + + + + 0 + 0 + + + + + 32 + 16 + + + + + Monospace + 20 + + + + SizeVerCursor + + + Qt::StrongFocus + + + Record center frequency in kHz + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Open file + + + + + + + :/preset-load.png:/preset-load.png + + + + + + + false + + + File currently opened + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + + Int + + + + + + + Interpolation + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + + + + + + + + S/s + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + 90 + 0 + + + + Record time from start + + + 00:00:00.000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/samplesink/sdrdaemonsink/readme.md b/plugins/samplesink/sdrdaemonsink/readme.md new file mode 100644 index 000000000..87a4e53f1 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/readme.md @@ -0,0 +1,59 @@ +

File sink plugin

+ +

Introduction

+ +This output sample sink plugin sends its samples to file in the SDRangel .sdriq format. + +The format is S16LE I/Q samples. Thus there are 4 bytes per sample. I and Q values are 16 bit signed integers. The file starts with a context header containing information about center frequency, sample rate and timestamp of the start of the recording. This header has a length which is a multiple of a sample size (normally 24 bytes thus 6 samples). Thus this file can be used as a raw I/Q file with S16LE samples tolerating a glitch at the start corresponding to the 6 "random" samples. For example in GNURadio you can simply specify your file source format as short complex. + +You can also zap the 24 bytes header with this Linux command: `tail -c +25 myfile.sdriq > myfile.raw` + +To convert in another format you may use the sox utility. For example to convert to 32 bit (float) complex samples do: `sox -r 48k −b 16 −e signed-integer -c 2 myfile.raw -e float -c 2 myfilec.raw` + +Note that you have to specify the sampling rate and use .raw for the file extensions. + +

Build

+ +The plugin is always built. + +

Interface

+ +![File sink plugin GUI](../../../doc/img/FileSink_plugin.png) + +

1: Start/Stop

+ +Device start / stop button. + + - Blue triangle icon: device is ready and can be started + - Red square icon: device is running and can be stopped + - Magenta (or pink) square icon: an error occured + +

2: File stream sample rate

+ +This is the file stream sample rate in kS/s after interpolation (4) from the baseband stream. Thus this is the sample rate (7) multiplied by the interpolation factor (6). + +

3: Frequency

+ +This is the center frequency in kHz that will be put in the file header. + +

4: Output file selection

+ +Use this file dialog to specify the output file. + +

5: File name

+ +This is the file path of the output file. + +

6: Interpolation factor

+ +The baseband stream is interpolated by this value before being written to file. It can vary in powers of two from 1 (no interpolation) to 64. + +

7: Baseband sample rate

+ +This is the baseband sample rate before interpolation in S/s. + +Use the wheels to adjust the sample rate. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. + +

8: Time counter

+ +This is the recording time count in HH:MM:SS.SSS \ No newline at end of file diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp new file mode 100644 index 000000000..191f3a9ff --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp @@ -0,0 +1,337 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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_filesinkgui.h" +#include "plugin/pluginapi.h" +#include "gui/colormapper.h" +#include "gui/glspectrum.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" + +#include "mainwindow.h" + +#include "device/devicesinkapi.h" +#include "sdrdaemonsinkgui.h" + +FileSinkGui::FileSinkGui(DeviceSinkAPI *deviceAPI, QWidget* parent) : + QWidget(parent), + ui(new Ui::FileSinkGui), + m_deviceAPI(deviceAPI), + m_settings(), + m_deviceSampleSink(0), + m_sampleRate(0), + m_generation(false), + m_fileName("./test.sdriq"), + m_startingTimeStamp(0), + m_samplesCount(0), + m_tickCount(0), + m_lastEngineState((DSPDeviceSinkEngine::State)-1) +{ + ui->setupUi(this); + + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold)); + ui->centerFrequency->setValueRange(7, 0, pow(10,7)); + + ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::ReverseGreenYellow)); + ui->sampleRate->setValueRange(7, 32000U, 9000000U); + + ui->fileNameText->setText(m_fileName); + + connect(&(m_deviceAPI->getMainWindow()->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); + m_statusTimer.start(500); + + displaySettings(); + + m_deviceSampleSink = new FileSinkOutput(m_deviceAPI, m_deviceAPI->getMainWindow()->getMasterTimer()); + connect(m_deviceSampleSink->getOutputMessageQueueToGUI(), SIGNAL(messageEnqueued()), this, SLOT(handleSinkMessages())); + m_deviceAPI->setSink(m_deviceSampleSink); + + connect(m_deviceAPI->getDeviceOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleDSPMessages()), Qt::QueuedConnection); +} + +FileSinkGui::~FileSinkGui() +{ + delete ui; +} + +void FileSinkGui::destroy() +{ + delete this; +} + +void FileSinkGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString FileSinkGui::getName() const +{ + return objectName(); +} + +void FileSinkGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 FileSinkGui::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +void FileSinkGui::setCenterFrequency(qint64 centerFrequency) +{ + m_settings.m_centerFrequency = centerFrequency; + displaySettings(); + sendSettings(); +} + +QByteArray FileSinkGui::serialize() const +{ + return m_settings.serialize(); +} + +bool FileSinkGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool FileSinkGui::handleMessage(const Message& message) +{ + if (FileSinkOutput::MsgReportFileSinkGeneration::match(message)) + { + m_generation = ((FileSinkOutput::MsgReportFileSinkGeneration&)message).getAcquisition(); + updateWithGeneration(); + return true; + } + else if (FileSinkOutput::MsgReportFileSinkStreamTiming::match(message)) + { + m_samplesCount = ((FileSinkOutput::MsgReportFileSinkStreamTiming&)message).getSamplesCount(); + updateWithStreamTime(); + return true; + } + else + { + return false; + } +} + +void FileSinkGui::handleDSPMessages() +{ + Message* message; + + while ((message = m_deviceAPI->getDeviceOutputMessageQueue()->pop()) != 0) + { + qDebug("FileSinkGui::handleDSPMessages: message: %s", message->getIdentifier()); + + if (DSPSignalNotification::match(*message)) + { + DSPSignalNotification* notif = (DSPSignalNotification*) message; + qDebug("FileSinkGui::handleDSPMessages: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency()); + m_sampleRate = notif->getSampleRate(); + m_deviceCenterFrequency = notif->getCenterFrequency(); + updateSampleRateAndFrequency(); + + delete message; + } + } +} + +void FileSinkGui::handleSinkMessages() +{ + Message* message; + + while ((message = m_deviceSampleSink->getOutputMessageQueueToGUI()->pop()) != 0) + { + //qDebug("FileSourceGui::handleSourceMessages: message: %s", message->getIdentifier()); + + if (handleMessage(*message)) + { + delete message; + } + } +} + +void FileSinkGui::updateSampleRateAndFrequency() +{ + m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + ui->deviceRateText->setText(tr("%1k").arg((float)(m_sampleRate*(1<centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + ui->sampleRate->setValue(m_settings.m_sampleRate); +} + +void FileSinkGui::sendSettings() +{ + if(!m_updateTimer.isActive()) + m_updateTimer.start(100); +} + + +void FileSinkGui::updateHardware() +{ + qDebug() << "FileSinkGui::updateHardware"; + FileSinkOutput::MsgConfigureFileSink* message = FileSinkOutput::MsgConfigureFileSink::create(m_settings); + m_deviceSampleSink->getInputMessageQueue()->push(message); + m_updateTimer.stop(); +} + +void FileSinkGui::updateStatus() +{ + int state = m_deviceAPI->state(); + + if(m_lastEngineState != state) + { + switch(state) + { + case DSPDeviceSinkEngine::StNotStarted: + ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case DSPDeviceSinkEngine::StIdle: + ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case DSPDeviceSinkEngine::StRunning: + ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); + break; + case DSPDeviceSinkEngine::StError: + ui->startStop->setStyleSheet("QToolButton { background-color : magenta; }"); + QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + break; + default: + break; + } + + m_lastEngineState = state; + } +} + +void FileSinkGui::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void FileSinkGui::on_sampleRate_changed(quint64 value) +{ + m_settings.m_sampleRate = value; + sendSettings(); +} + +void FileSinkGui::on_interp_currentIndexChanged(int index) +{ + if (index < 0) { + return; + } + + m_settings.m_log2Interp = index; + updateSampleRateAndFrequency(); + sendSettings(); +} + +void FileSinkGui::on_startStop_toggled(bool checked) +{ + if (checked) + { + if (m_deviceAPI->initGeneration()) + { + if (!m_deviceAPI->startGeneration()) + { + qDebug("FileSinkGui::on_startStop_toggled: device start failed"); + } + + DSPEngine::instance()->startAudioInput(); + } + } + else + { + m_deviceAPI->stopGeneration(); + DSPEngine::instance()->stopAudioInput(); + } +} + +void FileSinkGui::on_showFileDialog_clicked(bool checked) +{ + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save I/Q record file"), ".", tr("SDR I/Q Files (*.sdriq)")); + + if (fileName != "") + { + m_fileName = fileName; + ui->fileNameText->setText(m_fileName); + configureFileName(); + } +} + +void FileSinkGui::configureFileName() +{ + qDebug() << "FileSinkGui::configureFileName: " << m_fileName.toStdString().c_str(); + FileSinkOutput::MsgConfigureFileSinkName* message = FileSinkOutput::MsgConfigureFileSinkName::create(m_fileName); + m_deviceSampleSink->getInputMessageQueue()->push(message); +} + +void FileSinkGui::updateWithGeneration() +{ + ui->showFileDialog->setEnabled(!m_generation); +} + +void FileSinkGui::updateWithStreamTime() +{ + int t_sec = 0; + int t_msec = 0; + + if (m_settings.m_sampleRate > 0){ + t_msec = ((m_samplesCount * 1000) / m_settings.m_sampleRate) % 1000; + t_sec = m_samplesCount / m_settings.m_sampleRate; + } + + QTime t(0, 0, 0, 0); + t = t.addSecs(t_sec); + t = t.addMSecs(t_msec); + QString s_timems = t.toString("hh:mm:ss.zzz"); + ui->relTimeText->setText(s_timems); +} + +void FileSinkGui::tick() +{ + if ((++m_tickCount & 0xf) == 0) + { + FileSinkOutput::MsgConfigureFileSinkStreamTiming* message = FileSinkOutput::MsgConfigureFileSinkStreamTiming::create(); + m_deviceSampleSink->getInputMessageQueue()->push(message); + } +} diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h new file mode 100644 index 000000000..24c93d282 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FILESINKGUI_H +#define INCLUDE_FILESINKGUI_H + +#include + +#include "plugin/plugingui.h" +#include "../sdrdaemonoutput/filesinksettings.h" +#include "sdrdaemonsinkoutput.h" + + +class DeviceSinkAPI; +class DeviceSampleSink; + +namespace Ui { + class FileSinkGui; +} + +class FileSinkGui : public QWidget, public PluginGUI { + Q_OBJECT + +public: + explicit FileSinkGui(DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + virtual ~FileSinkGui(); + 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 bool handleMessage(const Message& message); + +private: + Ui::FileSinkGui* ui; + + DeviceSinkAPI* m_deviceAPI; + FileSinkSettings m_settings; + QString m_fileName; + QTimer m_updateTimer; + QTimer m_statusTimer; + DeviceSampleSink* m_deviceSampleSink; + int m_sampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + bool m_generation; + std::time_t m_startingTimeStamp; + int m_samplesCount; + std::size_t m_tickCount; + int m_lastEngineState; + + void displaySettings(); + void displayTime(); + void sendSettings(); + void configureFileName(); + void updateWithGeneration(); + void updateWithStreamTime(); + void updateSampleRateAndFrequency(); + +private slots: + void handleDSPMessages(); + void handleSinkMessages(); + void on_centerFrequency_changed(quint64 value); + void on_sampleRate_changed(quint64 value); + void on_startStop_toggled(bool checked); + void on_showFileDialog_clicked(bool checked); + void on_interp_currentIndexChanged(int index); + void updateHardware(); + void updateStatus(); + void tick(); +}; + +#endif // INCLUDE_FILESINKGUI_H diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp new file mode 100644 index 000000000..af051f894 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp @@ -0,0 +1,240 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "util/simpleserializer.h" +#include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "dsp/filerecord.h" + +#include "device/devicesinkapi.h" + +#include "sdrdaemonsinkgui.h" +#include "sdrdaemonsinkoutput.h" +#include "sdrdaemonsinkthread.h" + +MESSAGE_CLASS_DEFINITION(FileSinkOutput::MsgConfigureFileSink, Message) +MESSAGE_CLASS_DEFINITION(FileSinkOutput::MsgConfigureFileSinkName, Message) +MESSAGE_CLASS_DEFINITION(FileSinkOutput::MsgConfigureFileSinkWork, Message) +MESSAGE_CLASS_DEFINITION(FileSinkOutput::MsgConfigureFileSinkStreamTiming, Message) +MESSAGE_CLASS_DEFINITION(FileSinkOutput::MsgReportFileSinkGeneration, Message) +MESSAGE_CLASS_DEFINITION(FileSinkOutput::MsgReportFileSinkStreamTiming, Message) + +FileSinkOutput::FileSinkOutput(DeviceSinkAPI *deviceAPI, const QTimer& masterTimer) : + m_deviceAPI(deviceAPI), + m_settings(), + m_fileSinkThread(0), + m_deviceDescription("FileSink"), + m_fileName("./test.sdriq"), + m_startingTimeStamp(0), + m_masterTimer(masterTimer) +{ +} + +FileSinkOutput::~FileSinkOutput() +{ + stop(); +} + +void FileSinkOutput::openFileStream() +{ + if (m_ofstream.is_open()) { + m_ofstream.close(); + } + + m_ofstream.open(m_fileName.toStdString().c_str(), std::ios::binary); + + int actualSampleRate = m_settings.m_sampleRate * (1<setSamplerate(m_settings.m_sampleRate); + m_fileSinkThread->setLog2Interpolation(m_settings.m_log2Interp); + m_fileSinkThread->connectTimer(m_masterTimer); + m_fileSinkThread->startWork(); + + mutexLocker.unlock(); + //applySettings(m_generalSettings, m_settings, true); + qDebug("FileSinkOutput::start: started"); + + MsgReportFileSinkGeneration *report = MsgReportFileSinkGeneration::create(true); // acquisition on + getOutputMessageQueueToGUI()->push(report); + + return true; +} + +void FileSinkOutput::stop() +{ + qDebug() << "FileSourceInput::stop"; + QMutexLocker mutexLocker(&m_mutex); + + if(m_fileSinkThread != 0) + { + m_fileSinkThread->stopWork(); + delete m_fileSinkThread; + m_fileSinkThread = 0; + } + + if (m_ofstream.is_open()) { + m_ofstream.close(); + } + + MsgReportFileSinkGeneration *report = MsgReportFileSinkGeneration::create(false); // acquisition off + getOutputMessageQueueToGUI()->push(report); +} + +const QString& FileSinkOutput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int FileSinkOutput::getSampleRate() const +{ + return m_settings.m_sampleRate; +} + +quint64 FileSinkOutput::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +std::time_t FileSinkOutput::getStartingTimeStamp() const +{ + return m_startingTimeStamp; +} + +bool FileSinkOutput::handleMessage(const Message& message) +{ + if (MsgConfigureFileSinkName::match(message)) + { + MsgConfigureFileSinkName& conf = (MsgConfigureFileSinkName&) message; + m_fileName = conf.getFileName(); + openFileStream(); + return true; + } + else if (MsgConfigureFileSink::match(message)) + { + qDebug() << "FileSinkOutput::handleMessage: MsgConfigureFileSink"; + MsgConfigureFileSink& conf = (MsgConfigureFileSink&) message; + applySettings(conf.getSettings(), false); + return true; + } + else if (MsgConfigureFileSinkWork::match(message)) + { + MsgConfigureFileSinkWork& conf = (MsgConfigureFileSinkWork&) message; + bool working = conf.isWorking(); + + if (m_fileSinkThread != 0) + { + if (working) + { + m_fileSinkThread->startWork(); + } + else + { + m_fileSinkThread->stopWork(); + } + } + + return true; + } + else if (MsgConfigureFileSinkStreamTiming::match(message)) + { + MsgReportFileSinkStreamTiming *report; + + if (m_fileSinkThread != 0) + { + report = MsgReportFileSinkStreamTiming::create(m_fileSinkThread->getSamplesCount()); + getOutputMessageQueueToGUI()->push(report); + } + + return true; + } + else + { + return false; + } +} + +void FileSinkOutput::applySettings(const FileSinkSettings& settings, bool force) +{ + QMutexLocker mutexLocker(&m_mutex); + bool forwardChange = false; + + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency)) + { + m_settings.m_centerFrequency = settings.m_centerFrequency; + forwardChange = true; + } + + if (force || (m_settings.m_sampleRate != settings.m_sampleRate)) + { + m_settings.m_sampleRate = settings.m_sampleRate; + + if (m_fileSinkThread != 0) + { + m_fileSinkThread->setSamplerate(m_settings.m_sampleRate); + } + + forwardChange = true; + } + + if (force || (m_settings.m_log2Interp != settings.m_log2Interp)) + { + m_settings.m_log2Interp = settings.m_log2Interp; + + if (m_fileSinkThread != 0) + { + m_fileSinkThread->setSamplerate(m_settings.m_sampleRate); + } + + forwardChange = true; + } + + if (forwardChange) + { + qDebug("FileSinkOutput::applySettings: forward: m_centerFrequency: %llu m_sampleRate: %llu m_log2Interp: %d", + m_settings.m_centerFrequency, + m_settings.m_sampleRate, + m_settings.m_log2Interp); + DSPSignalNotification *notif = new DSPSignalNotification(m_settings.m_sampleRate, m_settings.m_centerFrequency); + m_deviceAPI->getDeviceInputMessageQueue()->push(notif); + } + +} diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h new file mode 100644 index 000000000..95fa6a152 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FILESINKOUTPUT_H +#define INCLUDE_FILESINKOUTPUT_H + +#include +#include +#include +#include +#include + +#include "dsp/devicesamplesink.h" + +#include "sdrdaemonsinksettings.h" + +class FileSinkThread; +class DeviceSinkAPI; + +class FileSinkOutput : public DeviceSampleSink { +public: + class MsgConfigureFileSink : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FileSinkSettings& getSettings() const { return m_settings; } + + static MsgConfigureFileSink* create(const FileSinkSettings& settings) + { + return new MsgConfigureFileSink(settings); + } + + private: + FileSinkSettings m_settings; + + MsgConfigureFileSink(const FileSinkSettings& settings) : + Message(), + m_settings(settings) + { } + }; + + class MsgConfigureFileSinkName : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const QString& getFileName() const { return m_fileName; } + + static MsgConfigureFileSinkName* create(const QString& fileName) + { + return new MsgConfigureFileSinkName(fileName); + } + + private: + QString m_fileName; + + MsgConfigureFileSinkName(const QString& fileName) : + Message(), + m_fileName(fileName) + { } + }; + + class MsgConfigureFileSinkWork : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool isWorking() const { return m_working; } + + static MsgConfigureFileSinkWork* create(bool working) + { + return new MsgConfigureFileSinkWork(working); + } + + private: + bool m_working; + + MsgConfigureFileSinkWork(bool working) : + Message(), + m_working(working) + { } + }; + + class MsgConfigureFileSinkStreamTiming : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgConfigureFileSinkStreamTiming* create() + { + return new MsgConfigureFileSinkStreamTiming(); + } + + private: + + MsgConfigureFileSinkStreamTiming() : + Message() + { } + }; + + class MsgReportFileSinkGeneration : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getAcquisition() const { return m_acquisition; } + + static MsgReportFileSinkGeneration* create(bool acquisition) + { + return new MsgReportFileSinkGeneration(acquisition); + } + + protected: + bool m_acquisition; + + MsgReportFileSinkGeneration(bool acquisition) : + Message(), + m_acquisition(acquisition) + { } + }; + + class MsgReportFileSinkStreamTiming : public Message { + MESSAGE_CLASS_DECLARATION + + public: + std::size_t getSamplesCount() const { return m_samplesCount; } + + static MsgReportFileSinkStreamTiming* create(std::size_t samplesCount) + { + return new MsgReportFileSinkStreamTiming(samplesCount); + } + + protected: + std::size_t m_samplesCount; + + MsgReportFileSinkStreamTiming(std::size_t samplesCount) : + Message(), + m_samplesCount(samplesCount) + { } + }; + + FileSinkOutput(DeviceSinkAPI *deviceAPI, const QTimer& masterTimer); + virtual ~FileSinkOutput(); + + virtual bool start(); + virtual void stop(); + + virtual const QString& getDeviceDescription() const; + virtual int getSampleRate() const; + virtual quint64 getCenterFrequency() const; + std::time_t getStartingTimeStamp() const; + + virtual bool handleMessage(const Message& message); + +private: + DeviceSinkAPI *m_deviceAPI; + QMutex m_mutex; + FileSinkSettings m_settings; + std::ofstream m_ofstream; + FileSinkThread* m_fileSinkThread; + QString m_deviceDescription; + QString m_fileName; + std::time_t m_startingTimeStamp; + const QTimer& m_masterTimer; + + void openFileStream(); + void applySettings(const FileSinkSettings& settings, bool force = false); +}; + +#endif // INCLUDE_FILESINKOUTPUT_H diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp new file mode 100644 index 000000000..02dc982df --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" + +#include "device/devicesinkapi.h" + +#include "sdrdaemonsinkgui.h" +#include "sdrdaemonsinkplugin.h" + +const PluginDescriptor FileSinkPlugin::m_pluginDescriptor = { + QString("File sink output"), + QString("3.4.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +const QString FileSinkPlugin::m_hardwareID = "FileSink"; +const QString FileSinkPlugin::m_deviceTypeID = FILESINK_DEVICE_TYPE_ID; + +FileSinkPlugin::FileSinkPlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& FileSinkPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void FileSinkPlugin::initPlugin(PluginAPI* pluginAPI) +{ + pluginAPI->registerSampleSink(m_deviceTypeID, this); +} + +PluginInterface::SamplingDevices FileSinkPlugin::enumSampleSinks() +{ + SamplingDevices result; + int count = 1; + + for(int i = 0; i < count; i++) + { + QString displayedName(QString("FileSink[%1]").arg(i)); + + result.append(SamplingDevice(displayedName, + m_hardwareID, + m_deviceTypeID, + QString::null, + i)); + } + + return result; +} + +PluginGUI* FileSinkPlugin::createSampleSinkPluginGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI) +{ + if(sinkId == m_deviceTypeID) + { + FileSinkGui* gui = new FileSinkGui(deviceAPI); + *widget = gui; + return gui; + } + else + { + return 0; + } +} diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.h new file mode 100644 index 000000000..c57ba173c --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FILESINKPLUGIN_H +#define INCLUDE_FILESINKPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +#define FILESINK_DEVICE_TYPE_ID "sdrangel.samplesink.filesink" + +class PluginAPI; +class DeviceSinkAPI; + +class FileSinkPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID FILESINK_DEVICE_TYPE_ID) + +public: + explicit FileSinkPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual SamplingDevices enumSampleSinks(); + virtual PluginGUI* createSampleSinkPluginGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI); + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; +}; + +#endif // INCLUDE_FILESOURCEPLUGIN_H diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.cpp new file mode 100644 index 000000000..648454a2c --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.cpp @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 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 "util/simpleserializer.h" +#include "sdrdaemonsinksettings.h" + +SDRdaemonSinkSettings::SDRdaemonSinkSettings() +{ + resetToDefaults(); +} + +void SDRdaemonSinkSettings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_sampleRate = 48000; + m_log2Interp = 0; + m_address = "127.0.0.1"; + m_port = 9090; +} + +QByteArray SDRdaemonSinkSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeU64(1, m_sampleRate); + s.writeU32(2, m_log2Interp); + s.writeString(3, m_address); + s.writeU32(4, m_port); + + return s.final(); +} + +bool SDRdaemonSinkSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + int intval; + quint32 uintval; + d.readU64(1, &m_sampleRate, 48000); + d.readU32(2, &m_log2Interp, 0); + d.readString(3, &m_address, ""); + d.readU32(4, &uintval, 9090); + m_port = uintval % (1<<16); + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.h new file mode 100644 index 000000000..acc5596ec --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.h @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESINK_SDRDAEMONSINK_SDRDAEMONSINKSETTINGS_H_ +#define PLUGINS_SAMPLESINK_SDRDAEMONSINK_SDRDAEMONSINKSETTINGS_H_ + +#include + +struct SDRdaemonSinkSettings { + quint64 m_centerFrequency; + quint64 m_sampleRate; + quint32 m_log2Interp; + QString m_address; + quint16 m_port; + + SDRdaemonSinkSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* PLUGINS_SAMPLESINK_SDRDAEMONSINK_SDRDAEMONSINKSETTINGS_H_ */ diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkthread.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkthread.cpp new file mode 100644 index 000000000..4c0745b68 --- /dev/null +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkthread.cpp @@ -0,0 +1,230 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 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 "dsp/samplesourcefifo.h" +#include "sdrdaemonsinkthread.h" + +SDRdaemonSinkThread::SDRdaemonSinkThread(std::ofstream *samplesStream, SampleSourceFifo* sampleFifo, QObject* parent) : + QThread(parent), + m_running(false), + m_ofstream(samplesStream), + m_bufsize(0), + m_samplesChunkSize(0), + m_sampleFifo(sampleFifo), + m_samplesCount(0), + m_samplerate(0), + m_log2Interpolation(0), + m_throttlems(SDRDAEMONSINK_THROTTLE_MS), + m_throttleToggle(false), + m_buf(0), + m_maxThrottlems(50) +{ + assert(m_ofstream != 0); +} + +SDRdaemonSinkThread::~SDRdaemonSinkThread() +{ + if (m_running) { + stopWork(); + } + + if (m_buf) delete[] m_buf; +} + +void SDRdaemonSinkThread::startWork() +{ + qDebug() << "SDRdaemonSinkThread::startWork: "; + + if (m_ofstream->is_open()) + { + qDebug() << "SDRdaemonSinkThread::startWork: file stream open, starting..."; + m_maxThrottlems = 0; + m_startWaitMutex.lock(); + m_elapsedTimer.start(); + start(); + while(!m_running) + m_startWaiter.wait(&m_startWaitMutex, 100); + m_startWaitMutex.unlock(); + } + else + { + qDebug() << "SDRdaemonSinkThread::startWork: file stream closed, not starting."; + } +} + +void SDRdaemonSinkThread::stopWork() +{ + qDebug() << "SDRdaemonSinkThread::stopWork"; + m_running = false; + wait(); +} + +void SDRdaemonSinkThread::setSamplerate(int samplerate) +{ + if (samplerate != m_samplerate) + { + qDebug() << "SDRdaemonSinkThread::setSamplerate:" + << " new:" << samplerate + << " old:" << m_samplerate; + + bool wasRunning = false; + + if (m_running) + { + stopWork(); + wasRunning = true; + } + + // resize sample FIFO + if (m_sampleFifo) { + m_sampleFifo->resize(samplerate); // 1s buffer + } + + // resize output buffer + if (m_buf) delete[] m_buf; + m_buf = new int16_t[samplerate*(1< 6)) + { + return; + } + + if (log2Interpolation != m_log2Interpolation) + { + qDebug() << "FileSinkThread::setLog2Interpolation:" + << " new:" << log2Interpolation + << " old:" << m_log2Interpolation; + + bool wasRunning = false; + + if (m_running) + { + stopWork(); + wasRunning = true; + } + + // resize output buffer + if (m_buf) delete[] m_buf; + m_buf = new int16_t[m_samplerate*(1< m_maxThrottlems) +// { +// qDebug("FileSinkThread::tick: m_maxThrottlems: %d", m_maxThrottlems); +// m_maxThrottlems = m_throttlems; +// } + + SampleVector::iterator readUntil; + + m_sampleFifo->readAdvance(readUntil, m_samplesChunkSize); + SampleVector::iterator beginRead = readUntil - m_samplesChunkSize; + m_samplesCount += m_samplesChunkSize; + + if (m_log2Interpolation == 0) + { + m_ofstream->write(reinterpret_cast(&(*beginRead)), m_samplesChunkSize*sizeof(Sample)); + } + else + { + int chunkSize = std::min((int) m_samplesChunkSize, m_samplerate); + + switch (m_log2Interpolation) + { + case 1: + m_interpolators.interpolate2_cen(&beginRead, m_buf, chunkSize*(1<write(reinterpret_cast(m_buf), m_samplesChunkSize*(1<. // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SDRDAEMONSINKTHREAD_H +#define INCLUDE_SDRDAEMONSINKTHREAD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dsp/inthalfbandfilter.h" +#include "dsp/interpolators.h" + +#define SDRDAEMONSINK_THROTTLE_MS 50 + +class SampleSourceFifo; + +class SDRdaemonSinkThread : public QThread { + Q_OBJECT + +public: + SDRdaemonSinkThread(std::ofstream *samplesStream, SampleSourceFifo* sampleFifo, QObject* parent = 0); + ~SDRdaemonSinkThread(); + + void startWork(); + void stopWork(); + void setSamplerate(int samplerate); + void setLog2Interpolation(int log2Interpolation); + void setBuffer(std::size_t chunksize); + bool isRunning() const { return m_running; } + std::size_t getSamplesCount() const { return m_samplesCount; } + void setSamplesCount(int samplesCount) { m_samplesCount = samplesCount; } + + void connectTimer(const QTimer& timer); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + + std::ofstream* m_ofstream; + std::size_t m_bufsize; + unsigned int m_samplesChunkSize; + SampleSourceFifo* m_sampleFifo; + std::size_t m_samplesCount; + + int m_samplerate; + int m_log2Interpolation; + int m_throttlems; + int m_maxThrottlems; + QElapsedTimer m_elapsedTimer; + bool m_throttleToggle; + + Interpolators m_interpolators; + int16_t *m_buf; + + void run(); + +private slots: + void tick(); +}; + +#endif // INCLUDE_SDRDAEMONSINKTHREAD_H