mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-04 16:01:14 -05:00
FT8 demod: test with .wav files
This commit is contained in:
parent
d6cafa08c5
commit
0d77b37ec1
@ -7,6 +7,8 @@ set(demodft8_SOURCES
|
|||||||
ft8demodbaseband.cpp
|
ft8demodbaseband.cpp
|
||||||
ft8demodwebapiadapter.cpp
|
ft8demodwebapiadapter.cpp
|
||||||
ft8plugin.cpp
|
ft8plugin.cpp
|
||||||
|
ft8buffer.cpp
|
||||||
|
ft8demodworker.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(demodft8_HEADERS
|
set(demodft8_HEADERS
|
||||||
@ -16,6 +18,8 @@ set(demodft8_HEADERS
|
|||||||
ft8demodbaseband.h
|
ft8demodbaseband.h
|
||||||
ft8demodwebapiadapter.h
|
ft8demodwebapiadapter.h
|
||||||
ft8plugin.h
|
ft8plugin.h
|
||||||
|
ft8buffer.h
|
||||||
|
ft8demodworker.h;
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
51
plugins/channelrx/demodft8/ft8buffer.cpp
Normal file
51
plugins/channelrx/demodft8/ft8buffer.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <QMutexLocker>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
41
plugins/channelrx/demodft8/ft8buffer.h
Normal file
41
plugins/channelrx/demodft8/ft8buffer.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef INCLUDE_FT8DEMOD_FT8BUFFER_H
|
||||||
|
#define INCLUDE_FT8DEMOD_FT8BUFFER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
|
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
|
@ -242,7 +242,6 @@ void FT8Demod::applySettings(const FT8DemodSettings& settings, bool force)
|
|||||||
<< " m_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]"
|
<< " m_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]"
|
||||||
<< " m_volume: " << settings.m_volume
|
<< " m_volume: " << settings.m_volume
|
||||||
<< " m_agcActive: " << settings.m_agc
|
<< " m_agcActive: " << settings.m_agc
|
||||||
<< " m_ft8SampleRate: " << settings.m_ft8SampleRate
|
|
||||||
<< " m_streamIndex: " << settings.m_streamIndex
|
<< " m_streamIndex: " << settings.m_streamIndex
|
||||||
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
||||||
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
|
<< " 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) {
|
if ((m_settings.m_volume != settings.m_volume) || force) {
|
||||||
reverseAPIKeys.append("volume");
|
reverseAPIKeys.append("volume");
|
||||||
}
|
}
|
||||||
if ((settings.m_ft8SampleRate != m_settings.m_ft8SampleRate) || force) {
|
|
||||||
reverseAPIKeys.append("ft8SampleRate");
|
|
||||||
}
|
|
||||||
if ((m_settings.m_agc != settings.m_agc) || force) {
|
if ((m_settings.m_agc != settings.m_agc) || force) {
|
||||||
reverseAPIKeys.append("agc");
|
reverseAPIKeys.append("agc");
|
||||||
}
|
}
|
||||||
@ -366,7 +362,7 @@ void FT8Demod::sendSampleRateToDemodAnalyzer()
|
|||||||
{
|
{
|
||||||
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(
|
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(
|
||||||
this,
|
this,
|
||||||
m_settings.m_ft8SampleRate
|
FT8DemodSettings::m_ft8SampleRate
|
||||||
);
|
);
|
||||||
messageQueue->push(msg);
|
messageQueue->push(msg);
|
||||||
}
|
}
|
||||||
@ -454,9 +450,6 @@ void FT8Demod::webapiUpdateChannelSettings(
|
|||||||
if (channelSettingsKeys.contains("title")) {
|
if (channelSettingsKeys.contains("title")) {
|
||||||
settings.m_title = *response.getFt8DemodSettings()->getTitle();
|
settings.m_title = *response.getFt8DemodSettings()->getTitle();
|
||||||
}
|
}
|
||||||
if (channelSettingsKeys.contains("audioDeviceName")) {
|
|
||||||
settings.m_ft8SampleRate = response.getFt8DemodSettings()->getFt8SampleRate();
|
|
||||||
}
|
|
||||||
if (channelSettingsKeys.contains("streamIndex")) {
|
if (channelSettingsKeys.contains("streamIndex")) {
|
||||||
settings.m_streamIndex = response.getFt8DemodSettings()->getStreamIndex();
|
settings.m_streamIndex = response.getFt8DemodSettings()->getStreamIndex();
|
||||||
}
|
}
|
||||||
@ -508,7 +501,6 @@ void FT8Demod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp
|
|||||||
response.getFt8DemodSettings()->setVolume(settings.m_volume);
|
response.getFt8DemodSettings()->setVolume(settings.m_volume);
|
||||||
response.getFt8DemodSettings()->setAgc(settings.m_agc ? 1 : 0);
|
response.getFt8DemodSettings()->setAgc(settings.m_agc ? 1 : 0);
|
||||||
response.getFt8DemodSettings()->setRgbColor(settings.m_rgbColor);
|
response.getFt8DemodSettings()->setRgbColor(settings.m_rgbColor);
|
||||||
response.getFt8DemodSettings()->setFt8SampleRate(settings.m_ft8SampleRate);
|
|
||||||
|
|
||||||
if (response.getFt8DemodSettings()->getTitle()) {
|
if (response.getFt8DemodSettings()->getTitle()) {
|
||||||
*response.getFt8DemodSettings()->getTitle() = settings.m_title;
|
*response.getFt8DemodSettings()->getTitle() = settings.m_title;
|
||||||
@ -685,9 +677,6 @@ void FT8Demod::webapiFormatChannelSettings(
|
|||||||
if (channelSettingsKeys.contains("title") || force) {
|
if (channelSettingsKeys.contains("title") || force) {
|
||||||
swgFT8DemodSettings->setTitle(new QString(settings.m_title));
|
swgFT8DemodSettings->setTitle(new QString(settings.m_title));
|
||||||
}
|
}
|
||||||
if (channelSettingsKeys.contains("audioDeviceName") || force) {
|
|
||||||
swgFT8DemodSettings->setFt8SampleRate(settings.m_ft8SampleRate);
|
|
||||||
}
|
|
||||||
if (channelSettingsKeys.contains("streamIndex") || force) {
|
if (channelSettingsKeys.contains("streamIndex") || force) {
|
||||||
swgFT8DemodSettings->setStreamIndex(settings.m_streamIndex);
|
swgFT8DemodSettings->setStreamIndex(settings.m_streamIndex);
|
||||||
}
|
}
|
||||||
@ -746,5 +735,4 @@ void FT8Demod::handleIndexInDeviceSetChanged(int index)
|
|||||||
.arg(m_deviceAPI->getDeviceSetIndex())
|
.arg(m_deviceAPI->getDeviceSetIndex())
|
||||||
.arg(index);
|
.arg(index);
|
||||||
m_basebandSink->setFifoLabel(fifoLabel);
|
m_basebandSink->setFifoLabel(fifoLabel);
|
||||||
m_basebandSink->setAudioFifoLabel(fifoLabel);
|
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,14 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include "dsp/dspengine.h"
|
#include "dsp/dspengine.h"
|
||||||
#include "dsp/dspcommands.h"
|
#include "dsp/dspcommands.h"
|
||||||
#include "dsp/spectrumvis.h"
|
#include "dsp/spectrumvis.h"
|
||||||
|
#include "maincore.h"
|
||||||
|
|
||||||
|
#include "ft8demodworker.h"
|
||||||
#include "ft8demodbaseband.h"
|
#include "ft8demodbaseband.h"
|
||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(FT8DemodBaseband::MsgConfigureFT8DemodBaseband, Message)
|
MESSAGE_CLASS_DEFINITION(FT8DemodBaseband::MsgConfigureFT8DemodBaseband, Message)
|
||||||
@ -30,9 +33,36 @@ FT8DemodBaseband::FT8DemodBaseband() :
|
|||||||
m_messageQueueToGUI(nullptr),
|
m_messageQueueToGUI(nullptr),
|
||||||
m_spectrumVis(nullptr)
|
m_spectrumVis(nullptr)
|
||||||
{
|
{
|
||||||
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
|
|
||||||
|
|
||||||
qDebug("FT8DemodBaseband::FT8DemodBaseband");
|
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(
|
QObject::connect(
|
||||||
&m_sampleFifo,
|
&m_sampleFifo,
|
||||||
&SampleSinkFifo::dataReady,
|
&SampleSinkFifo::dataReady,
|
||||||
@ -41,15 +71,18 @@ FT8DemodBaseband::FT8DemodBaseband() :
|
|||||||
Qt::QueuedConnection
|
Qt::QueuedConnection
|
||||||
);
|
);
|
||||||
|
|
||||||
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue());
|
|
||||||
m_channelSampleRate = 0;
|
m_channelSampleRate = 0;
|
||||||
|
m_sink.setFT8Buffer(&m_ft8Buffer);
|
||||||
|
|
||||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||||
|
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FT8DemodBaseband::~FT8DemodBaseband()
|
FT8DemodBaseband::~FT8DemodBaseband()
|
||||||
{
|
{
|
||||||
DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo());
|
m_workerThread->exit();
|
||||||
|
m_workerThread->wait();
|
||||||
|
delete[] m_ft8WorkerBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FT8DemodBaseband::reset()
|
void FT8DemodBaseband::reset()
|
||||||
@ -136,7 +169,7 @@ bool FT8DemodBaseband::handleMessage(const Message& cmd)
|
|||||||
|
|
||||||
if (m_channelSampleRate != m_channelizer.getChannelSampleRate())
|
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();
|
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)
|
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());
|
m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset());
|
||||||
|
|
||||||
if (m_channelSampleRate != m_channelizer.getChannelSampleRate())
|
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();
|
m_channelSampleRate = m_channelizer.getChannelSampleRate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,32 +199,12 @@ void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool forc
|
|||||||
{
|
{
|
||||||
if (m_spectrumVis)
|
if (m_spectrumVis)
|
||||||
{
|
{
|
||||||
DSPSignalNotification *msg = new DSPSignalNotification(m_settings.m_ft8SampleRate/(1<<settings.m_filterBank[settings.m_filterIndex].m_spanLog2), 0);
|
DSPSignalNotification *msg = new DSPSignalNotification(FT8DemodSettings::m_ft8SampleRate/(1<<settings.m_filterBank[settings.m_filterIndex].m_spanLog2), 0);
|
||||||
m_spectrumVis->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<<m_settings.m_filterBank[settings.m_filterIndex].m_spanLog2), 0);
|
|
||||||
m_spectrumVis->getInputMessageQueue()->push(msg);
|
m_spectrumVis->getInputMessageQueue()->push(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sink.applySettings(settings, force);
|
m_sink.applySettings(settings, force);
|
||||||
|
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,3 +219,24 @@ void FT8DemodBaseband::setBasebandSampleRate(int sampleRate)
|
|||||||
m_channelizer.setBasebandSampleRate(sampleRate);
|
m_channelizer.setBasebandSampleRate(sampleRate);
|
||||||
m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset());
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QRecursiveMutex>
|
#include <QRecursiveMutex>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
#include "dsp/samplesinkfifo.h"
|
#include "dsp/samplesinkfifo.h"
|
||||||
#include "dsp/downchannelizer.h"
|
#include "dsp/downchannelizer.h"
|
||||||
@ -28,9 +29,12 @@
|
|||||||
|
|
||||||
#include "ft8demodsettings.h"
|
#include "ft8demodsettings.h"
|
||||||
#include "ft8demodsink.h"
|
#include "ft8demodsink.h"
|
||||||
|
#include "ft8buffer.h"
|
||||||
|
|
||||||
class ChannelAPI;
|
class ChannelAPI;
|
||||||
class SpectrumVis;
|
class SpectrumVis;
|
||||||
|
class QThread;
|
||||||
|
class FT8DemodWorker;
|
||||||
|
|
||||||
class FT8DemodBaseband : public QObject
|
class FT8DemodBaseband : public QObject
|
||||||
{
|
{
|
||||||
@ -73,7 +77,6 @@ public:
|
|||||||
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; }
|
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; }
|
||||||
void setChannel(ChannelAPI *channel);
|
void setChannel(ChannelAPI *channel);
|
||||||
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
|
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
|
||||||
void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); }
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
@ -83,6 +86,7 @@ signals:
|
|||||||
* \param numSamples Number of samples analyzed
|
* \param numSamples Number of samples analyzed
|
||||||
*/
|
*/
|
||||||
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
|
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
|
||||||
|
void bufferReady(int16_t *buffer, QDateTime periodTS);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SampleSinkFifo m_sampleFifo;
|
SampleSinkFifo m_sampleFifo;
|
||||||
@ -93,6 +97,11 @@ private:
|
|||||||
int m_channelSampleRate;
|
int m_channelSampleRate;
|
||||||
MessageQueue *m_messageQueueToGUI;
|
MessageQueue *m_messageQueueToGUI;
|
||||||
SpectrumVis *m_spectrumVis;
|
SpectrumVis *m_spectrumVis;
|
||||||
|
FT8Buffer m_ft8Buffer;
|
||||||
|
int m_tickCount;
|
||||||
|
QThread *m_workerThread;
|
||||||
|
FT8DemodWorker *m_ft8DemodWorker;
|
||||||
|
int16_t *m_ft8WorkerBuffer;
|
||||||
QRecursiveMutex m_mutex;
|
QRecursiveMutex m_mutex;
|
||||||
|
|
||||||
bool handleMessage(const Message& cmd);
|
bool handleMessage(const Message& cmd);
|
||||||
@ -102,6 +111,7 @@ private:
|
|||||||
private slots:
|
private slots:
|
||||||
void handleInputMessages();
|
void handleInputMessages();
|
||||||
void handleData(); //!< Handle data when samples have to be processed
|
void handleData(); //!< Handle data when samples have to be processed
|
||||||
|
void tick();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INCLUDE_SSBDEMODBASEBAND_H
|
#endif // INCLUDE_SSBDEMODBASEBAND_H
|
||||||
|
@ -361,7 +361,7 @@ void FT8DemodGUI::applySettings(bool force)
|
|||||||
unsigned int FT8DemodGUI::spanLog2Max()
|
unsigned int FT8DemodGUI::spanLog2Max()
|
||||||
{
|
{
|
||||||
unsigned int spanLog2 = 0;
|
unsigned int spanLog2 = 0;
|
||||||
for (; m_settings.m_ft8SampleRate / (1<<spanLog2) >= 1000; spanLog2++);
|
for (; FT8DemodSettings::m_ft8SampleRate / (1<<spanLog2) >= 1000; spanLog2++);
|
||||||
return spanLog2 == 0 ? 0 : spanLog2-1;
|
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;
|
unsigned int limit = s2max < 1 ? 0 : s2max - 1;
|
||||||
ui->spanLog2->setMaximum(limit);
|
ui->spanLog2->setMaximum(limit);
|
||||||
//int spanLog2 = ui->spanLog2->value();
|
//int spanLog2 = ui->spanLog2->value();
|
||||||
m_spectrumRate = m_settings.m_ft8SampleRate / (1<<spanLog2);
|
m_spectrumRate = FT8DemodSettings::m_ft8SampleRate / (1<<spanLog2);
|
||||||
int bw = ui->BW->value();
|
int bw = ui->BW->value();
|
||||||
int lw = ui->lowCut->value();
|
int lw = ui->lowCut->value();
|
||||||
int bwMax = m_settings.m_ft8SampleRate / (100*(1<<spanLog2));
|
int bwMax = FT8DemodSettings::m_ft8SampleRate / (100*(1<<spanLog2));
|
||||||
int tickInterval = m_spectrumRate / 2400;
|
int tickInterval = m_spectrumRate / 2400;
|
||||||
tickInterval = tickInterval == 0 ? 1 : tickInterval;
|
tickInterval = tickInterval == 0 ? 1 : tickInterval;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "settings/serializable.h"
|
#include "settings/serializable.h"
|
||||||
#include "ft8demodsettings.h"
|
#include "ft8demodsettings.h"
|
||||||
|
|
||||||
|
const int FT8DemodSettings::m_ft8SampleRate = 12000;
|
||||||
#ifdef SDR_RX_SAMPLE_24BIT
|
#ifdef SDR_RX_SAMPLE_24BIT
|
||||||
const int FT8DemodSettings::m_minPowerThresholdDB = -120;
|
const int FT8DemodSettings::m_minPowerThresholdDB = -120;
|
||||||
const float FT8DemodSettings::m_mminPowerThresholdDBf = 120.0f;
|
const float FT8DemodSettings::m_mminPowerThresholdDBf = 120.0f;
|
||||||
@ -46,7 +47,6 @@ void FT8DemodSettings::resetToDefaults()
|
|||||||
m_inputFrequencyOffset = 0;
|
m_inputFrequencyOffset = 0;
|
||||||
m_rgbColor = QColor(0, 192, 255).rgb();
|
m_rgbColor = QColor(0, 192, 255).rgb();
|
||||||
m_title = "FT8 Demodulator";
|
m_title = "FT8 Demodulator";
|
||||||
m_ft8SampleRate = 12000;
|
|
||||||
m_streamIndex = 0;
|
m_streamIndex = 0;
|
||||||
m_useReverseAPI = false;
|
m_useReverseAPI = false;
|
||||||
m_reverseAPIAddress = "127.0.0.1";
|
m_reverseAPIAddress = "127.0.0.1";
|
||||||
@ -71,7 +71,6 @@ QByteArray FT8DemodSettings::serialize() const
|
|||||||
s.writeU32(5, m_rgbColor);
|
s.writeU32(5, m_rgbColor);
|
||||||
s.writeBool(11, m_agc);
|
s.writeBool(11, m_agc);
|
||||||
s.writeString(16, m_title);
|
s.writeString(16, m_title);
|
||||||
s.writeS32(17, m_ft8SampleRate);
|
|
||||||
s.writeBool(18, m_useReverseAPI);
|
s.writeBool(18, m_useReverseAPI);
|
||||||
s.writeString(19, m_reverseAPIAddress);
|
s.writeString(19, m_reverseAPIAddress);
|
||||||
s.writeU32(20, m_reverseAPIPort);
|
s.writeU32(20, m_reverseAPIPort);
|
||||||
@ -129,7 +128,6 @@ bool FT8DemodSettings::deserialize(const QByteArray& data)
|
|||||||
d.readU32(5, &m_rgbColor);
|
d.readU32(5, &m_rgbColor);
|
||||||
d.readBool(11, &m_agc, false);
|
d.readBool(11, &m_agc, false);
|
||||||
d.readString(16, &m_title, "SSB Demodulator");
|
d.readString(16, &m_title, "SSB Demodulator");
|
||||||
d.readS32(17, &m_ft8SampleRate, 12000);
|
|
||||||
d.readBool(18, &m_useReverseAPI, false);
|
d.readBool(18, &m_useReverseAPI, false);
|
||||||
d.readString(19, &m_reverseAPIAddress, "127.0.0.1");
|
d.readString(19, &m_reverseAPIAddress, "127.0.0.1");
|
||||||
d.readU32(20, &utmp, 0);
|
d.readU32(20, &utmp, 0);
|
||||||
|
@ -50,7 +50,6 @@ struct FT8DemodSettings
|
|||||||
bool m_agc;
|
bool m_agc;
|
||||||
quint32 m_rgbColor;
|
quint32 m_rgbColor;
|
||||||
QString m_title;
|
QString m_title;
|
||||||
int m_ft8SampleRate;
|
|
||||||
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
||||||
bool m_useReverseAPI;
|
bool m_useReverseAPI;
|
||||||
QString m_reverseAPIAddress;
|
QString m_reverseAPIAddress;
|
||||||
@ -76,6 +75,7 @@ struct FT8DemodSettings
|
|||||||
QByteArray serialize() const;
|
QByteArray serialize() const;
|
||||||
bool deserialize(const QByteArray& data);
|
bool deserialize(const QByteArray& data);
|
||||||
|
|
||||||
|
static const int m_ft8SampleRate;
|
||||||
static const int m_minPowerThresholdDB;
|
static const int m_minPowerThresholdDB;
|
||||||
static const float m_mminPowerThresholdDBf;
|
static const float m_mminPowerThresholdDBf;
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "util/messagequeue.h"
|
#include "util/messagequeue.h"
|
||||||
#include "maincore.h"
|
#include "maincore.h"
|
||||||
|
|
||||||
|
#include "ft8buffer.h"
|
||||||
#include "ft8demodsink.h"
|
#include "ft8demodsink.h"
|
||||||
|
|
||||||
const int FT8DemodSink::m_ssbFftLen = 1024;
|
const int FT8DemodSink::m_ssbFftLen = 1024;
|
||||||
@ -66,8 +67,7 @@ FT8DemodSink::FT8DemodSink() :
|
|||||||
m_agcActive(false),
|
m_agcActive(false),
|
||||||
m_audioActive(false),
|
m_audioActive(false),
|
||||||
m_spectrumSink(nullptr),
|
m_spectrumSink(nullptr),
|
||||||
m_audioFifo(24000),
|
m_ft8Buffer(nullptr),
|
||||||
m_ft8SampleRate(12000),
|
|
||||||
m_levelInNbSamples(1200) // 100 ms
|
m_levelInNbSamples(1200) // 100 ms
|
||||||
{
|
{
|
||||||
m_Bandwidth = 5000;
|
m_Bandwidth = 5000;
|
||||||
@ -77,8 +77,6 @@ FT8DemodSink::FT8DemodSink() :
|
|||||||
m_channelSampleRate = 48000;
|
m_channelSampleRate = 48000;
|
||||||
m_channelFrequencyOffset = 0;
|
m_channelFrequencyOffset = 0;
|
||||||
|
|
||||||
m_audioBuffer.resize(1<<14);
|
|
||||||
m_audioBufferFill = 0;
|
|
||||||
m_undersampleCount = 0;
|
m_undersampleCount = 0;
|
||||||
m_sum = 0;
|
m_sum = 0;
|
||||||
|
|
||||||
@ -94,7 +92,7 @@ FT8DemodSink::FT8DemodSink() :
|
|||||||
m_agc.setThresholdEnable(false); // no squelch
|
m_agc.setThresholdEnable(false); // no squelch
|
||||||
m_agc.setClamping(false); // no clamping
|
m_agc.setClamping(false); // no clamping
|
||||||
|
|
||||||
SSBFilter = new fftfilt(m_LowCutoff / m_ft8SampleRate, m_Bandwidth / m_ft8SampleRate, m_ssbFftLen);
|
SSBFilter = new fftfilt(m_LowCutoff / FT8DemodSettings::m_ft8SampleRate, m_Bandwidth / FT8DemodSettings::m_ft8SampleRate, m_ssbFftLen);
|
||||||
|
|
||||||
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
|
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
|
||||||
applySettings(m_settings, true);
|
applySettings(m_settings, true);
|
||||||
@ -178,8 +176,11 @@ void FT8DemodSink::processOneSample(Complex &ci)
|
|||||||
|
|
||||||
Real demod = (z.real() + z.imag()) * 0.7;
|
Real demod = (z.real() + z.imag()) * 0.7;
|
||||||
qint16 sample = (qint16)(demod * m_volume);
|
qint16 sample = (qint16)(demod * m_volume);
|
||||||
m_audioBuffer[m_audioBufferFill].l = sample;
|
|
||||||
m_audioBuffer[m_audioBufferFill].r = sample;
|
if (m_ft8Buffer) {
|
||||||
|
m_ft8Buffer->feed(sample);
|
||||||
|
}
|
||||||
|
|
||||||
m_demodBuffer[m_demodBufferFill++] = sample;
|
m_demodBuffer[m_demodBufferFill++] = sample;
|
||||||
calculateLevel(sample);
|
calculateLevel(sample);
|
||||||
|
|
||||||
@ -209,19 +210,6 @@ void FT8DemodSink::processOneSample(Complex &ci)
|
|||||||
|
|
||||||
m_demodBufferFill = 0;
|
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))
|
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);
|
Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > channelSampleRate ? channelSampleRate : (m_Bandwidth * 1.5f);
|
||||||
m_interpolator.create(16, channelSampleRate, interpolatorBandwidth, 2.0f);
|
m_interpolator.create(16, channelSampleRate, interpolatorBandwidth, 2.0f);
|
||||||
m_interpolatorDistanceRemain = 0;
|
m_interpolatorDistanceRemain = 0;
|
||||||
m_interpolatorDistance = (Real) channelSampleRate / (Real) m_ft8SampleRate;
|
m_interpolatorDistance = (Real) channelSampleRate / (Real) FT8DemodSettings::m_ft8SampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_channelSampleRate = channelSampleRate;
|
m_channelSampleRate = channelSampleRate;
|
||||||
m_channelFrequencyOffset = channelFrequencyOffset;
|
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);
|
Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f);
|
||||||
m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f);
|
m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f);
|
||||||
m_interpolatorDistanceRemain = 0;
|
m_interpolatorDistanceRemain = 0;
|
||||||
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) sampleRate;
|
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);
|
||||||
SSBFilter->create_filter(m_LowCutoff / (float) sampleRate, m_Bandwidth / (float) sampleRate, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow);
|
m_levelInNbSamples = FT8DemodSettings::m_ft8SampleRate / 10; // 100 ms
|
||||||
|
|
||||||
m_audioFifo.setSize(sampleRate);
|
|
||||||
m_ft8SampleRate = sampleRate;
|
|
||||||
m_levelInNbSamples = m_ft8SampleRate / 10; // 100 ms
|
|
||||||
|
|
||||||
QList<ObjectPipe*> pipes;
|
QList<ObjectPipe*> pipes;
|
||||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_channel, "reportdemod", pipes);
|
MainCore::instance()->getMessagePipes().getMessagePipes(m_channel, "reportdemod", pipes);
|
||||||
@ -281,7 +265,7 @@ void FT8DemodSink::applyFT8SampleRate(int sampleRate)
|
|||||||
|
|
||||||
if (messageQueue)
|
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);
|
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_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]"
|
||||||
<< " m_volume: " << settings.m_volume
|
<< " m_volume: " << settings.m_volume
|
||||||
<< " m_agcActive: " << settings.m_agc
|
<< " m_agcActive: " << settings.m_agc
|
||||||
<< " m_ft8SampleRate: " << settings.m_ft8SampleRate
|
|
||||||
<< " m_streamIndex: " << settings.m_streamIndex
|
<< " m_streamIndex: " << settings.m_streamIndex
|
||||||
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
||||||
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
|
<< " 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);
|
Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f);
|
||||||
m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f);
|
m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f);
|
||||||
m_interpolatorDistanceRemain = 0;
|
m_interpolatorDistanceRemain = 0;
|
||||||
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_ft8SampleRate;
|
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) FT8DemodSettings::m_ft8SampleRate;
|
||||||
SSBFilter->create_filter(m_LowCutoff / (float) m_ft8SampleRate, m_Bandwidth / (float) m_ft8SampleRate, settings.m_filterBank[settings.m_filterIndex].m_fftWindow);
|
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)
|
if ((m_settings.m_volume != settings.m_volume) || force)
|
||||||
|
@ -25,13 +25,13 @@
|
|||||||
#include "dsp/interpolator.h"
|
#include "dsp/interpolator.h"
|
||||||
#include "dsp/fftfilt.h"
|
#include "dsp/fftfilt.h"
|
||||||
#include "dsp/agc.h"
|
#include "dsp/agc.h"
|
||||||
#include "audio/audiofifo.h"
|
|
||||||
#include "util/doublebufferfifo.h"
|
#include "util/doublebufferfifo.h"
|
||||||
|
|
||||||
#include "ft8demodsettings.h"
|
#include "ft8demodsettings.h"
|
||||||
|
|
||||||
class SpectrumVis;
|
class SpectrumVis;
|
||||||
class ChannelAPI;
|
class ChannelAPI;
|
||||||
|
class FT8Buffer;
|
||||||
|
|
||||||
class FT8DemodSink : public ChannelSampleSink {
|
class FT8DemodSink : public ChannelSampleSink {
|
||||||
public:
|
public:
|
||||||
@ -41,15 +41,14 @@ public:
|
|||||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||||
|
|
||||||
void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumSink = spectrumSink; }
|
void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumSink = spectrumSink; }
|
||||||
|
void setFT8Buffer(FT8Buffer *buffer) { m_ft8Buffer = buffer; }
|
||||||
void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false);
|
void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false);
|
||||||
void applySettings(const FT8DemodSettings& settings, 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; }
|
double getMagSq() const { return m_magsq; }
|
||||||
bool getAudioActive() const { return m_audioActive; }
|
bool getAudioActive() const { return m_audioActive; }
|
||||||
void setChannel(ChannelAPI *channel) { m_channel = channel; }
|
void setChannel(ChannelAPI *channel) { m_channel = channel; }
|
||||||
void setAudioFifoLabel(const QString& label) { m_audioFifo.setLabel(label); }
|
|
||||||
|
|
||||||
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
|
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
|
||||||
{
|
{
|
||||||
@ -134,10 +133,7 @@ private:
|
|||||||
SpectrumVis* m_spectrumSink;
|
SpectrumVis* m_spectrumSink;
|
||||||
SampleVector m_sampleBuffer;
|
SampleVector m_sampleBuffer;
|
||||||
|
|
||||||
AudioVector m_audioBuffer;
|
FT8Buffer *m_ft8Buffer;
|
||||||
uint m_audioBufferFill;
|
|
||||||
AudioFifo m_audioFifo;
|
|
||||||
quint32 m_ft8SampleRate;
|
|
||||||
|
|
||||||
QVector<qint16> m_demodBuffer;
|
QVector<qint16> m_demodBuffer;
|
||||||
int m_demodBufferFill;
|
int m_demodBufferFill;
|
||||||
|
53
plugins/channelrx/demodft8/ft8demodworker.cpp
Normal file
53
plugins/channelrx/demodft8/ft8demodworker.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
38
plugins/channelrx/demodft8/ft8demodworker.h
Normal file
38
plugins/channelrx/demodft8/ft8demodworker.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef INCLUDE_FT8DEMODWORKER_H
|
||||||
|
#define INCLUDE_FT8DEMODWORKER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
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
|
@ -32,6 +32,7 @@
|
|||||||
WavFileRecord::WavFileRecord(quint32 sampleRate, quint64 centerFrequency) :
|
WavFileRecord::WavFileRecord(quint32 sampleRate, quint64 centerFrequency) :
|
||||||
FileRecordInterface(),
|
FileRecordInterface(),
|
||||||
m_fileBase("test"),
|
m_fileBase("test"),
|
||||||
|
m_fileBaseIsFileName(false),
|
||||||
m_sampleRate(sampleRate),
|
m_sampleRate(sampleRate),
|
||||||
m_centerFrequency(centerFrequency),
|
m_centerFrequency(centerFrequency),
|
||||||
m_recordOn(false),
|
m_recordOn(false),
|
||||||
@ -46,6 +47,7 @@ WavFileRecord::WavFileRecord(quint32 sampleRate, quint64 centerFrequency) :
|
|||||||
WavFileRecord::WavFileRecord(const QString& fileBase) :
|
WavFileRecord::WavFileRecord(const QString& fileBase) :
|
||||||
FileRecordInterface(),
|
FileRecordInterface(),
|
||||||
m_fileBase(fileBase),
|
m_fileBase(fileBase),
|
||||||
|
m_fileBaseIsFileName(false),
|
||||||
m_sampleRate(0),
|
m_sampleRate(0),
|
||||||
m_centerFrequency(0),
|
m_centerFrequency(0),
|
||||||
m_recordOn(false),
|
m_recordOn(false),
|
||||||
@ -139,6 +141,18 @@ void WavFileRecord::writeMono(qint16 sample)
|
|||||||
m_byteCount += 2;
|
m_byteCount += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WavFileRecord::writeMono(qint16 *samples, int nbSamples)
|
||||||
|
{
|
||||||
|
if (m_recordStart)
|
||||||
|
{
|
||||||
|
writeHeader();
|
||||||
|
m_recordStart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleFile.write(reinterpret_cast<const char*>(samples), 2*nbSamples);
|
||||||
|
m_byteCount += 2*nbSamples;
|
||||||
|
}
|
||||||
|
|
||||||
void WavFileRecord::start()
|
void WavFileRecord::start()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -171,7 +185,11 @@ bool WavFileRecord::startRecording()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#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);
|
m_sampleFile.open(m_currentFileName.toStdString().c_str(), std::ios::binary);
|
||||||
if (!m_sampleFile.is_open())
|
if (!m_sampleFile.is_open())
|
||||||
{
|
{
|
||||||
|
@ -102,11 +102,13 @@ public:
|
|||||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) override;
|
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 write(qint16 lSample, qint16 rSample); //!< write a single sample
|
||||||
void writeMono(qint16 sample); //!< write a single mono 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 start() override;
|
||||||
virtual void stop() override;
|
virtual void stop() override;
|
||||||
virtual bool handleMessage(const Message& message) override;
|
virtual bool handleMessage(const Message& message) override;
|
||||||
|
|
||||||
virtual void setFileName(const QString& fileBase) override;
|
virtual void setFileName(const QString& fileBase) override;
|
||||||
|
void setFileBaseIsFileName(bool fileBaseIsFileName) { m_fileBaseIsFileName = fileBaseIsFileName; }
|
||||||
virtual bool startRecording() override;
|
virtual bool startRecording() override;
|
||||||
virtual bool stopRecording() override;
|
virtual bool stopRecording() override;
|
||||||
virtual bool isRecording() const override { return m_recordOn; }
|
virtual bool isRecording() const override { return m_recordOn; }
|
||||||
@ -122,6 +124,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_fileBase;
|
QString m_fileBase;
|
||||||
|
bool m_fileBaseIsFileName;
|
||||||
quint32 m_sampleRate;
|
quint32 m_sampleRate;
|
||||||
quint64 m_centerFrequency;
|
quint64 m_centerFrequency;
|
||||||
bool m_recordOn;
|
bool m_recordOn;
|
||||||
|
@ -5617,9 +5617,6 @@ margin-bottom: 20px;
|
|||||||
"title" : {
|
"title" : {
|
||||||
"type" : "string"
|
"type" : "string"
|
||||||
},
|
},
|
||||||
"ft8SampleRate" : {
|
|
||||||
"type" : "integer"
|
|
||||||
},
|
|
||||||
"streamIndex" : {
|
"streamIndex" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
||||||
@ -56876,7 +56873,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2023-01-15T12:10:43.505+01:00
|
Generated 2023-01-17T00:44:14.657+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,8 +37,6 @@ FT8DemodSettings:
|
|||||||
type: integer
|
type: integer
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
ft8SampleRate:
|
|
||||||
type: integer
|
|
||||||
streamIndex:
|
streamIndex:
|
||||||
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -37,8 +37,6 @@ FT8DemodSettings:
|
|||||||
type: integer
|
type: integer
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
ft8SampleRate:
|
|
||||||
type: integer
|
|
||||||
streamIndex:
|
streamIndex:
|
||||||
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -5617,9 +5617,6 @@ margin-bottom: 20px;
|
|||||||
"title" : {
|
"title" : {
|
||||||
"type" : "string"
|
"type" : "string"
|
||||||
},
|
},
|
||||||
"ft8SampleRate" : {
|
|
||||||
"type" : "integer"
|
|
||||||
},
|
|
||||||
"streamIndex" : {
|
"streamIndex" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
||||||
@ -56876,7 +56873,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2023-01-15T12:10:43.505+01:00
|
Generated 2023-01-17T00:44:14.657+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,8 +48,6 @@ SWGFT8DemodSettings::SWGFT8DemodSettings() {
|
|||||||
m_rgb_color_isSet = false;
|
m_rgb_color_isSet = false;
|
||||||
title = nullptr;
|
title = nullptr;
|
||||||
m_title_isSet = false;
|
m_title_isSet = false;
|
||||||
ft8_sample_rate = 0;
|
|
||||||
m_ft8_sample_rate_isSet = false;
|
|
||||||
stream_index = 0;
|
stream_index = 0;
|
||||||
m_stream_index_isSet = false;
|
m_stream_index_isSet = false;
|
||||||
use_reverse_api = 0;
|
use_reverse_api = 0;
|
||||||
@ -96,8 +94,6 @@ SWGFT8DemodSettings::init() {
|
|||||||
m_rgb_color_isSet = false;
|
m_rgb_color_isSet = false;
|
||||||
title = new QString("");
|
title = new QString("");
|
||||||
m_title_isSet = false;
|
m_title_isSet = false;
|
||||||
ft8_sample_rate = 0;
|
|
||||||
m_ft8_sample_rate_isSet = false;
|
|
||||||
stream_index = 0;
|
stream_index = 0;
|
||||||
m_stream_index_isSet = false;
|
m_stream_index_isSet = false;
|
||||||
use_reverse_api = 0;
|
use_reverse_api = 0;
|
||||||
@ -134,7 +130,6 @@ SWGFT8DemodSettings::cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(reverse_api_address != nullptr) {
|
if(reverse_api_address != nullptr) {
|
||||||
delete reverse_api_address;
|
delete reverse_api_address;
|
||||||
}
|
}
|
||||||
@ -183,8 +178,6 @@ SWGFT8DemodSettings::fromJsonObject(QJsonObject &pJson) {
|
|||||||
|
|
||||||
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&ft8_sample_rate, pJson["ft8SampleRate"], "qint32", "");
|
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
|
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
|
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
|
||||||
@ -249,9 +242,6 @@ SWGFT8DemodSettings::asJsonObject() {
|
|||||||
if(title != nullptr && *title != QString("")){
|
if(title != nullptr && *title != QString("")){
|
||||||
toJsonValue(QString("title"), title, obj, QString("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){
|
if(m_stream_index_isSet){
|
||||||
obj->insert("streamIndex", QJsonValue(stream_index));
|
obj->insert("streamIndex", QJsonValue(stream_index));
|
||||||
}
|
}
|
||||||
@ -383,16 +373,6 @@ SWGFT8DemodSettings::setTitle(QString* title) {
|
|||||||
this->m_title_isSet = true;
|
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
|
qint32
|
||||||
SWGFT8DemodSettings::getStreamIndex() {
|
SWGFT8DemodSettings::getStreamIndex() {
|
||||||
return stream_index;
|
return stream_index;
|
||||||
@ -518,9 +498,6 @@ SWGFT8DemodSettings::isSet(){
|
|||||||
if(title && *title != QString("")){
|
if(title && *title != QString("")){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
if(m_ft8_sample_rate_isSet){
|
|
||||||
isObjectUpdated = true; break;
|
|
||||||
}
|
|
||||||
if(m_stream_index_isSet){
|
if(m_stream_index_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
@ -75,9 +75,6 @@ public:
|
|||||||
QString* getTitle();
|
QString* getTitle();
|
||||||
void setTitle(QString* title);
|
void setTitle(QString* title);
|
||||||
|
|
||||||
qint32 getFt8SampleRate();
|
|
||||||
void setFt8SampleRate(qint32 ft8_sample_rate);
|
|
||||||
|
|
||||||
qint32 getStreamIndex();
|
qint32 getStreamIndex();
|
||||||
void setStreamIndex(qint32 stream_index);
|
void setStreamIndex(qint32 stream_index);
|
||||||
|
|
||||||
@ -139,9 +136,6 @@ private:
|
|||||||
QString* title;
|
QString* title;
|
||||||
bool m_title_isSet;
|
bool m_title_isSet;
|
||||||
|
|
||||||
qint32 ft8_sample_rate;
|
|
||||||
bool m_ft8_sample_rate_isSet;
|
|
||||||
|
|
||||||
qint32 stream_index;
|
qint32 stream_index;
|
||||||
bool m_stream_index_isSet;
|
bool m_stream_index_isSet;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user