diff --git a/plugins/channelrx/demodft8/CMakeLists.txt b/plugins/channelrx/demodft8/CMakeLists.txt index 5779556cd..05df0ff0a 100644 --- a/plugins/channelrx/demodft8/CMakeLists.txt +++ b/plugins/channelrx/demodft8/CMakeLists.txt @@ -7,6 +7,8 @@ set(demodft8_SOURCES ft8demodbaseband.cpp ft8demodwebapiadapter.cpp ft8plugin.cpp + ft8buffer.cpp + ft8demodworker.cpp ) set(demodft8_HEADERS @@ -16,6 +18,8 @@ set(demodft8_HEADERS ft8demodbaseband.h ft8demodwebapiadapter.h ft8plugin.h + ft8buffer.h + ft8demodworker.h; ) include_directories( diff --git a/plugins/channelrx/demodft8/ft8buffer.cpp b/plugins/channelrx/demodft8/ft8buffer.cpp new file mode 100644 index 000000000..566b48e20 --- /dev/null +++ b/plugins/channelrx/demodft8/ft8buffer.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 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 // +// (at your option) any later version. // +// // +// 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 "ft8demodsettings.h" +#include "ft8buffer.h" + +FT8Buffer::FT8Buffer() : + m_bufferSize(FT8DemodSettings::m_ft8SampleRate*15), + m_sampleIndex(0) +{ + m_buffer = new int16_t[2*m_bufferSize]; +} + +FT8Buffer::~FT8Buffer() +{ + delete[] m_buffer; +} + +void FT8Buffer::feed(int16_t sample) +{ + QMutexLocker mlock(&m_mutex); + m_buffer[m_sampleIndex] = sample; + m_buffer[m_sampleIndex + m_bufferSize] = sample; + m_sampleIndex++; + + if (m_sampleIndex == m_bufferSize) { + m_sampleIndex = 0; + } +} + +void FT8Buffer::getCurrentBuffer(int16_t *bufferCopy) +{ + QMutexLocker mlock(&m_mutex); + std::copy(&m_buffer[m_sampleIndex], &m_buffer[m_sampleIndex + m_bufferSize], bufferCopy); +} diff --git a/plugins/channelrx/demodft8/ft8buffer.h b/plugins/channelrx/demodft8/ft8buffer.h new file mode 100644 index 000000000..53c57e6c0 --- /dev/null +++ b/plugins/channelrx/demodft8/ft8buffer.h @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 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 // +// (at your option) any later version. // +// // +// 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_FT8DEMOD_FT8BUFFER_H +#define INCLUDE_FT8DEMOD_FT8BUFFER_H + +#include +#include + +class FT8Buffer : public QObject +{ + Q_OBJECT +public: + FT8Buffer(); + ~FT8Buffer(); + + void feed(int16_t sample); + void getCurrentBuffer(int16_t *bufferCopy); + +private: + int16_t *m_buffer; + int m_bufferSize; + int m_sampleIndex; + QMutex m_mutex; +}; + +#endif // INCLUDE_FT8DEMOD_FT8BUFFER_H diff --git a/plugins/channelrx/demodft8/ft8demod.cpp b/plugins/channelrx/demodft8/ft8demod.cpp index 86ca3690f..2c380b94e 100644 --- a/plugins/channelrx/demodft8/ft8demod.cpp +++ b/plugins/channelrx/demodft8/ft8demod.cpp @@ -242,7 +242,6 @@ void FT8Demod::applySettings(const FT8DemodSettings& settings, bool force) << " m_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]" << " m_volume: " << settings.m_volume << " m_agcActive: " << settings.m_agc - << " m_ft8SampleRate: " << settings.m_ft8SampleRate << " m_streamIndex: " << settings.m_streamIndex << " m_useReverseAPI: " << settings.m_useReverseAPI << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress @@ -274,9 +273,6 @@ void FT8Demod::applySettings(const FT8DemodSettings& settings, bool force) if ((m_settings.m_volume != settings.m_volume) || force) { reverseAPIKeys.append("volume"); } - if ((settings.m_ft8SampleRate != m_settings.m_ft8SampleRate) || force) { - reverseAPIKeys.append("ft8SampleRate"); - } if ((m_settings.m_agc != settings.m_agc) || force) { reverseAPIKeys.append("agc"); } @@ -366,7 +362,7 @@ void FT8Demod::sendSampleRateToDemodAnalyzer() { MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create( this, - m_settings.m_ft8SampleRate + FT8DemodSettings::m_ft8SampleRate ); messageQueue->push(msg); } @@ -454,9 +450,6 @@ void FT8Demod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("title")) { settings.m_title = *response.getFt8DemodSettings()->getTitle(); } - if (channelSettingsKeys.contains("audioDeviceName")) { - settings.m_ft8SampleRate = response.getFt8DemodSettings()->getFt8SampleRate(); - } if (channelSettingsKeys.contains("streamIndex")) { settings.m_streamIndex = response.getFt8DemodSettings()->getStreamIndex(); } @@ -508,7 +501,6 @@ void FT8Demod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp response.getFt8DemodSettings()->setVolume(settings.m_volume); response.getFt8DemodSettings()->setAgc(settings.m_agc ? 1 : 0); response.getFt8DemodSettings()->setRgbColor(settings.m_rgbColor); - response.getFt8DemodSettings()->setFt8SampleRate(settings.m_ft8SampleRate); if (response.getFt8DemodSettings()->getTitle()) { *response.getFt8DemodSettings()->getTitle() = settings.m_title; @@ -685,9 +677,6 @@ void FT8Demod::webapiFormatChannelSettings( if (channelSettingsKeys.contains("title") || force) { swgFT8DemodSettings->setTitle(new QString(settings.m_title)); } - if (channelSettingsKeys.contains("audioDeviceName") || force) { - swgFT8DemodSettings->setFt8SampleRate(settings.m_ft8SampleRate); - } if (channelSettingsKeys.contains("streamIndex") || force) { swgFT8DemodSettings->setStreamIndex(settings.m_streamIndex); } @@ -746,5 +735,4 @@ void FT8Demod::handleIndexInDeviceSetChanged(int index) .arg(m_deviceAPI->getDeviceSetIndex()) .arg(index); m_basebandSink->setFifoLabel(fifoLabel); - m_basebandSink->setAudioFifoLabel(fifoLabel); } diff --git a/plugins/channelrx/demodft8/ft8demodbaseband.cpp b/plugins/channelrx/demodft8/ft8demodbaseband.cpp index 54a8ac610..f9a671803 100644 --- a/plugins/channelrx/demodft8/ft8demodbaseband.cpp +++ b/plugins/channelrx/demodft8/ft8demodbaseband.cpp @@ -16,11 +16,14 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "dsp/spectrumvis.h" +#include "maincore.h" +#include "ft8demodworker.h" #include "ft8demodbaseband.h" MESSAGE_CLASS_DEFINITION(FT8DemodBaseband::MsgConfigureFT8DemodBaseband, Message) @@ -30,9 +33,36 @@ FT8DemodBaseband::FT8DemodBaseband() : m_messageQueueToGUI(nullptr), m_spectrumVis(nullptr) { - m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); - qDebug("FT8DemodBaseband::FT8DemodBaseband"); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_ft8WorkerBuffer = new int16_t[FT8DemodSettings::m_ft8SampleRate*15]; + + m_workerThread = new QThread(); + m_ft8DemodWorker = new FT8DemodWorker(); + + m_ft8DemodWorker->moveToThread(m_workerThread); + + QObject::connect( + m_workerThread, + &QThread::finished, + m_ft8DemodWorker, + &QObject::deleteLater + ); + QObject::connect( + m_workerThread, + &QThread::finished, + m_ft8DemodWorker, + &QThread::deleteLater + ); + QObject::connect( + this, + &FT8DemodBaseband::bufferReady, + m_ft8DemodWorker, + &FT8DemodWorker::processBuffer + ); + + m_workerThread->start(); + QObject::connect( &m_sampleFifo, &SampleSinkFifo::dataReady, @@ -41,15 +71,18 @@ FT8DemodBaseband::FT8DemodBaseband() : Qt::QueuedConnection ); - DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue()); m_channelSampleRate = 0; + m_sink.setFT8Buffer(&m_ft8Buffer); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); } FT8DemodBaseband::~FT8DemodBaseband() { - DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo()); + m_workerThread->exit(); + m_workerThread->wait(); + delete[] m_ft8WorkerBuffer; } void FT8DemodBaseband::reset() @@ -136,7 +169,7 @@ bool FT8DemodBaseband::handleMessage(const Message& cmd) if (m_channelSampleRate != m_channelizer.getChannelSampleRate()) { - m_sink.applyFT8SampleRate(m_settings.m_ft8SampleRate); // reapply when channel sample rate changes + m_sink.applyFT8SampleRate(); // reapply when channel sample rate changes m_channelSampleRate = m_channelizer.getChannelSampleRate(); } @@ -152,12 +185,12 @@ void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool forc { if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { - m_channelizer.setChannelization(m_settings.m_ft8SampleRate, settings.m_inputFrequencyOffset); + m_channelizer.setChannelization(FT8DemodSettings::m_ft8SampleRate, settings.m_inputFrequencyOffset); m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset()); if (m_channelSampleRate != m_channelizer.getChannelSampleRate()) { - m_sink.applyFT8SampleRate(m_settings.m_ft8SampleRate); // reapply when channel sample rate changes + m_sink.applyFT8SampleRate(); // reapply when channel sample rate changes m_channelSampleRate = m_channelizer.getChannelSampleRate(); } } @@ -166,32 +199,12 @@ void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool forc { if (m_spectrumVis) { - DSPSignalNotification *msg = new DSPSignalNotification(m_settings.m_ft8SampleRate/(1<getInputMessageQueue()->push(msg); - } - } - - if ((settings.m_ft8SampleRate != m_settings.m_ft8SampleRate) || force) - { - m_sink.applyFT8SampleRate(settings.m_ft8SampleRate); - m_channelizer.setChannelization(settings.m_ft8SampleRate, settings.m_inputFrequencyOffset); - m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset()); - - if (getMessageQueueToGUI()) - { - DSPConfigureAudio *msg = new DSPConfigureAudio((int) settings.m_ft8SampleRate, DSPConfigureAudio::AudioOutput); - getMessageQueueToGUI()->push(msg); - } - - if (m_spectrumVis) - { - DSPSignalNotification *msg = new DSPSignalNotification(settings.m_ft8SampleRate/(1<getInputMessageQueue()->push(msg); } } m_sink.applySettings(settings, force); - m_settings = settings; } @@ -206,3 +219,24 @@ void FT8DemodBaseband::setBasebandSampleRate(int sampleRate) m_channelizer.setBasebandSampleRate(sampleRate); m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset()); } + +void FT8DemodBaseband::tick() +{ + QDateTime nowUTC = QDateTime::currentDateTimeUtc(); + + if (nowUTC.time().second() % 15 < 14) + { + if (m_tickCount++ == 0) + { + QDateTime periodTs = nowUTC.addSecs(-15); + qDebug("FT8DemodBaseband::tick: %s", qPrintable(nowUTC.toString("yyyy-MM-dd HH:mm:ss"))); + m_ft8Buffer.getCurrentBuffer(m_ft8WorkerBuffer); + emit bufferReady(m_ft8WorkerBuffer, periodTs); + periodTs = nowUTC; + } + } + else + { + m_tickCount = 0; + } +} diff --git a/plugins/channelrx/demodft8/ft8demodbaseband.h b/plugins/channelrx/demodft8/ft8demodbaseband.h index b05e8a627..1da9d968d 100644 --- a/plugins/channelrx/demodft8/ft8demodbaseband.h +++ b/plugins/channelrx/demodft8/ft8demodbaseband.h @@ -20,6 +20,7 @@ #include #include +#include #include "dsp/samplesinkfifo.h" #include "dsp/downchannelizer.h" @@ -28,9 +29,12 @@ #include "ft8demodsettings.h" #include "ft8demodsink.h" +#include "ft8buffer.h" class ChannelAPI; class SpectrumVis; +class QThread; +class FT8DemodWorker; class FT8DemodBaseband : public QObject { @@ -73,7 +77,6 @@ public: void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; } void setChannel(ChannelAPI *channel); void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } - void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); } signals: /** @@ -83,6 +86,7 @@ signals: * \param numSamples Number of samples analyzed */ void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples); + void bufferReady(int16_t *buffer, QDateTime periodTS); private: SampleSinkFifo m_sampleFifo; @@ -93,6 +97,11 @@ private: int m_channelSampleRate; MessageQueue *m_messageQueueToGUI; SpectrumVis *m_spectrumVis; + FT8Buffer m_ft8Buffer; + int m_tickCount; + QThread *m_workerThread; + FT8DemodWorker *m_ft8DemodWorker; + int16_t *m_ft8WorkerBuffer; QRecursiveMutex m_mutex; bool handleMessage(const Message& cmd); @@ -102,6 +111,7 @@ private: private slots: void handleInputMessages(); void handleData(); //!< Handle data when samples have to be processed + void tick(); }; #endif // INCLUDE_SSBDEMODBASEBAND_H diff --git a/plugins/channelrx/demodft8/ft8demodgui.cpp b/plugins/channelrx/demodft8/ft8demodgui.cpp index 5b1e9714d..3de5dda80 100644 --- a/plugins/channelrx/demodft8/ft8demodgui.cpp +++ b/plugins/channelrx/demodft8/ft8demodgui.cpp @@ -361,7 +361,7 @@ void FT8DemodGUI::applySettings(bool force) unsigned int FT8DemodGUI::spanLog2Max() { unsigned int spanLog2 = 0; - for (; m_settings.m_ft8SampleRate / (1<= 1000; spanLog2++); + for (; FT8DemodSettings::m_ft8SampleRate / (1<= 1000; spanLog2++); return spanLog2 == 0 ? 0 : spanLog2-1; } @@ -372,10 +372,10 @@ void FT8DemodGUI::applyBandwidths(unsigned int spanLog2, bool force) unsigned int limit = s2max < 1 ? 0 : s2max - 1; ui->spanLog2->setMaximum(limit); //int spanLog2 = ui->spanLog2->value(); - m_spectrumRate = m_settings.m_ft8SampleRate / (1<BW->value(); int lw = ui->lowCut->value(); - int bwMax = m_settings.m_ft8SampleRate / (100*(1<feed(sample); + } + m_demodBuffer[m_demodBufferFill++] = sample; calculateLevel(sample); @@ -209,19 +210,6 @@ void FT8DemodSink::processOneSample(Complex &ci) m_demodBufferFill = 0; } - - ++m_audioBufferFill; - - if (m_audioBufferFill >= m_audioBuffer.size()) - { - // uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - // if (res != m_audioBufferFill) { - // qDebug("FT8DemodSink::processOneSample: %u/%u samples written", res, m_audioBufferFill); - // } - - m_audioBufferFill = 0; - } } if (m_spectrumSink && (m_sampleBuffer.size() != 0)) @@ -248,27 +236,23 @@ void FT8DemodSink::applyChannelSettings(int channelSampleRate, int channelFreque Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > channelSampleRate ? channelSampleRate : (m_Bandwidth * 1.5f); m_interpolator.create(16, channelSampleRate, interpolatorBandwidth, 2.0f); m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) channelSampleRate / (Real) m_ft8SampleRate; + m_interpolatorDistance = (Real) channelSampleRate / (Real) FT8DemodSettings::m_ft8SampleRate; } m_channelSampleRate = channelSampleRate; m_channelFrequencyOffset = channelFrequencyOffset; } -void FT8DemodSink::applyFT8SampleRate(int sampleRate) +void FT8DemodSink::applyFT8SampleRate() { - qDebug("FT8DemodSink::applyFT8SampleRate: %d", sampleRate); + qDebug("FT8DemodSink::applyFT8SampleRate: %d", FT8DemodSettings::m_ft8SampleRate); Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f); m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f); m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) m_channelSampleRate / (Real) sampleRate; - - SSBFilter->create_filter(m_LowCutoff / (float) sampleRate, m_Bandwidth / (float) sampleRate, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow); - - m_audioFifo.setSize(sampleRate); - m_ft8SampleRate = sampleRate; - m_levelInNbSamples = m_ft8SampleRate / 10; // 100 ms + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) FT8DemodSettings::m_ft8SampleRate; + SSBFilter->create_filter(m_LowCutoff / (float) FT8DemodSettings::m_ft8SampleRate, m_Bandwidth / (float) FT8DemodSettings::m_ft8SampleRate, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow); + m_levelInNbSamples = FT8DemodSettings::m_ft8SampleRate / 10; // 100 ms QList pipes; MainCore::instance()->getMessagePipes().getMessagePipes(m_channel, "reportdemod", pipes); @@ -281,7 +265,7 @@ void FT8DemodSink::applyFT8SampleRate(int sampleRate) if (messageQueue) { - MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(m_channel, sampleRate); + MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(m_channel, FT8DemodSettings::m_ft8SampleRate); messageQueue->push(msg); } } @@ -299,7 +283,6 @@ void FT8DemodSink::applySettings(const FT8DemodSettings& settings, bool force) << " m_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]" << " m_volume: " << settings.m_volume << " m_agcActive: " << settings.m_agc - << " m_ft8SampleRate: " << settings.m_ft8SampleRate << " m_streamIndex: " << settings.m_streamIndex << " m_useReverseAPI: " << settings.m_useReverseAPI << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress @@ -337,8 +320,8 @@ void FT8DemodSink::applySettings(const FT8DemodSettings& settings, bool force) Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f); m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f); m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_ft8SampleRate; - SSBFilter->create_filter(m_LowCutoff / (float) m_ft8SampleRate, m_Bandwidth / (float) m_ft8SampleRate, settings.m_filterBank[settings.m_filterIndex].m_fftWindow); + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) FT8DemodSettings::m_ft8SampleRate; + SSBFilter->create_filter(m_LowCutoff / (float) FT8DemodSettings::m_ft8SampleRate, m_Bandwidth / (float) FT8DemodSettings::m_ft8SampleRate, settings.m_filterBank[settings.m_filterIndex].m_fftWindow); } if ((m_settings.m_volume != settings.m_volume) || force) diff --git a/plugins/channelrx/demodft8/ft8demodsink.h b/plugins/channelrx/demodft8/ft8demodsink.h index fae7d5eca..7c312bfaf 100644 --- a/plugins/channelrx/demodft8/ft8demodsink.h +++ b/plugins/channelrx/demodft8/ft8demodsink.h @@ -25,13 +25,13 @@ #include "dsp/interpolator.h" #include "dsp/fftfilt.h" #include "dsp/agc.h" -#include "audio/audiofifo.h" #include "util/doublebufferfifo.h" #include "ft8demodsettings.h" class SpectrumVis; class ChannelAPI; +class FT8Buffer; class FT8DemodSink : public ChannelSampleSink { public: @@ -41,15 +41,14 @@ public: virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumSink = spectrumSink; } + void setFT8Buffer(FT8Buffer *buffer) { m_ft8Buffer = buffer; } void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); void applySettings(const FT8DemodSettings& settings, bool force = false); - void applyFT8SampleRate(int sampleRate); + void applyFT8SampleRate(); - AudioFifo *getAudioFifo() { return &m_audioFifo; } double getMagSq() const { return m_magsq; } bool getAudioActive() const { return m_audioActive; } void setChannel(ChannelAPI *channel) { m_channel = channel; } - void setAudioFifoLabel(const QString& label) { m_audioFifo.setLabel(label); } void getMagSqLevels(double& avg, double& peak, int& nbSamples) { @@ -134,10 +133,7 @@ private: SpectrumVis* m_spectrumSink; SampleVector m_sampleBuffer; - AudioVector m_audioBuffer; - uint m_audioBufferFill; - AudioFifo m_audioFifo; - quint32 m_ft8SampleRate; + FT8Buffer *m_ft8Buffer; QVector m_demodBuffer; int m_demodBufferFill; diff --git a/plugins/channelrx/demodft8/ft8demodworker.cpp b/plugins/channelrx/demodft8/ft8demodworker.cpp new file mode 100644 index 000000000..7f6595810 --- /dev/null +++ b/plugins/channelrx/demodft8/ft8demodworker.cpp @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 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 // +// (at your option) any later version. // +// // +// 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 "dsp/wavfilerecord.h" + +#include "ft8demodsettings.h" +#include "ft8demodworker.h" + +FT8DemodWorker::FT8DemodWorker() +{ + QString relPath = "sdrangel/ft8/save"; + QDir dir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)); + dir.mkpath(relPath); + m_samplesPath = dir.absolutePath() + "/" + relPath; + qDebug("FT8DemodWorker::FT8DemodWorker: samples path: %s", qPrintable(m_samplesPath)); +} + +FT8DemodWorker::~FT8DemodWorker() +{} + +void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS) +{ + qDebug("FT8DemodWorker::processBuffer: %s", qPrintable(periodTS.toString("yyyy-MM-dd HH:mm:ss"))); + WavFileRecord *wavFileRecord = new WavFileRecord(FT8DemodSettings::m_ft8SampleRate); + QFileInfo wfi(QDir(m_samplesPath), periodTS.toString("yyyyMMdd_HHmmss")); + QString wpath = wfi.absoluteFilePath(); + qDebug("FT8DemodWorker::processBuffer: WAV file: %s.wav", qPrintable(wpath)); + wavFileRecord->setFileName(wpath); + wavFileRecord->setFileBaseIsFileName(true); + wavFileRecord->setMono(true); + wavFileRecord->startRecording(); + wavFileRecord->writeMono(buffer, 15*FT8DemodSettings::m_ft8SampleRate); + wavFileRecord->stopRecording(); + delete wavFileRecord; +} diff --git a/plugins/channelrx/demodft8/ft8demodworker.h b/plugins/channelrx/demodft8/ft8demodworker.h new file mode 100644 index 000000000..a097c774b --- /dev/null +++ b/plugins/channelrx/demodft8/ft8demodworker.h @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 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 // +// (at your option) any later version. // +// // +// 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_FT8DEMODWORKER_H +#define INCLUDE_FT8DEMODWORKER_H + +#include + +class QDateTime; + +class FT8DemodWorker : public QObject +{ + Q_OBJECT +public: + FT8DemodWorker(); + ~FT8DemodWorker(); + + void processBuffer(int16_t *buffer, QDateTime periodTS); + +private: + QString m_samplesPath; +}; + +#endif // INCLUDE_FT8DEMODWORKER_H diff --git a/sdrbase/dsp/wavfilerecord.cpp b/sdrbase/dsp/wavfilerecord.cpp index ccba9f42e..ba2855db3 100644 --- a/sdrbase/dsp/wavfilerecord.cpp +++ b/sdrbase/dsp/wavfilerecord.cpp @@ -32,6 +32,7 @@ WavFileRecord::WavFileRecord(quint32 sampleRate, quint64 centerFrequency) : FileRecordInterface(), m_fileBase("test"), + m_fileBaseIsFileName(false), m_sampleRate(sampleRate), m_centerFrequency(centerFrequency), m_recordOn(false), @@ -46,6 +47,7 @@ WavFileRecord::WavFileRecord(quint32 sampleRate, quint64 centerFrequency) : WavFileRecord::WavFileRecord(const QString& fileBase) : FileRecordInterface(), m_fileBase(fileBase), + m_fileBaseIsFileName(false), m_sampleRate(0), m_centerFrequency(0), m_recordOn(false), @@ -139,6 +141,18 @@ void WavFileRecord::writeMono(qint16 sample) m_byteCount += 2; } +void WavFileRecord::writeMono(qint16 *samples, int nbSamples) +{ + if (m_recordStart) + { + writeHeader(); + m_recordStart = false; + } + + m_sampleFile.write(reinterpret_cast(samples), 2*nbSamples); + m_byteCount += 2*nbSamples; +} + void WavFileRecord::start() { } @@ -171,7 +185,11 @@ bool WavFileRecord::startRecording() return false; } #else - m_currentFileName = m_fileBase + "_" + QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH_mm_ss_zzz") + ".wav"; // Don't use QString::arg on Android, as filename can contain %2 + if (m_fileBaseIsFileName) { + m_currentFileName = m_fileBase + ".wav"; + } else { + m_currentFileName = m_fileBase + "_" + QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH_mm_ss_zzz") + ".wav"; // Don't use QString::arg on Android, as filename can contain %2 + } m_sampleFile.open(m_currentFileName.toStdString().c_str(), std::ios::binary); if (!m_sampleFile.is_open()) { diff --git a/sdrbase/dsp/wavfilerecord.h b/sdrbase/dsp/wavfilerecord.h index c763c4808..fbada5367 100644 --- a/sdrbase/dsp/wavfilerecord.h +++ b/sdrbase/dsp/wavfilerecord.h @@ -102,11 +102,13 @@ public: virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) override; void write(qint16 lSample, qint16 rSample); //!< write a single sample void writeMono(qint16 sample); //!< write a single mono sample + void writeMono(qint16 *samples, int nbSamples); //!< write a buffer of mono samples virtual void start() override; virtual void stop() override; virtual bool handleMessage(const Message& message) override; virtual void setFileName(const QString& fileBase) override; + void setFileBaseIsFileName(bool fileBaseIsFileName) { m_fileBaseIsFileName = fileBaseIsFileName; } virtual bool startRecording() override; virtual bool stopRecording() override; virtual bool isRecording() const override { return m_recordOn; } @@ -122,6 +124,7 @@ public: private: QString m_fileBase; + bool m_fileBaseIsFileName; quint32 m_sampleRate; quint64 m_centerFrequency; bool m_recordOn; diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 7f17a415f..bae0ed842 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -5617,9 +5617,6 @@ margin-bottom: 20px; "title" : { "type" : "string" }, - "ft8SampleRate" : { - "type" : "integer" - }, "streamIndex" : { "type" : "integer", "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." @@ -56876,7 +56873,7 @@ except ApiException as e:
- Generated 2023-01-15T12:10:43.505+01:00 + Generated 2023-01-17T00:44:14.657+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/FT8Demod.yaml b/sdrbase/resources/webapi/doc/swagger/include/FT8Demod.yaml index 9e85af11e..c0cfd867e 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FT8Demod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FT8Demod.yaml @@ -37,8 +37,6 @@ FT8DemodSettings: type: integer title: type: string - ft8SampleRate: - type: integer streamIndex: description: MIMO channel. Not relevant when connected to SI (single Rx). type: integer diff --git a/swagger/sdrangel/api/swagger/include/FT8Demod.yaml b/swagger/sdrangel/api/swagger/include/FT8Demod.yaml index f6cf447e7..f2a8013f0 100644 --- a/swagger/sdrangel/api/swagger/include/FT8Demod.yaml +++ b/swagger/sdrangel/api/swagger/include/FT8Demod.yaml @@ -37,8 +37,6 @@ FT8DemodSettings: type: integer title: type: string - ft8SampleRate: - type: integer streamIndex: description: MIMO channel. Not relevant when connected to SI (single Rx). type: integer diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 7f17a415f..bae0ed842 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -5617,9 +5617,6 @@ margin-bottom: 20px; "title" : { "type" : "string" }, - "ft8SampleRate" : { - "type" : "integer" - }, "streamIndex" : { "type" : "integer", "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." @@ -56876,7 +56873,7 @@ except ApiException as e:
- Generated 2023-01-15T12:10:43.505+01:00 + Generated 2023-01-17T00:44:14.657+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.cpp index 2fc4b5bba..58ef2436d 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.cpp @@ -48,8 +48,6 @@ SWGFT8DemodSettings::SWGFT8DemodSettings() { m_rgb_color_isSet = false; title = nullptr; m_title_isSet = false; - ft8_sample_rate = 0; - m_ft8_sample_rate_isSet = false; stream_index = 0; m_stream_index_isSet = false; use_reverse_api = 0; @@ -96,8 +94,6 @@ SWGFT8DemodSettings::init() { m_rgb_color_isSet = false; title = new QString(""); m_title_isSet = false; - ft8_sample_rate = 0; - m_ft8_sample_rate_isSet = false; stream_index = 0; m_stream_index_isSet = false; use_reverse_api = 0; @@ -134,7 +130,6 @@ SWGFT8DemodSettings::cleanup() { } - if(reverse_api_address != nullptr) { delete reverse_api_address; } @@ -183,8 +178,6 @@ SWGFT8DemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); - ::SWGSDRangel::setValue(&ft8_sample_rate, pJson["ft8SampleRate"], "qint32", ""); - ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); @@ -249,9 +242,6 @@ SWGFT8DemodSettings::asJsonObject() { if(title != nullptr && *title != QString("")){ toJsonValue(QString("title"), title, obj, QString("QString")); } - if(m_ft8_sample_rate_isSet){ - obj->insert("ft8SampleRate", QJsonValue(ft8_sample_rate)); - } if(m_stream_index_isSet){ obj->insert("streamIndex", QJsonValue(stream_index)); } @@ -383,16 +373,6 @@ SWGFT8DemodSettings::setTitle(QString* title) { this->m_title_isSet = true; } -qint32 -SWGFT8DemodSettings::getFt8SampleRate() { - return ft8_sample_rate; -} -void -SWGFT8DemodSettings::setFt8SampleRate(qint32 ft8_sample_rate) { - this->ft8_sample_rate = ft8_sample_rate; - this->m_ft8_sample_rate_isSet = true; -} - qint32 SWGFT8DemodSettings::getStreamIndex() { return stream_index; @@ -518,9 +498,6 @@ SWGFT8DemodSettings::isSet(){ if(title && *title != QString("")){ isObjectUpdated = true; break; } - if(m_ft8_sample_rate_isSet){ - isObjectUpdated = true; break; - } if(m_stream_index_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.h index 03521f2eb..4532fcb9a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGFT8DemodSettings.h @@ -75,9 +75,6 @@ public: QString* getTitle(); void setTitle(QString* title); - qint32 getFt8SampleRate(); - void setFt8SampleRate(qint32 ft8_sample_rate); - qint32 getStreamIndex(); void setStreamIndex(qint32 stream_index); @@ -139,9 +136,6 @@ private: QString* title; bool m_title_isSet; - qint32 ft8_sample_rate; - bool m_ft8_sample_rate_isSet; - qint32 stream_index; bool m_stream_index_isSet;