diff --git a/plugins/samplesource/perseus/CMakeLists.txt b/plugins/samplesource/perseus/CMakeLists.txt
new file mode 100644
index 000000000..57673ba22
--- /dev/null
+++ b/plugins/samplesource/perseus/CMakeLists.txt
@@ -0,0 +1,78 @@
+project(perseus)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+set(perseus_SOURCES
+# perseusgui.cpp
+# perseusinput.cpp
+# perseusplugin.cpp
+ perseussettings.cpp
+# perseusthread.cpp
+)
+
+set(perseus_HEADERS
+# perseusgui.h
+# perseusinput.h
+# perseusplugin.h
+ perseussettings.h
+# perseusthread.h
+)
+
+set(perseus_FORMS
+ perseusgui.ui
+)
+
+if (BUILD_DEBIAN)
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
+ ${LIBAIRSPYHFSRC}
+ ${LIBAIRSPYHFSRC}/libperseus/src
+)
+else (BUILD_DEBIAN)
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
+ ${LIBAIRSPYHF_INCLUDE_DIR}
+)
+endif (BUILD_DEBIAN)
+
+#include(${QT_USE_FILE})
+#add_definitions(${QT_DEFINITIONS})
+add_definitions("${QT_DEFINITIONS} -DLIBAIRSPY_DYN_RATES")
+add_definitions(-DQT_PLUGIN)
+add_definitions(-DQT_SHARED)
+
+#qt4_wrap_cpp(perseus_HEADERS_MOC ${perseus_HEADERS})
+qt5_wrap_ui(perseus_FORMS_HEADERS ${perseus_FORMS})
+
+add_library(inputperseus SHARED
+ ${perseus_SOURCES}
+ ${perseus_HEADERS_MOC}
+ ${perseus_FORMS_HEADERS}
+)
+
+if (BUILD_DEBIAN)
+target_link_libraries(inputperseus
+ ${QT_LIBRARIES}
+ perseus
+ sdrbase
+ sdrgui
+ swagger
+)
+else (BUILD_DEBIAN)
+target_link_libraries(inputperseus
+ ${QT_LIBRARIES}
+ ${LIBAIRSPYHF_LIBRARIES}
+ sdrbase
+ sdrgui
+ swagger
+)
+endif (BUILD_DEBIAN)
+
+
+qt5_use_modules(inputperseus Core Widgets)
+
+install(TARGETS inputperseus DESTINATION lib/plugins/samplesource)
diff --git a/plugins/samplesource/perseus/perseusinput.cpp b/plugins/samplesource/perseus/perseusinput.cpp
new file mode 100644
index 000000000..5f89f4c60
--- /dev/null
+++ b/plugins/samplesource/perseus/perseusinput.cpp
@@ -0,0 +1,199 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 "SWGDeviceSettings.h"
+#include "SWGDeviceState.h"
+
+#include "dsp/filerecord.h"
+#include "dsp/dspcommands.h"
+#include "dsp/dspengine.h"
+#include "device/devicesourceapi.h"
+
+#include "perseusinput.h"
+#include "perseusthread.h"
+
+MESSAGE_CLASS_DEFINITION(PerseusInput::MsgConfigurePerseus, Message)
+MESSAGE_CLASS_DEFINITION(PerseusInput::MsgFileRecord, Message)
+MESSAGE_CLASS_DEFINITION(PerseusInput::MsgStartStop, Message)
+
+PerseusInput::PerseusInput(DeviceSourceAPI *deviceAPI) :
+ m_deviceAPI(deviceAPI),
+ m_fileSink(0),
+ m_deviceDescription("PerseusInput"),
+ m_running(false),
+ m_perseusThread(0),
+ m_perseusDescriptor(0)
+{
+ openDevice();
+ char recFileNameCStr[30];
+ sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceAPI->getDeviceUID());
+ m_fileSink = new FileRecord(std::string(recFileNameCStr));
+ m_deviceAPI->addSink(m_fileSink);
+}
+
+PerseusInput::~PerseusInput()
+{
+ m_deviceAPI->removeSink(m_fileSink);
+ delete m_fileSink;
+ closeDevice();
+}
+
+void PerseusInput::destroy()
+{
+ delete this;
+}
+
+void PerseusInput::init()
+{
+ applySettings(m_settings, true);
+}
+
+bool PerseusInput::start()
+{
+ if (m_running) stop();
+
+ applySettings(m_settings, true);
+
+ // start / stop streaming is done in the thread.
+
+ if ((m_plutoSDRInputThread = new PlutoSDRInputThread(PLUTOSDR_BLOCKSIZE_SAMPLES, m_deviceShared.m_deviceParams->getBox(), &m_sampleFifo)) == 0)
+ {
+ qFatal("PlutoSDRInput::start: cannot create thread");
+ stop();
+ return false;
+ }
+ else
+ {
+ qDebug("PlutoSDRInput::start: thread created");
+ }
+
+ m_perseusThread->setLog2Decimation(m_settings.m_log2Decim);
+ m_perseusThread->startWork();
+
+ m_running = true;
+
+ return true;
+}
+
+void PerseusInput::stop()
+{
+ if (m_perseusThread != 0)
+ {
+ m_perseusThread->stopWork();
+ delete m_perseusThread;
+ m_perseusThread = 0;
+ }
+
+ m_running = false;
+}
+
+QByteArray PerseusInput::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool PerseusInput::deserialize(const QByteArray& data)
+{
+ bool success = true;
+
+ if (!m_settings.deserialize(data))
+ {
+ m_settings.resetToDefaults();
+ success = false;
+ }
+
+ MsgConfigurePerseus* message = MsgConfigurePerseus::create(m_settings, true);
+ m_inputMessageQueue.push(message);
+
+ if (m_guiMessageQueue)
+ {
+ MsgConfigurePerseus* messageToGUI = MsgConfigurePerseus::create(m_settings, true);
+ m_guiMessageQueue->push(messageToGUI);
+ }
+
+ return success;
+}
+
+const QString& PerseusInput::getDeviceDescription() const
+{
+ return m_deviceDescription;
+}
+int PerseusInput::getSampleRate() const
+{
+ return (m_settings.m_devSampleRate / (1<push(messageToGUI);
+ }
+}
+
+bool PerseusInput::openDevice()
+{
+ m_deviceAPI->getSampleSourceSerial();
+ int deviceSequence = DevicePerseus::instance().getSequenceFromSerial(m_deviceAPI->getSampleSourceSerial().toStdString());
+
+ if ((m_perseusDescriptor = perseus_open(deviceSequence)) == 0)
+ {
+ qCritical("PerseusInput::openDevice: cannot open device: %s", perseus_errorstr());
+ return false;
+ }
+
+ int buf[32];
+ m_sampleRates.clear();
+
+ if (perseus_get_sampling_rates(m_perseusDescriptor, buf, sizeof(buf)/sizeof(buf[0])) < 0)
+ {
+ qCritical("PerseusInput::openDevice: cannot get sampling rates: %s", perseus_errorstr());
+ perseus_close(m_perseusDescriptor);
+ return false;
+ }
+ else
+ {
+ for (int i = 0; (i < 32) && (buf[i] != 0); i++)
+ {
+ qDebug("PerseusInput::openDevice: sample rate: %d", buf[i]);
+ m_sampleRates.push_back(buf[i]);
+ }
+ }
+
+ return true;
+}
+
+void PerseusInput::closeDevice()
+{
+ if (m_perseusDescriptor)
+ {
+ perseus_stop_async_input(m_perseusDescriptor);
+ perseus_close(m_perseusDescriptor);
+ }
+}
+
diff --git a/plugins/samplesource/perseus/perseusinput.h b/plugins/samplesource/perseus/perseusinput.h
new file mode 100644
index 000000000..96168a7fe
--- /dev/null
+++ b/plugins/samplesource/perseus/perseusinput.h
@@ -0,0 +1,137 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSINPUT_H_
+#define PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSINPUT_H_
+
+#include
+#include "dsp/devicesamplesource.h"
+#include "util/message.h"
+#include "perseussettings.h"
+
+class DeviceSourceAPI;
+class FileRecord;
+class PerseusThread;
+struct perseus_descr;
+
+class PerseusInput : public DeviceSampleSource {
+public:
+ class MsgConfigurePerseus : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ const PerseusSettings& getSettings() const { return m_settings; }
+ bool getForce() const { return m_force; }
+
+ static MsgConfigurePerseus* create(const PerseusSettings& settings, bool force)
+ {
+ return new MsgConfigurePerseus(settings, force);
+ }
+
+ private:
+ PerseusSettings m_settings;
+ bool m_force;
+
+ MsgConfigurePerseus(const PerseusSettings& 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)
+ { }
+ };
+
+ PerseusInput(DeviceSourceAPI *deviceAPI);
+ ~PerseusInput();
+ 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;
+ QString m_deviceDescription;
+ PerseusSettings m_settings;
+ bool m_running;
+ PerseusThread *m_perseusThread;
+ perseus_descr *m_perseusDescriptor;
+ std::vector m_sampleRates;
+ QMutex m_mutex;
+
+ bool openDevice();
+ void closeDevice();
+ bool applySettings(const PerseusSettings& settings, bool force = false);
+};
+
+#endif /* PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSINPUT_H_ */
diff --git a/plugins/samplesource/perseus/perseussettings.cpp b/plugins/samplesource/perseus/perseussettings.cpp
new file mode 100644
index 000000000..77a57a83f
--- /dev/null
+++ b/plugins/samplesource/perseus/perseussettings.cpp
@@ -0,0 +1,95 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 "perseussettings.h"
+#include "util/simpleserializer.h"
+
+
+PerseusSettings::PerseusSettings()
+{
+ resetToDefaults();
+}
+
+void PerseusSettings::resetToDefaults()
+{
+ m_centerFrequency = 7150*1000;
+ m_LOppmTenths = 0;
+ m_devSampleRateIndex = 0;
+ m_log2Decim = 0;
+ m_transverterMode = false;
+ m_transverterDeltaFrequency = 0;
+ m_adcDither = false;
+ m_adcPreamp = false;
+ m_wideBand = false;
+ m_attenuator = Attenuator_None;
+}
+
+QByteArray PerseusSettings::serialize() const
+{
+ SimpleSerializer s(1);
+
+ s.writeU32(1, m_devSampleRateIndex);
+ s.writeS32(2, m_LOppmTenths);
+ s.writeU32(3, m_log2Decim);
+ s.writeBool(4, m_transverterMode);
+ s.writeS64(5, m_transverterDeltaFrequency);
+ s.writeBool(6, m_adcDither);
+ s.writeBool(7, m_adcPreamp);
+ s.writeBool(8, m_wideBand);
+ s.writeS32(9, (int) m_attenuator);
+
+ return s.final();
+}
+
+bool PerseusSettings::deserialize(const QByteArray& data)
+{
+ SimpleDeserializer d(data);
+
+ if (!d.isValid())
+ {
+ resetToDefaults();
+ return false;
+ }
+
+ if (d.getVersion() == 1)
+ {
+ int intval;
+
+ d.readU32(1, &m_devSampleRateIndex, 0);
+ d.readS32(2, &m_LOppmTenths, 0);
+ d.readU32(3, &m_log2Decim, 0);
+ d.readBool(4, &m_transverterMode, false);
+ d.readS64(5, &m_transverterDeltaFrequency, 0);
+ d.readBool(6, &m_adcDither, false);
+ d.readBool(7, &m_adcPreamp, false);
+ d.readBool(8, &m_wideBand, false);
+ d.readS32(9, &intval, 0);
+
+ if ((intval >= 0) && (intval < (int) Attenuator_last)) {
+ m_attenuator = (Attenuator) intval;
+ } else {
+ m_attenuator = Attenuator_None;
+ }
+
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
diff --git a/plugins/samplesource/perseus/perseussettings.h b/plugins/samplesource/perseus/perseussettings.h
new file mode 100644
index 000000000..864bb0cec
--- /dev/null
+++ b/plugins/samplesource/perseus/perseussettings.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 PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSSETTINGS_H_
+#define PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSSETTINGS_H_
+
+#include
+
+struct PerseusSettings
+{
+ typedef enum
+ {
+ Attenuator_None,
+ Attenuator_10dB,
+ Attenuator_20dB,
+ Attenuator_30dB,
+ Attenuator_last
+ } Attenuator;
+
+ quint64 m_centerFrequency;
+ qint32 m_LOppmTenths;
+ quint32 m_devSampleRateIndex;
+ quint32 m_log2Decim;
+ bool m_transverterMode;
+ qint64 m_transverterDeltaFrequency;
+ bool m_adcDither;
+ bool m_adcPreamp;
+ bool m_wideBand;
+ Attenuator m_attenuator;
+
+ PerseusSettings();
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+};
+
+
+
+#endif /* PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSSETTINGS_H_ */