FT8 demod: implemented logging and GUI updates

This commit is contained in:
f4exb 2023-01-20 23:16:57 +01:00
parent d4363929f4
commit 6c15a0ffd2
9 changed files with 163 additions and 76 deletions

View File

@ -33,19 +33,19 @@ FFTEngine::Plan *FFTEngine::get_plan(int n, const char *why)
// cache fftw plans in the parent process,
// so they will already be there for fork()ed children.
plansmu.lock();
m_plansmu.lock();
for (int i = 0; i < nplans; i++)
for (int i = 0; i < m_nplans; i++)
{
if (plans[i]->n_ == n && plans[i]->type_ == fftw_type
if (m_plans[i]->n_ == n && m_plans[i]->type_ == M_FFTW_TYPE
#if TIMING
&& strcmp(plans[i]->why_, why) == 0
#endif
)
{
Plan *p = plans[i];
Plan *p = m_plans[i];
p->uses_ += 1;
plansmu.unlock();
m_plansmu.unlock();
return p;
}
}
@ -55,7 +55,7 @@ FFTEngine::Plan *FFTEngine::get_plan(int n, const char *why)
#endif
// fftw_make_planner_thread_safe();
plansmu2.lock();
m_plansmu2.lock();
fftwf_set_timelimit(5);
@ -80,7 +80,7 @@ FFTEngine::Plan *FFTEngine::get_plan(int n, const char *why)
// FFTW_MEASURE
// FFTW_PATIENT
// FFTW_EXHAUSTIVE
int type = fftw_type;
int type = M_FFTW_TYPE;
p->type_ = type;
p->fwd_ = fftwf_plan_dft_r2c_1d(n, p->r_, p->c_, type);
assert(p->fwd_);
@ -99,12 +99,12 @@ FFTEngine::Plan *FFTEngine::get_plan(int n, const char *why)
p->crev_ = fftwf_plan_dft_1d(n, p->cc2_, p->cc1_, FFTW_BACKWARD, type);
assert(p->crev_);
plansmu2.unlock();
m_plansmu2.unlock();
assert(nplans + 1 < 1000);
assert(m_nplans + 1 < 1000);
plans[nplans] = p;
nplans += 1;
m_plans[m_nplans] = p;
m_nplans += 1;
#if TIMING
if (0 && getpid() == plan_master_pid)
@ -115,7 +115,7 @@ FFTEngine::Plan *FFTEngine::get_plan(int n, const char *why)
}
#endif
plansmu.unlock();
m_plansmu.unlock();
return p;
}
@ -562,9 +562,9 @@ std::vector<float> FFTEngine::hilbert_shift(const std::vector<float> &x, float h
void FFTEngine::fft_stats()
{
for (int i = 0; i < nplans; i++)
for (int i = 0; i < m_nplans; i++)
{
Plan *p = plans[i];
Plan *p = m_plans[i];
qDebug("FT8::FFTEngine::fft_stats: %-13s %6d %9d %6.3fn",
p->why_,
p->n_,

View File

@ -67,7 +67,7 @@ public:
int uses_;
}; // Plan
FFTEngine() : nplans(0)
FFTEngine() : m_nplans(0)
{}
Plan *get_plan(int n, const char *why);
@ -84,12 +84,12 @@ public:
private:
void fft_stats();
QMutex plansmu;
QMutex plansmu2;
Plan *plans[1000];
int nplans;
QMutex m_plansmu;
QMutex m_plansmu2;
Plan *m_plans[1000];
int m_nplans;
// MEASURE=0, ESTIMATE=64, PATIENT=32
static const int fftw_type = FFTW_ESTIMATE;
static const int M_FFTW_TYPE = FFTW_ESTIMATE;
}; // FFTEngine
} // namespace FT8

View File

@ -102,6 +102,7 @@ void FT8DemodBaseband::setMessageQueueToGUI(MessageQueue *messageQueue)
void FT8DemodBaseband::setChannel(ChannelAPI *channel)
{
m_sink.setChannel(channel);
m_ft8DemodWorker->setChannel(channel);
}
void FT8DemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
@ -184,6 +185,7 @@ bool FT8DemodBaseband::handleMessage(const Message& cmd)
{
m_ft8DemodWorker->invalidateSequence();
m_deviceCenterFrequency = notif.getCenterFrequency();
m_ft8DemodWorker->setBaseFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
}
return true;
@ -199,6 +201,7 @@ void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool forc
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_ft8DemodWorker->invalidateSequence();
m_ft8DemodWorker->setBaseFrequency(m_deviceCenterFrequency + settings.m_inputFrequencyOffset);
m_channelizer.setChannelization(FT8DemodSettings::m_ft8SampleRate, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset());

View File

@ -622,7 +622,7 @@ void FT8DemodGUI::resizeMessageTable()
ui->messages->setItem(row, MESSAGE_COL_DT, new QTableWidgetItem("-0.0"));
ui->messages->setItem(row, MESSAGE_COL_DF, new QTableWidgetItem("0000"));
ui->messages->setItem(row, MESSAGE_COL_CALL1, new QTableWidgetItem("123456789ABCD"));
ui->messages->setItem(row, MESSAGE_COL_CALL2, new QTableWidgetItem("HF7SIEMA"));
ui->messages->setItem(row, MESSAGE_COL_CALL2, new QTableWidgetItem("PA900RAALTE"));
ui->messages->setItem(row, MESSAGE_COL_LOC, new QTableWidgetItem("JN000"));
ui->messages->setItem(row, MESSAGE_COL_INFO, new QTableWidgetItem("OSD-0-73"));
ui->messages->resizeColumnsToContents();
@ -668,10 +668,10 @@ void FT8DemodGUI::messagesReceived(const QList<FT8Message>& messages)
utcItem->setText(message.ts.toString("HHmmss"));
passItem->setText(tr("%1").arg(message.pass));
snrItem->setText(tr("%1").arg(message.snr));
correctItem->setText(tr("%1").arg(message.nbCorrectBits));
dtItem->setText(tr("%1").arg(message.dt, 0, 'f', 1));
dfItem->setText(tr("%1").arg((int) message.df));
dtItem->setText(tr("%1").arg(message.dt, 4, 'f', 1));
dfItem->setText(tr("%1").arg((int) message.df, 4));
snrItem->setText(tr("%1").arg(message.snr, 3));
call1Item->setText(message.call1);
call2Item->setText(message.call2);
locItem->setText(message.loc);

View File

@ -107,10 +107,10 @@ private:
enum MessageCol {
MESSAGE_COL_UTC,
MESSAGE_COL_N,
MESSAGE_COL_SNR,
MESSAGE_COL_DEC,
MESSAGE_COL_DT,
MESSAGE_COL_DF,
MESSAGE_COL_SNR,
MESSAGE_COL_CALL1,
MESSAGE_COL_CALL2,
MESSAGE_COL_LOC,

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<width>520</width>
<height>731</height>
</rect>
</property>
@ -18,7 +18,7 @@
</property>
<property name="minimumSize">
<size>
<width>500</width>
<width>520</width>
<height>0</height>
</size>
</property>
@ -36,7 +36,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>481</width>
<width>501</width>
<height>181</height>
</rect>
</property>
@ -742,7 +742,7 @@
<rect>
<x>0</x>
<y>460</y>
<width>481</width>
<width>501</width>
<height>251</height>
</rect>
</property>
@ -965,12 +965,24 @@
</item>
<item>
<widget class="QTableWidget" name="messages">
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="toolTip">
<string>Decoded messages</string>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<attribute name="verticalHeaderMinimumSectionSize">
<number>15</number>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>15</number>
</attribute>
<column>
<property name="text">
<string>UTC</string>
@ -987,14 +999,6 @@
<string>Successful decoder pass index</string>
</property>
</column>
<column>
<property name="text">
<string>SNR</string>
</property>
<property name="toolTip">
<string>Signal to noise ratio (dB) in 2.5 kHz bandwidth</string>
</property>
</column>
<column>
<property name="text">
<string>OKb</string>
@ -1019,6 +1023,14 @@
<string>Frequency shift</string>
</property>
</column>
<column>
<property name="text">
<string>SNR</string>
</property>
<property name="toolTip">
<string>Signal to noise ratio (dB) in 2.5 kHz bandwidth</string>
</property>
</column>
<column>
<property name="text">
<string>Call1</string>

View File

@ -19,6 +19,7 @@
#include <QDir>
#include <QDateTime>
#include "channel/channelapi.h"
#include "dsp/wavfilerecord.h"
#include "util/messagequeue.h"
#include "util/ft8message.h"
@ -26,11 +27,13 @@
#include "ft8demodsettings.h"
#include "ft8demodworker.h"
FT8DemodWorker::FT8Callback::FT8Callback(const QDateTime& periodTS, FT8::Packing& packing) :
FT8DemodWorker::FT8Callback::FT8Callback(const QDateTime& periodTS, qint64 baseFrequency, FT8::Packing& packing) :
m_packing(packing),
m_periodTS(periodTS)
m_periodTS(periodTS),
m_baseFrequency(baseFrequency)
{
m_msgReportFT8Messages = MsgReportFT8Messages::create();
m_msgReportFT8Messages->setBaseFrequency(baseFrequency);
}
int FT8DemodWorker::FT8Callback::hcb(
@ -69,22 +72,20 @@ int FT8DemodWorker::FT8Callback::hcb(
ft8Message.nbCorrectBits = correct_bits;
ft8Message.dt = off - 0.5;
ft8Message.df = hz0;
ft8Message.call1 = QString(call1.c_str());
ft8Message.call2 = QString(call2.c_str());
ft8Message.loc = QString(loc.c_str());
ft8Message.call1 = QString(call1.c_str()).simplified();
ft8Message.call2 = QString(call2.c_str()).simplified();
ft8Message.loc = QString(loc.c_str()).simplified();
ft8Message.decoderInfo = QString(comment);
cycle_mu.unlock();
qDebug("FT8DemodWorker::FT8Callback::hcb: %d %3d %3d %5.2f %6.1f %s [%s:%s:%s] (%s)",
qDebug("FT8DemodWorker::FT8Callback::hcb: %6.3f %d %3d %3d %5.2f %6.1f %s (%s)",
m_baseFrequency / 1000000.0,
pass,
(int)snr,
correct_bits,
off - 0.5,
hz0,
msg.c_str(),
call1.c_str(),
call2.c_str(),
loc.c_str(),
comment
);
@ -97,13 +98,19 @@ FT8DemodWorker::FT8DemodWorker() :
m_decoderTimeBudget(0.5),
m_lowFreq(200),
m_highFreq(3000),
m_reportingMessageQueue(nullptr)
m_invalidSequence(true),
m_baseFrequency(0),
m_reportingMessageQueue(nullptr),
m_channel(nullptr)
{
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));
relPath = "sdrangel/ft8";
m_logsPath = dir.absolutePath() + "/" + relPath;
qDebug("FT8DemodWorker::FT8DemodWorker: logs path: %s", qPrintable(m_logsPath));
}
FT8DemodWorker::~FT8DemodWorker()
@ -111,8 +118,14 @@ FT8DemodWorker::~FT8DemodWorker()
void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
{
qDebug("FT8DemodWorker::processBuffer: %s %d:%f [%d:%d]", qPrintable(periodTS.toString("yyyy-MM-dd HH:mm:ss")),
m_nbDecoderThreads, m_decoderTimeBudget, m_lowFreq, m_highFreq);
qDebug("FT8DemodWorker::processBuffer: %6.3f %s %d:%f [%d:%d]",
m_baseFrequency / 1000000.0,
qPrintable(periodTS.toString("yyyy-MM-dd HH:mm:ss")),
m_nbDecoderThreads,
m_decoderTimeBudget,
m_lowFreq,
m_highFreq
);
if (m_invalidSequence)
{
@ -121,23 +134,8 @@ void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
return;
}
if (m_recordSamples)
{
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;
}
int hints[2] = { 2, 0 }; // CQ
FT8Callback ft8Callback(periodTS, m_packing);
FT8Callback ft8Callback(periodTS, m_baseFrequency, m_packing);
m_ft8Decoder.getParams().nthreads = m_nbDecoderThreads;
std::vector<float> samples(15*FT8DemodSettings::m_ft8SampleRate);
@ -165,11 +163,78 @@ void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
);
m_ft8Decoder.wait(m_decoderTimeBudget + 1.0); // add one second to budget to force quit threads
qDebug("FT8DemodWorker::processBuffer: done: %d messages", ft8Callback.getReportMessage()->getFT8Messages().size());
qDebug("FT8DemodWorker::processBuffer: done: at %6.3f %d messages",
m_baseFrequency / 1000000.0, ft8Callback.getReportMessage()->getFT8Messages().size());
if (m_reportingMessageQueue) {
m_reportingMessageQueue->push(ft8Callback.getReportMessage());
} else {
delete ft8Callback.getReportMessage();
}
if (m_logMessages)
{
const QList<FT8Message>& ft8Messages = ft8Callback.getReportMessage()->getFT8Messages();
std::ofstream logFile;
double baseFrequencyMHz = m_baseFrequency/1000000.0;
for (const auto& ft8Message : ft8Messages)
{
if (!logFile.is_open())
{
QString channelReference = "d0c0"; // default
if (m_channel) {
channelReference = tr("d%1c%2").arg(m_channel->getDeviceSetIndex()).arg(m_channel->getIndexInDeviceSet());
}
QString logFileName(tr("%1_%2.txt").arg(periodTS.toString("yyyyMMdd")).arg(channelReference));
QFileInfo lfi(QDir(m_logsPath), logFileName);
QString logFilePath = lfi.absoluteFilePath();
if (lfi.exists()) {
logFile.open(logFilePath.toStdString(), std::ios::app);
} else {
logFile.open(logFilePath.toStdString());
}
}
if (ft8Message.call1 == "UNK") {
continue;
}
QString logMessage = QString("%1 %2 Rx FT8 %3 %4 %5 %6 %7 %8")
.arg(ft8Message.ts.toString("yyyyMMdd_HHmmss"))
.arg(baseFrequencyMHz, 9, 'f', 3)
.arg(ft8Message.snr, 6)
.arg(ft8Message.dt, 4, 'f', 1)
.arg(ft8Message.df, 4, 'f', 0)
.arg(ft8Message.call1)
.arg(ft8Message.call2)
.arg(ft8Message.loc);
logMessage.remove(0, 2);
logFile << logMessage.toStdString() << std::endl;
}
if (logFile.is_open()) {
logFile.close();
}
}
if (!m_reportingMessageQueue) {
delete m_reportingMessageQueue;
}
if (m_recordSamples)
{
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;
}
}

View File

@ -26,6 +26,7 @@
class QDateTime;
class MessageQueue;
class MsgReportFT8Messages;
class ChannelAPI;
class FT8DemodWorker : public QObject
{
@ -43,12 +44,14 @@ public:
void setHighFrequency(int highFreq) { m_highFreq = highFreq; }
void setReportingMessageQueue(MessageQueue *messageQueue) { m_reportingMessageQueue = messageQueue; }
void invalidateSequence() { m_invalidSequence = true; }
void setBaseFrequency(qint64 baseFrequency) { m_baseFrequency = baseFrequency; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
private:
class FT8Callback : public FT8::CallbackInterface
{
public:
FT8Callback(const QDateTime& periodTS, FT8::Packing& packing);
FT8Callback(const QDateTime& periodTS, qint64 baseFrequency, FT8::Packing& packing);
virtual int hcb(
int *a91,
float hz0,
@ -58,21 +61,20 @@ private:
int pass,
int correct_bits
);
const std::map<std::string, bool>& getMsgMap() {
return cycle_already;
}
MsgReportFT8Messages *getReportMessage() {
return m_msgReportFT8Messages;
}
const std::map<std::string, bool>& getMsgMap() { return cycle_already; }
MsgReportFT8Messages *getReportMessage() { return m_msgReportFT8Messages; }
private:
QMutex cycle_mu;
std::map<std::string, bool> cycle_already;
FT8::Packing& m_packing;
MsgReportFT8Messages *m_msgReportFT8Messages;
const QDateTime& m_periodTS;
qint64 m_baseFrequency;
};
QString m_samplesPath;
QString m_logsPath;
bool m_recordSamples;
bool m_logMessages;
int m_nbDecoderThreads;
@ -80,9 +82,11 @@ private:
int m_lowFreq;
int m_highFreq;
bool m_invalidSequence;
qint64 m_baseFrequency;
FT8::FT8Decoder m_ft8Decoder;
FT8::Packing m_packing;
MessageQueue *m_reportingMessageQueue;
ChannelAPI *m_channel;
};
#endif // INCLUDE_FT8DEMODWORKER_H

View File

@ -42,6 +42,7 @@ class FT8_API MsgReportFT8Messages : public Message {
MESSAGE_CLASS_DECLARATION
public:
QList<FT8Message>& getFT8Messages() { return m_ft8Messages; }
void setBaseFrequency(qint64 baseFrequency) { m_baseFrequency = baseFrequency; }
static MsgReportFT8Messages* create() {
return new MsgReportFT8Messages();
@ -49,9 +50,11 @@ public:
private:
QList<FT8Message> m_ft8Messages;
qint64 m_baseFrequency;
MsgReportFT8Messages() :
Message()
Message(),
m_baseFrequency(0)
{ }
};