From be1a4caae166851b2872d8266326e866f73f5960 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 8 May 2016 06:00:37 +0200 Subject: [PATCH] DV Serial support: working signal/slot mechanism --- CMakeLists.txt | 23 ++- plugins/channel/demoddsd/dsddecoder.h | 5 + plugins/channel/demoddsd/dsddemod.cpp | 29 +++- sdrbase/dsp/dspengine.cpp | 15 ++ sdrbase/dsp/dspengine.h | 25 +++ sdrbase/dsp/dvserialengine.cpp | 226 ++++++++++++++++++++++++++ sdrbase/dsp/dvserialengine.h | 63 +++++++ sdrbase/dsp/dvserialworker.cpp | 69 ++++++++ sdrbase/dsp/dvserialworker.h | 107 ++++++++++++ sdrbase/mainwindow.cpp | 5 + sdrbase/mainwindow.h | 1 + 11 files changed, 556 insertions(+), 12 deletions(-) create mode 100644 sdrbase/dsp/dvserialengine.cpp create mode 100644 sdrbase/dsp/dvserialengine.h create mode 100644 sdrbase/dsp/dvserialworker.cpp create mode 100644 sdrbase/dsp/dvserialworker.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 089972311..4264b8d81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,10 +52,6 @@ find_package(LibDSDcc) find_package(LibMbe) find_package(SerialDV) -if (LIBSERIALDV_FOUND) - add_definitions(-DDSD_USE_SERIALDV) -endif() - IF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|x86") SET(USE_SIMD "SSE2" CACHE STRING "Use SIMD instructions") ENDIF() @@ -292,6 +288,21 @@ else(FFTW3F_FOUND) add_definitions(-DUSE_KISSFFT) endif(FFTW3F_FOUND) +if (LIBSERIALDV_FOUND) + set(sdrbase_SOURCES + ${sdrbase_SOURCES} + sdrbase/dsp/dvserialworker.cpp + sdrbase/dsp/dvserialengine.cpp + ) + set(sdrbase_HEADERS + ${sdrbase_HEADERS} + sdrbase/dsp/dvserialworker.h + sdrbase/dsp/dvserialengine.h + ) + add_definitions(-DDSD_USE_SERIALDV) + include_directories(${LIBSERIALDV_INCLUDE_DIR}) +endif(LIBSERIALDV_FOUND) + #include(${QT_USE_FILE}) add_definitions(${QT_DEFINITIONS}) @@ -319,6 +330,10 @@ if(FFTW3F_FOUND) target_link_libraries(sdrbase ${FFTW3F_LIBRARIES}) endif(FFTW3F_FOUND) +if(LIBSERIALDV_FOUND) + target_link_libraries(sdrbase ${LIBSERIALDV_LIBRARY}) +endif(LIBSERIALDV_FOUND) + set_target_properties(sdrbase PROPERTIES DEFINE_SYMBOL "sdrangel_EXPORTS") target_compile_features(sdrbase PRIVATE cxx_generalized_initializers) # cmake >= 3.1.0 diff --git a/plugins/channel/demoddsd/dsddecoder.h b/plugins/channel/demoddsd/dsddecoder.h index bb283687c..dcc1591bb 100644 --- a/plugins/channel/demoddsd/dsddecoder.h +++ b/plugins/channel/demoddsd/dsddecoder.h @@ -32,6 +32,11 @@ public: short *getAudio(int& nbSamples) { return m_decoder.getAudio(nbSamples); } void resetAudio() { m_decoder.resetAudio(); } + bool mbeDVReady() const { return m_decoder.mbeDVReady(); } + void resetMbeDV() { m_decoder.resetMbeDV(); } + const unsigned char *getMbeDVFrame() const { return m_decoder.getMbeDVFrame(); } + int getMbeRateIndex() const { return (int) m_decoder.getMbeRate(); } + int getInLevel() const { return m_decoder.getInLevel(); } int getSamplesPerSymbol() const { return m_decoder.getSamplesPerSymbol(); } DSDcc::DSDDecoder::DSDSyncType getSyncType() const { return m_decoder.getSyncType(); } diff --git a/plugins/channel/demoddsd/dsddemod.cpp b/plugins/channel/demoddsd/dsddemod.cpp index 4ddebf5eb..e19c3a713 100644 --- a/plugins/channel/demoddsd/dsddemod.cpp +++ b/plugins/channel/demoddsd/dsddemod.cpp @@ -169,17 +169,30 @@ void DSDDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto } } - int nbAudioSamples; - short *dsdAudio = m_dsdDecoder.getAudio(nbAudioSamples); - - if (nbAudioSamples > 0) + if (DSPEngine::instance()->hasDVSerialSupport()) { - if (!m_running.m_audioMute) { - uint res = m_audioFifo.write((const quint8*) dsdAudio, nbAudioSamples, 10); - } - m_dsdDecoder.resetAudio(); + if (m_dsdDecoder.mbeDVReady()) + { + DSPEngine::instance()->push(); + m_dsdDecoder.resetMbeDV(); + } } + else + { + int nbAudioSamples; + short *dsdAudio = m_dsdDecoder.getAudio(nbAudioSamples); + + if (nbAudioSamples > 0) + { + if (!m_running.m_audioMute) { + uint res = m_audioFifo.write((const quint8*) dsdAudio, nbAudioSamples, 10); + } + + m_dsdDecoder.resetAudio(); + } + } + if ((m_scope != 0) && (m_scopeEnabled)) { diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index 53e29074a..abdde474a 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -154,3 +154,18 @@ QString DSPEngine::sourceDeviceDescription() { return m_deviceEngine->sourceDeviceDescription(); } + +void DSPEngine::setDVSerialSupport(bool support) +{ +#ifdef DSD_USE_SERIALDV + if (support) + { + m_dvSerialSupport = m_dvSerialEngine.scan(); + } + else + { + m_dvSerialEngine.release(); + m_dvSerialSupport = false; + } +#endif +} diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h index acd0b10cd..34a67f920 100644 --- a/sdrbase/dsp/dspengine.h +++ b/sdrbase/dsp/dspengine.h @@ -22,6 +22,9 @@ #include "dsp/dspdeviceengine.h" #include "audio/audiooutput.h" #include "util/export.h" +#ifdef DSD_USE_SERIALDV +#include "dsp/dvserialengine.h" +#endif class DSPDeviceEngine; class ThreadedSampleSink; @@ -65,10 +68,32 @@ public: QString errorMessage(); //!< Return the current error message QString sourceDeviceDescription(); //!< Return the source device description + bool hasDVSerialSupport() + { +#ifdef DSD_USE_SERIALDV + return m_dvSerialSupport; +#else + return false; +#endif + } + + void setDVSerialSupport(bool support); + + void push() + { +#ifdef DSD_USE_SERIALDV + m_dvSerialEngine.push(); +#endif + } + private: DSPDeviceEngine *m_deviceEngine; AudioOutput m_audioOutput; uint m_audioSampleRate; + bool m_dvSerialSupport; +#ifdef DSD_USE_SERIALDV + DVSerialEngine m_dvSerialEngine; +#endif }; #endif // INCLUDE_DSPENGINE_H diff --git a/sdrbase/dsp/dvserialengine.cpp b/sdrbase/dsp/dvserialengine.cpp new file mode 100644 index 000000000..e76b069a9 --- /dev/null +++ b/sdrbase/dsp/dvserialengine.cpp @@ -0,0 +1,226 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// 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 +#include +#include +#include +#include +#include + +#include +#include + +#include "audio/audiooutput.h" +#include "dvserialengine.h" +#include "dvserialworker.h" + +DVSerialEngine::DVSerialEngine() +{ + +} + +DVSerialEngine::~DVSerialEngine() +{ + release(); +} + +std::string DVSerialEngine::get_driver(const std::string& tty) +{ + struct stat st; + std::string devicedir = tty; + + // Append '/device' to the tty-path + devicedir += "/device"; + + // Stat the devicedir and handle it if it is a symlink + if (lstat(devicedir.c_str(), &st) == 0 && S_ISLNK(st.st_mode)) + { + char buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + + // Append '/driver' and return basename of the target + devicedir += "/driver"; + + if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0) + { + return basename(buffer); + } + } + return ""; +} + +void DVSerialEngine::register_comport(std::list& comList, + std::list& comList8250, const std::string& dir) +{ + // Get the driver the device is using + std::string driver = get_driver(dir); + + // Skip devices without a driver + if (driver.size() > 0) + { + //std::cerr << "register_comport: dir: "<< dir << " driver: " << driver << std::endl; + std::string devfile = std::string("/dev/") + basename((char *) dir.c_str()); + + // Put serial8250-devices in a seperate list + if (driver == "serial8250") + { + comList8250.push_back(devfile); + } + else + comList.push_back(devfile); + } +} + +void DVSerialEngine::probe_serial8250_comports(std::list& comList, + std::list comList8250) +{ + struct serial_struct serinfo; + std::list::iterator it = comList8250.begin(); + + // Iterate over all serial8250-devices + while (it != comList8250.end()) + { + + // Try to open the device + int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY); + + if (fd >= 0) + { + // Get serial_info + if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0) + { + // If device type is no PORT_UNKNOWN we accept the port + if (serinfo.type != PORT_UNKNOWN) + comList.push_back(*it); + } + close(fd); + } + it++; + } +} + +void DVSerialEngine::getComList() +{ + int n; + struct dirent **namelist; + m_comList.clear(); + m_comList8250.clear(); + const char* sysdir = "/sys/class/tty/"; + + // Scan through /sys/class/tty - it contains all tty-devices in the system + n = scandir(sysdir, &namelist, NULL, NULL); + if (n < 0) + perror("scandir"); + else + { + while (n--) + { + if (strcmp(namelist[n]->d_name, "..") + && strcmp(namelist[n]->d_name, ".")) + { + + // Construct full absolute file path + std::string devicedir = sysdir; + devicedir += namelist[n]->d_name; + + // Register the device + register_comport(m_comList, m_comList8250, devicedir); + } + free(namelist[n]); + } + free(namelist); + } + + // Only non-serial8250 has been added to comList without any further testing + // serial8250-devices must be probe to check for validity + probe_serial8250_comports(m_comList, m_comList8250); +} + +bool DVSerialEngine::scan() +{ + getComList(); + std::list::iterator it = m_comList.begin(); + + while (it != m_comList.end()) + { + DVSerialWorker *worker = new DVSerialWorker(); + + if (worker->open(*it)) + { + DVSerialController controller; + controller.worker = worker; + controller.device = *it; + controller.thread = new QThread(); + + controller.worker->moveToThread(controller.thread); + connect(controller.thread, SIGNAL(started()), controller.worker, SLOT(process())); + connect(controller.worker, SIGNAL(finished()), controller.thread, SLOT(quit())); + connect(controller.worker, SIGNAL(finished()), controller.worker, SLOT(deleteLater())); + connect(controller.thread, SIGNAL(finished()), controller.thread, SLOT(deleteLater())); + connect(&controller.worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), controller.worker, SLOT(handleTest())); + controller.thread->start(); + + m_controllers.push_back(controller); + qDebug() << "DVSerialEngine::scan: found device at: " << it->c_str(); + } + else + { + delete worker; + } + + it++; + } + + return m_controllers.size() > 0; +} + +void DVSerialEngine::release() +{ + qDebug("DVSerialEngine::release"); + std::vector::iterator it = m_controllers.begin(); + + while (it != m_controllers.end()) + { + disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleTest())); + it->worker->stop(); + it->thread->wait(100); + it->worker->m_inputMessageQueue.clear(); + it->worker->close(); + qDebug() << "DVSerialEngine::release: closed device at: " << it->device.c_str(); + ++it; + } + + m_controllers.clear(); +} + +void DVSerialEngine::push() +{ + std::vector::iterator it = m_controllers.begin(); + + while (it != m_controllers.end()) + { + it->worker->m_inputMessageQueue.push(DVSerialWorker::MsgTest::create()); + ++it; + } +} diff --git a/sdrbase/dsp/dvserialengine.h b/sdrbase/dsp/dvserialengine.h new file mode 100644 index 000000000..2b1049699 --- /dev/null +++ b/sdrbase/dsp/dvserialengine.h @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// 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 SDRBASE_DSP_DVSERIALENGINE_H_ +#define SDRBASE_DSP_DVSERIALENGINE_H_ + +#include +#include +#include +#include + +class QThread; +class DVSerialWorker; +class AudioFifo; + +class DVSerialEngine : public QObject +{ + Q_OBJECT +public: + DVSerialEngine(); + ~DVSerialEngine(); + + bool scan(); + void release(); + + int getNbDevices() const { return m_controllers.size(); } + void push(); + +private: + struct DVSerialController + { + QThread *thread; + DVSerialWorker *worker; + std::string device; + }; + + static std::string get_driver(const std::string& tty); + static void register_comport(std::list& comList, std::list& comList8250, const std::string& dir); + static void probe_serial8250_comports(std::list& comList, std::list comList8250); + void getComList(); + + std::list m_comList; + std::list m_comList8250; + std::vector m_controllers; +}; + + + +#endif /* SDRBASE_DSP_DVSERIALENGINE_H_ */ diff --git a/sdrbase/dsp/dvserialworker.cpp b/sdrbase/dsp/dvserialworker.cpp new file mode 100644 index 000000000..1921dec67 --- /dev/null +++ b/sdrbase/dsp/dvserialworker.cpp @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// 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 "dsp/dvserialworker.h" +#include "audio/audiofifo.h" + +MESSAGE_CLASS_DEFINITION(DVSerialWorker::MsgMbeDecode, Message) +MESSAGE_CLASS_DEFINITION(DVSerialWorker::MsgTest, Message) + +DVSerialWorker::DVSerialWorker() : + m_running(false), + m_currentGainIn(0), + m_currentGainOut(0) +{ +} + +DVSerialWorker::~DVSerialWorker() +{ +} + +bool DVSerialWorker::open(const std::string& serialDevice) +{ + return m_dvController.open(serialDevice); +} + +void DVSerialWorker::close() +{ + m_dvController.close(); +} + +void DVSerialWorker::process() +{ + m_running = true; + qDebug("DVSerialWorker::process: started"); + + while (m_running) + { + sleep(1); + } + + qDebug("DVSerialWorker::process: stopped"); + emit finished(); +} + +void DVSerialWorker::stop() +{ + m_running = false; +} + +void DVSerialWorker::handleTest() +{ + qDebug("DVSerialWorker::handleTest"); +} diff --git a/sdrbase/dsp/dvserialworker.h b/sdrbase/dsp/dvserialworker.h new file mode 100644 index 000000000..4f013e060 --- /dev/null +++ b/sdrbase/dsp/dvserialworker.h @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// 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 SDRBASE_DSP_DVSERIALWORKER_H_ +#define SDRBASE_DSP_DVSERIALWORKER_H_ + +#include +#include + +#include "dvcontroller.h" +#include "util/message.h" +#include "util/syncmessenger.h" +#include "util/messagequeue.h" + +class AudioFifo; + +class DVSerialWorker : public QObject { + Q_OBJECT +public: + class MsgTest : public Message + { + MESSAGE_CLASS_DECLARATION + public: + static MsgTest* create() { return new MsgTest(); } + private: + MsgTest() {} + }; + + DVSerialWorker(); + ~DVSerialWorker(); + + bool open(const std::string& serialDevice); + void close(); + void process(); + void stop(); + + void postTest() + { + //emit inputMessageReady(); + m_inputMessageQueue.push(MsgTest::create()); + } + + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + +signals: + void inputMessageReady(); + void finished(); + +private: + class MsgMbeDecode : public Message + { + MESSAGE_CLASS_DECLARATION + public: + const unsigned char *getMbeFrame() const { return m_mbeFrame; } + SerialDV::DVRate getMbeRate() const { return m_mbeRate; } + int getVolumeIndex() const { return m_volumeIndex; } + AudioFifo *getAudioFifo() { return m_audioFifo; } + + static MsgMbeDecode* create(const unsigned char *mbeFrame, int mbeRateIndex, int volumeIndex, AudioFifo *audioFifo) + { + return new MsgMbeDecode(mbeFrame, (SerialDV::DVRate) mbeRateIndex, volumeIndex, audioFifo); + } + + private: + unsigned char m_mbeFrame[SerialDV::MBE_FRAME_LENGTH_BYTES]; + SerialDV::DVRate m_mbeRate; + int m_volumeIndex; + AudioFifo *m_audioFifo; + + MsgMbeDecode(const unsigned char *mbeFrame, + SerialDV::DVRate mbeRate, + int volumeIndex, + AudioFifo *audioFifo) : + Message(), + m_mbeRate(mbeRate), + m_volumeIndex(volumeIndex), + m_audioFifo(audioFifo) + { + memcpy((void *) m_mbeFrame, (const void *) mbeFrame, SerialDV::MBE_FRAME_LENGTH_BYTES); + } + }; + + SerialDV::DVController m_dvController; + bool m_running; + int m_currentGainIn; + int m_currentGainOut; + short m_audioSamples[SerialDV::MBE_AUDIO_BLOCK_SIZE]; + +private slots: + void handleTest(); +}; + +#endif /* SDRBASE_DSP_DVSERIALWORKER_H_ */ diff --git a/sdrbase/mainwindow.cpp b/sdrbase/mainwindow.cpp index e1d8eaaa1..1be2efb9e 100644 --- a/sdrbase/mainwindow.cpp +++ b/sdrbase/mainwindow.cpp @@ -687,6 +687,11 @@ void MainWindow::on_action_Audio_triggered() audioDialog.exec(); } +void MainWindow::on_action_DV_Serial_triggered(bool checked) +{ + m_dspEngine->setDVSerialSupport(checked); +} + void MainWindow::on_sampleSource_currentIndexChanged(int index) { m_pluginManager->saveSourceSettings(m_settings.getWorkingPreset()); diff --git a/sdrbase/mainwindow.h b/sdrbase/mainwindow.h index a32cf22c8..ed5348452 100644 --- a/sdrbase/mainwindow.h +++ b/sdrbase/mainwindow.h @@ -153,6 +153,7 @@ private slots: void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); void on_action_Loaded_Plugins_triggered(); void on_action_Audio_triggered(); + void on_action_DV_Serial_triggered(bool checked); void on_sampleSource_currentIndexChanged(int index); void on_action_About_triggered(); };