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
+
+ 1
+
+
+ ButtonSwitch
+ QToolButton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+
+
+
+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